Mempelajari set instruksi prosesor ARM. Mempelajari set instruksi prosesor ARM Contoh instruksi assembler ARM

GBA ASM - Hari 2: Beberapa informasi tentang assembler ARM - Arsip WASM.RU

ARM adalah perusahaan yang membuat prosesor GBA. Prosesor ARM adalah prosesor RISC (berlawanan dengan prosesor INTEL). RISC adalah singkatan dari Reduced Intruction Set Computers (CISC - Complex...). Meskipun prosesor ini tidak memiliki banyak instruksi (yang merupakan hal yang baik), instruksi ARM (dan mungkin prosesor RISC lainnya, saya tidak tahu) memiliki banyak tujuan dan kombinasi yang berbeda, itulah yang membuat prosesor RISC sekuat mereka. .

Register

Saya tidak tahu tentang prosesor ARM lainnya, tetapi yang digunakan di GBA memiliki 16 register dan, tidak seperti prosesor Intel (dan lainnya), semua register dapat digunakan dengan aman (biasanya). Registernya adalah sebagai berikut:

r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15

Wow! Banyak! Saya akan menjelaskannya secara berurutan.

ro: Lakukan apa yang kamu mau!

r2 ke r12: sama

r13: Pada beberapa sistem ARM, r13 adalah penunjuk tumpukan (SP pada prosesor INTEL). Saya tidak yakin apakah r13 memainkan peran yang sama di GBA, saya hanya bisa memperingatkan Anda untuk berhati-hati saat bekerja dengan stack.

r14: Berisi alamat pengirim untuk prosedur yang dipanggil. Jika Anda tidak menggunakannya, Anda dapat melakukan apa pun yang Anda inginkan dengannya.

r15: Penghitung dan bendera Program, sama seperti IP (Instruction Pointer di Intel). Ini berbeda dari register IP Intel karena Anda memiliki akses gratis ke register tersebut sama seperti register lainnya, namun perhatikan bahwa mengubahnya akan menyebabkan kontrol ditransfer ke bagian lain dari kode dan flag akan berubah.

Mari kita berhitung sedikit... 16 dikurangi 3 (biasanya) memberi kita 13 register. Bukankah itu keren? Tenang.

Sekarang Anda mungkin bertanya-tanya apa sebenarnya register itu. Register adalah area memori khusus yang merupakan bagian dari prosesor dan tidak mempunyai alamat sebenarnya, tetapi hanya diketahui dengan namanya. Registernya 32-bit. Hampir semua hal dalam bahasa assembly menggunakan register, jadi Anda harus mengetahuinya sama seperti sepupu Anda.

Instruksi perakit ARM

Pertama, saya ingin memulai dengan mengatakan bahwa menurut saya siapa pun yang menemukan assembler ARM adalah seorang jenius.

Kedua, saya ingin memperkenalkan teman baik saya CMP. Sapa dia dan mungkin, mungkin saja, dia akan menjadi temanmu juga. CMP adalah singkatan dari CoMPare (membandingkan). Instruksi ini dapat membandingkan register dan nomor, register dan register, atau register dan lokasi memori. Kemudian, setelah perbandingan, CMP menetapkan tanda status yang memberi tahu Anda hasil perbandingannya. Seperti yang Anda ingat, register r15 berisi flag. Mereka melaporkan hasil perbandingannya. Instruksi CMP dirancang khusus untuk menetapkan nilai dari flag ini dan tidak untuk yang lain.

Bendera dapat berisi status berikut:

    EQ Setara / Setara

    NE Tidak Sama

    VS Kumpulan Overflow

    VC meluap-luap Jelas

    HI Lebih Tinggi / Lebih Tinggi

    LS Lebih Rendah atau Sama/Dibawah atau sama

    PL PLus / Ditambah

    MI MINus / Minus

    Set Bawaan CS

    CC Bawa Jelas

    GE Lebih besar dari atau Sama / Lebih besar dari atau sama

    GT Lebih Besar Dari / Lebih

    LE Kurang dari atau Sama / Kurang dari atau sama

    LT Kurang Dari / Kurang

    Z adalah Nol / Nol

    NZ bukan Nol / Bukan nol

Negara-negara ini memainkan peran yang sangat penting dalam assembler ARM.

CATATAN: hanya menandai kondisi penyimpanan (Sama, Kurang dari, dan seterusnya). Mereka tidak lagi penting.

Akhiran kondisi

Anda telah melihat instruksi B (Cabang). Instruksi B melakukan apa yang disebut lompatan tanpa syarat (seperti GoTo di Basic atau JMP di INTEL assembly). Namun ia mungkin memiliki akhiran (salah satu dari yang tercantum di atas), lalu ia memeriksa apakah status benderanya cocok dengan sufiks tersebut. Jika tidak, maka instruksi lompat tidak dijalankan. Jadi jika Anda ingin memeriksa apakah register r0 sama dengan register r4 dan kemudian menuju ke label bernama label34, maka Anda perlu menulis kode berikut:

    CMP r0, r4 ; Komentar di assembler muncul setelah titik koma (;)

    label BEQ34 ; B adalah instruksi lompat dan EQ adalah arti sufiks

    ; "Jika Sama"

CATATAN: Di Goldroad Assembler, label tidak perlu disertai dengan (, dan tidak boleh ada apa pun di baris selain nama label.

CATATAN II: CMP dan BEQ tidak perlu ditulis dengan huruf kapital, cukup untuk memperjelas saja.

Sekarang Anda tahu cara melakukan transisi tergantung pada keadaan benderanya, tetapi yang tidak Anda ketahui adalah Anda dapat melakukan apa pun tergantung pada keadaan benderanya, cukup tambahkan akhiran yang diinginkan ke instruksi mana pun!

Selain itu, Anda tidak perlu menggunakan CMP untuk menyetel status bendera. Jika Anda ingin, misalnya, instruksi SUB (Kurangi) untuk menyetel flag, tambahkan akhiran "S" ke instruksi tersebut (singkatan dari "Set flags"). Ini berguna jika Anda tidak ingin menyetel status flag dengan instruksi CMP tambahan, sehingga Anda dapat melakukan ini dan melompat jika hasilnya nol seperti ini:

    SUB r0,r1,0x0FF ; Menetapkan tanda sesuai dengan hasil eksekusi

    ; instruksi

    ldrZ r0,=0x0FFFF ; Akan memuat register r0 dengan 0x0FFFF hanya jika statusnya

    bendera sama dengan Nol.

Tinjauan

Hari ini kita belajar (lebih banyak lagi) tentang register. Kami juga belajar tentang fleksibilitas instruksi ARM, yang dapat dieksekusi (atau tidak dieksekusi) tergantung pada status flagnya. Kami belajar banyak hari ini.

Besok kita akan menggunakan pengetahuan assembler ARM yang diperoleh hari ini untuk menampilkan gambar di layar GBA.

Sesuatu yang tidak mungkin hanya akan terjadi sampai hal itu menjadi mungkin / Jean-Luc Picard, Kapten. ,USS Perusahaan/. Mike H, terjemahan. Akuila

Prosesor CISC melakukan operasi yang cukup kompleks dalam satu instruksi, termasuk operasi aritmatika dan logika pada isi sel memori. Instruksi prosesor CISC dapat memiliki panjang yang berbeda-beda.

Sebaliknya, RISC memiliki sistem instruksi yang relatif sederhana dengan pembagian yang jelas berdasarkan jenis operasi:

  • bekerja dengan memori (membaca dari memori ke register atau menulis dari register ke memori),
  • memproses data dalam register (aritmatika, logika, pergeseran data ke kiri/kanan atau rotasi bit dalam register),
  • perintah transisi bersyarat atau tidak bersyarat ke alamat lain.

Sebagai aturan (tetapi tidak selalu, dan hanya jika kode program memasuki memori cache pengontrol), satu perintah dijalankan dalam satu siklus prosesor. Panjang instruksi prosesor ARM ditetapkan - 4 byte (satu kata komputer). Faktanya, prosesor ARM modern dapat beralih ke mode operasi lain, misalnya ke mode THUMB, ketika panjang instruksi menjadi 2 byte. Ini memungkinkan Anda membuat kode lebih ringkas. Namun, kami tidak membahas mode ini dalam artikel ini, karena mode ini tidak didukung di prosesor Amber ARM v2a. Untuk alasan yang sama, kami tidak akan mempertimbangkan mode seperti Jazelle (dioptimalkan untuk mengeksekusi kode Java) dan kami tidak akan mempertimbangkan perintah NEON - perintah untuk operasi pada banyak data. Bagaimanapun, kami sedang mempelajari sistem instruksi ARM murni.

Register prosesor ARM.

Prosesor ARM memiliki beberapa set register, yang saat ini hanya 16 yang tersedia untuk programmer. Ada beberapa mode operasi prosesor, tergantung pada mode operasi, bank register yang sesuai dipilih. Mode operasi ini:

  • mode aplikasi (USR, mode pengguna),
  • mode supervisor atau mode sistem operasi (SVC, mode supervisor),
  • mode pemrosesan interupsi (IRQ, mode interupsi) dan
  • mode pemrosesan "interupsi mendesak" (FIRQ, mode interupsi cepat).

Artinya, misalnya, ketika terjadi interupsi, prosesor itu sendiri pergi ke alamat program pengendali interupsi dan secara otomatis “mengganti” bank register.

Prosesor ARM versi lama, selain mode operasi di atas, memiliki mode tambahan:

  • Batalkan (digunakan untuk menangani pengecualian akses memori),
  • Tidak terdefinisi (digunakan untuk mengimplementasikan koprosesor dalam perangkat lunak) dan
  • mode tugas istimewa dari sistem operasi Sistem.

Prosesor Amber ARM v2a tidak memiliki tiga mode tambahan ini.

Untuk Amber ARM v2a, kumpulan register dapat direpresentasikan sebagai berikut:

Register r0-r7 sama untuk semua mode.
Register r8-r12 hanya umum untuk mode USR, SVC, IRQ.
Daftar r13 adalah penunjuk tumpukan. Dia miliknya sendiri dalam semua mode.
Daftarkan r14 - register kembali dari subrutin juga berbeda di semua mode.
Register r15 adalah penunjuk ke instruksi yang dapat dieksekusi. Hal ini umum untuk semua mode.

Dapat dilihat bahwa mode FIRQ adalah yang paling terisolasi, memiliki register paling banyak. Hal ini dilakukan agar beberapa interupsi yang sangat kritis dapat diproses tanpa menyimpan register di stack, tanpa membuang waktu.

Perhatian khusus harus diberikan pada register r15, juga dikenal sebagai pc (Program Counter) - penunjuk ke perintah yang dapat dieksekusi. Anda dapat melakukan berbagai operasi aritmatika dan logika pada isinya, sehingga eksekusi program akan berpindah ke alamat lain. Namun, khusus untuk prosesor ARM v2a yang diimplementasikan dalam sistem Amber, terdapat beberapa kehalusan dalam interpretasi bit register ini.

Faktanya adalah bahwa dalam prosesor ini, dalam register r15 (pc), selain penunjuk sebenarnya ke perintah yang dapat dieksekusi, informasi berikut terkandung:

Bit 31:28 - tanda untuk hasil operasi aritmatika atau logika
Bits 27 - interupsi masker IRQ, interupsi dinonaktifkan saat bit disetel.
Bits 26 - Masker interupsi FIRQ, interupsi cepat dinonaktifkan saat bit disetel.
Bit 25:2 - penunjuk sebenarnya ke instruksi program hanya membutuhkan 26 bit.
Bit 1:0 - mode operasi prosesor saat ini.
3 - Pengawas
2 - Interupsi
1 - Interupsi Cepat
0 - Pengguna

Pada prosesor ARM lama, semua flag dan bit layanan terletak di register terpisah Daftar Status Program Saat Ini(cpsr) dan Daftar Status Program Tersimpan (spsr), untuk aksesnya terdapat perintah khusus terpisah. Hal ini dilakukan untuk memperluas ruang alamat yang tersedia untuk program.

Salah satu kesulitan dalam menguasai assembler ARM adalah nama alternatif dari beberapa register. Jadi seperti disebutkan di atas, r15 adalah pc yang sama. Ada juga r13 - ini adalah sp (Stack Pointer) yang sama, r14 adalah lr (Link Register) - register alamat pengirim dari prosedur. Selain itu, r12 adalah ip yang sama (Intra-Procedure -call scratch register) yang digunakan oleh kompiler C dengan cara khusus untuk mengakses parameter pada stack. Penamaan alternatif seperti itu terkadang membingungkan ketika Anda melihat kode program orang lain - kedua sebutan register ini ditemukan di sana.

Fitur eksekusi kode.

Di banyak jenis prosesor (misalnya, x86), hanya transisi ke alamat program lain yang dapat dilakukan dengan syarat. Hal ini tidak terjadi pada ARM. Setiap instruksi prosesor ARM mungkin dieksekusi secara kondisional atau tidak. Hal ini memungkinkan Anda meminimalkan jumlah transisi melalui program dan oleh karena itu menggunakan pipeline prosesor dengan lebih efisien.

Lagi pula, apa itu pipa? Satu instruksi prosesor kini dipilih dari kode program, instruksi sebelumnya sudah didekodekan, dan instruksi sebelumnya sudah dijalankan. Ini adalah kasus pipa 3 tahap prosesor Amber A23, yang kami gunakan dalam proyek kami untuk papan Mars Rover2Mars Rover2. Modifikasi prosesor Amber A25 memiliki pipeline 5 tahap, bahkan lebih efisien. Tapi, ada satu TAPI yang besar. Perintah lompat memaksa prosesor untuk membersihkan saluran pipa dan mengisinya kembali. Dengan demikian, perintah baru dipilih, tetapi masih belum ada yang perlu didekode dan, terlebih lagi, tidak ada yang bisa segera dijalankan. Efisiensi eksekusi kode menurun dengan seringnya transisi. Prosesor modern memiliki segala macam mekanisme prediksi cabang yang entah bagaimana mengoptimalkan pengisian saluran, tetapi prosesor kami tidak memilikinya. Bagaimanapun, ARM sebaiknya memungkinkan setiap perintah dijalankan secara kondisional.

Pada prosesor ARM, dalam semua jenis instruksi, empat bit kondisi eksekusi instruksi dikodekan dalam empat bit tertinggi dari kode instruksi:

Ada total 4 tanda kondisi di prosesor:
. Negatif - hasil operasinya negatif,
. Nol - hasilnya nol,
. Carry - carry terjadi saat melakukan operasi dengan nomor yang tidak ditandatangani,
. oVerflow - terjadi luapan saat melakukan operasi dengan nomor yang ditandatangani, hasilnya tidak sesuai dengan register)

Keempat tanda ini membentuk banyak kemungkinan kombinasi kondisi:

Kode Akhiran Arti Bendera
4"h0 persamaan Setara Z mengatur
4"h1 tidak Tidak sama Z jelas
4"h2 cs/jam Carry set / unsigned lebih tinggi atau sama C mengatur
4"h3 cc/lo Bawa jelas / unsigned lebih rendah C jelas
4"h4 mi Minus/negatif tidak diatur
4"h5 hal Plus/positif atau nol Tidak jelas
4"h6 vs Meluap V mengatur
4"h7 vc Tidak meluap V jelas
4"h8 Hai Tidak ditandatangani lebih tinggi C set dan Z jelas
4"h9 aku Tidak ditandatangani lebih rendah atau sama C jelas atau set Z
4"ha ya Ditandatangani lebih besar dari atau sama dengan N == V
4"hb lt Ditandatangani kurang dari T != V
4"hc GT Ditandatangani lebih dari Z == 0,N == V
4"HD le Ditandatangani kurang dari atau sama dengan Z == 1 atau N != V
4"dia Al Selalu (tanpa syarat)
4"hf - Kondisi tidak valid

Sekarang hal ini menyebabkan kesulitan lain dalam mempelajari instruksi prosesor ARM - banyaknya sufiks yang dapat ditambahkan ke kode instruksi. Misalnya, penambahan asalkan bendera Z disetel adalah perintah addeq as add + suffix eq . Lompat ke subrutin jika flag N=0 adalah blpl sebagai bl + akhiran pl .

Bendera (Negatif, Nol, Bawa, oVerflow) Hal yang sama tidak selalu diatur selama operasi aritmatika atau logika, seperti yang terjadi, katakanlah, pada prosesor x86, tetapi hanya jika pemrogram menginginkannya. Untuk ini, ada akhiran lain pada perintah mnemonik: "s" (dalam kode perintah dikodekan oleh bit 20). Jadi, perintah penambahan tidak mengubah tandanya, tetapi perintah penambahan mengubah tandanya. Atau mungkin juga ada perintah penambahan bersyarat, tetapi mengubah tandanya. Misalnya: tambahan. Jelas bahwa banyaknya kemungkinan kombinasi nama perintah dengan sufiks berbeda untuk eksekusi bersyarat dan flag pengaturan membuat kode rakitan prosesor ARM sangat aneh dan sulit dibaca. Namun, lama kelamaan Anda akan terbiasa dan mulai memahami teks ini.

Operasi aritmatika dan logika (Pemrosesan Data).

Prosesor ARM dapat melakukan berbagai operasi aritmatika dan logika.

Kode operasi empat-bit sebenarnya (Opcode) terdapat dalam bit instruksi prosesor.

Setiap operasi dilakukan pada isi register dan apa yang disebut shifter_operand. Hasil operasi ditempatkan di register. Rn dan Rd empat bit adalah indeks register di bank aktif prosesor.

Bergantung pada bit I 25, shifter_operand diperlakukan sebagai konstanta numerik, atau sebagai indeks register kedua operan, dan bahkan operasi shift pada nilai operan kedua.

Contoh sederhana dari perintah assembler akan terlihat seperti ini:

tambahkan r0,r1,r2 @ masukkan jumlah nilai register r1 dan r2 ke dalam register r0
sub r5,r4,#7 @ tempatkan selisihnya (r4-7) pada register r5

Operasi yang dilakukan diberi kode sebagai berikut:

4"h0 dan Logis AND Rd:= Rn AND shifter_operand
4"h1 eor Logis eksklusif OR Rd:= Rn XOR shifter_operand
4"h2 sub Pengurangan aritmatika Rd:= Rn - shifter_operand
4"h3 rsb Pengurangan terbalik aritmatika Rd:= shifter_operand - Rn
4"h4 tambahkan penjumlahan aritmatika Rd:= Rn + shifter_operand
4"h5 adc Penjumlahan aritmatika plus carry flag Rd:= Rn + shifter_operand + Carry Flag
4"h6 sbc Pengurangan aritmatika dengan carry Rd:= Rn - shifter_operand - NOT(Carry Flag)
4"h7 rsc Pengurangan terbalik aritmatika dengan carry Rd:= shifter_operand - Rn - NOT(Carry Flag)
4"h8 tst Logis AND, tetapi tanpa menyimpan hasilnya, hanya flag Rn AND shifter_operand S bit yang selalu disetel yang diubah
4"h9 teq Logis eksklusif OR, tetapi tanpa menyimpan hasilnya, hanya flag Rn EOR shifter_operand yang diubah
S bit selalu disetel
4"ha cmp Perbandingan, atau lebih tepatnya pengurangan aritmatika tanpa menyimpan hasilnya, hanya flag Rn yang berubah - bit shifter_operand S selalu disetel
4"hb cmn Perbandingan invers, atau lebih tepatnya penjumlahan aritmatika tanpa menyimpan hasilnya, hanya flag Rn + shifter_operand S bit yang selalu mengatur perubahan
4"hc orr Logis ATAU Rd:= Rn ATAU shifter_operand
4"hd mov Nilai salin Rd:= shifter_operand (tidak ada operan pertama)
4"dia bic Reset bit Rd:= Rn DAN BUKAN(shifter_operand)
4"hf mvn Salin nilai terbalik Rd:= BUKAN shifter_operand (tidak ada operan pertama)

Pemindah barel.

Prosesor ARM memiliki sirkuit “barrel shifter” khusus yang memungkinkan salah satu operan digeser atau diputar sejumlah bit tertentu sebelum operasi aritmatika atau logika apa pun. Ini adalah fitur prosesor yang cukup menarik, yang memungkinkan Anda membuat kode yang sangat efisien.

Misalnya:

@ mengalikan dengan 9 berarti mengalikan suatu angka dengan 8
@ dengan menggeser ke kiri sebanyak 3 bit ditambah angka lainnya
tambahkan r0, r1, r1, lsl #3 @ r0= r1+(r1<<3) = r1*9

@ mengalikan 15 adalah mengalikan 16 dikurangi angkanya
rsb r0, r1, r1, lsl #4 @ r0= (r1<<4)-r1 = r1*15

@ akses ke tabel kata 4 byte, di mana
@r1 adalah alamat dasar tabel
@r2 adalah indeks elemen dalam tabel
ldr r0,

Selain pergeseran logis ke kiri lsl, ada juga pergeseran logis ke kanan lsr dan pergeseran kanan aritmatika asr (pergeseran yang mempertahankan tanda, bit paling signifikan dikalikan ke kiri secara bersamaan dengan pergeseran).

Ada juga rotasi bit ror - bit dipindahkan ke kanan dan bit yang ditarik keluar didorong ke kiri.
Ada pergeseran satu bit melalui flag C - ini adalah perintah rrx. Nilai register digeser ke kanan sebanyak satu bit. Di sebelah kiri, flag C dimuat ke dalam bit paling signifikan dari register.

Pergeseran dapat dilakukan bukan dengan bilangan konstan yang tetap, tetapi dengan nilai register operan ketiga. Misalnya:

tambahkan r0, r1, r1, lsr r3 @ ini r0 = r1 + (r1>>r3);
tambahkan r0, r0, r1, lsr r3 @ ini r0 = r0 + (r1>>r3);

Jadi shifter_operand adalah apa yang kami jelaskan dalam perintah assembler, misalnya sebagai "r1, lsr r3" atau "r2, lsl #5".

Hal yang paling menarik adalah penggunaan shift dalam operasional tidak memerlukan biaya apa pun. Pergeseran ini (biasanya) tidak memerlukan siklus clock tambahan, yang sangat baik untuk kinerja sistem.

Menggunakan operan numerik.

Operasi aritmatika atau logika tidak hanya dapat menggunakan isi register, tetapi juga konstanta numerik sebagai operan kedua.

Sayangnya, ada satu batasan penting di sini. Karena semua perintah memiliki panjang tetap 4 byte (32 bit), tidak mungkin untuk menyandikan nomor “apa pun” di dalamnya. Pada kode operasi, 4 bit sudah ditempati oleh kode kondisi eksekusi (Cond), 4 bit untuk kode operasi itu sendiri (Opcode), kemudian 4 bit - register penerima Rd, dan 4 bit lainnya - register operan pertama Rn, ditambah berbagai flag I 25 (hanya menunjukkan konstanta numerik dalam kode operasi) dan S 20 (mengatur flag setelah operasi). Secara total, hanya ada 12 bit tersisa untuk kemungkinan konstanta, yang disebut shifter_operand - kita melihatnya di atas. Karena 12 bit hanya dapat mengkodekan angka dalam rentang yang sempit, pengembang prosesor ARM memutuskan untuk mengkodekan konstanta sebagai berikut. Dua belas bit shifter_operand dibagi menjadi dua bagian: indikator rotasi empat bit encode_imm dan nilai numerik delapan bit aktual imm_8.

Pada prosesor ARM, konstanta didefinisikan sebagai angka delapan bit di dalam angka 32-bit, diputar ke kanan dengan jumlah bit genap. Itu adalah:

imm_32 = imm_8 ROR (encode_imm *2)

Ternyata cukup rumit. Ternyata tidak semua bilangan konstan dapat digunakan dalam perintah assembler.

Kamu bisa menulis

tambahkan r0, r2, #255 @ konstanta dalam bentuk desimal
tambahkan r0, r3, #0xFF @ konstanta dalam heksadesimal

karena 255 berada dalam kisaran 8 bit. Perintah-perintah ini akan dikompilasi seperti ini:

0: e28200ff tambahkan r0, r2, #255 ; 0xff
4: e28300ff tambahkan r0, r3, #255 ; 0xff

Dan Anda bahkan bisa menulis

tambahkan r0, r4, #512
tambahkan r0, r5, 0x650000

Kode yang dikompilasi akan terlihat seperti ini:

0: e2840c02 tambahkan r0, r4, #512 ; 0x200
4: e2850865 tambahkan r0, r5, #6619136 ; 0x650000

Dalam hal ini, angka 512 itu sendiri, tentu saja, tidak sesuai dengan byte. Tapi kemudian kita bayangkan dalam bentuk heksadesimal 32'h00000200 dan melihat bahwa ini adalah 2 diperluas ke kanan sebanyak 24 bit (1 ror 24). Koefisien rotasinya dua kali lebih kecil dari 24, yaitu 12. Jadi ternyata shifter_operand = ( 4'hc , 8'h02 ) - ini adalah dua belas bit paling tidak signifikan dari perintah. Hal yang sama berlaku untuk angka 0x650000. Baginya, shifter_operand = ( 4'h8, 8'h65 ).

Jelas bahwa Anda tidak bisa menulis

tambahkan r0, r1,#1234567

atau Anda tidak bisa menulis

pindah r0, #511

karena di sini bilangan tidak dapat direpresentasikan sebagai imm_8 dan encode_imm - faktor rotasi. Kompiler assembler akan menimbulkan kesalahan.

Apa yang harus dilakukan jika sebuah konstanta tidak dapat langsung dikodekan ke dalam shifter_operand ? Kita harus melakukan segala macam trik.
Misalnya, Anda dapat memuat angka 512 terlebih dahulu ke dalam register gratis, lalu menguranginya dengan satu:

pindah r0, #511
subr0,r0,#1

Cara kedua untuk memuat nomor tertentu ke dalam register adalah dengan membacanya dari variabel khusus yang terletak di memori:

ldr r7,my_var
.....
my_var: .word 0x123456

Cara termudah untuk menulisnya adalah seperti ini:

ldr r2,=511

Dalam hal ini (perhatikan tanda "="), jika konstanta dapat direpresentasikan sebagai imm_8 dan encode_imm , jika dapat masuk ke dalam bit 12 shifter_operand , maka kompiler rakitan akan secara otomatis mengkompilasi ldr ke dalam instruksi mov. Tetapi jika nomor tersebut tidak dapat direpresentasikan dengan cara ini, maka kompiler itu sendiri akan mencadangkan sel memori dalam program untuk konstanta ini, dan akan memberi nama pada sel memori ini dan mengkompilasi perintah ke dalam ldr .

Inilah yang saya tulis:

ldr r7,my_var
ldr r8,=511
ldr r8,=1024
ldr r9,=0x3456
........
My_var: .word 0x123456

Setelah kompilasi saya mendapatkan ini:

18: e59f7030 ldr r7, ; 50
1c: e59f8030 ldr r8, ; 54
20: e3a08b01 pindah r8, #1024 ; 0x400
24: e59f902c ldr r9, ; 58
.............
00000050 :
50: 00123456 .kata 0x00123456
54: 000001ff .word 0x000001ff
58: 00003456 .kata 0x00003456

Perhatikan bahwa kompiler menggunakan pengalamatan memori relatif terhadap register pc (alias r15).

Membaca sel memori dan menulis register ke memori.

Seperti yang saya tulis di atas, prosesor ARM hanya dapat melakukan operasi aritmatika atau logika pada isi register. Data untuk operasi harus dibaca dari memori dan hasil operasi harus ditulis kembali ke memori. Ada perintah khusus untuk ini: ldr (mungkin dari kombinasi “LoaD Register”) untuk membaca dan str (mungkin “STore Register”) untuk menulis.

Kelihatannya hanya ada dua tim, namun nyatanya variasinya banyak. Lihat saja cara perintah ldr /str dikodekan pada prosesor Amber ARM untuk melihat berapa banyak bit flag tambahan L 20, W 21, B 22, U 23, P 24, I 25 - dan mereka menentukan perilaku spesifik dari perintah:

  • Bit L 20 menentukan tulis atau baca. 1 - ldr, baca, 0 - str, tulis.
  • Bit B 22 menentukan baca/tulis kata 32-bit atau byte 8-bit. 1 berarti operasi byte. Ketika sebuah byte dibaca ke dalam register, bit paling signifikan dari register direset ke nol.
  • Bit I 25 menentukan penggunaan bidang Offset. Jika I 25 ==0, maka Offset diartikan sebagai offset numerik yang harus ditambahkan ke alamat dasar dari register atau dikurangi. Namun penambahan atau pengurangan tergantung pada bit U 23.

(Cond) - kondisi untuk melakukan operasi. Ditafsirkan dengan cara yang sama seperti perintah logika/aritmatika - membaca atau menulis bisa bersyarat.

Jadi, dalam teks perakitan Anda dapat menulis sesuatu seperti ini:

ldr r1, @ ke register r1 baca kata di alamat dari register r0
ldrb r1, @ ke dalam register r1 baca byte pada alamat dari register r0
ldreq r2, @ pembacaan kata bersyarat
ldrgtb r2, @ byte bersyarat dibaca
ldr r3, @ membaca kata di alamat 8 relatif terhadap alamat dari register r4
ldr r4, @ membaca kata di alamat -16 relatif terhadap alamat dari register r5

Setelah menyusun teks ini, Anda dapat melihat kode sebenarnya dari perintah ini:

0: e5901000 ldr r1,
4: e5d01000 ldrb r1,
8: 05912000 ldreq r2,
c: c5d12000 ldrbgt r2,
10: e5943008 ldr r3,
14: e5154010 ldr r4,

Pada contoh di atas saya hanya menggunakan ldr , tetapi str digunakan dengan cara yang hampir sama.

Ada mode akses memori tulis kembali pra-indeks dan pasca-indeks. Dalam mode ini, penunjuk akses memori diperbarui sebelum atau setelah instruksi dijalankan. Jika Anda familiar dengan bahasa pemrograman C, maka Anda familiar dengan konstruksi akses pointer seperti ( *sumberp++;) atau ( a=*++psumber;). Prosesor ARM mengimplementasikan mode akses memori ini. Ketika perintah baca dijalankan, dua register diperbarui sekaligus - register penerima menerima nilai yang dibaca dari memori dan nilai dalam register penunjuk ke sel memori dipindahkan maju atau mundur.

Menulis perintah ini menurut saya agak tidak logis. Butuh waktu lama untuk membiasakan diri.

ldr r3, ! @psrc++; r3 = *psrc;
ldr r3, , #4 @ r3 = *psrc; psrc++;

Perintah ldr pertama pertama-tama menambah penunjuk, lalu membaca. Perintah kedua dibaca terlebih dahulu, lalu menambah penunjuk. Nilai pointer psrc ada di register r0.

Semua contoh yang dibahas di atas adalah untuk kasus ketika bit I 25 pada kode perintah direset. Tapi masih bisa dipasang! Maka nilai bidang Offset tidak akan berisi konstanta numerik, tetapi register ketiga yang berpartisipasi dalam operasi tersebut. Apalagi nilai register ketiga masih bisa digeser sebelumnya!

Berikut adalah contoh kemungkinan variasi kode:

0: e7921003 ldr r1, @ baca alamat - jumlah nilai dari register r2 dan r3
4: e7b21003 ldr r1, ! @ sama, namun setelah dibaca r2 akan bertambah nilainya dari r3
8: e6932004 ldr r2, , r4 @ pertama akan ada pembacaan di alamat r3, kemudian r3 bertambah r4
c: e7943185 ldr r3, @ baca alamat r4+r5*8
10: e7b43285 ldr r3, ! @ baca alamat r4+r5*32, setelah membaca r4 akan disetel ke nilai alamat ini
14: e69431a5 ldr r3, , r5, lsr #3 @ alamat untuk membaca r4, setelah menjalankan perintah r4 akan disetel ke r4+r5/8

Ini adalah variasi perintah baca/tulis pada prosesor ARM v2a.

Pada prosesor ARM model lama, variasi perintah ini bahkan lebih besar.
Hal ini disebabkan oleh fakta bahwa prosesor memungkinkan, misalnya, untuk membaca tidak hanya kata (angka 32-bit) dan byte, tetapi juga setengah kata (16 bit, 2 byte). Kemudian akhiran “h”, dari kata setengah kata, ditambahkan pada perintah ldr/str. Perintahnya akan terlihat seperti ldrh atau strh . Ada juga perintah untuk memuat setengah kata ldrsh atau byte ldrsb yang diartikan sebagai angka yang ditandatangani. Dalam kasus ini, bit paling signifikan dari kata atau byte yang dimuat dikalikan menjadi bit paling signifikan dari keseluruhan kata dalam register penerima. Misalnya, memuat setengah kata 0xffff25 dengan perintah ldrsh di register tujuan akan menghasilkan 0xffffff25 .

Banyak membaca dan menulis.

Perintah ldr /str bukan satu-satunya untuk mengakses memori. Prosesor ARM juga memiliki perintah yang memungkinkan Anda melakukan transfer blok - Anda dapat memuat konten beberapa kata berurutan dari memori dan beberapa register sekaligus. Anda juga dapat menulis nilai beberapa register secara berurutan ke dalam memori.

Mnemonik perintah transfer blok dimulai dari root ldm (LoaD Multiple) atau stm (Store Multiple). Tapi kemudian, seperti biasa di ARM, cerita dengan sufiks dimulai.

Secara umum, perintahnya terlihat seperti ini:

op(lanjutan)(mode) Rd(, {Register list} !}

Akhiran (Cond) dapat dimengerti, ini adalah syarat untuk menjalankan perintah. Akhiran (mode) adalah mode transmisi, akan dijelaskan lebih lanjut nanti. Rd adalah register yang menentukan alamat dasar dalam memori untuk membaca atau menulis. Tanda seru setelah register Rd menunjukkan bahwa register tersebut akan diubah setelah operasi baca/tulis. Daftar register yang dimuat dari memori atau dimasukkan ke dalam memori adalah (Daftar register).

Daftar register ditentukan dalam kurung kurawal yang dipisahkan dengan koma atau sebagai rentang. Misalnya:

stm r0,(r3,r1, r5-r8)

Memori akan ditulis secara tidak berurutan. Daftar tersebut hanya menunjukkan register mana yang akan ditulis ke memori dan hanya itu. Kode perintah berisi 16 bit yang dicadangkan untuk Daftar Register, persis dengan jumlah register di bank prosesor. Setiap bit dalam bidang ini menunjukkan register mana yang akan berpartisipasi dalam operasi.

Sekarang tentang mode baca/tulis. Ada ruang untuk kebingungan di sini. Faktanya adalah nama mode yang berbeda dapat digunakan untuk tindakan yang sama.

Jika kita membuat penyimpangan lirik kecil, maka kita perlu membicarakan... tumpukan. Tumpukan adalah cara untuk mengakses data tipe LIFO - Masuk Terakhir, Keluar Pertama (wiki) - masuk terakhir, keluar pertama. Tumpukan banyak digunakan dalam pemrograman saat memanggil prosedur dan menyimpan status register saat memasukkan fungsi dan memulihkannya saat keluar, serta saat meneruskan parameter ke prosedur yang dipanggil.

Siapa sangka, ada empat jenis tumpukan memori.

Tipe pertama adalah Full Descending. Ini adalah saat penunjuk tumpukan menunjuk ke elemen tumpukan yang terisi dan tumpukan bertambah menuju alamat yang semakin berkurang. Saat Anda perlu meletakkan sebuah kata di tumpukan, pertama-tama penunjuk tumpukan diturunkan (Pengurangan Sebelum), kemudian kata tersebut ditulis ke alamat penunjuk tumpukan. Saat Anda perlu menghapus kata komputer dari tumpukan, kata tersebut dibaca menggunakan nilai penunjuk tumpukan saat ini, kemudian penunjuk bergerak ke atas (Peningkatan Setelah).

Tipe kedua adalah Full Ascending. Tumpukannya tidak bertambah, tetapi naik, menuju alamat yang lebih besar. Penunjuk juga menunjuk ke elemen yang ditempati. Saat Anda perlu meletakkan sebuah kata di tumpukan, pertama-tama penunjuk tumpukan bertambah, kemudian kata tersebut ditulis ke penunjuk (Peningkatan Sebelum). Saat Anda perlu menghapus dari tumpukan, Anda terlebih dahulu membaca penunjuk tumpukan, karena menunjuk ke elemen yang ditempati, kemudian penunjuk tumpukan dikurangi (Pengurangan Setelah).

Tipe ketiga adalah Turun Kosong. Tumpukannya tumbuh ke bawah, seperti dalam kasus Menurun Penuh, namun perbedaannya adalah penunjuk tumpukan menunjuk ke sel yang kosong. Jadi, ketika Anda perlu meletakkan sebuah kata di tumpukan, catatan segera dibuat, kemudian penunjuk tumpukan dikurangi (Pengurangan Setelah). Saat dikeluarkan dari tumpukan, penunjuknya bertambah terlebih dahulu, lalu dibaca (Peningkatan Sebelumnya).

Tipe keempat adalah Blank Ascending. Saya harap semuanya jelas - tumpukannya bertambah ke atas. Penunjuk tumpukan menunjuk ke elemen kosong. Meletakkan tumpukan berarti menulis sebuah kata ke alamat penunjuk tumpukan dan menambah penunjuk tumpukan (Inkremen Setelah). Pop from stack - kurangi penunjuk tumpukan dan baca kata (Kurangi Sebelumnya).

Jadi, saat melakukan operasi pada tumpukan, Anda perlu menambah atau mengurangi penunjuk - (Peningkatan/Penurunan) sebelum atau sesudah (Sebelum/Sesudah) membaca/menulis ke dalam memori, bergantung pada jenis tumpukan. Prosesor Intel, misalnya, memiliki perintah khusus untuk bekerja dengan tumpukan, seperti PUSH (meletakkan kata di tumpukan) atau POP (mengeluarkan kata dari tumpukan). Tidak ada instruksi khusus pada prosesor ARM, tetapi instruksi ldm dan stm yang digunakan.

Jika Anda mengimplementasikan tumpukan menggunakan instruksi prosesor ARM, Anda mendapatkan gambar berikut:

Mengapa tim yang sama perlu diberi nama berbeda? Kurang ngerti sama sekali.. Disini tentunya perlu diperhatikan bahwa standar stack untuk ARM masih Full Descending.

Penunjuk tumpukan dalam prosesor ARM adalah register sp atau r13. Ini umumnya merupakan kesepakatan. Tentu saja menulis stm atau membaca ldm dapat dilakukan dengan register dasar lainnya juga. Namun, Anda perlu mengingat perbedaan register sp dari register lain - register ini dapat berbeda dalam mode operasi prosesor yang berbeda (USR, SVC, IRQ, FIRQ), karena mereka memiliki bank register sendiri.

Dan satu catatan lagi. Tulis baris seperti ini dalam kode perakitan ARM dorong (r0-r3), Tentu saja Anda bisa. Hanya pada kenyataannya mereka akan menjadi tim yang sama stmfd sp!,(r0-r3).

Terakhir, saya akan memberikan contoh kode assembly dan teks yang telah dibongkar. Kita punya:


stmfd sp!,(r0-r3)
stmdb sp!,(r0-r3)
dorong (r0-r3)

@ketiga instruksi ini sama dan melakukan hal yang sama
pop(r0-r3)
ldmia sp!,(r0-r3)
ldmfd r13!,(r0-r3)

Stmfd r4,(r0-r3,r5,r8)
stmea r4!,(r0-r3,r7,r9,lr,pc)
ldm r5,(r0,pc)

Setelah kompilasi kita mendapatkan:

0: dorongan e92d000f (r0, r1, r2, r3)
4: dorongan e92d000f (r0, r1, r2, r3)
8: dorongan e92d000f (r0, r1, r2, r3)
c: e8bd000f pop (r0, r1, r2, r3)
10: pop e8bd000f (r0, r1, r2, r3)
14: pop e8bd000f (r0, r1, r2, r3)
18: e904012f stmdb r4, (r0, r1, r2, r3, r5, r8)
1c: e8a4c28f stmia r4!, (r0, r1, r2, r3, r7, r9, lr, pc)
20: e8958001 ldm r5, (r0, buah)

Transisi dalam program.

Pemrograman tidak mungkin dilakukan tanpa transisi. Dalam program apa pun, ada eksekusi kode siklik, dan pemanggilan prosedur, fungsi, dan ada juga eksekusi bersyarat dari bagian-bagian kode.

Prosesor Amber ARM v2a hanya memiliki dua perintah: b (dari kata Cabang - cabang, transisi) dan bl (Cabang dengan Tautan - transisi dengan tetap mempertahankan alamat pengirim).

Sintaks perintahnya sangat sederhana:

label b(kond).
label bl(kond).

Jelas bahwa setiap transisi dapat bersifat kondisional, yaitu, program mungkin berisi kata-kata aneh seperti ini, yang dibentuk dari akar kata “b” dan “bl” dan akhiran kondisi (Cond):

beq, bne, bcs, bhs, bcc, blo, bmi, bpl, bvs, bvc, bhi, bls, bge, bgt, ble, bal, b

bleq, blne, blcs, blhs, blcc, bllo, blmi, blpl, blvs, blvc, blhi, blls, blge, blgt, blle, blal, bl

Variasinya luar biasa, bukan?

Perintah lompat berisi Offset 24-bit. Alamat lompatan dihitung sebagai jumlah dari nilai penunjuk pc saat ini dan nomor Offset yang digeser 2 bit ke kiri, ditafsirkan sebagai nomor bertanda:

PC baru = pc + Offset*4

Jadi, rentang transisinya adalah 32MB ke depan atau ke belakang.

Mari kita lihat apa itu transisi sambil mempertahankan alamat pengirim bl. Perintah ini digunakan untuk memanggil subrutin. Fitur menarik dari perintah ini adalah alamat pengirim dari prosedur saat prosedur dipanggil tidak disimpan di tumpukan, seperti pada prosesor Intel, tetapi di register r14 biasa. Kemudian, untuk kembali dari prosedur, Anda tidak memerlukan perintah ret khusus, seperti pada prosesor Intel yang sama, tetapi Anda cukup menyalin nilai r14 kembali ke pc. Sekarang sudah jelas mengapa register r14 memiliki nama alternatif lr (Link Register).

Mari kita lihat prosedur outbyte dari proyek hello-world untuk Amber SoC.

000004a0<_outbyte>:
4a0: e59f1454 ldr r1, ; 8fc< адрес регистра данных UART >
4a4: e59f3454 ldr r3, ; 900< адрес регистра статуса UART >
4a8: e5932000 ldr r2, ; membaca status saat ini
4ac: e2022020 dan r2, r2, #32
4b0: e3520000 cmp r2, #0 ; periksa apakah UART tidak sibuk
4b4: 05c10000 strbeq r0, ; tulis karakter ke UART hanya jika tidak sibuk
4b8: 01b0f00e movseq pc, lr; pengembalian bersyarat dari prosedur jika UART tidak sibuk
4bc: 1afffff9 bne 4a8<_outbyte+0x8>; loop untuk memeriksa status UART

Saya rasa dari komentar di bagian ini jelas bagaimana prosedur ini bekerja.

Catatan penting lainnya tentang transisi. Register r15 (pc) dapat digunakan dalam operasi aritmatika atau logika biasa sebagai register penerima. Jadi perintah seperti add pc,pc,#8 merupakan instruksi yang cukup untuk berpindah ke alamat lain.

Satu catatan lagi perlu dibuat mengenai transisi. Prosesor ARM lama juga memiliki instruksi cabang tambahan bx, blx dan blj. Ini adalah perintah untuk melompat ke fragmen kode dengan sistem perintah yang berbeda. Bx /blx memungkinkan Anda beralih ke kode THUMB 16-bit pada prosesor ARM. Blj adalah panggilan ke prosedur sistem instruksi Jazelle (dukungan bahasa Java di prosesor ARM). Amber ARM v2a kami tidak memiliki perintah ini.

Halo semua!
Berdasarkan pekerjaan saya adalah seorang programmer Java. Beberapa bulan terakhir bekerja memaksa saya untuk mengenal pengembangan untuk Android NDK dan, karenanya, menulis aplikasi asli dalam C. Di sini saya dihadapkan pada masalah dalam mengoptimalkan perpustakaan Linux. Banyak yang ternyata sama sekali tidak dioptimalkan untuk ARM dan memuat banyak prosesor. Sebelumnya saya praktis tidak pernah memprogram dalam bahasa assembly, sehingga pada awalnya sulit untuk mulai mempelajari bahasa ini, namun saya tetap memutuskan untuk mencobanya. Artikel ini ditulis, boleh dikatakan, dari seorang pemula untuk pemula. Saya akan mencoba menjelaskan dasar-dasar yang telah saya pelajari, saya harap ini menarik minat seseorang. Selain itu, saya akan dengan senang hati menerima kritik yang membangun dari para profesional.

Perkenalan
Jadi pertama-tama mari kita cari tahu apa itu ARM. Wikipedia memberikan definisi ini:

Arsitektur ARM (Mesin RISC Tingkat Lanjut, Mesin Acorn RISC, mesin RISC tingkat lanjut) adalah keluarga inti mikroprosesor 32-bit dan 64-bit berlisensi yang dikembangkan oleh ARM Limited. Perusahaan secara eksklusif mengembangkan kernel dan alat untuknya (kompiler, alat debugging, dll.), menghasilkan uang dengan melisensikan arsitekturnya kepada produsen pihak ketiga.

Jika ada yang belum tahu, kini sebagian besar perangkat seluler dan tablet dikembangkan dengan arsitektur prosesor ini. Keuntungan utama dari keluarga ini adalah konsumsi dayanya yang rendah, sehingga sering digunakan di berbagai sistem tertanam. Arsitekturnya telah berkembang seiring waktu, dan dimulai dengan ARMv7, 3 profil telah ditentukan: 'A'(aplikasi) - aplikasi, 'R' (waktu nyata) - waktu nyata, 'M' (mikrokontroler) - mikrokontroler. Anda bisa membaca sejarah perkembangan teknologi ini dan data menarik lainnya di Wikipedia atau dengan googling di Internet. ARM mendukung mode operasi yang berbeda (Thumb dan ARM, selain itu, Thumb-2 baru-baru ini muncul, yang merupakan campuran dari ARM dan Thumb). Pada artikel ini, kita akan melihat mode ARM itu sendiri, di mana set instruksi 32-bit dijalankan.

Setiap prosesor ARM dibuat dari blok berikut:

  • 37 register (hanya 17 yang terlihat selama pengembangan)
  • Unit logika aritmatika (ALU) - melakukan tugas aritmatika dan logika
  • Barrel shifter - perangkat yang dirancang untuk memindahkan blok data sejumlah bit tertentu
  • CP15 adalah sistem khusus yang mengontrol koprosesor ARM
  • Dekoder instruksi - berkaitan dengan mengubah instruksi menjadi serangkaian operasi mikro
Ini tidak semuanya komponen ARM, tetapi mempelajari lebih dalam tentang konstruksi prosesor berada di luar cakupan artikel ini.
Eksekusi saluran pipa
Prosesor ARM menggunakan pipeline 3 tahap (dimulai dengan ARM8, pipeline 5 tahap diimplementasikan). Mari kita lihat pipeline sederhana menggunakan prosesor ARM7TDMI sebagai contoh. Eksekusi setiap instruksi terdiri dari tiga langkah:

1. Tahap pengambilan sampel (F)
Pada tahap ini, instruksi mengalir dari RAM ke dalam pipa prosesor.
2. Tahap penguraian kode (D)
Instruksi diterjemahkan dan tipenya dikenali.
3. Fase Eksekusi (E)
Data memasuki ALU dan dieksekusi dan nilai yang dihasilkan ditulis ke register yang ditentukan.

Namun dalam pengembangannya harus diperhatikan bahwa ada instruksi yang menggunakan beberapa siklus eksekusi, misalnya load(LDR) atau store. Dalam hal ini tahap pelaksanaan (E) dibagi menjadi beberapa tahap (E1, E2, E3...).

Eksekusi bersyarat
Salah satu fungsi terpenting dari assembler ARM adalah eksekusi bersyarat. Setiap instruksi dapat dieksekusi secara kondisional dan sufiks digunakan untuk ini. Jika akhiran ditambahkan ke nama instruksi, parameternya diperiksa sebelum mengeksekusinya. Jika parameter tidak memenuhi kondisi, maka instruksi tidak dijalankan. Sufiks:
MI - angka negatif
PL - positif atau nol
AL - selalu jalankan instruksi
Masih banyak lagi sufiks eksekusi bersyarat. Baca sufiks dan contoh lainnya di dokumentasi resmi: dokumentasi ARM
Sekarang saatnya untuk mempertimbangkan...
Sintaks dasar assembler ARM
Bagi yang pernah bekerja dengan assembler sebelumnya, sebenarnya Anda bisa melewati poin ini. Untuk semua orang, saya akan menjelaskan dasar-dasar bekerja dengan bahasa ini. Jadi, setiap program bahasa assembly terdiri dari instruksi. Instruksi dibuat dengan cara ini:
(label) (instruksi|operan) (@ komentar)
Label adalah parameter opsional. Instruksi adalah mnemonik langsung dari instruksi ke prosesor. Petunjuk dasar dan penggunaannya akan dibahas di bawah. Operan - konstanta, alamat register, alamat dalam RAM. Komentar adalah parameter opsional yang tidak mempengaruhi eksekusi program.
Daftarkan nama
Nama register berikut diperbolehkan:
1.r0-r15

3.v1-v8 (register variabel, r4 hingga r11)

4.sb dan SB (register statis, r9)

5.sl dan SL (r10)

6.fp dan FP (r11)

7.ip dan IP (r12)

8.sp dan SP (r13)

9.lr dan LR (r14)

10.pc dan PC (penghitung program, r15).

Variabel dan konstanta
Dalam assembler ARM, seperti (secara praktis) bahasa pemrograman lainnya, variabel dan konstanta dapat digunakan. Mereka dibagi menjadi beberapa jenis berikut:
  • numerik
  • asah otak
  • Rangkaian
Variabel numerik diinisialisasi seperti ini:
sebuah SETA 100; variabel numerik "a" dibuat dengan nilai 100.
Variabel string:
kemungkinan SET "literal"; variabel ketidakmungkinan dibuat dengan nilai "literal". PERHATIAN! Nilai variabel tidak boleh melebihi 5120 karakter.
Variabel Boolean masing-masing menggunakan nilai TRUE dan FALSE.
Contoh instruksi assembler ARM
Dalam tabel ini saya telah mengumpulkan instruksi dasar yang diperlukan untuk pengembangan lebih lanjut (pada tahap paling dasar :):

Untuk memperkuat penggunaan instruksi dasar, mari kita tulis beberapa contoh sederhana, tapi pertama-tama kita memerlukan rantai alat lengan. Saya bekerja di Linux jadi saya memilih: frank.harvard.edu/~coldwell/toolchain (arm-unknown-linux-gnu toolchain). Itu dapat diinstal semudah program lain di Linux. Dalam kasus saya (Fedora Rusia), saya hanya perlu menginstal paket rpm dari situsnya.
Sekarang saatnya menulis contoh sederhana. Program ini sama sekali tidak berguna, tetapi yang utama adalah program itu berfungsi :) Ini kode yang saya tawarkan kepada Anda:
start: @ Baris opsional yang menunjukkan awal program mov r0, #3 @ Muat register r0 dengan nilai 3 mov r1, #2 @ Lakukan hal yang sama dengan register r1, hanya saja sekarang dengan nilai 2 tambahkan r2, r1, r0 @ Tambahkan nilai r0 dan r1, jawabannya ditulis ke r2 mul r3, r1, r0 @ Kalikan nilai register r1 dengan nilai register r0, jawabannya ditulis ke r3 stop: b stop @ Jalur penghentian program
Kami mengkompilasi program untuk mendapatkan file .bin:
/usr/arm/bin/arm-unknown-linux-gnu-as -o arm.o arm.s /usr/arm/bin/arm-unknown-linux-gnu-ld -Ttext=0x0 -o ​​​​arm. elf arm .o /usr/arm/bin/arm-unknown-linux-gnu-objcopy -O biner arm.elf arm.bin
(kodenya ada di file arm.s, dan rantai alat dalam kasus saya ada di direktori /usr/arm/bin/)
Jika semuanya berjalan dengan baik, Anda akan memiliki 3 file: arm.s (kode sebenarnya), arm.o, arm.elf, arm.bin (program sebenarnya yang dapat dieksekusi). Untuk memeriksa pengoperasian program, Anda tidak perlu memiliki perangkat lengan sendiri. Cukup dengan menginstal QEMU. Sebagai referensi:

QEMU adalah program sumber terbuka dan gratis untuk meniru perangkat keras dari berbagai platform.

Termasuk emulasi prosesor Intel x86 dan perangkat I/O. Dapat meniru 80386, 80486, Pentium, Pentium Pro, AMD64 dan prosesor lain yang kompatibel dengan x86; PowerPC, ARM, MIPS, SPARC, SPARC64, m68k - hanya sebagian.

Bekerja pada Suku Kata, FreeBSD, FreeDOS, Linux, Windows 9x, Windows 2000, Mac OS X, QNX, Android, dll.

Jadi, untuk meniru arm Anda memerlukan qemu-system-arm. Paket ini sudah yum, jadi bagi yang punya Fedora tidak perlu repot dan cukup jalankan perintah:
yum instal qemu-system-arm

Selanjutnya, kita perlu meluncurkan emulator ARM agar dapat menjalankan program arm.bin kita. Untuk melakukan ini, kita akan membuat file flash.bin, yang akan menjadi memori flash untuk QEMU. Sangat mudah untuk melakukan ini:
dd if=/dev/zero of=flash.bin bs=4096 count=4096 dd if=arm.bin of=flash.bin bs=4096 conv=notrunc
Sekarang kita memuat QEMU dengan memori flash yang dihasilkan:
qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
Outputnya akan menjadi seperti ini:

$ qemu-system-arm -M koneksi -pflash flash.bin -nografik -serial /dev/null
Monitor QEMU 0.15.1 - ketik "bantuan" untuk informasi lebih lanjut
(qemu)

Program arm.bin kita harus mengubah nilai empat register, oleh karena itu, untuk memeriksa operasi yang benar, mari kita lihat register yang sama. Hal ini dilakukan dengan perintah yang sangat sederhana: info register
Pada output Anda akan melihat 15 register ARM, dan empat di antaranya akan berubah nilainya. Periksa :) Nilai register cocok dengan yang diharapkan setelah eksekusi program:
(qemu) info register R00=00000003 R01=00000002 R02=00000005 R03=00000006 R04=00000000 R05=00000000 R06=00000000 R07=00000000 R08=00000000 R09=000 00000 R10=00000000 R11=00000000 R12=00000000 R13=00000000 R14= 00000000 R15=00000010 PSR=400001d3 -Z-- Sebuah svc32

P.S. Pada artikel kali ini saya mencoba menjelaskan dasar-dasar pemrograman pada assembler ARM. Saya harap Anda menikmatinya! Ini akan cukup untuk mempelajari lebih jauh ke dalam hutan bahasa ini dan menulis program di dalamnya. Jika semuanya berhasil, saya akan menulis lebih lanjut tentang apa yang saya temukan sendiri. Jika ada kesalahan, mohon jangan menendang saya, karena saya baru mengenal assembler.

Halo semua!
Berdasarkan pekerjaan saya adalah seorang programmer Java. Beberapa bulan terakhir bekerja memaksa saya untuk mengenal pengembangan untuk Android NDK dan, karenanya, menulis aplikasi asli dalam C. Di sini saya dihadapkan pada masalah dalam mengoptimalkan perpustakaan Linux. Banyak yang ternyata sama sekali tidak dioptimalkan untuk ARM dan memuat banyak prosesor. Sebelumnya saya praktis tidak pernah memprogram dalam bahasa assembly, sehingga pada awalnya sulit untuk mulai mempelajari bahasa ini, namun saya tetap memutuskan untuk mencobanya. Artikel ini ditulis, boleh dikatakan, dari seorang pemula untuk pemula. Saya akan mencoba menjelaskan dasar-dasar yang telah saya pelajari, saya harap ini menarik minat seseorang. Selain itu, saya akan dengan senang hati menerima kritik yang membangun dari para profesional.

Perkenalan
Jadi pertama-tama mari kita cari tahu apa itu ARM. Wikipedia memberikan definisi ini:

Arsitektur ARM (Mesin RISC Tingkat Lanjut, Mesin Acorn RISC, mesin RISC tingkat lanjut) adalah keluarga inti mikroprosesor 32-bit dan 64-bit berlisensi yang dikembangkan oleh ARM Limited. Perusahaan secara eksklusif mengembangkan kernel dan alat untuknya (kompiler, alat debugging, dll.), menghasilkan uang dengan melisensikan arsitekturnya kepada produsen pihak ketiga.

Jika ada yang belum tahu, kini sebagian besar perangkat seluler dan tablet dikembangkan dengan arsitektur prosesor ini. Keuntungan utama dari keluarga ini adalah konsumsi dayanya yang rendah, sehingga sering digunakan di berbagai sistem tertanam. Arsitekturnya telah berkembang seiring waktu, dan dimulai dengan ARMv7, 3 profil telah ditentukan: 'A'(aplikasi) - aplikasi, 'R' (waktu nyata) - waktu nyata, 'M' (mikrokontroler) - mikrokontroler. Anda bisa membaca sejarah perkembangan teknologi ini dan data menarik lainnya di Wikipedia atau dengan googling di Internet. ARM mendukung mode operasi yang berbeda (Thumb dan ARM, selain itu, Thumb-2 baru-baru ini muncul, yang merupakan campuran dari ARM dan Thumb). Pada artikel ini, kita akan melihat mode ARM itu sendiri, di mana set instruksi 32-bit dijalankan.

Setiap prosesor ARM dibuat dari blok berikut:

  • 37 register (hanya 17 yang terlihat selama pengembangan)
  • Unit logika aritmatika (ALU) - melakukan tugas aritmatika dan logika
  • Barrel shifter - perangkat yang dirancang untuk memindahkan blok data sejumlah bit tertentu
  • CP15 adalah sistem khusus yang mengontrol koprosesor ARM
  • Dekoder instruksi - berkaitan dengan mengubah instruksi menjadi serangkaian operasi mikro
Ini tidak semuanya komponen ARM, tetapi mempelajari lebih dalam tentang konstruksi prosesor berada di luar cakupan artikel ini.
Eksekusi saluran pipa
Prosesor ARM menggunakan pipeline 3 tahap (dimulai dengan ARM8, pipeline 5 tahap diimplementasikan). Mari kita lihat pipeline sederhana menggunakan prosesor ARM7TDMI sebagai contoh. Eksekusi setiap instruksi terdiri dari tiga langkah:

1. Tahap pengambilan sampel (F)
Pada tahap ini, instruksi mengalir dari RAM ke dalam pipa prosesor.
2. Tahap penguraian kode (D)
Instruksi diterjemahkan dan tipenya dikenali.
3. Fase Eksekusi (E)
Data memasuki ALU dan dieksekusi dan nilai yang dihasilkan ditulis ke register yang ditentukan.

Namun dalam pengembangannya harus diperhatikan bahwa ada instruksi yang menggunakan beberapa siklus eksekusi, misalnya load(LDR) atau store. Dalam hal ini tahap pelaksanaan (E) dibagi menjadi beberapa tahap (E1, E2, E3...).

Eksekusi bersyarat
Salah satu fungsi terpenting dari assembler ARM adalah eksekusi bersyarat. Setiap instruksi dapat dieksekusi secara kondisional dan sufiks digunakan untuk ini. Jika akhiran ditambahkan ke nama instruksi, parameternya diperiksa sebelum mengeksekusinya. Jika parameter tidak memenuhi kondisi, maka instruksi tidak dijalankan. Sufiks:
MI - angka negatif
PL - positif atau nol
AL - selalu jalankan instruksi
Masih banyak lagi sufiks eksekusi bersyarat. Baca sufiks dan contoh lainnya di dokumentasi resmi: dokumentasi ARM
Sekarang saatnya untuk mempertimbangkan...
Sintaks dasar assembler ARM
Bagi yang pernah bekerja dengan assembler sebelumnya, sebenarnya Anda bisa melewati poin ini. Untuk semua orang, saya akan menjelaskan dasar-dasar bekerja dengan bahasa ini. Jadi, setiap program bahasa assembly terdiri dari instruksi. Instruksi dibuat dengan cara ini:
(label) (instruksi|operan) (@ komentar)
Label adalah parameter opsional. Instruksi adalah mnemonik langsung dari instruksi ke prosesor. Petunjuk dasar dan penggunaannya akan dibahas di bawah. Operan - konstanta, alamat register, alamat dalam RAM. Komentar adalah parameter opsional yang tidak mempengaruhi eksekusi program.
Daftarkan nama
Nama register berikut diperbolehkan:
1.r0-r15

3.v1-v8 (register variabel, r4 hingga r11)

4.sb dan SB (register statis, r9)

5.sl dan SL (r10)

6.fp dan FP (r11)

7.ip dan IP (r12)

8.sp dan SP (r13)

9.lr dan LR (r14)

10.pc dan PC (penghitung program, r15).

Variabel dan konstanta
Dalam assembler ARM, seperti (secara praktis) bahasa pemrograman lainnya, variabel dan konstanta dapat digunakan. Mereka dibagi menjadi beberapa jenis berikut:
  • numerik
  • asah otak
  • Rangkaian
Variabel numerik diinisialisasi seperti ini:
sebuah SETA 100; variabel numerik "a" dibuat dengan nilai 100.
Variabel string:
kemungkinan SET "literal"; variabel ketidakmungkinan dibuat dengan nilai "literal". PERHATIAN! Nilai variabel tidak boleh melebihi 5120 karakter.
Variabel Boolean masing-masing menggunakan nilai TRUE dan FALSE.
Contoh instruksi assembler ARM
Dalam tabel ini saya telah mengumpulkan instruksi dasar yang diperlukan untuk pengembangan lebih lanjut (pada tahap paling dasar :):

Untuk memperkuat penggunaan instruksi dasar, mari kita tulis beberapa contoh sederhana, tapi pertama-tama kita memerlukan rantai alat lengan. Saya bekerja di Linux jadi saya memilih: frank.harvard.edu/~coldwell/toolchain (arm-unknown-linux-gnu toolchain). Itu dapat diinstal semudah program lain di Linux. Dalam kasus saya (Fedora Rusia), saya hanya perlu menginstal paket rpm dari situsnya.
Sekarang saatnya menulis contoh sederhana. Program ini sama sekali tidak berguna, tetapi yang utama adalah program itu berfungsi :) Ini kode yang saya tawarkan kepada Anda:
start: @ Baris opsional yang menunjukkan awal program mov r0, #3 @ Muat register r0 dengan nilai 3 mov r1, #2 @ Lakukan hal yang sama dengan register r1, hanya saja sekarang dengan nilai 2 tambahkan r2, r1, r0 @ Tambahkan nilai r0 dan r1, jawabannya ditulis ke r2 mul r3, r1, r0 @ Kalikan nilai register r1 dengan nilai register r0, jawabannya ditulis ke r3 stop: b stop @ Jalur penghentian program
Kami mengkompilasi program untuk mendapatkan file .bin:
/usr/arm/bin/arm-unknown-linux-gnu-as -o arm.o arm.s /usr/arm/bin/arm-unknown-linux-gnu-ld -Ttext=0x0 -o ​​​​arm. elf arm .o /usr/arm/bin/arm-unknown-linux-gnu-objcopy -O biner arm.elf arm.bin
(kodenya ada di file arm.s, dan rantai alat dalam kasus saya ada di direktori /usr/arm/bin/)
Jika semuanya berjalan dengan baik, Anda akan memiliki 3 file: arm.s (kode sebenarnya), arm.o, arm.elf, arm.bin (program sebenarnya yang dapat dieksekusi). Untuk memeriksa pengoperasian program, Anda tidak perlu memiliki perangkat lengan sendiri. Cukup dengan menginstal QEMU. Sebagai referensi:

QEMU adalah program sumber terbuka dan gratis untuk meniru perangkat keras dari berbagai platform.

Termasuk emulasi prosesor Intel x86 dan perangkat I/O. Dapat meniru 80386, 80486, Pentium, Pentium Pro, AMD64 dan prosesor lain yang kompatibel dengan x86; PowerPC, ARM, MIPS, SPARC, SPARC64, m68k - hanya sebagian.

Bekerja pada Suku Kata, FreeBSD, FreeDOS, Linux, Windows 9x, Windows 2000, Mac OS X, QNX, Android, dll.

Jadi, untuk meniru arm Anda memerlukan qemu-system-arm. Paket ini sudah yum, jadi bagi yang punya Fedora tidak perlu repot dan cukup jalankan perintah:
yum instal qemu-system-arm

Selanjutnya, kita perlu meluncurkan emulator ARM agar dapat menjalankan program arm.bin kita. Untuk melakukan ini, kita akan membuat file flash.bin, yang akan menjadi memori flash untuk QEMU. Sangat mudah untuk melakukan ini:
dd if=/dev/zero of=flash.bin bs=4096 count=4096 dd if=arm.bin of=flash.bin bs=4096 conv=notrunc
Sekarang kita memuat QEMU dengan memori flash yang dihasilkan:
qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
Outputnya akan menjadi seperti ini:

$ qemu-system-arm -M koneksi -pflash flash.bin -nografik -serial /dev/null
Monitor QEMU 0.15.1 - ketik "bantuan" untuk informasi lebih lanjut
(qemu)

Program arm.bin kita harus mengubah nilai empat register, oleh karena itu, untuk memeriksa operasi yang benar, mari kita lihat register yang sama. Hal ini dilakukan dengan perintah yang sangat sederhana: info register
Pada output Anda akan melihat 15 register ARM, dan empat di antaranya akan berubah nilainya. Periksa :) Nilai register cocok dengan yang diharapkan setelah eksekusi program:
(qemu) info register R00=00000003 R01=00000002 R02=00000005 R03=00000006 R04=00000000 R05=00000000 R06=00000000 R07=00000000 R08=00000000 R09=000 00000 R10=00000000 R11=00000000 R12=00000000 R13=00000000 R14= 00000000 R15=00000010 PSR=400001d3 -Z-- Sebuah svc32

P.S. Pada artikel kali ini saya mencoba menjelaskan dasar-dasar pemrograman pada assembler ARM. Saya harap Anda menikmatinya! Ini akan cukup untuk mempelajari lebih jauh ke dalam hutan bahasa ini dan menulis program di dalamnya. Jika semuanya berhasil, saya akan menulis lebih lanjut tentang apa yang saya temukan sendiri. Jika ada kesalahan, mohon jangan menendang saya, karena saya baru mengenal assembler.

1. Penghitung jam waktu nyata harus diaktifkan (1); Bit pemilihan sumber jam dihapus (2) jika pencatatan jam kerja tidak disediakan oleh generator jam utama.

2. Satu atau kedua bit pilihan peristiwa interupsi (3) harus disetel. Dan dipilih kejadian mana yang akan memicu permintaan interupsi (5).

3. Masker kejadian interupsi (4, 7) harus ditentukan.

2.5 Tentang pemrograman ARM7 di assembler

Set instruksi ARM7 (Bagian 1.4) hanya mencakup 45 instruksi, yang cukup kompleks karena beragamnya metode pengalamatan, bidang kondisional, dan pengubah. Program assembler rumit dan

Dengan sulit dibaca. Oleh karena itu, assembler jarang digunakan dalam pemrograman untuk arsitektur ARM7.

Pada saat yang sama, bahasa tingkat tinggi C menyembunyikan banyak fitur arsitektur dari pemrogram. Pemrogram praktis tidak menyentuh prosedur seperti memilih mode kernel, mengalokasikan memori untuk stack, dan menangani interupsi. Untuk mempelajari prosedur ini, berguna untuk menulis setidaknya satu program sederhana dalam bahasa assembly.

Selain itu, meskipun menggunakan C, Anda masih harus menggunakan bahasa assembly.

1. Harus dikontrol Kompiler C memantau apakah ia mengecualikan perintah-perintah penting selama optimasi, menganggapnya tidak diperlukan. Apakah kompiler menghasilkan kode yang sangat tidak efisien untuk operasi yang relatif sederhana karena optimasi yang tidak memadai. Untuk memastikan bahwa kompiler benar-benar menggunakan sumber daya perangkat keras yang dirancang untuk meningkatkan efisiensi algoritma tertentu.

2. Saat mencari kesalahan atau penyebab pengecualian (bagian 2.4.1).

3. Untuk mendapatkan kode yang benar-benar optimal dalam hal kinerja atau konsumsi memori (bagian 2.2.20, 3.1.5).

Mari kita lihat teknik dasar menulis program di assembler

Dengan tujuannya adalah untuk mendemonstrasikan semua kode yang dieksekusi oleh mikrokontroler, apa adanya, dan tanpa mediasi kompiler C.

Prosedur pembuatan proyek berbasis assembler hampir sama dengan program C (bagian 2.3.1–2.3.3). Hanya ada dua pengecualian:

a) file teks sumber diberi ekstensi *.S;

b) disini diasumsikan file STARTUP.S tidak terhubung dengan program.

2.5.1 Aturan dasar untuk menulis program di assembler

Teks program assembler biasanya diformat dalam empat kolom. Dapat dikatakan setiap baris terdiri dari empat field yaitu: label, operasi, operan, komentar. Bidang dipisahkan satu sama lain dengan karakter tab atau spasi.

Bidang utamanya adalah operasi dan operan. Operasi yang valid dan sintaksnya diberikan dalam tabel (1.4.2)

Label adalah sebutan simbolis dari alamat perintah. Di mana pun, alih-alih label, alamat perintah yang diawali dengan label akan diganti. Paling sering, tag digunakan dalam perintah transfer kontrol. Setiap label harus unik dan bersifat opsional. Tidak seperti banyak versi lainnya, di assembler RealView, label tidak diakhiri dengan titik dua (":").

Komentar secara opsional ditempatkan di akhir baris dan dipisahkan dengan titik koma (“;”).

Mari kita beri contoh sederhana.

2.5.2 Perintah semu

Assembler RealView mendukung apa yang disebut instruksi semu. Instruksi semu adalah notasi mnemonik yang sebenarnya tidak sesuai dengan set instruksi prosesor, namun digantikan oleh satu atau (jarang) beberapa instruksi. Perintah semu adalah sejenis makro dan berfungsi untuk menyederhanakan sintaksis. Daftar perintah semu yang didukung diberikan dalam tabel (2.5.1).

2.5.3 Arahan Majelis

Berbeda dengan perintah, arahan tidak membuat kode yang dapat dieksekusi yang dimuat ke dalam memori mikrokontroler. Arahan hanyalah instruksi kepada assembler; mereka mengontrol pembentukan kode yang dapat dieksekusi.

Mari kita lihat arahan assembler RealView 4 yang sering digunakan.

Nama EQU Konstanta

Menetapkan sebutan simbolis Nama pada Konstanta, yang menjadi sinonim untuk konstanta. Tujuan utamanya adalah untuk memperkenalkan nama-nama register kontrol,

Nama AREA, Parameter

Mendefinisikan area memori dengan Nama yang diberikan. Dengan menggunakan parameter, Anda menentukan tujuan area memori, misalnya DATA (data) atau CODE (kode). Alamat area yang ditentukan bergantung pada tujuan yang dipilih. Area CODE terletak mulai dari alamat 0x00000000, area DATA - di alamat 0x40000000. Program harus memiliki area KODE bernama RESET. Konstanta yang ditempatkan di memori program harus dideklarasikan di bagian dengan sepasang parameter CODE, READONLY.

Menunjukkan titik masuk ke dalam program, menunjukkan "awalnya". Salah satu arahan tersebut harus selalu ada dalam program. Biasanya ditempatkan segera setelah arahan AREA RESET, CODE.

Tabel 2.5.1 – Instruksi semu yang didukung oleh assembler RealView 4

Notasi mnemonik

Operasi

Implementasi sebenarnya

dan sintaksis

ADR (Lanj.)

ke daftar

Menambah atau mengurangi konstanta dari PC co-

Perintah TAMBAHKAN atau SUB

ADRL(Kelanjutan)

ke daftar

Double ADD atau SUB yang melibatkan PC

(rentang alamat yang diperluas)

ASR(Kelanjutan) (S)

Pergeseran aritmatika ke kanan

ASR(Kelanjutan) (S)

operan pergeseran

LDR (Lanj.)

ke daftar

pengalamatan (PC + offset langsung)

Menempatkan konstanta

dalam memori program

LDR(dari alamat indeks-

tion. Offsetnya adalah PC.

LSL (Bersyarat) (S)

Pergeseran logis ke kiri

LSL (Bersyarat) (S)

operan pergeseran

LSR(Kelanjutan) (S)

Pergeseran logis ke kanan

LSR(Kelanjutan) (S)

operan pergeseran

POP(Kelanjutan)

Pulihkan register dari tumpukan

Pemulihan

register

tim

LDMIA R13!,(...)

PUSH (Kelanjutan)

Kelestarian

register

tim

STMDB R13!,(...)

ROR(Kelanjutan) (S)

Pergeseran siklik ke kanan

ROR(Kelanjutan) (S)

operan pergeseran

RRX(Lanjutan.)(S)

Siklus terus menerus

transfer sebanyak 1 digit

operan pergeseran

Nama Ukuran SPASI

Cadangan memori untuk menyimpan data dengan Ukuran tertentu. Nama tersebut menjadi sinonim dengan alamat ruang yang dipesan. Kesatuan ruang alamat memungkinkan arahan ini digunakan untuk permanen dan RAM. Tujuan utamanya adalah untuk membuat variabel global dalam RAM (di area DATA).

Label DCB/DCW/DCD Konstan

Data "Flash" (Konstanta numerik) dalam memori program. Label menjadi sinonim dengan alamat dimana data akan dicatat. Arahan yang berbeda (DCB, DCW dan DCD) berfungsi untuk data dengan ukuran berbeda: byte, kata 16-bit, kata 32-bit (masing-masing).

Berfungsi sebagai tanda berakhirnya file. Semua teks setelah arahan ini diabaikan oleh assembler.

2.5.4 Makro

Makro adalah bagian program yang telah ditentukan sebelumnya yang melakukan beberapa operasi umum. Berbeda dengan subrutin yang dipanggil menggunakan perintah transfer kontrol, penggunaan makro tidak mengurangi kinerja, namun tidak mengurangi konsumsi memori program. Karena setiap kali makro dipanggil, assembler menyematkan seluruh teksnya ke dalam program.

Untuk mendeklarasikan makro, gunakan konstruksi berikut

$Parameter1, $Parameter2, ...

Parameter memungkinkan Anda mengubah teks makro setiap kali Anda mengaksesnya. Di dalam (di badan) makro, parameter juga digunakan dengan tanda “$” sebelumnya. Alih-alih parameter di badan makro, parameter yang ditentukan selama panggilan akan diganti.

Makro disebut seperti ini:

Nama Parameter1, Parameter2, ...

Dimungkinkan untuk mengatur pemeriksaan kondisi dan percabangan.

JIKA "$Parameter" == "Nilai"

Harap dicatat bahwa desain ini tidak mengarah pada pemeriksaan kondisi secara perangkat lunak oleh mikrokontroler. Kondisi ini diperiksa oleh assembler selama pembuatan kode yang dapat dieksekusi.