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.

1514131211OF10DF9IF8TF7SF6ZF54AF32PF10CFkontrol bayrakları: DF, IF, TFdurum bayrakları: OF, SF, ZF, AF, PF, CF
Şekil 6.1: 16 bitlik bayrak yazmacı. Kesik çizgili bitler 8086'da kullanılmaz; altı durum bayrağı (CF, PF, AF, ZF, SF, OF) aritmetik ve mantık komutlarının sonucunu tutar.

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.

Tablo 6.1: 8086'nın bayrakları. Durum bayrakları sonucu anlatır; kontrol bayrakları işlemcinin davranışını yönetir.
BitBayrakTür1 olur, eğer…Örnek
0CF (elde)durumişaretsiz taşma: toplamada en anlamlı bitten dışarı elde, çıkarmada ödünçFFh + 01h → AL = 00h, CF = 1; 7 − 9 → CF = 1
2PF (eşlik)durumsonucun düşük baytında çift sayıda 1 biti var96h = 1001 0110b → dört 1 → PF = 1
4AF (yardımcı elde)durumbit 3'ten bit 4'e elde ya da ödünç08h + 08h = 10h → AF = 1
6ZF (sıfır)durumsonucun bütün bitleri 0SUB AL, AL → ZF = 1
7SF (işaret)durumsonucun en anlamlı biti (MSB) 196h → SF = 1
8TF (tuzak)kontrolher komuttan sonra INT 1 (tek adım)hata ayıklayıcılar kullanır
9IF (kesme)kontrolINTR ucundan gelen kesmeler kabul edilirSTI → 1, CLI → 0
10DF (yön)kontroldizgi komutları SI ve DI'yi azaltırCLD → 0, STD → 1
11OF (taşma)durumiş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.

Tanım 6.1

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.

Tablo 6.2: Aynı bayt toplamasının işaretsiz ve işaretli yorumu. CF işaretsiz, OF işaretli sonucun yanlış çıktığını söyler.
AL + işlenenİşaretsiz yorumİşaretli yorumALCFOF
FFh + 01h255 + 1 = 256−1 + 1 = 000h10
64h + 32h100 + 50 = 150100 + 50 = 15096h01
C8h + 64h200 + 100 = 300−56 + 100 = 442Ch10
80h + 80h128 + 128 = 256−128 + (−128) = −25600h11
05h + 0Ah5 + 10 = 155 + 10 = 150Fh00

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

Kural (taşma bayrağı)

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.

Sık yapılan hata

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
Tablo 6.3: CF, OF ve diğer bayrakların izlemesi. Her satır komut yürütüldükten sonraki durumdur; başlangıçta bütün durum bayrakları 0'dır.
KomutMakine koduALCFOFSFZFAFPF
MOV AL, 9ChB0 9C9Ch000000
ADD AL, 70h04 700Ch100001
MOV AL, 50hB0 5050h100001
ADD AL, 50h04 50A0h011001
ADD AL, AL02 C040h110000
MOV AL, 0FFhB0 FFFFh110000
INC ALFE C000h100111
CMCF500h000111

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

A = 64h0716150403120100B = 32h00110010AL = 96h10010110dört tane 1 (çift) → PF = 14 + 2 nibble: elde yok → AF = 0
Şekil 6.2: 64h + 32h = 96h. Bitler sağdan sola toplanır; bayraklar sonucun bitlerinden ve eldelerden okunur.
  1. Sonuç. 0110 0100b + 0011 0010b = 1001 0110b = 96h. İşaretsiz yorumla 150 doğrudur, işaretli yorumla −106 yanlıştır.
  2. CF. En soldaki bitten dışarı elde çıkmadı; 150 ≤ 255. CF = 0.
  3. OF. Bit 7'ye giren elde 1, çıkan elde 0. İki pozitif sayının toplamı negatif göründü. OF = 1.
  4. SF ve ZF. Sonucun bit 7'si 1'dir, SF = 1. Sonuç sıfır değildir, ZF = 0.
  5. PF. 1001 0110b'de dört tane 1 vardır; dört çift sayıdır, PF = 1.
  6. 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.

Örnek 6.1

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.

hedef ← hedef ∘ kaynakADD AX, BXAX ← AL × işlenenMUL BLişlenen ← f(işlenen)INC CX
Şekil 6.3: Üç grubun işlenen biçimleri. 1. grupta sonuç hedefe yazılır; 2. grupta örtük AL, AX ve DX kullanılır; 3. grupta tek işlenen hem kaynak hem hedeftir.
  • 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 CX CX'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.

Tablo 6.4: Üç gruptaki komutların anlamı.
GrupKomutNe yapar?
1ADDiki değeri toplar, sonucu hedefe yazar
SUBkaynağı hedeften çıkarır, sonucu hedefe yazar
CMPkaynağı hedeften çıkarır, sonucu saklamaz; yalnız bayrakları ayarlar
ANDbit düzeyinde VE
TESTbit düzeyinde VE; sonucu saklamaz
ORbit düzeyinde VEYA
XORbit düzeyinde dışlayan VEYA
2MULişaretsiz çarpma
IMULişaretli çarpma
DIVişaretsiz bölme (bölüm ve kalan)
IDIVişaretli bölme (bölüm ve kalan)
3INC1 artırır
DEC1 azaltır
NOTbütün bitleri tersler (birler tümleyeni)
NEGiş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).

Tablo 6.5: ADD, SUB, CMP, AND, TEST, OR ve XOR'un geçerli işlenen birleşimleri.
BiçimÖrnekMakine kodu
REG, memoryADD AX, [BX]03 07
memory, REGSUB var1, CL28 0E + adres
REG, REGXOR AX, AX33 C0
memory, immediateAND BYTE PTR [SI], 0Fh80 24 0F
REG, immediateCMP 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.

Sık yapılan hata

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

Tablo 6.6: İki işlenenli komutların bayrak etkisi. ✓ sonuca göre ayarlanır, 0 her zaman temizlenir, ? tanımsızdır.
KomutCFZFSFOFPFAF
ADD, ADC, SUB, SBB, CMP✓✓✓✓✓✓
AND, OR, XOR, TEST0✓✓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).

Tablo 6.7: İki işlenenli komutların 8086'daki saat turu süreleri (Intel kılavuzu). Tek adresteki sözcük erişimine her aktarım için 4 tur eklenir.
BiçimADD, ADC, SUB, SBB, AND, OR, XORCMPTEST
reg, reg333
reg, mem9 + EA9 + EA9 + EA
mem, reg16 + EA9 + EA9 + EA
reg, imm445
mem, imm17 + EA10 + EA11 + EA
AL/AX, imm444

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
Tablo 6.8: ADD, SUB ve CMP izlemesi (başlangıçta bütün durum bayrakları 0).
KomutMakine koduALBLCFZFSFOFPFAF
mov al, 0F0hB0 F0F0h00h000000
add al, 20h04 2010h00h100000
mov bl, 7B3 0710h07h100000
sub bl, 980 EB 0910hFEh101001
cmp al, 10h3C 1010hFEh010010
cmp bl, 080 FB 0010hFEh001000
add bl, 280 C3 0210h00h110011

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.

Tablo 6.9: Üç CMP örneği. İşaretsiz sonuç CF'den, işaretli sonuç SF ile OF'nin karşılaştırmasından okunur.
KomutlarCFSFOFİşaretsizİşaretli
AL = 05h, BL = FEh; CMP AL, BL1005 < 2545 > −2 (SF = OF)
AL = 7Fh, BL = 80h; CMP AL, BL111127 < 128127 > −128 (SF = OF)
AL = 80h, BL = 01h; CMP AL, BL001128 > 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.

Tanım 6.2

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
Tablo 6.10: 0001FFFFh + 00020001h toplamasının izlemesi.
KomutMakine koduDXAXCXBXCFZF
mov ax, 0FFFFhB8 FF FF0000hFFFFh0000h0000h00
mov dx, 0001hBA 01 000001hFFFFh0000h0000h00
mov bx, 0001h · mov cx, 0002hBB 01 00 · B9 02 000001hFFFFh0002h0001h00
add ax, bx03 C30001h0000h0002h0001h11
adc dx, cx13 D10004h0000h0002h0001h00

İ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
Tablo 6.11: 00040000h − 00010001h çıkarmasının izlemesi.
KomutMakine koduDXAXCX:BXCFSF
mov ax, 0000h · mov dx, 0004hB8 00 00 · BA 04 000004h0000h0000:0000h00
mov bx, 0001h · mov cx, 0001hBB 01 00 · B9 01 000004h0000h0001:0001h00
sub ax, bx2B C30004hFFFFh0001:0001h11
sbb dx, cx1B D10002hFFFFh0001:0001h00

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.

Örnek 6.2

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

Sık yapılan hata

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

Tablo 6.12: AND, OR, XOR ve NOT doğruluk tabloları.
ABA AND BA OR BA XOR BNOT A
000001
010111
100110
111100

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.

AL='a' 61h0716150403020110maske DFh11011111ANDAL='A' 41h01000001AL='A' 41h0716050403020110maske 20h00100000ORAL='a' 61h01100001AL='a' 61h0716150403020110maske 20h00100000XORAL='A' 41h01000001BL='7' 37h0706151403121110maske 01h00000001TEST(saklanmaz)00000001
Şekil 6.4: Maskeleme. AND DFh bit 5'i temizler ('a' → 'A'); OR 20h bit 5'i kurar ('A' → 'a'); XOR 20h bit 5'i tersler; TEST 01h bit 0'ı sınar ve BL'yi değiştirmez.

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.

Tablo 6.13: Mantık komutlarıyla sık kullanılan kalıplar.
KodEtkiNeden?
XOR AX, AXAX = 0, ZF = 1, CF = 02 bayt (33 C0); MOV AX, 0 3 bayttır (B8 00 00) ve bayraklara dokunmaz
AND AL, 0Fh'7' (37h) → 7ASCII rakamı sayıya çevirir; yüksek yarım silinir
OR AL, 30h7 → '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, 1ZF = 0 ise tekAL değişmeden bit 0 sınanır
TEST AL, 80hZF = 0 ise negatifişaret biti sınanır
OR AX, AXAX değişmezZF 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
Tablo 6.14: Mantık komutlarının izlemesi. Mantık komutlarından sonra CF = OF = 0'dır; AF tanımsızdır (tabloda gösterilmedi).
KomutMakine koduALBLZFSFPFCFOF
mov al, 'a'B0 6161h00h00000
and al, 0DFh24 DF41h 'A'00h00100
or al, 20h0C 2061h 'a'00h00000
xor al, 20h34 2041h 'A'00h00100
mov bl, '7'B3 3741h37h00100
and bl, 0Fh80 E3 0F41h07h00000
test bl, 1F6 C3 0141h07h00000
xor bl, bl32 DB41h00h10100

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.

Sık yapılan hata

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.

bayt işlenen (8 bit)AL×işlenenAX (16 bit)MUL / IMULsözcük işlenen (16 bit)AX×işlenenDXAX32 bitlik sonuç DX:AX (DX yüksek)
Şekil 6.5: Bayt çarpmada AL × işlenen AX'e, sözcük çarpmada AX × işlenen DX:AX'e yazılır.
Tanım 6.3

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.

Tablo 6.15: Bayt çarpma örnekleri ve CF/OF.
AL, BLKomutHesapAXCF = OFNeden
C8h, 03hMUL BL200 × 3 = 6000258h1AH = 02h ≠ 0
C8h, 03hIMUL BL−56 × 3 = −168FF58h1−168 işaretli bayta sığmaz
FEh, 05hIMUL BL−2 × 5 = −10FFF6h0AH = FFh, AL'nin işaret uzantısı
FEh, 05hMUL BL254 × 5 = 127004F6h1AH = 04h ≠ 0
06h, 09hMUL BL6 × 9 = 540036h0AH = 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.

DXAXbölünen (32 bit)÷işlenenbölen (16 bit)AXbölümDXkalanbayt bölmede:AX ÷ işlenen → AL, AH
Şekil 6.6: Sözcük bölmede DX:AX bölünen, işlenen bölendir; bölüm AX'e, kalan DX'e yazılır.
Tablo 6.16: DIV ve IDIV'in örtük işlenenleri.
Komutİşlenen bayt iseİşlenen sözcük ise
DIV (işaretsiz)AL ← AX / işlenen, AH ← kalanAX ← DX:AX / işlenen, DX ← kalan
IDIV (işaretli)AL ← AX / işlenen, AH ← kalanAX ← 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.

Tanım 6.4

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.

Tablo 6.17: Bayt ve sözcük çarpma-bölme örnekleri.
BaşlangıçKomutSonuçAçıklama
AL = C8h, BL = 03hMUL BLAX = 0258h, CF = OF = 1200 × 3 = 600; AL'ye sığmadı
AL = C8h, BL = 03hIMUL BLAX = FF58h, CF = OF = 1−56 × 3 = −168; işaretli bayta sığmaz
AL = FEh, BL = 05hIMUL BLAX = FFF6h, CF = OF = 0−2 × 5 = −10; AH yalnız işaret uzantısı
AX = 0017h, BL = 05hDIV BLAL = 04h, AH = 03h (AX = 0304h)23 = 4 × 5 + 3
AX = FFE9h, BL = 05hIDIV BLAL = FCh, AH = FDh (AX = FDFCh)−23 = (−4) × 5 + (−3)
AX = 1000h, BL = 02hDIV BLINT 0bölüm 2048 AL'ye sığmaz
DX:AX = FFFFFFFFh, BX = 0001hDIV BXINT 0bö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, 0 ya da XOR 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.
Tanım 6.5

İş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.

AL11111001F9h = -7CBW: bit 7 AH'ye kopyalanırAX1111111111111001FFF9h = -7
Şekil 6.7: CBW: AL = F9h (−7) işaret biti 1 olduğu için AH = FFh olur; AX = FFF9h yine −7'dir.

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ık yapılan hata

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
Tablo 6.18: DIV, CWD ve IDIV izlemesi. Bölmeden sonra bayraklar tanımsızdır.
KomutMakine koduDXAXBXDX:AX (onluk)
MOV AX, 2000B8 D0 070000h07D0h0000h2000
XOR DX, DX33 D20000h07D0h0000h2000
MOV BX, 9BB 09 000000h07D0h0009h2000
DIV BXF7 F30002h00DEh0009hbölüm 222, kalan 2
MOV AX, -2000B8 30 F80002hF830h0009h0002F830h = 194 608 (yanlış bölünen)
CWD99FFFFhF830h0009hFFFFF830h = −2000
IDIV BXF7 FBFFFEhFF22h0009hbölüm −222, kalan −2
MOV AX, 2000B8 D0 07FFFEh07D0h0009hFFFE07D0h ≈ 4,29 milyar
DIV BXF7 F3bö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
Tablo 6.19: JO ve CBW ile işaretli taşmanın giderilmesi.
KomutAXDXCFOFSFAçıklama
mov al, -92 · mov bl, -9200A4h0000h000−92 = 256 − 92 = 164 = A4h
add al, bl0048h0000h110A4h + A4h = 148h; AL = 48h = +72
jo genislet0048h0000h110OF = 1: atlanır
mov al, -92 · cbwFFA4h0000h110AL'nin MSB'si 1 → AH = FFh
mov dx, ax · mov al, bl · cbwFFA4hFFA4h110iki sayı da sözcükte −92
add ax, dxFF48hFFA4h101FFA4h + 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.

Tanım 6.6

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
ret

Programı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.

Tablo 6.20: CBW ile işaretli bayt toplamı. Her tur için cbw ve add bx, ax sonrası durum.
TurALCBW sonrası AXBX (toplam)CX (tur başında)Açıklama
164h0064h0064h = 1004+100, MSB = 0 → AH = 00h
2E2hFFE2h0046h = 703−30, MSB = 1 → AH = FFh
35Ah005Ah00A0h = 1602+90
4F9hFFF9h0099h = 1531−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.

Sık yapılan hata

İş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
ret

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

Tablo 6.21: CWD ve ADC ile 32 bitlik toplamın izlemesi (her turda ADD ve ADC sonrası).
TurAXCWD: DXADD sonrası toplam, CFADC sonrası toplam+232 bitlik toplam
17530h (30 000)0000h7530h, CF = 00000h00007530h = 30 000
261A8h (25 000)0000hD6D8h, CF = 00000h0000D6D8h = 55 000
3F060h (−4000)FFFFhC738h, CF = 10000h0000C738h = 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.

z3 · bit 48–63z2 · bit 32–47z1 · bit 16–31z0 · bit 0–15A =9ABC (A1)DEF0 (A0)× B =EDCB (B1)FEDC (B0)A0 × B01. MULDDF1B640A1 × B02. MUL · 16 bit kayık9A0B8190A0 × B13. MUL · 16 bit kayıkCF14F850A1 × B14. MUL · 32 bit kayık8FBABF14+2 elde+2 eldesütun eldeleriA × B =8FBC283557D1B640
Şekil 6.8: A = 9ABCDEF0h ile B = EDCBFEDCh'nin çarpımı. Dört kısmi çarpım sütunlarına göre toplanır; sütun eldeleri bir üst sözcüğe geçer.

Ö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ç
Tablo 6.22: 32 × 32 bitlik çarpmanın izlemesi. z3…z0 sonucun dört sözcüğüdür (başlangıçta 0).
Komut(lar)DX:AXCFz3z2z1z0
mov ax,[x] · mul [y]DDF1:B640h10000h0000h0000h0000h
mov [z],ax · mov [z+2],dxDDF1:B640h10000h0000hDDF1hB640h
mov ax,[x+2] · mul [y]9A0B:8190h10000h0000hDDF1hB640h
add [z+2],ax9A0B:8190h10000h0000h5F81hB640h
adc [z+4],dx9A0B:8190h00000h9A0Ch5F81hB640h
mov ax,[x] · mul [y+2]CF14:F850h10000h9A0Ch5F81hB640h
add [z+2],axCF14:F850h10000h9A0Ch57D1hB640h
adc [z+4],dxCF14:F850h10000h6921h57D1hB640h
adc [z+6],0CF14:F850h00001h6921h57D1hB640h
mov ax,[x+2] · mul [y+2]8FBA:BF14h10001h6921h57D1hB640h
add [z+4],ax8FBA:BF14h10001h2835h57D1hB640h
adc [z+6],dx8FBA:BF14h08FBCh2835h57D1hB640h

İ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.
Tablo 6.23: Tek işlenenli komutların bayrak etkisi. ✓ sonuca göre ayarlanır, — değişmez.
KomutCFZFSFOFPFAF
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 = 05h0706050403120110NOT x11111010FAh+ 1 → NEG11111011FBhx = 80h1706050403020100NOT x011111117Fh+ 1 → NEG1000000080h
Şekil 6.9: NEG'in iki adımı: önce NOT (birler tümleyeni), sonra +1. Solda 05h → FAh → FBh (−5); sağda 80h → 7Fh → 80h: −128'in negatifi bayta sığmaz.
Tablo 6.24: Dört bayt değerinde NEG.
xNOT xNEG xCFOFAçıklama
05h (5)FAh (−6)FBh (−5)10pozitifin negatifi
FEh (−2)01h02h (2)10negatifin negatifi pozitiftir
80h (−128)7Fh80h11+128 bayta sığmaz
00hFFh00h00ZF = 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
Tablo 6.25: INC, DEC, NOT ve NEG izlemesi (başlangıçta bütün durum bayrakları 0).
KomutMakine koduALBLCFZFSFOFPFAF
mov al, 0FFhB0 FFFFh00h000000
inc alFE C000h00h010011
dec alFE C8FFh00h001011
mov bl, 7FhB3 7FFFh7Fh001011
inc blFE C3FFh80h001101
not blF6 D3FFh7Fh001101
neg blF6 DBFFh81h101011

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.

b7b6b5b4b3b2b1b0CF0b7b6b5b4b3b2b1b0CF0b7b6b5b4b3b2b1b0CFişaret biti korunur
Şekil 6.10: SHL/SAL (sağdan 0 girer, b7 CF'ye), SHR (soldan 0 girer, b0 CF'ye) ve SAR (işaret biti yerinde kalıp kendini kopyalar, b0 CF'ye).
Tanım 6.7

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.

b7b6b5b4b3b2b1b0CFb7b6b5b4b3b2b1b0CFb7b6b5b4b3b2b1b0CFb7b6b5b4b3b2b1b0CF
Şekil 6.11: ROL ve ROR 8 bitlik halkada döndürür ve çıkan bitin bir kopyasını CF'ye yazar. RCL ve RCR, CF'yi de halkaya katar: bayt için 9, sözcük için 17 bitlik halka.
  • 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.
Tablo 6.26: AL = 96h (1001 0110b) için yedi komutun tek adımlık sonucu. RCL ve RCR'de başlangıç CF'si sonucu değiştirir.
KomutCF = 0 ileCF = 1 ileSonuç CFOF
SHL AL, 12Ch (0010 1100)2Ch11
SHR AL, 14Bh (0100 1011)4Bh01
SAR AL, 1CBh (1100 1011)CBh00
ROL AL, 12Dh (0010 1101)2Dh11
ROR AL, 14Bh (0100 1011)4Bh01
RCL AL, 12Ch (0010 1100)2Dh (0010 1101)11
RCR AL, 14Bh (0100 1011)CBh (1100 1011)01 / 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 0

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

Tablo 6.27: 13 × 5'in kaydırarak hesaplanması. Her satır bir tur: SHR'nin çıkardığı bit, toplama ve SHL sonrası durum.
TurDL (SHR sonrası)CFToplamasonucBL (SHL sonrası)CX (LOOP sonrası)
102h10 + 0Dh0Dh (13)1Ah (26)3
201h0atlanır0Dh34h (52)2
300h10Dh + 34h41h (65)68h (104)1
400h0atlanır41hD0h0

Ç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 → −2

AX = 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
Tablo 6.28: 37 × 10 ve 370 ÷ 16 izlemesi.
KomutMakine koduAXAX (ikili)BXCL
mov ax, 37B8 25 000025h = 370000 0000 0010 01010000h00h
shl ax, 1D1 E0004Ah = 740000 0000 0100 10100000h00h
mov bx, ax8B D8004Ah0000 0000 0100 1010004Ah = 7400h
shl ax, 1D1 E00094h = 1480000 0000 1001 0100004Ah00h
shl ax, 1D1 E00128h = 2960000 0001 0010 1000004Ah00h
add ax, bx03 C30172h = 3700000 0001 0111 0010004Ah00h
mov bx, ax8B D80172h0000 0001 0111 00100172h00h
and bx, 000Fh83 E3 0F0172h0000 0001 0111 00100002h00h
mov cl, 4B1 040172h0000 0001 0111 00100002h04h
shr ax, clD3 E80017h = 230000 0000 0001 01110002h04h

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
ret

Programın yapısı satır satır şöyledir.

  • JMP start veri 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], AL sonucu üçü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 sum dö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

Tablo 6.29: Dizi toplama programının izlemesi.
AdımSIBXDICXALvec3Açıklama
jmp start0000h0000h0000h0000h00h0 0 0 0veriler (0102h–010Dh) atlanır
lea si / bx / di0102h0106h010Ah0000h00h0 0 0 0üç dizinin adresi
mov cx, 40102h0106h010Ah0004h00h0 0 0 04 tur
tur 1: mov al,[si] · add al,[bx]0102h0106h010Ah0004h04h0 0 0 01 + 3 = 4
tur 1: mov [di],al0102h0106h010Ah0004h04h4 0 0 0vec3[0] = 4
tur 1: üç inc · loop0103h0107h010Bh0003h04h4 0 0 0CX = 3 ≠ 0 → sum'a dön
tur 20104h0108h010Ch0002h07h4 7 0 02 + 5 = 7
tur 30105h0109h010Dh0001h0Bh4 7 11 05 + 6 = 11
tur 40106h010Ah010Eh0000h07h4 7 11 76 + 1 = 7; CX = 0, döngü biter
vec1 (0102h)1256vec2 (0106h)3561▲SIvec3 (010Ah)47117▲BX
Şekil 6.12: Program bittiğinde bellek: vec3 = 4, 7, 11, 7 (04h 07h 0Bh 07h). Son turdan sonra SI vec2'nin, BX vec3'ün başını gösterir.

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.

Tablo 6.30: Dizi toplama programının bellekteki baytları (emu8086).
AdresBaytlarAnlamı
0100hEB 0CJMP 010Eh; hedef = 0102h + 0Ch
0102h01 02 05 06vec1 (veri)
0106h03 05 06 01vec2 (veri)
010Ah04 07 0B 07vec3 (program çalıştıktan sonra)
010EhBE 02 01MOV SI, 0102h (LEA'nın kodu)
0111hBB 06 01MOV BX, 0106h
0114hBF 0A 01MOV DI, 010Ah
0117hB9 04 00MOV CX, 0004h; sabit little-endian
011Ah8A 04MOV AL, [SI] (sum:)
011Ch02 07ADD AL, [BX]
011Eh88 05MOV [DI], AL
0120h46 43 47INC SI, INC BX, INC DI (birer bayt)
0123hE2 F5LOOP 011Ah; F5h = −11
0125hC3RET

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.

Tanım 6.8

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

Tablo 6.31: 14 sayısının dört gösterimi.
GösterimBitlerBayt(lar)
İkili0000 11100Eh
Paketsiz BCD0000 0001 · 0000 010001h 04h (AH, AL)
Paketli BCD0001 010014h
ASCII0011 0001 · 0011 010031h 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ı

Tablo 6.32: BCD düzeltme komutları. Hepsi tek baytlık (AAM ve AAD iki baytlık) örtük işlenenli komutlardır.
KomutNe zaman?EtkiSüre (tur)
AAApaketsiz toplamadan sonraAL'yi BCD'ye düzeltir, elde AH'ye4
AASpaketsiz çıkarmadan sonraAL'yi düzeltir, ödünç AH'den4
AAMpaketsiz çarpmadan sonraAH ← AL / 10, AL ← AL mod 1083
AADpaketsiz bölmeden önceAL ← AH × 10 + AL, AH ← 060
DAApaketli toplamadan sonraAL'deki iki basamağı düzeltir4
DASpaketli çıkarmadan sonraAL'deki iki basamağı düzeltir4

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.

Kural (AAA ve DAA)

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
Tablo 6.33: AAA ile 9 + 5 izlemesi. Ekranda “14” görünür.
KomutMakine koduAXBLCFAFAçıklama
mov ah, 09h · mov al, 05hB4 09 · B0 050905h00h00iki basamak
add al, ah02 C4090Eh00h009 + 5 = 0Eh; ikili doğru, BCD değil
xor ah, ah32 E4000Eh00h0?onlar basamağı temizlendi
aaa370104h00h11E > 9: AL + 6 = 14h, AH + 1, yüksek yarım silinir
or ax, 3030h0D 30 303134h00h0?'1', '4'
mov bl, al · mov al, ah8A D8 · 8A C43131h34h0?'4' saklandı, AL = '1'
mov ah, 0Eh · int 10hB4 0E · CD 100E31h34h0?ekrana '1'
mov al, bl · int 10h8A C3 · CD 100E34h34h0?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
Tablo 6.34: AAS ile 5 − 8 izlemesi. Program AX = 0000h ile başlar.
KomutAXBLCFAFSFAçıklama
mov al, 05h · mov bl, 08h0005h08h000
sub al, bl00FDh08h1115 − 8 = −3 = FDh
aasFF07h08h11?AL − 6 = F7h, AH − 1 = FFh; yüksek yarım silinir
or al, 30hFF37h08h0?0AL = '7'
mov ah, 0Eh · int 10h0E37h08h0?0ekrana '7'
mov ah, 0 · int 16h—08h0?0tuş 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.

Tanım 6.9

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
Tablo 6.35: AAM, AAD ve DAA izlemesi. ? tanımsız bayrak.
KomutMakine koduAXBLAFCFAçıklama
MOV AL, 6 · MOV BL, 9B0 06 · B3 090006h09h00
MUL BLF6 E30036h09h?06 × 9 = 54; AL'ye sığdı
AAMD4 0A0504h09h??paketsiz “54”
MOV AX, 0702hB8 02 070702h09h??paketsiz “72”
AADD5 0A0048h09h??7 × 10 + 2 = 72 = 48h
MOV BL, 8 · DIV BLB3 08 · F6 F30009h08h??72 / 8 = 9, kalan 0
MOV AL, 19hB0 190019h08h??paketli “19”
ADD AL, 28h04 280041h08h109 + 8 = 17: yarımdan elde
DAA270047h08h10AF = 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.

Sık yapılan hata

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
Tablo 6.36: 15 °C'nin Fahrenhayta çevrilmesi.
KomutMakine koduAXCLCFOFAçıklama
mov cl, 15B1 0F0000h0Fh00c = 15
mov al, 9B0 090009h0Fh00
imul clF6 E90087h0Fh119 × 15 = 135; işaretli bayta sığmaz, AX'te eksiksiz
mov cl, 5B1 050087h05h11bölen
idiv clF6 F9001Bh05h??135 / 5 = 27, kalan 0
add al, 3204 20003Bh05h0027 + 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
Tablo 6.37: 59 °F'nin Santigrata çevrilmesi.
KomutMakine koduAXCLCFOFAçıklama
mov cl, 59B1 3B0000h3Bh00f = 59
sub cl, 3280 E9 200000h1Bh0059 − 32 = 27
mov al, 5B0 050005h1Bh00
imul clF6 E90087h1Bh115 × 27 = 135
mov cl, 9 · idiv clB1 09 · F6 F9000Fh09h??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.

Tablo 6.38: °C → °F programının sınır değerleri.
cIMUL: AXIDIV: ALADD sonrası ALOFSonuç
−72FD78h (−648)bölüm −129 sığmaz → INT 0bölme hatası
−71FD81h (−639)81h (−127)A1h (−95)0doğru
−10FFA6h (−90)EEh (−18)0Eh (14)0doğru
5301DDh (477)5Fh (95)7Fh (127)0doğru (en büyük)
5401E6h (486)61h (97)81h1yanlış: −127 okunur
720288h (648)bölüm 129 sığmaz → INT 0bö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.

Tablo 6.39: Komutların durum bayraklarına etkisi. ✓ sonuca göre, 0 temizlenir, — değişmez, ? tanımsız, ¹ yalnız sayı 1 iken tanımlı.
KomutCFZFSFOFPFAF
ADD SUB CMP ADC SBB✓✓✓✓✓✓
NEG✓✓✓✓✓✓
INC DEC—✓✓✓✓✓
AND OR XOR TEST0✓✓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

Tablo 6.40: Aritmetik ve mantık komutlarında sı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ırişaretli: OF; karşılaştırmada SF ≠ OF
DIV BX öncesi DX'i unutmakeski DX bölüneni büyütür; yanlış sonuç ya da INT 0XOR DX, DX ya da CWD
İşaretli bayt için MOV AH, 0−30 = E2h, 226 sayılırCBW
MUL 108086'da sabit işlenen yok; assembler reddederMOV BL, 10 · MUL BL
SHL AX, 480186 komutu; 8086'da çalışmazMOV CL, 4 · SHL AX, CL
İki sözcükte de ADDdüşük sözcüğün eldesi kaybolurADD, ardından ADC
ADD ile ADC arasına CMP koymakCF silinirarada yalnız MOV, INC, DEC, LOOP
Bit sınamak için ANDsayının öteki bitleri silinirTEST
Negatif sayıyı SAR ile bölüp IDIV sonucu beklemek−7 / 2: SAR −4, IDIV −3yuvarlama yönünü bilerek seçmek
AAD'yi DIV'den sonra yazmakpaketsiz sayı ikili sanılır: 0702h = 1794 bölünürAAD bölmeden önce
Sık yapılan hata

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 sum

Bu 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 tekrar

B5h = 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.

6.16Kendini Dene

  1. MOV AL, 9Ch ve ADD AL, 9Ch sonrası 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.

  2. SUB AL, BL komutu 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.

  3. AL = F0h, BL = 10h iken IMUL BL ve MUL BL sonrası 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.

  4. −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.

  5. CF = 1 ve AL = 81h iken art arda üç RCL AL, 1 komutundan 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.

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

  7. AL = B6h iken AND AL, 0F0h, OR AL, 05h, XOR AL, 0FFh sonrası 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.

  8. 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 AX kullanılsaydı CF değişmeyecek ve elde kaybolacaktı.

  9. MOV AX, 8000h ve NEG AX sonrası 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.

  10. INC AX ile ADD AX, 1 arasında makine kodu, süre ve bayrak bakımından ne fark vardır?

    Cevap

    INC AX tek 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

  1. Intel, The 8086 Family User's Manual, 1979 — Instruction Set bölümü.
  2. B. B. Brey, The Intel Microprocessors, 8. baskı, Pearson, 2009 — Bölüm 5.
  3. emu8086 yardım belgeleri: “8086 instructions” ve “Arithmetic and logic instructions”.
  4. K. R. Irvine, Assembly Language for x86 Processors, 7. baskı, Pearson, 2014 — Bölüm 7.
  5. A. S. Tanenbaum, Modern Operating Systems, 3. baskı, Pearson, 2008.
  6. M. Bodur, Mikroişlemcilere Giriş: Assembler ile Yazılım ve Arayüz, EMO, 2014.
  7. Ş. Ç. Ö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.