Add OPL4 playback and analyzer support

This commit is contained in:
2026-09-15 14:59:29 +03:00
commit 1686838e03
553 changed files with 50524 additions and 0 deletions
+330
View File
@@ -0,0 +1,330 @@
;==============================================================================
; 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