-
[OS] 20. Interrupt-timerOS/OS from Scratch 2021. 7. 18. 00:27
서론
이번 강의에서는 CPU Timer와 키보드 Interrupt를 구현하겠습니다. 해당 Github 강의는 다음과 같습니다.
https://github.com/cfenollosa/os-tutorial/tree/master/20-interrupts-timer
cfenollosa/os-tutorial
How to create an OS from scratch. Contribute to cfenollosa/os-tutorial development by creating an account on GitHub.
github.com
이론
PIT (Programmable Interval Timer)
PIT chip(문서)은 oscillator, prescaler, frequency dividers로 구성되며, frequency divider은 Timer가 IRQ0와 같은 외부 Circuitry를 제어할 수 있게 합니다. PIT의 oscillator는 약 1.193180 MHz로 작동하는데, freqeuncy divider는 이 진동수를 divider로 나누어서 더 느린 진동수로 작동하게 합니다. 입력 진동수에 따라 펄스가 발생할 때마다 counter를 감소시키고, counter가 0이 되면 counter가 리셋됩니다. 예를 들어 입력 진동수가 200Hz이고, counter가 10번마다 리셋되면, 출력 진동수는 200/10=20Hz가 되는 것입니다. PIT의 frequency divider는 크기가 16bit이며, 0부터 65535 사이의 값을 가질 수 있습니다.
PIT의 출력은 3개의 Channel로 구성되며, 이중 Channel 0는 IRQ0에 연결됩니다. PIT chip의 command 레지스터는 0x43 I/O port이며, Channel 0의 data I/O port는 0x40이며, frequency divider를 넘겨줍니다. 0x43 port에는 다음 데이터를 통해 명령을 줄 수 있습니다.
Bits Usage 6 and 7 Select channel : 0 0 = Channel 0 0 1 = Channel 1 1 0 = Channel 2 1 1 = Read-back command (8254 only) 4 and 5 Access mode : 0 0 = Latch count value command 0 1 = Access mode: lobyte only 1 0 = Access mode: hibyte only 1 1 = Access mode: lobyte/hibyte 1 to 3 Operating mode : 0 0 0 = Mode 0 (interrupt on terminal count) 0 0 1 = Mode 1 (hardware re-triggerable one-shot) 0 1 0 = Mode 2 (rate generator) 0 1 1 = Mode 3 (square wave generator) 1 0 0 = Mode 4 (software triggered strobe) 1 0 1 = Mode 5 (hardware triggered strobe) 1 1 0 = Mode 2 (rate generator, same as 010b) 1 1 1 = Mode 3 (square wave generator, same as 011b) 0 BCD/Binary mode: 0 = 16-bit binary, 1 = four-digit BCD
본 강의에서는 00110110b ((Channel0, Access Mode: lo/hi byte, Operating Mode: square wave generator, 16bit-binary) 설정을 사용합니다.
Keyboard Interrupt
키보드의 Interrupt은 IRQ1을 통해 처리합니다. 키보드 입력이 주어지면 PIC는 key가 눌리거나(key down) 땠을 때의(key up) scancode를 0x60 I/O port에 저장합니다. 이때 key up의 scancode는 key down의 scancode + 0x80의 값을 가집니다. 본 강의에서는 입력으로 주어진 scancode를 ASCII code로 변환해 출력하는 것을 구현합니다.
코드
본 강의의 코드는 이론을 그대로 코드를 구현한 것이므로 설명은 주석으로 대체하겠습니다.
cpu/timer.h
#ifndef TIMER_H #define TIMER_H #include "../kernel/util.h" void init_timer(u32 freq); #endif
cpu/timer.c
#include "timer.h" #include "../drivers/screen.h" #include "../kernel/util.h" #include "isr.h" u32 tick = 0; static void timer_callback(registers_t regs) { tick++; kprint("Tick: "); char tick_ascii[256]; int_to_ascii(tick, tick_ascii); kprint(tick_ascii); kprint("\n"); } void init_timer(u32 freq) { // Timer는 IRQ0 이용 // timer_callback IRQ0에 설치 register_interrupt_handler(IRQ0, timer_callback); // PIT reload value 설정 // divisor가 0이 되면 reload 됨 u32 divisor = 1193180 / freq; u8 low = (u8)(divisor & 0xFF); u8 high = (u8)((divisor >> 8) & 0xFF); // 명령 전송 // 0x43: Command Port // 0x36=00110110b: 16bit (Channel0, Access Mode: lo/hi byte // , Operating Mode: square wave generator, 16bit-binary) port_byte_out(0x43, 0x36); // 0x40: Channel 0 data port // PIT reload value의 하위 byte port_byte_out(0x40, low); // PIT reload value의 상위 byte port_byte_out(0x40, high); }
drivers/keyboard.h
#include "../cpu/types.h" void init_keyboard();
drivers/keyboard.c
#include "keyboard.h" #include "ports.h" #include "../cpu/isr.h" #include "screen.h" static void keyboard_callback(registers_t regs) { // PIC가 scancode를 0x60 port에 저장 u8 scancode = port_byte_in(0x60); char *sc_ascii; int_to_ascii(scancode, sc_ascii); kprint(sc_ascii); kprint(", "); // scancode에 해당하는 문자 출력 print_letter(scancode); kprint("\n"); } void init_keyboard() { register_interrupt_handler(IRQ1, keyboard_callback); } void print_letter(u8 scancode) { switch(scancode) { case 0x0: kprint("ERROR"); break; case 0x1: kprint("ESC"); break; case 0x2: kprint("1"); break; case 0x3: kprint("2"); break; case 0x4: kprint("3"); break; case 0x5: kprint("4"); break; case 0x6: kprint("5"); break; case 0x7: kprint("6"); break; case 0x8: kprint("7"); break; case 0x9: kprint("8"); break; case 0x0A: kprint("9"); break; case 0x0B: kprint("0"); break; case 0x0C: kprint("-"); break; case 0x0D: kprint("+"); break; case 0x0E: kprint("Backspace"); break; case 0x0F: kprint("Tab"); break; case 0x10: kprint("Q"); break; case 0x11: kprint("W"); break; case 0x12: kprint("E"); break; case 0x13: kprint("R"); break; case 0x14: kprint("T"); break; case 0x15: kprint("Y"); break; case 0x16: kprint("U"); break; case 0x17: kprint("I"); break; case 0x18: kprint("O"); break; case 0x19: kprint("P"); break; case 0x1A: kprint("["); break; case 0x1B: kprint("]"); break; case 0x1C: kprint("ENTER"); break; case 0x1D: kprint("LCtrl"); break; case 0x1E: kprint("A"); break; case 0x1F: kprint("S"); break; case 0x20: kprint("D"); break; case 0x21: kprint("F"); break; case 0x22: kprint("G"); break; case 0x23: kprint("H"); break; case 0x24: kprint("J"); break; case 0x25: kprint("K"); break; case 0x26: kprint("L"); break; case 0x27: kprint(";"); break; case 0x28: kprint("'"); break; case 0x29: kprint("`"); break; case 0x2A: kprint("LShift"); break; case 0x2B: kprint("\\"); break; case 0x2C: kprint("Z"); break; case 0x2D: kprint("X"); break; case 0x2E: kprint("C"); break; case 0x2F: kprint("V"); break; case 0x30: kprint("B"); break; case 0x31: kprint("N"); break; case 0x32: kprint("M"); break; case 0x33: kprint(","); break; case 0x34: kprint("."); break; case 0x35: kprint("/"); break; case 0x36: kprint("Rshift"); break; case 0x37: kprint("Keypad *"); break; case 0x38: kprint("LAlt"); break; case 0x39: kprint("Spc"); break; default: // Keyup은 Keydown+0x80 if(scancode<=0x7f) { kprint("Unknown key down"); } else if(0x80<=scancode && scancode<=0x39+0x80) { kprint("key up "); print_letter(scancode-0x80); } else { kprint("Unknown key up"); } break; } }
kernels/kernel.c
#include "../drivers/screen.h" #include "../drivers/keyboard.h" #include "../cpu/isr.h" #include "../cpu/timer.h" #include "util.h" void main() { kprint("\nKernel Start\n"); isr_install(); asm volatile("sti"); // Timer init_timer(50); // Keyboard init_keyboard(); }
실행 결과
Timer
CPU timer를 테스트한 결과 Tick이 계속해서 발생하는 것을 확인할 수 있습니다.
CPU Timer 결과 Keyboard
키보드 입력을 테스트한 결과 입력한 키가 출력되는 것을 볼 수 있습니다.
키보드 입력 결과 'OS > OS from Scratch' 카테고리의 다른 글
[OS] 22. Malloc (0) 2021.07.24 [OS] 21. Shell (0) 2021.07.23 [OS] 19. Interrupt-irqs (0) 2021.07.12 [OS] 18. Interrupts (0) 2021.07.03 [OS] 17. Video Scroll (0) 2021.07.02