aboutsummaryrefslogtreecommitdiffstats
path: root/src/text.h
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-18 16:40:24 +0200
committerBad Diode <bd@badd10de.dev>2021-05-18 16:40:24 +0200
commit0c7265cf0de9d4ec95d28c5e103c00a63f4a1697 (patch)
tree4a1145e849e078395430a8d718c4bd69a06fb29f /src/text.h
downloaduxngba-0c7265cf0de9d4ec95d28c5e103c00a63f4a1697.tar.gz
uxngba-0c7265cf0de9d4ec95d28c5e103c00a63f4a1697.zip
Proof of concept of UXN on the GBA
Diffstat (limited to 'src/text.h')
-rw-r--r--src/text.h232
1 files changed, 232 insertions, 0 deletions
diff --git a/src/text.h b/src/text.h
new file mode 100644
index 0000000..bfac4e4
--- /dev/null
+++ b/src/text.h
@@ -0,0 +1,232 @@
1#ifndef GBAEXP_TILES_H
2#define GBAEXP_TILES_H
3
4#include <stdarg.h>
5#include <stdio.h>
6
7#include "common.h"
8#include "bitmap.h"
9
10typedef enum {
11 TXT_MODE_TILED_BG,
12 TXT_MODE_MODE3,
13} TextMode;
14
15typedef struct Font {
16 // A pointer to an area of memory containing font data.
17 // TODO: Should we unpack each char everytime or unpack everything into RAM?
18 // Maybe this should be optional?
19 u16 *data;
20 // The char_map stores the index to the character position within the font
21 // array depending on the ascii number we want to render. This allows
22 // the usage reduced font sets, for example just uppercase letters and
23 // numbers.
24 u8 *char_map;
25 // Width and height of each font character. Only monospaced fonts are
26 // currently supported.
27 u8 char_width;
28 u8 char_height;
29 // The color of this font.
30 Color color;
31} Font;
32
33typedef struct TextEngine {
34 // Currently working on tiled backgrounds only. The X and Y positions
35 // correspond to the tile X and Y starting from the top left of the screen.
36 // For a 240x160 screen, we have 30x20 tiles available.
37 size_t cursor_x;
38 size_t cursor_y;
39 // Pointer to the memory being used for writing to the screen.
40 u16 *memory;
41 // TODO: Support other modes and monospaced fonts should be simple but we
42 // need to keep track of a couple of other variables, which may not be
43 // available for all modes. For example, tile modes can't have a font width
44 // smaller than the tile size, although bigger fonts (For example fonts
45 // composed of multiple tiles) should still be possible. This may not be
46 // a good time investment, but can be done in the future if needed.
47 // The mode controls how the text is rendered and how the memory is managed.
48 TextMode mode;
49 // The font used to render the text.
50 Font font;
51} TextEngine;
52
53static TextEngine text_engine = {0};
54
55static u8 default_char_map[256] = {0};
56
57void
58txt_putc_tile(char c) {
59 if (c == '\0') {
60 return;
61 }
62 if (c == '\n') {
63 text_engine.cursor_x = 0;
64 text_engine.cursor_y++;
65 } else {
66 text_engine.memory[text_engine.cursor_x + 32 * text_engine.cursor_y] = c;
67 text_engine.cursor_x++;
68 if (text_engine.cursor_x >= 30) {
69 text_engine.cursor_x = 0;
70 text_engine.cursor_y++;
71 }
72 }
73 if (text_engine.cursor_y >= 20) {
74 text_engine.cursor_y = 0;
75 }
76}
77
78void
79txt_putc_m3(char c) {
80 if (c == '\0') {
81 return;
82 }
83 if (c == '\n') {
84 text_engine.cursor_x = 0;
85 text_engine.cursor_y += text_engine.font.char_height;
86 } else {
87 u8 idx = text_engine.font.char_map[(int)c] * 2;
88 u32 *packed_char = text_engine.font.data;
89 packed_char += idx;
90 Tile tile = {0};
91 unpack_tiles(packed_char, &tile, 1);
92 int x = text_engine.cursor_x;
93 int y = text_engine.cursor_y;
94 for (size_t i = 0; i < text_engine.font.char_height; ++i) {
95 for (size_t j = 0; j < text_engine.font.char_width; ++j) {
96 if ((tile.row[i] >> 4 * j) & 0x1) {
97 // put_pixel_m4(x + j, y + i, 1, backbuffer);
98 // TODO: Clean this up please.
99 put_pixel_m3(x + j,
100 y + i,
101 text_engine.font.color,
102 FRAMEBUFFER);
103 }
104 }
105 }
106 text_engine.cursor_x += text_engine.font.char_width;
107 if (text_engine.cursor_x >= SCREEN_WIDTH) {
108 text_engine.cursor_x = 0;
109 text_engine.cursor_y += text_engine.font.char_height;
110 }
111 }
112 if (text_engine.cursor_y >= SCREEN_HEIGHT) {
113 text_engine.cursor_y = 0;
114 }
115}
116
117void
118txt_putc(char c) {
119 switch (text_engine.mode) {
120 case TXT_MODE_TILED_BG: {
121 txt_putc_tile(c);
122 } break;
123 case TXT_MODE_MODE3: {
124 txt_putc_m3(c);
125 } break;
126 }
127}
128
129static inline void
130txt_puts(char *msg) {
131 while (*msg) {
132 txt_putc(*msg++);
133 }
134}
135
136void
137txt_init_tile(size_t bg, Font font, size_t cb_idx) {
138 // The screenblock for the tile map should start after the tile memory
139 // (MEM_VRAM + 0x2000 for 256 characters). Since each screenblock is
140 // composed of 1024 screenblock entries of u16 (2 bytes), we need an
141 // screenblock index offset of 8192 / (1024 * 2) = 4 screen blocks.
142 size_t sb_idx = cb_idx * 8 + 4;
143
144 // Set the background parameters for the text layer.
145 BG_CTRL(bg) = BG_CHARBLOCK(cb_idx) | BG_SCREENBLOCK(sb_idx) | BG_PRIORITY(3);
146
147 // Load font data in video memory. Each character is unpacked into a tile of
148 // 8 32bit values (4bpp), meaning that for the full ASCII set of 256
149 // characters, we need 8192 bytes of VRAM (8 * 4 * 256).
150 text_engine.font = font;
151 unpack_tiles(font.data, &TILE_MEM[cb_idx], 256);
152
153 // Initialize default values.
154 if (font.color == 0) {
155 font.color = COLOR_WHITE;
156 }
157
158 // Load palette color.
159 PAL_BUFFER_BG[1] = font.color;
160
161 // Update text_engine variables.
162 text_engine.memory = SCREENBLOCK_MEM[sb_idx];
163}
164
165void
166txt_init_bitmap(TextMode mode, Font font) {
167 // NOTE: Only mode 3 is currently supported
168 assert(mode == TXT_MODE_MODE3);
169
170 // If font_map is NULL, initialize the standard 0-255 character map.
171 if (font.char_map == NULL) {
172 for (size_t i = 0; i < 256; ++i) {
173 default_char_map[i] = i;
174 }
175 font.char_map = &default_char_map;
176 }
177
178 // Initialize default values if set to zero.
179 if (font.char_width == 0) {
180 font.char_width = 8;
181 }
182 if (font.char_height == 0) {
183 font.char_height = 8;
184 }
185 if (font.color == 0) {
186 font.color = COLOR_WHITE;
187 }
188
189 // Prepare text engine.
190 text_engine.font = font;
191 text_engine.mode = mode;
192}
193
194// Print text to the screen with formatting.
195void
196txt_printf(char *msg, ...) {
197 va_list arg_list;
198 va_start(arg_list, msg);
199 char buf[512] = {0};
200 vsprintf(buf, msg, arg_list);
201 txt_puts(buf);
202}
203
204// TODO: Update for working on bitmap modes.
205void
206txt_clear_line(void) {
207 for (size_t i = 0; i < 30; ++i) {
208 text_engine.memory[i + 32 * text_engine.cursor_y] = ' ';
209 }
210 text_engine.cursor_x = 0;
211}
212
213// TODO: Update for working on bitmap modes.
214void
215txt_clear_screen(void) {
216 for (size_t j = 0; j < 20; ++j) {
217 for (size_t i = 0; i < 30; ++i) {
218 text_engine.memory[i + 32 * j] = ' ';
219 }
220 }
221 text_engine.cursor_x = 0;
222 text_engine.cursor_y = 0;
223}
224
225void
226txt_position(size_t tile_x, size_t tile_y) {
227 text_engine.cursor_x = tile_x;
228 text_engine.cursor_y = tile_y;
229}
230
231#endif // GBAEXP_TILES_H
232