working text mode
@@ -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")
|
||||
|
||||
6
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];
|
||||
|
||||
|
||||
BIN
font/mbf_big_00.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
font/mbf_big_00_black_bg.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
font/mbf_big_04.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
font/mbf_big_04_black_bg.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
font/mbf_small_00.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
font/mbf_small_00_black_bg.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
font/mbf_small_04.png
Normal file
|
After Width: | Height: | Size: 988 B |
BIN
font/mbf_small_04_black_bg.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
123
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]);
|
||||
// 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;
|
||||
i = (i+1)%10;
|
||||
sleep_ms(500);
|
||||
framebuffer_switched = false;
|
||||
}
|
||||
|
||||
tight_loop_contents();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------
|
||||
|
||||
131
text_mode.c
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "text_mode.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#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);
|
||||
}
|
||||
12
text_mode.h
Normal file
@@ -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);
|
||||