;============================================================================== ; inflate_call.s — SDCC обёртка (sdcccall(1)) для вызова inflate из C ; ; uint8_t inflate_vgz(uint8_t src_page, uint8_t dst_page); ; void copy_pages_to_tap(uint8_t n_pages); ; ; sdcccall(1): A = первый аргумент ; Возврат inflate_vgz: A = 0 (успех) / 1 (ошибка) ; ; Inflate binary живёт в экстра-странице WMF. Обёртка маппит её в Win 3 ; (ORG 0xC000) и делает JP 0xC000. Decode buffer — в Win 2 (0x8000), ; настраивается самим inflate. При выходе inflate восстанавливает Win 2. ; ; ╔══════════════════════════════════════════════════════════════════════╗ ; ║ Почему код inflate обязан быть в Win 3, а не Win 2: ║ ; ║ DEFLATE backref вычисляет HL = write_ptr − distance. ║ ; ║ При decode buffer в Win 2 (0x8000) → HL ∈ [0x0000..0xBFFF], ║ ; ║ никогда не попадает в Win 3 (0xC000+), где живёт код. ║ ; ║ Если поменять местами (код Win 2, decode Win 3) → HL может ║ ; ║ попасть в 0x8000–0xBFFF, что потребует ремапа Win 2 во время ║ ; ║ исполнения из неё — невозможно. ║ ; ╚══════════════════════════════════════════════════════════════════════╝ ; ; copy_pages_to_tap: ; Копирует N страниц из мегабуфера (#20+) в TAP-страницы (#A1+). ; DI на время копирования, восстанавливает Win 0/Win 3 из sys vars. ; ; inflate_vgz: ; 1. Сохранить аргументы, IX, SP, Win 1 ; 2. DI, маппить Win 3 = inflate page через порт ; 3. SP = 0xFFF0, push return addr → JP 0xC000 ; 4. По возврату: SP, Win 1 восстанавливаем, EI, IX → ret ; Win 0 / Win 3 — восстанавливает вызывающий C-код через WC API ;============================================================================== .module inflate_call .globl _inflate_vgz .globl _copy_pages_to_tap ; Progress bar params (filled by C before inflate_vgz) .globl _ifl_pb_text_pg .globl _ifl_pb_attr_ofs .globl _ifl_pb_total .globl _ifl_pb_bw .globl _ifl_pb_green ;--- Константы --------------------------------------------------------------- INFLATE_PAGE_IDX = 4 ; индекс inflate в плагине (1 code + 3 LUT) ; физ. страница = base_page + INFLATE_PAGE_IDX ; base_page читается из WC sys var (0x6002) PORT_W0 = 0x10AF ; TSConfig Win 0 page port PORT_W1 = 0x11AF ; TSConfig Win 1 page port PORT_W3 = 0x13AF ; TSConfig Win 3 page port ;--- DATA (0xB800+) ---------------------------------------------------------- .area _DATA _ifl_src_pg: .ds 1 ; src_page (физ. стр. TAP) _ifl_dst_pg: .ds 1 ; dst_page (физ. стр. мегабуфера) _ifl_sv_pg1: .ds 1 ; saved Win 1 physical page _ifl_sv_pg2: .ds 1 ; saved Win 2 physical page → inflate D reg _ifl_sv_sp: .ds 2 ; saved SP _ifl_inflate_pg: .ds 1 ; computed inflate phys page (debug/scan) ; Progress bar parameters (set by C, copied to inflate page at 0xC1F0) _ifl_pb_text_pg: .ds 1 ; text screen physical page _ifl_pb_attr_ofs: .ds 2 ; attr base offset within text page (lo, hi) _ifl_pb_total: .ds 1 ; estimated total output pages (0=disabled) _ifl_pb_bw: .ds 1 ; bar width in columns _ifl_pb_green: .ds 1 ; green attr value ;--- CODE (0x8000+) ---------------------------------------------------------- .area _CODE _inflate_vgz:: push ix ; callee-saved (SDCC) ;--- 1. сохранить аргументы --- ld (_ifl_src_pg), a ; A = src_page ld a, l ld (_ifl_dst_pg), a ; L = dst_page ;--- 2. DI --- di ;--- 3. сохранить Win 1, Win 2 из WC sys vars (@ 0x6001/0x6002) --- ld a, (0x6001) ld (_ifl_sv_pg1), a ld a, (0x6002) ld (_ifl_sv_pg2), a ;--- 4. сохранить SP --- ld (_ifl_sv_sp), sp .ifdef DBG_ENABLE ;--- DBG: 0x01 = saved context, about to map inflate page --- ld bc, #0xFFAF ld a, #0x01 out (c), a .endif ;--- 5. маппить Win 3 = inflate page (dynamic: base + IDX) --- ld a, (0x6002) ; Win 2 phys page = plugin base .ifdef DBG_ENABLE ld bc, #0xFEAF out (c), a ; DBG: FEAF = base page from 0x6002 .endif add a, #INFLATE_PAGE_IDX ; + 5 = inflate page ld (_ifl_inflate_pg), a ; save computed inflate page .ifdef DBG_ENABLE ld bc, #0xFDAF out (c), a ; DBG: FDAF = computed inflate page .endif ld bc, #PORT_W3 out (c), a .ifdef DBG_ENABLE ;--- DBG: 0x02 = Win3 mapped to inflate page --- ld bc, #0xFFAF ld a, #0x02 out (c), a ld a, (0xC000) ld bc, #0xFEAF out (c), a ; byte[0] — expect 0xF5 (push af) ld a, (0xC001) ld bc, #0xFDAF out (c), a ; byte[1] — expect 0xC5 (push bc) ;=== DBG: scan pages base+3..base+7 looking for inflate === ld a, (0x6002) add a, #3 ld d, a ld e, #0 _ifl_scan_loop: ld a, d ld bc, #PORT_W3 out (c), a ld a, #0xD0 add a, e ld bc, #0xFFAF out (c), a ld a, d ld bc, #0xFDAF out (c), a ld a, (0xC000) ld bc, #0xFEAF out (c), a inc d inc e ld a, e cp #5 jr nz, _ifl_scan_loop ;--- DBG: 0x04 = scan done, re-map Win3 to inflate page --- ld bc, #0xFFAF ld a, #0x04 out (c), a ld a, (_ifl_inflate_pg) ld bc, #PORT_W3 out (c), a .endif ;--- 6. стек в Win 3, push return addr --- ld sp, #0xFFF0 ld hl, #_ifl_return push hl .ifdef DBG_ENABLE ;--- DBG: 0x05 = SP set, return addr pushed, loading regs --- ld bc, #0xFFAF ld a, #0x05 out (c), a ld a, (_ifl_src_pg) ld bc, #0xFEAF out (c), a ld a, (_ifl_dst_pg) ld bc, #0xFDAF out (c), a .endif ;--- 6b. Copy progress bar params to inflate page (0xC1F0+) --- ; Win 3 = inflate page (mapped in step 5) ; Win 2 = _CODE/_DATA (still accessible for reading _ifl_pb_*) ld a, (_ifl_pb_text_pg) ld (0xC1F0), a ld hl, (_ifl_pb_attr_ofs) ld (0xC1F1), hl ld a, (_ifl_pb_total) ld (0xC1F3), a ld a, (_ifl_pb_bw) ld (0xC1F4), a ld a, (_ifl_pb_green) ld (0xC1F5), a ; Initialize runtime state xor a ld (0xC1F6), a ; pb_error = 0 ld (0xC1F7), a ; pb_col = 0 ;--- 7. входные регистры inflate: A=src, E=dst, D=saved_pg2 --- ld a, (_ifl_sv_pg2) ld d, a ; D = saved Win 2 page ld a, (_ifl_dst_pg) ld e, a ; E = dst_page ld a, (_ifl_src_pg) ; A = src_page .ifdef DBG_ENABLE ;--- DBG: 0x06 = about to JP 0xC000 --- push af push bc ld bc, #0xFFAF ld a, #0x06 out (c), a pop bc pop af .endif ;--- 8. запуск inflate --- ; JP 0xC200 = inflate_init (safe zone, survives backref LDIR) ; НЕ JP 0xC000! Entry zone 0xC000–0xC1FF перезаписывается ; LDIR overflow при распаковке backref'ов. jp 0xC200 ;--- сюда вернёмся по RET из inflate --- ; A = 0 (ok) / 1 (error) ; Win 2 уже восстановлен inflate → _DATA и код доступны _ifl_return: ld e, a ; результат → E .ifdef DBG_ENABLE ;--- DBG: 0x03 = returned from inflate, A=result --- ld bc, #0xFFAF ld a, #0x03 out (c), a ld a, e ld bc, #0xFEAF out (c), a .endif ;--- восстановить SP (→ C-стек, сразу после push ix) --- ld sp, (_ifl_sv_sp) ;--- восстановить Win 1 (WC API точка входа @ 0x6006) --- ld a, (_ifl_sv_pg1) ld bc, #PORT_W1 out (c), a ;--- вернуть результат --- ld a, e pop ix ; восстановить IX ei ; EI + задержка 1 instr → ret атомарен ret ;============================================================================== ; void copy_pages_to_tap(uint8_t n_pages) ; ; Копирует n_pages страниц из мегабуфера (#20..#20+n-1) в TAP (#A1..#A1+n-1). ; Использует прямые port-записи в Win 0 / Win 3. ; DI на время работы; при выходе: Win 0, Win 3 восстановлены, EI. ; ; sdcccall(1): A = n_pages ;============================================================================== MEGABUF_START = 0x20 ; первая физ. страница мегабуфера TAP_START = 0xA1 ; первая физ. страница TAP _copy_pages_to_tap:: or a ret z ; n=0 → ничего делать push ix ld c, a ; C = n_pages (сохраним) di ;--- сохранить текущие страницы Win 0 / Win 3 --- ld a, (0x6000) ld (_cpt_sv_pg0), a ld a, (0x6003) ld (_cpt_sv_pg3), a ;--- подготовить счётчики --- ld d, #MEGABUF_START ; D = src phys page ld e, #TAP_START ; E = dst phys page ld b, c ; B = loop counter _cpt_loop: push bc push de ;--- Map Win 0 = megabuf page D --- ld a, d ld bc, #PORT_W0 out (c), a ;--- Map Win 3 = TAP page E --- ld a, e ld b, #0x13 ; C ещё 0xAF от предыдущего BC out (c), a ;--- LDIR 16 КБ: Win 0 → Win 3 --- ld hl, #0x0000 ld de, #0xC000 ld bc, #0x4000 ldir pop de inc d ; следующая megabuf page inc e ; следующая TAP page pop bc djnz _cpt_loop ;--- восстановить Win 0 --- ld a, (_cpt_sv_pg0) ld bc, #PORT_W0 out (c), a ;--- восстановить Win 3 --- ld a, (_cpt_sv_pg3) ld b, #0x13 out (c), a ei pop ix ret ;--- static vars for copy (в CODE — не в DATA, чтобы не зависеть от Win 2) --- _cpt_sv_pg0: .ds 1 _cpt_sv_pg3: .ds 1