Replace closed WDFCVBI2.COD with from-scratch open SD/FAT32 driver

Renamed as zc_sd_driver.asm/zc_sd_driver_body.asm (no relation to Wild
Commander). Fixes a stale-IX bug where core_mkfile's filter_name_to_83
left IX pointing into driver memory instead of restoring it, causing
zifi.asm's save_downloaded_file to read the wrong page/address for
every SAVE512 call after MKFILE. All CORE dispatch entry points now
preserve IX across the call. Also fixes a body/#2000 jump-table
overlap, sdbuf DMA word-alignment, and a load_ram_page staleness race
in zifi.asm's own download bookkeeping.
This commit is contained in:
2026-09-09 20:50:06 +03:00
parent 06c9efa9a8
commit c3c5c7dccc
7 changed files with 5452 additions and 18 deletions
+1 -2
View File
@@ -51,8 +51,7 @@ AT+CIPSNTPTIME? — получить текущее врем
- теперь DMA-очистка под индикатор громкости дожидается завершения перед тем, как CPU начинает рисовать поверх той же области (была гонка записи); - теперь DMA-очистка под индикатор громкости дожидается завершения перед тем, как CPU начинает рисовать поверх той же области (была гонка записи);
- исправил переполнение буфера имени файла (`create_filename`): при скачивании имя файла из ссылки копировалось в 48-байтный буфер без проверки длины, и если оно было длиннее — переполнение затирало соседние переменные (`zifi.ini`, имена служебных папок) прямо в памяти. Теперь копирование корректно обрезается на 48 символах. Проверено на железе: создание папки и файла с длинными именами (в т.ч. ровно 48 символов) и последующее чтение содержимого — всё работает штатно, никакой драйверной обрезки до 8 символов нет. - исправил переполнение буфера имени файла (`create_filename`): при скачивании имя файла из ссылки копировалось в 48-байтный буфер без проверки длины, и если оно было длиннее — переполнение затирало соседние переменные (`zifi.ini`, имена служебных папок) прямо в памяти. Теперь копирование корректно обрезается на 48 символах. Проверено на железе: создание папки и файла с длинными именами (в т.ч. ровно 48 символов) и последующее чтение содержимого — всё работает штатно, никакой драйверной обрезки до 8 символов нет.
> [!CAUTION] **Заменил драйвер SD-карты.** Штатный `_sd/WDFCVBI2.COD` (закрытый бинарник без исходников, автор — "Hacker VBI") был заменён на собственный, открытый SD/FAT32-драйвер (`_sd/zc_sd_driver.asm` + `_sd/zc_sd_driver_body.asm`) — реализует те же ~15 функций CORE-таблицы, которые реально использует ZiFi. Заодно исправлена и порча имён папок на FAT32 (короткие имена 8.3 иногда совпадали с уже существующей записью в каталоге). Подробнее — `_sd/README.md`.
> **Известная серьёзная проблема (пока не исправлена): порча имён папок на FAT32.** Штатный драйвер SD-карты (`_sd/WDFCVBI2.COD`, закрытый бинарник без исходников, автор — "Hacker VBI") при создании папки/файла с длинным именем иногда генерирует короткое имя 8.3, совпадающее с уже существующей записью в каталоге — получаются две разные папки с одинаковым коротким именем на диске. Windows chkdsk потом это обнаруживает и переименовывает вторую запись в `<имя>.--N`, а по факту это два разных физических каталога, которые должны были быть одним (например, файлы за один день оказываются раскиданы между `2026_09_01` и `2026_0~1.--0`). Нашёл и подтвердил дизассемблированием конкретный баг в алгоритме генерации числового суффикса `~N` внутри драйвера, но исправить сам драйвер нельзя — исходников нет, только бинарник. Сейчас пишу отдельный, открытый SD/FAT32-драйвер на замену (реализует только те ~15 функций, которые реально использует ZiFi) — работа в процессе, ещё не готово к использованию.
**Известная нерешённая мелочь:** курсор мыши (аппаратный спрайт TSU) обрывается по фиксированной строке экрана прямо над нижней панелью управления — похоже на границу, завязанную на тайминг переключения видеорежима или на аппаратный бордюр, а не на что-то, что двигается вместе с содержимым панели. Я не нашёл способ поправить это без риска сломать что-то ещё, оставил как есть. **Известная нерешённая мелочь:** курсор мыши (аппаратный спрайт TSU) обрывается по фиксированной строке экрана прямо над нижней панелью управления — похоже на границу, завязанную на тайминг переключения видеорежима или на аппаратный бордюр, а не на что-то, что двигается вместе с содержимым панели. Я не нашёл способ поправить это без риска сломать что-то ещё, оставил как есть.
+50
View File
@@ -0,0 +1,50 @@
# zc_sd_driver
Открытый SD/FAT32-драйвер для ZiFi, замена закрытого `WDFCVBI2.COD`.
Реализует ту же CORE-таблицу (`CORE equ #2002` в `zifi.asm`), что и
оригинальный драйвер, но с открытыми исходниками.
## Файлы
- `zc_sd_driver.asm` — резидентная часть: таблица переходов CORE,
di/ei-обёртки над каждой точкой входа, распаковщик тела.
- `zc_sd_driver_body.asm` — основная логика: SPI/SD-протокол, DMA,
разбор FAT32 (BPB, цепочки кластеров, FAT), поиск по имени с LFN,
создание/удаление файлов и каталогов. Хранится в сжатом виде и
распаковывается в резидентную часть во время выполнения `DOS_SWP`.
- `body_syms.inc` — адреса функций/переменных тела, сгенерированные
из символьной таблицы сборки `zc_sd_driver_body.asm`, подключаются
в `zc_sd_driver.asm` как константы.
## Реализованные функции CORE
`DEV_INI`, `HDD`, `LOAD512`, `SAVE512`, `DOS_SWP`, `MKFILE`, `MKDIR`,
`DELFL`, `RENAME`, `FENTRY`, `LOADNON`, `NXTETY`, `SETDIR`, `SETROOT`,
`SEEK0`. Остальные точки входа таблицы — заглушки (не используются
ZiFi).
## Адресация
Драйвер загружается в физическую страницу `#0F`, отображается в CPU
слот 0 (`#0000-#3FFF`). `CORE` (`#2002`) — фиксированный адрес внутри
этого слота. Резидентная часть собирается с `ORG #2000`; тело
распаковывается в адреса `#0000..body_end` той же страницы —
`body_end` обязан оставаться в пределах `#2002` (граница проверяется
на этапе сборки).
`LOAD512`/`SAVE512` используют прямой DMA между SD-картой и указанной
вызывающим физической страницей/смещением, не затрагивая пейджинг
CPU.
## Сборка
```
cd _sd
sjasmplus.exe zc_sd_driver_body.asm --sym=body_syms_new.txt
# упаковать zc_sd_driver_body.bin -> zc_sd_driver_body.pb (PackBits)
# обновить body_syms.inc из body_syms_new.txt (без DATA/CONF)
sjasmplus.exe zc_sd_driver.asm
```
Результат (`zc_sd_driver.bin`) копируется в `_spg/` и подключается
через `_spg/spgbld.ini` (`Block = #e000, #0f, zc_sd_driver.bin`).
+861
View File
@@ -0,0 +1,861 @@
DEVICE ZXSPECTRUM48
; From-scratch open SD/FAT32 driver for ZiFi, replacing the closed
; WDFCVBI2.COD -- built and hardware-tested incrementally in
; D:\ST\sjasmplus\sdtest\sdtest.asm across this whole investigation
; (see [[project-zifi-custom-sd-driver]]), this file assembles the
; SAME proven code into the jump-table shape zifi.asm's CORE equ #2002
; expects.
;
; Table layout confirmed by cross-referencing WC's own KERNEL.ASM
; START table (E:\zx-evo-master\pentevo\soft\WC\source\core32\
; KERNEL.ASM) -- byte-for-byte matching offsets (DEV_INI=+3, HDD=+9,
; LOAD512=+21, SAVE512=+24, DOS_SWP=+27, MKFILE=+57, MKDIR=+60,
; DELFL=+63, RENAME=+66, FENTRY=+78, LOADNON=+84, NXTETY=+87) for the
; first 31 entries, PLUS three more (SETDIR=+93, SETROOT=+96, SEEK0=
; +99) that WC's own core32 table does NOT have -- WDFCVBI2.COD's own
; table is a superset/independent implementation, not a live reference
; into whatever WC happens to be installed (confirmed by the user
; directly: Wild-Commander-Improved, which has the LFN bug already
; fixed in ITS OWN core32, did NOT fix ZiFi's bug -- so ZiFi's driver
; is definitely self-contained, not calling into WC's shared kernel).
;
; Loaded via spgbld's Block directive at #E000/page matching the
; original WDFCVBI2.COD placement -- the exact mechanism by which that
; maps to the fixed #2002 jump-table address is still not 100% proven
; (see [[project-zifi-custom-sd-driver]] for the open question), but
; the original driver's own DOS_SWP/LOAD512-real-address evidence
; found earlier in this investigation, PLUS this file being structured
; identically (jump table at the very start of the loaded block),
; should reproduce whatever that mechanism is. THIS IS THE FIRST REAL
; HARDWARE TEST of that hypothesis -- expect to iterate.
;
; Entries zifi.asm never calls (grepped for every CORE+n EQU it
; actually uses) are safe no-op/stub implementations: RDD/SDD/GIPAG/
; GLSTCAT/TLSTCAT/SRHFCL/MKSG/RFRH/GENTRY/TENTRY/DLSG/CHTOSE/LOAD256/
; NXTETY2/REINI, and the three raw "Z0" slots WC's own table leaves
; unimplemented too. NXTETY (directory enumeration, a different job
; from FENTRY's single-name search) is ALSO a stub for now -- zifi.asm's
; only reference to it (VYGREB/VYG) is commented-out dead code
; ("Выгребаем каталог, чисто по приколу" -- "just for fun"), never
; actually called by the real running program.
; NOTE: this code is invoked via zifi.asm's init_sd_card (line ~4134),
; which does "ld a,sd_driver_page(#0f) / jp set_page0" -- i.e. maps
; page #0F into PAGE0 (CPU slot 0, addresses #0000-#3FFF), NOT slot 3.
; CORE=#2002 is a slot-0 address, so every internal absolute JP/CALL in
; this file must be assembled as if it lives at #2002, not #E000 --
; otherwise the driver hangs the instant it's entered (confirmed on
; hardware: first attempt used ORG #E000 and hung on the boot banner).
;
; spgbld's Block directive only accepts 512-byte-aligned addresses, and
; #2002 isn't one -- so we can't just tell it to load at offset #2002
; directly (Block = #e002 -> "Block address is not a 512 multiple!").
; Instead we load at the aligned #e000 (page-offset #2000, same as the
; original WDFCVBI2.COD placement) and pad 2 bytes here so the actual
; jump table lands exactly on #2002 once loaded. This is almost
; certainly what WDFCVBI2.COD's own "DOS_SWP; DEPACK Driver" call was
; really doing at runtime (relocating itself those same 2 bytes) --
; padding at assembly time is simpler and needs no runtime relocator.
ORG #2000
DS 2 ; pad offset #2000-#2001; jump table
; below starts at #2002 = CORE
CONF EQU #77
DATA EQU #57
; Body-routine addresses (sdinit, dma_set_addr, sdread_lba, parse_bpb,
; core_fentry, get_next_cluster, write_fat_entry, find_by_name,
; sdwrite_lba, cur_cluster_hi/lo, target_name, dot_name,
; clear_dir_entry, and everything else from zc_sd_driver_body.asm) --
; imported here as plain EQU constants (generated from that file's own
; --sym dump by build.ps1) so every JP/CALL/reference below resolves
; correctly even though the actual body code isn't present in this
; file -- it's compressed and depacked at runtime by drv_dos_swp
; instead. See the fuller comment further down and
; [[project-zifi-custom-sd-driver]].
INCLUDE "body_syms.inc"
;--- Build-time guard: the depacked body (ORG #0000..body_end, written
;--- by drv_dos_swp at runtime) MUST stay strictly under #2002 (CORE
;--- itself, this file's own resident jump table) -- otherwise depacking
;--- silently overwrites the START of THIS file's own resident code,
;--- every single time it runs. This isn't hypothetical: body growth
;--- crossed #2000 by 198 bytes in this exact project (body_end reached
;--- #20C6) and every CORE dispatch call (DEV_INI, HDD, FENTRY,
;--- everything) silently jumped to garbage as a result -- diagnosed
;--- the hard way via PEEKPHYS/checkpoint instrumentation after
;--- "zifi.ini not found" started happening. The threshold is #2002, not
;--- #2000: the 2 bytes at #2000-#2001 (see "DS 2" above) are pure
;--- alignment padding, never read by anything, so the depacked body may
;--- safely fill them too -- body_end==#2002 means the last byte written
;--- is at #2001, one below CORE. Never remove this check; if it fires,
;--- shrink zc_sd_driver_body.asm (or increase this IF condition's
;--- threshold together with confirming real headroom exists all the way
;--- to code_end below #4000) before doing anything else. See
;--- [[project-zifi-custom-sd-driver]]. ---
IF body_end > #2002
DEFB 1/0 ; BODY OVERLAPS RESIDENT CODE -- FIX THIS, see comment above
ENDIF
;=====================================================================
; Jump table -- 34 entries x 3 bytes (JP nnnn), offsets 0..99, must
; stay in this exact order/spacing to match every CORE+n EQU in
; zifi.asm.
;=====================================================================
JP drv_seldev ; +0
JP wrap_dev_init ; +3
JP drv_stub_ok ; +6 REINI
JP wrap_hdd ; +9
JP drv_stub_fail ; +12 RDD
JP drv_stub_fail ; +15 SDD
JP drv_stub_fail ; +18 GIPAG
JP wrap_load512 ; +21
JP wrap_save512 ; +24
JP wrap_dos_swp ; +27
JP drv_stub_fail ; +30 GLSTCAT
JP drv_stub_fail ; +33 TLSTCAT
JP drv_stub_fail ; +36 SRHFCL
JP drv_stub_fail ; +39 Z0
JP drv_stub_fail ; +42 MKSG
JP drv_stub_fail ; +45 Z0
JP drv_stub_fail ; +48 RFRH
JP drv_stub_fail ; +51 GENTRY
JP drv_stub_fail ; +54 TENTRY
JP wrap_mkfile ; +57
JP wrap_mkdir ; +60
JP wrap_delfl ; +63 DELFL
JP wrap_renam ; +66 RENAM
JP drv_stub_fail ; +69 DLSG
JP drv_stub_fail ; +72 CHTOSE
JP drv_stub_fail ; +75 Z0
JP wrap_fentry ; +78
JP drv_stub_fail ; +81 LOAD256
JP wrap_loadnon ; +84
JP drv_nxtety ; +87
JP drv_stub_fail ; +90 NXTETY2
JP wrap_setdir ; +93
JP wrap_setroot ; +96
JP wrap_seek0 ; +99
;=====================================================================
; di/ei wrappers around every entry point that does real work while
; executing from CPU slot 0 (this whole driver only exists there while
; PAGE0 points at page #0F -- see the ORG note above). Found on
; hardware: zifi.asm's own "off_int_dma"/save_mode flag (which init_sd_
; card/sd_exit toggle around SD operations) does NOT cover everything --
; int_main's pt_play (zifi.asm ~line 2350, gated by a SEPARATE "music_sw"
; flag, not save_mode) calls set_music_pages_lite EVERY interrupt
; whenever a track is playing, which repages PAGE0 *and* PAGE1 *and*
; PAGE3 for the music player's own use. If that fires while we're mid-
; CORE-call: PAGE0 changing while we're actively EXECUTING CODE FROM
; SLOT 0 crashes/jumps to garbage the instant the interrupt returns
; (next instruction fetch reads the wrong page); PAGE3 changing mid-
; LOAD512/SAVE512 silently swaps the transfer's source/dest to whatever
; the music player last pointed it at instead of the real caller data --
; confirmed on hardware as the cause of downloaded files playing back as
; a constant garbage pattern instead of the real track. Wrapping the
; real logic in a plain di/ei critical section (bodies below are
; UNCHANGED, just called instead of jumped to) closes both holes
; regardless of music_sw/save_mode state. The original hardware-tested
; function bodies keep their own names/all internal early-return paths
; exactly as they were -- only the outer entry/exit changed.
;=====================================================================
; Every wrap_* below preserves IX across the real call (push/pop around
; it) -- ZiFi.asm's own CORE-ABI convention relies on IX surviving a
; CORE call unmolested (save_downloaded_file sets "ld ix,read_threads"
; ONCE, then keeps using (ix+thread.n) addressing across MULTIPLE CORE
; calls, including AFTER MKFILE returns, with no re-load in between).
; filter_name_to_83 (zc_sd_driver_body.asm, used by core_mkfile/
; core_mkdir's short-name generation) uses IX internally as a plain
; scratch pointer (ld ix,sn_base / ld ix,sn_ext) and never restores it
; -- so a caller relying on IX surviving MKFILE was silently reading
; save_64's (ix+thread.page)/(ix+thread.adress) from wherever sn_base/
; sn_ext happened to leave IX pointing (this driver's own low, #0000-
; based body addresses, still mapped into slot 0 at that point) instead
; of zifi's real read_threads struct -- confirmed as the actual root
; cause of "every download saves as identical, page/track-independent
; garbage" (2026-09-09): read_threads.page/adress were correct the
; WHOLE time, SAVE512 was just told to read from the wrong place by a
; stale IX. See [[project-zifi-custom-sd-driver]]. ---
wrap_dev_init di
push ix
call drv_dev_init
pop ix
ei
ret
wrap_hdd di
push ix
call drv_hdd
pop ix
ei
ret
;--- NO di/ei needed here, matching what a real disassembly of the
;--- ORIGINAL closed WDFCVBI2.COD driver's own SAVE512/LOAD512 turned
;--- out to do (hand-verified: zero real DI/EI opcodes anywhere in its
;--- transfer routine at #22DB). That disassembly also showed WHY it
;--- didn't need any: its transfer uses the TS-Conf DMA controller, not
;--- a CPU-driven page1_port+LDIR loop. core_save512/core_load512 (in
;--- zc_sd_driver_body.asm) now do the same -- DMA straight between the
;--- SD card and the caller's page, addressed by physical page number,
;--- never touching the CPU's slot mapping at all. Two di/ei variants
;--- around the OLD LDIR-based version were tried first and both made
;--- things WORSE on real hardware (di/ei around the whole transfer:
;--- audio noise + hangs on track switch; di/ei only around the brief
;--- PAGE1-touch+LDIR: full hangs with garbage on screen) -- di/ei
;--- was never going to fix it, since the LDIR's dependence on PAGE1
;--- staying stable across the whole copy was the real hazard, not a
;--- timing race di/ei could close (a write-only page port can't be
;--- restored after set_music_pages_lite clobbers it mid-transfer,
;--- di/ei or not). See [[project-zifi-custom-sd-driver]]. ---
wrap_load512 push ix
call core_load512
pop ix
ret
wrap_save512 push ix
call core_save512
pop ix
ret
wrap_mkfile di
push ix
call core_mkfile
pop ix
ei
ret
wrap_mkdir di
push ix
call core_mkdir
pop ix
ei
ret
wrap_fentry di
push ix
call drv_fentry
pop ix
ei
ret
wrap_loadnon di
push ix
call drv_loadnon
pop ix
ei
ret
wrap_setdir di
push ix
call core_setdir
pop ix
ei
ret
wrap_setroot di
push ix
call core_setroot
pop ix
ei
ret
wrap_seek0 di
push ix
call stream_open
pop ix
ei
ret
wrap_delfl di
push ix
call core_delfl
pop ix
ei
ret
wrap_renam di
push ix
call core_renam
pop ix
ei
ret
; DOS_SWP now does real work (depacking the body -- see drv_dos_swp
; below), unlike every other entry here it previously had NO di/ei
; protection at all (harmless when it was a no-op). Real interrupts
; MUST stay off for the whole unpack: page #0F is mapped into slot 0
; the entire time, and zifi.asm's pt_play interrupt handler repages
; PAGE0/1/3 for the music player if it fires mid-CORE-call (see the
; header comment above) -- exactly the same risk this wrapping already
; protects every other substantive entry against.
; No `ei` here (unlike every other wrap_*): DOS_SWP is ALWAYS
; immediately followed by another di-guarded CORE call in zifi.asm's
; own fixed sequence (sd_init: CALL DOS_SWP; CALL DEV_INI; ...) -- so
; re-enabling interrupts here just to have the very next call disable
; them again is pointless, and genuinely risky while depacking: an
; interrupt landing in the gap would fire with PAGE0 still pointing at
; page #0F, jumping to #0038 (IM1 vector) which -- mid-depack -- can
; be anything from still-compressed garbage to not-yet-written body
; code, not a real ROM/interrupt handler. Confirmed by direct testing:
; this exact gap crashed a standalone depack test (wild PC, eventual
; HALT) until `ei` was removed here.
wrap_dos_swp di
call drv_dos_swp
ret
;--- drv_stub_ok / drv_stub_fail: safe stubs for every CORE-table slot
;--- zifi.asm never actually calls (confirmed via its own CORE+n EQU
;--- list) -- Z (success-shaped) or NZ (not-found/error-shaped)
;--- respectively, whichever is the safer default for a function
;--- nothing should ever invoke. ---
drv_stub_ok xor a
ret
drv_stub_fail xor a
inc a
ret
drv_seldev xor a
ret
;--- drv_dev_init: CORE+3, DEV_INI -- raw SD card init only (matches
;--- zifi.asm's own "CALL DEV_INI:JP NZ,ER0" -- HDD/partition search is
;--- a separate, later call). Z=ok, NZ=fail. ---
drv_dev_init call sdinit
ret
;--- drv_hdd: CORE+9, HDD ("SEARCH PARTITION" per WC's own naming) --
;--- reads sector 0, detects/validates the partition (detect_partition
;--- already does its own verify_fat32 check internally, falling back
;--- to sector 0 itself if no MBR partition table is present), then
;--- parses the real BPB. Z=ok, NZ=fail. ---
drv_hdd ld hl,0
ld de,0
call set_lba32
call sdread_lba
ret nz
call detect_partition
ret nz
call parse_bpb
; Pre-warm the RTC read HERE, during sd_init's quiet pre-EI
; window (zifi.asm calls sd_init at line ~56, then "ei" at
; line 57 -- HDD runs before interrupts are even on and
; before any active UI/video updates). rtc_to_fat_datetime's
; own caching means every LATER call (from core_mkdir/
; core_mkfile during the active WiFi-connect/download phase)
; just reuses this result instead of touching the RTC chip
; again. This matters because [[project-zifi-rtc-time-sync]]
; already found -- the hard way, on this exact board -- that
; touching #EFF7 (which this RTC read also does, to select
; the CMOS/RTC register space) during an active UI phase can
; cause real corruption even with correct di/ei and a proper
; read-modify-write, for reasons never fully root-caused;
; the only fix that actually worked there was moving WHEN
; the touch happens, not how. Doing it here avoids the same
; class of risk instead of re-discovering it the hard way.
call rtc_to_fat_datetime
xor a
ret
;--- drv_dos_swp: CORE+27, DOS_SWP ("SELDEVnDRIVERinit" / "DEPACK
;--- Driver" per WC's/zifi's own naming) -- the original closed driver
;--- was compressed and self-relocated here; this driver now does the
;--- same thing for real, not just in spirit: the "body" (everything
;--- from sdinit onward -- DMA/SD/FAT32/CORE-ABI logic, built and
;--- hardware-tested as zc_sd_driver_body.asm) is compressed and stored
;--- as body_packed_start..body_packed_end below. Decompressing it to
;--- its true addresses (#0000-body_end, see body_syms.inc) is what
;--- makes those addresses (and every JP/CALL into them from this
;--- resident half, including the jump table indirectly via wrap_hdd
;--- etc, and DELFL/RENAM below) valid -- this MUST run before any
;--- other CORE entry point does real work, matching zifi.asm's own
;--- fixed call order (sd_init: CALL DOS_SWP; DEPACK Driver -- always
;--- immediately after paging page #0F into slot 0, always before
;--- DEV_INI/HDD/etc).
;---
;--- Hrust1, not ZX0: ZX0 (both the "standard" forward AND backward
;--- Z80 decoders, dzx0_standard(_back).asm by Einar Saukas) was tried
;--- first and, after extensive isolated Z80-harness testing (see
;--- [[project-zifi-custom-sd-driver]]), both directions turned out to
;--- decompress small/simple payloads (even ones with real
;--- back-references) correctly but produce garbage specifically on
;--- this driver's full 8027-byte body -- root cause never found
;--- despite real effort, abandoned as too large a time sink for an
;--- optional space optimization.
;---
;--- Chased a real, never-root-caused bug here for a long time: every
;--- attempt to fix load_ini's address-0 collision FROM THIS SIDE
;--- (padding the body with a sacrifice_buf, later an ORG shift to
;--- #0200 with zero added bytes) reliably broke BOTH Z80 Hrust
;--- decoders tried (hand-transcribed classic dehst.asm DEHRUST,
;--- confirmed byte-for-byte correct against source via automated
;--- diff; and hrust13.exe's own "-depacker" self-relocating output)
;--- the instant core_load512's fix (in zc_sd_driver_body.asm) grew
;--- the body by its own ~53 bytes, regardless of padding/positioning
;--- -- see [[project-zifi-custom-sd-driver]] for the full history.
;--- Root cause in either decoder never found under real time
;--- pressure. Fixed properly instead: zifi.asm's load_ini/parse_ini
;--- now use LOAD512's SAME slot-1/#4000/PAGE1 convention as SAVE512
;--- (see core_load512's own comment in zc_sd_driver_body.asm), so
;--- load_ini never touches address 0 at all anymore.
;---
;--- Hrust1 fully abandoned at this point (both the classic hand-
;--- transcribed dehst.asm DEHRUST -- confirmed byte-for-byte correct
;--- against source via automated diff, TWICE -- and hrust13.exe's own
;--- "-depacker" output kept producing wrong Z80 decompression results
;--- on data that a PC-side reference tool, dehrust1.exe, decoded
;--- correctly every single time, on EVERY size/content tried,
;--- including this exact 8018-byte body with no changes at all except
;--- core_load512's own content -- so dehrust1.exe was never actually
;--- validating what was assumed; root cause never found under real
;--- time pressure -- see [[project-zifi-custom-sd-driver]]).
;---
;--- Replaced with a trivial, self-written, fully-verified PackBits-
;--- style RLE (round-tripped byte-for-byte on the PC first, in
;--- PowerShell, before ever touching Z80 code): control byte C,
;--- C=128 is the end marker; C<128 is a literal run of C+1 bytes
;--- (copied verbatim); C>128 is a repeat run of (257-C) copies of the
;--- single byte that follows (257-C covers 2..128 for C=255..129; C=1
;--- unused/invalid, C=128 reserved as the terminator instead of a
;--- literal-run-of-1 encoding). Only ~12.5% compression is actually
;--- needed here (8018-byte body must fit under ~7076 bytes to leave
;--- room for the #2000-#3FFF dispatcher) -- this achieves 5929 bytes
;--- (~74%), comfortable margin, on real driver code (long runs of
;--- identical bytes are common in data tables/padding). ---
drv_dos_swp ld ix,body_packed_start
ld de,0
call PB_DECODE
xor a
ret
;=====================================================================
; PB_DECODE: PackBits-style RLE decoder (see drv_dos_swp's comment for
; the format). IX=source, DE=dest. Doesn't touch SP.
;=====================================================================
PB_DECODE
PB_LOOP ld a,(ix+0)
inc ix
cp 128
ret z ; terminator
bit 7,a
jr z,PB_LITERAL
neg ; a = 256-ctrl (ctrl in 129..255)
inc a ; a = 257-ctrl (mod 256) = repeat count (2..128)
ld b,a
ld a,(ix+0)
inc ix
PB_REP_LOOP ld (de),a
inc de
djnz PB_REP_LOOP
jr PB_LOOP
PB_LITERAL inc a ; a = ctrl+1 = literal count (1..128)
ld b,a
PB_LIT_LOOP ld a,(ix+0)
inc ix
ld (de),a
inc de
djnz PB_LIT_LOOP
jr PB_LOOP
;--- drv_fentry: CORE+78, FENTRY -- real ABI is HL -> {flag(1),
;--- name(1-255),0} (flag = file-vs-dir attribute filter, not yet
;--- implemented -- see [[project-zifi-custom-sd-driver]] Next steps).
;--- On a match, automatically opens a stream on the found entry's
;--- cluster (matching the real ABI's own doc: "SEEK0 is automatically
;--- called") so a caller can go straight into LOAD512/SAVE512/LOADNON
;--- without any extra setup, exactly like zifi.asm's own real call
;--- sites do (FENTRY then LOAD512, nothing in between). Z=NOT FOUND,
;--- NZ=FOUND + [DE,HL]=length. ---
drv_fentry inc hl ; skip the flag byte (filter not
; implemented yet -- matches by name
; only)
call core_fentry
or a
ret z ; not found
ld hl,(fbn_cluster_hi)
ld (cur_cluster_hi),hl
ld hl,(fbn_cluster_lo)
ld (cur_cluster_lo),hl
call stream_open
ld de,(fbn_size_hi)
ld hl,(fbn_size_lo)
ld a,1
or a ; NZ (A=1, nonzero) -- found
ret
;--- drv_loadnon: CORE+84, LOADNON -- advances the CURRENTLY OPEN
;--- stream (opened by the last drv_fentry match) by B sectors without
;--- reading/writing data. Real ABI doesn't document a return value
;--- beyond implicit success. ---
drv_loadnon ld a,b
or a
ret z
dln_loop push bc
call strm_advance
pop bc
djnz dln_loop
xor a
ret
;--- drv_nxtety: CORE+87, NXTETY -- real ABI is a directory-listing
;--- iterator (GetNextEntryFromActiveDir), a genuinely different job
;--- from FENTRY's single-name search -- NOT YET BUILT. Stubbed as
;--- "always EndOfDir" (Z) since zifi.asm's only reference to it
;--- (VYGREB/VYG) is commented-out dead code, never actually called by
;--- the real running program -- revisit if that changes. ---
drv_nxtety xor a
ret
; CORE+99, SEEK0 (per FENTRY's own doc, "SEEK0 is automatically
; called" by FENTRY -- nothing in zifi.asm calls this directly) is
; wired straight to stream_open via wrap_seek0 in the jump-table
; wrapper block above -- no separate drv_seek0 needed.
;=====================================================================
; Everything that used to live inline below this point (ported,
; hardware-tested code from D:\ST\sjasmplus\sdtest\sdtest.asm -- DMA/
; SD/FAT32/CORE-ABI logic) now lives in zc_sd_driver_body.asm instead,
; compiled to its own true addresses (#0000 upward), compressed with
; ZX0 and depacked here at runtime by drv_dos_swp (see above) -- this
; is what freed enough room in this #2000-#3FFF half for DELFL/RENAM
; to fit alongside everything already here. body_syms.inc (included
; near the top of this file) supplies every body label as a plain EQU
; constant. See [[project-zifi-custom-sd-driver]].
;=====================================================================
;=====================================================================
; CORE ABI: DELFL/RENAM -- never actually called by zifi.asm (grepped
; the whole file for CALL DELFL/CALL RENAM, zero hits), but built
; anyway per explicit user request ("для полноты драйвера"). Both need
; to know exactly which directory entries to mark deleted (0xE5) --
; the short entry AND any preceding LFN run -- so find_by_name now
; tracks fbn_match_index_lo/fbn_match_run_start_lo (see above) for
; this purpose.
;=====================================================================
;--- core_free_chain: (cur_cluster_hi,cur_cluster_lo) -> frees every
;--- cluster in this FAT chain (FAT entry := 0, mirrored to all copies
;--- via write_fat_entry) by following get_next_cluster until chain_
;--- ended. A=0 ok, A=1 fail (disk error -- chain may be partially
;--- freed). ---
cfc_cur_hi dw 0
cfc_cur_lo dw 0
cfc_next_hi dw 0
cfc_next_lo dw 0
cfc_has_next db 0
core_free_chain
ld hl,(cur_cluster_hi)
ld (cfc_cur_hi),hl
ld hl,(cur_cluster_lo)
ld (cfc_cur_lo),hl
cfc_loop ld hl,(cfc_cur_hi)
ld (cur_cluster_hi),hl
ld hl,(cfc_cur_lo)
ld (cur_cluster_lo),hl
call get_next_cluster
ld a,(chain_ended)
or a
jr nz,cfc_nonext
ld hl,(cur_cluster_hi)
ld (cfc_next_hi),hl
ld hl,(cur_cluster_lo)
ld (cfc_next_lo),hl
ld a,1
ld (cfc_has_next),a
jr cfc_free_this
cfc_nonext xor a
ld (cfc_has_next),a
cfc_free_this ld hl,(cfc_cur_hi)
ld (cur_cluster_hi),hl
ld hl,(cfc_cur_lo)
ld (cur_cluster_lo),hl
ld hl,0
ld (new_val_hi),hl
ld (new_val_lo),hl
call write_fat_entry
or a
jr nz,cfc_fail
ld a,(cfc_has_next)
or a
jr z,cfc_done
ld hl,(cfc_next_hi)
ld (cfc_cur_hi),hl
ld hl,(cfc_next_lo)
ld (cfc_cur_lo),hl
jr cfc_loop
cfc_done xor a
ret
cfc_fail xor a
inc a
ret
;--- core_mark_deleted_range: (cur_cluster_hi,cur_cluster_lo already
;--- set by caller to the directory) + cmdr_start_lo/cmdr_end_lo
;--- (logical 32-byte-entry-index range, inclusive) -> marks each
;--- entry's first byte 0xE5 (deleted). A=0 ok, A=1 fail (disk error). ---
cmdr_start_lo dw 0
cmdr_end_lo dw 0
cmdr_cur_index_lo dw 0
cmdr_entptr dw 0
cmdr_dirty db 0
cmdr_this_sec_hi dw 0
cmdr_this_sec_lo dw 0
core_mark_deleted_range
call stream_open
ld hl,0
ld (cmdr_cur_index_lo),hl
cmdr_sector_loop
ld a,(stream_eoc)
or a
jp nz,cmdr_done
call strm_calc_sector
ld hl,(strm_sector_hi)
ld (cmdr_this_sec_hi),hl
ld hl,(strm_sector_lo)
ld (cmdr_this_sec_lo),hl
ld hl,(strm_sector_hi)
ld de,(strm_sector_lo)
call set_lba32
call sdread_lba
or a
jr nz,cmdr_fail
xor a
ld (cmdr_dirty),a
ld hl,sdbuf
ld (cmdr_entptr),hl
ld b,16
cmdr_entloop ld hl,(cmdr_cur_index_lo)
ld de,(cmdr_start_lo)
or a
sbc hl,de
jr c,cmdr_notinrange
ld hl,(cmdr_cur_index_lo)
ld de,(cmdr_end_lo)
or a
sbc hl,de
jr c,cmdr_inrange
jr nz,cmdr_notinrange
cmdr_inrange ld hl,(cmdr_entptr)
ld (hl),#e5
ld a,1
ld (cmdr_dirty),a
cmdr_notinrange
ld hl,(cmdr_cur_index_lo)
inc hl
ld (cmdr_cur_index_lo),hl
ld hl,(cmdr_entptr)
ld de,32
add hl,de
ld (cmdr_entptr),hl
djnz cmdr_entloop
ld a,(cmdr_dirty)
or a
jr z,cmdr_skip_write
ld hl,(cmdr_this_sec_hi)
ld de,(cmdr_this_sec_lo)
call set_lba32
call sdwrite_lba
or a
jr nz,cmdr_fail
cmdr_skip_write
ld hl,(cmdr_cur_index_lo)
ld de,(cmdr_end_lo)
or a
sbc hl,de
jr c,cmdr_continue
jr nz,cmdr_done
cmdr_continue call strm_advance
jp cmdr_sector_loop
cmdr_done xor a
ret
cmdr_fail xor a
inc a
ret
;--- core_delfl: HL -> {flag(1),name(1-255),0} -> finds the entry (via
;--- find_by_name in the active directory), frees its FAT chain, and
;--- marks its short entry + any preceding LFN run deleted. Z=NOT
;--- FOUND, NZ=FILE DELETED -- matches real DELFL's documented ABI
;--- (note the inverted sense vs MKDIR/MKFILE's Z=success). ---
core_delfl inc hl ; skip flag byte
ld (cmk_name_ptr),hl
ld hl,(active_dir_hi)
ld (cur_cluster_hi),hl
ld hl,(active_dir_lo)
ld (cur_cluster_lo),hl
ld hl,(cmk_name_ptr)
call find_by_name
or a
ret z ; not found -- Z, matches real DELFL
ld hl,(fbn_cluster_hi)
ld (cur_cluster_hi),hl
ld hl,(fbn_cluster_lo)
ld (cur_cluster_lo),hl
call core_free_chain
ld hl,(fbn_match_run_start_lo)
ld (cmdr_start_lo),hl
ld hl,(fbn_match_index_lo)
ld (cmdr_end_lo),hl
ld hl,(active_dir_hi)
ld (cur_cluster_hi),hl
ld hl,(active_dir_lo)
ld (cur_cluster_lo),hl
call core_mark_deleted_range
xor a
inc a ; NZ -- FILE DELETED
ret
;--- core_renam: HL -> {flag(1),oldname(1-255),0}, DE -> {newname
;--- (1-255),0} -> renames an entry in the active directory, preserving
;--- its cluster/size/attr (re-stamps date/time fresh). Z=NOT FOUND,
;--- NZ=SUCCESS -- matches real RENAM's documented ABI. Not
;--- transactional: the old entry is marked deleted before the new one
;--- is written, matching this driver's existing failure-mode
;--- conventions elsewhere (no rollback attempted) -- the real ABI has
;--- no code for "found but couldn't rename" to report anyway. ---
crn_newname_ptr dw 0
crn_old_attr db 0
crn_old_size_hi dw 0
crn_old_size_lo dw 0
crn_old_run_start_lo dw 0
crn_old_match_index_lo dw 0
core_renam inc hl ; skip flag byte -- oldname starts here
ld (cmk_name_ptr),hl
ld (crn_newname_ptr),de
ld hl,(active_dir_hi)
ld (cur_cluster_hi),hl
ld hl,(active_dir_lo)
ld (cur_cluster_lo),hl
ld hl,(cmk_name_ptr)
call find_by_name
or a
ret z ; not found -- Z, matches real RENAM
ld hl,(fbn_cluster_hi)
ld (cmk_cluster_hi),hl
ld hl,(fbn_cluster_lo)
ld (cmk_cluster_lo),hl
ld a,(fbn_attr)
ld (crn_old_attr),a
ld hl,(fbn_size_hi)
ld (crn_old_size_hi),hl
ld hl,(fbn_size_lo)
ld (crn_old_size_lo),hl
ld hl,(fbn_match_run_start_lo)
ld (crn_old_run_start_lo),hl
ld hl,(fbn_match_index_lo)
ld (crn_old_match_index_lo),hl
; refuse if the NEW name already exists -- generate_unique_
; shortname only avoids SHORT-name collisions, so without this
; explicit check RENAM could create a SECOND entry displaying
; the same long name as an existing one (confirmed on hardware:
; renaming into an already-used name did exactly that -- same
; bug class as core_mkdir/core_mkfile's own duplicate-guard,
; just missed here originally). Checked BEFORE touching the
; old entry at all, so a refusal here leaves everything as-is.
ld hl,(active_dir_hi)
ld (cur_cluster_hi),hl
ld hl,(active_dir_lo)
ld (cur_cluster_lo),hl
ld hl,(crn_newname_ptr)
call find_by_name
or a
ret nz ; new name already exists -- refuse
; (Z, matching RENAM's two-outcome ABI)
ld hl,(crn_old_run_start_lo)
ld (cmdr_start_lo),hl
ld hl,(crn_old_match_index_lo)
ld (cmdr_end_lo),hl
ld hl,(active_dir_hi)
ld (cur_cluster_hi),hl
ld hl,(active_dir_lo)
ld (cur_cluster_lo),hl
call core_mark_deleted_range
call rtc_to_fat_datetime
call clear_dir_entry
call stamp_dir_entry_datetime
ld a,(crn_old_attr)
ld (dir_entry_buf+11),a
ld hl,(cmk_cluster_hi)
ld (dir_entry_buf+20),hl
ld hl,(cmk_cluster_lo)
ld (dir_entry_buf+26),hl
ld hl,(crn_old_size_lo)
ld (dir_entry_buf+28),hl
ld hl,(crn_old_size_hi)
ld (dir_entry_buf+30),hl
ld hl,(active_dir_hi)
ld (cur_cluster_hi),hl
ld hl,(active_dir_lo)
ld (cur_cluster_lo),hl
ld hl,(crn_newname_ptr)
call filter_name_to_83
call generate_unique_shortname
or a
jr nz,crn_done ; degenerate failure -- old entry
; already deleted, nothing sane left
; to roll back to; ABI has no code
; for this anyway (see header comment)
ld hl,(crn_newname_ptr)
call build_lfn_entries
ld hl,(active_dir_hi)
ld (cur_cluster_hi),hl
ld hl,(active_dir_lo)
ld (cur_cluster_lo),hl
call find_free_run
or a
jr nz,crn_done
ld hl,(active_dir_hi)
ld (cur_cluster_hi),hl
ld hl,(active_dir_lo)
ld (cur_cluster_lo),hl
call write_name_entries
crn_done xor a
inc a ; NZ -- SUCCESS (per the real ABI's
; two-outcome contract; see header
; comment about the no-rollback
; degenerate case)
ret
;--- The PackBits-compressed body blob (see drv_dos_swp's comment for
;--- the format and PB_DECODE for the decoder). Produced by
;--- packbits_encode.ps1 (D:\Temp\claude\E--Claude\
;--- a54de7f1-2eb7-418e-8cf2-9a46028fb01b\scratchpad) -- no header, no
;--- footer, just the encoded stream ending in its own 0x80 terminator
;--- byte, so PB_DECODE needs no extra length/tail parameters at all. ---
body_packed_start:
INCBIN "zc_sd_driver_body.pb"
body_packed_end:
code_end:
SAVEBIN "zc_sd_driver.bin",#2000,code_end-#2000
File diff suppressed because it is too large Load Diff
Binary file not shown.
+12 -1
View File
@@ -10,7 +10,18 @@ Pager = 0
;Block = #e000, 5,violent.bin ;Block = #e000, 5,violent.bin
Block = #c000, 2,zifi.bin Block = #c000, 2,zifi.bin
Block = #c000, 3,ptplay.bin Block = #c000, 3,ptplay.bin
Block = #e000, #0f,WDFCVBI2.COD ; sd driver ;Block = #e000, #0f,WDFCVBI2.COD ; sd driver -- OLD, closed driver
; (backed up in _backup_old_driver/),
; disabled while testing the from-
; scratch replacement below
Block = #e000, #0f,zc_sd_driver.bin ; sd driver (from-scratch replacement,
; see [[project-zifi-custom-sd-driver]]) --
; driver is entered via PAGE0 (CPU slot 0,
; #0000-#3FFF) where CORE=#2002 lives; the
; .asm pads 2 bytes at its own #2000 so the
; jump table lands exactly on #2002 once
; loaded here (spgbld needs a 512-aligned
; Block address, #e002 is rejected)
Block = #c000, #10,cursor.tga.pix4 Block = #c000, #10,cursor.tga.pix4
+149 -15
View File
@@ -281,7 +281,13 @@ load_ram_page ld a,0
ex de,hl ; download adress for lists. after "Search:" ex de,hl ; download adress for lists. after "Search:"
1 ld (zipd_adr+1),hl 1 ld (zipd_adr+1),hl
IF cable_zifi ; snapshot load_ram_page NOW, before the (possibly multi-second)
; network receive below -- see saved_thread_page's own comment for why
; re-reading load_ram_page AFTER the call is wrong.
ld a,(load_ram_page+1)
ld (saved_thread_page),a
IF cable_zifi
ld a,(load_ram_page+1) ld a,(load_ram_page+1)
ld hl,modem_command ld hl,modem_command
call psb_get call psb_get
@@ -289,7 +295,6 @@ load_ram_page ld a,0
call zifi_get call zifi_get
ENDIF ENDIF
; bc: hl - lenght of readed data ; bc: hl - lenght of readed data
ld ix,read_threads ld ix,read_threads
ld a,c ld a,c
@@ -303,7 +308,7 @@ load_ram_page ld a,0
1 xor a 1 xor a
ld (ix+thread.num),a ld (ix+thread.num),a
ld a,(load_ram_page+1) ld a,(saved_thread_page)
ld (ix+thread.page),a ld (ix+thread.page),a
call set_page1 call set_page1
ld hl,status_copy ; clear statusbar gfx ld hl,status_copy ; clear statusbar gfx
@@ -3824,13 +3829,47 @@ sd_init
*/ */
; on_int_dma/off_int_dma bracket EVERY SD operation (init_sd_card at
; the start, sd_exit at the end -- see their own call sites) -- but
; save_mode alone doesn't cover everything: int_main's pt_play (~line
; 2350) is gated by its OWN SEPARATE music_sw flag, not save_mode, and
; unconditionally repages PAGE0/PAGE1/PAGE3 for the music player
; (set_music_pages_lite) on every single interrupt a track is playing.
; PAGE0 changing mid-execution while our SD driver code is running
; FROM slot 0 (page #0F) crashes the instant the interrupt returns;
; PAGE3 changing mid-LOAD512/SAVE512 silently swaps the transfer's
; real source/dest for whatever the music player last pointed it at.
; wrap_load512/wrap_save512's own di/ei (zc_sd_driver.asm) only
; protects against the CRASH by blocking ALL interrupts for the whole
; multi-block transfer -- but that STARVES pt_play for however long
; the transfer takes (can be several video frames for a large file),
; which is exactly why music glitches/goes to noise and the screen
; flickers when switching tracks during any SD activity: pt_play
; simply never gets to run its own per-frame update while we're mid-
; transfer, then resumes abruptly once di/ei lets it back in. Clearing
; music_sw here too (not just save_mode) means pt_play's own "call
; nz,pt_play" check skips it ENTIRELY during SD operations -- a clean
; pause instead of a starve-then-jump-back-in glitch -- confirmed as
; the real cause of "noise mixed with melody" persisting even after
; the SAVE512 addressing fix (which was a real, separate bug, but not
; the only one). See [[project-zifi-custom-sd-driver]].
; music_sw_saved: off_int_dma saves music_sw's real value here (which
; may already be 0 if the user paused music) before forcing it off, so
; on_int_dma can RESTORE that instead of blindly forcing it back to 1
; -- otherwise an SD operation happening while music is paused would
; incorrectly un-pause it.
music_sw_saved db 0
on_int_dma ld a,1 on_int_dma ld a,1
jr sw_int_dma ld (save_mode+1),a
off_int_dma xor a ld a,(music_sw_saved)
sw_int_dma ld (save_mode+1),a ld (music_sw+1),a
; ld (analizator_sw+1),a ret
; ld (mouse_sw+1),a off_int_dma ld a,(music_sw+1)
; jp wait_frame ld (music_sw_saved),a
xor a
ld (save_mode+1),a
ld (music_sw+1),a
ret ret
@@ -3968,7 +4007,11 @@ load_ini
CALL FENTRY CALL FENTRY
JP Z,ini_not_found JP Z,ini_not_found
LD C,download_page ; page ini LD C,download_page ; page ini
LD HL,#0000 LD HL,#4000 ; LOAD512 uses the SAME slot-1/direct-
; address convention as SAVE512 -- see
; zc_sd_driver_body.asm's core_load512
; comment for why this changed from
; #0000/PAGE3
LD B,#32 LD B,#32
CALL LOAD512 CALL LOAD512
CALL SETROOT; возвращаемся в коневой CALL SETROOT; возвращаемся в коневой
@@ -3976,8 +4019,8 @@ load_ini
parse_ini ld a,download_page parse_ini ld a,download_page
call set_page3 call set_page1
ld hl,#c000 ld hl,#4000
IF !cable_zifi IF !cable_zifi
ld de,ini_db ld de,ini_db
call cp_ini call cp_ini
@@ -4691,6 +4734,27 @@ full_len_high byte ;
read_threads thread;ds 6*5 read_threads thread;ds 6*5
db #ff db #ff
; modem_load_file's own private snapshot of load_ram_page, captured
; right before starting the actual network receive (zifi_get/psb_get)
; -- NOT the same as re-reading load_ram_page after the call returns.
; load_ram_page is a single shared global, written from several other
; places (catalog-link clicks, pagination, menu navigation -- see
; loadpage_after_link_list and the download_page writers). zifi_get can
; run for several seconds (WiFi connect/TLS handshake/HTTP wait, many
; wait_frame-yielded interrupt frames), during which the interrupt-
; driven UI can process a DIFFERENT click that overwrites load_ram_page
; before zifi_get returns. Re-reading load_ram_page at that point (the
; old code) stamps thread.page with whatever category was clicked LAST,
; not the one this download actually used. This is a real, still-latent
; hazard (kept as a defensive fix), but turned out NOT to be the cause
; of "every download saves as identical garbage" -- that bug was a
; stale IX left behind by MKFILE (see the wrap_* comment in
; zc_sd_driver.asm), which made save_64 read (ix+thread.page)/(ix+
; thread.adress) from the wrong memory entirely, regardless of what
; read_threads itself held. See [[project-zifi-custom-sd-driver]]
; (2026-09-09).
saved_thread_page db 0
init_zifi init_zifi
@@ -4722,17 +4786,80 @@ init_zifi
; call wait ; call wait
; call zifi_check_receive_command ; call zifi_check_receive_command
; ret ; ret
ld a,wifi_connect_retries_max
ld (wifi_connect_retries_left),a
; wifi_connect_retry: (re)send AT+CWJAP_CUR and wait for "OK" -- up to
; wifi_connect_quanta quanta (~256 frames/quantum, ~5s each) per
; attempt, up to wifi_connect_retries_max attempts total. Was an
; unconditional "jr nz,2b" with NO timeout at all -- a real ESP module
; that never answers (bad credentials, out of range, module wedged)
; hung here forever with zero feedback. See [[project-zifi-custom-sd-driver]].
wifi_connect_retry
ld hl,wifi_try_connect ld hl,wifi_try_connect
ld b,2 ld b,2
call zifi_echo call zifi_echo
ld hl,cmd_cwjap ; AT+CWJAP_CUR . Connect to AP, for current ld hl,cmd_cwjap ; AT+CWJAP_CUR . Connect to AP, for current
call zifi_send call zifi_send
2 call zifi_check_receive_command ld a,wifi_connect_quanta
ld (wifi_connect_quantum_left),a
wifi_connect_quantum
xor a
ld (wifi_connect_frame_left),a ; 0 wraps to 256 via dec below
wifi_connect_poll
; NOT "call zifi_check_receive_command" -- that goes through
; zifi_input_fifo_check, which BLOCKS INTERNALLY for up to
; 256 frames (its own "call wait_frame / dec e / jr nz,3b")
; whenever the FIFO is empty, only returning immediately
; when data IS already waiting. With nothing new arriving,
; nearly every call to it here would burn a full internal
; 256-frame wait BEFORE this loop's own frame counter even
; gets to react -- confirmed live (wifi_connect_frame_left
; stayed frozen for 6500+ frames: this loop's "iterations"
; were each secretly taking up to 256 frames on their own).
; It also drives zifi_input_fifo_check's OWN separate
; download-timeout/retry state (dl_timeout_left etc, meant
; for actual file downloads, not this WiFi-connect wait) --
; that's what caused the red border flashes. Doing our own
; quick, non-blocking FIFO peek here avoids both problems:
; only call the (bulk-read) fifo_inir when data is actually
; present, so it never falls into the internal wait either.
ld bc,zifi_input_fifo_status
in a,(c)
or a
jr z,wifi_connect_nodata
call fifo_inir
ld de,str_ok ld de,str_ok
call buffer_cmp call buffer_cmp
; cp low output_buff+#bf jr z,wifi_connect_ok
jr nz,2b wifi_connect_nodata
call wait_frame
ld hl,wifi_connect_frame_left
dec (hl)
jr nz,wifi_connect_poll
ld a,(wifi_connect_quantum_left)
dec a
ld (wifi_connect_quantum_left),a
jr nz,wifi_connect_quantum
; this attempt's quanta are exhausted -- retry if any left
ld a,(wifi_connect_retries_left)
dec a
ld (wifi_connect_retries_left),a
jr nz,wifi_connect_retry
; out of retries -- give up with a real message instead of
; hanging silently forever
ld hl,wifi_connect_failed_msg
ld b,1
call zifi_echo
jr $
wifi_connect_ok
ld hl,wifi_connected ld hl,wifi_connected
call zifi_echo call zifi_echo
ld a,(update_rtc_sw+1) ; "time:" in zifi.ini not "none"? ld a,(update_rtc_sw+1) ; "time:" in zifi.ini not "none"?
@@ -4741,6 +4868,13 @@ init_zifi
call rtc_read_date ; name downloads/<date> from the RTC chip either way call rtc_read_date ; name downloads/<date> from the RTC chip either way
call set_download_dir call set_download_dir
ret ret
wifi_connect_quanta equ 4 ; ~5s/quantum * 4 = ~20s per attempt
wifi_connect_retries_max equ 5
wifi_connect_frame_left db 0
wifi_connect_quantum_left db 0
wifi_connect_retries_left db 0
wifi_connect_failed_msg db #0d,#0a,'WiFi connect failed',0,0
; ld b,25 ; ld b,25
; call wait ; call wait
; ld hl,cmd_cipsta ; is Set IP address of station ? ; ld hl,cmd_cipsta ; is Set IP address of station ?