Files
Sprinter-SDCC/libc/gfx/gfx_core.c
T
snark13 858e5755ad ChangeLog:
- big commit.
2026-06-10 10:35:48 +03:00

142 lines
4.7 KiB
C

/*
* gfx_core.c — Sprinter graphics: common state + setup/teardown API.
*
* This module owns the variables that select which graphics page is
* visible vs drawn into, which W3 bank is mapped during gfx writes,
* and the row-base CPU address. All mode-specific primitives (256-
* color in gfx_raw_256.c / gfx_256.c, 16-color in gfx_raw_16.c /
* gfx_16.c, text in gfx_text_*.c) read this state via extern.
*
* Public API:
* gfx_init / gfx_done — switch video mode, restore previous
* gfx_set_visible_page / get — ESTEX $54 SELPAGE wrapper + cached value
* gfx_set_draw_page / get — updates _gfx_addr_base for the new page
* gfx_set_bank / get — sets the W3 page byte (0x50..0x5F)
* gfx_wait_vsync — EI; HALT until next frame interrupt
* (palette wrappers live in gfx_palette.c, backed by libc/video/palette.c)
*
* Shared state (extern from this file):
* _gfx_addr_base — 0xC000 for page 0, 0xC140 for page 1. Every
* mode-specific primitive uses this instead of a
* hard-coded 0xC000 so the same code targets the
* currently-selected draw page.
* _gfx_bank — read by _gfx_w3_video_begin in gfx_raw_common.c
* to map the right W3 page.
*/
#include <gfx.h>
#include <stdint.h>
/* From conio's videomode_raw.c — bypasses set_videotextmode's text-only
* validation so gfx_init can move INTO graphics modes. */
extern uint8_t _videomode_raw_get(void);
extern int _videomode_raw_set(uint8_t mode);
/* ---- Shared graphics state --------------------------------------- */
/* Cached values of the SELPAGE state. Page numbers are 0 or 1. */
static uint8_t _gfx_visible_page = 0;
static uint8_t _gfx_draw_page = 0;
/* The W3 page byte (0x50..0x5F) — see memory/sprinter_vram_transparency.md
* for the bit-meanings (0x50 normal, 0x54 temp, 0x58 transparent, 0x5C
* both). Read by _gfx_w3_video_begin in gfx_raw_common.c. */
uint8_t _gfx_bank = 0x50;
/* CPU address of column 0 in the current draw page. Each VRAM row is
* 1024 bytes wide — page 0 occupies bytes 0..319 (CPU 0xC000+), page 1
* occupies 320..639 (CPU 0xC140+), and the remaining 384 bytes hold
* mode descriptors / palette data we don't touch. Updated by
* gfx_set_draw_page; read by every primitive's raw helper. */
uint16_t _gfx_addr_base = 0xC000;
/* ---- gfx_init / gfx_done ----------------------------------------- */
uint8_t gfx_init(uint8_t mode, uint8_t page)
{
uint8_t prev = _videomode_raw_get();
_videomode_raw_set(mode);
_gfx_bank = 0x50;
gfx_set_visible_page(page);
gfx_set_draw_page(page);
return prev;
}
void gfx_done(uint8_t mode)
{
_videomode_raw_set(mode);
}
/* ---- Visible page (ESTEX $54 SELPAGE) ---------------------------- *
*
* Direct OUT to port 0xC9 bit 0 toggles the "screen mode page" register
* but doesn't notify DSS's display bookkeeping, leaving the screen in
* an inconsistent state (one of the swaps shows as black). Going
* through the syscall keeps DSS happy.
*/
void gfx_set_visible_page(uint8_t page)
{
_gfx_visible_page = page & 1;
__asm
push ix
ld a, (__gfx_visible_page)
ld b, a ; B = page 0/1
ld c, #0x54 ; ESTEX SELPAGE
rst #0x10
pop ix
__endasm;
}
uint8_t gfx_get_visible_page(void)
{
return _gfx_visible_page;
}
/* ---- Draw page --------------------------------------------------- */
void gfx_set_draw_page(uint8_t page)
{
_gfx_draw_page = page & 1;
/* Direct constants beat (0xC000 + (cond ? 0x140 : 0)) by 3 Z80
* instructions — SDCC doesn't fold the constant addition. */
_gfx_addr_base = _gfx_draw_page ? 0xC140 : 0xC000;
}
uint8_t gfx_get_draw_page(void)
{
return _gfx_draw_page;
}
/* ---- W3 bank (0x50..0x5F) ---------------------------------------- */
void gfx_set_bank(uint8_t bank)
{
_gfx_bank = bank;
}
uint8_t gfx_get_bank(void)
{
return _gfx_bank;
}
/* ---- Frame sync -------------------------------------------------- *
*
* Block until the next IM2 frame interrupt (50 Hz, programmed by DSS
* for keyboard / cursor maintenance). The Z80's HALT instruction
* sleeps the CPU until the next IRQ, which DSS handles and returns
* to the instruction after HALT — that's the start of the vertical
* retrace window, ideal for a tear-free page swap.
*/
void gfx_wait_vsync(void) __naked
{
__asm
ei
halt
ret
__endasm;
}
/* Palette wrappers (gfx_pal_load / gfx_pal_set / gfx_pal_get /
* gfx_pal_get_color / gfx_pal_reset) moved to gfx_palette.c — see also
* libc/video/palette.c for the shared low-level $A4 / $A6 implementation. */