2019-07-13 09:57:37 -04:00
|
|
|
; ed - line editor
|
|
|
|
;
|
|
|
|
; A text editor modeled after UNIX's ed, but simpler. The goal is to stay tight
|
|
|
|
; on resources and to avoid having to implement screen management code (that is,
|
|
|
|
; develop the machinery to have ncurses-like apps in Collapse OS).
|
|
|
|
;
|
|
|
|
; ed has a mechanism to avoid having to move a lot of memory around at each
|
|
|
|
; edit. Each line is an element in an doubly-linked list and each element point
|
|
|
|
; to an offset in the "scratchpad". The scratchpad starts with the file
|
|
|
|
; contents and every time we change or add a line, that line goes to the end of
|
|
|
|
; the scratch pad and linked lists are reorganized whenever lines are changed.
|
|
|
|
; Contents itself is always appended to the scratchpad.
|
|
|
|
;
|
|
|
|
; That's on a resourceful UNIX system.
|
|
|
|
;
|
|
|
|
; That doubly linked list on the z80 would use 7 bytes per line (prev, next,
|
|
|
|
; offset, len), which is a bit much. Moreover, there's that whole "scratchpad
|
|
|
|
; being loaded in memory" thing that's a bit iffy. We sacrifice speed for
|
|
|
|
; memory usage.
|
|
|
|
;
|
|
|
|
; So here's what we do. First, we have two scratchpads. The first one is the
|
2019-07-13 11:29:06 -04:00
|
|
|
; file being read itself. The second one is memory, for modifications we
|
2019-07-13 09:57:37 -04:00
|
|
|
; make to the file. When reading the file, we note the offset at which it ends.
|
|
|
|
; All offsets under this limit refer to the first scratchpad. Other offsets
|
|
|
|
; refer to the second.
|
|
|
|
;
|
2019-07-13 11:29:06 -04:00
|
|
|
; Then, our line list is just an array of 16-bit offsets. This means that we
|
2019-07-13 09:57:37 -04:00
|
|
|
; don't have an easy access to line length and we have to move a lot of memory
|
|
|
|
; around whenever we add or delete lines. Hopefully, "LDIR" will be our friend
|
|
|
|
; here...
|
|
|
|
;
|
|
|
|
; *** Requirements ***
|
2019-07-13 11:29:06 -04:00
|
|
|
; BLOCKDEV_SIZE
|
2019-07-20 19:35:19 -04:00
|
|
|
; FS_HANDLE_SIZE
|
|
|
|
; _blkGetC
|
|
|
|
; _blkPutC
|
|
|
|
; _blkSeek
|
|
|
|
; _blkTell
|
2019-07-13 14:01:20 -04:00
|
|
|
; addHL
|
|
|
|
; cpHLDE
|
2019-07-20 19:35:19 -04:00
|
|
|
; fsFindFN
|
|
|
|
; fsOpen
|
|
|
|
; fsGetC
|
|
|
|
; fsPutC
|
2019-07-13 14:01:20 -04:00
|
|
|
; intoHL
|
2019-07-13 09:57:37 -04:00
|
|
|
; printstr
|
|
|
|
; printcrlf
|
2019-07-14 17:29:00 -04:00
|
|
|
; stdioReadLine
|
2019-07-13 14:01:20 -04:00
|
|
|
; stdioPutC
|
2019-07-13 11:53:30 -04:00
|
|
|
; unsetZ
|
2019-07-13 15:28:44 -04:00
|
|
|
;
|
|
|
|
; *** Variables ***
|
|
|
|
;
|
|
|
|
.equ ED_CURLINE ED_RAMSTART
|
|
|
|
.equ ED_RAMEND ED_CURLINE+2
|
2019-07-13 09:57:37 -04:00
|
|
|
|
|
|
|
edMain:
|
2019-07-20 19:35:19 -04:00
|
|
|
; because ed only takes a single string arg, we can use HL directly
|
|
|
|
call ioInit
|
|
|
|
ret nz
|
2019-07-13 15:28:44 -04:00
|
|
|
; diverge from UNIX: start at first line
|
|
|
|
ld hl, 0
|
|
|
|
ld (ED_CURLINE), hl
|
|
|
|
|
2019-07-14 12:19:37 -04:00
|
|
|
call bufInit
|
2019-07-13 11:29:06 -04:00
|
|
|
|
2019-07-13 15:28:44 -04:00
|
|
|
.mainLoop:
|
2019-07-13 14:01:20 -04:00
|
|
|
ld a, ':'
|
|
|
|
call stdioPutC
|
2019-07-14 17:29:00 -04:00
|
|
|
call stdioReadLine ; --> HL
|
|
|
|
; Now, process line.
|
2019-07-13 09:57:37 -04:00
|
|
|
call printcrlf
|
2019-07-13 21:08:16 -04:00
|
|
|
call cmdParse
|
|
|
|
jr nz, .error
|
2019-07-14 09:04:51 -04:00
|
|
|
ld a, (CMD_TYPE)
|
2019-07-13 09:57:37 -04:00
|
|
|
cp 'q'
|
2019-07-14 09:04:51 -04:00
|
|
|
jr z, .doQuit
|
2019-07-14 17:29:00 -04:00
|
|
|
; The rest of the commands need an address
|
|
|
|
call edReadAddrs
|
|
|
|
jr nz, .error
|
|
|
|
ld a, (CMD_TYPE)
|
2019-07-14 10:32:28 -04:00
|
|
|
cp 'd'
|
|
|
|
jr z, .doDel
|
2019-07-14 17:29:00 -04:00
|
|
|
cp 'a'
|
|
|
|
jr z, .doAppend
|
|
|
|
cp 'i'
|
|
|
|
jr z, .doInsert
|
2019-07-13 21:08:16 -04:00
|
|
|
jr .doPrint
|
|
|
|
|
2019-07-14 09:04:51 -04:00
|
|
|
.doQuit:
|
|
|
|
xor a
|
|
|
|
ret
|
2019-07-14 10:32:28 -04:00
|
|
|
|
|
|
|
.doDel:
|
|
|
|
; bufDelLines expects an exclusive upper bound, which is why we inc DE.
|
|
|
|
inc de
|
|
|
|
call bufDelLines
|
|
|
|
jr .mainLoop
|
2019-07-14 17:29:00 -04:00
|
|
|
.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
|
|
|
|
|
2019-07-13 21:08:16 -04:00
|
|
|
.doPrint:
|
2019-07-13 22:09:17 -04:00
|
|
|
push hl
|
2019-07-13 14:01:20 -04:00
|
|
|
call bufGetLine
|
2019-07-13 21:08:16 -04:00
|
|
|
jr nz, .error
|
2019-07-13 11:53:30 -04:00
|
|
|
call printstr
|
|
|
|
call printcrlf
|
2019-07-13 22:09:17 -04:00
|
|
|
pop hl
|
|
|
|
call cpHLDE
|
2019-07-14 09:04:51 -04:00
|
|
|
jr z, .doPrintEnd
|
2019-07-13 22:09:17 -04:00
|
|
|
inc hl
|
2019-07-14 17:29:00 -04:00
|
|
|
jr .doPrint
|
2019-07-14 09:04:51 -04:00
|
|
|
.doPrintEnd:
|
|
|
|
ld (ED_CURLINE), hl
|
|
|
|
jr .mainLoop
|
2019-07-13 21:08:16 -04:00
|
|
|
.error:
|
2019-07-13 14:01:20 -04:00
|
|
|
ld a, '?'
|
|
|
|
call stdioPutC
|
|
|
|
call printcrlf
|
2019-07-13 21:08:16 -04:00
|
|
|
jr .mainLoop
|
2019-07-13 14:01:20 -04:00
|
|
|
|
2019-07-13 21:08:16 -04:00
|
|
|
|
|
|
|
; Transform an address "cmd" in IX into an absolute address in HL.
|
|
|
|
edResolveAddr:
|
|
|
|
ld a, (ix)
|
|
|
|
cp RELATIVE
|
|
|
|
jr z, .relative
|
|
|
|
; absolute
|
|
|
|
ld l, (ix+1)
|
|
|
|
ld h, (ix+2)
|
2019-07-13 16:30:30 -04:00
|
|
|
ret
|
2019-07-13 21:08:16 -04:00
|
|
|
.relative:
|
2019-07-13 16:30:30 -04:00
|
|
|
ld hl, (ED_CURLINE)
|
2019-07-13 22:09:17 -04:00
|
|
|
push de
|
2019-07-13 21:08:16 -04:00
|
|
|
ld e, (ix+1)
|
|
|
|
ld d, (ix+2)
|
2019-07-13 16:30:30 -04:00
|
|
|
add hl, de
|
2019-07-13 22:09:17 -04:00
|
|
|
pop de
|
2019-07-13 16:30:30 -04:00
|
|
|
ret
|
2019-07-13 15:28:44 -04:00
|
|
|
|
2019-07-14 09:04:51 -04:00
|
|
|
; Read absolute addr1 in HL and addr2 in DE. Also, check bounds and set Z if
|
|
|
|
; both addresses are within bounds, unset if not.
|
|
|
|
edReadAddrs:
|
|
|
|
ld ix, CMD_ADDR2
|
|
|
|
call edResolveAddr
|
|
|
|
ex de, hl
|
|
|
|
ld hl, (BUF_LINECNT)
|
|
|
|
ex de, hl ; HL: addr2 DE: cnt
|
|
|
|
call cpHLDE
|
|
|
|
jp nc, unsetZ ; HL (addr2) >= DE (cnt). no good
|
|
|
|
ex de, hl ; DE: addr2
|
|
|
|
ld ix, CMD_ADDR1
|
|
|
|
call edResolveAddr
|
|
|
|
ex de, hl ; HL: addr2, DE: addr1
|
|
|
|
call cpHLDE
|
|
|
|
jp c, unsetZ ; HL (addr2) < DE (addr1). no good
|
|
|
|
ex de, hl ; HL: addr1, DE: addr2
|
|
|
|
cp a ; ensure Z
|
|
|
|
ret
|
|
|
|
|