修改Linux0.00时钟中断为键盘中断
首先修改task1的代码使其运行一次后进入无限循环
将jmp _task1修改为jmp $
将timer_interrupt修改为:
1
_timer_interrupt:
2 PUSH ds
3 PUSH edx
4 PUSH ecx
5 PUSH ebx
6 PUSH eax
7 ;MOV eax, 0x10
8 ;MOV dx,ax
9 in al, 0x21 ; ┓
10 or al,( 1 << 1 ) ; ┣ 屏蔽当前中断
11 out 0x21 ,al ; ┛
12 mov al, 0x20 ; ┓置EOI位,其后8259A才能相应新的中断
13 out 0x20 ,al ; ┛
14 sti ; 允许响应新中断
15 in al, 0x60 ; 从0x60端口读出扫描码
16 ;MOV eax, 1
17 ;cmp DWORD [current],eax
18 ;je y1
19 ;MOV DWORD [current],eax
20 ;JMP TSS1_SEL : 0
21 ;jmp y2
22 y1:
23 ;MOV DWORD [current], 0
24 ;JMP TSS0_SEL : 0
25 ;MOV eax, 0x17
26 ;MOV ds,ax
27 ;MOV al, 65
28 call write_char ; 这里仅简单的将扫描码作为ANSI码打印出来
29 ;MOV ecx, 0xfff
30 y2:
31 cli
32 in al, 0x21 ; ┓
33 and al, ~ ( 1 << 1 ) ; ┣ 恢复接受当前中断
34 out 0x21 ,al ; ┛
35 POP eax
36 POP ebx
37 POP ecx
38 POP edx
39 POP ds
40 IRET
2 PUSH ds
3 PUSH edx
4 PUSH ecx
5 PUSH ebx
6 PUSH eax
7 ;MOV eax, 0x10
8 ;MOV dx,ax
9 in al, 0x21 ; ┓
10 or al,( 1 << 1 ) ; ┣ 屏蔽当前中断
11 out 0x21 ,al ; ┛
12 mov al, 0x20 ; ┓置EOI位,其后8259A才能相应新的中断
13 out 0x20 ,al ; ┛
14 sti ; 允许响应新中断
15 in al, 0x60 ; 从0x60端口读出扫描码
16 ;MOV eax, 1
17 ;cmp DWORD [current],eax
18 ;je y1
19 ;MOV DWORD [current],eax
20 ;JMP TSS1_SEL : 0
21 ;jmp y2
22 y1:
23 ;MOV DWORD [current], 0
24 ;JMP TSS0_SEL : 0
25 ;MOV eax, 0x17
26 ;MOV ds,ax
27 ;MOV al, 65
28 call write_char ; 这里仅简单的将扫描码作为ANSI码打印出来
29 ;MOV ecx, 0xfff
30 y2:
31 cli
32 in al, 0x21 ; ┓
33 and al, ~ ( 1 << 1 ) ; ┣ 恢复接受当前中断
34 out 0x21 ,al ; ┛
35 POP eax
36 POP ebx
37 POP ecx
38 POP edx
39 POP ds
40 IRET
注:因为键盘中断处理过程运行于Ring0,应此可以直接调用内核函数write_char
然后修改IDT表的0x21(0x21对应于IRQ1,表示键盘中断)项的offset_l和offset_h使其指向timer_interrupt中断处理过程.
1
void
init_idt()
2 {
3 int i;
4 for (i = 0 ;i < 256 ;i ++ )
5 {
6 if ( 0x21 == i || 0x80 == i)
7 {
8 continue ;
9 }
10 setup_int_gate((dword)ignore_int,i);
11 }
12 // setup_int_gate((dword)timer_interrupt,0x20);
13 setup_int_gate((dword)timer_interrupt, 0x21 );
14 setup_trap_gate((dword)system_interrupt, 0x80 );
15
16 idtr[ 0 ] = 8 * 256 ;
17 idtr[ 1 ] = ((dword) & idt_[ 0 ] + KERNEL_BASE) & 0xffff ;
18 idtr[ 2 ] = ((dword) & idt_[ 0 ] + KERNEL_BASE) >> 16 ;
19 }
2 {
3 int i;
4 for (i = 0 ;i < 256 ;i ++ )
5 {
6 if ( 0x21 == i || 0x80 == i)
7 {
8 continue ;
9 }
10 setup_int_gate((dword)ignore_int,i);
11 }
12 // setup_int_gate((dword)timer_interrupt,0x20);
13 setup_int_gate((dword)timer_interrupt, 0x21 );
14 setup_trap_gate((dword)system_interrupt, 0x80 );
15
16 idtr[ 0 ] = 8 * 256 ;
17 idtr[ 1 ] = ((dword) & idt_[ 0 ] + KERNEL_BASE) & 0xffff ;
18 idtr[ 2 ] = ((dword) & idt_[ 0 ] + KERNEL_BASE) >> 16 ;
19 }
最后启动键盘中断,将8259A主片的IRQ0位设为1,IRQ1位设为0
1
MOV edx,
0x21
2 in al,dx
3 AND al, 0xFD
4 OUT dx,al
注:0xFD对应二进制码111111012 in al,dx
3 AND al, 0xFD
4 OUT dx,al
调试结果:

按下a键输出一个字符,弹起a键输出另一个字符
由于直接将扫描码作为ANSI码输出因此会出现两个乱码字符
完整代码打包下载