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 const byte STAT_TXRDY = 0x01;
11 const byte STAT_RXRDY = 0x02;
12 const byte STAT_TXEMPTY = 0x04;
13 const byte STAT_PE = 0x08;
14 const byte STAT_OE = 0x10;
15 const byte STAT_FE = 0x20;
16 const byte STAT_SYNBRK = 0x40;
17 const byte STAT_DSR = 0x80;
18 
19 const byte MODE_BAUDRATE = 0x03;
20 const byte MODE_SYNCHRONOUS = 0x00;
21 const byte MODE_RATE1 = 0x01;
22 const byte MODE_RATE16 = 0x02;
23 const byte MODE_RATE64 = 0x03;
24 const byte MODE_WORDLENGTH = 0x0C;
25 const byte MODE_5BIT = 0x00;
26 const byte MODE_6BIT = 0x04;
27 const byte MODE_7BIT = 0x08;
28 const byte MODE_8BIT = 0x0C;
29 const byte MODE_PARITYEN = 0x10;
30 const byte MODE_PARITODD = 0x00;
31 const byte MODE_PARITEVEN = 0x20;
32 const byte MODE_STOP_BITS = 0xC0;
33 const byte MODE_STOP_INV = 0x00;
34 const byte MODE_STOP_1 = 0x40;
35 const byte MODE_STOP_15 = 0x80;
36 const byte MODE_STOP_2 = 0xC0;
37 const byte MODE_SINGLESYNC = 0x80;
38 
39 const byte CMD_TXEN = 0x01;
40 const byte CMD_DTR = 0x02;
41 const byte CMD_RXE = 0x04;
42 const byte CMD_SBRK = 0x08;
43 const byte CMD_RSTERR = 0x10;
44 const byte CMD_RTS = 0x20;
45 const byte CMD_RESET = 0x40;
46 const byte CMD_HUNT = 0x80;
47 
50 };
51 
52 
54  : Schedulable(scheduler), interf(interf_), clock(scheduler)
55 {
56  reset(time);
57 }
58 
60 {
61  // initialize these to avoid UMR on savestate
62  // TODO investigate correct initial state after reset
63  charLength = 0;
64  recvDataBits = SerialDataInterface::DATA_8;
65  recvStopBits = SerialDataInterface::STOP_1;
66  recvParityBit = SerialDataInterface::EVEN;
67  recvParityEnabled = false;
68  recvBuf = 0;
69  recvReady = false;
70  sendByte = 0;
71  sendBuffer = 0;
72  mode = 0;
73  sync1 = sync2 = 0;
74 
75  status = STAT_TXRDY | STAT_TXEMPTY;
76  command = 0xFF; // make sure all bits change
77  writeCommand(0, time);
78  cmdFaze = FAZE_MODE;
79 }
80 
82 {
83  byte result;
84  switch (port & 1) {
85  case 0:
86  result = readTrans(time);
87  break;
88  case 1:
89  result = readStatus(time);
90  break;
91  default:
92  UNREACHABLE; return 0;
93  }
94  return result;
95 }
96 
97 byte I8251::peekIO(word port, EmuTime::param /*time*/) const
98 {
99  switch (port & 1) {
100  case 0:
101  return recvBuf;
102  case 1:
103  return status; // TODO peekStatus()
104  default:
105  UNREACHABLE; return 0;
106  }
107 }
108 
109 
110 void I8251::writeIO(word port, byte value, EmuTime::param time)
111 {
112  switch (port & 1) {
113  case 0:
114  writeTrans(value, time);
115  break;
116  case 1:
117  switch (cmdFaze) {
118  case FAZE_MODE:
119  setMode(value);
120  if ((mode & MODE_BAUDRATE) == MODE_SYNCHRONOUS) {
121  cmdFaze = FAZE_SYNC1;
122  } else {
123  cmdFaze = FAZE_CMD;
124  }
125  break;
126  case FAZE_SYNC1:
127  sync1 = value;
128  if (mode & MODE_SINGLESYNC) {
129  cmdFaze = FAZE_CMD;
130  } else {
131  cmdFaze = FAZE_SYNC2;
132  }
133  break;
134  case FAZE_SYNC2:
135  sync2 = value;
136  cmdFaze = FAZE_CMD;
137  break;
138  case FAZE_CMD:
139  if (value & CMD_RESET) {
140  cmdFaze = FAZE_MODE;
141  } else {
142  writeCommand(value, time);
143  }
144  break;
145  default:
146  UNREACHABLE;
147  }
148  break;
149  default:
150  UNREACHABLE;
151  }
152 }
153 
154 void I8251::setMode(byte value)
155 {
156  mode = value;
157 
159  switch (mode & MODE_WORDLENGTH) {
160  case MODE_5BIT:
161  dataBits = SerialDataInterface::DATA_5;
162  break;
163  case MODE_6BIT:
164  dataBits = SerialDataInterface::DATA_6;
165  break;
166  case MODE_7BIT:
167  dataBits = SerialDataInterface::DATA_7;
168  break;
169  case MODE_8BIT:
170  dataBits = SerialDataInterface::DATA_8;
171  break;
172  default:
173  UNREACHABLE;
174  dataBits = SerialDataInterface::DATA_8;
175  }
176  interf.setDataBits(dataBits);
177 
179  switch(mode & MODE_STOP_BITS) {
180  case MODE_STOP_INV:
182  break;
183  case MODE_STOP_1:
184  stopBits = SerialDataInterface::STOP_1;
185  break;
186  case MODE_STOP_15:
187  stopBits = SerialDataInterface::STOP_15;
188  break;
189  case MODE_STOP_2:
190  stopBits = SerialDataInterface::STOP_2;
191  break;
192  default:
193  UNREACHABLE;
194  stopBits = SerialDataInterface::STOP_2;
195  }
196  interf.setStopBits(stopBits);
197 
198  bool parityEnable = (mode & MODE_PARITYEN) != 0;
201  interf.setParityBit(parityEnable, parity);
202 
203  unsigned baudrate;
204  switch (mode & MODE_BAUDRATE) {
205  case MODE_SYNCHRONOUS:
206  baudrate = 1;
207  break;
208  case MODE_RATE1:
209  baudrate = 1;
210  break;
211  case MODE_RATE16:
212  baudrate = 16;
213  break;
214  case MODE_RATE64:
215  baudrate = 64;
216  break;
217  default:
218  UNREACHABLE;
219  baudrate = 1;
220  }
221 
222  charLength = (((2 * (1 + unsigned(dataBits) + (parityEnable ? 1 : 0))) +
223  unsigned(stopBits)) * baudrate) / 2;
224 }
225 
226 void I8251::writeCommand(byte value, EmuTime::param time)
227 {
228  byte oldCommand = command;
229  command = value;
230 
231  // CMD_RESET, CMD_TXEN, CMD_RXE handled in other routines
232 
233  interf.setRTS((command & CMD_RTS) != 0, time);
234  interf.setDTR((command & CMD_DTR) != 0, time);
235 
236  if (!(command & CMD_TXEN)) {
237  // disable transmitter
239  status |= STAT_TXRDY | STAT_TXEMPTY;
240  }
241  if (command & CMD_RSTERR) {
242  status &= ~(STAT_PE | STAT_OE | STAT_FE);
243  }
244  if (command & CMD_SBRK) {
245  // TODO
246  }
247  if (command & CMD_HUNT) {
248  // TODO
249  }
250 
251  if ((command ^ oldCommand) & CMD_RXE) {
252  if (command & CMD_RXE) {
253  // enable receiver
254  status &= ~(STAT_PE | STAT_OE | STAT_FE); // TODO
255  recvReady = true;
256  } else {
257  // disable receiver
259  status &= ~(STAT_PE | STAT_OE | STAT_FE); // TODO
260  status &= ~STAT_RXRDY;
261  }
262  interf.signal(time);
263  }
264 }
265 
266 byte I8251::readStatus(EmuTime::param time)
267 {
268  byte result = status;
269  if (interf.getDSR(time)) {
270  result |= STAT_DSR;
271  }
272  return result;
273 }
274 
275 byte I8251::readTrans(EmuTime::param time)
276 {
277  status &= ~STAT_RXRDY;
278  interf.setRxRDY(false, time);
279  return recvBuf;
280 }
281 
282 void I8251::writeTrans(byte value, EmuTime::param time)
283 {
284  if (!(command & CMD_TXEN)) {
285  return;
286  }
287  if (status & STAT_TXEMPTY) {
288  // not sending
289  send(value, time);
290  } else {
291  sendBuffer = value;
292  status &= ~STAT_TXRDY;
293  }
294 }
295 
297 {
298  return clock;
299 }
300 
301 
303 {
304  recvDataBits = bits;
305 }
306 
308 {
309  recvStopBits = bits;
310 }
311 
312 void I8251::setParityBit(bool enable, ParityBit parity)
313 {
314  recvParityEnabled = enable;
315  recvParityBit = parity;
316 }
317 
319 {
320  // TODO STAT_PE / STAT_FE / STAT_SYNBRK
321  assert(recvReady && (command & CMD_RXE));
322  if (status & STAT_RXRDY) {
323  status |= STAT_OE;
324  } else {
325  recvBuf = value;
326  status |= STAT_RXRDY;
327  interf.setRxRDY(true, time);
328  }
329  recvReady = false;
330  if (clock.isPeriodic()) {
331  EmuTime next = time + (clock.getTotalDuration() * charLength);
332  setSyncPoint(next, RECV);
333  }
334 }
335 
337 {
338  return recvReady;
339 }
341 {
342  return (command & CMD_RXE) != 0;
343 }
344 
345 void I8251::send(byte value, EmuTime::param time)
346 {
347  status &= ~STAT_TXEMPTY;
348  sendByte = value;
349  if (clock.isPeriodic()) {
350  EmuTime next = time + (clock.getTotalDuration() * charLength);
351  setSyncPoint(next, TRANS);
352  }
353 }
354 
355 void I8251::executeUntil(EmuTime::param time, int userData)
356 {
357  switch (userData) {
358  case RECV:
359  assert(command & CMD_RXE);
360  recvReady = true;
361  interf.signal(time);
362  break;
363  case TRANS:
364  assert(!(status & STAT_TXEMPTY) && (command & CMD_TXEN));
365 
366  interf.recvByte(sendByte, time);
367  if (status & STAT_TXRDY) {
368  status |= STAT_TXEMPTY;
369  } else {
370  status |= STAT_TXRDY;
371  send(sendBuffer, time);
372  }
373  break;
374  default:
375  UNREACHABLE;
376  }
377 }
378 
379 
380 static enum_string<SerialDataInterface::DataBits> dataBitsInfo[] = {
385 };
387 
388 static enum_string<SerialDataInterface::StopBits> stopBitsInfo[] = {
389  { "INVALID", SerialDataInterface::STOP_INV },
391  { "1.5", SerialDataInterface::STOP_15 },
393 };
395 
396 static enum_string<SerialDataInterface::ParityBit> parityBitInfo[] = {
397  { "EVEN", SerialDataInterface::EVEN },
398  { "ODD", SerialDataInterface::ODD }
399 };
401 
402 static enum_string<I8251::CmdFaze> cmdFazeInfo[] = {
403  { "MODE", I8251::FAZE_MODE },
404  { "SYNC1", I8251::FAZE_SYNC1 },
405  { "SYNC2", I8251::FAZE_SYNC2 },
406  { "CMD", I8251::FAZE_CMD }
407 };
408 SERIALIZE_ENUM(I8251::CmdFaze, cmdFazeInfo);
409 
410 template<typename Archive>
411 void I8251::serialize(Archive& ar, unsigned /*version*/)
412 {
413  ar.template serializeBase<Schedulable>(*this);
414  ar.serialize("clock", clock);
415  ar.serialize("charLength", charLength);
416  ar.serialize("recvDataBits", recvDataBits);
417  ar.serialize("recvStopBits", recvStopBits);
418  ar.serialize("recvParityBit", recvParityBit);
419  ar.serialize("recvParityEnabled", recvParityEnabled);
420  ar.serialize("recvBuf", recvBuf);
421  ar.serialize("recvReady", recvReady);
422  ar.serialize("sendByte", sendByte);
423  ar.serialize("sendBuffer", sendBuffer);
424  ar.serialize("status", status);
425  ar.serialize("command", command);
426  ar.serialize("mode", mode);
427  ar.serialize("sync1", sync1);
428  ar.serialize("sync2", sync2);
429  ar.serialize("cmdFaze", cmdFaze);
430 }
432 
433 } // namespace openmsx