第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的规格来制造显卡。
- 刚开始的时候,电脑的规格是以IBM公司为中心规定的,他们也规定了画面模式的相关规格。而且各家显卡公司也都迎合IBM的规格来制造显卡。
- 给AX赋值
-
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,那么所指定的画面模式不能使用。
- 其中VBEMODE的定义:
-
画面模式的重要信息:
; 画面模式信息的确认 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结束。
- 定义静态字符数组keytable。当按下字符A时,我们会从FIFO缓冲区获得一个数据
-
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了。加油,再接再厉!