# 微處理機概論
## 課程主要內容(巨觀)
### 主機板主要三大部分(micro controller)
- **CPU (中央處理器)**(micro proccers)
- Reg(存取暫存器解決應用問題):r0~r15
- 負責處理指令、運算和控制運算流程。
- **Mem (記憶體)**
- 儲存資料和指令,供CPU存取和執行。
- **I/O (輸入輸出裝置)**
- 包括鍵盤、螢幕、印表機等外部設備。
- 透過匯流排(BUS)連接。
#### 使用匯流排連接(BUS)
- 用於在主機板上連接各種元件,實現資料和指令的傳輸。
#### CPU與I/O之間有介面晶片(Interface chips)
- 晶片內有許多暫存器(Registers)。
- 下指令是為了擷取記憶體。
1. **New instructions**(給予新的指令):Independent I/O(INTEL)
2. **Memory-mapped I/O**(記憶體配置I/O的空間):(ARM)
### 本學期的程式都可以被歸類為驅動程式的一環
- 利用CPU寫程式去控制或跟I/O互動。
- CPU需要去Drive(驅動) I/O。

### 2024/2/21
#### 16.2.1 The UART (介面晶片)

The UART = The Universal Asynchronous Receiver/Transmitter
- asynchronous = **非同步的** 需要有一個Buffer放資料
- synchronus = **同步** 會有很多限制導致效率變慢
- 在**非同步**的**傳送**(Transmitter)與**接收**(Reciver)時都需要一個**BUFFER**
- Syncronization ==> RULES
- Asycronizatioln ==> rules **(syncronization mechanisms 同步機制)**
在**非同步**之下都要探討**同步機制**
#### 16.2.2
On-chip static RAM 可以讓使用者自由存取
0x4000 000 - 0x4000 3FFF
AHB Peripherals/VPB Peripherals 留給介面晶片的位置
AHB與VPB是匯流排(BUS)
### 2024/02/27
####
暫存器的空間稱為 = I/O space
記憶體的空間稱為 = Memory space
位置解碼器
Address Decoder
Decoder的基本概念 = 一個位元(1bit)可以選取(0/1)兩個輸出
32-bit address 最多可以選擇2的32次方種輸出(4GB)
選擇到的輸出會勢能
LDR register, memory(effective address)
接下來交給CPU處理
CPU loads from memory to register (execution)
1. put the effective address into address bus to open a memory location
2. turn on read signal(CPU其中一個腳位)to let the data flow(data bus) from memory to CPU
3. read data from data bus into the register
CPU store from register to memory
STR register, memory
順序都一樣要把
2. read signal 改成 write signal
3. read data from data bus 改成 write(put) data into data bus
#### bus有三種
address bus(單向從CPU流到周邊) 其中最重要的東西是address decoder
Data bus 資料匯流排(雙向的) 由CPU利用read signal控制
#### Memory-mapped I/O
same decoder for memory space and I/O space
#### Independent I/O
different decoder for memory and I/O
#### Figure 16.2
4GB = 2的32次方
address decoder 有32個腳位 統稱 **32位元的位置**
假設2GB至4GB分配給I/O space
0GB至2GB分配給Memory space
#### Figure 16.3
只有部分位置會置能
UART只有6個8位元的空間 每個位元都有功能沒有浪費
0xE000C000
0xE000C004
0xE000C008
0xE000C00C U0LCR(**Line Control Register**) 這個暫存器是可以讀寫的 初始值為0
0xE000C014
0xE000C01C
Reciver <- BUFFER(aka Line) <- Transmitter
Line Control Register = 規範Line format
CPU initialize UART
### 2024/03/05
UART 有8位元
9600 bits/second
1 second = 15000000 clock cycles
15000000/9600 = 1562.5
16 bit 存不下
先把1562/16 = 97.~~625~~
等同 shift right 4 bits (除以16)
MOV r6, #0x61 將0x61存入UART的divisor latch
每個腳位都代表一個位元
進來或出來的資料都是位元流
每個腳位都有不同的功能
Write 0b0101 into bits 3-0 of register PINSEL0, the remaining bits unchanged
要把 0b0101 寫入bits 3,2,1,0 其他要保持不變
操作位元的行為: Read, Modify, Write
4個位元(bits)就是一個nibble
副程式的最後一行要有 BX LR 或者是 MOV PC, LR 否則會回不去主程式
PC => Program Counter
主程式呼叫副程式的方式: BX UARTConfig ;LR = return address
LR => Link Register
transmission speed
### 2024/03/26
CPU --POOL--> Peripheral
CPU(IR) <--INTERRUPT00-- Peripheral(IR)
1. BUFFER EMPTY
2. DATA READY
3. IR: Interrupt Request
4. INTA: Interrupt Acknowledge
- 終端確認
CPU 收到訊號後知道終端有什麼事情發生並且發送IR請CPU幫忙服務。
```asm=
LDR r5, =U0START
wait LDRB r6, [r5, #LSR0]
TST r6, #0x20
BEQ wait
STRB r0, [r5]
```
#### 15.1
Cortext-M4 operation modes
版子選擇 TM4C1233H6PM
```asm=
Stack EQU 0x00000100
DivbyZ EQU 0xD14
SYSHNDCTRL EQU 0xD24
Usagefault EQU 0xD2A
NVICBase EQU 0xE000E000
AREA STACK, NOINIT, READWRITE, ALIGN=3
StackMem
SPACE Stack
PRESERVE8
AREA RESET, CODE, READONLY
THUMB
; The vector table sits here
; we'll define just a few of them and leave the rest at 0 for now
DCD StackMem+Stack ; top of stack
DCD Reset_Handler ; Reset Handler
DCD NmiISR ; NMI Handler
DCD FalutISR ; Hard Fault Handler
DCD IntDefaultHandler ; MPU Fault Handler
DCD IntDefaultHandler ; Usage Fault Handler
EXPORT Reset_Handler
ENTRY
Reset_Handler
; enable the edivide-by-zero trap
; located in the NVIC
; base: 0xE000E000
; offset: 0xD14
; bit: 4
LDR r6, =NVICBase
LDR r7, =DivbyZ
LDR r1, [r6, r7]
ORR r1, #0x10 ; enable bit 4
STR r1, [r6, r7]
; now turn on the usage fault exception
LDR r7, =SYSHNDCTRL
LDR r1, [r6, r7]
ORR r1, #0x40000
STR r1, [r6, r7]
; try out a divide by 2 then a divide by 0
MOV r0, #0
MOV r1, #0x11111111
MOV r2, #0x22222222
MOV r3, #0x33333333
; this divide works just fine
UDIV r4, r2, r1
; this divide takes an exception
UDIV r5, r3, r0
Exit B Exit
NmiISR B NmiISR
FaultISR B FaultISR
IntDefaultHandler
; let's read the Usage Fault Status Register
LDR r7, =Usagefault
LDRH r1, [r6, r7]
TEQ r1, #0x200
IT NE
LDRNE r9, =0xDEADDEAD
; r1 should have bit 9 set indicating
; a divide-by-zero has taken place
done B done
ALIGN
END
```
## Quiz1

## HW1
### Part1
1. 執行課本p.348-349程式
印出字串”TKU-ECE+學號+英文名字"
Ex: TKU-ECE 407443210 Michael
2. 改寫上述程式使能印出其反向字串
Ex: leahciM 012344704 ECE-UKT
3. Calculate Keil Tool System Clock Frequency and rewrite the initialization to show about 9600 baud on the divisor latch window
**(1) [Prog1_1]**

```asm=
AREA UARTDEMO, CODE, READONLY
PINSEL0 EQU 0XE002C000 ; controls the function of pins
U0START EQU 0xE000C000 ; start of UART0 register
LCR0 EQU 0xC ; line control register for UART0
LSR0 EQU 0x14 ; line status register for UART0
RAMSTART EQU 0x40000000 ; start of onboard RAM for 2104
ENTRY
start
LDR sp, =RAMSTART ; set up stack pointer
BL UARTConfig ; initialize/configure UART0
LDR r1, =CharData ; starting address of characters
Loop
LDRB r0, [r1], #1 ; load character, increment address
CMP r0, #0 ; null terminated
BLNE Transmit ; send character to UART
BNE Loop ; contiune if not a '0'
done B done
UARTConfig
STMIA sp!, {r5, r6, LR}
LDR r5, =PINSEL0 ; base address of register
LDR r6, [r5] ; get contents
BIC r6, r6, #0xF ; clear out lower nibble
ORR r6, r6, #0x5 ; sets p0.0 to Tx0 and P0.1 to Rx0
STR r6, [r5]
LDR r5, =U0START
MOV r6, #0x83 ; set 8 bits, no parity, 1 stop bit
STRB r6, [r5, #LCR0] ; write control byte to LCR
MOV r6, #0x61 ; 9400 baud @15 MHz VPB clock
MOV r6, #3 ; set DLAB=0
STRB r6, [r5, #LCR0] ; Tx and Rx buffers set up
LDMDB sp!, {r5, r6, PC}
Transmit
STMIA sp!, {r5, r6, LR}
LDR r5, =U0START
wait
LDRB r6, [r5, #LSR0] ; get staus of buffer
TST r6, #0x20 ; buffer empty?
BEQ wait ; spin until buffer's empty
STRB r0, [r5]
LDMDB sp!, {r5, r6, PC}
CharData
DCB "TKU-ECE 411446510 TsaiPingCen", 0
END
```
這段程式碼的功能是初始化UART0並從內存中讀取字符,然後通過UART將字符發送出去,直到讀取到空字符為止。
**(2) [Prog1_2]**

```asm=
AREA UARTDEMO, CODE, READONLY
PINSEL0 EQU 0XE002C000 ; controls the function of pins
U0START EQU 0xE000C000 ; start of UART0 register
LCR0 EQU 0xC ; line control register for UART0
LSR0 EQU 0x14 ; line status register for UART0
RAMSTART EQU 0x40000000 ; start of onboard RAM for 2104
ENTRY
start
LDR sp, =RAMSTART ; set up stack pointer
BL UARTConfig ; initialize/configure UART0
LDR r1, =CharData ; starting address of characters
ADD r1, #28 ; TKU-ECE 411446510 TsaiPingCen has 29 character
MOV r2, #29 ; to print string times
Loop
LDRB r0, [r1], #-1 ; load character, increment address
CMP r2, #0 ; null terminated
SUB r2, #1
BLNE Transmit ; send character to UART
BNE Loop ; contiune if not a '0'
done B done
UARTConfig
STMIA sp!, {r5, r6, LR}
LDR r5, =PINSEL0 ; base address of register
LDR r6, [r5] ; get contents
BIC r6, r6, #0xF ; clear out lower nibble
ORR r6, r6, #0x5 ; sets p0.0 to Tx0 and P0.1 to Rx0
STR r6, [r5]
LDR r5, =U0START
MOV r6, #0x83 ; set 8 bits, no parity, 1 stop bit
STRB r6, [r5, #LCR0] ; write control byte to LCR
MOV r6, #0x61 ; 9400 baud @15 MHz VPB clock
MOV r6, #3 ; set DLAB=0
STRB r6, [r5, #LCR0] ; Tx and Rx buffers set up
LDMDB sp!, {r5, r6, PC}
Transmit
STMIA sp!, {r5, r6, LR}
LDR r5, =U0START
wait
LDRB r6, [r5, #LSR0] ; get staus of buffer
TST r6, #0x20 ; buffer empty?
BEQ wait ; spin until buffer's empty
STRBNE r0, [r5]
LDMDB sp!, {r5, r6, PC}
CharData
DCB "TKU-ECE 411446510 TsaiPingCen", 0
END
```
反轉字串最主要的概念就是先將初始位置設定到字串的最後一個位置,然後反向讀取數據。
**(F5)**
> 按F5時會完整將程式從第一行執行到最後一行
> 會將THR、TSR裡面的值會傳出來,當程式結束時,重新存取U0START(Receive)
**(F10)**
>按 F10 的時候會將程式碼逐步執行但輸出結果會如下圖。

為甚麼會造成這樣的情況呢?我們仔細看程式碼可以知道 `BLNE Transmit` 的意思是當我讀取到的資料不等於零時,則跳轉到 `Transmit` 標記位置,否則繼續執行下一條指令。
我們在前面知道 UART 傳送資料的時候會先將資料送入到 `Transmitter Holding Register` 再送到 `Transmitter Shift Register` 接著輸出,於是因為 `F10` 是逐步執行程式碼,資料被卡在了 `Transmitter Holding Register` 跟 `Transmitter Shift Register` 裡面拿不出來了。

```asm=
Loop
LDRB r0, [r1], #1 ; load character, increment address
CMP r0, #0 ; null terminated
BLNE Transmit ; send character to UART
BNE Loop ; contiune if not a '0'
done B done
```
**(F11)**
> 無窮迴圈 wait,因為使用F11沒有模擬輸出端,使傳出去的值卡在 `Transmitter holding register` 裡面,當在判斷THRE的狀態時,就會因為有值在裡面所以才會不斷進行迴圈。


### 2024/04/09
```arm=
Stack EQU 0x00000100
DivbyZ EQU 0xD14
SYSHNDCTRL EQU 0xD24
Usagefault EQU 0xD2A
NVICBase EQU 0xE000E000
AREA STACK, NOINIT, READWRITE, ALIGN=3
StackMem
SPACE Stack
PRESERVE8
AREA RESET, CODE, READONLY
THUMB
; The vector table sits here
; we'll define just a few of them and leave the rest at 0 for now
DCD StackMem+Stack ; top of stack
DCD Reset_Handler ; Reset Handler
DCD NmiISR ; NMI Handler
DCD FalutISR ; Hard Fault Handler
DCD IntDefaultHandler ; MPU Fault Handler
DCD IntDefaultHandler ; Usage Fault Handler
EXPORT Reset_Handler
ENTRY
Reset_Handler
; enable the edivide-by-zero trap ()
; located in the NVIC
; base: 0xE000E000
; offset: 0xD14
; bit: 4
LDR r6, =NVICBase
LDR r7, =DivbyZ
LDR r1, [r6, r7]
ORR r1, #0x10 ; enable bit 4
STR r1, [r6, r7]
; now turn on the usage fault exception
LDR r7, =SYSHNDCTRL
LDR r1, [r6, r7]
ORR r1, #0x40000
STR r1, [r6, r7]
; try out a divide by 2 then a divide by 0
MOV r0, #0
MOV r1, #0x11111111
MOV r2, #0x22222222
MOV r3, #0x33333333
; this divide works just fine
UDIV r4, r2, r1
; this divide takes an exception
UDIV r5, r3, r0
Exit B Exit
NmiISR B NmiISR
FaultISR B FaultISR
IntDefaultHandler
; let's read the Usage Fault Status Register
LDR r7, =Usagefault
LDRH r1, [r6, r7]
TEQ r1, #0x200
IT NE
LDRNE r9, =0xDEADDEAD
; r1 should have bit 9 set indicating
; a divide-by-zero has taken place
done B done
ALIGN
END
```
### 2024/04/23
啟動偵測錯誤的程式碼
```asm=
; now turn on the usage fault exception
LDR r7, =SYSHNDCTRL
LDR r1, [r6, r7]
LDR r1, #0x40000
STR r1, [r6, r7]
; 執行此行將會進行三個步驟
; 1. Fetch according to PC
; 2. Update PC
; 3. decode
; 4. Execute
; 若將此行註解則不會偵測到 Divide by zero
```
除予0的程式碼
```asm=
MOV r0, #0
MOV r1, #0x11111111
MOV r2, #0x22222222
MOV r3, #0x33333333
UDIV r4, r2, r1
UDIV r5, r3, r0
; 執行此行將會執行三個步驟
; 1. Stacking
; 2. Interrupt Table Lookup
; - 6x4=24 => Vector = 0x56
; 3. LR (暫存)
Exit B Exit ; PC = 0x4C + 4 = 0x50 -> 0x56
```
除予零時執行底下程式
如果跑到`IntDefaultHandler`分支的話,修改模式從 `Privileged thread mode` 改為 `privileged Handler mode`
`pivileged handler mode` => 中斷服務程式
`privileged thread mode` => 被中斷服務程式
```asm=
IntDefaultHandler
LDR r7, =Usagefault
LDRH r1, [r6, r7]
TST r1, #0x200 ; read bit 9 of the Usage Fault Status Register
done B done ; 若改為 `BX LR` 將會回去出錯的地方
```
若放在 `thread mode` 的地方,則`privileged -> unprivileged`
```asm=
IntDefaultHandler
; switch to user Thread mode
; 若放在thread mode
MRS r8, CONTROL
ORR r8, r8, #1
MSR CONTROL, r8
BX LR ; 回去被中斷的地方
```

## HW2
(1) 加入以下兩字串宣稱divide_by_0 DCB “DIVIDE-BY-ZERO Event”, 0not_divide_by_0 DCB “No DIVIDE-BY-ZERO Event”, 0 查看usage fault status register後,如發生除以零錯誤,則將字串divide_by_0寫入從位址0x20000120開始的記憶體空間,反之,則將字串not_divide_by_0寫入從位址0x20000130開始的記憶體空間。(務必說明exception entry三步驟及exception exit兩步驟。)
```asm=
Stack EQU 0x00000100
DivbyZ EQU 0xD14
SYSHNDCTRL EQU 0xD24
Usagefault EQU 0xD2A
NVICBase EQU 0xE000E000
AREA STACK, NOINIT, READWRITE, ALIGN = 3
StackMem
SPACE Stack
PRESERVE8
AREA RESET, CODE, READONLY
THUMB
DCD StackMem + Stack
DCD Reset_Handler
DCD NmiISR
DCD FaultISR
DCD IntDefaultHandler
DCD IntDefaultHandler ; MPU Fault Handler
DCD IntDefaultHandler ; Usage Fault Handler
EXPORT Reset_Handler
ENTRY
Reset_Handler
; 411446510 Tsai Ping Cen
;base: 0xE000E000
;offset: 0xD14
;bit: 4
LDR r6, =NVICBase
LDR r7, =DivbyZ
LDR r1, [r6, r7]
ORR r1, #0x10 ;enable bit 4
STR r1, [r6, r7]
;turn on the usage fault exception
LDR r7, =SYSHNDCTRL
LDR r1, [r6, r7]
ORR r1, #0x40000
STR r1, [r6, r7]
;try out a divide by 2 then a divide by 0!
MOV r0, #0
MOV r1, #0x11111111
MOV r2, #0x22222222
MOV r3, #0x33333333
;this divide works just fine
UDIV r4, r2, r1
;this divide takes an exception
UDIV r5, r3, r0
Exit B Exit
NmiISR B NmiISR
FaultISR B FaultISR
IntDefaultHandler
LDR r7, =Usagefault
LDRH r1, [r6, r7]
TST r1, #0x200
BNE Happened
BEQ NotHappened
Happened
LDR r3, =divide_by_0
LDR r1, =0x20000120 ; starting address of characters
Loop
LDRB r5, [r3], #1
STRB r5, [r1], #1
CMP r5, #0
BNE Loop
;MRS r8, CONTROL
;ORR r8, #1
;MSR CONTROL, r8
; 411446510 Tsai Ping Cen
BX LR
NotHappened
LDR r3, =not_divide_by_0
LDR r1, =0x20000130 ; starting address of characters
Loop2
LDRB r5, [r3], #1
STRB r5, [r1], #1
CMP r5, #0
BNE Loop2
MRS r8, CONTROL
ORR r8, #1
MSR CONTROL, r8
BX LR
;r1 should have bits 9 set indicating
;a divide-by-zero has taken place
done B done
divide_by_0
DCB "DIVIDE-BY-ZERO Event", 0
not_divide_by_0
DCB "“No DIVIDE-BY-ZERO Event", 0
ALIGN
END
```
(2)
- (a) from privileged thread mode to unprivileged thread mode
```asm=
Stack EQU 0x00000100
DivbyZ EQU 0xD14
SYSHNDCTRL EQU 0xD24
Usagefault EQU 0xD2A
NVICBase EQU 0xE000E000
AREA STACK, NOINIT, READWRITE, ALIGN = 3
StackMem
SPACE Stack
PRESERVE8
AREA RESET, CODE, READONLY
THUMB
DCD StackMem + Stack
DCD Reset_Handler
DCD NmiISR
DCD FaultISR
DCD IntDefaultHandler
DCD IntDefaultHandler ; MPU Fault Handler
DCD IntDefaultHandler ; Usage Fault Handler
EXPORT Reset_Handler
ENTRY
Reset_Handler
; 411446510 Tsai Ping Cen
;base: 0xE000E000
;offset: 0xD14
;bit: 4
LDR r6, =NVICBase
LDR r7, =DivbyZ
LDR r1, [r6, r7]
ORR r1, #0x10 ;enable bit 4
STR r1, [r6, r7]
;turn on the usage fault exception
LDR r7, =SYSHNDCTRL
LDR r1, [r6, r7]
ORR r1, #0x40000
STR r1, [r6, r7]
;try out a divide by 2 then a divide by 0!
MOV r0, #0
MOV r1, #0x11111111
MOV r2, #0x22222222
MOV r3, #0x33333333
; 411446510 Tsai Ping Cen
;this divide works just fine
UDIV r4, r2, r1
;this divide takes an exception
UDIV r5, r3, r0
Exit B Exit
NmiISR B NmiISR
FaultISR B FaultISR
IntDefaultHandler
LDR r7, =Usagefault
LDRH r1, [r6, r7]
TST r1, #0x200
BNE Happened
BEQ NotHappened
Happened
LDR r3, =divide_by_0
LDR r1, =0x20000120 ; starting address of characters
Loop
LDRB r5, [r3], #1
STRB r5, [r1], #1
CMP r5, #0
BNE Loop
MRS r8, CONTROL
ORR r8, #1
MSR CONTROL, r8
; 411446510 Tsai Ping Cen
BX LR
NotHappened
LDR r3, =not_divide_by_0
LDR r1, =0x20000130 ; starting address of characters
Loop2
LDRB r5, [r3], #1
STRB r5, [r1], #1
CMP r5, #0
BNE Loop2
MRS r8, CONTROL
ORR r8, #1
MSR CONTROL, r8
BX LR
;r1 should have bits 9 set indicating
;a divide-by-zero has taken place
done B done
divide_by_0
DCB "DIVIDE-BY-ZERO Event", 0
not_divide_by_0
DCB "“No DIVIDE-BY-ZERO Event", 0
ALIGN
END
```
### 第三部分

```asm=
AREA STACK, CODE, READONLY
ENTRY
; 411446510 Tsai Ping Cen
LDR sp, =0x40000080
LDR r2, =0x70
BL set_priority_group_number
BL read_priority_group_number
LDR r4, =0xf
LDR r5, =0x1f
BL count
done B done
set_priority_group_number
STMIA sp!, {r0, r1, lr}
LDR r0, =0x400000c8
LDR r1, [r0]
BIC r1, r1, #0x70
ORR r1, r1, r2
STR r1, [r0]
LDMDB sp!, {r0, r1, pc}
; 411446510 Tsai Ping Cen
read_priority_group_number
STMIB sp!, {r0, r1, lr}
LDR r0, =0x400000c8
LDR r3, [r0]
LSR r3, r3, #4
AND r3, r3, #7
LDMDA sp!, {r0, r1, pc}
count
STMDA sp!, {r0, r1, lr}
LDR r0, =0x400000c8
LDR r6, [r0]
BIC r7, r6, r5
BIC r8, r6, r4
LDMIB sp!, {r0, r1, pc}
ALIGN
END
```
## Quiz2

2. Write a sequence of instructions to get the width of an interrupt priority register (address 0x40000486) and put the width in R2.
- (a) using LSL with test pattern LSL.
- (b) using LSR without test pattern LSR.
> 第二小題(a)首先要設定 priority group 設定在 r2 這個暫存器中,接著使用 LSL(邏輯左移)和測試模式來獲取寬度。
> (a) 使用 LSL 和測試模式
1. 從地址 0x40000486 讀取寄存器的值。
2. 使用一個測試模式,逐位左移這個模式,並測試左移後的結果。
3. 計算左移的次數,直到測試結果為零,這樣就可以確定寄存器的寬度。
4. 將這個寬度存入 R2。
```ASM=
AREA pri, CODE, READONLY
ENTRY
LDR r0, =0x40000486
MOV r4, #0xFF ; test data
STRB r4, [r0]
LDRB r4, [r0]
MOV r2, #0
MOV r3, #1
TEST CMP r2, #8
BEQ OUT
TST r4, r3
BEQ OUT
ADD r2, r2, #1
LSL r3, r3, #1
B TEST
OUT
done B done
END
```
> (b) 使用 LSR 不用測試模式(意思是不要移動測試的值)
1. 從地址 0x40000486 讀取寄存器的值。
2. 使用一個測試模式,逐位右移要檢測bit width的值(0xFF),並測試右移後的結果。
3. 計算右移的次數,直到遇到1的時候跳出,這樣就可以確定寄存器的寬度。
4. 計算 8 - 遇到0的次數 = bit width
```asm=
AREA pri, CODE, READONLY
ENTRY
LDR r0, =0x40000486
MOV r3, #0xFF ; 11111111
STRB r3, [r0]
LDRB r3,[r0]
MOV r1, #1
MOV r2, #0
MOV r4, #8
TEST CMP r2, #8
BEQ OUT
TST r3, r1
BNE OUT
ADD r2, r2, #1
LSR r3, r3, #1
B TEST
OUT SUB r4, r2
MOV r2, r4
done B done
END
```
## 期末考可能會考的內容
- [ ] priority group number
- [ ] 計算 IRQ 的 pre-emption priority及 subpriority
LED
```asm=
AREA LED, code, readonly
ENTRY
LDR r0, =0x40000000
SUB r7, r7, r7
MOV r6, #2
mainloop
STR r6, [r0, #0x38]
MOVT r7, 0xF0000
spin
SUBS r7, r7, #1
BNE spin
CMP r6, #8
LTE LT
LSLLT r6, r6, #1
MOVGE r6, #2
B mainloop
stop B stop
END
```