X-Steel - Link Select 2

Friday 18 July 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.