Files
Sprinter-SDCC/libc/bios/text.c
T
snark13 c9d5ea35a4 libc: add bios/text.h BIOS text-output API
Direct __naked z80 wrappers over BIOS LP_PRINT_*/LP_SET_PLACE/LP_GET_PLACE
(RST 8, opcodes 081h-08Eh) for fast text rendering — a single BIOS call
replaces a wrchar() loop for constant-attribute/constant-character runs.
Covered by tests/bios_text.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 17:06:13 +03:00

303 lines
8.7 KiB
C

/*
* text.c — wrappers for the Sprinter BIOS text-output calls (RST 8,
* 081h..08Eh "LP_*"). See <bios/text.h> for the full opcode -> name
* mapping and semantics.
*
* ABI recap (docs/converted/bios.txt):
* - 081h/082h/083h explicitly preserve HL and IX -> no push/pop ix
* needed, and the result is the smallest/fastest of the set.
* - 084h/08Eh (place get/set) are used without push/pop ix elsewhere
* in this libc (libc/conio/conio.c gotoxy/wherex/wherey via the same
* opcodes) — empirically safe, followed here too.
* - Everything else clobbers IX like any other BIOS/ESTEX call, so
* every RST 8 below is bracketed with push ix / pop ix.
*
* Stack-argument layout: SDCC __sdcccall(1) places the first 8-bit arg
* in A, the second (only if the first was also 8-bit) in L; every arg
* beyond that lands on the stack in left-to-right declaration order,
* starting at [SP+2] (right after the 2-byte return address) — verified
* empirically via `sdcc -S` for 2..6 uint8 args and for pointer+N uint8
* args. IY is used as a scratch index register to read those without
* disturbing IX (the caller's frame pointer).
*/
#include <bios/text.h>
void bios_fillcharattr(char ch, uint8_t attr, uint8_t count) __naked
{
(void)ch; (void)attr; (void)count;
__asm
;; ch->A, attr->L, count on stack at [SP+2].
;; LP_PRINT_ALL (081h): A=ch, E=attr, B=count. Preserves HL,IX.
ld iy, #2
add iy, sp
ld b, 0 (iy) ; B = count
ld e, l ; E = attr
ld c, #0x81
rst #0x08
pop hl ; return address
inc sp ; consume count byte
jp (hl)
__endasm;
}
void bios_fillchar(char ch, uint8_t count) __naked
{
(void)ch; (void)count;
__asm
;; ch->A, count->L. LP_PRINT_SYM (082h): A=ch, B=count.
;; Preserves HL,IX.
ld b, l
ld c, #0x82
rst #0x08
ret
__endasm;
}
void bios_fillattr(uint8_t attr, uint8_t count) __naked
{
(void)attr; (void)count;
__asm
;; attr->A, count->L. LP_PRINT_ATR (083h): E=attr, B=count.
;; Preserves HL,IX.
ld e, a
ld b, l
ld c, #0x83
rst #0x08
ret
__endasm;
}
void bios_set_place(uint8_t row, uint8_t col) __naked
{
(void)row; (void)col;
__asm
;; row->A, col->L. LP_SET_PLACE (084h): D=row, E=col.
ld d, a
ld e, l
ld c, #0x84
rst #0x08
ret
__endasm;
}
uint16_t bios_get_place(void) __naked
{
__asm
;; LP_GET_PLACE (08Eh): returns D=row, E=col already the
;; uint16_t return convention (DE); nothing left to rearrange.
ld c, #0x8E
rst #0x08
ret
__endasm;
}
void bios_writeattr(const char *s, uint8_t len, uint8_t attr) __naked
{
(void)s; (void)len; (void)attr;
__asm
;; s->HL, len/attr on stack at [SP+2]/[SP+3].
;; LP_PRINT_LN (085h): HL=s, B=len, E=attr.
ld iy, #2
add iy, sp
ld b, 0 (iy) ; B = len
ld e, 1 (iy) ; E = attr
push ix
ld c, #0x85
rst #0x08
pop ix
pop hl ; return address
inc sp
inc sp ; consume len + attr
jp (hl)
__endasm;
}
void bios_write(const char *s, uint8_t len) __naked
{
(void)s; (void)len;
__asm
;; s->HL, len on stack at [SP+2].
;; LP_PRINT_LN2 (086h): HL=s, B=len.
ld iy, #2
add iy, sp
ld b, 0 (iy) ; B = len
push ix
ld c, #0x86
rst #0x08
pop ix
pop hl
inc sp
jp (hl)
__endasm;
}
void bios_writeattr_until(const char *s, uint8_t len, uint8_t attr, char sep) __naked
{
(void)s; (void)len; (void)attr; (void)sep;
__asm
;; s->HL, len/attr/sep on stack at [SP+2..4].
;; LP_PRINT_LN3 (087h): HL=s, B=len, E=attr, D=sep.
;; Pads with spaces after sep up to len.
ld iy, #2
add iy, sp
ld b, 0 (iy) ; B = len
ld e, 1 (iy) ; E = attr
ld d, 2 (iy) ; D = sep
push ix
ld c, #0x87
rst #0x08
pop ix
pop hl
inc sp
inc sp
inc sp ; consume len + attr + sep
jp (hl)
__endasm;
}
void bios_write_until(const char *s, uint8_t len, char sep) __naked
{
(void)s; (void)len; (void)sep;
__asm
;; s->HL, len/sep on stack at [SP+2..3].
;; LP_PRINT_LN4 (088h): HL=s, B=len, D=sep. Pads with spaces.
ld iy, #2
add iy, sp
ld b, 0 (iy) ; B = len
ld d, 1 (iy) ; D = sep
push ix
ld c, #0x88
rst #0x08
pop ix
pop hl
inc sp
inc sp
jp (hl)
__endasm;
}
void bios_clearwin(uint8_t row, uint8_t col, uint8_t height, uint8_t width,
uint8_t attr) __naked
{
(void)row; (void)col; (void)height; (void)width; (void)attr;
__asm
;; row->A, col->L, height/width/attr on stack at [SP+2..4].
;; LP_CLS_WIN (089h): D=row, E=col, H=height, L=width, B=attr.
ld iy, #2
add iy, sp
ld h, 0 (iy) ; H = height
ld c, 1 (iy) ; stash width (L is still col for now)
ld e, l ; E = col
ld d, a ; D = row
ld l, c ; L = width
ld b, 2 (iy) ; B = attr
push ix
ld c, #0x89
rst #0x08
pop ix
pop hl
inc sp
inc sp
inc sp ; consume height + width + attr
jp (hl)
__endasm;
}
void bios_scrollwin(uint8_t dir, uint8_t row, uint8_t count) __naked
{
(void)dir; (void)row; (void)count;
__asm
;; dir->A, row->L, count on stack at [SP+2].
;; LP_SCROLL_UD (08Ah): B=dir(1 up/2 down), D=row, E=count.
ld iy, #2
add iy, sp
ld e, 0 (iy) ; E = count
ld d, l ; D = row
ld b, a ; B = dir
push ix
ld c, #0x8A
rst #0x08
pop ix
pop hl
inc sp
jp (hl)
__endasm;
}
void bios_writeattr_stop(const char *s, uint8_t maxlen, uint8_t attr,
char sep) __naked
{
(void)s; (void)maxlen; (void)attr; (void)sep;
__asm
;; s->HL, maxlen/attr/sep on stack at [SP+2..4].
;; LP_PRINT_LN5 (08Bh): HL=s, B=maxlen, E=attr, D=sep.
;; Stops at sep (no padding).
ld iy, #2
add iy, sp
ld b, 0 (iy) ; B = maxlen
ld e, 1 (iy) ; E = attr
ld d, 2 (iy) ; D = sep
push ix
ld c, #0x8B
rst #0x08
pop ix
pop hl
inc sp
inc sp
inc sp
jp (hl)
__endasm;
}
void bios_write_stop(const char *s, uint8_t maxlen, char sep) __naked
{
(void)s; (void)maxlen; (void)sep;
__asm
;; s->HL, maxlen/sep on stack at [SP+2..3].
;; LP_PRINT_LN6 (08Ch): HL=s, B=maxlen, D=sep. Stops at sep.
ld iy, #2
add iy, sp
ld b, 0 (iy) ; B = maxlen
ld d, 1 (iy) ; D = sep
push ix
ld c, #0x8C
rst #0x08
pop ix
pop hl
inc sp
inc sp
jp (hl)
__endasm;
}
void bios_clearwin_ch(uint8_t row, uint8_t col, uint8_t height,
uint8_t width, uint8_t attr, char fillch) __naked
{
(void)row; (void)col; (void)height; (void)width; (void)attr; (void)fillch;
__asm
;; row->A, col->L, height/width/attr/fillch on stack [SP+2..5].
;; LP_CLS_WIN2 (08Dh): D=row, E=col, H=height, L=width, B=attr,
;; A=fillch.
ld iy, #2
add iy, sp
ld d, a ; D = row (free A for fillch later)
ld e, l ; E = col (free L for width later)
ld h, 0 (iy) ; H = height
ld c, 1 (iy) ; stash width
ld b, 2 (iy) ; B = attr
ld a, 3 (iy) ; A = fillch
ld l, c ; L = width
push ix
ld c, #0x8D
rst #0x08
pop ix
pop hl
inc sp
inc sp
inc sp
inc sp ; consume height + width + attr + fillch
jp (hl)
__endasm;
}