Pier Luca Montessoro: SimCPU (simulatore di CPU RISC e assemblatore)

# SimCPU: simulatore di CPU RISC e assemblatore

## Pier Luca Montessoro

## Introduzione

SimCPU è un simulatore di CPU RISC a 16 bit che permette di analizzare in modo dettagliato il funzionamento a basso livello di un calcolatore. La simulazione è organizzata in modo da essere estremamente aderente alla realtà: il file contenente il programma in linguaggio macchina (di SimCPU) viene caricato in memoria, il program counter viene inizializzato al valore indicato come "entry point" in tale file, ed infine viene passato il controllo alla console di simulazione con cui l'utente può controllare l'avanzamento del simulatore.

Oltre ai file di estensione .axx, contenenti i sorgenti in lunguaggio assembler, anche i file di estensione .exx, contenenti il linguaggio macchina, sono in formato ASCII e quindi possono essere scritti a mano (utile esercizio per comprendere gli aspetti di dettaglio del funzionamento di un elaboratore), oppure possono essere generati mediante l'assemblatore a partire da un file sorgente in linguaggio assembler.

Il simulatore e l'assemblatore sono scritti in linguaggio C utilizzando uno stile di programmazione semplice e leggibile, in modo che gli studenti che lo desiderano possano analizzarne il codice per comprendere come funziona un simulatore ed eventualmente estenderne le funzionalità (per informazioni sui file sorgenti di SimCPU e per le istruzioni per la compilazione si faccia riferimento al file readme.txt.

## Documentazione

- Descrizione dell'architettura hardware
- Descrizione del linguaggio macchina e del linguaggio assembly, descrizione in forma testuale (in inglese), corrispondenza tra istruzioni in linguaggio C e sequenze di istruzioni in linguaggio macchina
- Descrizione dei formati dei file per i programmi in linguaggio assembler e in linguaggio macchina (eseguibili di SimCPU)
- Sorgenti C del simulatore e dell'assemblatore e programmi eseguibili (MS-DOS in ambiente Windows) con esempi di programmi in assembler e in linguaggio macchina e esempi di implementazione di alcune funzioni di libreria di base

## Per installare ed eseguire SimCPU

1. Copiare SimCPU sul proprio hard disk nel direttorio scelto per l'installazione (ad esempio C:\SIMCPU);
2. Aprire una finestra MS-DOS e posizionare il direttorio di lavoro su quello di simcpu\run (es.: cd C:\SIMCPU\RUN);
3. Lanciare l'assemblatore (assembler.exe) e il simulatore (simcpu.exe) scrivendo il nome del file da assemblare/eseguire come parametro sulla linea di comando. È anche possibile eseguire tali programmi direttamente da Windows Explorer mediante doppio click; in tal caso viene richiesto il nome del file .axx (sorgente assembler per l'assemblatore) o .exx (file contenente il linguaggio macchina per il simulatore) che va scritto senza estensione.

## Funzionalità di tracing e debugging dei programmi

Una delle funzionalità più interessanti di SimCPU è la possibilità di analizzare in detteglio la successione dei cicli macchina che concorrono all'esecuzione di un'istruzione. Tale funzionalità viene attivata mediante il comando "set trace_mode extended" (v. esempio 3 nel seguito).

La funzionalità di tracing può anche essere attivata in modalità "instruction". In tal caso, all'inizio del ciclo di esecuzione di ogni istruzione, viene visualizzato il contenuto della memoria contenente l'istruzione da eseguire. Se il file contenente il codice macchina è stato generato con l'assemblatore di SimCPU, viene anche visualizzato il relativo codice sorgente assembler. Tale funzionalità, in combinazione con i breakpoint (comando "set breakpoint ..."), con l'esecuzione step-by-step (comando "run next" o semplicemente "next") e con i comandi per analizzare il contenuto della memoria e lo stato della CPU (comandi "display memory ..." e "display cpu" o semplicemente "cpu"), permette un facile debugging dei programmi.

## Esempi

Nel seguito, in rosso sono evidenziati i comandi scritti dall'utente per procedere nella simulazione e in blu le note aggiunte per documentare gli esempi.

Ogni comando può essere abbreviato (al limite, anche ad una sola lettera). Se si termina la riga con un comando incompleto, l'interprete ne chiede il completamento indicando le opzioni disponibili per il parametro successivo.

### Esempio 1: sum3.exx

Questo programma somma i valori di un vettore di tre elementi e lascia il risultato nel registro R10.
Nei file eseguibili di SimCPU ad ogni byte può essere associata una stringa di commento, per esempio contenente il codice sorgente assembly (questo avviene automaticamente utilizzando l'assemblatore fornito). Tale commento viene letto dal simulatore e mostrato quando si visualizza il contanuto della memoria (comando "display memory ..."), come illustrato in questo esempio.

`C:\simcpu>simcpu sum3`
`(NOTA: se non viene fornito il nome di un file sulla linea di comando, il programma lo richiede in modo interattivo)`

`SIMCPU 2.0 starting...`

`ENTERING COMMAND MODE: current program counter = 0005`
`> help`

`Available commands (can be abbreviated down to the first letter only):`

`Set Trace Instruction`
`Set Trace Extended`
`Set Trace Disabled`
`Set Breakpoint <address (hex)> Enabled`
`Set Breakpoint <address (hex)> Disabled`
`Run Next`
`Run Forever`
`Load Input-buffer $<string, up to the end of line>`
`Display Memory <address (hex)> <length (dec)>`
`Display Cpu`
`Display Input-buffer`
`EXit`
`Quit`
`Help`
`Control-C (to enter command line mode while simulation is running)`

`SHORTCUTS:`
`Next = Run Next`
`Cpu = Display Cpu`

`> d c`
`CPU status: PC = 0005 IR = 0000 SP = 0000`
`CPU status: zero = 0 negative = 0 carry = 0 overflow = 0`
`CPU status: R0 = 0000 R1 = 0000 R2 = 0000 R3 = 0000`
`CPU status: R4 = 0000 R5 = 0000 R6 = 0000 R7 = 0000`
`CPU status: R8 = 0000 R9 = 0000 R10 = 0000 R11 = 0000`
`CPU status: R12 = 0000 R13 = 0000 R14 = 0000 R15 = 0000`
`> d m 0 29`
`MEM[0000]: 03 | N: word 3`
`MEM[0001]: 00 |`
`MEM[0002]: 05 | DATA: byte 5`
`MEM[0003]: 09 | byte 9`
`MEM[0004]: 0C | byte 0C`
`MEM[0005]: 10 | START: LDWA R1 N`
`MEM[0006]: 20 |`
`MEM[0007]: 00 |`
`MEM[0008]: 00 |`
`MEM[0009]: 20 | LDWI R2 DATA`
`MEM[000A]: 10 |`
`MEM[000B]: 02 |`
`MEM[000C]: 00 |`
`MEM[000D]: A0 | LDWI R10 0`
`MEM[000E]: 10 |`
`MEM[000F]: 00 |`
`MEM[0010]: 00 |`
`MEM[0011]: B2 | LOOP: LDBR R11 R2`
`MEM[0012]: 31 |`
`MEM[0013]: BA | ADD R11 R10`
`MEM[0014]: 40 |`
`MEM[0015]: 20 | INC R2`
`MEM[0016]: 48 |`
`MEM[0017]: 10 | DEC R1`
`MEM[0018]: 49 |`
`MEM[0019]: F6 | JMPNZ LOOP`
`MEM[001A]: C3 |`
`MEM[001B]: 00 | HLT`
`MEM[001C]: CF |`
`> r f`

`ENTERING COMMAND MODE: current program counter = 001D`
`> d c`
`CPU status: PC = 001D IR = CF00 SP = 0000`
`CPU status: zero = 1 negative = 0 carry = 0 overflow = 0`
`CPU status: R0 = 0000 R1 = 0000 R2 = 0005 R3 = 0000`
`CPU status: R4 = 0000 R5 = 0000 R6 = 0000 R7 = 0000`
`CPU status: R8 = 0000 R9 = 0000 R10 = 001A R11 = 000C`
`CPU status: R12 = 0000 R13 = 0000 R14 = 0000 R15 = 0000`
`> q`

`(A questo punto il programma termina)`

`NOTA: per tracciare l'esecuzione di ogni singola istruzione si utilizzi il comando "set trace instruction" (abbreviato in "s t i") oppure "set trace extended" (abbreviato in "s t e")`

### Esempio 2: echo.exx

Questo programma legge il buffer di input (che è gestito in polling) e ne copia il contenuto nel buffer di output.

`C:\simcpu>simcpu echo`
`SIMCPU 2.0 starting...`

`ENTERING COMMAND MODE: current program counter = 0000`
`> l i $hello`
`(NOTA: questo comando permette di caricare il buffer di input, a simulazione ferma)`
`> d i`
`(NOTA: questo comando permette di visualizzare il contenuto del buffer di input, a simulazione ferma)`
`68 (ASCII 'h')`
`65 (ASCII 'e')`
`6C (ASCII 'l')`
`6C (ASCII 'l')`
`6F (ASCII 'o')`
`0A (ASCII '\n')`
`> r f`
`OUTPUT: 68 (ASCII h)`
`OUTPUT: 65 (ASCII e)`
`OUTPUT: 6C (ASCII l)`
`OUTPUT: 6C (ASCII l)`
`OUTPUT: 6F (ASCII o)`
`OUTPUT: 0A`

`(NOTA: a questo punto la simulazione continua eseguendo il ciclo di polling in attesa di dati dal dispositivo di input. Con Control-C si riattiva la console interattiva del simulatore e si può riempire nuovamente il buffer)`

`ENTERING COMMAND MODE: current program counter = 0004`
`> d i`
`(input buffer is empty)`
`> l i $bye`
`> r f`
`OUTPUT: 62 (ASCII b)`
`OUTPUT: 79 (ASCII y)`
`OUTPUT: 65 (ASCII e)`
`OUTPUT: 0A`

`(Ancora Control-C)`

`ENTERING COMMAND MODE: current program counter = 0000`
`> q`

`(A questo punto il programma termina)`

### Example 3: halt.exx

Questo programma effettua semplicemente il fetch e l'esecusione di un'unica istruzione: halt. È utile per provare la funzionalità di tracciamento delle singole istruzione ("set trace instruction" e "set trace enabled").

`C:\simcpu>simcpu halt`
`SIMCPU 2.0 starting...`

`ENTERING COMMAND MODE: current program counter = 0000`
`> s t e`
`> r f`

`TRACE: starting new instruction, program counter = 0000`
`TRACE: CF00 read from MEM[0000]`
`TRACE: instruction_register set from 0000 to CF00`
`TRACE: program counter set from 0000 to 0002`
`TRACE: CF00 read from instruction_register (opcode: HLT)`
`TRACE: halt instruction`

`ENTERING COMMAND MODE: current program counter = 0002`
`> q`

`(A questo punto il programma termina)`

`C:\simcpu>simcpu halt`
`SIMCPU 2.0 starting...`

`ENTERING COMMAND MODE: current program counter = 0000`
`> s t i`
`> r f`
`Exexuting instruction at address 0000:`
`MEM[0000]: 00 | start: HLT`
`MEM[0001]: CF |`

`ENTERING COMMAND MODE: current program counter = 0002`
`> q`

`(A questo punto il programma termina)`

Per contattare l'autore: montessoro@uniud.it
Home page: www.montessoro.it