Mikroişlemciler · Ders Notu
Bölüm 6: Aritmetik ve Mantıksal İşlemler
Bir işlemcinin asıl işi hesap yapmaktır. 8086'da bu işi yürütme biriminin içindeki aritmetik-mantık birimi (ALU) görür: iki sayıyı toplar, birini ötekinden çıkarır, bitlerini VE, VEYA, dışlayan VEYA kapılarından geçirir, sola ya da sağa kaydırır. Her işlemin sonunda ALU, sonucu yazmaca ya da belleğe yazmakla kalmaz; sonucun nasıl çıktığını bayrak yazmacındaki altı durum bitine de işler. Sonraki komutlar, özellikle koşullu atlamalar, kararlarını bu bitlere bakarak verir. Bu not 8086'nın aritmetik ve mantık komutlarını tek tek ele alır ve her komutun hangi bayrağı nasıl değiştirdiğini gösterir. Önce aynı bit dizisinin işaretsiz ve işaretli iki yorumu ile elde ve taşma bayrakları arasındaki fark açıklanır. Ardından toplama, çıkarma, karşılaştırma ve mantık komutları, çarpma ile bölmenin örtük yazmaçları, işaret genişletme, tek işlenenli komutlar, kaydırma ve döndürme gelir. 16 bitlik bir işlemcide 32 bitlik toplama, çıkarma ve çarpmanın nasıl yapıldığı adım adım izlenir. Son kısımlar dizi toplama, ikili kodlu onluk (BCD) aritmetik ve sıcaklık dönüşümü programlarını yazmaç ve bayrak düzeyinde inceler.
Öğrenme çıktıları
- Bayrakları hesaplamak
- Bir toplama ya da çıkarmanın sonucundan CF, ZF, SF, OF, PF ve AF'nin değerini bulmak; aynı sonucun işaretsiz ve işaretli yorumunu ayırıp CF ile OF farkını açıklamak.
- Komutları doğru işlenenle kullanmak
- İki işlenenli, çarpma-bölme ve tek işlenenli komut gruplarının geçerli işlenen biçimlerini bilmek; her komutun hangi bayrağı değiştirdiğini, hangisini koruduğunu ve hangisini tanımsız bıraktığını söylemek.
- Çarpma ve bölme
- MUL, IMUL, DIV ve IDIV'in örtük yazmaçlarını (AL, AX, DX:AX) kullanmak; bölmeden önce bölüneni CBW, CWD ya da üst yarıyı sıfırlayarak hazırlamak; bölme hatasının nedenlerini tanımak.
- Çok sözcüklü aritmetik
- ADC ve SBB ile 32 bitlik toplama ve çıkarma, dört MUL ile 32 × 32 bitlik çarpma yapmak; eldenin sözcükten sözcüğe nasıl taşındığını izlemek.
- Bit işlemleri
- AND, OR, XOR ve TEST ile bit temizlemek, kurmak, terslemek ve sınamak; kaydırma ve döndürme komutlarının CF'ye etkisini izlemek; kaydırma ve toplamayla çarpıp bölmek.
- Uygulama programlarını izlemek
- Dizi toplama, BCD düzeltme ve sıcaklık dönüşümü programlarını yazmaç, bellek ve bayrak düzeyinde adım adım izlemek; programın geçerli olduğu sayı aralığını bulmak.
6.1Bayraklar ve işlemin sonucu
6.1.1ALU sonucu nereye yazar?
8086'nın içi iki birime ayrılır. Yol arayüz birimi (BIU) bellekten komut baytlarını getirir, önden getirme kuyruğuna koyar ve bellek işlenenlerini okuyup yazar. Yürütme birimi (EU) kuyruktan aldığı komutu çözer ve yürütür. Toplama, çıkarma, VE, VEYA, kaydırma gibi bütün hesaplar EU'nun içindeki 16 bitlik aritmetik-mantık biriminde (arithmetic logic unit, ALU) yapılır. ALU'nun iki girişi ve bir çıkışı vardır. Girişler yazmaçlardan, komutun içindeki anlık değerden ya da BIU'nun bellekten getirdiği sayıdan gelir. Çıkış hedef yazmaca ya da BIU aracılığıyla belleğe gider.
ALU'nun ikinci bir çıkışı daha vardır: sonucun özelliklerini anlatan birkaç bit. Sonuç sıfır mı, en soldaki biti 1 mi, en soldaki bitten dışarı elde çıktı mı? Bu sorulara verilen evet/hayır cevapları, 16 bitlik bayrak yazmacındaki (FLAGS) durum bitlerine yazılır. Şekil 6.1 bayrak yazmacının bit düzenini gösterir. On altı bitin dokuzu kullanılır. Altısı durum bayrağıdır: CF, PF, AF, ZF, SF ve OF son aritmetik ya da mantık işleminin sonucunu anlatır. Üçü kontrol bayrağıdır: TF, IF ve DF işlemcinin davranışını yönetir ve aritmetik komutlar onlara dokunmaz.
Bayraklar, komutlar arasında bilgi taşıyan bir köprüdür. CMP AL, BL komutu iki sayıyı karşılaştırır ama sonucu hiçbir yazmaca yazmaz; yalnız bayrakları değiştirir. Hemen ardından gelen JZ, JC ya da JO gibi bir koşullu atlama, kararını bu bayraklara bakarak verir. Çok sözcüklü toplamada ise ilk toplamanın eldesi CF'de bekler ve ikinci toplama onu ADC ile alır. Bu yüzden her komut için iki soru sorulur: sonucu nereye yazar ve hangi bayrakları nasıl değiştirir?
Tablo 6.1 durum ve kontrol bayraklarını, hangi koşulda 1 olduklarını ve kısa birer örneği toplar. Bayrakların her birinin ayrıntılı anlatımı ve donanımdaki üretimi 8086 yazmaçlarını anlatan notta verilmiştir. Burada yalnız bir toplama ya da çıkarmadan sonra bayrakları hesaplamaya yetecek kurallar özetlenir.
| Bit | Bayrak | Tür | 1 olur, eğer… | Örnek |
|---|---|---|---|---|
| 0 | CF (elde) | durum | işaretsiz taşma: toplamada en anlamlı bitten dışarı elde, çıkarmada ödünç | FFh + 01h → AL = 00h, CF = 1; 7 − 9 → CF = 1 |
| 2 | PF (eşlik) | durum | sonucun düşük baytında çift sayıda 1 biti var | 96h = 1001 0110b → dört 1 → PF = 1 |
| 4 | AF (yardımcı elde) | durum | bit 3'ten bit 4'e elde ya da ödünç | 08h + 08h = 10h → AF = 1 |
| 6 | ZF (sıfır) | durum | sonucun bütün bitleri 0 | SUB AL, AL → ZF = 1 |
| 7 | SF (işaret) | durum | sonucun en anlamlı biti (MSB) 1 | 96h → SF = 1 |
| 8 | TF (tuzak) | kontrol | her komuttan sonra INT 1 (tek adım) | hata ayıklayıcılar kullanır |
| 9 | IF (kesme) | kontrol | INTR ucundan gelen kesmeler kabul edilir | STI → 1, CLI → 0 |
| 10 | DF (yön) | kontrol | dizgi komutları SI ve DI'yi azaltır | CLD → 0, STD → 1 |
| 11 | OF (taşma) | durum | işaretli taşma: sonuç işaretli aralığa sığmadı | 100 + 50 → AL = 96h (−106), OF = 1 |
Durum bayraklarının donanımdaki karşılığı basittir. ALU'nun toplayıcısı bitleri sağdan sola, tam toplayıcı zinciriyle toplar. Bayt işleminde bit 7'nin, sözcük işleminde bit 15'in dışarı verdiği elde doğrudan CF'ye gider. Bit 3'ün verdiği elde AF'ye gider. SF sonucun en anlamlı bitinin kopyasıdır. ZF, sonucun bütün bitlerini giriş alan bir VEYA-DEĞİL (NOR) kapısının çıkışıdır. PF, düşük baytın sekiz bitini dışlayan VEYA ağacından geçirip tersler; sözcük sonucunda bile yalnız düşük bayta bakar. OF ise en anlamlı bite giren elde ile o bitten çıkan eldenin dışlayan VEYA'sıdır. Bu son kural aşağıdaki örneklerde sayılarla görülür.
Çıkarmada ALU ayrı bir çıkarıcı kullanmaz. A − B işlemini A + (B'nin birler tümleyeni) + 1 diye toplayarak yapar; bu, ikiye tümleyenle eklemektir. Bu toplamada en soldan elde çıkması, aslında ödünç gerekmediği anlamına gelir. 8086 bu yüzden çıkarmada eldeyi tersleyip CF'ye yazar. Sonuçta CF, çıkarmada ödünç (borrow) bayrağı gibi davranır: işaretsiz yorumda küçük sayıdan büyük sayı çıkarılmışsa CF = 1 olur.
6.1.2CF mi, OF mi?
Bir yazmaçtaki sekiz bit, kendi başına ne işaretsiz ne işaretli bir sayıdır. 1001 0110b dizisi işaretsiz okunursa 150, ikiye tümleyenle işaretli okunursa −106 demektir. İşlemci toplamayı yalnız bitler üzerinde yapar ve iki yorum için aynı sonucu üretir. Hangi yorumun geçerli olduğunu bilen programcıdır. İşlemci her iki yorumu da hesaba katar ve iki ayrı bayrak tutar: CF işaretsiz yorumda, OF işaretli yorumda sonucun doğru olup olmadığını söyler.
Elde bayrağı (carry flag, CF), işlem işaretsiz yorumla yapıldığında sonucun hedefin boyutuna sığmadığını gösterir. Bayt toplamasında gerçek toplam 255'i aşarsa, çıkarmada sonuç 0'ın altına inerse CF = 1 olur.
Taşma bayrağı (overflow flag, OF), işlem ikiye tümleyenli işaretli yorumla yapıldığında sonucun aralığa sığmadığını gösterir. Bayt için aralık −128…127, sözcük için −32 768…32 767'dir. Aynı işaretli iki sayının toplamı ters işaretli çıkarsa OF = 1 olur.
Tablo 6.2 beş bayt toplamasını iki yorumla yan yana koyar. Her satırda AL'ye yazılan sonuç tektir; değişen yalnız onun nasıl okunduğudur.
| AL + işlenen | İşaretsiz yorum | İşaretli yorum | AL | CF | OF |
|---|---|---|---|---|---|
FFh + 01h | 255 + 1 = 256 | −1 + 1 = 0 | 00h | 1 | 0 |
64h + 32h | 100 + 50 = 150 | 100 + 50 = 150 | 96h | 0 | 1 |
C8h + 64h | 200 + 100 = 300 | −56 + 100 = 44 | 2Ch | 1 | 0 |
80h + 80h | 128 + 128 = 256 | −128 + (−128) = −256 | 00h | 1 | 1 |
05h + 0Ah | 5 + 10 = 15 | 5 + 10 = 15 | 0Fh | 0 | 0 |
İlk satırda işaretsiz toplam 256'dır ve bir bayta sığmaz. AL'de 256 − 256 = 0 kalır, dışarı çıkan elde CF'ye düşer. İşaretli yorumda ise −1 + 1 = 0 tamamen doğrudur; OF = 0. Bu satır, CF = 1'in işaretli hesapta bir hata olmadığını gösterir. İkinci satır tam tersidir. 150, bayta işaretsiz olarak sığar, CF = 0'dır. Ama işaretli bayt en çok 127 olabilir; iki pozitif sayının toplamı 96h çıkmıştır ve 96h'nin işaret biti 1'dir. İşaretli yorumda bu −106 demektir ve yanlıştır: OF = 1.
Üçüncü satırda iki yorum yine ayrışır. İşaretsiz 300 sığmaz, CF = 1 olur. İşaretli yorumda ise sayılardan biri negatif (−56), öteki pozitiftir (100). Farklı işaretli iki sayının toplamı, mutlak değerce büyük olanından daha küçüktür; bu yüzden asla aralığın dışına çıkamaz. Sonuç 44 doğrudur ve OF = 0'dır. Dördüncü satırda iki yorum da yanlıştır: 256 bayta sığmaz, −256 de sığmaz. İki bayrak birlikte 1 olur. Son satırda iki yorum da doğrudur ve iki bayrak da 0'dır.
OF'nin kuralı, en anlamlı bitteki eldeler cinsinden de söylenebilir. İkinci satırı bit bit toplayalım: 0110 0100b + 0011 0010b. Bit 5'te 1 + 1 = 10b olur; 0 yazılır, elde 1 bit 6'ya geçer. Bit 6'da 1 + 0 + 1 = 10b; 0 yazılır, elde bit 7'ye girer. Bit 7'de 0 + 0 + 1 = 1 yazılır ve dışarı elde çıkmaz. Bit 7'ye giren elde 1, çıkan elde 0'dır. İkisi farklı olduğu için OF = 1'dir. Dördüncü satırda ise bit 7'ye hiç elde girmez ama 1 + 1 dışarı elde çıkarır: giren 0, çıkan 1, yine OF = 1. İlk satırda bit 7'ye elde girer ve dışarı da elde çıkar: ikisi aynı olduğundan OF = 0.
Toplamada OF = (en anlamlı bite giren elde) XOR (en anlamlı bitten çıkan elde). Bunun işaret diliyle söylenişi şudur: aynı işaretli iki sayının toplamı ters işaretli çıkarsa OF = 1 olur; farklı işaretli iki sayı toplanınca OF hiçbir zaman 1 olmaz. Çıkarmada (A − B) kural, farklı işaretli iki sayıdan çıkan sonucun işareti A'nınkinden farklıysa OF = 1 biçimini alır.
CF'yi “taşma” diye okumak ve işaretli sayılarla çalışırken CF'ye bakmak. −1 + 1 toplamasında CF = 1 olur, oysa işaretli sonuç doğrudur. İşaretli sayılarla çalışan bir program OF'ye (ve karşılaştırmada SF ile OF'ye), işaretsiz sayılarla çalışan bir program CF'ye bakar. İşlemci hangisinin geçerli olduğunu bilmez; seçim komutu yazanındır.
6.1.3Kod izleme: CF, OF ve diğer bayraklar
Aşağıdaki kısa program bayrakların bir komuttan ötekine nasıl taşındığını gösterir. Program 9Ch ile başlar; 9Ch işaretsiz yorumda 156, işaretli yorumda −100'dür. Sonra iki ayrı toplama, ardından kendisiyle toplama, INC ve CMC gelir.
MOV AL, 9Ch ; 156 ya da -100
ADD AL, 70h ; + 112
MOV AL, 50h ; 80
ADD AL, 50h ; + 80
ADD AL, AL ; A0h + A0h
MOV AL, 0FFh
INC AL ; CF'ye dokunmaz
CMC ; CF tersine
RET| Komut | Makine kodu | AL | CF | OF | SF | ZF | AF | PF |
|---|---|---|---|---|---|---|---|---|
MOV AL, 9Ch | B0 9C | 9Ch | 0 | 0 | 0 | 0 | 0 | 0 |
ADD AL, 70h | 04 70 | 0Ch | 1 | 0 | 0 | 0 | 0 | 1 |
MOV AL, 50h | B0 50 | 50h | 1 | 0 | 0 | 0 | 0 | 1 |
ADD AL, 50h | 04 50 | A0h | 0 | 1 | 1 | 0 | 0 | 1 |
ADD AL, AL | 02 C0 | 40h | 1 | 1 | 0 | 0 | 0 | 0 |
MOV AL, 0FFh | B0 FF | FFh | 1 | 1 | 0 | 0 | 0 | 0 |
INC AL | FE C0 | 00h | 1 | 0 | 0 | 1 | 1 | 1 |
CMC | F5 | 00h | 0 | 0 | 0 | 1 | 1 | 1 |
İlk toplamada 156 + 112 = 268 bulunur. 268 bir bayta sığmaz; AL'de 268 − 256 = 12 = 0Ch kalır ve CF = 1 olur. İşaretli yorumda −100 + 112 = 12 doğrudur; işaretleri farklı iki sayı toplandığı için OF zaten 1 olamaz. 0Ch = 0000 1100b'de iki tane 1 vardır, bu yüzden PF = 1'dir. Düşük yarımlar C + 0 = C olduğundan bit 3'ten elde çıkmaz: AF = 0.
Üçüncü satırdaki MOV hiçbir bayrağı değiştirmez. Bu, sık unutulan bir kuraldır: MOV, LEA, XCHG gibi veri taşıma komutları ALU'dan geçmez ve bayraklar önceki aritmetik komuttan ne kaldıysa öyle kalır. CF = 1 ve PF = 1 yerinde durur.
Dördüncü satırda 80 + 80 = 160 işaretsiz yorumda doğrudur; CF = 0. Ama iki pozitif sayının toplamı A0h = 1010 0000b çıkmıştır ve işaret biti 1'dir. İşaretli yorumda bu −96'dır ve yanlıştır: OF = 1, SF = 1. Beşinci satırda A0h kendisiyle toplanır. İşaretsiz 160 + 160 = 320 sığmaz, CF = 1 olur. İşaretli −96 + (−96) = −192 de sığmaz, OF = 1 olur. AL'de kalan 40h iki yorumda da yanlış bir sonuçtur.
Yedinci satırdaki INC AL, FFh'ye 1 ekler ve AL = 00h olur. Sonuç sıfır olduğundan ZF = 1, düşük yarımdaki F + 1 elde verdiğinden AF = 1'dir. Aynı işlem ADD AL, 1 ile yapılsaydı dışarı elde çıkacak ve CF = 1 olacaktı. INC ise CF'ye hiç dokunmaz; tablodaki CF = 1 beşinci satırdaki toplamadan kalmıştır. Son satırdaki CMC (complement carry) yalnız CF'yi tersler. Aynı ailede STC CF'yi 1, CLC CF'yi 0 yapar.
6.1.4ADD ile bayraklar adım adım
Bayrakları elle hesaplamanın en güvenilir yolu, toplamayı kâğıt üzerinde ikili tabanda yapmaktır. MOV AL, 64h ve ADD AL, 32h komutları 100 ile 50'yi toplar. Şekil 6.2 iki işleneni, sonucu ve her bayrağın hangi bitten okunduğunu gösterir.
- Sonuç. 0110 0100b + 0011 0010b = 1001 0110b = 96h. İşaretsiz yorumla 150 doğrudur, işaretli yorumla −106 yanlıştır.
- CF. En soldaki bitten dışarı elde çıkmadı; 150 ≤ 255. CF = 0.
- OF. Bit 7'ye giren elde 1, çıkan elde 0. İki pozitif sayının toplamı negatif göründü. OF = 1.
- SF ve ZF. Sonucun bit 7'si 1'dir, SF = 1. Sonuç sıfır değildir, ZF = 0.
- PF. 1001 0110b'de dört tane 1 vardır; dört çift sayıdır, PF = 1.
- AF. Düşük yarımlar 4 + 2 = 6'dır; bit 3'ten bit 4'e elde geçmez, AF = 0.
Sonuç: AL = 96h; CF = 0, ZF = 0, SF = 1, OF = 1, PF = 1, AF = 0. Bu hesap emu8086'da çalıştırıldığında bayrak penceresinde aynı değerler görülür. Sunumdaki bayrak hesaplayıcısı ve Bayrak Laboratuvarı sayfası, başka sayılar ve başka komutlar için aynı tabloyu kurar.
MOV AL, 0C0h ve ADD AL, 0C0h sonrası bayraklar nedir?
C0h = 1100 0000b; işaretsiz 192, işaretli −64. Toplam 1 1000 0000b = 180h'dir. AL'ye 80h yazılır ve dışarı çıkan elde CF'ye düşer: CF = 1. Bit 7'ye giren elde 1 (bit 6'daki 1 + 1'den), çıkan elde de 1'dir; OF = 0. Gerçekten −64 + (−64) = −128 işaretli bayta tam sığar. SF = 1, ZF = 0. 80h'de tek bir 1 biti vardır: PF = 0. Düşük yarımlar 0 + 0 olduğundan AF = 0'dır.
6.2Komut grupları ve işlenen kuralları
6.2.1Üç grup
8086'nın aritmetik ve mantık komutları, işlenenlerini nasıl aldıklarına göre üç gruba ayrılır. Bu ayrım ezberi kolaylaştırır: aynı gruptaki komutlar aynı işlenen biçimlerini kabul eder ve makine kodunda da benzer kodlanır. Şekil 6.3 üç grubun işlenen düzenini yan yana gösterir.
- 1. grup, iki işlenenli komutlar: ADD, SUB, CMP, AND, TEST, OR, XOR. Bu listeye eldeli toplama ADC ile ödünçlü çıkarma SBB de eklenir. Biçim
komut hedef, kaynak'tır; işlem hedef ile kaynak arasında yapılır ve sonuç hedefe yazılır. - 2. grup, çarpma ve bölme: MUL, IMUL, DIV, IDIV. Komutta tek bir işlenen yazılır. İşlemin öteki yarısı ve sonucun gideceği yer sabittir: AL, AX ya da DX:AX. Bunlara örtük işlenen denir.
- 3. grup, tek işlenenli komutlar: INC, DEC, NOT, NEG. İşlenen hem okunur hem yazılır:
INC CXCX'i okur, 1 ekler ve sonucu yine CX'e yazar.
Tablo 6.4 her komutun ne yaptığını tek satırda özetler. Kaydırma ve döndürme komutları da tek işlenen alır, ama kaydırma sayısını ikinci bir işlenen olarak verdikleri için ayrı bir aile olarak ele alınır.
| Grup | Komut | Ne yapar? |
|---|---|---|
| 1 | ADD | iki değeri toplar, sonucu hedefe yazar |
SUB | kaynağı hedeften çıkarır, sonucu hedefe yazar | |
CMP | kaynağı hedeften çıkarır, sonucu saklamaz; yalnız bayrakları ayarlar | |
AND | bit düzeyinde VE | |
TEST | bit düzeyinde VE; sonucu saklamaz | |
OR | bit düzeyinde VEYA | |
XOR | bit düzeyinde dışlayan VEYA | |
| 2 | MUL | işaretsiz çarpma |
IMUL | işaretli çarpma | |
DIV | işaretsiz bölme (bölüm ve kalan) | |
IDIV | işaretli bölme (bölüm ve kalan) | |
| 3 | INC | 1 artırır |
DEC | 1 azaltır | |
NOT | bütün bitleri tersler (birler tümleyeni) | |
NEG | işaretini değiştirir (ikiye tümleyen) |
6.2.2İki işlenenli komutların işlenenleri
1. gruptaki komutların hepsi aynı beş işlenen birleşimini kabul eder. Bunlar Tablo 6.5'te örnekleriyle verilmiştir. REG, sekiz ya da on altı bitlik genel amaçlı yazmaçlardan biridir: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, SI, DI, BP, SP. Memory bir bellek işlenenidir ([BX], [BX+SI+7], bir değişken adı). Immediate ise komutun içine yazılmış anlık değerdir (5, −24, 3Fh, 10001101b).
| Biçim | Örnek | Makine kodu |
|---|---|---|
| REG, memory | ADD AX, [BX] | 03 07 |
| memory, REG | SUB var1, CL | 28 0E + adres |
| REG, REG | XOR AX, AX | 33 C0 |
| memory, immediate | AND BYTE PTR [SI], 0Fh | 80 24 0F |
| REG, immediate | CMP AL, 'A' | 3C 41 |
Kurallar MOV'un kurallarına benzer ve aynı donanım kısıtından gelir. İki işlenen birden bellekte olamaz: ADD [BX], [SI] geçersizdir, çünkü komutun ModR/M baytı yalnız tek bir bellek adresi tarif edebilir. İki işlenen aynı boyutta olmalıdır: ADD AL, BX 8 bitlik bir yazmaca 16 bitlik bir sayıyı eklemeye çalışır ve assembler reddeder. Hedef sabit olamaz: ADD 5, AL sonucu nereye yazacağını bilemez. Kesim yazmaçları aritmetik işleneni olamaz: ADD DS, AX geçersizdir; DS yalnız MOV, PUSH ve POP ile değiştirilir.
Bellek işleneni ile sabit birlikte kullanıldığında boyut ikisinden de anlaşılmaz. AND [SI], 0Fh yazımında 0Fh bir bayt da bir sözcük de olabilir; bu yüzden BYTE PTR ya da WORD PTR belirtilmelidir. İşlenenlerden biri yazmaç ya da türü bilinen bir değişkense boyut ondan alınır.
İki değişkeni doğrudan toplamaya çalışmak: ADD toplam, sayi. İki işlenen de bellektedir ve bu komut yoktur. Biri önce bir yazmaca alınır: MOV AX, sayi ve ADD toplam, AX.
6.2.3Sonuç nereye yazılır, bayraklar nasıl etkilenir?
İki işlenenli bir işlemden sonra sonuç her zaman ilk işlenende, yani hedefte saklanır. SUB AX, BX komutu AX − BX'i hesaplar ve AX'e yazar; BX değişmez. CMP ile TEST bu kuralın iki istisnasıdır. CMP, SUB'ın yaptığı çıkarmayı yapar ama sonucu atar. TEST, AND'in yaptığı bit bit VE'yi yapar ama sonucu atar. İkisi de yalnız bayrakları değiştirir ve böylece bir yazmacı bozmadan sınamayı mümkün kılar.
Bayrak etkisi, işlemin aritmetik mi mantık mı olduğuna bağlıdır. Tablo 6.6 iki aileyi karşılaştırır.
| Komut | CF | ZF | SF | OF | PF | AF |
|---|---|---|---|---|---|---|
ADD, ADC, SUB, SBB, CMP | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
AND, OR, XOR, TEST | 0 | ✓ | ✓ | 0 | ✓ | ? |
Aritmetik komutlar altı durum bayrağının hepsini sonuca göre ayarlar. Mantık komutlarında ise elde kavramı yoktur: her bit kendi başına işlenir ve bir bitten ötekine hiçbir şey geçmez. Bu yüzden CF ve OF her zaman 0 yapılır. ZF, SF ve PF sonuca göre ayarlanır. AF'nin değeri Intel belgelerinde tanımsız bırakılmıştır; bir program mantık komutundan sonra AF'ye güvenmemelidir. Tanımsız, bayrağın rastgele olduğu anlamına gelmez; gerçek işlemcide belirli bir değer alır, ama bu değer hiçbir belgede söz verilmiş değildir ve başka bir x86 işlemcide farklı çıkabilir.
6.2.4Süre: işlemci içinde ve yolda geçen zaman
Bir komutun süresi, işlenenlerinin nerede durduğuna sıkı sıkıya bağlıdır. Yazmaçtaki iki sayı ALU'ya doğrudan girer; ADD AX, BX 8086'da 3 saat turu sürer. Bellek işleneni varsa EU önce etkin adresi hesaplar, sonra BIU'dan bir okuma yol döngüsü ister. Bir yol döngüsü en az 4 saat turudur: adres yola konur, bellek okunur, veri yongaya alınır. Hedef bellekteyse sonucu geri yazmak için bir yol döngüsü daha gerekir. Bu okuma-değiştirme-yazma sırasında 8086'nın RD ucu önce, WR ucu sonra etkin olur. Tablo 6.7 Intel'in 8086 kılavuzundaki süreleri verir; EA, etkin adresin hesap süresidir (5–12 tur).
| Biçim | ADD, ADC, SUB, SBB, AND, OR, XOR | CMP | TEST |
|---|---|---|---|
| reg, reg | 3 | 3 | 3 |
| reg, mem | 9 + EA | 9 + EA | 9 + EA |
| mem, reg | 16 + EA | 9 + EA | 9 + EA |
| reg, imm | 4 | 4 | 5 |
| mem, imm | 17 + EA | 10 + EA | 11 + EA |
| AL/AX, imm | 4 | 4 | 4 |
Tablodaki farkların kaynağı yol döngüleridir. ADD [BX], AX belleği hem okur hem yazar ve 16 + EA tur sürer. CMP [BX], AX aynı çıkarmayı yapar ama sonucu yazmadığı için yalnız okur: 9 + EA tur. 5 MHz'lik bir 8086'da bir saat turu 200 ns'dir; ADD [BX], AX (EA = 5) yaklaşık 21 × 200 ns = 4,2 µs sürer. Aynı toplama iki yazmaç arasında 0,6 µs'dir. Sık kullanılan değerleri döngü boyunca yazmaçta tutmanın nedeni budur.
6.3Toplama, çıkarma ve karşılaştırma
6.3.1Kod izleme: ADD, SUB, CMP
Aşağıdaki program bir toplama, bir çıkarma ve iki karşılaştırma yapar; son satır negatif bir sayıyı sıfıra tamamlar. Her komutun bayrak etkisi, işlemin bit düzeyindeki ayrıntısıyla birlikte izlenir.
mov al, 0F0h ; 240
add al, 20h ; 240 + 32 = 272
mov bl, 7
sub bl, 9 ; 7 - 9
cmp al, 10h ; AL - 10h
cmp bl, 0
add bl, 2 ; FEh + 2| Komut | Makine kodu | AL | BL | CF | ZF | SF | OF | PF | AF |
|---|---|---|---|---|---|---|---|---|---|
mov al, 0F0h | B0 F0 | F0h | 00h | 0 | 0 | 0 | 0 | 0 | 0 |
add al, 20h | 04 20 | 10h | 00h | 1 | 0 | 0 | 0 | 0 | 0 |
mov bl, 7 | B3 07 | 10h | 07h | 1 | 0 | 0 | 0 | 0 | 0 |
sub bl, 9 | 80 EB 09 | 10h | FEh | 1 | 0 | 1 | 0 | 0 | 1 |
cmp al, 10h | 3C 10 | 10h | FEh | 0 | 1 | 0 | 0 | 1 | 0 |
cmp bl, 0 | 80 FB 00 | 10h | FEh | 0 | 0 | 1 | 0 | 0 | 0 |
add bl, 2 | 80 C3 02 | 10h | 00h | 1 | 1 | 0 | 0 | 1 | 1 |
Satır 2. F0h + 20h = 110h'dir. Dokuz bitlik bu sonucun düşük sekiz biti, 10h, AL'ye yazılır; dışarı taşan 1 CF'ye düşer. İşaretli yorumda −16 + 32 = 16 doğrudur ve OF = 0'dır. 10h = 0001 0000b'de tek bir 1 vardır, PF = 0. Düşük yarımlar 0 + 0 olduğundan AF = 0. Makine kodu 04 20'dir: 04, “AL'ye anlık bayt ekle” anlamına gelen kısa akümülatör biçimidir.
Satır 4. 7 − 9 = −2 bulunur ve 8 bitte ikiye tümleyenle FEh olarak yazılır. Küçük sayıdan büyük sayı çıkarıldığı için ödünç alınmıştır: CF = 1. Sonucun en anlamlı biti 1'dir, SF = 1. Düşük yarımlarda da 7 − 9 ödünç ister, AF = 1. İşaretli yorumda 7 − 9 = −2 doğrudur ve OF = 0'dır. Bu komut AL'yi değil BL'yi kullandığı için kısa biçim seçilemez; genel 80 /5 biçimi üç bayt tutar.
Satır 5. CMP AL, 10h, 10h − 10h = 0 çıkarmasını yapar. Sonuç sıfırdır, ZF = 1. Sonuç saklanmaz: AL hâlâ 10h'dir. Eşitlik sınamasının özü budur: iki sayı eşitse farkları sıfırdır ve ZF = 1 olur. Hemen ardından JE (ya da aynı komutun öteki adı JZ) gelirse atlama yapılır. Önceki satırdan kalan CF = 1 ve AF = 1 de silinmiştir; CMP bütün durum bayraklarını yeniden yazar.
Satır 6. CMP BL, 0, FEh − 0 işlemidir. Sonuç FEh'dir: sıfır değil (ZF = 0), işaret biti 1 (SF = 1), ödünç yok (CF = 0). Bu üç bayrak iki farklı yorumu taşır. İşaretsiz okunursa FEh = 254 > 0'dır; bunu CF = 0 söyler. İşaretli okunursa FEh = −2 < 0'dır; bunu SF ≠ OF söyler.
Satır 7. FEh + 2 = 100h'dir. BL'de 00h kalır; CF = 1 ve ZF = 1 olur. İşaretli yorumda −2 + 2 = 0 doğrudur, OF = 0. Bu satır CF ile OF arasındaki farkı bir kez daha gösterir: CF = 1, işaretli hesabın yanlış olduğu anlamına gelmez.
6.3.2CMP ile büyüklük karşılaştırması
CMP'nin asıl işi, iki sayının eşit olup olmadığını değil hangisinin büyük olduğunu bulmaktır. CMP A, B A − B'yi hesaplar. İşaretsiz sayılarda A < B ise çıkarma ödünç ister ve CF = 1 olur. İşaretli sayılarda durum daha inceliklidir. Fark taşmadıysa (OF = 0) sonucun işareti doğrudur ve A < B ise SF = 1'dir. Fark taşmışsa (OF = 1) işaret biti ters dönmüştür; bu kez A < B, SF = 0 ile gösterilir. İki durum tek bir kuralda birleşir: işaretli A < B ⇔ SF ≠ OF.
| Komutlar | CF | SF | OF | İşaretsiz | İşaretli |
|---|---|---|---|---|---|
AL = 05h, BL = FEh; CMP AL, BL | 1 | 0 | 0 | 5 < 254 | 5 > −2 (SF = OF) |
AL = 7Fh, BL = 80h; CMP AL, BL | 1 | 1 | 1 | 127 < 128 | 127 > −128 (SF = OF) |
AL = 80h, BL = 01h; CMP AL, BL | 0 | 0 | 1 | 128 > 1 | −128 < 1 (SF ≠ OF) |
İkinci satırda 7Fh − 80h = FFh çıkar: SF = 1. Ama işaretli 127 − (−128) = 255 bayta sığmaz, OF = 1'dir. İşaret biti burada yanıltır ve SF = OF olduğu için doğru karar “büyük”tür. Koşullu atlamalar bu kuralları hazır hâlde sunar. İşaretsiz karşılaştırmada JB/JA (below/above, CF ve ZF'ye bakar), işaretli karşılaştırmada JL/JG (less/greater, SF, OF ve ZF'ye bakar) kullanılır. Aynı CMP'den sonra hangi atlamanın seçildiği, sayıların nasıl yorumlanacağını belirler.
6.3.3ADC ile 32 bitlik toplama
8086'nın ALU'su en çok 16 bitlik sayıları toplar. 131 071 gibi 16 bite sığmayan bir sayı iki sözcüğe bölünerek saklanır: DX yüksek sözcüğü, AX düşük sözcüğü tutar ve çift DX:AX diye yazılır. İki 32 bitlik sayıyı toplamak için kâğıt üzerindeki toplamanın yöntemi izlenir: önce sağdaki basamaklar toplanır, eldesi soldaki basamaklara eklenir. Burada “basamak” 16 bitlik bir sözcüktür ve elde CF'de taşınır.
ADC (add with carry, eldeli toplama) hedef ← hedef + kaynak + CF işlemini yapar. SBB (subtract with borrow, ödünçlü çıkarma) hedef ← hedef − kaynak − CF işlemini yapar. İkisinin işlenen kuralları ve bayrak etkisi ADD ile SUB'ınkiyle aynıdır.
; DX:AX = 0001FFFFh, CX:BX = 00020001h
mov ax, 0FFFFh
mov dx, 0001h
mov bx, 0001h
mov cx, 0002h
add ax, bx ; düşük sözcükler
adc dx, cx ; yüksek + CF| Komut | Makine kodu | DX | AX | CX | BX | CF | ZF |
|---|---|---|---|---|---|---|---|
mov ax, 0FFFFh | B8 FF FF | 0000h | FFFFh | 0000h | 0000h | 0 | 0 |
mov dx, 0001h | BA 01 00 | 0001h | FFFFh | 0000h | 0000h | 0 | 0 |
mov bx, 0001h · mov cx, 0002h | BB 01 00 · B9 02 00 | 0001h | FFFFh | 0002h | 0001h | 0 | 0 |
add ax, bx | 03 C3 | 0001h | 0000h | 0002h | 0001h | 1 | 1 |
adc dx, cx | 13 D1 | 0004h | 0000h | 0002h | 0001h | 0 | 0 |
İlk sayı DX:AX = 0001FFFFh = 131 071, ikinci sayı CX:BX = 00020001h = 131 073'tür. ADD AX, BX düşük sözcükleri toplar: FFFFh + 0001h = 10000h. AX'e 0000h yazılır, dışarı çıkan 1 CF'ye düşer. Bu CF, yüksek sözcüğe taşınacak eldedir. ZF = 1 olması burada yanıltıcıdır: yalnız düşük sözcük sıfırdır, 32 bitlik toplam sıfır değildir. ADC DX, CX bu eldeyi kullanır: 0001h + 0002h + 1 = 0004h. Sonuç DX:AX = 00040000h = 262 144'tür ve 131 071 + 131 073 ile tutar.
Yöntem sözcük sayısından bağımsızdır. 48 bitlik bir sayı üç sözcükte tutulursa ilk sözcükler ADD ile, sonraki iki sözcük ADC ile toplanır. Elde zincir boyunca CF'de taşınır. ADD ile ADC arasına CF'yi değiştiren bir komut girmemelidir. MOV, INC, DEC ve LOOP CF'ye dokunmaz; bu yüzden bir döngü içinde sayaç artırılırken bile elde korunur. Buna karşılık araya giren bir ADD ya da CMP eldeyi siler.
6.3.4SBB ile 32 bitlik çıkarma
Çıkarmada da aynı düzen geçerlidir. En düşük sözcük SUB ile çıkarılır; düşük sözcük yetmezse bir üst sözcükten ödünç alınır ve bu ödünç CF'de bekler. SBB, üst sözcükten kaynakla birlikte bu ödüncü de çıkarır.
; DX:AX = 00040000h, CX:BX = 00010001h
mov ax, 0000h
mov dx, 0004h
mov bx, 0001h
mov cx, 0001h
sub ax, bx ; düşük sözcükler
sbb dx, cx ; yüksek - CF| Komut | Makine kodu | DX | AX | CX:BX | CF | SF |
|---|---|---|---|---|---|---|
mov ax, 0000h · mov dx, 0004h | B8 00 00 · BA 04 00 | 0004h | 0000h | 0000:0000h | 0 | 0 |
mov bx, 0001h · mov cx, 0001h | BB 01 00 · B9 01 00 | 0004h | 0000h | 0001:0001h | 0 | 0 |
sub ax, bx | 2B C3 | 0004h | FFFFh | 0001:0001h | 1 | 1 |
sbb dx, cx | 1B D1 | 0002h | FFFFh | 0001:0001h | 0 | 0 |
Birinci sayı 262 144, çıkarılan 65 537'dir. SUB AX, BX, 0000h − 0001h işlemini yapar. Düşük sözcük yetmez; ödünç alınır, AX = FFFFh ve CF = 1 olur. SBB DX, CX ödüncü öder: 0004h − 0001h − 1 = 0002h. Sonuç DX:AX = 0002FFFFh = 196 607'dir ve 262 144 − 65 537 ile tutar.
00012345h − 0000ABCDh işlemi SUB ve SBB ile nasıl yapılır? SUB sonrası CF kaçtır?
Birinci sayı DX:AX = 0001:2345h, ikinci sayı CX:BX = 0000:ABCDh olarak yüklenir. SUB AX, BX: 2345h − ABCDh negatiftir; 16 bitte 12345h − ABCDh = 7778h kalır ve ödünç alındığı için CF = 1 olur. SBB DX, CX: 0001h − 0000h − 1 = 0000h. Sonuç 00007778h = 30 584'tür (74 565 − 43 981).
İki sözcükte de SUB (ya da ADD) kullanmak. Böyle yazılırsa düşük sözcükten gelen ödünç ya da elde kaybolur; sonuç tam 10000h kadar yanlış çıkar. Kural: en düşük sözcükte ADD/SUB, sonraki her sözcükte ADC/SBB.
6.4Mantıksal işlemler
6.4.1Bit bit çalışan kapılar
Mantık komutları, sayısal tasarımdan bilinen kapıların yazılım karşılığıdır. Tablo 6.12 dört işlemin doğruluk tablosunu verir. AND ancak iki giriş de 1 ise 1 verir. OR en az bir giriş 1 ise 1 verir. XOR girişler farklıysa 1 verir. NOT tek girişlidir ve girişi tersler.
| A | B | A AND B | A OR B | A XOR B | NOT A |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 1 |
| 0 | 1 | 0 | 1 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 | 0 |
| 1 | 1 | 1 | 1 | 0 | 0 |
8086'nın mantık komutları bu kuralları bir sayının bütününe değil, bitlerine tek tek uygular. AND AL, BL komutu aslında sekiz bağımsız VE kapısıdır: AL'nin bit 0'ı BL'nin bit 0'ıyla, bit 1'i bit 1'iyle VE'lenir ve sonuç AL'nin aynı bitine yazılır. Komşu bitler arasında elde ya da ödünç geçmez. ALU'da bu işlem, toplayıcının elde zinciri devre dışı bırakılarak yapılır ve her bitte doğru kapının çıkışı seçilir. Bu yüzden mantık komutları toplama kadar hızlıdır: yazmaçlar arasında 3 saat turu.
Bit bit işlemenin bir sonucu, elde ve taşmanın anlamsız olmasıdır. 8086 bunu açıkça belirtir: AND, OR, XOR ve TEST'ten sonra CF = 0 ve OF = 0'dır. ZF, SF ve PF sonuca göre ayarlanır, AF tanımsızdır. NOT ise bir istisnadır: hiçbir bayrağı değiştirmez.
6.4.2Maskeleme
Mantık komutlarının en yaygın kullanımı bir sayının bazı bitlerini değiştirip ötekileri korumaktır. Bunun için ikinci işlenen olarak bir maske verilir. Maskenin her biti, karşı gelen bitin başına ne geleceğini seçer. Bu yönteme maskeleme (masking) denir.
- AND ile bit temizleme. x AND 0 = 0, x AND 1 = x'tir. Maskede 0 olan bitler sıfırlanır, 1 olanlar korunur.
- OR ile bit kurma. x OR 1 = 1, x OR 0 = x'tir. Maskede 1 olan bitler kesin 1 olur, 0 olanlar korunur.
- XOR ile bit tersleme. x XOR 1 = NOT x, x XOR 0 = x'tir. Maskede 1 olan bitler terslenir, 0 olanlar korunur.
- TEST ile bit sınama. TEST, AND yapar ama sonucu yazmaz. Maskedeki bit (ya da bitlerden biri) 1 ise ve sayıda da 1 ise sonuç sıfırdan farklıdır: ZF = 0. Sayı değişmez.
ASCII kodlamasında büyük ve küçük harfler yalnız bit 5'te ayrılır: 'A' = 41h = 0100 0001b, 'a' = 61h = 0110 0001b. Bu yüzden harf dönüşümü tek bir mantık komutuyla yapılır. Şekil 6.4 dört işlemi aynı harflerle gösterir.
Maske değerleri ikili yazıldığında anlamı açıkça görülür. DFh = 1101 1111b, yalnız bit 5'i 0 olan bir maskedir; AND ile bit 5'i siler. 20h = 0010 0000b, yalnız bit 5'i 1 olan maskedir; OR ile bit 5'i kurar, XOR ile tersler. Son şekilde BL = '7' = 37h = 0011 0111b, maske 01h ile TEST edilir. Sonuç 01h ≠ 0 olduğundan ZF = 0'dır: bit 0 birdir, yani sayı tektir.
6.4.3Sık kullanılan kalıplar
Bazı mantık komutu kullanımları o kadar yaygındır ki assembly programlarında neredeyse kalıp hâline gelmiştir. Tablo 6.13 en sık rastlananları toplar.
| Kod | Etki | Neden? |
|---|---|---|
XOR AX, AX | AX = 0, ZF = 1, CF = 0 | 2 bayt (33 C0); MOV AX, 0 3 bayttır (B8 00 00) ve bayraklara dokunmaz |
AND AL, 0Fh | '7' (37h) → 7 | ASCII rakamı sayıya çevirir; yüksek yarım silinir |
OR AL, 30h | 7 → '7' (37h) | 0–9 arası sayıyı ASCII rakama çevirir |
AND AL, 0DFh | 'a' → 'A' | bit 5'i temizler: büyük harf |
OR AL, 20h | 'A' → 'a' | bit 5'i kurar: küçük harf |
TEST AL, 1 | ZF = 0 ise tek | AL değişmeden bit 0 sınanır |
TEST AL, 80h | ZF = 0 ise negatif | işaret biti sınanır |
OR AX, AX | AX değişmez | ZF ve SF ayarlanır: AX sıfır mı, negatif mi? |
ASCII rakamları '0' = 30h'den '9' = 39h'ye kadar uzanır. Yüksek yarım her rakamda 3'tür, düşük yarım rakamın değeridir. AND AL, 0Fh yüksek yarımı sildiği için klavyeden okunan bir rakamı sayıya çevirir. OR AL, 30h bunun tersini yapar ve ekrana yazılacak karakteri hazırlar. Aynı iş SUB AL, 30h ve ADD AL, 30h ile de yapılabilir; mantık komutlarının farkı, girdi zaten dönüştürülmüşse yine doğru sonuç vermesidir.
OR AX, AX ilk bakışta anlamsızdır: bir sayıyı kendisiyle VEYA'lamak onu değiştirmez. Komutun amacı bayraklardır. AX sıfırsa ZF = 1, en anlamlı biti 1 ise SF = 1 olur. Aynı iş TEST AX, AX ve CMP AX, 0 ile de yapılır. OR ile TEST iki bayt tutar; CMP ise sabiti de taşıdığı için üç bayttır.
6.4.4Kod izleme: AND, OR, XOR, TEST
mov al, 'a' ; 61h
and al, 0DFh ; büyük harf
or al, 20h ; küçük harf
xor al, 20h ; tersle
mov bl, '7' ; 37h
and bl, 0Fh ; sayı 7
test bl, 1 ; tek mi?
xor bl, bl ; BL = 0| Komut | Makine kodu | AL | BL | ZF | SF | PF | CF | OF |
|---|---|---|---|---|---|---|---|---|
mov al, 'a' | B0 61 | 61h | 00h | 0 | 0 | 0 | 0 | 0 |
and al, 0DFh | 24 DF | 41h 'A' | 00h | 0 | 0 | 1 | 0 | 0 |
or al, 20h | 0C 20 | 61h 'a' | 00h | 0 | 0 | 0 | 0 | 0 |
xor al, 20h | 34 20 | 41h 'A' | 00h | 0 | 0 | 1 | 0 | 0 |
mov bl, '7' | B3 37 | 41h | 37h | 0 | 0 | 1 | 0 | 0 |
and bl, 0Fh | 80 E3 0F | 41h | 07h | 0 | 0 | 0 | 0 | 0 |
test bl, 1 | F6 C3 01 | 41h | 07h | 0 | 0 | 0 | 0 | 0 |
xor bl, bl | 32 DB | 41h | 00h | 1 | 0 | 1 | 0 | 0 |
61h AND DFh = 41h = 'A' bulunur. 41h = 0100 0001b'de iki tane 1 vardır, PF = 1. 41h OR 20h = 61h = 'a'; 0110 0001b'de üç tane 1 vardır, PF = 0. 61h XOR 20h yine 41h verir: bit 5 terslenmiştir. XOR komutu iki kez uygulandığında sayıyı eski hâline getirir; bu özellik basit şifreleme ve bit değiştirme işlerinde kullanılır.
BL'ye ASCII '7' = 37h yüklenir ve AND BL, 0Fh onu sayısal 7'ye çevirir. TEST BL, 1 07h AND 01h = 01h hesaplar; sonuç sıfır değildir, ZF = 0'dır ve sayı tektir. BL değişmeden 07h kalır. Son satırda BL kendisiyle XOR'lanır. Her bit kendisinden farklı olmadığı için sonuç 00h'dir; ZF = 1 olur. Sıfır tane 1 biti çift sayıdır, PF = 1.
Tablonun makine kodu sütunu, AL ile çalışan komutların kısa biçimlerini gösterir. AND AL, imm8 = 24, OR AL, imm8 = 0C, XOR AL, imm8 = 34, iki baytlık komutlardır. BL ile aynı işlemler genel 80 ya da F6 biçimine düşer ve üç bayt tutar. Bu, AL ve AX'in akümülatör olarak taşıdığı küçük ama sürekli bir üstünlüktür.
Bir biti sınamak için AND kullanmak ve sayıyı bozmak. AND AL, 80h işaret bitine bakar ama AL'nin öteki yedi bitini siler. Sayı sonra yeniden kullanılacaksa TEST yazılmalıdır. Aynı ilişki CMP ile SUB arasında da vardır.
6.5Çarpma ve bölme
6.5.1MUL ve IMUL
İki 8 bitlik sayının çarpımı 16 bite, iki 16 bitlik sayının çarpımı 32 bite kadar çıkabilir: 255 × 255 = 65 025 bir bayta değil ancak bir sözcüğe sığar. 8086 bu yüzden çarpmanın sonucunu her zaman işlenenin iki katı genişlikte bir yere yazar. Çarpmanın öteki işleneni ve sonucun yeri sabittir; komutta yalnız bir işlenen yazılır. Şekil 6.5 iki boyutu gösterir.
MUL işaretsiz, IMUL işaretli çarpma yapar. İşlenen bayt ise AX ← AL × işlenen; işlenen sözcük ise DX:AX ← AX × işlenen olur. İşlenen bir yazmaç ya da bellek işleneni olabilir (MUL BL, IMUL WORD PTR [SI]). 8086'da sabit işlenen yoktur: MUL 5 geçersizdir.
Çarpımın işaretsiz ya da işaretli olması sonucun bitlerini değiştirir; toplamadaki gibi tek bir işlem iki yorumu karşılamaz. AL = C8h ve BL = 03h için MUL BL 200 × 3 = 600 = 0258h verir. Aynı bitlerle IMUL BL ise C8h'yi −56 diye okur ve −56 × 3 = −168 = FF58h verir. Düşük baytlar (58h) aynıdır, yüksek baytlar farklıdır. Bu yüzden 8086'nın iki ayrı çarpma komutu vardır.
Çarpmadan sonra yalnız CF ve OF anlamlıdır ve ikisi her zaman eşittir. Sonucun üst yarısı (AH ya da DX) gerçekten gerekiyorsa CF = OF = 1 olur. MUL için “gerekli” üst yarının sıfırdan farklı olması demektir. IMUL için üst yarının alt yarının işaret uzantısından farklı olması demektir: sonuç alt yarıya işaretli olarak sığıyorsa üst yarı yalnız işaret bitinin kopyalarıdır ve bilgi taşımaz. SF, ZF, AF ve PF çarpmadan sonra tanımsızdır.
| AL, BL | Komut | Hesap | AX | CF = OF | Neden |
|---|---|---|---|---|---|
| C8h, 03h | MUL BL | 200 × 3 = 600 | 0258h | 1 | AH = 02h ≠ 0 |
| C8h, 03h | IMUL BL | −56 × 3 = −168 | FF58h | 1 | −168 işaretli bayta sığmaz |
| FEh, 05h | IMUL BL | −2 × 5 = −10 | FFF6h | 0 | AH = FFh, AL'nin işaret uzantısı |
| FEh, 05h | MUL BL | 254 × 5 = 1270 | 04F6h | 1 | AH = 04h ≠ 0 |
| 06h, 09h | MUL BL | 6 × 9 = 54 | 0036h | 0 | AH = 0 |
Çarpma 8086'nın en yavaş komutlarındandır. ALU'da donanım çarpıcı yoktur; EU, mikrokodla kaydır-topla yöntemini bit bit uygular. Intel kılavuzuna göre MUL yazmaç işleneniyle baytta 70–77, sözcükte 118–133 saat turu sürer; IMUL baytta 80–98, sözcükte 128–154 tur. Bellek işleneni varsa EA süresiyle birkaç tur daha eklenir. Süre aralık olarak verilir, çünkü mikrokod döngüsü çarpanın bitlerine göre farklı yollardan geçer. Bir toplama yalnız 3 tur sürdüğüne göre, sabit bir sayıyla çarpmayı kaydırma ve toplamaya çevirmek çoğu zaman kazançlıdır.
6.5.2DIV ve IDIV
Bölme, çarpmanın tersine işlenenin iki katı genişlikte bir bölünen ister. Bölen bir bayt ise bölünen AX'tir; bölen bir sözcük ise bölünen DX:AX'tir. Sonuç iki parçadır: bölüm ve kalan. Şekil 6.6 sözcük bölmeyi gösterir.
| Komut | İşlenen bayt ise | İşlenen sözcük ise |
|---|---|---|
DIV (işaretsiz) | AL ← AX / işlenen, AH ← kalan | AX ← DX:AX / işlenen, DX ← kalan |
IDIV (işaretli) | AL ← AX / işlenen, AH ← kalan | AX ← DX:AX / işlenen, DX ← kalan |
Bölmeden sonra bütün durum bayrakları tanımsızdır; bölme sonucu hakkında bayraklardan bilgi alınamaz. Bölme çarpmadan da yavaştır: DIV yazmaçla baytta 80–90, sözcükte 144–162 tur; IDIV baytta 101–112, sözcükte 165–184 tur sürer.
IDIV bölümü sıfıra doğru keser ve kalanı bölünenle aynı işaretli bırakır. −23 / 5 için bölüm −4,6'dır; sıfıra doğru kesilince −4 olur. Kalan, −23 = (−4) × 5 + kalan eşitliğinden −3 bulunur. Bu kurala göre her zaman bölünen = bölüm × bölen + kalan eşitliği sağlanır ve kalanın mutlak değeri bölenden küçüktür.
Bölme hatası (divide error), bölen 0 olduğunda ya da bölüm hedefine (AL ya da AX) sığmadığında oluşur. İşlemci bölmeyi bitirmez; 0 numaralı kesmeyi (INT 0) üretir. Kesme vektör tablosunun ilk girişi (0000:0000) bu kesmenin hizmet yordamını gösterir. DOS bu yordamda “Divide overflow” iletisini yazar ve programı sonlandırır.
Bölümün sığıp sığmayacağı bölmeden önce kolayca sınanır. Bayt bölmede bölüm 255'i aşmamalıdır; bu ancak AH < bölen ise sağlanır. Sözcük bölmede aynı kural DX < bölen biçimini alır. AX = 1000h (4096) ve BL = 2 için AH = 10h ≥ 2'dir: bölüm 2048 AL'ye sığmaz ve bölme hatası oluşur. DX:AX = FFFFFFFFh ve BX = 1 için de bölen sıfır olmadığı hâlde DX = FFFFh ≥ 1'dir ve bölüm sığmaz. IDIV'de sınır biraz daha dardır: 8086'da bayt bölümü −127…127, sözcük bölümü −32 767…32 767 aralığında olmalıdır; −128 ve −32 768 de bölme hatası verir.
8086'nın bölme hatasında yığına ittiği dönüş adresi bir ayrıntı daha taşır. INT 0 hizmeti çağrılırken itilen IP, bölme komutundan sonraki komutu gösterir. 80286 ve sonraki işlemcilerde ise bölme komutunun kendisini gösterir; böylece hata giderildikten sonra komut yeniden denenebilir. Bu fark yalnız kendi INT 0 yordamını yazan programları ilgilendirir.
6.5.3Çarpma ve bölme adım adım
Tablo 6.17 sunumdaki adım adım çarpma ve bölme örneklerini tek tabloda toplar. Her satır bağımsız bir denemedir; yazmaçlar satırın başında verilen değerlerle başlar.
| Başlangıç | Komut | Sonuç | Açıklama |
|---|---|---|---|
| AL = C8h, BL = 03h | MUL BL | AX = 0258h, CF = OF = 1 | 200 × 3 = 600; AL'ye sığmadı |
| AL = C8h, BL = 03h | IMUL BL | AX = FF58h, CF = OF = 1 | −56 × 3 = −168; işaretli bayta sığmaz |
| AL = FEh, BL = 05h | IMUL BL | AX = FFF6h, CF = OF = 0 | −2 × 5 = −10; AH yalnız işaret uzantısı |
| AX = 0017h, BL = 05h | DIV BL | AL = 04h, AH = 03h (AX = 0304h) | 23 = 4 × 5 + 3 |
| AX = FFE9h, BL = 05h | IDIV BL | AL = FCh, AH = FDh (AX = FDFCh) | −23 = (−4) × 5 + (−3) |
| AX = 1000h, BL = 02h | DIV BL | INT 0 | bölüm 2048 AL'ye sığmaz |
| DX:AX = FFFFFFFFh, BX = 0001h | DIV BX | INT 0 | bölüm FFFFFFFFh AX'e sığmaz |
Dördüncü satırda AX = 0017h = 23, BL = 5'tir. Bölüm 4 AL'ye, kalan 3 AH'ye yazılır ve yeni AX 0304h olur. Bu değer bir sayı olarak okunmamalıdır; AX'in iki yarısı artık iki ayrı sonuç taşır. Beşinci satırda aynı sayının negatifi bölünür. −23 = FFE9h'dir. Bölüm −4 = FCh, kalan −3 = FDh'dir; doğrulama: −4 × 5 + (−3) = −23.
6.5.4Bölmeye hazırlık: CBW ve CWD
Bölünen her zaman bölenin iki katı genişlikte olduğu için, bölmeden önce bölüneni genişletmek gerekir. Bölünecek sayı çoğu zaman yalnız AL'de ya da AX'te durur; üst yarının (AH ya da DX) ne olacağına programcı karar verir.
- İşaretsiz bölmeden önce üst yarı sıfırlanır:
MOV AH, 0ya daXOR DX, DX. Sayının değeri değişmez, yalnız önüne sıfırlar eklenir. - İşaretli bölmeden önce işaret genişletilir: işaret biti üst yarının bütün bitlerine kopyalanır. 8086'nın bunun için iki komutu vardır.
İşaret genişletme (sign extension), işaretli bir sayıyı değerini koruyarak daha geniş bir yere taşımaktır: en anlamlı bit yeni eklenen bütün bitlere kopyalanır. CBW (convert byte to word) AL'yi AX'e genişletir: AL'nin bit 7'si 0 ise AH = 00h, 1 ise AH = FFh olur. CWD (convert word to doubleword) AX'i DX:AX'e genişletir: AX'in bit 15'i 0 ise DX = 0000h, 1 ise DX = FFFFh olur. İki komut da işlenen almaz ve hiçbir bayrağı değiştirmez.
F9h, işaretli bayt olarak −7'dir. Aynı −7 sözcük olarak FFF9h yazılır: 65 536 − 7 = 65 529 = FFF9h. CBW bu dönüşümü tek komutta yapar ve 2 saat turu sürer. CWD ise 5 tur sürer. İkisi de örtük işlenen kullanır ve tek baytlık komutlardır (98 ve 99).
; işaretsiz: 1000 / 7
mov ax, 1000
xor dx, dx ; DX:AX = 1000
mov bx, 7
div bx ; AX = 142, DX = 6
; işaretli: -1000 / 7
mov ax, -1000
cwd ; DX = FFFFh
idiv bx ; AX = -142, DX = -6İlk parçada 1000 = 03E8h AX'e yüklenir ve DX sıfırlanır; bölünen DX:AX = 000003E8h olur. 1000 / 7 = 142, kalan 6'dır: AX = 008Eh, DX = 0006h. İkinci parçada −1000 = FC18h yüklenir. CWD, bit 15 bir olduğu için DX'e FFFFh koyar ve DX:AX = FFFFFC18h, 32 bitte −1000 olur. IDIV bölümü −142 = FF72h, kalanı −6 = FFFAh verir.
Sözcük bölmeden önce DX'i unutmak. DX'te önceki bir işlemden kalan sayı bölüneni büyütür. İşaretli −1000 için CWD yerine XOR DX, DX yazılırsa bölünen 0000FC18h = 64 536 olur. DIV bu durumda 64 536 / 7 = 9219 (2403h) gibi anlamsız bir sonuç verir. DX'te büyük bir değer kalmışsa bölüm sığmaz ve program bölme hatasıyla durur.
6.5.5Kod izleme: DIV, CWD ve IDIV
Aşağıdaki program aynı bölme işini üç kez yapar: önce işaretsiz, sonra işaretli, en sonda DX'i temizlemeyi unutarak.
MOV AX, 2000
XOR DX, DX ; DX:AX = 2000
MOV BX, 9
DIV BX ; işaretsiz bölme
MOV AX, -2000
CWD ; işaret genişlet
IDIV BX ; işaretli bölme
MOV AX, 2000 ; DX temizlenmedi!
DIV BX
RET| Komut | Makine kodu | DX | AX | BX | DX:AX (onluk) |
|---|---|---|---|---|---|
MOV AX, 2000 | B8 D0 07 | 0000h | 07D0h | 0000h | 2000 |
XOR DX, DX | 33 D2 | 0000h | 07D0h | 0000h | 2000 |
MOV BX, 9 | BB 09 00 | 0000h | 07D0h | 0009h | 2000 |
DIV BX | F7 F3 | 0002h | 00DEh | 0009h | bölüm 222, kalan 2 |
MOV AX, -2000 | B8 30 F8 | 0002h | F830h | 0009h | 0002F830h = 194 608 (yanlış bölünen) |
CWD | 99 | FFFFh | F830h | 0009h | FFFFF830h = −2000 |
IDIV BX | F7 FB | FFFEh | FF22h | 0009h | bölüm −222, kalan −2 |
MOV AX, 2000 | B8 D0 07 | FFFEh | 07D0h | 0009h | FFFE07D0h ≈ 4,29 milyar |
DIV BX | F7 F3 | bölme hatası: DX = FFFEh ≥ 9, bölüm AX'e sığmaz → INT 0 | |||
İlk bölmede 2000 / 9 = 222, kalan 2'dir: AX = 00DEh, DX = 0002h. Beşinci satır, CWD'nin neden gerektiğini gösterir. AX'e −2000 = F830h yüklenmiştir ama DX'te önceki bölmenin kalanı (2) durur. DX:AX'i 32 bitlik bir sayı olarak okuyan IDIV, bu hâliyle 194 608'i bölerdi. CWD, AX'in işaret biti 1 olduğu için DX'e FFFFh yazar ve bölüneni doğru −2000 yapar. IDIV −2000 / 9 için bölüm −222 = FF22h, kalan −2 = FFFEh verir. Kalan, bölünen gibi negatiftir.
Son iki satır klasik hatayı canlandırır. AX'e 2000 yüklenmiştir, ama DX'te IDIV'in kalanı (FFFEh) durur. İşaretsiz bölünen FFFE07D0h, yaklaşık 4,29 milyardır. Bunu 9'a bölmek 16 bite sığmayan bir bölüm verir; kural da bunu önceden söyler: DX = FFFEh ≥ 9. İşlemci INT 0 üretir ve program durur. Çözüm, bölmeden önce XOR DX, DX yazmaktır.
6.5.6İşaretli taşma: JO ve CBW
İki işaretli baytın toplamı bayta sığmayabilir. Bu durumda doğru sonuç, iki sayıyı önce sözcüğe genişletip 16 bitte toplamakla bulunur. Aşağıdaki program önce bayt toplamasını dener; taşma olursa sözcük toplamasına geçer. Programda iki atlama komutu vardır. JO etiket (jump if overflow) OF = 1 ise belirtilen etikete atlar, değilse bir sonraki komuta geçer. JMP etiket koşulsuz atlar. Tersi olan JNO, OF = 0 ise atlar.
mov al, -92 ; A4h
mov bl, -92
add al, bl ; 8 bit toplama
jo genislet ; OF = 1 ise
jmp bitti
genislet:
mov al, -92 ; birinci sayı
cbw ; AL -> AX
mov dx, ax
mov al, bl ; ikinci sayı
cbw
add ax, dx ; 16 bit toplama
bitti:
ret| Komut | AX | DX | CF | OF | SF | Açıklama |
|---|---|---|---|---|---|---|
mov al, -92 · mov bl, -92 | 00A4h | 0000h | 0 | 0 | 0 | −92 = 256 − 92 = 164 = A4h |
add al, bl | 0048h | 0000h | 1 | 1 | 0 | A4h + A4h = 148h; AL = 48h = +72 |
jo genislet | 0048h | 0000h | 1 | 1 | 0 | OF = 1: atlanır |
mov al, -92 · cbw | FFA4h | 0000h | 1 | 1 | 0 | AL'nin MSB'si 1 → AH = FFh |
mov dx, ax · mov al, bl · cbw | FFA4h | FFA4h | 1 | 1 | 0 | iki sayı da sözcükte −92 |
add ax, dx | FF48h | FFA4h | 1 | 0 | 1 | FFA4h + FFA4h = 1FF48h; AX = −184 |
Bayt toplamasında iki negatif sayının toplamı pozitif çıkmıştır: AL = 48h = +72. Doğru sonuç −184'tür ve −128…127 aralığında değildir. OF = 1 olduğu için JO genislet etiketine atlar. CF = 1 de olmuştur, ama işaretli yorumda CF'nin bir anlamı yoktur. Genişletme kısmında birinci sayı yeniden AL'ye yüklenir ve CBW ile FFA4h'ye genişletilir. DX'e saklanır. İkinci sayı BL'den alınır ve o da genişletilir. 16 bitlik toplam FFA4h + FFA4h = 1FF48h'dir; dışarı çıkan 1 CF'ye düşer, AX'te FF48h = −184 kalır. Sonuç sözcüğe sığmıştır, OF = 0'dır.
6.5.7CBW ile işaretli bayt dizisinin toplamı
Aynı fikir bir dizinin bütün elemanlarına uygulanabilir. Aşağıdaki program dört işaretli baytı 16 bitlik bir toplamda biriktirir. Program bir döngü kullanır ve döngüyü LOOP komutu kurar.
LOOP etiket komutu iki iş yapar: CX'i 1 azaltır, ardından CX sıfırdan farklıysa etikete atlar; CX sıfır olduysa bir sonraki komuta geçer. Döngüden önce CX'e tur sayısı yüklenir. CX = 4 ile başlayan bir döngü gövdesi dört kez çalışır. LOOP hiçbir bayrağı değiştirmez; CX'in azaltılması da bayraklara yansımaz.
org 100h
jmp basla
dizi db 100, -30, 90, -7
toplam dw 0
basla:
lea si, dizi
mov cx, 4
xor bx, bx ; BX = 16 bit toplam
dongu:
mov al, [si]
cbw ; AL -> AX (işaretle)
add bx, ax
inc si
loop dongu
mov toplam, bx ; 153 = 0099h
retProgramın başındaki JMP basla, veri tanımlarının üzerinden atlar. .COM programında kod ve veri aynı kesimdedir ve işlemci 0100h'den başlar; atlama olmasaydı dizinin baytları komut sanılırdı. LEA SI, dizi dizinin bağıl konumunu (0102h) SI'ya yükler. emu8086 bu satırı MOV SI, 0102h ile aynı üç baytlık koda (BE 02 01) çevirir. BX toplamı tutar ve sıfırlanır.
| Tur | AL | CBW sonrası AX | BX (toplam) | CX (tur başında) | Açıklama |
|---|---|---|---|---|---|
| 1 | 64h | 0064h | 0064h = 100 | 4 | +100, MSB = 0 → AH = 00h |
| 2 | E2h | FFE2h | 0046h = 70 | 3 | −30, MSB = 1 → AH = FFh |
| 3 | 5Ah | 005Ah | 00A0h = 160 | 2 | +90 |
| 4 | F9h | FFF9h | 0099h = 153 | 1 | −7; LOOP sonrası CX = 0 |
İkinci turda BX = 0064h'ye FFE2h eklenir: 0064h + FFE2h = 1 0046h. Dışarı çıkan elde atılır ve BX = 0046h = 70 kalır; 100 + (−30) = 70 doğrudur. Bu, ikiye tümleyenin bilinen özelliğidir: negatif bir sayıyı eklemek, elde atıldığında çıkarmakla aynı sonucu verir. Üçüncü turda toplam 160 olur. Toplama bayt olarak yapılsaydı 70 + 90 = 160 > 127 olacak, AL = A0h (−96) ve OF = 1 çıkacaktı. Sözcükte ise 160 rahatça sığar. Döngüden sonra toplam = 0099h = 153 belleğe yazılır.
İşaretli baytları genişletirken MOV AH, 0 kullanmak. Bu, sayıyı işaretsiz genişletir: −30 = E2h, 00E2h = 226 olarak eklenir; −7 de 249 sayılır ve toplam 153 yerine 665 çıkar. İşaretsiz veride MOV AH, 0, işaretli veride CBW kullanılır.
6.5.8CWD ve ADC ile 32 bitlik toplam
Elemanlar sözcük olduğunda toplam 16 bite de sığmayabilir. Çözüm, her elemanı CWD ile 32 bite genişletmek ve 32 bitlik bir toplamda ADD-ADC çiftiyle biriktirmektir.
org 100h
jmp basla
dizi dw 30000, 25000, -4000
toplam dw 0, 0 ; 32 bit toplam
basla:
lea si, dizi
mov cx, 3
dongu:
mov ax, [si]
cwd ; AX -> DX:AX
add [toplam], ax ; düşük sözcük
adc [toplam+2], dx ; yüksek + CF
add si, 2
loop dongu
retToplam iki sözcüktür: düşük sözcük toplam'da, yüksek sözcük toplam+2'de durur. Sözcük dizisinde her eleman iki bayt tuttuğu için SI her turda 2 artar.
| Tur | AX | CWD: DX | ADD sonrası toplam, CF | ADC sonrası toplam+2 | 32 bitlik toplam |
|---|---|---|---|---|---|
| 1 | 7530h (30 000) | 0000h | 7530h, CF = 0 | 0000h | 00007530h = 30 000 |
| 2 | 61A8h (25 000) | 0000h | D6D8h, CF = 0 | 0000h | 0000D6D8h = 55 000 |
| 3 | F060h (−4000) | FFFFh | C738h, CF = 1 | 0000h | 0000C738h = 51 000 |
İkinci turda 7530h + 61A8h = D6D8h bulunur. 16 bit işaretli yorumda bu sayı negatiftir ve OF = 1 olur; ama toplam 32 bit tutulduğu için sorun yoktur: yüksek sözcük 0000h'dir ve 55 000 doğrudur. Üçüncü turda −4000 = F060h'nin işaret biti 1'dir; CWD DX'e FFFFh koyar ve DX:AX = FFFFF060h, 32 bitte −4000 olur. Düşük sözcükte D6D8h + F060h = 1C738h; toplam C738h olur ve CF = 1'dir. Yüksek sözcükte 0000h + FFFFh + 1 = 10000h; toplam+2 = 0000h kalır. Sonuç 0000C738h = 51 000 = 30 000 + 25 000 − 4000'dir.
Programdaki add si, 2 CF'yi değiştirir; ama bu bir sorun değildir, çünkü sonraki turun eldesi yeniden ADD ile başlar. Elde yalnız ADD ile hemen ardındaki ADC arasında korunmalıdır. Bu iki komut bellek işleneni kullanır: her biri sözcüğü okur, toplar ve geri yazar. 8086'da her biri 16 + EA saat turu sürer ve iki yol döngüsü (bir okuma, bir yazma) gerektirir.
6.5.932 × 32 bitlik çarpma
8086 en çok 16 × 16 bit çarpar. 32 bitlik iki sayının çarpımı ise kâğıt üzerindeki çarpmanın yöntemiyle, 16 bitlik “basamaklarla” yapılır. A = A1:A0 ve B = B1:B0 olarak yarılara ayrılırsa
A × B = A1·B1·232 + (A1·B0 + A0·B1)·216 + A0·B0
bulunur. Dört kısmi çarpımın her biri bir MUL ile hesaplanır ve 32 bitlik sonucu DX:AX'e düşer. Kısmi çarpımlar, sonucun dört sözcüğüne (z3:z2:z1:z0, toplam 64 bit) konumlarına göre eklenir. 216 ile çarpılmış bir çarpım bir sözcük sola kaymıştır ve z2:z1'e eklenir; 232 ile çarpılmış olan iki sözcük sola kaymıştır ve z3:z2'ye eklenir.
Örnekte A = 9ABCDEF0h, B = EDCBFEDCh'dir. Kısmi çarpımlar A0 × B0 = DDF1B640h, A1 × B0 = 9A0B8190h, A0 × B1 = CF14F850h ve A1 × B1 = 8FBABF14h'dir. z0 sütununda yalnız B640h vardır. z1 sütununun toplamı DDF1h + 8190h + F850h = 257D1h'dir: 57D1h yazılır, 2 elde z2'ye geçer. z2 sütunu 9A0Bh + CF14h + BF14h + 2 = 22835h'dir: 2835h yazılır, 2 elde z3'e geçer. z3 = 8FBAh + 2 = 8FBCh. Sonuç 8FBC 2835 57D1 B640h'dir. Onluk denetim: 2 596 069 104 × 3 989 569 244 = 10 357 197 452 617 037 376.
Sütun eldesi 2 olabilir, ama bir ADD en çok 1 elde verir. Kod bu yüzden toplamaları tek tek yapar ve her ADD'nin eldesini hemen bir ADC ile bir üst sözcüğe taşır.
org 100h
mov ax, [x] ; A0
mul [y] ; A0*B0
mov [z], ax
mov [z+2], dx
mov ax, [x+2] ; A1
mul [y] ; A1*B0
add [z+2], ax
adc [z+4], dx
mov ax, [x] ; A0
mul [y+2] ; A0*B1
add [z+2], ax
adc [z+4], dx
adc [z+6], 0
mov ax, [x+2] ; A1
mul [y+2] ; A1*B1
add [z+4], ax
adc [z+6], dx
ret
x dw 0DEF0h, 9ABCh ; A = 9ABCDEF0h
y dw 0FEDCh, 0EDCBh ; B = EDCBFEDCh
z dw 4 dup(0) ; 64 bit sonuç| Komut(lar) | DX:AX | CF | z3 | z2 | z1 | z0 |
|---|---|---|---|---|---|---|
mov ax,[x] · mul [y] | DDF1:B640h | 1 | 0000h | 0000h | 0000h | 0000h |
mov [z],ax · mov [z+2],dx | DDF1:B640h | 1 | 0000h | 0000h | DDF1h | B640h |
mov ax,[x+2] · mul [y] | 9A0B:8190h | 1 | 0000h | 0000h | DDF1h | B640h |
add [z+2],ax | 9A0B:8190h | 1 | 0000h | 0000h | 5F81h | B640h |
adc [z+4],dx | 9A0B:8190h | 0 | 0000h | 9A0Ch | 5F81h | B640h |
mov ax,[x] · mul [y+2] | CF14:F850h | 1 | 0000h | 9A0Ch | 5F81h | B640h |
add [z+2],ax | CF14:F850h | 1 | 0000h | 9A0Ch | 57D1h | B640h |
adc [z+4],dx | CF14:F850h | 1 | 0000h | 6921h | 57D1h | B640h |
adc [z+6],0 | CF14:F850h | 0 | 0001h | 6921h | 57D1h | B640h |
mov ax,[x+2] · mul [y+2] | 8FBA:BF14h | 1 | 0001h | 6921h | 57D1h | B640h |
add [z+4],ax | 8FBA:BF14h | 1 | 0001h | 2835h | 57D1h | B640h |
adc [z+6],dx | 8FBA:BF14h | 0 | 8FBCh | 2835h | 57D1h | B640h |
İlk MUL'den sonra DX ≠ 0 olduğu için CF = 1'dir; bu CF yalnız “sonuç 16 bite sığmadı” der ve sonraki toplamalarla ilgisi yoktur. İkinci kısmi çarpımda z1 + AX = DDF1h + 8190h = 1 5F81h; z1 = 5F81h olur ve CF = 1'dir. ADC, z2 = 0 + 9A0Bh + 1 = 9A0Ch yapar. z2 önceden sıfır olduğu için burada yeni elde oluşmaz.
Üçüncü kısmi çarpım daha ilginçtir. z1 = 5F81h + F850h = 1 57D1h; elde z2'ye gider. z2 = 9A0Ch + CF14h + 1 = 1 6921h; bu kez elde z3'e taşmalıdır. adc [z+6], 0 komutu bu işi yapar: z3'e hiçbir sayı eklemez, yalnız eldeyi ekler ve z3 = 0001h olur. Bu satır unutulursa sonuç tam 248 eksik çıkar. Dördüncü kısmi çarpım z3:z2'ye eklenir: z2 = 6921h + BF14h = 1 2835h, z3 = 0001h + 8FBAh + 1 = 8FBCh. Bellekte z sırasıyla 40 B6 D1 57 35 28 BC 8F baytlarını taşır; little-endian düzende bu 8FBC283557D1B640h'dir.
Dört MUL'ün her biri 124–139 tur artı EA sürer; programın tamamı 8086'da 600 turu aşar. Aynı iş 80386'da tek bir 32 bitlik MUL ile yapılır. 16 bitlik bir işlemcide çok sözcüklü aritmetiğin bedeli budur.
6.6Tek işlenenli komutlar
6.6.1INC, DEC, NOT ve NEG
Üçüncü gruptaki dört komut tek bir işlenen alır. İşlenen bir yazmaç (AX, BX, …, SP ya da 8 bitlik yarılar) veya bir bellek işleneni olabilir; sabit olamaz. Bellek işleneninde boyut belirtilmelidir: INC BYTE PTR [BX]. Komut işleneni okur, değiştirir ve aynı yere geri yazar.
- INC işlenene 1 ekler, DEC 1 çıkarır. ZF, SF, OF, PF ve AF'yi sonuca göre ayarlar, ama CF'ye dokunmaz.
- NOT her biti tersler. Sonuç işlenenin birler tümleyenidir. Hiçbir bayrağı değiştirmez.
- NEG işlenenin işaretini değiştirir: 0 − işlenen hesaplar. Sonuç ikiye tümleyendir: bitler terslenir ve 1 eklenir. Altı durum bayrağının hepsini ayarlar; CF, işlenen sıfırdan farklıysa 1, sıfırsa 0 olur.
| Komut | CF | ZF | SF | OF | PF | AF |
|---|---|---|---|---|---|---|
INC, DEC | — | ✓ | ✓ | ✓ | ✓ | ✓ |
NOT | — | — | — | — | — | — |
NEG | ✓ (işlenen ≠ 0 ise 1) | ✓ | ✓ | ✓ | ✓ | ✓ |
INC ve DEC'in CF'yi koruması bir tasarım kararıdır. Çok baytlı ya da çok sözcüklü bir toplamada döngü her turda bir ADC yapar ve eldeyi bir sonraki tura taşır. Aynı döngü işaretçiyi INC ile ilerletir ve sayacı DEC ile azaltır. Bu iki komut CF'yi silseydi elde kaybolurdu. 8086 tasarımcıları bu yüzden INC ve DEC'i CF'den bağımsız bıraktı.
INC ve DEC aynı zamanda çok kısa komutlardır. 16 bitlik bir yazmacı artıran INC SI tek bayttır (46) ve 2 saat turu sürer; ADD SI, 1 üç bayt ve 4 turdur. 8 bitlik yazmaçta INC iki bayt (FE C0 gibi) ve 3 tur, bellekte 15 + EA turdur. NEG ve NOT yazmaçta 3, bellekte 16 + EA tur sürer.
6.6.2NEG: ikiye tümleyen adım adım
İkiye tümleyen, ikili sayılarda negatif sayıyı göstermenin standart yoludur. Bir sayının negatifi, bitleri terslenip 1 eklenerek bulunur. NEG bu iki adımı tek komutta yapar. Şekil 6.9 5'in ve −128'in negatifini adım adım gösterir.
| x | NOT x | NEG x | CF | OF | Açıklama |
|---|---|---|---|---|---|
| 05h (5) | FAh (−6) | FBh (−5) | 1 | 0 | pozitifin negatifi |
| FEh (−2) | 01h | 02h (2) | 1 | 0 | negatifin negatifi pozitiftir |
| 80h (−128) | 7Fh | 80h | 1 | 1 | +128 bayta sığmaz |
| 00h | FFh | 00h | 0 | 0 | ZF = 1; CF = 0 olan tek durum |
Birler tümleyeninin değeri, ikiye tümleyenden her zaman 1 eksiktir: NOT 5 = −6, NEG 5 = −5. −128'in durumu özeldir. İşaretli bayt aralığı −128…127'dir ve +128 bu aralıkta yoktur. NEG 80h sonucu yine 80h verir ve OF = 1 ile taşmayı bildirir. NEG'in CF kuralı da 0 − x çıkarmasından gelir: x sıfır değilse 0'dan pozitif bir sayı çıkarmak ödünç ister ve CF = 1 olur; x = 0 ise ödünç gerekmez.
6.6.3Kod izleme: INC, DEC, NOT, NEG
mov al, 0FFh
inc al ; FFh + 1
dec al ; 00h - 1
mov bl, 7Fh ; +127
inc bl ; +128 ?
not bl
neg bl| Komut | Makine kodu | AL | BL | CF | ZF | SF | OF | PF | AF |
|---|---|---|---|---|---|---|---|---|---|
mov al, 0FFh | B0 FF | FFh | 00h | 0 | 0 | 0 | 0 | 0 | 0 |
inc al | FE C0 | 00h | 00h | 0 | 1 | 0 | 0 | 1 | 1 |
dec al | FE C8 | FFh | 00h | 0 | 0 | 1 | 0 | 1 | 1 |
mov bl, 7Fh | B3 7F | FFh | 7Fh | 0 | 0 | 1 | 0 | 1 | 1 |
inc bl | FE C3 | FFh | 80h | 0 | 0 | 1 | 1 | 0 | 1 |
not bl | F6 D3 | FFh | 7Fh | 0 | 0 | 1 | 1 | 0 | 1 |
neg bl | F6 DB | FFh | 81h | 1 | 0 | 1 | 0 | 1 | 1 |
INC AL FFh'yi 00h yapar ve ZF = 1 olur. ADD AL, 1 olsaydı CF = 1 olacaktı; INC CF'yi değiştirmez ve CF = 0 kalır. DEC AL 00h − 1 = FFh hesaplar. Ödünç gerekmiştir, ama CF yine değişmez; yalnız SF = 1 olur. Bu iki satır, INC ve DEC'in sonucu sarmalayıp sarmalamadığını bayraklardan anlamanın tek yolunun ZF ve SF olduğunu gösterir.
BL'ye en büyük pozitif bayt olan 7Fh = +127 yüklenir. INC BL sonucu 80h olur: işaretsiz yorumda 128 doğrudur, işaretli yorumda −128'dir. İşaretli taşma olmuştur, OF = 1. NOT BL 80h'yi 7Fh'ye çevirir ama bayraklara dokunmaz: SF hâlâ 1 görünür, oysa BL'nin işaret biti artık 0'dır. Bu, NOT'tan sonra bayraklara bakmanın yanıltıcı olabileceğini gösterir. NEG BL +127'nin negatifini alır: −127 = 81h. İşlenen sıfır olmadığı için CF = 1'dir; sonuç aralıktadır, OF = 0.
6.7Kaydırma ve döndürme
6.7.1Kaydırma: SHL/SAL, SHR, SAR
Kaydırma komutları bir sayının bütün bitlerini bir yöne kaydırır. Bir uçtan çıkan bit CF'ye düşer, öteki uçtan yeni bir bit girer. Giren bitin ne olacağı komutu belirler. Sayısal tasarımdaki kaydırmalı yazmacın (shift register) yazılım karşılığıdır; tek fark, 8086'nın kaydırmayı bir saat darbesiyle değil bir komutla yapmasıdır. Şekil 6.10 üç kaydırmanın bit akışını gösterir.
SHL (shift logical left) ve aynı komutun öteki adı SAL bitleri sola kaydırır; sağdan 0 girer, en anlamlı bit CF'ye çıkar. Her kaydırma işaretsiz sayıyı 2 ile çarpar. SHR (shift logical right) mantıksal kaydırmadır: soldan 0 girer, en az anlamlı bit CF'ye çıkar; işaretsiz sayıyı 2'ye böler. SAR (shift arithmetic right) aritmetik kaydırmadır: en anlamlı bit yerinde kalır ve sağa da kopyalanır; işaretli sayıyı 2'ye böler.
96h = 1001 0110b örneğiyle üç kaydırma şöyle çalışır. SHL AL, 1: bitler sola kayar, b7 (1) CF'ye çıkar, sağdan 0 girer: 0010 1100b = 2Ch, CF = 1. İşaretsiz 150 × 2 = 300 bayta sığmamıştır; CF bunu gösterir. SHR AL, 1: b0 (0) CF'ye çıkar, soldan 0 girer: 0100 1011b = 4Bh = 75 = 150 / 2. SAR AL, 1: işaret biti 1 kopyalanır: 1100 1011b = CBh = −53. İşaretli yorumda 96h = −106'dır ve −106 / 2 = −53 doğrudur.
SAR'ın bölmesi IDIV'inkinden farklı yuvarlar. SAR her zaman eksi sonsuza doğru yuvarlar; IDIV sıfıra doğru keser. Pozitif sayılarda ikisi aynı sonucu verir, negatif tek sayılarda ayrılır. −7 = F9h için SAR AL, 1 FCh = −4 verir (−3,5 aşağı yuvarlanmıştır). −7 / 2 için IDIV ise −3 verir. Bu fark, bölmeyi kaydırmaya çevirirken akılda tutulmalıdır.
6.7.2Döndürme: ROL, ROR, RCL, RCR
Döndürme komutları kaydırmaya benzer, ama bir uçtan çıkan bit kaybolmaz; öteki uçtan geri girer. Bitler bir halka üzerinde döner. Şekil 6.11 dört döndürmenin bit akışını gösterir.
- ROL (sola döndür): b7 hem b0'a hem CF'ye gider.
- ROR (sağa döndür): b0 hem b7'ye hem CF'ye gider.
- RCL (CF üzerinden sola döndür): b7 CF'ye, eski CF b0'a gider.
- RCR (CF üzerinden sağa döndür): b0 CF'ye, eski CF b7'ye gider.
| Komut | CF = 0 ile | CF = 1 ile | Sonuç CF | OF |
|---|---|---|---|---|
SHL AL, 1 | 2Ch (0010 1100) | 2Ch | 1 | 1 |
SHR AL, 1 | 4Bh (0100 1011) | 4Bh | 0 | 1 |
SAR AL, 1 | CBh (1100 1011) | CBh | 0 | 0 |
ROL AL, 1 | 2Dh (0010 1101) | 2Dh | 1 | 1 |
ROR AL, 1 | 4Bh (0100 1011) | 4Bh | 0 | 1 |
RCL AL, 1 | 2Ch (0010 1100) | 2Dh (0010 1101) | 1 | 1 |
RCR AL, 1 | 4Bh (0100 1011) | CBh (1100 1011) | 0 | 1 / 0 |
Döndürmede hiçbir bit kaybolmaz. ROL AX, CL ile CL = 8 kez döndürülen 1234h, 3412h olur: yüksek ve düşük baytlar yer değiştirir. Döndürme komutları yalnız CF'yi ve (sayı 1 iken) OF'yi değiştirir; SF, ZF, PF ve AF'ye dokunmaz. Kaydırma komutları ise SF, ZF ve PF'yi sonuca göre ayarlar; AF tanımsızdır.
RCL ve RCR çok sözcüklü kaydırmanın anahtarıdır. 32 bitlik DX:AX'i 2 ile çarpmak için önce düşük sözcük kaydırılır, sonra yüksek sözcük CF üzerinden döndürülür: SHL AX, 1 AX'in b15'ini CF'ye çıkarır, RCL DX, 1 bu biti DX'in b0'ına sokar. Örneğin DX:AX = 0000FFFFh iken bu iki komuttan sonra DX:AX = 0001FFFEh olur. Bölme için ters sıra izlenir: SHR DX, 1 ve ardından RCR AX, 1.
6.7.3Kaydırma sayısı, OF ve süre
8086'da kaydırma sayısı yalnız iki biçimde verilir: sabit 1 ya da CL yazmacı. SHL AX, 1 ve SHR AL, CL geçerlidir. SHL AL, 4 gibi 1'den büyük bir sabit, 80186 ve sonraki işlemcilerde eklenmiştir; 8086 kodunda dört kez SHL AL, 1 ya da MOV CL, 4 ve SHL AL, CL yazılır. emu8086 bu biçimi kabul eder, ama ürettiği kod gerçek bir 8086'da çalışmaz. 8086, CL'deki sayıyı maskelemez; CL = 255 ise kaydırma 255 kez yapılır. 80286 ve sonrası sayıyı 5 bite (0–31) indirir.
OF kaydırma ve döndürmede yalnız sayı 1 iken tanımlıdır. Sola bir adımlık kaydırmada OF, işaret bitinin değişip değişmediğini söyler: OF = (yeni b7) XOR CF. 40h = 0100 0000b sola kaydırılınca 80h olur; işaret biti 0'dan 1'e döner ve OF = 1'dir. C0h sola kaydırılınca da 80h olur; ama işaret biti 1'den 1'e kalır ve OF = 0'dır: −64 × 2 = −128 doğrudur. Sayı CL ile verildiğinde ve 1'den büyükse OF tanımsızdır.
Süre bakımından kaydırma çarpmanın çok gerisindedir. Intel kılavuzuna göre yazmaçta SHL reg, 1 2 saat turu, SHL reg, CL 8 + 4n tur sürer (n kaydırma sayısı). Bellekte bir adımlık kaydırma 15 + EA, CL ile 20 + EA + 4n turdur. Bir bayt MUL'ün 70 turu aştığı düşünülürse, 2'nin kuvvetleriyle çarpıp bölmenin neden kaydırmaya bırakıldığı anlaşılır.
6.7.4Kod izleme: kaydırarak çarpma
Donanım çarpıcısı olmayan işlemciler çarpmayı kâğıt üzerindeki yöntemle yapar. Çarpanın bitleri sağdan sola tek tek incelenir. Bit 1 ise o anki çarpılan sonuca eklenir; her adımda çarpılan bir sola kaydırılır, yani 2 ile çarpılır. Aşağıdaki program 13 × 5'i bu yöntemle hesaplar.
org 100h
mov bl, carpilan ; 13
mov dl, carpan ; 5 = 0101b
mov cx, 4 ; 4 bit
kontrol:
shr dl, 1 ; en sağ bit → CF
jnc atla
add sonuc, bl ; bit 1 ise ekle
atla:
shl bl, 1 ; çarpılan × 2
loop kontrol
ret
carpilan db 13
carpan db 5
sonuc db 0Programda iki yeni denetim komutu vardır. JNC atla (jump if no carry) CF = 0 ise atla etiketine geçer ve toplamayı atlar. LOOP kontrol CX'i 1 azaltır ve CX sıfır değilse kontrol etiketine döner. CX = 4 ile döngü dört kez çalışır; çarpan 4 bitlik olduğu için bu yeterlidir.
| Tur | DL (SHR sonrası) | CF | Toplama | sonuc | BL (SHL sonrası) | CX (LOOP sonrası) |
|---|---|---|---|---|---|---|
| 1 | 02h | 1 | 0 + 0Dh | 0Dh (13) | 1Ah (26) | 3 |
| 2 | 01h | 0 | atlanır | 0Dh | 34h (52) | 2 |
| 3 | 00h | 1 | 0Dh + 34h | 41h (65) | 68h (104) | 1 |
| 4 | 00h | 0 | atlanır | 41h | D0h | 0 |
Çarpan 5 = 0101b'dir. Birinci turda SHR en sağdaki biti (1) CF'ye çıkarır ve DL = 02h olur. CF = 1 olduğundan JNC atlamaz; sonuca çarpılanın kendisi, 13 × 1 eklenir. İkinci turda çıkan bit 0'dır ve toplama atlanır. Üçüncü turda çıkan bit yine 1'dir. Bu sırada BL iki kez kaydırılmış ve 13 × 4 = 52 olmuştur; sonuç 13 + 52 = 65 = 41h olur. Dördüncü turda bit 0'dır. Döngü biter ve sonuc = 41h = 65 = 13 × 5 kalır. Son turda BL = D0h olmuştur; bu değer artık kullanılmaz ve bir bayta sığmayan 208 değerini gösterir.
Program çarpanın kaç bit olduğunu ve sonucun bayta sığacağını varsayar. Daha büyük çarpanlar için döngü 8 tur döner, çarpılan ve sonuç da sözcükte tutulur. Mikroişlemcilerin ilk kuşağında (Intel 8080, Zilog Z80) çarpma komutu yoktu ve her çarpma bu tür bir altyordamla yapılırdı. 8086'nın MUL komutu da içeride aynı yöntemi mikrokodla uygular.
6.7.5Kaydırmayla hızlı çarpma ve bölme
Sabit bir sayıyla çarpma, o sayının ikili açılımına göre kaydırma ve toplamaya dönüştürülebilir. 3 = 2 + 1 olduğundan AX × 3 = AX × 2 + AX'tir:
; AX ← AX × 3 = AX×2 + AX
mov bx, ax ; sakla
shl ax, 1 ; AX × 2
add ax, bx ; ×2 + ×1
; işaretsiz ÷ 8 ve kalan
mov dx, ax
and dx, 0007h ; kalan = alt 3 bit
mov cl, 3
shr ax, cl ; bölüm
; işaretli ÷ 4
mov cl, 2
sar ax, cl ; −7 → −2AX = 25 için ilk parça BX = 25, AX = 50 ve son olarak AX = 75 verir. Toplam süre 2 + 2 + 3 = 7 tur kadardır; MUL ise 118 turu aşar. İkinci parça 2'nin kuvvetine bölmeyi gösterir. Sağa n bit kaydırmak 2n'e böler; kaydırma sırasında dışarı çıkan alt n bit kalandır. Kalan, kaydırmadan önce AND ile alınır: alt üç biti 1 olan maske 0007h'dir. AX = 100 için kalan DX = 4, bölüm AX = 12 olur; 12 × 8 + 4 = 100.
Üçüncü parça işaretli sayıyı SAR ile böler. AX = −7 için SAR iki adımda −2 verir; IDIV ile −7 / 4 ise −1 olurdu. SAR eksi sonsuza, IDIV sıfıra doğru yuvarlar. Derleyiciler işaretli bölmeyi kaydırmaya çevirirken bu farkı gidermek için negatif sayılara önce (2n − 1) ekler.
6.7.6Kod izleme: 37 × 10 ve 370 ÷ 16
10 = 8 + 2 olduğu için AX × 10 = AX × 8 + AX × 2'dir. Aşağıdaki program önce 37'yi bu yolla 10 ile çarpar, ardından sonucu 16'ya böler ve kalanı ayrıca bulur.
org 100h
mov ax, 37
shl ax, 1 ; AX × 2
mov bx, ax ; sakla
shl ax, 1 ; AX × 4
shl ax, 1 ; AX × 8
add ax, bx ; ×8 + ×2
mov bx, ax
and bx, 000Fh ; kalan: alt 4 bit
mov cl, 4
shr ax, cl ; bölüm: ÷ 16
ret| Komut | Makine kodu | AX | AX (ikili) | BX | CL |
|---|---|---|---|---|---|
mov ax, 37 | B8 25 00 | 0025h = 37 | 0000 0000 0010 0101 | 0000h | 00h |
shl ax, 1 | D1 E0 | 004Ah = 74 | 0000 0000 0100 1010 | 0000h | 00h |
mov bx, ax | 8B D8 | 004Ah | 0000 0000 0100 1010 | 004Ah = 74 | 00h |
shl ax, 1 | D1 E0 | 0094h = 148 | 0000 0000 1001 0100 | 004Ah | 00h |
shl ax, 1 | D1 E0 | 0128h = 296 | 0000 0001 0010 1000 | 004Ah | 00h |
add ax, bx | 03 C3 | 0172h = 370 | 0000 0001 0111 0010 | 004Ah | 00h |
mov bx, ax | 8B D8 | 0172h | 0000 0001 0111 0010 | 0172h | 00h |
and bx, 000Fh | 83 E3 0F | 0172h | 0000 0001 0111 0010 | 0002h | 00h |
mov cl, 4 | B1 04 | 0172h | 0000 0001 0111 0010 | 0002h | 04h |
shr ax, cl | D3 E8 | 0017h = 23 | 0000 0000 0001 0111 | 0002h | 04h |
Her SHL, bitleri bir sola kaydırır ve sayıyı ikiye katlar: 37, 74, 148, 296. Arada saklanan ×2 değeri (74) ×8 değerine eklenir ve 370 = 37 × 10 bulunur. Bölme için önce kalan ayrılır: 370 = 0172h'nin alt dört biti 0010b = 2'dir ve AND BX, 000Fh yalnız onları bırakır. Sonra SHR AX, CL dört bit sağa kaydırır: 0172h → 0017h = 23. Denetim: 23 × 16 + 2 = 370.
Makine kodlarında iki ayrıntı göze çarpar. shl ax, 1 (D1 E0) ile shr ax, cl (D3 E8) farklı işlem kodları kullanır: D1 “bir kez”, D3 “CL kez” anlamına gelir. and bx, 000Fh ise 83 biçimiyle üç bayt tutar: sabit 0Fh tek bayta sığdığı için işlemci onu çalışırken 16 bite işaret genişletir. Intel kılavuzundaki sürelerle program 4 + 2 + 2 + 2 + 2 + 3 + 2 + 4 + 4 + (8 + 4 × 4) = 49 saat turu tutar; sözcük işlenenli bir MUL ile bir DIV ise birlikte 260 turu aşar.
6.8Örnek: dizi toplama
6.8.1Program
İki diziyi eleman eleman toplayıp sonucu üçüncü bir diziye yazmak, aritmetik komutların bellekle birlikte kullanıldığı en yalın örnektir. Aşağıdaki program vec3[i] = vec1[i] + vec2[i] işlemini i = 0…3 için yapar.
org 100h
jmp start
vec1 db 1, 2, 5, 6
vec2 db 3, 5, 6, 1
vec3 db ?, ?, ?, ?
start:
lea si, vec1
lea bx, vec2
lea di, vec3
mov cx, 4
sum:
mov al, [si]
add al, [bx]
mov [di], al
inc si
inc bx
inc di
loop sum
retProgramın yapısı satır satır şöyledir.
JMP startveri tanımlarının üzerinden atlar. .COM programında kod ve veri aynı kesimdedir ve yürütme 0100h'den başlar. Atlama olmasaydı işlemci vec1'in baytlarını (01 02 05 06) komut diye çözmeye çalışırdı.vec3 db ?, ?, ?, ?dört baytlık yer ayırır. Soru işareti “ilk değer önemsiz” demektir; emu8086 bu baytlara 0 yazar.LEA(load effective address) bir değişkenin değerini değil adresini (bağıl konumunu) yükler. SI, BX ve DI üç dizinin işaretçileri olur.MOV AL, [SI]SI'nın gösterdiği bellek baytını okur; bu, yazmaç dolaylı adreslemedir.ADD AL, [BX]ikinci dizinin elemanını ekler,MOV [DI], ALsonucu üçüncü diziye yazar.- Üç INC, işaretçileri bir sonraki elemana ilerletir. Diziler bayt dizisi olduğu için her işaretçi 1 artar; sözcük dizisi olsaydı 2 artması gerekirdi.
LOOP sumdöngüyü kurar.
LOOP komutu, döngü sayacı olarak her zaman CX'i kullanır. Her yürütülüşünde önce CX'i 1 azaltır, sonra CX'e bakar. CX sıfırdan farklıysa etikete (sum) geri atlar; sıfırsa bir sonraki komuta (RET) geçer. Döngüden önce CX'e 4 yüklendiği için gövde dört kez çalışır: LOOP'tan sonra CX sırasıyla 3, 2, 1 ve 0 olur ve son seferde atlama yapılmaz. LOOP bayrakları değiştirmez. 8086'da atlayan bir LOOP 17, atlamayan 5 saat turu sürer.
6.8.2Kod izleme
| Adım | SI | BX | DI | CX | AL | vec3 | Açıklama |
|---|---|---|---|---|---|---|---|
jmp start | 0000h | 0000h | 0000h | 0000h | 00h | 0 0 0 0 | veriler (0102h–010Dh) atlanır |
lea si / bx / di | 0102h | 0106h | 010Ah | 0000h | 00h | 0 0 0 0 | üç dizinin adresi |
mov cx, 4 | 0102h | 0106h | 010Ah | 0004h | 00h | 0 0 0 0 | 4 tur |
tur 1: mov al,[si] · add al,[bx] | 0102h | 0106h | 010Ah | 0004h | 04h | 0 0 0 0 | 1 + 3 = 4 |
tur 1: mov [di],al | 0102h | 0106h | 010Ah | 0004h | 04h | 4 0 0 0 | vec3[0] = 4 |
tur 1: üç inc · loop | 0103h | 0107h | 010Bh | 0003h | 04h | 4 0 0 0 | CX = 3 ≠ 0 → sum'a dön |
| tur 2 | 0104h | 0108h | 010Ch | 0002h | 07h | 4 7 0 0 | 2 + 5 = 7 |
| tur 3 | 0105h | 0109h | 010Dh | 0001h | 0Bh | 4 7 11 0 | 5 + 6 = 11 |
| tur 4 | 0106h | 010Ah | 010Eh | 0000h | 07h | 4 7 11 7 | 6 + 1 = 7; CX = 0, döngü biter |
Son turdan sonra işaretçiler dizilerin bir eleman ötesini gösterir. SI = 0106h artık vec2'nin ilk elemanıdır; BX = 010Ah vec3'ün ilk elemanıdır. Bu, dizilerin bellekte art arda durmasının doğal sonucudur ve döngü bittiği için zararsızdır. Toplamlar 255'i aşmadığı için CF hiçbir turda 1 olmaz; büyük elemanlarla toplam bir bayta sığmayabilir ve o zaman ADD'den sonra CF'ye bakmak gerekir.
6.8.3Bellekte program ve veri
Tablo 6.30 programın emu8086 bellek penceresindeki görünümünü verir (CS = DS = 0700h). Kod ve veri aynı kesimde, art arda durur.
| Adres | Baytlar | Anlamı |
|---|---|---|
| 0100h | EB 0C | JMP 010Eh; hedef = 0102h + 0Ch |
| 0102h | 01 02 05 06 | vec1 (veri) |
| 0106h | 03 05 06 01 | vec2 (veri) |
| 010Ah | 04 07 0B 07 | vec3 (program çalıştıktan sonra) |
| 010Eh | BE 02 01 | MOV SI, 0102h (LEA'nın kodu) |
| 0111h | BB 06 01 | MOV BX, 0106h |
| 0114h | BF 0A 01 | MOV DI, 010Ah |
| 0117h | B9 04 00 | MOV CX, 0004h; sabit little-endian |
| 011Ah | 8A 04 | MOV AL, [SI] (sum:) |
| 011Ch | 02 07 | ADD AL, [BX] |
| 011Eh | 88 05 | MOV [DI], AL |
| 0120h | 46 43 47 | INC SI, INC BX, INC DI (birer bayt) |
| 0123h | E2 F5 | LOOP 011Ah; F5h = −11 |
| 0125h | C3 | RET |
Tablodan üç şey okunur. Birincisi, emu8086 doğrudan adresli LEA SI, vec1 satırını MOV SI, 0102h ile aynı üç baytlık koda çevirir. İki komut aynı işi yapar: değişkenin bağıl konumu derleme anında bilindiği için onu bir sabit olarak yüklemek yeter. MASM ise LEA'yı kendi işlem koduyla (8D 36 02 01) dört bayt olarak üretir; o zaman sonraki adresler birer bayt kayar. İkincisi, atlama komutları hedef adresi değil, bir sonraki komuta göre uzaklığı taşır. JMP'nin uzaklığı 0Ch'dir: 0102h + 0Ch = 010Eh. LOOP'un uzaklığı F5h'dir; işaretli olarak −11'dir ve 0125h − 0Bh = 011Ah'ye, yani sum etiketine geri götürür. Üçüncüsü, MOV CX, 4 komutundaki sabit, bellekte düşük bayt önce, 04 00 olarak durur.
6.9BCD aritmetiği
6.9.1İkili kodlu onluk sayılar
İnsanlar onluk tabanda düşünür, işlemci ikili tabanda hesaplar. Hesap makineleri, kasa yazılımları, saat yongaları ve yedi parçalı göstergeler gibi onluk basamakları doğrudan gösteren aygıtlarda sayıyı her seferinde ikiliden onluğa çevirmek pahalıdır. Bunun yerine sayı basamak basamak saklanabilir.
İkili kodlu onluk (binary coded decimal, BCD) gösterimde her onluk basamak ayrı bir 4 bitlik grupta, 0000–1001 değerleriyle tutulur; 1010–1111 kullanılmaz. Paketsiz BCD (unpacked) her bayta bir basamak koyar; üst yarım 0'dır. Paketli BCD (packed) bir bayta iki basamak koyar: yüksek yarım onlar, düşük yarım birler basamağıdır.
| Gösterim | Bitler | Bayt(lar) |
|---|---|---|
| İkili | 0000 1110 | 0Eh |
| Paketsiz BCD | 0000 0001 · 0000 0100 | 01h 04h (AH, AL) |
| Paketli BCD | 0001 0100 | 14h |
| ASCII | 0011 0001 · 0011 0100 | 31h 34h ('1', '4') |
Paketli BCD'de 14 sayısı 14h baytıyla yazılır; onaltılık gösterimi onluk basamakları aynen okur. ASCII rakamlar ise paketsiz BCD'nin yüksek yarımına 3 eklenmiş hâlidir. Bu yakınlık sayesinde klavyeden okunan rakamlar AND 0Fh ile paketsiz BCD'ye, paketsiz BCD de OR 30h ile ekrana yazılacak karaktere dönüşür.
İşlemcinin toplayıcısı BCD bilmez; iki BCD sayıyı ikili sayılar gibi toplar. Sonuç çoğu zaman yanlış bir BCD'dir. 9 + 5 ikili olarak 0Eh verir; doğru BCD sonuç “14”tür. 8086 bu yüzden ikili işlemden sonra sonucu BCD'ye çeviren BCD düzeltme komutları sunar. Hepsi yalnız AL (ve gerektiğinde AH) üzerinde çalışır; BCD aritmetiği bu yüzden bayt bayt yapılır.
6.9.2Düzeltme komutları
| Komut | Ne zaman? | Etki | Süre (tur) |
|---|---|---|---|
AAA | paketsiz toplamadan sonra | AL'yi BCD'ye düzeltir, elde AH'ye | 4 |
AAS | paketsiz çıkarmadan sonra | AL'yi düzeltir, ödünç AH'den | 4 |
AAM | paketsiz çarpmadan sonra | AH ← AL / 10, AL ← AL mod 10 | 83 |
AAD | paketsiz bölmeden önce | AL ← AH × 10 + AL, AH ← 0 | 60 |
DAA | paketli toplamadan sonra | AL'deki iki basamağı düzeltir | 4 |
DAS | paketli çıkarmadan sonra | AL'deki iki basamağı düzeltir | 4 |
Düzeltmenin fikri 6 sayısına dayanır. Bir yarım bayt 16 değer alabilir, onluk basamak ise 10. Bir basamağın toplamı 9'u aşınca ikili toplam BCD'den 6 eksik kalır: 9 + 5 = 0Eh, oysa “14” = 14h = 0Eh + 6'dır. Basamak toplamı 15'i aşarsa (örneğin 9 + 8 = 17) yarım bayttan elde çıkar ve AF = 1 olur; bu durumda da 6 eklemek gerekir: 9 + 8 = 11h, 11h + 6 = 17h. Düzeltme komutları bu iki koşulu (yarım değeri > 9 ya da AF = 1) denetler ve gerekiyorsa 6 ekler ya da çıkarır.
AAA: AL'nin düşük yarımı 9'dan büyükse ya da AF = 1 ise AL'ye 6, AH'ye 1 eklenir ve AF = CF = 1 olur; değilse AF = CF = 0 olur. Her iki durumda AL'nin yüksek yarımı silinir. AAS aynı denetimi yapar, ama AL'den 6, AH'den 1 çıkarır.
DAA: Önce düşük yarım denetlenir: 9'dan büyükse ya da AF = 1 ise AL'ye 06h eklenir ve AF = 1 olur. Sonra yüksek basamak denetlenir: işlemden önceki AL 99h'den büyükse ya da CF = 1 ise AL'ye 60h eklenir ve CF = 1 olur. DAS aynı denetimlerle 06h ve 60h çıkarır.
Kuralların uygulanışı birkaç örnekte görülür. 38h + 45h = 7Dh; düşük yarım D > 9 olduğundan DAA 06h ekler ve AL = 83h olur: 38 + 45 = 83. 99h + 01h = 9Ah; düşük yarım A > 9, AL + 06h = A0h; ilk AL 99h'den büyük olduğundan 60h da eklenir ve AL = 00h, CF = 1 olur: 99 + 1 = 100, “00” ve yüzler basamağı CF'de. Çıkarmada 83h − 38h = 4Bh bulunur; düşük yarım B > 9 olduğundan DAS 06h çıkarır ve AL = 45h olur: 83 − 38 = 45. 25h − 48h = DDh; iki düzeltme de gerekir ve DAS AL = 77h, CF = 1 verir. Bu, 125 − 48 = 77 gibi okunur: CF = 1, yüksek basamaktan ödünç alındığını söyler.
6.9.3Kod izleme: AAA ile 9 + 5
Aşağıdaki program iki paketsiz BCD basamağını toplar, sonucu AAA ile düzeltir ve iki rakamı ekrana yazar. Yazdırma kısmı BIOS'un 10h kesmesinin 0Eh hizmetini (teletype) kullanır: AH = 0Eh iken INT 10h, AL'deki karakteri imlecin bulunduğu yere yazar ve imleci bir ilerletir.
; first number '9':
mov ah, 09h
; second number '5':
mov al, 05h
; al = al + ah = 09h + 05h = 0eh
add al, ah
; clear tens byte of bcd
xor ah, ah
; adjust result to bcd form, ah = 1, al = 4 -> '14'
aaa
; --- sonucu yazdır (ek) ---
or ax, 3030h ; ASCII'ye çevir
mov bl, al ; '4' sakla
mov al, ah ; '1'
mov ah, 0Eh
int 10h
mov al, bl
int 10h
ret| Komut | Makine kodu | AX | BL | CF | AF | Açıklama |
|---|---|---|---|---|---|---|
mov ah, 09h · mov al, 05h | B4 09 · B0 05 | 0905h | 00h | 0 | 0 | iki basamak |
add al, ah | 02 C4 | 090Eh | 00h | 0 | 0 | 9 + 5 = 0Eh; ikili doğru, BCD değil |
xor ah, ah | 32 E4 | 000Eh | 00h | 0 | ? | onlar basamağı temizlendi |
aaa | 37 | 0104h | 00h | 1 | 1 | E > 9: AL + 6 = 14h, AH + 1, yüksek yarım silinir |
or ax, 3030h | 0D 30 30 | 3134h | 00h | 0 | ? | '1', '4' |
mov bl, al · mov al, ah | 8A D8 · 8A C4 | 3131h | 34h | 0 | ? | '4' saklandı, AL = '1' |
mov ah, 0Eh · int 10h | B4 0E · CD 10 | 0E31h | 34h | 0 | ? | ekrana '1' |
mov al, bl · int 10h | 8A C3 · CD 10 | 0E34h | 34h | 0 | ? | ekrana '4' |
09h + 05h = 0Eh, ikili olarak 14'tür ve doğrudur; ama BCD'de E diye bir basamak yoktur. Düşük yarımlar 9 + 5 toplanırken bit 3'ten elde çıkmamıştır, AF = 0'dır. AAA düşük yarımın E > 9 olduğunu görür. AL'ye 6 ekler: 0Eh + 6 = 14h. AH'yi 1 artırır, AF ve CF'yi 1 yapar, AL'nin yüksek yarımını siler: AL = 04h. Sonuç AX = 0104h, yani paketsiz “14”tür. OR AX, 3030h iki baytın yüksek yarımlarına 3 koyar ve AX = 3134h, ASCII '1' ve '4' olur. Programın geri kalanı bu iki karakteri sırayla ekrana yazar.
XOR AH, AH satırının amacı, AAA'nın AH'ye ekleyeceği eldeyi temiz bir onlar basamağına yazmaktır. Bu satır olmasaydı AH'de birinci basamak (09h) kalır ve AAA onu 0Ah yapardı. AAA AL'nin yalnız düşük yarımına bakar; bu yüzden toplamadan önce ASCII rakamlarından 30h çıkarmak gerekmez. '7' + '8' = 37h + 38h = 6Fh'de düşük yarım F > 9'dur; AAA AH = 01h, AL = 05h verir ve OR AX, 3030h ile '1', '5' elde edilir.
6.9.4Kod izleme: AAS ile 5 − 8
; make 5 - 8, al = 0fdh (not in bcd form)
mov al, 05h
mov bl, 08h
sub al, bl
; convert to bcd, al = 7
; and 1 is borrowed from ah,
; like calculating 15 - 8:
aas
; --- yazdır ---
or al, 30h
mov ah, 0Eh
int 10h
mov ah, 0
int 16h
ret| Komut | AX | BL | CF | AF | SF | Açıklama |
|---|---|---|---|---|---|---|
mov al, 05h · mov bl, 08h | 0005h | 08h | 0 | 0 | 0 | |
sub al, bl | 00FDh | 08h | 1 | 1 | 1 | 5 − 8 = −3 = FDh |
aas | FF07h | 08h | 1 | 1 | ? | AL − 6 = F7h, AH − 1 = FFh; yüksek yarım silinir |
or al, 30h | FF37h | 08h | 0 | ? | 0 | AL = '7' |
mov ah, 0Eh · int 10h | 0E37h | 08h | 0 | ? | 0 | ekrana '7' |
mov ah, 0 · int 16h | — | 08h | 0 | ? | 0 | tuş beklenir; AL = basılan tuşun kodu |
5 − 8 = −3, ikiye tümleyenle FDh'dir. Ödünç alınmıştır (CF = 1); düşük yarımda da 5 − 8 ödünç istemiştir (AF = 1). AAS, AF = 1 olduğu için AL'den 6 çıkarır: FDh − 6 = F7h. AH'den 1 ödünç alır: 00h − 1 = FFh. AL'nin yüksek yarımını siler: AL = 07h. Sonuç AX = FF07h'dir. Ekranda “7” görünür.
Bu sonuç ilk bakışta tuhaftır: 5 − 8 = −3 olmalıydı. AAS'ın yaptığı, kâğıt üzerindeki çıkarmadaki gibi bir üst basamaktan 10 ödünç almaktır: 15 − 8 = 7. CF = 1, ödünç alındığını ve sonucun negatif olduğunu söyler. 7, −3'ün 10'a tümleyenidir (10 − 3 = 7). Çok basamaklı bir BCD çıkarmada bu ödünç, bir sonraki basamağın çıkarmasında SBB ile ödenir; AH'nin FFh olması da bunun bir yansımasıdır. AH = 01h ile başlansaydı (paketsiz “15”) AAS AX = 0007h, yani doğru “07” sonucunu verirdi.
6.9.5AAM ve AAD
Çarpma ve bölmenin düzeltmesi toplamadan farklı çalışır, çünkü burada 6 eklemek yetmez; sayı gerçekten 10'a bölünmeli ya da 10 ile çarpılmalıdır. 8086 bu iki işi mikrokodla yapan iki komut sunar.
AAM (ASCII adjust after multiply) AL'yi 10'a böler: bölüm AH'ye, kalan AL'ye yazılır. İki paketsiz basamağın çarpımını (en çok 9 × 9 = 81) iki paketsiz basamağa ayırır. AAD (ASCII adjust before division) AH'deki onlar ve AL'deki birler basamağını ikili sayıya çevirir: AL ← AH × 10 + AL, AH ← 0. Bölme komutundan önce yazılır. İkisi de SF, ZF ve PF'yi AL'ye göre ayarlar; OF, AF ve CF tanımsızdır.
mov al, 7
mov bl, 8
mul bl ; AX = 0038h (56)
aam ; AH = 05h, AL = 06h
mov ax, 0504h ; '54' paketsiz
aad ; AX = 0036h (54)
mov bl, 6
div bl ; AL = 09h, AH = 00hİlk parçada 7 × 8 = 56 = 0038h bulunur. AAM, 56'yı 10'a böler: AH = 5, AL = 6. AX = 0506h artık paketsiz “56”dır. İkinci parçada AX = 0504h paketsiz “54”tür. AAD bunu 5 × 10 + 4 = 54 = 36h'ye çevirir ve AH'yi sıfırlar. Normal DIV ile 54 / 6 = 9 bulunur: AL = 09h, kalan AH = 00h. AAD unutulsaydı DIV, 0504h = 1284'ü bölerdi ve 1284 / 6 = 214 olurdu.
AAM ve AAD'nin makine kodu ilginçtir: D4 0A ve D5 0A. İkinci bayt 0Ah, yani 10'dur. 8086'da bu bayt gerçekten taban olarak kullanılır; D4 10 AL'yi 16'ya böler. Intel bunu belgelememiştir ve assembler'lar her zaman 0Ah üretir. D4 00 sıfıra bölme olduğu için INT 0 üretir. AAM 83, AAD 60 saat turu sürer; mikrokodla yapılan bir bölme ve çarpmadır.
6.9.6Kod izleme: AAM, AAD ve DAA
MOV AL, 6
MOV BL, 9
MUL BL ; AX = 6 × 9
AAM ; basamaklara ayır
MOV AX, 0702h ; '72' paketsiz
AAD ; ikiliye çevir
MOV BL, 8
DIV BL
MOV AL, 19h ; paketli 19
ADD AL, 28h ; + 28
DAA ; BCD düzelt
RET| Komut | Makine kodu | AX | BL | AF | CF | Açıklama |
|---|---|---|---|---|---|---|
MOV AL, 6 · MOV BL, 9 | B0 06 · B3 09 | 0006h | 09h | 0 | 0 | |
MUL BL | F6 E3 | 0036h | 09h | ? | 0 | 6 × 9 = 54; AL'ye sığdı |
AAM | D4 0A | 0504h | 09h | ? | ? | paketsiz “54” |
MOV AX, 0702h | B8 02 07 | 0702h | 09h | ? | ? | paketsiz “72” |
AAD | D5 0A | 0048h | 09h | ? | ? | 7 × 10 + 2 = 72 = 48h |
MOV BL, 8 · DIV BL | B3 08 · F6 F3 | 0009h | 08h | ? | ? | 72 / 8 = 9, kalan 0 |
MOV AL, 19h | B0 19 | 0019h | 08h | ? | ? | paketli “19” |
ADD AL, 28h | 04 28 | 0041h | 08h | 1 | 0 | 9 + 8 = 17: yarımdan elde |
DAA | 27 | 0047h | 08h | 1 | 0 | AF = 1 → AL + 06h = 47h |
Programın son üç satırı DAA'nın AF'ye neden ihtiyaç duyduğunu gösterir. 19h + 28h = 41h'dir. Sonucun düşük yarımı 1'dir ve 9'dan büyük değildir; yalnız ona bakılsa düzeltme gerekmez gibi görünür. Ama birler basamağında 9 + 8 = 17 olmuş ve yarımdan dışarı elde çıkmıştır: AF = 1. DAA bu bayrağı görür ve 06h ekler: 41h + 06h = 47h. Sonuç “47” = 19 + 28'dir. Yüksek yarım 4 ≤ 9 ve CF = 0 olduğu için ikinci düzeltme yapılmaz.
Düzeltme komutunu toplamadan hemen sonra yazmamak. DAA ve AAA, AF ile CF'ye bakar; araya bir INC ya da MOV girse bile sorun çıkmayabilir, ama bayrakları değiştiren bir komut (ADD, CMP, AND) düzeltmeyi bozar. Bir başka hata, paketli BCD'de AAA ya da paketsiz BCD'de DAA kullanmaktır: AAA yüksek yarımı siler ve paketli sayının onlar basamağını yok eder.
6.10Örnek: sıcaklık dönüşümü
6.10.1Santigrattan Fahrenhayta
Santigrat derecesi (°C) ile Fahrenhayt derecesi (°F) arasındaki bağıntı f = c × 9 / 5 + 32'dir. Aşağıdaki program bu formülü tam sayı işlemleriyle hesaplar. Sıcaklıklar negatif olabileceği için işaretli komutlar (IMUL, IDIV) kullanılır.
org 100h
start:
;celsius to fahrenheit according to f = c * 9 / 5 + 32
mov cl, 15
mov al, 9
imul cl
mov cl, 5
idiv cl
add al, 32
ret| Komut | Makine kodu | AX | CL | CF | OF | Açıklama |
|---|---|---|---|---|---|---|
mov cl, 15 | B1 0F | 0000h | 0Fh | 0 | 0 | c = 15 |
mov al, 9 | B0 09 | 0009h | 0Fh | 0 | 0 | |
imul cl | F6 E9 | 0087h | 0Fh | 1 | 1 | 9 × 15 = 135; işaretli bayta sığmaz, AX'te eksiksiz |
mov cl, 5 | B1 05 | 0087h | 05h | 1 | 1 | bölen |
idiv cl | F6 F9 | 001Bh | 05h | ? | ? | 135 / 5 = 27, kalan 0 |
add al, 32 | 04 20 | 003Bh | 05h | 0 | 0 | 27 + 32 = 59 |
9 × 15 = 135 bulunur. 135, işaretli bir bayta (en çok 127) sığmaz; IMUL bu yüzden CF = OF = 1 yapar. Ama bu bir hata değildir: sonuç AX'te 0087h olarak eksiksiz durur. IDIV, AX'i 5'e böler; bölüm 27 = 1Bh AL'ye, kalan 0 AH'ye yazılır. 27 + 32 = 59 = 3Bh bulunur: 15 °C = 59 °F. Program bittiğinde AX = 003Bh, CX = 0005h'dir.
Formüldeki işlem sırası önemlidir. Önce 9 ile çarpılır, sonra 5'e bölünür. Tersi yapılsaydı, yani önce 9 / 5 hesaplansaydı, tam sayı bölmesi 1 verirdi ve f = c + 32 gibi yanlış bir formül ortaya çıkardı. Tam sayı aritmetiğinde bölme mümkün olduğunca sona bırakılır.
6.10.2Fahrenhayttan Santigrata
org 100h
start:
;fahrenheit to celsius according to: c = (f - 32) * 5 / 9
mov cl, 59
sub cl, 32
mov al, 5
imul cl
mov cl, 9
idiv cl
ret| Komut | Makine kodu | AX | CL | CF | OF | Açıklama |
|---|---|---|---|---|---|---|
mov cl, 59 | B1 3B | 0000h | 3Bh | 0 | 0 | f = 59 |
sub cl, 32 | 80 E9 20 | 0000h | 1Bh | 0 | 0 | 59 − 32 = 27 |
mov al, 5 | B0 05 | 0005h | 1Bh | 0 | 0 | |
imul cl | F6 E9 | 0087h | 1Bh | 1 | 1 | 5 × 27 = 135 |
mov cl, 9 · idiv cl | B1 09 · F6 F9 | 000Fh | 09h | ? | ? | 135 / 9 = 15, kalan 0 |
59 − 32 = 27, 5 × 27 = 135 ve 135 / 9 = 15 bulunur: 59 °F = 15 °C. Sonuç AL'dedir; AH kalanı (0) taşır. İki program birbirinin tersidir ve 15 ile 59 arasında tam olarak gidip gelir, çünkü iki bölmede de kalan sıfırdır.
6.10.3Neden IMUL ve IDIV? Sınırlar
Negatif sıcaklıklar işaretli komutların gerekçesidir. c = −10 için IMUL, −10 = F6h'yi işaretli okur ve AX = FFA6h = −90 verir. IDIV −90 / 5 = −18 = EEh hesaplar; 32 eklenince 0Eh = 14 bulunur: −10 °C = 14 °F doğrudur. Aynı hesap MUL ve DIV ile yapılsaydı F6h, 246 sanılırdı. MUL 9 × 246 = 2214 = 08A6h verir ve DIV bu sayıyı 5'e bölünce bölüm 442 AL'ye sığmaz: program bölme hatasıyla durur.
Tam sayı bölmesi kesirli kısmı atar. IDIV sıfıra doğru kestiği için c = 1 °C, 9 / 5 = 1 ile 33 °F olur; doğrusu 33,8'dir. Yuvarlama gerekiyorsa AH'deki kalan incelenir: kalan bölenin yarısından büyükse bölüme 1 eklenir.
Programın en önemli sınırı, sonucun işaretli bir bayt olan AL'de tutulmasıdır. Tablo 6.38 santigrattan fahrenhayta dönüşümün sınırlarını sayılarla gösterir.
| c | IMUL: AX | IDIV: AL | ADD sonrası AL | OF | Sonuç |
|---|---|---|---|---|---|
| −72 | FD78h (−648) | bölüm −129 sığmaz → INT 0 | bölme hatası | ||
| −71 | FD81h (−639) | 81h (−127) | A1h (−95) | 0 | doğru |
| −10 | FFA6h (−90) | EEh (−18) | 0Eh (14) | 0 | doğru |
| 53 | 01DDh (477) | 5Fh (95) | 7Fh (127) | 0 | doğru (en büyük) |
| 54 | 01E6h (486) | 61h (97) | 81h | 1 | yanlış: −127 okunur |
| 72 | 0288h (648) | bölüm 129 sığmaz → INT 0 | bölme hatası | ||
Geçerli aralık c ∈ [−71, 53]'tür. c = 54'te bölüm 97 sığar, ama 97 + 32 = 129 işaretli bayta sığmaz; ADD OF = 1 yapar ve AL = 81h, −127 diye okunur. c = 72'de bölümün kendisi (129) AL'ye sığmaz ve bölme hatası oluşur. Fahrenhayttan santigrata dönüşümde sınır f − 32'nin bir bayta sığmasıdır: f ∈ [−96, 127]. Daha geniş aralıklar için sözcük işlemleri gerekir: c AX'e alınır ve IMUL BX ile 16 bitlik çarpılır. Çarpım zaten DX:AX'te durduğu için CWD gerekmez; doğrudan IDIV ile bölünür.
6.11Hangi komut hangi bayrağı etkiler?
Tablo 6.39 bu notta anlatılan bütün komutların bayrak etkisini tek tabloda toplar. Bir programı izlerken ya da bir koşullu atlamanın neden beklenmedik davrandığını ararken ilk bakılacak yer burasıdır.
| Komut | CF | ZF | SF | OF | PF | AF |
|---|---|---|---|---|---|---|
ADD SUB CMP ADC SBB | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
NEG | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
INC DEC | — | ✓ | ✓ | ✓ | ✓ | ✓ |
AND OR XOR TEST | 0 | ✓ | ✓ | 0 | ✓ | ? |
NOT | — | — | — | — | — | — |
MUL IMUL | ✓ | ? | ? | ✓ | ? | ? |
DIV IDIV | ? | ? | ? | ? | ? | ? |
SHL SHR SAR | ✓ | ✓ | ✓ | ✓¹ | ✓ | ? |
ROL ROR RCL RCR | ✓ | — | — | ✓¹ | — | — |
AAA AAS | ✓ | ? | ? | ? | ? | ✓ |
AAM AAD | ? | ✓ | ✓ | ? | ✓ | ? |
DAA DAS | ✓ | ✓ | ✓ | ? | ✓ | ✓ |
CBW CWD MOV LEA LOOP | — | — | — | — | — | — |
Tablo birkaç genel kurala indirgenebilir. Veri taşıyan komutlar (MOV, LEA, XCHG, CBW, CWD) ve LOOP bayraklara dokunmaz. Toplama ve çıkarma ailesi bütün bayrakları yazar; INC ve DEC bunlardan yalnız CF'yi korur. Mantık komutları CF ile OF'yi temizler. Çarpma yalnız CF ile OF'ye, bölme hiçbir bayrağa anlamlı bir değer yazmaz. Döndürme yalnız CF ve OF'yi değiştirir. Tanımsız bir bayrağa güvenen program bir işlemcide çalışıp ötekinde bozulabilir.
6.12Sık yapılan hatalar
| Hatalı | Ne olur? | Doğrusu |
|---|---|---|
| İşaretli sayılarda CF'ye bakmak | −1 + 1 gibi doğru sonuçlar “taşmış” sanılır | işaretli: OF; karşılaştırmada SF ≠ OF |
DIV BX öncesi DX'i unutmak | eski DX bölüneni büyütür; yanlış sonuç ya da INT 0 | XOR DX, DX ya da CWD |
İşaretli bayt için MOV AH, 0 | −30 = E2h, 226 sayılır | CBW |
MUL 10 | 8086'da sabit işlenen yok; assembler reddeder | MOV BL, 10 · MUL BL |
SHL AX, 4 | 80186 komutu; 8086'da çalışmaz | MOV CL, 4 · SHL AX, CL |
| İki sözcükte de ADD | düşük sözcüğün eldesi kaybolur | ADD, ardından ADC |
| ADD ile ADC arasına CMP koymak | CF silinir | arada yalnız MOV, INC, DEC, LOOP |
| Bit sınamak için AND | sayının öteki bitleri silinir | TEST |
| Negatif sayıyı SAR ile bölüp IDIV sonucu beklemek | −7 / 2: SAR −4, IDIV −3 | yuvarlama yönünü bilerek seçmek |
| AAD'yi DIV'den sonra yazmak | paketsiz sayı ikili sanılır: 0702h = 1794 bölünür | AAD bölmeden önce |
MUL ve IMUL'ün işlenen boyutunu yanlış seçmek. MUL BX sözcük çarpmasıdır ve sonucu DX:AX'e yazar; DX'te saklanan bir değer varsa sessizce silinir. Bayt çarpma isteniyorsa işlenen 8 bitlik olmalıdır (MUL BL); o zaman yalnız AX değişir.
6.13Alıştırmalar ve çözümleri
Sunumdaki on bir alıştırma burada ara adımlarıyla çözülmüştür.
1. MOV AL, 0C0h ve ADD AL, 0C0h sonrası AL, CF, OF, SF, ZF, PF nedir?
C0h + C0h = 180h; AL = 80h, CF = 1. −64 + (−64) = −128 işaretli bayta sığar: OF = 0. SF = 1, ZF = 0. 80h'de tek bir 1 biti vardır: PF = 0.
2. CMP AL, BL sonrası işaretsiz “AL < BL” hangi bayrakla, işaretli “AL < BL” hangi bayraklarla anlaşılır?
İşaretsiz: CF = 1 (çıkarma ödünç istemiştir); bunu JB sınar. İşaretli: SF ≠ OF; bunu JL sınar. SF tek başına yetmez, çünkü fark taşarsa işaret biti ters döner.
3. AX = 0FFFFh, BX = 0FFFFh iken MUL BX sonrası DX, AX, CF nedir?
65 535² = 4 294 836 225 = FFFE0001h. DX = FFFEh, AX = 0001h. Üst yarı sıfır olmadığından CF = OF = 1.
4. AX = 0100h, BL = 0 iken DIV BL ne olur?
Sıfıra bölme: işlemci INT 0 (bölme hatası) üretir. AX ve bayraklar değişmez; denetim kesme hizmetine geçer.
5. Dizi toplama programını sözcük (DW) dizileri için değiştirin.
Veriler dw ile tanımlanır, AL yerine AX kullanılır ve işaretçiler 2'şer ilerler:
vec1 dw 1000, 2000, 3000, 4000
vec2 dw 5, 6, 7, 8
vec3 dw 4 dup(?)
; ...
sum:
mov ax, [si]
add ax, [bx]
mov [di], ax
add si, 2
add bx, 2
add di, 2
loop sumBu verilerle vec3 = 1005, 2006, 3007, 4008 olur. INC SI iki kez yazılabilir; ama ADD SI, 2 CF'yi değiştirir, INC değiştirmez. Döngü ADC kullanıyorsa INC'ler tercih edilir.
6. Bir bayttaki 1 bitlerini sayan programı SHL/TEST ve döngüyle yazın. PF ile karşılaştırın.
mov al, 0B5h ; sayılacak bayt
xor bl, bl ; sayaç
mov cx, 8
tekrar:
test al, 80h ; en soldaki bit 1 mi?
jz sifir
inc bl
sifir:
shl al, 1
loop tekrarB5h = 1011 0101b için BL = 5 bulunur. Her turda TEST en soldaki biti sınar, SHL bir sonraki biti en sola getirir. JZ (ZF = 1 ise atla) bit 0 ise sayacı artırmayı atlar. PF yalnız sayının tek ya da çift olduğunu söyler: B5h için PF = 0'dır (beş tek sayıdır), ama kaç tane 1 olduğunu vermez.
7. DX = 0005h, AX = 0000h, BX = 0005h iken DIV BX ne olur?
Bölünen DX:AX = 50000h = 327 680'dir. Bölüm 65 536 olurdu ve AX'e sığmaz. Kural da bunu söyler: DX = 5 ≥ bölen 5. Bölen sıfır olmasa da bölme hatası (INT 0) oluşur.
8. MOV AL, 70h ve ADD AL, 30h sonrası AL ve OF nedir? Doğru toplam 16 bitte nasıl elde edilir?
AL = A0h, OF = 1: 112 + 48 = 160 > 127. İki sayı ayrı ayrı CBW ile genişletilip 16 bitte toplanır: MOV AL, 70h · CBW · MOV DX, AX · MOV AL, 30h · CBW · ADD AX, DX → AX = 00A0h = 160.
9. 00012345h − 0000ABCDh işlemini SUB/SBB ile yapın. SUB sonrası CF kaçtır?
2345h − ABCDh = 7778h, CF = 1 (ödünç). 0001h − 0000h − 1 = 0000h. Sonuç 00007778h = 30 584.
10. 32 × 32 bit çarpmada dört kısmi çarpım sonucun hangi sözcüklerine eklenir?
A0·B0 → z1:z0; A1·B0 ve A0·B1 → z2:z1; A1·B1 → z3:z2. Her ADD'nin eldesi ADC ile bir üst sözcüğe taşınır; üçüncü kısmi çarpımdan sonra z3'e yalnız elde ekleyen bir ADC [z+6], 0 gerekebilir.
11. DX = 0000h, AX = 0FFFh, CF = 0 iken SHL AX, 1 ve RCL DX, 1 sonrası DX:AX nedir?
SHL: AX = 1FFEh, çıkan bit (b15 = 0) CF'ye: CF = 0. RCL: DX'e 0 girer, DX = 0000h. DX:AX = 00001FFEh = 0FFFh × 2.
6.14Özet
- Bayraklar: aritmetik ve mantık komutları sonucu hedefe, sonucun özelliklerini bayrak yazmacına yazar. CF işaretsiz taşmayı (toplamada elde, çıkarmada ödünç), OF işaretli taşmayı gösterir; OF = en anlamlı bite giren ve çıkan eldelerin XOR'u. ZF sonuç sıfır, SF en anlamlı bit, PF düşük baytta çift sayıda 1, AF bit 3'ten elde.
- İki işlenenli komutlar (ADD, ADC, SUB, SBB, CMP, AND, OR, XOR, TEST) sonucu hedefe yazar; CMP ve TEST yalnız bayrakları değiştirir. İki bellek işleneni, farklı boyut, sabit hedef ve kesim yazmacı kullanılamaz. Yazmaç-yazmaç 3, bellek hedefli 16 + EA saat turu.
- Karşılaştırma: CMP sonrası işaretsiz “küçük” CF = 1, işaretli “küçük” SF ≠ OF.
- Çok sözcüklü aritmetik: en düşük sözcükte ADD/SUB, üsttekilerde ADC/SBB; elde CF'de taşınır. 32 × 32 çarpma dört MUL ve bir ADD/ADC zinciriyle yapılır.
- Mantık komutları bit bit çalışır; CF = OF = 0 yapar. AND bit temizler, OR kurar, XOR tersler, TEST sınar. NOT hiçbir bayrağı değiştirmez.
- Çarpma ve bölme: bayt işlemde AX = AL × işlenen, AL = AX / işlenen (AH kalan); sözcükte DX:AX. MUL/IMUL yalnız CF = OF'yi anlamlı ayarlar; DIV/IDIV bütün bayrakları tanımsız bırakır. Bölen 0 ya da bölüm sığmazsa INT 0. IDIV sıfıra doğru keser.
- İşaret genişletme: CBW (AL → AX) ve CWD (AX → DX:AX) işaretli bölmeden ve işaretli toplamadan önce; işaretsiz veride üst yarı sıfırlanır.
- INC ve DEC CF'yi korur; NEG ikiye tümleyen alır ve işlenen sıfır değilse CF = 1 yapar; NEG 80h taşar.
- Kaydırma ve döndürme: sayı 1 ya da CL. SHL ×2, SHR işaretsiz ÷2, SAR işaretli ÷2 (eksi sonsuza yuvarlar). Döndürmede bit kaybolmaz; RCL/RCR CF ile 9 ya da 17 bitlik halka kurar ve çok sözcüklü kaydırmayı sağlar. Kaydırmayla çarpma MUL'den çok daha hızlıdır.
- BCD: AAA/AAS paketsiz toplama/çıkarma sonrası, DAA/DAS paketli toplama/çıkarma sonrası, AAM çarpma sonrası, AAD bölme öncesi. Düzeltme 6 (ve paketlide 60h) ekleyip çıkarmaya dayanır; AF bu yüzden vardır.
- Uygulama: dizi toplama LOOP ile, sıcaklık dönüşümü IMUL/IDIV ile yapılır; işaretli bayt sonucu yalnız belirli bir aralıkta doğrudur.
6.15Kavram listesi
Terimlerin İngilizce karşılıkları ve öteki bölümlerdeki kullanımları terim sözlüğünde bulunur.
- aritmetik-mantık birimi (ALU) arithmetic logic unit
- yürütme birimi (EU) execution unit
- bayrak yazmacı (FLAGS) flags register
- durum bayrağı / kontrol bayrağı status / control flag
- elde bayrağı (CF) carry flag
- taşma bayrağı (OF) overflow flag
- sıfır, işaret, eşlik bayrağı zero, sign, parity flag
- yardımcı elde bayrağı (AF) auxiliary carry flag
- elde / ödünç carry / borrow
- işaretli / işaretsiz sayı signed / unsigned number
- ikiye tümleyen / birler tümleyeni two's / one's complement
- aritmetik komut / mantık komutu arithmetic / logical instruction
- örtük işlenen implicit operand
- maskeleme masking
- çarpma / bölme multiplication / division
- bölünen / bölen / bölüm / kalan dividend / divisor / quotient / remainder
- işaret genişletme sign extension
- kaydırma / döndürme shift / rotate
- mantıksal / aritmetik kaydırma logical / arithmetic shift
- ikili kodlu onluk (BCD) binary-coded decimal
- paketli / paketsiz BCD packed / unpacked BCD
- BCD düzeltmesi BCD adjustment
- nibble
- döngü loop
- koşullu atlama conditional jump
- kesme vektörü interrupt vector
- mikrokod microcode
- saat turu clock cycle
- yol döngüsü bus cycle
6.16Kendini Dene
MOV AL, 9ChveADD AL, 9Chsonrası AL ve altı durum bayrağı nedir?Cevap
9Ch + 9Ch = 138h; AL = 38h, CF = 1. İşaretli −100 + (−100) = −200 bayta sığmaz ve iki negatifin toplamı pozitif (38h) çıkmıştır: OF = 1. SF = 0, ZF = 0. 38h = 0011 1000b'de üç tane 1: PF = 0. Düşük yarımlar C + C = 18h; bit 3'ten elde çıkar: AF = 1.
SUB AL, BLkomutu AL = 30h, BL = 50h için ve AL = 80h, BL = 01h için hangi CF ve OF değerlerini verir?Cevap
30h − 50h = E0h: işaretsiz 48 − 80 ödünç ister, CF = 1; işaretli 48 − 80 = −32 doğrudur, OF = 0. 80h − 01h = 7Fh: işaretsiz 128 − 1 = 127 doğrudur, CF = 0; işaretli −128 − 1 = −129 sığmaz ve negatiften pozitif çıkmıştır, OF = 1.
AL = F0h, BL = 10h iken
IMUL BLveMUL BLsonrası AX ve CF nedir? AL = F8h olsaydı IMUL ne verirdi?Cevap
IMUL: −16 × 16 = −256 = FF00h; −256 bayta sığmaz, CF = OF = 1. MUL: 240 × 16 = 3840 = 0F00h; AH ≠ 0, CF = 1. AL = F8h (−8) için IMUL −128 = FF80h verir; AH yalnız AL'nin işaret uzantısıdır ve CF = OF = 0 olur.
−50 sayısı 7'ye ve 8'e IDIV ile bölünüyor (AX = −50). Bölüm ve kalanlar nedir? −50, SAR ile 3 bit kaydırılırsa ne bulunur?
Cevap
IDIV 7: −50 / 7 = −7,14; sıfıra doğru −7 = F9h, kalan −50 − (−49) = −1 = FFh. IDIV 8: bölüm −6 = FAh, kalan −2 = FEh. SAR 3 (÷ 8): CEh = 1100 1110b → 1111 1001b = F9h = −7; −6,25 eksi sonsuza doğru −7'ye yuvarlanmıştır. IDIV ile SAR farklı sonuç verir.
CF = 1 ve AL = 81h iken art arda üç
RCL AL, 1komutundan sonra AL ve CF nedir?Cevap
1000 0001b, CF = 1 → 0000 0011b, CF = 1 → 0000 0111b, CF = 0 → 0000 1110b, CF = 0. AL = 0Eh, CF = 0. Dokuz bitlik halkada bitler dönmüş, hiçbiri kaybolmamıştır.
Paketli BCD ile 58 + 67 yapılıyor:
MOV AL, 58h,ADD AL, 67h,DAA. ADD ve DAA sonrası AL, AF ve CF nedir?Cevap
ADD: 58h + 67h = BFh; düşük yarımda 8 + 7 = 15 = Fh, elde yok (AF = 0), CF = 0. DAA: düşük yarım F > 9 → +06h = C5h, AF = 1; ilk AL BFh > 99h → +60h = 125h, AL = 25h, CF = 1. Sonuç “25” ve yüzler basamağında 1: 58 + 67 = 125.
AL = B6h iken
AND AL, 0F0h,OR AL, 05h,XOR AL, 0FFhsonrası AL ve PF nedir?Cevap
AND: B0h (düşük yarım silinir). OR: B5h (bit 0 ve 2 kurulur). XOR: 4Ah = 0100 1010b (bütün bitler terslenir). Üç tane 1 vardır: PF = 0. Mantık komutlarından sonra CF = OF = 0'dır.
48 bitlik DX:BX:AX = 0001 FFFF FFFFh sayısına 1 eklemek için hangi üç komut yazılır? Sonuç nedir?
Cevap
ADD AX, 1·ADC BX, 0·ADC DX, 0. AX = 0000h ve CF = 1; BX = FFFFh + 0 + 1 = 0000h ve CF = 1; DX = 0001h + 0 + 1 = 0002h. Sonuç 0002 0000 0000h'dir.INC AXkullanılsaydı CF değişmeyecek ve elde kaybolacaktı.MOV AX, 8000hveNEG AXsonrası AX, CF ve OF nedir? Neden?Cevap
AX = 8000h kalır; CF = 1 (işlenen sıfır değil), OF = 1. 8000h işaretli sözcükte −32 768'dir; +32 768 sözcük aralığına (en çok 32 767) sığmaz. NOT 8000h = 7FFFh, +1 = 8000h.
INC AXileADD AX, 1arasında makine kodu, süre ve bayrak bakımından ne fark vardır?Cevap
INC AXtek bayttır (40) ve 2 saat turu sürer; CF'ye dokunmaz.ADD AX, 1üç bayttır (05 01 00, ya da 83 biçimiyle de üç bayt) ve 4 tur sürer; CF dahil altı bayrağı yazar. AX = FFFFh iken ADD, CF = 1 yapar; INC CF'yi olduğu gibi bırakır.
6.17İleri okuma
- B. B. Brey, The Intel Microprocessors (8. baskı), Bölüm 5: aritmetik ve mantık komutları, BCD ve ASCII düzeltmeleri, kaydırma ve döndürme; bu notun konularını 8086'dan sonraki işlemcilere de uzatır.
- Intel, The 8086 Family User's Manual (1979), komut kümesi bölümü: her komutun bayrak etkisi, kodlaması ve saat turu süreleri birinci elden buradadır.
- K. R. Irvine, Assembly Language for x86 Processors (7. baskı), Bölüm 7: tam sayı aritmetiği, çok sözcüklü kaydırma ve toplama, ASCII ve paketli onluk aritmetik.
- emu8086 belgeleri, “8086 instructions”: her komutun kısa tanımı, bayrak tablosu ve örnek kodu; sunumdaki BCD örneklerinin kaynağıdır.
6.18Kaynaklar
- Intel, The 8086 Family User's Manual, 1979 — Instruction Set bölümü.
- B. B. Brey, The Intel Microprocessors, 8. baskı, Pearson, 2009 — Bölüm 5.
- emu8086 yardım belgeleri: “8086 instructions” ve “Arithmetic and logic instructions”.
- K. R. Irvine, Assembly Language for x86 Processors, 7. baskı, Pearson, 2014 — Bölüm 7.
- A. S. Tanenbaum, Modern Operating Systems, 3. baskı, Pearson, 2008.
- M. Bodur, Mikroişlemcilere Giriş: Assembler ile Yazılım ve Arayüz, EMO, 2014.
- Ş. Ç. Öztürk, O. Ergin, Intel 8086 ile Mikroişlemci Programlamaya Giriş, 2009.
İlgili sayfalar: Bölüm 6 sunumu · Çalışma soruları · Terim sözlüğü · © Sercan Külcü. Tüm hakları saklıdır.