这本书作者是日本人,虽然书翻译为中文,但是源代码里注释都是日文,对不懂日语的人来说看起来不够方便,我把书本中给出的注释翻译,以及书本介绍的一些知识,加到了注释里,阅读起来能够方便些。
前3天主要就是写一个ipl程序(initial program loader启动程序加载器),不论是用二进制编辑器直接写机器指令还是用汇编生成二进制程序,最终目的都是得到第一个扇区的512字节的启动程序。
0x10号bios函数用于显示文字
0x13号bios函数提供磁盘的读,写,扇区校验,寻道操作,AH=0x02读盘操作
这两个bios函数调用时以寄存器的值为参数,提供相应操作。
; haribote-ipl
; TAB=4
CYLS EQU 10 ;
ORG 0x7c00 ;ORG伪指令,编译不生成机器代码,用于给链接器定位用,将程序装载到内存的指定位置
;0x7c00~0x7dff 512字节放置启动程序
; 标准FAT12格式软盘专用的代码
JMP entry ;jmp跳转 至标签entry处;JMP 0x7c50 没问题,JMP跳转到的就是内存地址
DB 0x90 ;DB即data byte 往文件里写入一个字节,DW写一个word,16位,DD写double word,32位
DB "HARIBOTE" ;启动区的名称,可以是任意的字符串(8字节)
DW 512 ;每个扇区(sector)的大小(必须为 512字节)
DB 1 ;簇(cluster)的大小(必须为 1 个扇区)
DW 1 ;FAT(File Allocation Table:文件配置表)的起始位置(一般从第一个扇区开始)
DB 2 ;FAT的个数,必须为2
DW 224 ;根目录的大小(一般设成224项)
DW 2880 ;该磁盘的大小(必须为2880个扇区)
DB 0xf0 ;磁盘的种类(必须为0xf0)
DW 9 ;FAT的长度(必须是9扇区)
DW 18 ;一个磁道(track)有几个扇区(必须是18)
DW 2 ;磁头数(必须是2)
DD 0 ;不使用分区,必须是0
DD 2880 ;重写一次磁盘大小
DB 0,0,0x29 ;意义不明,固定
DD 0xffffffff ;(可能是)卷标号码
DB "HARIBOTEOS " ; 磁盘的名称(11字节)
DB "FAT12 " ; 磁盘格式名称(8字节)
RESB 18 ; reserve word 从当前地址空出18个字节,nask会把这18个字节填充为0x00
entry:
MOV AX,0 ;AX accumulator 累加寄存器 (16位)把0复制到AX中,初始化AX
MOV SS,AX ;SS stack segment 栈段寄存器 (16位)
MOV SP,0x7c00 ;SP stack pointer 栈指针寄存器(16位)
MOV DS,AX ;DS data segment 数据段寄存器(16位)
MOV AX,0x0820 ;MOV AL,[ES:BX] 代表ES*16+BX的内存地址
;0x7c00~0x7dff用于启动区,0x7e00~0xfbff没有特别的用途,操作系统随便用
;缓冲区地址,将要把软盘上读取的数据放到0x8200之后,内存分布图上0x8000后无人使用
;0x8000~0x81ff留给启动区,将启动区内容读到这里
MOV ES,AX ;ES extra segment 附加段寄存器(16位) ES=0x0820,BX=0
MOV CH,0 ;CH counter high 计数寄存器高位(8位) 柱面0
MOV DH,0 ;DH data high 数据寄存器高位(8位) 磁头0
MOV CL,2 ;CL counter low 计数寄存器地位(8位) 扇区2,计数读的扇区标号
readloop:
MOV SI,0 ;SI source index 源变址寄存器(16位) 记录失败
retry:
MOV AH,0x02 ;AH accumulator high 累加寄存器高位(8位) AX=0x02 读盘
MOV AL,1 ;AL accumulator low 累加寄存器低位(8位) AL=1 1个扇区
MOV BX,0 ;BX base 基址寄存器(16位)
MOV DL,0x00 ;DL data low 数据寄存器低位(8位) A驱动器
INT 0x13 ;INT interrupt 软件中断指令,暂且当作函数调用 调用磁盘BIOS
JNC next ;jump if not carry 如果进位标志为0,跳转至next 0x13调用有错标志为1,无错为0
ADD SI,1 ;SI加1
CMP SI,5 ;比较SI和5
JAE error ;jump if above or equal SI>=5时跳转到error
MOV AH,0x00 ;
MOV DL,0x00 ;A驱动器
INT 0x13 ;重置驱动器
JMP retry ;无条件 跳转到retry
next:
MOV AX,ES ;把内存地址后移0x200 512字节 512/16=0x20 [ES:BX]
ADD AX,0x0020 ;
MOV ES,AX ;没有ADD ES,0x20指令
ADD CL,1 ;CL+1
CMP CL,18 ;比较CL和18(1-18 18个扇区)
JBE readloop ;jump if below or equal cl<=18 跳转到readloop
MOV CL,1 ;扇区标号更新为1
ADD DH,1 ;磁头数+1,读磁盘反面
CMP DH,2 ;比较DH 2
JB readloop ;jump if below DH<2 时跳转到readloop
MOV DH,0 ;磁头初始化为0
ADD CH,1 ;柱面+1
CMP CH,CYLS ;比较CH 和 CYLS=10
JB readloop ;jump if below CH<10 跳转到readloop
;空软盘保存文件时,文件名会写在0x2600以后的地方,文件内容会写在0x4200以后
MOV [0x0ff0],CH ;把磁盘装载地址的结束写到内存0x0ff0处
JMP 0xc200 ;程序从启动区开始,把磁盘上的内容装载到内存0x8000号地址,磁盘0x4200处的内容应该位于
;0x8000+0x4200=0xc200号地址
error:
MOV SI,msg ;msg地址复制到SI
putloop:
MOV AL,[SI] ;[]表示内存地址,这句是把内存地址为SI的数据复制到AL中
ADD SI,1 ;SI+1
CMP AL,0 ;比较AL和0
JE fin ;jump if equal
MOV AH,0x0e ;
MOV BX,15 ;
INT 0x10 ;调用bios 0x10号函数
JMP putloop ;
fin:
HLT ;CPU停止动作,进入待机状态,外部发生变化,比如按下键盘,cpu会苏醒过来,继续执行程序
JMP fin ;无限循环
msg:
DB 0x0a,0x0a ;2个换行
DB "load error" ;DB直接写字符串,汇编语言自动查找编码
DB 0x0a ;换行
DB 0 ;标志msg结束
RESB 0x7dfe-$ ;$指示当前行在文件中所处的字节数;在使用ORG指令后
DB 0x55,0xaa ;软盘的第0x1fe即第510字节开始处必须为55 aa