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 namespace openmsx {
7 
8 constexpr byte STAT_TXRDY = 0x01;
9 constexpr byte STAT_RXRDY = 0x02;
10 constexpr byte STAT_TXEMPTY = 0x04;
11 constexpr byte STAT_PE = 0x08;
12 constexpr byte STAT_OE = 0x10;
13 constexpr byte STAT_FE = 0x20;
14 constexpr byte STAT_SYNBRK = 0x40;
15 constexpr byte STAT_DSR = 0x80;
16 
17 constexpr byte MODE_BAUDRATE = 0x03;
18 constexpr byte MODE_SYNCHRONOUS = 0x00;
19 constexpr byte MODE_RATE1 = 0x01;
20 constexpr byte MODE_RATE16 = 0x02;
21 constexpr byte MODE_RATE64 = 0x03;
22 constexpr byte MODE_WORDLENGTH = 0x0C;
23 constexpr byte MODE_5BIT = 0x00;
24 constexpr byte MODE_6BIT = 0x04;
25 constexpr byte MODE_7BIT = 0x08;
26 constexpr byte MODE_8BIT = 0x0C;
27 constexpr byte MODE_PARITYEN = 0x10;
28 constexpr byte MODE_PARITODD = 0x00;
29 constexpr byte MODE_PARITEVEN = 0x20;
30 constexpr byte MODE_STOP_BITS = 0xC0;
31 constexpr byte MODE_STOP_INV = 0x00;
32 constexpr byte MODE_STOP_1 = 0x40;
33 constexpr byte MODE_STOP_15 = 0x80;
34 constexpr byte MODE_STOP_2 = 0xC0;
35 constexpr byte MODE_SINGLESYNC = 0x80;
36 
37 constexpr byte CMD_TXEN = 0x01;
38 constexpr byte CMD_DTR = 0x02;
39 constexpr byte CMD_RXE = 0x04;
40 constexpr byte CMD_SBRK = 0x08;
41 constexpr byte CMD_RSTERR = 0x10;
42 constexpr byte CMD_RTS = 0x20;
43 constexpr byte CMD_RESET = 0x40;
44 constexpr byte CMD_HUNT = 0x80;
45 
46 
47 I8251::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 
55 void 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 
77 byte 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 
86 byte 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 
96 void 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:
132  UNREACHABLE;
133  }
134  break;
135  default:
136  UNREACHABLE;
137  }
138 }
139 
140 void I8251::setMode(byte newMode)
141 {
142  mode = newMode;
143 
144  auto dataBits = [&] {
145  switch (mode & MODE_WORDLENGTH) {
150  default: UNREACHABLE; return SerialDataInterface::DATA_8;
151  }
152  }();
153  interf.setDataBits(dataBits);
154 
155  auto stopBits = [&] {
156  switch(mode & MODE_STOP_BITS) {
161  default: UNREACHABLE; return SerialDataInterface::STOP_2;
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 
185 void 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 
225 byte 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 
234 byte I8251::readTrans(EmuTime::param time)
235 {
236  status &= ~STAT_RXRDY;
237  interf.setRxRDY(false, time);
238  return recvBuf;
239 }
240 
241 void 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 
255 void I8251::setParityBit(bool enable, ParityBit parity)
256 {
257  recvParityEnabled = enable;
258  recvParityBit = parity;
259 }
260 
261 void 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);
275  syncRecv.setSyncPoint(next);
276  }
277 }
278 
280 {
281  return (command & CMD_RXE) != 0;
282 }
283 
284 void 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);
290  syncTrans.setSyncPoint(next);
291  }
292 }
293 
294 void I8251::execRecv(EmuTime::param time)
295 {
296  assert(command & CMD_RXE);
297  recvReady = true;
298  interf.signal(time);
299 }
300 
301 void 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 
315 static constexpr std::initializer_list<enum_string<SerialDataInterface::DataBits>> dataBitsInfo = {
320 };
322 
323 static constexpr std::initializer_list<enum_string<SerialDataInterface::StopBits>> stopBitsInfo = {
324  { "INVALID", SerialDataInterface::STOP_INV },
326  { "1.5", SerialDataInterface::STOP_15 },
328 };
330 
331 static constexpr std::initializer_list<enum_string<SerialDataInterface::ParityBit>> parityBitInfo = {
332  { "EVEN", SerialDataInterface::EVEN },
333  { "ODD", SerialDataInterface::ODD }
334 };
336 
337 static 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
347 template<typename Archive>
348 void 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:998
#define UNREACHABLE
Definition: unreachable.hh:38