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