diff options
Diffstat (limited to 'src/text.h')
-rw-r--r-- | src/text.h | 177 |
1 files changed, 131 insertions, 46 deletions
@@ -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 | |
9 | typedef enum { | ||
10 | TXT_MODE_TILED_BG, | ||
11 | TXT_MODE_MODE3, | ||
12 | } TextMode; | ||
13 | |||
14 | typedef 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 | ||
10 | typedef struct TextEngine { | 32 | typedef 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 | ||
20 | static TextEngine text_engine = {0}; | 52 | static TextEngine text_engine = {0}; |
21 | 53 | ||
54 | static u8 default_char_map[256] = {0}; | ||
55 | |||
22 | void | 56 | void |
23 | txt_putc(char c) { | 57 | txt_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 | ||
77 | void | ||
78 | txt_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 | |||
111 | void | ||
112 | txt_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 | |||
43 | static inline void | 123 | static inline void |
44 | txt_puts(char *msg) { | 124 | txt_puts(char *msg) { |
45 | while (*msg) { | 125 | while (*msg) { |
@@ -48,7 +128,7 @@ txt_puts(char *msg) { | |||
48 | } | 128 | } |
49 | 129 | ||
50 | void | 130 | void |
51 | txt_init(size_t bg, Color clr, size_t cb_idx) { | 131 | txt_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 | |||
159 | void | ||
160 | txt_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 | |||
75 | txt_printf(char *msg, ...) { | 190 | txt_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. | ||
115 | void | 199 | void |
116 | txt_clear_line(void) { | 200 | txt_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. | ||
123 | void | 208 | void |
124 | txt_clear_screen(void) { | 209 | txt_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; |