12.键盘中断处理

简介

上一节我们实现了键盘中断事件调用显示所有可显示字符的字体图形显示,对中断处理机制有基本的认识。

目标

1.实现键盘扫描码字体图标显示。当键盘上的一个按键按下时,键盘会发送一个中断信号给CPU,与此同时,键盘会在指定端口(0x60) 输出一个数值,这个数值对应按键的扫描码(make code)叫通码,当按键弹起时,键盘又给端口输出一个数值,这个数值叫断码(break code).我们以按键按键’A’为例,当按键’A’按下时,键盘给端口0x60发出的扫描码是0X1E, 当按键’A’弹起时,键盘会给端口0x60发送断码0x9E。

断码 = 通码 + 0x80

键盘扫描码如下所示:
在这里插入图片描述

2.修改os.c文件如下:

// !compile method
// clang -m32 -c os.c -o os.o
// objconv -fnasm os.o -o os.s
//

#include "io.h"
#include "ascii_font.h"


//定义调色板颜色
#define  COL8_000000  0
#define  COL8_FF0000  1
#define  COL8_00FF00  2
#define  COL8_FFFF00  3
#define  COL8_0000FF  4
#define  COL8_FF00FF  5
#define  COL8_00FFFF  6
#define  COL8_FFFFFF  7
#define  COL8_C6C6C6  8
#define  COL8_840000  9
#define  COL8_008400  10
#define  COL8_848400  11
#define  COL8_000084  12
#define  COL8_840084  13
#define  COL8_008484  14
#define  COL8_848484  15


//屏幕宽度
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 200


void initPallet();



/**
 *绘制矩形
 *x             矩形左上角x坐标
 *y             矩形左上角y坐标
 *width         宽度
 *height        高度
 *colIndex      pallet_color 类型调色板颜色索引,即矩形颜色
 */

void fillRect(int x,int y,int width,int height,char colIndex);


//绘制桌面背景
void drawBackground();


/**
 *绘制字体
 *@param	addr		绘制的起始显存地址
 *@param 	x			绘制的x坐标
 *@param	y			绘制的y坐标
 *@param	col			绘制颜色
 *@param	pch			绘制的字符数组8*16,每一行共8位,共16行
 *@param	screenWidth	屏幕宽度
 */
void putChar(char *addr,int x,int y,char col,unsigned char *ch,int screenWidth);


/*
 *初始化鼠标指针
 *@param	vram		绘制的起始显存地址
 *@param	x			绘制鼠标指针最左上角x坐标
 *@param	y			绘制鼠标指针最左上角y坐标
 *@param	bc			绘制的矩形填充颜色,和背景色一样将能看到鼠标指针
 */
void init_mouse_cursor(char *vram,int x,int y,char bc);


int num = 0;


//操作系统C语言入口函数--可以指定为其他
void init_main() {

    initPallet();
	drawBackground();

	init_mouse_cursor((char *)0xa0000,100,100,COL8_008484);
	
    for(; ;){
        io_hlt();
    }
    
}

void initPallet(){
    //定义调色板
    static unsigned char table_rgb[16*3] = {
    
        0x00,  0x00,  0x00,		/*  0:黑色*/
        0xff,  0x00,  0x00,		/*  1:亮红*/
        0x00,  0xff,  0x00,		/*  2:亮绿*/
        0xff,  0xff,  0x00,		/*  3:亮黄*/
        0x00,  0x00,  0xff,		/*  4:亮蓝*/
        0xff,  0x00,  0xff,		/*  5:亮紫*/
        0x00,  0xff,  0xff,		/*  6:浅亮蓝*/
        0xff,  0xff,  0xff,		/*  7:白色*/
        0xc6,  0xc6,  0xc6,		/*  8:亮灰*/
        0x84,  0x00,  0x00,		/*  9:暗红*/
        0x00,  0x84,  0x00,		/* 10:暗绿*/
        0x84,  0x84,  0x00,		/* 11:暗黄*/
        0x00,  0x00,  0x84,		/* 12:暗青*/
        0x84,  0x00,  0x84,		/* 13:暗紫*/
        0x00,  0x84,  0x84,		/* 14:浅灰蓝*/
        0x84,  0x84,  0x84,		/* 15:暗灰*/
        
    };
    
	unsigned char *rgb = table_rgb;
	int flag = io_readFlag();
    io_cli();
    io_out8(0x03c8, 0);
	for(int i=0;i<16;i++){
		io_out8(0x03c9,rgb[0] / 4);
        io_out8(0x03c9,rgb[1] / 4);
        io_out8(0x03c9,rgb[2] / 4);
    	rgb += 3;
	}
	io_writeFlag(flag);
}


void fillRect(int x,int y,int width,int height,char colIndex){
    char *vram = (char *)0xa0000;
    for(int i=y;i<=y+height;i++){
        for(int j=x;j<=x+width;j++){
            vram[i*SCREEN_WIDTH+j] = colIndex;
        }
    }
}


void drawBackground(){
	fillRect(0,0,SCREEN_WIDTH-1,SCREEN_HEIGHT-29, COL8_008484);
    fillRect(0,SCREEN_HEIGHT-28,SCREEN_WIDTH-1,28, COL8_848484);


	fillRect(0,SCREEN_HEIGHT-27,SCREEN_WIDTH,1, COL8_848484);
	fillRect(0,SCREEN_HEIGHT-26,SCREEN_WIDTH,25, COL8_C6C6C6);
	
	fillRect(3,SCREEN_HEIGHT-24,56,1, COL8_FFFFFF);
	fillRect(2,SCREEN_HEIGHT-24,1,20, COL8_FFFFFF);

	fillRect(3,SCREEN_HEIGHT-4,56,1, COL8_848484);
	fillRect(59,SCREEN_HEIGHT-23,1,19, COL8_848484);

	fillRect(2,SCREEN_HEIGHT-3,57,0, COL8_000000);
	fillRect(60,SCREEN_HEIGHT-24,0,19, COL8_000000);

	fillRect(SCREEN_WIDTH-47,SCREEN_HEIGHT-24,43,1, COL8_848484);
	fillRect(SCREEN_WIDTH-47,SCREEN_HEIGHT-23,0,19, COL8_848484);

	fillRect(SCREEN_WIDTH-47,SCREEN_HEIGHT-3,43,0, COL8_FFFFFF);
	fillRect(SCREEN_WIDTH-3,SCREEN_HEIGHT-24,0,21, COL8_FFFFFF);
}


void putChar(char *addr,int x,int y,char col,unsigned char *pch,int screenWidth){
	
	for(int i=0;i<16;i++){
		char ch = pch[i];
		int off = (y+i)*screenWidth;
		
		//显示的字形最左边的是低地址,右侧的是高地址。例如:0x80,则高地址部分显示在内存的低地址,
		//最低位的应该偏移7
		if((ch & 0x01) != 0){
			addr[off+x+7] = col;
		}
		if((ch & 0x02) != 0){
			addr[off+x+6] = col;
		}
		if((ch & 0x04) != 0){
			addr[off+x+5] = col;
		}
		if((ch & 0x08) != 0){
			addr[off+x+4] = col;
		}	
		if((ch & 0x10) != 0){
			addr[off+x+3] = col;
		}
		if((ch & 0x20) != 0){
			addr[off+x+2] = col;
		}
		if((ch & 0x40) != 0){
			addr[off+x+1] = col;
		}	
		if((ch & 0x80) != 0){
			addr[off+x+0] = col;
		}
	}
}


void init_mouse_cursor(char *vram,int x,int y,char bc){
	//16*16 Mouse 
    //鼠标指针点阵
	static char cursor[16][16] = {
	 "*...............",
	 "**..............",
	 "*O*.............",
	 "*OO*............",
	 "*OOO*...........",
	 "*OOOO*..........",
	 "*OOOOO*.........",
	 "*OOOOOO*........",
	 "*OOOOOOO*.......",
	 "*OOOO*****......",
	 "*OO*O*..........",
	 "*O*.*O*.........",
	 "**..*O*.........",
	 "*....*O*........",
	 ".....*O*........",
	 "......*........."
	};
	
	for (int i = 0; i < 16; i++) {
		for (int j = 0; j < 16; j++) {
			int off = (i+y)*SCREEN_WIDTH+x+j;
			if (cursor[i][j] == '*') {
				vram[off] = COL8_000000;
			}
			if (cursor[i][j] == 'O') {
				vram[off] = COL8_FFFFFF;
			}
			if (cursor[i][j] == '.') {
				vram[off] = bc;
			}
		}
	}

}

/*
 *char 类型数据转换为16进制字符数据
 *@param	val		待转化为16进制的数值
 *@param	arr		保存16进制字符串数据的数组
 */
void char2HexStr(unsigned char val,char *arr) {
	unsigned char tmp = val >> 4;
	if(tmp>=10){
		arr[2] = 'a'+tmp-10;
	}
	else{
		arr[2] = '0'+tmp;
	}

	tmp = val & 0x0f;
	if(tmp>=10){
		arr[3] = 'a'+tmp-10;
	}
	else{
		arr[3] = '0'+tmp;
	}
}

/*
 *8259A中断调用
 *
 */
void int_8259A(char *index){
	//0x20是8259A控制端口
	//0x21对应的是键盘的中断向量。当键盘中断被CPU执行后,下次键盘再向CPU发送信号时,
	//CPU就不会接收,要想让CPU再次接收信号,必须向主PIC的端口再次发送键盘中断的中断向量号
	io_out8(0x20,0x21);

	//读取8259A  0x60端口键盘扫描码
	unsigned char data = io_in8(0x60);

	static char arr[4] = {'0','x'};
	
	char2HexStr(data,arr);

	unsigned char *ascii = ascii_array;
	
	for(int i=0;i<4;i++){
		int x = (num)%32*10;
		int y = (num)/32*20;

		putChar((char *)0xa0000,x,y,COL8_FFFFFF,ascii+(arr[i]-0x20)*16,SCREEN_WIDTH);
		
		num++;
	}
	
}

3.使用虚拟机加载运行floppy.img 文件如下:
在这里插入图片描述

按键盘A结果显示如下:
在这里插入图片描述

4.优化键盘中断服务子程序
中断实际上是将CPU当前正在执行的任务给打断,让CPU先处理中断任务,然后再返回处理原先的任务。处理流程和函数调用有相似之处,如下图所示:
在这里插入图片描述

我们要尽可能快的处理,然后把控制器交还给原来的任务。把键盘中断通码和断码数值缓存起来,然后把控制器交换给原来任务,等到CPU稍微空闲时再处理键盘事件。

修改os.c文件如下:

// !compile method
// clang -m32 -c os.c -o os.o
// objconv -fnasm os.o -o os.s
//

#include "io.h"
#include "ascii_font.h"


//定义调色板颜色
#define  COL8_000000  0
#define  COL8_FF0000  1
#define  COL8_00FF00  2
#define  COL8_FFFF00  3
#define  COL8_0000FF  4
#define  COL8_FF00FF  5
#define  COL8_00FFFF  6
#define  COL8_FFFFFF  7
#define  COL8_C6C6C6  8
#define  COL8_840000  9
#define  COL8_008400  10
#define  COL8_848400  11
#define  COL8_000084  12
#define  COL8_840084  13
#define  COL8_008484  14
#define  COL8_848484  15


//屏幕宽度
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 200


//定义键盘缓冲数据区
#define KEYBUF_LEN 32
struct _KeyBuf{
	//缓冲区数据长度
    unsigned char buf[KEYBUF_LEN];
	//下一数据读/写索引
    int next_r, next_w;
	//有效数据长度	
	int len;
}KeyBuf;

struct _KeyBuf keybuf = {{0},0,0,0};


void initPallet();



/**
 *绘制矩形
 *x             矩形左上角x坐标
 *y             矩形左上角y坐标
 *width         宽度
 *height        高度
 *colIndex      pallet_color 类型调色板颜色索引,即矩形颜色
 */

void fillRect(int x,int y,int width,int height,char colIndex);


//绘制桌面背景
void drawBackground();


/**
 *绘制字体
 *@param	addr		绘制的起始显存地址
 *@param 	x			绘制的x坐标
 *@param	y			绘制的y坐标
 *@param	col			绘制颜色
 *@param	pch			绘制的字符数组8*16,每一行共8位,共16行
 *@param	screenWidth	屏幕宽度
 */
void putChar(char *addr,int x,int y,char col,unsigned char *ch,int screenWidth);


/*
 *初始化鼠标指针
 *@param	vram		绘制的起始显存地址
 *@param	x			绘制鼠标指针最左上角x坐标
 *@param	y			绘制鼠标指针最左上角y坐标
 *@param	bc			绘制的矩形填充颜色,和背景色一样将能看到鼠标指针
 */
void init_mouse_cursor(char *vram,int x,int y,char bc);



/*
 *char 类型数据转换为16进制字符数据
 *@param	val		待转化为16进制的数值
 *@param	arr		保存16进制字符串数据的数组
 */
void char2HexStr(unsigned char val,char *arr);


int num = 0;


//操作系统C语言入口函数--可以指定为其他
void init_main() {

    initPallet();
	drawBackground();

	init_mouse_cursor((char *)0xa0000,100,100,COL8_008484);
	
    for(; ;){
		if(keybuf.len==0){
       		io_hlt();
		}
		else{
			io_cli();

			static char arr[4] = {'0','x'};
			unsigned char *ascii = ascii_array;
			for(int t=0;t<keybuf.len;t++){
				char2HexStr(keybuf.buf[t],arr);
				
				for(int i=0;i<4;i++){
					int x = (num)%32*10;
					int y = (num)/32*20;

					putChar((char *)0xa0000,x,y,COL8_FFFFFF,ascii+(arr[i]-0x20)*16,SCREEN_WIDTH);
					
					num++;
				}
			}
			keybuf.len = 0;
			keybuf.next_w = 0;

			io_sti();
		}
    }
    
}

void initPallet(){
    //定义调色板
    static unsigned char table_rgb[16*3] = {
    
        0x00,  0x00,  0x00,		/*  0:黑色*/
        0xff,  0x00,  0x00,		/*  1:亮红*/
        0x00,  0xff,  0x00,		/*  2:亮绿*/
        0xff,  0xff,  0x00,		/*  3:亮黄*/
        0x00,  0x00,  0xff,		/*  4:亮蓝*/
        0xff,  0x00,  0xff,		/*  5:亮紫*/
        0x00,  0xff,  0xff,		/*  6:浅亮蓝*/
        0xff,  0xff,  0xff,		/*  7:白色*/
        0xc6,  0xc6,  0xc6,		/*  8:亮灰*/
        0x84,  0x00,  0x00,		/*  9:暗红*/
        0x00,  0x84,  0x00,		/* 10:暗绿*/
        0x84,  0x84,  0x00,		/* 11:暗黄*/
        0x00,  0x00,  0x84,		/* 12:暗青*/
        0x84,  0x00,  0x84,		/* 13:暗紫*/
        0x00,  0x84,  0x84,		/* 14:浅灰蓝*/
        0x84,  0x84,  0x84,		/* 15:暗灰*/
        
    };
    
	unsigned char *rgb = table_rgb;
	int flag = io_readFlag();
    io_cli();
    io_out8(0x03c8, 0);
	for(int i=0;i<16;i++){
		io_out8(0x03c9,rgb[0] / 4);
        io_out8(0x03c9,rgb[1] / 4);
        io_out8(0x03c9,rgb[2] / 4);
    	rgb += 3;
	}
	io_writeFlag(flag);
}


void fillRect(int x,int y,int width,int height,char colIndex){
    char *vram = (char *)0xa0000;
    for(int i=y;i<=y+height;i++){
        for(int j=x;j<=x+width;j++){
            vram[i*SCREEN_WIDTH+j] = colIndex;
        }
    }
}


void drawBackground(){
	fillRect(0,0,SCREEN_WIDTH-1,SCREEN_HEIGHT-29, COL8_008484);
    fillRect(0,SCREEN_HEIGHT-28,SCREEN_WIDTH-1,28, COL8_848484);


	fillRect(0,SCREEN_HEIGHT-27,SCREEN_WIDTH,1, COL8_848484);
	fillRect(0,SCREEN_HEIGHT-26,SCREEN_WIDTH,25, COL8_C6C6C6);
	
	fillRect(3,SCREEN_HEIGHT-24,56,1, COL8_FFFFFF);
	fillRect(2,SCREEN_HEIGHT-24,1,20, COL8_FFFFFF);

	fillRect(3,SCREEN_HEIGHT-4,56,1, COL8_848484);
	fillRect(59,SCREEN_HEIGHT-23,1,19, COL8_848484);

	fillRect(2,SCREEN_HEIGHT-3,57,0, COL8_000000);
	fillRect(60,SCREEN_HEIGHT-24,0,19, COL8_000000);

	fillRect(SCREEN_WIDTH-47,SCREEN_HEIGHT-24,43,1, COL8_848484);
	fillRect(SCREEN_WIDTH-47,SCREEN_HEIGHT-23,0,19, COL8_848484);

	fillRect(SCREEN_WIDTH-47,SCREEN_HEIGHT-3,43,0, COL8_FFFFFF);
	fillRect(SCREEN_WIDTH-3,SCREEN_HEIGHT-24,0,21, COL8_FFFFFF);
}


void putChar(char *addr,int x,int y,char col,unsigned char *pch,int screenWidth){
	
	for(int i=0;i<16;i++){
		char ch = pch[i];
		int off = (y+i)*screenWidth;
		
		//显示的字形最左边的是低地址,右侧的是高地址。例如:0x80,则高地址部分显示在内存的低地址,
		//最低位的应该偏移7
		if((ch & 0x01) != 0){
			addr[off+x+7] = col;
		}
		if((ch & 0x02) != 0){
			addr[off+x+6] = col;
		}
		if((ch & 0x04) != 0){
			addr[off+x+5] = col;
		}
		if((ch & 0x08) != 0){
			addr[off+x+4] = col;
		}	
		if((ch & 0x10) != 0){
			addr[off+x+3] = col;
		}
		if((ch & 0x20) != 0){
			addr[off+x+2] = col;
		}
		if((ch & 0x40) != 0){
			addr[off+x+1] = col;
		}	
		if((ch & 0x80) != 0){
			addr[off+x+0] = col;
		}
	}
}


void init_mouse_cursor(char *vram,int x,int y,char bc){
	//16*16 Mouse 
    //鼠标指针点阵
	static char cursor[16][16] = {
	 "*...............",
	 "**..............",
	 "*O*.............",
	 "*OO*............",
	 "*OOO*...........",
	 "*OOOO*..........",
	 "*OOOOO*.........",
	 "*OOOOOO*........",
	 "*OOOOOOO*.......",
	 "*OOOO*****......",
	 "*OO*O*..........",
	 "*O*.*O*.........",
	 "**..*O*.........",
	 "*....*O*........",
	 ".....*O*........",
	 "......*........."
	};
	
	for (int i = 0; i < 16; i++) {
		for (int j = 0; j < 16; j++) {
			int off = (i+y)*SCREEN_WIDTH+x+j;
			if (cursor[i][j] == '*') {
				vram[off] = COL8_000000;
			}
			if (cursor[i][j] == 'O') {
				vram[off] = COL8_FFFFFF;
			}
			if (cursor[i][j] == '.') {
				vram[off] = bc;
			}
		}
	}

}


void char2HexStr(unsigned char val,char *arr) {
	unsigned char tmp = val >> 4;
	if(tmp>=10){
		arr[2] = 'a'+tmp-10;
	}
	else{
		arr[2] = '0'+tmp;
	}

	tmp = val & 0x0f;
	if(tmp>=10){
		arr[3] = 'a'+tmp-10;
	}
	else{
		arr[3] = '0'+tmp;
	}
}

/*
 *8259A中断调用
 *
 */
void int_8259A(char *index){
	//0x20是8259A控制端口
	//0x21对应的是键盘的中断向量。当键盘中断被CPU执行后,下次键盘再向CPU发送信号时,
	//CPU就不会接收,要想让CPU再次接收信号,必须向主PIC的端口再次发送键盘中断的中断向量号
	io_out8(0x20,0x21);

	//读取8259A  0x60端口键盘扫描码
	char data = io_in8(0x60);

	if(keybuf.len<KEYBUF_LEN){
		keybuf.buf[keybuf.next_w] = data;
		keybuf.len++;
		keybuf.next_w++;
	}
	else{
		keybuf.len = 0;
		keybuf.next_w = 0;
	}
}

编译加载floppy.img 运行点击键盘 abc 键显示如下:
在这里插入图片描述

至此,说明我们的键盘扫描码被存放到数据缓冲区中!

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值