Jumat, 18 Juli 2014

Instruksi jump, loop, dan call




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.