30天自制操作系统——第01天(上) 汇编语言
第01天(上) 汇编语言
对应书28~40页
今天先开始学习(复习)一些汇编知识,从helloos3开始讲起。😊
-
helloos3
helloos3和helloos2功能上没有变化,只是在昨天的基础上做了一些修改,主要改了程序主体部分,接下来我们来解读这段代码。
pigz.nas全部代码如下:
; pigz ; TAB=4 ORG 0x7c00 ; 指明程序的装载地址 ; 以下为标准FAT12格式软盘专用代码 JMP entry DB 0x90 DB "HELLOIPL" ; 启动区的名字可以是任意字符串(8字节) DW 512 ; 定义每个扇区的大小(必须为512字节) DB 1 ; 定义簇的大小(必须为1个扇区) DW 1 ; FAT的起始位置(一般从第一个扇区开始) DB 2 ; FAT的个数 DW 224 ; 根目录的大小(一般设成224项) DW 2880 ; 该磁盘的大小(必须是2880扇区) DB 0xf0 ; 磁盘的种类(必须是0xf0) 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 " ; 磁盘的名称(11字节) DB "FAT12 " ; 磁盘格式名称(8字节) 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 ; 直到读到第56行写入的"0"为止 JE fin MOV AH,0x0e ; 显示文字颜色 MOV BX,15 ; 指定字符颜色 INT 0x10 ; 调用显卡BIOS JMP putloop fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 msg: DB 0x0a, 0x0a ; 换行2次 DB "hello, world" DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00,直到0xdfe DB 0x55, 0xaa ; 以下是启动区以外部分的输出 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 RESB 4600 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 RESB 1469432首先我们可以注意到第一句代码
ORG 0x7c00是新增的,它指明了程序装载的地址。也就是说把下面DB、DW要写的内容写到内存0x7c00后面的地方。如果想知道为什么是0x7c00呢,可以参阅为什么主引导记录的内存地址是0x7C00?这篇文章。可能有朋友会问,helloos2里面没有这句,那程序被装载到哪里了呢?笔者猜想也是0x7c00这个位置,0x7c00应该默认装载启动程序的地址,其实不需我们设置。我们可以看到第8行
JMP entry这句汇编取代了helloos2里的DB 0xeb, 0x4e, 0x90,这是为什么呢?我们可以先去看看第00天的随笔中有一张表格,其中第一行如下所示。名称 开始字节 长度 内容 参考值 BS_jmpBOOT 0 3 一个短跳转指令 jmp short LABEL_STARTnop FAT12格式要求磁盘第一个扇区第一个字节开始的三个字节为“一个短跳转指令”,因此我们既可以把"JMP entry"写在这,也可以通过DB指令直接把对应的机器代码"0xeb, 0x4e, 0x90"写进内存,效果是一样的。接下来一直到29行都没有变化,因此我们开始看程序主体。
在"entry"这段,我们先初始化寄存器并加载显示内容:
entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX MOV ES,AX MOV SI,msg ; 加载显示内容CPU中有很多寄存器,但是这次我们只要用到SS,SP,DS,ES,因此只对它们初始化就够了。其中,SS为堆栈段寄存器;SP为堆栈指针寄存器;DS为数据段寄存器;ES为附加段寄存器。但是为什么要将SP初始化为0x7c00(与程序装载地址一样),笔者还不得而知。最后我们通过
MOV SI,msg加载显示的内容,就是将"msg"处的地址赋给SI寄存器,为我们显示字符做好准备。那么"msg"标号后面是什么呢,我们来看看这段代码:
msg: DB 0x0a, 0x0a ; 换行2次 DB "hello, world" DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00,直到0xdfe DB 0x55, 0xaa为了不紧贴着屏幕边缘显示,我们先做两个换行,就是向内存写入两个换行的ASCII码"0x0a"(查表可知),然后我们写入我们想要的内容"hello, world",紧接着再做一次换行,最后我们写一个0。由于第一个扇区还有好多剩余,因此我们需要在空余地部分用
RESB 0x7dfe-$填上"0"。至于这个美元符号"$"是什么,书作者在书里作了很详细的解释,这里就不赘述了。我们再看看标号为"putloop"的这一部分。
putloop: MOV AL,[SI] ADD SI,1 ; 给SI加1 CMP AL,0 ; 直到读到"0"为止 JE fin MOV AH,0x0e ; 显示文字颜色 MOV BX,15 ; 指定字符颜色 INT 0x10 ; 调用显卡BIOS JMP putloop哈哈,"entry"部分的最后一句终于在这里派上了用场,当程序执行到"putloop",SI寄存器还保存着"msg"的地址,我们通过间接寻址把"msg"的第一个字符(也就是空格的ASCII码)拷贝到AL寄存器中,然后让SI寄存器中的地址加1,如果拷贝到AL寄存器的数为0,就说明显示的文字结束了,跳转到fin结束程序,要是AL寄存器里的数不是0,那就调用BIOS进行显示。
"putloop"的6~7行是设置显示参数的过程,第8行是调用显卡BIOS。这个
INT 0x10指的是调用编号为0x10的BIOS程序,当我们执行这行命令后,BIOS就自动读取AH, AL, BH等寄存器的参数并进行显示了。INT 0x10介绍如下
INT 0x10功能0x13
- 描述:
以电传打字机的方式显示字符串
- 接受参数:
AH 0x13
AL 显示模式
BH 视频页
BL 属性值(如果AL=0x00或0x01)
CX 字符串的长度
DH,DL 屏幕上显示起始位置的行、列值
ES:BP 字符串的段:偏移地址
- 返回值:
无
- 显示模式(AL):
0x00:字符串只包含字符码,显示之后不更新光标位置,属性值在BL中
0x01:字符串只包含字符码,显示之后更新光标位置,属性值在BL中
0x02:字符串包含字符码及属性值,显示之后不更新光标位置
0x03:字符串包含字符码及属性值,显示之后更新光标位置
至于下面这段代码是做什么的,笔者也不是很清楚。😅
; 以下是启动区以外部分的输出
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 RESB 4600 DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 RESB 1469432还记得helloos2里面的主体是什么样的吗?当时的程序主体是往内存中写一堆看不懂的十六进制数,其实这就是这次程序主体部分经编译器编译成的机器码。
; 程序主体 DB 0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c DB 0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a DB 0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09 DB 0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb DB 0xee, 0xf4, 0xeb,0xfd今天汇编语言部分就到此结束了噢,预告一下,下一篇是Makefile的制作和使用,这是一个非常重要的工具,学会使用它将大有裨益。🤭

30天自制操作系统——第01天(上) 汇编语言
浙公网安备 33010602011771号