collapseos/apps/ed/buf.asm

212 lines
5.6 KiB
NASM

; buf - manage line buffer
;
; *** Variables ***
; Number of lines currently in the buffer
.equ BUF_LINECNT BUF_RAMSTART
; List of pointers to strings in scratchpad
.equ BUF_LINES @+2
; Points to the end of the scratchpad, that is, one byte after the last written
; char in it.
.equ BUF_PADEND @+ED_BUF_MAXLINES*2
; The in-memory scratchpad
.equ BUF_PAD @+2
.equ BUF_RAMEND @+ED_BUF_PADMAXLEN
; *** Code ***
; On initialization, we read the whole contents of target blkdev and add lines
; as we go.
bufInit:
ld hl, BUF_PAD ; running pointer to end of pad
ld de, BUF_PAD ; points to beginning of current line
ld ix, BUF_LINES ; points to current line index
ld bc, 0 ; line count
; init pad end in case we have an empty file.
ld (BUF_PADEND), hl
.loop:
call ioGetB
jr nz, .loopend
or a ; null? hum, weird. same as LF
jr z, .lineend
cp 0x0a
jr z, .lineend
ld (hl), a
inc hl
jr .loop
.lineend:
; We've just finished reading a line, writing each char in the pad.
; Null terminate it.
xor a
ld (hl), a
inc hl
; Now, let's register its pointer in BUF_LINES
ld (ix), e
inc ix
ld (ix), d
inc ix
inc bc
ld (BUF_PADEND), hl
ld de, (BUF_PADEND)
jr .loop
.loopend:
ld (BUF_LINECNT), bc
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 make HL point to its contents.
; Sets Z on success, unset if out of bounds.
bufGetLine:
push de ; --> lvl 1
ld de, (BUF_LINECNT)
call cpHLDE
pop de ; <-- lvl 1
jp nc, unsetZ ; HL > (BUF_LINECNT)
call bufLineAddr
; HL now points to an item in BUF_LINES.
call intoHL
; Now, HL points to our contents
cp a ; ensure Z
ret
; 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:
; Let's start with setting up BC, which is (CNT-DE) * 2
push hl ; --> lvl 1
ld hl, (BUF_LINECNT)
scf \ ccf
sbc hl, de
; mult by 2 and we're done
sla l \ rl h
push hl \ pop bc
pop hl ; <-- lvl 1
; Good! BC done. Now, let's adjust BUF_LINECNT by DE-HL
push hl ; --> lvl 1
scf \ ccf
sbc hl, de ; HL -> nb of lines to delete, negative
push de ; --> lvl 2
ld de, (BUF_LINECNT)
add hl, de ; adding DE to negative HL
ld (BUF_LINECNT), hl
pop de ; <-- lvl 2
pop hl ; <-- lvl 1
; Line count updated!
; One other thing... is BC zero? Because if it is, then we shouldn't
; call ldir (otherwise we're on for a veeeery long loop), BC=0 means
; that only last lines were deleted. nothing to do.
ld a, b
or c
ret z ; BC==0, return
; let's have invert HL and DE to match LDIR's signature
ex de, hl
; 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
; Insert string where DE points to memory scratchpad, then insert that line
; at index HL, offsetting all lines by 2 bytes.
bufInsertLine:
call bufIndexInBounds
jr nz, .append
push de ; --> lvl 1, scratchpad ptr
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
; HL is pointing to *first byte* after last line. Our source needs to
; be the second byte of the last line and our dest is the second byte
; after the last line.
push hl \ pop de
dec hl ; second byte of last line
inc de ; second byte beyond last line
; HL = BUF_LINECNT-1, DE = BUF_LINECNT, BC is set. We're good!
lddr
.set:
; 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
.append:
; nothing to move, just put the line there. Let's piggy-back on the end
; of the regular routine by carefully pushing the right register in the
; right place.
; But before that, make sure that HL isn't too high. The only place we
; can append to is at (BUF_LINECNT)
ld hl, (BUF_LINECNT)
push de ; --> lvl 1
push hl ; --> lvl 2
jr .set
; copy string that HL points to to scratchpad and return its pointer in
; scratchpad, in HL.
bufScratchpadAdd:
push de
ld de, (BUF_PADEND)
push de ; --> lvl 1
call strcpyM
inc de ; pad end is last char + 1
ld (BUF_PADEND), de
pop hl ; <-- lvl 1
pop de
ret
; Sets Z according to whether the line index in HL is within bounds.
bufIndexInBounds:
push de
ld de, (BUF_LINECNT)
call cpHLDE
pop de
jr c, .withinBounds
; out of bounds
jp unsetZ
.withinBounds:
cp a ; ensure Z
ret