OXYGENHUB
◆ main openmsx / src / sound / SN76489.hh
3.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#ifndef SN76489_HH
2
#define SN76489_HH
3
4
#include "ResampledSoundDevice.hh"
5
#include "SimpleDebuggable.hh"
6
7
#include <array>
8
9
namespace openmsx {
10
11
/** This class implements the Texas Instruments SN76489 sound chip.
12
  * Unlike the AY-3-8910, this chip only performs sound synthesis.
13
  *
14
  * Resources used:
15
  * - SN76489AN data sheet
16
  *   http://map.grauw.nl/resources/sound/texas_instruments_sn76489an.pdf
17
  * - Developer documentation on SMS Power!
18
  *   http://www.smspower.org/Development/SN76489
19
  * - John Kortink's reverse engineering results
20
  *   http://www.zeridajh.org/articles/various/sn76489/index.htm
21
  * - MAME's implementation
22
  *   https://github.com/mamedev/mame/blob/master/src/devices/sound/sn76496.cpp
23
  * - blueMSX's implementation
24
  *   https://sourceforge.net/p/bluemsx/code/HEAD/tree/trunk/blueMSX/Src/SoundChips/SN76489.c
25
  */
26
class SN76489 final : public ResampledSoundDevice
27
{
28
public:
29
	explicit SN76489(const DeviceConfig& config);
30
	~SN76489();
31
32
	// ResampledSoundDevice
33
	void generateChannels(std::span<float*> buffers, unsigned num) override;
34
35
	void reset(EmuTime time);
36
	void write(uint8_t value, EmuTime time);
37
38
	template<typename Archive>
39
	void serialize(Archive& ar, unsigned version);
40
41
private:
42
	class NoiseShifter {
43
	public:
44
		/** Sets the feedback pattern and resets the shift register to the
45
		  * initial state that matches that pattern.
46
		  */
47
		void initState(unsigned pattern, unsigned period);
48
49
		/** Gets the current output of this shifter: 0 or 1.
50
		  */
51
		[[nodiscard]] unsigned getOutput() const;
52
53
		/** Advances the shift register one step, to the next output.
54
		  */
55
		void advance();
56
57
		/** Records that the shift register should be advanced multiple steps
58
		  * before the next output is used.
59
		  */
60
		void queueAdvance(unsigned steps);
61
62
		/** Advances the shift register by the number of steps that were queued.
63
		  */
64
		void catchUp();
65
66
		template<typename Archive>
67
		void serialize(Archive& ar, unsigned version);
68
69
	private:
70
		unsigned pattern;
71
		unsigned period;
72
		unsigned random;
73
		unsigned stepsBehind;
74
	};
75
76
	/** Initialize registers, counters and other chip state.
77
	  */
78
	void initState();
79
80
	/** Initialize noise shift register according to chip variant and noise
81
	  * mode.
82
	  */
83
	void initNoise();
84
85
	[[nodiscard]] uint16_t peekRegister(unsigned reg, EmuTime time) const;
86
	void writeRegister(unsigned reg, uint16_t value, EmuTime time);
87
	template<bool NOISE> void synthesizeChannel(
88
		float*& buffer, unsigned num, unsigned generator);
89
90
private:
91
	NoiseShifter noiseShifter;
92
93
	/** Register bank. The tone period registers (0, 2, 4) are 12 bits wide,
94
	  * all other registers are 4 bits wide.
95
	  */
96
	std::array<uint16_t, 8> regs;
97
98
	/** The last register written to (0-7).
99
	  */
100
	uint8_t registerLatch;
101
102
	/** Frequency counter for each channel.
103
	  * These count down and when they reach zero, the output flips and the
104
	  * counter is re-loaded with the value of the period register.
105
	  */
106
	std::array<uint16_t, 4> counters;
107
108
	/** Output flip-flop state (0 or 1) for each channel.
109
	  */
110
	std::array<uint8_t, 4> outputs;
111
112
	struct Debuggable final : SimpleDebuggable {
113
		Debuggable(MSXMotherBoard& motherBoard, const std::string& name);
114
		[[nodiscard]] uint8_t read(unsigned address, EmuTime time) override;
115
		void write(unsigned address, uint8_t value, EmuTime time) override;
116
	} debuggable;
117
};
118
119
} // namespace openmsx
120
121
#endif