Larc Computer e Linguaggio Macchina

"Larc" o "little architecture", è un modello di computer molto semplice con assieme l'ISA (Instruction Set Architecture), set di istruzioni per quel computer. Fu sviluppato una dozzina di anni fa da Marc Collins quando era professore al Hobart and William Smith Colleges. E' un primo esempio di ISA su cui lavoreremo.

Questo documento descrive il modello di computer Larc e il suo linguaggio macchina. Anche viene presentato un semplice assembler Larc che è solo una notazione alternativa per il linguaggio macchina. senza tutte le utili possibilità di un normale linguaggio assembler. Poi presenteremo un programma che è in grado di simulare un Larc computer.

Il computer Larc

La Larc CPU contiene 16 registri general-porpouse, in aggiunta al registro PC (Program Counter). Ogni registro contiene un numero binario di 16-bit. I registri generali sono numerati da 0 a 15, o da 0b0000 a 0b1111 in binario; quindi, il numero del registro è un numero binario di quattro bit. Il registro numero 0 ha la particolare proprietà che contiene il valore sempre di zero.

La memoria principale, o RAM, per Larc ha 65536, o 2162^{16}, locazioni, e ogni locazione contiene un numero binario di 16 bit. La locazioni sono numerate da 0 a 65535, o da 0b0000000000000000 a 0b1111111111111111 in binario. Il numero della locazione è chiamato il suo indirizzo. Nota quindi che l'indirizzo di una locazione in memoria è un numero di 16 bit.

Un'istruzione del linguaggio macchina (machine languagge, ML) per Larc è un numero di 16 bit. La struttura del linguaggio macchina è discussa sotto. Ogni locazione della RAM può contenere una istruzione, ma i contenuti delle locazioni di memoria possono essere anche interpretati in altri modi, come ad esempio un intero che significa un dato per il programma. Il significato del numero dipende da come il numero è utilizzato.

Il funzionamento del computer è il ciclo fetch-and-execute di base. Il numero a 16 bit nel PC indica l'indirizzo della locazione di memoria che contiene la successiva istruzione in linguaggio macchina da eseguire. Il computer carica l'istruzione da quell'indirizzo, aggiunge uno al PC, e poi esegue l'istruzione. Ripete questo ciclo indefinitamente, finché uno è fermato in qualche modo. Alcune istruzioni modificano il PC quando sono eseguite; queste sono le istruzioni di "branch" o "jump".

Il linguaggio macchina di Larc

Una istruzione è un numero di 16 bit. I primi quattro bit dell'istruzione sono l'opcode (codice dell'operazione) the specificano il codice che corrisponde all'istruzione, e i rimanenti dodici bit contengono i dati dell'operazione. Ci sono diversi formati dei dati nell'istruzione, in base al opcode dell'istruzione.

Le seguenti tabelle descrivono le 16 tipi di istruzioni del linguaggio macchina. Reg[n] rappresenta il valore contenuto nel registro numero n. La funzione sext(x) estende x a un numero a 16 bit conservando il segno. Il mnemonico per una istruzione è utilizzato nella notazione assembler per quella istruzione.

Istruzioni aritmetiche

I dodici bit dei dati in un'istruzione, contengono tre numeri di registri di 4 bit, che sono riferiti qua come ra, rb e rc.

Opcode
Mnemonic
Semantica

0000

add

Reg[ra] = Reg[rb] + Reg[rc]

0001

sub

Reg[ra] = Reg[rb] - Reg[rc]

0010

mul

Reg[ra] = Reg[rb] * Reg[rc]

0011

div

Reg[ra] = Reg[rb] / Reg[rc], ma se Reg[rc] è zero, il computer si ferma per errore divisione per 0.

0100

sll

Reg[ra] = Reg[rb] << (Reg[rc] & 15); shift a sinistra (con riempimento di zeri)

0101

srl

Reg[ra] = Reg[rb] >>> (Reg[rc] & 15); shift a destra (con riempimento di zeri)

0110

nor

Reg[ra] = ~(Reg[rb] | Reg[rc]); operazione logica NOR bit a bit.

0111

slt

Reg[ra] = (Reg[rb] < Reg[rc]) ? 1 : 0;

Intruzioni immediate

I primi quattro bit, ra, rappresentano il numero di un registro. I successivi otto bit, indicati come limm, rappresentano un numero intero a 8 bit con segno. "limm" sta a significare "long immediate" e un "immediate" è un campo in un'istruzione che rappresenta una costante invece di un numero di registro.

Opcode
Mnemonic
Semantica

1000

li

Reg[ra] = sext(limm); load immediate

1001

lui

Reg[ra] = limm << 8; load upper immediate

1010

beqz

if (Reg[ra] == 0) PC = PC + sext(limm); diramazione se uguale a zero

1011

bnez

if (Reg[ra] != 0) PC = PC + sext(limm); diramazione se diverso da zero

Istruzioni memoria

I bit dei dati sono due numeri di registri di 4 bit, ra e rb, e un numero con segno di 4 bit, simm. "simm" significa "short immediate".

Opcode
Mnemonics
Semantica

1100

lw

Carica (load) il contenuto dalla locazione di memoria Reg[rb] + sext(simm) nel registro Reg[ra]

1101

sw

Salva (store) il valore di Reg[ra] nella locazione di memoria a indirizzo Reg[rb] + sext(simm)

Istruzioni salto (Jump)

Due campi da 4 bit, ra e rb; gli ultimi 4 bit sono ignorati.

Opcode
Mnemonic
Semantica

1110

jalr

Jump-and-link-register: salva il valore di PC in Reg[ra] e setta il valore di PC con il valore di Reg[rb]

Istruzione di chiamate a sistema (Syscall)

Tutti i bit della parte dati sono ignorati.

Opcode
Mnemonics
Semantica

1111

syscall

Chiamata di sistema numero Reg[1].

Per esempio, il numero a 16-bit 0b00010011001001111 rappresenta una operazione di sottrazione (codice 0001) applicata ai numeri di registro 3, 2 e 7 (numeri binari 0011, 0010 e 0111). Cioè, quando viene eseguita, i valori contenuti nei registri 2 e 7 sono aggiunti, e il risultato è copiato nel registro 3.

il numero 0b0000100011110000 significa aggiungere il valore del registro 15 e 0, e copiare il risultato nel registro 8. Poiché il valore nel registro 0 è sempre zero, questo ha l'effetto di copiare il valore dal registro 15 nel registro 8.

O consideriamo 0b1100000100100011. Questo è il comando "load" con argomenti 1, 2 e 3. Prende il valore dal registro 2 e aggiunge il numero 3 a quel valore, e utilizza il risultato come indirizzo di una locazione di memoria. Il valore contenuto in quella locazione è copiato nel registro 1. (Il valore SIMM nell'istruzione sarà molte volte zero. Valori non zero sono delle volte utili quando vogliamo caricare una sequenza di valori da locazioni di memoria consecutive.)

Il comando load-upper-immediate (lui) esiste perché un limm è un valore a 8 bit, ma il registro contiene valori a 16 bit. Il comando lui carica una valore a 8 bit nella parte più a sinistra, o superiore, degli otto bit di un registro, e riempie con zeri gli altri otto bit più bassi del registro. Nota che può essere difficile avere una costante arbitraria di 16 bit all'interno di un registro; richiede una combinazione di lui, li e altri comandi.

Il comando jalr può essere utilizzato per "saltare" a qualsiasi indirizzo di memoria. Prima di saltare, salva il valore corrente del PC in un registro per rendere possibile poi tornare indietro a quell'indirizzo in seguito; questo può essere fatto per invocare e ritornare da una subroutine. Le istruzioni di selezione, beqz e bnez, possono essere utilizzate per saltare e indirizzi vicini. Esse aggiungono un certo valore al valore del PC. La costante è trattato come valore con segno a 8 bit quindi nel range da -128 a 127. Da notare che la costante è aggiunta al valore del PC che è già stato incrementato come parte del ciclo fetch-and-execute, quindi la costante da una differenza (offset) rispetto l'istruzione che segue l'istruzione di branch in memoria.

L'istruzione syscall ha bisogno di maggiori spiegazioni. Esso ha senso se un programma è in esecuzione sotto il controllo di un sistema operativo. In un computer normale, il sistema operativo ha accesso completo a tutti gli aspetti del computer, mentre gli altri programmi hanno un accesso limitato. L'istruzione syscall è utilizzata per chiamare subroutine del sistema operativo. Questo è diverso rispetto al chiamare una normale subroutine perché significa rimuovere le restrizioni che si applicano ai normali programmi. Noi non implementeremo un sistema operativo, ma il simulatore simula alcune subroutine di sistema per dare a syscall qualcosa di utile da fare. Nota che in un computer reale, un errore di sistema, come la divisione per zero, anche trasferisce il controllo al sistema operativo.

Il linguaggio macchina Larc come istruzioni del Linguaggio Assembly

Una istruzione in linguaggio macchina è un numero binario di 16 bit. Ma per i nostri scopi, possiamo anche rappresentare le istruzioni del linguaggio macchina in forma testuale, utilizzando i mnemonici dalla tabella qui sopra. Una istruzione è rappresentata dai codici mnemonici seguita dai suoi argomenti, separati da spazi.

In un comando in linguaggio assembler, un argomento che è un numero di registro è rappresentato da un $ seguito da un numero decimale nel range da 0 a 15: $0, $1, $2, $3, ..., $15. Un valore immediato limm, che è un numero binario a 8 bit può essere rappresentato da una costante decimale nel ragne -128 a 255, da una costante esadecimale nel range da 0x00 a 0xFF, da una costante binaria da 0b00000000 a 0b11111111. Similmente, il valore simm in una istruzione di memoria, che è un numero binario a 4 bit, può essere rappresentato da una costante decimale nel range da -8 a 7, da una costante esadecimale nel range da 0x0 a 0xF, o da una costante binaria nel range da 0b0000 a 0b1111.

Alcuni esempi: add $2 $2 $3, beqz $2 -13, li $1 0xF3, sw $3 $2 0, jalr $11 $6, syscall

La seguente tabella mostra la sintassi del linguaggio assembler per le istruzioni in linguaggio macchina del Larc computer. Qui ra, rb e rc rappresentano i registri con numeri che vanno da 0 a 15. LIMM e SIMM rappresentano costanti con il numero appropriato di bit. Ci nono due possibili istruzioni per quelle di tipo memoria, perché il valore SIMM può essere omesso dall'istruzione in linguaggio assembler se il valore è zero.

add $ra $rb $rc

Reg[ra] = Reg[rb] + Reg[rc]

sub $ra $rb $rc

Reg[ra] = Reg[rb] - Reg[rc]

mul $ra $rb $rc

Reg[ra] = Reg[rb] * Reg[rc]

div $ra $rb $rc

Reg[ra] = Reg[rb] / Reg[rc]

sll $ra $rb $rc

Reg[ra] = Reg[rb] << Reg[rc]

srl $ra $rb $rc

Reg[ra] = Reg[rb] >>> Reg[rc]

nor $ra $rb $rc

Reg[ra] = ~(Reg[rb] | Reg[rc])

slt $ra $rb $rc

Reg[ra] = (Reg[rb] < Reg[rc])? 0 : 1

li $ra LIMM

Reg[ra] = sext(LIMM)

lui $ra LIMM

Reg[ra] = LIMM << 8

beqz $ra LIMM

if Reg[ra] == 0, then PC = PC + sext(LIMM)

brnez $ra LIMM

if Reg[ra] != 0, then PC = PC + sext(LIMM)

lw $ra $rb

Reg[ra] = Mem[ Reg[rb] ]

sw $ra $rb

Mem[ Reg[rb] ] = Reg[ra]

lw $ra $rb SIMM

Reg[ra] = Mem[ Reg[rb] + sext(SIMM) ]

sw $ra $rb SIMM

Mem[ Reg[rb] + sext(SIMM) ] = Mem[ra]

jalr $ra $rb

temp = PC; PC = Reg[rb]; Reg[ra] = temp

syscall

call system subroutine number Reg[1]

Tabella di riepilogo istruzioni e chiamate di sistema

Last updated