Anche se possono differire nei dettagli, le istruzioni del livello ISA, possono essere suddivise in gruppi tra cui :

  • Trasferimento Dati
  • Binarie
  • Unarie
  • Selezione e confronto - salti condizionati
  • Chiamata di una procedura
  • Iterazione
  • Input/Output

Istruzioni di trasferimento dati

La copia dati ( ovvero la creazione di una nuova sequenza di bit identica all’originale ) da una locazione all’altra è fondamentale.
Ci sono due ragioni per copiare i dati da una locazione ad un’altra :

  • assegnamento di valori a variabili ( A=B )
  • accesso ed utilizzo in modo efficiente

Ci sono 4 tipi di trasferimenti possibili :

  • registro to registro ( es. MOVE)
  • registro to memoria ( es. STORE )
  • memoria to memoria
  • memoria to registro ( es. LOAD )

Operazioni binarie

Le operazioni binarie sono quelle operazioni che producono un risultato dalla combinazione di due operandi. È abbastanza ovvio che un computer sia dotato di istruzioni aritmetiche, ma un’altro insieme di operazioni binarie sono le istruzioni booleane tra cui :

  • AND
  • OR
  • XOR
  • NOR
  • NAND

Un uso importante di AND è l’estrazione di bit da una word. Consideriamo per esempio un calcolatore con word da 32 bit che possono avere 4 caratteri ( ognuno da 8 bit ), e vogliamo estrarre ( isolare ) il secondo carattere per visualizzarlo sullo schermo. Per visualizzarlo sullo schermo bisogna estrarre il carattere e porlo negli 8 bit più a destra della word :

Ora non manca altro che “shiftare” il risultato di 16 volte verso destra, ottenendo il carattere isolato voluto :

Un utilizzo importante invece dell’OR è poter cambiare - per esempio - un carattere di 8 bit da una word da 32 bit :

Operazione unarie - shift e rotate

Le operazione unarie prendono in input un operando e danno in output un risultato, tra cui :

  • shift : scorrimento di una word e i bit che escono si “perdono”
  • rotazione contenuto : scorrimento di una word, ma i bit che escono da un’estremità rientrano dall’altra

Vediamo meglio la differenza tra i due con un esempio :

L’operazione di shift verso destra è spesso utilizzata con l’estensione del segno, ovvero che i bit che sono all’estrema sinistra vengono settati secondo il segno originario, 0 o 1. Quindi se “shifto” un numero negativo, esso rimane negativo :

Infatti sarebbe che shiftato destra di 2 bit sarebbe come dividere per e infatti otteniamo ( con shift con segno esteso ) .

Un’applicazione dello shift è la moltiplicazione e la divisione per potenze di 2 :

  • shift verso sinistra per bit : moltiplicazione per
  • shift verso destra per bit : divisione per

Inoltre - alcune volte - si può calcolare velocemente operazioni aritmetiche con lo shift, consideriamo per esempio di voler fare , allora possiamo procedere in questo modo :

e quindi invece che fare la moltiplicazione ( più lenta ) ci basta shiftare verso sinistra di 4 posizioni e sommarlo a shiftato verso sinistra di 1 posizione.

In generale lo shift verso destra può introdurre degli errori a causa del troncamento delle cifre che compongono il risultato. Mentre nel caso dei numeri negativi lo shift verso sinistra non produce la moltiplicazione per 2, a differenza dello shift verso destra che esegue la divisione correttamente.

Altre operazioni unarie sono :

  • CLR : contiene solo un indirizzo e serve per azzerare
  • INC : incrementa di 1
  • NEG : opposto del numero ( negativo o positivo ) ( diverso da NOT! )

Confronti e salti condizionati

Quasi tutti i programmi hanno bisogno della capacità di confrontare dei dati e alterare la sequenza di esecuzione delle istruzioni in base al risultato del confronto. Un metodo per farlo è fornire istruzioni di salto condizionato che verificano una certa condizione e poi “saltano” a un particolare indirizzo se la condizione è soddisfatta. La condizione che viene controllata è se un determinato bit del calcolatore (codici di controllo) è posto o meno a 0. Per esempio si può avere un bit di riporto che viene asserito quando si verifica un riporto all’ultimo bit di sinistra (bit C del PSW).

Invocazione di procedura

Una procedura è un insieme di istruzioni che svolge un certo compito e che può essere chiamata da diversi punti di un programma ( concetto analogo alla funzione in C ).

Quando una procedura ha terminato il proprio compito, l'esecuzione deve riprendere dall'istruzione successiva alla chiamata.

Quindi l’indirizzo di ritorno può essere salvato in :

  • memoria
  • registro
  • stack

Un trucco è inserire il l'indirizzo di ritorno in cima ad uno stack. Nel momento di chiamata della procedura la CPU fa il push dell’indirizzo di ritorno nello stack, quando la procedura ha finito si fa un pop dallo stack e si ritorna all’indirizzo di ritorno. Sta roba inoltre funziona anche per le chiamate ricorsive.

Istruzioni di ciclo - iterazione

Ci sono due metodi per controllare la condizione per uscire dal ciclo :

  • alla fine del ciclo
  • all’inizio del ciclo

Se scegliamo alla fine del ciclo, allora il corpo del ciclo sarà eseguito almeno una volta (do-while), anche se la condizione di controllo ( che sta alla fine ) è falsa fin dall’inizio.

Input/Output

I PC usano 3 schemi diversi di I/O :

  1. I/O programmato con attesa attiva
  2. I/O interrupt driven ( ovvero innescato dagli interrupt )
  3. I/O con DMA

I/O programmato

Il metodo I/O programmato è impiegato nei sistemi integrati o sistemi che devono rispondere velocemente agli stimoli esterni.

Per esempio, consideriamo un terminale con 4 registri da 1 byte ( vengono utilizzati però solo 2 bit ) :

dove i registri per la tastiera sono per l’input, e i registri per lo schermo sono per l’output. Inoltre ogni registro ha un suo indirizzo univoco.

Se l’I/O è memory-mapped, allora i 4 registri fanno parte dello spaziamento degli indirizzi memoria e quindi possono essere letti o scritti da istruzioni ordinarie. In caso contrario ( non memory-mapped ), ci sono istruzioni speciali I/O - IN e OUT - per la lettura e scrittura dei 4 registri.

La CPU aspetta dati in ingresso ( dalla tastiera ) effettuando un ciclo “infinito” ( attesa attiva ) di letture sul registro di stato, appena è pronto un carattere, legge il registro buffer della tastiera per ottenere il carattere arrivato. Funziona molto similmente per lo schermo.

I/O interrupt driven

Il metodo del I/O programmato è accettabile solo se la CPU non ha nient’altro da eseguire, ma se ha altri programmi, abbiamo bisogno di un’altro metodo di I/O. Una soluzione è l’utilizzo degli interrupt ( I/O interrupt driven ), ovvero invece di far rimanere in attesa la CPU, la CPU da un ordine al dispositivo di leggere il carattere e poi si rimette a fare quello che stava facendo. Quando il carattere è pronto il dispositivo abilita un interrupt per la CPU, che sospende quello che stava facendo, legge il carattere e poi riprende l’attività sospesa.

Il problema è che per leggere tanti caratteri ci vogliono tanti interrupt, e questo non va bene perché la CPU deve fermarsi e ripartire ogni volta, occorre quindi un modo per ridurre il numero di interrupt.

I/O con DMA

La soluzione per ridurre il numero di interrupt, sarebbe di tornare all’I/O programmato, ma affidando il compito di controllare lo stato dei registri ad un chip - chip DMA.

Il chip controllore DMA ( Direct Memory Access ), che ha accesso diretto al bus, ha 4 registri :

  1. contiene indirizzo di partenza per la lettura o scrittura
  2. conta il numero di byte ( o word ) da trasferire
  3. specifica il dispositivo I/O desiderato
  4. specifica se i dati vanno letti o scritti

Anche se toglie il carico pesante alla CPU, il chip DMA “ruba” cicli di bus alla CPU ( cycle stealing ) in fenomeni come la lettura o scrittura di un disco. Questo siccome la CPU rimane in attesa perché il DMA ha sempre precedenza nel bus ( I/O non tollerano ritardi ).


Controllo del flusso