30天自制操作系统-第03天-进入32位模式并导入C语言
1.制作真正的IPL
IPL(Initial Program Loader):启动程序装载器,软盘的第一个扇区,前面制作的IPL就显示一个Hello,world外什么事都没做,因此本节要在IPL里加入新的功能,读取下一个扇区(第二扇区)的内容
2.软盘的结构(1.44M)
80个柱面(磁道),每个柱面毎·刺头18个扇区,2个磁头,总容量=80 X 18 X 2 X 512 = 1440 X 1024 =1.44M
IPL位于C0-H0-S1(柱面-磁头-扇区), 因此下一个扇区就是C0-H0-S2
本节前半部分讲述如何读取软盘,熟悉了解读取磁盘的调用即可
;读取软盘代码片段
MOV AX,0x0820 ;从第二个扇区开始读取,第一个扇区(IPL)已经被系统读取到7c00,这里从8200开始,原因之一在于便于计算hanbote.sys文件内容 (8000+4200=C200)的偏移量
MOV ES,AX
MOV BX,0 ;数据返回地址 ES:BX,此时0820:0000
MOV CH,0 ; 柱面0,也即磁道 ,CX中的6~15位代表柱面号,(其中,CL的6~7为柱面数的高两位,CH存低8位)
MOV CL,2 ; 2号扇区,CX中的0~5位代表扇区号,扇区编号从1开始
MOV DH,0 ; 磁头0
MOV DL,0x00 ; A驱动器,如果是硬盘,则7为置1,假如有A、B两软驱,C为硬盘,则C盘为0x80
MOV AH,0x02 ; AH=0x02 : 表示读取磁盘操作
MOV AL,1 ; 读取的扇区数量,此时1个 扇区
INT 0x13 ;调用磁盘BIOS
JC error ;jump if carry 如果进位值为1,说明出现了错误跳转到error输出错误信息
磁盘读取完成后:
MOV [0x0ff0],CH ; IPL将读取到哪个柱面记录下来
JMP 0xc200
3.初版操作系统文件:\03_day\harib00e\hanbote.nas:
org 0xc200
fin:
HLT
JMP fin
编译成hanbote.sys
执行Make install 发现 imgtol.com在win64下不能运行,因此进入Win2000虚拟机执行,执行成功
4.初版实验总结:
一般向一个空软盘中保存文件时,文件名会写在0x002600以后的地方,文件的内容会写在0x004200以后的地方。当我们将操作系统本身的内容写到名为haribote.sys的文件中,再将其保存到磁盘映像里,确定好磁盘映像0x4200在内存中对应的地址位置,即可从启动区执行操作系统
代码读取了10个柱面,即10 X 2 X 18 X 512=184320=180K = 0x2d000,已经远远超过0x4200
原以为会采用读取FAT表的读取文件,看来作者采用了取巧的方式。
5.第二版\03_day\harib00g:
ipl10.nas
haribote.nas:
org 0xc200
mov al,0x13 ;VGA模式,320x200x8位彩色,对应的VRAM地址0xa0000
mov ah,00
int 0x10
fin:
hlt
jmp fin
注意知识点,int 10h功能调用
6.第二版实验:
执行 make run ,进入图形模式,屏幕全黑,没有光标闪烁,成功
7.进入32位前的准备
32位寄存器,EAX ,EBX等
32位模式和16位模式:32位模式,指的是CPU的模式。CPU有32位和16两种模式。如果通过16位模式启动,使用AX和CX等寄存器会十分方便,而EAX和ECX等32位的寄存器就会比较 麻烦。在不同的模式下机器语言的命令代码不一样,解释方法不一样,故16位模式的机器语言在32位模式下不能运行。另外,32位下可以使用CPU的自我保护功能(识别出可疑的机器 语言并进行屏蔽,以避免破坏系统)。
8.第三版\03_day\harib00h:
1)ipl10.nas
2)haribote.nas代码
; BOOT_INFO
CYLS EQU 0x0ff0 ;设定启动区
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 关于颜色数目的信息。颜色的位数
SCRNX EQU 0x0ff4 ; 分辨率X
SCRNY EQU 0x0ff6 ; 分辨率Y
VRAM EQU 0x0ff8 ; 图形缓冲区的开始地址
ORG 0xc200 ; 程序被装载到内容中的地址
MOV AL,0x13 ; VGA显卡,320*200*8位彩色
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
3)bootpack.c:
void Harimain(void)
{
fin:
goto fin;/*这里不支持嵌入式汇编,此处就是死循环,耗电*/
}
4)将bootpack.c转换为sys文件的步骤:
a.使用ccl.exe将bootpack.c生成bootpack.gas
b.使用gas2nask.exe将bootpack.gas生成bootpack.nas
c.使用nask.exe将bootpack.nas生成bootpack.obj
d.使用obi2bim.exe将bootpack.obj生成bootpack.bim
e.使用bim2hrb.exe将bootpack.bim生成bootpack.hrb
f.使用copy指令将asmhead.bin与bootpack.hrb结合生成baribote.sys文件
5)执行make run
9.最终版 第三版\03_day\harib00j:
1)ipl10.nas
2)asmhead.nas:
;此程序复杂,后面讲解
; haribote-os boot asm
; TAB=4
BOTPAK EQU 0x00280000 ; 加载bootpack
DSKCAC EQU 0x00100000 ; 磁盘缓存的位置
DSKCAC0 EQU 0x00008000 ; 磁盘缓存的位置(实模式)
; BOOT_INFO相关
CYLS EQU 0x0ff0 ; 引导扇区设置
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 关于颜色的信息
SCRNX EQU 0x0ff4 ; 分辨率X
SCRNY EQU 0x0ff6 ; 分辨率Y
VRAM EQU 0x0ff8 ; 图像缓冲区的起始地址
ORG 0xc200 ; 这个的程序要被装载的内存地址
; 画面モードを設定
MOV AL,0x13 ; VGA显卡,320x200x8bit
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ; 屏幕的模式(参考C语言的引用)
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
; 通过BIOS获取指示灯状态
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
; 防止PIC接受所有中断
; AT兼容机的规范、PIC初始化
; 然后之前在CLI不做任何事就挂起
; PIC在同意后初始化
MOV AL,0xff
OUT 0x21,AL
NOP ; 不断执行OUT指令
OUT 0xa1,AL
CLI ; 进一步中断CPU
; 让CPU支持1M以上内存、设置A20GATE
CALL waitkbdout
MOV AL,0xd1
OUT 0x64,AL
CALL waitkbdout
MOV AL,0xdf ; enable A20
OUT 0x60,AL
CALL waitkbdout
; 保护模式转换
[INSTRSET "i486p"] ; 说明使用486指令
LGDT [GDTR0] ; 设置临时GDT
MOV EAX,CR0
AND EAX,0x7fffffff ; 使用bit31(禁用分页)
OR EAX,0x00000001 ; bit0到1转换(保护模式过渡)
MOV CR0,EAX
JMP pipelineflush
pipelineflush:
MOV AX,1*8 ; 写32bit的段
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV SS,AX
; bootpack传递
MOV ESI,bootpack ; 源
MOV EDI,BOTPAK ; 目标
MOV ECX,512*1024/4
CALL memcpy
; 传输磁盘数据
; 从引导区开始
MOV ESI,0x7c00 ; 源
MOV EDI,DSKCAC ; 目标
MOV ECX,512/4
CALL memcpy
; 剩余的全部
MOV ESI,DSKCAC0+512 ; 源
MOV EDI,DSKCAC+512 ; 目标
MOV ECX,0
MOV CL,BYTE [CYLS]
IMUL ECX,512*18*2/4 ; 除以4得到字节数
SUB ECX,512/4 ; IPL偏移量
CALL memcpy
; 由于还需要asmhead才能完成
; 完成其余的bootpack任务
; bootpack启动
MOV EBX,BOTPAK
MOV ECX,[EBX+16]
ADD ECX,3 ; ECX += 3;
SHR ECX,2 ; ECX /= 4;
JZ skip ; 传输完成
MOV ESI,[EBX+20] ; 源
ADD ESI,EBX
MOV EDI,[EBX+12] ; 目标
CALL memcpy
skip:
MOV ESP,[EBX+12] ; 堆栈的初始化
JMP DWORD 2*8:0x0000001b
waitkbdout:
IN AL,0x64
AND AL,0x02
JNZ waitkbdout ; AND结果不为0跳转到waitkbdout
RET
memcpy:
MOV EAX,[ESI]
ADD ESI,4
MOV [EDI],EAX
ADD EDI,4
SUB ECX,1
JNZ memcpy ; 运算结果不为0跳转到memcpy
RET
; memcpy地址前缀大小
ALIGNB 16
GDT0:
RESB 8 ; 初始值
DW 0xffff,0x0000,0x9200,0x00cf ; 写32bit位段寄存器
DW 0xffff,0x0000,0x9a28,0x0047 ; 可执行的文件的32bit寄存器(bootpack用)
DW 0
GDTR0:
DW 8*3-1
DD GDT0
ALIGNB 16
bootpack:
3)naskfunc.nas:
[FORMAT "WCOFF"] ; 制作目标文件的模式
[BITS 32] ; 制作32位模式用的机器语言
; 制作目标文件的信息
[FILE "naskfunc.nas"] ; 程序中包含的函数名
GLOBAL _io_hlt ;
; 以下是实际的函数
[SECTION .text]
_io_hlt: ; void io_hlt(void);C语言从外部调用
HLT
RET
4) bootpack.c:
/* 告诉C编译器,有一个函数在别的文件里 */
void io_hlt(void); /* 实现在naskfunc.nas*/
/* 是函数声明却不用{},而用;,这表示的意思是:
函数在别的文件中,你自己找一下 */
void HariMain(void)
{
fin:
io_hlt(); /* 执行naskfunc.nas中的_io_hlt函数 */
goto fin;
}
5) 执行make run,成功
10.知识点总结
1) bios int 10h 中断
2)C调用汇编
3)进入保护模式(asmhead.nas中,但本节未讲解,留到后面章节)
4)编译链接,最终形成sys文件,每个过程涉及的文件格式需要了解或者深入研究

浙公网安备 33010602011771号