Skip to content

Commit 3f72f4c

Browse files
authored
Improve genesis emulation (#88)
* 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
1 parent ad5fbb5 commit 3f72f4c

File tree

7 files changed

+49
-21
lines changed

7 files changed

+49
-21
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ ESP32-S3-BOX-3 which provides:
2020
- NES Emulator (nofrendo)
2121
- Gameboy / Gameboy Color emulator (gnuboy)
2222
- Sega Master System / Game Gear emulator (smsplus)
23-
- Genesis emulator (gwenesis); NOTE: this is a WIP and does not support full-speed / sound / savestates yet.
23+
- Genesis emulator (gwenesis) - full speed / buttery smooth when muted; unmuted it runs a little slower but has nice sound
2424
- LVGL main menu with rom select (including boxart display) and settings page
2525
(all generated from Squareline Studio)
2626
- LVGL emulation paused menu with save slot select, save slot image display,

components/genesis/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ idf_component_register(
55
REQUIRES box-emu statistics
66
)
77
# target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-char-subscripts -Wno-attributes -Wno-implicit-fallthrough -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers)
8-
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-const-variable -Wno-unused-value -O3)
9-
# target_compile_definitions(${COMPONENT_LIB} PRIVATE GWENESIS_AUDIO_ACCURATE=0)
8+
target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-const-variable -Wno-unused-value -Ofast)
9+
target_compile_definitions(${COMPONENT_LIB} PRIVATE GWENESIS_AUDIO_ACCURATE=0)

components/genesis/gwenesis/src/bus/gwenesis_bus.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ __license__ = "GPLv3"
4343
#define GWENESIS_REFRESH_RATE_PAL 50
4444
#define GWENESIS_AUDIO_FREQ_PAL 52781
4545

46-
#define GWENESIS_AUDIO_ACCURATE 0
47-
4846
#define Z80_FREQ_DIVISOR 14 // Frequency divisor to Z80 clock
4947
#define VDP_CYCLES_PER_LINE 3420// VDP Cycles per Line
5048
#define SCREEN_WIDTH 320

components/genesis/src/genesis.cpp

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ static uint8_t *frame_buffer = nullptr;
3636

3737
extern unsigned char* VRAM;
3838
extern int zclk;
39-
int system_clock;
39+
static int system_clock;
4040
int scan_line;
4141

4242
int16_t *gwenesis_sn76489_buffer = nullptr;
@@ -46,7 +46,9 @@ int16_t *gwenesis_ym2612_buffer = nullptr;
4646
int ym2612_index;
4747
int ym2612_clock;
4848

49-
static int frameskip = 3;
49+
static constexpr int full_frameskip = 3;
50+
static constexpr int muted_frameskip = 2;
51+
static int frameskip = full_frameskip;
5052

5153
static FILE *savestate_fp = NULL;
5254
static int savestate_errors = 0;
@@ -185,8 +187,9 @@ void IRAM_ATTR run_genesis_rom() {
185187
static GamepadState previous_state = {};
186188
auto state = BoxEmu::get().gamepad_state();
187189

188-
// set frameskip to be 3 if muted, 60 otherwise
189-
frameskip = 3; // hal::is_muted() ? 3 : 60;
190+
bool sound_enabled = !espp::EspBox::get().is_muted();
191+
192+
frameskip = sound_enabled ? full_frameskip : muted_frameskip;
190193

191194
if (previous_state != state) {
192195
// button mapping:
@@ -224,8 +227,6 @@ void IRAM_ATTR run_genesis_rom() {
224227

225228
gwenesis_vdp_render_config();
226229

227-
bool sound_enabled = !espp::EspBox::get().is_muted();
228-
229230
/* Reset the difference clocks and audio index */
230231
system_clock = 0;
231232
zclk = sound_enabled ? 0 : 0x1000000;
@@ -238,7 +239,7 @@ void IRAM_ATTR run_genesis_rom() {
238239

239240
scan_line = 0;
240241

241-
int _vdp_cycles_per_line = VDP_CYCLES_PER_LINE / 2;
242+
static constexpr int _vdp_cycles_per_line = VDP_CYCLES_PER_LINE;
242243

243244
while (scan_line < lines_per_frame) {
244245
system_clock += _vdp_cycles_per_line;
@@ -251,7 +252,7 @@ void IRAM_ATTR run_genesis_rom() {
251252
* =1 : cycle accurate mode. audio is refreshed when CPUs are performing a R/W access
252253
* =0 : line accurate mode. audio is refreshed every lines.
253254
*/
254-
if (GWENESIS_AUDIO_ACCURATE == 0) {
255+
if (GWENESIS_AUDIO_ACCURATE == 0 && sound_enabled) {
255256
gwenesis_SN76489_run(system_clock);
256257
ym2612_run(system_clock);
257258
}
@@ -292,14 +293,13 @@ void IRAM_ATTR run_genesis_rom() {
292293
if (scan_line == (screen_height + 1)) {
293294
z80_irq_line(0);
294295
}
295-
296296
} // end of scanline loop
297297

298298
/* Audio
299299
* synchronize YM2612 and SN76489 to system_clock
300300
* it completes the missing audio sample for accurate audio mode
301301
*/
302-
if (GWENESIS_AUDIO_ACCURATE == 1) {
302+
if (GWENESIS_AUDIO_ACCURATE == 1 && sound_enabled) {
303303
gwenesis_SN76489_run(system_clock);
304304
ym2612_run(system_clock);
305305
}
@@ -324,8 +324,21 @@ void IRAM_ATTR run_genesis_rom() {
324324

325325
if (sound_enabled) {
326326
// push the audio buffer to the audio task
327-
int audio_len = REG1_PAL ? GWENESIS_AUDIO_BUFFER_LENGTH_PAL : GWENESIS_AUDIO_BUFFER_LENGTH_NTSC;
328-
espp::EspBox::get().play_audio((uint8_t*)gwenesis_ym2612_buffer, audio_len);
327+
int audio_len = std::max(sn76489_index, ym2612_index);
328+
// Mix gwenesis_sn76489_buffer and gwenesis_ym2612_buffer together
329+
const int16_t* sn76489_buffer = gwenesis_sn76489_buffer;
330+
const int16_t* ym2612_buffer = gwenesis_ym2612_buffer;
331+
for (int i = 0; i < audio_len; i++) {
332+
int16_t sample = 0;
333+
if (sn76489_index < audio_len) {
334+
sample += sn76489_buffer[sn76489_index];
335+
}
336+
if (ym2612_index < audio_len) {
337+
sample += ym2612_buffer[ym2612_index];
338+
}
339+
gwenesis_sn76489_buffer[i] = sample;
340+
}
341+
espp::EspBox::get().play_audio((uint8_t*)gwenesis_ym2612_buffer, audio_len * sizeof(int16_t));
329342
}
330343

331344
// manage statistics
@@ -336,6 +349,8 @@ void IRAM_ATTR run_genesis_rom() {
336349
if (elapsed < max_frame_time) {
337350
auto sleep_time = (max_frame_time - elapsed) / 1e3;
338351
std::this_thread::sleep_for(sleep_time * std::chrono::milliseconds(1));
352+
} else {
353+
vTaskDelay(1);
339354
}
340355
}
341356

main/main.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ extern "C" void app_main(void) {
2424
logger.info("Bootup");
2525

2626
// initialize the hardware abstraction layer
27-
BoxEmu &emu = BoxEmu::get();
2827
espp::EspBox &box = espp::EspBox::get();
2928
logger.info("Running on {}", box.box_type());
29+
BoxEmu &emu = BoxEmu::get();
3030
logger.info("Box Emu version: {}", emu.version());
3131

3232
// initialize
@@ -78,6 +78,10 @@ extern "C" void app_main(void) {
7878

7979
print_heap_state();
8080

81+
// set the task priority (for main) to high
82+
vTaskPrioritySet(nullptr, 20);
83+
84+
// main loop
8185
while (true) {
8286
// reset gui ready to play and user_quit
8387
gui.ready_to_play(false);

patches.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ else
77
echo "Applying patches to esp-idf in '${IDF_PATH}'"
88
fi
99

10-
lodestone_dir=$(pwd)
10+
cur_dir=$(pwd)
1111
patches=($(find patches -type f))
1212
cd "${IDF_PATH}"
1313
for patch in "${patches[@]}"; do
1414
echo "Applying patch: ${patch}"
15-
git apply ${lodestone_dir}/${patch}
15+
git apply ${cur_dir}/${patch}
1616
done
1717

18-
cd ${lodestone_dir}
18+
cd ${cur_dir}

sdkconfig.defaults

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,14 @@ CONFIG_LV_USE_THEME_DEFAULT=y
8585
CONFIG_LV_THEME_DEFAULT_DARK=y
8686
CONFIG_LV_THEME_DEFAULT_GROW=y
8787
CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=30
88+
89+
CONFIG_I2S_ISR_IRAM_SAFE=y
90+
CONFIG_I2C_ISR_IRAM_SAFE=y
91+
CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y
92+
CONFIG_GDMA_ISR_IRAM_SAFE=y
93+
CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=n
94+
95+
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
96+
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
97+
98+
CONFIG_SPI_FLASH_ROM_IMPL=y

0 commit comments

Comments
 (0)