diff --git a/README.md b/README.md index e3a47647..fd306d15 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ESP32-S3-BOX-3 which provides: - NES Emulator (nofrendo) - Gameboy / Gameboy Color emulator (gnuboy) - Sega Master System / Game Gear emulator (smsplus) - - Genesis emulator (gwenesis); NOTE: this is a WIP and does not support full-speed / sound / savestates yet. + - Genesis emulator (gwenesis) - full speed / buttery smooth when muted; unmuted it runs a little slower but has nice sound - LVGL main menu with rom select (including boxart display) and settings page (all generated from Squareline Studio) - LVGL emulation paused menu with save slot select, save slot image display, diff --git a/components/genesis/CMakeLists.txt b/components/genesis/CMakeLists.txt index 229d7d57..13bb778e 100644 --- a/components/genesis/CMakeLists.txt +++ b/components/genesis/CMakeLists.txt @@ -5,5 +5,5 @@ idf_component_register( REQUIRES box-emu statistics ) # target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-char-subscripts -Wno-attributes -Wno-implicit-fallthrough -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers) -target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-const-variable -Wno-unused-value -O3) -# target_compile_definitions(${COMPONENT_LIB} PRIVATE GWENESIS_AUDIO_ACCURATE=0) +target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-const-variable -Wno-unused-value -Ofast) +target_compile_definitions(${COMPONENT_LIB} PRIVATE GWENESIS_AUDIO_ACCURATE=0) diff --git a/components/genesis/gwenesis/src/bus/gwenesis_bus.h b/components/genesis/gwenesis/src/bus/gwenesis_bus.h index 7ebab971..dd499b35 100644 --- a/components/genesis/gwenesis/src/bus/gwenesis_bus.h +++ b/components/genesis/gwenesis/src/bus/gwenesis_bus.h @@ -43,8 +43,6 @@ __license__ = "GPLv3" #define GWENESIS_REFRESH_RATE_PAL 50 #define GWENESIS_AUDIO_FREQ_PAL 52781 -#define GWENESIS_AUDIO_ACCURATE 0 - #define Z80_FREQ_DIVISOR 14 // Frequency divisor to Z80 clock #define VDP_CYCLES_PER_LINE 3420// VDP Cycles per Line #define SCREEN_WIDTH 320 diff --git a/components/genesis/src/genesis.cpp b/components/genesis/src/genesis.cpp index cfcf1027..8b91fc01 100644 --- a/components/genesis/src/genesis.cpp +++ b/components/genesis/src/genesis.cpp @@ -36,7 +36,7 @@ static uint8_t *frame_buffer = nullptr; extern unsigned char* VRAM; extern int zclk; -int system_clock; +static int system_clock; int scan_line; int16_t *gwenesis_sn76489_buffer = nullptr; @@ -46,7 +46,9 @@ int16_t *gwenesis_ym2612_buffer = nullptr; int ym2612_index; int ym2612_clock; -static int frameskip = 3; +static constexpr int full_frameskip = 3; +static constexpr int muted_frameskip = 2; +static int frameskip = full_frameskip; static FILE *savestate_fp = NULL; static int savestate_errors = 0; @@ -185,8 +187,9 @@ void IRAM_ATTR run_genesis_rom() { static GamepadState previous_state = {}; auto state = BoxEmu::get().gamepad_state(); - // set frameskip to be 3 if muted, 60 otherwise - frameskip = 3; // hal::is_muted() ? 3 : 60; + bool sound_enabled = !espp::EspBox::get().is_muted(); + + frameskip = sound_enabled ? full_frameskip : muted_frameskip; if (previous_state != state) { // button mapping: @@ -224,8 +227,6 @@ void IRAM_ATTR run_genesis_rom() { gwenesis_vdp_render_config(); - bool sound_enabled = !espp::EspBox::get().is_muted(); - /* Reset the difference clocks and audio index */ system_clock = 0; zclk = sound_enabled ? 0 : 0x1000000; @@ -238,7 +239,7 @@ void IRAM_ATTR run_genesis_rom() { scan_line = 0; - int _vdp_cycles_per_line = VDP_CYCLES_PER_LINE / 2; + static constexpr int _vdp_cycles_per_line = VDP_CYCLES_PER_LINE; while (scan_line < lines_per_frame) { system_clock += _vdp_cycles_per_line; @@ -251,7 +252,7 @@ void IRAM_ATTR run_genesis_rom() { * =1 : cycle accurate mode. audio is refreshed when CPUs are performing a R/W access * =0 : line accurate mode. audio is refreshed every lines. */ - if (GWENESIS_AUDIO_ACCURATE == 0) { + if (GWENESIS_AUDIO_ACCURATE == 0 && sound_enabled) { gwenesis_SN76489_run(system_clock); ym2612_run(system_clock); } @@ -292,14 +293,13 @@ void IRAM_ATTR run_genesis_rom() { if (scan_line == (screen_height + 1)) { z80_irq_line(0); } - } // end of scanline loop /* Audio * synchronize YM2612 and SN76489 to system_clock * it completes the missing audio sample for accurate audio mode */ - if (GWENESIS_AUDIO_ACCURATE == 1) { + if (GWENESIS_AUDIO_ACCURATE == 1 && sound_enabled) { gwenesis_SN76489_run(system_clock); ym2612_run(system_clock); } @@ -324,8 +324,21 @@ void IRAM_ATTR run_genesis_rom() { if (sound_enabled) { // push the audio buffer to the audio task - int audio_len = REG1_PAL ? GWENESIS_AUDIO_BUFFER_LENGTH_PAL : GWENESIS_AUDIO_BUFFER_LENGTH_NTSC; - espp::EspBox::get().play_audio((uint8_t*)gwenesis_ym2612_buffer, audio_len); + int audio_len = std::max(sn76489_index, ym2612_index); + // Mix gwenesis_sn76489_buffer and gwenesis_ym2612_buffer together + const int16_t* sn76489_buffer = gwenesis_sn76489_buffer; + const int16_t* ym2612_buffer = gwenesis_ym2612_buffer; + for (int i = 0; i < audio_len; i++) { + int16_t sample = 0; + if (sn76489_index < audio_len) { + sample += sn76489_buffer[sn76489_index]; + } + if (ym2612_index < audio_len) { + sample += ym2612_buffer[ym2612_index]; + } + gwenesis_sn76489_buffer[i] = sample; + } + espp::EspBox::get().play_audio((uint8_t*)gwenesis_ym2612_buffer, audio_len * sizeof(int16_t)); } // manage statistics @@ -336,6 +349,8 @@ void IRAM_ATTR run_genesis_rom() { if (elapsed < max_frame_time) { auto sleep_time = (max_frame_time - elapsed) / 1e3; std::this_thread::sleep_for(sleep_time * std::chrono::milliseconds(1)); + } else { + vTaskDelay(1); } } diff --git a/main/main.cpp b/main/main.cpp index 2a34f2b2..33828396 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -24,9 +24,9 @@ extern "C" void app_main(void) { logger.info("Bootup"); // initialize the hardware abstraction layer - BoxEmu &emu = BoxEmu::get(); espp::EspBox &box = espp::EspBox::get(); logger.info("Running on {}", box.box_type()); + BoxEmu &emu = BoxEmu::get(); logger.info("Box Emu version: {}", emu.version()); // initialize @@ -78,6 +78,10 @@ extern "C" void app_main(void) { print_heap_state(); + // set the task priority (for main) to high + vTaskPrioritySet(nullptr, 20); + + // main loop while (true) { // reset gui ready to play and user_quit gui.ready_to_play(false); diff --git a/patches.sh b/patches.sh index 301f855b..3cb09b1b 100755 --- a/patches.sh +++ b/patches.sh @@ -7,12 +7,12 @@ else echo "Applying patches to esp-idf in '${IDF_PATH}'" fi -lodestone_dir=$(pwd) +cur_dir=$(pwd) patches=($(find patches -type f)) cd "${IDF_PATH}" for patch in "${patches[@]}"; do echo "Applying patch: ${patch}" - git apply ${lodestone_dir}/${patch} + git apply ${cur_dir}/${patch} done -cd ${lodestone_dir} \ No newline at end of file +cd ${cur_dir} diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 4713cb44..032f3c15 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -85,3 +85,14 @@ CONFIG_LV_USE_THEME_DEFAULT=y CONFIG_LV_THEME_DEFAULT_DARK=y CONFIG_LV_THEME_DEFAULT_GROW=y CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=30 + +CONFIG_I2S_ISR_IRAM_SAFE=y +CONFIG_I2C_ISR_IRAM_SAFE=y +CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y +CONFIG_GDMA_ISR_IRAM_SAFE=y +CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=n + +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y + +CONFIG_SPI_FLASH_ROM_IMPL=y