/* * 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 /* 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; }