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

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
原文地址:https://www.cnblogs.com/rixiang/p/5349667.html