summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-06 20:55:32 +0200
committerBad Diode <bd@badd10de.dev>2021-05-06 20:55:32 +0200
commitdb0c517337ffc147eb460dee2e079a9a356e7225 (patch)
treec8c05748293dcc540cbd561418ee019a98c69e91
parent6a3a8d96327d79100455e80374f7fbe008570649 (diff)
downloadgba-experiments-db0c517337ffc147eb460dee2e079a9a356e7225.tar.gz
gba-experiments-db0c517337ffc147eb460dee2e079a9a356e7225.zip
Expand text engine to allow writing to mode 3
-rw-r--r--src/main.c14
-rw-r--r--src/sequencer.c23
-rw-r--r--src/text.h177
3 files changed, 153 insertions, 61 deletions
diff --git a/src/main.c b/src/main.c
index 05bfa26..33e7ebf 100644
--- a/src/main.c
+++ b/src/main.c
@@ -6,6 +6,7 @@
6#include "sprites.h" 6#include "sprites.h"
7#include "text.h" 7#include "text.h"
8#include "sequencer.c" 8#include "sequencer.c"
9#include "bd-font.c"
9 10
10// 11//
11// Main functions. 12// Main functions.
@@ -14,14 +15,13 @@
14// TODO: Cleanup OBJ/OAM memory copying and access. 15// TODO: Cleanup OBJ/OAM memory copying and access.
15// 16//
16 17
17
18int main(void) { 18int main(void) {
19 // Configure the display in mode 0 to show OBJs, where tile memory is 19 // Configure the display in mode 0 to show OBJs, where tile memory is
20 // sequential. 20 // sequential.
21 DISP_CTRL = DISP_ENABLE_SPRITES | DISP_MODE_3 | DISP_BG_2; 21 DISP_CTRL = DISP_ENABLE_SPRITES | DISP_MODE_3 | DISP_BG_2;
22 22
23 // Initialize text engine. 23 // Initialize text engine.
24 // txt_init(0, COLOR_RED, 0); 24 txt_init_bitmap(TXT_MODE_MODE3, (Font){.data = bd_font, .char_width = 6});
25 25
26 // Register interrupts. 26 // Register interrupts.
27 irq_init(); 27 irq_init();
@@ -37,16 +37,6 @@ int main(void) {
37 handle_sequencer_input(); 37 handle_sequencer_input();
38 update_sequencer_sprites(); 38 update_sequencer_sprites();
39 render_sequencer_sprites(); 39 render_sequencer_sprites();
40
41 // DEBUG: Output
42 // txt_position(1,0);
43 // txt_clear_line();
44 // txt_printf(" BPM: %d\n\n", bpm);
45
46 // txt_clear_line();
47 // txt_printf(" Step: %d\n", step_counter);
48 // txt_clear_line();
49 // txt_printf(" Note: %s\n", note_names[active_note]);
50 }; 40 };
51 41
52 return 0; 42 return 0;
diff --git a/src/sequencer.c b/src/sequencer.c
index c83fa5a..6f7dd21 100644
--- a/src/sequencer.c
+++ b/src/sequencer.c
@@ -537,7 +537,6 @@ typedef struct SeqSprite {
537 u16 obj_attr_2; 537 u16 obj_attr_2;
538} SeqSprite; 538} SeqSprite;
539 539
540
541typedef enum { 540typedef enum {
542 SEQ_SELECT_TRIGGER, 541 SEQ_SELECT_TRIGGER,
543 SEQ_SELECT_CHANNEL, 542 SEQ_SELECT_CHANNEL,
@@ -1058,12 +1057,30 @@ update_sequencer_sprites(void) {
1058 int x = SEQ_ENV_POS_X + 8; 1057 int x = SEQ_ENV_POS_X + 8;
1059 int y = SEQ_ENV_POS_Y; 1058 int y = SEQ_ENV_POS_Y;
1060 1059
1060 draw_fill_rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, COLOR_BLACK);
1061
1061 // Clear wave A and draw. 1062 // Clear wave A and draw.
1062 draw_fill_rect(x, y, x + 64, y + 16, COLOR_BLACK); 1063 // draw_fill_rect(x, y, x + 64, y + 16, COLOR_BLACK);
1063 draw_wave_pattern(wave_a, x, y, COLOR_RED); 1064 draw_wave_pattern(wave_a, x, y, COLOR_RED);
1064 1065
1066 // Write wave text.
1067 txt_position(x, y + 20);
1068 txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x",
1069 wave_a[0], wave_a[1], wave_a[2], wave_a[3],
1070 wave_a[4], wave_a[5], wave_a[6], wave_a[7]);
1071 txt_position(x, y + 20 + 8);
1072 txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x",
1073 wave_a[8], wave_a[9], wave_a[10], wave_a[11],
1074 wave_a[12], wave_a[13], wave_a[14], wave_a[15]);
1075 // txt_position(0,y + 20 + 8);
1076 // txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
1077 // wave_b[0], wave_b[1], wave_b[2], wave_b[3],
1078 // wave_b[4], wave_b[5], wave_b[6], wave_b[7],
1079 // wave_b[8], wave_b[9], wave_b[10], wave_b[11],
1080 // wave_b[12], wave_b[13], wave_b[14], wave_b[15]);
1081
1065 // Clear wave B and draw. 1082 // Clear wave B and draw.
1066 draw_fill_rect(x + 64 + 16, y, x + 64 * 2 + 16, y + 16, COLOR_BLACK); 1083 // draw_fill_rect(x + 64 + 16, y, x + 64 * 2 + 16, y + 16, COLOR_BLACK);
1067 draw_wave_pattern(wave_b, x + 64 + 16, y, COLOR_CYAN); 1084 draw_wave_pattern(wave_b, x + 64 + 16, y, COLOR_CYAN);
1068 } else if ((current_selection == SEQ_SELECT_TRIGGER 1085 } else if ((current_selection == SEQ_SELECT_TRIGGER
1069 || current_selection == SEQ_SELECT_PARAMETER) 1086 || current_selection == SEQ_SELECT_PARAMETER)
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;