Newer
Older
tree-os / src / kernel / drivers / textMode / terminal.c
#include <cursor.h>
#include <terminal.h>
#include <util.h>

uint16_t *framebuffer = (uint16_t *)FRAMEBUFFER_LOCATION;
uint8_t currentFormat = 0x0A;

void setTextStyle(uint8_t style) { currentFormat = style; }

void setCharAtOffset(uint16_t offset, char c, uint8_t format) {
    *((uint16_t *)FRAMEBUFFER_LOCATION + offset) = format << 8 | c;
}

uint16_t getOffset(uint8_t x, uint8_t y) { return VIDEO_WIDTH * y + x; }

void clearScreen() {
    setCursorOffset(0);
    for (int y = 0; y < VIDEO_HEIGHT; y++) {
        for (int x = 0; x < VIDEO_WIDTH; x++) {
            setCharAtOffset(getOffset(x, y), ' ', currentFormat);
        }
    }
}

void shiftUp() {
    for (int y = 1; y < VIDEO_HEIGHT; y++) {
        for (int x = 0; x < VIDEO_WIDTH; x++) {
            uint16_t old =
                *(uint16_t *)(FRAMEBUFFER_LOCATION + getOffset(x, y) * 2);
            setCharAtOffset(getOffset(x, y - 1), old, old >> 8);
        }
    }
    for (int x = 0; x < VIDEO_WIDTH; x++) {
        setCharAtOffset(getOffset(x, VIDEO_HEIGHT - 1), ' ', currentFormat);
    }
}

void shiftDown() {
    for (int y = VIDEO_HEIGHT - 1; y >= 0; y--) {
        for (int x = 0; x < VIDEO_WIDTH; x++) {
            uint16_t old =
                *(uint16_t *)(FRAMEBUFFER_LOCATION + getOffset(x, y) * 2);
            setCharAtOffset(getOffset(x, y + 1), old, old >> 8);
        }
    }
    for (int x = 0; x < VIDEO_WIDTH; x++) {
        setCharAtOffset(x, ' ', currentFormat);
    }
}

uint16_t cursorOffset = 0;

void newLine() {
    uint8_t x = cursorOffset % VIDEO_WIDTH;
    uint8_t y = cursorOffset / VIDEO_WIDTH;
    x = 0;
    y++;
    if (y >= VIDEO_HEIGHT) {
        shiftUp();
        y--;
    }
    cursorOffset = getOffset(x, y);
    setCursorOffset(cursorOffset);
}

typedef enum { STANDARD, ANSI_ESCAPE } States;

typedef enum { BRACKET_OPEN, BUFFER } AnsiEscapeStates;

States currentState = STANDARD;

AnsiEscapeStates currentAnsiEscapeState = BRACKET_OPEN;

uint32_t ansiEscapeBuffer1 = 0;
uint32_t ansiEscapeBuffer2 = 0;

uint32_t *currentBuffer = &ansiEscapeBuffer1;

void writeChar(char c) {
    if (c == 0)
        return;
    switch (currentState) {
    case STANDARD:
        if (c == '\x1B') {
            currentState = ANSI_ESCAPE;
            currentAnsiEscapeState = BRACKET_OPEN;
        }
        break;
    case ANSI_ESCAPE:
        switch (currentAnsiEscapeState) {
        case BRACKET_OPEN:
            currentAnsiEscapeState = BUFFER;
            return;
        case BUFFER:
            switch (c) {
            case '1':
                *currentBuffer += 1;
            case '2':
                *currentBuffer += 2;
            case '3':
                *currentBuffer += 3;
            case '4':
                *currentBuffer += 4;
            case '5':
                *currentBuffer += 5;
            case '6':
                *currentBuffer += 6;
            case '7':
                *currentBuffer += 7;
            case '8':
                *currentBuffer += 8;
            case '9':
                *currentBuffer += 9;
            case '0':
                *currentBuffer *= 10; // shift right in decimal
                break;
            case 'H':
                cursorOffset = 0;
                setCursorOffset(0);
                currentState = STANDARD;
                return;
            case 'J':
                clearScreen();
                currentState = STANDARD;
                return;
            case 'A':
                if (cursorOffset <= 0) {
                    shiftDown();
                } else {
                    cursorOffset -= VIDEO_WIDTH;
                    setCursorOffset(cursorOffset);
                }
                currentState = STANDARD;
                return;
            case 'B':
                if (cursorOffset / VIDEO_WIDTH + 1 >= VIDEO_HEIGHT) {
                    shiftUp();
                } else {
                    cursorOffset += VIDEO_WIDTH;
                    setCursorOffset(cursorOffset);
                }
                currentState = STANDARD;
                return;
            case 'C':
                if ((cursorOffset + 1) / VIDEO_WIDTH >= VIDEO_HEIGHT) {
                    newLine();
                } else {
                    cursorOffset++;
                    setCursorOffset(cursorOffset);
                }
                currentState = STANDARD;
                return;
            case 'D':
                if (cursorOffset == 0) {
                    cursorOffset = VIDEO_WIDTH - 1;
                    newLine();
                } else {
                    cursorOffset--;
                }
                setCursorOffset(cursorOffset);
                currentState = STANDARD;
                return;
            }
        }
    }
    if (currentState != STANDARD) {
        return;
    }
    switch (c) {
    case '\n':
        newLine();
        return;
    case '\b':
        setCharAtOffset(--cursorOffset, ' ', currentFormat);
        setCursorOffset(cursorOffset);
        return;
    }
    setCharAtOffset(cursorOffset++, c, currentFormat);
    setCursorOffset(cursorOffset);
}