; > Interface
; Interface to host system
; IO commands/functions/queries/etc

; Done, tested:
; CLS, CLG, COLOUR, MODE, =MODE, =POS, =VPOS, OSCLI, DRAW, MOVE, PLOT, GCOL, VDU
; =POINT, =VDU, TIME=, TIME$=, =TIME, =TIME$, SOUND ON|OFF, =TIME$ checks for null return
; GET$#chn, BPUT#chn,s$, GCOL c, COLOUR l,[p|r,g,b], PLOT x,y, SOUND, ENVELOPE, LOAD s$,n
; 09-Mar-2009: LoadProgram can load and tokenise text file


; VDU Commands
; ============

; CLS - Clear Text Window
; =======================
.cmdCLS
clrb SV_COUNT
mov #12,r0
jmp IO_WRCH

; CLG - Clear graphics window
; ===========================
.cmdCLG
mov #16,r0
jmp IO_WRCH

; DRAW x,y - Send DRAW command
; ============================
.cmdDRAW
mov #5,-(sp)		; DRAW is PLOT 5
br  cmdMOVEetc

; MOVE x,y - Send MOVE command
; ============================
.cmdMOVE
mov #4,-(sp)		; MOVE is PLOT 4
.cmdMOVEetc
jsr pc,EvalInteger
mov r4,-(sp)		; Save X coord
br  cmdPLOTetc

; PLOT [k,]x,y - Send PLOT command
; ================================
.cmdPLOT
mov #69,-(sp)		; PLOT is PLOT 69
jsr pc,EvalInteger
mov r4,-(sp)		; Save X coord or PLOT command
jsr pc,EvalComma	; Get Y coord or X coord
jsr pc,CheckEndStatement
beq cmdPLOTxy		; Jump to do PLOT 69,x,y
mov (sp),2(sp)		; Move first param to command
mov r4,(sp)		; Save second param as X coord
.cmdPLOTetc
jsr pc,EvalComma	; Get Y coord
.cmdPLOTxy
mov (sp)+,r3		; R3=X, R4=Y
mov (sp)+,r2		; R0=command
mov #25,r0
jsr pc,IO_WRCH		; PLOT
mov r2,r0
jsr pc,IO_WRCH		; k
mov r3,r0
jsr pc,WrchWord		; X low byte, high byte
mov r4,r0		; Y low byte, high byte
.WrchWord
jsr pc,IO_WRCH		; low byte
swab r0
jmp IO_WRCH		; high byte

; MODE m - Set screen mode
; ========================
.cmdMODE
clrb SV_COUNT
jsr pc,EvalInteger
mov #22,r3
br  WrchR3R4

; GCOL [a,]c - Set graphics colour
; ================================
.cmdGCOL
clr -(sp)		; Prepare for GCOL c
jsr pc,EvalInteger
jsr pc,CheckEndStatement
beq cmdGCOL2
mov r4,(sp)		; Overwrite action on stack
jsr pc,EvalComma
.cmdGCOL2
mov (sp)+,r3		; r3=action, r4=colour
mov #18,r0
jsr pc,IO_WRCH
.WrchR3R4
mov r3,r0
jsr pc,IO_WRCH
.WrchR4
mov r4,r0
jmp IO_WRCH

; COLOUR l[,p|,r,g,b] - Set text or palette colour
; =================================================
.cmdCOLOUR
jsr pc,EvalInteger
mov #17,r3
jsr pc,CheckEndStatement
beq WrchR3R4		; Jump with COLOUR l
mov r4,-(sp)		; Save logical colour
jsr pc,EvalComma	; Get p or r
mov r2,r3		; Prepare r,g,b=0
mov r2,r1
jsr pc,CheckEndStatement
beq cmdCOLOURrgb	; Jump to do COLOUR l,p
mov r4,-(sp)		; Save r
jsr pc,EvalComma
mov r4,-(sp)		; Save g
jsr pc,EvalComma	; Get b
mov r4,r1		; r1=b
mov #16,r4		; r4=p=16 for screen
tst 4(sp)		; Check stacked logical colour
bpl cmdCOLOURscrn
mov #24,r4		; l<0, set r4=p=24 for border
.cmdCOLOURscrn
mov (sp)+,r2		; r2=g
mov (sp)+,r3		; r3=r
.cmdCOLOURrgb		; (sp)=l, r4=p, r3=r, r2=g, r1=b
mov #19,r0
jsr pc,IO_WRCH		; VDU 19
mov (sp)+,r0
jsr pc,IO_WRCH		; VDU 19,l
;mov r4,r0
;jsr pc,IO_WRCH		; VDU 19,l,p
jsr pc,WrchR4		; VDU 19,l,p
mov r3,r0
jsr pc,IO_WRCH		; VDU 19,l,p,r
mov r2,r0
jsr pc,IO_WRCH		; VDU 19,l,p,r,g
mov r1,r0
jmp IO_WRCH		; VDU 19,l,p,r,g,b

; VDU n[,|;[n]] - Send to VDU stream
; ==================================
.cmdVDUsemi
mov r4,r0
swab r0
jsr pc,IO_WRCH		; Send top byte
.cmdVDU
jsr pc,CheckEndStatement
beq cmdVDUexit		; End of statement, exit
jsr pc,EvalInteger
jsr pc,WrchR4		; Send low byte to output
jsr pc,SkipSpaces
inc r5			; Step past comma, semi or bar
cmp r0,#ASC","
beq cmdVDU		; Loop back if comma
cmp r0,#&3B		; Semicolon
beq cmdVDUsemi		; Loop back to send high byte for semi
mov #8,r1		; Prepare to send 8+1 zeros
cmp r0,#ASC"|"
beq cmdOFFzero		; Jump to send zeros if bar
dec r5			; Point back to current character
.cmdVDUexit
rts pc

; OFF - turn cursor off
; =====================
.cmdOFF
clr r1
br cmdOFF1

; ON - turn cursor on
; ===================
.cmdOFFon
mov #1,r1
.cmdOFF1
mov #23,r0
jsr pc,IO_WRCH
mov #1,r0
jsr pc,IO_WRCH
mov r1,r0
jsr pc,IO_WRCH
mov #7,r1
.cmdOFFzero
clr r0
.cmdOFFlp
jsr pc,IO_WRCH
dec r1
bne cmdOFFlp
rts pc


; Character Input Functions
; =========================

; =GET$[#chn] - wait for character from input or string from channel
; ==================================================================
; Also =GET$(x,y) - read char from screen
.fnGETs
CMP  R0,#ASC"#"
BNE  fnGETrdch
JSR  PC,EvalHashVal	; Get channel number
MOV  R4,R1		; R1=channel
ADR  SV_STRING,R4	; Point to string buffer
CLR  R3			; String length=0
.fnGETlp
JSR  PC,IO_BGET
BCS  fnGETend		; End of file
CMP  R0,#10
BEQ  fnGETend		; LF - end of line
CMP  R0,#13
BEQ  fnGETend		; CR - end of line
MOVB R0,(R4)+		; Store in string buffer
INC  R3
CMP  R3,#255
BCS  fnGETlp		; Loop for up to 255 characters
.fnGETend
ADR  SV_STRING,R4	; R4=string start, R3=length
MOV  #&8000,R2		; R2=string
RTS  PC

.fnGETrdch
JSR  PC,IO_RDCH		; Wait for key
MOV  #1,R3		; R3=string length
.fnReadChar
ADR  SV_STRING,R4	; R4=>string buffer
MOV  R0,(R4)		; Put character into buffer
MOV  #&8000,R2		; R2=string type
RTS  PC

; =INKEY$ time - wait specified time for character
; ================================================
.fnINKEYs
JSR  PC,fnINKEY		; call INKEY routine
MOV  R4,R0		; Move result to R0
INC  R3			; Change -1/0 to 0/1
BR   fnReadChar		; Jump to store 0 or 1 characters

; =INKEY time - wait specified time for character
; ===============================================
.fnINKEY
JSR  PC,EvalIntVal	; Evaluate parameter
MOV  R4,R1		; R1=parameter b0-b7 (and b8-b15)
SWAB R4
MOV  R4,R2		; R2=parameter b8-b15
BIC  #&FF00,R2
MOV  #&81,R0		; R0=INKEY
JSR  PC,IO_BYTE		; Read keypress
MOV  R1,R0
BCC  fnGETreturn
MOV  #&FFFF,R4		; R4/3=-1
MOV  #&FFFF,R3
CLR  R2			; R2=integer type
RTS  PC

; =GET - wait for character from input
; ====================================
; Also =GET(port), =GET(x,y) - read char from screen
.fnGET
JSR  PC,IO_RDCH		; Wait for key
.fnGETreturn
MOV  R0,R4		; R4=keypress
CLR  R3			; R3=b16-b31=0
CLR  R2			; R2=integer type
RTS  PC

; =POS - read horizontal cursor position
; ======================================
.fnPOS
JSR PC,fnVPOS
MOV R1,R4
BIC #&FF00,R4
CLR R2
RTS PC

; =VPOS - read vertical cursor position
; =====================================
.fnVPOS
MOV #&86,R0
JSR PC,IO_BYTE
;.fnReturnR2
MOV R2,R4
CLR R3
CLR R2
RTS PC

; =MODE - read current screen mode
; ================================
.fnMODE
mov #&87,r0
jsr pc,IO_BYTE
MOV R2,R4
br  fnReturnExtend

; =POINT(x,y) - read colour at point
; ==================================
.fnPOINT
jsr pc,EvalInteger	; Get X parameter
mov r4,-(sp)		; Save it
jsr pc,EvalComma	; Step past ',', get Y parameter
jsr pc,CheckClose	; Step past ')'
mov r4,MOS_BUF+2	; Put Y in control block
mov (sp)+,MOS_BUF+0	; Put X in control block
adr MOS_BUF,r1
mov #9,r0
jsr pc,IO_WORD		; Call POINT routine
movb MOS_BUF+4,r4	; Get pixel value
.fnReturnExtend
sxt r3			; Sign extend into r3
clr r2			; Type=Integer
rts pc

; =VDU n - read VDU variable
; ==========================
.fnVDU
jsr  pc,EvalIntVal	; Get VDU variable number
mov  #160,r0
mov  r4,r1
jsr  pc,IO_BYTE		; Read VDU variable
.fnReturnR1
mov  r1,r4		; r4=vdu variable
; allow 24-bit return values
mov  r2,r3
swab r3
bic  #&FF00,r3
;clr r3			; r3=0000
clr  r2			; Type=Integer
.cmdBPUTend
rts pc

; =ADVAL device - read device status
; ==================================
.fnADVAL
jsr pc,EvalIntVal
mov r4,r1
mov r3,r2
mov #&80,r0
jsr pc,IO_BYTE
br  fnReturnR1


; File Commands
; =============

; CLOSE#chn - Close channel
; =========================
.cmdCLOSE
jsr pc,EvalHash		; Step past '#', get integer
mov r4,r1		; Pass handle to R1
clr r0
jmp IO_FIND

; BPUT#chn,[byte|string(;)] - Write byte or string to channel
; ===========================================================
.cmdBPUT
jsr pc,EvalHash		; Step past '#', get integer handle
mov r4,-(sp)		; Save handle
jsr pc,CheckComma	; Step past ','
jsr pc,Evaluate		; Get data to send
bmi cmdBPUTs		; Send string
jsr pc,EnsureInteger	; Convert float
mov (sp)+,r1		; Get handle to R1
mov r4,r0		; Move byte to R0
jmp IO_BPUT		; Call MOS to write byte

.cmdBPUTs
mov (sp)+,r1		; Get handle to R1
tst r3
beq cmdBPUTzero		; Zero-length string
.cmdBPUTlp
movb (r4)+,r0
jsr pc,IO_BPUT		; Send character
dec r3
bne cmdBPUTlp		; Loop until all sent
.cmdBPUTzero
jsr pc,SkipSpaces
inc r5			; Step past ';'
cmp r0,#&3B		; Terminating ';'?
beq cmdBPUTend		; Don't output end-of-line
dec r5			; No ';', step back again
mov #13,r0
jmp IO_BPUT		; Write end-of-line terminator

; PTR#chn=val - Set file pointer
; ==============================
.cmdPTR
mov #1,r0		; R0=1 for write PTR
br  cmdPTR1

; EXT#chn=val - Set file extent
; =============================
.cmdEXT
mov #3,r0		; R0=3 for write EXT
.cmdPTR1
mov r0,-(sp)		; Save function
jsr pc,EvalHash		; Step past '#', get integer handle
mov r4,-(sp)		; Save handle
jsr pc,EvalEqual	; Step past '=', get integer pointer
mov r4,(ARGS_ADDR+0)	; Store pointer in ARGS block
mov r3,(ARGS_ADDR+2)
adr ARGS_ADDR,r2	; R2=>ARGS block
mov (sp)+,r1		; R1=handle
mov (sp)+,r0		; R0=function number
jmp IO_ARGS		; Call MOS to write PTR or EXT

; LOAD str$[,addr] - Load program/data
; ====================================
; Fetch CR-string parameter
; Call LoadProgram (finding TOP)
; Jump to Immediate mode
.cmdLOAD
jsr pc,EvalStringCR	; Get filename to R4
jsr pc,SkipSpaces
cmp r0,#ASC","
beq cmdLoadData
jsr pc,LoadProgram	; Load program, check TOP
jmp Immediate		; Drop to immediate mode
.cmdLoadData
jsr pc,StackStringAndOp
inc r5			; Step past comma
jsr pc,EvalInteger
mov r4,FILE_LOAD+0
mov r3,FILE_LOAD+2
jsr pc,UnstackStringDropOp

; Load data named at R4 to address in FILE_LOAD
; ---------------------------------------------
.LoadData
mov r4,FILE_NAME
clr FILE_EXEC
adr FILE_NAME,r1
mov #255,r0
jmp IO_FILE

; Load program named at R4
; ------------------------
.LoadProgram
mov  #&82,r0
jsr  pc,IO_BYTE
mov  r1,FILE_LOAD+2
mov  SV_PAGE,FILE_LOAD+0
jsr  pc,LoadData
mov  SV_PAGE,r1
movb (r1),r0
cmpb r0,#13
beq  FindTOP		; Starts with <cr>, assume 6502 BASIC
;
; Check for loaded text and tokenise
; ----------------------------------
mov  r4,FILE_NAME
mov  #5,r0
adr  FILE_NAME,r1
jsr  pc,IO_FILE		; Get file length
mov  FILE_LENGTH,r0
mov  SV_PAGE,r5		; Point to start of source/dest
add  r5,r0		; Point to end of loaded data
clrb (r0)		; Put zero terminator after loaded text
mov  r5,-(sp)		; Stack dest pointer
mov  (r5),r0		; Get first two characters
cmp  r0,#ASC"#"+256*ASC"!"	; Program starts with #!
bne  LoadText
.LoadTextSkip
movb (r5)+,r0		; Skip past first line
cmp  r0,#32
bcc  LoadTextSkip
.LoadText
; r5=>source
; (sp)=>dest
tstb (r5)		; r5=>source text
beq  LoadTextEnd
adr  SV_INPUT,r4
mov  r4,-(sp)
jsr  pc,Tokenise
mov  (sp)+,r1		; r1=SV_INPUT
sub  r1,r4		; r4=tokenised line length
add  #4,r4		; r4=BASIC line length
mov  (sp)+,r2		; r2=>dest
movb #13,(r2)+
clrb (r2)+
clrb (r2)+		; Line number=0
movb r4,(r2)+		; Store length
.LoadTextLp
movb (r1)+,r0		; Copy tokenised line to destination
movb r0,(r2)+
cmpb r0,#13
bne  LoadTextLp		; Loop until <cr>
mov  r2,-(sp)		; Restack dest
inc  r5			; Step past line terminator
br   LoadText		; Loop for another line
.LoadTextEnd
mov  (sp)+,r2
movb #&FF,(r2)		; Put terminator in place
;
; Check program consistancy and set TOP
; -------------------------------------
.FindTOP
mov  SV_PAGE,r1		; Start at PAGE
.FindTopLp
cmpb (r1),#13
bne  BadProgram
cmpb 1(r1),#&FF
beq  FindTopFound
movb 3(r1),r0		; Get length byte
bic  #&FF00,r0		; Ensure 8-bit value
add  r0,r1		; Point to next CR
br   FindTopLp
.FindTopFound
add  #2,r1
mov  r1,SV_TOP
rts  pc
.BadProgram
jsr  pc,PrintInline
equs "Bad program",13,0
align
jmp  Immediate

; SAVE str$[,start,end[,exec[,load]] - Save program/data
; ======================================================
.cmdSAVE
jsr pc,EvalStringCR	; Get filename to R4
jsr pc,SkipSpaces
cmp r0,#ASC","
beq cmdSaveData
mov SV_PAGE,FILE_START+0
clr FILE_START+2
mov SV_TOP,FILE_END+0
clr FILE_END+2
mov #&FB00,FILE_LOAD+0
mov #&FFFF,FILE_LOAD+2
clr FILE_EXEC+0
clr FILE_EXEC+2
mov r4,FILE_NAME
adr FILE_NAME,r1
clr r0
jsr pc,IO_FILE
jmp Immediate
.cmdSaveData
rts pc


; Time Commands and Functions
; ===========================

; TIME=val, TIME$=s$ - set TIME or TIME$
; ======================================
.cmdTIME
cmp r0,#ASC"$"		; Check for '$'
beq cmdTIMEs		; TIME$
jsr pc,EvalEqual	; Step past '=', get integer
mov r4,MOS_BUF+0
mov r3,MOS_BUF+2
clr MOS_BUF+4
adr MOS_BUF,r1
mov #2,r0
jmp IO_WORD		; Set TIME
.cmdTIMEs
inc r5			; Step past '$'
jsr pc,CheckEqual	; Check for '='
jsr pc,EvalString	; Get string
add r3,r4		; r4=>end of string
adr SV_STRING+1,r1
add r3,r1		; r1=>past end of string buffer
mov r3,r0		; Save string length
beq cmdTIME2		; Null string
.cmdTIMElp
movb -(r4),-(r1)	; Copy byte to string buffer
dec r3
bne cmdTIMElp
.cmdTIME2		; r1=>SV_STRING+1, r0=string length
movb r0,-(r1)		; Put length at start of buffer
mov #15,r0		; TIME$= is OSWORD 15
jmp IO_WORD		; Set TIME$

; =TIME, =TIME$ - read TIME or TIME$
; ==================================
.fnTIME
cmp r0,#ASC"$"
beq fnTIMEs		; =TIME$
adr ARGS_ADDR,r1
mov #1,r0
jsr pc,IO_WORD		; Read TIME
br  fnReturnARGS
.fnTIMEs
inc r5			; Step past '$'
adr SV_STRING,r1	; Point to control block
clr (r1)		; Read time as string
mov #14,r0
jsr pc,IO_WORD		; Read TIME$
adr SV_STRING,r4	; Start=MOS_BUF
mov (r4),r3		; Check returned string
beq fnTIMEnull		; Still zero, nothing returned
mov #24,r3		; Length=24
.fnTIMEnull
mov #&8000,r2		; Type=String
rts pc


; File Functions
; ==============

; =PTR#chn - Read file pointer
; ============================
.fnPTR
clr r0			; R0=0 for read PTR
br fnPTR1

; =EXT#chn - read file extent
; ===========================
.fnEXT
mov #2,r0		; R0=2 for read EXT
.fnPTR1
mov r0,-(sp)		; Save function number
jsr pc,EvalHashVal	; Step past '#', get integer handle
adr ARGS_ADDR,r2	; R2=>ARGS block
mov r4,r1		; R1=handle
mov (sp)+,r0		; R0=function number back
jsr pc,IO_ARGS		; Call MOS to read PTR or EXT
.fnReturnARGS
mov ARGS_ADDR+0,r4
mov ARGS_ADDR+2,r3	; Get returned extent
clr r2			; Return integer
rts pc

; =BGET#chn - read byte from channel
; ==================================
.fnBGET
jsr pc,EvalHashVal	; Step past '#', get integer handle
mov r4,r1		; R1=handle
jsr pc,IO_BGET		; Call MOS to read on this channel
br  fnReturnR0

; =EOF#chn - read end-of-file status
; ==================================
.fnEOF
jsr pc,EvalHashVal	; Step past '#', get integer handle
mov #127,r0		; OSBYTE 127,channel to read EOF
mov r4,r1
jsr pc,IO_BYTE
mov r1,r4		; Move result to r3/r4
mov r1,r3
clr r2			; Return integer
rts pc

; OSCLI - Execute command
; =======================
.cmdOSCLI
jsr pc,EvalStringCR
mov r4,r0
jmp IO_CLI
.fnOSCLI
jsr pc,EvalStrValCR
mov r4,r0
jsr pc,IO_CLI
br  fnReturnR0

; =OPENIN f$ - open file for input
; ================================
.fnOPENIN
mov #&40,r0		; OPENIN is OSFIND &40
br fnOPEN

; =OPENOUT f$ - open file for output
; ==================================
.fnOPENOUT
mov #&80,r0		; OPENOUT is OSFIND &80
br fnOPEN

; =OPENUP f$ - open file for update
; =================================
.fnOPENUP
mov #&C0,r0		; OPENUP is OSFIND &C0
.fnOPEN
mov r0,-(sp)
jsr pc,EvalStrValCR	; Get cr-string
mov r4,r1		; r1=>filename
mov (sp)+,r0		; r0=function
jsr pc,IO_FIND
.fnReturnR0
MOV R0,R4		; R4=returned result
CLR R3			; R3=b16-b31=0
CLR R2			; R2=integer type
RTS PC


; SOUND commands
; ==============

; ENVELOPE a,b,c,d,e,f,g,h,i,j,k,l,m,n
; ====================================
.cmdENVELOPE
jsr  pc,EvalInteger
adr  MOS_BUF,r1
movb r4,(r1)+
mov  r1,-(sp)
mov  #13,-(sp)
.cmdENVlp
jsr  pc,EvalComma
mov  2(sp),r1
movb r4,(r1)+
mov  r1,2(sp)
dec  (sp)
bne  cmdENVlp
mov  #8,r0
br   cmdSOUNDdo

; SOUND [ON|OFF|c,a,p,d] - Issue SOUND command
; ============================================
.cmdSOUND
clr  r1			; SOUND ON is *FX210,0
cmp  r0,#tknON
beq  cmdSOUNDon		; Jump to do SOUND ON
dec  r1			; SOUND OFF is *FX210,255
cmp  r0,#tknOFF
bne  cmdSOUND2		; Not SOUND OFF, jump past
.cmdSOUNDon
inc  r5			; Step past ON/OFF
mov  #210,r0
clr  r2
jmp  IO_BYTE
.cmdSOUND2		; SOUND c,a,p,d
jsr  pc,EvalInteger
adr  MOS_BUF,r1
mov  r4,(r1)+
mov  r1,-(sp)
mov  #3,-(sp)
.cmdSOUNDlp
jsr  pc,EvalComma
mov  2(sp),r1
mov  r4,(r1)+
mov  r1,2(sp)
dec  (sp)
bne  cmdSOUNDlp
mov  #7,r0
.cmdSOUNDdo
tst  (sp)+
tst  (sp)+
adr  MOS_BUF,r1
jmp  IO_WORD

; Acknowledge Escape state
; ========================
.IO_AckEsc
mov  #&DA,r0
clr  r1
clr  r2
jsr  pc,IO_BYTE		; Clear VDU queue
clrb SV_ESCFLG		; Clear local Escape flag
mov  #126,r0
jmp  IO_BYTE		; Acknowledge any Escape state
