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