OXYGENHUB
◆ main openmsx / src / sound / YMF278.hh
4.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef YMF278_HH
2
#define YMF278_HH
3
4
#include "ResampledSoundDevice.hh"
5
6
#include "EmuTime.hh"
7
#include "Rom.hh"
8
#include "SimpleDebuggable.hh"
9
#include "TrackedRam.hh"
10
#include "serialize_meta.hh"
11
12
#include "optional_fixed_span.hh"
13
14
#include <array>
15
#include <cstdint>
16
#include <functional>
17
#include <span>
18
#include <string>
19
20
namespace openmsx {
21
22
class DeviceConfig;
23
24
class YMF278 final : public ResampledSoundDevice
25
{
26
public:
27
	static constexpr auto k128 = size_t(128 * 1024); // 128kB
28
	using Block128 = optional_fixed_span<const uint8_t, k128>; // a 128kB read-only memory block
29
30
	using SetupMemPtrFunc = std::function<void(
31
		bool /*mode0*/,
32
		std::span<const uint8_t> /*rom*/,
33
		std::span<const uint8_t> /*ram*/,
34
		std::span<YMF278::Block128, 32> /*memPtrs*/)>;
35
36
public:
37
	YMF278(const std::string& name, size_t ramSize, DeviceConfig& config,
38
	       SetupMemPtrFunc setupMemPtrs_);
39
	~YMF278();
40
	void clearRam();
41
	void reset(EmuTime time);
42
	void writeReg(uint8_t reg, uint8_t data, EmuTime time);
43
	[[nodiscard]] uint8_t readReg(uint8_t reg);
44
	[[nodiscard]] uint8_t peekReg(uint8_t reg) const;
45
46
	[[nodiscard]] uint8_t readMem(unsigned address) const;
47
	void writeMem(unsigned address, uint8_t value);
48
49
	void setMixLevel(uint8_t x, EmuTime time);
50
51
	void setupMemoryPointers();
52
53
	template<typename Archive>
54
	void serialize(Archive& ar, unsigned version);
55
56
private:
57
	class Slot {
58
	public:
59
		Slot();
60
		void reset();
61
		[[nodiscard]] uint8_t compute_rate(int val) const;
62
		[[nodiscard]] uint8_t compute_decay_rate(int val) const;
63
		[[nodiscard]] unsigned decay_rate(int num, int sample_rate);
64
		void envelope_next(int sample_rate);
65
		[[nodiscard]] int16_t compute_vib() const;
66
		[[nodiscard]] uint16_t compute_am() const;
67
68
		template<typename Archive>
69
		void serialize(Archive& ar, unsigned version);
70
71
		uint32_t startAddr;
72
		uint16_t loopAddr;
73
		uint16_t endAddr; // Note: stored in 2s complement (0x0000 = 0, 0x0001 = -65536, 0xffff = -1)
74
		uint32_t step;       // fixed-point frequency step
75
				     // invariant: step == calcStep(OCT, FN)
76
		uint32_t stepPtr;    // fixed-point pointer into the sample
77
		uint16_t pos;
78
79
		int16_t env_vol;
80
81
		uint32_t lfo_cnt;
82
83
		int16_t DL;
84
		uint16_t wave;		// wave table number
85
		uint16_t FN;		// f-number         TODO store 'FN | 1024'?
86
		int8_t OCT;		// octave [-8..+7]
87
		bool PRVB;		// pseudo-reverb
88
		uint8_t TLdest;		// destination total level
89
		uint8_t TL;		// total level  (goes towards TLdest)
90
		uint8_t pan;		// pan-pot 0..15
91
		bool keyon;		// slot keyed on
92
		bool DAMP;
93
		uint8_t lfo;            // LFO speed 0..7
94
		uint8_t vib;		// vibrato 0..7
95
		uint8_t AM;		// AM level 0..7
96
		uint8_t AR;		// 0..15
97
		uint8_t D1R;		// 0..15
98
		uint8_t D2R;		// 0..15
99
		uint8_t RC;		// rate correction 0..15
100
		uint8_t RR;		// 0..15
101
102
		uint8_t bits;		// width of the samples
103
104
		uint8_t state;		// envelope generator state
105
		bool lfo_active;
106
	};
107
108
	// SoundDevice
109
	void generateChannels(std::span<float*> bufs, unsigned num) override;
110
111
	void writeRegDirect(uint8_t reg, uint8_t data, EmuTime time);
112
	[[nodiscard]] unsigned getRamAddress(unsigned addr) const;
113
	[[nodiscard]] int16_t getSample(const Slot& slot, uint16_t pos) const;
114
	[[nodiscard]] static uint16_t nextPos(const Slot& slot, uint16_t pos, uint16_t increment);
115
	void advance();
116
	[[nodiscard]] bool anyActive();
117
	void keyOnHelper(Slot& slot) const;
118
119
	MSXMotherBoard& motherBoard;
120
121
	struct DebugRegisters final : SimpleDebuggable {
122
		DebugRegisters(MSXMotherBoard& motherBoard, const std::string& name);
123
		[[nodiscard]] uint8_t read(unsigned address) override;
124
		void write(unsigned address, uint8_t value, EmuTime time) override;
125
	} debugRegisters;
126
127
	struct DebugMemory final : SimpleDebuggable {
128
		DebugMemory(MSXMotherBoard& motherBoard, const std::string& name);
129
		[[nodiscard]] uint8_t read(unsigned address) override;
130
		void write(unsigned address, uint8_t value) override;
131
	} debugMemory;
132
133
	std::array<Slot, 24> slots;
134
135
	/** Global envelope generator counter. */
136
	unsigned eg_cnt;
137
138
	int memAdr;
139
140
	Rom rom;
141
	TrackedRam ram;
142
143
	std::array<uint8_t, 256> regs;
144
145
	// To speed-up memory access we divide the 4MB address-space in 32 blocks of 128kB.
146
	// For each block we point to the corresponding region inside 'rom' or 'ram',
147
	// or 'nullBlock' means this region is unmapped.
148
	// Note: we can only _read_ via these pointers, not write.
149
	std::array<Block128, 32> memPtrs;
150
151
	// callback-function that fills in the above array
152
	SetupMemPtrFunc setupMemPtrs;
153
};
154
SERIALIZE_CLASS_VERSION(YMF278::Slot, 6);
155
SERIALIZE_CLASS_VERSION(YMF278, 4);
156
157
} // namespace openmsx
158
159
#endif