30天自制操作系统:第三天 进入32位模式并导入C语言

今天的内容稍稍有点多,一起看看吧

1.制作真正的IPL

到昨天为止,讲到的启动区虽然也称为IPL(Initial Program Loader,启动程序装载器),但它实质上并没有装载任何程序。
小节中作者给出了将之前的 hello-os 改名为 "纸娃娃操作系统" 的深意:用纸糊起来的,笔者将其理解为目前还有太多功能没有实现,只能是一个看起来像操作系统的东西。

从简单的程序开始,磁盘最初的512字节是启动区,所以要装载下一个512字节的内容。看第一段程序 harib00a:
在这里插入图片描述 JC指令
jump if carry, 如果进位标志(carry flag) 是1的话,指令就执行(完成跳转)

对于指令 INT 0x13,这里是调用BIOS的0x13号函数,函数作用如下图:
BIOS中断调用–百度百科

在这里插入图片描述
↑截自百度百科

图中也说明了当AH = 0x02时为读盘动作,其中0x13号函数的功能如下:

  • AH = 0x02(读盘)
  • AH = 0x03(写盘)
  • AH = 0x04(校验)
  • AH = 0x0c(寻道)
  • AL = 处理对象的扇区数(只能同时处理连续的扇区)
  • CH = 柱面号 & 0xff
  • CL = 扇区号(0 ~ 5位) | (柱面号&0x300) * * 2
  • DH = 磁头号
  • DL = 驱动器号
  • ES:BX = 缓冲地址(校验及寻道时不使用)
  • 返回值
  • FLAGS.CF == 0 没有错误,AH = 0
  • FLAGS.CF == 1 有错误,错误号码存入AH内(与重置(reset)功能一样)

FLAGS.CF是什么意思?光看意思也能够理解个大概:进位标志。
因此这里就明晰了JC指令的使用,如果出错,进位标志CF会被设置为1,进而跳转到error程序段。

在多个软盘驱动器的时候,用磁盘驱动器号来指定从哪个驱动器的软盘上读取数据。这里指定0号。
在这里插入图片描述


软盘的结构示意图:
在这里插入图片描述

一张软盘有80个柱面,2个磁头,18个扇区,一个扇区有512字节,所以一张软盘的容量为:
80 x 2 x 18 x 512 = 1 474 560 Byte = 1 440KB

含有IPL的启动区,位于C0-H0-S1(柱面0,磁头0,扇区1的缩写),下一个扇区是C0-H0-S2,本次要装载的就是这个扇区。

如果没有汇编基础,那么肯定有这样一个疑惑:如果单纯只用寄存器(16位)来表示内存地址,那么最大可以表示的地址为0xFFFFH,一共10000H = 64KB,显然我们对此并不满足,因此有了后来的EBX寄存器(32位),这样能够处理的空间大小变成了2 ^ 32 B= 4 GB,但在此BIOS阶段,用不着这么大的空间,所以只需要使用段寄存器来拓展即可。

段寄存器:通用寄存器 的组合能够访问到最多 FFFF:FFFFH即 (FFFF) * 16 + FFFF H = 1 114 095字节,也就是说可以指定1MB以内的内存了。

这里指定 ES = 0x0820, BX = 0, 软盘的数据会被装载到0x8200 ~ 0x83ff(一共512字节)的地方,使用0x8200的原因是这一块区域没有程序使用,可以将我们的操作系统装载到这片区域,

注:0x7c00 ~ 0x7dff用于启动区,0x7e00以后直到0x9fbff为止的区域都没有特别的用途,操作系统可以随便使用。


双击 !cons_nt.bat 文件 >>> 输入make完成文件编译 >>> 再次输入 make run 运行操作系统
在这里插入图片描述

2.试错

由于是机械型存储介质,发生一些硬件错误也是难免的,有时会发生不能读数据的状况,所以应该设计程序重复读几次盘,实在不行那就只能放弃然后打印错误信息来告知程序员这块出错了。

改良后的harib00b程序:
在这里插入图片描述
JNC指令的含义与JC相反,jump if not carry,意为没有进位则发生跳转。JAE,jump if above or equal,意思是大于或等于时跳转。

程序中给出了较为详细的注释,看几遍应该可以理清楚。

3.读到18扇区

harib00c程序:
在这里插入图片描述
慢慢理解本程序:
首先是JBE指令,jump if below or equal,意思是小于等于则跳转。
程序做的事情很简单,要读下一个扇区,只需要给CL加1,ES加上0x20就行了。CL是扇区号,这里有一点疑惑是在换扇区读盘时只加上了0x20H = 32Byte,前面有提到过,一个扇区的大小为512字节,按理来说每次读完一个扇区偏移地址需要偏移0x200,这里作者给出的是0x20,解释如下:
在这里插入图片描述
笔者认为这块可能是作者写错了(狗头保命,有待求证)


偏移问题 后续 9.15 更
这里是对段寄存器的操作,因此ADD AX, 0x20 体现在ES段寄存器上会扩大16倍(8086CPU中表现为ES * 16 + OFFSET, OFFSET表示偏移地址,因此只需要给段寄存器加上 512 / 16即可,在地址偏移的时候会扩大16倍,笔者汇编语言不够扎实,连这个都忘了。。。


这里为什么要循环呢,笔者初次读到程序也是这么想的,我们已经知道自己的目标是18扇区,那么直接将AL的值设置为17(连续读前2 ~ 18 共 17个扇区)不就好了吗。

确实,这么想是没问题的,但是作者查阅了BIOS读盘函数说明的“补充说明”部分:
在这里插入图片描述
这一部分暂且到这,只需要明白使用AL = 17 一次完成效果也是一样的,但为了章节推进,这里不做详细讲解。

到此,我们已经把磁盘上C0-H0-S2到C0-H0-S18的512 x 17 = 8704字节的内容,装载到了内存的0x8200 ~ 0xa3ff处。


4.读入10个柱面

C0-H0-S18扇区的下一扇区,是磁盘反面的C0-H1-S1,这次也从0xa400读入,按顺序读到C0-H1-S18后,接着读下一个柱面C1-H0-S1,一直读到C9-H1-S18。

harib00d程序:
在这里插入图片描述
JB指令:jump below,意思是如果小于,就跳转。
程序起始位置的EQU指令:相当于C语言的 #define,进行宏定义。
“CYLS EQU 10”意思是:“CYLS = 10”,EQU是equal的缩写(CYLS为cylinders的简写:柱面)。
在这里插入图片描述

5.着手开发操作系统

编写了一个非常小的程序:

fin:
	HLT
	JMP fin

通过作者提供的makefile文件做成了一个img文件,按照指示用二进制编辑器打开 haribote.img:
在这里插入图片描述
↑ 0x002600附近
在这里插入图片描述
↑ 0x004200附近

再打开haribote.sys,可以惊奇地发现:
在这里插入图片描述
与img文件中0x004200附近的内容是一样的,作者是想让我们知道:
一般向一个空软盘保存文件时,

  1. 文件名会写在0x002600以后的地方
  2. 文件的内容会写在0x004200以后的地方

接下来就是将操作系统本身的内容写到 haribote.sys 中,再把它保存到磁盘中,最后从启动区执行这个文件就可以了。

6.从启动区执行操作系统

在这里插入图片描述

7.确认操作系统的执行情况

这里详细介绍了画面模式,先看一下本次的 haribote.nas文件:
在这里插入图片描述
设置了AH = 0,然后调用了0x10中断:

INT 10h
显示服务 - 由BIOS或操作系统设定以供软件调用。AH=00h 设定显示模式;AH=01h 设定游标形态;AH=02h 设置游标位置;AH=03h 获取光标位置与形态;AH=04h 获取光标位置;AH=05h 设置显示页;AH=06h 清除或滚动栏画面(上);AH=07h 清除或滚动栏画面(下);AH=08h 读取游标处字符与属性;AH=09h 更改游标处字符与属性;AH=0Ah 更改游标处字符;AH=0Bh 设定边界颜色;AH=0Eh 在TTY模式下写字符;AH=0Fh 获取当前显示模式;AH=13h 写字符串。

可以发现这里调用了显卡BIOS的函数,这样就可以切换到显示模式了。
在这里插入图片描述

8.32位模式前期准备

在这里插入图片描述
↑为什么要切换到32位模式

一旦使用了32位模式就不可以再调用BIOS:
在这里插入图片描述
从BIOS得到键盘状态:
在这里插入图片描述
设置好了画面模式之后还把与画面相关的信息保存在了内存中(保存起来以后备用)

关于VRAM
在这里插入图片描述

9.开始导入C语言

这一部分均为作者的创意部分,笔者学识浅薄,暂时不能够对此做出深刻的解读,就先放上图书截图。
在这里插入图片描述
**加粗样式**
在这里插入图片描述
在这里插入图片描述
运行效果:一片漆黑
在这里插入图片描述

10.实现HLT

在前面的C语言程序,因为没有办法使用HLT指令(这是汇编指令),所以程序做了大量的循环。这里作者还是用汇编写了一个带HLT指令的程序:
在这里插入图片描述
函数名前面必须加上"_",否则不能与C语言函数链接(C语言中的函数在编译之后会在头部加上该符号)

在C语言中调用这个函数:
在这里插入图片描述

感受

今天的内容相较前两天确实多了很多,虽然写下了这篇博客,但还是有很多地方没有搞懂,需要慢慢理解,不断渗透,截图非常多的原因也很简单,对于很多内容作者已经给出了非常详尽的解析,在这里就不班门弄斧了。
坚持就是胜利,加油!

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
嵌入式C语言自我修养:从芯片、编译器到操作系统这本书是一本关于嵌入式系统开发的教程。它以C语言作为主要的编程语言,通过探索嵌入式系统的各个层面,帮助读者深入理解嵌入式系统的工作原理和开发过程。 首先,这本书深入讲解了嵌入式系统的硬件层面,如芯片的基本概念、架构和工作原理。读者可以了解到不同芯片的特点和应用场景,以及如何选择适合自己需求的芯片。 其次,这本书详细介绍了编译器的原理和使用方法。编译器是将高级语言转化为机器码的重要工具,对于嵌入式系统的开发尤为关键。通过学习编译器的原理和使用方法,读者可以更好地编写高效、可靠的嵌入式程序。 最后,这本书还讨论了嵌入式操作系统的概念和应用。操作系统是管理硬件和软件资源的重要组成部分,对于嵌入式系统的开发非常重要。通过学习嵌入式操作系统的原理和使用方法,读者可以更好地理解和设计嵌入式系统,提高系统的性能和稳定性。 总之,嵌入式C语言自我修养:从芯片、编译器到操作系统这本书是一本全面介绍嵌入式系统开发的教材。它以C语言为基础,从硬件到软件各个层面深入讲解了嵌入式系统的原理和开发过程。对于想要深入了解嵌入式系统并提升开发能力的读者来说,这本书是一本不可多得的学习资料。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nepu_bin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值