libc: сплит «1 функция = 1 модуль» — вся библиотека, wildcard-сборка
- bios/conio/env/errno/gfx/io/mem/mouse/stdio/stdlib/string/sys/time/ video разложены по модулям: общие статики и helpers — в internal _-модулях (_conio.h/_mouse.h/_gfx.h/_palette.h/_atexit.h/_time.h) - lib/Makefile: LIBC_C = wildcard libc/*/*.c — гранулярность файлов = гранулярность DCE линкера - эффект _CODE: gfx_text 6986→2568 Б, gfx_mous −1745, gfx_demo/d16 −542; ранее timedir −3270, ls −3098, stattest −2995 - комментарии оставшихся модулей переведены на русский; puts: убран мёртвый pchars; videomode_raw разложен на get/set - docs/libc-split-asm-cases.md — правила asm-связок между модулями; docs/libc-roadmap.md — план этапа - восстановлен examples/mdview/SAMPLE.MD (нужен make floppy) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* bank_io_w1.c — far-page accessors that swap window 1 (port 0xA2,
|
||||
* base 0x4000). Mirror of bank_io.c but through W1.
|
||||
*
|
||||
* SAFE ONLY in `--memory tiny` builds. In tiny mode all code and data
|
||||
* fit in W2, so both W1 and W3 are free for arbitrary banked data.
|
||||
*
|
||||
* small / huge: code lives in W1 (HOME) → swapping W1 unmaps the
|
||||
* function mid-call and crashes.
|
||||
* big : banked code segment lives in W1 → swap clobbers it.
|
||||
*
|
||||
* Split into its own .rel so that programs using only the W3 variants
|
||||
* do NOT pull these functions in (and vice versa).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
uint8_t bank_load_byte_w1(uint8_t phys_page, uint16_t off_in_window)
|
||||
{
|
||||
uint8_t saved = _io_page_w1;
|
||||
sprinter_page_w1(phys_page);
|
||||
uint8_t v = *((volatile uint8_t *)(0x4000u + off_in_window));
|
||||
sprinter_page_w1(saved);
|
||||
return v;
|
||||
}
|
||||
|
||||
void bank_store_byte_w1(uint8_t phys_page, uint16_t off_in_window, uint8_t v)
|
||||
{
|
||||
uint8_t saved = _io_page_w1;
|
||||
sprinter_page_w1(phys_page);
|
||||
*((volatile uint8_t *)(0x4000u + off_in_window)) = v;
|
||||
sprinter_page_w1(saved);
|
||||
}
|
||||
|
||||
void bank_read_w1(uint8_t phys_page, uint16_t off, void *dst, uint16_t n)
|
||||
{
|
||||
uint8_t saved = _io_page_w1;
|
||||
sprinter_page_w1(phys_page);
|
||||
memcpy(dst, (const void *)(0x4000u + off), n);
|
||||
sprinter_page_w1(saved);
|
||||
}
|
||||
|
||||
void bank_write_w1(uint8_t phys_page, uint16_t off, const void *src, uint16_t n)
|
||||
{
|
||||
uint8_t saved = _io_page_w1;
|
||||
sprinter_page_w1(phys_page);
|
||||
memcpy((void *)(0x4000u + off), src, n);
|
||||
sprinter_page_w1(saved);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* bank_io_w3.c — far-page accessors that swap window 3 (port 0xE2,
|
||||
* base 0xC000). Save the current W3 page, set the target, do the
|
||||
* byte/memcpy access, restore the saved page.
|
||||
*
|
||||
* Safe in `tiny`, `small`, `big` memory modes (W3 is free for data).
|
||||
* NOT safe in `huge` — there banked code lives in W3.
|
||||
*
|
||||
* No DI/EI around the swap: ISRs that don't touch W3 are unaffected;
|
||||
* concurrent W3 use from an ISR is unsafe.
|
||||
*
|
||||
* The `_w1` family (bank_io_w1.c) is the mirror through W1 and is
|
||||
* `tiny`-only. See sprinter_mem.h for the full safety matrix.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
uint8_t bank_load_byte(uint8_t phys_page, uint16_t off_in_window)
|
||||
{
|
||||
uint8_t saved = _io_page_w3;
|
||||
sprinter_page_w3(phys_page);
|
||||
uint8_t v = *((volatile uint8_t *)(0xC000u + off_in_window));
|
||||
sprinter_page_w3(saved);
|
||||
return v;
|
||||
}
|
||||
|
||||
void bank_store_byte(uint8_t phys_page, uint16_t off_in_window, uint8_t v)
|
||||
{
|
||||
uint8_t saved = _io_page_w3;
|
||||
sprinter_page_w3(phys_page);
|
||||
*((volatile uint8_t *)(0xC000u + off_in_window)) = v;
|
||||
sprinter_page_w3(saved);
|
||||
}
|
||||
|
||||
void bank_read(uint8_t phys_page, uint16_t off, void *dst, uint16_t n)
|
||||
{
|
||||
uint8_t saved = _io_page_w3;
|
||||
sprinter_page_w3(phys_page);
|
||||
memcpy(dst, (const void *)(0xC000u + off), n);
|
||||
sprinter_page_w3(saved);
|
||||
}
|
||||
|
||||
void bank_write(uint8_t phys_page, uint16_t off, const void *src, uint16_t n)
|
||||
{
|
||||
uint8_t saved = _io_page_w3;
|
||||
sprinter_page_w3(phys_page);
|
||||
memcpy((void *)(0xC000u + off), src, n);
|
||||
sprinter_page_w3(saved);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* bank_load_byte — прочитать байт из чужой физической страницы через
|
||||
* окно 3 (порт 0xE2, база 0xC000): сохранить текущую страницу W3,
|
||||
* подставить целевую, прочитать, вернуть страницу назад.
|
||||
*
|
||||
* Безопасно в режимах tiny/small/big (W3 свободно под данные),
|
||||
* НЕ безопасно в huge (там в W3 живёт banked-код). DI/EI вокруг свопа
|
||||
* нет — конкурентное использование W3 из ISR небезопасно.
|
||||
* Семейство *_w1 (через окно 1) — только для tiny; см. sprinter_mem.h.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
uint8_t bank_load_byte(uint8_t phys_page, uint16_t off_in_window)
|
||||
{
|
||||
uint8_t saved = _io_page_w3;
|
||||
sprinter_page_w3(phys_page);
|
||||
uint8_t v = *((volatile uint8_t *)(0xC000u + off_in_window));
|
||||
sprinter_page_w3(saved);
|
||||
return v;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* bank_load_byte_w1 — прочитать байт из чужой физической страницы через
|
||||
* окно 1 (порт 0xA2, база 0x4000) со свопом-возвратом страницы W1.
|
||||
*
|
||||
* ТОЛЬКО для `--memory tiny`: там весь код/данные в W2, и W1 свободно.
|
||||
* small/huge: в W1 живёт код (HOME) → своп размапит функцию на лету;
|
||||
* big: в W1 живёт banked-код → своп его затирает.
|
||||
* Зеркало W3-семейства; см. sprinter_mem.h (матрица безопасности).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
uint8_t bank_load_byte_w1(uint8_t phys_page, uint16_t off_in_window)
|
||||
{
|
||||
uint8_t saved = _io_page_w1;
|
||||
sprinter_page_w1(phys_page);
|
||||
uint8_t v = *((volatile uint8_t *)(0x4000u + off_in_window));
|
||||
sprinter_page_w1(saved);
|
||||
return v;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* bank_read — скопировать n байт из чужой физической страницы (окно 3,
|
||||
* порт 0xE2, база 0xC000) в dst через memcpy со свопом-возвратом W3.
|
||||
* Безопасность по режимам — как у bank_load_byte (см. sprinter_mem.h).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void bank_read(uint8_t phys_page, uint16_t off, void *dst, uint16_t n)
|
||||
{
|
||||
uint8_t saved = _io_page_w3;
|
||||
sprinter_page_w3(phys_page);
|
||||
memcpy(dst, (const void *)(0xC000u + off), n);
|
||||
sprinter_page_w3(saved);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* bank_read_w1 — скопировать n байт из чужой физической страницы
|
||||
* (окно 1, порт 0xA2, база 0x4000) в dst со свопом-возвратом W1.
|
||||
* ТОЛЬКО для `--memory tiny` — см. bank_load_byte_w1.c.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void bank_read_w1(uint8_t phys_page, uint16_t off, void *dst, uint16_t n)
|
||||
{
|
||||
uint8_t saved = _io_page_w1;
|
||||
sprinter_page_w1(phys_page);
|
||||
memcpy(dst, (const void *)(0x4000u + off), n);
|
||||
sprinter_page_w1(saved);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* bank_store_byte — записать байт в чужую физическую страницу через
|
||||
* окно 3 (порт 0xE2, база 0xC000) со свопом-возвратом страницы W3.
|
||||
* Безопасность по режимам — как у bank_load_byte (см. sprinter_mem.h).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void bank_store_byte(uint8_t phys_page, uint16_t off_in_window, uint8_t v)
|
||||
{
|
||||
uint8_t saved = _io_page_w3;
|
||||
sprinter_page_w3(phys_page);
|
||||
*((volatile uint8_t *)(0xC000u + off_in_window)) = v;
|
||||
sprinter_page_w3(saved);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* bank_store_byte_w1 — записать байт в чужую физическую страницу через
|
||||
* окно 1 (порт 0xA2, база 0x4000) со свопом-возвратом W1.
|
||||
* ТОЛЬКО для `--memory tiny` — см. bank_load_byte_w1.c.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void bank_store_byte_w1(uint8_t phys_page, uint16_t off_in_window, uint8_t v)
|
||||
{
|
||||
uint8_t saved = _io_page_w1;
|
||||
sprinter_page_w1(phys_page);
|
||||
*((volatile uint8_t *)(0x4000u + off_in_window)) = v;
|
||||
sprinter_page_w1(saved);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* bank_write — скопировать n байт из src в чужую физическую страницу
|
||||
* (окно 3, порт 0xE2, база 0xC000) через memcpy со свопом-возвратом W3.
|
||||
* Безопасность по режимам — как у bank_load_byte (см. sprinter_mem.h).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void bank_write(uint8_t phys_page, uint16_t off, const void *src, uint16_t n)
|
||||
{
|
||||
uint8_t saved = _io_page_w3;
|
||||
sprinter_page_w3(phys_page);
|
||||
memcpy((void *)(0xC000u + off), src, n);
|
||||
sprinter_page_w3(saved);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* bank_write_w1 — скопировать n байт из src в чужую физическую страницу
|
||||
* (окно 1, порт 0xA2, база 0x4000) со свопом-возвратом W1.
|
||||
* ТОЛЬКО для `--memory tiny` — см. bank_load_byte_w1.c.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void bank_write_w1(uint8_t phys_page, uint16_t off, const void *src, uint16_t n)
|
||||
{
|
||||
uint8_t saved = _io_page_w1;
|
||||
sprinter_page_w1(phys_page);
|
||||
memcpy((void *)(0x4000u + off), src, n);
|
||||
sprinter_page_w1(saved);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* mem_alloc_pages_bios — выделить n смежных 16-КБ физических страниц
|
||||
* из EMM-пула через BIOS EMM_FN2 ($C2, rst 8): B=npages → A=blk_id,
|
||||
* CF=err.
|
||||
*
|
||||
* in: n — 1..255 (сколько страниц).
|
||||
* out: blk_id (1..255) при успехе; 0 при ошибке (errno установлен).
|
||||
*
|
||||
* blk_id непрозрачен — отдай его mem_get_page_bios() за номерами
|
||||
* физических страниц и mem_free_block_bios() по окончании. Id
|
||||
* начинаются с 1; 0 зарезервирован как сигнал «не выделилось».
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
uint8_t mem_alloc_pages_bios(uint8_t n) __naked
|
||||
{
|
||||
(void)n;
|
||||
__asm
|
||||
;; SDCC: единственный uint8-аргумент в A; BIOS хочет n в B.
|
||||
push ix
|
||||
ld b, a
|
||||
ld c, #0xC2 ; BIOS EMM_FN2 (ALLOC)
|
||||
rst #0x08
|
||||
pop ix
|
||||
jr c, _alloc_fail
|
||||
ret ; CF=0 → A = blk_id (возврат uint8 в A)
|
||||
_alloc_fail:
|
||||
call __errno_set ; CF=1 → A = код ошибки
|
||||
xor a, a ; вернуть 0 = сигнал ошибки
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* mem_alloc_pages_estex — выделить n смежных 16-КБ физических страниц
|
||||
* из EMM-пула через ESTEX GETMEM ($3D): B=npages → A=blk_id, CF=err.
|
||||
*
|
||||
* in: n — 1..255; out: blk_id (1..255) или 0 при ошибке (errno).
|
||||
*
|
||||
* blk_id непрозрачен — mem_get_page_bios() даст номера страниц,
|
||||
* mem_free_block_estex() освободит. Id с 1; 0 = «не выделилось».
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
uint8_t mem_alloc_pages_estex(uint8_t n) __naked
|
||||
{
|
||||
(void)n;
|
||||
__asm
|
||||
;; SDCC: единственный uint8-аргумент в A; ESTEX хочет n в 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 (возврат uint8 в A)
|
||||
_alloc_fail:
|
||||
call __errno_set ; CF=1 → A = код ошибки
|
||||
xor a, a ; вернуть 0 = сигнал ошибки
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* mem_alloc_pages / mem_free_block / mem_get_page / mem_info — ESTEX EMM
|
||||
* wrappers for explicit 16 KB-page allocation.
|
||||
*
|
||||
* BIOS $C0 EMM_FN0 (MEMINFO) → HL=total pages, BC=free pages
|
||||
* BIOS $C2 EMM_FN2 (ALLOC) B=npages → A=block id, CF=err
|
||||
* BIOS $C3 EMM_FN3 (FREE) A=block id → CF=err
|
||||
* BIOS $C4 EMM_FN4 (PAGEID) A=blk, B=idx → A=physical page CF=err
|
||||
* BIOS $C6 EMM_FN6 (IOTEST) 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_bios(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, #0xC2 ; BIOS EMM_FN2
|
||||
rst #0x08
|
||||
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_bios(uint8_t blk_id) __naked
|
||||
{
|
||||
(void)blk_id;
|
||||
__asm
|
||||
;; SDCC single-uint8 arg → A on entry.
|
||||
push ix
|
||||
ld c, #0xC3 ; ESTEX FREEMEM
|
||||
rst #0x08
|
||||
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_bios(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_bios(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, #0xC0 ; BIOS INFOMEM → HL = total, BC = free
|
||||
rst #0x08
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,121 +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
|
||||
*
|
||||
* 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_estex(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_estex(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).
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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_estex(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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* mem_free_block_bios — освободить блок, выданный mem_alloc_pages_bios.
|
||||
* BIOS EMM_FN3 ($C3, rst 8): A=blk_id → CF=err.
|
||||
*
|
||||
* Идемпотентности НЕТ: повторное освобождение того же блока ставит
|
||||
* errno. Владение отслеживает вызывающий.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void mem_free_block_bios(uint8_t blk_id) __naked
|
||||
{
|
||||
(void)blk_id;
|
||||
__asm
|
||||
;; SDCC: единственный uint8-аргумент уже в A.
|
||||
push ix
|
||||
ld c, #0xC3 ; BIOS EMM_FN3 (FREE)
|
||||
rst #0x08
|
||||
pop ix
|
||||
ret nc ; CF=0 → успех
|
||||
jp __errno_set ; CF=1 → A = код; tail-call хелпера
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* mem_free_block_estex — освободить блок, выданный
|
||||
* mem_alloc_pages_estex. ESTEX FREEMEM ($3E): A=blk_id → CF=err.
|
||||
* Повторное освобождение ставит errno; владение — на вызывающем.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void mem_free_block_estex(uint8_t blk_id) __naked
|
||||
{
|
||||
(void)blk_id;
|
||||
__asm
|
||||
;; SDCC: единственный uint8-аргумент уже в A.
|
||||
push ix
|
||||
ld c, #0x3E ; ESTEX FREEMEM
|
||||
rst #0x10
|
||||
pop ix
|
||||
ret nc ; CF=0 → успех
|
||||
jp __errno_set ; CF=1 → A = код; tail-call хелпера
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* mem_get_page_bios — перевести пару (блок, индекс страницы) в номер
|
||||
* физической 16-КБ страницы (годен для OUT в PORT_PAGE_W1/W2/W3 или
|
||||
* bank_*-хелперов). BIOS EMM_FN4 ($C4, rst 8): A=blk, B=idx → A=page,
|
||||
* CF=err.
|
||||
*
|
||||
* in: blk_id — id от mem_alloc_pages_*; idx — 0..(n-1).
|
||||
* out: номер физической страницы (1..255); 0 при ошибке (errno).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
uint8_t mem_get_page_bios(uint8_t blk_id, uint8_t idx) __naked
|
||||
{
|
||||
(void)blk_id; (void)idx;
|
||||
__asm
|
||||
;; 2 аргумента uint8/uint8: blk_id → A, idx → L.
|
||||
push ix
|
||||
ld b, l ; BIOS хочет idx в B
|
||||
;; A всё ещё держит blk_id
|
||||
ld c, #0xC4 ; BIOS EMM_GETPAGE
|
||||
rst #0x08
|
||||
pop ix
|
||||
ret nc ; CF=0 → A = физ. страница (возврат)
|
||||
;; CF=1 → A = код; ставим errno, возвращаем 0 как сигнал.
|
||||
call __errno_set
|
||||
xor a, a
|
||||
ret
|
||||
__endasm;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* mem_info_bios — состояние EMM-аллокатора через BIOS INFOMEM ($C0,
|
||||
* rst 8): HL=всего страниц, BC=свободно.
|
||||
*
|
||||
* *total ← всего 16-КБ физических страниц в системе
|
||||
* *free_pages ← сколько сейчас доступно
|
||||
*
|
||||
* Оба указателя должны быть валидными uint16_t. Ошибок нет — INFOMEM
|
||||
* всегда успешен.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void mem_info_bios(uint16_t *total, uint16_t *free_pages) __naked
|
||||
{
|
||||
(void)total; (void)free_pages;
|
||||
__asm
|
||||
;; HL = total ptr, DE = free_pages ptr на входе.
|
||||
;; RST портит оба — прячем на стек через вызов.
|
||||
push ix
|
||||
push hl ; потом [SP+2] = total_ptr
|
||||
push de ; TOS [SP+0] = free_pages_ptr
|
||||
|
||||
ld c, #0xC0 ; BIOS INFOMEM → HL = total, BC = free
|
||||
rst #0x08
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* mem_info_estex — состояние EMM-аллокатора через ESTEX INFOMEM ($3C):
|
||||
* HL=всего страниц, BC=свободно.
|
||||
*
|
||||
* *total ← всего 16-КБ физических страниц в системе
|
||||
* *free_pages ← сколько сейчас доступно
|
||||
*
|
||||
* Оба указателя должны быть валидными uint16_t. Ошибок нет.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sprinter_mem.h>
|
||||
|
||||
void mem_info_estex(uint16_t *total, uint16_t *free_pages) __naked
|
||||
{
|
||||
(void)total; (void)free_pages;
|
||||
__asm
|
||||
;; HL = total ptr, DE = free_pages ptr на входе.
|
||||
;; RST 10 портит оба — прячем на стек через вызов.
|
||||
push ix
|
||||
push hl ; потом [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;
|
||||
}
|
||||
Reference in New Issue
Block a user