Bu sayfadaki yazılar
Sayfanın metni
Sayfa 29
Düşünürseniz, tıpkı ilkokuldan bildiğimiz çıkarma işlemi gibi, yalnız eldeleri onar onar değil, 256'şar alıyoruz. Bu arada, sırası gelmişken bir konuya daha değinelim. Geçen sayıda kısaca gördüğümüz CMP (CoMPare, kıyasla) komutu (hatırlayan var mı?), aslında SBC'nin bir benzeri. Aradaki fark şu: SBC, parametrede belirtilen sayıyı akümülatörden eksiltiyor ve sonucu akümülatörde gösteriyor. CMP da aynı işlemi yapıyor, ama akümülatörün içeriğini değiştirmiyor. Her iki durumda da, parametrede belirtilen rakam akümülatördeki rakamdan büyükse Carry sıfırlanıyor; eşit veya küçükse Carry 1 oluyor. Dolayısıyla:
LDA xx CMP yy BCC zz —— IF xx THEN zz LDA xx CMP yy BCS xx 7 —yy THEN zz LDA xx SBC yy BCC zz ——— Ax xx—yy:1F
A < 0 THEN zz
Bit Kaydırmaca
Gelgelelim çarpma ve bölme işlemlerine. Burada bizi acı bir sürpriz bekliyor; zira makina dilinde çarpma ve bölme işlemleri yok. A*B'yi elde etmek için yöntem, A'yı B defa kendi kendisiyle toplamak. Daha bilimsel, üstelik daha keyifli bir yöntem ise bitlerle oynamak. Buraya kadar bitlere bulaşmadan sürdürdüğümüz makina dili dizimiz, artık bit hesabına girmek zorunda. Hepimizin malumu ki, 0-255 arası herhangi bir sayı, iki haneli bir hex rakamla belirtilebileceği gibi, sekiz haneli bir binary rakamla da belirtilebilir. Örneğin, $A9 = 10101001, $87 = 10000111 filan. Onlu sistemde nasıl bir sayıyı onla çarpmak için arkasına bir sıfır eklemek yeterliyse, ikili sistemde de ikiyle çarpmak için arkaya sıfır eklemek yeterli. 1=1; 10=2; 100=4; 1000 = 8. Eğer rakamlarımız sekiz haneyle sınırlıysa, demek ki, ikiyle çarpmak için tüm bitleri bir sola kaydırıp arkada boşalan yere sıfır eklemek gerekiyor.
1010€109ü#E-i
ASL (Arithmetic Shift Left) komutu, akümülatörde bulunan veya parametrede belirtilen byte'ı bir sola kaydırıyor; soldan taşan biti de Carry'e atıyor. Böylece rakam ikiyle çarpılmış oluyor, fakat sonuç 256'dan büyükse, fazlalık 256 Carry'de gözüküyor. LSR (Logical Shift Right), ASL'nin tersi: Rakamı sağa kaydırıp ikiye bölmüş oluyor. Sağdan taşan kesir, Carry'e geçiyor. ASL ve LSR, parametresiz kullanıldıklarında, akümülatörün yükünü sağa sola kaydırıyorlar. Parametreli çeşitlerinden ise, mutlak, zero page, indeksli vb. bilumum adresleme imkânları mevcut. ROL ve ROR, ASL ve LSR'nin ikiz kardeşleri. Şu farkla ki, sola veya sağa kaydırmakla boşalan haneye 0 koyacakları yerde, Carry'de o an bulunan değer'i koyuyorlar. Yani, bir şeyi üst üste 8 kez ASL'lenince sonuç daima sıfır; oysa 9 kez üst üste ROL edilen sayı sonuçta ilk baştaki haline dönüyor. Çünkü, ilk ROL'de soldan taşan bit, bir sonraki ROL'de sağdan gene kuyruğa giriyor. Peki $47 ile $A5'i nasıl çarpacağız? $47'yi ikiliye çevirdiğimizde, arif olan cevabı hemen bilebilir. $47 -> 01000111. Sıfırları atlayıp birleri sayarsak, $47 (onlu 73), = $40 + $04 + $02 + $01 veya onlu 64+4+2+1. Yani bir sayıyı sırasıyla 1, 2, 4 ve 64'le çarpıp sonuçları toplamak, o sayıyı $47 ile çarpmakla eşdeğer. O halde işlem şundan ibaret:
00000001 * A5 * 00000010 * 00000100 * A5* 01000000
$47'yi $60 adresine, $A5'i iki byte'lık bir rakam olarak $61-62'ye koyup, iki byte'lık sonucu $63-64'te elde eden bir program şöyle olabilir:
1184190816 101001600
HAZIRLIK KISMI A9 A7 LDA code 85 STA $68 cag4 AS AZ LDA 85 62 STA $62 A9 BE LDA Dü SA 48
casa 85 61 STA $61 85 63 STA $63 COBE 85 STA $64 İŞLEM A5 62 LDA $62 Ccoi2? 46 LSR #68 gö ga — BCC Ccai6 65 64 ADC $64 85 64 STA $64 AS S1 LDA $61 & ADC $63 CO1E 85 63 STA $63 ceza “6 ASL co22 86 si ROL $61 ca24 A5 66 LDA co26 DO E8 — BNE scaiğ ceza sa BRK
C012'deki işlem, $60 adresindeki $47 sayısını tüp gibi sıkıp, sağdan bir biti-
ni taşıyor. Eğer taşan bit sıfırsa (carry clear), yapacak tek iş, $61-62'deki sayıyı ikiyle çarpıp devam etmek. Yok birse, (carry set) $61-62'de habire ikiye çarpılan sayının mevcut hali, $63-64'teki sonuç hanesine ekleniyor. İlk geçişte taşan 1 ve $A5 sonuca ekleniyor. İkinci geçişte de taşan 1, eklenen $A5*2. Üçüncüde yine 1, eklenen $A5*2*2. Dördüncü, beşinci ve altıncıda sıfır. Yedincide yine 1, eklenen $A5*2*2*2*2*2*2. C024-26, tüp sıkılıp bitti mi diye soruyor. Bitmediyse (BNE) baştan. Bittiyse, bu seferlik BRK.
* * «*
Ananevi yarışmamız bu sefer epeyce yüklü: MADDE 1: $AF85A6 ile $3120C0'yu toplayan programımızın X indeksli olarak kısa yazılışı. MADDE 2: $47 ile $A5'in çarpım sonucu. BÜYÜK İKRAMİYE: Tek byte'lık herhangi bir bölme işlemini, virgülden sonra sekiz bite kadar hesaplayan program.
Klavyeyle gezinmek için ← ve → tuşları.
Sayfa 28
SAÇMALIK NO 1: Makina dilinde $00-7F arası rakamlar pozitif, $80-FF arası rakamlar negatif sayılır. Tabii hemen dikkat ettiniz (mi?), $80 ve üstü rakamların ortak özelliği, ikili sistemde ifade edildiklerinde
$80= 10000000= 128
Bunun neden negatif olduğunu bugüne kadar anlayabilmiş değilim. SAÇMALIK NO 2: Bunu geçen ayın 4'üncü Anayasa maddesinde de görmüştük. Makina dilinde bir rakamın alabileceği en yüksek değer $FF (255) olduğundan, bu rakamı izleyen değer 256 değil, 0'dır! Yalnız nasıl olmuşsa bir kolaylık düşünmüşler, CARRY (kalan, elde var) diye bir şey icat etmişler. Şöyle ki, herhangi bir hesap sırasında sonuç $FF'ı aşıp sıfırlanırsa, CARRY 0 iken 1 oluyor. Ama CARRY bir tek bit; yani 1 iken 2 filan olmuyor. Yaptığı, sadece, “elde var 256” diye bir şeyi akılda tutmak. Carry ile ilgili iki faydalı komut:
SEC (kodu: 38) SEt Carry —Carry'i 1 yap. CLC (kodu: 18) CLear Carry —Carry'i sıfırla
İki komut daha: BCS (B0) Branch on Carry Set —Bir önceki işlemin sonucunda kalan varsa, parametrenin gösterdiği yere atla. BCC (90) Branch on Carry Clear —öncekinin tersi. Hazır yeri gelmişken devam edelim: BPL (10) Branch if Plus —önceki işlemin sonucu, yukarıdaki saçma mantığa göre pozitifse, dallan. BMI (30) Branch if Minus — negatifse dallan. Tüm “branch” komutlarının relatif adresleme kullandığını, geçen sayıda BEQ ve BNE'yi anlatırken görmüştük. Unutanlar için hatırlatalım; eğer komutun içerdiği koşul gerçekleşmişse, komutu izleyen rakam pozitifse program akışı o rakam artı 1 byte ileriye, negatifse FF eksi o rakam byte geriye atlayacaktır. Şöyle göstersek daha kolay anlaşılabilir: (F0= BEQ; nn= parametre)
program
xXx xx xx xx FO nn xx xx XxX XX «es
FA FB FC FD FE FF 00 81 92 93 ... Relatif Pozisyon
1. Aas Ba 18
Sonuç: program akışı $C014'ten devam eder; çünkü C000'daki komut
LDA $00 demektir, sonucu C002'deki komut ise sonuç sıfırsa hex 11 byte ileriden devam et demekte- dir. 2. COOO a9 a9 COO2 10 70
Sonuç: program $C004'ten aynen devam eder; çünkü C000'da aküye hex A9, yani negatif bir sayı yüklenmekte, C002 ise sonuç pozitifse hex 0F byte geri git demektir. “branch” komutlarında 00 parametresi anlamsızdır, çünkü koşul gerçekleşse de, gerçekleşmese de program bir sonraki byte'tan devam edecektir. FE parametresi, eğer koşul gerçekleşirse, programı kısır döngüye sokar; çünkü programı bir byte geriye, yani koşul komutuna atlatır.
olmaz
Sıra geldi aritmetiğin elifbasına: İki tane basit rakamı nasıl toplayacağız? Elcevap: ADC komutuyla, ADC komutu, akümülatörde o an bulunan sayıya, parametresinde belirtilen sayıyı ekler. Hemen hemen tüm adresleme türlerine sahiptir; yani akümülatöre dolaysız bir rakamı veya mutlak bir adreste, indeksli bir adreste, dolaylı bir adreste vb. bulunan bir rakamı ekleyebilir.
4+1 işlemini nasıl yaparız? Elcevap: LDA #$01 ADC #$01
Bu işlemin sonucu ne olur? Elcevap: Belli olmaz! Zira, ADC komutu, adı üstünde ADd with Carry demektir ve önceki işlemden kalan varsa, toplamanın sonucuna bir ekler. Carry 0 ise işlem sonucu 2, Carry 1 ise sonuç 3'tür. Binaenaleyh, özel birtakım emellerimiz yoksa, ADC işleminden önce CLC komutunu vermeyi asla ve kat'a
Carry'nin hikmetinin ne olduğunu anlatmanın en kestirme yolu, $4F85AC rakamıyla $3120C0 rakamını toplamaya çalışmak (isteyen desimale çevirip uğraşsın) İlk iş, LDA ve STA yöntemiyle bellekte herhangi 6 adrese, yukarıdaki rakamları yazmak. Örneğin, zero page'de $60-65 arasını seçelim:
Adres: 60 61 62 63 64 65
Rakam:1F 85 AC 31 (Bak. Şekil 1.)
20. CO
2011 85 66 STA $66 —50 (80)
Sonuç $50A6'dır. İnanmayan kontrol etsin. Tabii bu programı, X indeksli adresleme kullanıp çok daha kısa yazmak mümkün. Ve hayrettir ki, bu ayın Baytan Bitirmez Bilgisayar Bilgisi Birinciliğinin Birinci Bilmecesi Bu. Toplama böyle. Çıkarmaya gelince, bunun da komutu SBC (SuBtract with Carry) ve işleyişi ADC'nin tam tersi. Nasıl FF'ten artırıp 00'a ulaşınca Carry 1 oluyorsa, 00'dan eksiltip $FF'e ulaşırsak Carry 1 iken sıfırlanıyor (sıfırsa sıfır kalıyor). Nasıl tek haneli ADC işleminde doğru sonuç almak için Carry'i sıfırlamak gerekiyorsa, tek haneli SBC operasyonunda da Carry'i 1 yapmak şart. Yani işlem,
SEC LDA xx SBC yy
Çok haneli çıkarma işleminde, herhangi bir hanedeki SBC sonucu eksi ise, Carry sıfırlanıyor; dolayısıyla bir soldaki hanede SBC sonucu bir eksik veriyor.
90 A8 25
sBC A0 AZ 69
50 00 CO
Toplama sonucunu da $66-68'e
Program şöyle:
2090 18 CLC 2001 A5 65 LDA 20803 65 62 ADC $62 2005 85 68 STA $68 2007 A5 GBâ LDA 20609 65 61 ADC $6İ 20B 85 67 STA $67 A5 63 LDA $653 200F 65 6G6ü ADC Şekil 1
Elde var © co <192) tAC (172) (108) elde 1 (256) , *85 (133) zAB (166) elde B 31 <49) *1F (31) 148