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

Mikroişlemciler
INCLUDE direktifiMakrolarPUTC · GOTOXYPRINT · PRINTNİmleç makrolarıProsedürlerSayı okuma / yazmaPTHIS ve yığınÖrnek programlar
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, dizgi yazdırma) kullanmak.

Örnek programlar

Kütüphane makro ve prosedürleriyle sayı okuyan, hesaplayan ve ekranı düzenleyen programlar yazmak.

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

INCLUDE Sonrası Kaynak

; include satırının yerine:
PUTC MACRO char     ; şablon: 0 bayt
  ...
ENDM
GOTOXY MACRO col, row
  ...
ENDM
DEFINE_SCAN_NUM MACRO
  ...               ; prosedür şablonu
ENDM
; programın kendisi:
ORG  100h
PUTC 'A'            ; burada 8 bayt
RET
  • Assembler include satırını dosyanın metniyle değiştirir; ardından kaynağı tek parça olarak derler.
  • Makro tanımı yalnızca bir şablondur: kullanılmayan makro programa tek bayt eklemez.
  • Kod, makronun kullanıldığı yerde oluşur: PUTC 'A' açılınca 8 baytlık komut dizisi üretir.
  • Prosedürler de makroların içinde saklanır (DEFINE_…): DEFINE_ satırı yazılmayan prosedürün kodu programa hiç girmez.
  • Kütüphanenin tamamını eklemek programı büyütmez; program boyutunu yalnızca kullanılan makrolar ve tanımlanan prosedürler belirler.
Çalışan programda “emu8086.inc” diye bir şey yoktur: bellekte yalnızca açılmış komutlar ve tanımlanmış prosedürler bulunur.

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 stringdizgi yaz
PRINTN stringdizgi yaz + yeni satır
CURSOROFFimleci gizle
CURSORONimleci göster
Prosedürler · DEFINE_ + CALL
ProsedürGörev
PRINT_STRINGDS:SI'deki dizgiyi yaz
PTHISCALL'dan sonraki dizgiyi yaz
GET_STRINGklavyeden dizgi 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 dizgiyi 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 · 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.

abCR (13): satır başıLF (10): alt satır
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

PUTC ile Kontrol Karakterleri

KodAdPUTC ile verilince
07hBELHoparlörden kısa bir bip; imleç yerinden oynamaz.
08hBS (geri)İmleç bir sola gider; karakteri silmez. 0. sütunda etkisizdir.
0AhLF (satır besleme)İmleç bir alt satıra iner, sütun değişmez. Son satırda ekran bir satır yukarı kayar.
0DhCR (satır başı)İmleç aynı satırın 0. sütununa döner; satırdaki yazı yerinde kalır.
Yeni satır = CR + LF

PRINTN'in eklediği 13, 10 çifti bu iki hareketin birleşimidir: önce satır başı, sonra alt satır. Sıra ters olsa da (10, 13) imleç aynı yere varır.

Ekranda silme

Bir karakteri silmek için üçlü kullanılır: PUTC 8, PUTC ' ', PUTC 8. Sola git, boşlukla üstüne yaz, yine sola dön. Kütüphanenin geri silme işleyen prosedürleri de ekranı böyle düzeltir.

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

Kod İzleme: CR ve LF Ayrı Ayrı

AL61himleç (sütun, satır)(1, 0)ekrana

AH = 0Eh bu örnekte bir kez yüklenir; INT 10h AL'deki 'a'yı yazar ve imleci bir sağa, (1, 0) konumuna götürür.

AL62himleç (sütun, satır)(2, 0)ekranab

'b' yazılır; imleç (2, 0).

AL0Ahimleç (sütun, satır)(2, 1)ekranab

Yalnız LF (10): imleç bir alt satıra iner ama sütun 2'de kalır: (2, 1).

AL63himleç (sütun, satır)(3, 1)ekranabc

'c' alt satırın 2. sütununa yazılır: satırlar “merdiven” gibi kayar.

AL0Dhimleç (sütun, satır)(0, 1)ekranabc

Yalnız CR (13): imleç aynı satırın başına döner, (0, 1). Satırdaki c silinmez.

AL64himleç (sütun, satır)(1, 1)ekranabdc

'd' 0. sütuna yazılır. Ekranda “ab” ve altında “d c”. Düzgün yeni satır için 13 ve 10 birlikte gerekir.

ORG  100h
MOV  AH, 0Eh     ; teletype
MOV  AL, 'a'
INT  10h
MOV  AL, 'b'
INT  10h
MOV  AL, 10      ; yalnız LF
INT  10h
MOV  AL, 'c'
INT  10h
MOV  AL, 13      ; yalnız CR
INT  10h
MOV  AL, 'd'
INT  10h
RET

Makrolar

Satır Sonu ve Ekran Kayması

  • 0Eh hizmeti 79. sütuna yazdıktan sonra imleci kendiliğinden alt satırın başına geçirir; PRINT uzun bir dizgiyi bu yüzden kesmeden sürdürür.
  • İmleç son satırdayken (24) alt satıra geçmek gerekirse ekranın tamamı bir satır yukarı kayar: en üst satır kaybolur, en alta boş bir satır gelir.
  • Örnek: GOTOXY 75, 24 ardından PRINT 'Merhaba'. “Merha” 75–79. sütunlara yazılır; “ba” için ekran kayar. Sonuçta “Merha” 23. satırda, “ba” 24. satırın başında, imleç (2, 24) konumundadır.
  • GOTOXY ve PUTC sınır denetimi yapmaz: konumlar 0–79 (sütun) ve 0–24 (satır) aralığında tutulmalıdır.
sütun01234…7576777879satır 23…Merhasatır 24ba…imleç

Makrolar

Demo: GOTOXY Ekranı

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

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

Kod İzleme: AAM ve PUTC ile İki Basamak

AH00hAL3AhSPFFFEhyığınFFFC····FFFE0000hSPekran▌

AL = 58 (3Ah). Hedef: sayıyı iki onluk basamak olarak yazmak.

AH05hAL08hSPFFFEhyığınFFFC····FFFE0000hSPekran▌

AAM AL'yi 10'a böler: AH = 5 (onlar), AL = 8 (birler). Yalnızca AL bölündüğü için sayı 0–99 aralığında olmalıdır.

AH35hAL38hSPFFFEhyığınFFFC····FFFE0000hSPekran▌

ADD AX, 3030h iki bayta birden 30h ekler: AH = '5' (35h), AL = '8' (38h).

AH35hAL38hSPFFFChyığınFFFC3538hSPFFFE0000hekran▌

PUTC AH açılımı: önce AX (3538h) yığına, SP = FFFCh.

AH0EhAL35hSPFFFChyığınFFFC3538hSPFFFE0000hekran5▌

MOV AL, AH ve MOV AH, 0Eh: AX = 0E35h, birler basamağı yazmaçtan silindi. INT 10h ekrana 5 yazar.

AH35hAL38hSPFFFEhyığınFFFC····FFFE0000hSPekran5▌

POP AX: AX = 3538h geri geldi; '8' yeniden AL'de.

AH35hAL38hSPFFFEhyığınFFFC····FFFE0000hSPekran58▌

PUTC AL açılımı aynı kalıpla çalışır (MOV AL, AL zararsızdır). Ekranda 58; AX yine 3538h. PUTC AX'i koruduğu için iki basamak ayrı bir yazmaca kopyalanmadan yazılabildi.

ORG  100h
MOV  AL, 58
AAM              ; AH = 5, AL = 8
ADD  AX, 3030h   ; '5', '8'
PUSH AX          ; PUTC AH açılımı
MOV  AL, AH
MOV  AH, 0Eh
INT  10h
POP  AX          ; AX geri: 3538h
PUSH AX          ; PUTC AL açılımı
MOV  AL, AL
MOV  AH, 0Eh
INT  10h
POP  AX
RET

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.
ana kod … CALL print_num …RETprint_numprint_num_unsENDCALLakış yukarıdan aşağıDEFINE_… makrolarının açıldığı yer

Prosedürler

Girdi ve Çıktı Yazmaçları

ProsedürGirdiÇıktıAyrıntı
PRINT_STRINGDS:SI = dizgi adresi—dizgi 0 ile bitmeli
PTHISdizgi CALL'ın hemen ardında—dönüş adresini kendisi ilerletir
GET_STRINGDS:DI = tampon, DX = boyuttamponda 0 ile biten dizgien fazla DX − 1 karakter
CLEAR_SCREEN——imleç (0, 0) konumuna gider
SCAN_NUMklavyeCX (işaretli)CX'teki eski değer kaybolur
PRINT_NUMAX (işaretli)—PRINT_NUM_UNS de tanımlı olmalı
PRINT_NUM_UNSAX (işaretsiz)—0 … 65 535
Kütüphane prosedürleri kullandıkları diğer yazmaçları girişte yığına saklar, çıkışta geri yükler; değişen tek yazmaç çıktı yazmacıdır. CX'te bir döngü sayacı tutan program, SCAN_NUM'ı çağırmadan önce sayacı kendisi saklamalıdır (PUSH CX … POP CX).

Prosedürler

Dizgi Prosedürleri

PRINT_STRING
  • Null (0) ile sonlanan dizgiyi geçerli imleç konumuna yazar.
  • Dizginin 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 dizgiyi yazar.
  • Dizginin adresini yığından alır: dizgi, 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 dizgi yazar.

Prosedürler

Dizgi Yazdırma Yolları

YöntemDizgi nerede?SonlandırıcıAdres nasıl verilir?Uygun olduğu durum
PRINT 'metin'kodun içinde (makro açılımı)0 (makro ekler)makro kendisi alır (CS:SI)bir kez yazılan sabit ileti
PRINT_STRINGveri alanında0DS:SIbirden çok yerde yazılan ya da çalışırken oluşan dizgi
PTHISCALL'ın hemen ardında0yığındaki dönüş adresiçok sayıda kısa sabit ileti
INT 21h, AH = 09hveri alanında'$'DS:DXkütüphanesiz DOS programı
Sonlandırıcı yazılamaz

'$' ile biten dizgide '$' karakteri, 0 ile biten dizgide 0 baytı yazdırılamaz; dizgi orada biter.

Tampon ve yöntem uyumu

GET_STRING'in doldurduğu tampon 0 ile biter: doğrudan PRINT_STRING ile yazılır. AH = 09h ile yazılırsa '$' bulunana kadar bellekteki baytlar ekrana dökülür.

Prosedürler

GET_STRING ve CLEAR_SCREEN

GET_STRING
  • Kullanıcıdan null ile sonlanan bir dizgi alır.
  • Dizgi 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 · Kodu adım adım çalıştır

Kod İzleme: GET_STRING Mantığı (DX = 4)

AL00hDI011EhDX0004hZF0CF0tampon?ad+0?ad+1?ad+2?ad+3DIDX = 4ekran▌

GET_STRING okuma döngüsünün sadeleştirilmiş eşdeğeri (kütüphanedeki sürüm geri silmeyi de işler). DI = tampon, DX = 4 bayt; kullanıcı “Ece” yazacak.

AL00hDI011EhDX0004hZF0CF0tampon?ad+0?ad+1?ad+2?ad+3DIDX = 4ekran▌

4 > 1: tamponda karakter ve sondaki 0 için yer var → JBE atlamaz.

AL45hDI011EhDX0004hZF0CF0tampon?ad+0?ad+1?ad+2?ad+3DIDX = 4ekranE▌

INT 21h, AH = 01h: 'E' okunup ekrana yankılanır. Enter (13) değil → JE atlamaz.

AL45hDI011FhDX0003hZF0CF0tampon'E'ad+0?ad+1?ad+2?ad+3DIDX = 3ekranE▌

'E' tampona yazılır; DI bir ilerler, kalan yer DX = 3.

AL63hDI0120hDX0002hZF0CF0tampon'E'ad+0'c'ad+1?ad+2?ad+3DIDX = 2ekranEc▌

İkinci tur: 'c' okunur ve ad+1'e yazılır; DX = 2.

AL65hDI0121hDX0001hZF0CF0tampon'E'ad+0'c'ad+1'e'ad+2?ad+3DIDX = 1ekranEce▌

Üçüncü tur: 'e' ad+2'ye; DX = 1.

AL65hDI0121hDX0001hZF1CF0tampon'E'ad+0'c'ad+1'e'ad+2?ad+3DIDX = 1ekranEce▌

DX = 1: yalnızca sondaki 0 için yer kaldı → JBE bitti. Kullanıcı daha yazsa da okunmaz: en fazla DX − 1 = 3 karakter.

AL65hDI0121hDX0001hZF1CF0tampon'E'ad+0'c'ad+1'e'ad+20ad+3DIDX = 1ekranEce▌

Dizgi 0 ile sonlandırılır: tampon 'E','c','e',0 → PRINT_STRING ile yazdırılabilir.

LEA  DI, ad      ; tampon adresi
MOV  DX, 4       ; tampon boyutu
oku: CMP DX, 1   ; 0 için 1 bayt kalsın
JBE  bitti
MOV  AH, 01h
INT  21h         ; AL = tuş (yankılı)
CMP  AL, 13      ; Enter mi?
JE   bitti
MOV  [DI], AL    ; tampona yaz
INC  DI
DEC  DX
JMP  oku
bitti: MOV BYTE PTR [DI], 0
RET
ad   DB 4 DUP(?)

Prosedürler

GET_STRING Ayrıntıları

Yankı denetimi

Kütüphanedeki sürüm tuşu yankısız okur (INT 16h, AH = 0) ve ekrana yalnızca tampona aldığı karakteri yazar. Böylece kabul edilmeyen tuşlar ekranda iz bırakmaz.

Dolu tampon

DX − 1 karakter alındıktan sonra yeni harfler yok sayılır; prosedür Enter ya da geri silme bekler. Sadeleştirilmiş izlemedeki gibi kendiliğinden bitmez.

Geri silme (8)

Tamponda harf varsa DI bir geri alınır ve ekrana 8, ' ', 8 yazılır. Tampon boşsa tuş yok sayılır: imleç girdinin başından geri gitmez.

Enter (13)

Tampona yazılmaz; yerine 0 konur ve prosedür döner. DX ≤ 1 verilirse hiç okuma yapılmaz, çünkü yalnızca sondaki 0'a yer vardır.

GET_STRING'den sonra imleç girilen metnin hemen sağında kalır; sonraki çıktı aynı satırda başlar. Alt satıra geçmek için PUTC 13 ve PUTC 10 eklenir.

Prosedürler · Kodu adım adım çalıştır

Kod İzleme: Geri Silmeyi İşleyen Okuma

AL00hDIad+0ad?+0?+1?+2?+3?+4?+5DIekran

Tampon ad 6 bayt; DI ilk baytı gösterir. Kullanıcı sırasıyla A, b, geri silme, c ve Enter tuşlarına basacak.

AL41hDIad+0ad?+0?+1?+2?+3?+4?+5DIekran

INT 16h: AL = 'A' (41h); tuş yankısız okunduğu için ekranda henüz bir şey yok. Enter (13) ve geri silme (8) değil: ekle.

AL41hDIad+1ad'A'+0?+1?+2?+3?+4?+5DIekranA

'A' tampona yazılır, DI ilerler; ardından yankı: ekranda A.

AL62hDIad+2ad'A'+0'b'+1?+2?+3?+4?+5DIekranAb

Aynı yol 'b' için: tampon A, b; DI = ad+2; ekranda Ab.

AL08hDIad+2ad'A'+0'b'+1?+2?+3?+4?+5DIekranAb

Geri silme: AL = 08h. CMP AL, 8 → ZF = 1, JNE ekle atlamaz.

AL08hDIad+1ad'A'+0'b'+1?+2?+3?+4?+5DIekranAb

DI tamponun başında değil (ad+2 ≠ ad): silinecek harf var. DEC DI ile b'nin baytı yeniden boş sayılır; bayt bellekte durur ama dizginin dışında kalır.

AL08hDIad+1ad'A'+0'b'+1?+2?+3?+4?+5DIekranA

Ekranda üçlü: 8 imleci sola alır, boşluk b'nin üstüne yazılır, ikinci 8 imleci yine sola getirir. Görünen yalnızca A.

AL63hDIad+2ad'A'+0'c'+1?+2?+3?+4?+5DIekranAc

'c', b'nin bulunduğu bayta yazılır: tampon A, c; ekranda Ac.

AL0DhDIad+2ad'A'+0'c'+10+2?+3?+4?+5DIekranAc

Enter: JE bitti; DI'nin gösterdiği bayta 0 yazılır. Tampon 'A', 'c', 0: geri silinen harf dizgide yoktur.

ORG  100h
LEA  DI, ad
oku: MOV AH, 0
INT  16h         ; AL = tuş (yankısız)
CMP  AL, 13      ; Enter mi?
JE   bitti
MOV  AH, 0Eh
CMP  AL, 8       ; geri silme mi?
JNE  ekle
CMP  DI, OFFSET ad
JE   oku         ; silinecek harf yok
DEC  DI
INT  10h         ; 8: imleç sola
MOV  AL, ' '
INT  10h         ; harfin yerine boşluk
MOV  AL, 8
INT  10h         ; yine sola
JMP  oku
ekle: MOV [DI], AL
INC  DI
INT  10h         ; yankı
JMP  oku
bitti: MOV BYTE PTR [DI], 0
RET
ad   DB 6 DUP(?)

Prosedürler

Sayı Prosedürleri

ProsedürNe yapar?Girdi / çıktıTanım
SCAN_NUMKlavyeden çok basamaklı, 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 · Kodu adım adım çalıştır

Kod İzleme: SCAN_NUM Mantığı (girdi 307)

AX0000hCX0000hBX0000hZF1CX onluk:0ekran▌

SCAN_NUM'un özü: rakamlar soldan sağa okunur, sonuç = sonuç × 10 + rakam (kütüphanedeki sürüm eksi işaretini ve taşmayı da denetler). CX = 0.

AX0133hCX0000hBX0000hZF1CX onluk:0ekran3▌

INT 21h, AH = 01h: '3' tuşu, AL = 33h; karakter ekrana yankılanır.

AX0103hCX0000hBX0000hZF0CX onluk:0ekran3▌

Enter değil. SUB AL, '0': 33h − 30h = 3.

AX0000hCX0003hBX0000hZF0CX onluk:3ekran3▌

CBW AX = 0003h yapar; XCHG ile AX = eski sonuç (0), CX = rakam (3).

AX0000hCX0003hBX000AhZF0CX onluk:3ekran3▌

MUL BX: 0 × 10 = 0; ADD CX, AX → CX = 3.

AX0003hCX0000hBX000AhZF1CX onluk:0ekran30▌

'0' okunur: rakam 0; XCHG sonrası AX = 3, CX = 0.

AX001EhCX001EhBX000AhZF0CX onluk:30ekran30▌

3 × 10 = 30 (1Eh) → CX = 30 + 0 = 30.

AX0007hCX001EhBX000AhZF0CX onluk:30ekran307▌

'7' okunur: rakam 7 AX'e (CBW), sonuç 30 hâlâ CX'te.

AX012ChCX0133hBX000AhZF0CX onluk:307ekran307▌

XCHG: AX = 30, CX = 7. 30 × 10 = 300 (012Ch) → CX = 300 + 7 = 307 = 0133h.

AX010DhCX0133hBX000AhZF1CX onluk:307ekran307▌

Enter: AL = 0Dh = 13 → ZF = 1, JE bitti atlar.

AX010DhCX0133hBX000AhZF1CX onluk:307ekran307▌

Sonuç SCAN_NUM'daki gibi CX'te: 0133h = 307.

ORG  100h
XOR  CX, CX      ; sonuç = 0
oku: MOV AH, 01h
INT  21h         ; AL = tuş (yankılı)
CMP  AL, 13      ; Enter mi?
JE   bitti
SUB  AL, '0'     ; ASCII → rakam
CBW              ; AX = rakam
XCHG AX, CX      ; AX=sonuç, CX=rakam
MOV  BX, 10
MUL  BX          ; AX = sonuç × 10
ADD  CX, AX      ; CX = sonuç×10 + rakam
JMP  oku
bitti: RET

Prosedürler

SCAN_NUM'ın Denetimleri

  • - tuşu bir işaret bayrağı kurar; Enter'da bayrak kuruluysa NEG CX uygulanır.
  • Rakam olmayan tuş ekrandan silinir (8, ' ', 8) ve sayıya katılmaz.
  • Geri silme son basamağı atar: CX ← CX / 10.
  • Sayı 16 bite sığmayacaksa (× 10 ya da toplama taşarsa) son basamak kabul edilmez.
  • Enter de yankılanır: imleç satır başına döner (CR) ama alt satıra inmez. Örnek programdaki PTHIS dizgisi bu yüzden 13, 10 ile başlar.
GirdiCXPRINT_NUM
3070133h307
-25FFE7h-25
12a3007Bh123
400009C40h-25536
700001B58h7000
-4000063C0h25536
Basamaklar işaretsiz 16 bit olarak biriktirilir, işaret en sonda uygulanır: −32 768 … 32 767 dışındaki girdiler hata vermeden yanlış işaretli sonuç üretir. (Tablo, kütüphanedeki yordamın eşdeğer kodu çalıştırılarak elde edildi.)

Prosedürler · Kodu adım adım çalıştır

Kod İzleme: PRINT_NUM_UNS Mantığı (AX = 1205)

AX04B5hDX0000hCX0000hAX onluk:1205yığınFFF6····FFF8····FFFA····FFFC····ekran▌

Aynı çıktıyı veren eşdeğer kod (kütüphanedeki sürüm 10000, 1000, … ile böler). AX = 1205 = 04B5h, bölen BX = 10.

AX0078hDX0005hCX0001hAX onluk:120yığınFFF6····FFF8····FFFA····FFFC0005hSPekran▌

DIV BX: DX:AX / 10 → AX = 120, kalan DX = 5. Son basamak yığına, CX = 1.

AX000ChDX0000hCX0002hAX onluk:12yığınFFF6····FFF8····FFFA0000hSPFFFC0005hekran▌

AX ≠ 0 → döngü sürer: 120 / 10 = 12, kalan 0 yığına; CX = 2.

AX0001hDX0002hCX0003hAX onluk:1yığınFFF6····FFF80002hSPFFFA0000hFFFC0005hekran▌

12 / 10 = 1, kalan 2 yığına; CX = 3.

AX0000hDX0001hCX0004hAX onluk:0yığınFFF60001hSPFFF80002hFFFA0000hFFFC0005hekran▌

1 / 10 = 0, kalan 1 yığına; CX = 4 basamak.

AX0000hDX0001hCX0004hAX onluk:0yığınFFF60001hSPFFF80002hFFFA0000hFFFC0005hekran▌

OR AX, AX: AX = 0 → ZF = 1, JNZ atlamaz. Yığının üstünde en anlamlı basamak (1) var.

AX0E31hDX0001hCX0003hyığınFFF6····FFF80002hSPFFFA0000hFFFC0005hekran1▌

POP AX = 1; + '0' = 31h; INT 10h '1' yazar. LOOP: CX = 3.

AX0E32hDX0001hCX0002hyığınFFF6····FFF8····FFFA0000hSPFFFC0005hekran12▌

Sonraki basamak 2 → '2'. CX = 2.

AX0E30hDX0001hCX0001hyığınFFF6····FFF8····FFFA····FFFC0005hSPekran120▌

0 → '0'. CX = 1.

AX0E35hDX0001hCX0000hyığınFFF6····FFF8····FFFA····FFFC····ekran1205▌

5 → '5'. CX = 0, döngü biter; yığın FFFEh'ye döndü. Ekranda 1205.

ORG  100h
MOV  AX, 1205    ; yazdırılacak sayı
MOV  BX, 10
XOR  CX, CX      ; basamak sayacı
bol: XOR DX, DX
DIV  BX          ; AX=bölüm, DX=kalan
PUSH DX          ; basamak yığına
INC  CX
OR   AX, AX      ; bölüm 0 mı?
JNZ  bol
yaz: POP AX      ; önce en anlamlı
ADD  AL, '0'
MOV  AH, 0Eh
INT  10h
LOOP yaz
RET

Prosedürler

Kütüphanenin Yöntemi: Azalan Bölen

  • Bölen 10 000'den başlar, her turda 10'a bölünür; bölen 0 olunca yazdırma biter.
  • Bölüm (0–9) bir basamaktır: ADD AL, 30h ile yazılır, kalan bir sonraki turun sayısı olur.
  • Bir bayrak (CX = 1) baştaki sıfırları atlar; ilk basamak yazılınca bayrak 0 olur ve aradaki sıfırlar artık yazılır.
  • AX = 0 için ayrı bir dal doğrudan '0' yazar.
  • 65 535 ÷ 10 000 = 6 olduğundan bölüm her zaman AL'ye sığar.
BölenSayıİşlemEkran
100003007sayı < bölen, henüz basamak yok: atla—
100030073007 ÷ 1000 = 3, kalan 73
10077 ÷ 100 = 0, kalan 730
1077 ÷ 10 = 0, kalan 7300
177 ÷ 1 = 7, kalan 03007
Basamaklar soldan sağa, hesaplandıkları sırada yazılır; yığına itmeye ve ters çevirmeye gerek kalmaz.

Prosedürler · Kodu adım adım çalıştır

Kod İzleme: PRINT_NUM ve Eksi İşareti

AXFFE7hSPFFFEhSF0yığınFFF8····FFFA····FFFC····FFFE0000hSPekran▌

AX = −25; ikiye tümleyen gösterimiyle FFE7h. Gösterilen kod, kütüphanedeki PRINT_NUM'ın eşdeğeridir.

AXFFE7hSPFFFChSF0yığınFFF8····FFFA····FFFC0106hSPFFFE0000hekran▌

CALL print_num: dönüş adresi 0106h yığına, SP = FFFCh.

AXFFE7hSPFFFAhSF1yığınFFF8····FFFAFFE7hSPFFFC0106hFFFE0000hekran▌

PUSH AX çağıranın değerini saklar. OR AX, AX değeri değiştirmez ama bayrakları kurar: en anlamlı bit 1 → SF = 1.

AX0019hSPFFFAhSF0yığınFFF8····FFFAFFE7hSPFFFC0106hFFFE0000hekran▌

JNS atlamaz (sayı negatif). NEG AX: 0 − FFE7h = 0019h = 25, sayının mutlak değeri.

AX0019hSPFFFAhSF0yığınFFF8····FFFAFFE7hSPFFFC0106hFFFE0000hekran-▌

PUTC '-' açılımı: AX yığına, '-' yazılır, AX = 0019h geri gelir.

AX0019hSPFFFAhSF0yığınFFF8····FFFAFFE7hSPFFFC0106hFFFE0000hekran-25▌

CALL print_num_uns: 25'in basamakları yazılır (basamak döngüsü PRINT_NUM_UNS izlemesindekiyle aynıdır; prosedür kullandığı yazmaçları saklar).

AXFFE7hSPFFFChSF0yığınFFF8····FFFA····FFFC0106hSPFFFE0000hekran-25▌

POP AX: AX yeniden FFE7h. Sayı yazdırıldıktan sonra da AX'te durur.

AXFFE7hSPFFFEhSF0yığınFFF8····FFFA····FFFC····FFFE0000hSPekran-25▌

RET'ler: SP = FFFEh. Kütüphanedeki PRINT_NUM ayrıca AX = 0 için doğrudan '0' yazar.

ORG  100h
MOV  AX, -25       ; AX = FFE7h
CALL print_num
RET
print_num PROC
PUSH AX
OR   AX, AX        ; SF = işaret biti
JNS  pozitif
NEG  AX            ; AX = 0019h
PUSH AX            ; PUTC '-' açılımı
MOV  AL, '-'
MOV  AH, 0Eh
INT  10h
POP  AX
pozitif: CALL print_num_uns
POP  AX            ; AX = FFE7h geri
RET
print_num ENDP

Prosedürler

Basamak Ayırma: İki Yöntem

ÖlçütYığınlı yöntem (10'a böl, it, ters sırayla çek)Azalan bölen (10 000, 1000, …)
Bölme sayısıbasamak sayısı kadarbeşe kadar, ayrıca böleni küçültmek için birer bölme
Yığın kullanımıbasamak başına 2 baytyalnızca saklanan yazmaçlar
Baştaki sıfırlarkendiliğinden oluşmazbayrakla atlanır
Sabit genişlik (00042)ek kod gerekirbayrak kaldırılınca kendiliğinden
Başka tabanbölen 2, 8 ya da 16 yapılır; 16'da 9'dan büyük basamaklar 'A'–'F' olurbölen dizisi tabana göre değişir (16'da 4096, 256, 16, 1)
Çıkış sırasıbasamaklar önce yığına, sonra ters sırayla ekranasoldan sağa, hesaplandığı anda
İki yöntem de aynı çıktıyı verir: 1205 için iki yöntem de 1, 2, 0, 5 basamaklarını yazar. Seçimi yığın kullanımı, sabit genişlik ve taban gereksinimi belirler.

Prosedürler

Demo: PRINT_NUM ve PRINT_NUM_UNS

AX'e verilen değer (onluk, negatif ya da h ekli onaltılık) için iki prosedürün ekrana ne yazacağı gösterilir.

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

Kod İzleme: Sayı Oku ve Yazdır

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

CALL print_string: dönüş adresi yığına itilir (SP = FFFCh); prosedür DS:SI'deki dizgiyi 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 dizginin adresidir.

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

Dönüş, dizginin 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 dizgi baytları gelir; dizgi 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, dizginin 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 dizgiyi 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

Örnek: İki Sayının Toplamı

include 'emu8086.inc'
ORG  100h
CALL scan_num     ; 1. sayı → CX
MOV  BX, CX       ; sakla
PUTC 13
PUTC 10           ; alt satır
CALL scan_num     ; 2. sayı → CX
PUTC 13
PUTC 10
MOV  AX, BX
ADD  AX, CX       ; işaretli toplam
JO   tasma
CALL print_num
RET
tasma: PUTC '?'
RET
DEFINE_SCAN_NUM
DEFINE_PRINT_NUM
DEFINE_PRINT_NUM_UNS
END
  • İkinci SCAN_NUM da sonucu CX'e yazar: birinci sayı önce BX'e alınır.
  • Enter yalnızca satır başına döndürür; PUTC 13, PUTC 10 sonraki girdiyi alt satıra taşır.
  • JO işaretli taşmayı yakalar: 30 000 + 5000 = 35 000, 16 bitlik işaretli aralığı (32 767) aşar.
GirdilerEkran (son satır)
120, -4575
-7, -8-15
30000, 5000?

Örnek Program

Örnek: Çerçeveli Başlık

include 'emu8086.inc'
ORG  100h
CALL clear_screen
GOTOXY 10, 2
CALL cizgi
GOTOXY 10, 3
PRINT '|  BASLIK  |'
GOTOXY 10, 4
CALL cizgi
GOTOXY 0, 6       ; imleç alta
RET
cizgi PROC        ; +----------+
PUTC '+'
MOV  CX, 10
tire: PUTC '-'
LOOP tire
PUTC '+'
RET
cizgi ENDP
DEFINE_CLEAR_SCREEN
END
0123456+----------+|BASLIK|+----------+sütun 10 … 21
  • Kendi prosedürümüz (cizgi) kütüphane makrolarıyla birlikte kullanılır; tanımı RET'ten sonradır.
  • Döngüdeki PUTC '-' bir kez açılır, 10 kez çalışır: kod 10 kez kopyalanmaz.
  • PUTC yalnızca AX'i kullanır ve geri yükler; CX'e dokunmadığı için döngü sayacı güvendedir.

Örnek Program

Sık Yapılan Hatalar

DEFINE_ unutmak

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

DEFINE_'nin yeri

Her DEFINE_ makrosu JMP skip_proc_… ile başlar; programın başına konsa da işlemci gövdeyi atlar. Alışılmış yer yine RET ile END arasıdır.

Dizgiyi 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.

YanlışORG 100hDEFINE_SCAN_NUMana programRETDoğruORG 100hana programRETDEFINE_SCAN_NUMEND← ilk çalışan kod← yalnız CALL ile

Örnek Program

Sayı ve Ekran Hataları

Sonucu AX'te aramak

SCAN_NUM sonucu CX'e yazar. PRINT_NUM'dan önce MOV AX, CX unutulursa AX'teki eski değer yazılır.

İşaretsizi işaretli yazmak

AX = 40 000 (9C40h) PRINT_NUM ile −25536 görünür. 32 767'yi aşabilen işaretsiz değerler PRINT_NUM_UNS ile yazılır.

Enter'dan sonra alt satır beklemek

SCAN_NUM'dan sonra imleç girdinin satır başındadır; sonraki çıktı girilen sayının üstüne yazılır. Araya CR + LF gerekir.

Satır ile sütunu karıştırmak

GOTOXY 5, 10 imleci 5. sütun, 10. satıra götürür; tersi değil.

Açılımın bozduğu yazmacı vermek

GOTOXY AL, AH: makro AH'ye önce 02h yazdığı için satır 02h olur. Konum önce başka yazmaçlara alınır.

Tampon boyunu büyük vermek

DX, tamponun gerçek boyundan büyük verilirse GET_STRING tamponun ardındaki değişkenlerin üstüne yazar.

Ö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

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

Dizgiler

PRINT dizgiyi koda gömer; PRINT_STRING ve PTHIS tek kopya kodla 0 ile biten dizgi yazar; GET_STRING tamponu 0 ile bitirir.

Ö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 dizgiler 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 dizgisi ve döngüsüyle). PRINT_STRING: döngü 1 kez, 10 dizgi + 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 dizgi 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.

Son

Bölüm 9: emu8086.inc Kütüphanesi
Sonraki bölüm: Bölüm 10: 8086 Pinler · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.