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
6namespace openmsx {
7
8const int MODE_A = 0x60;
9const int MODEA_0 = 0x00;
10const int MODEA_1 = 0x20;
11const int MODEA_2 = 0x40;
12const int MODEA_2_ = 0x60;
13const int MODE_B = 0x04;
14const int MODEB_0 = 0x00;
15const int MODEB_1 = 0x04;
16const int DIRECTION_A = 0x10;
17const int DIRECTION_B = 0x02;
18const int DIRECTION_C0 = 0x01;
19const int DIRECTION_C1 = 0x08;
20const int SET_MODE = 0x80;
21const int BIT_NR = 0x0E;
22const int SET_RESET = 0x01;
23
24
25I8255::I8255(I8255Interface& interface_, EmuTime::param time,
26 StringSetting& invalidPpiModeSetting)
27 : interface(interface_)
28 , ppiModeCallback(invalidPpiModeSetting)
29{
30 reset(time);
31}
32
33void 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
42byte 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
59byte 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
76void 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
96byte 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
114byte 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
130byte 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
147byte 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
162byte 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
185byte I8255::peekPortC(EmuTime::param time) const
186{
187 return peekC1(time) | peekC0(time);
188}
189
190byte I8255::readC1(EmuTime::param time)
191{
192 if (control & DIRECTION_C1) {
193 // input
194 return byte(interface.readC1(time) << 4); // input not latched
195 } else {
196 // output
197 return latchPortC & 0xf0; // output is latched
198 }
199}
200
201byte I8255::peekC1(EmuTime::param time) const
202{
203 if (control & DIRECTION_C1) {
204 return byte(interface.peekC1(time) << 4); // input not latched
205 } else {
206 return latchPortC & 0xf0; // output is latched
207 }
208}
209
210byte 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
221byte 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
230byte I8255::readControlPort(EmuTime::param /*time*/) const
231{
232 return control;
233}
234
235void 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
249void 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
262void 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
284void 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
293void 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
302void 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
315void 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 auto bitmask = byte(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.
365byte 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
375byte 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
385byte 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
399template<typename Archive>
400void 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
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
I8255(I8255Interface &interface, EmuTime::param time, StringSetting &invalidPpiModeSetting)
Definition I8255.cc:25
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() const
This file implemented 3 utility functions:
Definition Autofire.cc:9
uint8_t byte
8 bit unsigned integer
Definition openmsx.hh:26
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)
#define UNREACHABLE