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

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

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_tools\qemu\fdimage0.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

 

 

转载于:https://www.cnblogs.com/rixiang/p/5419129.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值