# 2019-MPSL Lab1
> 2019 Fall Microprocessor System Lab
> Lab1 ARM Assembly I
> [name=0516009 吳宗達、0616015 劉姿利]
## What to Do
- Hamming Distance:計算兩個數長度為 2-bytes 的 hamming distance,並將結果存放至 result 變數中
- Fibonacci Serial:宣告一數值N (1≤N≤100),計算 Fib(N) 並將回傳值存放至 R4 暫存器
- Bubble Sort:每個 byte 為一個數字,利用組合語言完成長度為 8-byte bubble sort
## How to Do
### Hamming Distance
```asm=
.data
result: .byte 0
X: .word 0x55AA
Y: .word 0xAA55
One: .word 0x0001
.text
.global main
hamm:
movs R2, R1
and R2, R3
add R4, R2
asr R1, R1, #1
cmp R1, #0
bne hamm
bx lr
main:
ldr R0, =X
ldr R1, [R0] // R1 = X
ldr R0, =Y
ldr R2, [R0] // R2 = Y
ldr R0, =One
ldr R3, [R0]
eor R1, R1, R2
ldr R0, =result
ldrb R4, [R0]
bl hamm
strb R4, [R0]
L:
b L
```
#### Assemble Error in Original Code
```
movs R0, #X
```
- **The Error**
mov 用法 (manual 3.5.6 on page 89)
```
MOV{S}{cond} Rd, Operand2
```
其中 `Operand2` 參考 manual (3.3.3 on page 60) 裡面寫到將 constant 作為 `Operand2` 的四種形式:
- 能夠從 8-bit 數字向左 shift 成的 word
- Form `0x00XY00XY`
- Form `0xXY00XY00`
- Form `0xXYXYXYXY`
然而 `X` 為 `0x55AA` 並不在上面任何一個裡面,才會出現 error
- **Solution**
在 `movs` 的方式行不通後,我們決定採用 `ldr`
```asm
ldr R0, =X // 將 X 的位址存到 R0
ldr R1, [R0] // 將 X 的值存入 R1
```
就可以順利把數字存進 register 了
#### `cmp`, `bne` and Label out of Range
原本 15 至 16 行採用的是
```
bnez R1, hamm
```
但是 tag `hamm` 數值太大了會噴 label out of range
所以改成兩行的寫法
#### Load Byte
variable `result` 的資料類型為 `byte`,直接 `ldr` 的話會讀到不相關的位置(多讀到 3 個 byte),所以在讀值跟存值(不包含讀址)的時候我們需要使用 `ldrb` 以及 `strb`
#### 計算 Word 內 1 的數量
以下是 function `hamm`
- 需要被計算 1 的數量的 word 資料被存在 `R1`
- `R3` 內的數值為 1
- 最後 1 的數量會被累加到答案 `R4` 上
```cpp
hamm:
movs R2, R1 // 先複製一份 R1 到 R2
and R2, R3 // R2 和 1 做 and 取得最低位的 bit 數值
add R4, R2 // 將最低位加到答案上
asr R1, R1, #1 // 將 R1 向右 shift 一位
/* 以下三行表示如果 R1 等於 0 就從回圈出去 */
cmp R1, #0
bne hamm
bx lr
```
### Fib
```asm=
.text
.global main
.equ N, 50
fib:
movs R1, #0 // fib_0
movs R2, #1 // fib_1
movs R0, #0
loopI :
add R2, R1
sub R1, R2, R1
cmp R2, #0
blt overflow_error
add R0, #1
cmp R0, #N
blt loopI
movs R4, R2
bx lr
bound_check:
cmp R0, #1 // N < 1
blo range_error
cmp R0, #100 // N > 100
bgt range_error
bx lr
range_error:
movs R3, #0
mvn R4, R3
b end
overflow_error:
movs R3, #1
mvn R4, R3
b end
main:
movs R0, #N
bl bound_check
bl fib
end: b end
```
- 如何使用條件判斷
- manual 3.3.7 Conditional execution
```asm
// in C language
if (a<b) { /* do something */ }
// in asm
cmp a, b
blt do_something
```
- line 14, 17, 22, 24 有用到條件判斷
- 先做一次 compare, 下一行的指令就可以加上條件, 例如 line 21 先做了一次比較, line 22 裡本來的 `b` 指令就可以加上小於的判斷 `lo` 變成 `blo` 也就是先判斷上一個比較的結果是否滿足小於, 如果是才執行 `b`
- 實作迴圈
```asm
// in C++
for (int i=0; i<n; i++){
// do something
}
// in asm
movs R0, #0
loopI :
// do something
add R0, #1
cmp R0, #N
blt loopI
bx lr
```
- 如何設定負數 (overflow 將 R4 設成 -2, 輸入範圍錯誤將 R4 設成 -1)
- `MVN` - manual 3.5.6 MOV and MVN
> MOV{S}{cond} Rd, Operand2
> MOV{cond} Rd, #imm16 // imm16’ is any value in the range 0—65535.
> MVN{S}{cond} Rd, Operand2
- `MOV` 放常數只能放 0 - 65535, 不能設定 -1. 所以要利用 `MVN`, 而 `MVN` 不能直接放常數, 要接一個變數在後面, 像 line 28-29 先將 -1 的補數存到 R3, 再利用 `MVN` 將 R3 的 complement 存入 R4
- 執行
- 按 F8 可以一次跑很多行 (跑到下一次遇到中斷點), 在結尾設定中斷點就可以直接看執行結果
- 測試資料
- [x] n=0 (range error)
- [x] n=1 1
- [x] n=2 1
- [x] n=3 2
- [x] n=20 10946
- [x] n=50 (overflow)
- [x] n=101 (range error)
### Bubble Sort
```asm=
.data
// arr1: .byte 8, 7, 6, 5, 4, 3, 2, 1
arr1: .byte 0x19, 0x34, 0x14, 0x32, 0x52, 0x23, 0x61, 0x29
arr2: .byte 0x18, 0x17, 0x33, 0x16, 0xFA, 0x20, 0x55, 0xAC
.text
.global main
check_swap:
ldrb R5, [R2]
ldrb R6, [R3]
cmp R5, R6
ble end_check_swap
strb R5, [R3]
strb R6, [R2]
end_check_swap:
b ret_check_swap
do_sort:
movs R1, #0
movs R4, R0
add R4, #8
loopI:
movs R2, R0
add R3, R2, #1
loopJ:
b check_swap
ret_check_swap:
add R2, #1
add R3, #1
cmp R3, R4
blt loopJ
add R1, #1
cmp R1, #8
blt loopI
bx lr
print:
mov R1, #0
ldrb r4, [R0, R1]
add R1, #1
ldrb r5, [R0, R1]
add R1, #1
ldrb r6, [R0, R1]
add R1, #1
ldrb r7, [R0, R1]
add R1, #1
ldrb r4, [R0, R1]
add R1, #1
ldrb r5, [R0, R1]
add R1, #1
ldrb r6, [R0, R1]
add R1, #1
ldrb r7, [R0, R1]
bx lr
main:
ldr r0, =arr1
bl do_sort
bl print
ldr r0, =arr2
bl do_sort
bl print
end: b end
```
#### Load Byte with Offset
`ldr` 與 `str` 在 manual 3.4.3 on page 73 中的 syntax 為
```
op{type}{cond} Rt, [Rn, Rm {, LSL #n}]
```
當我們要取出一個 register 中特定位置的 byte
需要寫成
```
ldrb Rt, [Rn, Rm]
strb Rt, [Rn, Rm]
```
代表將以 `Rn` 為起點第 `Rm`(0 base) 個 byte load/store 值出來/進去
#### `lr` 覆蓋問題
##### Problem
bubble sort 的 functions 們設計如下
由 `main` function 呼叫 `do_sort` 再在 `do_sort` 中進行 `swap`
```graphviz
digraph{
node [shape=circle]
check_swap,do_sort,main
main -> do_sort [color=blue]
do_sort -> main [color=red]
do_sort -> check_swap [color=blue]
check_swap -> do_sort
}
```
但由於 `lr` register 只有一個,上圖兩個藍色箭頭都用 `bl` branch with link 過去的話,第二次 `bl` 會把 `do_sort` 返回 `main` 存放在 `lr` 內的 `pc` 位址洗掉,造成紅色箭頭沒辦法順利完成
##### Solution
```asm
check_swap:
ldrb R5, [R2]
ldrb R6, [R3]
cmp R5, R6
ble end_check_swap
strb R5, [R3]
strb R6, [R2]
end_check_swap:
b ret_check_swap
...
b check_swap
ret_check_swap:
...
```
最後我們 swap 的寫法如上,在 `b check_swap` 後馬上接上 `ret_check_swap` 的 label
而 `check_swap` label 後的 `bx lr` 就用 `ble end_check_swap` 和 `b ret_check_swap` 代替,就不會用到 `lr` 了
但更好的方法是善用 stack 與 `sp`,將之後才要用到的第一層 `lr` store 到 stack 上面,要用的時候再取出來,等之後有時間再來改成這樣的方式
## Feedback
覺得寫組語需要的思考方式和平常寫高階語言的方式非常不同,因為 register 是以 `R0`, `R1`, ... 編號下去,沒辦法以意義命名,寫的時候要很清楚每個 register 現在代表什麼以及還有哪些沒用過等等有點不習慣