openMSX
MSXPPI.cc
Go to the documentation of this file.
1 #include "MSXPPI.hh"
2 #include "I8255.hh"
3 #include "Keyboard.hh"
4 #include "LedStatus.hh"
5 #include "MSXCPUInterface.hh"
6 #include "MSXMotherBoard.hh"
7 #include "Reactor.hh"
8 #include "KeyClick.hh"
9 #include "CassettePort.hh"
10 #include "RenShaTurbo.hh"
11 #include "serialize.hh"
12 #include "unreachable.hh"
13 #include "memory.hh"
14 #include <string>
15 
16 namespace openmsx {
17 
18 // MSXDevice
19 
20 static std::unique_ptr<Keyboard> createKeyboard(const DeviceConfig& config)
21 {
22  bool keyGhosting = config.getChildDataAsBool("key_ghosting", true);
23  bool keyGhostingSGCprotected =
24  config.getChildDataAsBool("key_ghosting_sgc_protected", true);
25  string_ref keyboardType = config.getChildData("keyboard_type", "int");
26  bool hasKeypad = config.getChildDataAsBool("has_keypad", true);
27  bool hasYesNoKeys = config.getChildDataAsBool("has_yesno_keys", false);
28  bool codeKanaLocks = config.getChildDataAsBool("code_kana_locks", false);
29  bool graphLocks = config.getChildDataAsBool("graph_locks", false);
30  MSXMotherBoard& motherBoard = config.getMotherBoard();
31  return make_unique<Keyboard>(
32  motherBoard,
33  motherBoard.getScheduler(),
34  motherBoard.getCommandController(),
35  motherBoard.getReactor().getEventDistributor(),
36  motherBoard.getMSXEventDistributor(),
37  motherBoard.getStateChangeDistributor(),
38  keyboardType, hasKeypad, hasYesNoKeys,
39  keyGhosting, keyGhostingSGCprotected,
40  codeKanaLocks, graphLocks);
41 }
42 
44  : MSXDevice(config)
45  , cassettePort(getMotherBoard().getCassettePort())
46  , renshaTurbo(getMotherBoard().getRenShaTurbo())
47  , i8255(make_unique<I8255>(*this, getCurrentTime(), getCliComm()))
48  , click(make_unique<KeyClick>(config))
49  , keyboard(createKeyboard(config))
50  , prevBits(15)
51  , selectedRow(0)
52 {
54 }
55 
57 {
59 }
60 
62 {
63  i8255->reset(time);
64  click->reset(time);
65 }
66 
68 {
70 }
71 
73 {
74  switch (port & 0x03) {
75  case 0:
76  return i8255->readPortA(time);
77  case 1:
78  return i8255->readPortB(time);
79  case 2:
80  return i8255->readPortC(time);
81  case 3:
82  return i8255->readControlPort(time);
83  default: // unreachable, avoid warning
85  return 0;
86  }
87 }
88 
90 {
91  switch (port & 0x03) {
92  case 0:
93  return i8255->peekPortA(time);
94  case 1:
95  return i8255->peekPortB(time);
96  case 2:
97  return i8255->peekPortC(time);
98  case 3:
99  return i8255->readControlPort(time);
100  default: // unreachable, avoid warning
101  UNREACHABLE;
102  return 0;
103  }
104 }
105 
106 void MSXPPI::writeIO(word port, byte value, EmuTime::param time)
107 {
108  switch (port & 0x03) {
109  case 0:
110  i8255->writePortA(value, time);
111  break;
112  case 1:
113  i8255->writePortB(value, time);
114  break;
115  case 2:
116  i8255->writePortC(value, time);
117  break;
118  case 3:
119  i8255->writeControlPort(value, time);
120  break;
121  default:
122  UNREACHABLE;
123  }
124 }
125 
126 
127 // I8255Interface
128 
129 byte MSXPPI::readA(EmuTime::param time)
130 {
131  return peekA(time);
132 }
133 byte MSXPPI::peekA(EmuTime::param /*time*/) const
134 {
135  // port A is normally an output on MSX, reading from an output port
136  // is handled internally in the 8255
137  // TODO check this on a real MSX
138  // TODO returning 0 fixes the 'get_selected_slot' script right after
139  // reset (when PPI directions are not yet set). For now this
140  // solution is good enough.
141  return 0;
142 }
143 void MSXPPI::writeA(byte value, EmuTime::param /*time*/)
144 {
146 }
147 
148 byte MSXPPI::readB(EmuTime::param time)
149 {
150  return peekB(time);
151 }
152 byte MSXPPI::peekB(EmuTime::param time) const
153 {
154  if (selectedRow != 8) {
155  return keyboard->getKeys()[selectedRow];
156  } else {
157  return keyboard->getKeys()[8] | (renshaTurbo.getSignal(time) ? 1:0);
158  }
159 }
160 void MSXPPI::writeB(byte /*value*/, EmuTime::param /*time*/)
161 {
162  // probably nothing happens on a real MSX
163 }
164 
165 nibble MSXPPI::readC1(EmuTime::param time)
166 {
167  return peekC1(time);
168 }
169 nibble MSXPPI::peekC1(EmuTime::param /*time*/) const
170 {
171  return 15; // TODO check this
172 }
173 nibble MSXPPI::readC0(EmuTime::param time)
174 {
175  return peekC0(time);
176 }
177 nibble MSXPPI::peekC0(EmuTime::param /*time*/) const
178 {
179  return 15; // TODO check this
180 }
181 void MSXPPI::writeC1(nibble value, EmuTime::param time)
182 {
183  if ((prevBits ^ value) & 1) {
184  cassettePort.setMotor((value & 1) == 0, time); // 0=0n, 1=Off
185  }
186  if ((prevBits ^ value) & 2) {
187  cassettePort.cassetteOut((value & 2) != 0, time);
188  }
189  if ((prevBits ^ value) & 4) {
190  getLedStatus().setLed(LedStatus::CAPS, (value & 4) == 0);
191  }
192  if ((prevBits ^ value) & 8) {
193  click->setClick((value & 8) != 0, time);
194  }
195  prevBits = value;
196 }
197 void MSXPPI::writeC0(nibble value, EmuTime::param /*time*/)
198 {
199  selectedRow = value;
200 }
201 
202 
203 template<typename Archive>
204 void MSXPPI::serialize(Archive& ar, unsigned /*version*/)
205 {
206  ar.template serializeBase<MSXDevice>(*this);
207  ar.serialize("i8255", *i8255);
208 
209  // merge prevBits and selectedRow into one byte
210  byte portC = (prevBits << 4) | (selectedRow << 0);
211  ar.serialize("portC", portC);
212  if (ar.isLoader()) {
213  selectedRow = (portC >> 0) & 0xF;
214  nibble bits = (portC >> 4) & 0xF;
215  writeC1(bits, getCurrentTime());
216  }
217  ar.serialize("keyboard", *keyboard);
218 }
220 REGISTER_MSXDEVICE(MSXPPI, "PPI");
221 
222 } // namespace openmsx