A.L.U.
L’acronimo A.L.U. nel mondo degli elaboratori digitali sta ad indicare Arithmetic Logic Unit
ed è normalmente uno dei costituenti fondamentali di un microprocessore.
L’unità aritmetico logica esegue, come suggerisce il suo nome, delle operazioni aritmetiche
sui dati presenti in memoria; anche se possiamo dire che le A.L.U. dei microprocessori
eseguono prevalentemente delle somme.
Questo fatto è giustificato considerando che la sottrazione non è altro che l'operazione contraria
della somma, la moltiplicazione è una somma ripetuta, la divisione una sottrazione ripetuta,
l'elevamento a potenza è una moltiplicazione ripetuta, etc..
Dovendo eseguire prevalentemente delle somme dobbiamo tener conto dei dispositivi digitali che possiamo usare per eseguire queste. Abbiamo già visto come sia possibile costruire un sommatore a 4 bit semplicemente con delle porte logiche.
Tramite opportuni artifici è possibile far effettuare a questo apparato sia somme che sottrazioni.
Dobbiamo poi pensare di poter mantenere il risultato dell'operazione effettuata
in una memoria temporanea dalla quale sia possibile poter prelevare il dato.
Un registro
ad ingresso parallelo - uscita parallela costruito con dei latch
può essere adatto a questo scopo.
così possiamo collegarlo al dispositivo precedente
Questo è un dispositivo che funziona con 4 bit, quindi non possiamo illuderci
di poter fare un gran che come operazioni di conteggio, al massimo contiamo
da 0 a 15.
Possiamo allora pensare di aumentare la dimensione della parola utilizzando
dispositivi ad 8 bit costruiti con la stessa tecnologia .
Anche in questo caso si usano dei latch (flip-flop)
per memorizzare il valore ad 8 bit, inoltre apporteremo una modifica
importante, useremo come secondo operando (B) l'uscita del registro,
in modo da poter sommare il nuovo operando (A) al risultato precedente realizzando
in questo modo quello che normalmente viene definito un accumulatore.
Per usare questo dispositivo, si attiva dapprima l'ingresso CLEAR (cancellazione)
per azzerare i contenuti memorizzati nel latch. Usiamo quindi i pin di ingresso
(A) per immettere il nostro primo numero. Dopo aver premuto il comando ADD,
il sommatore, somma semplicemente tale numero all'output zero del registro
, quindi il risultato coincide con il numero che è stato immesso. Se ora
impostiamo in ingresso un altro numero e riattiviamo il comando ADD il totale
tra il nuovo numero immesso e quello presente precedentemente in uscita.
In questo modo funziona un registro accumulatore, con l'unico limite che
in uscita non potrà essere riportato un un numero superiore a 255 : il massimo
raggiungibile con 8 bit.
Il maggior problema che riguarda questo sommatore è abbastanza ovvio: immaginiamo
di dover sommare 100 numeri binari, ci sediamo e cominciamo ad inserire
i dati, terminato il lavoro scopriamo che un paio di numeri sono stati immessi
in modo sbagliato, bisogna quindi ripetere l'intera procedura.
Una soluzione a questo problema consiste nel memorizzare preventivamente
questi numeri in una memoria. Per stare comodi, useremo una RAM
da 64Kb (64K×8).
Se abbiamo digitato i nostri 100 numeri in questa RAM anziché direttamente
nel sommatore, effettuare le eventuali correzioni dovrebbe essere più semplice.
Adesso bisogna effettuare il collegamento tra la RAM e il sommatore. E'
ovvio che i segnali Dati Out della RAM devono essere collegati ai pin di
ingresso del sommatore. Per automatizzare
le operazioni useremo poi un contatore
a 16 bit in modo da poter indirizzare tutta la RAM da 0000h a FFFFh; scansionando
le celle della RAM una ad una.
Per usare un apparato del genere dobbiamo in primo luogo attivare il segnale
di CLEAR per azzerare il sommatore ed impostare a 0 (0000h) il contatore
a 16 bit.
All'inizio, memorizziamo manualmente, tramite il pannello di controllo i
nostri numeri nella RAM, dopo aver preventivamente azzerato tutti i valori
della stessa. Dopo che il Comando CLEAR ha azzerato il sommatore il contatore
a 16 bit inizia ad marciare partendo da 0000h.
A questo punto, ipotizzando che il clock sia sufficientemente lento da consentire
agli altri componenti del circuito di operare su ciascuna battuta del clock;
il contatore a 16 bit verrà incrementato e verrà indirizzato il valore successivo
nella RAM che sarà inviato e processato dal sommatore: un nuovo numero,
dunque, dovrebbe apparire in uscita come somma di quelli precedenti.
Un problema relativo a questo circuito è che non c'è modo di arrestarlo,
una volta fatto partire il contatore. Ad un certo punto il valore di uscita
si fermerà sul totale perché tutti i numeri rimanenti nella RAM saranno
0.
Se lo lasciamo libero di funzionare il dispositivo raggiungerà la fine del
conteggio a 16bit (FFFFh) arrivati a questo punto bisogna dobbiamo trovare
il modo di fermare il contatore altrimenti questo ripartirà da 0000h per
risommare i numeri memorizzati in un nuovo totale che diventerà il doppio
del precedente. Allo stato attuale l'unico modo di fermare la sequenza di
elaborazione è quello di interrompere il segnale di clock.
Teniamo presente che il sommatore somma numeri a 8 bit e ciascun numero presente nella RAM è limitato a 25510. Il sommatore è per ora un'addizionatore che non prevede il modo per effettuare sottrazioni anche se si possono usare numeri negativi in complemento a 2; in tal caso l'intervallo di valori sui quali sarà possibile eseguire somme algebriche è compreso tra -128÷127.
Osserviamo che questo circuito esegue soltanto la somma di 100 numeri,
restituendo il totale alla fine della sequenza di conteggio.
Immaginiamo, invece, di avere l'esigenza di sommare tra loro 50 coppie di
numeri e di avere la necessità di accedere a ciascun singolo totale; bisogna
trovare il modo di azzerare il sommatore dopo ogni singola coppia di numeri
trovata nella RAM ma sarebbe comunque difficile annotare su carta ciascuna
delle 50 somme previste .
Bisogna cambiare approccio e trovare il modo di registrare il risultato
delle 50 operazioni direttamente nella RAM modificando il circuito nel modo
seguente.
In questo circuito sono state cancellate alcune parti, come il comando
di esecuzione ADD e l'ingresso di cancellazione CLEAR, Questi elementi,
sono stati rimossi perché non è più del tutto ovvia la provenienza degli
input.
Il pulsante di cancellazione CLEAR è stato invece spostato sul contatore,
inoltre, ora che facciamo uso degli input dati della RAM, abbiamo bisogno
di un modo per controllare la scrittura nella RAM.
Per il momento lasciamo perdere queste problematiche hardware, ricordandoci che stiamo cercando una configurazione più generale del sommatore, in modo che non sia semplicemente limitato a calcolare un totale di un gruppo di numeri, ma vogliamo avere la completa libertà su quel che riguarda la quantità di numeri addizionabili e la quantità di somme differenti salvate nella RAM per un esame successivo.
Immaginiamo, per esempio, di voler sommare fra loro tre numeri e quindi voler sommare fra loro due numeri e poi sommare altri tre numeri tra loro. Potremmo immaginare di digitare questi numeri nella matrice RAM iniziando all'indirizzo 0000h in modo tale che il contenuto della memoria appaia simile a quello che segue:
Nella mappa della memoria i numeri sono codificati in esadecimale (postfisso h) questo perchè il sistema esadecimale ha una codifica più compatta del sistema decimale o binario.
Vogliamo memorizzare nelle celle della RAM lasciate vuote la somma dell'insieme
di numeri memorizzati precedentemente.
Le operazioni da eseguire sono di vario tipo e sono le seguenti:
● Caricamento del valore all'indirizzo 0000h
nell'accumulatore;
● Addizione del valore all'indirizzo 0001h
nell'accumulatore;
● Addizione del valore all'indirizzo 0002h
all'accumulatore;
● Memorizzazione del contenuto dell'accumulatore
all'indirizzo 0003h;
● Caricamento del valore all'indirizzo 0004h
nell'accumulatore;
● Addizione del valore all'indirizzo
0005h all'accumulatore
● Memorizzazione del contenuto all'accumulatore
all'indirizzo 0006h;
● Caricamento del valore all'indirizzo 0007h
dell'accumulatore;
● Addizione del valore all'indirizzo0 0008h
all'accumulatore;
● Addizione del valore all'indirizzo 0009h
all'accumulatore;
● Memorizzazione del contenuto dell'accumulatore
all'indirizzo 000Ah;
● Arresto del sommatore .
Si possono, dunque, individuare quattro tipi di operazioni diverse: Caricamento, Addizione, Memorizzazione e Arresto.
Non è sufficiente avere un gruppo di numeri nella RAM e attendere che il sommatore automatizzato svolga l'operazione corretta. Per ciascun numero nella RAM, abbiamo anche la necessità di una sorta di codice che indichi quale attività sta svolgendo il sommatore appunto: Caricamento, Addizione, Memorizzazione e Arresto.
Il modo più semplice (ma certamente non il più economico) per memorizzare
questi codici consiste nell'uso di una matrice RAM completamente separata.
E' possibile accedere questa seconda RAM contemporaneamente alla RAM principale.
Essa, anziché contenere numeri che devono essere sommati, contiene i codici
che indicano quale attività si suppone effettui il sommatore con l'indirizzo
corrispondente nella matrice RAM originale. Le due RAM possono essere rispettivamente
etichettate Dati (la matrice RAM originale) e Codice (la nuova matrice).
Abbiamo ,quindi, stabilito che il nostro nuovo sommatore deve essere in
grado di scrivere somme nella RAM originale (etichettata DATI).
La nuova RAM (etichettata CODICE) invece verrà scritta attraverso i suoi
propri pin di ingresso.
Notiamo che ogni istruzione nella RAM Codice corrisponde ad un valore nella RAM Dati che sarà caricato o sommato all'accumulatore oppure memorizzato nella stessa RAM Dati. I codici numerici usati in questo modo sono spesso chiamati codici di istruzione o codici operativi o ancora (in modo più conciso) opcode. Questi codici 'istruiscono' i componenti del circuito affinchè effettuino una determinata 'operazione'.
Per eseguire la Memorizzazione dei risultati delle somme nella RAM Dati,
è necessario che l'output del registro nel sommatore originale sia un input
per la RAM Dati. E' necessaria, inoltre, un'altra modifica: originariamente
l'output del sommatore era l'input del registro, mentre ora, per eseguire
l'istruzione Caricamento, l'output della RAM Dati
deve essere talvolta l'input del registro a 8 bit.
Ciò che è necessario, dunque, è un Multiplexer
(selettore) da 2 a 1, il sommatore rivisto appare come nello schema seguente.
In questo diagramma mancano alcune parti, tuttavia esso mostra tutti i
percorsi dati a 8 bit tra i diversi componenti. Il contatore a 16 bit fornisce
un indirizzo per le due RAM. L'output della RAM Dati passa come di consueto,
nel sommatore per eseguire l'istruzione di addizione.
Mentre l'input per il registro può essere sia l'output della matrice RAM
dei dati (nel caso di un'istruzione di caricamento) o l'output del sommatore
(nel caso di un'istruzione di addizione). L'output del registro ritorna
al sommatore, ma esso è anche l'input dati della matrice RAM Dati per un'istruzione
di memorizzazione.
In questo diagramma mancano tutti i piccoli segnali che controllano questi
componenti, noti collettivamente come segnali di controllo.
Essi includono gli input di clock e di cancellazione (CLEAR) al contatore
a 16 bit, la cancellazione del registro e dell''input di scrittura alla
matrice RAM dei Dati.
Alcuni di questi segnali saranno, ovviamente, basati sull'output della RAM
del Codice.
L'input di selezione per il multiplexer , ad esempio, deve essere 0 (per
selezionare l'output della RAM Dati) se l'output della matrice RAM Codice
indica un'istruzione di Caricamento.
L'input Scrittura alla matrice RAM dei Dati deve essere 1 solo quando l'opcode
è un'istruzione di Memorizzazione.
Questi segnali di controllo possono essere generati da diverse combinazioni
di porte logiche.
Con una quantità minima di hardware supplementare e l'aggiunta di un nuovo opcode, possiamo anche fare in modo che questo circuito sottragga un numero dal valore contenuto nell'accumulatore. Ampliamo, dunque, la tabella dei codici operativi
I codici delle operazioni Addizione e Sottrazione differiscono solo nei
bit meno significativi del valore del codice che chiamiamo Co.
Se l'opcode è 21h, il circuito dovrebbe eseguire le stesse operazioni svolte
per un'istruzione di Addizione, con l'eccezione che i dati uscenti dalla
RAM Dati vengono invertiti prima di passare al sommatore e l'input di riporto
(carry) del sommatore viene impostato a 1 (la sottrazione viene eseguita
con il metodo del complemento a due).
Questo sommatore automatizzato rivisto che include un invertitore può eseguire entrambe le operazioni di addizione e sottrazione.
Immaginando di voler sommare tra loro 56h e 2Ah e sottrarre 38h dalla somma . Si può eseguire l'operazione tramite i seguenti codici
Dopo l'operazione di Caricamento, l'accumulatore contiene il valore 56h . Dopo l'operazione Addizione l'accumulatore contiene la somma di 56h e 2Ah, ossia 80h. L'operazione di Sottrazione provoca l'inversione dei bit (complemento a uno) del valore successivo nella RAM Dati (38h) , il valore invertito C7h viene aggiunto ad 80h con l'input di riporto del sommatore impostato ad 1.
Il risultato è 48h (in decimale sarebbe 86 più 42 meno 56 uguale a 72).
Un problema esistente riguarda la dimensione Dati del sommatore, limitata
ad 8 bit.
Senza connettere tra loro due sommatori ad 8 bit, è possibile praticare
una soluzione meno costosa : immaginando di voler sommare tra loro due numeri
a 16 bit come ad esempio:
Questa addizione a 16 bit è equivalente a sommare separatamente i byte più a destra (spesso chiamati byte low-order):
e quindi sommare quelli più a sinistra, detti anche byte high-order.
per ottenere un risultato di 99D7h. Se pertanto memorizziamo i due numeri a 16 bit come segue
Il risultato D7h verrà memorizzato all'indirizzo 0002h ed il risultato 99h verrà memorizzato all'indirizzo 0005h.
Questa tecnica non è sempre valida, perchè opera correttamente per i numeri scelti nell'esempio, ma se invece i due numeri a 16 bit da sommare fossero stati 76ABh e 236Ch? In tal caso l'addizione dei 2 byte di destra dà come risultato un riporto:
Questo riporto deve essere addizionato alla somma dei due byte di sinistra:
per ottenere un risultato finale di 9A17h.
E' possibile migliorare i componenti del circuito della nostra unità di eleborazione per sommare correttamente due numeri a 16 bit? Si, è possibile. Tutto quello che è necessario fare è salvare il bit di Riporto Out del sommatore a 8 bit quando viene effettuata la prima addizione e quindi usare quel bit di Riporto Out come bit di Riporto Input per l'addizione successiva. Come può essere salvato un bit? Un bit può essere salvato mediante un latch a 1 bit ovviamente, in questo caso il latch è noto come latch di riporto (latch carry) che in una letteratura tecnica successiva verrebbe chiamato carry flag.
Per usare il latch di riporto, è necessario un altro opcode che chiameremo Addizione con riporto. Quando sommiamo fra loro numeri a 8 bit, useremo la vecchia normale istruzione Addizione. L'input di riporto al sommatore è 0 e l'input riporto dal sommatore è bloccato nel latch di riporto (sebbene non sia necessario usarlo).
Se vogliamo sommare fra loro due numeri a 16 bit, useremo la normale istruzione
Addizione per sommare i byte di destra.
L'input di riporto al sommatore è 0 e l'input di riporto è bloccato nel
latch di riporto.
Per sommare i 2 byte di sinistra, useremo la nuova istruzione Addizione
con riporto.
In questo caso i due numeri vengono addizionati usando l'output del latch
di riporto come input di riporto al sommatore.
Se la prima addizione si conclude con un riporto, tale bit di riporto è usato nella seconda addizione. Se non vi è riporto l'output del latch di riporto (carry flag) è 0.
Per sottrarre un numero a 16 bit da un altro, abbiamo la necessità di un'altra
nuova istruzione chiamata Sottrazione con prestito.
Un'istruzione Sottrazione normalmente richiede di
invertire i sottraendi ed impostare l'input riporto del sommatore a 1.
Un input riporto pari a 1 è normale e dovrebbe essere generalmente ignorato. Se dobbiamo sottrarre un numero a 16 bit, tuttavia, tale input riporto dovrebbe essere salvato nel latch riporto. Nella seconda sottrazione, l'input riporto per il sommatore dovrebbe essere impostato al risultato del latch riporto. Con le nuove operazioni Addizione con riporto e Sottrazione con prestito disponiamo finora di sette opcode:
Un problema evidente del dispositivo è che non consente il riutilizzo dei
risultati nei calcoli successivi. Immaginiamo di sommare tra loro tre numeri
e di sottrarre dal risultato un altro numero, memorizzando il valore finale.
Questo richiederebbe un'istruzione di Caricamento,
due istruzioni di Addizione , un'istruzione di Sottrazione
ed un'istruzione di Memorizzazione. Cosa dobbiamo
fare se vogliamo eseguire alte operazioni sulla somma originata?
Questo valore non è più accessibile dopo la prima sottrazione e va quindi
ricalcolata ogni volta che abbiamo necessità.
Il problema consiste nel fatto che abbiamo creato un sommatore automatizzato che indirizza la RAM Codice e la RAM Dati contemporaneamente e sequenzialmente iniziando dall'indirizzo 0000h.
Ogni istruzione nella RAM Codice corrisponde ad una locazione la medesimo indirizzo della RAM Dati. Quando un' istruzione memorizzazione provoca la memorizzazione di qualcosa nella memoria Dati, tale valore non può essere ricavato più tardi nell'accumulatore. per risolvere questo problema, bisogna effettuare una modifica fondamentale all'elaboratore che inizialmente può sembrare complessa ma che successivamente aumenta la flessibilità dell'apparato.
Attualmente disponiamo dei sette opcode visti sopra. Ciascuno di questi codici occupa 1 byte nella memoria. Vogliamo ora che ciascuna di queste istruzioni, a eccezione del codice arresto, richieda 3 byte di memoria. Il primo byte sarà il codice stesso e i successivi due byte saranno una locazione di memoria a 16 bit.
Per l'istruzione Caricamento tale indirizzo indica una locazione nella RAM Dati che contiene il byte da caricare nell'accumulatore. Per le istruzioni Addizione, Sottrazione, Addizione con riporto e Sottrazione con prestito, tale indirizzo indica la locazione del byte che deve essere sommato o sottratto dall'accumulatore.
Per l'istruzione di Memorizzazione , l'indirizzo indica dove devono essere memorizzati i contenuti dell'accumulatore.
Per esempio, il lavoro più semplice che l'attuale sommatore può svolgere
è sommare tra loro due numeri.
Per eseguire questa operazione, impostiamo le RAM Codice e Dati nel modo
seguente
In questa versione rivista, ogni istruzione (a eccezione dell'istruzione Arresto) richiede tre byte:
Ciascuno dei codici di istruzione (a eccezione di Arresto)
è seguito da 2 byte che indicano un indirizzo a 16 bit nella matrice RAM
Dati. Questi tre indirizzi possono essere 0000h, 0001h, e 0002h, ma possono
anche essere qualcos'altro.
In precedenza si è visto come sommare una coppia di numeri a 16 bit, in
particolare 76ABh e 232Ch, usando le istruzioni Addizione
e Addizione con riporto. In quel caso abbiamo memorizzato
i 2 byte di destra di tali numeri nelle locazioni di memoria 0000h e 0001h
e i 2 byte di sinistra nelle locazioni 0003h e 0004h.
Il risultato dell'addizione è stato memorizzato in 0002h e 0005h. Con le
modifiche effettuate possiamo memorizzare i due numeri e il risultato in
modo più razionale e magari in un'area di memoria mai usata prima:
Queste sei locazioni non devono essere necessariamente consecutive come
nell'esempio, ma possono essere sparse ovunque nella matrice RAM Dati di
64 KB.
Per sommare questi valori a queste locazioni di memoria, si deve impostare
la RAM Codice come segue:
Osserviamo che i 2 byte di destra posizionati agli indirizzi 4001h e 4003h vengono addizionati per primi e il risultato viene memorizzato all'indirizzo 4005h. I 2 byte di sinistra (agli indirizzi 4000h e 4002h) vengono addizionati mediante l'istruzione Addizione con riporto e il risultato è memorizzato all'indirizzo 4004h. Se rimuovessimo l'istruzione Arresto e aggiungessimo più istruzioni alla RAM Codice, un calcolo successivo potrebbe più avanti fare uso dei numeri originali e della loro somma semplicemente mediante un riferimento a questi indirizzi di memoria.
La chiave per realizzare questo progetto consiste nel far si che l'output
dati della RAM Codice entri in tre registri a 8 bit.
Ciascun registro memorizza un byte dell'istruzione di 3 byte.
Il primo registro memorizza il codice di istruzione, il secondo registro memorizza il byte di sinistra dell'indirizzo e il terzo registro memorizza il byte di destra dell'indirizzo. L'output del secondo e del terzo registro diventa l'indirizzo a 16 bit della RAM Dati:
Nella nostra macchina ogni istruzione ha lunghezza pari a tre byte e viene recuperata dalla memoria 1 byte alla volta; l'estrazione dell'istruzione richiede quindi tre cicli del segnale di Clock. Il ciclo di istruzione completo richiede un quarto ciclo del segnale di Clock. Queste modifiche rendono senza dubbio più complessi i segnali di controllo.
Si dice che la macchina esegue un istruzione quando essa effettua una serie
di azioni in risposta al codice dell'istruzione. Ogni codice macchina provoca
vari segnali di controllo in un modo unico, il quale causa varie reazioni
della macchina. Notiamo che rendendo questa macchina più versatile, l'abbiamo
resa anche più lenta.
Usando lo stesso oscillatore di clock, essa addiziona numeri a una velocità
pari a un quarto di quella del primo sommatore che abbiamo implementato.
In generale quando si migliora un aspetto della macchina, qualcos'altro
ne soffre.
Abbiamo introdotto due RAM, una per il codice e una per i dati, i modo tale che l'architettura del sommatore automatizzato risultasse più chiara e semplice possibile. Ora che abbiamo deciso di rendere ogni istruzione lunga 3 byte, con il secondo e terzo byte indicanti un indirizzo nel quale sono posizionati i dati, non è più necessario disporre di due RAM separate. Codice e dati possono essere memorizzati nella stessa RAM.
Per portare a termine questa operazione abbiamo necessità di un multiplexer (selettore) da 2 a 1 per determinare come viene indirizzata la matrice RAM. Generalmente l'indirizzo è, come in precedenza, il contatore a 16 bit. Il Dati Out della RAM è ancora connesso a tre registri che contengono il codice di istruzione e i due byte di indirizzo che accompagnano ogni istruzione.
L'indirizzo a 16 bit è però il secondo input del Multiplexer da 2 a 1. Dopo che l'indirizzo è bloccato (memorizzato), questo selettore consente all'indirizzo bloccato di essere l'indirizzo di input alla RAM.
Ora è possibile immettere le istruzioni e i dati in una sola RAM. il diagramma che segue, per esempio, mostra come sommare tra loro due numeri a 8 bit e sottrarre un terzo numero.
Le istruzioni iniziano, come di consueto, all'indirizzo 0000h poiché quella è la locazione in cui il contatore inizia ad accedere alla RAM dopo che è stato azzerato. l'istruzione Arresto finale è memorizzata all'indirizzo 000Ch. Possiamo memorizzare i tre numeri e i risultati ovunque nella RAM (eccetto che nei primi 13 byte poiché queste locazioni di memoria sono occupate dalle istruzioni), scegliamo tuttavia di memorizzare i dati iniziando dall'indirizzo 0010h. Se ora immaginiamo ora che ci sia la necessità di sommare due ulteriori numeri a quel risultato. Possiamo sostituire tutte le istruzioni appena immesse con nuove istruzioni, ma può anche darsi che non vogliamo farlo. Forse sarebbe meglio aggiungere le nuove istruzioni al termine di queste istruzioni, sostituendo in primo luogo l'istruzione Arresto con una nuova istruzione Caricamento all'indirizzo 000Ch.
Potremmo avere necessità di due nuove istruzioni di addizione, di un'istruzione memorizzazione e una nuova istruzione arresto. L'unico problema consiste nel fatto che abbiamo dati memorizzati all'indirizzo 0010h. Dobbiamo spostare tali dati a un indirizzo di memoria superiore. Dobbiamo poi modificare le istruzioni che fanno riferimento a tali istruzioni in memoria.
In questo caso, potremmo immettere le nuove istruzioni iniziando all'indirizzo 0020h e i nuovi dati all'indirizzo 0030h:
Osserviamo che la prima istruzione Caricamento fa
riferimento alla locazione di memoria 0013h, nella quale è stato memorizzato
il risultato del primo calcolo.
Ora disponiamo di alcune istruzioni che iniziano all'indirizzo 0000h, alcuni
dati che iniziano a 0010h, alcune altre istruzioni a 0020h e alcuni altri
dati a 0030h.
Vogliamo, quindi, che sommatore inizi all'indirizzo 0000h ed esegua tutte
le istruzioni.
Sappiamo di dover rimuovere l'istruzione Arresto
dall'indirizzo 000Ch e in questo caso con il termine rimozione si intende
sostituzione della stessa con qualcos'altro.
Tale attività è sufficiente? Il problema è dato dal fatto che qualunque
cosa sostituisce l'istruzione Arresto verrà interpretato
come un byte di istruzione. I byte verrano così memorizzati tre byte dopo
tale byte a 000Fh, 0012h, 0015h, 0018h, 001Bh e 001Eh.
Cosa succede se uno di questi byte è per caso un codice 11h? Questo codice
rappresenta un'istruzione Memorizzazione.
E se i 2 byte che seguono tale istruzione Memorizzazione
fanno riferimento all'indirizzo 0023h? Questo provocherebbe la scrittura
dei contenuti dell'accumulatore a tale indirizzo.
Tale indirizzo però contiene già qualcosa di importante! Anche se non accade
nulla di questo genere, il successivo byte di istruzione che il sommatore
recupera dalla memoria dopo quello alla locazione 001Eh sarà all'indirizzo
0021h e non all'indirizzo 0020h nel quale, in questo caso, è contenuta la
successiva istruzione reale.
Siamo d'accordo sul fatto che non è sufficiente rimuovere l'istruzione Arresto
all'indirizzo 000Ch e sperare che tutto vada bene?
Possiamo però sostituirla con una nuova istruzione
chiamata Vai a che aggiungendola al nostro repertorio.
Normalmente questo sommatore indirizza la RAM sequenzialmente. Un istruzione vai a fa sì che la macchina modifichi questo modello iniziando a indirizzare la RAM a un diverso indirizzo specificato nel precedente esempio possiamo sostituire l'istruzione Arresto all'indirizzo 000Ch con un'istruzione Vai a:
Il byte 30h è il codice per un'istruzione Vai a.
L'indirizzo a 16 bit che segue indica l'indirizzo della successiva istruzione
che il sommatore automatizzato leggerà.
Nell'esempio precedente, l'elaboratore inizia come al solito all'indirizzo
0000h, esegue un'istruzione di Caricamento, un'istruzione
di Addizione, un'istruzione di Sottrazione,
un'istruzione di Memorizzazione ed infine si ha l'Arresto.
L'istruzione Vai a influenza il contatore a 16 bit. Ogni volta che l'elaboratore incontra un'istruzione Vai a, il contatore deve in qualche modo essere forzato ad emettere quel nuovo indirizzo che segue il codice dell'opcode. Questa attività può essere implementata via hardware solo usando gli ingressi asincroni (indipendenti dal clock) dei flip-flop che costituiscono i registri; stiamo parlando degli ingressi di preimpostazione (Preset) e di cancellazione (Clear) di cui sono dotati i latch (flip-flop D).
Se il FF è a logica positiva, i due ingressi Pre e Clr sono entrambi a
0, se Pre=1 allora l'uscita Q=1, se Clr=1 allora Q=0.
Se vogliamo caricare un FF con un singolo valore (che chiamiamo A) possiamo
cablare il seguente circuito:
Normalmente Set=0 (Set=imposta). In tal caso l'input di Preset al FF è
0, l'input di cancellazione Clr è 0 anch'esso, a meno che il segnale di
Reset non sia 1.
Questo permette che il FF sia svuotato indipendentemente dal segnale Set.
Quando Set=1 Pre=1 e Clr=0 se A=1. Se A=0 Pre=0 e Clr=1 Ciò significa Q=A.
Abbiamo la necessità di uno di questi per ciascun bit del contatore a 16
bit. Una volta caricato un valore particolare, il contatore continuerà il
conteggio da quel valore in poi.
Modifichiamo sinteticamente il circuito precedente.
Dobbiamo solo assicurarci che il segnale di Set sia 1 solo se il codice di istruzione è 30h.
L'istruzione Vai a è certamente utile ma non tanto quanto un'istruzione che effettua la ramificazione solo alcune volte anziché tutte le volte. Un'istruzione di questo tipo è nota come ramificazione condizionata (basata su condizioni); il miglior modo per mostrare come sia utile questa istruzione può consistere nel porre una domanda:
Come possiamo convincere il nostro elaboratore a moltiplicar due numeri a 8bit. Come 1Ch volte A7h (28×167=4676)?
Il risultato della moltiplicazione di due valori ad 8 bit è un prodotto a 16bit.
I tre numeri coinvolti nella moltiplicazione, sono per comodità espressi come valori a 16bit. La prima operazione che facciamo è quella di stabilire dove collocare i due operandi ed il prodotto.
Sappiamo che moltiplicare A7h e 1Ch equivale a sommare per 28 volte il
valore di A7h.
Pertanto la locazione a 16bit agli indirizzi 1004h e 1005h sarà effettivamente
una somma cumulativa. Ecco il codice per sommare una sola volta A7h a tale
locazione
Al termine di queste sei istruzioni il valore a 16 bit nelle locazioni di memoria 1004h e 1005h sarà equivalente ad 1 volta il valore A7h. Possiamo ottenere questo risultato digitando queste sei istruzioni altre 27 volte con inizio all'indirizzo 0012h, oppure possiamo immettere un'istruzione Arresto all'indirizzo 0012h e premere per 28 volte il pulsante Reset per ottenere la risposta finale. Naturalmente, nessuna delle due opzioni è ideale . Entrambe richiedono di svolgere qualche azione, immettere un gruppo di istruzioni o premere il pulsante Reset per un numero di volte proporzionale a uno dei numeri da moltiplicare. Bisogna automatizzare queste iterazioni.
Cosa succede quindi se immettiamo un'istruzione Vai a all'indirizzo 0012h ? Questa istruzione fa si che il contatore inizi di nuovo da 0000h .
Questa operazione costituisce sicuramente l'artificio richiesto.
Con il primo passaggio, il valore a 16 bit nelle locazioni di memoria 1004h
e 1005h sarà uguale a 1 volta A7h . l'istruzione Vai a
riporterà poi all'inizio del procedimento.
Al termine del secondo passaggio, il risultato a 16 bit sarà uguale a 2
volte A7h. Infine, esso sarà uguale a 1Ch volte A7h; a questo punto però
non c'è modo per arrestare il procedimento. Esso prosegue (loop infinito).
Quello che vogliamo è un'istruzione Vai a che attivi
di nuovo il processo solo per il numero di volte necessario. Questa istruzione
è quella che implementa la ramificazione condizionale
. La prima cosa da aggiungere è un latch a 1 bit simile al latch di riporto.
Lo chiamiamo latch di zero poiché blocca (memorizza) un valore di 1 solo
se l'output dell'addizionatore a 8 bit è composto da soli zero:
L'output di tale porta NOR a 8 bit è 1 solo tutti gli input sono 0.
Come l'input Clock del latch di riporto, l'input Clock del latch zero
blocca un valore solo quando viene eseguita un'istruzione Addizione
, Sottrazione , Addizione con
riporto o Sottrazione con prestito.
Questo valore bloccato è noto come flag Zero. Il flag zero vale 1 se l'output
dell'addizionatore è costituito da zeri e vale 0 se l'output dell'addizionatore
non è costituito di soli zero.
Con il latch di riporto e il flag zero, possiamo ampliare il nostro repertorio
aggiungendo quattro istruzioni:
L'istruzione Vai a se non zero, per esempio, passa all'indirizzo specificato solo se l'output del flag di zero è 0.
In poche parole, non avverrà la ramificazione se l'ultima istruzione Addizione, Sottrazione, Addizione con riporto o Sottrazione con prestito darà risultato 0.
La definizione di queste istruzioni è solo un'aggiunta ai segnali di controllo che implementano il normale comando Vai a: se l'istruzione è Vai a se non zero, viene provocato il segnale Set sul contatore a 16 bit solo se il flag Zero è 0.
Affinchè il codice mostrato prima, moltiplichi due numeri è quindi necessario predisporre solo le istruzioni che seguono a partire dall'indirizzo 0012h.
Abbiamo già stabilito che attraverso il primo passaggio la locazione a 16 bit agli indirizzi 0004h e 0005h contiene 1 volta A7h.
Le istruzioni a questo punto caricano il byte della locazione 1003h nell'accumulatore.
Esso è 1Ch. Questo byte viene sommato al valore della locazione 001Eh. Tale
locazione contiene l'istruzione Arresto, ma naturalmente
è anche un numero valido. Sommare FFh a 1 Ch è equivalente a sottrarre 1
da 1Ch, pertanto il risultato è 1Bh. Esso non è 0, quindi il flag Zero vale
0. Il byte 1Bh viene memorizzato all'indirizzo 1003h.
L'istruzione che segue è un'istruzione Vai a se non zero.
Il flag Zero non è impostato ad 1, quindi la ramificazione avviene. L'istruzione
che segue è quella posizionata all'indirizzo 0000h.
Ricordiamo che l'istruzione Memorizzazione non influenza il flag Zero. Il flag Zero viene influenzato solo dalle istruzioni Addizione, Sottrazione, Addizione con riporto o Sottrazione con prestito, esso manterrà quindi lo stesso valore che è stato impostato l'ultima volta in cui è stata eseguita una di queste istruzioni.
Dopo il secondo passaggio, la locazione a 16 bit all'indirizzi 1004h e
1005h conterrà due volte il valore A7h. Il valore 1Bh viene addizionato
a FFh per ottenere il risultato 1Ah.
Esso non è 0, pertanto il procedimento riprende da capo.
Dopo ventotto passaggi, la locazione a 16 bit agli indirizzi 1004h e 1005h
conterrà il valore 1Ch A7h. Nella locazione 1003h ci sarà il valore 1.
Questo verrà addizionato a FFh e il risultato sarà zero. Il flag Zero verrà
impostato ! In tal modo l'istruzione Vai a se non zero
non ritornerà a 0000h.
La sucessiva istruzione è un'istruzione Arresto,
pertanto abbiamo terminato.
C.P.U.
Ora posso affermare che finalmente abbiamo assemblato un elemento di hardware che possiamo anche chiamare computer ; è senza dubbio rozzo e primitivo, ma è pur sempre un computer.
Ciò che fa la differenza è la ramificazione condizionale. Le ripetizioni controllate o cicli sono ciò che distingue i computer dalle calcolatrici.
Si è visto come un'istruzione di ramifcazione consenta a questa macchina di moltiplicare due numeri. Essa non è in oltre limitata a valori a 8 bit, ma può sommare, sottrarre, moltiplicare e dividere numeri a 16 bit, 24 bit, 32 bit o anche più grandi. Se può svolgere tali attività, può calcolare la radice quadrata, logaritmi e funzioni trigonometriche.
Il particolare computer che abbiamo assemblato, viene classificato come computer digitale poichè opera con numeri discreti. Una volta esistevano anche computer analogici che oggi sono tutti scomparsi (i dati sono dati discreti, ossia dati che assumono valori definiti e distinti; le informazioni analogiche sono continue e variano in un intervallo completo).
Un computer digitale si compone di quattro parti principali:un processore (o C.P.U.) ,una memoria,almeno una periferica di input e almeno una periferica di output.Nella nostra macchina,la memoria è costituita dalla matrice RAM di 64 KB.Le periferiche di input e output sono i pin di controllo RAM ed eventuali display a 7 segmenti ci consentono di immettere numeri nella memoria e di esaminare i risultati. Il processore, cioè la C.P.U. è tutta la parte rimanente.
Il processore che abbiamo visto è un processore a 8 bit. L'accumulatore ha la dimensione 8 bit e la maggior parte dei processori di dati hanno la dimensione di dati hanno la dimensione 8 bit. Il solo percorso di dati a 16 bit è l'indirizzo alla RAM. Usando anche per esso 8 bit ,saremmo stati limitati a 256 byte di memoria invece che a 65.536 byte e ciò sarebbe risultato piuttosto restrittivo.
Un processore o C.P.U. è costituito da numerosi componenti. Si è già identificato
l'accumulatore ,il quale è semplicemente un latch che blocca o mantiene
un numero nel processore.
Nel nostro computer, l'insieme costituito dall'invertitore a 8 bit e dall'addizionatore
a 8 bit possono essere chiamati A.L.U. o arithmetic logic unit. L'ALU effettua
solo operazioni aritmetiche e in particolare addizioni e sottrazioni.In
computer leggermente più sofisticati, l'ALU può anche effettuare funzioni
logiche,come per esempio AND,OR e XOR. Il contatore a 16 bit è chiamato
contatore di programma.
Il computer che abbiamo visto è costruito con componenti digitali, cavi,interruttori
e display. Tutti questi componenti sono chiamati hardware.
Le istruzioni e gli altri numeri che abbiamo immesso nella memoria sono
invece chiamati software. Con il termine
"soft"si intende che tali elementi possono essere modificati molto piu'
facilmente di quelli hardware.
Parlando di computer,la parola software è quasi sinonimo del termine programma di computer o,piu' semplicemente,del termine programma.La scrittura di software è nota come programmazione di computer.La programmazione di computer coincide con quanto effettuato quando abbiamo determinato la serie di istruzioni che consentono al nostro computer di moltiplicare fra loro due numeri.
Talvolta si fa riferimento alla programmazione di computer come scrittura
di codice o codifica. I codici operazione ai quali risponde un processore(come
per esempio 10h e 11h rispettivamente per le istruzioni a caricamento e
memorizzazione)sono noti come codici macchina
o linguaggio macchina.
Il termine linguaggio viene usato poiché il linguaggio macchina è simile
a un linguaggio umano scritto o parlato che una macchina "comprende"e al
quale risponde.
Abbiamo fatto riferimento alle istruzioni che la nostra macchina esegue
mediante frasi piuttosto lunghe,come per esempio Addizione
con riporto.
Ai codici macchina vengono generalmente assegnati nomi brevi e mnemonici
scritti con lettere maiuscole.
Questi termini mnemonici possono anche essere costituiti solo da 2 o 3 lettere.
Ecco un insieme di possibili nomi mnemonici per i codici macchina che il
nostro computer riconosce: