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 
6 namespace openmsx {
7 
8 const int MODE_A = 0x60;
9 const int MODEA_0 = 0x00;
10 const int MODEA_1 = 0x20;
11 const int MODEA_2 = 0x40;
12 const int MODEA_2_ = 0x60;
13 const int MODE_B = 0x04;
14 const int MODEB_0 = 0x00;
15 const int MODEB_1 = 0x04;
16 const int DIRECTION_A = 0x10;
17 const int DIRECTION_B = 0x02;
18 const int DIRECTION_C0 = 0x01;
19 const int DIRECTION_C1 = 0x08;
20 const int SET_MODE = 0x80;
21 const int BIT_NR = 0x0E;
22 const int SET_RESET = 0x01;
23 
24 
25 I8255::I8255(I8255Interface& interf, EmuTime::param time,
26  StringSetting& invalidPpiModeSetting)
27  : interface(interf)
28  , ppiModeCallback(invalidPpiModeSetting)
29 {
30  reset(time);
31 }
32 
33 void I8255::reset(EmuTime::param time)
34 {
35  latchPortA = 0;
36  latchPortB = 0;
37  latchPortC = 0;
38  writeControlPort(SET_MODE | DIRECTION_A | DIRECTION_B |
39  DIRECTION_C0 | DIRECTION_C1, time); // all input
40 }
41 
42 byte I8255::read(byte port, EmuTime::param time)
43 {
44  switch (port) {
45  case 0:
46  return readPortA(time);
47  case 1:
48  return readPortB(time);
49  case 2:
50  return readPortC(time);
51  case 3:
52  return readControlPort(time);
53  default:
55  return 0; // avoid warning
56  }
57 }
58 
59 byte I8255::peek(byte port, EmuTime::param time) const
60 {
61  switch (port) {
62  case 0:
63  return peekPortA(time);
64  case 1:
65  return peekPortB(time);
66  case 2:
67  return peekPortC(time);
68  case 3:
69  return readControlPort(time);
70  default:
72  return 0; // avoid warning
73  }
74 }
75 
76 void I8255::write(byte port, byte value, EmuTime::param time)
77 {
78  switch (port) {
79  case 0:
80  writePortA(value, time);
81  break;
82  case 1:
83  writePortB(value, time);
84  break;
85  case 2:
86  writePortC(value, time);
87  break;
88  case 3:
89  writeControlPort(value, time);
90  break;
91  default:
93  }
94 }
95 
96 byte I8255::readPortA(EmuTime::param time)
97 {
98  switch (control & MODE_A) {
99  case MODEA_0:
100  if (control & DIRECTION_A) {
101  // input
102  return interface.readA(time); // input not latched
103  } else {
104  // output
105  return latchPortA; // output is latched
106  }
107  case MODEA_1: // TODO but not relevant for MSX
108  case MODEA_2: case MODEA_2_:
109  default:
110  return 255; // avoid warning
111  }
112 }
113 
114 byte I8255::peekPortA(EmuTime::param time) const
115 {
116  switch (control & MODE_A) {
117  case MODEA_0:
118  if (control & DIRECTION_A) {
119  return interface.peekA(time); // input not latched
120  } else {
121  return latchPortA; // output is latched
122  }
123  case MODEA_1: // TODO but not relevant for MSX
124  case MODEA_2: case MODEA_2_:
125  default:
126  return 255;
127  }
128 }
129 
130 byte I8255::readPortB(EmuTime::param time)
131 {
132  switch (control & MODE_B) {
133  case MODEB_0:
134  if (control & DIRECTION_B) {
135  // input
136  return interface.readB(time); // input not latched
137  } else {
138  // output
139  return latchPortB; // output is latched
140  }
141  case MODEB_1: // TODO but not relevant for MSX
142  default:
143  return 255; // avoid warning
144  }
145 }
146 
147 byte I8255::peekPortB(EmuTime::param time) const
148 {
149  switch (control & MODE_B) {
150  case MODEB_0:
151  if (control & DIRECTION_B) {
152  return interface.peekB(time); // input not latched
153  } else {
154  return latchPortB; // output is latched
155  }
156  case MODEB_1: // TODO but not relevant for MSX
157  default:
158  return 255;
159  }
160 }
161 
162 byte I8255::readPortC(EmuTime::param time)
163 {
164  byte tmp = readC1(time) | readC0(time);
165  switch (control & MODE_A) {
166  case MODEA_0:
167  // do nothing
168  break;
169  case MODEA_1:
170  case MODEA_2: case MODEA_2_:
171  // TODO but not relevant for MSX
172  break;
173  }
174  switch (control & MODE_B) {
175  case MODEB_0:
176  // do nothing
177  break;
178  case MODEB_1:
179  // TODO but not relevant for MSX
180  break;
181  }
182  return tmp;
183 }
184 
185 byte I8255::peekPortC(EmuTime::param time) const
186 {
187  return peekC1(time) | peekC0(time);
188 }
189 
190 byte I8255::readC1(EmuTime::param time)
191 {
192  if (control & DIRECTION_C1) {
193  // input
194  return interface.readC1(time) << 4; // input not latched
195  } else {
196  // output
197  return latchPortC & 0xf0; // output is latched
198  }
199 }
200 
201 byte I8255::peekC1(EmuTime::param time) const
202 {
203  if (control & DIRECTION_C1) {
204  return interface.peekC1(time) << 4; // input not latched
205  } else {
206  return latchPortC & 0xf0; // output is latched
207  }
208 }
209 
210 byte I8255::readC0(EmuTime::param time)
211 {
212  if (control & DIRECTION_C0) {
213  // input
214  return interface.readC0(time); // input not latched
215  } else {
216  // output
217  return latchPortC & 0x0f; // output is latched
218  }
219 }
220 
221 byte I8255::peekC0(EmuTime::param time) const
222 {
223  if (control & DIRECTION_C0) {
224  return interface.peekC0(time); // input not latched
225  } else {
226  return latchPortC & 0x0f; // output is latched
227  }
228 }
229 
230 byte I8255::readControlPort(EmuTime::param /*time*/) const
231 {
232  return control;
233 }
234 
235 void I8255::writePortA(byte value, EmuTime::param time)
236 {
237  switch (control & MODE_A) {
238  case MODEA_0:
239  // do nothing
240  break;
241  case MODEA_1:
242  case MODEA_2: case MODEA_2_:
243  // TODO but not relevant for MSX
244  break;
245  }
246  outputPortA(value, time);
247 }
248 
249 void I8255::writePortB(byte value, EmuTime::param time)
250 {
251  switch (control & MODE_B) {
252  case MODEB_0:
253  // do nothing
254  break;
255  case MODEB_1:
256  // TODO but not relevant for MSX
257  break;
258  }
259  outputPortB(value, time);
260 }
261 
262 void I8255::writePortC(byte value, EmuTime::param time)
263 {
264  switch (control & MODE_A) {
265  case MODEA_0:
266  // do nothing
267  break;
268  case MODEA_1:
269  case MODEA_2: case MODEA_2_:
270  // TODO but not relevant for MSX
271  break;
272  }
273  switch (control & MODE_B) {
274  case MODEB_0:
275  // do nothing
276  break;
277  case MODEB_1:
278  // TODO but not relevant for MSX
279  break;
280  }
281  outputPortC(value, time);
282 }
283 
284 void I8255::outputPortA(byte value, EmuTime::param time)
285 {
286  latchPortA = value;
287  if (!(control & DIRECTION_A)) {
288  // output
289  interface.writeA(value, time);
290  }
291 }
292 
293 void I8255::outputPortB(byte value, EmuTime::param time)
294 {
295  latchPortB = value;
296  if (!(control & DIRECTION_B)) {
297  // output
298  interface.writeB(value, time);
299  }
300 }
301 
302 void I8255::outputPortC(byte value, EmuTime::param time)
303 {
304  latchPortC = value;
305  if (!(control & DIRECTION_C1)) {
306  // output
307  interface.writeC1(latchPortC >> 4, time);
308  }
309  if (!(control & DIRECTION_C0)) {
310  // output
311  interface.writeC0(latchPortC & 15, time);
312  }
313 }
314 
315 void I8255::writeControlPort(byte value, EmuTime::param time)
316 {
317  if (value & SET_MODE) {
318  // set new control mode
319  control = value;
320  if ((control & (MODE_A | MODE_B))) {
321  ppiModeCallback.execute();
322  }
323  // Some PPI datasheets state that port A and C (and sometimes
324  // also B) are reset to zero on a mode change. But the
325  // documentation is not consistent.
326  // TODO investigate this further.
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 relevant 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 // Returns the value that's current being output on port A.
364 // Or a floating value if port A is actually programmed as input.
365 byte I8255::getPortA() const
366 {
367  byte result = latchPortA;
368  if (control & DIRECTION_A) {
369  // actually set as input -> return floating value
370  result = 255; // real floating value not yet supported
371  }
372  return result;
373 }
374 
375 byte I8255::getPortB() const
376 {
377  byte result = latchPortB;
378  if (control & DIRECTION_B) {
379  // actually set as input -> return floating value
380  result = 255; // real floating value not yet supported
381  }
382  return result;
383 }
384 
385 byte I8255::getPortC() const
386 {
387  byte result = latchPortC;
388  if (control & DIRECTION_C0) {
389  // actually set as input -> return floating value
390  result |= 0x0f; // real floating value not yet supported
391  }
392  if (control & DIRECTION_C1) {
393  // actually set as input -> return floating value
394  result |= 0xf0; // real floating value not yet supported
395  }
396  return result;
397 }
398 
399 template<typename Archive>
400 void I8255::serialize(Archive& ar, unsigned /*version*/)
401 {
402  ar.serialize("latchPortA", latchPortA,
403  "latchPortB", latchPortB,
404  "latchPortC", latchPortC,
405  "control", control);
406 
407  // note: don't write to any output ports (is handled elsewhere)
408  // don't serialize 'warningPrinted'
409 }
411 
412 } // namespace openmsx
virtual byte peekB(EmuTime::param time) const =0
virtual void writeB(byte value, EmuTime::param time)=0
virtual nibble readC0(EmuTime::param time)=0
virtual byte readA(EmuTime::param time)=0
virtual void writeC1(nibble value, EmuTime::param time)=0
virtual byte readB(EmuTime::param time)=0
virtual void writeA(byte value, EmuTime::param time)=0
virtual nibble readC1(EmuTime::param time)=0
virtual void writeC0(nibble value, EmuTime::param time)=0
virtual byte peekA(EmuTime::param time) const =0
virtual nibble peekC1(EmuTime::param time) const =0
virtual nibble peekC0(EmuTime::param time) const =0
void reset(EmuTime::param time)
Definition: I8255.cc:33
byte getPortA() const
Definition: I8255.cc:365
I8255(I8255Interface &interf, EmuTime::param time, StringSetting &invalidPpiModeSetting)
Definition: I8255.cc:25
byte getPortC() const
Definition: I8255.cc:385
byte getPortB() const
Definition: I8255.cc:375
byte peek(byte port, EmuTime::param time) const
Definition: I8255.cc:59
byte read(byte port, EmuTime::param time)
Definition: I8255.cc:42
void serialize(Archive &ar, unsigned version)
Definition: I8255.cc:400
void write(byte port, byte value, EmuTime::param time)
Definition: I8255.cc:76
TclObject execute()
Definition: TclCallback.cc:35
This file implemented 3 utility functions:
Definition: Autofire.cc:9
const int SET_RESET
Definition: I8255.cc:22
const int MODE_A
Definition: I8255.cc:8
const int MODE_B
Definition: I8255.cc:13
const int DIRECTION_B
Definition: I8255.cc:17
const int MODEB_1
Definition: I8255.cc:15
const int MODEA_2
Definition: I8255.cc:11
const int SET_MODE
Definition: I8255.cc:20
const int MODEA_1
Definition: I8255.cc:10
const int MODEA_0
Definition: I8255.cc:9
const int DIRECTION_C1
Definition: I8255.cc:19
const int BIT_NR
Definition: I8255.cc:21
const int MODEB_0
Definition: I8255.cc:14
const int MODEA_2_
Definition: I8255.cc:12
const int DIRECTION_C0
Definition: I8255.cc:18
const int DIRECTION_A
Definition: I8255.cc:16
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1009
#define UNREACHABLE
Definition: unreachable.hh:38