天天看点

仿照着写个bootloader (三-1)

DiskDataReg        equ 0x01f0

DiskErrReg        equ 0x01f1

DiskSectCntReg    equ 0x01f2

DiskLoLBAAddr    equ 0x01f3

DiskMeLBAAddr    equ 0x01f4

DiskHiLBAAddr    equ 0x01f5

DiskModReg        equ 0x01f6

DiskCmdStatReg    equ 0x01f7

DiskReadCmd        equ 0x20

DiskWriteCmd    equ 0x30

Arg1Off        equ 0x06 ;第一个参数相对bp偏移

Arg2Off        equ 0x08 ;第二个参数相对bp偏移

Arg3Off        equ 0x0A ;第三个参数相对bp偏移

Arg4Off        equ 0x0C ;第四个参数相对bp偏移

StackBase    equ 0x0000

StackEnd    equ 0x2000

DataBase    equ 0x0300

DataEnd        equ 0x1FFF

AppLenOff        equ 0x10

AppEntryOff        equ 0x14

AppCodeSegOff    equ 0x16

AppSegNumsOff    equ 0x1A

AppRelocTab        equ 0x1C

;==========================

ProgLocSect equ 0x04

section bootloader align=16 vstart=0x7c00

jmp start

Relocation:

    ;前面取出没重定位前保存在用户程序中的地址,加上重定位的基址,得到最终的地址

    ;实模式下,地址20位。ax取低16位,dx取高16位,其中低4位有效

    ;20位右移4位,得到段地址。ax先右移4位,空出高位4位;

    ;dx低4位左移12位,即15-12位为段地址高位,再与ax的低12位相或,得到16为段地址

    add ax,word [cs:LoadPhyBase]

    adc dx,word [cs:LoadPhyBase+2]

    shr ax,0x04

    shl dx,0x0c

    or ax,dx

    ret

SetSectAddr:

    ;设置逻辑扇区的地址

    push bp

    mov bp,sp

    push bx

    ;发扇区总数

    mov dx,DiskSectCntReg

    mov al,byte [bp+Arg4Off]

    out dx,al

    ;发扇区地址

    mov dx,DiskLoLBAAddr

    mov al,byte [bp+Arg3Off]

    out dx,al

    mov dx,DiskMeLBAAddr

    mov al,byte [bp+Arg3Off+1]

    out dx,al

    mov dx,DiskHiLBAAddr

    mov al,byte [bp+Arg2Off]

    out dx,al

    mov dx,DiskModReg

    mov al,byte [bp+Arg2Off+1]

    out dx,al

    pop bx

    mov sp,bp

    pop bp

    ret

WaitDiskReady:

    ;等待磁盘就绪

    mov dx,DiskCmdStatReg

.waits:

    in al,dx

    and al,0x88

    cmp al,0x08

    jnz .waits

    ;判断是否有错误

    mov dx,DiskErrReg

.getErr:

    in al,dx

    cmp al,0x00

    ;al不为0出错

    jnz .resume

    ;没有出错 返回0

    xor ax,ax

    ret

.resume:

    ;出错 返回

    mov ax,0x01

    ret

CheckMagicNum:

    mov si,MagicNum

    mov di,0

    mov cx, 0x0A

CheckValidAppLoop:

    ;si和di不在同一个段中,因此cmpsb用不了

    mov ax,[di]

    cmp word [cs:si],ax

    jnz .notValidApp

    inc si

    inc di

    loop CheckValidAppLoop  

    xor ax,ax

    ret

.notValidApp:

    mov ax,0x01

    ret

ReadOneSection:

    call SetSectAddr

    ;发读命令

    mov dx,DiskCmdStatReg

    mov al,DiskReadCmd

    out dx,al

    call WaitDiskReady

    xor bx,bx ;准备拷贝,bx做索引

    ;读取保存在扇区上的数据的有效长度

    mov cx,0x100

    mov dx,DiskDataReg

.readw:

    in ax,dx

    mov [bx],ax

    add bx,2

    loop .readw

    ret

start:

xor ax,ax

xor dx,dx

;计算用于加载程序的内存段地址

mov ax,word [cs:LoadPhyBase]

mov dx,word[cs:LoadPhyBase+2]

mov bx,0x10

div bx

;dx:ax/bx->商存放在ax中

;商即为段地址

;为后面用户程序设置段寄存器(es/ds),指向LoadPhyBase所指的内存起址

;如果没有设置ds,用户程序运行时,程序不知道到何处去取出段头信息,程序大小

mov ds,ax

mov es,ax

push word 0x0001

push word ProgLocSect

push word 0xe000

push word 0x0200

;加载用户程序所在的第一个扇区

call ReadOneSection

;恢复堆栈

add sp,0x08

call CheckMagicNum

cmp ax,0x00

jnz $ ;魔术字不匹配就在此循环

;程序从磁盘读到0x1000:0000开始的内存中,程序长度记录在0x10002处

mov ax,word [AppLenOff]

mov dx,word [AppLenOff+0x02]

;从用户程序的总长度决定还有多少扇区要读取

mov bx,0x200

div bx

;ax:程序占了完整的几个扇区,dx:还多余几个字节

;多余的字节占用一个扇区,故,共占用ax+1个扇区

;前面已经读取了一个扇区,剩下ax个扇区还要读取

cmp dx,0x0000

;如果没有零星字节

jz .totalSections

;如果有零星字节,向上调整

inc ax

.totalSections:

cmp ax,0x0001

jz ReadSectComplete

;循环读取次数

mov cx,ax

dec cx

xor di,di

;段地址每次增加0x200,存储扇区内容

;保存ds,ds最终还是要指向0x1000

mov ax,ds

push ax

mov bx,ProgLocSect

;上次已经加载了ProgLocSect指定的扇区

;这次加载下一个扇区

inc bx

RemainSector:

    add ax,0x20

    mov ds,ax

    push cx ;cx的值可能会被子函数修改 保存起来

    ;继续读剩下的扇区

    push word 0x0001

    push word bx

    push word 0xe000

    push word 0x0200

    call ReadOneSection

    add sp,0x08

    inc bx

    pop cx

loop RemainSector

pop ax

mov ds,ax

;现在程序全在从LoadPhyBase开始的内存中,由ds指向

ReadSectComplete:

;用户程序重定位

RelocationCodeSeg:

    mov ax,word [AppCodeSegOff]

    mov dx,word [AppCodeSegOff+2]

    call Relocation

    ;修正用户头中代码段基址,先清空以前的内容

    mov word [AppCodeSegOff],0x0000

    mov word [AppCodeSegOff+2],0x0000

    mov word [AppCodeSegOff],ax

RelocationTabElem:

    ;修正用户头中重定位表中各项基址,先清空以前的内容

    mov cx,word [AppSegNumsOff]

    ;重定位表偏移

    xor bx,bx

    mov bx,AppRelocTab

  RelocRound:

    mov ax,word [bx] ;表项低位

    mov dx,word [bx+0x02] ;表项高位

    call Relocation

    mov word [bx],0x0000

    mov word [bx+0x02],0x0000

    mov word [bx],ax

    add bx,0x04

    loop RelocRound

    ;重定位结束,跳转到用户程序

    ;代码在[AppEntryOff]开始的位置

    ;开始时没加far,结果是近跳转,过不去

    ;远跳转,从内存中取出双字,修改cs:ip的值

    jmp far [AppEntryOff]

MagicNum    db 'M',0x2e,'A',0x2e,'G',0x2e,'I',0x2e,'C',0x2e

times 0x06 db 0x00

LoadPhyBase dd 0x10000

times 510-($-$$) db 0

db 0x55,0xaa

本文仅是对仿照着写个bootloader (三)的修改:

继续阅读