• 自制操作系统 (三) 从启动区执行操作系统并进入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
  • 相关阅读:
    WPF Image控件的Source属性是一个ImageSource对象
    wx:if 与hidden
    切换远程分支
    异步请求(简单一说)
    多维数组降维方法,简单一提
    3.25发版之最后的搜索框
    wepy-城市按字母排序
    new一个新对象。。。对象???
    参数函数是对象的理解
    群辉 MariaDB 10 远程连接
  • 原文地址:https://www.cnblogs.com/rixiang/p/5349667.html
Copyright © 2020-2023  润新知