1.AT&T匯編語法
前面寫過,boot.s程序使用的是as86匯編語法,而進入head.s需要啟動linux的時候,就需要使用AT&T匯編語法了,這與intel的匯編語法不一樣,微軟使用的是intel的匯編語法,暫時就不去了解了。我們目前使用的GNU工具,使用的是AT&T匯編語法,所以AT&T匯編語法是可以簡單的學習一下的。
2.開始匯編代碼編寫測試
2.1 第一個測試程序
這部分主要是參考網絡上的一些文章,從這些文章中快速了解at&t匯編語法在機器上實現的效果。
首先,編寫demo.s匯編程序,代碼如下所示:
.section .data
.section .text
.globl _start
_start:
movl $1, %eax
movl $4, %ebx
int $0x80
編譯程序,如下所示:
$ as demo.s -o demo.o
$ ld demo.o -o demo
$ ./demo
$ echo $?
4
我們需要查詢一下int 0x80的調用規則,才能更近一步了解程序的含義。
我截取一部分圖,如下圖所示:
所以,只要eax=1,我們就能調用到sys_exit函數。而ebx則是sys_exit函數的返回值。
2.2 匯編語法
2.2.1 基本語法
內存尋址在指令中可以表示成如下的通用格式:
ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER)
它所表示的地址可以這樣計算出來:
目標地址 = ADDRESS_OR_OFFSET + BASE_OR_OFFSET + MULTIPLIER * INDEX
其中ADDRESS_OR_OFFSET和MULTIPLIER必須是常數,BASE_OR_OFFSET和INDEX必須是寄存器。在有些尋址方式中會省略這4項中的某些項,相當于這些項是0。
1.直接尋址
根據以上公式,只使用ADDRESS_OR_OFFSET尋址,例如movl ADDRESS, %eax把ADDRESS地址處的32位數傳送到eax寄存器。
2.變址尋址
movl data_items(,%edi,4), %eax就屬于這種方式,用于訪問數組很方便。
3.間接尋址
只使用BASE_OR_OFFSET尋址,例如movl (%eax), %ebx,把eax寄存器的值看作地址,把這個地址處的32位數傳送到ebx寄存器。
4.基址尋址
只使用ADDRESS_OR_OFFSET和BASE_OR_OFFSET尋址,例如movl 4(%eax), %ebx,用于訪問結構體成員比較方便,例如一個結構體的基地址保存在eax寄存器中,其中一個成員在結構體內偏移量是4字節,要把這個成員讀上來就可以用這條指令。
5.立即數尋址
就是指令中有一個操作數是立即數,例:movl $3, %eax。
6.寄存器尋址
就是指令中有一個操作數是寄存器。在匯編程序中寄存器用助記符來表示,在機器指令中則要用幾個Bit表示寄存器的編號,這幾個Bit與可以看做寄存器的地址,但是和內存地址不在一個地址空間。
我從網上摘抄一個整體的描述表格:
2.2.2 asm volatile GCC的內嵌匯編語法
我們分為兩種,一種是基本的內聯匯編語法,一種是帶有c/c++表達式的內聯匯編語法。
1.基本的內聯匯編語法
格式為:
__asm__ __volatile__("Instruction List");
2.帶有c/c++格式的內聯匯編語法
格式為:
__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);
3 中斷功能
3.16 INT 10H
1.AH=00H 設定顯示模式的服務程序,AL 寄存器表示欲設定的模式
AL | 文字/圖形 | 分辨率 | 顏色 |
---|---|---|---|
00 | 文字 | 40*25 | 2 |
01 | 文字 | 40*25 | 16 |
02 | 文字 | 80*25 | 2 |
03 | 文字 | 80*25 | 16 |
04 | 圖形 | 320*200 | 2 |
05 | 圖形 | 320*200 | 4 |
06 | 圖形 | 640*200 | 2 |
2.AH=01H
光標起始處與終止處分別由 CL 與 CH 的 0 到 4 位表示。
CH 的第 7 位必須是 0,第 5、6 位表示光標屬性
位 6 | 位 5 | 屬性 |
---|---|---|
0 | 0 | 正常 |
0 | 1 | 隱形 |
1 | 0 | N/A |
1 | 1 | 閃爍緩慢 |
3.AH=02H
此功能是設定光標位置,位置用 DH、DL 表示,DH 表示列號,DL 表示行號。由左至右稱之為『列』,屏幕最上面一列為第零列,緊靠第零列的下一列稱為第一列……;由上而下稱之為『行』,屏幕最左邊一行稱之為第零行,緊靠第零行右邊的一行為第一行。故最左邊,最上面的位置為 DH=0 且 DL=0;最左邊第二列,DH=1,DL=0。如果是文字模式時,BH 為欲改變光標位置的顯示頁,如果是圖形模式,BH 要設為 0。
以行列來說明 DH、DL 之意義,底下以坐標方式解釋。在文字模式下,字符的位置類似數學直角座標系的座標,但是 Y 軸方向相反,Y 軸是以屏幕最上面為零,越下面越大,直到 24 為止,存于 DH 內。X 軸和直角座標系相同,越右邊越大,存于 DL 內,其最大值視顯示模式而變。
4.AH=03H
這個中斷服務程序返回時,會在 DX 里面有光標的行列位置,CX 內有光標的大小,DX、CX 之數值所代表的意義和 AH=01H/INT 10H、AH=02H/INT 10H 相同。
5.AH=13H
參數 | 說明 |
---|---|
AL | 顯示模式 |
BH | 視頻頁 |
BL | 屬性值(如果AL=0x00或0x01) |
CX | 字符串的長度 |
DH,DL | 屏幕上顯示起始位置的行、列值 |
ES:BP | 字符串的段:偏移地址 |
顯示模式分如下幾種:
參數 | 說明 |
---|---|
AL=0x00 | 字符串只包含字符碼,顯示之后不更新光標位置,屬性值在BL中 |
AL=0x01 | 字符串只包含字符碼,顯示之后更新光標位置,屬性值在BL中 |
AL=0x02 | 字符串包含字符碼及屬性值,顯示之后不更新光標位置 |
AL=0x03 | 字符串包含字符碼及屬性值,顯示之后更新光標位置 |
3.19 INT 13H
1.AH=00H 軟、硬盤控制器復位
功能說明:
此功能復位磁盤(軟盤和硬盤)控制器板和磁盤驅動器,它在磁盤控制器芯片上完成復位操場作并在磁盤進行所需的操作之前做一系列用于磁盤校準磁盤操作。
當磁盤I/O功能調用出現錯誤時,需要調用此功能,此刻復位功能將使BIOS象該磁盤重新插入一樣檢查驅動器中磁盤狀態,并將磁頭校準使之在應該在的位置上。
此功能調用不影響軟盤或硬盤上的數據。
參數1 | 入口參數2 | 說明 |
---|---|---|
AH=00H | DL需要復位的驅動器號 | 若產生錯誤,進位標志CF=1,錯誤碼在AH寄存器。詳情請見磁盤錯誤狀態返回碼一文。 |
2.AH=02H 讀扇區
調用此功能將從磁盤上把一個或更多的扇區內容讀進存貯器。因為這是一個低級功能,在一個操作中讀取的全部扇區必須在同一條磁道上(磁頭號和磁道號相同)。BIOS不能自動地從一條磁道末尾切換到另一條磁道開始,因此用戶必須 把跨多條磁道的讀操作分為若干條單磁道讀操作。
參數 | 說明 |
---|---|
AL | 置要讀的扇區數目,不允許使用讀磁道末端以外的數值,也不允許使該寄存器為0 |
DL | 需要進行讀操作的驅動器號 |
DH | 所讀磁盤的磁頭號 |
CH | 磁道號的低8位數 |
CL | 低5位放入所讀起始扇區號,位7-6表示磁道號的高2位 |
ES:BX | 讀出數據的緩沖區地址 |
返回參數:
如果CF=1,AX中存放出錯狀態。讀出后的數據在ES:BX區域依次排列。
詳情請參見磁盤錯誤狀態返回碼一文。
3.AH=03H 寫扇區
功能說明:
調用此功能將從磁盤上把一個或更多的扇區內容寫入驅動器。因為這是一個低級功能,在一個寫入操作中的全部扇區必須在同一條磁道上(磁頭號和磁道號相同)。BIOS不能自動地從一條磁道末尾切換到另一條磁道開始,因此用戶必須把跨多條磁道的寫操作分為若干條單磁道寫操作。
參數 | 說明 |
---|---|
AL | 置要寫的扇區數目,不允許使用讀磁道末端以外的數值,也不允許使該寄存器為0 |
DL | 需要進行寫操作的驅動器號 |
DH | 所寫磁盤的磁頭號 |
CH | 磁道號的低8位數 |
CL | 低5位放入所讀起始扇區號,位7-6表示磁道號的高2位 |
ES:BX | 放置寫入數據的存儲區地址 |
返回參數:
如果CF=1,AX中存放出錯狀態。
詳情請參見磁盤錯誤狀態返回碼一文。
4.AH=08H 得到磁盤驅動器參數
參數 | 說明 |
---|---|
AX | 0 |
BH | 0 |
BL | 1=360K 5.25寸,2=1.2M 5.25寸 ,3=720K 3.5寸 ,4=1.44M 3.5寸 |
CH | 存放10位磁道數的低8位(高2位在CL的D7、D6中)。1表示有1個磁道,2表示有2個磁道,依次類推。 |
CL | 0~5位存放每磁道的扇區數目。6和7位表示10位磁道數的高2位。 |
DH | 最大磁頭號(或說磁面數目)。0表示有1個磁面,1表示有2個磁面 |
DL | 本機軟盤驅動器的數目 |
ES:SI | 指向軟盤參數表 |
錯誤信息:
若產生錯誤,進位標志CF=1,AH存放錯誤信息碼。
把以上得到的磁盤參數分別放到parameters處相應的位置,磁盤參數占11字節的空間。 由于si是1個字節,所以是ax,bx,cx,dx,es,si共2*6-1=11字節空間。
3.21 INT 15H
1.AH=88H 獲取擴展內存大小
返回:
參數 | 說明 |
---|---|
AX | 內存大小,獲取1MB存儲區以上的內存大小 |
CF | 標志位,0表示沒有錯誤 |
磁盤錯誤狀態:
AH | 說明 |
---|---|
00H | 未出錯 |
01H | 非法功能調用命令區。 |
02H | 地址標記損壞,扇區標識(ID)無效或未找到。 |
03H | 企圖對有寫保護的軟盤執行寫操作。 |
04H | 所尋找的扇區沒找到。 |
05H | 復位操作失敗。 |
06H | 無介質。 |
07H | 初始化錯誤,數據未存在DMA的64K緩沖區內。 |
08H | DMA故障 |
09H | DMA邊界錯誤,數據未存在DMA的64K緩沖區內。 |
0AH | 檢測出錯誤碼率的扇區標志。 |
0BH | 所尋找的磁道沒找到。 |
0CH | 介質類型沒發現。 |
0DH | 扇區號有問題。 |
0EH | 發現控制數據地址標記。 |
0FH | 超出DMA邊界 |
10H | 讀磁盤時奇偶校驗錯,且糾錯碼(EDC)不能糾正。 |
11H | 讀磁盤時奇偶校驗錯,但糾錯碼(EDC)已糾正錯誤。 |
20H | 控制器錯。 |
40H | 查找操作無效。 |
80H | 超時錯誤,驅動器不響應。 |
AAH | 驅動器未準備好。 |
BBH | 不明錯誤。 |
CCH | 被選驅動器出現寫故障。 |
E0H | 錯誤寄存器是零 |
FFH | 非法操作。 |
4 保護模式以及編程
4.1 內存管理寄存器
從網上抄錄一幅圖:
從這幅圖中,我們可以看出,內存管理器分為全局描述符表寄存器GDTR、中斷描述符表寄存器IDTR、TR任務寄存器、局部描述符表寄存器LDTR。
- GDTR
GDTR寄存器用于存放全局符號描述表(GDT)的線性基地址(32位)和表長度值(16位)。基地址指定GDT表中的字節0在線性地址空間中的地址,表長度指明GDT表的字節長度值,指令LGDT和SGDT分別用于加載和保存GDTR寄存器的內容。在機器剛上電或處理器復位后,基地址被默認設置為0,而表長度被設置成0xFFFF。在保護模式初始化過程中必須給GDTR加載一個新值。
上面所說的就是GDTR寄存器。我們需要注意,是寄存器。而下面要說的是全局描述符表,是64位的。我們只需要放到內存中即可。然后將內存地址給GDTR寄存器。就可以訪問表中的內容了。
相應的描述如下:
G:
G=0時,段限長的20位為實際段限長,最大限長為220=1MB。
G=1時,則實際段限長為20位段限長乘以212=4KB,最大限長達到4GB。
D/B:
D位:
當描述符指向的是可執行代碼段時,則會形成D位,D=1使用32位地址和32/8位操作數,D=0使用16位地址和16/8位操作數。
B位:
當指向的是向下擴展的數據段,這一位就叫做B位,B=1時段的上界為4GB,B=0時段的上界為64KB。
當指向的是堆棧段,這一位也叫做B位,B=1使用32位操作數,堆棧指針用ESP,B=0時使用16位操作數,堆棧指針用SP。
P:
表示段是否存在于內存中,P為1表示存在。
該位在未開啟分頁模式的情況下有用。如果內存不足可以把內存中的某些段暫時移動到硬盤上去,騰出空間。這樣如果在訪問段時CPU發現P為0,就會觸發異常。操作系統捕獲到異常后會把段從硬盤挪會內存去,然后把P置為1。
如果開啟了分頁模式,那么分頁模式本身就支持內存置換。就不需要這個P字段了。
DPL:
該位為特權級,0為最高特權級,3為最低,表示訪問該段時CPU所需處于的最低特權級。
TYPE:
當TYPE<=7時,表示的是數據段。
值 | 說明 |
---|---|
0 | 只讀 |
1 | 只讀,已訪問 |
2 | 讀寫 |
3 | 讀寫,已訪問 |
4 | 只讀,向下擴展 |
5 | 只讀,向下擴展,已訪問 |
6 | 讀寫,向下擴展 |
7 | 讀寫,向下擴展,已訪問 |
當TYPE>=8時,表示的是代碼段。
值 | 說明 |
---|---|
8 | 只執行 |
9 | 只執行,已訪問 |
10 | 執行,可讀 |
11 | 執行,可讀,已訪問 |
12 | 只執行,一致 |
13 | 只執行,一致,已訪問 |
14 | 執行,可讀,一致 |
15 | 執行,可讀,一致,已訪問 |
- IDTR
與GDTR的作用類似,IDTR寄存器用于存放中斷描述符表的32位線性基地址和16位表長度值。指令LIDT與SIDT分別用于加載和保存中斷描述符表的內容。在機器剛剛上電或處理器復位后,基地址默認設置為0,長度值被設置為0xFFFF
- TR
TR寄存器用于存放當前任務TSS段的16位段選擇符,32位基地址、和16位段長度和描述符屬性值。它引用GDT表中的一個TSS類型放入描述符,指令LTR和STR分別用于加載和保存TR寄存器的段選擇符部分。
- LDTR
LDTR用于存放局部描述符表LDT的32位線性基地址、16位段限長和描述符屬性值。指令LLDT和SLDT用于加載和保存LDTR寄存器的段描述符部分,包含LDT表的段必須在GDT表中有一個段描述符項。