/* * gfx.h — Sprinter graphics primitives. * * Two main modes: * GFX_MODE_320x256x256 (0x81) — one byte per pixel, palette of 256 * entries. API functions suffixed _256. * GFX_MODE_640x256x16 (0x82) — 4 bits per pixel, palette of 16. * API functions suffixed _16. * * Common API (no suffix) covers things that are mode-agnostic: * gfx_init / gfx_done * gfx_set_visible_page / gfx_set_draw_page / gfx_set_bank * gfx_wait_vsync * gfx_pal_load / gfx_pal_set * gfx_load_default_font / gfx_set_font * * Addressing reminder: * pixel (x, y) lives at CPU 0xC000 + (x or x/2) with Port_Y (0x89) = y; * the gfx code maps a 16 KB VRAM page into W3 around every write. * For double-buffering, page 1 starts 320 bytes later (0xC140). * * Palette: BIOS $A4 (RST 8); 4 bytes per entry — B, G, R, pad. */ #ifndef GFX_H #define GFX_H #include /* ESTEX SETVMOD codes — same values as the SETVMOD `A` register. */ #define GFX_MODE_TEXT_40x32 0x02 #define GFX_MODE_TEXT_80x32 0x03 #define GFX_MODE_320x256x256 0x81 #define GFX_MODE_640x256x16 0x82 /* Pixel dimensions of mode 0x81 (320×256, 256 colours). */ #define GFX_WIDTH 320 #define GFX_HEIGHT 256 /* Pixel dimensions of mode 0x82 (640×256, 16 colours). Each byte at * 0xC000+x_byte holds two pixels: high nibble = LEFT (even-x), low * nibble = RIGHT (odd-x) — see memory/sprinter_graphics.md. */ #define GFX_WIDTH_16 640 #define GFX_HEIGHT_16 256 #define GFX_COLORS_16 16 /* ---- Setup / teardown -------------------------------------------- * * * Switch to `mode`, returning the previous mode for restore. `page` * (0 or 1) selects the initial graphics screen — both the visible and * the draw page are set to it, and the W3 bank is reset to 0x50 (the * canonical visible video page). Programs that don't double-buffer * just pass 0. */ uint8_t gfx_init(uint8_t mode, uint8_t page); /* Restore a previously-saved video mode. */ void gfx_done(uint8_t mode); /* ---- Page selection (double-buffering) and bank control ---------- * * * The Sprinter hardware holds two graphics screens. The VISIBLE page * is what's shown on screen; the DRAW page is where the gfx_* writes * land. Render the next frame into the hidden page and flip when ready. * * "Bank" is the W3 page byte (0x50..0x5F) — bits 2,3 select * normal/temp/transparent display modes. All gfx_* primitives map * THIS bank into W3 before touching 0xC000+. Default is 0x50. */ void gfx_set_visible_page(uint8_t page); /* 0 or 1 */ uint8_t gfx_get_visible_page(void); void gfx_set_draw_page(uint8_t page); /* 0 or 1 */ uint8_t gfx_get_draw_page(void); void gfx_set_bank(uint8_t bank); /* 0x50..0x5F */ uint8_t gfx_get_bank(void); /* Block until the next frame interrupt (50 Hz on Sprinter). Uses * `EI; HALT` — the Z80 sleeps until the next IM2 tick that DSS programs * for keyboard / cursor handling. Typical use: * * gfx_set_draw_page(hidden); * draw_frame(...); * gfx_wait_vsync(); // wait for vretrace * gfx_set_visible_page(hidden); // tear-free flip */ void gfx_wait_vsync(void); /* ---- 320×256×256 (mode 0x81) drawing API -------------------------- * * Colour args are palette indices 0..255. */ void gfx_clear256 (uint8_t color); void gfx_putpixel256 (int x, int y, uint8_t color); void gfx_hline256 (int x, int y, int len, uint8_t color); void gfx_vline256 (int x, int y, int len, uint8_t color); void gfx_line256 (int x0, int y0, int x1, int y1, uint8_t color); void gfx_rect256 (int x, int y, int w, int h, uint8_t color); void gfx_fill_rect256(int x, int y, int w, int h, uint8_t color); /* ---- 640×256×16 (mode 0x82) drawing API --------------------------- * * Colour args are palette indices 0..15. The accelerator works * byte-wise so vline16 falls back to per-row RMW (a byte spans two * horizontal pixels). */ void gfx_clear16 (uint8_t color); void gfx_putpixel16 (int x, int y, uint8_t color); void gfx_hline16 (int x, int y, int len, uint8_t color); void gfx_vline16 (int x, int y, int len, uint8_t color); void gfx_line16 (int x0, int y0, int x1, int y1, uint8_t color); void gfx_rect16 (int x, int y, int w, int h, uint8_t color); void gfx_fill_rect16(int x, int y, int w, int h, uint8_t color); /* ---- Bitmap-font text -------------------------------------------- * * Font is 256 glyphs × 8 rows × 1 byte (ZX-Spectrum format), 2 KB. * On first use the default system font is fetched via BIOS WIN_GET_ZG * (fn 0xB8). gfx_set_font() lets you swap in a custom font (the * pointer is held — keep the storage alive). */ void gfx_load_default_font(void); void gfx_set_font(const uint8_t *font); /* 320×256×256 text: one byte per pixel; advances x by 8 per char. */ void gfx_putchar256(int x, int y, char c, uint8_t fg, uint8_t bg); void gfx_text256 (int x, int y, const char *s, uint8_t fg, uint8_t bg); /* 640×256×16 text: 4 bits per pixel; x must be EVEN (byte-aligned). */ void gfx_putchar16 (int x, int y, char c, uint8_t fg, uint8_t bg); void gfx_text16 (int x, int y, const char *s, uint8_t fg, uint8_t bg); /* ---- Palette ----------------------------------------------------- * * Each graphics page has its own palette page (page 0 → palette 0, * page 1 → palette 1). For seamless double-buffering, load the same * palette into both. */ /* Load a contiguous block of palette entries. * pal_num: 0..3 (graphics palettes) * start: first colour slot (0..255) * count: number of slots (0 → 256) * data: pointer to count entries, each formatted (B, G, R, 0). */ void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count, const uint8_t *data); /* Convenience: set one palette entry from RGB. Internally builds the * BGR+pad triple and calls gfx_pal_load(pal_num, idx, 1, ...). */ void gfx_pal_set (uint8_t pal_num, uint8_t idx, uint8_t r, uint8_t g, uint8_t b); /* Read a contiguous block of entries back from a graphics palette. */ void gfx_pal_get (uint8_t pal_num, uint8_t start, uint8_t count, uint8_t *data); /* Read one entry into R, G, B pointers (any may be NULL). */ void gfx_pal_get_color(uint8_t pal_num, uint8_t idx, uint8_t *r, uint8_t *g, uint8_t *b); /* Restore the system default graphics palette (BIOS $A6, type=1). */ void gfx_pal_reset(void); #endif