CeDOS - kernel/drivers/vga_console.c

kernel/drivers/vga_console.c (view raw)

#include "drivers/console.h"
#include "linker.h"
#include "assembly.h"
#include "string.h"

#define VGA_TEXTMODE_COLUMNS 80
#define VGA_TEXTMODE_LINES 25
#define VGA_TEXTMODE_BPC 2
#define VGA_TEXTMODE_LINE_WIDTH (VGA_TEXTMODE_COLUMNS * VGA_TEXTMODE_BPC)
#define VGA_TEXTMODE_CELLS (VGA_TEXTMODE_COLUMNS * VGA_TEXTMODE_LINES)
#define VGA_MEM_POS(line, column) ((line) * VGA_TEXTMODE_COLUMNS * VGA_TEXTMODE_BPC + (column) * VGA_TEXTMODE_BPC)
#define VGA_MEM_CELL(line, column) ((line) * VGA_TEXTMODE_COLUMNS + (column))

#define VGA_TEXTMODE_MEM ((uint8_t*)0xB8000)
#define VGA_MEM_VALUE(line, column) (VGA_TEXTMODE_MEM[VGA_MEM_POS((line), (column))])
#define VGA_MEM_COLOR(line, column) (VGA_TEXTMODE_MEM[VGA_MEM_POS((line), (column)) + 1])
#define VGA_MEM_ADDR(line, column) (&(VGA_MEM_VALUE((line), (column))))

#define VGA_INDEX_REG ((uint16_t)0x3D4)
#define VGA_DATA_REG ((uint16_t)0x3D5)

uint32_t line = 0;
uint32_t column = 0;

uint32_t saved_line = 0;
uint32_t saved_column = 0;

uint8_t color = 0x0F;

int vga_con_init(void);
void vga_con_write_c(const char c);
void vga_con_write_n(const char *string, uint32_t num);
void vga_con_write_s(const char *string);
void vga_con_clear(int mode);
void vga_con_clear_line(int mode);
void vga_con_newline(void);
void vga_con_backspace(void);

CON_DRIVER vga_con = {
    "VGA console",
    vga_con_init,
    vga_con_write_c,
    vga_con_write_n,
    vga_con_write_s,
    vga_con_clear,
    vga_con_newline,
    vga_con_backspace
};

__attribute((always_inline)) inline void set_char(char value) {
    VGA_MEM_VALUE(line, column) = value;
    VGA_MEM_COLOR(line, column) = color;
}

__attribute((always_inline)) inline void lfcr() {
    line++;
    column = 0;
    
    if (line >= VGA_TEXTMODE_LINES) {
        memcpy(
                VGA_MEM_ADDR(0, 0), 
                VGA_MEM_ADDR(1, 0), 
                VGA_TEXTMODE_LINE_WIDTH * (VGA_TEXTMODE_LINES - 1)
            );
        memset(
                VGA_MEM_ADDR(VGA_TEXTMODE_LINES - 1, 0), 
                0, 
                VGA_TEXTMODE_LINE_WIDTH
            );
        line = VGA_TEXTMODE_LINES - 1;
    }
}

__attribute((always_inline)) inline void write_char(char value) {
    if (column >= VGA_TEXTMODE_COLUMNS  || value == '\n') {
        lfcr();
    }
    
    switch (value) {
        case '\n':
            break;
        case '\0':
            break;
        default:
            set_char(value);
            column++;
            break;
    }
}

__attribute((always_inline)) inline void set_cursor(uint16_t line, uint16_t column) {
    uint16_t pos = line * VGA_TEXTMODE_COLUMNS + column;
    outb(0x0F, VGA_INDEX_REG);
    outb((uint8_t)(pos & 0xFF), VGA_DATA_REG);
    outb(0x0E, VGA_INDEX_REG);
    outb((uint8_t)(pos >> 8), VGA_DATA_REG);
}

void enable_cursor(void) {
    outb(0x0A, VGA_INDEX_REG);
    outb((inb(VGA_DATA_REG) & 0x0C) | 0x00, VGA_DATA_REG);
    outb(0x0B, VGA_INDEX_REG);
    outb((inb(VGA_DATA_REG) & 0xE0) | 0x0F, VGA_DATA_REG);
}

int vga_con_init(void) {
    vga_con_clear(2);
    
    enable_cursor();
    set_cursor(line, column);
    
    for (uint32_t i = 0; i < 320 * 200; i++) {
        uint8_t* disp = (uint8_t*)0xA0000;
        disp[i] = (uint8_t)i;
    }
    
    return 1;
}

void vga_con_write_c(const char c) {
    static enum {
        NORMAL = 0,
        ESCAPE_START = 1,
        ESCAPE_N = 2,
        ESCAPE_M = 3,
        ESCAPE_C = 4
    } state;
    static int n, m;

    if (state == NORMAL && c == '\e') {
        // beginning of escape sequence
        state = ESCAPE_START;
        n = 0;
        m = 0;
    } else if (state == ESCAPE_START && c == '[') {
        state = ESCAPE_N;
    } else if (state == ESCAPE_N && c >= '0' && c <= '9') {
        n = n * 10 + (c - '0');
    } else if (state == ESCAPE_M && c >= '0' && c <= '9') {
        m = m * 10 + (c - '0');
    } else if (state == ESCAPE_N && c == ';') {
        state = ESCAPE_M;
    } else if (state == ESCAPE_N && c == 'G') {
        column = n;
        //set_cursor(line, column);
        state = NORMAL;
    } else if (state == ESCAPE_M && c == 'H') {
        line = n;
        column = m;
        //set_cursor(line, column);
        state = NORMAL;
    } else if (state == ESCAPE_N && c == 'J') {
        vga_con_clear(n);
        state = NORMAL;
    } else if (state == ESCAPE_N && c == 'K') {
        vga_con_clear_line(n);
        state = NORMAL;
    } else if (state == ESCAPE_N && c == 'm') {
        uint8_t color_compat[8] = { 0x00, 0x04, 0x02, 0x06, 0x01, 0x05, 0x03, 0x07 };
        if (n == 0) {
            color = 0x0F;
        } else if (n >= 30 && n <= 37) {
            color = (color & 0xF0) | color_compat[n - 30];
        } else if (n >= 40 && n <= 47) {
            color = (color & 0x0F) | (color_compat[n - 40] << 4);
        } else if (n >= 90 && n <= 97) {
            color = (color & 0xF0) | (color_compat[n - 90] + 8);
        } else if (n >= 100 && n <= 107) {
            color = (color & 0x0F) | ((color_compat[n - 100] + 8) << 4);
        } else if (n < 106) {
            color = n - 90 + 8;
        }
        state = NORMAL;
    } else if (state == ESCAPE_N && c == 's') {
        saved_line = line;
        saved_column = column;
        state = NORMAL;
    } else if (state == ESCAPE_N && c == 'u') {
        line = saved_line;
        column = saved_column;

        saved_line = 0;
        saved_column = 0;
        state = NORMAL;
    } else if (state != NORMAL) {
        state = NORMAL;
    } else if (c == 0x08) {
        vga_con_backspace();
        //set_cursor(line, column);
    } else {
        write_char(c);
        //set_cursor(line, column);
    }
}

void vga_con_write_n(const char *string, uint32_t num) {
    for (uint32_t i = 0; i < num; i++) {
        vga_con_write_c(string[i]);
    }
    set_cursor(line, column);
}

void vga_con_write_s(const char *string) {
    while (*string) {
        write_char(*(string++));
    }
    set_cursor(line, column);
}

void vga_con_clear_line(int mode) {
    // clear from cursor to end of screen
    // clear from beginning of screen to cursor
    // clear whole display
    int start, end;
    
    switch(mode) {
    case 0:
        start = column;
        end = VGA_TEXTMODE_COLUMNS;
        break;
    case 1:
        start = 0;
        end = column;
        break;
    default:
    case 2:
        start = 0;
        end = VGA_TEXTMODE_COLUMNS;
        break;
    }

    for (int i = start; i < end; i++) {
        VGA_MEM_VALUE(line, i) = 0;
        VGA_MEM_COLOR(line, i) = color;
    }
}

void vga_con_clear(int mode) {
    int cursor_pos = VGA_MEM_CELL(line, column);
    int start, end;

    switch(mode) {
    case 0:
        start = cursor_pos;
        end = VGA_TEXTMODE_CELLS;
        break;
    case 1:
        start = 0;
        end = cursor_pos;
        break;
    default:
    case 2:
        start = 0;
        end = VGA_TEXTMODE_CELLS;
        break;
    }

    for (int i = 0; i < VGA_TEXTMODE_CELLS; i++) {
        VGA_TEXTMODE_MEM[2 * i] = 0;
        VGA_TEXTMODE_MEM[2 * i + 1] = color;
    }
    
    line = 0;
    column = 0;
}

void vga_con_newline(void) {
    lfcr();
}

void vga_con_backspace(void) {
    if (column == 0 && line > 0) {
        line--;
        column = VGA_TEXTMODE_COLUMNS - 1;
    } else if (column > 0) {
        column--;
    }

    set_char(0);
    set_cursor(line, column);
}