美文网首页汇编
[013][x86汇编语言]从硬盘主引导扇区启动,显示hello

[013][x86汇编语言]从硬盘主引导扇区启动,显示hello

作者: AkuRinbu | 来源:发表于2018-05-25 05:22 被阅读0次

    学习笔记

    《x86汇编语言:从实模式到保护模式》
    https://www.jianshu.com/p/d481cb547e9f

    代码功能

    • 硬盘主引导扇区启动,显示hello world的 “迷你操作系统”

    本文

    • 硬盘图片
    硬盘
    http://www.sohu.com/a/63146493_115161

    代码联动

    • 软盘主引导扇区启动,显示hello world的 “迷你操作系统”

    https://www.jianshu.com/p/2c8ace0ee240

    • 软盘图片
    软盘.jpg

    运行测试

    从硬盘主引导扇区启动,显示hello world的“迷你操作系统”.png

    代码使用

    • 1、使用配书工具 nasmide.exe 编译 加载程序 hello_mbr.asm以及,生成二进制文件hello_mbr.bin

    • 2、使用配书工具 nasmide.exe 编译用户程序 hello.asm,生成二进制文件hello.bin

    • 3、使用配书工具 fixvhdwr.exe 将加载程序的二进制文件hello_mbr.bin写入虚拟硬盘的LBA逻辑扇区0(注意逻辑扇区号是

    • 4、使用配书工具 fixvhdwr.exe 将用户程序的二进制文件hello.bin写入虚拟硬盘的LBA逻辑扇区100(注意逻辑扇区号是一百

    • 5、打开 Oracle VM VirtualBox,之前应该配置好启动硬盘,点击绿色按钮启动

    • 补充、上述工具使用参考

    https://www.jianshu.com/p/9cb95d936451

    完整源码

    加载程序 hello_mbr.asm

    ;------------------------------------------------------------------------
    ;   加载程序开始
    ;------------------------------------------------------------------------
    ;   文件名:hello_mbr.asm
    ;   文件说明:硬盘主引导扇区代码(加载程序)
    ;   创建日期:3:00 2018/5/25
    ;------------------------------------------------------------------------
    
    
    
    ;------------------------------------------------------------------------
    ;   全局变量
    ;------------------------------------------------------------------------
    ;------------------------------------------------------------------------
    app_lba_start equ 100   ;(源地址):用户程序位于硬盘LBA逻辑扇区号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
    ;------------------------------------------------------------------------
    ;   循环读取用户程序全部扇区
    ;------------------------------------------------------------------------
    ;------------------------------------------------------------------------
            
            ;------------------------------------------------------------------------
            ;取得表示源地址的28位LBA逻辑扇区号
            ;------------------------------------------------------------------------
            xor di,di               ;高12位全是零
            mov si,app_lba_start    ;低16位使用全局变量app_lba_start的值
            
            ;------------------------------------------------------------------------
            ;设置目的地址
            ;------------------------------------------------------------------------
            xor bx,bx               ;每个新的扇区偏移地址从0x0000算起
            call read_hard_disk_0   ;完成1个扇区的加载,加载好用户程序开始的512字节内容
            
            ;------------------------------------------------------------------------
            ;从内存读出用户程序长度
            ;用户程序长度位于用户程序的头部段
            ;用户程序头部段位于用户程序的开始512字节,已经被加载到内存
            ;------------------------------------------------------------------------
            
            ;用户程序长度是 dd型 32位数据
            mov dx,[0x02]       ;用户程序长度 高16位
            mov ax,[0x00]       ;用户程序长度 低16位
            mov bx,512
            div bx              ;除法 余数存在DX,商存在AX
            cmp bx,0            ;余数不为0,代表没有除尽,程序所用总扇区数=AX+1
            jnz @1
            dec ax              ;余数为0,说明扇区数就是AX,已经加载完一个扇区,所以减去1 
        @1: 
                cmp ax,0            ;程序总长度小于512字节或者为512整数倍时,AX=0
                jz direct
                
            ;读取剩余的扇区
            push ds             ;压栈保护
                
                mov cx,ax           ;循环次数就是剩余扇区数
        @2: 
                mov ax,ds
                add ax,0x20         ;新读取一个扇区写入内存,段地址+0x20,偏移地址重新从0x0000算起
                mov ds,ax
                
                xor bx,bx           ;新的一段开始,偏移地址都是从0x0000开始
                xor di,di           ;已知用户程序非常短,表示逻辑扇区号用不到高12位
                inc si              ;源地址LBA逻辑扇区号+1
                call read_hard_disk_0       
                loop @2
                
            
            pop ds              ;弹处栈,恢复寄存器
    
    ;------------------------------------------------------------------------
    ;   回写
    ;------------------------------------------------------------------------
    ;   此时全部的用户程序已经加载到内存
    ;------------------------------------------------------------------------       
    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]           ;需要重新定位的表项数
                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字节
                loop realloc    
    ;------------------------------------------------------------------------
    ;   跳转到用户程序执行
    ;------------------------------------------------------------------------
    ;   已经完成对入口地址的回写
    ;------------------------------------------------------------------------       
            ;跳转到 用户程序 code_1段的 标号 start 处
            jmp far [0x04]
        
        
    ;------------------------------------------------------------------------
    ;   子程序:read_hard_disk_0
    ;------------------------------------------------------------------------
    ;   功能: 从硬盘指定位置,以扇区为单位,读取数据放到内存指定位置
    ;   参数: 
    ;           源地址 SI DI
    ;           目的地址 DS:[BX]
    ;   过程:
    ;           (1)设置要读取的扇区数;
    ;           (2)解读LBA模式扇区号;
    ;           (3)请求硬盘读操作;
    ;           (4)查询硬盘状态;
    ;           (5)完成连续读取数据。
    ;------------------------------------------------------------------------
    read_hard_disk_0:
            ; 寄存器压栈保护
            push ax
            push bx
            push cx
            push dx
        
            ;(1)设置要读取的扇区数;
                mov dx,0x1f2        ;Ox1f2          
                mov al,1            ;扇区数=1
                out dx,al
                
            ;(2)解读28位 LBA模式扇区号;
                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表示:从主硬盘读取数据
                or al,ah
                out dx,al
            
            ;(3)请求硬盘读操作;
                inc dx              ;0x1f7[命令端口]
                mov al,0x20         ;读命令
                out dx,al
            
            ;(4)查询硬盘状态;
            .waits:
                    in al,dx        ;0x1f7[状态端口]
                    and al,0x88     ;0x88 = 10001000B 保留第7位 BSY 以及 第3位DRQ
                    cmp al,0x08     ;0x08 说明硬盘已准备好与主机进行交换
                    jnz .waits      ;条件转移指令
                    
            ;(5)完成连续读取数据。
                mov dx,0x1f0        ;0x1f0[数据端口]
                mov cx,256          ;1个扇区是512字节=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标记的段的汇编地址转换为可以寻址内存的段基址
    ;   参数:
    ;           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
    ;------------------------------------------------------------------------
    ;   其余数据
    ;------------------------------------------------------------------------
    ;------------------------------------------------------------------------
        phy_base dd 0x10000         ;(目的地址)用户程序将被加载到的内存物理地址
    
    times   510-($-$$)  db 0
                        db 0x55,0xaa
    
    ;------------------------------------------------------------------------
    ;   加载程序结束
    ;------------------------------------------------------------------------
    

    用户程序 hello.asm

    ;------------------------------------------------------------------------
    ;   用户程序开始
    ;------------------------------------------------------------------------
    ;   文件名:hello.asm
    ;   文件说明:功能程序代码(用户程序)
    ;   创建日期: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)需要重新定位的表项数
            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 $                   ;无限循环 
    ;------------------------------------------------------------------------
    ;   子程序:put_string
    ;------------------------------------------------------------------------   
    ;   功能:在屏幕上显示字符串,字符串以0结尾
    ;   参数: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     
    ;------------------------------------------------------------------------
    ;   数据段
    ;------------------------------------------------------------------------
    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汇编语言]加载程序与用户程序
    https://www.jianshu.com/p/e101fce6cdb5

    [008][x86汇编语言]硬盘与显卡的访问与控制 加载程序c08_mbr.asm 用户程序c08.asm
    https://www.jianshu.com/p/72c151606908

    [009][x86汇编语言]学习加载程序的编写(c08_mbr.asm)
    https://www.jianshu.com/p/add4f948a321

    [010][x86汇编语言]学习用户程序的编写(c08.asm)
    https://www.jianshu.com/p/4177e9ff49d6

    相关文章

      网友评论

        本文标题:[013][x86汇编语言]从硬盘主引导扇区启动,显示hello

        本文链接:https://www.haomeiwen.com/subject/xizejftx.html