1.接收启动信息 & 2.试用结构体 & 3.试用箭头符号
在第五天之前,我们都是把vram、xsize、ysize这些值直接写在了bootpack.c文件中。而这些值应从asmhead.nas中获取,因为在asmhead.nas里面我们调用显卡函数并选择了模式,配置好了SCRNX,SCRNY,VRAM参数,如果不从asmhead.nas中获取,当画面模式改变时,系统就不能正常运行。
asmhead.nas中的相关代码:

;有关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      0xc200      ;这个程序要装载到内存中的地方;VGA显卡  320*200*8位彩色     MOV     AL,0x13MOV      AH,0x00INT     0x10MOV      BYTE [VMODE],8  ;记录画面模式MOV      WORD [SCRNX],320MOV     WORD [SCRNY],200MOV     DWORD [VRAM],0x000a0000
;用BIOS取得键盘上各种LED指示灯的状态MOV       AH,0x02INT      0x16            ;keyboard BIOSMOV       [LEDS],AL

因为要传递的变量不是一个两个,为了使代码简洁,利用结构体将这些相关的变量一股脑的打包传递过去。

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

4.显示字符
之前要显示字符是通过BIOS函数,但这次是32位模式,不能再依赖BIOS了,只能自力更生。
字符可以用8*16的长方形像素点阵来表示。8“位”是一个字节,一个字符是16个字节。

写入内存的字体数据:

static char font_A[16] = {0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00};

这仅仅是将刚才的0和1的排列,重新成16进制数而已。因为C语言无法用二进制数记录数据,只能写成十六进制或八进制。
数据备齐之后,只要描画到画面上就好了。用for语句将8个像素的程序循环16遍就可以了。

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就显示出来了。

5.增加字体
如果还想要显示其他的字符,那就需要一个字符一个字符的添加,太费时了。所以拿来一个现成的字体文件(hankaku.txt)来用,这是OSASK的字体。内容如下:


如果在C语言中使用这种字体数据,只需写上以下语句:
ertern char hankaku[4096];
像这种在源程序以外准备的数据,都需要加上extern属性,这样C编译器就能够知道它是外部数据,并在编译时做出相应调整。

void HariMain(void)
{struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;extern char hankaku[4096];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();}
}

6.显示字符串
上面都是输出字符的函数,现在来写一个显示字符串的函数。

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

所谓字符串是指按顺序排列在内存里,末尾加上0x00而组成的字符编码。所以s是指字符串的首地址,而使用*s就可以读取字符编码。

7.显示变量值
使用sprintf函数可以显示变量的值,它是printf的同类,我们不能随便使用printf函数,但是sprintf函数是可以的。因为sprintf函数不是按指定格式输出,只是将输出内容作为字符串写在内存中。
这个sprintf函数,是名为GO的C编译器附带的函数,它在制作者的精心设计下能够不适用操作系统的任何功能。
要在sprintf函数,需要添加C语言的头文件#include<stdio.h>

接下来在HariMain中使用此函数:

sprintf(s, "scrnx = %d", binfo->scrnx);
putfonts8_asc(binfo->vram, binfo->scrnx, 16, 64, COL8_FFFFFF, s);

输出:

8.显示鼠标指针
比较简单,直接上代码:

void HariMain(void)
{struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;char s[40], mcursor[256];int mx, my;init_palette();init_screen8(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);for (;;) {io_hlt();}
}
void init_mouse_cursor8(char *mouse, char bc)   //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;
}


接下来,只使用以下两个函数就行了。

init_mouse_cursor8(mcursor, COL8_008484);
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);

9.GDT与IDT的初始化
GDT:全局段号记录表
IDT:中断记录表
所谓分段:打个比方说,就是按照自己喜欢的方式,将合计4GB的内存分成很多块了,每一块的起始地址都看作0来处理。


CPU用8个字节的数据来表示这些信息。但是,用于指定段的寄存器只有16位。模仿图像调色板的做法:现有一个段号,存放在段寄存器里,然后预先设定好段号与段的对应关系。
段寄存器低三位不能用,能够处理的就只有位于0~8191的区域。

要使用鼠标,就必须要使用中断,所以我们必须设定IDT,IDT记录了0~255的中断号码与调用函数的对应关系。其设定方法与GDT相似(或许是因为使用同样的方法能简化CPU电路)。

struct SEGMENT_DESCRIPTOR {short limit_low, base_low;char base_mid, access_right;char limit_high, base_high;
};
struct GATE_DESCRIPTOR {short offset_low, selector;char dw_count, access_right;short offset_high;
};void init_gdtidt(void)
{struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) 0x0026f800;int i;/* 初期化GDT*/for (i = 0; i < 8192; i++) {set_segmdesc(gdt + i, 0, 0, 0);}set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a);load_gdtr(0xffff, 0x00270000);/*初期化IDT */for (i = 0; i < 256; i++) {set_gatedesc(idt + i, 0, 0, 0);}load_idtr(0x7ff, 0x0026f800);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;
}


变量idt也是一样,IDT被设为了0x26f800~0x26ffff。

for (i = 0; i < 8192; i++) {set_segmdesc(gdt + i, 0, 0, 0);}


内存地址只是一个编号,代表一个内存空间。那么这个空间是多大呢?原来在计算机中存储器的容量是以字节为基本单位的。也就是说一个内存地址代表一个字节(8bit)的存储空间。

30天自制OS学习笔记 (五)结构体、文字显示与GDT/IDT 初始化相关推荐

  1. 自制操作系统5-结构体、文字显示与GDT/IDT初始化

    DAY5 结构体.文字显示与GDT/IDT初始化 2020.10.17-2020.10.18 今天的内容主要学习了如何显示符号,将鼠标指针显示出来后,进而考虑如何让它动起来. 1. 接受启动信息 文档 ...

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

    第5天 结构体.文字显示与GDT/IDT初始化 https://weread.qq.com/web/reader/38732220718ff5cf3877215k34132fc02293416a75f ...

  3. 30天自制操作系统:第五天 结构体、文字显示与 GDT/IDT初始化

    今天的内容相比前几天多了很多,主要是一些东西用代码写出来更难理解,需要更多的时间去琢磨,因此对于一些较为基础的内容不会进行详细的描述. 1.接收启动信息(harib02a) 之前的程序大都是直接使用0 ...

  4. 五、结构体、文字显示与GDT/IDT初始化

    试用结构体:      结构体的好处是,可以将各个参数一股脑传递进去,而没有结构体则需要一个一个传递. 一个灵光一闪:原来一个大的软件,所有源文件全部编译进去成为一个整体存放在内存里,假如文件A中宏定 ...

  5. 操作系统实验第五天:结构体、文字显示与GDT/IDT初始化

    一.实验主要内容 内容1:接受启动信息 之前的bootpack.c中是将数字直接写入程序,但这些数字本身应该是从asmhead.nas先前保存下来的值中取.不然当画面模式改变时,程序就不能正确运行. ...

  6. 30天自制OS学习笔记 (四)C语言与画面显示的练习

    (四)C语言与画面显示的练习 1. 用C语言实现内存导入 这里说的用C语言实现内存写入不是用指针实现,而是用汇编语言写的内存写入函数,用C语言调用. _write_mem8: ; void write ...

  7. c语言如何宏定义枚举型结构体,C语言学习笔记--枚举结构体

    枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法格式来声明: enum 枚举类型名字 {名字0,名字1,...,名字n}: 枚举类型名字通常并不真的使用,要用的是大括号里面的名字,因为 ...

  8. c语言学习笔记【结构体02】结构体指针变量与结构体变量的函数参数,C语言学习笔记结构体02结构体指针变量与结构体变量的函数参数.docx...

    C 语言学习笔记[结构体02]结构体指针变量与结构体变量 的函数参数 C 语言学习笔记之结构体指针变量一提指针,那可 是 C 语言的核心了,有多少学子曾拜倒在指针的脚下.单纯的说指针,其实并不难,但是 ...

  9. C语言学习笔记(15)——结构体程序设计

    前言 C语言的基本数据类型有整数型.实数型及字符型,使用这些基本数据类型可以构造数组类型,并且可以定义相关数据类型的指针.本节介绍的结构体类型区别于以上任何数据类型,它还能把各种不同类型的数据组合成一 ...

最新文章

  1. 400全集python全套视频教程-千锋出品全套python视频教程,400大全集,你了解吗?...
  2. Mybatis之Oracle增删查改示例--转
  3. 如何升级浏览器_绿茶浏览器app下载安装_绿茶浏览器软件最新版免费下载
  4. java 中调用docker_如何通过Java程序执行docker命令
  5. loj#10050-The XOR Largest Pair【Trie(字典树)】
  6. 简单的实现RAII封装。
  7. Python 元组tuple - Python零基础入门教程
  8. IDEA自动勾选显示类型(specify type)
  9. Spring学习笔记之AOP配置篇(一) 注解配置
  10. Java多线程之后台线程不执行finally
  11. 关联省份与学校的级联HTML,JS实现的省份级联实例代码
  12. javascript 轮播图(缓存效果)详细篇
  13. 恢复触摸板功能的方法
  14. 多种方法破解Windows 系统密码
  15. ffmpeg系列之两种视频解码方式
  16. 华人操作系统项目列表
  17. Qt 之 QQ系统表情—实现动态显示效果
  18. 给下拉框加上可输入查询特性-升级版本
  19. Oracle LiveLabs实验:DB Security - Oracle Label Security (OLS)
  20. 如何使用predict()输出预测结果 以及 输出值转换为0/1值。

热门文章

  1. Excel批量删除选区非数字的单元格的操作
  2. 三星s5能升级到android7.0,三星这些手机能升级安卓7.0,新老用户都得看看
  3. 阿里云oss的文件无法使用URL访问,返回:This XML file does not appear to have any style information associated with it
  4. iphone语音延迟_如何在iPhone上共享或保存语音信箱
  5. 2009最新经典爱情短信收藏
  6. 团队编程--MP3播放器
  7. 查看虚拟机CENTOS7 的 IP 地址和命令
  8. linux下apache端口被占用,【linux】下Apache无法启动(8080端口被占用)
  9. python day6
  10. 远离“灰色地带”,网络视频变革中