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 }
56}
57
58byte I8255::peek(byte port, EmuTime::param time) const
59{
60 switch (port) {
61 case 0:
62 return peekPortA(time);
63 case 1:
64 return peekPortB(time);
65 case 2:
66 return peekPortC(time);
67 case 3:
68 return readControlPort(time);
69 default:
71 }
72}
73
74void I8255::write(byte port, byte value, EmuTime::param time)
75{
76 switch (port) {
77 case 0:
78 writePortA(value, time);
79 break;
80 case 1:
81 writePortB(value, time);
82 break;
83 case 2:
84 writePortC(value, time);
85 break;
86 case 3:
87 writeControlPort(value, time);
88 break;
89 default:
91 }
92}
93
94byte I8255::readPortA(EmuTime::param time)
95{
96 switch (control & MODE_A) {
97 case MODEA_0:
98 if (control & DIRECTION_A) {
99 // input
100 return interface.readA(time); // input not latched
101 } else {
102 // output
103 return latchPortA; // output is latched
104 }
105 case MODEA_1: // TODO but not relevant for MSX
106 case MODEA_2: case MODEA_2_:
107 default:
108 return 255; // avoid warning
109 }
110}
111
112byte I8255::peekPortA(EmuTime::param time) const
113{
114 switch (control & MODE_A) {
115 case MODEA_0:
116 if (control & DIRECTION_A) {
117 return interface.peekA(time); // input not latched
118 } else {
119 return latchPortA; // output is latched
120 }
121 case MODEA_1: // TODO but not relevant for MSX
122 case MODEA_2: case MODEA_2_:
123 default:
124 return 255;
125 }
126}
127
128byte I8255::readPortB(EmuTime::param time)
129{
130 switch (control & MODE_B) {
131 case MODEB_0:
132 if (control & DIRECTION_B) {
133 // input
134 return interface.readB(time); // input not latched
135 } else {
136 // output
137 return latchPortB; // output is latched
138 }
139 case MODEB_1: // TODO but not relevant for MSX
140 default:
141 return 255; // avoid warning
142 }
143}
144
145byte I8255::peekPortB(EmuTime::param time) const
146{
147 switch (control & MODE_B) {
148 case MODEB_0:
149 if (control & DIRECTION_B) {
150 return interface.peekB(time); // input not latched
151 } else {
152 return latchPortB; // output is latched
153 }
154 case MODEB_1: // TODO but not relevant for MSX
155 default:
156 return 255;
157 }
158}
159
160byte I8255::readPortC(EmuTime::param time)
161{
162 byte tmp = readC1(time) | readC0(time);
163 switch (control & MODE_A) {
164 case MODEA_0:
165 // do nothing
166 break;
167 case MODEA_1:
168 case MODEA_2: case MODEA_2_:
169 // TODO but not relevant for MSX
170 break;
171 }
172 switch (control & MODE_B) {
173 case MODEB_0:
174 // do nothing
175 break;
176 case MODEB_1:
177 // TODO but not relevant for MSX
178 break;
179 }
180 return tmp;
181}
182
183byte I8255::peekPortC(EmuTime::param time) const
184{
185 return peekC1(time) | peekC0(time);
186}
187
188byte I8255::readC1(EmuTime::param time)
189{
190 if (control & DIRECTION_C1) {
191 // input
192 return byte(interface.readC1(time) << 4); // input not latched
193 } else {
194 // output
195 return latchPortC & 0xf0; // output is latched
196 }
197}
198
199byte I8255::peekC1(EmuTime::param time) const
200{
201 if (control & DIRECTION_C1) {
202 return byte(interface.peekC1(time) << 4); // input not latched
203 } else {
204 return latchPortC & 0xf0; // output is latched
205 }
206}
207
208byte I8255::readC0(EmuTime::param time)
209{
210 if (control & DIRECTION_C0) {
211 // input
212 return interface.readC0(time); // input not latched
213 } else {
214 // output
215 return latchPortC & 0x0f; // output is latched
216 }
217}
218
219byte I8255::peekC0(EmuTime::param time) const
220{
221 if (control & DIRECTION_C0) {
222 return interface.peekC0(time); // input not latched
223 } else {
224 return latchPortC & 0x0f; // output is latched
225 }
226}
227
228byte I8255::readControlPort(EmuTime::param /*time*/) const
229{
230 return control;
231}
232
233void I8255::writePortA(byte value, EmuTime::param time)
234{
235 switch (control & MODE_A) {
236 case MODEA_0:
237 // do nothing
238 break;
239 case MODEA_1:
240 case MODEA_2: case MODEA_2_:
241 // TODO but not relevant for MSX
242 break;
243 }
244 outputPortA(value, time);
245}
246
247void I8255::writePortB(byte value, EmuTime::param time)
248{
249 switch (control & MODE_B) {
250 case MODEB_0:
251 // do nothing
252 break;
253 case MODEB_1:
254 // TODO but not relevant for MSX
255 break;
256 }
257 outputPortB(value, time);
258}
259
260void I8255::writePortC(byte value, EmuTime::param time)
261{
262 switch (control & MODE_A) {
263 case MODEA_0:
264 // do nothing
265 break;
266 case MODEA_1:
267 case MODEA_2: case MODEA_2_:
268 // TODO but not relevant for MSX
269 break;
270 }
271 switch (control & MODE_B) {
272 case MODEB_0:
273 // do nothing
274 break;
275 case MODEB_1:
276 // TODO but not relevant for MSX
277 break;
278 }
279 outputPortC(value, time);
280}
281
282void I8255::outputPortA(byte value, EmuTime::param time)
283{
284 latchPortA = value;
285 if (!(control & DIRECTION_A)) {
286 // output
287 interface.writeA(value, time);
288 }
289}
290
291void I8255::outputPortB(byte value, EmuTime::param time)
292{
293 latchPortB = value;
294 if (!(control & DIRECTION_B)) {
295 // output
296 interface.writeB(value, time);
297 }
298}
299
300void I8255::outputPortC(byte value, EmuTime::param time)
301{
302 latchPortC = value;
303 if (!(control & DIRECTION_C1)) {
304 // output
305 interface.writeC1(latchPortC >> 4, time);
306 }
307 if (!(control & DIRECTION_C0)) {
308 // output
309 interface.writeC0(latchPortC & 15, time);
310 }
311}
312
313void I8255::writeControlPort(byte value, EmuTime::param time)
314{
315 if (value & SET_MODE) {
316 // set new control mode
317 control = value;
318 if (control & (MODE_A | MODE_B)) {
319 ppiModeCallback.execute();
320 }
321 // Some PPI datasheets state that port A and C (and sometimes
322 // also B) are reset to zero on a mode change. But the
323 // documentation is not consistent.
324 // TODO investigate this further.
325 outputPortA(latchPortA, time);
326 outputPortB(latchPortB, time);
327 outputPortC(latchPortC, time);
328 } else {
329 // (re)set bit of port C
330 auto bitmask = byte(1 << ((value & BIT_NR) >> 1));
331 if (value & SET_RESET) {
332 // set
333 latchPortC |= bitmask;
334 } else {
335 // reset
336 latchPortC &= ~bitmask;
337 }
338 outputPortC(latchPortC, time);
339 // check for special (re)set commands
340 // not relevant for mode 0
341 switch (control & MODE_A) {
342 case MODEA_0:
343 // do nothing
344 break;
345 case MODEA_1:
346 case MODEA_2: case MODEA_2_:
347 // TODO but not relevant for MSX
348 break;
349 }
350 switch (control & MODE_B) {
351 case MODEB_0:
352 // do nothing
353 break;
354 case MODEB_1:
355 // TODO but not relevant for MSX
356 break;
357 }
358 }
359}
360
361// Returns the value that's current being output on port A.
362// Or a floating value if port A is actually programmed as input.
363byte I8255::getPortA() const
364{
365 byte result = latchPortA;
366 if (control & DIRECTION_A) {
367 // actually set as input -> return floating value
368 result = 255; // real floating value not yet supported
369 }
370 return result;
371}
372
373byte I8255::getPortB() const
374{
375 byte result = latchPortB;
376 if (control & DIRECTION_B) {
377 // actually set as input -> return floating value
378 result = 255; // real floating value not yet supported
379 }
380 return result;
381}
382
383byte I8255::getPortC() const
384{
385 byte result = latchPortC;
386 if (control & DIRECTION_C0) {
387 // actually set as input -> return floating value
388 result |= 0x0f; // real floating value not yet supported
389 }
390 if (control & DIRECTION_C1) {
391 // actually set as input -> return floating value
392 result |= 0xf0; // real floating value not yet supported
393 }
394 return result;
395}
396
397template<typename Archive>
398void I8255::serialize(Archive& ar, unsigned /*version*/)
399{
400 ar.serialize("latchPortA", latchPortA,
401 "latchPortB", latchPortB,
402 "latchPortC", latchPortC,
403 "control", control);
404
405 // note: don't write to any output ports (is handled elsewhere)
406 // don't serialize 'warningPrinted'
407}
409
410} // 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:363
byte getPortC() const
Definition I8255.cc:383
byte getPortB() const
Definition I8255.cc:373
byte peek(byte port, EmuTime::param time) const
Definition I8255.cc:58
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:398
void write(byte port, byte value, EmuTime::param time)
Definition I8255.cc:74
TclObject execute() const
This file implemented 3 utility functions:
Definition Autofire.cc:11
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