OXYGENHUB
◆ main x16-emulator / src / ymglue.cpp
2.8 KiB · C++ · last changed chore(): initial commit 15 days ago source blame history raw
1
#include "ymglue.h"
2
#include "ymfm_opm.h"
3
#include <cstdint>
4
5
class ym2151_interface : public ymfm::ymfm_interface {
6
	public:
7
		ym2151_interface():
8
			m_chip(*this),
9
			m_timers{0, 0},
10
			m_busy_timer{ 0 },
11
			m_irq_status{ false }
12
		{ }
13
		~ym2151_interface() { }
14
15
		virtual void ymfm_sync_mode_write(uint8_t data) override {
16
			m_engine->engine_mode_write(data);
17
		}
18
19
		virtual void ymfm_sync_check_interrupts() override {
20
			m_engine->engine_check_interrupts();
21
		}
22
23
		virtual void ymfm_set_timer(uint32_t tnum, int32_t duration_in_clocks) override {
24
			if (tnum >= 2) return;
25
			m_timers[tnum] = duration_in_clocks;
26
		}
27
28
		virtual void ymfm_set_busy_end(uint32_t clocks) override {
29
			m_busy_timer = clocks;
30
		}
31
32
		virtual bool ymfm_is_busy() override {
33
			return m_busy_timer > 0;
34
		}
35
36
		virtual void ymfm_update_irq(bool asserted) override {
37
			m_irq_status = asserted;
38
		}
39
40
		void update_clocks(int cycles) {
41
			m_busy_timer = std::max(0, m_busy_timer - (64 * cycles));
42
			for (int i = 0; i < 2; ++i) {
43
				if (m_timers[i] > 0) {
44
					m_timers[i] = std::max(0, m_timers[i] - (64 * cycles));
45
					if (m_timers[i] <= 0) {
46
						m_engine->engine_timer_expired(i);
47
					}
48
				}
49
			}	
50
		}
51
52
		void write(uint8_t addr, uint8_t value) {
53
			if (!ymfm_is_busy()) {
54
				m_chip.write_address(addr);
55
				m_chip.write_data(value);
56
			} else {
57
				printf("YM2151 write received while busy.\n");
58
			}
59
		}
60
61
		void generate(int16_t* output, uint32_t numsamples) {
62
			int s = 0;
63
			int ls, rs;
64
			update_clocks(numsamples);
65
			for (uint32_t i = 0; i < numsamples; i++) {
66
				m_chip.generate(&opm_out);
67
				ls = opm_out.data[0];
68
				rs = opm_out.data[1];
69
				if (ls < -32768) ls = -32768;
70
				if (ls > 32767) ls = 32767;
71
				if (rs < -32768) rs = -32768;
72
				if (rs > 32767) rs = 32767;
73
				output[s++] = ls;
74
				output[s++] = rs;
75
			}
76
		}
77
78
		uint8_t read_status() {
79
			return m_chip.read_status();
80
		}
81
82
		bool irq() {
83
			return m_irq_status;
84
		}
85
86
	private:
87
		ymfm::ym2151 m_chip;
88
		int32_t m_timers[2];
89
		int32_t m_busy_timer;
90
		bool m_irq_status;
91
92
		ymfm::ym2151::output_data opm_out;
93
};
94
95
namespace {
96
	ym2151_interface opm_iface;
97
	bool initialized = false;
98
}
99
100
extern "C" {
101
	void YM_Create(int clock) {
102
		// clock is fixed at 3.579545MHz
103
	}
104
105
	void YM_init(int sample_rate, int frame_rate) {
106
		// args are ignored
107
		initialized = true;
108
	}
109
110
	void YM_stream_update(uint16_t* output, uint32_t numsamples) {
111
		if (initialized) opm_iface.generate((int16_t*)output, numsamples);
112
	}
113
114
	void YM_write_reg(uint8_t reg, uint8_t val) {
115
		if (initialized) opm_iface.write(reg, val);
116
	}
117
118
	uint8_t YM_read_status() {
119
		if (initialized)
120
			return opm_iface.read_status();
121
		else
122
			return 0x00; // prevent programs that wait for the busy flag to clear from locking up (emulator compromise)
123
	}
124
125
	bool YM_irq() {
126
		if (initialized)
127
			return opm_iface.irq();
128
		else
129
			return false;
130
	}
131
}