Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio: implement YM2151 timers and IRQ support, add open bus behavior to IO space #143

Merged
merged 3 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 [<address>]\n");
printf("\tPrint instruction trace. Optionally, a trigger address\n");
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
Expand Down
9 changes: 6 additions & 3 deletions src/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
118 changes: 98 additions & 20 deletions src/ymglue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,98 @@
#include "ymfm_opm.h"
#include <cstdint>

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" {
Expand All @@ -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();
}
}
1 change: 1 addition & 0 deletions src/ymglue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down