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