Un registro fondamentale della CPU è lo Status Register. Rappresenta la memoria storica di quanto è accatuto durante l'esecuzione delle precedenti istruzioni. Lo Status Register è formato da una o più parole di memoria interna alla CPU, ed è suddiviso in due parti distinte; ciascuna riservata ad uno dei due diversi modi di operare del processore: Supervisore o Utente. Ad ogni bit, o gruppo di bit, è attribuito un preciso significato, il cui contenuto viene alterato durante l'esecuzione dei microprogrammi associati alle istruzioni, in modo da segnalare al programmatore (che deve limitarsi a consultare lo Status Register con apposite istruzioni) la corretta esecuzione o il verificarsi di particolari condizioni, come il superamento delle capacità di calcolo, o l'uguaglianza di due dati appena comparati. Per esempio volendo far saltare il nostro programma ad una subroutine quando il contenuto dell'accumulatore è 0, basta confrontare il contenuto dell'accumulatore con il valore desiderato, nel nostro caso 0. Questa operazione spetta alla ALU che provvederà a settare il relativo FLAG nello Status Register. Immediatamente dopo, onde evitare che altre istruzioni alterino tale flag, poniamo un'istruzione di salto condizionato, la cui condizione è proprio lo stato di tale flag nello Status Register. L'unità di governo non fa altro che modificare il contenuto del Program Counter, inserendovi l'indirizzo della sub-routine, se il flag è settato, altrimenti non effettua alcuna operazione, passando all'interpretazione della successiva istruzione.
Lo stack, traducibile in italiano con il termine pila o catasta, rappresenta una porzione di memoria principale che viene dedicata all'immagazzinamento dei dati temporanei. Il suo modello logico è il classico LIFO
, ovvero l'ultimo dato accatastato è il primo che può essere prelevato, e cioè in lettura si procede a ritroso rispetto all'oridine usato in scrittura.
Nella quasi totalità dei casi viene utilizzato per ricopiare il contenuto dei registri della CPU, in modo che i dati contenuti prima di una elaborazione non vadano persi, ciò accade quando si richiama una subroutine per eseguire un certo compito. La stessa istruzione di salto con ritorno provvede a memorizzare il contenuto del Program Counter, che punta già alla prossima istruzione, nello stack, e poi si limita semplicemente ad aggiornare il Program Counter, inserendo l'indirizzo della subroutine da elaborare. La CPU quando trova l'istruzione di ritorno, non fa altro che prelevare dallo Stack l'indirizzo precedentemente memorizzato e ricopiarlo nel Program Counter.
E' evidente quanto sia critica la gestione dello stack per il corretto funzionamento della macchina, e per facilitarne la gestione tutti, o quasi, i processori hanno un apposito registro detto Stack Pointer, che ha il compito di indicare quale sia la posizione attuale nell'area stack, quindi funge da indice, ed inoltre hanno specifiche istruzioni per inserire ed estrarre i dati nello stack. Lo stack viene anche utilizzato per memorizzare i dati intermedi del programma, come le variabili, per questo motivo nei sistemi multitasking ci sono più di uno stack; di solito vi è uno stack di sistema riservato all' OS ed uno per ciascun task in esecuzione, ed è compito dell'OS aggionare il contenuto dello Stack Pointer ad ogni cambio di task, in modo che punti sempre all'area corretta.
Gli interrupt sono dei segnali esterni che giungono dalle periferiche connesse alla CPU, e che indicano che si deve eseguire con urgenza un programma. Tale comunicazione presuppone l'esistenza di una sorta di autostrada, detta BUS , sulla quale corrono i segnali. Vi sono delle entrate e delle uscite, ciascuna delle quali corrisponde ad una precisa unità del sistema. In questo momento non è possibile affrontare approfonditamente questa problematica, e si rimanda ad una prossima puntata, specificatamente dedicata al funzionamento dei BUS. Per i nostri scopi è sufficiente sapere che le singole corsie, di queste autostrade, possono essere rappresentate con un segmento orientato, ovvero un comunissimo segmento corredato di una o due frecce che indicano la direzione consentita per il transito delle informazioni.
La CPU, non appena riceve la richiesta di interruzione, che possiamo indicare con la sigla IRQ , blocca il flusso dell'elaborazione in corso, copia il contenuto di tutti i suoi registri nello stack, e lancia il programma associato a quel particolare tipo di interrupt, di solito è un gestore della periferica che ha inviato la richiesta di interruzione. Terminata l'esecuzione del programma, viene ripristinato lo stato originario dei registri, prelevando il vecchio contenuto precedentemente depositato nello stack di sistema. E' evidente che l'ordine di lettura è fondamentale, altrimenti i registri non conterranno i dati giusti. Ripristinati i contenuti dei registri, riprende l'elaborazione, precedentemente interrotta, come se l'interruzione non ci fosse mai stata. Il gestore di interrupt deve essere una routine particolarmente veloce, onde ridurre al minimo la probabilità, che durante la sua elaborazione, sopraggiunga una nuova IRQ da parte di un'altra periferica. A dire il vero gli interrupt sono gerarchicamente ordinati in modo che una gestione di un interrupt possa essere interrotta, a sua volta, solo se sopraggiunge una richiesta appartenente ad un livello superiore, le altre vengono accodate o perse a seconda dell'architettura del sistema. In questo caso si parla di Interruzioni Mascherabili. Spesso è il gestore degli interrupt a decidere se disabilitare o meno le altre IRQ, quasi sempre sono disattivate solo quelle appartenenti ai livelli ritenuti meno importanti. In contrapposizione ci sono anche le interruzioni di tipo Non Mascherabile, generalmete indicata con l'acronimo NMI , utilizzate per segnalazioni particolarmente importanti legate a situazioni di pericolo come il comando di Reset, al quale viene generalemente attribuita una specifica corsia, o l'interruzione dell'alimentazione.
Vediamo in dettaglio cosa accade quando una periferica richiede l'intervento della CPU per gestire uno scambio di dati. Supponiamo di avere un sistema molto elementare come quello presente in figura.

1) La periferica inoltra la richiesta sulla linea indicata IRQ.
2) La CPU salva il contenuto dei suoi registri sullo stack.
3) Avvia il programma incaricato di gestire l'interrupt.
4) Il programma effettua l'interrogazione della periferica attraverso la linea indicata con DATI.
5) La CPU ripristina il contenuto dei propri registri prelevandolo dallo stack.
6) La CPU riprende l'esecuzione del programma come se non ci fosse mai stata l'interruzione.
E' evidente che volendo gestire più di una periferica sono necessarie più linee IRQ, come mostrato nella figura qui sotto.

La complessità del sistema diventa ben presto ingestibile, anche perchè è necessario duplicare anche le linee DATI. Per ora ci occuperemo di semplificare la segnalazione degli interrupt, mentre per quella relativa allo scambio dei dati si rimanda ad una prossima puntata dedicata ai bus.
La soluzione ottimale consiste nel mascherare la complessità del sistema al processore. Si realizza una sorta di interfaccia tra processore e periferiche, in modo che tutte le IRQ vengano ridirette a tale interfaccia, ed essa generi una sola IRQ verso il processore, come illustra la figura qui sotto:

L'IRQ Adapter generalmente possiede 2 registri, consultabili dalla CPU. Il primo, detto MASK Register, viene impostato dal Sistema Operativo attraverso il gestore degli interrupt, e serve per indicare, attraverso l'attivazione o meno dei suoi bit, quali fonti di interruzione possono inoltrare una IRQ alla CPU. Il secondo detto Interrupt Status Register, invece, funge da memoria storica, ovvero segnala, sempre con il solito sistema (associare un bit a ciascuna periferica connessa) da quale dispositivo sia giunta una richiesta.
In questo modo il gestore degli interrupt, impostando opportunamento il contenuto della maschera, effettua l'abilitazione degli IRQ ritenuti di importanza maggiore, da qui il nome di mascheramento delle interruzioni, e al tempo stesso può facilmente identificare da quale dispositivo sia giunta la richiesta.
Sembra che abbiamo solo spostato altrove il problema, in realtà è così, ma abbiamo gettato le basi per semplificare il sistema quando le fonti di interrupt sono davvero tante. Chiariamo subito questo punto con un esempio. Supponiamo di avere un sistema formato da 1 processore e ben 16 periferiche. Con il primo approccio dobbiamo avere 16 distinte linee di IRQ servite dal processore, mentre con il secondo ne basta una ed un Adapter in grado di gestirne 16, se ciò non fosse possibile, si potrebbe usare più di un Adapter, come mostrato in figura, dove ciascuno è abilitato a servire 8 periferiche.

Con lo schema appena illustrato (con 2 soli livelli e con una sola linea IRQ del processore) si può arrivare a gestire ben 64 periferiche, ovviamente il gestore degli interrupt diventa più complesso, perché deve consultare IRQ Adapter 0, solo per determinare quale dei possibili 8 IRQ Adapter di livello inferiore abbia richiesto l'attenzione, e quindi consultare l'ISR appropriato, come ciò avvenga esula dalla finalità di questo articolo. Di contro è possibile disabilitare un intero gruppo di periferiche agendo su un singolo bit del MASK Register dell'IRQ Adapter 0.
Gli IRQ Adapter sono di solito inglobati in chip, molto più complessi, che svolgono diverse funzioni di supporto al processore, questi chip, sono erroneamente definiti, in ambiente Wintel, CHIPSET, mentre in altri ambienti hanno nomi più specifici, basti pensare ai Complex Interface Adapter della Commodore, che non solo smistano gli IRQ, ma hanno al loro interno dei Timer programmabili con tanto di allarme, sempre gestito via IRQ, e dei controllori per le porte di I/O come seriale e parallela.
Le CPU più moderne hanno al loro interno già un limitato IRQ Adapter, e quindi semplificano di molto la vita del progettista di sistema, che deve occuparsi solo di eseguire la conversione degli IRQ provenienti dalle periferiche in modo che vengano automaticamente decodificati dal processore; per esempio i processori Motorola della famiglia 680X0 hanno tre linee di segnalazione, attraverso le quali riescono a decodificare ben 7 livelli distinti di interrupt, infatti 2^3 = 8, una configurazione viene utilizzata per indicare che non ci sono IRQ.
A questo punto è chiaro che il verificarsi dell'interrupt sfugge al controllo del programmatore, essendo strettamente legato alle comunicazioni hardware che avvengono tra le varie unità. Per consentire un analogo controllo da parte del programmatore sono state introdotte le eccezioni, note anche con il nome di Trap, ma alcuni costruttori si ostinano a chiamarle interrupt. Sono attivate da specifiche istruzioni macchina, ed in passato sono state utilizzate per gestire le chiamate al Sitema Operativo:
1) in un prefissato registro della CPU è memorizzato un particolare codice che indica il tipo di routine da lanciare, ed in altri registri sono memorizzati i parametri da passare;
2) si invoca la Trap, passando così il controllo al gestore delle eccezioni, che deve decodificare la richiesta ed avviare l'esecuzione della specifica routine.
Questo modo di procedere è alquanto obsoleto, e riflette una vecchia concezione della programmazione degli OS, ciò nonostante è ancora vivo e vegeto e struttato da sistemi molto diffusi come MacOS e MS-Dos sul quale poggia, ancora oggi, l'ambiente operativo Windows . L'uso ottimale delle eccezioni e quello del lancio di un gestore di situazioni critiche derivanti da errori di elaborazione, come la corruzione di dati vitali o l'esecuzione di operazioni errate o illegali, cosa che avviene nei sitemi AmigaOS con la fatidica comparsa della Software Failure o Guru Meditation per i nostalgici, o del Blue Screen nei più recenti Windows .
Quando la CPU riceve una richiesta di interruzione o di eccezione entra automaticamente in modo Supervisore, abilitando così alcune particolari funzioni o l'accesso ad aree di memoria altrimenti nascoste. Questo modo, a differenza di quello standard detto Utente, consente di manipolare dati vitali per la stabilità del sistema in piena sicurezza, perché non potrebbero essere alterati per errore in modo Utente, generalmente utilizzato per eseguire le applicazioni lanciate dall'utente. Negli OS di vecchia concezione come MS-Dos o MacOS l'intero OS è eseguito in modalità Supervisore, ecco perché le routine vanno richiamate attraverso particolari procedure basate sull'uso di eccezioni o interrupt, mentre in quelli di concezione più moderna solo il microkernel, o parte di esso gira in modalità Supervisore, e questo accade in sistemi come QNX o AmigaOS , mentre tutte le altre funzioni sono considerate come dei task che girano in modalità Utente, ciò non solo agevola le chiamate delle routine dell'OS, ma anche la sua realizzazione, in quanto può essere scomposto in moduli interscambiabili e aggiornabili singolarmente, mentre con il vecchio approccio è necessario ricompilare tutto l'OS ad ogni modifica.
Anche questa puntata è giunta al termine, la prossima farà il punto dello stato attuale e delle diverse tendenze del mercato.