BAB 3
Sasaran
Setelah anda menamatkan bab ini , diharapkan anda dapat :
ð Membuat code Assembler 8051 untuk membuat loop.
ð Membuat code Assembler 8051 menggunakan instruksi lompat bersyarat.
ð Menjelaskan kondisi yang berkaitan, pada setiap instruksi lompat bersyarat
ð Menulis kode untuk lompat jauh tidak bersyarat.
ð Menulis kode untuk lompat dekat tidak bersyarat.
ð Menghitung alamat tujuan dari instruksi-instruksi lompat.
ð Menbuat kode subrutin.
ð Menjelaskan perhatian penggunaan stack dalam subrutin.
ð Menjelaskan frekuensi kristal dengan siklus mesin.
ð Membuat kode untuk menghasilkan tundaan waktu.
Saat baru dinyalakan, 8051 menjalankan kode program mulai alamat 0000, lalu 0001, lalu 0002, dan seterusnya secara berurutan (sequence). Namun ada kalanya kita ingin memindahkan kontrol program ke tempat yang lain di luar urutan (branch). Ada banyak instruksi pada bahasa assembler 8051 yang dapat kita gunakan. Pada subBAB 6-1 kita akan membahas instruksi-instruksi yang digunakan untuk membuat loop (ind:kalang/putaran), dengan menggunakan instruksi unconditional jump (lompat tidak bersyarat)dan conditional jump (lompat bersyarat). Selanjutnya pada subBAB 6-2 kita akan membahas instruksi CALL dan penggunaannya. Terakhir pada subBAB 6-3 membahas subrutin untuk Tundaan Waktu.
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.
- 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.
- 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 ..
- Mencari nilai alamat instruksi pertama setelah instruksi JMP.
- Menghitung jarak alamat di atas tersebut dengan lokasi label yang dituju.
- 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.
Catatan penterjemah: Saya sendiri mendapatkan banyak manfaat dari bab ini. Karena saya menjadi semakin mengerti bagaimana proses jump itu sebenarnya. Namun jika anda bertanya apakah kita harus selalu melakukan perhitungan-perhitungan tersebut saat membuat sebuah program. Jawabannya tentu saja tidak. Serahkan perhitungan tersebut kepada Assembler, kita tinggal menentukan target jump dengan label. Dan periksa pada file list, lalu buatlah sedikit modifikasi jika terdapat error destination out range. Perkecil jarak jump dengan label. Mudah bukan?