Files
Sprinter-SDCC/libc/mem/mem_alloc.c
T
snark13 527d4a6a18 libc review: mem/, stdio/, fixes in sprinter-cc and FILE shim
libc/mem/:
  • Split bank_io.c into bank_io_w3.c (existing W3 helpers, base 0xC000,
    port 0xE2) and bank_io_w1.c (new mirror through W1, base 0x4000,
    port 0xA2).  Two .rel files so DCE picks only the needed group:
    a W3-only user pulls ~70 bytes instead of all 134.  W1 variants are
    `--memory tiny`-only (any other mode runs code from W1 or uses W1
    for the banked-code segment, swapping it crashes).
  • mem_alloc.c: add CF=err checks for mem_free_block and mem_get_page
    (were silently ignored), per-function docstrings on alloc/free/
    get_page/info, drop the confused "wait wrong order" comment in
    mem_info.  Header sprinter_mem.h gets matching per-function doc.

libc/stdio/:
  • Add hex_print.c (hex8/hex16/hex32, ~26 bytes) and dec_print.c
    (dec8/dec16/dec32, ~170 bytes) ported from solid-c STDLIB.ASM.
    Replaces the printf("%u"/"%X") wrappers in solid_helpers.c that
    dragged in the 3-5 KB printf machinery.
    - hex* use the classic cp 10 / sbc 0x69 / daa nibble→ASCII trick;
      hex8 self-calls for the high nibble, hex16/hex32 tail-call hex8.
    - dec32 is the master routine; dec8/dec16 jump into shared entry
      points (__dec_entry3 / __dec_entry5).  32-bit subtract-power-of-10
      keeps the high 16 bits in HL alt (shadow set).
    - DISCOVERY: ESTEX PUTCHAR ($5B) on our Sprinter build preserves
      the main register set + IX but CLOBBERS the shadow set
      (BC'/DE'/HL').  solid-c's original code assumed otherwise and
      garbled output for values ≥ 6 digits.  Fix: save/restore HL alt
      around the RST 10 in _dec_emit_or_skip.  Documented in
      memory/estex_putchar_abi.md.
  • file.c: drop stdaux/stdprn (no Sprinter printer API), change
    stdin/stdout/stderr fd markers to 0/-1/-2 (positive fds clash with
    ESTEX OPEN return values), add TODO header pointing at v2 buffered
    FILE rewrite (see docs/TODO.md for the Solid-C reference struct).

bin/sprinter-cc:
  • --memory big and --memory huge now always use crt0_banked.s (was:
    only with --bank flags), matching docs/memory_modes_implemented.md.
    When the user has no --bank flags, generate a tiny stub with
    `const uint8_t n_banks = 0;` and assemble bank.s for _bank_pages.
    Without this fix, openenv with --memory big could not see the
    estex_file_handle symbol exported by crt0_banked.

examples/openenv:
  • Add usage of estex_file_handle to confirm the crt0_banked startup-
    info is reachable.  Local extern decl — keeps the symbol out of
    sprinter.h since it only exists in big/huge builds.

examples/dec_test:
  • New regression test covering hex8/16/32 and dec8/16/32 across the
    interesting boundary values.

.gitignore: add .kilo/ (editor session cache).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 09:26:10 +03:00

138 lines
4.4 KiB
C

/*
* 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;
}