fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/chipset/e8530.c *
7 * Created: 2007-11-11 by Hampa Hug <hampa@hampa.ch> *
8 * Copyright: (C) 2007-2020 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 "e8530.h"
27
28
29#define DEBUG_SCC 0
30
31
32#define scc_get_chn(chn) (((chn) == 0) ? 'A' : 'B')
33
34
35static
36void e8530_init_chn (e8530_t *scc, unsigned chn)
37{
38 e8530_chn_t *c;
39
40 c = &scc->chn[chn];
41
42 c->txd_empty = 1;
43 c->rxd_empty = 1;
44
45 c->int_on_next_rx = 0;
46
47 c->char_clk_cnt = 0;
48 c->char_clk_div = 16384;
49
50 c->read_char_cnt = 0;
51 c->read_char_max = 1;
52
53 c->write_char_cnt = 0;
54 c->write_char_max = 1;
55
56 c->rtxc = 0;
57
58 c->tx_i = 0;
59 c->tx_j = 0;
60
61 c->rx_i = 0;
62 c->rx_j = 0;
63
64 c->set_inp_ext = NULL;
65 c->set_inp = NULL;
66
67 c->set_out_ext = NULL;
68 c->set_out = NULL;
69
70 c->set_rts_ext = NULL;
71 c->set_rts = NULL;
72
73 c->set_comm_ext = NULL;
74 c->set_comm = NULL;
75}
76
77void e8530_init (e8530_t *scc)
78{
79 scc->index = 0;
80
81 scc->pclk = 0;
82
83 e8530_init_chn (scc, 0);
84 e8530_init_chn (scc, 1);
85
86 scc->irq_ext = NULL;
87 scc->irq = NULL;
88 scc->irq_val = 0;
89}
90
91void e8530_free (e8530_t *scc)
92{
93}
94
95e8530_t *e8530_new (void)
96{
97 e8530_t *scc;
98
99 if ((scc = malloc (sizeof (e8530_t))) == NULL) {
100 return (NULL);
101 }
102
103 e8530_init (scc);
104
105 return (scc);
106}
107
108void e8530_del (e8530_t *scc)
109{
110 if (scc != NULL) {
111 e8530_free (scc);
112 free (scc);
113 }
114}
115
116void e8530_set_irq_fct (e8530_t *scc, void *ext, void *fct)
117{
118 scc->irq_ext = ext;
119 scc->irq = fct;
120}
121
122void e8530_set_inp_fct (e8530_t *scc, unsigned chn, void *ext, void *fct)
123{
124 if (chn < 2) {
125 scc->chn[chn].set_inp_ext = ext;
126 scc->chn[chn].set_inp = fct;
127 }
128}
129
130void e8530_set_out_fct (e8530_t *scc, unsigned chn, void *ext, void *fct)
131{
132 if (chn < 2) {
133 scc->chn[chn].set_out_ext = ext;
134 scc->chn[chn].set_out = fct;
135 }
136}
137
138void e8530_set_rts_fct (e8530_t *scc, unsigned chn, void *ext, void *fct)
139{
140 if (chn < 2) {
141 scc->chn[chn].set_rts_ext = ext;
142 scc->chn[chn].set_rts = fct;
143 }
144}
145
146void e8530_set_comm_fct (e8530_t *scc, unsigned chn, void *ext, void *fct)
147{
148 if (chn < 2) {
149 scc->chn[chn].set_comm_ext = ext;
150 scc->chn[chn].set_comm = fct;
151 }
152}
153
154void e8530_set_multichar (e8530_t *scc, unsigned chn, unsigned read_max, unsigned write_max)
155{
156 if (chn > 1) {
157 return;
158 }
159
160 scc->chn[chn].read_char_cnt = 0;
161 scc->chn[chn].read_char_max = (read_max < 1) ? 1 : read_max;
162
163 scc->chn[chn].write_char_cnt = 0;
164 scc->chn[chn].write_char_max = (write_max < 1) ? 1 : write_max;
165}
166
167void e8530_set_clock (e8530_t *scc, unsigned long pclk, unsigned long rtxca, unsigned long rtxcb)
168{
169 scc->pclk = pclk;
170 scc->chn[0].rtxc = rtxca;
171 scc->chn[1].rtxc = rtxcb;
172}
173
174static
175void e8530_set_irq (e8530_t *scc, unsigned char val)
176{
177 if (scc->irq_val == val) {
178 return;
179 }
180
181 scc->irq_val = val;
182
183 if (scc->irq != NULL) {
184 scc->irq (scc->irq_ext, val);
185 }
186}
187
188static
189void e8530_set_rts (e8530_t *scc, unsigned chn, unsigned char val)
190{
191 e8530_chn_t *c;
192
193 c = &scc->chn[chn];
194
195 if (c->set_rts != NULL) {
196 c->set_rts (c->set_rts_ext, val != 0);
197 }
198}
199
200static
201void e8530_set_int_cond (e8530_t *scc, unsigned chn, unsigned char cond)
202{
203 e8530_chn_t *c0, *c;
204
205 c = &scc->chn[chn];
206 c0 = &scc->chn[0];
207
208 if ((cond & 0x01) && (c->wr[1] & 0x01)) {
209 /* ext */
210
211 /* should check if IP is already set */
212
213 c0->rr[3] |= (chn == 0) ? 0x08 : 0x01;
214
215 c->rr0_latch_msk = c->wr[15];
216 c->rr0_latch_val = c->rr[0];
217 }
218
219 if ((cond & 0x02) && (c->wr[1] & 0x02)) {
220 /* transmit interrupt */
221 c0->rr[3] |= (chn == 0) ? 0x10 : 0x02;
222 }
223
224 if ((cond & 0x04) && (c->wr[1] & 0x18)) {
225 /* receive interrupt */
226 int data, spec, irq;
227
228 data = (c->rxd_empty == 0);
229 spec = (c->rr[0] & 0x80) != 0;
230 irq = 0;
231
232 switch ((c->wr[1] >> 3) & 3) {
233 case 0: /* disabled */
234 irq = 0;
235 break;
236
237 case 1: /* rx int on first char / special condition */
238 irq = (data && c->int_on_next_rx) || spec;
239 break;
240
241 case 2: /* rx int on all chars / special condition */
242 irq = data || spec;
243 break;
244
245 case 3: /* rx int on special condition */
246 irq = spec;
247 break;
248 }
249
250 if (data) {
251 c->int_on_next_rx = 0;
252 }
253
254 if (irq) {
255 c0->rr[3] |= (chn == 0) ? 0x20 : 0x04;
256 }
257 }
258
259 if ((c0->wr[9] & 0x08) == 0) {
260 /* MIE == 0 */
261 e8530_set_irq (scc, 0);
262 return;
263 }
264
265 e8530_set_irq (scc, c0->rr[3] != 0);
266}
267
268static
269void e8530_clr_int_cond (e8530_t *scc, unsigned chn, unsigned char cond)
270{
271 e8530_chn_t *c, *c0;
272
273 c = &scc->chn[chn];
274 c0 = &scc->chn[0];
275
276 if (cond & 0x01) {
277 /* ext */
278
279 c0->rr[3] &= (chn == 0) ? ~0x08 : ~0x01;
280
281 c->rr0_latch_msk = 0;
282 }
283
284 if (cond & 0x02) {
285 /* transmit interrupt */
286 c0->rr[3] &= (chn == 0) ? ~0x10 : ~0x02;
287 }
288
289 if (cond & 0x04) {
290 /* receive interrupt */
291 c0->rr[3] &= (chn == 0) ? ~0x20 : ~0x04;
292 }
293
294 if ((c0->wr[9] & 0x08) == 0) {
295 /* MIE == 0 */
296 e8530_set_irq (scc, 0);
297 return;
298 }
299
300 e8530_set_irq (scc, c0->rr[3] != 0);
301}
302
303/*
304 * Move a character from the input queue to RxD and adjust interrupt
305 * conditions.
306 */
307static
308void e8530_check_rxd (e8530_t *scc, unsigned chn)
309{
310 e8530_chn_t *c;
311
312 c = &scc->chn[chn];
313
314 if (c->rx_i == c->rx_j) {
315 return;
316 }
317
318 if (c->read_char_cnt == 0) {
319 return;
320 }
321
322 if ((c->wr[3] & 0x01) == 0) {
323 /* receiver disabled */
324 return;
325 }
326
327 if (c->rxd_empty == 0) {
328 /* should overwrite old character */
329 return;
330 }
331
332 c->read_char_cnt -= 1;
333
334 c->rr[8] = c->rxbuf[c->rx_j];
335 c->rx_j = (c->rx_j + 1) % E8530_BUF_MAX;
336
337 if (c->set_inp != NULL) {
338 c->set_inp (c->set_inp_ext, 1);
339 }
340
341 c->rr[0] |= 0x01;
342 c->rxd_empty = 0;
343
344 e8530_set_int_cond (scc, chn, 0x04);
345}
346
347/*
348 * Move a character from TxD to the output queue and adjust interrupt
349 * conditions.
350 */
351static
352void e8530_check_txd (e8530_t *scc, unsigned chn)
353{
354 e8530_chn_t *c;
355 unsigned char val;
356
357 c = &scc->chn[chn];
358
359 if (c->write_char_cnt == 0) {
360 return;
361 }
362
363 if (((c->tx_i + 1) % E8530_BUF_MAX) == c->tx_j) {
364 return;
365 }
366
367 if (c->txd_empty) {
368 /* tx underrun */
369 c->rr[0] |= 0x40;
370 return;
371 }
372
373 c->write_char_cnt -= 1;
374
375 val = c->wr[8];
376
377 c->txbuf[c->tx_i] = val;
378 c->tx_i = (c->tx_i + 1) % E8530_BUF_MAX;
379
380 if (c->set_out != NULL) {
381 c->set_out (c->set_out_ext, val);
382 }
383
384 c->rr[0] |= 0x04;
385 c->txd_empty = 1;
386
387 e8530_set_int_cond (scc, chn, 0x02);
388
389#if DEBUG_SCC
390 fprintf (stderr, "SCC %c: send %02X\n", scc_get_chn (chn), val);
391#endif
392}
393
394
395static
396unsigned e8530_get_clock_mode (e8530_t *scc, unsigned chn)
397{
398 switch ((scc->chn[chn].wr[4] >> 6) & 3) {
399 case 0:
400 return (1);
401
402 case 1:
403 return (16);
404
405 case 2:
406 return (32);
407
408 case 3:
409 return (64);
410 }
411
412 return (0);
413}
414
415static
416unsigned e8530_get_bits_per_char (e8530_t *scc, unsigned chn)
417{
418 switch ((scc->chn[chn].wr[5] >> 5) & 3) {
419 case 0:
420 return (5);
421
422 case 1:
423 return (7);
424
425 case 2:
426 return (6);
427
428 case 3:
429 return (8);
430 }
431
432 return (0);
433}
434
435static
436unsigned e8530_get_parity (e8530_t *scc, unsigned chn)
437{
438 switch (scc->chn[chn].wr[4] & 3) {
439 case 0:
440 return (0);
441
442 case 1: /* odd */
443 return (1);
444
445 case 2:
446 return (0);
447
448 case 3: /* even */
449 return (2);
450 }
451
452 return (0);
453}
454
455/*
456 * Get 2 * the number of stop bits
457 */
458static
459unsigned e8530_get_stop_bits (e8530_t *scc, unsigned chn)
460{
461 switch ((scc->chn[chn].wr[4] >> 2) & 3) {
462 case 0:
463 return (0);
464
465 case 1:
466 return (2);
467
468 case 2:
469 return (3);
470
471 case 3:
472 return (4);
473
474 }
475
476 return (0);
477}
478
479/*
480 * Update the communication parameters and call the callback function
481 */
482static
483void e8530_set_params (e8530_t *scc, unsigned chn)
484{
485 unsigned long bps, clk, mul, div;
486 e8530_chn_t *c;
487
488 c = &scc->chn[chn];
489
490 c->bpc = e8530_get_bits_per_char (scc, chn);
491 c->parity = e8530_get_parity (scc, chn);
492 c->stop = e8530_get_stop_bits (scc, chn);
493
494 mul = e8530_get_clock_mode (scc, chn);
495
496 if (c->stop == 0) {
497 /* sync mode */
498 mul = 1;
499 }
500
501 if (c->wr[14] & 0x01) {
502 /* baud rate generator enabled */
503
504 if (c->wr[14] & 0x02) {
505 clk = scc->pclk;
506 }
507 else {
508 clk = c->rtxc;
509 }
510
511 div = (c->wr[13] << 8) | c->wr[12];
512 div = 2 * mul * (div + 2);
513
514 c->char_clk_div = ((2 * c->bpc + c->stop + 2) * div) / 2;
515
516 if (c->parity != 0) {
517 c->char_clk_div += div;
518 }
519
520 bps = clk / div;
521 }
522 else {
523 c->char_clk_div = 16384;
524 bps = 0;
525 }
526
527 c->bps = bps;
528
529 if (c->set_comm != NULL) {
530 c->set_comm (c->set_comm_ext, c->bps, c->parity, c->bpc, c->stop);
531 }
532
533#if DEBUG_SCC
534 {
535 static const char p[3] = { 'N', 'O', 'E' };
536 static const char *s[5] = { "0", "", "1", "1.5", "2" };
537
538 fprintf (stderr, "SCC %c: %lu%c%u%s\n",
539 scc_get_chn (chn), c->bps, p[c->parity], c->bpc, s[c->stop]
540 );
541 }
542#endif
543}
544
545
546/*
547 * Set new RR0 value reflecting the external status and set an
548 * interrupt condition if an enabled status changed.
549 */
550static
551void e8530_set_rr0 (e8530_t *scc, unsigned chn, unsigned char val)
552{
553 unsigned char old;
554
555 chn &= 1;
556
557 old = scc->chn[chn].rr[0];
558 scc->chn[chn].rr[0] = val;
559
560 if ((old ^ val) & scc->chn[chn].wr[15] & 0xfa) {
561 e8530_set_int_cond (scc, chn, 0x01);
562 }
563}
564
565
566/*
567 * RR0: transmit/receive buffer status and external status
568 */
569static
570unsigned char e8530_get_rr0 (e8530_t *scc, unsigned chn)
571{
572 unsigned char val;
573
574 val = scc->chn[chn].rr[0] & ~scc->chn[chn].rr0_latch_msk;
575 val |= scc->chn[chn].rr0_latch_val & scc->chn[chn].rr0_latch_msk;
576
577 return (val);
578}
579
580/*
581 * RR2: interrupt vector
582 * If read from channel B, include status
583 */
584static
585unsigned char e8530_get_rr2 (e8530_t *scc, unsigned chn)
586{
587 unsigned char val;
588 e8530_chn_t *c0;
589
590 c0 = &scc->chn[0];
591
592 val = c0->rr[2];
593
594 if (chn == 1) {
595 unsigned char st;
596
597 /* include status in vector */
598
599 if (c0->rr[3] & 0x20) {
600 /* chn a rx */
601 st = 0x06 | (0x03 << 3);
602 }
603 else if (c0->rr[3] & 0x10) {
604 /* chn a tx */
605 st = 0x04 | (0x01 << 3);
606 }
607 else if (c0->rr[3] & 0x08) {
608 /* chn a ext */
609 st = 0x05 | (0x05 << 3);
610 }
611 else if (c0->rr[3] & 0x04) {
612 /* chn b rx */
613 st = 0x02 | (0x02 << 3);
614 }
615 else if (c0->rr[3] & 0x02) {
616 /* chn b tx */
617 st = 0x00 | (0x00 << 3);
618 }
619 else if (c0->rr[3] & 0x01) {
620 /* chn b ext */
621 st = 0x01 | (0x04 << 3);
622 }
623 else {
624 st = 0x03 | (0x06 << 3);
625 }
626
627 if (c0->wr[9] & 0x10) {
628 /* status high */
629 val = (val & 0x70) | ((st & 0x38) << 1);
630 }
631 else {
632 val = (val & 0xf1) | ((st & 0x07) << 1);
633 }
634 }
635
636 return (val);
637}
638
639/*
640 * RR3: interrupt pending
641 */
642static
643unsigned char e8530_get_rr3 (e8530_t *scc, unsigned chn)
644{
645 if (chn != 0) {
646 return (0);
647 }
648
649 return (scc->chn[0].rr[3]);
650}
651
652/*
653 * RR8: receive buffer
654 */
655static
656unsigned char e8530_get_rr8 (e8530_t *scc, unsigned chn)
657{
658 e8530_chn_t *c;
659 unsigned char val;
660
661 c = &scc->chn[chn];
662
663 val = c->rr[8];
664
665 c->rr[0] &= ~0x01;
666 c->rxd_empty = 1;
667
668 e8530_clr_int_cond (scc, chn, 0x04);
669
670 e8530_check_rxd (scc, chn);
671
672 return (val);
673}
674
675static
676unsigned char e8530_get_reg (e8530_t *scc, unsigned chn, unsigned reg)
677{
678 unsigned char val;
679
680 chn &= 1;
681
682 switch (reg) {
683 case 0x00:
684 val = e8530_get_rr0 (scc, chn);
685 break;
686
687 case 0x02:
688 val = e8530_get_rr2 (scc, chn);
689 break;
690
691 case 0x03:
692 val = e8530_get_rr3 (scc, chn);
693 break;
694
695 case 0x08:
696 val = e8530_get_rr8 (scc, chn);
697 break;
698
699 default:
700 val = scc->chn[chn].rr[reg & 15];
701 break;
702 }
703
704
705 scc->index = 0;
706
707#if DEBUG_SCC
708 fprintf (stderr, "SCC %c: get RR%u -> %02x\n",
709 scc_get_chn (chn), reg, val
710 );
711#endif
712
713 return (val);
714}
715
716/*
717 * WR0: command register
718 */
719static
720void e8530_set_wr0 (e8530_t *scc, unsigned chn, unsigned char val)
721{
722 scc->chn[chn].wr[0] = val;
723
724 scc->index = val & 7;
725
726 switch ((val >> 3) & 7) {
727 case 0x00: /* null command */
728 break;
729
730 case 0x01: /* point high */
731 scc->index += 8;
732 break;
733
734 case 0x02: /* reset external/status interrupts */
735 e8530_clr_int_cond (scc, chn, 0x01);
736 break;
737
738 case 0x03: /* send abort */
739 break;
740
741 case 0x04: /* enable interrupt on next rx character */
742 scc->chn[chn].int_on_next_rx = 1;
743 break;
744
745 case 0x05: /* reset tx interrupt pending */
746 e8530_clr_int_cond (scc, chn, 0x02);
747 break;
748
749 case 0x06: /* error reset */
750 scc->chn[chn].rr[1] &= 0x7f;
751 break;
752
753 case 0x07: /* reset highest ius */
754 break;
755 }
756}
757
758/*
759 * WR1: transmit/receive interrupt and data transfer mode definition
760 */
761static
762void e8530_set_wr1 (e8530_t *scc, unsigned chn, unsigned char val)
763{
764 scc->chn[chn].wr[1] = val;
765
766 e8530_set_int_cond (scc, chn, 0x00);
767}
768
769/*
770 * WR2: interrupt vector
771 */
772static
773void e8530_set_wr2 (e8530_t *scc, unsigned char val)
774{
775 scc->chn[0].wr[2] = val;
776 scc->chn[1].wr[2] = val;
777
778 scc->chn[0].rr[2] = val;
779 scc->chn[1].rr[2] = val;
780}
781
782/*
783 * WR3: receive parameters and control
784 */
785static
786void e8530_set_wr3 (e8530_t *scc, unsigned chn, unsigned char val)
787{
788 e8530_chn_t *c;
789
790 c = &scc->chn[chn];
791
792 if (c->wr[3] ^ val) {
793 if (val & 0x01) {
794#if DEBUG_SCC >= 1
795 fprintf (stderr, "SCC %c: receiver enable\n", scc_get_chn (chn));
796#endif
797 c->int_on_next_rx = 1;
798 c->rxd_empty = 1;
799 }
800 else {
801 c->rr[0] &= ~0x01;
802 e8530_clr_int_cond (scc, chn, 0x04);
803#if DEBUG_SCC >= 1
804 fprintf (stderr, "SCC %c: receiver disable\n", scc_get_chn (chn));
805#endif
806 }
807 }
808
809 if (val & 0x10) {
810#if DEBUG_SCC >= 1
811 if ((c->rr[0] & 0x10) == 0) {
812 fprintf (stderr, "SCC %c: sync/hunt mode\n", scc_get_chn (chn));
813 }
814#endif
815 c->rr[0] |= 0x10;
816 }
817
818 c->wr[3] = val;
819}
820
821/*
822 * WR4: transmit/receive miscellaneous parameters and modes
823 */
824static
825void e8530_set_wr4 (e8530_t *scc, unsigned chn, unsigned char val)
826{
827 scc->chn[chn].wr[4] = val;
828
829 e8530_set_params (scc, chn);
830}
831
832/*
833 * WR5: transmit parameters and controls
834 */
835static
836void e8530_set_wr5 (e8530_t *scc, unsigned chn, unsigned char val)
837{
838 e8530_chn_t *c;
839 unsigned char old;
840
841 c = &scc->chn[chn];
842
843 old = c->wr[5];
844 c->wr[5] = val;
845
846 if ((old ^ val) & 0x02) {
847 e8530_set_rts (scc, chn, (val & 0x02) != 0);
848 }
849
850 e8530_set_params (scc, chn);
851}
852
853/*
854 * WR8: transmit buffer
855 */
856static
857void e8530_set_wr8 (e8530_t *scc, unsigned chn, unsigned char val)
858{
859 e8530_chn_t *c;
860
861 c = &scc->chn[chn];
862
863 c->wr[8] = val;
864
865 c->rr[0] &= ~0x04;
866 c->txd_empty = 0;
867
868 e8530_clr_int_cond (scc, chn, 0x02);
869
870 e8530_check_txd (scc, chn);
871
872#if DEBUG_SCC
873 fprintf (stderr, "SCC %c: send %02X\n", scc_get_chn (chn), val);
874#endif
875}
876
877/*
878 * WR9: Master interrupt control
879 */
880static
881void e8530_set_wr9 (e8530_t *scc, unsigned char val)
882{
883 unsigned char old;
884
885 old = scc->chn[0].wr[9];
886
887 scc->chn[0].wr[9] = val;
888 scc->chn[1].wr[9] = val;
889
890 if ((old ^ val) & val & 0x08) {
891 /* MIE was enabled, re-check pending interrupts */
892 e8530_set_int_cond (scc, 0, 0);
893 e8530_set_int_cond (scc, 1, 0);
894 }
895}
896
897/*
898 * WR10: miscellaneous transmitter/receiver control bits
899 */
900static
901void e8530_set_wr10 (e8530_t *scc, unsigned chn, unsigned char val)
902{
903 scc->chn[chn].wr[10] = val;
904}
905
906/*
907 * WR11: clock mode control
908 */
909static
910void e8530_set_wr11 (e8530_t *scc, unsigned chn, unsigned char val)
911{
912 scc->chn[chn].wr[11] = val;
913}
914
915/*
916 * WR12: baud rate generator low byte
917 */
918static
919void e8530_set_wr12 (e8530_t *scc, unsigned chn, unsigned char val)
920{
921 scc->chn[chn].wr[12] = val;
922
923 e8530_set_params (scc, chn);
924}
925
926/*
927 * WR13: baud rate generator high byte
928 */
929static
930void e8530_set_wr13 (e8530_t *scc, unsigned chn, unsigned char val)
931{
932 scc->chn[chn].wr[13] = val;
933
934 e8530_set_params (scc, chn);
935}
936
937/*
938 * WR14: miscellaneous control bits
939 */
940static
941void e8530_set_wr14 (e8530_t *scc, unsigned chn, unsigned char val)
942{
943 scc->chn[chn].wr[14] = val;
944
945#if DEBUG_SCC
946 if ((val & 0xe0) != 0) {
947 fprintf (stderr, "SCC %c: dpll cmd: %u\n",
948 scc_get_chn (chn), (val >> 5) & 7
949 );
950 }
951#endif
952
953 e8530_set_params (scc, chn);
954}
955
956/*
957 * WR15: external/status interrupt control
958 */
959static
960void e8530_set_wr15 (e8530_t *scc, unsigned chn, unsigned char val)
961{
962 scc->chn[chn].wr[15] = val;
963 scc->chn[chn].rr[15] = val;
964}
965
966void e8530_set_reg (e8530_t *scc, unsigned chn, unsigned reg, unsigned char val)
967{
968 chn &= 1;
969
970#if DEBUG_SCC
971 fprintf (stderr, "SCC %c: set WR%u <- %02x\n",
972 scc_get_chn (chn), reg, val
973 );
974#endif
975
976 switch (reg) {
977 case 0x00:
978 e8530_set_wr0 (scc, chn, val);
979 break;
980
981 case 0x01:
982 e8530_set_wr1 (scc, chn, val);
983 break;
984
985 case 0x02:
986 e8530_set_wr2 (scc, val);
987 break;
988
989 case 0x03:
990 e8530_set_wr3 (scc, chn, val);
991 break;
992
993 case 0x04:
994 e8530_set_wr4 (scc, chn, val);
995 break;
996
997 case 0x05:
998 e8530_set_wr5 (scc, chn, val);
999 break;
1000
1001 case 0x08:
1002 e8530_set_wr8 (scc, chn, val);
1003 break;
1004
1005 case 0x09:
1006 e8530_set_wr9 (scc, val);
1007 break;
1008
1009 case 0x0a:
1010 e8530_set_wr10 (scc, chn, val);
1011 break;
1012
1013 case 0x0b:
1014 e8530_set_wr11 (scc, chn, val);
1015 break;
1016
1017 case 0x0c:
1018 e8530_set_wr12 (scc, chn, val);
1019 break;
1020
1021 case 0x0d:
1022 e8530_set_wr13 (scc, chn, val);
1023 break;
1024
1025 case 0x0e:
1026 e8530_set_wr14 (scc, chn, val);
1027 break;
1028
1029 case 0x0f:
1030 e8530_set_wr15 (scc, chn, val);
1031 break;
1032
1033 default:
1034 scc->chn[chn].wr[reg & 15] = val;
1035 break;
1036 }
1037
1038 if (reg != 0) {
1039 scc->index = 0;
1040 }
1041}
1042
1043
1044unsigned char e8530_get_ctl (e8530_t *scc, unsigned chn)
1045{
1046 return (e8530_get_reg (scc, chn & 1, scc->index));
1047}
1048
1049unsigned char e8530_get_ctl_a (e8530_t *scc)
1050{
1051 return (e8530_get_reg (scc, 0, scc->index));
1052}
1053
1054unsigned char e8530_get_ctl_b (e8530_t *scc)
1055{
1056 return (e8530_get_reg (scc, 1, scc->index));
1057}
1058
1059void e8530_set_ctl (e8530_t *scc, unsigned chn, unsigned char val)
1060{
1061 e8530_set_reg (scc, chn & 1, scc->index, val);
1062}
1063
1064void e8530_set_ctl_a (e8530_t *scc, unsigned char val)
1065{
1066 e8530_set_reg (scc, 0, scc->index, val);
1067}
1068
1069void e8530_set_ctl_b (e8530_t *scc, unsigned char val)
1070{
1071 e8530_set_reg (scc, 1, scc->index, val);
1072}
1073
1074unsigned char e8530_get_data (e8530_t *scc, unsigned chn)
1075{
1076 return (e8530_get_reg (scc, chn & 1, 8));
1077}
1078
1079unsigned char e8530_get_data_a (e8530_t *scc)
1080{
1081 return (e8530_get_reg (scc, 0, 8));
1082}
1083
1084unsigned char e8530_get_data_b (e8530_t *scc)
1085{
1086 return (e8530_get_reg (scc, 1, 8));
1087}
1088
1089void e8530_set_data (e8530_t *scc, unsigned chn, unsigned char val)
1090{
1091 e8530_set_reg (scc, chn & 1, 8, val);
1092}
1093
1094void e8530_set_data_a (e8530_t *scc, unsigned char val)
1095{
1096 e8530_set_reg (scc, 0, 8, val);
1097}
1098
1099void e8530_set_data_b (e8530_t *scc, unsigned char val)
1100{
1101 e8530_set_reg (scc, 1, 8, val);
1102}
1103
1104
1105void e8530_set_dcd (e8530_t *scc, unsigned chn, unsigned char val)
1106{
1107 unsigned char old;
1108
1109 chn &= 1;
1110 old = scc->chn[chn].rr[0];
1111
1112 if (val) {
1113 old &= ~0x08;
1114 }
1115 else {
1116 old |= 0x08;
1117 }
1118
1119 e8530_set_rr0 (scc, chn, old);
1120}
1121
1122void e8530_set_dcd_a (e8530_t *scc, unsigned char val)
1123{
1124 e8530_set_dcd (scc, 0, val);
1125}
1126
1127void e8530_set_dcd_b (e8530_t *scc, unsigned char val)
1128{
1129 e8530_set_dcd (scc, 1, val);
1130}
1131
1132void e8530_set_cts (e8530_t *scc, unsigned chn, unsigned char val)
1133{
1134 unsigned char rr0;
1135
1136 chn &= 1;
1137 rr0 = scc->chn[chn].rr[0];
1138
1139 if (val) {
1140 rr0 &= ~0x20;
1141 }
1142 else {
1143 rr0 |= 0x20;
1144 }
1145
1146 e8530_set_rr0 (scc, chn, rr0);
1147}
1148
1149void e8530_set_cts_a (e8530_t *scc, unsigned char val)
1150{
1151 e8530_set_cts (scc, 0, val);
1152}
1153
1154void e8530_set_cts_b (e8530_t *scc, unsigned char val)
1155{
1156 e8530_set_cts (scc, 1, val);
1157}
1158
1159void e8530_receive (e8530_t *scc, unsigned chn, unsigned char val)
1160{
1161 e8530_chn_t *c;
1162
1163 chn &= 1;
1164 c = &scc->chn[chn];
1165
1166 if (((c->rx_i + 1) % E8530_BUF_MAX) != c->rx_j) {
1167 c->rxbuf[c->rx_i] = val;
1168 c->rx_i = (c->rx_i + 1) % E8530_BUF_MAX;
1169 }
1170}
1171
1172void e8530_receive_a (e8530_t *scc, unsigned char val)
1173{
1174 e8530_receive (scc, 0, val);
1175}
1176
1177void e8530_receive_b (e8530_t *scc, unsigned char val)
1178{
1179 e8530_receive (scc, 1, val);
1180}
1181
1182unsigned char e8530_send (e8530_t *scc, unsigned chn)
1183{
1184 unsigned char val;
1185 e8530_chn_t *c;
1186
1187 chn &= 1;
1188 c = &scc->chn[chn];
1189
1190 if (c->tx_i == c->tx_j) {
1191 return (0);
1192 }
1193
1194 val = c->txbuf[c->tx_j];
1195 c->tx_j = (c->tx_j + 1) % E8530_BUF_MAX;
1196
1197 return (val);
1198}
1199
1200unsigned char e8530_send_a (e8530_t *scc)
1201{
1202 return (e8530_send (scc, 0));
1203}
1204
1205unsigned char e8530_send_b (e8530_t *scc)
1206{
1207 return (e8530_send (scc, 1));
1208}
1209
1210int e8530_inp_full (e8530_t *scc, unsigned chn)
1211{
1212 e8530_chn_t *c;
1213
1214 chn &= 1;
1215 c = &scc->chn[chn];
1216
1217 if (((c->rx_i + 1) % E8530_BUF_MAX) == c->rx_j) {
1218 return (1);
1219 }
1220
1221 return (0);
1222}
1223
1224int e8530_out_empty (e8530_t *scc, unsigned chn)
1225{
1226 e8530_chn_t *c;
1227
1228 chn &= 1;
1229 c = &scc->chn[chn];
1230
1231 if (c->tx_i == c->tx_j) {
1232 return (1);
1233 }
1234
1235 return (0);
1236}
1237
1238static
1239void e8530_reset_channel (e8530_t *scc, unsigned chn)
1240{
1241 e8530_chn_t *c;
1242
1243 chn &= 1;
1244 c = &scc->chn[chn];
1245
1246 c->rr[0] |= 0x04; /* tx empty */
1247// c->rr[0] |= 0x20; /* cts */
1248 c->rr[1] |= 0x01; /* all sent */
1249
1250 c->rr0_latch_msk = 0;
1251
1252 c->bps = 0;
1253 c->parity = 0;
1254 c->bpc = 0;
1255 c->stop = 0;
1256
1257 c->tx_i = 0;
1258 c->tx_j = 0;
1259
1260 c->rx_i = 0;
1261 c->rx_j = 0;
1262}
1263
1264void e8530_reset (e8530_t *scc)
1265{
1266 unsigned i;
1267
1268 scc->index = 0;
1269
1270 for (i = 0; i < 16; i++) {
1271 scc->chn[0].rr[i] = 0;
1272 scc->chn[0].wr[i] = 0;
1273 scc->chn[1].rr[i] = 0;
1274 scc->chn[1].wr[i] = 0;
1275 }
1276
1277 e8530_reset_channel (scc, 0);
1278 e8530_reset_channel (scc, 1);
1279
1280 e8530_set_rts (scc, 0, 0);
1281 e8530_set_rts (scc, 1, 0);
1282
1283 e8530_set_irq (scc, 0);
1284}
1285
1286static inline
1287void e8530_chn_clock (e8530_t *scc, unsigned chn, unsigned n)
1288{
1289 e8530_chn_t *c;
1290
1291 c = &scc->chn[chn];
1292
1293 if (n < c->char_clk_cnt) {
1294 c->char_clk_cnt -= n;
1295 return;
1296 }
1297 else {
1298 n -= c->char_clk_cnt;
1299 }
1300
1301 if (n > c->char_clk_div) {
1302 n = n % c->char_clk_div;
1303 }
1304
1305 c->char_clk_cnt = c->char_clk_div - n;
1306
1307 c->read_char_cnt = c->read_char_max;
1308 c->write_char_cnt = c->write_char_max;
1309
1310 e8530_check_rxd (scc, chn);
1311 e8530_check_txd (scc, chn);
1312}
1313
1314void e8530_clock (e8530_t *scc, unsigned n)
1315{
1316 e8530_chn_clock (scc, 0, n);
1317 e8530_chn_clock (scc, 1, n);
1318}