在上一篇,我讲了键盘操作会产生扫描码以及如何解析Pause键和Print Screen键的扫描码。

在这一篇,我会说清楚”键盘上的输入为什么会出现在显示器上“。

极简版

  1. 我们敲击键盘,产生扫描码。
  2. 操作系统获取扫描码,把扫描码解析成ASCII码。
  3. 操作系统把ASCII码写入显存,显示器上就会打印出显存中的可打印字符。

解析扫描码

在上一篇,我建立了一个Make Code和按键ASCII码(部分按键如Enter是我自己设置的值)的映射数组keyboardMap

按键不是Pause,也不是Print Screen,就进入下面的解析流程。

默认键值

  1. 当接收到的扫描码只有一个,
  2. 扫描码的类型是Make Code,
  3. 这个Make Code的值是V
  4. 那么键值是keyboardMap[V *3]

另一个键值

  1. 接收到的第一个扫描码是Make Code,值是V
  2. keyboardMap中查询出被按下的键是left_shiftright_shift
  3. 设置column的值是1。
  4. 接收到第二个扫描码是Make Code,值是N
  5. 从映射数组中查询这个扫描码对应的键值是:keyboardMap[N * 3 + column]

注意,查询到的键值是keyboardMap[N * 3 + column]。这就是键盘上的1A这类按键与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 + f1shift + 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位分别是: 0000b1010b。它们分别设置字符的背景色和前景色。

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;

全局视角下的流程

  1. tty任务和其他用户进程(TestA等)一起初始化,并使用restart运行tty任务。
  2. tty任务的流程如下:
    1. tty任务遍历所有的tty窗口。
    2. 如果被遍历到的tty窗口是当前tty,从C1缓冲区读取数据D。
    3. 把D转换成ASCII码,然后交给写显存模块打印到显示器。
  3. 发生时钟中断,给tty任务建立快照,调度模块让用户进程上CPU运行。
  4. 用户敲击键盘,8048监测到键盘操作,读取扫描码(第2套),传输给8042。
  5. 8042把扫描码(第2套)转换成第1套,放入键盘缓冲区C2,通知8259A发生键盘中断。
    1. 在C2中的数据被取走前,8042不再接收新数据。
  6. 键盘中断例程save当前进程,从C2读取数据,然后放入C1。
  7. tty任务在某个时刻获得CPU控制权,执行第2步的流程。

键盘--扫描码--ASCII码--显示器上的字符的更多相关文章

  1. 在线获取键盘按键值(ascii码)工具

    在线获取键盘按键值(ascii码)工具 http://www.bejson.com/othertools/keycodes/ 可以根据输入的值获取对应的键盘ascii码值

  2. 键盘对应的ASCII码

    ESC键 VK_ESCAPE (27)回车键: VK_RETURN (13)TAB键: VK_TAB (9)Caps Lock键: VK_CAPITAL (20)Shift键: VK_SHIFT ($ ...

  3. Oracle学习笔记:ASCII码转换(chr和ascii函数)

    今天get到一个骚操作,通过ascii码转换之后来进行互换编码的. select chr(ascii('f') + ascii('m') - ascii('a')) from dual; 有必要对as ...

  4. C#单纯的字母数字ASCII码转换

    字母转换成数字 byte[] array = new byte[1];   //定义一组数组array            array = System.Text.Encoding.ASCII.Ge ...

  5. C语言之ASCII码

    ASCII码 ASCII码值在65~90之间,为大写字母.ASCII码值在97~122之间,为小写字母.ASCII码值在48~57之间,为数字.ASCII码值不在上述3个范围内,为特殊字符.

  6. C#字母转换成数字/数字转换成字母 - ASCII码转换

    字母转换成数字 byte[] array = new byte[1];   //定义一组数组arrayarray = System.Text.Encoding.ASCII.GetBytes(strin ...

  7. 键盘上各键对应的ASCII码与扫描码

    键盘上各键对应的ASCII码与扫描码 vbKeyLButton 0x1 鼠标左键vbKeyRButton 0x2 鼠标右键vbKeyCancel 0x3 CANCEL 键vbKeyMButton 0x ...

  8. <转>键盘扫描码

    原文链接:http://www.cnblogs.com/wqw/archive/2009/08/30/1556618.html //以下是一个检测按键扫描码的程序 #i nclude <bios ...

  9. c语言-键盘扫描码

    定义: 键盘上的每一个键都有两个唯一的数值进行标志.为什么要用两个数值而不是一个数值呢?这是因为一个键可以被按下,也可以被释放.当一个键按下时,它们产生一个唯一的数值,当一个键被释放时,它也会产生一个 ...

随机推荐

  1. codeforces 903D

    D. Almost Difference time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  2. 图片转tfrecords

    import numpy as np import tensorflow as tf import time import os import cv2 from sklearn.utils impor ...

  3. ASP.Net MVP Framework had been dead !

    ASP.Net MVP Framework Project Description A project to get you started with creating and designing w ...

  4. js console 性能测试 & don't-use-array-foreach-use-for-instead

    don't-use-array-foreach-use-for-instead slower https://coderwall.com/p/kvzbpa/don-t-use-array-foreac ...

  5. How to build a sortable table in native js?

    How to build a sortable table in native/vanilla js? H5 DnD https://developer.mozilla.org/zh-CN/docs/ ...

  6. Keep Fitness

    Keep Fitness 健身 keep health 训练流程 Part 1 热身 5-10分钟 Part 2 肌肉力量训练 30分钟 大肌群包括:胸.背.腿.臀: 小肌群包括:肩.二头肌.三头肌. ...

  7. web development all in one

    web development all in one https://javascript.xgqfrms.xyz/web-development-all-in-one.html refs https ...

  8. how to disabled prefers-color-scheme in js & dark theme

    how to disabled prefers-color-scheme in js dark theme https://developer.mozilla.org/en-US/docs/Web/C ...

  9. umi

    umi https://github.com/umijs/umi https://umijs.org/zh/guide/ dva https://github.com/dvajs/dva https: ...

  10. React Hooks & react forwardRef hooks & useReducer

    React Hooks & react forwardref hooks & useReducer react how to call child component method i ...