Day14

今天继续补全了昨天的性能测试,还有提高分辨率,把键盘、鼠标传来的中断信息转换为我们可以直接理解的信息。

一些感想

很细致的对比实验

昨天作者测试性能的时候,因为担心初始化带来的影响,去掉了初始化。今天作者因为担心JMP到不同地址带来的影响,加了一个函数的空操作。不得不说作者知识面很广,而且这种问题也可以考虑到。。

提高分辨率的方法

我们在asmhead.nas的显卡设置那一部分,可以调整VBE的参数设置,于是就可以调用不同分辨率的显卡。
对于较高分辨率的显卡,BX = 模式设定 + 0x4000,AX = 0x4f02。这样才能正确设定显卡。
0x101·······640*480*8bit彩色
0x103·······800*600*8bit彩色
0x105·······1024*768*8bit彩色
0x107·······1280*1024*8bit彩色(对于QEMU来说不可用)
当然,不是所有显卡都能提升成这样的分辨率,所以需要先通过测试代码,来看到底支不支持。
另外,书中提到了画面模式信息的获取方法:
mode

键盘输入

查文档、查表可以得到键盘对应的中断数值,具体与哪个键对应(当然也可以自己一个键一个键按。。)。那么我们也可以因此来做一个键盘中断信息与字符对应的表。
不过目前的程序还不能对shift、ctrl等组合键作出判断。

鼠标输入

将对应的鼠标信息解码,配合上一些逻辑操作,就可以比如——移动窗口了。

进入正文

继续测试性能

harib11a
harib11b
harib11c

作者把昨天的数组、链表、链表+哨兵三个版本做了一下性能测试,同时设定了大量的计时器,来比较性能到底有没有提升。
结果发现,链表+哨兵会有略微的提升,如果追求极致的话这样也算挺不错了。不过这个提升甚至没有JMP地址带来的影响大。。

提高分辨率

harib11d

回到了汇编,改写画面模式设定

; 画面模式设定

        MOV        BX,0x4105 ; VBE的1024*768*8bit彩色
        MOV        AX,0x4f02
        INT        0x10
        MOV        BYTE [VMODE],8    ; 记下画面模式(参考C语言)
        MOV        WORD [SCRNX],1024
        MOV        WORD [SCRNY],768
        MOV        DWORD [VRAM],0xe0000000

0x101 0x103 0x105 随便选。。BX和AX里的数,是赋值规定要求这么做的。

提高分辨率(2)

harib11e

这一节主要是为了检测兼容性:是否支持VBE标准,以及是否支持高分辨率的显卡。

; 确认VBE是否存在
        MOV        AX,0x9000
        MOV        ES,AX
        MOV        DI,0
        MOV        AX,0x4f00
        INT        0x10
        CMP        AX,0x004f
        JNE        scrn320

前面这5条指令下来,如果有VBE,则AX就会变为0x004f。没有的话就只能使用scrn320了

; 检查VBE的版本
        MOV        AX,[ES:DI+4]
        CMP        AX,0x0200
        JB        scrn320            ; if (AX < 0x0200) goto scrn320

VBE的版本如果不是2.0以上,仍然不能使用高分辨率

; 取得画面模式信息

        MOV        CX,VBEMODE
        MOV        AX,0x4f01
        INT        0x10
        CMP        AX,0x004f
        JNE        scrn320

VBEMODE是我们预先制定好的想要设置到的模式。如果AX不等于0x004f,就意味着该模式不可用

; 画面模式信息的确认

        CMP        BYTE [ES:DI+0x19],8
        JNE        scrn320
        CMP        BYTE [ES:DI+0x1b],4
        JNE        scrn320
        MOV        AX,[ES:DI+0x00]
        AND        AX,0x0080
        JZ        scrn320            ; 模式属性的bit7是0,所以放弃

进行一系列确认之后,调成高分辨率:

; 画面模式的切换
        MOV        BX,VBEMODE+0x4000
        MOV        AX,0x4f02
        INT        0x10
        MOV        BYTE [VMODE],8    ; 记下画面模式(参考C语言)
        MOV        AX,[ES:DI+0x12]
        MOV        [SCRNX],AX
        MOV        AX,[ES:DI+0x14]
        MOV        [SCRNY],AX
        MOV        EAX,[ES:DI+0x28]
        MOV        [VRAM],EAX
        JMP        keystatus

如果失败了,就只能JMP scrn320,使用老的设定。

键盘输入(1)

harib11f

先尝试对一个字符作出正确的响应:

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)
    {
        putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, "A", 1);
    }
}

这样应该没什么问题。很容易显示。

键盘输入(2)

harib11g

建立一张对应的表(数组):

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) // 键盘数据
{
    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);
        }
    }
}

追记内容(1)

harib11h

加一个可以输入信息的窗口。
主函数里:

make_textbox8(sht_win, 8, 28, 144, 16, COL8_FFFFFF);
cursor_x = 8;
cursor_c = COL8_FFFFFF;

键盘FIFO接收部分:

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) // 一般字符
        {
            // 显示1个字符,就向前移动1个光标
            s[0] = keytable[i - 256];
            s[1] = 0;
            putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, s, 1);
            cursor_x += 8;
        }
    }
    if(i == 256 + 0x0e && cursor_x > 8) // 退格键
    {
        // 用空格键把光标消去后,后移1次光标
        putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, " ", 1);
        cursor_x -= 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);
}

新增函数,描绘文字的输入背景(绘制那个窗口的意思)

void make_textbox8(struct SHEET *sht, int x0, int y0, int sx, int sy, int c)
{
    int x1 = x0 + sx, y1 = y0 + sy;
    boxfill8(sht->buf, sht->bxsize, COL8_848484, x0 - 2, y0 - 3, x1 + 1, y0 - 3);
    boxfill8(sht->buf, sht->bxsize, COL8_848484, x0 - 3, y0 - 3, x0 - 3, y1 + 1);
    boxfill8(sht->buf, sht->bxsize, COL8_FFFFFF, x0 - 3, y1 + 2, x1 + 1, y1 + 2);
    boxfill8(sht->buf, sht->bxsize, COL8_FFFFFF, x1 + 2, y0 - 3, x1 + 2, y1 + 2);
    boxfill8(sht->buf, sht->bxsize, COL8_000000, x0 - 1, y0 - 2, x1 + 0, y0 - 2);
    boxfill8(sht->buf, sht->bxsize, COL8_000000, x0 - 2, y0 - 2, x0 - 2, y1 + 0);
    boxfill8(sht->buf, sht->bxsize, COL8_C6C6C6, x0 - 2, y1 + 1, x1 + 0, y1 + 1);
    boxfill8(sht->buf, sht->bxsize, COL8_C6C6C6, x1 + 1, y0 - 2, x1 + 1, y1 + 1);
    boxfill8(sht->buf, sht->bxsize, c,           x0 - 1, y0 - 1, x1 + 0, y1 + 0);
    return;
}

追记内容(2)

harib11i

鼠标点哪里,中间的窗口就会去哪里:
鼠标FIFO接收部分:

if((mdec.btn & 0x01) != 0)
{
    // 按下左键、移动sht_win
    sheet_slide(sht_win, mx - 80, my - 8);
}