从头写一个操作系统 05
lesson 6
你可能需要google这个概念:segmentation
目标: 学习16位实模式下的内存寻址
如果非常了解segmentation,可以跳过这节课。
lesson3中我们用[org]
定义了segmentation,其实它就是所有数据的偏移量。
CPU提供了几个特殊的寄存器:cs
、ds
、 ss
、 es
,对应着代码段,数据段,堆栈以及其他段(用户指定)。
注意:它们由CPU隐式调用的,所以当你给ds
赋值后,所有内存的访问都会以ds的值为偏移量。
进一步的说,计算真实地址并不能简单的将两个地址相加(地址与偏移量),而是用segment << 4 + address
这样的方式处理。比如如果ds
的值为 0x4d
, 查询 [0x20]
地址的值实际上是查询 0x4d0 + 0x20 = 0x4f0
。
理论讲的足够了,看一看代码,亲手试试。
注: 不能mov
一个字面量到上面的寄存器,需要使用通用寄存器来mov
。
你可能需要google一下:hard disk, cylinder, head, sector, carry bit
目标:令引导区加载硬盘数据,从而启动内核
我们的系统不可能只有512字节那么小,所以肯定需要从硬盘里读数据,以启动内核。
幸运的是,我们不用自己去写硬盘驱动控制盘片转动与停止,只需要调用BIOS的例程,就像之前将字符打印到屏幕上一样。这样做:赋值0x02
到al
(别的寄存器存储有关cylinder, head and sector的信息),然后调用int 0x13
中断。
13中断可以查阅 a detailed int 13h guide here。
这里,我们第一次遇见carry bit,它在寄存器计算中表示数据超出寄存器的存储范围,其实就是进位,比如寄存器最大能够存储256这个数字(1111 1111),如果再加一个1,就变为了257(1 0000 0000),此时carry bit
中就会是1,寄存器中是0。
mov ax, 0xFFFF
add ax, 1 ; ax = 0x0000 and carry = 1
carry bit
不能直接访问,只能作为别的操作指令判断依据,比如 jc
(当进位被设置时跳转)。
BIOS将al
的值设置为需要读取的扇区数,应该通常会比较它们的值是否一致。
Code
仔细看看 boot_sect_disk.asm中读取硬盘的指令与流程。
boot_sect_main.asm中设置了读取硬盘(disk_load
)所需的参数。注意我们写了一些不属于引导区的数据。
启动引导区实际上是hdd 0 的 head 0 的 cylinder 0 的 sector 1。
所以,在512字节后的512字节数据就是hdd 0 的 head 0 的 cylinder 0 的 sector 2。
main
程序写入了一些简单数据,然后让引导区读取它们。
注意:如果一直报错并且代码没什么问题,请确保qemu的启动磁盘的参数是正确的,并且dl
设置的是正确的。
BIOS 启动引导程序前,会将启动盘的编号写入dl
中,不过当我从hdd启动qemu时遇到了一些问题
这里有两个解决办法:
qemu -fda boot_sect_main.bin
,加入-fda
使dl
的值为0x00
,貌似可以工作。qemu boot_sect_main.bin -boot c
,-boot
会将dl
设置为0x80
,让引导程序读取数据。
Congratulations @geyu! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
You can view your badges on your Steem Board and compare to others on the Steem Ranking
If you no longer want to receive notifications, reply to this comment with the word
STOP
Do not miss the last post from @steemitboard:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!