源程序來源
加載程序功能
- 加載程序 知道 用戶程序位于 虛擬硬盤的LBA邏輯扇區(qū)100 處;
- 加載程序 知道 虛擬機(jī) 內(nèi)存物理地址
0x10000處空閑; - 加載程序 要把 用戶程序 從虛擬硬盤里取出來,然后放到 虛擬機(jī)里空閑的內(nèi)存空間那里;
- 加載程序 知道 虛擬機(jī) 開機(jī)后會(huì)讀取 虛擬硬盤主引導(dǎo)扇區(qū)(LBA模式邏輯扇區(qū)號(hào)0) 的內(nèi)容,將其復(fù)制到內(nèi)存
0x0000:0x7c00處開始執(zhí)行;
(我們)要做的事情
- 利用工具
nasmide.exe編譯加載程序的源文件.asm,生成一個(gè).bin二進(jìn)制文件,將.bin文件利用工具fixvhdwr.exe寫入到虛擬硬盤的主引導(dǎo)扇區(qū)(LBA模式邏輯扇區(qū)號(hào)0)。
加載程序:增加注釋
;
;文件名 c08-1.asm
;文件說明:硬盤主引導(dǎo)扇區(qū)代碼(加載程序)
;創(chuàng)建日期:9:12 2018/5/23
app_lba_start equ 100 ;用戶程序源地址的起始邏輯扇區(qū)號(hào)
SECTION mbr align=16 vstart=0x7c00
;設(shè)置棧段和棧指針
mov ax,0
mov ss,ax
mov sp,ax
mov ax,[cs:phy_base]
mov dx,[cs:phy_base+0x02]
mov bx,16
div bx ;物理地址0x10000 轉(zhuǎn)換為 段地址0x1000
mov ds,ax
mov es,ax
;以下讀取程序的起始部分
xor di,di ;28位起始邏輯扇區(qū)號(hào)的高12位
mov si,app_lba_start ;28位起始邏輯扇區(qū)號(hào)的低16位
xor bx,bx ; ???
call read_hard_disk_0
;以下判斷整個(gè)用戶程序有多大
mov dx,[2] ; 32位用戶程序長度的高16位
mov ax,[0] ; 32位用戶程序長度的低16位
mov bx,512 ; 1個(gè)扇區(qū)512字節(jié)
div bx
cmp dx,0 ; dx里存著余數(shù),余數(shù)不為0代表沒有除盡
jnz @1
dec ax
@1:
cmp ax,0 ; 小于1個(gè)扇區(qū)或者長度為512的整數(shù)倍時(shí)ax = 0
jz direct
; 讀取剩余的扇區(qū)
push ds ; 用戶程序的開頭是基于LBA邏輯扇區(qū)號(hào)計(jì)算出來的段地址
mov cx,ax ; 循環(huán)次數(shù)(剩余的扇區(qū)數(shù))
@2:
mov ax,ds
add ax,0x20 ; 512D = 0x20
mov ds,ax ; 得到下一個(gè)以512字節(jié)為邊界的段地址
xor bx,bx ; 新的一段開始,偏移地址都是從0x0000開始
inc si ; 新的LBA邏輯扇區(qū)號(hào),最初的是app_lba_start
call read_hard_disk_0 ; 不重新設(shè)置di,是因?yàn)閐i不需要修改,di = 0
loop @2 ; 循環(huán)讀,直到讀完整個(gè)功能程序(即用戶程序)
pop ds ; 恢復(fù)數(shù)據(jù)段基址到用戶程序頭部段
;計(jì)算入口點(diǎn)代碼段基址
direct: ; ds 指向用戶程序頭部段
mov dx,[0x08]
mov ax,[0x06] ; 用戶程序 "code_1段"(代碼段) 相對(duì)于用戶程序開頭的 偏移量
call calc_segment_base ; 結(jié)合用戶程序目的地址,計(jì)算 "code_1段"(代碼段) 的 段地址
mov [0x06],ax ; 將 "code_1段"(代碼段) 的段地址 回寫
; 開始處理段重定位表
mov cx,[0x0a] ; 需要重定位的表項(xiàng)數(shù)
mov bx,0x0c ; 需要重定位表項(xiàng)相對(duì)用戶程序開頭的偏移量
realloc:
mov dx,[bx+0x02] ; 32位表項(xiàng)偏移量,高16位
mov ax,[bx] ; 32位表項(xiàng)偏移量,低16位
call calc_segment_base ; 結(jié)合用戶程序目的地址, 計(jì)算 表項(xiàng) 的段地址
mov [bx],ax
add bx,4 ; 下一個(gè)重定位項(xiàng) (每項(xiàng)占4個(gè)字節(jié))
loop realloc
jmp far [0x04] ; 跳轉(zhuǎn)到用戶程序: code_1段 標(biāo)號(hào)start處開始執(zhí)行用戶程序
;-----------------------------------------------------------------
read_hard_disk_0:
push ax
push bx
push cx
push dx
;設(shè)置要讀取的扇區(qū)數(shù)為1
mov dx,0x1f2 ; 0x1f2
mov al,1 ; 扇區(qū)數(shù)
out dx,al
;設(shè)置起始的LBA扇區(qū)號(hào)
inc dx ; 0x1f3
mov ax,si
out dx,al ; LBA地址7~0
inc dx ; 0x1f4
mov al,ah
out dx,al ; LBA地址15~8
inc dx ;0x1f5
mov ax,di
out dx,al ; LBA地址23~16
inc dx ; 0x1f6
mov al,0xe0 ; LAB 主硬盤
or al,ah
out dx,al
; 請(qǐng)求硬盤讀
inc dx ; 0x1f7 [命令端口]
mov al,0x20 ; 讀命令
out dx,al
;查看狀態(tài)
.waits:
in al,dx ; 0x1f7 [狀態(tài)端口]
and al,0x88 ; 1000 1000 保留 BSY ... DRQ...
cmp al,0x08 ; 0x08 硬盤已準(zhǔn)備好與主機(jī)交換
jnz .waits
; 連續(xù)讀取數(shù)據(jù)
mov cx,256 ; 總共要讀取的字?jǐn)?shù)256字=512字節(jié)
mov dx,0x1f0 ; 0x1f0 [數(shù)據(jù)端口]
.readw:
in ax,dx ; 在子程序調(diào)用前已經(jīng)清零xor bx,bx
mov [bx],ax ; 指定數(shù)據(jù)段 DS 指向用戶程序目標(biāo)地址的段地址
add bx,2
loop .readw
pop dx
pop cx
pop bx
pop ax
ret
;-----------------------------------------------------------------
calc_segment_base: ;計(jì)算16位段地址
;輸入 DX:AX = 32位物理地址
;返回 AX = 16位段基地址
push dx
add ax,[cs:phy_base]
adc dx,[cs:phy_base+0x02] ; 用戶程序開頭的物理地址是0x10000位于標(biāo)號(hào)phy_base處
shr ax,4
ror dx,4
and dx,0xf000
or ax,dx
pop dx
ret
;-----------------------------------------------------------------
phy_base dd 0x10000 ;用戶程序目的地址的物理起始地址
times 510-($-$$) db 0
db 0x55,0xaa
用戶程序 同 源程序來源
c08.asm
加載程序 需要結(jié)合 用戶程序 理解的部分 跨越文件 通過內(nèi)存讀取數(shù)據(jù)

1、用戶程序的目的地址 0x1000.png

2、跨越文件 通過內(nèi)存 讀取用戶程序的總長度.png

3.用dx ax 2個(gè)16位存一個(gè)32位的dd.png

4、將ax回寫到用戶程序.png

5、需要重定位的表項(xiàng)個(gè)數(shù).png

6、回填段的基址.png

7、跳轉(zhuǎn)到用戶程序標(biāo)號(hào)start處.png

8、jmp指令跳轉(zhuǎn)到 code-1段的標(biāo)號(hào)start處開始執(zhí)行用戶程序.png
代碼說明
-
phy_base dd 0x10000: 用戶程序目的地址的物理起始地址 尋址內(nèi)存
物理地址 0x10000
使用 【段地址:偏移地址 = 0x1000:0x0000】 來映射
---------------------------------------------------------------------------
完成計(jì)算 段地址 的代碼片段
mov ax,[cs:phy_base]
mov dx,[cs:phy_base+0x02]
mov bx,16
div bx ;物理地址0x10000 轉(zhuǎn)換為 段地址0x1000
mov ds,ax
mov es,ax
-
app_lba_start equ 100: 用戶程序源地址的起始LBA邏輯扇區(qū)號(hào) 尋址硬盤
28位 LBA 邏輯扇區(qū)號(hào) 100
0000 0000 0000 0000 0000 0000 0100
27~24 23~16 15~8 7~0
----- DI --------- -------- SI ----------
-------------------------------------------------------------
;以下讀取程序的起始部分
xor di,di ;28位起始邏輯扇區(qū)號(hào)的高12位
mov si,app_lba_start ;28位起始邏輯扇區(qū)號(hào)的低16位
- 設(shè)置起始的LBA扇區(qū)號(hào)
;設(shè)置起始的LBA扇區(qū)號(hào)
inc dx ; 0x1f3
mov ax,si
out dx,al ; LBA地址7~0
inc dx ; 0x1f4
mov al,ah
out dx,al ; LBA地址15~8
inc dx ;0x1f5
mov ax,di
out dx,al ; LBA地址23~16
inc dx ; 0x1f6
mov al,0xe0 ; LAB 主硬盤
or al,ah
out dx,al
端口 0x1f6 的含義.png
《x86匯編語言:從實(shí)模式到保護(hù)模式》 第126頁
- 查看硬盤狀態(tài)
;查看狀態(tài)
.waits:
in al,dx ; 0x1f7 [狀態(tài)端口]
and al,0x88 ; 1000 1000 保留 BSY ... DRQ...
cmp al,0x08 ; 0x08 硬盤已準(zhǔn)備好與主機(jī)交換
jnz .waits
端口 0x1f7 部分狀態(tài)位的含義.png
《x86匯編語言:從實(shí)模式到保護(hù)模式》 第127頁
- 子程序
call read_hard_disk_0
子程序
call read_hard_disk_0
參數(shù)
di:si 是用戶程序位于硬盤的源地址的[LBA邏輯扇區(qū)號(hào)]
di : 28位起始邏輯扇區(qū)號(hào)的高12位
si : 28位起始邏輯扇區(qū)號(hào)的低16位
ds:bx 是用戶程序?qū)⒈凰屯鶅?nèi)存的目的地址的[段地址:偏移地址]
隱藏參數(shù) ds
bx : 固定初始化為0x0000,因?yàn)槊恳欢蔚钠频刂范际菑?x0000開始
瘋狂Debug
-
兩個(gè)文件,如果按照相同的行數(shù)輸入相同的代碼,編譯出來的機(jī)器碼連同行數(shù)都會(huì)一模一樣的,但是這里雖然機(jī)器碼相同,行數(shù)卻不同,預(yù)感這里有一個(gè)BUG:果然把寄存器DX寫成了寄存器AX;
Debug.png phy_base dd 0x10000, 用的是dd 型數(shù)據(jù),雙字,4個(gè)字節(jié);


