aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-23 17:37:16 +0200
committerBad Diode <bd@badd10de.dev>2021-05-23 17:37:16 +0200
commit0a3831824a4e93825c5b939f406545eead622fbe (patch)
treebd5fdcd4619f43c660a2677680f73a7faf50397e
parent27472e8acf860d981425a751a4e92343ccdf387a (diff)
downloaduxngba-0a3831824a4e93825c5b939f406545eead622fbe.tar.gz
uxngba-0a3831824a4e93825c5b939f406545eead622fbe.zip
Simplify and speed up the text engine
-rw-r--r--src/main.c9
-rw-r--r--src/text.h235
-rw-r--r--src/uxn/devices/ppu.c29
3 files changed, 35 insertions, 238 deletions
diff --git a/src/main.c b/src/main.c
index bf30628..f612ef7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2,7 +2,6 @@
2 2
3#include "common.h" 3#include "common.h"
4#include "bitmap.h" 4#include "bitmap.h"
5#include "bd-font.c"
6#include "filesystem.c" 5#include "filesystem.c"
7 6
8#include "rom.c" 7#include "rom.c"
@@ -281,13 +280,7 @@ int main(void) {
281 init_uxn(&u); 280 init_uxn(&u);
282 281
283 // Initialize text engine. 282 // Initialize text engine.
284 txt_init_hybrid( 283 txt_init(1, ppu.fg);
285 TXT_MODE_HYBRID,
286 (Font){
287 .data = bd_font,
288 .char_height = 8,
289 .char_width = 8,
290 }, ppu.fg);
291 txt_position(0,0); 284 txt_position(0,0);
292 285
293 // Main loop. 286 // Main loop.
diff --git a/src/text.h b/src/text.h
index c27537f..59ab9af 100644
--- a/src/text.h
+++ b/src/text.h
@@ -7,31 +7,7 @@
7#include "common.h" 7#include "common.h"
8#include "bitmap.h" 8#include "bitmap.h"
9#include "posprintf.h" 9#include "posprintf.h"
10 10#include "ppu.h"
11
12typedef enum {
13 TXT_MODE_TILED_BG,
14 TXT_MODE_HYBRID,
15 TXT_MODE_MODE3,
16} TextMode;
17
18typedef struct Font {
19 // A pointer to an area of memory containing font data.
20 // TODO: Should we unpack each char everytime or unpack everything into RAM?
21 // Maybe this should be optional?
22 u16 *data;
23 // The char_map stores the index to the character position within the font
24 // array depending on the ascii number we want to render. This allows
25 // the usage reduced font sets, for example just uppercase letters and
26 // numbers.
27 u8 *char_map;
28 // Width and height of each font character. Only monospaced fonts are
29 // currently supported.
30 u8 char_width;
31 u8 char_height;
32 // The color of this font.
33 Color color;
34} Font;
35 11
36typedef struct TextEngine { 12typedef struct TextEngine {
37 // Currently working on tiled backgrounds only. The X and Y positions 13 // Currently working on tiled backgrounds only. The X and Y positions
@@ -40,85 +16,15 @@ typedef struct TextEngine {
40 size_t cursor_x; 16 size_t cursor_x;
41 size_t cursor_y; 17 size_t cursor_y;
42 // Pointer to the memory being used for writing to the screen. 18 // Pointer to the memory being used for writing to the screen.
43 u16 *memory; 19 u32 *memory;
44 // TODO: Support other modes and monospaced fonts should be simple but we
45 // need to keep track of a couple of other variables, which may not be
46 // available for all modes. For example, tile modes can't have a font width
47 // smaller than the tile size, although bigger fonts (For example fonts
48 // composed of multiple tiles) should still be possible. This may not be
49 // a good time investment, but can be done in the future if needed.
50 // The mode controls how the text is rendered and how the memory is managed.
51 TextMode mode;
52 // The font used to render the text. 20 // The font used to render the text.
53 Font font; 21 u8 color;
54} TextEngine; 22} TextEngine;
55 23
56static TextEngine text_engine = {0}; 24static TextEngine text_engine = {0};
57 25
58static u8 default_char_map[256] = {0};
59
60void
61txt_putc_tile(char c) {
62 if (c == '\0') {
63 return;
64 }
65 if (c == '\n') {
66 text_engine.cursor_x = 0;
67 text_engine.cursor_y++;
68 } else {
69 text_engine.memory[text_engine.cursor_x + 32 * text_engine.cursor_y] = c;
70 text_engine.cursor_x++;
71 if (text_engine.cursor_x >= 30) {
72 text_engine.cursor_x = 0;
73 text_engine.cursor_y++;
74 }
75 }
76 if (text_engine.cursor_y >= 20) {
77 text_engine.cursor_y = 0;
78 }
79}
80
81void 26void
82txt_putc_m3(char c) { 27txt_putc(char c) {
83 if (c == '\0') {
84 return;
85 }
86 if (c == '\n') {
87 text_engine.cursor_x = 0;
88 text_engine.cursor_y += text_engine.font.char_height;
89 } else {
90 u8 idx = text_engine.font.char_map[(int)c] * 2;
91 u32 *packed_char = text_engine.font.data;
92 packed_char += idx;
93 Tile tile = {0};
94 unpack_tiles(packed_char, &tile, 1);
95 int x = text_engine.cursor_x;
96 int y = text_engine.cursor_y;
97 for (size_t i = 0; i < text_engine.font.char_height; ++i) {
98 for (size_t j = 0; j < text_engine.font.char_width; ++j) {
99 if ((tile.row[i] >> 4 * j) & 0x1) {
100 // put_pixel_m4(x + j, y + i, 1, backbuffer);
101 // TODO: Clean this up please.
102 put_pixel_m3(x + j,
103 y + i,
104 text_engine.font.color,
105 FRAMEBUFFER);
106 }
107 }
108 }
109 text_engine.cursor_x += text_engine.font.char_width;
110 if (text_engine.cursor_x >= SCREEN_WIDTH) {
111 text_engine.cursor_x = 0;
112 text_engine.cursor_y += text_engine.font.char_height;
113 }
114 }
115 if (text_engine.cursor_y >= SCREEN_HEIGHT) {
116 text_engine.cursor_y = 0;
117 }
118}
119
120void
121txt_putc_hybrid(char c) {
122 if (c == '\0') { 28 if (c == '\0') {
123 return; 29 return;
124 } 30 }
@@ -126,16 +32,9 @@ txt_putc_hybrid(char c) {
126 text_engine.cursor_x = 0; 32 text_engine.cursor_x = 0;
127 text_engine.cursor_y++; 33 text_engine.cursor_y++;
128 } else { 34 } else {
129 u8 idx = text_engine.font.char_map[(int)c] * 2;
130 u32 *packed_char = text_engine.font.data;
131 packed_char += idx;
132 Tile tile = {0};
133 unpack_tiles(packed_char, &tile, 1);
134 int x = text_engine.cursor_x; 35 int x = text_engine.cursor_x;
135 int y = text_engine.cursor_y; 36 int y = text_engine.cursor_y;
136 Tile *buf = text_engine.memory; 37 putfontchar(text_engine.memory, x, y, c, text_engine.color);
137 buf[x + y * 32] = tile;
138 dirty_tiles[y] |= 1 << x;
139 text_engine.cursor_x += 1; 38 text_engine.cursor_x += 1;
140 if (text_engine.cursor_x >= 30) { 39 if (text_engine.cursor_x >= 30) {
141 text_engine.cursor_x = 0; 40 text_engine.cursor_x = 0;
@@ -147,21 +46,6 @@ txt_putc_hybrid(char c) {
147 } 46 }
148} 47}
149 48
150void
151txt_putc(char c) {
152 switch (text_engine.mode) {
153 case TXT_MODE_TILED_BG: {
154 txt_putc_tile(c);
155 } break;
156 case TXT_MODE_MODE3: {
157 txt_putc_m3(c);
158 } break;
159 case TXT_MODE_HYBRID: {
160 txt_putc_hybrid(c);
161 } break;
162 }
163}
164
165static inline void 49static inline void
166txt_puts(char *msg) { 50txt_puts(char *msg) {
167 while (*msg) { 51 while (*msg) {
@@ -170,89 +54,13 @@ txt_puts(char *msg) {
170} 54}
171 55
172void 56void
173txt_init_tile(size_t bg, Font font, size_t cb_idx) { 57txt_init(u8 color, u32 *buf) {
174 // The screenblock for the tile map should start after the tile memory
175 // (MEM_VRAM + 0x2000 for 256 characters). Since each screenblock is
176 // composed of 1024 screenblock entries of u16 (2 bytes), we need an
177 // screenblock index offset of 8192 / (1024 * 2) = 4 screen blocks.
178 size_t sb_idx = cb_idx * 8 + 4;
179
180 // Set the background parameters for the text layer.
181 BG_CTRL(bg) = BG_CHARBLOCK(cb_idx) | BG_SCREENBLOCK(sb_idx) | BG_PRIORITY(3);
182
183 // Load font data in video memory. Each character is unpacked into a tile of
184 // 8 32bit values (4bpp), meaning that for the full ASCII set of 256
185 // characters, we need 8192 bytes of VRAM (8 * 4 * 256).
186 text_engine.font = font;
187 unpack_tiles(font.data, &TILE_MEM[cb_idx], 256);
188
189 // Initialize default values.
190 if (font.color == 0) {
191 font.color = COLOR_WHITE;
192 }
193
194 // Load palette color.
195 PAL_BUFFER_BG[1] = font.color;
196
197 // Update text_engine variables.
198 text_engine.memory = SCREENBLOCK_MEM[sb_idx];
199}
200
201void
202txt_init_bitmap(TextMode mode, Font font) {
203 // If font_map is NULL, initialize the standard 0-255 character map.
204 if (font.char_map == NULL) {
205 for (size_t i = 0; i < 256; ++i) {
206 default_char_map[i] = i;
207 }
208 font.char_map = &default_char_map;
209 }
210
211 // Initialize default values if set to zero.
212 if (font.char_width == 0) {
213 font.char_width = 8;
214 }
215 if (font.char_height == 0) {
216 font.char_height = 8;
217 }
218 if (font.color == 0) {
219 font.color = COLOR_WHITE;
220 }
221
222 // Prepare text engine. 58 // Prepare text engine.
223 text_engine.font = font; 59 text_engine.color = color;
224 text_engine.mode = mode;
225}
226
227void
228txt_init_hybrid(TextMode mode, Font font, u32 *buf) {
229 // If font_map is NULL, initialize the standard 0-255 character map.
230 if (font.char_map == NULL) {
231 for (size_t i = 0; i < 256; ++i) {
232 default_char_map[i] = i;
233 }
234 font.char_map = &default_char_map;
235 }
236
237 // Initialize default values if set to zero.
238 if (font.char_width == 0) {
239 font.char_width = 8;
240 }
241 if (font.char_height == 0) {
242 font.char_height = 8;
243 }
244 if (font.color == 0) {
245 font.color = COLOR_WHITE;
246 }
247
248 // Prepare text engine.
249 text_engine.font = font;
250 text_engine.mode = mode;
251 text_engine.memory = buf; 60 text_engine.memory = buf;
252} 61}
253 62
254// Print text to the screen with formatting. 63// Print text to the screen with formatting.
255
256#define txt_printf(msg, ...) \ 64#define txt_printf(msg, ...) \
257 { \ 65 { \
258 char buf[256] = {0}; \ 66 char buf[256] = {0}; \
@@ -261,36 +69,16 @@ txt_init_hybrid(TextMode mode, Font font, u32 *buf) {
261 } 69 }
262 70
263void 71void
264txt_clear_line_tiled(void) { 72txt_clear_line(void) {
265 for (size_t i = 0; i < 30; ++i) { 73 for (size_t i = 0; i < 30; ++i) {
266 text_engine.memory[i + 32 * text_engine.cursor_y] = ' '; 74 int x = text_engine.cursor_x;
75 int y = text_engine.cursor_y;
76 putfontchar(text_engine.memory, x, y, ' ', text_engine.color);
267 } 77 }
268 text_engine.cursor_x = 0; 78 text_engine.cursor_x = 0;
269} 79}
270 80
271void 81void
272txt_clear_line_bitmap(void) {
273 // DEBUG: should be on the text struct.
274 Tile *buf = &TILE_MEM[0];
275 for (size_t i = 0; i < 30; ++i) {
276 buf[i + text_engine.cursor_y * 30] = (Tile){0};
277 }
278}
279
280void
281txt_clear_line(void) {
282 switch (text_engine.mode) {
283 case TXT_MODE_TILED_BG: {
284 txt_clear_line_tiled();
285 } break;
286 case TXT_MODE_MODE3:
287 case TXT_MODE_HYBRID: {
288 txt_clear_line_bitmap();
289 } break;
290 }
291}
292
293void
294txt_clear_screen(void) { 82txt_clear_screen(void) {
295 for (size_t j = 0; j < 20; ++j) { 83 for (size_t j = 0; j < 20; ++j) {
296 text_engine.cursor_y = j; 84 text_engine.cursor_y = j;
@@ -307,4 +95,3 @@ txt_position(size_t tile_x, size_t tile_y) {
307} 95}
308 96
309#endif // GBAEXP_TILES_H 97#endif // GBAEXP_TILES_H
310
diff --git a/src/uxn/devices/ppu.c b/src/uxn/devices/ppu.c
index 02267f0..64f3fb4 100644
--- a/src/uxn/devices/ppu.c
+++ b/src/uxn/devices/ppu.c
@@ -1,4 +1,5 @@
1#include "ppu.h" 1#include "ppu.h"
2#include "bd-font.c"
2 3
3/* 4/*
4Copyright (c) 2021 Devine Lu Linvega 5Copyright (c) 2021 Devine Lu Linvega
@@ -14,11 +15,12 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14WITH REGARD TO THIS SOFTWARE. 15WITH REGARD TO THIS SOFTWARE.
15*/ 16*/
16 17
17#define FG_FRONT ((u32*)(MEM_VRAM)) 18#define FG_FRONT ((u32*)(MEM_VRAM))
18#define BG_FRONT ((u32*)(MEM_VRAM + KB(20))) 19#define BG_FRONT ((u32*)(MEM_VRAM + KB(20)))
19#define FG_BACK ((u32*)(MEM_VRAM + KB(44))) 20#define FG_BACK ((u32*)(MEM_VRAM + KB(44)))
20#define BG_BACK ((u32*)(MEM_VRAM + KB(64))) 21#define BG_BACK ((u32*)(MEM_VRAM + KB(64)))
21#define TILE_MAP ((u32*)(MEM_VRAM + KB(40))) 22#define TILE_MAP ((u32*)(MEM_VRAM + KB(40)))
23#define FONT_DATA ((u32*)(MEM_VRAM + KB(84)))
22 24
23static u32 unpack_icon_lut[256] = { 25static u32 unpack_icon_lut[256] = {
24 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, 26 0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100,
@@ -213,6 +215,18 @@ puticn(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, u8 flipx, u8 flipy) {
213 215
214IWRAM_CODE 216IWRAM_CODE
215void 217void
218putfontchar(u32 *layer, u16 tile_x, u16 tile_y, u8 ch, u8 color) {
219 u32 pos = (tile_x + tile_y * 32) * 8;
220 u32 *tile_data = &layer[pos];
221 u32 *font_data = &FONT_DATA[8 * ch];
222 for (size_t i = 0; i < 8; ++i) {
223 tile_data[i] = font_data[i] * color;
224 }
225 dirty_tiles[tile_y] |= 1 << tile_x;
226}
227
228IWRAM_CODE
229void
216putchr(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color, 230putchr(u32 *layer, u16 x, u16 y, u8 *sprite, u8 color,
217 u8 flipx, u8 flipy) { 231 u8 flipx, u8 flipy) {
218 u8 sprline1, sprline2; 232 u8 sprline1, sprline2;
@@ -316,7 +330,7 @@ initppu(Ppu *p, u8 hor, u8 ver, u8 pad) {
316 PAL_BUFFER_BG[2] = COLOR_RED; 330 PAL_BUFFER_BG[2] = COLOR_RED;
317 PAL_BUFFER_BG[3] = COLOR_BLUE; 331 PAL_BUFFER_BG[3] = COLOR_BLUE;
318 332
319 // Initialize memory map. 333 // Initialize background memory map.
320 u16 *mem_map_fg = SCREENBLOCK_MEM[sb_fg]; 334 u16 *mem_map_fg = SCREENBLOCK_MEM[sb_fg];
321 u16 *mem_map_bg = SCREENBLOCK_MEM[sb_bg]; 335 u16 *mem_map_bg = SCREENBLOCK_MEM[sb_bg];
322 size_t k = 0; 336 size_t k = 0;
@@ -325,5 +339,8 @@ initppu(Ppu *p, u8 hor, u8 ver, u8 pad) {
325 mem_map_bg[i] = k + 32 * 4; 339 mem_map_bg[i] = k + 32 * 4;
326 } 340 }
327 341
342 // Load font data into VRAM.
343 unpack_tiles(&bd_font, FONT_DATA, 256);
344
328 return 1; 345 return 1;
329} 346}