From fec14d0d858b632c946cad5dbdfad6be85386f7f Mon Sep 17 00:00:00 2001 From: mooinglemur Date: Wed, 19 Jul 2023 09:36:02 -0700 Subject: [PATCH 1/3] Audio: implement YM2151 timers and IRQ support, add open bus behavior to IO space --- src/main.c | 16 +++++++ src/memory.c | 9 ++-- src/ymglue.cpp | 118 ++++++++++++++++++++++++++++++++++++++++--------- src/ymglue.h | 1 + 4 files changed, 121 insertions(+), 23 deletions(-) diff --git a/src/main.c b/src/main.c index 7b29bcc2..b2b5c377 100644 --- a/src/main.c +++ b/src/main.c @@ -125,6 +125,7 @@ bool headless = false; bool fullscreen = false; bool testbench = false; bool enable_midline = false; +bool ym2151_irq_support = false; char *cartridge_path = NULL; uint8_t MHZ = 8; @@ -466,6 +467,9 @@ usage() printf("-midline-effects\n"); printf("\tApproximate mid-line raster effects when changing tile, sprite,\n"); printf("\tand palette data. Requires a fast host CPU.\n"); + printf("-enable-ym2151-irq\n"); + printf("\tConnect the YM2151 IRQ source to the emulated CPU. This option increases\n"); + printf("\tCPU usage as audio render is triggered for every every CPU instruction.\n"); #ifdef TRACE printf("-trace [
]\n"); printf("\tPrint instruction trace. Optionally, a trigger address\n"); @@ -931,6 +935,10 @@ main(int argc, char **argv) argc--; argv++; enable_midline = true; + } else if (!strcmp(argv[0], "-enable-ym2151-irq")){ + argc--; + argv++; + ym2151_irq_support = true; } else { usage(); } @@ -1404,6 +1412,14 @@ emulator_loop(void *param) #endif } + // The optimization from the opportunistic batching of audio rendering + // is lost if we need to track the YM2151 IRQ, so it has been made a + // command-line switch that's disabled by default. + if (ym2151_irq_support) { + audio_render(); + if (YM_irq()) irq6502(); + } + if (video_get_irq_out() || via1_irq() || (has_via2 && via2_irq())) { // printf("IRQ!\n"); irq6502(); diff --git a/src/memory.c b/src/memory.c index e1a4f28f..26941b26 100644 --- a/src/memory.c +++ b/src/memory.c @@ -152,14 +152,17 @@ real_read6502(uint16_t address, bool debugOn, uint8_t bank) if (!debugOn) { clockticks6502 += 3; } - if (address == 0x9f41) return YM_read_status(); - return 0; + if (address == 0x9f41) { + audio_render(); + return YM_read_status(); + } + return 0x9f; // open bus read } else if (address >= 0x9fb0 && address < 0x9fc0) { // emulator state return emu_read(address & 0xf, debugOn); } else { // future expansion - return 0; + return 0x9f; // open bus read } } else if (address < 0xc000) { // banked RAM int ramBank = debugOn ? bank : effective_ram_bank(); diff --git a/src/ymglue.cpp b/src/ymglue.cpp index fef55aef..3a7af748 100644 --- a/src/ymglue.cpp +++ b/src/ymglue.cpp @@ -2,10 +2,98 @@ #include "ymfm_opm.h" #include +class ym2151_interface : public ymfm::ymfm_interface { + public: + ym2151_interface(): + m_chip(*this), + m_timers{0, 0}, + m_busy_timer{ 0 }, + m_irq_status{ false } + { } + ~ym2151_interface() { } + + virtual void ymfm_sync_mode_write(uint8_t data) override { + m_engine->engine_mode_write(data); + } + + virtual void ymfm_sync_check_interrupts() override { + m_engine->engine_check_interrupts(); + } + + virtual void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) override { + if (tnum >= 2) return; + m_timers[tnum] = duration_in_clocks; + } + + virtual void ymfm_set_busy_end(uint32_t clocks) override { + m_busy_timer = clocks; + } + + virtual bool ymfm_is_busy() override { + return m_busy_timer > 0; + } + + virtual void ymfm_update_irq(bool asserted) override { + m_irq_status = asserted; + } + + void update_clocks(int cycles) { + m_busy_timer = std::max(0, m_busy_timer - (64 * cycles)); + for (int i = 0; i < 2; ++i) { + if (m_timers[i] > 0) { + m_timers[i] = std::max(0, m_timers[i] - (64 * cycles)); + if (m_timers[i] <= 0) { + m_engine->engine_timer_expired(i); + } + } + } + } + + void write(uint8_t addr, uint8_t value) { + if (!ymfm_is_busy()) { + m_chip.write_address(addr); + m_chip.write_data(value); + } else { + printf("Busy\n"); + } + } + + void generate(uint16_t* output, uint32_t numsamples) { + int s = 0; + int ls, rs; + update_clocks(numsamples); + for (uint32_t i = 0; i < numsamples; i++) { + m_chip.generate(&opm_out); + ls = opm_out.data[0]; + rs = opm_out.data[1]; + if (ls < -32768) ls = -32768; + if (ls > 32767) ls = 32767; + if (rs < -32768) rs = -32768; + if (rs > 32767) rs = 32767; + output[s++] = (uint16_t)ls; + output[s++] = (uint16_t)rs; + } + } + + uint8_t read_status() { + return m_chip.read_status(); + } + + bool irq() { + return m_irq_status; + } + + private: + ymfm::ym2151 m_chip; + int32_t m_timers[2]; + int32_t m_busy_timer; + bool m_irq_status; + + ymfm::ym2151::output_data opm_out; +}; + namespace { - ymfm::ym2151* opm; - ymfm::ymfm_interface opm_iface; - ymfm::ym2151::output_data opm_out; + ym2151_interface opm_iface; } extern "C" { @@ -15,31 +103,21 @@ extern "C" { void YM_init(int sample_rate, int frame_rate) { // args are ignored - opm = new ymfm::ym2151(opm_iface); } void YM_stream_update(uint16_t* output, uint32_t numsamples) { - int s = 0; - int ls, rs; - for (uint32_t i = 0; i < numsamples; i++) { - opm->generate(&opm_out); - ls = opm_out.data[0]; - rs = opm_out.data[1]; - if (ls < -32768) ls = -32768; - if (ls > 32767) ls = 32767; - if (rs < -32768) rs = -32768; - if (rs > 32767) rs = 32767; - output[s++] = (uint16_t)ls; - output[s++] = (uint16_t)rs; - } + opm_iface.generate(output, numsamples); } void YM_write_reg(uint8_t reg, uint8_t val) { - opm->write_address(reg); - opm->write_data(val); + opm_iface.write(reg, val); } uint8_t YM_read_status() { - return opm->read_status(); + return opm_iface.read_status(); + } + + bool YM_irq() { + return opm_iface.irq(); } } diff --git a/src/ymglue.h b/src/ymglue.h index 4c4045bd..bd5d6055 100644 --- a/src/ymglue.h +++ b/src/ymglue.h @@ -11,6 +11,7 @@ extern "C" { void YM_init(int sample_rate, int frame_rate); void YM_stream_update(uint16_t* output, uint32_t numsamples); void YM_write_reg(uint8_t reg, uint8_t val); + bool YM_irq(void); #ifdef __cplusplus } From 28b56078dfd003663f35608c299dd4106472a68a Mon Sep 17 00:00:00 2001 From: mooinglemur Date: Wed, 19 Jul 2023 09:51:10 -0700 Subject: [PATCH 2/3] fix typo --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index b2b5c377..e47d972a 100644 --- a/src/main.c +++ b/src/main.c @@ -469,7 +469,7 @@ usage() printf("\tand palette data. Requires a fast host CPU.\n"); printf("-enable-ym2151-irq\n"); printf("\tConnect the YM2151 IRQ source to the emulated CPU. This option increases\n"); - printf("\tCPU usage as audio render is triggered for every every CPU instruction.\n"); + printf("\tCPU usage as audio render is triggered for every CPU instruction.\n"); #ifdef TRACE printf("-trace [
]\n"); printf("\tPrint instruction trace. Optionally, a trigger address\n"); From f94a2c00389bcf73ef3c4996485455ca5b9ba94f Mon Sep 17 00:00:00 2001 From: mooinglemur Date: Wed, 19 Jul 2023 09:58:48 -0700 Subject: [PATCH 3/3] YM2151: Update busy console output, move compat unsigned cast out to C interface --- src/ymglue.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ymglue.cpp b/src/ymglue.cpp index 3a7af748..1838433f 100644 --- a/src/ymglue.cpp +++ b/src/ymglue.cpp @@ -54,11 +54,11 @@ class ym2151_interface : public ymfm::ymfm_interface { m_chip.write_address(addr); m_chip.write_data(value); } else { - printf("Busy\n"); + printf("YM2151 write received while busy.\n"); } } - void generate(uint16_t* output, uint32_t numsamples) { + void generate(int16_t* output, uint32_t numsamples) { int s = 0; int ls, rs; update_clocks(numsamples); @@ -70,8 +70,8 @@ class ym2151_interface : public ymfm::ymfm_interface { if (ls > 32767) ls = 32767; if (rs < -32768) rs = -32768; if (rs > 32767) rs = 32767; - output[s++] = (uint16_t)ls; - output[s++] = (uint16_t)rs; + output[s++] = ls; + output[s++] = rs; } } @@ -106,7 +106,7 @@ extern "C" { } void YM_stream_update(uint16_t* output, uint32_t numsamples) { - opm_iface.generate(output, numsamples); + opm_iface.generate((int16_t*)output, numsamples); } void YM_write_reg(uint8_t reg, uint8_t val) {