前言
这次我出现了一些BUG
,导致我忙活了一阵子,还好的是解决了
老规矩还是把整个流程过一遍,当MBR
忙活完后,就需要把自己手中的棒交给loader
了,MBR
的作用就是从硬盘中的内核加载器移动到内存中,然后跳转到内核加载器的地址去让内核加载器运行,在这方面需要对硬件需要一些了解特别是in out
指令,还有个各个端口访问(端口就是寄存器),因为需要通过这些端口读取数据
实验
书中也说了需要对rd_disk_m_16
函数要比较了解,所以我给出我的一些注释
mbr.S
%include "boot.inc" ;预处理
SECTION MBR vstart=0x7c00 ;设置段基址
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax
mov ax,0x600
mov bx,0x700
mov cx,0
mov dx,0x184f
int 0x10
;以下都是显存的设置 gs=0xb800 就是可以显示字符的内存地址
mov byte [gs:0x00],'1'
mov byte [gs:0x01],0xA4
mov byte [gs:0x02],' '
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'M'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'B'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'R'
mov byte [gs:0x09],0xA4
mov eax,LOADER_START_SECTOR ;起始扇区Lba地址
mov bx,LOADER_BASE_ADDR ;写入的地址
mov cx,1 ;待读入的扇区
call rd_disk_m_16 ;以下读程序的起始部分
jmp LOADER_BASE_ADDR ;将数据加载到内存中后就会转而执行这个,其实也就是loader.bin
;----功能:读取硬盘第n个扇区----
;eax LBA扇区号
;bx=将内存写入的地址
;cx=读入的扇区数
rd_disk_m_16:
mov esi,eax ;用来备份eax,di
mov di,cx ;di=1
mov dx,0x1f2 ;设置扇区的数量的端口
mov al,cl ;al=1,只需要8位
out dx,al ;将al写入dx寄存器中,dx寄存器就是端口
mov eax,esi ;将eax复位
mov dx,0x1f3 ;设置LBA的端口
out dx,al ;将LBA的7-0位读入端口去
mov cl,8 ;
shr eax,cl ;右移8位
mov dx,0x1f4
out dx,al ;将LBA的15-8位存入端口中去
shr eax,cl ;右移8位
mov dx,0x1f5
out dx,al ;将LBA的23-16位存入端口中去
shr eax,cl ;再次右移8位
and al,0x0f ;让低4位不变,因为低4位需要保存LBA的28-24位(LBA总共28位)
or al,0xe0 ;将前四位与1110取或,也就是前三位直接固定,保证寻址模式一定是LBA
mov dx,0x1f6 ;进入device端口
out dx,al ;将信息读给端口
mov dx,0x1f7 ;命令端口
mov al,0x20 ;读入扇区的命令
out dx,al ;将命令读入端口
.not_ready:
nop
in al,dx ;将端口中的信息读到al中,注意此时dx=0x1f7不变,此时是status寄存器,也就是状态端口
and al,0x88 ;通过第7位(从0开始),也就是第8位(从1开始),判断硬盘是不是忙碌的,也就是是否被占了,1代表被占
cmp al,0x08 ;
jnz .not_ready ;如果被占取了,就循环 jnz=jmp not equal
mov ax,di ;di=1
mov dx,256
mul dx ;dx=ax*dx 每次读取1个字,也就是两字节,一共512字节,所以需要256次
mov cx,ax ;cx指定循环的次数
mov dx,0x1f0 ;数据端口,终于开始读取数据了
.go_on_read:
in ax,dx ;将端口中指定的数据,也就是指定的扇区的数据读入到ax中
mov [bx],ax ;bx寄存器存储的就是0x900也就是loader的内存地址
add bx,2 ;每次读两字节
loop .go_on_read
ret ;返回后就会执行jmp跳转到0x900去了,此时机会执行loader.bin
times 510-($-$$) db 0 ;剩下的数据填充为0
db 0x55,0xaa ;魔数,判断是否为mbr
loader.S
%include "boot.inc"
SECTION LOADER vstart=LOADER_BASE_ADDR
mov byte [gs:0x00],'2'
mov byte [gs:0x01],0xA4
mov byte [gs:0x02],' '
mov byte [gs:0x03],0xA4
mov byte [gs:0x04],'L'
mov byte [gs:0x05],0xA4
mov byte [gs:0x06],'O'
mov byte [gs:0x07],0xA4
mov byte [gs:0x08],'A'
mov byte [gs:0x09],0xA4
mov byte [gs:0x0a],'D'
mov byte [gs:0x0b],0xA4
mov byte [gs:0x0c],'E'
mov byte [gs:0x0d],0xA4
mov byte [gs:0x0e],'R'
mov byte [gs:0x0f],0xA4
jmp $ ;通过死循环使程序悬停
实验结果&&出现的问题
在使用dd
命令写入磁盘的时候要尽量使用conv=notrunc
,这代表不截断数据的意思,一开始你不是给hd60M.img
文件分配60M
的大小吗,此时文件大小是512
字节的整数倍,并且那个bochsrc
数据中的CHS
配置也是根据你自己create
时配置的,其实也就是代表hd60M.img
的大小,如果你在dd
命令后使hd60M.img
大小变了,如果不是512
字节的整数倍,就会panic错误
,同时大小变了也就对不上bochsrc
的CHS
配置了,就会panic not fit disk image错误
参考
操作系统真象还原