第14天 高分辨率及键盘输入

第14天 高分辨率及键盘输入

2020.4.16

1. 继续测试性能(harib11a~harib11c)

  • 设定多个定时器(490+个),测试在下面三种情况中count的值:

    • harib10g(有移位)
    • harib10h(没有移位,没有哨兵)
    • harib10i(没有移位,有哨兵)
  • 先编写函数set490(bootpack.c):

    void set490(struct FIFO32 *fifo, int mode)
    {
        int i;
        struct TIMER *timer;
        if (mode != 0) {
            for (i = 0; i < 490; i++) {
                timer = timer_alloc();
                timer_init(timer, fifo, 1024 + i);
                timer_settime(timer, 100 * 60 * 60 * 24 * 50 + i * 100);
            }
        }
        return;
    }
    
    • 设定启动50天后才超时的定时器490个,使得50天后,每隔一秒就有一个定时器超时。
  • 在HariMain设定timer~timer3之前,加入代码set490(&fifo, 1)就可以设定490个定时器了。

  • 测试:

  • 分析:


    有时候,追加指令也会让JMP的执行速度加快。此处不再拓展。

  • 如此细微的改进值得么?

2. 提高分辨率(1)(harib11d)

  • 高分辨率的利用方法因为显卡不同而不同。 为了通过使用make run运行,我们只考虑之处QEMU的显卡。这个显卡顺利运行后,我们再考虑其他的显卡。

  • 改变画面模式。需要修改asmhead.nas:

    ; 设定画面模式
            MOV		BX,0x4101		; VBE的640x480x8bit彩色
            MOV		AX,0x4f02
            INT		0x10
            MOV		BYTE [VMODE],8	; 记下画面模式
            MOV		WORD [SCRNX],640
            MOV		WORD [SCRNY],480
            MOV		DWORD [VRAM],0xe0000000
    
    • 给AX赋值0x4f02,给BX赋值画面模式号码。就能切换到高分辨率模式了。
    • 以前320*200的时候,用的是:AH=0;AL=画面模式号码。
    • 画面模式有新旧之分。关于显卡的故事:
      • 刚开始的时候,电脑的规格是以IBM公司为中心规定的,他们也规定了画面模式的相关规格。而且各家显卡公司也都迎合IBM的规格来制造显卡。
  • VBE的画面模式号码:

3. 提高分辨率(2)(harib11e)

  • 倘若在真机上运行,我们不确定真机上显卡的型号,因此也就不知道生产这张显卡的公司是否与VESA合作,如果没有合作,我们便不能使用VBE。因此,我们需要先确认VBE是否存在。

  • 修改asmhead.nas:

    ; 确认VBE是否存在
            MOV		AX,0x9000
            MOV		ES,AX
            MOV		DI,0
            MOV		AX,0x4f00
            INT		0x10
            CMP		AX,0x004f
            JNE		scrn320
    
    • 给ES(附加段寄存器ES:存放当前执行程序中一个辅助数据段的段地址)赋值0x9000,给DI(SI是源变址寄存器,DI是目的变址寄存器)赋值0,给AX赋值0x4f00。
    • 然后执行INT 0x10如果有VBE,AX就会变成0x004f。如果没有变成0x004f,那么就只能使用320*200的画面
    • 对ES和DI进行赋值,是因为此显卡能够利用的VBE信息要写入内存中以ES:DI开始的512字节中,赋值是为了指定写入地址。
  • 需要保证VBE的版本在2.0及以上,否则就不能使用高分辨率。所以需要先调查一下VBE的版本号。

    ; 检查VBE的版本号   
    	MOV		AX,[ES:DI+4]
    	CMP		AX,0x0200
    	JB		scrn320			; if (AX < 0x0200) goto scrn320
    
    • 即使VBE版本号是2.0及以上,仍然不能保证所有的画面模式都能使用。
    • 这次的画面模式时0x105,即1024*768*8bit彩色。
    • 因此,还需要调查0x105这种画面模式能不能使用。
  • 调查0x105画面模式能否使用:

    ; 获取画面模式信息
            MOV		CX,VBEMODE
            MOV		AX,0x4f01
            INT		0x10
            CMP		AX,0x004f
            JNE		scrn320
    
    • 其中VBEMODE的定义:
      VBEMODE	EQU		0x105			; 1024 x  768 x 8bit彩色
      ; 修改0x105以切换画面模式
      ;	0x100 :  640 x  400 x 8bit彩色
      ;	0x101 :  640 x  480 x 8bit彩色
      ;	0x103 :  800 x  600 x 8bit彩色
      ;	0x105 : 1024 x  768 x 8bit彩色
      ;	0x107 : 1280 x 1024 x 8bit彩色
      
    • 存疑7:为什么MOV AX,0x4f01?
    • 再次对AX值进行确认,如果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			; 模式属性的第7位是0,所以放弃
    
    • 如果能够执行到这里。证明可以使用指定的VBE画面模式了。进行画面模式切换!
  • 进行画面模式切换

    ; 画面模式的切换
            MOV		BX,VBEMODE+0x4000
            MOV		AX,0x4f02
            INT		0x10
            MOV		BYTE [VMODE],8	; 记下画面模式
            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
    
    • 标签keystatus紧跟在标签scrn320代码的后面,为了跳过执行scrn320的代码。
  • 当VBE不存在或版本不够或模式有问题的情况下,只能使用320*200的画面模式了。

    scrn320:
            MOV		AL,0x13			; VGA图,320*200*8bit彩色
            MOV		AH,0x00
            INT		0x10
            MOV		BYTE [VMODE],8	; 
            MOV		WORD [SCRNX],320
            MOV		WORD [SCRNY],200
            MOV		DWORD [VRAM],0x000a0000  /*指定VRAM地址*/  
    
  • make run:

    1024*768的画面就是大!

4. 键盘输入(1)(harib11f)

  • 键盘按下和弹起都会产生一字节的数据。

  • 键盘按键按下时的数值表:

    • 按下产生的数值加上0x80就是弹起时产生的数值。
    • 保留的含义是:现在按下哪个按键都不会出现该数值。但将来可能会被分配。
  • 实现按下键盘A,显示字母A

    • 我们再次使用io_stihlt(),不再使用io_sti(),不再让CPU全力计数,而是让CPU休息。
    • 修改HariMain:
    for (;;) {
    	io_cli();
    	if (fifo32_status(&fifo) == 0) {
    		io_stihlt(); /*不再全力计数,而是休息*/
    	} 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 == 0x1e + 256) { /*按下A产生的数据是0x1e*/
    				putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, "A", 1);
    			}
    		} else if (512 <= i && i <= 767) { /* 鼠标数据 */
    			……
    		} else if (i == 10) { /*10s定时器*/
    			……
    		} else if (i == 3) { /*3s定时器*/
                ……
    		} else if (i == 1) { /*1s定时器*/
                ……
    		} else if (i == 0) { /*1s定时器*/
                ……
    		}
    	}
    }
    
    • 将窗口图层的名称由counter改为window
  • make run并按下键盘A:

    字符A显示出来了。

5. 键盘输入(2)(harib11g)

  • 显然,倘若每一个字符都跟harib11f中显示字符A一样去显示,代码会过于冗长和重复。

  • 修改HariMain:

    void HariMain(void)
    {
        ……
        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', '.'
        };
        ……
        for (;;) {
            io_cli();
            if (fifo32_status(&fifo) == 0) {
                io_stihlt();
            } 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 < 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);
                        }
                    }
                } else 
                ……
            }
        }
    }
    
    • 定义静态字符数组keytable。当按下字符A时,我们会从FIFO缓冲区获得一个数据0x1e + 256。 规定,keytable的下标就是从缓冲区获得的数值减去256,也就是说:keytable[0x1e] = ‘A’
    • 按照上述规则,将常用的26个字母,以及常用的符号数字都列举到keytable中(keytable空间大小为0x54)。keytable中无法显示的字符0填充。
    • s是字符串,以0结束。
  • make然后在VMware上运行:

    • 按下字母和数字和大部分符号都能正常显示。
    • 由于键盘上的@等字符的输入是需要按住shift的,因此无法正常显示。这一点以后需要修改。

6. 追记内容(1)(harib11h)

  • 既然能显示字符了,那么,制作一个简单的文本输入框。

  • 修改HariMain:

    void HariMain(void)
    {
        ……
        int mx, my, i, cursor_x, cursor_c; 
        ……
        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', '.'
        };
    
        ……
        make_window8(buf_win, 160, 52, "window");
        make_textbox8(sht_win, 8, 28, 144, 16, COL8_FFFFFF);
        cursor_x = 8;
        cursor_c = COL8_FFFFFF;
        ……
        for (;;) {
            io_cli();
            if (fifo32_status(&fifo) == 0) {
                io_stihlt();
            } 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) { /* 一般字符,可显示最多18个字符 */
                            /* 显示一个字符就移动一次光标 */
                            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) { /* 退格键 */
                        /* 用空字符代替光标,并移动光标 */
                        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);
                } else if (512 <= i && i <= 767) { /* 鼠标数据 */
                    ……
                } else if (i == 10) { /* 10s计时器 */
                    ……
                } else if (i == 3) { /* 3s计时器 */
                    ……
                } 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);
                    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);
                }
            }
        }
    }
    
    • cursor_x用于记住光标位置,输入一个字符cursor_x自加8,删除一个字符cursor_x自减8。(8<=cursor_x<=144。即最多输入18个字符,最少0个)
    • cursor_c是表示光标的颜色,使用计时器,让它每0.5s变化一次。
    • make_text0x8函数用来描绘文字输入背景。
      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;
      }
      
  • make后使用VMware运行:

7. 追记内容(2)(harib11i)

  • 现在,使用鼠标完成窗口移动。

  • 修改HariMain:

    for (;;) {
    	io_cli();
    	if (fifo32_status(&fifo) == 0) {
    		io_stihlt();
    	} else {
    		i = fifo32_get(&fifo);
    		io_sti();
    		if (256 <= i && i <= 511) { /* 键盘数据 */
    			……
    		} else if (512 <= i && i <= 767) { /* 鼠标数据 */
    			if (mouse_decode(&mdec, i - 512) != 0) {
    				……
    				sheet_slide(sht_mouse, mx, my);
    				if ((mdec.btn & 0x01) != 0) {
    					/* 按下左键、移动sht_win */
    					sheet_slide(sht_win, mx - 80, my - 8);
    				}
    			}
    		} else if (i == 10) { /* 10s定时器 */
    			……
    		} else if (i == 3) { /* 3s定时器 */
    			……
    		} else if (i <= 1) { /* 光标定时器 */
    			……
    		}
    	}
    }
    
    • 就添加了四行代码就完成了。这主要是以前的工作做得充分的功劳。
  • make以后用VMware运行:

    • 即使窗口图层跑到屏幕外也没问题。这多亏了在制作图层时下的苦功。

8. 再接再厉

  • 今天中午下了一场雷雨。听雨,其实是一件很美妙的事情。听雨落在屋顶上,听雨落在楼梯上蓝莓的叶片上,听雨落在地面上的泥土里,听雨落在小河、落在行人的紫色雨伞上……听,闭上眼睛听,那颗雨滴在落地的时候告诉了我,它和天上那朵穿着黑丝的云的暧昧邂逅。
  • 现在是2020.4.16 22:06。Github突然崩掉了,无法上传图片,因此,这篇文档留着明天再发布吧。
  • 已经结束两个周了,看看haribote.sys已经24KB了。加油,再接再厉!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值