自制操作系统 (三) 从启动区执行操作系统并进入C世界

时间:2024-09-19 15:07:44

qq:992591601 欢迎交流

2016.04.03 2016.05.31 2016.06.29

这一章是有些复杂的,我不太懂作者为什么要把这么多内容都放进一天。

1读入了十个柱面

2从启动区执行操作系统

3进入32位

4导入C语言

makefile的内容:

TOOLPATH = ../z_tools/
INCPATH = ../z_tools/haribote/ MAKE = $(TOOLPATH)make.exe -r
NASK = $(TOOLPATH)nask.exe
NASM = $(TOOLPATH)nasm.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 ipl.bin : ipl.asm Makefile
$(NASM) ipl.asm -o ipl.bin main.bin : main.nas Makefile
$(NASK) main.nas main.bin main.lst c_main.gas : c_main.c Makefile
$(CC1) -o c_main.gas c_main.c c_main.nas : c_main.gas Makefile
$(GAS2NASK) c_main.gas c_main.nas c_main.obj : c_main.nas Makefile
$(NASK) c_main.nas c_main.obj c_main.lst assemblyFunc.obj : assemblyFunc.nas Makefile
$(NASK) assemblyFunc.nas assemblyFunc.obj assemblyFunc.lst c_main.bim : c_main.obj assemblyFunc.obj Makefile
$(OBJ2BIM) @$(RULEFILE) out:c_main.bim stack:3136k map:c_main.map \
c_main.obj assemblyFunc.obj
# 3MB+64KB=3136KB c_main.hrb : c_main.bim Makefile
$(BIM2HRB) c_main.bim c_main.hrb haribote.sys : main.bin c_main.hrb Makefile
copy /B main.bin+c_main.hrb haribote.sys haribote.img : ipl.bin haribote.sys Makefile
$(EDIMG) imgin:../z_tools/fdimg0at.tek \
wbinimg src:ipl.bin len: from: to: \
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) c_main.nas
-$(DEL) c_main.map
-$(DEL) c_main.bim
-$(DEL) c_main.hrb
-$(DEL) haribote.sys src_only :
$(MAKE) clean
-$(DEL) haribote.img

注意end.exe,在第二天,作者提到:自己开发的磁盘映像管理工具end.exe,先读入一个空白的磁盘映像文件,然后在开头写入ipl.bin的内容,最后将结果输出为名为helloos.img的磁盘映像文件。

这样一来,逻辑上就很简单了,用makefile,调用end.exe,将ipl10.nas和操作系统代码,制成一个img文件。ipl10.nas是启动区。最后操作系统的启动是,在启动区启动。

然后我们用工具观察制成的img,会发现:

自制操作系统 (三) 从启动区执行操作系统并进入C世界

自制操作系统 (三) 从启动区执行操作系统并进入C世界

作者得出的结论:

一般向一个空软盘保存文件时

(1)文件名会写在0x002600以后的地方;

(2)文件内容会写在0x004200以后的地方。

所以,要执行磁盘映像上位于0x004200号地址的程序,现在的程序是从启动区开始,把磁盘上的内容装载到内存0x8000号地址,所以磁盘0x4200处的内容就应该位于内存0x8000+0x4200=0xc200号地址。

所以从启动区,启动操作系统就只需要jmp到0xc200地址即可。

这两张图可以简单解释原理(第二张是我画的):

自制操作系统 (三) 从启动区执行操作系统并进入C世界

自制操作系统 (三) 从启动区执行操作系统并进入C世界

ipl.asm:

; author:      无    名
; date: 2016.05.31 06.01 06.02 06.03
; description: bootsector CYLS equ
org 07c00h ;以下,FAT12格式引导程序专用代码
JMP entry
DB 0x90
DB "HELLOIPL" ; name
DW ; size of a sector(must be 512byte)
DB ; size of a cluster(must be a sector)
DW ; FAT begin from 1st sector
DB ; FAT num
DW ; size of root directory
DW ; size of disk(2880 sectors)
DB 0xf0 ; type of disk
DW ; length of FAT(must be 9 sectors)
DW ; how many sectors with a track
DW ; head num
DD ; 不使用分区
DD ; size of disk(2880 sectors)
DB ,,0x29
DD 0xffffffff
DB "HELLO-OS "
DB "FAT12 "
RESB ; 先空出18字节 entry:
mov ax,
mov ss,ax
mov sp,0x7c00
mov ds,ax ;read disk mov ax,0x0820
mov es,ax
mov ch, ;柱面0
mov dh, ;磁头0
mov cl, ;扇区2 readloop:
mov si, ;记录失败次数 retry:
mov ah,0x02 ;ah=0x02:读入磁盘
mov al, ;a sector
mov bx,
mov dl,0x00 ;A驱动器
int 0x13 ;调用磁盘bios
jnc next ;没出错跳转next
add si,
cmp si, ;if si > 5 errorへ
jae error
mov ah,0x00
mov dl,0x00
int 0x13 ;重置驱动器
jmp retry next:
mov ax,es
add ax,0x0020
mov es,ax
add cl,
cmp cl,
jbe readloop ; if cl <= 18 readloopへ
mov cl,
add dh,
cmp dh,
jb readloop ; if dh < 2 readloopへ
mov dh,
add ch,
cmp ch,CYLS
jb readloop ; if ch < CYLS readloopへ ;mov ax,cs ; print boot poem
;mov ds,ax
;mov es,ax
;call PrintStr
;jmp $ mov [0x0ff0],ch
jmp 0xc200 fin:
hlt
jmp fin error:
mov si,msg putloop:
mov al,[si]
add si,
cmp al, ; if al == 0 finへ
je fin
mov ah,0x0e
mov bx,
int 0x10
jmp putloop PrintStr:
mov ax,BootPoem ; print boot poem
mov bp,ax
mov cx,
mov ax,01301h
mov bx,00009h
mov dh,
mov dl,
int 10h
ret msg:
db 0x0a, 0x0a
db "load error"
db 0x0a
db BootPoem:
db "Hold fast to dreams"
db 0x0a
db "For if dreams die"
db 0x0a
db "Life is a broken-winged bird "
db 0x0a
db "That can never fly"
db 0x0a
db "Hold fast to dreams"
db 0x0a
db "For when dreams go"
db 0x0a
db "Life is a barren field"
db 0x0a
db "Frozen only with snow"
db 0x0a times -($-$$) db ; 接下来510字节写0
dw 0xaa55 ; 最后一个字0xaa55是引导程序结束标志

main.nas

; sonn-os boot asm
; TAB=4
; author: 无 名
; date: 2016.06.03 2016.06.05
; description: jmp to 32bit BOTPAK EQU 0x00280000
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显卡,320*200*8位彩色
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE], ; 记录画面模式
MOV WORD [SCRNX],
MOV WORD [SCRNY],
MOV DWORD [VRAM],0x000a0000 ; 用BIOS取得键盘上各种LED指示灯的状态 MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL ; PIC关闭一切中断
; 根据AT兼容机的规格、初始化PIC
; 必须在CLI之前进行,到CLI挂起
; 随后进行PIC初始化 MOV AL,0xff
OUT 0x21,AL
NOP ; 如果连续进行OUT命令,有些机种不可以
OUT 0xa1,AL CLI ; 禁止CPU级别中断 ; OPEN 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设0,禁止分页
OR EAX,0x00000001 ; bit0设1,为了切换到保护模式
MOV CR0,EAX
JMP pipelineflush
pipelineflush:
MOV AX,* ; 可读写的段,32bit
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV SS,AX ; 转移 MOV ESI,c_main
MOV EDI,BOTPAK
MOV ECX,*/
CALL memcpy MOV ESI,0x7c00
MOV EDI,DSKCAC
MOV ECX,/
CALL memcpy MOV ESI,DSKCAC0+
MOV EDI,DSKCAC+
MOV ECX,
MOV CL,BYTE [CYLS]
IMUL ECX,**/
SUB ECX,/
CALL memcpy ; main到此为止接下来是c_main ; c_main启动 MOV EBX,BOTPAK
MOV ECX,[EBX+]
ADD ECX, ; ECX += 3;
SHR ECX, ; ECX /= 4;
JZ skip
MOV ESI,[EBX+]
ADD ESI,EBX
MOV EDI,[EBX+]
CALL memcpy
skip:
MOV ESP,[EBX+]
JMP DWORD *:0x0000001b waitkbdout:
IN AL,0x64
AND AL,0x02
JNZ waitkbdout
RET memcpy:
MOV EAX,[ESI]
ADD ESI,
MOV [EDI],EAX
ADD EDI,
SUB ECX,
JNZ memcpy
RET ALIGNB
GDT0:
RESB
DW 0xffff,0x0000,0x9200,0x00cf
DW 0xffff,0x0000,0x9a28,0x0047 DW
GDTR0:
DW *-
DD GDT0 ALIGNB
c_main:

这里前一部分是对32位模式的启动,后一部分是对C语言的准备。

其实不是很好理解,等我理解了再回来补充吧。

。。EQU。。这样的语句其实就相当于C语言中的#define语句。

mov dword[vram],0x000a0000;

vram,显卡内存,也就是用于显示画面的内存,将之存储0xa0000,也就是种对画面的设置了。具体暂时不讨论。

这段代码应该还涉及16位进入32位,GDT寻址等等,这么重要的东西,作者都没有讲。无语。

c_main.c:

void io_hlt(void);

void HariMain(void)
{ fin:
io_hlt();
goto fin; }

c_main.c调用的汇编函数在assemblyFunc.nas中,这需要一个编译(将C语言编译为汇编文件c_main.nas)、链接(两个.nas汇编文件)的过程

_io_hlt:    ; void io_hlt(void);
HLT
RET