1 /*****************************************************************************
2 ** $Source: /cygdrive/d/Private/_SVNROOT/bluemsx/blueMSX/Src/IoDevice/I8255.c,v $
3 **
4 ** $Revision: 1.9 $
5 **
6 ** $Date: 2008-03-30 18:38:40 $
7 **
8 ** More info: http://www.bluemsx.com
9 **
10 ** Copyright (C) 2003-2006 Daniel Vik
11 **
12 ** This program is free software; you can redistribute it and/or modify
13 ** it under the terms of the GNU General Public License as published by
14 ** the Free Software Foundation; either version 2 of the License, or
15 ** (at your option) any later version.
16 **
17 ** This program is distributed in the hope that it will be useful,
18 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ** GNU General Public License for more details.
21 **
22 ** You should have received a copy of the GNU General Public License
23 ** along with this program; if not, write to the Free Software
24 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 **
26 ******************************************************************************
27 */
28 #include "I8255.h"
29 #include "SaveState.h"
30 #include <stdlib.h>
31
32 struct I8255
33 {
34 I8255Read peekA;
35 I8255Read readA;
36 I8255Write writeA;
37 I8255Read peekB;
38 I8255Read readB;
39 I8255Write writeB;
40 I8255Read peekCLo;
41 I8255Read readCLo;
42 I8255Write writeCLo;
43 I8255Read peekCHi;
44 I8255Read readCHi;
45 I8255Write writeCHi;
46 void* ref;
47
48 UInt8 reg[4];
49 };
50
readDummy(void * ref)51 static UInt8 readDummy(void* ref)
52 {
53 return 0xff;
54 }
55
writeDummy(void * ref,UInt8 value)56 static void writeDummy(void* ref, UInt8 value)
57 {
58 }
59
i8255Create(I8255Read peekA,I8255Read readA,I8255Write writeA,I8255Read peekB,I8255Read readB,I8255Write writeB,I8255Read peekCLo,I8255Read readCLo,I8255Write writeCLo,I8255Read peekCHi,I8255Read readCHi,I8255Write writeCHi,void * ref)60 I8255* i8255Create(I8255Read peekA, I8255Read readA, I8255Write writeA,
61 I8255Read peekB, I8255Read readB, I8255Write writeB,
62 I8255Read peekCLo, I8255Read readCLo, I8255Write writeCLo,
63 I8255Read peekCHi, I8255Read readCHi, I8255Write writeCHi,
64 void* ref)
65 {
66 I8255* i8255 = calloc(1, sizeof(I8255));
67
68 i8255->peekA = peekA ? peekA : readDummy;
69 i8255->readA = readA ? readA : readDummy;
70 i8255->writeA = writeA ? writeA : writeDummy;
71 i8255->peekB = peekB ? peekB : readDummy;
72 i8255->readB = readB ? readB : readDummy;
73 i8255->writeB = writeB ? writeB : writeDummy;
74 i8255->peekCLo = peekCLo ? peekCLo : readDummy;
75 i8255->readCLo = readCLo ? readCLo : readDummy;
76 i8255->writeCLo = writeCLo ? writeCLo : writeDummy;
77 i8255->peekCHi = peekCHi ? peekCHi : readDummy;
78 i8255->readCHi = readCHi ? readCHi : readDummy;
79 i8255->writeCHi = writeCHi ? writeCHi : writeDummy;
80 i8255->ref = ref;
81
82 return i8255;
83 }
84
i8255Reset(I8255 * i8255)85 void i8255Reset(I8255* i8255)
86 {
87 i8255->reg[3] = 0x9b;
88
89 i8255Write(i8255, 0, 0);
90 i8255Write(i8255, 1, 0);
91 i8255Write(i8255, 2, 0);
92 }
93
i8255Destroy(I8255 * i8255)94 void i8255Destroy(I8255* i8255)
95 {
96 free(i8255);
97 }
98
i8255LoadState(I8255 * i8255)99 void i8255LoadState(I8255* i8255)
100 {
101 SaveState* state = saveStateOpenForRead("i8255");
102
103 i8255->reg[0] = (UInt8)saveStateGet(state, "reg00", 0);
104 i8255->reg[1] = (UInt8)saveStateGet(state, "reg01", 0);
105 i8255->reg[2] = (UInt8)saveStateGet(state, "reg02", 0);
106 i8255->reg[3] = (UInt8)saveStateGet(state, "reg03", 0);
107
108 saveStateClose(state);
109 }
110
i8255SaveState(I8255 * i8255)111 void i8255SaveState(I8255* i8255)
112 {
113 SaveState* state = saveStateOpenForWrite("i8255");
114
115 saveStateSet(state, "reg00", i8255->reg[0]);
116 saveStateSet(state, "reg01", i8255->reg[1]);
117 saveStateSet(state, "reg02", i8255->reg[2]);
118 saveStateSet(state, "reg03", i8255->reg[3]);
119
120 saveStateClose(state);
121 }
122
i8255Peek(I8255 * i8255,UInt16 port)123 UInt8 i8255Peek(I8255* i8255, UInt16 port)
124 {
125 UInt8 value;
126
127 port &= 0x03;
128
129 switch (port) {
130 case 0:
131 switch (i8255->reg[3] & 0x60) {
132 case 0x00: // MODE 0
133 if (i8255->reg[3] & 0x10) {
134 return i8255->peekA(i8255->ref);
135 }
136 return i8255->reg[0];
137
138 case 0x20: // MODE 1
139 return 0xff;
140
141 default: // MODE 2
142 return 0xff;
143 }
144 break;
145
146 case 1:
147 switch (i8255->reg[3] & 0x04) {
148 case 0x00: // MODE 0
149 if (i8255->reg[3] & 0x02) {
150 return i8255->peekA(i8255->ref);
151 }
152 return i8255->reg[1];
153
154 default: // MODE 1
155 return 0xff;
156 }
157 break;
158
159 case 2:
160 value = i8255->reg[2];
161
162 if (i8255->reg[3] & 0x01) {
163 value = (value & 0xf0) | (i8255->peekCLo(i8255->ref) & 0x0f);
164 }
165 if (i8255->reg[3] & 0x08) {
166 value = (value & 0x0f) | (i8255->peekCHi(i8255->ref) << 4);
167 }
168 return value;
169
170 case 3:
171 return i8255->reg[3];
172 }
173
174 return 0xff;
175 }
176
i8255Read(I8255 * i8255,UInt16 port)177 UInt8 i8255Read(I8255* i8255, UInt16 port)
178 {
179 UInt8 value;
180
181 port &= 0x03;
182
183 switch (port) {
184 case 0:
185 switch (i8255->reg[3] & 0x60) {
186 case 0x00: // MODE 0
187 if (i8255->reg[3] & 0x10) {
188 return i8255->readA(i8255->ref);
189 }
190 return i8255->reg[0];
191
192 case 0x20: // MODE 1
193 return 0xff;
194
195 default: // MODE 2
196 return 0xff;
197 }
198 break;
199
200 case 1:
201 switch (i8255->reg[3] & 0x04) {
202 case 0x00: // MODE 0
203 if (i8255->reg[3] & 0x02) {
204 return i8255->readB(i8255->ref);
205 }
206 return i8255->reg[1];
207
208 default: // MODE 1
209 return 0xff;
210 }
211 break;
212
213 case 2:
214 // FIXME: Check mode
215
216 value = i8255->reg[2];
217
218 if (i8255->reg[3] & 0x01) {
219 value = (value & 0xf0) | (i8255->readCLo(i8255->ref) & 0x0f);
220 }
221 if (i8255->reg[3] & 0x08) {
222 value = (value & 0x0f) | (i8255->readCHi(i8255->ref) << 4);
223 }
224 return value;
225
226 case 3:
227 return i8255->reg[3];
228 }
229
230 return 0xff;
231 }
232
i8255Write(I8255 * i8255,UInt16 port,UInt8 value)233 void i8255Write(I8255* i8255, UInt16 port, UInt8 value)
234 {
235 port &= 0x03;
236
237 switch (port) {
238 case 0:
239 switch (i8255->reg[3] & 0x60) {
240 case 0x00: // MODE 0
241 break;
242 case 0x20: // MODE 1
243 break;
244 default: // MODE 2
245 break;
246 }
247
248 i8255->reg[0] = value;
249
250 if (!(i8255->reg[3] & 0x10)) {
251 i8255->writeA(i8255->ref, value);
252 }
253 return;
254
255 case 1:
256 switch (i8255->reg[3] & 0x04) {
257 case 0x00: // MODE 0
258 break;
259 default: // MODE 1
260 break;
261 }
262
263 i8255->reg[1] = value;
264
265 if (!(i8255->reg[3] & 0x02)) {
266 i8255->writeB(i8255->ref, value);
267 }
268 return;
269
270 case 2:
271 i8255->reg[2] = value;
272
273 // FIXME: Check mode
274
275 if (!(i8255->reg[3] & 0x01)) {
276 i8255->writeCLo(i8255->ref, value & 0x0f);
277 }
278 if (!(i8255->reg[3] & 0x08)) {
279 i8255->writeCHi(i8255->ref, value >> 4);
280 }
281 return;
282
283 case 3:
284 if (value & 0x80) {
285 i8255->reg[3] = value;
286 i8255Write(i8255, 0, i8255->reg[0]);
287 i8255Write(i8255, 1, i8255->reg[1]);
288 i8255Write(i8255, 2, i8255->reg[2]);
289 }
290 else {
291 UInt8 mask = 1 << ((value >> 1) & 0x07);
292 if (value & 0x01) {
293 i8255Write(i8255, 2, i8255->reg[2] | mask);
294 }
295 else {
296 i8255Write(i8255, 2, i8255->reg[2] & ~mask);
297 }
298 }
299 return;
300 }
301 }
302
303
304 #if 0
305
306 /*****************************************************************************
307 ** $Source: /cygdrive/d/Private/_SVNROOT/bluemsx/blueMSX/Src/IoDevice/I8255.c,v $
308 **
309 ** $Revision: 1.9 $
310 **
311 ** $Date: 2008-03-30 18:38:40 $
312 **
313 ** More info: http://www.bluemsx.com
314 **
315 ** Copyright (C) 2003-2006 Daniel Vik
316 **
317 ** This program is free software; you can redistribute it and/or modify
318 ** it under the terms of the GNU General Public License as published by
319 ** the Free Software Foundation; either version 2 of the License, or
320 ** (at your option) any later version.
321 **
322 ** This program is distributed in the hope that it will be useful,
323 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
324 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
325 ** GNU General Public License for more details.
326 **
327 ** You should have received a copy of the GNU General Public License
328 ** along with this program; if not, write to the Free Software
329 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
330 **
331 ******************************************************************************
332 */
333 #include "I8255.h"
334 #include "SaveState.h"
335 #include <stdlib.h>
336
337 struct I8255
338 {
339 I8255Read peekA;
340 I8255Read readA;
341 I8255Write writeA;
342 I8255Read peekB;
343 I8255Read readB;
344 I8255Write writeB;
345 I8255Read peekCLo;
346 I8255Read readCLo;
347 I8255Write writeCLo;
348 I8255Read peekCHi;
349 I8255Read readCHi;
350 I8255Write writeCHi;
351 void* ref;
352
353 UInt8 reg[4];
354
355 int useInputLatch;
356
357 UInt8 latchA;
358 UInt8 latchB;
359 UInt8 latchC;
360 };
361
362 static UInt8 readLatchA(I8255* i8255)
363 {
364 return i8255->latchA;
365 }
366
367 static UInt8 readLatchB(I8255* i8255)
368 {
369 return i8255->latchB;
370 }
371
372 static UInt8 readLatchCLo(I8255* i8255)
373 {
374 return i8255->latchC & 0xff;
375 }
376
377 static UInt8 readLatchCHi(I8255* i8255)
378 {
379 return i8255->latchC >> 4;
380 }
381
382 static void writeDummy(void* ref, UInt8 value)
383 {
384 }
385
386 I8255* i8255Create(I8255Read peekA, I8255Read readA, I8255Write writeA,
387 I8255Read peekB, I8255Read readB, I8255Write writeB,
388 I8255Read peekCLo, I8255Read readCLo, I8255Write writeCLo,
389 I8255Read peekCHi, I8255Read readCHi, I8255Write writeCHi,
390 void* ref, int useInputLatch)
391 {
392 I8255* i8255 = calloc(1, sizeof(I8255));
393
394 i8255->peekA = peekA ? peekA : readLatchA;
395 i8255->readA = readA ? readA : readLatchA;
396 i8255->writeA = writeA ? writeA : writeDummy;
397 i8255->peekB = peekB ? peekB : readLatchB;
398 i8255->readB = readB ? readB : readLatchB;
399 i8255->writeB = writeB ? writeB : writeDummy;
400 i8255->peekCLo = peekCLo ? peekCLo : readLatchCLo;
401 i8255->readCLo = readCLo ? readCLo : readLatchCLo;
402 i8255->writeCLo = writeCLo ? writeCLo : writeDummy;
403 i8255->peekCHi = peekCHi ? peekCHi : readLatchCHi;
404 i8255->readCHi = readCHi ? readCHi : readLatchCHi;
405 i8255->writeCHi = writeCHi ? writeCHi : writeDummy;
406 i8255->ref = ref;
407
408 i8255->useInputLatch = useInputLatch;
409
410 return i8255;
411 }
412
413 void i8255Reset(I8255* i8255)
414 {
415 i8255->reg[3] = 0x9b;
416
417 i8255->latchA = 0xff;
418 i8255->latchB = 0xff;
419 i8255->latchC = 0xff;
420
421 i8255Write(i8255, 0, 0);
422 i8255Write(i8255, 1, 0);
423 i8255Write(i8255, 2, 0);
424 }
425
426 void i8255Destroy(I8255* i8255)
427 {
428 free(i8255);
429 }
430
431 void i8255LoadState(I8255* i8255)
432 {
433 SaveState* state = saveStateOpenForRead("i8255");
434
435 i8255->reg[0] = (UInt8)saveStateGet(state, "reg00", 0);
436 i8255->reg[1] = (UInt8)saveStateGet(state, "reg01", 0);
437 i8255->reg[2] = (UInt8)saveStateGet(state, "reg02", 0);
438 i8255->reg[3] = (UInt8)saveStateGet(state, "reg03", 0);
439
440 saveStateClose(state);
441 }
442
443 void i8255SaveState(I8255* i8255)
444 {
445 SaveState* state = saveStateOpenForWrite("i8255");
446
447 saveStateSet(state, "reg00", i8255->reg[0]);
448 saveStateSet(state, "reg01", i8255->reg[1]);
449 saveStateSet(state, "reg02", i8255->reg[2]);
450 saveStateSet(state, "reg03", i8255->reg[3]);
451
452 saveStateClose(state);
453 }
454
455 #define INTR_B 0x01
456 #define IBF_B 0x02
457 #define OBF_B 0x02
458 #define STB_B 0x04
459 #define ACK_B 0x04
460 #define INTR_A 0x08
461 #define STB_A 0x10
462 #define IBF_A 0x20
463 #define ACK_A 0x40
464 #define OBF_A 0x80
465
466
467 void i8255WriteLatchA(I8255* i8255, UInt8 value)
468 {
469 if (i8255->reg[3] & 0x01) {
470 return;
471 }
472
473 switch (i8255->reg[3] & 0x60) {
474 case 0x00: // MODE 0
475 i8255->latchA = value;
476 break;
477 default: // MODE 1 and MODE 2
478 if (!(i8255->reg[2] & IBF_A)) {
479 if (i8255->latchA != value) {
480 i8255->reg[2] |= IBF_A;
481 i8255->reg[2] |= STB_A;
482 i8255->latchA = value;
483 if (i8255->reg[3] & 0x10) {
484 i8255->reg[2] |= INTR_A;
485 i8255->writeCLo(i8255->ref, i8255->reg[2] & 0x0f);
486 i8255->writeCHi(i8255->ref, i8255->reg[2] >> 4);
487 }
488 }
489 }
490 break;
491 }
492 }
493
494 void i8255WriteLatchB(I8255* i8255, UInt8 value)
495 {
496 if (i8255->reg[3] & 0x08) {
497 return;
498 }
499 switch (i8255->reg[3] & 0x04) {
500 case 0x00: // MODE 0
501 i8255->latchB = value;
502 break;
503 default: // MODE 1
504 if (!(i8255->reg[2] & IBF_B)) {
505 if (i8255->latchB != value) {
506 i8255->reg[2] |= IBF_B;
507 i8255->reg[2] |= STB_B;
508 i8255->latchB = value;
509 if (i8255->reg[3] & 0x04) {
510 i8255->reg[2] |= INTR_B;
511 i8255->writeCLo(i8255->ref, i8255->reg[2] & 0x0f);
512 }
513 }
514 }
515 break;
516 }
517 }
518
519 void i8255WriteLatchC(I8255* i8255, UInt8 value)
520 {
521 i8255->latchC = value;
522 }
523
524 UInt8 i8255Peek(I8255* i8255, UInt16 port)
525 {
526 UInt8 value;
527
528 port &= 0x03;
529
530 switch (port) {
531 case 0:
532 switch (i8255->reg[3] & 0x60) {
533 case 0x00: // MODE 0
534 if (i8255->reg[3] & 0x10) {
535 if (i8255->useInputLatch) {
536 return i8255->latchA;
537 }
538 return i8255->peekA(i8255->ref);
539 }
540 return i8255->reg[0];
541
542 case 0x20: // MODE 1
543 if (i8255->useInputLatch) {
544 return i8255->latchA;
545 }
546 return 0xff;
547
548 default: // MODE 2
549 if (i8255->useInputLatch) {
550 return i8255->latchA;
551 }
552 return 0xff;
553 }
554 break;
555
556 case 1:
557 switch (i8255->reg[3] & 0x04) {
558 case 0x00: // MODE 0
559 if (i8255->reg[3] & 0x02) {
560 if (i8255->useInputLatch) {
561 return i8255->latchB;
562 }
563 return i8255->peekA(i8255->ref);
564 }
565 return i8255->reg[1];
566
567 default: // MODE 1
568 if (i8255->useInputLatch) {
569 return i8255->latchB;
570 }
571 return 0xff;
572 }
573 break;
574
575 case 2:
576 value = i8255->reg[2];
577
578 if (i8255->reg[3] & 0x01) {
579 if (i8255->useInputLatch) {
580 value = (value & 0xf0) | (i8255->latchC & 0x0f);
581 }
582 else {
583 value = (value & 0xf0) | (i8255->peekCLo(i8255->ref) & 0x0f);
584 }
585 }
586 if (i8255->reg[3] & 0x08) {
587 if (i8255->useInputLatch) {
588 value = (value & 0xf0) | (i8255->latchC & 0xf0);
589 }
590 else {
591 value = (value & 0x0f) | (i8255->peekCHi(i8255->ref) << 4);
592 }
593 }
594 return value;
595
596 case 3:
597 return i8255->reg[3];
598 }
599
600 return 0xff;
601 }
602
603 UInt8 i8255Read(I8255* i8255, UInt16 port)
604 {
605 UInt8 value;
606
607 port &= 0x03;
608
609 switch (port) {
610 case 0:
611 switch (i8255->reg[3] & 0x60) {
612 case 0x00: // MODE 0
613 if (i8255->reg[3] & 0x10) {
614 if (i8255->useInputLatch) {
615 return i8255->latchA;
616 }
617 return i8255->readA(i8255->ref);
618 }
619 return i8255->reg[0];
620
621 case 0x20: // MODE 1
622 if (i8255->useInputLatch) {
623 return i8255->latchA;
624 }
625 i8255->reg[2] &= ~IBF_A;
626 return 0xff;
627
628 default: // MODE 2
629 if (i8255->useInputLatch) {
630 return i8255->latchA;
631 }
632 i8255->reg[2] &= ~IBF_A;
633 return 0xff;
634 }
635 break;
636
637 case 1:
638 switch (i8255->reg[3] & 0x04) {
639 case 0x00: // MODE 0
640 if (i8255->reg[3] & 0x02) {
641 if (i8255->useInputLatch) {
642 return i8255->latchB;
643 }
644 return i8255->readB(i8255->ref);
645 }
646 return i8255->reg[1];
647
648 default: // MODE 1
649 if (i8255->useInputLatch) {
650 return i8255->latchB;
651 }
652 i8255->reg[2] &= ~IBF_B;
653 return 0xff;
654 }
655 break;
656
657 case 2:
658 value = i8255->reg[2];
659
660 if (i8255->reg[3] & 0x60) {
661 value &= ~0x60;
662 if (i8255->reg[3] & 0x10) {
663 if (i8255->reg[3] & 0x01) {
664 value |= 20;
665 }
666 else {
667 value |= 40;
668 }
669 }
670 }
671
672 if (i8255->reg[3] & 0x04) {
673 value &= ~0x04;
674 if (i8255->reg[3] & 0x04) {
675 value |= 0x04;
676 }
677 }
678
679 if (i8255->reg[3] & 0x01) {
680 if (i8255->useInputLatch) {
681 value = (value & 0xf0) | (i8255->latchC & 0x0f);
682 }
683 else {
684 value = (value & 0xf0) | (i8255->readCLo(i8255->ref) & 0x0f);
685 }
686 }
687 if (i8255->reg[3] & 0x08) {
688 if (i8255->useInputLatch) {
689 value = (value & 0xf0) | (i8255->latchC & 0xf0);
690 }
691 else {
692 value = (value & 0x0f) | (i8255->readCHi(i8255->ref) << 4);
693 }
694 }
695
696 return value;
697
698 case 3:
699 return i8255->reg[3];
700 }
701
702 return 0xff;
703 }
704
705 void i8255Write(I8255* i8255, UInt16 port, UInt8 value)
706 {
707 port &= 0x03;
708
709 switch (port) {
710 case 0:
711 switch (i8255->reg[3] & 0x60) {
712 case 0x00: // MODE 0
713 break;
714 case 0x20: // MODE 1
715 break;
716 default: // MODE 2
717 break;
718 }
719
720 i8255->reg[0] = value;
721
722 if (!(i8255->reg[3] & 0x10)) {
723 i8255->writeA(i8255->ref, value);
724 }
725 return;
726
727 case 1:
728 switch (i8255->reg[3] & 0x04) {
729 case 0x00: // MODE 0
730 break;
731 default: // MODE 1
732 break;
733 }
734
735 i8255->reg[1] = value;
736
737 if (!(i8255->reg[3] & 0x02)) {
738 i8255->writeB(i8255->ref, value);
739 }
740 return;
741
742 case 2:
743 {
744 UInt8 mask = 0xff;
745
746 if (i8255->reg[3] & 0x04) {
747 mask &= 0xf8;
748 }
749 if (i8255->reg[3] & 0x60) {
750 mask &= 0x07;
751 }
752
753 if (!(i8255->reg[3] & 0x01) && (mask & 0x0f)) {
754 i8255->writeCLo(i8255->ref, value & mask & 0x0f);
755 }
756 if (!(i8255->reg[3] & 0x08) && (mask & 0xf0)) {
757 i8255->writeCHi(i8255->ref, value >> 4);
758 }
759
760 i8255->reg[2] = (i8255->reg[2] & ~mask) | (value & mask);
761 }
762 return;
763
764 case 3:
765 if (value & 0x80) {
766 if (value != i8255->reg[3]) {
767 // Mode change
768
769 if (value & 0x60) {
770 i8255->reg[2] = (i8255->reg[2] & 0xf8) | 0x00;
771 }
772
773 if (value & 0x04) {
774 i8255->reg[2] = (i8255->reg[2] & 0x07) | 0x00;
775 }
776 }
777
778 i8255->reg[3] = value;
779 i8255Write(i8255, 0, i8255->reg[0]);
780 i8255Write(i8255, 1, i8255->reg[1]);
781 i8255Write(i8255, 2, i8255->reg[2]);
782 }
783 else {
784 UInt8 mask = 1 << ((value >> 1) & 0x07);
785 if (value & 0x01) {
786 i8255Write(i8255, 2, i8255->reg[2] | mask);
787 }
788 else {
789 i8255Write(i8255, 2, i8255->reg[2] & ~mask);
790 }
791 }
792 return;
793 }
794 }
795 #endif