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
15namespace openmsx {
16
17const unsigned RAM_OFFSET = 0x2000;
18const unsigned RAM_SIZE = 0x800;
19
21 : MSXDevice(config)
22 , RS232Connector(MSXDevice::getPluggingController(), "msx-rs232")
23 , i8254(getScheduler(), &cntr0, &cntr1, nullptr, getCurrentTime())
24 , i8251(getScheduler(), interface, 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 , hasMemoryBasedIo(config.getChildDataAsBool("memorybasedio", false))
36 , hasRIPin(config.getChildDataAsBool("has_ri_pin", true))
37 , inputsPullup(config.getChildDataAsBool("rs232_pullup",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->size() != 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
58MSXRS232::~MSXRS232() = default;
59
60void MSXRS232::powerUp(EmuTime::param time)
61{
62 if (ram) ram->clear();
63 reset(time);
64}
65
66void 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
77byte 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->size() - 1)];
87 } else {
88 return 0xFF;
89 }
90}
91
92const 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->size() - 1)];
102 } else {
103 return unmappedRead.data();
104 }
105}
106
107void 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.data();
137 }
138}
139
141{
142 // OK, because this device doesn't call any 'fillDeviceXXXCache()'functions.
143 return true;
144}
145
146byte MSXRS232::readIO(word port, EmuTime::param time)
147{
148 if (ioAccessEnabled) {
149 return readIOImpl(port & 0x07, time);
150 }
151 return 0xFF;
152}
153
154byte 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
174byte 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
196void MSXRS232::writeIO(word port, byte value, EmuTime::param time)
197{
198 if (ioAccessEnabled) writeIOImpl(port & 0x07, value, time);
199}
200
201void 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
222byte 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)
230 // (Not connected on many RS232 BIOS v1 implementations)
231 // 6 Timer Output from i8253 Counter 2
232 // 7 CTS Clear to Send (0=Active, 1=Not active)
233 //
234 // On Toshiba HX-22, see
235 // http://www.msx.org/forum/msx-talk/hardware/toshiba-hx-22?page=3
236 // RetroTechie's post of 20-09-2012, 08:08
237 // ... The "RS-232 interrupt disable" bit can be read back via bit 3
238 // on this I/O port, if CN1 is open. If CN1 is closed, it always
239 // reads back as "0". ...
240
241 byte result = 0xFF; // Start with 0xFF, open lines on the data bus pull to 1
242 auto& dev = getPluggedRS232Dev();
243
244 // Mask out (active low) bits
245
246 if (dev.getDCD(time).value_or(inputsPullup)) result &= ~0x01;
247
248 if (hasRIPin && (dev.getRI(time).value_or(inputsPullup))) result &= ~0x02;
249
250 if (rxrdyIRQenabled && switchSetting && switchSetting->getBoolean()) result &= ~0x08;
251
252 if (i8254.getOutputPin(2).getState(time)) result &= ~0x40;
253
254 if (interface.getCTS(time)) result &= ~0x80;
255
256 return result;
257}
258
259void MSXRS232::setIRQMask(byte value)
260{
261 enableRxRDYIRQ(!(value & 1));
262}
263
264void MSXRS232::setRxRDYIRQ(bool status)
265{
266 if (rxrdyIRQlatch != status) {
267 rxrdyIRQlatch = status;
268 if (rxrdyIRQenabled) {
269 if (rxrdyIRQlatch) {
270 rxrdyIRQ.set();
271 } else {
272 rxrdyIRQ.reset();
273 }
274 }
275 }
276}
277
278void MSXRS232::enableRxRDYIRQ(bool enabled)
279{
280 if (rxrdyIRQenabled != enabled) {
281 rxrdyIRQenabled = enabled;
282 if (!rxrdyIRQenabled && rxrdyIRQlatch) {
283 rxrdyIRQ.reset();
284 }
285 }
286}
287
288
289// I8251Interface (pass calls from I8251 to outConnector)
290
291void MSXRS232::Interface::setRxRDY(bool status, EmuTime::param /*time*/)
292{
293 auto& rs232 = OUTER(MSXRS232, interface);
294 rs232.setRxRDYIRQ(status);
295}
296
297void MSXRS232::Interface::setDTR(bool status, EmuTime::param time)
298{
299 auto& rs232 = OUTER(MSXRS232, interface);
300 rs232.getPluggedRS232Dev().setDTR(status, time);
301}
302
303void MSXRS232::Interface::setRTS(bool status, EmuTime::param time)
304{
305 auto& rs232 = OUTER(MSXRS232, interface);
306 rs232.getPluggedRS232Dev().setRTS(status, time);
307}
308
309bool MSXRS232::Interface::getDSR(EmuTime::param time)
310{
311 auto& rs232 = OUTER(MSXRS232, interface);
312 return rs232.getPluggedRS232Dev().getDSR(time).value_or(rs232.inputsPullup);
313}
314
315bool MSXRS232::Interface::getCTS(EmuTime::param time)
316{
317 auto& rs232 = OUTER(MSXRS232, interface);
318 return rs232.getPluggedRS232Dev().getCTS(time).value_or(rs232.inputsPullup);
319}
320
321void MSXRS232::Interface::setDataBits(DataBits bits)
322{
323 auto& rs232 = OUTER(MSXRS232, interface);
324 rs232.getPluggedRS232Dev().setDataBits(bits);
325}
326
327void MSXRS232::Interface::setStopBits(StopBits bits)
328{
329 auto& rs232 = OUTER(MSXRS232, interface);
330 rs232.getPluggedRS232Dev().setStopBits(bits);
331}
332
333void MSXRS232::Interface::setParityBit(bool enable, ParityBit parity)
334{
335 auto& rs232 = OUTER(MSXRS232, interface);
336 rs232.getPluggedRS232Dev().setParityBit(enable, parity);
337}
338
339void MSXRS232::Interface::recvByte(byte value, EmuTime::param time)
340{
341 auto& rs232 = OUTER(MSXRS232, interface);
342 rs232.getPluggedRS232Dev().recvByte(value, time);
343}
344
345void MSXRS232::Interface::signal(EmuTime::param time)
346{
347 auto& rs232 = OUTER(MSXRS232, interface);
348 rs232.getPluggedRS232Dev().signal(time); // for input
349}
350
351
352// Counter 0 output
353
354void MSXRS232::Counter0::signal(ClockPin& pin, EmuTime::param time)
355{
356 auto& rs232 = OUTER(MSXRS232, cntr0);
357 ClockPin& clk = rs232.i8251.getClockPin();
358 if (pin.isPeriodic()) {
359 clk.setPeriodicState(pin.getTotalDuration(),
360 pin.getHighDuration(), time);
361 } else {
362 clk.setState(pin.getState(time), time);
363 }
364}
365
366void MSXRS232::Counter0::signalPosEdge(ClockPin& /*pin*/, EmuTime::param /*time*/)
367{
369}
370
371
372// Counter 1 output // TODO split rx tx
373
374void MSXRS232::Counter1::signal(ClockPin& pin, EmuTime::param time)
375{
376 auto& rs232 = OUTER(MSXRS232, cntr1);
377 ClockPin& clk = rs232.i8251.getClockPin();
378 if (pin.isPeriodic()) {
379 clk.setPeriodicState(pin.getTotalDuration(),
380 pin.getHighDuration(), time);
381 } else {
382 clk.setState(pin.getState(time), time);
383 }
384}
385
386void MSXRS232::Counter1::signalPosEdge(ClockPin& /*pin*/, EmuTime::param /*time*/)
387{
389}
390
391
392// RS232Connector input
393
395{
396 return i8251.isRecvReady();
397}
398
400{
401 return i8251.isRecvEnabled();
402}
403
405{
406 i8251.setDataBits(bits);
407}
408
410{
411 i8251.setStopBits(bits);
412}
413
414void MSXRS232::setParityBit(bool enable, ParityBit parity)
415{
416 i8251.setParityBit(enable, parity);
417}
418
419void MSXRS232::recvByte(byte value, EmuTime::param time)
420{
421 i8251.recvByte(value, time);
422}
423
424// version 1: initial version
425// version 2: added ioAccessEnabled
426// TODO: serialize switch status?
427template<typename Archive>
428void MSXRS232::serialize(Archive& ar, unsigned version)
429{
430 ar.template serializeBase<MSXDevice>(*this);
431 ar.template serializeBase<RS232Connector>(*this);
432
433 ar.serialize("I8254", i8254,
434 "I8251", i8251);
435 if (ram) ar.serialize("ram", *ram);
436 ar.serialize("rxrdyIRQ", rxrdyIRQ,
437 "rxrdyIRQlatch", rxrdyIRQlatch,
438 "rxrdyIRQenabled", rxrdyIRQenabled);
439 if (ar.versionAtLeast(version, 2)) {
440 ar.serialize("ioAccessEnabled", ioAccessEnabled);
441 } else {
442 assert(Archive::IS_LOADER);
443 ioAccessEnabled = !hasMemoryBasedIo; // we can't know the
444 // actual value, but this is probably
445 // safest
446 }
447
448 // don't serialize cntr0, cntr1, interface
449}
452
453} // namespace openmsx
#define REGISTER_MSXDEVICE(CLASS, NAME)
Definition MSXDevice.hh:354
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:261
void setParityBit(bool enable, ParityBit parity) override
Definition I8251.cc:255
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:279
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:74
void reset()
Reset the interrupt request on the bus.
Definition IRQHelper.hh:83
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:304
static std::array< byte, 0x10000 > unmappedWrite
Definition MSXDevice.hh:305
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:196
MSXRS232(const DeviceConfig &config)
Definition MSXRS232.cc:20
void setDataBits(DataBits bits) override
Definition MSXRS232.cc:404
void reset(EmuTime::param time) override
This method is called on reset.
Definition MSXRS232.cc:66
bool acceptsData() override
Definition MSXRS232.cc:399
bool ready() override
Definition MSXRS232.cc:394
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:414
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:409
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:428
void recvByte(byte value, EmuTime::param time) override
Definition MSXRS232.cc:419
~MSXRS232() override
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
Definition MSXRS232.cc:174
RS232Device & getPluggedRS232Dev() const
constexpr unsigned HIGH
Definition CacheLine.hh:10
This file implemented 3 utility functions:
Definition Autofire.cc:9
const unsigned RAM_OFFSET
Definition MSXRS232.cc:17
const unsigned RAM_SIZE
Definition MSXRS232.cc:18
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
STL namespace.
#define OUTER(type, member)
Definition outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define UNREACHABLE