diff --git a/CMakeLists.txt b/CMakeLists.txt index 74f123a..3b45699 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) +target_sources(picopal PRIVATE main.c cvideo.c) pico_set_program_name(picopal "picopal") @@ -44,6 +44,7 @@ target_link_libraries(picopal hardware_pio hardware_timer hardware_clocks + pico_multicore ) pico_add_extra_outputs(picopal) diff --git a/cvideo.c b/cvideo.c new file mode 100644 index 0000000..a907009 --- /dev/null +++ b/cvideo.c @@ -0,0 +1,196 @@ +#include "cvideo.h" + +#include +#include + +#include "pico/stdlib.h" +#include "hardware/clocks.h" +#include "hardware/dma.h" +#include "hardware/irq.h" +#include "hardware/pio.h" + +#include "video_dma.pio.h" + +// --------------------------- +// User-adjustable pins +// --------------------------- +#define VIDEO_PIO pio0 +#define DATA_SM 0 +#define SYNC_SM 1 + +#define DATA_PIN 2 // pixel / luma bit pin +#define SYNC_PIN 3 // sync pin + +#define SYNC_INTERVAL_S 0.000002f // 2 us per sync-SM instruction +#define ACTIVE_VIDEO_S 0.000052f // 52 us active video per line + +// Match the .pio program's published constant. +// pioasm will emit CLOCKS_PER_BIT in the generated header. +#ifndef CLOCKS_PER_BIT +#define CLOCKS_PER_BIT 6 +#endif + + +// Field/line tracking for line-based DMA. +// This is intentionally kept in software to match the sync SM's 288 active +// lines per field. +static volatile bool even_field = true; +static volatile uint line_in_field = 0; + +// DMA channel that feeds the PIO TX FIFO +static int video_dma_chan; + +static video_framebuffer_ptr_t framebuffer; +static volatile video_framebuffer_ptr_t pending_framebuffer; +static void (*framebuffer_switched_cb)() = NULL; + +// --------------------------- +// PIO init helpers +// --------------------------- +static void init_cvdata_program(PIO pio, uint sm, uint offset, float clkdiv, uint data_pin) { + pio_sm_config c = cvdata_program_get_default_config(offset); + + sm_config_set_set_pins(&c, data_pin, 1); + sm_config_set_out_pins(&c, data_pin, 1); + sm_config_set_clkdiv(&c, clkdiv); + + // Shift left, autopull every 32 bits. + // If the image is mirrored, change this to shift-right and/or change bit packing. + sm_config_set_out_shift(&c, false, true, 32); + + pio_gpio_init(pio, data_pin); + pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true); + + pio_sm_init(pio, sm, offset, &c); + + // Preload X = VIDEO_WIDTH - 1 + pio_sm_put_blocking(pio, sm, VIDEO_WIDTH - 1); + pio_sm_exec(pio, sm, pio_encode_pull(false, false)); + pio_sm_exec(pio, sm, pio_encode_mov(pio_x, pio_osr)); + pio_sm_exec(pio, sm, pio_encode_out(pio_null, 32)); +} + +static void init_cvsync_program(PIO pio, uint sm, uint offset, float clkdiv, uint sync_pin) { + pio_sm_config c = cvsync_program_get_default_config(offset); + + sm_config_set_sideset_pins(&c, sync_pin); + sm_config_set_clkdiv(&c, clkdiv); + + pio_gpio_init(pio, sync_pin); + pio_sm_set_consecutive_pindirs(pio, sm, sync_pin, 1, true); + + pio_sm_init(pio, sm, offset, &c); + + // Preload OSR = lines_per_field - 1 + pio_sm_put_blocking(pio, sm, LINES_PER_FIELD - 1); + pio_sm_exec(pio, sm, pio_encode_pull(false, false)); + + // Start with Y = 0, meaning "even field" in this program. + pio_sm_exec(pio, sm, pio_encode_set(pio_y, 0)); +} + +// --------------------------- +// DMA setup +// --------------------------- +static void init_video_dma(PIO pio, uint data_sm) { + video_dma_chan = dma_claim_unused_channel(true); + + dma_channel_config cfg = dma_channel_get_default_config(video_dma_chan); + channel_config_set_transfer_data_size(&cfg, DMA_SIZE_32); + channel_config_set_read_increment(&cfg, true); + channel_config_set_write_increment(&cfg, false); + + // Pace the DMA from the data SM TX FIFO DREQ + channel_config_set_dreq(&cfg, pio_get_dreq(pio, data_sm, true)); + + dma_channel_configure( + video_dma_chan, + &cfg, + &pio->txf[data_sm], // write address + NULL, // read address set per line + 0, // transfer count set per line + false // don't start yet + ); +} + +// --------------------------- +// Start DMA for one scanline +// --------------------------- +static inline void start_dma_for_current_line(void) { + if (line_in_field == 0 && even_field && pending_framebuffer) { + framebuffer = (video_framebuffer_ptr_t)pending_framebuffer; + pending_framebuffer = NULL; + if(framebuffer_switched_cb) + framebuffer_switched_cb(); + } + + uint src_line = even_field ? (line_in_field * 2u) : (line_in_field * 2u + 1u); + + dma_channel_set_read_addr(video_dma_chan, framebuffer[src_line], false); + dma_channel_set_trans_count(video_dma_chan, WORDS_PER_LINE, true); + + line_in_field++; + if (line_in_field >= LINES_PER_FIELD) { + line_in_field = 0; + even_field = !even_field; + } +} + +// --------------------------- +// PIO IRQ handler: +// The sync SM pushes one token into its RX FIFO during each line's back porch. +// As soon as the previous line DMA has finished, consume one token and arm the +// next scanline so the data FIFO is primed before visible pixels start. +// --------------------------- +static void pio0_irq0_handler(void) { + if (!pio_sm_is_rx_fifo_empty(VIDEO_PIO, SYNC_SM) && !dma_channel_is_busy(video_dma_chan)) { + (void)pio_sm_get(VIDEO_PIO, SYNC_SM); + start_dma_for_current_line(); + } +} + +void video_init(video_framebuffer_t fb) { + if ((VIDEO_WIDTH % 32) != 0) { + panic("VIDEO_WIDTH must be a multiple of 32"); + } + + framebuffer = fb; + pending_framebuffer = NULL; + + uint sync_offset = pio_add_program(VIDEO_PIO, &cvsync_program); + uint data_offset = pio_add_program(VIDEO_PIO, &cvdata_program); + + // Same timing idea as the original repo: + // sync SM: 1 instruction every 2 us + // data SM: enough PIO cycles to emit VIDEO_WIDTH bits in 52 us + float sys_hz = (float)clock_get_hz(clk_sys); + float data_clkdiv = (sys_hz / ((float)VIDEO_WIDTH / ACTIVE_VIDEO_S)) / (float)CLOCKS_PER_BIT; + float sync_clkdiv = sys_hz * SYNC_INTERVAL_S; + + init_cvdata_program(VIDEO_PIO, DATA_SM, data_offset, data_clkdiv, DATA_PIN); + init_cvsync_program(VIDEO_PIO, SYNC_SM, sync_offset, sync_clkdiv, SYNC_PIN); + init_video_dma(VIDEO_PIO, DATA_SM); + + // Route sync-SM RX FIFO notifications to CPU IRQ 0. + pio_set_irq0_source_enabled(VIDEO_PIO, pis_sm1_rx_fifo_not_empty, true); + irq_set_exclusive_handler(PIO0_IRQ_0, pio0_irq0_handler); + irq_set_enabled(PIO0_IRQ_0, true); + + // Start with the first line of the even field + even_field = true; + line_in_field = 0; + + // Prime the FIFO so the very first active line does not depend on IRQ latency. + start_dma_for_current_line(); + + pio_sm_set_enabled(VIDEO_PIO, DATA_SM, true); + pio_sm_set_enabled(VIDEO_PIO, SYNC_SM, true); +} + +void video_set_framebuffer(video_framebuffer_t fb) { + pending_framebuffer = fb; +} + +void video_set_framebuffer_switched_cb(void (*cb)()){ + framebuffer_switched_cb = cb; +} diff --git a/cvideo.h b/cvideo.h new file mode 100644 index 0000000..4f719d0 --- /dev/null +++ b/cvideo.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +// --------------------------- +// Video format +// --------------------------- +#define VIDEO_WIDTH 768 +#define VIDEO_HEIGHT 576 +#define WORDS_PER_LINE (VIDEO_WIDTH / 32) +#define LINES_PER_FIELD (VIDEO_HEIGHT / 2) + +typedef uint32_t video_framebuffer_t[VIDEO_HEIGHT][WORDS_PER_LINE]; +typedef uint32_t (*video_framebuffer_ptr_t)[WORDS_PER_LINE]; + +void video_init(video_framebuffer_t fb); +void video_set_framebuffer(video_framebuffer_t fb); +void video_set_framebuffer_switched_cb(void (*cb)()); diff --git a/font.xbm b/font.xbm new file mode 100644 index 0000000..03a731b --- /dev/null +++ b/font.xbm @@ -0,0 +1,324 @@ +#define font_width 160 +#define font_height 192 +static char font_bits[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x3F, 0xF9, + 0xCC, 0xCF, 0xCF, 0x7C, 0xFC, 0xF3, 0x9F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0x3F, 0x3F, 0xF9, 0xCC, 0x03, 0x4F, 0x3E, + 0xF9, 0xF3, 0x8F, 0x1F, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, + 0xFF, 0x3F, 0x3F, 0x79, 0x80, 0x03, 0x7F, 0x3E, 0xF9, 0xF3, 0xCF, 0x3F, + 0x9F, 0xE4, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0x3F, 0xFF, 0xFF, + 0xCC, 0xCB, 0x3F, 0x7F, 0xFC, 0xFF, 0xE7, 0x7F, 0x3E, 0xF0, 0xF3, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0x3F, 0xFF, 0xFF, 0xCC, 0x03, 0x3F, 0x3F, + 0xF4, 0xFF, 0xE7, 0x7F, 0x7E, 0xF8, 0xC0, 0xFF, 0x1F, 0xFE, 0xFF, 0xF3, + 0xFF, 0x3F, 0xFF, 0x7F, 0x80, 0x4F, 0x9F, 0x9F, 0xF9, 0xFF, 0xE7, 0x7F, + 0x3E, 0xF0, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, + 0xCC, 0x03, 0x9F, 0x9C, 0xF1, 0xFF, 0xE7, 0x7F, 0x9E, 0xE4, 0xF3, 0xCF, + 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0x3F, 0xFF, 0xFF, 0xCC, 0x03, 0xCF, 0x3C, + 0xE4, 0xFF, 0xCF, 0x3F, 0xFF, 0xFC, 0xF3, 0xCF, 0xFF, 0xFF, 0xFC, 0xF9, + 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0x1F, + 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9F, 0x9F, 0xFF, 0xFF, 0xFF, 0xE7, + 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x3F, 0x7F, 0xF8, + 0xC0, 0x8F, 0x0F, 0x7C, 0x78, 0xC0, 0x87, 0x1F, 0xFE, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xE1, 0x3B, 0x1F, 0x3F, 0xF3, 0xCE, 0x87, 0xCF, 0x3F, + 0xFF, 0xCF, 0x3B, 0xEF, 0xFC, 0xFC, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xCC, + 0x3B, 0x0F, 0xBF, 0xF3, 0xCF, 0x93, 0xCF, 0x3F, 0xFF, 0xC7, 0x3B, 0xEF, + 0xFC, 0xFC, 0xF3, 0x3F, 0xFF, 0x3F, 0xFF, 0xCE, 0x3B, 0x3F, 0xFF, 0xF3, + 0xE7, 0x99, 0x0F, 0x3E, 0xF8, 0xE7, 0x87, 0xEF, 0xFC, 0xFF, 0xFF, 0xCF, + 0x1F, 0xFE, 0xFC, 0xC7, 0x3B, 0x3F, 0xFF, 0xF9, 0xE7, 0x99, 0xFF, 0x3C, + 0xF7, 0xF3, 0x3B, 0x1F, 0xFC, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xF3, 0xE3, + 0x3B, 0x3F, 0x7F, 0xFC, 0xCF, 0x01, 0xFF, 0x3C, 0xF7, 0xF3, 0x3B, 0xFF, + 0xFC, 0xFC, 0xF3, 0xF3, 0x1F, 0xFE, 0xF3, 0xF3, 0x3B, 0x3F, 0x3F, 0xFF, + 0xCE, 0x9F, 0xEF, 0x3C, 0xF7, 0xF9, 0x3B, 0xFF, 0xFC, 0xFC, 0xF3, 0xCF, + 0xFF, 0xFF, 0xFC, 0xFF, 0x87, 0x0F, 0x3C, 0xF0, 0xE1, 0x9F, 0x1F, 0x7E, + 0xF8, 0xF9, 0x87, 0x1F, 0xFE, 0xFF, 0xF1, 0x3F, 0xFF, 0x3F, 0xFF, 0xF3, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x87, 0x3F, 0x3F, 0xF8, + 0xE1, 0x83, 0x0F, 0x3C, 0xF0, 0xE1, 0x33, 0x1F, 0x3E, 0xF0, 0xCC, 0xF3, + 0xE7, 0x39, 0xF3, 0xE1, 0x7B, 0x1F, 0x3E, 0xF3, 0xC0, 0x13, 0xCF, 0x3C, + 0xF3, 0xCC, 0x33, 0x3F, 0xFF, 0xF3, 0xE4, 0xF3, 0xC7, 0x38, 0xF3, 0xCC, + 0xCD, 0xCE, 0x3C, 0x73, 0xCE, 0x33, 0xCF, 0x3F, 0xFF, 0xCC, 0x33, 0x3F, + 0xFF, 0xF3, 0xE4, 0xF3, 0x07, 0x38, 0xF2, 0xCC, 0xB5, 0xCE, 0x3C, 0x78, + 0xFE, 0x33, 0x0F, 0x3E, 0xF8, 0xFC, 0x03, 0x3F, 0xFF, 0xF3, 0xF0, 0xF3, + 0x27, 0x39, 0xF0, 0xCC, 0xB5, 0x0E, 0x3C, 0x73, 0xFE, 0x33, 0xCF, 0x3F, + 0xFF, 0xC4, 0x33, 0x3F, 0xFF, 0xF3, 0xE4, 0xF3, 0xE7, 0x39, 0xF1, 0xCC, + 0x4D, 0xCF, 0x3C, 0x73, 0xCE, 0x33, 0xCF, 0x3F, 0xFF, 0xCC, 0x33, 0x3F, + 0x3F, 0xF3, 0xE4, 0xF3, 0xE7, 0x39, 0xF3, 0xCC, 0xFB, 0xCF, 0x3C, 0xF3, + 0xC0, 0x13, 0xCF, 0x3C, 0xFF, 0xCC, 0x33, 0x3F, 0x3F, 0xF3, 0xCC, 0xF3, + 0xE7, 0x39, 0xF3, 0xCC, 0x07, 0xCE, 0x3C, 0xF8, 0xE1, 0x83, 0x0F, 0x3C, + 0xFF, 0xE1, 0x33, 0x1F, 0x7E, 0xF8, 0xCC, 0x03, 0xE7, 0x39, 0xF3, 0xE1, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0x1F, 0x3E, 0xF8, + 0xE1, 0x03, 0xCF, 0x3C, 0x73, 0x9E, 0x39, 0xCF, 0x3C, 0xF0, 0xF1, 0xF3, + 0x1F, 0xFF, 0xFC, 0xFF, 0x33, 0xCF, 0x3C, 0xF3, 0xDC, 0xCF, 0xCF, 0x3C, + 0x73, 0x9E, 0x93, 0xCF, 0xFC, 0xF3, 0xF9, 0xF3, 0x3F, 0x7F, 0xF8, 0xFF, + 0x33, 0xCF, 0x3C, 0xF3, 0xFC, 0xCF, 0xCF, 0x3C, 0x73, 0x9E, 0x93, 0x1F, + 0xFE, 0xF9, 0xF9, 0xE7, 0x3F, 0x3F, 0xF3, 0xFF, 0x33, 0xCF, 0x3C, 0xF3, + 0xE1, 0xCF, 0xCF, 0x3C, 0x73, 0x92, 0xC7, 0x1F, 0xFE, 0xFC, 0xF9, 0xE7, + 0x3F, 0xFF, 0xFF, 0xFF, 0x83, 0xCF, 0x3C, 0xF8, 0xCF, 0xCF, 0xCF, 0x3C, + 0x73, 0x92, 0xC7, 0x3F, 0x7F, 0xFE, 0xF9, 0xCF, 0x3F, 0xFF, 0xFF, 0xFF, + 0xF3, 0xCF, 0x3C, 0xF9, 0xCF, 0xCF, 0xCF, 0x3C, 0x73, 0x80, 0x93, 0x3F, + 0x3F, 0xFF, 0xF9, 0xCF, 0x3F, 0xFF, 0xFF, 0xFF, 0xF3, 0xCF, 0x3C, 0xF3, + 0xCC, 0xCF, 0xCF, 0x7C, 0x78, 0x80, 0x93, 0x3F, 0x3F, 0xF0, 0xF9, 0x9F, + 0x3F, 0xFF, 0xFF, 0xFF, 0xF3, 0x1F, 0x3E, 0xF3, 0xE1, 0xCF, 0x1F, 0xFE, + 0xFC, 0xCC, 0x39, 0x3F, 0x3F, 0xF0, 0xF9, 0x9F, 0x3F, 0xFF, 0xFF, 0xFF, + 0xFF, 0x7F, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xF9, 0x3F, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0x3F, + 0x1F, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, + 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0x3F, 0xFF, + 0xFF, 0x3F, 0xFF, 0xFF, 0xF8, 0xFF, 0xF3, 0x3F, 0xFF, 0xF9, 0xFC, 0xC7, + 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0x3F, 0xFF, 0xFF, 0x3F, 0xFF, 0x7F, + 0xF8, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x9F, 0x0F, 0x3F, 0xFF, 0xE1, 0x3F, 0x1F, 0x7E, 0xFE, 0xC0, 0x83, 0x1F, + 0x7F, 0xF8, 0xCC, 0xCF, 0x07, 0x3C, 0xF8, 0xE1, 0xFF, 0x7F, 0x3E, 0xF8, + 0xCC, 0x07, 0xCF, 0x7C, 0x7E, 0xE6, 0x33, 0x3F, 0xFF, 0xF9, 0xE4, 0xCF, + 0x27, 0x39, 0xF3, 0xCC, 0xFF, 0x0F, 0x3E, 0xF3, 0xFC, 0x33, 0xCF, 0x3C, + 0x7C, 0xE6, 0x33, 0x3F, 0xFF, 0xF9, 0xF0, 0xCF, 0x27, 0x39, 0xF3, 0xCC, + 0xFF, 0x67, 0x3E, 0xF3, 0xFC, 0x33, 0x0F, 0x7C, 0xFE, 0xF0, 0x33, 0x3F, + 0xFF, 0xF9, 0xF0, 0xCF, 0x27, 0x39, 0xF3, 0xCC, 0xFF, 0x67, 0x3E, 0xF3, + 0xCC, 0x33, 0xCF, 0x7F, 0x7E, 0xFC, 0x33, 0x3F, 0xFF, 0xF9, 0xE4, 0xCF, + 0x27, 0x39, 0xF3, 0xCC, 0xFF, 0x8F, 0x3C, 0xF8, 0xE1, 0x07, 0x1F, 0x7E, + 0x7E, 0xF0, 0x33, 0x1F, 0xFE, 0xF9, 0xCC, 0x87, 0x27, 0x39, 0xF3, 0xE1, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xE6, 0xFF, 0xFF, + 0x7F, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0x7F, 0xFC, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xCF, + 0x9F, 0xFF, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xCF, 0x3F, 0xFF, 0x7F, 0x9E, + 0x83, 0x1F, 0x7C, 0xF8, 0xC1, 0xE7, 0xCF, 0x3C, 0x73, 0x9E, 0x39, 0xCF, + 0x3C, 0xF8, 0xF3, 0xCF, 0x3F, 0xFF, 0x7F, 0x9E, 0x33, 0xCF, 0x3C, 0xF6, + 0xFC, 0x87, 0xCF, 0x3C, 0x73, 0x9E, 0x93, 0xCF, 0xFC, 0xF9, 0xF3, 0xCF, + 0x3F, 0x3F, 0x66, 0x9E, 0x33, 0xCF, 0x3C, 0xFF, 0xE0, 0xE7, 0xCF, 0x3C, + 0x73, 0x92, 0xC7, 0xCF, 0xFC, 0xFC, 0xF9, 0xCF, 0x7F, 0x9E, 0x64, 0x9E, + 0x33, 0xCF, 0x3C, 0xFF, 0xC7, 0xE7, 0xCF, 0x3C, 0x73, 0x92, 0xC7, 0xCF, + 0x7C, 0xFE, 0xF9, 0xCF, 0x7F, 0x9E, 0x71, 0x9E, 0x33, 0xCF, 0x3C, 0xFF, + 0xC7, 0x87, 0xCF, 0x7C, 0x78, 0x80, 0x93, 0xCF, 0x3C, 0xFF, 0xF3, 0xCF, + 0x3F, 0xFF, 0x7F, 0x9E, 0x83, 0x1F, 0x3C, 0xFF, 0xE0, 0x8F, 0x1F, 0xFC, + 0xFC, 0xCC, 0x39, 0x1F, 0x3C, 0xF8, 0xF3, 0xCF, 0x3F, 0xFF, 0x7F, 0x9E, + 0xF3, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFC, 0xFF, 0xF3, 0xCF, 0x3F, 0xFF, 0x7F, 0x9E, 0xF3, 0xFF, 0xFC, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xE7, 0xCF, + 0x9F, 0xFF, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x3E, 0xFF, 0xFB, + 0xE3, 0xFF, 0xCF, 0xFC, 0xFC, 0xF3, 0x01, 0xFE, 0x1F, 0xE0, 0xFF, 0xFF, + 0x07, 0xF8, 0x7F, 0x80, 0x79, 0x3E, 0x7F, 0xF0, 0xC9, 0xFD, 0xCE, 0xFC, + 0xFC, 0xED, 0x79, 0x1E, 0x9E, 0xE7, 0xFF, 0xFF, 0xE7, 0x79, 0x78, 0x9E, + 0x79, 0xFE, 0x3F, 0xE0, 0xF9, 0x4B, 0x1F, 0xFE, 0xFC, 0xFD, 0x79, 0xEE, + 0x9D, 0xE7, 0x99, 0xFF, 0xE7, 0xB9, 0x77, 0x9E, 0x79, 0x3E, 0x9F, 0xE5, + 0xFB, 0xB7, 0x1F, 0xFE, 0xFC, 0xFB, 0x79, 0x36, 0x9B, 0xE7, 0xCC, 0xFF, + 0xE7, 0x59, 0x6C, 0x9E, 0x79, 0x3E, 0x9F, 0xFD, 0xE0, 0x7B, 0x3F, 0xFF, + 0xFF, 0xF5, 0x79, 0xD6, 0x9B, 0x67, 0xE6, 0x01, 0xE6, 0x59, 0x6B, 0x9E, + 0x79, 0x3E, 0x9F, 0xFE, 0xFB, 0x7B, 0x0F, 0xFC, 0xFF, 0xEB, 0x79, 0xD6, + 0x9B, 0x67, 0xE6, 0x7F, 0xE6, 0x59, 0x6C, 0x9E, 0x79, 0x3E, 0x9F, 0xE6, + 0xF8, 0xB7, 0x3F, 0xFF, 0xFC, 0xF7, 0x79, 0x36, 0x9B, 0xE7, 0xCC, 0x7F, + 0xE6, 0x59, 0x6B, 0x9E, 0x79, 0x3E, 0x3F, 0x60, 0xB9, 0x4B, 0x0F, 0xFC, + 0xFC, 0xEF, 0x79, 0xEE, 0x9D, 0xE7, 0x99, 0x7F, 0xE6, 0xB9, 0x77, 0x9E, + 0x79, 0x3E, 0x7F, 0xF0, 0xC4, 0xFD, 0x3E, 0xFF, 0xFC, 0xED, 0x79, 0x1E, + 0x9E, 0xE7, 0xFF, 0xFF, 0xE7, 0x79, 0x78, 0x9E, 0x01, 0xFE, 0x7F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xF3, 0x01, 0xFE, 0x1F, 0xE0, 0xFF, 0xFF, + 0x07, 0xF8, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFC, + 0xE3, 0x01, 0xFE, 0x7F, 0xE0, 0xFF, 0x01, 0x7E, 0x1F, 0xE0, 0xFF, 0xFB, + 0xEF, 0x1F, 0xFF, 0xF3, 0xB7, 0xFF, 0xFF, 0xFB, 0xEF, 0x79, 0xFE, 0x3F, + 0xF4, 0xFF, 0x79, 0x3E, 0x9F, 0xE7, 0xFF, 0xF9, 0xE6, 0x7B, 0xEF, 0xF3, + 0xB7, 0x3F, 0xFF, 0xFD, 0xF7, 0x79, 0xCE, 0x3C, 0xF4, 0xFF, 0x79, 0x7E, + 0x9F, 0x67, 0xE6, 0x7B, 0xEF, 0xBD, 0xF7, 0xFF, 0xCF, 0x3F, 0xFF, 0xFE, + 0xEF, 0x79, 0xCE, 0x7C, 0xF4, 0xFF, 0x79, 0x7E, 0x9F, 0xE7, 0xCC, 0xBB, + 0xEF, 0x7E, 0xFB, 0xF3, 0xFF, 0x0F, 0xFC, 0xF8, 0xF3, 0x79, 0xCE, 0xFC, + 0xF4, 0xF3, 0x79, 0x3E, 0x9E, 0xE7, 0x99, 0xD1, 0x47, 0x9F, 0xFD, 0xF1, + 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x79, 0xCE, 0xFC, 0xF4, 0xF3, 0x79, 0xFE, + 0x9F, 0xE7, 0x99, 0xAF, 0xBE, 0xFC, 0xEA, 0xF8, 0xFF, 0x3F, 0xFF, 0xFF, + 0xFF, 0x79, 0xCE, 0xFC, 0xF4, 0xFF, 0x79, 0xFE, 0x9F, 0xE7, 0xCC, 0xB7, + 0xDE, 0x7B, 0xEB, 0xDC, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x79, 0x2E, 0xFA, + 0xF4, 0xFF, 0x79, 0xFE, 0x9F, 0x67, 0xE6, 0x3B, 0xEE, 0xBD, 0xE3, 0xCC, + 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x79, 0xEE, 0xFF, 0xFF, 0xFF, 0x79, 0xFE, + 0x9F, 0xE7, 0xFF, 0xFD, 0xF6, 0xDE, 0xEF, 0xE1, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x01, 0xEE, 0xFF, 0xFF, 0xFF, 0x01, 0xFE, 0x1F, 0xE0, 0xFF, 0xFF, + 0xFE, 0xF8, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0xE0, 0xFF, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0xE7, 0xFF, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xDE, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xED, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0xE7, 0xF3, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xF3, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xED, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0xE7, 0xDE, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xFF, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0xE0, 0xFF, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0xE0, 0xFF, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0xE7, 0xF3, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xF3, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xFF, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0xE7, 0xC0, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xFF, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xF3, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0xE7, 0xF3, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0xE7, 0xFF, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0xE0, 0xFF, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, + 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, + 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, + 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, + 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x79, 0xE6, + 0x99, 0x67, 0x9E, 0x79, 0xE6, 0x99, 0x67, 0x9E, 0x01, 0x06, 0x18, 0x60, + 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, 0x06, 0x18, 0x60, 0x80, 0x01, + 0x06, 0x18, 0x60, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }; \ No newline at end of file diff --git a/main.c b/main.c index f5da604..25b6ed3 100644 --- a/main.c +++ b/main.c @@ -1,231 +1,116 @@ -// main.c #include #include #include "pico/stdlib.h" -#include "hardware/clocks.h" -#include "hardware/dma.h" -#include "hardware/irq.h" -#include "hardware/pio.h" +#include "pico/multicore.h" -#include "video_dma.pio.h" +#include "cvideo.h" -// --------------------------- -// User-adjustable pins -// --------------------------- -#define VIDEO_PIO pio0 -#define DATA_SM 0 -#define SYNC_SM 1 +#include "font.xbm" -#define DATA_PIN 2 // pixel / luma bit pin -#define SYNC_PIN 3 // sync pin +static video_framebuffer_t fb0, fb1; -// --------------------------- -// Video format -// --------------------------- -#define VIDEO_WIDTH 768 -#define VIDEO_HEIGHT 576 -#define WORDS_PER_LINE (VIDEO_WIDTH / 32) -#define LINES_PER_FIELD (VIDEO_HEIGHT / 2) +#define START_X 35 +#define END_X (VIDEO_WIDTH-40) +#define START_Y 20 +#define END_Y (VIDEO_HEIGHT-40) -#define SYNC_INTERVAL_S 0.000002f // 2 us per sync-SM instruction -#define ACTIVE_VIDEO_S 0.000052f // 52 us active video per line +#define CHARS_PER_LINE 32 +#define CHAR_LINES 20 -// Match the .pio program's published constant. -// pioasm will emit CLOCKS_PER_BIT in the generated header. -#ifndef CLOCKS_PER_BIT -#define CLOCKS_PER_BIT 6 -#endif +static char text_mode_buffer[20][32]; -// --------------------------- -// Framebuffer -// One bit per pixel, 32 pixels per word -// --------------------------- -static uint32_t framebuffer[VIDEO_HEIGHT][WORDS_PER_LINE]; +static void clear(video_framebuffer_t fb){ + memset(fb, 0, sizeof(video_framebuffer_t)); +} -// Field/line tracking for line-based DMA. -// This is intentionally kept in software to match the sync SM's 288 active -// lines per field. -static volatile bool even_field = true; -static volatile uint line_in_field = 0; +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; -// DMA channel that feeds the PIO TX FIFO -static int video_dma_chan; + uint flag = value << (31-pos_x); + if(value) + fb[y][index_x] |= flag; + else + fb[y][index_x] &= ~flag; +} -// --------------------------- -// Optional test pattern -// Replace this with your own drawing code. -// --------------------------- -#define BORDER 40 -static void fill_test_pattern(void) { - memset(framebuffer, 0x00, sizeof(framebuffer)); +#define CHAR_WIDTH font_width / 16 +#define CHAR_HEIGHT font_height / 16 - for (uint y = 0; y < VIDEO_HEIGHT; ++y) { - for (uint x = 0; x < VIDEO_WIDTH; ++x) { - bool on = false; +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; - // Simple visible pattern: - // border + checker - if (x < BORDER || x >= VIDEO_WIDTH - BORDER || y < BORDER || y >= VIDEO_HEIGHT - BORDER) { - on = true; - } else if (((x >> 4) ^ (y >> 4)) & 1) { - on = true; + 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; - if (on) { - uint word = x >> 5; - uint bit = 31 - (x & 31); // MSB-first - framebuffer[y][word] |= (1u << bit); + 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); + } } } } } -// --------------------------- -// PIO init helpers -// --------------------------- -static void init_cvdata_program(PIO pio, uint sm, uint offset, float clkdiv, uint data_pin) { - pio_sm_config c = cvdata_program_get_default_config(offset); - - sm_config_set_set_pins(&c, data_pin, 1); - sm_config_set_out_pins(&c, data_pin, 1); - sm_config_set_clkdiv(&c, clkdiv); - - // Shift left, autopull every 32 bits. - // If the image is mirrored, change this to shift-right and/or change bit packing. - sm_config_set_out_shift(&c, false, true, 32); - - pio_gpio_init(pio, data_pin); - pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true); - - pio_sm_init(pio, sm, offset, &c); - - // Preload X = VIDEO_WIDTH - 1 - pio_sm_put_blocking(pio, sm, VIDEO_WIDTH - 1); - pio_sm_exec(pio, sm, pio_encode_pull(false, false)); - pio_sm_exec(pio, sm, pio_encode_mov(pio_x, pio_osr)); - pio_sm_exec(pio, sm, pio_encode_out(pio_null, 32)); -} - -static void init_cvsync_program(PIO pio, uint sm, uint offset, float clkdiv, uint sync_pin) { - pio_sm_config c = cvsync_program_get_default_config(offset); - - sm_config_set_sideset_pins(&c, sync_pin); - sm_config_set_clkdiv(&c, clkdiv); - - pio_gpio_init(pio, sync_pin); - pio_sm_set_consecutive_pindirs(pio, sm, sync_pin, 1, true); - - pio_sm_init(pio, sm, offset, &c); - - // Preload OSR = lines_per_field - 1 - pio_sm_put_blocking(pio, sm, LINES_PER_FIELD - 1); - pio_sm_exec(pio, sm, pio_encode_pull(false, false)); - - // Start with Y = 0, meaning "even field" in this program. - pio_sm_exec(pio, sm, pio_encode_set(pio_y, 0)); -} - -// --------------------------- -// DMA setup -// --------------------------- -static void init_video_dma(PIO pio, uint data_sm) { - video_dma_chan = dma_claim_unused_channel(true); - - dma_channel_config cfg = dma_channel_get_default_config(video_dma_chan); - channel_config_set_transfer_data_size(&cfg, DMA_SIZE_32); - channel_config_set_read_increment(&cfg, true); - channel_config_set_write_increment(&cfg, false); - - // Pace the DMA from the data SM TX FIFO DREQ - channel_config_set_dreq(&cfg, pio_get_dreq(pio, data_sm, true)); - - dma_channel_configure( - video_dma_chan, - &cfg, - &pio->txf[data_sm], // write address - NULL, // read address set per line - 0, // transfer count set per line - false // don't start yet - ); -} - -// --------------------------- -// Start DMA for one scanline -// --------------------------- -static inline void start_dma_for_current_line(void) { - uint src_line = even_field ? (line_in_field * 2u) : (line_in_field * 2u + 1u); - - dma_channel_set_read_addr(video_dma_chan, framebuffer[src_line], false); - dma_channel_set_trans_count(video_dma_chan, WORDS_PER_LINE, true); - - line_in_field++; - if (line_in_field >= LINES_PER_FIELD) { - line_in_field = 0; - even_field = !even_field; - } -} - -// --------------------------- -// PIO IRQ handler: -// The sync SM pushes one token into its RX FIFO during each line's back porch. -// As soon as the previous line DMA has finished, consume one token and arm the -// next scanline so the data FIFO is primed before visible pixels start. -// --------------------------- -static void pio0_irq0_handler(void) { - if (!pio_sm_is_rx_fifo_empty(VIDEO_PIO, SYNC_SM) && !dma_channel_is_busy(video_dma_chan)) { - (void)pio_sm_get(VIDEO_PIO, SYNC_SM); - start_dma_for_current_line(); - } -} - -// --------------------------- -// Video start -// --------------------------- -static void video_init(void) { - if ((VIDEO_WIDTH % 32) != 0) { - panic("VIDEO_WIDTH must be a multiple of 32"); - } - - uint sync_offset = pio_add_program(VIDEO_PIO, &cvsync_program); - uint data_offset = pio_add_program(VIDEO_PIO, &cvdata_program); - - // Same timing idea as the original repo: - // sync SM: 1 instruction every 2 us - // data SM: enough PIO cycles to emit VIDEO_WIDTH bits in 52 us - float sys_hz = (float)clock_get_hz(clk_sys); - float data_clkdiv = (sys_hz / ((float)VIDEO_WIDTH / ACTIVE_VIDEO_S)) / (float)CLOCKS_PER_BIT; - float sync_clkdiv = sys_hz * SYNC_INTERVAL_S; - - init_cvdata_program(VIDEO_PIO, DATA_SM, data_offset, data_clkdiv, DATA_PIN); - init_cvsync_program(VIDEO_PIO, SYNC_SM, sync_offset, sync_clkdiv, SYNC_PIN); - init_video_dma(VIDEO_PIO, DATA_SM); - - // Route sync-SM RX FIFO notifications to CPU IRQ 0. - pio_set_irq0_source_enabled(VIDEO_PIO, pis_sm1_rx_fifo_not_empty, true); - irq_set_exclusive_handler(PIO0_IRQ_0, pio0_irq0_handler); - irq_set_enabled(PIO0_IRQ_0, true); - - // Start with the first line of the even field - even_field = true; - line_in_field = 0; - - // Prime the FIFO so the very first active line does not depend on IRQ latency. - start_dma_for_current_line(); - - pio_sm_set_enabled(VIDEO_PIO, DATA_SM, true); - pio_sm_set_enabled(VIDEO_PIO, SYNC_SM, true); -} - -int main(void) { - stdio_init_all(); - - fill_test_pattern(); // Replace with your own framebuffer writer - video_init(); - +void core1_entry() { + gpio_init(25); + gpio_set_dir(25, GPIO_OUT); while (true) { - // Your application code goes here. - // Some other code can freely draw into framebuffer[]. + gpio_put(25, 1); + sleep_ms(500); + gpio_put(25, 0); + sleep_ms(500); + } +} + + +void framebuffer_switched_cb(){ +} + +void main() { + stdio_init_all(); + multicore_launch_core1(core1_entry); + + clear(fb0); + clear(fb1); + video_init(fb0); + video_set_framebuffer_switched_cb(framebuffer_switched_cb); + + 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); + tight_loop_contents(); } } diff --git a/video_dma.pio b/video_dma.pio index a92f610..40a28d7 100644 --- a/video_dma.pio +++ b/video_dma.pio @@ -10,6 +10,7 @@ .define DATA_DELAY 4 .define LINE_IRQ 0 +.define HSHIFT_DELAY 0 .define PUBLIC CLOCKS_PER_BIT DATA_DELAY + 2 .program cvsync @@ -85,15 +86,16 @@ hsync_video: ; X = pixels_per_line - 1, preloaded once from C ; Each line: ; clear output -; reload Y from X ; wait for sync SM to raise LINE_IRQ +; apply a small extra delay for horizontal positioning +; reload Y from X ; shift out one bit per loop .wrap_target set pins, 0 - mov y, x wait 1 irq LINE_IRQ irq clear LINE_IRQ + mov y, x data_out: out pins, 1 [DATA_DELAY]