ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/c64/c64cia1.c

This is c64cia1.c in view mode; [Download] [Up]

/*
 * ../../../src/c64/c64cia1.c
 * This file is generated from ../../../src/cia-tmpl.c and ../../../src/c64/c64cia1.def,
 * Do not edit!
 */
/*
 * cia-tmpl.c - Template file for MOS6526 (CIA) emulation.
 *
 * Written by
 *  André Fachat (fachat@physik.tu-chemnitz.de)
 *
 * Patches and improvements by
 *  Ettore Perazzoli (ettore@comm2000.it)
 *  Andreas Boose (boose@rzgw.rz.fh-hannover.de)
 *
 * This file is part of VICE, the Versatile Commodore Emulator.
 * See README for copyright notice.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA.
 *
 */

/*
 * 03jun1997 a.fachat
 * complete timer rewrite
 *
 * There now is a new function, update_cia1(). It computes all differences
 * from one given state to a new state at rclk.  Well, not everything. For
 * the cascade mode (timer B counting timer A) as well as the shift register
 * the interrupt function is being used.
 *
 * The update routine computes, completely independent from the interrupt
 * stuff, the timer register values. The update routines are only called when
 * a timer register is touched.  int_*() is now called only _once_ per
 * interrupt, and it is much smaller than the last version. It is disabled
 * when the interrupt flag is not set (at least timer B. For timer A there
 * must not be a shift register operation and not timer B counting timer A)
 *
 * The timer B when counting timer A and the Shift register interrupt flags
 * may be set a few cycles too late due to late call of int_cia1t*() due to
 * opcode execution time. This can be fixed by checking in the beginning of
 * read_* and write_* if an int_* is scheduled and executing it before.  Then
 * the setting of the ICR could also be moved from update to int_*().  But
 * the bug only affects the contents of the ICR. The interrupt is generated
 * at the right time (hopefully).
 *
 * There is one HACK to make a game work: in update_cia1() a fix is done for
 * Arkanoid. This game counts shift register bits (i.e. TA underflows) by
 * setting TA to one-shot.  The ICR is read just before the int_cia1ta()
 * function is called, and the int bit is missed, so there is a check in
 * update_cia1() (this is probably a fix and not a hack... :-)
 *
 * Still some work has to be put into the dump functions, to really show the
 * state of the chip and not the state of the emulation. I.e. we need to
 * compute the bits described in the emulator test suite.
 *
 */

/*
 * 29jun1998 a.fachat
 *
 * Implementing the peek function assumes that the READ_PA etc macros
 * do not have side-effects, i.e. they can be called more than once
 * at one clock cycle.
 *
 */

#include "vice.h"

#include <stdio.h>
#include <time.h>
#include <string.h>

#include "vmachine.h"
#include "cia.h"
#include "vicii.h"
#include "maincpu.h"
#include "resources.h"


#include "kbd.h"
#include "c64cia.h"
#ifdef HAVE_RS232
#include "rsuser.h"
#endif

#include "interrupt.h"

#undef CIA1_TIMER_DEBUG
#undef CIA1_IO_DEBUG

#define	STORE_OFFSET 0
#define	READ_OFFSET 0

#define	CIAT_STOPPED	0
#define	CIAT_RUNNING	1
#define	CIAT_COUNTTA	2

#ifdef CIA1_TIMER_DEBUG
#define	my_set_int(int_num, value, rclk)				\
    do {								\
        if (cia1_debugFlag)					\
	    printf("set_int(rclk=%d, int=%d, d=%d pc=)\n",		\
		   rclk,(int_num),(value));			\
	maincpu_set_irq_clk((int_num), (value), (rclk));			\
	if ((value))							\
	    cia1int |= 0x80;						\
    } while(0)
#else
#define	my_set_int(int_num, value, rclk)				 \
    do {								 \
        maincpu_set_irq_clk((int_num), (value), (rclk));			 \
	if ((value))							 \
	    cia1int |= 0x80;						 \
    } while(0)
#endif

/* 
 * scheduling int_cia1t[ab] calls - 
 * warning: int_cia1ta uses maincpu_* stuff! 
 */

#define	my_set_tai_clk(clk) 						\
    do {								\
	cia1_tai = clk;						\
	maincpu_set_alarm_clk(A_CIA1TA, clk);				\
    } while(0)

#define	my_unset_tai() 							\
    do {								\
	cia1_tai = -1;							\
	maincpu_unset_alarm(A_CIA1TA);					\
    } while(0)

#define	my_set_tbi_clk(clk) 						\
    do {								\
	cia1_tbi = clk;						\
	maincpu_set_alarm_clk(A_CIA1TB, clk);				\
    } while(0)

#define	my_unset_tbi() 							\
    do {								\
	cia1_tbi = -1;							\
	maincpu_unset_alarm(A_CIA1TB);					\
    } while(0)

/*
 * Those routines setup the cia1t[ab]i clocks to a value above
 * rclk and schedule the next int_cia1t[ab] alarm
 */
#define	update_tai(rclk)							\
    do {								\
	if(cia1_tai < rclk) {						\
	    int t = cia1int;						\
	    cia1int = 0;						\
	    int_cia1ta(rclk - cia1_tai);				\
	    cia1int |= t;						\
	}								\
    } while(0)

#define	update_tbi(rclk)							\
    do {								\
	if(cia1_tbi < rclk) {						\
	    int t = cia1int;						\
	    cia1int = 0;						\
	    int_cia1tb(rclk - cia1_tbi);				\
	    cia1int |= t;						\
	}								\
    } while(0)

/* global */

static BYTE cia1[16];

#if defined(CIA1_TIMER_DEBUG) || defined(CIA1_IO_DEBUG)
int cia1_debugFlag = 0;

#endif

/* local functions */

static int update_cia1(CLOCK rclk);
static void check_cia1todalarm(CLOCK rclk);

void cia1_dump(FILE * fp);

/*
 * Local variables
 */

#define	cia1ier	cia1[CIA_ICR]
static int cia1int;		/* Interrupt Flag register for cia 1 */

static CLOCK cia1rdi;		/* real clock = clk-offset */

static CLOCK cia1_tau;		/* when is the next underflow? */
static CLOCK cia1_tai;		/* when is the next int_* scheduled? */
static unsigned int cia1_tal;	/* latch value */
static unsigned int cia1_tac;	/* counter value */
static unsigned int cia1_tat;	/* timer A toggle bit */
static unsigned int cia1_tap;	/* timer A port bit */
static int cia1_tas;		/* timer state (CIAT_*) */

static CLOCK cia1_tbu;		/* when is the next underflow? */
static CLOCK cia1_tbi;		/* when is the next int_* scheduled? */
static unsigned int cia1_tbl;	/* latch value */
static unsigned int cia1_tbc;	/* counter value */
static unsigned int cia1_tbt;	/* timer B toggle bit */
static unsigned int cia1_tbp;	/* timer B port bit */
static int cia1_tbs;		/* timer state (CIAT_*) */

static int cia1sr_bits;	/* number of bits still to send * 2 */

static BYTE cia1todalarm[4];
static BYTE cia1todlatch[4];
static char cia1todstopped;
static char cia1todlatched;
static int cia1todticks = 100000;	/* approx. a 1/10 sec. */

static BYTE cia1flag = 0;


/* ------------------------------------------------------------------------- */
/* CIA1 */


    /* Flag: Are the 3 C128 extended rows enabled?  */
    static int extended_keyboard_rows_enabled;

    /* Mask for the extended keyboard rows.  */
    static BYTE extended_keyboard_rows_mask;

    void cia1_enable_extended_keyboard_rows(int flag)
    {
	extended_keyboard_rows_enabled = flag;
    }

    void cia1_set_extended_keyboard_rows_mask(BYTE value)
    {
	extended_keyboard_rows_mask = value;
    }

inline static void check_cia1todalarm(CLOCK rclk)
{
    if (!memcmp(cia1todalarm, cia1 + CIA_TOD_TEN, sizeof(cia1todalarm))) {
	cia1int |= CIA_IM_TOD;
	if (cia1[CIA_ICR] & CIA_IM_TOD) {
	    my_set_int(I_CIA1TOD, IK_IRQ, rclk);
	}
    }
}

static int update_cia1(CLOCK rclk)
{
    int tmp = 0;
    unsigned int ista = 0;
    BYTE sif = (cia1int & cia1ier & 0x7f);
    /* Tick when we virtually added an interrupt flag first. */
    CLOCK added_int_clk = (cia1int & 0x80) ? rclk - 3 : CLOCK_MAX;

#ifdef CIA1_TIMER_DEBUG
    if (cia1_debugFlag)
	printf("CIA1: update: rclk=%d, tas=%d, tau=%d, tal=%u, ",
	       rclk, cia1_tas, cia1_tau, cia1_tal);
#endif

    if (cia1_tas == CIAT_RUNNING) {
	if (rclk < cia1_tau + 1) {
	    cia1_tac = cia1_tau - rclk;
	    tmp = 0;
	} else {
	    if (cia1[CIA_CRA] & 0x08) {
		tmp = 1;
		if ((cia1ier & CIA_IM_TA)
		    && (cia1_tau < added_int_clk))
		    added_int_clk = cia1_tau;
		cia1_tau = 0;
		my_unset_tai();
		cia1_tac = cia1_tal;
		cia1_tas = CIAT_STOPPED;
		cia1[CIA_CRA] &= 0xfe;

		/* this is a HACK for arkanoid... */
		if (cia1sr_bits) {
		    cia1sr_bits--;
		    if(cia1sr_bits==16) {

#ifdef HAVE_RS232
    if (rsuser_enabled) {
	userport_serial_write_sr(cia1[CIA_SDR]);
    }
#endif
		    }
		    if (!cia1sr_bits) {
			cia1int |= CIA_IM_SDR;
			if ((cia1ier & CIA_IM_SDR)
			    && (cia1_tau < added_int_clk))
			    added_int_clk = cia1_tau;
		    }
		}
	    } else {
		tmp = (rclk - cia1_tau - 1) / (cia1_tal + 1);
		cia1_tau += tmp * (cia1_tal + 1);
		if ((cia1ier & CIA_IM_TA)
		    && (cia1_tau < added_int_clk))
		    added_int_clk = cia1_tau;
		cia1_tau += 1 * (cia1_tal + 1);
		cia1_tac = cia1_tau - rclk;
	    }

	    if (cia1_tac == cia1_tal)
		ista = 1;

            cia1int |= CIA_IM_TA;
	}
    }
#ifdef CIA1_TIMER_DEBUG
    if (cia1_debugFlag)
	printf("aic=%d, tac-> %u, tau-> %d\n              tmp=%u, ", added_int_clk, cia1_tac, cia1_tau, tmp);
#endif

    if (cia1[CIA_CRA] & 0x04) {
	cia1_tap = cia1_tat;
    } else {
	cia1_tap = cia1_tac ? 0 : 1;
    }

    cia1_tbp = 0;
    if (cia1_tbs == CIAT_RUNNING) {
	if (rclk < cia1_tbu + 1) {
	    cia1_tbc = cia1_tbu - rclk;
	} else {
	    if (cia1[CIA_CRB] & 0x08) {
		tmp = 1;
		if ((cia1ier & CIA_IM_TB) && (cia1_tbu < added_int_clk))
		    added_int_clk = cia1_tbu;
		cia1_tbu = 0;
		my_unset_tbi();
		cia1_tbc = cia1_tbl;
		cia1_tbs = CIAT_STOPPED;
		cia1[CIA_CRB] &= 0xfe;
	    } else {
		tmp = (rclk - cia1_tbu - 1) / (cia1_tbl + 1);
		cia1_tbu += tmp * (cia1_tbl + 1);
		if ((cia1ier & CIA_IM_TB) && (cia1_tbu < added_int_clk))
		    added_int_clk = cia1_tbu;
		cia1_tbu += 1 * (cia1_tbl + 1);
		cia1_tbc = cia1_tbu - rclk;
	    }
	    if (!cia1_tbc)
		cia1_tbc = cia1_tbl;
            cia1int |= CIA_IM_TB;
	}
    } else if (cia1_tbs == CIAT_COUNTTA) {
	/* missing: set added_int */
	if ((!cia1_tbc) && ista) {
	    cia1_tbp = 1;
	    cia1_tbc = cia1_tbl;
	    cia1int |= CIA_IM_TB;
	}
    }
    if (cia1[CIA_CRB] & 0x04) {
	cia1_tbp ^= cia1_tbt;
    } else {
	cia1_tbp = cia1_tbc ? 0 : 1;
    }

#ifdef CIA1_TIMER_DEBUG
    if (cia1_debugFlag)
	printf("tbc-> %u, tbu-> %d, int %02x ->",
	       cia1_tbc, cia1_tbu, cia1int);
#endif

    /* have we changed the interrupt flags? */
    if (sif != (cia1ier & cia1int & 0x7f)) {
	/* if we do not read ICR, do standard operation */
	if (rclk != cia1rdi) {
	    if (cia1ier & cia1int & 0x7f) {
		/* sets bit 7 */
		my_set_int(I_CIA1FL, IK_IRQ, rclk);
	    }
	} else {
	    if (added_int_clk == cia1rdi) {
		cia1int &= 0x7f;
#ifdef CIA1_TIMER_DEBUG
		if (cia1_debugFlag)
		    printf("CIA1: TA Reading ICR at rclk=%d prevented IRQ\n",
			   rclk);
#endif
	    } else {
		if (cia1ier & cia1int & 0x7f) {
		    /* sets bit 7 */
		    my_set_int(I_CIA1FL, IK_IRQ, rclk);
		}
	    }
	}
    }
#ifdef CIA1_TIMER_DEBUG
    if (cia1_debugFlag)
	printf("%02x\n", cia1int);
#endif

    /* return true sif interrupt line is set at this clock time */
    return (!sif) && (cia1int & cia1ier & 0x7f);
}

/* ------------------------------------------------------------------------- */

void reset_cia1(void)
{
    int i;

    for (i = 0; i < 16; i++)
	cia1[i] = 0;

    cia1rdi = 0;
    cia1sr_bits = 0;

    cia1_tac = cia1_tbc = 0xffff;
    cia1_tal = cia1_tbl = 0xffff;

    cia1_tas = CIAT_STOPPED;
    cia1_tbs = CIAT_STOPPED;
    cia1_tat = 0;
    cia1_tbt = 0;

    my_unset_tbi();
    my_unset_tai();

    memset(cia1todalarm, 0, sizeof(cia1todalarm));
    cia1todlatched = 0;
    cia1todstopped = 0;
    maincpu_set_alarm(A_CIA1TOD, cia1todticks);

    cia1int = 0;

    
}


void REGPARM2 store_cia1(ADDRESS addr, BYTE byte)
{
    CLOCK rclk;

    addr &= 0xf;

    vic_ii_handle_pending_alarms(maincpu_num_write_cycles());

    rclk = clk - STORE_OFFSET;

#ifdef CIA1_TIMER_DEBUG
    if (cia1_debugFlag)
	printf("store cia1[%02x] %02x @ clk=%d, pc=\n",
	       (int) addr, (int) byte, rclk);
#endif

    switch (addr) {

      case CIA_PRA:		/* port A */
      case CIA_DDRA:
	cia1[addr] = byte;
	break;

      case CIA_PRB:		/* port B */
      case CIA_DDRB:
	if ((cia1[CIA_CRA] | cia1[CIA_CRB]) & 0x02) {
	    update_cia1(rclk);
	    if (cia1[CIA_CRA] & 0x02) {
		byte &= 0xbf;
		if (cia1_tap)
		    byte |= 0x40;
	    }
	    if (cia1[CIA_CRB] & 0x02) {
		byte &= 0x7f;
		if (cia1_tbp)
		    byte |= 0x80;
	    }
	}

    {
	static int old_lp = 0x10;
	int new_lp;

	cia1[addr] = byte;

	/* Handle software-triggered light pen.  */
	new_lp = (cia1[CIA_PRB] | ~cia1[CIA_DDRB]) & 0x10;
	if (new_lp != old_lp) {
	    vic_ii_trigger_light_pen(rclk);
	    old_lp = new_lp;
	}
    }
	break;

	/* This handles the timer latches.  The kludgy stuff is an attempt
           emulate the correct behavior when the latch is written to during
           an underflow.  */
      case CIA_TAL:
	update_tai(rclk); /* schedule alarm in case latch value is changed */
	update_cia1(rclk - 1);
	if (cia1_tac == cia1_tal && cia1_tas == CIAT_RUNNING) {
	    cia1_tac = cia1_tal = (cia1_tal & 0xff00) | byte;
	    cia1_tau = rclk + cia1_tac;
	    update_cia1(rclk);
	} else {
	    cia1_tal = (cia1_tal & 0xff00) | byte;
	}
	break;
      case CIA_TBL:
	update_tbi(rclk); /* schedule alarm in case latch value is changed */
	update_cia1(rclk - 1);
	if (cia1_tbc == cia1_tbl && cia1_tbs == CIAT_RUNNING) {
	    cia1_tbc = cia1_tbl = (cia1_tbl & 0xff00) | byte;
	    cia1_tbu = rclk + cia1_tbc + 1;
	    update_cia1(rclk);
	} else {
	    cia1_tbl = (cia1_tbl & 0xff00) | byte;
	}
	break;
      case CIA_TAH:
	update_tai(rclk); /* schedule alarm in case latch value is changed */
	update_cia1(rclk - 1);
	if (cia1_tac == cia1_tal && cia1_tas == CIAT_RUNNING) {
	    cia1_tac = cia1_tal = (cia1_tal & 0x00ff) | (byte << 8);
	    cia1_tau = rclk + cia1_tac;
	    update_cia1(rclk);
	} else {
	    cia1_tal = (cia1_tal & 0x00ff) | (byte << 8);
	}
	if (cia1_tas == CIAT_STOPPED)
	    cia1_tac = cia1_tal;
	break;
      case CIA_TBH:
	update_tbi(rclk); /* schedule alarm in case latch value is changed */
	update_cia1(rclk - 1);
	if (cia1_tbc == cia1_tbl && cia1_tbs == CIAT_RUNNING) {
	    cia1_tbc = cia1_tbl = (cia1_tbl & 0x00ff) | (byte << 8);
	    cia1_tbu = rclk + cia1_tbc + 1;
	    update_cia1(rclk);
	} else {
	    cia1_tbl = (cia1_tbl & 0x00ff) | (byte << 8);
	}
	if (cia1_tbs == CIAT_STOPPED)
	    cia1_tbc = cia1_tbl;
	break;

	/*
	 * TOD clock is stopped by writing Hours, and restarted
	 * upon writing Tenths of Seconds.
	 *
	 * REAL:  TOD register + (wallclock - cia1todrel)
	 * VIRT:  TOD register + (cycles - begin)/cycles_per_sec
	 */
      case CIA_TOD_TEN:	/* Time Of Day clock 1/10 s */
      case CIA_TOD_HR:		/* Time Of Day clock hour */
      case CIA_TOD_SEC:	/* Time Of Day clock sec */
      case CIA_TOD_MIN:	/* Time Of Day clock min */
	/* Mask out undefined bits and flip AM/PM on hour 12
	   (Andreas Boose <boose@rzgw.rz.fh-hannover.de> 1997/10/11). */
	if (addr == CIA_TOD_HR)
	    byte = ((byte & 0x1f) == 18) ? (byte & 0x9f) ^ 0x80 : byte & 0x9f;
	if (cia1[CIA_CRB] & 0x80)
	    cia1todalarm[addr - CIA_TOD_TEN] = byte;
	else {
	    if (addr == CIA_TOD_TEN)
		cia1todstopped = 0;
	    if (addr == CIA_TOD_HR)
		cia1todstopped = 1;
	    cia1[addr] = byte;
	}
	check_cia1todalarm(rclk);
	break;

      case CIA_SDR:		/* Serial Port output buffer */
	cia1[addr] = byte;
	if ((cia1[CIA_CRA] & 0x40) == 0x40) {
	    if (cia1sr_bits <= 16) {
		if(!cia1sr_bits) {

#ifdef HAVE_RS232
    if (rsuser_enabled) {
	userport_serial_write_sr(cia1[CIA_SDR]);
    }
#endif
		}
		if(cia1sr_bits < 16) {
	            /* switch timer A alarm on again, if necessary */
	            update_cia1(rclk);
	            if (cia1_tau) {
		        my_set_tai_clk(cia1_tau + 1);
	            }
		}

	        cia1sr_bits += 16;

#if defined (CIA1_TIMER_DEBUG)
	        if (cia1_debugFlag)
	    	    printf("CIA1: start SDR rclk=%d\n", rclk);
#endif
  	    }
	}
	break;

	/* Interrupts */

      case CIA_ICR:		/* Interrupt Control Register */
	update_cia1(rclk);

#if defined (CIA1_TIMER_DEBUG)
	if (cia1_debugFlag)
	    printf("CIA1 set CIA_ICR: 0x%x\n", byte);
#endif

	if (byte & CIA_IM_SET) {
	    cia1ier |= (byte & 0x7f);
	} else {
	    cia1ier &= ~(byte & 0x7f);
	}

	/* This must actually be delayed one cycle! */
#if defined(CIA1_TIMER_DEBUG)
	if (cia1_debugFlag)
	    printf("    set icr: ifr & ier & 0x7f -> %02x, int=%02x\n",
		   cia1ier & cia1int & 0x7f, cia1int);
#endif
	if (cia1ier & cia1int & 0x7f) {
	    my_set_int(I_CIA1FL, IK_IRQ, rclk);
	}
	if (cia1ier & (CIA_IM_TA + CIA_IM_TB)) {
	    if ((cia1ier & CIA_IM_TA) && cia1_tau) {
		my_set_tai_clk(cia1_tau + 1);
	    }
	    if ((cia1ier & CIA_IM_TB) && cia1_tbu) {
		my_set_tbi_clk(cia1_tbu + 1);
	    }
	}
	/* Control */
	break;

      case CIA_CRA:		/* control register A */
	update_tai(rclk); /* schedule alarm in case latch value is changed */
	update_cia1(rclk);
#if defined (CIA1_TIMER_DEBUG)
	if (cia1_debugFlag)
	    printf("CIA1 set CIA_CRA: 0x%x (clk=%d, pc=, tal=%u, tac=%u)\n",
		   byte, rclk, /*program_counter,*/ cia1_tal, cia1_tac);
#endif

	/* bit 7 tod frequency */
	/* bit 6 serial port mode */

	/* bit 4 force load */
	if (byte & 0x10) {
	    cia1_tac = cia1_tal;
	    if (cia1_tas == CIAT_RUNNING) {
		cia1_tau = rclk + cia1_tac + 2;
		my_set_tai_clk(cia1_tau + 1);
	    }
	}
	/* bit 3 timer run mode */
	/* bit 2 & 1 timer output to PB6 */

	/* bit 0 start/stop timer */
	/* bit 5 timer count mode */
	if ((byte & 1) && !(cia1[CIA_CRA] & 1))
	    cia1_tat = 1;
	if ((byte ^ cia1[addr]) & 0x21) {
	    if ((byte & 0x21) == 0x01) {	/* timer just started */
		cia1_tas = CIAT_RUNNING;
		cia1_tau = rclk + (cia1_tac + 1) + ((byte & 0x10) >> 4);
		my_set_tai_clk(cia1_tau + 1);
	    } else {		/* timer just stopped */
		cia1_tas = CIAT_STOPPED;
		cia1_tau = 0;
		/* 1 cycle delay for counter stop. */
		if (!(byte & 0x10)) {
		    /* 1 cycle delay for counter stop.  This must only happen
                       if we are not forcing load at the same time (i.e. bit
                       4 in `byte' is zero). */
		    if (cia1_tac > 0)
			cia1_tac--;
		}
		my_unset_tai();
	    }
	}
#if defined (CIA1_TIMER_DEBUG)
	if (cia1_debugFlag)
	    printf("    -> tas=%d, tau=%d\n", cia1_tas, cia1_tau);
#endif
	cia1[addr] = byte & 0xef;	/* remove strobe */

	break;

      case CIA_CRB:		/* control register B */
	update_tbi(rclk); /* schedule alarm in case latch value is changed */
	update_cia1(rclk);

#if defined (CIA1_TIMER_DEBUG)
	if (cia1_debugFlag)
	    printf("CIA1 set CIA_CRB: 0x%x (clk=%d, pc=, tbl=%u, tbc=%u)\n",
		   byte, rclk, cia1_tbl, cia1_tbc);
#endif


	/* bit 7 set alarm/tod clock */
	/* bit 4 force load */
	if (byte & 0x10) {
	    cia1_tbc = cia1_tbl;
	    if (cia1_tbs == CIAT_RUNNING) {
		cia1_tbu = rclk + cia1_tbc + 2;
#if defined(CIA1_TIMER_DEBUG)
		if (cia1_debugFlag)
		    printf("CIA1: rclk=%d force load: set tbu alarm to %d\n", rclk, cia1_tbu);
#endif
		my_set_tbi_clk(cia1_tbu + 1);
	    }
	}
	/* bit 3 timer run mode */
	/* bit 2 & 1 timer output to PB6 */

	/* bit 0 stbrt/stop timer */
	/* bit 5 & 6 timer count mode */
	if ((byte & 1) && !(cia1[CIA_CRB] & 1))
	    cia1_tbt = 1;
	if ((byte ^ cia1[addr]) & 0x61) {
	    if ((byte & 0x61) == 0x01) {	/* timer just started */
		cia1_tbu = rclk + (cia1_tbc + 1) + ((byte & 0x10) >> 4);
#if defined(CIA1_TIMER_DEBUG)
		if (cia1_debugFlag)
		    printf("CIA1: rclk=%d start timer: set tbu alarm to %d\n", rclk, cia1_tbu);
#endif
		my_set_tbi_clk(cia1_tbu + 1);
		cia1_tbs = CIAT_RUNNING;
	    } else {		/* timer just stopped */
#if defined(CIA1_TIMER_DEBUG)
		if (cia1_debugFlag)
		    printf("CIA1: rclk=%d stop timer: set tbu alarm\n", rclk);
#endif
		my_unset_tbi();
		cia1_tbu = 0;
		if (!(byte & 0x10)) {
		    /* 1 cycle delay for counter stop.  This must only happen
                       if we are not forcing load at the same time (i.e. bit
                       4 in `byte' is zero). */
		    if (cia1_tbc > 0)
			cia1_tbc--;
		}
		/* this should actually read (byte & 0x61), but as CNT is high
		   by default, bit 0x20 is a `don't care' bit */
		if ((byte & 0x41) == 0x41) {
		    cia1_tbs = CIAT_COUNTTA;
		    update_cia1(rclk);
		    /* switch timer A alarm on if necessary */
		    if (cia1_tau) {
			my_set_tai_clk(cia1_tau + 1);
		    }
		} else {
		    cia1_tbs = CIAT_STOPPED;
		}
	    }
	}
	cia1[addr] = byte & 0xef;	/* remove strobe */
	break;

      default:
	cia1[addr] = byte;
    }				/* switch */
}


/* ------------------------------------------------------------------------- */

BYTE REGPARM1 read_cia1(ADDRESS addr)
{

#if defined( CIA1_TIMER_DEBUG )

    BYTE read_cia1_(ADDRESS addr);
    BYTE tmp = read_cia1_(addr);

    if (cia1_debugFlag)
	printf("read cia1[%x] returns %02x @ clk=%d, pc=\n",
	       addr, tmp, clk - READ_OFFSET);
    return tmp;
}

BYTE read_cia1_(ADDRESS addr)
{

#endif

    static BYTE byte;
    CLOCK rclk;

    addr &= 0xf;

    vic_ii_handle_pending_alarms(0);

    rclk = clk - READ_OFFSET;


    switch (addr) {

      case CIA_PRA:		/* port A */

    {
	BYTE val = cia1[CIA_PRA] | ~cia1[CIA_DDRA];
	BYTE msk = (cia1[CIA_PRB] | ~cia1[CIA_DDRB]) & ~joy[2];
	BYTE m;
	int i;

	for (m = 0x1, i = 0; i < 8; m <<= 1, i++)
	    if (!(msk & m))
		val &= ~rev_keyarr[i];
	byte = val & ~joy[2];
    }
	return byte;
	break;

      case CIA_PRB:		/* port B */

    {
	BYTE val = ~cia1[CIA_DDRB];
	BYTE msk = (cia1[CIA_PRA] | ~cia1[CIA_DDRA]) & ~joy[1];
	BYTE m;
	int i;

	for (m = 0x1, i = 0; i < 8; m <<= 1, i++)
	    if (!(msk & m))
		val &= ~keyarr[i];

	if (extended_keyboard_rows_enabled)
	    for (m = 0x1, i = 8; i < 11; m <<= 1, i++)
		if (!(extended_keyboard_rows_mask & m))
		    val &= ~keyarr[i];

	byte = (val | (cia1[CIA_PRB] & cia1[CIA_DDRB])) & ~joy[1];
    }
        if ((cia1[CIA_CRA] | cia1[CIA_CRB]) & 0x02) {
	    update_cia1(rclk);
	    if (cia1[CIA_CRA] & 0x02) {
		byte &= 0xbf;
		if (cia1_tap)
		    byte |= 0x40;
	    }
	    if (cia1[CIA_CRB] & 0x02) {
		byte &= 0x7f;
		if (cia1_tbp)
		    byte |= 0x80;
	    }
	}

	return byte;
	break;

	/* Timers */
      case CIA_TAL:		/* timer A low */
	update_cia1(rclk);
	return ((cia1_tac ? cia1_tac : cia1_tal) & 0xff);

      case CIA_TAH:		/* timer A high */
	update_cia1(rclk);
	return ((cia1_tac ? cia1_tac : cia1_tal) >> 8) & 0xff;

      case CIA_TBL:		/* timer B low */
	update_cia1(rclk);
	return cia1_tbc & 0xff;

      case CIA_TBH:		/* timer B high */
	update_cia1(rclk);
	return (cia1_tbc >> 8) & 0xff;

	/*
	 * TOD clock is latched by reading Hours, and released
	 * upon reading Tenths of Seconds. The counter itself
	 * keeps ticking all the time.
	 * Also note that this latching is different from the input one.
	 */
      case CIA_TOD_TEN:	/* Time Of Day clock 1/10 s */
      case CIA_TOD_SEC:	/* Time Of Day clock sec */
      case CIA_TOD_MIN:	/* Time Of Day clock min */
      case CIA_TOD_HR:		/* Time Of Day clock hour */
	if (!cia1todlatched)
	    memcpy(cia1todlatch, cia1 + CIA_TOD_TEN, sizeof(cia1todlatch));
	if (addr == CIA_TOD_TEN)
	    cia1todlatched = 0;
	if (addr == CIA_TOD_HR)
	    cia1todlatched = 1;
	return cia1[addr];

      case CIA_SDR:		/* Serial Port Shift Register */
#if 0				/*def DEBUG */
	cia1_dump(stdout);
	/* little hack .... */
	{
	    int i;
	    printf("\nmaincpu_ints:");
	    for (i = 0; i < NUMOFINT; i++) {
		printf(" %d", maincpu_int_status.pending_int[i]);
	    }
	    printf("\n");
	}
#endif
	return (cia1[addr]);

	/* Interrupts */

      case CIA_ICR:		/* Interrupt Flag Register */
	{
	    BYTE t = 0;

	    
#ifdef CIA1_TIMER_DEBUG
	    if (cia1_debugFlag)
		printf("CIA1 read intfl: rclk=%d, alarm_ta=%d, alarm_tb=%d\n",
			rclk, maincpu_int_status.alarm_clk[A_CIA1TA],
			maincpu_int_status.alarm_clk[A_CIA1TB]);
#endif
	
	    cia1rdi = rclk;
            t = cia1int;	/* we clean cia1int anyway, so make int_* */
	    cia1int = 0;	/* believe it is already */

            if (rclk >= cia1_tai)
                int_cia1ta(rclk - cia1_tai);
            if (rclk >= cia1_tbi)
                int_cia1tb(rclk - cia1_tbi);

	    cia1int |= t;	/* some bits can be set -> or with old value */

	    update_cia1(rclk);
	    t = cia1int | cia1flag;

#ifdef CIA1_TIMER_DEBUG
	    if (cia1_debugFlag)
		printf("CIA1 read intfl gives cia1int=%02x -> %02x @"
		       " PC=, sr_bits=%d, clk=%d, ta=%d, tb=%d\n",
		       cia1int, t, cia1sr_bits, clk, 
			(cia1_tac ? cia1_tac : cia1_tal),
			cia1_tbc);
#endif

	    cia1flag = 0;
	    cia1int = 0;
	    my_set_int(I_CIA1FL, 0, rclk);

	    return (t);
	}
    }				/* switch */

    return (cia1[addr]);
}

BYTE REGPARM1 peek_cia1(ADDRESS addr)
{
    /* This code assumes that update_cia1 is a projector - called at
     * the same cycle again it doesn't change anything. This way
     * it does not matter if we call it from peek first in the monitor
     * and probably the same cycle again when the CPU runs on...
     */
    CLOCK rclk;

    addr &= 0xf;

    vic_ii_handle_pending_alarms(0);

    rclk = clk - READ_OFFSET;

    switch (addr) {

	/*
	 * TOD clock is latched by reading Hours, and released
	 * upon reading Tenths of Seconds. The counter itself
	 * keeps ticking all the time.
	 * Also note that this latching is different from the input one.
	 */
      case CIA_TOD_TEN:	/* Time Of Day clock 1/10 s */
      case CIA_TOD_SEC:	/* Time Of Day clock sec */
      case CIA_TOD_MIN:	/* Time Of Day clock min */
      case CIA_TOD_HR:		/* Time Of Day clock hour */
	if (!cia1todlatched)
	    memcpy(cia1todlatch, cia1 + CIA_TOD_TEN, sizeof(cia1todlatch));
	return cia1[addr];

	/* Interrupts */

      case CIA_ICR:		/* Interrupt Flag Register */
	{
	    BYTE t = 0;

	    
#ifdef CIA1_TIMER_DEBUG
	    if (cia1_debugFlag)
		printf("CIA1 read intfl: rclk=%d, alarm_ta=%d, alarm_tb=%d\n",
			rclk, maincpu_int_status.alarm_clk[A_CIA1TA],
			maincpu_int_status.alarm_clk[A_CIA1TB]);
#endif
	
	    cia1rdi = rclk;
            t = cia1int;	/* we clean cia1int anyway, so make int_* */
	    cia1int = 0;	/* believe it is already */

            if (rclk >= cia1_tai)
                int_cia1ta(rclk - cia1_tai);
            if (rclk >= cia1_tbi)
                int_cia1tb(rclk - cia1_tbi);

	    cia1int |= t;	/* some bits can be set -> or with old value */

	    update_cia1(rclk);
	    t = cia1int | cia1flag;

#ifdef CIA1_TIMER_DEBUG
	    if (cia1_debugFlag)
		printf("CIA1 read intfl gives cia1int=%02x -> %02x @"
		       " PC=, sr_bits=%d, clk=%d, ta=%d, tb=%d\n",
		       cia1int, t, cia1sr_bits, clk, 
			(cia1_tac ? cia1_tac : cia1_tal),
			cia1_tbc);
#endif

/*
	    cia1flag = 0;
	    cia1int = 0;
	    my_set_int(I_CIA1FL, 0, rclk);
*/
	    return (t);
	}
      default:
	break;
    }				/* switch */

    return read_cia1(addr);
}

/* ------------------------------------------------------------------------- */

int int_cia1ta(long offset)
{
    CLOCK rclk = clk - offset;

#if defined(CIA1_TIMER_DEBUG)
    if (cia1_debugFlag)
	printf("CIA1: int_cia1ta(rclk = %u, tal = %u, cra=%02x\n",
	       rclk, cia1_tal, cia1[CIA_CRA]);
#endif

    cia1_tat = (cia1_tat + 1) & 1;

    if ((cia1_tas == CIAT_RUNNING) && !(cia1[CIA_CRA] & 8)) {
	/* if we do not need alarm, no PB6, no shift register, and not timer B
	   counting timer A, then we can savely skip alarms... */
	if ( ( (cia1ier & CIA_IM_TA) && 
		(!(cia1int & 0x80)) )
	    || (cia1[CIA_CRA] & 0x42)
	    || (cia1_tbs == CIAT_COUNTTA)) {
	    if(offset > cia1_tal+1) {
	        my_set_tai_clk(
			clk - (offset % (cia1_tal+1)) + cia1_tal + 1 );
	    } else {
	        my_set_tai_clk(rclk + cia1_tal + 1 );
	    }
	} else {
	    /* cia1_tai = rclk + cia1_tal +1; - now keeps tai */
	    /* printf("cia1 unset alarm: clk=%d, rclk=%d, rdi=%d -> tai=%d\n",
			clk, rclk, cia1rdi, cia1_tai); */
	    maincpu_unset_alarm(A_CIA1TA);	/* do _not_ clear cia1_tai */
	}
    } else {
#if 0
	cia1_tas = CIAT_STOPPED;
	cia1[CIA_CRA] &= 0xfe;	/* clear run flag. Correct? */
	cia1_tau = 0;
#endif
	my_unset_tai();
    }

    if (cia1[CIA_CRA] & 0x40) {
	if (cia1sr_bits) {
#if defined(CIA1_TIMER_DEBUG)
	    if (cia1_debugFlag)
		printf("CIA1: rclk=%d SDR: timer A underflow, bits=%d\n",
		       rclk, cia1sr_bits);
#endif
	    if (!(--cia1sr_bits)) {
		cia1int |= CIA_IM_SDR;
	    }
	    if(cia1sr_bits == 16) {

#ifdef HAVE_RS232
    if (rsuser_enabled) {
	userport_serial_write_sr(cia1[CIA_SDR]);
    }
#endif
	    }
	}
    }
    if (cia1_tbs == CIAT_COUNTTA) {
	if (!cia1_tbc) {
	    cia1_tbc = cia1_tbl;
	    cia1_tbu = rclk;
#if defined(CIA1_TIMER_DEBUG)
	    if (cia1_debugFlag)
		printf("CIA1: timer B underflow when counting timer A occured, rclk=%d!\n", rclk);
#endif
	    cia1int |= CIA_IM_TB;
	    my_set_tbi_clk(rclk);
	} else {
	    cia1_tbc--;
	}
    }

    /* CIA_IM_TA is not set here, as it can be set in update(), reset
       by reading the ICR and then set again here because of delayed
       calling of int() */
    if ((IK_IRQ == IK_NMI && cia1rdi != rclk - 1)
        || (IK_IRQ == IK_IRQ && cia1rdi < rclk - 1)) {
        if ((cia1int | CIA_IM_TA) & cia1ier & 0x7f) {
            my_set_int(I_CIA1FL, IK_IRQ, rclk);
        }
    }

    return 0;
}


/*
 * Timer B can run in 2 (4) modes
 * cia1[f] & 0x60 == 0x00   count system 02 pulses
 * cia1[f] & 0x60 == 0x40   count timer A underflows
 * cia1[f] & 0x60 == 0x20 | 0x60 count CNT pulses => counter stops
 */


int int_cia1tb(long offset)
{
    CLOCK rclk = clk - offset;

#if defined(CIA1_TIMER_DEBUG)
    if (cia1_debugFlag)
	printf("CIA1: timer B int_cia1tb(rclk=%d, tbs=%d)\n", rclk, cia1_tbs);
#endif

    cia1_tbt = (cia1_tbt + 1) & 1;

    /* running and continous, then next alarm */
    if (cia1_tbs == CIAT_RUNNING) {
	if (!(cia1[CIA_CRB] & 8)) {
#if defined(CIA1_TIMER_DEBUG)
	    if (cia1_debugFlag)
		printf("CIA1: rclk=%d cia1tb: set tbu alarm to %d\n", rclk, rclk + cia1_tbl + 1);
#endif
	    /* if no interrupt flag we can safely skip alarms */
	    if (cia1ier & CIA_IM_TB) {
		if(offset > cia1_tbl+1) {
		    my_set_tbi_clk( 
			clk - (offset % (cia1_tbl+1)) + cia1_tbl + 1);
		} else {
		    my_set_tbi_clk(rclk + cia1_tbl + 1);
		}
	    } else {
		/* cia1_tbi = rclk + cia1_tbl + 1; */
		maincpu_unset_alarm(A_CIA1TB);
	    }
	} else {
#if 0
	    cia1_tbs = CIAT_STOPPED;
	    cia1[CIA_CRB] &= 0xfe; /* clear start bit */
	    cia1_tbu = 0;
#endif /* 0 */
#if defined(CIA1_TIMER_DEBUG)
	    if (cia1_debugFlag)
		printf("CIA1: rclk=%d cia1tb: unset tbu alarm\n", rclk);
#endif
	    my_unset_tbi();
	}
    } else {
	if (cia1_tbs == CIAT_COUNTTA) {
	    if ((cia1[CIA_CRB] & 8)) {
		cia1_tbs = CIAT_STOPPED;
		cia1[CIA_CRB] &= 0xfe;		/* clear start bit */
		cia1_tbu = 0;
	    }
	}
	cia1_tbu = 0;
	my_unset_tbi();
#if defined(CIA1_TIMER_DEBUG)
	if (cia1_debugFlag)
	    printf("CIA1: rclk=%d cia1tb: unset tbu alarm\n", rclk);
#endif
    }

    if ((IK_IRQ == IK_NMI && cia1rdi != rclk - 1)
        || (IK_IRQ == IK_IRQ && cia1rdi < rclk - 1)) {
        if ((cia1int | CIA_IM_TB) & cia1ier & 0x7f) {
            my_set_int(I_CIA1FL, IK_IRQ, rclk);
        }
    }

    return 0;
}

/* ------------------------------------------------------------------------- */

void cia1_set_flag(void)
{
    cia1int |= CIA_IM_FLG;
    if (cia1[CIA_ICR] & CIA_IM_FLG) {
        my_set_int(I_CIA1FL, IK_IRQ, clk);
    }
}

void cia1_set_sdr(BYTE data)
{
    cia1[CIA_SDR] = data;
    cia1int |= CIA_IM_SDR;
    if (cia1[CIA_ICR] & CIA_IM_SDR) {
        my_set_int(I_CIA1FL, IK_IRQ, clk);
    }
}

/* ------------------------------------------------------------------------- */

int int_cia1tod(long offset)
{
    int t, pm;
    CLOCK rclk = clk - offset;

#ifdef DEBUG
    if (cia1_debugFlag)
	printf("CIA1: TOD timer event (1/10 sec tick), tod=%02x:%02x,%02x.%x\n",
	       cia1[CIA_TOD_HR], cia1[CIA_TOD_MIN], cia1[CIA_TOD_SEC],
	       cia1[CIA_TOD_TEN]);
#endif

    /* set up new int */
    maincpu_set_alarm(A_CIA1TOD, cia1todticks);

    if (!cia1todstopped) {
	/* inc timer */
	t = bcd2byte(cia1[CIA_TOD_TEN]);
	t++;
	cia1[CIA_TOD_TEN] = byte2bcd(t % 10);
	if (t >= 10) {
	    t = bcd2byte(cia1[CIA_TOD_SEC]);
	    t++;
	    cia1[CIA_TOD_SEC] = byte2bcd(t % 60);
	    if (t >= 60) {
		t = bcd2byte(cia1[CIA_TOD_MIN]);
		t++;
		cia1[CIA_TOD_MIN] = byte2bcd(t % 60);
		if (t >= 60) {
		    pm = cia1[CIA_TOD_HR] & 0x80;
		    t = bcd2byte(cia1[CIA_TOD_HR] & 0x1f);
		    if (!t)
			pm ^= 0x80;	/* toggle am/pm on 0:59->1:00 hr */
		    t++;
		    t = t % 12 | pm;
		    cia1[CIA_TOD_HR] = byte2bcd(t);
		}
	    }
	}
#ifdef DEBUG
	if (cia1_debugFlag)
	    printf("CIA1: TOD after event :tod=%02x:%02x,%02x.%x\n",
	       cia1[CIA_TOD_HR], cia1[CIA_TOD_MIN], cia1[CIA_TOD_SEC],
		   cia1[CIA_TOD_TEN]);
#endif
	/* check alarm */
	check_cia1todalarm(rclk);
    }
    return 0;
}

/* -------------------------------------------------------------------------- */


void cia1_prevent_clk_overflow(CLOCK sub)
{

    update_tai(clk);
    update_tbi(clk);

    update_cia1(clk);

    if(cia1_tai && (cia1_tai != -1))
        cia1_tai -= sub;
    if(cia1_tbi && (cia1_tbi != -1))
        cia1_tbi -= sub;

    if (cia1_tau)
	cia1_tau -= sub;
    if (cia1_tbu)
	cia1_tbu -= sub;
    if (cia1rdi > sub)
	cia1rdi -= sub;
    else
	cia1rdi = 0;
}

#if 0

void cia1_dump(FILE * fp)
{

    update_cia1(clk);
    fprintf(fp, "[CIA1]\n");
    fprintf(fp, "PA %d %d\n", cia1[CIA_PRA], cia1[CIA_DDRA]);
    fprintf(fp, "PB %d %d\n", cia1[CIA_PRB], cia1[CIA_DDRB]);
    fprintf(fp, "TA %u %u %d [$%02x = ", cia1_tac, cia1_tal,
	    cia1[CIA_CRA], cia1[CIA_CRA]);
    fprintf(fp, "%s%s%s%s%s]\n",
	    (cia1[CIA_CRA] & 1) ? "running " : "stopped ",
	    (cia1[CIA_CRA] & 8) ? "one-shot " : "continous ",
	    (cia1[CIA_CRA] & 16) ? "force-load " : "",
	    (cia1[CIA_CRA] & 32) ? "cnt " : "phi2 ",
	    (cia1[CIA_CRA] & 64) ? "sr_out " : "sr_in ");

    fprintf(fp, "TB %u %u %d [$%02x = ", cia1_tbc, cia1_tbl,
	    cia1[CIA_CRB], cia1[CIA_CRB]);
    fprintf(fp, "%s%s%s%s]\n",
	    (cia1[CIA_CRB] & 1) ? "running " : "stopped ",
	    (cia1[CIA_CRB] & 8) ? "one-shot " : "continous ",
	    (cia1[CIA_CRB] & 16) ? "force-load " : "",
	    (cia1[CIA_CRB] & 32)
	    ? ((cia1[CIA_CRB] & 64) ? "timerA+cnt" : "cnt ")
	    : ((cia1[CIA_CRB] & 64) ? "timerA" : "phi2 ")
	);

    fprintf(fp, "ICR %u %u %u", cia1int, cia1ier,
	    maincpu_int_status.pending_int[I_CIA1FL]);
    fprintf(fp, " [fl= %s%s%s%s%s] ",
	    (cia1int & 1) ? "TA " : "",
	    (cia1int & 2) ? "TB " : "",
	    (cia1int & 4) ? "Alarm " : "",
	    (cia1int & 8) ? "SDR " : "",
	    (cia1int & 16) ? "Flag " : "");
    fprintf(fp, "[mask= %s%s%s%s%s]\n",
	    (cia1ier & 1) ? "TA " : "",
	    (cia1ier & 2) ? "TB " : "",
	    (cia1ier & 4) ? "Alarm " : "",
	    (cia1ier & 8) ? "SDR " : "",
	    (cia1ier & 16) ? "Flag " : "");

    fprintf(fp, "SR %d %d\n", cia1[CIA_SDR], cia1sr_bits);
    fprintf(fp, "TOD %d %d %d %d\n", cia1[CIA_TOD_HR],
	    cia1[CIA_TOD_MIN], cia1[CIA_TOD_SEC], cia1[CIA_TOD_SEC]);
}


void cia1_undump_line(char *s)
{
    unsigned int d1, d2, d3, d4;

    if (s == strstr(s, "PA")) {
	sscanf(s + 2, "%u %u", &d1, &d2);
	cia1[CIA_PRA] = d1;
	cia1[CIA_DDRA] = d2;
	store_cia1(CIA_PRA, cia1[CIA_PRA]);
    } else if (s == strstr(s, "PB")) {
	sscanf(s + 2, "%u %u", &d1, &d2);
	cia1[CIA_PRB] = d1;
	cia1[CIA_DDRB] = d2;
	store_cia1(CIA_PRB, cia1[CIA_PRB]);
    } else if (s == strstr(s, "TA")) {
	sscanf(s + 2, "%u %u %u", &cia1_tac, &cia1_tal, &d1);
	cia1[CIA_CRA] = d1;
	if ((cia1[CIA_CRA] & 0x21) == 0x01) {
	    cia1_tau = clk + cia1_tac;
	    cia1_tas = CIAT_RUNNING;
	    my_set_tai_clk(cia1_tau + 1);
	} else {
	    cia1_tau = 0;
	    cia1_tas = CIAT_STOPPED;
	}
    } else if (s == strstr(s, "TB")) {
	sscanf(s + 2, "%u %u %u", &cia1_tbc, &cia1_tbl, &d1);
	cia1[CIA_CRB] = d1;
	if ((cia1[CIA_CRB] & 0x61) == 0x01) {
	    cia1_tbu = clk + cia1_tbc;
	    cia1_tbs = CIAT_RUNNING;
	    my_set_tbi_clk(cia1_tbu + 1);
	} else {
	    cia1_tbu = 0;
	    if ((cia1[CIA_CRB] & 0x61) == 0x41) {
		cia1_tbs = CIAT_COUNTTA;
	    } else {
		cia1_tbs = CIAT_STOPPED;
	    }
	}
    } else if (s == strstr(s, "ICR")) {
	sscanf(s + 3, "%d %d", &d1, &d2);
	cia1int = d1;
	cia1ier = d2;
	if (cia1int & cia1ier & 0x7f) {
	    my_set_int(I_CIA1FL, IK_IRQ, rclk);
	} else {
	    my_set_int(I_CIA1FL, 0, rclk);
	}
    } else if (s == strstr(s, "SR")) {
	sscanf(s + 2, "%d %d", &d1, &cia1sr_bits);
	cia1[CIA_SDR] = d1;
    } else if (s == strstr(s, "TOD")) {
	sscanf(s + 3, "%u %u %u %u", &d1, &d2, &d3, &d4);
	cia1[CIA_TOD_HR] = d1;
	cia1[CIA_TOD_MIN] = d2;
	cia1[CIA_TOD_SEC] = d3;
	cia1[CIA_TOD_TEN] = d4;
    } else {
	printf("unknown dump format line for CIA1: \n%s\n", s);
    }
}

#endif



These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.