basic/운영체제
[OS만들기] day 07: PIC(Programmable Interrupt Controller)의 초기화
NoelBird
2022. 11. 22. 13:34
오늘의 결과물
timer로 interrupt로 인해서 This is the timer interrupt 부분의 첫 글자가 계속 다른 글자로 변경됩니다.
저번 시간 간단 복습
뭔가 저번 시간에 했던 IDT가 잘 기억이 나지 않았습니다.
간단하게 요약하면, cli라는 명령어로 EFLAGS라는 레지스터의 interrupt 플래그를 0으로 만들어 줍니다.
그리고 IDT를 등록해서 인터럽트가 걸리면 어디로 갈 지 설정해줍니다.
이후, sti 명령어로 EFLAGS 레지스터의 interrupt 플래그를 1로 만들어줍니다.
interrupt 플래그가 1이면, 인터럽트를 받을 준비가 됐다는 의미입니다.
오늘의 코드
; init.inc
SysCodeSelector equ 0x08
SysDataSelector equ 0x10
VideoSelector equ 0x18
; 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
; 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까지 입력 했음
내용 살펴보면서 이해하기