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