Mikroişlemciler · Ders Notu
Bölüm 5.1: Kesmeler
Bir işlemci yalnız kendi programını yürütseydi dış dünyadan haberi olmazdı. Klavyeye basılır, zamanlayıcı bir darbe üretir, disk sürücüsü veriyi hazırlar; işlemcinin bu olaylara zamanında cevap vermesi gerekir. Kesme, işlemcinin o anki işini bir komut sınırında bırakıp olaya ait yordamı çalıştırması ve sonra kaldığı yerden devam etmesidir. Not, kesmenin ne olduğunu ve yoklamadan farkını anlatarak başlar. Ardından 8086'nın kesme vektör tablosunu ve INT ile IRET komutlarının yığına etkisini ele alır. Sonra işlemcinin kendi ürettiği istisnalara, INTR ve NMI pinlerinden gelen donanım kesmelerine, 8259A kesme denetleyicisine ve kesme gecikmesine geçer. Son kesimde bir kesme hizmet yordamı baştan yazılır, vektörü kurulur ve program biterken eski vektör geri yüklenir. Her kod parçası yazmaçlar, bayraklar, vektör tablosu ve yığınla birlikte adım adım izlenir.
Öğrenme çıktıları
- Kesme kavramını açıklamak
- Kesmenin ne olduğunu, yoklamadan (polling) farkını ve yazılım, donanım ve iç kesmelerin nereden geldiğini açıklamak.
- Vektör adresini hesaplamak
- Kesme vektör tablosunu kullanarak tip n için vektörün adresini (n × 4), oradaki baytlardan ISR'nin CS:IP değerini ve fiziksel adresini bulmak.
- INT ve IRET'i yığında izlemek
- INT n ve IRET sırasında FLAGS, CS ve IP'nin yığına hangi sırayla yazıldığını, SP'nin nasıl değiştiğini ve IF ile TF'nin neden sıfırlandığını adım adım göstermek.
- İstisnaları tanımak
- Bölme hatasının (INT 0) iki nedenini, INTO'nun (INT 4) çalışma koşulunu, tek adım (INT 1) ve kesme noktası (INT 3) düzeneklerini açıklamak.
- Donanım kesmelerini izlemek
- INTR kabul sürecini, kesme önceliklerini, 8259A'nın IRR, IMR ve ISR yazmaçlarını ve IMR ile tek bir IRQ'yu maskelemeyi izlemek; kesme gecikmesini saat turu olarak hesaplamak.
- Kesme hizmet yordamı yazmak
- Kendi ISR'sini yazmak, vektörünü güvenle kurmak, eski vektörü saklayıp geri yüklemek ve ISR yazım kurallarına uymak.
5.1.1Kesme kavramı
5.1.1.1Kesme nedir?
Bir program çalışırken işlemci komutları sırayla getirir, çözer ve yürütür. IP her komuttan sonra bir sonraki komutu gösterir; atlama ve çağrı komutları dışında bu sıra bozulmaz. Oysa bilgisayarın çevresinde programdan bağımsız olaylar olur. Kullanıcı bir tuşa basar, zamanlayıcı yongası belirli aralıklarla darbe üretir, seri porta bir karakter ulaşır. İşlemcinin bu olayları kaçırmaması ve onlara kısa sürede cevap vermesi gerekir.
Kesme (interrupt), işlemcinin o anki işini geçici olarak bırakıp özel bir yordamı çalıştırmasını sağlayan olaydır. Bu yordama kesme hizmet yordamı (interrupt service routine, ISR) denir. ISR bitince işlemci, kesildiği komuttan sonraki komutla programa devam eder.
Tanımdaki en önemli nokta son cümledir. Kesilen program kesildiğini fark etmez. Yazmaçları, bayrakları ve belleği, kesme olmasaydı alacakları değerlerle devam eder. Bunu sağlamak hem işlemcinin hem de ISR'yi yazan programcının görevidir. İşlemci dönüş adresini ve bayrakları kendiliğinden saklar. ISR ise kullandığı yazmaçları saklayıp çıkarken geri yükler.
Günlük hayattan bir benzetme kesmenin adımlarını iyi anlatır. Kitap okurken telefon çalar. Okuyucu önce okuduğu sayfayı işaretler, sonra telefonu açar ve konuşur. Konuşma bitince işaretli sayfaya döner ve okumayı sürdürür. Sayfayı işaretlemek dönüş adresini saklamaktır; telefon konuşması ISR'dir; işaretli sayfaya dönmek IRET komutudur. Okuyucu sayfayı işaretlemeyi unutursa nereden devam edeceğini bilemez. İşlemci de dönüş adresini yığına yazmadan ISR'ye geçseydi aynı duruma düşerdi.
Şekil 5.1.1 bu akışı zaman ekseninde gösterir. Ana program bir noktada durur, denetim ISR'ye geçer, ISR'nin son komutu IRET denetimi ana programa geri verir. Kesmeyi başlatan şey iki türlü olabilir. Program bir INT komutu yürütür ya da işlemcinin dışından bir istek gelir. İki durumda da işlemcinin sonrasında yaptıkları aynıdır.
Kesmelerin ikinci, daha gündelik bir kullanımı da vardır. 8086 tabanlı bir PC'de ekrana karakter yazmak, klavyeden tuş okumak, diske erişmek ya da programı bitirmek için hazır yordamlar bulunur. Bunlar anakarttaki ROM'da duran BIOS'un ve işletim sistemi DOS'un yordamlarıdır. Program bu yordamları adreslerini bilmeden, bir INT komutuyla çağırır. Örneğin ekrana karakter yazmak için video donanımını süren onlarca komut yazmak gerekmez; INT 10h bu işi yapar. Bu yüzden yazılım kesmeleri, programlamayı kolaylaştıran hazır hizmetler olarak da görülür. Ekran, klavye, disk sürücüsü ve bellek yönetimi gibi donanımlarla çalışan kesme işlevlerinin ayrıntısı 5.2 Kesme Fonksiyonları notunun konusudur. Bu not kesmenin kendisini, yani işlemcinin bir kesmeyi nasıl kabul edip nasıl geri döndüğünü anlatır.
5.1.1.2Yoklama mı, kesme mi?
İşlemcinin bir aygıtın durumunu öğrenmesinin iki yolu vardır. Birincisi aygıta sürekli sormaktır: “Veri hazır mı?” Bu yönteme yoklama (polling) denir. İşlemci bir döngü içinde aygıtın durum portunu okur, hazır bitine bakar, hazır değilse yeniden okur. İkincisi aygıtın kendisinin haber vermesidir. Aygıt verisi hazır olduğunda işlemcinin bir pinini sürer; işlemci o anki komutu bitirir ve ISR'ye geçer. Bu da kesme yöntemidir.
Şekil 5.1.2 iki yöntemi aynı zaman ekseninde karşılaştırır. Yoklamanın bedelini bir sayıyla görmek mümkündür. Klavyenin durumunu soran en kısa yoklama döngüsü üç komuttan oluşur: portu okuyan bir IN, hazır bitini sınayan bir TEST ve hazır değilse başa dönen bir koşullu atlama.
; yoklama döngüsü (port numarası ve bit örnek)
bekle: IN AL, 64h ; durum portunu oku (10 tur)
TEST AL, 01h ; veri hazır biti (4 tur)
JZ bekle ; hazır değilse yeniden sor (16 tur)Intel'in 8086 kılavuzuna göre sabit portlu IN 10, yazmaç ile sabiti sınayan TEST 4, atlayan bir JZ 16 saat turu sürer. Bir tur 30 saat turudur; 5 MHz'lik bir 8086'da bir saat turu 200 ns olduğundan döngü 6 µs'de bir döner. İşlemci bir saniyede aygıta yaklaşık 166 000 kez sorar. Hızlı yazan bir kullanıcı saniyede on tuşa basar. Bu durumda 166 000 sorudan yalnız on tanesi “evet” cevabı alır, geri kalanı boşa gider. Daha kötüsü, işlemci bu süre içinde başka bir iş yapamaz.
Yoklamayı daha seyrek yapmak işlemciye zaman kazandırır, ama bu kez olay geç fark edilir. Program her 10 ms'de bir sorarsa bir tuşa basıldıktan sonra cevap 10 ms'ye kadar gecikebilir. Aralık uzadıkça kaybolan veri riski de artar. Örneğin seri porttan 9600 bit/s hızla gelen karakterler arasında yaklaşık 1 ms vardır. Aygıtın tek karakterlik bir tamponu varsa, 1 ms içinde okunmayan karakterin üzerine bir sonraki yazılır.
Kesmede bu ikilem ortadan kalkar. İşlemci olay olmadıkça ana programı yürütür; olay olduğu anda, en geç o anki komut bitince ISR'ye geçer. Bedeli ise düzenektir. Her olay için bir yordam yazılır, yordamın adresi bir tabloya konur, dönüş adresi için yığın kullanılır ve donanımda isteği işlemciye ileten hatlar gerekir. Tablo 5.1.1 iki yöntemi karşılaştırır.
| Ölçüt | Yoklama | Kesme |
|---|---|---|
| Donanım | basit; ek hat gerekmez | istek hattı, gerekirse kesme denetleyicisi |
| İşlemci zamanı | boşa harcanır; sorguların çoğu “hayır” döner | yalnız olay olduğunda harcanır |
| Tepki süresi | sorgu aralığına bağlı; olay geç algılanabilir | kısa; en geç yürüyen komut bitince |
| Yazılım düzeni | tek bir döngü | vektör tablosu, yığın ve ISR |
| Uygun olduğu yer | tek işli, basit sistemler; çok hızlı ve sürekli veri | seyrek ve zamanı belirsiz olaylar; çok görevli sistemler |
Yoklama her zaman kötü değildir. Mikrodenetleyicili küçük bir devre yalnız bir düğmeyi bekliyorsa yoklama en sade çözümdür. Çok hızlı bir aygıt her birkaç mikrosaniyede veri üretiyorsa, her veri için kesme düzeneğinin yüküne katlanmak yerine sıkı bir yoklama döngüsü daha hızlı olabilir. Genel amaçlı bir bilgisayarda ise klavye, zamanlayıcı, disk ve seri port gibi birbirinden bağımsız ve zamanı önceden bilinmeyen olaylar vardır. Bunların hepsini tek bir döngüde yoklamak hem işlemciyi meşgul eder hem de tepkiyi yavaşlatır. PC mimarisi bu yüzden kesmelere dayanır.
“Kesmede işlemci aygıtı hiç beklemez” sonucunu “kesme her zaman yoklamadan hızlıdır” biçiminde genellemek. Kesme düzeneğinin kendisi de zaman alır: 8086'da bir donanım kesmesinin kabulü 61, IRET 24 saat turudur. Saniyede binlerce kez gelen bir olayda bu ek yük yoklamanın maliyetini aşabilir. Kesmenin üstünlüğü, seyrek olaylarda işlemcinin boşa beklememesidir.
5.1.1.3Kesme türleri ve tip numaraları
8086 en çok 256 farklı kesmeyi ayırt eder. Her kesmenin 0 ile 255 (00h–FFh) arasında bir tip numarası vardır. İşlemci kesmeyi kabul ettiğinde ilk iş bu numarayı belirler, sonra numaraya göre hangi yordama gideceğini bir tablodan okur. Kesmeler, tip numarasının nereden geldiğine ve kesmeyi neyin başlattığına göre üç gruba ayrılır.
Yazılım kesmesi (software interrupt), program içindeki INT n komutuyla bilerek çağrılan kesmedir; tip numarası n komutun içindedir. Donanım kesmesi (hardware interrupt), bir dış aygıtın işlemcinin INTR ya da NMI pinini sürmesiyle başlayan kesmedir. İç kesme ya da istisna (exception), işlemcinin bir komutu yürütürken kendisinin ürettiği kesmedir; sıfıra bölme bunun en bilinen örneğidir.
Yazılım kesmeleri. BIOS ve DOS hizmetleri bu yolla kullanılır: INT 10h ekran, INT 16h klavye, INT 21h DOS hizmetleridir. Bu grupta iki özel komut daha vardır. INT 3 hata ayıklayıcıların kullandığı kesme noktası komutudur ve tek baytla kodlanır. INTO ise taşma bayrağı OF = 1 olduğunda INT 4'ü çağırır, OF = 0 ise hiçbir şey yapmaz. Bu iki komut, hata ayıklama ve hata yakalama amacıyla kullanıldıkları için sık sık istisnalarla birlikte anılır.
Donanım kesmeleri. 8086'nın kesme isteği için iki giriş pini vardır. INTR (interrupt request) maskelenebilir girişidir: IF = 0 iken işlemci bu pini dikkate almaz. Klavye, zamanlayıcı, disk ve seri port istekleri bu pinden gelir. NMI (non-maskable interrupt) ise maskelenemez girişidir; IF ne olursa olsun kabul edilir. Bellek eşlik hatası ya da güç kaybı gibi, programın kapatıp geçiştiremeyeceği acil olaylar için ayrılmıştır.
İç kesmeler. İşlemci bazı durumları kendisi kesmeye dönüştürür. DIV ya da IDIV sıfıra böldüğünde veya bölüm hedef yazmaca sığmadığında INT 0 üretilir. Tuzak bayrağı TF = 1 iken her komuttan sonra INT 1 üretilir; bu, hata ayıklayıcıların programı tek adım yürütmesini sağlar.
Tablo 5.1.2, ilk beş tip numarasının görevini verir. Bu beş tipi 8086'nın kendisi kullanır; numaraları sabittir ve programcı bunları başka bir iş için kullanmamalıdır.
| Tip | Kaynak | Açıklama |
|---|---|---|
| 00h | iç | Bölme hatası: DIV/IDIV sıfıra bölme ya da bölüm sığmıyor |
| 01h | iç | Tek adım (TF = 1); hata ayıklayıcılar kullanır |
| 02h | donanım | NMI pini (maskelenemez) |
| 03h | yazılım | Kesme noktası; tek baytlık INT 3 (CCh) |
| 04h | yazılım | INTO: OF = 1 ise taşma kesmesi |
| 05h–FFh | — | Intel 05h–1Fh'yi ileriki işlemciler için ayırmıştır; PC'de BIOS, DOS ve kullanıcı programları kullanır |
Intel, 05h–1Fh aralığını ileriki işlemcileri için ayırdığını belgelerinde açıkça yazmıştı. IBM PC'nin tasarımcıları ise bu uyarıya uymadı ve BIOS hizmetlerini 05h, 08h–1Fh aralığına yerleştirdi. Örneğin ekranı yazıcıya basan BIOS yordamı tip 05h'yi kullanır. 80186 ve 80286 ile birlikte tip 05h, BOUND komutunun sınır aşımı kesmesi oldu. Böylece aynı numara iki farklı anlam kazandı ve PC yazılımı yıllarca bu çakışmayla uğraştı. Bu öykü, ayrılmış numaraların neden ayrılmış kalması gerektiğini iyi gösterir.
5.1.1.4Üç kaynağın karşılaştırması
Üç kesme türü birbirinden ilk bakışta çok farklı görünür: biri bir komut, biri bir pin, biri bir hata durumudur. Aralarındaki fark ise yalnız iki noktadadır: tip numarasının nereden geldiği ve kesmenin ne zaman oluştuğu. Tablo 5.1.3 bu farkları yan yana koyar.
| Yazılım (INT n) | Donanım (INTR / NMI) | İç (istisna) | |
|---|---|---|---|
| Tetikleyen | programdaki komut | dış aygıt, pin | işlemcinin kendisi |
| Zamanlama | eş zamanlı: hep aynı yerde | eş zamansız: herhangi bir komuttan sonra | eş zamanlı: hatalı komutta |
| Tip numarası | komutun içinde | INTR: 8259A, INTA ile; NMI: hep 2 | sabit: 0, 1 |
| IF = 0 iken | yine çalışır | INTR bekler, NMI çalışır | yine çalışır |
| Tipik kullanım | BIOS/DOS hizmeti, INT 3, INTO | klavye, zamanlayıcı, disk | bölme hatası, tek adım |
Eş zamanlı ve eş zamansız sözcükleri burada program akışına göre kullanılır. Bir yazılım kesmesi programın aynı noktasında, her çalıştırmada aynı biçimde oluşur. Bir istisna da öyledir: aynı veriyle aynı DIV komutu her seferinde aynı hatayı verir. Donanım kesmesi ise programın hangi komutunda geleceği bilinemeyen bir olaydır. Kullanıcı tuşa bir kez döngünün başında, bir kez ortasında basar. Bu yüzden donanım kesmesinin ISR'si, programın herhangi bir noktasında çalışabilecekmiş gibi yazılmalıdır. ISR'lerin yazmaçları saklama zorunluluğu buradan gelir.
Tablonun altında önemli bir ortak nokta yatar. Tip numarası bir kez belirlendikten sonra üç tür de aynı adımları izler: FLAGS, CS ve IP yığına itilir, IF ve TF sıfırlanır, CS:IP vektör tablosundaki değerle yüklenir. Yani işlemcinin içinde tek bir kesme düzeneği vardır; kaynaklar yalnız bu düzeneğe hangi numarayla ve hangi anda girildiğini belirler.
CLI komutunun bütün kesmeleri kapattığını sanmak. CLI yalnız IF'yi sıfırlar ve yalnız INTR girişini etkiler. INT 21h gibi yazılım kesmeleri, sıfıra bölme gibi istisnalar ve NMI, IF = 0 iken de çalışır. Kritik bir kod parçasını “kesmelerden korumak” için CLI kullanıldığında NMI'ın yine gelebileceği unutulmamalıdır.
5.1.2Kesme vektör tablosu
5.1.2.1Tablonun yapısı
İşlemci bir kesmenin tip numarasını öğrendikten sonra o kesmeye ait ISR'nin nerede olduğunu bulmalıdır. 8086'da bu bilgi belleğin en başındaki bir tabloda durur. Tablonun yeri sabittir; donanım her kesmede aynı adrese bakar.
Kesme vektörü (interrupt vector), bir kesme tipinin hizmet yordamının 4 baytlık uzak adresidir: 2 bayt IP (bağıl konum) ve 2 bayt CS (kesim). Kesme vektör tablosu (interrupt vector table, IVT), 256 kesme tipinin vektörlerini sırayla tutan ve fiziksel 00000h–003FFh adreslerini kaplayan 1 KB'lık tablodur.
Tablonun boyu kolayca hesaplanır: 256 tip × 4 bayt = 1024 bayt = 1 KB. Tip 0'ın vektörü 00000h'de, tip 1'inki 00004h'te, tip 2'ninki 00008h'de başlar ve böyle sürer. Son tip FFh'nin vektörü 003FCh–003FFh baytlarındadır. Tip n'in vektörünün başladığı adres bu yüzden basit bir çarpımdır:
vektör adresi = n × 4 (kesim 0000h içinde)
Vektörün dört baytının dizilişi de sabittir. Şekil 5.1.3 tabloyu gösterir. Düşük adresli sözcük (n × 4 ve n × 4 + 1) IP değerini tutar. Yüksek adresli sözcük (n × 4 + 2 ve n × 4 + 3) ise CS değeridir. İki sözcük de 8086'nın bütün sözcükleri gibi küçük uçlu (little-endian) yazılır: önce düşük bayt, sonra yüksek bayt.
Tablonun neden belleğin en başında olduğu sorusunun cevabı 8086'nın tasarımındadır. Donanımın tabloyu bulmak için hiçbir yazmaca bakması gerekmez; adres yalnız tip numarasından üretilir. n × 4 işlemi devrede iki bitlik bir kaydırmadır. Tip numarasının 8 biti adres hatlarının A2–A9 bitlerine konur; A0, A1 ve A10–A19 sıfırdır. Bu yüzden vektör okuma ek bir toplayıcı ya da yazmaç gerektirmez. Bedeli, belleğin ilk 1 KB'ının her zaman RAM olması zorunluluğudur. Programlar tabloyu değiştirebilmelidir; ROM'da olsaydı hiçbir program kendi ISR'sini kuramazdı.
Tablo, işlemcinin sıradan bir bellek bölgesidir. Özel bir koruması yoktur. Kesim yazmacı 0000h yapılan her program tablonun herhangi bir baytını okuyup yazabilir. Bu esneklik, programların kendi ISR'lerini kurabilmesini sağlar; aynı zamanda hatalı bir programın tabloyu bozarak bütün sistemi çökertebilmesi anlamına gelir. Korumalı kipteki sonraki işlemciler bu sorunu tabloyu korumalı bir yapıya taşıyarak çözer. 8086'nın gerçek kipinde ise tablonun güvenliği tamamen programcıya kalmıştır.
Vektörün dört baytını “CS önce, IP sonra” sırasıyla düşünmek. CS:IP yazılışı kesim önce yazıldığı için bu yanılgıyı doğurur. Bellekte ise sıra terstir: n × 4 adresinde IP'nin düşük baytı, n × 4 + 2 adresinde CS'nin düşük baytı vardır.
5.1.2.2Vektör adresi adım adım
Kuralın uygulanışını bir örnek üzerinden izlemek yararlıdır. Program INT 10h komutunu yürütüyor olsun. İşlemcinin vektörü bulup ISR'ye geçmesi dört adımda gerçekleşir.
INT 10h için vektörün adresini, ISR'nin CS:IP değerini ve fiziksel adresini bulalım. Tablonun 0000:0040h adresinden başlayan dört baytı sırasıyla 65 F0 00 F0 olsun.
- Tip numarası. Komutun ikinci baytı tip numarasıdır: n = 10h = 16.
- Vektör adresi. n × 4 = 10h × 4 = 40h (onlu: 16 × 4 = 64). Vektör 0000:0040h–0000:0043h baytlarındadır. İkili sayıda 0001 0000b iki bit sola kaydırılınca 0100 0000b = 40h olur.
- Baytları okuma. 0040h'te 65h, 0041h'te F0h vardır; düşük sözcük IP = F065h'dir. 0042h'te 00h, 0043h'te F0h vardır; yüksek sözcük CS = F000h'dir.
- Fiziksel adres. CS × 16 + IP = F0000h + F065h = FF065h. ISR, belleğin en üst 64 KB'ındaki BIOS ROM'unun içinde başlar.
Örnekte okunan baytlar Şekil 5.1.4'te görülür. İşlemci bu iki sözcüğü iki ayrı bellek okumasıyla alır. 8086 çift adresteki bir sözcüğü tek yol çevrimiyle okur. n × 4 ve n × 4 + 2 adreslerinin ikisi de çift olduğundan her sözcük tek çevrimde gelir. Vektör tablosunun 4 baytlık hizalanması bu açıdan da yerindedir.
Dördüncü adımdaki fiziksel adres hesabı, 8086'nın bütün bellek erişimlerinde kullandığı kesimleme kuralıdır: 16 bitlik kesim değeri dört bit sola kaydırılır (16 ile çarpılır) ve bağıl konum eklenir. CS = F000h kesimi F0000h'de başlar. IP = F065h bu kesimin içinde bir konumdur. Sonuç 20 bitlik FF065h adresidir.
Burada sıranın önemli bir ayrıntısı vardır. İşlemci vektörü okuyup CS:IP'yi yüklemeden önce dönüş bilgisini yığına yazar. Yani ISR'nin ilk komutu FF065h'den getirilirken eski CS, IP ve FLAGS çoktan yığındadır. Bu sıra bozulsaydı, yani CS:IP önce yüklenseydi, dönüş adresi kaybolurdu. Adımların tam sırası Tablo 5.1.5'tedir.
5.1.2.3Tablodan yordama, yordamdan tabloya
Vektör tablosu üzerinde iki yönlü hesap yapılır. Birinci yönde tip numarasından vektörün adresine gidilir; bu, n × 4 çarpımıdır. İkinci yönde tablonun bir adresinden hangi tipin vektörü olduğu bulunur; bu da adresi 4'e bölmektir. İkinci yön, bellek dökümünde bir bayt dizisine bakıp ne olduğunu anlamak için gerekir.
Bir bellek dökümünde 0000:0058h adresinden başlayarak 2E E8 00 F0 baytları görülüyor. a) Hangi kesmenin vektörüdür? b) ISR'nin CS:IP değeri ve fiziksel adresi nedir? c) Aynı tablodaki tip 1Ch'nin vektörü hangi adreslerdedir?
- a) 58h / 4 = 16h. Adres 4'ün katı olduğu için bir vektörün başıdır: tip 16h, yani BIOS klavye hizmeti INT 16h.
- b) Düşük sözcük: 2Eh, E8h → IP = E82Eh. Yüksek sözcük: 00h, F0h → CS = F000h. Fiziksel adres F0000h + E82Eh = FE82Eh.
- c) 1Ch × 4 = 70h. IP 00070h–00071h, CS 00072h–00073h baytlarındadır.
Adres 4'e tam bölünmeseydi, örneğin 005Ah, bu bir vektörün başı değil, tip 16h vektörünün CS yarısı olurdu (5Ah = 4 × 16h + 2).
Hesap bütün tipler için aynıdır: IP n × 4 ve n × 4 + 1'de, CS n × 4 + 2 ve n × 4 + 3'tedir. Tip 00h için adres 0000h, tip FFh için 03FCh çıkar; aradaki bütün adresler 4'ün katıdır.
Tip numarasını doğrudan adres sanmak (INT 21h → 00021h) ya da 4 yerine 2 ile çarpmak (INT 21h → 00042h). Doğrusu 21h × 4 = 84h'dir. Onaltılık çarpımda yanılmamak için sayıyı önce onluya çevirmek de bir yoldur: 21h = 33, 33 × 4 = 132 = 84h.
5.1.2.4PC'de tip numaralarının paylaşımı
256 tip numarasının ilk beşi 8086'ya aittir. Geri kalanı, işlemciyi kullanan sistemin tasarımcılarına bırakılmıştır. IBM PC ve DOS'ta numaralar kabaca şöyle paylaşılmıştır (Tablo 5.1.4).
| Tip | Vektör adresi | Görev |
|---|---|---|
| 00h–04h | 000h–013h | 8086'nın kendi tipleri (bölme hatası, tek adım, NMI, kesme noktası, taşma) |
| 05h | 014h | BIOS: ekranı yazıcıya bas |
| 08h–0Fh | 020h–03Fh | donanım kesmeleri IRQ0–IRQ7 (08h zamanlayıcı, 09h klavye) |
| 10h | 040h | BIOS: video / ekran hizmetleri |
| 13h | 04Ch | BIOS: disk hizmetleri |
| 16h | 058h | BIOS: klavye hizmetleri |
| 1Ah | 068h | BIOS: saat / tarih |
| 20h | 080h | DOS: programı sonlandır |
| 21h | 084h | DOS: genel hizmetler (AH ile seçilir) |
| 33h | 0CCh | fare sürücüsü |
| 60h–67h | 180h–19Fh | kullanıcı programlarına ayrılmış |
| 70h–77h | 1C0h–1DFh | PC/AT'de ikinci 8259A'nın IRQ8–IRQ15 kesmeleri |
Bu tablodan çıkan pratik sonuç şudur: kendi ISR'sini kuracak bir program, kullanılmayan bir tip seçmelidir. 60h–67h aralığı bu amaçla ayrılmıştır. Notun son kesimindeki örnekler bu yüzden tip 60h, 61h ve 90h'yi kullanır. Kullanılan bir tipin, örneğin INT 21h'nin vektörünü değiştiren bir program, DOS'un bütün hizmetlerini kendi yordamına yönlendirmiş olur. Bu bazen bilerek yapılır. Örneğin bir program, DOS'un bölme hatası yordamı yerine kendi yordamını çalıştırmak için tip 0'ın vektörünü değiştirir. O zaman program biterken eski vektörü geri yüklemek zorunludur.
5.1.3INT ve IRET
5.1.3.1INT n'in adımları
INT n komutu iki bayttır: işlem kodu CDh ve tip numarası n. Örneğin INT 21h bellekte CD 21 olarak durur. İşlemci bu komutu yürütürken programcının göremediği bir dizi iş yapar. Bu işler bir yordam çağrısına benzer, ama ondan daha fazlasını kapsar. Tablo 5.1.5 adımları sırasıyla verir.
| Adım | İş | Etkisi | Yol çevrimi |
|---|---|---|---|
| 1 | FLAGS yığına itilir | SP ← SP − 2, [SS:SP] ← FLAGS | bellek yazma |
| 2 | IF ve TF sıfırlanır | IF = 0, TF = 0 | — |
| 3 | CS yığına itilir | SP ← SP − 2, [SS:SP] ← CS | bellek yazma |
| 4 | IP yığına itilir (INT'ten sonraki komutun adresi) | SP ← SP − 2, [SS:SP] ← IP | bellek yazma |
| 5 | vektör okunur | IP ← [0000:n×4], CS ← [0000:n×4+2] | iki bellek okuma |
| 6 | ISR çalışır, sonunda IRET | IP, CS, FLAGS sırasıyla yığından alınır | üç bellek okuma |
Tablonun her satırının bir gerekçesi vardır. Adım 1 bayrakları saklar. Kesilen programın son karşılaştırmasının sonucu, örneğin ZF, ISR boyunca değişebilir. Program bir CMP ile bir JE arasında kesilirse ve bayraklar saklanmazsa, JE yanlış yöne atlar. Bayrakların otomatik saklanması bu yüzden zorunludur. CALL komutu bayrakları saklamaz, çünkü bir yordamın ne zaman çağrılacağını program bilir.
Adım 2 IF ve TF'yi sıfırlar. IF = 0, ISR'nin daha ilk komutunda başka bir maskelenebilir kesmeyle kesilmesini önler. TF = 0 ise ISR'nin tek adım kesmesiyle adım adım durdurulmasını engeller. Bu sıfırlamanın FLAGS yığına itildikten sonra yapılması önemlidir. Yığındaki kopya eski IF ve TF değerlerini taşır. IRET bu kopyayı geri yüklediğinde IF ve TF de kesmeden önceki değerlerine döner.
Adım 3 ve 4 dönüş adresini saklar. ISR başka bir kesimde olabileceği için yalnız IP yetmez, CS de saklanır. Yığına yazılan IP, INT komutunun kendi adresi değil, INT'ten sonraki komutun adresidir. İşlemci INT'i getirirken IP'yi zaten iki bayt ilerletmiştir.
Adım 5 vektörü okur ve CS:IP'ye yükler. Bu andan sonra işlemcinin getireceği ilk komut ISR'nin ilk komutudur. BIU'nun önden getirme kuyruğu da boşaltılır; kuyruktaki baytlar INT'ten sonraki komutlara aitti ve artık geçersizdir.
Yığına toplam 6 bayt, yani 3 sözcük yazılır. INT n'in tamamı 8086'da 51 saat turu sürer. Bu turların önemli bir kısmı beş bellek erişimine harcanır: yığına üç yazma ve vektör tablosundan iki okuma. Kuyruğun boşaltılıp ISR'nin ilk baytlarının yeniden getirilmesi de ayrıca zaman alır.
IRET (interrupt return), kesme hizmet yordamından dönüş komutudur. Yığından sırasıyla IP'yi, CS'yi ve FLAGS'i alır; her alışta SP 2 artar. Tek bayttır (CFh) ve 8086'da 24 saat turu sürer.
IRET, INT'in yığına yazdıklarını ters sırada geri okur. Yığın son giren ilk çıkar düzeninde çalıştığı için bu doğaldır: en son itilen IP ilk alınır, en önce itilen FLAGS en son alınır. FLAGS'in geri gelmesiyle IF ve TF de eski değerlerini alır. Böylece kesmeden önce IF = 1 olan bir program, IRET'ten sonra yine IF = 1 ile devam eder.
Tablo 5.1.6 bu notta geçen kesme komutlarının kodlamasını ve süresini toplu olarak verir.
| Komut | Makine kodu | Uzunluk | Saat turu |
|---|---|---|---|
INT n | CD n | 2 bayt | 51 |
INT 3 | CC | 1 bayt | 52 |
INTO | CE | 1 bayt | 53 (OF = 1) / 4 (OF = 0) |
IRET | CF | 1 bayt | 24 |
CLI / STI | FA / FB | 1 bayt | 2 |
5.1.3.2Yığın adım adım: INT 10h
Adımların yığına etkisini somut sayılarla izleyelim. Bir .COM programında INT 10h komutu 0700:0104 adresinde olsun. emu8086'nın .COM yüklemesinde CS = DS = SS = 0700h ve SP = FFFEh'dir. Başlangıçta yalnız IF = 1'dir; 8086'nın FLAGS yazmacında bit 1 ve bit 12–15 her zaman 1 okunduğu için FLAGS = F202h'dir. INT 10h'nin vektörü Örnek 5.1.1'deki gibi F000:F065 olsun.
| Adım | İş | SP | [FFF8h] | [FFFAh] | [FFFCh] | CS:IP | FLAGS |
|---|---|---|---|---|---|---|---|
| 0 | başlangıç | FFFEh | — | — | — | 0700:0104 | F202h |
| 1 | FLAGS itilir | FFFCh | — | — | F202h | 0700:0104 | F202h |
| 2 | IF = TF = 0 | FFFCh | — | — | F202h | 0700:0104 | F002h |
| 3 | CS itilir | FFFAh | — | 0700h | F202h | 0700:0104 | F002h |
| 4 | IP itilir | FFF8h | 0106h | 0700h | F202h | 0700:0104 | F002h |
| 5 | CS:IP ← vektör | FFF8h | 0106h | 0700h | F202h | F000:F065 | F002h |
| 6 | IRET: IP, CS alınır | FFFCh | 0106h | 0700h | F202h | 0700:0106 | F002h |
| 7 | IRET: FLAGS alınır | FFFEh | 0106h | 0700h | F202h | 0700:0106 | F202h |
Tablo 5.1.7'nin satırları şöyle okunur. Adım 1'de SP önce 2 azalır ve FFFCh olur, FLAGS'in o anki değeri F202h bu adrese yazılır. Adım 2'de yazmaçtaki IF ve TF sıfırlanır; F202h'deki bit 9 (IF) silinince FLAGS = F002h olur. Yığındaki kopya ise F202h olarak kalır. Adım 3'te SP = FFFAh olur ve CS = 0700h yazılır. Adım 4'te SP = FFF8h olur ve dönüş adresi yazılır. INT 10h iki bayt olduğundan dönüş adresi 0104h + 2 = 0106h'dir. Adım 5'te CS:IP vektörden yüklenir ve ISR çalışmaya başlar. Bu örnekte ISR ekrana bir karakter yazar.
ISR bitip IRET çalışınca yığın ters yönde boşalır. Adım 6'da [FFF8h]'deki 0106h IP'ye, [FFFAh]'daki 0700h CS'ye alınır ve SP = FFFCh olur. Adım 7'de [FFFCh]'deki F202h FLAGS'e alınır ve SP = FFFEh'ye döner. IF yeniden 1'dir. Program 0700:0106'dan, yani INT 10h'den sonraki komuttan devam eder.
Son satırdaki bir ayrıntı dikkat ister: IRET'ten sonra yığındaki üç sözcük bellekte durmaya devam eder (Şekil 5.1.6). POP ve IRET belleği silmez, yalnız SP'yi artırır. SP'nin üstündeki bölge artık “boş” sayılır ve bir sonraki PUSH ya da kesme bu değerlerin üzerine yazar. Bir hata ayıklayıcıda yığın penceresine bakarken eski değerlerin görünmesi bu yüzdendir.
Aynı program kendi ISR'sini INT 10h vektörüne koyarak emu8086'da çalıştırıldığında yığına yazılan sözcükler okunabilir. Kod şudur:
org 100h
mov ah, 0Eh ; 0100h, 2 bayt
mov al, 'A' ; 0102h, 2 bayt
int 10h ; 0104h, 2 bayt (CD 10)
ret ; 0106h: dönüş adresiINT 10h yürütüldükten hemen sonra SP = FFF8h'dir ve yığında [FFF8h] = 0106h, [FFFAh] = 0700h, [FFFCh] = F202h sözcükleri bulunur. IRET'ten sonra SP = FFFEh'dir. Değerler Tablo 5.1.7 ile birebir aynıdır.
Yığına itilen IP'yi INT komutunun kendi adresi sanmak (bu örnekte 0104h). İtilen değer bir sonraki komutun adresidir (0106h). Aksi hâlde IRET işlemciyi yeniden INT'e götürür ve program sonsuz bir döngüye girer.
5.1.3.3INT/IRET ile CALL/RET
INT ile bir yordam çağrısı arasındaki benzerlik açıktır: ikisi de dönüş adresini yığına yazar ve başka bir yere atlar. Farklar ise ISR'lerin neden ayrı bir dönüş komutu gerektirdiğini gösterir. Tablo 5.1.8 iki çifti karşılaştırır.
| CALL / RET | INT n / IRET | |
|---|---|---|
| Hedef adres | komutun içinde (etiket) | vektör tablosundan (n × 4) |
| Yığına yazılan | IP (yakın) ya da CS + IP (uzak) | FLAGS + CS + IP (6 bayt) |
| Bayraklar | değişmez | IF = 0, TF = 0 yapılır |
| Dönüş | RET: IP (ve CS) geri alınır | IRET: IP, CS, FLAGS geri alınır |
| Komut boyu | 3 bayt (yakın CALL) | 2 bayt (INT 3: 1 bayt) |
| Kim çağırır? | yalnız program | program, işlemci (istisna) ya da donanım |
En önemli satır sonuncusudur. Bir yordamı yalnız program çağırır; program o noktada bayrakların durumunu bilir ve gerekiyorsa kendisi saklar. ISR ise bir donanım olayıyla, programın haberi olmadan çağrılabilir. Bu yüzden bayrakları saklama işi işlemciye verilmiştir.
Hedef adres satırı da önemli bir esneklik sağlar. Assembler CALL'un hedefini komutun içine yazar ve bu değer değişmez. INT'in hedefi ise tablodan çalışma anında okunur. Vektörü değiştirmek, kodu hiç değiştirmeden bütün INT çağrılarını başka bir yordama yönlendirir. DOS'un, sürücülerin ve yerleşik programların BIOS hizmetlerine kendi işlevlerini ekleyebilmesi bu sayededir. Kesme zincirleme (interrupt chaining) tekniği bu esnekliğe dayanır. Yeni yordam kendi işini yapar, sonra eski vektöre atlayarak eski yordamı da çalıştırır.
Komut boyu satırında INT'in 2 bayt olması da bir üstünlüktür. Uzak bir CALL 5 bayttır; BIOS'un bir yordamını uzak CALL ile çağırmak hem daha uzun kod hem de BIOS'un sürümüne bağlı sabit bir adres demektir. INT 10h ise her BIOS'ta aynı iki bayttır.
5.1.3.4RET ile biten ISR: kod izleme
Tablo 5.1.8'deki farkın pratik sonucunu görmek için bilerek hatalı bir program izlenir. Program tip 61h'ye kendi ISR'sini kurar, INT 61h ile çağırır, ama ISR'yi IRET yerine yakın RET ile bitirir.
org 100h
mov ax, 0
mov es, ax
mov word ptr es:[61h*4], offset isr
mov word ptr es:[61h*4+2], cs
int 61h
hlt ; SP = ?
isr:
ret ; HATA!Kod satır satır şöyle çalışır. mov ax, 0 ve mov es, ax ES'yi 0000h yapar. Kesim yazmacına sabit doğrudan yüklenemediği için 0 önce AX'e konur. Bundan sonra es:[…] ile yazılan her adres vektör tablosunun içindedir. Üçüncü satır tip 61h'nin vektörünün IP yarısına, 61h × 4 = 184h adresine, isr etiketinin bağıl konumunu yazar. Assembler 61h*4 ifadesini kendisi hesaplar ve komuta 0184h olarak koyar. Dördüncü satır 186h adresine CS'nin değerini yazar. CS ile ISR aynı kesimdedir; böylece vektör 0700:0114'ü gösterir.
Programın baytlarını saymak, izleme tablosundaki IP değerlerini anlamayı kolaylaştırır. Komutların uzunlukları ve adresleri şöyledir: mov ax, 0 3 bayt (0100h–0102h), mov es, ax 2 bayt (0103h–0104h), ES önekli sözcük yazması 7 bayt (0105h–010Bh), CS yazması 5 bayt (010Ch–0110h), int 61h 2 bayt (0111h–0112h), hlt 1 bayt (0113h) ve ret 1 bayttır (0114h). Bu yüzden isr etiketi 0114h'dir ve INT 61h'nin dönüş adresi 0113h'dir.
| Satır | Komut | CS:IP (sonraki) | SP | IF | Yığın / bellek |
|---|---|---|---|---|---|
| 2 | mov ax, 0 | 0700:0103 | FFFEh | 1 | [FFFEh] = 0000h |
| 3 | mov es, ax | 0700:0105 | FFFEh | 1 | ES = 0000h |
| 4 | mov … [61h*4], offset isr | 0700:010C | FFFEh | 1 | [0000:0184h] = 0114h |
| 5 | mov … [61h*4+2], cs | 0700:0111 | FFFEh | 1 | [0000:0186h] = 0700h |
| 6 | int 61h | 0700:0114 | FFF8h | 0 | [FFF8h] = 0113h, [FFFAh] = 0700h, [FFFCh] = F202h |
| 9 | ret (hatalı) | 0700:0113 | FFFAh | 0 | [FFFAh] = 0700h, [FFFCh] = F202h yığında kaldı |
| 7 | hlt | 0700:0114 | FFFAh | 0 | işlemci durdu, IF = 0 |
İzlemenin kritik iki satırı vardır (Tablo 5.1.9). Satır 6'da int 61h yığına FLAGS (F202h), CS (0700h) ve dönüş IP'si (0113h) olmak üzere 6 bayt yazar. SP = FFF8h ve IF = 0 olur, CS:IP vektörden 0700:0114 olarak yüklenir. Satır 9'da ISR'deki yakın ret yalnız yığının tepesindeki sözcüğü alır: IP = 0113h. CS değişmez; ISR ile program aynı kesimde olduğu için bu sefer tesadüfen doğru kesime dönülür. SP ise yalnız 2 artar ve FFFAh olur. Beklenen değer FFFEh'dir; yığın 4 bayt kaymıştır. FLAGS yığından geri alınmadığı için IF = 0 kalmıştır.
int 61h'den sonra: yığında üç sözcük, SP = FFF8h, IF = 0. Sağda hatalı ret'ten sonra: yalnız IP alınmış, CS ve FLAGS artık veri olarak yığında kalmış, SP = FFFAh, IF hâlâ 0.Program hlt'e ulaştığında iki sorun birden vardır (Şekil 5.1.7). Birincisi, kesmeler kapalıdır. HLT işlemciyi bir kesme gelene kadar durdurur. IF = 0 olduğu için INTR'den gelen hiçbir istek işlemciyi uyandıramaz; yalnız NMI ya da RESET bunu yapabilir. Bu küçük programda işlemci sonsuza kadar bekler. İkincisi, yığın kaymıştır. HLT yerine bir ret olsaydı yığının tepesindeki 0700h'yi dönüş adresi sanır ve 0700:0700 adresine atlardı. Orada çalıştırılacak anlamlı bir kod yoktur.
Sorunu RETF ile çözmeye çalışmak da yetmez. Uzak dönüş komutu IP ve CS'yi alır, SP'yi 4 artırır. FLAGS'in 2 baytı yine yığında kalır ve IF eski hâline dönmez. Bu yüzden kural kesindir: ISR'ler her zaman IRET ile biter.
ISR'yi PROC/ENDP içinde yazıp sonuna alışkanlıkla RET koymak. Assembler buna itiraz etmez, çünkü RET geçerli bir komuttur. Hata ancak çalışma anında, çoğu zaman ISR'den çok sonra, yanlış bir dönüş adresi ya da bir daha gelmeyen kesmeler biçiminde ortaya çıkar. Belirtinin nedenden uzak olması bu hatayı bulmayı zorlaştırır.
5.1.4İstisnalar
İstisnalar işlemcinin bir komutu yürütürken karşılaştığı özel durumları programa bildirme yoludur. 8086'nın iki gerçek istisnası vardır: bölme hatası (INT 0) ve tek adım (INT 1). Kesme noktası (INT 3) ve taşma kesmesi (INTO ile INT 4) aslında komutla çağrılan yazılım kesmeleridir. Ancak bu ikisi de hata ayıklama ve hata yakalama işinde kullanıldığı için istisnalarla birlikte ele alınır. Intel'in 8086 kılavuzu bu dört kesmeyi INT n ile birlikte “iç kesmeler” başlığı altında toplar.
5.1.4.1Bölme hatası (INT 0)
DIV ve IDIV komutları bölümü sabit boyutlu bir yazmaca yazar. 8 bitlik bölmede bölünen AX'tedir, bölüm AL'ye, kalan AH'ye yazılır. 16 bitlik bölmede bölünen DX:AX'tedir, bölüm AX'e, kalan DX'e yazılır. Bölüm hedef yazmaca sığmazsa doğru bir sonuç üretmenin yolu yoktur. İşlemci bu durumda sonucu yazmaz ve tip 0 kesmesini üretir.
Bölme hatası (divide error), DIV ya da IDIV komutunda bölen sıfır olduğunda ya da bölüm hedef yazmaca sığmadığında işlemcinin ürettiği tip 0 kesmesidir. Hedef 8 bitlik bölmede AL, 16 bitlik bölmede AX'tir.
Tanımdaki ikinci koşul çoğu zaman gözden kaçar. Sıfıra bölme olmadan da bölme hatası oluşabilir.
AX = 1000 (03E8h) ve BL = 2 iken DIV BL çalışıyor. Ne olur? Aynı bölme nasıl hatasız yapılır?
- 8 bitlik bölme: AX / BL = 1000 / 2 = 500. Bölüm AL'ye yazılacaktır.
- AL en çok 255 tutar. 500 > 255 olduğu için bölüm sığmaz. Bölen sıfır olmadığı hâlde işlemci INT 0 üretir.
- Çözüm 16 bitlik bölmedir: DX = 0, AX = 1000, BX = 2 ve
DIV BX. Bölünen DX:AX = 0000:03E8h = 1000'dir. Bölüm 500 = 01F4h AX'e, kalan 0 DX'e yazılır. AX en çok 65 535 tutar ve 500 rahatça sığar.
Daha küçük bir sınır örneği: AX = 01FEh (510) / 2 = 255 = FFh sığar; AX = 0200h (512) / 2 = 256 sığmaz ve INT 0 üretir. Bu değerler emu8086'da çalıştırılarak sağlanmıştır.
Kural kısaca şöyle söylenebilir: 8 bitlik DIV'de AH ≥ BL ise, yani bölünenin yüksek yarısı bölenden küçük değilse, bölüm 255'i aşar ve hata oluşur. Örnekte AH = 03h, BL = 02h'dir ve 03h ≥ 02h olduğu için hata kaçınılmazdır. 16 bitlik DIV için koşul DX ≥ bölendir. Bu sınama bölmeden önce bir CMP ile yapılabilir.
İşaretli bölme IDIV'de sınırlar işaretli aralıktır. 8086'da 8 bitlik IDIV bölümü −127 ile +127 arasında olmalıdır; 16 bitlik IDIV'de aralık −32 767 ile +32 767'dir. Bu sınır, ikiye tümleyen gösterimde mümkün olan en küçük değeri (−128 ya da −32 768) dışarıda bırakır. Örneğin AX = −128 (FF80h) ve BL = 1 iken IDIV BL bölümü −128 bulur ve 8086'da yine INT 0 üretir. Sonraki işlemciler bu en küçük değeri kabul eder.
Dönüş adresi. 8086'nın bölme hatasında yığına ittiği IP, DIV'den sonraki komutun adresidir. Yani IRET programı bölmeyi atlayarak sürdürür. Bölme hiç yapılmamış gibi devam edilir; hedef yazmaçlar eski değerlerini korur. 80286 ve sonraki işlemcilerde bu davranış değiştirilmiş ve hatalı komutun kendi adresi itilir hâle gelmiştir. Böylece hata yordamı nedeni ortadan kaldırıp komutu yeniden deneyebilir. 8086 için yazılmış bir hata yordamı ise bu imkâna sahip değildir; ancak sonucun geçersiz olduğunu bir yolla programa bildirebilir.
DOS ne yapar? Bir program başlarken INT 0'ın vektörü DOS'un kendi yordamını gösterir. Bu yordam ekrana “Divide overflow” yazar ve programı sonlandırır. Program kendi yordamını kurarak bu davranışı değiştirebilir. Bunun en sade biçimi aşağıdaki izlemede görülür.
Bölme hatasını yalnız “sıfıra bölme” olarak düşünmek ve yalnız bölenin sıfır olup olmadığını sınamak. Bölünen büyük, bölen küçük olduğunda bölüm hedefe sığmaz ve aynı kesme oluşur. Bölmeden önce hem bölenin sıfır olmadığını hem de bölümün sığacağını (8 bitte AH < bölen, 16 bitte DX < bölen) denetlemek INT 0'ı baştan önler.
5.1.4.2Kendi INT 0 yordamımız: kod izleme
Aşağıdaki program tip 0'ın vektörünü kendi yordamına yönlendirir ve bilerek sıfıra böler. Yordam AX'e FFFFh yazarak programa “sonuç geçersiz” işareti bırakır.
org 100h
mov ax, 0
mov es, ax ; ES = 0: IVT
mov word ptr es:[0*4], offset hata
mov word ptr es:[0*4+2], cs
mov ax, 100
mov bl, 0
div bl ; INT 0!
mov dl, al ; dönüş buraya
ret
hata:
mov ax, 0FFFFh ; hata işareti
iretİlk iki satır ES'yi 0000h yapar. es:[0*4] tip 0'ın vektörünün IP yarısıdır, yani tablonun en başıdır. Üçüncü satır buraya hata etiketinin bağıl konumunu, dördüncü satır 0000:0002h'ye CS'yi yazar. Programdan önce vektör, öykünücünün varsayılan yordamını gösteriyordu; bu izlemede eski değer F000:1000h olarak alınmıştır. Bu kısa örnekte eski vektör saklanmaz. Gerçek bir programda saklanır ve çıkıştan önce geri yüklenir.
Bağıl konumları sayalım: mov ax, 0 3, mov es, ax 2, iki vektör yazması 7 ve 5, mov ax, 100 3, mov bl, 0 2, div bl 2, mov dl, al 2 ve ret 1 bayttır. Böylece div bl 0116h'de, mov dl, al 0118h'de ve hata etiketi 011Bh'dedir.
| Satır | Komut | AX | BL | DL | CS:IP (sonraki) | SP | IF | Vektör 0 / yığın |
|---|---|---|---|---|---|---|---|---|
| 2 | mov ax, 0 | 0000h | 00h | 00h | 0700:0103 | FFFEh | 1 | F000:1000 |
| 3 | mov es, ax | 0000h | 00h | 00h | 0700:0105 | FFFEh | 1 | F000:1000 |
| 4 | mov … [0*4], offset hata | 0000h | 00h | 00h | 0700:010C | FFFEh | 1 | F000:011B |
| 5 | mov … [0*4+2], cs | 0000h | 00h | 00h | 0700:0111 | FFFEh | 1 | 0700:011B |
| 6 | mov ax, 100 | 0064h | 00h | 00h | 0700:0114 | FFFEh | 1 | 0700:011B |
| 7 | mov bl, 0 | 0064h | 00h | 00h | 0700:0116 | FFFEh | 1 | 0700:011B |
| 8 | div bl → INT 0 | 0064h | 00h | 00h | 0700:011B | FFF8h | 0 | [FFF8h] = 0118h, [FFFAh] = 0700h, [FFFCh] = F202h |
| 12 | mov ax, 0FFFFh | FFFFh | 00h | 00h | 0700:011E | FFF8h | 0 | yığın aynı |
| 13 | iret | FFFFh | 00h | 00h | 0700:0118 | FFFEh | 1 | yığın boşaldı |
| 9 | mov dl, al | FFFFh | 00h | FFh | 0700:011A | FFFEh | 1 | — |
| 10 | ret | FFFFh | 00h | FFh | 0700:0000 | 0000h | 1 | DOS'a dönüş |
Tablo 5.1.10'un satırları üç evrede okunur. Satır 4–5 vektörü iki parçada değiştirir. Satır 4'ten sonra vektörün yarısı yeni, yarısı eskidir: F000:011B. Bu ara durum hiçbir yordamı göstermez. Bu programda tip 0'ı yalnız programın kendi DIV'i tetikleyebildiği için ara durum zararsızdır. Donanım kesmelerinin vektörleri değiştirilirken ise bu ara durum ciddi bir tehlikedir.
Satır 8'de div bl sıfıra böler. İşlemci bölümü yazmaz ve tip 0 kesmesini başlatır. Yığına FLAGS (F202h), CS (0700h) ve dönüş IP'si itilir. İtilen IP, DIV'in adresi 0116h değil, DIV'den sonraki komutun adresi 0118h'dir. SP = FFF8h ve IF = 0 olur, CS:IP ← 0700:011B. AX değişmemiştir; bölünen 0064h olduğu gibi durur.
div bl INT 0'ı tetikledikten sonra: tip 0'ın vektörü 0700:011Bh, yığında dönüş IP'si 0118h, CS ve FLAGS; IF = 0 ve CS:IP yordamın ilk komutunda.Satır 12–13 yordamdır. mov ax, 0FFFFh sonuç yazmacına geçersiz bir işaret koyar. iret IP = 0118h, CS = 0700h ve FLAGS = F202h'yi geri alır; SP = FFFEh ve IF = 1 olur. DIV yeniden çalışmaz. Satır 9'da ana program mov dl, al ile AL'yi DL'ye alır ve DL = FFh'den hatayı anlar. Son satırdaki ret, .COM programlarında yığının tepesindeki 0000h'yi alır ve PSP'nin başındaki INT 20h ile DOS'a döner.
Bu programdaki işaretleme yönteminin bir zayıflığı vardır. FFh, gerçek bir bölmenin de sonucu olabilir: 255 / 1 = 255. Gerçek programlarda hata bilgisi bunun yerine ayrı bir değişkende ya da bir bayrakta, örneğin CF'de döndürülür. CF'yi değiştirmek için ISR'nin yığındaki FLAGS kopyasını değiştirmesi gerekir; çünkü IRET o kopyayı geri yükler.
5.1.4.3INTO ve taşma kesmesi (INT 4)
İşaretli toplama ve çıkarmada sonuç işaretli aralığa sığmazsa OF = 1 olur. İşlemci bu durumda kendiliğinden bir kesme üretmez; OF'yi sınamak programın işidir. INTO (interrupt on overflow) bu sınamayı tek baytta yapar.
INTO, OF = 1 ise tip 4 kesmesini çağıran, OF = 0 ise hiçbir şey yapmayan tek baytlık (CEh) komuttur. İşaretli bir toplama ya da çıkarmanın hemen ardından yazılır.
AL = 64h, BL = 32h iken ADD AL, BL ve ardından INTO çalışıyor. Sonra aynı işlem BL = F6h ile tekrarlanıyor. Her iki durumda AL, OF, CF nedir ve INT 4 çağrılır mı?
- 64h + 32h = 96h. İşaretsiz yorumda 100 + 50 = 150 ≤ 255; elde yok, CF = 0. İşaretli yorumda iki pozitif sayının toplamı +150'dir ve +127'yi aşar. 96h'nin en yüksek biti 1'dir; sonuç −106 gibi görünür. OF = 1 olur ve INTO, INT 4'ü çağırır. AL = 96h olarak kalır; INTO sonucu düzeltmez.
- 64h + F6h = 15Ah; AL = 5Ah ve CF = 1. İşaretli yorumda +100 + (−10) = +90 doğrudur; farklı işaretli iki sayının toplamı taşamaz: OF = 0. INTO hiçbir şey yapmaz. CF = 1 olması INTO'yu etkilemez.
İki durum emu8086'da çalıştırıldığında da aynı sonuç alınır: birincide INT 4 yordamı çalışır, ikincide çalışmaz.
INTO'nun maliyeti küçüktür: OF = 0 iken yalnız 4 saat turu sürer. Bu yüzden işaretli aritmetik yapan bir program her önemli işlemden sonra bir INTO koyarak taşmaları merkezî bir yordamda yakalayabilir. Pratikte ise DOS, INT 4 için anlamlı bir yordam kurmaz. Varsayılan vektör çoğu zaman yalnız IRET'ten oluşan boş bir yordamı gösterir. INTO'yu kullanacak program kendi INT 4 yordamını kurmalıdır.
INTO'nun CF'ye baktığını sanmak. INTO yalnız OF'ye bakar; işaretsiz taşma (CF = 1) INTO'yu tetiklemez. İşaretsiz sayılarla çalışan bir program taşmayı JC ile yakalar, INTO ile değil.
5.1.4.4Tek adım (INT 1)
Bir programdaki hatayı bulmanın en etkili yolu onu komut komut yürütüp her komuttan sonra yazmaçlara bakmaktır. 8086 bu işi donanımla destekler. FLAGS yazmacının bit 8'i olan tuzak bayrağı TF = 1 iken işlemci her komuttan sonra tip 1 kesmesini üretir. INT 1 yordamı, yani hata ayıklayıcının bir parçası, yazmaçları ekrana yazar ve kullanıcının bir tuşa basmasını bekler. Sonra IRET ile programa döner, program bir komut daha yürütür ve aynı döngü sürer.
TF'yi doğrudan değiştiren bir komut yoktur. CLC/STC'nin, CLD/STD'nin ve CLI/STI'nin TF için karşılığı bulunmaz. TF yığın üzerinden değiştirilir: FLAGS yığına itilir, bir yazmaca alınır, bit 8 kurulur ve değer yeniden FLAGS'e yüklenir.
PUSHF
POP AX
OR AH, 01h ; bit 8 = TF
PUSH AX
POPF ; TF = 1Başlangıçta FLAGS = F202h ise PUSHF ve POP AX sonrasında AX = F202h olur. AH'nin bit 0'ı FLAGS'in bit 8'idir; OR AH, 01h AX'i F302h yapar. POPF bu değeri FLAGS'e yükler ve TF = 1 olur. TF'yi sıfırlamak için aynı dizi AND AH, 0FEh ile yazılır.
Burada ince ama önemli bir nokta vardır. INT 1 yordamı da bir komut dizisidir. TF = 1 iken bu yordamın her komutu da INT 1 üretseydi, yordam kendini sonsuza kadar keserdi. Bunu önleyen, INT n adımlarının ikincisidir: işlemci kesmeye girerken FLAGS'i yığına itip TF'yi sıfırlar. INT 1 yordamı TF = 0 ile çalışır ve kendi komutları adım adım kesilmez. Yordam IRET ile döndüğünde yığındaki FLAGS kopyası TF = 1'i geri yükler ve izleme sürer.
Tek adım kesmesinin önceliği bütün kesmelerin en düşüğüdür (Tablo 5.1.12). Aynı komut sınırında başka bir istek de bekliyorsa önce o hizmet görür.
5.1.4.5Kesme noktası (INT 3)
Tek adım, uzun bir programın her komutunda durmak için yavaştır. Çoğu zaman program belirli bir noktaya kadar tam hızda çalışsın, orada dursun istenir. Bu noktaya kesme noktası (breakpoint) denir. 8086'da kesme noktaları INT 3 ile kurulur.
INT 3'ün tek baytlık özel bir kodlaması vardır: CCh. Genel INT n biçimiyle yazılsaydı iki baytlık CD 03 olurdu. Hata ayıklayıcı bir kesme noktasını dört adımda kurar ve kaldırır:
- Durulacak komutun ilk baytını saklar ve yerine CCh yazar.
- Program tam hızda çalışır. O adrese gelindiğinde CCh, yani INT 3 yürütülür. Yığına FLAGS, CS ve INT 3'ten sonraki adres itilir; hata ayıklayıcının INT 3 yordamı yazmaçları gösterir.
- Hata ayıklayıcı saklanan baytı geri yazar ve yığındaki dönüş IP'sini 1 azaltır. Böylece dönüş adresi, CCh'nin bulunduğu, yani asıl komutun başladığı adres olur.
- IRET ile asıl komut baştan, bozulmamış hâliyle yürütülür.
Tek baytlık kodlamanın nedeni üçüncü adımdadır. 8086'nın en kısa komutları tek bayttır: INC AX, PUSH BX, CLC gibi. İki baytlık CD 03 böyle bir komutun yerine yazılsaydı, ikinci bayt bir sonraki komutun ilk baytını bozardı. Program o sonraki komuta bir atlamayla ulaşırsa, artık bozuk bir komut yürütülürdü. CCh ise her komutun ilk baytının yerine sığar ve yalnız o komutu etkiler. Dönüş IP'sinin 1 azaltılması da yine INT 3'ün tek bayt olmasına dayanır.
emu8086'nın “single step” düğmesi aynı işi öykünücünün içinde yapar. Gerçek bir 8086 sisteminde ise DOS'un DEBUG programı ve benzeri araçlar INT 1 ve INT 3 vektörlerine kendi yordamlarını kurarak çalışır.
Hata ayıklayıcı altında çalışan bir programın kodu belleğe döküldüğünde CCh baytlarını programın kendi komutları sanmak. Kesme noktası kurulu adreslerde bellekte geçici olarak CCh durur. Program kendi kodunu okuyor ya da sağlama toplamı hesaplıyorsa, hata ayıklayıcı altında farklı sonuç bulabilir.
5.1.5Donanım kesmeleri
5.1.5.1INTR ve NMI pinleri
Dış aygıtlar 8086'ya kesme isteğini iki pin üzerinden iletir. İki pinin davranışı, tetiklenme biçiminden tip numarasının belirlenmesine kadar farklıdır.
INTR (pin 18) maskelenebilir kesme isteği girişidir. Düzey tetiklemelidir: işlemci pini her komutun son saat turunda yoklar ve pin o anda 1 ise isteği görür. IF = 0 ise istek dikkate alınmaz; pin 1'de kalmaya devam ederse IF = 1 olduğu anda görülür. Tip numarası işlemcinin içinde sabit değildir. İşlemci isteği kabul edince INTA̅ (pin 24, kesme onayı) çıkışından iki darbe üretir ve tip numarasını veri yolundan okur. Bu düzen, tek bir INTR pininin arkasına birçok aygıt bağlamayı mümkün kılar; her aygıta farklı bir tip numarası verilebilir.
NMI (pin 17) maskelenemez kesme girişidir. Yükselen kenarla tetiklenir: pinin 0'dan 1'e geçişi işlemcinin içindeki bir tutucuya kaydedilir ve istek kaybolmaz. IF'ye bakılmaz; CLI ile kapatılamaz. Tip numarası her zaman 2'dir, bu yüzden INTA döngüsü gerekmez ve vektör doğrudan 00008h'den okunur. IBM PC'de NMI bellek eşlik hatasını bildirmek için kullanılır. Güç kaybını sezen devreler de NMI ile işlemciye, kaynak tamamen kesilmeden önce önemli verileri saklaması için birkaç milisaniyelik süre tanır.
| INTR | NMI | |
|---|---|---|
| Pin | 18 | 17 |
| Tetiklenme | düzey (komut sonunda yoklanır) | yükselen kenar (içeride tutulur) |
| IF = 0 iken | bekletilir | kabul edilir |
| Tip numarası | INTA ile veri yolundan (PC'de 08h–0Fh) | sabit 2 |
| Kullanım | klavye, zamanlayıcı, disk, seri port | eşlik hatası, güç kaybı |
Düzey tetiklemenin bir sonucu vardır. Aygıt, işlemci isteği kabul edene kadar INTR'yi 1'de tutmalıdır. İstek çok kısa bir darbe olarak gelir ve işlemci o anda IF = 0 ile çalışıyorsa, istek kaybolur. PC'de bu sorunu aygıtlarla işlemci arasındaki 8259A çözer. 8259A gelen istekleri kendi yazmacında saklar ve INTR'yi istek hizmet görene kadar tutar.
NMI'ın vektörünün de INTA ile okunduğunu düşünmek. INTA döngüsü yalnız INTR içindir. NMI'ın tipi sabittir (2) ve işlemci bunu dışarıya sormaz. Aynı biçimde INT 0, INT 1, INT 3 ve INT 4'ün tipleri de sabittir; dış donanımla ilgisi yoktur.
5.1.5.2INTR kabul süreci
Bir tuşa basıldığı andan klavye ISR'sinin ilk komutunun yürütülmesine kadar geçen olaylar zincirini adım adım izleyelim. PC'de klavye denetleyicisi 8259A'nın IRQ1 girişine bağlıdır.
- Klavye denetleyicisi IRQ1 hattını etkinleştirir. 8259A isteği kaydeder ve maske izin veriyorsa INTR pinini 1 yapar.
- 8086 o anda yürüttüğü komutu bitirir. Kesmeler komut ortasında kabul edilmez; işlemci INTR'yi yalnız komut sınırlarında yoklar.
- IF = 0 ise istek bekletilir; INTR 1'de kalır. IF = 1 ise istek kabul edilir.
- İşlemci iki INTA̅ darbesi üretir. Birinci darbe 8259A'ya “hazırlan” der. İkinci darbede 8259A tip numarasını veri yolunun D0–D7 hatlarına koyar; PC'de klavye için bu 09h'dir.
- Bundan sonrası INT n ile aynıdır: FLAGS, CS, IP yığına itilir; IF = TF = 0 yapılır; CS:IP ← [n × 4], yani [0000:0024h].
- ISR tuşu okuyup işini bitirince 8259A'ya EOI (kesme sonu) gönderir ve IRET ile döner.
Şekil 5.1.9'daki dördüncü adım, donanım kesmesini yazılım kesmesinden ayıran tek adımdır. INTA döngüsü bir okuma yol çevrimine benzer: işlemci adres yoluna anlamlı bir adres koymaz, RD̅ yerine INTA̅'yı etkinleştirir ve veri yolundaki baytı alır. İki darbe arasında maksimum kipte LOCK̅ sinyali de etkin tutulur; böylece başka bir yol yöneticisi bu sırada yola giremez. Tip numarası alındıktan sonra işlemci, sanki programda bir INT 09h komutu varmış gibi davranır.
İkinci adımdaki kural, yani kesmenin yalnız komut sınırında kabul edilmesi, ISR tasarımının temelidir. Bir komut yarım kalmaz. Örneğin ADD [BX], AX belleği okuyup yazarken araya kesme girmez; ISR belleği her zaman ya komuttan önceki ya da komuttan sonraki hâliyle görür. Ancak iki ayrı komutla yapılan bir iş, iki komut arasında kesilebilir. ISR'nin ve ana programın paylaştığı veriler için bu durum dikkate alınmalıdır.
5.1.5.3Öncelik ve PC kesme haritası
Birden çok kesme isteği aynı komut sınırında bekliyorsa işlemci hangisine önce bakacağına sabit bir sırayla karar verir. Tablo 5.1.12 8086'nın öncelik sırasını verir.
| Sıra | Kaynak | Gerekçe |
|---|---|---|
| 1 (en yüksek) | bölme hatası, INT n, INTO | yürütülen komutun kendisinden doğar; o komutun parçasıdır |
| 2 | NMI | acil donanım olayı; maskelenemez |
| 3 | INTR | sıradan donanım isteği; IF ile bekletilebilir |
| 4 (en düşük) | tek adım (TF) | hata ayıklama; beklemesi bir şey kaybettirmez |
Sıralamanın mantığı açıktır. Bir komutun kendi ürettiği kesme, o komutun yürütülmesinin parçasıdır ve önce ele alınır. Dış istekler arasında NMI, programın kapatamayacağı acil bir olay olduğu için INTR'nin önündedir. Tek adım ise insanın yazmaçlara bakması içindir; birkaç mikrosaniye beklemesi hiçbir şey kaybettirmez.
Bu öncelik, işlemcinin kendi girişleri arasındadır. INTR'nin arkasındaki sekiz aygıtın birbirine göre önceliğini ise 8259A belirler. PC'nin standart ayarında IRQ0 en yüksek, IRQ7 en düşük önceliklidir. Tablo 5.1.13 IBM PC'nin donanım kesmelerini verir. BIOS açılışta 8259A'yı IRQ0'a tip 08h verecek biçimde programlar; böylece IRQ n'in tipi 08h + n olur.
| IRQ | Aygıt | Tip | Vektör adresi |
|---|---|---|---|
| 0 | zamanlayıcı (18,2 Hz) | 08h | 00020h |
| 1 | klavye | 09h | 00024h |
| 3 / 4 | seri portlar (COM2 / COM1) | 0Bh / 0Ch | 0002Ch / 00030h |
| 6 | disket | 0Eh | 00038h |
| 7 | yazıcı | 0Fh | 0003Ch |
Zamanlayıcının 18,2 Hz'lik frekansı ilginç bir hesaptan gelir. PC'nin 8253 zamanlayıcı yongası 1 193 182 Hz'lik bir saatle beslenir. Sayaç en büyük bölme değeri olan 65 536 ile çalıştırıldığında çıkış frekansı 1 193 182 / 65 536 ≈ 18,2 Hz olur. Bu da yaklaşık her 55 ms'de bir INT 08h demektir. BIOS bu kesmeyi günün saatini saymak için kullanır.
Klavye örneği, donanım kesmesi ile yazılım kesmesi arasındaki iş bölümünü de gösterir. Bir tuşa basılınca önce donanım kesmesi INT 09h çalışır. Bu ISR klavye denetleyicisinden tuşun kodunu okur ve BIOS'un klavye tamponuna koyar. Program ise tuşu okumak istediğinde yazılım kesmesi INT 16h'yi çağırır; INT 16h bu tampondan okur. Böylece kullanıcı program meşgulken de tuşlara basabilir. Tuşlar tamponda bekler ve program hazır olduğunda sırayla alınır. Tamponun ve INT 16h hizmetlerinin ayrıntısı 5.2 Kesme Fonksiyonları notunun konusudur.
5.1.5.48259A kesme denetleyicisi
8086'nın tek bir INTR pini vardır, PC'nin ise en az altı kesme kaynağı. Bu kaynakları tek pine toplayan, aralarında öncelik sıralaması yapan ve işlemciye doğru tip numarasını veren yonga 8259A'dır.
Programlanabilir kesme denetleyicisi (programmable interrupt controller, 8259A), sekiz kesme isteği girişini (IRQ0–IRQ7) öncelik sırasına koyan, maskeleyen ve işlemcinin INTA döngüsünde ilgili tip numarasını veri yoluna koyan yongadır. PC'de port 20h ve 21h adreslerindedir.
8259A'nın içinde, her biri sekiz bitlik ve her biti bir IRQ'ya karşılık gelen üç yazmaç vardır (Şekil 5.1.10).
- IRR (interrupt request register, kesme istek yazmacı): gelmiş ama henüz hizmet görmemiş istekleri tutar. Bir IRQ hattı etkinleşince ilgili bit 1 olur.
- IMR (interrupt mask register, kesme maske yazmacı): her IRQ için bir maske biti tutar. Bit 1 ise o hattın isteği işlemciye iletilmez. IF bütün INTR girişini kapatırken IMR tek tek hatları kapatır. PC'de port 21h'den okunur ve yazılır.
- ISR (in-service register, hizmette yazmacı): o an hizmet görmekte olan IRQ'yu işaretler. INTA sırasında seçilen isteğin biti IRR'den ISR'ye geçer. EOI gelene kadar bu bit 1 kalır ve aynı ya da daha düşük öncelikli IRQ'lar bekletilir.
Üçüncü yazmacın adı ISR'dir ve kesme hizmet yordamının kısaltmasıyla aynıdır. Karışıklığı önlemek için bu notta yazmaçtan söz edilirken “ISR yazmacı” denir.
EOI. ISR yazmacındaki bit kendiliğinden silinmez. Hizmet yordamı işini bitirince 8259A'ya bunu bildirmelidir. Bu bildirime kesme sonu (end of interrupt, EOI) denir. PC'de iki komutla yapılır:
MOV AL, 20h ; özel olmayan EOI komutu
OUT 20h, AL ; 8259A'nın komut portunaBu komut, ISR yazmacındaki en yüksek öncelikli 1 bitini siler. Port numarası ile komut değerinin ikisinin de 20h olması tesadüftür. EOI unutulursa 8259A o IRQ'nun hâlâ hizmette olduğunu sanır. Aynı ve daha düşük öncelikli IRQ'lar bir daha işlemciye ulaşmaz. Örneğin zamanlayıcı ISR'sinde EOI unutulursa, IRQ0 en yüksek öncelikli olduğu için klavye, disk ve seri port kesmelerinin hepsi durur. Sistem donmuş gibi görünür.
Basamaklama. Sekiz giriş bazen yetmez. 8259A'lar basamaklanabilir (cascade): bir ana 8259A'nın her girişine bir yardımcı 8259A bağlanırsa 8 × 8 = 64 IRQ'ya çıkılır. IBM PC/AT ikinci bir 8259A'yı ana yonganın IRQ2 girişine bağlamıştır. İkinci yonganın girişleri IRQ8–IRQ15 adını alır ve tip 70h–77h'ye eşlenir. AT'de IRQ8 gerçek zamanlı saat, IRQ14 sabit diskin kesmesidir.
IMR'de bir bitin 1 olmasını “kesmeye izin var” diye okumak. IMR bir maske yazmacıdır: 1 engeller, 0 izin verir. IF'de ise durum tersidir: IF = 1 izin verir. İki kuralı karıştırmamak için IMR'deki 1'i “maskeli”, IF'deki 1'i “açık” diye okumak yararlıdır.
5.1.5.5IMR ile maskeleme: kod izleme
Bazen bir programın yalnız tek bir aygıtın kesmesini kısa bir süre engellemesi gerekir. Örneğin klavye tamponunu doğrudan değiştiren bir kod parçası, o sırada klavye ISR'sinin çalışmasını istemez; ama zamanlayıcının saymaya devam etmesini ister. CLI burada fazla kabadır, çünkü bütün INTR girişini kapatır. Doğru araç IMR'dir.
org 100h
in al, 21h ; IMR'yi oku
or al, 00000010b ; IRQ1 maskele
out 21h, al
mov cx, 3 ; kritik iş
in al, 21h
and al, 11111101b ; IRQ1 aç
out 21h, al
retProgram oku–değiştir–yaz kalıbını iki kez uygular. IMR'nin yalnız bir biti değiştirilmek istenir; öbür yedi bit başka programların, BIOS'un ya da işletim sisteminin ayarıdır. Bu yüzden IMR önce okunur, istenen bit OR ile kurulur ya da AND ile silinir ve sonuç geri yazılır. Doğrudan MOV AL, 02h / OUT 21h, AL yazmak IRQ1'i maskelerdi; ama öbür bütün IRQ'ların maskesini kaldırırdı.
İzlemede IMR'nin başlangıç değeri örnek olarak B8h alınmıştır: 1011 1000b. Bu değerde IRQ3, 4, 5 ve 7 maskelidir; IRQ0 (zamanlayıcı), IRQ1 (klavye), IRQ2 ve IRQ6 (disket) açıktır.
| Satır | Komut | AL (ikili) | AL | CX | IMR | IRQ1 |
|---|---|---|---|---|---|---|
| 2 | in al, 21h | 1011 1000 | B8h | 0000h | B8h | açık |
| 3 | or al, 00000010b | 1011 1010 | BAh | 0000h | B8h | açık |
| 4 | out 21h, al | 1011 1010 | BAh | 0000h | BAh | maskeli |
| 5 | mov cx, 3 | 1011 1010 | BAh | 0003h | BAh | maskeli |
| 6 | in al, 21h | 1011 1010 | BAh | 0003h | BAh | maskeli |
| 7 | and al, 11111101b | 1011 1000 | B8h | 0003h | BAh | maskeli |
| 8 | out 21h, al | 1011 1000 | B8h | 0003h | B8h | açık |
| 9 | ret | 1011 1000 | B8h | 0003h | B8h | açık |
Tablo 5.1.14'ün okunuşu şöyledir. Satır 2'de IMR okunur: AL = B8h. Satır 3'te or al, 02h yalnız bit 1'i kurar: 1011 1000 OR 0000 0010 = 1011 1010 = BAh. Diğer bitler korunur. IMR henüz değişmemiştir; değişen yalnız AL'dir. Satır 4'te out değeri 8259A'ya yazar ve IMR = BAh olur. Bu andan sonra IRQ1 isteği INTR'ye ulaşmaz; IF ve öbür IRQ'lar etkilenmez.
out 21h, al sonrasında AL ve IMR bitleri: ikisi de BAh. Maskeli bitler (1) IRQ3, 4, 5, 7 ve yeni maskelenen IRQ1'dir; IRQ0, IRQ2 ve IRQ6 açıktır.Satır 5 kesilmemesi gereken işin yerini tutar. Bu sırada klavyeye basılırsa istek kaybolmaz: 8259A isteği IRR'de saklar. Satır 6–8 maskeyi aynı kalıpla kaldırır. and al, 0FDh ile 1111 1101b maskesi yalnız bit 1'i siler: 1011 1010 AND 1111 1101 = 1011 1000 = B8h. out ile IMR yeniden B8h olur. IRR'de bekleyen klavye isteği varsa şimdi INTR'ye ulaşır ve hizmet görür. Satır 9'da program döner; maske ilk hâline gelmiştir ve başkasının bitlerine dokunulmamıştır.
IMR'nin CLI'dan farkı bu izlemede açıkça görülür. CLI bütün maskelenebilir kesmeleri bekletir; zamanlayıcının darbeleri de beklerken saat geri kalabilir. IMR ise yalnız seçilen aygıtı bekletir. Bedeli, IMR'ye erişimin iki IN/OUT komutu gerektirmesi ve bir G/Ç portuna yazmak olduğu için CLI'dan daha yavaş olmasıdır.
IMR'ye okumadan yazmak: MOV AL, 02h ve OUT 21h, AL. Bu iki satır IRQ1'i maskeler ama IRQ3, 4, 5 ve 7'nin maskesini kaldırır. O hatlara ISR'si kurulmamış bir aygıt bağlıysa, gelen ilk istek işlemciyi anlamsız bir vektöre gönderir.
5.1.5.6Kesmenin bekletildiği anlar
8086 kesmeyi yalnız komut sınırlarında kabul eder. Bazı durumlarda bu sınır bilerek bir komut ileri kaydırılır. Tablo 5.1.15 bu durumları toplar.
| Durum | Davranış | Neden |
|---|---|---|
| komut ortası | istek, yürüyen komut bitene kadar bekler | komut yarım kalmasın |
| önekler (LOCK, REP, kesim öneki) | önekle komut arasında kesme kabul edilmez | önek komutun parçasıdır |
MOV SS, … ya da POP SS sonrası | bir sonraki komut da bitene kadar kesme gelmez | ardından gelen MOV SP, … güvenle tamamlansın |
STI sonrası | bir komut daha yürütülür, sonra kesme kabul edilir | STI / IRET ya da STI / HLT arasına kesme girmesin |
| REP'li dizgi komutu | turlar arasında kesilebilir; IRET'ten sonra kalan CX kadar sürer | uzun bir kopyalama gecikmeyi büyütmesin |
HLT ve WAIT | INTR ya da NMI işlemciyi uyandırır | bekleme kesmeyle biter |
SS yüklendikten sonra. Yığını taşımak için iki komut gerekir: MOV SS, AX ve MOV SP, 0FFFEh. İki komut arasında bir kesme gelseydi, işlemci FLAGS, CS ve IP'yi yeni SS ile eski SP'nin gösterdiği yere iterdi. Bu adres büyük olasılıkla başka bir verinin üzerindedir. 8086 bu yüzden bir kesim yazmacına yazan her komuttan sonra kesmeyi bir komut erteler. Bunun güvenli çalışması için MOV SP, MOV SS'nin hemen ardından yazılmalıdır.
STI'den sonra. STI kesmeleri hemen değil, kendisinden sonraki komut bittikten sonra açar. Bu küçük gecikme iki yaygın kalıbı güvenli kılar. Birincisi bir ISR'nin sonundaki STI / IRET dizisidir: yeni kesme, IRET bitmeden ve yığın boşalmadan gelemez. İkincisi STI / HLT dizisidir. Program “kesmeleri aç ve bir kesme gelene kadar bekle” demek ister. Kesme STI ile HLT arasına girseydi, ISR çalışıp dönerdi ve HLT bir sonraki kesmeye kadar gereksiz yere beklerdi.
REP'li dizgi komutları. REP MOVSB ile 60 000 baytlık bir kopyalama yüz binlerce saat turu sürer. Bütün bu süre boyunca kesmelerin beklemesi kabul edilemez. 8086 bu yüzden REP'li komutu turlar arasında kesebilir. Yığına itilen dönüş adresi, REP önekinin adresidir; SI, DI ve CX o ana kadarki ilerlemeyi tutar. IRET'ten sonra komut yeniden başlar ve kalan CX kadar tur atar. Bu düzenin 8086'ya özgü bir kusuru vardır: işlemci kesmeden dönüşte yalnız bir öneki hatırlar. REP ES: MOVSB gibi iki önekli bir komut kesilirse dönüşte kesim öneki kaybolur ve kopyalama yanlış kesimden sürer. Bu yüzden böyle bir satır CLI ile STI arasına alınır ya da kesim öneki gerektirmeyecek biçimde yazılır.
HLT ve WAIT. HLT işlemciyi durdurur. INTR (IF = 1 iken) ya da NMI işlemciyi uyandırır; ISR çalışır ve IRET, HLT'den sonraki komuta döner. Ana programı “bir şey olana kadar uyu” diye yazmanın en ekonomik yolu budur. WAIT, 8087 eş işlemcisinin işini bitirmesini bekler; bu bekleme sırasında gelen kesme de kabul edilir.
Yığını taşırken önce SP'yi, sonra SS'yi yüklemek ya da iki komut arasına başka bir komut koymak. Erteleme yalnız kesim yazmacına yazan komuttan sonraki tek komutu korur. Doğru sıra MOV SS, AX ve hemen ardından MOV SP, …'dur; en güvenlisi bu iki satırı ayrıca CLI/STI arasına almaktır.
5.1.5.7Kesme gecikmesi
Bir aygıt kesme istediğinde ISR'nin hemen çalışmaya başlaması beklenir. Gerçekte bu süre sıfır değildir ve bazı aygıtlar için sınırlıdır.
Kesme gecikmesi (interrupt latency), kesme isteğinin gelmesiyle ISR'nin ilk komutunun başlaması arasında geçen süredir.
Gecikme iki parçadan oluşur. Birincisi, istek geldiğinde yürümekte olan komutun bitmesini beklemektir. İkincisi kabul sürecinin kendisidir: INTA döngüleri, üç yığın yazması, iki vektör okuması ve kuyruğun yeniden doldurulması. Tablo 5.1.16 ilgili süreleri verir.
| İşlem | Saat turu | 5 MHz'de süre |
|---|---|---|
| INT n | 51 | 10,2 µs |
| INT 3 | 52 | 10,4 µs |
| INTO (OF = 1 / OF = 0) | 53 / 4 | 10,6 / 0,8 µs |
| INTR kabulü (iki INTA dahil) | 61 | 12,2 µs |
| IRET | 24 | 4,8 µs |
| 16 bitlik IDIV (yazmaç) | 165–184 | 33–36,8 µs |
5 MHz'lik bir 8086'da INTR geldiğinde 16 bitlik bir IDIV yeni başlamıştır. En kötü kesme gecikmesi kaç saat turu ve kaç µs'dir? İşlemci 8 MHz'de çalışsaydı ne olurdu?
- En kötü durumda yürüyen komut bitmelidir: IDIV en çok 184 tur.
- Ardından kabul süreci: 61 tur. Toplam 184 + 61 = 245 tur.
- 5 MHz'de bir saat turu 1 / 5 MHz = 200 ns'dir. 245 × 200 ns = 49 µs.
- 8 MHz'de bir tur 125 ns'dir: 245 × 125 ns ≈ 30,6 µs.
Program o sırada kesmeleri CLI ile kapatmışsa, kapalı geçen süre bu gecikmeye doğrudan eklenir. 100 µs'lik bir CLI–STI aralığı en kötü gecikmeyi 5 MHz'de yaklaşık 149 µs'ye çıkarır.
Bu sayıların anlamı aygıta göre değişir. Saniyede 18,2 kez gelen zamanlayıcı kesmesi için 49 µs önemsizdir; iki kesme arasında 55 ms vardır. Seri port ise farklıdır. 9600 bit/s hızla gelen karakterler arasında yaklaşık 1,04 ms, 115 200 bit/s hızda ise yaklaşık 87 µs vardır. Tek karakterlik tamponu olan bir seri port denetleyicisinde ISR, bir sonraki karakter gelmeden önceki karakteri okumalıdır. Gecikme ile ISR'nin süresinin toplamı bu aralığı aşarsa karakter kaybolur.
Bu hesap, ISR yazımının iki temel kuralını doğrudan verir. Birincisi, CLI–STI arası kısa tutulmalıdır; çünkü bu süre bütün aygıtların gecikmesine eklenir. İkincisi, ISR'nin kendisi de kısa tutulmalıdır. ISR çalışırken IF = 0'dır ve aynı ya da daha düşük öncelikli IRQ'lar EOI'ye kadar bekler. Uzun bir ISR, öbür bütün aygıtlar için uzun bir CLI ile aynı etkiyi yapar.
ISR'nin işlemciden aldığı pay da hesaplanabilir. Zamanlayıcı ISR'si 200 saat turu sürsün. Saniyede 18,2 kez çalıştığında 200 × 18,2 = 3640 tur harcar. 5 MHz'lik işlemcinin saniyedeki 5 000 000 turunun bu yalnız %0,07'sidir. Kesmenin yoklamaya göre üstünlüğü bu sayıda açıkça görülür.
5.1.6Kendi kesme hizmet yordamımız
5.1.6.1Vektörü kurmak: INT 90h
Bir kesme hizmet yordamı kurmak üç iş gerektirir: yordamı yazmak, kullanılmayan bir tip seçmek ve o tipin vektörüne yordamın adresini yazmak. Aşağıdaki program bu üç işi en sade biçimde yapar ve kendi kesmesini bir kez çağırır.
org 100h
mov ax, 0
mov es, ax ; ES = 0: IVT kesimi
cli ; kesmeleri kapat
mov word ptr es:[90h*4], offset isr ; IP
mov word ptr es:[90h*4+2], cs ; CS
sti ; kesmeleri aç
int 90h ; kendi kesmemiz
ret
isr:
mov ah, 0Eh
mov al, '*'
int 10h
iret ; RET değil!Programdaki her kararın bir gerekçesi vardır.
- Tip seçimi. 90h, PC'de BIOS'un ve DOS'un kullanmadığı bir tiptir. Vektörü 90h × 4 = 240h adresindedir; IP 0000:0240h–0241h'de, CS 0000:0242h–0243h'dedir.
- ES = 0. Vektör tablosu 0000h kesimindedir. DS programın kendi verisini gösterdiği için tabloya ES üzerinden,
es:önekiyle erişilir. Kesim yazmacına sabit yüklenemediğinden 0 önce AX'e konur. - CLI / STI. Vektör iki ayrı MOV ile, iki sözcük olarak yazılır. İlk MOV'dan sonra vektörün IP yarısı yeni, CS yarısı eskidir. O anda bu tipte bir kesme gelseydi, işlemci anlamsız bir adrese atlardı. CLI bu kısa aralıkta maskelenebilir kesmeleri bekletir. Tip 90h'yi yalnız programın kendisi çağırdığı için bu örnekte tehlike yoktur; ama donanım tiplerinde aynı kalıp zorunludur ve her vektör yazılışında uygulanması iyi bir alışkanlıktır.
- ISR'nin sonu. ISR, BIOS'un INT 10h teletype hizmetiyle ekrana bir yıldız yazar ve IRET ile döner. Bir ISR'nin içinden başka bir yazılım kesmesi çağrılabilir; iç içe kesmelerde yığına her seviye için 6 bayt daha eklenir.
Program çalıştırıldığında ekrana * yazılır ve program DOS'a döner. Bu kısa örnek eski vektörü saklamaz ve geri yüklemez. Program bittikten sonra tip 90h'nin vektörü, artık başka bir programın yükleneceği bir belleği göstermeye devam eder. Başka bir program INT 90h çağırırsa oradaki rastgele baytlar komut olarak yürütülür. Eski vektörü saklayıp geri yükleyen doğru biçim aşağıda ayrıca izlenir.
DOS altında vektörleri doğrudan bellekten yazmak yerine DOS'un iki hizmeti kullanılabilir: INT 21h, AH = 25h vektör atar, AH = 35h vektör okur. Bu hizmetler vektör yazılırken kesmeleri kendileri yönetir ve işletim sisteminin vektörlerden haberdar olmasını sağlar. Bu yüzden daha güvenli yoldur. Hizmetlerin yazmaç düzeni 5.2 Kesme Fonksiyonları notunda verilir; burada vektör tablosunun nasıl çalıştığını görmek için doğrudan yazma yöntemi kullanılmıştır.
5.1.6.2INT 60h ile kendi ISR'miz: kod izleme
Aynı düzenin biraz farklı bir biçimi, ISR'ye yazmaç üzerinden parametre geçirir. Ana program yazılacak karakteri AL'ye koyar; ISR yalnız AH'yi hazırlar ve INT 10h'yi çağırır. Kullanıcı programlarına ayrılmış 60h tipi kullanılır.
org 100h
mov ax, 0
mov es, ax ; ES = 0: IVT
cli
mov word ptr es:[60h*4], offset isr
mov word ptr es:[60h*4+2], cs
sti
mov al, '#'
int 60h ; kendi kesmemiz
ret
isr:
mov ah, 0Eh
int 10h
iretBağıl konumlar şöyledir: mov ax, 0 0100h, mov es, ax 0103h, cli 0105h, iki vektör yazması 0106h ve 010Dh, sti 0112h, mov al, '#' 0113h, int 60h 0115h, ret 0117h ve isr 0118h. Vektör 60h × 4 = 180h adresindedir.
| Satır | Komut | AX | ES | CS:IP (sonraki) | SP | FLAGS | IVT 0180h–0183h / yığın | Ekran |
|---|---|---|---|---|---|---|---|---|
| 2 | mov ax, 0 | 0000h | 0700h | 0700:0103 | FFFEh | F202h | ?? ?? ?? ?? | |
| 3 | mov es, ax | 0000h | 0000h | 0700:0105 | FFFEh | F202h | ?? ?? ?? ?? | |
| 4 | cli | 0000h | 0000h | 0700:0106 | FFFEh | F002h | ?? ?? ?? ?? | |
| 5 | mov … [60h*4], offset isr | 0000h | 0000h | 0700:010D | FFFEh | F002h | 18 01 ?? ?? | |
| 6 | mov … [60h*4+2], cs | 0000h | 0000h | 0700:0112 | FFFEh | F002h | 18 01 00 07 | |
| 7 | sti | 0000h | 0000h | 0700:0113 | FFFEh | F202h | 18 01 00 07 | |
| 8 | mov al, '#' | 0023h | 0000h | 0700:0115 | FFFEh | F202h | 18 01 00 07 | |
| 9 | int 60h | 0023h | 0000h | 0700:0118 | FFF8h | F002h | [FFF8h] = 0117h, [FFFAh] = 0700h, [FFFCh] = F202h | |
| 12 | mov ah, 0Eh | 0E23h | 0000h | 0700:011A | FFF8h | F002h | yığın aynı | |
| 13 | int 10h | 0E23h | 0000h | 0700:011C | FFF8h | F002h | yığın aynı | # |
| 14 | iret | 0E23h | 0000h | 0700:0117 | FFFEh | F202h | yığın boşaldı | # |
| 10 | ret | 0E23h | 0000h | 0700:0000 | 0000h | F202h | DOS'a dönüş | # |
Tablo 5.1.17 dört evreye ayrılır. Hazırlık (satır 2–3). ES başlangıçta, .COM programının öbür kesim yazmaçları gibi 0700h'dir. mov es, ax sonrasında ES = 0000h olur ve es:[180h] artık vektör tablosundaki 0000:0180h baytını gösterir. Tablonun bu dört baytının program öncesindeki değeri bilinmez; izlemede “??” ile gösterilmiştir.
Vektörü yazma (satır 4–7). cli IF'yi sıfırlar ve FLAGS F202h'den F002h'ye iner. Satır 5 IP yarısına offset isr = 0118h'yi yazar; bellekte küçük uçlu düzende 18 01 görülür. Satır 6 CS yarısına 0700h'yi yazar: 00 07. Vektör artık 0700:0118'dir. sti ile IF = 1 ve FLAGS = F202h olur. 8086'da FLAGS'in bit 1 ve 12–15 bitleri hep 1 olduğu için IF'nin tek başına değeri 0200h fark yaratır.
Kesme (satır 8–9). mov al, '#' AL'ye 23h koyar. Bu değer ISR'ye yazmaçla geçirilen parametredir. int 60h yığına FLAGS (F202h), CS (0700h) ve dönüş IP'si 0117h'yi iter. INT 60h 0115h'te başlar ve iki bayttır; dönüş adresi 0117h'dir. IF = 0 olur ve CS:IP, 0000:0180h'deki vektörden 0700:0118 olarak yüklenir.
int 60h yürütüldükten sonra: tip 60h'nin vektörü 0700:0118h, yığında dönüş IP'si 0117h, CS ve FLAGS; SP = FFF8h, IF = 0 ve CS:IP ISR'nin ilk komutunda.ISR ve dönüş (satır 12–14 ve 10). ISR'de mov ah, 0Eh AX'i 0E23h yapar. AH = 0Eh, BIOS'un teletype hizmetini seçer; AL'deki karakter yazılacaktır. int 10h ekrana # yazar. Bu, kesmenin içinden çağrılan ikinci bir kesmedir. BIOS kendi yığın kullanımını geri bırakır ve döndüğünde SP yine FFF8h'dir. iret IP = 0117h, CS = 0700h ve FLAGS = F202h'yi geri alır; SP = FFFEh ve IF = 1 olur. Ana programdaki ret DOS'a döner.
ISR'nin AX'i değiştirdiğine dikkat edilmelidir. Ana program kesmeden sonra AX = 0E23h ile devam eder. Bu programda AL bilerek ISR'ye parametre olarak verildiği için AX'in değişmesi bir sorun yaratmaz. Ama ISR bir donanım kesmesine bağlı olsaydı, ana program AH'nin aniden 0Eh olduğunu görürdü. Bu tür bir ISR kullandığı her yazmacı saklamalıdır.
5.1.6.3Vektörü sakla, değiştir, geri yükle: kod izleme
Önceki iki programın eksiği, tabloyu değiştirip öyle bırakmalarıdır. Doğru bir program önce eski vektörü saklar, işini bitirince geri yükler. Aşağıdaki program bunu tip 3 için yapar ve ISR'nin ana programın yazmaçlarını nasıl koruduğunu da gösterir.
org 100h
xor ax, ax
mov es, ax ; ES = 0: IVT
mov ax, offset isr3
xchg ax, es:[3*4] ; yeni IP ↔ eski
mov eski, ax
mov ax, cs
xchg ax, es:[3*4+2] ; yeni CS ↔ eski
mov eski+2, ax
mov ax, 1234h
int 3 ; bizim ISR
mov bx, eski
mov es:[3*4], bx ; geri yükle
mov bx, eski+2
mov es:[3*4+2], bx
ret
isr3: push ax
mov ax, 0E21h ; AH=0Eh, AL='!'
int 10h
pop ax
iret
eski dw ?, ?Programın yeni öğesi XCHG komutudur. xchg ax, es:[3*4], AX ile bellekteki sözcüğü tek komutta değiş tokuş eder: yeni IP belleğe yazılırken eski IP AX'e gelir. Okuma ve yazma iki ayrı MOV ile yapılsaydı, arada iki komutluk bir aralık kalırdı. XCHG ile eski değer okunmadan yeni değerin yazılması da, yazılmadan önce okunan değerin kaybolması da mümkün değildir. Eski vektör eski adlı iki sözcüklük değişkene saklanır. xor ax, ax AX'i sıfırlar; MOV'dan bir bayt kısadır ama bayrakları değiştirir: ZF = 1 ve PF = 1 olur. Bu yüzden FLAGS bu programda F246h'dir.
Programdan önceki tip 3 vektörü F000:1003h'dir: emu8086 her n tipi için varsayılan vektörü F000:(1000h + n) adresine kurar (tip 0 için F000:1000h). Gerçek bir PC'de bu değer BIOS'a ve yüklü hata ayıklayıcıya göre değişir; programın doğruluğu bu değere bağlı değildir. Bağıl konumlar: int 3 011Ch'de ve tek bayttır, isr3 0130h'de, eski 0138h'dedir.
| Satır | Komut | AX | BX | Tip 3 vektörü | CS:IP (sonraki) | SP | IF | Yığın / çıktı |
|---|---|---|---|---|---|---|---|---|
| 2–3 | xor ax, ax / mov es, ax | 0000h | 0000h | F000:1003 | 0700:0104 | FFFEh | 1 | — |
| 4 | mov ax, offset isr3 | 0130h | 0000h | F000:1003 | 0700:0107 | FFFEh | 1 | — |
| 5 | xchg ax, es:[3*4] | 1003h | 0000h | F000:0130 | 0700:010C | FFFEh | 1 | — |
| 6 | mov eski, ax | 1003h | 0000h | F000:0130 | 0700:010F | FFFEh | 1 | eski = 1003h |
| 7 | mov ax, cs | 0700h | 0000h | F000:0130 | 0700:0111 | FFFEh | 1 | — |
| 8 | xchg ax, es:[3*4+2] | F000h | 0000h | 0700:0130 | 0700:0116 | FFFEh | 1 | — |
| 9 | mov eski+2, ax | F000h | 0000h | 0700:0130 | 0700:0119 | FFFEh | 1 | eski+2 = F000h |
| 10 | mov ax, 1234h | 1234h | 0000h | 0700:0130 | 0700:011C | FFFEh | 1 | — |
| 11 | int 3 | 1234h | 0000h | 0700:0130 | 0700:0130 | FFF8h | 0 | [FFF8h] = 011Dh, [FFFAh] = 0700h, [FFFCh] = F246h |
| 17 | push ax | 1234h | 0000h | 0700:0130 | 0700:0131 | FFF6h | 0 | [FFF6h] = 1234h |
| 18 | mov ax, 0E21h | 0E21h | 0000h | 0700:0130 | 0700:0134 | FFF6h | 0 | — |
| 19 | int 10h | 0E21h | 0000h | 0700:0130 | 0700:0136 | FFF6h | 0 | çıktı: ! |
| 20 | pop ax | 1234h | 0000h | 0700:0130 | 0700:0137 | FFF8h | 0 | — |
| 21 | iret | 1234h | 0000h | 0700:0130 | 0700:011D | FFFEh | 1 | yığın boşaldı |
| 12–13 | mov bx, eski / mov es:[3*4], bx | 1234h | 1003h | 0700:1003 | 0700:0126 | FFFEh | 1 | — |
| 14–15 | mov bx, eski+2 / mov es:[3*4+2], bx | 1234h | F000h | F000:1003 | 0700:012F | FFFEh | 1 | — |
| 16 | ret | 1234h | F000h | F000:1003 | 0700:0000 | 0000h | 1 | DOS'a dönüş |
Tablo 5.1.18'in ilk evresi vektörün değiştirilmesidir. Satır 5'te XCHG yeni IP'yi (0130h) tabloya yazar ve eski IP'yi (1003h) AX'e getirir. Vektör o an F000:0130'dur: yarısı yeni, yarısı eski. Satır 8'de ikinci XCHG yeni CS'yi (0700h) yazar, eski CS'yi (F000h) AX'e getirir. Vektör artık 0700:0130'dur ve eski vektör F000:1003, eski değişkeninde saklıdır. Tip 3'ü yalnız program çağırdığı için bu örnekte CLI gerekmemiştir. Bir donanım tipinin vektörü değiştirilirken bu satırlar CLI ile STI arasına alınır.
İkinci evre kesmedir. Satır 10'da AX = 1234h olur; bu, ana programın korunması gereken değeridir. Satır 11'de int 3 tek bayttır (CCh). Yığına FLAGS (F246h), CS (0700h) ve dönüş IP'si 011Dh itilir. Dönüş adresi 011Ch + 1 = 011Dh'dir; INT 3'ün tek baytlığı burada da görülür. CS:IP ← 0700:0130 olur.
push ax'ten sonra: yığında dört sözcük vardır. Altta kesmenin ittiği FLAGS, CS ve IP, üstte ISR'nin sakladığı AX = 1234h; SP = FFF6h.Üçüncü evre ISR'dir. Satır 17'de ISR kullanacağı AX'i önce yığına iter: SP = FFF6h ve [FFF6h] = 1234h (Şekil 5.1.13). Satır 18–19 AX = 0E21h ile ekrana ! yazar. Satır 20'de pop ax 1234h'yi geri alır; SP = FFF8h olur ve yığının tepesi yeniden kesmenin ittiği IP'dir. Bu noktada PUSH ile POP'un sayısı eşit olmalıdır. Eşit olmasaydı IRET yanlış sözcükleri dönüş adresi diye alırdı. Satır 21'de iret IP = 011Dh, CS = 0700h ve FLAGS = F246h'yi geri alır; SP = FFFEh ve IF = 1 olur.
Son evre geri yüklemedir. Satır 12–15 eski değişkenindeki iki sözcüğü tabloya geri yazar. Burada da vektör bir an 0700:1003 gibi anlamsız bir ara değerden geçer. Satır 16'da program döndüğünde AX = 1234h korunmuştur ve vektör tablosu programdan önceki hâlindedir. Bir programın vektör tablosunda iz bırakmadan çıkması budur.
Programın her çıkış yolunda vektörü geri yüklemeyi unutmak. Program yalnız normal sonunda geri yükleme yapar, ama bir hata durumunda erkenden DOS'a dönerse, vektör artık yüklü olmayan bir kodu gösterir. Bir sonraki INT 3 ya da donanım kesmesi sistemi çökertir. Geri yükleme kodu, programın bütün çıkışlarının geçtiği tek bir yere konmalıdır.
5.1.6.4ISR yazım kuralları
Önceki izlemeler ISR yazımının kurallarını tek tek ortaya koydu. Tablo 5.1.19 bu kuralları ve uyulmadığında ortaya çıkan belirtileri toplar.
| Kural | Uyulmazsa |
|---|---|
| Kullanılan her yazmaç PUSH/POP ile korunur (FLAGS'i işlemci korur). | Ana program, değeri kendiliğinden değişmiş bir yazmaçla devam eder; hata rastgele anlarda görülür. |
Donanım ISR'si 8259A'ya EOI gönderir: MOV AL, 20h · OUT 20h, AL. | 8259A ISR yazmacındaki biti silmez; aynı ve daha düşük öncelikli IRQ'lar bir daha gelmez. |
| ISR kısa tutulur; uzun iş bir bayrak değişkeniyle ana programa bırakılır. | Kesme gecikmesi büyür, hızlı aygıtların verisi kaybolur. |
| Donanım ISR'sinden INT 21h çağrılmaz. | DOS yeniden girişli değildir; kesilen DOS çağrısının verileri bozulur. |
| Vektör CLI ile ya da AH = 25h ile yazılır, çıkışta eskisi geri yüklenir. | Program bittikten sonra kesme, artık başka bir şeyin bulunduğu belleğe atlar. |
| ISR IRET ile biter. | Yığın 4 bayt kayar, IF kapalı kalır. |
Yazmaçları korumak. Bir donanım ISR'si programın herhangi bir komutundan sonra çalışabilir. Programın o anda hangi yazmacı kullandığı bilinemez. Bu yüzden ISR, kullandığı her yazmacı girişte iter ve çıkışta ters sırayla geri alır. Bayrakları saklamaya gerek yoktur; INT ve IRET bunu kendisi yapar. Kesim yazmaçları da unutulmamalıdır. ISR kendi verisine erişmek için DS'yi değiştirirse, DS'yi de saklamalıdır.
Kısa ISR ve bayrak değişkeni. Uzun süren bir iş ISR'nin içinde yapılmaz. ISR yalnız acil kısmı yapar: aygıttan veriyi okur, bir tampona koyar ve bir değişkeni 1 yapar. Ana program bu değişkene döngüsünde bakar ve uzun işi kesmeler açıkken yapar. Bu kalıp yoklamaya benzer görünür, ama yoklanan şey yavaş bir aygıt değil bellekteki bir değişkendir ve veri kaybolma tehlikesini ISR ortadan kaldırmıştır.
Yeniden girişli (reentrant) yordam, bir çağrısı henüz bitmeden, örneğin bir kesme yüzünden, yeniden çağrıldığında da doğru çalışan yordamdır. Bunun için yordam kalıcı durumunu sabit bellek hücrelerinde değil, yığında ya da yazmaçlarda tutar.
DOS'u çağırmamak. DOS, çalışırken kendi iç değişkenlerini ve kendi yığınını sabit bellek alanlarında tutar. Program bir INT 21h çağrısının ortasındayken bir donanım kesmesi gelir ve ISR yeniden INT 21h çağırırsa, ikinci çağrı birincinin değişkenlerinin ve yığınının üzerine yazar. Birinci çağrıya dönüldüğünde veriler bozulmuştur. DOS yeniden girişli olmadığı için donanım ISR'leri DOS hizmetlerini kullanmaz. BIOS'un INT 10h gibi hizmetleri ise çoğu zaman bu açıdan daha güvenlidir; yukarıdaki örnek ISR'ler bu yüzden INT 10h'yi kullanır ve yazılım kesmesiyle çağrılır.
ISR içinde STI. ISR, IF = 0 ile başlar. Uzun bir ISR, işinin başında STI ile daha öncelikli donanım kesmelerine izin verebilir; 8259A aynı ve daha düşük öncelikli IRQ'ları EOI'ye kadar zaten bekletir. Bu durumda ISR'nin kendisi de yeniden girişli yazılmalıdır. Çünkü başka bir aygıtın kesmesi, ISR'nin ortasında başka bir yordam çalıştırabilir ve ISR'nin paylaştığı değişkenlere dokunabilir.
Bu kuralları uygulayan tipik bir donanım ISR'sinin iskeleti şöyledir. Kod, kuralların sırasını göstermek için yazılmıştır; aygıtın ne olduğuna göre ortadaki satırlar değişir.
isr_irq:
push ax ; kullanılan yazmaçları sakla
push ds
mov ax, seg veri ; ISR'nin kendi veri kesimi
mov ds, ax
in al, 60h ; aygıttan veriyi al (örnek port)
mov son_kod, al ; tampona / değişkene koy
mov hazir, 1 ; ana programa bayrak
mov al, 20h ; EOI
out 20h, al
pop ds ; ters sırayla geri al
pop ax
iret ; RET değilYazılım kesmesiyle çağrılan bir ISR'ye EOI koymak. EOI yalnız 8259A'dan gelen donanım kesmeleri içindir. INT 60h gibi bir yazılım kesmesinin ISR'si EOI gönderirse, 8259A o sırada hizmette olan başka bir IRQ'nun bitini siler ve o aygıtın kesme düzeni bozulur.
5.1.7Alıştırmalar ve çözümleri
Sunumdaki altı alıştırma burada ara adımlarıyla çözülmüştür.
1. INT 13h vektörü hangi fiziksel adreslerde bulunur?
13h × 4 = 4Ch (onlu: 19 × 4 = 76). Vektör 0004Ch–0004Fh baytlarındadır. IP 0004Ch–0004Dh'de, CS 0004Eh–0004Fh'dedir.
2. INT 21h çalışmadan önce SP = FFFEh ise ISR içindeyken SP kaçtır? Yığında ne vardır?
INT üç sözcük, 6 bayt iter: FFFEh − 6 = FFF8h. Yığının tepesinde, [FFF8h]'de dönüş IP'si; [FFFAh]'da CS; [FFFCh]'de FLAGS vardır. FLAGS'in yığındaki kopyası IF = 1'i taşır; yazmaçtaki FLAGS ise IF = TF = 0'dır.
3. IMR = B8h iken yalnız IRQ0'ı (zamanlayıcı) da maskeleyen komutları yazın. IMR kaç olur?
Oku–değiştir–yaz kalıbı kullanılır: IN AL, 21h · OR AL, 01h · OUT 21h, AL. 1011 1000 OR 0000 0001 = 1011 1001 = B9h. IRQ3, 4, 5 ve 7 maskeli kalır, IRQ0 da maskelenir. Zamanlayıcı maskeli kaldıkça günün saati ilerlemez; bu yüzden böyle bir maske çok kısa tutulur.
4. Tip 61h'nin vektörü 0700:0120h yapılacak. Hangi fiziksel adreslere hangi baytlar yazılır?
61h × 4 = 184h. Önce IP, sonra CS yazılır; ikisi de küçük uçludur. 00184h ← 20h, 00185h ← 01h, 00186h ← 00h, 00187h ← 07h. Bu ISR'nin fiziksel adresi 07000h + 0120h = 07120h'dir.
5. 5 MHz'lik 8086'da INTR geldiğinde MUL BX (en çok 133 tur) yeni başlamışsa kesme gecikmesi en çok ne kadardır?
Önce MUL bitmelidir: 133 tur. Ardından kabul: 61 tur. Toplam 194 tur. 194 × 200 ns = 38,8 µs ≈ 39 µs.
6. ISR'nin IRET yerine RET ile bitmesi ne gibi sorunlara yol açar?
Yakın RET yalnız IP'yi alır. CS ve FLAGS yığında kalır, SP 4 bayt kayar. FLAGS geri gelmediği için IF = 0 kalır ve maskelenebilir kesmeler bir daha kabul edilmez. ISR başka kesimdeyse CS de yanlış kalır ve program yanlış kesimde devam eder. İlk RET ya da POP, yığında kalan CS'yi veri ya da dönüş adresi diye alır. RET ile biten ISR izlemesinde bu durumların hepsi görülür.
5.1.8Özet
- Kesme, işlemcinin o anki işini bir komut sınırında bırakıp bir kesme hizmet yordamını (ISR) çalıştırması ve IRET ile kaldığı yere dönmesidir. Kesilen program kesildiğini fark etmez.
- Yoklamada işlemci aygıta sürekli sorar ve zamanının çoğunu boşa harcar; kesmede aygıt hazır olunca işlemciyi uyarır. Kesmenin bedeli vektör tablosu, yığın ve ISR düzenidir.
- Kesmeler üç kaynaktan gelir: yazılım (INT n, INT 3, INTO), donanım (INTR, NMI) ve iç (bölme hatası INT 0, tek adım INT 1). Tip numarası belirlendikten sonra üçü de aynı adımları izler.
- Kesme vektör tablosu 00000h–003FFh'dedir: 256 tip × 4 bayt. Tip n'in vektörü n × 4 adresindedir; düşük sözcük IP, yüksek sözcük CS'dir, ikisi de küçük uçludur.
- INT n: FLAGS itilir, IF = TF = 0 yapılır, CS ve IP itilir (SP − 6), CS:IP ← vektör. İtilen IP, INT'ten sonraki komutun adresidir. IRET IP, CS ve FLAGS'i geri alır; IF ve TF eski değerlerine döner.
- ISR'yi RET ile bitirmek yığını 4 bayt kaydırır ve IF'yi kapalı bırakır; RETF de FLAGS'i geri almaz. ISR'ler her zaman IRET ile biter.
- Bölme hatası hem bölen sıfırken hem de bölüm hedefe sığmadığında oluşur. 8086 DIV'den sonraki komutun adresini iter. INTO yalnız OF = 1 iken INT 4'ü çağırır. TF = 1 iken her komuttan sonra INT 1 üretilir; TF yığın üzerinden değiştirilir. INT 3 tek bayttır (CCh) ve kesme noktası için kullanılır.
- INTR düzey tetiklemelidir, yalnız IF = 1 iken ve komut sınırında kabul edilir; tip numarası ikinci INTA darbesinde 8259A'dan okunur. NMI yükselen kenarla tetiklenir, maskelenemez ve hep tip 2'dir.
- Öncelik: bölme hatası / INT n / INTO, sonra NMI, sonra INTR, en son tek adım. PC'de IRQ n'in tipi 08h + n'dir; zamanlayıcı 08h, klavye 09h.
- 8259A'da IRR bekleyen istekleri, IMR maskeleri (port 21h, bit 1 = engelli), ISR yazmacı hizmetteki IRQ'yu tutar. Donanım ISR'si sonunda EOI gönderir:
MOV AL, 20h·OUT 20h, AL. IMR oku–değiştir–yaz kalıbıyla değiştirilir. - SS yüklendikten ve STI'den sonra kesme bir komut ertelenir; REP'li dizgi komutları turlar arasında kesilebilir.
- Kesme gecikmesi = yürüyen komutun kalan süresi + kabul süresi (INTR için 61 tur). CLI ile kapalı geçen süre gecikmeye eklenir; CLI–STI arası ve ISR kısa tutulur.
- Kendi ISR'si kurulurken kullanılmayan bir tip seçilir ve vektör CLI/STI arasında (ya da INT 21h, AH = 25h ile) yazılır. Eski vektör saklanır ve çıkışta geri yüklenir. ISR kullandığı yazmaçları korur ve IRET ile biter.
5.1.9Kavram listesi
Terimlerin İngilizce karşılıkları ve öteki bölümlerdeki kullanımları terim sözlüğünde bulunur.
- kesme interrupt
- kesme hizmet yordamı (ISR) interrupt service routine
- yoklama polling
- donanım / yazılım kesmesi hardware / software interrupt
- istisna exception
- kesme vektörü interrupt vector
- kesme vektör tablosu (IVT) interrupt vector table
- kesme bayrağı (IF) interrupt flag
- tuzak bayrağı (TF) trap flag
- tek adım single-step
- kesme noktası (INT 3) breakpoint
- maskelenebilir / maskelenemez kesme maskable / non-maskable interrupt
- kesme onayı (INTA) interrupt acknowledge
- programlanabilir kesme denetleyicisi (8259A) programmable interrupt controller
- kesme maske yazmacı (IMR) interrupt mask register
- kesme sonu (EOI) end of interrupt
- kesme gecikmesi interrupt latency
- yeniden girişli reentrant
- yığın stack
- bağıl konum offset
- fiziksel adres physical address
5.1.10Kendini Dene
Bir bellek dökümünde 0000:0020h adresinden başlayarak
A5 FE 00 F0baytları görülüyor. Hangi tipin vektörüdür, PC'de hangi aygıta aittir? ISR'nin fiziksel adresi nedir?Cevap
20h / 4 = 08h: tip 08h, yani IRQ0'a bağlı zamanlayıcı kesmesi. IP = FEA5h, CS = F000h. Fiziksel adres F0000h + FEA5h = FFEA5h'dir.
0700:0200 adresindeki
INT 21hçalışmadan önce SP = FFF0h ve FLAGS = F246h'dir. ISR'nin ilk komutunda SP, yığındaki üç sözcük ve FLAGS nedir?Cevap
SP = FFF0h − 6 = FFEAh. [FFEAh] = 0202h (INT 21h iki bayt), [FFECh] = 0700h, [FFEEh] = F246h. Yazmaçtaki FLAGS'te IF (bit 9) silinir: F246h − 0200h = F046h. TF zaten 0'dır.
BL = 2 iken
DIV BL, AX = 01FEh için ve AX = 0200h için ne sonuç verir?Cevap
510 / 2 = 255 = FFh AL'ye sığar: AL = FFh, AH = 00h. 512 / 2 = 256 sığmaz; bölen sıfır olmadığı hâlde INT 0 üretilir. Kısa sınama: ikinci durumda AH = 02h ≥ BL = 02h'dir.
IMR = 00h iken zamanlayıcı ve klavye kesmelerini birlikte maskeleyen komut dizisi nedir? Yeni IMR kaçtır? Bu sırada bir tuşa basılırsa ne olur?
Cevap
IN AL, 21h·OR AL, 03h·OUT 21h, AL; IMR = 03h olur. Basılan tuşun isteği 8259A'nın IRR'sinde bekler; maske kaldırılınca hizmet görür.8 MHz'lik bir 8086'da INTR geldiğinde en çok 184 turluk bir IDIV yeni başlamışsa en kötü kesme gecikmesi kaç µs'dir?
Cevap
184 + 61 = 245 tur. 8 MHz'de bir tur 125 ns'dir: 245 × 125 ns ≈ 30,6 µs.
Bir program yığını taşımak için önce
MOV SP, 0FFFEh, sonraMOV SS, AXyazıyor. Bu sıra neden güvenli değildir?Cevap
Erteleme yalnız kesim yazmacına yazan komuttan sonraki komutu korur. İki komut arasında, yani MOV SP'den sonra kesme gelebilir. O anda SP yeni, SS eskidir ve FLAGS, CS, IP yanlış bir yere itilir. Doğru sıra önce
MOV SS, hemen ardındanMOV SP'dir.Bir donanım ISR'si BX ve DX'i kullanıyor ama yalnız BX'i yığına itip geri alıyor. Ana programda ne gibi bir belirti görülür?
Cevap
Kesme programın herhangi bir komutundan sonra gelebildiği için DX, ana programın bilmediği anlarda değişir. Program çoğu zaman doğru çalışır, ama kesmenin tam DX kullanılırken geldiği seyrek anlarda yanlış sonuç verir. Hata tekrarlanması zor, rastgele görünen bir hatadır.
Hata ayıklayıcı 0150h adresindeki tek baytlık bir komutun yerine CCh yazmıştır. INT 3 yığına hangi IP'yi iter? Hata ayıklayıcı bu değeri neden değiştirir?
Cevap
INT 3 tek bayt olduğu için itilen IP 0151h'dir. Hata ayıklayıcı saklanan asıl baytı 0150h'ye geri yazar ve yığındaki IP'yi 1 azaltarak 0150h yapar. IRET sonrasında asıl komut baştan, bozulmamış hâliyle yürütülür.
Bir program CLI ile kesmeleri kapatmışken bellek eşlik hatası oluşuyor. Kesme kabul edilir mi? Vektör hangi adresten okunur?
Cevap
Eşlik hatası PC'de NMI ile bildirilir. NMI maskelenemez; IF = 0 iken de kabul edilir. Tipi sabit 2'dir ve vektör 2 × 4 = 00008h–0000Bh baytlarından okunur. INTA döngüsü yapılmaz.
5.1.11İleri okuma
- Intel, The 8086 Family User's Manual (1979), Interrupts kesimi: kesme türleri, öncelik sırası, INTR ve NMI zamanlaması ve komutların saat turu sayıları bu kaynaktadır. Tablo 5.1.6 ve 5.1.16'daki değerler buradan alınmıştır.
- B. B. Brey, The Intel Microprocessors (8. baskı), Bölüm 12: gerçek kipte kesmeler, 8259A'nın programlanması ve basamaklanması, PC'deki kesme düzeni.
- A. S. Tanenbaum, Modern Operating Systems (3. baskı): kesmelerin işletim sistemindeki yeri, aygıt sürücüleri ve kesme ile yoklamanın karşılaştırılması.
- R. Brown, Ralf Brown's Interrupt List: PC'deki bütün BIOS, DOS ve sürücü kesmelerinin numara numara başvuru listesi.
5.1.12Kaynaklar
- A. S. Tanenbaum, Modern Operating Systems, 3. baskı, Pearson, 2008.
- Intel, The 8086 Family User's Manual, 1979 — Interrupts bölümü.
- B. B. Brey, The Intel Microprocessors, 8. baskı, Pearson, 2009 — Bölüm 6 ve 12.
- emu8086 yardım belgeleri: “Interrupts” ve “Supported interrupts” sayfaları.
- R. Brown, Ralf Brown's Interrupt List (BIOS/DOS kesme başvurusu).
İlgili sayfalar: Bölüm 5.1 sunumu · Çalışma soruları · Terim sözlüğü · © Sercan Külcü. Tüm hakları saklıdır.