ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [OS] 16. Video Driver
    OS/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
Designed by Tistory.