当前位置: 首页 > news >正文

自制操作系统day7(获取按键编码、FIFO缓冲区、鼠标、键盘控制器(Keyboard Controller, KBC)、PS/2协议)

day7


获取按键编码(hiarib04a)


void inthandler21(int *esp)
{struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;  // 获取系统启动信息结构体指针unsigned char data, s[4];                                   // data: 键盘数据缓存,s: 格式化字符串缓存io_out8(PIC0_OCW2, 0x61);     // 发送EOI命令(0x61)通知PIC中断处理完成// 具体说明:
// 1. PIC0_OCW2 是主PIC的操作命令字寄存器(端口地址0x20)
// 2. 0x61 的二进制形式是 01100001,其中:
//    - 高三位 011 表示「指定IRQ级别的EOI命令」
//    - 低五位 00001 表示IRQ1(键盘中断)
// 3. 该操作完成两个功能:
//    a. 清除PIC的中断服务寄存器(ISR)对应位
//    b. 允许PIC继续接收新的中断请求data = io_in8(PORT_KEYDAT);                                 // 从键盘数据端口读取扫描码sprintf(s, "%02X", data);                                   // 将扫描码转为16进制字符串boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31); // 清空显示区域(青灰色背景)putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s); // 显示白色文本的扫描码return;
}

这句话用来通知PIC“已经知道发生了IRQ1中断哦”。如果是IRQ3,则写成0x63。也就是说,将“0x60+IRQ号码”输出给OCW2就可以。执行这句话之后,PIC继续时刻监视IRQ1中断是否发生。

如果忘记了执行这句话,PIC就不再监视IRQ1中断,不管下次由键盘输入什么信息,系统都感知不到了。

从编号为0x0060的设备输入的8位信息是按键编码。编号为0x0060的设备就是键盘。


加快中断处理(hiarib04b


struct KEYBUF {unsigned char data, flag;
};
#define PORT_KEYDAT		0x0060struct KEYBUF keybuf;
void inthandler21(int *esp)
{unsigned char data;io_out8(PIC0_OCW2, 0x61);	data = io_in8(PORT_KEYDAT);if (keybuf.flag == 0) {keybuf.data = data;keybuf.flag = 1;}return;
}
for (;;) {io_cli();//如果flag是0,
//表示缓冲区为空;如果flag是1,就表示缓冲区中存有数据if (keybuf.flag == 0) {io_stihlt();} else {i = keybuf.data;keybuf.flag = 0;io_sti();sprintf(s, "%02X", i);boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);}}

io_sti();io_hlt();不等于io_stihlt();

如果io_sti()之后产生了中断,keybuf里就会存入数据,这时候让CPU进入HLT状态,keybuf里存入的数据就不会被觉察到。根据CPU的规范,机器语言的STI指令之后,如果紧跟着HLT指令,那么就暂不受理这两条指令之间的中断,而要等到HLT指令之后才受理,

缓冲区建立优点:

  1. 异步处理机制:键盘中断(IRQ1)发生时需要立即响应,但此时系统可能正在处理其他任务。缓冲区作为数据的中转站,允许中断处理程序快速保存数据后立即返回
  2. 防止数据丢失:当用户快速连续按键时,中断可能连续触发。缓冲区可以存储多个按键数据(虽然当前实现是单缓冲区,后续可以扩展为队列)
  3. 线程安全:通过flag标志位(keybuf.flag)实现简单的同步机制,避免主程序读取数据时与中断程序发生竞争
  4. 解耦硬件操作:将底层硬件读取(io_in8)与上层逻辑处理分离,提高代码的可维护性

自此中断处理和调用流程

  1. 中断向量表初始化 (在 int.c 的 PIC 初始化中)
int.
// PIC初始化时设置中断向量号
io_out8(PIC0_ICW2, 0x20);  // IRQ0-7对应INT 0x20-0x27
io_out8(PIC1_ICW2, 0x28);  // IRQ8-15对应INT 0x28-0x2f
  1. 汇编层中断门处理 (在 naskfunc.nas 中实现)
naskfunc.nas
_asm_inthandler21: ; 对应键盘中断(IRQ1)PUSH    ESPUSH    DSPUSHADMOV     EAX,ESPPUSH    EAXMOV     AX,SSMOV     DS,AXMOV     ES,AXCALL    _inthandler21 ; 调用C语言处理函数POP     EAXPOPADPOP     DSPOP     ESIRETD
  1. C语言中断服务程序 (在 int.c 中实现)
int.c
// 键盘中断处理程序
void inthandler21(int *esp) {unsigned char data;io_out8(PIC0_OCW2, 0x61); // 通知PIC中断处理完成data = io_in8(PORT_KEYDAT); // 读取键盘数据// ... 缓冲区处理 ...
}

完整的中断调用流程:

  1. 硬件中断触发 → 2. CPU查IDT表 → 3. 跳转至_asm_inthandlerXX → 4. 保存上下文 → 5. 调用C处理函数 → 6. 恢复上下文 → 7. IRETD返回

制作FIFO缓冲区(hiarib04c


就是将缓冲区的数据部分弄成链表,但这里是静态数组,到下面整理缓冲区就是动态分配内存了

struct KEYBUF {unsigned char data[32];int next;
};
void inthandler21(int *esp)
{unsigned char data;io_out8(PIC0_OCW2, 0x61);data = io_in8(PORT_KEYDAT);if (keybuf.next < 32) {keybuf.data[keybuf.next] = data;keybuf.next++;}return;
}
for (;;) {io_cli();if (keybuf.next == 0) {io_stihlt();} else {i = keybuf.data[0];keybuf.next--;for (j = 0; j < keybuf.next; j++) {keybuf.data[j] = keybuf.data[j + 1];}io_sti();sprintf(s, "%02X", i);boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);}}

但这个程序,中断处理的时候涉及数据移送操作,如果在数据移送前中断的话,数据就会乱

所以下面就改善一下


改善FIFO缓冲区(hiarib04d


其实就是一个双指针,一个读数据指针,一个写数据指针

写的时候直接覆盖写,到了len最大容量就归0


整理FIFO缓冲区(hiarib04e


struct FIFO8 {unsigned char *buf;int p, q, size, free, flags;
};
/*可变缓冲区,size存入缓冲区的总字节数,变量free保存缓冲区里美欧数据的字节数,
buf存缓冲区的地址,p代表下一个数据写入地址(next_w),q代表下一个数据读出地址
(next_r)。
*/
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
/* 初始化FIFO缓冲区 */
{
fifo->size = size;
fifo->buf = buf;
fifo->free = size; /* 缓冲区的大小 */
fifo->flags = 0;
fifo->p = 0; /* 下一个数据写入位置 */
fifo->q = 0; /* 下一个数据读出位置 */
return;
}
#define FLAGS_OVERRUN 0x0001
int fifo8_put(struct FIFO8 *fifo, unsigned char data)
/* 向FIFO传送数据并保存 */
{
if (fifo->free == 0) {
/* 空余没有了,溢出 */
fifo->flags |= FLAGS_OVERRUN;
return -1;
}
fifo->buf[fifo->p] = data;
fifo->p++;
if (fifo->p == fifo->size) {
fifo->p = 0;
}
fifo->free--;
return 0;
}
int fifo8_get(struct FIFO8 *fifo)
/* 从FIFO取得一个数据 */
{
int data;
if (fifo->free == fifo->size) {
/* 如果缓冲区为空,则返回 -1 */
return -1;
}
data = fifo->buf[fifo->q];
fifo->q++;
if (fifo->q == fifo->size) {
fifo->q = 0;
}
fifo->free++;
return data;
}
int fifo8_status(struct FIFO8 *fifo)
/* 报告一下到底积攒了多少数据 */
{
return fifo->size - fifo->free;
}

鼠标(harib04f


#define PORT_KEYDAT 0x0060
#define PORT_KEYSTA 0x0064
#define PORT_KEYCMD 0x0064
#define KEYSTA_SEND_NOTREADY 0x02
#define KEYCMD_WRITE_MODE 0x60
#define KBC_MODE 0x47
void wait_KBC_sendready(void)
{
/* 等待键盘控制电路准备完毕 */
for (;;) {
if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) {
break;
}
}
return;
//键盘控制电路(keyboard controller, KBC)做好准备动作,等待控制指令的到来。
//因为虽然CPU的电路很快,但键盘控制电路却没有那么快。
//如果键盘控制电路可以接受CPU指令了,CPU从设备号码0x0064处所读
//取的数据的倒数第二位(从低位开始数的第二位)应该是0。在确认到这一位是0之前,程序一直通过for语句循环查询。
}
void init_keyboard(void)
{
/* 初始化键盘控制电路 
一边确认可否往键盘
控制电路传送信息,一边发送模式设定指令,指令中包含着要设定为何种模式。模
式设定的指令是0x60,利用鼠标模式的模式号码是0x47,*/
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, KBC_MODE);
return;
}

键盘控制器(Keyboard Controller, KBC)

i8042 键盘控制器-------详细介绍 - LinKArftc - 博客园 (cnblogs.com)

在x86架构中,键盘控制器(Keyboard Controller, KBC)通过特定端口与CPU通信。以下是代码中涉及的端口地址、宏定义和函数的详细解释:


一、端口地址定义

宏定义端口地址功能描述
PORT_KEYDAT0x0060键盘数据端口:用于读取键盘扫描码或发送键盘配置参数。
PORT_KEYSTA0x0064键盘状态端口:读取键盘控制器的状态(如是否准备好接收命令)。
PORT_KEYCMD0x0064键盘命令端口:向键盘控制器发送控制命令(与状态端口共享地址,操作区分方向)。

二、关键宏定义

宏定义功能描述
KEYSTA_SEND_NOTREADY0x02状态寄存器中的“发送未就绪”标志位:若该位为1,表示控制器忙,不能接收新命令。
KEYCMD_WRITE_MODE0x60键盘控制器的命令:写入操作模式到配置字节。
KBC_MODE0x47键盘控制器的工作模式参数:启用键盘和鼠标接口,并设置扫描码转换模式。

三、函数解析

1. wait_KBC_sendready()

void wait_KBC_sendready(void) {for (;;) {if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) {break;}}return;
}
  • 功能:等待键盘控制器准备好接收命令。
  • 实现
    1. 循环读取状态端口(0x0064)。
    2. 检查状态寄存器的第1位(KEYSTA_SEND_NOTREADY):
      • 若为0,表示控制器就绪,退出循环。
      • 若为1,继续等待。

2. init_keyboard()

void init_keyboard(void) {wait_KBC_sendready();io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);  // 发送模式设置命令wait_KBC_sendready();io_out8(PORT_KEYDAT, KBC_MODE);           // 写入模式参数return;
}
  • 功能:初始化键盘控制器的工作模式。
  • 流程
    1. 等待控制器就绪。
    2. 向命令端口(0x0064)发送命令 0x60KEYCMD_WRITE_MODE),表示要写入配置字节。
    3. 再次等待控制器就绪。
    4. 向数据端口(0x0060)写入模式参数 0x47KBC_MODE),配置控制器行为。

四、配置字节 0x47 的位定义

名称功能
7Reserved保留位,必须设为0。
6Translation Mode1=启用扫描码转换(将XT键盘扫描码集1转换为AT键盘扫描码集2)。
5Second Port Clock0=启用鼠标接口(PS/2 Port 2)。
4First Port Clock0=启用键盘接口(PS/2 Port 1)。
3Ignore Lock Keys0=正常处理锁定键(如Caps Lock)。
2System Flag由BIOS设置的系统标志(通常设为0)。
1Second Port IRQ1=启用鼠标中断(IRQ12)。
0First Port IRQ1=启用键盘中断(IRQ1)。

0x47(二进制 01000111)的具体配置

作用
70保留位为0,符合规范。
61启用扫描码转换(将XT扫描码转换为AT扫描码,增强兼容性)。
50启用鼠标接口(允许鼠标数据传输)。
40启用键盘接口(允许键盘数据传输)。
30正常处理Caps Lock等锁定键。
20系统标志为0(通常由BIOS设置)。
11启用鼠标中断(IRQ12),允许鼠标事件触发中断。
01启用键盘中断(IRQ1),允许键盘事件触发中断。

五、硬件交互原理

  1. 端口I/O操作
    • io_in8(port):从指定端口读取1字节数据。
    • io_out8(port, data):向指定端口写入1字节数据。
    • 在x86中,端口地址空间独立于内存地址空间,需通过IN/OUT指令访问。
  2. 键盘控制器流程
    • 发送命令:向PORT_KEYCMD0x0064)写入命令字节。
    • 写入参数:向PORT_KEYDAT0x0060)写入命令参数(如模式字节)。
    • 状态检查:从PORT_KEYSTA0x0064)读取状态,确保操作安全。

六、代码的意义

这段代码的目标是配置键盘控制器以支持键盘和鼠标输入,具体作用包括:

  1. 启用键盘接口(允许接收按键扫描码)。
  2. 启用鼠标接口(为后续鼠标驱动做准备)。
  3. 设置扫描码转换模式,确保兼容性。

七、实际应用场景

  • 操作系统启动初期:在初始化输入设备时调用init_keyboard(),确保键盘可用。
  • 外设驱动开发:理解端口通信是编写键盘/鼠标驱动的基础。

总结

这段代码通过操作键盘控制器的端口,配置其工作模式,为后续处理键盘和鼠标输入奠定基础。理解端口地址、状态标志和配置字节的细节,是开发操作系统输入子系统的关键一步。

发送鼠标激活指令

#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4
void enable_mouse(void)
{
/* 激活鼠标 */
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
return; /* 顺利的话,键盘控制其会返送回ACK(0xfa)*/
}

在x86架构中,PS/2键盘控制器(KBC)负责管理键盘和鼠标的通信。以下是代码的逐层解析:


一、关键宏定义

宏定义功能描述
KEYCMD_SENDTO_MOUSE0xD4键盘控制器命令:指示下一个写入数据端口的字节将发送到鼠标(而非键盘)。
MOUSECMD_ENABLE0xF4鼠标命令:启用鼠标数据报告模式(开始发送移动/按键事件)。

二、代码流程分析

函数 enable_mouse()

void enable_mouse(void) {wait_KBC_sendready();            // 等待KBC就绪io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);  // 发送命令0xD4到命令端口(0x64)wait_KBC_sendready();            // 再次等待KBC就绪io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);      // 发送命令0xF4到数据端口(0x60)return;  // 正常情况下,KBC会返回ACK(0xFA)
}

三、分步详解

1. wait_KBC_sendready()

  • 作用:确保键盘控制器(KBC)可接收新命令。
  • 实现:循环读取状态端口(0x64),直到状态寄存器的“发送未就绪”位(KEYSTA_SEND_NOTREADY=0x02)为0。

2. 发送命令 0xD4 到命令端口(0x64

  • 目的:通知KBC,下一个写入数据端口(0x60)的字节是发送给鼠标的指令(而非键盘)。
  • PS/2协议:键盘和鼠标共享同一控制器,通过此命令区分目标设备。

3. 发送命令 0xF4 到数据端口(0x60

  • 作用:向鼠标发送“启用数据报告”指令。此后,鼠标将开始发送移动和按键事件数据包。
  • 响应:鼠标应返回0xFA(ACK确认),表示指令已接收。

四、硬件交互逻辑

  1. KBC与设备的通信链

    CPU → KBC命令端口(0x64) → 发送命令0xD4 → KBC → 转发到鼠标
    CPU → KBC数据端口(0x60) → 发送数据0xF4 → KBC → 转发到鼠标
  2. ACK确认机制

    • 鼠标收到有效指令后,会通过KBC返回0xFA(需通过中断或轮询读取数据端口获取)。
    • 此代码未处理ACK,可能依赖后续中断服务程序(ISR)处理。

五、技术细节

1. 键盘控制器状态寄存器(PORT_KEYSTA=0x64)

名称说明
0Output Buffer Full1=数据端口(0x60)有数据待读取。
1Input Buffer Full1=控制器忙,不能接收命令(代码检查此位)。
2-7保留

2. 鼠标命令 0xF4 的作用

  • 启用数据报告模式:鼠标持续发送数据包,格式为3字节:

    Byte 1: Y溢出 | X溢出 | Y符号位 | X符号位 | 保留 | 中键 | 右键 | 左键
    Byte 2: X轴移动量(补码)
    Byte 3: Y轴移动量(补码)

六、实际应用场景

  • 操作系统初始化阶段:在键盘初始化后调用此函数,激活鼠标输入。
  • 驱动程序开发:理解PS/2协议是处理传统输入设备的基础。

七、注意事项

  • ACK处理:需在中断处理中捕获0xFA,否则可能错过鼠标数据。
  • 兼容性:现代硬件可能使用USB替代PS/2,需额外判断设备存在性。
  • 错误处理:未收到ACK时需重试或报错。

总结

此代码通过键盘控制器向鼠标发送启用指令,使其进入数据报告模式。核心步骤为:

  1. 等待控制器就绪 → 2. 声明目标为鼠标 → 3. 发送启用命令。
    理解PS/2协议和端口I/O操作是开发输入设备驱动的关键。

PS/2协议解释

PS/2协议是早期计算机用于连接键盘和鼠标的通信标准,尽管逐渐被USB取代,但在许多传统系统和嵌入式设备中仍有应用。以下是其核心机制和关键细节的详解:


一、物理接口与电气特性

  1. 接口定义
    • PS/2接口为6针Mini-DIN,实际使用4针:

      引脚功能
      1数据线(DATA)
      3地线(GND)
      4VCC(+5V)
      5时钟线(CLK)
  2. 通信方式
    • 同步串行传输:设备(键盘/鼠标)与主机(键盘控制器)通过CLK同步,DATA线传输数据。
    • 主从模式:默认设备控制CLK,主机可接管时钟(如发送命令时)。

二、数据帧格式

每帧数据包含 11位,按顺序如下:

名称说明
1起始位0标志数据开始
8数据位0/1LSB(最低位)优先发送
1奇偶校验位0/1奇校验(数据位+校验位的1个数为奇)
1停止位1标志数据结束

示例:键盘发送按键按下码 0x1C(字母’A’的扫描码)的帧结构:

0 0011100 1 1 → 起始位 + 数据(二进制0011100,LSB优先为0011100) + 奇偶位 + 停止位。


三、通信方向与流程

1. 设备到主机(如按键事件)

  1. 设备检测到动作(如按键按下),生成中断(IRQ1键盘/IRQ12鼠标)。
  2. 设备拉低CLK并发送数据帧。
  3. 主机读取DATA线,在CLK下降沿采样数据位。

2. 主机到设备(如发送命令)

  1. 主机拉低CLK至少100μs,通知设备准备接收命令。
  2. 主机控制CLK,逐位发送命令帧。
  3. 设备在接收到命令后,返回ACK(0xFA)或错误码。

四、关键命令与响应

1. 键盘常用命令

命令功能响应
重置0xFF复位键盘0xFA + 0xAA
启用扫描0xF4开始发送按键事件0xFA
设置LED0xED控制Num/Caps/Scroll灯0xFA

2. 鼠标常用命令

命令功能响应
重置0xFF复位鼠标0xFA + 0xAA
启用报告0xF4开始发送移动/按键数据0xFA
设置分辨率0xE8调整移动灵敏度0xFA

五、数据包格式

1. 键盘数据包

  • 单字节:传输按键的扫描码(按下或释放)。
    • 例如:0x1C(A键按下),0x9C(A键释放,前缀0xF0表示释放)。

2. 鼠标数据包

  • 标准3字节格式(兼容模式):

    字节位7-0
    Byte1Y溢出
    Byte2X轴移动量(8位补码,-128~127)
    Byte3Y轴移动量(8位补码,-128~127)

六、错误处理与重试

  1. 奇偶校验错误:主机检测到校验错误时,可发送重传命令(0xFE)。
  2. 超时:若设备未响应,主机可复位设备或记录错误。
  3. ACK缺失:未收到0xFA时,主机应重发命令或初始化设备。

七、PS/2与USB的对比

特性PS/2USB
热插拔不支持(可能损坏设备)支持
中断机制专用IRQ(低延迟)轮询或中断传输
协议复杂度简单(固定数据包)复杂(多种传输类型、描述符)
应用场景传统设备、嵌入式系统现代外设、即插即用

八、操作系统中的实现

  1. 初始化流程
    • 启用键盘/鼠标接口 → 发送重置命令 → 配置扫描码模式。
  2. 中断处理程序
    • 读取数据端口(0x60) → 解析扫描码或鼠标包 → 传递事件到上层。
  3. 缓冲区管理
    • 维护循环队列存储输入事件,避免数据丢失。

总结

PS/2协议通过同步串行通信实现低延迟输入,其核心在于时钟同步、数据帧格式和命令响应机制。

从鼠标接受数据(harib04g


struct FIFO8 mousefifo;
void inthandler2c(int *esp)
/* 来自PS/2鼠标的中断 */
{
unsigned char data;
io_out8(PIC1_OCW2, 0x64); /* 通知PIC1 IRQ-12的受理已经完成 */
io_out8(PIC0_OCW2, 0x62); /* 通知PIC0 IRQ-02的受理已经完成 */
data = io_in8(PORT_KEYDAT);
fifo8_put(&mousefifo, data);
return;
}

在x86系统中,处理来自PS/2鼠标的中断(IRQ12)时,需要与可编程中断控制器(PIC)交互以确认中断处理完成。以下是代码的详细解释:


一、代码功能概述

这段代码是处理PS/2鼠标中断的中断服务程序(ISR),主要完成以下操作:

  1. 通知PIC中断处理完成:通过向主PIC和从PIC发送EOI(End of Interrupt)命令。
  2. 读取鼠标数据:从键盘控制器数据端口获取鼠标事件数据。
  3. 存储数据到缓冲区:将数据存入FIFO队列供后续处理。

二、关键代码解析

1. 中断处理流程

void inthandler2c(int *esp) {unsigned char data;// 通知PIC中断处理完成io_out8(PIC1_OCW2, 0x64);  // 从PIC(IRQ12)的EOIio_out8(PIC0_OCW2, 0x62);  // 主PIC(IRQ2)的EOI// 读取鼠标数据data = io_in8(PORT_KEYDAT);// 存入FIFO队列fifo8_put(&mousefifo, data);return;
}

2. PIC的端口与命令

  • PIC1_OCW2:从PIC(PIC1)的操作命令字端口,地址为0xA0
  • PIC0_OCW2:主PIC(PIC0)的操作命令字端口,地址为0x20
  • EOI命令值0x640x62是特殊EOI命令,通知PIC中断已处理。

三、PIC的EOI通知机制

1. 主从PIC的级联

  • 主PIC(PIC0):处理IRQ0-IRQ7。
  • 从PIC(PIC1):处理IRQ8-IRQ15,其输出连接到主PIC的IRQ2。
  • 中断传递:当从PIC的IRQ12(鼠标中断)触发时,主PIC的IRQ2也会被激活。

2. 发送EOI命令的必要性

  • 目的:告诉PIC中断处理已完成,允许其继续响应新中断。
  • 级联场景:处理从PIC的中断(如IRQ12)时,需向主从PIC均发送EOI。

3. 特殊EOI命令详解

  • OCW2格式

    7 ®6 (SL)5 (EOI)4-0 (L3-L0)
    011中断级别
  • 从PIC(IRQ12)的EOI命令0x64

    • 二进制:01100100
    • SL=1, EOI=1:表示发送特殊EOI命令。
    • L3-L0=0100:指定从PIC的中断级别4(对应IRQ12 = IRQ8 + 4)。
  • 主PIC(IRQ2)的EOI命令0x62

    • 二进制:01100010
    • SL=1, EOI=1:特殊EOI命令。
    • L3-L0=0010:指定主PIC的中断级别2(IRQ2)。

四、代码执行步骤

  1. 发送EOI到从PIC

    io_out8(0xA0, 0x64); // 通知从PIC,IRQ4(即IRQ12)已处理
    • 从PIC收到命令后,清除IRQ4的中断状态。
  2. 发送EOI到主PIC

    io_out8(0x20, 0x62); // 通知主PIC,IRQ2(级联中断)已处理
    • 主PIC收到命令后,清除IRQ2的中断状态。
  3. 读取鼠标数据

    data = io_in8(0x60); // 从键盘控制器数据端口读取鼠标数据包
    • 数据端口0x60用于传输键盘和鼠标的输入数据。
  4. 存储数据到缓冲区

    fifo8_put(&mousefifo, data); // 将数据存入队列供后续解析
    • 使用FIFO队列避免中断处理过程中数据丢失。

五、关联硬件行为

  • 中断触发:鼠标移动或点击时,键盘控制器通过IRQ12通知CPU。
  • PIC级联:从PIC通过主PIC的IRQ2向CPU传递中断。
  • 数据包格式:PS/2鼠标数据为3字节,需在后续代码中解析。

六、常见问题

1. 为何需要同时通知主从PIC?

  • 从PIC的中断通过主PIC的IRQ2级联,需两者均确认中断完成,否则后续中断会被阻塞。

2. 0x640x62如何计算?

  • 特殊EOI命令公式:OCW2 = 0x60 | (IRQ_LEVEL & 0x07)
    • 从PIC IRQ4:0x60 | 4 = 0x64
    • 主PIC IRQ2:0x60 | 2 = 0x62

3. 是否必须使用特殊EOI?

  • 常规EOI(0x20)仅适用于非特殊模式,级联中断需特殊EOI明确指定中断级别。

总结

这段代码通过向主从PIC发送特殊EOI命令,确保中断处理正确完成,并读取鼠标数据存入缓冲区。理解PIC的级联机制和OCW2命令格式,是编写可靠中断处理程序的关键。

相关文章:

  • 鸿蒙Flutter实战:23-混合开发详解-3-源码模式引入
  • FreeBSD14.2因为爆内存而导致Xfce4视窗被卡,桌面变黑色,只能看到鼠标在窗体中心,鼠标无反应,键盘无反应
  • 自制操作系统day8 (鼠标数据取得、通往32位模式之路、A20GATE、切换到保护模式、控制寄存器cr0-cr4以及cr8、ALIGNB)
  • 创建信任所有证书的HttpClient:Java 实现 HTTPS 接口调用,等效于curl -k
  • 【Java面试】从Spring Boot到Kafka:技术栈与业务场景全面剖析
  • 养生新策:五维开启健康生活
  • 青少年编程与数学 02-020 C#程序设计基础 01课题、C#编程概要
  • 现代生活健康养生新策略
  • STM32:0.96寸OLED屏驱动全解析——SSD1306 I2C通信与显存配置指南
  • 二十、面向对象底层逻辑-ServiceRegistry接口设计集成注册中心
  • AI 多 Agent 图形化开发深度解析:iVX IDE 与主流产品技术架构对比研究
  • 在 Matter.js 物理引擎中,isSensor 布尔属性的使用
  • 【AI问答】Java类中,一些变量设置了@NotNull,怎么在调用内部方法时校验变量是否为空
  • Ubuntu20.04的安装(VMware)
  • LInux—shell编程
  • MySQL的可重复读事务隔离级别的实现原理
  • 人脸识别备案开启安全防护模式!紧跟《办法》!
  • 核实发票的真实性与合法性-发票查验接口-虚假发票防范
  • 前端图片裁剪上传全流程详解:从预览到上传的完整流程
  • python学习 day5
  • 西安学校网站建设哪家专业/百度问答怎么赚钱
  • 自己做的网站网站搜索/百度词条搜索排行
  • WordPress客服插件破解版/端点seo博客
  • 网站换域名能换不/软文范例大全800字
  • 做团购网站多少钱/百度免费咨询
  • 工业设计公司简介/行者seo无敌