一、实验主要内容
1.接受启动信息:
关键代码:
重点内容:
(1)通过指针来获得显示画面模式,这样可以避免当画面模式改变时系统无法正常运行的问题。
2.试用结构体:
关键代码:
重点内容:
- 如何理解结构体:struct后面是结构体的名字同时也是这个结构体的首地址,结构体可以理解为一个文件柜,各个变量可以看做接口,需要使用的时候接上这个接口即可,通过结构体对象.xxxx来访问。
- 试用箭头记号:
关键代码:
重点内容:
(1)这也是对指针访问的一种形式,这样子更形象一些,但使用这种方法访问程序会有危险。
4.显示字符:
关键代码:
重点内容:
- 如何理解这8个if呢?首先d做&运算的目的是检查不为1的位置,该位置就是它在VRAM中x的偏移量,也就是说在x的什么位置写入一个1,虽然有16个十六进制数,但8个if会将它们的每一个位置都检查一遍,符合条件则执行,执行完毕后就会看到下图的效果:
- 改进版,写入到数组去显示,代码如下:
那么又如何理解这个方法呢?很简单,只需要把p[0]-p[7]看做上图中的每一列就好,在绘图的时候就会遍历每一列,为1的点则画一个像素,这样的做法效率高而且简单。
运行截图:
5.增加字体:
关键代码:
重点内容:
- 制作显示的字体:作者已经提供好字体库,重点是理解它如何显示到屏幕上。首先通过makefont编译器生成二进制文件,也就是我们看到的那种01矩阵序列,然后二进制文件经过bin2obj程序的处理与ojb文件进行连接,最后变成汇编程序,经过一系列步骤后生成可执行文件,运行后即可看到欲显示的字符了。
- 外部程序使用字体库:通过extern关键字导入,务必保持c文件与字体库文件在同一个目录,否则会报错。Extern关键字的作用是声明该数据是外部导入数据,默认是根目录地址。
- 如何理解关键代码的putfont8()函数最后一个参数的表达式:如hankaku+’1’*16,这是一种寻址方式,首地址+要显示的目标字符的地址,16是偏移量。为什么是16呢?一共需要16行8位二进制才能显示一个字符呗。
- 运行截图:
6.显示字符串:
关键代码:
重点内容:
(1)如何理解这段代码呢:首先呢我们要明确字符串都是以0x00(对应的ASCII是’\0’)表示结尾,因此可以用于判断是否读取完整个字符串。再者字符串的首地址都是以字符串的首个字母开始,因为用*s可以表示首地址。这个代码其实就是将要画的字符存起来了,再调用putfont8()去画。本质还是画一个字符。X+=8是两个字符之间间隔几个像素。
(2)运行截图:
7.显示变量值:
关键代码:
重点内容:
- 如何将变量输出在屏幕上:按照之前的套路,肯定是先将要显示的东西写入VRAM才可以,那么如何将变量快速的写入到指定的内存中去呢?利用sprintf()即可,操作简单,只需要三个参数,显示的字符串数组(数组名就是它的首地址),输出格式、写入的值。binfo->scrnx起到导航作用,把当前变量值存储到%d里面,然后再将这个字符串写入s中。最后再将整个字符串s画出来。
- 由于是c语言函数,务必导入头文件:<stdio.h>
- 格式化输出说明:
(4)运行截图:
- 显示指针鼠标:
关键代码:
重点内容:
- 从本质来说,鼠标其实就是绘制一个箭头的图像。通过遍历鼠标数据(类似于txt文档那种图案),通过检测特定位置的标记绘制特定的像素(这个时候已经在内存的地址写好要显示的内容了),比较难的点就是如何拿到当前位置的背景颜色,作者也没有说明,只是将背景颜色当作一个参数传进来了。然后将鼠标的数据拷贝一份到VRAM里面去,再通过绘制函数画出来即可。总的来说就是根据图像数据得到在内存中的数据,最后存入VRAM即可显示出来。
- 代码如下:
Vram和vxsize是关于VRAM的信息,也就是vram第一个像素的地址以及这个画面的最大x分辨率。Pxsize和pysize是显示图像的长宽,px0和py0指定图像在界面上显示的位置,buf和bxsize分别表示存放图像的地址和每一行包含的像素数。
- 运行截图:
9.GDT与IDT初始化:
重点内容:
- 分段:通俗地讲,按照一定的方式将内存分割成许多不同的段,防止内存地址在使用的时候出现重叠。
- 表示一个分段需要的信息:段的大小、段的起始地址、段的管理属性(禁止写入、禁止执行、系统专用等等)。通过段寄存器来实现管理,只需要把对应设置设为1或者0即可。
- 不同的寄存器位数不一样,这个时候需要对寄存器不同的为进行编号,号码称之为段号,与内存上的段对应,不同的段有着不同的段号,管理的段的时候先找到段号即可。
- 如何设定段号呢?段号由一个表来维护,也就是GDT(全局段记录表),这个表在内存的某个地方,然后将内存的起始地址(或者某个段的起始地址)与有效设定个数放在cpu内被称作GDTR的特殊寄存器中就完成了设定。
- 顾名思义IDT就是中断记录表了,记录了0-255号中断号码与调用函数的对应关系,因此当鼠标中断时,调用函数里面的内容应该包括:获取当前鼠标的位置、在当前位置绘制鼠标。由于中断的速度极快以及人眼的视觉暂留效果,我们会看到鼠标移动的效果。
- 关键代码:
SEGMENT_DESCRIPTOR中存放了GDT的8字节内容,同理GATE_DESCRIPTOR存放了IDT的8字节内容,两者都是以cpu资料为基础的。Gdt被赋值为0x00270000,意思就是将内存地址0x270000~0x27ffff设为GDT(就是用该地址范围记录全局段记录表),同理IDT。
- 如何理解下图代码:
首先gdt的起始地址是0x270000,i每次迭代1,但gdt在地址却是前进了8个位置,原因就是gdt指针指向的结构体是8字节大小的。
二、遇到的问题及解决方法
三、程序设计创新点
(1)尝试着让界面显示中文字体:
关键代码:
重新对字体数据库进行编码即可。
运行截图: