1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
|
---
language: "MIPS Assembly"
filename: MIPS.asm
contributors:
- ["Stanley Lim", "https://github.com/Spiderpig86"]
translators:
- ["AstiaSun", "https://github.com/AstiaSun"]
lang: uk-ua
---
Мова асемблера MIPS (англ. Microprocessor without Interlocked Pipeline Stages) була написана для роботи з мікропорцесорами MIPS, парадигма яких була описана в 1981 році [Джоном Геннессі](https://uk.wikipedia.org/wiki/Джон_Лерой_Геннессі). Ці RISC процесори використовуються у таких вбудованих системах, як маршрутизатори та мережеві шлюзи.
[Read More](https://en.wikipedia.org/wiki/MIPS_architecture)
```asm
# Коментарі позначені як'#'
# Всі символи після '#' ігноруються лексичним аналізатором асемблера.
# Зазвичай програми поділяються на .data та .text частини
.data # У цьому розділі дані зберігаються у пам'яті, виділеній в RAM, подібно до змінних
# в мовах програмування вищого рівня
# Змінна оголошується наступним чином: [назва]: .[тип] [значенння]
# Наприклад:
hello_world: .asciiz "Hello World\n" # Оголосити текстову змінну
num1: .word 42 # word - це чисельний тип 32-бітного розряду
arr1: .word 1, 2, 3, 4, 5 # Масив чисел
arr2: .byte 'a', 'b' # Масив буквених символів (розмір кожного - 1 байт)
buffer: .space 60 # Виділити місце в RAM
# (не очищується, тобто не заповнюється 0)
# Розміри типів даних
_byte: .byte 'a' # 1 байт
_halfword: .half 53 # 2 байти
_word: .word 3 # 4 байти
_float: .float 3.14 # 4 байти
_double: .double 7.0 # 8 байтів
.align 2 # Вирівнення пам'яті даних, де число
# показує кількість байтів, вирівнених
# у степені 2. (.align 2 означає
# чисельне (word) вирівнювання оскільки
# 2^2 = 4 байти)
.text # Розділ, що містить інструкції та
# логіку програми
.globl _main # Оголошує назву інструкції як
# глобальну, тобто, яка є доступною для
# всіх інших файлів
_main: # програми MIPS виконують інструкції
# послідовно, тобто першочергово код
# буде виконуватись після цієї позначки
# Виведемо на екран "hello world"
la $a0, hello_world # Завантажує адресу тексту у пам'яті
li $v0, 4 # Завантажує значення системної
# команди (вказуючи тип функціоналу)
syscall # Виконує зазначену системну команду
# з обраним аргументом ($a0)
# Регісти (використовуються, щоб тримати дані протягом виконання програми)
# $t0 - $t9 # Тимчасові регістри використовуються
# для проміжних обчислень всередині
# підпрограм (не зберігаються між
# викликами функцій)
# $s0 - $s7 # Збережені регісти, у яких значення
# збегіраються між викликами підпрограм.
# Зазвичай збегрігаються у стеку.
# $a0 - $a3 # Регістри для передачі аргументів для
# підпрограм
# $v0 - $v1 # Регістри для значень, що повертаються
# від викликаної функції
# Типи інструкції завантаження / збереження
la $t0, label # Скопіювати адресу в пам'яті, де
# зберігається значення змінної label
# в регістр $t0
lw $t0, label # Скопівати чисельне значення з пам'яті
lw $t1, 4($s0) # Скопівати чисельне значення з адреси
# пам'яті ресгіста зі зміщенням в
# 4 байти (адреса + 4)
lb $t2, label # Скопіювати буквений символ в частину
# нижчого порядку регістра $t2
lb $t2, 0($s0) # Скопіювати буквений символ з адреси
# в $s0 із зсувом 0
# Подіне використання і 'lh' для halfwords
sw $t0, label # Збегігти чисельне значення в адресу в
# пам'яті, що відповідає змінній label
sw $t0, 8($s0) # Збегігти чисельне значення в адресу,
# зазначеній у $s0, та зі зсувом у 8 байтів
# Така ж ідея використання 'sb' та 'sh' для буквених символів та halfwords.
# 'sa' не існує
### Математичні операції ###
_math:
# Пам'ятаємо, що попередньо потрібно завантажити данні в пам'ять
lw $t0, num # Із розділа з данними
li $t0, 5 # Або безпосередньо з константи
li $t1, 6
add $t2, $t0, $t1 # $t2 = $t0 + $t1
sub $t2, $t0, $t1 # $t2 = $t0 - $t1
mul $t2, $t0, $t1 # $t2 = $t0 * $t1
div $t2, $t0, $t1 # $t2 = $t0 / $t1 (Може не підтримуватись
# деякими версіями MARS)
div $t0, $t1 # Виконує $t0 / $t1. Отримати частку можна
# за допомогою команди 'mflo', остаток - 'mfhi'
# Bitwise Shifting
sll $t0, $t0, 2 # Побітовий здвиг вліво з безпосереднім
# значенням (константою) 2
sllv $t0, $t1, $t2 # Здвиг вліво зі змінною кількістю у
# регістрі
srl $t0, $t0, 5 # Побітовий здвиг вправо (не збегігає
# знак, знак розширюється 0)
srlv $t0, $t1, $t2 # Здвиг вправо зі змінною кількістю у
# регістрі
sra $t0, $t0, 7 # Побітовий арифметичний збвиг вправо
# (зберігає знак)
srav $t0, $t1, $t2 # Здвиг вправо зі змінною кількістю у
# регістрі
# Bitwise operators
and $t0, $t1, $t2 # Побітове І (AND)
andi $t0, $t1, 0xFFF # Побітове І з беспосереднім значенням
or $t0, $t1, $t2 # Побітове АЛЕ (OR)
ori $t0, $t1, 0xFFF # Побітове АЛЕ з беспосереднім значенням
xor $t0, $t1, $t2 # Побітова виключна диз'юнкція (XOR)
xori $t0, $t1, 0xFFF # Побітове XOR з беспосереднім значенням
nor $t0, $t1, $t2 # Побітова стрілка Пірса (NOR)
## Розгалуження ##
_branching:
# В овсновному інструкції розгалуження мають наступну форму:
# <instr> <reg1> <reg2> <label>
# де label - це назва змінної, в яку ми хочемо перейти, якщо зазначене твердження
# правдиве
beq $t0, $t1, reg_eq # Перейдемо у розгалуження reg_eq
# якщо $t0 == $t1, інакше -
# винонати наступну строку
bne $t0, $t1, reg_neq # Розгалужується, якщо $t0 != $t1
b branch_target # Розгалуження без умови завжди виконується
beqz $t0, req_eq_zero # Розгалужується, якщо $t0 == 0
bnez $t0, req_neq_zero # Розгалужується, якщо $t0 != 0
bgt $t0, $t1, t0_gt_t1 # Розгалужується, якщо $t0 > $t1
bge $t0, $t1, t0_gte_t1 # Розгалужується, якщо $t0 >= $t1
bgtz $t0, t0_gt0 # Розгалужується, якщо $t0 > 0
blt $t0, $t1, t0_gt_t1 # Розгалужується, якщо $t0 < $t1
ble $t0, $t1, t0_gte_t1 # Розгалужується, якщо $t0 <= $t1
bltz $t0, t0_lt0 # Розгалужується, якщо $t0 < 0
slt $s0, $t0, $t1 # Інструкція, що посилає сигнал коли
# $t0 < $t1, результат зберігається в $s0
# (1 - правдиве твердження)
# Просте твердження якщо (if)
# if (i == j)
# f = g + h;
# f = f - i;
# Нехай $s0 = f, $s1 = g, $s2 = h, $s3 = i, $s4 = j
bne $s3, $s4, L1 # if (i !=j)
add $s0, $s1, $s2 # f = g + h
L1:
sub $s0, $s0, $s3 # f = f - i
# Нижще наведений приклад знаходження максимального значееня з 3 чисел
# Пряма трансляція в Java з логіки MIPS:
# if (a > b)
# if (a > c)
# max = a;
# else
# max = c;
# else
# max = b;
# else
# max = c;
# Нехай $s0 = a, $s1 = b, $s2 = c, $v0 = повернути регіст
ble $s0, $s1, a_LTE_b # якщо (a <= b) розгалуження(a_LTE_b)
ble $s0, $s2, max_C # якщо (a > b && a <=c) розгалуження(max_C)
move $v0, $s1 # інакше [a > b && a > c] max = a
j done # Перейти в кінець програми
a_LTE_b: # Мітка розгаруження, коли a <= b
ble $s1, $s2, max_C # якщо (a <= b && b <= c) розгалуження(max_C)
move $v0, $s1 # якщо (a <= b && b > c) max = b
j done # Перейти в кінець програми
max_C:
move $v0, $s2 # max = c
done: # Кінець програми
## Цикли ##
_loops:
# Цикл складається з умови виходу та з інстукції переходу після його завершення
li $t0, 0
while:
bgt $t0, 10, end_while # Коли $t0 менше 10, продовжувати інтерації
addi $t0, $t0, 1 # Збільшити значення
j while # Перейти на початок циклу
end_while:
# Транспонування 2D матриці
# Припустмо, що $a0 зберігає адресу цілочисельної матриці розмірністю 3 x 3
li $t0, 0 # Лічильник для i
li $t1, 0 # Лічильник для j
matrix_row:
bgt $t0, 3, matrix_row_end
matrix_col:
bgt $t1, 3, matrix_col_end
# ...
addi $t1, $t1, 1 # Збільшити лічильник стовпця (col)
matrix_col_end:
# ...
addi $t0, $t0, 1
matrix_row_end:
## Функції ##
_functions:
# Фукнції - це процедури, що викликаються, приймають аргументи та повертають значення
main: # Програма починається з головної функції
jal return_1 # jal збереже поточний PC в $ra,
# а потім перейде до return_1
# Як передати аргументи?
# По-перше, ми маємо передати значення аргументів у регістри аргументів
li $a0, 1
li $a1, 2
jal sum # Тепер ми можемо викликати функцію
# Як щодо рекурсії?
# Тут потрібно дещо більше роботи оскільки ми маємо впевнетись, що ми збережемо
# та зчитаємо попередній PC в $ra, оскільки jal автоматично перепише її при виклику
li $a0, 3
jal fact
li $v0, 10
syscall
# Ця функйія повертає 1
return_1:
li $v0, 1 # Завантажити val в регіст $v0
jr $ra # Повернутись до попереднього PC і продовжити виконання
# Function with 2 args
sum:
add $v0, $a0, $a1
jr $ra # Повернутись
# Фекурсивна функція, яка знаходить факторіал
fact:
addi $sp, $sp, -8 # Виділити місце в стеку
sw $s0, ($sp) # Зберегти регістр, що містить поточне число
sw $ra, 4($sp) # Зберегти попереднє PC
li $v0, 1 # Проініціліазувати значення, що повертатиметься
beq $a0, 0, fact_done # Закінчити, якщо параметр 0
# Інакше, продовжити рекурсію
move $s0, $a0 # Скоріювати $a0 в $s0
sub $a0, $a0, 1
jal fact
mul $v0, $s0, $v0 # Множення
fact_done:
lw $s0, ($sp)
lw $ra, ($sp) # Відновити PC
addi $sp, $sp, 8
jr $ra
## Макроси ##
_macros:
# Макроси надзвичайно корисні для заміни блоків коду, що повторюються, за допомогою
# однієї змінної, для покращення читабельності
# Це не заміна функцій.
# Вони мають бути оголошені перед використанням
# Макрос для виведення нових строк (оскільки операція лосить часто виконується)
.macro println()
la $a0, newline # Значення нової строки зберігатиметься тут
li $v0, 4
syscall
.end_macro
println() # Асемблер скопіює цей блок коду сюди
# перед тим, як виконувати його
# Можна передавати параметри у макроси.
# Параметри позначаються знаком '%' з довільною назвою
.macro print_int(%num)
li $v0, 1
lw $a0, %num
syscall
.end_macro
li $t0, 1
print_int($t0)
# Ми таком можемо передавати безпосередньо значення в макроси
.macro immediates(%a, %b)
add $t0, %a, %b
.end_macro
immediates(3, 5)
# Одночасно із назвами змінних
.macro print(%string)
la $a0, %string
li $v0, 4
syscall
.end_macro
print(hello_world)
## Масиви ##
.data
list: .word 3, 0, 1, 2, 6 # Це масив чисел
char_arr: .asciiz "hello" # Це текстовий масив
buffer: .space 128 # Видтляє блок пам'яті, що
# автоматично не очищується
# Ці блоки пам'яті вирівнюють
# оддин одного
.text
la $s0, list # Завантажити адрегу у список
li $t0, 0 # Лічильник
li $t1, 5 # Довжина списку
loop:
bgt $t0, $t1, end_loop
lw $a0, ($s0)
li $v0, 1
syscall # Вивести число
addi $s0, $s0, 4 # Розмір числа - 4 байти
addi $t0, $t0, 1 # Збільшити
j loop
end_loop:
## Включення ##
# Це потрібно для імпорту стороннії файлівв програму (але насправді, код з цього файлу
# копіюється та вставляється в місце, де оголошений імпорт)
.include "somefile.asm"
```
|