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.