學習筆記
《x86匯編語言:從實模式到保護模式》
http://www.itdecent.cn/p/d481cb547e9f
代碼功能
- 從硬盤主引導扇區(qū)啟動,顯示
hello world的 “迷你操作系統(tǒng)”
本文
- 硬盤圖片
硬盤
http://www.sohu.com/a/63146493_115161
代碼聯(lián)動
- 從軟盤主引導扇區(qū)啟動,顯示
hello world的 “迷你操作系統(tǒng)”
- 軟盤圖片
軟盤.jpg
運行測試

從硬盤主引導扇區(qū)啟動,顯示hello world的“迷你操作系統(tǒng)”.png
代碼使用
1、使用配書工具 nasmide.exe 編譯
加載程序 hello_mbr.asm以及,生成二進制文件hello_mbr.bin2、使用配書工具 nasmide.exe 編譯
用戶程序 hello.asm,生成二進制文件hello.bin3、使用配書工具 fixvhdwr.exe 將加載程序的二進制文件
hello_mbr.bin寫入虛擬硬盤的LBA邏輯扇區(qū)0(注意邏輯扇區(qū)號是零)4、使用配書工具 fixvhdwr.exe 將用戶程序的二進制文件
hello.bin寫入虛擬硬盤的LBA邏輯扇區(qū)100(注意邏輯扇區(qū)號是一百)5、打開 Oracle VM VirtualBox,之前應該配置好啟動硬盤,點擊綠色按鈕啟動
補充、上述工具使用參考
完整源碼
加載程序 hello_mbr.asm
;------------------------------------------------------------------------
; 加載程序開始
;------------------------------------------------------------------------
; 文件名:hello_mbr.asm
; 文件說明:硬盤主引導扇區(qū)代碼(加載程序)
; 創(chuàng)建日期:3:00 2018/5/25
;------------------------------------------------------------------------
;------------------------------------------------------------------------
; 全局變量
;------------------------------------------------------------------------
;------------------------------------------------------------------------
app_lba_start equ 100 ;(源地址):用戶程序位于硬盤LBA邏輯扇區(qū)號100
SECTION mbr align=16 vstart=0x7c00
;------------------------------------------------------------------------
; 功能:準備工作
;------------------------------------------------------------------------
;------------------------------------------------------------------------
mov ax,0
mov ss,ax
mov sp,ax
;取得表示目的地址的32位物理地址
mov ax,[cs:phy_base]
mov dx,[cs:phy_base+0x02]
;將物理地址轉換為 段地址:偏移地址 中的段地址
mov bx,16
div bx
mov ds,ax ; ds寄存器 = 0x1000
;------------------------------------------------------------------------
; 循環(huán)讀取用戶程序全部扇區(qū)
;------------------------------------------------------------------------
;------------------------------------------------------------------------
;------------------------------------------------------------------------
;取得表示源地址的28位LBA邏輯扇區(qū)號
;------------------------------------------------------------------------
xor di,di ;高12位全是零
mov si,app_lba_start ;低16位使用全局變量app_lba_start的值
;------------------------------------------------------------------------
;設置目的地址
;------------------------------------------------------------------------
xor bx,bx ;每個新的扇區(qū)偏移地址從0x0000算起
call read_hard_disk_0 ;完成1個扇區(qū)的加載,加載好用戶程序開始的512字節(jié)內容
;------------------------------------------------------------------------
;從內存讀出用戶程序長度
;用戶程序長度位于用戶程序的頭部段
;用戶程序頭部段位于用戶程序的開始512字節(jié),已經(jīng)被加載到內存
;------------------------------------------------------------------------
;用戶程序長度是 dd型 32位數(shù)據(jù)
mov dx,[0x02] ;用戶程序長度 高16位
mov ax,[0x00] ;用戶程序長度 低16位
mov bx,512
div bx ;除法 余數(shù)存在DX,商存在AX
cmp bx,0 ;余數(shù)不為0,代表沒有除盡,程序所用總扇區(qū)數(shù)=AX+1
jnz @1
dec ax ;余數(shù)為0,說明扇區(qū)數(shù)就是AX,已經(jīng)加載完一個扇區(qū),所以減去1
@1:
cmp ax,0 ;程序總長度小于512字節(jié)或者為512整數(shù)倍時,AX=0
jz direct
;讀取剩余的扇區(qū)
push ds ;壓棧保護
mov cx,ax ;循環(huán)次數(shù)就是剩余扇區(qū)數(shù)
@2:
mov ax,ds
add ax,0x20 ;新讀取一個扇區(qū)寫入內存,段地址+0x20,偏移地址重新從0x0000算起
mov ds,ax
xor bx,bx ;新的一段開始,偏移地址都是從0x0000開始
xor di,di ;已知用戶程序非常短,表示邏輯扇區(qū)號用不到高12位
inc si ;源地址LBA邏輯扇區(qū)號+1
call read_hard_disk_0
loop @2
pop ds ;彈處棧,恢復寄存器
;------------------------------------------------------------------------
; 回寫
;------------------------------------------------------------------------
; 此時全部的用戶程序已經(jīng)加載到內存
;------------------------------------------------------------------------
direct:
;計算用戶程序入口點 代碼段code-1 段基址
;===================== 用戶程序 ==============================
;code_entry dw start ;偏移地址 [0x04]
; dd section.code_1.start ;段地址 [0x06]
;===================== 用戶程序 ==============================
mov dx,[0x08]
mov ax,[0x06] ; 用戶程序 "code_1段"(代碼段) 相對于用戶程序開頭的 偏移量
call calc_segment_base ; 結合用戶程序目的地址,計算 "code_1段"(代碼段) 的 段地址
mov [0x06],ax ; 將 "code_1段"(代碼段) 的段地址 回寫
;回寫段重定位表
;===================== 用戶程序 ==============================
; code_1_segment dd section.code_1.start ;[0x0c]
; data_1_segment dd section.data_1.start ;[0x10]
;===================== 用戶程序 ==============================
mov cx,[0x0a] ;需要重新定位的表項數(shù)
mov bx,0x0c ;段重定位表起始偏移量
realloc:
mov dx,[bx+0x02] ;高16位
mov ax,[bx] ;低16位
call calc_segment_base
mov [bx],ax ;回寫,填到低16位位置處即可
add bx,4 ;單個表項是4字節(jié)
loop realloc
;------------------------------------------------------------------------
; 跳轉到用戶程序執(zhí)行
;------------------------------------------------------------------------
; 已經(jīng)完成對入口地址的回寫
;------------------------------------------------------------------------
;跳轉到 用戶程序 code_1段的 標號 start 處
jmp far [0x04]
;------------------------------------------------------------------------
; 子程序:read_hard_disk_0
;------------------------------------------------------------------------
; 功能: 從硬盤指定位置,以扇區(qū)為單位,讀取數(shù)據(jù)放到內存指定位置
; 參數(shù):
; 源地址 SI DI
; 目的地址 DS:[BX]
; 過程:
; (1)設置要讀取的扇區(qū)數(shù);
; (2)解讀LBA模式扇區(qū)號;
; (3)請求硬盤讀操作;
; (4)查詢硬盤狀態(tài);
; (5)完成連續(xù)讀取數(shù)據(jù)。
;------------------------------------------------------------------------
read_hard_disk_0:
; 寄存器壓棧保護
push ax
push bx
push cx
push dx
;(1)設置要讀取的扇區(qū)數(shù);
mov dx,0x1f2 ;Ox1f2
mov al,1 ;扇區(qū)數(shù)=1
out dx,al
;(2)解讀28位 LBA模式扇區(qū)號;
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 ;0xe0 = 11100000B 第7位置1表示:LBA模式 第5位置1表示:從主硬盤讀取數(shù)據(jù)
or al,ah
out dx,al
;(3)請求硬盤讀操作;
inc dx ;0x1f7[命令端口]
mov al,0x20 ;讀命令
out dx,al
;(4)查詢硬盤狀態(tài);
.waits:
in al,dx ;0x1f7[狀態(tài)端口]
and al,0x88 ;0x88 = 10001000B 保留第7位 BSY 以及 第3位DRQ
cmp al,0x08 ;0x08 說明硬盤已準備好與主機進行交換
jnz .waits ;條件轉移指令
;(5)完成連續(xù)讀取數(shù)據(jù)。
mov dx,0x1f0 ;0x1f0[數(shù)據(jù)端口]
mov cx,256 ;1個扇區(qū)是512字節(jié)=256字
.readw:
in ax,dx
mov [bx],ax ;加載到內存,新的段從偏移地址0x0000算起
add bx,2 ;以字為單位進行加載
loop .readw
;寄存器彈?;謴? pop dx
pop cx
pop bx
pop ax
;子程序返回
ret
;------------------------------------------------------------------------
; 子程序:calc_segment_base
;------------------------------------------------------------------------
; 功能:將用戶程序用SECTION標記的段的匯編地址轉換為可以尋址內存的段基址
; 參數(shù):
; DX 匯編地址的高16位
; AX 匯編地址的低16位
; 返回: AX作為16的段基址
;------------------------------------------------------------------------
calc_segment_base:
push dx
add ax,[cs:phy_base]
adc dx,[cs:phy_base+0x02]
shr ax,4
ror dx,4
and dx,0xf000
or ax,dx
pop dx
ret
;------------------------------------------------------------------------
; 其余數(shù)據(jù)
;------------------------------------------------------------------------
;------------------------------------------------------------------------
phy_base dd 0x10000 ;(目的地址)用戶程序將被加載到的內存物理地址
times 510-($-$$) db 0
db 0x55,0xaa
;------------------------------------------------------------------------
; 加載程序結束
;------------------------------------------------------------------------
用戶程序 hello.asm
;------------------------------------------------------------------------
; 用戶程序開始
;------------------------------------------------------------------------
; 文件名:hello.asm
; 文件說明:功能程序代碼(用戶程序)
; 創(chuàng)建日期:3:38 2018/5/25
;------------------------------------------------------------------------
;------------------------------------------------------------------------
; 頭部段
;------------------------------------------------------------------------
SECTION header vstart=0
;指令匯編地址是指令相對于用戶程序開頭的偏移量
;(1)用戶程序長度
program_length dd program_end ;指令匯編地址[0x00]
;(2)用戶程序入口地址
code_entry dw start ;偏移地址 [0x04]
dd section.code_1.start ;段地址 [0x06]
;(3)需要重新定位的表項數(shù)
realloc_tbl_len dw (header_end - code_1_segment)/4 ;[0x0a]
;(4)段重定位表項
code_1_segment dd section.code_1.start ;[0x0c]
data_1_segment dd section.data_1.start ;[0x10]
stack_segment dd section.stack.start ;[0x14]
header_end:
;------------------------------------------------------------------------
; 代碼段
;------------------------------------------------------------------------
SECTION code_1 align=16 vstart=0
;用戶程序入口
start:
;設置寄存器
mov ax,[stack_segment]
mov ss,ax
mov sp,stack_end
mov ax,[data_1_segment]
mov ds,ax
mov si,hello_world
call put_string
jmp $ ;無限循環(huán)
;------------------------------------------------------------------------
; 子程序:put_string
;------------------------------------------------------------------------
; 功能:在屏幕上顯示字符串,字符串以0結尾
; 參數(shù):DS:SI 指向字符串首地址
;------------------------------------------------------------------------
put_string:
push ax
push es
push si
mov ax,0xb800 ;顯存
mov es,ax
mov bx,0
.put_char:
mov al,[si]
or al,al ;al=0?
jz .exit
mov byte [es:bx],al
inc si
add bx,2
jmp .put_char
.exit:
pop si
pop es
pop ax
ret
;------------------------------------------------------------------------
; 數(shù)據(jù)段
;------------------------------------------------------------------------
SECTION data_1 align=16 vstart=0
hello_world db 'H '
db 'E '
db 'L '
db 'L '
db 'O '
db ' '
db 'W '
db 'O '
db 'R '
db 'L '
db 'D '
db 0
;------------------------------------------------------------------------
; 棧段
;------------------------------------------------------------------------
SECTION stack align=16 vstart=0
resb 256
stack_end:
;------------------------------------------------------------------------
; 用戶程序結束
;------------------------------------------------------------------------
SECTION trail align=16
program_end:
;------------------------------------------------------------------------
源碼參考
[012][x86匯編語言]加載程序與用戶程序
http://www.itdecent.cn/p/e101fce6cdb5
[008][x86匯編語言]硬盤與顯卡的訪問與控制 加載程序c08_mbr.asm 用戶程序c08.asm
http://www.itdecent.cn/p/72c151606908
[009][x86匯編語言]學習加載程序的編寫(c08_mbr.asm)
http://www.itdecent.cn/p/add4f948a321
[010][x86匯編語言]學習用戶程序的編寫(c08.asm)
http://www.itdecent.cn/p/4177e9ff49d6
