从硬盘中读取用户程序到内存中
-
加载器和用户程序头之间的约定 或者说 协议 前面一章已经说过
-
外围设备以及其接口,总线,ICH的作用以及关系
-
I/O端口以及端口访问,硬盘的端口特性
-
内存空间当前的分布
-
过程调用
代码实现以及详细注释
;加载器的两个难点:
;难点1 决定加载用户程序到内存中哪个地址上,即需要找到一个合适的空闲内存
;难点2 找到用户程序在硬盘中的位置,起始扇区号和用户程序的大小
;内存空间分部:
;A0000 ~ FFFFF ROM BIOS
;10000 ~ 9FFFF AVALIBLE SPACE
;00000 ~ 0FFFF LOADER AND STACK OF IT 主引导扇区的代码可以放在这里 从0x7c00开始
app_lba_start equ 100 ;声明 用户程序起始逻辑扇区号
SECTION mbr align=16 vstart=0x7c00 ;引导代码 段内所有元素的汇编地址都从0x7c00开始
;设置堆栈段和栈指针 SS=0 SP=0 指向0x7c00开始处
mov ax,0
mov ss,ax
mov sp,ax
;计算出用户程序被加载物理地址的段地址
mov ax,[cs:phy_base] ;被加载的物理地址保存在标号phy_base处的一个双字单元中 所以需要两个16bit的寄存器存储
mov dx,[cs:phy_base+0x02] ;注意 是小端模式存储 由于拿到的是物理地址 要转化为段地址才能将数据段指针指向那里 要左移4bit
mov bx,16 ;div bx可见是16bit除法 被除数(dx|ax) / 除数(bx) = 商(ax) ---余数(dx)
div bx ;这就转化为段地址了
mov ds,ax ;存入ds 数据段指向这个段地址
mov es,ax ;存入es 附加段指向这个段地址
;开始读取程序的起始部分
xor di,di ;di清零
mov si,app_lba_start ;程序在硬盘上的起始逻辑扇区号: DI:SI 0:100
xor bx,bx ;开始读取硬盘上的用户程序内容 加载到DS:0x0000处 采用基址寻址 使用bx寄存器提供偏移 bx先设置为0
call read_hard_disk_0
read_hard_disk_0: ;从硬盘读取一个逻辑扇区
;输入: DI:SI=起始逻辑扇区号 SRC 访问硬盘需要提供扇区起始地址和要访问扇区数目 LBA28编址方式 每个扇区号用28bit数值表示 最多可表示2^28个扇区
; DS:BX=目标缓冲区的地址 DST
push ax ;保护现场 将即将会被破坏的寄存器保存到栈中
push bx
push cx
push dx
;主磁盘有8个端口0x1f0到0x1f7 分配:f3到f5端口寄存器存放23位的起始扇区号 f6的8bits用于放24~27扇区号以及设置模式和选择盘 f7控制读写
mov dx,0x1f2
mov al,1
out dx,al ;要读取的扇区数为1 写入到0x1f2端口寄存器中
inc dx ;指向下一个端口 0x1f3
mov ax,si ;起始逻辑扇区预先保存在di:si里面的 使用多个端口寄存器存放 低地址端口寄存器从si中取 高地址的从di中取
out dx,al ;0x1f3端口寄存器放7~0位 si的低8bit
inc dx ;指向下一个端口 0x1f4
mov al,ah
out dx,al ;0x1f4端口寄存器放15~8 si的高8bit
inc dx ;0x1f5
mov ax,di
out dx,al ;0x1f5端口寄存器放23~16 di的低8bits
inc dx ;0x1f6 还剩27~24的4bit没有放 要写到0x1f6的低4位中
mov al,0xe0 ;设置LBA28模式 并选择主盘 di的高8bits
or al,ah ;di的高8bits在ah中 如果有值 则放到al里 和0xe0一起
out dx,al ;一起写到0x1f6
inc dx ;0x1f7 既是命令端口又是状态端口
mov al,0x20 ;读命令
out dx,al
;等待硬盘Ready
.waits:
in al,dx ;读出0x1f7里的数据-->al
and al,0x88 ;0x88=0b10001000 al=al&0x88 保留第7bit和第3bit
cmp al,0x08 ;0x08=0b00001000 cmp比较 判断0x1f7的第3bit是否为1 3bit表示硬盘是否已准备好和主机交换数据
jnz .waits ;不忙,且硬盘已准备好数据传输
mov cx,256 ;总共要读取的字数
mov dx,0x1f0 ;下面还是读取扇区里的内容
;循环读取 从0x1f7端口中读到DS:BX中 BX在30行被清0 BX每次循环+2 跳一个word
.readw:
in ax,dx
mov [bx],ax ;DS:BX
add bx,2
loop .readw
pop dx ;恢复现场
pop cx
pop bx
pop ax
ret
phy_base dd 0x10000 ;用户程序被加载的物理地址 不是一定要在这里
times 510-($-$$) db 0
db 0x55,0xaa