通过【自制操作系统01】电脑启动过程的硬核讲解和【自制操作系统02】环境准备和启动区实现的讲解,我们实现了最简单的操作系统之一(只有一条机器指令)。
今天我们要更进一步,逐步完善这个最简单的操作系统。之前最简单的操作系统写在引导区的512字节。这么小的空间以后肯定不会用来写操作系统的代码了,所以它的主要任务就是从硬盘读取更多的数据到内存。在里面,然后跳转到内存中的那个位置开始运行。
我必须复习每节课中提到的四个跳跃:
一跳:按下电源键,CPU会强制PC的寄存器值被初始化到这个位置就是BIOS程序的入口地址。二跳转:入口地址为跳转指令,跳转到该位置,开始执行 3.跳转:执行一些硬件检测工作后,最后一步是将引导区的内容加载(复制)到内存中,并跳到它。四跳转:boot区的代码主要是加载操作系统内核,跳转到加载的地方
事实上,我们可以无限期地跳跃。只要某个环节的任务比较复杂,我们可以分两步走。但是,完全可以停止从第三次跳转开始,将操作系统所需的所有指令和数据从硬盘加载到内存中执行,但这显然是不好的。
一、代码概览
别的不谈了,先把本章的全部代码发过来
mbr.asm
;----BIOS把启动区加载到内存的该位置,所以需设置地址偏移量
section mbr vstart=0x7c00
;----设置堆栈地址
mov sp,0x7c00
;----卷屏中断,目的是清屏
mov ax,0x0600
mov bx,0x0700
mov cx,0
mov dx,0x184f
int 0x10
;----直接往显存中写数据
mov ax,0xb800
mov gs,ax
mov byte [gs:0x00],'m'
mov byte [gs:0x02],'b'
mov byte [gs:0x04],'r'
;----读取硬盘(第2扇区)并加载到内存(0x900)
mov eax,0x02 ;起始扇区lba地址,LBA=(柱面号*磁头数+磁头号)*扇区数+扇区编号-1
mov bx,0x900 ;写入的内存地址,之后用
mov cx,4 ;待读入的扇区数
call read_disk
jmp 0x900
;----读硬盘方法,eax为lba扇区号,bx为待写入内存地址,cx为读入的扇区数
read_disk:
mov esi,eax ;备份
mov di,cx ;备份
;第一步,设置要读取的扇区数
mov dx,0x1f2
mov al,cl
out dx,al
mov eax,esi ;恢复
;第二步,设置LBA地址
mov cl,8
;0-7位写入0x1f3
mov dx,0x1f3
out dx,al
;8-15位写入0x1f4
mov dx,0x1f4
shr eax,cl
out dx,al
;16-23位写入0x1f5
mov dx,0x1f5
shr eax,cl
out dx,al
;24-27位写入0x1f6
mov dx,0x1f6
shr eax,cl
and al,0x0f ;lba的24-27位
or al,0xe0 ;另外4位为1110,表示lba模式
out dx,al
;第三步,写入读命令
mov dx,0x1f7
mov al,0x20
out dx,al
;第四步,检测硬盘状态
.not_ready:
nop
in al,dx
and al,0x88 ;第4位为1表示准备好,第7位为1表示忙
cmp al,0x08
jnz .not_ready
;第五步,读数据
mov ax,di
mov dx,256
mul dx
mov cx,ax
mov dx,0x1f0
.go_on_read:
in ax,dx
mov [bx],ax
add bx,2
loop .go_on_read
ret
;----512字节的最后两字节是启动区标识
times 510-($-$$) db 0
db 0x55,0xaa
.asm
section loader vstart=0x900
mov byte [gs:0xa0],'l'
mov byte [gs:0xa2],'o'
mov byte [gs:0xa4],'a'
mov byte [gs:0xa6],'d'
mov byte [gs:0xa8],'e'
mov byte [gs:0xaa],'r'
mbr.bin: mbr.asm
nasm -I include/ -o out/mbr.bin mbr.asm -l out/mbr.lst
loader.bin: loader.asm
nasm -I include/ -o out/loader.bin loader.asm -l out/loader.lst
os.raw: mbr.bin loader.bin
../bochs/bin/bximage -hd -mode="flat" -size=60 -q target/os.raw
dd if=out/mbr.bin of=target/os.raw bs=512 count=1
dd if=out/loader.bin of=target/os.raw bs=512 count=4 seek=2
brun:
make install
make only-bochs-run
only-bochs-run:
../bochs/bin/bochs -f ../bochs/bochsrc.disk -q
install:
make clean
make -r os.raw
clean:
rm -rf target/*
rm -rf out/*
二、磁盘
如果你粗略阅读代码,至少你可以知道mbr.asm中的代码。前半部分是在屏幕上输出一个mbr字符串,上节课为了做一个最小的操作系统,写的很直观。代码,可选。后半部分只是读取几个扇区的硬盘数据,加载到内存中的某个位置,然后跳转到这个位置。这部分是mbr的重点和职责。
那么如何读取硬盘中的数据,就得从磁盘的结构说起。我对硬件不是很了解,所以我只能做一个粗略的想法。硬盘是磁盘的一种硬盘数据怎么导入空硬盘,分为硬盘和软盘。但是它们的逻辑结构是一样的:
光盘()
头(头)
跟踪(跟踪)
扇区 ()
气缸()
我不想担心它是如何移动的。我只需要弄清楚。一旦确定了磁头、柱面和扇区,就确定了 512 字节的区域。这就够了。这是硬盘的CHS表示,即(柱面)、Head(磁头)、(扇区),只要知道硬盘的CHS个数,就可以确定硬盘的容量。硬盘容量=柱面数×磁头数×扇区数×512B。
如果不考虑这个物理结构,硬盘实际上是由n个512字节的区域组成的。我们可以从 0 开始编号,每 512 字节加一个,这样我们根本不需要考虑任何扇区。 ,这个是我最喜欢的(好像是软件工程师的思维),这个方法叫做LBA 。
LBA = (柱面号 * 磁头数 + 磁头号) * 扇区数 + 扇区编号 - 1
所以如果CPU需要处理硬盘,要么用CHS记法,要么至少告诉硬盘柱面、磁头、扇区号,要么用LBA记法告诉硬盘LBA号,然后给硬盘一个 read Still write 信号。硬盘厂商上千万,CPU厂商也不一样。自然要有硬盘接口标准。该标准称为ATA标准,也可以通常称为IDE硬盘接口技术标准。你可以下载这个标准的总共三卷,但我们用的不多。找到了正宗的中文版论文《IDE接口硬盘读写技术》。这基本上就够了。
三、IDE 硬盘接口技术
CPU和外设通过IO接口进行交互,所以核心就是这个技术标准定义的IO接口是什么硬盘数据怎么导入空硬盘,它们的作用是什么
I/O 地址读取(主机从硬盘读取数据)写入(主机数据写入硬盘)
1F0H
数据寄存器
数据寄存器
1F1H
错误寄存器(只读寄存器)
特征寄存器
1F2H
扇区计数寄存器
扇区计数寄存器
1F3H
扇区号寄存器或 LBA 块地址 0~7
扇区号或LBA块地址0~7
1F4H
轨道号或LBA块地址8~15的低8位
轨道号或LBA块地址8~15的低8位
1F5H
高8位轨道号或LBA块地址16~23
高8位轨道号或LBA块地址16~23
1F6H
驱动器/头或LBA块地址24~27
驱动器/头或LBA块地址24~27
1F7H
命令寄存器或状态寄存器
命令寄存器
所以如果你要写一个读取文件的程序,不难分析整个过程:
在 1F3H ~ 1F6H 写入 1F2H 中要读取的扇区数。这四个端口写入计算出的起始 LBA 地址。在1F7H处,写入读命令的指令号,不断检查1F7H(此时已经成为状态寄存器的意思)如果第四步不忙,开始从1F0H读取数据到内存中的指定位置,直到阅读完成
这五个步骤对应上面的代码
最后别忘了我们的代码还是要加载到boot area的,所以最后两个字节还是boot area 0x55 0xaa
四、运行代码
写完mbr.asm后,我们再写一个.asm,把它的起始地址设置为0x900(因为这是读写磁盘后存储的内存位置,是我们自己定义的),放在第二个扇区盘的(这个也是我们自己设置的,只要和读盘的代码一致即可)
.asm
section loader vstart=0x900
mov byte [gs:0xa0],'l'
mov byte [gs:0xa2],'o'
mov byte [gs:0xa4],'a'
mov byte [gs:0xa6],'d'
mov byte [gs:0xa8],'e'
mov byte [gs:0xaa],'r'
剩下的本质都在我们的文件里,可以参考上面的代码
执行make brun,可以看到如下效果,说明从磁盘加载代码到内存的过程已经生效。
五、开源项目和课程规划
如果你对做一个操作系统感兴趣,不妨跟着这一系列的课程来阅读,甚至加入我们(下面的公众号和助手微信)一起开发。
参考书
《操作系统真相还原》这本书真的很棒!强烈推荐
项目开源
项目开源地址:
当你看到这篇文章时,代码可能比文章中写了更多的部分。您可以通过提交记录历史查看历史代码。我会慢慢整理提交历史和项目描述文档,争取为每节课准备一个可执行代码。当然,文章中的代码也是完整的,完全可以使用复制粘贴。
文章来源:https://www.cnblogs.com/flashsun/p/12232630.html