简介
上节实现了对键盘中断服务子程序的处理和修改优化了中断程序,但只是简单的在中断服务子程序中记录断码或通码,缓冲区使用效率不高。
目标
实现鼠标中断处理、优化中断缓存。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设