AMIGA ASSEMBLER
Retro bilgisayarMerhaba. Yeni bir örnek programla yazı dizimize devam ediyoruz. Yine her zamanki gibi örneğimizi yazıp, her satırı tek tek size anlatacağım.
Bu sayımızdaki örnek programda amacımız; belli bir hafıza bölgesinde bir veya birkaç byte'ı aramak olacaktır.
Diyelim ki arama yapacağımız hafıza bölgesi buffer'dan bufferend'e kadar olan 10 byte uzunluğundaki hafıza bölgesi ve aramamız gereken değer ise 5 değeri olsun. Amacımız buffer ile başlayan bu bölgede 5 değeri taşıyan byte'ın hafıza adresini bulmak olacaktır.
Yine size aynı işi yapan birkaç örnek program sunacağım ve siz de aynı işin değişik şekillerde nasıl yapıldığını göreceksiniz.
ÖRNEK PROGRAM 3a:
1 START:
LEA bufferA0
2
LEA bufferend,A 1
3 Ll:
CMP.B #5,(A0)
4
BEQ L2
5
ADD.L #1,A0
6
CMP.L Al A0
7
BNE Ll
8 L2:
RTS
9 Buffer:
dc.b0,0,0,0,0,0,0,0,0,0,
0,0.0,0,5,0,0,0,0,0
lOBufferEnd:
Şimdi ilk örneğimizin açıklamasına geçelim. Rutinin her satırını tek tek açıklıyorum.
SATIR-1:
START: LEA buffer,A0 Programın başlangıcını, aratma yapılacak hafıza bölgesinin başlangıcını temsil eden değer olan buffer değerini A0 adres registerine atamak ile yapıyoruz.
SATIR-2:
LEA bufferend,A1 İkinci satırda aynen birincisi gibi ama bu sefer arama bölgesinin sonunu belirten bufferend değerini A1 adres registerine gönderiyoruz. Böylece üzerinde çalıştığımız hafıza bölgesinin başlangıç ve bitiş değerlerini adres registerleri üzerinden kullanabiliriz.
SATIR-3:
L1: CMP.B #5,(A0) İşte arama yaptığımız ilk satır burası. Şu anda A0 registerinde buffer değeri yani hafıza bölgemizin ilk byte'ı bulunmaktadır. Ve biz
CMP komutu ile A0 adres registerinin gösterdiği hafıza adresinde 5 sayısı var mı ona bakıyoruz. Örneğimizde bu hafıza adresinde 5 değeri olmadığına göre bu satır olumsuz sona erecektir. Ve durum system flaglarına iletilecektir.
SATIR-4:
BEQ L2 Branch komutlarından BEQ kullanıp bir satır önceki işlemin sonucunun 0 (yani cmp.b #5,(a0) satırının sonucunda eşitsizlik) olup olmadığına bakıyoruz. Yine örnek üzerinden yola çıkarsak bu işlemin sonucunun 0 olmadığını (5 eşit değil 0) görürüz.
Öyleyse beq komutu L2 Label'ına atlama yapmayacak demektir. Diğer bir deyişle beq komutu system durum flagları üzerinde araştırmasını yapıp sonucu özelliği üzerinde değerlendirip bir alt satırdan işleyişe devam etmesi gerektiğine karar veriyor.
SATIR-5:
ADD.L #1,A0 Önceki iki satırda arama bölgesindeki ilk byte'ın kontrollerini yapmış oluyoruz ve aramamız gereken değer olan 5 değerinin bu byte'a eşit olmadığını görüyoruz. Şimdi yapmamız gereken şey ise arama yapılacak bölgedeki bir sonraki byte'ı aranacak sayı ile (5) karşılaştırma yapmamız gerekiyor.
Bu iş için de Buffer+1 değerine ulaşmak için bu satırı kullanıyoruz, add.l #1,a0 ile A0 adres registerindeki hafıza adresinin değerini bir artırıyoruz ve böylece A0 bu satırdan itibaren buffer+1 değerine sahip olacaktır.
SATIR-6:
CMP.L A0,A1 Bir önceki satırda A0'ın içeriğini bir değer artırmıştık. Ve şu anda değeri buffer+1 olmaktadır. Aynı şekilde programa başlarken A1 registerine de bufferend değerini yani arama bölgesinin en son yerini belirlemek için kullanmıştık. Şimdi bu satırda A0 ile A1 registerlerinin karşılaştırmasını yapıyoruz. Amacımız ise A0 registerinin bufferend yani arama bölgesinin sonunu geçip geçmediğini kontrol etmek oluyor. Açıklamaya bir alt satırdan devam ediyoruz.
SATIR-7
BNE L1 Bu komut bir üst satırdaki komut ile bağlantılı olarak çalışıyor. Satır-6; A0 ve A1 adres registerlerindeki değerlerin eşit olup olmadığını kontrol ederek durumu system flaglarına yazı-
AMIGA
yor. Ve Satır-7'de durumu system flaglarından okuyup sonucu değerlendiriyor. ASSEMBLER
Rutinde bu satır aynı zamanda rutin döngüsünün son satırını temsil etmektedir. Ve son işlemin sonucu (A0 ile A1'in karşılaştırmalarının sonucu) eşit (0) oluncaya kadar L1 olarak isimlendirilmiş label'a (SATIR-3)'e geri dönüyor.
Buraya kadar oluşturduğumuz satır-3'den satır-7'ye kadar olan döngü sonucunda arama bölgesinin sadece bir byte'ını kontrol etmiş olduk. Döngünün son satırı ile arama bölgesinin sonuna ulaşmadığımızı görünce counter olarak kullandığımız A0 registeri yardımı ile arama hafıza bölgesinin bir sonraki satırını kontrol ediyoruz.
Bu işlem bufferend-buffer kadar (bu bölgenin 20 byte olduğunu düşünürsek 20 defa tekrar ediliyor.) sürecektir.
SATIR-8:
L2: RTS işlemci bu satıra iki türlü gelebilir. Birincisi satır-6 ve satır-7'deki arama yapılan hafıza bölgesinin sonunun gelip gelmediğini kontrol eden cmp ve bne komutlarının etkisi ile döngüden kurtulabilir.
İkinci olarak aratılan sayıyı (5) bulduğunda (satır-3 ve satır-4'den) satır-4'deki beq L2 komutu ile döngüden çıkabilir.
Tabii bizim verdiğimiz örneği göz önüne alarak sonucu değerlendirdiğimizde aratılan hafıza bölgesinin 15'inci byte'ındaki 5 değeri olduğunda bilgisayar bu satıra satır-4'den inebilir. Yani arama işlemi sonucunda aranan sayı bulunmuş olacaktır.
SATIR-9:
Buffer dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0, 0,0,0,0 Bilindiği gibi bu satır örneğimizdeki arama bölgesinin kendisini oluşturmaktadır. BUFFER olarak adlandırdığımız bu hafıza bölgesinin uzunluğu 20 byte'dan oluşmaktadır. Daha öncede anlattığım gibi dc.b ifadesi bu bölgeyi oluşturan verilerin birer byte olarak dizildikleri anlamına geliyor.
SATIR-10:
Bufferend: Bu satırda yapmak istediğimiz olay, bir önceki satır ile doğrudan ilgilidir. Yani bu satırda bulunan ve tek bir bufferend: isimli label; 20 byte olarak tanımladığımız buffer isimli hafıza bölgesinin sonunu belirlemek için kullanılmaktadır.
Programımızın genel amacı 5 değerli byte'ın hafıza adresini bulmaktı. Bu da buffer+15 adresi olmaktadır.
Kısa rutinimizin asıl kısmında, buffer bölgesinden tek tek byte'ları alıp 5 değeri ile karşılaştırdık. Arama bölgemizin sonunu belirlemek için şu yolu kullandık.
Rutinin başında bufferend label'ını A1 adres registerine gönderdik. Sonra ana döngünün sonunda her defasında bufferend'e ulaşıp ulaşmadığımızı kontrol ettik. Şimdi örneğimizin bir başka şeklini göreceksiniz.
ÖRNEK PROGRAM 3b:
1 START:
LEA buffer,A0
2
MOVE.L#20,D0
3 L1:
CMP.B #5,(A0)
4
BEQ L2
5
SUB.L #1,D0
6
CMPL #0,D0
7
BNE L1
8 L2:
RTS
9 Buffer
dc.b 0,0,0,0,0,0,0,0,0,
0,0,0,0,0,5,0,0,0,0,0
Evet. Rutinin bu hali ile ilk haline şöyle bir göz gezdirin, ilk haline göre bu hali biraz kısıtlamalı oldu. Neden mi? Şöyle;
Eskisinde arama işlemi yaptığımız ve ayırdığımız hafıza bölgesinin başını ve sonunu label'lar ile belirlemiştik. Şimdi ise bölgenin başına bir label (buffer) ve sonuna ise hiçbir şey koymadık. Peki durum böyle olunca ve diyelim ki hafıza bölgesinin 15'inci byte'ında 5 değeri bulunmuyor. Bu şekilde ayrılan bölgenin sonunu nasıl bulacağız? Bu sorunun cevabı 2. satırda.
Yukarıda rutinin bu halinin kısıtlamalı olduğundan bahsetmiştim. Bunun nedeni ise, ikinci satırdaki move.l #20,d0 olmaktadır. Biz bu satırla birinci programdaki bufferend label'ı yerine bölgenin sonunu değil de, uzunluğunu D0 data registerine göndermiş oluyoruz. Ve bunu yine programın her döngüsü esnasında kontrol ederek arama bölgesinin sonunu buluyoruz.
Kısıtlama işine gelince: Şimdi birinci rutin için bazı şeyler söylemek istiyorum. Buffer ile bufferend label'ları arasında kaç byte olduğu bizi ilgilendirmemektedir. Biz nasıl olsa ayırdığımız hafıza bölgesinin başlangıcını ve sonunu label'lar aracılığı ile biliyoruz. Arada uzunluğun kaç byte olduğunu bilmemiz ve bunu bir yerlere yazmamız gerekmez. Bölgenin başlangıcını bulmak istediğimizde buffer label'ının adresini okumamız, sonunu bulmak istediğimizde ise bufferend label'ını okumamız ve data satırlarının uzunluğunu öğrenmemiz gerektiği zaman ise bufferend label'ının değerinden buffer label'ının değerini çıkarmamız (bufferend-buffer) yeterlidir, ikinci rutinde ise programa bazı kısıtlamalar getirdik. Yani başlarken uzunluğun 20 değer olduğunu (move.l #20,d0) ile kaydettik diğer bir deyişle formatladık.
Şu anda birinci rutinin çok daha kullanışlı olduğunu ve ikincisine kısıtlamalı olduğu için hiçbir zaman gerek duyulmayacağı fikrine kapılabilirsiniz. Veya ikisinin de çok az farkları olduğu ve sonuçları nasıl olsa aynı olduğu için kullanımlarında fark olmadığını düşünebilirsiniz. Ama ikisinin de kullanım alanları farklı şekilde olabiliyor. Program yazarken ana programdaki görevlerine göre kullanım farklılıkları ve kolaylıkları gösterebiliyorlar. Biz de bizim için hangisi gerekli ise onu kullanıyoruz. Zaten bu tür döngü işlemlerinde döngüye şekil verirken ne tür şekilde kullanacağımız kendiliğinden ortaya çıkıyor.
Bu arada unutmadan ikinci örnek hakkında bir şey söylemek istiyorum. Rutinin sonucunda işlemci bölgenin 15'inci byte'ında 5 değerini yakalayınca şöyle davranacaktır. Üçüncü satırdaki cmp komutunun etkisi ile A0'daki buffer değeri her defasında bir değer artacaktır. Ve RTS komutuna yani programın sonucuna olan dallanma dördüncü satırda gerçekleştiği için program sona erdiğinde 5 değerinin hafıza bölgesinde yerini gösteren A0 registerinde +1 değeri bulunmaktadır.
Yine iki örnek için bir açıklama daha getirelim, iki rutine de başlarken buffer label'ını ve gösterdiği hafıza bölgesinin değerini A0 registerine atadık. Bu A0 registerinin rutin içerisindeki birinci görevi olmaktadır. Daha sonra programın akışı ile döngü içerisinde bu registeri bir sayaç olarak kullandık. Bize her döngü dilimi içerisinde arama bölgesinde bir sonraki byte'ın hafıza adresinin değerini verdi. Bu da onun ikinci görevi oldu.
Şimdi yeni bir rutin ve örnek dizisine geçiyoruz. Bu sefer amacımız bir önceki örnek gibi, fakat daha kapsamlı olarak bir byte'ın aratılması yerine birkaç byte'ın birden, bir hafıza bölgesinde aranılmasını içeriyor.
Bir hafıza bölgesinde birden çok byte'ı aynı anda aramanın binbir türlü yolu vardır. Ve herkes coding stiline göre kendine göre yöntemler gerçekleştirebilir. Ben size bir kısa bir de uzun yoldan bu işi nasıl yapacağınızı ifade etmeye çalışacağım.
Örneğimizde amacımız PERİ kelimesini arama bölgesi içerisinden bulup, başlangıç adresini belirlemek olacaktır.
ÖRNEK PROGRAM 4a:
1 START:
LEA
Buffer,A0
2
LEA
bufferend A1
3 Ll:
CMP.B
#"P",(A0)
4
BNE
L2
5
CMP.B
#"E",1(A0)
6
BNE
L2
7
CMP.B
#"R",2(A0)
8
BNE
L2
9
CMP.B
#'T'3(A0)
10
BNE
L2
11
JMP
L3
12 L2:
ADD.L
#1A0
13
CMRL
A1,A0
14
BNE
L1
15 L3:
RTS
16 buffer
dc.b'ABCHGKPOIDF
PEGFGDPERIDFDSFRETRYREY"
17 bufferend:
SATIR-1:
START: LEA buffer,A0 Rutinimizin başlangıç satırında bir adres atama komutu (LEA = Load Effective Address) bulunmaktadır. Görevi ise Buffer label'ının gösterdiği hafıza adresini A0 adres registerine atamak olmaktadır. Programdaki görevi ise aranacak kelimenin bulunduğu hafıza adresinin başlangıcını adres registeri üzerinden kullanmak. Yani amacımız buffer isimli sabit hafıza adresini, bir adres registeri içeriğine atayıp rahat bir şekilde, ona değişken bir şekil vermek oluyor.
SATIR-2:
LEA bufferend,A1 ikinci komutumuz ise yine birincisi gibi bir atama işlemi yapar, bufferend isimli hafıza adresini temsil eden label olarak A1 adres registerine atanmaktadır.
SATIR-3:
L1: CMP.B #"P",(A0) Üçüncü satıra gelince, okumayı durdurup bu satıra şöyle yan gözle bir bakın. Karşınızda mükemmel bir mimari düşünüş eseri durmaktadır! Nasıl mı? İşte şöyle...
Assembler'ın en büyük ve benim en hoşuma giden kolaylıklarından biri karşınızda.
CMP.B #"P",(A0) Bu satırda bulunan komuttaki #"P" ifadesinin ne olduğunu anlayabildiniz mi?
Bu ifadenin anlamı #"P" = #80 = #$50 olmaktadır. Yani komuttaki yerine koyarsak.
a) Cmp.b #"P",(a0) b) Cmp.b #80,(a0) c) Cmp.b #$50,(a0)
Gördüğünüz gibi bu satırların anlamı P harfinin ASCII kod değerini veya 80 değerini veya $50 değerini A0 adres registerinin gösterdiği adresteki değer ile karşılaştırmak oluyor, işte bu kullanım assembler program listesinde harf kullanılması gereken yerlerde onların ASCII kodlarını kullanmak yerine, gerçek kendi harfleri ile ifadesini sağlıyor. Assembler derleyicisi listedeki bu tür kullanımların bulunduğu satırları obje kodunu üretirken yapısı gereği ASCII kodlarını tespit edip kod içerisinde bulunması gerektiği yerlere koymaktadır.
Tabii bu kullanım assemblerda konabilecek bütün komutlar için geçerlidir. Bir kaç örnek verilmek gerekirse;
a) Move.b #"P",(a0) b) Move.l #"SCSI",(a0) c) Jmp "SCSI" d) Add.l #"A",d0 e) Add.l #"A","SCSI"
gibi kullanabiliriz. Çift tırnak içerisinde verdiğimiz ifadeler komutların değer kullanım formatları gereği üç türlü olmalıdır. Yani BYTE veya WORD veya LONG WORD olmalıdır.
Yukarıda a kullanımında P harfinin değeri olan 80, tek harf olarak kullanıldığından move komutu byte modunda kullanılmıştır, b kullanımında ise SCSI'yı oluşturan LONGWORD değer, yani; $53,$43,$53,$49 sayıları ifade edilmektedir, c kullanımında ise SCSI yalın bir değer olmaktan çıkarak direkt bir adres ifadesi olmaktadır. Ve bu adres değeri de $53435349 adresi olmaktadır, d kullanımında ise A değeri yani ASCII 65 değeri olarak işlem görmektedir. Son olarak e kullanımında 65 değeri SCSI yani $53435349 adresindeki sayı ile toplanması şeklinde işlem görecektir.
Bu sayıdaki yazı dizimizi noktalıyoruz. Konumuza ve son örneğimizin diğer satırlarının açıklamasına diğer sayıda devam edeceğiz. Hoşçakalın.