《30天自制操作系统》笔记-day2

本文最后更新于 2026年7月4日 凌晨

Day2

Day2,这一章主要讲述了寄存器的基础知识,汇编进阶指令,makefile的使用;并完成了一个512字节的启动程序

寄存器

寄存器是CPU中的存储电路,可以理解为物理上的变量

16位寄存器

16bit寄存器 含义
AX 累加寄存器
CX 计数寄存器
DX 数据寄存器
BX 基址寄存器
SP 栈指针寄存器
BP 基址指针寄存器
SI 源变址寄存器
DI 目的变址寄存器

每个寄存器有各自的优点,编译成机器语言后程序的简洁程度不同,比如AX寄存器有一组专属的短编码
ADD为例:

  1. AX寄存器:编译后的机器语言只需要一个操作码字节不需要指定目标寄存器字节,就是AX寄存器
  2. 其他寄存器:编译后的机器语言除了操作码字节还有一个目标寄存器字节
1
2
3
4
5
6
7
ADD AX 0x1234
;编译: 0x05 0x34 0x12
; 0x05是AX寄存器特有ADD的操作码

ADD CX 0x1234
;编译: 0x81 0xC1 0x34 0x12
; 0x81是其他寄存器所有ADD的操作码

ModR/M 字节
规则:
0xC1为例将其拆分为二进制:11000001
11:Mod(寻址方式),11两个操作都是寄存器,00/01/10都与内存有关
000:reg,扩展操作码,配合0x81使用也就是ADD = 000
001:R/M,目标寄存器, 001代表CX寄存器
0xC1翻译过来就是寄存器模式+ADD运算+目标寄存器为CX

寄存器的扩展

上述寄存器是16位寄存器,除指针寄存器以外都是8位寄存器拼接而来

16bit 8bit 含义
AX AH 累加寄存器高8位
AL 累加寄存器低8位
CX CH 计数寄存器高8位
CL 计数寄存器低8位
BX BH 基址寄存器高8位
BL 基址寄存器低8位
DX DH 数据寄存器高8位
DL 数据寄存器低8位

指针寄存器不可拆分:Intel在设计8086时就没有给SP/BP/SI/DI提供8位拆分访问,属于硬件设计选择

32位寄存器

32位寄存器与上述类似,由16位寄存器拼接而来,但是又稍有区别

  1. 低16位名称是原16位寄存器名称
  2. 高16位无名
32bit 16bit 含义
EAX 无名 累加寄存器高16位
AX 累加寄存器低16位
ECX 无名 计数寄存器高16位
CX 计数寄存器低16位
EDX 无名 数据寄存器高16位
DX 数据寄存器低16位
EBX 无名 基址寄存器高16位
BX 基址寄存器低16位
ESP 无名 栈指针寄存器高16位
SP 栈指针寄存器低16位
EBP 无名 基址指针寄存器高16位
BP 基址指针寄存器低16位
ESI 无名 源变址寄存器高16位
SI 源变址寄存器低16位
EDI 无名 目的变址寄存器高16位
DI 目的变址寄存器低16位

汇编语言

ORG

开始执行的时候,把机器语言装载到内存的哪个地址

同时[[Day1]]中的$意思也随之改变,不是文件的第几个字节,代表将要读入的内存地址

规定:0x00007c00-0x00007dff :启动区内容的装载地址
对于启动区程序,ORG必须指定为0x7c00

JMP

跳转,类似于c语言中的goto语句

MOV

功能上来说类似于赋值

1
MOV AX,0 ; 相当于AX=0

标号

entry:msg:这些都是标号,与JMP搭配使用,可以跳转到指定的目的地

  • 每一个标号都是一个对应内存地址,这个地址是基于ORG计算出来的
  • 相当于是一个别名,用别名代替内存地址方便编码

在本程序中entry对应地址0x7c50

1
2
3
JMP entry
JMP 0x7c50
;两段代码意义一样

[]符号

  • 方括号代表内存地址
  • 可以通过BYTE,WORD,DWORD来控制字节长度

例:

1
MOV BYTE [678],123 
  • BYTE:操作一个字节

    • 如果BYTE换成WORD,就会同时操作两个字节,123转换为16位二进制,低8位存储在[678]地址,高8位存储在[679]地址
  • 678:内存地址

    • 这里涉及到一个地址的解引用,类似于C语言的指针
      • *p类似于[678]
      • p类似于678
    • 内存地址可以是常数,可以是寄存器;比如SI寄存器的是678(一个内存地址),[SI]就是取地址678处存储的值(假设为 123)
  • 123:存储在内存地址里的值

只有BX/BP/SI/DI才能做指向内存地址,并且完成[]的解引用
其他寄存器只能用作存储地址数值,但是不能解引用

再来看程序中的代码,对SI进行解引用,也就是获取对应地址存储的值,将其一字节截取并赋值给AL

1
MOV AL,[SI]

ADD

加法指令
SI+1,这里是对寄存器本身的值做加法;不要被上一小节影响,这里是单纯的寄存器操作,不涉及内存地址

1
ADD SI,1

CMP

比较指令
相当于把AL寄存器中的数值与后面的常数做比较

1
CMP AL,0

JE

跳转指令的一种
如果相等就做跳转,不等则不跳转

1
JE fin

CMPJE相结合就相当于是一个C语言的if语句

1
2
3
4
5
CMP AL,0
JE fin

; if(AL == 0){goto fin}

INT

软件中断指令
程序员主动写指令,手动触发中断,用来调用 BIOS 内置函数,相当于一套底层系统调用接口,和嵌入式硬件中断不是一回事,只是底层机制同源。
参考网站

1
2
3
MOV AH,0x0e ;显示一个文字
MOV BX,10 ;指定字符颜色
INT 0x10 ;调用显卡BIOS

AH设置为0x0e,显示字符

BX指定了显示字符的颜色
INT调用BIOS中的函数,实现字符显示

HLT

HLT是让CPU停止动作的指令,不过并不是彻底地停止(如果要彻底停止CPU的动作,只能切断电源),而是让CPU进入待机状态。

启动区程序

ipl.nas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
; hello-os
; TAB=4

ORG 0x7c00 ; 指明程序装载地址

; FAT12格式软盘的专用代码

JMP entry
DB 0x90
DB "HELLOIPL" ; 启动区名称(任意字符串)
DW 512 ; 每个扇区大小(必须是512byte)
DB 1 ; 簇的大小(必须是一个扇区)
DW 1 ; FAT的起始位置
DB 2 ; FAT的个数
DW 224 ; 根目录的大小(一般为224项)
DW 2880 ; 磁盘大小(必须是2880扇区)
DB 0xF0 ; 磁盘种类(必须是F0)
DW 9 ; FAT长度(必须是9扇区)
DW 18 ; 1个磁道有多少个扇区(必须是18)
DW 2 ; 磁头数(必须是2)
DD 0 ; 不使用分区,必须是0
DD 2880 ; 重写一次磁盘大小
DB 0,0,0x29 ; 意义不明,固定
DD 0xFFFFFFFF ; (可能是)卷标号码
DB "HELLO-OS "; 磁盘名称(11byte)
DB "FAT12 " ; 磁盘格式名称(8byte)
RESB 18 ; 空出18个字节

; 程序主体

entry:
MOV AX,0 ;初始化寄存器
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX

MOV SI,msg

putloop:
MOV AL,[SI]
ADD SI,1 ;给SI+1
CMP AL,0
JE fin
MOV AH,0x0e ;显示一个文字
MOV BX,15 ;指定字符颜色
INT 0x10 ;调用显卡BIOS
JMP putloop

fin:
HLT ;让CPU停止,等待指令
JMP fin ;无限循环

msg:
DB 0x0a, 0x0a
DB  "This is boot erae"
DB 0x0a
DB 0x00

RESB 0x7DFE-$ ; 补0到0x7DFE = 0x7C00(H)+510(D),最后两个字节是启动标签

DB 0x55, 0xaa ;启动标签

程序的流程

  1. 指定输出字符串的首地址
  2. 在循环里面循环输出每个字符
  3. 直到指针指向的内容为0
  4. HLT指令CPU停止

启动区

当前程序将作为一个512字节的启动区

改写asm.bat

1
..\z_tools\nask.exe ipl.nas ipl.bin

输出的ipl.bin只有512字节

编辑makeimg.bat

1
..\z_tools\edimg.exe   imgin:../z_tools/fdimg0at.tek   wbinimg src:ipl.bin len:512 from:0 to:0   imgout:helloos.img

输出镜像后run即可

Makefile

上述使用bat脚本过于麻烦,可以使用Makefile文件整合起来,把所有操作整合到一个文件,一气呵成完成镜像制作

1
2
3
4
5
6
7
# 文件生成规则

ipl.bin : ipl.nas Makefile
../z_tools/nask.exe ipl.nas ipl.bin ipl.list

helloos.img : ipl.bin Makefile
../z_tools/edimg.exe imgin:../z_tools/fdimg0at.tek wbinimg src:ipl.bin len:512 from:0 to:0 imgout:helloos.img

使用

作者准备了一个make脚本,tolset\z_new_w\make.bat

复制到makefile同级文件夹下

执行

1
2
./make -r ipl.bin //生成bin文件
./make -r helloos.img //生成img文件

run启动与bat生成的img效果一样

但是现在还是需要加参数-r [文件名]
可以通过在Makefile里面添加命令来实现更简洁的编译

在之前的文件追加即可,因为我没有软盘所以就没有写install

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
img:
../z_tools/make.exe -r helloos.img

asm:
../z_tools/make.exe -r ipl.bin

run:
../z_tools/make.exe img
copy helloos.img ..\z_tools\qemu\fdimage0.bin
..\z_tools\make.exe -C ../z_tools/qemu
clean:
-del ipl.bin
-del ipl.list

src_only:
../z_tools/make.exe clean
-del helloos.img

运行

1
2
3
4
5
./make asm //生成bin
./make img //生成img
./make run //运行img镜像
./make clean //只保留源码和镜像
./make src_only //只保留源码

问题

  1. 为什么Day1RESB 0x1FE-$RESB 0x7DFE-$不一样但是在十六进制编辑器里面启动标签0x55 0xAA的偏移量都一样
    Day1 无 ORG 时 $=文件偏移
    Day2有ORG 0x7c00$=内存地址,两者差了一个0x7c00的偏移,所以 0x1FE - 文件偏移0x7DFE - 内存地址 结果相同。

小结

Day2的核心收获

  1. 寄存器的基础知识
  2. 汇编语言的进阶指令MOV/JMP/ADD等等
  3. Makefile的基础使用方法
  4. Day1与Day2的$符的区别

《30天自制操作系统》笔记-day2
https://www.ming-ice-tea.top/2026/07/03/《30天自制操作系统》笔记-day2/
作者
Ming
发布于
2026年7月3日
许可协议