Bahasa Mesin
Setiap jenis CPU (Central Processing Unit) atau dikenal juga sebagai mikroprosesor,
hanya dapat mengerti atau menterjemahkan setiap bahasa mesin yang
dimilikinya. Artinya setiap CPU telah dilengkapi perangkat intruksi
(instructions set) yang dimilikinya. Menyusun program dengan perangkat
instruksi pada CPU tertentu disebut dengan program bahasa mesin
(machine language). Instruksi-instruksi dalam bahasa mesin adalah
bilangan-bilangan yang merupakan kode mesin disimpan dalam memori.
Setiap instruksi akan memiliki kode numerik yang unik disebut sebagai
kode operasi (operation code) atau disingkat sebagai opcode. Instruksi
pada Prosesor 80x86 memiliki variasi ukuran. Opcode selalu berada
pada awal setiap instruksi. Beberapa instruksi juga diikuti dengan
data
(seperti konstanta atau alamat) digunakan oleh instruksi.
Bahasa
mesin sangat sulit diprogram secara langsung. Menterjemahkan
maksud/arti dari instruksi yang berupa kode numerik bagi manusia
sangat membosankan untuk dihapalkan atau dimengerti maksudnya. Sebagai
contoh, instruksi katakan untuk menambah isi dari register EAX dan
EBX dan hasilnya disimpan kembali dalam register EAX dikodekan oleh
kode heksa: 03 CE.
Program
bahasa mesin yang merupakan kumpulan kode operasi disimpan
dalammemori CPU untuk selanjutnya dieksekusi satu persatu sehingga
membentuk program secara utuh.
Bahasa Assembly
Tidak
seperti halnya bahasa mesin yang merupakan kumpulan kode operasi yang
berupa kode numerik yaitu kode heksa, yang sangat sulit untuk diingat
atau dimengerti. Penulisan program bahasa assembly disimpan sebagai
teks seperti halnya bahasa pemrograman aras tinggi. Setiap instruksi
assembly kenyataannya mewakili satu instruksi bahasa mesin. Sebagai
contoh, instruksi penjumlahan yang telah diuraikan sebelumnya akan
mewakili bahasa assembly berikut ini:
add eax, ebx
Instruksi ini akan lebih bisa dimengerti daripada kode mesin. Instruksi add adalah sebuah mnemonic untuk instruksi penjumlahan. Bentuk secara umum dari sebuah instruksi assembly adalah:
mnemonic operand(s)
Assembler
adalah sebuah program yang membaca file teks dari instruksi assembly
dan dikonversi menjadi kode mesin. Kompiler adalah program juga
melakukan pengkonversian namun untuk bahasa tingkat tinggi. Assembler
lebih sederhana daripada kompiler. Setiap perintah bahasa assembly
secara langsung mewakili satu instruksi mesin. Perintah bahasa tingkat
tinggi lebih komplek dan memerlukan banyak instruksi mesin. Perbedaan
penting lainnya antara assembly dan bahasa tingkat tinggi adalah,
bahwa setiap perbedaan jenis CPU akan memiliki instruksi mesin
tersendiri juga akan memiliki bahasa assembly tersendiri pula. Porting
(maksudnya: peng-adaptasi-an) program assembly ke berbagai jenis
arsitektur komputer sangatlah sulit jika dibandingkan dengan bahasa
pemrograman beraras tinggi (high level language). Dalam pembahasan
selanjutnya digunakan Netwide Assembler atau disingkat NASM. NASM
tersedia secara gratis di internet. Yang sering digunakan adalah
Microsoft's Assembler (MASM) atau Borland's Assembler (TASM). Terdapat
perbedaan sintak untuk MASM/TASM dan NASM.
Operand sebuah Instruksi
Instruksi
bahasa mesin memiliki sejumlah variasi dan jenis operand; akan
tetapi, secara umum, setiap instruksi itu sendiri akan memiliki
sejumlah operand yang telah ditetapkan (0 sampai 3).
Jenis-jenis operand yang ada adalah sebagai berikut:
- Register
Operand ini mengarah secara langsung ke isi dari register-register CPU. - Memori
Operand ini mengarah ke data dari isi memori. Alamat dari data dalam memori dapat berupa konstan yang dikodekan dalam instruksi atau dihitung melalui sebuah nilai dalam register-register. Alamat biasanya offset dari awal sebuah segmen. - Seketika (Immidiate)
Operand
ini merupakan nilai yang tetap yang diperlihatkan dalam instruksi itu
sendiri, disimpan dalam instruksi itu sendiri (dalam segmen kode),
tidak dalam segmen data.
- Implikasi (Implied)
Operand
ini tidak ditunjukkan secara ekplisit. Sebagai contoh, instruksi
menaikkan (increment) adalah menaikkan satu isi dari sebuah register
atau memori. Ini salah satu dari implikasi (implied).
Instruksi Dasar
Sebagian besar instruksi dasar adalah instruksi MOV.
Instruksi ini memindahkan data dari salah satu lokasi ke lokasi
lainnya, seperti operator assignment dalam bahasa pemrograman beraras
tinggi.
Terdiri dari dua operand:
mov dest, src
Data yang dispesifikasikan oleh src disalin ke dest.
Satu pembatasan adalah bahwa keduanya tidak diperbolehkan operand
memori, artinya tidak dapat memindahkan data dari memori ke memori.
Hal
ini ini ke luar ciri-ciri dari assembly. Sedikit banyak terdapat
aturan yang harus diikuti tentang bagaimana cara variasi instruksi
digunakan. Operand harus memiliki ukuran yang sama. Nilai register
AX tidak dapat disimpan dalam register BL. Ukuran dari register AX
adalah pasangan register sebesar 32bit, sedangkan BL ukurannya 16bit
untuk mikroprosesor 80486.
Sebagai contoh:
mov eax, 3 ; store 3 into EAX register (3 is immediate operand) mov bx, ax ; store the value of AX into the BX register
Tanda semikolon ';' merupakan tanda awal untuk penulisan komentar.
Instruksi ADD digunakan untuk operasi penjumlahan integer:
add eax, 4 ; eax = eax + 4
add al, ah ; al = al + ah
Instruksi SUB merupakan instruksi pembagian secara integer:
sub bx, 10 ; bx = bx - 10
sub ebx, edi ; ebx = ebx - edi
Instruksi
INC dan DEC adalah instruksi penaikan (increment) atau penurunan
(decrement) dengan nilai satu. Ini adalah salah satu dari operan
secara implisit, kode mesin untuk INC dan DEC adalah kecil ukurannya
dibanding dengan instruksi ADD dan SUB.
inc ecx ; ecx++ dec dl ; dl--
Pengarahan (Directive)
Directive
merupakan sebuah artifak assembler bukan CPU. Secara umum digunakan
untuk menginstruksikan assembler untuk melakukan sesuatu atau
menginformasikan tentang sesuatu ke assembler. Directive tidak diubah
kedalam kode mesin.
Penggunaan umum dari directive adalah:
- Mendefinisikan konstanta
- Mendefinisikan memori untuk penyimpanan data
- Kelompok memori kedalam segmen
- Melampirkan kode sumber (source code) secara kondisional (conditionally include)
- Melampirkan file lainnya
Pelolosan
kode NASM melalui sebuah perintah (command) preprosesor
(preprocessor) seperti C. Begitu pula, pengarahan preprosesor
(directive preprocessor) dimulai dengan % dibandingkan # dalam C.
Pengarahan EQU
Pengarahan
EQU dapat digunakan untuk mendefinisikan sebuah simbol. Simbol adalah
nama dari konstanta yang dapat digunakan dalam program assembly.
Formatnya adalah:
simbol equ nilai
Nilai dari simbol tidak dapat didefinisikan secara berulang (didefinisikan kembali).
Pengarahan %define
Pengarahan ini adalah sama dengan pengarahan #define dalam C. Ini secara umum digunakan untuk mendefinisikan mkro konstan seperti dalam C.
%define SIZE 100
mov eax, SIZE
mov eax, SIZE
Kode
di atas mendefinisikan sebuah makro dengan nama SIZE sebagai
konstanta 100 dan ditunjukkan penggunaannya dalam instruksi. Makro
sangat fleksibel daripada simbol dalam dua cara. Makro dapat
didefinisikan kembali dan bisa lebih dari sekedar bilangan konstan.
Pengarahan Data
Pengarahan
data digunakan dalam segmen data untuk mendefinisikan ruang memori.
Terdapat dua cara menyiapkan ruang memori. Cara pertama, hanya
mendefinisikan ruang untuk data; cara kedua mendefinisikan ruang dan
nilai awal. Metode pertama menggunakan satu dari pengarahan RESX.
X diganti dengan huruf sebagai ukuran dari obyek (atau beberapa
obyek) yang akan disimpan. Berikut ini ditunjukkan beberapa
kemungkinan nilai itu.
UNIT
|
HURUF
|
byte
|
B
|
word
|
W
|
double word
|
D
|
quad word
|
Q
|
ten bytes
|
T
|
Metode kedua
(mendefinisikan sebuah nilai awal) menggunakan satu dari pengarahan
DX. Huruf X adalah sama dengan pengarahan RESX. Hal ini sangat umum
untuk menandai lokasi memori dengan label. Label merupakan salah satu
cara mudah untuk mengarahkan lokasi memori dalam kode.
Berikut ini terdapat beberapa contoh:
LABEL
|
INSTRUKSI
|
KOMENTAR
|
L1
|
db 0
|
; byte dengan label L1 diinisialisasi dengan nilai 0
|
L2
|
dw 1000
|
; word dengan label L2 ddiinisialisasi dengan nilai 1000
|
L3
|
db 110101b
|
; byte diinisialisasi dengan biner 110101 (53 dalam desimal)
|
L4
|
db 12h
|
; byte diinisialisasi dengan heksa 12 (18 dalam desimal)
|
L5
|
db 17o
|
; byte diinisialisasi dengan oktal 17 (15 dalam desimal)
|
L6
|
dd 1A92h
|
; double word ddiinisialisasi dengan heksa 1A92
|
L7
|
resb 1
|
; menyiapkan ruang 1 byte tidak diinisialisasi
|
L8
|
db "A"
|
; byte diinisialisasi oleh kode ASCII untuk A (65)
|
Tanda "..." dan '...' adalah sama perlakuannya. Konsekwensinya data yang didefinisikan disimpan secara berurutan dalam memori. Dengan demikian L2 disimpan secara seketika sesuada L1 dalam memori. urutan dari memori dapat didefinisikan juga.
LABEL
|
INSTRUKSI
|
KOMENTAR
|
L9
|
db 0, 1, 2, 3
|
; mendefinisikan ruang 4 byte dinisialisasi dengan nilai 0, 1, 2, 3
|
L10
|
db "w", "o", "r", ’d’, 0
|
; mendefinisikan urut-urutan karakter dalam memori yaitu ASCII w, o, r, d dan diakhiri dengan konstanta 0
|
L11
|
db ’word’, 0
|
; mendefinisikan urut-urutan karakter dalam memori yaitu ASCII word dan diakhir dengan konstanta 0
|
Untuk urutan yang sangat besar, pengarahan TIMES dalam NASM seringkali digunakan. Pengarahan ini mengulang sejumlah operand spesifik, sebagai contoh:
LABEL
|
INSTRUKSI
|
KOMENTAR
|
L12
|
times 100 db 0
|
; sama dengan db 0 sebanyak 100 kali
|
L13
|
resw 100
|
; menyediakan ruang memori untuk 100 word
|
Ingat bahwa
label dapat digunakan untuk mengarahkan ke data dalam kode. Terdapat
dua cara bahwa sebuah label dapat digunakan. Jika label akan
digunakan, label akan diinterprestasikan sebagai alamat (atau offset)
dari data. Jika label diletakkan dalam kurung kotak ([...]), akan
diinterprestasikan sebagai data pada alamat yang ditunjukkan oleh
label. Dalam bentuk yang lain, salah satu yang harus diingat bahwa
sebuah label adalah sebagai pointer data pada alamat tertentu dan
kurung kotak direferensikan sebagai pointer seperti halnya araterisk
(*) dalam C. (MASM/TASM memiliki perbedaan konvensi). Dalam mode
32bit, pengalamatannya adalah 32bit.
Berikut ini terdapat beberapa contoh:
NO
|
INSTRUKSI
|
KOMENTAR
|
1
|
mov al, [L1]
|
; salin byte memori dengan alamat yang ditunjukkan oleh L1 ke register AL
|
2
|
mov eax, L1
|
; register EAX = alamat byte ditunjukkan oleh L1
|
3
|
mov [L1], ah
|
; salin AH ke byte memori dengan alamat oleh L1
|
4
|
mov eax, [L6]
|
; salin double word memori dengan alamat oleh L6 ke register EAX
|
5
|
add eax, [L6]
|
; EAX = EAX + double word memori dengan alamat oleh L6
|
6
|
add [L6], eax
|
; double word memori dengan alamat oleh L6 += EAX
|
7
|
mov al, [L6]
|
; salin byte pertama dari double word memori dengan alamat oleh L6 ke AL
|
Label yang
digunakan dalam contoh di atas merupakan label yang diberikan dalam
contoh sebelumnya. Baris 7 dari contoh di atas menunjukkan sebuah
properti yang penting untuk NASM. Assembler tidak dapat menangkap dari
jenis data yang ditunjukkan oleh label tersebut. Ini tergantung pada
programmer untuk membuat suatu kepastian bahwa penggunaan sebuah label
harus benar. Selanjutnya hal ini secara umum untuk menyimpan alamat
dari sebuah data dalam register dan menggunakan register seperti
halnya pointer dalam variabel C. Lagi, tidak ada pengecekan yang
dilakukan bahwa sebuah pointer digunakan secara benar. Cara ini,
assembly akan lebih banyak menghasilkan error daripada C.
Instruksi berikutnya adalah:
mov [L6], 1 ; simpan (store) 1 byte ke memori yang ditunjukkan oleh label L6
Pernyataan
tersebut menghasilkan sebuah operasi dengan ukuran yang tidak salah
(error), mengapa ?. Sebab assembler tidak mengetahui apa yang akan
disimpan (store) sebagai 1 byte, word atau double word. Untuk
memperbaiki ini, tambahkan sebuah spesifikasi ukuran:
mov dword [L6], 1 ; simpan (store) 1 byte ke memori yang ditunjukkan oleh label L6
Hal
ini akan mengatakan ke assembler untuk menyimpan 1 byte ke double
word yang dimulai dengan alamat yang ditunjukkan oleh label L6.
Spesifikasi ukuran lainnya adalah: BYTE, WORD, QWORD and TWORD.
Input and Output
Input
dan output merupakan aktifitas yang sangat bergantung dengan sistem.
Hal ini merupakan antarmuka denga perangkat keras sistem. Bahasa
tingkat tinggi, seperti C, dilengkapi dengan pustaka (library) standar
berupa rutin-rutin program yang dapat diperoleh secara sederhana,
programming membentuk antarmuka I/O. Bahasa assembly tidak memiliki
library standar. C harus secara langsung mengakses perangkat keras
(melalui operasi pengijinan dalam mode protect) atau menggunakan rutin
beraras rendah yang diperoleh dalam sistem operasi.
Sangat
umum rutin assembly di-antarmuka-kan dengan C. Salah satu
keuntungannya bahwa kode assembly dapat digunakan sebagai library
standar C untuk rutin I/O. Selanjutnya, salah satu yang harus
diketahui adalah aturan untuk meloloskan informasi rutin dengan
informasi yang digunakan C. Aturan ini sangat komplek untuk dijelaskan
disini (akan dijelaskan pada pembahasan berikutnya). Untuk
menyederhanakan I/O, digunakan rutin yang siap dipakai untuk
menyembunyikan aturan-aturan C yang komplek dan akan diperoleh cara
antarmuka yang sangat sederhana.
Berikut ini diuraikan fungsi setiap rutin yang dapat anda peroleh disini.
NAMA RUTIN
|
FUNGSI RUTIN
|
print_int
|
tampilkan ke layat nilai integer yang disimpan dalam register EAX
|
print_char
|
tampilkan ke layar karekter dalam bentuk nilai ASCII yang disimpan dalam register AL
|
print_string
|
tampilkan
ke layar berupa string dengan alamat yang disimpan dalam register
EAX. String harus berupa Ctype string (diakhiri dengan null).
|
print_nl
|
tampilkan ke layar karakter baris baru (new line)
|
read_int
|
baca sebuah nilai integer dari keyboard dan disimpan kedalam register EAX
|
read_char
|
baca karakter tunggal dari keyboard dan disimpan dalam bentuk kode ASCII kedalam register EAX.
|
Seluruh rutin
menyediakan nilai dari seluruh register, kecuali rutin read. Rutin
ini memodifikasi nilai yang ada dalam register EAX. Untuk menggunakan
rutin ini, harus menyertakan file rutin dalam program dengan
pengarahan preprosesor %include yang akan menginformasikan ke assemble mengenai penggunaan file rutin tersebut.
Berikut ini harus ditambahkan ke program assembly anda sebelum menggunakan rutin:
%include "asm_io.inc"
Untuk
menggunakan salah satu rutin print, terlebih dahulu muati register
EAX dengan nilai yang benar dan gunakan instruksi CALL untuk mengakses
rutin. Instruksi CALL adalah sama dengan sebuah pemanggilan fungsi
dalam bahasa pemrograman beraras tinggi. Selanjutnya akan melompat
mengeksekusi ke bagian kode yang lainnya, tetapi akan kembali ke
asalnya sesudah rutin selesai dieksekusi.
Sumber :
http://fpga.songolimo.net/kuliah-1/bahasa-assembly/pengantar-bahasa-assembly
0 komentar:
Posting Komentar