ChangeLog:

- big commit.
This commit is contained in:
2026-06-10 10:35:48 +03:00
parent f87b52bb7f
commit 858e5755ad
20 changed files with 411 additions and 1347 deletions
+7
View File
@@ -356,6 +356,11 @@ char cputs(const char *s) __naked
or a, l
ret z
;; IX is callee-saved under SDCC's z80 ABI. We use it as a
;; function pointer below (ld ix, #__raw_putch_raw0/1) so save
;; the caller's value up front and restore before every ret.
push ix
;; KEEP_EXIST_ATTR? high byte of g_text_attr != 0
ld a, (_g_text_attr + 1)
or a, a
@@ -398,11 +403,13 @@ char cputs(const char *s) __naked
_cputs_loop_end:
call __set_cursor
pop ix ; restore caller's IX
xor a, a ; return 0
ret
_cputs_fast:
call __cputs_pchars
pop ix ; restore caller's IX
xor a, a ; return 0
ret
__endasm;
+4 -41
View File
@@ -13,7 +13,7 @@
* 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
* gfx_pal_load / gfx_pal_set — BIOS $A4 PIC_SET_PAL wrappers
* (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
@@ -136,43 +136,6 @@ void gfx_wait_vsync(void) __naked
__endasm;
}
/* ---- Palette (BIOS $A4 PIC_SET_PAL) ----------------------------- */
static uint8_t pal_num_;
static uint8_t pal_start_;
static uint8_t pal_count_;
static uint16_t pal_data_;
void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
const uint8_t *data)
{
pal_num_ = pal_num;
pal_start_ = start;
pal_count_ = count;
pal_data_ = (uint16_t)(uintptr_t)data;
__asm
push ix
ld a, (_pal_start_)
ld e, a ; E = start
ld a, (_pal_count_)
ld d, a ; D = count (0 256)
ld hl, (_pal_data_) ; HL = data
ld b, #0xFF ; mask
ld a, (_pal_num_) ; A = palette number
ld c, #0xA4 ; BIOS PIC_SET_PAL
rst #0x08
pop ix
__endasm;
}
void gfx_pal_set(uint8_t pal_num, uint8_t idx,
uint8_t r, uint8_t g, uint8_t b)
{
uint8_t entry[4];
entry[0] = b;
entry[1] = g;
entry[2] = r;
entry[3] = 0;
gfx_pal_load(pal_num, idx, 1, entry);
}
/* 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. */
+32
View File
@@ -201,4 +201,36 @@ enum {
#define COLOR_BLINK 0x80u
#define COLOR(fg, bg) ((uint8_t)((((bg) & 0x07) << 4) | ((fg) & 0x0F)))
/* Text-mode palette. The 16 logical CGA colours seen by COLOR(fg, bg)
* actually live in four 256-entry hardware palette planes indexed by the
* full 8-bit attribute byte:
*
* TEXT_PAL_PAPER — background colour, non-blink phase
* TEXT_PAL_INK — foreground colour, non-blink phase
* TEXT_PAL_BLINK_PAPER — background colour during the blink half-cycle
* TEXT_PAL_BLINK_INK — foreground colour during the blink half-cycle
*
* For non-blinking attributes (bit 7 = 0) all four planes display the
* same colours, so writing to PAPER/INK is enough. For blinking attrs
* (bit 7 = 1) the renderer alternates between the non-blink and blink
* planes — that's how flash is implemented in hardware.
*
* These wrappers add 4 to the plane index and forward to the low-level
* <palette.h> API (pal_load / pal_set_color / pal_get / pal_get_color).
* Use text_pal_reset() to restore the system default CGA palette. */
#define TEXT_PAL_PAPER 0
#define TEXT_PAL_INK 1
#define TEXT_PAL_BLINK_PAPER 2
#define TEXT_PAL_BLINK_INK 3
void text_pal_load (uint8_t plane, uint8_t start, uint8_t count,
const uint8_t *bgr0);
void text_pal_set_color(uint8_t plane, uint8_t attr,
uint8_t r, uint8_t g, uint8_t b);
void text_pal_get (uint8_t plane, uint8_t start, uint8_t count,
uint8_t *bgr0);
void text_pal_get_color(uint8_t plane, uint8_t attr,
uint8_t *r, uint8_t *g, uint8_t *b);
void text_pal_reset (void);
#endif
+11
View File
@@ -145,4 +145,15 @@ void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
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
+28 -4
View File
@@ -47,23 +47,47 @@
* n : 1..255
* ret : blk_id (1..255) on success; 0 on failure with errno set.
* The id is opaque — pass it to mem_get_page() and mem_free_block(). */
uint8_t mem_alloc_pages(uint8_t n);
uint8_t mem_alloc_pages_estex(uint8_t n);
uint8_t mem_alloc_pages_bios(uint8_t n);
/* Release a block previously returned by mem_alloc_pages().
* On error errno is set (e.g. EINVAL for unknown id). Double-free is
* NOT idempotent: the second call sets errno. */
void mem_free_block(uint8_t blk_id);
void mem_free_block_estex(uint8_t blk_id);
void mem_free_block_bios(uint8_t blk_id);
/* Translate (block, page-index) into a physical page number suitable
* for sprinter_page_w1/w2/w3() or the bank_*() helpers below.
* blk_id: from mem_alloc_pages()
* idx : 0..(n-1)
* ret : physical page (1..255) on success; 0 on failure (errno set). */
uint8_t mem_get_page(uint8_t blk_id, uint8_t idx);
uint8_t mem_get_page_bios(uint8_t blk_id, uint8_t idx);
/* Query the EMM allocator state. Both pointers must be non-NULL.
* Cannot fail (no error path). */
void mem_info(uint16_t *total, uint16_t *free_pages);
void mem_info_estex(uint16_t *total, uint16_t *free_pages);
void mem_info_bios(uint16_t *total, uint16_t *free_pages);
#define MEM_MANAGE_MODE_BIOS
#ifdef MEM_MANAGE_MODE_ESTEX
#define mem_alloc_pages mem_alloc_pages_estex
#define mem_free_block mem_free_block_estex
#define mem_info mem_info_estex
#define mem_get_page mem_get_page_bios
#elif defined MEM_MANAGE_MODE_BIOS
#define mem_alloc_pages mem_alloc_pages_bios
#define mem_free_block mem_free_block_bios
#define mem_info mem_info_bios
#define mem_get_page mem_get_page_bios
#endif
/* ===================================================================
* Far-page accessors via window 3 (base 0xC000, port 0xE2)
-137
View File
@@ -1,137 +0,0 @@
/*
* mem_alloc_pages / mem_free_block / mem_get_page / mem_info — ESTEX EMM
* wrappers for explicit 16 KB-page allocation.
*
* ESTEX $3C INFOMEM → HL=total pages, BC=free pages
* ESTEX $3D GETMEM B=npages → A=block id, CF=err
* ESTEX $3E FREEMEM A=block id → CF=err
* BIOS $C4 EMM_GETPAGE A=blk, B=idx → A=physical page CF=err
*
* Pattern: every RST 10h / RST 8 is bracketed with push/pop IX because
* ESTEX/BIOS clobber it and the C caller uses it as a frame pointer.
*/
#include <stdint.h>
#include <sprinter_mem.h>
/*
* Allocate `n` contiguous 16-KB physical pages from the EMM pool.
*
* in: n — 1..255 (number of pages requested).
* out: blk_id (1..255) on success; 0 on failure with errno set.
*
* The returned block id is opaque — pass it to mem_get_page() to obtain
* each physical-page number, and to mem_free_block() when done. Block
* ids start at 1; id 0 is reserved as the "allocation failed" sentinel.
*/
uint8_t mem_alloc_pages(uint8_t n) __naked
{
(void)n;
__asm
;; SDCC single-uint8 arg A on entry; ESTEX GETMEM wants n in B.
push ix
ld b, a
ld c, #0x3D ; ESTEX GETMEM
rst #0x10
pop ix
jr c, _alloc_fail
ret ; CF=0 A = blk_id, return as uint8 in A
_alloc_fail:
call __errno_set ; CF=1 A = ESTEX errcode
xor a, a ; return 0 = failure sentinel
ret
__endasm;
}
/*
* Release a block previously returned by mem_alloc_pages().
*
* in: blk_id (1..255).
* out: void; on ESTEX error errno is set (e.g. EINVAL for unknown id).
*
* Idempotent guarantees are NOT provided — freeing the same block twice
* sets errno on the second call. Caller is responsible for tracking
* ownership.
*/
void mem_free_block(uint8_t blk_id) __naked
{
(void)blk_id;
__asm
;; SDCC single-uint8 arg A on entry.
push ix
ld c, #0x3E ; ESTEX FREEMEM
rst #0x10
pop ix
ret nc ; CF=0 success
jp __errno_set ; CF=1 A = ESTEX errcode; tail-call helper
__endasm;
}
/*
* Translate a (block, page-index) pair into a physical 16-KB page number,
* suitable for OUT to PORT_PAGE_W1/W2/W3 or for bank_*() helpers.
*
* in: blk_id — id returned by mem_alloc_pages().
* idx — 0..(n-1), where n was the count passed to alloc.
* out: physical page number (1..255) on success;
* 0 on failure with errno set (invalid block or idx out of range).
*/
uint8_t mem_get_page(uint8_t blk_id, uint8_t idx) __naked
{
(void)blk_id; (void)idx;
__asm
;; 2-arg uint8/uint8: blk_id A, idx L.
push ix
ld b, l ; BIOS wants idx in B
;; A still has blk_id
ld c, #0xC4 ; BIOS EMM_GETPAGE
rst #0x08
pop ix
ret nc ; CF=0 A = phys page (return value)
;; CF=1 A = errcode; set errno, return 0 as sentinel.
call __errno_set
xor a, a
ret
__endasm;
}
/*
* Query the EMM allocator about its current state.
*
* *total ← number of 16-KB physical pages installed in the system
* *free_pages ← number currently available for allocation
*
* Both pointers must be non-NULL writeable uint16_t locations.
* No error path: ESTEX INFOMEM always succeeds.
*/
void mem_info(uint16_t *total, uint16_t *free_pages) __naked
{
(void)total; (void)free_pages;
__asm
;; HL = total ptr, DE = free_pages ptr on entry.
;; RST 10 clobbers both stash on the stack across the call.
push ix
push hl ; later [SP+2] = total_ptr
push de ; TOS [SP+0] = free_pages_ptr
ld c, #0x3C ; ESTEX INFOMEM HL = total, BC = free
rst #0x10
pop de ; DE = free_pages_ptr
ld a, c
ld (de), a
inc de
ld a, b
ld (de), a ; *free_pages = BC
pop de ; DE = total_ptr
ld a, l
ld (de), a
inc de
ld a, h
ld (de), a ; *total = HL
pop ix
ret
__endasm;
}