ed: add 'd' cmd
This commit is contained in:
parent
50d0dc982c
commit
8af1cf468c
@ -46,6 +46,17 @@ bufAddLine:
|
||||
pop de
|
||||
ret
|
||||
|
||||
; transform line index HL into its corresponding memory address in BUF_LINES
|
||||
; array.
|
||||
bufLineAddr:
|
||||
push de
|
||||
ex de, hl
|
||||
ld hl, BUF_LINES
|
||||
add hl, de
|
||||
add hl, de ; twice, because two bytes per line
|
||||
pop de
|
||||
ret
|
||||
|
||||
; Read line number specified in HL and loads the I/O buffer with it.
|
||||
; Like ioGetLine, sets HL to line buffer pointer.
|
||||
; Sets Z on success, unset if out of bounds.
|
||||
@ -54,10 +65,7 @@ bufGetLine:
|
||||
ld de, (BUF_LINECNT)
|
||||
call cpHLDE
|
||||
jr nc, .outOfBounds ; HL > (BUF_LINECNT)
|
||||
ex de, hl
|
||||
ld hl, BUF_LINES
|
||||
add hl, de
|
||||
add hl, de ; twice, because two bytes per line
|
||||
call bufLineAddr
|
||||
; HL now points to seek offset in memory
|
||||
ld e, (hl)
|
||||
inc hl
|
||||
@ -71,3 +79,43 @@ bufGetLine:
|
||||
.outOfBounds:
|
||||
pop de
|
||||
jp unsetZ
|
||||
|
||||
; Given line indexes in HL and DE where HL < DE < CNT, move all lines between
|
||||
; DE and CNT by an offset of DE-HL. Also, adjust BUF_LINECNT by DE-HL.
|
||||
; WARNING: no bounds check. The only consumer of this routine already does
|
||||
; bounds check.
|
||||
bufDelLines:
|
||||
ex de, hl
|
||||
push hl ; --> lvl 1
|
||||
scf \ ccf
|
||||
sbc hl, de ; HL now has delcount -1
|
||||
inc hl ; adjust for actual delcount
|
||||
; We have the number of lines to delete in HL. We're going to move this
|
||||
; to BC for a LDIR, but before we do, there's two things we need to do:
|
||||
; adjust buffer line count and multiply by 2 (we move words, not bytes).
|
||||
push de ; --> lvl 2
|
||||
ex de, hl ; del cnt now in DE
|
||||
ld hl, (BUF_LINECNT)
|
||||
scf \ ccf
|
||||
sbc hl, de ; HL now has adjusted line cnt
|
||||
ld (BUF_LINECNT), hl
|
||||
; Good! one less thing to think about. Now, let's prepare moving DE
|
||||
; (delcnt) to BC. But first, we'll multiply by 2.
|
||||
sla e \ rl d
|
||||
push hl \ pop bc ; BC: delcount * 2
|
||||
pop de ; <-- lvl 2
|
||||
pop hl ; <-- lvl 1
|
||||
; At this point we have higher index in HL, lower index in DE and number
|
||||
; of bytes to delete in BC. It's convenient because it's rather close
|
||||
; to LDIR's signature! The only thing we need to do now is to translate
|
||||
; those HL and DE indexes in memory addresses, that is, multiply by 2
|
||||
; and add BUF_LINES
|
||||
push hl ; --> lvl 1
|
||||
ex de, hl
|
||||
call bufLineAddr
|
||||
ex de, hl
|
||||
pop hl ; <-- lvl 1
|
||||
call bufLineAddr
|
||||
; Both HL and DE are translated. Go!
|
||||
ldir
|
||||
ret
|
||||
|
@ -53,6 +53,8 @@ cmdParse:
|
||||
jr z, .nullCmd
|
||||
cp 'p'
|
||||
jr z, .okCmd
|
||||
cp 'd'
|
||||
jr z, .okCmd
|
||||
; unsupported cmd
|
||||
ret ; Z unset
|
||||
.nullCmd:
|
||||
|
@ -91,11 +91,21 @@ edMain:
|
||||
ld a, (CMD_TYPE)
|
||||
cp 'q'
|
||||
jr z, .doQuit
|
||||
cp 'd'
|
||||
jr z, .doDel
|
||||
jr .doPrint
|
||||
|
||||
.doQuit:
|
||||
xor a
|
||||
ret
|
||||
|
||||
.doDel:
|
||||
call edReadAddrs
|
||||
jr nz, .error
|
||||
; bufDelLines expects an exclusive upper bound, which is why we inc DE.
|
||||
inc de
|
||||
call bufDelLines
|
||||
jr .mainLoop
|
||||
.doPrint:
|
||||
call edReadAddrs
|
||||
jr nz, .error
|
||||
|
@ -35,4 +35,40 @@ Thus, code that glue parts together could look like:
|
||||
MOD2_RAMSTART .equ MOD1_RAMEND
|
||||
#include "mod2.asm"
|
||||
|
||||
## Stack management
|
||||
|
||||
Keeping the stack "balanced" is a big challenge when writing assembler code.
|
||||
Those push and pop need to correspond, otherwise we end up with completely
|
||||
broken code.
|
||||
|
||||
The usual "push/pop" at the beginning and end of a routine is rather easy to
|
||||
manage, nothing special about them.
|
||||
|
||||
The problem is for the "inner" push and pop, which are often necessary in
|
||||
routines handling more data at once. In those cases, we walk on eggshells.
|
||||
|
||||
A naive approach could be to indent the code between those push/pop, but indent
|
||||
level would quickly become too big to fit in 80 chars.
|
||||
|
||||
I've tried ASCII art in some places, where comments next to push/pop have "|"
|
||||
indicating the scope of the push/pop. It's nice, but it makes code complicated
|
||||
to edit, especially when dense comments are involved. The pipes have to go
|
||||
through them.
|
||||
|
||||
Of course, one could add descriptions next to each push/pop describing what is
|
||||
being pushed, and I do it in some places, but it doesn't help much in easily
|
||||
tracking down stack levels.
|
||||
|
||||
So, what I've started doing is to accompany each "non-routine" (at the
|
||||
beginning and end of a routine) push/pop with "--> lvl X" and "<-- lvl X"
|
||||
comments. Example:
|
||||
|
||||
push af ; --> lvl 1
|
||||
inc a
|
||||
push af ; --> lvl 2
|
||||
inc a
|
||||
pop af ; <-- lvl 2
|
||||
pop af ; <-- lvl 1
|
||||
|
||||
I think that this should do the trick, so I'll do this consistently from now on.
|
||||
[zasm]: ../apps/zasm/README.md
|
||||
|
Loading…
Reference in New Issue
Block a user