Riparazione di un TAP: un caso pratico

fab 26-07-2018.
Categoria: Programmazione

A volte può capitare, durante l'opera di preservazione delle cassette, di incontrare problemi relativi al trasferimento dei programmi sui moderni computer. Questo avviene principalmente a causa del degrado della superficie magnetica sulla quale sono immagazzinati i dati. 
Tuttavia, alcuni dati contenuti all'interno dei file TAP possono essere in comune con altre cassette che utilizzano lo stesso loader (poiché appartenenti alla stessa casa editrice). Nel presente articolo esamineremo dal punto di vista tecnico il caso del TAP di Epic 3000 numero 6.


Ready64 mi ha fornito diversi tentativi di dump della cassetta: dei tre game contenuti, consto che 2 caricano bene.

Per prima cosa identifico il loader, grazie al monitor del VICE: si tratta del Turbo 220, comune nelle cassette della Fermont, e di altri giochi da edicola. Turbo 220 non dispone di checksum per verificare la correttezza del caricamento, e ciò non facilita il compito. Come nella maggior parte dei caricatori turbo, anche nel Turbo 220 un'onda corrisponde a un bit: in questo caso, un bit 0 è un'onda di valore attorno a $1A e un bit 1 è un'onda di valore attorno a $2A (l'unità di misura è il byte in un file TAP, cioè 1/123156 di secondo).

Quindi passo i dump a WAV-PRG (versione a linea di comando), che è in grado di identificare le onde che non sono plausibilmente né bit 0 né bit 1. Col primo tentativo di dump ottengo il seguente risultato:

trying to get a sync using loader Turbo 220
got a pilot tone and a block at 51715
name  start 2049 end 21312
Program ends at 197819, 18263 bytes long, broken
trying to get a sync using loader Turbo 220
got a pilot tone and a block at 257519
name  start 2049 end 42856
Program ends at 395487, 17246 bytes long, broken
trying to get a sync using loader Turbo 220
got a pilot tone and a block at 635648
name  start 2049 end 38504
Program ends at 927288, 36455 bytes long, loader does not have checksum, no errors detected
trying to get a sync using loader Turbo 220
got a pilot tone and a block at 979019
name  start 2049 end 44904
Program ends at 1321859, 42855 bytes long, loader does not have checksum, no errors detected
trying to get a sync using loader Turbo 220

Rileviamo la presenza di 4 programmi complessivamente: l'introduzione e i 3 giochi. Come annunciato da Ready64, il primo dei giochi non funziona mentre gli altri 2 sì. Però neanche l'introduzione funziona.

Provo il secondo tentativo e, con delusione, mi accorgo che ha problemi esattamente negli stessi punti. Evidentemente la cassetta di origine era danneggiata in quei punti in maniera seria. Quindi non è possibile riparare il primo usando il secondo. Il terzo e il quarto sono messi anche peggio. Quindi decido di insistere usando solo il primo.

Apro il TAP con un hex editor e, per prima cosa, vado al byte 197819: WAV-PRG ha trovato un problema nelle onde immediatamente successive a quella.

0197808 27 27 1a 1c 1a 1a 27 1c 1a 1a 2a 41 5a 2d 8f 11
0197824 43 2a 65 46 57 27 35 1a 1a 17 27 1a 27 27 1a 1a
0197840 27 1a 1a 27 1c 1a 1a 1a 25 27 27 27 27 27 1a 27
0197856 27 1a 27 1a 27 1a 27 1c 1a 1a 1a 27 1c 1a 1a 1c

Una sequenza di 12 onde problematiche, quasi tutte più lunghe del dovuto, la prima vale $41 e l'ultima vale $35. Il TAP contiene troppo pochi byte, visto che la somma dei valori dei byte è correlata alla lunghezza totale del nastro registrato (in un TAP, il valore di un byte rappresenta la durata di un'onda), e quella non cambia che la cassetta sia danneggiata o no.
Come fare a sapere quanti byte mancano? Un modo è presupporre che in tutti i programmi il numero di onde dopo la fine del programma sia costante: in fondo il formato è lo stesso. Vediamo che succede in un programma funzionante, ad esempio il terzo (cioè il secondo gioco). Il gioco finisce di caricare al byte 927288.

0927280 1a 1a 1a 1a 1c 1a 1a 27 1a 1a 1a 1c 1a 1a 1c 1a
0927296 00 c3 f6 01 00 15 60 00 00 ff ff ff 00 ff ff ff

I byte dal 927289 al 297295 sono ancora validi: sono esattamente 8. Presupponiamo che lo stesso valga per i giochi non funzionanti.
L'introduzione, nella memoria del C64, occupa 21312-2049 byte, cioè 19263 byte. I bit sono quella cifra moltiplicata per 8, quindi 154104. Un bit è un'onda, e, in un file TAP, un'onda è un byte. Il programma inizia al byte 51715, dunque dovrebbe finire al byte 51715+154104=205819. Sommiamo le 8 onde che dovrebbero essere presenti subito dopo, e otteniamo 205827.

0205792 25 1c 1a 1a 1a 1c 1a 1a 1a 1a 1c 1a 1a 1a 1c 1a
0205808 1a 1a 1c 1a 00 c3 f6 01 00 8d 75 00 00 40 cf 58

Il byte 205812 è il primo che non rappresenta un'onda valida: 15 byte troppo presto. Come primo tentativo di riparazione, sostituiamo, sempre con un hex editor, le 12 onde anomale con 27 onde valide, e proviamo ad usare tutti bit 0. Passando questo TAP a WAV-PRG, non vengono riscontrati errori, perché le onde anomale sono state sostituite da onde valide, e il formato non comprende la checksum, quindi non è in grado di accorgersi dell'errore.

Proviamo a caricare il TAP così modificato in VICE, e analizziamo la memoria. Il primo byte danneggiato, nella memoria del C64, è 18263 byte dopo quello iniziale, che è 2049, quindi 20312. Disassembliamo col monitor del VICE la memoria prima e dopo:

(C:$4f86) d +20262 +20336
.C:4f26  A5 64       LDA $64
.C:4f28  C9 06       CMP #$06
.C:4f2a  F0 03       BEQ $4F2F
.C:4f2c  4C 48 B2    JMP $B248
.C:4f2f  A5 65       LDA $65
.C:4f31  85 22       STA $22
.C:4f33  A5 66       LDA $66
.C:4f35  85 23       STA $23
.C:4f37  20 E7 A9    JSR $A9E7
.C:4f3a  4C 26 09    JMP $0926
.C:4f3d  A2 06       LDX #$06
.C:4f3f  BD 5A 26    LDA $265A,X
.C:4f42  95 60       STA $60,X
.C:4f44  CA          DEX
.C:4f45  D0 F8       BNE $4F3F
.C:4f47  86 10       STX $10
.C:4f49  4C 26 09    JMP $0926
.C:4f4c  A2 06       LDX #$06
.C:4f4e  BD 5A 26    LDA $265A,X
.C:4f51  95 68       STA $68,X
.C:4f53  CA          DEX
.C:4f54  D0 F8       BNE $4F4E
.C:4f56  86 11       STX $11
.C:4f58  00          BRK
.C:4f59  00          BRK
.C:4f5a  00          BRK
.C:4f5b  02          JAM
.C:4f5c  C9 0F       CMP #$0F
.C:4f5e  DA          NOOP
.C:4f5f  A1 00       LDA ($00,X)
.C:4f61  BA          TSX
.C:4f62  8A          TXA
.C:4f63  18          CLC
.C:4f64  69 05       ADC #$05
.C:4f66  A8          TAY
.C:4f67  A2 00       LDX #$00
.C:4f69  B9 00 01    LDA $0100,Y
.C:4f6c  F0 49       BEQ $4FB7
.C:4f6e  10 3E       BPL $4FAE
.C:4f70  29 F0       AND #$F0

Si nota che questa parte di memoria contiene codice Assembly. Si notano i bit 0 aggiunti, a partire dal byte 20312 (in esadecimale $4f58). Ma si nota anche che, dopo gli zeri aggiunti e pochi altri byte, il codice Assembly appare di nuovo valido, e ciò rassicura sul fatto che la quantità di onde aggiunte è giusta.
Una cosa che si nota è la frequenza dell'istruzione JMP $0926. Sembra la struttura di un programma creato con un compilatore BASIC, dove ci sono alcune istruzioni Assembly per ogni opcode, ed, eseguito l'opcode, si ritorna al loop principale, che potrebbe essere proprio a $0926. L'introduzione è quella classica delle cassette Fermont, con una schermata grafica e un selettore di giochi. Una supposizione è che le introduzioni delle cassette Fermont dello stesso periodo siano simili, e in particolare usino lo stesso compilatore. Proviamo a caricarne una da una cassetta funzionante dello stesso periodo (POKE n.9), e disassemblare la stessa area di memoria. Eccone il codice:

(C:$03f5) d +20262 +20336
.C:4f26  A5 64       LDA $64
.C:4f28  C9 06       CMP #$06
.C:4f2a  F0 03       BEQ $4F2F
.C:4f2c  4C 48 B2    JMP $B248
.C:4f2f  A5 65       LDA $65
.C:4f31  85 22       STA $22
.C:4f33  A5 66       LDA $66
.C:4f35  85 23       STA $23
.C:4f37  20 E7 A9    JSR $A9E7
.C:4f3a  4C 26 09    JMP $0926
.C:4f3d  A2 06       LDX #$06
.C:4f3f  BD 5A 26    LDA $265A,X
.C:4f42  95 60       STA $60,X
.C:4f44  CA          DEX
.C:4f45  D0 F8       BNE $4F3F
.C:4f47  86 10       STX $10
.C:4f49  4C 26 09    JMP $0926
.C:4f4c  A2 06       LDX #$06
.C:4f4e  BD 5A 26    LDA $265A,X
.C:4f51  95 68       STA $68,X
.C:4f53  CA          DEX
.C:4f54  D0 F8       BNE $4F4E
.C:4f56  86 11       STX $11
.C:4f58  4C 26 09    JMP $0926
.C:4f5b  82 C9       NOOP #$C9
.C:4f5d  0F DA A1    SLO $A1DA
.C:4f60  00          BRK
.C:4f61  BA          TSX
.C:4f62  8A          TXA
.C:4f63  18          CLC
.C:4f64  69 05       ADC #$05
.C:4f66  A8          TAY
.C:4f67  A2 00       LDX #$00
.C:4f69  B9 00 01    LDA $0100,Y
.C:4f6c  F0 49       BEQ $4FB7
.C:4f6e  10 3E       BPL $4FAE
.C:4f70  29 F0       AND #$F0

Quasi identica, ma la sequenza di bit 0 che era stata aggiunta a mano nell'altro TAP qui è sostituita da codice Assembly valido, contenente tra l'altro proprio l'istruzione JMP $0926. La riparazione sarà completa quando i bit 0 aggiunti a mano saranno sostituiti dalla sequenza di bit presa da questa cassetta. Per comodità la sequenza di byte 00 00 00 02 verrà sostituita da 4C 26 09 82.

Passiamo al gioco successivo. Era danneggiato a partire dalla posizione 395487 (che, a causa dei 15 byte aggiunti in precedenza, diventa 395502). Vediamo che c'è lì:

0395488 27 1a 27 27 1a 27 1c 1a 1a 27 1c 1a 14 2a 27 2a
0395504 41 33 1a 1a 1c 1a 1a 1a 27 1a 1a 27 1c 1a 27 1a

Solo 2 onde anomale, bene. La prima sembra la somma di un bit 0 e un bit 1 ($41 può essere plausibilmente la somma di $27 e $1a, che sono un 1 e uno 0 validi) e la seconda la somma di due bit 0 ($1a+$19). L'unico problema è sapere se sostituire il primo con una sequenza 01 o 10, il secondo verrà sostituito da 00. Proviamo con la sequenza 01.

Eseguendo WAV-PRG sul risultato, viene individuato un altro problema:

Program ends at 462526, 25624 bytes long, broken

A 462526 c'è una sequenza di onde anomale piuttosto lunga. Applicando lo stesso metodo come per il programma precedente, per vedere quante onde vanno aggiunte, risulta che ne servono 49! Una porzione di nastro rovinato della cassetta piuttosto lungo. Sostituiamo le onde anomale con bit 0, e aggiungiamo 49 bit 0. Eseguendo nuovamente WAV-PRG, non rileviamo altri errori. A questo punto vediamo che succede se proviamo a carica questo programma con VICE, disassembliamo l'area di memoria dove si era verificato il primo errore:

.C:4b5d  AD 11 D0    LDA $D011
.C:4b60  09 20       ORA #$20
.C:4b62  8D 11 D0    STA $D011

Ottimo, il risultato è decisamente plausibile! Eseguendo la stessa procedura nella seconda area, non si trova codice Assembly né testo ASCII identificabile. Si tratta con tutta probabilità di dati grafici, la cui corruzione può portare al massimo a delle schermate grafiche danneggiate.

.C:6c10  AC B6 F6    LDY $F6B6
.C:6c13  06 2B       ASL $2B
.C:6c15  6B 06       ARR #$06
.C:6c17  B3 06       LAX ($06),Y
.C:6c19  BC 00 00    LDY $0000,X
.C:6c1c  00          BRK
.C:6c1d  00          BRK
.C:6c1e  00          BRK
.C:6c1f  00          BRK
.C:6c20  00          BRK
.C:6c21  05 94       ORA $94
.C:6c23  B2          JAM
.C:6c24  0B 03       ANC #$03
.C:6c26  94 01       STY $01,X

Invece, più avanti, del codice valido rassicura sul fatto che è stato aggiunto il numero giusto di onde.

.C:863a  A2 00       LDX #$00
.C:863c  BD 7C 86    LDA $867C,X
.C:863f  9D 10 01    STA $0110,X
.C:8642  E8          INX
.C:8643  D0 F7       BNE $863C

Il suddetto codice valido viene regolarmente eseguito. Analizzandolo, si scopre che la maggior parte di ciò che viene caricato da cassetta è compresso col metodo "run-length encoding" (in pratica, se si vuole caricare lo stesso valore in N locazioni di memoria consecutive, si usa un valore speciale di segnalazione, preceduto dal valore desiderato e dal numero N: esempio, $00 $14 $94 significa "metti il valore 0 in 14 locazioni consecutive", dove $94 è il valore speciale scelto dagli autori). La decompressione avviene dall'ultimo byte al primo, perciò, in certi casi, i byte precedenti quelli riparati in precedenza potrebbero uscire corrotti dal procedimento di decompressione. Ma tutto va bene, perché, dopo la decompressione, si nota del codice BASIC valido all'inizio della memoria BASIC:

(C:$033a) m 801
>C:0801  32 08 01 00  97 34 35 2c  31 38 32 3a  97 34 37 2c   2....45,182:.47,
>C:0811  31 38 32 3a  97 34 39 2c  31 38 32 3a  97 34 36 2c   182:.49,182:.46,
>C:0821  35 35 3a 97  34 38 2c 35  35 3a 97 35  30 2c 35 35   55:.48,55:.50,55
>C:0831  00 63 08 02  00 97 35 31  2c 31 36 36  3a 97 35 33   .c....51,166:.53
>C:0841  2c 31 36 36  3a 97 35 35  2c 31 36 36  3a 97 35 32   ,166:.55,166:.52
>C:0851  2c 36 33 3a  97 35 34 2c  36 33 3a 97  35 36 2c 36   ,63:.54,63:.56,6
>C:0861  33 00 79 08  03 00 97 35  33 32 38 31  2c 30 3a 97   3.y....53281,0:.
>C:0871  35 33 32 38  30 2c 30 00  bb 08 04 00  9c 3a 43 24   53280,0......:C$
>C:0881  b2 22 53 43  45 52 49 46  46 4f 22 3a  99 22 93 9e   ."SCERIFFO":."..

Fatto! Il gioco parte. Effettivamente, giocando un po', si incontrano delle schermate corrotte. Mando a Ready64 la versione così riparata, e mi danno un consiglio prezioso: scaricare la versione del gioco in formato T64, presente su Ready64. Quella versione consentirebbe un'astuta comparazione dei dati, per poter vedere come dovrebbe apparire la memoria, in particolare nella parte dove in precedenza erano stati aggiunti bit 0. Confrontando il valori alla locazione 25624+2049, cioè 27673, si vedono valori diversi per una decina di byte, e poi gli stessi.

A questo punto è bastato ricostruire questi 10 byte nel file TAP al posto dei bit 0 scritti in precedenza... et voilà, il gioco è fatto!

Commenti
Commenta gioco Ci sono 0 commenti per questo articolo. Registrati se vuoi lasciarne uno.
Commodore 64
Pannello Utente
243 Visitatori, 1 Utente
cepsanta
Ciao, ospite!
(Login | Registrati)
Merchandising
Ultimo Commento
Clicca per leggere tutti i commenti
Cool Croc Twins, the
Avete avuto la possibilità di leggere gli ultimi numeri di Zzap! Italia? Dal 1992 fino alla fine delle pubblicazioni è un declino inesorabile alle nuove recensioni, se ne potrebbero contare su una singola mano, un abbandono precoce che ...
leggi »
Phobos
Articolo
Intervista a Luciano Merighi (Merifon)
Ottimo articolo… solo una nota all’Autore dell’articolo… tra i programmatori bolognesi “pionieri” c’ero anche io: Andrea Paselli! Con Luca Zarri e Marco Corazza ho realizzato Chuck Rock, Over The Net, Mystere, Halley Adventure… ...
Andrea Paselli
Superlink