Přerušení
Přerušení (interrupt) je schopnost procesoru přerušit právě vykonávaný program a začít vykonávat jiný program (obsluhu přerušení). Přerušení se začalo implementovat do procesorů z důvodu obsluhy periferii – přesněji V/V zařízení. Procesor je mnohem rychlejší než ostatní hardware, a tak kdyby se zabýval pouze obsluhou periferie, byl by v danou chvíli nevyužit a většinu svého procesorového času by strávil ve smyčce čekáním na daný hardware. V dnešní době se přerušení využívá mnohem víc při přepínání procesů. Takový jeden pohyb myší vyvolá přerušení hardwarové, které obslouží daný driver, ten následně pošle informaci jádru, to pošle informaci o změně na ploše a aktivní aplikaci, která například reaguje zvýrazněním odkazu. Vždy musí dojít k přerušení, neboť se měnil program, který vykonával danou činnost. V minulé díle jsme si na příkladu ukázali volání služeb BIOSu pomocí přerušení (instrukce INT).
Přerušení v architektuře x86 se vyvolá dvěma základními způsoby: Software-generated (softwarově), a to pří dekódování instrukce INT n (zmíněné volání BIOSu). A External-hardware generated (hardwarově), tedy vyvolané vnějším okolím. To se dále rozděluje na NMI (Non Maskable Interrupt) nemaskovatelné a na INTR (INTerrupt Request). Vše shrnuto v tabulce:
- Software-generated (softwarové)
- External-hardware generated (hardwarové)
- NMI (Non Maskable Interrupt)
- INTR (INTerrupt Request)
Software generated
Softwarová přerušení vznikají v jediném případě, a to když procesor narazí na instrukci INT n, kde n je vektor přerušení v rozsahu 0–255, tedy INT 0h – INT 0ffh. Vyžívá se k volání služeb operačního systému. Tento typ přerušení nelze maskovat.
External-hardware generated
Když jsem v minulém odstavci definoval toto přerušení jako vyvolané mimo procesor, nebyla to úplně pravda, protože do hardwarového přerušení se řadí tzv. Internal HW, která vznikají při vnitřní chybě kódu, např. dělení nulou, špatná instrukce. Externě vyvolané přerušení nastává při žádosti ze vstupu (vnější hardware) nebo chybě matematického koprocesoru.
INTerrupt Request
Je typ signálu přerušení, které lze tzv. maskovat, a to znamená, že pokud je bit IF (Interrupt Enable Flag) v příznakovém registru nastaven na 0, přerušení generované signálem INTR se neprovede.
Non Maskable Interrupt
Je-li typ signálu přerušení, které naopak nelze maskovat, tehdy se nezávisle na bitu IF se po přijetí signálu MNI přerušení provede.
Obsluha přerušení
Co se tedy stane, když procesor obdrží žádost o přerušení? Nejprve zkontroluje, jestli se daný signál bude maskovat a pokud ne, přeruší právě vykonávaný program a začne vykonávat program obsluhy přerušení, to v následujícím pořadí:
1. Na zásobník se uloží příznakový registr Flags
2. Příznaky IF a TF se vynulují
3. Na zásobník se uloží registr CS
5. Na zásobník se uloží registr IP
6. Registr IP se naplní hodnotou obsluhy přerušení
Interrupt Vector Table
Aby procesor věděl, jaké hodnoty má uložit do registrů CS,IP a hlavně, aby mohl rozlišovat různé druhy přerušení, existuje IVT ( tabulka vektorů přerušení ). Je to tabulka ukazatelů na příslušné obsluhy, obsahuje tedy hodnoty CS a IP volaných programů. IVT je uložená na fyzickém začátku paměti, tedy na adrese 0000h:0000h, jak jsme již zmínili obsahuje 256 položek ( řádků ) a každá má délku 4B. Celá tabulka má délku 1KB až po adresu 0000h:0400h. První dva byte řádku obsahují offset daného přerušení a druhé dva byte segment.
Adresa: | tabulka: | přerušení: |
0000h:0000h | offset:segment | INT 0 |
0000h:0004h | offset:segment | INT 1 |
0000h:0008h | offset:segment | INT 2 |
… | … | … |
0000h:03FCh | offset:segment | INT 0FFh |
Návrat z obsluhy přerušení provádí instrukce IRET ( Interrupt RETurn ). Tato instrukce načte z zásobníku „staré” IP,CS a Flags a tím předá kontrolu zpět do volajícího programu. V následujícím pořadí:
1. Ze zásobníku se načte IP
2. Ze zásobníku se načte CS
3. Ze zásobníku se načte Flags
Vyhrazená přerušení
Procesor 8086 má 5 rezervovaných přerušení:
INT 0h nastává při dělení nulou, a to instrukcemi DIV a IDIV.
INT 1h nastane po provedení instrukce, když je bit TF ( Trap Flag ) v příznakovém registru nastaven. Jde o tzv. krokovací režim.
INT 2h nastává při přijetí vnějšího NMI.
INT 3h se generuje, když procesor narazí na jedno-slabikovou ( 1B ) instrukci INT 3 se strojovým kódem 0CCh. Používá se k ladění programů.
INT 4h pokud je OF ( Overflow Flag ) nastaven a procesor narazí na instrukci INT0, provede se následující přerušení.
Všechny tyto přerušení můžeme také generovat softwarově uvnitř programu.
V dnešní ukázce si ukážeme, jak nastavit naši vlastní obsluhu přerušení, obsluhující přerušení INT 0 – dělení nulou. Princip při nastavování rutiny všech 256-přerušení je stejný, jde pouze o změnu IVT tabulky. Jako v minulém díle pracujeme v reálném režimu, a k ukázce nám bude stačit boot sector. Funkčnost obsluhy jednoduše vyzkoušíte změnou z dělení nulou na jinou hodnotu.
; preruseni.asm
org 0x7c00 ;nastaveni segmentu
jmp main
;promene
boot_zpr db 'Start ...',13,10,0
chyba_zpr db 'Deleno nulou',13,10,0
int_zpr db 'Nastaveno preruseni.',13,10,0
konec_zpr db 'Konec programu.',13,10,0
bootdev db 0
zprava: ;funkce na vypis na obrazovku
lodsb
or al,al
jz short zprava_konec
mov ah,0eh
mov bx,0007h
int 10h
jmp zprava
zprava_konec:
ret
main:
cli
mov ax,9000h
mov ss,ax
mov sp,0xffff ;nastaveni zasobniku
sti
mov [bootdev],dl ;dl = zarizeni ze ktereho je bootovano
mov ax,0
mov ds,ax
mov si,boot_zpr ;vypis boot zpravy
call zprava
mov word [ds:0h*4],obsluha ;nastaveni obsluhy preruseni
mov word [ds:0h*4+2],0
jmp umela_chyba
obsluha: ;nastane pri deleni 0
mov si,chyba_zpr
call zprava
iret
umela_chyba:
mov si,int_zpr
call zprava
mov bx,0 ; deleni nulou
div bx
mov si,konec_zpr
call zprava
smycka:
jmp smycka ;restart
times 510-($-$$) db 90h ;sector musi byt dlouhy 512B
dw 0xaa55 ;oznaceni boot sectoru
Kód přeložíme a nahrajeme na disketu:
nasm -o preruseni.bin -f bin preruseni.asm
dd if=preruseni.bin of=/dev/fd0 bs=512