开发操作系统前的第一个程序
虚拟机选好了,开发工具选好了,平台选好了,可以开始开发操作系统了吗?其实,还不行,要先准备一个工具程序 - 安装引导程序的程序
为什么要先开发安装引导程序的程序?
原因很简单,如果你马上开始写系统,比如引导程序,就算你写好了操作系统的第一个要运行的引导程序,你也会发现一个问题,你要怎么写到硬盘里?在windows里面复制到硬盘就行?当然不行了,因为你根本无法直接操作扇区。所以,不管怎么样,都要把直接读写硬盘的工具程序先开发出来,其实这就是安装操作系统的最后一步要用的程序:安装引导程序的程序。
一般来说,在复制完操作系统所需的文件后,才会安装引导程序,毕竟来说,如果原来的磁盘有系统,如果一开始就把引导给改了,如果新系统又没装成,那连原来的系统也引导不了了。如果在复制完所有的文件后,在安装引导程序,那安装的风险就会下降很多很多。
安装引导程序的程序的运行方式
这个程序有几种运行方式:1、在windows或linux下运行;2、在DOS下运行;3、在自己的系统里运行
在windows或linux下运行
在我看来,这仅仅是在开发完成后,正式安装程序用的可选方式之一。
为什么?
这就要考虑在物理机器上的运行方式了,难道你在物理机器上测试的时候,还把电脑拆开挂个硬盘上去后,在windows里运行完安装程序后,再把安装好的系统硬盘拆下来装到别的地方上去?或者说在用U盘启动windows后,在windows里安装?虽然能做到,但每次启动windows的时间,就够你烦的。当然,如果你想借机休息,那倒是应该在windows下运行。
在DOS下运行
这是我选择的方式,很简单,做成DOS启动盘以后来安装和测试很简单,即使在物理机上用U盘启动DOS也很简单。更重要的是,我可以把安装程序放进虚拟软盘里,测试安装过程。在软盘上测OK了,在转到U盘上测试,不用一上来就搞物理机。
在自己的系统中运行
这是终极方法,所有的操作系统都必须这么做,但是现在还做不到,只能放弃
工具程序的功能
这个工具程序的主要功能其实很简单,就是把MBR启动程序和活动分区的启动程序写到对应的地方,核心功能是直接读写硬盘。在这个功能之上,可以发展出其他辅助的功能。
DOS下直接读写硬盘 – INT 13H
详细的内容放到书里的附录写,这里只放DAP的结构、扩展读、扩展写两个调用规范
/* extend INT 13H use this */
typedef struct _disk_address_packet_t
{
unsigned char dap_PacketSize,
dap_Rsvd;
unsigned short dap_BlockCount;
unsigned long dap_BufAddress; /* segment:offset */
unsigned long dap_BlockNum; /* Low 32-bit */
unsigned long dap_BlockNumH; /* high 32-bit */
}dap_t;
扩展读
入口:
AH = 42h
DL = 驱动器号
DS:SI = 磁盘地址数据包(Disk Address Packet)
返回:
CF = 0,AH = 0 成功
CF = 1,AH = 错误码
这个调用将磁盘上的数据读入内存。如果出现错误,DAP 的 BlockCount项中则记录了出错前实际读取的数据块个数。
扩展写
入口:
AH = 43h
AL
0 位 = 0 关闭写校验
1 打开写校验
1 - 7 位保留,置 0
DL = 驱动器号
DS:SI = 磁盘地址数据包(DAP)
返回:
CF = 0,AH = 0 成功
CF = 1,AH = 错误码
这个调用将内存中的数据写入磁盘。如果打开了写校验选项,但 BIOS不支持,则会返回错误码 AH = 01h,CF = 1。功能 48h 可以检测BIOS是否支持写校验。
如果出现错误,DAP 的 BlockCount 项中则记录了出错前实际写入的数据块个数。
这里有一点要注意:网络上很多资料把DS:SI写成了DS:DI
DOS下直接读写硬盘的C函数
把INT 13H的读写的功能封装成C函数,就完事了,没那么神秘。函数贴出来,
/* 2019-10-12. order source. robin.
purpose : Read/Write hard disk directly.
argument:
char nDisk: hard disk id, base 0
int bRW: 1 read disk, 0 write disk
unsigned long nSectAddr: 32-bit sector address
void * pBuf: sector buffer
unsigned int nBufSize: buffer size
output : success return 0, failed return other
*/
int DiskRW( char nDisk, int bRW, unsigned long nSectAddr, void * pBuf,
unsigned int nBufSize )
{
unsigned char nCmd, retval;
dap_t dap;
nCmd = bRW ? 0x42 : 0x43;
/* limit the read/write size */
if( nBufSize > (unsigned int)(32 * 1024) )
return 1;
if( 0 == (nBufSize /= 512) )
return 0;
dap.dap_PacketSize = sizeof(dap_t);
dap.dap_Rsvd = 0;
dap.dap_BlockCount = (unsigned short)nBufSize;
dap.dap_BufAddress = (unsigned long)((void far *)(pBuf));
dap.dap_BlockNum = nSectAddr;
dap.dap_BlockNumH = 0;
nDisk |= 0x80;
asm{
lea si, dap;
mov dl, nDisk;
mov ah, nCmd
int 013h
mov retval, ah
}
return retval;
}
DOS下安装引导程序的程序
就是一个直接读写硬盘的程序,
》》读:功能就是把读出的扇区数据保存到文件中。
》》写:把文件中的数据写到扇区中
读写的单位都是一个扇区,如果提供的数据文件不足一个扇区,也会写一个扇区的数据进去。
这个程序为了写书临时写了一个简化的版本,很简单的,就100行左右的程序,这里贴一个运行的截图
完整的程序就不贴出来了,可以把这个程序当作安装引导程序的程序,写个批处理逐个扇区写进去就行了,就是要自己把每个扇区单独做成一个文件。
真正用的程序,比较复杂,毕竟要考虑很多不同的运行环境。
如果是初学者,可以用DiskRW这个代码自己扩展,