diff --git a/Code/README.md b/Code/README.md index 93e9074..41bd82c 100644 --- a/Code/README.md +++ b/Code/README.md @@ -15,7 +15,7 @@ See how to setup pico sdk development https://github.com/clockworkpi/PicoCalc/wiki/Setting-Up-the-Pico-SDK-on-Linux-for-Pico-Development -## How to compile FUZIX for picocalc +## How to compile FUZIX ```bash git clone https://github.com/EtchedPixels/FUZIX.git cd FUZIX @@ -31,7 +31,7 @@ make world now copy `build/fuzix.uf2` into picocalc like every pico device -## How to compile PicoMite(MMB) for picocalc +## How to compile PicoMite(MMB) ```bash git clone https://github.com/cuu/PicoMite.git @@ -48,4 +48,32 @@ make copy `build/PicoMite.uf2` into picocalc like every pico device +## How to compile NES emulator +```bash +git clone https://github.com/shapoco/shapones.git + +cd shapones +git apply ~/github/clockworkpi/PicoCalc/Code/shapones.patch +cd samples/v3/ +mkdir build +cd build +export PICO_SDK_PATH=/to/where/your/pico/sdk/is +cmake .. +make +``` + +copy picocalc_nes.uf2 into picocalc + +use up/down key to select *.nes rom, +* - key is select +* = key is start, +* [ key is a +* ] key is b + +press -(select) and =(start) together, will reset the emulator to the rom selection page. + + + + + diff --git a/Code/shapones.patch b/Code/shapones.patch new file mode 100644 index 0000000..cec413e --- /dev/null +++ b/Code/shapones.patch @@ -0,0 +1,1914 @@ +diff --git a/samples/fatfs/source/mmc_pico_spi.c b/samples/fatfs/source/mmc_pico_spi.c +index 58e633b..a5865bd 100644 +--- a/samples/fatfs/source/mmc_pico_spi.c ++++ b/samples/fatfs/source/mmc_pico_spi.c +@@ -15,12 +15,14 @@ + // see: https://qiita.com/Yukiya_Ishioka/items/6b5b6cb246f1d1e94461 + + #include "pico/stdlib.h" +-#include "ws19804_c.h" ++#include ++#include ++#include "picocalc_c.h" + +-#define DEF_SPI_TX_PIN 11 +-#define DEF_SPI_RX_PIN 12 +-#define DEF_SPI_SCK_PIN 10 +-#define DEF_SPI_CSN_PIN 22 ++#define DEF_SPI_TX_PIN 19 ++#define DEF_SPI_RX_PIN 16 ++#define DEF_SPI_SCK_PIN 18 ++#define DEF_SPI_CSN_PIN 17 + + #define FCLK_FAST() { } + #define FCLK_SLOW() { } +@@ -80,26 +82,57 @@ static volatile UINT Timer1, Timer2; /* 1kHz decrement timer stopped at z + static BYTE CardType; /* Card type flags */ + + ++void HWReadSPI(unsigned char *buff, int cnt) { ++ spi_read_blocking(Pico_SD_SPI_MOD, 0xff, buff, cnt); ++} ++ ++void HWSendSPI(const unsigned char *buff, int cnt) { ++ spi_write_blocking(Pico_SD_SPI_MOD, buff, cnt); ++} + + /*-----------------------------------------------------------------------*/ + /* SPI controls (Platform dependent) */ + /*-----------------------------------------------------------------------*/ + + /* Initialize MMC interface */ +-static void init_spi (void) ++static void init_sd_spi (void) + { ++ ++ //initilase GPIO ports ++ gpio_init(DEF_SPI_SCK_PIN ); ++ gpio_init(DEF_SPI_TX_PIN); ++ gpio_init(DEF_SPI_RX_PIN); ++ gpio_put(DEF_SPI_SCK_PIN,0); ++ gpio_put(DEF_SPI_TX_PIN,0); ++ gpio_set_pulls(DEF_SPI_RX_PIN,true,false); ++ ++ //set GPIO post function ++ gpio_set_function(DEF_SPI_SCK_PIN, GPIO_FUNC_SPI); // SCK ++ gpio_set_function(DEF_SPI_TX_PIN, GPIO_FUNC_SPI); // TX ++ gpio_set_function(DEF_SPI_RX_PIN, GPIO_FUNC_SPI); // RX ++ ++ gpio_set_dir(DEF_SPI_SCK_PIN,GPIO_OUT); ++ gpio_set_dir(DEF_SPI_TX_PIN,GPIO_OUT); ++ gpio_set_dir(DEF_SPI_RX_PIN,GPIO_IN); ++ ++ + /* CS# */ + gpio_init( DEF_SPI_CSN_PIN ); ++ gpio_set_function(DEF_SPI_CSN_PIN, GPIO_FUNC_SIO); + gpio_set_dir( DEF_SPI_CSN_PIN, GPIO_OUT); + + CS_HIGH(); /* Set CS# high */ +- ++ ++ //initalise SPI module ++ spi_init(Pico_SD_SPI_MOD, 250000); ++ spi_set_format(Pico_SD_SPI_MOD, 8, 0, 0, SPI_MSB_FIRST); ++ + sleep_ms(10); + } + + static BYTE rcvr_spi() { + BYTE buff; +- ws19804_read_blocking(0xff, &buff, 1); ++ HWReadSPI( &buff, 1); + return buff; + } + +@@ -109,12 +142,12 @@ static void rcvr_spi_multi ( + UINT btr /* Number of bytes to receive (even number) */ + ) + { +- ws19804_read_blocking(0xff, buff, btr); ++ HWReadSPI(buff, btr); + } + + + static void xmit_spi(BYTE buff) { +- ws19804_write_blocking(&buff, 1); ++ HWSendSPI(&buff, 1); + } + + /* Send multiple byte */ +@@ -123,7 +156,7 @@ static void xmit_spi_multi ( + UINT btx /* Number of bytes to send (even number) */ + ) + { +- ws19804_write_blocking(buff, btx); ++ HWSendSPI(buff, btx); + } + + +@@ -298,7 +331,7 @@ DSTATUS disk_initialize ( + + + if (drv) return STA_NOINIT; /* Supports only drive 0 */ +- init_spi(); /* Initialize SPI */ ++ init_sd_spi(); /* Initialize SPI */ + + if (Stat & STA_NODISK) return Stat; /* Is card existing in the soket? */ + +diff --git a/samples/font/mono8x16/mono8x16.cpp b/samples/font/mono8x16/mono8x16.cpp +index 0e322f2..317e79f 100644 +--- a/samples/font/mono8x16/mono8x16.cpp ++++ b/samples/font/mono8x16/mono8x16.cpp +@@ -2,71 +2,127 @@ + + namespace mono8x16 { + +-static constexpr int W = 8; +-static constexpr int H = 16; +-static constexpr int CHAR_STRIDE = (W * H + 7) / 8; ++ static constexpr int W = 8; ++ static constexpr int H = 16; ++ static constexpr int CHAR_STRIDE = (W * H + 7) / 8; + +-void draw_char_rgb444( +- uint8_t *dest, +- int dest_stride, +- int dest_w, +- int dest_h, +- int x0, +- int y0, +- const char c, +- uint16_t color +-) { +- if (c <= 0x20 || 0x80 <= c) return; +- const uint8_t *rd_ptr = bmp + (c - 0x20) * CHAR_STRIDE; +- for (int iy = 0; iy < H; iy++) { +- int y = y0 + iy; +- uint8_t pattern = *(rd_ptr++); +- if (0 <= y && y < dest_h) { +- for (int ix = 0; ix < W; ix++) { +- int x = x0 + ix; +- if ((pattern & 1) && 0 <= x && x < dest_w) { +- int wr_index = dest_stride * y + x * 3 / 2; +- if ((x & 1) == 0) { +- dest[wr_index ] = (color >> 4) & 0xff; +- dest[wr_index+1] &= 0x0f; +- dest[wr_index+1] |= (color << 4) & 0xf0; ++ void draw_char_rgb444( ++ uint8_t *dest, ++ int dest_stride, ++ int dest_w, ++ int dest_h, ++ int x0, ++ int y0, ++ const char c, ++ uint16_t color ++ ) { ++ if (c <= 0x20 || 0x80 <= c) return; ++ const uint8_t *rd_ptr = bmp + (c - 0x20) * CHAR_STRIDE; ++ for (int iy = 0; iy < H; iy++) { ++ int y = y0 + iy; ++ uint8_t pattern = *(rd_ptr++); ++ if (0 <= y && y < dest_h) { ++ for (int ix = 0; ix < W; ix++) { ++ int x = x0 + ix; ++ if ((pattern & 1) && 0 <= x && x < dest_w) { ++ int wr_index = dest_stride * y + x * 3 / 2; ++ if ((x & 1) == 0) { ++ dest[wr_index] = (color >> 4) & 0xff; //高8位 ++ dest[wr_index + 1] &= 0x0f; ++ dest[wr_index + 1] |= (color << 4) & 0xf0;//低4位 ++ } else { ++ dest[wr_index] &= 0xf0; ++ dest[wr_index] |= (color >> 8) & 0xf;//高4位 ++ dest[wr_index + 1] = color & 0xff;//低8位 ++ } + } +- else { +- dest[wr_index ] &= 0xf0; +- dest[wr_index ] |= (color >> 8) & 0xf; +- dest[wr_index+1] = color & 0xff; ++ pattern >>= 1; ++ } ++ } ++ } ++ } ++ ++ void draw_string_rgb444( ++ uint8_t *dest, ++ int dest_stride, ++ int dest_w, ++ int dest_h, ++ int x0, ++ int y0, ++ const char *str, ++ uint16_t color ++ ) { ++ char c; ++ while ((c = *(str++)) != '\0') { ++ draw_char_rgb444( ++ dest, ++ dest_stride, ++ dest_w, ++ dest_h, ++ x0, ++ y0, ++ c, ++ color ++ ); ++ x0 += W + 1; ++ } ++ } ++ ++ void draw_char_rgb565( ++ uint8_t *dest, ++ int dest_stride, ++ int dest_w, ++ int dest_h, ++ int x0, ++ int y0, ++ const char c, ++ uint16_t color ++ ) { ++ if (c <= 0x20 || 0x80 <= c) return; ++ const uint8_t *rd_ptr = bmp + (c - 0x20) * CHAR_STRIDE; ++ for (int iy = 0; iy < H; iy++) { ++ int y = y0 + iy; ++ uint8_t pattern = *(rd_ptr++); ++ if (0 <= y && y < dest_h) { ++ for (int ix = 0; ix < W; ix++) { ++ int x = x0 + ix; ++ if ((pattern & 1) && 0 <= x && x < dest_w) { ++ int wr_index = dest_stride * y + x * 2; // 每个像素占2字节 ++ dest[wr_index ] = (color >> 8) & 0xff; ++ dest[wr_index + 1 ] = (color & 0xff); ++ + } ++ pattern >>= 1; + } +- pattern >>= 1; + } + } + } +-} + +-void draw_string_rgb444( +- uint8_t *dest, +- int dest_stride, +- int dest_w, +- int dest_h, +- int x0, +- int y0, +- const char* str, +- uint16_t color +-) { +- char c; +- while ((c = *(str++)) != '\0') { +- draw_char_rgb444( +- dest, +- dest_stride, +- dest_w, +- dest_h, +- x0, +- y0, +- c, +- color +- ); +- x0 += W + 1; ++ void draw_string_rgb565( ++ uint8_t *dest, ++ int dest_stride, ++ int dest_w, ++ int dest_h, ++ int x0, ++ int y0, ++ const char *str, ++ uint16_t color ++ ) { ++ char c; ++ while ((c = *(str++)) != '\0') { ++ draw_char_rgb565( ++ dest, ++ dest_stride, ++ dest_w, ++ dest_h, ++ x0, ++ y0, ++ c, ++ color ++ ); ++ x0 += W + 1; ++ } + } +-} ++ + + } +diff --git a/samples/font/mono8x16/mono8x16.hpp b/samples/font/mono8x16/mono8x16.hpp +index b18fedc..764445a 100644 +--- a/samples/font/mono8x16/mono8x16.hpp ++++ b/samples/font/mono8x16/mono8x16.hpp +@@ -29,6 +29,27 @@ void draw_string_rgb444( + uint16_t color + ); + ++ ++ void draw_char_rgb565( ++ uint8_t *dest, ++ int dest_stride, ++ int dest_w, ++ int dest_h, ++ int x0, ++ int y0, ++ const char c, ++ uint16_t color ++ ); ++ void draw_string_rgb565( ++ uint8_t *dest, ++ int dest_stride, ++ int dest_w, ++ int dest_h, ++ int x0, ++ int y0, ++ const char* str, ++ uint16_t color ++ ); + }; + + #endif +diff --git a/samples/v3/CMakeLists.txt b/samples/v3/CMakeLists.txt +new file mode 100644 +index 0000000..56eccdd +--- /dev/null ++++ b/samples/v3/CMakeLists.txt +@@ -0,0 +1,65 @@ ++cmake_minimum_required(VERSION 3.12) ++ ++# Pull in PICO SDK (must be before project) ++set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) ++include(${PICO_SDK_PATH}/pico_sdk_init.cmake) ++ ++project(picocalc_nes C CXX ASM) ++set(CMAKE_C_STANDARD 11) ++set(CMAKE_CXX_STANDARD 17) ++ ++# Initialize the SDK ++pico_sdk_init() ++ ++add_executable(picocalc_nes) ++ ++pico_generate_pio_header(picocalc_nes ${CMAKE_CURRENT_LIST_DIR}/picocalc.pio) ++ ++set(SHAPONES_PATH ../../core) ++set(FATFS_PATH ../fatfs/source) ++ ++target_sources( ++ picocalc_nes PRIVATE ++ common.cpp ++ picocalc.cpp ++ i2ckbd.cpp ++ picocalc_nes.cpp ++ boot_menu.cpp ++ ${SHAPONES_PATH}/src/cpu.cpp ++ ${SHAPONES_PATH}/src/dma.cpp ++ ${SHAPONES_PATH}/src/input.cpp ++ ${SHAPONES_PATH}/src/interrupt.cpp ++ ${SHAPONES_PATH}/src/memory.cpp ++ ${SHAPONES_PATH}/src/ppu.cpp ++ ${SHAPONES_PATH}/src/apu.cpp ++ ${SHAPONES_PATH}/src/shapones.cpp ++ ${SHAPONES_PATH}/src/utils_std.cpp ++ ${FATFS_PATH}/ff.c ++ ${FATFS_PATH}/ffsystem.c ++ ${FATFS_PATH}/ffunicode.c ++ ${FATFS_PATH}/mmc_pico_spi.c ++ ../font/mono8x16/mono8x16.cpp ++ ../font/mono8x16/mono8x16_bmp.cpp ++) ++ ++target_include_directories( ++ picocalc_nes PRIVATE ++ . ++ ${SHAPONES_PATH}/include ++ ${FATFS_PATH} ++ ../font/mono8x16 ++) ++ ++target_link_libraries( ++ picocalc_nes PRIVATE ++ pico_stdlib ++ hardware_pio ++ hardware_pwm ++ hardware_dma ++ hardware_irq ++ hardware_spi ++ hardware_i2c ++ pico_multicore ++) ++ ++pico_add_extra_outputs(picocalc_nes) +diff --git a/samples/v3/Makefile.sample.mk b/samples/v3/Makefile.sample.mk +new file mode 100644 +index 0000000..61488a1 +--- /dev/null ++++ b/samples/v3/Makefile.sample.mk +@@ -0,0 +1,38 @@ ++.PHONY: all install clean ++ ++REPO_DIR=$(shell pwd) ++SRC_DIR=. ++BUILD_DIR=build ++ ++BIN_NAME=pico_nes_ws19804.uf2 ++BIN=$(BUILD_DIR)/$(BIN_NAME) ++ ++CORE_DIR=../../core ++FATFS_DIR=../fatfs/source ++ ++SRC_LIST=\ ++ $(wildcard $(SRC_DIR)/*.*) \ ++ $(wildcard $(SRC_DIR)/../font/mono8x16/*.*) \ ++ $(wildcard $(CORE_DIR)/src/*.*) \ ++ $(wildcard $(CORE_DIR)/include/shapones/*.*) \ ++ $(wildcard $(FATFS_DIR)/*.*) ++ ++all: $(BIN) ++ ++$(BIN): $(SRC_LIST) CMakeLists.txt ++ mkdir -p $(BUILD_DIR) ++ cd $(BUILD_DIR) \ ++ && cmake .. \ ++ && make -j ++ @echo "------------------------------" ++ @echo "UF2 File:" ++ @echo $(REPO_DIR)/$(BIN) ++ @ls -l $(REPO_DIR)/$(BIN) ++ ++install: $(BIN) ++ sudo mkdir -p /mnt/e ++ sudo mount -t drvfs e: /mnt/e ++ cp $(BIN) /mnt/e/. ++ ++clean: ++ rm -rf build +diff --git a/samples/v3/boot_menu.cpp b/samples/v3/boot_menu.cpp +new file mode 100644 +index 0000000..c34e5fe +--- /dev/null ++++ b/samples/v3/boot_menu.cpp +@@ -0,0 +1,174 @@ ++#include "boot_menu.hpp" ++ ++#include ++ ++#include "picocalc.hpp" ++#include "mono8x16.hpp" ++#include "common.hpp" ++ ++#include "ff.h" ++#include "diskio.h" ++ ++constexpr int MAX_FILES = 64; ++ ++// enumerate NES files ++static int enum_files(FATFS *fs, char **fname_list, int *fsize_list); ++ ++// show ROM select menu ++static int rom_select(int num_files, char **file_list); ++ ++// load NES file ++static bool load_nes(const char *fname, int size); ++ ++bool boot_menu() { ++ FATFS fs; ++ char *fname_list[MAX_FILES]; ++ int fsize_list[MAX_FILES]; ++ int num_files = enum_files(&fs, fname_list, fsize_list); ++ if (num_files <= 0) { ++ return false; ++ } ++ ++ int index = rom_select(num_files, fname_list); ++ ++ if ( ! load_nes(fname_list[index], fsize_list[index])) { ++ return false; ++ } ++ ++ for (int i = 0; i < num_files; i++) { ++ free(fname_list[i]); ++ } ++ ++ return true; ++} ++ ++static int enum_files(FATFS *fs, char **fname_list, int *fsize_list) { ++ char tmp[16]; ++ int y = 20; ++ constexpr int x_result = 120; ++ ++ DSTATUS dsts; ++ FRESULT fres; ++ ++ clear_frame_buff(); ++ ++ // Init FatFS ++ draw_string(0, y, "Init FatFS"); update_lcd(); ++ dsts = disk_initialize(0); ++ //dsts = STA_NOINIT; ++ if (dsts & STA_NOINIT) { ++ sprintf(tmp, "[NG] code=0x%x", (int)dsts); ++ draw_string(x_result, y, tmp); update_lcd(); ++ return -1; ++ } ++ draw_string(x_result, y, "[OK]"); update_lcd(); ++ y += 20; ++ ++ // Mount ++ draw_string(0, y, "Disk Mount"); update_lcd(); ++ fres = f_mount(fs, "", 0); ++ if (fres != FR_OK) { ++ sprintf(tmp, "[NG] code=0x%x", (int)fres); ++ draw_string(x_result, y, tmp); update_lcd(); ++ return -1; ++ } ++ draw_string(x_result, y, "[OK]"); update_lcd(); ++ y += 20; ++ ++ // Enumerate File ++ draw_string(0, y, "File List"); update_lcd(); ++ ++ DIR dobj; ++ FILINFO finfo; ++ fres = f_findfirst(&dobj, &finfo, "", "*.nes"); ++ int num_files = 0; ++ while (fres == FR_OK && finfo.fname[0] && strlen(finfo.fname) > 4 ) { ++ fname_list[num_files] = (char*)malloc(strlen(finfo.fname) + 1); ++ strcpy(fname_list[num_files], finfo.fname); ++ fsize_list[num_files] = finfo.fsize; ++ fres = f_findnext(&dobj, &finfo); ++ num_files++; ++ } ++ if (fres != FR_OK) { ++ sprintf(tmp, "[NG] code=0x%x", (int)fres); ++ draw_string(x_result, y, tmp); update_lcd(); ++ return -1; ++ } ++ f_closedir(&dobj); ++ draw_string(x_result, y, "[OK]"); update_lcd(); ++ y += 20; ++ ++ return num_files; ++} ++ ++static int rom_select(int num_files, char **file_list) { ++ int sel_index = 0; ++ int page_index = 0; ++ char buf[6]; ++ for(;;) { ++ clear_frame_buff(); ++ page_index = (sel_index / items_per_page) * items_per_page; ++ ++ for (int i = 0; i < items_per_page && (page_index + i) < num_files; i++) { ++ draw_string(20, i * 20, file_list[page_index + i]); ++ } ++ ++ sprintf(buf,"%02d",sel_index+1); ++ draw_string(0,FRAME_BUFF_HEIGHT-20,buf); ++ draw_string(0, (sel_index % items_per_page) * 20, "=>"); ++ ++ update_lcd(); ++ ++ switch (wait_key()) { ++ case nes::input::BTN_UP: ++ if (sel_index == 0) { ++ sel_index = num_files - 1; ++ } else { ++ sel_index--; ++ } ++ break; ++ case nes::input::BTN_DOWN: ++ if (sel_index == num_files - 1) { ++ sel_index = 0; ++ } else { ++ sel_index++; ++ } ++ break; ++ case nes::input::BTN_A: ++ case nes::input::BTN_START: ++ return sel_index; ++ } ++ } ++} ++ ++static bool load_nes(const char *fname, int size) { ++ FIL fil; ++ FRESULT fr; ++ ++ clear_frame_buff(); ++ draw_string(0, 0, "Loading..."); ++ update_lcd(); ++ ++ fr = f_open(&fil, fname, FA_READ); ++ if (fr) { ++ draw_string(0, 20, "File open failed."); ++ update_lcd(); ++ return false; ++ } ++ ++ UINT sz; ++ uint8_t *ines = (uint8_t*)malloc(size); ++ fr = f_read(&fil, ines, size, &sz); ++ if (fr) { ++ draw_string(0, 20, "File read failed."); ++ update_lcd(); ++ return false; ++ } ++ ++ f_close(&fil); ++ ++ nes::memory::map_ines(ines); ++ picocalc::set_spi_speed(SYS_CLK_FREQ / 4); ++ ++ return true; ++} +diff --git a/samples/v3/boot_menu.hpp b/samples/v3/boot_menu.hpp +new file mode 100644 +index 0000000..a0f9b70 +--- /dev/null ++++ b/samples/v3/boot_menu.hpp +@@ -0,0 +1,7 @@ ++#ifndef BOOT_MENU_HPP ++#define BOOT_MENU_HPP ++ ++// show boot menu ++bool boot_menu(); ++constexpr int items_per_page = 11;//(FRAME_BUFF_HEIGHT/20)-1 ++#endif +diff --git a/samples/v3/common.cpp b/samples/v3/common.cpp +new file mode 100644 +index 0000000..00654fd +--- /dev/null ++++ b/samples/v3/common.cpp +@@ -0,0 +1,173 @@ ++#include "common.hpp" ++#include "i2ckbd.hpp" ++ ++#include ++#include ++ ++static absolute_time_t now; ++uint8_t keycheck = 0; ++uint8_t keyread = 0; ++uint8_t frame_buff[FRAME_BUFF_STRIDE * nes::SCREEN_HEIGHT]; ++//0 == released, 1 = pressed ++int input_pins[] = { ++ PIN_PAD_A, PIN_PAD_B, PIN_PAD_SELECT, PIN_PAD_START, ++ PIN_PAD_UP, PIN_PAD_DOWN, PIN_PAD_LEFT, PIN_PAD_RIGHT ++}; ++ ++int wait_key() { ++ // wait any button pushed ++ int i = -1; ++ for (;;) { ++ sleep_ms(10); ++ for (i = 0; i < 8; i++) { ++ if (input_pins[i] == 1) { ++ input_pins[i] = 0; ++ return i; ++ } ++ } ++ } ++ return i; ++} ++ ++void draw_string(int x, int y, const char *str) { ++ mono8x16::draw_string_rgb565( ++ frame_buff, FRAME_BUFF_STRIDE, FRAME_BUFF_WIDTH, FRAME_BUFF_HEIGHT, ++ x, y, str, 0xffff); ++} ++ ++void clear_frame_buff() { ++ for (int i = 0; i < FRAME_BUFF_STRIDE * FRAME_BUFF_HEIGHT; i++) { ++ frame_buff[i] = 0; ++ } ++} ++ ++void update_lcd() { ++ picocalc::start_write_data((picocalc::WIDTH - FRAME_BUFF_WIDTH) / 2, (picocalc::HEIGHT - FRAME_BUFF_HEIGHT) / 2, ++ FRAME_BUFF_WIDTH, FRAME_BUFF_HEIGHT, frame_buff); ++ picocalc::finish_write_data(); ++} ++ ++void setBacklight(int level){//STM32: i2c reg is REG_ID_BKL(0x05) ++ //level is 0-100% ++ level*=255; ++ level/=100; ++ i2ckbd::I2C_Send_RegData(i2ckbd::I2C_KBD_ADDR,0x05,(uint8_t)level); ++} ++ ++//keyboard key status to input_pins map ++void set_kdb_key(uint8_t pin_offset, uint8_t key_status) { ++ ++ if (key_status == 1) { ++ input_pins[pin_offset] = 1; ++ } else if (key_status == 3) { ++ input_pins[pin_offset] = 0; ++ } ++} ++ ++void kbd_interrupt() { ++ int kbd_ret; ++ int c; ++ static int ctrlheld = 0; ++ uint8_t key_stat = 0;//press,release, or hold ++ if (keycheck == 0) { ++ if (keyread == 0) { ++ kbd_ret = i2ckbd::write_i2c_kbd(); ++ keyread = 1; ++ } else { ++ kbd_ret = i2ckbd::read_i2c_kbd(); ++ keyread = 0; ++ } ++ keycheck=KEYCHECKTIME; ++ } ++ if (kbd_ret < 0) { ++ if (i2ckbd::check_if_failed() > 0) { ++ printf("try to reset i2c\n"); ++ i2ckbd::reset_failed(); ++ i2ckbd::init_i2c_kbd();//re-init ++ } ++ } ++ ++ if (kbd_ret) { ++ if (kbd_ret == 0xA503)ctrlheld = 0; ++ else if (kbd_ret == 0xA502) { ++ ctrlheld = 1; ++ } else if ((kbd_ret & 0xff) == 1) {//pressed ++ key_stat = 1; ++ } else if ((kbd_ret & 0xff) == 3) { ++ key_stat = 3; ++ } ++ ++ c = kbd_ret >> 8; ++ int realc = -1; ++ switch (c) { ++ case 0xA1: ++ case 0xA2: ++ case 0xA3: ++ case 0xA4: ++ case 0xA5: ++ realc = -1;//skip shift alt ctrl keys ++ break; ++ default: ++ realc = c; ++ break; ++ } ++ ++ c = realc; ++ if (c >= 'a' && c <= 'z' && ctrlheld)c = c - 'a' + 1; ++ ++ switch (c) { ++ case 0xb5://UP ++ set_kdb_key(4, key_stat); ++ break; ++ case 0xb6://DOWN ++ set_kdb_key(5, key_stat); ++ break; ++ case 0xb4://LEFT ++ set_kdb_key(6, key_stat); ++ break; ++ case 0xb7://RIGHT ++ set_kdb_key(7, key_stat); ++ break; ++ case '-':// select ++ set_kdb_key(2, key_stat); ++ break; ++ case '=':// start ++ set_kdb_key(3, key_stat); ++ break; ++ case '['://B ++ set_kdb_key(1, key_stat); ++ break; ++ case ']'://A ++ set_kdb_key(0, key_stat); ++ break; ++ default: ++ break; ++ } ++ } ++} ++ ++static void __attribute__ ((optimize("-Os"))) __not_in_flash_func(timer_tick_cb)(unsigned alarm) { ++ ++ absolute_time_t next; ++ update_us_since_boot(&next, to_us_since_boot(now) + ( TICKSPERSEC)); ++ if (hardware_alarm_set_target(0, next)) { ++ update_us_since_boot(&next, time_us_64() + ( TICKSPERSEC)); ++ hardware_alarm_set_target(0, next); ++ } ++ ++ kbd_interrupt(); ++ if(keycheck){ ++ keycheck--; ++ } ++ ++} ++ ++void device_init() { ++ ++ hardware_alarm_claim(0); ++ update_us_since_boot(&now, time_us_64()); ++ hardware_alarm_set_callback(0, timer_tick_cb); ++ hardware_alarm_force_irq(0); ++ ++ //setBacklight(80); ++} +\ No newline at end of file +diff --git a/samples/v3/common.hpp b/samples/v3/common.hpp +new file mode 100644 +index 0000000..9ae098b +--- /dev/null ++++ b/samples/v3/common.hpp +@@ -0,0 +1,51 @@ ++#ifndef COMMON_HPP ++#define COMMON_HPP ++ ++#include "stdint.h" ++#include "pico/stdlib.h" ++#include "hardware/clocks.h" ++#include "hardware/gpio.h" ++#include "shapones/shapones.hpp" ++#include "picocalc.hpp" ++#include "mono8x16.hpp" ++ ++// system clock frequency ++static constexpr uint32_t SYS_CLK_FREQ = 250 * MHZ; ++ ++// frame buffer for DMA (RGB565) ++constexpr int FRAME_BUFF_WIDTH = 240; ++constexpr int FRAME_BUFF_STRIDE = FRAME_BUFF_WIDTH * 2; ++constexpr int FRAME_BUFF_HEIGHT = nes::SCREEN_HEIGHT; ++extern uint8_t frame_buff[FRAME_BUFF_STRIDE * nes::SCREEN_HEIGHT]; ++ ++// pad pins ++static constexpr int PIN_PAD_A = 0; ++static constexpr int PIN_PAD_B = 0; ++static constexpr int PIN_PAD_START = 0; ++static constexpr int PIN_PAD_SELECT = 0; ++static constexpr int PIN_PAD_RIGHT = 0; ++static constexpr int PIN_PAD_DOWN = 0; ++static constexpr int PIN_PAD_LEFT = 0; ++static constexpr int PIN_PAD_UP = 0; ++extern int input_pins[]; ++ ++static constexpr uint TICKSPERSEC = 1000; /* Ticks per second */ ++static constexpr uint8_t KEYCHECKTIME=16; ++ ++// wait until some key is pressed ++int wait_key(); ++ ++// clear frame buffer with black color ++void clear_frame_buff(); ++ ++// draw string to frame buffer ++void draw_string(int x, int y, const char *str); ++ ++// transfer image from frame buffer to LCD ++void update_lcd(); ++ ++void device_init(); ++ ++void setBacklight(int); ++ ++#endif +diff --git a/samples/v3/i2ckbd.cpp b/samples/v3/i2ckbd.cpp +new file mode 100644 +index 0000000..15a3111 +--- /dev/null ++++ b/samples/v3/i2ckbd.cpp +@@ -0,0 +1,79 @@ ++#include ++#include ++#include "i2ckbd.hpp" ++ ++namespace i2ckbd{ ++ ++ static uint8_t i2c_inited = 0; ++ uint16_t i2c_failed = 0; ++ ++ void init_i2c_kbd(){ ++ gpio_set_function(I2C_KBD_SCL, GPIO_FUNC_I2C); ++ gpio_set_function(I2C_KBD_SDA, GPIO_FUNC_I2C); ++ i2c_init(I2C_KBD_MOD, I2C_KBD_SPEED); ++ gpio_pull_up(I2C_KBD_SCL); ++ gpio_pull_up(I2C_KBD_SDA); ++ ++ i2c_inited = 1; ++ } ++ ++ int write_i2c_kbd(){ ++ int retval; ++ unsigned char msg[2]; ++ msg[0] = 0x09; ++ ++ if(i2c_inited == 0) return -1; ++ ++ retval = i2c_write_timeout_us(I2C_KBD_MOD, I2C_KBD_ADDR, msg, 1, false, 500000); ++ if ( retval == PICO_ERROR_GENERIC || retval == PICO_ERROR_TIMEOUT) { ++ printf( "i2c write error\n"); ++ i2c_failed+=1; ++ return -1; ++ } ++ return 0; ++ } ++ ++ int read_i2c_kbd(){ ++ int retval; ++ //static int ctrlheld=0; ++ uint16_t buff = 0; ++ unsigned char msg[2]; ++ int c = -1; ++ msg[0] = 0x09; ++ ++ if(i2c_inited == 0) return -1; ++ ++ retval = i2c_read_timeout_us(I2C_KBD_MOD, I2C_KBD_ADDR, (unsigned char*)&buff, 2, false, 500000); ++ if ( retval == PICO_ERROR_GENERIC || retval == PICO_ERROR_TIMEOUT) { ++ printf("i2c read error read\n"); ++ i2c_failed+=1; ++ return -1; ++ } ++ ++ return buff; ++ } ++ ++ int I2C_Send_RegData(int i2caddr,int reg,char command){ ++ int retval; ++ unsigned char I2C_Send_Buffer[2]; ++ I2C_Send_Buffer[0]=reg; ++ I2C_Send_Buffer[1]=command; ++ uint8_t I2C_Sendlen=2; ++ uint16_t I2C_Timeout=1000; ++ ++ retval=i2c_write_timeout_us(I2C_KBD_MOD, (uint8_t)i2caddr, (uint8_t *)I2C_Send_Buffer, I2C_Sendlen,false, I2C_Timeout*1000); ++ ++ if ( retval == PICO_ERROR_GENERIC || retval == PICO_ERROR_TIMEOUT) { ++ printf( "I2C_Send_RegData write error\n"); ++ return -1; ++ } ++ return 0; ++ } ++ int check_if_failed(){ ++ return i2c_failed; ++ } ++ void reset_failed(){ ++ i2c_deinit(I2C_KBD_MOD); ++ i2c_failed = 0; ++ } ++} +\ No newline at end of file +diff --git a/samples/v3/i2ckbd.hpp b/samples/v3/i2ckbd.hpp +new file mode 100644 +index 0000000..01ba106 +--- /dev/null ++++ b/samples/v3/i2ckbd.hpp +@@ -0,0 +1,26 @@ ++#ifndef I2C_KEYBOARD_HPP ++#define I2C_KEYBOARD_HPP ++ ++#include ++#include ++#include ++#include ++ ++#define I2C_KBD_MOD i2c1 ++ ++namespace i2ckbd { ++ ++ static constexpr uint I2C_KBD_SDA = 6; ++ static constexpr uint I2C_KBD_SCL = 7; ++ static constexpr uint I2C_KBD_SPEED = 400000; ++ static constexpr uint I2C_KBD_ADDR = 0x1F; ++ ++ ++ void init_i2c_kbd(); ++ int write_i2c_kbd(); ++ int read_i2c_kbd(); ++ int check_if_failed(); ++ void reset_failed(); ++ int I2C_Send_RegData(int i2caddr, int reg, char command); ++} ++#endif +diff --git a/samples/v3/picocalc.cpp b/samples/v3/picocalc.cpp +new file mode 100644 +index 0000000..91cd5de +--- /dev/null ++++ b/samples/v3/picocalc.cpp +@@ -0,0 +1,436 @@ ++ ++#include "picocalc.hpp" ++#include "picocalc_c.h" ++#include "picocalc.pio.h" ++ ++namespace picocalc { ++ ++static int sys_clock_hz; ++static direction_t curr_dir = EMPTY; ++static int curr_speed = 10 * MHZ; ++ ++static PIO spi_pio; ++static uint spi_sm; ++static uint spi_offset; ++static uint dma_tx; ++ ++// SPI push byte ++static void pio_push(uint8_t data); ++ ++// SPI pop byte ++static uint8_t pio_pop(); ++ ++// wait for SPI to idle ++static void pio_wait_idle(); ++ ++// setup SPI direction and speed ++static void setup_pio(direction_t new_dir, int new_speed); ++ ++// set SPI direction ++static void set_spi_direction(direction_t new_dir); ++ ++void init(int sys_clk_hz) { ++ sys_clock_hz = sys_clk_hz; ++ ++ spi_pio = pio0; ++ spi_sm = 0; ++ ++ spi_offset = pio_add_program(spi_pio, &picocalc_program ); ++ pio_gpio_init(spi_pio, PIN_MISO); ++ pio_gpio_init(spi_pio, PIN_MOSI); ++ pio_gpio_init(spi_pio, PIN_SCK); ++ pio_sm_set_consecutive_pindirs(spi_pio, spi_sm, PIN_MISO, 1, false); ++ pio_sm_set_consecutive_pindirs(spi_pio, spi_sm, PIN_MOSI, 1, true); ++ pio_sm_set_consecutive_pindirs(spi_pio, spi_sm, PIN_SCK, 1, true); ++ ++ dma_tx = dma_claim_unused_channel(true); ++ ++ gpio_init(PIN_RST); ++ gpio_init(PIN_DC); ++ gpio_init(PIN_LCD_CS); ++ gpio_init(PIN_RAM_CS); ++ gpio_set_dir(PIN_RST, GPIO_OUT); ++ gpio_set_dir(PIN_DC, GPIO_OUT); ++ gpio_set_dir(PIN_RAM_CS,GPIO_OUT); ++ gpio_set_dir(PIN_LCD_CS, GPIO_OUT); ++ ++ gpio_put(PIN_RST, 1); ++ gpio_put(PIN_LCD_CS, 1); ++ gpio_put(PIN_RAM_CS,1); ++ ++ // hardware reset ++ gpio_put(PIN_RST, 1); ++ sleep_ms(1); ++ gpio_put(PIN_RST, 0); ++ sleep_ms(10); ++ gpio_put(PIN_RST, 1); ++ sleep_ms(10); ++ ++ { ++ uint8_t data[] = {0xc3}; ++ write_command(0xF0,data,sizeof(data)); ++ } ++ { ++ uint8_t data[] = {0x96}; ++ write_command(0xF0,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0x48}; ++ write_command(0x36,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0x65}; ++ write_command(0x3A,data,sizeof(data)); ++ } ++ { ++ // Frame Rate Control ++ uint8_t data[] = {0xA0}; ++ write_command(0xB1,data,sizeof(data)); ++ } ++ { ++ uint8_t data[] = {0x00}; ++ write_command(0xB4,data,sizeof(data)); ++ } ++ { ++ uint8_t data[] = {0xc6}; ++ write_command(0xB7,data,sizeof(data)); ++ } ++ { ++ uint8_t data[] = {0x02,0xE0}; ++ write_command(0xB9,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0x80,0x06}; ++ write_command(0xC0,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0x15}; ++ write_command(0xC1,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0xA7}; ++ write_command(0xC2,data,sizeof(data)); ++ } ++ { ++ uint8_t data[] = {0x04}; ++ write_command(0xC5,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0x40,0x8A,0x00,0x00,0x29,0x19,0xAA,0x33}; ++ write_command(0xE8,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0xF0,0x06,0x0F,0x05,0x04,0x20,0x37,0x33,0x4C,0x37,0x13,0x14,0x2B,0x31}; ++ write_command(0xE0,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0xF0,0x11,0x1B,0x11,0x0F,0x0A,0x37,0x43,0x4C,0x37,0x13,0x13,0x2C,0x32}; ++ write_command(0xE1,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0x3C}; ++ write_command(0xF0,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0x69}; ++ write_command(0xF0,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0x00}; ++ write_command(0x35,data,sizeof(data)); ++ } ++ write_command(0x11);//TFT_SLPOUT ++ sleep_ms(120); ++ //TFT_INVON ++ write_command(0x21); ++ ++ clear(0); ++ ++ write_command(0x29);//TFT_DISPON ++ sleep_ms(120); ++ ++ { ++ uint8_t data[] = {0x00,0x00,0x01,0x3F}; ++ write_command(0x2A,data,sizeof(data)); ++ } ++ ++ { ++ uint8_t data[] = {0x00,0x00,0x01,0x3F}; ++ write_command(0x2B,data,sizeof(data)); ++ } ++ write_command(0x2C); ++ ++/* ++ { ++ //// Positive Gamma Control ++ uint8_t data[] = {0x00,0x03,0x09,0x08,0x16,0x0a,0x3f,0x78,0x4c,0x09,0x0a,0x08,0x16,0x1a,0x0f}; ++ write_command(0xE0,data,sizeof(data)); ++ } ++ { ++ //// Negative Gamma Control ++ uint8_t data[] = {0x00,0x16,0x19,0x03,0x0f,0x05,0x32,0x45,0x46,0x04,0x0e,0x0d,0x35,0x37,0x0f}; ++ write_command(0xE1,data,sizeof(data)); ++ } ++ { ++ // Power Control 1 ++ uint8_t data[] = {0x17,0x15}; ++ write_command(0xC0,data,sizeof(data)); ++ } ++ { ++ // Power Control 2 ++ uint8_t data[] = {0x41}; ++ write_command(0xC1,data,sizeof(data)); ++ } ++ { ++ //// VCOM Control ++ uint8_t data[] = {0x00,0x12,0x80}; ++ write_command(0xC5,data,sizeof(data)); ++ } ++ { ++ // Memory Access Control ++ //MX,MV, RGB mode ++ uint8_t data[] = {0x48}; // (0x40 | 0x20) or 0x48 ++ write_command(0x36,data,sizeof(data)); ++ } ++ ++ { ++ // Pixel Interface Format ++ uint8_t data[] = {0x65}; //0x65=16 bit colour for SPI,0x66=18bits ++ write_command(0x3A,data,sizeof(data)); ++ } ++ ++ { ++ // Interface Mode Control ++ uint8_t data[] = {0x00}; ++ write_command(0xB0,data,sizeof(data)); ++ } ++ { ++ // Frame Rate Control ++ uint8_t data[] = {0xA0}; ++ write_command(0xB1,data,sizeof(data)); ++ } ++ ++ { ++ // Display Inversion Control ++ uint8_t data[] = {0x02}; ++ write_command(0xB4,data,sizeof(data)); ++ } ++ ++ { ++ // Display Function Control ++ uint8_t data[] = {0x02,0x02,0x3B}; ++ write_command(0xB6,data,sizeof(data)); ++ } ++ ++ { ++ // Entry Mode Set ++ uint8_t data[] = {0xC6,0xE9,0x00}; ++ write_command(0xB7,data,sizeof(data)); ++ } ++ ++ { ++ // Adjust Control 3 ++ uint8_t data[] = {0xA9,0x51,0x2C,0x82}; ++ write_command(0xF7,data,sizeof(data)); ++ } ++ ++ write_command(0x11);//TFT_SLPOUT ++ sleep_ms(120); ++ ++ //TFT_INVON ++ write_command(0x21); ++ ++ clear(0); ++ ++ write_command(0x29);//TFT_DISPON ++ sleep_ms(120); ++*/ ++} ++ ++void setup_pio(direction_t new_dir, int new_speed) { ++ if (new_dir == curr_dir && new_speed == curr_speed) return; ++ ++ float div = sys_clock_hz / 2.f / new_speed; ++ ++ pio_sm_set_enabled(spi_pio, spi_sm, false); ++ ++ // load new PIO ++ if (new_dir == TX) { ++ pio_sm_config pio_cfg = picocalc_program_get_default_config(spi_offset); ++ sm_config_set_out_pins(&pio_cfg, PIN_MOSI, 1); ++ sm_config_set_sideset_pins(&pio_cfg, PIN_SCK); ++ sm_config_set_fifo_join(&pio_cfg, PIO_FIFO_JOIN_TX); ++ sm_config_set_out_shift(&pio_cfg, false, true, 8); ++ sm_config_set_clkdiv(&pio_cfg, div); ++ pio_sm_init(spi_pio, spi_sm, spi_offset, &pio_cfg); ++ } ++ else if (new_dir == RX) { ++ pio_sm_config pio_cfg = picocalc_program_get_default_config(spi_offset); ++ sm_config_set_out_pins(&pio_cfg, PIN_MOSI, 1); ++ sm_config_set_in_pins(&pio_cfg, PIN_MISO); ++ sm_config_set_sideset_pins(&pio_cfg, PIN_SCK); ++ sm_config_set_out_shift(&pio_cfg, false, true, 8); ++ sm_config_set_in_shift(&pio_cfg, false, true, 8); ++ sm_config_set_clkdiv(&pio_cfg, div); ++ pio_sm_init(spi_pio, spi_sm, spi_offset, &pio_cfg); ++ } ++ ++ hw_set_bits(&spi_pio->input_sync_bypass, 1u << PIN_MISO); ++ pio_sm_set_enabled(spi_pio, spi_sm, true); ++ ++ curr_dir = new_dir; ++ curr_speed = new_speed; ++} ++ ++void set_spi_direction(direction_t new_dir) { ++ setup_pio(new_dir, curr_speed); ++} ++ ++void set_spi_speed(int new_speed) { ++ setup_pio(curr_dir, new_speed); ++} ++ ++void clear(uint16_t color) { ++ uint8_t data[WIDTH * 2]; ++ for (int x = 0; x < WIDTH * 2; x++) { ++ data[x] = color; ++ } ++ for (int y = 0; y < HEIGHT; y++) { ++ start_write_data(0, y, WIDTH, 1, data); ++ finish_write_data(); ++ } ++} ++ ++void start_write_data(int x0, int y0, int w, int h, uint8_t *data) { ++ int x1 = x0 + w - 1; ++ int y1 = y0 + h - 1; ++ { ++ uint8_t xcoord[] = { ++ (uint8_t)(x0 >> 8), ++ (uint8_t)(x0 & 0xff), ++ (uint8_t)(x1 >> 8), ++ (uint8_t)(x1 & 0xff) ++ }; ++ write_command(0x2a, xcoord, sizeof(xcoord)); ++ } ++ { ++ uint8_t ycoord[] = { ++ (uint8_t)(y0 >> 8), ++ (uint8_t)(y0 & 0xff), ++ (uint8_t)(y1 >> 8), ++ (uint8_t)(y1 & 0xff) ++ }; ++ write_command(0x2b, ycoord, sizeof(ycoord)); ++ } ++ //write_command(0x2c, data, w * h * 3 / 2); ++ ++ { ++ gpio_put(PIN_DC, 0); ++ gpio_put(PIN_LCD_CS, 0); ++ uint8_t cmd = 0x2c; ++ write_blocking(&cmd, 1); ++ gpio_put(PIN_DC, 1); ++ ++ { ++ dma_channel_config dma_cfg = dma_channel_get_default_config(dma_tx); ++ channel_config_set_transfer_data_size(&dma_cfg, DMA_SIZE_8); ++ channel_config_set_dreq(&dma_cfg, pio_get_dreq(spi_pio, spi_sm, true)); ++ dma_channel_configure(dma_tx, &dma_cfg, ++ &spi_pio->txf[spi_sm], // write address ++ data, // read address ++ w * h * 2, // element count (each element is of size transfer_data_size) ++ false); // don't start yet ++ } ++ ++ dma_start_channel_mask(1u << dma_tx); ++ } ++ ++} ++ ++void finish_write_data() { ++ dma_channel_wait_for_finish_blocking(dma_tx); ++ pio_wait_idle(); ++ gpio_put(PIN_LCD_CS, 1); ++} ++ ++void write_command(uint8_t cmd, const uint8_t *data, int len) { ++ set_spi_direction(TX); ++ gpio_put(PIN_DC, 0); // command mode ++ gpio_put(PIN_LCD_CS, 0); ++ write_blocking(&cmd, 1); ++ if (data) { ++ gpio_put(PIN_DC, 1); // data mode ++ write_blocking(data, len); ++ } ++ gpio_put(PIN_LCD_CS, 1); ++} ++ ++void write_command(uint8_t cmd) { ++ set_spi_direction(TX); ++ write_command(cmd, nullptr, 0); ++} ++ ++void write_blocking(const uint8_t *data, int len) { ++ set_spi_direction(TX); ++ for(int i = 0; i < len; i++) { ++ pio_push(data[i]); ++ } ++ pio_wait_idle(); ++} ++ ++void read_blocking(uint8_t tx_repeat, uint8_t *buff, int len) { ++ set_spi_direction(RX); ++ int tx_remain = len, rx_remain = len; ++ io_rw_8 *txfifo = (io_rw_8 *) &spi_pio->txf[spi_sm]; ++ io_rw_8 *rxfifo = (io_rw_8 *) &spi_pio->rxf[spi_sm]; ++ while (tx_remain || rx_remain) { ++ if (tx_remain && !pio_sm_is_tx_fifo_full(spi_pio, spi_sm)) { ++ *txfifo = tx_repeat; ++ --tx_remain; ++ } ++ if (rx_remain && !pio_sm_is_rx_fifo_empty(spi_pio, spi_sm)) { ++ *buff++ = *rxfifo; ++ --rx_remain; ++ } ++ } ++} ++ ++static void pio_push(uint8_t data) { ++ while (pio_sm_is_tx_fifo_full(spi_pio, spi_sm)) { } ++ *(volatile uint8_t*)&spi_pio->txf[spi_sm] = data; ++} ++ ++static uint8_t pio_pop() { ++ while (pio_sm_is_rx_fifo_empty(spi_pio, spi_sm)) { } ++ return *(volatile uint8_t*)&spi_pio->rxf[spi_sm]; ++} ++ ++static void pio_wait_idle() { ++ uint32_t stall_mask = 1u << (spi_sm + PIO_FDEBUG_TXSTALL_LSB); ++ spi_pio->fdebug = stall_mask; ++ while (!(spi_pio->fdebug & stall_mask)) { } ++} ++ ++extern "C" { ++ ++void ws19804_write_blocking(const uint8_t *buff, int len) { ++ picocalc::write_blocking(buff, len); ++} ++void ws19804_read_blocking(uint8_t tx_repeat, uint8_t *buff, int len) { ++ picocalc::read_blocking(tx_repeat, buff, len); ++} ++ ++} ++ ++} +diff --git a/samples/v3/picocalc.hpp b/samples/v3/picocalc.hpp +new file mode 100644 +index 0000000..6b82739 +--- /dev/null ++++ b/samples/v3/picocalc.hpp +@@ -0,0 +1,58 @@ ++#ifndef WS19804_HPP ++#define WS19804_HPP ++ ++#include "stdint.h" ++#include "pico/stdlib.h" ++#include "hardware/pio.h" ++#include "hardware/gpio.h" ++#include "hardware/clocks.h" ++#include "hardware/dma.h" ++ ++namespace picocalc { ++ ++static constexpr int WIDTH = 320; ++static constexpr int HEIGHT = 320;//LCD SCREEN HEIGHT,not logical Height ++static constexpr uint PIN_DC = 14; ++static constexpr uint PIN_LCD_CS = 13; ++//static constexpr uint PIN_TP_CS = 16; ++static constexpr uint PIN_RAM_CS = 21; ++static constexpr uint PIN_SCK = 10; ++static constexpr uint PIN_MOSI = 11; ++static constexpr uint PIN_MISO = 12; ++static constexpr uint PIN_RST = 15; ++//static constexpr uint PIN_BL = 13; ++ ++enum direction_t { ++ EMPTY, TX, RX ++}; ++ ++// setup GPIO, PIO, LCD ++void init(int sys_clk_hz); ++ ++// set SPI clock frequency ++void set_spi_speed(int new_speed); ++ ++// clear display ++void clear(uint16_t color); ++ ++// start DMA ++void start_write_data(int x0, int y0, int w, int h, uint8_t *data); ++ ++// wait DMA to finish ++void finish_write_data(); ++ ++// write LCD command ++void write_command(uint8_t cmd, const uint8_t *data, int len); ++ ++// write LCD command ++void write_command(uint8_t cmd); ++ ++// SPI write ++void write_blocking(const uint8_t *data, int len); ++ ++// SPI read ++void read_blocking(uint8_t tx_repeat, uint8_t *buff, int len); ++ ++} ++ ++#endif +diff --git a/samples/v3/picocalc.pio b/samples/v3/picocalc.pio +new file mode 100644 +index 0000000..2af8cc1 +--- /dev/null ++++ b/samples/v3/picocalc.pio +@@ -0,0 +1,7 @@ ++.program picocalc ++.side_set 1 ++ ++.wrap_target ++ out pins, 1 side 0 ++ in pins, 1 side 1 ++.wrap +diff --git a/samples/v3/picocalc_c.h b/samples/v3/picocalc_c.h +new file mode 100644 +index 0000000..8599482 +--- /dev/null ++++ b/samples/v3/picocalc_c.h +@@ -0,0 +1,14 @@ ++#ifndef WS19804_C_H ++#define WS19804_C_H ++ ++#include "stdint.h" ++ ++#define Pico_SD_SPI_MOD spi0 ++ ++// SPI write function for FatFS ++void ws19804_write_blocking(const uint8_t *data, int len); ++ ++// SPI read function for FatFS ++void ws19804_read_blocking(uint8_t tx_repeat, uint8_t *buff, int len); ++ ++#endif +diff --git a/samples/v3/picocalc_nes.cpp b/samples/v3/picocalc_nes.cpp +new file mode 100644 +index 0000000..fbc1069 +--- /dev/null ++++ b/samples/v3/picocalc_nes.cpp +@@ -0,0 +1,251 @@ ++#include "hardware/gpio.h" ++#include "hardware/clocks.h" ++#include "hardware/vreg.h" ++#include "hardware/watchdog.h" ++#include "pico/stdlib.h" ++#include "pico/multicore.h" ++#include "pico/util/queue.h" ++#include "pico/time.h" ++ ++#include "i2ckbd.hpp" ++#include "picocalc.hpp" ++ ++#include "pwm_audio.hpp" ++ ++#include "shapones/shapones.hpp" ++ ++#include "common.hpp" ++#include "boot_menu.hpp" ++ ++// monitor pin for debugging ++static constexpr int PIN_MONITOR = 1; ++ ++// speaker PWM out ++static constexpr int PIN_SPEAKER = 26; ++ ++// APU configuration ++static constexpr int SPK_LATENCY = 256; ++static constexpr int SPK_PWM_FREQ = 22050; ++ ++/* ++// NES color table ,rgb444 ++static const uint16_t COLOR_TABLE[] = { ++ 0x555, 0x027, 0x019, 0x308, 0x406, 0x603, 0x500, 0x410, ++ 0x230, 0x040, 0x040, 0x040, 0x034, 0x000, 0x000, 0x000, ++ 0x999, 0x05C, 0x33F, 0x62E, 0x81B, 0xA16, 0x922, 0x740, ++ 0x560, 0x270, 0x080, 0x072, 0x067, 0x000, 0x000, 0x000, ++ 0xFFF, 0x5AF, 0x78F, 0xB6F, 0xE5F, 0xF5B, 0xF76, 0xD82, ++ 0xAB0, 0x7C0, 0x5D2, 0x3D7, 0x3BD, 0x444, 0x000, 0x000, ++ 0xFFF, 0xADF, 0xCCF, 0xDBF, 0xFBF, 0xFBD, 0xFBB, 0xEC9, ++ 0xDD7, 0xBE7, 0xAE9, 0x9EB, 0xADE, 0xAAA, 0x000, 0x000, ++}; ++*/ ++ ++// NES color table ,rgb565 ++static const uint16_t COLOR_TABLE[] = { ++0x52aa, 0x010e, 0x0093, 0x3011, 0x400c, 0x6006, 0x6000, 0x4080, ++0x2180, 0x0220, 0x0220, 0x0220, 0x0188, 0x0000, 0x0000, 0x0000, ++0x9cd3, 0x02b9, 0x319f, 0x611d, 0x8897, 0xa88c, 0xa904, 0x7220, ++0x5320, 0x23a0, 0x0440, 0x03a4, 0x032e, 0x0000, 0x0000, 0x0000, ++0xffff, 0x555f, 0x745f, 0xbb3f, 0xeabf, 0xfab7, 0xfbac, 0xdc44, ++0xadc0, 0x7660, 0x56e4, 0x36ee, 0x35db, 0x4228, 0x0000, 0x0000, ++0xffff, 0xaeff, 0xce7f, 0xdddf, 0xfddf, 0xfddb, 0xfdd7, 0xee73, ++0xdeee, 0xbf6e, 0xaf73, 0x9f77, 0xaefd, 0xad55, 0x0000, 0x0000 ++}; ++ ++/* ++static const uint16_t COLOR_TABLE[] = { ++ 0x52AA, 0x0117, 0x00D2, 0x1861, 0x20C2, 0x30A0, 0x2840, 0x2108, ++ 0x10C2, 0x0020, 0x0020, 0x0020, 0x018A, 0x0000, 0x0000, 0x0000, ++ 0x9CD3, 0x02BF, 0x1CFF, 0x4E3F, 0x733F, 0xA89F, 0x9105, 0x82E0, ++ 0x5B00, 0x2120, 0x0010, 0x0019, 0x0053, 0x0000, 0x0000, 0x0000, ++ 0xFFFF, 0x52FF, 0x7BFF, 0xB7FF, 0xE7FF, 0xF79E, 0xFFDC, 0xD740, ++ 0xAC00, 0x8300, 0x5A40, 0x3AA0, 0x3B7C, 0x5294, 0x0000, 0x0000, ++ 0xFFFF, 0xBFFF, 0xDDFF, 0xEFFC, 0xFFFF, 0xFFFB, 0xFFEA, 0xD749, ++ 0xCE79, 0xC6B8, 0xAD8C, 0x94EF, 0xC678, 0xAD55, 0x0000, 0x0000, ++}; ++ */ ++// line buffer FIFO between core1 --> core0 ++// <------------- STRIDE ------------> ++// +---------+-----------------------+ A ++// | y coord | color numbers (256px) | | ++// | 1 Byte | 256 Byte | | ++// +---------+-----------------------+ DEPTH ++// | : | : | | ++// | : | : | | ++// +---------+-----------------------+ V ++static constexpr int LINE_FIFO_DEPTH = 8; ++static constexpr int LINE_FIFO_STRIDE = nes::SCREEN_WIDTH + 1; ++static uint8_t line_buff[LINE_FIFO_DEPTH * LINE_FIFO_STRIDE]; ++static volatile int line_fifo_wptr = 0; ++static volatile int line_fifo_rptr = 0; ++ ++// sound buffer for DMA ++static uint8_t spk_buff[SPK_LATENCY]; ++ ++// start game ++static void boot_nes(); ++ ++// core0 main loop ++static void cpu_loop(); ++ ++// core1 main loop ++static void ppu_loop(); ++ ++// APU DMA finish IRQ handler ++static void apu_dma_handler(); ++ ++// let APU fill the sound buffer ++static void apu_fill_buffer(PwmAudio::sample_t *buff); ++ ++// PWM audio driver ++PwmAudio speaker(PIN_SPEAKER, SPK_LATENCY, 8, (float)SYS_CLK_FREQ / SPK_PWM_FREQ, apu_dma_handler); ++ ++int main() { ++ // setup clocks ++ vreg_set_voltage(VREG_VOLTAGE_1_30); ++ sleep_ms(100); ++ stdio_init_all(); ++ set_sys_clock_khz(SYS_CLK_FREQ / 1000, true); ++ setup_default_uart(); ++ ++ // setup monitor pin ++ gpio_init(PIN_MONITOR); ++ gpio_set_dir(PIN_MONITOR, GPIO_OUT); ++ gpio_put(PIN_MONITOR, 0); ++ ++ i2ckbd::init_i2c_kbd(); ++ device_init(); ++ ++ picocalc::init(SYS_CLK_FREQ); ++ ++ // show boot menu ++ if ( ! boot_menu()) { ++ for(;;) sleep_ms(100); ++ } ++ ++ // boot game ++ boot_nes(); ++ ++ return 0; ++} ++ ++static void boot_nes() { ++ // set APU sampling rate ++ nes::apu::set_sampling_rate(SPK_PWM_FREQ); ++ ++ // reset ++ nes::reset(); ++ ++ // start APU loop ++ apu_fill_buffer(speaker.get_buffer(0)); ++ apu_fill_buffer(speaker.get_buffer(1)); ++ speaker.play(); ++ ++ // start PPU loop ++ multicore_launch_core1(ppu_loop); ++ ++ // start CPU loop ++ cpu_loop(); ++} ++ ++ ++static void cpu_loop() { ++ auto t_last_frame = get_absolute_time(); ++ int frame_count = 0; ++ char fps_str[16]; ++ for(;;) { ++ // run CPU ++ nes::cpu::service(); ++ ++ // update input status ++ nes::input::InputStatus input_status; ++ input_status.raw = 0; ++ for(int i = 0; i < 8; i++) { ++ if (input_pins[i] == 1) { ++ input_status.raw |= (1 << i); ++ } ++ } ++ if( (input_status.raw & 0x0C) == 0x0C) { ++ watchdog_enable(1, 1); ++ watchdog_reboot(0, 0, 0); ++ } ++ nes::input::set_raw(0, input_status); ++ ++ // check line buffer FIFO state ++ int fifo_rptr = line_fifo_rptr; ++ if (line_fifo_wptr != fifo_rptr) { ++ // convert color number to RGB565 ++ int y = line_buff[fifo_rptr * LINE_FIFO_STRIDE]; ++ int x0 = (nes::SCREEN_WIDTH - FRAME_BUFF_WIDTH) / 2; ++ uint8_t *rd_ptr = line_buff + (fifo_rptr * LINE_FIFO_STRIDE + x0 + 1); ++ uint8_t *wr_ptr = frame_buff + (y * FRAME_BUFF_STRIDE); ++ for (int x = 0; x < FRAME_BUFF_WIDTH; x++) { ++ ++ uint16_t c0 = COLOR_TABLE[*(rd_ptr++) & 0x3f]; ++ *(wr_ptr++) = (c0 >> 8) & 0xff; ++ *(wr_ptr++) = c0 & 0xff; ++ ++ } ++ line_fifo_rptr = (fifo_rptr + 1) % LINE_FIFO_DEPTH; ++ ++ if (y == nes::SCREEN_HEIGHT - 1) { ++ // fps measurement ++ auto t_now = get_absolute_time(); ++ if (frame_count < 60-1) { ++ frame_count++; ++ } ++ else { ++ auto t_diff = absolute_time_diff_us(t_last_frame, t_now); ++ float fps = (60.0f * 1000000) / t_diff; ++ sprintf(fps_str, "%5.2ffps", fps); ++ t_last_frame = t_now; ++ frame_count = 0; ++ } ++ ++ // DMA transfer ++ picocalc::finish_write_data(); ++ //draw_string(0, 0, fps_str); ++ picocalc::start_write_data((picocalc::WIDTH - FRAME_BUFF_WIDTH) / 2, (picocalc::HEIGHT - FRAME_BUFF_HEIGHT) / 2, FRAME_BUFF_WIDTH, FRAME_BUFF_HEIGHT, frame_buff); ++ static int tmp = 0; ++ //gpio_put(PIN_MONITOR, tmp); ++ tmp ^= 1; ++ } ++ } ++ } ++} ++ ++ ++static void ppu_loop() { ++ constexpr int FRAME_DELAY_US = 16666; ++ absolute_time_t next_time = delayed_by_us(get_absolute_time(), FRAME_DELAY_US); ++ ++ for(;;) { ++ int wptr = line_fifo_wptr; ++ bool eol = nes::ppu::service(&line_buff[wptr * LINE_FIFO_STRIDE + 1]); ++ int y = nes::ppu::current_focus_y(); ++ if (eol && y < nes::SCREEN_HEIGHT) { ++ // Vsync ++ if (y == 0) { ++ busy_wait_until(next_time); ++ next_time = delayed_by_us(get_absolute_time(), FRAME_DELAY_US); ++ } ++ ++ // push new line ++ line_buff[wptr * LINE_FIFO_STRIDE] = y; ++ line_fifo_wptr = (wptr + 1) % LINE_FIFO_DEPTH; ++ } ++ } ++} ++ ++static void apu_dma_handler() { ++ speaker.flip_buffer(); ++ apu_fill_buffer(speaker.get_next_buffer()); ++} ++ ++static void apu_fill_buffer(PwmAudio::sample_t *buff) { ++ nes::apu::service(spk_buff, speaker.LATENCY); ++ for (int i = 0; i < speaker.LATENCY; i++) { ++ buff[i] = spk_buff[i]; ++ } ++} +\ No newline at end of file +diff --git a/samples/v3/pwm_audio.hpp b/samples/v3/pwm_audio.hpp +new file mode 100644 +index 0000000..73c2c24 +--- /dev/null ++++ b/samples/v3/pwm_audio.hpp +@@ -0,0 +1,109 @@ ++#ifndef PWM_AUDIO_HPP ++#define PWM_AUDIO_HPP ++ ++#include "stdint.h" ++#include "hardware/gpio.h" ++#include "hardware/pwm.h" ++#include "hardware/dma.h" ++#include "hardware/clocks.h" ++#include "hardware/irq.h" ++#include "pico/stdlib.h" ++ ++using DmaFinishdedHandler = void(*)(); ++ ++class PwmAudio; ++static PwmAudio *inst; ++ ++class PwmAudio { ++public: ++ using sample_t = uint32_t; ++ ++ const dma_channel_transfer_size DMA_WIDTH = ++ sizeof(sample_t) == 1 ? DMA_SIZE_8 : ++ sizeof(sample_t) == 2 ? DMA_SIZE_16 : ++ DMA_SIZE_32; ++ ++ const int PIN; ++ const int SLICE_NUM; ++ const int SAMPLE_BITS; ++ const float FREQUENCY; ++ const int LATENCY; ++ DmaFinishdedHandler dma_handler; ++ ++ sample_t *buff; ++ int dma_ch; ++ int playing_bank; ++ ++ PwmAudio(int pin, int latency, int sample_bits, float freq_ratio, DmaFinishdedHandler dma_handler) : ++ PIN(pin), ++ SLICE_NUM(pwm_gpio_to_slice_num(pin)), ++ SAMPLE_BITS(sample_bits), ++ FREQUENCY(freq_ratio), ++ LATENCY(latency), ++ buff(new sample_t[latency]), ++ dma_handler(dma_handler) ++ { ++ gpio_set_function(PIN, GPIO_FUNC_PWM); ++ ++ int pwm_period = 1 << SAMPLE_BITS; ++ float pwm_clkdiv = freq_ratio / pwm_period; ++ ++ pwm_config pwm_cfg = pwm_get_default_config(); ++ pwm_config_set_clkdiv(&pwm_cfg, pwm_clkdiv); ++ pwm_config_set_wrap(&pwm_cfg, pwm_period - 1); ++ pwm_init(SLICE_NUM, &pwm_cfg, true); ++ ++ pwm_set_gpio_level(PIN, 1 << (SAMPLE_BITS-1)); ++ ++ inst = this; ++ } ++ ++ void play() { ++ dma_ch = dma_claim_unused_channel(true); ++ dma_channel_set_irq0_enabled(dma_ch, true); ++ irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); ++ irq_set_enabled(DMA_IRQ_0, true); ++ ++ playing_bank = 0; ++ start_dma(); ++ } ++ ++ void stop() { ++ dma_channel_unclaim(dma_ch); ++ pwm_set_gpio_level(PIN, 1 << (SAMPLE_BITS-1)); ++ } ++ ++ void start_dma() { ++ dma_channel_config dma_cfg = dma_channel_get_default_config(dma_ch); ++ channel_config_set_transfer_data_size(&dma_cfg, DMA_SIZE_32); ++ channel_config_set_read_increment(&dma_cfg, true); ++ channel_config_set_write_increment(&dma_cfg, false); ++ channel_config_set_dreq(&dma_cfg, DREQ_PWM_WRAP0 + SLICE_NUM); ++ dma_channel_configure( ++ dma_ch, ++ &dma_cfg, ++ &pwm_hw->slice[SLICE_NUM].cc, // write addr ++ buff + (playing_bank * LATENCY), // read addr ++ LATENCY, // number of data ++ true // start immediately ++ ); ++ } ++ ++ void flip_buffer() { ++ int fill_bank = playing_bank; ++ playing_bank = (playing_bank + 1) & 1; ++ inst->start_dma(); ++ dma_hw->ints0 = (1u << dma_ch); ++ } ++ ++ sample_t* get_buffer(int bank) { ++ return buff + (bank * LATENCY); ++ } ++ ++ sample_t* get_next_buffer() { ++ int next_bank = (playing_bank + 1) & 1; ++ return get_buffer(next_bank); ++ } ++}; ++ ++#endif