hypothalamus 0.6.0

An optimizing Brainfuck AOT compiler with an LLVM IR backend
Documentation
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;

extern void hypothalamus_bf_entry(void) __asm__("__HYPOTHALAMUS_ENTRY_SYMBOL__");

#define REG_DISPCNT (*(volatile u16 *)0x04000000)
#define REG_VCOUNT  (*(volatile u16 *)0x04000006)
#define VRAM        ((volatile u16 *)0x06000000)
#define SCREEN_W 240u
#define SCREEN_H 160u
#define CELL_W 6u
#define CELL_H 8u
#define COLS 40u
#define ROWS 20u
#define COLOR_BG 0x0000u
#define COLOR_FG 0x7FFFu

static u32 cursor_x;
static u32 cursor_y;

static void wait_vblank(void) {
    while (REG_VCOUNT >= 160u) {}
    while (REG_VCOUNT < 160u) {}
}

static void clear_screen(void) {
    for (u32 i = 0; i < SCREEN_W * SCREEN_H; i++) {
        VRAM[i] = COLOR_BG;
    }
    cursor_x = 0;
    cursor_y = 0;
}

static u8 glyph_row(u8 ch, u32 row) {
    if (ch >= 'a' && ch <= 'z') {
        ch = (u8)(ch - ('a' - 'A'));
    }

    switch (ch) {
    case 'A': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x11; case 3: return 0x1F; case 4: return 0x11; case 5: return 0x11; case 6: return 0x11; } break;
    case 'B': switch (row) { case 0: return 0x1E; case 1: return 0x11; case 2: return 0x11; case 3: return 0x1E; case 4: return 0x11; case 5: return 0x11; case 6: return 0x1E; } break;
    case 'C': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x10; case 3: return 0x10; case 4: return 0x10; case 5: return 0x11; case 6: return 0x0E; } break;
    case 'D': switch (row) { case 0: return 0x1E; case 1: return 0x11; case 2: return 0x11; case 3: return 0x11; case 4: return 0x11; case 5: return 0x11; case 6: return 0x1E; } break;
    case 'E': switch (row) { case 0: return 0x1F; case 1: return 0x10; case 2: return 0x10; case 3: return 0x1E; case 4: return 0x10; case 5: return 0x10; case 6: return 0x1F; } break;
    case 'F': switch (row) { case 0: return 0x1F; case 1: return 0x10; case 2: return 0x10; case 3: return 0x1E; case 4: return 0x10; case 5: return 0x10; case 6: return 0x10; } break;
    case 'G': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x10; case 3: return 0x17; case 4: return 0x11; case 5: return 0x11; case 6: return 0x0E; } break;
    case 'H': switch (row) { case 0: return 0x11; case 1: return 0x11; case 2: return 0x11; case 3: return 0x1F; case 4: return 0x11; case 5: return 0x11; case 6: return 0x11; } break;
    case 'I': switch (row) { case 0: return 0x0E; case 1: return 0x04; case 2: return 0x04; case 3: return 0x04; case 4: return 0x04; case 5: return 0x04; case 6: return 0x0E; } break;
    case 'J': switch (row) { case 0: return 0x01; case 1: return 0x01; case 2: return 0x01; case 3: return 0x01; case 4: return 0x11; case 5: return 0x11; case 6: return 0x0E; } break;
    case 'K': switch (row) { case 0: return 0x11; case 1: return 0x12; case 2: return 0x14; case 3: return 0x18; case 4: return 0x14; case 5: return 0x12; case 6: return 0x11; } break;
    case 'L': switch (row) { case 0: return 0x10; case 1: return 0x10; case 2: return 0x10; case 3: return 0x10; case 4: return 0x10; case 5: return 0x10; case 6: return 0x1F; } break;
    case 'M': switch (row) { case 0: return 0x11; case 1: return 0x1B; case 2: return 0x15; case 3: return 0x15; case 4: return 0x11; case 5: return 0x11; case 6: return 0x11; } break;
    case 'N': switch (row) { case 0: return 0x11; case 1: return 0x19; case 2: return 0x15; case 3: return 0x13; case 4: return 0x11; case 5: return 0x11; case 6: return 0x11; } break;
    case 'O': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x11; case 3: return 0x11; case 4: return 0x11; case 5: return 0x11; case 6: return 0x0E; } break;
    case 'P': switch (row) { case 0: return 0x1E; case 1: return 0x11; case 2: return 0x11; case 3: return 0x1E; case 4: return 0x10; case 5: return 0x10; case 6: return 0x10; } break;
    case 'Q': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x11; case 3: return 0x11; case 4: return 0x15; case 5: return 0x12; case 6: return 0x0D; } break;
    case 'R': switch (row) { case 0: return 0x1E; case 1: return 0x11; case 2: return 0x11; case 3: return 0x1E; case 4: return 0x14; case 5: return 0x12; case 6: return 0x11; } break;
    case 'S': switch (row) { case 0: return 0x0F; case 1: return 0x10; case 2: return 0x10; case 3: return 0x0E; case 4: return 0x01; case 5: return 0x01; case 6: return 0x1E; } break;
    case 'T': switch (row) { case 0: return 0x1F; case 1: return 0x04; case 2: return 0x04; case 3: return 0x04; case 4: return 0x04; case 5: return 0x04; case 6: return 0x04; } break;
    case 'U': switch (row) { case 0: return 0x11; case 1: return 0x11; case 2: return 0x11; case 3: return 0x11; case 4: return 0x11; case 5: return 0x11; case 6: return 0x0E; } break;
    case 'V': switch (row) { case 0: return 0x11; case 1: return 0x11; case 2: return 0x11; case 3: return 0x11; case 4: return 0x11; case 5: return 0x0A; case 6: return 0x04; } break;
    case 'W': switch (row) { case 0: return 0x11; case 1: return 0x11; case 2: return 0x11; case 3: return 0x15; case 4: return 0x15; case 5: return 0x15; case 6: return 0x0A; } break;
    case 'X': switch (row) { case 0: return 0x11; case 1: return 0x11; case 2: return 0x0A; case 3: return 0x04; case 4: return 0x0A; case 5: return 0x11; case 6: return 0x11; } break;
    case 'Y': switch (row) { case 0: return 0x11; case 1: return 0x11; case 2: return 0x0A; case 3: return 0x04; case 4: return 0x04; case 5: return 0x04; case 6: return 0x04; } break;
    case 'Z': switch (row) { case 0: return 0x1F; case 1: return 0x01; case 2: return 0x02; case 3: return 0x04; case 4: return 0x08; case 5: return 0x10; case 6: return 0x1F; } break;
    case '0': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x13; case 3: return 0x15; case 4: return 0x19; case 5: return 0x11; case 6: return 0x0E; } break;
    case '1': switch (row) { case 0: return 0x04; case 1: return 0x0C; case 2: return 0x04; case 3: return 0x04; case 4: return 0x04; case 5: return 0x04; case 6: return 0x0E; } break;
    case '2': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x01; case 3: return 0x02; case 4: return 0x04; case 5: return 0x08; case 6: return 0x1F; } break;
    case '3': switch (row) { case 0: return 0x1E; case 1: return 0x01; case 2: return 0x01; case 3: return 0x0E; case 4: return 0x01; case 5: return 0x01; case 6: return 0x1E; } break;
    case '4': switch (row) { case 0: return 0x02; case 1: return 0x06; case 2: return 0x0A; case 3: return 0x12; case 4: return 0x1F; case 5: return 0x02; case 6: return 0x02; } break;
    case '5': switch (row) { case 0: return 0x1F; case 1: return 0x10; case 2: return 0x10; case 3: return 0x1E; case 4: return 0x01; case 5: return 0x01; case 6: return 0x1E; } break;
    case '6': switch (row) { case 0: return 0x0E; case 1: return 0x10; case 2: return 0x10; case 3: return 0x1E; case 4: return 0x11; case 5: return 0x11; case 6: return 0x0E; } break;
    case '7': switch (row) { case 0: return 0x1F; case 1: return 0x01; case 2: return 0x02; case 3: return 0x04; case 4: return 0x08; case 5: return 0x08; case 6: return 0x08; } break;
    case '8': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x11; case 3: return 0x0E; case 4: return 0x11; case 5: return 0x11; case 6: return 0x0E; } break;
    case '9': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x11; case 3: return 0x0F; case 4: return 0x01; case 5: return 0x01; case 6: return 0x0E; } break;
    case '!': switch (row) { case 0: return 0x04; case 1: return 0x04; case 2: return 0x04; case 3: return 0x04; case 4: return 0x04; case 5: return 0x00; case 6: return 0x04; } break;
    case '?': switch (row) { case 0: return 0x0E; case 1: return 0x11; case 2: return 0x01; case 3: return 0x02; case 4: return 0x04; case 5: return 0x00; case 6: return 0x04; } break;
    case '.': switch (row) { case 5: return 0x00; case 6: return 0x04; } break;
    case ',': switch (row) { case 5: return 0x04; case 6: return 0x08; } break;
    case ':': switch (row) { case 1: return 0x04; case 5: return 0x04; } break;
    case '-': switch (row) { case 3: return 0x1F; } break;
    case '+': switch (row) { case 2: return 0x04; case 3: return 0x1F; case 4: return 0x04; } break;
    case '/': switch (row) { case 0: return 0x01; case 1: return 0x02; case 2: return 0x02; case 3: return 0x04; case 4: return 0x08; case 5: return 0x08; case 6: return 0x10; } break;
    case '<': switch (row) { case 1: return 0x02; case 2: return 0x04; case 3: return 0x08; case 4: return 0x04; case 5: return 0x02; } break;
    case '>': switch (row) { case 1: return 0x08; case 2: return 0x04; case 3: return 0x02; case 4: return 0x04; case 5: return 0x08; } break;
    case '[': switch (row) { case 0: return 0x0E; case 1: return 0x08; case 2: return 0x08; case 3: return 0x08; case 4: return 0x08; case 5: return 0x08; case 6: return 0x0E; } break;
    case ']': switch (row) { case 0: return 0x0E; case 1: return 0x02; case 2: return 0x02; case 3: return 0x02; case 4: return 0x02; case 5: return 0x02; case 6: return 0x0E; } break;
    case ' ': return 0x00;
    default: switch (row) { case 0: return 0x1F; case 1: return 0x11; case 2: return 0x15; case 3: return 0x11; case 4: return 0x15; case 5: return 0x11; case 6: return 0x1F; } break;
    }
    return 0x00;
}

static void newline(void) {
    cursor_x = 0;
    cursor_y++;
    if (cursor_y >= ROWS) {
        clear_screen();
    }
}

static void draw_char(u8 ch) {
    u32 px = cursor_x * CELL_W;
    u32 py = cursor_y * CELL_H;
    for (u32 row = 0; row < CELL_H; row++) {
        u8 bits = 0;
        if (row > 0 && row < 8u) {
            bits = glyph_row(ch, row - 1u);
        }
        for (u32 col = 0; col < CELL_W; col++) {
            u16 color = COLOR_BG;
            if (col < 5u && (bits & (u8)(1u << (4u - col))) != 0u) {
                color = COLOR_FG;
            }
            VRAM[(py + row) * SCREEN_W + px + col] = color;
        }
    }
}

void hypothalamus_bf_putchar(u8 byte) __asm__("__HYPOTHALAMUS_PUTCHAR_SYMBOL__");
void hypothalamus_bf_putchar(u8 byte) {
    if (byte == '\r') {
        return;
    }
    if (byte == '\n') {
        newline();
        return;
    }

    draw_char(byte);
    cursor_x++;
    if (cursor_x >= COLS) {
        newline();
    }
}

int hypothalamus_bf_getchar(void) __asm__("__HYPOTHALAMUS_GETCHAR_SYMBOL__");
int hypothalamus_bf_getchar(void) {
    return -1;
}

void runtime_main(void) {
    REG_DISPCNT = 0x0403u;
    clear_screen();
    hypothalamus_bf_entry();
    for (;;) {
        wait_vblank();
    }
}