程序10-1 linux/kernel/chr_drv/keyboard.S


  1 /*

  2  *  linux/kernel/keyboard.S

  3  *

  4  *  (C) 1991  Linus Torvalds

  5  */

  6

  7 /*

  8  *      Thanks to Alfred Leung for US keyboard patches

  9  *              Wolfgang Thiel for German keyboard patches

 10  *              Marc Corsini for the French keyboard

 11  */

    /*

     *      感谢Alfred Leung添加了US键盘补丁程序;

     *              Wolfgang Thiel添加了德语键盘补丁程序;

     *              Marc Corsini添加了法文键盘补丁程序。

     */

 12

 13 /* KBD_FINNISH for Finnish keyboards

 14  * KBD_US for US-type

 15  * KBD_GR for German keyboards

 16  * KBD_FR for Frech keyboard

 17  */

    /*

     * KBD_FINNISH 是芬兰键盘。

     * KBD_US 是美式键盘。

     * KBD_GR 是德式键盘。

     * KBD_FR 是法式键盘。

     */

 18 #define KBD_FINNISH         // 定义使用的键盘类型。用于后面选择采用的字符映射码表。

 19

 20 .text

 21 .globl _keyboard_interrupt  // 申明为全局变量,用于在初始化时设置键盘中断描述符。

 22

 23 /*

 24  * these are for the keyboard read functions

 25  */

    /*

     * 以下这些用于读键盘操作。

     */

    // size是键盘缓冲区(缓冲队列)长度(字节数)。

    /* 值必须是2的次方!并且与tty_io.c中的值匹配!!!! */

 26 size    = 1024          /* must be a power of two ! And MUST be the same

 27                            as in tty_io.c !!!! */

 

    // 以下是键盘缓冲队列数据结构tty_queue中的偏移量(include/linux/tty.h,第16行)。

 28 head = 4                // 缓冲区头指针字段在tty_queue结构中的偏移。

 29 tail = 8                // 缓冲区尾指针字段偏移。

 30 proc_list = 12          // 等待该缓冲队列的进程字段偏移。

 31 buf = 16                // 缓冲区字段偏移。

 32

    // 在本程序中使用了3个标志字节。mode是键盘特殊键(ctrlaltcaps)的按下状态标志;

    // leds 是用于表示键盘指示灯的状态标志。 e0是当收到扫描码 0xe0 0xe1 时设置的标志。

    // 每个字节标志中各位的含义见如下说明:

    // (1) mode是键盘特殊键的按下状态标志。

    // 表示大小写转换键(caps)、交换键(alt)、控制键(ctrl)和换档键(shift)的状态。

    //     7 caps键按下;

    //     6 caps键的状态(应该与leds中对应caps的标志位一样);

    //     5 alt键按下;

    //     4 alt键按下;

    //     3 ctrl键按下;

    //     2 ctrl键按下;

    //     1 shift键按下;

    //     0 shift键按下。

    // (2) leds是用于表示键盘指示灯的状态标志。即表示数字锁定键(num-lock)、大小写转换

    // 键(caps-lock)和滚动锁定键(scroll-lock)的LED发光管状态。

    //     7-3 0不用;

    //     2 caps-lock

    //     1 num-lock(初始置1,也即设置数字锁定键(num-lock)发光管为亮);

    //     0 scroll-lock

    // (3) 当扫描码是0xe00xe1时,置该标志。表示其后还跟随着1个或2个字符扫描码。通

    // 常若收到扫描码0xe0则意味着还有一个字符跟随其后;若收到扫描码0xe1则表示后面还跟

    // 随着2个字符。参见程序列表后说明。

    //     1 =1 收到0xe1标志;

    //     0 =1 收到0xe0标志。

 33 mode:   .byte 0         /* caps, alt, ctrl and shift mode */

 34 leds:   .byte 2         /* num-lock, caps, scroll-lock mode (nom-lock on) */

 35 e0:     .byte 0

 36

 37 /*

 38  *  con_int is the real interrupt routine that reads the

 39  *  keyboard scan-code and converts it into the appropriate

 40  *  ascii character(s).

 41  */

    /*

     * con_int是实际的中断处理子程序,用于读键盘扫描码并将其转换

     * 成相应的ascii字符。[ 注:这段英文注释已过时。]

     */

    //// 键盘中断处理程序入口点。

    // 当键盘控制器接收到用户的一个按键操作时,就会向中断控制器发出一个键盘中断请求信号

    // IRQ1。当CPU响应该请求时就会执行键盘中断处理程序。该中断处理程序会从键盘控制器相

    // 应端口(0x60)读入按键扫描码,并调用对应的扫描码子程序进行处理。

    // 首先从端口0x60读取当前按键的扫描码。然后判断该扫描码是否是0xe0 0xe1,如果是

    // 的话就立刻对键盘控制器作出应答,并向中断控制器发送中断结束(EOI)信号,以允许键

    // 盘控制器能继续产生中断信号,从而让我们来接收后续的字符。 如果接收到的扫描码不是

    // 这两个特殊扫描码,我们就根据扫描码值调用按键跳转表 key_table 中相应按键处理子程

    // 序,把扫描码对应的字符放入读字符缓冲队列read_q中。然后,在对键盘控制器作出应答

    // 并发送EOI信号之后,调用函数do_tty_interrupt()(实际上是调用 copy_to_cooked()

    // read_q中的字符经过处理后放到secondary辅助队列中。

 42 _keyboard_interrupt:

 43         pushl %eax

 44         pushl %ebx

 45         pushl %ecx

 46         pushl %edx

 47         push %ds

 48         push %es

 49         movl $0x10,%eax         // dses段寄存器置为内核数据段。

 50         mov %ax,%ds

 51         mov %ax,%es

 52         movl _blankinterval,%eax

 53         movl %eax,_blankcount   // 预置黑屏时间计数值为blankinterval(滴答数)。

 54         xorl %eax,%eax          /* %eax is scan code */   /* eax中是扫描码 */

 55         inb $0x60,%al           // 读取扫描码èal

 56         cmpb $0xe0,%al          // 扫描码是0xe0吗?若是则跳转到设置e0标志代码处。

 57         je set_e0

 58         cmpb $0xe1,%al          // 扫描码是0xe1吗?若是则跳转到设置e1标志代码处。

 59         je set_e1

 60         call key_table(,%eax,4) // 调用键处理程序 key_table + eax*4(参见502行)。

 61         movb $0,e0              // 返回之后复位e0标志。

 

    // 下面这段代码(55-65行)针对使用8255APC标准键盘电路进行硬件复位处理。端口0x61

    // 8255A 输出口B 的地址,该输出端口的第7位(PB7)用于禁止和允许对键盘数据的处理。

    // 这段程序用于对收到的扫描码做出应答。方法是首先禁止键盘,然后立刻重新允许键盘工作。

 62 e0_e1:  inb $0x61,%al           // PPI端口B状态,其位7用于允许/禁止(0/1)键盘。

 63         jmp 1f                  // 延迟一会。

 64 1:      jmp 1f

 65 1:      orb $0x80,%al           // al7置位(禁止键盘工作)。

 66         jmp 1f

 67 1:      jmp 1f

 68 1:      outb %al,$0x61          // 使PPI PB7位置位。

 69         jmp 1f

 70 1:      jmp 1f

 71 1:      andb $0x7F,%al          // al7复位。

 72         outb %al,$0x61          // 使PPI PB7位复位(允许键盘工作)。

 73         movb $0x20,%al          // 8259中断芯片发送EOI(中断结束)信号。

 74         outb %al,$0x20

 75         pushl $0                // 控制台tty=0,作为参数入栈。

 76         call _do_tty_interrupt  // 将收到数据转换成规范模式并存放在规范字符缓冲队列中。

 77         addl $4,%esp            // 丢弃入栈的参数,弹出保留的寄存器,并中断返回。

 78         pop %es

 79         pop %ds

 80         popl %edx

 81         popl %ecx

 82         popl %ebx

 83         popl %eax

 84         iret

 85 set_e0: movb $1,e0              // 收到扫描前导码0xe0时,设置e0标志(位0)。

 86         jmp e0_e1

 87 set_e1: movb $2,e0              // 收到扫描前导码0xe1时,设置e1标志(位1)。

 88         jmp e0_e1

 89

 90 /*

 91  * This routine fills the buffer with max 8 bytes, taken from

 92  * %ebx:%eax. (%edx is high). The bytes are written in the

 93  * order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.

 94  */

    /*

     * 下面该子程序把ebx:eax中的最多8个字符添入缓冲队列中。(ebx

     * 高字)所写入字符的顺序是al,ah,eal,eah,bl,bh...直到eax等于0

     */

    // 首先从缓冲队列地址表table_listtty_io.c99行)取控制台的读缓冲队列read_q地址。

    // 然后把al寄存器中的字符复制到读队列头指针处并把头指针前移1字节位置。若头指针移出

    // 读缓冲区的末端,就让其回绕到缓冲区开始处。 然后再看看此时缓冲队列是否已满,即比较

    // 一下队列头指针是否与尾指针相等(相等表示满)。 如果已满,就把ebx:eax中可能还有的

    // 其余字符全部抛弃掉。如果缓冲区还未满,就把ebx:eax中数据联合右移8个比特(即把ah

    // 值移到alblàahbhàbl),然后重复上面对al的处理过程。直到所有字符都处理完后,

    // 就保存当前头指针值,再检查一下是否有进程等待着读队列,如果有救唤醒之。

 95 put_queue:

 96         pushl %ecx

 97         pushl %edx                   // 下句取控制台tty结构中读缓冲队列指针。

 98         movl _table_list,%edx         # read-queue for console

 99         movl head(%edx),%ecx         // 取队列头指针èecx

100 1:      movb %al,buf(%edx,%ecx)      // al中的字符放入头指针位置处。

101         incl %ecx                    // 头指针前移1字节。

102         andl $size-1,%ecx            // 调整头指针。若超出缓冲区末端则绕回开始处。

103         cmpl tail(%edx),%ecx          # buffer full - discard everything

                                         // 头指针==尾指针吗?(即缓冲队列满了吗?)

104         je 3f                        // 如果已满,则后面未放入的字符全抛弃。

105         shrdl $8,%ebx,%eax           // ebx8个比特右移8位到eax中,ebx不变。

106         je 2f                        // 还有字符吗?若没有(等于0)则跳转。

107         shrl $8,%ebx                 // ebx值右移8位,并跳转到标号1继续操作。

108         jmp 1b

109 2:      movl %ecx,head(%edx)         // 若已将所有字符都放入队列,则保存头指针。

110         movl proc_list(%edx),%ecx    // 该队列的等待进程指针?

111         testl %ecx,%ecx              // 检测是否有等待该队列的进程。

112         je 3f                        // 无,则跳转;

113         movl $0,(%ecx)               // 有,则唤醒进程(置该进程为就绪状态)。

114 3:      popl %edx

115         popl %ecx

116         ret

117

    // 从这里开始是键跳转表key_table中指针对应的各个按键(或松键)处理子程序。供上面第

    // 53行语句调用。键跳转表key_table在第513行开始。

    //

    // 下面这段代码根据ctrlalt的扫描码,分别设置模式标志mode中相应位。如果在该扫描

    // 码之前收到过0xe0扫描码(e0标志置位),则说明按下的是键盘右边的ctrlalt键,则

    // 对应设置ctrlalt在模式标志mode中的比特位。

118 ctrl:   movb $0x04,%al               // 0x4mode中左ctrl键对应的比特位(位2)。

119         jmp 1f

120 alt:    movb $0x10,%al               // 0x10mode中左alt键对应的比特位(位4)。

121 1:      cmpb $0,e0                   // e0置位了吗(按下的是右边的ctrl/alt键吗)?

122         je 2f                        // 不是则转。

123         addb %al,%al                 // 是,则改成置相应右键标志位(位3或位5)。

124 2:      orb %al,mode                 // 设置mode标志中对应的比特位。

125         ret

    // 这段代码处理ctrlalt键松开时的扫描码,复位模式标志mode中的对应比特位。在处理

    // 时要根据e0标志是否置位来判断是否是键盘右边的ctrlalt键。

126 unctrl: movb $0x04,%al               // mode中左ctrl键对应的比特位(位2)。

127         jmp 1f

128 unalt:  movb $0x10,%al               // 0x10mode中左alt键对应的比特位(位4)。

129 1:      cmpb $0,e0                   // e0置位了吗(释放的是右边的ctrl/alt键吗)?

130         je 2f                        // 不是,则转。

131         addb %al,%al                 // 是,则改成复位相应右键的标志位(位3或位5)。

132 2:      notb %al                     // 复位mode标志中对应的比特位。

133         andb %al,mode

134         ret

135

    // 这段代码处理左、右shift键按下和松开时的扫描码,分别设置和复位mode中的相应位。

136 lshift:

137         orb $0x01,mode               // 是左shift键按下,设置mode中位0

138         ret

139 unlshift:

140         andb $0xfe,mode              // 是左shift键松开,复位mode中位0

141         ret

142 rshift:

143         orb $0x02,mode               // 是右shift键按下,设置mode中位1

144         ret

145 unrshift:

146         andb $0xfd,mode              // 是右shift键松开,复位mode中位1

147         ret

148

    // 这段代码对收到caps键扫描码进行处理。通过mode中位7可以知道caps键当前是否正处于

    // 在按下状态。若是则返回,否则就翻转mode标志中caps键按下的比特位(位6)和leds

    // 志中caps-lock比特位(位2),设置mode标志中caps键已按下标志位(位7)。

149 caps:   testb $0x80,mode             // 测试mode中位7是否已置位(即在按下状态)。

150         jne 1f                       // 如果已处于按下状态,则返回(186行)。

151         xorb $4,leds                 // 翻转leds标志中caps-lock比特位(位2)。

152         xorb $0x40,mode              // 翻转mode标志中caps键按下的比特位(位6)。

153         orb $0x80,mode               // 设置mode标志中caps键已按下标志位(位7)。

    // 这段代码根据leds标志,开启或关闭LED指示器。

154 set_leds:

155         call kb_wait                 // 等待键盘控制器输入缓冲空。

156         movb $0xed,%al               /* set leds command */

157         outb %al,$0x60               // 发送键盘命令0xed0x60端口。

158         call kb_wait

159         movb leds,%al                // leds标志,作为参数。

160         outb %al,$0x60               // 发送该参数。

161         ret

162 uncaps: andb $0x7f,mode              // caps键松开,则复位mode中的对应位(位7)。

163         ret

164 scroll:

165         testb $0x03,mode             // 若此时ctrl键也同时按下,则

166         je 1f

167         call _show_mem               // 显示内存状态信息(mm/memory.c457行)。

168         jmp 2f

169 1:      call _show_state             // 否则显示进程状态信息(kernel/sched.c45行)。

170 2:      xorb $1,leds                 // scroll键按下,则翻转leds中对应位(位0)。

171         jmp set_leds                 // 根据leds标志重新开启或关闭LED指示器。

172 num:    xorb $2,leds                 // num键按下,则翻转leds中的对应位(位1)。

173         jmp set_leds                 // 根据leds标志重新开启或关闭LED指示器。

174

175 /*

176  *  curosr-key/numeric keypad cursor keys are handled here.

177  *  checking for numeric keypad etc.

178  */

    /*

     * 这里处理方向键/数字小键盘方向键,检测数字小键盘等。

     */

179 cursor:

180         subb $0x47,%al          // 扫描码是数字键盘上的键(其扫描码>=0x47)发出的?

181         jb 1f                   // 如果小于则不处理,返回(198行)。

182         cmpb $12,%al            // 如果扫描码 > 0x530x53 - 0x47 = 12),则

183         ja 1f                   // 表示扫描码值超过830x53),不处理,返回。

184         jne cur2                /* check for ctrl-alt-del */ /* 检测ctrl-alt-del*/

    // 若等于12,说明del键已被按下,则继续判断ctrlalt是否也被同时按下。

185         testb $0x0c,mode        // ctrl键按下了吗?无,则跳转。

186         je cur2

187         testb $0x30,mode        // alt键按下吗?

188         jne reboot              // 有,则跳转到重启动处理(594行)。

189 cur2:   cmpb $0x01,e0           /* e0 forces cursor movement */ /* e0置位指光标移动*/

                                    // e0标志置位了吗?

190         je cur                  // 置位了,则跳转光标移动处理处cur

191         testb $0x02,leds        /* not num-lock forces cursor */ /* num-lock键则不许*/

                                    // 测试leds中标志num-lock键标志是否置位。

192         je cur                  // 若没有置位(numLED不亮),则也处理光标移动。

193         testb $0x03,mode        /* shift forces cursor */ /* shift键也使光标移动 */

                                    // 测试模式标志modeshift按下标志。

194         jne cur                 // 如果有shift键按下,则也进行光标移动处理。

195         xorl %ebx,%ebx          // 否则查询小数字表(199行),取键的数字ASCII码。

196         movb num_table(%eax),%al      // eax作为索引值,取对应数字字符èal

197         jmp put_queue           // 字符放入缓冲队列中。由于要放入队列的字符数<=4,因此

198 1:      ret                     // 在执行put_queue前需把ebx清零,见87行上的注释。

199

    // 这段代码处理光标移动或插入删除按键。

200 cur:    movb cur_table(%eax),%al      // 取光标字符表中相应键的代表字符èal

201         cmpb $'9,%al            // 若字符<='9'5623),说明是上一页、下一页、

202         ja ok_cur               // 插入或删除键,则功能字符序列中要添入字符'~'。不过

203         movb $'~,%ah            // 本内核并没有对它们进行识别和处理。

204 ok_cur: shll $16,%eax           // ax中内容移到eax高字中。

205         movw $0x5b1b,%ax        // 'esc ['放入ax,与eax高字中字符组成移动序列。

206         xorl %ebx,%ebx          // 由于只需把eax中字符放入队列,因此需要把ebx清零。

207         jmp put_queue           // 将该字符放入缓冲队列中。

208

209 #if defined(KBD_FR)

210 num_table:

211         .ascii "789 456 1230."  // 数字小键盘上键对应的数字ASCII码表。

212 #else

213 num_table:

214         .ascii "789 456 1230,"

215 #endif

216 cur_table:

217         .ascii "HA5 DGC YB623"  // 小键盘上方向键或插入删除键对应的移动表示字符表。

218

219 /*

220  * this routine handles function keys

221  */

    /*

     * 下面子程序处理功能键。

     */

    // 把功能键扫描码变换成转义字符序列并存放到读队列中。

222 func:

223         subb $0x3B,%al             // 'F1'的扫描码是0x3B,因此al中是功能键索引号。

224         jb end_func                // 如果扫描码小于0x3b,则不处理,返回。

225         cmpb $9,%al                // 功能键是F1--F10

226         jbe ok_func                // 是,则跳转。

227         subb $18,%al               // 是功能键F11F12吗?F11F12扫描码是0x570x58

228         cmpb $10,%al               // 是功能键F11

229         jb end_func                // 不是,则不处理,返回。

230         cmpb $11,%al               // 是功能键F12

231         ja end_func                // 不是,则不处理,返回。

232 ok_func:

233         testb $0x10,mode           // alt键也同时按下了吗?

234         jne alt_func               // 是则跳转处理更换虚拟控制终端。

235         cmpl $4,%ecx               /* check that there is enough room */ /*检查空间*/

236         jl end_func                // [??]需要放入4个字符,如果放不下,则返回。

237         movl func_table(,%eax,4),%eax      // 取功能键对应字符序列。

238         xorl %ebx,%ebx             // 因要放入队列字符数=4,因此执行put_queue之前

239         jmp put_queue              // 需把ebx清零。

    // 处理 alt + Fn 组合键,改变虚拟控制终端。此时eax中是功能键索引号(F1 -- 0),对应

    // 虚拟控制终端号。

240 alt_func:

241         pushl %eax                 // 虚拟控制终端号入栈,作为参数。

242         call _change_console       // 更改当前虚拟控制终端(chr_dev/tty_io.c87行)。

243         popl %eax                  // 丢弃参数。

244 end_func:

245         ret

246

247 /*

248  * function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.

249  */

    /*

     * 功能键发送的扫描码,F1键为:'esc [ [ A' F2键为:'esc [ [ B'等。

     */

250 func_table:

251         .long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b

252         .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b

253         .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b

254

    // 扫描码-ASCII字符映射表。

    // 根据前面定义的键盘类型(FINNISHUSGERMENFRANCH),将相应键的扫描码映射到

    // ASCII字符。

255 #if     defined(KBD_FINNISH)    // 以下是芬兰语键盘的扫描码映射表。

256 key_map:

257         .byte 0,27              // 扫描码0x00,0x01对应的ASCII码;

258         .ascii "1234567890+'"   // 扫描码0x02,...0x0c,0x0d对应的ASCII码,以下类似。

259         .byte 127,9

260         .ascii "qwertyuiop}"

261         .byte 0,13,0

262         .ascii "asdfghjkl|{"

263         .byte 0,0

264         .ascii "'zxcvbnm,.-"

265         .byte 0,'*,0,32         /* 36-39 */   /* 扫描码0x36-0x39对应的ASCII */

266         .fill 16,1,0            /* 3A-49 */   /* 扫描码0x3A-0x49对应的ASCII */

267         .byte '-,0,0,0,'+       /* 4A-4E */   /* 扫描码0x4A-0x4E对应的ASCII */

268         .byte 0,0,0,0,0,0,0     /* 4F-55 */   /* 扫描码0x4F-0x55对应的ASCII */

269         .byte '<

270         .fill 10,1,0

271

272 shift_map:                      // shift键同时按下时的映射表。

273         .byte 0,27

274         .ascii "!\"#$%&/()=?`"

275         .byte 127,9

276         .ascii "QWERTYUIOP]^"

277         .byte 13,0

278         .ascii "ASDFGHJKL\\["

279         .byte 0,0

280         .ascii "*ZXCVBNM;:_"

281         .byte 0,'*,0,32         /* 36-39 */

282         .fill 16,1,0            /* 3A-49 */

283         .byte '-,0,0,0,'+       /* 4A-4E */

284         .byte 0,0,0,0,0,0,0     /* 4F-55 */

285         .byte '>

286         .fill 10,1,0

287

288 alt_map:                         // alt键同时按下时的映射表。

289         .byte 0,0

290         .ascii "\0@\0$\0\0{[]}\\\0"

291         .byte 0,0

292         .byte 0,0,0,0,0,0,0,0,0,0,0

293         .byte '~,13,0

294         .byte 0,0,0,0,0,0,0,0,0,0,0

295         .byte 0,0

296         .byte 0,0,0,0,0,0,0,0,0,0,0

297         .byte 0,0,0,0           /* 36-39 */

298         .fill 16,1,0            /* 3A-49 */

299         .byte 0,0,0,0,0         /* 4A-4E */

300         .byte 0,0,0,0,0,0,0     /* 4F-55 */

301         .byte '|

302         .fill 10,1,0

303

304 #elif defined(KBD_US)           // 以下是美式键盘的扫描码映射表。

305

306 key_map:

307         .byte 0,27

308         .ascii "1234567890-="

309         .byte 127,9

310         .ascii "qwertyuiop[]"

311         .byte 13,0

312         .ascii "asdfghjkl;'"

313         .byte '`,0

314         .ascii "\\zxcvbnm,./"

315         .byte 0,'*,0,32         /* 36-39 */

316         .fill 16,1,0            /* 3A-49 */

317         .byte '-,0,0,0,'+       /* 4A-4E */

318         .byte 0,0,0,0,0,0,0     /* 4F-55 */

319         .byte '<

320         .fill 10,1,0

321

322

323 shift_map:

324         .byte 0,27

325         .ascii "!@#$%^&*()_+"

326         .byte 127,9

327         .ascii "QWERTYUIOP{}"

328         .byte 13,0

329         .ascii "ASDFGHJKL:\""

330         .byte '~,0

331         .ascii "|ZXCVBNM<>?"

332         .byte 0,'*,0,32         /* 36-39 */

333         .fill 16,1,0            /* 3A-49 */

334         .byte '-,0,0,0,'+       /* 4A-4E */

335         .byte 0,0,0,0,0,0,0     /* 4F-55 */

336         .byte '>

337         .fill 10,1,0

338

339 alt_map:

340         .byte 0,0

341         .ascii "\0@\0$\0\0{[]}\\\0"

342         .byte 0,0

343         .byte 0,0,0,0,0,0,0,0,0,0,0

344         .byte '~,13,0

345         .byte 0,0,0,0,0,0,0,0,0,0,0

346         .byte 0,0

347         .byte 0,0,0,0,0,0,0,0,0,0,0

348         .byte 0,0,0,0           /* 36-39 */

349         .fill 16,1,0            /* 3A-49 */

350         .byte 0,0,0,0,0         /* 4A-4E */

351         .byte 0,0,0,0,0,0,0     /* 4F-55 */

352         .byte '|

353         .fill 10,1,0

354

355 #elif defined(KBD_GR)           // 以下是德语键盘的扫描码映射表。

356

357 key_map:

358         .byte 0,27

359         .ascii "1234567890\\'"

360         .byte 127,9

361         .ascii "qwertzuiop@+"

362         .byte 13,0

363         .ascii "asdfghjkl[]^"

364         .byte 0,'#

365         .ascii "yxcvbnm,.-"

366         .byte 0,'*,0,32         /* 36-39 */

367         .fill 16,1,0            /* 3A-49 */

368         .byte '-,0,0,0,'+       /* 4A-4E */

369         .byte 0,0,0,0,0,0,0     /* 4F-55 */

370         .byte '<

371         .fill 10,1,0

372

373

374 shift_map:

375         .byte 0,27

376         .ascii "!\"#$%&/()=?`"

377         .byte 127,9

378         .ascii "QWERTZUIOP\\*"

379         .byte 13,0

380         .ascii "ASDFGHJKL{}~"

381         .byte 0,''

382         .ascii "YXCVBNM;:_"

383         .byte 0,'*,0,32         /* 36-39 */

384         .fill 16,1,0            /* 3A-49 */

385         .byte '-,0,0,0,'+       /* 4A-4E */

386         .byte 0,0,0,0,0,0,0     /* 4F-55 */

387         .byte '>

388         .fill 10,1,0

389

390 alt_map:

391         .byte 0,0

392         .ascii "\0@\0$\0\0{[]}\\\0"

393         .byte 0,0

394         .byte '@,0,0,0,0,0,0,0,0,0,0

395         .byte '~,13,0

396         .byte 0,0,0,0,0,0,0,0,0,0,0

397         .byte 0,0

398         .byte 0,0,0,0,0,0,0,0,0,0,0

399         .byte 0,0,0,0           /* 36-39 */

400         .fill 16,1,0            /* 3A-49 */

401         .byte 0,0,0,0,0         /* 4A-4E */

402         .byte 0,0,0,0,0,0,0     /* 4F-55 */

403         .byte '|

404         .fill 10,1,0

405

406

407 #elif defined(KBD_FR)           // 以下是法语键盘的扫描码映射表。

408

409 key_map:

410         .byte 0,27

411         .ascii "&{\"'(-}_/@)="

412         .byte 127,9

413         .ascii "azertyuiop^$"

414         .byte 13,0

415         .ascii "qsdfghjklm|"

416         .byte '`,0,42           /* coin sup gauche, don't know, [*|mu] */

417         .ascii "wxcvbn,;:!"

418         .byte 0,'*,0,32         /* 36-39 */

419         .fill 16,1,0            /* 3A-49 */

420         .byte '-,0,0,0,'+       /* 4A-4E */

421         .byte 0,0,0,0,0,0,0     /* 4F-55 */

422         .byte '<

423         .fill 10,1,0

424

425 shift_map:

426         .byte 0,27

427         .ascii "1234567890]+"

428         .byte 127,9

429         .ascii "AZERTYUIOP<>"

430         .byte 13,0

431         .ascii "QSDFGHJKLM%"

432         .byte '~,0,'#

433         .ascii "WXCVBN?./\\"

434         .byte 0,'*,0,32         /* 36-39 */

435         .fill 16,1,0            /* 3A-49 */

436         .byte '-,0,0,0,'+       /* 4A-4E */

437         .byte 0,0,0,0,0,0,0     /* 4F-55 */

438         .byte '>

439         .fill 10,1,0

440

441 alt_map:

442         .byte 0,0

443         .ascii "\0~#{[|`\\^@]}"

444         .byte 0,0

445         .byte '@,0,0,0,0,0,0,0,0,0,0

446         .byte '~,13,0

447         .byte 0,0,0,0,0,0,0,0,0,0,0

448         .byte 0,0

449         .byte 0,0,0,0,0,0,0,0,0,0,0

450         .byte 0,0,0,0           /* 36-39 */

451         .fill 16,1,0            /* 3A-49 */

452         .byte 0,0,0,0,0         /* 4A-4E */

453         .byte 0,0,0,0,0,0,0     /* 4F-55 */

454         .byte '|

455         .fill 10,1,0

456

457 #else

458 #error "KBD-type not defined"

459 #endif

460 /*

461  * do_self handles "normal" keys, ie keys that don't change meaning

462  * and which have just one character returns.

463  */

    /*

     * do_self用于处理“普通”键,也即含义没有变化并且只有一个字符返回的键。

     */

    // 首先根据mode标志选择alt_mapshift_mapkey_map映射表之一。

464 do_self:

465         lea alt_map,%ebx             // alt键同时按下时的映射表基址alt_map

466         testb $0x20,mode             /* alt-gr */  /* alt键同时按下了? */

467         jne 1f                       // 是,则向前跳转到标号1处。

468         lea shift_map,%ebx           // shift键同时按下时的映射表基址shift_map

469         testb $0x03,mode             // shift键同时按下了吗?

470         jne 1f                       // 有,则向前跳转到标号1处去映射字符。

471         lea key_map,%ebx             // 否则使用普通映射表key_map

    // 然后根据扫描码取映射表中对应的ASCII字符。若没有对应字符,则返回(转none)。

472 1:      movb (%ebx,%eax),%al         // 将扫描码作为索引值,取对应的ASCIIèal

473         orb %al,%al                  // 检测看是否有对应的ASCII码。

474         je none                      // 若没有(对应的ASCII=0),则返回。

    // ctrl键已按下或caps键锁定,并且字符在 'a'--'}'0x61--0x7D)范围内,则将其转成

    // 大写字符(0x41--0x5D)。

475         testb $0x4c,mode             /* ctrl or caps */  /* 控制键已按下或caps亮?*/

476         je 2f                        // 没有,则向前跳转标号2处。

477         cmpb $'a,%al                 // al中的字符与'a'比较。

478         jb 2f                        // al<'a',则转标号2处。

479         cmpb $'},%al                 // al中的字符与'}'比较。

480         ja 2f                        // al>'}',则转标号2处。

481         subb $32,%al                 // al转换为大写字符(减0x20)。

    // ctrl键已按下,并且字符在 '`'--'_'0x40--0x5F)之间,即是大写字符,则将其转换为

    // 控制字符(0x00--0x1F)。

482 2:      testb $0x0c,mode             /* ctrl */  /* ctrl键同时按下了吗?*/

483         je 3f                        // 若没有则转标号3

484         cmpb $64,%al                 // al'@'64)字符比较,即判断字符所属范围。

485         jb 3f                        // 若值<'@',则转标号3

486         cmpb $64+32,%al              // al'`'96)字符比较,即判断字符所属范围。

487         jae 3f                       // 若值>='`',则转标号3

488         subb $64,%al                 // 否则al0x40,转换为0x00--0x1f的控制字符。

    // 若左alt键同时按下,则将字符的位7置位。即此时生成值大于0x7f的扩展字符集中的字符。

489 3:      testb $0x10,mode             /* left alt */  /* alt键同时按下?*/

490         je 4f                        // 没有,则转标号4

491         orb $0x80,%al                // 字符的位7置位。

    // al中的字符放入读缓冲队列中。

492 4:      andl $0xff,%eax              // eax的高字和ah

493         xorl %ebx,%ebx               // 由于放入队列字符数<=4,因此需把ebx清零。

494         call put_queue               // 将字符放入缓冲队列中。

495 none:   ret

496

497 /*

498  * minus has a routine of it's own, as a 'E0h' before

499  * the scan code for minus means that the numeric keypad

500  * slash was pushed.

501  */

    /*

     * 减号有它自己的处理子程序,因为在减号扫描码之前的0xe0

     * 意味着按下了数字小键盘上的斜杠键。

     */

    // 注意,对于芬兰语和德语键盘,扫描码0x35对应的是'-'键。参见第264365行。

502 minus:  cmpb $1,e0                   // e0标志置位了吗?

503         jne do_self                  // 没有,则调用do_self对减号符进行普通处理。

504         movl $'/,%eax                // 否则用'/'替换减号'-'èal

505         xorl %ebx,%ebx               // 由于放入队列字符数<=4,因此需把ebx清零。

506         jmp put_queue                // 并将字符放入缓冲队列中。

507

508 /*

509  * This table decides which routine to call when a scan-code has been

510  * gotten. Most routines just call do_self, or none, depending if

511  * they are make or break.

512  */

    /*

     * 下面是一张子程序地址跳转表。当取得扫描码后就根据此表调用相应的扫描码

     * 处理子程序。大多数调用的子程序是do_self,或者是none,这起决于是按键

     * make)还是释放键(break)

     */

513 key_table:

514         .long none,do_self,do_self,do_self      /* 00-03 s0 esc 1 2 */

515         .long do_self,do_self,do_self,do_self   /* 04-07 3 4 5 6 */

516         .long do_self,do_self,do_self,do_self   /* 08-0B 7 8 9 0 */

517         .long do_self,do_self,do_self,do_self   /* 0C-0F + ' bs tab */

518         .long do_self,do_self,do_self,do_self   /* 10-13 q w e r */

519         .long do_self,do_self,do_self,do_self   /* 14-17 t y u i */

520         .long do_self,do_self,do_self,do_self   /* 18-1B o p } ^ */

521         .long do_self,ctrl,do_self,do_self      /* 1C-1F enter ctrl a s */

522         .long do_self,do_self,do_self,do_self   /* 20-23 d f g h */

523         .long do_self,do_self,do_self,do_self   /* 24-27 j k l | */

524         .long do_self,do_self,lshift,do_self    /* 28-2B { para lshift , */

525         .long do_self,do_self,do_self,do_self   /* 2C-2F z x c v */

526         .long do_self,do_self,do_self,do_self   /* 30-33 b n m , */

527         .long do_self,minus,rshift,do_self      /* 34-37 . - rshift * */

528         .long alt,do_self,caps,func             /* 38-3B alt sp caps f1 */

529         .long func,func,func,func               /* 3C-3F f2 f3 f4 f5 */

530         .long func,func,func,func               /* 40-43 f6 f7 f8 f9 */

531         .long func,num,scroll,cursor            /* 44-47 f10 num scr home */

532         .long cursor,cursor,do_self,cursor      /* 48-4B up pgup - left */

533         .long cursor,cursor,do_self,cursor      /* 4C-4F n5 right + end */

534         .long cursor,cursor,cursor,cursor       /* 50-53 dn pgdn ins del */

535         .long none,none,do_self,func            /* 54-57 sysreq ? < f11 */

536         .long func,none,none,none               /* 58-5B f12 ? ? ? */

537         .long none,none,none,none               /* 5C-5F ? ? ? ? */

538         .long none,none,none,none               /* 60-63 ? ? ? ? */

539         .long none,none,none,none               /* 64-67 ? ? ? ? */

540         .long none,none,none,none               /* 68-6B ? ? ? ? */

541         .long none,none,none,none               /* 6C-6F ? ? ? ? */

542         .long none,none,none,none               /* 70-73 ? ? ? ? */

543         .long none,none,none,none               /* 74-77 ? ? ? ? */

544         .long none,none,none,none               /* 78-7B ? ? ? ? */

545         .long none,none,none,none               /* 7C-7F ? ? ? ? */

546         .long none,none,none,none               /* 80-83 ? br br br */

547         .long none,none,none,none               /* 84-87 br br br br */

548         .long none,none,none,none               /* 88-8B br br br br */

549         .long none,none,none,none               /* 8C-8F br br br br */

550         .long none,none,none,none               /* 90-93 br br br br */

551         .long none,none,none,none               /* 94-97 br br br br */

552         .long none,none,none,none               /* 98-9B br br br br */

553         .long none,unctrl,none,none             /* 9C-9F br unctrl br br */

554         .long none,none,none,none               /* A0-A3 br br br br */

555         .long none,none,none,none               /* A4-A7 br br br br */

556         .long none,none,unlshift,none           /* A8-AB br br unlshift br */

557         .long none,none,none,none               /* AC-AF br br br br */

558         .long none,none,none,none               /* B0-B3 br br br br */

559         .long none,none,unrshift,none           /* B4-B7 br br unrshift br */

560         .long unalt,none,uncaps,none            /* B8-BB unalt br uncaps br */

561         .long none,none,none,none               /* BC-BF br br br br */

562         .long none,none,none,none               /* C0-C3 br br br br */

563         .long none,none,none,none               /* C4-C7 br br br br */

564         .long none,none,none,none               /* C8-CB br br br br */

565         .long none,none,none,none               /* CC-CF br br br br */

566         .long none,none,none,none               /* D0-D3 br br br br */

567         .long none,none,none,none               /* D4-D7 br br br br */

568         .long none,none,none,none               /* D8-DB br ? ? ? */

569         .long none,none,none,none               /* DC-DF ? ? ? ? */

570         .long none,none,none,none               /* E0-E3 e0 e1 ? ? */

571         .long none,none,none,none               /* E4-E7 ? ? ? ? */

572         .long none,none,none,none               /* E8-EB ? ? ? ? */

573         .long none,none,none,none               /* EC-EF ? ? ? ? */

574         .long none,none,none,none               /* F0-F3 ? ? ? ? */

575         .long none,none,none,none               /* F4-F7 ? ? ? ? */

576         .long none,none,none,none               /* F8-FB ? ? ? ? */

577         .long none,none,none,none               /* FC-FF ? ? ? ? */

578

579 /*

580  * kb_wait waits for the keyboard controller buffer to empty.

581  * there is no timeout - if the buffer doesn't empty, we hang.

582  */

    /*

     * 子程序kb_wait用于等待键盘控制器缓冲空。不存在超时处理 - 如果

     * 缓冲永远不空的话,程序就会永远等待(死掉)

     */

583 kb_wait:

584         pushl %eax

585 1:      inb $0x64,%al                 // 读键盘控制器状态。

586         testb $0x02,%al               // 测试输入缓冲器是否为空(等于0)。

587         jne 1b                        // 若不空,则跳转循环等待。

588         popl %eax

589         ret

590 /*

591  * This routine reboots the machine by asking the keyboard

592  * controller to pulse the reset-line low.

593  */

    /*

     * 该子程序通过设置键盘控制器,向复位线输出负脉冲,使系统复

     * 位重启(reboot)。

     */

    // 该子程序往物理内存地址0x472处写值0x1234。该位置是启动模式(reboot_mode)标志字。

    // 在启动过程中ROM BIOS会读取该启动模式标志值并根据其值来指导下一步的执行。 如果该

    // 值是0x1234,则BIOS 就会跳过内存检测过程而执行热启动(Warm-boot)过程。 如果若该

    // 值为0,则执行冷启动(Cold-boot)过程。

594 reboot:

595         call kb_wait                  // 首先等待键盘控制器输入缓冲器空。

596         movw $0x1234,0x472            /* don't do memory check */ /* 不检测内存 */

597         movb $0xfc,%al                /* pulse reset and A20 low */

598         outb %al,$0x64                // 向系统复位引脚和A20线输出负脉冲。

599 die:    jmp die                       // 停机。