30天制作操作系统
9unk Lv5

第 0 天:准备阶段

下载工具和原版镜像

原版光盘(含iso文件)

1.png

二进制编辑器

BZ 是一个修改二进制文件的工具。工具可到网上下载,或者使用工具包中 BZ。

4.png

取消只读选框,可对文件进行修改。

工具目录 tolset

  1. nask 编译器

把 nas 文件(作者的汇编源码)转为二进制的,用来把 nas 文件编译成二进制 img 文件。可能用翻译更贴切,不过说成编译也没有什么毛病。

路径:/tolset/z_tools/nask.exe

  1. imgtol.com

将 img 文件刻录到软盘的工具。

用法:imgtol.com w a: helloos.img

路径:/tolset/z_tools/imgtol.com

  1. make.exe

读取并执行 Makefile 的 GNU 系列工具,能读取指定目录下的 Makefile 脚本。其参数与 Makefile 有关。

路径:/tolset/z_tools/make.exe

制作运行 img 操作

  1. 将 nas 源码文件编译成 img 文件
1
nask.exe helloos.nas helloos.img
  1. 将 img 写入软盘 A
1
imgtol.com w a: helloos.img

2.png

需要重启系统,从软盘进入 img 系统

  1. 使用 qemu 启动 img 系统
1
make.exe -C ../z_tools/qemu

3.png

第 1 天:汇编入门

NASM 简介

NASM 全称 The Netwide Assembler,是一款基于 80x86 和 x86-64 平台的汇编语言编译程序,其设计初衷是为了实现编译器程序跨平台和模块化的特性。NASM支持大量的文件格式,包括 Linux,*BSD,a.out,ELF,COFF,Mach−O,Microsoft 16−bit OBJ,Win32 以及Win64,同时也支持简单的二进制文件生成。NASM 是众多汇编器中,免费且比较好用的汇编编译器。

知识学习

5.png

6.png

  1. DB(Define Byte):定义一串字节数据(8位)

例如:

1
2
3
4
5
6
7
8
9

; 定义 6 个字节数据: eb,4e,90
DB 0xeb,0x4e,0x90

; 定义 8 个字节数据:HELLOIPL
DB "HELLOIPL"

; 定义 10 个字节
DB 10

编译器默认会将数据认为是 10 进制,所以需要使用 0x 来表示这个数据是 16 进制的。

  1. DW(伪指令):定义一串字型数据(16 位)

例如:

1
2
; 定义 10 个字,即 20 个字节
DW 10
  1. DD(伪指令):定义一串双字型数据(32 位)

例如:

1
2
; 定义 10 个双字,即 40 个字节
DD 10
  1. RESB(Reserve Byte): “RESB”, “RESW”, “RESD”, “RESQ” and “REST” 被设计用在模块的 BSS 段中:它们声明未初始化的内存空间。每一个带有单个操作数,用来表明字节数,字数,或双字数或其它的需要保留单位。这些指令在初始化空间中默认填充0。
1
2
; 初始化 18 个字节的空间,并填充0。
RESB 18
  1. $:用法表示当前的指令在哪个位置。
1
2
; 我当前的指令在 $ 位置,或者说现在已经使用了 $ 个字节。这个指令意思是:填充 "0x1fe-$" 个值位 "0x00" 的字节。
RESB 0x1fe-$

上面的这些指令都是伪指令。伪指令是只有编译器能执行的指令,编译器会将伪指令编译成数据。

专业术语

  1. 启动区(boot sector)

软盘的第一个扇区称为启动区,因为计算机读写软盘的时候是以 512 字节为一个单位进行读写的,所以软盘中的 512 字节称为一个扇区。

计算机在读写软盘的时候,会先读取第一个扇区,如果第一个扇区的最后两个字节不是 0x55 AA,计算机就会认为这张盘上没有所需的启动程序,就会报一个不能启动的错误。因此第一个扇区也被称为启动区。

  1. IPL(initial program loader)

IPL 启动程序加载器。启动区的 512 字节,肯定是放不下一个正常的操作系统,所以就有了 IPL 这个程序(放到启动区中)来加载操作系统。因此有时启动区也称为 IPL。

第 2 天:汇编语言学习 与 makefile 入门

7.png

8.png

知识点

  1. 8 位寄存器
  • AL:累加寄存器低位
  • CL:计数寄存器低位
  • DL:数据寄存器低位
  • BL:基地址寄存器低位
  • AH:累加寄存器高位
  • CH:计数寄存器高位
  • DH:数据寄存器高位
  • BH:基地址寄存器高位
  1. 16 位寄存器
  • AX:累加寄存器
  • CX:计数寄存器
  • DX:数据寄存器
  • BX:基地址寄存器
  • SP:栈指针寄存器
  • BP:基地址指针寄存器
  • SI:源变地址寄存器
  • DI:目的变地址寄存器

AX、CX、DX、BX 可拆分成 8 位寄存器:AH、AL、BH、BL、CH、CL、DH、DL 。这里的 X 表示扩展(extend)的意思,因为之前是 8 位寄存器,现在扩展到了 16 位寄存器。。

  1. 32 位寄存器
  • EAX:累加寄存器
  • ECX:计数寄存器
  • EDX:数据寄存器
  • EBX:基地址寄存器
  • ESP:栈指针寄存器
  • EBP:基地址指针寄存器
  • ESI:源变地址寄存器
  • EDI:目的变地址寄存器
  1. 段寄存器
  • ES:附加段寄存器
  • CS:代码段寄存器
  • SS:栈段寄存器
  • DS:数据段寄存器
  • FS:没有名称
  • GS:没有名称
  1. 内存地址
    一个程序被加载到内存后,CPU 必须要从内存中读取处理一条一条的指令,程序才得以运行。内存中的指令存放在内存地址中,只有当你告诉 CPU 我要执行哪个内存地址的指令时,CPU 才会正确执行这条指令。内存地址用 [常量] 来表示。

指令

  1. ORG(origin) 指令:修改段内偏移地址。
    工作原理:org 指令本身不能决定程序将要加载的内存的什么位置,它只是告诉编译器,程序在编译好后需要加载到 xxx 地址,所以在编译时帮我调整好数据访问时的地址。

地址调整方法:指定地址+旧的偏移地址

1
2
3
4
5
6

; 在源码中这条指令的偏移地址是 0,也就是旧的偏移地址是 0。
; 当程序加载的时候,偏移地址=0x7c00+0
; 0x7c00~0x7dff :是启动区内容的装载地址

ORG 0x7c00
  1. JMP 指令:无条件跳转指令
    工作原理:jmp 跳转指令根据跳转的距离分为:段内短转移、段内近转移、段间转移。段内转移都是通过当前 IP(偏移地址)的转移位移来进行跳转的。比如,当前 IP=5 ,要跳转的 IP=8,指令跳转的时候就是 jmp 5+3=jmp 8。这里的 3 表示 IP 向后移动 3 个偏移地址。

段内跳转方法:当前IP + 位移

1
2
3
4
5

; "entry:" 的术语叫标号,表示将一段程序做一个标识。
; 当我需要用这段程序的时候,可以使用 "指令 标号"来执行这段程序。

jmp entry
  1. MOV 指令:传送指令
    工作原理:将一个寄存器或者内存单元中的值,传送到另一个寄存器或内存单元中。
1
2
; 将值 0,传送到 ax 寄存器中。原来 ax 寄存器中的值会被覆盖
mov ax,0
  1. ADD 指令:加法指令
    工作原理:将两个数值相加
1
2
; si=si+1
add si,1
  1. CMP 指令:比较指令
1
2
; 将 al 中的值和 0 进行比较
cmp al,0
  1. JE 指令:有条件跳转指令,根据比较结果进行指令跳转。如果比较结果相等,则跳转到制定地址;如果跳转指令不相等,则不跳转,继续执行下一条指令。
1
2
3
4

; 如果 al=0,就跳转到 fin 标号处
cmp al,0
je fin
  1. INT 指令:软件中断指令。int 指令调用的是操作系统提供的子程序或者其他特殊的程序

  2. HLT 指令:使程序停止运行,CPU 处于待机状态,当外部发生变化,比如按键盘、鼠标等,CPU 就会醒过来,继续执行程序。

Makefile 入门

9.png

Makefile语法:

1
2
规则名称 : 执行这个规则所需要的文件(以空格符隔开)
该规则的执行指令

make 语法:

make -r 规则名

优点:

使用灵活方便:当规则执行,找不到需要的文件时。程序会自动需找并执行所需文件的规则。Makefile 可集成多个规则,并按要求执行。

第 3 天:进入 32 位模式并导入 C 语言

制作真正的 IPL

10.png

INT 0x13 中断指向的是磁盘服务程序。

用途:将指定扇区的代码加载到内存指定位置

参数表:

功能
00H 磁盘系统复位
01H 读取磁盘系统状态
02H 读扇区
03H 写扇区
04H 检验扇区
05H 格式化磁道
06H 格式化坏磁道
07H 格式化驱动器
08H 读取驱动器参数
09H 初始化硬盘参数
0AH 读长扇区
0BH 写长扇区
0CH 查寻
0DH 硬盘系统复位
0EH 读扇区缓冲区
0FH 写扇区缓冲区
10H 读取驱动器状态
11H 校准驱动器
12H 控制器 RAM 诊断
13H 控制器驱动诊断
14H 控制器内部诊断
15H 读取磁盘类型
16H 读取磁盘变化状态
17H 设置磁盘类型
18H 设置格式化媒体类型
19H 磁头保护
1AH 格式化 ESDI 驱动器
  • 功能02H

    入口参数:AH=02H

    • AL=扇区数
    • CH=柱面
    • CL=扇区
    • DH=磁头
    • DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘
    • ES:BX=缓冲区地址

出口参数:CF=0 操作成功,AH=00H,AL=传输扇区数,否则,AH=状态码。

12.png

上面的图片具体描述了一个软盘的结构图:柱面(环状),一个软盘有 0~79(共 80)个柱面;每个柱面会被分成 1~18(共 18)个扇区;一个软盘有 0~1(共 2)个磁头用来读取数据。

JC 指令

JC(jump if carry),意思是如果 CF(carry flag)是 1 的话就跳转。CF 是标志寄存器,通常用来做条件跳转或数据比较。

11.png

从上面部分程序中可以看到:程序先读取扇区,如果成功了 CF=0。如果失败了 CF=1,程序会跳到 error 部分继续执行,最后程序跳到 msg 部分继续执行,最后输出 load error 的信息。

简单来说就是:读扇区失败了,屏幕就会输出 load error 的信息。

新增内容解释

1
2
3
4
5
6
7
8
9
10
11
12
13

MOV AX,0x0820 ; 将系统文件放到内存 0x0820~0x083ff
MOV ES,AX
MOV CH,0 ; 定位到柱面 0
MOV DH,0 ; 定位到磁头 0
MOV CL,2 ; 定位到扇区 2(扇区1存放要IPL)

MOV AH,0x02 ; 执行读盘操作
MOV AL,1 ; 读取一个扇区
MOV BX,0
MOV DL,0x00 ; 设置驱动器名称为 A
INT 0x13 ; 调用磁盘 BISO 程序
JC error ; 如果程序执行错误,就跳到 error 标号处

试错

软盘很不可靠,有时会发生不能读取数据的状况,所以要多读取几次,这里尝试读取 5 次,如果还是报错,就会跳转到 error 部分提示错误信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

MOV AX,0x0820
MOV ES,AX
MOV CH,0 ; 定位到柱面0
MOV DH,0 ; 定位到磁头0
MOV CL,2 ; 定位到扇区2
readloop:
MOV SI,0 ; 记录失败次数的寄存器

retry:
MOV AH,0x02 ; AH=0x02:读入磁盘
MOV AL,1 ; 读取一个扇区
MOV BX,0
MOV DL,0x00 ; 设置驱动器名为 A
INT 0x13 ; 调用中断,读取磁盘
JNC fin ; 没有出错的话跳转到 fin
ADD SI,1 ; 出错的话,失败次数加 1
CMP SI,5 ; 比较 SI 与 5 的值
JAE error ; SI >= 5 时,跳转到 error
MOV AH,0x00 ; 磁盘系统复位
MOV DL,0x00 ; 复位的驱动器为 A
INT 0x13 ; 执行中断开始复位
JMP retry ; 复位后再跳转,重新读盘

JNC 是 “Jump if not carry” 的缩写。也就是说进位标志是 0 的话就跳转。因为当读取磁盘报错时中断会将 cf 置为 1。

reference

《30天自制操作系统》笔记1 — 准备阶段

org指令详解

int 0x13 中断理解

BIOS系统服务 —— 直接磁盘服务(int 0x13)

  • 本文标题:30天制作操作系统
  • 本文作者:9unk
  • 创建时间:2020-09-06 09:44:11
  • 本文链接:https://9unkk.github.io/2020/09/06/30-tian-zhi-zuo-cao-zuo-xi-tong/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!