【读书笔记-《30天自制操作系统》-10】Day11

2024-08-30 08:12

本文主要是介绍【读书笔记-《30天自制操作系统》-10】Day11,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本篇内容继续围绕显示展开。首先对鼠标显示做了些优化,鼠标箭头在到达画面边缘时能够实现部分隐藏;接下来制作了窗口,实现了窗口显示;最后还在窗口的基础上实现了计数器,显示计数的变化并消除闪烁的问题。
在这里插入图片描述

1. 画面边缘隐藏部分鼠标

首先优化一下鼠标显示,在鼠标箭头移动到画面边缘时,隐藏部分鼠标箭头。主程序中原代码如下:

	if (mx > binfo->scrnx - 16) {mx = binfo->scrnx - 16;}if (my > binfo->scrny - 16) {my = binfo->scrny - 16;}

修改为如下代码:

	if (mx > binfo->scrnx - 1) {mx = binfo->scrnx - 1;}if (my > binfo->scrny - 1) {my = binfo->scrny - 1;}

原理也比较简单,显示效果如下:
在这里插入图片描述

可以看出鼠标箭头实现了在画面边缘的部分隐藏,但移出画面之外的部分又产生了其他异常。这里是与图层的刷新有关,只要对于画面之外的部分不进行刷新,就不会产生上面的问题了。修改sheet_refreshsub函数如下:

void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1)
{int h, bx, by, vx, vy, bx0, by0, bx1, by1;unsigned char *buf, c, *vram = ctl->vram;struct SHEET *sht;for (h = 0; h <= ctl->top; h++) {sht = ctl->sheets[h];buf = sht->buf;bx0 = vx0 - sht->vx0;by0 = vy0 - sht->vy0;bx1 = vx1 - sht->vx0;by1 = vy1 - sht->vy0;/* 如果超出了范围则进行修正 */if (bx0 < 0) { bx0 = 0; }if (by0 < 0) { by0 = 0; }if (bx1 > sht->bxsize) { bx1 = sht->bxsize; }if (by1 > sht->bysize) { by1 = sht->bysize; }for (by = by0; by < by1; by++) {vy = sht->vy0 + by;for (bx = bx0; bx < bx1; bx++) {vx = sht->vx0 + bx;c = buf[by * sht->bxsize + bx];if (c != sht->col_inv) {vram[vy * ctl->xsize + vx] = c;}}}}return;
}

这样修改之后,鼠标在画面边缘的显示就正常多了
在这里插入图片描述
2. 窗口显示

在进入下一步 制作窗口之前,先进行一个小优化。

之前图层控制的数据结构SHTCTL *ctl,在每次进行图层相关的操作时,都需要将ctl指针作为参数传入进去。为了简化,将这一指针加入图层信息描述的结构体中,这样每次操作图层时从图层信息结构体中获取该指针即可,不需要再作为参数单独传入了。

struct SHEET {unsigned char *buf;int bxsize, bysize, vx0, vy0, col_inv, height, flags;struct SHTCTL *ctl; /* 此处增加图层控制指针 */
};

在初始化函数shtctl_init中,需要给此指针赋值:

struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize)
{struct SHTCTL *ctl;int i;ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof (struct SHTCTL));if (ctl == 0) {goto err;}ctl->vram = vram;ctl->xsize = xsize;ctl->ysize = ysize;ctl->top = -1;for (i = 0; i < MAX_SHEETS; i++) {ctl->sheets0[i].flags = 0; ctl->sheets0[i].ctl = ctl; }
err:return ctl;
}

之前所有涉及到ctl指针的部分也都要一并修改。

接下来就可以制作窗口了。其实制作窗口也是准备一个图层,并在上面绘制图案,这里作者写了一个函数make_window8

void make_window8(unsigned char *buf, int xsize, int ysize, char *title)
{static char closebtn[14][16] = {"OOOOOOOOOOOOOOO@","OQQQQQQQQQQQQQ$@","OQQQQQQQQQQQQQ$@","OQQQ@@QQQQ@@QQ$@","OQQQQ@@QQ@@QQQ$@","OQQQQQ@@@@QQQQ$@","OQQQQQQ@@QQQQQ$@","OQQQQQ@@@@QQQQ$@","OQQQQ@@QQ@@QQQ$@","OQQQ@@QQQQ@@QQ$@","OQQQQQQQQQQQQQ$@","OQQQQQQQQQQQQQ$@","O$$$$$$$$$$$$$$@","@@@@@@@@@@@@@@@@"};int x, y;char c;boxfill8(buf, xsize, COL8_C6C6C6, 0,         0,         xsize - 1, 0        );boxfill8(buf, xsize, COL8_FFFFFF, 1,         1,         xsize - 2, 1        );boxfill8(buf, xsize, COL8_C6C6C6, 0,         0,         0,         ysize - 1);boxfill8(buf, xsize, COL8_FFFFFF, 1,         1,         1,         ysize - 2);boxfill8(buf, xsize, COL8_848484, xsize - 2, 1,         xsize - 2, ysize - 2);boxfill8(buf, xsize, COL8_000000, xsize - 1, 0,         xsize - 1, ysize - 1);boxfill8(buf, xsize, COL8_C6C6C6, 2,         2,         xsize - 3, ysize - 3);boxfill8(buf, xsize, COL8_000084, 3,         3,         xsize - 4, 20       );boxfill8(buf, xsize, COL8_848484, 1,         ysize - 2, xsize - 2, ysize - 2);boxfill8(buf, xsize, COL8_000000, 0,         ysize - 1, xsize - 1, ysize - 1);putfonts8_asc(buf, xsize, 24, 4, COL8_FFFFFF, title);for (y = 0; y < 14; y++) {for (x = 0; x < 16; x++) {c = closebtn[y][x];if (c == '@') {c = COL8_000000;} else if (c == '$') {c = COL8_848484;} else if (c == 'Q') {c = COL8_C6C6C6;} else {c = COL8_FFFFFF;}buf[(5 + y) * xsize + (xsize - 21 + x)] = c;}}return;
}

虽然程序代码看起来不少,其实与之前描绘鼠标的原理一样,只是在适当的位置描绘出图案。这样在主函数中也需要增加一些内容:

/* 新增了窗口的图层管理指针与buf*/struct SHEET *sht_back, *sht_mouse, *sht_win;unsigned char *buf_back, buf_mouse[256], *buf_win;/* 为窗口分配图层和内存*/sht_win   = sheet_alloc(shtctl);buf_win   = (unsigned char *) memman_alloc_4k(memman, 160 * 68);sheet_setbuf(sht_win, buf_win, 160, 68, -1);init_screen8(buf_back, binfo->scrnx, binfo->scrny);init_mouse_cursor8(buf_mouse, 99);make_window8(buf_win, 160, 68, "window");putfonts8_asc(buf_win, 160, 24, 28, COL8_000000, "Welcome to");putfonts8_asc(buf_win, 160, 24, 44, COL8_000000, "  Haribote-OS!");sheet_slide(sht_win, 80, 72);sheet_updown(sht_back,  0);sheet_updown(sht_win,   1);sheet_updown(sht_mouse, 2);

从程序中可以看出,将窗口图层的高度设置为1,处于背景图层与鼠标图层之间,这样鼠标就可以位于窗口图层之上。运行之后的显示结果如下
在这里插入图片描述窗口的显示完成了,下面更进一步,由CPU进行计数,并将数字在窗口中显示出来:

void HariMain(void)
{struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;char s[40], keybuf[32], mousebuf[128];int mx, my, i;unsigned int memtotal, count = 0;struct MOUSE_DEC mdec;struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;struct SHTCTL *shtctl;struct SHEET *sht_back, *sht_mouse, *sht_win;unsigned char *buf_back, buf_mouse[256], *buf_win;init_gdtidt();init_pic();io_sti();fifo8_init(&keyfifo, 32, keybuf);fifo8_init(&mousefifo, 128, mousebuf);io_out8(PIC0_IMR, 0xf9); io_out8(PIC1_IMR, 0xef); init_keyboard();enable_mouse(&mdec);memtotal = memtest(0x00400000, 0xbfffffff);memman_init(memman);memman_free(memman, 0x00001000, 0x0009e000); /* 0x00001000 - 0x0009efff */memman_free(memman, 0x00400000, memtotal - 0x00400000);init_palette();shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);sht_back  = sheet_alloc(shtctl);sht_mouse = sheet_alloc(shtctl);sht_win   = sheet_alloc(shtctl);buf_back  = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);buf_win   = (unsigned char *) memman_alloc_4k(memman, 160 * 52);sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1); sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);sheet_setbuf(sht_win, buf_win, 160, 52, -1); init_screen8(buf_back, binfo->scrnx, binfo->scrny);init_mouse_cursor8(buf_mouse, 99);make_window8(buf_win, 160, 52, "counter");/* 增加计数器 */sheet_slide(sht_back, 0, 0);mx = (binfo->scrnx - 16) / 2; my = (binfo->scrny - 28 - 16) / 2;sheet_slide(sht_mouse, mx, my);sheet_slide(sht_win, 80, 72);sheet_updown(sht_back,  0);sheet_updown(sht_win,   1);sheet_updown(sht_mouse, 2);sprintf(s, "(%3d, %3d)", mx, my);putfonts8_asc(buf_back, binfo->scrnx, 0, 0, COL8_FFFFFF, s);sprintf(s, "memory %dMB   free : %dKB",memtotal / (1024 * 1024), memman_total(memman) / 1024);putfonts8_asc(buf_back, binfo->scrnx, 0, 32, COL8_FFFFFF, s);sheet_refresh(sht_back, 0, 0, binfo->scrnx, 48);for (;;) {count++; /* 计数 */sprintf(s, "%010d", count);boxfill8(buf_win, 160, COL8_C6C6C6, 40, 28, 119, 43);putfonts8_asc(buf_win, 160, 40, 28, COL8_000000, s);sheet_refresh(sht_win, 40, 28, 120, 44);io_cli();if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {io_sti();} else {if (fifo8_status(&keyfifo) != 0) {i = fifo8_get(&keyfifo);io_sti();sprintf(s, "%02X", i);boxfill8(buf_back, binfo->scrnx, COL8_008484,  0, 16, 15, 31);putfonts8_asc(buf_back, binfo->scrnx, 0, 16, COL8_FFFFFF, s);sheet_refresh(sht_back, 0, 16, 16, 32);} else if (fifo8_status(&mousefifo) != 0) {i = fifo8_get(&mousefifo);io_sti();if (mouse_decode(&mdec, i) != 0) {sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);if ((mdec.btn & 0x01) != 0) {s[1] = 'L';}if ((mdec.btn & 0x02) != 0) {s[3] = 'R';}if ((mdec.btn & 0x04) != 0) {s[2] = 'C';}boxfill8(buf_back, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);putfonts8_asc(buf_back, binfo->scrnx, 32, 16, COL8_FFFFFF, s);sheet_refresh(sht_back, 32, 16, 32 + 15 * 8, 32);mx += mdec.x;my += mdec.y;if (mx < 0) {mx = 0;}if (my < 0) {my = 0;}if (mx > binfo->scrnx - 1) {mx = binfo->scrnx - 1;}if (my > binfo->scrny - 1) {my = binfo->scrny - 1;}sprintf(s, "(%3d, %3d)", mx, my);boxfill8(buf_back, binfo->scrnx, COL8_008484, 0, 0, 79, 15); putfonts8_asc(buf_back, binfo->scrnx, 0, 0, COL8_FFFFFF, s); sheet_refresh(sht_back, 0, 0, 80, 16);sheet_slide(sht_mouse, mx, my);}}}}
}

在这里插入图片描述
显示的结果就是窗口中的数字在不断增加。但是数字显示的地方闪烁很严重,能看到背景色在一闪一闪。这是因为在刷新图层的时候,总是先刷新背景图层,再刷新窗口图层,因为数字的更新,刷新一直在进行,就会导致背景色一直在闪烁。

解决这一问题的方法很简单,如果只有窗口变化,其实不用刷新背景图层。因此refresh只对对象图层及其上面的图层进行刷新就可以了。实际修改时,sheet_refresh函数只刷新指定的图层及其上面的图层;而sheet_slide函数中,图层的移动有时会导致下面的图层露出,因此需要从最下面的图层开始刷新,而对于移动后的位置,也只需要刷新目标图层与上面的图层即可。

进行这些修改之后,计数器的数字显示闪烁问题解决了,但将鼠标放在计数器上面,又会发现鼠标的闪烁问题。这里需要另外的解决方法了。

struct SHTCTL {unsigned char *vram, *map;int xsize, ysize, top;struct SHEET *sheets[MAX_SHEETS];struct SHEET sheets0[MAX_SHEETS];
};struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize)
{struct SHTCTL *ctl;int i;ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof (struct SHTCTL));if (ctl == 0) {goto err;}ctl->map = (unsigned char *) memman_alloc_4k(memman, xsize * ysize);if (ctl->map == 0) {memman_free_4k(memman, (int) ctl, sizeof (struct SHTCTL));goto err;}ctl->vram = vram;ctl->xsize = xsize;ctl->ysize = ysize;ctl->top = -1; for (i = 0; i < MAX_SHEETS; i++) {ctl->sheets0[i].flags = 0;ctl->sheets0[i].ctl = ctl;}
err:return ctl;
}

这里开辟了一块与VRAM同样大小的内存,命名为map,用来表示画面上的点是哪个图层的像素,如图所示:
在这里插入图片描述
多个图层重叠的情况下,根据这个map进行刷新,就可以清楚地确定图层互相之间的覆盖关系了。

使用map首先要向map中写入图层的号码

void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
{int h, bx, by, vx, vy, bx0, by0, bx1, by1;unsigned char *buf, sid, *map = ctl->map;struct SHEET *sht;if (vx0 < 0) { vx0 = 0; }if (vy0 < 0) { vy0 = 0; }if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }for (h = h0; h <= ctl->top; h++) {sht = ctl->sheets[h];sid = sht - ctl->sheets0; /* 将进行了减法计算的地址作为图层号码使用*/buf = sht->buf;bx0 = vx0 - sht->vx0;by0 = vy0 - sht->vy0;bx1 = vx1 - sht->vx0;by1 = vy1 - sht->vy0;if (bx0 < 0) { bx0 = 0; }if (by0 < 0) { by0 = 0; }if (bx1 > sht->bxsize) { bx1 = sht->bxsize; }if (by1 > sht->bysize) { by1 = sht->bysize; }for (by = by0; by < by1; by++) {vy = sht->vy0 + by;for (bx = bx0; bx < bx1; bx++) {vx = sht->vx0 + bx;if (buf[by * sht->bxsize + bx] != sht->col_inv) {map[vy * ctl->xsize + vx] = sid;}}}}return;
}

这个函数的原理其实与refreshsub函数基本一样,只不过后者是写入色号,而这里是把图层号码sid写入到map。

同样改写了sheet_refreshsub函数,使其可以使用map:

void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1)
{int h, bx, by, vx, vy, bx0, by0, bx1, by1;unsigned char *buf, *vram = ctl->vram, *map = ctl->map, sid;struct SHEET *sht;if (vx0 < 0) { vx0 = 0; }if (vy0 < 0) { vy0 = 0; }if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }for (h = h0; h <= h1; h++) {sht = ctl->sheets[h];buf = sht->buf;sid = sht - ctl->sheets0;bx0 = vx0 - sht->vx0;by0 = vy0 - sht->vy0;bx1 = vx1 - sht->vx0;by1 = vy1 - sht->vy0;if (bx0 < 0) { bx0 = 0; }if (by0 < 0) { by0 = 0; }if (bx1 > sht->bxsize) { bx1 = sht->bxsize; }if (by1 > sht->bysize) { by1 = sht->bysize; }for (by = by0; by < by1; by++) {vy = sht->vy0 + by;for (bx = bx0; bx < bx1; bx++) {vx = sht->vx0 + bx;if (map[vy * ctl->xsize + vx] == sid) {vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];}}}}return;
}

根据map中记录的图层编号,分别进行刷新。由于参照map进行刷新,不需要再从下到上刷新每一个图层,因此这个函数增加了传参h1,用于和h0一起指定刷新图层的范围。

对于sheet_slide做如下修改:

void sheet_slide(struct SHEET *sht, int vx0, int vy0)
{struct SHTCTL *ctl = sht->ctl;int old_vx0 = sht->vx0, old_vy0 = sht->vy0;sht->vx0 = vx0;sht->vy0 = vy0;if (sht->height >= 0) { sheet_refreshmap(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);sheet_refreshmap(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height);sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0, sht->height - 1);sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height, sht->height);}return;
}

在sheet_slide函数中,首先重写map,分别对应移动前后的图层。然后调用sheet_refrehsub函数。对于原位置,需要对上层图层移动后的下层图层重新绘制;而在新位置,只需要绘制移动过去的图层即可。

简单理解,其实就是通过map把叠加后的显示结果存储起来,最终绘制的时候只需要绘制一张画面,而不是从下向上描绘多个图层来最终实现叠加效果,这样避免了从下而上的刷新,就消除了闪烁。相比原来实现叠加的方法也更进了一步。

本篇内容其实修改增加的代码不多,主要还是在前文的基础上进行,但是方法概念需要好好理解。显示问题暂告一段落,下一篇开始定时器的相关内容,敬请期待。

这篇关于【读书笔记-《30天自制操作系统》-10】Day11的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1120264

相关文章

30常用 Maven 命令

Maven 是一个强大的项目管理和构建工具,它广泛用于 Java 项目的依赖管理、构建流程和插件集成。Maven 的命令行工具提供了大量的命令来帮助开发人员管理项目的生命周期、依赖和插件。以下是 常用 Maven 命令的使用场景及其详细解释。 1. mvn clean 使用场景:清理项目的生成目录,通常用于删除项目中自动生成的文件(如 target/ 目录)。共性规律:清理操作

2024网安周今日开幕,亚信安全亮相30城

2024年国家网络安全宣传周今天在广州拉开帷幕。今年网安周继续以“网络安全为人民,网络安全靠人民”为主题。2024年国家网络安全宣传周涵盖了1场开幕式、1场高峰论坛、5个重要活动、15场分论坛/座谈会/闭门会、6个主题日活动和网络安全“六进”活动。亚信安全出席2024年国家网络安全宣传周开幕式和主论坛,并将通过线下宣讲、创意科普、成果展示等多种形式,让广大民众看得懂、记得住安全知识,同时还

Linux操作系统 初识

在认识操作系统之前,我们首先来了解一下计算机的发展: 计算机的发展 世界上第一台计算机名叫埃尼阿克,诞生在1945年2月14日,用于军事用途。 后来因为计算机的优势和潜力巨大,计算机开始飞速发展,并产生了一个当时一直有效的定律:摩尔定律--当价格不变时,集成电路上可容纳的元器件的数目,约每隔18-24个月便会增加一倍,性能也将提升一倍。 那么相应的,计算机就会变得越来越快,越来越小型化。

《C++标准库》读书笔记/第一天(C++新特性(1))

C++11新特性(1) 以auto完成类型自动推导 auto i=42; //以auto声明的变量,其类型会根据其初值被自动推倒出来,因此一定需要一个初始化操作; static auto a=0.19;//可以用额外限定符修饰 vector<string> v;  auto pos=v.begin();//如果类型很长或类型表达式复杂 auto很有用; auto l=[] (int

c++习题30-求10000以内N的阶乘

目录 一,题目  二,思路 三,代码    一,题目  描述 求10000以内n的阶乘。 输入描述 只有一行输入,整数n(0≤n≤10000)。 输出描述 一行,即n!的值。 用例输入 1  4 用例输出 1  24   二,思路 n    n!           0    1 1    1*1=1 2    1*2=2 3    2*3=6 4

读书笔记(一):双脑记

谁又知道年轻人那反复无常的大脑有着怎样的运行机制?尽管他们的大脑已被荷尔蒙折腾地七荤八素;却偶尔还会有灵感跻身夹缝之间; 层级化:每时每刻,人类都在进行抽象化,也就是说,从客观事实中发展出更具普遍意义的理论和知识。利用这种方法,我们得以不断地开发出新的更为简洁的描述层级,方便我们那容量有限的大脑加以处理。分层的概念几乎可以应用于任何复杂系统,甚至包括我们的社交世界,也即是人们的个人生

嵌入式面试经典30问:二

1. 嵌入式系统中,如何选择合适的微控制器或微处理器? 在嵌入式系统中选择合适的微控制器(MCU)或微处理器(MPU)时,需要考虑多个因素以确保所选组件能够满足项目的具体需求。以下是一些关键步骤和考虑因素: 1.1 确定项目需求 性能要求:根据项目的复杂度、处理速度和数据吞吐量等要求,确定所需的处理器性能。功耗:评估系统的功耗需求,选择低功耗的MCU或MPU以延长电池寿命或减少能源消耗。成本

1、简述linux操作系统启动流程

1、简述linux操作系统启动流程 启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它。这是因为BIOS中包含了CPU的相关信息、设备启动顺序信息、硬盘信息、内存信息、时钟信息、PnP特性等等。开机时将ROM中的指令映射到RAM的低地址空间,CPU读取到这些指令,硬件的健康状况进行检查,按照BIOS中设置的启

操作系统是怎么为不同的程序分配所需的内存空间的

操作系统为不同的程序分配内存空间的过程涉及多个关键步骤,确保每个程序都有其所需的内存资源,同时避免程序之间的冲突。以下是操作系统如何为程序分配内存空间的详细过程: 1. 内存管理的基础概念 虚拟内存:现代操作系统使用虚拟内存机制来为程序提供隔离的内存空间。每个程序运行在其独立的虚拟地址空间中,这使得程序间的内存互不干扰。物理内存:实际的 RAM(随机存取存储器),由操作系统和硬件共同管理。虚拟

【全网最全】2024年数学建模国赛A题30页完整建模文档+17页成品论文+保奖matla代码+可视化图表等(后续会更新)

您的点赞收藏是我继续更新的最大动力! 一定要点击如下的卡片,那是获取资料的入口! 【全网最全】2024年数学建模国赛A题30页完整建模文档+17页成品论文+保奖matla代码+可视化图表等(后续会更新)「首先来看看目前已有的资料,还会不断更新哦~一次购买,后续不会再被收费哦,保证是全网最全资源,随着后续内容更新,价格会上涨,越早购买,价格越低,让大家再也不需要到处买断片资料啦~💰💸👋」�