AppleWin/source/6522.h
TomCh 910313f176
Fix edge-case for 6522 Timer write (#1333, PR #1334)
Refactor GetOpcodeCyclesForRead()/Write() to make then consistent & consolidate common code.
2024-10-15 21:26:38 +01:00

162 lines
4.5 KiB
C++

#pragma once
class SY6522
{
public:
SY6522(UINT slot, bool isMegaAudio) : m_slot(slot), m_isMegaAudio(isMegaAudio), m_isBusDriven(false), m_bad6522(false)
{
for (UINT i = 0; i < kNumTimersPer6522; i++)
m_syncEvent[i] = NULL;
Reset(true);
}
~SY6522(void)
{
}
void InitSyncEvents(class SyncEvent* event0, class SyncEvent* event1)
{
m_syncEvent[0] = event0;
m_syncEvent[1] = event1;
}
void InitBadState(bool bad6522)
{
m_bad6522 = bad6522;
}
void Reset(const bool powerCycle);
void StartTimer1(void);
void StopTimer1(void);
bool IsTimer1Active(void) { return m_timer1Active; }
void StopTimer2(void);
bool IsTimer2Active(void) { return m_timer2Active; }
void UpdateIFR(BYTE clr_ifr, BYTE set_ifr = 0);
void UpdateTimer1(USHORT clocks);
void UpdateTimer2(USHORT clocks);
enum { rORB = 0, rORA, rDDRB, rDDRA, rT1CL, rT1CH, rT1LL, rT1LH, rT2CL, rT2CH, rSR, rACR, rPCR, rIFR, rIER, rORA_NO_HS, SIZE_6522_REGS };
BYTE GetReg(BYTE reg)
{
switch (reg)
{
case rDDRA: return m_regs.DDRA;
case rORA: return m_regs.ORA;
case rACR: return m_regs.ACR;
case rPCR: return m_regs.PCR;
case rIFR: return m_regs.IFR;
}
_ASSERT(0);
return 0;
}
BYTE GetBusViewOfORB(void) { return m_regs.ORB & m_regs.DDRB; } // Return how the AY8913 sees ORB on the bus (ie. not CPU's view which will be OR'd with !DDRB)
USHORT GetRegT1C(void) { return m_regs.TIMER1_COUNTER.w; }
USHORT GetRegT2C(void) { return m_regs.TIMER2_COUNTER.w; }
void GetRegs(BYTE regs[SIZE_6522_REGS]) { memcpy(&regs[0], (BYTE*)&m_regs, SIZE_6522_REGS); } // For debugger
void SetRegIRA(BYTE reg) { m_regs.ORA = reg; }
bool IsTimer1IrqDelay(void) { return m_timer1IrqDelay ? true : false; }
void SetBusBeingDriven(bool state) { m_isBusDriven = state; }
bool IsBad(void) { return m_bad6522; }
BYTE Read(BYTE nReg);
void Write(BYTE nReg, BYTE nValue);
void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
void LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT version);
void SetTimersActiveFromSnapshot(bool timer1Active, bool timer2Active, UINT version);
// ACR
static const BYTE ACR_RUNMODE = 1 << 6;
static const BYTE ACR_RM_ONESHOT = 0 << 6;
static const BYTE ACR_RM_FREERUNNING = 1 << 6;
// IFR & IER:
static const BYTE IxR_SSI263 = 1 << 1;
static const BYTE IxR_VOTRAX = 1 << 4;
static const BYTE IxR_TIMER2 = 1 << 5;
static const BYTE IxR_TIMER1 = 1 << 6;
static const BYTE IFR_IRQ = 1 << 7;
static const UINT kExtraTimerCycles = 2; // Rockwell, Fig.16: period = N+2 cycles
static const UINT kNumTimersPer6522 = 2;
private:
USHORT SetTimerSyncEvent(BYTE reg, USHORT timerLatch);
USHORT GetTimer1Counter(BYTE reg);
USHORT GetTimer2Counter(BYTE reg);
bool IsTimer1Underflowed(BYTE reg);
bool IsTimer2Underflowed(BYTE reg);
bool CheckTimerUnderflow(USHORT& counter, int& timerIrqDelay, const USHORT clocks);
int OnTimer1Underflow(USHORT& counter);
UINT GetOpcodeCyclesForRead(BYTE reg);
UINT GetOpcodeCyclesForWrite(BYTE reg);
UINT GetOpcodeCycles(BYTE reg, UINT zpOpcodeCycles, UINT opcodeCycles, BYTE zpOpcode, BYTE opcode, bool abs16x, bool abs16y, bool indx, bool indy);
void StartTimer2(void);
void StartTimer1_LoadStateV1(void);
#pragma pack(push)
#pragma pack(1) // Ensure 'struct Regs' is packed so that GetRegs() can just do a memcpy()
struct IWORD
{
union
{
struct
{
BYTE l;
BYTE h;
};
USHORT w;
};
};
struct Regs
{
BYTE ORB; // $00 - Port B
BYTE ORA; // $01 - Port A (with handshaking)
BYTE DDRB; // $02 - Data Direction Register B
BYTE DDRA; // $03 - Data Direction Register A
IWORD TIMER1_COUNTER; // $04 - Read counter (L) / Write latch (L)
// $05 - Read / Write & initiate count (H)
IWORD TIMER1_LATCH; // $06 - Read / Write & latch (L)
// $07 - Read / Write & latch (H)
IWORD TIMER2_COUNTER; // $08 - Read counter (L) / Write latch (L)
// $09 - Read counter (H) / Write latch (H)
BYTE SERIAL_SHIFT; // $0A
BYTE ACR; // $0B - Auxiliary Control Register
BYTE PCR; // $0C - Peripheral Control Register
BYTE IFR; // $0D - Interrupt Flag Register
BYTE IER; // $0E - Interrupt Enable Register
BYTE ORA_NO_HS; // $0F - Port A (without handshaking)
//
IWORD TIMER2_LATCH; // Doesn't exist in 6522
};
#pragma pack(pop)
Regs m_regs;
int m_timer1IrqDelay;
int m_timer2IrqDelay;
bool m_timer1Active;
bool m_timer2Active;
class SyncEvent* m_syncEvent[kNumTimersPer6522];
UINT m_slot;
bool m_isMegaAudio;
bool m_isBusDriven;
static const UINT kExtraMegaAudioTimerCycles = kExtraTimerCycles + 1;
// For mb-audit
bool m_bad6522;
};