从启动区执行操作系统
ipl:
; haribote-ipl
; TAB=4
CYLS EQU 10 ;
ORG 0x7c00 ; 我们将磁盘映像的启示512字节加载到Boot Sector Entry中
;
JMP entry
DB 0x90
DB "HARIBOTE" ;
DW 512 ;
DB 1 ;
DW 1 ;
DB 2 ;
DW 224 ;
DW 2880 ;
DB 0xf0 ;
DW 9 ;
DW 18 ;
DW 2 ;
DD 0 ;
DD 2880 ;
DB 0,0,0x29 ;
DD 0xffffffff ;
DB "HARIBOTEOS " ;
DB "FAT12 " ;
RESB 18 ;
;
entry:
MOV AX,0 ; init
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
;
MOV AX,0x0820
MOV ES,AX
MOV CH,0 ; 柱面 0
MOV DH,0 ; 磁头 0
MOV CL,2 ; 扇区 2(扇区从1开始到18)
readloop:
MOV SI,0 ;
retry:
MOV AH,0x02 ; AH=0x02 :
MOV AL,1 ;
MOV BX,0
MOV DL,0x00 ;
INT 0x13 ;
JNC next ;
ADD SI,1 ;
CMP SI,5 ;
JAE error ; SI >= 5
MOV AH,0x00
MOV DL,0x00 ;
INT 0x13 ;
JMP retry
next:
MOV AX,ES ;
ADD AX,0x0020
MOV ES,AX ; ADD ES,0x020 写入位置向后移动512字节
ADD CL,1 ; 写入扇区+1
CMP CL,18 ; CL大于18 ?
JBE readloop ; CL <= 18
MOV CL,1
ADD DH,1
CMP DH,2 ;CL=1 磁头+=1 磁头>=2 ?
JB readloop ; DH < 2
MOV DH,0
ADD CH,1
CMP CH,CYLS ;CYLS=10 我们要读取10个柱面
JB readloop ; CH < CYLS
; 执行到这里说明我们读取完成
; 10*2*18*512/1024=180KB 我们将磁盘中180KB读取到0x08200~0x34fff
; 那0x08000~0x081fff 呢 这部分是留给启动区的 要将这512字节留给启动区
; 通过我们的观察 操作系统的代码的启示位置位于文件偏移的0x4200处,加载后的内存位置应该是
; 0x8000+0x4200=0xc200
; 所以这里我们要跳转到操作系统的代码起始处 c200
MOV [0x0ff0],CH ; CH=10
JMP 0xc200
;到这里
error:
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ;
CMP AL,0
JE fin
MOV AH,0x0e ;
MOV BX,15 ;
INT 0x10 ;
JMP putloop
fin:
HLT ;
JMP fin ;
msg:
DB 0x0a, 0x0a ;
DB "load error"
DB 0x0a ;
DB 0
RESB 0x7dfe-$ ;
DB 0x55, 0xaa
启动区的制作大体就是这样 读取我们的操作系统 然后跳转到操作系统代码的入口处。
这里示例的代码很简单。
haribote.nas
; haribote-os
; TAB=4
ORG 0xc200 ; 写入内存位置
MOV AL,0x13 ; al=13 调用显卡设置模式
; al=0x13 VGA图形模式 320*200*8 彩色模式 调色板模式
MOV AH,0x00
INT 0x10
fin:
HLT
JMP fin
32位模式前期准备
一旦使用32位模式就不能调用BIOS功能了,因为BIOS是用16位机器语言写的,在进入BIOS前 我们要获取我们想获取的信息。
; haribote-os
; TAB=4
; BOOT_INFO
CYLS EQU 0x0ff0 ; 把我们需要的信息保存在0X0FF* 的区域
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ;
SCRNX EQU 0x0ff4 ;
SCRNY EQU 0x0ff6 ;
VRAM EQU 0x0ff8 ;
ORG 0xc200 ;
MOV AL,0x13 ;
MOV AH,0x00
INT 0x10 ;VGA显卡 320*200*8
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
BIOS内存分布图
接下来我们直接切换到32位模式 然后运行C语言写的程序。
所以haribote.sys 前半部分是由汇编写的 后面是用c语言写的。汇编的后半部分使我们切换到32位模式的代码,暂时不作讲解。
接下来是c语言部分。
比如:
void HariMain(void)
{
fin:
/* */
goto fin;
}
程序一直在死循环中。
那么bootpack.c是怎么样变成机器语言呢?
- ccl.exe c编译器 bootpack.c –> bootpack.gas
- gas2nask.exe 汇编格式转换 bootpack.gas –> bootpack.nas
- nask.exe 汇编编译为目标文件 bootpack.nas –> bootpack.obj
- obj2bim.exe 链接生成二进制映像文件替代品 bootpack.obj –> bootpack.bim
- bim2hrb.exe bootpack.bim –> bootpack.hrb
此时c语言就变成了机器语言 使用copy指令将asmhead.bin 与bootpack.hrb 结合起来,成为haribote.sys
程序是从以HariMain命名的函数开始运行的。
为了不要我们程序进入死循环中,我们想办法让程序进入HLT状态
; naskfunc
; TAB=4
[FORMAT "WCOFF"] ;
[BITS 32] ;
;
[FILE "naskfunc.nas"] ;
GLOBAL _io_hlt ;
;
[SECTION .text] ;
_io_hlt: ; void io_hlt(void);
HLT
RET
显而易见,我们添加了io_hlt函数的实现 它的行为很简单,使计算机处于HLT状态。
同时看一下c文件
void io_hlt(void);
void HariMain(void)
{
fin:
io_hlt();
goto fin;
}
可以同时看一下我们的编译规则
bootpack.bim : bootpack.obj naskfunc.obj Makefile
$(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \
bootpack.obj naskfunc.obj
把汇编文件和c文件链接到一起即可。
make run 运行,大功告成。
最后附上asmhead.nas, (切换到32位模式)
; haribote-os boot asm
; TAB=4
BOTPAK EQU 0x00280000 ;
DSKCAC EQU 0x00100000 ;
DSKCAC0 EQU 0x00008000 ;
; BOOT_INFO娭學
CYLS EQU 0x0ff0 ;
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ;
SCRNX EQU 0x0ff4 ;
SCRNY EQU 0x0ff6 ;
VRAM EQU 0x0ff8 ;
ORG 0xc200 ;
;
MOV AL,0x13 ;
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ;
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
;
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
;
;
;
;
MOV AL,0xff
OUT 0x21,AL
NOP ;
OUT 0xa1,AL
CLI ;
;
CALL waitkbdout
MOV AL,0xd1
OUT 0x64,AL
CALL waitkbdout
MOV AL,0xdf ; enable A20
OUT 0x60,AL
CALL waitkbdout
; 僾儘僥僋僩儌乕僪堏峴
[INSTRSET "i486p"] ;
LGDT [GDTR0] ;
MOV EAX,CR0
AND EAX,0x7fffffff ;
OR EAX,0x00000001 ;
MOV CR0,EAX
JMP pipelineflush
pipelineflush:
MOV AX,1*8 ;
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 ;
SUB ECX,512/4 ;
CALL memcpy
;
;
;
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 ;
RET
memcpy:
MOV EAX,[ESI]
ADD ESI,4
MOV [EDI],EAX
ADD EDI,4
SUB ECX,1
JNZ memcpy ;
RET
;
ALIGNB 16
GDT0:
RESB 8 ;
DW 0xffff,0x0000,0x9200,0x00cf ;
DW 0xffff,0x0000,0x9a28,0x0047 ;
DW 0
GDTR0:
DW 8*3-1
DD GDT0
ALIGNB 16
bootpack: