Ciao ragazzi, visto l’interesse nel convertire i giochi su disco in cartucce per GX4000, ho pensato di mettere insieme una breve (beh, nella mia testa era partita come breve) guida/FAQ con tutti i dettagli sui metodi che utilizzo. Premessa: me ne occupo solo da poco tempo e sto imparando strada facendo, quindi per favore non prendete quello che dico come oro colato, non sono assolutamente un esperto dell’hardware Amstrad. Tuttavia, mi piace davvero molto imparare a conoscere questa vecchia macchina e mettermi alla prova: può essere un hobby davvero soddisfacente e che crea dipendenza.
Quando ho deciso di fare il mio primo tentativo, ho letto l’ottimo articolo della fanzine CPC qui: https://www.cpcwiki.eu/imgs/6/6f/CPC_Fanzine_-_GX4000_conversion.pdf
Quell’articolo contiene un sacco di informazioni molto utili che mi hanno permesso di iniziare, e sono stato molto fortunato perché il primo gioco che ho scelto di convertire, Dynamite Dan, era adatto alla conversione con i metodi descritti. Purtroppo ci sono alcune situazioni che l’articolo non menziona e, di fatto, la maggior parte dei giochi che ho convertito da allora non sarebbe convertibile usando i metodi riportati lì.
L’articolo descrive come usare un editor esadecimale per modificare direttamente un file su disco, in modo da apportare modifiche a livello di byte al codice del gioco. Questo funziona solo nelle situazioni in cui il programma non è né compresso né crittografato. Per la maggior parte dei giochi che cercherete di convertire, il file su disco da cui partirete sarà «crackato», e i cracker adorano crittografare e (soprattutto) comprimere i loro lavori. Sebbene con forme di crittografia rudimentali (spesso basate su semplici algoritmi XOR) si possa andare a ritroso per capire quali dovrebbero essere i byte su disco (l’ho fatto con Shogun), è un lavoro lunghissimo che richiede un’intensa concentrazione e il tracciamento continuo di appunti. Non è una strada che raccomando.
Perché spesso è necessario partire da una versione crackata piuttosto che da una commerciale? In molti casi, i giochi su disco caricano i dati dal disco a un certo punto, anche se si tratta solo di una schermata di caricamento. Molti giochi più grandi caricano ciascun livello singolarmente. Spesso i giochi commerciali accedevano direttamente al controller del floppy disk anziché passare tramite la ROM AMSDOS. Non appena un gioco fa questo, non può essere convertito con nocart, quindi non girerà sul GX4000. I cracker spesso prendevano i giochi commerciali e sostituivano l’accesso diretto al floppy con chiamate al firmware AMSDOS, poiché ciò consentiva di bypassare la protezione dalla copia, ecc. Quindi le versioni crackate spesso funzionano con nocart così come sono.
Ci sono due modi affidabili per verificare se il file .dsk in vostro possesso usa il firmware o meno. Primo: caricatelo in WinAPE e andate nel menu «edit disk». Cliccate su «show system files». Se vedete più file, ci sono buone probabilità che funzioni con nocart così com’è, poiché usa i file standard AMSDOS con header standard. Per conferma, potete eseguire il gioco in WinAPE e impostare un breakpoint sul controller del floppy disk (FDC) tramite Debug → Breakpoints → Input/Output → Add → FDC Data. Quando il debugger si interrompe mentre il gioco tenta di caricare i dati, date un’occhiata in basso nel debugger e verificate se la casella di controllo «Upper ROM» è spuntata. Se lo è, è un’ottima cosa perché significa che viene usata l’AMSDOS.
Devo precisare che non ho provato ogni singolo emulatore esistente, ma tra quelli che ho provato WinAPE è di gran lunga il migliore per qualsiasi tipo di lavoro di sviluppo, nonostante abbia qualche piccolo difetto secondario che mi piacerebbe cambiare.
Quindi ho convertito un paio di giochi usando il metodo di modifica manuale del file .dsk (usando un editor esadecimale, HxD), ma l’ho trovato frustrante perché era molto difficile tenere traccia delle modifiche fatte e, se sbagliavi un solo byte, potevi tornare al punto di partenza o trovarti in uno stato sconosciuto. Facendo lo sviluppatore di professione, sono abituato a usare sistemi di controllo versione come git per poter suddividere tutte le mie modifiche in piccoli commit, in modo da avere sempre uno storico con le note di ciò che ho fatto.
Così, per il gioco successivo che ho convertito, ho deciso di sperimentare un metodo alternativo che si è rivelato un successo; ed è questo il metodo che ho usato per tutti i giochi convertiti in seguito.
Inizio con un progetto vuoto di CPCTelera. Probabilmente avrete presente questa eccellente utility: è un compilatore C per Amstrad che genera un file .dsk. Ci sono tantissimi documenti se cercate su Google.
A questo punto ci sono due possibilità per qualsiasi gioco: i file sono compressi oppure no. Per scoprirlo, avviate il gioco (la partita vera e propria, dove state giocando) e mettete in pausa l’esecuzione in un punto qualsiasi premendo F7. Nel debugger vedrete l’indirizzo dell’istruzione corrente (PC) con i byte dell’opcode sulla destra. Aprite il file .dsk in un editor esadecimale e cercate la manciata di byte a partire da dove vi siete messi in pausa. Se li trovate nel file .dsk e tutto corrisponde, ottimo: i file binari probabilmente non sono compressi e potete saltare il passaggio successivo.
Se sono compressi, la cosa è un po’ più complicata, poiché dovete individuare il punto esatto in cui la decompressione è completata e il gioco si avvia. Il modo più semplice per farlo è eseguire il gioco (ad esempio lanciando run "disc") e impostare preventivamente un breakpoint in WinAPE sull’indirizzo di avvio effettivo. Potete trovarlo nell’editor del disco, facendo clic con il tasto destro del mouse su un file, selezionando «Properties» e poi guardando «Exec Address».
Ora parlerò dell’uso dell’IA per commentare e annotare il codice disassemblato, che è lo strumento più utile della cassetta degli attrezzi. Farlo a mano è possibile, ma è un lavoro enorme, lunghissimo e frustrante — non davvero fattibile se avete un lavoro, una famiglia o qualsiasi altro hobby. Quindi, una volta messi in pausa da qualche parte all’inizio o all’interno della routine di decompressione, scorrete nel debugger, di solito in avanti nella memoria, per circa 0x1000 byte. In genere fino a quando non vedete un «RET», ma l’indirizzo finale non è fondamentale perché potete prenderne altro in seguito. Fai Shift+clic per selezionare quel blocco di istruzioni, poi fai clic con il tasto destro e seleziona «Disassemble». Premi OK sulla finestra di dialogo. Premi F3 per mostrare l’assembler se non è apparso automaticamente. Ora hai il codice sorgente, ma in un formato grezzo, che è fondamentalmente inutile se non viene suddiviso in routine e accuratamente commentato.
Una volta ottenuto l’assembly grezzo da WinAPE, copialo e incollalo in un nuovo file all’interno del tuo progetto (inizia una nuova cartella/progetto git per ogni gioco che converti). Io uso una convenzione di nomi come GameZ0x1000-0x2000.raw.asm. Poi usa uno strumento di IA con un prompt specifico per convertirlo in qualcosa di leggibile. Non c’è bisogno di spendere soldi per gli strumenti di IA! Quelli gratuiti sono perfettamente adeguati. Quelli che uso io sono Opencode e Freebuff, ed entrambi offrono una varietà di modelli che puoi usare in modo completamente gratuito. Sono estremamente facili da configurare e utilizzare. Scegli il modello più potente disponibile al massimo delle sue impostazioni (chiedi all’IA se non sei sicuro di quale sia il migliore). Di seguito includo il prompt che uso io: puoi metterlo in un file e poi copiarlo/incollarlo oppure puntare lo strumento di IA verso di esso e verso il tuo file .asm:
«Per il blocco di codice z80 situato in (percorso del tuo file), progettato per girare su Amstrad CPC, aggiungi commenti inline che mostrino le funzioni ad alto livello e dividilo in subroutine, con un’intestazione per ogni subroutine principale/secondaria che ne descriva lo scopo. Modifica le etichette per renderle più descrittive, ma mantieni l’indirizzo alla fine dell’etichetta. In cima al file aggiungi un commento riassuntivo con un elenco puntato delle funzioni principali contenute all’interno. Genera il disassemblato commentato come un singolo blocco senza omettere alcuna istruzione o dato in un nuovo file accanto a quello esistente. Cerca di identificare, ove possibile, le aree che sono effettivamente dati e non codice. Se necessario, fai riferimento al manuale del firmware qui per le chiamate ROM ecc.: https://www.cantrell.org.uk/david/tech/cpc/cpc-firmware/firmware.pdf»
Una volta che l’IA ha fatto il suo lavoro, avrai qualcosa di sperabilmente leggibile da poter analizzare. Se ha fatto un brutto lavoro, o se le routine non sembrano rilevanti per la decompressione e l’avvio del gioco, ti basta prendere un’altra sezione di memoria e ripetere, oppure provare un modello di IA alternativo.
Con un po’ di fortuna vedrai qualcosa come «decompress data» seguito da JP 0xXXXX per avviare effettivamente il gioco (o la sua schermata del titolo). Per la mia esperienza c’è quasi sempre un’istruzione JP XXXXX che rappresenta il punto di ingresso a quello che ci sarebbe stato nel gioco commerciale. Molto spesso si tratta di un numero tondo come 0x100 o 0x500, poiché gli sviluppatori amano scegliere numeri tondi. Un’altra alternativa per trovare il punto di ingresso è eseguire la versione commerciale effettiva e vedere dove si avvia davvero esaminando le proprietà del file su disco.
Una volta ottenuto il punto di ingresso del gioco (cosa banale se non hai una versione compressa/crittografata), puoi fare il dump del contenuto della RAM inserendo un breakpoint a questo indirizzo.
OK, questo testo sta diventando molto più lungo di quanto intendessi, quindi se a qualcuno interessa saperne di più, me lo faccia sapere e continuerò a scrivere qui sotto oppure sposterò il tutto in un articolo della wiki!