diff --git a/apps/ed/README.md b/apps/ed/README.md index c16c7b5..2ea0bf9 100644 --- a/apps/ed/README.md +++ b/apps/ed/README.md @@ -10,11 +10,15 @@ mostly a repeat of `Ued`'s man page. ## Differences There are a couple of differences with `Ued` that are intentional. Differences -not listed here are either bugs or simply arent implemented yet. +not listed here are either bugs or simply aren't implemented yet. * Always has a prompt, `:`. * No size printing on load * Initial line is the first one +* Line input is for one line at once. Less scriptable for `Ued`, but we can't + script `ed` in Collapse OS anyway... +* For the sake of code simplicity, some commands that make no sense are + accepted. For example, `1,2a` is the same as `2a`. ## Usage @@ -32,6 +36,8 @@ range is out of bounds. * `(addrs)p`: Print lines specified in `addrs` range. This is the default command. If only `(addrs)` is specified, it has the same effect. * `(addrs)d`: Delete lines specified in `addrs` range. +* `(addr)a`: Appends a line after `addr`. +* `(addr)i`: Insert a line before `addr`. * `q`: quit `ed` ### Current line @@ -41,7 +47,7 @@ to it and makes the app much more usable. The current line starts at `1` and every command changes the current line to the last line that the command affects. For example, `42p` changes the current line to `42`, `3,7d`, to 7. -### Address ranges +### Addresses An "address" is a line number. The first line is `1`. An address range is a start line and a stop line, expressed as `start,stop`. For example, `2,4` refer diff --git a/apps/ed/buf.asm b/apps/ed/buf.asm index 7115b4e..65cc517 100644 --- a/apps/ed/buf.asm +++ b/apps/ed/buf.asm @@ -3,24 +3,30 @@ ; Lines in edited file aren't loaded in memory, their offsets is referenced to ; in this buffer. ; +; About scratchpad and offsets. There are two scratchpads: file and memory. +; The file one is the contents of the active blkdev. The second one is +; in-memory, for edits. We differentiate between the two with a +; "scatchpad mask". When the high bits of the offset match the mask, then we +; know that this offset is from the scratchpad. +; ; *** Consts *** ; ; Maximum number of lines allowed in the buffer. .equ BUF_MAXLINES 0x800 ; Size of our scratchpad .equ BUF_PADMAXLEN 0x1000 +; Scratchpad mask (only applies on high byte) +.equ BUF_SCRATCHPAD_MASK 0b11110000 ; *** Variables *** ; Number of lines currently in the buffer .equ BUF_LINECNT BUF_RAMSTART ; List of words pointing to scratchpad offsets .equ BUF_LINES BUF_LINECNT+2 -; size of file we read in bufInit. That offset is the beginning of our -; in-memory scratchpad. -.equ BUF_FSIZE BUF_LINES+BUF_MAXLINES*2 +; Points to the end of the scratchpad +.equ BUF_PADEND BUF_LINES+BUF_MAXLINES*2 ; The in-memory scratchpad -.equ BUF_PADLEN BUF_FSIZE+2 -.equ BUF_PAD BUF_PADLEN+2 +.equ BUF_PAD BUF_PADEND+2 .equ BUF_RAMEND BUF_PAD+BUF_PADMAXLEN @@ -29,8 +35,8 @@ ; On initialization, we read the whole contents of target blkdev and add lines ; as we go. bufInit: - ld hl, 0 - ld (BUF_PADLEN), hl + ld hl, BUF_PAD + ld (BUF_PADEND), hl ld ix, BUF_LINES ld bc, 0 ; line count .loop: @@ -45,8 +51,6 @@ bufInit: call ioGetLine jr .loop .loopend: - ; HL currently has the result of the last blkTell - ld (BUF_FSIZE), hl ld (BUF_LINECNT), bc ret @@ -76,10 +80,27 @@ bufGetLine: ld d, (hl) ; DE has seek offset ex de, hl - ; and now HL has it. We're ready to call ioGetLine! pop de + ; is it a scratchpad offset? + ld a, h + and BUF_SCRATCHPAD_MASK + cp BUF_SCRATCHPAD_MASK + jr z, .fromScratchpad + ; not from scratchpad cp a ; ensure Z jp ioGetLine ; preserves AF +.fromScratchpad: + ; remove scratchpad mask + ld a, BUF_SCRATCHPAD_MASK + xor 0xff + and h + ld h, a + ; HL is now a mask-less offset to BUF_PAD + push de ; --> lvl 1 + ld de, BUF_PAD + add hl, de + pop de ; <-- lvl 1 + ret .outOfBounds: pop de jp unsetZ @@ -125,3 +146,63 @@ bufDelLines: ; Both HL and DE are translated. Go! ldir ret + +; Insert string where DE points to memory scratchpad, then insert that line +; at index HL, offsetting all lines by 2 bytes. +bufInsertLine: + push de ; --> lvl 1, scratchpad offset + push hl ; --> lvl 2, insert index + ; The logic below is mostly copy-pasted from bufDelLines, but with a + ; LDDR logic (to avoid overwriting). I learned, with some pain involved, + ; that generalizing this code wasn't working very well. I don't repeat + ; the comments, refer to bufDelLines + ex de, hl ; line index now in DE + ld hl, (BUF_LINECNT) + scf \ ccf + sbc hl, de + ; mult by 2 and we're done + sla l \ rl h + push hl \ pop bc + ; From this point, we don't need our line index in DE any more because + ; LDDR will start from BUF_LINECNT-1 with count BC. We'll only need it + ; when it's time to insert the line in the space we make. + ld hl, (BUF_LINECNT) + call bufLineAddr + push hl \ pop de + dec hl + dec hl + ; HL = BUF_LINECNT-1, DE = BUF_LINECNT, BC is set. We're good! + lddr + ; We still need to increase BUF_LINECNT + ld hl, (BUF_LINECNT) + inc hl + ld (BUF_LINECNT), hl + ; A space has been opened at line index HL. Let's fill it with our + ; inserted line. + pop hl ; <-- lvl 2, insert index + call bufLineAddr + pop de ; <-- lvl 1, scratchpad offset + ld (hl), e + inc hl + ld (hl), d + ret + +; copy string that HL points to to scratchpad and return its seek offset, in HL. +bufScratchpadAdd: + push de + ld de, (BUF_PADEND) + push de ; --> lvl 1 + call strcpyM + ld (BUF_PADEND), de + pop hl ; <-- lvl 1 + ; we have a memory offset in HL, but it's not what we want! we want a + ; seek offset stamped with the "scratchpad mask" + ld de, BUF_PAD + scf \ ccf + sbc hl, de + ld a, h + or BUF_SCRATCHPAD_MASK + ld h, a + ; now we're good... + pop de + ret diff --git a/apps/ed/cmd.asm b/apps/ed/cmd.asm index bf731fd..473d17c 100644 --- a/apps/ed/cmd.asm +++ b/apps/ed/cmd.asm @@ -55,6 +55,10 @@ cmdParse: jr z, .okCmd cp 'd' jr z, .okCmd + cp 'a' + jr z, .okCmd + cp 'i' + jr z, .okCmd ; unsupported cmd ret ; Z unset .nullCmd: diff --git a/apps/ed/glue.asm b/apps/ed/glue.asm index 6eb3dd2..5a5a5cb 100644 --- a/apps/ed/glue.asm +++ b/apps/ed/glue.asm @@ -4,6 +4,7 @@ jp edMain +#include "lib/util.asm" #include "lib/parse.asm" .equ IO_RAMSTART USER_RAMSTART #include "ed/io.asm" diff --git a/apps/ed/main.asm b/apps/ed/main.asm index ea472c2..5582b01 100644 --- a/apps/ed/main.asm +++ b/apps/ed/main.asm @@ -39,9 +39,8 @@ ; intoHL ; printstr ; printcrlf -; stdioGetLine +; stdioReadLine ; stdioPutC -; stdioReadC ; unsetZ ; ; *** Variables *** @@ -59,19 +58,24 @@ edMain: .mainLoop: ld a, ':' call stdioPutC -.inner: - call stdioReadC - jr nz, .inner ; not done? loop - ; We're done. Process line. + call stdioReadLine ; --> HL + ; Now, process line. call printcrlf - call stdioGetLine call cmdParse jr nz, .error ld a, (CMD_TYPE) cp 'q' jr z, .doQuit + ; The rest of the commands need an address + call edReadAddrs + jr nz, .error + ld a, (CMD_TYPE) cp 'd' jr z, .doDel + cp 'a' + jr z, .doAppend + cp 'i' + jr z, .doInsert jr .doPrint .doQuit: @@ -79,16 +83,22 @@ edMain: 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 +.doAppend: + inc de +.doInsert: + call stdioReadLine ; --> HL + call bufScratchpadAdd ; --> HL + ; insert index in DE, line offset in HL. We want the opposite. + ex de, hl + call bufInsertLine + call printcrlf + jr .mainLoop + .doPrint: - call edReadAddrs - jr nz, .error -.doPrintLoop: push hl call bufGetLine jr nz, .error @@ -98,7 +108,7 @@ edMain: call cpHLDE jr z, .doPrintEnd inc hl - jr .doPrintLoop + jr .doPrint .doPrintEnd: ld (ED_CURLINE), hl jr .mainLoop diff --git a/apps/lib/util.asm b/apps/lib/util.asm new file mode 100644 index 0000000..61e1a32 --- /dev/null +++ b/apps/lib/util.asm @@ -0,0 +1,21 @@ +; Copy string from (HL) in (DE), that is, copy bytes until a null char is +; encountered. The null char is also copied. +; HL and DE point to the char right after the null char. +strcpyM: + ld a, (hl) + ld (de), a + inc hl + inc de + or a + jr nz, strcpyM + ret + +; Like strcpyM, but preserve HL and DE +strcpy: + push hl + push de + call strcpyM + pop de + pop hl + ret + diff --git a/apps/zasm/glue.asm b/apps/zasm/glue.asm index e7a9fd1..09aaf2b 100644 --- a/apps/zasm/glue.asm +++ b/apps/zasm/glue.asm @@ -53,6 +53,7 @@ jp zasmMain #include "zasm/const.asm" +#include "lib/util.asm" #include "zasm/util.asm" .equ IO_RAMSTART USER_RAMSTART #include "zasm/io.asm" diff --git a/apps/zasm/util.asm b/apps/zasm/util.asm index 4208510..a6f7179 100644 --- a/apps/zasm/util.asm +++ b/apps/zasm/util.asm @@ -107,27 +107,6 @@ strcmp: ; early, set otherwise) ret -; Copy string from (HL) in (DE), that is, copy bytes until a null char is -; encountered. The null char is also copied. -; HL and DE point to the char right after the null char. -strcpyM: - ld a, (hl) - ld (de), a - inc hl - inc de - or a - jr nz, strcpyM - ret - -; Like strcpyM, but preserve HL and DE -strcpy: - push hl - push de - call strcpyM - pop de - pop hl - ret - ; If string at (HL) starts with ( and ends with ), "enter" into the parens ; (advance HL and put a null char at the end of the string) and set Z. ; Otherwise, do nothing and reset Z. diff --git a/kernel/stdio.asm b/kernel/stdio.asm index 60b5fb5..0ecf8b9 100644 --- a/kernel/stdio.asm +++ b/kernel/stdio.asm @@ -202,3 +202,12 @@ stdioReadC: stdioGetLine: ld hl, STDIO_BUF ret + +; Repeatedly call stdioReadC until Z is set, then make HL point to the read +; buffer. +stdioReadLine: + call stdioReadC + jr nz, stdioReadLine + ld hl, STDIO_BUF + ret + diff --git a/tools/emul/shell/shell_.asm b/tools/emul/shell/shell_.asm index 624fea9..74ef5f5 100644 --- a/tools/emul/shell/shell_.asm +++ b/tools/emul/shell/shell_.asm @@ -36,8 +36,7 @@ jp _blkTell jp printcrlf jp stdioPutC - jp stdioReadC - jp stdioGetLine + jp stdioReadLine jp blkGetC jp blkSeek jp blkTell diff --git a/tools/emul/shell/user.h b/tools/emul/shell/user.h index feeb864..2eab1c8 100644 --- a/tools/emul/shell/user.h +++ b/tools/emul/shell/user.h @@ -29,8 +29,7 @@ .equ _blkTell 0x45 .equ printcrlf 0x48 .equ stdioPutC 0x4b -.equ stdioReadC 0x4e -.equ stdioGetLine 0x51 -.equ blkGetC 0x54 -.equ blkSeek 0x57 -.equ blkTell 0x5a +.equ stdioReadLine 0x4e +.equ blkGetC 0x51 +.equ blkSeek 0x54 +.equ blkTell 0x57