보호모드로 진입하고 PE 재배치까지 한 후 메인 함수 호출을 마지막으로 고비는 넘겼다고 생각했다.
“그래~ 이제부터 내 세상이닷”
…..
화면에 HELLO 라는 문구 하나 출력 뒤에 뭘해야 할지 모르겠다…ㅠㅠ
아~~~휴~~~
뭘 해야 하지???
??? 그런데 왜 키보드 에 타이핑 하면 글자가 안 써지지??
그러고 보니 이제까지 키보드 입력에 관한 건 한번도 처리 안해 봤네???
그래… 키보드 입력 처리 해보자..
자료를 찾아보니 리얼모드에서는 int 21h 로 키보드 입력을 받는다고 한다. 당연히 난 보호모드에 진입되어 있으니 BIOS 함수들은 사용못하겠지.. 그러면 어떻게 해야 하냐??
검색!!!
헉…8259 Programmable Interrupt Controller 이건 모지??
뭐 이건… 뭐만 하려고 하면 굴비 엮듯이 줄줄이 모르는 단어들이 쏟아지는지 ㅡㅡ;;;
흠…
CPU로 인터럽트를 전달하기 위한 칩이란다. Master와 Slave 구조로 되어 있고 Master가 CPU와 연결되어 있단다. Master와 Slave는 각각 8개 입력 라인을 받을 수 있는데 Master 1개 입력 라인이 Slave와 연결되어 있으므로 총 15개 입력 라인을 사용 할수 있다고 한다. CPU가 여러개일 경우 각각 CPU에 연결 할 수 없기 때문에(?) Advanced PIC(APIC)가 나왔고 CPU 자체에 내장된 Local APIC 와 메인보드에 붙어 있는 I/O APIC로 발전 되었다고 하는데… CPU 여러개일 때는 모르겠고 …ㅡㅡ;;;
여튼 키보드에서 키가 입력 되었을 때, 8259 에서는 인터럽트 신호가 발생하고 CPU에서는 하던일을 멈추고 입력된 데이터를 읽는다 라는게 그 흐름 같아 보인다. (뭐 인터럽트에 대한 일반적인….내용).
리얼모드에서는 0~F 까지 (15개 입력라인을 가질 수 있다고 했다.) IRQ0~IRQ15까지 번호가 지정되었는데 보호모드에서는 IBM 초기 아키텍쳐 설계 실수로 충돌이 난다고 한다.(CPU 인터럽트와 충돌) 그래서 이 IRQ를 옮겨야 한다고 한다.(더 깊은 설명이 있는데… 번역이 대략 난감 ㅡㅡ…)
그럼 어떻게 옮겨???
8259 칩은 0x20 은 Master, 0xA0라는 Slave 명령 IO 포트가 있고 0x21은 master Data IO 포트, 0xA1은 Slave Data IO 포트가 있다. 명령 IO 포트에 명령을 보내고 데이터 IO 포트에 데이터를 보내면 된다고 한다. 그냥 가져다 쓰자 ㅡㅡ;;;;
예제는..
/* Init master interrupt controller */ outb(0x11, 0x20); /* Start init sequence */ outb(0x20, 0x21); /* Vector base */ outb(0x04, 0x21); outb(0x01, 0x21); outb(0xFF, 0x21); /* Init slave interrupt controller */ outb(0x11, 0xA0); /* Start init sequence */ outb(0x28, 0xA1); /* Vector base */ outb(0x02, 0xA1); outb(0x01, 0xA1); outb(0xFF, 0xA1);
이렇다는데… outb 함수 2번째 인자가 IO 포트 이고 1번째 인자가 명령 또는 데이터 값인가 보다. outb 함수가 IO포트에 쓰는(?) 함수인것 같은데 어셈블리어로 구성되어 있고 out 명령어를 사용한다. 이건 또 모지???
out : IO 포트에 데이터를 쓰는 명령어..
예제좀 바꿔야 겠다. outb 함수명 보다는 다른 이름으로~^^
흠… 그냥 인라인 어셈으로 해야 하나??
void Write_port_byte(unsigned short port, unsigned char data) { __asm { mov edx, [ebp+8] mov al, [ebp+12] out dx, al } } void InitPIC() { /* Init master interrupt controller */ Write_port_byte(0x20, 0x11); /* Start init sequence */ Write_port_byte(0x21, 0x20); /* Vector base */ Write_port_byte(0x21, 0x04); Write_port_byte(0x21, 0x01); Write_port_byte(0x21, 0xFF); /* Init slave interrupt controller */ Write_port_byte(0xA0, 0x11); /* Start init sequence */ Write_port_byte(0xA1, 0x28); /* Vector base */ Write_port_byte(0xA1, 0x02); Write_port_byte(0xA1, 0x01); Write_port_byte(0xA1, 0xFF); }
되는지 안되는지는 모르겠다… 8259 PIC 초기화(?) 는 이정도로 끝마치고 이제.. 키보드 입력을 받는 부분을 만들자…
8259 PIC 의 0x60 포트를 읽으면 키 입력값이 들어온다고 한다.
0x60 포트에서 입력된 키 값은 아스키 코드값이 아니라고 한다.. 이를 다시 아스키 코드로 변환해야 한단다.. 흠.. 아.. 그러고 보니 이제까지 0xB8000 메모리 주소에 아스키 값 써서 화면에 글자 출력 하는 방법을 썼는데 아무래도 printf 함수도 하나 만들어야 겠다. 할게 많네 …갈길이 멀다..
우선 printf 함수 부터…
unsigned char *Screen = (unsigned char*)0xB8000; void printf( char *msg ) { while(*msg != '\0') { *Screen++ = *msg++; *Screen++ = 7; } } void putc(char Code) { *Screen++ = Code; *Screen++ = 7; }
전역변수로 Screen 변수를 선언하고 이제 printf() 함수를 호출해서 문자열을 출력하고 putc() 함수를 호출 해서 문자를 호출 하는 함수를 만들었다. 나중에 printf 함수를 손좀 봐서 C Standard library를 구현해야 겠다. 안돼면 가져오는 걸~^^로 하고 지금은 임시…
키보드 스캔코드를 아스키 코드로 변환할 변환 테이블 만들고(만든다긴 보다는 그냥 인터넷 검색.)….
0x60 포트에서 키보드 값 읽어서 화면에 출력… 이걸 무한 반복..
void InitPIC(); unsigned char Read_port_byte(unsigned short port); void Write_port_byte(unsigned short port, unsigned char data); unsigned char *Screen = (unsigned char*)0xB8000; #define KEY_F1 0x80 #define KEY_F2 (KEY_F1 + 1) #define KEY_F3 (KEY_F2 + 1) #define KEY_F4 (KEY_F3 + 1) #define KEY_F5 (KEY_F4 + 1) #define KEY_F6 (KEY_F5 + 1) #define KEY_F7 (KEY_F6 + 1) #define KEY_F8 (KEY_F7 + 1) #define KEY_F9 (KEY_F8 + 1) #define KEY_F10 (KEY_F9 + 1) #define KEY_F11 (KEY_F10 + 1) #define KEY_F12 (KEY_F11 + 1) #define KEY_INS 0x90 #define KEY_DEL (KEY_INS + 1) #define KEY_HOME (KEY_DEL + 1) #define KEY_END (KEY_HOME + 1) #define KEY_PGUP (KEY_END + 1) #define KEY_PGDN (KEY_PGUP + 1) #define KEY_LFT (KEY_PGDN + 1) #define KEY_UP (KEY_LFT + 1) #define KEY_DN (KEY_UP + 1) #define KEY_RT (KEY_DN + 1) #define KEY_PRNT (KEY_RT + 1) #define KEY_PAUSE (KEY_PRNT + 1) #define KEY_LWIN (KEY_PAUSE + 1) #define KEY_RWIN (KEY_LWIN + 1) #define KEY_MENU (KEY_RWIN + 1) static const unsigned char kbd_scancode[] = { 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' ', 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,0, 0, KEY_HOME, KEY_UP, KEY_PGUP,'-', KEY_LFT,'5', KEY_RT, '+', KEY_END, KEY_DN, KEY_PGDN,KEY_INS,KEY_DEL,0, 0, 0, KEY_F11, KEY_F12 }; void printf( char *msg ) { while(*msg != '\0') { *Screen++ = *msg++; *Screen++ = 7; } } void putc(char Code) { *Screen++ = Code; *Screen++ = 7; } int StartKernel(int argc, char *argv[]) { char *hello = "Hello OS!"; unsigned char* vidmem = (unsigned char*)0xB8000; printf(hello); InitPIC(); while(1) { data = Read_port_byte(0x60); if( data != 0 ) { putc((char)kbd_scancode[data]); } } return 0; } unsigned char Read_port_byte(unsigned short port) { unsigned char KeyCode = 0; __asm { mov dx, port lea ebx, KeyCode in ax, dx mov [ebx], ax } return KeyCode; } void Write_port_byte(unsigned short port, unsigned char data) { __asm { mov dx, [ebp+8] mov al, [ebp+12] out dx, al } } void InitPIC() { /* Init master interrupt controller */ Write_port_byte(0x20, 0x11); /* Start init sequence */ Write_port_byte(0x21, 0x20); /* Vector base */ Write_port_byte(0x21, 0x04); Write_port_byte(0x21, 0x01); Write_port_byte(0x21, 0xFF); /* Init slave interrupt controller */ Write_port_byte(0xA0, 0x11); /* Start init sequence */ Write_port_byte(0xA1, 0x28); /* Vector base */ Write_port_byte(0xA1, 0x02); Write_port_byte(0xA1, 0x01); Write_port_byte(0xA1, 0xFF); }
자.. 잘되나 볼까???
뭐지… 그냥 이상한 값 출력 하다가 Virtual PC 창이 닫혀 버린다… 뭐가 잘못 된거지?? 음…계속 키 값이 입력 되는 것인가?? 다시 한번 확인…
음… 값은 키값이 반복되는 모습이다… 그런데 난 키보드 안 눌렀는데?? 키보드 버퍼에 쓰레기 값이 있는건가?? 우선 0x64 에서 현재 키보드 상태값을 읽는 부분이 있다. 출력 버퍼 상태라든지 입력 버퍼 상태 등이 기록된다는데 출력버퍼(출력인지 입력인지 잘 모르겠지만, 키가 입력되었으니 CPU 에게는 출력해야 한다고 가정…)의 상태가 체크 되어 있으면 화면에 출력 하는 루틴으로 수정해 보잣!!
int StartKernel(int argc, char *argv[]) { char *hello = "Hello OS!"; char *complete = "complete."; unsigned char* vidmem = (unsigned char*)0xB8000; unsigned char data = 0; printf(hello); InitPIC(); while(1) { while((Read_port_byte(0x64) & 1) == 0 ) ; data = Read_port_byte(0x60); if( data != 0 ) { putc((char)kbd_scancode[data]); } } return 0; }
으흐흐흐 잘 된다..
키 하나 입력 될 때마다 하나 하나 화면에 찍힌다…
그런데 키 하나 입력 될때 마다 공백이 생기네??? 음…
아… Key down 과 Key up 이 때문에 key down 때는 글자가 찍히고 Key up은 다른 값으로 찍히나 보다.. 맞나?? 검색…
아.. 키보드에서 a 키를 입력(down)하면 1E 가 발생하고 a 키 UP은 1E | 0x80 = 9E 가 발생한다.. 0x80 비트가 Key down/up 을 나타내는 것 같다. 그러면 저기 코드에서 0x80 비트가 체크 되지 않을 때만 화면에 출력 하는 코드로 수정… 다시 확인.
void InitPIC(); unsigned char Read_port_byte(unsigned short port); void Write_port_byte(unsigned short port, unsigned char data); unsigned char *Screen = (unsigned char*)0xB8000; #define KEY_F1 0x80 #define KEY_F2 (KEY_F1 + 1) #define KEY_F3 (KEY_F2 + 1) #define KEY_F4 (KEY_F3 + 1) #define KEY_F5 (KEY_F4 + 1) #define KEY_F6 (KEY_F5 + 1) #define KEY_F7 (KEY_F6 + 1) #define KEY_F8 (KEY_F7 + 1) #define KEY_F9 (KEY_F8 + 1) #define KEY_F10 (KEY_F9 + 1) #define KEY_F11 (KEY_F10 + 1) #define KEY_F12 (KEY_F11 + 1) #define KEY_INS 0x90 #define KEY_DEL (KEY_INS + 1) #define KEY_HOME (KEY_DEL + 1) #define KEY_END (KEY_HOME + 1) #define KEY_PGUP (KEY_END + 1) #define KEY_PGDN (KEY_PGUP + 1) #define KEY_LFT (KEY_PGDN + 1) #define KEY_UP (KEY_LFT + 1) #define KEY_DN (KEY_UP + 1) #define KEY_RT (KEY_DN + 1) #define KEY_PRNT (KEY_RT + 1) #define KEY_PAUSE (KEY_PRNT + 1) #define KEY_LWIN (KEY_PAUSE + 1) #define KEY_RWIN (KEY_LWIN + 1) #define KEY_MENU (KEY_RWIN + 1) static const unsigned char kbd_scancode[] = { 0, 0x1B, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0, ' ', 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,0, 0, KEY_HOME, KEY_UP, KEY_PGUP,'-', KEY_LFT,'5', KEY_RT, '+', KEY_END, KEY_DN, KEY_PGDN,KEY_INS,KEY_DEL,0, 0, 0, KEY_F11, KEY_F12 }; void printf( char *msg ) { while(*msg != '\0') { *Screen++ = *msg++; *Screen++ = 7; } } void putc(char Code) { *Screen++ = Code; *Screen++ = 7; } int StartKernel(int argc, char *argv[]) { char *hello = "Hello OS!"; char *complete = "complete."; unsigned char* vidmem = (unsigned char*)0xB8000; unsigned char data = 0; printf(hello); InitPIC(); while(1) { while((Read_port_byte(0x64) & 1) == 0 ) ; data = Read_port_byte(0x60); if(data & 0x80) continue; if( data != 0 ) { putc((char)kbd_scancode[data]); } } return 0; } unsigned char Read_port_byte(unsigned short port) { unsigned char KeyCode = 0; __asm { mov dx, port xor eax, eax lea ebx, KeyCode in ax, dx mov [ebx], ax } return KeyCode; } void Write_port_byte(unsigned short port, unsigned char data) { __asm { mov dx, [ebp+8] mov al, [ebp+12] out dx, al } } void InitPIC() { /* Init master interrupt controller */ Write_port_byte(0x20, 0x11); /* Start init sequence */ Write_port_byte(0x21, 0x20); /* Vector base */ Write_port_byte(0x21, 0x04); Write_port_byte(0x21, 0x01); Write_port_byte(0x21, 0xFF); /* Init slave interrupt controller */ Write_port_byte(0xA0, 0x11); /* Start init sequence */ Write_port_byte(0xA1, 0x28); /* Vector base */ Write_port_byte(0xA1, 0x02); Write_port_byte(0xA1, 0x01); Write_port_byte(0xA1, 0xFF); }
오케이… 잘된다!!!!
너무 잘되니.. 손이 부들부들… ㅎㅎㅎㅎ
음.. 다 좋은데 키 입력을 이렇게 받을 순 없다.. ㅡㅡ;;;
키입력 관련해서 검색 해 보니 전에 잠깐 언급했던 IDT(Interrupt descriptor table)을 이용해서 키 입력 처리를 하네.. 흠…. 할게 너무 많어 ㅠㅠ
이젠 IDT에 대해서 알아보고 키보드 입력 처리를 해보잣..
'프로그래밍 > OS 만들기' 카테고리의 다른 글
OS 만들기 #17 IDT - 2 (0) | 2015.08.10 |
---|---|
OS 만들기 #16 IDT - 1 (0) | 2015.07.30 |
OS 만들기 #14 PE 재배치 (0) | 2014.03.28 |
OS 만들기 #13 보호모드 진입 (0) | 2014.03.27 |
OS 만들기 #12 - PE 파일 포맷 (0) | 2013.09.10 |