自制操作系统日志——第十天

自制操作系统日志——第十天

今天的主要任务就是继续优化内存管理部分,然后进一步的做一下叠加处理,主要就了解图层的设置,整个程序利用图层的整体过程。



一、内存管理优化

由于之前的内存管理程序中,都是以1字节为单位进行管理的,这就有可能导致出现过多的不连续的小段的内存空间地址,也会重复多次的内存进行分配和释放,不利于我们系统的运行稳定性和利用率等等。

因此,下面我们将以0x1000(4kb)为单位进行内存的分配于与管理。如果分配的不足4kb则向上取整,直接默认为4kb。。对了,再此之前,我们先将内存管理的代码写入到memory.c当中,以便进行分割管理:

在memory.c中增加以下内容:

unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size)
{
	unsigned int a;
	size = (size + 0xfff) & 0xfffff000;//向上取整,确保为是以4k为单位的,不够4k的按4k算
	a = memman_alloc(man, size);
	return a; 
}

int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size)
{
	int i;
	size = (size+0xfff) & 0xfffff000;
	i = memman_free(man, addr, size);
	return i;
}

size = (size + 0xfff) & 0xfffff000
⇒ 在二进制中若以0x1000为单位对0x12345678进行向上舍入,则就变成:0x12346000。 至于这里为什么使用size + 0xfff 这里我们用十进制说明以下:
假如我们有456元,以100为单位进行向上舍入,则按上述的理解我们应该进行(456+99)= 555 ==> 500 。 z因此这个公式的意思也就差不多是这样子。至于后面的&操作就是相对于将555中后两个55舍去的意思。。

二、叠加处理

所谓的叠加处理,我们主要思想就是利用图层技术进行的。我们按高度顺序画面从下往上显示处所有的图层。(最高层的画面一般都会显现出来,下面的图层是可能会被高图层盖住的,就像纸张的叠加一样)

我们可以把图层视为一张纸,而我们画面就是纸上我们描绘的东西。也就是说最上层的纸往往是可以看见的,越下面的图层越可能被覆盖住。

以下我们来按照这个图层的概念来详细解释以下代码的逻辑:
设定图层的结构体:

#define MAX_SHEETS 256 //能管理的最大数
struct SHEET
{
	unsigned char *buf;//记录图层上所描绘图画的地址
	int bxsize, bysize, vx0, vy0, col_inv, height, flags;
	//图层整体大小用bxsize、bysize表示;vx0, vy0 图层在vram画面上位置的相对坐标
	//col_inv 表颜色的透明度;  height 表图层高度;flag记录图层各种设定信息
};
struct SHTCTL//图层管理
{
	unsigned char *vram;//vram x y 代表VRAM的地址和画面大小
	int xsize, ysize, top;//top最上面图层的高度
	struct SHEET *sheets[MAX_SHEETS];//记忆地址变量领域,由于shets0之中的图层比较混乱,因此这里将图层能照高度升序进行写入
	struct SHEET sheets0[MAX_SHEETS];//存放准备好的256个图层信息
};

sheet.c:

//初始化图层管理表的地址
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize)
{
	struct SHTCTL *ctl;
	int i;
	//返回一个空闲的addr地址,用sizeof计算该变量的地址空间大小,然后进行分配对应的内存空间大小
	ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof (struct SHTCTL));
	if(ctl == 0)
	{
		goto err;
	} 
	ctl->vram  = vram; //导入vram的地址和分辨率
	ctl->xsize = xsize;
	ctl->ysize = ysize;
	ctl->top = -1;//此时,一个SHEET都没有,也就是说没有图层在最上面
	for(i = 0; i <  MAX_SHEETS; i++)
	{
		ctl->sheets0[i].flags = 0;//让所有的图层都标记未使用
	}
err:
    return ctl;
}

给管理表初始化,并分配一段空闲的内存空间,将vram显卡的数据导入其中。

sheet.c:

//取得新生成的未使用的图层
struct SHEET *sheet_alloc(struct SHTCTL *ctl)
{
	struct SHEET *sht; //建立一个新的图层,以便进行后续的返回
	int i;
	for(i = 0; i < MAX_SHEETS; i++) //开始循环,直到找到可用的图层为止
	{
		if(ctl->sheets0[i].flags == 0)
		{
			sht = &ctl->sheets0[i];//将该可用图层的地址传入给sht
			sht->flags = SHEET_USE; //标记为正在使用
			sht->height = -1; //隐藏,先将高度设为-1,置于底层
			return sht;
		}
	}
	return 0; //所有的sheet都在使用当中
}

分配一个可用的图层,并先将图层数据写入sheets0中,sheets0仅仅是用于存储图层的数据,我们后续显示在屏幕当中的是利用按高度拍好顺序的sheets中的图层数据。

sheet.c:

//设定图层的缓冲区大小和透明色的函数
void sheet_setbuf(struct SHEET *sht, unsigned char *buf, int xsize, int ysize, int col_inv)
{//将图层和图层在画面中的大小以及
	sht->buf = buf;//记录该图层所描绘图形的地址
	sht->bxsize = xsize;//图层整体大小
	sht->bysize = ysize;
	sht->col_inv = col_inv;//给予透明色号
	return;
}

这里主要是设定每一层的图层的整体大小,以及透明度的设定!!除此之外,还将个图层的缓冲区地址传入其中。

sheet.c:

//指定了vx0~vy1 的刷新范围
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;//记录图层上描绘图画的地址
		//使用vx0~vy1,对比bx0~by1进行倒推
		bx0 = vx0 - sht->vx0;
		by0 = vy0 - sht->vy0;
		bx1 = vx1 - sht->vx0;
		by1 = vy1 - sht->vy0;
		if(bx0 < 0) {bx0 = 0;} //这里的bx0、1都是相对距离,即相对于sht->vx0,也就是刷新的部分=图层在画面上的的起始地址+相对于起始地址的距离
		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;//vy0 vx0代表图层在VRAM上画面的相对地址,用这个来让图层进行移动
				c = buf[by * sht->bxsize + bx];//记录原图层对应像素点上的颜色信息
				if (c != sht->col_inv) //如果透明了就不往里写,例如:鼠标图层中是会有一部分不用往里面写入颜色的,即init_mouse中的bc值,如果读取的c值和图层中的col值一样就不写入,默认为前一个图层的颜色
				{
					vram[vy * ctl->xsize + vx] = c;//写入vram中
				}
			}	

		}
	}
	
}

这里是指定图层在移动后,或者位置改变后进行刷新的部分。利用仅刷新指定范围内的数据,来减少程序的运行的时间(因为这样子可以不用再将所有的像素全部刷新一遍了)。

至于图中的bx by的那四个if判断的语句,是为了便于在两个图层有重叠部分时,仅仅只刷新上方图层的重叠部分的!!!

在这里插入图片描述

如图所示,如果需要刷新第i个图层时(此时绿色框的作为i-1的图层已经刷新完毕了),由于刷新的区间为vx0~vy1这部分之间,因此至于图中的蓝色部分是重叠的需要刷新的。

因此,我们首先使用bx0 = vx0 - sht->vx0 ; 由于我们前面所作的坐标是以向下为y轴正方向,以向右为x轴正方向的。因此,这里bx0 一定会为负数。由于我们的bx0主要是作为刷新时的相对于图层框起始地址的偏移地址所使用。因此,若我们设置为0,则说明我们不需要在加上相对位置了,因此直接在下面的计算中当 for (bx = bx0; bx < bx1; bx++) vx = sht->vx0 + bx;开始时,就可以直接从sht->vx0处开始,即重叠部分的左上角处开始。

同理,这里的bx1 = vx1 - sht->vx0 ,也就是说,我们的bx实际上就是从sht->vx0 处开始刷新直到vx1 - sht->vx0 处,也就是蓝色填充框的右下角处。这样子我们就可以直接指定范围进行更新了,极大的减少了对其他无关像素的刷新操作。

对于 if(bx1 > sht->bxsize) {bx1 = sht->bxsize;} 这两个的判断,主要用于其他重叠的情况,比如在右上角重叠时。 开始刷新的部分就应该为sht->vx0 + (vx0 - sht->vx0) 【这里是因为vx0 - sht->vx0 >0 不需要改变】,而 bx1则直接就为sht->bxize , 也就是设定好了右下角的部分。

进一步继续修改,设定高度,以及更新sheets
sheet.c:

//设定底板的高度
void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height)
{
	int h, old = sht->height; //存储之前的高度信息

	//如果指定的高度过高或者过低,则进行修正
	if(height > ctl->top+1)
	{
		height = ctl->top+1;
	}
	if(height < -1)
	{
		height = -1;
	}
	sht->height = height; //设定高度

	//进行sheets[]的重新排列,也就是按高度顺序写入sheets中
	if(old > height)
	{//比以前低
		if(height >= 0) //把中间的往上提
		{
			for(h = old; h > height; h--)
			{
				ctl->sheets[h] = ctl->sheets[h-1];
				ctl->sheets[h]->height = h;
			}
			ctl->sheets[height] = sht; 
		}else{// 隐藏
			if(ctl->top > old)//把上面的降下来
			{
				for(h = old; h < ctl->top; h++)
				{
					ctl->sheets[h] = ctl->sheets[h+1];
					ctl->sheets[h]->height = h;
				}
			}
			ctl->top--; //由于中间图层减少一个,所以最上面的图层高度下降
		}
		sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize); //按新的图层信息重新绘制画面
	}else if(old < height)//比以前高
	{
		if(old > 0)
		{
		//把中间的拉下去
		for(h = old; h < height; h++)
		{
			ctl->sheets[h] = ctl->sheets[h+1];
			ctl->sheets[h]->height = h;
		}
		ctl->sheets[height] = sht;
		}else{//由隐藏状态转化为显示状态
			//将已在上面的提上来
			for(h = ctl->top; h >= height ; h--)
			{
				ctl->sheets[h+1] = ctl->sheets[h];
				ctl->sheets[h+1]->height =h+1;
			}
			ctl->sheets[height] = sht;
			ctl->top++;//由于已显示的图层增加一个,所以最上面的图层高度增加
		}
		sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize);//按新图层重新绘制画面
	}
	return;
}
//从下往上,将透明以外的所有像素都复制到vram中
void sheet_refresh(struct SHTCTL *ctl, struct SHEET *sht, int bx0, int by0, int bx1, int by1)
{
	if (sht->height >= 0) { /* 如果正在显示,则按新图层进行刷新 */
		sheet_refreshsub(ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1);
	}
	return;
}

//进行上下左右移动
void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0)
{
	int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
	sht->vx0 = vx0;
	sht->vy0 = vy0;
	if(sht->height >= 0)//如果正在显示//按图层信息刷新画面
	{
		/*由于鼠标图层的地址已经改变了,因此以下两条语句:(因为sub这个是只刷新指定范围内的)
		如果只执行第一句的话,那么新鼠标无法显示(执行完后,因为鼠标图层改变了,不再源像位置了,因此图层只会刷新原先地方存在的图层的)
		如果只执行第二句的话,那么可能会显示不完整
		*/
		sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize);
		sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize);
	}
	return;
}
//释放已使用的图层
void sheet_free(struct SHTCTL *ctl, struct SHEET *sht)
{
	if(sht->height >= 0)
	{
		sheet_updown(ctl, sht, -1);//处于显示状态,先设定隐藏
	}
	sht->flags = 0; //为使用标志
	return;
}

然后,让我们来看看主函数的部分:

struct SHTCTL *shtctl;//用于管理的
	struct SHEET *sht_back, *sht_mouse;//准备了两个图层
	unsigned char *buf_back, buf_mouse[256];//缓冲区,用于在其中描绘需要的图形。binfo->vram = buf_back

init_gdtidt();//初始化gdt、idt表
	init_pic();  //初始化pic控制器
	io_sti();
    fifo8_init(&keyfifo, 32, keybuf);
    fifo8_init(&mousefifo, 128, mousebuf);
    io_out8(PIC0_IMR, 0xf9); /* 开放PIC1和键盘中断(11111001),键盘是IRQ1 */
	io_out8(PIC1_IMR, 0xef); /* 开放鼠标中断(11101111) ,鼠标是IRQ12*/

    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);//分配给背景一个图层,背景图层位于最底层,sheets0[0]
	sht_mouse = sheet_alloc(shtctl);//sheets0[1]
	buf_back  = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);//分配给back一个与vram大小一样的地址
	sheet_setbuf(sht_back, buf_back,binfo->scrnx, binfo->scrny, -1);//没有透明色,由于我们已经给图层管理表的初始地址设定在了vram当中,因此任何图层都会在vram空间里
	sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);//鼠标像素阵16x16,给予最不透明度
    init_screen8(buf_back, binfo->scrnx, binfo->scrny);//往back地址中填入颜色
    init_mouse_cursor8(buf_mouse, 99); //背景色99号,往mouse中填入颜色和透明度
    sheet_slide(shtctl, sht_back, 0, 0);//将背景页面不移动,直接覆盖整个画面
 
	mx = (binfo->scrnx - 16) / 2; /* 计算画面的中心坐标*/
	my = (binfo->scrny - 28 - 16) / 2;
	sheet_slide(shtctl, sht_mouse, mx, my);//移动鼠标到画面中心
	sheet_updown(shtctl, sht_back,  0);//背景图的高度设置为0,并且将sheets按高度顺序写入
	sheet_updown(shtctl, sht_mouse, 1);//鼠标图层高度设置为1
	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(shtctl, sht_back, 0, 0, binfo->scrnx, 48);

	for(;;)   
	{
		io_cli(); //IF=0
		if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0)
		{
			io_stihlt();
		}else 
		{ 
			if (fifo8_status(&keyfifo) != 0)
			{
			   i = fifo8_get(&keyfifo);
			   io_sti();
			   sprintf(s,"%02x",i);
			   boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
			   putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
		       sheet_refresh(shtctl, 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[3] = 'C';
				}
				boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32+15*8 - 1, 31);
				putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
				sheet_refresh(shtctl, 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 - 16)
				{
					mx = binfo->scrnx - 16;
				}
				if(my > binfo->scrny - 16)
				{
					my = binfo->scrny - 16;
				}
				sprintf(s,"(%3d,%3d)", mx, my);
				boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15);//隐藏坐标
				putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);//显示坐标
				sheet_refresh(shtctl, sht_back, 0, 0, 80, 16);
				sheet_slide(shtctl, sht_mouse, mx, my);//包含sheet_slide与sheet_refresh
				}
		    }
	   }
	}
}

然后,make run一下:
在这里插入图片描述


总结

以上就是关于图层叠加的部分,这一张部分可以多看看代码,理解一下图层叠加这个有意思的东西。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值