Skip to content

Commit

Permalink
Improve genesis emulation (#88)
Browse files Browse the repository at this point in the history
* wip some tests

* reduce full frameskip back to 3 and fix sa

* fix genesis some

* working / good genesis audio

* update sdkconfig

* fix sa

* update readme

* minor update to patches
  • Loading branch information
finger563 authored Oct 14, 2024
1 parent ad5fbb5 commit 3f72f4c
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 21 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions components/genesis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 0 additions & 2 deletions components/genesis/gwenesis/src/bus/gwenesis_bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 27 additions & 12 deletions components/genesis/src/genesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand All @@ -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
Expand All @@ -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);
}
}

Expand Down
6 changes: 5 additions & 1 deletion main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions patches.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}
cd ${cur_dir}
11 changes: 11 additions & 0 deletions sdkconfig.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 3f72f4c

Please sign in to comment.