3.6让MBR使用磁盘
这里通过讲解一个汇编程序讲解如何使用MBR去使用磁盘
改造MBR
boot.inc
;------------- loader和kernel ---------- LOADER_BASE_ADDR equ 0x900 ;表示loader加载的内存位置 LOADER_START_SECTOR equ 0x2;表示读取的磁盘数,用LBA方式描述的磁盘位置
boot.inc其实相当于一个配置文件,该文件目前定义了两个宏,LOADER_BASE_ADDR
LOADER_START_SECTOR
这两个宏。作用在程序里面已经表示出来了
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;放在gs段寄存器中 ; 清屏 ;利用0x06号功能,上卷全部行,则可清屏。 ; ----------------------------------------------------------- ;INT 0x10 功能号:0x06 功能描述:上卷窗口 ;------------------------------------------------------ ;输入: ;AH 功能号= 0x06 ;AL = 上卷的行数(如果为0,表示全部) ;BH = 上卷行属性 ;(CL,CH) = 窗口左上角的(X,Y)位置 ;(DL,DH) = 窗口右下角的(X,Y)位置 ;无返回值: mov ax, 0600h mov bx, 0700h mov cx, 0 ; 左上角: (0, 0) mov dx, 184fh ; 右下角: (80,25), ; 因为VGA文本模式中,一行只能容纳80个字符,共25行。 ; 下标从0开始,所以0x18=24,0x4f=79 int 10h ; int 10h ; 输出字符串:MBR 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 ;A表示绿色背景闪烁,4表示前景色为红色 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地址0x2 mov bx,LOADER_BASE_ADDR ; 写入的地址0x9000 mov cx,1 ; 待读入的扇区数 call rd_disk_m_16 ; 以下读取程序的起始部分(一个扇区) jmp LOADER_BASE_ADDR ;------------------------------------------------------------------------------- ;功能:读取硬盘n个扇区 rd_disk_m_16: ;------------------------------------------------------------------------------- ; eax=LBA扇区号 ; ebx=将数据写入的内存地址 ; ecx=读入的扇区数 mov esi,eax ;备份eax mov di,cx ;备份cx ;读写硬盘: ;第1步:设置要读取的扇区数 mov dx,0x1f2 ;为磁盘Primary通道设置Sector count mov al,cl out dx,al ;读取的扇区数 mov eax,esi ;恢复ax ;第2步:将LBA地址存入0x1f3 ~ 0x1f6 ;LBA地址7~0位写入端口0x1f3 mov dx,0x1f3 out dx,al ;LBA地址15~8位写入端口0x1f4 mov cl,8 shr eax,cl mov dx,0x1f4 out dx,al ;LBA地址23~16位写入端口0x1f5 shr eax,cl mov dx,0x1f5 out dx,al shr eax,cl and al,0x0f ;lba第24~27位 or al,0xe0 ; 设置7~4位为1110,表示lba模式 mov dx,0x1f6 out dx,al ;第3步:向0x1f7端口写入读命令,0x20 mov dx,0x1f7 mov al,0x20 out dx,al ;第4步:检测硬盘状态 .not_ready: ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 nop in al,dx and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙 cmp al,0x08 jnz .not_ready ;若未准备好,继续等。 ;第5步:从0x1f0端口读数据 mov ax, di mov dx, 256 mul dx mov cx, ax ; di为要读取的扇区数,一个扇区有512字节,每次读入一个字, ; 共需di*512/2次,所以di*256 mov dx, 0x1f0 .go_on_read: in ax,dx mov [bx],ax add bx,2 loop .go_on_read ret times 510-($-$$) db 0 db 0x55,0xaa
%include "boot.inc"是让编译器nasm在编译之前把boot.inc包含进来,%include是nasm编译器中的预处理指令。
mov eax,LOADER_START_SECTOR ; 起始扇区lba地址0x2
mov bx,LOADER_BASE_ADDR ; 写入的地址0x9000
mov cx,1 ; 待读入的扇区数
这三行程序是在进入rd_disk_m_16程序之前传递的参数。
后面执行的是读取磁盘内容,并将磁盘内容写到规定的位置。
其中涉及的知识点比较多。
1.显示器文本显示模式的地址是0xb800
2.磁盘读写步骤
第一步:设置要读取的扇区数
第二步,将LBA地址存入0xlf3~0xlf6
第三步,向0xlf7端口写入读命令0x20
第四步,检测硬盘状态
第五步,从0x1f0端口读取数据
上面的数据是如何来的可以通过前面章节对硬盘的讲解。
实现内核加载器
%include "boot.inc" section loader vstart=LOADER_BASE_ADDR ; 输出背景色绿色,前景色红色,并且跳动的字符串"1 MBR" mov byte [gs:0x00],'2' mov byte [gs:0x01],0xA4 ; A表示绿色背景闪烁,4表示前景色为红色 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 $ ; 通过死循环使程序悬停在此