9. 키보드 디바이스 드라이버를 추가하자

들어가기

키보드를 제어하는 방법에 대해서 알아보고 스캔 코드를 ASCII 코드로 변환해보자

본론

9.1 키보드 컨트롤러의 구조와 기능

9.1.1 키보드 컨트롤러, I/O 포트, 레지스터

  • 할당된 포트는 두 개이지만 포트에서 데이터를 읽을 때와 쓸 때 접근하는 레지스터가 다름.
  • 그중 상태 레지스터는 키보드 컨트롤러의 상태를 표시하는 레지스터.

9.2 키보드 컨트롤러 제어

9.2.1 키보드와 키보드 컨트롤러 활성화

  • BIOS에 의해 키보드는 활성화된 상태이지만 만약을 대비해 직접 활성화.
  • 키보드 컨트롤러에서 키보드 디바이스를 활성화하려면 커맨드(OxAE) 전송.
  • 키보드 컨트롤러에서 키보드 데이터를 받을 준비가 된 것이므로 따로 키보드도 활성화 시킴.
    • 입력 버퍼에 키보드로 보낼 커맨드를 직접 입력.
    • 정상적으로 처리한 경우 응답(0xFA) 전송.

9.2.2 IA-32e 모드의 호출 규약

  • IA-32e 모드의 C 호출 규약과 보호 모드의 C 호출 규약을 비교하면 3가지 차이.
    1. 파라미터를 전달할 때 레지스터 우선 사용.
    2. 레지스터 또는 스택에 파라미터를 삽입하는 순서가 다름.
    3. 반환 값을 사용하는 레지스터의 차이.

9.2.3 키보드 컨트롤러에서 키 값 읽기

 BYTE kGetKeyboardScanCode(void) {
    // 출력 버퍼에 데이터가 있을 때까지 대기.
    while(kIsOutBufferFull() == FALSE) {
        ;
    }
    // 출력 버퍼에서 키 값을 읽어서 반환.
    return kInPortByte(0x60);
}
  • 키보드의 키가 눌리거나 떨어질 때마가 키 별로 할당된 스캔 코드가 출력 버퍼에 쌓임.
  • 상태 레지스터를 읽어서 출력 버퍼에 데이터를 확인한 후, 읽어서 저장.

9.2.4 A20 게이트 활성화와 프로세서 리셋

  • A20 게이트 비트와 프로세서 리셋 비트는 출력 포트의 비트를 변경하면서 적용.
  • 출력 포트는 0xD0, 0xD1 커맨드로 접근.
  • 프로세서를 리셋하는 방법은 출력 포트의 데이터를 0으로 설정.
  • A20 게이트를 활성화하는 방법은 0xD0으로 데이터를 읽고 비트 1을 1로 설정 후, 0xD1으로 전달.

9.2.5 키보드 LED 상태 제어

  • 키보드의 LED 상태를 변경하려면 입력 버퍼로 0xED 커맨드 전송해 LED 정보가 전송 될 것을 알림.
  • ACK을 확인한 후, LED 상태 정보 전송.

9.3 스캔 코드와 간단한 쉘

9.3.1 키보드와 스캔 코드

  • 키보드의 모든 키는 각자의 고유 코드를 가지고 있으며, 키보드는 키가 눌리거나 떨어질 때마다 그 상태에 해당하는 키 값을 키보드 컨트롤러에 전달.
  • 일반적으로 떨어졌을 때의 키 값은 눌러졌을 때의 값에 비트 7을 1로 설정한 것.
  • 스캔 코드와 우리가 사용하는 ASCII 코드는 일치하지 않아 스캔 코드의 변경이 필요.

9.3.2 스캔 코드를 ASCII 문자로 변환

  • 확장 키를 제외한 스캔 코드를 테이블을 사용해 변환.
typedef struct kKeyMappingEntryStruct {
    // 조합되지 않은 일반 키.
    BYTE bNormalCode;
    // 조합된 키.
    BYTE bCombinedCode;
} KEYMAPPINGENTRY
  • 매핑 테이블을 만들기 전 엔트리 자료구조 정의.

9.4 키보드 디바이스 드라이버의 통합과 빌드

9.4.1 키보드 디바이스 드라이버 파일 추가

Keyboard.h 코드 보기
Keyboard.c 코드 보기

9.4.2 어셈블리어 유틸리티 파일 추가

AssemblyUtility.asm 코드 보기
AssemblyUtility.h 코드 보기

9.4.3 C 언어 커널 엔트리 포인트 파일 수정

C 언어 커널 코드 보기

9.4.4 빌드와 실행

마치며

끝!

Share