《30天自制操作系统》——走出保护模式的困境

现在我在吃饭,假设我是程序而我前面的桌子是数据。我知道桌子在哪,我也知道我的嘴在哪。我伸出筷子去夹菜,在保护模式下,程序段基址和数据段基址不一样,导致我前方的时空扭曲了,筷子伸到了口大丁桌子上而我并不知道,所以上帝要把数据拷贝到口大丁的桌子上。那么既然我面前的桌子没用能不能不要了呢?也不行,因为桌子没了,我就移到了桌子的位置,而我收回的手就只能把菜送到自己的后脑勺了。
我是个吃货,不光胖自己身后桌子上的东西也不放过 ( 坑爹的 gcc 会把字符常量放在代码段的后面 ) 。我把筷子伸向背后,空间又扭曲了,筷子出现的位置离口大丁桌子的距离和我一样宽。上帝没法区分我和我背后的桌子,他干脆把我和桌子一起拷贝过去 ( 因为程序长度不一定,稍微改动源码长度就会变 ) ,这样一来数据段中和我体型一样宽的地区都被浪费了。
数据段基址一般是 0 ,否则操作一些硬件就会比较麻烦,比如显存从 0xa0000 开始,如果数据段基址不为 0 ,代码在访问显存时就需要在原来的地址上减去这个数。但从 0 开始的问题就在于,上帝 ( 处于实模式,在我运行之前运行并结束的负责复制内存和设置全局变量表的程序 ) 一般离 0 不会太远,如果我实在太胖,那么上帝在复制我和桌子的过程中很可能把他自己也盖住了,上帝创世未捷身先死那我也不存在了。
可以在我和桌子前面加上一段为上帝留的空间 0x0000-0x9000 ,这下上帝不怕被覆盖了。同时注意,在可执行文件中我的代码不能离上帝太近,否则我的复制可能盖住我自己,假如上帝代码从 0x8000 开始,应该在我和上帝之间留出 0x1000 的间隔。
这顿饭不容易啊,还是不要当吃货了。

程序更改如下:
ipl.nas中改成jmp 0x8200,你应该知道改什么地方。

程序: asmhead.nas
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; haribote-os boot asm
; TAB=4

BOTPAK    EQU        0x00280000        ; 主程序地址
DSKCAC    EQU        0x00100000        ; 
DSKCAC0    EQU        0x00008000        ;
DATPAK    EQU        0x00000000        ; 程序数据地址

; 有关BOOT_INFO
CYLS    EQU    0x0ff0        ; 设定启动区
LEDS    EQU    0x0ff1        ; 键盘状态
VMODE    EQU    0x0ff2        ; 颜色位数
SCRNX    EQU    0x0ff4        ; 分辨率X (screen x)
SCRNY    EQU    0x0ff6        ; 分辨率Y (screen y)
VRAM    EQU    0x0ff8        ; 图像缓冲区开始地址,显卡内存

        ORG        0x8200        

; 初始化栈地址
        mov        esp,0x8000

; 设定Graphic Mode
        MOV        AL,0x13            ; VGA显卡,320x200x8位彩色    
        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

; PIC 可编程中断控制器 有两个PIC 每个PIC有8个输入0-7 处理为1,忽略为0
; cli关闭所有中断,sti打开所有中断


        MOV        AL,0xff
        OUT        0x21,AL            ; PCI1 data
        NOP                    ; 太快可能会有问题
        OUT        0xa1,AL            ; PCI2 data

        CLI                    ; 关闭全部中断

; 蛋疼的键盘A20 address enable

        CALL    waitkbdout
        MOV        AL,0xd1
        OUT        0x64,AL
        CALL    waitkbdout
        MOV        AL,0xdf            ; enable A20
        OUT        0x60,AL
        CALL    waitkbdout    

; 切换到保护模式
        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        ; 栈段

; 主程序加载到0x280000

        MOV        ESI,bootpack+0x1000    
        MOV        EDI,BOTPAK+0x9000        
        MOV        ECX,0x77000/4
        CALL    memcpy
; 数据加载到0x000000

        MOV        esi,bootpack+0x1000
        mov        eax,DATPAK+0x9000
        MOV        edi,eax
        MOV        ecx,0x77000/4    ; 小心不要覆盖引导区
        CALL    memcpy

; 跳转主程序

        MOV        ESP,0x9ffff    ; 设置栈地址
        JMP        DWORD 2*8:0x0000f000    ; 跳转到0x280000

waitkbdout:
        IN         AL,0x64
        AND         AL,0x02        ; cpu可向键盘写命令时为1
        JNZ        waitkbdout        ; 
        RET

memcpy:
        MOV        EAX,[ESI]
        ADD        ESI,4
        MOV        [EDI],EAX
        ADD        EDI,4
        SUB        ECX,1
        JNZ        memcpy    
        RET
; 全局变量表

        ALIGNB    16    ; 16字节对齐 bss段
GDT0:
        RESB    8                    ; 第一项为0,这是规定
        DW        0xffff,0x0000,0x9200,0x00cf    ; 整个内存
        DW        0xffff,0x0000,0x9a28,0x0047    ; 程序段

        DW        0
GDTR0:
        DW        8*3-1        ; 表的大小(字节)减1
        DD        GDT0        ; 表的地址

        ALIGNB    16
bootpack:
        resb    0x1000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

程序: Makefile
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
default:
    make run

run: ipl.nas asmhead.nas func.nas bootpack.c stdio.c
    nasm -f bin ipl.nas -o ipl.img -l ipl.lst
    nasm -f bin asmhead.nas -o asmhead.o -l asmhead.lst
    i386-elf-gcc -Wall -c bootpack.c -o bootpack.o
    nasm -f elf func.nas -o func.o -l func.lst
    i386-elf-ld -Tdata 0x9000 -Ttext 0xf000 bootpack.o func.o -o bootpack 
    i386-elf-objcopy -S -O binary bootpack bootpack.bin
    cat bootpack.bin >> asmhead.o 
    dd if=asmhead.o of=ipl.img bs=512 seek=1 conv=notrunc
    qemu-system-i386 -fda ipl.img -boot a
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值