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

本文档介绍了如何使用VESA BIOS Extension (VBE) 设置更高分辨率的显示模式,如640x480x8bit彩色,并通过检查VBE版本确保兼容性。同时,讲解了如何验证和选择合适的画面模式,以及如何处理键盘输入,包括将扫描码映射到相应的字符并显示。此外,还涉及了键盘输入的优化,如处理退格键和调整字符显示位置。最后提到了鼠标输入与窗口移动的相关处理。
摘要由CSDN通过智能技术生成

14.1、提高分辨率(1)

		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

这次调用int 0x10中断,BX=0x4101,AX=0x4f02,显存地址变成了0xe0000000(之前AH=0;AL=画面模式号码;显存地址是0x00a00000)。之前都是通过BIOS设置的画面,现在算是因为显卡的进步,重新设置的设置显示画面的方式。
VBE(VESA-BIOS extension)这是显卡的BIOS的标准。VESA协会为了规范当时显卡各种奇葩设定,专门制定了这个VBE,通过这个标准可以设定各家显卡的画面显示(当然也得是加入了VBE的显卡厂商)。设定方式就是
AX=0x4f02;BX=画面模式号码;

画面模式号码分辨率
0x101640 x 480 x 8bit 彩色
0x103800 x 600 x 8bit 彩色
0x1051024 x 768 x 8bit 彩色
0x1071280 x 1024 x 8bit 彩色

这些号码需要加上0x4000,再赋值到BX中

14.2、提高分辨率(2)

以上当确定主机有VESA合作的显卡时,确实可以使用VBE设置画面。但是如果没有的话将会出错,所以设置高分辨率时需要提前检查是不是有VBE,如果没有只能回去使用320 x 200了。

		MOV		AX,0x9000
		MOV		ES,AX
		MOV		DI,0
		MOV		AX,0x4f00
		INT		0x10
		CMP		AX,0x004f
		JNE		scrn320

当AX=0x4f00时发生int 0x10中断,如果存在VBE,AL=4f,AH=status(0x00 成功;0x01 失败),AX=0x004f。
显卡能够利用的VBE信息,将会写入到ES:DI指定的开始地址,共512字节。

在本操作系统中,如果VBE不是2.0版本,就没办法使用更高的分辨率,再来判断以下VBE版本:

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

接下来查看0x105画面模式能不能使用,因为即使是VBE2.0也不能保证所有画面模式都能使用。

VBEMODE	EQU		0x105

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

AX=0x4f01:获取 SuperVGA 模式信息
CX=VBE模式号码,如果能够设置,AX=4f,并把该画面模式信息写入到ES:DI开始的256字节地址中。这将覆盖之前写入的VBE的信息

画面模式信息,详细看AX=0x4f01连接:
模式信息
我们来验证以下三项:

  1. 颜色数是不是8位
  2. 是否为调色板模式
  3. 画面模式号码是否能加上0x4000再进行指定(bit7是1就可以)
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 ; 不行就回到320 x 200

如果以上都验证通过了,那便可以切换到高分辨率了。

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:
		MOV		AH,0x02
		INT		0x16 			; keyboard BIOS
		MOV		[LEDS],AL

切换后使用的仍是AX=0x4f01中断的ES:DI地址,查中断手册,如果设置高分辨率AX=0x4f02,VBE3.0+ 会使用ES:DI继续写入信息,覆盖之前写入的256字节信息,但作者就直接使用了之前的256字节内容,可以当时它们没有3.0以上版本吧。
1
2

结果
还是行?现在VBE不是3.0吗?不了解了。。。。。

14.3、键盘输入(1)

之前在读入键盘数据时读入的扫描码,可以对应按键,例如:1E ==> A,从缓冲区读取到1E,就输出A,这也是一个方法:

	for (;;) {
		io_cli();
		if (fifo32_status(&fifo) == 0) {
			io_stihlt();
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (256 <= i && i <= 511) { /* 之前缓冲区加了256 */
				sprintf(s, "%02X", i - 256);
				putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
				if (i == 0x1e + 256) {
					// 如果i == 0x1e + 256 就输出A
					putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, "A", 1);
				}

按照这个方法也可以打印所有字符了,那再改进以下,把键盘字符都放到数组里:

	// static char会被编译成DB指令
	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) { // i - 256就是扫描码,在keytable索引
						s[0] = keytable[i - 256];
						s[1] = 0;
						putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, s, 1);
					}
				}

这个存在问题,作者列出的扫描码和现在中国的键盘有不同的地方
1、没有响应组合键
2、‘[’会是‘@’
3、等等
解决办法:重新设计数组就好了

14.4、追记内容

14.4.1、编辑字符

看关键代码注释

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; // cursor_x用来标记显示字符的起始位置(左上角)
					}
				}
				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); // corsor_c光标颜色
				sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);
				
				} else if (512 <= i && i <= 767) { /* 鼠标数据*/
				if (mouse_decode(&mdec, i - 512) != 0) {
					/* 已经收集了3字节的数据,所以显示出来 */
					(略)
				}
			} else if (i == 10) { /* 10秒定时器 */
				putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
			} else if (i == 3) { /* 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); /* 下面设定0 */
					cursor_c = COL8_000000;
				} else {
					timer_init(timer3, &fifo, 1); /* 下面设定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);
			}
		}
	}

14.4.2、移动窗口

看关键代码注释

else if (512 <= i && i <= 767) { /* 鼠标数据*/
				if (mouse_decode(&mdec, i - 512) != 0) {
					/* 已经收集了3字节的数据,所以显示出来 */
					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);/* 包含sheet_refresh含sheet_refresh */
					if ((mdec.btn & 0x01) != 0) { /* 按下左键、移动sht_win */
						
						// 鼠标一按,sht_win窗口就移动过去,有点僵硬哈
						sheet_slide(sht_win, mx - 80, my - 8); // 窗口的左上角距离鼠标左上角少(80,8)
					}
				}
			}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值