自制操作系统笔记-第2章
helloos.nas节选
ORG 0x7c00 ;指明程序的装载地址 ;以下记述用于标准FAT12格式软盘
JMP entry ; 0xeb, 0x4e,
DB 0x90 ; 0x90
DB "HELLOIPL" ;引导扇区(启动区)的名称(8字节)
DW 512 ;一个扇区(sector)的大小(必须是512字节)
DB 1 ; 簇(cluster)的大小(必须是1个扇区)
DW 1 ; FAT的起始位置(一般从第1扇区开始)
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 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 “hello, world" DB 0x0a ;换行 DB 0
RESB 0x7dfe-$ ; 填写0x00直到0x7dfe,程序最开始的ORG 0x7c00表示程序加载到内存中的0x7c00, ,0x1fe转为十进制是510, 0x7c00 + 0x1fe 就是 0x7dfe, 至于为什么是0x1fe第一章说过了,为什么是7c00本章下面介绍了。
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 ,源头、起点,把程序装载到内存中的指定地址。有了它,$符的含义就随之变化,不再是输出文件的第几个字节,而代表将要读入的内存地址。我理解,没写ORG时,$就代表镜像文件从开头到当前行的字节数,有了ORG,就代表的是内存的地址号。
JMP 源于英jump,即跳转,用法:
JMP 标签名
MOV 源于英文move,意为移动, 功能为赋值,非常重要,理解了它就理解了汇编语言的一大半。
MOV AX,0 相当于 AX=0;
MOV SS,AX 相当于 SS=AX;
执行MOV SS,AX后,AX的值不变
CPU里有一种名为寄存器的存储电路,相当于机器语言中的变量,具有代表性的有以下8个:
- AX accumulator 累加寄存器 ,用来进行各种计算 为分AH AL
- CX counter 计数寄存器,为方便计数 分为 CH CL
- DX data 数据寄存器,分为DH DL
- BX base 基址寄存器,适合做为计算内存地址的基点 分为 BH BL
- SP stack pointer 栈指针寄存器
- BP base pointer 基址指针寄存器
- SI source index 源变址寄存器
- DI destination index 目的变址寄存器
这些都是16位寄存器,这个顺序是按机器语言中寄存器的编号顺序排列的,X表示扩展(extend),因为之前CPU寄存器是8位的,后来变为16位,所以在寄存器名后面加个X,
在16位寄存器名字前面加上一个E(源于Extend)就是32位寄存器的名字了:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI。
EAX的32位中的低16位是与AX共用的,而高16位没有名字也没有编号。
32位CPU也只能存储32字节,(32/8=4字节,4字节/寄存器*8个寄存器=32字节)。
CPU里还有6个16位的段寄存器:
- ES 附加段寄存器 extra segment
- CS 代码段寄存器 code segment
- SS 栈段寄存器 stack segment
- DS 数据段寄存器 data segment
- FS 没有名称 segment part2
- GS 没有名称 segment part3
MOV SI,msg ?
JMP entry , entry是标号,每个标号对应的数字由汇编语言编译器根据ORG指令计算出来的,“标号的地方对应的内存地址”就是标号的值 。书中例子entry 相当于0x7c50。
MOV AX,entry 会把0x7c50代入到AX寄存器里。
MOV AL, [SI] 方括号代表内存,
对CPU来说,内存是外部存储器,CPU与内存之间的电信号交换,不仅为了存取数据,程序本身也在内存里。程序必须放在内存里。CPU在执行机器语言时,必须从内存中一个命令一个命令的读取程序,顺序执行。
MOV 指令的数据传送源和传送目的地 不仅可以是寄存器或常数,也可以是内存地址。这时我们就用[]表示内存地址。
BYTE,WORD,DWORD都是汇编语言保留字。
MOVE BYTE [678],123 用内存的678号地址来保存“123”这个数值。即在内存地址678号存储01111011(即123);
MOVE WORD [678],123 将123转为0000 0000 0111 1011,将0111 1011存到内存地址678号中,将0000 0000 存到内存地址679中。
汇编语言里指定内存地址时,要用下面方式:
数据大小 [地址]
数据大小为BYTE,使用的存储单元就只是地址所指定的字节,为WORD则相邻的一个字节也会成为这个指令的操作对象。如果为DWORD,则相邻的三个字成为操作对象。相邻指地址增加的方向的相邻。
内存地址的指定方法,不仅可以使用常数,还可使用寄存器,如 BYTE [SI]、WORD [BX]
只有BX,BP,SI,DI这几个寄存器可以用来指定内存地址,(因为CPU没有对应的电路,也就是没有对应的机器语言),所以想把DX内存里的内容赋值给AL的时候, 这样写:
MOV BX, DX
MOV AL, BYTE [BX]
--------------------------------------------------------------------------------------------------------------------------------------
MOV指令有个规则,即源数据和目的数据必须位数相同,即向AL里代入的也只有BYTE,那就可以省略BYTE,
MOV AL, BYTE [SI] 可简写为 MOV AL [SI]
-----------------------------------------------------------------------------------------------------------------------------------
ADD 加法指令,ADD SI,1 就相当于 SI=SI+1.
CMP 比较指令(compare),即if语句的一部分,如if(a ==3) { ... },对a和3比较,翻译成汇编语言必须先写CMP a,3 告诉CPU比较的对象,然后下一步再写“如果二者相等需要做什么”。
JE是条件跳转指令之一(jump if equal),即根据比较结果决定 是否跳转,如相等则跳转到指定地址,否则继续执行下一条指令。
-----------------------------------------------------------------------------------------------------------------------------------
INT是软件中断指令(interrupt,中途打断)。暂时先看作一个函数调用。
INT 0x10 ,INT后面是个数字,使用不同数字可以调用 不同函数,0x10即16号函数,功能是控制显卡。
https://wenku.baidu.com/view/9be1ae2cabea998fcc22bcd126fff705cc175c1d.html 查询BIOS 的中断相关信息,得知:
- AH=0x0e; (有的地方也写做0EH ,H表示HEX,十六进制)
- AL=字符;
- BH=0; (页码)
- BL=color code; (需图形模式)
- 返回值: 无
- 注: beep、back space、CR、LF都会被当做控制字符处理
所以,按这里所写的步骤往寄存器里代入各种值,再调用 INT 0x10,就能顺利在屏幕上显示一个字符。目前颜色只能是白色。
-------------------------------------------------------------------------------------------------------------------------------
HLT 让CPU停止动作的指令(halt 停止),并不是彻底停止(除非断电),而是让CPU进入待机状态,只要外部发生变化,如按键,动鼠标CPU就会醒来,
fin: HLT JMP fin
所以,这里有没有HLT都会是无限循环,但是如果没有HLT,CPU就会不信的执行JMP指令,
--------------------------------------------------------------------------------------------------------------------------------
用C语言改写后的helloos.nas程序节选:
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;
------------------------------------------------------------------------------------------------------------------------------
内存最开始0号地址是BIOS程序用来实现各种功能的地方,不能随便用。内存0xf0000号地址附件还存放着BIOS程序本身,也不能使用。还有其它地方也不能用,
0x00007c00-0x00007dff : 启动区内容的装载地址(这是从7c00到7dff的意思,7c00 + 01ff(511)就是7dff , 一共是512字节
程序中ORG指令的值就是这个数。所以程序才能正常运行。(这个数就是规定)
-----------
projects/02_day/helloos4 下的文件说明:
- !cons_*.bat 打开命令行窗口的批处理
- asm.bat 汇编编译批处理,调用了 ..\z_tools\nask.exe
- helloos.img 最后生成的磁盘镜像文件
- ipl.bin 汇编编译器..\z_tools\nask.exe 生成的二进制文件
- ipl.lst 汇编编译生成的输出列表文件,是个文本文件,可以简单的确认每个指令是怎么翻译成机器语言的。
- ipl.nas 启动程序的汇编源代码
- install.bat 将镜像写入软盘,没有软驱,现在用不上
- makeimg.bat 利用 ..\z_tools\edimg.exe ,将bin文件写入并生成镜像文件helloos.img
- run.bat 将镜像复制到模拟器qemu的文件夹,然后打开模拟器
执行步骤: !con* ->asm ->makeimg -> run
-------------------------------------------------------------------------------------------------
Makefile工具
利用z_tools/make.exe ,位于tolset/z_new_w文件夹中,调用它的批处理文件 make.bat:
..\z_tools\make.exe %1 %2 %3 %4 %5 %6 %7 %8 %9
执行make.exe时,会在执行命令时的当前文件夹下找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
后面直接使用这个Makefile工具来编译,生成IMG镜,运行
make asm 编译nas文件为bin文件
make img 将bin文件写入Img文件 ,默认动作,执行make即 make img
make run 在模拟器上运行img文件
make clear 删除最终结果外的一切中间文件: ipl.bin和ipl.lst
make src_only 删除源代码外的所有: ipl.bin和ipl.lst 和 helloos.img
以上内容放在projects/02_day/helloos5/