CPU
內部的寄存器中,有一種特殊的寄存器(對于不同的處理器,個數和結構都可能不同)。這種寄存器在ARM
中,被稱為狀態寄存器就是CPSR
(current program status register
)寄存器。
CPSR
和其他寄存器不一樣,其他寄存器是用來存放數據的,都是整個寄存器具有一個含義。而CPSR
寄存器是按位起作用的,也就是說,它的每一位都有專門的含義,記錄特定的信息。
CPSR
寄存器是32位
的
CPSR
的低8位
(包括I
、F
、T
和M[4:0]
)稱為控制位,程序無法修改。除非CPU
運行于特權模式下,程序才能修改控制位N
、Z
、C
、V
均為條件碼標志位。它們的內容可被算術或邏輯運算的結果所改變,并且可以決定某條指令是否被執行。意義重大
案例:
改變
cpsr
寄存器的值,代碼執行流程也會跟隨改變打開
ViewController.m
文件,寫入以下代碼:#import "ViewController.h" @implementation ViewController void funcB(int a, int b){ if(a == b){ printf("a == b"); } else{ printf("error"); } } - (void)viewDidLoad { funcB(10, 20); } @end
真機運行項目,使用斷點單步調試,來到
funcB
函數
cmp
指令和b.ne
指令搭配使用cmp w8, w9
:比較w8
、w9
的值b.ne
指令,有條件的跳轉原本代碼流程:
funcB
函數的a
、b
兩個參數,分別傳入10
和20
。此時w8 ≠ w9
,執行b.ne
指令,應該跳轉到標號處
b.ne
指令的跳轉,和cpsr
寄存器有關
cpsr
寄存器的值為0x80000000
,將0x8
轉為二進制1000
。將首位1
右移一位,變為0100
,十六進制位0x4
使用
register write
命令,改變cpsr
寄存器的值為0x40000000
向下執行
1
步,沒有跳轉到b.ne
指令的標號處,而是向下繼續執行了代碼
原本傳入的
a
、b
參數,它們的值完全不一樣。但由于cpsr
寄存器被改變,導致代碼執行流程跟隨改變
N(Negative)標志
CPSR
的第31
位是N
,符號標志位。它記錄相關指令執行后,其結果是否為負。如果為負N = 1
,如果是非負數N = 0
在
ARM64
的指令集中,有的指令在執行時影響狀態寄存器,例如:add\sub\or
等,它們大都是運算指令(進行邏輯或算數運算)
案例:
改變
N
標志位打開
ViewController.m
文件,寫入以下代碼:#import "ViewController.h" @implementation ViewController void funcB(int a, int b){ asm( "mov w0,#0x0\n" "adds w0,w0,#-0xff\n" ); } - (void)viewDidLoad { funcB(10, 20); } @end
向下執行
1
步,將#0x0
寫入w0
cpst
為0x40000000
,此時N
標志位為0
向下執行
1
步,w0
加等#-0xff
cpst
為0x80000000
,此時N
標志位為1
在
ARM64
中,add
加法不帶進位,而adds
帶進位的。sub
和subs
是做減法,用法類似。如果指定了s
,則這些指令將會根據結果來更新N
、Z
、C
和V
標記
Z(Zero)標志
CPSR
的第30
位是Z
,0
標志位。它記錄相關指令執行后,其結果是否為0
。如果結果為0
,那么Z = 1
。如果結果不為0
,那么Z = 0
對于
Z
的值,我們可以這樣來看,Z
標記相關指令的計算結果是否為0
。如果為0
,則Z
要記錄下是0
這樣的肯定信息。在計算機中1
表示邏輯真,表示肯定。所以當結果為0
的時候Z = 1
,表示結果是0
。如果結果不為0
,則Z
要記錄下不是0
這樣的否定信息。在計算機中0
表示邏輯假,表示否定。所以當結果不為0
的時候Z = 0
,表示結果不為0
案例:
改變
Z
標志位打開
ViewController.m
文件,寫入以下代碼:#import "ViewController.h" @implementation ViewController void funcB(int a, int b){ asm( "mov w0,#0x0\n" "adds w0,w0,#0x1\n" ); } - (void)viewDidLoad { funcB(10, 20); } @end
向下執行
1
步,將#0x0
寫入w0
cpst
為0x40000000
,此時N
標志位為0
,Z
標志位為1
向下執行
1
步,w0
加等#0x1
cpst
為0x00000000
,此時N
標志位為0
,Z
標志位為0
計算結果為
1
,結果為非負數,所以N
標志位為0
。結果不為零,所以Z
標志位為0
C(Carry)標志
CPSR
的第29
位是C
,進位標志位。一般情況下,進行無符號數的運算
- 加法運算:當運算結果產生了進位時(無符號數溢出),
C=1
,否則C=0
- 減法運算(包括
CMP
):當運算時產生了借位時(無符號數溢出),C=0
,否則C=1
對于位數為
N
的無符號數來說,其對應的二進制信息的最高位,即第N - 1
位,就是它的最高有效位,而假想存在的第N
位,就是相對于最高有效位的更高位
進位
我們知道,當兩個數據相加的時候,有可能產生從最高有效位想更高位的進位。例如:兩個
32位
數據,0xaaaaaaaa + 0xaaaaaaaa
,將產生進位。由于這個進位值在32位
中無法保存,我們就只是簡單的說這個進位值丟失了。其實CPU
在運算的時候,并不丟棄這個進位制,而是記錄在一個特殊的寄存器的某一位上。ARM
下就用C
位來記錄這個進位值
案例:
打開
ViewController.m
文件,寫入以下代碼:#import "ViewController.h" @implementation ViewController void funcB(int a, int b){ asm( "mov w0,#0xaaaaaaaa\n" "adds w0,w0,w0\n" "adds w0,w0,w0\n" "adds w0,w0,w0\n" "adds w0,w0,w0\n" ); } - (void)viewDidLoad { funcB(10, 20); } @end
向下執行
1
步,將#0xaaaaaaaa
寫入w0
cpst
為0x40000000
,此時N
標志位為0
,Z
標志位為1
,C
標志位為0
向下執行
1
步,w0
加等#0xaaaaaaaa
w0
為0x55555554
,因為0xaaaaaaaa + 0xaaaaaaaa
結果溢出cpst
為0x30000000
,此時N
標志位為0
,Z
標志位為0
,C
標志位為1
向下執行
1
步,w0
加等#0x55555554
w0
為0xaaaaaaa8
cpst
為0x90000000
,此時N
標志位為1
,Z
標志位為0
,C
標志位為0
向下執行
1
步,w0
加等#0xaaaaaaa8
w0
為0x55555550
,因為0xaaaaaaa8 + 0xaaaaaaa8
結果溢出cpst
為0x30000000
,此時N
標志位為0
,Z
標志位為0
,C
標志位為1
向下執行
1
步,w0
加等#0x55555550
w0
為0xaaaaaaa0
cpst
為0x90000000
,此時N
標志位為1
,Z
標志位為0
,C
標志位為0
借位
當兩個數據做減法的時候,有可能向更高位借位。例如:兩個
32位
數據:0x00000000 - 0x000000ff
,將產生借位。借位后,相當于計算0x100000000 - 0x000000ff
。得到0xffffff01
這個值。由于借了一位,所以C
位用來標記借位
案例:
打開
ViewController.m
文件,寫入以下代碼:#import "ViewController.h" @implementation ViewController void funcB(){ asm( "mov w0,#0x0\n" "subs w0,w0,#0xff\n" "subs w0,w0,#0xff\n" "subs w0,w0,#0xff\n" ); } - (void)viewDidLoad { funcB(); } @end
向下執行
1
步,將#0x0
寫入w0
cpst
為0x40000000
,此時N
標志位為0
,Z
標志位為1
,C
標志位為0
向下執行
1
步,w0
減等#0xff
w0
為0xffffff01
,因為#0x0 - #0xff
,結果溢出cpst
為0x80000000
,此時N
標志位為1
,Z
標志位為0
,C
標志位為0
向下執行
1
步,w0
減等#0xff
w0
為0xfffffe02
cpst
為0xa0000000
,此時N
標志位為1
,Z
標志位為0
,C
標志位為1
向下執行
1
步,w0
減等#0xff
w0
為0xfffffd03
cpst
為0xa0000000
,此時N
標志位為1
,Z
標志位為0
,C
標志位為1
V(Overflow)溢出標志
CPSR
的第28
位是V
,溢出標志位。在進行有符號數運算的時候,如果超過了機器所能標識的范圍,稱為溢出
- 正數 + 正數 = 負數,溢出
V = 1
。否則V = 0
- 負數 + 負數 = 正數,溢出
V = 1
。否則V = 0
- 正數 + 負數,在同等寬度下,不可能溢出
案例1:
正數 + 正數 = 負數
打開
ViewController.m
文件,寫入以下代碼:#import "ViewController.h" @implementation ViewController void funcB(){ asm( "mov w0,#0xaaaaaaaa\n" "adds w0,w0,w0\n" "adds w0,w0,w0\n" ); } - (void)viewDidLoad { funcB(); } @end
向下執行
1
步,將#0xaaaaaaaa
寫入w0
cpst
為0x40000000
,此時N
標志位為0
,Z
標志位為1
,C
標志位為0
,V
標志位為0
向下執行
1
步,w0
加等#0xaaaaaaaa
w0
為0x55555554
,因為0xaaaaaaaa + 0xaaaaaaaa
結果溢出cpst
為0x30000000
,此時N
標志位為0
,Z
標志位為0
,C
標志位為1
,V
標志位為1
向下執行
1
步,w0
加等#0x55555554
w0
為0xaaaaaaa8
,因為0x55555554 + 0x55555554
對于有符號數,結果溢出cpst
為0x90000000
,此時N
標志位為1
,Z
標志位為0
,C
標志位為0
,V
標志位為1
在計算過程中,底層無法得知當前是
無符號數
還是有符號數
。運算時,C
標志位按無符號數
運算,而V
標志位按有符號數
運算
案例2:
負數 + 負數 = 正數
打開
ViewController.m
文件,寫入以下代碼:#import "ViewController.h" @implementation ViewController void funcB(){ asm( "mov w0,#-0x7fffffff\n" "adds w0,w0,w0\n" ); } - (void)viewDidLoad { funcB(); } @end
向下執行
1
步,將#-0x7fffffff
寫入w0
cpst
為0x40000000
,此時N
標志位為0
,Z
標志位為1
,C
標志位為0
,V
標志位為0
向下執行
1
步,w0
加等#-0x7fffffff
w0
為0x00000002
,因為#-0x7fffffff + #-0x7fffffff
結果溢出cpst
為0x30000000
,此時N
標志位為0
,Z
標志位為0
,C
標志位為1
,V
標志位為1
總結
狀態寄存器
- 狀態寄存器就是
CPSR
,也稱之為標志寄存器
- 在
ARM64
中,狀態寄存器(cpsr
)為32位
- 最高
4位
(28
、29
、30
、31
)為標志位
N
標志
- 負標記位
- 執行結果為負數,
N = 1
。非負數,N = 0
Z
標志
0
標記位- 結果為
0
,Z = 1
。結果非0
,Z = 0
C
標志
- 無符號數溢出
- 加法:進位
C = 1
,否則C = 0
- 減法:借位
C = 0
,否則C = 1
V
標志
- 有符號數溢出
- 正數 + 正數 = 負數,溢出
V = 1
。否則V = 0
- 負數 + 負數 = 正數,溢出
V = 1
。否則V = 0
- 正數 + 負數,在同等寬度下,不可能溢出
匯編指令
subs
指令:和sub
指令相似,做減法。影響目標寄存器,同時影響狀態寄存器
adds
指令:和add
指令相似,做加法。影響目標寄存器,同時影響狀態寄存器