Bölüm 9: emu8086.inc Kütüphanesi
Giriş
Öğrenme Çıktıları
Bir kütüphane dosyasını kaynağa eklemeyi ve derleyicinin dosyayı nerede aradığını açıklamak.
PUTC, GOTOXY, PRINT, PRINTN gibi makroların neye genişlediğini göstermek.
DEFINE_… ve CALL ile hazır prosedürleri (sayı okuma/yazma, dizgi yazdırma) kullanmak.
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.inciçindeki herhangi bir işlevi kullanmak için kaynak dosyanın başınainclude 'emu8086.inc'yazılır.- Bu kütüphane, emu8086 platformunda sık kullanılan ekran, klavye ve sayı işlevlerini içerir.
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
includesatı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.
Kütüphane ve INCLUDE
Kütüphanede Neler Var?
| Makro | Görev |
|---|---|
PUTC char | tek karakter yaz |
GOTOXY col, row | imleci konumlandır |
PRINT string | dizgi yaz |
PRINTN string | dizgi yaz + yeni satır |
CURSOROFF | imleci gizle |
CURSORON | imleci göster |
| Prosedür | Görev |
|---|---|
PRINT_STRING | DS:SI'deki dizgiyi yaz |
PTHIS | CALL'dan sonraki dizgiyi yaz |
GET_STRING | klavyeden dizgi oku |
CLEAR_SCREEN | ekranı temizle |
SCAN_NUM | sayı oku → CX |
PRINT_NUM | AX'i işaretli yaz |
PRINT_NUM_UNS | AX'i işaretsiz yaz |
CALL ile çağrılır.Makrolar
Ekran Makroları
| Makro | Ne yapar? | Arka planda |
|---|---|---|
PUTC char | Geç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 string | Verilen dizgiyi yazar. | 0Eh ile karakter karakter |
PRINTN string | PRINT ile aynı; sonuna CR + LF (13, 10) ekleyerek yeni satıra geçer. | 0Eh ile karakter karakter |
CURSOROFF | Metin imlecini kapatır (gizler). | INT 10h, AH = 01h |
CURSORON | Metin imlecini açar. | INT 10h, AH = 01h |
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
Makrolar · Kodu adım adım çalıştır
Kod İzleme: PRINT, GOTOXY, PUTC
include: makro tanımları derleme sırasında eklenir; kod üretilmez.
ORG 100h: COM programı 100h'den başlar.
PRINT: 12 karakter (0–11. sütun) yazılır; imleç 12. sütunda.
GOTOXY 10, 5: imleç 10. sütun, 5. satıra taşınır. Ekrana bir şey yazılmaz.
PUTC 65: 65 = 41h = 'A' yazılır, imleç 11. sütuna geçer.
PUTC 'B': karakter sabiti doğrudan verilebilir.
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 '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.
İ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
| Kod | Ad | PUTC ile verilince |
|---|---|---|
07h | BEL | Hoparlörden kısa bir bip; imleç yerinden oynamaz. |
08h | BS (geri) | İmleç bir sola gider; karakteri silmez. 0. sütunda etkisizdir. |
0Ah | LF (satır besleme) | İmleç bir alt satıra iner, sütun değişmez. Son satırda ekran bir satır yukarı kayar. |
0Dh | CR (satır başı) | İmleç aynı satırın 0. sütununa döner; satırdaki yazı yerinde kalır. |
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.
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ı
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.
'b' yazılır; imleç (2, 0).
Yalnız LF (10): imleç bir alt satıra iner ama sütun 2'de kalır: (2, 1).
'c' alt satırın 2. sütununa yazılır: satırlar “merdiven” gibi kayar.
Yalnız CR (13): imleç aynı satırın başına döner, (0, 1). Satırdaki c silinmez.
'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, 24ardındanPRINT '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.
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
AL = 58 (3Ah). Hedef: sayıyı iki onluk basamak olarak yazmak.
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.
ADD AX, 3030h iki bayta birden 30h ekler: AH = '5' (35h), AL = '8' (38h).
PUTC AH açılımı: önce AX (3538h) yığına, SP = FFFCh.
MOV AL, AH ve MOV AH, 0Eh: AX = 0E35h, birler basamağı yazmaçtan silindi. INT 10h ekrana 5 yazar.
POP AX: AX = 3538h geri geldi; '8' yeniden AL'de.
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,
ENDdirektifinden önce kendiDEFINE_…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
CALLyapılır; adlar küçük harfle yazılır (print_num). PRINT_NUM, işini yaparkenPRINT_NUM_UNS'yi çağırır; ikisi birlikte tanımlanmalıdır.
Prosedürler
Girdi ve Çıktı Yazmaçları
| Prosedür | Girdi | Çıktı | Ayrıntı |
|---|---|---|---|
PRINT_STRING | DS:SI = dizgi adresi | — | dizgi 0 ile bitmeli |
PTHIS | dizgi CALL'ın hemen ardında | — | dönüş adresini kendisi ilerletir |
GET_STRING | DS:DI = tampon, DX = boyut | tamponda 0 ile biten dizgi | en fazla DX − 1 karakter |
CLEAR_SCREEN | — | — | imleç (0, 0) konumuna gider |
SCAN_NUM | klavye | CX (işaretli) | CX'teki eski değer kaybolur |
PRINT_NUM | AX (işaretli) | — | PRINT_NUM_UNS de tanımlı olmalı |
PRINT_NUM_UNS | AX (işaretsiz) | — | 0 … 65 535 |
PUSH CX … POP CX).Prosedürler
Dizgi Prosedürleri
- 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
- 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
Prosedürler
Dizgi Yazdırma Yolları
| Yöntem | Dizgi 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_STRING | veri alanında | 0 | DS:SI | birden çok yerde yazılan ya da çalışırken oluşan dizgi |
PTHIS | CALL'ın hemen ardında | 0 | yığındaki dönüş adresi | çok sayıda kısa sabit ileti |
INT 21h, AH = 09h | veri alanında | '$' | DS:DX | kütüphanesiz DOS programı |
'$' ile biten dizgide '$' karakteri, 0 ile biten dizgide 0 baytı yazdırılamaz; dizgi orada biter.
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
- 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.
- 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)
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.
4 > 1: tamponda karakter ve sondaki 0 için yer var → JBE atlamaz.
INT 21h, AH = 01h: 'E' okunup ekrana yankılanır. Enter (13) değil → JE atlamaz.
'E' tampona yazılır; DI bir ilerler, kalan yer DX = 3.
İkinci tur: 'c' okunur ve ad+1'e yazılır; DX = 2.
Üçüncü tur: 'e' ad+2'ye; DX = 1.
DX = 1: yalnızca sondaki 0 için yer kaldı → JBE bitti. Kullanıcı daha yazsa da okunmaz: en fazla DX − 1 = 3 karakter.
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ı
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.
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.
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.
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.
PUTC 13 ve PUTC 10 eklenir.Prosedürler · Kodu adım adım çalıştır
Kod İzleme: Geri Silmeyi İşleyen Okuma
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.
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.
'A' tampona yazılır, DI ilerler; ardından yankı: ekranda A.
Aynı yol 'b' için: tampon A, b; DI = ad+2; ekranda Ab.
Geri silme: AL = 08h. CMP AL, 8 → ZF = 1, JNE ekle atlamaz.
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.
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.
'c', b'nin bulunduğu bayta yazılır: tampon A, c; ekranda Ac.
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ür | Ne yapar? | Girdi / çıktı | Tanım |
|---|---|---|---|
SCAN_NUM | Klavyeden çok basamaklı, işaretli bir sayı okur (Enter ile biter). | çıktı: CX | DEFINE_SCAN_NUM |
PRINT_NUM | AX'teki işaretli sayıyı onluk olarak yazar. | girdi: AX | DEFINE_PRINT_NUM + DEFINE_PRINT_NUM_UNS |
PRINT_NUM_UNS | AX'teki işaretsiz sayıyı yazar. | girdi: AX | DEFINE_PRINT_NUM_UNS |
16 bit: −32 768 … 32 767
16 bit: 0 … 65 535
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)
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.
INT 21h, AH = 01h: '3' tuşu, AL = 33h; karakter ekrana yankılanır.
Enter değil. SUB AL, '0': 33h − 30h = 3.
CBW AX = 0003h yapar; XCHG ile AX = eski sonuç (0), CX = rakam (3).
MUL BX: 0 × 10 = 0; ADD CX, AX → CX = 3.
'0' okunur: rakam 0; XCHG sonrası AX = 3, CX = 0.
3 × 10 = 30 (1Eh) → CX = 30 + 0 = 30.
'7' okunur: rakam 7 AX'e (CBW), sonuç 30 hâlâ CX'te.
XCHG: AX = 30, CX = 7. 30 × 10 = 300 (012Ch) → CX = 300 + 7 = 307 = 0133h.
Enter: AL = 0Dh = 13 → ZF = 1, JE bitti atlar.
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 kuruluysaNEG CXuygulanı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.
| Girdi | CX | PRINT_NUM |
|---|---|---|
| 307 | 0133h | 307 |
| -25 | FFE7h | -25 |
| 12a3 | 007Bh | 123 |
| 40000 | 9C40h | -25536 |
| 70000 | 1B58h | 7000 |
| -40000 | 63C0h | 25536 |
Prosedürler · Kodu adım adım çalıştır
Kod İzleme: PRINT_NUM_UNS Mantığı (AX = 1205)
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.
DIV BX: DX:AX / 10 → AX = 120, kalan DX = 5. Son basamak yığına, CX = 1.
AX ≠ 0 → döngü sürer: 120 / 10 = 12, kalan 0 yığına; CX = 2.
12 / 10 = 1, kalan 2 yığına; CX = 3.
1 / 10 = 0, kalan 1 yığına; CX = 4 basamak.
OR AX, AX: AX = 0 → ZF = 1, JNZ atlamaz. Yığının üstünde en anlamlı basamak (1) var.
POP AX = 1; + '0' = 31h; INT 10h '1' yazar. LOOP: CX = 3.
Sonraki basamak 2 → '2'. CX = 2.
0 → '0'. CX = 1.
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, 30hile 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ölen | Sayı | İşlem | Ekran |
|---|---|---|---|
| 10000 | 3007 | sayı < bölen, henüz basamak yok: atla | — |
| 1000 | 3007 | 3007 ÷ 1000 = 3, kalan 7 | 3 |
| 100 | 7 | 7 ÷ 100 = 0, kalan 7 | 30 |
| 10 | 7 | 7 ÷ 10 = 0, kalan 7 | 300 |
| 1 | 7 | 7 ÷ 1 = 7, kalan 0 | 3007 |
Prosedürler · Kodu adım adım çalıştır
Kod İzleme: PRINT_NUM ve Eksi İşareti
AX = −25; ikiye tümleyen gösterimiyle FFE7h. Gösterilen kod, kütüphanedeki PRINT_NUM'ın eşdeğeridir.
CALL print_num: dönüş adresi 0106h yığına, SP = FFFCh.
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.
JNS atlamaz (sayı negatif). NEG AX: 0 − FFE7h = 0019h = 25, sayının mutlak değeri.
PUTC '-' açılımı: AX yığına, '-' yazılır, AX = 0019h geri gelir.
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).
POP AX: AX yeniden FFE7h. Sayı yazdırıldıktan sonra da AX'te durur.
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çüt | Yığı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ı kadar | beşe kadar, ayrıca böleni küçültmek için birer bölme |
| Yığın kullanımı | basamak başına 2 bayt | yalnızca saklanan yazmaçlar |
| Baştaki sıfırlar | kendiliğinden oluşmaz | bayrakla atlanır |
Sabit genişlik (00042) | ek kod gerekir | bayrak kaldırılınca kendiliğinden |
| Başka taban | bölen 2, 8 ya da 16 yapılır; 16'da 9'dan büyük basamaklar 'A'–'F' olur | bö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 ekrana | soldan sağa, hesaplandığı anda |
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?
Bellekte CALL pthis'in hemen ardından dizgi baytları gelir; dizgi 0 ile biter.
CALL, “sonraki komutun” adresini iter. Bu adres, dizginin ilk baytı olan 0Dh'nin adresidir.
PTHIS yığındaki adresi okur ve oradan itibaren baytları 0'a kadar ekrana yazar.
Yığındaki adres, 0 baytının bir ötesiyle değiştirilir: artık CALL print_num'u gösterir.
RET: işlemci dizgiyi atlayıp CALL print_num'dan devam eder. Veri komut sanılıp çalıştırılmaz.
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 10sonraki girdiyi alt satıra taşır. JOişaretli taşmayı yakalar: 30 000 + 5000 = 35 000, 16 bitlik işaretli aralığı (32 767) aşar.
| Girdiler | Ekran (son satır) |
|---|---|
| 120, -45 | 75 |
| -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
- 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
CALL scan_num var ama DEFINE_SCAN_NUM yoksa derleyici etiketi bulamaz: undefined hatası.
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.
PRINT_STRING ve PTHIS 0 görene kadar yazar; 0 yoksa bellekteki rastgele baytlar ekrana dökülür.
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.
Örnek Program
Sayı ve Ekran Hataları
SCAN_NUM sonucu CX'e yazar. PRINT_NUM'dan önce MOV AX, CX unutulursa AX'teki eski değer yazılır.
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.
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.
GOTOXY 5, 10 imleci 5. sütun, 10. satıra götürür; tersi değil.
GOTOXY AL, AH: makro AH'ye önce 02h yazdığı için satır 02h olur. Konum önce başka yazmaçlara alınır.
DX, tamponun gerçek boyundan büyük verilirse GET_STRING tamponun ardındaki değişkenlerin üstüne yazar.
Özet
Özet
include 'emu8086.inc': önce kaynak klasörü, sonra Inc klasörü aranır.
PUTC, GOTOXY (col, row), PRINT, PRINTN, CURSOROFF, CURSORON: INT 10h üzerine kurulu, kullanıldıkları yere açılır.
END'den önce DEFINE_…, kullanırken CALL. Girdi/çıktı yazmaçları: SI, DI, DX, CX, AX.
SCAN_NUM → CX (işaretli); PRINT_NUM AX'i işaretli, PRINT_NUM_UNS işaretsiz yazar.
Dizgi CALL'dan hemen sonra; prosedür dönüş adresini dizginin sonrasına taşır.
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
- Ekranın ortasına (sütun 35, satır 12) “MERKEZ” yazan programı makrolarla yazın.
- Kullanıcıdan iki sayı okuyup toplamını yazdıran programı yazın.
PRINTmakrosu 10 kez kullanılırsa dizgiler ve döngü kodu kaç kez bulunur? PRINT_STRING ile karşılaştırın.- AX = 8000h iken PRINT_NUM ve PRINT_NUM_UNS ne yazar?
DEFINE_PTHIS,ORG 100h'nin hemen altına yazılırsa ne olur?- Kullanıcının adını GET_STRING ile alıp “Merhaba, ad!” yazdırın.
İpuçları / cevaplar
GOTOXY 35, 12ardındanPRINT 'MERKEZ',RET.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.- PRINT: 10 kopya (her biri kendi dizgisi ve döngüsüyle). PRINT_STRING: döngü 1 kez, 10 dizgi + 10 × (LEA + CALL).
- PRINT_NUM: −32768, PRINT_NUM_UNS: 32768.
- Program başlarken doğrudan PTHIS'in koduna girer; yığında geçerli dizgi adresi olmadığından anlamsız çıktı veya kilitlenme.
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.incdosyası (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.