From 0c7265cf0de9d4ec95d28c5e103c00a63f4a1697 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Tue, 18 May 2021 16:40:24 +0200 Subject: Proof of concept of UXN on the GBA --- src/text.h | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 src/text.h (limited to 'src/text.h') diff --git a/src/text.h b/src/text.h new file mode 100644 index 0000000..bfac4e4 --- /dev/null +++ b/src/text.h @@ -0,0 +1,232 @@ +#ifndef GBAEXP_TILES_H +#define GBAEXP_TILES_H + +#include +#include + +#include "common.h" +#include "bitmap.h" + +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? + u16 *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 + // correspond to the tile X and Y starting from the top left of the screen. + // For a 240x160 screen, we have 30x20 tiles available. + size_t cursor_x; + size_t cursor_y; + // 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_tile(char c) { + if (c == '\0') { + return; + } + if (c == '\n') { + text_engine.cursor_x = 0; + text_engine.cursor_y++; + } else { + 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; + text_engine.cursor_y++; + } + } + if (text_engine.cursor_y >= 20) { + text_engine.cursor_y = 0; + } +} + +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[(int)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) { + // put_pixel_m4(x + j, y + i, 1, backbuffer); + // TODO: Clean this up please. + put_pixel_m3(x + j, + y + i, + text_engine.font.color, + FRAMEBUFFER); + } + } + } + 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) { + txt_putc(*msg++); + } +} + +void +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 + // screenblock index offset of 8192 / (1024 * 2) = 4 screen blocks. + size_t sb_idx = cb_idx * 8 + 4; + + // Set the background parameters for the text layer. + BG_CTRL(bg) = BG_CHARBLOCK(cb_idx) | BG_SCREENBLOCK(sb_idx) | BG_PRIORITY(3); + + // 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). + 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] = font.color; + + // Update text_engine variables. + 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. +void +txt_printf(char *msg, ...) { + va_list arg_list; + va_start(arg_list, msg); + 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.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.memory[i + 32 * j] = ' '; + } + } + text_engine.cursor_x = 0; + text_engine.cursor_y = 0; +} + +void +txt_position(size_t tile_x, size_t tile_y) { + text_engine.cursor_x = tile_x; + text_engine.cursor_y = tile_y; +} + +#endif // GBAEXP_TILES_H + -- cgit v1.2.1