键盘--扫描码--ASCII码--显示器上的字符
在上一篇,我讲了键盘操作会产生扫描码以及如何解析Pause键和Print Screen键的扫描码。
在这一篇,我会说清楚”键盘上的输入为什么会出现在显示器上“。
极简版
- 我们敲击键盘,产生扫描码。
- 操作系统获取扫描码,把扫描码解析成ASCII码。
- 操作系统把ASCII码写入显存,显示器上就会打印出显存中的可打印字符。
解析扫描码
在上一篇,我建立了一个Make Code和按键ASCII码(部分按键如Enter是我自己设置的值)的映射数组keyboardMap。
按键不是Pause,也不是Print Screen,就进入下面的解析流程。
默认键值
- 当接收到的扫描码只有一个,
- 扫描码的类型是Make Code,
- 这个Make Code的值是
V, - 那么键值是
keyboardMap[V *3]。
另一个键值
- 接收到的第一个扫描码是Make Code,值是
V。 - 从
keyboardMap中查询出被按下的键是left_shift或right_shift。 - 设置
column的值是1。 - 接收到第二个扫描码是Make Code,值是
N。 - 从映射数组中查询这个扫描码对应的键值是:
keyboardMap[N * 3 + column]。
注意,查询到的键值是keyboardMap[N * 3 + column]。这就是键盘上的1、A这类按键与shift键组合时的值,即默认值外的另一个值。
提问
请想一想,点击shift + A键,读取扫描码的过程是什么样的?
Enter
Enter键和Backspace键很容易解析。获取扫描码(Make Code)S,获取键值keyboardMap[N * 3]。
根据键值识别出是Enter键后就把光标设置到下一行。
识别出是Backspace就这样处理最新的两个字节:将高字节设置成0Fh,将低字节设置成空字符的ASCII码;另外,将光标的位置后退两个字符。
Caps Lock等键
没有弄明白。
清屏
使用linux的命令行、显示器上打印满了字符,我们可以使用clear让命令行终端的字符全部消失。让屏幕上的字符全部消失,这就是清屏。
clear命令只是移动了光标的位置,并没有清除屏幕上的字符。按下方向键中的Up能定位到已经消失的字符。
我们的清屏,是让字符彻底从屏幕上消失,无论怎么按Up键都不会再看到字符。
清屏其实很简单,就是往写满字符的显存区域写入空字符。
tty
什么是tty
我觉得tty是一个比较过时的功能,我几乎没用过。
想体验一番tty是什么的同学,可以在安装了linux系统的电脑或虚拟机上按下shift + alt + f1或shift + alt + f2键在不同的tty之间切换。下面是我在虚拟机上切换tty的效果,一个是图形界面,一个是纯命令行界面。
VGA
我只讲述VGA模式下的tty。
如上图所示,不同的tty窗口展示的内容完全不相同,在tty0窗口敲击键盘,数据只会出现在tty0的显示器;切换到tty1后,显示器上不会出现刚刚敲击键盘的数据;反过来也是一样。
不同tty之间,是完全隔离的,只是公用一个键盘。
VGA是什么呢?我觉得弄清楚这个概念的方方面面没有更多的作用。对VGA,了解有限的下面这些有限的知识就够了。
字符显示
在这种模式下,显示器一共能显示25行字符,每行80个字符。
每个字符占用2个字节,高字节是字符的颜色,低字节是字符的ASCII码。例如
mov ah, 0Ah
mov al, 'A'
mov ah, 0Ah 中的 0Ah 的高4位和低4位分别是: 0000b 和 1010b。它们分别设置字符的背景色和前景色。
mov al, 'A' 中的 A 是要打印的字符。
寄存器
VGA模式下的显示器是一个硬件,提供了多组寄存器。
读写这些寄存器,能设置光标的位置、点击Up等方向键能滚屏。
读写这类寄存器,需先往一类寄存器中写入另一类寄存器的索引,即往另一类寄存器的第N个寄存器中写入数据;然后往第二类寄存器写入数据,不需要再指定具体是哪个寄存器。
tty的实现
显示器上的内容是显存中的数据的映射。要让不同的tty窗口打印不同的内容,只需把显存分割成若干块,每个tty分配一块。
显示器一满屏所需空间是80 * 25 * 2 = 4000个字节。显存的内存地址是0xB8000~~~0xBFFFF,总计0xBFFFF - 0xB8000 + 1 = 0x8000 = 32768字节。如果实现3个tty,每个tty对应的显存大小大概是32768 / 3 = 10922个字节,能存储两”满屏“数据。
伪代码
每个tty设计一个缓冲区C1,数据从键盘缓冲区C2到这个缓冲区,然后再从C1读取数据写入对应tty的显存区域。
tty对应的显存区域,也设计一个结构来存储,叫Console。
直接看伪代码吧。
typedef struct{
// tty使用的显存的开始位置
int original_address;
// tty使用的显存的大小
int limit;
// tty使用显存的当前位置
int current_address;
// tty的光标位置
int cursor_address;
}Console;
typedef struct{
// 缓冲区下一个要处理的字符的
int tail;
// 缓冲区下一个空闲位置
int head;
// 缓冲区存储的数据的数量
int count;
// 存储数据的数组
int buff[256];
Console * console;
}TTY;
全局视角下的流程
- tty任务和其他用户进程(TestA等)一起初始化,并使用
restart运行tty任务。 - tty任务的流程如下:
- tty任务遍历所有的tty窗口。
- 如果被遍历到的tty窗口是当前tty,从C1缓冲区读取数据D。
- 把D转换成ASCII码,然后交给写显存模块打印到显示器。
- 发生时钟中断,给tty任务建立快照,调度模块让用户进程上CPU运行。
- 用户敲击键盘,8048监测到键盘操作,读取扫描码(第2套),传输给8042。
- 8042把扫描码(第2套)转换成第1套,放入键盘缓冲区C2,通知8259A发生键盘中断。
- 在C2中的数据被取走前,8042不再接收新数据。
- 键盘中断例程
save当前进程,从C2读取数据,然后放入C1。 - tty任务在某个时刻获得CPU控制权,执行第2步的流程。
键盘--扫描码--ASCII码--显示器上的字符的更多相关文章
- 在线获取键盘按键值(ascii码)工具
在线获取键盘按键值(ascii码)工具 http://www.bejson.com/othertools/keycodes/ 可以根据输入的值获取对应的键盘ascii码值
- 键盘对应的ASCII码
ESC键 VK_ESCAPE (27)回车键: VK_RETURN (13)TAB键: VK_TAB (9)Caps Lock键: VK_CAPITAL (20)Shift键: VK_SHIFT ($ ...
- Oracle学习笔记:ASCII码转换(chr和ascii函数)
今天get到一个骚操作,通过ascii码转换之后来进行互换编码的. select chr(ascii('f') + ascii('m') - ascii('a')) from dual; 有必要对as ...
- C#单纯的字母数字ASCII码转换
字母转换成数字 byte[] array = new byte[1]; //定义一组数组array array = System.Text.Encoding.ASCII.Ge ...
- C语言之ASCII码
ASCII码 ASCII码值在65~90之间,为大写字母.ASCII码值在97~122之间,为小写字母.ASCII码值在48~57之间,为数字.ASCII码值不在上述3个范围内,为特殊字符.
- C#字母转换成数字/数字转换成字母 - ASCII码转换
字母转换成数字 byte[] array = new byte[1]; //定义一组数组arrayarray = System.Text.Encoding.ASCII.GetBytes(strin ...
- 键盘上各键对应的ASCII码与扫描码
键盘上各键对应的ASCII码与扫描码 vbKeyLButton 0x1 鼠标左键vbKeyRButton 0x2 鼠标右键vbKeyCancel 0x3 CANCEL 键vbKeyMButton 0x ...
- <转>键盘扫描码
原文链接:http://www.cnblogs.com/wqw/archive/2009/08/30/1556618.html //以下是一个检测按键扫描码的程序 #i nclude <bios ...
- c语言-键盘扫描码
定义: 键盘上的每一个键都有两个唯一的数值进行标志.为什么要用两个数值而不是一个数值呢?这是因为一个键可以被按下,也可以被释放.当一个键按下时,它们产生一个唯一的数值,当一个键被释放时,它也会产生一个 ...
随机推荐
- 实战交付一套dubbo微服务到k8s集群(4)之dubbo微服务底包镜像制作
1.下载jre镜像 在运维主机(mfyxw50.mfyxw.com)操作 [root@mfyxw50 ~]# docker pull registry.cn-hangzhou.aliyuncs.com ...
- java中string,stringBuffer和StringBuider
最近学习到StringBuffer,心中有好些疑问,搜索了一些关于String,StringBuffer,StringBuilder的东西,现在整理一下. 关于这三个类在字符串处理中的位置不言而喻,那 ...
- .NET Core项目自动化测试和代码覆盖率审查
这篇文章给大家分享一下,如何配置.NET Core项目自动化测试和代码覆盖率审查. 基本知识,请参考这里: https://docs.microsoft.com/en-us/dotnet/core/t ...
- 手撕 part1
1.宏定义三个数最大值 挺有意思 max((a), (b), (c)) (a) > (b)? ((a) > (c)? (a) : (c)) ((b) > (c)? (b) : (c) ...
- 攻防世界-Web-lottery(.git泄露、php源码审计、弱类型利用)
扫描目录,发现.git泄露: 提取.git泄露的源码,得到许多文件: 网站这里: 这就要审计一下代码,找找漏洞了. 经过一番审计,猜数字对应的函数在api.php中: 我们要绕过这个$win_numb ...
- 力扣485. 最大连续1的个数-C语言实现-简单题
题目 [题目传送门] 给定一个二进制数组, 计算其中最大连续1的个数. 示例 1: 输入: [1,1,0,1,1,1] 输出: 3 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3 ...
- pagehide event & sendBeacon
pagehide event & sendBeacon 通过 API 测试 pagehide 是否触发了 pagehide 不支持正常的 fetch 请求发送 pagehide 仅支持 sen ...
- Apple Watch Series 6 屏幕误触放大后无法还原问题和解决方案
Apple Watch Series 6 屏幕误触放大后无法还原问题和解决方案 shit Apple,只能放大,不能缩小! 解决方案 关闭缩放功能 https://support.apple.com/ ...
- Redis in Action : Redis 实战学习笔记
1 1 1 Redis in Action : Redis 实战学习笔记 1 http://redis.io/ https://github.com/antirez/redis https://ww ...
- learning all in one
learning learning all in one https://github.com/xgqfrms/learning/tree/gh-pages/GraphQL https://githu ...