helloos3例程
1. 源码解释
新的helloos.nas源码
; hello-os
; TAB=4
ORG 0x7c00 ; 指明程序的装载地址
; 以下记述用于标准的FAT12格式软盘
JMP entry ; 直接跳转到entry(0x7c50)
DB 0x90
DB "HELLOIPL" ; 启动区名称可以是任意字符串(8字节) initial program loader
DW 512 ; 每个扇区(sector)的大小(必须是512个字节)
DB 1 ; 簇(cluster)的大小必须是512个字节
DW 1 ; FAT的起始位置(一般从第一个扇区开始)
DB 2 ; FAT的个数(必须是2)
DW 224 ; 根目录的大小(一般设置为224项)
DW 2880 ; 该磁盘的大小(必须是2880扇区)
DB 0xf0 ; 磁盘的种类(必须是0xf0)
DW 9 ; FAT的长度(必须是9扇区)
DW 18 ; 1个磁道(track)或者完整的一圈有几个扇区(必须是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 ; msg(0x7c74)
putloop:
MOV AL,[SI]
ADD SI,1 ; SI = SI+1
CMP AL,0
JE fin ; 条件跳转(jump if equal),与CMP搭配使用,如果AL==0?,跳转到fin
MOV AH,0x0e ; 显示一个文字
MOV BX,15 ; 指定字符color
INT 0x10 ; 调用显卡BIOS
JMP putloop
fin:
HLT ; CPU suspend(halt),wait command
JMP fin ; 无限loop
msg:
DB 0x0a, 0x0a ; 换行2次
DB "hello, world"
DB 0x0a ; 换行
DB 0
RESB 0x7dfe-$ ; 从此处写0x00直到0x7dfe
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
:用于将接下来的机器指令加载到指定的内存地址。由于制定了起始地址,因此$
也由“本行之前写入多少个字节”变为“引入ORG指定起始地址的条件后,本行之前写入多少个字节”。
我对比了helloos3例程中编译出的helloos.img和helloos2中的,发现二进制级别相同,也不知道内存加载的时候是怎么识别到ORG指定的。
JMP
:跳转,类似于C语言的goto
语句。entry
:标签的声明,用于指定JMP
的目的地。MOV
:赋值。例如,MOV AH,0x0e
就可以认为是AH = 0x0e
。ADD
:加法指令。例如,ADD SI, 1
就可以认为是SI = SI + 1。CMP
:比较指令。例如,CMP a, 3
可以认为是if (a == 3){}
。JE
:条件跳转(jump if equal),一般作为CMP的下一句,因为要以CMP的比较结果作为条件。例如,CMP AL, 0; JE fin
就可以认为是if (AL == 0) {goto fin;}
。INT
:中断指令,暂时认为是一个函数调用。计算机的BIOS中,有一些操作系统开发人员经常会用到的一些基础函数组件。INT就是就可以调用这些函数,例如,INT 0x10
就是调用16号函数,用于控制显卡。HLT
:使CPU停止动作进入待机状态。只要外部发生变化,必然按下键盘或者移动鼠标,CPU就会醒来继续执行程序。本源码中如果使用HLT指令,CPU就会不停的执行JMP指令,这样会使CPU的loading达到100%。
程序主体的功能就是打印"hello, world",将程序主体翻译为C语言伪代码可以写作:
; 程序主体
entry:
AX = 0;
SS = AX;
SP = 0x7c00;
DS = AX;
ES = AX;
SI = msg;
putloop:
AL = BYTE [SI];
SI = SI + 1;
AL = 0x0e
BX = 15;
INT 0x10;
goto putloop;
fin:
HLT;
goto fin;
另外,源码中出现了一些寄存器变量:
AX
:accumulator,累加寄存器。CX
:counter,计数寄存器。DX
:data,数据寄存器。BX
:base,基址寄存器。SP
:stack pointer,栈指针寄存器。BP
:base pointer,基址指针寄存器。SI
:source index,源变址寄存器。DI
:destination index,目的变址寄存器。
这些寄存器是16位寄存器,因此可以存储一个16bits内容。X表示扩展(extend)的意思,就是从8bits扩展为16bits。有时使用不同的寄存器可以实现不同的功能,但是对编译完的二进制文件来说,简洁程度却是不同的。例如:
ADD CX, 0x1234
编译为81 C1 34 12
(四字节),而ADD AX, 0x1234
编译为05 34 12
(三字节)。
CPU中还有8个8bits寄存器:
AL
:累加寄存器低位(accumulator low)CL
:计数寄存器低位(counter low)DL
:数据寄存器低位(data low)BL
:基址寄存器低位(base low)AH
:累加寄存器高位(accumulator high)CH
:计数寄存器高位(counter high)DH
:数据寄存器高位(data high)BH
:基址寄存器高位(base high)
对于32bits的计算机,也有32位寄存器EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI
,其中的E也是来源于extend。虽然EAX
是32bits寄存器,但是它的低16位就是AX
,因此可以直接通过访问AX
获取EAX
的低16bits,如果期望获取高16bits,就需要使用移位指令把高16bits移到低16bits才能访问。
还有一些段寄存器(segment register),这些都是16bits寄存器。
ES
:附加段寄存器(extra segment)CS
:代码段寄存器(code segment)SS
:栈段寄存器(stack segment)DS
:数据段寄存器(data segment)FS
:附加段寄存器(segment part1)GS
:附加段寄存器(segment part2)
MOV AL,[SI]
中的方括号,甚至可以参照C语言方括号理解,就是取出括号中对应地址的内容。另一个例子,MOV BYTE [678], 123
,首先根据BYTE关键字认为该句指令操作的是一个8bits空间,然后可以理解为[678]=123
,即将123这个8bits数放置到地址678位置。
dataSize [addr]
是一个常用组合。dataSize使用BYTE,WORD或DWORD分别指定1字节,2字节或4字节;addr可以使用数字也可以使用BX,BP,SI,DI寄存器。例如,期望将DX里的内容赋值给AL,可以写作:
MOV BX, DX
MOV AL, BYTE [BX]
2. 0x7c00地址
0x00007c00 ~ 0x00007dff
共510字节,是启动区装载的内存地址。
这种内存的分布,应该就是最初设计操作系统的人定的规矩。此外,0x0地址是BIOS程序用来实现不同功能的地方;0xf0000地址附近存放着BIOS程序本身。
helloos4例程
当前仅制作512字节的启动区,把helloos.nas源码文件的后半段都删掉,只需要最初的512字节,并重命名为ipl.nas,即该源文件删掉“; 启动区以外部分的输出”。并修改编译脚本asm.bat为:
# asm.bat
..\z_tools\nask.exe ipl.nas ipl.bin ipl.lst
在输出ipl.bin的同时输出了ipl.lst,这是个列表文件(本质是一个文本文件),用来查看每个指令是如何翻译成机器指令的。本次的主输出文件为512字节,与之对应,列表文件也比较小。
此外,增加一个makeing.bat文件。这个脚本的功能是将所输出的ipl.bin写入磁盘映像img的开头,组成一个新的完整的磁盘映像文件helloos.img。
# makeing.bat
..\z_tools\edimg.exe imgin:../z_tools/fdimg0at.tek wbinimg src:ipl.bin len:512 from:0 to:0 imgout:helloos.img
helloos5例程
编写简单的makefile,自动化整个编译运行过程。
make.exe 会检查目标产物文件是否存在,还会判断源文件的修改日期以便决定是否需要重新编译产物。
首先创建一个初版的Makefile文件,不需要加扩展名后缀:
# 文件生成规则
ipl.bin : ipl.nas Makefile
../z_tools/nask.exe ipl.nas ipl.bin ipl.lst
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
在该文件中,#表示该行是一个注释行。
“ipl.bin : ipl.nas Makefile”表示,如果期望制作一个ipl.bin文件,就需要先检查一下ipl.nas和Makefile是否准备好,如果存在,make工具就会自动执行下一行命令。helloos.img同理,其中“\”是换行符号。
然后编辑一个make.bat脚本用来调用make.exe程序处理Makefile文件:
# make.bat
..\z_tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
make自动化脚本编辑完成后,就可以通过make -r ipl.bin
和make -r helloos.img
分别生成ipl.bin文件和helloos.img文件。
为了更加自动化,将下面内容添加到Makefile文件中:
default :
../z_tools/make.exe img
######################################################################
asm :
../z_tools/make.exe -r ipl.bin
img :
../z_tools/make.exe -r helloos.img
run :
../z_tools/make.exe img
copy helloos.img ..\z_tools\qemu\fdimage0.bin
../z_tools/make.exe -C ../z_tools/qemu
install :
../z_tools/make.exe img
../z_tools/imgtol.com w a: helloos.img
clean :
-del ipl.bin
-del ipl.lst
src_only :
../z_tools/make.exe clean
-del helloos.img
可以简化命令输入。例如,make img
的本质就是直接调用../z_tools/make.exe -r helloos.img
命令,因此会生成helloos.img文件。此外,default是Makefile中的一个关键字,意思是如果只执行make命令,其后不跟任何选项参数,则默认执行这一句,所以在helloos5文件夹下,cmd执行make
效果与执行make img
相同。