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 0 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:512 from:0 to:0 copy from:haribote.sys to:@: imgout:haribote.img img : $(MAKE) haribote.img run : $(MAKE) img $(COPY) haribote.img ..z_toolsqemufdimage0.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,会发现:
作者得出的结论:
一般向一个空软盘保存文件时
(1)文件名会写在0x002600以后的地方;
(2)文件内容会写在0x004200以后的地方。
所以,要执行磁盘映像上位于0x004200号地址的程序,现在的程序是从启动区开始,把磁盘上的内容装载到内存0x8000号地址,所以磁盘0x4200处的内容就应该位于内存0x8000+0x4200=0xc200号地址。
所以从启动区,启动操作系统就只需要jmp到0xc200地址即可。
这两张图可以简单解释原理(第二张是我画的):
ipl.asm:
; author: 无 名 ; date: 2016.05.31 06.01 06.02 06.03 ; description: bootsector CYLS equ 10 org 07c00h ;以下,FAT12格式引导程序专用代码 JMP entry DB 0x90 DB "HELLOIPL" ; name DW 512 ; size of a sector(must be 512byte) DB 1 ; size of a cluster(must be a sector) DW 1 ; FAT begin from 1st sector DB 2 ; FAT num DW 224 ; size of root directory DW 2880 ; size of disk(2880 sectors) DB 0xf0 ; type of disk DW 9 ; length of FAT(must be 9 sectors) DW 18 ; how many sectors with a track DW 2 ; head num DD 0 ; 不使用分区 DD 2880 ; size of disk(2880 sectors) DB 0,0,0x29 DD 0xffffffff DB "HELLO-OS " DB "FAT12 " RESB 18 ; 先空出18字节 entry: mov ax,0 mov ss,ax mov sp,0x7c00 mov ds,ax ;read disk 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 ;a sector mov bx,0 mov dl,0x00 ;A驱动器 int 0x13 ;调用磁盘bios jnc next ;没出错跳转next add si,1 cmp si,5 ;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,1 cmp cl,18 jbe readloop ; if cl <= 18 readloopへ mov cl,1 add dh,1 cmp dh,2 jb readloop ; if dh < 2 readloopへ mov dh,0 add ch,1 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,1 cmp al,0 ; if al == 0 finへ je fin mov ah,0x0e mov bx,15 int 0x10 jmp putloop PrintStr: mov ax,BootPoem ; print boot poem mov bp,ax
mov cx,170 mov ax,01301h mov bx,00009h mov dh,10 mov dl,0 int 10h ret msg: db 0x0a, 0x0a db "load error" db 0x0a db 0 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 510-($-$$) db 0 ; 接下来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],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 ; 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,1*8 ; 可读写的段,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,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 ; main到此为止接下来是c_main ; c_main启动 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 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