/*
* linux/kernel/keyboard.S
*
* (C) 1991 Linus Torvalds
*/
/*
* Thanks to Alfred Leung for US keyboard patches
* Wolfgang Thiel for German keyboard patches
* Marc Corsini for the French keyboard
*/
#include <linux/config.h> // 内核配置头文件
.text
.globl _keyboard_interrupt
/*
* these are for the keyboard read functions
*/
/* 键盘缓冲区长度 */
size = 1024 /* must be a power of two ! And MUST be the same
as in tty_io.c !!!! */
/* 缓冲队列结构中的偏移量 */
head = 4 // 缓冲区头指针偏移量
tail = 8 // 缓冲区尾指针偏移量
proc_list = 12 // 等待该缓冲区进程偏移量
buf = 16 // 缓冲区偏移量
/*
* 定义变量mode,表示特殊键按下的状态
* cap,alt,ctrl,shift
*/
mode: .byte 0 /* caps, alt, ctrl and shift mode */
/*
* 数字锁定键,大小写转换键,滚动锁定键scroll-lock
* 定义变量leds
*/
leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */
/*
* 0xe0或者是0xe1指示气候跟随的是一个或者是两个字符扫描码
*/
e0: .byte 0
/*
* con_int is the real interrupt routine that reads the
* keyboard scan-code and converts it into the appropriate
* ascii character(s).
*/
_keyboard_interrupt: // 键盘中断处理程序入口点
/* 保存寄存器 */
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
push %ds
push %es
//////////////////////////////////////////////
// ds,es设置为内核数据段
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
//////////////////////////////////////////////
xorl %al,%al /* %eax is scan code, al = 0 */
inb $0x60,%al // 现在al寄存器就是中断之后读取的键盘扫描码
/////////////////////////////////////////////
cmpb $0xe0,%al // 如果扫描码是0xe0
je set_e0 // 跳转到set_e0
/////////////////////////////////////////////
cmpb $0xe1,%al // 如果是0xe1
je set_e1
/////////////////////////////////////////////
call key_table(,%eax,4) // 调用键处理程序key_table + %eax * 4
movb $0,e0 // 复位e0标志
////////////////////////////////////////////
// 下面的代码是使硬件复位处理。首先是禁止键盘的工作,然后开启
// 键盘工作
e0_e1: inb $0x61,%al
jmp 1f // 时间延迟
1: jmp 1f
1: orb $0x80,%al
jmp 1f
1: jmp 1f
1: outb %al,$0x61
jmp 1f
1: jmp 1f
1: andb $0x7F,%al
outb %al,$0x61
///////////////////////////////////////////
movb $0x20,%al // 发送中断结束信号
outb %al,$0x20
pushl $0 // 将控制台号tty = 0压入栈
call _do_tty_interrupt // 调用_do_tty_interrupt
addl $4,%esp // 丢弃入栈参数,并返回
///////////////////////////////////////////
// 恢复上下文
pop %es
pop %ds
popl %edx
popl %ecx
popl %ebx
popl %eax
///////////////////////////////////////////
iret // 中断返回
set_e0: movb $1,e0
jmp e0_e1
set_e1: movb $2,e0
jmp e0_e1
/*
* This routine fills the buffer with max 8 bytes, taken from
* %ebx:%eax. (%edx is high). The bytes are written in the
* order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.
*/
/* 下面的子程序将ebx:eax中最多8个字符添加入缓冲队列,知道eax = 0 */
/* --------------------------------------------- */
/* ebx (4 char) | eax (4 char) */
/* --------------------------------------------- */
put_queue:
pushl %ecx
pushl %edx
movl _table_list,%edx # read-queue for console,得到_table_list地址
movl head(%edx),%ecx // ecx = 缓冲队列头指针
1: movb %al,buf(%edx,%ecx) // 将al中的字符放置在缓冲队列头指针位置
incl %ecx
andl $size-1,%ecx // ecx指向缓冲队列最后
cmpl tail(%edx),%ecx # buffer full - discard everything?
// 头指针 == 尾指针
je 3f // 到3处继续执行,将后面未放入的字符全部抛弃
shrdl $8,%ebx,%eax
je 2f // 还有剩余的字符吗?没有,跳转2f
shrl $8,%ebx // ebx右移
jmp 1b // 还有剩余字符,跳转到1标号
///////////////////////////////////////
// 所有字符放入了队列 //
2: movl %ecx,head(%edx) // 保存头指针
movl proc_list(%edx),%ecx // 是否存在等待进程?
testl %ecx,%ecx
je 3f // 没有等待任务,跳转
/* task_struct结构第一个字段就是state */
movl $0,(%ecx) // 有,则置进程为可运行就绪状态
//////////////////////////////////////
//////////////////////////////////////
// 中断返回 //
3: popl %edx
popl %ecx
ret
/////////////////////////////////////
/* 根据ctrl或者是alt的扫描码,设置全局变量mode */
ctrl: movb $0x04,%al
jmp 1f
alt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: orb %al,mode
ret
/* 下面的代码设置ctrl或者alt键松开时扫描码,设置mode */
unctrl: movb $0x04,%al
jmp 1f
unalt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: notb %al
andb %al,mode
ret
/* 下面的代码根据扫描码,设置mode */
lshift: // 左shift按下
orb $0x01,mode
ret
unlshift: // 左shift松开
andb $0xfe,mode
ret
rshift: // 右shift按下
orb $0x02,mode
ret
unrshift: // 右shift松开
andb $0xfd,mode
ret
/* caps键标志 */
caps: testb $0x80,mode // 测试mode中的caps键是否按下?
jne 1f // 如果已经按下,中断返回
xorb $4,leds // 设置leds变量
xorb $0x40,mode // 设置mode中的位
orb $0x80,mode
/* 下面的代码是根据leds的表示,开启或者是关闭LED灯 */
set_leds:
call kb_wait // 等待键盘控制器输入缓冲空
movb $0xed,%al /* set leds command */
outb %al,$0x60
call kb_wait // 等待键盘控制器输入缓冲空
movb leds,%al // 取leds 标志,作为参数
outb %al,$0x60 // 发送
ret
/* caps键释放,设置mode值 */
uncaps: andb $0x7f,mode
ret
/* scroll键按下,设置leds值,并调用set_leds,重新显示led */
scroll:
xorb $1,leds
jmp set_leds
/* num-lock键 */
num: xorb $2,leds
jmp set_leds
/*
* curosr-key/numeric keypad cursor keys are handled here.
* checking for numeric keypad etc.
*/
/* 处理方向键/数字小键盘方向键 */
cursor:
subb $0x47,%al // 扫描码是小数字键盘上的键(其扫描码>=0x47)发出的?
jb 1f // 如果小于则不处理,返回
cmpb $12,%al // 如果扫描码 > 0x53
ja 1f // 不处理,返回
jne cur2 /* check for ctrl-alt-del */
testb $0x0c,mode // 有ctrl 键按下吗?
je cur2 // 无,则跳转
testb $0x30,mode // 有alt按下吗?
jne reboot // 无,则跳转reboot
cur2: cmpb $0x01,e0 /* e0 forces cursor movement */
/* e0 置位表示光标移动 */
je cur // 如果置位了,跳转到cur
testb $0x02,leds /* not num-lock forces cursor */
// 测试leds 中标志num-lock 键标志是否置位
je cur // 如果没有置位(num 的LED 不亮),则也进行光标移动处理
testb $0x03,mode /* shift forces cursor */
// 测试模式标志mode 中shift 按下标志
jne cur // 如果有shift 键按下,则也进行光标移动处理
// 取对应键的数字ASCII 码 -> al
xorl %ebx,%ebx
movb num_table(%eax),%al
jmp put_queue // 将该字符放入缓冲队列中
1: ret
/* 处理光标的移动 */
cur: movb cur_table(%eax),%al // 取光标字符表中相应键的代表字符 -> al
cmpb $'9,%al // 若该字符<='9',说明是上一页、下一页、插入或删除键
ja ok_cur // 功能字符序列中要添入字符'~'
movb $'~,%ah
ok_cur: shll $16,%eax // 将ax 中内容移到eax 高字中
movw $0x5b1b,%ax // 在ax中放入$0x5b1b,与eax 高字中字符组成移动序列
xorl %ebx,%ebx
jmp put_queue // 将该字符放入缓冲队列中
#if defined(KBD_FR)
num_table:
.ascii "789 456 1230." // 数字小键盘上键对应的数字ASCII 码表
#else
num_table:
.ascii "789 456 1230,"
#endif
cur_table:
.ascii "HA5 DGC YB623" // 数字小键盘上方向键或插入删除键对应的移动表示字符表
/*
* this routine handles function keys
*/
/* 功能键 */
func:
//////////////////////////////////////
pushl %eax
pushl %ecx
pushl %edx
/////////////////////////////////////
call _show_stat // kernl/sched.c
popl %edx
popl %ecx
popl %eax
/////////////////////////////////////
subb $0x3B,%al // 功能键'F1'的扫描码是0x3B
jb end_func // 如果扫描码小于0x3b,则不处理
cmpb $9,%al // 功能键是F1-F10?
jbe ok_func // 是,则跳转
subb $18,%al // 是功能键F11,F12 吗?
cmpb $10,%al // 是功能键F11?
jb end_func // 不是,则不处理
cmpb $11,%al // 是功能键F12?
ja end_func // 不是,则不处理
ok_func:
cmpl $4,%ecx /* check that there is enough room */
jl end_func // 需要放入4 个字符序列,如果放不下,则返回
movl func_table(,%eax,4),%eax // 取功能键对应字符序列
xorl %ebx,%ebx
jmp put_queue // 放入缓冲队列中
end_func:
ret
/*
* function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.
*/
/*
* 功能键发送的扫描码,F1 键为:'esc [ [ A', F2 键为:'esc [ [ B'等
*/
func_table:
.long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
.long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
.long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b
/* 扫描码-ASCII 字符映射表 */
#if defined(KBD_FINNISH) // 芬兰语键盘
key_map:
.byte 0,27
.ascii "1234567890+'"
.byte 127,9
.ascii "qwertyuiop}"
.byte 0,13,0
.ascii "asdfghjkl|{"
.byte 0,0
.ascii "'zxcvbnm,.-"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
// shift 键同时按下时的映射表
shift_map:
.byte 0,27
.ascii "!/"#$%&/()=?`"
.byte 127,9
.ascii "QWERTYUIOP]^"
.byte 13,0
.ascii "ASDFGHJKL//["
.byte 0,0
.ascii "*ZXCVBNM;:_"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
// alt 键同时按下时的映射表
alt_map:
.byte 0,0
.ascii "/0@/0$/0/0{[]}///0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_US) // 美式键盘
key_map:
.byte 0,27
.ascii "1234567890-="
.byte 127,9
.ascii "qwertyuiop[]"
.byte 13,0
.ascii "asdfghjkl;'"
.byte '`,0
.ascii "//zxcvbnm,./"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!@#$%^&*()_+"
.byte 127,9
.ascii "QWERTYUIOP{}"
.byte 13,0
.ascii "ASDFGHJKL:/""
.byte '~,0
.ascii "|ZXCVBNM<>?"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "/0@/0$/0/0{[]}///0"
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_GR) // 德语键盘
key_map:
.byte 0,27
.ascii "1234567890//'"
.byte 127,9
.ascii "qwertzuiop@+"
.byte 13,0
.ascii "asdfghjkl[]^"
.byte 0,'#
.ascii "yxcvbnm,.-"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "!/"#$%&/()=?`"
.byte 127,9
.ascii "QWERTZUIOP//*"
.byte 13,0
.ascii "ASDFGHJKL{}~"
.byte 0,''
.ascii "YXCVBNM;:_"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "/0@/0$/0/0{[]}///0"
.byte 0,0
.byte '@,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#elif defined(KBD_FR) // 法语键盘
key_map:
.byte 0,27
.ascii "&{/"'(-}_/@)="
.byte 127,9
.ascii "azertyuiop^$"
.byte 13,0
.ascii "qsdfghjklm|"
.byte '`,0,42 /* coin sup gauche, don't know, [*|mu] */
.ascii "wxcvbn,;:!"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '<
.fill 10,1,0
shift_map:
.byte 0,27
.ascii "1234567890]+"
.byte 127,9
.ascii "AZERTYUIOP<>"
.byte 13,0
.ascii "QSDFGHJKLM%"
.byte '~,0,'#
.ascii "WXCVBN?.///"
.byte 0,'*,0,32 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte '-,0,0,0,'+ /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '>
.fill 10,1,0
alt_map:
.byte 0,0
.ascii "/0~#{[|`//^@]}"
.byte 0,0
.byte '@,0,0,0,0,0,0,0,0,0,0
.byte '~,13,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0
.byte 0,0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0 /* 36-39 */
.fill 16,1,0 /* 3A-49 */
.byte 0,0,0,0,0 /* 4A-4E */
.byte 0,0,0,0,0,0,0 /* 4F-55 */
.byte '|
.fill 10,1,0
#else
#error "KBD-type not defined"
#endif
/*
* do_self handles "normal" keys, ie keys that don't change meaning
* and which have just one character returns.
*/
/*
* do_self handles "normal" keys
*/
do_self:
//////////////////////////////////////////////////////////////////
// 根据模式标志mode 选择alt_map、shift_map 或key_map 映射表之一 //
lea alt_map,%ebx // alt 键同时按下时的映射表基址alt_map