openMSX
MSXRS232.cc
Go to the documentation of this file.
1#include "MSXRS232.hh"
2
3#include "RS232Device.hh"
4#include "CacheLine.hh"
5#include "Ram.hh"
6#include "Rom.hh"
7#include "BooleanSetting.hh"
8#include "MSXException.hh"
9#include "serialize.hh"
10
11#include "one_of.hh"
12#include "outer.hh"
13#include "unreachable.hh"
14
15#include <cassert>
16#include <memory>
17
18namespace openmsx {
19
20const unsigned RAM_OFFSET = 0x2000;
21const unsigned RAM_SIZE = 0x800;
22
24 : MSXDevice(config)
25 , RS232Connector(MSXDevice::getPluggingController(), "msx-rs232")
26 , i8254(getScheduler(), &cntr0, &cntr1, nullptr, getCurrentTime())
27 , i8251(getScheduler(), interface, getCurrentTime())
28 , rom(config.findChild("rom")
29 ? std::make_unique<Rom>(
30 MSXDevice::getName() + " ROM", "rom", config)
31 : nullptr) // when the ROM is already mapped, you don't want to specify it again here
32 , ram(config.getChildDataAsBool("ram", false)
33 ? std::make_unique<Ram>(
34 config, MSXDevice::getName() + " RAM",
35 "RS232 RAM", RAM_SIZE)
36 : nullptr)
37 , rxrdyIRQ(getMotherBoard(), MSXDevice::getName() + ".IRQrxrdy")
38 , hasMemoryBasedIo(config.getChildDataAsBool("memorybasedio", false))
39 , hasRIPin(config.getChildDataAsBool("has_ri_pin", true))
40 , inputsPullup(config.getChildDataAsBool("rs232_pullup",false))
41 , ioAccessEnabled(!hasMemoryBasedIo)
42 , switchSetting(config.getChildDataAsBool("toshiba_rs232c_switch",
43 false) ? std::make_unique<BooleanSetting>(getCommandController(),
44 "toshiba_rs232c_switch", "status of the RS-232C enable switch",
45 true) : nullptr)
46{
47 if (rom && (rom->size() != one_of(0x2000u, 0x4000u))) {
48 throw MSXException("RS232C only supports 8kB or 16kB ROMs.");
49 }
50
51 EmuDuration total(1.0 / 1.8432e6); // 1.8432MHz
52 EmuDuration hi (1.0 / 3.6864e6); // half clock period
53 EmuTime::param time = getCurrentTime();
54 i8254.getClockPin(0).setPeriodicState(total, hi, time);
55 i8254.getClockPin(1).setPeriodicState(total, hi, time);
56 i8254.getClockPin(2).setPeriodicState(total, hi, time);
57
58 powerUp(time);
59}
60
61MSXRS232::~MSXRS232() = default;
62
63void MSXRS232::powerUp(EmuTime::param time)
64{
65 if (ram) ram->clear();
66 reset(time);
67}
68
69void MSXRS232::reset(EmuTime::param /*time*/)
70{
71 rxrdyIRQlatch = false;
72 rxrdyIRQenabled = false;
73 rxrdyIRQ.reset();
74
75 ioAccessEnabled = !hasMemoryBasedIo;
76
77 if (ram) ram->clear();
78}
79
80byte MSXRS232::readMem(word address, EmuTime::param time)
81{
82 if (hasMemoryBasedIo && (0xBFF8 <= address) && (address <= 0xBFFF)) {
83 return readIOImpl(address & 0x07, time);
84 }
85 word addr = address & 0x3FFF;
86 if (ram && ((RAM_OFFSET <= addr) && (addr < (RAM_OFFSET + RAM_SIZE)))) {
87 return (*ram)[addr - RAM_OFFSET];
88 } else if (rom && (0x4000 <= address) && (address < 0x8000)) {
89 return (*rom)[addr & (rom->size() - 1)];
90 } else {
91 return 0xFF;
92 }
93}
94
95const byte* MSXRS232::getReadCacheLine(word start) const
96{
97 if (hasMemoryBasedIo && (start == (0xBFF8 & CacheLine::HIGH))) {
98 return nullptr;
99 }
100 word addr = start & 0x3FFF;
101 if (ram && ((RAM_OFFSET <= addr) && (addr < (RAM_OFFSET + RAM_SIZE)))) {
102 return &(*ram)[addr - RAM_OFFSET];
103 } else if (rom && (0x4000 <= start) && (start < 0x8000)) {
104 return &(*rom)[addr & (rom->size() - 1)];
105 } else {
106 return unmappedRead.data();
107 }
108}
109
110void MSXRS232::writeMem(word address, byte value, EmuTime::param time)
111{
112
113 if (hasMemoryBasedIo && (0xBFF8 <= address) && (address <= 0xBFFF)) {
114 // when the interface has memory based I/O, the I/O port
115 // based I/O is disabled, but it can be enabled by writing
116 // bit 4 to 0xBFFA. It is disabled again at reset.
117 // Source: Sony HB-G900P and Sony HB-G900AP service manuals.
118 // We assume here you can also disable it by writing 0 to it.
119 if (address == 0xBFFA) {
120 ioAccessEnabled = (value & (1 << 4))!=0;
121 }
122 return writeIOImpl(address & 0x07, value, time);
123 }
124 word addr = address & 0x3FFF;
125 if (ram && ((RAM_OFFSET <= addr) && (addr < (RAM_OFFSET + RAM_SIZE)))) {
126 (*ram)[addr - RAM_OFFSET] = value;
127 }
128}
129
131{
132 if (hasMemoryBasedIo && (start == (0xBFF8 & CacheLine::HIGH))) {
133 return nullptr;
134 }
135 word addr = start & 0x3FFF;
136 if (ram && ((RAM_OFFSET <= addr) && (addr < (RAM_OFFSET + RAM_SIZE)))) {
137 return &(*ram)[addr - RAM_OFFSET];
138 } else {
139 return unmappedWrite.data();
140 }
141}
142
144{
145 // OK, because this device doesn't call any 'fillDeviceXXXCache()'functions.
146 return true;
147}
148
149byte MSXRS232::readIO(word port, EmuTime::param time)
150{
151 if (ioAccessEnabled) {
152 return readIOImpl(port & 0x07, time);
153 }
154 return 0xFF;
155}
156
157byte MSXRS232::readIOImpl(word port, EmuTime::param time)
158{
159 switch (port) {
160 case 0: // UART data register
161 case 1: // UART status register
162 return i8251.readIO(port, time);
163 case 2: // Status sense port
164 return readStatus(time);
165 case 3: // no function
166 return 0xFF;
167 case 4: // counter 0 data port
168 case 5: // counter 1 data port
169 case 6: // counter 2 data port
170 case 7: // timer command register
171 return i8254.readIO(port - 4, time);
172 default:
174 }
175}
176
177byte MSXRS232::peekIO(word port, EmuTime::param time) const
178{
179 if (hasMemoryBasedIo && !ioAccessEnabled) return 0xFF;
180 port &= 0x07;
181 switch (port) {
182 case 0: // UART data register
183 case 1: // UART status register
184 return i8251.peekIO(port, time);
185 case 2: // Status sense port
186 return 0; // TODO not implemented
187 case 3: // no function
188 return 0xFF;
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 return i8254.peekIO(port - 4, time);
194 default:
196 }
197}
198
199void MSXRS232::writeIO(word port, byte value, EmuTime::param time)
200{
201 if (ioAccessEnabled) writeIOImpl(port & 0x07, value, time);
202}
203
204void MSXRS232::writeIOImpl(word port, byte value, EmuTime::param time)
205{
206 switch (port) {
207 case 0: // UART data register
208 case 1: // UART command register
209 i8251.writeIO(port, value, time);
210 break;
211 case 2: // interrupt mask register
212 setIRQMask(value);
213 break;
214 case 3: // no function
215 break;
216 case 4: // counter 0 data port
217 case 5: // counter 1 data port
218 case 6: // counter 2 data port
219 case 7: // timer command register
220 i8254.writeIO(port - 4, value, time);
221 break;
222 }
223}
224
225byte MSXRS232::readStatus(EmuTime::param time)
226{
227
228 // Info from http://nocash.emubase.de/portar.htm
229 //
230 // Bit Name Expl.
231 // 0 CD Carrier Detect (0=Active, 1=Not active)
232 // 1 RI Ring Indicator (0=Active, 1=Not active)
233 // (Not connected on many RS232 BIOS v1 implementations)
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 = 0xFF; // Start with 0xFF, open lines on the data bus pull to 1
245 const auto& dev = getPluggedRS232Dev();
246
247 // Mask out (active low) bits
248 if (dev.getDCD(time).value_or(inputsPullup)) result &= ~0x01;
249 if (hasRIPin && (dev.getRI(time).value_or(inputsPullup))) result &= ~0x02;
250 if (rxrdyIRQenabled && switchSetting && switchSetting->getBoolean()) result &= ~0x08;
251 if (i8254.getOutputPin(2).getState(time)) result &= ~0x40;
252 if (interface.getCTS(time)) result &= ~0x80;
253
254 return result;
255}
256
257void MSXRS232::setIRQMask(byte value)
258{
259 enableRxRDYIRQ(!(value & 1));
260}
261
262void 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
276void 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
289void MSXRS232::Interface::setRxRDY(bool status, EmuTime::param /*time*/)
290{
291 auto& rs232 = OUTER(MSXRS232, interface);
292 rs232.setRxRDYIRQ(status);
293}
294
295void MSXRS232::Interface::setDTR(bool status, EmuTime::param time)
296{
297 const auto& rs232 = OUTER(MSXRS232, interface);
298 rs232.getPluggedRS232Dev().setDTR(status, time);
299}
300
301void MSXRS232::Interface::setRTS(bool status, EmuTime::param time)
302{
303 const auto& rs232 = OUTER(MSXRS232, interface);
304 rs232.getPluggedRS232Dev().setRTS(status, time);
305}
306
307bool MSXRS232::Interface::getDSR(EmuTime::param time)
308{
309 const auto& rs232 = OUTER(MSXRS232, interface);
310 return rs232.getPluggedRS232Dev().getDSR(time).value_or(rs232.inputsPullup);
311}
312
313bool MSXRS232::Interface::getCTS(EmuTime::param time)
314{
315 const auto& rs232 = OUTER(MSXRS232, interface);
316 return rs232.getPluggedRS232Dev().getCTS(time).value_or(rs232.inputsPullup);
317}
318
319void MSXRS232::Interface::setDataBits(DataBits bits)
320{
321 const auto& rs232 = OUTER(MSXRS232, interface);
322 rs232.getPluggedRS232Dev().setDataBits(bits);
323}
324
325void MSXRS232::Interface::setStopBits(StopBits bits)
326{
327 const auto& rs232 = OUTER(MSXRS232, interface);
328 rs232.getPluggedRS232Dev().setStopBits(bits);
329}
330
331void MSXRS232::Interface::setParityBit(bool enable, ParityBit parity)
332{
333 const auto& rs232 = OUTER(MSXRS232, interface);
334 rs232.getPluggedRS232Dev().setParityBit(enable, parity);
335}
336
337void MSXRS232::Interface::recvByte(byte value, EmuTime::param time)
338{
339 const auto& rs232 = OUTER(MSXRS232, interface);
340 rs232.getPluggedRS232Dev().recvByte(value, time);
341}
342
343void MSXRS232::Interface::signal(EmuTime::param time)
344{
345 const auto& rs232 = OUTER(MSXRS232, interface);
346 rs232.getPluggedRS232Dev().signal(time); // for input
347}
348
349
350// Counter 0 output
351
352void 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
364void MSXRS232::Counter0::signalPosEdge(ClockPin& /*pin*/, EmuTime::param /*time*/)
365{
367}
368
369
370// Counter 1 output // TODO split rx tx
371
372void 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
384void MSXRS232::Counter1::signalPosEdge(ClockPin& /*pin*/, EmuTime::param /*time*/)
385{
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
412void MSXRS232::setParityBit(bool enable, ParityBit parity)
413{
414 i8251.setParityBit(enable, parity);
415}
416
417void 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?
425template<typename Archive>
426void 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(Archive::IS_LOADER);
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, interface
447}
450
451} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:356
bool getState(EmuTime::param time) const
Definition ClockPin.cc:55
void setPeriodicState(EmuDuration::param total, EmuDuration::param hi, EmuTime::param time)
Definition ClockPin.cc:33
byte peekIO(word port, EmuTime::param time) const
Definition I8251.cc:86
void setStopBits(StopBits bits) override
Definition I8251.hh:46
bool isRecvReady() const
Definition I8251.hh:41
void recvByte(byte value, EmuTime::param time) override
Definition I8251.cc:262
void setParityBit(bool enable, ParityBit parity) override
Definition I8251.cc:256
void writeIO(word port, byte value, EmuTime::param time)
Definition I8251.cc:96
void setDataBits(DataBits bits) override
Definition I8251.hh:45
byte readIO(word port, EmuTime::param time)
Definition I8251.cc:77
bool isRecvEnabled() const
Definition I8251.cc:280
uint8_t peekIO(uint16_t port, EmuTime::param time) const
Definition I8254.cc:54
void writeIO(uint16_t port, uint8_t value, EmuTime::param time)
Definition I8254.cc:67
uint8_t readIO(uint16_t port, EmuTime::param time)
Definition I8254.cc:41
ClockPin & getClockPin(unsigned cntr)
Definition I8254.cc:115
ClockPin & getOutputPin(unsigned cntr)
Definition I8254.cc:121
void set()
Set the interrupt request on the bus.
Definition IRQHelper.hh:76
void reset()
Reset the interrupt request on the bus.
Definition IRQHelper.hh:85
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:306
static std::array< byte, 0x10000 > unmappedWrite
Definition MSXDevice.hh:307
EmuTime::param getCurrentTime() const
Definition MSXDevice.cc:125
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:199
MSXRS232(const DeviceConfig &config)
Definition MSXRS232.cc:23
void setDataBits(DataBits bits) override
Definition MSXRS232.cc:402
void reset(EmuTime::param time) override
This method is called on reset.
Definition MSXRS232.cc:69
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:130
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:149
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
Definition MSXRS232.cc:63
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:95
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:110
byte readMem(word address, EmuTime::param time) override
Read a byte from a location at a certain time from this device.
Definition MSXRS232.cc:80
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:143
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:177
RS232Device & getPluggedRS232Dev() const
constexpr unsigned HIGH
Definition CacheLine.hh:10
This file implemented 3 utility functions:
Definition Autofire.cc:11
const unsigned RAM_OFFSET
Definition MSXRS232.cc:20
const unsigned RAM_SIZE
Definition MSXRS232.cc:21
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
STL namespace.
#define OUTER(type, member)
Definition outer.hh:42
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define UNREACHABLE