diff --git a/src/main.c b/src/main.c
index 7b29bcc2..e47d972a 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 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..1838433f 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("YM2151 write received while busy.\n");
+ }
+ }
+
+ void generate(int16_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++] = ls;
+ output[s++] = 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((int16_t*)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
}