字符从键盘输入到在显示器显示出来的详细过程

曾经有一段时间,一直想搞清楚,当我敲下键盘时,os到底是做了些什么,让我能看到一个字符被显示出来。
现把过程总结一下,消除一下和我有同样感觉的孩子们。
 
1)首先知道一下键盘的构成;6位的计数器经过两个八选一译码器对键盘进行扫描 在键盘没有任何按键时,扫描随着计数器的循环计数反复进行。
可以这么想,扫描就是一束光线,当没有键被按下时,光线可以横穿过键盘的任何位置。
2)若有键按下,扫描会发现该信号(键被按下以后挡住了光线的横穿),该信号使计数器停止计数,将扫描码发送给 8042。
每一个键的位置都用一组数字表示,这组数字就是所谓的扫描码。
你可能好奇,扫描码怎么就传给8042了? 其实特简单,就是直接用电路联通的。
3)8042将该扫描码放在缓冲区中,并告诉8259A产生了中断。
缓冲区大小为1byte,如果此时键盘又有新的键按下,8042将不再接收,一直等到缓冲区被清理。
4)8259A识别出键盘中断,将cpu的中断寄存器的特定位置为1。
注意到目前为止cpu一直再执行其它程序或者空闲,根本还对键盘按下的事件没做任何反应。虽然描述的比较复杂,但这个过程其实特快。
5)接下来就是典型的中断处理过程了。 cpu执行完一条指令后,发现有中断存在,进入中断周期,通过IDT的映射将其处理交给键盘处理程序。
 
6)键盘处理程序第一步就是从8042的缓冲区(仅有一个字节)将该扫描码移动到键盘缓冲区(链表实现的 大小可调整,太大了也没什么用),此时可以接受下一个字符按键了。

7)若键盘缓冲区非空,则开始处理这些按键码,首先将扫描码转换为ASCII码
若是可打印字符如a 则将其显示
其他功能键做相应处理。

最后就是显示的过程了:
可通过bios调用或者写显存实现。
1 将该字符的ASCII码加上一些属性(如颜色控制,最简单的rgb,此时只有最纯的rgb三种颜色及其叠加的组合共8种),这些属性也是由01表示的,0代表无,1代表有 放入显存中(集显就是内存中的特定位置 )。

2 字符发生器,通过读这个特定位置的数据,将ASCII码及其属性信息转换为一个光点矩阵,1表示有光。
字符发生器实际是一个rom,用映射的功能实现这种转换。
如ASCII = 65, 字符发生器会把其转换为a的光点矩阵,颜色由后面的3把枪控制。

3 该光点矩阵可以作为显示器的输入信号。每个点都有3把电子枪发射(分别发rgb)的光叠加成的。
 
 
在此放一些关于键盘的比较关键的程序
有兴趣的可以找我讨论
 

task_table 中增加一项

PUBLIC TASK task_table[NR_TASKS]= {

{task_tty, STACK_SIZE_TTY, "tty"}};

/*======================================================================*

task_tty

*======================================================================*/

PUBLIC void task_tty()

{

TTY* p_tty;

init_keyboard(); //初始化键盘缓冲区,指定中断号和其对应的中断处理程序,打开该中断

for(p_tty=TTY_FIRST;p_tty

init_tty(p_tty); //tty和其console对应,初始化屏幕

}

select_console(0);

while(1) {

for(p_tty=TTY_FIRST;p_tty

tty_do_read(p_tty);

tty_do_write(p_tty);//从对应的console缓冲区中读出字符,并将其放入显存

}

}

}

/*======================================================================*

init_tty

*======================================================================*/

PRIVATE void init_tty(TTY* p_tty)

{

p_tty->inbuf_count= 0;

p_tty->p_inbuf_head= p_tty->p_inbuf_tail = p_tty->in_buf;

init_screen(p_tty);

}

/*======================================================================*

tty_do_read

*======================================================================*/

PRIVATE void tty_do_read(TTY* p_tty)

{

if(is_current_console(p_tty->p_console)) {//若为当前控制台则

keyboard_read(p_tty);//将键盘的扫描码从缓冲区读出来并解析为用户真正想要的输入,然后调用in_process(key)对输入进行处理

}

}

/*======================================================================*

tty_do_write

*======================================================================*/

PRIVATE void tty_do_write(TTY* p_tty)

{

if(p_tty->inbuf_count) {

charch = *(p_tty->p_inbuf_tail);

p_tty->p_inbuf_tail++;

if(p_tty->p_inbuf_tail == p_tty->in_buf + TTY_IN_BYTES) {

p_tty->p_inbuf_tail= p_tty->in_buf;

}

p_tty->inbuf_count--;

out_char(p_tty->p_console, ch); //将一个字符放在显存中的对应位置,此函数在console .c中

}

}

/*======================================================================*

/*======================================================================*

in_process

*======================================================================*/

PUBLIC void in_process(TTY* p_tty, u32 key)

{

char output[2] = {'\0', '\0'};

if (!(key & FLAG_EXT)) {

put_key(p_tty,key);

}

else {

int raw_code = key &MASK_RAW;

switch(raw_code) {

case ENTER:

put_key(p_tty,'\n');

break;

case BACKSPACE:

put_key(p_tty,'\b');

break;

case UP:

if ((key &FLAG_SHIFT_L) || (key & FLAG_SHIFT_R)) {

scroll_screen(p_tty->p_console,SCR_DN);

}

break;

caseDOWN:

if((key & FLAG_SHIFT_L) || (key & FLAG_SHIFT_R)) {

scroll_screen(p_tty->p_console,SCR_UP);

}

break;

caseF1:

caseF2:

caseF3:

caseF4:

caseF5:

caseF6:

caseF7:

caseF8:

caseF9:

case F10:

caseF11:

caseF12:

/*Alt + F1~F12 */

if((key & FLAG_ALT_L) || (key & FLAG_ALT_R)) {

select_console(raw_code - F1);//选择控制台

}

break;

default:

break;

}

}

}

/*======================================================================*

put_key

*======================================================================*/

PRIVATE void put_key(TTY* p_tty, u32 key)

{

if(p_tty->inbuf_count < TTY_IN_BYTES) {

*(p_tty->p_inbuf_head)= key;

p_tty->p_inbuf_head++;

if(p_tty->p_inbuf_head == p_tty->in_buf + TTY_IN_BYTES) {

p_tty->p_inbuf_head= p_tty->in_buf;

}

p_tty->inbuf_count++;

}

}

tty_write

*======================================================================*/

PUBLIC void tty_write(TTY* p_tty, char*buf, int len)

{

char* p = buf;

int i = len;

while (i) {

out_char(p_tty->p_console,*p++);

i--;

}

}

/*======================================================================*

sys_write

*======================================================================*/

PUBLIC int sys_write(char* buf, int len,PROCESS* p_proc)

{

tty_write(&tty_table[p_proc->nr_tty], buf, len);

return 0;

}

Keyboard 的内容

/*======================================================================*

init_keyboard

*======================================================================*/

PUBLIC void init_keyboard()

{

kb_in.count= 0;

kb_in.p_head= kb_in.p_tail = kb_in.buf;

shift_l = shift_r = 0;

alt_l = alt_r = 0;

ctrl_l = ctrl_r = 0;

caps_lock = 0;

num_lock = 1;

scroll_lock= 0;

set_leds();

put_irq_handler(KEYBOARD_IRQ, keyboard_handler);/*设定键盘中断处理程序*/

enable_irq(KEYBOARD_IRQ); /*开键盘中断*/

}

/*======================================================================*

keyboard_handler 读入到键盘缓冲区

*======================================================================*/

PUBLIC void keyboard_handler(int irq)

{

u8scan_code = in_byte(KB_DATA);

if(kb_in.count < KB_IN_BYTES) {

*(kb_in.p_head)= scan_code;

kb_in.p_head++;

if(kb_in.p_head == kb_in.buf + KB_IN_BYTES) {

kb_in.p_head= kb_in.buf;

}

kb_in.count++;

}

}

/*======================================================================*

keyboard_read

*======================================================================*/

PUBLIC void keyboard_read(TTY* p_tty)

{

u8 scan_code;

char output[2];

int make; /*1: make; 0: break. */

u32 key = 0;/* 用一个整型来表示一个键。比如,如果 Home 被按下,

* key 值将为定义在keyboard.h 中的 'HOME'

*/

u32* keyrow; /*指向 keymap[] 的某一行 */

if(kb_in.count> 0){

code_with_E0= 0;

scan_code= get_byte_from_kbuf();

/* 下面开始解析扫描码 */一部分省略了

if(scan_code == 0xE1) {

inti;

u8pausebrk_scode[] = {0xE1, 0x1D, 0x45,

0xE1, 0x9D, 0xC5};

intis_pausebreak = 1;

for(i=1;i<6;i++){

if(get_byte_from_kbuf() != pausebrk_scode[i]) {

is_pausebreak= 0;

break;

}

}

if(is_pausebreak) {

key= PAUSEBREAK;

}

}

elseif (scan_code == 0xE0) {

scan_code= get_byte_from_kbuf();

/*PrintScreen 被按下 */

if(scan_code == 0x2A) {

if(get_byte_from_kbuf() == 0xE0) {

if(get_byte_from_kbuf() == 0x37) {

key= PRINTSCREEN;

make= 1;

}

}

}

/*PrintScreen 被释放 */

if(scan_code == 0xB7) {

if(get_byte_from_kbuf() == 0xE0) {

if(get_byte_from_kbuf() == 0xAA) {

key= PRINTSCREEN;

make= 0;

}

}

}

/*不是PrintScreen,此时scan_code0xE0紧跟的那个值. */

if(key == 0) {

code_with_E0= 1;

}

}

if((key != PAUSEBREAK) && (key != PRINTSCREEN)) {

/*首先判断Make Code 还是 Break Code */

make= (scan_code & FLAG_BREAK ? 0 : 1);

……..

default:

break;

}

in_process(p_tty, key);

}

}

}

}

/*======================================================================*

get_byte_from_kbuf

*======================================================================*/

PRIVATE u8 get_byte_from_kbuf() /* 从键盘缓冲区中读取下一个字节 */

{

u8 scan_code;

while (kb_in.count <= 0) {} /* 等待下一个字节到来 */

disable_int();

scan_code = *(kb_in.p_tail);

kb_in.p_tail++;

if (kb_in.p_tail == kb_in.buf + KB_IN_BYTES) {

kb_in.p_tail = kb_in.buf;

}

kb_in.count--;

enable_int();

returnscan_code;

}

 

你可能感兴趣的:(一个os的实现)