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