Bölüm 9: Makrolar
Giriş
Öğrenme Çıktıları
MACRO / ENDM ile parametreli makro yazmak.
Derleyicinin makro çağrısını nasıl metin olarak açtığını izlemek.
Boyut, hız ve kullanım açısından iki yöntemi karşılaştırıp doğru olanı seçmek.
Etiket içeren makrolarda Duplicate declaration hatasını önlemek.
PUTC (ekrana karakter) ve POWER (üs alma) makrolarını adım adım çalıştırmak.
Parametre yan etkileri, yazmaç bozulması ve sınır durumlarını fark etmek.
Makro Kavramı
Makrolar
- Makrolar prosedürlere benzer: tekrar eden bir kod parçasına ad verir.
- Ancak çalışma zamanında çağrılmazlar; derleme aşamasında gerçek komutlarla değiştirilen geçici yapılardır.
- Kod içinde kullanılmayan makrolar görmezden gelinir: makine koduna hiçbir bayt eklemezler.
- Makrolar, prosedürlerden farklı olarak, kullanıldıkları kodun üzerinde (önce) tanımlanmalıdır; derleyici makroyu tanımadan çağrıyı açamaz.
Makro Kavramı
Makro Tanımlama
name MACRO [parametreler,...] <komutlar> ENDM
Makro Kavramı
Makro Genişletme Adım Adım
MyMacro MACRO p1, p2, p3 MOV AX, p1 MOV BX, p2 MOV CX, p3 ENDM
Önce tanım: derleyici MyMacro'yu ve p1, p2, p3 parametrelerini öğrenir; henüz kod üretmez.
MyMacro 1, 2, 3MOV AX, p1 MOV BX, p2 MOV CX, p3
MyMacro 1, 2, 3: p1 = 1, p2 = 2, p3 = 3.
MyMacro 1, 2, 3MOV AX, 1 MOV BX, p2 MOV CX, p3
1. satır: MOV AX, p1 → MOV AX, 1.
MyMacro 1, 2, 3MOV AX, 1 MOV BX, 2 MOV CX, p3
2. satır: MOV BX, p2 → MOV BX, 2.
MyMacro 1, 2, 3MOV AX, 1 MOV BX, 2 MOV CX, 3
3. satır: MOV CX, p3 → MOV CX, 3.
MyMacro 4, 5, DXMOV AX, p1 MOV BX, p2 MOV CX, p3
MyMacro 4, 5, DX: p1 = 4, p2 = 5, p3 = DX.
MyMacro 4, 5, DXMOV AX, 4 MOV BX, p2 MOV CX, p3
1. satır: MOV AX, p1 → MOV AX, 4.
MyMacro 4, 5, DXMOV AX, 4 MOV BX, 5 MOV CX, p3
2. satır: MOV BX, p2 → MOV BX, 5.
MyMacro 4, 5, DXMOV AX, 4 MOV BX, 5 MOV CX, DX
3. satır: MOV CX, p3 → MOV CX, DX.
MOV AX, 00001h MOV BX, 00002h MOV CX, 00003h MOV AX, 00004h MOV BX, 00005h MOV CX, DX RET
Sonuç (emu8086'nın gösterdiği gibi): iki çağrı 6 komuta açıldı. Sabitler onaltılık olarak gösterilir.
Makro Kavramı · Kodu adım adım çalıştır
Açılmış Kodu Çalıştırmak
Yükleme sonrası CX = 0012h: programın boyutu 5 × 3 + 2 + 1 = 18 bayt. SP = FFFEh.
AX = 0001h.
BX = 0002h.
CX = 0003h. Birinci çağrı bitti; hiçbir CALL/RET yok.
AX = 0004h.
BX = 0005h.
CX ← DX = 0000h (DX başlangıçta sıfır).
RET: program biter.
ORG 100h MOV AX, 00001h ; MyMacro 1, 2, 3 MOV BX, 00002h MOV CX, 00003h MOV AX, 00004h ; MyMacro 4, 5, DX MOV BX, 00005h MOV CX, DX RET
SP hiç değişmedi: makro yığın kullanmaz (gövdesinde PUSH/POP yoksa). Prosedürde CALL/RET SP'yi değiştirirdi.
Makro Kavramı · Kodu adım adım çalıştır
Tuzak: Parametreler Metindir
MyMacro BX, AX, 0 çağrısının amacı: AX ← BX, BX ← AX (takas) sanılıyor.
AX = 1111h.
BX = 2222h.
Makronun 1. satırı: MOV AX, p1 → MOV AX, BX → AX = 2222h.
2. satır: MOV BX, AX. Programcı “eski AX” (1111h) bekliyordu ama AX bir önceki satırda değişti → BX = 2222h.
CX = 0.
Sonuç: AX = BX = 2222h. 1111h kayboldu.
ORG 100h MOV AX, 1111h MOV BX, 2222h MOV AX, BX ; MyMacro BX,AX,0 MOV BX, AX ; p2 = AX (bozuk!) MOV CX, 0 RET
Makro parametresi bir değişken değil, yerine konan bir metindir: değeri, kullanıldığı satırda okunur. Makro gövdesinin değiştirdiği yazmaçlar parametre olarak geçirilmemelidir.
Makro Kavramı
Parametre Türleri: Aynı Makro, Farklı Açılım
TOPLA MACRO x, y, z MOV AX, x ADD AX, y MOV z, AX ENDM
| Çağrı | Açılım | Durum |
|---|---|---|
TOPLA a, b, c | MOV AX, a · ADD AX, b · MOV c, AX | geçerli |
TOPLA BX, 5, CX | MOV AX, BX · ADD AX, 5 · MOV CX, AX | geçerli |
TOPLA a, b, 7 | MOV 7, AX | hedef sabit olamaz |
TOPLA k, b, c (k DB) | MOV AX, k | boyut uyuşmazlığı |
TOPLA a, b | MOV , AX | eksik işlenen |
- Parametre yazmaç, değişken, sabit ya da ifade olabilir; hangisinin kabul edileceğini makro değil, açılımda oluşan komut belirler.
- Hata makronun kullanıldığı satırda bildirilir, ama nedeni gövdedeki bir satırdır: hata iletisini anlamak için açılım düşünülür.
- Eksik parametre boş metinle değiştirilir. MASM'da
z:REQparametreyi zorunlu yapar,y:=<1>ona varsayılan değer verir.
Makro Kavramı · Kodu adım adım çalıştır
Kod İzleme: TOPLA ile İki Açılım
org 100h mov ax, a ; TOPLA a, b, c add ax, b mov c, ax mov ax, c ; TOPLA c, 5, d add ax, 5 mov d, ax ret a dw 1200h b dw 0345h c dw ? d dw ?
1. açılım TOPLA a, b, c: x yerine a yazıldı → AX = 1200h.
ADD AX, b: 1200h + 0345h = 1545h.
MOV c, AX: c = 1545h. Bellekten belleğe toplama olmadığı için makro AX'i ara yazmaç olarak kullanır.
2. açılım TOPLA c, 5, d: aynı gövde, başka metin. x = c → AX = 1545h.
y = 5 bu kez bir sabit: ADD AX, 5 → AX = 154Ah.
MOV d, AX: d = 154Ah. İki açılım 6 komut üretti; makro AX'i bozdu ve bunu çağıran bilmelidir.
RET. Toplam kod 2 × (3 + 4 + 3) = 20 bayt + RET; her TOPLA kullanımı gövdeyi yeniden yazar.
Makro: TOPLA MACRO x, y, z / MOV AX, x / ADD AX, y / MOV z, AX / ENDM. Kod, iki çağrının açılmış hâlidir.
Makro Kavramı
Parametre İfadeleri ve İşlem Önceliği
ALAN MACRO en, boy MOV AX, en*boy ENDM FARK MACRO p, q MOV CX, p-q ENDM
- Parametre bir değer değil metindir: ifadenin yerine konduğu satırda işlem önceliği yeniden uygulanır.
ALAN 2+3, 4: çarpma önce yapılır, AX = 14 olur; beklenen 20 idi.- Çözüm: gövdede parametreleri paranteze almak:
(en)*(boy),(p)-(q).
| Çağrı | Açılım | Sonuç |
|---|---|---|
ALAN 2+3, 4 | MOV AX, 2+3*4 | AX = 000Eh (14) |
düzeltilmiş ALAN 2+3, 4 | MOV AX, (2+3)*(4) | AX = 0014h (20) |
FARK 10, 4+1 | MOV CX, 10-4+1 | CX = 0007h |
ALAN BX, 2 | MOV AX, BX*2 | hata: * yalnız sabitlerle |
İfade derleme anında hesaplanır; yazmaç içeren bir ifade çalışma anında çarpılamaz. Yazmacı çarpmak için MUL ya da SHL komutu gerekir.
Makro Kavramı
Etkileşimli Makro Genişletici
Boyut karşılaştırması: aynı gövde bir prosedür olsaydı gövde + RET bir kez yazılır, her kullanım yalnızca 3 baytlık bir CALL olurdu.
Makro ve Prosedür
Prosedür Özellikleri
- Prosedür
CALLkomutu ile çağrılır. - Prosedür belleğin belirli bir adresinde tek kopya olarak bulunur.
- Çağrıldığında CPU kontrolü bu bölgeye aktarır;
RETile kontrol programın ana kısmına geri döner. - Dönüş adresini saklamak için yığın kullanılır.
- Aynı prosedür 100 kez çağrılırsa dosya boyutu çok az artar: yakın
CALLkomutu yalnızca 3 bayt yer kaplar. - Parametre geçirmek için yığın veya yazmaçlar kullanılır.
- Prosedür, adı ve
ENDPdirektifiyle sonlandırılır.
YAZ_A PROC PUSH AX MOV AL, 'A' MOV AH, 0Eh INT 10h POP AX RET YAZ_A ENDP CALL YAZ_A ; 3 bayt CALL YAZ_A ; 3 bayt
Makro ve Prosedür
Makro Özellikleri
- Makro sadece adı yazılarak kullanılır (CALL yok).
- Makrolar program kodunda doğrudan genişletilir.
- Aynı makro 100 kez kullanılırsa derleyici makroyu 100 kez genişletir; çalıştırılabilir dosyanın boyutu giderek büyür.
- Parametre geçirmek için makro adının ardından değerler yazılır.
- Makroyu sonlandırmak için
ENDMdirektifi yeterlidir. - Makro tanımında etiketler varsa ve makro birden fazla kez kullanılırsa Duplicate declaration hatası alınabilir; değişken, etiket ve prosedür adları için
LOCALdirektifi kullanılır.
PUTC MACRO char PUSH AX MOV AL, char MOV AH, 0Eh INT 10h POP AX ENDM PUTC 'A' ; 8 bayt PUTC 'B' ; 8 bayt
Makro ve Prosedür
Makro mu, Prosedür mü?
| Ölçüt | Makro | Prosedür |
|---|---|---|
| Ne zaman işlenir? | Derleme zamanı (metin değiştirme) | Çalışma zamanı (CALL/RET) |
| Kullanım | PUTC 'A' | CALL YAZ_A |
| Kod boyutu | Her kullanımda gövde kadar artar | Gövde 1 kez + her çağrı 3 bayt |
| Hız | Daha hızlı (CALL/RET ve yığın erişimi yok) | Her çağrıda CALL + RET maliyeti |
| Parametre | Ad sonrası değerler, metin olarak | Yazmaç ya da yığın |
| Yığın | Kullanmaz (gövde kullanmadıkça) | Dönüş adresi için kullanır |
| Tanım yeri | Kullanımdan önce | Herhangi bir yerde |
| Bitiş | ENDM | ad ENDP |
| Etiketler | LOCAL gerekir | Sorun yok (tek kopya) |
Kural: kısa ve sık kullanılan, hız gerektiren kod → makro; uzun ve çok yerde kullanılan kod → prosedür. İkisi birlikte de kullanılabilir: makro, parametreleri yazmaçlara koyup bir prosedürü çağırabilir.
Makro ve Prosedür
Boyut Hesabı: 1, 10 ve 100 Kullanım
1 kullanım: makro 8 bayt; prosedür 8 bayt gövde + 1 bayt RET + 3 bayt CALL = 12 bayt. Burada makro daha küçük.
10 kullanım: makro 80 bayt; prosedür 9 + 30 = 39 bayt.
100 kullanım: makro 800 bayt; prosedür 9 + 300 = 309 bayt. Fark kullanım sayısıyla büyür.
Makro ve Prosedür
Karar Alıştırması: Makro mu, Prosedür mü?
Beş durum: her birinde kod boyutu (kaç kopya?) ve hız (CALL/RET maliyeti) birlikte tartılır.
1. Ekrana tek karakter yazmak, 200 yerde → kısa gövde (8 bayt) ama 200 kopya = 1600 bayt. Prosedür 9 + 600 = 609 bayt. Boyut önemliyse prosedür; hız önemliyse makro.
2. İki yazmacı takas eden 1 satırlık iş → XCHG zaten tek komut; makro okunabilirlik için yeterli, prosedür gereksiz yük (makro).
3. 300 baytlık sıralama algoritması, 3 yerde → makro 900 bayt, prosedür 300 + 9 bayt: açıkça prosedür.
4. Sıkı bir döngü içinde her turda yapılan 2 komutluk iş → CALL/RET maliyeti işin kendisinden büyük: makro.
5. Parametreleri yazmaçlara koyup ortak kodu çağırmak → ikisi birlikte: makro YAZ_SAYI x → MOV AX, x + CALL print_num (emu8086.inc de böyle çalışır).
Makro ve Prosedür · Kodu adım adım çalıştır
Makro Parametreyi Hazırlar, Prosedür İşi Yapar
org 100h mov al, 4 ; YAZ_RAKAM 4 call rakam_yaz mov al, 7 ; YAZ_RAKAM 7 call rakam_yaz ret rakam_yaz: add al, '0' ; rakam → ASCII mov ah, 0Eh int 10h ret
Makro YAZ_RAKAM d, derlemede iki satıra açıldı: mov al, d + call rakam_yaz. Gövdenin asıl işi prosedürde, tek kopya. İlk kopya: AL = 04h.
CALL, dönüş adresini (CALL'dan sonraki komut, 0105h) yığına iter: SP = FFFCh. IP ← rakam_yaz.
Prosedür gövdesi: AL + 30h = 34h ('4'), AH = 0Eh, INT 10h ekrana 4 yazar.
RET yığından 0105h'yi çeker: SP = FFFEh, yürütme makronun ilk kopyasından sonraki satırda sürer.
Makronun ikinci kopyası: AL = 07h. Her kopya yalnız 5 bayt (2 bayt MOV + 3 bayt CALL).
Yine CALL: bu kez dönüş adresi 010Ah. Aynı prosedür, farklı dönüş adresi.
Aynı tek prosedür kopyası çalışır: AL = 37h ('7'). Ekranda 47.
RET → 010Ah. Yığın yine boşaldı; çağıranın SP'si korunmuş oldu.
Program biter. Makro parametreyi yazmaca koydu, ortak kod prosedürde: emu8086.inc'deki DEFINE_... + makro ikilisi de böyle çalışır.
Makro: YAZ_RAKAM MACRO d / mov al, d / call rakam_yaz / ENDM. 10 kullanımda kod 10 × 5 + 7 = 57 bayt; gövdenin tamamı makroda olsaydı 10 × 8 = 80 bayt.
Makro ve Prosedür · Kodu adım adım çalıştır
Kod İzleme: Makronun İçindeki RET
org 100h mov al, 'A' ; YAZ 'A' mov ah, 0Eh int 10h ret ; makronun RET'i mov al, 'B' ; YAZ 'B' mov ah, 0Eh int 10h ret ret
1. açılım YAZ 'A': AL = 41h.
AH = 0Eh, INT 10h: ekrana A yazıldı.
Makronun gövdesindeki RET: makro bir prosedür değildir, CALL ile gelinmedi. RET yığının tepesindeki 0000h'yi alır: IP = 0000h.
PSP:0000'daki INT 20h programı bitirir. YAZ 'B' ve ana programın kendi RET'i yürütülmedi; ekranda yalnızca A var.
Makro: YAZ MACRO c / MOV AL, c / MOV AH, 0Eh / INT 10h / RET / ENDM. RET yalnızca CALL ile girilen prosedürde anlamlıdır.
Makro ve Prosedür
Makro, EQU ve Prosedür
| Ölçüt | EQU | Makro | Prosedür |
|---|---|---|---|
| Ne yerine konur? | tek bir değer ya da ifade | bir ya da daha çok satır | hiçbir şey; CALL ile gidilir |
| Parametre | yok | var (metin) | yazmaç, yığın ya da bellek |
| Ne zaman? | derleme | derleme | çalışma |
| Bellekte | yer kaplamaz | her kullanımda gövde kadar | tek kopya + her çağrıda 3 bayt |
| Örnek | SATIR EQU 80 | PUTC 'A' | CALL PRINT_NUM |
Etiketler ve LOCAL
Etiket İçeren Makrolar: Sorun
Gövdesinde etiket olan bir makro iki kez kullanılırsa, genişletme sonucu aynı etiket iki kez tanımlanmış olur:
BEKLE MACRO ilk: DEC CX JNZ ilk ENDM MOV CX, 5 BEKLE MOV CX, 9 BEKLE
Derleyicinin gördüğü:
MOV CX, 5 ilk: DEC CX ; 1. kopya JNZ ilk MOV CX, 9 ilk: DEC CX ; 2. kopya ! JNZ ilk
ilk:'e atlanacağı belirsizdir.Etiketler ve LOCAL
Çözüm: LOCAL Direktifi
MyMacro2 MACRO LOCAL label1, label2 CMP AX, 2 JE label1 CMP AX, 3 JE label2 label1: INC AX label2: ADD AX, 2 ENDM
LOCAL, makro gövdesinin ilk satırı olmalıdır (MACRO satırından hemen sonra).- Listelenen adlar her genişletmede derleyici tarafından benzersiz adlarla değiştirilir (MASM'da
??0000,??0001, …). - Etiketler, değişkenler ve makro içindeki prosedür adları için kullanılabilir.
| Kullanım | label1 | label2 |
|---|---|---|
| 1. MyMacro2 | ??0000 | ??0001 |
| 2. MyMacro2 | ??0002 | ??0003 |
Etiketler ve LOCAL · Kodu adım adım çalıştır
MyMacro2'yi Çalıştırmak (AX = 2)
Etiketler LOCAL ile benzersiz adlara dönüştürülmüş hâli.
AX = 2.
CMP AX, 2: 2 − 2 = 0 → ZF = 1.
JE: ZF = 1 olduğundan ??0000'a (label1) atlanır.
INC AX → AX = 3.
ADD AX, 2 → AX = 5.
Program biter.
ORG 100h MOV AX, 2 ; MyMacro2 öncesi CMP AX, 2 ; MyMacro2 başlar JE ??0000 CMP AX, 3 JE ??0001 ??0000: INC AX ??0001: ADD AX, 2 ; MyMacro2 biter RET
AX = 3 olsaydı label2'ye atlar, AX = 5 olur. AX = 7 → hiçbir atlama yok, label1'e düşer: INC + ADD → AX = 10.
Etiketler ve LOCAL · Kodu adım adım çalıştır
LOCAL ile İki Kopya, İki Ayrı Etiket
org 100h mov cx, 3 ??0000: dec cx ; BEKLE (1. kopya) jnz ??0000 mov cx, 2 ??0001: dec cx ; BEKLE (2. kopya) jnz ??0001 ret
Makroda LOCAL ilk var. Derleyici her açılımda ilk yerine yeni bir ad koydu: 1. kopyada ??0000 (0103h), 2. kopyada ??0001 (0109h). CX = 3.
1. tur: DEC CX → CX = 2, ZF = 0; JNZ ??0000'a (0103h) döner.
2. tur: CX = 1, ZF = 0 → yine 0103h.
3. tur: CX = 0 → ZF = 1; JNZ atlamaz, 1. kopya bitti.
CX = 2: makronun ikinci kullanımı için sayaç.
2. kopya: DEC CX → CX = 1; JNZ ??0001'e (0109h) döner, 1. kopyanın etiketine değil.
CX = 0, ZF = 1: döngü biter. Aynı etiket adı iki kez tanımlanmadığı için Duplicate declaration hatası yok.
RET: program biter. Toplam 3 + 2 = 5 DEC çalıştı.
Makro: BEKLE MACRO / LOCAL ilk / ilk: DEC CX / JNZ ilk / ENDM; ana programda mov cx, 3 / BEKLE / mov cx, 2 / BEKLE.
Etiketler ve LOCAL · Kodu adım adım çalıştır
Kod İzleme: BUYUK p, q, r ile İki Açılım
org 100h mov ax, x ; BUYUK x, y, m1 cmp ax, y jge ??0000 mov ax, y ??0000: mov m1, ax mov ax, y ; BUYUK y, 4, m2 cmp ax, 4 jge ??0001 mov ax, 4 ??0001: mov m2, ax ret x dw 7 y dw -3 m1 dw ? m2 dw ?
1. açılım BUYUK x, y, m1: AX = x = 7.
CMP AX, y: 7 − (−3) = 10 → SF = 0, OF = 0.
JGE işaretli karşılaştırır: SF = OF → ??0000 etiketine atlanır. LOCAL bu kopyadaki ok etiketini ??0000 yaptı.
m1 = 7.
2. açılım BUYUK y, 4, m2: AX = y = −3 (FFFDh); CMP AX, 4: −3 − 4 = −7 → SF = 1, OF = 0.
SF ≠ OF: JGE ??0001 atlamaz. İkinci kopyanın etiketi ??0001; birincisiyle çakışmadı.
Büyük olan ikinci parametre: AX = 4.
m2 = 4; RET. JAE kullanılsaydı −3 (FFFDh) işaretsiz 65533 sayılır ve m2 = −3 olurdu.
Makro: BUYUK MACRO p, q, r / LOCAL ok / MOV AX, p / CMP AX, q / JGE ok / MOV AX, q / ok: MOV r, AX / ENDM. Birinci çağrıda parametreler değişken, ikincide biri sabit.
PUTC Makrosu
PUTC: Ekrana Karakter Yazdırma
; karakter yazar, imleci ilerletir PUTC MACRO char push ax mov al, char mov ah, 0eh int 10h pop ax ENDM ORG 100h START: PUTC 'A' PUTC 'B' RET
- Makro,
charparametresindeki karakteri yazar ve imleci bir sağa ilerletir. INT 10h,AH = 0Eh: BIOS teletype çıkışı. AL'deki karakter imleç konumuna yazılır; 0Dh, 0Ah, 07h, 08h gibi kontrol karakterleri yorumlanır.push ax/pop ax: makro AX'i kullandığı için çağıranın AX değerini korur. Makronun “görünmez” yan etkisi kalmaz.- Bu makro
emu8086.inckütüphanesindeki PUTC makrosunun aynısıdır.
PUTC 'B''den sonraki RET programı bitirir; o olmasa işlemci program sonundaki rastgele baytları çalıştırmaya devam ederdi.PUTC Makrosu · Kodu adım adım çalıştır
PUTC 'A' ve PUTC 'B' Çalışırken
Yükleme sonrası CX = 0011h: 2 kopya × 8 bayt + RET = 17 bayt. Ekran boş.
AX (0000h) yığına saklanır; SP = FFFCh.
AL = 41h ('A').
AH = 0Eh → AX = 0E41h.
INT 10h: ekrana A yazılır, imleç ilerler.
AX eski değerine (0000h) döner; SP = FFFEh.
İkinci kopya: AX yine saklanır.
AL = 42h ('B').
AX = 0E42h.
INT 10h: ekranda AB.
AX = 0000h; makro çağıranın AX'ini bozmadı.
RET: program biter.
ORG 100h push ax ; PUTC 'A' mov al, 'A' mov ah, 0eh int 10h pop ax push ax ; PUTC 'B' mov al, 'B' mov ah, 0eh int 10h pop ax RET
emu8086 ekran penceresinde (80×25) çıktı: AB. Her PUTC kopyası 8 bayt: 50 B0 41 B4 0E CD 10 58.
PUTC Makrosu
Yazmaç Saklama Makroları
SAKLA MACRO PUSH AX PUSH BX PUSH CX PUSH DX ENDM GERI_AL MACRO POP DX POP CX POP BX POP AX ENDM
- PUTC'deki
push ax/pop axkalıbı büyük makrolarda dört yazmaca çıkar; bu kalıp da bir makro olabilir. - GERI_AL, SAKLA'nın ters sırasıyla çeker: yığın son giren ilk çıkar. Sıra aynı yazılırsa AX ile DX, BX ile CX yer değiştirir.
- Her SAKLA + GERI_AL çifti 8 bayt ve 8 yığın erişimi ekler: 8086'da 4 × 11 (PUSH) + 4 × 8 (POP) = 76 saat turu.
- Bu iki makro bir gövdede birlikte kullanılmalıdır; araya giren bir koşullu çıkış yığını dengesiz bırakır.
PUSHA / POPA bütün genel amaçlı yazmaçları saklar; 8086'da bu komutlar yoktur, bu yüzden makroyla yazılır.POWER Makrosu
Üs Alma: POWER Makrosu
POWER macro b, e, r mov ax, 1 ; geçici sonuç = 1 mov cx, e ; üs → CX mov bx, b ; taban → BX powerloop: mul bx ; DX:AX = AX * BX loop powerloop ; e kez tekrarla mov r, ax ; sonucu yaz endm
ORG 100h start: POWER base, exponent, result ret base dw 2 ; taban exponent dw 3 ; üs result dw ? ; sonuç
- Sonuç r = be: AX'i 1 ile başlatıp e kez taban ile çarpar.
MUL BX: DX:AX ← AX × BX (işaretsiz). DX de değişir!LOOP: CX'i 1 azaltır, CX ≠ 0 ise etikete atlar.
POWER Makrosu · Kodu adım adım çalıştır
POWER 2, 3 Adım Adım
Veriler kodun hemen arkasında: base = 0113h, exponent = 0115h, result = 0117h (kod 19 bayt).
AX = 1 (geçici sonuç).
CX ← [0115h] = 3 (üs).
BX ← [0113h] = 2 (taban).
1. tur: DX:AX = 1 × 2 = 0002h.
LOOP: CX = 2 ≠ 0 → powerloop.
2. tur: AX = 2 × 2 = 0004h.
LOOP: CX = 1 → powerloop.
3. tur: AX = 4 × 2 = 0008h.
LOOP: CX = 0 → döngüden çık.
result ← AX: [0117h] = 0008h = 23.
RET: program biter.
ORG 100h mov ax, 1 ; POWER açılımı mov cx, exponent mov bx, base powerloop: mul bx loop powerloop mov result, ax ret base dw 2 ; 0113h exponent dw 3 ; 0115h result dw ? ; 0117h
MUL sonrası DX = 0 olduğundan CF = OF = 0. Sonuç 16 biti aşsaydı (ör. 216) DX ≠ 0 ve CF = 1 olurdu; makro yalnızca AX'i sakladığı için sonuç yanlış olurdu.
POWER Makrosu
POWER Makrosunun Tuzakları
LOOP önce CX'i azaltır: FFFFh → döngü 65 536 kez döner; b0 = 1 olmalıydı. Çözüm: JCXZ.powerloop LOCAL değil: ikinci kullanımda Duplicate declaration.Düzeltilmiş sürüm:
POWER MACRO b, e, r LOCAL p_loop, p_end PUSH BX PUSH CX PUSH DX MOV AX, 1 MOV CX, e MOV BX, b JCXZ p_end ; e = 0 → 1 p_loop: MUL BX LOOP p_loop p_end: MOV r, AX POP DX POP CX POP BX ENDM
POWER Makrosu · Kodu adım adım çalıştır
Düzeltilmiş POWER 7, 2 Adım Adım
org 100h push bx ; POWER açılımı push cx push dx mov ax, 1 mov cx, us mov bx, taban jcxz ??0001 ; us = 0 → 1 ??0000: mul bx loop ??0000 ??0001: mov sonuc, ax pop dx pop cx pop bx ret taban dw 7 us dw 2 sonuc dw ?
Çağıranın yazmaçları BX = 0AAAh, CX = 0BBBh, DX = 0CCCh. Düzeltilmiş makro önce üçünü de yığına saklar: SP = FFF8h.
AX = 1, CX ← us = 2, BX ← taban = 7. Yazmaçlar artık makronun.
JCXZ: CX = 2 ≠ 0, atlama yok. us = 0 olsaydı doğrudan ??0001'e gidilir, sonuç 1 olurdu (LOOP'un 65 536 tur tuzağı yok).
1. MUL: DX:AX = 1 × 7 = 0007h. MUL, DX'i de yazar (DX = 0).
LOOP: CX = 1 ≠ 0 → ??0000.
2. MUL: DX:AX = 7 × 7 = 0031h = 49.
LOOP: CX = 0 → döngüden çık.
sonuc ← AX: [011Fh] = 0031h = 7². Etiketler LOCAL ile ??0000 / ??0001 adını aldı.
Üç POP, PUSH'ların tersi sırada: DX = 0CCCh, CX = 0BBBh, BX = 0AAAh geri geldi; SP = FFFEh. Makro yalnız AX'i değiştirdi.
RET: program biter.
Kod, düzeltilmiş makronun POWER taban, us, sonuc açılımıdır; LOCAL adları derleyicinin ürettiği biçimde gösterildi.
POWER Makrosu
İç İçe Makrolar
PRINT2 MACRO c1, c2 PUTC c1 PUTC c2 ENDM PRINT2 'O', 'K'
; 1. düzey: PRINT2 açılır
PUTC 'O'
PUTC 'K'Önce dıştaki makro açılır: gövdedeki iki satır da birer makro çağrısıdır.
PRINT2 MACRO c1, c2 PUTC c1 PUTC c2 ENDM PRINT2 'O', 'K'
; 2. düzey: PUTC'ler açılır push ax mov al, 'O' mov ah, 0eh int 10h pop ax push ax mov al, 'K' mov ah, 0eh int 10h pop ax
Derleyici iç makroları da açar: tek satırlık çağrı 10 komut (16 bayt) üretir. Ekranda: OK.
PUTC, PRINT2'den önce tanımlanmış olmalıdır. Bir makro kendini çağırırsa (özyineleme) koşullu derleme olmadan genişletme hiç bitmez.
Makro Kütüphaneleri
Makro Kütüphaneleri: emu8086.inc
- Sık kullanılan makrolar ayrı bir dosyada toplanır ve
INCLUDEile programa eklenir. - Kullanılmayan makrolar kod üretmediği için büyük bir kütüphaneyi eklemek dosyayı büyütmez.
- Makrolar başka makroları çağırabilir (iç içe genişletme).
- Kütüphane, makroların yanında
DEFINE_...makrolarıyla prosedürler de sunar (ör.DEFINE_SCAN_NUM): ikisi birlikte kullanılır.
| Makro | İşlev |
|---|---|
PUTC ch | Tek karakter yazar |
PRINT 'metin' | Metin yazar |
PRINTN 'metin' | Metin + yeni satır |
GOTOXY x, y | İmleci taşır |
CURSOROFF | İmleci gizler |
include 'emu8086.inc' ORG 100h GOTOXY 10, 5 PRINT 'Merhaba' PUTC '!' RET
Makro Kütüphaneleri · Kodu adım adım çalıştır
emu8086.inc: PRINT 'OK' Açılımı
org 100h push ax ; PRINT 'OK' push si jmp ??0003 ; veriyi atla ??0001 db 'OK', 0 ??0003: lea si, ??0001 ??0000: mov al, cs:[si] cmp al, 0 jz ??0002 inc si mov ah, 0Eh int 10h jmp ??0000 ??0002: pop si pop ax ret
PRINT 'OK' açıldı. Makro AX ve SI'yı kullanacağı için önce ikisini yığına saklar.
Dizgi, makronun içinde kodun arasına gömülü (0104h: 'O', 'K', 0). JMP, işlemcinin bu baytları komut sanmaması için üzerinden atlar.
SI ← dizginin adresi 0104h. LOCAL adları (??0000…) her PRINT için yenidir.
AL ← CS:[SI] = 4Fh ('O'). Dizgi kod kesiminde olduğu için CS: öneki kullanılır. 0 değil → devam.
SI bir ilerler, INT 10h/0Eh ekrana O yazar, JMP döngü başına döner.
AL = 4Bh ('K'); sonlandırıcı değil.
Ekranda OK.
AL = 00h: CMP → ZF = 1, JZ çıkışa atlar.
SI ve AX yığından geri alınır: makro çağıranın yazmaçlarını bozmadı.
RET: program biter. Tek satırlık PRINT 'OK', 27 baytlık kod + veri üretti.
emu8086.inc'deki PRINT makrosunun açılımı (LOCAL adları derleyicinin ürettiği biçimde). PRINTN aynı işi yapıp sonuna CR LF ekler.
İleri Makro Özellikleri
Makro İşleçleri: &, <>, !, %, ;;
| İşleç | Görevi | Örnek ve açılım |
|---|---|---|
& | parametreyi bitişik metne ekler | SIFIRLA MACRO r · XOR r&X, r&X → SIFIRLA B: XOR BX, BX |
< > | virgül ya da boşluk içeren metni tek parametre yapar | YAZ <'A', 'B'> → tek parametre: 'A', 'B' |
! | sonraki karakteri sıradan karakter yapar | <5 !> 3> → metin: 5 > 3 |
% | ifadeyi hesaplar, sonucu metin olarak koyar | GOSTER %(4*8) → parametre 32 |
;; | makro açıklaması; açılıma kopyalanmaz | ;; yalnızca tanımda görünür |
- Bu işleçler MASM ve TASM'da tanımlıdır; makro tanımı yazılırken metni parçalamak ve birleştirmek için kullanılır.
&olmadanrXtek bir ad sayılır ve parametre yerine konmaz.- Tek
;ile yazılan açıklama her açılımda tekrarlanır;;;liste dosyasını kısaltır.
İleri Makro Özellikleri
Tekrar Blokları: REPT, IRP, IRPC
REPT 3 SHL AX, 1 ENDMaçılım: üç kez
SHL AX, 1 (AX × 8)IRP r, <AX,BX,CX> PUSH r ENDMaçılım:
PUSH AX · PUSH BX · PUSH CXIRPC c, OK PUTC '&c' ENDMaçılım:
PUTC 'O' · PUTC 'K'- Tekrar blokları adı olmayan, tanımlandığı yerde hemen açılan makrolardır; ENDM ile biter.
- REPT gövdeyi n kez, IRP listedeki her öğe için bir kez, IRPC metindeki her karakter için bir kez yazar. MASM 6'da IRP ve IRPC, FOR ve FORC adlarıyla da yazılır.
- Döngü sayacına gerek kalmaz ama kod büyür:
REPT 100gövdeyi 100 kez kopyalar. Sayaçlı LOOP ise tek kopyayla çalışır. - En yaygın kullanım derleme anında tablo üretmektir: sayı tabloları, maskeler, kare ya da sinüs değerleri.
İleri Makro Özellikleri · Kodu adım adım çalıştır
Kod İzleme: REPT ile Üretilen Kare Tablosu
org 100h lea bx, kare mov al, 5 xlat ; AL = kare[5] mov dl, al mov al, 7 xlat ; AL = kare[7] add al, dl ret kare db 0, 1, 4, 9, 16, 25, 36, 49
Tablo REPT ile derlemede üretildi; bellekte yalnızca 8 bayt var. LEA BX, kare: BX = 010Fh.
AL = 5: karesi aranan sayı, aynı zamanda tablo indisi.
XLAT: AL ← [BX + AL] = kare+5 = 25 (19h). Çarpma yapılmadı, tablodan okundu.
DL = 19h: ilk sonuç saklanır.
AL = 7, XLAT → kare+7 = 49 (31h).
ADD AL, DL: 49 + 25 = 74 (4Ah) = 5² + 7².
RET. XLAT 11 saat turudur; 8086'da 8 bitlik MUL ise 70–77 tur sürer.
Tablonun kaynağı: n = 0 / kare LABEL BYTE / REPT 8 / DB n*n / n = n + 1 / ENDM. Kod bu bloğun açılmış hâliyle çalıştırıldı. EQU sabiti değiştirilemez; = ile tanımlanan sembol her turda yeni değer alır.
İleri Makro Özellikleri
Koşullu Derleme: IF, IFB, IFIDN
KAYDIR MACRO r, n IFB <n> ;; n verilmedi mi? SHL r, 1 ELSE MOV CL, n SHL r, CL ENDIF ENDM HATA_AYIKLA = 1 IF HATA_AYIKLA PUTC '*' ; yalnız deneme sürümünde ENDIF
| Direktif | Koşul |
|---|---|
IF ifade | ifade ≠ 0 |
IFE ifade | ifade = 0 |
IFB <p> | parametre boş |
IFNB <p> | parametre dolu |
IFIDN <a>,<b> | iki metin aynı |
IFDIF <a>,<b> | iki metin farklı |
- Koşul derleme anında değerlendirilir: seçilmeyen dal makine koduna hiç girmez, çalışma anında bayrak ya da atlama yoktur.
EXITMaçılımı o noktada bitirir;IFIDN <r>, <CX>ile gövdenin bozacağı yazmacın parametre olarak verilmesi yakalanabilir.
İleri Makro Özellikleri · Kodu adım adım çalıştır
Kod İzleme: KAYDIR AX ve KAYDIR BX, 3
org 100h mov ax, 5 mov bx, 3 shl ax, 1 ; KAYDIR AX mov cl, 3 ; KAYDIR BX, 3 shl bx, cl ret
AX = 5, BX = 3: kaydırılacak değerler.
KAYDIR AX: n boş, IFB dalı seçildi → tek komut SHL AX, 1. AX = 000Ah (5 × 2).
KAYDIR BX, 3: n = 3, ELSE dalı → MOV CL, 3. 8086'da 1'den büyük kaydırma sayısı CL'de verilir.
SHL BX, CL: 3 × 2³ = 0018h (24). Aynı makro iki farklı uzunlukta kod üretti: 2 bayt ve 4 bayt.
RET. KAYDIR AX açılımı CL'ye dokunmadı; yalnız ikinci açılım CL'yi değiştirdi.
Kod, KAYDIR makrosunun iki çağrısının açılmış hâlidir. Hangi dalın yazılacağına derleyici karar verdi; çalışma anında karşılaştırma yapılmadı.
İleri Makro Özellikleri
Makroda Hata Ayıklama ve Listeleme
.LISTMACROALL açılımın bütün satırlarını, .LISTMACRO yalnız kod üretenleri listeler, .NOLISTMACRO açılımı gizler.??0000 gibi adlar aldığı için öykünücüdeki atlama hedefleri kaynaktaki adlarla görünmez; hangi kopyanın çalıştığını bu numaralar gösterir.İleri Makro Özellikleri
Sık Yapılan Makro Hataları
TOPLA AX, BX, CX gibi çağrılarda gövde parametreyi okunmadan değiştirebilir.en*boy yerine (en)*(boy); aksi hâlde öncelik değişir.REPT 1000 1000 kopya üretir; çalışma anında tekrar için LOOP gerekir.Özet
Özet
| Konu | Hatırlanacak nokta |
|---|---|
| Tanım | ad MACRO p1, p2 … ENDM; kullanımdan önce tanımlanır. |
| Genişletme | Derleme zamanında metin değiştirme; çalışma zamanında CALL yok. |
| Parametreler | Metin olarak yerine konur; değerleri kullanıldıkları satırda okunur. |
| Boyut / hız | Makro: her kullanımda gövde kadar büyür, daha hızlıdır. Prosedür: 3 bayt CALL, CALL/RET maliyeti. |
| LOCAL | Etiketlerin her genişletmede benzersiz olması için gövdenin ilk satırı. |
| Yan etkiler | Makronun değiştirdiği yazmaçlar PUSH/POP ile korunur (PUTC gibi). |
| Sınır durumları | LOOP'tan önce CX = 0 kontrolü (JCXZ). |
Özet
Alıştırmalar
MyMacro AX, BX, CXçağrısının genişletilmiş hâlini yazın. Hangi satır gereksizdir?- İki sözcük değişkeni toplayıp üçüncüsüne yazan
TOPLA a, b, cmakrosunu yazın. - PUTC makrosu 50 kez, aynı işi yapan prosedür 50 kez çağrılırsa kod boyutları ne olur?
- POWER makrosu
POWER base, exponent, resultile iki kez kullanıldığında hangi hata alınır? Nasıl düzeltilir? - exponent = 0 iken orijinal POWER makrosu kaç kez MUL çalıştırır?
- Bir makronun AX dışında hiçbir yazmacı bozmaması için hangi komutlar eklenmelidir?
Cevaplar
MOV AX, AX/MOV BX, BX/MOV CX, CX: üçü de etkisizdir (derlenir ama hiçbir şey değiştirmez).TOPLA MACRO a, b, c/MOV AX, a/ADD AX, b/MOV c, AX/ENDM(bellek–bellek işlemi olmadığı için AX aracı).- Makro: 50 × 8 = 400 bayt. Prosedür: 9 + 50 × 3 = 159 bayt.
- Duplicate declaration (powerloop).
LOCAL powerloopeklenir. - 65 536 kez (CX: 0 → FFFFh → … → 0).
- Gövdenin başında kullanılan yazmaçlar için PUSH, sonunda ters sırada POP.
Özet
Kaynaklar
- emu8086 yardım belgeleri: 8086 Assembler Tutorial, Part 10 (Macros) ve
emu8086.inc. - Microsoft, MASM Programmer's Guide — MACRO, ENDM, LOCAL direktifleri.
- K. R. Irvine, Assembly Language for x86 Processors, 7. baskı, Pearson, 2014 — Bölüm 10 (Makrolar).
- B. B. Brey, The Intel Microprocessors, 8. baskı, Pearson, 2009.