Topik ini membahas MOV dari sisi operannya, yaitu dari mana data diambil dan ke mana data ditulis. Ketiga bentuk operan yang diterima MOV diperkenalkan satu per satu, kemudian digabungkan dalam satu program kecil yang diperiksa lewat gdb. Batasan ukuran operan, larangan pemindahan memori ke memori, dan perlakuan khusus immediate 64-bit dibuktikan dengan keluaran asli assembler dan debugger.
Tiga bentuk operan#
MOV menyalin data dari satu tempat ke tempat lain tanpa mengubah nilainya, dan inilah instruksi yang paling sering muncul dalam kode assembly. Bentuk umumnya adalah mov tujuan, sumber menurut sintaks Intel yang digunakan NASM, sehingga arah perpindahan dibaca dari kiri ke kanan. Tujuan dan sumber hanya boleh berasal dari tiga jenis tempat, dan setiap instruksi MOV paling banyak memuat satu operan memori.
Immediate adalah konstanta yang tertanam langsung di dalam kode instruksi, misalnya angka 42 pada mov rax, 42. Operan register menyalin data antar register, misalnya mov rbx, rax, dan tidak menyentuh memori sama sekali. Operan memori ditulis dalam tanda kurung siku seperti mov [hasil], rbx, dan alamatnya dihitung pada saat instruksi dijalankan.
| Bentuk | Contoh | Perpindahan |
|---|---|---|
| immediate | mov rax, 42 | konstanta ke register |
| register | mov rbx, rax | register ke register |
| memori | mov [hasil], rbx | register ke memori |
Ketiga bentuk dapat dicampur dengan bebas selama aturan satu memori dipatuhi. Konstanta dapat ditulis ke register maupun ke memori, register dapat disalin ke register lain maupun ke memori, tetapi memori hanya boleh menjadi tujuan atau sumber tunggal pada satu instruksi. Dengan demikian, mov [a], 5 dan mov [a], rbx adalah bentuk yang sah, sedangkan bentuk yang menyebut dua alamat memori sekaligus tidak pernah ada pada MOV.
Ketiga bentuk dalam satu program#
Program berikut memindahkan satu nilai melalui ketiga bentuk operan secara berurutan. Nilai 42 dimuat dari immediate ke rax, disalin ke rbx, lalu disimpan ke label hasil pada section .data. gdb menghentikan program setelah ketiga MOV selesai dan membaca isi memori pada alamat hasil.
default rel
section .data
hasil: dq 0
section .text
global _start
_start:
mov rax, 42 ; immediate ke register
mov rbx, rax ; register ke register
mov [hasil], rbx ; register ke memori
mov rdi, rbx
mov eax, 60
syscall# rakit dan tautkan program
nasm -f elf64 operan-mov.asm -o operan-mov.o
ld operan-mov.o -o operan-mov
# isi memori pada label hasil setelah ketiga MOV dijalankan
gdb -batch -ex 'break _start' -ex 'run' -ex 'stepi 3' -ex 'x/1gx &hasil' ./operan-movBreakpoint 1 at 0x401000
Breakpoint 1, 0x0000000000401000 in _start ()
0x000000000040100f in _start ()
0x402000: 0x000000000000002aKeluaran menampilkan satu baris berupa alamat 0x402000 beserta isinya, yaitu 0x2a atau 42 dalam heksadesimal. Alamat tersebut tetap sama karena executable statik yang dihasilkan ld menempatkan section .data pada posisi yang sama setiap kali program dijalankan. Penanda x/1gx membaca 8 byte sekaligus, sehingga seluruh isi hasil terlihat dalam satu baris keluaran. Tiga perpindahan tersebut melibatkan dua register dan satu slot memori tanpa mengubah nilai di sepanjang jalurnya. Dua instruksi terakhir pada program hanya menyiapkan kode keluar, sehingga perhatian tetap tertuju pada tiga MOV pertama.
Larangan memori ke memori#
MOV tidak menyediakan bentuk yang menyalin langsung dari memori ke memori. Batasan ini berasal dari encoding x86, yang hanya menyediakan tempat bagi satu operan memori pada satu instruksi MOV, sehingga assembler apa pun akan menolak permintaan yang sama. Permintaan yang melanggar aturan itu ditolak NASM dengan pesan error, sebagaimana ditunjukkan rangkaian perintah berikut.
# NASM menolak MOV dengan dua operan memori
cat > dua-memori.asm <<'EOF'
section .data
a: dq 1
b: dq 2
section .text
global _start
_start:
mov [a], [b]
EOF
nasm -f elf64 dua-memori.asm -o dua-memori.o
echo "exit=$?"dua-memori.asm:9: error: invalid combination of opcode and operands
exit=1Pesan error menyebut baris 9 sebagai lokasinya dan kombinasi opcode serta operan yang tidak sah sebagai penyebabnya, sedangkan kode keluar 1 menandakan perakitan gagal total. Tidak ada berkas objek yang dihasilkan, sehingga langkah penautan tidak mungkin dilanjutkan. Jalur yang benar menyalin lewat register sebagai perantara, sebagaimana pola berikut.
mov rax, [sumber]
mov [tujuan], raxBiaya tambahannya adalah satu instruksi, dan pola inilah yang digunakan di mana saja ketika data harus berpindah antar alamat memori. Ukuran salinannya mengikuti nama register perantara, sehingga penyalinan 8 byte menggunakan rax seperti pada pola di atas. Urutan muat dari memori, mengolah, lalu menyimpan kembali juga menjadi kerangka dasar hampir semua operasi memori pada x86.
Ukuran operan#
Ukuran perpindahan mengikuti nama register yang digunakan. Menulis ke rax berarti memindahkan 8 byte, ke eax 4 byte, ke ax 2 byte, dan ke al 1 byte. Apabila tidak ada register yang menyatakan ukuran, misalnya ketika konstanta ditulis langsung ke memori, ukurannya harus dinyatakan secara eksplisit dengan penanda ukuran.
mov byte [hasil], 5 ; 1 byte
mov word [hasil], 5 ; 2 byte
mov dword [hasil], 5 ; 4 byte
mov qword [hasil], 5 ; 8 byteTanpa penanda, NASM menolak instruksi itu dengan pesan operation size not specified, karena tanda kurung siku saja tidak menyatakan berapa byte yang ditulis. Penanda ukuran juga berguna ketika operan memori berpasangan dengan register, misalnya untuk membatasi penulisan ke satu byte dari alamat yang sama. Penanda yang dipilih harus sesuai maksud program, karena menulis byte pada slot 8 byte yang dituju meninggalkan tujuh byte tidak tersentuh.
Immediate 64-bit dan movabs#
Immediate pada kebanyakan encoding MOV dibatasi hingga 32 bit. Pada register 64 bit, nilai 32 bit itu diperluas tanda sehingga nilai kecil maupun negatif tetap didukung, sedangkan nilai yang tidak muat dalam 32 bit memerlukan encoding khusus bernama movabs. NASM memilih encoding itu secara otomatis setiap kali immediate melebihi 32 bit, sehingga kode sumber tetap menulis MOV seperti biasa. Nilai yang muat dalam 32 bit, termasuk nilai negatif seperti -1, cukup dikodekan pendek karena diperluas tanda pada saat dijalankan.
Program berikut memuat konstanta 64-bit ke rax, lalu nilai akhirnya diperiksa lewat gdb. Perintah objdump di akhir memperlihatkan encoding yang benar-benar dipilih NASM.
default rel
section .text
global _start
_start:
mov rax, 0x1122334455667788 ; immediate 64-bit, dikodekan sebagai movabs
mov edi, 0
mov eax, 60
syscall# rakit dan tautkan program
nasm -f elf64 immediate-64.asm -o immediate-64.o
ld immediate-64.o -o immediate-64
# nilai penuh rax setelah MOV immediate 64-bit
gdb -batch -ex 'break _start' -ex 'run' -ex 'stepi' -ex 'info registers rax' ./immediate-64
# bentuk encoding yang dipilih NASM dapat dilihat lewat objdump
objdump -d -M intel immediate-64.oBreakpoint 1 at 0x401000
Breakpoint 1, 0x0000000000401000 in _start ()
0x000000000040100a in _start ()
rax 0x1122334455667788 1234605616436508552
immediate-64.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_start>:
0: 48 b8 88 77 66 55 44 movabs rax,0x1122334455667788
7: 33 22 11
a: bf 00 00 00 00 mov edi,0x0
f: b8 3c 00 00 00 mov eax,0x3c
14: 0f 05 syscallNilai rax terbaca 0x1122334455667788 utuh beserta representasi desimalnya. objdump menunjukkan instruksi pertama dikodekan sebagai movabs dengan panjang 10 byte, karena konstanta 8 byte tertanam utuh di dalam instruksi tersebut, sedangkan mov eax, 0x3c pada baris berikutnya hanya membutuhkan 5 byte. Konstanta sebesar ini mahal pada ukuran kode, sehingga compiler menghindarinya bila nilainya masih dapat dipecah ke instruksi yang lebih pendek.
Kesalahan umum#
Lupa penanda ukuran saat menulis konstanta ke memori. Instruksi mov [hasil], 5 ditolak NASM dengan pesan operation size not specified, karena tanda kurung siku tidak menyatakan berapa byte yang dimaksud. Tambahkan penanda ukuran seperti pada mov qword [hasil], 5, dan instruksi yang sama langsung dirakit.
Membalik arah operan. Sintaks Intel menempatkan tujuan di kiri, sehingga mov rax, rbx menyalin rbx ke rax dan bukan sebaliknya. Kesalahan ini lazim pada pembaca yang baru pindah dari sintaks AT&T yang menempatkan sumber di kiri, dan gejalanya adalah dua register yang nilainya tertukar saat debug.
Memercayai immediate 64-bit yang ditulis langsung ke memori. Perakitan berhasil karena NASM hanya memberi warning, tetapi nilainya telah dipotong menjadi 32 bit sebelum disimpan. Selalu muat konstanta 64-bit ke register terlebih dahulu, lalu simpan register itu ke tujuan memori.
Ringkasan#
- MOV menerima tiga bentuk operan, yaitu immediate, register, dan memori, dengan tujuan selalu di sebelah kiri menurut sintaks Intel.
- Perpindahan memori ke memori tidak disediakan dan harus dipecah menjadi dua MOV melalui register perantara.
- Ukuran perpindahan mengikuti nama register, atau dinyatakan dengan byte, word, dword, dan qword ketika operan memori berdiri sendiri.
- Immediate 64-bit memerlukan encoding movabs berukuran 10 byte yang dipilih NASM secara otomatis.
Referensi#
- NASM: The Netwide Assembler manual versi 2.16.03 — NASM — bab instruksi memuat sintaks MOV, penanda ukuran, dan perlakuan immediate.
- MOV — x86 and x86-64 Instruction Set Reference — Felix Cloutier — rincian setiap bentuk MOV beserta batasan operannya.
- Intel 64 and IA-32 Architectures Software Developer’s Manual — Intel — Volume 2 mendokumentasikan encoding MOV, termasuk bentuk movabs.