summaryrefslogtreecommitdiffstats
path: root/src/text.h
blob: e38c220d39277214d7d4a6967293072a708ee036 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#ifndef GBAEXP_TILES_H
#define GBAEXP_TILES_H

#include <stdarg.h>

#include "common.h"
#include "bd-font.c"

typedef struct TextEngine {
    // Currently working on tiled backgrounds only. The X and Y positions
    // correspond to the tile X and Y starting from the top left of the screen.
    // For a 240x160 screen, we have 30x20 tiles available.
    size_t cursor_x;
    size_t cursor_y;
    // Pointer to the screenblock being used for writing to the screen.
    u16 *screenblock;
} TextEngine;

static TextEngine text_engine = {0};

void
txt_putc(char c) {
    if (c == '\0') {
        return;
    }
    if (c == '\n') {
        text_engine.cursor_x = 0;
        text_engine.cursor_y++;
    } else {
        text_engine.screenblock[text_engine.cursor_x + 32 * text_engine.cursor_y] = c;
        text_engine.cursor_x++;
        if (text_engine.cursor_x >= 30) {
            text_engine.cursor_x = 0;
            text_engine.cursor_y++;
        }
    }
    if (text_engine.cursor_y >= 20) {
        text_engine.cursor_y = 0;
    }
}

static inline void
txt_puts(char *msg) {
    while (*msg) {
        txt_putc(*msg++);
    }
}

void
txt_init(size_t bg, Color clr, size_t cb_idx) {
    // The screenblock for the tile map should start after the tile memory
    // (MEM_VRAM + 0x2000 for 256 characters). Since each screenblock is
    // composed of 1024 screenblock entries of u16 (2 bytes), we need an
    // screenblock index offset of 8192 / (1024 * 2) = 4 screen blocks.
    size_t sb_idx = cb_idx * 8 + 4;

    // Set the background parameters for the text layer.
    BG_CTRL(bg) = BG_CHARBLOCK(cb_idx) | BG_SCREENBLOCK(sb_idx) | BG_PRIORITY(3);

    // Load font data in video memory. Each character is unpacked into a tile of
    // 8 32bit values (4bpp), meaning that for the full ASCII set of 256
    // characters, we need 8192 bytes of VRAM (8 * 4 * 256).
    unpack_tiles(&bd_font, &CHARBLOCK_MEM[cb_idx], 256);

    // Load palette color.
    PAL_BUFFER_BG[1] = clr;

    // Update text_engine variables.
    text_engine.screenblock = SCREENBLOCK_MEM[sb_idx];
}

// Print text to the screen with formatting.
void
txt_printf(char *msg, ...) {
    va_list arg_list;
    va_start(arg_list, msg);
    char c;
    char prev_c;
    while ((c = *msg++) != '\0') {
        if (c != '%') {
            txt_putc(c);
            prev_c = c;
            continue;
        }
        c = *msg++;
        switch (c) {
            case 's': {
                txt_puts(va_arg(arg_list, char *));
            } break;
            case 'd': {
                char buf[32] = {0};
                int x = va_arg(arg_list, int);
                sprintf(buf, "%d", x);
                txt_puts(buf);
            } break;
            case 'u': {
                char buf[32] = {0};
                unsigned int x = va_arg(arg_list, unsigned int);
                sprintf(buf, "%u", x);
                txt_puts(buf);
            } break;
            case 'x': {
                char buf[32] = {0};
                unsigned int x = va_arg(arg_list, unsigned int);
                sprintf(buf, "%x", x);
                txt_puts(buf);
            } break;
            default: {
                txt_putc('%');
                txt_putc(c);
            } break;
        }
    }
}

void
txt_clear_line(void) {
    for (size_t i = 0; i < 30; ++i) {
        text_engine.screenblock[i + 32 * text_engine.cursor_y] = ' ';
    }
    text_engine.cursor_x = 0;
}

void
txt_clear_screen(void) {
    for (size_t j = 0; j < 20; ++j) {
        for (size_t i = 0; i < 30; ++i) {
            text_engine.screenblock[i + 32 * j] = ' ';
        }
    }
    text_engine.cursor_x = 0;
    text_engine.cursor_y = 0;
}

void
txt_position(size_t tile_x, size_t tile_y) {
    text_engine.cursor_x = tile_x;
    text_engine.cursor_y = tile_y;
}

#endif // GBAEXP_TILES_H