Skip to content

day 07: PIC(Programmable Interrupt Controller)의 초기화

오늘의 결과물

timer_interrupt

timer로 interrupt로 인해서 This is the timer interrupt 부분의 첫 글자가 계속 다른 글자로 변경됩니다.

저번 시간 간단 복습

뭔가 저번 시간에 했던 IDT가 잘 기억이 나지 않았습니다.

간단하게 요약하면, cli라는 명령어로 EFLAGS라는 레지스터의 interrupt 플래그를 0으로 만들어 줍니다.

그리고 IDT를 등록해서 인터럽트가 걸리면 어디로 갈 지 설정해줍니다.

이후, sti 명령어로 EFLAGS 레지스터의 interrupt 플래그를 1로 만들어줍니다.

interrupt 플래그가 1이면, 인터럽트를 받을 준비가 됐다는 의미입니다.

오늘의 코드

1
2
3
4
; init.inc
SysCodeSelector     equ 0x08
SysDataSelector     equ 0x10
VideoSelector       equ 0x18
  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
; boot.asm
%include "src/init.inc"

[org 0]
    jmp 07C0h:start

start:
    mov ax, cs
    mov ds, ax
    mov es, ax

reset:              ; 플로피 디스크를 리셋합니다.
    mov ax, 0       ;
    mov dl, 0       ; drive=0 (A:)
    int 13h         ;
    jc reset        ; 에러가 나면 다시 합니다.

    mov ax, 0xB800
    mov es, ax
    mov di, 0
    mov ax, word [msgBack]
    mov cx, 0x7FF

paint:
    mov word [es:di], ax
    add di, 2
    dec cx
    jnz paint

read:
    mov ax, 0x1000          ; ES:BX = 1000:0000
    mov es, ax
    mov bx, 0

    mov ah, 2               ; 디스크에 있는 데이터를 es:bx 주소로
    mov al, 1               ; 1섹터를 읽을 것이라고 알림
    mov ch, 0               ; 0번째 실린더
    mov cl, 2               ; 2번째 섹터부터 읽기 시작합니다.
    mov dh, 0               ; Head=0
    mov dl, 0               ; Drive=0, A: 드라이브
    int 13h

    jc read; 에러가 나면 다시 함

    mov dx, 0x3F2   ; 플로피디스크 드라이브의
    xor al, al      ; 모터를 끈다.
    out dx, al

    cli

    mov al, 0x11    ; PIC의 초기화
    out 0x20, al    ; 마스터 PIC
    dw 0x00eb, 0x00eb   ; jmp $+2, jmp $+2
    out 0xA0, al    ; 슬레이브 PIC
    dw 0x00eb, 0x00eb

    mov al, 0x20    ; 마스터 PIC 인터럽트 시작점
    out 0x21, al
    dw 0x00eb, 0x00eb
    mov al, 0x28    ; 슬레이브 PIC 인터럽트 시작점
    out 0xA1, al
    dw 0x00eb, 0x00eb

    mov al, 0x04    ; 마스터 PIC의 IRQ 2번에
    out 0x21, al    ; 슬레이브 PIC가 연결되어 있습니다.
    dw 0x00eb, 0x00eb
    mov al, 0x02    ; 슬레이브 PIC가 마스터 PIC의
    out 0xA1, al    ; IRQ 2번에 연결되어 있습니다.
    dw 0x00eb, 0x00eb

    mov al, 0x01    ; 8086 모드를 사용합니다.
    out 0x21, al
    dw 0x00eb, 0x00eb
    out 0xA1, al
    dw 0x00eb, 0x00eb

    mov al, 0xFF    ; PIC에서 모든 인터럽트를
    out 0xA1, al    ; 막아놓습니다.
    dw 0x00eb, 0x00eb
    mov al, 0xFB    ; 마스터 PIC의 IRQ 2번을 제외한
    out 0x21, al    ; 모든 인터럽트를 막아둡니다.

    lgdt[gdtr]

    mov eax, cr0
    or eax, 0x00000001
    mov cr0, eax

    jmp $+2
    nop
    nop

    mov bx, SysDataSelector
    mov ds, bx
    mov es, bx
    mov fs, bx
    mov gs, bx
    mov ss, bx

    jmp dword SysCodeSelector:0x010000

    msgBack db '.', 0x67

gdtr:
    dw gdt_end - gdt - 1    ; GDT의 limit
    dd gdt+0x7C00           ; GDT의 베이스 어드레스

gdt:
    dd 0, 0
    dd 0x0000FFFF, 0x00CF9A00
    dd 0x0000FFFF, 0x00CF9200
    dd 0x8000FFFF, 0x0040920B
gdt_end:

times 510-($-$$) db 0
dw 0AA55h
  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
; kernel.asm
%include "src/init.inc"

[org 0x010000]
[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

    lidt [idtr]
    mov al, 0xFE        ; 막아두었던 인터럽트 중
    out 0x21, al        ; 타이머만 다시 유효하게 합니다.
    sti

    jmp $

;*****************************
;********** Subroutines ******
;*****************************

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


;*********************************
;*********** Data Area ***********
;*********************************
msgPMode db "We are in Protected Mode", 0
msg_isr_ignore db "This is an ignorable inturrupt", 0
msg_isr_32_timer db "This is the timer inturrupt", 0

idtr:
    dw 256*8 - 1        ; IDT의 Limit
    dd 0                ; IDT의 Base Address

;*********************************
;*** Interrupt Service Routines **
;*********************************
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

;*********************************
;************* IDT ***************
;*********************************

idt_ignore:
    dw isr_ignore
    dw SysCodeSelector
    db 0
    db 0x8E
    dw 0x0001

idt_timer:
    dw isr_32_timer
    dw 0x08
    db 0
    db 0x8E
    dw 0x0001

times 512 - ($-$$) db 0

아직 코드만 작성했고, 이해하지는 못 했습니다.

특히 PIC(Programmable Interrupt Controller)라는 칩이 어떤 칩이고,

out 이라는 명령어를 통해서 어떻게 동작하는 것인지 잘 모르겠습니다.

i/o는 특별한 맵 같은 것이 있을 지도 궁금합니다.

왜 마스터 PIC가 0x20인지, 슬레이브 PIC가 0xA0인지 궁금합니다.

자세한 내용은 다음 시간에 알아보는 것으로 하겠습니다.

오늘의 명령어 정리

  • JC(Jump if Carry flag set): 캐리 플래그가 1일 때 JUMP

  • PUSHAD: 범용 레지스터 값들을 스택에 저장 (레지스터 백업하는 용도로 사용합니다.) Stack에 저장하는 순서는 다음과 같습니다. eax => ecx => edx => ebx => esp => ebp => esi => edi

  • POPAD: pushad의 반대 역할(스택에 들어있는 값들을 register로 pop하기)

  • PUSHFD: PUSH Flags Dword. x86 flags를 순서대로 dword 만큼 스택에 저장합니다.

  • PTR: 메모리에 접근해서 얼마만큼 읽어 들일것인지 데이터 타입을 재정의 하는 명령어입니다.

done

p. 124까지 입력 했음

내용 살펴보면서 이해하기