Mempelajari cara memecah program menjadi modul-modul prosedur, memahami mekanisme stack, instruksi CALL/RET, dan teknik penerusan parameter pada Assembly 8086.
Capaian Pembelajaran Bab Ini
Setelah mempelajari bab ini, mahasiswa mampu menjelaskan organisasi dan mekanisme kerja stack; menulis dan memanggil prosedur menggunakan instruksi CALL dan RET; menerapkan konsep save/restore register; melakukan penerusan parameter melalui register, memori, dan stack; serta membandingkan prosedur dengan makro.
Saat program semakin besar, menulis semua kode dalam satu blok monolitik menjadi sulit dibaca, sulit di-debug, dan sulit dimaintenance. Prosedur (disebut juga subroutine atau function) memungkinkan kita memecah program menjadi modul-modul kecil yang masing-masing bertanggung jawab atas satu tugas spesifik.
↻
Reusability
Kode yang sama tidak perlu ditulis ulang — cukup panggil prosedurnya
⚙
Maintainability
Bug diperbaiki di satu tempat, otomatis berlaku untuk semua pemanggil
▦
Modularity
Program besar dipecah menjadi modul-modul kecil yang mudah dipahami
Analogi: Resep Masakan
❌ Tanpa Prosedur
Resep nasi goreng menuliskan langkah menanak nasi dari awal (cuci beras, ukur air, nyalakan kompor, tunggu 15 menit...) di setiap resep yang butuh nasi. Padahal langkahnya sama.
✓ Dengan Prosedur
Buat satu prosedur "TanakNasi()" yang bisa dipanggil dari resep nasi goreng, nasi kuning, atau lontong. Jika ada cara lebih baik menanak nasi, ubah hanya di satu tempat.
Dalam bahasa C, prosedur disebut function. Dalam Assembly 8086, kita menggunakan instruksi CALL untuk memanggil dan RET untuk kembali. Namun sebelum memahami CALL/RET, kita harus memahami stack — karena stack adalah mekanisme yang membuat CALL/RET bekerja.
10.2 Konsep Stack (Tumpukan)
Stack adalah area memori khusus yang beroperasi dengan prinsip LIFO (Last In, First Out — masuk terakhir, keluar pertama). Bayangkan setumpuk piring: piring yang ditaruh terakhir akan diambil pertama.
Operasi PUSH (masukkan)
...
...
Data lama
← PUSH AX
← PUSH BX
↑ SP menunjuk ke sini
Operasi POP (ambil)
...
...
Data lama
AX (masih ada)
← POP BX
↑ SP pindah ke sini
Tiga Fakta Penting tentang Stack 8086
▶Tumbuh ke bawah (grows downward): Alamat stack menurun setiap kali data dimasukkan. SP dimulai dari alamat tinggi dan bergerak ke alamat rendah.
▶Selalu 16-bit (word): Setiap operasi PUSH/POP selalu menangani 2 byte, tidak peduli apakah data aslinya 8-bit atau 16-bit.
▶Berada di segmen stack (SS:SP): Lokasi fisik stack ditentukan oleh register SS (Stack Segment) dan SP (Stack Pointer).
10.3 Register SS dan SP
Dua register segment yang mengelola stack:
Register
Nama
Fungsi
Ukuran
SS
Stack Segment
Menunjuk ke segmen memori tempat stack berada. Menentukan alamat dasar stack.
16 bit
SP
Stack Pointer
Menunjuk ke elemen teratas (top of stack). Berubah otomatis oleh PUSH, POP, CALL, RET, dan INT.
16 bit
Alamat fisik elemen teratas stack dihitung sebagai: SS × 16 + SP. Pada model memori .model small, SS biasanya sama dengan DS (segmen data dan stack berada di segmen yang sama). SP diinisialisasi oleh direktif .stack 100h yang mengalokasikan 256 byte dan menempatkan SP di awal area tersebut.
Segmen Stack (SS)
SS:FFFESP awal (setelah .stack)
SS:FFFC
SS:FFFA← PUSH pertama akan ke sini
SS:FFF8
...
SS:FF00Batas bawah stack (256 byte)
Alamat tinggi di atas, alamat rendah di bawah. SP bergerak ke bawah saat PUSH.
10.4 PUSH dan POP
PUSH dan POP adalah dua instruksi dasar untuk mengoperasikan stack.
PUSH src
1. SP ← SP − 2
2. [SS:SP] ← src (16-bit)
PUSHAX; simpan AX ke stackPUSHBX; simpan BX ke stackPUSHDS; simpan DS ke stackPUSHSI; simpan SI ke stack
POP dest
1. dest ← [SS:SP] (16-bit)
2. SP ← SP + 2
POPSI; ambil dari stack ke SIPOPDS; ambil dari stack ke DSPOPBX; ambil dari stack ke BXPOPAX; ambil dari stack ke AX
⚠ Urutan POP harus kebalikan PUSH! Jika Anda push AX, BX, CS — maka harus pop CS, BX, AX (LIFO). Jika urutan terbalik, nilai akan masuk ke register yang salah.
Apa yang Bisa Di-PUSH/POP?
Bisa
Tidak Bisa
Register 16-bit: AX, BX, CX, DX, SI, DI, BP, SP
Register 8-bit: AL, AH, BL, dll.
Segment register: CS, DS, SS, ES
Immediate value: PUSH 5 ← ERROR
Memory operand 16-bit: PUSH [SI]
Memory operand 8-bit: PUSH BYTE PTR [SI] ← ERROR
Flags: PUSHF / POPF
—
Catatan: Pada 8086, PUSH immediate tidak didukung. Fitur ini baru tersedia mulai 80186.
10.5 Ilustrasi Operasi Stack Langkah demi Langkah
Misalkan awalnya SP = 1000h, AX = 1234h, dan BX = 5678h. Perhatikan bagaimana stack berubah:
Langkah
Instruksi
SP
[SS:SP]
[SS:SP+2]
Keterangan
0
—
1000h
??
??
Keadaan awal
1
PUSH AX
0FFEh
1234h
??
SP−2, AX disimpan di [0FFEh]
2
PUSH BX
0FFCh
5678h
1234h
SP−2, BX disimpan di [0FFCh]
3
POP CX
0FFEh
1234h
??
CX = 5678h (dari top), SP+2
4
POP DX
1000h
??
??
DX = 1234h (dari top), SP+2
Hasil Akhir
AX1234h
BX5678h
CX5678h
DX1234h
CX mendapat nilai BX dan DX mendapat nilai AX — inilah efek LIFO. SP kembali ke 1000h (seimbang).
10.6 Aturan Emas Stack
Melanggar aturan ini adalah sumber bug paling umum dalam pemrograman prosedur pada 8086:
1
Setiap PUSH harus memiliki POP yang sesuai
Jumlah PUSH dan POP di dalam satu prosedur harus seimbang. Jika tidak, SP akan salah saat RET.
2
Urutan POP harus kebalikan PUSH
PUSH AX, BX → POP BX, AX. Jika urutan salah, register akan mendapat nilai yang salah.
3
Jangan akses di bawah batas stack
Jika SP melewati batas bawah stack (stack underflow), data akan ditulis ke area memori yang tidak diperuntukkan bagi stack — bisa menimpa kode atau data penting.
4
Save register yang akan diubah, restore sebelum RET
Jika prosedur menggunakan AX, BX, CX — push ketiganya di awal, pop ketiganya (terbalik) sebelum RET. Dengan begitu register pemanggil tidak terpengaruh.
10.7 Stack Frame
Stack frame (juga disebut activation record) adalah blok data di stack yang terkait dengan satu pemanggilan prosedur. Stack frame berisi: alamat return, parameter, dan variabel lokal.
Alamat Tinggi← pemanggil
Parameter ke-N[BP+8+]
Parameter 2[BP+6]
Parameter 1[BP+4]
Return Address (IP)[BP+2]
Old BP (disimpan BP lama)[BP+0] ← BP
Variabel lokal 1[BP−2]
Variabel lokal 2[BP−4]
Alamat Rendah← SP
Gambar 10.1: Struktur stack frame tipikal. Parameter diakses via [BP+offset], variabel lokal via [BP−offset].
10.8 CALL dan RET
CALL dan RET adalah pasangan instruksi yang mengimplementasikan mekanisme pemanggilan prosedur.
CALL target
Near CALL:
1. PUSH IP (alamat instruksi berikutnya)
2. IP ← alamat target
Far CALL:
1. PUSH CS
2. PUSH IP
3. CS:IP ← alamat target
RET [n]
Near RET:
1. POP IP
Far RET:
1. POP IP
2. POP CS
RET n (membersihkan parameter):
1. POP IP
2. SP ← SP + n
Program Utama
...
CALL prosedur ← ①
instrksi_selanjut: ← ④
...
② PUSH IP lalu lompat
→
←
④ POP IP kembali
Prosedur
prosedur: ← ②
...
instruksi prosedur
...
RET ← ③
① CALL → ② Push return address & jump → ③ Eksekusi prosedur → ④ RET kembali ke instruksi setelah CALL
RET n — Membersihkan Parameter dari Stack
Jika parameter dikirim via stack, setelah RET alamat return sudah di-pop tapi parameter masih tersisa di stack. RET n secara otomatis menambah SP sebesar n setelah pop, sehingga parameter ikut terbuang. Misalnya, jika 2 parameter word dikirim: RET 4 (2 × 2 byte = 4).
10.9 Deklarasi Prosedur (PROC / ENDP)
Prosedur dideklarasikan menggunakan pasangan direktif PROC dan ENDP. Tipe prosedur (NEAR atau FAR) menentukan apakah CALL/RET hanya menyimpan IP atau CS dan IP.
Sintaks Prosedur
; Near procedure (default, segmen kode sama)nama_prosedurPROCNEAR; ... isi prosedur ...RETnama_prosedurENDP; Far procedure (segmen kode berbeda)nama_prosedurPROCFAR; ... isi prosedur ...RETnama_prosedurENDP
NEAR vs FAR
Aspek
NEAR
FAR
Segmen kode
Sama dengan pemanggil
Bisa berbeda dari pemanggil
CALL push
Hanya IP (2 byte)
CS + IP (4 byte)
RET pop
Hanya IP
IP + CS
Ukuran instruksi CALL
3 byte
5 byte
Kecepatan
Lebih cepat
Lebih lambat
Penggunaan
Mayoritas prosedur (default)
Prosedur antar segmen, ISR
Pada model .model small, hampir semua prosedur menggunakan NEAR (dan NEAR adalah default jika tidak ditulis).
10.10 Save & Restore Register
Prosedur yang "baik" tidak memiliki efek samping pada register yang digunakan oleh pemanggil. Caranya: simpan (push) register yang akan dimodifikasi di awal prosedur, dan kembalikan (pop) sebelum RET.
Pola Save/Restore
CETAK_KARAKTERPROCNEAR; Input: DL = karakter yang akan dicetak; Output: tidak ada; Mengubah: AX, DX (disimpan dan dikembalikan)PUSHAX; ① simpan register yang akan dipakaiPUSHDX; ② urutan push: AX, DXMOVAH, 02h; fungsi DOS: cetak karakter; DL sudah berisi karakter dari pemanggilINT21hPOPDX; ③ urutan pop: DX, AX (kebalikan!)POPAX; ④ register dikembalikan ke nilai semulaRETCETAK_KARAKTERENDP
Kode 10.1: Prosedur dengan save/restore register. Pemanggil bisa yakin AX dan DX tidak berubah setelah CALL.
Di mana menaruh PUSH/POP? Push segera setelah label prosedur (sebelum kode apapun). Pop tepat sebelum RET (setelah semua kode prosedur). Jangan push/pop di tengah-tengah logika prosedur karena mempersulit pembacaan dan rawan error.
10.11 Penerusan Parameter: Via Register
Metode paling sederhana: parameter disimpan di register sebelum CALL, dan prosedur membacanya dari register tersebut. Cocok untuk parameter sedikit (1–4 parameter).
Contoh: Prosedur CETAK_ANGKA — cetak angka 0-9
; Prosedur: CETAK_ANGKA; Input: AL = angka (0-9); Output: mencetak angka tersebut ke layar; Mengubah: AX, DX (disave/direstore)CETAK_ANGKAPROCNEARPUSHAXPUSHDXADDAL, 30h; konversi angka 0-9 ke ASCII '0'-'9'MOVDL, AL; DL = karakter ASCIIMOVAH, 02h; fungsi cetak karakterINT21hPOPDXPOPAXRETCETAK_ANGKAENDP; === Program Utama ===MOVAL, 7CALLCETAK_ANGKA; mencetak '7' ke layarMOVAL, 3; parameter: angka 3CALLCETAK_ANGKA; mencetak '3' ke layar
Kode 10.2: Parameter via register — AL berisi angka yang akan dicetak.
Konvensi Parameter via Register (Umum)
Register
Peran Umum
Contoh
AL / AX
Parameter pertama / nilai kembalian
AL = karakter, AX = hasil penjumlahan
BX
Parameter kedua / pointer data
BX = offset string
CX
Counter / panjang data
CX = jumlah elemen array
DX
Parameter ketiga / port I/O
DX = alamat port
SI / DI
Pointer source / destination
SI = offset awal, DI = offset tujuan
10.12 Penerusan Parameter: Via Memori
Parameter disimpan di variabel global di segmen data. Prosedur mengaksesnya langsung menggunakan nama variabel. Metode ini paling mudah dipahami tapi kurang modular.
Contoh: Hitung luas persegi panjang
.data
panjang dw15; parameter 1 (global)
lebar dw8; parameter 2 (global)
luas dw?; hasil (global).code; Prosedur: HITUNG_LUAS; Membaca: panjang, lebar (variabel global); Menulis: luas (variabel global)HITUNG_LUASPROCNEARPUSHAXPUSHBXMOVAX, panjang; AX = panjangMOVBX, lebar; BX = lebarMULBX; DX:AX = panjang × lebarMOVluas, AX; simpan hasil (asumsi tidak overflow)POPBXPOPAXRETHITUNG_LUASENDP; === Program Utama ===MOVpanjang, 15; set parameterMOVlebar, 8; set parameterCALLHITUNG_LUAS; hasil: luas = 120
Kode 10.3: Parameter via memori — prosedur membaca/menulis variabel global langsung.
Kelemahan Metode Ini: Prosedur terikat pada nama variabel tertentu (panjang, lebar, luas), sehingga tidak bisa di-reuse untuk menghitung luas dari pasangan nilai lain tanpa mengubah variabel global terlebih dahulu. Ini melanggar prinsip modularitas.
10.13 Penerusan Parameter: Via Stack
Metode paling fleksibel dan profesional. Parameter di-PUSH ke stack sebelum CALL, dan prosedur mengaksesnya via [BP + offset]. Ini juga memungkinkan rekursi.
Contoh: Jumlah dua bilangan via stack
; Prosedur: TAMBAH; Parameter [BP+4]: bilangan pertama (word); Parameter [BP+6]: bilangan kedua (word); Return: AX = hasil penjumlahanTAMBAHPROCNEARPUSHBP; ① simpan BP pemanggilMOVBP, SP; ② BP = SP (BP sekarang menunjuk stack frame)MOVAX, [BP+4] ; ③ AX = parameter 1ADDAX, [BP+6] ; ④ AX += parameter 2POPBP; ⑤ kembalikan BP pemanggilRET4; ⑥ kembali & bersihkan 4 byte parameterTAMBAHENDP; === Program Utama ===PUSH25; push parameter 2 (kedua dulu!)PUSH17; push parameter 1CALLTAMBAH; AX = 17 + 25 = 42; RET 4 di dalam prosedur sudah membersihkan stack
Kode 10.4: Parameter via stack dengan RET n untuk membersihkan parameter.
Isi Stack saat di dalam prosedur TAMBAH
Param 2: 25 (0019h)[BP+6]
Param 1: 17 (0011h)[BP+4]
Return Address (IP)[BP+2]
Old BP ← BP menunjuk sini[BP]
⚠ Urutan Push Parameter: Parameter yang di-push terakhir berada di alamat paling rendah di stack, sehingga menjadi parameter pertama ([BP+4]). Jadi jika konvensinya "param1, param2", maka di program utama: PUSH param2 dulu, lalu PUSH param1. Ini sesuai konvensi C (right-to-left push order).
10.14 Contoh Program: Prosedur Cetak String
Prosedur ini menerima offset string di register DX dan mencetak seluruh string hingga karakter '$'. Ini membungkus fungsionalitas INT 21h AH=09h ke dalam prosedur yang lebih rapi.
Assembly 8086
.model small
.stack100h.data
pesan1 db'Halo dari prosedur!$'
pesan2 db'Prosedur membuat kode rapi.$'
newline db0Dh, 0Ah, '$'; CR + LF.code; === PROSEDUR-PROSEDUR ===; CETAK_STR: Cetak string yang diakhiri '$'; Input: DX = offset stringCETAK_STRPROCNEARPUSHAXMOVAH, 09hINT21hPOPAXRETCETAK_STRENDP; CETAK_NEWLINE: Pindah ke baris baruCETAK_NEWLINEPROCNEARPUSHDXLEADX, newlineCALLCETAK_STR; prosedur memanggil prosedur!POPDXRETCETAK_NEWLINEENDP; === PROGRAM UTAMA ===mulai:MOVAX, @dataMOVDS, AXLEADX, pesan1; parameter via registerCALLCETAK_STRCALLCETAK_NEWLINE; pindah baris baruLEADX, pesan2CALLCETAK_STRMOVAH, 4ChINT21hend mulai
Kode 10.5: Prosedur memanggil prosedur (nested call). CETAK_NEWLINE memanggil CETAK_STR di dalamnya.
Apa yang Terjadi di Stack Saat Nested Call?
Momen
Stack (top di atas)
CALL CETAK_STR (1x)
[Return Addr 1] ← SP
RET dari CETAK_STR
(kosong, SP kembali)
CALL CETAK_NEWLINE
[Return Addr 2] ← SP
PUSH DX di dalamnya
[DX lama] ← SP [Return Addr 2]
CALL CETAK_STR (2x)
[Return Addr 3] ← SP [DX lama] [Return Addr 2]
RET dari CETAK_STR
[DX lama] ← SP [Return Addr 2]
POP DX
[Return Addr 2] ← SP
RET dari CETAK_NEWLINE
(kosong, SP kembali)
Stack menangani nested call secara otomatis — setiap CALL push return address, setiap RET pop-nya. Tidak perlu konfigurasi tambahan.
Karena 8086 tidak memiliki instruksi perkalian untuk operasi sederhana yang kita kendalikan penuh, kita bisa membuat prosedur perkalian menggunakan penjumlahan berulang: a × b = a + a + ... + a (b kali).
Assembly 8086
; Prosedur: KALI; Input: AX = pengali (a), BX = pengali (b); Output: AX = a × b; Catatan: hanya untuk bilangan positif kecilKALIPROCNEARPUSHCXPUSHDXMOVCX, BX; CX = b (banyaknya penjumlahan)MOVDX, 0; DX = akumulator hasilJCXZkali_selesai; jika b = 0, hasil = 0kali_ulang:ADDDX, AX; DX += aLOOPkali_ulangkali_selesai:MOVAX, DX; AX = hasilPOPDXPOPCXRETKALIENDP; === Pemanggilan ===MOVAX, 7; a = 7MOVBX, 6; b = 6CALLKALI; AX = 42
Kode 10.6: Perkalian dengan repeated addition — mendemonstrasikan parameter via register (AX, BX) dan return value via register (AX).
10.16 Contoh Program: Rekursi — Faktorial
Rekursi adalah prosedur yang memanggil dirinya sendiri. Stack sangat penting di sini karena setiap pemanggilan rekursif membuat stack frame baru. Berikut perhitungan faktorial: n! = n × (n−1)!.
Assembly 8086 — Faktorial Rekursif
; Prosedur: FAKTORIAL; Input: AX = n (bilangan bulat ≥ 0); Output: AX = n!; Mengubah: BX, CX (disave/direstore)FAKTORIALPROCNEARPUSHBXPUSHCXCMPAX, 1; base case: n ≤ 1?JBEfakt_base; ya, 1! = 1 dan 0! = 1; Recursive case: n! = n × (n-1)!MOVCX, AX; CX = n (simpan untuk dikalikan nanti)DECAX; AX = n - 1PUSHCX; simpan n di stack (aman dari CALL)CALLFAKTORIAL; AX = (n-1)!POPCX; CX = n (ambil kembali)MULCX; DX:AX = (n-1)! × nJMPfakt_selesaifakt_base:MOVAX, 1; 0! = 1! = 1fakt_selesai:POPCXPOPBXRETFAKTORIALENDP; === Pemanggilan: hitung 5! ===MOVAX, 5; n = 5CALLFAKTORIAL; AX = 120
Kode 10.7: Faktorial rekursif — setiap CALL FAKTORIAL menambah stack frame baru ke stack.
Trace Rekursi: FAKTORIAL(5)
FAKTORIAL(5) → simpan CX=5, panggil FAKTORIAL(4)
FAKTORIAL(4) → simpan CX=4, panggil FAKTORIAL(3)
FAKTORIAL(3) → simpan CX=3, panggil FAKTORIAL(2)
FAKTORIAL(2) → simpan CX=2, panggil FAKTORIAL(1)
FAKTORIAL(1) → base case, return AX=1
FAKTORIAL(2) → AX = 1 × 2 = 2, return
FAKTORIAL(3) → AX = 2 × 3 = 6, return
FAKTORIAL(4) → AX = 6 × 4 = 24, return
FAKTORIAL(5) → AX = 24 × 5 = 120, return
Pada titik terdalam, stack berisi 5 stack frame (5× return address + 5× CX + 5× BX + 5× CX internal). Total sekitar 40 byte stack untuk n=5. Untuk n=10, stack frame 10× lebih banyak.
⚠ Bahaya Rekursi pada 8086: Stack 8086 sangat terbatas (default hanya 256 byte dengan .stack 100h). Rekursi dalam mendalam dapat menyebabkan stack overflow. Untuk faktorial, n > ~30 sudah berbahaya. Selalu alokasikan stack yang cukup besar untuk program rekursif.
10.17 Prosedur vs Makro
Meskipun keduanya digunakan untuk modularisasi kode, prosedur dan makro bekerja dengan cara yang sangat berbeda. Pemilihan yang tepat tergantung pada konteks penggunaan.
Aspek
Prosedur (CALL/RET)
Makro
Waktu ekspansi
Run-time — kode dipanggil saat eksekusi
Assemble-time — kode disalin oleh assembler
Salinan kode di memori
1 salinan — hemat memori
N salinan — setiap pemanggilan = 1 salinan baru
Overhead eksekusi
Ada — PUSH return addr, jump, POP, jump back
Tidak ada — kode inline, secepat kode biasa
Ukuran memori kode
Kecil (kode prosedur + 3 byte per CALL)
Besar (kode makro × jumlah pemanggilan)
Parameter
Via register, memori, atau stack
Via argumen makro (substitusi teks)
Rekursi
Mendukung (stack frame)
Tidak mendukung (akan ekspansi tak terbatas)
Debugging
Lebih mudah (satu lokasi kode)
Lebih sulit (kode tersebar di banyak tempat)
Direktif
PROC / ENDP
MACRO / ENDM
Contoh Deklarasi Makro
; Deklarasi makro: CETAK_CHARCETAK_CHARMACROkarakterPUSHAXPUSHDXMOVDL, karakterMOVAH, 02hINT21hPOPDXPOPAXENDM; Pemanggilan makro (diassembler menjadi kode inline)CETAK_CHAR'A'; assembler ganti dengan isi makro, DL='A'CETAK_CHAR'B'; assembler ganti dengan salinan lain, DL='B'CETAK_CHAR'C'; assembler ganti dengan salinan lain, DL='C'; Setelah assemble, hasilnya sama seperti menulis:; PUSH AX / PUSH DX / MOV DL,'A' / MOV AH,02h / INT 21h / POP DX / POP AX; PUSH AX / PUSH DX / MOV DL,'B' / MOV AH,02h / INT 21h / POP DX / POP AX; PUSH AX / PUSH DX / MOV DL,'C' / MOV AH,02h / INT 21h / POP DX / POP AX
Kode 10.8: Deklarasi dan pemanggilan makro. Perhatikan bahwa tidak ada CALL/RET — kode di-inline langsung.
Gunakan Prosedur Jika...
• Kode makro panjang (>10 instruksi)
• Dipanggil dari banyak tempat
• Butuh rekursi
• Memori kode terbatas
• Butuh modularitas sejati
Gunakan Makro Jika...
• Kode sangat pendek (1–5 instruksi)
• Dipanggil sedikit kali (1–3x)
• Performa sangat kritis
• Butuh fleksibilitas parameter tipe apapun
• Memori kode berlimpah
10.18 Soal Latihan
Kerjakan soal-soal berikut untuk menguji pemahaman Anda. Klik "Lihat jawaban" setelah mencoba menjawab sendiri.
Soal 1. Jelaskan perbedaan antara prosedur (near call) dan makro. Sebutkan masing-masing satu keuntungan dan satu kerugian.
▶ Lihat jawaban
Prosedur adalah blok kode yang ada satu kali di memori dan dipanggil via CALL/RET pada saat run-time. Keuntungan: menghemat memori kode. Kerugian: ada overhead CALL/RET (push return address, jump, pop, jump back).
Makro adalah template yang diekspansi oleh assembler pada saat assemble-time — setiap pemanggilan disalin ulang. Keuntungan: tidak ada overhead eksekusi (secepat kode inline). Kerugian: memori kode membengkak jika makro dipanggil berkali-kali dan berisi banyak instruksi.
Soal 2. Apa yang terjadi di dalam stack saat instruksi CALL prosedur dieksekusi? Jelaskan langkah demi langkah.
▶ Lihat jawaban
Saat CALL prosedur dieksekusi: (1) CPU menghitung alamat instruksi berikutnya (alamat return = IP + panjang instruksi CALL), (2) alamat return tersebut di-PUSH ke stack (SP dikurangi 2, lalu alamat disimpan di [SS:SP]), (3) IP diisi dengan alamat tujuan prosedur, sehingga eksekusi berpindah ke sana. Saat RET: (1) alamat return di-POP dari stack (nilai di [SS:SP] dipindah ke IP, SP ditambah 2), (2) eksekusi kembali ke instruksi setelah CALL.
Soal 3. Mengapa aturan "PUSH dan POP harus seimbang" sangat penting dalam prosedur? Apa yang terjadi jika melanggarnya?
▶ Lihat jawaban
Jika di dalam prosedur Anda melakukan PUSH tapi tidak POP (atau sebaliknya), nilai SP akan tidak kembali ke semula saat RET. Akibatnya, RET akan mengambil alamat return yang salah dari stack, dan program akan melompat ke alamat yang tidak terduga — biasanya menyebabkan crash atau perilaku tak terdefinisi. Aturan emas: setiap PUSH di dalam prosedur harus memiliki POP yang sesuai sebelum RET.
Soal 4. Bandingkan tiga metode penerusan parameter (via register, via memori, via stack). Kapan masing-masing paling tepat digunakan?
▶ Lihat jawaban
Via Register: Parameter disimpan di register (AX, BX, dll.). Paling cepat, kode paling ringkas. Cocok untuk parameter sedikit (1-4 parameter) dan tipe data sederhana.
Via Memori: Parameter disimpan di variabel global di segmen data. Mudah dipahami oleh pemula. Cocok untuk parameter banyak atau data kompleks (array, string). Kerugian: menggunakan memori global sehingga prosedur tidak sepenuhnya modular.
Via Stack: Parameter di-PUSH ke stack sebelum CALL, diakses via [BP+offset] di dalam prosedur. Paling fleksibel dan mendukung rekursi. Cocok untuk prosedur library yang benar-benar modular. Kerugian: kode lebih kompleks dan sedikit lebih lambat karena akses memori via BP.
Soal 5. Apa fungsi register BP (Base Pointer) dalam prosedur yang menerima parameter via stack? Mengapa tidak menggunakan SP langsung?
▶ Lihat jawaban
BP digunakan sebagai frame pointer tetap untuk mengakses parameter dan variabel lokal di stack. Alasan tidak menggunakan SP langsung: SP berubah-ubah selama eksekusi prosedur (setiap PUSH/POP mengubah SP), sehingga offset relatif terhadap SP tidak konsisten. BP diset sekali di awal prosedur (MOV BP, SP) dan tidak diubah lagi, sehingga parameter selalu bisa diakses pada offset yang tetap (misalnya [BP+4], [BP+6]).
Soal 6. Tulis prosedur Assembly 8086 bernama TITIK yang mencetak satu karakter titik (.) ke layar menggunakan INT 21h, lalu panggil prosedur tersebut 5 kali dari program utama untuk mencetak ".....".
▶ Lihat jawaban
Kode: TITIK PROC PUSH AX PUSH DX MOV DL, '.' MOV AH, 02h INT 21h POP DX POP AX RET TITIK ENDP
; Program utama: MOV CX, 5 cetak_lima: CALL TITIK LOOP cetak_lima
Setiap pemanggilan CALL TITIK akan mencetak satu titik. LOOP memanggilnya 5 kali, menghasilkan ".....".
10.19 Kuis Bab 10
Uji pemahaman Anda. Pilih satu jawaban yang paling tepat. Setelah memilih, jawaban benar akan ditandai dan penjelasan muncul.
1. Instruksi apa yang secara otomatis menyimpan alamat return ke stack?
CALL secara otomatis mendorong (push) alamat instruksi berikutnya ke stack sebelum melompat ke prosedur. JMP tidak menyimpan alamat return. RET mengambil alamat dari stack. PUSH mendorong nilai yang ditentukan programmer, bukan alamat return.
2. Setelah eksekusi PUSH AX, nilai SP akan...
Stack pada 8086 tumbuh ke arah alamat rendah (grows downward). PUSH mendekrement SP sebesar 2 (karena 8086 adalah 16-bit), lalu menyimpan data di alamat baru [SS:SP]. Jadi SP berkurang 2.
3. Apa perbedaan antara CALL dan CALL FAR?
CALL (near call) hanya menyimpan IP (2 byte) ke stack karena prosedur berada di segmen kode yang sama. CALL FAR menyimpan CS dan IP (4 byte) karena prosedur bisa berada di segmen kode yang berbeda.
4. Dalam prosedur yang menerima parameter via stack, mengapa BP digunakan alih-alih SP?
SP berubah setiap kali ada PUSH/POP di dalam prosedur, sehingga offset relatif terhadap SP tidak dapat diandalkan. BP diset sekali (MOV BP, SP) di awal prosedur dan dijaga tetap, sehingga parameter bisa diakses di offset yang konsisten.
5. Makro diekspansi pada saat kapan?
Makro adalah fitur assembler. Pada saat assemble-time, assembler mengganti setiap pemanggilan makro dengan salinan isi makro tersebut. Tidak ada overhead pada run-time karena ekspansi sudah terjadi sebelum program berjalan.
6. Berapa byte yang ditempati di stack oleh satu kali PUSH pada 8086?
8086 adalah prosesor 16-bit. Setiap operasi stack (PUSH/POP/CALL/RET) selalu beroperasi pada unit 16-bit (2 byte = 1 word), tidak peduli apakah yang di-push adalah byte atau word. SP selalu berubah ±2.
7. Jika SP = 1000h sebelum CALL, berapa nilai SP sesaat setelah CALL dieksekusi (near call)?
CALL (near) mendorong 2 byte alamat return ke stack. SP dikurangi 2: 1000h − 2 = 0FFEh. Alamat return disimpan di [SS:0FFEh] dan [SS:0FFFh].
8. Keuntungan utama menggunakan prosedur dibandingkan menulis ulang kode yang sama adalah...
Prosedur menyimpan satu salinan kode di memori yang bisa dipanggil berkali-kali, sehingga menghemat memori. Jika ada bug, perbaikan hanya dilakukan di satu tempat (maintainability). Kecepatan sebenarnya sedikit lebih lambat karena overhead CALL/RET.