Welcome to My World (www.dgmayor.com)

소프트웨어/C & 자료구조 & 커널 & DB

13. 인터럽트 - ISR 구현

dgmayor 2022. 6. 14. 11:21
728x90
 

10 ~ 13 강 소스코드입니다.
 

--------------------------------------------------------------------------------
 

대망의 마지막 인터럽트 구현입니다.
 
1. PIC 셋팅 ( 하드웨어 셋팅 )
2. IDT ( 전에 구현했던 GDT와 같은 종류 )
3. ISR ( 실제 인터럽트 발생 시 실행되는 코드 )
 
ISR이라고 거창하게 적었지만 실상은 인터럽트 발생시 실행할 함수 구현하는 겁니다.
 
대표적으로 int_ignore() 함수를 작성해봅시다.
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void idt_ignore()
{
 
    __asm__ __volatile__
    (
        "push gs;"
        "push fs;"
        "push es;"
        "push ds;"
        "pushad;"
        "pushfd;"
        "mov al, 0x20;"
        "out 0x20, al;"
    );
 
    kprintf("idt_ignore"540);
    
    __asm__ __volatile__
    (
        "popfd;"
        "popad;"
        "pop ds;"
        "pop es;"
        "pop fs;"
        "pop gs;"
        "leave;"
        "nop;"
        "iretd;"
    );
    
    
}
cs

 

인터럽트는 언제든지, 뜬금없는 타이밍에 발생합니다. 따라서 기존에 실행하고 있던 문맥들을 저장하지 않으면 안됩니다. 따라서 함수 도입부에 모든 레지스터들을 스택에 저장(push) 합니다. 그리고 인터럽트가 처리되고 나면 이를 그대로 스택에서 꺼내(pop) 인터럽트 발생 전에 실행되었던 문맥을 복구합니다.
 
mov al, 0x20 과 out 0x20,al 은 PIC 하드웨어를 다시 세팅하는 부분입니다. 인터럽트를 다시 받아들일 준비를 하는 코드입니다.
 
이런 패턴으로 타이머와 키보드 또한 똑같이 구현해봅시다. 그 전에 interrupt.c 맨 상단에 전역변수로 다음과 같이 만들어둡시다.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
unsigned char keyt[2= { 'A'0 };
unsigned char key[2= { 'A'0 };
 
void idt_timer()
{
 
    __asm__ __volatile__
    (
        "push gs;"
        "push fs;"
        "push es;"
        "push ds;"
        "pushad;"
        "pushfd;"
        "mov al, 0x20;"
        "out 0x20, al;"
        
    );
 
    kprintf(keyt, 740);
    keyt[0]++;
 
    __asm__ __volatile__
    (
        "popfd;"
        "popad;"
        "pop ds;"
        "pop es;"
        "pop fs;"
        "pop gs;"
        "leave;"
        "nop;"
        "iretd;"
    );
 
 
}
 
void idt_keyboard()
{
 
    __asm__ __volatile__
    (
        "push gs;"
        "push fs;"
        "push es;"
        "push ds;"
        "pushad;"
        "pushfd;"
        "in al, 0x60;"
        "mov al, 0x20;"
        "out 0x20, al;"
    );
 
    kprintf(key, 840);
    key[0]++;
 
    __asm__ __volatile__
    (
        "popfd;"
        "popad;"
        "pop ds;"
        "pop es;"
        "pop fs;"
        "pop gs;"
        "leave;"
        "nop;"
        "iretd;"
    );
 
}
 
 
cs
 

보면 알겠지만 타이머와 키보드 모두 입력이 올 때마다 keyt, key 를 각각 해당 위치에 출력을 하고 하나를 더해줍니다. 예상하건데 타이머는 시도때도 없이 인터럽트가 발생하니 알 수 없는 문자들이 바뀌어가며 찍혀나가겠고, 키보드를 칠 때만 문자가 찍혀나올 것입니다.
 

--------------------------------------------------------------------------------
 
 
이제 모든 구현은 끝이 났습니다. 최종적인 코드는 다음과 같습니다.
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include "interrupt.h"
#include "function.h"
 
struct IDT inttable[3];
struct IDTR idtr = { 256 * 8 - 1,0 };
 
unsigned char keyt[2= { 'A'0 };
unsigned char key[2= { 'A'0 };
 
void init_intdesc()
{
 
    int i,j;
    unsigned int ptr;
    unsigned short *isr;
 
    {  // 0x00 : isr_ignore
        ptr = (unsigned int)idt_ignore;
        inttable[0].selector = (unsigned short)0x08;
        inttable[0].type = (unsigned short)0x8E00;
        inttable[0].offsetl = (unsigned short)(ptr & 0xFFFF);
        inttable[0].offseth = (unsigned short)(ptr >> 16);
 
    }
 
    {  // 0x01 : isr_timer
        ptr = (unsigned int)idt_timer;
        inttable[1].selector = (unsigned short)0x08;
        inttable[1].type = (unsigned short)0x8E00;
        inttable[1].offsetl = (unsigned short)(ptr & 0xFFFF);
        inttable[1].offseth = (unsigned short)(ptr >> 16);
 
    }
 
    {  // 0x02 : isr_keyboard
        ptr = (unsigned int)idt_keyboard;
        inttable[2].selector = (unsigned short)0x08;
        inttable[2].type = (unsigned short)0x8E00;
        inttable[2].offsetl = (unsigned short)(ptr & 0xFFFF);
        inttable[2].offseth = (unsigned short)(ptr >> 16);
 
    }
 
    // 물리주소 0x0 번지에 ISR 배치 시작
 
    for (i = 0; i < 256; i++)
    {
        isr = (unsigned short*)(0x0 + i * 8);
        *isr = inttable[0].offsetl;
        *(isr + 1= inttable[0].selector;
        *(isr + 2= inttable[0].type;
        *(isr + 3= inttable[0].offseth;
 
    }
 
    // 타이머 ISR 등록
    {
        isr = (unsigned short*)(0x0 + 8 * 0x20);
        *isr = inttable[1].offsetl;
        *(isr + 1= inttable[1].selector;
        *(isr + 2= inttable[1].type;
        *(isr + 3= inttable[1].offseth;
    }
 
    // 키보드 ISR 등록
 
    {
        isr = (unsigned short*)(0x0 + 8 * 0x21);
        *isr = inttable[2].offsetl;
        *(isr + 1= inttable[2].selector;
        *(isr + 2= inttable[2].type;
        *(isr + 3= inttable[2].offseth;
    
    }
 
    //  인터럽트 작동 시작
 
    __asm__ __volatile__("mov eax, %0"::"r"(&idtr));
    __asm__ __volatile__("lidt [eax]");
    __asm__ __volatile__("mov al,0xFC");
    __asm__ __volatile__("out 0x21,al");
    __asm__ __volatile__("sti");
 
    return;
 
 
}
 
void idt_ignore()
{
 
    __asm__ __volatile__
    (
        "push gs;"
        "push fs;"
        "push es;"
        "push ds;"
        "pushad;"
        "pushfd;"
        "mov al, 0x20;"
        "out 0x20, al;"
    );
 
    kprintf("idt_ignore"540);
    
    __asm__ __volatile__
    (
        "popfd;"
        "popad;"
        "pop ds;"
        "pop es;"
        "pop fs;"
        "pop gs;"
        "leave;"
        "nop;"
        "iretd;"
    );
    
    
}
 
void idt_timer()
{
 
    __asm__ __volatile__
    (
        "push gs;"
        "push fs;"
        "push es;"
        "push ds;"
        "pushad;"
        "pushfd;"
        "mov al, 0x20;"
        "out 0x20, al;"
        
    );
 
    kprintf(keyt, 740);
    keyt[0]++;
 
    __asm__ __volatile__
    (
        "popfd;"
        "popad;"
        "pop ds;"
        "pop es;"
        "pop fs;"
        "pop gs;"
        "leave;"
        "nop;"
        "iretd;"
    );
 
 
}
 
void idt_keyboard()
{
 
    __asm__ __volatile__
    (
        "push gs;"
        "push fs;"
        "push es;"
        "push ds;"
        "pushad;"
        "pushfd;"
        "in al, 0x60;"
        "mov al, 0x20;"
        "out 0x20, al;"
    );
 
    kprintf(key, 840);
    key[0]++;
 
    __asm__ __volatile__
    (
        "popfd;"
        "popad;"
        "pop ds;"
        "pop es;"
        "pop fs;"
        "pop gs;"
        "leave;"
        "nop;"
        "iretd;"
    );
 
}
 
cs
 

이제 남은 것은 컴파일 후, 실제 제대로 돌아가는지 확인하는 것입니다. 컴파일하고 가상머신에 돌려봅시다.
 
 
컴파일을 하고...
 
 
 
 
 
 

보면 어디서 알 수 없는 ignore 인터럽트가 걸렸고 ( 254개 중에 하나겠죠? )
타이머 인터럽트는 계속 걸리니 문자가 끊임없이 바뀌어서 출력됩니다.
키보드는 제가 키를 누를 때마다 문자가 바뀌는 것을 알 수 있습니다.
 
 
 
--------------------------------------------------------------------------------
 
 
 

이렇게 해서 인터럽트를 C언어로 구현해봤습니다. OS 만드는 데 큰 산을 넘은 셈입니다.
 
다음 강의에서는 키보드 인터럽트를 활용해서 키보드 입력을 받는 키보드 드라이버를 만들어보도록 하겠습니다.
 
감사합니다.
 
728x90