本文主要是介绍【读书笔记-《30天自制操作系统》-13】Day14,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
相比前几篇的内容,本篇不仅内容更为简单,而且与显示相关,更为有趣。首先通过调用VBE的显示模式提高显示画面的分辨率,然后分别实现按下键盘按键显示对应的字符,以及通过鼠标移动窗口。因为是以前面讲过的很多内容为基础,程序代码很简单,而且能切实看到成果,也更有趣。
1. 提高画面分辨率
现在要把显示画面的分辨率提高到640x480,就又要修改BIOS的画面模式设定所用的汇编语言代码了。
; 设定画面模式MOV BX,0x4101 ; VBE的640x480x8bit彩色MOV AX,0x4f02INT 0x10MOV BYTE [VMODE],8 ; 记录下画面模式MOV WORD [SCRNX],640MOV WORD [SCRNY],480MOV DWORD [VRAM],0xe0000000
记得之前切换画面模式用的汇编语言指令是“AH = 0, AL = 画面模式号码”,但是上面这段代码却有所不同,用的是AX = 0x4f02。这是什么原因呢?
从前电脑的规格是以IBM公司为中心决定的,IBM规定了画面显示模式的规格,各家显卡公司也按照这一规格来进行实现。而后来各个显卡公司开发出了更多性能更好的显卡,具有各种画面显示模式,设置和使用的方法也各不相同,导致程序员比较无所适从。于是多家显卡公司进行协商,成立了VESA协会。这个协会制定了几乎可以通用的设置方法,制作了专门的BIOS,这个追加的BIOS被称为VESA BIOS extension,简称为VBE。利用它就可以扩展显卡的显示模式了。
VBE的画面模式号码如下:
- 0x101: 640x480x8bit 彩色
- 0x103: 800x600x8bit 彩色
- 0x105: 1024x768x8bit 彩色
- 0x107: 1280x1024x8bit 彩色
为了在真机上使用VBE的显示模式,我们还需要进行一些确认。
(1) 确认是否支持VBE?
有些公司的产品仍然不支持VBE,因此无法使用VBE的显示模式。我们通过如下操作来进行确认:
; 确认VBE是否存在MOV AX,0x9000MOV ES,AXMOV DI,0MOV AX,0x4f00INT 0x10CMP AX,0x004fJNE scrn320
这里给ES赋值0x9000,给DI赋值为0,给AX赋值为0x4f00,再执行INT 0x10,如果有VBE的话,AX就会变成0x004f。否则说明没有VBE,仍然只能使用之前320x200的画面。而为ES和DI进行赋值,是因为此显卡能利用的VBE信息将要写入内存中以ES:DI开始的512字节中。
(2) 确认VBE的版本是否在2.0以上
使用高分辨率,也需要VBE的版本在2.0以上。
; VBE版本确认MOV AX,[ES:DI+4]CMP AX,0x0200JB scrn320 ; if (AX < 0x0200) goto scrn320
(3) 即使VBE版本在2.0以上,也还是需要确认画面模式0x105是否能够使用:
; 获取画面模式信息MOV CX,VBEMODEMOV AX,0x4f01INT 0x10CMP AX,0x004fJNE scrn320
这里对AX的值进行了确认,如果是0x004f以外的值,所指定的画面模式就不能使用。
此次取得的画面模式信息也被写入内存从ES:DI开始的256字节中,这样刚才VBE的版本信息会被覆盖。但确认VBE版本之后这个信息就不需要了,因此没什么影响。
(4) 其他画面模式信息确认
这还没完,还有最后的几项信息需要确认:
- 颜色数是否为8
- 是否为调色板模式
- 画面模式号码是否可以加上0x4000再进行指定
; 画面模式信息确认CMP BYTE [ES:DI+0x19],8JNE scrn320CMP BYTE [ES:DI+0x1b],4JNE scrn320MOV AX,[ES:DI+0x00]AND AX,0x0080JZ scrn320 ; 模式属性的bit7是0,放弃
以上这些信息如果都确认OK,那么就可以使用VBE的画面模式了。如果确认不通过,则还是只能使用之前的分辨率。
; 画面模式切换MOV BX,VBEMODE+0x4000MOV AX,0x4f02INT 0x10MOV BYTE [VMODE],8 ; MOV AX,[ES:DI+0x12]MOV [SCRNX],AXMOV AX,[ES:DI+0x14]MOV [SCRNY],AXMOV EAX,[ES:DI+0x28]MOV [VRAM],EAXJMP keystatusscrn320:MOV AL,0x13 ; VGA图,320x200x8bit彩色MOV AH,0x00INT 0x10MOV BYTE [VMODE],8 ; MOV WORD [SCRNX],320MOV WORD [SCRNY],200MOV DWORD [VRAM],0x000a0000
调整分辨率后,画面明显变大了。
2. 键盘输入与鼠标移动窗口
前面已经讲过关于键盘和鼠标的内容了。但是键盘只是实现了按下按键显示按键码,而鼠标也只是实现了移动鼠标箭头,其实还没有什么实用的功能。本篇中来实现按下键盘按键显示对应的字符,以及通过鼠标移动窗口的功能。
2.1 键盘信息输入
前面已经讲过,按下一个键盘按键,会显示一个按键码,松开的时候也会显示一个按键码。利用这一点,我们来实现按键显示对应字符的功能。
比如按下按键A时,显示的按键码是1E,按下B的时候,显示的按键码是30,……,关于每个按键的按键码形成了一张对照表。根据表中的按键码数值,可以编写keytable对照表,根据按键码来显示按键的字符。
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);}}}
目前的程序还只能显示一个字符,再对程序进行修改,使其可以显示多个字符,并且修改了显示画面。
int mx, my, i, cursor_x, cursor_c;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) { /* 一般字符 *//* 显示一个字符就前移一次光标 */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) { /* backspace *//* 用空格键把光标消去后,后移一次光标 */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) { if (mouse_decode(&mdec, i - 512) != 0) {sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.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';}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) { putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);} else if (i == 3) { 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);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用来记住光标显示的位置,输入一个字符后,变量就递增8,cursor_c则表示光标的颜色,并且每0.5s变化一次,产生闪烁效果。显示效果如下:
不过我这里遇到一个问题,按下一个按键松开之后,输入框中会一直重复输入这个字符,把输入框占满,而此时再按backspace键也没有反应。暂时还没找到原因,先继续往下学,看看后续能否解决这个问题
键盘显示字符做完了,鼠标也修改一下,程序也很简单,只需要在主程序鼠标处理的部分增加几行代码:
if ((mdec.btn & 0x01) != 0) {/* 按下左键之后,就移动sht_win窗口 */sheet_slide(sht_win, mx - 80, my - 8);}
这样只要点击鼠标,窗口就会移动到鼠标点击的位置。
这一篇的内容可以说是非常简单了,而且实现效果立竿见影。不过从下一篇开始就要进入多任务的学习了,这部分内容是个难点,还是提前做好准备吧。
这篇关于【读书笔记-《30天自制操作系统》-13】Day14的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!