diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b45699..7e28f30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/video_dma.pio ) -target_sources(picopal PRIVATE main.c cvideo.c) +target_sources(picopal PRIVATE main.c cvideo.c text_mode.c) pico_set_program_name(picopal "picopal") diff --git a/cvideo.h b/cvideo.h index 4f719d0..821fba1 100644 --- a/cvideo.h +++ b/cvideo.h @@ -10,6 +10,12 @@ #define WORDS_PER_LINE (VIDEO_WIDTH / 32) #define LINES_PER_FIELD (VIDEO_HEIGHT / 2) +// Active and visible field in the framebuffer +#define START_X 35 +#define END_X (VIDEO_WIDTH-40) +#define START_Y 20 +#define END_Y (VIDEO_HEIGHT-40) + typedef uint32_t video_framebuffer_t[VIDEO_HEIGHT][WORDS_PER_LINE]; typedef uint32_t (*video_framebuffer_ptr_t)[WORDS_PER_LINE]; diff --git a/font/mbf_big_00.png b/font/mbf_big_00.png new file mode 100644 index 0000000..ac183cb Binary files /dev/null and b/font/mbf_big_00.png differ diff --git a/font/mbf_big_00_black_bg.png b/font/mbf_big_00_black_bg.png new file mode 100644 index 0000000..e31abd5 Binary files /dev/null and b/font/mbf_big_00_black_bg.png differ diff --git a/font/mbf_big_04.png b/font/mbf_big_04.png new file mode 100644 index 0000000..be03d98 Binary files /dev/null and b/font/mbf_big_04.png differ diff --git a/font/mbf_big_04_black_bg.png b/font/mbf_big_04_black_bg.png new file mode 100644 index 0000000..21fe41c Binary files /dev/null and b/font/mbf_big_04_black_bg.png differ diff --git a/font/mbf_small_00.png b/font/mbf_small_00.png new file mode 100644 index 0000000..088a737 Binary files /dev/null and b/font/mbf_small_00.png differ diff --git a/font/mbf_small_00_black_bg.png b/font/mbf_small_00_black_bg.png new file mode 100644 index 0000000..e4098cc Binary files /dev/null and b/font/mbf_small_00_black_bg.png differ diff --git a/font/mbf_small_04.png b/font/mbf_small_04.png new file mode 100644 index 0000000..ec9232c Binary files /dev/null and b/font/mbf_small_04.png differ diff --git a/font/mbf_small_04_black_bg.png b/font/mbf_small_04_black_bg.png new file mode 100644 index 0000000..37ccd27 Binary files /dev/null and b/font/mbf_small_04_black_bg.png differ diff --git a/main.c b/main.c index 25b6ed3..21a8452 100644 --- a/main.c +++ b/main.c @@ -5,112 +5,67 @@ #include "pico/multicore.h" #include "cvideo.h" +#include "text_mode.h" -#include "font.xbm" -static video_framebuffer_t fb0, fb1; - -#define START_X 35 -#define END_X (VIDEO_WIDTH-40) -#define START_Y 20 -#define END_Y (VIDEO_HEIGHT-40) - -#define CHARS_PER_LINE 32 -#define CHAR_LINES 20 - -static char text_mode_buffer[20][32]; - -static void clear(video_framebuffer_t fb){ - memset(fb, 0, sizeof(video_framebuffer_t)); -} - -static void set_bit(video_framebuffer_t fb, uint x, uint y, bool value) { - if(x>=VIDEO_WIDTH || y>=VIDEO_HEIGHT) - return; - uint index_x = x / 32; - uint pos_x = x % 32; - - uint flag = value << (31-pos_x); - if(value) - fb[y][index_x] |= flag; - else - fb[y][index_x] &= ~flag; -} - -#define CHAR_WIDTH font_width / 16 -#define CHAR_HEIGHT font_height / 16 - -void draw_character(video_framebuffer_t fb, unsigned int x, unsigned int y, unsigned int scale, char character) { - uint8_t row = character / 16; - uint8_t column = character % 16; - uint32_t font_x = column * 10; - uint32_t font_y = row * 12; - - for (int j = font_y; j < font_y + CHAR_HEIGHT; j++) { - for (int i = font_x; i < font_x + CHAR_WIDTH; i++) { - // XBM images pad rows with zeros - uint32_t array_position; - if (font_width %8 != 0) { - array_position = (j * (font_width + (8 - font_width % 8))) + i; - } - else { - array_position = (j * font_width) + i; - } - uint32_t array_index = array_position / 8; - // XBM image, low bits are first - uint32_t byte_position = array_position % 8; - - uint8_t value = font_bits[array_index] >> byte_position & 1 ; - - for (int sx = 0; sx < scale; sx++) { - for (int sy = 0; sy < scale; sy++) { - set_bit(fb, x + ((i - font_x) * scale) + sx, y + ((j- font_y) * scale) + sy, !value); - } - } - } - } -} +// -------------------------------- +// CORE 1 +// -------------------------------- void core1_entry() { - gpio_init(25); - gpio_set_dir(25, GPIO_OUT); - while (true) { - gpio_put(25, 1); - sleep_ms(500); - gpio_put(25, 0); - sleep_ms(500); - } + // Nothing yet } +// -------------------------------- + + +// -------------------------------- +// CORE 0 +// -------------------------------- + +static video_framebuffer_t fb0, fb1; +static text_mode_buffer_t text_mode_buffer; + +static bool framebuffer_switched = true; +static bool text_mode = true; void framebuffer_switched_cb(){ + framebuffer_switched = true; } void main() { stdio_init_all(); multicore_launch_core1(core1_entry); - clear(fb0); - clear(fb1); - video_init(fb0); + init_font_cache(); + video_set_framebuffer_switched_cb(framebuffer_switched_cb); + video_init(fb0); + + memset(text_mode_buffer, 0, CHAR_LINES*CHARS_PER_LINE); + strcpy(&text_mode_buffer[0][0], "PicoPAL text mode"); + strcpy(&text_mode_buffer[1][0], "-----------------"); + + gpio_init(25); + gpio_set_dir(25, GPIO_OUT); int fbnum = 1; video_framebuffer_ptr_t fbs[] = {fb0, fb1}; - int i = 0; while (true) { - clear(fbs[fbnum]); - for(int y=0; y<20; y++){ - for(int x=0; x<32; x++){ - draw_character(fbs[fbnum], START_X+x*(CHAR_WIDTH+1)*2, START_Y+y*(CHAR_HEIGHT+1)*2, 2, '0'+(i+x+y)%10); - } - } - video_set_framebuffer(fbs[fbnum]); - fbnum = (fbnum+1)%2; - i = (i+1)%10; - sleep_ms(500); + // Check if need to draw new frame + if(framebuffer_switched){ + gpio_put(25, fbnum); + + if(text_mode){ + draw_text_mode(fbs[fbnum], text_mode_buffer); + } + fbnum = (fbnum+1)%2; + framebuffer_switched = false; + } tight_loop_contents(); } } + +// -------------------------------- diff --git a/text_mode.c b/text_mode.c new file mode 100644 index 0000000..8ca5fc5 --- /dev/null +++ b/text_mode.c @@ -0,0 +1,131 @@ +#include "text_mode.h" + +#include +#include +#include "pico/stdlib.h" + +#include "font.xbm" + +static void clear(video_framebuffer_t fb){ + memset(fb, 0, sizeof(video_framebuffer_t)); +} + +#define CHAR_WIDTH font_width / 16 +#define CHAR_HEIGHT font_height / 16 + +static uint16_t glyph_rows[256][CHAR_HEIGHT]; +static uint32_t glyph_rows_x2[256][CHAR_HEIGHT]; + +void init_font_cache() { + for (int ch = 0; ch < 256; ++ch) { + uint8_t row = ch / 16; + uint8_t column = ch % 16; + uint32_t font_x = column * CHAR_WIDTH; + uint32_t font_y = row * CHAR_HEIGHT; + + for (int j = 0; j < CHAR_HEIGHT; ++j) { + uint16_t packed = 0; + + for (int i = 0; i < CHAR_WIDTH; ++i) { + uint32_t src_x = font_x + i; + uint32_t src_y = font_y + j; + uint32_t array_position; + + if ((font_width % 8) != 0) { + array_position = (src_y * (font_width + (8 - font_width % 8))) + src_x; + } else { + array_position = (src_y * font_width) + src_x; + } + + uint32_t array_index = array_position / 8; + uint32_t byte_position = array_position % 8; + uint8_t value = (font_bits[array_index] >> byte_position) & 1u; + + if (!value) { + packed |= (uint16_t)(1u << (CHAR_WIDTH - 1 - i)); + } + } + + glyph_rows[ch][j] = packed; + + uint32_t packed_x2 = 0; + for (int i = 0; i < CHAR_WIDTH; ++i) { + if (packed & (1u << (CHAR_WIDTH - 1 - i))) { + packed_x2 |= (uint32_t)(3u << ((CHAR_WIDTH - 1 - i) * 2)); + } + } + glyph_rows_x2[ch][j] = packed_x2; + } + } +} + +static inline void blit_row_bits(video_framebuffer_t fb, unsigned int x, unsigned int y, uint64_t bits, unsigned int width) { + if (y >= VIDEO_HEIGHT || x >= VIDEO_WIDTH || width == 0) { + return; + } + + if (x + width > VIDEO_WIDTH) { + unsigned int old_width = width; + width = VIDEO_WIDTH - x; + bits >>= (old_width - width); + } + + uint word = x >> 5; + uint shift = x & 31u; + uint64_t aligned = bits << (64u - width - shift); + fb[y][word] |= (uint32_t)(aligned >> 32); + if (shift + width > 32u && word + 1u < WORDS_PER_LINE) { + fb[y][word + 1u] |= (uint32_t)aligned; + } +} + +void draw_character(video_framebuffer_t fb, unsigned int x, unsigned int y, unsigned int scale, char character) { + if(character==0) + return; + unsigned char glyph = (unsigned char)character; + + if (scale == 1) { + for (int j = 0; j < CHAR_HEIGHT; ++j) { + blit_row_bits(fb, x, y + j, glyph_rows[glyph][j], CHAR_WIDTH); + } + return; + } + + if (scale == 2) { + for (int j = 0; j < CHAR_HEIGHT; ++j) { + uint32_t row_bits = glyph_rows_x2[glyph][j]; + blit_row_bits(fb, x, y + (j * 2u), row_bits, CHAR_WIDTH * 2u); + blit_row_bits(fb, x, y + (j * 2u) + 1u, row_bits, CHAR_WIDTH * 2u); + } + return; + } + + for (int j = 0; j < CHAR_HEIGHT; ++j) { + uint16_t row_bits = glyph_rows[glyph][j]; + for (int sy = 0; sy < (int)scale; ++sy) { + for (int i = 0; i < CHAR_WIDTH; ++i) { + if (row_bits & (1u << (CHAR_WIDTH - 1 - i))) { + for (int sx = 0; sx < (int)scale; ++sx) { + unsigned int px = x + (unsigned int)(i * scale + sx); + unsigned int py = y + (unsigned int)(j * scale + sy); + if (px < VIDEO_WIDTH && py < VIDEO_HEIGHT) { + uint word = px >> 5; + uint bit = 31u - (px & 31u); + fb[py][word] |= (1u << bit); + } + } + } + } + } + } +} + +void draw_text_mode(video_framebuffer_t fb, text_mode_buffer_t buffer){ + clear(fb); + for(int y=0; y<20; y++){ + for(int x=0; x<32; x++){ + draw_character(fb, START_X+x*(CHAR_WIDTH+1)*2, START_Y+y*(CHAR_HEIGHT+1)*2, 2, buffer[y][x]); + } + } + video_set_framebuffer(fb); +} diff --git a/text_mode.h b/text_mode.h new file mode 100644 index 0000000..f54c8c9 --- /dev/null +++ b/text_mode.h @@ -0,0 +1,12 @@ +#pragma once + +#include "cvideo.h" + +// Text mode config +#define CHARS_PER_LINE 32 +#define CHAR_LINES 20 + +typedef char text_mode_buffer_t[CHAR_LINES][CHARS_PER_LINE]; + +void init_font_cache(); +void draw_text_mode(video_framebuffer_t fb, text_mode_buffer_t buffer);