35
La gestione degli interrupt TE@AC TEAM Alessandro Memo Gestione degli interrupt 1 Perchè gli interrupt? Modalità standard dell'I/O, scarsa efficenza, modalità collegata agli eventi, che cos'è un interrupt? 2" Salvataggio e ripristino del contesto Contesto minimo a carico della CPU e contesto specifico a carico del programmatore. 3" La gestione degli interrupt nell'architettura INTEL Il ciclo di un interrupt, la Interrupt Vector Table. 4" La gestione degli interrupt nei PC Il problema della contemporaneitá e della prioritá, il Programmable Interrupt Controller, gli INT e gli IRQ, il segnale EOI. 5" Realizzazione di una Interrupt Service Routine La modifica della Vector Table, il caricatore e le modalitá residente e transiente, il problema dei segmenti, il problema della rientranza, il tick clock e l'interrupt 1C, esempi in linguaggio macchina (DEBUG), in Assembler e in C. 6" Gli interrupt software Le chiamate di sistema, le funzioni del DOS, la portabilita' e la standardizzazione. INDICE DETTAGLIATO DELLA SCHEDA 1. Perchè gli interrupt inefficenza del polling animazione sull'inefficenza del polling verifica in C sull'inefficenza dell'interrupt metafora sull'interrupt efficenza dell'interrupt metafora sull'inefficenza dell'interrupt interrupt hardware e software pag. 1 di 35

Prima proposta di scheda concettuale · Web viewIl PIC, prima di poterlo utilizzare, va inizializzato inviando 3 o 4 parole di inizializzazione (dette ICW1-ICW4 da Inizialization

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

La gestione degli interrupt TE@AC TEAM Alessandro Memo

Gestione degli interrupt

1 Perchè gli interrupt? Modalità standard dell'I/O, scarsa efficenza, modalità collegata agli eventi, che cos'è un interrupt?

2"Salvataggio e ripristino del contesto Contesto minimo a carico della CPU e contesto specifico a carico del programmatore.

3"La gestione degli interrupt nell'architettura INTEL Il ciclo di un interrupt, la Interrupt Vector Table.

4"La gestione degli interrupt nei PC Il problema della contemporaneitá e della prioritá, il Programmable Interrupt Controller, gli INT e gli IRQ, il segnale EOI.

5"Realizzazione di una Interrupt Service Routine La modifica della Vector Table, il caricatore e le modalitá residente e transiente, il problema dei segmenti, il problema della rientranza, il tick clock e l'interrupt 1C, esempi in linguaggio macchina (DEBUG), in Assembler e in C.

6"Gli interrupt software Le chiamate di sistema, le funzioni del DOS, la portabilita' e la standardizzazione.

INDICE DETTAGLIATO DELLA SCHEDA

1. Perchè gli interrupt inefficenza del polling animazione sull'inefficenza del polling verifica in C sull'inefficenza dell'interrupt metafora sull'interrupt efficenza dell'interrupt metafora sull'inefficenza dell'interrupt interrupt hardware e software definizione di interrupt

2. Salvataggio e ripristino del contesto Interrupt Service Routine il contesto attuale contesto minimo, specifico e invariante il contesto minimo nelle CPU INTEL verifica con il DEBUG del contesto minimo

3. L'interrupt nelle CPU INTEL

pag. 1 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memoclassificazione degli interrupt nelle CPU INTEL interrupt hardware mascherabile interrupt hardware non mascherabili interrupt software eccezioni faults eccezioni traps eccezioni aborts tabella dei tipi dentificativi degli interrupt cosa succede nella CPU quando arriva un interrupt

4. Gli interrupt nei PC fonti di interrupt nei PC PIC (8259 Programmable Interrupt Controller) registri IMR, ISR ed IRR del PIC inzializzazione e controllo del PIC controllo del PIC animazione sui compiti svolti dal PIC

5. Realizzare una ISR scelta del tipo di interrupt l'interrupt 1Ch note sulla scrittura delle ISR l'accesso alle variabili cenni sulla segmentazione salvataggio del contesto specifico allocazione residente aggiornamento tabella dei vettori delle interruzioni loader il problema della rientranza

6. Gli interrupt software portabilità del software BIOS, DOS e driver

 

1. Perché gli interrupt?

Tutti i programmi scritti con i tradizionali linguaggi imperativi, dal Pascal al C++, per accedere all'I/O (ad esempio la tastiera e la stampante) utilizzano funzioni tipo readln(), scanf(), cin() ed altre che svolgono il loro compito con un'inefficenza incredibile. Nell'esempio descritto nell'animazione si sprecano 499.999 istruzioni per ogni effettiva istruzione di lettura di I/O, con un'efficenza di 1/500.000 !!! La causa di tutto ciò è la tecnica adottata per acquisire i dati, normalmente detta polling, in cui per effettuare la lettura di un dato si acquisisce

pag. 2 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memocontinuamente lo stato della periferica in attesa che il dato risulti disponibile: nel caso di periferiche lente (stampante) o pilotate direttamente dall'uomo (tastiera/mouse) l'attesa del dato determina inevitabilmente una grave inefficenza.

METAFORA:

Si pensi ad un insegnante che sta spiegando in classe e viene interrotto dal bussare alla porta:

Termina la frase in corso

Si annota da qualche parte dove era arrivato

Fa entrare chi lo ha interrotto e lo riconosce

Gestisce la richiesta in base a chi lo ha interrotto e che cosa gli richiede

Se in questa gestione utilizza delle risorse che stava usando prima (ad esempio la lavagna, parte della cattedra, etc.), ne salva lo stato precedente

Alla fine ripristina lo stato delle eventuali risorse utilizzate

Saluta chi lo ha interrotto e riprende da dove era arrivato, come se non fosse successo niente.

 L'alternativa è ricorrere a linguaggi orientati ad eventi, o più semplicemente utilizzare gli interrupt. Quando si deve acquisire un dato, si puó proseguire con un altro programma e lasciare che sia la periferica ad avvisarci quando il dato è disponibile. In quel momento si accantonerá momentaneamente il programma in corso e si leggerá il dato. In tal modo l'efficenza ritorna a livelli accettabili.

Ovviamente la modesta perdita di tempo introdotta per accantonare momentaneamente il programma e poi riprenderlo (scambio di contesto) determina il limite dell'utilizzo degli interrupt: se la frequenza dei dati di I/O gestiti è elevata, i tempi di scambio del contesto non sono piú trascurabili rispetto alla gestione del dato, e quindi anche la tecnica dell'interrupt diventa inefficente. In tal caso si potrebbe ricorrere alla tecnica del DMA (Direct Memory Access) [presumibilmente affrontata in Elettronica ].

METAFORA:

Si pensi al telefono. La sua suoneria può essere vista come un interrupt che ci avvisa quando si manifesta l'evento arrivo di una chiamata.

Se noi volessimo gestire questo tipo di evento in modalità polling dovremmo alzare la cornetta ogni 15 secondi, per verificare se nel frattempo è qualcuno ci sta chiamando. L'inefficenza è

pag. 3 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo

evidentissima!

La gestione a suoneria (leggi interrupt) è sicuramente più efficente, ma fino a quando? Se le chiamate arrivano una ad ogni ora, non ci sono problemi di sorta, se arrivano una ogni 5 minuti è quasi impossibile continuare a lavorare. Se poi arrivassero continuamente, sarebbe meglio disabilitare la suoneria e rispondere alle chiamate in arrivo semplicemente tenendo sempre alzata la cornetta (paragonabile al DMA?).

Per quanto abbiamo detto fino ad ora, il concetto di interrupt è strettamente collegato a quello di gestione di un dato, ma vi è anche un'altra possibilità. Gli interrupt infatti si possono genericamente dividere in due categorie:

interrupt hardware

sono quegli interrupt generati da dispositivi esterni alla CPU, che hanno il compito di comunicare il verificarsi di eventi esterni (tipo quelli di cui abbiamo parlato fino ad ora)

interrupt software

sono delle istruzioni (INT xx) che possono essere assimilate alle chiamate di sottoprogrammi (CALL xx) ma che sfruttano il meccanismo delle interruzioni per passare il controllo dal programma chiamante a quello chiamato, e viceversa; vengono utilizzati per accedere direttamente alle risorse del Sistema Operativo

Concludiamo questa prima parte con la definizione formale di interrupt, tratta da un tradizionale testo scolastico:

Un interrupt è un segnale o un messaggio, generalmente di natura asincrona, che arriva

alla CPU per avvisarla del verificarsi di un certo evento

 

2. Salvataggio e ripristino del contesto

Un programma in esecuzione è normalmente un insieme di istruzioni eseguite una dopo l'altra. Quando arriva un interrupt, la CPU deve mandare in esecuzione un programma particolare, detto ISR (da Interrupt Service Routine) predisposto ad hoc dal programmatore per quel particolare interrupt.

Affinchè il meccanismo dell'interruzione funzioni correttamente, è necessario che tutte le azioni svolte dall'ISR siano trasparenti rispetto al programma principale, cioè che al termine venga ripristinato tutto come era prima dell'interrupt. In termini informatici, che la ISR sia perfettamente rientrante. Per fare ciò bisogna che la CPU, prima di mandare in esecuzione l'ISR, salvi

pag. 4 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memotutto quello che stava facendo (cioè il suo contesto attuale) , ed alla fine dell'ISR lo ripristini com'era.

Dobbiamo fare svelti a gestire l'interrupt, e quindi non possiamo perdere tempo per salvare tutto il contesto attuale (variabili, stato del programma, l'immagine sullo schermo, ...) ma solo quello che effettivamente verrà modificato dall'ISR. D'altra parte la CPU non sa esattamente che cosa modificherà l'ISR e quindi non può conoscere a priori che cosa salvare. Chiamiamo:

contesto minimoquello che viene modificato sicuramente ad ogni esecuzione di una qualsiasi ISR

contesto specificoquello che viene modificato dall'esecuzione di quella particolare ISR

contesto invariantetutto quello che fa parte del contesto attuale ma che non viene modificato dall'esecuzione

dell'ISRIl contesto minimo dipende dal tipo di CPU in uso, e nel caso della famiglia INTEL è costituito dai registri CS, IP e FLAG: CS (Code Segment) ed IP (Instruction Pointer) rappresentano l'indirizzo della prossima istruzione da eseguire e quindi sicuramente il lancio della ISR li modifica. Meno intuitivo è il salvataggio del registro FLAG, e verrà descritto successivamente. La CPU salva automaticamente il contesto minimo nello stack prima di passare il controllo alla ISR, e lo ripristina quando l'ISR è terminata, per effetto dell'esecuzione dell'istruzione IRET

(Interrupt RETurn).

Il contesto specifico dipende dalla particolare ISR, e dovrà essere il programmatore che scrive l'ISR ad includervi all'inizio le istruzioni per salvarlo, ed alla fine prima dell'IRET, mettere le rispettive istruzioni per ripristinarlo. Poichè il contesto specifico verrà salvato nello stack, normalmente una ISR comincia con una o più istruzioni di tipo PUSH ed alla fine troveremo, in ordine inverso, le corrispondenti POP.

Il contesto invariante non influisce il funzionamento degli interrupt, e quindi può essere tranquillamente trascurato.

 

3. La gestione degli interrupt nell'architettura INTEL

L'INTEL, dal processore 80486 in poi, distingue tra interruzioni (interrupt) ed eccezioni (exceptions): le prime vengono utilizzate per comunicare alla CPU il verificarsi di certi eventi esterni, mentre le seconde servono a gestire il malfunzionamento di istruzioni. Gli interrupt software generati dalle istruzioni INT xx vengono gestiti dalla CPU

pag. 5 di 26

"

La gestione degli interrupt TE@AC TEAM Alessandro Memocome se fossero eccezioni.

A loro volta gli interrupt si possono suddividere in interrupt hardware ed interrupt software.

Gli interrupt hardware sono ulteriormente classificabili in interrupt mascherabili o non mascherabili.

Gli interrupt mascherabili sono quelli che fanno capo al piedino INTR, e normalmente vengono utilizzabili dai vari dispositivi esterni. Le CPU INTEL distinguono fino a 255 interrupt mascherabili. L'istruzione STI (SeT Interrupt enable flag) abilita l'inoltro di interrupt mascherabili, mentre l'istruzione CLI (CLear Interrupt enable flag) li disabilita.

L'interrupt non mascherabile ha ovviamente una priorità maggiore dei precedenti, in quanto non può essere mai disabilitato. Viene attivato portando a livello alto la linea NMI (Non-Maskable Interrupt); la CPU termina l'istruzione in corso e poi genera un interrupt di tipo 2, associato per default all'interrupt NMI. Non è possibile annidare interrupt NMI, e viene automaticamente disabilitato il bit IF per evitare che sopraggiungano interrupt mascherabili.

Gli interrupt software vengono determinati dall'esecuzione di istruzioni INT N ed in tal caso viene attivata la corrispondente ISR il cui indirizzo è all'N-esimo posto della tabella delle interruzioni (interrupt vector table). Le eccezioni si possono suddividere in:

faultanomalie di funzionamento rilevate e gestite immediatamente prima dell'esecuzione dell'istruzione che le genera: ad esempio nella gestione della memoria virtuale, quando il gestore genera un miss, cioè quel blocco di memoria non ` presente in cache;

trapanomalie di funzionamento rilevate e gestite immediatamente dopo dell'esecuzione dell'istruzione che le genera: ad esempio un interrupt software;

abortanomalie di funzionamento che non permettono di individuare esattamente l'istruzione che le hanno determinate: ad esempio un malfunzionamento segnalato da una periferica.

Nella allegata tabella delle interruzioni sono riportati i 256 possibili tipi di interruzioni nelle CPU x86.

Rivediamo ora in dettaglio che cosa succede all'interno di una CPU 486 o successive:

Appena la CPU termina un'istruzione:

1. verifica che l'istruzione appena terminata non abbia prodotto una eccezione di tipo TRAPS

2. verifica che la prossima istruzione da eseguire non produca una eccezione di tipo

pag. 6 di 26

La gestione degli interrupt TE@AC TEAM Alessandro MemoFAULTS

3. verifica se è arrivato un interrupt hardware non mascherabile sulla linea NMI

4. verifica se sono arrivati interrupt hardware mascherabili sulla linea INTR e se il bit di flag IF è abilitato

5. se in modalità protetta, verifica che la prossima istruzione da eseguire non produca eccezioni di tipo FAULTS

Se è stato rilevato un interrupt, la CPU deve per prima cosa salvare il contesto minimo nello stack, e poi stabilire dove è allocata la ISR relativa. Se è arrivato un interrupt hardware non mascherabile il tipo di default vale 2. Se è arrivato un interrupt hardware mascherabile legge il valore ad 8 bit immesso nel Data Bus dalla periferica, che identifica il tipo di interruzione richiesta. Se l'istruzione da eseguire è un interrupt software, il tipo è contenuto direttamente nell'istruzione stessa. Se l'interruzione è un'eccezione, il tipo viene attribuito per default.

I tipi di interrupt disponibili sono in tutto 256. Prima di mandare in esecuzione l'ISR relativa, in caso di interrupt hardware mascherabile e non, il bit IF viene azzerato, disabilitando l'arrivo di ulteriori interrupt mascherabili. Il programmatore, se necessario, può riabilitarlo manualmente (con l'istruzione STI) consentendo di eseguire un interrupt durante l'esecuzione di una ISR. Quando la ISR termina, viene ripristinato il registro di flag pre-esistente, con il relativo valore del bit IF.

Per determinare l'indirizzo della routine ISR associata ad un dato tipo di interrupt si utilizza una tabella dei puntatori alle 256 possibili ISR, allocata per default a partire dall'indirizzo di memoria 0000:0000. Nel funzionamento in normal mode ogni indirizzo è costituito da 4 byte, due per il segmento e due per l'offset. Per questo la tabella occupa 1 KByte. Ovviamente gli indirizzi sono allocati in maniera sequenziale. Se ad esempio la memoria contiene queste informazioni:

0000:0000 12 34 56 78 01 02 03 04 - 05 06 07 08 ...

significa che la routine ISR associata all'interrupt di tipo 0 è allocata a partire dall'indirizzo 7856:3412, mentre quella associata all'interrupt di tipo 1 è allocata a partire dall'indirizzo 0403:0201.

 

4 La gestione degli interrupt nei PC

In un PC vi sono molte fonti di generazione di interruzioni:

ogni volta che si preme un tasto della tastiera, l'integrato che la gestione emette un interrupt

ogni volta che si sposta il mouse o che se ne preme un pulsante, il driver del mouse genera un interrupt

l'integrato timer 8253 contenuto nella scheda madre genera un interrupt 18,2 volte al

pag. 7 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memosecondo

ogni volta che la stampante ha terminato di stampare un carattere, o svuotato il suo buffer interno

se tentate di dividere per zero (in linguaggio macchina) la CPU genera un interrupt non mascherabile

per accedere alle risorse del calcolatore si usano normalmente delle chiamate a servizi offerti dal Sistema Operativo (DOS, API, ...) richiamabili via interrupt software

Esclusi gli ultimi due esempi, tutti gli altri producono interrupt hardware mascherabili, cioè fanno capo all'unica linea INT della CPU. Sorgono quindi alcuni problemi quali:

1. come fanno più dispositivi a condividere l'unico accesso?

2. come fa la CPU a sapere chi ha lanciato l'interrupt? (questo lo sappiamo già ; se ci sono dubbi tornare alla fine del punto 3 della scheda)

3. mentre è in esecuzione una ISR, che cosa succede se arrivano altri interrupt?

4. se arrivano contemporaneamente due interrupt, chi dei due viene soddisfatto per primo?

A questi ed altri problemi soddisfa un integrato apposito, inizialmente presente nelle schede madre dei PC ed ora inglobato nel chip set che accompagna la CPU, denominato 8259A Programmable Interrupt Controller, o più semplicemente PIC.

Nei primissimi PC ve ne era un solo esemplare, ma si sentì subito la necessità di ampliare il numero di interrupt gestiti fisicamemnte, e quindi si passò a due PIC collegati in cascata, il cui schema funzionale è riportato quì a fianco.

L'integrato PIC 1 possiede, tra le altre, 8 linee di ingresso a cui pervengono le richieste di interruzione di altrettanti dispositivi ed una linea d'uscita con cui controlla direttamente il segnale INT della CPU.

L'integrato PIC 2 è collegato in cascata, cioè ha altre 8 linee per altrettante richieste di interruzione (dette IRQ da Interrupt ReQuest) e gestisce l'uscita come il PIC 1, ma ora è

collegata alla linea IRQ2 del PIC 1. La programmazione del PIC avviene mediante un meccanismo abbastanza complesso accedendo a due indirizzi mappati sull'integrato: per il PIC 1 questi due indirizzi sono relativi alle porte di I/O 20h e 21h, per il PIC 2 sono 0A0h ed 0A1h.

Il PIC contiene al suo interno 3 registri ad 8 bit:

IMR (Interrupt Mask Register)

serve per abilitare o meno le singole linee di richiesta di interruzione; ponendo un bit 1 nella

pag. 8 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memorelativa posizione del registro si disabilita l'IRQ associato

ISR (In Service Register, sola lettura)

serve per sapere quali interrupt siano attualmente in gestione dalle rispettive routine. Se un bit di questo registro è a 1 significa che la relativa richiesta è in esecuzione

IRR (Interrupt Request Register, sola lettura)

serve per sapere quali richieste IRQ siano state attivate dai dispositivi esterni. Se un bit di questo registro è a 1 significa che è arrivata una richiesta nella rispettiva linea IRQ.

Il PIC, prima di poterlo utilizzare, va inizializzato inviando 3 o 4 parole di inizializzazione (dette ICW1-ICW4 da Inizialization Command Word): questa fase viene fatta una tantum all'accensione del sistema, ed è bene non modificarla per non bloccare il regolare funzionamento del PIC. Quando invece il PIC opera regolarmente, si possono inviare fino a 3 parole di controllo (dette OCW1-OCW4 da Operational Command Word).

Con la OCW1 all'indirizzo 021h per il PIC1 e 0A1h per il PIC2, si puà accedere direttamente ai rispettivi registri IMR, e quindi abilitare o disabilitare l'accettazione delle richieste. Con la OCW2 all'indirizzo 020h per PIC1 e 0A0h per PIC2 possiamo emettere un comando EOI (da End Of Interrupt) per avvisare il rispettivo PIC che la richiesta di INT è stata soddisfatta completamente. Con la OCW3 sempre all'indirizzo 020h/0A0h possiamo leggere i valori dei registri ISR ed IRR.

Queste le istruzioni in Assembler ed in linguaggio C necessarie per poter accedere direttamente al PIC ed alle sue più semplici possibilità di programmazione.

Programmazione del PIC in Linguaggio Macchinaindirizzi relativi al PIC 1, per accedere al PIC 2 sostituire 20h e 21h con 0A0h ed 0A1h

1. Abilitare l'inoltro di una richiesta IRQ (ad esempio IRQ3) IN AL,21h ; leggo il registro IMR AND AL,0F7h ; 11110111 = azzero il bit 3 OUT 21h,AL ; abilito (anche?) IRQ3

2. Disabilitare l'inoltro di una richiesta IRQ (ad esempio IRQ4) IN AL,21h ; leggo il registro IMR OR AL,10h ; 00010000 = setto il bit 4

pag. 9 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo OUT 21h,AL ; disabilito (anche?) IRQ4

3. Emetto un EOI (per lasciar passare eventuali altri interrupt) MOV AL,20h ; parola di controllo OCW2 OUT 20h,AL ; abilito (anche?) IRQ3

4. Leggo il valore del registro ISR MOV AL,0Bh ; parola di comando OCW3 per leggere ISR OUT 20h,AL ; IN AL,20h ; leggo il valore di ISR

5. Leggo il valore del registro IRR MOV AL,0Ah ; parola di comando OCW3 per leggere IRR OUT 20h,AL ; IN AL,20h ; leggo il valore di ISR

Programmazione del PIC in Linguaggio Cindirizzi relativi al PIC 1, per accedere al PIC 2 sostituire 0x20 e 0x21 con 0xA0 ed 0xA1

Preambolo comune a tutti gli esempi successivi:

#include <dos.h> #define PORT_0 0x20 #define PORT_1 0x21

1. Abilitare l'inoltro di una richiesta IRQ (ad esempio IRQ3) #define AND_MASK 0xF7 ... outportb(inportb(PORT_1) & AND_MASK);

2. Disabilitare l'inoltro di una richiesta IRQ (ad esempio IRQ4) #define OR_MASK 0x10 ... outportb(inportb(PORT_1) | AND_MASK);

3. Emetto un EOI (per lasciar passare eventuali altri interrupt) #define EOI 0x20 ... outportb(PORT_0, EOI);

4. Leggo il valore del registro ISR #define READ_ISR 0x0B ... unsigned char isr; outportb(PORT_0,READ_ISR); isr = inportb(PORT_0);

5. Leggo il valore del registro IRR

pag. 10 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo #define READ_IRR 0x0A ... unsigned char irr; outportb(PORT_0,READ_IRR); irr = inportb(PORT_0);

Per semplicità analizziamo il funzionamento di un solo PIC, in quanto il funzionamento del secondo PIC è del tutto simile al primo.

Quando una linea IRQ viene attivata dal dispositivo esterno, il bit relativo del registro IRR viene posto a 1, se il corrispondente bit del registro IMR è a livello basso, cioè se quella IRQ è abilitata.

Se non sono pendenti altre richieste a priorità superiore, viene settato il bit relativo del registro ISR e la richiesta viene inoltrata alla CPU tramite la linea INT.

La CPU risponde mediante la linea INTA (da INTerrupt Acknowledge) per avvisare il PIC di inoltrare il codice di identificazione di interrupt. A questo punto il PIC emette il codice associato alla linea IRQ durante la fase di inzializzazione.

Nei PC alle richieste IRQ0-IRQ7 vengono associati i codici 09-0F, mentre alle richieste IRQ8-IRQ15 vengono associati i codici 70-7F.

A questo punto la CPU è in grado di soddisfare la richiesta, ma il PIC rimane bloccato fino a che non viene avvisato che la richiesta è stata soddisfatta e può inoltrare eventuali richieste pendenti o successive. Ciò deve essere effettuata dal programmatore emettendo l'opportuno messaggio EOI all'interno della sua ISR.

Si consiglia di visionare l'animazione allegata, che riassume graficamente le varie fasi che abbiamo appena descritto.

 

5. Realizzazione di una Interrupt Service Routine

Come indicazione generale, per scrivere una routine di gestione di interrupt possiamo adottare la seguente scaletta operativa:

1. Scegliere il tipo di interrupt a cui agganciare l'ISR 2. Scrivere il corpo della routine 3. Curare l'accesso alle variabili 4. Aggiungere le istruzioni per il salvataggio ed il recupero del contesto specifico 5. Allocare in maniera stabile e sicura la routine ISR 6. Aggiornare la tabella dei vettori di interruzione con il nuovo indirizzo 7. Considerare il problema della rientranza

Per rendere più comprensibile la trattazione, scriviamo una ISR di esempio che visualizzi pag. 11 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memocostantemente sull'angolo in alto a destra dello schermo una cifra (dallo 0 al 9) che si incrementi ad ogni secondo (circa).

Scegliere il tipo di interrupt a cui agganciare l'ISR: per una corretta implementazione del problema avremmo bisogno di un generatore di impulsi della frequenza di 1 Hz da collegare ad una linea IRQ ancora libera dei due PIC del nostro calcolatore. Tuttavia la Microsoft, ancora quando produsse il primo MS-DOS ed in previsione di futuri sviluppi multitasking, predispose un meccanismo di interruzione basato sull'INT 1Ch, a cui il programmatore esperto può collegarsi. Per ulteriori approfondimenti su questo tipo di interrupt consultare la scheda INT 1C.

L'interrupt 1C

Il calcolatore contiene al suo interno un integrato, l'8253 Programmable Timer Counter che genera tre distinti segnali di clock, le cui caratteristiche sono modificabili via software. Il segnale più importante è quello prodotto dal canale 0, che oscilla alla frequenza di 18,2 Hz (pari ad un periodo di circa 55 millisecondi) e che è fisicamente collegato alla linea IRQ0 del primo PIC.

In tal modo il canale 0 dell'8253 genera 18,2 volte al secondo un interrupt di tipo 08. La ISR relativa svolge molti compiti, quali l'aggiornamento dell'ora interna del PC e la tempificazione dei motori di rotazione dei dischi. È quindi molto pericoloso cambiare i parametri di questo segnale o modificare la relativa ISR.

Per poter utilizzare in maniera relativamente tranquilla questa tempificazione, la Microsoft fin dal suo primo MS-DOS ha incluso alla fine della ISR dell'interrupt 08, appena prima di eseguire l'IRET, l'istruzione INT 1Ch, e il vettore dell'interrupt 1C lo ha fatto puntare ad una istruzione IRET. Riassumendo:

alla frequenza di 18,2 volte al secondo l'8253 generata la richiesta IRQ0 al PIC,

... che a sua volta lancia l'interrupt hardware mascherabile tipo 08,

... a cui la CPU risponde mandando in esecuzione la relativa ISR,

... che prima di concludersi lancia l'interrupt software di tipo 1Ch,

... la cui ISR non fa niente e ritorna al punto in cui è stata lanciata,

... e quindi può concludersi la ISR dell'interrupt 08, che ritorna il controllo alla CPU nel punto in cui è stata interrotta.

Per realizzare una ISR personalizzata, che venga lanciata 18,2 volte al secondo è sufficiente scrivere la propria routine e porre il suo indirizzo di inizio nella tabella dei vettori delle interruzioni, in corrispondenza al tipo 1Ch.

pag. 12 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo

Nell'esempio che sviluppiamo ci conviene seguire la strada più semplice e collegarci all'INT 1C.

Scrivere il corpo della routine: oramai tutti i linguaggi (escluso il Java per scelta metodologica inderogabile, leggi sicurezza) permettono di accedere alle risorse fisiche del calcolatore, e quindi forniscono strumenti per scrivere routine ISR. Tuttavia i linguaggi di basso livello sono sicuramente i più indicati a questo scopo. Per questo motivo si propone come linguaggio principale l'Assembler, e se ne prevede anche una versione alternativa in linguaggio C.

Nella stesura di una ISR bisogna ricordare che in generale NON È POSSIBILE utilizzare le funzioni del DOS in quanto sono ISR non rientranti (vedere l'ultima osservazione di questo punto della scheda). Ciò significa che per accedere alle risorse del PC, tipo lo schermo e la tastiera, è necessario scriversi le proprie routine!

Il problema è talmente semplice che se ne propone direttamente la soluzione, opportunamente commentata:

POSIZ EQU 159 ; posizione del carattere sullo schermoSegVideo EQU 0B800h ; indirizzo inizio memoria video (a colori)Cifra DB '0' ; carattere ASCII della cifra da visualizzareTempo DB 18 ; numero di interrupt da aspettare per ... ; ... sapere se è passato un secondo

routine: DEC tempo ; controllo se è passato un secondo JNZ mostra ; se no, salta a visualizzare la cifra MOV tempo,18 ; se si ripristina il valore e ...mostra: MOV AX,SegVideo ;aggiusta il valore del segmento ES MOV ES,AX MOV ES:POSIZ,Cifra ; scrivi la cifra sul videoavanza: INC Cifra ; valuta la prossima cifra CMP Cifra,'9'+1 JNZ esci ; se minore di 10 va bene MOV Cifra,'0' ; altrimenti riportala a 0 (in ASCII)esci: ...

Routine ISR in linguaggio C (prima parte)

#include <dos.h>#define POSIZIONE 79 /*1*/#define ATTRIBUTI 0x7900 /*2*/

void MioInt (void){ unsigned contatore = 18; /*3*/ int carattere = 0; /*4*/ unsigned int (far *schermo) = MK_FP (0xB800,POSIZIONE*2); contatore--; /*6*/ if (!contatore) { contatore = 18; /*7*/ *schermo = carattere + '0' + ATTRIBUTI; /*8*/ carattere = (++carattere) % 10; /*9*/ }

pag. 13 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo}

COMMENTI

1.definiamo la posizione della cifra sullo schermo: con 79 la mettiamo nell'ultimo spazio della prima riga dello schermo 2.definiamo l'attributo (colore di sfondo e di primo piano) della cifra 3.variabile necessaria per contare 18 interrupt, per sapere quando è passato un secondo 4.variabile cifra 5.variabile, anche se è sempre costante, che contiene l'indirizzo segmento:offset della cella di memoria dello schermo video dove scrivere la cifra 6.verifico se è passato un secondo 7.in caso affermativo ripristino il contatore ... 8.... e scrivo il carattere sullo schermo

9.determino il carattere successivo

Curare l'accesso alle variabili: in linguaggio macchina nelle CPU x86 l'accesso alla memoria avviene (anche se non solo) attraverso un meccanismo di segmentazione.

Cenni sulla segmentazione

I registri delle CPU x86 contenenti indirizzi (SP, SI, IP, ...) sono a 16 bit, mentre lo spazio di memoria indirizzabile (determinabile dall'Address Bus?) raggiunge almeno il MByte, cioè almeno 20 bit: come fare?L'INTEL ha deciso che l'indirizzo a 16 bit detto OFFSET doveva essere combinato con un'altra informazione numerica detta SEGMENTO per ottenere i bit dell'indirizzo fisico emesso dalla CPU.Le prime CPU x86 avevano solo 4 registri di segmento: CS, SS, DS ed ES, e l'associazione dell'OFFSET ad un particolare SEGMENTO avviene in maniera automatica: se l'indirizzo si riferisce ad una istruzione si utilizza automaticamente il registro di segmento CS (Code Segment), se si riferisce ad un dato si usa il segmento DS (Data Segment), se si riferisce allo stack si usa il segmento SS (Stack Segment), mentre il registro di segmento ES (Extra Segment) non ha alcun impiego prefissato.

In caso di necessità il programmatore può specificare direttamente quale registro di segmento utilizzare in una particolare istruzione, sovrapponendo alla scelta di default un altro segmento (overriding).

Se nella routine ISR si fa riferimento a variabili, queste devono essere rese accessibili al

pag. 14 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memoprogramma: se siamo in linguaggio macchina occorre ricordarsi di inizializzare correttamente il registro DS (Data Segment) mentre se utilizziamo il linguaggio C bisogna sincerarsi che le variabili manipolate dalla ISR siano di tipo globale, cioè allocate in forma statica.

In linguaggio macchina non è facile passare il valore del registro DS alla routine ISR, quindi si preferisce ricorrere ad una scorciatoia, ove possibile: se la routine ha dimensioni contenute, meno di 64 KByte, la sua creazione può essere inglobata in un modello .COM, in cui per definizione i valori dei quattro registri di segmento coincidono. In tal modo prima di accedere ai dati all'interno della ISR sarà sufficiente attribuire al registro DS il valore del registro CS, che viene già inizializzato dalla stessa CPU al lancio della ISR.

Questa la versione corretta del corpo della routine di interruzione d'esempio:

POSIZ EQU 159 ; posizione del carattere sullo schermoSegVideo EQU 0B800h ; indirizzo inizio memoria video (a colori)Cifra DB '0' ; carattere ASCII della cifra da visualizzareTempo DB 18 ; numero di interrupt da aspettare per ... ; ... sapere se è passato un secondo

routine: PUSH CS ; copio il valore del registro CS ... POP DS ; ... nel registro DS, per accedere ai dati DEC tempo ; controllo se è passato un secondo JNZ mostra ; se no, salta a visualizzare la cifra MOV tempo,18 ; se si ripristina il valore e ...mostra: MOV AX,SegVideo ;aggiusta il valore del segmento ES MOV ES,AX MOV ES:POSIZ,Cifra ; scrivi la cifra sul videoavanza: INC Cifra ; valuta la prossima cifra CMP Cifra,'9'+1 JNZ esci ; se minore di 10 va bene MOV Cifra,'0' ; altrimenti riportala a 0 (in ASCII)esci: ...

Routine ISR in linguaggio C (seconda parte)

#include <dos.h>#define POSIZIONE 79 #define ATTRIBUTI 0x7900 /*1*/unsigned int (far *schermo) = MK_FP (0xB800,POSIZIONE*2);

void MioInt (void){ static unsigned contatore = 18; /*2*/ static int carattere = 0; /*3*/ contatore--; if (!contatore) { contatore = 18; *schermo = carattere + '0' + ATTRIBUTI; carattere = (++carattere) % 10; }

pag. 15 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo}

COMMENTI

1.la variabile viene posta fuori dalla funzione per farla diventare globale 2.la variabile viene dichiarata static, in modo da allocarla staticamente anche senza farla vedere da altre funzioni

3.vedi sopra

Aggiungere le istruzioni per il salvataggio ed il recupero del contesto specifico: in linguaggio macchina questa operazione deve essere fatta direttamente dal programmatore, in base ai registri effettivamente utilizzati, mentre in linguaggio C, se la funzione viene dichiarata di tipo interrupt queste istruzioni vengono aggiunte automaticamente. Anzi, per non sbagliare il compilatore normalmente salva tutti i registri della CPU, indipendentemente dal fatto che questi vengano realmente modificati all'interno della funzione ISR.

Questa la versione aggiornata del corpo della routine di interruzione d'esempio:

POSIZ EQU 159 ; posizione del carattere sullo schermoSegVideo EQU 0B800h ; indirizzo inizio memoria video (a colori)Cifra DB '0' ; carattere ASCII della cifra da visualizzareTempo DB 18 ; numero di interrupt da aspettare per ... ; ... sapere se è passato un secondo

MioInt: PUSH AX ; salvataggio del contesto specifico PUSH ES PUSH DSroutine: PUSH CS ; copio il valore del registro CS ... POP DS ; ... nel registro DS, per accedere ai dati DEC tempo ; controllo se è passato un secondo JNZ mostra ; se no, salta a visualizzare la cifra MOV tempo,18 ; se si ripristina il valore e ...mostra: MOV AX,SegVideo ;aggiusta il valore del segmento ES MOV ES,AX MOV ES:POSIZ,Cifra ; scrivi la cifra sul videoavanza: INC Cifra ; valuta la prossima cifra CMP Cifra,'9'+1 JNZ esci ; se minore di 10 va bene MOV Cifra,'0' ; altrimenti riportala a 0 (in ASCII)esci: POP DS ; ripristino del contesto specifico POP ES ; si noti l'inversione dell'ordine ... POP AX ; ... per effetto dello stack IRET ; termine della routine ISR

Routine ISR in linguaggio C (terza parte)

#include <dos.h>

pag. 16 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo#define POSIZIONE 79 #define ATTRIBUTI 0x7900 unsigned int (far *schermo) = MK_FP (0xB800,POSIZIONE*2);

static void interrupt MioInt (void) /*1*/{ static unsigned contatore = 18; static int carattere = 0; contatore--; if (!contatore) { contatore = 18; *schermo = carattere + '0' + ATTRIBUTI; carattere = (++carattere) % 10; }}

COMMENTI1.specifica che la funzione è di tipo interrupt, e quindi viene salvato l'intero contesto al suo inizio, viene poi ripristinato al termine e si conclude con una IRET invece che una semplice RET.

Allocare in maniera stabile e sicura la routine ISR: ad una routine ISR viene di solito attribuito il significato di processo in background, cioè viene mandata in esecuzione e poi continua a funzionare mentre la CPU esegue un altro processo in foreground.

In DOS, quando carichiamo un programma, questo va ad occupare la memoria a disposizione a partire dalla prima cella libera. Se dopo ne carichiamo un'altro, quest'ultimo andrà ad occupare la memoria a partire dallo stesso indirizzo iniziale. Per evitare questo, possiamo:

1. caricare il programma in background (tipicamente la ISR) e spostare l'indirizzo della prima cella libera di memoria alla fine del programma appena caricato

2. caricare il programma in foreground.

Esiste una funzione DOS che svolge proprio questi compiti, si chiama TSR da Terminate but Stay Resident e produce l'effetto di terminare il programma in corso e di mantenerlo residente spostando il puntatore della memoria libera di una quantità stabilita dal programmatore, definita in multipli di blocchi da 16 Byte, detti paragrafi.

Questo un esempio di programma .COM scritto in Assembler con direttive semplificate, che rimane residente:

DOSSEG .MODEL tiny ; modalita` .COM .CODE org 0100h ; locazione iniziale standardStart: JMP Inizio ; salto la routine ISR

pag. 17 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo ; ............. area dati .............TSR EQU 31h ; funzione TSR del DOSDOS EQU 21h ; codice interrupt DOS ; ...MioInt: ; ........ area contenente la ISR ..... ; ...Inizio: MOV AH,TSR ; funzione TSR MOV DX, ((Inizio-Start)/16)+11h INT DOS END Start

Ricordarsi di compilare il file .ASM con modalità .COM: ad esempio conTASM digitare 'tasm file' e poi 'tlink file /t'

Routine ISR in linguaggio Assembler (direttive non semplificate)

ASSUME CS:CODECODE SEGMENTStart: JMP Inizio ; ... inizio area dati ...SPAZIO EQU 17+($-Start)/16TSR EQU 31hDOS EQU 21hMioInt: ; ... area contenente la ISR ... ; ...Inizio: MOV AH,TSR MOV DX,SPAZIO INT DOSCODE ENDSEND

Routine ISR in linguaggio C (quarta parte)

#include <dos.h>#define POSIZIONE 79 #define ATTRIBUTI 0x7900 unsigned int (far *schermo) = MK_FP (0xB800,POSIZIONE*2);static void interrupt MioInt (void); /*1*/

void main (void) /*2*/{ unsigned IndirHiMem = _SS + ((_SP+8)/16); /*3*/ unsigned LungProg = IndirHiMem - _psp +1; /*4*/ keep (0, LungProg); /*5*/}

static void interrupt MioInt (void)

pag. 18 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo{ static unsigned contatore = 18; static int carattere = 0; contatore--; if (!contatore) { contatore = 18;

*schermo = carattere + '0' + ATTRIBUTI;carattere = (++carattere) % 10;

}}

COMMENTI1. prototipo della funzione ISR 2. funzione principale che ha il compito di allocare la ISR in maniera definitiva 3. variabile (anche se in realtà &grave; una costante) che calcola l'offset dell'indirizzo

della fine del programma corrente: per far ciò si basa sulla posizione dello stack che viene per default posto dopo l'area di codice, ed utilizza le variabili di sistema predefinite _SS ed _SP.

4. variabile (anche se in realtà &grave; una costante) che calcola la lunghezza del programma sottraendo alla fine del programma l'offset del suo inizio (variabile di sistema _psp)

5. la funzione keep(stato,dimensione) termina il programma e lo lascia residente, restituendo come codice d'uscita il valore stato e preservando dimensione paragrafi da 16 Byte. In pratica richiama la funzione 31h del DOS.

Si noti il calcolo dei paragrafi da mantenere residenti: si fa uso delle etichette Inizio e Start per far riferimento alle celle di memoria associate e calcolare la lunghezza del blocco di celle tra Start e Inizio. La quantità; 17h viene sommata per arrotondare per eccesso il risultato della divisione.

Aggiornare la tabella dei vettori di interruzione con il nuovo indirizzo: si potrebbe accedere direttamente alle celle di memoria della tabella, ma in tal caso si rischia di essere interrotti nel bel mezzo della scrittura da un'interrupt, avendo salvato ad esempio solo la parte bassa dell'indirizzo. Verrebbe quindi passato un indirizzo errato in CS:IP, con l'ovvia conseguenza di bloccare il sistema.

Una soluzione possibile sarebbe quella di racchiudere le istruzioni di scrittura dell'indirizzo tra due comandi di disabilitazione e riabilitazione degli interrupt.

La soluzione migliore è invece quella di ricorrere ad una specifica funzione del DOS. Questa funzione richiede che

venga caricato nel registro AH il valore 25h che la identifica

pag. 19 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo venga caricato nel registro AL il tipo identificativo dell'interrupt di cui si vuole cambiare

l'ISR venga caricato nei registri DS:DX l'indirizzo segmento:offset dove è allocata la routine

ISR

Inglobando le soluzioni proposte in questi due ultimi punti possiamo scrivere il seguente programma, detto loader, che ha solo il compito di caricare in memoria in maniera permanente la routine ISR di nome MioInt e di aggiornare di conseguenza la tabellla dei vettori delle interruzioni:

DOSSEG .MODEL tiny .CODE org 0100hStart: JMP Inizio ; ............. area dati .............TSR EQU 31h ; funzione TSR del DOSDOS EQU 21h ; codice interrupt DOSSETVECT EQU 25h ; funzione SetVect del DOS

MioInt: ; routine ISR personalizzata ; ...

Inizio: MOV AH,SETVECT ; funzione Set Vector MOV AL,1Ch ; tipo di interrupt MOV DX,Offset MioInt ; salvo l'offset ; il segmento non serve perchè ; CS e DS si equivalgono nei file .COM INT DOS ; aggiorna la tabella MOV AH,TSR ; funzione TSR MOV DX, ((Inizio-Start)/16)+11h INT DOS

END Start

Routine ISR in linguaggio Assembler (finale)

DOSSEG .MODEL tiny .CODE org 0100hStart: JMP Inizio ; ............. area dati .............TSR EQU 31h ; funzione TSR del DOSDOS EQU 21h ; codice interrupt DOSSETVECT EQU 25h ; funzione SetVect del DOSSegVideo EQU 0B800h ; indirizzo inizio memoria video (a colori)POSIZ EQU 159 ; posizione carattere sullo schermo

pag. 20 di 26

La gestione degli interrupt TE@AC TEAM Alessandro MemoCifra DB '0' ; carattere ASCII della cifraTempo DB 18 ; tipo di interrupt da aspettare per ... ; ... sapere se è passato un secondoMioInt: PUSH AX ; salvataggio del contesto specifico PUSH ES PUSH DSroutine: PUSH CS ; copio il valore del registro CS ... POP DS ; ... in DS, per accedere ai dati DEC tempo ; controllo se è passato un secondo JNZ mostra ; se no, vado a visualizzare la cifra MOV tempo,18 ; se si ripristina il valore e ...mostra: MOV AX,SegVideo ;aggiusta il valore del segmento ES MOV ES,AX MOV ES:POSIZ,Cifra ; scrivi la cifra sul videoavanza: INC Cifra ; valuta la prossima cifra CMP Cifra,'9'+1 JNZ esci ; se minore di 10 va bene MOV Cifra,'0' ; altrimenti riportala a 0esci: POP DS ; ripristino del contesto specifico POP ES ; si noti l'inversione d'ordine ... POP AX ; ... per effetto dello stack IRET ; termine della routine ISR

Inizio: MOV AH,SETVECT ; funzione Set Vector MOV AL,1Ch ; tipo di interrupt MOV DX,Offset MioInt ; salvo l'offset ; il segmento non serve perchè ; CS e DS si equivalgono nei file .COM INT DOS ; aggiorna la tabella MOV AH,TSR ; funzione TSR MOV DX, ((Inizio-Start)/16)+11h INT DOS

END Star

Routine ISR in linguaggio C (finale)

#include <dos.h>#define POSIZIONE 79#define ATTRIBUTI 0x7900#define INT_TIMER 0x1C unsigned int (far *schermo) = MK_FP (0xB800,POSIZIONE*2);static void interrupt MioInt (void);

void main (void){ unsigned IndirHiMem = _SS + ((_SP+8)/16); unsigned LungProg = IndirHiMem - _psp +1; setvect (INT_TIMER, MioInt); keep (0, LungProg);}

static void interrupt MioInt (void){ static unsigned contatore = 18; static int carattere = 0; contatore--;

pag. 21 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo if (!contatore) { contatore = 18;

*schermo = carattere + '0' + ATTRIBUTI;carattere = (++carattere) % 10;

}}

Considerare il problema della rientranza: può succedere, anche se spesso non è auspicabile, che mentre è in esecuzione una routine ISR arrivi un altro interrupt che richiami la stessa ISR, in un meccanismo che richiama il concetto di riconversione. Se la routine è ancora in grado di funzionare correttamente, la ISR di dice rientrante.

I problemi nascono dal fatto che una ISR può utilizzare una risorsa a cui ha diritto di accesso, ma che è contraddistinta da molteplicità unaria: l'accesso legittimo ed in qualche modo concorrente ad una risorsa da parte di due istanze diverse dello stesso processo porta inevitabilmente a situazioni di incongruenza ben note nella programmazione dei nuclei di sistemi operativi multitasking.

La soluzione è relativamente semplice: si devono individuare le zone critiche, in cui si accede a risorse condivise, e delimitarle con istruzioni cli...sti che garantiscono la mutua esclusione dall'ingresso in queste aree.

Molte funzioni del DOS non sono rientranti, e questo ci obbliga per precauzione a non utilizzarle quando siamo in una routine ISR

6. Gli interrupt software

Le differenze tra un interrupt software (INT N) e una chiamata ad un sottoprogramma (CALL XXXX) sono relativamente poche, ma molto importanti.

Infatti anche se negli interrupt software il meccanismo di selezione della procedura da eseguire &egarve; abbastanza macchinoso, è in realtà l'artefice principale della portabilità del software, assicura cioè che un programma scritto in un PC possa essere eseguito anche in un altro PC con dotazione hardware diversa, purché dotato dello stesso sistema operativo.

Infatti quando si deve accedere ad una risorsa, ad esempio il video, il programmatore corretto non accede direttamente ai registri della scheda video, ma utilizza le chiamate di sistema che hanno validità generale e sono implementate via interrupt software.

pag. 22 di 26

La gestione degli interrupt TE@AC TEAM Alessandro MemoIl programma lancia un INT N prestabilito, con N in funzione di quale risorsa si vuole utilizzare, e questo in tutto il software standard. La tabella dei vettori di interruzione è invece diversa in ogni PC, e richiama la routine specifica per l'hardware installato in quel momento.

Se si fa riferimento alle risorse di base standard l'insieme delle routine che vi accedono a basso livello prende il nome di BIOS, mentre se vi si accede a livello più astratto è costituito dalle cosiddette chiamate di sistema operativo (funzioni del DOS o API in Windows).

Se invece si fa riferimento a risorse particolari, come la stampante o lo scanner, gli stessi produttori delle periferiche producono e distribuiscono i driver (insiemi di routine da richiamare per ottenere certe funzioni dalle specifiche risorse) che il possessore del PC dovrà installare (cioè farle caricare in forma residente e agganciarle agli opportuni interrupt in maniera automatica all'atto dell'accensione).

A titolo di esempio si può visionare un esempio di accesso al video attraverso chiamate al BIOS ed al DOS in Assembler e in linguaggio C.

Accesso al BIOS in linguaggio Assembler

; programma BIOS.ASM : richiama una funzione BIOS. Cancella, con un colore di; sfondo ocra, una finestra di testo di 15 righe e 70 colonne DOSSEG .MODEL SMALL .STACK 0100h .DATA .CODEInizio: MOV AH,06 ; funzione SCROLL UP MOV AL,15 ; numero di linee MOV BH,01101001b ; attributo di riemp. MOV CH,0 ; riga angolo alto sn MOV CL,0 ; col. angolo alto sn MOV DH,14 ; riga angolo basso dx MOV DL,69 ; col. angolo basso dx INT 10h ; interruzione BIOS

MOV AH,4Ch; ; funzione DOS termina INT 21h ; chiamata funz. DOS

END Inizio

Accesso al DOS in linguaggio Assembler

pag. 23 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo

; programma DOS.ASM : richiama una funzione DOS che scrive una stringa sullo; schermo riempie un'area di testo di 15 per 70 DOSSEG .MODEL SMALL .STACK 0100h .DATAStringa DB 70 DUP ('+'), 0Dh, 0Ah, '$' ; stringa composta da 70 caratteri '+' ; completa di CR, LF, e di fine stringa .CODEInizio: MOV AX,@DATA ; preambolo standard MOV DS,AX

MOV CX,15 ; 15 stringheAncora: MOV DX,Offset Stringa ; indir. stringa MOV AH,09h ; funzione DISPLAY STRING INT 21h ; chiamata funz. DOS LOOP Ancora

MOV AH,4Ch; ; funzione DOS termina INT 21h ; chiamata funz. DOS END Inizio

Accesso al BIOS in linguaggio C

/* BIOS.C programma dimostrativo delle chiamate al BIOS. Colora di ocra una finestra di testo da 15 righe per 60 colonne */

#include <dos.h#define VIDEO 0x10

void main (void){ union REGS regs; regs.h.ah = 06 ; /* funzione SCROLL UP */ regs.h.al = 15 ; /* numero di linee */ regs.h.bh = 0x69 ; /* attributo */ regs.h.ch = 3 ; /* riga angolo alto dx */ regs.h.cl = 10 ; /* col. angolo alto dx */ regs.h.dh = 17 ; /* riga angolo basso sn */ regs.h.dl = 69 ; /* col. angolo basso sn */ int86 (VIDEO, &regs, &regs);}

Accesso al DOS in linguaggio C

/* DOS.C programma dimostrativo delle chiamate al DOS. Funzione 09: Display String riempie un'area di testo di 15 x 30 */

pag. 24 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo#include <dos.h#define DOS 0x21

void main (void){ union REGS regs; int i ; char Stringa[30] = " "; Stringa[27]=0x0D; /* Ritorno Carrello */ Stringa[28]=0x0A; /* Avanzamento Riga */ Stringa[29]='$'; /* Fine stringa in DOS */ regs.x.dx = Stringa ; /* assegnazione indirizzo */ regs.h.ah = 9 ; for ( i=0; i<15; i++) int86 (DOS, &regs, &regs);}

Tabella degli interrupt in x86

funzione CPU INT #(hex) tipo nei PC

Divide Error 00 FAULT exception handlers

Single STep (trace) 01 TRAP exception handlers

NMI Interrupt 02 NMI parity error

Breackpoint (INT) 03 TRAP exception handlers

Overflow (INTO) 04 TRAP exception handlers

Bounds check 05 FAULT exception handlers

Invalid OP Code 06 FAULT exception handlers

Device not avaiable 07 FAULT exception handlers

- 08 Hardware IRQ0 System Timer

- 09 Hardware IRQ1 Keyboard

- 0A Hardware IRQ2 Redirect IRQ8-15

- 0B Hardware IRQ3 Serial COMM2/COMM4

- 0C Hardware IRQ4 Serial COMM1/COMM3

- 0D Hardware IRQ5 LPT2,Sound Card, ...

- 0E Hardware IRQ6 Floppy Disk Controller

- 0F Hardware IRQ7 Parallel LPT1

pag. 25 di 26

La gestione degli interrupt TE@AC TEAM Alessandro Memo

Interrupt 2 bytes 10-6F interrupt software ISR

- 70 Hardware IRQ8 Real Time Clock

- 71 Hardware IRQ9 disponibile

- 72 Hardware IRQ10 disponibile

- 73 Hardware IRQ11 disponibile

- 74 Hardware IRQ12 disponibile

- 75 Hardware IRQ13 Floating Point Unit

- 76 Hardware IRQ14 Hard Disk Controller

- 77 Hardware IRQ15 riservato

Interrupt 2 bytes 78-FF interrupt software ISR

In modalità protected mode vengono utilizzati gli interrupt di tipo 10-18 per eccezioni FAULT particolari, quali (11)SegmentNotPresent, (12)StackFault, (13)GeneralProtectionFault, (14)PageFault,(17)AlignmentCheck.

pag. 26 di 26