From 51c977f2ed9d7e110ca6fd63da1d557f0660e9da Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Thu, 12 Dec 2019 10:51:13 -0500 Subject: [PATCH] basic: allow multiple commands on the same line --- apps/basic/README.md | 13 +++++++++++-- apps/basic/fs.asm | 2 +- apps/basic/main.asm | 32 +++++++++++++++++++++++++++----- apps/basic/tok.asm | 41 ++++++++++++++++++++++++++++++----------- 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/apps/basic/README.md b/apps/basic/README.md index 8a31e47..8af3b08 100644 --- a/apps/basic/README.md +++ b/apps/basic/README.md @@ -88,13 +88,22 @@ etc.) always to so in variable `A`. Another is that whenever a number is expected, expressions, including the ones with variables in it, work fine. +### One-liners + +The `:` character, when not inside a `""` literal, allows you to cram more than +one instruction on the same line. + +Things are special with `if`. All commands following a `if` are bound to that +`if`'s condition. `if 0 foo:bar` doesn't execute `bar`. + +Another special thing is `goto`. A `goto` followed by `:` will have the commands +following the `:` before the goto occurs. + ### Commands There are two types of commands: normal and direct-only. The latter can only be invoked in direct mode, not through a code listing. -`bye`: Direct-only. Quits BASIC - `list`: Direct-only. Prints all lines in the code listing, prefixing them with their associated line number. diff --git a/apps/basic/fs.asm b/apps/basic/fs.asm index 23a8f0c..7cce471 100644 --- a/apps/basic/fs.asm +++ b/apps/basic/fs.asm @@ -46,7 +46,7 @@ basLDBAS: call parseDecimal jr nz, .notANumber push ix \ pop de - call toSep + call toSepOrEnd call rdSep call bufAdd pop hl ; <-- lvl 1 diff --git a/apps/basic/main.asm b/apps/basic/main.asm index 56db9ab..f4559f7 100644 --- a/apps/basic/main.asm +++ b/apps/basic/main.asm @@ -41,14 +41,14 @@ basLoop: call parseDecimal jr z, .number ld de, basCmds1 - call basCallCmd + call basCallCmds jr z, basLoop ; Error call basERR jr basLoop .number: push ix \ pop de - call toSep + call toSepOrEnd call rdSep call bufAdd jp nz, basERR @@ -110,6 +110,27 @@ basCallCmd: call rdSep jp (ix) +; Call a series of ':'-separated commands in (HL) using cmd table in (DE). +; Stop processing as soon as one command unsets Z. +basCallCmds: + ; Commands are not guaranteed at all to preserve HL and DE, so we + ; preserve them ourselves here. + push hl ; --> lvl 1 + push de ; --> lvl 2 + call basCallCmd + pop de ; <-- lvl 2 + pop hl ; <-- lvl 1 + ret nz + call toEnd + ret z ; no more cmds + ; we met a ':', we have more cmds + inc hl + call basCallCmds + ; move the the end of the string so that we don't run cmds following a + ; ':' twice. + call strskip + ret + basERR: ld hl, .sErr call printstr @@ -154,7 +175,7 @@ basRUN: call bufStr ld de, basCmds2 push ix ; --> lvl 1 - call basCallCmd + call basCallCmds pop ix ; <-- lvl 1 jp nz, .err call .maybeGOTO @@ -258,10 +279,11 @@ basIF: ret z ; expr is true, execute next ; (HL) back to beginning of args, skip to next arg - call toSep + call toSepOrEnd call rdSep + ret nz ld de, basCmds2 - jp basCallCmd + jp basCallCmds basINPUT: ; If our first arg is a string literal, spit it diff --git a/apps/basic/tok.asm b/apps/basic/tok.asm index 1e6c630..e6adf41 100644 --- a/apps/basic/tok.asm +++ b/apps/basic/tok.asm @@ -1,10 +1,16 @@ -; Sets Z is A is ' ' or '\t' (whitespace), or ',' (arg sep) +; Whether A is a separator or end-of-string (null or ':') +isSepOrEnd: + or a + ret z + cp ':' + ret z + ; continue to isSep + +; Sets Z is A is ' ' or '\t' (whitespace) isSep: cp ' ' ret z cp 0x09 - ret z - cp ',' ret ; Expect at least one whitespace (0x20, 0x09) at (HL), and then advance HL @@ -23,8 +29,8 @@ rdSep: ld a, (hl) call isSep jr z, .loop - or a ; cp 0 - jp z, .fail + call isSepOrEnd + jp z, .fail ; unexpected EOL. fail cp a ; ensure Z ret .fail: @@ -33,12 +39,27 @@ rdSep: ret ; Advance HL to the next separator or to the end of string. -toSep: +toSepOrEnd: ld a, (hl) - call isSep + call isSepOrEnd ret z inc hl - jr toSep + jr toSepOrEnd + +; Advance HL to the end of the line, that is, either a null terminating char +; or the ':'. +; Sets Z if we met a null char, unset if we met a ':' +toEnd: + ld a, (hl) + or a + ret z + cp ':' + jr z, .havesep + inc hl + jr toEnd +.havesep: + inc a ; unset Z + ret ; Read (HL) until the next separator and copy it in (DE) ; DE is preserved, but HL is advanced to the end of the read word. @@ -47,9 +68,7 @@ rdWord: push de .loop: ld a, (hl) - call isSep - jr z, .stop - or a + call isSepOrEnd jr z, .stop ld (de), a inc hl