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