-
[OS] 16. Video DriverOS/OS from Scratch 2021. 7. 2. 14:08
서론
이번 강의에서는 이전 강의에서 사용했던 이론과 코드를 응용해 화면에 문자열을 출력해보겠습니다. 해당 Github 강의는 다음과 같습니다.
https://github.com/jschang0215/os-tutorial/tree/master/16-video-driver
jschang0215/os-tutorial
How to create an OS from scratch. Contribute to jschang0215/os-tutorial development by creating an account on GitHub.
github.com
코드
screen.h
#define VIDEO_ADDRESS 0xb8000 #define MAX_ROWS 25 #define MAX_COLS 80 #define WHITE_ON_BLACK 0x0f #define RED_ON_WHITE 0xf4 // 스크린 I/O 포트 #define REG_SCREEN_CTRL 0x3d4 #define REG_SCREEN_DATA 0x3d5 // Public Kernel API void clear_screen(); void kprint_at(char *message, int col, int row); void kprint(char *message);
screen.c
kprint_at, kprint 함수를 통해 문자열을 출력할 수 있습니다. 코드가 비교적 직관적이어서 설명은 주석으로 대체하겠습니다.
#include "screen.h" #include "ports.h" // Private Function 선언 int get_cursor_offset(); void set_cursor_offset(int offset); int print_char(char c, int col, int row, char attr); int get_offset(int col, int row); int get_offset_row(int offset); int get_offset_col(int offset); // Public Kernel API Functions // 특정 위치에 문자열 출력 // col, row 음수이면 현재 offset에 출력 void kprint_at(char *message, int col, int row) { int offset; if(col>=0 && row>=0) { offset = get_offset(col, row); } else { offset = get_cursor_offset(); row = get_offset_row(offset); col = get_offset_col(offset); } int i=0; while(message[i]!=0) { offset = print_char(message[i++], col, row, WHITE_ON_BLACK); row = get_offset_row(offset); col = get_offset_col(offset); } } // kprint_at의 기본 Wrapper void kprint(char *message) { kprint_at(message, -1, -1); } // Private Kernel Functions // VGA 직접 접근해 문자 출력 // col, row 음수이면 현재 커서 위치에 출력 // attr 0이면 white on black // 다음 문자의 offset 리턴 // 커서를 리턴된 offset위치로 이동 int print_char(char c, int col, int row, char attr) { unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS; if(!attr) { attr = WHITE_ON_BLACK; } // 좌표가 범위 밖일 때 경고 문자 출력 if(col>=MAX_COLS || row>=MAX_ROWS) { vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E'; vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE; return get_offset(col, row); } int offset; if(col>=0 && row>=0) { offset = get_offset(col, row); } else { offset = get_cursor_offset(); } // 줄바꿈일때는 현재 row에 +1 if(c=='\n') { row = get_offset_row(offset); offset = get_offset(0, row+1); } else { vidmem[offset] = c; vidmem[offset+1] = attr; offset += 2; } set_cursor_offset(offset); return offset; } // VGA port이용해 현재 커서 위치 반환 int get_cursor_offset() { // (data 14)로 상위 바이트로 커서의 offset 알아냄 port_byte_out(REG_SCREEN_CTRL, 14); int offset = port_byte_in(REG_SCREEN_DATA) << 8; port_byte_out(REG_SCREEN_CTRL, 15); offset += port_byte_in(REG_SCREEN_DATA); // offset은 커서위치의 2배 (문자+설정) return offset*2; } // 커서 위치 설정 void set_cursor_offset(int offset) { // 하나의 셀은 2개의 데이터로 이루어짐 offset /= 2; port_byte_out(REG_SCREEN_CTRL, 14); // 레지스터에 입력 port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8)); port_byte_out(REG_SCREEN_CTRL, 15); port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff)); } // 빈 화면으로 void clear_screen() { int screen_size = MAX_ROWS*MAX_COLS; int i; char *screen = VIDEO_ADDRESS; for(i=0; i<screen_size; i++) { screen[i*2] = ' '; screen[i*2+1] = WHITE_ON_BLACK; } set_cursor_offset(get_offset(0, 0)); } int get_offset(int col, int row) { return 2*(row*MAX_COLS + col); } int get_offset_row(int offset) { return offset/(2*MAX_COLS); } int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }
kernel.c
#include "../drivers/screen.h" void main() { clear_screen(); kprint_at("X", 1, 6); kprint_at("This text spans multiple lines", 75, 10); kprint_at("There is a line\nbreak", 0, 20); kprint("There is a line\nbreak"); kprint_at("What happens when we run out of space?", 45, 24); }
실행 결과
make로 실행하면, 문자열들이 출력된 것을 확인할 수 있습니다.
실행결과 'OS > OS from Scratch' 카테고리의 다른 글
[OS] 18. Interrupts (0) 2021.07.03 [OS] 17. Video Scroll (0) 2021.07.02 [OS] 15. Video Ports (0) 2021.06.30 [OS] 14. Checkpoint (0) 2021.06.28 [OS] 13. Kernel Barebones (0) 2021.06.27