Makine Diline Merhaba (4)
Retro bilgisayar12. Altprogram Komutları
BASIC'ten de tanıdığınız çok önemli bir programlama tekniği olan altprogram tekniği makine dili bazında da mümkün. BASIC'te altprogramı çağırmak için GOSUB komutu, altprogramdan geri dönmek için ise RETURN komutu kullanılır. Peki, bir alt programı çağırmak, GOTO veya JMP gibi doğrudan sıçrama komutlarından nerede farklılaşırlar?.. Bir altprogram çağırıldığında BASIC yorumlayıcısı veya ana işlem ünitesi daha sonra RETURN komutuna rastladığında geri dönebilmek için çağıran komutun yerini aklında tutmak zorundadır. Gerek BASIC yorumlayıcısı gerek ana işlem ünitesi bu işlemi otomatik olarak yapmaktadır. Fakat, biz gene de ne olduğunu daha yakından inceleyelim. Ana işlem ünitesinin RETURN komutuna rastladığı zaman geri döneceği yeri bulabilmesi açısından altprogram çağırılırken, geri dönüş adresi bir yere saklanır. Bu yer özel olarak rezerve edilmiş bir hafıza bölgesidir. Bu bölgeye yığın (stack) adı verilir. Bu yığının yeri bellidir ve $0100 adresinden $01FF adresine kadar uzanır (256'dan 511'e kadar). Ana işlem ünitesine o anda geçerli bilginin bu yığının neresinde olduğunu hatırlatması için stackpointer adlı yığın göstergecini kullanır. Bu göstergeci yazımızın başında kısaca tanımıştık. Şimdi, bir altprogram çağırıldığında neler oluyor; yakından görelim. Ana işlem ünitesi o anki adresi alır ve iki Byte'a ayırır. Adresin yüksek değerli byte'ı $100 artı SP'ye (stackpointer) saklanır. SP bir eksiltilir. Arkasından adresin düşük değerli byte'ı $100 artı SP'ye saklanır. SP'nin değeri bir kere daha eksiltilir. Artık basit bir sıçrama işlemiyle altprograma gidilir. Diyelim ki ana işlem ünitesi RETURN komutuna rastladı, geri dönmesi gerekiyor. Bu sefer tam tersi iş-
Ayhan KALAYLIOĞLU ğından alınır. Artık program sayacı, altprogramı çağırma komutunun hemen arkasındaki adresi göstermektedir, programın akışı devam edebilir. Yığının çalışmasını bir kere daha inceleyelim. Yığına bir şey saklanacağı zaman önce bilgi yığına yazılıyor, arkasından yığın göstergeci bir eksiltiliyor. Yığından bir byte geri alınırken, önce SP bir artırılıyor, arkasından değer yığından okunuyor. Görülmektedir ki yığın yukarıdan aşağıya doğru çalışıyor ($1FF'ten $100'a doğru). Başta ters gelen bu olay sizi şaşırtmasın. İşlem gerçekleşir. SP'nin değeri bir artırılır ve yığından bir byte alınır ($100 + SP adresli). Bu byte, program sayacının düşük değerli byte'ı olacak. SP bir kere daha artırılır ve program sayacının yüksek değerli byte'ı da yığından alınır. Bir örnek yığında gelişen olayları daha anlaşılır hale getirecektir.
M. ULSEL.
$C480 JSR$2000
SP -FA
(SOLFA)—$C4 SP —-SP-I
($01F9) — $82 $P —-SP-I
SP — $F8
Program $2000'deki altprograma aitti. Diyelim ki o adreste doğrudan RETURN komutu bulunsun.
SP - $F8
SP-SP41 PCL-($01F9) — $82
PCH-(SOLFA)- $C4
SP - $FA
Program sayacı şu anda $C482'de durmakta. Bu değer otomatik olarak bir daha artırılır. Böylece $C483 adresini, yani $C480'de bulunan altprogram çağırma komutunun hemen arkasındaki komutun adresini elde ederiz. Yığın “Last in-First out” prensibiyle çalışır. Yani yığına son atılan bilgi ilk olarak geri çıkar. Bu özelliğe güvenip içiçe geçmiş birçok derinlikte altprogramlar yazabiliriz. Herbir altprograma rastlandığında yığına yeni bir geri dönüş adresi atılır. Her RETURN komutuna rastlandığında yığından sıradaki geri dönüş adresi alınır ve bir üst sıradaki altprograma geri dönülür. En sonuncu RETURN komutu ile de ana programa dönülür. Yığının çalışma prensibini anladıysanız o'nu aynı zamanda kendi programınızın ana değerlerini saklamak için de kullanabilirsiniz. Bu konuyu bir sonraki komut grubunda inceleyeceğiz. Aşağıdaki tabloda altprogram çağırma ve altprogramdan geri dönme komutlarını ve komut kodlarını bulacaksınız:
Komut Komut kodu JSR $20 RTS $60
13. Yığın Komutları
6510 ana işlem ünitemiz gerek aküyü, gerek durum register'ini yığında saklayıp, daha sonra geri almamıza olanak tanıyor. Bu işlemler sırasında, yığın göstergecinin, okuma işlemi sırasında bir artırılması ve yazma işlemi sırasında bir eksiltilmesi gibi işlemler otomatik olarak kendiliğinden gerçekleşir. Şimdi bu komutları görelim.
PHA
PHA (push accu) komutu akümülatör'ün içeriğini yığına atmamıza yarar. Yığın göstergecinin bir eksiltilmesi işlemi otomatik olarak gerçekleşir. Akümülatör'ün içeriği korunur. İşlemi BASIC'te formüle edersek:
POKE 256-4 SP, A:SP -SP-I
$0100
Ana işlem ünitesinin yığınının $0100 adresinden, $01FF adresine kadar uzandığını bir kere daha hatırlatalım.
PHP
PHP (“push processor status') komutu durum register'ini olduğu gibi yığında saklamamıza yarar. Yığın göstergecinin bir eksiltilmesi otomatik olarak gerçekleşir. Durum register'inin içeriği değişmez. BASIC'te, yapılan işlemi görelim: POKE 256+SP, A:SP=SP-1
$0100 PLA
PLA (pull accu') komutu PHA komutunun tersi işlemi yapar. Yığın göstergeci bir artırılır ve yığından bir byte, akümülatör'e okunur. Okunan değere göre N- ve Z-flag'ları etkilenir. Bu işlem BASIC'te şöyle görünür.
SP — SP 1:A —- PEEK SP)
$0100
PLP
PLP (“pull processor status') komutu PHP komutunun tam tersi işlemi yapar. Okunan byte olduğu gibi durum registerine yerleşir, dolayısıyla bütün flag'lar etkilenir. Yapılan işlemi şöyle yazabiliriz:
SP — SP 4 1:P — PEEK (256-4 SP)
$0100 Aşağıdaki tabloda komutları ve karşılık gelen komut kodlarını bulacaksınız.
Komut Komut kodu PHA $48 PHP $08 PLA $68
| PLP $28
14. Interrupt Komutları
Bu gruptaki komutları başta pek kullanmayacağız, çünkü biraz dikkat ve bilgi istiyor. 6510 ana işlem ünitemiz yürüyen bir programı dışarıdan gelen bir etkiyle kesebilme becerisine sahiptir. Bu işlemin gerçekleşmesi için ana işlem ünitesinin Interrupt (IRQ, Interrupt Request) bacağının elektriksel sinyallerle uyarılması gerekiyor. Interrupt gerçekleşince gelişen olaylar, altprogram işlemine benziyor. Önce yürümekte olan programın kesildiği adres yığına atılıyor, arkasından da durum register'i. Şimdi hafızada bulunan ana interrupt programını yürütmek için $EFFE ve $FFFF adresleri üzerinden indirekt olarak sıçrama işlemi yapılıyor. Bundan başka, program içinde de interrupt yaratma imkânımız var. BRK ('break') komutu bu iş için öngörülmüş. Ama işlem ünitesi bu emre rastladığında interrupt işleminin aynısını yapıyor. Tek farkla, B-flag'ı yakalıyor. COMMODORE 64'ümüzün işletim sistemindeki interrupt değerlendirme altprogramı ('service routine') bu flag'ı da kontrol ediyor. Bu yüzden BRK işleminin normal interrupt'la karışması engelleniyor. Bu komut genelde bazı özel programları çağırmak için kullanılıyor (Mesela monitör programı). Interrupt değerlendirme altprogramı işini bitirdiğinde asıl programa geri dönebilmesi için normal RTS komutunu kullanamaz, çünkü bu komut durum register'ini yığından almıyor. Bu durumda RTI ('return from interrupt') komutu kullanılır. Bu komut hem adresi, hem de durum register'ini yığından alır ve yürütülmesi kesilen programa flag'lar bozulmadan devam edilir.
MEHMET ULUSEL
Aşağıdaki tabloda komutları ve komut kodlarını bulacaksınız.
Komut Komut kodu BRK $00 RTI $40
15. Etkisiz Komutlar
Bilinen komutların en sonuncusuna geldik. Bu komut hiçbir şey yapmıyor, bu yüzden adı NOP ('no operation'). Bu kullanımına örnek olarak, bir program içindeki gereksiz yerleri atmak için o bölgeyi NOP komutlarıyla doldurmak verilebilir; böylece programın geri kalan kısmını kaydırmak ve içindeki adresleri ayarlamak zorunda kalmayız. Ayrıca, bu komut programın akışı içinde belli bir gecikme yaratmak için kullanılır. Ne de olsa NOP'ta bir komut ve yürütülmesi için belli bir zamanın geçmesi gerekiyor.
Komut Komut kodu NOP $EA
16. Bilinmeyen Komutlar
Bu gruptaki komutlar ne ola ki?.. Daha evvelki bölümde 151 emri gördük. Görecek başka bir şey kaldı mı ki?.. Aslında resmi dökümanlara bakılırsa kalmadı. Fakat 6510 ana işlem ünitesinin kullanılmıyor denilen 105 ihtimalde (256-151) ne yaptığını merak eden varsa bu bölümü okusun. Bir kere daha belirtelim, burada belirtilen komutlar aletinizde çalışmayabilir veya sizinkinde çalışır da başkasınınkinde çalışmayabilir. O yüzden, kullanırsanız sorumluluk size ait.
TABLO 1 İşlem Kodları (onaltılık olarak) Crash 02, 12, 22, 32, 42, 52, 62, 72, 92, 82, D2, F2 Hiçbir şey — 1A, 3A, 5A, 7A, DA, (1 Byte)
Hiçbir şey — 04, 14, 34, 44, 54, 64, 74
(2 Byte) 80, 82, 89, C2, D4, E2, F4 Hiçbir şey — 0C, 1C, 3C, 5C, 7C, DC, FC (3 Byte)
Aslında bu bilinmeyen komutların arasında işe yarayacak gibi olanları da var. O yüzden midir, bilemiyorum; Almanlar bayılıyor bu komutları kullanmaya. Tabii, böyle bir programı beylik bir monitör programı ile inceleyince bir şey anlaşılmıyor. Fakat siz böyle bir durumda aşağıdaki listeleri elinize alıp programı çözebilirsiniz. Neyse, daha fazla uzatmadan konumuza geçelim.
ayrılıyorlar. Bunlar sırayla: 1. Grup: Bu komutlar bilgisayarın hiçbir itiraza meydan vermeden kilitlenmesine (crash etmesine) yol açıyor. Böyle bir durumda ya bilgisayarınızı resetliyorsunuz, ya da kapatıp yeniden açıyorsunuz. Başka bir kurtuluş yok. Bu gruba giren komutların hepsinin ortak özelliği, düşük değerli nibble'larının (1 nibble — 4 bit) içeriğinin “2” olması. Fakat düşük değerli nibble'ı
TABLO 2 (IND,X), (IND),Y ABS ABS,X ABS,Y $..,X $.. 2 2 3 3 ASL:ORA 03 13 0F 1F 1B 17 07 ROL:AND 23 33 2F 3F 3B 37 21 LSR:EOR 43 53 4F 5F 5B 57 47 ROR:ADC 63 73 6F 7F 7B 71 67 DEC:CMP C3 D3 CF DF DB D7 C7 F3 EF FF FB F7 E7 Lİ
MEHMET ULSEL
Bu gruptaki komutlar yaptıkları işin düzeni açısından dört kategoriye
*2” olan her emir bilgisayarı crash ettirecek diye bir kural yok, mesela $A2 kodu LDX komutuna karşılık geliyor. 2.Grup: Bu komutlar hiçbir şey yapmıyorlar. Bunlar işlem olarak 3, 2 veya | byte sürebiliyorlar. Son kategoriye bizim NOP'ta giriyor.
3.Grup: Şimdi ilginç bir yere geldik. Burada, ana işlem ünitesi “iki” resmi emri bitişik olarak peşpeşe yürütüyor. Her iki işlemde aynı adresleme tipini kullanıyor. Mesela $E7, $DD byte'- ları peşpeşe gelince INC $DD: SBC $DD işlemleri peşpeşe yapılıyor. Bu işlem iki byte sürerken, aynı işlemi resmi komutlarla yazmaya kalktığımızda dört byte sürecekti. Bu grubun komutlarında STA komutunda kullanılabilen tüm adresleme tipleri geçerli. Tabii, buradan da ilginç bir olay çıkıyor: $E3 ve $DD byte'ları peşpeşe geldiğinde INC ($DD,X):SBC
TABLO 3
AND MM: ROR A işlemi yürür. Daha sonra akümülatör'ün 0 numaralı bit'i carry'e gider. V-flag'ı akümülatör'ün 5 ve 6 numa-
Akü ile X-register'i arasında yapılan AND işleminin sonucu (MM,X)
Akü ile X-register'i arasında yapılan AND işleminin sonucu 0.cı say-
Akü ile X-register'i arasında yapılan AND işleminin sonucu NNMM Akü ile X- arasında AND işlemi yapılır, sonuca bir ve MM 4-1 ad- Akü ile X-register'i arasında yapılan AND işleminin sonucu MM,X Akü ile X-register'i arasında yapılan AND işleminin sonucu yığın NN 4 1 değeri arasında AND işlemi yapılır, sonucu NNMM,Y adresine saklanır. NN 4 1 değeri arasında yapılan AND işleminin so-
MEHMET ULUSOL
NN 4 1 arasında yapılan AND işleminin soadresinin içeriği ile yığın göstergeci arasında AND işlemi yürütülür, sonuç X-register'inde saklanır. Daha sonra TXS: TXA
X-register'i ile akü arasındaki AND işleminin sonucu X-register'inde MM değeri Carry gözönüne
Komut Yapılan işlem 0B MM AND MM işlemi yürütür ve N-flag'ını carry'ye alır. 2B MM 0B MM işleminin aynısı. 4B MM AND A işlemi yürütür. 6B MM ralı bit'leri arasında Exclusive or sonucu. 83 MM adresinde saklanır. 87 MM fada MM adresinde saklanır. 8B MM TKA: AND MM işlemi yürütür. 8FMMNN adresinde saklanır. 93 MM resinin içeriği toplanır, sonuç (MM),Y adresine saklanır. 97 M adresinde saklanır. 9B MM NN gestergecine alınır, daha sonra yığın göstergeci ile 9CMMNN Y-register'i ile nucu NNMM,X adresine saklanır. 9SEMMNN 9CMMNN işlemine benziyor, fakat X- ve Y- değişik. Akü, X-register'i ve nucu NNMM,Y adresinde saklanır. A3 MM LDA (MM,X): TAX işlemi yürütür. A7 MM LDA MM: TAx işlemi yürütür. AB MM Yaptığı işlem tam belirlenemedi. AFMMNN LDA MMNN: TAX işlemi yürütür. B3 MM LDA (MM,)Y: TAX işlemi yürütür. B?7 MM LDA MM,Y: TAX işlemi yürütür. işlemleri yürütülür. BEMMNN LDA MMNN,Y: TAX işlemi yürütür. CB MM saklanır. Daha sonra X-register'inden alınmaksızın çıkarılır, sonuç X-register'inde saklanır. EB MM MM işlemi yürütür.
($DD,X) işlemleri yürüyor. Ama, daha evvel de gördüğümüz gibi INC emri için indirekt adresleme hiçbir şekilde geçerli değil.
İki numaralı tabloda bütün olabilecek kombinasyonları bulabilirsiniz. Diyelim ki $C7 koduna rastladığında ana işlem ünitesi ne yapar?.. Bu kodu tabloda ararız; bu kod beşinci satırın son kolonunda bulunuyor. Bu kolonun en üstüne bakarız ve bu emrin adresleme tipi ile, kaç byte süren bir işlem olduğunu buluruz. Aynı satırın en soluna bakıp hangi işlemin yürütüleceğini buluruz. Buna göre C7
MM (MM 0.cı sayfada bir hafıza elemanını simgelemektedir) kombinasyonu, bildiğimiz DEC MM: CMP MM işlemlerini yürütecektir.
Üçüncü gruptaki komutların yürütülme süreleri, aynı adresleme tipi ile sözkonusu işlemlerin sadece birinin yürüme zamanına eşit. Yani $E7 $DD, sonuç olarak INC $DD kadar hızlı.
En kullanışlı olarak dikkat çeken emir kombinasyonları, “DEC...: CMP...? kombinasyonu (döngüler kurmak için) ve ASL, ROL, LSR, ROR komutlarının indirekt adresleme ile kullanımı (grafik ekranlarında işlemler yapmak için). 4.Grup: Bu grubun komutları basit şekilde tanımlanamayan işlemler yapıyorlar. Bu komutları Tablo 3'te bulabilirsiniz. Sonuç olarak sizi bir kere daha uyaralım. Bu komutları kullanmadan önce iyi düşünün. Çünkü burada anlatılan komutların yürüme şekli de ana işlem ünitesinin üretimine bağlı olarak değişebilir. Size tavsiyemiz, siz gene de standart emir setinden şaşmayın, o'nun her zaman doğru çalışacağı garantidir çünkü.