OXYGENHUB
◆ main openmsx / src / laserdisc / PioneerLDControl.cc
5.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "PioneerLDControl.hh"
2
3
#include "CacheLine.hh"
4
#include "MSXException.hh"
5
#include "MSXPPI.hh"
6
#include "VDP.hh"
7
#include "serialize.hh"
8
9
namespace openmsx {
10
11
/*
12
 * Laserdisc Control: there are three bits involved here. There are three
13
 * bits/connections involved.
14
 * - EXTACK (from Laserdisc -> MSX) will remain low for a while to acknowledge
15
 *   it has received a command and is executing
16
 * - EXTCONTROL (from MSX -> Laserdisc) one bit information which is used for
17
 *   sending commands
18
 * - PULSE (internal to MSX) 8175.5hz signal which used by software to
19
 *   create the right pulses for communicating with the Laserdisc over
20
 *   EXTCONTROL.
21
 *
22
 * Sound Muting: left and right audio channels from Laserdisc input can
23
 * be muted independently. After reset or startup both channels are muted.
24
 * The left muting is controlled by bit 7 of 0x7fff; set is not muted,
25
 * cleared is muted. If this bit changed from 0->1 (rising edge triggered
26
 * and inverted) then bit 4 of register C of PPI switches L muting; set
27
 * for muting disabled, clear for muting enabled.
28
 *
29
 * Cassette Input: If the motor relay is OFF then audio on the R channel
30
 * ends up in the PSG (regardless of muting); if the motor relay is ON
31
 * then normal tape input is used.
32
 */
33
PioneerLDControl::PioneerLDControl(DeviceConfig& config)
34
	: MSXDevice(config)
35
	, rom(getName() + " ROM", "rom", config)
36
	, irq(getMotherBoard(), "PioneerLDControl.IRQdisplayoff")
37
{
38
	if (config.getChildDataAsBool("laserdisc", true)) {
39
		laserdisc.emplace(getHardwareConfig(), *this);
40
	}
41
}
42
43
void PioneerLDControl::init()
44
{
45
	MSXDevice::init();
46
47
	const auto& refs = getReferences();
48
	ppi = !refs.empty() ? dynamic_cast<MSXPPI*>(refs[0]) : nullptr;
49
	if (!ppi) {
50
		throw MSXException("Invalid PioneerLDControl configuration: "
51
		                   "need reference to PPI device.");
52
	}
53
54
	vdp = refs.size() == 2 ? dynamic_cast<VDP*>(refs[1]) : nullptr;
55
	if (!vdp) {
56
		throw MSXException("Invalid PioneerLDControl configuration: "
57
		                   "need reference to VDP device.");
58
	}
59
	reset(getCurrentTime());
60
}
61
62
PioneerLDControl::~PioneerLDControl() = default;
63
64
void PioneerLDControl::reset(EmuTime time)
65
{
66
	muteL = muteR = true;
67
	superimposing = false;
68
	extInt = false;
69
70
	irq.reset();
71
	if (laserdisc) laserdisc->setMuting(muteL, muteR, time);
72
}
73
74
byte PioneerLDControl::readMem(uint16_t address, EmuTime time)
75
{
76
	byte val = PioneerLDControl::peekMem(address, time);
77
	if (address == 0x7fff) {
78
		extInt = false;
79
		irq.reset();
80
	}
81
	return val;
82
}
83
84
byte PioneerLDControl::peekMem(uint16_t address, EmuTime time) const
85
{
86
	byte val = 0xff;
87
88
	if (address == 0x7fff) {
89
		if (videoEnabled) {
90
			val &= 0x7f;
91
		}
92
		if (!extInt) {
93
			val &= 0xfe;
94
		}
95
	} else if (address == 0x7ffe) {
96
		if (clock.getTicksTill(time) & 1) {
97
			val &= 0xfe;
98
		}
99
		if (laserdisc && laserdisc->extAck(time)) {
100
			val &= 0x7f;
101
		}
102
	} else if (0x4000 <= address && address < 0x6000) {
103
		val = rom[address & 0x1fff];
104
	}
105
	return val;
106
}
107
108
const byte* PioneerLDControl::getReadCacheLine(uint16_t address) const
109
{
110
	if ((address & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) {
111
		return nullptr;
112
	} else if (0x4000 <= address && address < 0x6000) {
113
		return &rom[address & 0x1fff];
114
	} else {
115
		return unmappedRead.data();
116
	}
117
}
118
119
void PioneerLDControl::writeMem(uint16_t address, byte value, EmuTime time)
120
{
121
	if (address == 0x7fff) {
122
		// superimpose
123
		superimposing = !(value & 1);
124
		irq.set(superimposing && extInt);
125
126
		updateVideoSource();
127
128
		// Muting
129
		if (!muteL && !(value & 0x80)) {
130
			muteR = !(ppi->peekIO(2, time) & 0x10);
131
		}
132
		muteL = !(value & 0x80);
133
		if (laserdisc) laserdisc->setMuting(muteL, muteR, time);
134
135
	} else if (address == 0x7ffe) {
136
		if (laserdisc) laserdisc->extControl(value & 1, time);
137
	}
138
}
139
140
byte* PioneerLDControl::getWriteCacheLine(uint16_t address)
141
{
142
	if ((address & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) {
143
		return nullptr;
144
	} else {
145
		return unmappedWrite.data();
146
	}
147
}
148
149
void PioneerLDControl::videoIn(bool enabled)
150
{
151
	if (videoEnabled && !enabled) {
152
		// raise an interrupt when external video goes off
153
		extInt = true;
154
		if (superimposing) irq.set();
155
	}
156
	videoEnabled = enabled;
157
	updateVideoSource();
158
}
159
160
void PioneerLDControl::updateVideoSource()
161
{
162
	const auto* videoSource = (videoEnabled && superimposing && laserdisc)
163
	                        ? laserdisc->getRawFrame()
164
	                        : nullptr;
165
	vdp->setExternalVideoSource(videoSource);
166
}
167
168
template<typename Archive>
169
void PioneerLDControl::serialize(Archive& ar, unsigned /*version*/)
170
{
171
	ar.serialize("clock", clock,
172
	             "mutel", muteL,
173
	             "muter", muteR);
174
	// videoEnabled is restored from LaserdiscPlayer. Set to false
175
	// for now so that the irq does not get changed during load
176
	if constexpr (Archive::IS_LOADER) {
177
		videoEnabled = false;
178
	}
179
	ar.serialize("superimposing", superimposing,
180
	             "extint",        extInt,
181
	             "irq",           irq);
182
	if (laserdisc) ar.serialize("laserdisc", *laserdisc);
183
184
	if constexpr (Archive::IS_LOADER) {
185
		updateVideoSource();
186
		if (laserdisc) {
187
			laserdisc->setMuting(muteL, muteR, getCurrentTime());
188
		}
189
	}
190
}
191
192
REGISTER_MSXDEVICE(PioneerLDControl, "PBASIC");
193
INSTANTIATE_SERIALIZE_METHODS(PioneerLDControl);
194
195
} // namespace openmsx