Bölüm 8: 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 hex 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çirmeyin.
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
ENDPyönergesiyle 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; yürütülebilir 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
ENDMyönergesi 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
LOCALyönergesi 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ü?
Her durum için makro mu, prosedür mü seçerdiniz? › ile cevapları açın.
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).
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 Yönergesi
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
Deneyin: AX = 3 → label2'ye atlar, AX = 5 olur. AX = 7 → hiçbir atlama yok, label1'e düşer: INC + ADD → AX = 10.
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 ; eklendi
- 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 denetim 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 sonra RET yoktu; program sonundaki rastgele baytları çalıştırmaya devam ederdi. Buraya RET eklendi.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 (kaynaktaki RET'siz sürümde 0010h). 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.
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
İç İç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.
POWER Makrosu
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
Ö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 koruyun (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 word 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 yönergeleri.
- 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.