mirror of
https://github.com/hsoft/collapseos.git
synced 2024-11-23 19:48:06 +11:00
ed: add support for 'a' and 'i'
This commit is contained in:
parent
77a23cee84
commit
eefadc3917
@ -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
|
||||
|
101
apps/ed/buf.asm
101
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
|
||||
|
@ -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:
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
jp edMain
|
||||
|
||||
#include "lib/util.asm"
|
||||
#include "lib/parse.asm"
|
||||
.equ IO_RAMSTART USER_RAMSTART
|
||||
#include "ed/io.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
|
||||
|
21
apps/lib/util.asm
Normal file
21
apps/lib/util.asm
Normal file
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -36,8 +36,7 @@
|
||||
jp _blkTell
|
||||
jp printcrlf
|
||||
jp stdioPutC
|
||||
jp stdioReadC
|
||||
jp stdioGetLine
|
||||
jp stdioReadLine
|
||||
jp blkGetC
|
||||
jp blkSeek
|
||||
jp blkTell
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user