30天自制操作系统(第14天)

14天 高分辨率及键盘输入

14.1 提高分辨率(1

之前的分辨率为320*200*8彩色,VGA显卡。如果要使用新画面模式,就需要使用VBE画面模式。
切换到不使用 VBE 的画面模式时用 “AH = 0 AL= 画面模式号码; ”,而切换 到使用 VBE 的画面模式时用 “AX = 0x4f02 BX = 画面模式号码; ”。而这种必须使 用 VBE 才能利用的画面模式就称作 画面模式。
VBE 的画面模式号码如下:
0x101……640× 480× 8bit 彩色
0x103……800× 600× 8bit 彩色
0x105……1024× 768× 8bit 彩色
0x107……1280× 1024× 8bit 彩色
;画面设定 文件asmhead.nas
;修改前代码
;		MOV 	AL,0x13			;VGA显卡,320*200*8彩色
;		MOV		AH,0x00
;		INT		0x10
;		MOV		BYTE [VMODE],8	;记录画面模式
;		MOV		WORD [SCRNX],320;两个字节
;		MOV		WORD [SCRNY],200;两个字节
;		MOV		DWORD [VRAM],0x000a0000;VRAM是用来显示画面的内存,地址0xa0000~0xaffff的64KB

;修改后代码		
		MOV 	BX,0x4101		;VBE显卡,640x480*8彩色
		MOV		AX,0x4f02
		INT		0x10
		MOV		BYTE [VMODE],8	;记录画面模式
		MOV		WORD [SCRNX],640;两个字节
		MOV		WORD [SCRNY],480;两个字节
		MOV		DWORD [VRAM],0xe0000000;VRAM是用来显示画面的内存,地址0xa0000~0xaffff的64KB

14.2 提高分辨率(2

1° 首先确认VBE是否存在: VBE 的话, AX 就会变为0x004f;而且此显卡能利用的VBE信息要写入到内存中以ES:DI开始的512字节中。
2° 检查VBE版本: if (AX < 0x0200) goto scrn320
3° 取得画面模式信息:通过 VBE 来查看一下画面模式 0x105 能不能使用。如果AX是0x004f以外的值,就意味着所指定的画面模式不能使用。
4°  画面模式信息中,重要的信息有如下 6 个:
        WORD [ES : DI+0x00] :     模式属性 …… bit7 不是 1 就不好办 ( 能加上 0x4000 )
        WORD [ES : DI+0x12] :     X 的分辨率
        WORD [ES : DI+0x14] :     Y 的分辨率
        BYTE [ES : DI+0x19] :       颜色数 …… 必须为 8
        BYTE [ES : DI+0x1b] :       颜色的指定方法 …… 必须为 4 ( 4 是调色板模式 )
        DWORD [ES : DI+0x28] :   VRAM 的地址
5° 画面模式信息的确认:1)颜色数是否为8;2)是否为调色板模式;3)画面模式号码可否加上0x4000再进行指定。
6°  画面模式的切换
VBEMODE EQU		0x105			;1024 x  768 x 8bit
; 画面模式号码如下
;	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

BOTPAK	EQU		0x00280000		
DSKCAC	EQU		0x00100000		
DSKCAC0	EQU		0x00008000		

;有关BOOT_INFO
CYLS	EQU		0x0ff0			;设定启动区
LEDS	EQU		0x0ff1
VMODE	EQU		0x0ff2			;关于色彩数目的信息,颜色位数
SCRNX	EQU		0x0ff4			;分辨率的x
SCRNY	EQU		0x0ff6			;分辨率的y
VRAM	EQU		0x0ff8			;图像缓冲区的开始地址

		ORG 	0xc200			;程序要装载到的位置,书本P79页已说明0xc200的由来
		
; 确认VBE是否存在
		MOV 	AX,0x9000
		MOV 	ES,AX
		MOV 	DI,0
		MOV 	AX,0x4f00
		INT 	0x10
		CMP 	AX,0x004f
		JNE 	scrn320
		
; 检查VBE的版本
		MOV 	AX,[ES:DI+4]
		CMP 	AX,0x0200
		JB 		scrn320 		; if (AX < 0x0200) goto scrn320
		
; 取得画面模式信息
		MOV		CX,VBEMODE
		MOV		AX,0x4f01
		INT		0x10
		CMP		AX,0x004f
		JNE		scrn320
		
; 画面模式信息的确认
		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

scrn320:
		MOV 	AL,0x13			;VGA显卡,320*200*8彩色
		MOV		AH,0x00
		INT		0x10
		MOV		BYTE [VMODE],8	;记录画面模式
		MOV		WORD [SCRNX],320;两个字节
		MOV		WORD [SCRNY],200;两个字节
		MOV		DWORD [VRAM],0x000a0000;VRAM是用来显示画面的内存,地址0xa0000~0xaffff的64KB
		
		
;用BIOS取得键盘上各种LED指示灯的状态
keystatus:
		MOV		AH,0x02
		INT		0x16			;keyboard BIOS
		MOV		[LEDS],AL

14.3 键盘输入(1

在窗口中打印通过键盘输入的字符‘A’。
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) {
                putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, "A", 1);
            }
        } else if (512 <= i && i <= 767) { /* 鼠标数据 */
            (中略)
        } 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) { /* 光标用定时器 */
            (中略)
        } else if (i == 0) { /* 光标用定时器 */
            (中略)
        }
    }
}

14.4 键盘输入(2

创建字符串数组,如果通过键盘按下对应字符,则在窗口中显示。
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;// 回车符,理解成'\n',表示字符串终止符
            putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, s, 1);
        }
    }
} else if (512 <= i && i <= 767) { /* 鼠标数据 */

14.5 追记内容(1)

通过14.4节中已经实现在窗口中显示一个键盘输入的字符,下面将在窗口中实现连续输入字符。
有以下要求:1.将输入的位置存在光标,2.实时输入,3.可以删除字符。
思路:首先在窗口中画一个白底的输入框,再判断键盘中断产生时按下的字符是否在字符串keytable中。如果在,进一步确认是否为退格符,如果是则用空字符进行覆盖,否则显示该字符。光标用计时器也需要更新,调用boxfill8和sheet_refresh函数进行刷新光标。
// 绘制白底文本输入框
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;
}


void HariMain(void)
{
	(中略)
	int 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_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 == 0x0e + 256 && 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);
			} else if (512 <= i && i <= 767) {/* 鼠标数据 */
				(中略)
				}
			} else if (i == 10) {/* 10s计时器 */
				(中略)
			} else if (i == 3) {/* 3s计时器 */
				(中略)
			} else if (i <= 1) {/* 光标用计时器 */
				if( i == 1){
					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.6 追记内容(2

实现鼠标拖动窗口移动的功能,思路为当按下鼠标左键时,调用sheet_slide函数将sht_win的位置修改为(mx-80, my-8) 。
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) {
            /* 收集了3字节的数据,所以显示出来 */
            (中略)
            /* 光标移动 */
            (中略)
            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) { /* 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) { /* 光标用定时器 */
            (中略)
        }
    }
}

你可能感兴趣的:(其他)