fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/chipset/e6522.c *
7 * Created: 2007-11-09 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2007-2023 Hampa Hug <hampa@hampa.ch> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22
23#include <stdlib.h>
24#include <stdio.h>
25
26#include "e6522.h"
27
28
29#define DEBUG_VIA 0
30
31
32#define E6522_IFR_CA2 0x01
33#define E6522_IFR_CA1 0x02
34#define E6522_IFR_SR 0x04
35#define E6522_IFR_CB2 0x08
36#define E6522_IFR_CB1 0x10
37#define E6522_IFR_T2 0x20
38#define E6522_IFR_T1 0x40
39#define E6522_IFR_SET 0x80
40
41
42void e6522_init (e6522_t *via, unsigned addr_shift)
43{
44 via->addr_shift = addr_shift;
45
46 via->ora = 0x00;
47 via->orb = 0x00;
48
49 via->ira = 0x00;
50 via->irb = 0x00;
51
52 via->ddra = 0x00;
53 via->ddrb = 0x00;
54
55 via->shift_val = 0;
56 via->shift_cnt = 0;
57
58 via->acr = 0x00;
59 via->pcr = 0x00;
60
61 via->ifr = 0x00;
62 via->ier = 0x00;
63
64 via->t1_latch = 0;
65 via->t1_val = 0;
66 via->t1_hot = 0;
67
68 via->t2_latch = 0;
69 via->t2_val = 0;
70 via->t2_hot = 0;
71
72 via->ca1_inp = 0;
73 via->ca2_inp = 0;
74
75 via->cb1_inp = 0;
76 via->cb2_inp = 0;
77
78 via->set_ora_ext = NULL;
79 via->set_ora = NULL;
80 via->set_ora_val = 0;
81
82 via->set_orb_ext = NULL;
83 via->set_orb = NULL;
84 via->set_orb_val = 0;
85
86 via->set_ca2_ext = NULL;
87 via->set_ca2 = NULL;
88 via->set_ca2_val = 0;
89
90 via->set_cb2_ext = NULL;
91 via->set_cb2 = NULL;
92 via->set_cb2_val = 0;
93
94 via->set_shift_out_ext = NULL;
95 via->set_shift_out = NULL;
96
97 via->irq_ext = NULL;
98 via->irq = NULL;
99 via->irq_val = 0;
100}
101
102void e6522_free (e6522_t *via)
103{
104}
105
106void e6522_set_ora_fct (e6522_t *via, void *ext, void *fct)
107{
108 via->set_ora_ext = ext;
109 via->set_ora = fct;
110}
111
112void e6522_set_orb_fct (e6522_t *via, void *ext, void *fct)
113{
114 via->set_orb_ext = ext;
115 via->set_orb = fct;
116}
117
118void e6522_set_ca2_fct (e6522_t *via, void *ext, void *fct)
119{
120 via->set_ca2_ext = ext;
121 via->set_ca2 = fct;
122}
123
124void e6522_set_cb2_fct (e6522_t *via, void *ext, void *fct)
125{
126 via->set_cb2_ext = ext;
127 via->set_cb2 = fct;
128}
129
130void e6522_set_shift_out_fct (e6522_t *via, void *ext, void *fct)
131{
132 via->set_shift_out_ext = ext;
133 via->set_shift_out = fct;
134}
135
136void e6522_set_irq_fct (e6522_t *via, void *ext, void *fct)
137{
138 via->irq_ext = ext;
139 via->irq = fct;
140}
141
142
143static
144void e6522_set_ora_out (e6522_t *via)
145{
146 unsigned char val;
147
148 val = via->ora & via->ddra;
149 val |= via->ira & ~via->ddra;
150
151 if (via->set_ora != NULL) {
152 via->set_ora (via->set_ora_ext, val);
153 }
154}
155
156static
157void e6522_set_orb_out (e6522_t *via)
158{
159 unsigned char val;
160
161 val = via->orb & via->ddrb;
162 val |= via->irb & ~ via->ddrb;
163
164 if (via->set_orb != NULL) {
165 via->set_orb (via->set_orb_ext, val);
166 }
167}
168
169/*
170 * Set the CA2 output according to the VIA's state
171 */
172static
173void e6522_set_ca2_out (e6522_t *via)
174{
175 unsigned char val;
176
177 if ((via->pcr & 0x08) == 0) {
178 /* CA2 is input */
179 val = 1;
180 }
181 else if ((via->pcr & 0x06) == 0x06) {
182 /* manual output mode (1) */
183 val = 1;
184 }
185 else if ((via->pcr & 0x06) == 0x04) {
186 /* manual output mode (0) */
187 val = 0;
188 }
189 else {
190 /* not implemented */
191 val = 1;
192 }
193
194
195 if (val == via->set_ca2_val) {
196 return;
197 }
198
199 via->set_ca2_val = val;
200
201 if (via->set_ca2 != NULL) {
202 via->set_ca2 (via->set_ca2_ext, val);
203 }
204}
205
206/*
207 * Set the CB2 output according to the VIA's state
208 */
209static
210void e6522_set_cb2_out (e6522_t *via)
211{
212 unsigned char val;
213
214 if ((via->acr & 0x1c) != 0) {
215 /* shift register enabled */
216
217 if (via->acr & 0x10) {
218 val = ((via->shift_val & 1) != 0);
219 }
220 else {
221 /* shift register input */
222 val = 1;
223 }
224 }
225 else if ((via->pcr & 0x80) == 0) {
226 /* CB2 is input */
227 val = 1;
228 }
229 else if ((via->pcr & 0x60) == 0x60) {
230 /* manual output mode (1) */
231 val = 1;
232 }
233 else if ((via->pcr & 0x60) == 0x40) {
234 /* manual output mode (0) */
235 val = 0;
236 }
237 else {
238 /* not implemented */
239 val = 1;
240 }
241
242
243 if (val == via->set_cb2_val) {
244 return;
245 }
246
247 via->set_cb2_val = val;
248
249 if (via->set_cb2 != NULL) {
250 via->set_cb2 (via->set_cb2_ext, val);
251 }
252}
253
254static
255void e6522_set_shift_out (e6522_t *via, unsigned char val)
256{
257 if (via->set_shift_out != NULL) {
258 via->set_shift_out (via->set_shift_out_ext, val);
259 }
260}
261
262/*
263 * Set the IRQ output
264 */
265static
266void e6522_set_irq (e6522_t *via, unsigned char val)
267{
268 if (via->irq_val == val) {
269 return;
270 }
271
272 via->irq_val = val;
273
274 if (via->irq != NULL) {
275 via->irq (via->irq_ext, val);
276 }
277}
278
279/*
280 * Set the IFR and trigger an interrupt if necessary
281 */
282static
283void e6522_set_ifr (e6522_t *via, unsigned char val)
284{
285 val &= 0x7f;
286
287 if (val & via->ier) {
288 val |= 0x80;
289 }
290
291 via->ifr = val;
292
293 e6522_set_irq (via, (via->ifr & 0x80) != 0);
294}
295
296/*
297 * Set the IER and trigger an interrupt if necessary
298 */
299static
300void e6522_set_ier (e6522_t *via, unsigned char val)
301{
302 if (val & 0x80) {
303 via->ier |= (val & 0x7f);
304 }
305 else {
306 via->ier &= (~val & 0x7f);
307 }
308
309 e6522_set_ifr (via, via->ifr);
310}
311
312
313unsigned char e6522_shift_out (e6522_t *via)
314{
315 unsigned char val;
316
317 if ((via->acr & 0x1c) != 0x1c) {
318 return (0);
319 }
320
321 val = (via->shift_val >> 7) & 1;
322
323 via->shift_val = ((via->shift_val << 1) | (via->shift_val >> 7)) & 0xff;
324 via->shift_cnt = (via->shift_cnt + 1) & 7;
325
326 if (via->shift_cnt == 0) {
327 e6522_set_ifr (via, via->ifr | E6522_IFR_SR);
328 }
329
330 return (val);
331}
332
333void e6522_shift_in (e6522_t *via, unsigned char val)
334{
335 if ((via->acr & 0x1c) != 0x0c) {
336 return;
337 }
338
339 via->shift_val = ((via->shift_val << 1) | (val != 0)) & 0xff;
340 via->shift_cnt = (via->shift_cnt + 1) & 7;
341
342 if (via->shift_cnt == 0) {
343 e6522_set_ifr (via, via->ifr | E6522_IFR_SR);
344 }
345
346 e6522_set_ifr (via, via->ifr | E6522_IFR_CB1);
347}
348
349
350static
351unsigned char e6522_get_ora (e6522_t *via, int handshake)
352{
353 unsigned char val;
354
355 if (handshake) {
356 e6522_set_ifr (via, via->ifr & ~(E6522_IFR_CA1 | E6522_IFR_CA2));
357 }
358
359 val = via->ora & via->ddra;
360 val |= via->ira & ~via->ddra;
361
362 return (val);
363}
364
365static
366unsigned char e6522_get_orb (e6522_t *via)
367{
368 unsigned char val;
369
370 e6522_set_ifr (via, via->ifr & ~(E6522_IFR_CB1 | E6522_IFR_CB2));
371
372 val = via->orb & via->ddrb;
373 val |= via->irb & ~via->ddrb;
374
375 return (val);
376}
377
378static
379unsigned char e6522_get_t1_counter_low (e6522_t *via)
380{
381 e6522_set_ifr (via, via->ifr & ~E6522_IFR_T1);
382
383 return (via->t1_val & 0xff);
384}
385
386static
387unsigned char e6522_get_t1_counter_high (e6522_t *via)
388{
389 return ((via->t1_val >> 8) & 0xff);
390}
391
392static
393unsigned char e6522_get_t1_latch_low (e6522_t *via)
394{
395 return (via->t1_latch & 0xff);
396}
397
398static
399unsigned char e6522_get_t1_latch_high (e6522_t *via)
400{
401 return ((via->t1_latch >> 8) & 0xff);
402}
403
404static
405unsigned char e6522_get_t2_counter_low (e6522_t *via)
406{
407 e6522_set_ifr (via, via->ifr & ~E6522_IFR_T2);
408
409 return (via->t2_val & 0xff);
410}
411
412static
413unsigned char e6522_get_t2_counter_high (e6522_t *via)
414{
415 return ((via->t2_val >> 8) & 0xff);
416}
417
418static
419unsigned char e6522_get_shift (e6522_t *via)
420{
421 e6522_set_ifr (via, via->ifr & ~E6522_IFR_SR);
422
423 via->shift_cnt = 0;
424
425 return (via->shift_val);
426}
427
428static
429unsigned char e6522_get_acr (e6522_t *via)
430{
431 return (via->acr);
432}
433
434static
435unsigned char e6522_get_pcr (e6522_t *via)
436{
437 return (via->pcr);
438}
439
440static
441void e6522_set_ora (e6522_t *via, unsigned char val, int handshake)
442{
443 via->ora = val;
444
445 e6522_set_ora_out (via);
446
447 if (handshake) {
448 e6522_set_ifr (via, via->ifr & ~(E6522_IFR_CA1 | E6522_IFR_CA2));
449 }
450}
451
452static
453void e6522_set_ddra (e6522_t *via, unsigned char val)
454{
455 via->ddra = val;
456
457 e6522_set_ora_out (via);
458}
459
460static
461void e6522_set_orb (e6522_t *via, unsigned char val)
462{
463 via->orb = val;
464
465 e6522_set_orb_out (via);
466 e6522_set_ifr (via, via->ifr & ~(E6522_IFR_CB1 | E6522_IFR_CB2));
467}
468
469static
470void e6522_set_ddrb (e6522_t *via, unsigned char val)
471{
472 via->ddrb = val;
473
474 e6522_set_orb_out (via);
475}
476
477static
478void e6522_set_t1_latch_low (e6522_t *via, unsigned char val)
479{
480 via->t1_latch &= 0xff00;
481 via->t1_latch |= (val & 0xff);
482}
483
484static
485void e6522_set_t1_counter_high (e6522_t *via, unsigned char val)
486{
487 via->t1_latch &= 0x00ff;
488 via->t1_latch |= (val & 0xff) << 8;
489
490 via->t1_reload = 1;
491
492 if ((via->acr & 0x40) == 0) {
493 via->t1_hot = 1;
494 }
495
496 e6522_set_ifr (via, via->ifr & ~E6522_IFR_T1);
497}
498
499static
500void e6522_set_t1_latch_high (e6522_t *via, unsigned char val)
501{
502 via->t1_latch &= 0x00ff;
503 via->t1_latch |= (val & 0xff) << 8;
504}
505
506static
507void e6522_set_t2_latch_low (e6522_t *via, unsigned char val)
508{
509 via->t2_latch &= 0xff00;
510 via->t2_latch |= (val & 0xff);
511}
512
513static
514void e6522_set_t2_counter_high (e6522_t *via, unsigned char val)
515{
516 via->t2_latch &= 0x00ff;
517 via->t2_latch |= (val & 0xff) << 8;
518
519 via->t2_val = via->t2_latch;
520
521 if ((via->acr & 0x20) == 0) {
522 via->t2_hot = 1;
523 }
524
525 e6522_set_ifr (via, via->ifr & ~E6522_IFR_T2);
526}
527
528static
529void e6522_set_shift (e6522_t *via, unsigned char val)
530{
531 e6522_set_ifr (via, via->ifr & ~E6522_IFR_SR);
532
533 via->shift_val = val;
534 via->shift_cnt = 0;
535
536 if ((via->acr & 0x1c) == 0x1c) {
537 /* shift out with external clock */
538 if (via->set_shift_out != NULL) {
539 e6522_set_shift_out (via, via->shift_val);
540 e6522_set_ifr (via, via->ifr | E6522_IFR_SR);
541 }
542 }
543}
544
545static
546void e6522_set_acr (e6522_t *via, unsigned char val)
547{
548 via->acr = val;
549
550 if ((via->acr & 0x1c) == 0) {
551 /* shift register disabled */
552 via->shift_cnt = 0;
553 }
554
555 e6522_set_cb2_out (via);
556}
557
558static
559void e6522_set_pcr (e6522_t *via, unsigned char val)
560{
561 via->pcr = val;
562
563 e6522_set_ca2_out (via);
564 e6522_set_cb2_out (via);
565}
566
567
568void e6522_set_ca1_inp (e6522_t *via, unsigned char val)
569{
570 unsigned char old;
571
572 old = (via->ca1_inp != 0);
573 val = (val != 0);
574
575 via->ca1_inp = val;
576
577 if (old != val) {
578 if ((val != 0) == ((via->pcr & 0x01) != 0)) {
579 e6522_set_ifr (via, via->ifr | E6522_IFR_CA1);
580 }
581 }
582}
583
584void e6522_set_ca2_inp (e6522_t *via, unsigned char val)
585{
586 unsigned char old;
587
588 old = (via->ca2_inp != 0);
589 val = (val != 0);
590
591 via->ca2_inp = val;
592
593 if (via->pcr & 0x08) {
594 /* CA2 is output */
595 return;
596 }
597
598 if (old != val) {
599 if ((val != 0) == ((via->pcr & 0x04) != 0)) {
600 e6522_set_ifr (via, via->ifr | E6522_IFR_CA2);
601 }
602 }
603}
604
605void e6522_set_cb1_inp (e6522_t *via, unsigned char val)
606{
607 unsigned char old;
608
609 old = (via->cb1_inp != 0);
610 val = (val != 0);
611
612 via->cb1_inp = val;
613
614 if (old != val) {
615 if ((val != 0) == ((via->pcr & 0x10) != 0)) {
616 e6522_set_ifr (via, via->ifr | E6522_IFR_CB1);
617 }
618 }
619}
620
621void e6522_set_cb2_inp (e6522_t *via, unsigned char val)
622{
623 unsigned char old;
624
625 old = (via->cb2_inp != 0);
626 val = (val != 0);
627
628 via->cb2_inp = val;
629
630 if ((via->acr & 0x1c) != 0) {
631 /* shift register enabled */
632 return;
633 }
634
635 if (via->pcr & 0x80) {
636 /* CB2 is output */
637 return;
638 }
639
640 if (old != val) {
641 if ((val != 0) == ((via->pcr & 0x40) != 0)) {
642 e6522_set_ifr (via, via->ifr | E6522_IFR_CB2);
643 }
644 }
645}
646
647void e6522_set_ira_inp (e6522_t *via, unsigned char val)
648{
649 if (via->ira == val) {
650 return;
651 }
652
653 via->ira = val;
654
655 e6522_set_ora_out (via);
656}
657
658void e6522_set_irb_inp (e6522_t *via, unsigned char val)
659{
660 if (via->irb == val) {
661 return;
662 }
663
664 via->irb = val;
665
666 e6522_set_orb_out (via);
667}
668
669void e6522_set_shift_inp (e6522_t *via, unsigned char val)
670{
671 if ((via->acr & 0x1c) == 0x0c) {
672 /* shift in with external clock */
673 via->shift_val = val;
674 via->shift_cnt = 0;
675 e6522_set_ifr (via, via->ifr | E6522_IFR_SR);
676 }
677}
678
679
680static
681unsigned long e6522_addr_decode (e6522_t *via, unsigned long addr)
682{
683 return (addr >> via->addr_shift);
684}
685
686unsigned char e6522_get_uint8 (e6522_t *via, unsigned long addr)
687{
688 unsigned char val;
689
690 addr = e6522_addr_decode (via, addr);
691
692 switch (addr) {
693 case 0x00:
694 val = e6522_get_orb (via);
695 break;
696
697 case 0x01:
698 val = e6522_get_ora (via, 1);
699 break;
700
701 case 0x02:
702 val = via->ddrb;
703 break;
704
705 case 0x03:
706 val = via->ddra;
707 break;
708
709 case 0x04:
710 val = e6522_get_t1_counter_low (via);
711 break;
712
713 case 0x05:
714 val = e6522_get_t1_counter_high (via);
715 break;
716
717 case 0x06:
718 val = e6522_get_t1_latch_low (via);
719 break;
720
721 case 0x07:
722 val = e6522_get_t1_latch_high (via);
723 break;
724
725 case 0x08:
726 val = e6522_get_t2_counter_low (via);
727 break;
728
729 case 0x09:
730 val = e6522_get_t2_counter_high (via);
731 break;
732
733 case 0x0a:
734 val = e6522_get_shift (via);
735 break;
736
737 case 0x0b:
738 val = e6522_get_acr (via);
739 break;
740
741 case 0x0c:
742 val = e6522_get_pcr (via);
743 break;
744
745 case 0x0d:
746 val = via->ifr;
747 break;
748
749 case 0x0e:
750 val = via->ier | 0x80;
751 break;
752
753 case 0x0f:
754 val = e6522_get_ora (via, 0);
755 break;
756
757 default:
758 val = 0xaa;
759 break;
760 }
761
762#if DEBUG_VIA
763 fprintf (stderr, "via: get %04lX -> %02X\n", addr, val);
764#endif
765
766 return (val);
767}
768
769unsigned short e6522_get_uint16 (e6522_t *via, unsigned long addr)
770{
771 return (e6522_get_uint8 (via, addr));
772}
773
774unsigned long e6522_get_uint32 (e6522_t *via, unsigned long addr)
775{
776 return (e6522_get_uint8 (via, addr));
777}
778
779
780void e6522_set_uint8 (e6522_t *via, unsigned long addr, unsigned char val)
781{
782 addr = e6522_addr_decode (via, addr);
783
784#if DEBUG_VIA
785 fprintf (stderr, "via: set %04lX <- %02X\n", addr, val);
786#endif
787
788 switch (addr) {
789 case 0x00:
790 e6522_set_orb (via, val);
791 break;
792
793 case 0x01:
794 e6522_set_ora (via, val, 1);
795 break;
796
797 case 0x02:
798 e6522_set_ddrb (via, val);
799 break;
800
801 case 0x03:
802 e6522_set_ddra (via, val);
803 break;
804
805 case 0x04:
806 e6522_set_t1_latch_low (via, val);
807 break;
808
809 case 0x05:
810 e6522_set_t1_counter_high (via, val);
811 break;
812
813 case 0x06:
814 e6522_set_t1_latch_low (via, val);
815 break;
816
817 case 0x07:
818 e6522_set_t1_latch_high (via, val);
819 break;
820
821 case 0x08:
822 e6522_set_t2_latch_low (via, val);
823 break;
824
825 case 0x09:
826 e6522_set_t2_counter_high (via, val);
827 break;
828
829 case 0x0a:
830 e6522_set_shift (via, val);
831 break;
832
833 case 0x0b:
834 e6522_set_acr (via, val);
835 break;
836
837 case 0x0c:
838 e6522_set_pcr (via, val);
839 break;
840
841 case 0x0d:
842 e6522_set_ifr (via, via->ifr & ~val & 0x7f);
843 break;
844
845 case 0x0e:
846 e6522_set_ier (via, val);
847 break;
848
849 case 0x0f:
850 e6522_set_ora (via, val, 0);
851 break;
852
853 }
854}
855
856void e6522_set_uint16 (e6522_t *via, unsigned long addr, unsigned short val)
857{
858 e6522_set_uint8 (via, addr, val & 0xff);
859}
860
861void e6522_set_uint32 (e6522_t *via, unsigned long addr, unsigned long val)
862{
863 e6522_set_uint8 (via, addr, val & 0xff);
864}
865
866void e6522_reset (e6522_t *via)
867{
868 via->ora = 0x00;
869 via->orb = 0x00;
870
871 via->ddra = 0x00;
872 via->ddrb = 0x00;
873
874 via->shift_val = 0;
875 via->shift_cnt = 0;
876
877 via->pcr = 0x00;
878 via->acr = 0x00;
879
880 via->ifr = 0x00;
881 via->ier = 0x00;
882
883 via->t1_reload = 0;
884 via->t1_timeout = 0;
885 via->t1_hot = 0;
886 via->t1_latch = 0;
887 via->t1_val = 0;
888
889 via->t2_timeout = 0;
890 via->t2_hot = 0;
891 via->t2_latch = 0;
892 via->t2_val = 0;
893
894 e6522_set_ora_out (via);
895 e6522_set_orb_out (via);
896
897 e6522_set_ifr (via, via->ifr);
898
899#if DEBUG_VIA
900 fprintf (stderr, "via: reset\n");
901#endif
902}
903
904static
905void e6522_clock_t1 (e6522_t *via)
906{
907 if (via->t1_reload) {
908 via->t1_reload = 0;
909 via->t1_timeout = 0;
910 via->t1_val = via->t1_latch;
911 return;
912 }
913
914 via->t1_val = (via->t1_val - 1) & 0xffff;
915
916 if (via->t1_val == 0) {
917 via->t1_timeout = 1;
918 }
919 else if (via->t1_timeout) {
920 via->t1_timeout = 0;
921
922 if (via->acr & 0x40) {
923 /* free running */
924
925 e6522_set_ifr (via, via->ifr | E6522_IFR_T1);
926
927 via->t1_reload = 1;
928 }
929 else {
930 /* one shot */
931
932 if (via->t1_hot) {
933 via->t1_hot = 0;
934 e6522_set_ifr (via, via->ifr | E6522_IFR_T1);
935 }
936 }
937 }
938}
939
940static
941void e6522_clock_t2 (e6522_t *via)
942{
943 via->t2_val = (via->t2_val - 1) & 0xffff;
944
945 if (via->t2_val == 0) {
946 via->t2_timeout = 1;
947 }
948 else if (via->t2_timeout) {
949 via->t2_timeout = 0;
950
951 if (via->acr & 0x20) {
952 /* free running */
953 ;
954 }
955 else {
956 /* one shot */
957
958 if (via->t2_hot) {
959 via->t2_hot = 0;
960 e6522_set_ifr (via, via->ifr | E6522_IFR_T2);
961 }
962 }
963 }
964}
965
966void e6522_clock (e6522_t *via, unsigned n)
967{
968 while (n-- > 0) {
969 e6522_clock_t1 (via);
970 e6522_clock_t2 (via);
971 }
972}