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!