/* * 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 #include uint8_t mem_alloc_pages(uint8_t n) __naked { (void)n; __asm ;; SDCC single-uint8 arg → A on entry. push ix ld b, a ld c, #0x3D rst #0x10 pop ix jr c, _alloc_fail ret _alloc_fail: call __errno_set xor a, a ; 0 = failure ret __endasm; } 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 rst #0x10 pop ix ret __endasm; } 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 ;; A = physical page number. Return as uint8 → A. ret __endasm; } void mem_info(uint16_t *total, uint16_t *free_pages) __naked { (void)total; (void)free_pages; __asm ;; HL = total, DE = free_pages on entry. ;; ESTEX INFOMEM clobbers everything; stash both pointers on stack. push ix push hl ; [SP+0..1] = total ptr push de ; [SP+2..3] = free_pages ptr (wait wrong order) ;; Actually after two pushes: SP+0 = free_pages_ptr, SP+2 = total_ptr. ;; That's the layout we'll use below. ld c, #0x3C ; ESTEX INFOMEM → HL=total, BC=free rst #0x10 ;; HL = total value, BC = free value. pop de ; DE = free_pages ptr ld a, c ld (de), a inc de ld a, b ld (de), a pop de ; DE = total ptr ld a, l ld (de), a inc de ld a, h ld (de), a pop ix ret __endasm; }