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