通过[自制操作系统01]对计算机启动过程的严格解释和[自制操作系统02]环境准备和启动区域的实现,我们实现了最简单的操作系统(仅一条机器指令)。
今天,我们必须更进一步,逐步改进这个最简单的操作系统。之前最简单的操作系统写在引导区的512字节中。这么小的空间将来肯定不会用于编写操作系统的代码,因此其主要任务是将更多数据从硬盘读取到内存内部,然后跳转到内存位置以开始运行。
我必须回顾每节课中提到的四个跳跃:
一次跳转:按下电源按钮,CPU将强制将PC寄存器的值初始化为0xffff0,此位置是BIOS程序的入口地址。两次跳转:入口地址是一条跳转指令,跳转到该位置0xfe05b,开始执行三个跳转:在执行了一些硬件检测工作之后,最后一步是将引导区的内容加载(复制)到内存0x7c00中,并跳转到该位置。四跳:引导区中的代码主要加载操作系统内核并跳至加载点
实际上,我们可以无限跳,只要某个链接的任务很复杂,我们就可以分两步走。但是,完全有可能停止从第三次跳转中跳出,并将操作系统所需的所有指令和数据从硬盘加载到内存中并执行它们,但这显然不好。
一、代码概述
我们别说什么,首先发送一份本章完整代码的副本
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,0xaaloader.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],rMakefile
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表示形式,即圆柱(圆柱),磁头(磁头),扇区(扇区),只要知道硬盘的CHS数量,就可以确定硬盘的容量。硬盘容量=柱面数×磁头数×扇区数×512B。
如果您不考虑这种物理结构,则硬盘实际上由n个多个512字节区域组成。我们可以从0开始编号,然后每512字节加一个,因此我们完全不需要考虑任何扇区。圆柱体,这是我的最爱(似乎是软件工程师的想法),这种方法称为LBA表示法。
LBA = (柱面号 * 磁头数 + 磁头号) * 扇区数 + 扇区编号 - 1因此,如果CPU需要处理硬盘,则它必须使用CHS表示法,或者至少告诉硬盘柱面号,磁头和扇区号,或者使用LBA表示法告诉硬盘LBA。编号,然后为硬盘提供一个已读取的“仍然写入”信号。硬盘制造商有数千万,而CPU制造商也有所不同。自然,必须有一个硬盘接口标准。该标准称为ATA标准,也可以通常称为IDE硬盘接口技术标准。该标准可以下载AT_Attachment_with_Packet_Interface的三卷内容,但使用量并不大。我找到了一篇相当真实的中文版论文“ IDE接口硬盘读写技术”。这基本上是足够的。
三、IDE硬盘接口技术
CPU和设备通过IO接互,所以核心是该技术标准定义的IO接口是什么,它们的功能是什么


因此,如果您想编写一个程序来读取文件,则不难分析整个过程:
在1F3H〜1F6H中写入要在1F2H中读取的扇区数。这四个端口写入计算出的起始LBA地址。在1F7H处,写入读取命令的指令号并连续检查1F7H(此时已变为状态寄存器的含义)。如果第四步不忙,它将继续从1F0H读取数据到寄存器中的指定位置。内存,直到被读取为止。
这五个步骤与上面的代码相对应

最后,不要忘了我们的代码仍然加载到引导区中,因此最后两个字节仍必须是引导区标识符0x55 0xaa
四、运行代码
在写入mbr.asm之后,我们编写另一个loader.asm,将其起始地址设置为0x900(因为这是在读写磁盘后存储的内存位置,这是我们自己的定义),并将其放入磁盘的第二个扇区(也可以由我们自己设置,只要它与读取磁盘的代码一致)
loader.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剩下的本质在于我们的Makefile,您可以参考上面的代码
执行make brun,您会看到以下效果,这意味着将加载程序代码从磁盘加载到内存的过程是有效的。


六、开源项目和课程计划
如果您对制作操作系统感兴趣,则不妨按照这一系列课程学习,甚至加入我们(下面的官方帐户和微信助手)共同开发。
项目开源项目的开源地址:/ sunym1993 / flashos
当您看到本文时,代码可能编写了比文章更多的部分。您可以通过提交记录历史记录查看历史代码。我将慢慢整理提交历史和项目描述文档,并努力为每节课准备一个可执行代码。当然,本文中的代码也很完整,完全可以使用复制和粘贴。
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/dianqi/article-354586-1.html
是个男人就不会像这样的想法
美国就开始使坏
记得当时是先警告美舰