Bölüm 5: emu8086.inc Kütüphanesi

Mikroişlemciler
INCLUDE direktifiMakrolarPUTC · GOTOXYPRINT · PRINTNİmleç makrolarıProsedürlerSayı okuma / yazmaPTHIS ve yığınCALL ve RETMakro mu, prosedür mü?
Sercan KÜLCÜ · Giresun Üniversitesi · Elektrik-Elektronik Mühendisliği

Giriş

Öğrenme Çıktıları

INCLUDE

Bir kütüphane dosyasını kaynağa eklemeyi ve derleyicinin dosyayı nerede aradığını açıklamak.

Makrolar

PUTC, GOTOXY, PRINT, PRINTN gibi makroların neye genişlediğini göstermek.

Prosedürler

DEFINE_… ve CALL ile hazır prosedürleri (sayı okuma/yazma, dizge yazdırma) kullanmak.

CALL / RET

Yığın üzerinden dönüş adresinin nasıl saklandığını izlemek; makro ile prosedürü boyut ve hız açısından karşılaştırmak.

Kütüphane ve INCLUDE

Ortak Fonksiyonlar Kütüphanesi

  • Programlamayı kolaylaştırmak için sık kullanılan bazı işlevler hazır olarak bulunur.
  • Başka dosyalarda tanımlanan işlevler INCLUDE direktifi ile kullanılır.
  • emu8086.inc içindeki herhangi bir işlevi kullanmak için kaynak dosyanın başına include 'emu8086.inc' yazılır.
  • Bu kütüphane, emu8086 platformunda sık kullanılan ekran, klavye ve sayı işlevlerini içerir.
include'emu8086.inc'1. kaynak dosyanınklasörüyoksa2. emu8086 kurulumundakiInc klasörübulunan dosyanın içeriği, include satırının yerine eklenir

INCLUDE bir direktiftir: işlemci komutu üretmez, derleme sırasında metin ekler.

Kütüphane ve INCLUDE

Kütüphanede Neler Var?

Makrolar · doğrudan kullanılır
MakroGörev
PUTC chartek karakter yaz
GOTOXY col, rowimleci konumlandır
PRINT stringdizge yaz
PRINTN stringdizge yaz + yeni satır
CURSOROFFimleci gizle
CURSORONimleci göster
Prosedürler · DEFINE_ + CALL
ProsedürGörev
PRINT_STRINGDS:SI'deki dizgeyi yaz
PTHISCALL'dan sonraki dizgeyi yaz
GET_STRINGklavyeden dizge oku
CLEAR_SCREENekranı temizle
SCAN_NUMsayı oku → CX
PRINT_NUMAX'i işaretli yaz
PRINT_NUM_UNSAX'i işaretsiz yaz
Makrolar kullanıldıkları yere kopyalanır; prosedürler bir kez tanımlanır ve CALL ile çağrılır.

Makrolar

Ekran Makroları

MakroNe yapar?Arka planda
PUTC charGeçerli imleç konumuna bir ASCII karakter yazar.INT 10h, AH = 0Eh
GOTOXY col, rowİmleci verilen sütun ve satıra konumlandırır.INT 10h, AH = 02h
PRINT stringVerilen dizgeyi yazar.0Eh ile karakter karakter
PRINTN stringPRINT ile aynı; sonuna CR + LF (13, 10) ekleyerek yeni satıra geçer.0Eh ile karakter karakter
CURSOROFFMetin imlecini kapatır (gizler).INT 10h, AH = 01h
CURSORONMetin imlecini açar.INT 10h, AH = 01h
Makro parametreleri sabit, yazmaç ya da değişken olabilir: PUTC 65, PUTC 'B', PUTC AL.

Makrolar

PUTC ve GOTOXY Neye Genişler?

PUTC 'A'

PUSH AX        ; AX'i koru
MOV  AL, 'A'   ; parametre
MOV  AH, 0Eh   ; teletype
INT  10h
POP  AX        ; AX geri

5 komut, 8 bayt. Yazmaçlar korunur; çağıran kod etkilenmez.

GOTOXY 10, 5

PUSH AX
PUSH BX
PUSH DX
MOV  AH, 02h   ; imleç konumla
MOV  DH, 5     ; satır (row)
MOV  DL, 10    ; sütun (col)
MOV  BH, 0     ; sayfa 0
INT  10h
POP  DX
POP  BX
POP  AX
Parametre sırası sütun, satırdır (x, y); BIOS'ta sütun DL'ye, satır DH'ye gider. Sol üst köşe (0, 0), sağ alt köşe (79, 24).

Makrolar

PRINT Neye Genişler?

PUSH AX
PUSH SI
JMP  skip_dcl          ; veriyi atla
s_dcl DB 'Hello World!', 0
skip_dcl:
LEA  SI, s_dcl
next_char:
MOV  AL, CS:[SI]       ; karakteri al
CMP  AL, 0
JZ   printed           ; 0 → bitti
INC  SI
MOV  AH, 0Eh
INT  10h
JMP  next_char
printed:
POP  SI
POP  AX
  • Dizge, makronun açıldığı yere, kodun içine gömülür; JMP ile üzerinden atlanır.
  • Sonuna 0 eklenir (null ile sonlanan dizge).
  • Etiketler LOCAL tanımlıdır: makro her açılışta yeni adlar alır, çakışma olmaz.
  • Veri kod kesiminde olduğu için CS:[SI] kesim öneki kullanılır.
  • PRINTN'de dizge DB sdat, 13, 10, 0 olarak tanımlanır.

Makrolar · Kodu adım adım çalıştır

Kod İzleme: PRINT, GOTOXY, PUTC

0510150123456… 80 × 25

include: makro tanımları derleme sırasında eklenir; kod üretilmez.

0510150123456… 80 × 25

ORG 100h: COM programı 100h'den başlar.

0510150123456Hello World!… 80 × 25

PRINT: 12 karakter (0–11. sütun) yazılır; imleç 12. sütunda.

0510150123456Hello World!… 80 × 25

GOTOXY 10, 5: imleç 10. sütun, 5. satıra taşınır. Ekrana bir şey yazılmaz.

0510150123456Hello World!A… 80 × 25

PUTC 65: 65 = 41h = 'A' yazılır, imleç 11. sütuna geçer.

0510150123456Hello World!AB… 80 × 25

PUTC 'B': karakter sabiti doğrudan verilebilir.

0510150123456Hello World!AB… 80 × 25

RET: DOS'a dönüş. END yalnızca derleyiciye “kaynak bitti” der.

include emu8086.inc
ORG    100h
PRINT 'Hello World!'
GOTOXY 10, 5
PUTC 65           ; 65 - is an ASCII code for 'A'
PUTC 'B'
RET               ; return to operating system.
END               ; directive to stop the compiler.

Makrolar

PRINTN ve İmleç Makroları

PRINT ile PRINTN

PRINT 'a' ardından PRINT 'b' → ab aynı satırda.

PRINTN 'a' ardından PRINT 'b' → a ve alt satırda b.

CR (13) imleci satır başına, LF (10) bir alt satıra götürür; yeni satır için ikisi birden gerekir.

CURSOROFF / CURSORON

İmleç şeklini ayarlayan INT 10h, AH = 01h kullanılır: CH = başlangıç, CL = bitiş tarama satırı.

CH'nin 5. biti 1 yapılınca imleç gizlenir; CURSORON normal şekli geri yükler.

Oyun veya menü ekranlarında yanıp sönen imleci saklamak için kullanılır.

PRINTN 'Ad Soyad:'   ; yazıp alt satıra geç
CURSOROFF
GOTOXY 30, 12
PRINT 'Merhaba'
CURSORON

Makrolar

Demo: GOTOXY Ekranı

80 × 25 metin ekranında bir hücreye tıklayın: imleci oraya götüren makro, BIOS yazmaçları ve video belleğindeki karşılığı gösterilir.

Prosedürler

Prosedür Kullanımı: DEFINE_ ve CALL

include 'emu8086.inc'
ORG 100h
    ...
    CALL print_num   ; kullan
    ...
RET

DEFINE_PRINT_NUM    ; tanımla
DEFINE_PRINT_NUM_UNS
END
  • Her prosedür, END direktifinden önce kendi DEFINE_… makrosuyla bir kez tanımlanmalıdır.
  • DEFINE_… makrosu, prosedürün kodunu bulunduğu yere açar.
  • Tanımlar genellikle RET'ten sonra konur: aksi hâlde işlemci akış içinde prosedür koduna girer.
  • Kodda prosedür adıyla CALL yapılır; adlar küçük harfle yazılır (print_num).
  • PRINT_NUM, işini yaparken PRINT_NUM_UNS'yi çağırır; ikisi birlikte tanımlanmalıdır.

Prosedürler

Dizge Prosedürleri

PRINT_STRING
  • Null (0) ile sonlanan dizgeyi geçerli imleç konumuna yazar.
  • Dizgenin adresi DS:SI'den alınır.
  • Tanım: DEFINE_PRINT_STRING.
LEA  SI, msg
CALL print_string
msg DB 'Merhaba', 0
PTHIS
  • Null ile sonlanan dizgeyi yazar.
  • Dizgenin adresini yığından alır: dizge, CALL'dan hemen sonra tanımlanır.
  • Tanım: DEFINE_PTHIS.
CALL pthis
DB 'Merhaba', 0
; buradan devam eder
PRINT makrosu her kullanımda kodu kopyalar; PRINT_STRING ve PTHIS ise tek kopya kodla istenen sayıda dizge yazar.

Prosedürler

GET_STRING ve CLEAR_SCREEN

GET_STRING
  • Kullanıcıdan null ile sonlanan bir dizge alır.
  • Dizge DS:DI'deki tampona (buffer) yazılır.
  • DX = tampon boyutu; sona 0 eklendiği için en fazla DX − 1 karakter alınır.
  • Enter tuşuna basılınca giriş durur.
  • Tanım: DEFINE_GET_STRING.
CLEAR_SCREEN
  • Ekranı temizler ve imleci en üste (0, 0) taşır.
  • Tanım: DEFINE_CLEAR_SCREEN.
LEA  DI, ad          ; tampon
MOV  DX, 20          ; boyut
CALL get_string
CALL clear_screen
LEA  SI, ad
CALL print_string
RET
ad DB 20 DUP(0)

Prosedürler

Sayı Prosedürleri

ProsedürNe yapar?Girdi / çıktıTanım
SCAN_NUMKlavyeden çok haneli, işaretli bir sayı okur (Enter ile biter).çıktı: CXDEFINE_SCAN_NUM
PRINT_NUMAX'teki işaretli sayıyı onluk olarak yazar.girdi: AXDEFINE_PRINT_NUM + DEFINE_PRINT_NUM_UNS
PRINT_NUM_UNSAX'teki işaretsiz sayıyı yazar.girdi: AXDEFINE_PRINT_NUM_UNS
İşaretli aralık

16 bit: −32 768 … 32 767

İşaretsiz aralık

16 bit: 0 … 65 535

Aynı bitler

FFE7h = −25 (işaretli) = 65 511 (işaretsiz)

Prosedürler

Demo: PRINT_NUM ve PRINT_NUM_UNS

AX'e bir değer verin (onluk, negatif ya da h ekli onaltılık): iki prosedürün ekrana ne yazacağını görün.

Örnek Program · Kodu adım adım çalıştır

Kod İzleme: Sayı Oku ve Yazdır

LEA SI, msg1: SI ← msg1 dizgesinin bağıl konumu (offset).

CALL print_string: dönüş adresi yığına itilir (SP = FFFCh); prosedür DS:SI'deki dizgeyi 0'a kadar yazar.

CALL scan_num: kullanıcı -25 yazıp Enter'a basar; karakterler ekrana yankılanır.

Dönüş: CX = FFE7h = −25 (ikiye tümleyen). SP yeniden FFFEh.

MOV AX, CX: PRINT_NUM sayıyı AX'te bekler.

CALL pthis: yığına itilen “dönüş adresi” aslında bir sonraki satırdaki dizgenin adresidir.

PTHIS dizgeyi yazar: 13, 10 (yeni satır) ve “You have entered: ”. Sonra dönüş adresini 0'ın ötesine taşır.

Dönüş, dizgenin sonrasına yapılır. CALL print_num: AX = FFE7h işaretli yazılır.

RET: DOS'a dönüş. Satır 11–17 veri ve prosedür tanımlarıdır, akışta çalıştırılmaz.

include 'emu8086.inc'
ORG    100h
LEA    SI, msg1       ; ask for the number
CALL   print_string   ;
CALL   scan_num       ; get number in CX.
MOV    AX, CX         ; copy the number to AX.
CALL   pthis          ; print the following string:
DB  13, 10, 'You have entered: ', 0
CALL   print_num      ; print number in AX.
RET                   ; return to operating system.
msg1   DB  'Enter the number: ', 0
DEFINE_SCAN_NUM
DEFINE_PRINT_STRING
DEFINE_PRINT_NUM
DEFINE_PRINT_NUM_UNS  ; required for print_num.
DEFINE_PTHIS
END                   ; directive to stop the compiler.

Örnek Program

PTHIS Nasıl Çalışır?

CALL pthis0D0A'You have entered: '00CALL print_numkod kesimi (bellek, soldan sağa artan adres)yığın:—← SP = FFFEh

Bellekte CALL pthis'in hemen ardından dizge baytları gelir; dizge 0 ile biter.

CALL pthis0D0A'You have entered: '00CALL print_numkod kesimi (bellek, soldan sağa artan adres)yığın:adres(0Dh)← SP = FFFCh

CALL, “sonraki komutun” adresini iter. Bu adres, dizgenin ilk baytı olan 0Dh'nin adresidir.

CALL pthis0D0A'You have entered: '00CALL print_numkod kesimi (bellek, soldan sağa artan adres)yığın:adres(0Dh)← SP = FFFCh

PTHIS yığındaki adresi okur ve oradan itibaren baytları 0'a kadar ekrana yazar.

CALL pthis0D0A'You have entered: '00CALL print_numkod kesimi (bellek, soldan sağa artan adres)yığın:adres(CALL print_num)← SP = FFFCh

Yığındaki adres, 0 baytının bir ötesiyle değiştirilir: artık CALL print_num'u gösterir.

CALL pthis0D0A'You have entered: '00CALL print_numkod kesimi (bellek, soldan sağa artan adres)yığın:—← SP = FFFEh

RET: işlemci dizgeyi atlayıp CALL print_num'dan devam eder. Veri komut sanılıp çalıştırılmaz.

Bu teknik, dönüş adresinin yığında saklandığı bilgisine dayanır. Benzer şekilde PRINT makrosu da veriyi kod içine gömer ama JMP ile atlar.

Örnek Program

Sık Yapılan Hatalar

DEFINE_ unutmak

CALL scan_num var ama DEFINE_SCAN_NUM yoksa derleyici etiketi bulamaz: undefined hatası.

DEFINE_'yi akışa koymak

Tanımlar ORG 100h'nin hemen altına konursa işlemci prosedür kodunu ana program sanarak çalıştırır. Yeri: RET ile END arası.

Dizgeyi 0 ile bitirmemek

PRINT_STRING ve PTHIS 0 görene kadar yazar; 0 yoksa bellekteki rastgele baytlar ekrana dökülür.

Yazmaçları varsaymak

Prosedürler sonuçlarını belirli yazmaçlarda döndürür (SCAN_NUM → CX). Başka yazmaçtaki değerin korunduğu belgede yazmıyorsa varsayılmamalıdır.

CALL Komutu

CALL Komutu

  • Derleyici ilk aşamada DEFINE_… tanımlarını işler; bunlar prosedürlere genişleyen normal makrolardır.
  • CALL talimatı prosedürleri çağırmak için kullanılır.
  • Derleyici CALL'daki prosedür adını, prosedürün bulunduğu adrese dönüştürür.
  • CALL yürütüldüğünde dönüş adresi yığına itilir ve kontrol prosedüre aktarılır; prosedür RET ile geri döner.
  • Prosedür kodu dosyada bir kez bulunur: aynı prosedür 100 kez çağrılsa da gövdesi çoğalmaz; her çağrı yalnızca 3 baytlık bir CALL ekler.
  • Bu yaklaşım yürütülebilir dosyanın boyutunu küçük tutar.

CALL Komutu

CALL ve RET Adım Adım

IP0110hSPFFFEhyığınFFFAhFFFChFFFEh← SP

CALL print_num 0110h adresinde, 3 bayt (E8 + 16 bit bağıl uzaklık). Sonraki komut 0113h'de. (Örnek adresler.)

IP0110hSPFFFChyığınFFFAhFFFCh0113h← SPFFFEh

1) SP ← SP − 2, dönüş adresi 0113h yığına yazılır.

IP0200hSPFFFChyığınFFFAhFFFCh0113h← SPFFFEh

2) IP ← prosedürün adresi (ör. 0200h). Prosedür çalışır.

IP0113hSPFFFEhyığınFFFAhFFFCh0113hFFFEh← SP

RET: IP ← [SP] = 0113h, SP ← SP + 2. Ana program kaldığı yerden sürer.

Prosedür içinde PUSH edilen her değer RET'ten önce POP edilmelidir; yoksa RET yanlış adrese döner.

CALL Komutu

Makro mu, Prosedür mü?

3 × PUTC (makro)8 bayt kod8 bayt kod8 bayt kodtoplam 24 bayt; her kullanım +83 × CALL (prosedür)CALLCALLCALLprosedür (1 kopya)her kullanım +3 bayt
ÖlçütMakroProsedür
Ne zaman işlenir?derleme sırasında metin olarak açılırçalışma sırasında CALL ile çağrılır
Kod boyutuher kullanımda kopyatek kopya + çağrı başına 3 bayt
HızCALL/RET yükü yok, biraz daha hızlıher çağrıda yığın işlemi
Parametremakro argümanlarıyazmaçlar veya yığın
emu8086.incPUTC, GOTOXY, PRINT …PRINT_NUM, SCAN_NUM … (DEFINE_ gerekli)

Özet

Özet

INCLUDE

include 'emu8086.inc': önce kaynak klasörü, sonra Inc klasörü aranır.

Makrolar

PUTC, GOTOXY (col, row), PRINT, PRINTN, CURSOROFF, CURSORON: INT 10h üzerine kurulu, kullanıldıkları yere açılır.

Prosedürler

END'den önce DEFINE_…, kullanırken CALL. Girdi/çıktı yazmaçları: SI, DI, DX, CX, AX.

Sayılar

SCAN_NUM → CX (işaretli); PRINT_NUM AX'i işaretli, PRINT_NUM_UNS işaretsiz yazar.

PTHIS

Dizge CALL'dan hemen sonra; prosedür dönüş adresini dizgenin sonrasına taşır.

CALL / RET

CALL dönüş adresini iter ve atlar; RET adresi çekip döner. Prosedür kodu bir kez bulunur.

Özet

Alıştırmalar

  1. Ekranın ortasına (sütun 35, satır 12) “MERKEZ” yazan programı makrolarla yazın.
  2. Kullanıcıdan iki sayı okuyup toplamını yazdıran programı yazın.
  3. PRINT makrosu 10 kez kullanılırsa dizgeler ve döngü kodu kaç kez bulunur? PRINT_STRING ile karşılaştırın.
  4. AX = 8000h iken PRINT_NUM ve PRINT_NUM_UNS ne yazar?
  5. DEFINE_PTHIS, ORG 100h'nin hemen altına yazılırsa ne olur?
  6. Kullanıcının adını GET_STRING ile alıp “Merhaba, ad!” yazdırın.
İpuçları / cevaplar
  1. GOTOXY 35, 12 ardından PRINT 'MERKEZ', RET.
  2. CALL scan_num → MOV BX,CX → CALL scan_num → MOV AX,BX, ADD AX,CX, CALL print_num; DEFINE_SCAN_NUM, DEFINE_PRINT_NUM, DEFINE_PRINT_NUM_UNS.
  3. PRINT: 10 kopya (her biri kendi dizgesi ve döngüsüyle). PRINT_STRING: döngü 1 kez, 10 dizge + 10 × (LEA + CALL).
  4. PRINT_NUM: −32768, PRINT_NUM_UNS: 32768.
  5. Program başlarken doğrudan PTHIS'in koduna girer; yığında geçerli dizge adresi olmadığından anlamsız çıktı veya kilitlenme.
  6. LEA DI,ad / MOV DX,20 / CALL get_string, PRINT 'Merhaba, ', LEA SI,ad / CALL print_string, PUTC '!'.

Özet

Kaynaklar

  • emu8086 yardım belgeleri: “emu8086.inc — library of common functions” ve “Macros” sayfaları.
  • emu8086 kurulumundaki Inc/emu8086.inc dosyası (makro ve prosedürlerin kaynak kodu).
  • B. B. Brey, The Intel Microprocessors, 8. baskı, Pearson, 2009 — prosedürler ve makrolar.
  • A. S. Tanenbaum, Modern Operating Systems, 3. baskı, Pearson, 2008 (kaynak sunumda faydalanılmıştır).

Son

Bölüm 5: emu8086.inc Kütüphanesi
Sonraki bölüm: Bölüm 6: Aritmetik ve Mantıksal İşlemler
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.