SubBAB 3-1: Instruksi-instruksi LOOP dan JUMP
Pada
subBAB ini pertama kita akan mendiskusikan bagaimana membuat proses
looping pada 8051. Selanjutnya kita akan mendiskusikan
instruksi-intruksi jump, baik unconditional maupun conditional.
Looping pada 8051
Mengulang aliran/urutan instruksi beberapa kali, disebut loop.
Loop adalah salah satu yang paling sering dikerjakan oleh banyak CPU,
termasuk 8051. Pada 8051, proses loop dapat dilakukan dengan berbagai
cara. Misalnya …
…
ULANG: NOP
SJMP ULANG
Pada contoh di atas program akan terus berulang mengeksekusi NOP dan SJMP. Demikian pula …
…
TUNGGU: NOP
JB P1.2,TUNGGU
…
program
akan terus mengeksekusi NOP dan JNB sampai bit P1.2 menjadi 0s
(rendah). Nah, dua contoh di atas adalah penggunaan loop dengan jumlah
pengulangan tidak terbatas, dan bisa berlangsung terus menerus. Namun
ada kalanya kita menginginkan loop dengan jumlah tertentu. Hal tersebut
dapat dilakukan dengan menggunakan instruksi “DJNZ reg,label“. Instruksi ini akan men-decrement register (operand reg), jika setelah itu nilai register tidak nol, maka program akan melompat ke alamat yang ditunjuk oleh label.
Sebelum memulai loop, tentu register terlebih dahulu diisi dengan nilai
counter atau cacahan untuk menentukan berapa kali pengulangan yang
diinginkan. Dengan kata lain nilai dari reg adalah jumlah pengulangan.
Ingat bahwa dengan DJNZ, perintah decrement, pengujian register, dan
lompat telah diringkas menjadi satu instruksi saja. Sesuatu yang sangat
efisien.
Contoh 3-1
|
Tulis pogram untuk
(a) men-Clear-kan ACC, lalu
(b) tambahkan 3 pada akumulator sebanyak 10 kali.
Jawaban:
;program ini untuk menambah 3 pada akumulator sebanyak 10 kali
MOV A,#0 ;A=0 , clear ACC
MOV R2,#10 ;isi pencacah R2 = 10
ULANG: ADD A,#3 ;Tambah 3 pada ACC
DJNZ R2,ULANG ;ulagi samppai 10 kali
MOV R5,A ;Simpan hasilnya pada ACC
|
Dalam program seperti contoh 3-1, register R2 digunakan sebagai counter. Counter pertama-tama di-set menjadi 10. Setiap iteration
(ind: sekali aliran dalam loop), instruksi DJNZ akan men-decrement R2
dan memeriksa nilainya. Jika R2 tidak 00, maka program akan melompat
pada alamat tujuan, dalam hal adalah label “LAGI“.
Loop akan berlangsung terus sampai R2 = 00. Setelah R2 = 00 program
akan mengalir keluar dari loop dan mulai mengeksekusi instruksi dibawah
DJNZ, dalam hal ini adalah instruksi “MOV R5,A“.
Ingat
dalam menggunakan DJNZ, semua register dapat digunakan sebagai counter,
yaitu R0 s/d R7, termasuk juga semua lokasi RAM dan SFR (direct
addressing). (Dijelaskan pada BAB 5).
Contoh 3-2
|
Berapa jumlah maksimum pengulangan yang bisa dilakukan di Contoh 3-1.
Jawaban:
R2
bertindak sebagai pencacah (counter), dan R2 adalah register 8-bit,
yang dapat menampung nilai maksimal FFh (255 desimal). Sehingga jumlah
pengulangan yang bisa dilakukan contoh 3-1 adalah sebanyak 256 kali.
|
Loop di dalam Loop
Seperti
yang tunjukkan pada contoh 3-2, bahwa cacahan maksimum dari counter
adalah 256. Lalu bagaimana jika kita membutuhkan cacahan/pengulangan
yang lebih besar/banyak dari 256? Untuk melakukan hal itu, kita
menggunakan loop di dalam loop, dalam bahasa jawa disebut nested loop. Kita menggunakan dua (atau lebih) register untuk digunakan sebagai counter. Lihat contoh 3-3.
Contoh 3-3
|
Tulis program untuk (a) mengisi akumulator dengan nilai 55h, dan (b) complement nilainya sebanyak 700 kali.
Jawaban:
Karena
700 adalah lebih besar dari 255 (kapasitas maksimum untuk semua
register), maka kita menggunakan dua register untuk membuat cacahan.
Kode berikut adalah menunjukkan bagaimana menggunaakn R2 dan R3 sebagai
cacahan.
MOV A,#55h ;A=55h
MOV R2,#10 ;isi pencacah R2 = 10 (loop terluar)
LAGI: MOV R3,#70 ;isi pencacah R3 = 70 (loop terdalam)
ULANG: CPL A ;Complement ACC
DJNZ R3,ULANG ;ulangi sampai 70 kali
DJNZ R2,LAGI ;ulalgi loop luar sampai 10 kali
Dalam program ini R2 digunakna sebagai pencacah pada loop terdalam. Pada instruksi “DJNZ R2,ULANG”, jika R2 kemudian menjadi 0
|
Conditional Jump lainnya
Ringkasan
dari conditional jump pada 8051 dapat kita lihat pada table 3-1.
Penjelasan lebih mendetil dapat kita lihat pada lampiran A. Harap
dicatat bahwa semua perintah conditional jump
ini, seperti JZ (lompat jika A=0), atau JC (lompat jika CY=1), akan
membuat program melompat pada lokasi yang ditunjuk hanya .., sekali lagi
hanya, kondisi yang diminta terpenuhi. Selanjutnya kita akan membahas
beberepa instruksi jump tersebut agar lebih mudah dipahami.
Tabel 3-1: Instruksi Lompat Bersyarat pada 8051
Instruksi Penjelasan
JZ Rel Lompat jika A = 0
JNZ Rel Lompat jika A <> 0
DJNZ Rn,Rel Decrement Rn, lompat jika Rn <> 0
DJNZ direct,Rel Decrement direct, lompat jika direct <> 0
CJNE A,direct,Rel Lompat jika A <> direct byte
CJNE A,#data,Rel Lompat jika A <> data byte
CJNE Rn,#data,Rel Lompat jika Rn <> data byte
CJNE @Ri,#data,Rel Lompat jika @Ri <> data byte
JC Rel Lompat jika CY = 1
JNC Rel Lompat jika CY = 0
JB Bit,Rel Lompat jika Bit = 1
JNB Bit,Rel Lompat jika Bit = 0
JBC Bit,Rel Lompat jika Bit = 1 , lalu Clear Bit
JZ (jump if A=0)(lompat jika A=0)
Instruksi ini akan memeriksa isi A. Jika dia 00, maka program akan melompat ke alamat yang ditunjuk. Misalnya seperti ini.
MOV A,R2 ;A=R2
JZ LABEL1 ;lompat jika A = 0
MOV A,R1 ;A=R1
JZ LABEL1 ;lompat jika A = 0
…
LABEL1:
Pada program ini, jika isi R2 atau R1 adalah 00, maka program akan melompat ke label “LABEL1“.
Ingat instruksi JZ ini hanya berlaku untuk register A. Instruksi ini
juga tidak perlu dilengkapi dengan operand apapun dalam syntax-nya. Hal
ini karena operand A sudah terintegrasi dalam instruksi JZ itu sendiri.
Lihat contoh 3-4. Bagaimana jika kita hendak menggunakan register yang
lain? Tentu bisa, hanya saja bukan dengan instruksi JZ dan JNZ, dan oleh
karena itu gunakan instruksi yang lain, misalnya CJNE.
Contoh 3-4
|
Tulis program untuk memastikan jika isi R5 adalah 0, jika ya maka isi dengan 55h.
Jawaban:
MOV A,R5 ;Salin isi R5 ke A
JNZ LANJUT ;Lompat jika A <> 0
MOV R5,#55h ;isi dengan 55h
LANJUT: …
|
JNC (jump if no carry, jump if CY=1)(lompat jika CY=1)
Instruksi
ini, menggunakan bendera Carry sebagai menentu keputusan dalam jump.
Saat mengeksekusi “JNC LABEL”, CPU akan memeriksa status bendera
Carry(CY). Jika CY=1, maka program akan melompat ke alamat yang
ditunjuk. Namun jika CY=0, maka program akan mengeksekusi instruksi
selanjutnya dibawah JNC tersebut.
Bagaimana dengan Bit yang lain selain carry. Kita dapat menggunakan JB atau JNB yang akan kita diskusikan pada bab 4 dan 8.
Contoh 3-5
|
Cari jumlah dari niai 79h, F5h, dan E2h. Simpan hasilnya pada register R0 (low byte) dan R5 (high byte).
Jawaban:
MOV A,#0 ;Clear A, A= 0
MOV R5,A ;Clear R5
ADD A,#79h ;A = 0 + 79h = 79h
JNC LANJUT ;Jika tidak lebih, ke angka dua
INC R5 ;Jika lebih, Inc R5
LANJUT: ADD A,#0F5h ;A = 79h + F5h = 6Eh dan CY=1
JNC TERUS ;Jika tidak lebih, ke angka tiga
INC R5 ;Jika lebih, Inc R5
TERUS: ADD A,#E2h ;A = 6Eh + E2h = 50h dan CY=1
JNC SELESAI ;Jika tidak lebih, selesai
INC R5 ;Jika lebih, Inc R5
SELESAI: MOV R0,A ;Sekarang R0=50h dan R5=02
|
Semua Conditional Jump masuk dalam golongan lompat jarak pendek (SJMP)
Harap
dicatat bahwa semua lompat bersyarat pada 8051 adalah termasuk golongan
lompat jarak pendek. Sehingga alamat yang dituju haruslah tidak lebih
jauh -128 byte ke belakang s/d +127 byte ke depan. Nilai tersebut
relatif terhadap alamat pada instruksi pertama dibawah instruksi lompat
bersyarat tersebut. Konsep yang sangat penting pada lompat jarak pendek
tersebut akan dibahas pada akhir bab ini.
Instruksi-instyruksi Lompat tidak beryarat
UnConditional Jump
adalah memindahkan kontrol program ke tempat lain tanpa syarat kondisi
apapun. Pada 8051 ada tiga instruksi untuk Unconditional Jump ini, yaitu
LJMP (long Jump / lompat jauh), AJMP (Absolute Jump), dan SJMP (short
Jump /lompat pendek).
LJMP (long Jump)
LJMP
adalah lompat jarak jauh tanpa syarat kondisi. Disebut juga sebagai
Jump 16-bit. Ini adalah instruksi 3-byte, di mana byte pertama adalah
opcode, sedang dua byte yang lain adalah representasi dari alamat 16-bit
yang dituju. Dengan 2 byte ini kontrol program dapat dipindahkan ke
semua alamat memory program yang bisa dijangkau 8051 yakni alamat
16-bit, mulai dari alamat 0000 s/d FFFFh.
Seperti
yang kita tahu bahwa Program Counter (PC) adalah register khusus 16-bit
juga. Dua byte dibelakang opcode langsung disalin ke PC. Namun dalam
prakteknya tidak semua alamat-alamat tersebut digunakan Chip 8051 untuk
menyimpan memory program. Misalnya AT89C51 (buatan ATMEL) yang hanya
memiliki memory program sebesar 4 kb, yang diletakkan pada lokasi memory
program 0000 s/d 0FFFh. Sehingga dalam membuat program kita sebaiknya
memperhatikan dengan seksama alamat yang dituju saat menggunakan
instruksi LJMP ini, jangan sampai progam melompat ke alamat kosong.
Namun
8051 juga memiliki Unconditional Jump lain yang lebih efisien, yaitu
SJMP dan AJMP, dimana instruksi-instruksi ini hanya membutuhkan 2 byte.
Sehingga dapat menghemat penggunaan memory jauh lebih baik dari pada
LJMP.
AJMP (Absolute JUMP)
AJMP
ini adalah lompat tidak bersyarat jarak menengah. Disebut juga sebagai
Jump 11-bit. Ini adalah instruksi 2-byte. Prosesnya adalah mirip dengan
LJMP namum hanya dibatasi pada blok 2 kb yang sama dari nilai PC setelah
instruksi AJMP. Maksudnya alamat instruksi tepat di bawah AJMP, dan
alamat label yang dituju, harus berada pada blok 2 KB yang sama.
Misalnya jika setelah alamat instruksi di bawah instruksi AJMP berada di
antara 0000 s/d 07FFh maka alamat yang dituju juga harus di antara
alamat tersebut (blok 2 KB yang sama). 11-bit dalam instruksi ini
langsung disalin ke PC. Instruksi ini sama efisiennya dengan SJMP dalam
penggunakan memory, karena hanya menggunakan 2-byte.
SJMP (short Jump)
SJMP
adalah lompat tanpa syarat jarak pendek. Disebut juga sebagai Jump
relatif 8-bit. Ini adalah instruksi 2 byte. Byte pertama adalah opcode,
sedang byte lainnya adalah alamat relatif yang dituju. Ya alamat
relatif, sehingga nilai pada byte ke dua ini bukan representasi dari
alamat yang dituju, melainkan nilai relatif terhadap nilai PC saat itu.
Sangat berbeda dengan LJMP (atau juga AJMP) yang byte dibelakang opcode
adalah benar-benar nilai dari alamat yang dituju. Apa maksudnya relatif ?
jika nilai dari byte kedua (byte tujuan) adalah lebih kecil dari 80h,
maka alamat tujuan adalah nilai PC setelah instruksi SJMP, ditambah byte
ini. Namun jika nilai byte ini sama atau lebih besar dari 80h maka
alamat tujuan adalah nilai PC saat setelah instruksi SJMP dikurangi
nilai 2′s complement dari byte tujuan (Lihat BAB 6 untuk bahasan 2′s
Complement dan bilangan bertanda). Atau dengan kata lain, byte ke dua
ini seperti bilangan bertanda, memiliki nilai positif atau negatif
antara -128 s/d +128. Dengan demikian alamat tujuan harusnya sama atau
diantara, minimal -128 dari nilai PC, atau maksimal +128 dari nilai PC.
Sama
seperti AJMP, instruksi SJMP ini adalah instruksi 2-byte, sehingga
dalam penggunaan memory program menjadi lebih hemat 1-byte dibanding
LJMP. Dalam penulisan program dengan bahasa Assembler, kita akan
diberitahu jika ternyata alamat tujuan di luar jangkauan. Jika ini
terjadi maka instruksi dapat digantikan dengan instruksi AJMP atau LJMP.
Menghitung alamat SJMP
Selain
Instruksi SJMP, semua conditional Jump semacam JNC, JZ dan DJNZ juga
menggunakan mekanisme yang sama dalam menentukan alamat tujuan. Alamat
tujuan adalah relatif terhadap nilai PC setelah instruksi yang
berkaitan. Untuk menghitung alamat tujuan, byte alamat dari instruksi
akan langsung ditambahkan pada PC. Byte alamat ini seperti bilangan
bertanda, sehingga jika nilainya positif maka program akan melompat
dibawah instruksi Jump. Namun jika nilainya negatif maka program akan
melompat di atas instruksi Jump. Untuk bilangan bertanda akan dibahas
pada BAB 6. Untuk lebih jelas akan SJMP silahkan lihat contoh 3-6
Contoh 3-6
|
Menggunakan file List berikut ini, periksalah perhitungan alamat lompat ke depan (forward jump).
Lokasi Opcode Baris Instruksi
0000 1 ORG 0000
0000 7800 2 MOV R0,#0
0002 7455 3 MOV A,#55h
0004 6003 4 JZ LANJUT
0006 08 5 INC R0
0007 04 6 LAGI: INC A
0008 04 7 INC A
0009 2477 8 LANJUT: ADD A,#77h
000B 5005 9 JNC SELESAI
000D E4 10 CLR A
000E F8 11 MOV R0,A
000F F9 12 MOV R1,A
0010 FA 13 MOV R2,A
0011 FB 14 MOV R3,A
0012 2B 15 SELESAI: ADD A,R3
0013 50F2 16 JNZ LAGI
0015 80FE 17 TUNGGU: SJMP TUNGGU
0017 19 END
Jawaban:
Pertama harap diperhatikan bahwa instruksi JZ dan JNCkeduanya di atas adalah lompat ke depan (jump forward). alamat target untuk jump forward
dihitung dengan cara menambahkan PC di instruksi berikutnya, dengan
byte ke dua dari instruksi short jump, di mana disebut relative address.
Pada baris ke 4 instruksi “JZ LANJUT”
memiliki opcode 50h dan operan 03h pada alamat 0004h dan 0005h. 03h
adalah alamat relatif, relatif terhadap alamat dari instruksi setelah
instruksi jump itu sendiri, yaitu “INC R0” dengan alamat 0006. Tambahkan 0006h dengan 03h hasilnya 0009h, inilah alamat sebenarnya. Hal yang sama juga terjadi pada “JNC SELESAI“.
Instruksi ini memiliki opcode 50h dan Operand 05h. 05h adalah alamat
relatif terhadap instruksi berikutya. Yaitu alamat 000Dh untuk instruksi
“CLR A“, kita mendapat 000Dh + 05h = 0012h. Inilah alamat target sebenarnya untuk label SELESAI.
|
Contoh 3-7
|
Periksalah perhitungan lompat ke belakang pada contoh 3-6.
Jawaban:
Dalam
List program kita mendapatkan instruksi “JNC LAGI” memiliki opcode 50h
dan alamat relatif F2h ditambahkan dengan alamat instruksi setelah JNC,
yaitu 0015h. Perhitungannya adalah demikian, karena alamat relatif ini
merupakan bilangan bertanda maka cara penjumlahan yang sedikit berbeda
akan kita ambil. Bilangan word PC, yaitu 0015h kita ambil low byte saja,
yaitu 15h. Sehingga kita akan mendapatkan 15h + F2h = 107h. Sekali lagi
kita ambil low byte saja, sehingga kita mendapatkan 07h. Nilai ini yang
kemudian kita sandingkan dengan high byte pada PC, menjadi 0007h.
Akhirnya, inilah alamat target dari “JNC LAGI”.
|
Perhitungan Jump dengan tujuan ke belakang
Dalam
hal lompat ke depan (jump forward) nilai alamat relatif dapat langsung
ditambahkan pada PC, yaitu nilai antara 00h s/d 7Fh, atau 0 s/d 127
desimal. Untuk lompat kebelakang (jump backward) nilai dari alamat
relatif tersebut adalah -1 s/d -128.
Tentu
saja CPU harus dapat membedakan mana yang lompat ke depan, dan mana
yang lompat ke belakang. Dalam 8051, semua lompat relatif jarak pendek
tidak ada yang melebihi jangkauan -128 s/d +127 dari alamat sesudah
instruksi lompat tersebut. Jika kita memaksa untuk lompat diluar
jangkauan tersebut, Assembler akan memberitahukan pada kita lewat file
List, tentang adanya kesalahan ini (DESTINATION ADDRESS OUT OF RANGE FOR
RELATIVE REFERENCE).
SubBAB 3-2: INSRUKSI CALL
Instruksi
pemindahan kontrol program yang lainnya adalah CALL, di mana instruksi
ini digunakan untuk memanggil sebuah subrutin. Sebelumya akan kita
perjelas apa itu subrutin. Subrutin adalah sekumpulan/blok kode
instruksi yang memiliki tugas tertentu. Kumpulan instruksi dalam
subrutin tersebut bisa digunakan atau dijalankan dengan cara memanggil
(CALL). Bahkan bisa dipanggil dari mana saja secara berulang-ulang.
Dalam bahasa Assembler subrutin diawali oleh Label, yakni nama untuk
dipanggil, dan diakhiri oleh instruksi RET. Dengan menggunakan subrutin
kita tidak perlu untuk mengetikkan dua buah (atau lebih) blok kode yang
sama. Kita cukup membuat satu blok kode dalam sebuah subrutin, yang
kemudian bisa dipanggil (call) untuk dijalankan. Hal ini tidak saja
membuat program kita lebih rapi dan terstruktur, namun dapat menghemat
penggunaan memory program jauh lebih efisien. Untuk perintah CALL ini,
pada 8051 menyediakan dua buah instruksi, yaitu LCALL (long call) untuk
memanggil subrutin pada jarak jauh, sedang ACALL (Absolute Call) adalah
digunakan untuk memanggil subrutin dengan jarak menengah. 8051 tidak
menyediakan CALL jarak pendek.
LCALL (long call)
Ini
adalah instruksi 3-byte. Byte pertama adalah opcode, sedang 2-byte
lainnya adalah alamat 16-bit yang dituju. Saat instruksi LCALL ini
dijalankan, CPU tidak lagi mengeksekusi instruksi-instruksi di bawah
LCALL, namun segera melompat pada alamat yang dituju. Namun berbeda
dengan LJMP yang hanya melompat begitu saja. Sementara itu LCALL
digunakan untuk menjalankan blok rutin di tempat lain sampai selasai,
dan kemudian kembali menjalankan instruksi-instruksi dibawah instruksi
LCALL tadi, yang sempat ditinggalkannya.
Setelah
LCALL dieksekusi, dan CPU hendak melompat ke alamat yang dituju,
sebelumnya CPU akan menyimpan alamat kode dari instruksi yang letaknnya
persis di bawah LCALL tersebut, ke dalam stack memory. Dan kemudian CPU
akan mengeksekusi blok rutin yang dipangggil sampai selesai, yakni
dijalankannya instruksi RET (return) yang menandakan akhir dari
subrutin. Ingat setiap subrutin selalu diakhiri oleh instruksi RET.
Kemudian CPU akan mengambil alamat kode program yang tadi disimpan dalam
stack memory, diletakkan pada PC, dan kembali menjalankan kode mulai
dari alamat PC yang baru, yakni alamat kode instruksi persis di bawah
LCALL tadi.
Saya
jelaskan sekali lagi. Saat subrutin hendak dijalankan, CPU akan
menyimpan PC (Progam Counter) ke dalam stack, kontrol program akan
berpindah ke alamat subrutin yang dituju, dan kemudian subrutin yang
letakknya ditempat yang jauh itu segera dijalankan. Sampai kemudian CPU
menemukan instruksi RET, yang berarti akhir dari subrutin, maka CPU
mengembalikan kontrol program yang tadi ditinggalkannya.
Beberapa hal yang harus kita perhatikan untuk program pada contoh 3-8.
1. perhatikan subrutin DELAY. Setelah instruksi “LCALL DELAY” dibaca dan hendak dijalankan, alamat pada instruksi tepat di bawahnya, yakni “MOV A,#044h“, di simpan (push) ke dalam stack. Kemudian CPU mulai menjalankan instruksi mulai dari alamat 300h.
2. Pada
subrutin DELAY, pertama counter R5 di-set menjadi 255 (R5=FFh);
sehingga, loop akan diulang 256 kali. Saat R5 menjadi 0, kontrol menuju
instruksi RET, di mana dengan mengeksekusi RET ini alamat yang tadi
disimpan pada stack, dikeluarkan lagi (pop) dan diletakkan kembali pada
PC (program Counter). PC kemudian menunjukan alamat instruksi tepat di
bawah CALL. Dan program dilanjutkan kembali.
Contoh 3-8
|
Tulis
program yang men-toggle (bolak-balik) setiap bit pada port 1 dengan
menuliskan 55h dan Aah secara terus menerus. Gunakan tundaan waktu
setiap setelah menulis port. Program ini dugunakan untuk menguji port
pada 8051 yang akan dibahas pada bab selanjutnya.
Jawaban:
ORG 0
LAGI: MOV A,#55h ;Isi A dengan 55h
MOV P1,A ;salin A pada P1
ACALL DELAY ;Tunda
MOV A,#0AAh ;Isi A dengan AAh
MOV P1,A ;salin A pada P1
ACALL DELAY ;Tunda
SJMP LAGI ;Ulang
;——Tundaan Waktu
ORG 300h
DELAY:
MOV R5,#0FFh ;R5 = FFh sebagai pencacah counter
ULANG: DJNZ R5,ULANG ;Tunggu sampai R5 = 0
RET
END
|
Berapa
waktu tundaan yang bisa kita dapatkan tergantung dari frekuensi
osilator kristal pada system 8051. Untuk menghitung waktu yang benar
akan kita bahas lebihjelas pada BAB 4. Namun bagaimanapun juga, kita
dapat menambah waktu tundaan dengan menggunakan nested loop seperti yang
ditunjukkan di bawah ini.
TUNDAAN: ;pangkal(nested) dari subruitn
MOV R4,#255 ;R4 = 255 (FFh)
LANJUT: MOV R5,#255 ;R5 = 255 (FFh)
ULANG: DJNZ R5,ULANG ;tetap di sini smp R4=0
DJNZ R4,ULANG ;decrement R4
;isi terus R5 smp R4=0
RET ;kembali ke pemanggil
Instruksi CALL dan aturan dalam Stack
Stack
dan pointer-nya akan kita bahas lebih menyeluruh pada bab 5. Namun untu
sedikit lebih mengerti isi stack pada CPU, kita akan pelajari isi stack
dan pointernya untuk contoh 3-8. Yang ditunjukkan pada contoh 3-9.
Contoh 3-9
|
Periksalah isi stack setelah mengeksekusi LCALL pertama dalam program dibawah ini.
jawaban:
Lokasi Opcode Baris Instruksi
0000 1 ORG 0
0000 7455 2 LAGI: MOV A,#55h ;anu
0002 F590 3 MOV P1,A ;anu
0004 120300 4 ACALL DELAY ;anu
0007 74AA 5 MOV A,#0AAh ;anu
0009 F590 6 MOV P1,A ;anu
000B 120300 7 ACALL DELAY ;anu
000E 80F0 8 SJMP LAGI ;anu
0010 9
0010 10 ;——Tundaan Waktu
0300 11 ORG 300h
0300 12 DELAY:
0300 7DFF 13 MOV R5,#0FFh ;anu
0302 DDFE 14 ULANG: DJNZ R5,ULANG ;anu
0304 22 15 RET
0305 16 END
Saat
mengeksekusi LCALL pertama, alamat dari instruksi “MOV A,#0Aah”
kemudian disimpan ke dalam stack. Perhatikan yang pertama disimpan
adalah lowbyte baru kemudian high byte. Instruksi terakhir dari
rutinyang dipanggil haruslah instruksi RET, dimana akan membuat CPU
mengambil 2-byte (POP) dari stack ke dalam PC dan kemudian kembali
menjalankaninstruksi mulai dari alamat 0007h. Diagram dibawah
mengilustrasikan bingkai stack setelah LCALL.
|
Menggunakan instruksi PUSH dan POP di dalam Subrutin
Saat
kita memanggil subrutin dengan instruksi CALL, memory stack akan
menyimpan alamat di mana CPU akan kembali setelah menjalankan subrutin.
Dengan demikian, kita harus sangat-sangat hati-hati saat hendak
memanipulasi isi memory stack. Aturannya adalah bahwa jumlah perintah
PUSH harus sama dengan jumlah POP yang keduanya digunakan dalam
subrutin. Dengan kata lain di dalam subrutin, dimana ada PUSH di situ
haruslah ada POP. Lihat contoh 3-10.
Kecuali
kita benar-benar mengerti apa yang kita lakukan dan tahu resikonya,
tidak dilarang bagi kita untuk mengubah stack. Sekali lagi, hanya jika
kita benar-benar mengerti apa yang kita kerjakan.
Memanggil subrutin
Dalam
pemrograman bahasa assembler, biasanya kita memiliki satu rutin utama
dan beberapa subrutin. Program utama dijalankan begitu CPU dinyalakan.
Dan dalam perjalanannya program utama bisa memanggil subrutin-subrutin
tersebut sekali maupun berulang-ulang untuk tujuan tertentu yang sudah
ditentukan. Selanjutnya kita dapat menempatkan subrutin-subrutin
tersebut sebagai modul-modul terpisah, dan bahkan disimpan pada file
terpisah. Dengan demikian program akan terlihat menjadi sangat ringkas
dan terstruktur dengan baik. Dan jika ada kesalahan program, kita dapat
melokalisasi kesalahan tersebut dengan mudah. Dalam prakteknya
modul-modul tersebut dapat digunakan sebagai modul-modul untuk program
yang lain. Sehingga akan dapat mempersingkat waktu perancangan program
lainnya.
Contoh 3-10
|
Periksa stack untuk instruksi LCALL pertama dari program di bawah ini.
LOC OBJ LINE INSTRUCTION
0000 1 ORG 0
0000 7455 2 LAGI: MOV A,#55h ; A = 55h
0002 F590 3 MOV P1,A ; P1 = A
0004 7C99 4 MOV R4,#99h ;..
0006 7D67 5 MOV R5,#67h ;..
0008 120300 6 ACALL DELAY ;Tunda
000B 74AA 7 MOV A,#0AAh ; A = AAh
000D F590 8 MOV P1,A ; P1 = A
000F 120300 9 ACALL DELAY ;Tunda
0012 80EC 10 SJMP LAGI ;Ulang terus
0014 11 ;——Tundaan Waktu
0300 12 ORG 300h
0300 C004 13 DELAY: PUSH 4 ;Push R4
0302 C005 14 PUSH 5 ;Push R5
0304 7CFF 15 MOV R4,#0FFh ;R5 = FFh
0306 7DFF 16 LANJUT: MOV R5,#0FFh ;R5 = 255
0308 DDFE 17 ULANG: DJNZ R5,ULANG ;..
030A DDFA 18 DJNZ R4,LANJUT ;..
030C D005 19 POP 5 ;Pop to R5
030F D004 20 POP 4 ;Pop to R4
0310 22 21 RET
0311 22 END
Jawaban:
Perhatikan bahwa untuk instruksi
PUSH dan POP operand yang digunakan hanya, sekali lagi hanya register
dengan pengalamatan langsung (Direct Address). Dan ini bingkai stack
setelah instruksi LCALL pertama.
|
Harap
diingat bahwa saat menggunakan LCALL, alamat tujuan dapat berada di
semua alamat yang bisa dijangkau 8051, yakni 0000 s/d FFFFh. Hal yang
berbeda jika menggunakan instruksi call yang lain, ACALL, di mana akan
dijelaskan nanti.
;—- PROGAM UTAMA MEMANGGIL SUBRUTIN
MULAI: LCALL SUBRUTIN_1
LCALL SUBRUTIN_2
LCALL SUBRUTIN_3
SELESAI: SJMP SELESAI
;——-Akhir dari Rutin Utama
SUBRUTIN_1: …
…
RET
;——-Akhir dari Sub Rutin 1
SUBRUTIN_2: …
…
RET
;——-Akhir dari Sub Rutin 2
SUBRUTIN_3: …
…
RET
;——-Akhir dari Sub Rutin 3
|
Gambar 3-1: Ilustrasi bagaimana Program memanggil subrutin-subrutin pada Assembler 8051.
ACALL (absolute call)
ACALL
adalah instruksi 2-byte yang berbeda dengan LCALL yang merupakan
instruksi 3-byte. Alamat tujuan dalam instruksi ACALL haruslah berada
dalam blok 2 KB yang sama dari perintah ACALL tersebut. Persisnya adalah
blok yang sama dengan alamat yang persis di bawah ACALL (mirip dengan
AJMP). Tidak ada perbedaan antara ACALL dan LCALL dalam hal
penanganannya dalam subrutin. Di mana nilai PC akan disimpan dalam
stack, dan menjalankan subrutin sampai menemukan instruksi RET. Dan
kembali menjalankan instruksi yang tepat di bawah instruksi ACALL tadi.
Pada
beberapa versi turunan dari 8051, seperti AT89C2051, chip ini memiliki
memory program hanya sebesar 2 KB di dalamnya. Sehingga hanya dengan
instruksi ACALL seluruh alamat memory program sudah dapat dijadikan
tujuan ACALL. Dengan demikian menggunakan ACALL akan membuat ukuran kode
program kita menjadi lebih kecil dibandingkan dengan menggunakan LCALL.
Contoh 3-11
|
Seorang
perancang menggunakan mikrokontroller AT891051 yang hanya memiliki 1 kB
ROM flash di dalam chip. Yang mana yang paling baik untuk digunakan,
yaitu intruksi LCALL dan instruksi ACALL.
Jawaban:
Dengan
hanya menggunakan ACALL, kita dapat mengakses semua alamat dalam ROM
flash dalam chip. Dibanding LCALL, kita setiap ACALL, kita dapat
menghemat 1 byte.
|
Tentu
saja untuk tujuan membuat program yang ringkas, kita dapat memprogram
lebih efisien jika memiliki pengetahuan yang baik akan
instruksi-instruksi yang dimiliki oleh mikoroprosesor dan menggunakanya
dengan bijaksana. Lihat contoh 3-12.
Contoh 3-12
|
Tulis ulang Contoh 3-8 paling efisien yang anda bisa.
Jawaban:
ORG 0
MOV A,#55h ;Isi A dengan 55h
LAGI: MOV P1,A ;salin A pada P1
ACALL DELAY ;Tunda
CPL A ;Balik A
SJMP LAGI ;Ulang
;——Tundaan Waktu
DELAY:
MOV R5,#0FFh ;R5 = FFh sebagai pencacah counter
ULANG: DJNZ R5,ULANG ;Tunggu sampai R5 = 0
RET
Ingat
dalam program ini langkah pertama adalah membuat A menjadi 55h. Dengan
men-complement A kita mendapatkan AAh, men-complement A sekali lagi kita
mendapatkan lagi 55h, demikian seterusnya. Kenapa ? “01010101″ biner
untuk 55h, jika dicomplement akan menjadi “10101010″, yaitu AAh. Sekali
lagi dicomplement akan menjadi seperti semula, yaitu “01010101″.
|
SUBBAB 3,3: MEMBUAT TUNDAAN WAKTU DAN PERHITUNGANNYA
Dalam
subBAB yang lalu kita menggunakan subrutin DELAY. Bagaimana membuat
berbagai Tundaan waktu dan menghitung tundaan yang benar, akan kita
bahas pada subBAB ini.
Siklus Mesin
Siklus
Mesin (dalam bahasa madura : Machine Cycle). Untuk CPU menjalankan
setiap instruksinya dibutuhkan beberappa ciklus clock. Pada keluarga
8051, siklus clock ini kemudian disebut dengan machine cycle.
Berserta siklus mesin-nya. Untuk menghitung tundaan waktu, kita
menggunakan daftar ini. Pada keluarga 8051, lama dari siklus mesin
tergantung pada frekuensi dari kristal osilator yang terhubung pada
system 8051. Kristal osilator ini bersama dengan rangkaian pendukung di
dalam chip, membentuk sumber clock bagi CPU 8051 (lihat bab 4).
Frekeunsi dari kristal osilator yang dapat dihubungkan bervariasi mulai
dari 4 MHZ sampai dengan 33 MHz, tergantung dari frekuensi yang
dibolehkan bagi setiap jenis chip 8051. Namun dari pada itu, frekuensi
11.0592 MHz (atau kelipatannya) adalah ideal digunakan agar 8051 dapat
kompatibel dengan port serial milik komputer PC kita (lihat bab 10).
Pada 8051, setiap siklus mesin setidak dibutuhkan 12 perioda osilator.
Sehingga perhitungannya adalah frekuensi siklus mesin adalah 1 / 12 dari
frekuensi osilator. Lihat contoh 3-13.
Contoh 3-13
|
Berikut
ini ditunjukkan frekuensi kristal untuk tiga system berbasis 8051
berbeda. Cari perioda dari siklus mesin masing-masing ..
(a) 11.0592 MHz (b) 16 MHz (c) 20 MHz
Jawaban:
(a) 11.0592/12 = 921.6 kHz, siklus mesin = 1/921.6 kHz = 1.085 uS
(b) 16 /12 = 1.333 MHz, siklus mesin = 1/1.333 MHz = 0.75 uS
(c) 20 /12 = 1.666 MHz, siklus mesin = 1/921.6 MHz = 0.60 uS
|
Contoh 3-14
|
Untuk system 8051 dengan 11.05920 MHz, cari berapa panjang waktu untuk mengeksekusi setiap instruksi di bawah ini
(a) MOV R3,#55 (b) DEC R3 (c) DJNZ R2,Target
(b) LJMP (e) SJMP (f) NOP (No Operation)
(g) MUL AB
Jawaban:
Siklus
mesin untuk system 11.0592 MHz adalah 1.085 uS seperti yang ditunjukkan
pada Contoh 3-13. Tabvel A-1 lampiran A menunjukkan siklus mesin dari
masing-masing instruksi adalah.
Instruksi Siklus mesin Lama eksekusi
(a) MOV R3,#55 1 1 x 1.085 uS = 1.085 uS
(b) DEC R3 1 1 x 1.085 uS = 1.085 uS
(c) DJNZ R2,Tareget 2 2 x 1.085 uS = 2.17 uS
(b) LJMP 2 2 x 1.085 uS = 2.17 uS
(e) SJMP 2 2 x 1.085 uS = 2.17 uS
(f) NOP (No Operation) 1 1 x 1.085 uS = 1.085 uS
(g) MUL AB 4 4 x 1.085 uS = 4.34 uS
|
Perhitungan Tundaan
Seperti
yang kita lihat pada sub BAB lalu, subrutin untuk tundaan memiliki dia
bagian penting: (1) pengaturan counter, dan (2) loop. Sebagian besar
dari tundaan waktu dikerjakan oleh bagian tubuh dari loop, seperti yang
ditunjukkan contoh 3-15
Contoh 3-15
|
Cari panjang tundaan waktu dari rutin berikut, jika frekuensi kristal = 11.0592 MHz.
Siklus Mesin
MOV A,#55h
LAGI: MOV P1,A
ACALL DELAY
CPL A
SJMP LAGI
;——Tundaan Waktu
DELAY: MOV R3,#200
ULANG: DJNZ R3,ULANG
RET
Jawaban:
Dari Tabel A-1 dalamLampiran A, kita mendapatkan siklus mesin dari masing-masing perintah dalam subrutin DELAY.
Siklus Mesin
DELAY: MOV R3,200 1
ULANG: DJNZ R3,ULANG 2
RET 1
Sehingga waktu tundaan adalah [(200 x 2) +1+1] X 1.085 Us = 436.17 uS.
|
Kita
dapat menghitung tundaan waktu (berbasis instruksi) di dalam loop, dan
mengabaikan siklus clock yang digunakan instruksi diluar loop.
Pada
contoh 3-15, nilai tertinggi dari register R3 adalah 255, sehingga
salah satu cara untuk memperpanjang tundaan adalah dengan menambahkan
instruksi NOP pada bagian loop. NOP adalah berarti No Operation, yang
berarti setidaknya membuang waktu 1 siklus mesin. Hal ini dicontokan
pada contoh 3-16.
Loop Tundaan di dalam Loop
Salah
satu cara yag lain untuk menghasilkan tundaan waktu yang panjang adalah
dengan menggunakan loop di dalam loop. Di mana hal ini dinamankan pula
dengan nested loop. Lihat contoh 3-17.
Contoh 3-16
|
Cari tundaan waktu dari rutin berikut, dengan menganggap frekuensi kristal = 11.0592 MHz.
Siklus Mesin
DELAY: MOV R3,#250 1
ULANG: NOP 1
NOP 1
NOP 1
NOP 1
DJNZ R3,ULANG 2
RET 1
Jawaban:
Waktu
tundaan dalam loop ULANG dalah [250(1+1+1+1+2)] X 1.085 Us = 1500 X
1.085 Us = 1627.5 Us. Dan ditambahkan pula dua instruksi diluar loop
kita akan mendapatkan 1627.5 uS + 2 x 1.085 uS = 1629.67 uS.
|
Contoh 3-17
|
Untuk Siklus Mesin = 1.085 uS, cari tundaan waktu dari subrutin di bawah ini.
Siklus Mesin
DELAY: MOV R2,#200 1
LANJUT: MOV R3,250 1
ULANG: NOP 1
NOP 1
DJNZ R3,ULANG 2
DJNZ R2, LANJUT 2
RET 1
Jawaban:
Untuk
loop ULANG, kita akan mendapatkan (4 x 250) 1.085 uS = 1085 uS. Dan
LANJUT mengulang loop ULANG sebanyak 200 kali, sehingga kita mendapatkan
200 x 1085 = 217000, hal itu jika kita memasukkkan kelebihan waktu
overhead. Namun instruksi “MOV R3,#250” dan “DJNZ R3,LANJUT”
di awal dan diakhir loop ULANG tambahkan (3 x 200 x 1.085 uS) = 651 uS
kelebihan waktu. Sehingga panjang sebenarnya dari ke dua loop adalah
217000 + 651 = 217651 uS = 217,651 mS. Adapun perhitungan tersebut masih
mengabaikan perintah diawal dan diakhir subrutin yaitu CALL dan RET,
yang masing-masing membutuhkan 2 MC.
|
Penulisan Program lebih Cepat
Ada satu lagi instruksi sejenis yang didukung oleh assembler 8051, yaitu JMP. Syntax-nya “JMP Label“. Pada instruksi ini, assembler akan melakukan ..
1. Mencari nilai alamat instruksi pertama setelah instruksi JMP.
2. Menghitung jarak alamat di atas tersebut dengan lokasi label yang dituju.
3. Memilih menggunakan SJMP, AJMP, atau LJMP.
Jika
jaraknya sangat pendek maka assembler akan menggunakan SJMP, dan jika
jaraknya lebih jauh maka assembler menggunakan AJMP, dan jika sangat
jauh maka LJMP yang digunakan. Sangat mudah bukan?!
Satu-satunya masalah dari instruksi “JMP Label” dan sejenisnya seperti “CALL Label“, jika label berada dibawah perintah tersebut, assembler akan langsung menggunakan LJMP atau LCALL hal ini disebut forward reference. Dan dapat dipastikan program kita akan sedikit lebih gemuk.
Penulisan instruksi Jump dengan “JMP Label“, atau instruksi Call dengan “CALL Label“, akan membuat perancangan progran lebih cepat. Dan untuk mengatasi masalah forward reference
tersebut maka para perancang program selalu membuat subrutin-subrutin
di atas rutin utama. Sehingga label dituju JMP maupun CALL sekiranya
berada di atas pula. Demikianlah kebiasaan para perancang program
tersebut.
RINGKASAN
Aliran
dari proses program adalah selalu berurutan, dari instruksi ke
instruksi berikutnya, kecuali instruksi pemindahan kontrol (control transfer instruction)
di-eksekusi. Macam-macam dari instruksi pemindahan kontrol dalam bahasa
Assembler adalah condtional jump (lompat bersyarat), unconditioanl jump
(lompat tidak bersyarat), dan instruksi call.
Loop
action pada bahasa Assembler 8051, dikerjakan dnegan menggunakan
instruksi khusus dimana akan men-decrement counter dan melompat pada
ujung loop jika counter tidak nol. Instruksi Jump lainnya adalah dengan
lompat bersyarat, yang berdasarkan nilai CY, akumulator, atau bit port
I/O dan lainnya. Lompat tidak bersyarat dapat berupa lompat jarak pendek
yang tergantung nilai relatif pada alamat target. Perhatian khusus
harus diberikan atas pengaruh insturksi LCALL dan ACALL terhadap stack.