Table of Contents
VICE
Intro
Si tratta di un emulatore, disponibile per vari sistemi operativi, di home computer Commodore.
Installazione
Si può installare dai repo Ubuntu, ma poi bisogna copiare le ROM dal pacchetto sorgente, pena errore di “fail to load kernal”.
Le tre ROM da copiare corrispondono ai chip fisici:
$ wget http://www.zimmers.net/anonftp/pub/cbm/crossplatform/emulators/VICE/vice-2.4.tar.gz $ tar vzxf vice-2.4.tar.gz $ cd Scaricati/vice-2.4/data/C64 # cp kernal basic chargen /usr/lib/vice/C64
Copiare anche le ROM dei varie drive e datassette:
$ cd Scaricati/vice-2.4/data/DRIVES # cp d* /usr/lib/vice/DRIVES
Esecuzione
Digitare “Commodore…” e seleziore il 64. Oppure da bash digitare x64.
Tastiera
Apple
Home: fn + cursore sx
Ad esempio per cancellare lo schermo, sul C64 si fa shift + Home, qui si fa shift + fn + cursore sx.
RUN/STOP: esc
RESTORE: fn + cursorse su
Insert: vedi qui
Altri tasti sono disponibili qui.
Salvataggi
Per salvare un programma Basic (ed eventualmente poi importarlo in un vero C64…) si fa:
Create and attach an empty disk image….
Dare poi il nome del programma, che avrà come estensione .d64. Una volta creata l'immagine disco, si potrà collegarla semplicemente con Attach a disk image; i vari programmi verranno salvati come file separati con estensione .PRG.
Machine Language Monitor
Un MLM (machine language monitor) in assembly è un tool che consente di:
- visualizzare locazioni di memoria
- scrivere/modificare locazioni di memoria
- eseguire codice da memoria
Per entrare nel VICE Monitor digitare Alt+H (Command+H per i Mac ). Quando è attivo il monitor non è attivo il Basic.
Visualizzare i registri
Visualizzare i registri (con 'R'):
(C:$e5d1) R ADDR A X Y SP 00 01 NV-BDIZC LIN CYC STOPWATCH .;e5d1 00 00 0a f3 2f 37 00100010 000 000 5425056 (C:$e5d1)
Leggere e scrivere nella memoria
Visualizzare il contenuto della memoria (con 'M'):
(C:$1011) M 033c 0348 >C:033c 00 00 00 00 00 00 00 00 00 00 00 00 00 ............. (C:$0349)
Le locazioni $033C-$0348 sono tutte vuote; scriviamo nella memoria (con '>C') in linguaggio macchina:
(C:$0349) >C:033c ad 80 03 ae 81 03 8d 81 03 8e 80 03 00 (C:$0349)
Di seguito alcuni opcodes utilizzati:
Il programma consiste nello scambiare il contenuto delle locazioni $0380 e $381 (questo è ancora da verificare/modificare).
Per verificare il contenuto della memoria appena scritta:
(C:$0349) M 033c 0348 >C:033c ad 80 03 ae 81 03 8d 81 03 8e 80 03 00 ............. (C:$0349)
Verifichiamo e quindi copiamo dei valori nelle locazioni $0380 e $381:
(C:$0410) M $0380 $0381 >C:0380 00 00 (C:$0382) >C:0380 11 99
Adesso la locazione $0380 contiene il valore $11 e la $0381 contiene $99.
Eseguire codice da memoria
Eseguiamo il programma (con 'G') che inverte le locazioni:
(C:$0382) G 033c
Le due locazioni adesso hanno effettivamente il contenuto invertito:
(C:$e5d1) M 0380 0381 >C:0380 99 11
Disassemblare
Per disassemblare (tradurre da linguaggio macchina in assembly) le locazioni dove si è salvato il codice si fa così (con 'D'):
(C:$0382) D 033c 0348 .C:033c AD 80 03 LDA $0380 .C:033f AE 81 03 LDX $0381 .C:0342 8D 81 03 STA $0381 .C:0345 8E 80 03 STX $0380 .C:0348 00 BRK
L'ultimo comando (BRK) presente in $0348 interrompe il Monitor e passa il controllo al Basic nel VICE. Anche qui possiamo verificare il valore, modificato, delle locazioni $0380 (896 in decimale) e $0381 (897):
? PEEK(896) 153 (cioè $99) ? PEEK(897) 17 (cioè $11)
Un altro modo per passare dal monitor al Basic è premendo 'X'.
Assemblare
Per assemblare (scrivere in assembly per poi essere tradotto in linguaggio macchina) si fa così (con 'A'):
(C:$034b) A 033c .033c LDA #$99 .033e STA $0380 .0341 LDX #$98 .0343 STX $0381 .0346 (C:$0346) M 033c 034a >C:033c a9 99 8d 80 03 a2 98 8e 81 03 00 00 00 00 00 ............... (C:$034b)
Il carattere '#' indica che il valore da caricare nel registro non è quello di una locazione di memoria, ma un valore immediato (o letterale).
Basic
L'esecuzione può avvenire anche nel Basic del C64:
SYS 828
Dove '828' è il decimale di $033C.
Il programma, che carica i valori $99 e $98 nelle locazioni $0380 e $0381, è stato eseguito:
(C:$e5d1) M 0380 0381 >C:0380 99 98 ..
CHROUT
Per scrivere a video possiamo usare il seguente codice:
(C:$e5cd) >C:0400 3 9 1 f 20 12 f 2 5 12 14 f (C:$e5cd) X
dove $0400 è l'inizio della memoria video del C64; i caratteri che seguono non sono ASCII, ma screen codes (vedi Appendice B della C64 Programmer's Reference Guide).
Il codice produrrà il seguente output:
Invece che scrivere direttamente in memoria, si possono usare le KERNAL subroutines. Uno dei vantaggi di queste è che usano i codici ASCII; ad es. la CHROUT ($FFD2) mostra a video il contenuto del registro A.
(C:$e5cf) A 033c LDA #$41 .033e JSR $FFD2 .0341 LDA #$42 .0343 JSR $FFD2 .0346 LDA #$43 .0348 JSR $FFD2 .034b (C:$034b) G 033c
A video comparirà velocemente la scritta 'ABC' ($41 $42 $43).
Aggiungendo 'RTS' (Return from subroutine):
.034b RTS
è possibile eseguire il comando dal Basic:
SYS 828
Loop
Ipotizziamo di voler ottimizzare il precedente codice memorizzando, invece di 'ABC' la sequenza 'HELLO' in locazioni di memoria consecutive e leggerle tramite un loop.
(C:$e5cd) A 033c .033c LDX #$00 .033e LDA $034A,X .0341 JSR $FFD2 .0344 INX .0345 CPX #$06 .0347 BNE $033E .0349 RTS
Per questo loop andremo ad utilizzare il registro X perché:
- il registro X può essere incrementato (riga $0344), a differenza dell'Accumulatore
- può essere implementato, assieme all'accumulatore, per ottenere un indirizzamento di tipo absolute (vedi riga $033e)
Le righe $0345 e $0347 possono essere spiegate così: CPX ('compare X') in realtà effettua una sottrazione del valore immediato $06 dal registro X. In questo modo, si ottiene un valore, che se non è zero (BNE) comporta un branch, creando così il loop.
Il flag Carry, oltre che dalle operazioni aritmetiche, viene influenzato anche dalle operazioni di comparazione CPX, CPY e CMP, come indicato qui di seguito.
- BCS effettua il branch se il valore nel registro (X,Y o A) è >= del valore comparato (flag Carry=1)
- BCC effettua il branch se il valore nel registro (X,Y o A) è < del valore comparato (flag Carry=0)
Ma in questo caso è quindi necessario cambiare anche la comparazione in CPX #$07, con un valore maggiore di uno rispetto a quello cercato.
Per stampare la stringa voluta, sarà sufficiente memorizzare i valori ASCII esadecimali di HELLO e RETURN. Sono valori che dobbiamo memorizzare come tali, non possiamo assemblarli:
(C:$0368) >C:034a 48 45 4c 4c 4f 0d
Richiamando la subroutine dal Basic otteniamo:
Salvare
Per potere salvare il codice bisogna essere in Basic; bisogna:
- reperire, tramite dei PEEK, i valori sia del codice che i valori di testo di HELLO
- ricrearli poi con dei POKE
Recuperiamo intanto il codice e valori stringa; il codice in assembly è compreso tra $033c e $034f, che corrispondono ai decimali 828 e 847. Quindi:
Si tratta dei valori decimali del codice esadecimale inserito nelle locazioni. Tali valori possono poi essere ricopiati tramite comandi Basic DATA e memorizzati con dei POKE:
Tale codice, che ricrea il codice assembly di prima, si può ovviamente salvare come un qualsiasi programma Basic:
Per salvare su nastro usa il comando File-Create and attach datassette image…. Dopodiché si potrà salvare il listato ed eseguirlo:
GETIN e STOP
Un'altra KERNAL subroutine, oltre alla già vista CHROUT, è GETIN ($FFE4), che testa la pressione di un tasto (simile al comando Basic GET) e lo salva nel registro A in formato ASCII.
C'è anche STOP ($FFE1), che testa la pressione di RUN/STOP.
Il seguente programma testa proprio inizialmente la pressione di RUN/STOP ed in tal caso effettua un opportuno RTS:
(C:$e5d4) A 033C JSR $FFe1 .033f BEQ $0351 ... .0351 RTS
Successivamente chiama la KERNAL routine GETIN e ottiene quindi la pressione di un tasto, che viene salvato in A sotto forma di codice ASCII:
... .0341 JSR $FFe4 .0344 CMP #$30 .0346 BCC $033C .0348 CMP #$3A .034a BCS $033C .034c JSR $FFD2 .034f NOP .0351 RTS
Siccome in questo programma ci interessano solo i numeri da 0 a 9 (codici ASCII da $30 a $39), le righe $0344-$0346 testano se il codice ASCII digitato è minore di 30 (vedi uso BCC sopra) mentre le righe $0348-$034a testano se il codice ASCII digitato è maggiore o uguale di 3A; non essendo questi valori accettabili, in entrambe le condizioni il programma torna all'inizio per un ulteriore verifica del pulsante premuto.
Quindi solo la pressione di numeri 0-9 produrrà un output:
AND, ORA e EOR
Con gli operandi AND, ORA e EOR si possono effettuare operazioni sui singoli bit dell'accumulatore A, senza toccare gli altri bit.
- per azzerare un bit dell'accumulatore A si può usare AND; ad es. per azzerare il suo LSB (il bit meno significativo) si può usare
AND #$FE
- per settare a 1 un bit dell'accumulatore A si può usare OR; ad es. per rettare a 1 il suo MSB (il bit più significativo,) si può usare
ORA #$80
- per invertire alcuni bit dell'accumulatore A si può usare EOR
VIC 20
VICMON
VICMON è un MLM per VIC20. Per eseguirlo su VICE è necessario
- avere il file in formato .crt
- impostare la cartuccia come indicato qui e cioè:
Hello World
Di seguito il codice per stampare a video “HELLO WORLD”.
., 1100 ldx #$00 ., 1102 lda $110e,x ., 1105 beq $110d ., 1107 jsr $ffd2 ., 110a inx ., 110b bne $1102 ., 110d brk
Questi i valori ASCII da memorizzare:
.m 110e .:110e 48 45 4c 4c 4f .:1113 20 57 4f 52 4c .:1118 44 00
Conviene prima della 'H', inserire il valore ASCII $93 per cancellare lo schermo, quindi le locazioni di memoria dove viene salvato il testo cambiano così:
.m 110e .:110e 93 48 45 4c 4c .:1113 4f 20 57 4f 52 .:1118 4c 44 00
Variante senza CHROUT
Una variante prevede l'output a video senza l'uso della subroutine kernal CHROUT, ma effettuando l'output direttamente sulla memoria video del VIC 20 $1E00-$1FFF (7680-8191).
Come prima cosa conviene sfruttare gli screen codes della scritta già presente a video:
Poi basta cambiare l'indirizzo della prima locazione di memoria in $1113 (dando Return ad ogni riga), per sovrascrivere le locazioni con i codici ASCII in Screen codes:
Il codice è stato cambiato, ed è stato necessario modificare sia la mappa schermo che la mappa colore (in rosso), altrimenti le scritte non sarebbero comparse; inoltre, a causa dello scrolling, è stato necessario modificare le locazioni schermo e colore a partire dalla sesta riga (a partire quindi dalle locazioni $1E6E - 7790 per lo schermo e $966E - 38510 per la mappa colore), così che la scritta non scomparisse in alto.
Mappa della memoria
Programmando il VIC 20 in assembly, si va direttamente ad utilizzare/scrivere sulla sua memoria; può essere quindi utili conoscere alcune aree della memoria, così da comprenderne meglio il comportamento del computer.
Reset vector
L'indirizzo del reset vector è nella memoria in KERNAL rom ($E000-$FFFF 57344-65535); è contenuto in due indirizzi di memoria, $FFFD - 65533 (high byte) e $FFFC - 65532 (low byte); facendo un po' di calcoli si ottiene:
che è l'indirizzo del reset vector ($FD22 - 64802), che può quindi essere richiamato, ad es. da BASIC, per effettuare un reset del computer:
La routine Kernal vera e propria presente in $FD22 è abbastanza complessa; incollo qui di seguito le prime istruzioni:
Start-of-Basic RAM
L'inizio della RAM Basic è definito, a seconda che il VIC 20 sia o meno espanso, così:
tratto da qui. Il vettore di inizio Basic è dato dal contenuto di due locazioni, 43 (low byte) e 44 (high byte); il valore $1001 è confermato:
Questo valore e i successivi, potrebbero interferire con il codice assembly che scriviamo; fino ad adesso per questo abbiamo utilizzato $1100 - 4352: un programma Basic potrebbe andare a sovrascriverlo. E' consigliabile quindi spostare in avanti il vettore di Start-of-Basic; come indicato qui conviene procedere così:
- settare a '0' il valore della prima locazione della nuova area Basic
- impostare i valori della nuova area Basic al valore delle prima locazione + 1
- dare 'NEW' in Basic, così lo stesso si posiziona correttamente nella nuova area
Per la nuova locazione dello Start-of-Basic prendiamo l'attuale + 1024, cioè 4096+1024=5120; per ottenere quanto sopra si procede quindi così:
POKE 5120,0 POKE 43,1:POKE 44,20:NEW
Infatti 5121=1+20*256.
Per conferma, si può provare a digitare il seguente programma in BASIC e verificare dove viene memorizzato:
1234PRINT"CIAO"
e controllare quello che viene memorizzato nel nuovo Start-of-Basic a $1400:
dove:
- '00' è il primo byte a $1400
- '140D' è il Next line link
- '04D2' è '1234'
- '99' è il token di PRINT
- poi seguono i PETSCII di “CIAO”
- '00' è il termine della riga
- la prossima riga inizia, come da indicazioni sopra (Next link link), a '140D'