openMSX
MSXRS232.cc
Go to the documentation of this file.
1 #include "MSXRS232.hh"
2 #include "RS232Device.hh"
3 #include "CacheLine.hh"
4 #include "Ram.hh"
5 #include "Rom.hh"
6 #include "BooleanSetting.hh"
7 #include "MSXException.hh"
8 #include "serialize.hh"
9 #include "memory.hh"
10 #include "outer.hh"
11 #include "unreachable.hh"
12 #include <cassert>
13 
14 namespace openmsx {
15 
16 const unsigned RAM_OFFSET = 0x2000;
17 const unsigned RAM_SIZE = 0x800;
18 
20  : MSXDevice(config)
21  , RS232Connector(MSXDevice::getPluggingController(), "msx-rs232")
22  , i8254(getScheduler(), &cntr0, &cntr1, nullptr, getCurrentTime())
23  , i8251(getScheduler(), interf, getCurrentTime())
24  , rom(config.findChild("rom")
25  ? make_unique<Rom>(
26  MSXDevice::getName() + " ROM", "rom", config)
27  : nullptr) // when the ROM is already mapped, you don't want to specify it again here
28  , ram(config.getChildDataAsBool("ram", false)
29  ? make_unique<Ram>(
30  config, MSXDevice::getName() + " RAM",
31  "RS232 RAM", RAM_SIZE)
32  : nullptr)
33  , rxrdyIRQ(getMotherBoard(), MSXDevice::getName() + ".IRQrxrdy")
34  , rxrdyIRQlatch(false)
35  , rxrdyIRQenabled(false)
36  , hasMemoryBasedIo(config.getChildDataAsBool("memorybasedio", false))
37  , ioAccessEnabled(!hasMemoryBasedIo)
38  , switchSetting(config.getChildDataAsBool("toshiba_rs232c_switch",
39  false) ? make_unique<BooleanSetting>(getCommandController(),
40  "toshiba_rs232c_switch", "status of the RS-232C enable switch",
41  true) : nullptr)
42 {
43  if (rom && rom->getSize() != 0x2000 && rom->getSize() != 0x4000) {
44  throw MSXException("RS232C only supports 8kB or 16kB ROMs.");
45  }
46 
47  EmuDuration total(1.0 / 1.8432e6); // 1.8432MHz
48  EmuDuration hi (1.0 / 3.6864e6); // half clock period
50  i8254.getClockPin(0).setPeriodicState(total, hi, time);
51  i8254.getClockPin(1).setPeriodicState(total, hi, time);
52  i8254.getClockPin(2).setPeriodicState(total, hi, time);
53 
54  powerUp(time);
55 }
56 
57 MSXRS232::~MSXRS232() = default;
58 
60 {
61  if (ram) ram->clear();
62  reset(time);
63 }
64 
66 {
67  rxrdyIRQlatch = false;
68  rxrdyIRQenabled = false;
69  rxrdyIRQ.reset();
70 
71  ioAccessEnabled = !hasMemoryBasedIo;
72 
73  if (ram) ram->clear();
74 }
75 
77 {
78  if (hasMemoryBasedIo && (0xBFF8 <= address) && (address <= 0xBFFF)) {
79  return readIOImpl(address & 0x07, time);
80  }
81  word addr = address & 0x3FFF;
82  if (ram && ((RAM_OFFSET <= addr) && (addr < (RAM_OFFSET + RAM_SIZE)))) {
83  return (*ram)[addr - RAM_OFFSET];
84  } else if (rom && (0x4000 <= address) && (address < 0x8000)) {
85  return (*rom)[addr & (rom->getSize() - 1)];
86  } else {
87  return 0xFF;
88  }
89 }
90 
92 {
93  if (hasMemoryBasedIo && (start == (0xBFF8 & CacheLine::HIGH))) {
94  return nullptr;
95  }
96  word addr = start & 0x3FFF;
97  if (ram && ((RAM_OFFSET <= addr) && (addr < (RAM_OFFSET + RAM_SIZE)))) {
98  return &(*ram)[addr - RAM_OFFSET];
99  } else if (rom && (0x4000 <= start) && (start < 0x8000)) {
100  return &(*rom)[addr & (rom->getSize() - 1)];
101  } else {
102  return unmappedRead;
103  }
104 }
105 
106 void MSXRS232::writeMem(word address, byte value, EmuTime::param time)
107 {
108 
109  if (hasMemoryBasedIo && (0xBFF8 <= address) && (address <= 0xBFFF)) {
110  // when the interface has memory based I/O, the I/O port
111  // based I/O is disabled, but it can be enabled by writing
112  // bit 4 to 0xBFFA. It is disabled again at reset.
113  // Source: Sony HB-G900P and Sony HB-G900AP service manuals.
114  // We assume here you can also disable it by writing 0 to it.
115  if (address == 0xBFFA) {
116  ioAccessEnabled = (value & (1 << 4))!=0;
117  }
118  return writeIOImpl(address & 0x07, value, time);
119  }
120  word addr = address & 0x3FFF;
121  if (ram && ((RAM_OFFSET <= addr) && (addr < (RAM_OFFSET + RAM_SIZE)))) {
122  (*ram)[addr - RAM_OFFSET] = value;
123  }
124 }
125 
127 {
128  if (hasMemoryBasedIo && (start == (0xBFF8 & CacheLine::HIGH))) {
129  return nullptr;
130  }
131  word addr = start & 0x3FFF;
132  if (ram && ((RAM_OFFSET <= addr) && (addr < (RAM_OFFSET + RAM_SIZE)))) {
133  return &(*ram)[addr - RAM_OFFSET];
134  } else {
135  return unmappedWrite;
136  }
137 }
138 
140 {
141  if (ioAccessEnabled) {
142  return readIOImpl(port & 0x07, time);
143  }
144  return 0xFF;
145 }
146 
147 byte MSXRS232::readIOImpl(word port, EmuTime::param time)
148 {
149  byte result;
150  switch (port) {
151  case 0: // UART data register
152  case 1: // UART status register
153  result = i8251.readIO(port, time);
154  break;
155  case 2: // Status sense port
156  result = readStatus(time);
157  break;
158  case 3: // no function
159  result = 0xFF;
160  break;
161  case 4: // counter 0 data port
162  case 5: // counter 1 data port
163  case 6: // counter 2 data port
164  case 7: // timer command register
165  result = i8254.readIO(port - 4, time);
166  break;
167  default:
168  UNREACHABLE; return 0;
169  }
170  return result;
171 }
172 
174 {
175  if (hasMemoryBasedIo && !ioAccessEnabled) return 0xFF;
176  byte result;
177  port &= 0x07;
178  switch (port) {
179  case 0: // UART data register
180  case 1: // UART status register
181  result = i8251.peekIO(port, time);
182  break;
183  case 2: // Status sense port
184  result = 0; // TODO not implemented
185  break;
186  case 3: // no function
187  result = 0xFF;
188  break;
189  case 4: // counter 0 data port
190  case 5: // counter 1 data port
191  case 6: // counter 2 data port
192  case 7: // timer command register
193  result = i8254.peekIO(port - 4, time);
194  break;
195  default:
196  UNREACHABLE; return 0;
197  }
198  return result;
199 }
200 
201 void MSXRS232::writeIO(word port, byte value, EmuTime::param time)
202 {
203  if (ioAccessEnabled) writeIOImpl(port & 0x07, value, time);
204 }
205 
206 void MSXRS232::writeIOImpl(word port, byte value, EmuTime::param time)
207 {
208  switch (port) {
209  case 0: // UART data register
210  case 1: // UART command register
211  i8251.writeIO(port, value, time);
212  break;
213  case 2: // interrupt mask register
214  setIRQMask(value);
215  break;
216  case 3: // no function
217  break;
218  case 4: // counter 0 data port
219  case 5: // counter 1 data port
220  case 6: // counter 2 data port
221  case 7: // timer command register
222  i8254.writeIO(port - 4, value, time);
223  break;
224  }
225 }
226 
227 byte MSXRS232::readStatus(EmuTime::param time)
228 {
229 
230  // Info from http://nocash.emubase.de/portar.htm
231  //
232  // Bit Name Expl.
233  // 0 CD Carrier Detect (0=Active, 1=Not active)
234  // 1 RI Ring Indicator (0=Active, 1=Not active) (N/C in MSX)
235  // 6 Timer Output from i8253 Counter 2
236  // 7 CTS Clear to Send (0=Active, 1=Not active)
237  //
238  // On Toshiba HX-22, see
239  // http://www.msx.org/forum/msx-talk/hardware/toshiba-hx-22?page=3
240  // RetroTechie's post of 20-09-2012, 08:08
241  // ... The "RS-232 interrupt disable" bit can be read back via bit 3
242  // on this I/O port, if CN1 is open. If CN1 is closed, it always
243  // reads back as "0". ...
244 
245  byte result = 0; // TODO check unused bits
246 
247  // TODO bit 0: carrier detect
248 
249  if (!rxrdyIRQenabled && switchSetting && switchSetting->getBoolean()) {
250  result |= 0x08;
251  }
252 
253  if (!interf.getCTS(time)) {
254  result |= 0x80;
255  }
256  if (i8254.getOutputPin(2).getState(time)) {
257  result |= 0x40;
258  }
259  return result;
260 }
261 
262 void MSXRS232::setIRQMask(byte value)
263 {
264  enableRxRDYIRQ(!(value & 1));
265 }
266 
267 void MSXRS232::setRxRDYIRQ(bool status)
268 {
269  if (rxrdyIRQlatch != status) {
270  rxrdyIRQlatch = status;
271  if (rxrdyIRQenabled) {
272  if (rxrdyIRQlatch) {
273  rxrdyIRQ.set();
274  } else {
275  rxrdyIRQ.reset();
276  }
277  }
278  }
279 }
280 
281 void MSXRS232::enableRxRDYIRQ(bool enabled)
282 {
283  if (rxrdyIRQenabled != enabled) {
284  rxrdyIRQenabled = enabled;
285  if (!rxrdyIRQenabled && rxrdyIRQlatch) {
286  rxrdyIRQ.reset();
287  }
288  }
289 }
290 
291 
292 // I8251Interface (pass calls from I8251 to outConnector)
293 
294 void MSXRS232::I8251Interf::setRxRDY(bool status, EmuTime::param /*time*/)
295 {
296  auto& rs232 = OUTER(MSXRS232, interf);
297  rs232.setRxRDYIRQ(status);
298 }
299 
300 void MSXRS232::I8251Interf::setDTR(bool status, EmuTime::param time)
301 {
302  auto& rs232 = OUTER(MSXRS232, interf);
303  rs232.getPluggedRS232Dev().setDTR(status, time);
304 }
305 
306 void MSXRS232::I8251Interf::setRTS(bool status, EmuTime::param time)
307 {
308  auto& rs232 = OUTER(MSXRS232, interf);
309  rs232.getPluggedRS232Dev().setRTS(status, time);
310 }
311 
312 bool MSXRS232::I8251Interf::getDSR(EmuTime::param time)
313 {
314  auto& rs232 = OUTER(MSXRS232, interf);
315  return rs232.getPluggedRS232Dev().getDSR(time);
316 }
317 
318 bool MSXRS232::I8251Interf::getCTS(EmuTime::param time)
319 {
320  auto& rs232 = OUTER(MSXRS232, interf);
321  return rs232.getPluggedRS232Dev().getCTS(time);
322 }
323 
324 void MSXRS232::I8251Interf::setDataBits(DataBits bits)
325 {
326  auto& rs232 = OUTER(MSXRS232, interf);
327  rs232.getPluggedRS232Dev().setDataBits(bits);
328 }
329 
330 void MSXRS232::I8251Interf::setStopBits(StopBits bits)
331 {
332  auto& rs232 = OUTER(MSXRS232, interf);
333  rs232.getPluggedRS232Dev().setStopBits(bits);
334 }
335 
336 void MSXRS232::I8251Interf::setParityBit(bool enable, ParityBit parity)
337 {
338  auto& rs232 = OUTER(MSXRS232, interf);
339  rs232.getPluggedRS232Dev().setParityBit(enable, parity);
340 }
341 
342 void MSXRS232::I8251Interf::recvByte(byte value, EmuTime::param time)
343 {
344  auto& rs232 = OUTER(MSXRS232, interf);
345  rs232.getPluggedRS232Dev().recvByte(value, time);
346 }
347 
348 void MSXRS232::I8251Interf::signal(EmuTime::param time)
349 {
350  auto& rs232 = OUTER(MSXRS232, interf);
351  rs232.getPluggedRS232Dev().signal(time); // for input
352 }
353 
354 
355 // Counter 0 output
356 
357 void MSXRS232::Counter0::signal(ClockPin& pin, EmuTime::param time)
358 {
359  auto& rs232 = OUTER(MSXRS232, cntr0);
360  ClockPin& clk = rs232.i8251.getClockPin();
361  if (pin.isPeriodic()) {
363  pin.getHighDuration(), time);
364  } else {
365  clk.setState(pin.getState(time), time);
366  }
367 }
368 
369 void MSXRS232::Counter0::signalPosEdge(ClockPin& /*pin*/, EmuTime::param /*time*/)
370 {
371  UNREACHABLE;
372 }
373 
374 
375 // Counter 1 output // TODO split rx tx
376 
377 void MSXRS232::Counter1::signal(ClockPin& pin, EmuTime::param time)
378 {
379  auto& rs232 = OUTER(MSXRS232, cntr1);
380  ClockPin& clk = rs232.i8251.getClockPin();
381  if (pin.isPeriodic()) {
383  pin.getHighDuration(), time);
384  } else {
385  clk.setState(pin.getState(time), time);
386  }
387 }
388 
389 void MSXRS232::Counter1::signalPosEdge(ClockPin& /*pin*/, EmuTime::param /*time*/)
390 {
391  UNREACHABLE;
392 }
393 
394 
395 // RS232Connector input
396 
398 {
399  return i8251.isRecvReady();
400 }
401 
403 {
404  return i8251.isRecvEnabled();
405 }
406 
408 {
409  i8251.setDataBits(bits);
410 }
411 
413 {
414  i8251.setStopBits(bits);
415 }
416 
417 void MSXRS232::setParityBit(bool enable, ParityBit parity)
418 {
419  i8251.setParityBit(enable, parity);
420 }
421 
423 {
424  i8251.recvByte(value, time);
425 }
426 
427 // version 1: initial version
428 // version 2: added ioAccessEnabled
429 // TODO: serialize switch status?
430 template<typename Archive>
431 void MSXRS232::serialize(Archive& ar, unsigned version)
432 {
433  ar.template serializeBase<MSXDevice>(*this);
434  ar.template serializeBase<RS232Connector>(*this);
435 
436  ar.serialize("I8254", i8254);
437  ar.serialize("I8251", i8251);
438  if (ram) ar.serialize("ram", *ram);
439  ar.serialize("rxrdyIRQ", rxrdyIRQ);
440  ar.serialize("rxrdyIRQlatch", rxrdyIRQlatch);
441  ar.serialize("rxrdyIRQenabled", rxrdyIRQenabled);
442  if (ar.versionAtLeast(version, 2)) {
443  ar.serialize("ioAccessEnabled", ioAccessEnabled);
444  } else {
445  assert(ar.isLoader());
446  ioAccessEnabled = !hasMemoryBasedIo; // we can't know the
447  // actual value, but this is probably
448  // safest
449  }
450 
451  // don't serialize cntr0, cntr1, interf
452 }
454 REGISTER_MSXDEVICE(MSXRS232, "RS232");
455 
456 } // namespace openmsx
byte readIO(word port, EmuTime::param time)
Definition: I8254.cc:93
MSXRS232(const DeviceConfig &config)
Definition: MSXRS232.cc:19
ClockPin & getOutputPin(unsigned cntr)
Definition: I8254.cc:173
bool isRecvEnabled()
Definition: I8251.cc:318
void serialize(Archive &ar, unsigned version)
Definition: MSXRS232.cc:431
byte peekIO(word port, EmuTime::param time) const
Definition: I8251.cc:95
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition: MSXRS232.cc:76
void setState(bool status, EmuTime::param time)
Definition: ClockPin.cc:16
void setStopBits(StopBits bits) override
Definition: I8251.hh:45
void setParityBit(bool enable, ParityBit parity) override
Definition: I8251.cc:294
bool acceptsData() override
Definition: MSXRS232.cc:402
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
Definition: MSXRS232.cc:139
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void writeIO(word port, byte value, EmuTime::param time)
Definition: I8254.cc:119
EmuDuration::param getHighDuration() const
Definition: ClockPin.cc:73
EmuDuration::param getTotalDuration() const
Definition: ClockPin.cc:67
bool isRecvReady()
Definition: I8251.hh:40
byte peekIO(word port, EmuTime::param time) const
Definition: I8254.cc:106
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
void setParityBit(bool enable, ParityBit parity) override
Definition: MSXRS232.cc:417
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition: MSXRS232.cc:173
const unsigned RAM_SIZE
Definition: MSXRS232.cc:17
bool ready() override
Definition: MSXRS232.cc:397
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:135
void writeIO(word port, byte value, EmuTime::param time)
Definition: I8251.cc:108
bool isPeriodic() const
Definition: ClockPin.hh:34
byte readIO(word port, EmuTime::param time)
Definition: I8251.cc:79
void reset(EmuTime::param time) override
This method is called on reset.
Definition: MSXRS232.cc:65
void setDataBits(DataBits bits) override
Definition: MSXRS232.cc:407
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
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.
Definition: MSXRS232.cc:106
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
bool getState(EmuTime::param time) const
Definition: ClockPin.cc:58
void set()
Set the interrupt request on the bus.
Definition: IRQHelper.hh:78
byte * getWriteCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for writing...
Definition: MSXRS232.cc:126
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:840
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: MSXRS232.cc:201
ClockPin & getClockPin(unsigned cntr)
Definition: I8254.cc:167
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:269
const byte * getReadCacheLine(word start) const override
Test that the memory in the interval [start, start + CacheLine::SIZE) is cacheable for reading...
Definition: MSXRS232.cc:91
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Definition: MSXRS232.cc:59
const unsigned RAM_OFFSET
Definition: MSXRS232.cc:16
void setStopBits(StopBits bits) override
Definition: MSXRS232.cc:412
const string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:373
void recvByte(byte value, EmuTime::param time) override
Definition: MSXRS232.cc:422
#define OUTER(type, member)
Definition: outer.hh:38
void setDataBits(DataBits bits) override
Definition: I8251.hh:44
void recvByte(byte value, EmuTime::param time) override
Definition: I8251.cc:300
std::unique_ptr< T > make_unique()
Definition: memory.hh:27
void setPeriodicState(EmuDuration::param total, EmuDuration::param hi, EmuTime::param time)
Definition: ClockPin.cc:36
#define UNREACHABLE
Definition: unreachable.hh:35