x86匯編語言筆記
8086通用寄存器
16位寄存器:AX、BX、CX、DX、SI、DI、BP、SP。
前4個可分為高8位和低8位來使用:AH、AL、BH、BL、CH、CL、DH、DL。
內存分段
采用分段技術解決地址重定位問題,在硬件級別用兩個段寄存器來支持,代碼段寄存器CS和數據段寄存器DS。
實模式下CPU訪問物理地址的方式:
段基址:偏移地址
。
8086段寄存器
- 代碼段寄存器:CS
- 數據段寄存器:DS
- 附加段寄存器:ES
- 棧段寄存器:SS
如何訪問指令?
當程序開始時,CS指向代碼段的起始地址,IP指令指針指向段內偏移。即
CS:IP
。注意:一般沒有指定段寄存器時,默認使用DS。
如何訪問數據?
在訪問內存單元時,則采用
DS:偏移地址
。注意1:由於8086提供20位的物理地址,所以在計算物理地址時,要將段寄存器左移4位再加上偏移地址。由此可以得出8086最大只能訪問1MB的內存空間。
注意2:內存是以字節為單位,內存中的每一個字節都對應一個物理地址。
計算機的啟動流程
注意:主引導扇區的最有兩個字節是0x55和0xaa。
顯存
顯存存在於顯卡之中,一般CPU是通過把顯存映射到
0xB8000~0xBFFFF
這部分內存空間,來直接訪問顯存,顯卡在加電自檢后會初始化為80 * 25模式,即屏幕上顯示25行,每行80個字符,每屏總共2000個字符。顯卡通過ASCII編碼來識別CPU傳入顯存的數據。
屏幕上每個字符對應着顯存中的連續兩個字節,前一個字節為字符的ASCII代碼,后一個字節為字符的顯示屬性。
注意:Intel的處理器不允許把一個立即數傳送到段寄存器,必須通過其他寄存器來中轉。
mov指令
mov 目的操作數, 源操作數
注意:mov指令不允許目的操作數和源操作數都為內存單元,並且目的操作數不能為立即數。
注意:內存的增長是從低地址到高地址的。
除法
無符號除法指令:div
-
16位除法:
被除數放在ax寄存器中。
格式:
div x ;這里的x可以代表寄存器或者[內存單元]或者立即數
\[ax / x = s \cdots m \]參數:
- x:除數。
- s:表示商,放在al寄存器。
- m:表示余數,放在ah寄存器。
-
32位除法:
被除數的高16位放在dx寄存器中,低16位放在ax中。
格式:
div x ;這里的x可以代表寄存器或者[內存單元]或者立即數
\[(dx << 4 + ax) / x = s \cdots m \]參數:
- x:除數。
- s:表示商,放在ax寄存器。
- m:表示余數,放在dx寄存器。
在段之間批量傳送數據
使用movsb和movsw指令可以在兩個段之間批量傳送數據。movsb每次傳送一個字節,movsw每次傳送一個字。
格式:
; 重復執行
rep movsb ; rep指令前綴表示cx寄存器的值不為0,則重復執行該指令。
rep movsw
; 只執行一次
movsb
movsw
參數:
- 源地址:
DS:SI
,即段地址由DS寄存器提供,SI寄存器提供偏移地址。 - 目的地址:
ES:DI
,即段地址由ES寄存器提供,DI寄存器提供偏移地址。 - 數量:由CX寄存器指定。
傳送方向:
- 正向:從低地址到高地址,每次使SI和DI加1或2。
- 反向:從高地址到低地址,每次使SI和DI減1或2。
一個特殊的寄存器flag
通過cld指令來將flag寄存器的DF位設置為0,std指令將flag寄存器的DF位設置位1。
ZF位表示運算結果是否為0。
1
:運算結果為00
:運算結果不為0DF位表示movsb/movsw指令是正向還是反向。
0
:正向1
:反向
關於使用寄存器提供偏移地址的問題
在8086處理器上只能使用BX、SI、DI、BP寄存器來提供偏移地址。
加一和減一、加法和減法
加一和減一對應的指令為inc
和dec
。
加法和減法對應的指令為add
和sub
。
負數相關指令
neg 寄存器
:將對應寄存器的值變為負數。cbw
:無操作數,將AL寄存器的數值擴展到AX寄存器。cwd
:無操作數,將AX寄存器的數值擴展到DX和AX寄存器中,DX存高16位,AX存低16位。
jns指令
標志寄存器的SF位:當計算結果的最高位是0時,該位為0,否則為1。
jns
指令,當標志寄存器的SF位為0時,則進行跳轉。
標記寄存器的其他標志位
- 奇偶標志位PF:當運算結果的低8位中,有偶數個1則PF為1,否則為0。
- 進位標志位CF:當進行算術運算時,如果有向最高位進位或借位,則CF為1,否則為0。
- 溢出標志位OF:如果發生溢出則為1,否則為0。
指令對標志寄存器的影響:
條件跳轉指令
根據標志跳轉:
jz
:ZF為1則轉移,jnz
:ZF為0則轉移。jo
:OF為1則轉移,jno
:OF為0則轉移。jc
:CF為1則轉移,jnc
:CF為0則轉移。jp
:PF為1則轉移,jnp
:PF為0則轉移。
根據比較結果跳轉:
可以通過cmp
指令進行比較。
根據CX寄存器進行跳轉:
jcxz
:如果CX寄存器為0,則進行跳轉。
棧段
由SS寄存器來確定段地址,SP寄存器確定偏移地址。
壓棧:push
指令,會將SP的內容減去操作數的字長,再把操作數壓入棧中。
彈棧:pop
指令,會將SP的內容加上操作數的字長,再把棧頂的數據彈出。
注意:實模式下,棧的大小為64KB。
尋址方式
-
寄存器尋址
-
立即尋址
-
內存尋址
-
直接尋址
-
基址尋址:使用BX和BP寄存器進行尋址
注意:BP寄存器默認使用SS段寄存器,使得棧內數據可以像數據段一樣訪問。
-
變址尋址:使用SI和DI寄存器進行尋址
-
基址變址寄存器:同時使用BX/BP和SI/DI寄存器進行尋址
-
I/O端口讀寫方式
I/O端口的范圍:0 ~ 65535(0x0 ~ 0xffff)。
-
讀取I/O端口
in 存放位置, 端口號
存放位置:必須是AL或AX寄存器。
端口號:立即數或DX寄存器。
-
寫入I/O端口
out 端口號, 寫入數據
端口號:立即數或DX寄存器。
寫入數據:必須是AL或AX寄存器。
一些磁盤相關的知識:
-
LBA28邏輯扇區編制法:即通過28個bit來表示邏輯扇區號,而不關心扇區的具體位置。(從0開始算起)
-
端口號:
端口號 含義 1f2 表示要讀取的扇區數 1f3 表示邏輯扇區號的0~7位 1f4 表示邏輯扇區號的8~15位 1f5 表示邏輯扇區號的16~23位 1f6 低4位存放邏輯扇區號的24~27位,第4位表示硬盤號(0主盤,1從盤),第6位表示扇區編制方式(0:CHS、1:LBA) 1f7 命令端口:寫入0x20表示讀硬盤請求,狀態端口:第7位表示硬盤忙碌狀態(0不忙,1忙碌),第3位表示硬盤數據是否准備好(1:ok,0:no),第0位表示讀取數據是否出錯(1出錯,0沒出錯) 1f0 硬盤接口的數據端口(16位端口),當硬盤准備好后,可以從這個端口讀取數據
過程調用
call指令:
近調用的原理是先把IP寄存器的值壓入棧中,再修改IP寄存器,而遠調用則是分別把CS和IP寄存器的值壓入棧,再修改CS和IP寄存器的值。
-
直接近調用
call 立即數
-
間接近調用
call 寄存器/[內存地址]
-
直接遠調用
call 段地址:偏移地址
-
間接遠調用
call far [內存地址]
這種方式會從對應的內存地址中獲取兩個字的數據,前一個字作為段地址,后一個字作為偏移地址。
返回指令:
ret
的原理是彈出棧中的值到IP寄存器,retf
的原理是先彈出到IP寄存器,再彈到CS寄存器。
ret
:相對近返回retf
:相對遠返回
abc、shr、ror指令
abc
:同add
指令差不多,但是會加上CF位的值(CF是表示是否進位)。shr
:邏輯右移,類似C語言中的>>
。shl
:邏輯左移,類似C語言中的<<
。ror
:循環右移,將移出的bit放到左邊空缺的位置,同時送入CF標志位。rol
:循環左移,將移出的bit放到右邊空缺的位置,同時送入CF標志位。
無條件跳轉指令
-
相對短轉移:立即數表示偏移量
jmp short 立即數
-
相對近轉移:立即數表示偏移量
jmp near 立即數 ; 或 jmp 立即數
-
間接絕對近轉移:寄存器或內存地址表示偏移地址
jmp near 寄存器/[內存地址] ; 或 jmp 寄存器/[內存地址]
-
直接絕對遠轉移
jmp 段地址:偏移地址
-
間接絕對遠轉移
jmp far 寄存器/[內存地址]
偽指令resb
-
resb
從當前位置開始,保留指定數量的字節,但不初始化它們的值。(區分db、dw、dd、dq)
-
resw
:與resb相同,但是單位為字。 -
resd
:與resb相同,但是單位為雙字。
光標操作
屏幕上光標的位置保存在顯卡內部的兩個光標寄存器(8位)中,合起來是一個16位數值,表示在第幾個位置(\(0 \sim {(80 * 25) - 1}\))。
注意:文字模式下,屏幕顯示80*25個字符。
讀取光標:
步驟:
- 向端口號0x3d4的索引寄存器指定索引,0xe是光標位置的高8位,0xf是光標位置的低8位。
- 通過端口號0x3d5的數據寄存器讀取數據。
設置光標:
步驟:
- 向端口號0x3d4的索引寄存器寫入光標位置的索引。
- 向端口號0x3d5的數據寄存器寫入光標位置。
乘法
mul
指令
mul 寄存器/[內存地址]
- 8位乘法:將mul指令里指定的值乘以AL寄存器中的值,並把結果保存到AX寄存器中。
- 16位乘法:將mul指令里指定的值乘以AX寄存器中的值,並把結果的高16位保存到DX,低16位保存到AX中。
cpuid指令
用於返回處理器的標識和特性信息。
在eax寄存器中指定要返回CPU信息。返回結果放在eax、ebx、ecx或edx中。
eflags寄存器
comvcc指令
可以看成mov
指令加上條件判斷功能,與cmp
或test
指令配合使用。
偽代碼:
if (condition) {
mov dest, source
}
sgdt指令
格式:sgdt 寄存器/[內存單元]
把gdtr寄存器的內容保存到指定位置。
movzx/movsx指令
-
movzx:帶零擴展傳送指令
格式:
movzx r16/r32, r8/r16/m8/m16
把指定8位/16位寄存器/內存單元的數據放到16/32位寄存器中,並且將高位設置為0。
-
movsx:帶符號擴展傳送指令
格式:
movsx r16/r32, r8/r16/m8/m16
把指定8位/16位寄存器/內存單元的數據放到16/32位寄存器中,並且將高位設置為跟符號位一致。
cmps指令
cmp
指令的升級版,通過cx(16位)或ecx(32位)寄存器來指定比較次數,ds:si/esi
寄存器指定源地址,es:di/edi
寄存器指定目的地址,並根據eflags寄存器中的df位來決定地址變化的方向,\(df=0\)則正向比較,地址遞增,\(df=1\)則反向比較,地址遞減,如果沒有加上rep
指令前綴,則只比較一次。
cmpsb ;字節比較
cmpsw ;字比較
cmpsd ;雙字比較
有關的rep
指令前綴:
rep
:一直重復到cx寄存器的值為0。repz/repe
:一直重復到cx寄存器的值為0或比較的內容不相等。repnz/repne
:一直重復到cx寄存器的值為0或比較的內容相等。
調用門
調用門(Call Gate)用於不同特權級的程序之間進行控制轉移。本質上只是一個描述符(不同於代碼段和數據段)。
調用門的特權級檢查規則:
pushf/popf指令
把16位的flags寄存器/32位的eflags寄存器壓或彈棧到flag寄存器中。
GDT、LDT和TSS的關系
任務切換與特權級切換的區別
任務門
注意:任務是不可重入的。
任務切換:
-
中斷引起:
在保護模式下,產生中斷后CPU會根據中斷號查詢中斷描述符表,如果對應的中斷描述符是一個任務門,那么就會進行任務切換。
-
使用遠過程調用指令引起:
在使用遠過程調用指令時,CPU會去GDT中查找對應的段描述符,如果該描述符為任務門描述符,則會發起任務切換。
頁目錄項和頁表項的結構
參數解釋:
參數 | 含義 |
---|---|
P | 該頁表/頁是否存在於內存中,1存在,0不存在 |
RW | 讀寫位,0該頁只讀,1可讀可寫 |
US | 用戶/管理位,1允許所有特權級訪問,0只允許特權級為0、1、2的程序訪問 |
PWT | 頁級通寫位,與高速緩存相關 |
PCD | 頁級高速緩存禁止位 |
A | 訪問位,表示該頁是否被訪問過 |
D | 臟位,表示該頁是否被寫過 |
PAT | 頁屬性表支持位,與高速緩存相關 |
G | 全局位,表示該頁是否為全局性的,如果是,則一直保留在高速緩存中 |
AVL | 被處理器忽略,軟件可使用 |
CR3寄存器的內容
bts指令
將指定位置的bit設置為1,並將其舊值設置到eflags寄存器的CF位中。
格式:
bts r/m16, r16
bts r/m32, r32
保護模式下的中斷和異常向量分配
在實模式下,是由中斷向量表定義了中斷的入口地址(位於內存最低端的1KB),而保護模式下,是由中斷描述符表(IDT)定義了中斷的入口。(由idtr寄存器指定了中斷描述符表所在位置)
中斷描述符表里面保存了中斷門、陷阱門和任務門。
中斷門、陷阱門:
中斷描述符寄存器:
保護模式下的中斷處理過程:
保護模式下通過中斷實現任務切換:
bound指令
用於檢查數組是否超出索引(這個數組是指源操作數所指向的內存單元里面存放了上限和下限,大小為雙字)
格式:
bound r16, m16
bound r32, m32
ud2指令
該指令無操作數,執行該指令會引發一個無效操作碼異常。
格式:
ud2