动手写操作系统9----键盘&鼠标中断实现

键盘&鼠标中断实现

本节主要实现键盘中断和鼠标中断,键盘中断实现将键盘数据显示到屏幕;鼠标中断实现鼠标位置的移动。

                                             

 

键盘中断通过主8259A的IRQ1触发,鼠标中断通过从8259A的IRQ4触发

CPU通过中断向量号来寻址待执行的中断代码

中断向量号 = 起始向量号 + 中断请求号

起始向量号通过中断控制字ICW2被初始化  中断请求号即为8259A引脚编号

打开主IRQ1和从IRQ4中断响应引脚

    ;OCW(operation control word)
	;当OCW[i] = 1 时,屏蔽对应的IRQ(i)管线的信号
	;IRQ1对应的是键盘产生的中断
    mov al, 11111001b
    out 021h, al
    call io_delay

    ;CPU忽略所有来自从8259A芯片的信号
	;鼠标是通过从8259A的IRQ4管线向CPU发送信号
    mov al, 11101111b
    out 0A1h, al
    call io_delay

初始化中断控制字ICW2如下所示:

;向主8259A发送ICW2
	;20h 对应二进制00100000 
	;ICW2[0,1,2] = 0 
	;8259A根据被设置的起始向量号(起始向量号通过中断控制字ICW2被初始化)加上中断请求号计算出中断向量号
	;当主8259A对应的IRQ0管线向CPU发送信号时,CPU根据0x20这个值去查找要执行的代码,
	;,CPU根据0x21这个值去查找要执行的代码,依次类推。
    mov al, 020h
    out 021h, al
    call io_delay

	;向从8259A发送ICW2
	;28h 对应二进制00100100
	;8259A根据被设置的起始向量号(起始向量号通过中断控制字ICW2被初始化)加上中断请求号计算出中断向量号
	;当从8259A对应的IRQ0管线向CPU发送信号时,CPU根据0x28这个值去查找要执行的代码,
	;IRQ1管线向CPU发送信号时,CPU根据0x29这个值去查找要执行的代码,依次类推。
    mov al, 028h
    out 0A1h, al
    call io_delay

主控制器起始向量号为0x20 从控制器起始向量号为0x28  因此

键盘中断中断向量号为0x20+0x01=0x21

鼠标中断中断向量号为0x28+0x04=0x2c

因此中断描述符初始化如下:

;中断描述符表
LABLE_IDT:
    %rep 0x21
        Gate SelectorCode32, SpuriousHandler, 0, DA_386IGate
    %endrep
	
	;键盘中断向量(8259A 键盘中断向量0x20,IRQ1 是键盘中断请求,0x20 + IRQ[n] = 0x21
	.0x21:
		Gate SelectorCode32, KeyboardHandler, 0, DA_386IGate
		
	%rep 10
		Gate SelectorCode32, SpuriousHandler, 0, DA_386IGate
	%endrep
	
	;从中断控制器8259A 中断向量0x28,IRQ4 是鼠标中断请求,0x28 + IRQ[n] = 0x2c
	.0x2c:
		Gate SelectorCode32, MouseHandler, 0, DA_386IGate

相应的键盘和鼠标中断处理函数:

;键盘中断程序
LabelKeyboardHandler:
	KeyboardHandler EQU LabelKeyboardHandler - $$
	; 注意中断切换过程
	push es
	push ds
	pushad
	mov eax, esp
	push eax
	
	call int_keyboard
	
	pop eax
	mov esp, eax
	popad
	pop ds
	pop es
	iretd
	
;鼠标中断程序
LabelMouseHandler:
	MouseHandler EQU LabelMouseHandler - $$
	; 注意中断切换过程
	push es
	push ds
	pushad
	mov eax, esp
	push eax
	
	call int_mouse
	
	pop eax
	mov esp, eax
	popad
	pop ds
	pop es
	iretd

中断处理函数的处理过程

将当前寄存器值入栈

执行中断响应函数

将栈中寄存器值恢复,继续执行中断前指令

键盘中断响应函数

void int_keyboard(char *index){

        //0x20是8259A控制端口

        //0x21对应的是键盘的中断向量。当键盘中断被CPU执行后,下次键盘再向CPU发送信号时,

        //CPU就不会接收,要想让CPU再次接收信号,必须向主PIC的端口再次发送键盘中断的中断向量号

        io_out8(0x20, 0x21);

        unsigned char data = io_in8(PORT_KEYDATA);

        fifo8_put(&keybufInfo, data);

}

将键盘中断产生数据存入队列中,主函数不断检测队列中是否有数据,如果有数据,则将相应数据显示到屏幕中

    for(;;){
		if (keybufInfo.len > 0) {
			io_cli();
			static char keyval[4] = {'0','x'};
			for(int i=0; i<keybufInfo.len; i++){
				char data = fifo8_get(&keybufInfo);
				static int x = 0;
				char2HexStr(data, keyval);
				showString(keyval, x%SCREEN_WIDTH, x/SCREEN_WIDTH*20, COL8_FFFFFF);
				x += 32;
			}
			io_seti();
		} else if (mousebufInfo.len > 0) {
			io_cli();
			for(int t=0;t<mousebufInfo.len;t++){
				mouseCursorMoved(&mouseDes, COL8_008484);
			}
			io_seti();
		} else {
			io_hlt();
		}  
    }

鼠标电路初始化

内核加载完成,初始化中断描述符表,要想响应鼠标中断,需要首先实现鼠标电路的初始化

//初始化键盘控制电路,鼠标控制电路是连接在键盘控制电路上,通过键盘电路实现初始化
void init_mouse(){
	
	waitKBCReady();
	
	//0x60让键盘电路进入数据接受状态
	io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
	waitKBCReady();
	
	//数据0x47要求键盘电路启动鼠标模式,这样鼠标硬件所产生的数据信息,通过键盘电路端口0x60就可读到
	io_out8(PORT_KEYDATA, KBC_MODE);
	waitKBCReady();
	
	io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
	waitKBCReady();
	
	//0xf4数据激活鼠标电路,激活后将会给CPU发送中断信号
	io_out8(PORT_KEYDATA, MOUSECMD_ENABLE);
}

只有当端口0x64端口数据第二个比特为0时,鼠标电路才能接受来自内核命令。

//鼠标电路对应的一个端口是 0x64, 通过读取这个端口的数据来检测鼠标电路的状态,
//内核会从这个端口读入一个字节的数据,如果该字节的第二个比特位为0,那表明鼠标电路可以
//接受来自内核的命令,因此,在给鼠标电路发送数据前,内核需要反复从0x64端口读取数据,
//并检测读到数据的第二个比特位,直到该比特位为0时,才发送控制信息
void waitKBCReady(){
	for(;;){
		if((io_in8(PORT_KEYSTA)&0x02)==0){
			break;
		}
	}
}

鼠标中断响应函数

在鼠标中断响应函数中,将鼠标中断产生数据存入队列中

void int_mouse(char *index){

        //当中断处理后,要想再次接收中断信号,就必须向中断控制器发送一个字节的数据

        io_out8(0x20, 0x20);

        io_out8(0xa0, 0x20);

        //读取鼠标数据

        unsigned char data = io_in8(PORT_MOUSEDATA);

        fifo8_put(&mousebufInfo, data);

}

主循环不断检测鼠标队列,依次处理每个数据

        for(;;){
		if (keybufInfo.len > 0) {
			io_cli();
			static char keyval[4] = {'0','x'};
			for(int i=0; i<keybufInfo.len; i++){
				char data = fifo8_get(&keybufInfo);
				static int x = 0;
				char2HexStr(data, keyval);
				showString(keyval, x%SCREEN_WIDTH, x/SCREEN_WIDTH*20, COL8_FFFFFF);
				x += 32;
			}
			io_seti();
		} else if (mousebufInfo.len > 0) {
			io_cli();
			for(int t=0;t<mousebufInfo.len;t++){
				mouseCursorMoved(&mouseDes, COL8_008484);
			}
			io_seti();
		} else {
			io_hlt();
		}  
    }

读取队列中的数据,每三个为一组进行解析,每三个数据构建结构体MouseDes来表示当前鼠标的移动信息,然后擦除旧的鼠标信息,计算鼠标新的坐标,绘制新的鼠标。

void mouseCursorMoved(MouseDes *mdec, char bc){
	unsigned char data = fifo8_get(&mousebufInfo);
	if(mouse_decode(mdec, data) != 0){
		//擦除之前鼠标位置
		fillRect(mdec->x, mdec->y, 16, 16, bc);
		//计算鼠标新的坐标
		mdec->x += mdec->offX;
		mdec->y += mdec->offY;
		if(mdec->x < 0){
			mdec->x = 0;
		}
		if(mdec->x > SCREEN_WIDTH-16/2){
			mdec->x = SCREEN_WIDTH-16/2;
		}
		if(mdec->y < 0){
			mdec->y = 0;
		}
		if(mdec->y > SCREEN_HEIGHT-16){
			mdec->y = SCREEN_HEIGHT-16;
		}
		//绘制鼠标
		init_mouse_cursor((char *)VGA_ADDR, mdec->x, mdec->y, COL8_008484);
	}
}

鼠标移动模型以及处理

//鼠标处理需要连续处理3字节
//phase 表示处理字节阶段
//offX, offY 当前鼠标的偏移
//x,y 鼠标当前所在的坐标位置
typedef struct _MouseDes{
	char buf[3], phase;
	int offX, offY;
	int x, y, btn;
}MouseDes;

每三个数据即可给出鼠标中断的数据信息,主要包括鼠标按键类型(左 滑轮 右)

鼠标上下移动  左右移动数据。第一个字节0xab, a的数值必须在0-3这个范围内,由于a对应的是八比特中的高四位,所以这意味着该字节的第7,8两个比特位必须为0,b对应着八比特位中的低四位,它的值必须在8-F之间,这意味着该字节数据对应的第4个比特位必须为1.把第一个字节转换成二进制,那么它必须满足下面格式(X,*代表0或1):

0 0 X X 1 * \ * * 三个*用来表示鼠标按键,当鼠标的左键,滚轮,右键被按下时,对应的比特位会设置为1.第二个字节用来表示鼠标的左右移动,对该字节进行相应处理后,可以得到鼠标平移的坐标变换。第三个字节的数据表示鼠标的上下移动,对该字节进行相应处理后,可以得到鼠标垂直移动时的坐标数变化。

具体的处理过程如下所示:

int mouse_decode(MouseDes *mdec, unsigned char dat){
	int flag = -1;
	if(mdec->phase == 0){
		if(dat == 0xfa){
			mdec->phase = 1;
		}
		flag = 0;
	}
	else if(mdec->phase == 1){
		if((dat&0xc8) == 0x08){
			mdec->buf[0] = dat;
			mdec->phase = 2;
		}
		flag = 0;
	}
	else if(mdec->phase == 2){
		mdec->buf[1] = dat;
		mdec->phase = 3;
		flag = 0;
	}
	else if(mdec->phase == 3){
		mdec->buf[2] = dat;
		mdec->phase  =1;
		mdec->btn = mdec->buf[0]&0x07;
		mdec->offX = mdec->buf[1];
		mdec->offY = mdec->buf[2];
		if((mdec->buf[0]&0x10) != 0){
			mdec->offX |= 0xffffff00;
		}
		if((mdec->buf[0]&0x20) != 0){
			mdec->offY |= 0xffffff00;
		}
		mdec->offY = -mdec->offY;
		flag = 1;
	}
	return flag;
}

代码位置:https://github.com/ChenWenKaiVN/bb_os_core

参考链接:

https://www.jianshu.com/p/5f7244fb71c4

https://blog.csdn.net/Zllvincent/article/details/84099179

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值