858e5755ad
- big commit.
142 lines
4.7 KiB
C
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. */
|