diff --git a/apps/basic/glue.asm b/apps/basic/glue.asm index db70b93..9b9a634 100644 --- a/apps/basic/glue.asm +++ b/apps/basic/glue.asm @@ -14,10 +14,12 @@ .inc "lib/ari.asm" .inc "lib/parse.asm" .inc "lib/fmt.asm" -.equ EXPR_PARSE parseLiteral +.equ EXPR_PARSE parseLiteralOrVar .inc "lib/expr.asm" .inc "basic/tok.asm" -.equ BUF_RAMSTART USER_RAMSTART +.equ VAR_RAMSTART USER_RAMSTART +.inc "basic/var.asm" +.equ BUF_RAMSTART VAR_RAMEND .inc "basic/buf.asm" .equ BAS_RAMSTART BUF_RAMEND .inc "basic/main.asm" diff --git a/apps/basic/main.asm b/apps/basic/main.asm index 134eef0..9fc3785 100644 --- a/apps/basic/main.asm +++ b/apps/basic/main.asm @@ -12,6 +12,7 @@ ; *** Code *** basStart: ld (BAS_INITSP), sp + call varInit call bufInit xor a ld hl, .welcome @@ -51,8 +52,20 @@ basLoop: ; If found, jump to it. If not found, unset Z. We expect commands to set Z ; on success. Therefore, when calling basCallCmd results in NZ, we're not sure ; where the error come from, but well... +; Before being evaluated, (HL) is copied in BAS_SCRATCHPAD because some +; evaluation routines (such as parseExpr) mutate the string it evaluates. +; TODO: straighten this situation up. Mutating a string like this breaks +; expectations. basCallCmd: - ; First, get cmd length + push de ; --> lvl 1 + ld de, BAS_SCRATCHPAD + call strcpy + ex de, hl ; HL now points to scratchpad + pop de ; <-- lvl 1 + ; let's see if it's a variable assignment. + call varTryAssign + ret z ; Done! + ; Second, get cmd length call fnWSIdx cp 7 jp nc, unsetZ ; Too long, can't possibly fit anything. diff --git a/apps/basic/var.asm b/apps/basic/var.asm new file mode 100644 index 0000000..038cde3 --- /dev/null +++ b/apps/basic/var.asm @@ -0,0 +1,97 @@ +; *** Variables *** +; A list of words for each member of the A-Z range. +.equ VAR_TBL VAR_RAMSTART +.equ VAR_RAMEND @+52 + +; *** Code *** + +varInit: + ld b, VAR_RAMEND-VAR_RAMSTART + ld hl, VAR_RAMSTART + xor a +.loop: + ld (hl), a + inc hl + djnz .loop + ret + +; Check if A is a valid variable letter (a-z or A-Z). If it is, set A to a +; valid VAR_TBL index and set Z. Otherwise, unset Z (and A is destroyed) +varChk: + call upcase + sub 'A' + ret c ; Z unset + cp 27 ; 'Z' + 1 + jr c, .isVar + ; A > 'Z' + dec a ; unset Z + ret +.isVar: + cp a ; set Z + ret + +; Try to interpret line at (HL) and see if it's a variable assignment. If it +; is, proceed with the assignment and set Z. Otherwise, NZ. +varTryAssign: + inc hl + ld a, (hl) + dec hl + cp '=' + ret nz + ld a, (hl) + call varChk + ret nz + ; We have a variable! Its table index is currently in A. + push ix ; --> lvl 1 + push hl ; --> lvl 2 + push af ; --> lvl 3. save for later + ; Let's start by evaluating that expression + inc hl \ inc hl + call parseExpr ; --> number in IX + jr nz, .exprErr + pop af ; <-- lvl 3 + add a, a ; * 2 because each element is a word + ld hl, VAR_TBL + call addHL + ; HL placed, write number + push de ; --> lvl 3 + push ix \ pop de + ld (hl), e + inc hl + ld (hl), d + pop de ; <-- lvl 3 + xor a ; ensure Z +.end: + pop hl ; <-- lvl 2 + pop ix ; <-- lvl 1 + ret +.exprErr: + pop af ; <-- lvl 3 + jr .end + +; Check if value at (HL) is a variable. If yes, returns its associated value. +; Otherwise, jump to parseLiteral. +parseLiteralOrVar: + inc hl + ld a, (hl) + dec hl + or a + ; if more than one in length, it can't be a variable + jp nz, parseLiteral + ld a, (hl) + call varChk + jp nz, parseLiteral + ; It's a variable, resolve! + add a, a ; * 2 because each element is a word + push hl ; --> lvl 1 + ld hl, VAR_TBL + call addHL + push de ; --> lvl 2 + ld e, (hl) + inc hl + ld d, (hl) + push de \ pop ix + pop de ; <-- lvl 2 + pop hl ; <-- lvl 1 + cp a ; ensure Z + ret