注釋/說明
L : 字面量
R: 內存變量
M: 寄存器
S: 標號
寄存器
在 masm 匯編中, 一般有以下幾種寄存器
通用目的寄存器 | 段寄存器 | 指令指針寄存器 | 標志位寄存器 |
---|---|---|---|
eax | cs | eip | OF |
ebx | ds | SF | |
ecx | es | ZF | |
edx | ss | CF | |
esi | AF | ||
edi | PF | ||
ebp | DF | ||
esp | IF | ||
TF |
通用目的寄存器
eax : 全稱為 Accumulator Register(累加器). 在進行乘法和除法時會將其中一部分操作結果自動放在 eax 中, 調用函數時也需要把返回值保存在 eax 中. 因此在執行這些操作時不要用 eax 存儲數據
ebx : 全稱為 Base Register(基地址寄存器). 一般沒什么專門的用途, 常用來充當臨時變量或地址
ecx : 全稱為 Counter Register(計數寄存器). 一般用來充當計數器, 在循環中的使用較多.
edx : 全稱為 Data Register(數據寄存器). edx 常被 eax 用來擴展數位 (例如進行有符號除法時, edx : eax 共同組成被除數, edx 的最高位充當符號位)
esi 和 edi : 全稱為 Source Index (源變址寄存器) / The Destination Index (目的變址寄存器). 這兩者的區別不是很大, 不過在 CPU 中往往是 esi 讀取數據, 而 edi 寫入數據. 一般在字符串操作上面的用途較多, 且往往和循環指令 STOS, MOVSB, SCASB 一起出現來完成大量數據的保存, 加載與掃描工作.
ebp : 全稱為 Base Pointer (棧基指針寄存器). 在函數調用時常用來作棧底指向棧中的數據, 在整個函數運行的過程中其內部存放的地址不會發生改變. 該寄存器為特殊寄存器, 不能對其進行修改.
esp : 全稱為 Stack Pointer (棧頂指針寄存器). 與 ebp 一樣與函數調用有關, 且每一次的 push, pop, ret, call 操作時其內部的地址都會發生改變. 該寄存器為特殊寄存器, 不能對其進行修改.
段寄存器
cs : 全稱是Code Segement (代碼段寄存器).
ds : 全稱是Data Segment (數據段寄存器).
es : 全稱是Extra(Data) Segment (擴展數據段寄存器)
ss : 全稱是Stack Segment (棧段寄存器)
由於我還沒能了解段寄存器的作用, 因此只能將完整的名稱寫上, 后續會補
指令指針寄存器
eip : 全稱是Extend Instruction Pointer (擴展指令指針寄存器). 主要用來指向內存中的地址, 以表示接下來應該獲取, 解碼並執行的指令. 該寄存器也是一種特殊寄存器, 不能對其進行修改.
標志位寄存器
寄存器 | 功能 | 全稱 | 標志形式 |
---|---|---|---|
OF | 溢出標志 | overflow flag | 溢出置1, 否則置0 |
SF | 符號標志 | sign flag | 正數置0, 負數置1 |
ZF | 零標志 | zero flag | 結果為0置1, 否則置0 |
CF | 進位標志 | carry flag | 無符號數最高有效位進位, 或有符號數最高有效位需要借位, 則置1. 否則置0 |
AF | 輔助進位標志 | auxiliary carry flag | 用以表示加減法做到一半時有沒有形成進位/借位,如果有則置1, 否則置0 |
PF | 奇偶標志 | parity flag | 若結果為1的二進制位個數是偶數則置1, 為奇數置0 |
DF | 方向標志 | direction flag | 用以代表esi和edi的增減, 置1遞減, 指令遞增 |
IF | 中斷標志 | interrupt flag | IF置1,響應外部中斷,置0則屏蔽外部中斷; |
TF | 陷進標志 | trap flag | TF置1時,處理器每次只執行一條指令,即單步執行. 置0反之 |
mov 和 movl
將源操作數的內容復制到目標操作數, 兩者必須有一個是寄存器
mov M/R, L/M/R
xchg
交換兩個操作數的內容, 兩者必須有一個是寄存器
xchg M/R, M/R
add 和 sub
add 為加法指令, sub 為減法指令. 兩個操作數必須有一個是寄存器
add M/R, L/M/R
sub M/R, L/M/R
neg
切換操作數的正負號, 即求補碼
neg M/R
inc 和 dec
inc 自加指令, dec 自減指令
inc M/R
dec M/R
mul 和 imul
mul 為無符號乘法, imul 為有符號乘法.
mul 的積存儲在 edx : eax 中, 其中eax作被乘數, 只支持單個操作數. imul 的積存儲在自定義的寄存器中, 被乘數和乘數也可存儲在自定義的寄存器中, 最多支持三個操作數.
mul M/R
imul M/R
imul R, L/M/R
imul R, M/R, L
div 和 idiv
div 為無符號除法, idiv 為有符號除法.
div 計算的被除數存儲在 edx : eax 中, 除數為自定義的內存變量或寄存器, 商存儲在 eax 中, 余數存儲在 edx 中. 只支持單操作數
idiv 與 div 差不多. 不過在進行有關負數除法運算時, 需要將 edx 和 eax 全部求補才可進行運算, 否則會出錯.
div M/R
idiv M/R
SHL 和 SHR
SHL 和 SHR 為邏輯左移 / 邏輯右移. 每移動 n 位二進制數, 結果擴大 / 縮小原來的 \(2^{n}\) 倍, 注意如果值為負數, 進行邏輯右移后其可能會出錯.
shl M/R, L
shr M/R, L
SAL 和 SAR
SAL 和 SLR 為算數左移 / 算術右移. 每移動 n 位二進制數, 結果擴大 / 縮小原來的 \(2^{n}\) 倍, 負數進行算術右移不會像邏輯右移一樣出現錯誤.
sal M/R, L
sar M/R, L
數位擴展
指令 | 寄存器 | 大小 | 擴展到 | 寄存器 | 大小 |
---|---|---|---|---|---|
cwd | ax | 16 | → | dx : ax | 32 |
cdq | eax | 32 | → | edx : eax | 64 |
cqo | rax | 64 | → | rdx : rax | 128 |
有了數位擴展指令, 就可以很方便的進行負數的有符號除法.
xor edx, edx
xor eax, eax
mov eax, -1
cdq
mov ebx, 1
idiv ebx
寄存器結果
eax = FFFFFFFF
edx = 00000000
lea
加載有效地址, 將源操作數的地址加載到目標操作數中. 由於實際地址要在程序運行時才能知道, 因此涉及這些地址的操作應該用 lea 來完成. 該指令的目標操作數必須是寄存器
lea R, M
ptr
用來將數據移動到與之大小不符的另一個區域所需要使用的指令, 例如一個為 4 個字節的 int 內存變量, 若移動到一個其大小為 2 個字節的 ax 寄存器中, 則需要 ptr 進行轉換; 或一個為 2 個字節的 int 內存變量, 若移動到一個其大小為 4 個字節的 eax 寄存器中, 也需要 ptr 進行轉換. size 必須與 R 的大小相符, 源操作數必須是內存變量
mov R, size ptr M
int x = -1;
__asm
{
xor eax, eax
mov ax, word ptr x
}
寄存器結果
;轉換前的eax
eax = 00000000
;轉換后的eax
eax = 0000FFFF
movzx 和 movsx
與 ptr 指令一樣, 用來將數據移動到與之大小不符的另一個區域所需要使用的指令, 但是限制沒有 ptr 那么多. 擴展的大小視目標操作數大小而定
movzx 的 zx 表示 Zero eXtend , 用 0 來擴展數位, movsx 的 sx 表示 Sign eXtend, 用符號位來擴展數位
movzx R, M/R
movsx R, M/R
offset
offset 看上去也是和 lea 一樣用來獲取地址的, 不過與 lea 不同的是, offset 獲取的是內存變量或標號到數據段數據段的起始地址的距離. 源操作數只能是內存變量或標號. 且 offset 只能獲取編譯時已知的地址 (如符號常量等)
mov M/R, offset M/S