diff --git a/.gitignore b/.gitignore index 1a07a50..d512e27 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build uf2 .pio .vscode +*_bin.h \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 4a2c3e3..a8f62a9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -6,23 +6,24 @@ platform = https://github.com/maxgerhardt/platform-raspberrypi.git#651837d09a1a5 framework = arduino board_build.core = earlephilhower -board_build.filesystem_size = 512k +#board_build.filesystem_size = 512k -#upload_protocol = mbed -upload_protocol = cmsis-dap +upload_protocol = mbed +#upload_protocol = cmsis-dap debug_tool = cmsis-dap build_src_filter = +<*.cpp> -#lib_ldf_mode = chain+ +lib_ldf_mode = chain+ lib_deps = bodmer/TFT_eSPI@^2.5.43 + xreef/PCF8574 library@^2.3.7 build_flags = -I. -Isrc/tft-espi-config/ - -Iinc + -Iext -Iext/minigb_apu [env:pico] diff --git a/src/core.h b/src/core.h index 367b29e..01e7be8 100644 --- a/src/core.h +++ b/src/core.h @@ -5,30 +5,37 @@ #define GBCOLOR_HEADER_ONLY #include "gbcolors.h" -/* Multicore command structure. */ -union core_cmd -{ - struct - { - /* Does nothing. */ -#define CORE_CMD_NOP 0 - /* Set line "data" on the LCD. Pixel data is in pixels_buffer. */ -#define CORE_CMD_LCD_LINE 1 - /* Control idle mode on the LCD. Limits colours to 2 bits. */ -#define CORE_CMD_IDLE_SET 2 - /* Set a specific pixel. For debugging. */ -#define CORE_CMD_SET_PIXEL 3 - uint8_t cmd; - uint8_t unused1; - uint8_t unused2; - uint8_t data; - }; - uint32_t full; +enum class ScalingMode { + NORMAL = 0, + STRETCH, + STRETCH_KEEP_ASPECT, + COUNT }; +extern volatile ScalingMode scalingMode; + +/* Multicore command structure. */ +union core_cmd { + struct + { + /* Does nothing. */ +#define CORE_CMD_NOP 0 + /* Set line "data" on the LCD. Pixel data is in pixels_buffer. */ +#define CORE_CMD_LCD_LINE 1 + /* Control idle mode on the LCD. Limits colours to 2 bits. */ +#define CORE_CMD_IDLE_SET 2 + /* Set a specific pixel. For debugging. */ +#define CORE_CMD_SET_PIXEL 3 + uint8_t cmd; + uint8_t unused1; + uint8_t unused2; + uint8_t data; + }; + uint32_t full; +}; extern palette_t palette; // Colour palette -void lcd_draw_line(struct gb_s *gb, const uint8_t *pixels, const uint_fast8_t line); +void lcd_draw_line(struct gb_s* gb, const uint8_t* pixels, const uint_fast8_t line); void core1_init(); diff --git a/src/lcd_core.cpp b/src/lcd_core.cpp index 4518f7f..6d91c41 100644 --- a/src/lcd_core.cpp +++ b/src/lcd_core.cpp @@ -16,16 +16,17 @@ static uint8_t pixels_buffer[LCD_WIDTH]; palette_t palette; // Colour palette +volatile ScalingMode scalingMode = ScalingMode::NORMAL; + static int lcd_line_busy = 0; -#define IS_LINE_REPEATED(line) ((line % 2) || (line % 6 == 0)) -// #define IS_LINE_REPEATED(line) 0 +#define IS_REPEATED(pos) ((pos % 2) || (pos % 6 == 0)) static void calcExtraLineTable() { uint8_t offset = 0; for (uint8_t line = 0; line < LCD_HEIGHT; ++line) { scaledLineOffsetTable[line] = offset; - offset += 1 + IS_LINE_REPEATED(line); + offset += 1 + IS_REPEATED(line); } } @@ -54,21 +55,66 @@ void lcd_draw_line(struct gb_s* gb, const uint8_t pixels[LCD_WIDTH], multicore_fifo_push_blocking(cmd.full); } -void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) { +void lcd_write_pixels_normal(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) { + const uint16_t colOffset = (tft.width() - nmemb) / 2; + const uint16_t lineOffset = (tft.height() - LCD_HEIGHT) / 2; + tft.setAddrWindow(colOffset, lineOffset + line, nmemb, 1); + tft.pushColors((uint16_t*) pixels, nmemb, true); +} + +void lcd_write_pixels_stretched(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) { static uint16_t doubledPixels[320]; uint16_t pos = 0; - for (int i = 0; i < nmemb; ++i) { - doubledPixels[pos++] = pixels[i]; - doubledPixels[pos++] = pixels[i]; + for (int col = 0; col < nmemb; ++col) { + doubledPixels[pos++] = pixels[col]; + doubledPixels[pos++] = pixels[col]; } + const uint16_t stretchedWidth = pos; - uint8_t repeatedLines = IS_LINE_REPEATED(line); - // tft.setAddrWindow(0, scaledLineOffsetTable[line], nmemb * 2, 1 + repeatedLines); - tft.setAddrWindow(0, scaledLineOffsetTable[line], nmemb * 2, 1); - tft.pushColors((uint16_t*)doubledPixels, nmemb * 2, true); + uint8_t repeatedLines = IS_REPEATED(line); + tft.setAddrWindow(0, scaledLineOffsetTable[line], stretchedWidth, 1); + tft.pushColors((uint16_t*)doubledPixels, stretchedWidth, true); if (repeatedLines) { - tft.setAddrWindow(0, scaledLineOffsetTable[line] + 1, nmemb * 2, 1); - tft.pushColors((uint16_t*)doubledPixels, nmemb * 2, true); + tft.setAddrWindow(0, scaledLineOffsetTable[line] + 1, stretchedWidth, 1); + tft.pushColors((uint16_t*)doubledPixels, stretchedWidth, true); + } +} + +void lcd_write_pixels_stretched_keep_aspect(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) { + static uint16_t doubledPixels[320]; + uint16_t pos = 0; + for (int col = 0; col < nmemb; ++col) { + doubledPixels[pos++] = pixels[col]; + if (IS_REPEATED(col)) { + doubledPixels[pos++] = pixels[col]; + } + } + const uint16_t stretchedWidth = pos; + + const uint16_t colOffset = (tft.width() - stretchedWidth) / 2; + + uint8_t repeatedLines = IS_REPEATED(line); + tft.setAddrWindow(colOffset, scaledLineOffsetTable[line], stretchedWidth, 1); + tft.pushColors((uint16_t*)doubledPixels, stretchedWidth, true); + if (repeatedLines) { + tft.setAddrWindow(colOffset, scaledLineOffsetTable[line] + 1, stretchedWidth, 1); + tft.pushColors((uint16_t*)doubledPixels, stretchedWidth, true); + } +} + +void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) { + switch (scalingMode) + { + case ScalingMode::STRETCH: + lcd_write_pixels_stretched(pixels, line, nmemb); + break; + case ScalingMode::STRETCH_KEEP_ASPECT: + lcd_write_pixels_stretched_keep_aspect(pixels, line, nmemb); + break; + case ScalingMode::NORMAL: + default: + lcd_write_pixels_normal(pixels, line, nmemb); + break; } } @@ -112,7 +158,7 @@ void core1DispatchLoop() { break; case CORE_CMD_IDLE_SET: - lcd_display_control(true, cmd.data); + lcd_fill(TFT_BLACK); break; case CORE_CMD_NOP: diff --git a/src/main.cpp b/src/main.cpp index 4d9207f..f510a47 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,8 @@ /* RP2040 Headers */ #include +#include + /* Project headers */ #include "hedley.h" #include "minigb_apu.h" @@ -39,14 +41,25 @@ #include "i2s.h" /* GPIO Connections. */ -#define GPIO_UP 16 -#define GPIO_DOWN 16 -#define GPIO_LEFT 16 -#define GPIO_RIGHT 16 -#define GPIO_A 16 -#define GPIO_B 16 -#define GPIO_SELECT 16 -#define GPIO_START 16 +#ifdef USE_PAD_GPIO +#define PIN_UP 2 +#define PIN_DOWN 3 +#define PIN_LEFT 4 +#define PIN_RIGHT 5 +#define PIN_A 6 +#define PIN_B 7 +#define PIN_SELECT 8 +#define PIN_START 9 +#else +#define PIN_UP 0 +#define PIN_DOWN 1 +#define PIN_LEFT 2 +#define PIN_RIGHT 3 +#define PIN_A 5 +#define PIN_B 4 +#define PIN_SELECT 6 +#define PIN_START 7 +#endif #if ENABLE_SOUND /** @@ -72,6 +85,9 @@ static unsigned char rom_bank0[65536]; static uint8_t ram[32768]; static uint8_t manual_palette_selected = 0; +static PCF8574 pcf8574(0x20, 16, 17); + + static struct { unsigned a : 1; @@ -130,9 +146,9 @@ void gb_error(struct gb_s* gb, const enum gb_error_e gb_err, const uint16_t addr #endif } -void stop(); +void reset(); -void start() { +void startEmulator() { #if ENABLE_LCD #if ENABLE_SDCARD /* ROM File selector */ @@ -150,7 +166,7 @@ void start() { if (ret != GB_INIT_NO_ERROR) { Serial.printf("Error: %d\n", ret); - stop(); + reset(); } /* Automatically assign a colour palette to the game */ @@ -182,38 +198,15 @@ void start() { Serial.print("\n> "); } -void stop() { +void reset() { Serial.println("\nEmulation Ended"); /* stop lcd task running on core 1 */ multicore_reset_core1(); - while (true) - ; + watchdog_reboot(0,0,0); } -void setup(void) { - /* Overclock. */ - { - const unsigned vco = 1596 * 1000 * 1000; /* 266MHz */ - const unsigned div1 = 6, div2 = 1; - - vreg_set_voltage(VREG_VOLTAGE_1_15); - sleep_ms(2); - set_sys_clock_pll(vco, div1, div2); - sleep_ms(2); - } - - /* Initialise USB serial connection for debugging. */ - Serial.begin(115200); - while (!Serial) - ; - -#if ENABLE_SDCARD - time_init(); -#endif - // sleep_ms(5000); - Serial.println("INIT: "); - - /* Initialise GPIO pins. */ +#ifdef USE_PAD_GPIO +void initJoypad() { gpio_set_function(GPIO_UP, GPIO_FUNC_SIO); gpio_set_function(GPIO_DOWN, GPIO_FUNC_SIO); gpio_set_function(GPIO_LEFT, GPIO_FUNC_SIO); @@ -240,6 +233,49 @@ void setup(void) { gpio_pull_up(GPIO_B); gpio_pull_up(GPIO_SELECT); gpio_pull_up(GPIO_START); +} +#else +void initJoypad() { + for (int pin = 0; pin < 8; ++pin) { + pcf8574.pinMode(pin, INPUT_PULLUP); + } + + pcf8574.setLatency(5); + + if (pcf8574.begin()){ + Serial.println("PCF8574 initialized"); + }else{ + Serial.println("PCF8574 initialization failed"); + while (true) ; + } +} +#endif + +void setup(void) { + /* Overclock. */ + { + const unsigned vco = 1596 * 1000 * 1000; /* 266MHz */ + const unsigned div1 = 6, div2 = 1; + + vreg_set_voltage(VREG_VOLTAGE_1_15); + sleep_ms(2); + set_sys_clock_pll(vco, div1, div2); + sleep_ms(2); + } + + /* Initialise USB serial connection for debugging. */ + Serial.begin(115200); + //while (!Serial) ; + //delay(2000); + +#if ENABLE_SDCARD + time_init(); +#endif + // sleep_ms(5000); + Serial.println("INIT: "); + + /* Initialise joypad. */ + initJoypad(); /* Set SPI clock to use high frequency. */ #if 0 @@ -264,7 +300,7 @@ void setup(void) { i2s_init(&i2s_config); #endif - start(); + startEmulator(); } void nextPalette() { @@ -277,7 +313,7 @@ void prevPalette() { manual_assign_palette(palette, manual_palette_selected); } -void handleInput(uint_fast32_t& frames) { +void handleSerial(uint_fast32_t& frames) { static uint64_t start_time = time_us_64(); /* Serial monitor commands */ @@ -287,37 +323,6 @@ void handleInput(uint_fast32_t& frames) { } switch (input) { -#if 0 - static bool invert = false; - static bool sleep = false; - static uint8_t freq = 1; - static ili9225_color_mode_e colour = ILI9225_COLOR_MODE_FULL; - - case 'i': - invert = !invert; - mk_ili9225_display_control(invert, colour); - break; - - case 'f': - freq++; - freq &= 0x0F; - mk_ili9225_set_drive_freq(freq); - Serial.printf("Freq %u\n", freq); - break; -#endif - case 'c': { -#if 0 - static ili9225_color_mode_e mode = ILI9225_COLOR_MODE_FULL; - union core_cmd cmd; - - mode = !mode; - cmd.cmd = CORE_CMD_IDLE_SET; - cmd.data = mode; - multicore_fifo_push_blocking(cmd.full); -#endif - break; - } - case 'i': gb.direct.interlace = !gb.direct.interlace; break; @@ -387,7 +392,7 @@ void handleInput(uint_fast32_t& frames) { } case 'q': - stop(); + reset(); case 'p': nextPalette(); @@ -407,14 +412,35 @@ void handlePad() { prev_joypad_bits.b = gb.direct.joypad_bits.b; prev_joypad_bits.select = gb.direct.joypad_bits.select; prev_joypad_bits.start = gb.direct.joypad_bits.start; - gb.direct.joypad_bits.up = gpio_get(GPIO_UP); - gb.direct.joypad_bits.down = gpio_get(GPIO_DOWN); - gb.direct.joypad_bits.left = gpio_get(GPIO_LEFT); - gb.direct.joypad_bits.right = gpio_get(GPIO_RIGHT); - gb.direct.joypad_bits.a = gpio_get(GPIO_A); - gb.direct.joypad_bits.b = gpio_get(GPIO_B); - gb.direct.joypad_bits.select = gpio_get(GPIO_SELECT); - gb.direct.joypad_bits.start = gpio_get(GPIO_START); + +#ifdef USE_PAD_GPIO + gb.direct.joypad_bits.up = gpio_get(PIN_UP); + gb.direct.joypad_bits.down = gpio_get(PIN_DOWN); + gb.direct.joypad_bits.left = gpio_get(PIN_LEFT); + gb.direct.joypad_bits.right = gpio_get(PIN_RIGHT); + gb.direct.joypad_bits.a = gpio_get(PIN_A); + gb.direct.joypad_bits.b = gpio_get(PIN_B); + gb.direct.joypad_bits.select = gpio_get(PIN_SELECT); + gb.direct.joypad_bits.start = gpio_get(PIN_START); +#else + #if 0 + Serial.printf("pins:\n"); + for (int i = 0; i < 8; ++i) { + int in = pcf8574.digitalRead(i); + Serial.printf(" %d", in); + } + Serial.printf("\n"); + #endif + + gb.direct.joypad_bits.up = pcf8574.digitalRead(PIN_UP); + gb.direct.joypad_bits.down = pcf8574.digitalRead(PIN_DOWN); + gb.direct.joypad_bits.left = pcf8574.digitalRead(PIN_LEFT); + gb.direct.joypad_bits.right = pcf8574.digitalRead(PIN_RIGHT); + gb.direct.joypad_bits.a = pcf8574.digitalRead(PIN_A); + gb.direct.joypad_bits.b = pcf8574.digitalRead(PIN_B); + gb.direct.joypad_bits.select = pcf8574.digitalRead(PIN_SELECT); + gb.direct.joypad_bits.start = pcf8574.digitalRead(PIN_START); +#endif /* hotkeys (select + * combo)*/ if (!gb.direct.joypad_bits.select) { @@ -441,13 +467,21 @@ void handlePad() { #if ENABLE_SDCARD write_cart_ram_file(&gb); #endif - stop(); + reset(); } if (!gb.direct.joypad_bits.a && prev_joypad_bits.a) { /* select + A: enable/disable frame-skip => fast-forward */ gb.direct.frame_skip = !gb.direct.frame_skip; Serial.printf("I gb.direct.frame_skip = %d\n", gb.direct.frame_skip); } + if (!gb.direct.joypad_bits.b && prev_joypad_bits.b) { + /* select + B: change scaling mode */ + scalingMode = (ScalingMode)(((int) scalingMode + 1) % ((int) ScalingMode::COUNT)); + union core_cmd cmd; + cmd.cmd = CORE_CMD_IDLE_SET; + multicore_fifo_push_blocking(cmd.full); + Serial.printf("I Scaling mode: = %d\n", scalingMode); + } } } @@ -470,5 +504,5 @@ void loop() { #endif handlePad(); - handleInput(frames); + handleSerial(frames); }