/*
 * 6522 module
 *
 * 12/02/94 jkb  creation
 *
 * TODO: add T2 code
 */

#if ((defined(USER_VIA) && defined(VIAU)) \
  || (defined(SYSTEM_VIA) && defined(VIAS)))

#include <stdio.h>
#include <unistd.h>
#include <errno.h>

#include "6522.h"
#include "module_inc.h"
#include "memory.h"
#include "6502P.h"
#include "timer.h"

static byte read_via(word addr);
static byte write_via(word addr, byte val);

t_6522 i_6522 = {
    init, update,
    read_via, write_via
};

extern int do_irq;
static byte ora, orb, ddra, ddrb, sr, acr, pcr, ifr, ier;
static int t1_timer = 0;

/* ARGSUSED */
static void sig_alarm(int x) {
    if (get_I())
	return;

    /* set IFR T1 time-out */
    ifr |= IFR_SET + IFR_T1;

    /* generate interrupt */
    do_irq = 1;
}

static byte read_via(word addr) {
    addr &= 0x0f;

    switch (addr) {
    case 0: /* output reg B */
    case 15:
	ifr &= ~IFR_CB1;
	if (pcr & 0xa0 != 0x20)
	    ifr &= ~IFR_CB2;
	return orb;
    case 1: /* output reg A */
	ifr &= ~IFR_CA1;
	if (pcr & 0x0a != 0x02)
	    ifr &= ~IFR_CA2;
	return ora;
    case 2: /* DDRB */
	return ddrb;
    case 3: /* DDRA */
	return ddra;
    case 4: /* T1 low order counter */
	return query_timer_expire(t1_timer) & 0x00ff;
    case 5: /* T1 high order counter */
	return query_timer_expire(t1_timer) & 0x00ff;
    case 6: /* T1 low order latch */
	ifr &= ~IFR_T1;
	return query_timer_interval(t1_timer) & 0x00ff;
    case 7: /* T1 high order latch */
	return 0xff; /* unreadable? */
    case 8: /* T2 low order latch */
	ifr &= ~IFR_T2;
	return 0; /* fixme */
    case 9: /* T2 high order latch */
	return 0; /* fixme */
    case 10: /* Shift reg */
	ifr &= ~IFR_SR;
	return sr;
    case 11: /* Auxiliary control reg */
	return acr;
    case 12: /* Peripheral control reg */
	return pcr;
    case 13: /* interrupt flag reg */
	return ifr;
    case 14: /* interrupt enable reg */
	return ier;
    }
}

static byte write_via(word addr, byte val) {
    addr &= 0x0f;

    switch (addr) {
    case 0: /* output reg B */
    case 15:
	ifr &= ~IFR_CB1;
	if (pcr & 0xa0 != 0x20)
	    ifr &= ~IFR_CB2;
	orb = val;
	break;
    case 1: /* output reg A */
	ifr &= ~IFR_CA1;
	if (pcr & 0x0a != 0x02)
	    ifr &= ~IFR_CA2;
	ora = val;
	break;
    case 2: /* DDRB */
	ddrb = val;
	break;
    case 3: /* DDRA */
	ddra = val;
	break;
    case 5: /* T1 high order counter */
	/* copy low order latch and clear T1 interrupt */
	change_timer(t1_timer, TIMER_VALUE, 0, 
		     0x100 * val + query_timer_interval(t1_timer), NULL);
	ifr &= ~IFR_T1;
	break;
    case 4: /* T1 low order latch */
    case 6:	
	change_timer(t1_timer, TIMER_INTERVAL,
		     (query_timer_interval(t1_timer) & 0xff00) + val,
		     0, NULL);
	break;
    case 7: /* T1 high order latch */
	change_timer(t1_timer, TIMER_INTERVAL,
		     (query_timer_interval(t1_timer) & 0x00ff) + 0x100 * val,
		     0, NULL);
	break;
    case 8: /* T2 low order latch */
	/* fixme */
	break;
    case 9: /* T2 high order latch */
	ifr &= ~IFR_T2;
	/* fixme */
	break;
    case 10: /* Shift reg */
	ifr &= ~IFR_SR;
	sr = val;
	break;
    case 11: /* Auxiliary control reg */
	acr = val;
	break;
    case 12: /* Peripheral control reg */
	pcr = val;
	break;
    case 13: /* interrupt flag reg */
	ifr = val;
	break;
    case 14: /* interrupt enable reg */
	ier = val;
	break;
    }

    return 0;
}

static void init() {
    t1_timer = add_timer(1000000/HZ_6502, 1000000/HZ_6502, sig_alarm);
}

static time_t update(time_t inc) {
    static time_t time = 0;

/*    printf("Updating 6522 by %d ticks\n", inc); */
    return time += inc;
}

#else

static int kludge; /* Our favourite DECAlpha OSF/1 hack */

#endif
