Tugas ARSITEKTUR KOMPUTER
Dalam melengkapi
tugas penilaian pada mata kuliah Arsitektur Komputer, tentang penjabaran masing
masing bahasa pemrograman. Untuk pembahasan kali ini saya sedikit menjabarka
tentang element interuksi. Yaitu seperti yang ter tulis dibawah ini.
Dan juga sedikit tentang pembacaan AVR dengan Bahasa Asembler dan C
untuk Mikrokontroler
MULAI :
MOV TMOD, #001H
MOV TH0, #0D8H
MOV TL0, #0EFH
SETB TR0
PENJELASAN :
MULAI : Sebagai
Label
Element Instruksi
MOV
|
TMOD
|
#001H
|
OPCODE
|
OP1
|
OP2
|
Jenis Instruksi :
Pemindahan Data
Mode pengalamatan :
menggunakan 2 operand
Pada perogram
tersebut di atas berarti kita akan memindahkan nilai 001H kedalam alamat TMOD, TMOD merupakan perangkat
I/O dan 001H merupakan bilangan
heksadesimal.
Element Instruksi
MOV
|
TH0
|
#0D8H
|
OPCODE
|
OP1
|
OP2
|
Jenis Instruksi :
Pemindahan Data
Mode pengalamatan :
menggunakan 2 operand
Pada perogram
tersebut di atas berarti kita akan memindahkan nilai 0D8H kedalam alamat TH0, TH0 merupakan perangkat
I/O dan 0D8H merupakan bilangan
heksadesimal.
Element Instruksi
MOV
|
TL0
|
#0EFH
|
OPCODE
|
OP1
|
OP2
|
Jenis Instruksi :
Pemindahan Data
Mode pengalamatan :
menggunakan 2 operand
Pada perogram
tersebut di atas berarti kita akan memindahkan nilai 0EFH kedalam alamat TL0, TL0 merupakan perangkat
I/O dan 0EFH merupakan bilangan
heksadesimal.
Element Instruksi
SETB
|
TR0
|
OPCODE
|
OP1
|
Jenis Instruksi :
Instruksi Logika (kontrol)
Mode pengalamatan :
menggunakan 1 operand
Pada perogram
tersebut di atas berarti kita akan melakukan Set bit port TR0 high (port TR0 diberi nilai logika
1).
Bahasa Asembler dan C untuk Mikrokontroler
Program sumber (source program) untuk
mikrokontroler dapat dituliskan dalam berbagai bahasa, antara lain bahasa
asembler dan C. Ada juga program yang dituliskan menggunakan bahasa basic untuk
mikrokontroler, source program harus dikompilasi/diasembli menggunakan program
yang sesuai. Penggunaan bahasa sangat tergantung pada kepentingan dan kemampuan
suatu system aplikasi mikrokontroler.
Setiap bahasa memiliki kelebihan dan kekurangannya masing-masing.
Bahasa assembler dituliskan dengan detail setiap langkah yang dijalankan,
sehingga penulisanya relative lebih panjang. Pemrogram harus mengetahui benar
proses yang terjadi dalam program tersebut. Namun demikian, program assembler
sangat cocok dalam hal efisiensi penggunaan memori program. Di sini lain,
bahasa C memiliki kemudahan dalam penulisan program, namun terkadang kode yang
dihasilkan akan memakan memori program yang besar.
Program assembler berisi mnemonic instruksi, label,
dan pengarah assembler (directive). Mnemonic instruksi, label, dan pengarah
assembler (directive). Mnemonic instruksi dan pengarah sering kali membutuhkan
operan dalam penulisannya. Baris kode dalam penulisan program assembler
dibatasi hingga 120 karakter.
Label
Pada penulisan program, Anda akan banyak menggunakan
label untuk menandai suatu rutin program. Pemberian label akan sangat berguna
untuk menentukan tujuan instruksi lompatan (jump) dan percabangan (branch)
serta sebagai nama variable dalam memori program dan RAM. Namun demikian,
penggunaan label merupakan kenutuhan software assembler/compiler untuk
mengenali suatu rutin program yang Anda buat. Anda dapat menambahkan komentar
anda sendiri untuk memberi keterangan pada rutin atau instruksi yang Anda tulis
sebagai suatu komentar. Komentar tidak akan mempengaruhi proses asembli dan
akan diabaikan oleh software assembler/compiler. Untuk menuliskan komentar,
Anda dapat memisahkannya dengan kode program menggunakan tanda titik koma (“;”)
pada awal komentar anda.
Secara umum setiap baris dalam program assembler AVR
memiliki bentuk sebagai berikut:
1. [label:] directive
[operan] [komentar]
2. [label:] instruksi
[operan] [komentar]
3. [komentar]
4. Baris kosong
Cara penulisan komentar adalah
sebagai berikut:
[; teks komentar]
Pengarah Asembler
Dalam pennulisan program assembler, dikenal adanya
sejumlah pengarah assembler (directive). Pengarah assembler tidak secara
langsung diterjemahkan dalam opcode pada waktu asembli atau kompilasi. Pengarah
digunakan untuk mengatur penempatan program ke dalam memori, mendefinisikan
macro, inisialisasi memori, dan sebagainya. Pengarah assembler AVR dituliskan
dengan didahului tanda titik(“.”), sehingga disebut juga perintah bertitik.
Berikut ini pengarah assembler yang digunakan dalam pemrograman AVR.
Tabel 5.1 Daftar Pengarah
Asembler
Pengarah
|
Deskripsi
|
BYTE
|
Byte untuk variable
|
CSEG
|
Segmen kode
|
DB
|
Mendefinisikanbyte
konstanta
|
DEF
|
Mendefinisikan nama
symbol dari register
|
DEVICE
|
Mendefinisikan tipe
AVR
|
DSEG
|
Segmen dat
|
DW
|
Mendefinisikan word
konstanta
|
ENDMACRO
|
Mengakhiri macro
|
EQU
|
Menentukan suatu
symbol sama dengan ekspresi
|
ESEG
|
Segmen EEPROM
|
EXIT
|
Mengakhiri file
|
INCLUDE
|
Membaca source code
dari file lain
|
LIST
|
Membangkitkan file
list
|
LISTMAC
|
Ekspansi file list untuk
macro
|
MACRO
|
Memulai macro
|
NOLIST
|
Tidak membangkitkan
file list
|
ORG
|
Menentukan awal
program
|
SET
|
Menentukan suatu
symbol sebagai ekspresi
|
Penjelasan untuk masing-masing pengarah diberikan
berikut ini.
1. BYTE
Pengarah BYTE merupakan pengarah yang digunakan untuk
menjaga ketersediaan memori didalam SRAM. Agar dapat digunakan untuk menunjuk
suatu lokasi memori, pengarah BYTE harus dituliskan melalui label. Pengarah
BYTE akan mengambil sebuah parameter yaitu banyaknya byte yang akan
dipertahankan. Pengarah ini hanya dapat digunakan dalam Data Segment.
Sintak:
Label: .BYTE ekspresi
Contoh:
. DSEG
varl: .BYTE 1
table: .BYTE tab_size
.CSEG
1di r30, low (var1)
1di r31, high (var1)
2. CSEG
Pengarah CSEG digunakan untuk mendefinisikan awal
suatu Code Segment. Sebuah file assembler dapat terdiri dari beberapa Code
Segment, yang semuanya terhubung dengan satu Code Segment. Pengarah BYTE tidak
dapat digunakan di dalam sebuah Code Segment. Code Segment memiliki pencacah
lokasinya sendiri, yaitu berupa pencacah kata. Pengarah ORG dapat digunakan
untuk menempatkan kode dan konstanta pada suatu lokasi tertentu di dalam Memori
Program. Dalam penulisannya, pengarah CSEG tidak memerlukan suatu operan
apapun.
Sintak:
.CSEG
Contoh:
. DSEG ; Memulai data segment
vartab: .BYTE 4 ;
. CSEG ; Memulai code
segment
const: .DW 2 ; Tulis 0×0002 dlm prog. Mem.
Mov r1,r0 ; Lakukan sesuatu
3. DB
Pengarah DB digunakan untuk menjaga sumber daya memori
yang ada di dalam Memori Program atau memori EEPROM. Pengarah DB mengambil beberapa
ekspresi sekaligus sebagai suatu daftar ekspresi. Dalam penulisannya, daftar
ekspresi dituliskan sebagai runtun ekspresi yang masing-masing dipisahkan
dengan tanda koma. Masing-masing ekspresi dapat berupa bilangan antara – 128
hingga 255. Untuk bilangan negative, bilangan komplemen dua 8 bit akan
ditempatkan pada Memori Program atau Memori EEPROM.Pengarah DB harus
ditempatkan pada Code Segment atau EEPROM Segment dan minimal harus mengandung
sebuah ekspresi. Bila pengarah DB berada pada Code Segment dan daftar ekspresi
berisi lebih dari satu ekspresi, maka ekspresi-ekspresi tersebut akan dikemas
dalam dua byte kata pada Memori Program.
Sintaks:
Label : .DB daftar_ekspresi
Contoh:
.CSEG
konstanta: .DB 5, 255, 0b01000101, 0xaa
.ESEG
eeconstan: .DB 0xff
4. DEF
Pengarah DEF mengijinkan suatu register sehingga dapat
ditunjuk menggunakan suatu symbol, suatu symbol yang didefinisikan disela-sela
Memori Program dapat digunakan untuk menunjuk suatu register. Sebuah symbol
dapat kembali didefinisikan pada bagian lain dalam program.
Sintaks:
.DEF Symbol=Register
Contoh:
.DEF temp=R16
.DEF ior=R0
.CSEG
1di temp, 0xf0 ; Load 0xf0 ke register temp
in ior, 0x3f ; Baca SREG ke dalam register oir
eor temp, ior ; exclusive OR temp dan ior
5. DEVICE
Pengarah DEVICE akan memberitahukan kepada software
assembler mengenai jenis/tipe perangkat mikrokontroler apa yang akan digunakan
untuk mengeksekusi kode program tersebut. Apabila pengarah ini anda gunakan,
maka adanya suatu instruksi yang tidak mendukung tipe mikrokontroler akan
ditampilkan sebagai peringatan (warning). Peringatan ini misalnya, apabila
ukuran Code Segment atau EEPROM Segment melebihi kapasitas mikrokontroler yang
dispesifikasikan. Bila pengarah ini tidak digunakan, maka anggap semua intruksi
mendukung untuk digunakan pada perangkat mikrokontroler tersebut.
Sintak:
.DEVICE AT90S1200 | AT902313 | AT90S8515
Contoh:
.DEVICE AT90S2313 ; Menggunakan AT90S2313
6. DSEG
Pengarah DSEG digunakan untuk mengawali suatu Data
Segment. Sebuah file assembler dapat berisi beberapa Data Segment, yang akan
dinyatakan dalam sebuah Data Segment pada saat diasembli. Sebuah Data Segment
lazimnya hanya berisi pengarah BYTE dan label saja.
Sintaks:
.DSEG
Contoh:
.DSEG
var1: .BYTE
table: .BYTE tab_size
.CSEG
1di r30, low(var1)
1di r30, high(var1)
1d r1, z
7. DW
Pengarah ini digunakan untuk mendefinisikan konstanta
dalam Memori Program atau memori EEPROM. Pengarah DB akan membawa sebuah daftar
ekspresi dan minimal harus mengandung sebuah ekspresi. Pengarah ini harus
ditempatkan di dalam Code Segment atau EEPROM Segment. Daftar ekspresi
merupakan runtun ekspresi, yang masing-masing dipisahkan dengan tanda koma dan
merupakan bilangan antara – 23768 hingga 65535. Apabila ekspresi menyatakan
bilangan negative, maka bilangan komplemen dua 16-bit akan ditempatkan pada
Memori Program.
Sintaks:
LABEL: .DW daftar_ekspresi
Contoh:
.CSEG
varlist: .DW 64286, 0b0101011110100011,0xffff
.ESEG
eevar: .DW0xfafa
8. ENDMACRO
Penagarah ini digunakan untuk mendefinisikan akhir
sebuah macro. Pengarah ini tidak membutuhkan parameter.
Sintaks:
.ENDMACRO
Contoh:
.MACRO SUBI16
subi r16, low(@0)
sbci r17, high (@0)
.ENDMACRO
9. EQU
Pengarah EQU digunakan untuk memberikan nilai terhadap
suatu label. Label tersebut kemudian dapat digunakan pada papan ekspresi selanjutnya.
Sebuah label yang telah diberi nilai menggunakan pengarah EQU merupakan sebuah
konstanta dan tidak dapat diubah nilainya atau didefinisikan ulang.
Contoh:
.EQU oi_offset=0×23
.EQU porta = io_offset + 2
.CSEG
clr r2
out porta, r2
10. ESEG
Pengaran ini digunakan untuk mendefinisikan EEPROM
Segments. Sebuah file assembler bisa saja terdiri dari beberapa EEPROM Segment,
namun pada saat diasembli, semuanya akan dikumpulkan dalam satu EEPROM Segment.
Pengarah ESEG tidak memerlukan parameter apapun dalam penulisannya.
Sintaks:
.ESEG
Contoh:
.DSEG
vartab: .BYTE 4
.ESEG
eevar: .DW 0x0ff0
.CSEG
const: .DW 2
mov r1, r0
1. EXIT
Pengarah EXIT akan memberitahu pada software Asembler
untuk menghentikan proses asembli. Pada umumnya assembler akan berjalan hingga
akhir file. Apabila pengarah EXIT ditemukan dalam suatu file include, maka
Asembler akan melanjutkan prosesnya pada baris sesudah pengarah INCLUDE dalam
file yang berisi pengarah INCLUDE tersebut.
Contoh:
.EXIT ; keluar dari file
12. INCLUDE
Pengarah INCLUDE akan memberitahukan pada software
Asembler untuk mulai membaca pada sebuah file khusus yang ditunjuk. Setelah
itu, Asembler akan membaca hingga akhir file tersebut atau hingga pengarah EXIT
dijumpai.
Sintaks:
.INCLUDE “nama_file”
Contoh:
.EQU sreg=0x0f
.EQU sphigh=0x0e
.EQU splow=0x0d
.INCLUDE “iodefs.asm”
in r0, sreg
13. LIST
Pengarah LIST akan memberitahukan Asembler untuk
membangkitkan file list. Asembler akan membangkitkan file list yang berisi
gabungan kode assembler, alamat, dan opcode. File list akan dibangkitkan dalam
mode defaut Asembler. Pengarah ini dapat pula digunakan bersama dengan mengarah
NOLIST dengan maksud untuk hanya membangkitkan file list pada bagian tertentu
saja.
Sintaks:
.LIST
Contoh:
.NOLIST
.INCLUDE “macro. inc”
.INCLUDE “consdef. inc”
.LIST ;mengaktifkan pembangkitan file LIST
14. LISTMAC
Pengarah LISTMAC akan mengatakan pada Asembler bahwa
ketika sebuah macro dipanggil, maka penambahan macro akan ditampilkan pada file
list yang dibangkitkan. Dalam mode default, hanya macro yang dipanggil dengan
parameter yang akan ditempatkan dalam file list.
Sintaks:
.LISTMAC
Contoh:
.MACRO MACX
add r0, @0
eor r1, @1
.ENDMACRO
.LISTMAC
MACX r2, r1
15. MACRO
Pengarah MACRO digunakan untuk mengawali suatu macro.
pengarah ini membutuhkan parameter berupa nama_macro.
Suatu macro diakhiri menggunakan pengarah ENDMACRO.
Pada kondisi default, hanya pemanggilan macro yang ditampilkan pada file list
yang dibangkitkan oleh software assembler. Untuk memasukkan macro yang ditunjuk
ke dalam file list, maka pengarah LISTMAC harus digunakan. Suatu macro ditandai
dengan tanda “+” pada opcode di dalam file list.
Sintaks:
.MACRO
Contoh:
.MACRO SUBI16
subi @1, low (@0)
sbci @2, high (@0)
.ENDMACRO
.CSEG
SUBI16 0X1234, r16, r17
16. NOLIST
Pengarah ini digunakan agar suatu runtuh instruksi
tidak dimasukkan dalam file list yang dibangkitkan. Pada umumnya, software
assembler akan membangkitkan file list yang berisi alamat, instruksi sumber,
dan opcode dari file sumber. Pembangkitan file list akan dilakukan secara
otomatis dalam mode default dan dapat dibuat tidak aktif menggunakan pengarah
ini. Pengarah ini juga dapat digunakan bersama pengarah LIST yang digunakan
untuk membangkitkan file list hanya untuk bagian tertentu dari program sumber.
Sintaks:
.NOLIST
Contoh:
.NOLIST ; Pembangkitan file list tidak diaktifkan
.INCLUDE “macro.inc”
.INCLUDE “cons.def”
.LIST ; Bangkitkan file list kembali
17. ORG
Pengarah ORG digunakan untuk menentukan pencacah
lokasi pada suatu nilai mutlak tertentu. Nilai tersebut dituliskan sebagai
parameter pengarah ini. ORG berarti nilai awal (origin). Apabila ORG digunakan
pada Data Segment, makan nilai parameter akan menunjuk pada lokasi SRAM dan
bila digunakan pada Code Segment, maka parameter tersebut akan menunjuk pada
lokasi Memori Program. Sedangkan bila digunakan pada EEPROM Segment, maka
parameter dari pengarah ini akan menunjuk lokasi EEPROM. Nilai default pencacah
lokasi Code dan EEPROM adalah nol, sedangkan nilai default pencacah lokasi SRAM
adalah 32 (karena register-register menempati alamat 0-31) pada saat proses
asembli dimulai.
Sintaks:
.ORG ekspresi
Contoh:
.DSEG ; Mulai data segment
.ORG 0×37 ; Set alamat SRAM pada 37hex
variable: .BYTE 1 ;
.CSEG
.ORG 0×10 ; Set Program Counter dengan 10hex
mov r0, r1 ;
18. SET
Pengarah SET digunakan untuk memberikan suatu nilai
tertentu pada suatu label. Label tersebut dapat digunakan pada ekspresi selanjutnya.
Sinteks:
.SET label=ekspresi
Contoh:
.SET io_ofset = 0×26
.SET porta = io_ofset +2
.CSEG
clr r2
out porta,r2
Ekspresi
Ekspresi dapat berupa operan, operator, maupun fungsi.
Semua ekspresi akan diterjemahkan sebagai kode 32-bit oleh assembler/compiler.
Operan
operan yang dapat digunakan dalam
program assembler antara lain :
̶ Label yang
didefinisikan oleh user dan diberi nilai pencacah lokasi sesuai dengan tempat
di mana label tersebut akan muncul.
̶ Variable yang
didefinisikan oleh user menggunakan pengarah SET.
̶ Konstanta integer,
dituliskan dalam beberapa format berikut:
- Desimal (default): 0, 23, 50
- Heksadesimal:0x0a, 0xff, $80
- Biner: 0b00101110
̶ PC: nilai pencacah
Memori Program pada saat itu.
Fungsi
Fungsi yang digunakan pada assembler
AVR didefinisikan dengan cara berikut:
̶ LOW (ekspresi)
mengembalikan byte low dari sebuah ekspresi.
̶ HIGH (ekspresi)
mengembalikan byte kedua dari sebuah ekspresi.
̶ BYTE2 (ekspresi) sama
dengan HIGH.
̶ BYTE3 (ekspresi)
mengembalikan byte ketiga dari sebuah ekspresi.
̶ BYTE4 (ekspresi)
mengembalikan byte keempat dari sebuah ekspresi.
̶ LWRD (ekspresi)
menunjuk pada bit 0-15 dari sebuah ekspresi.
̶ HWRD (ekspresi)
menunjuk pada bit 16-31 dari sebuah ekspresi.
̶ PAGE (ekspresi)
menunjuk pada bit 16-21 dari sebuah ekspresi.
̶ EXP2 (ekspresi)
menunjuk 2 pangkat nilai ekspresi.
̶ LOG2 (ekspresi)
menunjuk bagian integer dari log 2 dari ekspresi.
Operator
Operator yang digunakan dalam
pemrograman assembler AVR diberikan pada Tabel 5.2.
Tabel 5.2 Daftar Operator
Tipe
|
Simbol
|
Deskripsi
|
Contoh
|
Kalkulasi
|
+
|
Penjumlahan
|
ldi r30, c1+c2
|
-
|
Pengurangan
|
ldi r17, c1-c2
|
|
*
|
Perkalian
|
ldi r30, label*2
|
|
/
|
Pembagian-Integer
|
ldi r30, label/2
|
|
Biner
|
&
|
Bitwise AND
|
ldi r18, High(c1&c2)
|
|
|
Bitwise OR
|
ldi r18, Low(c1|c2)
|
|
^
|
Bitwise Exclusive-OR
|
ldi r18, Low(c1^c2)
|
|
~
|
Bitwise NOT
|
ldi r16.~0xf0
|
|
<<
|
Geser kiri
|
ldi r17,1<<bitm
|
|
>>
|
Geser kanan
|
ldi r17,c1>>c2
|
|
Logika
|
<
|
Kurang dari
|
ori r18,bitmask*(c1<c2)+1
|
>
|
Lebih dari
|
ori r18,bitmask*(c1>c2)+1
|
|
= =
|
Sama dengan
|
andi r19,bitmask*(c1= =c2)+1
|
|
<=
|
Kurang dari atau sama dengan
|
ori r18,bitmask*(c1<=c2)+1
|
|
>=
|
Lebih dari atau sama dengan
|
ori r18,bitmask*(c1>=c2)+1
|
|
!=
|
Tidak sama dengan
|
.SET flag=(c1!=c2)
|
|
&&
|
AND
|
ldi r18, Low(c1&&c2)
|
|
| |
|
OR
|
ldi r18, Low(c1| |c2)
|
|
!
|
NOT
|
ldi r1, !0xf0
|
Dasar-dasar
Pemrograman AVR
Struktur Program
Bentuk umum program yang biasa
digunakan adalah sebagai berikut:
Deklarasi konstanta
Definisi variable
File include
Vektor Reset dan intrupsi
Program utama
Akhir program
Penggunaan Register
Penggunaan register dalam pemrograman AVR merupakan
hal yang sangat esensial dan tidak dapat dipisahkan dalam penulisan program.
Untuk itu anda perlu berhati-hati dalam menuliskan instruksi, memilih register,
dan hal-hal lain yang berkaitan dengan penunggunan register. Mula-mula
definisikan nama register menggunakan pengarah DEF dan pilihlah nama yang anda
anggap mudah diingat dan mencerminkan fungsi dan kegunaannya, Jika anda perlu
menggunakan akses pointer, gunakanlah register R26 sampai R31. Sedangkan bila
anda ingin melakukan pencacahan menggunakan counter 16-bit, pilihlah pasangan
register R24:R25. Apabila anda ingin membaca dari memori program, misal: nilai
konstanta, anda dapat menggunakan Z (R31:R30) dan R0. Register R16 sampai R31,
dapat anda gunakan untuk mengakses bit tunggal suatu register tertentu.
Dalam mikrokontroler AVR terdapat 32 buah register.
Register tersebut memiliki nama asli R0 hingga R31, namun anda dapat memberi
nama sesuai yang anda kehendaki dengan menggunakan pengarah assembler. Nama
yang anda berikan hendaknya adalah nama yang mudah diingat dan mencerminkan
kegunaan atau isi dari register tersebut.
.DEF reg_bilangan1 = R16
Dengan memberi nama reg_bilangan1 pada R16, maka anda
dapat menggunakan reg_bilangan1 untuk menunjuk R16.
Misalkan anda ingin memberi nilai 150 pada register
R16, anda dapat menggunakan perintah berikut:
LDI reg_bilangan1, 150
Penulisan bilangan pada assembler AVR memiliki default
pada basis bilangan decimal. Bila anda ingin menuliskan bilangan dalam basis
heksadesimal atau biner, maka anda harus menggunakan format penulisan yang
lain, misalnya 0x6a atau $6a untuk heksadesimal, atau 0b01101010 untuk
penulisan bilangan dalam format biner.
Dalam satu perintah, Anda mungkin ingin mengoperasikan
dua buah register. Misalnya anda ingin memindahkan isi suatu register ke
register yang lain, anda dapat menggunakan perintah MOV.
.DEF reg_bilangan1 = R16
.DEF reg_penampung = R17
LDI reg_bilangan1, 150
MOV reg_penampung, reg_bilangan1
Perintah diatas akan menulis nilai 150 pada register
reg_bilangan1 dan kemudian menyalin isi register reg_bilangan1 ke
reg_penampung. Perhatikan, bahwa register yang dituliskan terlebih dahulu pada
instruksi MOV merupakan register target yang merupakan register di mana hasil
operasi akan dituliskan.
Dua baris pertama yang berisi pengarah assembler di
atas tidak akan menghasilkan kode apapun dalam Memori Program AVR. Penggunaan
pengarah assembler hanya untuk menberikan suatu informasi kepada software
assembler dalam proses pelaksanaan asembli.
Untuk memuati reg_penampungan dengan suatu bilangan
(missal 150), anda mingkin akan menuliskan perintah berikut:
.DEF reg_penampung = R15
LDI reg_penampung, 150
Maka anda akan kehilangan data. Perlu anda ketahui
bahwa hanya register R16 hingga R31 yang dimuati dengan suatu konstanta
menggunakan instruksi ldi. Keterbatasan ini sangat tidak menyenangkan, namun
tidak dapat dihindari karena kontruksi instruksi set AVR. Sebagai pengecualian,
terdapat satu instruksi yang berlaku untuk semua register yaitu clr, yang akan
mengeset isi register dengan nilai nol. Perintah clr <nama_register>,
berlaku untuk semua register.
Di samping instruksi LDI, terdapat instruksi-instruksi
lain yang memiliki pembatasan dalam penggunaan dalam program assembler.
Instruksi-instruksi tersebut adalah:
1. ANDI Rx, K ;bit-AND
register Rx dengan konstanta K.
2. CBR Rx, M ;bersihkan
semua bit dalam register Rx dan set ke “1” dengan mask value M.
3. CPI Rx, K ;bandingkan
isi register Rx dengan konstanta bernilai K.
4. CBCI Rx, K ;kurangkan
isi register Rx beserta nilai carry flag-nya dari nilai konstanta K.
5. SBR Rx, M ;set semua
bit dalam register Rx dengan nilai “1”, dengan mask value M.
6. SER Rx ;set semua bit
dalam register Rx (sama dengan perintah LDI Rx,255).
7. SUBI Rx, K ;kurangkan
nilai konstanta K dari isi register Rx, dan simpan hasilnya ke register Rx.
Semua instruksi di
atas hanya berlaku untuk register R16 sampai R31. Apabila anda ingin
menggunakan instruksi-instruksi di atas, Anda perlu memilih salah satu register
diatas. Hal itu akan memudahkan anda dalam pemrograman. Hal ini juga merupakan
alasan tambahan digunakannya pengarah assembler untuk mendefinisikan lokasi
register sehingga kita dapat lebih mudah mengubah lokasi register yang kita
inginkan.
Sedikit mengulang apa
yang telah anda pelajari di muka, dalam pemrograman assembler, terdapat aturan
tambahan yang berlaku untuk pasangan register R26:R27, R28:R29, dan R30:R31.
Seperti anda ketahui, ketiga pasangan register ini memiliki nama tambahan yaitu
X, Y dan Z. Pasangan tersebut merupakan pointer register 16-bit yang dapat
menunjuk alamat dengan maksimum 16-bit ke dalam lokasi memori SRAM (X, Y, Z)
atau ke dalam lokasi Memori Program (Z). Byte bawah dari alamat 16-bit tersebut
ditempatkan pada register yang lebih rendah, sedangkan byte atas ditempatkan
pada register yang lebih tinggi. Kedua bagian ini memiliki nama yang
sendiri-sendiri, misalkan, byte atas dari Z dinamakan ZH(=R31) dan byte bawah
dari Z dinamakan ZL(= R30). Nama-nama tersebut didefinisikan dalam file header
standar untuk masing-masing mikrokontroler. Pembagian nama pointer 16-bit
tersebut dalam dua byte yang berbeda dilakukan seperti berikut:
.EQU Adress = RAMEND
1di YH, HIGH (Adress)
1di YL, LOW (Adress)
Pengaksesan melalui
pointer dilakukan menggunakan instruksi yang dirancang khusus. Untuk membaca,
digunakan instruksi LD (LoaD), sedangkan penulisan dilakukan dengan instruksi
ST (Store).
Hanya terdapat satu
instruksi akses baca untuk Memori Program yang didefinisikan pada pasangan
register Z. Instruksi tersebut bernama LPM (Load Program Memory). Instruksi ini
akan menyalin alamat byte Z dalam memori program ke register R0. Mengingat
Program memori diatur dalam word-wise (satu instruksi pada satu alamat
berisi 16-bit atau 2-byte atau 1-word) LSB menentukan byte bawah (jika LSB =
“0”) atau byte atas (jika LSB = “1”). Hal ini mengakibatkan alamat asli harus
dikalikan dua dan akses dibatasi untuk 15-bit atau 32kB memori program.
1di ZH, HIGH
(2*Adress)
1di ZL, LOW
(2*Adress)
LPM
Setelah perintah ini, alamat harus di-incremen untuk
menunjuk byte berikutnya dalam memori program. Karena sering digunakan,
increment pointer didefinisikan melalui perintah berikut:
ADIW Z, 1
LPM
ADIW berarti Add Immediate Word.
Komplemen instruksi ADIW adalah SBIW (SuBtract
Immediate Word). ADIW dan SBIW dapat digunakan untuk pasangan register X, Y,
dan Z, dan untuk pasangan register R24:R25 yang tidak memiliki nama tambahan
dan tidak mengijinkan akses SRAM dan lokasi Memori Program. R24:R25 merupakan
pasangan memori yang cocok untuk penanganan nilai 16-bit.
Membuat Tabel
Seringkali kita perlu membuat suatu tabel data untuk
melakukan operasi-operasi di dalam program yang kita buat. Di sini kita
membahas bagaimana suatu perintah dituliskan untuk membentuk suatu tabel
menggunakan pengarah .DB dan .DW. dengan perintah tersebut Anda dapat
memasukkan daftar nilai (data)bytewise atau wordwise.
Data bytewise ditata dalam format
sepeti berikut:
.DB 123, 45, 67, 89 ; data empat byte
.DB “data teks” ; data karakter ASCII
Anda harus selalu menulis data berjumlah genap, jika
tidak maka assembler akan menambahkan nilai nol pada bagian akhir daftar nilai
yang barangkali tidak Anda harapkan.
Daftar data yang sama dapat Anda tuliskan dalam
wordwise, sebagaimana contoh berikut:
.DB 12345,6789 ; daftar 2 word
Selain data berupa konstanta, anda juga dapat
memasukkan label (target lompatan), seperti contoh berikut:
Label1:
[. . perintah . .]
Label2;
[. . perintah lainnya . .]
Tabel:
.DB label1,Label2 ; daftar label
Penggunaan khusus register pointer adalah untuk mengakses
dirinya sendiri. Register terletak pada 32-byte pertama dalam mikrokontroler
(0×0000 sampai 0x001F). Akses ini hanya akan bermanfaat apabila Anda harus
menyalin isi register ke SRAM atau EEPROM atau sebaliknya.
Penggunaan utama pointer adalah untuk mengakses tabel
dengan nilai tetapan pada memori program. Sebagai contoh, tabel dengan 10 niali
16-bit dengan nilai kelima dibaca ke R25:R25.
Table_1:
.DW 0×1234, 0×2345, 0×3456, 0×4568, 0×5678 ;
.DW 0×6789, 0x789A, 0x89AB, 0x9ABC, 0xABCD;
Read5:
LDI ZH, HIGH (Table_1*2) ;
LDI ZL, LOW (Table_1*2) ;
ADIW ZL, 10 ;
LPM ;
MOV R24, R0 ;
ADIW ZL, 1 ;
LPM ;
MOV R25, R0 ;
Port
Port merupakan gerbang yang menghubungkan CPU dengan
komponen lain baik internal maupun eksternal. CPU berkomunikasi dengan
komponen-komponen tersebut, membaca nilai atau menulis suatu niali padanya.
Port yang paling sering digunakan adalah register flag yang kepadanya
dituliskan nilai hasil operasi sebelumnya dan syarat percabangan dibaca
darinya.
Port AVR memiliki alamat tetap yang digunakan untuk
berkomunikasi dengan CPU. Alamat Port pada AVR sudah tetap dan tidak bergantung
pada tipe mikrokontroler AVR. Misalkan saja, alamat port B adalah 0×18. Anda
tidak perlu menghafal alamat port-port tersebut, karena masing-masing port
memiliki nama alias yang didefinisikan dalam file include (file header) dari
masing-masing mikrokontroler. Dalam file include, Port B didefinisikan
menggunakan baris berikut:
.EQU PORTB, 0×18
Port biasanya diorganisasikan dalam bilangan 8 bit,
amaun juga dapat diakses sebagai 8 single bit. Bila single bit hendak
digunakan, maka Anda dapat jhuga menggunakan nama yang didefinisikan dalam file
include. Dengan mengikuti aturan penamaan tersebut, anda tidak perlu
mengingat-ingat posisi bit tersebut dalam suatu port. Sebagai contoh, MCU
General Control Register yang diberi nama MCUCR berisi sejumlah single contoh
bit yang mengendalikan kerja mikrokontroler secara umum. MCUCR merupakan sebuah
port yang dikemas dengan 8 control bit, dengan namanya masing-masing (ISC00,
ISC01, . .). Sehingga apabila anda ingin membawa mikrokontroler Anda pada
kondisi sleep, anda perlu tahu dari data sheet bagaimana anda harus mengeset
bit yang bersangkutan.
.DEF my_setting = R16
1di my_setting = 0b00100000
out MCUCR, my_setting
sleep
Perintah OUT akan membawa isi register my_setting,
yang berisi Sleep-Enable-Bit bernama SE, ke port MCUCR dan mengeset
mikrokontroler ke kondisi sleep apabila perintah SLEEP dieksekusi.
Pada perintah diatas, seluruh isi register my_setting
akan disalin ke MCUCR. Dari contoh di atas dapat anda lihat bahwa bit SE (bit5)
berlogika “1”(set) sedangkan Sleep-Mode-Bit bernama SM (bit4) akan
di-clear(“0”). Ini berakibat system masuk ke mode idle atau half-sleep
(setengah tidut) yang akan membuat tidak ada eksekusi perintah berikutnya yang
dilakukan, namun system tetap akan bereaksi terhadap interupsi timer dan
interupsi eksternal lainnya. Mode sleep yang lain dalam AVR adalah mode
power-down. Untuk masuk dalam mode ini, pertama SE harus diset dan bit SM juga
harus diset. Apabila program mendapatkan instruksi SLEEP, maka mikrokontroler
hanya bisa dibangunkan oleh reset eksternal, reset akibat WDT overflow atau
interupsi eksternal pada NIT0 atau INT1.
Untuk membaca isi suatu port, Anda dapat menggunakan
instruksi IN. Berikut ini contoh penggunaannya di dalam program.
.DEF my_read = R16
IN my_read, MCUCR
Penggalan program di atas akan membaca isi port MCUCR
dan menyalinya dalam register my_read. Pada beberapa port, terdapat sejumlah
bit yang tidak terdefinisikan atau tidak digunakan. Bila bit-bit tersebut
dibaca, maka hasil yang diperoleh adalah nol (“0”).
Semakin sering Anda membaca port 8-bit, maka anda
harus memperhatikan seluruh perubahan status port tersebut. Terkadang anda
tidak perlu membaca keseluruhan isi port dan memisahkan bit yang berkaitan
saja. Untuk keperluan ini, Anda dapat menggunakan instruksi yang tepat dan
memberikan kesempatan untuk eksekusi perintah pada level bit tertentu. Mengeset
atau meng-clear bit suatu port juga dapat Anda lakukan tanpa harus membaca dan
menulis bit-bit lain dalam port tersebut. Perintah-perintah tersebut adalah SBI
(Set Bit I/O) dan CBI (Clear Bit I/O). Contoh penggunaan instruksi ini adalah
sebagai berikut:
.EQU ActiveBit = 2
SBI PortB, ActiveBit
CBI PortB, ActiveBit
Kedua instruksi di atas memiliki kelemahan, yakni
hanya port yang berlokasi pada alamat kurang dari 0×20 yang dapat diakses. Port
dengan alamat di atas 0×20 tidak dapat diakses menggunakan cara ini. Pada kasus
lain, port dapat diakses dengan cara lain. Cara lain untuk mengakses port
adalah dengan menggunakan perintah akses SRAM, seperti ST dan LD. Caranya
adalah dengan menambahkan 0×20 pada alamat port. Berikut ini ditunjukkan cara
pengaksesan tersebut.
.DEF my_port = R16
1di ZH, HIGH (PortB+32)
1di ZL, LOW (PortB+32)
1d my_Port, Z
Penggunaan cara diatas hanya pada khusus tertentu
saja.
Mengakses SRAM
SRAM merupakan memori dalam mikrokontroler AVR yang
tidak dapat diakses langsung dari CPU. Apabila Anda ingin mengakses lokasi
memori ini, anda perlu menggunakan register sebagai media penyimpan sementara.
Proses pengaksesan SRAM jelas memerlukan waktu yang lebih lama dibanding dengan
pengaksesan register itu sendiri, karena register merupakan memori yang berada
di dalam CPU. Namun disisi lain, tipe paling rendah dari AVR saja memiliki 128
byte SRAM, ukuran yang cukup besar disbanding dengan jumlah register yang hanya
32 buah. Pada AVR tipe AT90S8515 ke atas, terdapat fitur tambahan yang
memungkinkan kita untuk menyambungkan dengan RAM eksternal guna memperbesar
ukuran SRAM internal yang hanya 512 byte. Dari sudut pandang assembler,
pengaksesan Ram eksternal sama persis dengan pengaksesan SRAM internal. Tidak
ada perintah khusus yang diperuntukkan dalam pengaksesan RAM eksternal.
Terdapat berbagai kemudahan yang ditawarkan apabila
kita menggunakan memori SRAM. Tidak hanya akses dengan alamat tetap yang
diperbolehkan, tetapi kita juga dapat menggunakan pointer untuk mengakses
lokasi memori. Dengan demikian, floating access terhadap beberapa lokasi dapat
deprogram. Dengan cara ini, Anda dapat membangun sebuah ring buffer untuk
penyimpanan sementara suatu nilai atau tabel kalkulasi. Hal ini ternyata tidak
mungkin dilakukan apabila hanya menggunakan register.
Hal lain yang berhubungan dengan itu adalah akses
dengan menggunakan sebuah offset pada suatu alamat awal yang tetap dalam satu
register pointer. Dalam kasus ini, alamat tetap disimpan dalam register
pointer, nilai konstanta ditambahkan pada alamat tersebut dan akses baca/tulis
dilakukan pada alamat tersebut menggunakan sebuah offset.
Selanjutnya akan membicarakan penggunaan SRAM sebagai
stack. Anda dapat melakukan push terhadap suatu nilai ke dalam stack, menjadi
isi suatu register, mengembalikan alamat utama untuk pemanggilan subrutin atau
mengembalikan alamat utama untuk sebuah interupsi yang dipicu hardware.
Dalam penggunaan SRAM, Anda perlu terlebih dahulu
mendefinisikan alamat yang hendak Anda gunakan. Alamat SRAM yang dapat
digunakan adalah mulai dari 0×0060 hingga batas akhir SRAM fisik dari jenis AVR
yang anda gunakan.
Anda dapat menyalin isi register R1 ke dalam lokasi
pertama SRAM dengan perintah berikut.
STS 0×0060, R1
Sedangkan untuk operasi sebaliknya, yaitu menyalin isi
SRAM pada alamat 0×0060 ke dalam register R1, dapat anda lakukan dengan
menggunakan perintah:
LDS R1, 0×0060
Untuk memudahkan Anda dalam melakukan Akses terhadap
memori SRAM, Anda dapat memberi nama simbolik yang mudah diingat untuk alamat
SRAM tersebut. Dengan cara ini, Anda tidak perlu lagi menuliskan bilangan heksa
dari alamat 0×0060 Anda beri nama temp_satu. Anda dapat menuliskan dalam
program sebagai berikut:
.EQU temp_satu = 0×0060
STS temp_satu, R1
Dengan penamaan seperti di atas, Anda akan lebih mudah
untuk mengingat lokasi yang anda inginkan.
Cara pengaksesan SRAM yang lain adalah dengan menggunakan
pointer. Untuk keperluan ini, Anda membutuhkan dua buah register yang digunakan
untuk mengalamati 16-bit lokasi. Anda dapat menggunakan pasangan register X, Y,
dan Z. Pasangan register tersebut mengizinkan kita untuk melakukan akses
terhadap lokasi yang mereka tunjuk secara langsung (misalnya dengan ST X, r1),
yang ditunjuk setelah nilai alamat di-decremen (misalnya dengan ST X, R1) atau
dengan increment alamat (dengan ST +X, R1). Contoh pengaksesan tiga sel memori
adalah sebagai berikut:
.EQU temp_satu = 0×0060
.DEG reg_satu = r1
.DEF reg_dua = R2
.DEF reg_tiga = R3
LDI XH, HIGH (temp_satu)
LDI XL, LOW (temp_satu)
LD reg_satu, X+
LD reg_dua, X+
LD reg_tiga, X
Bentuk ketiga merupakan bentuk pengaksesan yang cukup
jarang dilakukan, misalkan saja kita sangat sering mengakses tiga buah lokasi
SRAM. Sementara kita memiliki sisa pasangan register, kita akan berusahan untuk
menggunakannya untuk keperluan tersebut. Bila kita menggunakan instruksi LD/ST,
Anda harus selalu mengubah pointer apabila anda ingin mengakses lokasi yang
lain. Hal itu sungguh tidak menyenangkan. Untuk menghindarinya, anda dapat
menggunakan pengaksesan dengan offset, yang cukup membingungkan bagi para
pemula. Selam akses berlangsung, isi register tidak pernah berubah. Alamat
dihitung menggunakan penjumlahan sementara terhadap nilai offset yang tetap.
Dari contoh di atas, pengaksesan lokasi 0×0062 akan tampak seperti berikut:
Pertama, pointer register kita
gunakan menunjuk lokasi 0×0060.
.EQU temp_satu = 0×0060
.DEF reg_satu = R1
LDI YH, HIGH (temp_satu)
LDI YL, LOW (temp_satu)
[ . . . ]
Kemudian bila di suatu tempat dalam program kita
hendak mengakses lokasi SRAM 0×0062, kita dapat menggunakan perintah berikut:
STD Y+2, reg_satu
Perhatikan bahwa 2 tidak hanya ditambahkan sementara
terhadap Y. Penambahan sementara seperti ini tidak dapat dilakukan untuk
pointer register X, hanya Y dan Z saja.
Perintah lain yang masih berhubungan degnan
pengaksesan SRAM dengan menggunakan offset adalah seperti berikut:
LDD reg_satu, Y+2
SRAM juga umum digunakan sebagai stavk dalam program
assembler. Stavk memiliki struktur LAST-In-First-Out, artinya yang
terakhir masuk dalam stack akan menjadi yang pertama keluar. Untuk menggunakan
SRAM sebagai stack, pertama ANDa perlu mendefinisikan stack pointer. Stack
pointer merupakan pointer 16-bit, maka dapat diakses layaknya sebuah port.
Kedua registernya bernama SPH:SPL. SPH merupakan byte atas (MSB), sedangkan SPL
merupakan byte bawah (LSB). Hal ini hanya berlaku untuk AVR dengan kapasitas
SRAM lebih dari 256 byte. Bila tidak, maka SPH tidak didefinisikan dan tidak
dapat digunakan.
Gambar 5.3 Metoda Penanganan
Stack
Untuk membangun stack, stack pointer harus dimuati
dengan alamat tertinggi SRAM.
.DEF reg_satu = R16
LDI reg_satu, HIGH (RAMEND)
OUT SPH, reg_satu
LDI reg_satu. LOW (RAMEND)
OUT SPL, reg_satu
Nilai RAMEND tentu saja tergantung pada jenis AVR yang
anda gunakan. Nilai ini juga didefinisikan dalam file header standar
masing-masing jenis AVR. Untuk file 2313def.inc, nilai ini dinyatakan dalam
baris berikut:
.equ RAMEND = $xxx
File 2313def.inc dimasukkan dalam program yang anda
buat menggunakan pengarah INCLUDE pada awal kode.
Penggunaan STACK
Menggunakan stack dalam program cukup mudah. Isi
register didorong pada stack dengan perintah seperti berikut:
PUSH reg_satu
Apabila anda kini ingin mendapatkan isi register tadi,
Anda cukup menuliskan perintah berikut:
POP reg_satu
Dengan instruksi POP, anda akan mendapatkan nilai yang
terkhir didorong ke stack.
Perintah menggunakan PUSH dan POP hanya bermanfaat apabila
isi register masih Anda perlukan pada baris lain berikutnya, semua register
telah terpakai atau tidak terdapat tempat lain yang dapat anda gunakan untuk
menyimpan nilai tersebut. Bila kondisi-kondisi tersebut tidak terpenuhi, maka
penggunaan stack tidak akan bermanfaat dan hanya akan membuang waktu saja.
Penggunaan stack yang lain adalah pada saat Anda
memanggil suatu subrutin. Dalam hal ini, stack akan anda perlukan untuk
menyimpan lokasi di mana anda memanggil suatu subrutin. Alamat tersebut
nantinya akan digunakan untuk kembali lagi setelah subrutin selesai dieksekusi.
Pada saat program memanggila suatu subrutin, maka alamat tersebut akan didorong
ke stack menggunakan instruksi PUSH dan setelah subrutin selesai dieksekusi,
alamat dalam stack akan dimunculkan kembali menggunakan instruksi POP. Eksekusi
program kemudian akan dilanjutkan tepat pada instruksi setelah instruksi
pemanggilan subrutin berada.
RCALL subrutin1
[…] program selanjutnya berada
subrutin1: ;subrutin yang dipanggil1
[…]melakukan sesuatu
[…]ingin kembali
RET
Selama mengeksekusi instruksi RCALL sementara PC telah
di increment, alamat 16-bit didorong ke stack. Setelah Program menemukan
instruksi RET, isi program counter dimuati kembali dengan alamat dalam stack.
Penanganan interupsi hardware tidak mungkin dilakukan
tanpa stack. Interupsi akan memotong aliran eksekusi program, apapun yang
sedang dilakukan saat itu. Program lalau menuju pada rutin interupsi dan
mengeksekusinya. Setelah itu, program harus kembali ke lokasi sebelum
terjadinya interupsi untuk melanjutkan pekerjaannya. Hal ini memerlukan suatu
tempat penyimpanan sementara, yaitu stack.
Dalam menggunakan stack, seringkali terjadi berbagai
masalah, terutama bagi anda yang belum terbiasa dengan stack. Cara pintar untuk
menggunakan stack adalah menggunakan tanpa harus mengatur stack pointer. Karena
jika pointer ini diset ke nol “0” pada awal program, pointer akan menunjuk pada
register R0.
Kondisi Reset
Pad saat catu daya mikrokontroler ON, maka
mikrokontroler akan mulai bekerja dari kondisi reset. Program counter akan
menunjuk nilai nol dank ode pada alamat pertama memori program akan dieksekusi.
Selain pada saat pertama catu daya dihidupkan, terhadap beberapa keadaan yang
membuat PC bernilai nol dan masuk pada kondisi reset. Hal-hal berikut akan
membawa program dalam mikrokontroler masuk ke dalam kondisi reset:
1. Pada saat Power ON.
2. Saat reset eksternal
terjadi, yaitu ketika pin reset diaktifkan.
3. Pada saat Watchdog
Timer mencapai nilai maksimum (overflow). Watchdog Timer merupakan pewaktu
independent yang harus anda reset dari waktu ke waktu sebelum mencapai nilai
maksimum, jika tidak maka mikroprosesor akan restart.
Reset mokrokontroler yang diakibatkan oleh WDT terjadi
apabila WDT dienable melalui program dan isi WDT mencapai nilai maksimum. Dalam
kondisi default, WDT tidak aktif (disable). Untuk mengenabel WDT, diperlukan
suatu perintah terhadap port WDT. Sedangkan untuk mencegah terjadinya reset
akibat WDT mencapai nilai maksimum, maka Anda harus mengembalikan nilai WDT
menjadi nol sebelum mencapai nilai maksimum. Hal itu dapat anda lakukan dengan
menggunakan perintah WDR.
Sesudah eksekusi reset, yang akan membuat semua
register dan port dalam keadaan default, kode pada alamat 0000 akan dieksekusi.
Selama eksekusi, PC akan sudah di-incremen dank ode berikutnya akan dibaca
untuk dipegang pada fetch buffer. Teknik ini dinamakan fetch during execution.
Dengan teknik inilah satu clock osilator untuk setiap kode, kecuali untuk
percabangan dan lompatan. Ini sungguh sangat mengagumkan.
Perintah pertama yang akan dieksekusi selalu berlokasi
pada alamat 0000. Untuk mengatakan pad software assembler bahwa program yang
anda buat dimulau dari alamat ini, anda dapat menggunakan pengarah CSEG dan
ORG. Penulisannya dalam program adalah seperti berikut:
.CSEG
.ORG 0000
Pengarah yang pertama, menyatakan bahwa assembler
berada pada kode segment. Sedangkan pengarah ORG akan menyatakan, bahwa kode
setelah pengarah ini akan dituliskan pada alamat 0000 dalam Memori program.
Bila program anda akan dimulai dari alamat 0000, maka penggunaan kode pengarah
ini tidak mutlak. Anda dapat membuangnya tanpa akan mendapatkan error. Namun
bila anda menghendaki program anda dimulai pada alamat lain, maka anda perlu
menuliskannya. Pengarah ORG juga dapat anda gunakan pada saat Anda ingin
membuat suatu tabel data pada code segment.
Seperti telah disampaikan sebelumnya, kode pertama
selalu dimulai dari alamat 0000. Alamat 0000 ini juga disebut sebagai reset
vector yang merupakan alamat yang akandituju apabila terjadi kondisi reset.
Beberapa alamat sesudah reset vector merupakan interrupt vector, misalnya 0001,
0002, dan seterusnya. Interrupt vector merupakan alamat yang akan dituju
apabila interrupt diijinkan dan kemudianinterrupt tersebut terjadi. Posisi yang
disebut sebagai vector mungkin akan berbeda untuk masing-masing AVR dan
bergantung pada ada/tidaknya interrupt internal dan eksternal. Perintah untuk
menangani terjadinya interupsi harus ditempatkan pada vector interupsi. Bila
anda menggunakan interupsi, maka kode pertama dalam reset vector harus
merupakan perintah lompatan. Hal ini agar program yang anda jalankan tidak
mengeksekusi rutin interupsi sebelum terjadinya interupsi yang anda inginkan.
Demikian juga dengan perintah pada vector interrupsi, mestinya juga merupakan
perintah lompatan menuju rutin interupsi yang anda inginkan dan dapat anda
tempatkan pada bagaian lain program yang bukan merupakan vector interupsi.
Bentuk umum rangkaian program yang biasa digunakan
adalah sebagai berikut:
.CSEG
.ORG 0000
RJMP Start
RJMP Rutin_Int1
RJMP Rutin_Int2
[…disini merupakan tempat untuk vector interupsi
yang lain…]
Rutin_Int1:
[…berisi rutin program untuk penanganan
interupsi1…]
Rutin_Int2:
[…berisi rutin program untuk interusi2…]
[…Rutin Interupsi yang lain…]
Start: ; Di sini awal dari program anda
[…program utama anda berada…]
.EXIT
Perintah RJMP akan menghasilkan lompatan nemuju label
yang ditunjuk. Nama label diakhiri dengan tanda titik dua (“:”).
Aliran program dan linier, kecuali bila terdapat perubahan
rangkaian eksekusi. Perubahan rangkaian eksekusi dapat terjadi apabila program
menemukan kondisi percabangan atau interupsi. Percabangan merupakan hal yang
sangat lazim digunakan dan terjadi apabila salah satu kondisi dalam percabangan
bersyarat terjadi.
Sebagai contoh, anda hendak membuat pencacah 32-bit
menggunakan empat register, R1 hingga R4. Lest significant byte dalam R1
di-incrfemen. Setiap kali register R1 overflow saat operasi increment tersebut
(yakni ketika dicapai nilai 255+1=0), maka anda harus increment register R2.
Begitu seterusnya sampai R4.
Untuk melakukan increment pada saat register, anda
dapat menggunakan instruksi INC. Apabila terjadi overflow saat eksekusi INC R1,
bit Z (zero) dalam status register akan diset :1:. Bit C yang diset ketika
overflow tidak akan berubah selama eksekusi INC. Zero bit dan Zero-flag cukup
untuk anda gunakan untuk mendeteksi terjadinya overflow.
Bila Zero-bit diset, Anda harus melakukan eksekusi
increment tambahan terhadap register yang lain. Interupsi percabangan yang
dapat kita gunakan adalah BRNE ((Branch if Not Equal). Selain BRNE, terdapat
instruksi percabangan lain yaitu BRNZ (Branch if not Zero).
Penggalan program untuk melakukan pencacahan naik
32-bit tampak seperti berikut:
Count 32:
INC R1
BRNE Count 32
INC R2
BRNE Count 32
INC R3
BRNE Count 32
INC R4
Instruksi percabangan yang merupakan kebalikan BRNE
adalah BREQ (BRanch if Equal).
Untuk melakukan percabangan, anda tidak harus
menggunakan instruksi-instruksi percabangan di atas yang mengacu pada perubahan
Zero-bit. Anda dapat pula menggunakan bit status yang menggunakan bit status
yang lain untuk melakukan percabangan. Berikut ini instruksi-instruksi
percabangan dalam AVR.
Tabel 5.3 Daftar Instruksi percabangan dan Kondisi yang
mempengaruhinya.
INSTRUKSI
|
OPERAN
|
DESKRIPSI
|
FLAG
|
CPSE
|
Rd, Rr
|
Compare, skip if
Equal
|
Z
|
CP
|
Rd, Rr
|
Compare
|
Z
|
CPC
|
Rd, Rr
|
Compare with Carry
|
Z
|
CPI
|
Rd, k
|
Compare Register
with Immediate
|
Z
|
SBRC
|
Rr, b
|
Skip if Bit in
Register Cleared
|
Rr(b)
|
SBRS
|
Rr, b
|
Skip if Bit in
Register is Set
|
Rr(b)
|
SBIC
|
P, b
|
Skip if Bit in I/O
Register Cleared
|
I/O(P,b)
|
SBIS
|
P, b
|
Skip if Bit in I/O
is Set
|
I/O(P,b)
|
BRBS
|
S, k
|
Branch is status
Flag Set
|
S
|
BRBC
|
S, k
|
Branch is status
Flag Clear
|
S
|
BREQ
|
K
|
Branch if Equal
|
Z
|
BRNE
|
K
|
Branch if Not Equal
|
Z
|
BRCS
|
K
|
Branch if Carry Set
|
C
|
BRCC
|
K
|
Branch if Carry
Cleared
|
C
|
BRSH
|
K
|
Branch if Same or
Higher
|
C
|
BRLO
|
K
|
Branch if Lower
|
C
|
BRMI
|
K
|
Branch if Minus
|
N
|
BRPL
|
K
|
Branch if Plus
|
N
|
BRGE
|
K
|
Branch if Greater
or Equal, Signed
|
N,v
|
BRLT
|
K
|
Branch if Less Than
Zero, Signed
|
N,v
|
BRHS
|
K
|
Branch if
Half-carry Flag Set
|
H
|
BRHC
|
K
|
Branch if
Half-carry Flag Cleared
|
H
|
BRTS
|
K
|
Branch if T-Flag
Set
|
T
|
BRTC
|
K
|
Branch if T-Flag
Cleared
|
T
|
BRVS
|
K
|
Branch if Overflow
Flag is Set
|
V
|
BRVS
|
K
|
Branch if Overflow
Flag is Cleared
|
V
|
BRIE
|
K
|
Branch if Interrupt
Enabled
|
I
|
BRID
|
K
|
Branch if Interupt
Disabled
|
I
|
Pewaktuan Eksekusi Program
Salah satu kelebihan AVR disbanding mikrokontroler
yang lain terletak pada kecepatannya dalam melakukan eksekusi program. AVR
membutuhkan waktu satu siklus clock untuk melakukan eksekusi terhadap satu
instruksi. Bila AVR bekerja pada clock input 4 MHz, maka waktu satu siklus
instruksi adalah 250 ns, sedangkan bila anda memasang kristal osilator 10 MHz
sebagai clock input, waktu yang dibutuhkan untuk melakukan eksekusi sebuah
instruksi adalah 100 ns. Waktu yang dibutuhkan untuk mengeksekusi sebuah
instruksi dinamakan waktu siklus instruksi (instruction clyces time),
yang nilainya bergantung pada kristal yang anda gunakan sebagai input clock.
Namun demikian, Anda perlu tahu bahwa terdapat beberapa instruksi yang
membutuhkan lebih dari satu instruksi, yaitu untuk instruksi percabangan atau
lompatan. Instruksi-instruksi tersebut mungkin membutuhkan lebih dari satu
siklus instruksi (bila percabangan dilakukan).
Apabila anda ingin membuat program anda melakukan
dengan waktu yang tepat, mungkin anda akan memerlukan instruksi NOP. Instruksi
ini tidak akan mengerjakan apapun, kecuali hanya melewatkan waktu eksekusi
saja. Instruksi NOP (No Operation), dapat digunakan untuk membuat sebuah delay (tunda)
pendek.
Misalkan anda bekerja dengan clock input 4 MHz dan
Anda menghendaki delay 1 us, anda dapat menggunakan instruksi NOP dengan
menempatkannya dalam program sebanyak empat buah.
NOP
NOP
NOP
NOP
Namun bila Anda hendak membuat delay 1 ms, tidak
berarti anda harus menggunakan instruksi ini sebanyak 4000 buah. Hal ini merupakan
hal bodoh dan hanya menyita ruang dalam Memori Program. Yang perlu anda lakukan
hanyalah membuat sebuah delay menggunakan pencacah dan beberapa instruksi
percabangan. Dengan element tersebut, anda dapat membuat suatu loop dimana
waktu eksekusi yang diperlukan senilai dengan delay yang anda inginkan. Pada
prinsipnya, delay menggunakan loop dibangun menggunakan pencacah yang akan
menghitung naik atau turun dan apabila suatu kondisi di capai, maka program
akan keluar dari loop tersebut.
Sebagai contoh, kita akan membuat suatu delay
menggunakan pencacah 8-bit.
CLR R1
Count:
DEC R1
BREQ Count
Pencacah 16-bit juga dapat anda gunakan untuk membuat
suatu delay dengan tepat, misalkan seperti ini:
LDI ZH, HIGH (65535)
LDI ZH, LOW (65535)
Count :
SBWIN ZL, 1
BRNE Count
Hal yang perlu anda perhatikan dalam membangaun suatu
delay adalah menghitung berapa siklus instruksi yang dilakukan untuk
menyelesaikan delay tersebut. Hal ini terkadang cukup membuat kita pusing.
Ingat bahwa eksekusi instruksi lompatan dapat membutuhkan waktu lebih dari satu
siklus instruksi. Anda harus Melakukan kalkulasi terhadap semua proses
eksekusi, yang semestinya dapat dirumuskan dengan persamaan tertentu sesuai
dengan model loop yang anda buat. Apabila anda menginginkan kemudahan dalam menghitung
lama waktu delay, anda dapat mengujinya menggunakan simulator ataupun software
assembler/compiler yang dilengkapi dengan penghitung waktu instruksi.
Macro
Apabila anda sering menggunakan suatu runtun program
tertentu di dalam program anda, maka rangkaian program tersebut tidak harus
selalu anda tuliskan aetiap kali anda membutuhkannya. Anda dapat menjadikan
rangkaian program tersebut sebagai suatu makro dan anda dapat menggunakannya
beberapa kali dalam program seperti yang anda inginkan. Untuk menggunakan suatu
makro, Anda cukup memanggil namanya saja.
Makro merupakan suatu rangkaian kode program yang
berisi instruksi-instruksi, yang dapat anda gunakan kembali dalam program
dengan hanya menyebutkan namanya saja. Sebagai contoh, kita akan membuat sebuah
macro yang merupakan delay 1 us pada clock 4 MHz dengan nama Delay1.
.MACRO Delay1
NOP
NOP
NOP
NOP
.ENDMACRO
Makro tersebut dapat anda panggil bila anda
membutuhkan suatu delay 1 us tanpa harus menuliskan kembali kode program yang
sama.
[…instruksi-instruksi dalam program anda…]
Delay1
[…instruksi-instruksi berikutnya…]
Delay1 ;Anda kembali ingin menggunakan delay1
[…selanjutnya lagi…]
Dengan cara ini, maka delay1 yang berisi 4 buah NOP
akan dimasukkan dalam kode sumber program saat proses asembli/kompilasi.
Penggunaan macro, pada dasarnya hanya memberikan
kemudahan dalam penulisan dan editing kode sumber program yang Anda buat. Dari
segi software Asembler, penggunaan makro tidak memberikan keuntungan yang
berarti.
Apalagi ruang memori AVR anda cukup terbatas,
penggunaan makro tidak begitu dianjurkan. Dalam kasus ini, anda sebaiknya
menggunakan subrutin yang dapat anda panggil setiap waktu. Penggunaan subrutin
akan sangat menghemat ruang memori program pada AVR anda.
Terdapat perbedaan yang cukup mendasar antara macro
dan subrutin. Pada penggunaan macro, kode akan tetap dituliskan dalam Memori
Program setiap nama macro disebutkan, sedangkan pada subrutin hanya dituliskan
sekali dalam Memori Program. Bila subrutin hendak digunakan, yang perlu anda
lakukan adalah memanggilnya dengan instruksi RCALL.
Untuk subrutin delay 10 siklus instruksi, anda dapat
menuliskan rangkaian perintah berikut ini:
Delay10:
NOP
NOP
NOP
RET
Sebuah subrutin harus diawali dengan label dan
diakhiri dengan instruksi RET. Sedangkan RET1 digunakan untuk mengakhiri rutin
instrupsi.
Dalam contoh di atas subrutin Delay10 hanya terdapat 3
buah instruksi NOP dan sebuah instruksi RET. Sepuluh siklus eksekusi diperoleh
dari kalkulasi seluruh eksekusi instruksi tersebut. Sepuluh siklus instruksi
tersebut diperoleh dari 3 siklus untuk NOP dan 4 siklus untuk RET. Sisanya,
yaitu 3 siklus digunakan untuk memanggil subrutin tersebut.
Pemanggilan subrutin Delay10 dilakukan seperti berikut
ini:
[…perintah dalam program anda…]
RCAAL Delay10 ;memanggila subrutin Delay10
[…perintah lain berikutnya…]
Instruksi RCAAL berarti memanggil suatu alamat
relative tertentu, yaitu yang dinyatakan dengan label. Jarak relative
lompatandari pemanggilan ini akan dihitung oleh assembler/compiler. Pada penggunaan
subrutin, eksekusi program akan mengalami lompatan menuju subrutin yang
dipanggila dan setelah eksekusi terhadap subrutin tersebut selesai (menemukan
instruksi RET), aliran program akan kembali meneruskan perintah berikutnya.
Bila anda ingin menuju suatu lokasi tertentu dalam
program, anda dapat menggunakan instruksi RJMP (Relative JuMP). Instruksi RCAAL
dan RJMP merupakan instruksi percabangan tidak beryarat , yang akan mengalihkan
aliran program tanpa mensyaratkan kondisi tertentu. Instruksi RJMP tidak dapat
digunakan untuk memanggil suatu subrutin, karena instruksi ini tidak melakukan
pendorongan PC ke Stack yang mengakibatkan ketika ditemui instruksi RET,
program tidak dapat mendapatkan kembali nilai PC sebelumnya (saat eksekusi
RJMP).
Ilustrasi pemanggilan subrutin dengan RCALL dan
lompatan menggunakan RJMP ditunjukkan dalam gambar 5.4.
Gambar 5.4 Aliran Program Untuk
RCALL dan RJMP
Percabangan Bersyarat
Untuk melakukan percabangan dengan memperhatikan
kondisi tertentu, Anda perlu menggunakan instruksi percabangan bersyarat.
Instruksi percabangan bersarat akan melakukan lompatan menuju suatu rangkaian
program (berlabel) apabila suatu kondisi dipenuhi atau suatu kondisi tidak
dipenuhi. Apabila anda ingin memanggil suatu subruti, dengan syarat suatu
kondisi terepenuhi, anda dapat menggunakan perintah seperti berikut:
SBRC R1, 7 ; Lewati instruksi berikutnya bila
bit7, R1 “0”
RCALL Delay10 ; Panggil Rutin Delay10
[…instruksi berikutnya…]
Instruksi SBRC (Skip next instruction if Bit in
Register is Clear) merupakan instruksi pengecek kondisi bit dari suatu
register. Instruksi ini akan melewatkan satu instruksi berikutnya apabila bit b
yang register Rx berlogika nol (“0”). Dalam contoh diatas, RCALL hanya akan
dieksekusi apabila bit 7 dalam register R1 berlogika “0”. Bila bit 7 dalam
register R1 berlogika “1”maka perintah RCALL tidak akan dieksekusi, yang
berarti subrutin delay10 tidak akan dipanggil.
Bila anda menghendaki sebaliknya, yakni subrutin
Delay10 hanya dieksekusi apabila bit7 dalam register R1 berlogika “1”, maka
anda dapat menggunakan instruksi lai yang serupa, yaitu SBRS (Skip next
instruction if Bit in Register is Set).
Instruksi percabangan bersyarat yang lain adalah CPSE
(Compare two registers, and Skip next instruction if Equal).
CPSE R1, R2 ; Compare R1 and R2, skip if equal
RCALL SomeSubroutine ; Call SomeSubroutine
Anda juga dapat melakukan percabangan bersyarat
berdasarkan kondisi bit tertentu didalam port. Instruksi yang dapat anda
gunakan adalah SBIC atau SBIC. SBIC (Skip next instruction if Bit in I/O space
is Clear) berarti lewatkan satu instruksi berikutnya apabila bit b pada port
berlogika “0”. Sebaliknya, instruksi SBIS akan melewatkan satu instruksi
berikutnya apabila bit dalam port yang ditunjuk berlogika “1”.
SBIC PortB, 0 ; Lewati instruksi berikutnya bila
bit0, portB “0”
RJMP ATarget ; Jump to the label ATarget
Dalam contoh di atas, instruksi RJMP hanya akan
dieksekusi apabila bit 0 dalam portB berlogika “0”. Untuk menggunakan Instruksi
ini, anda perlu mengetahui, bahwa hanya 16 port pertama yang dapat dilakukan
akses per bit-nya.
Interupsi
Apabila anda membuat suatu aplikasi mikrokontroler,
tentunya anda menghendaki agar system tersebut dapat berinteraksi dengan dunia
luar. Sebagai contoh, anda tentu akan lebih menyukai aplikasi yang dapat
memberi respon ketika anda beri masukan dari pada salah satu pin, dari pada
system yang hanya melakukan suatu pola kegiatan yang tidak memperdulikan input
yang anda berikan. Agar suatu system aplikasi yang anda buat dapat bereaksi
terhadap input yang diberikan, anda dapat menggunakan metoda polling atau
dengan menggunakan interupsi. Metoda polling merupakan cara pengecekan terhadap
adanya suatu perubahan kondisi dengan menggunakan suatu loop. Dengan bahasa
yang labih sederhana, metoda polling akan mengecek suatu pin input atau status
lain secara rutin selama menjalankan pekerjaan utamanya. Namun bila masukan
yang terjadi berupa suatu pulsa yang sangat pendek, sedangkan periode
pengecekan dari polling relative lama, maka perubahan input tadi bisa saja
terdeteksi. Agar perubahan kondisi yang terjadi dapat selalu terdeteksi,
penggunaan interupsi merupakan solusi yang sangat tepat.
Interupsi dalam mikrokontroler merupakan cara
bagaimana system memberikan respon terhadap suatu kondisi masukan tertentu,
ketika proses pekerjaan sedang berlangsung. Apabila eksekusi dienable dan
terjadi suatu interupsi, system akan meninggalkan pekerjaannya, menuju pada
rutin penanganan interupsi yang sesuai dan setelah selesai selesai akan kembali
melanjutkan pekerjaan.
Interupsi dapat terjadi akibat dipicu oleh adanya
trigger dari input internal maupun interupsi eksternal hardware. Untuk
menggunakan interupsi dalam system aplikasi Anda, hal pertama harus anda
lakukan adalah enable interupsi flag. AVR memiliki satu bit untuk enable
interupsi yang bernama Interrupt Enable Flag. Bit ini dapat anda set dengan
menggunakan perintah SEI (Set Enable Interrupt).
Apabila kondisi terjadi, misalnya terjadi perubahan
pada bit port, mikrokontroler akan mendorong nilai PC ke dalam stack. Bila
tidak, maka mikrokontroler tidak akan dapat kembali pada lokasi ketika
terjadinya interupsi, yang berada dapat kembali pada lokasi ketika terjadinya
interupsi, ;yang dapat berada di mana saja dan kapan saja. Setelah
menyelesaikan rutin interupsi, program akan melanjutkan pekerjaannya.
Terjadinya interupsi, akan membawa program menuju vector interupsi yang sesuai.
Biasanya, kode yang terdapat dalam vector interupsi adalah intruksi JUMP untuk
mengarahkan program menuju rutin interupsi yang telah dibuat. Vector interupsi
merupakan lokasi kusus yang akan dituju oleh program apabila terjadi interupsi
dan hal itu tergantung pada komponen hardware dan kondisi-kondisi yang hendak
ditangani dengan interupsi. Semakin banyak komponen hardware dan kondisi interupsi
yang dapat ditangani, akan semakin banyak vector interupsi. Daftar vector
intrupsi untuk beberapa tipe AVR ditunjukkan dalam tabel berikut.
Dari data di atas tampak bahwa masing-masing tipe AVR
memiliki perbedaan kemampuan untuk menangani interupsi. Semakin atas, interupsi
di atas memiliki prioritas yang semakin tinggi. Apabila dua buah interupsi
terjadi bersamaan, maka interupsi dengan alamat yang lebih rendah yang akan
ditangani terlebih dahulu. Interupsi dengan vector interupsi yang lebih tinggi harus
menunggu sampai interupsi dengan prioritas lebih tinggi diselesaikan. Untuk
mendisable terjadinya interupsi yang lebih rendah, dalam rutin interupsi harus
memuat program untuk mendisable flag Interupsi.
Tabel 5.4 Interrupt Vector
Untuk Beberapa AVR
Name
|
Int Vector Adress
|
Triggered by …
|
||
2313
|
2323
|
8515
|
||
RESET
|
0000
|
0000
|
0000
|
Hardware Reset,
Power-on Reset, Watchdog Reset
|
INTO
|
0001
|
0001
|
0001
|
Level change on the
external INT0-Pin
|
INT1
|
0002
|
-
|
0002
|
Level change on the
external INT1-Pin
|
TIMER1 CAPT
|
0003
|
-
|
0003
|
Capture event on
Timer 1
|
TIMER1 COMPA
|
-
|
-
|
0004
|
Timer1= Compore A
|
TIMER1 COMPB
|
-
|
-
|
0005
|
Timer1= Compore B
|
TIMER1 COMP1
|
0004
|
-
|
-
|
Timer1= Compore 1
|
TIMER1 OVF
|
0005
|
-
|
0006
|
Timer1 Overflow
|
TIMER0 OVF
|
0006
|
0002
|
0007
|
Timer0 Overflow
|
SPI STC
|
-
|
-
|
0008
|
Serial transmit
complete
|
UART RX
|
0007
|
-
|
0009
|
UART char in
receive buffer available
|
UART UDRE
|
0008
|
-
|
000A
|
UART transmitter
ran empty
|
UART TX
|
0009
|
-
|
000B
|
UART All sent
|
ANA COMP
|
-
|
-
|
000C
|
Analog Comparator
|
Bila pekerjaan dalam rutin interupsi telah selesai,
dalam bagian akhir rutin interupsi harus terdapat kode untuk mengenabel kembali
flag tersebut. Untuk mengeset ulang bit I-status dapat dilakukan dengan dua
cara. Yang pertama dengan instruksi RETI dan yang kedua menggunakan SEI/RET.
RETI
Atau dengan
SET
RET
Cara kedua berbeda dengan cara pertama. Untuk
mudahnya, sebaiknya anda gunakan cara pertama, yaitu dengan RETI.
Hal yang sangat
penting dalam penggunaannya adalah bagaimana mengamankan data dan
kondisi-kondisi sebelum terjadinya interupsi, sehingga tidak hilang atau
berubah akibat pelaksanaan rutin interupsi. Oleh karena itu, instruksi pertama
dalam rutin interupsi harus merupakan instruksi untuk menyimpan isi register ke
stack dan instruksi terakhir sebelum RETI merupakan instruksi untuk memunculkannya
kembali dari stack.
.CSEG ; Code_Segment, mulai di sini
.ORG 0000 ; Alamat 0000
RJMP Mulai ; Reset vector 0000
RJMP IService ; 0001=Int-Vektor pertama, INTO
service routine
[…] vector interupsi yang lain
Mulai: ; Di sini program utama dimulai
[…]
IService: ; Here we start with the
Interrupt-Service-Routine
PUSH R16; save a registr to stack
IN R16, SREG; read status register
PUSH R16; and put on stack
[…] Here the Int-Service-Routine does something
and uses R16
POP R16; get previous flag register from stack
OUT SREG,R16 ; restore old status
POP R16; get previous content of R16 from the
stack
RETI ; and return from int
Komentar
Posting Komentar