内容1:制作IPL。
重点总结:
- 学习并理解IPL概念,掌握IPL启动过程;
- 学习软盘的结构图及各个部分(柱面,磁头,扇区等)表示的含义;
- 学习并理解缓冲区地址等相关概念;
- 学习并掌握如何试错。
概念阐述:
(1)汇编指令:
a.JC
JC即“jump if carry”,如果进位标志是1的话,就跳转。
b.JNC
JNC即“jump if not carry”,如果进位标志是0的话,就跳转。
c.JAE
JAE即“jump if above or equal”,如果大于或者等于,就跳转。
(2)INT 0x13
AH=0x02;(读盘)
AH=0x03;(写盘)
AH=0x04;(校验)
AH=0x0c;(寻道)
AL=处理对象的扇区数;(只能同时处理连续的扇区)
CH=柱面号&0xff;
CL=扇区号(0-5位)|(柱面号&0x300)>>2;
DH=磁头号;
DL=驱动器号;
ES:BX=缓冲地址;(校验及寻道时不使用)
返回值;
FLACS.CF==0:没有错误,AH=0
FLACS.CF==1:有错误,错误号码存入AH内(与重置功能一样)
(3)软盘
·柱面有80个(0~79号)
·每个柱面有18个扇区(1~18号)
CH:柱面号:0
CL:扇区号:0
DH:磁头号:2
DL:驱动器号:0
一张软盘有80个柱面,2个磁头,18个扇区,且一个扇区有512个字节。
所以,一张软盘的容量是:
80*2*18*512=1474560Byte=1440KB
含有IPL的启动区,位于C0-H0-S1(柱面0,磁头0,扇区1的缩写,下个扇区是C0-H0-S2)
关键代码:
(1)制作IPL代码添加部分
MOV AX,0x0820
MOV ES,AX
MOV CH,0 ; 柱面0
MOV DH,0 ; 磁头0
MOV CL,2 ; 扇区2
MOV AH,0x02 ; AH=0x02 : 读盘
MOV AL,1 ; 1个扇区
MOV BX,0
MOV DL,0x00 ; A驱动器
INT 0x13 ; 调用磁盘BIOS
JC error
(2)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
#在这里将原来代码中重复的部分利用MAKE等替代,增强了代码整体的可读性,但是整体代码思路和模式不变。
default :
$(MAKE) img
#文件命名规则
ipl.bin : ipl.nas Makefile
$(NASK) ipl.nas ipl.bin ipl.lst
haribote.img : ipl.bin Makefile
$(EDIMG) imgin:../z_tools/fdimg0at.tek \
wbinimg src:ipl.bin len:512 from:0 to:0 imgout:haribote.im
# 命令
asm :
$(MAKE) ipl.bin
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
src_only :
$(MAKE) clean
-$(DEL) haribote.img
(3)试错代码添加部分
; 读磁盘
MOV AX,0x0820
MOV ES,AX
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
MOV DL,0x00 ; A驱动器
INT 0x13 ; 调用BIOS
JNC fin ; 没出错的话跳转到fin
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
结果显示:
内容2:读入扇区和柱面
重点总结:
- 理解并学会如何读入18个扇区;
- 理解并学会如何读入10个柱面
关键代码:
(1)读入18个扇区
; 读磁盘
MOV AX,0x0820
MOV ES,AX
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
MOV DL,0x00 ; A驱动器
INT 0x13 ; 调用BIOS
JNC next ; 没出错就跳转到next
ADD SI,1 ; SI+1
CMP SI,5 ; 比较SI和5
JAE error ; SI >= 5时,跳转到erro
MOV AH,0x00
MOV DL,0x00 ; A驱动器
INT 0x13 ; 重置驱动器
JMP retry
next:
MOV AX,ES ; 把内存地址后移0x200 2*16^2=512字节
ADD AX,0x0020
MOV ES,AX ;因为没有ADD ES,0x020指令,所以在此绕弯
ADD CL,1 ; 往CL加1
CMP CL,18 ; 比较CL与18
JBE readloop ; 如果CL <= 18,跳转至readloop
注:JBE,“jump if below or equal”小于等于则跳转
(2)读入10个柱面
CYLS EQU 10 ; 定义初识变量,10个柱面
next:
MOV AX,ES ; 把内存地址后移0x200 2*16^2=512字节
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
ADD CH,1
CMP CH,CYLS
JB readloop ; 如果CH < CYLS,则跳转到readloop
注:JB,“jump if below”小于则跳转
内容3:着手开发操作系统并启动,确认操作系统执行情况
重点总结:
- 初步编写操作系统程序,查看文件内容;
- 学会如何从启动区启动操作系统;
- 学会调用新的BIOS函数确定操作系统的执行情况。
关键代码:
(1)着手开发
fin:
HLT
JMP fin
(2)从启动区执行
; haribote-os
; TAB=4
ORG 0xc200 ; 载入启动区地址。
fin:
HLT
JMP fin
; 根据haribote.sys内容修改
JMP 0xc200
(3)确认执行情况
; haribote-os
; TAB=4
ORG 0xc200 ; 这个程序的地址会被装载到什么地方呢?
MOV AL,0x13 ; VGA显卡,320x200x8位彩色
MOV AH,0x00
INT 0x10
fin:
HLT
JMP fin
结果显示:
(1)着手开发
(2)从启动区执行
(3)确认执行情况
内容4:32位前期准备与C语言导入
重点总结:
- 理解16位与32位操作系统差异,能够调用BIOS;
- 明白显卡内存的含义,掌握如何使用显卡内存;
- 学习并理解C语言转化为机器语言的过程,最终实现C代码导入。
关键代码:
; 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显卡,320x200x8bit彩色
MOV AH,0x00 ; 选择模式
INT 0x10
MOV BYTE [VMODE],8 ; 记录画面模式
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
; 用BIOS取得键盘上各种LED指令灯的状态
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
fin:
HLT
JMP fin
bookpack.c:
void HariMain(void)
{
fin:
/* 这里想写上HLT,但是C语言中不能使用HLT */
goto fin;
}
内容5:实现HLT
重点总结:
- 利用汇编语言编写函数;
- 学会设置WCOFF模式,设定成32位机器语言模式;
- 理解并学习文件命名规则,明白“链接”的含义。
关键代码:
naskfunc:
; naskfunc
; TAB=4
[FORMAT "WCOFF"] ; 制作目标文件的模式
[BITS 32] ; 制作32位模式用的机械语言
; 制作目标文件的信息
[FILE "naskfunc.nas"] ; 程序中包含的函数名
GLOBAL _io_hlt ; 程序中包含的函数名
; 以下是实际的函数
[SECTION .text] ; 目标文件中写了这些之后在写程序
_io_hlt: ; void io_hlt(void);
HLT
RET
bookpack.c:
/* 告诉C编译器,有一个函数在别的文件里 */
void io_hlt(void);
/* 是函数声明却不用{},而用;,这表示的意思是:函数是在别的文件中,你自己找一下吧*/
void HariMain(void)
{
fin:
io_hlt(); /* 执行naskfunc.nas里面的_io_hlt */
goto fin;
}