Un simulatore per il Larc Computer

Il modello Larc di computer e il suo linguaggio macchina sono stati discussi nella pagina precedente. Questo documento discute un programma che può essere utilizzato per simulare e debuggare i programmi Larc. I programma è scritto in java ed è disponibile come eseguibile file jar, larcsim.jar. Ci riferiremo al programma come larcsim.

Programmi nel Larc assembly language

Larcsim può caricare un programma Larc da un file. Rappresenteremo un programma in linguaggio macchina Larc tramite un file di testo, utilizzando un semplice linguaggio assembler. Il programma può includere istruzioni in linguaggio assembler ma anche alcune funzionalità addizionali. Quando un file di testo di linguaggio assembler è letto, esso è trasformato in numero di 16 bit che sono caricati nella memoria RAM del Larc computer. Le locazioni della memoria che non sono settate dal file, sono automaticamente stabilite partendo da zero. La sintassi è come segue.

Un programma assembler può contenere interi espressi formato binario, esadecimale e decimale. I numeri binari iniziano con 0b o 0B, i numeri esadecimali con 0x o 0X. Un numero binario contiene da 1 a 16 uni e zeri. Un numero esadecimale contiene da 1 a 4 cifre esadecimali. Un numero decimale deve essere nel range da -32768 a 65535, cioè 215-2^{15} a 21612^{16} -1. I numeri negativi sono rappresentati nella forma complemento a due. (Numeri negati da -32768 a -1 e numeri positivi da 32768 a 65535 sono solo due notazioni alternative per lo stesso set di numeri a 16 bit; non c'è modo di distinguerli una volta che sono stati trasformati in linguaggio macchina.) Esempi includono 0b1101101010000111, 0b11001, 0xFFFF, 0xA7, -1, 0 e 63882.

Inoltre, in un programma in linguaggio assembly, una singolo apice seguito da un carattere può essere utilizzato per rappresentare un code number UNICODE a 16 bit. Per esempio, poiché il codice per il carattere 'a' è 9, la notazione 'a nel programma rappresenta il numero 97. Nota che 'a, 97, 0b01100001 e 0x61 sono notazioni alternative per lo stesso numero a 16 bit.

Per come larcsim è concepita, una istruzione assembler come lui $2 -89 è solo un'altra notazione rispetto al numero a 16 bit. In effetti, lui $2 -89, 0x97A7, 0b1001001010100111, -27993 e 37543 sono solo differenti rappresentazioni dello stesso numero a 16 bit.

Una stringa è una sequenza di caratteri. Può essere rappresentata in Larc come una sequenza di numeri a 16 bit immagazzinati in locazioni di memoria consecutive. Noi assumeremo che una stringa è terminata dal carattere 0 (cioè, dal numero 0 immagazzinato in memoria alla fine della stringa). Come comodità in linguaggio assembler, se il primo carattere non vuoto di una riga sono le doppie virgolette, il resto della riga è interpretato come stringa. Cioè, i caratteri restanti della linea sono convertiti nel loro rispettivo codice UNICODE e salvati in locazioni consecutive di memoria, con lo zero al termine. Nella stringa, la sequenza di caratteri \n è convertito in un singolo carattere ine-linea (codifica numerica 10), e \\ è convertito in un singolo backslash. Nessun altro carattere di escape è riconosciuto.

Un programma può contenere commenti, che iniziano con un # e si estendono fino alla fine della linea. (Ma un # all'interno di una stringa NON inizia un commento; esso farebbe parte come carattere della stringa.) Linee vuote e linee che contengono solo commenti sono ignorate.

Solo una istruzione del linguaggio assembler, un intero, un carattere o una stringa ci possono essere in una riga. Quando un file è letto, i valori del file sono immagazzinati in locazioni consecutive di memoria della RAM, iniziando dalla locazione a indirizzo zero. Tuttavia, l'indirizzo di destinazione può essere cambiato da una linea nel file che contiene il carattere @ seguito da un numero decimale nel range da 0 a 65535. Questo resetta l'indirizzo di destinazione al valore intero. Per esempio, una linea contenente @150 significa il prossimo valore nel file sarà salvato nella locazione numero 150, e i valori seguenti nelle locazioni 151, 152, 153 e così via. Questo può essere utile per salvare valori di dati o subroutine a specifiche, conosciute locazioni di memoria.

Le chiamate di sistema nel simulatore Larc

Il Larc computer specifica la struttura della CPU e della RAM, ma non specifica nessun dispositivo di input/output come una tastiera o un monitor. Come i computer usano e utilizzano quei device è un argomento più avanzato per ora non trattato. Ma dal punto di vista del programma, i device sono controllati direttamente dal sistema operativo e indirettamente (da altri programmi) tramite la chiamata di subroutine che sono parte del sistema operativo. Per motivi storici, queste subroutine di sistema sono spesso chiamate "system traps" o "system services". Noi utilizzeremo il termine "chiamata di sistema". L'istruzione syscall in assembler è utilizzata per chiamare una chiamata di sistema.

Il simulatore Larc al momento non simula un sistema operativo. Tuttavia, mette a disposizione cinque tipi di chiamate e sistema che possono essere invocate tramite syscall. Le chiamate sono numerate con 0, 1, 2, 3 e 4. Per chiamare la uno, il numero di chiamata deve essere caricato nel registro 1. La chiamata syscall prende il numero della chiamata dal registro 1, esegue la chiamata, e non ritorna finché la chiamata non è stata eseguita. Se la chiamata ritorna qualche dato, quel dato sarà disponibile in alcuni registri quando la chiamata ritorna. Qui viene specificato cosa le chiamate fanno:

  • Trap numero 0: Halt - ferma il computer. Questa trap non prende nessun parametro. Cioè, alla fine del programma, tu dovresti fare li $1 0, seguito da syscall.

  • Trap numero 1: Stampa la stringa - Stampa la stringa che è in memoria. Due parametri sono necessari (in aggiunta al numero 1 nel registro 1). Il registro 2 deve contenere l'indirizzo di memoria del primo carattere della stringa. Il registro 3 deve contenere la lunghezza massima della stringa. Valori dalla memoria saranno l'output, interpretando essi come caratteri UNICODE, finché non è incontrato il valore zero o è raggiunta la massima lunghezza. Lo zero alla fine della stringa non fa parte dell'output, e il carattere di end-of-line NON è automaticamente aggiunto alla fine.

  • Trap numero 2: Stampa intero - Stampa il valore dal registro 2, considerandolo come un intero con segno. Il registro 2 è utilizzato per passare il valore alla chiamata. Il carattere di end-of-line NON è automaticamente stampato dopo l'intero.

  • Trap numero 3: Legge la stringa - Legge la stringa digitata dall'utente, e la conserva in memoria. Due parametri sono necessari. Il registro 2 deve contenere l'indirizzo della locazione in memoria, dove la stringa inizierà. Il registro 3 deve contenere la massima lunghezza della stringa. La chiamata non ritornerà finché l'utente non ha digitato una riga e dato l'invio. I caratteri dell'input sono convertiti nel rispettivo codice UNICODE e collocati in locazioni di memoria successive, iniziando dall'indirizzo stecificato nel registro 2. Il carattere di fine linea dell'input NON è immagazzinato in memoria. Un numero 0 è sempre aggiunto alla fine della stringa, Se il numero di caratteri digitati dall'utente eccede la lunghezza massima meno uno, allora i caratteri in eccesso sono scartati. Le la lunghezza della stringa digitata in input è minore del massimo specificato, il registro 3 contiene la lunghezza effettiva.

  • Trap numero 4: Legge l'intero - Legge l'input di un intero. L'intero sarà caricato nel registro 1 quando la chiamata ritorna (rimpiazzando il numero della trap). L'utente deve digitare un intero decimale nel range da -32768 a 65525 e digitare l'invio. Per rendere le cose semplici per il programmatore, la trap costringe l'utente a immettere solo valori corretti.

Se il valore nel registro 1 non è un numero lecito di trap quando la syscall è eseguita, allora il computer si fermerà con il messaggio "Bad Trap Number error".

Come esempio, qui un programma completo, hello-world.s, in Larc asembly language, con commenti, che stampa la stringa "Hello World", e poi si ferma:

# An Hello World program for the Larc Simulator
             
li $1 1         # trap number for Print String
li $2 16        # "Hello World" is stored at location 16
li $3 20        # maxlength for the string; larger than actual length is OK
syscall         # call the Print String trap
             
li $1 0         # trap number for Halt
syscall         # halt
             
@16             # store the following, starting at location 16
"Hello World

Utilizzo di Larcsim

Larcsim è un programma con una GUI. E' scritto in java ed è necessario avere java installato per poterlo eseguire. Questo programma utilizza una singola finestra, che è suddivisa in quattro sezioni: pannello di controllo, registri, memoria, e input/output.

L'area di input/output è utilizzata per le 4 chiamate di sistema per I/O. Quando una chiamata di sistema richiede un input, l'area di I/O rimane con il bordo in colore ciano invece di grigio, e l'utente deve digitare un input prima di fare qualsiasi altra cosa.

Ogni riga della Memoria mostra un indirizzo e il valore di una locazione di memoria. Possiamo scorrere il contenuto della memoria tramite la barra di scorrimento sulla destra. Si possono vedere i valori memorizzati in memoria in sei differenti formati. Si può selezionare il formato utilizzando il menù di popup. Nella figura sopra, la memoria è riempita con zeri, e zero come comando assembler rappresenta l'istruzione add $0 $0 $0.

Il formato Signed Decimal mostra i numeri binari a 16-bit come numeri decimali nel range da -32768 a 32767. Per il formato Unsigned Decimal, il range dei numeri va da 0 a 65535. Per numeri nel range 0 - 32767, i due formati danno lo stesso risultato.

Nel formato Character Display, alcuni caratteri non visibili sono mostrati nel formato ad esempio '<13> per il carattere numero 13. In particolare lo zero è mostrato come '<0>. I caratteri ASCII stampabili verranno presentati correttamente.

Similmente, si possono vedere i sedici registri in formati differenti, ma solo i quattro formati numerici sono disponibili. Nota che si possono modificare il contenuto dei registri, eccetto per il registro zero il cui valore è sempre zero. Si può digitare un numero dentro i registri utilizzando uno qualsiasi dei quattro formati; non siamo obbligati a utilizzare il formato correntemente selezionato. Quando digitiamo un nuovo valore, l'input verrà controllato nella correttezze quando diamo l'invio o quando il registro perde il focus di input. Si può utilizzare il bottone "Step" per eseguire il programma una istruzione alla volta, o il bottone "Run" per riprendere l'esecuzione automatica.



Da ultimo, il pannello di controllo nella sezione in alto a sinistra della finestra contiene i bottoni e gli input per controllare il Larc computer. Clicca "Step" per eseguire il ciclo fetch-and-execute di una istruzione. Clicca "Run" per lasciare il computer eseguire normalmente il programma, automaticamente caricando ed eseguendo le istruzioni finché il programma termina. Quando il programma è in esecuzione, il bottone "Run" cambia in "Stop", che può essere cliccato se si vuole interrompere la normale esecuzione del programma. Il bottone "Reset CPU" setterà a zero il PC e tutti i registri. Quando il computer è in esecuzione di un programma, molti dei bottoni e degli input sono disabilitati.

Quando il check box "Run Fast" è selezionato, il computer eseguirà le istruzioni più velocemente (circa 300 istruzioni al secondo) invece della velocità usuale (circa 5 istruzioni al secondo) . Il bottone "Stop at Breakpoint" sarà discusso sotto.

Quando il computer esegue un'istruzione (eccetto quando esegue in fast mode), utilizza il colore di background nei registri e in memoria per aiutare a visualizzare l'effetto delle istruzioni. Un registro il cui valore è utilizzato nell'istruzione ha il colore di background settato a blu. Un registro il cui valore è modificato ha il colore di background settato a giallo. Il display della memoria scrolla automaticamente al centro del display per mostrare le locazioni di memoria accedute più recentemente, e il background color di quella locazione è settato. Ad eccezione delle istruzioni di load e store, le più recenti locazioni accedute sono quelle che contengono le istruzioni che sono state eseguite. Per un'istruzione di memoria, la più recente locazione acceduta è quella il cui valore è letto o scritto.

L'input del Program Counter rappresenta il PC della CPU Larc. Il suo valore è l'indirizzo in memoria dell'istruzione della prossima istruzione da eseguire. Nota che si può modificare il valore finché il computer non è in esecuzione. Il valore verrà utilizzato quando clicchiamo "Run" o "Step".

Il bottone "Poke" e gli input a fianco permettono di immettere valori nella RAM. (Non si può editare la Memoria direttamente dal display.) Il primo input box serve per il valore che può essere in uno qualsiasi dei sei formati della memoria. Il secondo input è l'indirizzo della memoria dove il valore sarà salvato. Premendo l'invio in uno degli input, è come premere direttamente il bottone "Poke". Nota che quando facciamo questo, l'indirizzo di input è incrementato e l'input per il valore è selezionato, in modo che si possa scrivere il prossimo valore. Questo rende semplice immettere un insieme di valori in locazioni sequenziali di memoria, così da poter immettere brevi programmi a mano.

Cliccando il bottone "Load Memory From File" permetterà di ricercare un file in linguaggio assembler da caricare in memoria del simulatore. assumando che non ci siano errori di sintassi nel programma, tutte le locazioni di memoria verranno settate a zero e i valori dal file verranno salvati in memoria. Anche, l'input del Program Counter verrà settato a zero, che è il più appropriato generalmente punto di inizio per l'esecuzione di un programma. Il bottone "Save Memory" permette di salvare in output file il contenuto della memoria. L' output è in formato esadecimale. Questo non mostrerà i comandi in linguaggio assembler o stringhe, ma l'output file può essere caricato di nuovo in Larcsim per duplicare esattamente la memoria che era stata salvata.

Se mandiamo in esecuzione larcsim da riga di comando, si può specificare un file di un programma in assembler che viene caricato in partenza. Se, ad esempio, se larcsim.jar e myprogram.txt sono nella directory corrente, possiamo mandare in esecuzione larcsim con il comando:

java -jar larcsim.jar myprogram.txt

e larcsim tenterà di caricare un programma in linguaggio assembler dal file myprogram.txt.

Debugging con Larcsim: i Breakpoint

Un debugger è un programma che controlla l'esecuzione di un altro programma e può mostrare le informazioni circa lo stato corrente di quel programma, come il valore delle sue variabili e la prossima istruzione nel programma. Un debugger permette di eseguire un programma passo a passo. E dovrebbe poter permettere di settare dei breakpoint ("punti d'interruzione") nel programma. Quando un programma è in esecuzione e si arriva a un breakpoint, l'esecuzione si dovrebbe fermare in modo da poter ispezionare lo stato corrente dell'esecuzione.

Larcsim è il simulatore e debugger per programmi in linguaggio macchina Larc. Esso permette di eseguire un programma a passi di una istruzione in linguaggio macchina alla volta. E permette di settare dei breakpont nel programma.

Per settare un breakpoint a qualche istruzione nel programma, bisogna cliccare alla locazione di memoria che contiene l'istruzione. Si otterrà un menù a pop-up con l'opzione di poter settare un breakpoint. (Se un breakpoint è già stato settato a quell'indirizzo, si ha la possibilità di rimuoverlo). Quando c'è un breakpoint in una locazione, quella locazione è bordata in blu scuro sul display della memoria. Da notare che non è possibile settare un breakpoint mentre un programma è in esecuzione o quando un programma sta aspettando l'input di un utente.

Quando un programma è in esecuzione e arriva a un breakpoint, esso si interrompe appena prima di eseguire l'istruzione al breakpoint (a meno che "Strop at Breakpoints" è non settato). In questo modo si possono ispezionare i valori nei registri a in memoria, e si possono modificare i valori se si vuole. Si può anche cambiare il valore del Program Counter.


Per eseguire Larcsim sul vostro computer, dovete avere installato un JDK (Java Development Kit) ad esempio uno scaricato da adoptium.net. Se avete installato un JDK, potete probabilmente azionare il programma semplicemente con un double-click sul file larcsim.jar.

Possiamo sempre mandare in esecuzione Larcsim da riga di comando (assumendo di avere installato un JDK) utilizzando il seguente comando in una directory contenente il file larcsim.jar.

java -jar larcsim.jar

Nel nostro caso quindi dalla directory:

PS C:\SCUOLA> java -jar .\larcsim.jar

Algoritmo 3N + 1

Consideriamo il seguente algoritmo:

  1. Si prenda un intero positivo n.

  2. Se n = 1, l'algoritmo termina.

  3. Se n è pari, si divida per due; altrimenti si moltiplichi per 3 e si aggiunga 1.

O, algebricamente:

f(n)={n2,se n pari 3n+1,se n dispari f(n) = \begin{cases} \frac n 2,&\text{se } n\text{ pari }\\ 3n + 1, &\text{se } n\text{ dispari } \end{cases}

È possibile formare una successione applicando la funzione ripetutamente prendendo come primo elemento un qualunque intero positivo e, ad ogni passaggio, applicare la funzione al risultato precedente, cioè:

ai={n,per i=0,f(ai1),per i>0,a_i = \begin{cases} n ,&\text{per } i = 0,\\ f(a_{i-1}), &\text{per } i > 0, \end{cases}

Per esempio, iniziando con n = 6, otteniamo la successione 6, 3, 10, 5, 16, 8, 4, 2, 1.

Il file 3N+1.s che implementa l'algoritmo:

# Computes the 3N+1 sequence starting from 3 and counts the
# number of terms in the sequence.  The terms of the 
# sequence appear in Register 5, and Register 6 is used
# for counting.

li $4 3  # Initialize register 5 with the first term, N, of the sequence.
li $5 1  # Initialize counting register with one, which counts the initial term.

li $1 1  # Store the constants used in the calculation in registers 1, 2, and 3.
li $2 3
li $3 15


add $10 $0 $4   # Copy N into register 10
sub $10 $10 $1  # Subtract 1 from (copy of) N
beqz $10 10     # If result is zero, the sequence is done, jump to end.

add $5 $5 $1    # Add 1 to register 6, to count this step in the sequence

add $10 $0 $4   # Copy N into register 10.

sll $10 $10 $3  # Set register 10 to the low-order bit in N.
srl $10 $10 $3  #     so register 10 will be non-zero when N is odd.

bnez $10 2      # If N is odd, jump ahead by 2 (skipping next two instructions.

srl $4 $4 $1    # Divide N by 2.
beqz $0 -10     # Unconditional jump back 10 instructions, to start of loop.

mul $4 $4 $2    # Multiply N by 3.
add $4 $4 $1    # Add 1 to N.
beqz $0 -13     # Unconditional jump back 13 instructions, to start of loop.

li $1 0         # These two instructions will halt the computer.
syscall

Somma e prodotto

Esegue la somma e il prodotto di due numeri presi dall'input e ritorna il risultato nell'output. Esempio di utilizzo delle chiamate di sistema per eseguire le operazioni di Input/Output.

Il file product-and-sum.s per implementare la funzionalità:


# This program reads two ints from the user and outputs their sum
# and their product.  Note that it does not prompt the user for input,
# and it does not print new-lines after the outputs.

li $1 4        # "read int" syscall, to read the first input number
syscall

add $2 $0 $1   # copy the first input integer into register 2

li $1 4        # for reading the second input number
syscall

add $3 $0 $1   # copy the second input integer into register 3

add $4 $2 $3   # compute the sum of the two inputs
mul $5 $2 $3   # compute the product of the two inputs

li $1 2        # "print int" syscall
add $2 $0 $4   # put the sum into register 2, which holds the value to be printed
syscall

li $1 2        # "print int" syscall
add $2 $0 $5   # put the product into register 2, which holds the value to be printed
syscall

li $1 0        # "halt" syscall
syscall

Il programma Hello World

Un semplice programma, hello-world.s, che scrive in output il messaggio "Hello World":

# An Hello World program for the Larc Simulator
             
li $1 1         # trap number for Print String
li $2 16        # "Hello World" is stored at location 16
li $3 20        # maxlength for the string; larger than actual length is OK
syscall         # call the Print String trap
             
li $1 0         # trap number for Halt
syscall         # halt
             
@16             # store the following, starting at location 16
"Hello World

Last updated