1 /*
2  * viacore.c - Core functions for VIA emulation.
3  *
4  * Written by
5  *  Andre Fachat <fachat@physik.tu-chemnitz.de>
6  *  Andreas Boose <viceteam@t-online.de>
7  *
8  * This file is part of VICE, the Versatile Commodore Emulator.
9  * See README for copyright notice.
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24  *  02111-1307  USA.
25  *
26  */
27 
28 #include "vice.h"
29 
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include "alarm.h"
34 #include "clkguard.h"
35 #include "interrupt.h"
36 #include "lib.h"
37 #include "log.h"
38 #include "monitor.h"
39 #include "snapshot.h"
40 #include "types.h"
41 #include "via.h"
42 
43 
44 /*
45  * 24jan97 a.fachat
46  * new interrupt handling, hopefully according to the specs now.
47  * All interrupts (note: not timer events (i.e. alarms) are put
48  * into one interrupt flag.
49  * if an interrupt condition changes, the function (i.e. cpp macro)
50  * update_myviairq() id called, that checks the IRQ line state.
51  * This is now possible, as ettore has decoupled A_* alarm events
52  * from interrupts for performance reasons.
53  *
54  * A new function for signaling rising/falling edges on the
55  * control lines is introduced:
56  *      myvia_signal(VIA_SIG_[CA1|CA2|CB1|CB2], VIA_SIG_[RISE|FALL])
57  * which signals the corresponding edge to the VIA. The constants
58  * are defined in via.h.
59  *
60  * Except for shift register and input latching everything should be ok now.
61  */
62 
63 /* Timer debugging */
64 /*#define MYVIA_TIMER_DEBUG */
65 /* when PB7 is really used, set this
66    to enable pulse output from the timer.
67    Otherwise PB7 state is computed only
68    when port B is read -
69    not yet implemented */
70 #define MYVIA_NEED_PB7
71 /* When you really need latching, define this.
72    It implies additional READ_PR* when
73    writing the snapshot. When latching is
74    enabled: it reads the port when enabling,
75    and when an active C*1 transition occurs.
76    It does not read the port when reading the
77    port register. Side-effects beware! */
78 /* FIXME: this doesnt even work anymore */
79 /* #define MYVIA_NEED_LATCHING */
80 
81 /*
82  * local functions
83  */
84 
85 #define IS_CA2_OUTPUT()          ((via_context->via[VIA_PCR] & 0x0c) == 0x0c)
86 #define IS_CA2_INDINPUT()        ((via_context->via[VIA_PCR] & 0x0a) == 0x02)
87 #define IS_CA2_HANDSHAKE()       ((via_context->via[VIA_PCR] & 0x0c) == 0x08)
88 #define IS_CA2_PULSE_MODE()      ((via_context->via[VIA_PCR] & 0x0e) == 0x09)
89 #define IS_CA2_TOGGLE_MODE()     ((via_context->via[VIA_PCR] & 0x0e) == 0x08)
90 
91 #define IS_CB2_OUTPUT()          ((via_context->via[VIA_PCR] & 0xc0) == 0xc0)
92 #define IS_CB2_INDINPUT()        ((via_context->via[VIA_PCR] & 0xa0) == 0x20)
93 #define IS_CB2_HANDSHAKE()       ((via_context->via[VIA_PCR] & 0xc0) == 0x80)
94 #define IS_CB2_PULSE_MODE()      ((via_context->via[VIA_PCR] & 0xe0) == 0x90)
95 #define IS_CB2_TOGGLE_MODE()     ((via_context->via[VIA_PCR] & 0xe0) == 0x80)
96 
97 #define IS_PA_INPUT_LATCH()      (via_context->via[VIA_ACR] & 0x01)
98 #define IS_PB_INPUT_LATCH()      (via_context->via[VIA_ACR] & 0x02)
99 
100 /*
101  * 01apr98 a.fachat
102  *
103  * One-shot Timing (partly from 6522-VIA.txt):
104 
105                      +-+ +-+ +-+ +-+ +-+ +-+   +-+ +-+ +-+ +-+ +-+ +-+
106                 02 --+ +-+ +-+ +-+ +-+ +-+ +-#-+ +-+ +-+ +-+ +-+ +-+ +-
107                        |   |                           |
108                        +---+                           |
109        WRITE T1C-H ----+   +-----------------#-------------------------
110         ___                |                           |
111         IRQ OUTPUT --------------------------#---------+
112                            |                           +---------------
113                            |                           |
114         PB7 OUTPUT --------+                           +---------------
115                            +-----------------#---------+
116          T1                | N |N-1|N-2|N-3|     | 0 | -1|N  |N-1|N-2|
117          T2                | N |N-1|N-2|N-3|     | 0 | -1| -2| -3| -4|
118                            |                           |
119                            |<---- N + 1.5 CYCLES ----->|<--- N + 2 cycles --->
120                                                          +---+
121  myviat*u* clk ------------------------------------------+   +--------
122                                                      |
123                                                      |
124                                                   call of
125                                                 int_myvia*
126                                                    here
127 
128    real myviatau value = myviatau* + TAUOFFSET
129    myviatbu = myviatbu* + 0
130 
131  *
132  * IRQ and PB7 are set/toggled at the low-high transition of Phi2,
133  * but int_* is called a half-cycle before that. Does that matter?
134  *
135  * PB7 output is still to be implemented
136  */
137 
138 /* timer values do not depend on a certain value here, but PB7 does... */
139 #define TAUOFFSET       (-1)
140 
141 
142 static void viacore_intt1(CLOCK offset, void *data);
143 static void viacore_intt2(CLOCK offset, void *data);
144 
145 
via_restore_int(via_context_t * via_context,int value)146 static void via_restore_int(via_context_t *via_context, int value)
147 {
148     (via_context->restore_int)(via_context, via_context->int_num, value);
149 }
150 
update_myviairq(via_context_t * via_context)151 inline static void update_myviairq(via_context_t *via_context)
152 {
153     (via_context->set_int)(via_context, via_context->int_num,
154                            (via_context->ifr & via_context->ier & 0x7f)
155                            ? via_context->irq_line : 0, *(via_context->clk_ptr));
156 }
157 
update_myviairq_rclk(via_context_t * via_context,CLOCK rclk)158 inline static void update_myviairq_rclk(via_context_t *via_context, CLOCK rclk)
159 {
160     (via_context->set_int)(via_context, via_context->int_num,
161                            (via_context->ifr & via_context->ier & 0x7f)
162                            ? via_context->irq_line : 0, rclk);
163 }
164 
165 /* the next two are used in myvia_read() */
166 
myviata(via_context_t * via_context)167 inline static CLOCK myviata(via_context_t *via_context)
168 {
169     if (*(via_context->clk_ptr) < via_context->tau - TAUOFFSET) {
170         return via_context->tau - TAUOFFSET - *(via_context->clk_ptr) - 2;
171     } else {
172         return (via_context->tal - (*(via_context->clk_ptr) - via_context->tau
173                                     + TAUOFFSET) % (via_context->tal + 2));
174     }
175 }
176 
myviatb(via_context_t * via_context)177 inline static CLOCK myviatb(via_context_t *via_context)
178 {
179     CLOCK t2;
180 
181     if (via_context->via[VIA_ACR] & 0x20) {
182         t2 = (via_context->t2ch << 8) | via_context->t2cl;
183     } else {
184         t2 = via_context->tbu - *(via_context->clk_ptr) - 2;
185 
186         if (via_context->tbi) {
187             uint8_t t2hi = via_context->t2ch;
188 
189             if (*(via_context->clk_ptr) == via_context->tbi + 1) {
190                 t2hi--;
191             }
192 
193             t2 = (t2hi << 8) | (t2 & 0xff);
194         }
195     }
196 
197     return t2;
198 }
199 
update_myviatal(via_context_t * via_context,CLOCK rclk)200 inline static void update_myviatal(via_context_t *via_context, CLOCK rclk)
201 {
202     via_context->pb7x = 0;
203     via_context->pb7xx = 0;
204 
205     if (rclk > via_context->tau) {
206         int nuf = (via_context->tal + 1 + rclk - via_context->tau)
207                   / (via_context->tal + 2);
208 
209         if (!(via_context->via[VIA_ACR] & 0x40)) {
210             /* one shot mode */
211             if (((nuf - via_context->pb7sx) > 1) || (!(via_context->pb7))) {
212                 via_context->pb7o = 1;
213                 via_context->pb7sx = 0;
214             }
215         }
216         via_context->pb7 ^= (nuf & 1);
217 
218         via_context->tau = TAUOFFSET + via_context->tal + 2
219                    + (rclk - (rclk - via_context->tau + TAUOFFSET)
220                    % (via_context->tal + 2));
221         if (rclk == via_context->tau - via_context->tal - 1) {
222             via_context->pb7xx = 1;
223         }
224     }
225 
226     if (via_context->tau == rclk) {
227         via_context->pb7x = 1;
228     }
229 
230     via_context->tal = via_context->via[VIA_T1LL]
231                        + (via_context->via[VIA_T1LH] << 8);
232 }
233 
234 /* ------------------------------------------------------------------------- */
viacore_disable(via_context_t * via_context)235 void viacore_disable(via_context_t *via_context)
236 {
237     alarm_unset(via_context->t1_alarm);
238     alarm_unset(via_context->t2_alarm);
239     alarm_unset(via_context->sr_alarm);
240     via_context->enabled = 0;
241 }
242 
243 /*
244  * according to Rockwell, all internal registers are cleared, except
245  * for the Timer (1 and 2, counter and latches) and the shift register.
246  */
viacore_reset(via_context_t * via_context)247 void viacore_reset(via_context_t *via_context)
248 {
249     int i;
250 
251     /* port data/ddr */
252     for (i = 0; i < 4; i++) {
253         via_context->via[i] = 0;
254     }
255     /* timer 1/2 counter/latches */
256 #if 0
257     for (i = 4; i < 10; i++) {
258         via_context->via[i] = 0xff;
259     }
260 #endif
261     /* omit shift register (10) */
262     for (i = 11; i < 16; i++) {
263         via_context->via[i] = 0;
264     }
265 
266     via_context->tal = 0xffff;
267     via_context->t2cl = 0xff;
268     via_context->t2ch = 0xff;
269     via_context->tau = *(via_context->clk_ptr);
270     via_context->tbu = *(via_context->clk_ptr);
271 
272     via_context->read_clk = 0;
273 
274     via_context->ier = 0;
275     via_context->ifr = 0;
276 
277     via_context->pb7 = 0;
278     via_context->pb7x = 0;
279     via_context->pb7o = 0;
280     via_context->pb7xx = 0;
281     via_context->pb7sx = 0;
282 
283     via_context->shift_state = 0;
284 
285     /* disable vice interrupts */
286     via_context->tai = 0;
287     via_context->tbi = 0;
288     alarm_unset(via_context->t1_alarm);
289     alarm_unset(via_context->t2_alarm);
290     alarm_unset(via_context->sr_alarm);
291     update_myviairq(via_context);
292 
293     via_context->oldpa = 0;
294     via_context->oldpb = 0;
295 
296     via_context->ca2_state = 1;
297     via_context->cb2_state = 1;
298     (via_context->set_ca2)(via_context, via_context->ca2_state);      /* input = high */
299     (via_context->set_cb2)(via_context, via_context->cb2_state);      /* input = high */
300 
301     if (via_context && via_context->reset) {
302         (via_context->reset)(via_context);
303     }
304 
305     via_context->enabled = 1;
306 }
307 
viacore_signal(via_context_t * via_context,int line,int edge)308 void viacore_signal(via_context_t *via_context, int line, int edge)
309 {
310     switch (line) {
311         case VIA_SIG_CA1:
312             if ((edge ? 1 : 0) == (via_context->via[VIA_PCR] & 0x01)) {
313                 if (IS_CA2_TOGGLE_MODE() && !(via_context->ca2_state)) {
314                     via_context->ca2_state = 1;
315                     (via_context->set_ca2)(via_context, via_context->ca2_state);
316                 }
317                 via_context->ifr |= VIA_IM_CA1;
318                 update_myviairq(via_context);
319 #ifdef MYVIA_NEED_LATCHING
320                 if (IS_PA_INPUT_LATCH()) {
321                     via_context->ila = (via_context->read_pra)(via_context, VIA_PRA);
322                 }
323 #endif
324             }
325             break;
326         case VIA_SIG_CA2:
327             if (!(via_context->via[VIA_PCR] & 0x08)) {
328                 via_context->ifr |= (((edge << 2)
329                                     ^ via_context->via[VIA_PCR]) & 0x04) ?
330                                     0 : VIA_IM_CA2;
331                 update_myviairq(via_context);
332             }
333             break;
334         case VIA_SIG_CB1:
335             if ((edge ? 0x10 : 0) == (via_context->via[VIA_PCR] & 0x10)) {
336                 if (IS_CB2_TOGGLE_MODE() && !(via_context->cb2_state)) {
337                     via_context->cb2_state = 1;
338                     (via_context->set_cb2)(via_context, via_context->cb2_state);
339                 }
340                 via_context->ifr |= VIA_IM_CB1;
341                 update_myviairq(via_context);
342 #ifdef MYVIA_NEED_LATCHING
343                 if (IS_PB_INPUT_LATCH()) {
344                     via_context->ilb = (via_context->read_prb)(via_context);
345                 }
346 #endif
347             }
348             break;
349         case VIA_SIG_CB2:
350             if (!(via_context->via[VIA_PCR] & 0x80)) {
351                 via_context->ifr |= (((edge << 6)
352                                     ^ via_context->via[VIA_PCR]) & 0x40) ?
353                                     0 : VIA_IM_CB2;
354                 update_myviairq(via_context);
355             }
356             break;
357     }
358 }
359 
viacore_store(via_context_t * via_context,uint16_t addr,uint8_t byte)360 void viacore_store(via_context_t *via_context, uint16_t addr, uint8_t byte)
361 {
362     CLOCK rclk;
363 
364     if (*(via_context->rmw_flag)) {
365         (*(via_context->clk_ptr))--;
366         *(via_context->rmw_flag) = 0;
367         viacore_store(via_context, addr, via_context->last_read);
368         (*(via_context->clk_ptr))++;
369     }
370 
371     /* stores have a one-cycle offset if CLK++ happens before store */
372     rclk = *(via_context->clk_ptr) - via_context->write_offset;
373 
374     addr &= 0xf;
375 
376     switch (addr) {
377         /* these are done with saving the value */
378         case VIA_PRA:           /* port A */
379             via_context->ifr &= ~VIA_IM_CA1;
380             if (!IS_CA2_INDINPUT()) {
381                 via_context->ifr &= ~VIA_IM_CA2;
382             }
383             if (IS_CA2_HANDSHAKE()) {
384                 via_context->ca2_state = 0;
385                 (via_context->set_ca2)(via_context, via_context->ca2_state);
386                 if (IS_CA2_PULSE_MODE()) {
387                     via_context->ca2_state = 1;
388                     (via_context->set_ca2)(via_context, via_context->ca2_state);
389                 }
390             }
391             if (via_context->ier & (VIA_IM_CA1 | VIA_IM_CA2)) {
392                 update_myviairq(via_context);
393             }
394             /* fall through */
395 
396         case VIA_PRA_NHS: /* port A, no handshake */
397             via_context->via[VIA_PRA_NHS] = byte;
398             addr = VIA_PRA;
399             /* fall through */
400 
401         case VIA_DDRA:
402             via_context->via[addr] = byte;
403             byte = via_context->via[VIA_PRA] | ~(via_context->via[VIA_DDRA]);
404             (via_context->store_pra)(via_context, byte, via_context->oldpa, addr);
405             via_context->oldpa = byte;
406             break;
407 
408         case VIA_PRB:           /* port B */
409             via_context->ifr &= ~VIA_IM_CB1;
410             if ((via_context->via[VIA_PCR] & 0xa0) != 0x20) {
411                 via_context->ifr &= ~VIA_IM_CB2;
412             }
413             if (IS_CB2_HANDSHAKE()) {
414                 via_context->cb2_state = 0;
415                 (via_context->set_cb2)(via_context, via_context->cb2_state);
416                 if (IS_CB2_PULSE_MODE()) {
417                     via_context->cb2_state = 1;
418                     (via_context->set_cb2)(via_context, via_context->cb2_state);
419                 }
420             }
421             if (via_context->ier & (VIA_IM_CB1 | VIA_IM_CB2)) {
422                 update_myviairq(via_context);
423             }
424             /* fall through */
425 
426         case VIA_DDRB:
427             via_context->via[addr] = byte;
428             byte = via_context->via[VIA_PRB] | ~(via_context->via[VIA_DDRB]);
429             (via_context->store_prb)(via_context, byte, via_context->oldpb, addr);
430             via_context->oldpb = byte;
431             break;
432 
433         case VIA_SR:            /* Serial Port output buffer */
434             via_context->via[addr] = byte;
435             /* shift state can only be reset once 8 bits are complete */
436             if (via_context->ifr & VIA_IM_SR) {
437                 via_context->ifr &= ~VIA_IM_SR;
438                 update_myviairq(via_context);
439                 via_context->shift_state = 0;
440             }
441 
442             (via_context->store_sr)(via_context, byte);
443             break;
444 
445         /* Timers */
446 
447         case VIA_T1CL:
448         case VIA_T1LL:
449             via_context->via[VIA_T1LL] = byte;
450             update_myviatal(via_context, rclk);
451             break;
452 
453         case VIA_T1CH:  /* Write timer A high */
454             via_context->via[VIA_T1LH] = byte;
455             update_myviatal(via_context, rclk);
456             /* load counter with latch value */
457             via_context->tau = rclk + via_context->tal + 3 + TAUOFFSET;
458             via_context->tai = rclk + via_context->tal + 2;
459             alarm_set(via_context->t1_alarm, via_context->tai);
460 
461             /* set pb7 state */
462             via_context->pb7 = 0;
463             via_context->pb7o = 0;
464 
465             /* Clear T1 interrupt */
466             via_context->ifr &= ~VIA_IM_T1;
467             update_myviairq(via_context);
468             break;
469 
470         case VIA_T1LH:          /* Write timer A high order latch */
471             via_context->via[addr] = byte;
472             update_myviatal(via_context, rclk);
473 
474             /* CAUTION: according to the synertek notes, writing to T1LH does
475                NOT change the interrupt flags. however, not doing so breaks eg
476                the VIC20 game "bandits". also in a seperare test program it was
477                verified that indeed writing to the high order latch clears the
478                interrupt flag, also on synertek VIAs. (see via_t1irqack) */
479 
480             /* Clear T1 interrupt */
481             via_context->ifr &= ~VIA_IM_T1;
482             update_myviairq(via_context);
483             break;
484 
485         case VIA_T2LL:          /* Write timer 2 low latch */
486             via_context->via[VIA_T2LL] = byte;
487             (via_context->store_t2l)(via_context, byte);
488             break;
489 
490         case VIA_T2CH:            /* Write timer 2 high counter/latch */
491             /* update counter and latch values */
492             via_context->via[VIA_T2LH] = byte;
493             via_context->t2cl = via_context->via[VIA_T2LL];
494             via_context->t2ch = byte;
495 
496             /* start T2 only in timer mode, leave unchanged in pulse counting mode */
497             if (!(via_context->via[VIA_ACR] & 0x20)) {
498                 /* set the next alarm to the low latch value as timer cascading mode change
499                 matters at each underflow of the T2 low counter */
500                 via_context->tbu = rclk + via_context->t2cl + 3;
501                 via_context->tbi = rclk + via_context->t2cl + 1;
502                 alarm_set(via_context->t2_alarm, via_context->tbi);
503             }
504 
505             /* Clear T2 interrupt */
506             via_context->ifr &= ~VIA_IM_T2;
507             update_myviairq(via_context);
508             break;
509 
510         /* Interrupts */
511 
512         case VIA_IFR:           /* 6522 Interrupt Flag Register */
513             via_context->ifr &= ~byte;
514             update_myviairq(via_context);
515 
516             /* FIXME: clearing any timer interrupt should set the relevant timer alarm */
517             break;
518 
519         case VIA_IER:           /* Interrupt Enable Register */
520             if (byte & VIA_IM_IRQ) {
521                 /* set interrupts */
522                 via_context->ier |= byte & 0x7f;
523             } else {
524                 /* clear interrupts */
525                 via_context->ier &= ~byte;
526             }
527             update_myviairq(via_context);
528             break;
529 
530         /* Control */
531 
532         case VIA_ACR:
533             /* bit 7 timer 1 output to PB7 */
534             update_myviatal(via_context, rclk);
535             if ((via_context->via[VIA_ACR] ^ byte) & 0x80) {
536                 if (byte & 0x80) {
537                     via_context->pb7 = 1 ^ via_context->pb7x;
538                 }
539             }
540             if ((via_context->via[VIA_ACR] ^ byte) & 0x40) {
541                 via_context->pb7 ^= via_context->pb7sx;
542                 if ((byte & 0x40)) {
543                     if (via_context->pb7x || via_context->pb7xx) {
544                         if (via_context->tal) {
545                             via_context->pb7o = 1;
546                         } else {
547                             via_context->pb7o = 0;
548                             if ((via_context->via[VIA_ACR] & 0x80)
549                                 && via_context->pb7x
550                                 && (!(via_context->pb7xx))) {
551                                 via_context->pb7 ^= 1;
552                             }
553                         }
554                     }
555                 }
556             }
557             via_context->pb7sx = via_context->pb7x;
558 
559             /* bit 1, 0  latch enable port B and A */
560 #ifdef MYVIA_NEED_LATCHING
561             /* switch on port A latching - FIXME: is this ok? */
562             if ((!(via_context->via[addr] & 1)) && (byte & 1)) {
563                 via_context->ila = (via_context->read_pra)(via_context, addr);
564             }
565             /* switch on port B latching - FIXME: is this ok? */
566             if ((!(via_context->via[addr] & 2)) && (byte & 2)) {
567                 via_context->ilb = (via_context->read_prb)(via_context);
568             }
569 #endif
570 
571             /* switch between timer and pulse counting mode if bit 5 changes */
572             if ((via_context->via[VIA_ACR] ^ byte) & 0x20) {
573                 if (byte & 0x20) {
574                     /* Pulse counting mode: set t2 to the current T2 value;
575                     PB6 should always update t2 and update irq on underflow */
576                     CLOCK stop = myviatb(via_context);
577                     via_context->t2cl = (uint8_t)(stop & 0xff);
578                     via_context->t2ch = (uint8_t)((stop >> 8) & 0xff);
579 
580                     /* stop alarm to prevent t2 and T2 updates */
581                     alarm_unset(via_context->t2_alarm);
582                     via_context->tbi = 0;
583                 } else {
584                     /* Timer mode; set the next alarm to the low latch value as timer cascading mode change
585                     matters at each underflow of the T2 low counter */
586                     via_context->tbu = rclk + via_context->t2cl + 3;
587                     via_context->tbi = rclk + via_context->t2cl + 1;
588                     alarm_set(via_context->t2_alarm, via_context->tbi);
589                 }
590             }
591 
592             /* handle the t2 alarm for the serial shift register
593              *
594              * FIXME: it is not clear what happens when pulse counting mode is
595              *        selected for t2
596              */
597             if ((byte & 0x20) == 0) {
598                 if (((byte & 0x0c) == 0x04) || /* FIXME: shift register under t2 control */
599                     ((byte & 0x1c) == 0x10)) {  /* FIXME: shift register free running at t2 rate */
600                     /* Timer mode; set the next alarm to the low latch value as timer cascading mode change
601                     matters at each underflow of the T2 low counter */
602                     via_context->tbu = rclk + via_context->t2cl + 3;
603                     via_context->tbi = rclk + via_context->t2cl + 1;
604                     alarm_set(via_context->t2_alarm, via_context->tbi);
605                 }
606             }
607 
608             /* bit 4, 3, 2 shift register control */
609             if ((byte & 0x0c) == 0x08) {
610                 /* shift under control of phi2 */
611                 if ((byte & 0x10) == 0x10) {
612                     alarm_set(via_context->sr_alarm, rclk + 3); /* FIXME */
613                 } else {
614                     alarm_set(via_context->sr_alarm, rclk + 3); /* FIXME */
615                 }
616             } else {
617                 /* when disabled or external clock, stop the alarm */
618                 alarm_unset(via_context->sr_alarm);
619             }
620 
621             via_context->via[addr] = byte;
622             (via_context->store_acr)(via_context, byte);
623 
624             break;
625 
626         case VIA_PCR:
627 
628             /* bit 7, 6, 5  CB2 handshake/interrupt control */
629             /* bit 4  CB1 interrupt control */
630 
631             /* bit 3, 2, 1  CA2 handshake/interrupt control */
632             /* bit 0  CA1 interrupt control */
633 
634             if ((byte & 0x0e) == 0x0c) { /* set output low */
635                 via_context->ca2_state = 0;
636             } else
637             if ((byte & 0x0e) == 0x0e) { /* set output high */
638                 via_context->ca2_state = 1;
639             } else {                    /* set to toggle/pulse/input */
640                 /* FIXME: is this correct if handshake is already active? */
641                 via_context->ca2_state = 1;
642             }
643             (via_context->set_ca2)(via_context, via_context->ca2_state);
644 
645             if ((byte & 0xe0) == 0xc0) { /* set output low */
646                 via_context->cb2_state = 0;
647             } else
648             if ((byte & 0xe0) == 0xe0) { /* set output high */
649                 via_context->cb2_state = 1;
650             } else {                    /* set to toggle/pulse/input */
651                 /* FIXME: is this correct if handshake is already active? */
652                 via_context->cb2_state = 1;
653             }
654             (via_context->set_cb2)(via_context, via_context->cb2_state);
655 
656             (via_context->store_pcr)(via_context, byte, addr);
657 
658             via_context->via[addr] = byte;
659 
660             break;
661 
662         default:
663             via_context->via[addr] = byte;
664     }
665 }
666 
667 
668 /* ------------------------------------------------------------------------- */
669 
viacore_read(via_context_t * via_context,uint16_t addr)670 uint8_t viacore_read(via_context_t *via_context, uint16_t addr)
671 {
672 #ifdef MYVIA_TIMER_DEBUG
673     uint8_t viacore_read_(via_context_t *via_context, uint16_t);
674     uint8_t retv = myvia_read_(via_context, addr);
675     addr &= 0x0f;
676     if ((addr > 3 && addr < 10) || app_resources.debugFlag) {
677         log_message(via_context->log,
678                     "myvia_read(%x) -> %02x, clk=%d", addr, retv,
679                     *(via_context->clk_ptr));
680     }
681     return retv;
682 }
683 
viacore_read_(via_context_t * via_context,uint16_t addr)684 uint8_t viacore_read_(via_context_t *via_context, uint16_t addr)
685 {
686 #endif
687     uint8_t byte = 0xff;
688     CLOCK rclk;
689 
690     addr &= 0xf;
691 
692     via_context->read_clk = *(via_context->clk_ptr);
693     via_context->read_offset = 0;
694     rclk = *(via_context->clk_ptr);
695 
696     if (addr >= VIA_T1CL && addr <= VIA_IER) {
697         if (via_context->tai && (via_context->tai < *(via_context->clk_ptr))) {
698             viacore_intt1(*(via_context->clk_ptr) - via_context->tai,
699                           (void *)via_context);
700         }
701         if (via_context->tbi && (via_context->tbi < *(via_context->clk_ptr))) {
702             viacore_intt2(*(via_context->clk_ptr) - via_context->tbi,
703                           (void *)via_context);
704         }
705     }
706 
707     switch (addr) {
708         case VIA_PRA:           /* port A */
709             via_context->ifr &= ~VIA_IM_CA1;
710             if ((via_context->via[VIA_PCR] & 0x0a) != 0x02) {
711                 via_context->ifr &= ~VIA_IM_CA2;
712             }
713             if (IS_CA2_HANDSHAKE()) {
714                 via_context->ca2_state = 0;
715                 (via_context->set_ca2)(via_context, via_context->ca2_state);
716                 if (IS_CA2_PULSE_MODE()) {
717                     via_context->ca2_state = 1;
718                     (via_context->set_ca2)(via_context, via_context->ca2_state);
719                 }
720             }
721             if (via_context->ier & (VIA_IM_CA1 | VIA_IM_CA2)) {
722                 update_myviairq(via_context);
723             }
724             /* falls through */
725 
726         case VIA_PRA_NHS: /* port A, no handshake */
727             /* WARNING: this pin reads the voltage of the output pins, not
728                the ORA value as the other port. Value read might be different
729                from what is expected due to excessive load. */
730 #ifdef MYVIA_NEED_LATCHING
731             if (IS_PA_INPUT_LATCH()) {
732                 byte = via_context->ila;
733             } else {
734                 byte = (via_context->read_pra)(via_context, addr);
735             }
736 #else
737             byte = (via_context->read_pra)(via_context, addr);
738 #endif
739             via_context->ila = byte;
740             via_context->last_read = byte;
741             return byte;
742 
743         case VIA_PRB:           /* port B */
744             via_context->ifr &= ~VIA_IM_CB1;
745             if ((via_context->via[VIA_PCR] & 0xa0) != 0x20) {
746                 via_context->ifr &= ~VIA_IM_CB2;
747             }
748             if (via_context->ier & (VIA_IM_CB1 | VIA_IM_CB2)) {
749                 update_myviairq(via_context);
750             }
751 
752             /* WARNING: this pin reads the ORA for output pins, not
753                the voltage on the pins as the other port. */
754 #ifdef MYVIA_NEED_LATCHING
755             if (IS_PB_INPUT_LATCH()) {
756                 byte = via_context->ilb;
757             } else {
758                 byte = (via_context->read_prb)(via_context);
759             }
760 #else
761             byte = (via_context->read_prb)(via_context);
762 #endif
763             via_context->ilb = byte;
764             byte = (byte & ~(via_context->via[VIA_DDRB]))
765                    | (via_context->via[VIA_PRB] & via_context->via[VIA_DDRB]);
766 
767             if (via_context->via[VIA_ACR] & 0x80) {
768                 update_myviatal(via_context, rclk);
769                 byte = (byte & 0x7f)
770                        | (((via_context->pb7 ^ via_context->pb7x)
771                            | via_context->pb7o) ? 0x80 : 0);
772             }
773             via_context->last_read = byte;
774             return byte;
775 
776         /* Timers */
777 
778         case VIA_T1CL /*TIMER_AL */:    /* timer A low counter */
779             via_context->ifr &= ~VIA_IM_T1;
780             update_myviairq(via_context);
781             via_context->last_read = (uint8_t)(myviata(via_context) & 0xff);
782             return via_context->last_read;
783 
784         case VIA_T1CH /*TIMER_AH */:    /* timer A high counter */
785             via_context->last_read = (uint8_t)((myviata(via_context) >> 8) & 0xff);
786             return via_context->last_read;
787 
788         case VIA_T2CL /*TIMER_BL */:    /* timer B low counter */
789             via_context->ifr &= ~VIA_IM_T2;
790             update_myviairq(via_context);
791             via_context->last_read = (uint8_t)(myviatb(via_context) & 0xff);
792             return via_context->last_read;
793 
794         case VIA_T2CH /*TIMER_BH */:    /* timer B high counter */
795             via_context->last_read = (uint8_t)((myviatb(via_context) >> 8) & 0xff);
796             return via_context->last_read;
797 
798         case VIA_SR:            /* Serial Port Shift Register */
799             /* shift state can only be reset once 8 bits are complete */
800             if (via_context->ifr & VIA_IM_SR) {
801                 via_context->ifr &= ~VIA_IM_SR;
802                 update_myviairq(via_context);
803                 via_context->shift_state = 0;
804             }
805             via_context->last_read = via_context->via[addr];
806             return via_context->last_read;
807 
808         /* Interrupts */
809 
810         case VIA_IFR:           /* Interrupt Flag Register */
811             {
812                 uint8_t t = via_context->ifr;
813                 if (via_context->ifr & via_context->ier /*[VIA_IER] */) {
814                     t |= 0x80;
815                 }
816                 via_context->last_read = t;
817                 return (t);
818             }
819 
820         case VIA_IER:           /* 6522 Interrupt Control Register */
821             via_context->last_read = (via_context->ier /*[VIA_IER] */ | 0x80);
822             return via_context->last_read;
823     }
824 
825     via_context->last_read = via_context->via[addr];
826 
827     return via_context->via[addr];
828 }
829 
830 /* return value of a register without side effects */
831 /* FIXME: this is buggy/incomplete */
viacore_peek(via_context_t * via_context,uint16_t addr)832 uint8_t viacore_peek(via_context_t *via_context, uint16_t addr)
833 {
834 
835     addr &= 0xf;
836 
837     switch (addr) {
838         case VIA_PRA:
839         case VIA_PRA_NHS: /* port A, no handshake */
840             {
841                 uint8_t byte;
842                 /* WARNING: this pin reads the voltage of the output pins, not
843                 the ORA value as the other port. Value read might be different
844                 from what is expected due to excessive load. */
845 #ifdef MYVIA_NEED_LATCHING
846                 if (IS_PA_INPUT_LATCH()) {
847                     byte = via_context->ila;
848                 } else {
849                     /* FIXME: side effects ? */
850                     byte = (via_context->read_pra)(via_context, addr);
851                 }
852 #else
853                 /* FIXME: side effects ? */
854                 byte = (via_context->read_pra)(via_context, addr);
855 #endif
856                 return byte;
857             }
858 
859         case VIA_PRB:           /* port B */
860             {
861                 uint8_t byte;
862 #ifdef MYVIA_NEED_LATCHING
863                 if (IS_PB_INPUT_LATCH()) {
864                     byte = via_context->ilb;
865                 } else {
866                     /* FIXME: side effects ? */
867                     byte = (via_context->read_prb)(via_context);
868                 }
869 #else
870                 /* FIXME: side effects ? */
871                 byte = (via_context->read_prb)(via_context);
872 #endif
873                 byte = (byte & ~(via_context->via[VIA_DDRB]))
874                        | (via_context->via[VIA_PRB] & via_context->via[VIA_DDRB]);
875                 if (via_context->via[VIA_ACR] & 0x80) {
876                     /* update_myviatal(via_context, rclk); */
877                     byte = (byte & 0x7f) | (((via_context->pb7 ^ via_context->pb7x)
878                                              | via_context->pb7o) ? 0x80 : 0);
879                 }
880                 return byte;
881             }
882         case VIA_DDRA:
883         case VIA_DDRB:
884             break;
885 
886         /* Timers */
887 
888         case VIA_T1CL /*TIMER_AL */:    /* timer A low */
889             return (uint8_t)(myviata(via_context) & 0xff);
890 
891         case VIA_T1CH /*TIMER_AH */:    /* timer A high */
892             return (uint8_t)((myviata(via_context) >> 8) & 0xff);
893 
894         case VIA_T1LL: /* timer A low order latch */
895         case VIA_T1LH: /* timer A high order latch */
896             break;
897 
898         case VIA_T2CL /*TIMER_BL */:    /* timer B low */
899             return (uint8_t)(myviatb(via_context) & 0xff);
900 
901         case VIA_T2CH /*TIMER_BH */:    /* timer B high */
902             return (uint8_t)((myviatb(via_context) >> 8) & 0xff);
903 
904         case VIA_IFR:           /* Interrupt Flag Register */
905             return via_context->ifr;
906 
907         case VIA_IER:           /* 6522 Interrupt Control Register */
908             return via_context->ier | 0x80;
909 
910         case VIA_PCR:
911         case VIA_ACR:
912         case VIA_SR:
913             break;
914     }
915 
916     return via_context->via[addr];
917 }
918 
919 /* ------------------------------------------------------------------------- */
920 
viacore_intt1(CLOCK offset,void * data)921 static void viacore_intt1(CLOCK offset, void *data)
922 {
923     CLOCK rclk;
924     via_context_t *via_context = (via_context_t *)data;
925 
926     rclk = *(via_context->clk_ptr) - offset;
927 
928 
929 #ifdef MYVIA_TIMER_DEBUG
930     if (app_resources.debugFlag) {
931         log_message(via_context->log, "myvia timer A interrupt");
932     }
933 #endif
934 
935     if (!(via_context->via[VIA_ACR] & 0x40)) {     /* one-shot mode */
936 #ifdef MYVIA_TIMER_DEBUG
937         log_message(via_context->log,
938                     "MYVIA Timer A interrupt -- one-shot mode: next int won't happen");
939 #endif
940         alarm_unset(via_context->t1_alarm);
941         via_context->tai = 0;
942     } else {                    /* continuous mode */
943         /* load counter with latch value */
944         via_context->tai += via_context->tal + 2;
945         alarm_set(via_context->t1_alarm, via_context->tai);
946 
947         /* Let tau also keep up with the cpu clock
948            this should avoid "% (via_context->tal + 2)" case */
949         via_context->tau += via_context->tal + 2;
950     }
951     via_context->ifr |= VIA_IM_T1;
952     update_myviairq_rclk(via_context, rclk);
953 
954     /* TODO: toggle PB7? */
955     /*(viaier & VIA_IM_T1) ? 1:0; */
956 }
957 
958 /* WARNING: this is a hack, used to interface with c64fastiec.c, c128fastiec.c */
viacore_set_sr(via_context_t * via_context,uint8_t data)959 void viacore_set_sr(via_context_t *via_context, uint8_t data)
960 {
961     if (!(via_context->via[VIA_ACR] & 0x10) && (via_context->via[VIA_ACR] & 0x0c)) {
962         via_context->via[VIA_SR] = data;
963         via_context->ifr |= VIA_IM_SR;
964         update_myviairq(via_context);
965         via_context->shift_state = 15;
966     }
967 }
968 
do_shiftregister(CLOCK offset,void * data)969 static inline void do_shiftregister(CLOCK offset, void *data)
970 {
971     CLOCK rclk;
972     via_context_t *via_context = (via_context_t *)data;
973     rclk = *(via_context->clk_ptr) - offset;
974 
975     if (via_context->shift_state < 16) {
976         /* FIXME: CB1 should be toggled, and interrupt flag set according to edge detection in PCR */
977         if (via_context->shift_state & 1) {
978             if (via_context->via[VIA_ACR] & 0x10) {
979                 /* FIXME: shift out */
980                 via_context->via[VIA_SR] = ((via_context->via[VIA_SR] << 1 ) & 0xfe) | ((via_context->via[VIA_SR] >> 7) & 1);
981             } else {
982                 /* shift in */
983                 /* FIXME: we should read CB2 here instead of 1, but CB2 state must not be controlled by PCR
984                     until the signalling function is correct with shifter active, just use 1 instead */
985                 via_context->via[VIA_SR] = (via_context->via[VIA_SR] << 1 ) | 1;
986             }
987         }
988         via_context->shift_state += 1;
989         /* next shifter bit; set SR interrupt if 8 bits are complete */
990         if (via_context->shift_state == 16) {
991             via_context->ifr |= VIA_IM_SR;
992             update_myviairq_rclk(via_context, rclk);
993             via_context->shift_state = 0;
994         }
995     }
996 }
997 
998 /* T2 can be switched between 8 and 16 bit modes ad-hoc, any time, by setting
999    the shifter to be controlled by T2 via selecting the relevant ACR shift
1000    register operating mode.
1001    This change affects how the next T2 low underflow is handled */
viacore_intt2(CLOCK offset,void * data)1002 static void viacore_intt2(CLOCK offset, void *data)
1003 {
1004     CLOCK rclk;
1005     int next_alarm;
1006     via_context_t *via_context = (via_context_t *)data;
1007 
1008     rclk = *(via_context->clk_ptr) - offset;
1009 
1010 #ifdef MYVIA_TIMER_DEBUG
1011     if (app_resources.debugFlag) {
1012         log_message(via_context->log, "MYVIA timer B interrupt.");
1013     }
1014 #endif
1015     /* If the shifter is under T2 control, the T2 timer works differently, and have a period of T2 low.
1016        T2 high is still cascaded though and decreases at each T2 low underflow */
1017     if ((via_context->via[VIA_ACR] & 0x0c) == 0x04) {
1018         /* 8 bit timer mode; reload T2 low from latch */
1019         via_context->t2cl = via_context->via[VIA_T2LL];
1020 
1021         /* set next alarm to T2 low period */
1022         next_alarm = via_context->via[VIA_T2LL] + 2;
1023 
1024         /* T2 acts as a pulse generator for CB1
1025            every second underflow is a pulse updating the shift register,
1026            until all 8 bits are complete */
1027         do_shiftregister(offset, data);
1028     } else if ((via_context->via[VIA_ACR] & 0x1c) == 0x10) {
1029 
1030         /* set next alarm to T2 low period */
1031         next_alarm = via_context->via[VIA_T2LL] + 2;
1032 
1033         /* same as above, except bits will we clocked out CB2 repeatedly without
1034          * stopping after 8 bits */
1035         do_shiftregister(offset, data);
1036     } else {
1037         /* 16 bit timer mode; it is guaranteed that T2 low is in underflow */
1038         via_context->t2cl = 0xff;
1039 
1040         /* set next alarm to 256 cycles later, until t2 high underflow */
1041         next_alarm = (via_context->t2ch) ? 256 : 0;
1042     }
1043 
1044     /* T2 low count underflow always decreases T2 high count */
1045     via_context->t2ch--;
1046 
1047     /* set the next T2 low underflow alarm, or turn off the alarm */
1048     if (next_alarm) {
1049         via_context->tbu += next_alarm;
1050         via_context->tbi += next_alarm;
1051         alarm_set(via_context->t2_alarm, via_context->tbi);
1052     } else {
1053         alarm_unset(via_context->t2_alarm);
1054         via_context->tbi = 0;
1055     }
1056 
1057     /* 16 bit timer underflow generates an interrupt */
1058     /* FIXME: does 16 bit underflow generate an IRQ in 8 bit mode? 8 bit underflow does not */
1059     /* FIXME: no IRQ when shift register is in free running mode? */
1060     if (via_context->t2ch == 0xff) {
1061         via_context->ifr |= VIA_IM_T2;
1062         update_myviairq_rclk(via_context, rclk);
1063     }
1064 }
1065 
1066 /* alarm callback for the case when the shift register is under phi2 control */
viacore_intsr(CLOCK offset,void * data)1067 static void viacore_intsr(CLOCK offset, void *data)
1068 {
1069     CLOCK rclk;
1070     via_context_t *via_context = (via_context_t *)data;
1071     rclk = *(via_context->clk_ptr) - offset;
1072     do_shiftregister(offset, data);
1073     alarm_set(via_context->sr_alarm, rclk + 1);
1074 }
1075 
viacore_clk_overflow_callback(CLOCK sub,void * data)1076 static void viacore_clk_overflow_callback(CLOCK sub, void *data)
1077 {
1078     via_context_t *via_context;
1079 
1080     via_context = (via_context_t *)data;
1081 
1082     if (via_context->enabled == 0) {
1083         return;
1084     }
1085 
1086 #if 0
1087     via_context->tau = via_context->tal + 2 -
1088                        ((*(via_context->clk_ptr) + sub - via_context->tau)
1089                         % (via_context->tal + 2));
1090 
1091     via_context->tbu = via_context->tbl + 2 -
1092                        ((*(via_context->clk_ptr) + sub - via_context->tbu)
1093                         % (via_context->tbl + 2));
1094 #else
1095     via_context->tau -= sub;
1096     via_context->tbu -= sub;
1097 #endif
1098 
1099     if (via_context->tai) {
1100         via_context->tai -= sub;
1101     }
1102 
1103     if (via_context->tbi) {
1104         via_context->tbi -= sub;
1105     }
1106 
1107     if (via_context->read_clk > sub) {
1108         via_context->read_clk -= sub;
1109     } else {
1110         via_context->read_clk = 0;
1111     }
1112 }
1113 
viacore_setup_context(via_context_t * via_context)1114 void viacore_setup_context(via_context_t *via_context)
1115 {
1116     int i;
1117 
1118     via_context->read_clk = 0;
1119     via_context->read_offset = 0;
1120     via_context->last_read = 0;
1121     via_context->log = LOG_ERR;
1122 
1123     via_context->my_module_name_alt1 = NULL;
1124     via_context->my_module_name_alt2 = NULL;
1125 
1126     via_context->write_offset = 1;
1127     /* assume all registers 0 at powerup */
1128     for (i = 0; i < 16; i++) {
1129         via_context->via[i] = 0;
1130     }
1131     /* timers and timer latches apparently do not contain 0 at powerup */
1132     via_context->via[4] = via_context->via[6] = 0xff;
1133     via_context->via[5] = via_context->via[7] = 223;  /* my vic20 gives 223 here (gpz) */
1134     via_context->via[8] = 0xff;
1135     via_context->via[9] = 0xff;
1136 }
1137 
viacore_init(via_context_t * via_context,alarm_context_t * alarm_context,interrupt_cpu_status_t * int_status,clk_guard_t * clk_guard)1138 void viacore_init(via_context_t *via_context, alarm_context_t *alarm_context,
1139                   interrupt_cpu_status_t *int_status, clk_guard_t *clk_guard)
1140 {
1141     char *buffer;
1142 
1143     if (via_context->log == LOG_ERR) {
1144         via_context->log = log_open(via_context->my_module_name);
1145     }
1146 
1147     buffer = lib_msprintf("%sT1", via_context->myname);
1148     via_context->t1_alarm = alarm_new(alarm_context, buffer, viacore_intt1, via_context);
1149     lib_free(buffer);
1150 
1151     buffer = lib_msprintf("%sT2", via_context->myname);
1152     via_context->t2_alarm = alarm_new(alarm_context, buffer, viacore_intt2, via_context);
1153     lib_free(buffer);
1154 
1155     buffer = lib_msprintf("%sSR", via_context->myname);
1156     via_context->sr_alarm = alarm_new(alarm_context, buffer, viacore_intsr, via_context);
1157     lib_free(buffer);
1158 
1159     via_context->int_num = interrupt_cpu_status_int_new(int_status, via_context->myname);
1160     clk_guard_add_callback(clk_guard, viacore_clk_overflow_callback, via_context);
1161 }
1162 
viacore_shutdown(via_context_t * via_context)1163 void viacore_shutdown(via_context_t *via_context)
1164 {
1165     lib_free(via_context->prv);
1166     lib_free(via_context->myname);
1167     lib_free(via_context->my_module_name);
1168     lib_free(via_context->my_module_name_alt1);
1169     lib_free(via_context->my_module_name_alt2);
1170     lib_free(via_context);
1171 }
1172 
1173 /*------------------------------------------------------------------------*/
1174 
1175 /* The name of the modul must be defined before including this file.  */
1176 #define VIA_DUMP_VER_MAJOR      2
1177 #define VIA_DUMP_VER_MINOR      1
1178 
1179 /*
1180  * The dump data:
1181  *
1182  * UBYTE        ORA
1183  * UBYTE        DDRA
1184  * UBYTE        ORB
1185  * UBYTE        DDRB
1186  * UWORD        T1L
1187  * UWORD        T1C
1188  * UBYTE        T2LL
1189  * UBYTE        T2LH
1190  * UBYTE        T2CL
1191  * UBYTE        T2CH
1192  * UWORD        T2C
1193  * UBYTE        SR
1194  * UBYTE        ACR
1195  * UBYTE        PCR
1196  * UBYTE        IFR              active interrupts
1197  * UBYTE        IER              interrupt masks
1198  * UBYTE        PB7              bit 7 = pb7 state
1199  * UBYTE        SRHBITS          shift register state helper
1200  * UBYTE        CABSTATE         bit 7 = ca2 state, bi 6 = cb2 state
1201  * UBYTE        ILA              input latch port A
1202  * UBYTE        ILB              input latch port B
1203  */
1204 
1205 /* FIXME!!!  Error check.  */
1206 
viacore_snapshot_write_module(via_context_t * via_context,snapshot_t * s)1207 int viacore_snapshot_write_module(via_context_t *via_context, snapshot_t *s)
1208 {
1209     snapshot_module_t *m;
1210 
1211     if (via_context->tai && (via_context->tai <= *(via_context->clk_ptr))) {
1212         viacore_intt1(*(via_context->clk_ptr) - via_context->tai,
1213                       (void *)via_context);
1214     }
1215     if (via_context->tbi && (via_context->tbi <= *(via_context->clk_ptr))) {
1216         viacore_intt2(*(via_context->clk_ptr) - via_context->tbi,
1217                       (void *)via_context);
1218     }
1219 
1220     m = snapshot_module_create(s, via_context->my_module_name, VIA_DUMP_VER_MAJOR, VIA_DUMP_VER_MINOR);
1221 
1222     if (m == NULL) {
1223         return -1;
1224     }
1225 
1226     if (0
1227         || SMW_B(m, via_context->via[VIA_PRA]) < 0
1228         || SMW_B(m, via_context->via[VIA_DDRA]) < 0
1229         || SMW_B(m, via_context->via[VIA_PRB]) < 0
1230         || SMW_B(m, via_context->via[VIA_DDRB]) < 0
1231         || SMW_W(m, (uint16_t)(via_context->tal)) < 0
1232         || SMW_W(m, (uint16_t)myviata(via_context)) < 0
1233         || SMW_B(m, via_context->via[VIA_T2LL]) < 0
1234         || SMW_B(m, via_context->via[VIA_T2LH]) < 0
1235         || SMW_B(m, via_context->t2cl) < 0
1236         || SMW_B(m, via_context->t2ch) < 0
1237         || SMW_W(m, (uint16_t)myviatb(via_context)) < 0
1238         || SMW_B(m, (uint8_t)((via_context->tai ? 0x80 : 0) | (via_context->tbi ? 0x40 : 0))) < 0
1239         || SMW_B(m, via_context->via[VIA_SR]) < 0
1240         || SMW_B(m, via_context->via[VIA_ACR]) < 0
1241         || SMW_B(m, via_context->via[VIA_PCR]) < 0
1242         || SMW_B(m, (uint8_t)(via_context->ifr)) < 0
1243         || SMW_B(m, (uint8_t)(via_context->ier)) < 0
1244         /* FIXME! */
1245         || SMW_B(m, (uint8_t)((((via_context->pb7 ^ via_context->pb7x) | via_context->pb7o) ? 0x80 : 0))) < 0
1246         /* SRHBITS */
1247         || SMW_B(m, (uint8_t)via_context->shift_state) < 0
1248         || SMW_B(m, (uint8_t)((via_context->ca2_state ? 0x80 : 0) | (via_context->cb2_state ? 0x40 : 0))) < 0
1249         || SMW_B(m, via_context->ila) < 0
1250         || SMW_B(m, via_context->ilb) < 0) {
1251         snapshot_module_close(m);
1252         return -1;
1253     }
1254 
1255     return snapshot_module_close(m);
1256 }
1257 
viacore_snapshot_read_module(via_context_t * via_context,snapshot_t * s)1258 int viacore_snapshot_read_module(via_context_t *via_context, snapshot_t *s)
1259 {
1260     uint8_t vmajor, vminor;
1261     uint8_t byte;
1262     uint8_t byte1, byte2, byte3, byte4, byte5, byte6;
1263     uint16_t word1, word2, word3;
1264     uint16_t addr;
1265     CLOCK rclk = *(via_context->clk_ptr);
1266     snapshot_module_t *m;
1267 
1268     m = snapshot_module_open(s, via_context->my_module_name, &vmajor, &vminor);
1269 
1270     if (m == NULL) {
1271         if (via_context->my_module_name_alt1 == NULL) {
1272             return -1;
1273         }
1274 
1275         m = snapshot_module_open(s, via_context->my_module_name_alt1,
1276                                  &vmajor, &vminor);
1277         if (m == NULL) {
1278             if (via_context->my_module_name_alt2 == NULL) {
1279                 return -1;
1280             }
1281 
1282             m = snapshot_module_open(s, via_context->my_module_name_alt2,
1283                                      &vmajor, &vminor);
1284             if (m == NULL) {
1285                 return -1;
1286             }
1287         }
1288     }
1289 
1290     /* if major version does not match, the snapshot is not compatible */
1291     if (vmajor != VIA_DUMP_VER_MAJOR) {
1292         snapshot_set_error(SNAPSHOT_MODULE_INCOMPATIBLE);
1293         snapshot_module_close(m);
1294         return -1;
1295     }
1296     /* Do not accept versions higher than current */
1297     if (vminor > VIA_DUMP_VER_MINOR) {
1298         snapshot_set_error(SNAPSHOT_MODULE_HIGHER_VERSION);
1299         snapshot_module_close(m);
1300         return -1;
1301     }
1302 
1303     alarm_unset(via_context->t1_alarm);
1304     alarm_unset(via_context->t2_alarm);
1305     alarm_unset(via_context->sr_alarm);
1306 
1307     via_context->tai = 0;
1308     via_context->tbi = 0;
1309 
1310     if (0
1311         || SMR_B(m, &(via_context->via[VIA_PRA])) < 0
1312         || SMR_B(m, &(via_context->via[VIA_DDRA])) < 0
1313         || SMR_B(m, &(via_context->via[VIA_PRB])) < 0
1314         || SMR_B(m, &(via_context->via[VIA_DDRB])) < 0
1315         || SMR_W(m, &word1) < 0
1316         || SMR_W(m, &word2) < 0
1317         || SMR_B(m, &(via_context->via[VIA_T2LL])) < 0
1318         || SMR_B(m, &(via_context->via[VIA_T2LH])) < 0
1319         || SMR_B(m, &(via_context->t2cl)) < 0
1320         || SMR_B(m, &(via_context->t2ch)) < 0
1321         || SMR_W(m, &word3) < 0
1322         || SMR_B(m, &byte1) < 0
1323         || SMR_B(m, &(via_context->via[VIA_SR])) < 0
1324         || SMR_B(m, &(via_context->via[VIA_ACR])) < 0
1325         || SMR_B(m, &(via_context->via[VIA_PCR])) < 0
1326         || SMR_B(m, &byte2) < 0
1327         || SMR_B(m, &byte3) < 0
1328         || SMR_B(m, &byte4) < 0
1329         /* SRHBITS */
1330         || SMR_B(m, &byte5) < 0
1331         /* CABSTATE */
1332         || SMR_B(m, &byte6) < 0
1333         || SMR_B(m, &(via_context->ila)) < 0
1334         || SMR_B(m, &(via_context->ilb)) < 0) {
1335         snapshot_module_close(m);
1336         return -1;
1337     }
1338 
1339     addr = VIA_DDRA;
1340     byte = via_context->via[VIA_PRA] | ~(via_context->via[VIA_DDRA]);
1341     (via_context->undump_pra)(via_context, byte);
1342     via_context->oldpa = byte;
1343 
1344     addr = VIA_DDRB;
1345     byte = via_context->via[VIA_PRB] | ~(via_context->via[VIA_DDRB]);
1346     (via_context->undump_prb)(via_context, byte);
1347     via_context->oldpb = byte;
1348 
1349     via_context->tal = word1;
1350     via_context->via[VIA_T1LL] = via_context->tal & 0xff;
1351     via_context->via[VIA_T1LH] = (via_context->tal >> 8) & 0xff;
1352 
1353     via_context->tau = rclk + word2 + 2 /* 3 */ + TAUOFFSET;
1354     via_context->tai = rclk + word2 + 1;
1355 
1356     via_context->tbu = rclk + word3 + 2 /* 3 */;
1357     via_context->tbi = rclk + word3 + 0;
1358 
1359     if (byte1 & 0x80) {
1360         alarm_set(via_context->t1_alarm, via_context->tai);
1361     } else {
1362         via_context->tai = 0;
1363     }
1364     if ((byte1 & 0x40) ||
1365         ((via_context->via[VIA_ACR] & 0x1c) == 0x04) ||
1366         ((via_context->via[VIA_ACR] & 0x1c) == 0x10) ||
1367         ((via_context->via[VIA_ACR] & 0x1c) == 0x14)){
1368         alarm_set(via_context->t2_alarm, via_context->tbi);
1369     } else {
1370         via_context->tbi = 0;
1371     }
1372     /* FIXME: SR alarm */
1373     if ((via_context->via[VIA_ACR] & 0x0c) == 0x08) {
1374         alarm_set(via_context->sr_alarm, rclk + 1);
1375     }
1376 
1377     via_context->ifr = byte2;
1378     via_context->ier = byte3;
1379 
1380     via_restore_int(via_context, via_context->ifr & via_context->ier & 0x7f);
1381 
1382     /* FIXME! */
1383     via_context->pb7 = byte4 ? 1 : 0;
1384     via_context->pb7x = 0;
1385     via_context->pb7o = 0;
1386     via_context->shift_state = byte5;
1387 
1388     via_context->ca2_state = byte6 & 0x80;
1389     via_context->cb2_state = byte6 & 0x40;
1390 
1391     /* undump_pcr also restores the ca2_state/cb2_state effects if necessary;
1392        i.e. calls set_c*2(c*2_state) if necessary */
1393     addr = VIA_PCR;
1394     byte = via_context->via[addr];
1395     (via_context->undump_pcr)(via_context, byte);
1396 
1397     addr = VIA_SR;
1398     byte = via_context->via[addr];
1399     (via_context->store_sr)(via_context, byte);
1400 
1401     addr = VIA_ACR;
1402     byte = via_context->via[addr];
1403     (via_context->undump_acr)(via_context, byte);
1404 
1405     return snapshot_module_close(m);
1406 }
1407 
viacore_dump(via_context_t * via_context)1408 int viacore_dump(via_context_t *via_context)
1409 {
1410     mon_out("Port A: %02x DDR: %02x no HS: %02x\n",
1411             viacore_peek(via_context, 0x01), viacore_peek(via_context, 0x03), viacore_peek(via_context, 0x0f));
1412     mon_out("Port B: %02x DDR: %02x\n", viacore_peek(via_context, 0x00), viacore_peek(via_context, 0x02));
1413     mon_out("Timer 1: %04x Latch: %04x\n", viacore_peek(via_context, 0x04) + (viacore_peek(via_context, 0x05) * 256),
1414             viacore_peek(via_context, 0x06) + (viacore_peek(via_context, 0x07) * 256));
1415     mon_out("Timer 2: %04x\n", viacore_peek(via_context, 0x08) + (viacore_peek(via_context, 0x09) * 256));
1416     mon_out("Aux. control: %02x\n", viacore_peek(via_context, 0x0b));
1417     mon_out("Per. control: %02x\n", viacore_peek(via_context, 0x0c));
1418     mon_out("IRQ flags: %02x\n", viacore_peek(via_context, 0x0d));
1419     mon_out("IRQ enable: %02x\n", viacore_peek(via_context, 0x0e));
1420     mon_out("\nSynchronous Serial I/O Data Buffer: %02x (%s, shifting %s)\n",
1421             viacore_peek(via_context, 0x0a),
1422             ((via_context->via[VIA_ACR] & 0x1c) == 0) ? "disabled" : "enabled",
1423             (via_context->via[VIA_ACR] & 0x10) ? "out" : "in");
1424     return 0;
1425 }
1426