openMSX
I8255.cc
Go to the documentation of this file.
1 #include "I8255.hh"
2 #include "I8255Interface.hh"
3 #include "serialize.hh"
4 #include "unreachable.hh"
5 #include "CliComm.hh"
6 
7 namespace openmsx {
8 
9 const int MODE_A = 0x60;
10 const int MODEA_0 = 0x00;
11 const int MODEA_1 = 0x20;
12 const int MODEA_2 = 0x40;
13 const int MODEA_2_ = 0x60;
14 const int MODE_B = 0x04;
15 const int MODEB_0 = 0x00;
16 const int MODEB_1 = 0x04;
17 const int DIRECTION_A = 0x10;
18 const int DIRECTION_B = 0x02;
19 const int DIRECTION_C0 = 0x01;
20 const int DIRECTION_C1 = 0x08;
21 const int SET_MODE = 0x80;
22 const int BIT_NR = 0x0E;
23 const int SET_RESET = 0x01;
24 
25 
26 I8255::I8255(I8255Interface& interf, EmuTime::param time, CliComm& cliComm_)
27  : interface(interf)
28  , cliComm(cliComm_)
29  , warningPrinted(false)
30 {
31  reset(time);
32 }
33 
34 void I8255::reset(EmuTime::param time)
35 {
36  latchPortA = 0;
37  latchPortB = 0;
38  latchPortC = 0;
39  writeControlPort(SET_MODE | DIRECTION_A | DIRECTION_B |
40  DIRECTION_C0 | DIRECTION_C1, time); // all input
41 }
42 
43 byte I8255::read(byte port, EmuTime::param time)
44 {
45  switch (port) {
46  case 0:
47  return readPortA(time);
48  case 1:
49  return readPortB(time);
50  case 2:
51  return readPortC(time);
52  case 3:
53  return readControlPort(time);
54  default:
56  return 0; // avoid warning
57  }
58 }
59 
60 byte I8255::peek(byte port, EmuTime::param time) const
61 {
62  switch (port) {
63  case 0:
64  return peekPortA(time);
65  case 1:
66  return peekPortB(time);
67  case 2:
68  return peekPortC(time);
69  case 3:
70  return readControlPort(time);
71  default:
73  return 0; // avoid warning
74  }
75 }
76 
77 void I8255::write(byte port, byte value, EmuTime::param time)
78 {
79  switch (port) {
80  case 0:
81  writePortA(value, time);
82  break;
83  case 1:
84  writePortB(value, time);
85  break;
86  case 2:
87  writePortC(value, time);
88  break;
89  case 3:
90  writeControlPort(value, time);
91  break;
92  default:
94  }
95 }
96 
97 byte I8255::readPortA(EmuTime::param time)
98 {
99  switch (control & MODE_A) {
100  case MODEA_0:
101  if (control & DIRECTION_A) {
102  // input
103  return interface.readA(time); // input not latched
104  } else {
105  // output
106  return latchPortA; // output is latched
107  }
108  case MODEA_1: // TODO but not relevant for MSX
109  case MODEA_2: case MODEA_2_:
110  default:
111  return 255; // avoid warning
112  }
113 }
114 
115 byte I8255::peekPortA(EmuTime::param time) const
116 {
117  switch (control & MODE_A) {
118  case MODEA_0:
119  if (control & DIRECTION_A) {
120  return interface.peekA(time); // input not latched
121  } else {
122  return latchPortA; // output is latched
123  }
124  case MODEA_1: // TODO but not relevant for MSX
125  case MODEA_2: case MODEA_2_:
126  default:
127  return 255;
128  }
129 }
130 
131 byte I8255::readPortB(EmuTime::param time)
132 {
133  switch (control & MODE_B) {
134  case MODEB_0:
135  if (control & DIRECTION_B) {
136  // input
137  return interface.readB(time); // input not latched
138  } else {
139  // output
140  return latchPortB; // output is latched
141  }
142  case MODEB_1: // TODO but not relevant for MSX
143  default:
144  return 255; // avoid warning
145  }
146 }
147 
148 byte I8255::peekPortB(EmuTime::param time) const
149 {
150  switch (control & MODE_B) {
151  case MODEB_0:
152  if (control & DIRECTION_B) {
153  return interface.peekB(time); // input not latched
154  } else {
155  return latchPortB; // output is latched
156  }
157  case MODEB_1: // TODO but not relevant for MSX
158  default:
159  return 255;
160  }
161 }
162 
163 byte I8255::readPortC(EmuTime::param time)
164 {
165  byte tmp = readC1(time) | readC0(time);
166  switch (control & MODE_A) {
167  case MODEA_0:
168  // do nothing
169  break;
170  case MODEA_1:
171  case MODEA_2: case MODEA_2_:
172  // TODO but not relevant for MSX
173  break;
174  }
175  switch (control & MODE_B) {
176  case MODEB_0:
177  // do nothing
178  break;
179  case MODEB_1:
180  // TODO but not relevant for MSX
181  break;
182  }
183  return tmp;
184 }
185 
186 byte I8255::peekPortC(EmuTime::param time) const
187 {
188  return peekC1(time) | peekC0(time);
189 }
190 
191 byte I8255::readC1(EmuTime::param time)
192 {
193  if (control & DIRECTION_C1) {
194  // input
195  return interface.readC1(time) << 4; // input not latched
196  } else {
197  // output
198  return latchPortC & 0xf0; // output is latched
199  }
200 }
201 
202 byte I8255::peekC1(EmuTime::param time) const
203 {
204  if (control & DIRECTION_C1) {
205  return interface.peekC1(time) << 4; // input not latched
206  } else {
207  return latchPortC & 0xf0; // output is latched
208  }
209 }
210 
211 byte I8255::readC0(EmuTime::param time)
212 {
213  if (control & DIRECTION_C0) {
214  // input
215  return interface.readC0(time); // input not latched
216  } else {
217  // output
218  return latchPortC & 0x0f; // output is latched
219  }
220 }
221 
222 byte I8255::peekC0(EmuTime::param time) const
223 {
224  if (control & DIRECTION_C0) {
225  return interface.peekC0(time); // input not latched
226  } else {
227  return latchPortC & 0x0f; // output is latched
228  }
229 }
230 
231 byte I8255::readControlPort(EmuTime::param /*time*/) const
232 {
233  return control;
234 }
235 
236 void I8255::writePortA(byte value, EmuTime::param time)
237 {
238  switch (control & MODE_A) {
239  case MODEA_0:
240  // do nothing
241  break;
242  case MODEA_1:
243  case MODEA_2: case MODEA_2_:
244  // TODO but not relevant for MSX
245  break;
246  }
247  outputPortA(value, time);
248 }
249 
250 void I8255::writePortB(byte value, EmuTime::param time)
251 {
252  switch (control & MODE_B) {
253  case MODEB_0:
254  // do nothing
255  break;
256  case MODEB_1:
257  // TODO but not relevant for MSX
258  break;
259  }
260  outputPortB(value, time);
261 }
262 
263 void I8255::writePortC(byte value, EmuTime::param time)
264 {
265  switch (control & MODE_A) {
266  case MODEA_0:
267  // do nothing
268  break;
269  case MODEA_1:
270  case MODEA_2: case MODEA_2_:
271  // TODO but not relevant for MSX
272  break;
273  }
274  switch (control & MODE_B) {
275  case MODEB_0:
276  // do nothing
277  break;
278  case MODEB_1:
279  // TODO but not relevant for MSX
280  break;
281  }
282  outputPortC(value, time);
283 }
284 
285 void I8255::outputPortA(byte value, EmuTime::param time)
286 {
287  latchPortA = value;
288  if (!(control & DIRECTION_A)) {
289  // output
290  interface.writeA(value, time);
291  }
292 }
293 
294 void I8255::outputPortB(byte value, EmuTime::param time)
295 {
296  latchPortB = value;
297  if (!(control & DIRECTION_B)) {
298  // output
299  interface.writeB(value, time);
300  }
301 }
302 
303 void I8255::outputPortC(byte value, EmuTime::param time)
304 {
305  latchPortC = value;
306  if (!(control & DIRECTION_C1)) {
307  // output
308  interface.writeC1(latchPortC >> 4, time);
309  }
310  if (!(control & DIRECTION_C0)) {
311  // output
312  interface.writeC0(latchPortC & 15, time);
313  }
314 }
315 
316 void I8255::writeControlPort(byte value, EmuTime::param time)
317 {
318  if (value & SET_MODE) {
319  // set new control mode
320  control = value;
321  if ((control & (MODE_A | MODE_B)) && !warningPrinted) {
322  warningPrinted = true;
323  cliComm.printWarning("Invalid PPI mode selected. "
324  "This is not yet correctly emulated. "
325  "On a real MSX this will most likely hang.");
326  }
327  outputPortA(latchPortA, time);
328  outputPortB(latchPortB, time);
329  outputPortC(latchPortC, time);
330  } else {
331  // (re)set bit of port C
332  byte bitmask = 1 << ((value & BIT_NR) >> 1);
333  if (value & SET_RESET) {
334  // set
335  latchPortC |= bitmask;
336  } else {
337  // reset
338  latchPortC &= ~bitmask;
339  }
340  outputPortC(latchPortC, time);
341  // check for special (re)set commands
342  // not releant for mode 0
343  switch (control & MODE_A) {
344  case MODEA_0:
345  // do nothing
346  break;
347  case MODEA_1:
348  case MODEA_2: case MODEA_2_:
349  // TODO but not relevant for MSX
350  break;
351  }
352  switch (control & MODE_B) {
353  case MODEB_0:
354  // do nothing
355  break;
356  case MODEB_1:
357  // TODO but not relevant for MSX
358  break;
359  }
360  }
361 }
362 
363 template<typename Archive>
364 void I8255::serialize(Archive& ar, unsigned /*version*/)
365 {
366  ar.serialize("latchPortA", latchPortA,
367  "latchPortB", latchPortB,
368  "latchPortC", latchPortC,
369  "control", control);
370 
371  // note: don't write to any output ports (is handled elsewhere)
372  // don't serialize 'warningPrinted'
373 }
375 
376 } // namespace openmsx
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
#define UNREACHABLE
Definition: unreachable.hh:38