Project/OStrial in AMAZON

#n 중간 건너뛰고 키보드 드라이버 처리

NONE_31D 2019. 11. 24. 15:27

바로 앞 14강에서는 키보드의 입력을 받는 처리를 진행해주었다. 이번 파트에서는 입력은 되었지만 아스키코드로 변환이 되지 않는 부분을 고쳐줄 예정이다.


먼저, interrupt.h에 다음과 같이 함수 하나를 선언해준다. 

 

unsigned char transScan(unsigned char);

 

interrupt.c에 다음과 같이 transScan 함수를 구현해준다. 

일일이 잡작업하는 함수긴 하지만, 키보드의 스캔 코드에 따라 알맞은 알파벳으로 변경하는 함수이다.

스캔 코드는 키보드가 하드웨어에게 어떤 키를 눌렀는지에 대해 보내주는 데이터이다. 제일 처음 만들어진 스캔코드 세트는 1이고, 최신에 와서도 하위 호환성을 유지하기 위해 set1을 꾸준히 사용한다. 그래서 이 운영체제에서도 키보드 스캔 코드는 set1 기준으로 변환되었고, 변환 표는 아래와 같은 사이트에서 확인 가능하다.

http://users.utcluj.ro/~baruch/sie/labor/PS2/Scan_Codes_Set_1.htm

 

unsigned char transScan(unsigned char target)
{
    unsigned char result;

    switch (target) 
    {
    case 0x1E: result = 'a'break;
    case 0x30: result = 'b'break;
    case 0x2E: result = 'c'break;
    case 0x20: result = 'd'break;
    case 0x12: result = 'e'break;
    case 0x21: result = 'f'break;
    case 0x22: result = 'g'break;
    case 0x23: result = 'h'break;
    case 0x17: result = 'i'break;
    case 0x24: result = 'j'break;
    case 0x25: result = 'k'break;
    case 0x26: result = 'l'break;
    case 0x32: result = 'm'break;
    case 0x31: result = 'n'break;
    case 0x18: result = 'o'break;
    case 0x19: result = 'p'break;
    case 0x10: result = 'q'break;
    case 0x13: result = 'r'break;
    case 0x1F: result = 's'break;
    case 0x14: result = 't'break;
    case 0x16: result = 'u'break;
    case 0x2F: result = 'v'break;
    case 0x11: result = 'w'break;
    case 0x2D: result = 'x'break;
    case 0x15: result = 'y'break;
    case 0x2C: result = 'z'break;
    case 0x39: result = ' 'break// 스페이스
    case 0x0E: result = 0x08break// 백스페이스 아스키코드 = 8

    default: result = 0xFFbreak
        // 아스키로 구현이 되지 않거나, 함수 내부에서 구현이 되지 않은 코드는 넘겨버리고 0xFF로 고정한다.

    }

    return result;

}

default를 정해준 이유는 0xFF로 리턴된 모든 값을 무시하기 위해서이다. 

 

이후, 특정 라인에 써진 문자를 지울 함수를 function.h에 선언, function.c에 구현한다.

void kprintf_line_clear(int);
void kprintf_line_clear(int line)
{
    char *video = (char*)(0xB8000 + 160 * line);
    for (int i = 0; i < 160 ; i++)
    {
        *video++ = 0;
        *video++ = 0x03;
    }
}

특정 줄을 받아서 비디오 디스크립터의 주소로 넘어가 값을 바꾸게 되는 함수이다.

 

이어 idt_keyboard() 함수를 수정한다.

1. interrupt.c에 다음 두 줄 추가

static unsigned char keyboard[160] = { 0, };
static unsigned short index = 0;

 

2. interrupt.c 내부의 idt_keyboard() 수정

void idt_keyboard()
{

    __asm__ __volatile__
    (
        "push gs;"
        "push fs;"
        "push es;"
        "push ds;"
        "pushad;"
        "pushfd;"
        "xor al,al;"
        "in al, 0x60;"
    );

    __asm__ __volatile__("mov %0, al;" :"=r"(keybuf) );
    
    keybuf = transScan(keybuf);

    if (keybuf == 0x08 && index != 0
        keyboard[--index] = 0;
    else if (keybuf != 0xFF && keybuf != 0x08)
        keyboard[index++] = keybuf;

    kprintf_line_clear(8);
    kprintf(keyboard,8,0);

    __asm__ __volatile__
    (
        "mov al, 0x20;"
        "out 0x20, al;"
    );

    __asm__ __volatile__
    (
        "popfd;"
        "popad;"
        "pop ds;"
        "pop es;"
        "pop fs;"
        "pop gs;"
        "leave;"
        "nop;"
        "iretd;"
    );

}

 

앞에서 선언한 transScan에서 0x08은 백스페이스라고 언급해두었는데, 이번 함수를 통해 백스페이스를 받을 경우 버퍼의 인덱스를 앞으로 한 칸 옮기고 0을 저장하면서 값을 날리는 것을 구현하였다. 

이런식으로 입력받은 버퍼를 8줄로 출력하는데, 출력하기 전에 line_clear 함수를 통해 출력할 위치에 올라와 있는 내용을 지운다. 

 

 

수정한 파일을 저장하고 가상머신에 os를 올려 실행하면 다음과 같이 정상적으로 구동되는 것을 확인할 수 있다. 하지만 아직 숫자, 특문이나 다른 언어에

대해 구현이 되지 않았고, 그러기 위해 추가적인 기능구현이 필요하다.