Data yang digunakan program harus dideklarasikan terlebih dahulu sebelum dapat dibaca lewat mode pengalamatan. Topik ini membahas direktif definisi data NASM untuk setiap ukuran nilai, cara memesan ruang kosong di section .data maupun .bss, dan konstanta equ yang hanya berlaku pada saat perakitan. Seluruh mekanismenya dibuktikan dengan satu program yang mencetak nilai setiap jenis data, ditambah pengukuran ukuran berkas objek yang memperlihatkan perbedaan ruang berkas antara .data dan .bss.
Direktif definisi data#
Empat direktif dasar menyimpan nilai yang sudah ditentukan sejak perakitan. Direktif db menulis satu byte, dw dua byte, dd empat byte, dan dq delapan byte, sesuai singkatan define byte, define word, define doubleword, dan define quadword. Beberapa nilai dapat ditulis berurutan dalam satu baris dengan pemisah koma, sehingga satu direktif dq dapat membuat sebuah array sekaligus.
| Direktif | Ukuran | Contoh penulisan |
|---|---|---|
db | 1 byte | suhu db 28 |
dw | 2 byte | tegangan dw 220 |
dd | 4 byte | detak dd 2400000000 |
dq | 8 byte | waktu dq 3600000000000 |
String juga dideklarasikan dengan db, karena string pada dasarnya adalah deretan byte berisi kode ASCII. Tanda kutip ganda atau tunggal sama-sama diterima oleh NASM, dan byte 10 di akhir menambahkan newline agar tulisannya berpindah baris saat dicetak. Berbeda dengan bahasa C, NASM tidak menambahkan byte penutup nol secara otomatis, sehingga panjang string menjadi tanggung jawab penulis program. Kebiasaan yang menyelamatkan banyak program adalah menaruh direktif times untuk ruang bernilai nol, misalnya times 8 db 0 yang menuliskan byte 0 sebanyak delapan kali berurutan.
Konstanta equ dan panjang terhitung#
Direktif equ mendefinisikan konstanta yang hanya digunakan pada saat perakitan dan tidak menempati satu byte memori pun. Konstanta itu dapat digunakan sebagai nilai langsung pada instruksi, sebagai operand mov, atau dalam perhitungan konstanta lain.
; tidak-dijalankan
; konstanta equ hanya hidup saat perakitan
JUMLAH_ELEMEN equ 8
UKURAN_ELEMEN equ 8
TOTAL_BYTE equ JUMLAH_ELEMEN * UKURAN_ELEMEN ; 64
mov rdi, TOTAL_BYTE ; dipakai sebagai nilai langsungPola paling sering digunakan bersama equ adalah perhitungan panjang data dengan simbol $, yaitu penanda posisi assembler saat ini. Ekspresi PANJANG equ $ - pesan yang ditulis tepat setelah definisi pesan menghasilkan selisih posisi terhadap awal data tersebut, sehingga panjang string terhitung otomatis setiap kali isinya diubah. Pola ini menghapus satu sumber error yang lazim, yaitu panjang yang diketik secara manual dan tidak ikut berubah ketika string diperbaiki.
Program contoh#
Program berikut mendeklarasikan satu data untuk setiap ukuran, lalu mencetak nilai satu per satu beserta panjang pesan hasil perhitungan $ - label. Setiap pemuatan nilai menggunakan register seukuran datanya, sehingga jumlah byte yang dibaca sesuai dengan direktif yang mendeklarasikannya.
; data.asm, definisi data dengan db, dw, dd, dq, times, resb, dan equ
default rel
BATAS_ATAS equ 100 ; konstanta waktu rakit, tidak menempati memori
section .data
pesan db "Sensor suhu terbaca", 10
PANJANG equ $ - pesan ; 20 byte, terhitung otomatis
suhu db 28
tegangan dw 220
detak dd 2400000000
waktu dq 3600000000000
nol times 8 db 0
label1 db "panjang pesan = "
len1 equ $ - label1
label2 db "suhu (db) = "
len2 equ $ - label2
label3 db "tegangan (dw) = "
len3 equ $ - label3
label4 db "detak (dd) = "
len4 equ $ - label4
label5 db "waktu (dq) = "
len5 equ $ - label5
section .bss
buffer resb 64
hitungan resd 1
digit resb 25
section .text
global _start
_start:
lea rsi, [pesan]
mov rdx, PANJANG
call cetak_teks
lea rsi, [label1]
mov rdx, len1
mov rax, PANJANG
call cetak_baris
lea rsi, [label2]
mov rdx, len2
movzx rax, byte [suhu] ; muat 1 byte sesuai ukuran db
call cetak_baris
lea rsi, [label3]
mov rdx, len3
movzx rax, word [tegangan] ; muat 2 byte sesuai ukuran dw
call cetak_baris
lea rsi, [label4]
mov rdx, len4
mov eax, [detak] ; muat 4 byte, bagian atas rax otomatis nol
call cetak_baris
lea rsi, [label5]
mov rdx, len5
mov rax, [waktu] ; muat 8 byte sesuai ukuran dq
call cetak_baris
mov rdi, BATAS_ATAS ; konstanta equ dipakai sebagai immediate
mov rax, 60
syscall
cetak_baris: ; rsi = alamat label, rdx = panjang label, rax = nilai
push rax
call cetak_teks
pop rax
call cetak_angka
ret
cetak_teks: ; tulis rsi sepanjang rdx byte ke stdout
mov rax, 1
mov rdi, 1
syscall
ret
cetak_angka: ; tulis rax sebagai desimal tanpa tanda diikuti baris baru
mov rbx, 10 ; pembagi
lea r12, [digit + 24]
mov byte [r12], 10 ; byte newline di ujung buffer
lea r12, [digit + 24]
.isi:
xor edx, edx
div rbx
add dl, 48 ; 48 adalah kode ASCII angka 0
dec r12
mov [r12], dl
test rax, rax
jnz .isi
lea rdx, [digit + 25]
sub rdx, r12 ; panjang digit ditambah newline
mov rsi, r12
mov rax, 1
mov rdi, 1
syscall
ret
section .note.GNU-stack noalloc noexec nowrite progbits# rakit, tautkan, lalu jalankan program
nasm -f elf64 data.asm -o data.o
ld data.o -o data
./data
echo "exit=$?"Sensor suhu terbaca
panjang pesan = 20
suhu (db) = 28
tegangan (dw) = 220
detak (dd) = 2400000000
waktu (dq) = 3600000000000
exit=100Keluaran memperlihatkan setiap nilai tercetak utuh sesuai ukurannya. Instruksi movzx rax, byte [suhu] memuat satu byte lalu mengisi sisa register dengan nol, sedangkan mov eax, [detak] memuat empat byte dan otomatis mengosongkan 32 bit atas rax. Kode keluar 100 pada baris terakhir berasal dari mov rdi, BATAS_ATAS, yaitu konstanta equ yang digunakan sebagai nilai langsung, sehingga program itu sekaligus membuktikan bahwa konstanta perakitan dapat mengisi instruksi tanpa pernah berada di memori.
Pemesanan ruang dan perbedaan .data dengan .bss#
Section .data menyimpan byte yang benar-benar ditulis ke berkas, sedangkan section .bss hanya menyatakan ukuran tanpa isi. Direktif pemesanan di .bss adalah resb, resw, resd, dan resq untuk 1, 2, 4, dan 8 byte, sesuai singkatan reserve byte sampai reserve quadword. Dua berkas objek berikut memesan 1000 byte pada tempat yang berbeda, dan perbedaannya langsung terlihat pada ukuran berkasnya.
; ruang-data.asm, seribu byte di section .data
section .data
nol: times 1000 db 0; ruang-bss.asm, seribu byte di section .bss
section .bss
nol: resb 1000nasm -f elf64 ruang-data.asm -o ruang-data.o
nasm -f elf64 ruang-bss.asm -o ruang-bss.o
# bandingkan ukuran kedua berkas objek
wc -c ruang-data.o ruang-bss.o1568 ruang-data.o
544 ruang-bss.o
2112 totalKedua berkas memesan 1000 byte yang sama, tetapi ruang-data.o berukuran 1568 byte karena byte nol itu benar-benar tertulis di dalamnya, sedangkan ruang-bss.o hanya berukuran 544 byte karena .bss tidak menyimpan isi sama sekali. Ukuran .bss hanya berupa catatan yang dibaca loader saat program dijalankan, sehingga 1000 byte itu baru benar-benar dialokasikan di memori tanpa pernah membebani berkas. Pada program utama data, perintah size merangkum ketiga section tersebut.
# periksa ukuran section pada program utama
size data text data bss dec hex filename
244 113 100 457 1c9 dataKolom text berisi kode instruksi berukuran 244 byte, kolom data berisi 113 byte dari 43 byte data utama ditambah 70 byte string label, dan kolom bss berisi 100 byte dari 93 byte yang dipesan ditambah pembulatan penyelarasan yang dilakukan linker. Kolom dec dan hex hanyalah jumlah ketiganya dalam desimal dan heksadesimal. Perbandingan ini sekaligus menunjukkan bahwa memindahkan data besar dari .data ke .bss memangkas ukuran berkas tanpa mengubah perilaku program.
Kesalahan umum#
Menaruh resb di section .data. Direktif pemesanan memang dirakit, tetapi NASM memberi warning dan mengisinya dengan nol di dalam berkas, sehingga tujuan hemat ruang .bss tidak tercapai. Perbaikannya adalah memindahkan deklarasi itu ke section .bss, sebagaimana dibuktikan perintah berikut.
# resb di .data memunculkan warning, bukan error
cat > salah-tempat.asm <<'EOF'
section .data
isi: resb 8
EOF
nasm -f elf64 salah-tempat.asm -o salah-tempat.o
echo "exit=$?"salah-tempat.asm:2: warning: uninitialized space declared in non-BSS section `.data': zeroing [-w+zeroing]
exit=0Mengira string otomatis diberi penutup. NASM menyimpan byte persis seperti yang ditulis, sehingga db "AB" hanya menghasilkan dua byte tanpa nol penutup. Penulisan ke terminal yang menggunakan panjang lebih besar dari isi sebenarnya akan menyertakan byte acak yang bersebelahan. Selalu hitung panjangnya dengan pola NAMA equ $ - label yang diletakkan tepat setelah data yang diukur.
Memperlakukan equ seperti variabel. Konstanta equ tidak menempati memori dan tidak dapat diubah saat program berjalan, karena nilainya disubstitusi oleh assembler. Data yang nilainya berubah selama eksekusi harus dideklarasikan di .data dengan nilai awal atau di .bss sebagai ruang kosong.
Ringkasan#
- Direktif
db,dw,dd, dandqmenyimpan data 1, 2, 4, dan 8 byte sekaligus menjadi sarana penulisan string, sedangkantimes N db 0mengulang satu nilai berkali-kali. - Section
.bssmemesan ruang denganresb,resw,resd, danresqtanpa menambah ukuran berkas, karena isinya hanya catatan ukuran, sedangkan ruang yang dipesan diisi nol oleh kernel saat program dimuat. - Konstanta
equhanya berlaku pada saat perakitan, digunakan sebagai nilai langsung, dan bersama$ - labelmenghitung panjang data secara otomatis. - Perintah
sizemengelompokkan isi program ke dalam kolomtext,data, danbsssehingga dampak penempatan data terhadap ukuran berkas terlihat langsung.
Referensi#
- NASM 2.16.03 documentation — NASM Project
— bab direktif memuat
db,dw,dd,dq,times,resb,equ, serta perilaku section.datadan.bss. - size(1) — Linux man-pages
— penjelasan kolom
text,data, danbssyang ditampilkan perintahsize.