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