Bölüm 8.1: Yığın

Mikroişlemciler
Yığın ve LIFOSS:SPPUSH / POPBayt sırasıYazmaç saklamaTakasPUSHF / POPFYığın güvenliği
Sercan KÜLCÜ · Giresun Üniversitesi · Elektrik-Elektronik Mühendisliği

Giriş

Öğrenme Çıktıları

Yığın modeli

8086 yığınının SS:SP ile nasıl adreslendiğini ve aşağı doğru büyüdüğünü açıklamak.

PUSH / POP

Geçerli işlenen türlerini bilmek ve her komutta SP'nin nasıl değiştiğini adım adım izlemek.

Yazmaç saklama

Yığını yazmaç değerlerini korumak, geri yüklemek ve takas etmek için doğru sırayla kullanmak.

PUSHF / POPF

Bayrak yazmacını yığında saklamak ve bir işlemin bozduğu bayrakları geri yüklemek.

Yığın güvenliği

Dengesiz PUSH/POP, taşma ve yanlış sıralama hatalarını tanımak ve önlemek.

emu8086 izleme

Programları tek adım çalıştırırken yazmaç ve yığın pencerelerini yorumlamak.

Yığın Kavramı

Yığın (Stack)

  • Yığın, geçici verileri saklamak için ayrılmış bir bellek alanıdır.
  • CALL komutu prosedürün dönüş adresini yığına koyar; RET bu değeri yığından alır ve o adrese döner.
  • INT bir kesmeyi çağırdığında benzer bir işlem olur: bayrak (durum) yazmacı, kod kesimi (CS) ve bağıl konum (IP) yığına saklanır; IRET ile kesmeden dönülür.
  • PUSH: 16 bitlik bir değeri yığına koyar.
  • POP: 16 bitlik bir değeri yığından alır.
Kim kullanır?Programcı (PUSH/POP), CPU (CALL, INT), işletim sistemi ve BIOS kesme yordamları.
Birim8086'da yığına her zaman sözcük (2 bayt) yazılır ve okunur; PUSH AL geçersizdir.
YönYığın yüksek adresten düşük adrese doğru, yani aşağı doğru büyür.

Yığın Kavramı

LIFO Algoritması

  • LIFO, Last In First Out (son giren ilk çıkar) ifadesinin kısaltmasıdır.
  • Yığına eklenen son öğe, yığından çıkan ilk öğe olur.
  • Yığına konan değerler sırasıyla 1, 2, 3, 4, 5 ise ilk alınan değer 5 olur; ardından 4, 3, 2 ve en son 1 alınır.
Sonuç: yığın, değerleri ters sırada geri verir. Yazmaç geri yüklerken POP sırası PUSH sırasının tersi olmalıdır.
yığınPUSHPOP

Başlangıçta yığın boş.

1yığınPUSHPOP

PUSH 1: 1 yığının üstüne konur.

12yığınPUSHPOP

PUSH 2: 2 yığının üstüne konur.

123yığınPUSHPOP

PUSH 3: 3 yığının üstüne konur.

1234yığınPUSHPOP

PUSH 4: 4 yığının üstüne konur.

12345yığınPUSHPOP

PUSH 5: 5 yığının üstüne konur.

1234yığınPUSHPOP5çıkan

POP → 5 çıkar. Çıkış sırası şimdiye kadar: 5

123yığınPUSHPOP4çıkan

POP → 4 çıkar. Çıkış sırası şimdiye kadar: 5, 4

12yığınPUSHPOP3çıkan

POP → 3 çıkar. Çıkış sırası şimdiye kadar: 5, 4, 3

1yığınPUSHPOP2çıkan

POP → 2 çıkar. Çıkış sırası şimdiye kadar: 5, 4, 3, 2

yığınPUSHPOP1çıkan

POP → 1 çıkar. Çıkış sırası şimdiye kadar: 5, 4, 3, 2, 1

Yığın Kavramı

Yığın Belleği: SS ve SP

  • Yığın belleği SS (Stack Segment, yığın kesimi) ve SP (Stack Pointer, yığın işaretçisi) yazmaçlarını kullanır.
  • SS:SP her zaman yığının üstündeki (en son konan) sözcüğü gösterir.
  • Başlangıç değerlerini genellikle işletim sistemi (yükleyici) belirler: .COM'da SS = CS, SP = FFFEh; .EXE'de başlıktaki değerler.
  • BP de varsayılan olarak SS kesimini kullanır; yığındaki verilere erişmek için idealdir.
SS0700h

Yığın kesimi SS = 0700h (emu8086'da .COM programı için tüm kesim yazmaçları 0700h).

SS0700hSS × 1607000h

Kesim değeri 16 ile çarpılır (onaltılıkta sağa bir 0 eklenir): 07000h.

SS0700hSS × 1607000h+ SPFFFEh

Bağıl konum olarak yığın işaretçisi SP = FFFEh eklenir.

SS0700hSS × 1607000h+ SPFFFEhFiziksel adres16FFEh

Yığının üstü fiziksel olarak 16FFEh adresindedir (20 bit).

Yığın Kavramı

Yığın Aşağı Doğru Büyür

  • .COM programında kod, veri ve yığın aynı 64 KB kesimi paylaşır.
  • Program 0100h'den yukarı doğru, yığın FFFEh'den aşağı doğru yerleşir; aradaki boş alan ikisine de yeter.
  • Her PUSH SP'yi 2 azaltır, her POP 2 artırır.
  • Çok fazla PUSH yapılırsa yığın kodun/verinin üzerine yazabilir: yığın taşması (stack overflow).
PSP (0000–00FFh)Kod + veriORG 100h’den başlarprogram büyürYığınSP = FFFEh’den başlaryığın büyür (SP azalır)boş alanFFFFh0000h0100hCS = DS = SS = ES= 0700h(tek 64 KB kesim)

PUSH ve POP

PUSH Komutu

BiçimGeçerli işlenenlerÖrnek
PUSH REGAX, BX, CX, DX, DI, SI, BP, SPPUSH AX
PUSH SREGDS, ES, SS, CSPUSH DS
PUSH memory[BX], [BX+SI+7], 16 bitlik değişken…PUSH [BX], PUSH sayi
PUSH immediate5, −24, 3Fh, 10001101b…PUSH 3Fh
PUSH immediate komutu 8086/8088'de yoktur; 80186 ile gelmiştir. emu8086 bunu kabul eder ama gerçek 8086 için MOV AX, 5 + PUSH AX yazılmalıdır.
Yalnızca 16 bitlik işlenenler kullanılabilir: PUSH AL, PUSH byte_degisken hatalıdır. Bayraklar için ayrı komut vardır: PUSHF.

Ayrıntı: 8086'da PUSH SP, SP azaltıldıktan sonraki değeri yığına yazar; 80286 ve sonrası azaltmadan önceki değeri yazar.

PUSH ve POP

POP Komutu

BiçimGeçerli işlenenlerÖrnek
POP REGAX, BX, CX, DX, DI, SI, BP, SPPOP BX
POP SREGDS, ES, SS (CS hariç)POP DS
POP memory[BX], [BX+SI+7], 16 bitlik değişken…POP sonuc
Neden CS yok?CS'yi tek başına değiştirmek, IP aynı kalırken programın akışını rastgele bir yere atar. CS yalnızca RETF, IRET, uzak JMP/CALL ile birlikte değişir.
Sabit yokPOP 5 anlamsızdır: alınan değer bir yere yazılmalıdır.
BayraklarPUSH/POP bayrakları değiştirmez; yalnızca POPF bayrak yazmacının tamamını yığından yükler.

Yaygın deyim: PUSH CS + POP DS → DS'ye CS'nin değeri kopyalanır (kesim yazmaçları arasında doğrudan MOV yoktur).

PUSH ve POP

PUSH ve POP Nasıl Çalışır?

PUSH kaynak
  1. SP yazmacından 2 çıkarılır.
  2. Kaynak değeri SS:SP adresine yazılır.
POP hedef
  1. SS:SP adresindeki değer hedefe yazılır.
  2. SP yazmacına 2 eklenir.

Sıra önemlidir: PUSH önce azaltır sonra yazar; POP önce okur sonra artırır. Böylece SP her an dolu olan en üst hücreyi gösterir.

····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

Başlangıç: SP = FFFEh, AX = 1234h. (.COM'da FFFEh'de DOS'un koyduğu 0000h vardır.)

····FFF8····FFFA····FFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıdaPUSH AX · 1

PUSH AX ① SP ← SP − 2 = FFFCh. Yeni üst hücre henüz yazılmadı.

····FFF8····FFFA1234hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıdaPUSH AX · 2

② Kaynak değer SS:SP adresine yazılır: [FFFCh] ← 1234h.

····FFF8····FFFA1234hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıdaPOP BX · 1

POP BX ① SS:SP'deki değer hedefe yazılır: BX ← 1234h.

····FFF8····FFFA1234hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıdaPOP BX · 2

② SP ← SP + 2 = FFFEh. FFFCh'deki 1234h silinmez; yalnızca artık yığının dışında kalır (soluk).

PUSH ve POP

Yığında Bayt Sırası (Little-Endian)

Sözcük görünümü1234hSS:FFFCBayt görünümü (bellek)34hFFFC12hFFFD00hFFFE00hFFFF← düşük bayt (AL)← yüksek bayt (AH)DOS’un 0000h sözcüğü
  • 8086 bir sözcüğü belleğe düşük bayt önce yazar: PUSH AX (AX = 1234h) sonrası [FFFCh] = 34h, [FFFDh] = 12h.
  • emu8086'nın stack penceresi sözcükleri gösterir; memory penceresi baytları gösterir. İkisi aynı veriyi farklı biçimde sunar.
  • SP her zaman çift bir değer olarak tutulmalıdır; tek adreste sözcük erişimi 8086'da ek bir bellek döngüsü harcar.

PUSH ve POP

Ne Değişir, Ne Değişmez?

KomutSPBellekHedefBayraklar
PUSH kaynak−2[SS:SP] ← kaynak—değişmez
POP hedef+2okunur, silinmezhedef ← [SS:SP]değişmez
PUSHF−2[SS:SP] ← FLAGS—değişmez
POPF+2okunurFLAGS ← [SS:SP]hepsi yüklenir
CALL (yakın)−2[SS:SP] ← dönüş IPIP ← hedefdeğişmez
RET (yakın)+2okunurIP ← [SS:SP]değişmez
INT n−6FLAGS, CS, IPCS:IP ← vektörIF = TF = 0
IRET+6okunurIP, CS, FLAGShepsi yüklenir

Bu yüzden bir karşılaştırmanın sonucu (bayraklar) PUSH/POP arasında güvenle taşınabilir; bayrağı saklamak gerekiyorsa PUSHF/POPF kullanılır.

Örnekler · Kodu adım adım çalıştır

İlk Örnek: PUSH AX / POP BX

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

emu8086 .COM başlangıcı: CS = DS = SS = ES = 0700h, IP = 0100h, SP = FFFEh.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

MOV AX, 42: 42 onluk sayıdır → AX = 002Ah (42h değil!).

····FFF6····FFF8····FFFA002AhFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSH AX: SP = FFFEh − 2 = FFFCh; [FFFCh] ← 002Ah.

····FFF6····FFF8····FFFA002AhFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

POP BX: BX ← [FFFCh] = 002Ah; SP tekrar FFFEh. Değer AX'ten BX'e yığın üzerinden kopyalandı.

····FFF6····FFF8····FFFA002AhFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

RET: yığındaki 0000h alınır, IP = 0000h → PSP'deki INT 20h programı sonlandırır.

ORG    100h
MOV    AX, 42     ; AX'e 42 ata
PUSH   AX         ; AX'i yığına koy
POP    BX         ; yığından BX'e al
RET               ; işletim sistemine dön
END

Örnekler · Kodu adım adım çalıştır

LIFO Sırası: 1, 2, 3 Girer → 3, 2, 1 Çıkar

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

Başlangıç: SP = FFFEh.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

AX = 0001h.

····FFF6····FFF8····FFFA0001hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSH: SP = FFFCh, [FFFCh] = 0001h.

····FFF6····FFF8····FFFA0001hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

AX = 0002h (AX'in eski değeri yığında güvende).

····FFF6····FFF80002hFFFASP0001hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSH: SP = FFFAh, [FFFAh] = 0002h.

····FFF6····FFF80002hFFFASP0001hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

AX = 0003h.

····FFF60003hFFF8SP0002hFFFA0001hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSH: SP = FFF8h; yığında üç değer var.

····FFF60003hFFF80002hFFFASP0001hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

POP BX → 3 (son giren ilk çıkar). SP = FFFAh.

····FFF60003hFFF80002hFFFA0001hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

POP BX → 2. SP = FFFCh.

····FFF60003hFFF80002hFFFA0001hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

POP BX → 1. SP = FFFEh; yığın başlangıç hâline döndü.

MOV    AX, 1      ; AX'e 1 ata
PUSH   AX         ; 1'i yığına koy
MOV    AX, 2      ; AX'e 2 ata
PUSH   AX         ; 2'yi yığına koy
MOV    AX, 3      ; AX'e 3 ata
PUSH   AX         ; 3'ü yığına koy
POP    BX         ; BX ← 3
POP    BX         ; BX ← 2
POP    BX         ; BX ← 1

Örnekler · Kodu adım adım çalıştır

Bir Yazmacın Değerini Saklamak

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

Amaç: AX'i geçici olarak başka bir iş için kullanıp sonra eski değerine döndürmek.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

AX = 1234h.

····FFF6····FFF8····FFFA1234hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

AX yığına saklandı: [FFFCh] = 1234h.

····FFF6····FFF8····FFFA1234hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

AX başka bir iş için kullanıldı: AX = 5678h.

····FFF6····FFF8····FFFA1234hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

POP AX: AX eski değerine döndü → 1234h.

····FFF6····FFF8····FFFA1234hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

RET: 0000h'ye dönüş, program biter.

ORG    100h
MOV    AX, 1234h
PUSH   AX         ; AX'in değerini sakla
MOV    AX, 5678h  ; AX'i değiştir
POP    AX         ; eski değeri geri yükle
RET
END

Prosedürlerin girişinde kullandıkları yazmaçları PUSH, çıkışta POP etmesi bu desenin aynısıdır.

Örnekler · Kodu adım adım çalıştır

Yığın ile İki Yazmacı Takas Etmek

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

emu8086'da yükleme sonrası: CX = 000Bh (programın bayt sayısı: 3+3+1+1+1+1+1 = 11), SP = FFFEh.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

AX = 1212h.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

BX = 3434h.

····FFF6····FFF8····FFFA1212hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSH AX: [FFFCh] = 1212h, SP = FFFCh.

····FFF6····FFF83434hFFFASP1212hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSH BX: [FFFAh] = 3434h, SP = FFFAh.

····FFF6····FFF83434hFFFA1212hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

POP AX: üstteki 3434h AX'e gelir.

····FFF6····FFF83434hFFFA1212hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

POP BX: sıradaki 1212h BX'e gelir. Takas tamam!

····FFF6····FFF83434hFFFA1212hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

RET: program sonu.

ORG    100h
MOV    AX, 1212h  ; AX ← 1212h
MOV    BX, 3434h  ; BX ← 3434h
PUSH   AX         ; AX'i yığına koy
PUSH   BX         ; BX'i yığına koy
POP    AX         ; AX ← BX'in eski değeri
POP    BX         ; BX ← AX'in eski değeri
RET
END

Aynı sırayla POP edilseydi (önce BX) değerler yerinde kalırdı. Takasın sırrı LIFO'dur.

Örnekler · Kodu adım adım çalıştır

Yığın Kullanımı: Üç Yazmaç

····FFF6····FFF8····FFFA····FFFC····FFFESPSS:bağıl konum · küçük adres yukarıda

.BIN olarak yüklendi: CS = SS = 0100h, IP = 0000h, SP = FFFEh. Yığın başlangıçta boş.

····FFF6····FFF8····FFFA····FFFC····FFFESPSS:bağıl konum · küçük adres yukarıda

AX = 0002h.

····FFF6····FFF8····FFFA····FFFC····FFFESPSS:bağıl konum · küçük adres yukarıda

BX = 0004h.

····FFF6····FFF8····FFFA····FFFC····FFFESPSS:bağıl konum · küçük adres yukarıda

CX = 0008h.

····FFF6····FFF8····FFFA0002hFFFCSP····FFFESS:bağıl konum · küçük adres yukarıda

push ax: SP = FFFCh, [FFFCh] = 0002h.

····FFF6····FFF80004hFFFASP0002hFFFC····FFFESS:bağıl konum · küçük adres yukarıda

push bx: SP = FFFAh, [FFFAh] = 0004h.

····FFF60008hFFF8SP0004hFFFA0002hFFFC····FFFESS:bağıl konum · küçük adres yukarıda

push cx: SP = FFF8h, [FFF8h] = 0008h.

····FFF60008hFFF80004hFFFASP0002hFFFC····FFFESS:bağıl konum · küçük adres yukarıda

pop ax: AX ← 0008h (üstteki).

····FFF60008hFFF80004hFFFA0002hFFFCSP····FFFESS:bağıl konum · küçük adres yukarıda

pop bx: BX ← 0004h (ortadaki; değişmedi).

····FFF60008hFFF80004hFFFA0002hFFFC····FFFESPSS:bağıl konum · küçük adres yukarıda

pop cx: CX ← 0002h. Sonuç: AX ile CX'in değerleri yer değiştirdi.

mov ax, 2
mov bx, 4
mov cx, 8
push ax
push bx
push cx
pop  ax
pop  bx
pop  cx

Kaynak: emu8086 ile tek adım çalıştırma (single step). Yığın penceresi SS:SP çevresini gösterir.

Örnekler · Kodu adım adım çalıştır

Yığınla Diziyi Ters Çevirme

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

metin = 'ABC' bellekte 0117h adresinde. Hedef: ekrana ters sırada yazmak.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

SI = 0117h (metin dizisinin adresi).

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

Döngü sayacı CX = 3.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

AL ← [SI] = 'A' (41h).

····FFF6····FFF8····FFFA0041hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSH AX: 'A' yığına (AH = 00h ile birlikte, 0041h).

····FFF6····FFF8····FFFA0041hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

SI = 0118h: sonraki karakter.

····FFF6····FFF8····FFFA0041hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

LOOP: CX = 2 → geri dön.

····FFF6····FFF8····FFFA0041hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

AL ← [SI] = 'B' (42h).

····FFF6····FFF80042hFFFASP0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSH AX: 'B' yığına (AH = 00h ile birlikte, 0042h).

····FFF6····FFF80042hFFFASP0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

SI = 0119h: sonraki karakter.

····FFF6····FFF80042hFFFASP0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

LOOP: CX = 1 → geri dön.

····FFF6····FFF80042hFFFASP0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

AL ← [SI] = 'C' (43h).

····FFF60043hFFF8SP0042hFFFA0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSH AX: 'C' yığına (AH = 00h ile birlikte, 0043h).

····FFF60043hFFF8SP0042hFFFA0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

SI = 011Ah: sonraki karakter.

····FFF60043hFFF8SP0042hFFFA0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

LOOP: CX = 0 → döngü biter.

····FFF60043hFFF8SP0042hFFFA0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

İkinci döngü için CX = 3.

····FFF60043hFFF80042hFFFASP0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

POP DX: DX = 0043h → DL = 'C' (son giren ilk çıkar).

····FFF60043hFFF80042hFFFASP0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

AH = 02h: DOS karakter yazma işlevi.

····FFF60043hFFF80042hFFFASP0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

INT 21h: ekrana C yazılır; AL = DL = 43h olur.

····FFF60043hFFF80042hFFFASP0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

LOOP: CX = 2 → geri dön.

····FFF60043hFFF80042hFFFA0041hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

POP DX: DX = 0042h → DL = 'B' (son giren ilk çıkar).

····FFF60043hFFF80042hFFFA0041hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

AH = 02h: DOS karakter yazma işlevi.

····FFF60043hFFF80042hFFFA0041hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

INT 21h: ekrana B yazılır; AL = DL = 42h olur.

····FFF60043hFFF80042hFFFA0041hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

LOOP: CX = 1 → geri dön.

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

POP DX: DX = 0041h → DL = 'A' (son giren ilk çıkar).

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

AH = 02h: DOS karakter yazma işlevi.

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

INT 21h: ekrana A yazılır; AL = DL = 41h olur.

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

LOOP: CX = 0 → döngü biter.

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

Ekranda CBA: dizi yığın sayesinde ters çevrildi. RET ile program biter.

ORG  100h
MOV  SI, OFFSET metin
MOV  CX, 3
koy: MOV  AL, [SI]
     PUSH AX       ; karakteri sakla
     INC  SI
     LOOP koy
MOV  CX, 3
yaz: POP  DX       ; DL = karakter
     MOV  AH, 2
     INT  21h      ; DL'yi ekrana yaz
     LOOP yaz
RET
metin DB 'ABC'   ; 0117h

Örnekler

Yazmaçlar Ne Olur?

Kod çalıştıktan sonra AX, BX, CX ve SP ne olur? (Başlangıç: SP = FFFEh)

MOV AX, 1111h
MOV BX, 2222h
MOV CX, 3333h
PUSH AX
PUSH BX
PUSH CX
POP AX
POP CX
PUSH AX
POP BX
POP BX

PUSH'lardan sonra yığın (üstten alta): 3333h, 2222h, 1111h; SP = FFF8h.

POP AX → AX = 3333h; POP CX → CX = 2222h; SP = FFFCh.

PUSH AX (3333h), POP BX → BX = 3333h; ardından POP BX → BX = 1111h.

Sonuç: AX = 3333h, BX = 1111h, CX = 2222h, SP = FFFEh (4 PUSH, 4 POP → dengeli).

Örnekler

Takas Yöntemlerinin Karşılaştırması

Yığın ile
PUSH AX
PUSH BX
POP  AX
POP  BX
4 bayt · 4 bellek erişimi. Her yazmaç çifti ve kesim yazmaçları için çalışır.
Geçici yazmaç ile
MOV CX, AX
MOV AX, BX
MOV BX, CX
6 bayt · bellek erişimi yok, ama CX'in değeri bozulur.
XCHG ile
XCHG AX, BX
1 bayt (93h) · en kısa ve en hızlı yol. Kesim yazmaçlarıyla kullanılamaz.
yığın4 baytgeçici yazmaç6 baytXCHG1 bayt
Yığın yönteminin asıl değeri takas değil, saklamadır: yazmaçları prosedür ya da kesme süresince korumak, ara sonuçları geçici olarak tutmak, kesim yazmaçlarını kopyalamak (PUSH DS / POP ES).

PUSH ve POP · Kodu adım adım çalıştır

Bayrakları Saklamak: PUSHF / POPF

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

Başlangıç: bayraklar temiz (IF = 1).

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

AX = FFFFh.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

FFFFh + 1 = 1 0000h → AX = 0000h, CF = 1, ZF = 1.

····FFF6····FFF8····FFFAF257hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

PUSHF: FLAGS sözcüğü (F257h: CF, PF, AF, ZF, IF ve 8086'nın hep 1 olan bitleri) yığına.

····FFF6····FFF8····FFFAF257hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

BX = 5.

····FFF6····FFF8····FFFAF257hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

5 + 3 = 8 → CF = 0, ZF = 0. Önceki sonuç bayrakları kayboldu…

····FFF6····FFF8····FFFAF257hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

…POPF ile geri geldi: CF = 1, ZF = 1.

····FFF6····FFF8····FFFAF257hFFFC0000hFFFESS:bağıl konum · küçük adres yukarıda

Program biter.

ORG   100h
MOV   AX, 0FFFFh
ADD   AX, 1      ; CF=1, ZF=1
PUSHF            ; bayrakları sakla
MOV   BX, 5
ADD   BX, 3      ; CF=0, ZF=0
POPF             ; bayraklar geri
RET

Kesme yordamları bayrakları kendileri saklamaz; INT bunu otomatik yapar. Normal kodda bir sonucu korumak için PUSHF/POPF gerekir.

Yığın Güvenliği

Yığın Güvenliği

  • PUSH ve POP komutları programın çalışması süresince geçici verileri saklar.
  • Yığının bütünlüğü eşit sayıda PUSH ve POP ile korunmalıdır (her yolda, her dalda!).
  • Eşit sayıda PUSH ve POP yapılmazsa yığın bozulur; işletim sistemine geri dönüş yapılamayabilir.
  • RET yığının üstünde bir dönüş adresi bekler. .COM programında DOS, yığına başlangıçta 0000h koyar: RET ile CS:0000h'ye (PSP) gidilir; orada duran INT 20h programı sonlandırır.
Fazla PUSHRET, dönüş adresi yerine son konan veriyi alır → rastgele adrese atlama.
Fazla POPDönüş adresi bir yazmaca “yenir”; RET yanlış yere döner.
Yanlış sıraDeğerler karışır (takas gibi), program sessizce yanlış çalışır.
FFFAAX değeriFFFCdönüş IPFFFE0000hRET bunu IP'ye alırPUSH AX'in POP'u unutuldu

Yığın Güvenliği · Kodu adım adım çalıştır

Hata Örneği: Dengesiz PUSH

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

Program sonunda RET ile DOS'a dönülmesi bekleniyor.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

AX = 1234h.

····FFF6····FFF8····FFFA1234hFFFCSP0000hFFFESS:bağıl konum · küçük adres yukarıda

AX yığına kondu ama hiç POP edilmeyecek.

····FFF6····FFF8····FFFA1234hFFFC0000hFFFESPSS:bağıl konum · küçük adres yukarıda

RET üstteki sözcüğü dönüş adresi sanar: IP = 1234h. CPU 0700:1234h'deki rastgele baytları komut olarak çalıştırmaya başlar → program çöker ya da donar.

ORG   100h
MOV   AX, 1234h
PUSH  AX         ; POP unutuldu!
RET              ; IP ← ?

Düzeltme: RET'ten önce POP AX eklenir ya da hiç PUSH yapılmaz. Her dalın (JMP/Jcc) PUSH/POP sayısı ayrı ayrı sayılır.

Yığın Güvenliği

Yığın Taşması ve Alt Taşma

Taşma (overflow)
  • Sonsuz özyineleme, döngü içinde POP'suz PUSH.
  • SP küçüldükçe yığın aşağı inip kod ve verinin üzerine yazar (.COM'da aynı kesim).
  • 8086 bunu denetlemez; hata yoktur, yalnızca bozulma vardır.
Alt taşma (underflow)
  • PUSH edilmemiş değeri POP etmek.
  • SP = FFFEh iken POP → SP = 0000h (16 bitte sarar); sonraki PUSH FFFEh'ye yazar.
  • Okunan değer çöptür (ya da DOS'un 0000h'si).
PSP0000hkod + veri0100hboş alanyığın ↓FFFEhtaşma: SP küçülür, koda ineralt taşma: SP = FFFEh + 2 → 0000h (sarar)
Kural: yığın alanının boyutu programın en derin çağrı zinciri + en çok PUSH sayısına göre seçilir. .EXE programlarında .STACK 100h gibi bir direktifle ayrı ve yeterli bir yığın kesimi ayrılır.

Yığın Güvenliği

İyi Uygulamalar

1. Ayna sırasıPUSH AX, BX, CX → POP CX, BX, AX. Kodu alt alta yazınca ayna görüntüsü gibi görünmeli.
2. Tek çıkışProsedürde birden çok RET yerine tek bir çıkış etiketi kullanılır; POP'lar bir kez yazılır.
3. SP'ye dokunmamakSP'yi doğrudan değiştirmek yalnızca parametre temizleme (ADD SP, n) veya yerel alan ayırma (SUB SP, n) içindir.
4. BP çerçevesiYığındaki verilere [SP+n] ile erişilemez (8086'da SP taban yazmaç değildir); MOV BP, SP kullanılır.
5. Kesimleri korumakDS/ES değiştiren yordamlar onları da PUSH/POP etmeli.
6. emu8086 ile denetlemekstack düğmesi yığın penceresini açar; RET'ten önce SP'nin girişteki değerine döndüğü denetlenir.
PUSH AXPOP CXPUSH BXPOP BXPUSH CXPOP AXayna görüntüsü

Yığın Güvenliği

emu8086 ile Yığını İzlemek

1. Derle ve yüklecompile → emulate. Yazmaç panelinde CS, IP, SS, SP'nin başlangıç değerleri görülür (.COM: 0700h, 0100h, 0700h, FFFEh).
2. Tek adımsingle step her basışta bir komut çalıştırır; değişen yazmaçlar vurgulanır. step back ile geri alınabilir.
3. stack penceresiAlttaki stack düğmesi SS:SP çevresindeki sözcükleri gösterir; SP'nin gösterdiği satır işaretlidir.
4. Bellek görünümüOrtadaki liste baytları (hex, onluk, ASCII) gösterir: B8 12 12 = MOV AX, 1212h. Little-endian burada görülür.
Yükleme sonrası CX, programın bayt sayısını gösterir (ör. takas örneğinde 000Bh = 11 bayt). Bu, DOS'un DEBUG programından kalma bir gelenektir; program CX'e güvenmemelidir.

Laboratuvar · 8086 komutları adım adım

Laboratuvar: Yığın

Özet

Özet

KavramHatırlanacak nokta
AdresYığının üstü = SS:SP; fiziksel adres SS × 16 + SP.
Büyüme yönüAşağı: PUSH SP −= 2, POP SP += 2. Birim her zaman sözcük.
PUSH / POPREG, SREG, bellek (POP CS yok). PUSH sabit yalnızca 80186+ / emu8086.
LIFODeğerler ters sırada döner → saklarken ayna sırası, takasta aynı sıra.
PUSHF / POPFBayrak yazmacını saklar ve geri yükler; PUSH ve POP bayraklara dokunmaz.
GüvenlikEşit sayıda PUSH/POP; .COM'da son RET DOS'un 0000h'sini alır.

Özet

Alıştırmalar

  1. SS = 2000h, SP = 0100h iken PUSH AX sonrası yazılan baytların fiziksel adresleri nedir?
  2. MOV AX,1 / MOV BX,2 / MOV CX,3 / PUSH AX / PUSH BX / PUSH CX / POP BX / POP CX / POP AX sonunda AX, BX, CX?
  3. Yalnızca PUSH/POP ile DS'nin değerini ES'ye kopyalayın.
  4. SP = FFFEh iken PUSH AX / PUSH BX / PUSHF / POP CX yürütülüyor. SP kaç olur, CX'e ne gelir?
  5. AX'i ve bayrakları bir işlem boyunca koruyan PUSH/POP dizisini yazın.
Cevaplar
  1. SP = 00FEh → 20000h + 00FEh = 200FEh (AL) ve 200FFh (AH).
  2. AX = 1, BX = 3, CX = 2: AX yerinde kalır, BX ile CX takas edilir.
  3. PUSH DS + POP ES.
  4. SP = FFFAh; CX = PUSHF anındaki bayrak yazmacı. Yığında AX ve BX'in kopyaları kalır.
  5. PUSH AX, PUSHF … POPF, POP AX (ayna sırası).

Özet

Kaynaklar

  • Intel, The 8086 Family User's Manual, 1979 — PUSH, POP, CALL, RET, INT, IRET komut tanımları.
  • B. B. Brey, The Intel Microprocessors, 8. baskı, Pearson, 2009 — Bölüm 4 ve 6.
  • K. R. Irvine, Assembly Language for x86 Processors, 7. baskı, Pearson, 2014 — yığın işlemleri ve prosedürler.
  • emu8086 yardım belgeleri: 8086 Assembler Tutorial, Part 9 (The Stack).

Son

Bölüm 8.1: Yığın
Sonraki bölüm: Bölüm 8.2: Prosedürler · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.