From db0c517337ffc147eb460dee2e079a9a356e7225 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Thu, 6 May 2021 20:55:32 +0200 Subject: Expand text engine to allow writing to mode 3 --- src/main.c | 14 +---- src/sequencer.c | 23 +++++++- src/text.h | 177 +++++++++++++++++++++++++++++++++++++++++--------------- 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 @@ #include "sprites.h" #include "text.h" #include "sequencer.c" +#include "bd-font.c" // // Main functions. @@ -14,14 +15,13 @@ // TODO: Cleanup OBJ/OAM memory copying and access. // - int main(void) { // Configure the display in mode 0 to show OBJs, where tile memory is // sequential. DISP_CTRL = DISP_ENABLE_SPRITES | DISP_MODE_3 | DISP_BG_2; // Initialize text engine. - // txt_init(0, COLOR_RED, 0); + txt_init_bitmap(TXT_MODE_MODE3, (Font){.data = bd_font, .char_width = 6}); // Register interrupts. irq_init(); @@ -37,16 +37,6 @@ int main(void) { handle_sequencer_input(); update_sequencer_sprites(); render_sequencer_sprites(); - - // DEBUG: Output - // txt_position(1,0); - // txt_clear_line(); - // txt_printf(" BPM: %d\n\n", bpm); - - // txt_clear_line(); - // txt_printf(" Step: %d\n", step_counter); - // txt_clear_line(); - // txt_printf(" Note: %s\n", note_names[active_note]); }; 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 { u16 obj_attr_2; } SeqSprite; - typedef enum { SEQ_SELECT_TRIGGER, SEQ_SELECT_CHANNEL, @@ -1058,12 +1057,30 @@ update_sequencer_sprites(void) { int x = SEQ_ENV_POS_X + 8; int y = SEQ_ENV_POS_Y; + draw_fill_rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, COLOR_BLACK); + // Clear wave A and draw. - draw_fill_rect(x, y, x + 64, y + 16, COLOR_BLACK); + // draw_fill_rect(x, y, x + 64, y + 16, COLOR_BLACK); draw_wave_pattern(wave_a, x, y, COLOR_RED); + // Write wave text. + txt_position(x, y + 20); + txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x", + wave_a[0], wave_a[1], wave_a[2], wave_a[3], + wave_a[4], wave_a[5], wave_a[6], wave_a[7]); + txt_position(x, y + 20 + 8); + txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x", + wave_a[8], wave_a[9], wave_a[10], wave_a[11], + wave_a[12], wave_a[13], wave_a[14], wave_a[15]); + // txt_position(0,y + 20 + 8); + // txt_printf("%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n", + // wave_b[0], wave_b[1], wave_b[2], wave_b[3], + // wave_b[4], wave_b[5], wave_b[6], wave_b[7], + // wave_b[8], wave_b[9], wave_b[10], wave_b[11], + // wave_b[12], wave_b[13], wave_b[14], wave_b[15]); + // Clear wave B and draw. - draw_fill_rect(x + 64 + 16, y, x + 64 * 2 + 16, y + 16, COLOR_BLACK); + // draw_fill_rect(x + 64 + 16, y, x + 64 * 2 + 16, y + 16, COLOR_BLACK); draw_wave_pattern(wave_b, x + 64 + 16, y, COLOR_CYAN); } else if ((current_selection == SEQ_SELECT_TRIGGER || 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 @@ #include #include "common.h" -#include "bd-font.c" + +typedef enum { + TXT_MODE_TILED_BG, + TXT_MODE_MODE3, +} TextMode; + +typedef struct Font { + // A pointer to an area of memory containing font data. + // TODO: Should we unpack each char everytime or unpack everything into RAM? + // Maybe this should be optional? + u32 *data; + // The char_map stores the index to the character position within the font + // array depending on the ascii number we want to render. This allows + // the usage reduced font sets, for example just uppercase letters and + // numbers. + u8 *char_map; + // Width and height of each font character. Only monospaced fonts are + // currently supported. + u8 char_width; + u8 char_height; + // The color of this font. + Color color; +} Font; typedef struct TextEngine { // Currently working on tiled backgrounds only. The X and Y positions @@ -13,14 +35,26 @@ typedef struct TextEngine { // For a 240x160 screen, we have 30x20 tiles available. size_t cursor_x; size_t cursor_y; - // Pointer to the screenblock being used for writing to the screen. - u16 *screenblock; + // Pointer to the memory being used for writing to the screen. + u16 *memory; + // TODO: Support other modes and monospaced fonts should be simple but we + // need to keep track of a couple of other variables, which may not be + // available for all modes. For example, tile modes can't have a font width + // smaller than the tile size, although bigger fonts (For example fonts + // composed of multiple tiles) should still be possible. This may not be + // a good time investment, but can be done in the future if needed. + // The mode controls how the text is rendered and how the memory is managed. + TextMode mode; + // The font used to render the text. + Font font; } TextEngine; static TextEngine text_engine = {0}; +static u8 default_char_map[256] = {0}; + void -txt_putc(char c) { +txt_putc_tile(char c) { if (c == '\0') { return; } @@ -28,7 +62,7 @@ txt_putc(char c) { text_engine.cursor_x = 0; text_engine.cursor_y++; } else { - text_engine.screenblock[text_engine.cursor_x + 32 * text_engine.cursor_y] = c; + text_engine.memory[text_engine.cursor_x + 32 * text_engine.cursor_y] = c; text_engine.cursor_x++; if (text_engine.cursor_x >= 30) { text_engine.cursor_x = 0; @@ -40,6 +74,52 @@ txt_putc(char c) { } } +void +txt_putc_m3(char c) { + if (c == '\0') { + return; + } + if (c == '\n') { + text_engine.cursor_x = 0; + text_engine.cursor_y += text_engine.font.char_height; + } else { + u8 idx = text_engine.font.char_map[c] * 2; + u32 *packed_char = text_engine.font.data; + packed_char += idx; + Tile tile = {0}; + unpack_tiles(packed_char, &tile, 1); + int x = text_engine.cursor_x; + int y = text_engine.cursor_y; + for (size_t i = 0; i < text_engine.font.char_height; ++i) { + for (size_t j = 0; j < text_engine.font.char_width; ++j) { + if ((tile.row[i] >> 4 * j) & 0x1) { + FRAMEBUFFER[y + i][x + j] = text_engine.font.color; + } + } + } + text_engine.cursor_x += text_engine.font.char_width; + if (text_engine.cursor_x >= SCREEN_WIDTH) { + text_engine.cursor_x = 0; + text_engine.cursor_y += text_engine.font.char_height; + } + } + if (text_engine.cursor_y >= SCREEN_HEIGHT) { + text_engine.cursor_y = 0; + } +} + +void +txt_putc(char c) { + switch (text_engine.mode) { + case TXT_MODE_TILED_BG: { + txt_putc_tile(c); + } break; + case TXT_MODE_MODE3: { + txt_putc_m3(c); + } break; + } +} + static inline void txt_puts(char *msg) { while (*msg) { @@ -48,7 +128,7 @@ txt_puts(char *msg) { } void -txt_init(size_t bg, Color clr, size_t cb_idx) { +txt_init_tile(size_t bg, Font font, size_t cb_idx) { // The screenblock for the tile map should start after the tile memory // (MEM_VRAM + 0x2000 for 256 characters). Since each screenblock is // 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) { // Load font data in video memory. Each character is unpacked into a tile of // 8 32bit values (4bpp), meaning that for the full ASCII set of 256 // characters, we need 8192 bytes of VRAM (8 * 4 * 256). - unpack_tiles(&bd_font, &TILE_MEM[cb_idx], 256); + text_engine.font = font; + unpack_tiles(font.data, &TILE_MEM[cb_idx], 256); + + // Initialize default values. + if (font.color == 0) { + font.color = COLOR_WHITE; + } // Load palette color. - PAL_BUFFER_BG[1] = clr; + PAL_BUFFER_BG[1] = font.color; // Update text_engine variables. - text_engine.screenblock = SCREENBLOCK_MEM[sb_idx]; + text_engine.memory = SCREENBLOCK_MEM[sb_idx]; +} + +void +txt_init_bitmap(TextMode mode, Font font) { + // NOTE: Only mode 3 is currently supported + assert(mode == TXT_MODE_MODE3); + + // If font_map is NULL, initialize the standard 0-255 character map. + if (font.char_map == NULL) { + for (size_t i = 0; i < 256; ++i) { + default_char_map[i] = i; + } + font.char_map = &default_char_map; + } + + // Initialize default values if set to zero. + if (font.char_width == 0) { + font.char_width = 8; + } + if (font.char_height == 0) { + font.char_height = 8; + } + if (font.color == 0) { + font.color = COLOR_WHITE; + } + + // Prepare text engine. + text_engine.font = font; + text_engine.mode = mode; } // Print text to the screen with formatting. @@ -75,56 +190,26 @@ void txt_printf(char *msg, ...) { va_list arg_list; va_start(arg_list, msg); - char c; - while ((c = *msg++) != '\0') { - if (c != '%') { - txt_putc(c); - continue; - } - c = *msg++; - switch (c) { - case 's': { - txt_puts(va_arg(arg_list, char *)); - } break; - case 'd': { - char buf[12] = {0}; - int x = va_arg(arg_list, int); - sprintf(buf, "%d", x); - txt_puts(buf); - } break; - case 'u': { - char buf[12] = {0}; - unsigned int x = va_arg(arg_list, unsigned int); - sprintf(buf, "%u", x); - txt_puts(buf); - } break; - case 'x': { - char buf[12] = {0}; - unsigned int x = va_arg(arg_list, unsigned int); - sprintf(buf, "%x", x); - txt_puts(buf); - } break; - default: { - txt_putc('%'); - txt_putc(c); - } break; - } - } + char buf[512] = {0}; + vsprintf(buf, msg, arg_list); + txt_puts(buf); } +// TODO: Update for working on bitmap modes. void txt_clear_line(void) { for (size_t i = 0; i < 30; ++i) { - text_engine.screenblock[i + 32 * text_engine.cursor_y] = ' '; + text_engine.memory[i + 32 * text_engine.cursor_y] = ' '; } text_engine.cursor_x = 0; } +// TODO: Update for working on bitmap modes. void txt_clear_screen(void) { for (size_t j = 0; j < 20; ++j) { for (size_t i = 0; i < 30; ++i) { - text_engine.screenblock[i + 32 * j] = ' '; + text_engine.memory[i + 32 * j] = ' '; } } text_engine.cursor_x = 0; -- cgit v1.2.1