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
8constexpr byte STAT_TXRDY = 0x01;
9constexpr byte STAT_RXRDY = 0x02;
10constexpr byte STAT_TXEMPTY = 0x04;
11constexpr byte STAT_PE = 0x08;
12constexpr byte STAT_OE = 0x10;
13constexpr byte STAT_FE = 0x20;
14constexpr byte STAT_SYNBRK = 0x40;
15constexpr byte STAT_DSR = 0x80;
16
17constexpr byte MODE_BAUDRATE = 0x03;
18constexpr byte MODE_SYNCHRONOUS = 0x00;
19constexpr byte MODE_RATE1 = 0x01;
20constexpr byte MODE_RATE16 = 0x02;
21constexpr byte MODE_RATE64 = 0x03;
22constexpr byte MODE_WORDLENGTH = 0x0C;
23constexpr byte MODE_5BIT = 0x00;
24constexpr byte MODE_6BIT = 0x04;
25constexpr byte MODE_7BIT = 0x08;
26constexpr byte MODE_8BIT = 0x0C;
27constexpr byte MODE_PARITYEN = 0x10;
28constexpr byte MODE_PARITODD = 0x00;
29constexpr byte MODE_PARITEVEN = 0x20;
30constexpr byte MODE_STOP_BITS = 0xC0;
31constexpr byte MODE_STOP_INV = 0x00;
32constexpr byte MODE_STOP_1 = 0x40;
33constexpr byte MODE_STOP_15 = 0x80;
34constexpr byte MODE_STOP_2 = 0xC0;
35constexpr byte MODE_SINGLESYNC = 0x80;
36
37constexpr byte CMD_TXEN = 0x01;
38constexpr byte CMD_DTR = 0x02;
39constexpr byte CMD_RXE = 0x04;
40constexpr byte CMD_SBRK = 0x08;
41constexpr byte CMD_RSTERR = 0x10;
42constexpr byte CMD_RTS = 0x20;
43constexpr byte CMD_RESET = 0x40;
44constexpr byte CMD_HUNT = 0x80;
45
46
47I8251::I8251(Scheduler& scheduler, I8251Interface& interf_, EmuTime::param time)
48 : syncRecv (scheduler)
49 , syncTrans(scheduler)
50 , interf(interf_), 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;
60 recvDataBits = SerialDataInterface::DATA_8;
61 recvStopBits = SerialDataInterface::STOP_1;
62 recvParityBit = SerialDataInterface::EVEN;
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 cmdFaze = FAZE_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; return 0;
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; return 0;
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 (cmdFaze) {
104 case FAZE_MODE:
105 setMode(value);
106 if ((mode & MODE_BAUDRATE) == MODE_SYNCHRONOUS) {
107 cmdFaze = FAZE_SYNC1;
108 } else {
109 cmdFaze = FAZE_CMD;
110 }
111 break;
112 case FAZE_SYNC1:
113 sync1 = value;
114 if (mode & MODE_SINGLESYNC) {
115 cmdFaze = FAZE_CMD;
116 } else {
117 cmdFaze = FAZE_SYNC2;
118 }
119 break;
120 case FAZE_SYNC2:
121 sync2 = value;
122 cmdFaze = FAZE_CMD;
123 break;
124 case FAZE_CMD:
125 if (value & CMD_RESET) {
126 cmdFaze = FAZE_MODE;
127 } else {
128 writeCommand(value, time);
129 }
130 break;
131 default:
133 }
134 break;
135 default:
137 }
138}
139
140void I8251::setMode(byte newMode)
141{
142 mode = newMode;
143
144 auto dataBits = [&] {
145 switch (mode & MODE_WORDLENGTH) {
151 }
152 }();
153 interf.setDataBits(dataBits);
154
155 auto stopBits = [&] {
156 switch(mode & MODE_STOP_BITS) {
162 }
163 }();
164 interf.setStopBits(stopBits);
165
166 bool parityEnable = (mode & MODE_PARITYEN) != 0;
169 interf.setParityBit(parityEnable, parity);
170
171 unsigned baudrate = [&] {
172 switch (mode & MODE_BAUDRATE) {
173 case MODE_SYNCHRONOUS: return 1;
174 case MODE_RATE1: return 1;
175 case MODE_RATE16: return 16;
176 case MODE_RATE64: return 64;
177 default: UNREACHABLE; return 1;
178 }
179 }();
180
181 charLength = (((2 * (1 + unsigned(dataBits) + (parityEnable ? 1 : 0))) +
182 unsigned(stopBits)) * baudrate) / 2;
183}
184
185void I8251::writeCommand(byte value, EmuTime::param time)
186{
187 byte oldCommand = command;
188 command = value;
189
190 // CMD_RESET, CMD_TXEN, CMD_RXE handled in other routines
191
192 interf.setRTS((command & CMD_RTS) != 0, time);
193 interf.setDTR((command & CMD_DTR) != 0, time);
194
195 if (!(command & CMD_TXEN)) {
196 // disable transmitter
198 status |= STAT_TXRDY | STAT_TXEMPTY;
199 }
200 if (command & CMD_RSTERR) {
201 status &= ~(STAT_PE | STAT_OE | STAT_FE);
202 }
203 if (command & CMD_SBRK) {
204 // TODO
205 }
206 if (command & CMD_HUNT) {
207 // TODO
208 }
209
210 if ((command ^ oldCommand) & CMD_RXE) {
211 if (command & CMD_RXE) {
212 // enable receiver
213 status &= ~(STAT_PE | STAT_OE | STAT_FE); // TODO
214 recvReady = true;
215 } else {
216 // disable receiver
218 status &= ~(STAT_PE | STAT_OE | STAT_FE); // TODO
219 status &= ~STAT_RXRDY;
220 }
221 interf.signal(time);
222 }
223}
224
225byte I8251::readStatus(EmuTime::param time)
226{
227 byte result = status;
228 if (interf.getDSR(time)) {
229 result |= STAT_DSR;
230 }
231 return result;
232}
233
234byte I8251::readTrans(EmuTime::param time)
235{
236 status &= ~STAT_RXRDY;
237 interf.setRxRDY(false, time);
238 return recvBuf;
239}
240
241void I8251::writeTrans(byte value, EmuTime::param time)
242{
243 if (!(command & CMD_TXEN)) {
244 return;
245 }
246 if (status & STAT_TXEMPTY) {
247 // not sending
248 send(value, time);
249 } else {
250 sendBuffer = value;
251 status &= ~STAT_TXRDY;
252 }
253}
254
255void I8251::setParityBit(bool enable, ParityBit parity)
256{
257 recvParityEnabled = enable;
258 recvParityBit = parity;
259}
260
261void I8251::recvByte(byte value, EmuTime::param time)
262{
263 // TODO STAT_PE / STAT_FE / STAT_SYNBRK
264 assert(recvReady && (command & CMD_RXE));
265 if (status & STAT_RXRDY) {
266 status |= STAT_OE;
267 } else {
268 recvBuf = value;
269 status |= STAT_RXRDY;
270 interf.setRxRDY(true, time);
271 }
272 recvReady = false;
273 if (clock.isPeriodic()) {
274 EmuTime next = time + (clock.getTotalDuration() * charLength);
276 }
277}
278
280{
281 return (command & CMD_RXE) != 0;
282}
283
284void I8251::send(byte value, EmuTime::param time)
285{
286 status &= ~STAT_TXEMPTY;
287 sendByte = value;
288 if (clock.isPeriodic()) {
289 EmuTime next = time + (clock.getTotalDuration() * charLength);
291 }
292}
293
294void I8251::execRecv(EmuTime::param time)
295{
296 assert(command & CMD_RXE);
297 recvReady = true;
298 interf.signal(time);
299}
300
301void I8251::execTrans(EmuTime::param time)
302{
303 assert(!(status & STAT_TXEMPTY) && (command & CMD_TXEN));
304
305 interf.recvByte(sendByte, time);
306 if (status & STAT_TXRDY) {
307 status |= STAT_TXEMPTY;
308 } else {
309 status |= STAT_TXRDY;
310 send(sendBuffer, time);
311 }
312}
313
314
315static constexpr std::initializer_list<enum_string<SerialDataInterface::DataBits>> dataBitsInfo = {
320};
322
323static constexpr std::initializer_list<enum_string<SerialDataInterface::StopBits>> stopBitsInfo = {
324 { "INVALID", SerialDataInterface::STOP_INV },
328};
330
331static constexpr std::initializer_list<enum_string<SerialDataInterface::ParityBit>> parityBitInfo = {
332 { "EVEN", SerialDataInterface::EVEN },
333 { "ODD", SerialDataInterface::ODD }
334};
336
337static constexpr std::initializer_list<enum_string<I8251::CmdFaze>> cmdFazeInfo = {
338 { "MODE", I8251::FAZE_MODE },
339 { "SYNC1", I8251::FAZE_SYNC1 },
340 { "SYNC2", I8251::FAZE_SYNC2 },
341 { "CMD", I8251::FAZE_CMD }
342};
344
345// version 1: initial version
346// version 2: removed 'userData' from Schedulable
347template<typename Archive>
348void I8251::serialize(Archive& ar, unsigned version)
349{
350 if (ar.versionAtLeast(version, 2)) {
351 ar.serialize("syncRecv", syncRecv,
352 "syncTrans", syncTrans);
353 } else {
355 }
356 ar.serialize("clock", clock,
357 "charLength", charLength,
358 "recvDataBits", recvDataBits,
359 "recvStopBits", recvStopBits,
360 "recvParityBit", recvParityBit,
361 "recvParityEnabled", recvParityEnabled,
362 "recvBuf", recvBuf,
363 "recvReady", recvReady,
364 "sendByte", sendByte,
365 "sendBuffer", sendBuffer,
366 "status", status,
367 "command", command,
368 "mode", mode,
369 "sync1", sync1,
370 "sync2", sync2,
371 "cmdFaze", cmdFaze);
372}
374
375} // namespace openmsx
bool isPeriodic() const
Definition: ClockPin.hh:34
EmuDuration::param getTotalDuration() const
Definition: ClockPin.cc:65
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:261
I8251(Scheduler &scheduler, I8251Interface &interf, EmuTime::param time)
Definition: I8251.cc:47
void execRecv(EmuTime::param time)
Definition: I8251.cc:294
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 reset(EmuTime::param time)
Definition: I8251.cc:55
openmsx::I8251::SyncRecv syncRecv
void execTrans(EmuTime::param time)
Definition: I8251.cc:301
byte readIO(word port, EmuTime::param time)
Definition: I8251.cc:77
openmsx::I8251::SyncTrans syncTrans
void serialize(Archive &ar, unsigned version)
Definition: I8251.cc:348
bool isRecvEnabled() const
Definition: I8251.cc:279
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
static void restoreOld(Archive &ar, std::vector< Schedulable * > schedulables)
Definition: Schedulable.hh:77
virtual void recvByte(byte value, EmuTime::param time)=0
virtual void setStopBits(StopBits bits)=0
virtual void setParityBit(bool enable, ParityBit parity)=0
virtual void setDataBits(DataBits bits)=0
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr byte MODE_STOP_1
Definition: I8251.cc:32
constexpr byte STAT_OE
Definition: I8251.cc:12
constexpr byte MODE_WORDLENGTH
Definition: I8251.cc:22
constexpr byte CMD_RESET
Definition: I8251.cc:43
constexpr byte STAT_FE
Definition: I8251.cc:13
constexpr byte MODE_STOP_2
Definition: I8251.cc:34
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
constexpr byte MODE_RATE1
Definition: I8251.cc:19
constexpr byte STAT_PE
Definition: I8251.cc:11
constexpr byte STAT_DSR
Definition: I8251.cc:15
constexpr byte MODE_6BIT
Definition: I8251.cc:24
constexpr byte MODE_8BIT
Definition: I8251.cc:26
constexpr byte MODE_7BIT
Definition: I8251.cc:25
constexpr byte CMD_RTS
Definition: I8251.cc:42
constexpr byte CMD_TXEN
Definition: I8251.cc:37
constexpr byte MODE_SYNCHRONOUS
Definition: I8251.cc:18
constexpr byte MODE_PARITEVEN
Definition: I8251.cc:29
constexpr byte MODE_RATE16
Definition: I8251.cc:20
constexpr byte CMD_RXE
Definition: I8251.cc:39
constexpr byte MODE_SINGLESYNC
Definition: I8251.cc:35
constexpr byte MODE_STOP_BITS
Definition: I8251.cc:30
constexpr byte MODE_RATE64
Definition: I8251.cc:21
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
constexpr byte MODE_BAUDRATE
Definition: I8251.cc:17
constexpr byte STAT_TXEMPTY
Definition: I8251.cc:10
constexpr byte CMD_DTR
Definition: I8251.cc:38
constexpr byte MODE_PARITYEN
Definition: I8251.cc:27
constexpr byte STAT_RXRDY
Definition: I8251.cc:9
constexpr byte MODE_5BIT
Definition: I8251.cc:23
constexpr byte STAT_SYNBRK
Definition: I8251.cc:14
constexpr byte CMD_RSTERR
Definition: I8251.cc:41
constexpr byte CMD_HUNT
Definition: I8251.cc:44
constexpr byte STAT_TXRDY
Definition: I8251.cc:8
constexpr byte CMD_SBRK
Definition: I8251.cc:40
constexpr byte MODE_STOP_15
Definition: I8251.cc:33
constexpr byte MODE_PARITODD
Definition: I8251.cc:28
constexpr byte MODE_STOP_INV
Definition: I8251.cc:31
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009
#define UNREACHABLE
Definition: unreachable.hh:38