;##########################################################################
;
;                          *** ct-BASIC ***
;
; EHBASIC modified for T65 Core on FPGA by C. Meyer 3/2009
; Version 2.3 mit Hardware-Multiplier für Textdarstellung
; 
; Source "main_cm.asm" in 6502 Simulator (Download unter http://home.pacbell.net/michal_k/) 
; als Main-Datei übersetzen und als Code "ehbas_X.dat" bzw. "ehbasic.dat" speichern mit:
; X - siehe unten
; Dateityp "Binary image"
; Options: Begin $B000, End $FFFF bei AIM=45056 in BASIC.INI
; C.Meyer 06/2010, cm@ct.de
;
; Interpreter basiert auf EHBASIC von Lee Davidson
; http://www.themotionstore.com/leeedavison/6502/ehbasic/index.html
;##########################################################################


	.START Reset
	.ROM_AREA $B200, $FFFF
	.OPT	Proc65c02, CaseInsensitive

; supported by t65 core: inc, dec, phx, plx, phy, ply
; missing: bra, ora, lda, cmp, sbc, tsb*2, trb*2, stz*2, bit*2, wai, stp, jmp, bbr*8, bbs*8

VGA640		= 0	; Version für VGA 640x480
LCD320		= 0	; Version für LCD 320 x 240, 40 Zeichen / 20 zeilen
LCD480		= 1	; Version für LCD 480 x 234, 60 Zeichen / 19 zeilen
; nur eines von beiden darf aktiv sein:
Mouse		= 0	; Version für Maus statt Keyboard
AutoStart	= 1	; Version lädt AUTOSTART.BAS

; Bootloader.asm stellt fest, ob Maus oder Keyboard angeschlossen ist und lädt:
; ehbas_k.dat bei Keyboard,
; ehbas_m.dat bei Maus und
; ehbas_a.dat ohne Maus oder Keyboard (autostart).
; ODER:
; ehbasic.dat für Verwendung ohne Bootloader direkt aus BASIC.INI



	.INCLUDE "defines_cm.asm"	; HW- und ZeroPage-Belegung
	
	; $B000 bis $B3FF frei für Plot-Daten
	; $B000 bis $B0FF ScopeData
	*=$B000
	.INCLUDE "plotlib_cm.asm"
	; gleich anschließend
	.INCLUDE "extensions_cm.asm" 	; darf $BFFF nicht überschreiten

	*=ROM_start	; immer bei $C000!

;Jump Table of important ROM routines	

	JMP 	Reset		; $C000, Cold Start
	JMP 	LAB_WARM	; $C003, do EhBASIC warm start
	JMP 	ClearVars	; $C006, Reset Vars, String Space etc.
	
	JMP 	AccToBank	; $C009, Memory Bank Management
	JMP 	BankToAcc  	; $C00C 

	JMP 	TickerDelay	; $C00F, VSync delay * Acc
	JMP 	Getkey		; der auf ZP $03 kann verbogen werden
	JMP 	Waitkey  		
	JMP 	WriteScreen		
	JMP 	WriteAcc		
	JMP 	ReceiveByte	; über COMCH
	JMP 	SendByte	; über COMCH
	JMP 	LAB_SCGB	; wichtig für CALL Erweiterungen: ",<bytevar>"
; LAB_SCGI, wichtig für CALL Erweiterungen: ",<intvar>", Ergebnis in Itempl/Itemph
	JSR	LAB_SCOMMA	; scan for "," , else do syntax error then warm start
; LAB_EVNM, wichtig für CALL Erweiterungen, wie vor, jedoch ohne Komma
	JSR	LAB_EVNM	; evaluate expression and check is numeric,					; else do type mismatch
	JMP	LAB_F2FX	; save integer part of FAC1 in temporary integer

	.INCLUDE "basic_cm.asm"
	.INCLUDE "monitor_cm.asm"
	.INCLUDE "mouse_cm.asm"
	
;#####################################################
;###             Memory Bank Access                ###
;#####################################################
	
; for Acc move from/to (BankPtrL),Y in Bank (AccessBank)
; BankPtrL = $52
; BankPtrH = $53
; AccessBank = $46, 70 dez.
; Benutzt X-Reg., Y als zus. Pointer (=0 wenn nicht gebraucht)

AccToBank:	
	LDX AccessBank
	STX Bank
	STA (BankPtr),Y
	JMP BankRestore
BankToAcc:
	LDX AccessBank
	STX Bank
	LDA (BankPtr),Y
BankRestore:
	LDX CurrBank
	STX Bank
	RTS

;############################################################################
;#	            Tastaturabfrage und PS/2-Konvertierung                  #
;############################################################################

UpCaseConvert:		; wandelt Akku mit ASCII in Großbuchstaben für BASIC
	CMP #32	; Cursor, Tab und sonstige Sondertasten
	BCC UpCaseConvExit
	;weitere hier
	;CMP #$08
	;BNE UpCaseCaps
	;LDA #$7F
UpCaseCaps:
	CMP #$41	; alle von "A" an
	BMI UpCaseConvExit
	CMP #$7B
	BPL UpCaseConvExit
	CMP #$60
	BPL UpCaseConvEOR
	CMP #$5B
	BPL UpCaseConvExit
UpCaseConvEOR:
	EOR CapsFlg
UpCaseConvExit:
	RTS

;############################################################################

; Tastenwert holen, nicht warten
GetKey:
	STY	ysave
	STX	xsave
	LDY 	Clineh		; immediate Mode?
	INY
	STY	RunFlag		; ist 0 wenn immediate Mode

	.IF Mouse
	
	LDA	peekEvent
	CMP	#2		; Maus-Event?
	BNE	GetKey_0
; Maus-Event behandeln
	JSR	MouseEvent
	JMP	GetKeyExit
	
	.ENDIF

GetKey_0:
	LDA #0
	LDY 	KbdPtr		; prüfen, ob PS/2- oder SPI-"Taste" eingetroffen
	CPY 	iKbdPtr
	BEQ 	GetKeyExit	; wenn nix mehr da
	INY
	STY 	KbdPtr
	LDA 	KbdBuf,Y	
GetKey_1:
	BEQ 	GetKeyExit	; Nullbyte
	BMI 	DispatchControls_0	; wenn >$80
	JSR 	UpCaseConvert
GetKeyExit:
	LDY	ysave
	LDX	xsave
	CMP 	#0
	BNE	GetKeyExit_0
	CLC
	RTS
GetKeyExit_0:
	SEC
	RTS
GetKeyNone:
	LDA	#0
	BEQ	GetKeyExit	; immer

; einige Sonderfälle für Dispatcher
PadEnter:
	LDA	#$0D
	BNE	GetKeyExit
PadDiv:
	LDA	#$2F
	BNE	GetKeyExit

;############################################################################
	
; Sondertasten behandeln, Pfeiltasten usw.
DispatchControls_0:
	LDY	#0
	STY	asave		; nur ausführen, aber nicht merken
DispatchControls:
	STA	work
;	STA 	$0600	;Test
	LDA	work
	BPL	CodeDispatch_0	; 0..127 immer
	CMP	#$D8
	BEQ	CodeDispatch_0	; CAPS immer
	CMP	#$DA
	BEQ	CodeDispatch_0	; Keypad-ENTER immer
	
	LDY 	RunFlag		; immediate Mode?
	BNE	GetKeyNone	; nein, also nicht behandeln
CodeDispatch_0:	; Suchroutine
	LDY	#$FF
CodeDispatch_1:
	INY
	LDA	SpecialCode,Y
	BEQ	GetKeyNone	; Nicht gefunden?
	CMP	work
	BNE	CodeDispatch_1
	LDA	SpecialActionL,Y
	STA	ptrsrc
	LDA	SpecialActionH,Y
	STA	ptrsrc+1
	JMP	(ptrsrc)


SpecialCode:
; normale ASCII-Controls
	.DB	$0D	; CR
	.DB	$0A	; LF
	.DB	$0B	; VT
	.DB	$09	; TAB, hier HT
	.DB	$03	; ETX
	.DB	$08	; BS, hier DEL
	.DB	$0C	; CLS
	.DB	$1F	; HOME
; Tastatur-Befehle PS/2	
	.DB	$F0	; Insert
	.DB	$F1	; Delete
	.DB	$DA	; Enter auf Keypad
	.DB	$D8	; CAPS
	.DB	$F5	; Pfeil hoch
	.DB	$F2	; Pfeil runter
	.DB	$EB	; Pfeil links
	.DB	$F4	; Pfeil rechts
	.DB	$AD	; Shift-Return
	.DB	$CA	; / Keypad
	.DB	$FC	; PrtScreen
	.DB	$EC	; Pos1
	.DB	$E9	; End
	.DB	$A9	; Tab-Taste
	.DB	$FD	; PgUp
	.DB	$FA	; PgDwn
	.DB	$81	; F1 - LIST
	.DB	$82	; F2 - DIR
	.DB	$80	; F3 (!)
	.DB	$84	; F4
	.DB	$85	; F5
	.DB	$86	; F6
	.DB	$83	; F7 (!)
	.DB	$88	; F8
	.DB	$89	; F9
	.DB	$8A	; F10
	.DB	$8B	; F11
	.DB	$8C	; F12
	.DB	$00
	
SpecialActionL:
; normale ASCII-Controls
	.DB	<cr
	.DB	<lf
	.DB	<vt
	.DB	<ht
	.DB	<omode_etx
	.DB	<del
	.DB	<cls	
	.DB	<hc
; Tastatur-Befehle PS/2
	.db	<InsertToggle	
	.db	<Delete	
	.db	<PadEnter	
	.db	<CapsToggle	
	.db	<vt	
	.db	<lf	
	.db	<bs_ctrl	
	.db	<ht_ctrl	
	.db	<ShiftReturn
	.DB	<PadDiv
	.DB	<ClearFullScreen_0
	.DB	<cr_0		; Pos1
	.DB	<CursEndOfRow	; End
	.DB	<Copy
	.DB	<hc
	.DB	<PgDwn
	.DB	<F1
	.DB	<F2
	.DB	<F3
	.DB	<F4
	.DB	<F5
	.DB	<F6
	.DB	<F7
	.DB	<F8
	.DB	<F9
	.DB	<F10
	.DB	<F11
	.DB	<F12
	

SpecialActionH:
; normale ASCII-Controls
	.DB	>cr
	.DB	>lf
	.DB	>vt
	.DB	>ht
	.DB	>omode_etx
	.DB	>del
	.DB	>cls	
	.DB	>hc
; Tastatur-Befehle PS/2
	.DB	>InsertToggle	
	.db	>Delete	
	.db	>PadEnter	
	.db	>CapsToggle	
	.db	>vt	
	.db	>lf	
	.db	>bs_ctrl	
	.db	>ht_ctrl	
	.db	>ShiftReturn
	.DB	>PadDiv
	.DB	>ClearFullScreen_0
	.DB	>cr_0		; Pos1
	.DB	>CursEndOfRow	; End
	.DB	>Copy
	.DB	>hc
	.DB	>PgDwn
	.DB	>F1
	.DB	>F2
	.DB	>F3
	.DB	>F4
	.DB	>F5
	.DB	>F6
	.DB	>F7
	.DB	>F8
	.DB	>F9
	.db	>F10
	.db	>F11
	.db	>F12
	
; F1 bis F12 definiert in renumber_cm.asm

Copy:		
; Holt Taste unter dem Cursor
	LDA 	TextWR
	JMP	GetKeyExit

; Taste abfragen: wartet bis Taste gedrückt
; ASCII-Tastenwert in A und Carry "0"

WaitKey:
	JSR GetKey
	BCC WaitKey
	RTS

;############################################################################
	
; INSERT-Status auf PS/2-Code hin ändern
InsertToggle:		
	LDA 	InsFlg
	EOR 	#$20
	STA 	InsFlg
	BEQ	InsertToggle_0
InsertToggle_0:
	JSR 	InsertStatus
InsertToggle_1:
	JMP	GetKeyNone

; CAPS-Status auf PS/2-Code hin ändern
CapsToggle:		
	LDA 	CapsFlg
	EOR 	#$20
	STA 	CapsFlg
	JSR 	CapsStatus
	LDA	#$ED	
	STA	PS2sendStack+1
	LDA	#$04		; CapsLock An	
	STA	PS2sendStack
	LDA 	CapsFlg
	BNE	CapsToggle_0
	LDA	#$00		; CapsLock Aus	
	STA	PS2sendStack
CapsToggle_0:
	LDA	#2
	STA 	PS2sendStackPtr	; immer zuletzt	
	JMP	GetKeyNone
	
	

CursEndOfRow:
	JSR	SearchEndOfTextRow
	INY
	STY	TextPosX
	JMP	GetKeyNone

PgDwn:
	LDY	TextWinBtm
	STY	TextPosX
	JMP	GetKeyNone
	

; Pfeiltasten behandeln, mit Ctrl auf nächstes/vorhergehendes Trennzeichen
bs_ctrl:
	LDA	PS2ctrlFlag
	BNE	SearchPrevSpace
	JMP	bs		
SearchPrevSpace:
	LDY	TextX
	DEY
	BMI	SearchNextSpace_0
	STY	TextX
	LDA 	TextWR
	JSR	SearchDivider
	BCC	SearchPrevSpace		; ansonsten immer
	BCS	SearchNextSpace_0
	
ht_ctrl:
	LDA	PS2ctrlFlag
	BNE	SearchNextSpace
	JMP	ht	
SearchNextSpace:
	INC	TextX
	LDA 	TextWR
	JSR	SearchDivider
	BCC	SearchNextSpace
SearchNextSpace_0:
	CLC
	LDY	TextX
	STY	TextPosX
	JMP	GetKeyNone

SearchDivider:
	CMP	#' '
	BEQ	SearchDivider_1	; Exit wenn gefunden
	CMP	#','
	BEQ	SearchDivider_1
	CMP	#':'
	BEQ	SearchDivider_1
	CMP	#';'
	BEQ	SearchDivider_1
	CMP	#'"'
	BEQ	SearchDivider_1
SearchDivider_0:
	CLC
	RTS
SearchDivider_1:
	SEC
	RTS
		
SearchEndOfTextRow:
; Ende der (Listing-)Zeile suchen, nur gesamter Bildschirm, kein Fenster berücksichtigt
	LDY 	#IbuffL+1		; max. Länge
	CPY 	TextWinRight
	BCC 	SearchEndOfTextRowLoop	; wenn IbuffL >= colstotal 
	LDY 	TextWinRight
SearchEndOfTextRowLoop:
	DEY
	STY 	TextX
	BEQ	SearchEndOfTextRowExit
	LDA 	TextWR
	CMP 	#' '		; erstes Nicht-Space-zeichen von hinten suchen
	BEQ 	SearchEndOfTextRowLoop
SearchEndOfTextRowExit:
; in Acc ist jetzt das erste gefundene Nicht-Space-Zeichen von hinten
; in Y die Endposition der Zeile (Pos. des Zeichens in Acc) oder 0, wenn nix drin
	RTS	

ShiftReturn:		
; Holt ganze Zeile unter dem Cursor ab linkem Rand in den Buffer und führt sie aus
IbuffL = IBuffe-IBuffs-1
	JSR SearchEndOfTextRow
	INY
	LDA	#0		; neues Ende
	STA 	Ibuffs,Y
	DEY
ShiftRetLoop:
	STY 	TextX
	LDA 	TextWR
	STA 	Ibuffs,Y
	DEY
	BPL 	ShiftRetLoop
	JSR	LAB_CRLF	; print CR/LF
	LDX 	#<Ibuffs
	LDY 	#>Ibuffs
	JMP 	LAB_1280	; execute buffer

Delete:		
; Bildschirmzeile ab Cursor eins nach links
	LDA	PS2ctrlFlag
	;AND	PS2altFlag	; wird noch nicht gesetzt
	BNE	RebootJmp
	LDX TextWinRight
	INX
	STX work
	LDX TextPosX
	INX			; Cursorposition+1
DelLoop:
	STX TextX
	LDA TextWR
	DEX
	STX TextX
	STA TextWR
	INX
	INX
	CPX work		; Ende?
	BNE DelLoop
	LDA #$20		; Leerzeichen am Ende der Zeile
	DEX
	STX TextX
	STA TextWR
	LDX TextPosX
	STX TextX
	JMP GetKeyNone

RebootJmp:
	JMP	Reboot	; CTRL-ALT-DELETE
	
;############################################################################
;#                        Statusanzeige obere Zeile                         #
;############################################################################
	
ErrStatus:
	STY ystatus
	STX xstatus
	.IF LCD320
	LDA #15
	STA ColorP
	LDA LabErrFlags
	BNE ErrStatus0
	LDA #'-'-'0'
ErrStatus0:
	CLC
	ADC #'0'
	LDY #0
	LDX #29
	JSR WriteCharXY
	.ENDIF
	.IF LCD480
	LDA #4
	STA ColorP
	LDA LabErrFlags
	BNE ErrStatus0
	LDA #'-'-'0'
ErrStatus0:
	CLC
	ADC #'0'
	LDY #0
	LDX #$27
	JSR WriteCharXY
	.ENDIF	
	.IF VGA640
	LDA #4
	STA ColorP
	LDA LabErrFlags
	BNE ErrStatus0
	LDA #'-'-'0'
ErrStatus0:
	CLC
	ADC #'0'
	LDY #0
	LDX #$3B
	JSR WriteCharXY
	.ENDIF	
	JMP StatusRestore

ComStatus:
	STY ystatus
	STX xstatus
	.IF LCD320
	LDA #15
	STA ColorP
	LDA COMCH
	CLC
	ADC #'0'
	LDY #0
	LDX #28
	JSR WriteCharXY
	.ENDIF
	.IF LCD480
	LDA #5
	STA ColorP
	LDA COMCH
	CLC
	ADC #'0'
	LDY #0
	LDX #$31
	JSR WriteCharXY
	.ENDIF
	.IF VGA640
	LDA #5
	STA ColorP
	LDA COMCH
	CLC
	ADC #'0'
	LDY #0
	LDX #$45
	JSR WriteCharXY
	.ENDIF
	JMP StatusRestore
	
BankStatus:
	STY ystatus
	STX xstatus
	.IF LCD320
	LDA #15
	STA ColorP
	LDA CurrBank
	CLC
	ADC #'0'
	LDY #0
	LDX #19
	JSR WriteCharXY
	.ENDIF
	.IF LCD480
	LDA #6
	STA ColorP
	LDA CurrBank
	CLC
	ADC #'0'
	LDY #0
	LDX #$1D
	JSR WriteCharXY
	.ENDIF
	.IF VGA640
	LDA #6
	STA ColorP
	LDA CurrBank
	CLC
	ADC #'0'
	LDY #0
	LDX #$31
	JSR WriteCharXY
	.ENDIF
StatusRestore:
	LDA Color
	STA ColorP
	LDY ystatus
	LDX xstatus
	RTS
	
	.IF Mouse
	.ELSE
CapsStr: .db "CAPS",0
InsStr: .DB "INS",0
	.ENDIF
	

; ShiftLock CAPS in Status anzeigen ab $24 bei LCD, $4C bei VGA
CapsStatus:
	.IF Mouse
	RTS
	.ELSE
	
	STY ystatus
	STX xstatus
	LDY #0
	.IF LCD320
	LDA #15
	STA ColorP
	LDX #$24
	.ENDIF
	.IF LCD480
	LDA #10
	STA ColorP
	LDX #$38
	.ENDIF
	.IF VGA640
	LDA #10
	STA ColorP
	LDX #$4C
	.ENDIF
	STX ScreenIdx
	LDX #0
	STX TextIdx
CapsStatusLoop:
	LDX TextIdx
	LDA CapsStr,X
	BEQ CapsStatusExit		
	LDA CapsFlg
	BNE CapsStatusOn
	LDA #green
	STA ColorP
	LDA #' '
	BNE CapsStatusOff	; immer	
CapsStatusOn:
	LDA CapsStr,X
CapsStatusOff:
	LDX ScreenIdx
	JSR writeCharXY
	INC TextIdx
	INC ScreenIdx
	JMP CapsStatusLoop
CapsStatusExit:
	JMP StatusRestore
	
	.ENDIF	; Mouse
	
; Insert INS in Status anzeigen ab $20 bei LCD, $48 bei VGA

InsertStatus:
	.IF Mouse
	RTS
	.ELSE
	
	STY ystatus
	STX xstatus
	LDY #0
	.IF LCD320
	LDA #15
	STA ColorP
	LDX #$20
	.ENDIF
	.IF LCD480
	LDA #13
	STA ColorP
	LDX #$34
	.ENDIF
	.IF VGA640
	LDA #13
	STA ColorP
	LDX #$48
	.ENDIF
	STX ScreenIdx
	LDX #0
	STX TextIdx
InsertStatusLoop:
	LDX TextIdx
	LDA InsStr,X
	BEQ InsertStatusExit		
	LDA InsFlg
	BNE InsertStatusOn	
	LDA #green
	STA ColorP
	LDA #' '
	BNE InsertStatusOff	; immer	
InsertStatusOn:
	LDA InsStr,X
InsertStatusOff:
	LDX ScreenIdx
	JSR writeCharXY
	INC TextIdx
	INC ScreenIdx
	JMP InsertStatusLoop
InsertStatusExit:
	JSR	UpdateCursorStyle
	JMP StatusRestore
		
	.ENDIF	; Mouse
	

UpdateCursorStyle:
	LDA	InsFlg
	BNE	UpdateCursorStyleInsOn
UpdateCursorStyleInsOff:
	LDA 	#7
	JMP	CursorSetStyle	
UpdateCursorStyleInsOn:	
	LDA 	#6
	JMP	CursorSetStyle	; kehrt mit RTS zurück
	
;############################################################################
; Scanset 2, deutsche Tastatur von Carsten Meyer, cm@ix.de
;
; Designed and Written by Daryl Rictor (c) 2001   65c02@altavista.com
; Offered as freeware.  No warranty is given.  Use at your own risk.
;
; Unshifted table for scancodes to ascii conversion
;		        Scan|Keyboard
;		        Code|Key
;		        ----|----------
PS2toASCII:
	.IF Mouse
	.ELSE

	.BYTE $00	; 00 no key pressed
	.byte $89	; 01 F9
	.byte $87	; 02 relocated F7
	.byte $85	; 03 F5
	.byte $80	; 04 F3
	.byte $81	; 05 F1
	.byte $82	; 06 F2
	.byte $8C	; 07 F12
	.byte $00	; 08 
	.byte $8A	; 09 F10
	.byte $88	; 0A F8
	.byte $86	; 0B F6
	.byte $84	; 0C F4
	.byte $A9	; 0D tab
	.byte '^'	; 0E `~
	.byte $8F	; 0F relocated Print Screen key
	.byte $03	; 10 relocated Pause/Break key
	.byte $A0	; 11 left alt (right alt too)
	.byte $00	; 12 left shift
	.byte $E0	; 13 relocated Alt release code
	.byte $00	; 14 left ctrl (right ctrl too)
	.byte $71	; 15 qQ
	.byte $31	; 16 1!
	.byte $00	; 17 
	.byte $00	; 18 
	.byte $00	; 19 
	.byte 'y'	; 1A zZ
	.byte $73	; 1B sS
	.byte $61	; 1C aA
	.byte $77	; 1D wW
	.byte $32	; 1E 2@
	.byte $A1	; 1F Windows 98 menu key (left side)
	.byte $02	; 20 relocated ctrl-break key
	.byte $63	; 21 cC
	.byte $78	; 22 xX
	.byte $64	; 23 dD
	.byte $65	; 24 eE
	.byte $34	; 25 4$
	.byte $33	; 26 3#
	.byte $A2	; 27 Windows 98 menu key (right side)
	.byte $00	; 28
	.byte $20	; 29 space
	.byte $76	; 2A vV
	.byte $66	; 2B fF
	.byte $74	; 2C tT
	.byte $72	; 2D rR
	.byte $35	; 2E 5%
	.byte $A3	; 2F Windows 98 option key (right click, right side)
	.byte $00	; 30
	.byte $6E	; 31 nN
	.byte $62	; 32 bB
	.byte $68	; 33 hH
	.byte $67	; 34 gG
	.byte 'z'	; 35 yY
	.byte $36	; 36 6^
	.byte $00	; 37
	.byte $00	; 38
	.byte $00	; 39
	.byte $6D	; 3A mM
	.byte $6A	; 3B jJ
	.byte $75	; 3C uU
	.byte $37	; 3D 7&
	.byte $38	; 3E 8*
	.byte $00	; 3F
	.byte $00	; 40
	.byte $2C	; 41 ,<
	.byte $6B	; 42 kK
	.byte $69	; 43 iI
	.byte $6F	; 44 oO
	.byte $30	; 45 0)
	.byte $39	; 46 9(
	.byte $00	; 47
	.byte $00	; 48
	.byte $2E	; 49 .>
	.byte '-'	; 4A /?
	.byte $6C	; 4B lL
	.byte '['	; 4C ;:
	.byte $70	; 4D pP
	.byte '\'	; 4E -_
	.byte $00	; 4F
	.byte $00	; 50
	.byte $00	; 51
	.byte ']'	; 52 '"
	.byte $00	; 53
	.byte '~'	; 54 Ü gibts nicht, statt [{
	.byte $27	; 55 ' war =+
	.byte $00	; 56
	.byte $00	; 57
	.byte $D8	; 58 caps
	.byte $00	; 59 r shift
	.byte $0D	; 5A <Enter>
	.byte '+'	; 5B ]}
	.byte $00	; 5C
	.byte '#'	; 5D \|
	.byte $00	; 5E
	.byte $00	; 5F
	.byte $00	; 60
	.byte '<'	; 61
	.byte $00	; 62
	.byte $00	; 63
	.byte $00	; 64
	.byte $00	; 65
	.byte $08	; 66 bkspace
	.byte $00	; 67
	.byte $00	; 68
	.byte $31	; 69 kp 1
	.byte '/'+$80	; 6A kp / converted from E04A in code
	.byte $34	; 6B kp 4
	.byte $37	; 6C kp 7
	.byte $00	; 6D
	.byte $00	; 6E
	.byte $00	; 6F
	.byte $30	; 70 kp 0
	.byte $2E	; 71 kp .
	.byte $32	; 72 kp 2
	.byte $35	; 73 kp 5
	.byte $36	; 74 kp 6
	.byte $38	; 75 kp 8
	.byte $1B	; 76 esc
	.byte $F7	; 77 num lock
	.byte $8B	; 78 F11
	.byte $2B	; 79 kp +
	.byte $33	; 7A kp 3
	.byte $2D	; 7B kp -
	.byte $2A	; 7C kp *
	.byte $39	; 7D kp 9
	.byte $8D	; 7E scroll lock
	.byte $00	; 7F 
	.ENDIF		; Mouse
;
;############################################################################
;
; Table for shifted scancodes 
;
PS2toASCIIshifted:
	.IF Mouse
	.ELSE
	.byte $00	; 80 
	.byte $C9	; 81 F9
	.byte $C7	; 82 relocated F7 
	.byte $C5	; 83 F5 (F7 actual scancode=83)
	.byte $C3	; 84 F3
	.byte $C1	; 85 F1
	.byte $C2	; 86 F2
	.byte $CC	; 87 F12
	.byte $00	; 88 
	.byte $CA	; 89 F10
	.byte $C8	; 8A F8
	.byte $C6	; 8B F6
	.byte $C4	; 8C F4
	.byte $B9	; 8D tab
	.byte $7E	; 8E `~
	.byte $CF	; 8F relocated Print Screen key
	.byte $03	; 90 relocated Pause/Break key
	.byte $A0	; 91 left alt (right alt)
	.byte $00	; 92 left shift
	.byte $E0	; 93 relocated Alt release code
	.byte $00	; 94 left ctrl (and right ctrl)
	.byte $51	; 95 qQ
	.byte $21	; 96 1!
	.byte $00	; 97 
	.byte $00	; 98 
	.byte $00	; 99 
	.byte 'Y'	; 9A zZ
	.byte $53	; 9B sS
	.byte $41	; 9C aA
	.byte $57	; 9D wW
	.byte '"'	; 9E 2@
	.byte $E1	; 9F Windows 98 menu key (left side)
	.byte $02	; A0 relocated ctrl-break key
	.byte $43	; A1 cC
	.byte $58	; A2 xX
	.byte $44	; A3 dD
	.byte $45	; A4 eE
	.byte $24	; A5 4$
	.byte $40	; A6 3#
	.byte $E2	; A7 Windows 98 menu key (right side)
	.byte $00	; A8
	.byte $20	; A9 space
	.byte $56	; AA vV
	.byte $46	; AB fF
	.byte $54	; AC tT
	.byte $52	; AD rR
	.byte $25	; AE 5%
	.byte $E3	; AF Windows 98 option key (right click, right side)
	.byte $00	; B0
	.byte $4E	; B1 nN
	.byte $42	; B2 bB
	.byte $48	; B3 hH
	.byte $47	; B4 gG
	.byte 'Z'	; B5 yY
	.byte '&'	; B6 6^
	.byte $00	; B7
	.byte $00	; B8
	.byte $00	; B9
	.byte $4D	; BA mM
	.byte $4A	; BB jJ
	.byte $55	; BC uU
	.byte '/'	; BD 7&
	.byte '('	; BE 8*
	.byte $00	; BF
	.byte $00	; C0
	.byte ';'	; C1 ,<
	.byte $4B	; C2 kK
	.byte $49	; C3 iI
	.byte $4F	; C4 oO
	.byte '='	; C5 0)
	.byte ')'	; C6 9(
	.byte $00	; C7
	.byte $00	; C8
	.byte ':'	; C9 .>
	.byte '_'	; CA /?
	.byte $4C	; CB lL
	.byte '{'	; CC ;:
	.byte $50	; CD pP
	.byte '?'	; CE -_
	.byte $00	; CF
	.byte $00	; D0
	.byte $00	; D1
	.byte '}'	; D2 '"
	.byte $00	; D3
	.byte 'µ'	; D4 [{
	.byte '|'	; D5 =+
	.byte $00	; D6
	.byte $00	; D7
	.byte $D8	; D8 caps
	.byte $00	; D9 r shift
	.byte $AD	; DA <Enter> shifted!
	.byte '*'	; DB ]}
	.byte $00	; DC
	.byte $27	; DD \|
	.byte $00	; DE
	.byte $00	; DF
	.byte $00	; E0
	.byte '>'	; E1
	.byte $00	; E2
	.byte $00	; E3
	.byte $00	; E4
	.byte $00	; E5
	.byte $08	; E6 bkspace
	.byte $00	; E7
	.byte $00	; E8
	.byte '1'	; E9 kp 1
	.byte '/'+$80	; EA kp / converted from E04A in code
	.byte '4'	; EB kp 4
	.byte '7'	; EC kp 7
	.byte $00	; ED
	.byte $00	; EE
	.byte $00	; EF
	.byte '0'	; F0 kp 0
	.byte ','	; F1 kp .
	.byte '2'	; F2 kp 2
	.byte '5'	; F3 kp 5
	.byte '6'	; F4 kp 6
	.byte '8'	; F5 kp 8
	.byte $1B	; F6 esc
	.byte $00	; F7 num lock
	.byte $CB	; F8 F11
	.byte '+'	; F9 kp +
	.byte '3'	; FA kp 3
	.byte '-'	; FB kp -
	.byte '*'	; FC kp *
	.byte '9'	; FD kp 9
	.byte $CD	; FE scroll lock
	.BYTE $00	; FF 
	.ENDIF		; Mouse
	

;############################################################################
;#               Zeichen-Ausgabe auf Bildschirm oder COM                    #
;############################################################################

; Kommunikationsroutinen
	

omode_stx:		; sende STX (Start Text), warte auf ACK
	LDA #$80
	STA Omode
	LDA #2		; CTRL-B als Startzeichen senden
	JSR SendByte	; 
	LDA #4	
	JSR TickerDelay	; Verarbeitung abwarten
	RTS
	
omode_etx:
	LDA #$00
	STA Omode
	LDA #3		; CTRL-C/ETX als Ende-Zeichen-senden
	JMP SendByte	; nicht auf Screen, sondern Abschlusszeichen seriell

WriteAcc:		; Zeichen in Acc ausgeben
	CMP #$02	; STX? Omode auf AVR-COM/OptoBusCore, $02 senden
	BEQ omode_stx
	CMP #$03	; ETX? Omode auf Screen
	BEQ omode_etx	
	BIT Omode	; wenn $80, Ausgabe auf COM
	BPL WriteScreen
;	BVS WriteStatus	; Fehlermeldungen landen hier mit omode=$40

SendByte:		; Zeichen auf AVR-COM ausgeben
	STA asave
	STY ysave
	LDA COMCH
	BEQ SendByte0

SendByte1:		; SerialCore
	BIT UART1TxS
	BVS SendByte1	; warte bis Buffer nicht mehr voll
	LDA asave
	STA UART1D	; ausgeben
	LDY #0
Ydelay:		; 102 us Pause pro Byte
	DEY
	BNE Ydelay
	LDY ysave
	RTS
		
SendByte0:		; AVR COMport SPI
	BIT Tx0ACK
	BPL SendByte0	; noch kein ACK empfangen?
SendByte0weiter:
	LDA asave
	STA COMport
	LDY #$01	; Byte ausgeben, F_INT kurz triggern
	STY IRQena
	DEY
	STY IRQena	; ist 0
	JSR Ydelay
	CMP #$0D	; Acc=CR?
	BNE SendByte0exit
	LDA #0
	STA Tx0ACK	; ACK löschen nach CR
SendByte0exit:
	LDY ysave
	LDA asave		
	RTS

	

WriteStatus:		; Fehlermeldungen landen hier mit omode=$40

WriteScreen:		; Akku auf Screen, aktualisiert TextPosX/Y
	STA	asave
	STX	xsave	; benutzt nur A und X, sofern keine Controls
	LDX	TextPosX
	STX	TextX
	LDX	TextPosY
	STX	TextY
; I/O-Steuerzeichen behandeln
	CMP 	#$20	; writable?
	BCC 	WriteControls
	
writeChar:
	LDX 	InsFlg
	BEQ 	writeChar1
	STY	ysave
	JSR	Insert		; kehrt mit restore zurück
writeChar1:
	STA	TextWR		; Zeichen in GTEXT-Schreibregister
	LDX	TextPosX
	CPX	TextWinRight	; #colstotal	; Ende der Zeile?
	BEQ	writeCharLF
	INX
	STX	TextX
	STX 	TextPosX
	LDX	xsave
	LDA	asave
	RTS	

writeCharLF:	
	STY	ysave
	LDX	TextWinLeft		; #0		; Zeilenanfang
	STX	TextPosX
	JMP	lf

restore:
	LDA TextPosX
	STA TextX
	LDA TextPosY
	STA TextY
	LDA Color
	STA ColorP
	LDX xsave
	LDY ysave
	LDA asave
	RTS	


WriteControls:
	STY ysave
	JMP DispatchControls

	

vt:	LDX TextPosY
	CPX TextWinTop	; #rowstart
	BEQ restore	; schon ganz oben?
	DEX
	STX TextPosY
	JMP restore

bs:	LDX TextPosX
	CPX TextWinLeft
	BEQ restore	; nicht unter 0
	DEX
	STX TextPosX
	JMP restore
	
ht:	LDX TextPosX
	CPX TextWinRight	; #colstotal-1
	BEQ restore	; nicht über Ende der Zeile
	INX
	STX TextPosX
	JMP restore

lf:	LDX TextPosY
	INC TextPosY
	CPX TextWinBtm	; #rowstotal
	BEQ scroll
	JMP restore

hc:	LDA TextWinTop	; #rowstart	; Home Cursor
	STA TextPosY
	LDA TextWinLeft	; #0
	STA TextPosX
	JMP restore

cr:	LDA #0
	STA InsFlg
	JSR InsertStatus
cr_0:
	LDA TextWinLeft	; #0
	STA TextPosX
cr_end:
	JMP restore

del:	LDX TextPosX
	BEQ del_end	; nicht unter 0
	DEX
	STX TextX
	STX TextPosX
	LDA #$20	; mit Leerzeichen löschen
	STA TextWR
del_end:
	JMP restore
		
scroll:
	LDY TextWinTop	; #rowstart+1
scrollLoop:
	INY
	JSR LineCopyUp
	CPY TextWinBtm	; #rowstotal	; letzte Zeile?
	BNE scrollLoop
	JSR lineclrY
	STY TextPosY
	JMP restore
	
OverlayClear:
; Bigfont-Overlay-Text löschen
	LDA	#' '
	LDY	#11	
OverlayClear_Y:
	LDX	#colstotal/2	
OverlayClear_X:
	STY BtnY
	STX BtnX
	STA BtnWR		; Zeichen in GTEXT-Schreibregister
	DEX
	BPL OverlayClear_X
	DEY
	BPL OverlayClear_Y
	LDA	#0
	RTS
	
ClearFullScreen:
	STA asave
	STX xsave
	STY ysave
ClearFullScreen_0:
	JSR SetFullScreen
	JSR OverlayClear
	LDA	#0
	STA	CursorStyle	; wird in InserStatus zurückgesetzt
	STA	MouseOverlayToggle
	JSR CursorOn
	LDY #rowstotal		; Müll in letzter Zeile+1
	JSR lineclrY
	
	LDA #$02		; CLS von Tastatur oder Reset immer auf grün
	STA Color
	LDA #$00
	STA GFrameC
	JSR FrameClear		; Grafik löschen
	
cls:	LDY #0		; oberste Zeile
	LDX #0
	.IF LCD320
	LDA #$01
	.ELSE
	LDA #7
	.ENDIF	
	STA ColorP
statusloop1:		; Statuszeile ausgeben
	LDA statusline1,X
	BEQ statusloop2	; String-Ende?
	JSR writecharxy
	INX 
	BNE statusloop1	; max. 255 Zeichen
statusloop2:		; Rest der Statuszeile löschen
	.IF LCD320
	.ELSE
	JSR	StatusBar
	.ENDIF
	
	JSR ClearWindow
	JSR CapsStatus
	JSR COMstatus
	JSR BankStatus
	JSR ErrStatus
	JSR InsertStatus
	JMP restore

StatusBar:
	LDX #0
	LDY #1
	LDA #202	; code page 437 "-"
statusbarloop:
	JSR writecharxy
	INX
	CPX #colstotal
	BNE statusbarloop
	RTS

ClearWindow:
	LDA Color	; letzte Grundfarbe
	STA ColorP
	LDA #$20
	LDY TextWinTop	; #rowstart 
clsloop:
	JSR lineclrY
	INY
	CPY TextWinBtm	; #rowstotal+1
	BNE clsloop
	JSR lineclrY
	LDX TextWinLeft
	STX TextX
	STX TextPosX
	LDY TextWinTop	; #rowstart
	STY TextY
	STY TextPosY
	RTS
			
Insert:		
; Bildschirmzeile ab Cursor eins nach rechts
	LDX TextWinRight
	CPX TextPosX	; schon am Ende der Zeile im Fenster?
	BEQ NoInsert
InsLoop:
	DEX
	STX TextX
	LDA TextWR
	PHA
	LDA ColorP
	INX
	STX TextX
	STA ColorP
	PLA
	STA TextWR
	DEX
	CPX TextPosX		; An Cursorposition?
	BNE InsLoop
NoInsert:
	JMP restore


lineclrY:			
; Löscht Zeile in Y
	LDA	#0		; Cursor während Zugriff ausblenden
	STA	CursorStyle	; wird in InserStatus zurückgesetzt
	LDX TextWinLeft		; Ende der Zeile
lineclrFromX:
	STY TextY
	DEX
	LDA #$20		; Leerzeichen
lineclrloop:	
	INX
	STX TextX
	STA TextWR
	CPX TextWinRight
	BNE lineclrloop
	JMP 	UpdateCursorStyle

LineCopyUp:
; kopiert Zeile Y in Zeile Y-1
	LDA	#0		; Cursor während Zugriff ausblenden
	STA	CursorStyle	; wird in InserStatus zurückgesetzt
	STY TextY
	LDX TextWinRight
LineCopyUploop:
	STX TextX
	LDA TextWR
	PHA
	LDA ColorP
	DEY
	STY TextY
	STA ColorP
	PLA
	STA TextWR
	INY
	STY TextY
	DEX
	CPX TextWinLeft	;#colstotal	; Ende der Zeile?
	BPL LineCopyUploop
	JMP 	UpdateCursorStyle

; Zeichen in A in Zeile X und Spalte Y schreiben, ohne Cursorposition zu verändern
WriteCharXY:
	STY TextY
	STX TextX
	STA TextWR		; Zeichen in GTEXT-Schreibregister
restoreTextXY:
	PHA
	LDA TextPosY
	STA TextY
	LDA TextPosX
	STA TextX
	PLA
	RTS
	
ReadCharXY:
	STY TextY
	STX TextX
	LDA TextWR		; Zeichen aus GTEXT-RAM
	JMP restoreTextXY
	
	
SetFullScreen:
;rowstart  = 2	; 
;rowstotal = 39	; 
;colstotal = 80	; 
	LDA #$00
	STA TextWinLeft
	STA TextPosX
	LDA #colstotal-1
	STA TextWinRight
	LDA #rowstart
	STA TextWinTop
	STA TextPosY
	LDA #rowstotal-1
	STA TextWinBtm
	
;	LDA colstotal		; in neuem main.bit nicht mehr nötig
;	STA TextCols	
	
	JMP restoreTextXY

;############################################################################

; Byte in Acc vom FPGA-Modul SPI, von SerialCore oder OptoBusCore
; setzt Carry=1 wenn Byte empfangen, sonst Carry=0 und Acc=0

ReceiveByte:
	STY ytemp
; von AVRCOM- oder UART-IRQ im Buffer abgelegt
	LDY RxPtr	; prüfen, ob etwas empfangen wurde
	CPY iRxPtr
	BEQ ReceiveByteExitNone	; nix da
	INY
	STY RxPtr
	LDA RxBuf,Y

ReceiveByteOK:
	LDY ytemp
	CMP #0
	SEC
	RTS

ReceiveByteExitNone:
	LDY ytemp
ReceiveStringError:
	LDA #0
	CLC
	RTS

;############################################################################


; Universelle String-Input-Routine, behandelt CR/LF und LabReqIgnoreChr
; als Ende-Zeichen
; Für LABINP-Antwort vom Modul setzt sie
; HashSignIdx	= $0716	; Position nach "#" in Ibuffs
; EqualSignIdx	= $0717 ; Position nach "=" in Ibuffs
; Setzt ein (von INPUT entferntes) <"> an den Ibuffs-Anfang $220

ReceiveString:
	LDA 	#10		; Timeout 0,15s
	STA 	TimeOut
	LDX	#0		; Pointer für Ibuffs
	STX	HashSignIdx
	STX	EqualSignIdx
	STX	ColonSignIdx
	LDA	#'"'		; String-Input erwünscht, also mit "-Anfang simulieren
	STA	Ibuffs
	STA	Srchc
	INX
ReceiveString1:
	LDA	#0
	STA	Ibuffs,X	; Mit Ende-Null vorbelegen
	LDA	TimeOut
	BEQ	ReceiveStringError
	JSR	ReceiveByte
	BCC	ReceiveString1
	CMP	#$0A		; evt. übrig von der letzten Antwort, ignorieren
	BEQ	ReceiveString1
	CMP 	#$0D		; CR?
	BEQ 	ReceiveStringEnded
	CMP 	LabReqIgnoreChr
	BEQ 	ReceiveStringRemaining
	CMP 	#'#'		; Startzeichen der Antwort?
	BNE 	ReceiveString2
	STX	HashSignIdx
	INC	HashSignIdx
ReceiveString2:
	CMP 	#':'		; Ergebnis der Antwort?
	BNE 	ReceiveString3
	STX	ColonSignIdx
	INC	ColonSignIdx
ReceiveString3:
	CMP 	#'='		; Ergebnis der Antwort?
	BNE 	ReceiveString4
	STX	EqualSignIdx
	INC	EqualSignIdx
ReceiveString4:
	CPX	#Ibuffe-Ibuffs	; compare character count with max
	BCS	ReceiveStringError	; exit if buffer full, Error!
	STA	Ibuffs,X	; else store in buffer
	INX
	BNE	ReceiveString1	; immer!
	BEQ	ReceiveStringError
	
ReceiveStringEnded:
	SEC
	RTS
	
ReceiveStringRemaining:
; Rest nach LabReqIgnoreChr abrufen (aber nicht speichern) bis CR kommt
	LDA	TimeOut
	BEQ	ReceiveStringError
	JSR	ReceiveByte
	BCC	ReceiveStringRemaining
	CMP 	#$0D		; CR?
	BNE	ReceiveStringRemaining
	LDX	#'X'-'0'		; "Extra Ignored (Rest of Line)"
	STX	LabErrFlags
	JSR 	ErrStatus
	JMP 	ReceiveStringEnded


TickerDelay:	; Timer-Wert in Acc, 60 => 1s
	STA	TimeOut
TickerDelayLoop:
	LDA	TimeOut	; NMI-Timer
	BNE	TickerDelayLoop
	RTS
	


;############################################################################
;#                   OS-Routinen, Hilfsroutinen COM                         #
;############################################################################

;IbuffS nach Zeichen durchsuchen
SearchIbuffAcc:
	LDY	#0	
;hier Start bei Y
SearchIbuffAccY:
	CMP	IBuffS,Y
	BEQ	SearchIbuffAccFound
	INY
	BPL	SearchIbuffAccY
	CLC
	RTS
SearchIbuffAccFound:	; Zeichen an Position Y gefunden, Carry gesetzt
	SEC
	RTS
	
	
; does BASIC Timeout error
AVRCOM_ERR
	LDX	#$1E			; error code $1E ("Timeout" error)
	JMP	LAB_XERR		; do error #X, then warm start
	
;Warten bis Antwort vom AVR (intern) eintrifft und bis Timeout nochmal versuchen	
WriteResponse:
	JSR	ReceiveString
	BCC	WriteResponseExit
	JSR	WriteIbuffS1
	JMP	WriteResponse		; nochmal versuchen
WriteResponseExit:
	RTS

;Ibuff ab Start Y als String ausgeben
WriteIBuffS:
	LDY	#0	
	BEQ	WriteIbuffSlp	
WriteIbuffS1:
	LDY	#1
WriteIbuffSlp:
	LDA	IBuffS,Y		; Adresse des Strings +1 wg. "
	BEQ	WriteIbuffSExit
	JSR	WriteScreen
	INY
	BNE	WriteIbuffSlp
WriteIbuffSExit:
	JMP	Print_CR		; print CR/LF and return
	

; allg. Ausgabe an AVR

; Hilfsroutine zur Screen-Ausgabe eines Strings nach JSR WriteMsg
WriteMsg:		; Put Msg String Routine
	PLA		; Get the return address LSB from stack
	STA Cmdptr	; ..into w/s
	PLA		; Get the return address MSB from stack
	STA Cmdptr+1	; ..into w/s
WriteMsgChar:
	LDY #$0
	INC Cmdptr	; Point to the next byte of embedded text
	BNE WriteMsgNext
	INC Cmdptr+1	; Increment MSB text pointer if carried over
WriteMsgNext:
	LDA (Cmdptr),Y	; Get the next character of embedded text
	BMI WriteMsgExit; ..reached terminator - execute from here
	JSR WriteScreen	; Send character to Screen
	JMP WriteMsgChar; ..and go for the next character
WriteMsgExit:
	JMP (Cmdptr)	; Continue execution from the negative byte

; Hilfsroutine zur COM-Ausgabe eines Strings nach JSR PutMsg
SendMsg:		; Put Msg String Routine
	PLA		; Get the return address LSB from stack
	STA Cmdptr	; ..into w/s
	PLA		; Get the return address MSB from stack
	STA Cmdptr+1	; ..into w/s
SendMsgChar:
	LDY #$0
	INC Cmdptr	; Point to the next byte of embedded text
	BNE SendMsgNext
	INC Cmdptr+1	; Increment MSB text pointer if carried over
SendMsgNext:
	LDA (Cmdptr),Y	; Get the next character of embedded text
	BMI SendMsgExit	; ..reached terminator - execute from here
	JSR SendByte	; Send character to AVR
	JMP SendMsgChar	; ..and go for the next character
SendMsgExit:
	JMP (Cmdptr)	; Continue execution from the negative byte

; Integer-Ausgabe 16 Bit in A (MSB), X (LSB) auf Screen
WriteInteger =	LAB_295E	; print XA as unsigned integer

; Integer-Ausgabe 16 Bit in A (MSB), X (LSB) auf COM-Kanal
SendInteger:
	STA FAC1_1	; save high byte as FAC1 mantissa1
	STX FAC1_2	; save low byte as FAC1 mantissa2
	LDA #$00
	STA FAC1_3	; clear FAC1 mantissa3
	LDX #$90	; set exponent to 16d bits
	STX FAC1_e	; set FAC1 exponent
	BNE SendIntegerFAC	; immer
	
; Bank * $10000 plus Startadresse übermitteln
SendBankStart:
	LDA CurrBank	; Bank-Adresse (*$10000) addieren
	STA FAC2_1
	LDA #0
	STA FAC2_2
	STA FAC2_3
	STA FAC2_s
	STA FAC1_r
	LDA #$98
	STA FAC2_e
	JSR LAB_2474	; add FAC2 to FAC1
SendIntegerFAC:
	SEC		; set integer is +ve flag
	LDA #$00
	STA FAC1_r	; clear FAC1 rounding byte
	STA FAC1_s	; clear FAC1 sign (b7)
	JSR LAB_24D0	; do ABS and normalise FAC1
	LDY #$00	; clear index
	TYA		; clear A
	JSR LAB_297B	; convert FAC1 to string, skip sign character save
	JSR LAB_20AE	; print " terminated string to Sutill/Sutilh
	JSR LAB_22B6	; pop string off descriptor stack, or from top of string
	LDY #$00	; reset index
	TAX		; copy length to X
	BEQ SendIntegerExit	; exit (RTS) if null string
SendIntegerLoop:
	LDA (ut1_pl),Y	; get next byte
	JSR SendByte	; go output the character
	INY		; increment index
	DEX		; decrement count
	BNE SendIntegerLoop	; loop if not done
SendIntegerExit:
	RTS

;############################################################################
	
SendScratchStr:		; 0-Terminated-String im scratch-Buffer an COM übermitteln
	LDX #0
SendScratchStrLoop:
	LDA scratch,x
	BEQ SendScratchStrExit
	JSR SendByte
	INX
	BNE SendScratchStrLoop
SendScratchStrExit:
	RTS

; String-Expression in BASIC auswerten und in Ibuffs und scratch kopieren	
EvaStrToScratch:
	JSR	LAB_EVEX
	JSR	LAB_EVST	; evaluate string
; String in Ibuf kopieren, Länge in A
	TAX
	LDY #0
EvaStringLoop:
	LDA (ut1_pl),Y		; get byte from string
	STA	scratch,Y	; Kopie anlegen
	INY
	CPY	#15		; nicht länger als 15 Zeichen
	BEQ	EvaStringExit
	DEX
	BNE EvaStringLoop
EvaStringExit:
	LDA #0
	STA	scratch,Y	; Kopie anlegen
	RTS	; StringEnde+1 (0) jetzt in Y
	
COMswitch:
	LDA COMCH
	STA COMCHsave
	LDA #0
	STA COMCH
	JSR LAB_FlushRx		; COM Buffer leeren
	RTS


;############################################################################
;############################################################################
;#        OS-Routinen, RST, IRQ-Aufbereitung und Initialisierung            #
;############################################################################
;############################################################################


Reset:
	SEI		; IRQ sperren bis IRQ-Routine ins RAM kopiert
	CLD		; clear decimal mode
	
	LDX #$FF	; empty stack
	TXS		; set the stack
	
	LDA	#<WriteAcc
	STA	WritejpL	; alten Write-Vektor wiederherstellen
	LDA	#>WriteAcc
	STA	WritejpH
	LDA	#<GetKey
	STA	GetkeyjpL	; alten GetKey-Vektor wiederherstellen
	LDA	#>GetKey
	STA	GetkeyjpH
	JSR	PlotInit1	; kehrt mit Acc=0 zurück
	STA COMCH
	STA IRQena
	STA iRxPtr
	STA RxPtr
	STA KbdPtr
	STA iKbdPtr
	STA CapsFlg	; UpCase-Flag, $20=Basic, $00=Terminal
	STA GetbFlg
	STA Omode	; 0 wenn PRINT, $80 wenn LABOUT auf COM
	STA Gsmall
	STA GFrameC	; Fenster-Hintergrund
	LDA IRQack
	LDA IRQclr
	LDA CurrBank
	STA Bank
	STA AccessBank	; für PEEKs/POKEs
	STA	PS2sendStackPtr
	
	LDA #$80	; FlowControl einschalten
	STA Tx0ACK

	STA GsizeX
	LDA #50		; Grafik-Fenster
	STA GstartX
	LDA #36		; Grafik-Fenster
	STA GstartY
	LDA #$60	; Grafik-Fenster
	STA GsizeY
	
	JSR ClearFullScreen
	
	JSR CapsToggle
	JSR COMstatus
	JSR	BankStatus
	LDA	#0	
	STA Timer60
	CLI
	.IF Mouse
; PS/2-Maus initialisieren	
	STA	MouseButtons
	STA	MouseButtonsOld
	STA	MouseBufPtr
	STA	MouseSync
	LDA	#1
	STA	MouseStyle
	LDA	#$20
	STA	MousePosX
	STA	MousePosY
	STA	MouseX
	STA	MouseY
	LDA	#$F4		; Stream report on
	STA	PS2sendStack+3
	LDA	#$E7		; Set Scaling
	STA	PS2sendStack+2
	LDA	#$E8		; Set Resolution
	STA	PS2sendStack+1
	LDA	#$01		; 2 Count/mm
	STA	PS2sendStack+0
	LDA	#4
	STA 	PS2sendStackPtr	; immer zuletzt
	.ELSE
	
; PS/2-Tastatur initialisieren	
	STA	MouseStyle	; kein Zeiger
	LDA	#$F6	
	STA	PS2sendStack+2
	LDA	#$ED	
	STA	PS2sendStack+1
	LDA	#$04		; CapsLock	
	STA	PS2sendStack
	LDA	#3
	STA 	PS2sendStackPtr	; immer zuletzt
	.ENDIF		; Mouse

; now do the signon message, Y = $00 here

LAB_signon
	LDA 	#10
	JSR	TickerDelay
	JSR 	COMswitch
	LDA 	#$0D
	STA	Rbyte1	; initial Random Seed
	JSR 	SendByte
	LDA 	#5
	STA	Rbyte3
	JSR	TickerDelay
	JSR 	LAB_FlushRx
	LDA 	#$00
	STA 	PS2breakFlag
	STA 	PS2shiftFlag
	STA 	PS2ctrlFlag
	STA 	PS2altFlag
	STA 	PS2specialFlag
	STA	peekEvent
	LDA 	COMCHsave
	STA 	COMCH
	JSR 	LAB_FlushRx
	
	.IF mouse
	.ELSE
	.IF autostart
	.ELSE
; keine Abfrage bei Maus- und Autostart-Version
	JSR 	WriteMsg
	.BYTE $0D,$0D,$0A,"[C]old/[W]arm/[M]onitor/[H]elp ?"	
	NOP
LAB_nokey
	JSR GetKey		; call scan input device
	BCC LAB_nokey		; loop if no key
	AND #$DF		; mask xx0x xxxx, ensure upper case
	CMP #'W'		; compare with [W]arm start
	BEQ LAB_dowarm		; branch if [W]arm start
	CMP #'M'		; compare with [M]onitor
	BEQ LAB_doMon		; branch if Monitor
	CMP #'H'		; compare with [W]arm start
	BEQ LAB_BASIChelp	; branch if [H]elp
 	CMP #'C'		; compare with [C]old start
	BNE LAB_nokey		; loop if not [C]old start
	
	.ENDIF	; mouse
	.ENDIF	; autostart
	
LAB_doCold:
	LDA #0
	LDY #$07
ResetPageLoop:
	STY Bank
	STA RAM_base
	STA RAM_base+1
	STA RAM_base+2
	DEY
	BPL ResetPageLoop
	STA CurrBank
	STA AccessBank
	JSR Bankstatus
	LDA	#$A5
	STA	FunnyComp	
	JMP LAB_COLD		; do EhBASIC cold start
LAB_dowarm:
	.IF mouse
	.ELSE
	.IF autostart
	.ELSE
	LDA	FunnyComp
	CMP	#$A5
	BNE	LAB_doCold	; erst nach erfolgreichem Kaltstart einen Warmstart zulassen
	.ENDIF	; mouse
	.ENDIF	; autostart
	
	JSR Bankstatus
LAB_release2
	JSR GetKey		; call scan input device
	BCS LAB_release2	; loop if key
	JMP LAB_WARM		; do EhBASIC warm start
LAB_again:
	JMP Reset

	.IF mouse
	.ELSE

LAB_BASIChelp:
LAB_release3
	JSR GetKey		; call scan input device
	BCS LAB_release3	; loop if key
	JSR 	ClearFullScreen
	JSR 	COMswitch
	LDA	#$80	; ACK vorbelegen
	STA Tx0ACK
	JSR 	SendMsg
	.ASCII "9:TTY=BASHELP.TXT",$0D
	NOP
;	JMP	BREAK
	JSR WriteResponse
	LDA COMCHsave
	STA COMCH
	JMP LAB_signon	; do EhBASIC warm start
	
LAB_doMon:
	JMP	MonitorBoot	

	.ENDIF	; mouse

;############################################################################
;#                            IRQ- und BRK-Handler                          #
;############################################################################
; BRK-Handler entfernt 06/2010

; CMs IRQ Handler: COM-, VS60Hz- und SPI-IRQ auswerten
	.IF Mouse
IRQ_COM_JMP:
	JMP	IRQ_COM
	.ENDIF

IRQentry:
	PHA
	TYA	; PHY führt hier zu sporadischen Fehlern!
	PHA
	LDA IRQsrc	; HW-Interrupt-Quelle vorrangig
	AND #$0F	; ungültige ausblenden
	STA irqs	; retten	
; IRQs zuordnen: Bit 0 ist PS/2
IRQ_PS2:
	LSR 	irqs	; Saved IRQsrc
;	BEQ 	IRQexit	; sonst nix drin?	
	BCC 	IRQ_COM_JMP	; JMP auf IRQ_COM
	
	.IF Mouse
	
; Maus ist im Stream Mode, sendet also selbsttätig Bewegungen
	LDA	MouseSync	; Inaktivitäts-Timer abgelaufen?
	BNE	IRQ_MouseSync_1
IRQ_MouseSync_0:
	LDA	#$00
	STA	MouseBufPtr	
IRQ_MouseSync_1:
	LDA	#2
	STA	MouseSync	; Timeout für Maus-Aktivität setzen
	
	LDY	MouseBufPtr
	CPY	#3	; Überlauf?	
	BEQ	IRQ_MouseSync_0	; wieder auf -1 + INY
	LDA	PS2code		; Rohdaten vom Port
	STA	MouseBuf,Y	; alter MouseBuf-Pointer
	INY
	STY	MouseBufPtr
	CPY	#3		; Vollzähliger Datensatz?	
	BNE	IRQ_COM_JMP	; nein, noch nicht
; Maus-Datensatz ist jetzt vollständig und kann dekodiert werden
	
; Maus-Bewegung auswerten
IRQ_MouseMoveX:
	CLC
	LDA	MouseBuf+1	; X-Movement
	BEQ	IRQ_MouseMoveY
	ADC	MousePosX
	.IF VGA640
	CMP	#11
	BCC	IRQ_MouseMoveY
	CMP	#171
	.ELSE
	CMP	#0
	BCC	IRQ_MouseMoveY
	CMP	#120		; für 480x234 Panel
	.ENDIF
	BCS	IRQ_MouseMoveY
	STA	MouseX
	STA	MousePosX
	.IF VGA640
	SEC			; Umrechnen in Text-Koordinaten
	SBC	#11		; Offset VGA-IF abziehen	
	.ENDIF
	LSR
	STA	MouseTextX
IRQ_MouseMoveY:
	LDA	MouseBuf+2
	BEQ	IRQ_MouseButtons
	SEC
	LDA	MousePosY	; Y-Movement
	SBC	MouseBuf+2
	.IF VGA640
	CMP	#6
	BCC	IRQ_MouseButtons

	CMP	#124
	.ELSE
	CMP	#0
	BCC	IRQ_MouseButtons
	CMP	#58		; für 480x234 Panel	
	.ENDIF
	BCS	IRQ_MouseButtons
	STA	MousePosY
	STA	MouseY
; Umrechnen in Text-Koordinaten: -6 /3
	.IF VGA640
	SEC			
	SBC	#6
	.ENDIF
	CLC
; Wertebereich nun 0..117 oben .. unten bei VGA
; einfache Reihenentwicklung für Faktor 1,3333 (0,33333*8)
; y = (n + n/4 + n/16..)/8
	STA	irqa
	LSR	irqa
	LSR	irqa
	ADC	irqa
	LSR	irqa
	LSR	irqa
	ADC	irqa
	LSR
	LSR
	STA	MouseTextY
	
; Maus-Button-Behandlung, linke Taste
IRQ_MouseButtons:
	LDA	MouseBuf
	AND	#$07
	CMP	#7		; Notfall: alle Buttons gleichzeitig?
	BEQ	IRQ_MouseReset
	STA	MouseButtons
	CMP	MouseButtonsOld
	BEQ	IRQ_MouseButtonExit

	STA	peekScancode	; enthielt MouseButtons
	LDA	#0
	STA	peekKey		; noch kein Token zugeordnet
	LDA	#2		; Maus-Events 
	STA	peekEvent

IRQ_MouseButtonExit:	
IRQ_MouseExit:
	LDA	MouseButtons
	STA	MouseButtonsOld
	JMP	IRQ_COM
	
IRQ_MouseReset:
	JMP	Reset

	.ELSE
	
; war Bit 0, also PS/2
	LDY	#$80		; für zu setzende Flags
	LDA	PS2breakFlag
	BNE	IRQ_PS2_0	; es folgt nur der Loslass-Code
	
	LDA	PS2code		; Rohdaten vom Port
; Shift, Controls und Sondertasten umsetzen
	CMP	#$F0		; neuer Break-Code?
	BNE	IRQ_PS2_A
	STY	PS2breakFlag
	BEQ	IRQ_COM_JMP	; ignorieren
IRQ_PS2_A:
	CMP	#$FA		; Acknowledge?
	BEQ	IRQ_COM_JMP	; ignorieren
IRQ_PS2_B:
	CMP	#$E0		; Special Code?
	BNE	IRQ_PS2_C
	STY	PS2specialFlag
	BEQ	IRQ_COM_JMP
IRQ_PS2_C:
	BNE	IRQ_PS2_1	; immer

; Taste wurde losgelassen
IRQ_PS2_0:
	LDY	#0
	STY	PS2breakFlag	; löschen
	STY	PS2specialFlag	; löschen
	LDA	PS2code		; Rohdaten vom Port
	CMP	#$12
	BNE	IRQ_PS2_0A
	STY	PS2shiftFlag
	BEQ	IRQ_COM		; immer
IRQ_PS2_0A:
	CMP	#$59
	BNE	IRQ_PS2_0B
	STY	PS2shiftFlag
	BEQ	IRQ_COM		; immer
IRQ_PS2_0B:
	CMP	#$14
	BNE	IRQ_PS2_0C
	STY	PS2ctrlFlag
IRQ_PS2_0C:
; nichts weiter machen
IRQ_COM_JMP:
	JMP	IRQ_COM		; immer

; Taste wurde gedrückt
IRQ_PS2_1:
	LDA	PS2code		; Rohdaten vom Port
	CMP	#$12
	BNE	IRQ_PS2_1A
	STY	PS2shiftFlag
	BEQ	IRQ_COM		; immer
IRQ_PS2_1A:
	CMP	#$59
	BNE	IRQ_PS2_1B
	STY	PS2shiftFlag
	BEQ	IRQ_COM		; immer
IRQ_PS2_1B:
	CMP	#$14
	BNE	IRQ_PS2_1C
	STY	PS2ctrlFlag
	BEQ	IRQ_COM		; immer
IRQ_PS2_1C:
	
; neues Zeichen in Kbd-Buffer speichern
	STA	peekScancode
	TAY
	BIT 	PS2specialFlag		; E0-Codes?
	BMI 	IRQ_PS2_1D		; dann Controls nicht maskieren
	
	BIT 	PS2ctrlFlag		; Ctrl?
	BMI 	IRQ_PS2_maskControls
IRQ_PS2_1D:	
	ORA	PS2specialFlag
	STA	peekScancode
	BMI 	IRQ_PS2_storeBuf	; E0-Codes
	LDA 	PS2toASCII,Y		; Default: Unshifted
	BIT 	PS2shiftFlag
	BPL 	IRQ_PS2_storeBuf	; kein Shift?
	LDA 	PS2toASCIIshifted,Y	; Shifted Key
	JMP 	IRQ_PS2_storeBuf
IRQ_PS2_maskControls:
	LDA 	PS2toASCII,Y	; Default: Unshifted
	AND 	#$0F
IRQ_PS2_storeBuf:
	LDY	#1		; Tastatur-Events "1"
	STY	peekEvent
	STA 	peekkey
;	STA 	$0600	;Test
	LDY 	iKbdPtr
	INY		; Zeiger erhöhen, weil Byte eingetroffen
	STA 	KbdBuf,Y
	STY 	iKbdPtr	
		
	.ENDIF		; Mouse
	
; war Bit 1, also AVRCOM
IRQ_COM:
	LSR 	irqs	; Saved IRQsrc
	BCC	IRQ_UART
	LDA COMport	; COM-Byte von SPI eingetroffen
	BEQ IRQ_UART	; Null-byte, nix machen
	CMP #$06
	BNE IRQ_COM_store
	LDA #$80	; ACK feststellen, aber nicht speichern
	STA Tx0ACK
	
; Bit 2, also UART?
IRQ_UART:
	LSR 	irqs	; Saved IRQsrc
	BCC	IRQ_SPI
	BIT UART1RxS	; Rx Status, Bit 7 = data present
	BPL IRQ_SPI	; Bit 7 = "0", nix mehr da
	LDA UART1D	; Byte abholen, setzt auch Rx_ack
	LDY iRxPtr
	INY		; Zeiger erhöhen, weil Byte eingetroffen
	STA RxBuf,Y	; in Eingangspuffer
	STY iRxPtr
	JMP IRQ_UART	; noch was da?
	
IRQ_COM_store:
	LDY iRxPtr	; war AVRCOM oder UART
	INY		; Zeiger erhöhen, weil Byte eingetroffen
	STA RxBuf,Y	; in Eingangspuffer
	STY iRxPtr

; Bit 3, also SPI?
IRQ_SPI:
	LSR 	irqs	; Saved IRQsrc
	BCC	IRQexit		
	LDY 	iKbdPtr	; war SPI-Key
	LDA 	SPIkey	; Keyboard-Byte von SPI eingetroffen
	CMP	#10	; LF wird als CR gespeichert
	BNE	IRQ_SPI_storeAsCR
	LDA	#13
IRQ_SPI_storeAsCR:
	CMP	#13	; CR wird immer gespeichert
	BEQ	IRQ_SPI_store
	CMP	#32	; kleiner SPACE?
	BCC 	IRQexit	; Controls, nix machen	
	JSR	UpCaseConvert
IRQ_SPI_store:
	INY		; Zeiger erhöhen, weil Byte eingetroffen
	STA KbdBuf,Y
	STY iKbdPtr
	LDY	#3		; SPI-Events "1"
	STY	peekEvent
	STA 	peekkey
		
IRQexit:
	LDY IRQack	; IRQsrc gelesen, FIFO nächsten Eintrag, falls vorhanden
	PLA
	TAY
	PLA
	RTI

;############################################################################

; 60Hz Ticker NMI, reentrant zu IRQ
; PS2sendStackPtr	= $07E0	; Anzahl der PS/2-Bytes zum Gerät (max. 5)
; PS2sendStack = $07E8	; 5 Bytes an PS/2, wird von oben abgearbeitet

NMIentry:
	PHA
	TYA
	PHA
;	LDA NMIack	; NMI angenommen
	LDY	PS2sendStackPtr
	BEQ	NMI_PS2sent	; alles gesendet?
	DEY
	LDA	PS2sendStack,Y
	STA	PS2code		; hat 1 Frame Zeit zum Senden
	STY	PS2sendStackPtr
NMI_PS2sent:	
	LDA 	TimeOut	
	BEQ 	NMI_noDecTimer
	DEC 	TimeOut
NMI_noDecTimer:
	DEC 	Timer60	; war VSync
	.IF Mouse
	LDA 	MouseSync	
	BEQ 	NMI_noDecMouseSync
	DEC	MouseSync
NMI_noDecMouseSync:
	.ENDIF
	PLA
	TAY
	PLA
	RTI


HelpTxt        .BYTE $0d, $0a
               .byte "x = HEX 4 char max~"
               .byte "x.x - Hexdump~"
               .byte "x:DD[ DD] - Change bytes~"
               .byte "xG - Go~"
               .byte "xL - Text/Hex List~"
               .byte "x.x>xI - Move down~"
               .BYTE "x.x>xM - Move up~"
               .byte "x.xQ - Text dump~"
               .byte "R - Regs~"
               .byte "V - Version~"
               .byte "X - Exit~"
               .byte $00
Porttxt        .byte "6502 Mon v5.1b"
               .byte $0d, $0a
               .byte "(? for help)"
               .BYTE $00	;, "EhBasic by Lee Davison"
               
statusline1:
	.IF LCD320
	.db "EhBASIC #2.3j BANK   LABBUS            "
	.DB 0	; String-Ende, max. 40 Zeichen!
	.ENDIF

	.IF Mouse

	.IF LCD480
	.DB 134,135,136,32,131,132,133,32,128,129,130,"   #2.3j/cm  " 
	.db "BANK    LABERR    LABBUS          ",146,147
	.DB 0	; String-Ende, max. 60 Zeichen!
	.ENDIF
	.IF VGA640
	.DB "RUN STOP LOAD    c't-Lab EhBASIC #2.3j/cm   " 
	.db "BANK    LABERR    LABBUS         CLS"
	.DB 0	; String-Ende, max. 80 Zeichen!
	.ENDIF

	.ELSE
	
	.IF LCD480
	.DB "c't-Lab EhBASIC #2.3j   " 
	.db "BANK    LABERR    LABBUS            "
	.DB 0	; String-Ende, max. 79 Zeichen!
	.ENDIF
	.IF VGA640
	.DB "c't-Lab EhBASIC Terminal #2.3g by cm        " 
	.db "BANK    LABERR    LABBUS            "
	.DB 0	; String-Ende, max. 79 Zeichen!
	.ENDIF

	.ENDIF

	               
	*= $FFF4		; CALL -12 Terminal, -9 Monitor
	JMP Terminal
	
	.IF	Mouse
	.ELSE
	JMP MonitorBoot
	.ENDIF
	
	
; system vectors
	*= $FFFA
SysVec:
	.word	NMIentry	; NMI vector
	.word	Reset		; RESET vector
	.word	IRQentry	; IRQ vector


	.END
