İNTERRUPTLAR

İki sayıdır intro efektleri bölümünde interruptları kullanarak bir şeyler yapıyoruz. Aslında “interrupt” konusunu intro efektlerine başlamadan önce anlatmam gerekiyordu. Ama yine de geç kalmış sayılmayız ☺ Öncelikle “interrupt” kelimesi ne demektir, C64’te ne işe yarar vs. soruların cevabını verelim. Kelime anlamı olarak “interrupt” “kesmek, akışını durdurmak, yarıda kesmek, düzenini bozmak” demektir. Bu durumda biz “interrupt” ile C64’te neyi kesiyoruz, neyin akışını durduruyoruz buna bir göz atalım. C64’ü açıp karşınıza gelen ilk ekrana bakın. İlk gözünüze çarpan yanıp sönen bir kursör vardır. Oysa C64’ün içinde bundan çok daha fazlası yapılmaktadır. C64’te sürekli çalışan bir program vardır ve bunun adı KERNAL dediğimiz işletim sistemidir. Bu işletim sistemi sürekli olarak tuşları okur, kursörü yakıp söndürür, sistem saatini günceller vs.. Hazır başlamışken biraz daha detaya inelim ve mikroişlemciden başlayalım. C64’e ilk enerji verdiğimizde aslında mikroişlemci kendi içerisinde bir şeyler yapmaktadır. Tamamen hardware kaynaklı olan bu işlemler başlamadan önce işlemci besleme geriliminin belli bir seviyeye çıkması için bekler. Ama bu bekleme çok kısa olduğu için biz fark etmeyiz. Daha sonra işlemcinin tasarım aşamasında belirlenen ve ilk enerji verildiğinde hangi adreslerden itibaren çalışmaya başlayacağı bilgisi vardır. Bu bilgiler yani ilk çalışma adresleri 6510 için vektör adres olarak tasarlanmıştır ve adreslenebilen bellek aralığının son 6 adresidir. Hardware vektör denilen bu adresler aşağıda verilmiştir.

VEKTÖR VEKTÖR ADI $FFFA 43 FE $FE43 NMI VECTOR $FFFC E2 FC $FCE2 RESET VECTOR $FFFE 48 FF $FF48 IRQ VECTOR Yukarıdaki tabloda işlemci ilk enerji verildiğinde NMI (Non maskable interrupt-engellenemez interrupt) işlemleri için $FFFA ve $FFFB adreslerinde tutulan sayıların ($43 ve $FE) gösterdiği adrese dolaylı sıçrama (indirect jump) yapar ve çalışmaya $FE43 adresinden itibaren devam eder. Aynı şekilde RESET için $FCE2 adresinden, IRQ (Interrupt ReQuest) için $FF48 adresinden devam eder. Bu arada belirteyim, RESET işlemi en yüksek önceliğe sahiptir yani o anda işlemci ne iş yapıyor olursa olsun hatta kilitlenmiş olsun hardware olarak –mesela bir reset butonu ile- bir RESET işareti geldiği anda her şeyi yeniden başlatır. Veya işlemcinin NMI, RESET ve IRQ girişlerine aynı anda işaret uygulayın önce RESET işlemleri yapılacaktır. Vektör adresleri yani $FFFA----$FFFF sabittir. Sadece bu adreslerdeki sayılar değişebilir. Diyelim ki C64 için yeni bir işletim sistemi tasarladınız. Yapmanız gereken sadece bu vektör adreslerdeki sayıları kendi NMI-RESET-IRQ rutinlerini gösterecek şekilde değiştirmek olacaktır. Neyse konuyu fazla dağıtmadan devam edelim. IRQ vektörünün gösterdiği adresten itibaren ($FF48) rutini incelediğimizde $FF58 adresinde JMP ($0314) komutunu görürüz. İşlemci interrupt işlemleri sırasında bu adrese geldiğinde $0314 ve $0315 adreslerinde tutulan sayıların gösterdiği adresten itibaren çalışmaya devam edeceğini anlar. Bu arada $0314 ve $0315 adresleri RAM’de olup bu adreslerde tutulan sayılar daha önce RESET rutininin ilk çalışması sırasında yerleştirilmiştir $0314 adresinde $31, $0315 adresinde ise $EA sayıları bulunmaktadır ve JMP ($0314) komutu bir yerde JMP $EA31 gibi çalışmaktadır. O halde neden direct jump yerine indirect jump komutu kullanılmaktadır. Bana göre cevap esnek bir işletim sistemi tasarlandığı içindir. Gerçekten de işletim sistemi tarafından kullanılan bu RAM vektör adresleri sayesinde C64 için çok güzel programlar yapılmıştır. Bu RAM vektörler sayesinde kullanıcı işletim sisteminin çalışmasını aksatmadan araya girip kendi rutinlerini aynı anda çalıştırabilir veya işletim sistemi rutinlerini kısmen devre dışı bırakabilir. Konumuz interruptlar olacağı için sadece interrupt (IRQ) vektörü üzerinde duracağız. IRQ vektörünü değiştirmeden önce mutlaka SEI komutu kullanılmalıdır. Çünkü işletim sistemi zaten o anda kendi interrupt rutinini çalıştırmaktadır. Siz $0314 veya $0315 adresinin içeriğini değiştirdiğiniz anda işletim sistemi bu vektöre uğruyor olabilir. İşletim sistemi artık rastgele bir adresten çalışmaya başlayacaktır ve belki de kilitlenecektir. Mesela siz $0314 adresine o an için $00 değerini yazmış olun ve işletim sistemi de JMP ($0314) komutunu uyguluyor olsun. İşletim sistemi normalde $EA31 adresine uğraması gerekirken birden $EA00 adresine gidecektir. Artık ne yapar, nasıl çalışır bilinmez.

C64 TÜRKİYE #7 20 İşte bunun için IRQ vektörünü değiştirmeden önce SEI komutunu kullanıp işlemciye sen şimdilik interruptları unut, biraz bekle diyoruz. IRQ vektörünü değiştirip CLI komutuyla işlemciye kaldığın yerden devam et diyoruz. Bu arada kendi IRQ rutinimiz de yazmalıyız ki CLI komutundan sonra işlemci IRQ vektörü üzerinden bizim rutinimize uğradığında çalıştırabilecek bir şeyler bulsun. Kendi IRQ rutinimizi yazdık ama bir sorun var gibi. Normalde bir assembler programı yazıldığında, program bir kez çalışacaksa program sonunda “RTS” komutu, sürekli çalışacaksa “JMP prg. başlangıcı” komutu olmalıdır. IRQ rutinlerinde ise işiniz bittikten hemen sonra mutlaka JMP $EA31 veya JMP $EA81 komutu kullanılmalıdır. Aslında JMP $EA81 komutu $EA31 adresindeki rutinin sonunda yer alır ve ikisinin arasında kursör yakıp söndürme, klavye tarama gibi işlemler yapılır. Eğer JMP $EA31 kullanırsanız kendi IRQ rutininiz çalışırken hiçbir şey olmamış gibi klavyeyi kullanabilir veya bir başka assembler rutini çalıştırabilirsiniz. Eğer $EA81 kullanırsanız sadece IRQ rutinleri çalışmış olacaktır. Dikkat ettiyseniz -JMP $EA31 kullanırsanız hiçbir şey olmamış gibi klavyeyi kullanabilir veya başka bir assembler rutin çalıştırabilirsiniz- ifadesini kullandım. Yani aynı anda iki program çalışabilir gibi bir durum var. Şimdi biraz başa dönelim ve interrupt kelimesinin anlamlarına bir daha bakalım. “Akışını durdurmak, yarıda kesmek” demişiz. İnterrupt mikroişlemcinin içinde saniyede 60 kez oluşur, yani asıl çalışan program saniyede 60 kez durdurulur, interrupt rutini çalışır ve işlem bittikten sonra asıl program kaldığı yerden çalışmaya devam eder. Bu nedenle interrupt rutiniyle asıl program aynı anda çalışıyormuş gibi görünür. (SEI komutu işlemci içinde interrupt işlemlerini durdurur) Özetleyecek olursak; 1- İnterrupt vektörü $0314 ve $0315 adreslerinde bulunur. 2- Bu vektörü değiştirmeden önce SEI komutu kullanılmalıdır. 3- Vektör değiştirildikten sonra CLI komutu kullanılmalıdır. 4- Eğer vektörü değiştirdiyseniz kendi rutininiz mutlaka olmalıdır. 5- Kendi rutininizin son komutu mutlaka JMP $EA31 veya JMP $EA81 olmalıdır. 6- İnterrupt saniyede 60 kez meydana geldiği için kendi interrupt rutininiz içinde sonsuz döngü veya çok sayıda iç içe döngü olmamalıdır. Yani rutininizin çalışma süresi 1/60 saniyeden uzun olmamalıdır. İşte örnekler.... *=$0900 start sei ;İnterruptları durdur lda #<user ;kullanıcı rutini adresinin LOW byte’ını sta $0314 ;IRQ vektörü LOW byte adresine yaz ldx #>user ;kullanıcı rutini adresinin HIGH byte’ını sta $0315 ;IRQ vektörü HIGH byte adresine yaz cli ;İnterruptları serbest bırak rts ;BASIC’e dönüş user jmp $ea31 ;İşletim sistemi IRQ rutinine geri dön .end İlk örnekte sadece irq vektörünü nasıl değiştireceğimizi öğreniyoruz. Kendi rutinimizde hiçbir işlem yapmayıp doğrudan işletim sisteminin interrupt rutinine geri dönüyoruz. 2. örnek *=$0900 start sei ;İnterruptları durdur lda #<user ;kullanıcı rutini adresinin LOW byte’ını sta $0314 ;IRQ vektörü LOW byte adresine yaz ldx #>user ;kullanıcı rutini adresinin HIGH byte’ını sta $0315 ;IRQ vektörü HIGH byte adresine yaz cli ;İnterruptları serbest bırak rts ;BASIC’e dönüş user inc $d020 ;Ekranın dış rengini değiştir. jmp $ea31 ;İşletim sistemi IRQ rutinine geri dön. .end

C64 TÜRKİYE #7 21 2. örnekte ise çok basit bir şekilde “flashing border” efekti yaptık. Bunun için INC $D020 komutunu kullandık. $D020 adresinde ekranın BORDER dediğimiz dış kısmının renk değeri bulunur. “inc $d020” komutu ise bu adresin değerini 1 arttırır. Böylece renk değişmiş olur. Bu işlem saniyede 60 kez yapıldığı için ekran flash yapıyor gibi gözükür. 3. örnek *=$0900 start sei ;İnterruptları durdur lda #<user ;kullanıcı rutini adresinin LOW byte’ını sta $0314 ;IRQ vektörü LOW byte adresine yaz ldx #>user ;kullanıcı rutini adresinin HIGH byte’ını sta $0315 ;IRQ vektörü HIGH byte adresine yaz lda #$00 ;başlangıç değeri 0 sta cnt ;cnt (sayaç) adresine yaz cli ;İnterruptları serbest bırak rts ;BASIC’e dönüş user dec cnt ;sayacı 1 azalt bne exit ;sıfır olmadıysa “exit” yazan yere git inc $d020 ;sıfır olduysa ekranın dış rengini değiştir. exit jmp $ea31 ;İşletim sistemi IRQ rutinine geri dön. cnt .byte 0 .end 3. örnekte ise interrupt içinde sayaç kullanmayı gördük. İşletim sistemi rutinimize her uğrayışında sayacın değerini 1 azaltmakta ve sayaç değeri 0 olmadıysa rutinden çıkmaktadır. Eğer sayaç değeri 0 olduysa ekranın dış rengi değiştirilmektedir. Yaklaşık 6-7 saniyede bir ekranın dış rengi değişmektedir. Eğer daha kısa zaman süresi isterseniz sayaç değerini belli değer için kontrol etmeniz gerekmektedir. Mesela sayaç değerini $40 (64) olarak belirleyin. Yaklaşık 2 saniyede bir renk değişecektir. Şu anki haliyle sayaç değeri 0 gözükse bile aslında 256 dır. Nasıl oluyor derseniz açıklayalım. Sayacın ilk değeri sıfırdır. Rutinimizin ilk komutu “DEC CNT” olduğundan sayacımız bir azalmakta ve 255 olmaktadır. Rutinimize her uğrayışta sayaç bir azaltıldığı için ancak 256. kez uğrandığında tekrar 0 olmakta ve renk değiştirme işlemi yapılmaktadır.

TARAMA SATIRI İNTERRUPTI (RASTER LINE INTERRUPT)

C64’te çeşitli interrupt kaynakları vardır. Bunlardan 4 tanesi VIC ile, 5 tanesi CIA dediğimiz 6526 ile ilgilidir. Aslında C64’te 2 adet 6526 olduğu için C64’teki interrupt kaynağı 14 adettir diyebiliriz.

VIC’e ait olan interruptlar şunlardır.

1- Tarama satırı interruptı 2- Sprite-arkaplan çarpışması interruptı 3- Sprite-sprite çarpışması interruptı 4- Light pen interruptı CIA’lara ait olan interruptlar ise şunlardır.

1- A zamanlayıcısı interruptı 2- B zamanlayıcısı interruptı 3- Gün saati alarm interruptı 4- Seri port dolu/boş sinyali interruptı 5- /FLAG (user port’ta bulunur) girişi interruptı

Yukarıdaki interrupt kaynaklarından en çok kullanılanı tarama satırı interruptıdır. Tarama satırı (raster) bir ekran görüntüsü oluşturulurken VIC tarafından üretilir ve bu satırın (rasterin) değeri $D012 adresinde tutulur ve sürekli değişir. $D012 adresi çift fonksiyonlu bir register olup okunduğunda o anki raster konumunun en alt 8 bitidir. 9.bit ise $D011 adresindeki registerin en soldaki (7.bit) bitidir. Normalde kullanılabilen ekran bölümü 51 ($33) ile 251 ($FB) raster satırları arasındadır. $D012’ye ve $D011’in 7. bitine yazılacak bir değer iç raster karşılaştırmasında kullanılır. O anki raster değeri

C64 TÜRKİYE #7 22 ile yazılmış olan değer birbirine eşitse raster interrupt latch biti “1” olur. Kafanızın karışmaya başladığından eminim. Ama güzel bir intro veya demo yapabilmek için bunları ve daha fazlasını bilmeniz gerekmektedir.

Raster interruptını kullanmadan önce yapılacak bir işlem daha vardır. $D01A adresindeki interrupt enable registerine hangi interruptı kullanacağımızı bildirmemiz gerekmektedir. $D01A adresine $1 sayısını yazdığımızda artık raster interruptını kullanabiliriz. Elimizde bir adres daha var $D019 adresi. $D019’da bir interrupt oluştuğunda $D01A ile seçilen interrupta ait bit “1” olur ve programcı hangi interruptın meydana geldiğini anlayabilir. Fakat $D019’daki ilgili bit “1” olduktan sonra hep öyle kalır. Bir sonraki interrupt için bu adresin temizlenmesi gerekir. Şimdi yazdıklarımızı toparlayalım ve basitleştirelim.

1- $D01A adresine $1 yazarak raster interruptını kullanacağımızı belirleriz. 2- $D012 adresine hangi rasterde interrupt yapacağımızı yazarız. 3- $D019 adresini interrupt rutini içinde temizleriz. 4- $D012’yi interrupt rutini içinde kontrol edip rutinimizi çalıştırırız.

Bütün bu öğrendiklerimizle ilgili bir örnek verelim. 
*=$0801 
.word nextline 
.word 2004  
; 2004 
.byte $9e 
; SYS 
.text "2061" 
; 2061 
.byte 0 
; komutu 
nextline 
.word 0 
start  
sei 
;interruptları durdur 
lda 
#<irq  
;interrupt vektörünü 
ldx 
#>irq  
;kendi rutinimizi 
sta 
$0314  
;gösterecek şekilde 
stx 
$0315  
;değiştir. 
lda 
#$30  
;raster değeri 
sta 
$d012  
;raster registerine 
lda 
#$01  
;raster interrupt yapacağımızı 
sta 
$d01a  
;interrupt enable registerine bildir. 
cli 
;interruptları serbest bırak. 
Jmp 
* 
;sonsuz döngüye gir. 
irq 
inc 
$d019  
;bir sonraki interrupt için temizle 
lda 
#$30  
;bu değeri 
wait0  
cmp 
$d012  
;raster registeri ile karşılaştır. 
bne 
wait0  
;eşit değilse bekle. Eşit ise devam et 
lda 
#$01  
;aküye 1 sayısı 
ldx 
#$0b  
;X’e $0b 
ldy 
#$0a  
;Y’ye $0a 
tekrar1 
dex 
;x registerini 1 azalt 
bne 
tekrar1 
;0 oluncaya kadar tekrarla 
sta 
$d020  
;0 olduysa aküdeki değer ekranın dış rengi ve 
sta 
$d021  
;ekranın iç rengi olsun 
tekrar2 
dey 
;y registerini 1 azalt 
bne 
tekrar2 
;0 oluncaya kadar bekle. 0 olduysa 
sty 
$d020  
;ekranın dış rengi ve 
sty 
$d021  
;ekranın iç rengi olsun 
jmp 
$ea81  
;işletim sistemine geri dön. 
.end 
X registerini renk şeritinin başlangıcını sabitlemek için, Y registerini ise biti-
şini sabitlemek için kullanıyoruz. Daha doğrusu çizginin zamanlaması için kullanıyoruz. 
Eğer bu sayıları değiştirirseniz çizginiz titremeye başlar.