basic: begin implementing a line buffer
This commit is contained in:
parent
f5b04fc02f
commit
4c6de413df
15
CODE.md
15
CODE.md
@ -51,6 +51,21 @@ Therefore, shadow registers should only be used in code that doesn't call
|
||||
routines or that call a routine that explicitly states that it preserves
|
||||
shadow registers.
|
||||
|
||||
## Z for success
|
||||
|
||||
The vast majority of routines use the Z flag to indicate success. When Z is set,
|
||||
it indicates success. When Z is unset, it indicates error. This follows the
|
||||
tradition of a zero indicating success and a nonzero indicating error.
|
||||
|
||||
Important note: only Z indicate success. Many routines return a meaningful
|
||||
nonzero value in A and still set Z to indicate success.
|
||||
|
||||
In error conditions, however, most of the time A is set to an error code.
|
||||
|
||||
In many routines, this is specified verbosely, but it's repeated so often that
|
||||
I started writing it in short form, "Z for success", which means what is
|
||||
described here.
|
||||
|
||||
## Stack management
|
||||
|
||||
Keeping the stack "balanced" is a big challenge when writing assembler code.
|
||||
|
@ -14,3 +14,17 @@ writing from scratch, so here I am, writing from scratch again...
|
||||
The biggest challenge here is to extract code from zasm, adapt it to fit BASIC,
|
||||
not break anything, and have the wisdom to see when copy/pasting is a better
|
||||
idea.
|
||||
|
||||
## Design goal
|
||||
|
||||
The reason for including a BASIC dialect in Collapse OS is to supply some form
|
||||
of system administration swiss knife. zasm, ed and the shell can do
|
||||
theoretically anything, but some tasks (which are difficult to predict) can
|
||||
possibly be overly tedious. One can think, for example, about hardware
|
||||
debugging. Poking and peeking around when not sure what we're looking for can
|
||||
be a lot more effective with the help of variables, conditions and for-loops in
|
||||
an interpreter.
|
||||
|
||||
Because the goal is not to provide a foundation for complex programs, I'm
|
||||
planning on intentionally crippling this BASIC dialect for the sake of
|
||||
simplicity.
|
||||
|
97
apps/basic/buf.asm
Normal file
97
apps/basic/buf.asm
Normal file
@ -0,0 +1,97 @@
|
||||
; *** Consts ***
|
||||
.equ BUF_POOLSIZE 0x1000
|
||||
|
||||
; *** Variables ***
|
||||
; A pointer to free space in the pool.
|
||||
.equ BUF_FREE BUF_RAMSTART
|
||||
; The line pool. Each line consists of a two bytes binary number followed by
|
||||
; a one byte length followed by the command string, which doesn't include its
|
||||
; line number (example "10 print 123" becomes "print 123"), but which is null
|
||||
; terminated. The one byte length includes null termination. For example, if
|
||||
; we have a line record starting at 0x1000 and that its length field indicates
|
||||
; 0x42, this means that the next line starts at 0x1045 (0x42+2+1).
|
||||
.equ BUF_POOL @+2
|
||||
.equ BUF_RAMEND @+BUF_POOLSIZE
|
||||
|
||||
bufInit:
|
||||
ld hl, BUF_POOL
|
||||
ld (BUF_FREE), hl
|
||||
ret
|
||||
|
||||
; Add line at (HL) with line number DE to the pool. The string at (HL) should
|
||||
; not contain the line number prefix or the whitespace between the line number
|
||||
; and the comment.
|
||||
; Note that an empty string is *not* an error. It will be saved as a line.
|
||||
; Don't send strings that are more than 0xfe in length. It won't work well.
|
||||
; Z for success.
|
||||
; The only error condition that is handled is when there is not enough space
|
||||
; left in the pool to add a string of (HL)'s size. In that case, nothing will
|
||||
; be done and Z will be unset.
|
||||
;
|
||||
; DESTROYED REGISTER: DE. Too much pushpopping around to keep it. Not worth it.
|
||||
bufAdd:
|
||||
push hl ; --> lvl 1
|
||||
push de ; --> lvl 2
|
||||
; First step: see if we're within the pool's bounds
|
||||
call strlen
|
||||
inc a ; strlen doesn't include line termination
|
||||
ld hl, (BUF_FREE)
|
||||
call addHL
|
||||
; add overhead (3b)
|
||||
inc hl \ inc hl \ inc hl
|
||||
ld de, BUF_RAMEND
|
||||
sbc hl, de
|
||||
; no carry? HL >= BUF_RAMEND, error. Z already unset
|
||||
jr nc, .error
|
||||
; We have enough space, proceed
|
||||
ld hl, (BUF_FREE)
|
||||
pop de ; <-- lvl 2
|
||||
ld (hl), e
|
||||
inc hl
|
||||
ld (hl), d
|
||||
inc hl
|
||||
; A has been untouched since that strlen call. Let's use it as-is.
|
||||
ld (hl), a
|
||||
inc hl ; HL now points to dest for our string.
|
||||
ex de, hl
|
||||
pop hl \ push hl ; <--> lvl 1. recall orig, but also preserve
|
||||
call strcpyM
|
||||
; Copying done. Let's update the free zone marker.
|
||||
ld (BUF_FREE), de
|
||||
xor a ; set Z
|
||||
pop hl ; <-- lvl 1
|
||||
ret
|
||||
.error:
|
||||
pop de
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Set IX to point to the first valid line we have in the pool.
|
||||
; Error if the pool is empty.
|
||||
; Z for success.
|
||||
bufFirst:
|
||||
ld a, (BUF_POOL+2)
|
||||
or a
|
||||
jp z, unsetZ
|
||||
ld ix, BUF_POOL
|
||||
xor a ; set Z
|
||||
ret
|
||||
|
||||
; Given a valid line record in IX, move IX to the next valid line.
|
||||
; This routine doesn't check that IX is valid. Ensure IX validity before
|
||||
; calling. This routine also doesn't check that the next line is within the
|
||||
; bounds of the pool because this check is done during bufAdd.
|
||||
; The only possible error is if there is no next line.
|
||||
; Z for success.
|
||||
bufNext:
|
||||
push de ; --> lvl 1
|
||||
ld d, 0
|
||||
ld e, (ix+2)
|
||||
add ix, de
|
||||
inc ix \ inc ix \ inc ix
|
||||
pop de ; <-- lvl 1
|
||||
ld a, (ix+2)
|
||||
or a
|
||||
jp z, unsetZ
|
||||
xor a ; set Z
|
||||
ret
|
@ -17,6 +17,8 @@
|
||||
.equ EXPR_PARSE parseLiteral
|
||||
.inc "lib/expr.asm"
|
||||
.inc "basic/tok.asm"
|
||||
.equ BAS_RAMSTART USER_RAMSTART
|
||||
.equ BUF_RAMSTART USER_RAMSTART
|
||||
.inc "basic/buf.asm"
|
||||
.equ BAS_RAMSTART BUF_RAMEND
|
||||
.inc "basic/main.asm"
|
||||
USER_RAMSTART:
|
||||
|
@ -12,6 +12,7 @@
|
||||
; *** Code ***
|
||||
basStart:
|
||||
ld (BAS_INITSP), sp
|
||||
call bufInit
|
||||
xor a
|
||||
ld hl, .welcome
|
||||
call printstr
|
||||
@ -32,12 +33,13 @@ basPrompt:
|
||||
call basDirect
|
||||
jr basPrompt
|
||||
.number:
|
||||
; do nothing for now, we only support direct mode.
|
||||
ld hl, .sNumber
|
||||
call basPrintLn
|
||||
push ix \ pop de
|
||||
call toWS
|
||||
call rdWS
|
||||
call bufAdd
|
||||
jp nz, basERR
|
||||
call printcrlf
|
||||
jr basPrompt
|
||||
.sNumber:
|
||||
.db "A number!", 0
|
||||
.sPrompt:
|
||||
.db "> ", 0
|
||||
|
||||
@ -104,6 +106,27 @@ basBYE:
|
||||
.sBye:
|
||||
.db "Goodbye!", 0
|
||||
|
||||
basLIST:
|
||||
call printcrlf
|
||||
call bufFirst
|
||||
ret nz
|
||||
.loop:
|
||||
ld e, (ix)
|
||||
ld d, (ix+1)
|
||||
ld hl, BAS_SCRATCHPAD
|
||||
call fmtDecimal
|
||||
call printstr
|
||||
ld a, ' '
|
||||
call stdioPutC
|
||||
push ix \ pop hl
|
||||
inc hl \ inc hl \ inc hl
|
||||
call printstr
|
||||
call printcrlf
|
||||
call bufNext
|
||||
jr z, .loop
|
||||
ret
|
||||
|
||||
|
||||
basPRINT:
|
||||
call parseExpr
|
||||
jp nz, basERR
|
||||
@ -116,6 +139,8 @@ basPRINT:
|
||||
basCmds1:
|
||||
.dw basBYE
|
||||
.db "bye", 0, 0, 0
|
||||
.dw basLIST
|
||||
.db "list", 0, 0
|
||||
; statements
|
||||
basCmds2:
|
||||
.dw basPRINT
|
||||
|
@ -46,3 +46,11 @@ fnWSIdx:
|
||||
pop bc
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Advance HL to the next whitespace or to the end of string.
|
||||
toWS:
|
||||
ld a, (hl)
|
||||
call isSep
|
||||
ret z
|
||||
inc hl
|
||||
jr toWS
|
||||
|
Loading…
Reference in New Issue
Block a user