OXYGENHUB
◆ main openmsx / src / sound / MSXPSG.cc
3.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "MSXPSG.hh"
2
3
#include "CassettePort.hh"
4
#include "JoystickPort.hh"
5
#include "LedStatus.hh"
6
#include "MSXMotherBoard.hh"
7
#include "RenShaTurbo.hh"
8
#include "serialize.hh"
9
10
#include "StringOp.hh"
11
#include "checked_cast.hh"
12
#include "stl.hh"
13
14
namespace openmsx {
15
16
[[nodiscard]] static byte getKeyboardLayout(const MSXPSG& psg)
17
{
18
	// As many (mostly European) configs do not specify the layout
19
	// in the PSG config, assume 50on for these cases.
20
	auto value = psg.getDeviceConfig().getChildData("keyboardlayout", "50on");
21
	StringOp::casecmp cmp; // case-insensitive
22
	if (cmp(value, "50on")) {
23
		return 0x00;
24
	} else if (cmp(value, "jis")) {
25
		return 0x40;
26
	}
27
	throw MSXException(
28
		"Illegal keyboard layout configuration in '", psg.getName(),
29
	        "' device configuration: '", value,
30
		"', expected 'jis' or '50on'.");
31
}
32
33
// MSXDevice
34
MSXPSG::MSXPSG(const DeviceConfig& config)
35
	: MSXDevice(config)
36
	, cassette(getMotherBoard().getCassettePort())
37
	, renShaTurbo(getMotherBoard().getRenShaTurbo())
38
	, ports(generate_array<2>([&](auto i) { return &getMotherBoard().getJoystickPort(unsigned(i)); }))
39
	, keyLayout(getKeyboardLayout(*this))
40
	, addressMask(config.getChildDataAsBool("mirrored_registers", false) ? 0x0f : 0xff)
41
	, ay8910(getName(), *this, config, getCurrentTime())
42
{
43
	reset(getCurrentTime());
44
}
45
46
MSXPSG::~MSXPSG()
47
{
48
	powerDown(EmuTime::dummy());
49
}
50
51
void MSXPSG::reset(EmuTime time)
52
{
53
	registerLatch = 0;
54
	ay8910.reset(time);
55
}
56
57
void MSXPSG::powerDown(EmuTime /*time*/)
58
{
59
	getLedStatus().setLed(LedStatus::KANA, false);
60
}
61
62
byte MSXPSG::readIO(uint16_t port, EmuTime time)
63
{
64
	switch (port & 0x03) {
65
	case 2:
66
		return ay8910.readRegister(registerLatch, time);
67
	default:
68
		// nothing for 0, 1 and 3
69
		return 0xFF;
70
	}
71
}
72
73
byte MSXPSG::peekIO(uint16_t /*port*/, EmuTime time) const
74
{
75
	return ay8910.peekRegister(registerLatch, time);
76
}
77
78
void MSXPSG::writeIO(uint16_t port, byte value, EmuTime time)
79
{
80
	switch (port & 0x03) {
81
	case 0:
82
		registerLatch = value & addressMask;
83
		break;
84
	case 1:
85
		ay8910.writeRegister(registerLatch, value, time);
86
		break;
87
	}
88
}
89
90
91
// AY8910Periphery
92
byte MSXPSG::readA(EmuTime time)
93
{
94
	byte joystick = ports[selectedPort]->read(time) |
95
	                ((renShaTurbo.getSignal(time)) ? 0x10 : 0x00);
96
97
#if 0
98
	This breaks NinjaTap test programs.
99
	Perhaps this behavior is different between MSX machines with a discrete
100
	AY8910 chip versus machines with the PSG integrated in the MSX-engine?
101
102
	// pin 6,7 input is ANDed with pin 6,7 output
103
	byte pin67 = prev << (4 - 2 * selectedPort);
104
	joystick &= (pin67| 0xCF);
105
#endif
106
107
	byte cassetteInput = cassette.cassetteIn(time) ? 0x80 : 0x00;
108
	return joystick | keyLayout | cassetteInput;
109
}
110
111
void MSXPSG::writeB(byte value, EmuTime time)
112
{
113
	byte val0 =  (value & 0x03)       | ((value & 0x10) >> 2);
114
	byte val1 = ((value & 0x0C) >> 2) | ((value & 0x20) >> 3);
115
	ports[0]->write(val0, time);
116
	ports[1]->write(val1, time);
117
	selectedPort = (value & 0x40) >> 6;
118
119
	if ((prev ^ value) & 0x80) {
120
		getLedStatus().setLed(LedStatus::KANA, !(value & 0x80));
121
	}
122
	prev = value;
123
}
124
125
// version 1: initial version
126
// version 2: joystickportA/B moved from here to MSXMotherBoard
127
template<typename Archive>
128
void MSXPSG::serialize(Archive& ar, unsigned version)
129
{
130
	ar.template serializeBase<MSXDevice>(*this);
131
	ar.serialize("ay8910", ay8910);
132
	if (ar.versionBelow(version, 2)) {
133
		assert(Archive::IS_LOADER);
134
		// in older versions there were always 2 real joystick ports
135
		ar.serialize("joystickportA", *checked_cast<JoystickPort*>(ports[0]),
136
		             "joystickportB", *checked_cast<JoystickPort*>(ports[1]));
137
	}
138
	ar.serialize("registerLatch", registerLatch);
139
	byte portB = prev;
140
	ar.serialize("portB", portB);
141
	if constexpr (Archive::IS_LOADER) {
142
		writeB(portB, getCurrentTime());
143
	}
144
	// selectedPort is derived from portB
145
}
146
INSTANTIATE_SERIALIZE_METHODS(MSXPSG);
147
REGISTER_MSXDEVICE(MSXPSG, "PSG");
148
149
} // namespace openmsx