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:
2026-07-06 16:08:58 +03:00
parent 46553f4e07
commit 4a081501d8
237 changed files with 4830 additions and 3933 deletions
-52
View File
@@ -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);
}
-52
View File
@@ -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);
}
+23
View File
@@ -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;
}
+22
View File
@@ -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;
}
+18
View File
@@ -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);
}
+18
View File
@@ -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);
}
+17
View File
@@ -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);
}
+17
View File
@@ -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);
}
+18
View File
@@ -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);
}
+18
View File
@@ -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);
}
+34
View File
@@ -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;
}
+31
View File
@@ -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;
}
-138
View File
@@ -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;
}
-121
View File
@@ -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;
}
+24
View File
@@ -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;
}
+22
View File
@@ -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;
}
+31
View File
@@ -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;
}
+45
View File
@@ -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;
}
+44
View File
@@ -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;
}