plazma - amatör bilgisayar kültürü

Amiga Assembly Kursu

Şemseddin 'Endo' Moldibi

Şekil 1.

Merhaba, bu sayıdan itibaren sizlerle birlikte Amiga Assembly ile nasıl program yazacağımızı öğreneceğiz. Amiga Assembly ile program yazmak son derece eğlenceli ve kolaydır. Basit bir Assembly kodu aşağıdaki gibidir:

1. Örnek bir program

;basit bir kontrol toplami (checksum) hesaplama 
start:
  move.l  #0,d0	      ;D0 register’ine 
                      ;0 koy

  move.l  #0,d1	      ;D1 register’ine 
                      ;0 koy

  lea	  data(pc),a0 ;data’nin adresini 
                      ;A0’a al. A0 data'ya 
                      ;bakan bir pointer
                      ;olmus oluyor

.loop:
  move.b  (a0)+,d1    ;A0’in gosterdigi 
		      ;adresten bayt olarak 
                      ;oku ve D1’e koy
		      ;A0'i (pointer) bir 
                      ;arttir

	add.l	d1,d0 ;D1’i longword olarak 
                      ;D0’a ekle

	cmp.b	#0,d1 ;D1 ile sifiri 
                      ;karsilastir

	bne.s	.loop ;D1 sifir degilse 
                      ;.loop’a git

	move.l	d0,checksum 
                      ;D0’in degerini 
                      ;checksum’a yaz

	rts	      ;programdan cik

data:
	dc.b ‘plazma‘,0 
                      ;bayt olarak plazma 
                      ;yazisi

	even          ;bir sonraki adresi 
                      ;cift yap

checksum:
	dc.l	0     ;checksum’i tutacak 
                      ;bir longword

Peki bu kodu nasıl derleyeceğiz? Bunun için Amiga’da kullanabileceğiniz pek çok assembler vardır, ben yazılarımda AsmPro V1.17 kullanacağım. Sizler dilerseniz AsmOne, PhxAss ya da başka bir derleyici kullanabilirsiniz. İleriki sayılarda diğer derleyicilere de değineceğiz.

Yukarıdaki kodu derlemek için sisteminizde AsmPro olmalı, eğer yoksa http://www.aminet.net adresinden AsmPro kelimesini aratarak bulabilirsiniz. Aminet’ten indirdiğiniz lha arşivini bir yere açın ve AsmPro’yu çalıştırın.

Sizden “AsmPro:” isimli disketi yerleştirmenizi isteyebilir, bu noktada CLI’den (shell penceresi) “Assign AsmPro: <AsmPro’nun-bulunduğu-klasör>” ile AsmPro’nun olduğu yeri gösterebilirsiniz. Dilerseniz “cancel” deyip geçebilirsiniz. Bu ve benzeri AmigaOS konularını Plazma’nın ileriki sayılarında okuyabilirsiniz.

Nerede kalmıştık? Kodu derlemek için AsmPro’yu açtınız, karşınıza workspace’iniz (çalışma alanınız) için kullanacağınız RAM tipi ve miktarını gireceğiniz bir ekran gelecektir. Burada Fastmem veya Chipmem’i işaretleyip miktar için ~100 KB seçebilirsiniz. (Daha fazla da olabilir) Burada bir hatırlatma yapalım seçtiğimiz miktar sadece derlenmiş program için değil tüm çalışmamız için kullanılacaktır, yani üzerinde çalıştığımız kaynak kodlar (source), derleme işleminde kullanılan etiketler (label) vb., o yüzden biraz yüksek tutmakta fayda vardır.

Devam edelim, herhangi bir şey yazmıyorken Enter tuşuna bastığınızda AsmPro ekranı temizler. Escape tuşu ile hızlıca Editör’e girip çıkabilirsiniz. Bir kez Escape tuşuna basarak editöre girin ve yukarıdaki kodu aynen yazın. Burada kod satırlarının sağ tarafındaki açıklama (comment) kısımlarını da yazabilirsiniz, “;”den sonrasını AsmPro dikkate almayacaktır.

Yazmayı bitirdiğinizde tekrar ESC tuşu ile editörden çıkın. `A` (tek harf, sadece A) komutunu yazıp enter’a bastığınızda derleme işlemi gerçekleşecektir. Ekranda aşağıdaki gibi satırlar görmelisiniz:

>a
Pass 1..
Pass 2..
No Errors
>

Derlediğiniz kodu çalıştırmak için sadece J komutunu (jump) vermeniz yeterlidir. Aynı şekilde “j start” da diyebilirsiniz, bu şekilde kodunuzun belirli bir yerinden itibaren çalıştırmaya başlayabilirsiniz.

Eğer derleme işlemi sırasında hatalar görüyorsanız, konsol ekranında (editör dışında) sağ tuş ile Assembler/Preferences/Assembler menü seçeneğini seçerek derleyici ayarlarına ulaşabilirsiniz. Burada “UCase = LCase” ve “Label :” seçeneklerini seçin. Bunlar sırasıyla büyük/küçük harf farkını kapatır ve etiketlerden sonra “:” konma zorunluluğu getirir.

Eğer hata almaya devam ediyorsanız yazdığınız kodda hata vardır. İyice gözden geçirip tekrar deneyin.

J komutu ile programınızı çalıştırdığınızda aşağıdakine benzer satırlar göreceksiniz;

D0: 00000285 00000000 00000000 00000000
A0: 11390FC1 00000000 00000000 00000000
SSP: 110BCBF4 USP: ...

Bu gördükleriniz 68000’in register’larıdır. D0 register’ının değeri ‘plazma’nın harflerinin hafızadaki değerlerinin toplamı olan $285 olarak görülmekte. Bu şekilde programımız bittiğinde register'ları hangi durumda bıraktığını görebilirsiniz.

Ayrıca aşağıdaki komutlarla 'checksum' adresindeki değeri de görebilirsiniz,

>h checksum

Bu komut ile hafızanın dump’ını alırken, aşağıdaki komutla hafızanın disassemble edilmiş halini görebilirsiniz. Yön tuşlarıyla hafızanın başka bölgelerini de görebilirsiniz.

>d checksum

Benzer şekilde a ve d komutlarını arka arkaya verdiğinizde programınızın derlenmiş halini görebilirsiniz.

Programımızda kullandığımız 68000 komutlarının anlamlarını öğrenmeye başlamadan önce 68000 işlemcinin register’larına kısaca bir göz atalım.

2. 68000 Register’ları

68000 işlemci 16/32 bitlik bir işlemcidir, 32 bitlik veriler üzerinde çalışabilir ancak veri yolu 16 bit, adres yolu 24 bitliktir.

68000 işlemcilerde 8 adet data register’ı, 8 adet adres register’ı, durum register’ı (SR, status register), 2 adet yığın göstergeci (USP ve SSP) ve program göstergeci vardır.

Registerlarla .b, .w, .l gibi sonekler (suffix) kullanarak sırasıyla byte (8 bit), word (16 bit) ve longword (32 bit) işlem yapabilirsiniz. Buna ileride tekrar değineceğiz.

Şekil 2.

Data register’ları D0-D7 olarak yazılır. Aritmetik işlemler ve diğer depolama gereksinimleri için kullanılabilirler. Bu register’lar üzerinde bayt (8 bit), word (16 bit) veya longword (32 bit) olarak işlem yapılabilir.

Adres register’ları A0-A7 şeklindedir. Belirli hafıza adreslerini tutarlar ve o adreslerle ilgili okuma/yazma işlemlerini gerçekleştirmede kullanılırlar (pointers). Data register’ları gibi 32 bitlik register’lardır. Ancak bu register’lar üzerinde sadece word ve longword işlem uygulanabilir. (Örneğin ADD.W #1,A0 kullanılabilir, ancak ADD.B #1,A0 kullanamazsınız.)

Bu noktada ufak bir ek bilgi verelim, data register’larının tümünü dilediğiniz gibi kullanabilirsiniz, ancak adres register’larının sonuncusu olan A7 aynı zamanda yığın göstergecidir (SP, stack pointer) Bu nedenle bu register’ı diğerleri gibi kullanmayın!

Durum register’ı (SR, status register) 2 bayt’tır ve aşağıdaki bilgileri içerir;

  1. 0: C,Carry: Aritmetik ve kaydırma komutlarından etkilenir

  2. 1: V,Overflow: Taşma olduğunda set edilir

  3. 2: Z,Zero: Son işlemin sonucu sıfır ise set olur

  4. 3: N,Negative: Son işlemin sonucu negatif ise set olur

  5. 4: X,Extended: Aritmetik işlemlerden etkilenir

  6. 5-7: Kullanılmıyor

  7. 8: I0: Interrupt bitleri

  8. 9: I1: Interrupt bitleri

  9. 10: I2: Interrupt bitleri

  10. 11: Kullanılmıyor

  11. 12: Kullanılmıyor

  12. 13: S,Supervisor: İşlemcinin hangi modda çalıştığını gösterir

  13. 14: Kullanılmıyor

  14. 15: T,Trace: İşlemci trace modunda ise set durumdadır

SR’nin ilk bayt’ına kullanıcı bayt’ı, ikinciye ise sistem baytı adı verilir.

3. Komutlara Genel Bakış

68000 işlemci komutlarını veri transfer, aritmetik, lojik ve dallanma komutları olarak gruplandırabiliriz. Şimdi bu komutların bir listesini verelim:

Not: Bu liste 68000 komutlarının tam listesi değildir, en sık karşılaşacağımız ve sık kullanılan komutlardır, bunlar dışındaki komutlara pek ihtiyacınız olmayacaktır, yine de yeri geldikçe onlara da değineceğiz.

Listede kullanılacak semboller aşağıdaki gibidir:

label: Etiket veya adres

reg: Register

an: Adres register'ı, A0-A7

dn: Data register'ı, D0-D7

kaynak

hedef

<ea>: Efektif adres

#n: Değer

3.1. Dallanma Komutları

  1. BCC label Koşullu dallanma

  2. BRA label Koşulsuz dallanma (JMP)

  3. BSR label Alt rutine dallanma (RTS ile dönülür)

  4. JMP label Koşulsuz dallanma (BRA)

  5. JSR label Alt rutine dallanma (RTS ile dönülür)

  6. RTS Alt rutinden geri dön (JMP, BSR)

3.2. Aritmetik İşlem Komutları

  1. ADD kaynak,hedef hedefe, kaynağı ekler

  2. ADDI #n,<ea> adrese n ekler

  3. CLR <ea> Adres temizlenir

  4. CMP kaynak,hedef Kaynak ve hedef adreslerin bitleri karşılaştırılır

  5. DIVS kaynak,hedef Hedef (32 bit) Kaynağa (16 bit) bölünür, bölme işleminin sonucu Hedef'in alt-word'ünde, kalan ise üst-word'de tutulur. Bölme işlemi işaret dikkate alınarak yapılır (Signed)

  6. DIVU kaynak,hedef Hedef (32 bit) Kaynağa (16 bit) bölünür, bölme işleminin sonucu Hedef'in alt-word'ünde, kalan ise üst-word'de tutulur. Bölme işlemi işaret dikkate alınmadan yapılır (Unsigned)

  7. MULS kaynak,hedef Hedef (word), kaynak (word) ile çarpılır, sonuç hedef'te tutulur (longword)

  8. MULU kaynak,hedef Hedef (word), kaynak (word) ile çarpılır, sonuç hedef'te tutulur (Unsigned)

  9. NEG <ea> Negatife çevirir (2'lik sistemde komplement)

  10. SUB kaynak,hedef hedef'ten kaynağı çıkarır

  11. SUBI #n,<ea> adresten n çıkarır

  12. TST <ea> Adres test edilir sonuç N ve Z bayraklarını etkiler

3.3. Mantıksal İşlem Komutları

  1. AND kaynak,hedef hedef, kaynak ile mantıksal VE işleminden geçer

  2. ANDI #n,<ea> hedef, n ile mantıksal VE işleminden geçer

  3. EOR kaynak,hedef hedef, kaynak ile mantıksal XOR işleminden geçer

  4. EOR #n,<ea> hedef, n ile mantıksal XOR işleminden geçer

  5. NOT <ea> Adresin mantıksal DEĞİL'i alınır

  6. OR kaynak,hedef hedef, kaynak ile mantıksal VEYA işleminden geçer

  7. ORI #n,<ea> hedef, n ile mantıksal VEYA işleminden geçer

3.4. Bit İşlem Komutları

  1. BCHG #n,<ea> Adresin n'inci bitini ters çevirir (toggle)

  2. BCLR #n,<ea> Adresin n'inci bitini temizler (sıfır yapar)

  3. BSET #n,<ea> Adresin n'inci bitini set eder (bir yapar)

  4. BTST #n,<ea> Adresin n'inci bitini test eder, Z bayrağını etkiler

  5. ASL, ASRn, <ea> Aritmetik sola, sağa kaydırma

  6. LSL, LSRn, <ea> Mantıksal sola, sağa kaydırma

  7. ROL, RORn, <ea> Sola, sağa döndürme

3.5. Veri transfer Komutları

  1. EXG rn,rn İki register'ın içerikleri değiştirilir

  2. LEA <ea>,an an adres register'ına verilen efektif adres yüklenir

  3. MOVE kaynak,hedef Kaynak'tan okunan değer hedef'e yazılır

  4. SWAP dn dn data register'ının alt-word'ü ile üst-word'ünü yer değiştirir

Genel olarak 68000 komutları bunlardır, bu komutların pek çoğunda .b, .w ve .l gibi işlemin kaç bit üzerinden yapılacağını belirleyebiliriz.

Bu sayıda veri transfer komutlarına biraz daha detaylı bakalım, bu diğer komutları da anlamanıza yardımcı olacaktır. Sonraki yazılarımızda diğer komutları da detaylıca inceleyeceğiz.

4. Veri Transfer Komutları

Move komutları veri transfer işlemini gerçekleştirirler. İleride detaylı olarak değineceğimiz pek çok adresleme yöntemini kullanabilirler.

Genel kullanımı "MOVE kaynak,hefef" şeklindedir; aşağıdaki örnekleri inceleyiniz;

move.l	#12,d0	   ;D0 registerine 
                   ;12 degeri koy (32bit)

move.w	#$1234,d7  ;D7'ye $1234 
                   ;degeri koy (word)

Bir register'a word olarak değer girdiğinizde 32 bitlik register'ın alt 16 biti değişir ve üst 16 bit aynı kalır. Örneğin;

move.l #$11223344,d0   ;D0 => $11223344
move.w #$5566,d0       ;D0 => $11225566
move.b #$77,d0         ;D0 => $11225577

NOT: Komutlarda sonek kullanmadığınızda .w olarak kabul edilir. Ancak bazı komutlar sadece 8 bit veya sadece 16 bit destekliyorsa derleyici uygun şekilde derleme yapacaktır.

Şekil 3.

MOVE komutunda kaynak ve hedef birer register olabileceği gibi bir hafıza adresi de (ya da etiket) olabilir. Aşağıdaki örnekleri inceleyin;

move.l src1,d0   ;D0 => $00000001
move.l src1,dst   ;dst[0] => $00000001
move.w src2,d    ;D5 => $XXXX00AA
move.l src2,6    ;D6 => $00AA0055
rts

src1: dc.l $01,$02,$03,$04
src2: dc.w $AA,$55
dst:  dc.l $00,$00,$00,$00

Örneklerde verilen src1, src2 ve dst'nin hafızada nasıl durduklarına bakmak anlamamıza yardımcı olacaktır:

src1:	$00000001,$00000002,$00000003,$00000004

src2:	$00AA,$0055 ;$00AA0055 gibi 
                    ;(longword) düşünebiliriz.

dst:	$00000000,$00000000,$00000000,$00000000

Move komutunun başka bazı kullanım şekilleri de vardır, bunlara ileride tekrar bakacağız. Diğer Veri transfer işlemi yapan komutlar aşağıdadır;

lea <adres>,a0 ;<adres> degerini 
                     ;A0 register'ına koy

Burada dikkat edilmesi gereken nokta, LEA (Load Effective Address) komutu <adres>'in gösterdiği yerdeki değeri değil adresin kendisini register'a yazacaktır. Eğer adresin gösterdiği (point ettiği) değeri okumak istiyorsanız MOVE <adres>,<register> kullanmalısınız.

lea $11223344,a0	;A0 => $11223344

lea src,a1      ;A1 => $1CF6723F (farkli olabilir)
		;"plazma" yazisinin baslangic 
                ;adresi

lea src(PC),a1  ;A1 => $1CF6723F (farkli olabilir)
		;"plazma" yazisinin baslangic 
                ;adresi

src:
    dc.b 'plazma',0

Son örnekte src'nin adresi Program Sayacı (PC)'ye göre relatif olarak okunur ve A6'ya yazılır. Bunun bir öncekinden farkı hafızada longword olarak değil word olarak tutulmasıdır. Yani "bulunduğum yerden XXXX ilerisi" anlamına gelir.

Tahmin edebileceğiniz gibi aşağıdaki 2 kullanım aynı sonucu verecektir:

move.l  #src,a6
lea     src,a6 

Veri transfer komutlarına aşağıdaki 2 komutu da ekleyebiliriz;

exg d0,d1;D0 <=> D1 (degerleri degistirir)
exg a0,d2;A0 <=> D2 (degerleri degistirir)

NOT: exg (exchange) komutu sadece 32 bit destekler, bu durumda exg veya exg.l yazabilirsiniz. Ancak exg.w ve exg.b kullanamazsınız.

swapd0  ;D0'ın alt 16 bit ile üst 
        ;16 bitinin yerini değiştirir 

NOT: swap komutu sadece data register'ları ile çalışır. Adres register'ları ile kullanamazsınız.

Yazımı bitirmeden önce değinmek istediğim son bir konu daha var.

5. Amiga’nın Hafıza Yapısı

Standart bir Amiga 500’te 512 KB Ram vardır. Bu alan $000000 - $7FFFFF arasındadır. Amiga’nın işletim sistemi tümüyle multi tasking olduğundan diskten yüklenen bir program hafızanın (Chip Ram veya varsa Fast Ram) herhangi bir yerine otomatik olarak yerleştirilir ve orada çalıştırılır. Bu nedenle, pek çok eski Amiga programcısının yaptığı gibi, mutlak adres (absolute memory) kullanımından kaçınmak gerekir. Kaldı ki AmigaDOS programınızın yüklenmesi sırasında tüm relocate işlemlerini sizin yerinize otomatik olarak yapacaktır.

Hafızanın genel görünümü aşağıdaki gibidir:

  1. $000000-$07FFFF Chip RAM

  2. $080000-$1FFFFF Rezerve

  3. $200000-$9FFFFF Fast RAM (eğer varsa)

  4. $A00000-$BEFFFF Rezerve

  5. $BFD000-$BFDF00 CIA B (Çift adresler)

  6. $BFE001-$BFEF00 CIA A (Tek adresler)

  7. $C00000-$DFEFFF Rezerve (Genişleme için)

  8. $DFF000-$DFFFFF Custom Chip (Hardware) Register’ları

  9. $E00000-$E7FFFF Rezerve

  10. $E80000-$EFFFFF Genişleme Portları

  11. $F00000-$F7FFFF Rezerve

  12. $F80000-$FFFFFF ROM

Amiga’da kod yazabilmek için hafıza yapısını bilmek kadar Amiga’nın özel çiplerini (custom chips) de bilmek gerekir. Bunlar $DFF000-$DFFFFF arasında bulunan hardware register’ları tarafından kontrol edilen, mikro işlemciden bağımsız çalışabilen, Amiga’nın görüntü ve ses çıkışını kontrol edebilen özel çiplerdir.

Agnus, Denice ve Paula olarak adlandırılan bu özel çipler Amiga’nın, döneminin en güçlü bilgisayarı olmasındaki temel faktörlerdir.

Agnus, blok halindeki hafızanın bir yerden başka bir yere taşınması işlemini (blitting) gerçekleştirir. Bu işlem sırasında aynı zamanda taşınan veri üzerinde çeşitli işlemler (masking, AND, OR) uygulayabilir. Ek olarak çizgi çizebilir ve bir poligonun içini doldurabilir. Denice, ekran görüntüsü ile ilgili tüm işleri üstlenmiştir (copper). Paula ise tüm giriş/çıkış işlemlerinden sorumludur. (Disk sürücü ve ses gibi.) Custom Chip’ler ve hardware register’lara ileriki sayılarda çok daha detaylı olarak değineceğiz.

Bu noktada Fast Ram ve Chip Ram arasındaki farka kısaca değinelim. Chip Ram, 68000’in ve Amiga’nın özel çiplerinin doğrudan erişebileceği bellektir. Fast Ram ise sadece 68000 tarafından erişilebilir. Burada önemli bir nokta; ses ve görüntü ile ilgili tüm verilerin Chip Ram’de durması gerektiğidir.

6. Özet

Amiga Assembly'ye giriş niteliğindeki bu yazımıza burada son veriyoruz. Bu sayıda Amiga'nın register'ları ve data transfer komutlarına değindik, ayrıca kodlarımızı nasıl derleyeceğimizi öğrendik.

Önümüzdeki sayıda yeni örnek kodlarımızla 68000 komutlarını incelemeye devam edeceğiz. Ek olarak Amiga Assembly ile kod yazarken olmazsa olmaz konulardan olan kütüphane (library) fonksiyonlarının nasıl kullanıldığını da göreceğiz.

Ayrıca AsmPro'nun nasıl kullanıldığı ile ilgili ayrı bir yazıyı da bir sonraki sayıda bulabilirsiniz.

Soru ve önerileriniz için bana aşağıdaki adresten ulaşabilirsiniz. Bir sonraki sayıya kadar hoşçakalın.

semseddinm (at) gmail (nokta) com

plazma - 2008