Programmare la REU con il C64
di Francesco Sblendorio
(Ringrazio Massimiliano ‘WizKid’ De Ruvo per avermi suggerito delle ottime fonti)Segue la mia traduzione in italiano dell’articolo REU Programming di Robin Harbron.
Nota: questo articolo è apparso in origine sulla “Loadstar Letter #46” pubblicata nel 1997. Grazie al team di Loadstar per aver dato il permesso di pubblicazione sul mio sito (di Robin Harbron, NdT).
Vuoi sapere come usare la REU nei tuoi programmi? I seguenti programmi BASIC e assembly sono esempi di come verificare la presenza di una REU, determinare le sue dimensioni e quindi memorizzarci, prelevarci o scambiarci segmenti di memoria.
La REU è gestita tramite IO2, cioè le locazioni di memoria che vanno da $DF00 a $DFFF. La REU ha effettivamente 7 registri con alcuni registri che sono replicati, quindi tutte le operazioni di PEEK e POKE saranno effettuate sempre e comunque sulle locazioni di memoria che vanno da $DF00 a $DF0A (in decimale da 57088 a 57098).
Come determinare la presenza di una REU? Se la REU è presente, le locazioni che vanno da $DF02 a $DF05 manterranno i valori che sono in esse memorizzati. Non conosco altre periferiche che si comportano in questo modo, quindi le seguenti routine sfruttano questa proprietà. Nella versione BASIC (che usa solo una variabile per essere il più possibile discreta), X sarà uguale a 0 se non è presente una REU, sarà uguale a 1 se la REU è presente.
Nella versione assembly (SYS 16384 per far partire la routine da BASIC), l’accumulatore sarà 0 se non è presente una REU, varrà 1 se invece è presente. In BASIC sarà possibile leggere il contenuto dell’accumulatore attraverso PEEK(780) dopo aver chiamato la routine.
Ora, quanto è grande la REU installata? Questa è la mia soluzione (di Robin Harbron, NdT), anche se sembra non essere quella ottimale. Questa routine ci metterà un po’ di secondi nella versione BASIC (ovviamente la versione assembly non impiegherà del tempo significativamente percepibile). In parole povere, la routine scrive il numero di banco nel primo byte di ogni banco (tutti i possibili 256 banchi, il che significherebbe avere una REU da 16MB!). I banchi che non sono disponibili sembreranno “coprire” i banchi esistenti, quindi ciò che sarà scritto in un banco non-disponibile finirà per sovrascrivere ciò che è stato scritto in un precedente banco. Quindi andiamo a scorrere nuovamente i banchi cercando quanti valori crescenti e consecutivi riusciamo a trovare. Questo è il numero di banchi effettivamente disponibili. Questa routine è anche non-distruttiva. Legge e memorizza il contenuto originale della REU, quindi lo riscrive dov’era prima quando ha finito. La routine richiede un buffer di 257 byte per fare questo, a cui puntano la variabile S (nella versione BASIC) o la label temp (nella versione assembly). Dopo aver eseguito la versione BASIC, la variabile A conterrà il numero di banchi disponibili (da 64kB ciascuno, NdT). Per esempio, A=8 dopo l’esecuzione del programma significa avere una REU da 512kB. La versione assembly ritorna il numero di banchi all’interno dell’accumulatore.
Veniamo ora allo scopo principale di una REU: memorizzare e restituire dati. La routine seguente è un modello che puoi adattare e usare. Può sia memorizzare sulla REU, sia prelevare dati dalla REU che fare swap tra REU e memoria principale, nel caso particolare si userà il contenuto della memoria video in bassa risoluzione come dati da scambiare.
Ecco una descrizione dettagliata dei registri:
$DF02-$DF03 (57090-57091) puntano all’indirizzo base della memoria del computer con la quale vogliamo operare. È memorizzato nel formato standard LO/HI. $0400 è l’indirizzo che ci andiamo a scrivere per operare con la memoria video.
$DF04-$DF06 (57092-57094) puntano all’indirizzo base della memoria nella REU che vogliamo utilizzare. È memorizzato nel formato LO/HI/BANK. Il numero di banco può essere pensato come a un byte “ulteriormente più alto”. Memorizzeremo le informazioni nella locazione $0 del banco 0.
$DF07-$DF08 (57095-57096) determinano la lunghezza in byte del trasferimento. È memorizzata anch’essa nel formato LO/HI. Memorizzando uno zero in questo registro equivale a specificare la lunghezza massima, cioè 64kB.
$DF0A permette di tenere fisso l’indirizzo della memoria del C64 o della REU, per permetterti di leggere o scrivere particolari locazioni più di una volta. Se il bit 7 è acceso, è fissato l’indirizzo della memoria del C64, se è acceso il bit 6 è fissato l’indirizzo della REU. Ciò ha diversi usi: puoi fissare l’indirizzo della REU (memorizzandoci per esempio 0 o 255) e settore la lunghezza a 8000 byte. Puoi quindi riempire un intero schermo bitmap con quel byte in proprio 8000 cicli o trasferendolo dalla REU al C64! Quest’ultimo modo di procedere è 4 volte più veloce rispetto alla stessa operazione fatta facendo uso della sola CPU. Altro esempio: campionare una particolare locazione di memoria; questo è stato fatto per studiare il generatore di numeri casuali del SID.
$DF01 (57089) esegue l’effettiva operazione. È necessario accendere il bit 7, per dire alla REU di eseguire l’operazione di MOVE. Gli altri bit dicono alla REU come effettuare tale operazione di MOVE:
- Se il bit 5 è acceso, i registri di indirizzo e di lunghezza rimangono intatti dopo l’esecuzione del comando. Ciò è utile se hai intenzione di eseguire più trasferimenti consecutivi identici. Se il bit è spento, i registri di indirizzo punteranno all’indirizzo immediatamente successivo l’ultimo indirizzo a cui si è acceduto, e il registro di lunghezza sarà impostato a 1.
- Se il bit 4 è acceso, il comando sarà eseguito immediatamente. Se è spento, il comando non sarà eseguito fin quando non verrà scritta la locazione $FF00. Può apparire strano, ma è utile nel caso si vogliano trasferire dati nelle aree di memoria nei dintorni delle ROM o dell’I/O. Questa opzione dà la possibilità di impostare i registri per il trasferimento, quindi fare switch su I/O (per esempio), quindi eseguire il trasferimento. Per far partire il trasferimento basterà un semplice LDA $FF00 : STA $FF00.
- I bit 1 e 0 definiscono il tipo di trasferimento. 00 è per trasferire da C64 a REU, 01 per trasferire da REU a C64, 10 per effettuare uno swap tra REU e C64 (in questo caso il tempo impiegato è doppio in quanto doppio è il lavoro da fare), e 11 per effettuare un’operazione di compare tra REU e C64. Per usare la funzione di compare, spegnere il bit 5 di $DF00, eseguire il compare (esattamente come qualsiasi altro trasferimento, con la differenza che nessun byte sarà effettivamente spostato). Ora, se il bit 5 di $DF00 è acceso, allora è stata trovata almeno una differenza tra la memoria della REU e quella del C64.
Segue la versione BASIC. Devi solo impostare la variabile X a seconda del commento presente nella linea 60.
Nella versione assembly, bisogna solo impostare le label c64 all’indirizzo base C64, reu all’indirizzo base REU, bank al numero di banco della REU (ho usato 2 perché il mio assemblatore utilizza per sé i banchi 0 e 1), length al numero di byte da trasferire, e action a 0, 1, 2 o 3 a seconda del tipo di trasferimento che vuoi effettuare.
Commenti
Posta un commento