From 8af1cf468c0c7b199b8320717d09f13ffdec50aa Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Sun, 14 Jul 2019 10:32:28 -0400 Subject: [PATCH] ed: add 'd' cmd --- apps/ed/buf.asm | 56 ++++++++++++++++++++++++++++++++++++++++++++---- apps/ed/cmd.asm | 2 ++ apps/ed/main.asm | 10 +++++++++ kernel/README.md | 36 +++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 4 deletions(-) diff --git a/apps/ed/buf.asm b/apps/ed/buf.asm index ec695c9..82f2da3 100644 --- a/apps/ed/buf.asm +++ b/apps/ed/buf.asm @@ -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 diff --git a/apps/ed/cmd.asm b/apps/ed/cmd.asm index 9291004..bf731fd 100644 --- a/apps/ed/cmd.asm +++ b/apps/ed/cmd.asm @@ -53,6 +53,8 @@ cmdParse: jr z, .nullCmd cp 'p' jr z, .okCmd + cp 'd' + jr z, .okCmd ; unsupported cmd ret ; Z unset .nullCmd: diff --git a/apps/ed/main.asm b/apps/ed/main.asm index b80fbd2..5a7e1a0 100644 --- a/apps/ed/main.asm +++ b/apps/ed/main.asm @@ -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 diff --git a/kernel/README.md b/kernel/README.md index a712445..d32f474 100644 --- a/kernel/README.md +++ b/kernel/README.md @@ -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