• 自制操作系统(五) 保护模式寻址原理、字符鼠标指针显示


    首先就是对前一天程序的一些优化,引入了结构体:

    struct BOOTINFO {
        char cyls, leds, vmode, reserve;
        short scrnx, scrny;
        char *vram;
    };
    
    void HariMain(void)
    {
        char *vram;
        int xsize, ysize;
        struct BOOTINFO *binfo;
    
        init_palette();
        binfo = (struct BOOTINFO *) 0x0ff0;
        xsize = (*binfo).scrnx;
        ysize = (*binfo).scrny;
        vram = (*binfo).vram;
    
        init_screen(vram, xsize, ysize);
    
        for (;;) {
            io_hlt();
        }
    }

    这里面用到的结构体BOOTINFO其实只是一个存储单元。

    binfo = (struct BOOTINFO *) 0x0ff0;这句获取0x0ff0开始一连串值。

    作用相当于binfo_scrnx = (short *)0xff4;等几句。

    另外,作为参数时,可以不用传入那么多参数。

    之后就是,字符显示:

    void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
    {
        int i;
        char *p, d /* data */;
        for (i = 0; i < 16; i++) {
            p = vram + (y + i) * xsize + x;
            d = font[i];
            if ((d & 0x80) != 0) { p[0] = c; }
            if ((d & 0x40) != 0) { p[1] = c; }
            if ((d & 0x20) != 0) { p[2] = c; }
            if ((d & 0x10) != 0) { p[3] = c; }
            if ((d & 0x08) != 0) { p[4] = c; }
            if ((d & 0x04) != 0) { p[5] = c; }
            if ((d & 0x02) != 0) { p[6] = c; }
            if ((d & 0x01) != 0) { p[7] = c; }
        }
        return;
    }

    这段代码显示字符A,具体原理就是用for语句将8个像素的程序循环16遍。需要在HariMain里,

    初始化

        static char font_A[16] = {

            0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,

            0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00

        };

    作为参数。

    一个A就出现这么多代码,肯定不行。所以作者引入了OSASK字体。加入hankaku.txt文件。另需编译器makefont.exe将之编译为hankaku.bin文件。

    新的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
    MAKEFONT = $(TOOLPATH)makefont.exe
    BIN2OBJ  = $(TOOLPATH)bin2obj.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
    
    hankaku.bin : hankaku.txt Makefile
        $(MAKEFONT) hankaku.txt hankaku.bin
    
    hankaku.obj : hankaku.bin Makefile
        $(BIN2OBJ) hankaku.bin hankaku.obj _hankaku
    
    c_main.bim : c_main.obj assemblyFunc.obj hankaku.obj Makefile
        $(OBJ2BIM) @$(RULEFILE) out:c_main.bim stack:3136k map:c_main.map 
            c_main.obj assemblyFunc.obj hankaku.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

    之后有了显示字符和字符串的函数:

    void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
    {
        int i;
        char *p, d /* data */;
        for (i = 0; i < 16; i++) {
            p = vram + (y + i) * xsize + x;
            d = font[i];
            if ((d & 0x80) != 0) { p[0] = c; }
            if ((d & 0x40) != 0) { p[1] = c; }
            if ((d & 0x20) != 0) { p[2] = c; }
            if ((d & 0x10) != 0) { p[3] = c; }
            if ((d & 0x08) != 0) { p[4] = c; }
            if ((d & 0x04) != 0) { p[5] = c; }
            if ((d & 0x02) != 0) { p[6] = c; }
            if ((d & 0x01) != 0) { p[7] = c; }
        }
        return;
    }
    
    void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
    {
        extern char hankaku[4096];
        for (; *s != 0x00; s++) {
            putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
            x += 8;
        }
        return;
    }

    鼠标的显示就是一样的道理了:

    void init_mouse_cursor8(char *mouse, char bc)
    /* 准备鼠标指针 */
    {
        static char cursor[16][16] = {
            "**************..",
            "*OOOOOOOOOOO*...",
            "*OOOOOOOOOO*....",
            "*OOOOOOOOO*.....",
            "*OOOOOOOO*......",
            "*OOOOOOO*.......",
            "*OOOOOOO*.......",
            "*OOOOOOOO*......",
            "*OOOO**OOO*.....",
            "*OOO*..*OOO*....",
            "*OO*....*OOO*...",
            "*O*......*OOO*..",
            "**........*OOO*.",
            "*..........*OOO*",
            "............*OO*",
            ".............***"
        };
        int x, y;
    
        for (y = 0; y < 16; y++) 
        {
            for (x = 0; x < 16; x++)
            {
                if (cursor[y][x] == '*')
                {
                    mouse[y * 16 + x] = COL8_000000;
                }
                if (cursor[y][x] == 'O') 
                {
                    mouse[y * 16 + x] = COL8_FFFFFF;
                }
                if (cursor[y][x] == '.') 
                {
                    mouse[y * 16 + x] = bc;
                }
            }
        }
        return;
    }
    
    void putblock8_8(char *vram, int vxsize, int pxsize,
        int pysize, int px0, int py0, char *buf, int bxsize)
    {
        int x, y;
        for (y = 0; y < pysize; y++)
        {
            for (x = 0; x < pxsize; x++) 
            {
                vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x];
            }
        }
        return;
    }

    此时c_main.c里main函数如下:

    void HariMain(void)
    {
        struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
        char s[40], mcursor[256];
        int mx, my;
    
        init_palette();
        init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
        mx = (binfo->scrnx - 16) / 2; 
        my = (binfo->scrny - 28 - 16) / 2;
        init_mouse_cursor8(mcursor, COL8_008484);
        putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
        sprintf(s, "(%d, %d)", mx, my);
        putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
    
        for (;;)
        {
            io_hlt();
        }
    }

    可见,先初始化调色板、再初始化屏幕、再初始化鼠标,然后画鼠标,显示字符。

      

    这章涉及保护模式寻址,关于GDT。

    2016.06.06我已经非常透彻理解了保护模式寻址,画了下面的图:

          虽然人们在谈到中断(Interrupt)时,总会拿轮询(Polling)来做“反面”例子,但中断和轮询并不是完全对立的两个概念,呃,它们是对立统一的。
    “CPU执行完每条指令时,都会去检查一个中断标志位”,这句话是所有关于中断长篇大论的开场白,但很容易被人忽略,其实,这就是中断的本质。
    举个例子,CPU老板是一家公司的光杆司令,所有的顾客都要他亲自跑去处理,还要跟有关部门打点关系,CPU觉得顾客和公关这两样事它一个人搞不来,这就是轮询;终于这家公司升级发展了,CPU老板请了一个秘书,所有的顾客都先由秘书经手,CPU心情好的时候就去看一下,大部分时间都忙着去公关了,这时它觉得轻松了很多,这就是中断了~~
          也就是说,中断和轮询是从CPU老板的角度来看的,不管怎样,事件都还是有人来时刻跟踪才能被捕获处理,不过是老板还是秘书的问题。所有的中断(或者异步,回调等)背后都有一个轮询(循环,listener)。
     
    通往32位之路:
    PIC关闭一切中断                     
    ; PIC关闭一切中断
    ;    根据AT兼容机的规格、初始化PIC
    ;    必须在CLI之前进行,到CLI挂起
    ;    随后进行PIC初始化
    
            MOV        AL,0xff
            OUT        0x21,AL
            NOP                        ; 如果连续进行OUT命令,有些机种不可以
            OUT        0xa1,AL
    
            CLI                        ; 禁止CPU级别中断

    设置A20

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

    切换到保护模式。

    INSTRSET指令,是为了能够使用386以后的LGDT,EAX,CR0等关键字。

    通过带入CR0而切换到保护模式时,要马上执行JMP指令。因为变成保护模式后,机器语言的解释要发生变化。CPU为了加快指令的执行速度而使用了管道这一机制,就是说,前一条指令还在执行的时候,就开始解释下一条甚至是再下一条指令。因为模式变了,就得重新解释一遍,所以加入了JMP指令

    ; 保护模式
    
    [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
  • 相关阅读:
    VMware安装虚拟机(Ubuntu)
    鼠标拖拽事件
    css层叠样式表
    html--form表单常用操作
    python学习之HTML-table标签
    python之web前端day01
    字符串各种操作,关于字符串的内置函数
    正则中匹配次数的问题
    re模块
    Github网站打不开的问题
  • 原文地址:https://www.cnblogs.com/rixiang/p/5419129.html
Copyright © 2020-2023  润新知