openMSX
I8251.cc
Go to the documentation of this file.
1#include "I8251.hh"
2#include "serialize.hh"
3#include "unreachable.hh"
4#include <cassert>
5
6namespace openmsx {
7
8static constexpr byte STAT_TXRDY = 0x01;
9static constexpr byte STAT_RXRDY = 0x02;
10static constexpr byte STAT_TXEMPTY = 0x04;
11static constexpr byte STAT_PE = 0x08;
12static constexpr byte STAT_OE = 0x10;
13static constexpr byte STAT_FE = 0x20;
14static constexpr byte STAT_SYN_BRK = 0x40;
15static constexpr byte STAT_DSR = 0x80;
16
17static constexpr byte MODE_BAUDRATE = 0x03;
18static constexpr byte MODE_SYNCHRONOUS = 0x00;
19static constexpr byte MODE_RATE1 = 0x01;
20static constexpr byte MODE_RATE16 = 0x02;
21static constexpr byte MODE_RATE64 = 0x03;
22static constexpr byte MODE_WORD_LENGTH = 0x0C;
23static constexpr byte MODE_5BIT = 0x00;
24static constexpr byte MODE_6BIT = 0x04;
25static constexpr byte MODE_7BIT = 0x08;
26static constexpr byte MODE_8BIT = 0x0C;
27static constexpr byte MODE_PARITY_EVEN = 0x10;
28static constexpr byte MODE_PARITY_ODD = 0x00;
29static constexpr byte MODE_PARITEVEN = 0x20;
30static constexpr byte MODE_STOP_BITS = 0xC0;
31static constexpr byte MODE_STOP_INV = 0x00;
32static constexpr byte MODE_STOP_1 = 0x40;
33static constexpr byte MODE_STOP_15 = 0x80;
34static constexpr byte MODE_STOP_2 = 0xC0;
35static constexpr byte MODE_SINGLE_SYNC = 0x80;
36
37static constexpr byte CMD_TXEN = 0x01;
38static constexpr byte CMD_DTR = 0x02;
39static constexpr byte CMD_RXE = 0x04;
40static constexpr byte CMD_SBRK = 0x08;
41static constexpr byte CMD_RST_ERR = 0x10;
42static constexpr byte CMD_RTS = 0x20;
43static constexpr byte CMD_RESET = 0x40;
44static constexpr byte CMD_HUNT = 0x80;
45
46
47I8251::I8251(Scheduler& scheduler, I8251Interface& interface_, EmuTime::param time)
48 : syncRecv (scheduler)
49 , syncTrans(scheduler)
50 , interface(interface_), clock(scheduler)
51{
52 reset(time);
53}
54
55void I8251::reset(EmuTime::param time)
56{
57 // initialize these to avoid UMR on savestate
58 // TODO investigate correct initial state after reset
59 charLength = 0;
63 recvParityEnabled = false;
64 recvBuf = 0;
65 recvReady = false;
66 sendByte = 0;
67 sendBuffer = 0;
68 mode = 0;
69 sync1 = sync2 = 0;
70
71 status = STAT_TXRDY | STAT_TXEMPTY;
72 command = 0xFF; // make sure all bits change
73 writeCommand(0, time);
74 cmdPhase = CmdPhase::MODE;
75}
76
77byte I8251::readIO(word port, EmuTime::param time)
78{
79 switch (port & 1) {
80 case 0: return readTrans(time);
81 case 1: return readStatus(time);
82 default: UNREACHABLE;
83 }
84}
85
86byte I8251::peekIO(word port, EmuTime::param /*time*/) const
87{
88 switch (port & 1) {
89 case 0: return recvBuf;
90 case 1: return status; // TODO peekStatus()
91 default: UNREACHABLE;
92 }
93}
94
95
96void I8251::writeIO(word port, byte value, EmuTime::param time)
97{
98 switch (port & 1) {
99 case 0:
100 writeTrans(value, time);
101 break;
102 case 1:
103 switch (cmdPhase) {
104 using enum CmdPhase;
105 case MODE:
106 setMode(value);
107 if ((mode & MODE_BAUDRATE) == MODE_SYNCHRONOUS) {
108 cmdPhase = SYNC1;
109 } else {
110 cmdPhase = CMD;
111 }
112 break;
113 case SYNC1:
114 sync1 = value;
115 if (mode & MODE_SINGLE_SYNC) {
116 cmdPhase = CMD;
117 } else {
118 cmdPhase = SYNC2;
119 }
120 break;
121 case SYNC2:
122 sync2 = value;
123 cmdPhase = CMD;
124 break;
125 case CMD:
126 if (value & CMD_RESET) {
127 cmdPhase = MODE;
128 } else {
129 writeCommand(value, time);
130 }
131 break;
132 default:
134 }
135 break;
136 default:
138 }
139}
140
141void I8251::setMode(byte newMode)
142{
143 mode = newMode;
144
145 auto dataBits = [&] {
146 switch (mode & MODE_WORD_LENGTH) {
148 case MODE_5BIT: return D5;
149 case MODE_6BIT: return D6;
150 case MODE_7BIT: return D7;
151 case MODE_8BIT: return D8;
152 default: UNREACHABLE;
153 }
154 }();
155 interface.setDataBits(dataBits);
156
157 auto stopBits = [&] {
158 switch(mode & MODE_STOP_BITS) {
160 case MODE_STOP_INV: return INV;
161 case MODE_STOP_1: return S1;
162 case MODE_STOP_15: return S1_5;
163 case MODE_STOP_2: return S2;
164 default: UNREACHABLE;
165 }
166 }();
167 interface.setStopBits(stopBits);
168
169 bool parityEnable = (mode & MODE_PARITY_EVEN) != 0;
170 SerialDataInterface::Parity parity = (mode & MODE_PARITEVEN) ?
171 SerialDataInterface::Parity::EVEN : SerialDataInterface::Parity::ODD;
172 interface.setParityBit(parityEnable, parity);
173
174 unsigned baudrate = [&] {
175 switch (mode & MODE_BAUDRATE) {
176 case MODE_SYNCHRONOUS: return 1;
177 case MODE_RATE1: return 1;
178 case MODE_RATE16: return 16;
179 case MODE_RATE64: return 64;
180 default: UNREACHABLE;
181 }
182 }();
183
184 charLength = (((2 * (1 + unsigned(dataBits) + (parityEnable ? 1 : 0))) +
185 unsigned(stopBits)) * baudrate) / 2;
186}
187
188void I8251::writeCommand(byte value, EmuTime::param time)
189{
190 byte oldCommand = command;
191 command = value;
192
193 // CMD_RESET, CMD_TXEN, CMD_RXE handled in other routines
194
195 interface.setRTS((command & CMD_RTS) != 0, time);
196 interface.setDTR((command & CMD_DTR) != 0, time);
197
198 if (!(command & CMD_TXEN)) {
199 // disable transmitter
200 syncTrans.removeSyncPoint();
201 status |= STAT_TXRDY | STAT_TXEMPTY;
202 }
203 if (command & CMD_RST_ERR) {
204 status &= ~(STAT_PE | STAT_OE | STAT_FE);
205 }
206 if (command & CMD_SBRK) {
207 // TODO
208 }
209 if (command & CMD_HUNT) {
210 // TODO
211 }
212
213 if ((command ^ oldCommand) & CMD_RXE) {
214 if (command & CMD_RXE) {
215 // enable receiver
216 status &= ~(STAT_PE | STAT_OE | STAT_FE); // TODO
217 recvReady = true;
218 } else {
219 // disable receiver
220 syncRecv.removeSyncPoint();
221 status &= ~(STAT_PE | STAT_OE | STAT_FE); // TODO
222 status &= ~STAT_RXRDY;
223 }
224 interface.signal(time);
225 }
226}
227
228byte I8251::readStatus(EmuTime::param time)
229{
230 byte result = status;
231 if (interface.getDSR(time)) {
232 result |= STAT_DSR;
233 }
234 return result;
235}
236
237byte I8251::readTrans(EmuTime::param time)
238{
239 status &= ~STAT_RXRDY;
240 interface.setRxRDY(false, time);
241 return recvBuf;
242}
243
244void I8251::writeTrans(byte value, EmuTime::param time)
245{
246 if (!(command & CMD_TXEN)) {
247 return;
248 }
249 if (status & STAT_TXEMPTY) {
250 // not sending
251 send(value, time);
252 } else {
253 sendBuffer = value;
254 status &= ~STAT_TXRDY;
255 }
256}
257
258void I8251::setParityBit(bool enable, Parity parity)
259{
260 recvParityEnabled = enable;
261 recvParityBit = parity;
262}
263
264void I8251::recvByte(byte value, EmuTime::param time)
265{
266 // TODO STAT_PE / STAT_FE / STAT_SYN_BRK
267 assert(recvReady && (command & CMD_RXE));
268 if (status & STAT_RXRDY) {
269 status |= STAT_OE;
270 } else {
271 recvBuf = value;
272 status |= STAT_RXRDY;
273 interface.setRxRDY(true, time);
274 }
275 recvReady = false;
276 if (clock.isPeriodic()) {
277 EmuTime next = time + (clock.getTotalDuration() * charLength);
278 syncRecv.setSyncPoint(next);
279 }
280}
281
283{
284 return (command & CMD_RXE) != 0;
285}
286
287void I8251::send(byte value, EmuTime::param time)
288{
289 status &= ~STAT_TXEMPTY;
290 sendByte = value;
291 if (clock.isPeriodic()) {
292 EmuTime next = time + (clock.getTotalDuration() * charLength);
293 syncTrans.setSyncPoint(next);
294 }
295}
296
297void I8251::execRecv(EmuTime::param time)
298{
299 assert(command & CMD_RXE);
300 recvReady = true;
301 interface.signal(time);
302}
303
304void I8251::execTrans(EmuTime::param time)
305{
306 assert(!(status & STAT_TXEMPTY) && (command & CMD_TXEN));
307
308 interface.recvByte(sendByte, time);
309 if (status & STAT_TXRDY) {
310 status |= STAT_TXEMPTY;
311 } else {
312 status |= STAT_TXRDY;
313 send(sendBuffer, time);
314 }
315}
316
317
318static constexpr std::initializer_list<enum_string<SerialDataInterface::DataBits>> dataBitsInfo = {
323};
325
326static constexpr std::initializer_list<enum_string<SerialDataInterface::StopBits>> stopBitsInfo = {
331};
333
334static constexpr std::initializer_list<enum_string<SerialDataInterface::Parity>> parityBitInfo = {
337};
339
340static constexpr std::initializer_list<enum_string<I8251::CmdPhase>> cmdFazeInfo = {
341 { "MODE", I8251::CmdPhase::MODE },
342 { "SYNC1", I8251::CmdPhase::SYNC1 },
343 { "SYNC2", I8251::CmdPhase::SYNC2 },
344 { "CMD", I8251::CmdPhase::CMD }
345};
347
348// version 1: initial version
349// version 2: removed 'userData' from Schedulable
350template<typename Archive>
351void I8251::serialize(Archive& ar, unsigned version)
352{
353 if (ar.versionAtLeast(version, 2)) {
354 ar.serialize("syncRecv", syncRecv,
355 "syncTrans", syncTrans);
356 } else {
357 Schedulable::restoreOld(ar, {&syncRecv, &syncTrans});
358 }
359 ar.serialize("clock", clock,
360 "charLength", charLength,
361 "recvDataBits", recvDataBits,
362 "recvStopBits", recvStopBits,
363 "recvParityBit", recvParityBit,
364 "recvParityEnabled", recvParityEnabled,
365 "recvBuf", recvBuf,
366 "recvReady", recvReady,
367 "sendByte", sendByte,
368 "sendBuffer", sendBuffer,
369 "status", status,
370 "command", command,
371 "mode", mode,
372 "sync1", sync1,
373 "sync2", sync2,
374 "cmdFaze", cmdPhase); // TODO fix spelling error if we ever need to upgrade this savestate format
375}
377
378} // namespace openmsx
bool isPeriodic() const
Definition ClockPin.hh:34
EmuDuration::param getTotalDuration() const
Definition ClockPin.cc:64
virtual bool getDSR(EmuTime::param time)=0
virtual void setRxRDY(bool status, EmuTime::param time)=0
virtual void setDTR(bool status, EmuTime::param time)=0
virtual void setRTS(bool status, EmuTime::param time)=0
virtual void signal(EmuTime::param time)=0
byte peekIO(word port, EmuTime::param time) const
Definition I8251.cc:86
void recvByte(byte value, EmuTime::param time) override
Definition I8251.cc:264
I8251(Scheduler &scheduler, I8251Interface &interface, EmuTime::param time)
Definition I8251.cc:47
void execRecv(EmuTime::param time)
Definition I8251.cc:297
void writeIO(word port, byte value, EmuTime::param time)
Definition I8251.cc:96
void reset(EmuTime::param time)
Definition I8251.cc:55
void execTrans(EmuTime::param time)
Definition I8251.cc:304
byte readIO(word port, EmuTime::param time)
Definition I8251.cc:77
void setParityBit(bool enable, Parity parity) override
Definition I8251.cc:258
void serialize(Archive &ar, unsigned version)
Definition I8251.cc:351
bool isRecvEnabled() const
Definition I8251.cc:282
static void restoreOld(Archive &ar, std::vector< Schedulable * > schedulables)
virtual void recvByte(byte value, EmuTime::param time)=0
virtual void setStopBits(StopBits bits)=0
virtual void setParityBit(bool enable, Parity parity)=0
virtual void setDataBits(DataBits bits)=0
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define SERIALIZE_ENUM(TYPE, INFO)
#define UNREACHABLE