突破连续读取72个扇区
1.简介
因为一些特殊的原因 当我们读取超过72个扇区的时候 就会出现错误
但是我们后边的开发 肯定是要超过72个扇区的
2.代码
我们的操作系统加载器 秉承简单够用的原则
只要能把编译好的二进制内核送进内存就可以了
所以加载器的算法是 连续读取软盘扇区 将扇区的内容写入到从0x8000 开始的内存中
以下是我们内核加载器的代码
org 0x7c00; LoadAddr EQU 08000h entry: mov ax, 0 mov ss, ax mov ds, ax mov es, ax mov BX, LoadAddr ; ES:BX 数据存储缓冲区 mov CH, 1 ;CH 用来存储柱面号 mov DH, 0 ;DH 用来存储磁头号 readFloppy: cmp byte [load_count], 0 je beginLoad mov CL, 1 ;CL 用来存储扇区号 mov AH, 0x02 ; AH = 02 表示要做的是读盘操作 mov AL, 18 ; AL 表示要练习读取几个扇区 mov DL, 0 ;驱动器编号,一般我们只有一个软盘驱动器,所以写死 ;为0 INT 0x13 ;调用BIOS中断实现磁盘读取功能 inc CH dec byte [load_count] JC fin add bx, 512 * 18 jmp readFloppy beginLoad: jmp LoadAddr load_count db 3 ;连续读取几个柱面 fin: HLT jmp fin
load_count 指的是要读取的软盘柱面数
一个1.44M软盘 其中一个磁面有80个柱面 一个柱面有有两面 上面和背面 每面对应一个磁道 一个磁道有18个扇区 一个扇区有512字节
上面代码中
load_cout 的值设置为3 也就是程序要连续读取3个柱面 也就是要将软盘中大约 3* 18 * 512 字节
也就是27k的内容写入地址为08000h的内存中
随着我们开发的操作系统功能越来越强大 其代码量也越来越大 现在内核编译后 已经接近15k了 超过27k是迟早的事情
一旦超过27k 那么我们的软盘在往内存拷贝内核时 就需要连续将4个柱面 也就是72扇区的数据写入到内存中
按照设想 我们只要把上面load_cout的值改成4就可以了
然而一旦改成4 问题就出现了
因为读取4个柱面 也就要连续向内存读入18*4 = 72个扇区的内容
现在大多数BIOS提供的int 013h软盘读取中断功能
一旦发现调用代码要读取的内容有72扇区以上 就会返回失败
如果有同学尝试着把上面代码的load_cout该成4 然后再运行程序 就会发现内核加载失败
也就是int 03h 的中断调用返回失败
这样一来 一旦我们的内核大小超过27k的话 我们现在的加载器就无法正确加载了
为了绕过这个限制
现在我们加载器的做法是
不再一次连续读取18个扇区 而是一次读取一个扇区
把这个扇区的数据先读入一个给定的 大小为512字节的缓冲区内
然后再把该缓冲区的内容 拷贝到指定的内存中 也就是我们要多做一次没有意义的拷贝工作
由于我们的内核要加载到内存08000h
因此 我将08000h前512字节 也就是起始地址为07E00h开始的512字节内存作为软盘一个扇区数据的缓冲区
每次从软盘读入一个扇区数据时 先把数据写入到这个缓冲区 然后再把这个缓冲区的数据拷贝到08000h之后的地址
代码如下
org 0x7c00; LoadAddr EQU 08000h BufferAddr EQU 7E0h BaseOfStack equ 07c00h entry: mov ax, 0 mov ss, ax mov ds, ax mov ax, BufferAddr mov es, ax mov ax, 0 mov ss, ax mov sp, BaseOfStack mov di, ax mov si, ax mov BX, 0 ; ES:BX 数据存储缓冲区 mov CH, 1 ;CH 用来存储柱面号 mov DH, 0 ;DH 用来存储磁头号 mov CL, 0 ;CL 用来存储扇区号 ;每次都把扇区写入地址 07E00处 readFloppy: cmp byte [load_count], 0 je beginLoad mov bx, 0 inc cl mov AH, 0x02 ; AH = 02 表示要做的是读盘操作 mov AL, 1 ; AL 表示要读取几个扇区 mov DL, 0 ;驱动器编号,一般我们只有一个软盘驱动器,所以写死 ;为0 INT 0x13 ;调用BIOS中断实现磁盘读取功能 JC fin ;把刚写入07E00的一个扇区的内容写入从08000h开始的地址 copySector: push si push di push cx mov cx, 0200h ;缓冲区数据大小,也就是512字节 mov di, 0 mov si, 0 mov ax, word [load_section];es mov ds, ax copy: cmp cx, 0 je copyend mov al, byte [es:si] ;es:si指向07E00 mov byte [ds:di], al inc di inc si dec cx jmp copy copyend: pop cx pop di pop si mov bx, ds add bx, 020h mov ax, 0 mov ds, ax mov word [load_section], bx mov bx, 0 ;end of copySector cmp cl, 18 jb readFloppy inc CH mov cl, 0 dec byte [load_count] jmp readFloppy beginLoad: mov ax, 0 mov ds, ax jmp LoadAddr load_count db 10 ;连续读取几个柱面 load_section dw 0800h fin: HLT jmp fin
有了上面改动后 加载代码能读取任意多个扇区数据到内存而不用担心Bochs虚拟机的限制
虽然我用的是Virtual Box 但据说Virtual Box使用的也是Bochs代码
所以当我连续加载扇区超过72时 Virtual Box对代码的数据读取请求也返回识别
使用上面的修改后 数据的读取问题也能得到解决了
3.MessageBox的计数器效果
这里插一个部分 就是实现一个MessageBox的计数器
void CMain(void) { ... for(;;) { char* pStr = intToHexStr(counter); counter++; boxfill8(shtMsgBox->buf, 160, COL8_C6C6C6, 40, 28, 119, 43); showString(shtctl, shtMsgBox, 40, 28, COL8_000000,pStr); io_cli(); if (fifo8_status(&keyinfo) + fifo8_status(&mouseinfo) == 0) { io_sti(); } ... }
编译之后运行
但是最终的效果 会出现一直闪烁