30天自制操作系统 pdf_30天自制操作系统-汇编实现初版镜像

上篇文章为了吸引读者(其实也没吸引几个人),先介绍了怎么让自己的系统镜像跑起来

VictorYXL:30天自制操作系统-初版镜像和启动​zhuanlan.zhihu.com

这篇回归正题,介绍这个镜像的实现。

系统引导扇区

一个操作系统,想要被BIOS找到,依靠的就是引导扇区。所以操作系统的第一步就是实现引导扇区,上一篇的镜像其实就是一个引导扇区+空的系统,这篇的重点就是怎么实现引导扇区。

关于汇编语言,书中用的工具是作者自己写的nask工具,我这里还是用通用的nasm给大家介绍。实现引导的code在这里,是我第一版代码的整理,以后的代码也都放在这里。

VictorYXL/MyOSRefine​github.com
a43196eb4b44d7468961c66cf1b887b6.png

2.13.03下测试可以运行,和作者代码差距不大,整个代码包含四段,前三段是512个字节的引导扇区,最后是引导扇区以外的内容。

引导扇区的实现

第一段指明程序加载的位置。

    ORG     0x7c00

ORG命令指明将整个程序加载到内存的0x7c00处,0x7c00是人为规定的地址,不能更改。

第二段标准FAT12格式软盘的结构

为了方便大家理解,我加了一些中文注释

; Format of floppy disk in fat12
    JMP     entry           ; JMP跳转指令,跳转到目标内存地址
                            ; 注意1 这列的跳转指的是计算机执行时候的跳转,而不是汇编的时候跳转
                            ; 注意2 entry表示entry代码段在内存中的起始位置
    DB      0x90            ; DB,DD和DW分别是向镜像中写单字节,双字节和四字节
    DB      "MyOS IPL"      ; 写入启动区名字,必须是8字节
    DW      512             ; 启动扇区大小512字节
    DB      1               ; 1个簇大小是1个扇区
    DW      1               ; FAT从第一个扇区启动
    DB      2               ; FAT个数为2个
    DW      224             ; 根目录大小224
    DW      2880            ; 磁盘包含2880个扇区
    DB      0xf0            ; 磁盘种类必须为0xf0
    DW      9               ; FAT长度为9扇区
    DW      18              ; 1个磁道18个扇区
    DW      2               ; 2个磁头
    DD      0               ; 不使用分区
    DD      2880            ; 同上,磁盘大小
    DB      0, 0, 0x29      ; 无意义,固定这么写
    DD      0xffffffff      ; 无意义,固定这么写
    DB      "MyOS       "   ; 磁盘名,11字节
    DB      "FAT12   "      ; 磁盘格式名,8字节
    RESB    18              ; 空18个字节,填充0x00

这里的代码基本都是写死的,如果对磁头磁道扇区这些概念不熟悉,后面在读写磁盘的时候也会讲到,其他的细节都是基于fat12格式软盘的信息,不必细究。

第三段是核心代码,调用中断显示Hello, world

初始化寄存器

这里先简单介绍下寄存器,对此比较了解的同学可以跳过。寄存器是CPU里的存储电路,存储CPU需要处理的数据和处理的结果,这里使用的是16位寄存器,主要的是以下几个(图来自书本)。

77d2c7e69802ecbc0ea57a214b5b2bbb.png
基础寄存器

0fd85bcb110fb9a68c35d06ed451f1e1.png
段寄存器

其中基础寄存器的前四种可以拆分为高位和低位单独处理,如AX包含AL(低位)和AH(高位)。在32位电脑中用EAX、ECX等表示32为寄存器,我们这里使用16位寄存器就够了,对各个寄存器作用这里只做简单介绍,更多细节不清楚的同学自己百度下吧。

; Boot kernel
entry:
    ; Init register
    MOV     AX, 0
    MOV     SS, AX
    MOV     SP, 0x7c00
    MOV     DS, AX
    MOV     ES, AX
    MOV     SI, msg

SS:SP是指向栈顶的单元,这里通过AX赋值成0:0x7c00,指向我们这段代码本身,并且初始化DS和ES为0,最后把msg的地址赋给SI,和entry一样,msg也是代码段的起始位置,。

接下来进入循环打印的阶段

; Loop: print string with interrupt
print:
    MOV     AL, [SI]
    ADD     SI, 1
    CMP     AL, 0
    JE      end
    MOV     AH, 0x0e
    MOV     BX, 15
    INT     0x10
    JMP     print

这里的核心就是打印SI的内容,[SI]表示SI内存中的值,理解成C语言的指针,SI和[SI]的关系大概就是就是p和*p的关系。把SI指向的字符赋给AX的低位,SI自身再向后移动一位,接下来是判断,CMP和JE实现,如果CMP的两个值相同则跳转到JE的内容,这里就是判断如果读到了0字符则进入end。后面是调用0x10中断显示字符,调用的格式如下:

ae5926fba13c0e76c22ae05d094605ec.png
0x10中断

中断结束再跳回到开头继续打印。

打印结束

; Wait
end:
    HLT
    JMP     end

这段是打印结束的等待,HLT是操作系统交出CPU控制权等待外部信号,我们这里不做任何处理,表示程序结束。

打印内容

; msg
msg:
    DB      0x0a, 0x0a, 0x0a
    DB      "Hello,world!"
    DB      0x0a
    DB      0

这段很好理解, 0x0a是换行,中间是要打印的字符串,最后的0是结束标志,对应前面循环打印的结束。

引导扇区结尾

; Boot end
    TIMES   510 - ($ - $$) DB 0
    DB      0x55, 0xaa

引导区最后一段是引导扇区结束,引导扇区结束一定要以0x55, 0xaa结束,所以前面补充在510字节前补充0。TIMES的重复执行,格式是TIMES 循环次数 执行内容,$ 和 $$分别表示当前地址和段首地址,所以就是到当前职位到510字节前全部写0,最后写上0x55, 0xaa两个字节结束。

第四段是引导扇区意外的内容

; Output outside booting section
    DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
    RESB    4600
    DB      0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
    RESB    1468432

满足2880*512个字节就好,内容不重要,反正也不会被执行。

执行汇编

最后用nasm将这段汇编翻译成机器码。

nasm IPL.nsm -o MyOS.img

如果对汇编不是很熟悉,在写这段汇编的时候,一定要想明白自己是在填充镜像的内容,还是在模拟镜像的运行,否则很容易陷入结构上的混乱,是在不行就多写几次。

写的比较随意,如有疏漏欢迎指正。

TIMES 0x510 - ($ - $$) DB 0

处有失误,应该是

TIMES 510 - ($ - $$) DB 0

感谢 @qiujian2008 的指正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值