Vai al contenuto

Personal computer/Linguaggio Macchina

Wikibooks, manuali e libri di testo liberi.
Indice del libro

Linguaggio Assembler

[modifica | modifica sorgente]

Il linguaggio macchina è una sequenza di bit e la sua rappresentazione simbolica è l'assembler, costituisce il livello dell'architettura visibile agli sviluppatori dei compilatori.

Le istruzioni del linguaggio macchina devono permettere di utilizzare le risorse interne del calcolatore, e quindi di accedere alla memoria, ai registri interni alla cpu e di eseguire delle operazioni, per le diverse implementazioni di questo aspetto, i linguaggi macchina sono classificabili in base a

  • Memorizzazione dell'operando nella CPU
  • Numero di chiamate esplicite agli operandi per istruzione
  • Collocazione degli operandi
  • Indirizzamento alla memoria
  • Operazioni
  • Tipologia e dimensione degli operandi

Supponiamo di voler eseguire una semplice operazione quale una somma tra due numeri (A e B) e di voler salvare il risultato in un registro interno alla cpu, il linguaggio macchina deve quindi fornire una codifica per l'operazione che voglio eseguire e gli operandi (indirizzo dei valori in ingresso e del registro in uscita) per cui si può generalizzare indicando il seguente formato per una istruzione assembler:

etichetta codice_mnemonico operandi.

L'etichetta è un parametro opzionale e rappresenta in modo simbolico l'indirizzo dell'istruzione; è utile per la gestione del flusso;

il codice_mnemonico è un identificativo per il codice operativo;

operandi è una lista di operandi separati di virgola, il primo dei quali è quello di destinazione

Memorizzazione dell'operando nella CPU

[modifica | modifica sorgente]

In generale tutte le architetture sono classificabili in base ai tre tipi di approccio:

  • Stack Architecture: Tutti gli operandi sono sullo stack; in particolare una qualsiasi istruzione usa come operandi sorgente gli ultimi due sullo stack, li cancella e mette l'operando risultato in cima allo stack
  • Accumulator Architecture: Uno dei due operandi sorgente è sempre il valore di un registro speciale detto accumulatore; il risultato dell'operazione viene messo di nuovo sull'accumulatore
  • General-Purpose Register (GPR) Architecture: gli operandi risiedono tutti su registri interni alla CPU oppure in memoria.

Vantaggi e svantaggi dei diversi tipi di approcci sono legati alla misura in cui la macchina si adatta alle necessità del compilatore ed a quanto risulta efficiente il codice rispetto alle altre metodologie. Per questo motivo tutte le macchine moderne utilizzano registri GPR, nel qual modo vengono velocizzate tutte le operazioni. Il tempo di accesso ad un registro è inferiore rispetto a quello della memoria e viene pure fornito uno strumento generale di memorizzazione più semplice da usare.

Da notare inoltre che gli altri due approcci non consentono l'accesso casuale e la gestione di pipeline, ad esempio l'esecuzione dell'operazione (A*B)-(C*D) deve essere eseguita sequenzialmente, mentre utilizzando i registri è possibile eseguire le operazioni tra parentesi in qualsiasi ordine e quindi anche in pipeline.

Va comunque detto che, nonostante l'affermazione delle macchine GPR, esistono degli "ibridi": proprio l'Intel 8086 è una via di mezzo tra una macchina GPR ed una ad accumulatore.

Numero di chiamate esplicite agli operandi per istruzione

[modifica | modifica sorgente]

Il numero degli operandi che sono chiamati in modo esplicito in una tipica istruzione dipende fortemente dal tipo di memorizzazione. Ad esempio l'istruzione C = A + B può essere tradotta nelle tre categorie di insiemi di istruzioni:

Pila

PUSH A Metti A nello stack

PUSH B Metti B nello stack

ADD Esegui la somma tra gli ultimi due valori nello stack

POP C Prendi C dallo stack


Accumulatore

LOAD A Carica A sull'accumulatore

ADD B Aggiungici B

STORE C Salva in C il valore dell'accumulatore


Registro

LOAD R1,A Inserisci A nel registro R1

ADD R1,B Somma il registro R1 con B e salva il valore in R1

STORE C,R1 Inserisci il valore di R1 in C


In questo esempio il numero di operandi espliciti per la sola operazione di ADD è rispettivamente 0,1,2 - ma nel caso di macchine GPR può anche essere tre. Infatti, nell'esempio considerato l'istruzione ADD R1,B esegue la somma tra R1 e B salvando il risultato nella posizione del primo operando. Ma per certi Instruction Set esistono anche operazioni del tipo ADD R1,R2,R3 a seguito della quale la somma tra i valori di R2 ed R3 viene salvata nel registro R1.

Riassumendo quindi il numero di operandi espliciti per un'istruzione della ALU è compreso tra 0 e 3.

Collocazione degli operandi

[modifica | modifica sorgente]

Oltre alla presenza di due o tre operandi in una istruzione ALU, un'altra caratteristica che differenzia le architetture GPR è il numero di operandi in memoria consentiti in una tipica istruzione che può variare da zero a tre.

Le possibili combinazioni di numero di operandi e numero di indirizzamenti a memoria sono sette, ma bastano tre per classificare quasi tutte le macchine esistenti:

  • register-register (3,0)
  • memory-register (2,1)
  • memory-memory (3,3)

La coppia tra parentesi indica il (numero di operandi espliciti di un'istruzione ALU), (numero di operandi risiedenti in memoria)

Le macchine R-R (register-register) hanno il vantaggio di codificare più semplicemente le istruzioni (in quanto a lunghezza fissa) che risultano anche essere più corte (lo spazio dei registri è indirizzabile con meno bit rispetto a quello della memoria) tuttavia questo comporta un più alto numero di istruzioni per riferimenti in memoria. D'altro canto le M-M non sprecano registri per memorizzare temporaneamente i dati ma presentano un collo di bottiglia nella velocita di accesso alla memoria.


Indirizzamento alla memoria

[modifica | modifica sorgente]

Indipendentemente dal tipo di architettura a registri o con riferimenti alla memoria, è necessario definire il metodo di interpretazione degli indirizzi di memoria e la loro specifica.

Interpretazione degli indirizzi di memoria

[modifica | modifica sorgente]

Esistono due diverse convenzioni per ordinare i byte all'interno di una parola:

  • Little Endian ordinamento a partire dal byte meno significativo, l'indirizzo di un dato è quello del suo byte meno significativo
  • Big Endian ordinamento a partire dal byte più significativo, l'indirizzo è quindi quello del suo byte più significativo.

La scelta di una delle due tecniche è del tutto convenzionale visto che nessuna presenta vantaggi/svantaggi rispetto all'altra, ed è possibile applicare la stessa tecnica di posizionamento anche ai bit nonostante poche architetture dispongano di istruzioni che accedono direttamente ad essi. Un esempio di microprocessore Little Endian è un qualsiasi Intel della famiglia 80x86; i microprocessori Motorola sono invece Big Endian.

Ben più importante è l'allineamento. Alcune macchine (ormai quasi tutte) richiedono che l'accesso ad un oggetto di s byte, collocato all'indirizzo A, sia allineato con l'indirizzo, ovvero la divisione tra A ed s dia resto 0. Ad esempio una parola da 4 byte richiede che il suo indirizzo sia multiplo di 4.

Sebbene possa sembrare una limitazione, l'allineamento rende l'esecuzione dei programmi più veloce.


Modalità di Indirizzamento

[modifica | modifica sorgente]

La modalità di indirizzamento è il modo in cui le architetture specificano l'indirizzo degli oggetti a cui accedere, che possono essere una costante, un registro o una locazione di memoria. Le modalità principali sono:

  • Registro ADD R4,R3

Il valore dell'indirizzo è un registro

  • Immediato ADDI R4,10

Il valore dell'operando sorgente è una costante

  • Offset ADD R4,100(R1)

Accede a variabili locali, l'indirizzo dell'operando sorgente è calcolato come somma di 100 ed il valore di R1.


Le modalità di indirizzamento possono ridurre anche sensibilmente il numero di operazioni da effettuare a discapito di una maggiore complessità della macchina.


Gli operatori supportati dalla maggior parte degli Instruction Set possono essere raggruppati nelle seguenti categorie:

  • Aritmetico / Logiche: Operazioni aritmetiche (somma, sottrazione...) o logiche (and, or...).
  • Trasferimenti di dati: Istruzioni di trasferimento per macchine con indirizzamento in memoria.
  • Controllo: Diramazione (branch), salto (jump), chiamata a procedura (call), trappole (trap), permettono il controllo del flusso di esecuzione di un programma modificando il valore del Program Counter (PC, contiene il valore dell'indirizzo della prossima istruzione da eseguire).
  • Sistema: Chiamate al sistema operativo, istruzioni per la gestione della memoria virtuale.
  • Virgola mobile: Operazioni con numeri rappresentati in virgola mobile (floating point).
  • Stringa: Spostamento di stringhe, confronto fra stringhe, ricerca su una stringa.

Tipologia e dimensione degli operandi

[modifica | modifica sorgente]

Solitamente il tipo stesso di un operando fornisce la sua dimensione. I tipi più comuni sono il carattere (1 byte, 8 bit), mezza parola (16 bit), parola (32 bit), virgola mobile in singola precisione (32 bit) o in doppia precisione (64 bit). I caratteri vengono rappresentati nel formato EBCDIC (IBM) o più frequentemente in ASCII (attualmente anche UNICODE). I numeri in virgola mobile sono ormai quasi tutti rappresentati seguendo lo standard IEEE 754 anche se in precedenza ogni costruttore sceglieva una propria rappresentazione.

I numeri naturali vengono rappresentati con numeri binari in complemento a due.

Rappresentazion in complemento a due

[modifica | modifica sorgente]

Dato un numero binario qualsiasi ad n bit (), il valore decimale lo si calcola come Tuttavia con questa rappresentazione sono rappresentabili soltanto numero positivi.

Una rappresentazione alternativa è quella in complemento a 2 che permette con un numero binario a n bit un numero decimale compreso tra . Con questa rappresentazione il bit di maggior peso è quello di segno (0 indica un numero positivo, 1 uno negativo) e il valore decimale può essere calcolato come: . Questa operazione permette di eseguire la sottrazione di due numeri qualsiasi con un sommatore, infatti A-B è equivalente alla somma tra A e il complemento a 2 di B. Un numero negativo si ottiene anche sommando 1 alla negazione del numero in valore assoluto, per cui se con n=4 bit posso rappresentare il numero 6 come 0110, -6 in complemento a due sarà 1 + not(0110) = 1 + 1001 = 1010.

Assembler DLX

[modifica | modifica sorgente]

Assembler IA16

[modifica | modifica sorgente]