进一步修改helloos.nas:
; hello-os
; TAB=4
ORG 0x7c00 ; 指明程序的装载地址
; 以下的记述用于标准FAT12格式的软盘
JMP entry
DB 0x90
DB "HELLOIPL" ; 启动区的名称可以是任意的字符串
DW 512 ; 每个扇区(sector)的大小(必须为512字节)
DB 1 ; 簇(cluster)的大小(必须为1个扇区)
DW 1 ; FAT12的起始位置(一般从第一个扇区开始)
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 " ; 磁盘格式名称
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 ; 换行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指令——来源于英文“origin”,程序要从指定的这个地址开始,也就是要把程序装载到内存中的指定地址,这里指定的地址是0x7c00。
有了这条指令的话,美元符($)的含义 也随之变化,它不再是指输出文件的第几个字节,而是代表将要读入的内存地址。
- JMP指令——相当于C语言的goto语句,来源于英文的jump,意思是“跳转”。
- “entry:”——标签的声明,用于指定JMP指令的跳转目的地等。
- MOV指令——相当于赋值,MOV的源和目的可以是寄存器、常数、内存地址,用方括号([ ])来表示内存地址,MOV指令有一个规则:源数据和目的数据位数必须相同。所以在源数据中可以省略数据大小(BYTE)等的描述。
CPU里有一种名为寄存器的存储电路,在机器语言中就相当于变量的功能。
- AX——accumulator,累加寄存器
- CX——counter,计数寄存器
- DX——data,数据寄存器
- BX——base,基址寄存器
- SP——stack pointer,栈指针寄存器
- BP——base pointer,基址指针寄存器
- SI——source index,源变址寄存器
- DI——destination index,目的变址寄存器
说明:
- 这些寄存器都是16位寄存器,可以存储16位的二进制数,不管使用哪一个,差不多都能进行同样的计算,但如果都用AX来进行各种运算的话,程序就可以写得很简洁。“简洁”是指“用机器语言写程序”的情况,从汇编语言的源代码上是看不到这些区别的。
- 其中"X"表示扩展(extend)的意思,之所以说是扩展,是因为之前寄存器都是8位的,现在变成了16位。另外这几个寄存器不是按照字母排序的,是按照机器语言中寄存器的编号顺序排序的。
- 这8个寄存器全部只有16个字节(16bit*8/8),也就是说CPU最多只能存储16个字节
另外,CPU中还有8个8位寄存器:
- AL——累加寄存器低位(accumulator low)
- CL——计数寄存器低位(counter low)
- DL——数据寄存器低位(data low)
- BL——基址寄存器低位(base low)
- AH——累加寄存器高位(accumulator high)
- CH——计数寄存器高位(counter high)
- DH——数据寄存器高位(data high)
- BH——基址寄存器高位(base high)
名字看起来和上面的16位寄存器有点像,这是因为:AX的0~7低八位称为AL;AX的8-15高8位称为AH.
BP、SP、SI、DI不能分为H和L,如果需要取高8位或者低8位,需要用“MOV AX,SI”,然后再用AH、AL来取值
-----------------------------------------------------------------------------------------------------------------------
32位寄存器有:EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI,它们和前面的16位寄存器共用,32位寄存器中低16位是AX,高16位没有名字,也没有寄存器编号,如果要取高16位,需要用移位命令获取
-----------------------------------------------------------------------------------------------------------------------
段寄存器(segment register)–16位寄存器
- ES——附加段寄存器(extra segment)
- CS——代码段寄存器(code segment)
- SS——栈段寄存器(stack segment)
- DS——数据段寄存器(data segment)
- FS——没有名称(segment part 2)
- GS——没有名称(segment part 3)
在汇编语言中,所有标号都是单纯的数字,每个标号对应的数字都是由汇编语言编译器根据ORG指令计算出来的,编译器计算出的标号的地方对应的内存地址就是那个标号的值。
内存到底是什么?
简单用一句话概括就是超大规模存储单元住宅区。前面通过对寄存器的说明了解到CPU的存储能力很差(32位CPU所有的寄存器如果用来存储,只能存储8[通用寄存器个数]*4[每个寄存器字节数]+6[段寄存器个数]*2[每个寄存器字节数] =44字节),如果想让CPU处理大量信息,就必须给它准备一套用于存储的电路。
------------------------------------------------------------------------------------------------------------------
对CPU来说,内存实际是外部存储器,程序本身保存在内存里,CPU在执行机器语言时,从内存一个命令一个命令地读取程序,顺序执行。
内存虽然如此重要,但是它离CPU的位置却相当远,当CPU向内存请求数据或者输出数据时,内存需要花费很长时间才能实现CPU的要求,所以CPU访问内存的速度比访问寄存器的速度慢很多倍
BYTE、WORD、DWORD是汇编的保留字,用法如下:
MOV BYTE [678],123
BYTE指明操作8位,用内存的“678”号地址保持“123”这个数值;这里“678”是一大串的开(ON)或者关(OFF)的电信号,当内存收到这一串电信号后,电路中某8个存储单元就会响应,这8个存储单元会记住代表"123"的开(ON)或关(OFF)的电信号。
MOV WORD [678],123
WORD指明操作16位,此时内存地址678号和679号都会做出反应,123被解释成16位数值,低8位保存在678号,高8位保存在679号上
数据大小[地址]是一个固定组合,如果知道数据大小为BYTE,那么使用的单元就只是地址所指定的字节;如果为WORD,则相邻的一个字节也会成为这个指令的操作对象;如果为DWORD,则与WORD相邻的两个字节也会成为这个指令的操作对象(共4个字节)。(这里相邻指的是地址增加方向的相邻)
--------------------------------------------------------------------------------------------------------------------------
内存地址的指的方法:常数和寄存器。比如“BYTE [SI]”、“WORD [BX]”,如果SI中保持的是978,那么“BYTE [SI]”解释为指定地址为978的内存。
注意:
虽然寄存器可以用来指定内存地址,但可用作此用途的寄存器非常有限,只有BX、BP、SI、DI。剩下的AX、CX、DX、SP不能用来指定内存地址,这是因为CPU没有处理这种指令的电路,或者没有表示这种处理的机器语言。
- ADD——加法指令,ADD SI,1 对应C语言中的SI=SI+1
- CMP——比较指令
JE——条件跳转指令之一,如果比较结果相等就跳转,不等就不跳转,继续执行下一条指令。这条指令源于英文“jump if equal”。
条件跳转指令是指根据比较结果决定跳转或者不跳转。
因此,如下指令
CMP AL,0
JE fin
相当于
if(AL==0){goto fin;}
- INT——软中断指令,源自英文“Interrupt”,意思是中途打断
BIOS(basic input output
system 基本输入输出系统程序)程序在电脑出厂时就存储在电脑主板的ROM(Read Olny memory)单元里,厂家会在BIOS中预先写入操作系统开发人员经常会用到的函数,而INT就是用来调用这些函数的指令。INT后面使用不同的数字可以调用不同的函数,本次调用0x10(16)号函数,它的功能是控制显卡,具体用法如下:
显示一个字符:
- AH=0x0e;
- AL=character code;
- BH=0;
- BL=color code;
- 返回值:无
注:beep、退格(back space)、CR、LF都会被当做控制字符处理
- HLT——让CPU进去待机状态,源自“halt”,停止的意思
至此,上面的汇编程序可以改成C语言:
entry:
AX = 0;
SS = AX;
SP = 0x7c00;
DS = AX;
ES = AX;
SI = msg;
putloop:
AL = BYTE [SI];
SI = SI + 1;
if (AL == 0) { goto fin; }
AH = 0x0e;
BX = 15;
INT 0x10;
goto putloop;
fin:
HLT;
goto fin;
至此,可以打印出“hellow world”。
为什么程序的装载地址是0x7c00呢?
电脑中的内存一般都非常大(64MB,512MB,…),但这些内存并不是想怎么用就怎么用的,0号地址处,是BIOS用来实现各种不同功能的地方,0xf0000存放这BIOS程序本身,启动区内容的装载地址为:0x00007c00~0x00007dff。0x7c00这个值是由当时设计的人决定的。
制作512字节的启动区:
- 修改asm.bat,输出文件名修改为ipl.bin,输出文件列表ipl.lst(可以确认每个指令是怎么翻译成机器语言的)
..\z_tools\nask.exe ipl.nas ipl.bin ipl.lst
- 增加makeing.bat,以ipl.bin为基础,制作磁盘映像文件helloos.img。
..\z_tools\edimg.exe imgin:../z_tools/fdimg0at.tek wbinimg src:ipl.bin len:512 from:0 to:0 imgout:helloos.img
编译测试:asm -> makeing -> run
使用Makefile实现:
- 新建Makefile文件,输入:
# 默认动作
default :
../z_tools/make.exe img
# 文件生成规则
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
# 命令
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
#号表示注释。下一行“ipl.bin : ipl.nas Makefile”的意思是,如果想要制作文件ipl.bin,就先检查一下ipl.nas和Makefile这两个文件是否都准备好了。如果这两个文件都有了,Make工具就会自动执行Makefile的下一行。
“\”是续行符号,表示这一行太长写不下,跳转到下一行继续写。
- 需要调用make.exe来让这个Makefile发挥作用,新建make.bat:
..\z_tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9