openMSX
MC6850.cc
Go to the documentation of this file.
1 #include "MC6850.hh"
2 #include "MidiInDevice.hh"
3 #include "MSXMotherBoard.hh"
4 #include "EmuTime.hh"
5 #include "serialize.hh"
6 
7 namespace openmsx {
8 
9 // control register bits
10 constexpr unsigned CR_CDS1 = 0x01; // Counter Divide Select 1
11 constexpr unsigned CR_CDS2 = 0x02; // Counter Divide Select 2
12 constexpr unsigned CR_CDS = CR_CDS1 | CR_CDS2;
13 constexpr unsigned CR_MR = CR_CDS1 | CR_CDS2; // Master Reset
14 // CDS2 CDS1
15 // 0 0 divide by 1
16 // 0 1 divide by 16
17 // 1 0 divide by 64
18 // 1 1 master reset (!)
19 
20 constexpr unsigned CR_WS1 = 0x04; // Word Select 1 (mostly parity)
21 constexpr unsigned CR_WS2 = 0x08; // Word Select 2 (mostly nof stop bits)
22 constexpr unsigned CR_WS3 = 0x10; // Word Select 3: 7/8 bits
23 constexpr unsigned CR_WS = CR_WS1 | CR_WS2 | CR_WS3; // Word Select
24 // WS3 WS2 WS1
25 // 0 0 0 7 bits - 2 stop bits - Even parity
26 // 0 0 1 7 bits - 2 stop bits - Odd parity
27 // 0 1 0 7 bits - 1 stop bit - Even parity
28 // 0 1 1 7 bits - 1 stop bit - Odd Parity
29 // 1 0 0 8 bits - 2 stop bits - No Parity
30 // 1 0 1 8 bits - 1 stop bit - No Parity
31 // 1 1 0 8 bits - 1 stop bit - Even parity
32 // 1 1 1 8 bits - 1 stop bit - Odd parity
33 
34 constexpr unsigned CR_TC1 = 0x20; // Transmit Control 1
35 constexpr unsigned CR_TC2 = 0x40; // Transmit Control 2
36 constexpr unsigned CR_TC = CR_TC1 | CR_TC2; // Transmit Control
37 // TC2 TC1
38 // 0 0 /RTS low, Transmitting Interrupt disabled
39 // 0 1 /RTS low, Transmitting Interrupt enabled
40 // 1 0 /RTS high, Transmitting Interrupt disabled
41 // 1 1 /RTS low, Transmits a Break level on the Transmit Data Output.
42 // Interrupt disabled
43 
44 constexpr unsigned CR_RIE = 0x80; // Receive Interrupt Enable: interrupt
45 // at Receive Data Register Full, Overrun, low-to-high transition on the Data
46 // Carrier Detect (/DCD) signal line
47 
48 // status register bits
49 constexpr unsigned STAT_RDRF = 0x01; // Receive Data Register Full
50 constexpr unsigned STAT_TDRE = 0x02; // Transmit Data Register Empty
51 constexpr unsigned STAT_DCD = 0x04; // Data Carrier Detect (/DCD)
52 constexpr unsigned STAT_CTS = 0x08; // Clear-to-Send (/CTS)
53 constexpr unsigned STAT_FE = 0x10; // Framing Error
54 constexpr unsigned STAT_OVRN = 0x20; // Receiver Overrun
55 constexpr unsigned STAT_PE = 0x40; // Parity Error
56 constexpr unsigned STAT_IRQ = 0x80; // Interrupt Request (/IRQ)
57 
58 MC6850::MC6850(const std::string& name_, MSXMotherBoard& motherBoard, unsigned clockFreq_)
59  : MidiInConnector(motherBoard.getPluggingController(), name_ + "-in")
60  , syncRecv (motherBoard.getScheduler())
61  , syncTrans(motherBoard.getScheduler())
62  , txClock(EmuTime::zero())
63  , clockFreq(clockFreq_)
64  , rxIRQ(motherBoard, name_ + "-rx-IRQ")
65  , txIRQ(motherBoard, name_ + "-tx-IRQ")
66  , txDataReg(0), txShiftReg(0) // avoid UMR
67  , outConnector(motherBoard.getPluggingController(), name_ + "-out")
68 {
69  reset(EmuTime::zero());
70  setDataFormat();
71 }
72 
73 // (Re-)initialize chip to default values (Tx and Rx disabled)
74 void MC6850::reset(EmuTime::param time)
75 {
76  syncRecv .removeSyncPoint();
77  syncTrans.removeSyncPoint();
78  txClock.reset(time);
79  txClock.setFreq(clockFreq);
80  rxIRQ.reset();
81  txIRQ.reset();
82  rxReady = false;
83  txShiftRegValid = false;
84  pendingOVRN = false;
85  controlReg = CR_MR;
86  statusReg = 0;
87  rxDataReg = 0;
88  setDataFormat();
89 }
90 
92 {
93  return peekStatusReg();
94 }
95 
97 {
98  byte result = statusReg;
99  if (rxIRQ.getState() || txIRQ.getState()) result |= STAT_IRQ;
100  return result;
101 }
102 
104 {
105  byte result = peekDataReg();
106  statusReg &= ~(STAT_RDRF | STAT_OVRN);
107  if (pendingOVRN) {
108  pendingOVRN = false;
109  statusReg |= STAT_OVRN;
110  }
111  rxIRQ.reset();
112  return result;
113 }
114 
116 {
117  return rxDataReg;
118 }
119 
120 void MC6850::writeControlReg(byte value, EmuTime::param time)
121 {
122  byte diff = value ^ controlReg;
123  if (diff & CR_CDS) {
124  if ((value & CR_CDS) == CR_MR) {
125  reset(time);
126  } else {
127  // we got out of MR state
128  rxReady = true;
129  statusReg |= STAT_TDRE;
130 
131  txClock.reset(time);
132  switch (value & CR_CDS) {
133  case 0: txClock.setFreq(clockFreq, 1); break;
134  case 1: txClock.setFreq(clockFreq, 16); break;
135  case 2: txClock.setFreq(clockFreq, 64); break;
136  }
137  }
138  }
139 
140  controlReg = value;
141  if (diff & CR_WS) setDataFormat();
142 
143  // update IRQ status
144  rxIRQ.set(( value & CR_RIE) && (statusReg & STAT_RDRF));
145  txIRQ.set(((value & CR_TC) == 0x20) && (statusReg & STAT_TDRE));
146 }
147 
148 // Sync data-format related parameters with the current value of controlReg
149 void MC6850::setDataFormat()
150 {
151  outConnector.setDataBits(controlReg & CR_WS3 ? DATA_8 : DATA_7);
152 
153  StopBits stopBits[8] = {
156  };
157  outConnector.setStopBits(stopBits[(controlReg & CR_WS) >> 2]);
158 
159  outConnector.setParityBit(
160  (controlReg & (CR_WS3 | CR_WS2)) != 0x10, // enable
161  (controlReg & CR_WS1) ? ODD : EVEN);
162 
163  // start-bits, data-bits, parity-bits, stop-bits
164  byte len[8] = {
165  1 + 7 + 1 + 2,
166  1 + 7 + 1 + 2,
167  1 + 7 + 1 + 1,
168  1 + 7 + 1 + 1,
169  1 + 8 + 0 + 2,
170  1 + 8 + 0 + 1,
171  1 + 8 + 1 + 1,
172  1 + 8 + 1 + 1,
173  };
174  charLen = len[(controlReg & CR_WS) >> 2];
175 }
176 
177 void MC6850::writeDataReg(byte value, EmuTime::param time)
178 {
179  if ((controlReg & CR_CDS) == CR_MR) return;
180 
181  txDataReg = value;
182  statusReg &= ~STAT_TDRE;
183  txIRQ.reset();
184 
185  if (syncTrans.pendingSyncPoint()) {
186  // We're still sending the previous character, only
187  // buffer this one. Don't accept any further characters
188  } else {
189  // We were not yet sending. Start sending at the next txClock.
190  // Important: till that time TDRE should go low
191  // (MC6850 detection routine in Synthesix depends on this)
192  txClock.advance(time); // clock edge before or at 'time'
193  txClock += 1; // clock edge strictly after 'time'
194  syncTrans.setSyncPoint(txClock.getTime());
195  }
196 }
197 
198 // Triggered between transmitted characters, including before the first and
199 // after the last character.
200 void MC6850::execTrans(EmuTime::param time)
201 {
202  assert(txClock.getTime() == time);
203  assert((controlReg & CR_CDS) != CR_MR);
204 
205  if (txShiftRegValid) {
206  txShiftRegValid = false;
207  outConnector.recvByte(txShiftReg, time);
208  }
209 
210  if (statusReg & STAT_TDRE) {
211  // No next character to send, we're done.
212  } else {
213  // There already is a next character, start sending that now
214  // and accept a next one.
215  statusReg |= STAT_TDRE;
216  if (((controlReg & CR_TC) == 0x20)) txIRQ.set();
217 
218  txShiftReg = txDataReg;
219  txShiftRegValid = true;
220 
221  txClock += charLen;
222  syncTrans.setSyncPoint(txClock.getTime());
223  }
224 }
225 
226 // MidiInConnector sends a new character.
227 void MC6850::recvByte(byte value, EmuTime::param time)
228 {
229  assert(acceptsData() && ready());
230 
231  if (statusReg & STAT_RDRF) {
232  // So, there is a byte that has to be read by the MSX still!
233  // This happens when the MSX program doesn't
234  // respond fast enough to an earlier received byte.
235  // The STAT_OVRN flag only becomes active once the prior valid
236  // character has been read from the data register.
237  pendingOVRN = true;
238  } else {
239  rxDataReg = value;
240  statusReg |= STAT_RDRF;
241  }
242  // both for OVRN and RDRF an IRQ is raised
243  if (controlReg & CR_RIE) rxIRQ.set();
244 
245  // Not ready now, but we will be in a while
246  rxReady = false;
247 
248  // The MC6850 has separate TxCLK and RxCLK inputs, but both share a
249  // common divider. This implementation hard-codes an input frequency
250  // for both. Below we want the receive clock period, but it's OK
251  // to calculate that as 'txClock.getPeriod()'.
252  syncRecv.setSyncPoint(time + txClock.getPeriod() * charLen);
253 }
254 
255 // Triggered when we're ready to receive the next character.
256 void MC6850::execRecv(EmuTime::param time)
257 {
258  assert(acceptsData());
259  assert(!rxReady);
260  rxReady = true;
261  getPluggedMidiInDev().signal(time); // trigger (possible) send of next char
262 }
263 
264 // MidiInDevice queries whether it can send a new character 'now'.
265 bool MC6850::ready()
266 {
267  return rxReady;
268 }
269 
270 // MidiInDevice queries whether it can send characters at all.
271 bool MC6850::acceptsData()
272 {
273  return (controlReg & CR_CDS) != CR_MR;
274 }
275 
276 // MidiInDevice informs us about the format of the data it will send
277 // (MIDI is always 1 start-bit, 8 data-bits, 1 stop-bit, no parity-bits).
278 void MC6850::setDataBits(DataBits /*bits*/)
279 {
280  // ignore
281 }
282 void MC6850::setStopBits(StopBits /*bits*/)
283 {
284  // ignore
285 }
286 void MC6850::setParityBit(bool /*enable*/, ParityBit /*parity*/)
287 {
288  // ignore
289 }
290 
291 // version 1: initial version
292 // version 2: added control
293 // version 3: actually working MC6850 with many more member variables
294 template<typename Archive>
295 void MC6850::serialize(Archive& ar, unsigned version)
296 {
297  if (ar.versionAtLeast(version, 3)) {
298  ar.template serializeBase<MidiInConnector>(*this);
299  ar.serialize("outConnector", outConnector,
300 
301  "syncRecv", syncRecv,
302  "syncTrans", syncTrans,
303 
304  "txClock", txClock,
305  "rxIRQ", rxIRQ,
306  "txIRQ", txIRQ,
307 
308  "rxReady", rxReady,
309  "txShiftRegValid", txShiftRegValid,
310  "pendingOVRN", pendingOVRN,
311 
312  "rxDataReg", rxDataReg,
313  "txDataReg", txDataReg,
314  "txShiftReg", txShiftReg,
315  "controlReg", controlReg,
316  "statusReg", statusReg);
317  } else if (ar.versionAtLeast(version, 2)) {
318  ar.serialize("control", controlReg);
319  } else {
320  controlReg = 3;
321  }
322 
323  if constexpr (Archive::IS_LOADER) {
324  setDataFormat();
325  }
326 }
328 
329 } // namespace openmsx
EmuDuration getPeriod() const
Returns the length of one clock-cycle.
void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
void advance(EmuTime::param e)
Advance this clock in time until the last tick which is not past the given time.
void setFreq(unsigned freq)
Change the frequency at which this clock ticks.
EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
Definition: DynamicClock.hh:37
void set()
Set the interrupt request on the bus.
Definition: IRQHelper.hh:74
void reset()
Reset the interrupt request on the bus.
Definition: IRQHelper.hh:83
bool getState() const
Get the interrupt state.
Definition: IRQHelper.hh:103
byte readDataReg()
Definition: MC6850.cc:103
byte peekStatusReg() const
Definition: MC6850.cc:96
void reset(EmuTime::param time)
Definition: MC6850.cc:74
void writeControlReg(byte value, EmuTime::param time)
Definition: MC6850.cc:120
byte readStatusReg() const
Definition: MC6850.cc:91
void writeDataReg(byte value, EmuTime::param time)
Definition: MC6850.cc:177
MC6850(const std::string &name, MSXMotherBoard &motherBoard, unsigned clockFreq)
Definition: MC6850.cc:58
byte peekDataReg() const
Definition: MC6850.cc:115
void serialize(Archive &ar, unsigned version)
Definition: MC6850.cc:295
MidiInDevice & getPluggedMidiInDev() const
virtual void signal(EmuTime::param time)=0
void setStopBits(StopBits bits) override
void setDataBits(DataBits bits) override
void recvByte(byte value, EmuTime::param time) override
void setParityBit(bool enable, ParityBit parity) override
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr unsigned CR_TC1
Definition: MC6850.cc:34
constexpr unsigned CR_RIE
Definition: MC6850.cc:44
constexpr unsigned STAT_TDRE
Definition: MC6850.cc:50
constexpr byte STAT_FE
Definition: I8251.cc:13
constexpr unsigned CR_WS1
Definition: MC6850.cc:20
constexpr unsigned CR_CDS
Definition: MC6850.cc:12
constexpr unsigned CR_TC2
Definition: MC6850.cc:35
constexpr unsigned CR_CDS2
Definition: MC6850.cc:11
constexpr byte STAT_PE
Definition: I8251.cc:11
constexpr unsigned STAT_DCD
Definition: MC6850.cc:51
constexpr unsigned STAT_OVRN
Definition: MC6850.cc:54
constexpr unsigned STAT_IRQ
Definition: MC6850.cc:56
constexpr unsigned STAT_CTS
Definition: MC6850.cc:52
constexpr unsigned CR_WS
Definition: MC6850.cc:23
constexpr unsigned CR_TC
Definition: MC6850.cc:36
constexpr unsigned CR_MR
Definition: MC6850.cc:13
constexpr unsigned STAT_RDRF
Definition: MC6850.cc:49
constexpr unsigned CR_WS2
Definition: MC6850.cc:21
constexpr unsigned CR_WS3
Definition: MC6850.cc:22
constexpr unsigned CR_CDS1
Definition: MC6850.cc:10
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:998