diff --git a/_sd/README.md b/_sd/README.md index 592ed24..80e507b 100644 --- a/_sd/README.md +++ b/_sd/README.md @@ -7,7 +7,9 @@ ## Файлы - `zc_sd_driver.asm` — резидентная часть: таблица переходов CORE, - di/ei-обёртки над каждой точкой входа, распаковщик тела. + сохранение IX в точках входа и распаковщик тела. Длинные рабочие операции + оставляют растровые прерывания включёнными; конфликтующие задачи ZiFi + блокируются снаружи через `init_sd_card`/`sd_exit`. - `zc_sd_driver_body.asm` — основная логика: SPI/SD-протокол, DMA, разбор FAT32 (BPB, цепочки кластеров, FAT), поиск по имени с LFN, создание/удаление файлов и каталогов. Хранится в сжатом виде и diff --git a/_sd/zc_sd_driver.asm b/_sd/zc_sd_driver.asm index 8926de6..bc0108a 100644 --- a/_sd/zc_sd_driver.asm +++ b/_sd/zc_sd_driver.asm @@ -216,59 +216,46 @@ wrap_save512 push ix call core_save512 pop ix ret -wrap_mkfile di - push ix +; Runtime filesystem calls are already bracketed by zifi.asm's +; init_sd_card/sd_exit. That disables every IRQ job which can repage CPU +; slots or touch DMA, while leaving the raster interrupts themselves alive. +; Keeping DI across FAT scans and card busy waits skipped the text-mode raster +; lines and produced full-height bands / flashes of the backing splash. +wrap_mkfile push ix call core_mkfile pop ix - ei ret -wrap_mkdir di - push ix +wrap_mkdir push ix call core_mkdir pop ix - ei ret -wrap_fentry di - push ix +wrap_fentry push ix call drv_fentry pop ix - ei ret -wrap_loadnon di - push ix +wrap_loadnon push ix call drv_loadnon pop ix - ei ret -wrap_setdir di - push ix +wrap_setdir push ix call core_setdir pop ix - ei ret -wrap_setroot di - push ix +wrap_setroot push ix call core_setroot pop ix - ei ret -wrap_seek0 di - push ix +wrap_seek0 push ix call stream_open pop ix - ei ret -wrap_delfl di - push ix +wrap_delfl push ix call core_delfl pop ix - ei ret -wrap_renam di - push ix +wrap_renam 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 diff --git a/_spg/ZIFI/zifi.spg b/_spg/ZIFI/zifi.spg index a543b7d..5b05450 100644 Binary files a/_spg/ZIFI/zifi.spg and b/_spg/ZIFI/zifi.spg differ diff --git a/includes.asm b/includes.asm index 96f278b..773daea 100644 --- a/includes.asm +++ b/includes.asm @@ -273,13 +273,19 @@ rfilp4 ld bc,0 ret ENDIF IFUSED set_ports -set_ports ld c,#AF +set_ports ld a,1 + ld (dma_busy),a + ld c,#AF .m1 ld b,(hl) inc hl inc b - jr z,dma_stats + jr z,.wait outi jr .m1 +.wait call dma_stats + xor a + ld (dma_busy),a + ret ENDIF IFUSED set_ports_nowait @@ -393,4 +399,4 @@ SIN_DAT DB #EC,#EE,#F0,#F2,#F4,#F6,#F7,#F9 DB #FA,#FB,#FC,#FD,#FE,#FE,#FF,#FF - ENDIF \ No newline at end of file + ENDIF diff --git a/zifi.asm b/zifi.asm index 67add63..d5447a4 100644 --- a/zifi.asm +++ b/zifi.asm @@ -372,28 +372,28 @@ zifi_get call wait_frame ld hl,0 ld (readed_len_low+1),hl ld hl,cmd_conn2site ; AT+CIPSTART - connect to site - call zifi_send + call zifi_send_raw ; response is polled below; do not enter fifo_inir's hidden wait ld de,str_error call buffer_cmp jr nz,zg_ok ld hl,cmd_cipclose - call zifi_send + call zifi_send_raw 1 ld de,str_ok call buffer_cmp jr z,zifi_get + call poll_input_fifo call wait_frame - call fifo_inir jr 1b -zg_ok ld b,15 ; TLS handshakes (SSL connect) can take longer than one poll +zg_ok ld b,250 ; up to ~5s for a TLS connect reply, without a hidden FIFO wait zg_ok_wait ld de,str_ok - call buffer_cmp + call buffer_cmp jr z,zg_ok_done push bc + call poll_input_fifo call wait_frame - call fifo_inir pop bc djnz zg_ok_wait jr zifi_get ; still no OK - start the whole connection attempt over @@ -401,10 +401,12 @@ zg_ok_done 1 ld hl,cmd_cipsend ; AT+CIPSEND=, call zifi_send_raw -2 call fifo_inir +2 call poll_input_fifo ld de,str_ok call buffer_cmp - jr nz,2b + jr z,1f + call wait_frame + jr 2b 1 ld a,(hl) inc hl cp #ff @@ -713,6 +715,19 @@ read_pack_proc cp 0xbf ; push af ret +; Read pending ESP bytes only when FIFO already contains data. fifo_inir +; otherwise calls zifi_input_fifo_check, which may wait 256 frames and used to +; let the start-up splash show through while an NTP server was silent. +; BC is preserved because callers can use it as a retry counter. +poll_input_fifo push bc + ld bc,zifi_input_fifo_status + in a,(c) + or a + jr z,.done + call fifo_inir +.done pop bc + ret + fifo_inir push de call zifi_input_fifo_check ld hl,output_buff ; read data from fifo, count: #bf @@ -2274,6 +2289,12 @@ frame ld a,0 save_mode ld a,1 or a jr z,int_ex_regs + ; Foreground screen updates and the SD driver share the single TS-Conf + ; DMA register set with mouse sprite uploads. Never interrupt a DMA + ; programming sequence with another one. + ld a,(dma_busy) + or a + jr nz,int_ex_regs mouse_sw ld a,1 or a call nz,mouse_proc @@ -2578,6 +2599,7 @@ ma2 ld (hl),a jp restore_page3 ay_volume ds 3 analyzer_active db 0 +dma_busy db 0 @@ -3434,7 +3456,12 @@ all_init di ld hl,splash_copy jp set_ports -gfx_init ld hl,cursor_copy +gfx_init ; The splash is useful only during the initial delay. Clear its + ; backing 256-colour pages before installing the normal UI, so a late + ; or missed text raster can reveal only black, never the old banner. + ld hl,clr_gfx256_screen + call set_ports + ld hl,cursor_copy call set_ports ld hl,menu_copy call set_ports @@ -4244,6 +4271,7 @@ cmd_sntp_time db "AT+CIPSNTPTIME?",13,10,0 str_sntp_time db "+CIPSNTPTIME:",0 str_epoch_year db "1970" sntp_outer_left db 0 +sntp_reply_left db 0 month_names db "JanFebMarAprMayJunJulAugSepOctNovDec" ; Five bounded attempts. On final failure CF=1; caller deliberately falls @@ -4260,13 +4288,17 @@ ntp_sync_rtc ld b,5 ntp_sync_rtc_once call clear_input_fifo ; drop stale bytes before reading the reply ld hl,cmd_sntp_cfg - call zifi_send - ld b,15 ; bounded wait for CIPSNTPCFG's own OK + call zifi_send_raw ; the bounded loop below owns reception + ld a,50 ; one-second, frame-based timeout for CIPSNTPCFG's OK + ld (sntp_reply_left),a sntp_cfg_wait ld de,str_ok call buffer_cmp jr z,sntp_cfg_ok - call fifo_inir - djnz sntp_cfg_wait + call poll_input_fifo ; never call the blocking FIFO waiter while NTP is silent + call wait_frame + ld hl,sntp_reply_left + dec (hl) + jr nz,sntp_cfg_wait scf ret ; module didn't even accept the config - give up sntp_cfg_ok @@ -4275,13 +4307,17 @@ sntp_cfg_ok sntp_query call clear_input_fifo ; give the background sync a bit of real time each round ld hl,cmd_sntp_time - call zifi_send - ld b,15 ; bounded wait for THIS query's reply to arrive + call zifi_send_raw + ld a,50 ; one-second, frame-based timeout for THIS reply + ld (sntp_reply_left),a sntp_query_wait ld de,str_sntp_time call buffer_cmp jr z,sntp_have_line - call fifo_inir - djnz sntp_query_wait + call poll_input_fifo + call wait_frame + ld hl,sntp_reply_left + dec (hl) + jr nz,sntp_query_wait scf ret ; module never answered the query at all - give up @@ -4309,47 +4345,37 @@ sntp_no_sync scf ret ; this attempt exhausted its bounded epoch wait sntp_real_time pop hl ; hl -> string start, this is a real time now - - di ; keep CMOS unlock/select/write atomic vs the raster ISR - ld bc,#eff7 ; #EFF7 is a SHARED system register (video mode/turbo/ - in a,(c) ; cache/memlock bits, not just the CMOS-enable bit) - - or #80 ; must read-modify-write it, not stomp it with a literal value - out (c),a + push hl + call sntp_validate_reply + pop hl + jr nc,.valid + scf ; malformed reply: leave RTC untouched, let outer 5-attempt loop retry + ret +.valid ; register b - ld a,#0b - ld b,#df - out (c),a - ld a,#82 - ld b,#bf - out (c),a + ld d,#0b + ld e,#82 + call rtc_write_reg ; day of week (3-letter name, offset 0..2), 0=Sunday push hl push hl pop ix - push bc call day_of_week_index ; -> a = 0-6, 0=Sunday - pop bc pop hl - push af - ld a,#06 - ld b,#df - out (c),a - ld b,#bf - pop af - out (c),a + ld d,#06 + ld e,a + call rtc_write_reg ; year (last 2 of the 4 digits, offset 20..23) push hl ld de,22 add hl,de - ld a,#09 - ld b,#df - out (c),a - ld b,#bf call code_time_rtc - out (c),a + ld d,#09 + ld e,a + call rtc_write_reg pop hl ; month (3-letter name, offset 4..6) @@ -4358,39 +4384,27 @@ sntp_real_time pop hl ; hl -> string start, this is a real time now add hl,de push hl pop ix - push bc call month_to_bcd ; -> a = BCD month - pop bc pop hl - push af - ld a,#08 - ld b,#df - out (c),a - ld b,#bf - pop af - out (c),a + ld d,#08 + ld e,a + call rtc_write_reg ; date of the month (offset 8..9, space-padded for single digits - and0 ; code_time_rtc's "and #0f" happens to treat a leading space as digit 0) push hl ld de,8 add hl,de - ld a,#07 - ld b,#df - out (c),a - ld b,#bf call code_time_rtc - out (c),a + ld d,#07 + ld e,a + call rtc_write_reg pop hl ; hours (offset 11..12), adjusted by the configured GMT offset push hl ld de,11 add hl,de - ld a,#04 - ld b,#df - out (c),a - ld b,#bf ld a,(hl) and #0f @@ -4431,46 +4445,35 @@ gmt_time add 0 rl e rl e or e - out (c),a + ld d,#04 + ld e,a + call rtc_write_reg pop hl ; minutes (offset 14..15) push hl ld de,14 add hl,de - ld a,#02 - ld b,#df - out (c),a - ld b,#bf call code_time_rtc - out (c),a + ld d,#02 + ld e,a + call rtc_write_reg pop hl ; seconds (offset 17..18) push hl ld de,17 add hl,de - ld a,#00 - ld b,#df - out (c),a - ld b,#bf call code_time_rtc - out (c),a + ld d,#00 + ld e,a + call rtc_write_reg pop hl ; register b - ld a,#0b - ld b,#df - out (c),a - ld a,#02 - ld b,#bf - out (c),a - - ld bc,#eff7 - in a,(c) - and #7f - out (c),a - ei + ld d,#0b + ld e,#02 + call rtc_write_reg ld hl,ntp_synced_msg ld b,1 @@ -4478,8 +4481,130 @@ gmt_time add 0 xor a ; NC = NTP date was written to RTC ret +; Validate the complete ESP-AT asctime() payload before changing the RTC. +; This rejects partial/garbled FIFO replies such as a year "20F0". A bad +; reply is retryable; it must never turn into a believable but wrong folder. +; in: HL -> "Www Mmm dd hh:mm:ss yyyy"; out: NC = valid, C = invalid. +sntp_validate_reply + push hl + + ; Exact day-of-week and month names. + push hl + pop ix + call day_of_week_index + jr c,.bad + pop hl + push hl + ld de,4 + add hl,de + push hl + pop ix + call month_to_bcd + jr c,.bad + pop hl + + ; Day: a space is allowed only for 1..9, otherwise it is 01..31. + push hl + ld de,8 + add hl,de + call sntp_day_bcd + jr c,.bad + cp #01 + jr c,.bad + cp #32 + jr nc,.bad + pop hl + + ; Hours/minutes/seconds must be strict decimal pairs in their ranges. + push hl + ld de,11 + add hl,de + call sntp_bcd2 + jr c,.bad + cp #24 + jr nc,.bad + pop hl + push hl + ld de,14 + add hl,de + call sntp_bcd2 + jr c,.bad + cp #60 + jr nc,.bad + pop hl + push hl + ld de,17 + add hl,de + call sntp_bcd2 + jr c,.bad + cp #60 + jr nc,.bad + pop hl + + ; RTC holds a two-digit year, but accept only a real 20xx date (2020-2099). + push hl + ld de,20 + add hl,de + ld a,(hl) + cp "2" + jr nz,.bad + inc hl + ld a,(hl) + cp "0" + jr nz,.bad + inc hl + call sntp_bcd2 + jr c,.bad + cp #20 + jr c,.bad + pop hl + or a + ret +.bad pop hl + scf + ret + +; Strict decimal pair at HL -> BCD. HL is left after the two characters. +sntp_bcd2 ld a,(hl) + sub "0" + jr c,.bad + cp 10 + jr nc,.bad + rlca + rlca + rlca + rlca + ld b,a + inc hl + ld a,(hl) + sub "0" + jr c,.bad + cp 10 + jr nc,.bad + or b + or a + ret +.bad scf + ret + +; Day accepts the ESP's space-padded single-digit form (" 5") too. +sntp_day_bcd ld a,(hl) + cp " " + jr nz,sntp_bcd2 + inc hl + ld a,(hl) + sub "1" + jr c,.bad + cp 9 + jr nc,.bad + inc a + or a + ret +.bad scf + ret + ; in: ix -> 3-letter month name (e.g. from CIPSNTPTIME's reply) -; out: a = BCD month 01-12 (clobbers af,bc,de,hl); defaults to January if not found +; out: a = BCD month 01-12; C set if the name is not an English month. month_to_bcd ld hl,month_names ld c,1 mtb_loop ld a,(ix+0) @@ -4502,16 +4627,18 @@ mtb_next ld de,3 jr z,mtb_notfound inc c jr mtb_loop -mtb_notfound ld c,1 +mtb_notfound scf + ret mtb_found ld a,c cp 10 jr c,mtb_done sub 10 add a,#10 -mtb_done ret +mtb_done or a + ret ; in: ix -> 3-letter day-of-week name (e.g. from CIPSNTPTIME's reply) -; out: a = 0-6, 0=Sunday (clobbers af,bc,de,hl); defaults to Sunday if not found +; out: a = 0-6, 0=Sunday (clobbers af,bc,de,hl); C set if not found day_of_week_index ld hl,dow_names ld c,0 @@ -4535,25 +4662,65 @@ dow_next ld de,3 jr z,dow_notfound inc c jr dow_loop -dow_notfound ld c,0 ; ran out of names - default to Sunday +dow_notfound scf + ret dow_found ld a,c + or a ret dow_names db "SunMonTueWedThuFriSat" ntp_synced_msg db "Time synced via NTP.",0,0 +; One RTC register per atomic section. The old single DI around the whole +; NTP conversion suppressed the raster chain and exposed splash_copy. +; D = CMOS register, E = value for write; clobbers AF/BC/DE. +rtc_write_reg di + ld bc,#eff7 + in a,(c) + or #80 + out (c),a + ld a,d + ld b,#df + out (c),a + ld a,e + ld b,#bf + out (c),a + ld bc,#eff7 + in a,(c) + and #7f + out (c),a + ei + ret + +; A = CMOS register, returns A = value; preserves DE, clobbers BC. +rtc_read_reg push de + di + ld e,a + ld bc,#eff7 + in a,(c) + or #80 + out (c),a + ld a,e + ld b,#df + out (c),a + ld b,#bf + in a,(c) + ld e,a + ld bc,#eff7 + in a,(c) + and #7f + out (c),a + ld a,e + pop de + ei + ret + ; Reads the current date straight back off the RTC chip into DIR_date, as ; "20YY_MM_DD" - used to name the downloads/ folder. Works regardless of ; whether "time:" is "none" or the NTP sync above failed: the chip keeps ; ticking on its own battery either way, so this is the one source of truth. rtc_read_date - di - ld bc,#eff7 - in a,(c) - or #80 - out (c),a - ld de,DIR_date+1 ld a,"2" ld (de),a @@ -4563,37 +4730,22 @@ rtc_read_date inc de ; year ld a,#09 - ld b,#df - out (c),a - ld b,#bf - in a,(c) + call rtc_read_reg call bcd_to_ascii ld a,"_" ld (de),a inc de ; month ld a,#08 - ld b,#df - out (c),a - ld b,#bf - in a,(c) + call rtc_read_reg call bcd_to_ascii ld a,"_" ld (de),a inc de ; date of the month ld a,#07 - ld b,#df - out (c),a - ld b,#bf - in a,(c) + call rtc_read_reg call bcd_to_ascii - - ld bc,#eff7 - in a,(c) - and #7f - out (c),a - ei ret bcd_to_ascii ; in: a = BCD byte, de = destination; writes 2 ASCII digits, advances de