Files
snark13 737c974400 Add mdview markdown viewer, reorganize tests/examples and libc layout
- Split tests/ (libc feature tests) and examples/ (real apps); shared
  app.mk in repo root, was examples/example.mk
- libc/io/* split into libc/{conio,env,errno,file,mouse,string,sys,
  time,video}/ — clearer module boundaries
- New examples/mdview/: markdown viewer (Phases 1-5 + light nested
  lists). Headers (H1-H4), HR, ulist/olist/quote with nesting via
  leading spaces, fenced code blocks, inline emphasis (bold/italic/
  underscore/code), wrap/unwrap mode with soft wrap (F2), horizontal
  pan (← →) with '>' truncation indicator
- libc additions: scroll() in conio (ESTEX SCROLL), strlwr/strupr,
  gets() test
- Makefile updates across tests/ for the new shared app.mk path

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 22:23:36 +03:00

306 lines
8.4 KiB
C
Raw Permalink 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.
/*
* mouse.c — Sprinter mouse driver wrappers (RST 30h).
*
* All calls use the same pattern as ESTEX (push/pop IX around the RST)
* since the driver doesn't promise to preserve registers either.
*/
#include <mouse.h>
/* Scratch for READ_STATE — RST 30h clobbers HL/DE/A so we can't keep the
* state pointer in HL across the call. Explicit `= 0` so SDCC reserves
* real BSS storage — see memory/sdcc_static_storage_gotcha.md. */
static uint16_t mb_x = 0, mb_y = 0;
static uint8_t mb_buttons = 0;
int mouse_init(void) __naked
{
__asm
push ix
ld c, #0x00 ; INITIALIZATION
rst #0x30 ; MOUSE
pop ix
jr c, _mi_err
ld de, #0
ret
_mi_err:
ld de, #-1
ret
__endasm;
}
void mouse_show(void) __naked
{
__asm
push ix
ld c, #0x01 ; SHOW MOUSE CURSOR
rst #0x30 ; MOUSE
pop ix
ret
__endasm;
}
void mouse_hide(void) __naked
{
__asm
push ix
ld c, #0x02 ; HIDE MOUSE CURSOR
rst #0x30 ; MOUSE
pop ix
ret
__endasm;
}
void mouse_refresh(void) __naked
{
__asm
push ix
ld c, #0x83 ; MOUSE REFRESH
rst #0x30 ; MOUSE
pop ix
ret
__endasm;
}
void mouse_read(mouse_state_t *st) __naked
{
(void)st;
__asm
;; HL = state ptr on entry.
push ix
push hl ; stash ptr across RST
ld c, #0x03 ; READ MOUSE STATE
rst #0x30 ; MOUSE
;; Returns: A=buttons, HL=x, DE=y (CF=err but we ignore here)
ld (_mb_x), hl
ld (_mb_y), de
ld (_mb_buttons), a
pop hl ; restore state ptr
pop ix
;; Copy scratch *st. Struct layout: x(2), y(2), buttons(1).
ld de, (_mb_x)
ld (hl), e
inc hl
ld (hl), d
inc hl
ld de, (_mb_y)
ld (hl), e
inc hl
ld (hl), d
inc hl
ld a, (_mb_buttons)
ld (hl), a
ret
__endasm;
}
void mouse_goto(int x, int y) __naked
{
(void)x; (void)y;
__asm
;; HL = x, DE = y.
push ix
ld c, #0x04 ; GOTO MOUSE CURSOR
rst #0x30 ; MOUSE
pop ix
ret
__endasm;
}
void mouse_bounds_x(int xmin, int xmax) __naked
{
(void)xmin; (void)xmax;
__asm
;; HL = xmin, DE = xmax.
push ix
ld c, #0x08 ; HORZ BOUNDS
rst #0x30 ; MOUSE
pop ix
ret
__endasm;
}
void mouse_bounds_y(int ymin, int ymax) __naked
{
(void)ymin; (void)ymax;
__asm
;; HL = ymin, DE = ymax.
push ix
ld c, #0x07 ; VERT BOUNDS
rst #0x30 ; MOUSE
pop ix
ret
__endasm;
}
/* ---- $09 LOAD CURSOR + $0B RETURN CURSOR ---------------------- */
/* Scratch for the IX-passing convention — RST 30h takes the bitmap
* pointer in IX, so we have to set it up explicitly.
*
* Initialised to 0 so SDCC reserves real BSS storage — uninitialised
* `static uint8_t` declarations can coalesce to a single address and
* stomp on each other. See memory/sdcc_static_storage_gotcha.md. */
static uint16_t mc_image = 0;
static uint8_t mc_width = 0;
static uint8_t mc_height = 0;
static uint8_t mc_hot_x = 0;
static uint8_t mc_hot_y = 0;
/* Saved struct pointer for mouse_get_cursor. SDCC __sdcccall(1) passes
* `c` in HL, then stashes it in DE around the inline asm. Our asm
* clobbers DE (driver returns hot_y/hot_x in D/E) so the post-asm
* `c->width = ...` writes would otherwise land at a garbage address.
* We park the pointer in BSS instead so SDCC re-fetches it from
* memory after the asm. */
static mouse_cursor_t *mc_dest = 0;
void mouse_load_cursor(const mouse_cursor_t *c)
{
/* Copy fields out of the C struct into our scratch globals so the
* asm side has well-known names. */
mc_image = (uint16_t)(uintptr_t)c->image;
mc_width = c->width;
mc_height = c->height;
mc_hot_x = c->hot_x;
mc_hot_y = c->hot_y;
__asm
push ix
ld ix, (_mc_image)
ld a, (_mc_height)
ld h, a
ld a, (_mc_width)
ld l, a
ld a, (_mc_hot_y)
ld d, a
ld a, (_mc_hot_x)
ld e, a
ld b, #0
ld c, #0x09 ; LOAD MOUSE CURSOR
rst #0x30 ; MOUSE
pop ix
__endasm;
}
void mouse_get_cursor(mouse_cursor_t *c)
{
mc_dest = c; /* park ptr in BSS */
mc_image = (uint16_t)(uintptr_t)c->image;
__asm
push ix
ld ix, (_mc_image) ; IX = bitmap buffer from caller
ld c, #0x0B ; RETURN CURSOR
rst #0x30 ; mouse driver, NOT ESTEX
;; Returns: H=height, L=width, D=hot_y, E=hot_x.
ld a, h
ld (_mc_height), a
ld a, l
ld (_mc_width), a
ld a, d
ld (_mc_hot_y), a
ld a, e
ld (_mc_hot_x), a
pop ix
__endasm;
/* Re-fetch the struct pointer from BSS — `c` (kept in DE by SDCC
* around the inline asm) was clobbered by the RST 30h above. */
mouse_cursor_t *p = mc_dest;
p->width = mc_width;
p->height = mc_height;
p->hot_x = mc_hot_x;
p->hot_y = mc_hot_y;
}
/* ---- $0E / $0F SENSITIVITY ------------------------------------ */
/* GET returns H=vert, L=horz in HL. We expose the two halves as
* separate getters so the simple "uint8_t" return ABI works cleanly. */
static int ms_query(void) __naked
{
__asm
push ix
ld c, #0x0E ; GET SENSITIVITY
rst #0x30 ; MOUSE
ld d, h
ld e, l
pop ix
ret
__endasm;
}
uint8_t mouse_get_sensitivity_x(void)
{
return (uint8_t)(ms_query() & 0xFF); /* E = horz */
}
uint8_t mouse_get_sensitivity_y(void)
{
return (uint8_t)(ms_query() >> 8); /* D = vert */
}
void mouse_set_sensitivity(uint8_t horz, uint8_t vert) __naked
{
(void)horz; (void)vert;
/* Pack into HL: H=vert, L=horz. */
__asm
push ix
ld h, l
ld l, a
ld c, #0x0F ; SET SENSITIVITY
rst #0x30 ; MOUSE
pop ix
__endasm;
}
/* ---- $81 CHANGE VIDEO MODE ------------------------------------ */
/* SDCC __sdcccall(1): single uint8_t arg arrives in A. */
void mouse_video_mode_changed(uint8_t mode) __naked
{
(void)mode;
__asm
push ix
;; A already holds the mode byte (from SDCC ABI).
ld c, #0x81 ; CHANGE VIDEO MODE
rst #0x30 ; MOUSE
pop ix
ret
__endasm;
}
/* CURSOR_TEXT_MODES ($0A):
* B = 0
* H = AND symbol mask L = XOR symbol mask
* D = AND attribute mask E = XOR attribute mask
*
* SDCC __sdcccall(1) gives us:
* sym_and in L (low byte of HL arg)
* sym_xor in E (low byte of DE arg)
* attr_and at [SP+2]
* attr_xor at [SP+3]
*/
void mouse_text_cursor(uint8_t sym_and, uint8_t sym_xor,
uint8_t attr_and, uint8_t attr_xor) __naked
{
(void)sym_and; (void)sym_xor; (void)attr_and; (void)attr_xor;
__asm
;; SDCC __sdcccall(1) for 4×uint8_t args:
;; arg1 sym_and A
;; arg2 sym_xor L
;; arg3 attr_and stack low byte (packed into HL.L on caller, push HL)
;; arg4 attr_xor stack high byte (HL.H pushed by caller)
pop iy ; return address
pop bc ; C = attr_and (low), B = attr_xor (high)
push ix
;; Target: H=sym_and, L=sym_xor, D=attr_and, E=attr_xor, B=0
ld h, a ; H = sym_and (from A)
; L already holds sym_xor
ld d, c ; D = attr_and
ld e, b ; E = attr_xor
ld b, #0
ld c, #0x0A ; CURSOR TEXT MODE
rst #0x30 ; MOUSE
pop ix
jp (iy)
__endasm;
}