openMSX
MSXPSG.cc
Go to the documentation of this file.
1 #include "MSXPSG.hh"
2 #include "LedStatus.hh"
3 #include "CassettePort.hh"
4 #include "MSXMotherBoard.hh"
5 #include "JoystickPort.hh"
6 #include "RenShaTurbo.hh"
7 #include "StringOp.hh"
8 #include "serialize.hh"
9 #include "checked_cast.hh"
10 #include "stl.hh"
11 
12 namespace openmsx {
13 
14 [[nodiscard]] static byte getKeyboardLayout(const MSXPSG& psg)
15 {
16  // As many (mostly European) configs do not specify the layout
17  // in the PSG config, assume 50on for these cases.
18  auto value = psg.getDeviceConfig().getChildData("keyboardlayout", "50on");
19  StringOp::casecmp cmp; // case-insensitive
20  if (cmp(value, "50on")) {
21  return 0x00;
22  } else if (cmp(value, "jis")) {
23  return 0x40;
24  }
25  throw MSXException(
26  "Illegal keyboard layout configuration in '", psg.getName(),
27  "' device configuration: '", value,
28  "', expected 'jis' or '50on'.");
29 }
30 
31 // MSXDevice
33  : MSXDevice(config)
34  , cassette(getMotherBoard().getCassettePort())
35  , renShaTurbo(getMotherBoard().getRenShaTurbo())
36  , ports(generate_array<2>([&](auto i) { return &getMotherBoard().getJoystickPort(unsigned(i)); }))
37  , selectedPort(0)
38  , prev(255)
39  , keyLayout(getKeyboardLayout(*this))
40  , addressMask(config.getChildDataAsBool("mirrored_registers", true) ? 0x0f : 0xff)
41  , ay8910("PSG", *this, config, getCurrentTime())
42 {
43  reset(getCurrentTime());
44 }
45 
47 {
48  powerDown(EmuTime::dummy());
49 }
50 
51 void MSXPSG::reset(EmuTime::param time)
52 {
53  registerLatch = 0;
54  ay8910.reset(time);
55 }
56 
57 void MSXPSG::powerDown(EmuTime::param /*time*/)
58 {
60 }
61 
62 byte MSXPSG::readIO(word /*port*/, EmuTime::param time)
63 {
64  return ay8910.readRegister(registerLatch, time);
65 }
66 
67 byte MSXPSG::peekIO(word /*port*/, EmuTime::param time) const
68 {
69  return ay8910.peekRegister(registerLatch, time);
70 }
71 
72 void MSXPSG::writeIO(word port, byte value, EmuTime::param time)
73 {
74  switch (port & 0x03) {
75  case 0:
76  registerLatch = value & addressMask;
77  break;
78  case 1:
79  ay8910.writeRegister(registerLatch, value, time);
80  break;
81  }
82 }
83 
84 
85 // AY8910Periphery
86 byte MSXPSG::readA(EmuTime::param time)
87 {
88  byte joystick = ports[selectedPort]->read(time) |
89  ((renShaTurbo.getSignal(time)) ? 0x10 : 0x00);
90 
91  // pin 6,7 input is ANDed with pin 6,7 output
92  byte pin67 = prev << (4 - 2 * selectedPort);
93  joystick &= (pin67| 0xCF);
94 
95  byte cassetteInput = cassette.cassetteIn(time) ? 0x80 : 0x00;
96  return joystick | keyLayout | cassetteInput;
97 }
98 
99 void MSXPSG::writeB(byte value, EmuTime::param time)
100 {
101  byte val0 = (value & 0x03) | ((value & 0x10) >> 2);
102  byte val1 = ((value & 0x0C) >> 2) | ((value & 0x20) >> 3);
103  ports[0]->write(val0, time);
104  ports[1]->write(val1, time);
105  selectedPort = (value & 0x40) >> 6;
106 
107  if ((prev ^ value) & 0x80) {
108  getLedStatus().setLed(LedStatus::KANA, !(value & 0x80));
109  }
110  prev = value;
111 }
112 
113 // version 1: initial version
114 // version 2: joystickportA/B moved from here to MSXMotherBoard
115 template<typename Archive>
116 void MSXPSG::serialize(Archive& ar, unsigned version)
117 {
118  ar.template serializeBase<MSXDevice>(*this);
119  ar.serialize("ay8910", ay8910);
120  if (ar.versionBelow(version, 2)) {
121  assert(Archive::IS_LOADER);
122  // in older versions there were always 2 real joystick ports
123  ar.serialize("joystickportA", *checked_cast<JoystickPort*>(ports[0]),
124  "joystickportB", *checked_cast<JoystickPort*>(ports[1]));
125  }
126  ar.serialize("registerLatch", registerLatch);
127  byte portB = prev;
128  ar.serialize("portB", portB);
129  if constexpr (Archive::IS_LOADER) {
130  writeB(portB, getCurrentTime());
131  }
132  // selectedPort is derived from portB
133 }
136 
137 } // namespace openmsx
void reset(EmuTime::param time)
Definition: AY8910.cc:525
byte readRegister(unsigned reg, EmuTime::param time)
Definition: AY8910.cc:538
byte peekRegister(unsigned reg, EmuTime::param time) const
Definition: AY8910.cc:563
void writeRegister(unsigned reg, byte value, EmuTime::param time)
Definition: AY8910.cc:582
virtual bool cassetteIn(EmuTime::param time)=0
Reads one bit from the cassette port.
void setLed(Led led, bool status)
Definition: LedStatus.cc:40
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition: MSXDevice.hh:33
MSXMotherBoard & getMotherBoard() const
Get the mother board this device belongs to.
Definition: MSXDevice.cc:71
LedStatus & getLedStatus() const
Definition: MSXDevice.cc:154
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:126
JoystickPortIf & getJoystickPort(unsigned port)
MSXPSG(const DeviceConfig &config)
Definition: MSXPSG.cc:32
void powerDown(EmuTime::param time) override
This method is called when MSX is powered down.
Definition: MSXPSG.cc:57
~MSXPSG() override
Definition: MSXPSG.cc:46
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
Definition: MSXPSG.cc:72
void serialize(Archive &ar, unsigned version)
Definition: MSXPSG.cc:116
void reset(EmuTime::param time) override
This method is called on reset.
Definition: MSXPSG.cc:51
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition: MSXPSG.cc:62
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition: MSXPSG.cc:67
bool getSignal(EmuTime::param time)
Get the output signal in negative logic.
Definition: RenShaTurbo.cc:23
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