 /*
  * UAE - The Un*x Amiga Emulator
  *
  * CIA chip support
  *
  * Copyright 1995 Bernd Schmidt, Alessandro Bissacco
  * Copyright 1996, 1997 Stefan Reinauer, Christian Schmitt
  */


#include "sysconfig.h"
#include "sysdeps.h"
#include <assert.h>

#include "options.h"
#include "events.h"
#include "memory.h"
#include "custom.h"
#include "cia.h"
#include "serial.h"
#include "disk.h"
#include "xwin.h"
#include "keybuf.h"
#include "gui.h"
#include "savestate.h"
#include "inputdevice.h"
#include "zfile.h"
#include "ar.h"
#include "parallel.h"
#include "akiko.h"
#include "debug.h"
#include "arcadia.h"
#include "audio.h"

//#define CIA_DEBUG_R
//#define CIA_DEBUG_W
//#define DONGLE_DEBUG

#define TOD_HACK

#define DIV10 (10 * CYCLE_UNIT / 2) /* Yes, a bad identifier. */

/* battclock stuff */
#define RTC_D_ADJ      8
#define RTC_D_IRQ      4
#define RTC_D_BUSY     2
#define RTC_D_HOLD     1
#define RTC_E_t1       8
#define RTC_E_t0       4
#define RTC_E_INTR     2
#define RTC_E_MASK     1
#define RTC_F_TEST     8
#define RTC_F_24_12    4
#define RTC_F_STOP     2
#define RTC_F_RSET     1

static unsigned int clock_control_d = RTC_D_ADJ + RTC_D_HOLD;
static unsigned int clock_control_e = 0;
static unsigned int clock_control_f = RTC_F_24_12;

static unsigned int ciaaicr, ciaaimask, ciabicr, ciabimask;
static unsigned int ciaacra, ciaacrb, ciabcra, ciabcrb;

/* Values of the CIA timers.  */
static unsigned long ciaata, ciaatb, ciabta, ciabtb;
/* Computed by compute_passed_time.  */
static unsigned long ciaata_passed, ciaatb_passed, ciabta_passed, ciabtb_passed;

static unsigned long ciaatod, ciabtod, ciaatol, ciabtol, ciaaalarm, ciabalarm;
static int ciaatlatch, ciabtlatch;
static int oldled, oldovl;

unsigned int ciabpra;

static unsigned long ciaala, ciaalb, ciabla, ciablb;
static int ciaatodon, ciabtodon;
static unsigned int ciaapra, ciaaprb, ciaadra, ciaadrb, ciaasdr, ciaasdr_cnt;
static unsigned int ciabprb, ciabdra, ciabdrb, ciabsdr, ciabsdr_cnt;
static int div10;
static int kbstate, kback, ciaasdr_unread;
static unsigned int sleepyhead = 0;

#ifdef TOD_HACK
static int tod_hack, tod_hack_delay;
#endif

static uae_u8 serbits;
static int warned = 10;

static void setclr (unsigned int *p, unsigned int val)
{
    if (val & 0x80) {
	*p |= val & 0x7F;
    } else {
	*p &= ~val;
    }
}

#include "newcpu.h"

static void RethinkICRA (void)
{
    if (ciaaimask & ciaaicr) {
	ciaaicr |= 0x80;
	INTREQ_0 (0x8000 | 0x0008);
    } else {
	ciaaicr &= 0x7F;
    }
}

static void RethinkICRB (void)
{
    if (ciabimask & ciabicr) {
	ciabicr |= 0x80;
	INTREQ_0 (0x8000 | 0x2000);
    } else {
	ciabicr &= 0x7F;
    }
}

void rethink_cias (void)
{
    RethinkICRA ();
    RethinkICRB ();
}

/* Figure out how many CIA timer cycles have passed for each timer since the
   last call of CIA_calctimers.  */

static void compute_passed_time (void)
{
    unsigned long int ccount = (get_cycles () - eventtab[ev_cia].oldcycles + div10);
    unsigned long int ciaclocks = ccount / DIV10;

    ciaata_passed = ciaatb_passed = ciabta_passed = ciabtb_passed = 0;

    /* CIA A timers */
    if ((ciaacra & 0x21) == 0x01) {
	assert ((ciaata+1) >= ciaclocks);
	ciaata_passed = ciaclocks;
    }
    if ((ciaacrb & 0x61) == 0x01) {
	assert ((ciaatb+1) >= ciaclocks);
	ciaatb_passed = ciaclocks;
    }

    /* CIA B timers */
    if ((ciabcra & 0x21) == 0x01) {
	assert ((ciabta+1) >= ciaclocks);
	ciabta_passed = ciaclocks;
    }
    if ((ciabcrb & 0x61) == 0x01) {
	assert ((ciabtb+1) >= ciaclocks);
	ciabtb_passed = ciaclocks;
    }
}

/* Called to advance all CIA timers to the current time.  This expects that
   one of the timer values will be modified, and CIA_calctimers will be called
   in the same cycle.  */

static void CIA_update (void)
{
    unsigned long int ccount = (get_cycles () - eventtab[ev_cia].oldcycles + div10);
    unsigned long int ciaclocks = ccount / DIV10;

    int aovfla = 0, aovflb = 0, asp = 0, bovfla = 0, bovflb = 0, bsp = 0;

    div10 = ccount % DIV10;

    /* CIA A timers */
    if ((ciaacra & 0x21) == 0x01) {
	assert ((ciaata+1) >= ciaclocks);
	if ((ciaata+1) == ciaclocks) {
	    if ((ciaacra & 0x48) == 0x40 && ciaasdr_cnt > 0 && --ciaasdr_cnt == 0)
		asp = 1;
	    aovfla = 1;
	    if ((ciaacrb & 0x61) == 0x41 || (ciaacrb & 0x61) == 0x61) {
		if (ciaatb-- == 0)
		    aovflb = 1;
	    }
	}
	ciaata -= ciaclocks;
    }
    if ((ciaacrb & 0x61) == 0x01) {
	assert ((ciaatb+1) >= ciaclocks);
	if ((ciaatb+1) == ciaclocks) aovflb = 1;
	ciaatb -= ciaclocks;
    }

    /* CIA B timers */
    if ((ciabcra & 0x21) == 0x01) {
	assert ((ciabta+1) >= ciaclocks);
	if ((ciabta+1) == ciaclocks) {
	    if ((ciabcra & 0x48) == 0x40 && ciabsdr_cnt > 0 && --ciabsdr_cnt == 0)
		bsp = 1;
	    bovfla = 1;
	    if ((ciabcrb & 0x61) == 0x41 || (ciabcrb & 0x61) == 0x61) {
		if (ciabtb-- == 0)
		    bovflb = 1;
	    }
	}
	ciabta -= ciaclocks;
    }
    if ((ciabcrb & 0x61) == 0x01) {
	assert ((ciabtb+1) >= ciaclocks);
	if ((ciabtb+1) == ciaclocks) bovflb = 1;
	ciabtb -= ciaclocks;
    }

    if (aovfla) {
	ciaaicr |= 1; RethinkICRA();
	ciaata = ciaala;
	if (ciaacra & 0x8) ciaacra &= ~1;
    }
    if (aovflb) {
	ciaaicr |= 2; RethinkICRA();
	ciaatb = ciaalb;
	if (ciaacrb & 0x8) ciaacrb &= ~1;
    }
    if (asp) {
	ciaaicr |= 8; RethinkICRA();
    }
    if (bovfla) {
	ciabicr |= 1; RethinkICRB();
	ciabta = ciabla;
	if (ciabcra & 0x8) ciabcra &= ~1;
    }
    if (bovflb) {
	ciabicr |= 2; RethinkICRB();
	ciabtb = ciablb;
	if (ciabcrb & 0x8) ciabcrb &= ~1;
    }
    if (bsp) {
	ciabicr |= 8; RethinkICRB();
    }
}

/* Call this only after CIA_update has been called in the same cycle.  */

static void CIA_calctimers (void)
{
    long int ciaatimea = -1, ciaatimeb = -1, ciabtimea = -1, ciabtimeb = -1;

    eventtab[ev_cia].oldcycles = get_cycles ();
    if ((ciaacra & 0x21) == 0x01) {
	ciaatimea = (DIV10 - div10) + DIV10 * ciaata;
    }
    if ((ciaacrb & 0x61) == 0x41) {
	/* Timer B will not get any pulses if Timer A is off. */
	if (ciaatimea >= 0) {
	    /* If Timer A is in one-shot mode, and Timer B needs more than
	     * one pulse, it will not underflow. */
	    if (ciaatb == 0 || (ciaacra & 0x8) == 0) {
		/* Otherwise, we can determine the time of the underflow. */
		/* This may overflow, however.  So just ignore this timer and
		   use the fact that we'll call CIA_handler for the A timer.  */
#if 0
		ciaatimeb = ciaatimea + ciaala * DIV10 * ciaatb;
#endif
	    }
	}
    }
    if ((ciaacrb & 0x61) == 0x01) {
	ciaatimeb = (DIV10 - div10) + DIV10 * ciaatb;
    }

    if ((ciabcra & 0x21) == 0x01) {
	ciabtimea = (DIV10 - div10) + DIV10 * ciabta;
    }
    if ((ciabcrb & 0x61) == 0x41) {
	/* Timer B will not get any pulses if Timer A is off. */
	if (ciabtimea >= 0) {
	    /* If Timer A is in one-shot mode, and Timer B needs more than
	     * one pulse, it will not underflow. */
	    if (ciabtb == 0 || (ciabcra & 0x8) == 0) {
		/* Otherwise, we can determine the time of the underflow. */
#if 0
		ciabtimeb = ciabtimea + ciabla * DIV10 * ciabtb;
#endif
	    }
	}
    }
    if ((ciabcrb & 0x61) == 0x01) {
	ciabtimeb = (DIV10 - div10) + DIV10 * ciabtb;
    }
    eventtab[ev_cia].active = (ciaatimea != -1 || ciaatimeb != -1
	|| ciabtimea != -1 || ciabtimeb != -1);
    if (eventtab[ev_cia].active) {
	unsigned long int ciatime = ~0L;
	if (ciaatimea != -1) ciatime = ciaatimea;
	if (ciaatimeb != -1 && ciaatimeb < ciatime) ciatime = ciaatimeb;
	if (ciabtimea != -1 && ciabtimea < ciatime) ciatime = ciabtimea;
	if (ciabtimeb != -1 && ciabtimeb < ciatime) ciatime = ciabtimeb;
	eventtab[ev_cia].evtime = ciatime + get_cycles ();
    }
    events_schedule();
}

void CIA_handler (void)
{
    CIA_update ();
    CIA_calctimers ();
}

void cia_diskindex (void)
{
    ciabicr |= 0x10;
    RethinkICRB();
}
void cia_parallelack (void)
{
    ciaaicr |= 0x10;
    RethinkICRA();
}

static int checkalarm (unsigned long tod, unsigned long alarm, int inc)
{
    if (tod == alarm)
	return 1;
    if (!inc)
	return 0;
    /* emulate buggy TODMED counter.
     * it counts: .. 29 2A 2B 2C 2D 2E 2F 20 30 31 32 ..
     * (2F->20->30 only takes couple of cycles but it will trigger alarm..
     */
    if (tod & 0x000fff)
	return 0;
    if (((tod - 1) & 0xfff000) == alarm)
	return 1;
    return 0;
}

STATIC_INLINE void ciab_checkalarm (int inc)
{
    if (checkalarm (ciabtod, ciabalarm, inc)) {
	ciabicr |= 4;
	RethinkICRB();
    }
}

STATIC_INLINE void ciaa_checkalarm (int inc)
{
    if (checkalarm (ciaatod, ciaaalarm, inc)) {
        ciaaicr |= 4;
	RethinkICRA();
    }
}

void CIA_hsync_handler (void)
{
    if (ciabtodon) {
	ciabtod++;
	ciabtod &= 0xFFFFFF;
	ciab_checkalarm (1);
    }

    if (keys_available() && kback && (ciaacra & 0x40) == 0 && (hsync_counter & 15) == 0) {
	/*
	 * This hack lets one possible ciaaicr cycle go by without any key
	 * being read, for every cycle in which a key is pulled out of the
	 * queue.  If no hack is used, a lot of key events just get lost
	 * when you type fast.  With a simple hack that waits for ciaasdr
	 * to be read before feeding it another, it will keep up until the
	 * queue gets about 14 characters ahead and then lose events, and
	 * the mouse pointer will freeze while typing is being taken in.
	 * With this hack, you can type 30 or 40 characters ahead with little
	 * or no lossage, and the mouse doesn't get stuck.  The tradeoff is
	 * that the total slowness of typing appearing on screen is worse.
	 */
	if (ciaasdr_unread == 2) {
	    ciaasdr_unread = 0;
	} else if (ciaasdr_unread == 0) {
	    switch (kbstate) {
	     case 0:
		ciaasdr = (uae_s8)~0xFB; /* aaarghh... stupid compiler */
		kbstate++;
		break;
	     case 1:
		kbstate++;
		ciaasdr = (uae_s8)~0xFD;
		break;
	     case 2:
		ciaasdr = ~get_next_key();
		ciaasdr_unread = 1;      /* interlock to prevent lost keystrokes */
		break;
	    }
	    ciaaicr |= 8;
	    RethinkICRA();
	    sleepyhead = 0;
	} else if (!(++sleepyhead & 15)) {
	    ciaasdr_unread = 0;          /* give up on this key event after unread for a long time */
	}
    }
}

#ifdef TOD_HACK
static void tod_hack_reset (void)
{
    struct timeval tv;
    uae_u32 rate = currprefs.ntscmode ? 60 : 50;
    gettimeofday (&tv, NULL);
    tod_hack = (uae_u32)(((uae_u64)tv.tv_sec) * rate  + tv.tv_usec / (1000000 / rate));
    tod_hack -= ciaatod;
    tod_hack_delay = 10 * 50;
}
#endif

void CIA_vsync_handler ()
{
#ifdef TOD_HACK
    if (currprefs.tod_hack && ciaatodon) {
	struct timeval tv;
	uae_u32 t, nt, rate = currprefs.ntscmode ? 60 : 50;

	if (tod_hack_delay > 0) {
	    tod_hack_delay--;
	    if (tod_hack_delay == 0) {
		tod_hack_reset ();
		tod_hack_delay = 0;
		write_log ("TOD_HACK re-initialized CIATOD=%06.6X\n", ciaatod);
	    }
	}
	if (tod_hack_delay == 0) {
	    gettimeofday (&tv, NULL);
	    t = (uae_u32)(((uae_u64)tv.tv_sec) * rate + tv.tv_usec / (1000000 / rate));
	    nt = t - tod_hack;
	    if ((nt < ciaatod && ciaatod - nt < 10) || nt == ciaatod)
		return; /* try not to count backwards */
	    ciaatod = nt;
	    ciaatod &= 0xffffff;
	    ciaa_checkalarm (0);
	    return;
	}
    }
#endif
    if (ciaatodon) {
	ciaatod++;
	ciaatod &= 0xFFFFFF;
	ciaa_checkalarm (1);
    }
}

static void bfe001_change (void)
{
    uae_u8 v = ciaapra;

    v |= ~ciaadra; /* output is high when pin's direction is input */
    if ((v & 2) != oldled) {
	int led = (v & 2) ? 0 : 1;
	oldled = v & 2;
	gui_data.powerled = led;
	led_filter_audio();
    }
    if ((v & 1) != oldovl) {
	oldovl = v & 1;
	if (!oldovl || ersatzkickfile) {
	    map_overlay (1);
	} else if (!(currprefs.chipset_mask & CSMASK_AGA)) {
	    /* pin disconnected in AGA chipset, CD audio mute on/off on CD32 */
	    map_overlay (0);
	}
    }
}

static uae_u8 ReadCIAA (unsigned int addr)
{
    unsigned int tmp;

    compute_passed_time ();

#ifdef CIA_DEBUG_R
    write_log("R_CIAA: bfe%x01 %08.8X\n", addr, M68K_GETPC);
#endif

    switch (addr & 0xf) {
    case 0:
#ifdef ACTION_REPLAY
	action_replay_ciaread();
#endif
	tmp = DISK_status() & 0x3c;
	tmp |= handle_joystick_buttons (ciaadra);
	tmp |= (ciaapra | (ciaadra ^ 3)) & 0x03;
	if (ciaadra & 0x40)
	    tmp = (tmp & ~0x40) | (ciaapra & 0x40);
	if (ciaadra & 0x80)
	    tmp = (tmp & ~0x80) | (ciaapra & 0x80);
#ifdef DONGLE_DEBUG
	if (notinrom())
	    write_log ("BFE001 R %02.2X %s\n", tmp, debuginfo(0));
#endif
	return tmp;
    case 1:
#ifdef PARALLEL_PORT
	if (isprinter () > 0) {
	    tmp = ciaaprb;
	} else if (isprinter () < 0) {
	    uae_u8 v;
	    parallel_direct_read_data (&v);
	    tmp = v;
#ifdef ARCADIA
	} else if (arcadia_rom) {
	    tmp = arcadia_parport (0, ciaaprb, ciaadrb);
#endif
	} else {
	    tmp = handle_parport_joystick (0, ciaaprb, ciaadrb);
	}
#else
	tmp = handle_parport_joystick (0, ciaaprb, ciaadrb);
#ifdef DONGLE_DEBUG
	if (notinrom())
	    write_log ("BFE101 R %02.2X %s\n", tmp, debuginfo(0));
#endif
#endif
	if (ciaacrb & 2) {
	    int pb7 = 0;
	    if (ciaacrb & 4)
		pb7 = ciaacrb & 1;
	    tmp &= ~0x80;
	    tmp |= pb7 ? 0x80 : 00;
	}
	if (ciaacra & 2) {
	    int pb6 = 0;
	    if (ciaacra & 4)
		pb6 = ciaacra & 1;
	    tmp &= ~0x40;
	    tmp |= pb6 ? 0x40 : 00;
	}
	return tmp;
    case 2:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFE201 R %02.2X %s\n", ciaadra, debuginfo(0));
#endif
	return ciaadra;
    case 3:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFE301 R %02.2X %s\n", ciaadrb, debuginfo(0));
#endif
	return ciaadrb;
    case 4:
	return (uae_u8)((ciaata - ciaata_passed) & 0xff);
    case 5:
	return (uae_u8)((ciaata - ciaata_passed) >> 8);
    case 6:
	return (uae_u8)((ciaatb - ciaatb_passed) & 0xff);
    case 7:
	return (uae_u8)((ciaatb - ciaatb_passed) >> 8);
    case 8:
	if (ciaatlatch) {
	    ciaatlatch = 0;
	    return (uae_u8)ciaatol;
	} else
	    return (uae_u8)ciaatod;
    case 9:
	if (ciaatlatch)
	    return (uae_u8)(ciaatol >> 8);
	else
	    return (uae_u8)(ciaatod >> 8);
    case 10:
	if (!ciaatlatch) { /* only if not already latched. A1200 confirmed. (TW) */
	    ciaatlatch = 1;
	    ciaatol = ciaatod;
	}
	return (uae_u8)(ciaatol >> 16);
    case 12:
	if (ciaasdr_unread == 1)
	    ciaasdr_unread = 2;
	return ciaasdr;
    case 13:
	tmp = ciaaicr; ciaaicr = 0; RethinkICRA();
	return tmp;
    case 14:
	return ciaacra;
    case 15:
	return ciaacrb;
    }
    return 0;
}

static uae_u8 ReadCIAB (unsigned int addr)
{
    unsigned int tmp;

#ifdef CIA_DEBUG_R
    write_log("R_CIAB: bfd%x00 %08.8X\n", addr, M68K_GETPC);
#endif

    compute_passed_time ();

    switch (addr & 0xf) {
    case 0:
	tmp = 0;
#ifdef SERIAL_PORT
	if (currprefs.use_serial)
	    tmp = serial_readstatus(ciabdra);
#endif
#ifdef PARALLEL_PORT
	if (isprinter () > 0) {
	    tmp |= ciabpra & (0x04 | 0x02 | 0x01);
	} else if (isprinter () < 0) {
	    uae_u8 v;
	    parallel_direct_read_status (&v);
	    tmp |= v & 7;
	} else {
	    tmp |= handle_parport_joystick (1, ciabpra, ciabdra);
	}
#endif
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFD000 R %02.2X %s\n", tmp, debuginfo(0));
#endif
	return tmp;
    case 1:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFD100 R %02.2X %s\n", ciabprb, debuginfo(0));
#endif
	tmp = ciabprb;
	if (ciabcrb & 2) {
	    int pb7 = 0;
	    if (ciabcrb & 4)
		pb7 = ciabcrb & 1;
	    tmp &= ~0x80;
	    tmp |= pb7 ? 0x80 : 00;
	}
	if (ciabcra & 2) {
	    int pb6 = 0;
	    if (ciabcra & 4)
		pb6 = ciabcra & 1;
	    tmp &= ~0x40;
	    tmp |= pb6 ? 0x40 : 00;
	}
	return tmp;
    case 2:
	return ciabdra;
    case 3:
	return ciabdrb;
    case 4:
	return (uae_u8)((ciabta - ciabta_passed) & 0xff);
    case 5:
	return (uae_u8)((ciabta - ciabta_passed) >> 8);
    case 6:
	return (uae_u8)((ciabtb - ciabtb_passed) & 0xff);
    case 7:
	return (uae_u8)((ciabtb - ciabtb_passed) >> 8);
    case 8:
	if (ciabtlatch) {
	    ciabtlatch = 0;
	    return (uae_u8)ciabtol;
	} else
	    return (uae_u8)ciabtod;
    case 9:
	if (ciabtlatch)
	    return (uae_u8)(ciabtol >> 8);
	else
	    return (uae_u8)(ciabtod >> 8);
    case 10:
	if (!ciabtlatch) {
	    ciabtlatch = 1;
	    ciabtol = ciabtod;
	}
	return (uae_u8)(ciabtol >> 16);
    case 12:
	return ciabsdr;
    case 13:
	tmp = ciabicr; ciabicr = 0; RethinkICRB();
	return tmp;
    case 14:
	return ciabcra;
    case 15:
	return ciabcrb;
    }
    return 0;
}

static void WriteCIAA (uae_u16 addr,uae_u8 val)
{
#ifdef CIA_DEBUG_W
    write_log("W_CIAA: bfe%x01 %02.2X %08.8X\n", addr, val, M68K_GETPC);
#endif
#ifdef ACTION_REPLAY
    ar_ciaa[addr & 0xf] = val;
#endif
    switch (addr & 0xf) {
    case 0:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFE001 W %02.2X %s\n", val, debuginfo(0));
#endif
	ciaapra = (ciaapra & ~0xc3) | (val & 0xc3);
	bfe001_change ();
	handle_cd32_joystick_cia (ciaapra, ciaadra);
	break;
    case 1:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFE101 W %02.2X %s\n", val, debuginfo(0));
#endif
	ciaaprb = val;
#ifdef PARALLEL_PORT
	if (isprinter() > 0) {
	    doprinter (val);
	    cia_parallelack ();
	} else if (isprinter() < 0) {
	    parallel_direct_write_data (val, ciaadrb);
	    cia_parallelack ();
#ifdef ARCADIA
	} else if (arcadia_rom) {
	    arcadia_parport (1, ciaaprb, ciaadrb);
#endif
	}
#endif
	break;
    case 2:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFE201 W %02.2X %s\n", val, debuginfo(0));
#endif
	ciaadra = val; bfe001_change (); break;
    case 3:
	ciaadrb = val;
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFE301 W %02.2X %s\n", val, debuginfo(0));
#endif
#ifdef ARCADIA
	if (arcadia_rom)
	    arcadia_parport (1, ciaaprb, ciaadrb);
#endif
	break;
    case 4:
	CIA_update ();
	ciaala = (ciaala & 0xff00) | val;
	CIA_calctimers ();
	break;
    case 5:
	CIA_update ();
	ciaala = (ciaala & 0xff) | (val << 8);
	if ((ciaacra & 1) == 0)
	    ciaata = ciaala;
	if (ciaacra & 8) {
	    ciaata = ciaala;
	    ciaacra |= 1;
	}
	CIA_calctimers ();
	break;
    case 6:
	CIA_update ();
	ciaalb = (ciaalb & 0xff00) | val;
	CIA_calctimers ();
	break;
    case 7:
	CIA_update ();
	ciaalb = (ciaalb & 0xff) | (val << 8);
	if ((ciaacrb & 1) == 0)
	    ciaatb = ciaalb;
	if (ciaacrb & 8) {
	    ciaatb = ciaalb;
	    ciaacrb |= 1;
	}
	CIA_calctimers ();
	break;
    case 8:
	if (ciaacrb & 0x80) {
	    ciaaalarm = (ciaaalarm & ~0xff) | val;
	} else {
	    ciaatod = (ciaatod & ~0xff) | val;
	    ciaatodon = 1;
	    ciaa_checkalarm (0);
#ifdef TOD_HACK
	    if (currprefs.tod_hack)
		tod_hack_reset ();
#endif
	}
	break;
    case 9:
	if (ciaacrb & 0x80) {
	    ciaaalarm = (ciaaalarm & ~0xff00) | (val << 8);
	} else {
	    ciaatod = (ciaatod & ~0xff00) | (val << 8);
	}
	break;
    case 10:
	if (ciaacrb & 0x80) {
	    ciaaalarm = (ciaaalarm & ~0xff0000) | (val << 16);
	} else {
	    ciaatod = (ciaatod & ~0xff0000) | (val << 16);
	    ciaatodon = 0;
	}
	break;
    case 12:
	CIA_update ();
	ciaasdr = val;
	if (ciaacra & 0x40) {
	    kback = 1;
	} else {
	    ciaasdr_cnt = 0;
	}
	if ((ciaacra & 0x41) == 0x41 && ciaasdr_cnt == 0)
	    ciaasdr_cnt = 8 * 2;
	CIA_calctimers ();
	break;
    case 13:
	setclr(&ciaaimask,val);
	break;
    case 14:
	CIA_update ();
	val &= 0x7f; /* bit 7 is unused */
	ciaacra = val;
	if (ciaacra & 0x10) {
	    ciaacra &= ~0x10;
	    ciaata = ciaala;
	}
	if (ciaacra & 0x40)
	    kback = 1;
	CIA_calctimers ();
	break;
    case 15:
	CIA_update ();
	ciaacrb = val;
	if (ciaacrb & 0x10) {
	    ciaacrb &= ~0x10;
	    ciaatb = ciaalb;
	}
	CIA_calctimers ();
	break;
    }
}

static void WriteCIAB (uae_u16 addr,uae_u8 val)
{
#ifdef CIA_DEBUG_W
    write_log("W_CIAB: bfd%x00 %02.2X %08.8X\n", addr, val, M68K_GETPC);
#endif
#ifdef ACTION_REPLAY
    ar_ciab[addr & 0xf] = val;
#endif
    switch (addr & 0xf) {
    case 0:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFD000 W %02.2X %s\n", val, debuginfo(0));
#endif
	ciabpra = val;
#ifdef SERIAL_PORT
	if (currprefs.use_serial)
	    serial_writestatus(ciabpra, ciabdra);
#endif
#ifdef PARALLEL_PORT
	if (isprinter () < 0)
	    parallel_direct_write_status (val, ciabdra);
#endif
	break;
    case 1:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFD100 W %02.2X %s\n", val, debuginfo(0));
#endif
	ciabprb = val; DISK_select(val); break;
    case 2:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFD200 W %02.2X %s\n", val, debuginfo(0));
#endif
	ciabdra = val;
#ifdef SERIAL_PORT
	if (currprefs.use_serial)
	    serial_writestatus (ciabpra, ciabdra);
#endif
	break;
    case 3:
#ifdef DONGLE_DEBUG
	if (notinrom ())
	    write_log ("BFD300 W %02.2X %s\n", val, debuginfo(0));
#endif
	ciabdrb = val; break;
    case 4:
	CIA_update ();
	ciabla = (ciabla & 0xff00) | val;
	CIA_calctimers ();
	break;
    case 5:
	CIA_update ();
	ciabla = (ciabla & 0xff) | (val << 8);
	if ((ciabcra & 1) == 0)
	    ciabta = ciabla;
	if (ciabcra & 8) {
	    ciabta = ciabla;
	    ciabcra |= 1;
	}
	CIA_calctimers ();
	break;
    case 6:
	CIA_update ();
	ciablb = (ciablb & 0xff00) | val;
	CIA_calctimers ();
	break;
    case 7:
	CIA_update ();
	ciablb = (ciablb & 0xff) | (val << 8);
	if ((ciabcrb & 1) == 0)
	    ciabtb = ciablb;
	if (ciabcrb & 8) {
	    ciabtb = ciablb;
	    ciabcrb |= 1;
	}
	CIA_calctimers ();
	break;
    case 8:
	if (ciabcrb & 0x80) {
	    ciabalarm = (ciabalarm & ~0xff) | val;
	} else {
	    ciabtod = (ciabtod & ~0xff) | val;
	    ciabtodon = 1;
	    ciab_checkalarm (0);
	}
	break;
    case 9:
	if (ciabcrb & 0x80) {
	    ciabalarm = (ciabalarm & ~0xff00) | (val << 8);
	} else {
	    ciabtod = (ciabtod & ~0xff00) | (val << 8);
	}
	break;
    case 10:
	if (ciabcrb & 0x80) {
	    ciabalarm = (ciabalarm & ~0xff0000) | (val << 16);
	} else {
	    ciabtod = (ciabtod & ~0xff0000) | (val << 16);
	    ciabtodon = 0;
	}
	break;
    case 12:
	CIA_update ();
	ciabsdr = val;
	if ((ciabcra & 0x40) == 0)
	    ciabsdr_cnt = 0;
	if ((ciabcra & 0x41) == 0x41 && ciabsdr_cnt == 0)
	    ciabsdr_cnt = 8 * 2;
	CIA_calctimers ();
	break;
    case 13:
	setclr(&ciabimask,val);
	break;
    case 14:
	CIA_update ();
	val &= 0x7f; /* bit 7 is unused */
	ciabcra = val;
	if (ciabcra & 0x10) {
	    ciabcra &= ~0x10;
	    ciabta = ciabla;
	}
	CIA_calctimers ();
	break;
    case 15:
	CIA_update ();
	ciabcrb = val;
	if (ciabcrb & 0x10) {
	    ciabcrb &= ~0x10;
	    ciabtb = ciablb;
	}
	CIA_calctimers ();
	break;
    }
}

void CIA_inprec_prepare(void)
{
    sleepyhead = 0;
}

void CIA_reset (void)
{
#ifdef TOD_HACK
    tod_hack = 0;
    if (currprefs.tod_hack)
	tod_hack_reset ();
#endif
    kback = 1;
    kbstate = 0;
    ciaasdr_unread = 0;
    serbits = 0;
    oldovl = -1;
    oldled = -1;

    if (!savestate_state) {
	ciaatlatch = ciabtlatch = 0;
	ciaapra = 0; ciaadra = 0;
	ciaatod = ciabtod = 0; ciaatodon = ciabtodon = 0;
	ciaaicr = ciabicr = ciaaimask = ciabimask = 0;
	ciaacra = ciaacrb = ciabcra = ciabcrb = 0x4; /* outmode = toggle; */
	ciaala = ciaalb = ciabla = ciablb = ciaata = ciaatb = ciabta = ciabtb = 0xFFFF;
	ciaaalarm = ciabalarm = 0;
	ciabpra = 0x8C; ciabdra = 0;
	div10 = 0;
	ciaasdr_cnt = 0; ciaasdr = 0;
	ciabsdr_cnt = 0; ciabsdr = 0;
	ciaata_passed = ciaatb_passed = ciabta_passed = ciabtb_passed = 0;
    }
    CIA_calctimers ();
    if (! ersatzkickfile)
	map_overlay (0);
#ifdef SERIAL_PORT
    if (currprefs.use_serial && !savestate_state)
	serial_dtr_off (); /* Drop DTR at reset */
#endif
    if (savestate_state) {
	bfe001_change ();
	/* select drives */
	DISK_select (ciabprb);
    }
#ifdef CD32
    if (cd32_enabled) {
	akiko_reset ();
	if (!akiko_init ())
	    cd32_enabled = 0;
    }
#endif
}

void dumpcia (void)
{
    console_out ("A: CRA %02x CRB %02x ICR %02x IM %02x TA %04x (%04x) TB %04x (%04x)\n",
	   ciaacra, ciaacrb, ciaaicr, ciaaimask, ciaata, ciaala, ciaatb, ciaalb);
    console_out ("TOD %06x (%06x) ALARM %06x %c%c\n",
	ciaatod, ciaatol, ciaaalarm, ciaatlatch ? 'L' : ' ', ciaatodon ? ' ' : 'S');
    console_out ("B: CRA %02x CRB %02x ICR %02x IM %02x TA %04x (%04x) TB %04x (%04x)\n",
	   ciabcra, ciabcrb, ciaaicr, ciabimask, ciabta, ciabla, ciabtb, ciablb);
    console_out ("TOD %06x (%06x) ALARM %06x %c%c\n",
	ciabtod, ciabtol, ciabalarm, ciabtlatch ? 'L' : ' ', ciabtodon ? ' ' : 'S');
}

/* CIA memory access */

static uae_u32 REGPARAM3 cia_lget (uaecptr) REGPARAM;
static uae_u32 REGPARAM3 cia_wget (uaecptr) REGPARAM;
static uae_u32 REGPARAM3 cia_bget (uaecptr) REGPARAM;
static uae_u32 REGPARAM3 cia_lgeti (uaecptr) REGPARAM;
static uae_u32 REGPARAM3 cia_wgeti (uaecptr) REGPARAM;
static void REGPARAM3 cia_lput (uaecptr, uae_u32) REGPARAM;
static void REGPARAM3 cia_wput (uaecptr, uae_u32) REGPARAM;
static void REGPARAM3 cia_bput (uaecptr, uae_u32) REGPARAM;

addrbank cia_bank = {
    cia_lget, cia_wget, cia_bget,
    cia_lput, cia_wput, cia_bput,
    default_xlate, default_check, NULL, "CIA",
    cia_lgeti, cia_wgeti, ABFLAG_IO
};


/* e-clock is 10 CPU cycles, 6 cycles low, 4 high
 * data transfer happens during 4 high cycles
 */
#define ECLOCK_DATA_CYCLE 4

static void cia_wait_pre (void)
{
#ifndef CUSTOM_SIMPLE
    int div10 = (get_cycles () - eventtab[ev_cia].oldcycles) % DIV10;
    int cycles;

    cycles = 5 * CYCLE_UNIT / 2;
    if (div10 > DIV10 * ECLOCK_DATA_CYCLE / 10) {
	cycles += DIV10 - div10;
	cycles += DIV10 * ECLOCK_DATA_CYCLE / 10;
    } else {
	cycles += DIV10 * ECLOCK_DATA_CYCLE / 10 - div10;
    }
    do_cycles (cycles);
#endif
}

static void cia_wait_post (void)
{
    do_cycles (2 * CYCLE_UNIT / 2);
}

uae_u32 REGPARAM2 cia_bget (uaecptr addr)
{
    int r = (addr & 0xf00) >> 8;
    uae_u8 v;

#ifdef JIT
    special_mem |= S_READ;
#endif
#ifdef CD32
    if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END)
	return akiko_bget (addr);
#endif
    cia_wait_pre ();
    v = 0xff;
    switch ((addr >> 12) & 3)
    {
	case 0:
	v = (addr & 1) ? ReadCIAA (r) : ReadCIAB (r);
	break;
	case 1:
	v = (addr & 1) ? 0xff : ReadCIAB (r);
	break;
	case 2:
	v = (addr & 1) ? ReadCIAA (r) : 0xff;
	break;
	case 3:
	if (currprefs.cpu_level == 0 && currprefs.cpu_compatible)
	    v = (addr & 1) ? regs.irc : regs.irc >> 8;
	if (warned > 0) {
	    write_log ("cia_bget: unknown CIA address %x PC=%x\n", addr, M68K_GETPC);
	    warned--;
	}
	break;
   }
    cia_wait_post ();
    return v;
}

uae_u32 REGPARAM2 cia_wget (uaecptr addr)
{
    int r = (addr & 0xf00) >> 8;
    uae_u16 v;

#ifdef JIT
    special_mem |= S_READ;
#endif
#ifdef CD32
    if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END)
	return akiko_wget (addr);
#endif
    cia_wait_pre ();
    v = 0xffff;
    switch ((addr >> 12) & 3)
    {
	case 0:
	v = (ReadCIAB (r) << 8) | ReadCIAA (r);
	break;
	case 1:
	v = (ReadCIAB (r) << 8) | 0xff;
	break;
	case 2:
	v = (0xff << 8) | ReadCIAA (r);
	break;
	case 3:
	if (currprefs.cpu_level == 0 && currprefs.cpu_compatible)
	    v = regs.irc;
	if (warned > 0) {
	    write_log ("cia_wget: unknown CIA address %x PC=%x\n", addr, M68K_GETPC);
	    warned--;
	}
	break;

    }
    cia_wait_post ();
    return v;
}

uae_u32 REGPARAM2 cia_lget (uaecptr addr)
{
    uae_u32 v;
#ifdef JIT
    special_mem |= S_READ;
#endif
#ifdef CD32
    if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END)
	return akiko_lget (addr);
#endif
    v = cia_wget (addr) << 16;
    v |= cia_wget (addr + 2);
    return v;
}

static uae_u32 REGPARAM2 cia_wgeti (uaecptr addr)
{
    if (currprefs.cpu_level >= 2)
	return dummy_wgeti(addr);
    return cia_wget(addr);
}
static uae_u32 REGPARAM2 cia_lgeti (uaecptr addr)
{
    if (currprefs.cpu_level >= 2)
	return dummy_lgeti(addr);
    return cia_lget(addr);
}

void REGPARAM2 cia_bput (uaecptr addr, uae_u32 value)
{
    int r = (addr & 0xf00) >> 8;

#ifdef JIT
    special_mem |= S_WRITE;
#endif
#ifdef CD32
    if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END) {
	akiko_bput (addr, value);
	return;
    }
#endif
    cia_wait_pre ();
    if ((addr & 0x2000) == 0)
	WriteCIAB (r, value);
    if ((addr & 0x1000) == 0)
	WriteCIAA (r, value);
    if (((addr & 0x3000) == 0x3000) && warned > 0) {
	write_log ("cia_bput: unknown CIA address %x %x\n", addr, value);
	warned--;
    }
    cia_wait_post ();
}

void REGPARAM2 cia_wput (uaecptr addr, uae_u32 value)
{
    int r = (addr & 0xf00) >> 8;
#ifdef JIT
    special_mem |= S_WRITE;
#endif
#ifdef CD32
    if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END) {
	akiko_wput (addr, value);
	return;
    }
#endif
    cia_wait_pre ();
    if ((addr & 0x2000) == 0)
	WriteCIAB (r, value >> 8);
    if ((addr & 0x1000) == 0)
	WriteCIAA (r, value & 0xff);
    if (((addr & 0x3000) == 0x3000) && warned > 0) {
	write_log ("cia_wput: unknown CIA address %x %x\n", addr, value);
	warned--;
    }
    cia_wait_post ();
}

void REGPARAM2 cia_lput (uaecptr addr, uae_u32 value)
{
#ifdef JIT
    special_mem |= S_WRITE;
#endif
#ifdef CD32
    if (cd32_enabled && addr >= AKIKO_BASE && addr < AKIKO_BASE_END) {
	akiko_lput (addr, value);
	return;
    }
#endif
    cia_wput (addr, value >> 16);
    cia_wput (addr + 2, value & 0xffff);
}

#ifdef CDTV
/* CDTV batterybacked RAM emulation */

static uae_u8 cdtv_battram[4096];
#define CDTV_NVRAM_MASK 4095
#define CDTV_NVRAM_SIZE 2048
int cdtv_enabled;

static void cdtv_battram_reset (void)
{
    struct zfile *f = zfile_fopen (currprefs.flashfile,"rb");
    if (!f)
	return;
    zfile_fread (cdtv_battram, CDTV_NVRAM_SIZE,1 ,f);
    zfile_fclose (f);
}

static void cdtv_battram_write (int addr, int v)
{
    struct zfile *f;
    int offset = addr & CDTV_NVRAM_MASK;
    if (offset >= CDTV_NVRAM_SIZE)
	return;
    if (cdtv_battram[offset] == v)
	return;
    cdtv_battram[offset] = v;
    f = zfile_fopen (currprefs.flashfile,"rb+");
    if (!f)
	return;
    zfile_fseek (f, offset, SEEK_SET);
    zfile_fwrite (cdtv_battram + offset, 1, 1, f);
    zfile_fclose (f);
}

static uae_u8 cdtv_battram_read (int addr)
{
    uae_u8 v;
    int offset;
    offset = addr & CDTV_NVRAM_MASK;
    if (offset >= CDTV_NVRAM_SIZE)
	return 0;
    v = cdtv_battram[offset];
    return v;
}
#endif

/* battclock memory access */

static uae_u32 REGPARAM3 clock_lget (uaecptr) REGPARAM;
static uae_u32 REGPARAM3 clock_wget (uaecptr) REGPARAM;
static uae_u32 REGPARAM3 clock_bget (uaecptr) REGPARAM;
static void REGPARAM3 clock_lput (uaecptr, uae_u32) REGPARAM;
static void REGPARAM3 clock_wput (uaecptr, uae_u32) REGPARAM;
static void REGPARAM3 clock_bput (uaecptr, uae_u32) REGPARAM;

addrbank clock_bank = {
    clock_lget, clock_wget, clock_bget,
    clock_lput, clock_wput, clock_bput,
    default_xlate, default_check, NULL, "Battery backed up clock",
    dummy_lgeti, dummy_wgeti, ABFLAG_IO
};

uae_u32 REGPARAM2 clock_lget (uaecptr addr)
{
    return (clock_wget (addr) << 16) | clock_wget (addr + 2);
}

uae_u32 REGPARAM2 clock_wget (uaecptr addr)
{
    return (clock_bget (addr) << 8) | clock_bget (addr + 1);
}

uae_u32 REGPARAM2 clock_bget (uaecptr addr)
{
    time_t t = time(0);
    struct tm *ct;

#ifdef CDTV
    if (cdtv_enabled && addr >= 0xdc8000)
	return cdtv_battram_read (addr);
#endif
    ct = localtime (&t);
#ifdef JIT
    special_mem |= S_READ;
#endif
    switch (addr & 0x3f) {
    case 0x03: return ct->tm_sec % 10;
    case 0x07: return ct->tm_sec / 10;
    case 0x0b: return ct->tm_min % 10;
    case 0x0f: return ct->tm_min / 10;
    case 0x13: return ct->tm_hour % 10;
    case 0x17: return ct->tm_hour / 10;
    case 0x1b: return ct->tm_mday % 10;
    case 0x1f: return ct->tm_mday / 10;
    case 0x23: return (ct->tm_mon+1) % 10;
    case 0x27: return (ct->tm_mon+1) / 10;
    case 0x2b: return ct->tm_year % 10;
    case 0x2f: return ct->tm_year / 10;
    case 0x33: return ct->tm_wday;  /*Hack by -=SR=- */
    case 0x37: return clock_control_d;
    case 0x3b: return clock_control_e;
    case 0x3f: return clock_control_f;
    }
    return 0;
}

void REGPARAM2 clock_lput (uaecptr addr, uae_u32 value)
{
    clock_wput (addr, value >> 16);
    clock_wput (addr + 2, value);
}

void REGPARAM2 clock_wput (uaecptr addr, uae_u32 value)
{
    clock_bput (addr, value >> 8);
    clock_bput (addr + 1, value);
}

void REGPARAM2 clock_bput (uaecptr addr, uae_u32 value)
{
#ifdef JIT
    special_mem |= S_WRITE;
#endif
#ifdef CDTV
    if (cdtv_enabled && addr >= 0xdc8000) {
	cdtv_battram_write (addr, value);
	return;
    }
#endif
    switch (addr & 0x3f) {
    case 0x37: clock_control_d = value; break;
    case 0x3b: clock_control_e = value; break;
    case 0x3f: clock_control_f = value; break;
    }
}

#ifdef SAVESTATE

/* CIA-A and CIA-B save/restore code */

uae_u8 *restore_cia (int num, uae_u8 *src)
{
    uae_u8 b;
    uae_u16 w;
    uae_u32 l;

    /* CIA registers */
    b = restore_u8 ();					/* 0 PRA */
    if (num) ciabpra = b; else ciaapra = b;
    b = restore_u8 ();					/* 1 PRB */
    if (num) ciabprb = b; else ciaaprb = b;
    b = restore_u8 ();					/* 2 DDRA */
    if (num) ciabdra = b; else ciaadra = b;
    b = restore_u8 ();					/* 3 DDRB */
    if (num) ciabdrb = b; else ciaadrb = b;
    w = restore_u16 ();					/* 4 TA */
    if (num) ciabta = w; else ciaata = w;
    w = restore_u16 ();					/* 6 TB */
    if (num) ciabtb = w; else ciaatb = w;
    l = restore_u8 ();					/* 8/9/A TOD */
    l |= restore_u8 () << 8;
    l |= restore_u8 () << 16;
    if (num) ciabtod = l; else ciaatod = l;
    restore_u8 ();					/* B unused */
    b = restore_u8 ();					/* C SDR */
    if (num) ciabsdr = b; else ciaasdr = b;
    b = restore_u8 ();					/* D ICR INFORMATION (not mask!) */
    if (num) ciabicr = b; else ciaaicr = b;
    b = restore_u8 ();					/* E CRA */
    if (num) ciabcra = b; else ciaacra = b;
    b = restore_u8 ();					/* F CRB */
    if (num) ciabcrb = b; else ciaacrb = b;

    /* CIA internal data */

    b = restore_u8 ();					/* ICR MASK */
    if (num) ciabimask = b; else ciaaimask = b;
    w = restore_u8 ();					/* timer A latch */
    w |= restore_u8 () << 8;
    if (num) ciabla = w; else ciaala = w;
    w = restore_u8 ();					/* timer B latch */
    w |= restore_u8 () << 8;
    if (num) ciablb = w; else ciaalb = w;
    w = restore_u8 ();					/* TOD latched value */
    w |= restore_u8 () << 8;
    w |= restore_u8 () << 16;
    if (num) ciabtol = w; else ciaatol = w;
    l = restore_u8 ();					/* alarm */
    l |= restore_u8 () << 8;
    l |= restore_u8 () << 16;
    if (num) ciabalarm = l; else ciaaalarm = l;
    b = restore_u8 ();
    if (num) ciabtlatch = b & 1; else ciaatlatch = b & 1;	/* is TOD latched? */
    if (num) ciabtodon = b & 2; else ciaatodon = b & 2;		/* is TOD stopped? */
    b = restore_u8 ();
    if (num)
	div10 = CYCLE_UNIT * b;
    b = restore_u8 ();
    if (num) ciabsdr_cnt = b; else ciaasdr_cnt = b;
    return src;
}

uae_u8 *save_cia (int num, int *len, uae_u8 *dstptr)
{
    uae_u8 *dstbak,*dst, b;
    uae_u16 t;

    if (dstptr)
	dstbak = dst = dstptr;
    else
	dstbak = dst = malloc (16 + 12 + 1 + 1);

    compute_passed_time ();

    /* CIA registers */

    b = num ? ciabpra : ciaapra;				/* 0 PRA */
    save_u8 (b);
    b = num ? ciabprb : ciaaprb;				/* 1 PRB */
    save_u8 (b);
    b = num ? ciabdra : ciaadra;				/* 2 DDRA */
    save_u8 (b);
    b = num ? ciabdrb : ciaadrb;				/* 3 DDRB */
    save_u8 (b);
    t = (num ? ciabta - ciabta_passed : ciaata - ciaata_passed);/* 4 TA */
    save_u16 (t);
    t = (num ? ciabtb - ciabtb_passed : ciaatb - ciaatb_passed);/* 8 TB */
    save_u16 (t);
    b = (num ? ciabtod : ciaatod);			/* 8 TODL */
    save_u8 (b);
    b = (num ? ciabtod >> 8 : ciaatod >> 8);		/* 9 TODM */
    save_u8 (b);
    b = (num ? ciabtod >> 16 : ciaatod >> 16);		/* A TODH */
    save_u8 (b);
    save_u8 (0);						/* B unused */
    b = num ? ciabsdr : ciaasdr;				/* C SDR */
    save_u8 (b);
    b = num ? ciabicr : ciaaicr;				/* D ICR INFORMATION (not mask!) */
    save_u8 (b);
    b = num ? ciabcra : ciaacra;				/* E CRA */
    save_u8 (b);
    b = num ? ciabcrb : ciaacrb;				/* F CRB */
    save_u8 (b);

    /* CIA internal data */

    save_u8 (num ? ciabimask : ciaaimask);			/* ICR */
    b = (num ? ciabla : ciaala);			/* timer A latch LO */
    save_u8 (b);
    b = (num ? ciabla >> 8 : ciaala >> 8);		/* timer A latch HI */
    save_u8 (b);
    b = (num ? ciablb : ciaalb);			/* timer B latch LO */
    save_u8 (b);
    b = (num ? ciablb >> 8 : ciaalb >> 8);		/* timer B latch HI */
    save_u8 (b);
    b = (num ? ciabtol : ciaatol);			/* latched TOD LO */
    save_u8 (b);
    b = (num ? ciabtol >> 8 : ciaatol >> 8);		/* latched TOD MED */
    save_u8 (b);
    b = (num ? ciabtol >> 16 : ciaatol >> 16);		/* latched TOD HI */
    save_u8 (b);
    b = (num ? ciabalarm : ciaaalarm);			/* alarm LO */
    save_u8 (b);
    b = (num ? ciabalarm >> 8 : ciaaalarm >> 8);	/* alarm MED */
    save_u8 (b);
    b = (num ? ciabalarm >> 16 : ciaaalarm >> 16);	/* alarm HI */
    save_u8 (b);
    b = 0;
    if (num)
	b |= ciabtlatch ? 1 : 0;
    else
	b |= ciaatlatch ? 1 : 0; /* is TOD latched? */
    if (num)
	b |= ciabtodon ? 2 : 0;
    else
	b |= ciaatodon ? 2 : 0;   /* TOD stopped? */
    save_u8 (b);
    save_u8 (div10 / CYCLE_UNIT);
    save_u8 (num ? ciabsdr_cnt : ciaasdr_cnt);
    *len = dst - dstbak;
    return dstbak;
}

#endif /* SAVESTATE */