Compare commits
2 Commits
4c6de413df
...
9d1003e7a2
Author | SHA1 | Date | |
---|---|---|---|
|
9d1003e7a2 | ||
|
62138b12cf |
12
CODE.md
12
CODE.md
@ -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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
@ -1,97 +1,181 @@
|
||||
; *** 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
|
||||
|
||||
; *** 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
|
||||
; A pointer to the first free line
|
||||
.equ BUF_LFREE BUF_RAMSTART
|
||||
; A pointer to the first free byte in the pool
|
||||
.equ BUF_PFREE @+2
|
||||
; The line index. Each record consists of 4 bytes: 2 for line number,
|
||||
; 2 for pointer to string in pool. Kept in order of line numbers.
|
||||
.equ BUF_LINES @+2
|
||||
; The line pool. A list of null terminated strings. BUF_LINES records point
|
||||
; to those strings.
|
||||
.equ BUF_POOL @+BUF_MAXLINES*4
|
||||
.equ BUF_RAMEND @+BUF_POOLSIZE
|
||||
|
||||
bufInit:
|
||||
ld hl, BUF_LINES
|
||||
ld (BUF_LFREE), hl
|
||||
ld hl, BUF_POOL
|
||||
ld (BUF_FREE), hl
|
||||
ld (BUF_PFREE), hl
|
||||
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
|
||||
; 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.
|
||||
; Error conditions are:
|
||||
; * not enough space in the pool
|
||||
; * not enough space in the line index
|
||||
bufAdd:
|
||||
push hl ; --> lvl 1
|
||||
push de ; --> lvl 2
|
||||
; First step: see if we're within the pool's bounds
|
||||
; Check whether we have enough pool space. This is done in all cases.
|
||||
call strlen
|
||||
inc a ; strlen doesn't include line termination
|
||||
ld hl, (BUF_FREE)
|
||||
exx ; preserve HL and DE
|
||||
ld hl, (BUF_PFREE)
|
||||
call addHL
|
||||
; add overhead (3b)
|
||||
inc hl \ inc hl \ inc hl
|
||||
ld de, BUF_RAMEND
|
||||
sbc hl, de
|
||||
exx ; restore
|
||||
; 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
|
||||
ret nc
|
||||
|
||||
; Check the kind of operation we make: add, insert or replace?
|
||||
call bufFind
|
||||
jr z, .replace ; exact match, replace
|
||||
call c, .insert ; near match, insert
|
||||
|
||||
; do we have enough index space?
|
||||
exx ; preserve HL and DE
|
||||
ld hl, (BUF_LFREE)
|
||||
ld de, BUF_POOL-4
|
||||
or a ; reset carry
|
||||
sbc hl, de
|
||||
exx ; restore
|
||||
; no carry? HL >= BUF_POOL, error. Z already unset
|
||||
ret nc
|
||||
|
||||
; We have enough space.
|
||||
; set line index data
|
||||
push de ; --> lvl 1
|
||||
ld (ix), e
|
||||
ld (ix+1), d
|
||||
ld de, (BUF_PFREE)
|
||||
ld (ix+2), e
|
||||
ld (ix+3), d
|
||||
|
||||
; Increase line index size
|
||||
ld de, (BUF_LFREE)
|
||||
inc de \ inc de \ inc de \ inc de
|
||||
ld (BUF_LFREE), de
|
||||
|
||||
; Fourth step: copy string to pool
|
||||
ld de, (BUF_PFREE)
|
||||
call strcpyM
|
||||
; Copying done. Let's update the free zone marker.
|
||||
ld (BUF_FREE), de
|
||||
xor a ; set Z
|
||||
pop hl ; <-- lvl 1
|
||||
ld (BUF_PFREE), de
|
||||
pop de ; <-- lvl 1
|
||||
ret
|
||||
.error:
|
||||
|
||||
; No need to add a new line, just replace the current one.
|
||||
.replace:
|
||||
ld (ix), e
|
||||
ld (ix+1), d
|
||||
push de
|
||||
ld de, (BUF_PFREE)
|
||||
ld (ix+2), e
|
||||
ld (ix+3), d
|
||||
call strcpyM
|
||||
ld (BUF_PFREE), de
|
||||
pop de
|
||||
ret
|
||||
|
||||
; An insert is exactly like an add, except that lines following insertion point
|
||||
; first.
|
||||
.insert:
|
||||
push hl
|
||||
push de
|
||||
push bc
|
||||
; We want a LDDR that moves from (BUF_LFREE)-1 to (BUF_LFREE)+3
|
||||
; for a count of (BUF_LFREE)-BUF_LINES
|
||||
ld hl, (BUF_LFREE)
|
||||
ld de, BUF_LINES
|
||||
or a ; clear carry
|
||||
sbc hl, de
|
||||
ld b, h
|
||||
ld c, l
|
||||
ld hl, (BUF_LFREE)
|
||||
ld d, h
|
||||
ld e, l
|
||||
dec hl
|
||||
inc de \ inc de \ inc de
|
||||
lddr
|
||||
pop bc
|
||||
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.
|
||||
; 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 a, (BUF_POOL+2)
|
||||
or a
|
||||
jp z, unsetZ
|
||||
ld ix, BUF_POOL
|
||||
xor a ; set Z
|
||||
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
|
||||
or a ; clear carry
|
||||
ld de, (BUF_LFREE)
|
||||
sbc hl, de
|
||||
jr z, .empty
|
||||
cp a ; ensure Z
|
||||
.end:
|
||||
pop de
|
||||
pop hl
|
||||
ret
|
||||
.empty:
|
||||
call unsetZ
|
||||
jr .end
|
||||
|
||||
; Given a line index in (IX), set HL to its associated string pointer.
|
||||
bufStr:
|
||||
ld l, (ix+2)
|
||||
ld h, (ix+3)
|
||||
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
|
||||
; Browse lines looking for number DE. Set IX to point to one of these :
|
||||
; 1 - an exact match
|
||||
; 2 - the first found line to have a higher line number
|
||||
; 3 - EOF
|
||||
; Set Z on an exact match, C on a near match, NZ and NC on EOF.
|
||||
bufFind:
|
||||
call bufFirst
|
||||
ret nz
|
||||
.loop:
|
||||
ld a, d
|
||||
cp (ix+1)
|
||||
ret c ; D < (IX+1), situation 2
|
||||
jr nz, .next
|
||||
ld a, e
|
||||
cp (ix)
|
||||
ret c ; E < (IX), situation 2
|
||||
ret z ; exact match!
|
||||
.next:
|
||||
call bufNext
|
||||
ret nz
|
||||
jr .loop
|
||||
|
@ -118,8 +118,7 @@ basLIST:
|
||||
call printstr
|
||||
ld a, ' '
|
||||
call stdioPutC
|
||||
push ix \ pop hl
|
||||
inc hl \ inc hl \ inc hl
|
||||
call bufStr
|
||||
call printstr
|
||||
call printcrlf
|
||||
call bufNext
|
||||
|
Loading…
Reference in New Issue
Block a user