简介

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

目标

实现鼠标中断处理、优化中断缓存。pc中8259A中断控制器连接模型如下:

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

mov  al, 11111001b   ;CPU只接收主8259A, IRQ1管线发送的信号,其他管线发送信号一概忽略
out  0x21, al       ;IRQ1对应的是键盘产生的中断
call io_delaymov  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  3dw %2 & 0xffffdw %1 & 0xffffdb (%1>>16) & 0xff dw ((%2>>8) & 0x0f00) | (%3 & 0xf0ff)db (%1>>24) & 0xff %endmacroDA_32       EQU 4000h   ; 32 位段DA_C        EQU 98h ; 存在的只执行代码段属性值DA_DRW      EQU 92h ; 存在的可读写数据段属性值DA_DRWA     EQU 93h ; 存在的已访问可读写数据段类型值;中断描述符表;Gate selecotor, offset, DCount, Attr%macro Gate 4dw  (%2 & 0xffff)dw  %1dw  (%3 & 0x1f) | ((%4 << 8) & 0xff00)dw  ((%2>>16) & 0xffff)%endmacroDA_386IGate EQU 8Eh ; 中断调用门org 0x9000 jmp entry[SECTION .gdt];定义全局描述符                                段基址           段偏移上限       段属性LABEL_GDT:              GDescriptor         0,              0,             0LABEL_DESC_CODE:        GDescriptor         0,              SegCodeLen-1,  DA_C+DA_32 LABEL_DESC_VIDEO:       GDescriptor         0xb8000,        0xffff,        DA_DRWLABEL_DESC_STACK:       GDescriptor         0,              STACK_TOP-1,   DA_DRWA+DA_32LABEL_DESC_VRAM:        GDescriptor         0,              0xffffffff,    DA_DRW;gdt 表大小GdtLen  equ     $-LABEL_GDT;gdt表偏移上限和基地址GdtPtr  dw      GdtLen-1dd      0;cpu开机进入实模式时使用的段寄存器 cs,ds,es,ss 和偏移地址组成内存地址,内存地址=段寄存器 * 16 + 偏移地址 ;保护模式中段寄存器保存的是gdt 描述表中各个描述符的偏移,也叫选择子SelectorCode32      EQU     LABEL_DESC_CODE-LABEL_GDTSelectorVideo       EQU     LABEL_DESC_VIDEO-LABEL_GDTSelectorStack       EQU     LABEL_DESC_STACK-LABEL_GDTSelectorVRAM        EQU     LABEL_DESC_VRAM-LABEL_GDT;中断描述符表LABEL_IDT:%rep  0x21Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate%endrep;键盘中断向量(8259A 键盘中断向量0x20,IRQ1 是键盘中断请求,0x20 + IRQ[n] = 0x21.0x21:Gate  SelectorCode32, KeyboardHandler,0, DA_386IGate%rep  10Gate  SelectorCode32, SpuriousHandler,0, DA_386IGate%endrep;从中断控制器8259A 中断向量0x28,IRQ4 是鼠标中断请求,0x28 + IRQ[n] = 0x2c.0x2c:Gate  SelectorCode32, MouseHandler,0, DA_386IGateIdtLen  equ $ - LABEL_IDTIdtPtr  dw  IdtLen - 1dd  0[SECTION .s16][BITS 16]
entry:mov ax,cs mov ds,axmov es,axmov ss,axmov sp,0x100 ;设置屏幕色彩模式mov al,0x13mov ah,0int 0x10;设置LABEL_DESC_CODE描述符段基址mov eax,0 mov ax,cs shl eax,4add eax,SEG_CODE32mov word [LABEL_DESC_CODE+2],axshr eax,16mov [LABEL_DESC_CODE+4],almov [LABEL_DESC_CODE+7],ah;设置栈空间xor eax,eaxmov ax,cs shl eax,4add eax,LABEL_STACKmov word [LABEL_DESC_STACK+2],axshr eax,16mov byte [LABEL_DESC_STACK+4],almov byte [LABEL_DESC_STACK+7],ahmov eax,0mov ax,dsshl eax,4 add eax,LABEL_GDTmov dword [GdtPtr+2],eax;设置GDTR 寄存器lgdt [GdtPtr]cli     ;关闭可可屏蔽中断,如键盘中断in al,0x92 or al,0x02out 0x92,al mov eax,cr0or eax,1 mov cr0,eaxcall init8259A;加载中断描述符xor   eax, eaxmov   ax,  dsshl   eax, 4add   eax, LABEL_IDTmov   dword [IdtPtr + 2], eaxlidt  [IdtPtr]sti     ;恢复中断jmp dword SelectorCode32:0;初始化8259A中断控制器
init8259A:mov  al, 0x11     ;向主8259A发送ICW1out  0x20, alcall io_delayout 0xa0, al        ;向从8259A发送ICW1call 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发送ICW2out 0x21, al      ;;call io_delaymov  al, 0x28        ;向从8259A发送ICW2out  0xa1, alcall io_delay;04h 分解成ICW3 相当于ICW[2] = 1, ;这表示从8259A通过主IRQ2管线连接到主8259A控制器mov  al, 0x04       ; 向主8259A发送ICW3 out  0x21, alcall io_delaymov  al, 0x02     ;向从8259A 发送 ICW3 out  0xa1, alcall io_delaymov  al, 0x02out  0x21, alcall io_delayout  0xa1, alcall 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_delaymov  al, 11101111b ;IRQ4 允许鼠标中断out  0xa1, al       ;鼠标是通过从8259A的IRQ4管线向CPU发送信号call io_delayretio_delay:nopnopnopnopret[SECTION .s32][BITS 32]
SEG_CODE32:mov ax,SelectorStackmov ss,ax mov esp,STACK_TOPmov ax,SelectorVRAMmov ds,ax call init_main fin:    hltjmp fin;8259A中断控制器
LABEL_8259A:SpuriousHandler  equ LABEL_8259A - $$iretd;键盘中断程序
LabelKeyboardHandler:KeyboardHandler equ LabelKeyboardHandler - $$push espush dspushadmov  eax, esppush eaxcall int_keyboardpop  eaxmov  esp, eaxpopadpop  dspop  esiretd;鼠标中断程序
LabelMouseHandler:MouseHandler equ LabelMouseHandler - $$push espush dspushadmov  eax, esppush eaxcall int_mousepop  eaxmov  esp, eaxpopadpop  dspop  esiretd;导入io操作函数模块%include "io.s";导入C语言编写的功能模块%include "os.s"    ;32位模式代码长度SegCodeLen  EQU $-SEG_CODE32[SECTION .gs]ALIGN 32 [BITS 32]LABEL_STACK:times 1024 db 0STACK_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,则高地址部分显示在内存的低地址,//最低位的应该偏移7if((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,则高地址部分显示在内存的低地址,//最低位的应该偏移7if((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,再把鼠标放入虚拟机后移动鼠标屏幕上回出现一串信息,效果如下:

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

13.实现鼠标中断处理相关推荐

  1. 便利贴--13{随鼠标移动的canvas,未完成}

    便利贴--13{随鼠标移动的canvas,未完成} canvas canvas class manyPoint {constructor(val) {} }//鼠标 class mouseAround ...

  2. RK3399平台开发系列讲解(中断篇)13.17、中断处理方式的汇总

  3. 苹果iOS 13概念曝光 音量UI大改鼠标即时配对

    [TechWeb]自从苹果发布iOS 12以来,关于iOS 13的消息就频繁曝光.很多网友都希望在iOS 12中尚未实现的功能可以在iOS 13上得以呈现.日前国外设计师曝光了一些有关iOS 13新的 ...

  4. 让鼠标漫天飞舞:在内核中实现鼠标的中断处理

    代码的调试和运行,以及更详细的讲解,请参看视频: Linux kernel Hacker, 从零构建自己的内核 上一节,我们成功实现了键盘按键的中断响应,本节,我们看如何响应鼠标的中断信号,并做相应处 ...

  5. [读码][js,css3]能感知鼠标方向的图片遮罩效果

    效果图: 无意间看到过去流行的一个效果:[能感知鼠标方向的图片遮罩效果] 近来不忙,就仔细的看了一看 看到后来发现,网上有好多版本,谁是原著者似乎已经无法考证. 读码就要读比较全面的,读像是原著的代码 ...

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

    键盘&鼠标中断实现 本节主要实现键盘中断和鼠标中断,键盘中断实现将键盘数据显示到屏幕:鼠标中断实现鼠标位置的移动. 键盘中断通过主8259A的IRQ1触发,鼠标中断通过从8259A的IRQ4触 ...

  7. usb扩展坞同时接键盘鼠标_一种带有扩展坞功能的一体式键鼠的制作方法

    一种带有扩展坞功能的一体式键鼠的制作方法 [技术领域] [0001]本实用新型涉及加固计算机技术领域,具体涉及一种带有扩展坞功能的一体式键 ea啦O [背景技术] [0002]随着加固计算机技术的不断 ...

  8. delphi控件使用

    Standard页 1-      Tlistbox控件 重要属性items.* 举例,界面设计如下左,执行结果如下右: 代码为 procedure TForm1.Button1Click(Sende ...

  9. c语言案例分析105,C语言实战105例源码

    C语言实战105例源码 以下程序大家如有兴趣可在文件夹下载即可 第1部分 基础篇 实例1 一个价值"三天"的BUG 2 实例2 灵活使用递增(递减)操作符 5 实例3 算术运算符计 ...

  10. C语言程序设计 参考书籍

    如要进一步学习C语言,可以参考以下图书. C语言高级编程及实例剖析 作者:王为青,刘变红 编著 出版社:人民邮电出版社 原价:48 出版日期:2007-3-1 第1章 内存管理 1.1 预备知识 1. ...

最新文章

  1. Michael Brostein 最新几何深度学习综述:超越 WL 和原始消息传递的 GNN
  2. 两岸MVP强强联手--最硬Windows Server 2008达人
  3. 民生银行马坡数据中心基础设施Uptime Mamp;O运营管理体系建立与实施
  4. 原神服务器维护后抽奖池会更新吗,原神:更新维护一小时,补偿60原石,玩家祈求多维护几天!...
  5. OpenCV Mat基础认知感
  6. python 菜鸟-Python 运算符
  7. 金税盘计算机USB无法识别,金税盘无法识别怎么办
  8. NLP自然语言处理 之 jieba中文处理
  9. USB转串口芯片CH340
  10. 改led背光有光斑_为什么把车灯改成LED,效果还不如卤素?
  11. Grub4Dos 学习笔记
  12. YOLO v3源码详解
  13. scrapy框架爬虫
  14. 01.Win10修改用户名及user文件名称的一波三折
  15. 我犯的错和解决AnimationEvent 'NewEvent' has no receiver! Are you missing a component
  16. SVN windows 快速入门
  17. 简单的方法Android的数据恢复你的Andr​​oid手机
  18. 用python实现todolist_todolist项目
  19. 前端架构师面试题及答案,最终入职阿里
  20. 大唐天下全返app系统开发

热门文章

  1. 统计学-贾俊平(第四版)学习笔记
  2. |app自动化测试之Appium 原理 与 JsonWP 协议分析
  3. 智能电子秤全国产化电子元件推荐方案
  4. 人脸数据库使用授权求助帖
  5. php如何让图片大小自适应,dedecms怎么让图片自适应屏幕大小
  6. 基于UML的人事管理系统
  7. php laravel 分页,laravel 分页问题
  8. 电脑罗盘时钟html怎么设置成桌面,抖音时钟屏保怎么设置 罗盘时钟屏保设置方法...
  9. Linux驱动开发-编写DS18B20驱动
  10. 计算机3d相册代码,3D水晶相册代码【有显示图】