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& references = getReferences();
53  ppi = references.size() >= 1 ?
54  dynamic_cast<MSXPPI*>(references[0]) : nullptr;
55  if (!ppi) {
56  throw MSXException("Invalid PioneerLDControl configuration: "
57  "need reference to PPI device.");
58  }
59 
60  vdp = references.size() == 2 ?
61  dynamic_cast<VDP*>(references[1]) : nullptr;
62  if (!vdp) {
63  throw MSXException("Invalid PioneerLDControl configuration: "
64  "need reference to VDP device.");
65  }
66 }
67 
69 {
70 }
71 
73 {
74  mutel = muter = true;
75  superimposing = false;
76  extint = false;
77 
78  irq.reset();
79  if (laserdisc) laserdisc->setMuting(mutel, muter, time);
80 }
81 
83 {
84  byte val = PioneerLDControl::peekMem(address, time);
85  if (address == 0x7fff) {
86  extint = false;
87  if (irq.getState()) {
88  irq.reset();
89  }
90  }
91  return val;
92 }
93 
95 {
96  byte val = 0xff;
97 
98  if (address == 0x7fff) {
99  if (videoEnabled) {
100  val &= 0x7f;
101  }
102  if (!extint) {
103  val &= 0xfe;
104  }
105  } else if (address == 0x7ffe) {
106  if (clock.getTicksTill(time) & 1) {
107  val &= 0xfe;
108  }
109  if (laserdisc && laserdisc->extAck(time)) {
110  val &= 0x7f;
111  }
112  } else if (0x4000 <= address && address < 0x6000) {
113  val = rom[address & 0x1fff];
114  }
115  return val;
116 }
117 
119 {
120  if ((start & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) {
121  return nullptr;
122  } else if (0x4000 <= start && start < 0x6000) {
123  return &rom[start & 0x1fff];
124  } else {
125  return unmappedRead;
126  }
127 }
128 
130 {
131  if (address == 0x7fff) {
132  // superimpose
133  superimposing = !(value & 1);
134  if (superimposing) {
135  if (extint && !irq.getState()) {
136  irq.set();
137  }
138  } else if (irq.getState()) {
139  irq.reset();
140  }
141 
142  updateVideoSource();
143 
144  // Muting
145  if (!mutel && !(value & 0x80)) {
146  muter = !(ppi->peekIO(2, time) & 0x10);
147  }
148  mutel = !(value & 0x80);
149  if (laserdisc) laserdisc->setMuting(mutel, muter, time);
150 
151  } else if (address == 0x7ffe) {
152  if (laserdisc) laserdisc->extControl(value & 1, time);
153  }
154 }
155 
157 {
158  if ((start & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) {
159  return nullptr;
160  } else {
161  return unmappedWrite;
162  }
163 }
164 
165 void PioneerLDControl::videoIn(bool enabled)
166 {
167  if (videoEnabled && !enabled) {
168  // raise an interrupt when external video goes off
169  extint = true;
170  if (superimposing) {
171  irq.set();
172  }
173  }
174  videoEnabled = enabled;
175  updateVideoSource();
176 }
177 
178 void PioneerLDControl::updateVideoSource()
179 {
180  auto* videoSource = (videoEnabled && superimposing && laserdisc)
181  ? laserdisc->getRawFrame()
182  : nullptr;
183  vdp->setExternalVideoSource(videoSource);
184 }
185 
186 template<typename Archive>
187 void PioneerLDControl::serialize(Archive& ar, unsigned /*version*/)
188 {
189  ar.serialize("clock", clock);
190  ar.serialize("mutel", mutel);
191  ar.serialize("muter", muter);
192  // videoEnabled is restored from LaserdiscPlayer. Set to false
193  // for now so that the irq does not get changed during load
194  if (ar.isLoader()) {
195  videoEnabled = false;
196  }
197  ar.serialize("superimposing", superimposing);
198  ar.serialize("extint", extint);
199  ar.serialize("irq", irq);
200  if (laserdisc) ar.serialize("laserdisc", *laserdisc);
201 
202  if (ar.isLoader()) {
203  updateVideoSource();
204  if (laserdisc) {
205  laserdisc->setMuting(mutel, muter, getCurrentTime());
206  }
207  }
208 }
209 
212 
213 } // namespace openmsx
virtual void init()
Definition: MSXDevice.cc:52
REGISTER_MSXDEVICE(DebugDevice,"DebugDevice")
void serialize(Archive &ar, unsigned version)
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:136
const HardwareConfig & getHardwareConfig() const
Returns the hardwareconfig this device belongs to.
Definition: MSXDevice.hh:41
bool getChildDataAsBool(string_ref name, bool defaultValue=false) const
Definition: DeviceConfig.cc:56
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.
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.
bool getState() const
Get the interrupt state.
Definition: IRQHelper.hh:95
byte peekMem(word address, EmuTime::param time) const override
Read a byte from a given memory location.
const Devices & getReferences() const
Get the device references that are specified for this device.
Definition: MSXDevice.cc:130
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:32
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:25
void reset()
Reset the interrupt request on the bus.
Definition: IRQHelper.hh:85
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:266
void videoIn(bool enabled)
void set()
Set the interrupt request on the bus.
Definition: IRQHelper.hh:76
Unified implementation of MSX Video Display Processors (VDPs).
Definition: VDP.hh:58
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:802
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:267
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:496
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
unsigned short word
16 bit unsigned integer
Definition: openmsx.hh:28