basic: allow multiple commands on the same line

This commit is contained in:
Virgil Dupras 2019-12-12 10:51:13 -05:00
parent 5d33d165a2
commit 51c977f2ed
4 changed files with 69 additions and 19 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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