Architettura del Calcolatore - Lezione in semi-presenza

Lezione in semi-presenza

5. L'unità centrale di processamento (Central Processing Unit, CPU)

5.2. Linguaggi della Macchina: Codificare le istruzioni

SlideSlide

SlideSlide

SlideSlide

La CPU funziona con un linguaggio non ambiguo.

Come codifichiamo le istruzioni? Con i numeri binari.

Quanti bit servono per codificare un carattere? 8 bit.

Codice ASCII per codificare i caratteri.

Come si possono codificare le istruzioni che inseriremo nell’Instruction Register?

Non ci serve sapere cosa inserire nel Program Counter, poiché già sappiamo cosa c’è e sappiamo anche a cosa serve, ovvero serve affinché il ciclo operativo funzioni. Siccome nell’Instruction Register ci deve essere un’istruzione, bisogna imparare come eseguirla.

L’Instruction Register deve essere lungo almeno una parola perché contiene l’istruzione. Proprio perché si tratta di un’istruzione, è più semplice che sia lunga due parole. Questo perché così risulterà più semplice leggerla.

Ciò che occorre codificare sono le operazioni (somma, sottrazione, divisione…), e bisogna codificare tutte quelle istruzioni che sono tipiche degli algoritmi.

Le istruzioni degli algoritmi sono ben comprensibili sfruttando i diagrammi di flusso. Tramite i diagrammi di flusso si comprende che le istruzioni non sono tutte uguali perché hanno una diversa importanza. Ognuna di queste istruzioni ha un segno diverso. Perciò bisognerà trattare ogni istruzione in un diverso modo, comprendendo cos’è che bisogna calcolare.

Se un’istruzione contiene solo due parole, non se ne possono inserire di più. Perciò, istruzioni troppo lunghe non si possono codificare.

Per fare in modo che entrino anche le istruzioni originariamente troppo lunghe, dobbiamo pensare a un modo per codificarle, in modo che entrino nell’Instruction register.

Nei diagrammi di flusso, le istruzioni erano poche, ma con quelle poche si potevano rappresentare tutte le funzioni utili a scrivere gli algoritmi.

Le tipologie di istruzioni nei diagrammi di flusso erano le affermazioni (la somma…), i salti condizionati (in cui c’è una condizione), e il salto (che fondamentalmente è il flusso).

La prima istruzione che si analizzerà è l’Istruzione di assegnazione/modifica: che rappresenta così: Assegna ad A il valore 2 (scrittura in linguaggio naturale).

Non si può scrivere ciò dentro 32, 64 bit perché non centrerebbe, infatti sappiamo che un carattere è rappresentato da 8 bit, e già per quattro caratteri avremmo utilizzato una parola. Perciò occorre un modo più sintetico per descrivere la stessa cosa.

Si potrebbe usare la scrittura A = 2 oppure A 2.

Oltre all’Instruction Register, nella CPU ci sono anche altri registri (R1; R2…) che sono gli operatori, poi c’è l’ALU che consente di svolgere le operazioni, e poi c’è la capacità di estrapolare qualche cosa dalla memoria.

Se prendiamo A e B, questi possono essere Registri o Locazioni di memoria. Si è detto che in un’istruzione lo spazio a disposizione è poco, perciò, cosa possiamo permetterci di codificare?

Al massimo possiamo permetterci di codificare un indirizzo.

A e B sono delle locazioni di memoria e delle variabili. Se A e B sono entrambi spazi di memoria, devono avere entrambi un loro indirizzo. Una volta venuto a conoscenza degli indirizzi, posso copiare il contenuto di B nella locazione di memoria A, o viceversa.

Ma non posso codificarlo, perché se volessi codificare entrambi come spazi di memoria, avrei la necessità di scrivere sia il primo indirizzo sia il secondo indirizzo.

In questo modo, però, si occuperebbe tutto l’Instruction Register senza aver indicato l’operazione da fare. Perciò due indirizzi non possono convivere, perciò, se A e B sono entrambi indirizzi, quest’operazione non è possibile.

Le operazioni che invece è possibile scrivere sono quelle in cui l’operando (ciò che voglio inserire) è una costante e i destinatari non sono locazioni di memoria, ma sono registri interni.

Se infatti possiamo inserire nell’Instruction Register solo un indirizzo, l’altro prelevato si potrà inserire in un registro della memoria. Il numero dei registri non è alto e quindi ho bisogno di pochi bit: se i registri sono quattro, ho bisogno di soli due bit.

L’operazione di mettere insieme un indirizzo e un registro, la si può fare sia in uscita che in entrata.

L’operatore è la freccetta, mentre gli operatori sono A e 2. Gli operandi prendono nomi specifici, cioè sorgente e destinazione.

Uno dei due operandi è l’indirizzo, o una costante, mentre l’altro operando deve essere un registro, perché altrimenti lo spazio per mettere le informazioni non c’è. Generalmente l’operando 1 è un registro mentre l’operando 2 è un indirizzo. L’operazione di “ sposta” è segnalata dalla freccetta.

Nell’operando B, che è un indirizzo, non posso scrivere B + 1 (dove questa B è un indirizzo) perché sarebbe come scrivere un’operazione dentro un’operazione.

B + 1 deve diventare un’operazione che deve essere svolta precedentemente.

Slide

Spostare le informazioni tra due locazioni di memoria è possibile. E’ impossibile spostarle direttamente. Per farlo bisogna prima risolvere un problema.

Per scambiare A e B occorre trovare una posizione temporanea perché non si può immediatamente scambiare A con B poiché risulterà che B è già occupato.

Questo terzo elemento viene chiamato Temp e deriva dal significato dell’operazione “sposta”

Con questa tipologia di istruzione è possibile svolgere solo le operazioni che il processore è in grado di svolgere; si tratta di un altro vincolo.

SlideSlide

Slide

La seconda tipologia di operazioni che si ha a disposizione sono Le operazioni di Salto. Si tratta di operazioni complicate da codificare in due spazietti.

C’è il SALTA SE; la CONDIZIONE; l’OPERANDO.

Se si verifica una data condizione si può operare, altrimenti no.

Quello che si sta definendo è un linguaggio che ha una specifica sintassi: è scritto in un determinato modo e segue delle regole. Inoltre, in questo linguaggio, il numero delle istruzioni è ridotto, e queste istruzioni hanno un effetto sulla macchina.

Effetto che è il significato delle istruzioni, la loro semantica.

Una volta passata l’istruzione nella macchina, qualche cosa avviene al suo interno. Un qualche cosa che in realtà è sempre la stessa cosa.

Tramite queste istruzioni si sta dicendo precisamente ciò che deve avvenire all’interno della macchina. Si deve notare, perciò, come il concetto di “ ambiguità” (che era scomodo) è stato eliminato.

Ogni macchina divisa in parti, come la macchina di Von Neumann, per funzionare devono avere un linguaggio ben specifico. Quando un qualsiasi utente usa una di queste macchine, utilizza il loro linguaggio.

La Macchina di Von Neumann è formata da una parte che esegue e da una parte che memorizza. Nessuna di queste due parti può funzionare indipendentemente dall’altra perché sono necessarie l’una all’altra.

Per fare funzionare la parte che esegue, ovvero la CPU, abbiamo bisogno di uno specifico linguaggio. Linguaggio che non deve essere ambiguo, e deve avere una sintassi particolare, perché bisogna accertarsi che faccia quello che l’utente gli dice di fare.

Le istruzioni sono di diverso tipo, ed hanno una loro classificazione:

  • istruzione di assegnazione/modifica
  • istruzione di salto e di controllo
  • istruzioni di I/O

Vediamo un esempio per l’istruzione di controllo:

se (A “maggiore” B) allora il MASSIMO è A; altrimenti il MASSIMO è B

le istruzioni di condizione prevedono: una condizione; una modifica della sequenza delle istruzioni.

Il salto condizionato è composto da:

  • frase lecita: SALTA-SE-“COND” “OPERANDO” (vedi slides).

La stessa versione del salto condizionato, in inglese, sarebbe JMP; JUMP era salta se “maggiore”. La scelta dell’italiano, dell’inglese o di altri simboli, è irrilevante, basta che ci si mette d’accordo.

Queste istruzioni si codificano tramite i numeri binari.

Parte di ciò che abbiamo a disposizione, ce lo giochiamo con l’indirizzo, mentre l’altra parte ce la giochiamo con il registro.

Supponendo che i registri siano 8 e la lunghezza totale sia di 32 bit, e la parola è da 16, quanti spazi rimangono per descrivere l’operazione?

Sapendo che la parola è di 16 bit e noi abbiamo a disposizione 32 bit, i rimanenti bit saranno 16. ma, ammesso che volessimo codificare anche i registri (che sono 8), quanti bit ci occorrerebbero? Ci servirebbero 3 cifre differenti poiché 2 alla terza da 8 come risultato.

Lo stesso discorso varrebbe se dovessimo codificare 100 cifre: siccome contiamo in base due (sistema binario), per codificare 100 cifre faremo 2 elevato alla 10.

Quindi, avendo 2 cifre e 3 posizioni, i numeri che si possono codificare sono 8 perché il risultato di 2 elevato alla 3 è 8. 3 posizioni sono 3 bit. 16 – 3 = 13. i bit che ci rimangono sono 13.

Il passaggio successivo codificare “ sposta”, “ somma”… in 13 bit. Non possiamo codificare 3 numeri, ma se ne codificassimo due ce la potremo fare? Cioè se SPOSTA lo chiamassimo SP e SOMMA lo chiamassimo SO?

Per codificare una cifra occorrono 8 bit. Questo sistema di codifica dei caratteri si chiama codice Asci. Se dobbiamo codificare due cifre, e per una sola di queste occorrono 8 bit, ne codificheremo 1 e mezzo?

Dentro 32 bit ho quindi:

  • un indirizzo
  • un registro
  • un codice operativo (che dice alla macchina cosa fare)

In realtà, per fare in modo che si riesca a codificare tutte e due queste cifre, bisogna ordinare le operazioni fin’ora elencate e ordinarle in sequenza; queste operazioni prenderanno il nome di 0.0 (operazione di “alt”), 0.1… le possiamo ordinare lessicograficamente, quindi come se appartenessero ad un dizionario. Perciò, le diverse operazioni che si possono codificare in 13 bit sono 2 elevato alla 13 (molte di più di 13).

Il codice operativo è un numero che non ha il significato di un numero di quell’operazione, ma è un numero che potrebbe rappresentare l’ordine di una lista che potremmo mettere da parte, per comprendere, successivamente, ciò che abbiamo scritto.

Abbiamo conosciuto un linguaggio, e abbiamo fatto in modo che fosse contenuto in 32 bit. E questo linguaggio ha due sintassi: una comprensibile e leggibile da parte degli utenti (umani) e l’altra leggibile da parte della macchina.

Sarà il codice operativo che dirà alla macchina cosa deve fare.

Dato questo linguaggio, una delle cose che possiamo farci è tradurre gli algoritmi. L’algoritmo tradotto nel linguaggio della macchina, non è altro che un programma.

Quanti programmi ci sono a fronte di un algoritmo specifico?

Saranno tanti quanti i diversi linguaggi di macchine. Se vogliamo comunicare con una macchina, dovremo programmarla. Programmare consiste nello scrivere l’algoritmo per una specifica macchina, che ha uno specifico linguaggio.

Ovviamente qualsiasi linguaggio, di qualsiasi macchina, ha la caratteristica di non essere ambiguo. Perciò l’operazione di programmazione è una semplice operazione di traduzione in un linguaggio che non deve essere ambiguo.

Naturalmente si tratta di un linguaggio ristretto. Perciò, se un linguaggio di una macchina non prevede lo svolgimento di una determinata operazione che in quel momento ci serve svolgere, dovremo usare una sottoparte di programma.

Se vogliamo svolgere una divisione tra due numeri interi, usiamo l’algoritmo (non è ancora il programma). Dati due numeri, svolgeremo l’operazione tramite l’algoritmo, che poi bisognerà tradurre in un programma.

Ciò che vogliamo fare è spostare ciò che è contenuto nel Registro di memoria A in AX, e poi spostare ciò che è contenuto nel registro di memoria B in BX. Questi dati possono essere svolti dall’ALU solo quando si trovano nei registri. I contenuti di A e B li abbiamo messi in AX e BX perché devono entrare nella CPU.

A è maggiore o minore di B? Se è maggiore bisognerà fare qualche cosa, se è minore di B bisognerà fare qualcos’altro. Questo qualche cosa o qualche cos’altro, consiste nel saltare da una posizione o saltare a un’altra posizione.

Comparare AX e BX, significa sottrarli. Se la comparazione è uguale a 0, bisogna passare nella posizione “ allora”. Se non è uguale a 0, l’operazione dovrà continuare.

Data una macchina, è formata da una sequenza di istruzioni. Istruzioni che chiamiamo “ linguaggio macchina” (parlando della macchina fisica).

Non tutte le macchine hanno lo stesso linguaggio, al contrario, ogni macchina ha un suo proprio linguaggio che sta a differenti astrazioni.

Se ad una macchina specifica si vuole far eseguire un’istruzione, bisogna che la si comunichi nell’unico linguaggio che può comprendere. Se noi usiamo un altro linguaggio perché non siamo a conoscenza di quello usato dalla macchina, la macchina non funzionerà mai.

Le macchine possono essere fisiche, e cioè immediatamente realizzate attraverso dispositivi fisici e il loro linguaggio viene detto linguaggio-macchina. Mentre esistono anche le macchine virtuali che non esistono fisicamente, ma anch’esse hanno un linguaggio specifico.

I linguaggi di queste macchine sono completamente definiti. Anche nelle lingue usate dagli umani, vi sono lingue completamente definite, ma poco usate, come l’esperanto ad esempio.

Il linguaggio macchina non consiste né nell’insieme degli 0 e 1 del computer, né nel vedere come è la struttura di un cellulare quando ci cade, ma è la differenza di potenziale contenuta nei transistor e cioè quanta elettricità è presente, in un determinato momento, in un transistor.

I transistor sono elementi bistabili, o elementi, piccoli e unitari che possono assumere due stati. Il linguaggio macchina è fatto dalla differenza di potenziale dei transistor. Anche l’elemento fisico che caratterizza i nostri computer è un transistor, perché ha quindi due stati.

L’algoritmo che è stato convertito, e che quindi è diventato un programma, deve andare a finire nella memoria. A eseguirlo sarà l’algoritmo vitale o sistema operativo. Ogni istruzione dovrà essere caricata, localizzata, decodificata ed eseguita, fin quando non si incontrerà l’istruzione “ alt”.

A sinistra, in figura, c’è la memoria, mentre a destra ci sono la maggior parte dei registri della CPU. I dati che ci interessano, si trovano nelle celle di memoria 200 e 202. il risultato lo vogliamo ottenere nella cella di memoria 204.

Siccome vogliamo eseguire questo programma, l’indicazione di dove la prima istruzione del programma si trova, dovrà essere molto precisa. Così, nel Program Counter verrà caricato l’indirizzo della prima istruzione (100). Poi, quest’istruzione verrà messa nel MAR, infatti, l’unico modo che si ha se si vuole comunicare con la memoria, è usare il MBR o il MAR.

Una volta caricata anche l’istruzione “101”, si avrà terminato l’operazione e, nell’Instruction Register si troverà ciò di cui si aveva bisogno.

In realtà l’operazione è svolta per metà (carica e localizza).

Una volta caricato in memoria un programma, lo si è modificato adattandolo alla memoria e precisamente a quella parte di memoria dove il programma è stato scritto. Ciò che va spostato (in questo caso 202 e 200) dipende da dove il programma andrà posto all’interno della memoria.

La Macchina di Von Neumann mischia dati ed algoritmi, carica decodifica ed esegue.

La Macchina di Von Neumann, è una macchina virtuale che, quindi, in realtà non esiste. Ogni volta che un utente usa una qualsiasi macchina, ne deve conoscere le regole e il linguaggio, quindi è l’utente che si adegua alla macchina.

Benché la Macchina di Von Neumann non esista, tutti gli elementi tecnologici, in grado di fare calcoli, sono Macchine di Von Neumann (computer, cellulare, ...)