首页 > 编程语言 >myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动

myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动

时间:2022-11-24 11:08:20浏览次数:61  
标签:鼠标 中断 dataport C++ 键盘 myos3 80 VideoMemory


​​myos1 大学生利用C++构建一个完整的操作系统打印helloworld​​myos2 大学生利用C++构建一个完整的操作系统之响应键盘中断
myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动

1. 按键中断

​计算机自制操作系统(十六):中断—键盘驱动 - 知乎 (zhihu.com)​

在中断​​IDT​​​ 中 ​​256​​​个中断源产生的中断在IDT表中注册时, 全部指向了同一个中断服务程序 ​​class InterruptHandler​​, 其实是不准确的, 因为并没有建立各自中断号和中断服务程序之间的一一对应关系, 所以接下来利用按键中断进行演示, 展示通过按键类继承中断服务器程序来实现按键的实际中断操作;

// keyboard.h
class KeyBoardDriver: public InterruptHandler { // 这里面继承中断服务程序, 在键盘初始化时
public:
KeyBoardDriver(InterruptManager *manager);
~KeyBoardDriver();
virtual uint32_t HandleInterrupt(uint32_t esp); // 继承与中断管理器中的中断处理方法
private:
Port8Bit dataport; // 数据端口
Port8Bit commandport; // 命令端口
};

1.1 键盘中断的初始化

myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_操作系统

键盘中断的具体流程就是当我们点击键盘时, 键盘上面的芯片8048会把键盘扫描码发送给​​主板上的8042​​, 而8042是按照字节码处理数据, 并存储到 ​​输出缓冲区​​ , 然后8042会给中断代理 8059A发送中断信号, 这样中断控制器通过读取8042的输出缓冲区寄存器, 获得键盘的扫描码; 总的来讲, 操作系统其实是在对8042这个芯片进行数据的操作(读写)来识别中断的


myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_c++_02

键盘中断通过主8259A的IRQ1触发, 而CPU通过中断向量号来寻址待执行的中断代码

myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_c++_03myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_初始化_04

如此相当于在中断描述符表中注册了键盘中断对应的向量, 然后在重写中断处理函数​​HandleInterrupt​​就能实现按键中断

剩下的端口定义以及对端口的读写, 见​​i8042 键盘控制器-------详细介绍 - LinKArftc

同时还需要有一个知识点 电脑键盘的通码与断码 :

类型:通码(make code)和断码(break code)。

  1. 当一个键被按下或持续按住时,键盘会将该键的通码发送给主机;
  2. 而当一个键被释放时,键盘会将该键的断码发送给主机。

根据键盘按键扫描码的不同,在此可将按键分为如下几类:

  1. 第一类按键,通码为1字节, 断码为myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_操作系统_05。如A键,其通码为myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_操作系统_06,断码为myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_初始化_07
  2. 第二类按键,通码为2字节,$ 0xE0 + 0xNN$形式,断码为myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_初始化_08形式。如 ​​​right ctrl​​​键,其通码为myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_服务程序_09,断码为myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_服务程序_10,
  3. 第三类特殊按键有两个,​​print screen​​​键通码为myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_初始化_11,断码为 myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_服务程序_12; ​​​pause​​​键通码为myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_c++_13; 断码为空。

通码断码表如下:

myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_操作系统_14

根据上述知识点, 可以获得下面的对应端口读写来初始化键盘 :

// keyboard.cpp
#include "drivers/keyboard.h"
KeyBoardDriver::KeyBoardDriver(InterruptManager* manager)
: InterruptHandler(0x01 + manager->HardwareInterruptOffset(), manager),
dataport(0x60), // 这里设定键盘控制器的数据端口
commandport(0x64){ // 这里设定键盘控制器的命令端口
while(commandport.Read() & 0x01){ // 清空键盘的input_buffer
dataport.Read();
}
commandport.Write(0xae); // 激活键盘
commandport.Write(0x20); // 8042芯片, 要读取一个字节
uint8_t status = (dataport.Read() | 1) & ~0x10; // 开启键盘中断
commandport.Write(0x60); // 告诉控制器, 我要开始写入了
dataport.Write(status);
dataport.Write(0xf4);
printf("finish keyboard init ! \n");
}

1.2 键盘中断的处理函数

这是一个简单的键盘中断的处理函数, 打印不同按键被按下和弹起后, 8042芯片所读到的字节码printf到屏幕上

// 这里会产生两次按键中断, 是因为啥呢, 是因为啥呢, 是因为按下和弹起
uint32_t KeyBoardDriver::HandleInterrupt(uint32_t esp) {
printf("have keyboard Interrupt !!! \n");
uint8_t key = dataport.Read();
char* foo = (char*)"KEYBOARD 0X00 ";
const char* hex = "0123456789ABCDEF";
foo[11] = hex[(key >> 4) & 0x0f];
foo[12] = hex[key & 0x0f];
printf((const char*)foo);
printf("\n");
return esp;
}

1.3 结果

myos3 大学生利用C++构建一个完整的操作系统之代码重构并实现键盘打字和鼠标移动_服务程序_15

2. 鼠标中断

​源码见我的gitee​

2.1 鼠标中断初始化

其实鼠标中断和键盘中断是一样的, 都是通过对寄存器进行读写, 只不过鼠标中断的控制器在每次读写时, 还有三个字节的数据, 鼠标每一次动作都是3个字节数据,为什么是3个。想想也知道:两个坐标,一个状态;

#ifndef __MOUSE_H__
#define __MOUSE_H__

#include "common/types.h"
#include "hardware/interrupts.h"
#include "hardware/port.h"

using namespace myos;
using namespace myos::common;
using namespace myos::hardware;

// 鼠标中断是IQ2
class MouseDriver: public InterruptHandler {
public:
MouseDriver(InterruptManager *manager);
~MouseDriver();
virtual uint32_t HandleInterrupt(uint32_t esp); // 继承与中断管理器中的中断处理方法
private:
Port8Bit dataport; // 数据端口
Port8Bit commandport; // 命令端口
uint8_t buffer[3];
// 每次mouse读取数据是数据流,
// 两种模式: 三个字节和四个字节的包, 这里是使用的是三个字节的包
// xy的信息和左右中拿个按下的信息, x移动的距离, y移动的距离
// 触发一次中断, 读取一个字节
uint8_t offset; // 读取的哪个字节
uint8_t buttons; // 哪个按键被按下

int8_t x, y; // 鼠标位置初始化
};

这里面的初始化设置中间点显示白色

MouseDriver::MouseDriver(InterruptManager* manager) 
: InterruptHandler(0x0C + manager->HardwareInterruptOffset(), manager),
dataport(0x60), // 这里设定键盘鼠标控制器的数据端口
commandport(0x64), // 这里设定键盘鼠标控制器的命令端口
offset(0),
buttons(0),
x(40), y(12){ // 默认鼠标在中间

uint16_t* VideoMemory = (uint16_t*)0xb8000;
VideoMemory[y * 80 + x] = ((VideoMemory[y * 80 + x] & 0xf000) >> 4) |
((VideoMemory[y * 80 + x] & 0x0f00) << 4) |
(VideoMemory[y * 80 + x] & 0x00ff); // 让中间的点变白

commandport.Write(0xa8);
commandport.Write(0x20);
uint8_t status = (dataport.Read() | 2) & ~0x20; // 按位设置开启鼠标
commandport.Write(0x60); // 告诉我们要些数据了
dataport.Write(status);
commandport.Write(0xd4); // 写入鼠标寄存器写数据了
dataport.Write(0xf4); // 告诉键盘或者数据开启鼠标
dataport.Read(); // 读取数据流
printf("finish mouse init ! \n");
}

2.2 中断处理函数

​​鼠标的中断处理函数​​ 根据每次read到的三个字节的数据, 进行xy位置以及不同状态的显示

uint32_t MouseDriver::HandleInterrupt(uint32_t esp) {
uint8_t status = commandport.Read();
if (!(status & 0x20)) return esp; // 不是鼠标就直接返回esp

buffer[offset] = dataport.Read(); // 先读一个数据
offset = (offset + 1) % 3; // offset偏移一位

// offset为0, 则表示x, y操作过了, 只需要控制一下x,y即可
if (offset == 0) {
uint16_t* VideoMemory = (uint16_t*)0xb8000;
VideoMemory[y * 80 + x] = ((VideoMemory[y * 80 + x] & 0xf000) >> 4) |
((VideoMemory[y * 80 + x] & 0x0f00) << 4) |
(VideoMemory[y * 80 + x] & 0x00ff);

x += buffer[1];
if (x < 0) x = 0;
else if (x >= 80) x = 79;

y -= buffer[2];
if (y < 0) y = 0;
else if (y >= 25) y = 24;

VideoMemory[y * 80 + x] = ((VideoMemory[y * 80 + x] & 0xf000) >> 4) |
((VideoMemory[y * 80 + x] & 0x0f00) << 4) |
(VideoMemory[y * 80 + x] & 0x00ff);

for (uint8_t i = 0; i < 3; i++) {
// 判断buf是否是按键按下, 如果按下就进行翻转一下, 这里只需要判断buffer[0]的下三位表示左中右,
if ((buffer[0] & (1 << i)) != (buttons & (1 << i))) {
VideoMemory[y * 80 + x] = ((VideoMemory[y * 80 + x] & 0xf000) >> 4) |
((VideoMemory[y * 80 + x] & 0x0f00) << 4) |
(VideoMemory[y * 80 + x] & 0x00ff);
}
}
buttons = buffer[0];
}
return esp;
}


标签:鼠标,中断,dataport,C++,键盘,myos3,80,VideoMemory
From: https://blog.51cto.com/u_15888063/5882652

相关文章

  • mycompiler1 大学生利用C++构建一个编译器之词法分析器
    文章目录​​1.定义语言​​​​2.编译器工作流程​​​​2.1.编译器处理的两大过程和分层设计​​​​3.词法分析器的实现​​​​3.1.有限状态机(正则匹配)​​​​3......
  • myos2 大学生利用C++构建一个完整的操作系统之响应键盘中断
    文章目录​​1.类型统一types​​​​2.IO读写之port​​​​2.1通过C语言利用汇编指令对IO的读写控制​​​​2.2port8bit的定义和实现​​​​3.全局描述之GDT​​......
  • myos1 大学生利用C++构建一个完整的操作系统打印helloworld
    文章目录​​1.工具预备​​​​1.1Ubuntu涉及到的编译工具​​​​1.2VScode涉及到的插件​​​​1.3virtualBox创建一个新的空虚拟机​​​​2.文件目录​​​​3.......
  • 【iOS-Cocos2d游戏开发之十二】浅析使用C++/C/OC进行iOS游戏混编出现“failed with ex
    ​​​李华明Himi ​​​​原创   大家都知道Xcode中支持C、C++、Object-C3种语言的混编,在上一节Box2d中介绍过cocos2d封装的box2d是c++源码实现的,那么如果想让编译器......
  • c++ 代码模板
    #include<bits/stdc++.h>usingnamespacestd;typedefunsignedlonglongull;typedeflongdoubledoubleL;typedeflonglongll;#define_SILENCE_CXX20_CISO646......
  • c++类的二进制组件复用
    本文内容是对EssntialCOM一书中第一章内容的总结,该章内容很好的阐述了c++类的二进制复用所需要面对的一些问题及解决方案。我在使用c++开发模块中,使用了以下几种分发方......
  • windows--cmake与c++的使用教程(14)
    1概述本文基于前文环境本节目标:target_include_directories用法2作用target_include_directories的作用,用于给固定目标指定头文件搜索路径。moderncmake之......
  • C++零基础入门学习路线图
    C++入门学习路线图分为三阶段:C++基础入门、C++核心编程、C++提高编程。以下学习路线图参考B站黑马程序员《匠心精作C++从0到1入门编程》C++基础入门 1C++初识 ......
  • C++全栈开发学习路线图
    C语言基础与提高 C语言基础 指针、内存管理 变量、条件、字符串、数组、函数、结构体 C语言提高 多级指针的使用 接口的封......
  • 数据结构(二):括号匹配(C++,栈)
    好家伙,写题,题目代码在最后 来吧,  1.栈 栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一......