인터럽트와 예외(키보드 인터럽트 핸들러 구현)

아래와 같이 소스를 짠다. (파일은 이전 장에서 사용하던 파일 중 kernel 만 변경)

kernel.asm

책에서는 mov al, 0xFC 로 타이머와 키보드만 유효하게 하나, 실행되지 않으므로 0xFD 키보드만 유효하게 한다.

%include "init.inc"


[org 0x10000]

[bits 32]


PM_Start:

mov bx, SysDataSelector

mov ds, bx

mov es, bx

mov fs, bx

mov gs, bx

mov ss, bx

lea esp, [PM_Start]

mov edi, 0

lea esi, [msgPMode]

call printf

cld

mov ax, SysDataSelector

mov es, ax

xor eax, eax

xor ecx, ecx

mov ax, 256 ; IDT 영역에 256개의

mov edi, 0 ; 디스크립터를 복사한다.

loop_idt:

lea esi, [idt_ignore]

mov cx, 8 ; 디스크립터 하나는 8바이트 이다.

rep movsb

dec ax

jnz loop_idt

mov edi, 8*0x20

lea esi, [idt_timer]

mov cx, 8

rep movsb

mov edi, 8*0x21 ; 키보드 IDT 디스크립터를 복사한다.

lea esi, [idt_keyboard]    ; idt_keyboard는 디스크립터의 포인터 이다.

mov cx, 8

rep movsb

lidt [idtr]

mov al, 0xFD ; 막아두었던 인터럽트 중

out 0x21, al ; 타이머와 키보드만 다시 유효하게 한다.

sti


jmp $


printf:

push eax

push es

mov ax, VideoSelector

mov es, ax

printf_loop:

mov al, byte [esi]

mov byte [es:edi], al

inc edi

mov byte [es:edi], 0x06

inc esi

inc edi

or al, al

jz printf_end

jmp printf_loop

printf_end:

pop es

pop eax

ret


msgPMode db "We are in Protected Mode", 0

msg_isr_ignore db "This is an ignorable interrupt", 0

msg_isr_32_timer db ".This is the timer interrupt", 0

msg_isr_33_keyboard db ".This is the keyboard interrupt", 0


idtr:

dw 256*8-1 ; IDT의 Limit

dd 0 ; IDT의 Base Address


isr_ignore:

push gs

push fs

push es

push ds

pushad

pushfd

mov al, 0x20

out 0x20, al

mov ax, VideoSelector

mov es, ax

mov edi, (80*7*2)

lea esi, [msg_isr_ignore]

call printf

popfd

popad

pop ds

pop es

pop fs

pop gs

iret

isr_32_timer:

push gs

push fs

push es

push ds

pushad

pushfd

mov al, 0x20

out 0x20, al

mov ax, VideoSelector

mov es, ax

mov edi, (80*2*2)

lea esi, [msg_isr_32_timer]

call printf

inc byte [msg_isr_32_timer]

popfd

popad

pop ds

pop es

pop fs

pop gs

iret ; 인터럽트가 발생한 당시의 프로그램의 다음 명령으로 돌아가서 프로그램 재개


isr_33_keyboard:    ; 인터럽트 핸들러 루틴

pushad

push gs

push fs

push es

push ds

pushfd

in al, 0x60    ; 키보드에서 어느 키가 눌렸는지 알아내기 위해 사용하는 명령어, 키보드 버퍼에 있는 문자 스
                    캔코드를 가져온다.
 이 스캔코드는 아스키코드와는 다르다. 또한 버퍼에서 문자를 가져오지 않
                    으면 키보드에서 인터럽트는 발생하지 않는다.

; PIC 리셋 코드

mov al, 0x20    ; 마스터 PIC는 0x20, 0x21 포트를 사용하고, 슬레이브 PIC는 0xA0, 0xA1 포트를 사용한다.

out 0x20, al

mov ax, VideoSelector

mov es, ax

mov edi, (80*4*2)

lea esi, [msg_isr_33_keyboard]

call printf

inc byte [msg_isr_33_keyboard]    ; 아스키코드 값이 1씩 추가된다. 여기서 맨 처음 출력되는 것은 '.' 이고
                                        엔터 시 값이 아스키 값이 1씩 증가되어 문자열이 출력된다.

popfd

pop ds

pop es

pop fs

pop gs

popad

iret

idt_ignore:

dw isr_ignore

dw 0x08

db 0

db 0x8E

dw 0x0001

idt_timer:

dw isr_32_timer

dw 0x08

db 0

db 0x8E

db 0x0001


idt_keyboard:

dw isr_33_keyboard

dw 0x08

db 0

db 0x8E

dw 0x0001

times 512-($-$$) db 0

컴파일 후 실행 시 키보드를 누를때, 뗄때마다 맨 앞의 값이 하나 바뀌는것을 알 수 있다.


신기방기 하구만.. 하하..








+ Recent posts