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(), 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
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->getSize() - 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->getSize() - 1)];
102 } else {
103 return unmappedRead;
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;
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) (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
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::I8251Interf::setRxRDY(bool status, EmuTime::param /*time*/)
290{
291 auto& rs232 = OUTER(MSXRS232, interf);
292 rs232.setRxRDYIRQ(status);
293}
294
295void MSXRS232::I8251Interf::setDTR(bool status, EmuTime::param time)
296{
297 auto& rs232 = OUTER(MSXRS232, interf);
298 rs232.getPluggedRS232Dev().setDTR(status, time);
299}
300
301void MSXRS232::I8251Interf::setRTS(bool status, EmuTime::param time)
302{
303 auto& rs232 = OUTER(MSXRS232, interf);
304 rs232.getPluggedRS232Dev().setRTS(status, time);
305}
306
307bool MSXRS232::I8251Interf::getDSR(EmuTime::param time)
308{
309 auto& rs232 = OUTER(MSXRS232, interf);
310 return rs232.getPluggedRS232Dev().getDSR(time);
311}
312
313bool MSXRS232::I8251Interf::getCTS(EmuTime::param time)
314{
315 auto& rs232 = OUTER(MSXRS232, interf);
316 return rs232.getPluggedRS232Dev().getCTS(time);
317}
318
319void MSXRS232::I8251Interf::setDataBits(DataBits bits)
320{
321 auto& rs232 = OUTER(MSXRS232, interf);
322 rs232.getPluggedRS232Dev().setDataBits(bits);
323}
324
325void MSXRS232::I8251Interf::setStopBits(StopBits bits)
326{
327 auto& rs232 = OUTER(MSXRS232, interf);
328 rs232.getPluggedRS232Dev().setStopBits(bits);
329}
330
331void MSXRS232::I8251Interf::setParityBit(bool enable, ParityBit parity)
332{
333 auto& rs232 = OUTER(MSXRS232, interf);
334 rs232.getPluggedRS232Dev().setParityBit(enable, parity);
335}
336
337void MSXRS232::I8251Interf::recvByte(byte value, EmuTime::param time)
338{
339 auto& rs232 = OUTER(MSXRS232, interf);
340 rs232.getPluggedRS232Dev().recvByte(value, time);
341}
342
343void 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
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, interf
447}
450
451} // namespace openmsx
Definition: one_of.hh:7
bool getState(EmuTime::param time) const
Definition: ClockPin.cc:56
void setPeriodicState(EmuDuration::param total, EmuDuration::param hi, EmuTime::param time)
Definition: ClockPin.cc:34
byte peekIO(word port, EmuTime::param time) const
Definition: I8251.cc:86
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: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:44
byte readIO(word port, EmuTime::param time)
Definition: I8251.cc:77
bool isRecvEnabled() const
Definition: I8251.cc:279
byte peekIO(word port, EmuTime::param time) const
Definition: I8254.cc:54
byte readIO(word port, EmuTime::param time)
Definition: I8254.cc:41
ClockPin & getClockPin(unsigned cntr)
Definition: I8254.cc:115
void writeIO(word port, byte value, EmuTime::param time)
Definition: I8254.cc:67
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:33
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:301
EmuTime::param getCurrentTime() const
Definition: MSXDevice.cc:126
static byte unmappedWrite[0x10000]
Definition: MSXDevice.hh:302
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
std::string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:727
This file implemented 3 utility functions:
Definition: Autofire.cc:9
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
STL namespace.
#define OUTER(type, member)
Definition: outer.hh:41
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009
#define UNREACHABLE
Definition: unreachable.hh:38