ed: add 'd' cmd

This commit is contained in:
Virgil Dupras 2019-07-14 10:32:28 -04:00
parent 50d0dc982c
commit 8af1cf468c
4 changed files with 100 additions and 4 deletions

View File

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

View File

@ -53,6 +53,8 @@ cmdParse:
jr z, .nullCmd
cp 'p'
jr z, .okCmd
cp 'd'
jr z, .okCmd
; unsupported cmd
ret ; Z unset
.nullCmd:

View File

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

View File

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