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

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

方框中括着的地址仅仅是为了与asmhead.nas保持一致才出现的。
内容2:试用结构体
采用之前的写法xsize = ((short)0x0ff4);修改代码后应该会使得代码的行数有所下降,但是像是使用了什么特殊技巧。我们还是尝试一下更加普通的写法。如下:
在这里插入图片描述

矩形圈住的部分中,binfo是以上面定义的结构体定义的变量的首地址。以这样的表达形式可以比较节省代码空间。这里要注意括号是不能省略的,不然会被误解为*(binfo.scrnx)。
内容3:试用箭头记号
除了(binfo).scrnx的表现手法,还可以用binfo->scrnx,被称之为箭头标记的方式。这样看上去代码还更简洁一些。
内容4:显示字符
字符可以用8
16的长方形像素点阵来表示。想象一个下图左边的数据,以这样的方式将图形转变成右边的数据。八位是一个字节,十六行是一个字符,也就是一个字节。
在这里插入图片描述

那这种字体数据是怎么写到程序里的呢?有一种临时方案:
在这里插入图片描述

这仅仅是将刚刚的0和1的排列,重写为十六进制数而已。但是读起来很费劲,暂时就先这样子。
用for语句,将画8个像素的程序循环16遍,就可以显示出一个字符了。制作函数如下:
在这里插入图片描述

前面和0x80,0x40,0x20等数字进行与操作的作用就是取单个的一位出来,如果这一位不是零,就将对应的像素块设置为c对应的颜色种类。,运行之后,可以看到在屏幕上显示出了A。
在这里插入图片描述

内容5:增加字体
我们的程序只能显示“A”而不能显示别的字符。所以决定沿用OSASK的字体数据。这次将hankaku.txt这个文本文件加入到源程序中。
在这里插入图片描述

当然,这既不是C语言,也不是汇编语言,所以需要专业的编译器。在这里就直接使用制作OSASK时曾经使用过的工具(makefont.exe)。
编译后生成hankaku.bin文件,因为它不是目标文件,所以要加上连接必需的接口信息,将它变成目标文件。A的字符编码是0x41,所以A的字体数据,放在自“hankaku+0x41 *16”开始的16字节里。
我们使用以上字体数据,向bootpack.c里添加了很多内容,代码如下:
在这里插入图片描述

这样子,就可以在屏幕上显示出ABC 123.如下:
在这里插入图片描述

内容6:显示字符串
显示了六个字符就写了这么多代码,不是很美观,所以制作函数如下:
在这里插入图片描述

函数可以把参数字符串循环打印出来,每打印一个位置向右移动8即可。
这样,把要打印出来的字符串直接带入函数即可。像如下这么写:
在这里插入图片描述

就可以有如下显示:
在这里插入图片描述

黑影其实就是错了个位换成黑色然后打印出来的相同的图案。
内容7:显示变量值
怎么显示变量的值呢?可以使用sprintf函数,这个函数不是按指定格式输出,而是将输出内容作为字符串写在内存中,是名为GO的C编译器附带的函数,它在制作者的精心设计之下能够不使用操作系统的任何功能,只对内存进行操作,所以可以应用于所有操作系统。
代码如下:
在这里插入图片描述

Sprintf函数参数中,s就是要写入的地址,写入的内容是后面引号的部分,binfo->scrnx的值是赋给了前面%d的部分。
下面这个函数则是将这个字符串输出到屏幕上的函数,和之前的用法是一样的。
最后输出如下:
在这里插入图片描述

内容8:显示鼠标指针
思路和显示字符是差不多的。
首先,将鼠标指针的大小定为1616.定下来之后,就简单了。先准备1616=256字节的内存,然后往里面写入鼠标指针的数据。我们把程序写在init_mouse_cursor8里,如下:
在这里插入图片描述

要将背景色显示出来,还需要作成下面这个函数。,也就是将buf中的数据复制到vram中。
在这里插入图片描述

vram和vxsize是关于VRAM的信息。他们的值分别是0xa0000和320。
pxsize和pysize是想要显示的图形的大小,所以都是16。
px0,py0指定图形在画面上的显示位置。
buf和bxsize分别制定图形的存放地址和每一行含有的像素数。
然后运行,一切正常:
在这里插入图片描述

内容9:GDT与IDT的初始化
要想让鼠标动起来,首先要将GDT和 IDT初始化。那么什么是GDT IDT呢?
它们都是与CPU有关的设定,再说它们之前先要说一下分段。
所谓分段,打个比方,就是按照自己喜欢的方式,将合计4GB的内存分成很多块,每一块的起始地址都看作0。这样,任何程序都可以先写上一句ORG 0.这样分割出来的块,就叫做段。
曾经用16位的时候,如果计算地址,只需要将地址乘以16就可以了,但是现在是32位,不能继续那么用。如果还是MOV AL,[DS:EBX]的形式,CPU会往EBX中加上某个值来计算,这个值是DS所表示的段的起始地址。
按这样的方法,想要表示一个段,需要有以下信息:
1)段的大小是多少
2)段的起始地址在哪里
3)段的管理属性(禁止写入,禁止执行,系统专用等)
想要表示这些信息需要八个字节,即64位,但是段寄存器只有16位。所以可以模仿调色板的做法。也就是说,先有一个段号,存放在段寄存器中,然后设定好段号和段的对应关系。
段号怎么设定呢?这是对于CPU的设定,不需要像调色板那样使用io_out。但因为能够使用0-8191的范围,即可以定义8192个段,所以设定这些段就需要65532字节,将之存储到内存,这64KB的数据就被称作GDT。
GDT是“global descriptor table”的缩写,意思是全局段号记录表。将这些数据整齐地排列在内存的某个地方,然后将内存的起始地址和有效设定个数放在CPU中被称作GDTR的特殊寄存器中,设定完成。
IDT是“interrupt descriptor table”的缩写,直译过来就是“中断记录表”。当CPU遇到外部状况变化,或者是内部偶然发生某些错误时,会临时切换过去处理这种突发事件。中断处理机制解决了CPU使用“查询”这种处理方法可能无法及时响应的问题。
后面则是对GDT,IDT的一个初始化的部分。
这次实验的内容大致如此。
二、遇到的问题及解决方法
关于操作上的问题这次并没有碰到什么,按着步骤来即可。关于知识点上倒是有些问题。
问题1:关于GDT和IDT的储存大小为什么是这么大?
发现是自己看书没有看认真,其实在前面就说了GDT的大小就是那大小为64KB的数据,存储全局段号用的。IDT的大小也就是取决于我们规定的中断的号码所占据的内存。

问题2:之前在说ORG时从来没提过DS,现在又说了DS,为什么
查询有关资料,应该是之前的DS在未被提及时默认为零,之前也没有涉及到相关知识,所以就没有说。
三、程序设计创新点
1、创新点1
如果想要显示自己除了这些字符以外的字符,应该怎么设置。
因为自己设定的字符没有对应的ASII码,所以只能通过hankaku + 数值*16的形式去进行输出,比如我想设置一个字符‘王’,字符设置和代码如下:
在这里插入图片描述

运行结果如下:
在这里插入图片描述

创新点2:想知道在使用sprintf的情况下可不可以输出汉字,尝试如下:
在这里插入图片描述

运行结果如下:
在这里插入图片描述

可以看出还是不可以的。这应该还是前面编译器的问题,编译器不能识别出汉字,也无法转换并显示出来。