Drodzy magnetofoniarze, pamiętacie te pełne napięcia chwile z dzieciństwa, gdy chodziliście po domu na palcach, błagaliście domowników aby mówili szeptem, wszystko po to aby nie zakłócić spokojnego przebiegu ładowania Ulubionej Gry? Ile razy wasze starania spełzły na niczym, gdy w 15. minucie komputer niemiłym buczeniem oznajmiał "nic z tego"? Nadeszła chwila oczyszczenia! Za Wasze zszargane nerwy odpowiada nie szczekający pies, nie głośna muzyka z Pokoju Starszego Brata, lecz błąd w OS-ie.
Podczas pracy nad A8CAS notorycznie przydarzał mi się dziwny problem: podczas odczytu w emulatorze obrazu CAS kasety, raz na kilkanaście-kilkadziesiąt prób występował błąd odczytu. Poszukiwania spełzły na niczym, dopiero kol. FUJI przeanalizował sprawę dogłębnie i wywnioskował, że podczas odczytu w pewnej szczególnej sytuacji błędnie wykrywane jest baudrate rekordu. Rzut oka na źródła OS-u potwierdził obawy.
Główna procedura obliczania baudrate, SBR ($ED3D) oczekuje aż sygnał na DATA IN zmieni się z 1 na 0, czyli na 1. bit rekordu. W tym momencie aktualne wartości VCOUNT i RTCLOK+2 są zapisywane pod TIMER1 i TIMER1+1. Potem procedura odczekuje 10 zmian sygnału DATA IN i znowu zapisuje aktualne VCOUNT i RTCLOK+2, tym razem w akumulatorze i rejestrze Y.
SBR = $ED3D ;entry
SBR1 LDA BRKKEY
BNE SBR2 ;if BREAK key not pressed
JMP PBK ;process BREAK key, return
SBR2 SEI
LDA TIMFLG ;timeout flag
BNE SBR3 ;if no timeout
BEQ SBR5 ;process timeout
; Czekaj na bit startu (0)
SBR3 LDA SKSTAT ;???
AND #$10 ;extract start bit???
BNE SBR1 ;if start bit
STA SAVIO ;save serial data in
; Zapisz aktualne VCOUNT i RTCLOK+2 w TIMER1, TIMER1+1
LDX VCOUNT ;vertical line counter
LDY RTCLOK+2 ;low byte of VBLANK clock
STX TIMER1
STY TIMER1+1 ;save initial timer value
LDX #1
STX TEMP3 ;set mode flag???
LDY #10 ;10 bits
; Odczekaj 10 zmian sygnału
SBR4 LDA BRKKEY ;???
BEQ PBK ;if BREAK key pressed, process, return
LDA TIMFLG ;timeout flag
BNE SBR6 ;if no timeout
SBR5 CLI
JMP ITO ;indicate timeout, return
SBR6 LDA SKSTAT ;???
AND #$10 ;extract ???
CMP SAVIO ;previous serial data in
BEQ SBR4 ;if data in not changed
STA SAVIO ;save serial data in
DEY ;decrement bit counter
BNE SBR4 ;if not done
DEC TEMP3 ;decrement mode???
BMI SBR7 ;if done with both modes
; Po 10 zmianach sygnału, zapamiętaj aktualne VCOUNT i RTCLOK+2
LDA VCOUNT ;???
LDY RTCLOK+2 ;???
; Oblicz baudrate
JSR CBR ;compute baud rate
LDY #9 ;9 bits
; Zignoruj następne 9 zmian sygnału (rekord zaczyna się od 20 naprzemiennych
; bitów 0 i 1 (2 bajty $55), zczego tylko 10 pierwszych jest używanych do
; obliczania baudrate)
BNE SBR4 ;set bit counter
; Dalej nieistotne - zapis wyniku do AUDF3/4
Potem jest skok do CBR ($ECC8), gdzie obliczana jest ilość skanlinii które minęły pomiędzy ww. dwoma momentami (dokładniej, ilość skanlinii/2, w końcu to VCOUNT).
CONS1X DB 131 ;liczba skanlinii/2 w NTSC
DB 156 ;liczba skanlinii/2 w PAL
CBR = $ECC8 ;entry
; w A i Y znajdują się wartości odpowiednio VCOUNT i RTCLOK+2 po 10. zmianie sygnału
STA TIMER2 ;save final timer value
STY TIMER2+1
JSR AVV ;adjust VCOUNT value
STA TIMER2 ;save adjusted timer 2 value
LDA TIMER1
JSR AVV ;adjust VCOUNT value
STA TIMER1 ;save adjusted timer 1 value
LDA TIMER2
SEC
SBC TIMER1
STA TEMP1 ;save difference
LDA TIMER2+1
SEC
SBC TIMER1+1
TAY ;difference
LDX PALNTS ; 0 = NTSC, 1 = PAL
LDA #0
SEC
SBC CONS1X,X ;???
CBR1 CLC
ADC CONS1X,X ;accumulate product
DEY
BPL CBR1 ;if not done
; W tym momencie A zawiera ilość skanlinii/2, które minęły podczas 10. zmian sygnału DATA IN.
; Dalej nieistotne - robiona jest interpolacja i ustalane są wartości dla AUDF3/4 na postawie tablicy
CBR, w uproszczeniu, odejmuje pary liczb TIMER2, TIMER2+1 od TIMER1, TIMER1+1 aby uzyskać odpowiednią wartość. Przedtem jednak każda z par liczb jest lekko modyfikowana (skok do AVV ($ED2E)), żeby odejmowanie miało sens:
CONS2X DB 7 ;liczba skanlinii/2 od skanlinii 248. do 0. w NTSC
DB 32 ;liczba skanlinii/2 od skanlinii 248. do 0. w PAL
AVV = $ED2D ;entry
; W linii 248 (= $7C * 2) zachodzi przerwanie VBL, w którym RTCLOK+2 jest zwiększane o 1.
CMP #$7C
BMI AVV1 ;if A < $7C
SEC
SBC #$7C
RTS ;return
AVV1 CLC
LDX PALNTS
ADC CONS2X,X
RTS ;return
AVV bierze pod uwagę, że w 248 linii ekranu następuje zwiększenie RTCLOK+2 o 1.
Ogólnie wszystko działa dobrze, przy następującym założeniu: w VCOUNT wartość $7C pojawia się po wystąpieniu przerwania VBL (a tym samym po zwiększeniu RTCLOK+2). Niestety założenie to nie jest prawdą. VCOUNT jest zwiększane zawsze w 111 cyklu poprzedniej skanlinii (czyli VCOUNT przeskakuje na $7C w 111 cyklu skanlinii 247), natomiast CPU zaczyna obsługiwać VBL najwcześniej w 9. cyklu linii 248 (12 cykli później). Linia 248 jest daleko poniżej końca Display Listy, standardowo jest też wyłączone DMA dla PMG, więc całe 12 cykli jest tu dostępne dla CPU. Na każdą ramkę przypada zatem 12-cyklowy okres, podczas którego jest problem. Jeśli w okolicy tych dwunastu cykli zmieni się sygnał DATA IN, wykryte baudrate może być nieprawidłowe.
FUJI wykonał odpowiednie testy na prawdziwym sprzęcie (spreparowany programik i nagranie składające się z 10000 dwubajtowych rekordów $5555, mam nadzieję że napisze tu coś od siebie), i wyszło że prawdopodobieństwo błędu jest ok. 1/2000.
Czy ktoś już wcześniej natknął się na ten błąd i analizował sprawę? Nie znalazłem na necie nic w tym temacie.