字符从键盘输入到在显示器显示出来的详细过程
曾经有一段时间,一直想搞清楚,当我敲下键盘时,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<TTY_END;p_tty++) {
init_tty(p_tty); //将tty和其console对应,初始化屏幕
}
select_console(0);
while(1) {
for(p_tty=TTY_FIRST;p_tty<TTY_END;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_code为0xE0紧跟的那个值. */
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;
}