This is an old revision of the document!
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:
Il file di configurazione sui Mac è qui:
/Users/<user>/.config/vice/vicerc
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', per 'change memory') 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 (C:$0382) M $0380 $0381 >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
Di seguito uno schema delle operazioni svolte da/verso registri CPU/memoria:
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)
Alla locazione 0346 è stato digitato Return, per indicare la fine dell'input.
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, salvate nella ROM omonima. 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
L'indirizzamento di memoria della KERNAL ROM va da $E000 a $FFFF, come indicato nella memory map.
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 Indexed mode (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, che verifica il flag Zero) 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.
Riassumendo, dopo aver effettuato una comparazione, si possono usare i seguenti mnemonics per ottenere dei branch, a seconda delle varie condizioni:
Condizione | Flag | Branch da usare | Note |
---|---|---|---|
“registro uguale a” | Z - Zero | BEQ (branch equal 0) | |
“registro diverso a” | Z - Zero | BNE (branch not equal 0) | |
“registro minore di” | C - Carry | BCC (branch carry clear) | |
“registro maggiore o uguale a” | C - Carry | BCS (branch carry set) | |
“registro negativo” (MSB=1) | N - Negative | BMI (branch minus) | |
“registro positivo” (MSB=0) | N - Negative | BPL (branch plus) | |
“overflow numero con segno” (bit 6=1) | V - Overflow | BVS (branch overflow set) | |
“no overflow numero con segno” (bit 6=0) | V - Overflow | BVC (branch overflow clear) |
Per stampare la stringa voluta, sarà sufficiente memorizzare i valori ASCII esadecimali di HELLO e RETURN a partire dalla locazione $034a. 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.
Una tabella completa dei codici ASCII (PETSCII) per il C64 è presente qui.
Quindi solo la pressione di numeri 0-9 produrrà un output:
Esempio 1
Questo altro programma testa la pressione dei tasti '4' oppure RUN/STOP:
.C:033c 20 E1 FF JSR $FFE1 .C:033f F0 0D BEQ $034E .C:0341 20 E4 FF JSR $FFE4 .C:0344 C9 34 CMP #$34 .C:0346 D0 F4 BNE $033C .C:0348 20 D2 FF JSR $FFD2 .C:034b 20 3C 03 JSR $033C .C:034e 60 RTS
Esempio 2
Altro esempio, in cui si vuole consentire la visualizzazione dei soli caratteri alfanumerici (0-9 e A-Z):
(C:$0354) d 033c 0357 .C:033c 20 E1 FF JSR $FFE1 ; verifica RUN/STOP .C:033f F0 1C BEQ $035D ; se premuto, vai RTS .C:0341 20 E4 FF JSR $FFE4 ; testa pulsante .C:0344 C9 30 CMP #$30 ; .C:0346 90 F4 BCC $033C ; se < $30 ('0') vai ad inizio .C:0348 C9 75 CMP #$5B ; .C:034a B0 F0 BCS $033C ; se >= $5B (carattere dopo 'Z') vai ad inizio .C:034c C9 3A CMP #$3A ; .C:034e 90 07 BCC $0357 ; se < $3A ('9') vai a stampa carattere .C:0350 C9 41 CMP #$41 ; .C:0352 B0 03 BCC $033c ; se < $41 ('A') vai ad inizio .C:0357 20 D2 FF JSR $FFD2 ; stampa carattere .C:035a 20 3C 03 JSR $033C ; vai ad inizio .C:035d 60 RTS ; return from subroutine
Il codice sopra si prende cura di non visualizzare i caratteri prima dello '0', dopo la 'Z' e in mezzo tra le cifre e le lettere.
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 (non ci sono equivalenti per i registri X o Y).
- per azzerare un bit dell'accumulatore A si può usare AND, funzionando quindi come una mask; 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:
Addizione
Per sommare dei numeri si usa:
- l'accumulatore A (non i registri X e Y) e un byte di memoria
- l'operando ADC “ADd with Carry”
- non esiste un operando di addizione senza Carry, ma è possibile cancellare il flag Carry, prima dell'ADC, con CLC
- il risultato dell'operazione resta nell'accumulatore A
L'esempio seguente mostra la somma di due numeri da 2 byte ciascuno ($03A1:$03A0 + $03B1:$03B0 = $03C1:$03C0).
(C:$034f) d 033c 034c .C:033c 18 CLC ; Carry azzerato .C:033d AD A0 03 LDA $03A0 ; copia valore da locazione $03A0 in A .C:0340 6D B0 03 ADC $03B0 ; somma valore in A con valore in locazione $03B0; qui il carry può essere a 1 .C:0343 8D C0 03 STA $03C0 ; salva somma in A in $03C0 .C:0346 AD A1 03 LDA $03A1 ; copia valore da locazione $03A1 in A .C:0349 6D B1 03 ADC $03B1 ; tiene conto del valore del carry precedente e fa la somma dell'high byte .C:034c 8D C1 03 STA $03C1 ; salva somma in A in $03C1
Qui vengono caricati dei valori nelle locazioni di memoria oggetto di somma:
(C:$03a2) >C:03a0 8d 45 (C:$03a2) >C:03b0 7a 52
Sottrazione
Prima di effettuare una sottrazione conviene settare il carry (borrow) flag con SEC.
Il seguente esempio sottrae due valori (8D-45) e salva il risultato in $0382:
(C:$e5d4) >C:0380 8d 45 ; carica i valori degli operandi in $0380 e $0381 .C:033c 38 SEC .C:033d AD 80 03 LDA $0380 ; A=8D .C:0340 ED 81 03 SBC $0381 ; A=8D-45 .C:0343 8D 82 03 STA $0382 ; $0382=A .C:0346 60 RTS (C:$04c2) m 0382 0383 >C:0382 48 00
Shift register
ASL
Per effettuare uno shift register a sinistra, dell'accumulatore o di una locazione di memoria, si possono usare ASL o ROL:
- ASL: non usa il carry nel primo bit LSB, che imposta a 0
- ROL: usa il carry nel primo bit LSB
Il seguente programma effettua uno shift left di A e salva il risultato nella locazione $0349; dopo 7 iterazioni un bit si sposta dal bit 0 al bit 7:
(C:$034b) d 033c 0347 .C:033c A9 01 LDA #$01 .C:033e A2 07 LDX #$07 .C:0340 0A ASL A .C:0341 CA DEX .C:0342 D0 FC BNE $0340 .C:0344 8D 49 03 STA $0349 .C:0347 00 BRK (C:$0489) m 0349 034a >C:0349 80 00
Ad ogni iterazione, il registro A viene moltiplicato di 2. Infatti, la locazione $0349 contiene, dopo 7 iterazioni, il valore $80.
ROL
Invece, il seguente programma simula l'utilizzo di un numero composto da un low byte ($0351) e da un high byte ($0352); viene caricato il valore $8F nel low byte e, tramite un ASL e subito dopo un ROL, passando per il Carry, dopo 8 iterazioni, il valore finisce spostato nell'high byte:
(C:$0348) d 033c 0352 .C:033c A9 00 LDA #$00 .C:033e 8D 52 03 STA $0352 .C:0341 A9 8F LDA #$8F .C:0343 8D 51 03 STA $0351 .C:0346 A2 08 LDX #$08 .C:0348 0E 51 03 ASL $0351 .C:034b 2E 52 03 ROL $0352 .C:034e CA DEX .C:034f D0 F7 BNE $0348
Subroutine
Con i comandi TAX, TXA, TAY, TYA si copia il contenuto di A in X o Y e viceversa. Qui di seguito un programma che li usa; il programma effettua la somma di due numeri ad una cifra.
La prima parte riprende quanto evidenziato sopra, cioè:
- verifica la pressione di RUN/STOP
- accetta l'input di numeri nel range 0-9
- stampa il numero
- con l'istruzione AND #$0F prende in considerazione la parte destra del codice ASCII, in modo che se il tasto premuto è 0, il codice ASCII è $30 e viene memorizzato $0, cioè il valore binario effettivo:
.C:033c 20 E1 FF JSR $FFE1 .C:033f F0 10 BEQ $0351 .C:0341 20 E4 FF JSR $FFE4 .C:0344 C9 30 CMP #$30 .C:0346 90 F4 BCC $033C .C:0348 C9 3A CMP #$3A .C:034a B0 F0 BCS $033C .C:034c 20 D2 FF JSR $FFD2 .C:034f 29 0F AND #$0F .C:0351 60 RTS
Questa prima parte è una subroutine, che viene richiamata più volte dal codice vero e proprio sottostante.
La seconda parte richiama due volte la subroutine di input ed effettua e stampa la somma di due numeri nel range 0-9:
.C:0352 20 3C 03 JSR $033C ; richiama subroutine di input .C:0355 8D C0 03 STA $03C0 ; salva il valore binario del primo addendo in una locazione .C:0358 A9 2B LDA #$2B ; codice ASCII per '+' .C:035a 20 D2 FF JSR $FFD2 .C:035d 20 3C 03 JSR $033C .C:0360 AA TAX ; salva il valore binario del secondo addendo nel registro X, visto che non viene usato .C:0361 A9 3D LDA #$3D ; codice ASCII per '=' .C:0363 20 D2 FF JSR $FFD2 .C:0366 8A TXA ; riporta il valore binario del secondo addendo in A .C:0367 18 CLC ; non considerare Carry .C:0368 6D C0 03 ADC $03C0 ; somma di A, che contiene il secondo addendo, con la locazione in cui è stato memorizzato il primo addendo .C:036b AA TAX ; salva il valore binario della somma (questa volta) nel registro X, visto che non viene usato .C:036c C9 0A CMP #$0A .C:036e 90 07 BCC $0379 ; se somma <$0A (10) vai a stampa_risultato .C:0370 A9 31 LDA #$31 ; somma è >=$0a: stampa "1" .C:0372 20 D2 FF JSR $FFD2 .C:0375 8A TXA ; copia valore binario somma presente in X in A .C:0376 38 SEC .C:0377 E9 0A SBC #$0A ; A (somma) = A - $0A .C:0379 09 30 ORA #$30 ; stampa risultato; aggiungi '$3' nel nibble di sinistra, così da ottenere il valore ASCII della somma dei due addendi .C:037b 20 D2 FF JSR $FFD2 .C:037e A9 0D LDA #$0D ; stampa Return .C:0380 20 D2 FF JSR $FFD2 .C:0383 60 RTS
Qui di seguito un esempio in cui si effettua una sottrazione di due valori, sempre usando la subroutine presente in $033C:
.C:0352 20 3C 03 JSR $033C .C:0355 8D C0 03 STA $03C0 ; salva minuendo in $03C0 .C:0358 A9 2D LDA #$2D .C:035a 20 D2 FF JSR $FFD2 .C:035d 20 3C 03 JSR $033C ; salva sottraendo in $03C1 .C:0360 8D C1 03 STA $03C1 .C:0363 A9 3D LDA #$3D .C:0365 20 D2 FF JSR $FFD2 .C:0368 AD C0 03 LDA $03C0 .C:036b 38 SEC .C:036c ED C1 03 SBC $03C1 ; effettua $03C0-$03C1 e lascia in A .C:036f 09 30 ORA #$30 .C:0371 20 D2 FF JSR $FFD2 .C:0374 A9 0D LDA #$0D .C:0376 20 D2 FF JSR $FFD2 .C:0379 60 RTS
Qui di seguito un altro esempio che testa se un numero è pari o dispari facendo un LSR (logical shift right) e controllando il carry (usa la stessa subroutine sopra). Se C=1 è dispari, altrimenti pari:
.C:0352 20 3C 03 JSR $033C .C:0355 4A LSR A .C:0356 90 24 BCC $037C ; C=0, vai a stampa ' IS EVEN' .C:0358 A9 20 LDA #$20 ; C=1, stampa 'IS ODD' (spazio) .C:035a 20 D2 FF JSR $FFD2 .C:035d A9 49 LDA #$49 ; 'I' .C:035f 20 D2 FF JSR $FFD2 .C:0362 A9 53 LDA #$53 ; 'S' .C:0364 20 D2 FF JSR $FFD2 .C:0367 A9 20 LDA #$20 ; (spazio) .C:0369 20 D2 FF JSR $FFD2 .C:036c A9 4F LDA #$4F ; 'O' .C:036e 20 D2 FF JSR $FFD2 .C:0371 A9 44 LDA #$44 ; 'D' .C:0373 20 D2 FF JSR $FFD2 .C:0376 A9 44 LDA #$44 ; 'D' .C:0378 20 D2 FF JSR $FFD2 .C:037b 60 RTS .C:037c A9 20 LDA #$20 ; (spazio) .C:037e 20 D2 FF JSR $FFD2 .C:0381 A9 49 LDA #$49 ; 'I' .C:0383 20 D2 FF JSR $FFD2 .C:0386 A9 53 LDA #$53 ; 'S' .C:0388 20 D2 FF JSR $FFD2 .C:038b A9 20 LDA #$20 ; (spazio) .C:038d 20 D2 FF JSR $FFD2 .C:0390 A9 45 LDA #$45 ; 'E' .C:0392 20 D2 FF JSR $FFD2 .C:0395 A9 56 LDA #$56 ; 'V' .C:0397 20 D2 FF JSR $FFD2 .C:039a A9 45 LDA #$45 ; 'E' .C:039c 20 D2 FF JSR $FFD2 .C:039f A9 4E LDA #$4E ; 'N' .C:03a1 20 D2 FF JSR $FFD2 .C:03a4 60 RTS
Indirizzamento Indirect, Indexed
L'indirizzamento di tipo Indirect, Indexed consente di raggiungere più di 256 byte di memoria.
Start & End of Basic
Il Start-of-Basic e l'End-of-Basic è definito in questi registri:
Da questi valori di default, si capisce che i byte disponibili per Basic, all'accensione del C64, sono
40960-2049 = 38911
Kick Assembler
Un assembler avanzato è Kick Assembler; qui un corso che spiega come creare un gioco usando questo ambiente di sviluppo.
Nel classico comando per eseguire un programma da disco:
LOAD "*",8,1
il parametro '1' indica al computer di caricare in memoria il programma da disco iniziando alla locazione di memoria specificata dai primi due byte del file (programma) da caricare, che solitamente è $0801.
Se si effettua un LIST del programma appena caricato compare solitamente una singola linea:
Che quindi punta alla locazione 2049 ($080b); quella è la locazione da dove parte il programma vero e proprio, quindi abbastanza all'inizio dello Start-of-Basic.
All'inizio di ogni codice di Kick Assembler bisogna inserire la seguente istruzione:
// $801 BasicUpstart2(main) // $810 main:
Quello che fa BasicUpstart2(main) è il seguente piccolo programma in Basic:
10 SYS2064
Quindi, nella posizione $801 viene inserito il programma 10 SYS2064, che quindi rimanderà alla locazione 2064 ($810), dove sarà presente il codice main:.
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'
Espansione RAM
Con il VIC 20 è possibile usare una scheda che consente di espandere il computer, così come le originali cartucce da 3K, 8K o 16K. Ad es., una scheda compatibile è la seguente:
I settaggi DIP switch corrispondono alle linee del connettore di espansione del computer:
Di seguito è spiegato come settare i DIP switch per ottenere le espansioni RAM:
DIP switch | Espansione | Bytes free |
---|---|---|
11100000 | 3K | 6655 |
11110000 | 8K | 11775 |
11111000 | 16K | 19967 |
11111100 | 24K | 28159 |