ABOUT ME

-

  • [OS] 20. Interrupt-timer
    OS/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
Designed by Tistory.