INT 0x13 函数
- 磁盘读、写、扇区检验,以及寻道
- 需要输入的参数:
AH=0x02;(读盘)
AH=0x03;(写盘)
AH=0x04;(校验)
AH=0x0c;(寻道)
AL=处理对象的扇区数;(只能同时处理连续的扇区)
CH=柱面号 &0xff;
CL=扇区号(0-5位)|(柱面号&0x300)>>2;
DH=磁头号;
DL=驱动器号;
ES:BX=缓冲地址;(校验及寻道时不使用) - 返回值:
FLAGS.CF == 0; 没有错误,AH == 0
FLAGS.CF == 1; 有错误,错误号码存入AH内(与重置(reset)功能一样)
寄存器
- CH、CL、DH、DL分别是柱面号、扇区号、磁头号、驱动器号
- 驱动器:在有多个软盘驱动器的时候,用磁盘驱动器号来指定从哪个驱动器的软盘上读取数据。现在的电脑,基本都只有1个软盘驱动器,而以前一般都是2个。既然现在只有一个,那不用多想,指定0号就行了。
软盘
- 结构:
- 柱面:一圈一圈的那个就是
- 磁头:既可以从软盘正面接触磁盘,也可以从软盘背面接触磁盘。软盘磁盘是两面都能记录数据的,因此我们有正面和反面两个磁头,分别是磁头0号和磁头1号。
- 扇区:软盘分为18份,每一份称为一个扇区
- 含有
IPL
的启动区,位于C0-H0-S1
(柱面0,磁头0,扇区1的缩写),下一个扇区是C0-H0-S2
缓冲区地址
- 附加段寄存器ES
- 一个BX只能表示
0~0xffff
,也就是0~65535
,最大64K - 之后引入的EBX,可以处理4G内存
MOV AL, [ES:BX]
:ES×16+BX
- 在代码中指定了
ES=0x0820
,BX=0
。所以软盘的数据将被装载到内存中0x8200
到0x83ff
的地方,0x8000
~0x81ff
这512字节是留给启动区的,要将启动区的内容读到那里。 - 段寄存器DS
MOV AL, [SI]
相当于MOV AL, [DS:SI]
- DS必须预先指定为0,否则地址的值就要加上这个数的16倍,就会读写到其他的地方,引起混乱
代码
- ipl.nas
; haribote-ipl ; TAB=4 ORG 0x7c00 ; 指明程序的装载地址 ; 以下的记述用于标准FAT12格式的软盘 JMP entry DB 0x90 DB "HARIBOTE" ; 引导扇区的名称,随意写,8字节 DW 512 ; 每个扇区的大小,必须是512 DB 1 ; 簇(cluster)的大小,必须为1 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 ; 重写一次磁盘大小,4字节 DB 0,0,0x29 ; 意义不明,固定 DD 0xffffffff ; 卷标号 DB "HARIBOTEOS " ; 磁盘名称,11字节 DB "FAT12 " ; 磁盘格式名称,8字节 RESB 18 ; 空出18字节 ; 核心程序 entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX ; 段寄存器初始化为0 ; 僨傿僗僋傪撉傓 MOV AX,0x0820 MOV ES,AX ; 指定附加段地址寄存器的值0x0820 MOV CH,0 ; 柱面0 MOV DH,0 ; 磁头0 MOV CL,2 ; 扇区2 MOV AH,0x02 ; AH=0x02: 读盘 MOV AL,1 ; 1个扇区 MOV BX,0 ; 指定基址寄存器的值0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JC error ; 如果进位标志是1的话,就跳转 ; fin 中让CPU停止,进入等待模式,并不断循环 fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 error: MOV SI,msg ; 不断累加SI中的地址,并从中读取内容。当内容不等于0时,调用中断显示字符,然后重复本步骤; ; 当内容为0时,跳转到fin 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 msg: DB 0x0a, 0x0a ; 换行2次 DB "load error" DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00,直到0x001fe DB 0x55, 0xaa
试错
- 如果软盘不能读取数据,重试几次,如果一直重试下去,要是磁盘真的坏了,程序就会陷入死循环
- 部分代码:
- 完整代码:
; haribote-ipl ; TAB=4 ORG 0x7c00 ; 指明程序的装载地址 ; 以下的记述用于标准FAT12格式的软盘 JMP entry DB 0x90 DB "HARIBOTE" ; 引导扇区的名称,随意写,8字节 DW 512 ; 每个扇区的大小,必须是512 DB 1 ; 簇(cluster)的大小,必须为1 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 ; 重写一次磁盘大小,4字节 DB 0,0,0x29 ; 意义不明,固定 DD 0xffffffff ; 卷标号 DB "HARIBOTEOS " ; 磁盘名称,11字节 DB "FAT12 " ; 磁盘格式名称,8字节 RESB 18 ; 空出18字节 ; 核心程序 entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX ; 段寄存器初始化为0 ; 僨傿僗僋傪撉傓 MOV AX,0x0820 MOV ES,AX ; 指定附加段地址寄存器的值0x0820 MOV CH,0 ; 柱面0 MOV DH,0 ; 磁头0 MOV CL,2 ; 扇区2 MOV SI,0 ; 记录失败次数的寄存器 retry: MOV AH,0x02 ; AH=0x02: 读盘 MOV AL,1 ; 1个扇区 MOV BX,0 ; 指定基址寄存器的值0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JNC fin ; 没出错的话跳转到fin,进位标志是0的话就跳转 ADD SI,1 ; 出错往SI加1 CMP SI,5 ; 比较SI和5 JAE error ; SI >= 5时,跳转到error;JAE大于或等于时跳转 MOV AH,0x00 ; 系统复位”。它的功能是复位软盘状态,再读一次。 MOV DL,0x00 ; A驱动器 INT 0x13 ; 重置驱动器 JMP retry ; fin 中让CPU停止,进入等待模式,并不断循环 fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 error: MOV SI,msg ; 不断累加SI中的地址,并从中读取内容。当内容不等于0时,调用中断显示字符,然后重复本步骤; ; 当内容为0时,跳转到fin 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 msg: DB 0x0a, 0x0a ; 换行2次 DB "load error" DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00,直到0x001fe DB 0x55, 0xaa
读到18扇区
- 如果要读取下一扇区,只需给CL加1,给ES加上0x20
; haribote-ipl ; TAB=4 ORG 0x7c00 ; 指明程序的装载地址 ; 以下的记述用于标准FAT12格式的软盘 JMP entry DB 0x90 DB "HARIBOTE" ; 引导扇区的名称,随意写,8字节 DW 512 ; 每个扇区的大小,必须是512 DB 1 ; 簇(cluster)的大小,必须为1 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 ; 重写一次磁盘大小,4字节 DB 0,0,0x29 ; 意义不明,固定 DD 0xffffffff ; 卷标号 DB "HARIBOTEOS " ; 磁盘名称,11字节 DB "FAT12 " ; 磁盘格式名称,8字节 RESB 18 ; 空出18字节 ; 核心程序 entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX ; 段寄存器初始化为0 ; 僨傿僗僋傪撉傓 MOV AX,0x0820 MOV ES,AX ; 指定附加段地址寄存器的值0x0820 MOV CH,0 ; 柱面0 MOV DH,0 ; 磁头0 MOV CL,2 ; 扇区2 readloop: MOV SI,0 ; 记录失败次数的寄存器 retry: MOV AH,0x02 ; AH=0x02: 读盘 MOV AL,1 ; 1个扇区 MOV BX,0 ; 指定基址寄存器的值0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JNC next ; 没出错时跳转到next ADD SI,1 ; 往SI加1 CMP SI,5 ; 比较SI与5 JAE error ; SI >= 5时,跳转到error MOV AH,0x00 MOV DL,0x00 ; A驱动器 INT 0x13 ; 重置驱动器 JMP retry next: MOV AX,ES ; 把内存地址后移0x200 ADD AX,0x0020 MOV ES,AX ; 因为没有ADD ES,0x020指令,所以这里稍微绕个弯 ADD CL,1 ; 往CL里加1 CMP CL,18 ; 比较CL与18 JBE readloop ; 如果CL <= 18 跳转至readloop ; fin 中让CPU停止,进入等待模式,并不断循环 fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 error: MOV SI,msg ; 不断累加SI中的地址,并从中读取内容。当内容不等于0时,调用中断显示字符,然后重复本步骤; ; 当内容为0时,跳转到fin 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 msg: DB 0x0a, 0x0a ; 换行2次 DB "load error" DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00,直到0x001fe DB 0x55, 0xaa
- 一个扇区的大小是512,要读取下一个扇区,就得给CL加上1,给ES加上0x20(521 / 16)。因为
MOV AL, [ES:BX]
:ES × 16 + BX = 512
,BX = 0
,ES = 512 / 16
;其实也可以写成BX + 512
。 - 这里不是非要用循环,在调用读盘函数
INT 0x13
的地方,只要将AL的值设置成17就行了。这样,程序一下子就能将扇区2~18
共17个扇区的数据完整的读进来。
读入10个柱面
- 程序:
; haribote-ipl ; TAB=4 CYLS EQU 10 ; 相当于C语言的#define命令,用来声明常数,CYLS = 10。 ORG 0x7c00 ; 指明程序的装载地址 ; 以下的记述用于标准FAT12格式的软盘 JMP entry DB 0x90 DB "HARIBOTE" ; 引导扇区的名称,随意写,8字节 DW 512 ; 每个扇区的大小,必须是512 DB 1 ; 簇(cluster)的大小,必须为1 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 ; 重写一次磁盘大小,4字节 DB 0,0,0x29 ; 意义不明,固定 DD 0xffffffff ; 卷标号 DB "HARIBOTEOS " ; 磁盘名称,11字节 DB "FAT12 " ; 磁盘格式名称,8字节 RESB 18 ; 空出18字节 ; 核心程序 entry: MOV AX,0 ; 初始化寄存器 MOV SS,AX MOV SP,0x7c00 MOV DS,AX ; 段寄存器初始化为0 ; 僨傿僗僋傪撉傓 MOV AX,0x0820 MOV ES,AX ; 指定附加段地址寄存器的值0x0820 MOV CH,0 ; 柱面0 MOV DH,0 ; 磁头0 MOV CL,2 ; 扇区2 readloop: MOV SI,0 ; 记录失败次数的寄存器 retry: MOV AH,0x02 ; AH=0x02: 读盘 MOV AL,1 ; 1个扇区 MOV BX,0 ; 指定基址寄存器的值0 MOV DL,0x00 ; A驱动器 INT 0x13 ; 调用磁盘BIOS JNC next ; 没出错时跳转到next ADD SI,1 ; 往SI加1 CMP SI,5 ; 比较SI与5 JAE error ; SI >= 5时,跳转到error MOV AH,0x00 MOV DL,0x00 ; A驱动器 INT 0x13 ; 重置驱动器 JMP retry next: MOV AX,ES ; 把内存地址后移0x200 ADD AX,0x0020 MOV ES,AX ; 因为没有ADD ES,0x020指令,所以这里稍微绕个弯 ADD CL,1 ; 往CL里加1 CMP CL,18 ; 比较CL与18 JBE readloop ; 如果CL <= 18 跳转至readloop MOV CL,1 ADD DH,1 ; 更改磁头的位置 CMP DH,2 JB readloop ; DH < 2 跳转到readloop MOV DH,0 ; 将磁头的位置变为0 ADD CH,1 ; 跳到下一个柱面 CMP CH,CYLS ; 宏定义 CYLS = 10 JB readloop ; CH < CYLS 跳转到readloop ; fin 中让CPU停止,进入等待模式,并不断循环 fin: HLT ; 让CPU停止,等待指令 JMP fin ; 无限循环 error: MOV SI,msg ; 不断累加SI中的地址,并从中读取内容。当内容不等于0时,调用中断显示字符,然后重复本步骤; ; 当内容为0时,跳转到fin 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 msg: DB 0x0a, 0x0a ; 换行2次 DB "load error" DB 0x0a ; 换行 DB 0 RESB 0x7dfe-$ ; 填写0x00,直到0x001fe DB 0x55, 0xaa
着手开发操作系统
- 之前写的不是操作系统,只是操作系统的启动区,被用来把操作系统装载到内存中
- 新建一个文本文档并命名为
haribote.nas
fin: HLT JMP fin
- 因为新添了文件,需要在Makefile中规定它的编译规则
TOOLPATH = ../z_tools/ MAKE = $(TOOLPATH)make.exe -r NASK = $(TOOLPATH)nask.exe EDIMG = $(TOOLPATH)edimg.exe IMGTOL = $(TOOLPATH)imgtol.com COPY = copy DEL = del default : $(MAKE) img # 启动区文件 # ipl.nas —> ipl.bin ipl.bin : ipl.nas Makefile $(NASK) ipl.nas ipl.bin ipl.lst # 操作系统本身的内容 # haribote.nas —> haribote.sys haribote.sys : haribote.nas Makefile $(NASK) haribote.nas haribote.sys haribote.lst # 操作系统镜像文件 # ipl.bin & haribote.sys —> haribote.img haribote.img : ipl.bin haribote.sys Makefile $(EDIMG) imgin:../z_tools/fdimg0at.tek \ wbinimg src:ipl.bin len:512 from:0 to:0 \ copy from:haribote.sys to:@: \ imgout:haribote.img # 僐儅儞僪 img : $(MAKE) haribote.img run : $(MAKE) img $(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin $(MAKE) -C ../z_tools/qemu install : $(MAKE) img $(IMGTOL) w a: haribote.img clean : -$(DEL) ipl.bin -$(DEL) ipl.lst -$(DEL) haribote.sys -$(DEL) haribote.lst src_only : $(MAKE) clean -$(DEL) haribote.img
- 输入
make run
- 打开haribote.img,可以发现在0x2600地址附近保存着文件名。在0x4200地址附近,看到“F4 EB FD”。
- 以上内容可以总结为:一般向一个空软盘保存文件时
(1)文件名会写在0x2600以后的地方;
(2)文件的内容会写在0x4200以后的地方。
从启动区执行操作系统
- 如何执行磁盘映像上位于0x004200号地址的程序
- 程序是从启动区开始,把磁盘上的内容装载到内存
0x8000
号地址,所以磁盘0x4200处的内容就应该位于内存0x8000+0x4200=0xc200
号地址 - 在
haribote.nas
中加上ORG 0xc200
; haribote-os ; TAB=4 ORG 0xc200 ; 这个程序将要被装载到内存的什么地方 MOV AL,0x13 ; VAG显卡,320x200x8位色彩 MOV AH,0x00 ; 设定AH=0x00后,调用显卡的BIOS函数 INT 0x10 fin: HLT JMP fin
- 设置显卡模式:
- 输入
make run
32位模式前期准备
- CPU有16位和32位两种模式:
以16位模式启动的话,用AX和CX等16位寄存器会非常方便
16位模式的机器语言在32位模式下不能运行
32位模式下可以使用的内存容量远远大于1MB
CPU的自我保护功能(识别出可疑的机器语言并进行屏蔽,以免破坏系统)在16位下不能用,但32位下能用
32位模式下不能调用BIOS功能。因为BIOS是用16位机器语言写的。如果我们有什么事情想用BIOS来做,那就全部都放在开头先做 - 想从BIOS得到键盘状态。所谓键盘状态,是指NumLock是ON还是OFF等这些状态
- 修改
haribote.nas
; haribote-os ; TAB=4 ; 有关BOOT_INFO CYLS EQU 0x0ff0 ; 设定启动区 LEDS EQU 0x0ff1 VMODE EQU 0x0ff2 ; 关于颜色数目的信息。颜色的位数 SCRNX EQU 0x0ff4 ; 分辨率X(screen x) SCRNY EQU 0x0ff6 ; 分辨率Y(screen y) VRAM EQU 0x0ff8 ; 图像缓冲区的开始地址 ORG 0xc200 ; 这个程序将要被装载到内存的什么地方 MOV AL,0x13 ; VGA显卡,320x200x8位色彩 INT 0x10 MOV BYTE [VMODE],8 ; 记录画面模式,把画面模式的信息保存在了内存里 MOV WORD [SCRNX],320 MOV WORD [SCRNY],200 MOV DWORD [VRAM],0x000a0000 ; VRAM指的是显卡内存,VRAM的各个地址都对应着画面上的象素,可以绘制出图案 ; 不同画面模式的像素数/内存也不一样,所以需要预先把要使用的VRAM地址保存在BOOT_INFO里以备用 ; 用BIOS取得键盘上各种LED指示灯的状态 MOV AH,0x02 INT 0x16 ; keyboard BIOS MOV [LEDS],AL fin: HLT JMP fin
开始导入C语言
- 将原来的
haribote.nas
改为asmhead.nas
前半部分是用汇编语言写的,后半部分是用C语言写的
为了调用C语言写的程序,添加了100行左右的汇编代码 - C语言
void HariMain(void) { fin: /* 这里想写上HLT,但C原因呢中不能用HLT */ // goto指令是新出现的,相当于汇编语言中的JMP,实际上也是被编译成JMP指令 goto fin; }
- 修改Makefile文件
TOOLPATH = ../z_tools/ INCPATH = ../z_tools/haribote/ MAKE = $(TOOLPATH)make.exe -r NASK = $(TOOLPATH)nask.exe CC1 = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet GAS2NASK = $(TOOLPATH)gas2nask.exe -a OBJ2BIM = $(TOOLPATH)obj2bim.exe BIM2HRB = $(TOOLPATH)bim2hrb.exe RULEFILE = $(TOOLPATH)haribote/haribote.rul EDIMG = $(TOOLPATH)edimg.exe IMGTOL = $(TOOLPATH)imgtol.com COPY = copy DEL = del default : $(MAKE) img # 僼傽僀儖惗惉婯懃 ipl10.bin : ipl10.nas Makefile $(NASK) ipl10.nas ipl10.bin ipl10.lst asmhead.bin : asmhead.nas Makefile $(NASK) asmhead.nas asmhead.bin asmhead.lst bootpack.gas : bootpack.c Makefile $(CC1) -o bootpack.gas bootpack.c bootpack.nas : bootpack.gas Makefile $(GAS2NASK) bootpack.gas bootpack.nas bootpack.obj : bootpack.nas Makefile $(NASK) bootpack.nas bootpack.obj bootpack.lst bootpack.bim : bootpack.obj Makefile $(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \ bootpack.obj # 3MB+64KB=3136KB bootpack.hrb : bootpack.bim Makefile $(BIM2HRB) bootpack.bim bootpack.hrb 0 haribote.sys : asmhead.bin bootpack.hrb Makefile copy /B asmhead.bin+bootpack.hrb haribote.sys haribote.img : ipl10.bin haribote.sys Makefile $(EDIMG) imgin:../z_tools/fdimg0at.tek \ wbinimg src:ipl10.bin len:512 from:0 to:0 \ copy from:haribote.sys to:@: \ imgout:haribote.img # 僐儅儞僪 img : $(MAKE) haribote.img run : $(MAKE) img $(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin $(MAKE) -C ../z_tools/qemu install : $(MAKE) img $(IMGTOL) w a: haribote.img clean : -$(DEL) *.bin -$(DEL) *.lst -$(DEL) *.gas -$(DEL) *.obj -$(DEL) bootpack.nas -$(DEL) bootpack.map -$(DEL) bootpack.bim -$(DEL) bootpack.hrb -$(DEL) haribote.sys src_only : $(MAKE) clean -$(DEL) haribote.img
- 解释文件作用
- cc1是C编译器,可以将C语言程序编译成汇编语言源程序,输出的是gas用的源程序。它不能翻译成nask。
- gas2nask可以把gas变换成nask
- 使用nask制作obj文件。obj文件又称为目标文件
- 目标文件是一种特殊的机器语言文件,必须与其他文件链接(link)后才能变成真正可以执行的机器语言
- 为了能做成完整的机器语言文件,必须将必要的目标文件全部链接上。完成这项工作的,就是obj2bim
- 映像文件:映像文件即不是文件本来的状态,而是一种代替形式
- bim文件也“不是本来的状态,而是一种代替的形式”,也还不是完成品。这只是将各个部分全部都链接在一起,做成了一个完整的机器语言文件,而为了能实际使用,我们还需要针对每一个不同操作系统的要求进行必要的加工,比如说加上识别用的文件头,或者压缩等
- 函数名HariMain非常重要,程序就是从以HariMain命名的函数开始运行的,所以这个函数名不能更改
实现HLT
- 就是让程序处于那个不耗电的状态
- 创建文件
naskfunc.nas
- 用汇编写的函数,之后还要与
bootpack.obj
链接,所以也需要编译成目标文件。因此将输出格式设定为WCOFF
模式。另外,还要设定成32位机器语言模式 - 函数名的前面加上“_”,否则就不能很好地与C语言函数链接
- RET指令,相当于C语言的return
- 修改bootpack.c
遇到的问题及解决方法
- ORG命令后的地址是什么地址?
ORG后的地址是程序被载入内存时的起始地址;在我们的实验代码中,org指令本身并不能决定程序将要加载到内存的什么位置,它只是告诉编译器,我的程序在编译好后需要加载到哪个地址;(因为只有编译才会逐行读取里面代码) - 码中为什么总是使用ORG
0x7c00?
引导程序加载到内存的7c00h处是一项标准,并不是在编程时决定的,在实际装系统的过程中(并非是虚拟机),将org后的数字改成其他值,bois程序一样将它加载到7c00处; - 为什么有时候去掉ORG指定地址或者使用7c00后的地址后还可以正常执行吗?
ORG相当于段地址;不一定。在数据访问时,都是采用段地址+基址的方式,如果不用org指定地址那么段地址就会清0,最后读取数据的地址就不是我们预期的那个地址了;当程序中没有对内存的寻址操作时,不加org
0x7c00就不会出错。 - 如果说ORG指令后的地址是启动区的装载地址,那么ORG以及之前的指令被加载内存的什么地方?
org指令只会在编译期影响到内存寻址指令的编译(编译器会把所有程序用到的段内偏移地址自动加上org后跟的数值),而其自身并不会被编译成机器码。 - 为什么0x8000到0x81ff区域也要留给启动区,而且为什么不是从0x7e00开始?
解决方法:经过查阅了一些资料得知,BIOS读完启动扇区以后,会跳转到0x7C00启动,占用0x7C00-0x7DFF这一段(512字节),而一般bootloader还需要一个栈空间或者读磁盘的交换空间,一般是放到0x7E00-0x7FFF这512字节里,所以有些操作系统的镜像起点是0x8000。然后启动区确实会拷贝一份映射到0x8000位置上。但是BIOS里读取启动扇区都是加载到0x7C00上的。