Inisialisasi Stack Pointer: Fondasi Memori Sebelum Masuk ke Bahasa C

Oleh: Reza Ervani bin Asmanu

Dalam kode bootloader (boot.S) yang kita tulis untuk Raspberry Pi, terdapat satu baris instruksi yang tampak sederhana namun memegang peran paling krusial dalam transisi dari Assembly ke C:

mov sp, #0x8000

Mengapa kita harus mengatur register sp (Stack Pointer)? Mengapa angka 0x8000 yang dipilih? Artikel ini akan membedah mekanisme memori “Tumpukan” (Stack) pada arsitektur ARM dan strategi tata letak memori yang kita gunakan.

1. Definisi dan Fungsi Stack

Dalam ilmu komputer, Stack adalah area memori khusus yang beroperasi dengan prinsip LIFO (Last In, First Out).

Berbeda dengan memori statis (tempat kita menyimpan variabel global) atau Heap (tempat malloc), Stack bersifat dinamis dan sementara. Bahasa tingkat tinggi seperti C mutlak membutuhkan Stack untuk tiga hal utama:

  1. Menyimpan Variabel Lokal: Variabel yang dideklarasikan di dalam fungsi (misal: int i = 0;) hidup di dalam Stack.
  2. Menyimpan Return Address: Saat fungsi A memanggil fungsi B, CPU harus mencatat alamat “jalan pulang” ke fungsi A. Catatan ini disimpan di Stack.
  3. Preservasi Register: Jika fungsi membutuhkan banyak register untuk berhitung, ia harus menyimpan nilai register lama ke Stack agar tidak menimpa data fungsi sebelumnya.

Tanpa Stack yang terinisialisasi, fungsi main() pada bahasa C tidak akan bisa beroperasi. CPU akan mengalami crash saat mencoba menyimpan variabel lokal pertama.

2. Mekanisme “Full Descending Stack” pada ARM

Salah satu keunikan arsitektur ARM (dan banyak arsitektur modern lainnya) adalah arah pertumbuhan Stack.

  • Kode Program & Data: Tumbuh ke atas (dari alamat rendah ke tinggi).
    • Contoh: Baris 1 di alamat 0x100, Baris 2 di 0x104, dst.
  • Stack: Tumbuh ke bawah (dari alamat tinggi ke rendah).
    • Contoh: Data pertama di 0x8000, data kedua di 0x7FFC, dst.

Ini disebut Descending Stack. Bayangkan seperti membangun gedung pencakar langit (Kode Program) yang tumbuh ke atas, dan menggali sumur (Stack) yang semakin dalam ke bawah.

3. Strategi Alamat 0x8000: Titik Pisah Memori

Mengapa kita memilih angka 0x8000? Keputusan ini didasarkan pada tata letak memori standar Raspberry Pi dan efisiensi pemisahan ruang.

Mari kita visualisasikan peta memori di sekitar alamat 0x8000:

    ALAMAT TINGGI (High Address)
^
|
+-----+------------------+
| ... | (Memori Bebas) |
| | |
| | KERNEL (Kode OS) | <-- Kernel tumbuh ke ATAS
| | (main.c, dll) | (0x8000, 0x8004, 0x8008...)
+-----+------------------+ <-- TITIK MULAI (0x8000)
| | STACK | (sp diset di sini)
| | (Variabel Lokal) | <-- Stack tumbuh ke BAWAH
| | | (0x7FFC, 0x7FF8, 0x7FF4...)
+-----+------------------+
| ... | |
| | (Exception Table)|
+-----+------------------+
|
ALAMAT RENDAH (Low Address)

Logika “Punggung-punggungan”

Dengan mengatur sp tepat di 0x8000:

  1. Kernel (Kernel Image): Dimuat oleh GPU mulai dari 0x8000 dan menempati alamat 0x8000 ke atas (misal: 0x8000 s.d 0x8100).
  2. Stack: Kita inisialisasi pointer di 0x8000. Ketika ada data yang dimasukkan (push) ke stack, pointer akan bergeser turun (dikurangi) terlebih dahulu.
    • Data pertama akan masuk di 0x7FFC (4 byte di bawah 0x8000).
    • Data kedua di 0x7FF8.

Keuntungan:

Dengan strategi ini, Kernel dan Stack bergerak menjauh satu sama lain. Kita tidak perlu menghitung secara manual berapa ukuran Kernel kita agar tidak tertabrak oleh Stack. Selama Stack tidak tumbuh terlalu besar hingga mencapai alamat 0x0000 (Stack Overflow), sistem kita aman.

4. Bedah Instruksi

mov sp, #0x8000

Mari kita terjemahkan instruksi ini ke dalam bahasa manusia yang formal:

  1. mov (Move): Instruksi transfer data. Salin nilai di sebelah kanan (source) ke lokasi di sebelah kiri (destination).
  2. #0x8000 (Immediate Value): Nilai konstan heksadesimal 8000.
  3. sp (Stack Pointer / R13): Register R13 pada ARM yang didedikasikan untuk menyimpan alamat puncak tumpukan.

Interpretasi Sistem:

“Wahai CPU, sebelum engkau menjalankan kode C yang rumit, tetapkanlah titik awal tumpukanmu di alamat memori 0x8000. Jika nanti kode C meminta ruang penyimpanan sementara, mulailah mengalokasikannya dari alamat di bawah 0x8000.”

Kesimpulan

Baris mov sp, #0x8000 adalah batas demarkasi. Sebelum baris ini, kita berada di dunia yang tidak stabil tanpa memori sementara. Setelah baris ini, lingkungan runtime C tercipta, memungkinkan kita memanggil fungsi, membuat variabel, dan menjalankan logika sistem operasi yang kompleks dengan aman.