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 {
68 }
69 
71 {
72  mutel = muter = true;
73  superimposing = false;
74  extint = false;
75 
76  irq.reset();
77  if (laserdisc) laserdisc->setMuting(mutel, muter, time);
78 }
79 
81 {
82  byte val = PioneerLDControl::peekMem(address, time);
83  if (address == 0x7fff) {
84  extint = false;
85  irq.reset();
86  }
87  return val;
88 }
89 
91 {
92  byte val = 0xff;
93 
94  if (address == 0x7fff) {
95  if (videoEnabled) {
96  val &= 0x7f;
97  }
98  if (!extint) {
99  val &= 0xfe;
100  }
101  } else if (address == 0x7ffe) {
102  if (clock.getTicksTill(time) & 1) {
103  val &= 0xfe;
104  }
105  if (laserdisc && laserdisc->extAck(time)) {
106  val &= 0x7f;
107  }
108  } else if (0x4000 <= address && address < 0x6000) {
109  val = rom[address & 0x1fff];
110  }
111  return val;
112 }
113 
115 {
116  if ((start & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) {
117  return nullptr;
118  } else if (0x4000 <= start && start < 0x6000) {
119  return &rom[start & 0x1fff];
120  } else {
121  return unmappedRead;
122  }
123 }
124 
126 {
127  if (address == 0x7fff) {
128  // superimpose
129  superimposing = !(value & 1);
130  irq.set(superimposing && extint);
131 
132  updateVideoSource();
133 
134  // Muting
135  if (!mutel && !(value & 0x80)) {
136  muter = !(ppi->peekIO(2, time) & 0x10);
137  }
138  mutel = !(value & 0x80);
139  if (laserdisc) laserdisc->setMuting(mutel, muter, time);
140 
141  } else if (address == 0x7ffe) {
142  if (laserdisc) laserdisc->extControl(value & 1, time);
143  }
144 }
145 
147 {
148  if ((start & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) {
149  return nullptr;
150  } else {
151  return unmappedWrite;
152  }
153 }
154 
155 void PioneerLDControl::videoIn(bool enabled)
156 {
157  if (videoEnabled && !enabled) {
158  // raise an interrupt when external video goes off
159  extint = true;
160  if (superimposing) irq.set();
161  }
162  videoEnabled = enabled;
163  updateVideoSource();
164 }
165 
166 void PioneerLDControl::updateVideoSource()
167 {
168  auto* videoSource = (videoEnabled && superimposing && laserdisc)
169  ? laserdisc->getRawFrame()
170  : nullptr;
171  vdp->setExternalVideoSource(videoSource);
172 }
173 
174 template<typename Archive>
175 void PioneerLDControl::serialize(Archive& ar, unsigned /*version*/)
176 {
177  ar.serialize("clock", clock);
178  ar.serialize("mutel", mutel);
179  ar.serialize("muter", muter);
180  // videoEnabled is restored from LaserdiscPlayer. Set to false
181  // for now so that the irq does not get changed during load
182  if (ar.isLoader()) {
183  videoEnabled = false;
184  }
185  ar.serialize("superimposing", superimposing);
186  ar.serialize("extint", extint);
187  ar.serialize("irq", irq);
188  if (laserdisc) ar.serialize("laserdisc", *laserdisc);
189 
190  if (ar.isLoader()) {
191  updateVideoSource();
192  if (laserdisc) {
193  laserdisc->setMuting(mutel, muter, getCurrentTime());
194  }
195  }
196 }
197 
200 
201 } // 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:52
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:130
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:136
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...