1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-23 19:58:06 +11:00

basic: add buffer line index

This commit is contained in:
Virgil Dupras 2019-11-19 20:43:01 -05:00
parent 4c6de413df
commit 62138b12cf
3 changed files with 98 additions and 67 deletions

12
CODE.md
View File

@ -51,6 +51,10 @@ 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 routines or that call a routine that explicitly states that it preserves
shadow registers. shadow registers.
Another important note is that routines returning success with Z generally don't
preserve AF: too complicated. But otherwise, AF is often preserved. For example,
register fiddling routines in core try to preserve AF.
## Z for success ## Z for success
The vast majority of routines use the Z flag to indicate success. When Z is set, The vast majority of routines use the Z flag to indicate success. When Z is set,
@ -102,4 +106,12 @@ comments. Example:
pop af ; <-- lvl 1 pop af ; <-- lvl 1
I think that this should do the trick, so I'll do this consistently from now on. I think that this should do the trick, so I'll do this consistently from now on.
## String length
Pretty much every routine expecting a string have no provision for a string
that doesn't have null termination within 0xff bytes. Treat strings of such
lengths with extra precaution and distrust proper handling of existing routines
for those strings.
[zasm]: ../apps/zasm/README.md [zasm]: ../apps/zasm/README.md

View File

@ -1,97 +1,117 @@
; *** Consts *** ; *** Consts ***
; maximum number of lines (line number maximum, however, is always 0xffff)
.equ BUF_MAXLINES 0x100
; Size of the string pool
.equ BUF_POOLSIZE 0x1000 .equ BUF_POOLSIZE 0x1000
; *** Variables *** ; *** Variables ***
; A pointer to free space in the pool. ; A pointer to the first free line
.equ BUF_FREE BUF_RAMSTART .equ BUF_LFREE BUF_RAMSTART
; The line pool. Each line consists of a two bytes binary number followed by ; A pointer to the first free byte in the pool
; a one byte length followed by the command string, which doesn't include its .equ BUF_PFREE @+2
; line number (example "10 print 123" becomes "print 123"), but which is null ; The line index. Each record consists of 4 bytes: 2 for line number,
; terminated. The one byte length includes null termination. For example, if ; 2 for pointer to string in pool. Kept in order of line numbers.
; we have a line record starting at 0x1000 and that its length field indicates .equ BUF_LINES @+2
; 0x42, this means that the next line starts at 0x1045 (0x42+2+1). ; The line pool. A list of null terminated strings. BUF_LINES records point
.equ BUF_POOL @+2 ; to those strings.
.equ BUF_POOL @+BUF_MAXLINES*4
.equ BUF_RAMEND @+BUF_POOLSIZE .equ BUF_RAMEND @+BUF_POOLSIZE
bufInit: bufInit:
ld hl, BUF_LINES
ld (BUF_LFREE), hl
ld hl, BUF_POOL ld hl, BUF_POOL
ld (BUF_FREE), hl ld (BUF_PFREE), hl
ret ret
; Add line at (HL) with line number DE to the pool. The string at (HL) should ; Add line at (HL) with line number DE to the buffer. The string at (HL) should
; not contain the line number prefix or the whitespace between the line number ; not contain the line number prefix or the whitespace between the line number
; and the comment. ; and the comment.
; Note that an empty string is *not* an error. It will be saved as a line. ; 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. ; Z for success.
; The only error condition that is handled is when there is not enough space ; Error conditions are:
; left in the pool to add a string of (HL)'s size. In that case, nothing will ; * not enough space in the pool
; be done and Z will be unset. ; * not enough space in the line index
;
; DESTROYED REGISTER: DE. Too much pushpopping around to keep it. Not worth it.
bufAdd: bufAdd:
push hl ; --> lvl 1 exx ; preserve HL and DE
push de ; --> lvl 2 ; First step: do we have index space?
; First step: see if we're within the pool's bounds ld hl, (BUF_LFREE)
ld de, BUF_POOL
or a ; reset carry
sbc hl, de
exx ; restore
; no carry? HL >= BUF_POOL, error. Z already unset
ret nc
; Second step: see if we're within the pool's bounds
call strlen call strlen
inc a ; strlen doesn't include line termination inc a ; strlen doesn't include line termination
ld hl, (BUF_FREE) exx ; preserve HL and DE
ld hl, (BUF_PFREE)
call addHL call addHL
; add overhead (3b)
inc hl \ inc hl \ inc hl
ld de, BUF_RAMEND ld de, BUF_RAMEND
sbc hl, de sbc hl, de
exx ; restore
; no carry? HL >= BUF_RAMEND, error. Z already unset ; no carry? HL >= BUF_RAMEND, error. Z already unset
jr nc, .error ret nc
; We have enough space, proceed ; We have enough space.
ld hl, (BUF_FREE) ; Third step: set line index data
pop de ; <-- lvl 2 push de ; --> lvl 1
push hl ; --> lvl 2
ld hl, (BUF_LFREE)
ld (hl), e ld (hl), e
inc hl inc hl
ld (hl), d ld (hl), d
inc hl inc hl
; A has been untouched since that strlen call. Let's use it as-is. ld de, (BUF_PFREE)
ld (hl), a ld (hl), e
inc hl ; HL now points to dest for our string. inc hl
ex de, hl ld (hl), d
pop hl \ push hl ; <--> lvl 1. recall orig, but also preserve inc hl
ld (BUF_LFREE), hl
pop hl \ push hl ; <-- lvl 2, restore and preserve
; Fourth step: copy string to pool
ld de, (BUF_PFREE)
call strcpyM call strcpyM
; Copying done. Let's update the free zone marker. ld (BUF_PFREE), de
ld (BUF_FREE), de pop hl ; <-- lvl 2
xor a ; set Z pop de ; <-- lvl 1
pop hl ; <-- lvl 1
ret ret
.error:
; Set IX to point to the beginning of the pool.
; Z set if (IX) is a valid line, unset if the pool is empty.
bufFirst:
ld ix, BUF_LINES
jp bufEOF
; Given a valid line record in IX, move IX to the next line.
; This routine doesn't check that IX is valid. Ensure IX validity before
; calling.
bufNext:
inc ix \ inc ix \ inc ix \ inc ix
jp bufEOF
; Returns whether line index at IX is past the end of file, that is,
; whether IX == (BUF_LFREE)
; Z is set when not EOF, unset when EOF.
bufEOF:
push hl
push de
push ix \ pop hl
ld de, (BUF_LFREE)
sbc hl, de
jr z, .empty
cp a ; ensure Z
.end:
pop de pop de
pop hl pop hl
ret ret
.empty:
call unsetZ
jr .end
; Set IX to point to the first valid line we have in the pool. ; Given a line index in (IX), set HL to its associated string pointer.
; Error if the pool is empty. bufStr:
; Z for success. ld l, (ix+2)
bufFirst: ld h, (ix+3)
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 ret

View File

@ -118,8 +118,7 @@ basLIST:
call printstr call printstr
ld a, ' ' ld a, ' '
call stdioPutC call stdioPutC
push ix \ pop hl call bufStr
inc hl \ inc hl \ inc hl
call printstr call printstr
call printcrlf call printcrlf
call bufNext call bufNext