Files
VGMPLAY.WMF/src_sdcc/asm/txtlib.s
T

562 lines
17 KiB
ArmAsm
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;==============================================================================
; txtlib.s — Text buffer library (hand-optimised Z80, sdcccall(1))
;
; Replaces lib/txtlib.c. Every public function matches the ABI
; generated by SDCC for the prototypes in inc/txtlib.h.
;
; Buffer layout: buf[0] = max capacity, buf[1] = current length,
; buf[2..] = character data.
;
; sdcccall(1) register conventions:
; 1st ptr/u16 → HL 2nd ptr/u16 → DE
; 1st u8 → A 2nd u8 (when 1st is u8) → L
; Further args → stack (callee-cleans)
; Return u8 → A Return u16 → HL
;==============================================================================
.module txtlib_mod
.globl _buf_append_str
.globl _buf_append_char
.globl _buf_append_u8_hex
.globl _buf_append_u16_dec
.globl _buf_append_u32_dec
.globl _buf_append_mmss
.globl _print_line
.globl _wc_prsrw_attr ; from wc_api.s
.area _CODE
;----------------------------------------------------------------------
; Helper: nibble (0–15 in A) → ASCII char ('0'–'F') in A
;----------------------------------------------------------------------
_nib2asc:
cp a, #10
jr c, 001$
add a, #7
001$: add a, #0x30
ret
;======================================================================
; uint8_t buf_append_str(char *buf, const char *src)
; HL=buf DE=src → A=added (no stack params)
;======================================================================
_buf_append_str::
ld c, (hl) ; C = max
inc hl
ld b, (hl) ; B = curlen
ld a, c
sub a, b ; A = remaining
jr z, bas_z
jr c, bas_z
push hl ; save &buf[1]
ld c, a ; C = remaining
inc hl ; HL → buf[2]
ld a, b
add a, l
ld l, a
jr nc, bas_nc
inc h
bas_nc: ld b, #0 ; B = added
bas_lp: ld a, (de)
or a, a
jr z, bas_dn
ld (hl), a
inc hl
inc de
inc b
dec c
jr nz, bas_lp
bas_dn: pop hl ; &buf[1]
ld a, (hl)
add a, b
ld (hl), a
ld a, b
ret
bas_z: xor a, a
ret
;======================================================================
; uint8_t buf_append_char(char *buf, char c)
; HL=buf stack: c(1) → A=0|1 callee-cleans 1
;======================================================================
_buf_append_char::
push ix
ld ix, #0
add ix, sp
; 4(ix) = c
ld c, (hl) ; max
inc hl
ld a, (hl) ; curlen
cp a, c
jr nc, bac_f
ld e, a
inc a
ld (hl), a ; buf[1]++
inc hl ; → buf[2]
ld a, e
add a, l
ld l, a
jr nc, bac_nc
inc h
bac_nc: ld a, 4 (ix)
ld (hl), a
ld a, #1
jr bac_r
bac_f: xor a, a
bac_r: pop ix
pop hl
inc sp ; skip c (1 byte)
jp (hl)
;======================================================================
; uint8_t buf_append_u8_hex(char *buf, uint8_t val)
; HL=buf stack: val(1) → A=0|2 callee-cleans 1
;======================================================================
_buf_append_u8_hex::
push ix
ld ix, #0
add ix, sp
; 4(ix) = val
ld c, (hl) ; max
inc hl
ld a, c
sub a, (hl) ; remaining
cp a, #2
jr c, bh8_f
ld b, (hl) ; curlen
ld a, b
add a, #2
ld (hl), a ; buf[1] += 2
inc hl
ld a, b
add a, l
ld l, a
jr nc, bh8_nc
inc h
bh8_nc: ld a, 4 (ix)
rrca
rrca
rrca
rrca
and a, #0x0F
call _nib2asc
ld (hl), a
inc hl
ld a, 4 (ix)
and a, #0x0F
call _nib2asc
ld (hl), a
ld a, #2
jr bh8_r
bh8_f: xor a, a
bh8_r: pop ix
pop hl
inc sp
jp (hl)
;----------------------------------------------------------------------
; Shared helper: emit one decimal digit.
; A = digit (0–9)
; B = "added" counter (0 = not-yet-started → suppress leading 0)
; C = remaining buffer space
; IY = destination pointer (advanced on write)
;----------------------------------------------------------------------
_emit_digit:
push af
or a, b ; z iff digit==0 AND not started
jr z, ed_skip
ld a, c
or a, a
jr z, ed_skip ; no space
dec c
pop af
add a, #0x30
ld 0 (iy), a
inc iy
inc b
ret
ed_skip:
pop af
ret
;----------------------------------------------------------------------
; Shared helper: extract one 32-bit decimal digit via repeated
; subtraction of a 4-byte constant taken from inline data after CALL.
;
; Usage: call _d32_digit
; .db lo, b1, b2, hi ; 32-bit subtrahend (LE)
;
; In: DEHL = 32-bit value, -3(ix) = scratch,
; B = started/added, C = remaining, IY = dest ptr
; Out: DEHL = remainder, digit emitted via _emit_digit
;----------------------------------------------------------------------
_d32_digit:
ex (sp), hl ; HL = inline-data ptr, (SP)=val_lo
ld a, (hl)
ld (d32c0 + 1), a
inc hl
ld a, (hl)
ld (d32c1 + 1), a
inc hl
ld a, (hl)
ld (d32c2 + 1), a
inc hl
ld a, (hl)
ld (d32c3 + 1), a
inc hl ; HL past data = true ret addr
ex (sp), hl ; (SP)=true ret, HL=val_lo restored
ld -3 (ix), #0xFF
d32d_lp:inc -3 (ix)
push de
push hl
ld a, l
d32c0: sub a, #0x00 ; patched: subtrahend byte 0
ld l, a
ld a, h
d32c1: sbc a, #0x00 ; patched: byte 1
ld h, a
ld a, e
d32c2: sbc a, #0x00 ; patched: byte 2
ld e, a
ld a, d
d32c3: sbc a, #0x00 ; patched: byte 3
ld d, a
jr c, d32d_un
pop af
pop af
jr d32d_lp
d32d_un:pop hl
pop de
ld a, -3 (ix)
jp _emit_digit ; tail-call, ret goes past inline data
;----------------------------------------------------------------------
; Shared 16-bit decimal division: 10000/1000/100/10/ones.
; Entry _div_10k_ones: full 16-bit decimal (10000..1).
; Entry _div_1k_ones: skip 10000, do 1000..1.
; In: DE=value, B=added counter, C=remaining, IY=dst.
; Out: B=updated, DE=remainder. A,HL clobbered.
;----------------------------------------------------------------------
_div_10k_ones:
;-- 10000 (neg 0xD8F0) --
ld a, #0xFF
_d10k: inc a
ld hl, #0xD8F0
add hl, de
jr nc, _d10ke
ex de, hl
jr _d10k
_d10ke: call _emit_digit
_div_1k_ones:
;-- 1000 (neg 0xFC18) --
ld a, #0xFF
_d1k: inc a
ld hl, #0xFC18
add hl, de
jr nc, _d1ke
ex de, hl
jr _d1k
_d1ke: call _emit_digit
;-- 100 (neg 0xFF9C) --
ld a, #0xFF
_d100: inc a
ld hl, #0xFF9C
add hl, de
jr nc, _d100e
ex de, hl
jr _d100
_d100e: call _emit_digit
;-- 10 (neg 0xFFF6) --
ld a, #0xFF
_d10: inc a
ld hl, #0xFFF6
add hl, de
jr nc, _d10e
ex de, hl
jr _d10
_d10e: call _emit_digit
;-- ones (always emit) --
ld a, c
or a, a
ret z
ld a, e
add a, #0x30
ld 0 (iy), a
inc iy
inc b
ret
;======================================================================
; uint8_t buf_append_u16_dec(char *buf, uint16_t val)
; HL=buf DE=val → A=added (no stack params)
;======================================================================
_buf_append_u16_dec::
push iy
ld c, (hl)
inc hl
ld a, c
sub a, (hl)
jr z, d16_z
jr c, d16_z
ld c, a ; C = remaining
ld b, (hl) ; B = curlen (temp)
push hl ; save &buf[1]
inc hl
ld a, b
add a, l
ld l, a
jr nc, d16_nc
inc h
d16_nc: push hl
pop iy ; IY = dst
ld b, #0 ; B = added/started
call _div_10k_ones
pop hl ; &buf[1]
ld a, (hl)
add a, b
ld (hl), a
ld a, b
pop iy
ret
d16_z: xor a, a
pop iy
ret
;======================================================================
; uint8_t buf_append_u32_dec(char *buf, uint32_t val)
; HL=buf stack: val(4) → A=added callee-cleans 4
; 4(ix)=byte0(LSB) 5(ix)=byte1 6(ix)=byte2 7(ix)=byte3(MSB)
;======================================================================
_buf_append_u32_dec::
push ix
ld ix, #0
add ix, sp
push iy ; -2,-1(ix)
push af ; -4,-3(ix) scratch
push af ; -6,-5(ix) scratch
; -3(ix) used as digit counter per power
ld c, (hl)
inc hl
ld a, c
sub a, (hl)
jp z, d32_z
jp c, d32_z
ld c, a ; C = remaining
ld b, (hl) ; B = curlen (temp)
push hl ; save &buf[1] → -8,-7(ix)
inc hl
ld a, b
add a, l
ld l, a
jr nc, d32_nc
inc h
d32_nc: push hl
pop iy ; IY = dst
; Load 32-bit val into DEHL
ld l, 4 (ix)
ld h, 5 (ix)
ld e, 6 (ix)
ld d, 7 (ix)
ld b, #0 ; B = added/started
;-- 1,000,000 / 100,000 / 10,000 via shared subroutine --
call _d32_digit
.db 0x40, 0x42, 0x0F, 0x00 ; 1,000,000
call _d32_digit
.db 0xA0, 0x86, 0x01, 0x00 ; 100,000
call _d32_digit
.db 0x10, 0x27, 0x00, 0x00 ; 10,000
;-- remainder < 10000, fits in HL → move to DE --
ex de, hl
call _div_1k_ones
pop hl ; &buf[1]
ld a, (hl)
add a, b
ld (hl), a
ld a, b ; return value
d32_exit:
; clean locals, restore, callee-clean 4
pop de ; eat -6,-5
pop de ; eat -4,-3
pop iy
pop ix
pop hl ; ret addr
pop de ; skip val lo
pop de ; skip val hi
jp (hl)
d32_z: xor a, a
jr d32_exit
;======================================================================
; void print_line(wc_window_t *win, uint8_t y, const char *buf,
; uint8_t attr)
; HL=win stack: y(1) buf(2) attr(1) callee-cleans 4
; Prints buf content + spaces padding to fill content width.
;======================================================================
_print_line::
push ix
ld ix, #0
add ix, sp
; 4(ix)=y 5,6(ix)=buf 7(ix)=attr
push af ; -2,-1(ix): cw at -1, slen at -2
push hl ; -4,-3(ix): win
;-- cw = get_content_width(win) --
ld de, #4
add hl, de
ld a, (hl)
or a, a
jr nz, pl_nz
ld a, #96
pl_nz: sub a, #4
ld -1 (ix), a
;-- slen = buf[1] --
ld l, 5 (ix)
ld h, 6 (ix)
inc hl
ld a, (hl)
ld -2 (ix), a
;-- print_len = min(slen, cw) --
cp a, -1 (ix)
jr c, pl_min
ld a, -1 (ix)
pl_min: ld c, a ; C = print_len
;-- 1st wc_prsrw_attr(win, data, y, 2, print_len, attr) --
ld a, 7 (ix)
push af
inc sp
ld b, #0
push bc
ld h, #2
ld l, 4 (ix)
push hl
; DE = buf + 2
ld a, 5 (ix)
add a, #2
ld e, a
ld a, 6 (ix)
adc a, #0
ld d, a
; HL = win
ld l, -4 (ix)
ld h, -3 (ix)
call _wc_prsrw_attr
;-- padding? --
ld a, -2 (ix)
cp a, -1 (ix)
jr nc, pl_done
; pad = cw − slen
ld a, -1 (ix)
sub a, -2 (ix)
ld c, a
; Fill buf[2..2+pad-1] with 0x20 (runtime space fill)
ld l, 5 (ix)
ld h, 6 (ix)
inc hl
inc hl ; HL = &buf[2]
ld b, c ; B = pad count
ld a, #0x20
pf_fill:ld (hl), a
inc hl
djnz pf_fill ; B=0 after loop
;-- 2nd wc_prsrw_attr(win, &buf[2], y, 2+slen, pad, attr) --
ld a, 7 (ix)
push af
inc sp
push bc ; len=pad (B=0, C=pad)
ld a, -2 (ix)
add a, #2
ld h, a ; x = 2 + slen
ld l, 4 (ix) ; y
push hl
ld e, 5 (ix)
ld d, 6 (ix)
inc de
inc de ; DE = &buf[2]
ld l, -4 (ix)
ld h, -3 (ix)
call _wc_prsrw_attr
pl_done:
ld sp, ix
pop ix
pop hl
pop af
pop af ; callee-clean 4
jp (hl)
;----------------------------------------------------------------------
; buf_append_mmss(buf, sec) — append "MM:SS" (5 chars) to buffer
;
; sdcccall(1): HL = buf, DE = sec (uint16_t, 0..5999)
; No division — subtraction loops only (~30 bytes code).
;----------------------------------------------------------------------
_buf_append_mmss:
push hl
pop ix ; IX = buf
; DE = total seconds. Divide by 60 via subtraction → B=min, E=sec
ld b, #0
mmss_d60:
ld a, e
sub a, #60
ld c, a
ld a, d
sbc a, #0
jr c, mmss_d60_done
ld d, a
ld e, c
inc b
jr mmss_d60
mmss_d60_done:
; B = minutes, E = seconds
push de ; save seconds (mmss_2dig clobbers DE)
ld a, b
call mmss_2dig ; appends 2 digits of A
; Append ':'
ld a, 1 (ix)
ld c, a
ld b, #0
inc a
ld 1 (ix), a
push ix
pop hl
add hl, bc
inc hl ; skip buf[0] (max)
inc hl ; skip buf[1] (len)
ld (hl), #0x3A ; ':'
; Append seconds
pop de ; restore E = seconds
ld a, e
; fall through to mmss_2dig — but IX is still buf
; call mmss_2dig
call mmss_2dig
ret
; Subroutine: append 2 ASCII digits of A (0..99) to buf at IX
; Destroys A, C, B, HL
mmss_2dig:
ld c, #0x30 ; tens digit = '0'
mmss_t10:
sub a, #10
jr c, mmss_t10_done
inc c
jr mmss_t10
mmss_t10_done:
add a, #10 ; undo last sub
add a, #0x30 ; units → ASCII
ld b, a ; save units
; Append tens (C) then units (B)
ld a, 1 (ix) ; len
ld l, a
ld h, #0
add a, #2
ld 1 (ix), a ; len += 2
push ix
pop de ; DE = buf base
add hl, de
inc hl ; skip buf[0] (max)
inc hl ; skip buf[1] (len)
ld (hl), c ; tens
inc hl
ld (hl), b ; units
ret