13.实现鼠标中断处理

2023-05-16

简介

上节实现了对键盘中断服务子程序的处理和修改优化了中断程序,但只是简单的在中断服务子程序中记录断码或通码,缓冲区使用效率不高。

目标

实现鼠标中断处理、优化中断缓存。pc中8259A中断控制器连接模型如下:
在这里插入图片描述

1.鼠标发送中断信号的数据线在从8259A芯片的IRQ4信号线,为了接收鼠标中断信号,我们在初始化中断控制芯片时,必须启用该信号线,同时,从8259A芯片是通过主8259A的IRQ2信号线连接在一起的,所以,也必须同时启动主8259A芯片的IRQ2信号线。故init8259A:初始化中断控制器中如下代码修改如下:

mov  al, 11111001b	;CPU只接收主8259A, IRQ1管线发送的信号,其他管线发送信号一概忽略
out  0x21, al		;IRQ1对应的是键盘产生的中断
call io_delay

mov  al, 11101111b	;IRQ4 允许鼠标中断
out  0xa1, al		;鼠标是通过从8259A的IRQ4管线向CPU发送信号
call io_delay

2.鼠标电路对应的一个端口是 0x64, 通过读取这个端口的数据来检测鼠标电路的状态,内核会从这个端口读入一个字节的数据,如果该字节的第二个比特位为0,那表明鼠标电路可以接受来自内核的命令,因此,在给鼠标电路发送数据前,内核需要反复从0x64端口读取数据,并检测读到数据的第二个比特位,直到该比特位为0时,才发送控制信息。

3.修改kernel.s 如下:

   	;全局描述符结构 8字节
    ; byte7 byte6 byte5 byte4 byte3 byte2 byte1 byte0
    ; byte6低四位和 byte1 byte0 表示段偏移上限
    ; byte7 byte4 byte3 byte2 表示段基址
    
    ;定义全局描述符数据结构
    ;3 表示有3个参数分别用 %1、%2、%3引用参数
    ;%1:段基址     %2:段偏移上限  %3:段属性
    %macro GDescriptor  3
        dw %2 & 0xffff
        dw %1 & 0xffff
        db (%1>>16) & 0xff 
        dw ((%2>>8) & 0x0f00) | (%3 & 0xf0ff)
        db (%1>>24) & 0xff 
    %endmacro
    DA_32       EQU 4000h   ; 32 位段
    DA_C        EQU 98h ; 存在的只执行代码段属性值
    DA_DRW      EQU 92h ; 存在的可读写数据段属性值
    DA_DRWA     EQU 93h ; 存在的已访问可读写数据段类型值


	;中断描述符表
	;Gate selecotor, offset, DCount, Attr
	%macro Gate 4
		dw  (%2 & 0xffff)
		dw  %1
		dw  (%3 & 0x1f) | ((%4 << 8) & 0xff00)
		dw  ((%2>>16) & 0xffff)
	%endmacro
	DA_386IGate EQU 8Eh ; 中断调用门

   
    org 0x9000 
    jmp entry
    
    [SECTION .gdt]
    ;定义全局描述符                                段基址           段偏移上限       段属性
    LABEL_GDT:              GDescriptor         0,              0,             0
    LABEL_DESC_CODE:        GDescriptor         0,              SegCodeLen-1,  DA_C+DA_32 
    LABEL_DESC_VIDEO:       GDescriptor         0xb8000,        0xffff,        DA_DRW
    LABEL_DESC_STACK:       GDescriptor         0,              STACK_TOP-1,   DA_DRWA+DA_32
    LABEL_DESC_VRAM:        GDescriptor         0,              0xffffffff,    DA_DRW

    ;gdt 表大小
    GdtLen  equ     $-LABEL_GDT

    ;gdt表偏移上限和基地址
    GdtPtr  dw      GdtLen-1
            dd      0


    ;cpu开机进入实模式时使用的段寄存器 cs,ds,es,ss 和偏移地址组成内存地址,内存地址=段寄存器 * 16 + 偏移地址 
    ;保护模式中段寄存器保存的是gdt 描述表中各个描述符的偏移,也叫选择子
    

    SelectorCode32      EQU     LABEL_DESC_CODE-LABEL_GDT
    SelectorVideo       EQU     LABEL_DESC_VIDEO-LABEL_GDT
    SelectorStack       EQU     LABEL_DESC_STACK-LABEL_GDT
    SelectorVRAM        EQU     LABEL_DESC_VRAM-LABEL_GDT


	;中断描述符表
	LABEL_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


	IdtLen  equ $ - LABEL_IDT
	IdtPtr  dw  IdtLen - 1
		    dd  0

    [SECTION .s16]
    [BITS 16]
entry:
    mov ax,cs 
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov sp,0x100 

    ;设置屏幕色彩模式
    mov al,0x13
    mov ah,0
    int 0x10

    ;设置LABEL_DESC_CODE描述符段基址
    mov eax,0 
    mov ax,cs 
    shl eax,4
    add eax,SEG_CODE32
    mov word [LABEL_DESC_CODE+2],ax
    shr eax,16
    mov [LABEL_DESC_CODE+4],al
    mov [LABEL_DESC_CODE+7],ah


    ;设置栈空间
    xor eax,eax
    mov ax,cs 
    shl eax,4
    add eax,LABEL_STACK
    mov word [LABEL_DESC_STACK+2],ax
    shr eax,16
    mov byte [LABEL_DESC_STACK+4],al
    mov byte [LABEL_DESC_STACK+7],ah


    mov eax,0
    mov ax,ds
    shl eax,4 
    add eax,LABEL_GDT
    mov dword [GdtPtr+2],eax

    ;设置GDTR 寄存器
    lgdt [GdtPtr]


    cli     ;关闭可可屏蔽中断,如键盘中断

    in al,0x92 
    or al,0x02
    out 0x92,al 

    mov eax,cr0
    or eax,1 
    mov cr0,eax


	
	call init8259A

	;加载中断描述符
	xor   eax, eax
	mov   ax,  ds
	shl   eax, 4
	add   eax, LABEL_IDT
	mov   dword [IdtPtr + 2], eax
	lidt  [IdtPtr]

	
	sti		;恢复中断
    jmp dword SelectorCode32:0



;初始化8259A中断控制器
init8259A:
	mov  al, 0x11		;向主8259A发送ICW1
	out  0x20, al
	call io_delay

	out 0xa0, al		;向从8259A发送ICW1
	call io_delay


	;20h 分解成ICW2 是, ICW2[0,1,2] = 0, 这是强制要求的,
	;也就是ICW2的值不能是0x21,0x22之类,只要前三位是0就行
	;整个ICW2 = 0x20,这样的话,当主8259A对应的IRQ0管线向CPU发送信号时,
	;CPU根据0x20这个值去查找要执行的代码,IRQ1管线向CPU发送信号时,
	;CPU根据0x21这个值去查找要执行的代码,依次类推

	mov al, 0x20		;向主8259A发送ICW2
	out 0x21, al		;
						;
	call io_delay

	mov  al, 0x28		;向从8259A发送ICW2
	out  0xa1, al
	call io_delay

	;04h 分解成ICW3 相当于ICW[2] = 1, 
	;这表示从8259A通过主IRQ2管线连接到主8259A控制器
	mov  al, 0x04		; 向主8259A发送ICW3 
	out  0x21, al
	call io_delay

	mov  al, 0x02		;向从8259A 发送 ICW3 
	out  0xa1, al
	call io_delay

	mov  al, 0x02
	out  0x21, al
	call io_delay

	out  0xa1, al
	call io_delay


	;还需要再向两个芯片分别发送一个字节,叫OCW(operation control word), 
	;一个OCW是一字节数据,	也就是8bit,每一bit设置作用是,当OCW[i] = 1 时,
	;屏蔽对应的IRQ(i)管线的信号,例如OCW[0]=1, 那么IRQ0管线的信号将不会被CPU接收,以此类推
	;
	mov  al, 11111001b	;CPU只接收主8259A, IRQ1管线发送的信号,其他管线发送信号一概忽略
	out  0x21, al		;IRQ1对应的是键盘产生的中断
	call io_delay

	mov  al, 11101111b	;IRQ4 允许鼠标中断
	out  0xa1, al		;鼠标是通过从8259A的IRQ4管线向CPU发送信号
	call io_delay

	ret

io_delay:
     nop
     nop
     nop
     nop
     ret


    [SECTION .s32]
    [BITS 32]
SEG_CODE32:
	
    mov ax,SelectorStack
    mov ss,ax 
    mov esp,STACK_TOP

    mov ax,SelectorVRAM
    mov ds,ax 
   
    call init_main 

fin:    
    hlt
    jmp fin

;8259A中断控制器
LABEL_8259A:
	SpuriousHandler  equ LABEL_8259A - $$
	iretd

;键盘中断程序
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


    ;导入io操作函数模块
    %include "io.s"


    ;导入C语言编写的功能模块
    %include "os.s"    

 
    ;32位模式代码长度
    SegCodeLen  EQU $-SEG_CODE32

    [SECTION .gs]
    ALIGN 32 
    [BITS 32]

    LABEL_STACK:
        times 1024 db 0
    STACK_TOP   EQU $ - LABEL_STACK

4.修改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);

//初始化鼠标硬件
void init_mouse();

int num = 0;


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

	init_mouse_cursor((char *)0xa0000,100,100,COL8_008484);
	
	init_mouse();
	

    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_keyboard(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;
	}
}


#define  PORT_KEYDAT  0x60
#define  PORT_KEYSTA  0x64
#define  PORT_KEYCMD  0x64
#define  KEYCMD_WRITE_MODE  0x60
#define  KBC_MODE     0x47

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


#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4

//初始化键盘控制电路,鼠标控制电路是连接在键盘控制电路上,通过键盘电路实现初始化
void init_mouse(){
	waitKBCReady();

	//0x60让键盘电路进入数据接受状态
	io_out8(PORT_KEYCMD,KEYCMD_WRITE_MODE);
	waitKBCReady();

	//数据0x47要求键盘电路启动鼠标模式,这样鼠标硬件所产生的数据信息,通过键盘电路端口0x60就可读到
	io_out8(PORT_KEYDAT,KBC_MODE);	
	
	waitKBCReady();
	io_out8(PORT_KEYCMD,KEYCMD_SENDTO_MOUSE);
	waitKBCReady();
	//0xf4数据激活鼠标电路,激活后将会给CPU发送中断信号
	io_out8(PORT_KEYDAT,MOUSECMD_ENABLE);
}



/*
 *8259A 鼠标中断调用
 *
 */
void int_mouse(char *index){
	
	unsigned char *ascii = ascii_array;
	static char *msg = "mouse";
	for(int i=0;msg[i] != '\0';i++){
		int x =  135 + i*10;
		int y = 30;
		putChar((char *)0xa0000,x,y,COL8_FFFFFF,ascii+(msg[i]-0x20)*16,SCREEN_WIDTH);
	}
}

鼠标电路被激活后将给CPU发送中断信号显示mouse图形,加载运行floppy.img 效果如下:
在这里插入图片描述

优化缓存机制

1.随着功能的丰富,os.c 文件也越来越大会导致修改的难度增加,我们先对os.c 文件分离,考虑到我们OS 是一个简单小巧,文件不会太多。故把os.c 文件中相关申明、类型定义部分提取到os.h 文件中。修改后os.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


//定义缓冲区
typedef struct _FIFO8{
	//指向缓冲区
    char* buf;
	//r:读索引,w:写索引
	//len:存储数据长度
    int r, w, size, len, flag;
}FIFO8;


//*******************************函数声明*************************

//初始化调色板
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);

//初始化鼠标硬件
void init_mouse();

//缓存初始化
void fifo8_init(FIFO8 *fifo,int size,char *buf);
//缓冲区存放数据
int fifo8_put(FIFO8 *fifo,char data);
//缓冲区读取数据
int fifo8_get(FIFO8 *fifo);

这样我们只需要在os.c 文件中 #include "os.h",os.c 文件中写相关实现逻辑和屏蔽不需要暴露的功能!

2.os.c文件如下:

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

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


 
static char keybuf[32];
static char mousebuf[128];

static FIFO8 keybufInfo;
static FIFO8 mousebufInfo;


static int num = 0;


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

	fifo8_init(&keybufInfo,32,keybuf);
	fifo8_init(&mousebufInfo,128,mousebuf);

	init_mouse_cursor((char *)0xa0000,100,100,COL8_008484);
	
	init_mouse();
	

    for(; ;){
		if(keybufInfo.len>0){
			io_cli();

			static char arr[4] = {'0','x'};
			unsigned char *ascii = ascii_array;
			for(int t=0;t<keybufInfo.len;t++){
				char data = fifo8_get(&keybufInfo);
				char2HexStr(data,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++;
				}
			}

			io_sti();
		}
		else if(mousebufInfo.len>0){
			io_cli();

			static char arr[4] = {'0','x'};
			unsigned char *ascii = ascii_array;
			for(int t=0;t<mousebufInfo.len;t++){
				char data = fifo8_get(&mousebufInfo);
				char2HexStr(data,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++;
				}
			}

			io_sti();
		}
		else{
       		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;
			}
		}
	}

}


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

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

	fifo8_put(&keybufInfo,data);
}


#define  PORT_KEYDAT  0x60
#define  PORT_KEYSTA  0x64
#define  PORT_KEYCMD  0x64
#define  KEYCMD_WRITE_MODE  0x60
#define  KBC_MODE     0x47

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


#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4

//初始化键盘控制电路,鼠标控制电路是连接在键盘控制电路上,通过键盘电路实现初始化
void init_mouse(){
	waitKBCReady();

	//0x60让键盘电路进入数据接受状态
	io_out8(PORT_KEYCMD,KEYCMD_WRITE_MODE);
	waitKBCReady();

	//数据0x47要求键盘电路启动鼠标模式,这样鼠标硬件所产生的数据信息,通过键盘电路端口0x60就可读到
	io_out8(PORT_KEYDAT,KBC_MODE);	
	
	waitKBCReady();
	io_out8(PORT_KEYCMD,KEYCMD_SENDTO_MOUSE);
	waitKBCReady();
	//0xf4数据激活鼠标电路,激活后将会给CPU发送中断信号
	io_out8(PORT_KEYDAT,MOUSECMD_ENABLE);
}



/*
 *8259A 鼠标中断调用
 *
 */
void int_mouse(char *index){
	//当中断处理后,要想再次接收中断信号,就必须向中断控制器发送一个字节的数据
	io_out8(0x20,0x20);
	io_out8(0xa0,0x20);
	
	//读取鼠标数据
	char data = io_in8(0x60);
	fifo8_put(&mousebufInfo,data);
}


void fifo8_init(FIFO8 *fifo, int size,char *buf){
	fifo->buf = buf;
	fifo->r = 0;
	fifo->w = 0;
	fifo->size = size;
	fifo->len = 0;
	fifo->flag = 0;
}


int fifo8_put(FIFO8 *fifo,char data){
    if (fifo->len == fifo->size) {
        return -1;
    }

    fifo->buf[fifo->w] = data;
    fifo->w++;
    if (fifo->w == fifo->size) {
        fifo->w = 0;
    }

    fifo->len++;
    return 0;
}


int fifo8_get(FIFO8 *fifo) {
    
    if (fifo->len == 0) {
        return -1;
    }

   	int data = fifo->buf[fifo->r];
    fifo->r++;
    if (fifo->r == fifo->size) {
        fifo->r = 0;
    }

    fifo->len--;
    return data;
}

中断处理后,要想再次接收中断信号,必须向中断控制器发送一个字节的数据,这个字节数据叫OCW2, OCW2[0-2] 用来表示中断的优先级,OCW2[3-4]这两位必须设置为0,OCW[5]这一位称之为End of Interrupt, 这一位设置为1,表示当前中断处理结束,控制器可以继续调用中断函数处理到来的中断信号,要想下一次继续处理中断信号,这一位必须设置为1,OCW2[6-7]设置为0即可,我们代码中发送OCW2时的数值是0x20,也就是仅仅把OCW[5]设置为1。

3.加载运行floppy.img效果如下:
在这里插入图片描述

运行内核文件后屏幕上会显示0xfa,是鼠标被激活时传送过来的。

点击键盘a 字符,界面会出现0x1e、0x9e,再把鼠标放入虚拟机后移动鼠标屏幕上回出现一串信息,效果如下:
在这里插入图片描述

至此我们的键盘鼠标中断处理完成,最后鼠标发送的数据,需要连续三个字节一起解读。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

13.实现鼠标中断处理 的相关文章

随机推荐

  • WinRAR分析

    1 xff0e 样本概况 1 1 应用程序信息 应用程序名称 xff1a WinRAR 6 11 0 MD5值 xff1a F2BD2E3D8B7C7C8EEFA2B28E3280EDA3 SHA1值 xff1a DC2B378B27D1A
  • 树莓派搭建代理服务器(基于Squid)

    第一步 xff1a https blog csdn net bql email article details 79147250 第二个 xff1a 要使用Proxifier等软件 sudo gedit etc squid squid co
  • Nginx 使用naxsi 防xss、防注入攻击配置

    61 61 对于nginx有相应模块来完成WAF构建 xff0c 此处使用的是naxsi模块 61 61 Naxsi是一个开放源代码 高效 低维护规则的Nginx web应用防火墙模块 Naxsi的主要目标是帮助人们加固他们的web应用程序
  • pytorch 把tensor转换成int

    直接在tensor变量的后面加 item xff0c 就能把tensor类型转换成int类型 xff0c 我也不知道为什么 xff0c 试了别的都不行 准确的说是把Tensor 转化成Python scales
  • 最新软件设计师知识点分析(思维导图)

    大家好 xff0c 作为一个考取软件设计师的过来人 xff0c 今天我给大家分享一下关于软件设计师考题的复习经验 1 多做多分析多总结近年来习题 xff08 达到看到题就知道考点是什么怎么解 xff09 2 知道怎么在题中怎么找答案 3 梳
  • 记一次阿里云服务器被攻击导致无法ssh登陆(root也不行)

    服务器被攻击导致无法ssh 博主早上登陆准备做一些骚操作 xff0c 结果突然发现ssh不上服务器 xff0c 并且收到短信说服务器被挖矿病毒攻击 xff0c 然后服务器就斯密达了 xff0c 发现报错如下 xff1a Permission
  • Python geometry_msgs.msg.PoseStamped() Examples

    https www programcreek com python example 70252 geometry msgs msg PoseStamped
  • github 下载历史版本的代码

    操作如下所示 xff1a 根据历史commit xff0c 找到历史版本 xff0c 然后下载
  • 欢迎使用CSDN-markdown编辑器

    转 Eclipse下tomcat输出路径配置 标签 xff1a tomcateclipseserverpath 2012 07 12 16 09 7256人阅读 评论 0 收藏 举报 分类 xff1a eclipse tomcat xff0
  • 那根你想顺着去打人的网线是怎么制作的?

    更多精彩关注微信公众号 xff1a 信息技术员 现代社会大家都很浮躁 xff0c 总有人嚷嚷着要顺着网线去打人 xff0c 也不问问自己会做网线么 xff1f 本文手把手教你怎么制作一根网线 一 准备工作 1 网线一根 2 网线钳一把 3
  • Linux下动态库使用小结

    1 xff0e 静态库和动态库的基本概念 静态库 xff0c 是在可执行程序连接时就已经加入到执行码中 xff0c 在物理上成为执行程序的一部分 xff1b 使用静态库编译的程序运行时无需该库文件支持 xff0c 哪里都可以用 xff0c
  • 傻瓜式教学——手把手教你电脑三种方式连接打印机

    如何判断打印机和电脑的连接方式 xff1f 电脑连接打印机有三种方式情况 xff0c 一是通过USB直连 xff0c 二是通过网络连接 xff0c 三是通过共享连接 可通过下面方法判断连接方式 如果通过 口和电脑连接 xff0c 则是USB
  • 服务器为什么能够稳定可靠运行?

    前几天github服务器故障 xff0c 传言服务器被偷走一度上了热搜 xff0c 后证实传言是P图 xff08 下图为假 xff09 但确实每次大型互联网公司服务器故障都引发了人们的广泛讨论 其中还有不少上了热搜 那么服务器到底是何方神圣
  • Yanmar(洋马)发动机SPN-FMI代码在仪表显示

    分享一个自己在仪表上显示洋马发动机SPN FMI代码过程的记录 1 问 xff1a SPN和FMI什么意思 xff1f 答 xff1a 见SAE J1939 73 5 6 诊断故障码定义 诊断故障代码 xff08 DTC xff09 由4
  • APM调试,地面站随手记

    最近随公司调试4轴和8轴APM多旋翼 xff0c 本文将心得记录下来 xff0c 以备自己和他人查阅 xff0c 水平有限 xff0c 如有错误 xff0c 请不吝赐教 本文不定期更新 xff0c 转载请注明出处 2016 9 8 一 自检
  • 解决同一局域网下不同网段能ping通但是ssh不上服务器的情况

    一 xff1a 问题描述 xff1a 在公司的局域网网络环境下有四个ip网段 xff0c 分别是192 168 1 0 2 0 3 0 5 0 xff0c 服务器用的是5 0网段的 xff0c 而个人电脑用的则是1 0网段的 xff0c 在
  • STM32单片机电源端并联电容的重要性

    如图 xff0c 笔者用TQFP 32 100PIN 0 55MM转直插的转接板焊了一个STM32F207VET6的板子 板上引出了SWD调试接口 xff08 仅占用PA13和PA14 xff09 xff0c USART1串口引脚 xff0
  • Linux信号量常用操作表

    以下函数失败时均返回 1 xff0c 所在头文件为 include lt sys sem h gt 创建用于区分信号量的键值key xff1a key t key 61 ftok 34 foo bar 34 39 a 39 xff0c 第一
  • 一文加强对React的记忆(2021 年 6 月更新),收藏再也不用查看文档、教程了

    我不经常使用 React xff0c 所以每当我需要在 React 中做最小的事情时 xff0c 我都必须查看文档 教程或在论坛上发布问题 这就是我决定做这个记忆辅助工具的原因 xff0c 鉴于我的记忆力不是那么好 xff0c 我想为什么不
  • 13.实现鼠标中断处理

    简介 上节实现了对键盘中断服务子程序的处理和修改优化了中断程序 xff0c 但只是简单的在中断服务子程序中记录断码或通码 xff0c 缓冲区使用效率不高 目标 实现鼠标中断处理 优化中断缓存 pc中8259A中断控制器连接模型如下 1 鼠标