《30天自制操作系统》10_day_学习笔记

harib07a:
  整理内存管理函数:memman_alloc和memman_free能够以最小1字节进行内存管理,但时间久了后,容易产生外部碎片;为此,笔者编写了一些以0x1000字节为单位进行内存分配和释放的函数,它们会把指定的内存大小按照0x1000字节为单位向上舍入(roundup),0x1000大小正好是4KB.
  1、向下舍入:以0x1000为单位向下舍入
    0x1234_5678 & 0xffff_f000 = 0x1234_5000;
    i = i & 0xffff_f000 ;
  2、向上舍入:以0x1000为单位向上舍入
    i = (i + 0xfff) & 0xffff_f000

unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size) {
    unsigned int a;
    //以0x1000为单位向上舍入的方式进行内存分配
    size = (size + 0xfff) & 0xfffff000;
    a = memman_alloc(man, size);
    return a;
}
int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size) {
    int i;
    //以0x1000为单位向上舍入的方式进行内存释放
    size = (size + 0xfff) & 0xfffff000;
    i = memman_free(man, addr, size);
    return i;
}

harib07b:
  图层的定义:下面我们来解决图层叠加的问题,编写一段程序,既适用于于鼠标的叠加,也适应于窗口的叠加!

#define MAX_SHEETS        256
//图层的整体大小:bxsize*byseze
//vx0和vy0表示图层在画面上坐标的位置
//col_inv表示透明色色号
//height :表示图层的高度
//flags : 用于存放图层的各种设定信息
struct SHEET {               //图层的结构体
    unsigned char *buf;         //记录图层上描绘内容的结构体
    int bxsize, bysize, vx0, vy0, col_inv, height, flags;
};
/* 图层管理的结构体;实现图层的叠加 */
struct SHTCTL {
    unsigned char *vram;        //VRAM地址
    int xsize, ysize, top;       //VRAM画面的大小;top:最上面图层的高度
    struct SHEET *sheets[MAX_SHEETS];//sheets0[]啊没找高度排序后的图层地址
    struct SHEET sheets0[MAX_SHEETS];//用于存放256个图层的信息
};

  图层的处理:界面图层处理的函数都被封装在sheet.c中,关于图层的具体实现方法和原理请对照图片和注释看

  (注释已经很详细了,应该能看懂!代码有点长,我们折起来吧!)

   

//sheet.c
#include "bootpack.h"

#define SHEET_USE        1
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;                              /* 表示没有图层(0) */
    for (i = 0; i < MAX_SHEETS; i++) {
        ctl->sheets0[i].flags = 0;               /* 标记为未使用 */
    }
err:
    return ctl;                                //初始化失败
}

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->flags = SHEET_USE;            /* 要分配,标记为正在使用 */
            sht->height = -1;                 /* 隐藏,此时高度还没有设置 */
            return sht;                      //取得(分配)成功
        }
    }
    return 0;                        /* 没有空闲图层,分配失败 */
}
//设定图层缓冲区大小和透明色的函数
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;
}
//设定底板高度的函数:
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[]重新排序 */
    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 {                            /* 高度小于0时,表示图层没有初始化,隐藏 */
            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_refresh(ctl);                    /* 按照新图层的信息重新绘制画面 */
    } 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++;                      /* 由于已显示的图层增加了1个,所以最上面的图层高度增加 */
        }
        sheet_refresh(ctl);                   /* 按照新图层信息重新描绘画面 */
    }
    return;
}
//图层刷新函数。
//刷新原理:对于已经设定的高度的所有图层,从下往上,将透明以外的所有像素都复制到VRAM中,由于是从下往上复制,所以最后最上面的内容就留在了画面上。
void sheet_refresh(struct SHTCTL *ctl)
{
    int h, bx, by, vx, vy;
    unsigned char *buf, c, *vram = ctl->vram;
    struct SHEET *sht;
    for (h = 0; h <= ctl->top; h++) {           //从图层0 开始,向高图层一个一个走
        sht = ctl->sheets[h];                  //获得每一层的地址
        buf = sht->buf;                     //将该地址的内容放到VRAM缓冲区
        for (by = 0; by < sht->bysize; by++) {    //VRAM按照从左到右,从上到下的顺序,不断的更新VRAM缓存
            vy = sht->vy0 + by;
            for (bx = 0; bx < sht->bxsize; bx++) {
                vx = sht->vx0 + bx;
                c = buf[by * sht->bxsize + bx];
                if (c != sht->col_inv) {
                    vram[vy * ctl->xsize + vx] = c;
                }
            }
        }
    }
    return;
}
//功能:不改变图层的高度,只上下左右移动图层(滑动)
void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0)
{
    sht->vx0 = vx0;
    sht->vy0 = vy0;
    if (sht->height >= 0) {               /* 正在显示 */
        sheet_refresh(ctl);               /* 按照新图层的信息刷新页面 */
    }
    return;
}
//释放已经使用图层的内存的函数
void sheet_free(struct SHTCTL *ctl, struct SHEET *sht)
{
    if (sht->height >= 0) {
        sheet_updown(ctl, sht, -1);        /* 如果该图层正在显示,设置height=-1;隐藏 */
    }
    sht->flags = 0;                     /* FLAG=0表示该图层未使用 */
    return;
}
View Code

harib07c:
  上一步我们实现了界面图层显示的叠加,但当我们MAKE RUN 发现实在是太慢了!接下来我们来提高图层叠加处理的速度!
  1、提高刷新的速度
  从void sheet_refresh(struct SHTCTL *ctl)中我们可以看到,只要鼠标动了,就会对整个界面进行刷新!然而我们只需让它刷新相关的部分。我们在函数sheet_refresh()添加一个条件函数IF,只刷新鼠标经过的部分。

void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1){
    int h, bx, by, vx, vy;
    unsigned char *buf, c, *vram = ctl->vram;
    struct SHEET *sht;
    for (h = 0; h <= ctl->top; h++) {
        sht = ctl->sheets[h];
        buf = sht->buf;
        for (by = 0; by < sht->bysize; by++) {
            vy = sht->vy0 + by;
            for (bx = 0; bx < sht->bxsize; bx++) {
                vx = sht->vx0 + bx;
                if (vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1) {
                    c = buf[by * sht->bxsize + bx];
                    if (c != sht->col_inv) {
                        vram[vy * ctl->xsize + vx] = c;
                    }
                }
            }
        }
    }
    return;
}

  2、使用fefreshsub提高sheet_slide的速度

  //首先记住移动前的位置,在设定新的位置,最后只要重新描绘移动前和移动后的地方。
  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);

  3、图层内文字显示问题
  当我们在图层上显示或者改写文字时,并不是改写图层的全部内容;但是我们却要每次重写64000个像素的内容,怎么办?我们重新来编写sheet_refresh()函数吧!把文字显示单独处理。
  注  意:指定的范围不是直接指定画面内的左边,而是以缓冲区的坐标来表示的。

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;
}

  同时也要对sheet_updown进行修改,当然改动的只有sheet_refresh(ctl):
  sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize);

harib07c:
  make run一下,还是感觉有些别扭;我们看看还有那些可以改进的地方。我们看看上面的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;
        /* 使用vx0--vy1,对bx0--by1进行倒推得到图层中需要刷新的范围
            vx = sht->vx0 ; --> bx = vx - sht->vx0;  */
        bx0 = vx0 - sht->vx0;               //倒推得到需要刷新的范围(bx0,by0)(bx1,by1)
        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; //将该像素出缓冲区的内容给VRAM
                }
            }
        }
    }
    return;
}
原文地址:https://www.cnblogs.com/pengfeiz/p/5798664.html