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