3)搬运操作系统
搬运操作系统
我们已经把启动区(MBR)从硬盘中先拷贝到0x7c00,再拷贝到0x90000,并跳转到go处了,接下来的代码为:
1 |
|
寄存器初始化
代码中全是mov
操作,即给寄存器赋值,经过这一系列的赋值操作,完成了相关寄存器的初始化,即把cs寄存器的值复制给ds、es、ss寄存器,sp寄存器的值为0xFF00。
再来看一下寄存器的图:
所以cs寄存器是多少呢?按照我们的理解cs是代码段寄存器,现在应该是最新的代码存放地址即0x9000,cs是怎么变成这个值的呢?
在跳转时执行了jmpi go,0x9000
,拆开的写法就是mov cs,#0x9000
和 jmpi go,cs
所以现在cs、ds、es、ss都为0x9000,sp为0xFF00,栈顶的地址为sp:ss
即0x9FF00
CPU 访问内存的三种途径
CPU 访问内存有三种途径——访问代码的 cs:ip,访问数据的 ds:XXX,以及访问栈的 ss:sp。其中, cs 作为访问指令的代码段寄存器,被赋值为了 0x9000。ds 作为访问数据的数据段寄存器,也被赋值为了 0x9000。ss 和 sp 作为栈段寄存器和栈指针寄存器,分别被赋值为了 0x9000 和 0xFF00,由此计算出栈顶地址 ss:sp 为 0x9FF00,之后的压栈和出栈操作就以这个栈顶地址为基准。
移动操作系统到内存
1 |
|
前面的mov是给寄存器赋值,int在汇编中代表中断,int 0x13
代表触发0x13号中断。
中断是打断CPU正在执行的指令流,转而去执行中断处理程序的过程,其中dx、cx、bx、ax寄存器的值相当于这个中断处理程序的参数,叫寄存器传参,可以简单的看作执行一个中断函数。0x13这个中断BIOS已经提前写好,相当于是读取磁盘的函数。
本段代码的注释已经写的很明确了,直接说最终的作用吧——从硬盘的第 2 个扇区开始,把数据加载到内存 0x90200 处,共加载 4 个扇区。
再往后的 jnc 和 jmp(j),表示成功和失败分别跳转到哪个标签处,相当于我们高级语言的 if else。,如果失败就重试,如果成功就跳转到标签ok_load_setup处执行
1 |
|
这部分贴了闪客提炼的代码,核心功能是把从硬盘第 6 个扇区开始往后的 240 个扇区,加载到内存 0x10000 处,和之前的从硬盘复制到内存是一个道理。
至此,整个操作系统的全部代码,就已经全部从硬盘加载到内存中了。然后这些代码,又通过一个熟悉的段间跳转指令 jmpi 0,0x9020,跳转到 0x90200 处,就是硬盘第二个扇区开始处的内容。
编译过程
整个编译过程,就是通过 Makefile 和 build.c 配合完成的:
bootsect.s 编译成 bootsect 放在硬盘的 1 扇区,setup.s 编译成 setup 放在硬盘的 2~5 扇区,剩下的全部代码(head.s 作为开头,与各种 .c 和其他 .s 等文件一起)编译并链接成 system,放在硬盘的随后 240 个扇区。
所以 0x90200 处的代码,也就是我们即将跳转到的内存地址处的代码,就是从硬盘第二个扇区加载过来的。而第二个扇区的最开始处,也就是 setup 二进制文件的内容,是由 setup.s 源代码文件编译后形成的。