User Tools

Site Tools


content:retrocomputing:vice

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é:

  1. il registro X può essere incrementato (riga $0344), a differenza dell'Accumulatore
  2. 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.

Un'alternativa a BNE può essere anche BCC, che sfrutta il flag Carry; nella fattispecie:
  • 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:

Cancellando il programma Basic (ad esempio con 'NEW') il codice in memoria rimane comunque

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:

Quasi tutta la memoria RAM del VIC 20 è assegnata al BASIC; questo consente di scrivere programmi quanto più grandi possibile. Per ottenere più spazio per i programmi in linguaggio macchina bisogna quindi riassegnare la memoria assegnata al BASIC

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ì:

  1. settare a '0' il valore della prima locazione della nuova area Basic
  2. impostare i valori della nuova area Basic al valore delle prima locazione + 1
  3. 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.

Questo consente di avere uno spazio sufficiente per l'assembly, da $1000 a $13FF, prima dell'area Basic che va da $1400 a $1DFF, prima a sua volta della memoria video, che inizia a $1E00.

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'
content/retrocomputing/vice.txt · Last modified: 2023/08/27 15:20 by admin