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

Mikroişlemciler
Yığın ve LIFOSS:SPPUSH / POPBayt sırasıYazmaç saklamaTakasCALL / RETParametre geçirmeINT / IRETYığı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.

CALL / RET / INT

Dönüş adresinin ve bayrakların yığında nasıl saklandığını, parametrelerin BP ile nasıl okunduğunu göstermek.

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 word (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) word'ü 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 (hex'te 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
Dikkat: 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:ofset · 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:ofset · küçük adres yukarıdaPUSH AX · 1

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

····FFF8····FFFA1234hFFFCSP0000hFFFESS:ofset · küçük adres yukarıdaPUSH AX · 2

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

····FFF8····FFFA1234hFFFCSP0000hFFFESS:ofset · küçük adres yukarıdaPOP BX · 1

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

····FFF8····FFFA1234hFFFC0000hFFFESPSS:ofset · 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)

Word görünümü1234hSS:FFFCBayt görünümü (bellek)34hFFFC12hFFFD00hFFFE00hFFFF← düşük bayt (AL)← yüksek bayt (AH)DOS’un 0000h word’ü
  • 8086 bir word'ü belleğe düşük bayt önce yazar: PUSH AX (AX = 1234h) sonrası [FFFCh] = 34h, [FFFDh] = 12h.
  • emu8086'nın stack penceresi word'leri 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 word 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 sonucunu (bayrakları) PUSH/POP arasında güvenle taşıyabilirsiniz; 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:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA002AhFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA002AhFFFC0000hFFFESPSS:ofset · 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:ofset · 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:ofset · küçük adres yukarıda

Başlangıç: SP = FFFEh.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

AX = 0001h.

····FFF6····FFF8····FFFA0001hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA0001hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF80002hFFFASP0001hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF80002hFFFASP0001hFFFC0000hFFFESS:ofset · küçük adres yukarıda

AX = 0003h.

····FFF60003hFFF8SP0002hFFFA0001hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60003hFFF80002hFFFASP0001hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60003hFFF80002hFFFA0001hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

POP BX → 2. SP = FFFCh.

····FFF60003hFFF80002hFFFA0001hFFFC0000hFFFESPSS:ofset · 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:ofset · 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:ofset · küçük adres yukarıda

AX = 1234h.

····FFF6····FFF8····FFFA1234hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA1234hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA1234hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA1234hFFFC0000hFFFESS:ofset · 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:ofset · 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:ofset · küçük adres yukarıda

AX = 1212h.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

BX = 3434h.

····FFF6····FFF8····FFFA1212hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF83434hFFFASP1212hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF83434hFFFA1212hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF83434hFFFA1212hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF6····FFF83434hFFFA1212hFFFC0000hFFFESS:ofset · 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 etseydik (ö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:ofset · 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:ofset · küçük adres yukarıda

AX = 0002h.

····FFF6····FFF8····FFFA····FFFC····FFFESPSS:ofset · küçük adres yukarıda

BX = 0004h.

····FFF6····FFF8····FFFA····FFFC····FFFESPSS:ofset · küçük adres yukarıda

CX = 0008h.

····FFF6····FFF8····FFFA0002hFFFCSP····FFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF80004hFFFASP0002hFFFC····FFFESS:ofset · küçük adres yukarıda

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

····FFF60008hFFF8SP0004hFFFA0002hFFFC····FFFESS:ofset · küçük adres yukarıda

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

····FFF60008hFFF80004hFFFASP0002hFFFC····FFFESS:ofset · küçük adres yukarıda

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

····FFF60008hFFF80004hFFFA0002hFFFCSP····FFFESS:ofset · küçük adres yukarıda

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

····FFF60008hFFF80004hFFFA0002hFFFC····FFFESPSS:ofset · 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:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

SI = 0117h (metin dizisinin adresi).

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

Döngü sayacı CX = 3.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA0041hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA0041hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

SI = 0118h: sonraki karakter.

····FFF6····FFF8····FFFA0041hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

LOOP: CX = 2 → geri dön.

····FFF6····FFF8····FFFA0041hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF80042hFFFASP0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF80042hFFFASP0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

SI = 0119h: sonraki karakter.

····FFF6····FFF80042hFFFASP0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

LOOP: CX = 1 → geri dön.

····FFF6····FFF80042hFFFASP0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60043hFFF8SP0042hFFFA0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60043hFFF8SP0042hFFFA0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

SI = 011Ah: sonraki karakter.

····FFF60043hFFF8SP0042hFFFA0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60043hFFF8SP0042hFFFA0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60043hFFF80042hFFFASP0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60043hFFF80042hFFFASP0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60043hFFF80042hFFFASP0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

INT 21h: ekrana C yazılır.

····FFF60043hFFF80042hFFFASP0041hFFFC0000hFFFESS:ofset · küçük adres yukarıda

LOOP: CX = 2 → geri dön.

····FFF60043hFFF80042hFFFA0041hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60043hFFF80042hFFFA0041hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF60043hFFF80042hFFFA0041hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

INT 21h: ekrana B yazılır.

····FFF60043hFFF80042hFFFA0041hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

LOOP: CX = 1 → geri dön.

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

INT 21h: ekrana A yazılır.

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF60043hFFF80042hFFFA0041hFFFC0000hFFFESS:ofset · 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

Tahmin Et: 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

Önce kendiniz düşünün, sonra › ile adım adım açın.

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

Örnekler

Etkileşimli Yığın Simülatörü

Yazmaçlar (hex, düzenlenebilir)

SPFFFEh
SS:SP çevresi

Çalıştırılan komutlar

Deneyin: AX ve BX'i takas edin; sonra yığın boşken POP yapmayı deneyin (gerçek 8086 bunu engellemez, SP yine 2 artar).

CALL / RET

CALL ve RET

  • CALL hedef (yakın): bir sonraki komutun adresini (dönüş IP'si) yığına koyar, sonra IP ← hedef.
  • RET: yığının üstündeki word'ü IP'ye alır; program CALL'dan sonraki komuttan devam eder.
  • Uzak (far) CALL önce CS'yi sonra IP'yi koyar; RETF ikisini ters sırada alır.
  • RET n: dönüşten sonra SP'ye ayrıca n ekler; yığına konmuş parametreleri temizlemek için kullanılır.
  • İç içe çağrılarda her CALL yığına yeni bir dönüş adresi ekler; RET'ler bunları ters sırada tüketir (LIFO).
KomutYığınaSP
CALL nearIP−2
CALL farCS, IP−4
RET← IP+2
RETF← IP, CS+4
RET 4← IP+2+4

CALL / RET · Kodu adım adım çalıştır

CALL / RET Adım Adım

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

Başlangıç: IP = 0100h, SP = FFFEh.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

AX = 0005h; IP bir sonraki komutu (0103h) gösterir.

····FFF6····FFF8····FFFA0106hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

CALL KARE: dönüş adresi 0106h (CALL 3 bayt: 0103h + 3) yığına konur; IP = 0107h.

····FFF6····FFF8····FFFA0106hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

MUL AX: DX:AX = 5 × 5 = 0019h (25). DX = 0000h.

····FFF6····FFF8····FFFA0106hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

RET: IP ← [FFFCh] = 0106h; SP = FFFEh. Ana programa dönüldü.

····FFF6····FFF8····FFFA0106hFFFC0000hFFFESS:ofset · küçük adres yukarıda

Ana programın RET'i 0000h'yi alır → program biter.

ORG  100h        ; 0100h
MOV  AX, 5       ; 0100h
CALL KARE        ; 0103h
RET              ; 0106h
KARE PROC
MUL  AX          ; 0107h  DX:AX = AX*AX
RET              ; 0109h
KARE ENDP

CALL / RET

Prosedürde Yazmaç Koruma

TOPLA_N PROC       ; AX = 1+2+...+CX
        PUSH BX    ; BX'i koru
        PUSH CX    ; CX'i koru
        XOR  AX, AX
        MOV  BX, 1
dongu:  ADD  AX, BX
        INC  BX
        LOOP dongu
        POP  CX    ; ters sıra!
        POP  BX
        RET
TOPLA_N ENDP
  • Prosedür, kullandığı yazmaçları girişte yığına koyar, çıkışta ters sırada geri alır. Çağıran taraf yazmaçlarının bozulmayacağını bilir.
  • Dönüş adresi bu PUSH'ların altında kalır; RET'e gelindiğinde tüm PUSH'lar POP edilmiş olmalıdır.
  • Sonucu döndüren yazmaç (burada AX) korunmaz; aksi hâlde sonuç kaybolur.
RET anında yığının üstünde dönüş adresi yerine CX olsaydı, program CX'in değerindeki adrese atlardı.

CALL / RET · Kodu adım adım çalıştır

Yığınla Parametre Geçirme (BP ile)

····FFF4····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

Çağıran, parametreleri yığına koyar; prosedür bunları BP üzerinden okur.

····FFF4····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

AX = 7.

····FFF4····FFF6····FFF8····FFFA0007hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

1. parametre yığında: [FFFCh] = 0007h.

····FFF4····FFF6····FFF8····FFFA0007hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

AX = 3.

····FFF4····FFF6····FFF80003hFFFASP0007hFFFC0000hFFFESS:ofset · küçük adres yukarıda

2. parametre: [FFFAh] = 0003h.

····FFF4····FFF6010BhFFF8SP0003hFFFA0007hFFFC0000hFFFESS:ofset · küçük adres yukarıda

CALL: dönüş adresi 010Bh → [FFF8h]. IP = 010Fh.

····FFF40000hFFF6SP010BhFFF80003hFFFA0007hFFFC0000hFFFESS:ofset · küçük adres yukarıda

Eski BP saklanır: [FFF6h] = 0000h.

····FFF40000hFFF6SP010BhFFF80003hFFFA0007hFFFC0000hFFFESS:ofset · küçük adres yukarıda

MOV BP, SP: BP = FFF6h sabit bir çerçeve noktası olur.

····FFF40000hFFF6SP010BhFFF80003hFFFA0007hFFFC0000hFFFESS:ofset · küçük adres yukarıda

[BP+6] = [FFFCh] = 0007h → AX = 7.

····FFF40000hFFF6SP010BhFFF80003hFFFA0007hFFFC0000hFFFESS:ofset · küçük adres yukarıda

[BP+4] = [FFFAh] = 0003h → AX = 000Ah.

····FFF40000hFFF6010BhFFF8SP0003hFFFA0007hFFFC0000hFFFESS:ofset · küçük adres yukarıda

BP eski değerine döner; SP = FFF8h (dönüş adresi üstte).

····FFF40000hFFF6010BhFFF80003hFFFASP0007hFFFC0000hFFFESS:ofset · küçük adres yukarıda

RET: IP = 010Bh, SP = FFFAh.

····FFF40000hFFF6010BhFFF80003hFFFA0007hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

ADD SP, 4: iki parametre atılır, SP = FFFEh. Yığın dengelendi.

····FFF40000hFFF6010BhFFF80003hFFFA0007hFFFC0000hFFFESS:ofset · küçük adres yukarıda

Program biter.

ORG  100h
MOV  AX, 7
PUSH AX          ; 1. parametre
MOV  AX, 3
PUSH AX          ; 2. parametre
CALL TOPLA
ADD  SP, 4       ; parametreleri at
RET
TOPLA PROC
PUSH BP
MOV  BP, SP
MOV  AX, [BP+6]  ; 1. parametre
ADD  AX, [BP+4]  ; 2. parametre
POP  BP
RET
TOPLA ENDP

[BP+0] = eski BP, [BP+2] = dönüş adresi, [BP+4] = son konan parametre. ADD SP, 4 yerine prosedürde RET 4 de yazılabilirdi.

CALL / RET

Yığın Çerçevesi ve Yerel Değişkenler

1. parametre (7)[BP+6]2. parametre (3)[BP+4]dönüş adresi[BP+2]eski BP[BP+0]yerel değişken 1[BP−2]yerel değişken 2[BP−4]BPSPküçük adres yukarıda · büyük adres aşağıda
PUSH BP
MOV  BP, SP
SUB  SP, 4        ; yerel alan
MOV  [BP-2], AX   ; yerel 1
...
MOV  SP, BP       ; yerelleri at
POP  BP
RET  4            ; 2 parametreyi at
  • SUB SP, 4 yığında iki word'lük yerel alan açar: [BP−2], [BP−4].
  • MOV SP, BP yerel alanı tek komutla geri verir.
  • Parametreler pozitif, yereller negatif BP ofsetindedir.

INT / IRET

INT ve IRET: Yığında Üç Word

····FFF8····FFFA····FFFC0000hFFFESP

0700:0103h adresindeki INT 21h (2 bayt) çalışacak. FLAGS = 0202h (IF = 1), SP = FFFEh.

····FFF8····FFFA0202hFFFCFLAGSSP0000hFFFE

① FLAGS yığına: SP = FFFCh, [FFFCh] = 0202h.

····FFF80700hFFFACSSP0202hFFFCFLAGS0000hFFFE

② CS yığına: SP = FFFAh, [FFFAh] = 0700h.

0105hFFF8IPSP0700hFFFACS0202hFFFCFLAGS0000hFFFE

③ Dönüş IP'si yığına: SP = FFF8h, [FFF8h] = 0105h (INT'ten sonraki komut). Ardından IF = TF = 0.

0105hFFF8IPSP0700hFFFACS0202hFFFCFLAGS0000hFFFEKesme vektör tablosu[0000:0084h] → IP[0000:0086h] → CS

④ Yeni CS:IP, vektör tablosundaki 21h × 4 = 84h adresinden okunur: IP ← [0000:0084h], CS ← [0000:0086h]. Kesme yordamı çalışır.

0105hFFF8IP0700hFFFACS0202hFFFCFLAGS0000hFFFESPIRET sonrası:IP = 0105h, CS = 0700hFLAGS = 0202h

⑤ Yordamın sonundaki IRET: IP, CS ve FLAGS ters sırada geri alınır; SP = FFFEh. Program 0700:0105h'den devam eder.

Donanım kesmelerinde (ör. zamanlayıcı, klavye) CPU aynı üç word'ü kendisi yığına koyar. Bu yüzden kesme yordamları kullandıkları yazmaçları mutlaka PUSH/POP ile korumalıdır.

INT / IRET · Kodu adım adım çalıştır

Bayrakları Saklamak: PUSHF / POPF

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

AX = FFFFh.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFAF257hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFAF257hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

BX = 5.

····FFF6····FFF8····FFFAF257hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFAF257hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFAF257hFFFC0000hFFFESS:ofset · 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.

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

Hata Örneği: Dengesiz PUSH

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

Program sonunda RET ile DOS'a dönmek istiyoruz.

····FFF6····FFF8····FFFA····FFFC0000hFFFESPSS:ofset · küçük adres yukarıda

AX = 1234h.

····FFF6····FFF8····FFFA1234hFFFCSP0000hFFFESS:ofset · küçük adres yukarıda

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

····FFF6····FFF8····FFFA1234hFFFC0000hFFFESPSS:ofset · küçük adres yukarıda

RET üstteki word'ü 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 ekleyin ya da hiç PUSH etmeyin. Her dalın (JMP/Jcc) PUSH/POP sayısını ayrı ayrı sayın.

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).
Kural: yığın alanının boyutunu programın en derin çağrı zinciri + en çok PUSH sayısına göre seçin. .EXE programlarında .STACK 100h gibi bir yönergeyle ayrı ve yeterli bir yığın kesimi ayırın.

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ın; POP'lar bir kez yazılır.
3. SP'ye dokunmayınSP'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ın.
5. Kesimleri koruyunDS/ES değiştiren yordamlar onları da PUSH/POP etmeli.
6. emu8086 ile denetleyinstack düğmesi yığın penceresini açar; RET'ten önce SP'nin girişteki değerine döndüğünü kontrol edin.

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ğerlerini not edin (.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 word'leri 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 bir DOS geleneğidir; programınız CX'e güvenmemelidir.

Ö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 word.
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.
CALL / RETDönüş adresi = CALL'dan sonraki komutun adresi; RET n parametreleri temizler.
INT / IRETFLAGS, CS, IP (6 bayt) otomatik saklanır; vektör adresi n × 4.
ParametrelerPUSH ile verilir, [BP+4], [BP+6]… ile okunur.
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. Bir prosedür girişte PUSH BX, PUSH CX yapıp çıkışta yalnızca POP BX yaparsa ne olur?
  5. 0700:0200h'deki CALL 0300h (3 bayt) yürütülürken SP = FFF0h ise sonrasında SP ve [SS:SP]?
  6. Üç parametreli bir prosedürde (PUSH BP / MOV BP,SP sonrası) ilk konan parametre hangi adrestedir?
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. RET, CX'in eski değerini dönüş adresi sanır → yanlış adrese atlar.
  5. SP = FFEEh, [FFEEh] = 0203h.
  6. [BP+8] (sonra [BP+6], en son konan [BP+4]).

Ö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: Yığın
Sonraki bölüm: Bölüm 8: Makrolar
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.