5、结构体、文字显示与GDT/IDT初始化

接收启动信息
bootpack.c节选

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
    char *vram;  //BYTE类型地址
    int xsize, ysize;
    short *binfo_scrnx, *binfo_scrny;  //WORD类型地址
    int *binfo_vram;   //DWORD类型地址

    init_palette();    //设置调色板
    binfo_scrnx = (short *) 0x0ff4;   //0x0ff4是为了与asmhead.nas保持一致出现的
    binfo_scrny = (short *) 0x0ff6;
    binfo_vram = (int *) 0x0ff8;
    xsize = *binfo_scrnx;
    ysize = *binfo_scrny;
    vram = (char *) *binfo_vram;

    init_screen(vram, xsize, ysize);  //显示背景

    for (;;) 
    {
        io_hlt();
    }
}

试用结构体
bootpack.c节选

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();
    }
}

试用箭头记号
bootpack.c节选

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
    struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;

    init_palette();
    init_screen(binfo->vram, binfo->scrnx, binfo->scrny); //显示背景 

    for (;;) 
    {
        io_hlt();
    }
}

显示字符
bootpack.c节选

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
    struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
    static char font_A[16] =  //存储字符A
    {       
        0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
        0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
    };

    init_palette();
    init_screen(binfo->vram, binfo->scrnx, binfo->scrny); //显示背景 
    putfont8(binfo->vram, binfo->scrnx, 10, 10, COL8_FFFFFF, font_A);  //输出字符A

    for (;;) 
    {
        io_hlt();
    }
}

//x,y表示起始像素点位置
void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
    int i;
    char *p, d;
    for (i = 0; i < 16; i++)  //字符的存储:每行8位,共16行,所以一个字符用16个字节
    {
        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;
}

make run
这里写图片描述

增加字体
沿用OSASK的字体数据
hankaku.txt中的节选


char 0x41
........
...**...
...**...
...**...
...**...
..*..*..
..*..*..
..*..*..
..*..*..
.******.
.*....*.
.*....*.
.*....*.
***..***
........
........

char 0x42
........
****....
.*..*...
.*...*..
.*...*..
.*...*..
.*..*...
.****...
.*...*..
.*....*.
.*....*.
.*....*.
.*...*..
*****...

Makefile中的变动

MAKEFONT = $(TOOLPATH)makefont.exe
BIN2OBJ  = $(TOOLPATH)bin2obj.exe

#使用makefont.exe编译hankaku.txt字库
hankaku.bin : hankaku.txt Makefile
    $(MAKEFONT) hankaku.txt hankaku.bin

#然后连接所必须的接口信息,生成目标文件hankaku.obj,这样就可以与bootpack.obj连接了
hankaku.obj : hankaku.bin Makefile
    $(BIN2OBJ) hankaku.bin hankaku.obj _hankaku

#使用obj2bim.exe将bootpack.obj生成二进制映像文件bootpack.bim,
#这一步是因为C语言不能编写所有的程序,有一部分用汇编来写,然后链接到C语言程序上 
bootpack.bim : bootpack.obj naskfunc.obj hankaku.obj Makefile
    $(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \
        bootpack.obj naskfunc.obj hankaku.obj
# 3MB+64KB=3136KB

bootpack.c节选

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
    struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
    extern char hankaku[4096];   //在源程序以外准备的数据,都需要加上extern

    init_palette();    //设置调色板
    init_screen(binfo->vram, binfo->scrnx, binfo->scrny);  //设置背景

    //显示字符
    putfont8(binfo->vram, binfo->scrnx,  8, 8, COL8_FFFFFF, hankaku + 'A' * 16);
    putfont8(binfo->vram, binfo->scrnx, 16, 8, COL8_FFFFFF, hankaku + 'B' * 16);
    putfont8(binfo->vram, binfo->scrnx, 24, 8, COL8_FFFFFF, hankaku + 'C' * 16);
    putfont8(binfo->vram, binfo->scrnx, 40, 8, COL8_FFFFFF, hankaku + '1' * 16);
    putfont8(binfo->vram, binfo->scrnx, 48, 8, COL8_FFFFFF, hankaku + '2' * 16);
    putfont8(binfo->vram, binfo->scrnx, 56, 8, COL8_FFFFFF, hankaku + '3' * 16);

    for (;;) 
    {
        io_hlt();
    }
}

make run
这里写图片描述

显示字符串
bootpack.c节选

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
    struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
    extern char hankaku[4096];   //在源程序以外准备的数据,都需要加上extern

    init_palette();    //设置调色板
    init_screen(binfo->vram, binfo->scrnx, binfo->scrny);  //设置背景

    //显示字符串
    putfonts8_asc(binfo->vram, binfo->scrnx,  8,  8, COL8_FFFFFF, "ABC 123");
    putfonts8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "Haribote OS.");
    putfonts8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_FFFFFF, "Haribote OS.");

    for (;;) 
    {
        io_hlt();
    }
}

void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
    extern char hankaku[4096];  
    for (; *s != 0x00; s++)    //字符串都是以0x00结尾的
    {
        putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
        x += 8;
    }
    return;
}

make run
这里写图片描述

显示变量值
bootpack.c中的变动

#include <stdio.h>

void HariMain(void)  //程序从此处开始运行,函数名不能改
{
    struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
    extern char hankaku[4096];   //在源程序以外准备的数据,都需要加上extern
    char s[40];

    init_palette();    //设置调色板
    init_screen(binfo->vram, binfo->scrnx, binfo->scrny);  //设置背景

    //显示字符串
    putfonts8_asc(binfo->vram, binfo->scrnx,  8,  8, COL8_FFFFFF, "ABC 123");
    putfonts8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "Haribote OS.");
    putfonts8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_FFFFFF, "Haribote OS.");

    //sprintf不是按指定格式输出,只是将输出内容作为字符串写在内存中,可应用于所有操作系统
    sprintf(s, "scrnx = %d", binfo->scrnx);  
    putfonts8_asc(binfo->vram, binfo->scrnx, 16, 64, COL8_FFFFFF, s);

    for (;;) 
    {
        io_hlt();
    }
}

void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
    extern char hankaku[4096];  
    for (; *s != 0x00; s++)    //字符串都是以0x00结尾的
    {
        putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
        x += 8;
    }
    return;
}

make run
这里写图片描述

显示鼠标指针
与显示字符的思想类似。
bootpack.c变动

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();
    }
}

//制作要显示的字符
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;
}

//进行显示,只需将buf中的数据复制到vram中
//vxsize与vysize与vram有关,vysize=0xa0000, vxsize=320
//pxsize与pysize是想要显示图像的大小,因为鼠标指针大小为16*16,所以两个值都为16
//buf指定图形存放地址,bxsize指定每一行含有的像素值,与pxsize大体相同
//px0与py0为要显示字符的起始像素点
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;
}

make run
这里写图片描述

GDT与IDT的初始化
两者都是与CPU有关的设定
GDT global (segment) descriptor table,全局段号记录表
IDT interrupt descriptor table,中断记录表

asmhead.nas需要增加设定,因为之前只是为运行bootpack.c做了一些设定,这一步跳过。

bootpack.c节选

struct BOOTINFO               //12字节
{
    char cyls, leds, vmode, reserve;
    short scrnx, scrny;
    char *vram;
};

struct SEGMENT_DESCRIPTOR {  //8字节
    short limit_low, base_low;
    char base_mid, access_right;
    char limit_high, base_high;
};

struct GATE_DESCRIPTOR {     //8字节
    short offset_low, selector;
    char dw_count, access_right;
    short offset_high;
};

void init_gdtidt(void);
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar);
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);

void init_gdtidt(void)
{
    //0x270000到0x27ffff设为GDT,因为这一块内存没有特殊用途
    struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;

    //0x26f800到0x26ffff设为IDT  
    struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) 0x0026f800;  
    int i;

    /* GDT的初始化*/
    for (i = 0; i < 8192; i++) {   //完成对8192个段的设定
        set_segmdesc(gdt + i, 0, 0, 0);   //将每个段的上限、基址、访问权限都设为0
    }

    //将1号段上限设为4G,地址为0,表示CPU所能管理的全部内存本身
    set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);

    //将2号段上限设为512KB,地址为0x280000,正好是bootpack.hrb可以执行的地方
    set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a);  
    load_gdtr(0xffff, 0x00270000); //因为C语言不能给GDTR赋值,所以写成汇编

    /* IDT的初始化 */
    for (i = 0; i < 256; i++) 
    {
        set_gatedesc(idt + i, 0, 0, 0);
    }
    load_idtr(0x7ff, 0x0026f800);  //给IDTR赋值

    return;
}

void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
    if (limit > 0xfffff) 
    {
        ar |= 0x8000;        /* G_bit = 1 */
        limit /= 0x1000;
    }
    sd->limit_low    = limit & 0xffff;
    sd->base_low     = base & 0xffff;
    sd->base_mid     = (base >> 16) & 0xff;
    sd->access_right = ar & 0xff;
    sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);
    sd->base_high    = (base >> 24) & 0xff;
    return;
}

void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{
    gd->offset_low   = offset & 0xffff;
    gd->selector     = selector;
    gd->dw_count     = (ar >> 8) & 0xff;
    gd->access_right = ar & 0xff;
    gd->offset_high  = (offset >> 16) & 0xffff;
    return;
}

段的地址在CPU中被称为基址,所以用了base这样的变量名,为了兼容,它分为low(2字节),mid(1字节),high(1字节),然后按顺序填入相应的数值。
段的上限只能使用20位,在段属性中一个标志位Gbit,为1的时候limit的单位不解释成字节(byte),而解释成页(page),1页是4KB。20位段的上限分别写入limit_high和limit_low中,再将段属性写入limit_high高4位中。
对于12位段的访问权属性ar(access_right),高四位放在limit_high高4位中。
0x00:未使用的记录表
0x92:系统专用,可读写的段,不可执行。
0x9a:系统专用,可执行的段,可读不可写。
0xf2 :应用程序用,可读写的段,不可执行。
0xfa : 应用程序,可执行的段,可读不可写。

naskfunc.nas中添加

GLOBAL _load_gdtr, _load_idtr

_load_gdtr:                 ; void load_gdtr(int limit, int addr);
        MOV     AX,[ESP+4]  ; limit
        MOV     [ESP+6],AX
        LGDT    [ESP+6]
        RET

 _load_idtr:                ;void load_idtr(intlimit,int addr);
        MOV     AX,[ESP+4]  ; limit
        MOV     [ESP+6],AX
        LIDT    [ESP+6]
        RET

load_getr用来指定段的上限和地址值赋值给名为GDTR的48位寄存器。LGDT指令用来从指定位置读取6个字节,然后赋值给GDTR寄存器。DOWRD[ESP+4]中存放段的上限,如0x0000ffff,DWORD[ESP+8]中存放地址,如0x002700,按字节写出为ffff00000002700(靠右边为低位),为了执行LGDT,希望成为ffff00002700形式。

make run后没有什么变化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值