Menjadikan NASM bahasa yang produktif: %define dan %assign untuk konstanta, %macro/%endmacro untuk instruksi baru dengan parameter, %if/%ifdef untuk kompilasi bersyarat (misal dukungan multi-platform), struc/istruc untuk layout struct yang rapi, dan %include untuk modularisasi file assembly.

Sejauh ini kalian menulis assembly dengan gaya "flat": setiap instruksi ditulis ulang, setiap konstanta muncul mentah-mentah. Episode 13 menunjukkan bahwa NASM bukan sekadar assembler — ia punya preprocessor yang membuat kalian bisa menulis kode yang terstruktur, dapat dipakai ulang, dan bahkan bersyarat.
Mengapa penting? Karena di dunia nyata, file assembly sering menjadi bagian dari build system yang kompleks: kode yang sama harus bisa dikompilasi untuk Linux dan Windows, atau dengan fitur yang berbeda-beda. %if dan macro memungkinkan itu. Dan saat kalian membaca kode proyek open source seperti kernel, kalian akan menemukan pola %macro dan struc ini di mana-mana.
%define mirip #define di C — substitusi tekstual. %assign menghitung nilai numerik sekali (tidak bisa di-redefine):
%define SYS_WRITE 1
%define SYS_EXIT 60
%define FD_STDOUT 1
%assign BUFFER_SIZE 4096
%assign LINES 3 * BUFFER_SIZE ; dihitung saat assemblePenting: %define bersifat tekstual — %define SCALE 2*4 akan mengganti teks 2*4, bukan 8. Jika ingin nilai numerik, pakai %assign (atau equ).
Note
Bedakan tiga mekanisme: equ adalah konstanta waktu-assembly untuk ekspresi (misal len equ $-msg), %define adalah substitusi teks preprocessor, dan %assign adalah perhitungan numerik preprocessor. Pilih sesuai kebutuhan — jangan mencampurnya tanpa alasan.
Macro memungkinkan kalian mendefinisikan instruksi baru yang bisa memuat banyak instruksi. Parameternya direferensikan sebagai %1, %2, dst, dan jumlah parameter bisa bervariasi dengan %0:
; Macro: syscall 3-argumen yang umum
%macro syscall3 4
mov rax, %1
mov rdi, %2
mov rsi, %3
mov rdx, %4
syscall
%endmacro
; Macro: cetak string di .data
%macro print_str 1
mov rax, 1
mov rdi, 1
mov rsi, %1
mov rdx, %1_len
syscall
%endmacro
section .data
msg db "Halo macro!", 0x0a
msg_len equ $-msg
section .text
global _start
_start:
print_str msg ; mencetak msg
syscall3 60, 0, 0, 0 ; exit(0)Perhatikan syscall3 4 — angka 4 setelah nama macro adalah jumlah parameter yang wajib. print_str di atas mengasumsikan label %1_len ada; pola ini umum di codebase NASM.
Macro yang berisi jmp/loop butuh label unik per pemanggilan. NASM menyediakan %%label — label lokal per-expansi macro:
%macro repeat_call 2 ; %1 = counter, %2 = target
mov rcx, %1
%%loop:
call %2
loop %%loop
%endmacroSetiap kali repeat_call dipanggil, %%loop menjadi label yang berbeda — tanpa ini, label ganda akan membuat NASM error.
Preprocessor NASM mendukung cabang bersyarat — kode hanya di-assemble jika kondisi terpenuhi:
%define TARGET_LINUX
%ifdef TARGET_LINUX
%define SYS_EXIT 60
%elifdef TARGET_WINDOWS
%define SYS_EXIT 60
%else
%define SYS_EXIT 1
%endif
%if BUFFER_SIZE > 4096
%assign BUF_STR 1
%else
%assign BUF_STR 0
%endifDirektif yang tersedia: %ifdef, %ifndef, %if, %elif, %elifdef, %else, %endif. Kondisi di %if memakai ekspresi aljabar biasa (==, >, &, dst). Ini cara standar untuk multi-platform build.
Tip
Pola produksi yang umum: compile-time flag untuk mengendalikan fitur. Misal nasm -f elf64 -DTARGET_LINUX -o file.o file.asm — flag -D di command line setara %define. Ini memungkinkan satu file sumber untuk banyak target tanpa mengedit kode.
Di episode 11 kita menghitung offset struct secara manual. NASM menyediakan struc/endstruc untuk mendefinisikan layout dengan nama field:
struc Person
.age resd 1 ; offset 0 (int)
.pad1 resd 1 ; offset 4 (padding 4 byte)
.id resq 1 ; offset 8 (long, aligned 8)
.name resb 32 ; offset 16 (char[32])
endstruc
section .data
person1 istruc Person
at Person.age, dd 25
at Person.id, dq 1001
at Person.name, db "Alice", 0
iend
section .text
; person1.age = 30
mov dword [person1 + Person.age], 30
; memuat person1.id
mov rax, [person1 + Person.id]istruc...iend mengisi struct dengan nilai awal; at Person.field memastikan nilai diletakkan di offset yang benar. Akses field jadi [base + Person.nama_field] — jauh lebih terbaca daripada offset mentah.
%include menyisipkan isi file lain secara tekstual — cara NASM memecah kode besar:
; syscalls.inc
%define SYS_READ 0
%define SYS_WRITE 1
%define SYS_OPEN 2
%define SYS_EXIT 60%include "syscalls.inc"
section .text
global _start
_start:
mov rax, SYS_WRITE
...Pola umum di proyek nyata: satu file .inc berisi konstanta dan macro, satu file entry.asm berisi _start, dan file-file fungsi yang di-link bersama. Ini membuat codebase assembly tetap bisa dikelola saat tumbuh besar.
Warning
%include adalah penyertaan tekstual murni — file disisipkan apa adanya. Ini berarti: (1) urutan penyertaan penting, (2) setiap file yang di-%include di-assemble ulang setiap kali, dan (3) menyertakan file yang sama dua kali akan meng-define simbol dua kali (error). Gunakan %ifndef guard untuk mencegah double-include.
%define tekstual vs %assign numerik — konteks penggunaan menentukan hasil.%macro nama jumlah.%%label.%endif → NASM error "expected ENDIF" yang menjatuhkan seluruh compile.Person agar akses memori efisien.Pada episode 13 ini, kalian telah mengubah NASM menjadi bahasa yang produktif dan terstruktur.
Inti yang harus dibawa pulang:
%define (teks) vs %assign (numerik) vs equ — pilih sesuai kebutuhan.%macro/%endmacro membuat instruksi baru dengan parameter (%1, %2) dan label lokal %%label.%ifdef/%if/%elif/%else memungkinkan kompilasi bersyarat & multi-platform.struc/istruc memberi nama pada offset struct; %include memecah file besar.%ifndef mencegah double-include.Di episode 14 selanjutnya, kalian menjembatani dua dunia: inline assembly dalam C (GCC) — menulis kode assembly langsung di dalam fungsi C dengan __asm__, belajar constraint "r"/"m"/"=r" dan clobber, serta kapan kalian seharusnya (dan tidak seharusnya) memakai inline asm di produksi. Ini keterampilan yang dipakai kernel Linux dan library seperti glibc setiap hari.