shithub: minivmac

ref: d488fabe62aabd0d42421eb3e423a9308ae1fa13
dir: /src/VIA2EMDV.c/

View raw version
/*
	VIA2EMDV.c

	Copyright (C) 2008 Philip Cummins, Paul C. Pratt

	You can redistribute this file and/or modify it under the terms
	of version 2 of the GNU General Public License as published by
	the Free Software Foundation.  You should have received a copy
	of the license along with this file; see the file COPYING.

	This file 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
	license for more details.
*/

/*
	Versatile Interface Adapter EMulated DEVice

	Emulates the VIA found in the Mac Plus.

	This code adapted from vMac by Philip Cummins.
*/

#ifndef AllFiles
#include "SYSDEPNS.h"

#include "MYOSGLUE.h"
#include "EMCONFIG.h"
#include "GLOBGLUE.h"
#endif

#include "VIA2EMDV.h"

/*
	ReportAbnormalID unused 0x0510 - 0x05FF
*/

#ifdef VIA2_iA0_ChangeNtfy
IMPORTPROC VIA2_iA0_ChangeNtfy(void);
#endif

#ifdef VIA2_iA1_ChangeNtfy
IMPORTPROC VIA2_iA1_ChangeNtfy(void);
#endif

#ifdef VIA2_iA2_ChangeNtfy
IMPORTPROC VIA2_iA2_ChangeNtfy(void);
#endif

#ifdef VIA2_iA3_ChangeNtfy
IMPORTPROC VIA2_iA3_ChangeNtfy(void);
#endif

#ifdef VIA2_iA4_ChangeNtfy
IMPORTPROC VIA2_iA4_ChangeNtfy(void);
#endif

#ifdef VIA2_iA5_ChangeNtfy
IMPORTPROC VIA2_iA5_ChangeNtfy(void);
#endif

#ifdef VIA2_iA6_ChangeNtfy
IMPORTPROC VIA2_iA6_ChangeNtfy(void);
#endif

#ifdef VIA2_iA7_ChangeNtfy
IMPORTPROC VIA2_iA7_ChangeNtfy(void);
#endif

#ifdef VIA2_iB0_ChangeNtfy
IMPORTPROC VIA2_iB0_ChangeNtfy(void);
#endif

#ifdef VIA2_iB1_ChangeNtfy
IMPORTPROC VIA2_iB1_ChangeNtfy(void);
#endif

#ifdef VIA2_iB2_ChangeNtfy
IMPORTPROC VIA2_iB2_ChangeNtfy(void);
#endif

#ifdef VIA2_iB3_ChangeNtfy
IMPORTPROC VIA2_iB3_ChangeNtfy(void);
#endif

#ifdef VIA2_iB4_ChangeNtfy
IMPORTPROC VIA2_iB4_ChangeNtfy(void);
#endif

#ifdef VIA2_iB5_ChangeNtfy
IMPORTPROC VIA2_iB5_ChangeNtfy(void);
#endif

#ifdef VIA2_iB6_ChangeNtfy
IMPORTPROC VIA2_iB6_ChangeNtfy(void);
#endif

#ifdef VIA2_iB7_ChangeNtfy
IMPORTPROC VIA2_iB7_ChangeNtfy(void);
#endif

#ifdef VIA2_iCB2_ChangeNtfy
IMPORTPROC VIA2_iCB2_ChangeNtfy(void);
#endif

#define Ui3rPowOf2(p) (1 << (p))
#define Ui3rTestBit(i, p) (((i) & Ui3rPowOf2(p)) != 0)

#define VIA2_ORA_CanInOrOut (VIA2_ORA_CanIn | VIA2_ORA_CanOut)

#if ! Ui3rTestBit(VIA2_ORA_CanInOrOut, 7)
#ifdef VIA2_iA7
#error "VIA2_iA7 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORA_CanInOrOut, 6)
#ifdef VIA2_iA6
#error "VIA2_iA6 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORA_CanInOrOut, 5)
#ifdef VIA2_iA5
#error "VIA2_iA5 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORA_CanInOrOut, 4)
#ifdef VIA2_iA4
#error "VIA2_iA4 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORA_CanInOrOut, 3)
#ifdef VIA2_iA3
#error "VIA2_iA3 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORA_CanInOrOut, 2)
#ifdef VIA2_iA2
#error "VIA2_iA2 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORA_CanInOrOut, 1)
#ifdef VIA2_iA1
#error "VIA2_iA1 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORA_CanInOrOut, 0)
#ifdef VIA2_iA0
#error "VIA2_iA0 defined but not used"
#endif
#endif

#define VIA2_ORB_CanInOrOut (VIA2_ORB_CanIn | VIA2_ORB_CanOut)

#if ! Ui3rTestBit(VIA2_ORB_CanInOrOut, 7)
#ifdef VIA2_iB7
#error "VIA2_iB7 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORB_CanInOrOut, 6)
#ifdef VIA2_iB6
#error "VIA2_iB6 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORB_CanInOrOut, 5)
#ifdef VIA2_iB5
#error "VIA2_iB5 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORB_CanInOrOut, 4)
#ifdef VIA2_iB4
#error "VIA2_iB4 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORB_CanInOrOut, 3)
#ifdef VIA2_iB3
#error "VIA2_iB3 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORB_CanInOrOut, 2)
#ifdef VIA2_iB2
#error "VIA2_iB2 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORB_CanInOrOut, 1)
#ifdef VIA2_iB1
#error "VIA2_iB1 defined but not used"
#endif
#endif

#if ! Ui3rTestBit(VIA2_ORB_CanInOrOut, 0)
#ifdef VIA2_iB0
#error "VIA2_iB0 defined but not used"
#endif
#endif

typedef struct {
	ui5b T1C_F;  /* Timer 1 Counter Fixed Point */
	ui5b T2C_F;  /* Timer 2 Counter Fixed Point */
	ui3b ORB;    /* Buffer B */
	/* ui3b ORA_H;     Buffer A with Handshake */
	ui3b DDR_B;  /* Data Direction Register B */
	ui3b DDR_A;  /* Data Direction Register A */
	ui3b T1L_L;  /* Timer 1 Latch Low */
	ui3b T1L_H;  /* Timer 1 Latch High */
	ui3b T2L_L;  /* Timer 2 Latch Low */
	ui3b SR;     /* Shift Register */
	ui3b ACR;    /* Auxiliary Control Register */
	ui3b PCR;    /* Peripheral Control Register */
	ui3b IFR;    /* Interrupt Flag Register */
	ui3b IER;    /* Interrupt Enable Register */
	ui3b ORA;    /* Buffer A */
} VIA2_Ty;

LOCALVAR VIA2_Ty VIA2_D;

#define kIntCA2 0 /* One_Second */
#define kIntCA1 1 /* Vertical_Blanking */
#define kIntSR 2 /* Keyboard_Data_Ready */
#define kIntCB2 3 /* Keyboard_Data */
#define kIntCB1 4 /* Keyboard_Clock */
#define kIntT2 5 /* Timer_2 */
#define kIntT1 6 /* Timer_1 */

#define VIA2_dolog (dbglog_HAVE && 0)

/* VIA2_Get_ORA : VIA Get Port A Data */
/*
	This function queries VIA Port A interfaced hardware
	about their status
*/

LOCALFUNC ui3b VIA2_Get_ORA(ui3b Selection)
{
	ui3b Value = (~ VIA2_ORA_CanIn) & Selection & VIA2_ORA_FloatVal;

#if Ui3rTestBit(VIA2_ORA_CanIn, 7)
	if (Ui3rTestBit(Selection, 7)) {
		Value |= (VIA2_iA7 << 7);
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanIn, 6)
	if (Ui3rTestBit(Selection, 6)) {
		Value |= (VIA2_iA6 << 6);
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanIn, 5)
	if (Ui3rTestBit(Selection, 5)) {
		Value |= (VIA2_iA5 << 5);
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanIn, 4)
	if (Ui3rTestBit(Selection, 4)) {
		Value |= (VIA2_iA4 << 4);
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanIn, 3)
	if (Ui3rTestBit(Selection, 3)) {
		Value |= (VIA2_iA3 << 3);
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanIn, 2)
	if (Ui3rTestBit(Selection, 2)) {
		Value |= (VIA2_iA2 << 2);
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanIn, 1)
	if (Ui3rTestBit(Selection, 1)) {
		Value |= (VIA2_iA1 << 1);
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanIn, 0)
	if (Ui3rTestBit(Selection, 0)) {
		Value |= (VIA2_iA0 << 0);
	}
#endif

	return Value;
}

/* VIA2_Get_ORB : VIA Get Port B Data */
/*
	This function queries VIA Port B interfaced hardware
	about their status
*/

LOCALFUNC ui3b VIA2_Get_ORB(ui3b Selection)
{
	ui3b Value = (~ VIA2_ORB_CanIn) & Selection & VIA2_ORB_FloatVal;

#if Ui3rTestBit(VIA2_ORB_CanIn, 7)
	if (Ui3rTestBit(Selection, 7)) {
		Value |= (VIA2_iB7 << 7);
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanIn, 6)
	if (Ui3rTestBit(Selection, 6)) {
		Value |= (VIA2_iB6 << 6);
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanIn, 5)
	if (Ui3rTestBit(Selection, 5)) {
		Value |= (VIA2_iB5 << 5);
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanIn, 4)
	if (Ui3rTestBit(Selection, 4)) {
		Value |= (VIA2_iB4 << 4);
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanIn, 3)
	if (Ui3rTestBit(Selection, 3)) {
		Value |= (VIA2_iB3 << 3);
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanIn, 2)
	if (Ui3rTestBit(Selection, 2)) {
		Value |= (VIA2_iB2 << 2);
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanIn, 1)
	if (Ui3rTestBit(Selection, 1)) {
		Value |= (VIA2_iB1 << 1);
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanIn, 0)
	if (Ui3rTestBit(Selection, 0)) {
		Value |= (VIA2_iB0 << 0);
	}
#endif

	return Value;
}

#define ViaORcheckBit(p, x) \
	(Ui3rTestBit(Selection, p) && \
	((v = (Data >> p) & 1) != x))

LOCALPROC VIA2_Put_ORA(ui3b Selection, ui3b Data)
{
#if 0 != VIA2_ORA_CanOut
	ui3b v;
#endif

#if Ui3rTestBit(VIA2_ORA_CanOut, 7)
	if (ViaORcheckBit(7, VIA2_iA7)) {
		VIA2_iA7 = v;
#ifdef VIA2_iA7_ChangeNtfy
		VIA2_iA7_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanOut, 6)
	if (ViaORcheckBit(6, VIA2_iA6)) {
		VIA2_iA6 = v;
#ifdef VIA2_iA6_ChangeNtfy
		VIA2_iA6_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanOut, 5)
	if (ViaORcheckBit(5, VIA2_iA5)) {
		VIA2_iA5 = v;
#ifdef VIA2_iA5_ChangeNtfy
		VIA2_iA5_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanOut, 4)
	if (ViaORcheckBit(4, VIA2_iA4)) {
		VIA2_iA4 = v;
#ifdef VIA2_iA4_ChangeNtfy
		VIA2_iA4_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanOut, 3)
	if (ViaORcheckBit(3, VIA2_iA3)) {
		VIA2_iA3 = v;
#ifdef VIA2_iA3_ChangeNtfy
		VIA2_iA3_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanOut, 2)
	if (ViaORcheckBit(2, VIA2_iA2)) {
		VIA2_iA2 = v;
#ifdef VIA2_iA2_ChangeNtfy
		VIA2_iA2_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanOut, 1)
	if (ViaORcheckBit(1, VIA2_iA1)) {
		VIA2_iA1 = v;
#ifdef VIA2_iA1_ChangeNtfy
		VIA2_iA1_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORA_CanOut, 0)
	if (ViaORcheckBit(0, VIA2_iA0)) {
		VIA2_iA0 = v;
#ifdef VIA2_iA0_ChangeNtfy
		VIA2_iA0_ChangeNtfy();
#endif
	}
#endif
}

LOCALPROC VIA2_Put_ORB(ui3b Selection, ui3b Data)
{
#if 0 != VIA2_ORB_CanOut
	ui3b v;
#endif

#if Ui3rTestBit(VIA2_ORB_CanOut, 7)
	if (ViaORcheckBit(7, VIA2_iB7)) {
		VIA2_iB7 = v;
#ifdef VIA2_iB7_ChangeNtfy
		VIA2_iB7_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanOut, 6)
	if (ViaORcheckBit(6, VIA2_iB6)) {
		VIA2_iB6 = v;
#ifdef VIA2_iB6_ChangeNtfy
		VIA2_iB6_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanOut, 5)
	if (ViaORcheckBit(5, VIA2_iB5)) {
		VIA2_iB5 = v;
#ifdef VIA2_iB5_ChangeNtfy
		VIA2_iB5_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanOut, 4)
	if (ViaORcheckBit(4, VIA2_iB4)) {
		VIA2_iB4 = v;
#ifdef VIA2_iB4_ChangeNtfy
		VIA2_iB4_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanOut, 3)
	if (ViaORcheckBit(3, VIA2_iB3)) {
		VIA2_iB3 = v;
#ifdef VIA2_iB3_ChangeNtfy
		VIA2_iB3_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanOut, 2)
	if (ViaORcheckBit(2, VIA2_iB2)) {
		VIA2_iB2 = v;
#ifdef VIA2_iB2_ChangeNtfy
		VIA2_iB2_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanOut, 1)
	if (ViaORcheckBit(1, VIA2_iB1)) {
		VIA2_iB1 = v;
#ifdef VIA2_iB1_ChangeNtfy
		VIA2_iB1_ChangeNtfy();
#endif
	}
#endif

#if Ui3rTestBit(VIA2_ORB_CanOut, 0)
	if (ViaORcheckBit(0, VIA2_iB0)) {
		VIA2_iB0 = v;
#ifdef VIA2_iB0_ChangeNtfy
		VIA2_iB0_ChangeNtfy();
#endif
	}
#endif
}

LOCALPROC VIA2_SetDDR_A(ui3b Data)
{
	ui3b floatbits = VIA2_D.DDR_A & ~ Data;
	ui3b unfloatbits = Data & ~ VIA2_D.DDR_A;

	if (floatbits != 0) {
		VIA2_Put_ORA(floatbits, VIA2_ORA_FloatVal);
	}
	VIA2_D.DDR_A = Data;
	if (unfloatbits != 0) {
		VIA2_Put_ORA(unfloatbits, VIA2_D.ORA);
	}
	if ((Data & ~ VIA2_ORA_CanOut) != 0) {
		ReportAbnormalID(0x0501,
			"Set VIA2_D.DDR_A unexpected direction");
	}
}

LOCALPROC VIA2_SetDDR_B(ui3b Data)
{
	ui3b floatbits = VIA2_D.DDR_B & ~ Data;
	ui3b unfloatbits = Data & ~ VIA2_D.DDR_B;

	if (floatbits != 0) {
		VIA2_Put_ORB(floatbits, VIA2_ORB_FloatVal);
	}
	VIA2_D.DDR_B = Data;
	if (unfloatbits != 0) {
		VIA2_Put_ORB(unfloatbits, VIA2_D.ORB);
	}
	if ((Data & ~ VIA2_ORB_CanOut) != 0) {
		ReportAbnormalID(0x0502,
			"Set VIA2_D.DDR_B unexpected direction");
	}
}


LOCALPROC VIA2_CheckInterruptFlag(void)
{
	ui3b NewInterruptRequest =
		((VIA2_D.IFR & VIA2_D.IER) != 0) ? 1 : 0;

	if (NewInterruptRequest != VIA2_InterruptRequest) {
		VIA2_InterruptRequest = NewInterruptRequest;
#ifdef VIA2_interruptChngNtfy
		VIA2_interruptChngNtfy();
#endif
	}
}


LOCALVAR ui3b VIA2_T1_Active = 0;
LOCALVAR ui3b VIA2_T2_Active = 0;

LOCALVAR blnr VIA2_T1IntReady = falseblnr;

LOCALPROC VIA2_Clear(void)
{
	VIA2_D.ORA   = 0; VIA2_D.DDR_A = 0;
	VIA2_D.ORB   = 0; VIA2_D.DDR_B = 0;
	VIA2_D.T1L_L = VIA2_D.T1L_H = 0x00;
	VIA2_D.T2L_L = 0x00;
	VIA2_D.T1C_F = 0;
	VIA2_D.T2C_F = 0;
	VIA2_D.SR = VIA2_D.ACR = 0x00;
	VIA2_D.PCR   = VIA2_D.IFR   = VIA2_D.IER   = 0x00;
	VIA2_T1_Active = VIA2_T2_Active = 0x00;
	VIA2_T1IntReady = falseblnr;
}

GLOBALPROC VIA2_Zap(void)
{
	VIA2_Clear();
	VIA2_InterruptRequest = 0;
}

GLOBALPROC VIA2_Reset(void)
{
	VIA2_SetDDR_A(0);
	VIA2_SetDDR_B(0);

	VIA2_Clear();

	VIA2_CheckInterruptFlag();
}

LOCALPROC VIA2_SetInterruptFlag(ui3b VIA_Int)
{
	VIA2_D.IFR |= ((ui3b)1 << VIA_Int);
	VIA2_CheckInterruptFlag();
}

LOCALPROC VIA2_ClrInterruptFlag(ui3b VIA_Int)
{
	VIA2_D.IFR &= ~ ((ui3b)1 << VIA_Int);
	VIA2_CheckInterruptFlag();
}

#ifdef _VIA_Debug
#include <stdio.h>
#endif

GLOBALPROC VIA2_ShiftInData(ui3b v)
{
	/*
		external hardware generates 8 pulses on CB1,
		writes 8 bits to CB2
	*/
	ui3b ShiftMode = (VIA2_D.ACR & 0x1C) >> 2;

	if (ShiftMode != 3) {
#if ExtraAbnormalReports
		if (ShiftMode == 0) {
			/* happens on reset */
		} else {
			ReportAbnormalID(0x0503, "VIA Not ready to shift in");
				/*
					Observed (rarely) in Crystal Quest played
					at 1x speed in "-t mc64".
				*/
		}
#endif
	} else {
		VIA2_D.SR = v;
		VIA2_SetInterruptFlag(kIntSR);
		VIA2_SetInterruptFlag(kIntCB1);
	}
}

GLOBALFUNC ui3b VIA2_ShiftOutData(void)
{
	/*
		external hardware generates 8 pulses on CB1,
		reads 8 bits from CB2
	*/
	if (((VIA2_D.ACR & 0x1C) >> 2) != 7) {
		ReportAbnormalID(0x0504, "VIA Not ready to shift out");
		return 0;
	} else {
		VIA2_SetInterruptFlag(kIntSR);
		VIA2_SetInterruptFlag(kIntCB1);
		VIA2_iCB2 = (VIA2_D.SR & 1);
		return VIA2_D.SR;
	}
}

#define CyclesPerViaTime (10 * kMyClockMult)
#define CyclesScaledPerViaTime (kCycleScale * CyclesPerViaTime)

LOCALVAR blnr VIA2_T1Running = trueblnr;
LOCALVAR iCountt VIA2_T1LastTime = 0;

GLOBALPROC VIA2_DoTimer1Check(void)
{
	if (VIA2_T1Running) {
		iCountt NewTime = GetCuriCount();
		iCountt deltaTime = (NewTime - VIA2_T1LastTime);
		if (deltaTime != 0) {
			ui5b Temp = VIA2_D.T1C_F; /* Get Timer 1 Counter */
			ui5b deltaTemp =
				(deltaTime / CyclesPerViaTime) << (16 - kLn2CycleScale);
					/* may overflow */
			ui5b NewTemp = Temp - deltaTemp;
			if ((deltaTime > (0x00010000UL * CyclesScaledPerViaTime))
				|| ((Temp <= deltaTemp) && (Temp != 0)))
			{
				if ((VIA2_D.ACR & 0x40) != 0) { /* Free Running? */
					/* Reload Counter from Latches */
					ui4b v = (VIA2_D.T1L_H << 8) + VIA2_D.T1L_L;
					ui4b ntrans = 1 + ((v == 0) ? 0 :
						(((deltaTemp - Temp) / v) >> 16));
					NewTemp += (((ui5b)v * ntrans) << 16);
#if Ui3rTestBit(VIA2_ORB_CanOut, 7)
					if ((VIA2_D.ACR & 0x80) != 0) { /* invert ? */
						if ((ntrans & 1) != 0) {
							VIA2_iB7 ^= 1;
#ifdef VIA2_iB7_ChangeNtfy
							VIA2_iB7_ChangeNtfy();
#endif
						}
					}
#endif
					VIA2_SetInterruptFlag(kIntT1);
#if VIA2_dolog && 1
					dbglog_WriteNote("VIA2 Timer 1 Interrupt");
#endif
				} else {
					if (VIA2_T1_Active == 1) {
						VIA2_T1_Active = 0;
						VIA2_SetInterruptFlag(kIntT1);
#if VIA2_dolog && 1
						dbglog_WriteNote("VIA2 Timer 1 Interrupt");
#endif
					}
				}
			}

			VIA2_D.T1C_F = NewTemp;
			VIA2_T1LastTime = NewTime;
		}

		VIA2_T1IntReady = falseblnr;
		if ((VIA2_D.IFR & (1 << kIntT1)) == 0) {
			if (((VIA2_D.ACR & 0x40) != 0) || (VIA2_T1_Active == 1)) {
				ui5b NewTemp = VIA2_D.T1C_F; /* Get Timer 1 Counter */
				ui5b NewTimer;
#ifdef _VIA_Debug
				fprintf(stderr, "posting Timer1Check, %d, %d\n",
					Temp, GetCuriCount());
#endif
				if (NewTemp == 0) {
					NewTimer = (0x00010000UL * CyclesScaledPerViaTime);
				} else {
					NewTimer =
						(1 + (NewTemp >> (16 - kLn2CycleScale)))
							* CyclesPerViaTime;
				}
				ICT_add(kICT_VIA2_Timer1Check, NewTimer);
				VIA2_T1IntReady = trueblnr;
			}
		}
	}
}

LOCALPROC CheckT1IntReady(void)
{
	if (VIA2_T1Running) {
		blnr NewT1IntReady = falseblnr;

		if ((VIA2_D.IFR & (1 << kIntT1)) == 0) {
			if (((VIA2_D.ACR & 0x40) != 0) || (VIA2_T1_Active == 1)) {
				NewT1IntReady = trueblnr;
			}
		}

		if (VIA2_T1IntReady != NewT1IntReady) {
			VIA2_T1IntReady = NewT1IntReady;
			if (NewT1IntReady) {
				VIA2_DoTimer1Check();
			}
		}
	}
}

GLOBALFUNC ui4b VIA2_GetT1InvertTime(void)
{
	ui4b v;

	if ((VIA2_D.ACR & 0xC0) == 0xC0) {
		v = (VIA2_D.T1L_H << 8) + VIA2_D.T1L_L;
	} else {
		v = 0;
	}
	return v;
}

LOCALVAR blnr VIA2_T2Running = trueblnr;
LOCALVAR blnr VIA2_T2C_ShortTime = falseblnr;
LOCALVAR iCountt VIA2_T2LastTime = 0;

GLOBALPROC VIA2_DoTimer2Check(void)
{
	if (VIA2_T2Running) {
		iCountt NewTime = GetCuriCount();
		ui5b Temp = VIA2_D.T2C_F; /* Get Timer 2 Counter */
		iCountt deltaTime = (NewTime - VIA2_T2LastTime);
		ui5b deltaTemp = (deltaTime / CyclesPerViaTime)
			<< (16 - kLn2CycleScale); /* may overflow */
		ui5b NewTemp = Temp - deltaTemp;
		if (VIA2_T2_Active == 1) {
			if ((deltaTime > (0x00010000UL * CyclesScaledPerViaTime))
				|| ((Temp <= deltaTemp) && (Temp != 0)))
			{
				VIA2_T2C_ShortTime = falseblnr;
				VIA2_T2_Active = 0;
				VIA2_SetInterruptFlag(kIntT2);
#if VIA2_dolog && 1
				dbglog_WriteNote("VIA2 Timer 2 Interrupt");
#endif
			} else {
				ui5b NewTimer;
#ifdef _VIA_Debug
				fprintf(stderr, "posting Timer2Check, %d, %d\n",
					Temp, GetCuriCount());
#endif
				if (NewTemp == 0) {
					NewTimer = (0x00010000UL * CyclesScaledPerViaTime);
				} else {
					NewTimer = (1 + (NewTemp >> (16 - kLn2CycleScale)))
						* CyclesPerViaTime;
				}
				ICT_add(kICT_VIA2_Timer2Check, NewTimer);
			}
		}
		VIA2_D.T2C_F = NewTemp;
		VIA2_T2LastTime = NewTime;
	}
}

#define kORB    0x00
#define kORA_H  0x01
#define kDDR_B  0x02
#define kDDR_A  0x03
#define kT1C_L  0x04
#define kT1C_H  0x05
#define kT1L_L  0x06
#define kT1L_H  0x07
#define kT2_L   0x08
#define kT2_H   0x09
#define kSR     0x0A
#define kACR    0x0B
#define kPCR    0x0C
#define kIFR    0x0D
#define kIER    0x0E
#define kORA    0x0F

GLOBALFUNC ui5b VIA2_Access(ui5b Data, blnr WriteMem, CPTR addr)
{
	switch (addr) {
		case kORB   :
#if VIA2_CB2modesAllowed != 0x01
			if ((VIA2_D.PCR & 0xE0) == 0)
#endif
			{
				VIA2_ClrInterruptFlag(kIntCB2);
			}
			VIA2_ClrInterruptFlag(kIntCB1);
			if (WriteMem) {
				VIA2_D.ORB = Data;
				VIA2_Put_ORB(VIA2_D.DDR_B, VIA2_D.ORB);
			} else {
				Data = (VIA2_D.ORB & VIA2_D.DDR_B)
					| VIA2_Get_ORB(~ VIA2_D.DDR_B);
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kORB", Data, WriteMem);
#endif
			break;
		case kDDR_B :
			if (WriteMem) {
				VIA2_SetDDR_B(Data);
			} else {
				Data = VIA2_D.DDR_B;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kDDR_B", Data, WriteMem);
#endif
			break;
		case kDDR_A :
			if (WriteMem) {
				VIA2_SetDDR_A(Data);
			} else {
				Data = VIA2_D.DDR_A;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kDDR_A", Data, WriteMem);
#endif
			break;
		case kT1C_L :
			if (WriteMem) {
				VIA2_D.T1L_L = Data;
			} else {
				VIA2_ClrInterruptFlag(kIntT1);
				VIA2_DoTimer1Check();
				Data = (VIA2_D.T1C_F & 0x00FF0000) >> 16;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kT1C_L", Data, WriteMem);
#endif
			break;
		case kT1C_H :
			if (WriteMem) {
				VIA2_D.T1L_H = Data;
				VIA2_ClrInterruptFlag(kIntT1);
				VIA2_D.T1C_F = (Data << 24) + (VIA2_D.T1L_L << 16);
				if ((VIA2_D.ACR & 0x40) == 0) {
					VIA2_T1_Active = 1;
				}
				VIA2_T1LastTime = GetCuriCount();
				VIA2_DoTimer1Check();
			} else {
				VIA2_DoTimer1Check();
				Data = (VIA2_D.T1C_F & 0xFF000000) >> 24;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kT1C_H", Data, WriteMem);
#endif
			break;
		case kT1L_L :
			if (WriteMem) {
				VIA2_D.T1L_L = Data;
			} else {
				Data = VIA2_D.T1L_L;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kT1L_L", Data, WriteMem);
#endif
			break;
		case kT1L_H :
			if (WriteMem) {
				VIA2_D.T1L_H = Data;
			} else {
				Data = VIA2_D.T1L_H;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kT1L_H", Data, WriteMem);
#endif
			break;
		case kT2_L  :
			if (WriteMem) {
				VIA2_D.T2L_L = Data;
			} else {
				VIA2_ClrInterruptFlag(kIntT2);
				VIA2_DoTimer2Check();
				Data = (VIA2_D.T2C_F & 0x00FF0000) >> 16;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kT2_L", Data, WriteMem);
#endif
			break;
		case kT2_H  :
			if (WriteMem) {
				VIA2_D.T2C_F = (Data << 24) + (VIA2_D.T2L_L << 16);
				VIA2_ClrInterruptFlag(kIntT2);
				VIA2_T2_Active = 1;

				if ((VIA2_D.T2C_F < (128UL << 16))
					&& (VIA2_D.T2C_F != 0))
				{
					VIA2_T2C_ShortTime = trueblnr;
					VIA2_T2Running = trueblnr;
					/*
						Running too many instructions during
						a short timer interval can crash when
						playing sounds in System 7. So
						in this case don't let timer pause.
					*/
				}
				VIA2_T2LastTime = GetCuriCount();
				VIA2_DoTimer2Check();
			} else {
				VIA2_DoTimer2Check();
				Data = (VIA2_D.T2C_F & 0xFF000000) >> 24;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kT2_H", Data, WriteMem);
#endif
			break;
		case kSR:
#ifdef _VIA_Debug
			fprintf(stderr, "VIA2_D.SR: %d, %d, %d\n",
				WriteMem, ((VIA2_D.ACR & 0x1C) >> 2), Data);
#endif
			if (WriteMem) {
				VIA2_D.SR = Data;
			}
			VIA2_ClrInterruptFlag(kIntSR);
			switch ((VIA2_D.ACR & 0x1C) >> 2) {
				case 3 : /* Shifting In */
					break;
				case 6 : /* shift out under o2 clock */
					if ((! WriteMem) || (VIA2_D.SR != 0)) {
						ReportAbnormalID(0x0505,
							"VIA shift mode 6, non zero");
					} else {
#ifdef _VIA_Debug
						fprintf(stderr, "posting Foo2Task\n");
#endif
						if (VIA2_iCB2 != 0) {
							VIA2_iCB2 = 0;
#ifdef VIA2_iCB2_ChangeNtfy
							VIA2_iCB2_ChangeNtfy();
#endif
						}
					}
#if 0 /* possibly should do this. seems not to affect anything. */
					VIA2_SetInterruptFlag(kIntSR); /* don't wait */
#endif
					break;
				case 7 : /* Shifting Out */
					break;
			}
			if (! WriteMem) {
				Data = VIA2_D.SR;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kSR", Data, WriteMem);
#endif
			break;
		case kACR:
			if (WriteMem) {
#if 1
				if ((VIA2_D.ACR & 0x10) != ((ui3b)Data & 0x10)) {
					/* shift direction has changed */
					if ((Data & 0x10) == 0) {
						/*
							no longer an output,
							set data to float value
						*/
						if (VIA2_iCB2 == 0) {
							VIA2_iCB2 = 1;
#ifdef VIA2_iCB2_ChangeNtfy
							VIA2_iCB2_ChangeNtfy();
#endif
						}
					}
				}
#endif
				VIA2_D.ACR = Data;
				if ((VIA2_D.ACR & 0x20) != 0) {
					/* Not pulse counting? */
					ReportAbnormalID(0x0506,
						"Set VIA2_D.ACR T2 Timer pulse counting");
				}
				switch ((VIA2_D.ACR & 0xC0) >> 6) {
					/* case 1: happens in early System 6 */
					case 2:
						ReportAbnormalID(0x0507,
							"Set VIA2_D.ACR T1 Timer mode 2");
						break;
				}
				CheckT1IntReady();
				switch ((VIA2_D.ACR & 0x1C) >> 2) {
					case 0: /* this isn't sufficient */
						VIA2_ClrInterruptFlag(kIntSR);
						break;
					case 1:
					case 2:
					case 4:
					case 5:
						ReportAbnormalID(0x0508,
							"Set VIA2_D.ACR shift mode 1,2,4,5");
						break;
					default:
						break;
				}
				if ((VIA2_D.ACR & 0x03) != 0) {
					ReportAbnormalID(0x0509,
						"Set VIA2_D.ACR T2 Timer latching enabled");
				}
			} else {
				Data = VIA2_D.ACR;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kACR", Data, WriteMem);
#endif
			break;
		case kPCR:
			if (WriteMem) {
				VIA2_D.PCR = Data;
#define Ui3rSetContains(s, i) (((s) & (1 << (i))) != 0)
				if (! Ui3rSetContains(VIA2_CB2modesAllowed,
					(VIA2_D.PCR >> 5) & 0x07))
				{
					ReportAbnormalID(0x050A,
						"Set VIA2_D.PCR CB2 Control mode?");
				}
				if ((VIA2_D.PCR & 0x10) != 0) {
					ReportAbnormalID(0x050B,
						"Set VIA2_D.PCR CB1 INTERRUPT CONTROL?");
				}
				if (! Ui3rSetContains(VIA2_CA2modesAllowed,
					(VIA2_D.PCR >> 1) & 0x07))
				{
					ReportAbnormalID(0x050C,
						"Set VIA2_D.PCR CA2 INTERRUPT CONTROL?");
				}
				if ((VIA2_D.PCR & 0x01) != 0) {
					ReportAbnormalID(0x050D,
						"Set VIA2_D.PCR CA1 INTERRUPT CONTROL?");
				}
			} else {
				Data = VIA2_D.PCR;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kPCR", Data, WriteMem);
#endif
			break;
		case kIFR:
			if (WriteMem) {
				VIA2_D.IFR = VIA2_D.IFR & ((~ Data) & 0x7F);
					/* Clear Flag Bits */
				VIA2_CheckInterruptFlag();
				CheckT1IntReady();
			} else {
				Data = VIA2_D.IFR;
				if ((VIA2_D.IFR & VIA2_D.IER) != 0) {
					Data |= 0x80;
				}
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kIFR", Data, WriteMem);
#endif
			break;
		case kIER   :
			if (WriteMem) {
				if ((Data & 0x80) == 0) {
					VIA2_D.IER = VIA2_D.IER & ((~ Data) & 0x7F);
						/* Clear Enable Bits */
#if 0 != VIA2_IER_Never0
					/*
						of course, will be 0 initially,
						this just checks not cleared later.
					*/
					if ((Data & VIA2_IER_Never0) != 0) {
						ReportAbnormalID(0x050E, "IER Never0 clr");
					}
#endif
				} else {
					VIA2_D.IER = VIA2_D.IER | (Data & 0x7F);
						/* Set Enable Bits */
#if 0 != VIA2_IER_Never1
					if ((VIA2_D.IER & VIA2_IER_Never1) != 0) {
						ReportAbnormalID(0x050F, "IER Never1 set");
					}
#endif
				}
				VIA2_CheckInterruptFlag();
			} else {
				Data = VIA2_D.IER | 0x80;
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kIER", Data, WriteMem);
#endif
			break;
		case kORA   :
		case kORA_H :
			if ((VIA2_D.PCR & 0xE) == 0) {
				VIA2_ClrInterruptFlag(kIntCA2);
			}
			VIA2_ClrInterruptFlag(kIntCA1);
			if (WriteMem) {
				VIA2_D.ORA = Data;
				VIA2_Put_ORA(VIA2_D.DDR_A, VIA2_D.ORA);
			} else {
				Data = (VIA2_D.ORA & VIA2_D.DDR_A)
					| VIA2_Get_ORA(~ VIA2_D.DDR_A);
			}
#if VIA2_dolog && 1
			dbglog_Access("VIA2_Access kORA", Data, WriteMem);
#endif
			break;
	}
	return Data;
}

GLOBALPROC VIA2_ExtraTimeBegin(void)
{
	if (VIA2_T1Running) {
		VIA2_DoTimer1Check(); /* run up to this moment */
		VIA2_T1Running = falseblnr;
	}
	if (VIA2_T2Running & (! VIA2_T2C_ShortTime)) {
		VIA2_DoTimer2Check(); /* run up to this moment */
		VIA2_T2Running = falseblnr;
	}
}

GLOBALPROC VIA2_ExtraTimeEnd(void)
{
	if (! VIA2_T1Running) {
		VIA2_T1Running = trueblnr;
		VIA2_T1LastTime = GetCuriCount();
		VIA2_DoTimer1Check();
	}
	if (! VIA2_T2Running) {
		VIA2_T2Running = trueblnr;
		VIA2_T2LastTime = GetCuriCount();
		VIA2_DoTimer2Check();
	}
}

/* VIA Interrupt Interface */

#ifdef VIA2_iCA1_PulseNtfy
GLOBALPROC VIA2_iCA1_PulseNtfy(void)
{
	VIA2_SetInterruptFlag(kIntCA1);
}
#endif

#ifdef VIA2_iCA2_PulseNtfy
GLOBALPROC VIA2_iCA2_PulseNtfy(void)
{
	VIA2_SetInterruptFlag(kIntCA2);
}
#endif

#ifdef VIA2_iCB1_PulseNtfy
GLOBALPROC VIA2_iCB1_PulseNtfy(void)
{
	VIA2_SetInterruptFlag(kIntCB1);
}
#endif

#ifdef VIA2_iCB2_PulseNtfy
GLOBALPROC VIA2_iCB2_PulseNtfy(void)
{
	VIA2_SetInterruptFlag(kIntCB2);
}
#endif