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


    harib11a--harib11c:
      继续测试性能:我们在harib10h中进行了定时链表结构的改进“消除了移位处理”。下面我们设定490个定时器(它们都被设定启动50天才超时)来测试一下改进的效果。我们首先编写函数set490()来初始化490个定时器,接着测定他们在真机上的运行结果。分别运行5次,取平均值归纳如下:

    (1)追加490个定时器的值set490(&fifo,1);
      harib11a:0096521077......harib10g里加入set490(有移位)
      harib11b:0096522038......harib10h里加入set490(没有移位(链表),没有哨兵)
      harib11c:0096522097.......harib10i里加入set490(没有移位(链表),有哨兵)
    (2)不追加490个定时器的值set490(&fifo,0);
      harib11a:0096522095......harib10g里加入set490(有移位)
      harib11b:0096522038......harib10h里加入set490(没有移位(链表),没有哨兵)
      harib11c:0096522101.......harib10i里加入set490(没有移位(链表),有哨兵)
    (3)参考:不加入set490()语句的时候
      harib10g:0099969263(有移位)
      harib10h:0099969184(没有移位(链表),没有哨兵)
       harib10i:0099969264(没有移位(链表),有哨兵)

      测试结果分析:从上面的结果,我们可以看出,把定时器组织成链表的结构、在链表中加入“哨兵”都有利于系统的改进。对于(2)和(3),在HariMain中,循环执行“count++”的for语句虽然都最终被编译成JMP指令,但是如果前面加上了“set490(&fifo,0)”那么以后各个指令的地址都会相应的错开几个字节,造成JMP指令的地址有很小的变化。因此时间也稍稍延迟,执行结果大约差了3%。

    void set490(struct FIFO32 *fifo, int mode) 
    {  //定时器设置函数SET490()
        int i;
        struct TIMER *timer;    //一个TIMER结构体临时变量
        if (mode != 0) {      //mode不为0;创建490个定时器
            for (i = 0; i < 490; i++) {
                timer = timer_alloc();      //分配一个定时器的空间,并获得地址
                timer_init(timer, fifo, 1024 + i);//第i个定时器的数据为1024+i写入FIFO缓冲区
                timer_settime(timer, 100 * 60 * 60 * 24 * 50 + i * 100);//设置时间(笔者所说的50天)
            }
        }
        return;
    }

    harib11d:
      VBE介绍:由于市面上显卡公司繁多,各个显卡公司在显卡上对画面模式的设定和使用方法各不相同(就是乱七八糟的显卡没有一个统一的标准)。鉴于此,成立了一个VESA协会(Video Electronic Standards Association 视频电子标准协会)这个协会制定了几乎可以通用的画面模式设定方法,制作了专门的BIOS。这个追加的BIOS被称作“VESA BIOS extension”(VESA-BIOS的扩展,简称VBE)利用它,可以提高显卡的分辨率。
      VBE模式号码:(注:QEMU模拟器中不能指定VBE的0x107号)
        0x101......640  *480  *8bit彩色
        0x103......800  *600  *8bit彩色
        0x105......1024*768  *8bit彩色
        0x107......1280*1024*8bit彩色

    //例子:设定VBE的640*480*8bit真彩色模式
    MOV        BX,0x4101            ; BX = 0x4000 + VBE模式号码
    MOV        AX,0x4f02            ; AX = 0x4f02(这个是固定值)
    INT        0x10                 ; 调用0x10(2号)中断
    MOV        BYTE [VMODE],8          ; 记录画面模式的信息(关于画面模式信息,请看下一部分)
    MOV        WORD [SCRNX],640      ; x分辨率
    MOV        WORD [SCRNY],480       ; y分辨率
    MOV        DWORD [VRAM],0xe0000000; VRAM地址

    harib11e:
      上一步,我们已经成功在QEMU模拟器上改变了系统的分辨率。接下来,我们想看看在真机上是否也能运行。因为我们不确定真机上是什么显卡(如果机器上显卡公司没有参与VESA,那么就不能使用VBE了)。笔者在这里主要是想让我们明白VBE的原理,虽然没什么卵用,它只是显卡公司通用的一个规则而已。但认真看理解了还是会有收获的!下面我们一步一步确认。

      1、确定VBE是否存在
       方法介绍:如果显卡能利用VBE,那么VBE的信息都写在从ES:DI开始的连续512字节中的(这是规定!)。令ES = 0x9000; DI = 0;AX = 4f00; 在调用0x10中断,如果有VBE,AX就会变成0x004f(不要问为什么,这是检查规则!)

    ;确认VBE是否存在
    MOV        AX,0x9000;
    MOV        ES,AX   ;ES = 0x9000
    MOV        DI,0    ;DI = 0
    MOV        AX,0x4f00 ;AX = 4f00
    INT        0x10    ;调用0x10中断
    CMP        AX,0x004f;比较
    JNE        scrn320  ;不相等跳转AX!=0x004f

      2、检查VBE的版本

    ;如果VBE版本不是2.0以上,一样不能用高分辨率。
    MOV        AX,[ES:DI+4]
    CMP        AX,0x0200
    JB        scrn320     ; if (AX < 0x0200) goto scrn320

      3、获得某个画面模式的信息
        调用0x10中断后,会将画面模式信息保存在ES:DI开始的256字节中。在画面模式信息中,重要的信息由以下6个:
        WORD     [ES : DI+0x00]:模式属性....第7位不为1(+0x4000)
        WORD     [ES : DI+0x12]:x的分辨率
        WORD     [ES : DI+0x14]:y的分辨率
        BYTE       [ES : DI+0x19]:颜色数....必须为8
        BYTE       [ES : DI+0x1B]:颜色的指定方法....必须为4(4是标色板模式)
        DWORD   [ES : DI+0x28]:VRAM的地址

    ;这里获得VBE号为0x105的画面模式信息  : VBEMODE EQU 0X105
    MOV        CX,VBEMODE
    MOV        AX,0x4f01
    INT        0x10
    CMP        AX,0x004f
    JNE        scrn320

      4、画面模式信息的确认

    ;下面确认三个信息:颜色数、调色板模式、模式属性BIT7
    CMP        BYTE [ES:DI+0x19],8 ;确认颜色数是否为8
    JNE        scrn320
    CMP        BYTE [ES:DI+0x1b],4 ;确认调色板模式是否为4
    JNE        scrn320
    MOV        AX,[ES:DI+0x00]   ;确认模式属性bit7为1
    AND        AX,0x0080
    JZ        scrn320    

       5、画面模式切换

    ;上面都确认无误,可以进行模式切换(其实上面的确认是多次一举,主要是笔者想让我们理解VBE)
    MOV        BX,VBEMODE+0x4000 ;BX = 0x4000 + VBE模式号码
    MOV        AX,0x4f02      ;AX = 0x4f02(固定值)
    INT        0x10          ;调用0x10中断
    MOV        BYTE [VMODE],8    ; 记下画面模式
    MOV        AX,[ES:DI+0x12]   ; X分辨率
    MOV        [SCRNX],AX
    MOV        AX,[ES:DI+0x14]   ; Y分辨率
    MOV        [SCRNY],AX
    MOV        EAX,[ES:DI+0x28]  ; VRAM地址
    MOV        [VRAM],EAX
    JMP        keystatus      ; 跳到键盘的BIOS

       6、最后一步320*200的默认画面

    scrn320:
        MOV        AL,0x13            ; VGA图像,320*200
        MOV        AH,0x00
        INT        0x10
        MOV        BYTE [VMODE],8    ; 记录画面模式信息
        MOV        WORD [SCRNX],320
        MOV        WORD [SCRNY],200
        MOV        DWORD [VRAM],0x000a0000

    harib11f:
      键盘上的键按下和弹起都会有一个8位的键值。弹起的键值为松开的键值+0x80;至于键盘按下键的键值表,可以看书本的P273,也可以自行上网搜索。下面我们实现按下A键时,显示字母A。“保留?”指的是还未分配的键值,为以后键盘做准备。

    void HariMain(void)
    {    //.......
        i = fifo32_get(&fifo);//从缓冲区获取中断信息
        io_sti();
        if (256 <= i && i <= 511) {     /* 如果是键盘中断 */
            sprintf(s, "%02X", i - 256); //显示按下的键的键值
            putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
            if (i == 0x1e + 256) {    //如果按下的键值是0xle(A键)在窗口图层显示“A”
                putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, "A", 1);
            }
        }
        //......
    }

    harib11g:
      这里我们进一步改进,在键盘上按下键时,在窗口上显示相应的字符。方法:首先建立一个键盘字符表,将常用的字符放在里面,接着每按下一个键,根据键值相对字符‘0’的偏移,在表中寻找字符,然后输出到窗口即可。

    static char keytable[0x54] = {//字符表
        0,   0,   '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '^', 0,   0,
        'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '@', '[', 0,   0,   'A', 'S',
        'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', ':', 0,   0,   ']', 'Z', 'X', 'C', 'V',
        'B', 'N', 'M', ',', '.', '/', 0,   '*', 0,   ' ', 0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   '7', '8', '9', '-', '4', '5', '6', '+', '1',
        '2', '3', '0', '.'
        };
    if (256 <= i && i <= 511) {       /* FIFO中取出的额是键盘中断的数据 */
        sprintf(s, "%02X", i - 256);   //在背景图层显示键值
        putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
        if (i < 256 + 0x54) {
            if (keytable[i - 256] != 0) {//在字符表中索引,输出相应的字符
                s[0] = keytable[i - 256];
                s[1] = 0;
                putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, s, 1);
            }
        }
    }

    harib11h:
      这里我们来做一个好玩的东西。在键盘上敲字符,然后输入到窗口中。像一个小的记事本一样的。直接上代码,我们修改bootpack.c中的内容。

    //HariMain节选
    for (;;) {
            io_cli();
            if (fifo32_status(&fifo) == 0) {//缓冲区没有内容,
                io_stihlt();          //CPU等着中断
            } else {
                i = fifo32_get(&fifo);   //获取缓冲区中断的内容
                io_sti();
                if (256 <= i && i <= 511) {                  /* 得到的是键盘的数据 */
                    sprintf(s, "%02X", i - 256);              //把键盘的键值输出到背景图层
                    putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
                    if (i < 0x54 + 256) {                  //获得的键值是一般的字符
                        if (keytable[i - 256] != 0 && cursor_x < 144) { /* 字符键值没有超出字母表的范围,而且当前窗口已输入字符少于16个(144/8) */
                            s[0] = keytable[i - 256];           //HASH字符表keytable
                            s[1] = 0;                    //把字符输出到窗口图层中
                            putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, s, 1);
                            cursor_x += 8;                 //光标的位置向后移动8个像素(一个字符宽8个像素)
                        }
                    }
                    if (i == 256 + 0x0e && cursor_x > 8) { /* 按下的是Backspace,并且光标没有到行首 */
                                            /* 在光标后面的一个字符用‘ ’覆盖 */
                        putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, " ", 1);
                        cursor_x -= 8;            //光标的位置迁移8个像素
                    }
                    /* 再次显示和刷新光标 */
                    boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
                    sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);
                } else if (512 <= i && i <= 767) {           /* 获取的是鼠标动作的数据 */
                    if (mouse_decode(&mdec, i - 512) != 0) {     //获得鼠标数据放在MEDC中
                        sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);//把鼠标数据的X,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';
                        }
                        //显示字符串S
                        putfonts8_asc_sht(sht_back, 32, 16, COL8_FFFFFF, COL8_008484, s, 15);
                        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);//把新的鼠标坐标的位置输出
                        putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);
                        sheet_slide(sht_mouse, mx, my);  //接着调用图层移动函数,把窗口移动到鼠标点击的位置
                    }
                } else if (i == 10) {             /* 10秒定时器超时,在背景图层显示"10[sec]" */
                    putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
                } else if (i == 3) {              /* 3秒定时器超时,在背景图层显示"3[sec]" */
                    putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
                } else if (i <= 1) {             /* 光标定时器超时 */
                    if (i != 0) {
                        timer_init(timer3, &fifo, 0);   /* 初始化光标定时器,设置光标颜色白色 */
                        cursor_c = COL8_000000;
                    } else {
                        timer_init(timer3, &fifo, 1);   /* 初始化光标定时器,设置光标颜色黑色 */
                        cursor_c = COL8_FFFFFF;
                    }
                    timer_settime(timer3, 50);       //接着设定光标定时器的时间50次计数
                    boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
                    sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);//刷新。count每计数50光标闪烁一次。
                }
            }
        }

    harib11i:
      这里我们使用鼠标来完成窗口的移动吧!还记得我们前面创建的图层移动函数sheet_slide()吗?我们获取鼠标点击位置的坐标,然后把坐标作为参数给函数sheet_slide()来实现窗口图层的移动。好了,我们来看看代码吧!

    //HariMain节选            
    if (mouse_decode(&mdec, i - 512) != 0) {//把鼠标动作的三个字节的数据写到变量mdec中
        //..............
        if ((mdec.btn & 0x01) != 0) {    //如果按下了鼠标的左键
            /* 调用图层移动函数,把窗口图层移动到鼠标的位置 */
            sheet_slide(sht_win, mx - 80, my - 8);
        }
    }
  • 相关阅读:
    【leetcode】Triangle (#120)
    【leetcode】Unique Binary Search Trees (#96)
    【leetcode】Maximum Subarray (53)
    【转】算法杂货铺——k均值聚类(K-means)
    EM算法总结
    人工智能聚类算法总结
    K-MEANS算法总结
    在线制作css动画——CSS animate
    IOS 固定定位底部input输入框,获取焦点时弹出的输入法键盘挡住input
    响应式、手机端、自适应 百分比实现div等宽等高的方法
  • 原文地址:https://www.cnblogs.com/pengfeiz/p/5812851.html
Copyright © 2020-2023  润新知