ASSEMBLER-4

Geçen sayıda değinmeye başladığımız 16 Bit işlemler oldukça başımızı ağrıtmıştı. Şimdi isterseniz biraz bu sayı karmaşasından uzaklaşalım ve basit bir Assembler prog- Tamını incelemeye başlayalım. Yalnız burada yeni bir komut karşımıza çıkmakta: BNE “Branch if Not Egual zero” veya diğer bir deyişle “eğer sıfır değilse dallan” bu komutun kısa bir açıklamasıdır. Burada sadece statü registerindeki Z-göstergesi (Bu göstergeyi INX, DER ... komutlarını incelerken detaylı olarak ele almıştık) ““0” değerini almışsa bir dallanma yani istenen bir adrese sıçrama gerçekleşir. Aşağıda göreceğiniz programın akış diyagramı Resim 1'de gösterilmiştir. 1500 LDX # $FF 1502 LDY # $FF 1504 DEY 1505 BNE $1504 1507 DEX 1508 BNE $1502 1S0A BRK Programda ilk olarak X ve Y registerlerine başlangıç değerleri yüklenir. Bunu takip eden satırdaki DEY komutu ise Y registerindeki başlangıç değerini bir indirerek $FE olmasını sağlar. Daha sonra bilgisayar BNE komutu ile karşılaştığı için statü Şer, Z göstergesine (Sıfır göstergesine) bakar. İlk etap sonunda doğal olarak burada sıfır değeri yer almayacağı için (Y registe-

Komut Adresleme Byte Kod Desimal Süre Etkilediği Yöntemi Sayısı Flaglar INX Sihir 1 22 2 NZ INY Sihir 1 200 2 NZ INC Mutlak 3 EE 238 6 NZ DEX Sihir 1 CA 202 2 NZ DEY Sihir 1 88 198 2 NZ DEC Mutlak 3 CE 206 6 NZ SED Sihir 1 F8 248 2 - CLD Sihir 1 D8 216 2 -

ANIL GÜL ri sıfırdan farklı olduğu için değeri 1'dir) program tekrar $1504 adresine döner. Burada Y registeri tekrar | azaltılır ve tekrar BNE ile 0 değerine ulaşıp ulaşmadığı kontrol edilir. Bu işlemler tam 255 defa tekrarlandıktan sonra Z göstergesi O değerini alacaktır ve dolayısıyla program bir sonraki satıra atlama imkânını bulacaktır. Bir sonraki satırda ise bu sefer DEX ile X registeri | azaltılacak ve BNE ile sıfır değerini alıp almadığı kontrol edilecektir. İlk kontrolde X registeri sıfır olamayacağı için program tekrar $1502'ye dönecektir. Bu şekilde yukarıda 255 defa yapılan azaltma işlemi tekrar yapılacak, fakat X registeri yine sıfır olmadığı için yukarıdaki döngü tam 255 defa tekrarlanacaktır. Sonuçta dış döngü 255 defa, iç döngü ise 65025 defa yapılacaktır. Yukarıda incelediğimiz programı yazarken bilgisayarın, BNE $1504 komutundan sonraki adres olarak $1508 yerine $1507 vermiş olması herhalde dikkatinizi çekmiştir. İlk bakışta BNE üç baytlık bir komut gibi gözükmektedir, fakat hafızada sadece iki bayt işgal etmiştir. Bu Branch komutlarının özel adresleme sisteminden kaynaklanmaktadır. “Relatif adresleme” olarak adlandırılan bu sistemi daha sonra diğer Branch komutları ile beraber ele alacağız. Her ne kadar makine dili çok çok hızlı işlese de G 1500 ile örnek programımızı çalıştırdığımızda registerler gösterilene dek bir miktar zaman geçecektir. Eğer daha uzun gecikmelere ihtiyaç duyulursa DEC komutu kullanılarak bir döngü daha ilave edilebilir.

Program
Yavaşlatma
SFF-X-Reg | LDX FF
| FE
DEY
â
BNE 1504
Negatif
DEX
BNE 1502
Negatif
en

Resim 2'de son öğrendiğimiz komutlarla ilgili bazı gerekli bilgiler yer almaktadır. Burada yine komutlar için harcanan süre çevrim sayısı (cycle) birim alınarak verilmiştir. Bu değerlerden faydalanarak yazdığınız programdaki bekleme sürelerini isteğiniz doğrultusunda belirleyebilirsiniz. Bu bekleme sürelerinde bilgisayarımız programın işleyişini yavaşlatmaktan başka bir iş yapmaz fakat bu oldukça önemli bir unsurdur. Örneğin bir kelime işlem programı düşünün. Eğer bu programda tüm metinler bir anda ekrana gelip giderse hiçbir işe yaramaz. Dolayısıyla benzeri durumlarda makine dilinin işleyişini yavaşlatmak son derece önemlidir. Yalnız geciktirme programlarının oldukça çeşidi vardır. Bunlara yine daha sonra zamanı geldiğinde değineceğiz.

Carry ve Overflow:

Şu ana dek bilgisayarımızın tam sayıları nasıl sakladığını öğrendik ve buna bağlı olarak da aritmetik işlemlerinde kullanılan 9 yeni komut ile tanıştık. İsterseniz kısa bir hatırlatma yapalım: Tam sayılar C-64 tarafından ikinci dereceden karşılıkları ile algılanmaktadır. 8-Bit bir sayının 7. Bit'i o sayının önündeki işareti belirtmektedir. Eğer bu Bit “0” ise bu sayı her zaman kullandığımız normal bir ikilik sayıdır. Diğer durumda ise, yani 7. Bit “1” ise elimizde negatif bir sayının ikinci dereceden karşılığı var demektir. 16 Bit'lik (2 Bayt) sayılar kullanırken ise 15. Bit işaret göstergesi olarak kullanılır. Bu sayılarla işlem yaptığımız bazen yanlış sonuç çıktığını daha önce belirtmiştik. Bu sorunu çözerken statü registerindeki iki bayrağı (göstergeyi) daha kullanacağız: Carry ve V-bayrağı. “To Carry” dilimizde “taşımak” anlamına gelmektedir. Register göstergesinde bu bayrak kısaca “C” ile gösterilir. Şimdi, her şey iyi de burada taşınan nedir, diye sorabilirsiniz. Bu soruyu en iyisi bir örnek ile cevaplayalım. Örneğimizde normal ikilik sayılarla (İşaret Bit'ine bakmadan) işlem yapacağız. 128 ile 130'u toplayalım:

128 1000 0000 +130 1000 0010

258 (1)0000 0010

Sonuç olarak elde ettiğimiz 258 tastamam doğrudur fakat 8 Bit'e sığmamaktadır. Burada artan Bit özel olarak bu iş için ayrılmış bir yere, yani Carry Bit'ine taşınmıştır. Eğer C-64'de aritmetik işlemlerinde bu tür bir artan olursa Carry Bit'i olur. (Resim 3) Bazı durumlarda bu Carry Bit'ini göz ardı edebiliriz, fakat genellikle sonraki işlemlerde bu Bit'in kullanılması gerekir. Yine burada da bazı özel durumlar için hata söz konusudur. Bilgisayarımız “1” değerini aldığında hemen işe negatif sayıları karıştırmakta ve yanlış sonuçlara ulaşmaktadır. Bunu aşağıdaki örneği incelerseniz daha iyi anlayabilirsiniz:

64 0100 0000
— 0000

(126) - 1000 0010

Görüldüğü gibi sonuç yanlıştır. 6. Bit'leri toplarken 7. Bit “1” olmuştur. İkinci dereceden karşılıklarla çalışırken bu negatif bir sayıyı göstereceği için işlem hatalı olacaktır. Bu gibi durumlarda imdadımıza statü registerindeki V-bayrağı (göstergesi) yetişir ve değerini alır. Kısaca şöyle diyebiliriz: Eğer yukarıdaki örnekte olduğu gibi istenmeyen bir zamanda yanlışlıkla 6.Bit'ten 7.Bit'e bir taşma meydana gelirse bu bayrak “1” olur. Bu işin aslı esasında daha karışıktır, fakat bu kadar bilgi bizim için yeterlidir. Görüldüğü gibi makine dilinde hemen hemen tüm operasyonlardan önce olabilecek hatalara karşı önlem alınmalıdır. Aslında bu bilgisayar kullanırken hiç alışık olmadığımız bir unsurdur, fakat ileride bu öğrendiklerimizin pratik uygulamasına geçtiğimizde bunların şimdiki kadar başımızı ağrıtmayacağını göreceğiz. Yukarıdaki işlemde 6. Bit'ten 7. Bit'e bir taşma meydana gelirse 16-Bit sayılar kullanırsak problem kendi kendine çözülür:

64 00000000 01000000
*66 00000000 01000010
130 00000000 10000010

Bildiğimiz gibi 16 Bit sayılarda 15. Bit işaret göstergesi olarak kullanılır. Şimdi diğer bir durumu inceleyelim.

-125 1000 0011 -4 1100 0000 (469) (1)0100 0011

Burada da yanlış bir sonuçla karşılaştık. Yine yanlışlıkla işaret göstergesi değişikliğe uğradı. Yalnız bu sefer değişiklik 6. Bit yerine Carry'den kaynaklanmakta. Carry olduğu için 7. Bit bu sefer “0” olmuştur. Bu problemi de 16-Bit sayıları kullanırsanız kolayca çözebilirsiniz. Şimdi yine öğrendiklerimizi kısaca toplayalım: Eğer 8- Bit işlemlerde ikinci dereceden karşılıklarla gösterilebilinen alanın (-127 ile + 127 arası) dışına çıkılırsa işaret Bit'i (7.Bit) değişmekte ve yanlış sonuç vermektedir. Bu durumda hemen statü registerinde V-bayrağı (göstergesi) üzerinde bir kırmızı lamba yanar ve bize 16-Bit sayılar kullanmamızı söyler. Şimdi gelelim Carry Bit'inin unutulabileceği durumlara. İkinci dereceden karşılıkları ile yapılan tüm 8-Bit aritmetik işlemlerinde bu Bit göz ardı edilebilir. Bunu iki örnekte gösterelim:

*4 0000 0100
— — 1110
*2 (1)000 000

Carry Bit'ine bakılmazsa sonuç doğrudur.

— 1111 1010
*1111 1110
— 1000

Burada da yine Carry Bit'i ihmal edilirse sonuç doğru çıkar. Artık C-64'ün tam sayıları nasıl işlediğini öğrendik. Şimdi bu sayı karmaşasını yine bir yana bırakarak pratik uygulamalara geçelim.

Bilgisayar Hesaplıyor: ADC, CLC

ADC, bizim 6510 mikroişlemciden öğreneceğimiz ilk aritmetik komutudur. Anlamı “Add with Carry”, yani “Carry ile birlikte topla”'dır. Şimdi elimizde iki tane 8- Bit sayı olsun: SAYI1 ve SAYI2. Yalnız bu sayılar fazla büyük değil ve toplamlarında herhangi bir taşma beklenmiyor. Akümülatöre yükleyelim:

LDA #$SAYII
Ve bunu izleyen satır:
ADC # $SAYI2

Bu komutlarla karşılaşan mikroişlemci iki sayıyı toplar ve sonucu akümülatöre yazar. Sonucu görmek istediğinizde akümülatörün değerini STA ile herhangi bir adrese yazdırırız. Yalnız Assembler programlarında Carry'i etkileyen birçok işlem vardır. Bu nedenle hiçbir zaman Carry'nin 1 veya 0 olduğundan emin olamayız. ADC komutu hesap işlemine Carry'i de dahil ettiği için bunun silinmiş olması gerekir. 6510 makine dilinde bu işi yapan CLC komutu mevcuttur. SAYI1 = 12 ve SAYI2 = 7 alınırsa programımız şu şekilde olur:

1200 CLC
1201 LDA
1203 $07
1205 STA 1500

İlk satırımızda yer alan CLC bir baytlık komuttur ve sadece Carry'yi etkilemektedir. ADC ise burada kullanılan şekliyle iki baytlık bir komuttur ve dolaysız olarak adreslenmiştir. Daha önce de belirttiğimiz gibi ADC birkaç göstergeyi (bayrağı) etkileyebilir. Bunların başında Carry ve V-bayrağı gelir, fakat 7. Bit'in 1 değerini almasıyla ortaya negatif bir sayı çıkacağı için N-bayrağı ve $FF değeri aşıldığında da Z-bayrağı değişikliğe uğrayabilir.

$1201 numaralı satırı aşağıdaki şekilde değiştirdiğimizde programımız daha ilginç hale gelir:

1201 LDA $1400

Görüldüğü gibi burada mutlak adresleme yöntemini kullandık ve LDA 3 baytlık bir komuta dönüştü. Dolayısıyla programın kalan kısmı 1 bayt ileriye kaydırılacaktır. Bu son hali ile programımız $1400 adresinin içerdiği değere ADC komutunun ardındaki sayıyı ekleyecektir. LDA komutunda yaptığımız değişikliğin aynını ADC için de yapabiliriz. ADC'nin de mutlak adreslenmesi ile örneğin $1300 adresindeki değerin $1400'dekine eklenmesini sağlayabiliriz. Bu işlemi gerçekleştiren program şu şekildedir:

1200 CLC
1201 LDA $1400
1204 ADC $1300
1207 STA $1500

ADC mutlak olarak adreslendiği için 3 bayt kaplamıştır. Bu türden 8-Bit işlemler oldukça sık kullanılır: Örneğin bir döngüde ADC kullanılarak adım aralığı istenilen şekilde belirlenebilir. Pratikte 16-Bit hesap işlemlerinin çok önemli yeri vardır. Bildiğiniz gibi 16-Bit'lik bir sayı yüksek bayt ve alçak bayt olmak üzere iki parçaya ayrılır. İnceleyeceğimiz örnekte sadece 16 Bit'lik iki sayı toplanacak. SAYI1'in alçak baytı $1300'e, yüksek baytı ise $1301'e yerleştirilmelidir. Aynı şekilde SAYI2 ise $1400 ve $1401 adreslerinde yer almaktadır. Programımızın başında her zaman kullanmamız gereken hazırlık komutları yer almaktadır.

SÜRE 2: JOYSTICK

1200 CLD
1201 CLD

Önce her iki sayının da alçak baytlarını toplayalım:

1202 LDA $1300
1205 ADC $1400
1208 STA $1500

Burada 15. Bit işareti belirlediği için bu tür bir hata söz konusu değildir. Fakat birçok durumda iki sayının toplamı 255'ten ($FF) büyük olabilir. Eğer bu son hal gerçekleşirse Carry değerini alır. Şimdi yüksek baytları toplayalım: 120B LDA $1301 120E ADC $1401 1211 STA $1501

Carry Bit'inde ne olursa olsun yukarıdaki toplam işleminde beraber toplanacaktır. İşlemizin sonucu $1500

(Alçak Bayt) ve $1501'de (Yüksek Bayt) yer almaktadır. Örnek olarak 2176 (0000 1000 1000 0000) ve 1009 (0000 0011 1111 0001) sayılarını toplayalım. Burada kullanacağımız bellek birimleri şu şekilde düzenlenmelidir:

1300 1000 0000 Alçak Bayt 1301 0000 1000 Yüksek Bayt 1400 1111 0001 Alçak Bayt (SAYI2) 1401 000 001 Yüksek Bayt

İlk önce alçak baytları toplayalım:

1300 1000 0000 1400 1111 0001 Carry 0 1500 0001 Carry 1 Şimdi de yüksek baytları: 1301 0000 1000 1401 0000 0011 Carry 1 1501 0000 1100

Böylelikle sonuç olan 3185 (0000 1100 0111 0001) alçak ve yüksek bayt olarak $1500 ve $1501 adreslerinde yer alacaktır. Yalnız burada Carry'nin değeri “1”'dir. Bu nedenle yeni bir işleme başlamadan önce CLC ile Carry temizlenmelidir. Toplam hakkında bilmemiz gereken her şey yukarıda anlatılanlardan ibaret. Gelecek sayıda makine dilinde çıkartmanın nasıl yapıldığını göreceğiz. Hoşça kalın...