OXYGENHUB
◆ main openmsx / src / SC3000PPI.cc
4.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "SC3000PPI.hh"
2
3
#include "CassettePort.hh"
4
#include "GlobalSettings.hh"
5
#include "JoystickPort.hh"
6
#include "MSXMotherBoard.hh"
7
#include "Reactor.hh"
8
#include "XMLElement.hh"
9
#include "serialize.hh"
10
11
#include "narrow.hh"
12
13
namespace openmsx {
14
15
// MSXDevice
16
17
SC3000PPI::SC3000PPI(const DeviceConfig& config)
18
	: MSXDevice(config)
19
	, cassettePort(getMotherBoard().getCassettePort())
20
	, i8255(*this, getCurrentTime(), config.getGlobalSettings().getInvalidPpiModeSetting())
21
	, keyboard(
22
		config.getMotherBoard(),
23
		config.getMotherBoard().getScheduler(),
24
		config.getMotherBoard().getCommandController(),
25
		config.getMotherBoard().getReactor().getEventDistributor(),
26
		config.getMotherBoard().getMSXEventDistributor(),
27
		config.getMotherBoard().getStateChangeDistributor(),
28
		Keyboard::Matrix::SEGA, config)
29
{
30
	MSXMotherBoard& motherBoard = getMotherBoard();
31
	auto time = getCurrentTime();
32
33
	for (unsigned i = 0; i < 2; i++) {
34
		ports[i] = &motherBoard.getJoystickPort(i);
35
		// Clear strobe bit, or MSX joysticks will stay silent.
36
		ports[i]->write(0xFB, time);
37
	}
38
39
	reset(time);
40
}
41
42
void SC3000PPI::reset(EmuTime time)
43
{
44
	i8255.reset(time);
45
}
46
47
uint8_t SC3000PPI::readIO(uint16_t port, EmuTime time)
48
{
49
	return i8255.read(port & 0x03, time);
50
}
51
52
uint8_t SC3000PPI::peekIO(uint16_t port, EmuTime time) const
53
{
54
	return i8255.peek(port & 0x03, time);
55
}
56
57
void SC3000PPI::writeIO(uint16_t port, uint8_t value, EmuTime time)
58
{
59
	i8255.write(port & 0x03, value, time);
60
}
61
62
63
// I8255Interface
64
65
uint8_t SC3000PPI::readA(EmuTime time)
66
{
67
	return peekA(time);
68
}
69
uint8_t SC3000PPI::peekA(EmuTime time) const
70
{
71
	if (selectedRow == 7) {
72
		// Joystick hardware cannot detect when it's being read, so using the
73
		// read method for peeking should be safe.
74
		uint8_t joy1 = ports[0]->read(time) & 0x3F;
75
		uint8_t joy2 = ports[1]->read(time) & 0x3F;
76
		return narrow_cast<uint8_t>(joy1 | (joy2 << 6));
77
	} else {
78
		return keyboard.getKeys()[selectedRow];
79
	}
80
}
81
void SC3000PPI::writeA(uint8_t /*value*/, EmuTime /*time*/)
82
{
83
}
84
85
uint8_t SC3000PPI::readB(EmuTime time)
86
{
87
	return peekB(time);
88
}
89
uint8_t SC3000PPI::peekB(EmuTime time) const
90
{
91
	// TODO: Are bits 4-7 available regardless of which keyboard row is selected?
92
	//       That would make sense, but check the schematics.
93
	if (selectedRow == 7) {
94
		// Joystick hardware cannot detect when it's being read, so using the
95
		// read method for peeking should be safe.
96
		uint8_t joy2 = ports[1]->read(time) & 0x3F;
97
		return 0xF0 | (joy2 >> 2);
98
	} else {
99
		/*
100
			Signal  Description
101
102
			PB0     Keyboard input
103
			PB1     Keyboard input
104
			PB2     Keyboard input
105
			PB3     Keyboard input
106
			PB4     /CONT input from cartridge terminal B-11
107
			PB5     FAULT input from printer
108
			PB6     BUSY input from printer
109
			PB7     Cassette tape input
110
		*/
111
		auto keys = keyboard.getKeys()[selectedRow + 7];
112
		return 0xF0 | keys;
113
	}
114
}
115
void SC3000PPI::writeB(uint8_t /*value*/, EmuTime /*time*/)
116
{
117
}
118
119
uint4_t SC3000PPI::readC1(EmuTime time)
120
{
121
	return peekC1(time);
122
}
123
uint4_t SC3000PPI::peekC1(EmuTime /*time*/) const
124
{
125
	// TODO: Check.
126
	return 15;
127
}
128
uint4_t SC3000PPI::readC0(EmuTime time)
129
{
130
	return peekC0(time);
131
}
132
uint4_t SC3000PPI::peekC0(EmuTime /*time*/) const
133
{
134
	// TODO: Is this selection readable at all? And if so, what is the value
135
	//       of bit 3?
136
	return selectedRow;
137
}
138
void SC3000PPI::writeC1(uint4_t value, EmuTime time)
139
{
140
	if ((prevBits ^ value) & 1) {
141
		cassettePort.setMotor((value & 1) == 0, time); // 0=0n, 1=Off
142
	}
143
	if ((prevBits ^ value) & 2) {
144
		cassettePort.cassetteOut((value & 2) != 0, time);
145
	}
146
	//if ((prevBits ^ value) & 4) {
147
	//	cassetteDevice.Mute(); // CASAUD, mute case speaker (1=enable, 0=disable)
148
	//}
149
	//if ((prevBits ^ value) & 8) {
150
	//	click.setClick((value & 8) != 0, time);
151
	//}
152
	prevBits = value;
153
}
154
void SC3000PPI::writeC0(uint4_t value, EmuTime /*time*/)
155
{
156
	selectedRow = value & 7;
157
	//fprintf(stderr, "SC3000PPI: selected row %d\n", selectedRow);
158
}
159
160
template<typename Archive>
161
void SC3000PPI::serialize(Archive& ar, unsigned /*version*/)
162
{
163
	ar.template serializeBase<MSXDevice>(*this);
164
	ar.serialize("i8255", i8255);
165
166
	// merge prevBits and selectedRow into one byte
167
	auto portC = uint8_t((prevBits << 4) | (selectedRow << 0));
168
	ar.serialize("portC", portC);
169
	if constexpr (Archive::IS_LOADER) {
170
		selectedRow = (portC >> 0) & 0xF;
171
		uint4_t bits = (portC >> 4) & 0xF;
172
		writeC1(bits, getCurrentTime());
173
	}
174
	ar.serialize("keyboard", keyboard);
175
}
176
INSTANTIATE_SERIALIZE_METHODS(SC3000PPI);
177
REGISTER_MSXDEVICE(SC3000PPI, "SC-3000 PPI");
178
179
} // namespace openmsx