; > Variables
; Handle BASIC variables
; 24-Feb-2009: Static integer variables and indirection
; 25-Feb-2009: Reading $<addr> and $$<addr>


; CLEAR - Clear heap
; ==================
; LOMEM=TOP
; VAREND=TOP
; DATAPTR=PAGE
; STACK=HIMEM
; Clear dynamic variables
.cmdCLEAR		; Fall through

.VarsHeapInit
mov  SV_TOP,SV_LOMEM	; LOMEM=TOP, start of heap
mov  SV_TOP,SV_VAREND	; VAREND=TOP, end of heap
;.VarsXXX
jsr  pc,VarsXXX

; Clear dynamic variables, don't clear DATA or Stack
; --------------------------------------------------
.VarsClear
adr  SV_VARPTR,r1			; Point to variables pointers
mov  #(SV_ENDPTR+2-SV_VARPTR)/2,r0	; Number of pointers
.VarsClearLp
; jsr pc,Debug_DumpRegsHex
clr  (r1)+		; Clear this pointer
dec  r0
bne  VarsClearLp	; Loop for all pointers
rts  pc

.VarsXXX
mov  SV_PAGE,SV_DATA	; DATAPTR=PAGE, start of program
mov  SV_HIMEM,SV_STACK	; Clear BASIC stack
rts  pc

; VarFind - Find a variable, and create it if non-existant
; ========================================================
; On entry, r5=>first character of name
; On exit,  NE=variable found
;           EQ=variable not found
;           CS=bad variable name
;           r4=address of data block
;           r3=size of data block
;           r2=type of variable
;           r1/r0=corrupted
;
.VarFind
movb (r5),r0		; Get first character
clr  r4			; Zero base for !x, ?x, |x
cmpb r0,#ASC"!"
beq  VarFindIndirect	; Evaluate !x and 0!x
cmpb r0,#ASC"?"
beq  VarFindIndirect	; Evaluate ?x as 0?x
cmpb r0,#ASC"|"
beq  VarFindIndirect	; Evaluate |x as 0|x
cmpb r0,#ASC"$"
beq  VarFindChkDol	; Evaluate $<addr> and $$<addr>
cmpb r0,#ASC"@"
bcs  VarFindExit	; Bad variable name

; VarFindVariable - Find a variable ignoring indirections
; -------------------------------------------------------
.VarFindVariable
movb (r5),r0		; Get first character
cmp  r0,#ASC"["
bcc  VarFindDyn
cmpb 1(r5),#ASC"%"	; Is it <uc>% ?
bne  VarFindDyn		; No, look for dynamic variable
cmpb 2(r5),#ASC"("	; Is it <uc>%( ?
beq  VarFindDyn		; Yes, look for dynamic array entry
add  #2,r5		; Step past <uc>%
add  r0,r0
add  r0,r0		; r0=ASC"<uc>"*4
adr  SV_VARS-4*ASC"@",r4
add  r0,r4		; r4=><data block>
mov  #4,r3		; r3=4, size of data block
clr  r2			; r2=0 - integer
movb (r5),r0
cmpb r0,#ASC"!"		; Is it var!offset ?
beq  VarFindIndirect
cmpb r0,#ASC"?"		; Is it var?offset ?
beq  VarFindIndirect
clc			; CC=variable name OK
.VarFindExit
rts  pc			; NE=variable found
.VarFindChkDol
cmpb 1(r5),#ASC"$"	; Is it $$<addr> ?
bne  VarFindIndirect	; No, jump for $<addr>
inc  r5			; Step past first '$'
inc  r0			; Make operator '%'
.VarFindIndirect
mov  r0,-(sp)		; Stack indirection operator
mov  r4,-(sp)		; Stack address
inc  r5
jsr  pc,EvalIntVal	; Evaluate following numeric
add  (sp)+,r4		; Add stacked offset to numeric
mov  (sp)+,r3		; Get indirection operator back
clr  r2			; Initially set as integer
cmpb r3,#ASC"!"
beq  VarFindPling	; CC=ok, return with size=4
cmpb r3,#ASC"$"
beq  VarFindDollar	; CC=ok, return fixed cr-string
cmpb r3,#ASC"%"
beq  VarFindDouble	; CC=ok, return fixed null-string
cmpb r3,#ASC"?"
beq  VarFindQuery	; CC=ok, return with size=1
mov  #5,r3		; |offset - five bytes
mov  #&80,r2		; r2=&0080 - float
clc			; CC=ok, NE=found
rts  pc
.VarFindPling
mov  #4,r3		; name?offset - four bytes, sets NE=found
rts  pc
.VarFindQuery
mov  #1,r3		; name?offset - single byte, sets NE=found
rts  pc
.VarFindDollar
mov  #&8100,r2		; type=cr-string
rts  pc
.VarFindDouble
mov  #&8200,r2		; type=null-string
rts pc
;
.VarFindDyn
cmp  r0,r0
sec
rts  pc


.errNoSuchVar2
jmp  errNoSuchVar

; VarFindVal - Find a variable and return its value
; =================================================
; On entry, r5=>first character of name
; On exit,  NE=variable found
;           EQ=variable not found
;           CS=bad variable name
;           r4/r3/r2=value
;           r1/r0=corrupted
;
.VarFindVal
jsr  pc,VarFind
beq  errNoSuchVar2	; Variable not found or bad variable name
tst  r2			; r2=0 int/byte, r2=0080 float, r2=8xxx string
bmi  VarFindString	; If string, return string descriptor
movb (r4)+,r0		; Get first byte
bic  #&FF00,r0
dec  r3
beq  VarFindByte	; Single byte
movb (r4)+,r1		; Get second byte
bic  #&FF00,r1
swab r1
bis  r1,r0		; r0=1st/2nd bytes
movb (r4)+,r1		; Get third byte
movb (r4)+,r2		; Get fourth byte
bic  #&FF00,r1
bic  #&FF00,r2
swab r2
bis  r2,r1		; r1=3rd/4th bytes
clr  r2			; Prepare for word
cmp  r3,#3
beq  VarFindWord
movb (r4),r2		; Get fifth byte for float
.VarFindWord
mov  r1,r3
.VarFindByte
mov  r0,r4
clc
tst  r2
rts  pc

; If a string found, registers will hold:
; r2=&8000, r4=>string descriptor block => addr.lo, addr.hi, length, allocated
; r2=&8100, r4=>start of cr-string
; r2=&8200, r4=>start of null-string
.VarFindString
clr  r3			; Set length to zero
clr  r0			; Look for CHR$0
bit  #&0200,r2		; A null-string?
bne  VarFindStrCount
mov  #13,r0		; Look for CHR$13
bit  #&0100,r2		; A cr-string?
bne  VarFindStrCount
movb 2(r4),r3		; Get string length
bic  #&FF00,r3		; 8-bit length
mov  (r4),r4		; Get string length
clc			; CLC=OK
tst  r2
rts  pc
.VarFindStrCount
mov  r4,r1		; Copy start address to r1
.VarFindStrLp
inc  r3			; Increment length
cmp  r3,#256		; String too long?
bcc  VarFindStrZero	; No terminator, return null
cmpb (r1)+,r0		; Terminator found?
bne  VarFindStrLp	; No, loop until found or too long
dec  r3			; Remove terminator from count
clc			; CLC=OK
tst  r2			; Set flags from string type
rts  pc
.VarFindStrZero
clr  r3			; Return zero-length string
clc			; CLC=OK
tst  r2			; Set flags from string type
rts  pc

;; ; Check if variable name is valid
;; ; ===============================
;; ; On entry, r5=>start of variable name
;; ; On exit,  r5=>preserved
;; ;           r1=length of name, EQ/NE set
;; ;           r0=corrupted
;; .VarCheckName
;; mov  r5,-(sp)
;; clr  r1		; No characters counted
;; .VarChkNameLp
;; movb (r5)+,r0
;; cmp  r0,#ASC"0"
;; bcc  VarChkNameEnd
;; cmp  r0,#ASC"@"
;; bcs  VarChkNameAlpha
;; cmp  r0,#ASC"9"+1
;; bcs  VarChkNameEnd
;; .VarChkNameNxt
;; inc  r1
;; br   VarChkNameLp
;; .VarChkNameAlpha
;; cmpb r0,#ASC"_"
;; bcs  VarChkNameLwr
;; cmpb r0,#ASC"["
;; bcc  VarChkNameNxt
;; .VarChkNameEnd
;; mov (sp)+,r5
;; tst r1		; Set EQ/NE from name length
;; rts pc
;; .VarChkNameLwr
;; cmpb r0,#ASC"{"
;; bcc  VarChkNameNxt
;; br   VarChkNameEnd
;; 
