首页 > 其他分享 >第5天 结构体、文字显示与GDT

第5天 结构体、文字显示与GDT

时间:2023-09-21 21:55:33浏览次数:35  
标签:文字 info GDT 字符 int char vram font 结构

结构体

获取启动信息

我们需要获取我们之前使用ashead.asm设置的启动信息,以便于我们之后的开发
img
这些启动信息我们利用指针,定义指针,根据指针地址就能获取到定义的值,如下所示:

    char *Scrnx = 0x0ff4;
    char *Scrny =  0xff6;
    char *vram = 0x0ff8;

试用结构体

但是这样的变量没有组织的散落在各个地方后期维护起来会有些困难,我们需要利用类似OOP的编程思想,将相同类型的数据统一封装到一个struct结构体中,如下所示:

struct BootInfo
{
	// 引导扇区设置
	char cyls;
	// LED 指示灯状态
	char leds;
	// 关于颜色的信息
	char vmode;
	// 分辨率X
	short scrnx;
	// 分辨率y
	short scrny;
	// 图像缓冲区的起始地址
	char *vram;
};

struct结构体占用内存大小就是struct中所有变量大小的和我们的例子就是12个字节。结构体.结构体变量与(结构体).结构体变量是不一样的。
使用structur的好处是在定义变量的时候如果参数变多的话会成以下这样子:

    void pirnt(int x, int y, int a, int b....) {

    }

如果函数中的参数变多的话,函数与函数之间调用传递参数的时候会很麻烦,如果使用结构体的话,之间将参数封装到结构体中,按结构体传参即可:

    void print(struct BootInfo info) {
        info.a
        info.b
        ...
    }

使用结构体时可以使用'.'来访问结构体中的变量。
如果使用结构体指针的话则需要使用结构体指针->变量或是*(结构体指针).变量来访问就结构体指针变量。

显示字符

因为从实模式切换到保护模式就没办法使用BIOS中断向量表里的方法了直接在屏幕中输入字符了,所有我们需要构建一个字体库,根据字体库来大于字符串。
如下图所示,下图是一个18*18的字符A,我们可以将有像素的像素点设置成1,没像素的像素点设置为0。在输出字母的时候根据二进制10就可以输出我们想要的字符了。
img
c语言没办法定义二进制,如果将以上二进制数据整合到c语言中则是以下变量:

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

接下来我们以8位为一组,遍历每一位,如果为1则在屏幕上在该地方图上一个像素点。遍历的方法就是对每一位进行与运算,如果运算结果大于 不等于0则表示这一位的等于1。与运算的规则则是两边数值进行比较如果两边的二进制都是1,那么运算结果为1,如果是0,那么运算结果为0。例如我们有一个二进制数: 1011,我们想要判断它的第三位是否为1,我们可以这样进行与运算。
1011
0010

0010
运算结果大于0那么就表示该位数是等于1的。

显存的计算地址和矩形的计算地址一样第几行 y + i是因为上面的那个字符序列共有十六行,每遍历一次需要换一次行,x和之前一样,向左空出多少个像素点来进行展示。一个字符需要空出十六行来进行展示,所以要遍历十六次。

/**
 * vram: 显存地址
 * xSize: 每行像素数 320 * 200就是320
 * c: 颜色下标
 * x: 字符串显示x轴 从左到右空出多少个像素
 * y: 字符显示y轴
 * font: 字符序列
 */
void put_font_8(unsigned char *vram, int x_size, char c, int x, int y, char font[16])
{
	int i;
	char *p, d /* data */;
	for (i = 0; i < 16; i++)
	{
		// 和矩形计算公式一样 根据行数和列数 计算出字符需要展示的位置
		p = vram + (y + i) * x_size + 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;
}

在main方法中调用函数,运行可得

put_font_8(boot_info->vram, boot_info->scrx, 0x0E, 8, 8, font_A);

img

其实别的字符也可使用这种方式来进行定义,但是一个一个定义太麻烦了,我们就直接使用书中案例中定义好的编译到我们程序中使用算法,将文件hankaku.txt保存到我们的项目中,使用
makefont hankaku.txt font.bin 将txt文件转成.bin文件
bin2obj font.bin font.obj _font 在将.bin文件专场obj文件,_font定义的就在汇编中展示的全局遍历的内容,如果是_font的话则是这样展示的

_font: 
	db 0x00 0x12

之后在于bockpack进行链接即可
obj2bim @D:\os\os\tolset\z_tools\haribote\haribote.rul out:bootpack.bim stack:3136k map:bootpack.map .\bootpack.obj naskfun.obj font.obj

在使用时我们在项目中定义以下font遍历即可,遍历名称就是我们之前定义的_font,长度是4096 / 16 = 256个字符。

extern char font[4096];

它这在hankaku.txt中定义的文件序列似乎与ascill码相同,在使用的时候直接根据ascill码的序列 * 16就能找到我们想要找到字符数据了。

put_font_8(boot_info->vram, boot_info->scrx, 0x0E, 8, 8, font + 'A' * 16);

显示字符串

显示字符串的话也挺简单的,因为在c语言中,字符串的末尾是\0就是0x00,我们只要遍历字符串,如果到底0x00了,那么就表示到字符串末尾了,在遍历的过程中我们将每一位字符串进行大于,字符与字符之间间隔8个像素点就可以了。

void printFont8_ascii(char *vram, int x_size, int x, int y, char color, unsigned char *str)
{
	// 末尾字符是0x00
	for (; *str != 0x00; str++)
	{
		put_font_8(vram, x_size, color, x, y, font + *str * 16);
		x += 8;
	}
}

使用如下:

	printFont8_ascii(boot_info->vram, boot_info->scrnx, 10, 10, 0x0e, "Hello lyra OS。");

img

输出内存地址

如果系统出问题了,在debug的过程中,需要知道内存中的值是什么,由于各个系统之间格式化输出都是不一样的,所以没办法使用printf直接进行输出。但是我们可以将字符串写入到内存中,在由我们之前写好的字符串输出程序进行输出,想要使用这个函数我们需要导入stdio.h,函数的参数为:变量地址、值、值。
如下所示,先获取bootinfo中部存储的scrnx大小,然后保存到变量c中:

	sprintf(s, "size_x:%d", boot_info->scrnx);

最后使用我们定义好的字符串对这个变量进行输出就可以了。

	printFont8_ascii(boot_info->vram, boot_info->scrnx, 10, 10, 0x0e, s);

由于c语言没有自带这个函数,我们需要引入叫go的c语言编译器的include才能够使用,使用ccl --help可以查询cc1的命令帮助
img
很明显,这个命令是我们需要的,这个命令可以将.h文件导入到我们项目中。在编译的时候添加这个参数就可以使用了sprintf函数了。

	cc1 -I D:\os\30dayMakeOS\tolset\z_tools\haribote -o bootpack.gas .\bootpack.c

运行结果:
img

标签:文字,info,GDT,字符,int,char,vram,font,结构
From: https://www.cnblogs.com/lyraHeartstrings/p/17422284.html

相关文章

  • 9.21日数据结构练习题
    用栈操作去判断一个字符串是不是回文数列1#include<iostream>2#defineMAXSIZE1003usingnamespacestd;4//定义一个栈的结构体5//包含顶指针,尾指针,长度6typedefstruct{7char*base;8char*top;9intstacksize;10}SqStack;11//创......
  • C语言-数据结构之顺序表
    #include<stdio.h>#defineN128typedefintdata_type;typedefstruct{ data_typedata[N]; intlast;}sqlist;sqlist*list_create();intlist_show(sqlist*L);intlist_clear(sqlist*L);intlist_destory(sqlist*L);intlist_empty(sqlist*L......
  • 深入浅出程序设计竞赛(进阶篇)VO.7 进阶数据结构
    第五章二叉堆P2168[NOI2015]荷马史诗哈夫曼树P2827[NOIP2016提高组]蚯蚓找最长的蚯蚓只需要直到相对大小,其余蚯蚓长度\(+q\)等价于新产生的两条蚯蚓长度\(-q\)新产生的第一/二条蚯蚓长度分别单调,可以用队列代替堆时间复杂度\(O(n\logn+m)\)P1801黑匣子对顶堆......
  • 数据结构
    1.数据结构——栈(子弹弹夹     2.数据结构——队列(地铁安检)  3.数组  4.链表  5.红黑树 ......
  • 设计思路-关于树节点结构设计
    增加path字段存储树的路径1.可以通过当前节点追溯到上级所有父节点2.可以通过当前节点查询所有子节点比如满足以下需求参考sql------------------------------所有设备权限------------------------------1.获得节点explainSELECT`id`,`userid`,`name......
  • 计算机体系结构
    流水线级数的作用是如何体现的:在可行范围内,把一条指令的执行流程划分的越细致,也就是级数越多,可以并行化执行的指令执行的指令数目越多感觉分的级数越多,在不考虑各级之间的通信同步延迟情况下,时钟周期就能够越短,由于CPI最小也就是1了,时钟周期降低了,那么流水线下一条指令的执行时间......
  • PHP多层级菜单树形结构递归处理
    如题:一、数据库菜单数据表使用图片中id和parent_id两个参数来关联父子关系二、将数据库中的数据变成树状多层级解构```{ "id":1, "parentId":0, "treePath":"0", "name":"系统管理", "type":2, "path":"/system",......
  • css实现文字切换
    .textc{--num:'今日大吉';animation:text-change3slinearforwards;}.textc::after{content:var(--num);font-size:20px;}@keyframestext-change{33%{--num:'明日好运';}67%{--num:'......
  • 前后端交互,后端给的数据结构和想预想的不一样?
    前后端交互时,后端返回的数据结构与前端预期的不一致是一种常见的问题,通常可以通过以下方式来解决:明确数据结构规范:在项目初期,前后端开发团队应该明确定义数据结构规范,包括接口的输入参数和返回结果的格式、字段名称和数据类型等。这有助于双方确保一致性。文档化:文档是关键,要确保有......
  • Android 妙用TextView实现左边文字,右边图片
    有时候,需要文字在左边,右边有个箭头,我个人之前会有两种做法:使用线性布局来实现或者使用约束布局,一个左对齐,一个右对齐这几天突然想到是否可以使用TextView的设置图标的方式实现,研究发现确实可以实现我的需求,也是记录下文字和图标左右显示效果:代码:<TextViewandroid:id="@+id/......