openMSX
PioneerLDControl.cc
Go to the documentation of this file.
1 #include "PioneerLDControl.hh"
2 #include "CacheLine.hh"
3 #include "serialize.hh"
4 #include "LaserdiscPlayer.hh"
5 #include "MSXPPI.hh"
6 #include "MSXException.hh"
7 #include "VDP.hh"
8 #include "memory.hh"
9 
10 namespace openmsx {
11 
12 /*
13  * Laserdisc Control: there are three bits involved here. There are three
14  * bits/connections involved.
15  * - EXTACK (from Laserdisc -> MSX) will remain low for a while to acknowledge
16  * it has received a command and is executing
17  * - EXTCONTROL (from MSX -> Laserdisc) one bit information which is used for
18  * sending commands
19  * - PULSE (internal to MSX) 8175.5hz signal which used by software to
20  * create the right pulses for communicating with the Laserdisc over
21  * EXTCONTROL.
22  *
23  * Sound Muting: left and right audio channels from Laserdisc input can
24  * be muted independently. After reset or startup both channels are muted.
25  * The left muting is controlled by bit 7 of 0x7fff; set is not muted,
26  * cleared is muted. If this bit changed from 0->1 (rising edge triggered
27  * and inverted) then bit 4 of register C of PPI switches L muting; set
28  * for muting disabled, clear for muting enabled.
29  *
30  * Cassette Input: If the motor relay is OFF then audio on the R channel
31  * ends up in the PSG (regardless of muting); if the motor relay is ON
32  * then normal tape input is used.
33  */
35  : MSXDevice(config)
36  , rom(getName() + " ROM", "rom", config)
37  , clock(EmuTime::zero)
38  , irq(getMotherBoard(), "PioneerLDControl.IRQdisplayoff")
39  , videoEnabled(false)
40 {
41  if (config.getChildDataAsBool("laserdisc", true)) {
42  laserdisc = make_unique<LaserdiscPlayer>(
43  getHardwareConfig(), *this);
44  }
46 }
47 
49 {
51 
52  const auto& refs = getReferences();
53  ppi = refs.size() >= 1 ? dynamic_cast<MSXPPI*>(refs[0]) : nullptr;
54  if (!ppi) {
55  throw MSXException("Invalid PioneerLDControl configuration: "
56  "need reference to PPI device.");
57  }
58 
59  vdp = refs.size() == 2 ? dynamic_cast<VDP*>(refs[1]) : nullptr;
60  if (!vdp) {
61  throw MSXException("Invalid PioneerLDControl configuration: "
62  "need reference to VDP device.");
63  }
64 }
65 
67 
69 {
70  mutel = muter = true;
71  superimposing = false;
72  extint = false;
73 
74  irq.reset();
75  if (laserdisc) laserdisc->setMuting(mutel, muter, time);
76 }
77 
79 {
80  byte val = PioneerLDControl::peekMem(address, time);
81  if (address == 0x7fff) {
82  extint = false;
83  irq.reset();
84  }
85  return val;
86 }
87 
89 {
90  byte val = 0xff;
91 
92  if (address == 0x7fff) {
93  if (videoEnabled) {
94  val &= 0x7f;
95  }
96  if (!extint) {
97  val &= 0xfe;
98  }
99  } else if (address == 0x7ffe) {
100  if (clock.getTicksTill(time) & 1) {
101  val &= 0xfe;
102  }
103  if (laserdisc && laserdisc->extAck(time)) {
104  val &= 0x7f;
105  }
106  } else if (0x4000 <= address && address < 0x6000) {
107  val = rom[address & 0x1fff];
108  }
109  return val;
110 }
111 
113 {
114  if ((start & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) {
115  return nullptr;
116  } else if (0x4000 <= start && start < 0x6000) {
117  return &rom[start & 0x1fff];
118  } else {
119  return unmappedRead;
120  }
121 }
122 
124 {
125  if (address == 0x7fff) {
126  // superimpose
127  superimposing = !(value & 1);
128  irq.set(superimposing && extint);
129 
130  updateVideoSource();
131 
132  // Muting
133  if (!mutel && !(value & 0x80)) {
134  muter = !(ppi->peekIO(2, time) & 0x10);
135  }
136  mutel = !(value & 0x80);
137  if (laserdisc) laserdisc->setMuting(mutel, muter, time);
138 
139  } else if (address == 0x7ffe) {
140  if (laserdisc) laserdisc->extControl(value & 1, time);
141  }
142 }
143 
145 {
146  if ((start & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) {
147  return nullptr;
148  } else {
149  return unmappedWrite;
150  }
151 }
152 
153 void PioneerLDControl::videoIn(bool enabled)
154 {
155  if (videoEnabled && !enabled) {
156  // raise an interrupt when external video goes off
157  extint = true;
158  if (superimposing) irq.set();
159  }
160  videoEnabled = enabled;
161  updateVideoSource();
162 }
163 
164 void PioneerLDControl::updateVideoSource()
165 {
166  auto* videoSource = (videoEnabled && superimposing && laserdisc)
167  ? laserdisc->getRawFrame()
168  : nullptr;
169  vdp->setExternalVideoSource(videoSource);
170 }
171 
172 template<typename Archive>
173 void PioneerLDControl::serialize(Archive& ar, unsigned /*version*/)
174 {
175  ar.serialize("clock", clock);
176  ar.serialize("mutel", mutel);
177  ar.serialize("muter", muter);
178  // videoEnabled is restored from LaserdiscPlayer. Set to false
179  // for now so that the irq does not get changed during load
180  if (ar.isLoader()) {
181  videoEnabled = false;
182  }
183  ar.serialize("superimposing", superimposing);
184  ar.serialize("extint", extint);
185  ar.serialize("irq", irq);
186  if (laserdisc) ar.serialize("laserdisc", *laserdisc);
187 
188  if (ar.isLoader()) {
189  updateVideoSource();
190  if (laserdisc) {
191  laserdisc->setMuting(mutel, muter, getCurrentTime());
192  }
193  }
194 }
195 
198 
199 } // namespace openmsx
unsigned getTicksTill(EmuTime::param e) const
Calculate the number of ticks for this clock until the given time.
Definition: Clock.hh:58
virtual void init()
Definition: MSXDevice.cc:51
bool getChildDataAsBool(string_ref name, bool defaultValue=false) const
Definition: DeviceConfig.cc:56
void serialize(Archive &ar, unsigned version)
const Devices & getReferences() const
Get the device references that are specified for this device.
Definition: MSXDevice.cc:129
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void writeMem(word address, byte value, EmuTime::param time) override
Write a given byte to a given location at a certain time to this device.
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
byte * getWriteCacheLine(word address) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
PioneerLDControl(const DeviceConfig &config)
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:135
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX...
Definition: MSXDevice.hh:31
void reset()
Reset the interrupt request on the bus.
Definition: IRQHelper.hh:87
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:268
void videoIn(bool enabled)
void set()
Set the interrupt request on the bus.
Definition: IRQHelper.hh:78
Unified implementation of MSX Video Display Processors (VDPs).
Definition: VDP.hh:61
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:840
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition: MSXPPI.cc:72
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:269
const HardwareConfig & getHardwareConfig() const
Returns the hardwareconfig this device belongs to.
Definition: MSXDevice.hh:43
const string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:373
void reset(EmuTime::param time) override
This method is called on reset.
void setExternalVideoSource(const RawFrame *externalSource)
Enable superimposing.
Definition: VDP.hh:498
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...