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  outputPortA(latchPortA, time);
324  outputPortB(latchPortB, time);
325  outputPortC(latchPortC, time);
326  } else {
327  // (re)set bit of port C
328  byte bitmask = 1 << ((value & BIT_NR) >> 1);
329  if (value & SET_RESET) {
330  // set
331  latchPortC |= bitmask;
332  } else {
333  // reset
334  latchPortC &= ~bitmask;
335  }
336  outputPortC(latchPortC, time);
337  // check for special (re)set commands
338  // not releant for mode 0
339  switch (control & MODE_A) {
340  case MODEA_0:
341  // do nothing
342  break;
343  case MODEA_1:
344  case MODEA_2: case MODEA_2_:
345  // TODO but not relevant for MSX
346  break;
347  }
348  switch (control & MODE_B) {
349  case MODEB_0:
350  // do nothing
351  break;
352  case MODEB_1:
353  // TODO but not relevant for MSX
354  break;
355  }
356  }
357 }
358 
359 template<typename Archive>
360 void I8255::serialize(Archive& ar, unsigned /*version*/)
361 {
362  ar.serialize("latchPortA", latchPortA,
363  "latchPortB", latchPortB,
364  "latchPortC", latchPortC,
365  "control", control);
366 
367  // note: don't write to any output ports (is handled elsewhere)
368  // don't serialize 'warningPrinted'
369 }
371 
372 } // namespace openmsx
openmsx::I8255Interface::readB
virtual byte readB(EmuTime::param time)=0
openmsx::I8255Interface::readC1
virtual nibble readC1(EmuTime::param time)=0
openmsx::I8255Interface::writeB
virtual void writeB(byte value, EmuTime::param time)=0
openmsx::DIRECTION_C1
const int DIRECTION_C1
Definition: I8255.cc:19
serialize.hh
openmsx::BIT_NR
const int BIT_NR
Definition: I8255.cc:21
openmsx::I8255Interface::peekC0
virtual nibble peekC0(EmuTime::param time) const =0
I8255.hh
openmsx::I8255::write
void write(byte port, byte value, EmuTime::param time)
Definition: I8255.cc:76
openmsx::I8255::peek
byte peek(byte port, EmuTime::param time) const
Definition: I8255.cc:59
openmsx::I8255Interface::peekA
virtual byte peekA(EmuTime::param time) const =0
openmsx::MODEA_0
const int MODEA_0
Definition: I8255.cc:9
openmsx::DIRECTION_A
const int DIRECTION_A
Definition: I8255.cc:16
openmsx::I8255Interface::readA
virtual byte readA(EmuTime::param time)=0
openmsx::I8255Interface::writeC0
virtual void writeC0(nibble value, EmuTime::param time)=0
openmsx::MODEB_0
const int MODEB_0
Definition: I8255.cc:14
openmsx::MODEA_2_
const int MODEA_2_
Definition: I8255.cc:12
openmsx::MODE_B
const int MODE_B
Definition: I8255.cc:13
openmsx::StringSetting
Definition: StringSetting.hh:8
openmsx::I8255::reset
void reset(EmuTime::param time)
Definition: I8255.cc:33
openmsx::SET_RESET
const int SET_RESET
Definition: I8255.cc:22
UNREACHABLE
#define UNREACHABLE
Definition: unreachable.hh:38
openmsx::SET_MODE
const int SET_MODE
Definition: I8255.cc:20
openmsx::I8255Interface::readC0
virtual nibble readC0(EmuTime::param time)=0
openmsx::TclCallback::execute
TclObject execute()
Definition: TclCallback.cc:40
INSTANTIATE_SERIALIZE_METHODS
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
openmsx::I8255Interface::peekC1
virtual nibble peekC1(EmuTime::param time) const =0
openmsx::I8255::read
byte read(byte port, EmuTime::param time)
Definition: I8255.cc:42
openmsx::I8255::I8255
I8255(I8255Interface &interf, EmuTime::param time, StringSetting &invalidPpiModeSetting)
Definition: I8255.cc:25
openmsx::I8255Interface::writeC1
virtual void writeC1(nibble value, EmuTime::param time)=0
openmsx::I8255::serialize
void serialize(Archive &ar, unsigned version)
Definition: I8255.cc:360
openmsx::I8255
Definition: I8255.hh:19
I8255Interface.hh
openmsx::MODEB_1
const int MODEB_1
Definition: I8255.cc:15
openmsx::MODE_A
const int MODE_A
Definition: I8255.cc:8
openmsx::MODEA_1
const int MODEA_1
Definition: I8255.cc:10
unreachable.hh
openmsx::I8255Interface::writeA
virtual void writeA(byte value, EmuTime::param time)=0
openmsx::DIRECTION_B
const int DIRECTION_B
Definition: I8255.cc:17
openmsx::I8255Interface::peekB
virtual byte peekB(EmuTime::param time) const =0
openmsx::I8255Interface
Definition: I8255Interface.hh:9
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
openmsx::MODEA_2
const int MODEA_2
Definition: I8255.cc:11
openmsx::DIRECTION_C0
const int DIRECTION_C0
Definition: I8255.cc:18