[013][x86匯編語言]從硬盤主引導扇區(qū)啟動,顯示hello world的“迷你操作系統(tǒng)”

學習筆記

《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)”

http://www.itdecent.cn/p/2c8ace0ee240

  • 軟盤圖片
軟盤.jpg

運行測試

從硬盤主引導扇區(qū)啟動,顯示hello world的“迷你操作系統(tǒng)”.png

代碼使用

  • 1、使用配書工具 nasmide.exe 編譯 加載程序 hello_mbr.asm以及,生成二進制文件hello_mbr.bin

  • 2、使用配書工具 nasmide.exe 編譯用戶程序 hello.asm,生成二進制文件hello.bin

  • 3、使用配書工具 fixvhdwr.exe 將加載程序的二進制文件hello_mbr.bin寫入虛擬硬盤的LBA邏輯扇區(qū)0(注意邏輯扇區(qū)號是

  • 4、使用配書工具 fixvhdwr.exe 將用戶程序的二進制文件hello.bin寫入虛擬硬盤的LBA邏輯扇區(qū)100(注意邏輯扇區(qū)號是一百

  • 5、打開 Oracle VM VirtualBox,之前應該配置好啟動硬盤,點擊綠色按鈕啟動

  • 補充、上述工具使用參考

http://www.itdecent.cn/p/9cb95d936451

完整源碼

加載程序 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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容