Introduzione. Le istruzioni del livello della macchina standard (livello 2) si possono dividere, con buona approssimazione, in due gruppi:



Documenti analoghi
4 3 4 = 4 x x x 10 0 aaa

Introduzione. Classificazione di Flynn... 2 Macchine a pipeline... 3 Macchine vettoriali e Array Processor... 4 Macchine MIMD... 6

CPU. Maurizio Palesi

Appunti sulla Macchina di Turing. Macchina di Turing

Architettura hardware

Esercizi su. Funzioni

Con il termine Sistema operativo si fa riferimento all insieme dei moduli software di un sistema di elaborazione dati dedicati alla sua gestione.

4. Operazioni aritmetiche con i numeri binari

Lezione 8. La macchina universale

DMA Accesso Diretto alla Memoria

ESEMPIO 1: eseguire il complemento a 10 di 765

SISTEMI DI NUMERAZIONE E CODICI

Siamo così arrivati all aritmetica modulare, ma anche a individuare alcuni aspetti di come funziona l aritmetica del calcolatore come vedremo.

A intervalli regolari ogni router manda la sua tabella a tutti i vicini, e riceve quelle dei vicini.

ARCHITETTURE MICROPROGRAMMATE. 1. Necessità di un architettura microprogrammata 1. Cos è un architettura microprogrammata? 4

Configurazione della ricerca desktop di Nepomuk. Sebastian Trüg Anne-Marie Mahfouf Traduzione della documentazione in italiano: Federico Zenith

Gestione della memoria centrale

Il Sistema Operativo. C. Marrocco. Università degli Studi di Cassino

Struttura del calcolatore

Tipi primitivi. Ad esempio, il codice seguente dichiara una variabile di tipo intero, le assegna il valore 5 e stampa a schermo il suo contenuto:

Sistemi Operativi IMPLEMENTAZIONE DEL FILE SYSTEM. Implementazione del File System. Struttura del File System. Implementazione

Arduino: Programmazione

Sommario. Definizione di informatica. Definizione di un calcolatore come esecutore. Gli algoritmi.

Airone Gestione Rifiuti Funzioni di Esportazione e Importazione

Complemento al corso di Fondamenti di Informatica I corsi di laurea in ingegneria, settore dell informazione Università la Sapienza Consorzio Nettuno

Architettura di un sistema di calcolo

Testi di Esercizi e Quesiti 1

Calcolatori Elettronici A a.a. 2008/2009

Corso di Sistemi di Elaborazione delle informazioni

STRUTTURE DEI SISTEMI DI CALCOLO

Uso di base delle funzioni in Microsoft Excel

Introduzione. Il principio di localizzazione... 2 Organizzazioni delle memorie cache... 4 Gestione delle scritture in una cache...

Esempi di algoritmi. Lezione III

SISTEMI OPERATIVI. Prof. Enrico Terrone A. S: 2008/09

Parte II Indice. Operazioni aritmetiche tra valori rappresentati in binario puro. Rappresentazione di numeri con segno

Più processori uguale più velocità?

Dispensa di Informatica I.1

I sistemi di numerazione

Mon Ami 3000 Produzione interna/esterna Gestione della produzione interna/esterna

LE SUCCESSIONI 1. COS E UNA SUCCESSIONE

Pronto Esecuzione Attesa Terminazione

Laboratorio di Informatica

CALCOLATORI ELETTRONICI A cura di Luca Orrù. Lezione n.7. Il moltiplicatore binario e il ciclo di base di una CPU

L unità di controllo. Il processore: unità di controllo. Le macchine a stati finiti. Struttura della macchina a stati finiti

Sistemi Operativi. 5 Gestione della memoria

Cos è ACCESS? E un programma di gestione di database (DBMS) Access offre: un ambiente user frendly da usare (ambiente grafico)

La gestione di un calcolatore. Sistemi Operativi primo modulo Introduzione. Sistema operativo (2) Sistema operativo (1)

Alessandro Pellegrini

Architettura del calcolatore

Sistemi Operativi IMPLEMENTAZIONE DEL FILE SYSTEM. D. Talia - UNICAL. Sistemi Operativi 9.1

Ing. Paolo Domenici PREFAZIONE

APPUNTI DI MATEMATICA LE FRAZIONI ALGEBRICHE ALESSANDRO BOCCONI

Sistema Operativo. Fondamenti di Informatica 1. Il Sistema Operativo

Il Sistema Operativo

Corso di Informatica

LA TRASMISSIONE DELLE INFORMAZIONI QUARTA PARTE 1

Scopo della lezione. Informatica. Informatica - def. 1. Informatica

Esame di INFORMATICA

Università di Roma Tor Vergata Corso di Laurea triennale in Informatica Sistemi operativi e reti A.A Pietro Frasca.

RAPPRESENTAZIONE BINARIA DEI NUMERI. Andrea Bobbio Anno Accademico

Gestione Risorse Umane Web

Quinto Homework. Indicare il tempo necessario all'esecuzione del programma in caso di avvio e ritiro fuori ordine.

Funzioni in C. Violetta Lonati

Manuale d uso per la raccolta: Sicurezza degli impianti di utenza a gas - Postcontatore

Elementi di informatica

Università di Roma Tor Vergata Corso di Laurea triennale in Informatica Sistemi operativi e reti A.A Pietro Frasca.

Introduzione alla programmazione in C

Informatica B a.a 2005/06 (Meccanici 4 squadra) PhD. Ing. Michele Folgheraiter

Rappresentazione dei numeri in un calcolatore

Fondamenti di Informatica 2. Le operazioni binarie

Informatica per le discipline umanistiche 2 lezione 14

PROCEDURA INVENTARIO DI MAGAZZINO di FINE ESERCIZIO (dalla versione 3.2.0)

CAPITOLO 7 - SCAMBIO DI MESSAGGI

Come masterizzare dischi con Nero 11

Strutturazione logica dei dati: i file

MODELLO CLIENT/SERVER. Gianluca Daino Dipartimento di Ingegneria dell Informazione Università degli Studi di Siena

Input/Output. Moduli di Input/ Output. gestiscono quantità di dati differenti a velocità diverse in formati diversi. n Grande varietà di periferiche

REGOLAMENTO TENNIS STAR

Architettura di un calcolatore

Registratori di Cassa

Algoritmi e strutture dati. Codici di Huffman

Algoritmo. I dati su cui opera un'istruzione sono forniti all'algoritmo dall'esterno oppure sono il risultato di istruzioni eseguite precedentemente.

Gli array. Gli array. Gli array. Classi di memorizzazione per array. Inizializzazione esplicita degli array. Array e puntatori

L espressione torna invece sempre vera (quindi la soluzione originale) se cambiamo contemporaneamente il verso: 1 < 0.

I Thread. I Thread. I due processi dovrebbero lavorare sullo stesso testo

Introduzione. Coordinazione Distribuita. Ordinamento degli eventi. Realizzazione di. Mutua Esclusione Distribuita (DME)

Concetto di Funzione e Procedura METODI in Java

CONTROLLO DI GESTIONE DELLO STUDIO

Appunti di informatica. Lezione 2 anno accademico Mario Verdicchio

5.3 TABELLE RECORD Inserire, eliminare record in una tabella Aggiungere record Eliminare record


Pagina 2 di 14. Indice

B+Trees. Introduzione

Logistica magazzino: Inventari

I TUTORI. I tutori vanno creati la prima volta seguendo esclusivamente le procedure sotto descritte.

La somma. Esempio: Il prodotto. Esempio:

IL SISTEMA INFORMATIVO

All interno del computer si possono individuare 5 componenti principali: SCHEDA MADRE. MICROPROCESSORE che contiene la CPU MEMORIA RAM MEMORIA ROM

Calcolatori Elettronici. La memoria gerarchica La memoria virtuale

(71,1), (35,1), (17,1), (8,1), (4,0), (2,0), (1,0), (0,1) 0, = 1, 431 0, = 0, 862 0, = 1, 792 0, = 1, 448 0, = 0, 896

Transcript:

Appunti di Informatica Appendice 3 Istruzioni e flusso di controllo TIPI DI ISTRUZIONI. 1 Introduzione.. 1 Istruzioni per il movimento dei dati. 2 Istruzioni per operazioni diadiche... 2 Istruzioni per operazioni monadiche 3 Confronti e salti condizionati.. 3 Istruzioni per la chiamata di procedure... 4 Istruzioni per il controllo dei cicli... 5 CONTROLLO DI FLUSSO DI UN PROGRAMMA.. 6 Premessa... 6 Istruzioni per la chiamata di procedure... 6 Le coroutine.. 6 I trap. 9 Gli interrupt.. 9 Sequenza di operazioni per la gestione di un interrupt.. 10 La gestione trasparente degli interrupt. 11 Controller di interrupt... 12 Tiipii dii iistruziionii Introduzione Le istruzioni del livello della macchina standard (livello 2) si possono dividere, con buona approssimazione, in due gruppi: le istruzioni generali sono quelle con la più vasta applicazione: ad esempio, la capacità di spostare i dati all interno della macchina è necessaria in quasi tutte le applicazioni; le istruzioni speciali hanno invece un campo di applicazione molto più ristretto: ad esempio, l istruzione MOVEP del processore Motorola 68000 prende i contenuti di un dato registro e li immagazzina in memoria in byte alternati, ossia con un byte non utilizzato ogni due. Poche applicazioni possono usare effettivamente questa istruzione e nessun compilatore la genererà mai. Il nostro obbiettivo è quello di esaminare i principali gruppi di istruzioni generali.

Appunti di Calcolatori Elettronici Appendice 3 Istruzioni per il movimento dei dati Una delle operazioni più importanti in un calcolatore è quella per copiare dati da un posto ad un altro: ciò significa creare un nuovo oggetto con una configurazione di bit uguale all originale. Ad esempio, quando diciamo che i contenuti della locazione 2000 di memoria sono stati spostati nel registro R1, intendiamo quasi sempre che ne è stata fatta una copia identica e che l originale è rimasto inalterato nella locazione 2000. Per questo motivo, le istruzioni per il movimento dei dati andrebbero forse chiamate più propriamente istruzioni per la duplicazione dei dati. I dati possono essere memorizzati in posti diversi, che cioè differiscono per il modo in cui si accede alle parole: tre posti comuni sono una particolare parola di memoria, un registro oppure uno stack (pila); bisogna però notare che una struttura a stack è solo una struttura di memoria che logicamente è gestita come uno stack, ma che fisicamente è qualcosa di ben diverso: infatti, uno stack può essere implementato tramite registri speciali o tramite la stessa memoria, mentre invece il modo di accedere agli elementi dello stack è diverso dal modo standard di accedere alla memoria o ai registri; ad esempio, l accesso alla memoria richiede un indirizzo, mentre invece l inserimento di un nuovo elemento in cima allo stack non richiede che venga esplicitamente indirizzato lo stack stesso. Le istruzioni per il movimento dei dati hanno alcuni fondamentali requisiti: richiedono che sia la sorgente dell informazione (cioè l originale) sia la destinazione (dove andrà posta la copia) siano specificare in modo implicito o esplicito; richiedono inoltre che sia specificata la quantità di dati da spostare: esistono infatti istruzioni per lo spostamento di quantità di dati che vanno da un solo bit all intera memoria. Nelle macchine con lunghezza di parola fissata, il numero di parole da spostare viene solitamente specificato nell istruzione stessa: per esempio, esistono istruzioni separate per spostare parole (32 bit) e parole corte (16 bit). Se invece consideriamo una macchina con lunghezza di parola variabile, la cosa è più complessa: in questi casi, non viene specificata la quantità di dati da copiare, ma solo l indirizzo sorgente e quello di destinazione e la copia dei dati prosegue fin quando non si trova, nei dati stessi, un campo indicatore di fine dei dati. Tanto per riportare degli esempi concreti, i processori Motorola 680x0 hanno una istruzione MOVE generale, con due operandi arbitrari, che può spostare i dati ovunque tra registri, memoria e stack. Le CPU Intel, invece, hanno istruzioni MOVE molto più limitate, ma ne hanno molte, per cui anche in questo caso è possibile spostare qualsiasi cosa ovunque. Istruzioni per operazioni diadiche Le operazioni diadiche sono quelle che combinano due operandi per produrre un risultato. Quasi tutte le macchine di livello 2 hanno istruzioni per eseguire l addizione e la sottrazione sugli interi. Ad eccezione che nei vecchi microcalcolatori ad 8 bit, anche la moltiplicazione e la divisione di interi è ormai standard. Un particolare gruppo di operazioni diadiche è quello che comprende le istruzioni booleane. In generale, è noto che esistono 16 distinte funzioni booleane a due variabili, ma poche macchine di livello 2 hanno istruzioni per tutte e 16: tanto per Autore: Sandro Petrizzelli aggiornamento: 7 luglio 2001 2

Le Istruzioni ed il flusso di controllo fare un esempio semplice, dato che la funzione che restituisce TRUE indipendentemente dagli argomenti è inutile e quindi mai implementata. Tre istruzioni booleane presenti in molte macchine sono AND, OR e XOR (exclusive OR). Istruzioni per operazioni monadiche Le operazioni monadiche hanno un unico operando e producono un unico risultato. Dato che, rispetto alle operandi diadiche, è necessario specificare un unico indirizzo anziché due, le istruzioni per le operazioni monadiche sono spesso più corte. Tipici esempi di istruzioni monadiche sono quelle per traslare o ruotare i contenuti di una parola o di un byte. In particolare, sono frequenti le istruzioni di traslazione a destra con estensione di segno: questo tipo di traslazione implica che le posizioni che si liberano a sinistra sono occupate dal bit di segno originale (0 o 1); in altre parole, è come se il bit di segno venisse allungato verso destra e, ovviamente, il segno rimane lo stesso. Tanto per chiarirci le idee, riportiamo un esempio semplice: A 11111111 11111111 11111111 11110000 Traslazione senza estensione di segno 00111111 11111111 11111111 11111100 Traslazione con estensione di segno 11111111 11111111 11111111 11111100 Un uso importante della traslazione riguarda la moltiplicazione e la divisione per potenze di due: se un numero intero positivo è traslato a sinistra di k bit, il risultato (a meno di overflow) corrisponde al numero originale moltiplicato per 2 k ; viceversa, se un numero intero positivo è traslato a destra di k bit, il risultato corrisponde al numero originale diviso per 2 k. Certe operazioni diadiche si verificano così frequentemente con particolari operandi che, a volte, le macchine di livello 2 prevedono istruzioni monadiche per eseguirle più velocemente: ad esempio, lo spostamento di 0 in una parola di memoria oppure in un registro è molto comune quando si inizializza una procedura di calcolo; si tratta di un caso particolare dell istruzione generale per muovere i dati, per cui, per questioni di efficienza, si fornisce spesso l operazione CLEAR con un solo indirizzo, corrispondente alla locazione in cui porre lo 0. Un altro esempio frequente nei calcoli è la somma di 1 ad una parola: una forma monadica di questa istruzione di addizione è l operazione di incremento unitario, che appunto aggiunge 1 alla locazione specificata come unico operando. Un ulteriore esempio è la negazione di una parola, che in realtà corrisponde a sottrarre tale parola da 0: la negazione di X corrisponde infatti a 0-X. Confronti e salti condizionati Quasi tutti i programmi hanno bisogno di poter controllare i loro dati e di poter cambiare la sequenza delle istruzioni da eseguire a seconda dei risultati. Un aggiornamento: 7 luglio 2001 3 Autore: Sandro Petrizzelli

Appunti di Calcolatori Elettronici Appendice 3 esempio semplice è la funzione per il calcolo della radice quadrata di un valore x (funzione sqrt): se x è negativo, la radice non va calcolata e va invece restituito un messaggio di errore, altrimenti il calcolo può essere eseguito. Un metodo comunque per fare questo è prevedere le cosiddette istruzioni di salto condizionato (dette anche diramazioni): tali istruzioni controllano una certa condizione e saltano ad un particolare indirizzo di memoria se essa è verificata. A volte, è presente un bit nell istruzione, che può essere posto ad 1 o a 0, il che significa che il salto deve avvenire se si verifica oppure non si verifica la condizione. Una condizione molto comune da verificare è controllare se un particolare bit della macchina vale 0 o meno. Molte macchine hanno dei bit usati per specificare determinate condizioni. Ad esempio, ci può essere un bit di overflow che viene posto ad 1 ogni volta che una istruzione aritmetica fornisce un risultato scorretto: controllando allora questo bit, si controlla di fatto l overflow sulla operazione aritmetica precedente e, in presenza di overflow, si può fare un salto ad una routine di errore. Il controllo sullo 0 è importante per i cicli e per altri scopi. Se tutte le istruzioni di salto condizionato controllassero solo un bit, allora per controllare se una intera parola vale 0 avremmo bisogno di un test separato per ciascun bit. Per evitare questa complicazione, molte macchine hanno un unica istruzione per controllare una parola e saltare se tutti i suoi bit sono 0. In pratica, l hardware contiene un registro in cui tutti i bit vengono riuniti in OR per fornire un singolo bit, che ovviamente varrà 0 se e solo se tutti i bit di partenza sono anch essi a 0. Il confronto tra due parole (o due caratteri) per vedere se sono uguali oppure quale è maggiore, è un altro caso importante, spesso applicato per esempio nelle funzioni di ordinamento. Per istruzioni di questo tipo sono necessari tre indirizzi: due per i dati da confrontare ed uno per l indirizzo del salto da effettuare se la condizione è vera. Allora, mentre i calcolatori che hanno formati di istruzioni a tre operandi non hanno problemi con questo tipo di istruzioni, quelli che prevedono non più di due operandi devono in qualche modo aggirare il problema: una soluzione molto comune è quella di prevedere una istruzione che esegue il confronto e, in base ad esso, attiva uno o più bit di condizione; l istruzione successiva va a leggere tali bit di condizione e decide se e dove effettuare il salto; in alcune situazioni particolari, i salti condizionati vengono invece gestiti tramite una istruzione che salta l istruzione successiva se si verifica la condizione considerata; evidentemente, l istruzione cui punta il salto è a sua volta, generalmente, una istruzione di salto. Non solo, ma su alcune macchine si possono saltare anche più istruzioni consecutivi, facendo specificare all istruzione stessa di confronto il numero di byte da saltare (il Motorola 68000 ha diverse istruzioni di questo tipo). Infine, le cosiddette istruzioni di salto incondizionato sono un caso particolare di istruzioni di salto, in quanto si ritiene implicitamente che la condizione sia sempre verificata. Istruzioni per la chiamata di procedure E noto che una procedura è un gruppo di istruzioni che esegue un certo compito e che può essere invocato (chiamato) da diversi punti del programma principale. Spesso si usa il termine subroutine (o routine) al posto di procedura. Autore: Sandro Petrizzelli aggiornamento: 7 luglio 2001 4

Le Istruzioni ed il flusso di controllo Quando la procedura è terminato il proprio compito, deve necessariamente tornare all istruzione seguente a quella di chiamata, il che implica che l indirizzo di ritorno debba essere trasmesso alla procedura. Ci sono allora tre posti in cui tale indirizzo di ritorno può essere memorizzato: la soluzione peggiore è di porlo in una singola locazione fissata della memoria: infatti, se la procedura chiama sua volta un altra procedura, la seconda chiamata farà sì che l indirizzo di ritorno della prima chiamata vada perso. Un leggero miglioramento si ottiene se l istruzione di chiamata della procedura provvede direttamente a memorizzare l indirizzo di ritorno nella prima parola della procedura chiamata, mentre invece la prima istruzione eseguibile della procedura stessa si trova nella seconda parola: in questo caso, la procedura potrà ritornare saltando indirettamente alla prima parola e quindi sarà anche possibile chiamare altre procedure; non sarà invece possibile che una procedura chiami se stessa (chiamata ricorsiva), in quanto anche in questo caso l indirizzo di ritorno della prima chiamata andrebbe perso ( 1 ); non funzionerebbe nemmeno uno schema cosiddetto di ricorsione indiretta, in cui per esempio la procedura A chiama una procedura B che chiama una procedura C che chiama a sua volta A; una prima soluzione davvero valida è quella di porre l indirizzo di ritorno in un registro, lasciando alla procedura chiamata la responsabilità di memorizzare il suo contenuto in un posto sicuro: in questo modo, infatti, se la procedura è ricorsiva, dovrà porre l indirizzo di ritorno in un posto diverso ogni volta che è chiamata; la soluzione migliore è quella di spingere (push) l indirizzo di ritorno in uno stack: la procedura chiamante inserire (push) l indirizzo di ritorno nello stack, mentre invece la procedura chiamata, quando ha terminato, elimina l indirizzo di ritorno dallo stack (pop) e lo mette nel contatore di programma. Con questa forma di chiamata di procedura, la ricorsione non causa alcun problema: l indirizzi di ritorno viene automaticamente salvato in modo da evitare la distruzione degli indirizzi di ritorno precedenti. Istruzioni per il controllo dei cicli La necessità di eseguire un gruppo di istruzioni per un numero fissato di volte si verifica molto spesso e quindi alcune macchine hanno istruzioni apposite per facilitare queste situazioni. Tutti gli schemi utilizzati presuppongono l uso di un contatore, che viene prima inizializzato e poi aumentato o diminuito di una certa quantità costante ad ogni ciclo; se si verifica una certa condizione, il ciclo termina. 1 La capacità di una procedura di chiamare se stessa (detta ricorsione) risulta molto importante sia per i teorici sia per i programmatori pratici aggiornamento: 7 luglio 2001 5 Autore: Sandro Petrizzelli

Appunti di Calcolatori Elettronici Appendice 3 Controllllo dii fllusso dii un programma Premessa Il flusso di controllo si riferisce alla sequenza con cui le istruzioni di un programma vengono eseguite. Nel caso più semplice, le istruzioni eseguite in successione vengono prelevate da locazioni di memoria consecutive: quindi, l ordine dinamico in cui il processore esegue effettivamente le istruzioni è lo stesso in cui appaiono nel listato del programma; il registro contatore di programma, che contiene sempre l indirizzo della prossima istruzione da eseguire, è una sorte di funzione monotona linearmente crescente con il tempo. Alterazioni a tale ordine sequenziale vengono sia da istruzioni di chiamata di procedura (in generale da istruzioni di salto), in quanto fermano l'esecuzione della procedura in corso e fanno cominciare la procedura chiamata, e da altri tipi di istruzioni (chiamate di coroutine oppure trap oppure interrupt) che esamineremo nel dettaglio nei prossimi paragrafi: in tutti questi casi, l ordine di esecuzione delle istruzioni è alterato rispetto a quello sequenziale ed il registro contatore di programma non cresce più linearmente, ma in modo decisamente più complicato. Istruzioni per la chiamata di procedure Una procedura è un gruppo di istruzioni che esegue un certo compito e che può essere chiamato da un qualsiasi punto del programma. Quando la procedura ha finito il suo compito, deve ritornare alla istruzione che segue la chiamata: questo implica che l' indirizzo di ritorno debba essere in qualche modo trasmesso ala procedura. Ci sono 3 posti in cui l'indirizzo di ritorno può essere sistemato: la memoria, un registro o lo stack. Di queste però, la soluzione peggiore è l'uso di una locazione fissa di memoria: infatti, ciò escluderebbe la possibilità di effettuare una seconda chiamata, in quanto ogni chiamata distruggerebbe l'indirizzo di ritorno della chiamata precedente. Migliore è l'uso di un registro, nel senso che l'istruzione di chiamata pone in tale registro l'indirizzo di ritorno. Tuttavia, con questa scelta, per chiamate successive (cioè da una procedura ad un altra procedura e così via) si dovrebbero occupare più registri (a meno di non voler ritrovare lo stesso errore di prima) e la cosa non è ovviamente opportuna. La soluzione migliore è senz'altro l'uso dello stack: ogni istruzione di chiamata spinge (push) l'indirizzo di ritorno nello stack; quando la procedura è finita, estrae (pop) ed elimina l'indirizzo di ritorno dallo stack e lo mette nel contatore di programma. In questo modo, si possono supportare tutte le chiamate di procedura, incluse le cosiddette chiamate ricorsive, nelle quali cioè una procedura chiama se stessa. Le coroutine Quando c'è una istruzione di chiamata di procedura, c'è una evidente quanto fondamentale distinzione tra la procedura chiamante e la procedura chiamata. Supponiamo che ci sia una procedura A che, ad un certo punto della propria esecuzione, chiama la procedura B: Autore: Sandro Petrizzelli aggiornamento: 7 luglio 2001 6

Le Istruzioni ed il flusso di controllo tale procedura (a meno di ulteriori chiamate) lavora per un certo tempo (quello necessario alla esecuzione delle sue istruzioni) e poi ritorna alla procedura A; per chiamare B, A usa una istruzione di chiamata di procedura la quale pone l'indirizzo di ritorno (che è l'indirizzo dell'istruzione seguente la chiamata) sulla cima dello stack e l'indirizzo di B (cioè della prima istruzione di B) nel contatore di programma; una volta terminata, B usa una istruzione di ritorno, la quale preleva l'indirizzo di ritorno dallo stack e ponendolo nel contatore di programma. La cosa fondamentale da notare in questi passaggi è la seguente: la procedura chiamata (cioè B) viene eseguita, dopo ogni chiamata, sempre dall'inizio alla fine; la procedura chiamante (cioè A), una volta eseguita B, riprende non dall'inizio ma dall'istruzione seguente la chiamata. chiamata di A dal programma principale ritorno da A al programma principale procedura A chiamata B chiamata B chiamata B ritorno procedura B ritorno Schematizzazione delle istruzioni di chiamata a procedura e di ritorno da procedura Una cosa diversa accade quando invece due procedure A e B si chiamano a vicenda: per esempio, supponiamo che in ciascuna di queste due procedure ci siano varie chiamate all'altra; supponiamo che per prima venga chiamata A (per esempio dal programma principale); all'interno di A viene chiamata B, la quale quindi comincia dall'inizio; ad un certo punto di B c'è una chiamata ad A: tale chiamata è però tale che l'esecuzione di A non avvenga dall'inizio, bensì dall'istruzione successiva alla chiamata di B; proseguendo, c'è una nuova chiamata a B, la quale riprende l'esecuzione non dall'inizio, ma nel punto in cui aveva poco fa chiamato A e così via. La figura seguente illustra quanto appena descritto: aggiornamento: 7 luglio 2001 7 Autore: Sandro Petrizzelli

Appunti di Calcolatori Elettronici Appendice 3 chiamata di A dal programma principale ritorno da A al programma principale procedura A procedura B Schematizzazione delle istruzioni di chiamata a coroutine e di ritorno da coroutine Due procedure che si considerano a vicenda una procedura (poiché sono chiamate, eseguono un certo lavoro e poi ritornano all'istruzione che segue la chiamata) vengono definite coroutine. Da quanto si è detto, si deduce che, per un meccanismo di questo tipo, non bastano le normali istruzioni di chiamata e di ritorno. Per poter realizzare questo meccanismo serve una istruzione che possa semplicemente scambiare la cima dello stack con il contatore di programma, usando per esempio un registro interno come variabile di scambio. Vediamo allora di analizzare il meccanismo considerando un programma principale e le due procedure A e B: il programma principale chiama A: viene memorizzato l'indirizzo di ritorno nello stack e viene inserito nel contatore di programma (program counter) l'indirizzo della prima istruzione di A; ad un certo punto della propria esecuzione, A chiama B: l'indirizzo di ritorno viene impilato e l'indirizzo della prima istruzione di B viene inserito nel contatore di programma; ad un certo punto della propria esecuzione, B chiama A: vengono scambiati il program counter e la cima dello stack: in tal modo, il program counter contiene l'indirizzo della istruzione di A successiva alla chiamata di B, mentre la cima dello stack contiene l'indirizzo dell'istruzione di B successiva all'ultima chiamata; ad ogni chiamata lo stack non viene né incrementato né decrementato; alla fine delle istruzioni di B, si passa ad eseguire le ultime istruzioni di A con lo stack che contiene, in cima, l'indirizzo di ritorno al programma principale. Una istruzione di chiamata di coroutine viene spesso detta istruzione di resume ed è presente nelle macchine di livello 2 (livello del linguaggio Assembler). Autore: Sandro Petrizzelli aggiornamento: 7 luglio 2001 8

Le Istruzioni ed il flusso di controllo I trap Un trap è semplicemente un tipo di chiamata di procedura: in particolare, si tratta di una chiamata automatica attivata da una certa condizione provocata dal programma (ad esempio l'overflow). Quando si verifica una certa condizione (piuttosto rara per la verità) durante l'esecuzione di un programma, interviene un trap, il quale devia il flusso di controllo verso una locazione di memoria fissata; in tale locazione c'è un salto ad una opportuna procedura chiamata gestore di trap: tale procedura esegue l azione appropriata per l evento che l ha determinata, come ad esempio la stampa di un messaggio di errore a video. Il punto essenziale sulle trap è dunque quello di essere attivate da certe condizioni eccezionali provocate dal programma in corso di esecuzione e scoperte dall hardware oppure dal microprogramma. Non è l unica soluzione possibile, in quanto, ad esempio in presenza di overflow, si potrebbe procedere in altro modo: in primo luogo, si predispone un apposito registro da 1 bit che viene posto ad 1 quando si verifica l overflow; in secondo luogo, il contenuto di tale registro deve essere controllato da una istruzione successiva, che sarà del tipo salta se è attivo il bit di overflow. Il problema di una simile scelta è che comporta lentezza e spreco di spazio, al contrario invece dei trap, che risparmiano sia il tempo sia la memoria rispetto ai controlli espliciti fatti dai programmatori. Dal punto di vista concreto, i trap vengono implementati spesso demandando al microprogramma il compito di fare i test. Alcune condizioni comuni che possono provocare trap sono gli overflow e gli underflow in virgola mobile, gli overflow di interi, la violazione di protezioni, i codici operativi non definiti, gli overflow degli stack, i tentativi di far partire un dispositivo di I/O non esistente, la divisione per 0 e così via. Gli interrupt Anche gli interrupt, come i trap, sono cambiamenti nel flusso di controllo durante l'esecuzione del programma: tuttavia, al contrario dei trap, essi non dipendono dall'esecuzione del programma, bensì da qualcos'altro (spesso sono in relazione a operazioni di I/O); ad esempio, un programma può istruire il disco (tramite il suo controllore) per avviare il trasferimento di informazioni in memoria, indicando che dovrà fornire un interrupt non appena il trasferimento sarà finito. Al pari dei trap, un interrupt ferma il programma in corso e trasferisce il controllo ad un gestore di interrupt, il quale esegue le azioni appropriate; quando ha finito, il gestore di interrupt restituisce il controllo al programma interrotto. In particolare, il programma deve riprendere dall'esatto punto in cui era stato sospeso, il che significa che è necessario ripristinare tutti i registri interni allo stato di pre-interruzione. In due parole, possiamo dire che, mentre i trap sono sincroni con il programma, gli interrupt sono asincroni rispetto ad esso: se un programma viene eseguito tante volte con lo stesso ingresso, i trap si manifesteranno sempre e negli stessi punti, mentre invece gli interrupt possono anche variare, a seconda ad esempio della disponibilità dei dispositivi di I/O. I moderni calcolatori sono spesso sotto il controllo di un insieme gerarchico di programmi, alla cui sommità c'è naturalmente il sistema operativo. Per poter trasferire il controllo del calcolatore da un certo programma ad un altro (a seconda della operazione che si intende fare), è necessario fornire un meccanismo mediante il quale il funzionamento di un programma possa essere temporaneamente aggiornamento: 7 luglio 2001 9 Autore: Sandro Petrizzelli

Appunti di Calcolatori Elettronici Appendice 3 "interrotto" a favore di un altro. Una interruzione, come si detto poco fa, è un evento oppure un preciso segnale esterno, che provoca appunto la sospensione del funzionamento di un programma. Quando si verifica una interruzione, il contenuto di tutti i registri viene immediatamente salvato e il controllo del calcolatore passa ad una routine di servizio di interruzione, facente parte, in genere, del sistema operativo: essa determina da dove viene la richiesta di interruzione (generalmente esaminando un certo numero di flag o indicatori) e quindi trasferisce a sua volta il controllo ad una routine per la gestione di quel particolare tipo di interruzione; successivamente, il funzionamento del programma viene ripreso esattamente dal punto in cui era stato sospeso: questo lo si ottiene facilmente inserendo in tutti i registri i valori che erano stati salvati al momento della interruzione. Tra le cause più comuni di interruzioni vi sono operazioni di input e output di dati, oppure il rilevamento di un errore in un programma o anche il fatto che un programma ha superato il limite di tempo previsto. Il repertorio di istruzioni della maggior parte degli elaboratori include delle apposite istruzioni che attivano o disattivano le interruzioni. Esempi di interrupt HARDWARE sono: - divisione per zero - caduta di tensione - comunicazione eventi hardware al processore - esaurimento carta stampante - completamento di una azione di un drive e così via. Esempi di interrupt SOFTWARE sono invece: - attivazione programmi del BIOS definiti dal sistema operativo - attivazione programmi del BIOS definiti e gestiti dal software applicativo - attivazione programmi di tabella (memorizzazione di indirizzi). Ogni interrupt specifico è identificato da un numero che ne specifica il tipo. Per ogni interrupt, come si è detto, è previsto un programma gestore di interrupt, che esegue l'intero lavoro richiesto dall'interrupt. L'indirizzo del gestore è contenuto a sua volta nella cosiddetta tabella degli interrupt. Sequenza dii operaziionii per lla gestiione dii un iinterrupt Vogliamo adesso vedere, con sufficiente dettagliato, le azioni che vengono compiute quando si verifica un interrupt, nel caso specifico che questo provenga da un dispositivo di I/O, come ad esempio un disco che ha appena terminato un trasferimento di dati verso la memoria: 1) il controllore del dispositivo asserisce una apposita linea di interrupt sul bus di sistema, al fine di avvisare la CPU dell interrupt e quindi avviare la sequenza di gestione; 2) non appena la CPUU è pronta a servire l interrupt, asserisce un apposito segnale di ricevimento dell interrupt sul bus; 3) vedendo l asserimento del suddetto segnale, il controllore del dispositivo pone un numero intero sulla linea dei dati, al fine di identificare se stesso; tale numero è detto vettore di interrupt; Autore: Sandro Petrizzelli aggiornamento: 7 luglio 2001 10

Le Istruzioni ed il flusso di controllo 4) la CPU legge il vettore di interrupt e lo salva temporaneamente; successivamente, essa memorizza nello stack sia il registro contatore di programma sia il registro di stato (PSW, Program Status Word); 5) a questo punto, la CPU ricerca il nuovo valore da inserire nel registro contatore di programma: usa quindi il vettore di interrupt per indicizzare la tabella degli interrupt, situata nel fondo della memoria: il valore così prelevato dalla tabella degli interrupt corrisponde all indirizzo della routine di servizio di interrupt per il dispositivo che ha provocato l interrupt, per cui tale indirizzo viene posto nel program counter; 6) così facendo, comincia l esecuzione della routine di servizio dell interrupt verificatosi: la prima operazione compiuta consiste nel salvare nello stack il contenuto di una serie di registri (tranne il program counter, che è stato già salvato), in numero tale da poter riprendere successivamente l esecuzione del programma esattamente da dove è stata interrotta; 7) la seconda operazione consiste nell individuare univocamente il dispositivo che ha causato l interrupt, dato che spesso più dispositivi vengono serviti dalla stessa routine: basta andare a leggere il contenuto di un particolare registro dei vari dispositivi potenzialmente coinvolti; 8) può adesso partire la gestione vera e propria dell interrupt, tramite l esecuzione del codice opportuno della routine di servizio dell interrupt; 9) al termine dell esecuzione, tutti i registri precedentemente salvati nello stack vengono ripristinati; eventualmente, si può anche inviare un segnale al dispositivo che ha causato l interrupt, per segnalare che questo è stato servito; 10) infine, viene eseguita l istruzione return form interrupt, che riporta la CPU nella modalità e nello stato in cui si trovata appena prima che si verificasse l interrupt. Da questo momento, il calcolatore continua come se nessuna interruzione si fosse verificata. La gestiione trasparente degllii iinterrupt Un concetto chiave legato agli interrupt è la cosiddetta trasparenza: quando capita un interrupt, vengono avviate determinate azioni ed eseguite determinate procedure, ma, quando tutto questo è stato fatto, il calcolatore dovrebbe ritornare nello stesso stato in cui era prima dell'interrupt. Una routine di interrupt che ha questa proprietà si dice che è trasparente. Mentre sui piccoli calcolatori il processo di gestione di interrupt risulta complessivamente uguale a quello prima descritto, nei grandi calcolatori le cose si complicano per via del fatto che ci sono molti dispositivi di I/O e molti utenti. Infatti, per questi calcolatori spesso accade che, mentre si sta eseguendo una certa routine di interrupt, ci siano una o più altre richieste di interrupt da parte di altri dispositivi. Per uscire da situazioni di questo tipo, si possono adottare due diversi meccanismi: una prima soluzione è la seguente: quando una routine di interrupt viene chiamata, prima ancora di salvare il contenuto dei registri, essa deve fare in modo che ogni altra possibile richiesta di interrupt venga ignorata; deve cioè "disabilitare" gli interrupt seguenti. In tal modo, gli interrupt vengono eseguiti in ordine sequenziale: una volta terminata una routine, si passa all'interrupt aggiornamento: 7 luglio 2001 11 Autore: Sandro Petrizzelli

Appunti di Calcolatori Elettronici Appendice 3 immediatamente successivo, poi al successivo e così via. Tuttavia, questo metodo può comportare dei problemi per quei dispositivi che non possono tollerare grandi ritardi della CPU nel prestare loro attenzione; la strategia più conveniente è la seguente: tenendo conto del tempo che i vari dispositivi possono passare in attesa di essere "serviti", si assegna a ciascuno di essi (cioè ai rispettivi interrupt) una priorità, che ovviamente sarà alta per quelli critici e più bassa per gli altri. In questo modo, mentre si sta eseguendo una routine di interrupt di priorità N, ogni tentativo di provocare un interrupt fatto da qualsiasi dispositivo con priorità minore di N è ignorato, finché la routine di interrupt non è finita; solo alla fine della suddetta routine, saranno servite le richieste di interrupt, sempre a partire da quelle con priorità maggiore. Nel caso dei processori INTEL, ci sono 2 livelli di interrupt, cioè 2 priorità: ci sono gli interrupt mascherabili (bassa priorità) e quelli non mascherabili (alta priorità). Gli interrupt della seconda categoria sono usati solo per segnalare errori gravi imminenti, mentre, ad esempio, tutti i dispositivi di I/O usano interrupt mascherabili. Controllller dii iinterrupt Questo dispositivo serve per gestire le priorità dei vari interrupt, nel senso che, quando ci sono contemporaneamente due o più richieste di interrupt al microprocessore, stabilisce quale debba essere servita per prima e quali debbano restare in attesa. Autore: Sandro Petrizzelli e-mail: sandry@iol.it sito personale: http://users.iol.it/sandry Autore: Sandro Petrizzelli aggiornamento: 7 luglio 2001 12