535 lines
11 KiB
C++
535 lines
11 KiB
C++
/******************************************************************************/
|
|
/* Mednafen Sega Saturn Emulation Module */
|
|
/******************************************************************************/
|
|
/* sh7095.h:
|
|
** Copyright (C) 2015-2016 Mednafen Team
|
|
**
|
|
** 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.,
|
|
** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifndef __MDFN_SH7095_H
|
|
#define __MDFN_SH7095_H
|
|
|
|
class SH7095 final
|
|
{
|
|
public:
|
|
|
|
SH7095(const char* const name_arg, const unsigned dma_event_id_arg, uint8 (*exivecfn_arg)(void)) MDFN_COLD;
|
|
~SH7095() MDFN_COLD;
|
|
|
|
void Init(void) MDFN_COLD;
|
|
|
|
void ForceInternalEventUpdates(void);
|
|
void AdjustTS(int32 delta, bool force_set = false);
|
|
|
|
void TruePowerOn(void) MDFN_COLD;
|
|
void Reset(bool power_on_reset, bool from_internal_wdt = false) MDFN_COLD;
|
|
void SetNMI(bool level);
|
|
void SetIRL(unsigned level);
|
|
void SetMD5(bool level);
|
|
|
|
void SetFTI(bool state);
|
|
void SetFTCI(bool state);
|
|
|
|
INLINE void SetExtHalt(bool state)
|
|
{
|
|
ExtHalt = state;
|
|
|
|
if(ExtHalt)
|
|
SetPEX(PEX_PSEUDO_EXTHALT); // Only SetPEX() here, ClearPEX() is called in the pseudo exception handling code as necessary.
|
|
}
|
|
|
|
template<unsigned which, bool DebugMode>
|
|
void Step(void);
|
|
|
|
|
|
//private:
|
|
uint32 R[16];
|
|
uint32 PC;
|
|
|
|
// Control registers
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
uint32 SR;
|
|
uint32 GBR;
|
|
uint32 VBR;
|
|
};
|
|
uint32 CtrlRegs[3];
|
|
};
|
|
|
|
sscpu_timestamp_t timestamp;
|
|
sscpu_timestamp_t MA_until;
|
|
sscpu_timestamp_t MM_until;
|
|
sscpu_timestamp_t write_finish_timestamp;
|
|
#if 0
|
|
sscpu_timestamp_t WB_until[16];
|
|
#endif
|
|
|
|
INLINE void SetT(bool new_value) { SR &= ~1; SR |= new_value; }
|
|
INLINE bool GetT(void) { return SR & 1; }
|
|
|
|
INLINE bool GetS(void) { return (bool)(SR & 0x002); }
|
|
INLINE bool GetQ(void) { return (bool)(SR & 0x100); }
|
|
INLINE bool GetM(void) { return (bool)(SR & 0x200); }
|
|
INLINE void SetQ(bool new_q) { SR = (SR &~ 0x100) | (new_q << 8); }
|
|
INLINE void SetM(bool new_m) { SR = (SR &~ 0x200) | (new_m << 9); }
|
|
|
|
// System registers
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
uint32 MACH;
|
|
uint32 MACL;
|
|
uint32 PR;
|
|
};
|
|
uint32 SysRegs[3];
|
|
};
|
|
|
|
INLINE uint64 GetMAC64(void) { return MACL | ((uint64)MACH << 32); }
|
|
INLINE void SetMAC64(uint64 nv) { MACL = nv; MACH = nv >> 32; }
|
|
|
|
enum // must be in range of 0 ... 7
|
|
{
|
|
PEX_POWERON = 0,
|
|
PEX_RESET = 1,
|
|
PEX_CPUADDR = 2,
|
|
PEX_DMAADDR = 3,
|
|
PEX_INT = 4,
|
|
PEX_NMI = 5,
|
|
PEX_PSEUDO_DMABURST = 6,
|
|
PEX_PSEUDO_EXTHALT = 7
|
|
};
|
|
enum { EPENDING_PEXBITS_SHIFT = 16 };
|
|
enum { EPENDING_OP_OR = 0xFF000000 };
|
|
|
|
uint32 EPending;
|
|
|
|
INLINE void SetPEX(const unsigned which)
|
|
{
|
|
EPending |= (1U << (which + EPENDING_PEXBITS_SHIFT));
|
|
EPending |= EPENDING_OP_OR;
|
|
}
|
|
|
|
INLINE void ClearPEX(const unsigned which)
|
|
{
|
|
EPending &= ~(1U << (which + EPENDING_PEXBITS_SHIFT));
|
|
|
|
if(!(EPending & (0xFF << EPENDING_PEXBITS_SHIFT)))
|
|
EPending = 0;
|
|
}
|
|
|
|
uint32 Pipe_ID;
|
|
uint32 Pipe_IF;
|
|
|
|
enum
|
|
{
|
|
EXCEPTION_POWERON = 0,// Power-on
|
|
EXCEPTION_RESET, // "Manual" reset
|
|
EXCEPTION_ILLINSTR, // General illegal instruction
|
|
EXCEPTION_ILLSLOT, // Slot illegal instruction
|
|
EXCEPTION_CPUADDR, // CPU address error
|
|
EXCEPTION_DMAADDR, // DMA Address error
|
|
EXCEPTION_NMI, // NMI
|
|
EXCEPTION_BREAK, // User break
|
|
EXCEPTION_TRAP, // Trap instruction
|
|
EXCEPTION_INT, // Interrupt
|
|
};
|
|
|
|
enum
|
|
{
|
|
VECNUM_POWERON = 0, // Power-on
|
|
VECNUM_RESET = 2, // "Manual" reset
|
|
VECNUM_ILLINSTR = 4, // General illegal instruction
|
|
VECNUM_ILLSLOT = 6, // Slot illegal instruction
|
|
VECNUM_CPUADDR = 9, // CPU address error
|
|
VECNUM_DMAADDR = 10, // DMA Address error
|
|
VECNUM_NMI = 11, // NMI
|
|
VECNUM_BREAK = 12, // User break
|
|
|
|
VECNUM_TRAP_BASE = 32, // Trap instruction
|
|
VECNUM_INT_BASE = 64, // Interrupt
|
|
};
|
|
|
|
enum
|
|
{
|
|
EPENDING_IVECNUM_SHIFT = 8, // 8 bits
|
|
EPENDING_E_SHIFT = 16, // 8 bits
|
|
EPENDING_IPRIOLEV_SHIFT = 28 // 4 bits
|
|
};
|
|
|
|
template<bool DebugMode>
|
|
uint32 Exception(const unsigned exnum, const unsigned vecnum);
|
|
|
|
//
|
|
//
|
|
//
|
|
uint32 IBuffer;
|
|
|
|
uint32 (MDFN_FASTCALL *MRFPI[8])(uint32 A);
|
|
|
|
uint8 (MDFN_FASTCALL *MRFP8[8])(uint32 A);
|
|
uint16 (MDFN_FASTCALL *MRFP16[8])(uint32 A);
|
|
uint32 (MDFN_FASTCALL *MRFP32[8])(uint32 A);
|
|
|
|
void (MDFN_FASTCALL *MWFP8[8])(uint32 A, uint8);
|
|
void (MDFN_FASTCALL *MWFP16[8])(uint32 A, uint16);
|
|
void (MDFN_FASTCALL *MWFP32[8])(uint32 A, uint32);
|
|
|
|
//
|
|
//
|
|
// Cache:
|
|
//
|
|
//
|
|
struct
|
|
{
|
|
// Rather than have separate validity bits, we're putting an INvalidity bit(invalid when =1)
|
|
// in the upper bit of the Tag variables.
|
|
uint32 Tag[4];
|
|
uint8 LRU;
|
|
alignas(4) uint8 Data[4][16];
|
|
} Cache[64];
|
|
|
|
uint8 CCR;
|
|
|
|
void SetCCR(uint8 V);
|
|
enum { CCR_CE = 0x01 }; // Cache Enable
|
|
enum { CCR_ID = 0x02 }; // Instruction Replacement Disable
|
|
enum { CCR_OD = 0x04 }; // Data Replacement Disable
|
|
enum { CCR_TW = 0x08 }; // Two-Way Mode
|
|
enum { CCR_CP = 0x10 }; // Cache Purge
|
|
enum { CCR_W0 = 0x40 }; //
|
|
enum { CCR_W1 = 0x80 }; //
|
|
void AssocPurge(const uint32 A);
|
|
|
|
template<typename T>
|
|
void Write_UpdateCache(uint32 A, T V);
|
|
//
|
|
// End cache stuff
|
|
//
|
|
|
|
//
|
|
//
|
|
// Interrupt controller registers and related state
|
|
//
|
|
//
|
|
void INTC_Reset(void) MDFN_COLD;
|
|
|
|
bool NMILevel;
|
|
uint8 IRL;
|
|
|
|
uint16 IPRA;
|
|
uint16 IPRB;
|
|
uint16 VCRWDT;
|
|
uint16 VCRA;
|
|
uint16 VCRB;
|
|
uint16 VCRC;
|
|
uint16 VCRD;
|
|
uint16 ICR;
|
|
|
|
//
|
|
//
|
|
//
|
|
uint16 BCR1, BCR1M;
|
|
|
|
//
|
|
//
|
|
//
|
|
uint8 SBYCR;
|
|
bool Standby;
|
|
|
|
//
|
|
//
|
|
// Free-running timer registers and related state
|
|
//
|
|
//
|
|
struct
|
|
{
|
|
sscpu_timestamp_t lastts; // Internal timestamp related.
|
|
|
|
bool FTI;
|
|
bool FTCI;
|
|
|
|
uint16 FRC;
|
|
uint16 OCR[2];
|
|
uint16 FICR;
|
|
uint8 TIER;
|
|
uint8 FTCSR;
|
|
uint8 FTCSRM; // Bits set to 1 like FTCSR, but unconditionally reset all bits to 0 on FTCSR read.
|
|
uint8 TCR;
|
|
uint8 TOCR;
|
|
uint8 RW_Temp;
|
|
} FRT;
|
|
|
|
void FRT_Reset(void) MDFN_COLD;
|
|
|
|
void FRT_CheckOCR(void);
|
|
void FRT_ClockFRC(void);
|
|
|
|
void FRT_WDT_Update(void);
|
|
void FRT_WDT_Recalc_NET(void);
|
|
uint32 FRT_WDT_ClockDivider;
|
|
sscpu_timestamp_t FRT_WDT_NextTS;
|
|
|
|
//
|
|
//
|
|
// Watchdog timer registers and related state.
|
|
//
|
|
//
|
|
struct
|
|
{
|
|
uint8 WTCSR; // We don't let a CPU program set bit3 to 1, but we do set bit3 to 1 as part of the standby NMI recovery process(for internal use).
|
|
uint8 WTCSRM;
|
|
uint8 WTCNT;
|
|
uint8 RSTCSR;
|
|
uint8 RSTCSRM;
|
|
} WDT;
|
|
|
|
void WDT_Reset(bool from_internal_wdt) MDFN_COLD; // Reset-reset only, NOT standby reset!
|
|
void WDT_StandbyReset(void) MDFN_COLD;
|
|
|
|
//
|
|
// DMA unit registers and related state
|
|
//
|
|
bool DMA_RunCond(unsigned ch);
|
|
bool DMA_InBurst(void);
|
|
void DMA_CheckEnterBurstHack(void);
|
|
void DMA_DoTransfer(unsigned ch);
|
|
sscpu_timestamp_t DMA_Update(sscpu_timestamp_t); // Takes/return external timestamp
|
|
void DMA_StartSG(void);
|
|
|
|
const unsigned event_id_dma;
|
|
sscpu_timestamp_t dma_lastts; // External SH7095_mem_timestamp related.
|
|
|
|
int32 DMA_ClockCounter;
|
|
int32 DMA_SGCounter; // When negative, smaller granularity scheduling for DMA_Update()
|
|
bool DMA_RoundRobinRockinBoppin;
|
|
|
|
struct
|
|
{
|
|
uint32 SAR;
|
|
uint32 DAR;
|
|
uint32 TCR; // 24-bit, value of 0 = 2^24 tranfers
|
|
uint16 CHCR;
|
|
uint16 CHCRM;
|
|
uint8 VCR;
|
|
uint8 DRCR;
|
|
} DMACH[2];
|
|
|
|
uint8 DMAOR;
|
|
uint8 DMAORM;
|
|
|
|
|
|
//
|
|
//
|
|
// Division unit registers and related state
|
|
//
|
|
//
|
|
void DIVU_S32_S32(void);
|
|
void DIVU_S64_S32(void);
|
|
|
|
sscpu_timestamp_t divide_finish_timestamp;
|
|
uint32 DVSR;
|
|
uint32 DVDNT;
|
|
uint32 DVDNTH;
|
|
uint32 DVDNTL;
|
|
uint32 DVDNTH_Shadow;
|
|
uint32 DVDNTL_Shadow;
|
|
uint16 VCRDIV;
|
|
uint8 DVCR;
|
|
|
|
#if 0
|
|
struct
|
|
{
|
|
uint8 SMR; // Mode
|
|
uint8 BRR; // Bit rate
|
|
uint8 SCR; // Control
|
|
uint8 TDR; // Transmit data
|
|
uint8 SSR; // Status
|
|
uint8 RDR; // Receive data
|
|
|
|
uint8 RSR; // Receive shift register
|
|
uint8 TSR; // Transmit shift register
|
|
} SCI;
|
|
#endif
|
|
|
|
const char* const cpu_name;
|
|
|
|
//
|
|
//
|
|
//
|
|
bool ExtHalt;
|
|
|
|
uint8 (*const ExIVecFetch)(void);
|
|
uint8 GetPendingInt(uint8*);
|
|
void RecalcPendingIntPEX(void);
|
|
|
|
template<bool DebugMode>
|
|
INLINE void FetchIF(bool ForceIBufferFill);
|
|
|
|
template<bool DebugMode, bool DelaySlot, bool IntPreventNext, bool SkipFetchIF>
|
|
void DoIDIF_Real(void);
|
|
|
|
template<typename T, bool BurstHax>
|
|
T ExtBusRead(uint32 A);
|
|
|
|
template<typename T>
|
|
void ExtBusWrite(uint32 A, T V);
|
|
|
|
template<typename T>
|
|
void OnChipRegWrite(uint32 A, uint32 V);
|
|
|
|
template<typename T>
|
|
T OnChipRegRead(uint32 A);
|
|
|
|
template<typename T, unsigned region, bool CacheEnabled, bool TwoWayMode, bool IsInstr>
|
|
T MemReadRT(uint32 A);
|
|
|
|
template<typename T>
|
|
T MemRead(uint32 A);
|
|
|
|
template<typename T, unsigned region, bool CacheEnabled>
|
|
void MemWriteRT(uint32 A, T V);
|
|
|
|
template<typename T>
|
|
void MemWrite(uint32 A, T V);
|
|
|
|
|
|
template<unsigned which, int DebugMode, bool delayed>
|
|
INLINE void Branch(uint32 target);
|
|
|
|
template<unsigned which, int DebugMode, bool delayed>
|
|
INLINE void CondRelBranch(bool cond, uint32 disp);
|
|
|
|
|
|
template<unsigned which, int DebugMode>
|
|
INLINE void UCDelayBranch(uint32 target);
|
|
|
|
template<unsigned which, int DebugMode>
|
|
INLINE void UCRelDelayBranch(uint32 disp);
|
|
|
|
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
public:
|
|
|
|
enum
|
|
{
|
|
// GSREG_PC_ID and GSREG_PC_IF are only valid when Step<true>() was called most recently(but they may be invalid
|
|
// for a while after <false>, too...).
|
|
GSREG_PC_ID = 0,
|
|
GSREG_PC_IF,
|
|
|
|
GSREG_PID,
|
|
GSREG_PIF,
|
|
|
|
GSREG_EP,
|
|
|
|
GSREG_RPC,
|
|
|
|
GSREG_R0, GSREG_R1, GSREG_R2, GSREG_R3, GSREG_R4, GSREG_R5, GSREG_R6, GSREG_R7,
|
|
GSREG_R8, GSREG_R9, GSREG_R10, GSREG_R11, GSREG_R12, GSREG_R13, GSREG_R14, GSREG_R15,
|
|
|
|
GSREG_SR,
|
|
GSREG_GBR,
|
|
GSREG_VBR,
|
|
|
|
GSREG_MACH,
|
|
GSREG_MACL,
|
|
GSREG_PR,
|
|
//
|
|
//
|
|
//
|
|
GSREG_NMIL,
|
|
GSREG_IRL,
|
|
GSREG_IPRA,
|
|
GSREG_IPRB,
|
|
GSREG_VCRWDT,
|
|
GSREG_VCRA,
|
|
GSREG_VCRB,
|
|
GSREG_VCRC,
|
|
GSREG_VCRD,
|
|
GSREG_ICR,
|
|
//
|
|
//
|
|
//
|
|
GSREG_DVSR,
|
|
GSREG_DVDNT,
|
|
GSREG_DVDNTH,
|
|
GSREG_DVDNTL,
|
|
GSREG_DVDNTHS,
|
|
GSREG_DVDNTLS,
|
|
GSREG_VCRDIV,
|
|
GSREG_DVCR,
|
|
|
|
//
|
|
//
|
|
//
|
|
GSREG_WTCSR,
|
|
GSREG_WTCSRM,
|
|
GSREG_WTCNT,
|
|
GSREG_RSTCSR,
|
|
GSREG_RSTCSRM,
|
|
//
|
|
//
|
|
//
|
|
GSREG_DMAOR,
|
|
GSREG_DMAORM,
|
|
|
|
GSREG_DMA0_SAR,
|
|
GSREG_DMA0_DAR,
|
|
GSREG_DMA0_TCR,
|
|
GSREG_DMA0_CHCR,
|
|
GSREG_DMA0_CHCRM,
|
|
GSREG_DMA0_VCR,
|
|
GSREG_DMA0_DRCR,
|
|
|
|
GSREG_DMA1_SAR,
|
|
GSREG_DMA1_DAR,
|
|
GSREG_DMA1_TCR,
|
|
GSREG_DMA1_CHCR,
|
|
GSREG_DMA1_CHCRM,
|
|
GSREG_DMA1_VCR,
|
|
GSREG_DMA1_DRCR,
|
|
|
|
GSREG_FRC,
|
|
GSREG_OCR0,
|
|
GSREG_OCR1,
|
|
GSREG_FICR,
|
|
GSREG_TIER,
|
|
GSREG_FTCSR,
|
|
GSREG_FTCSRM,
|
|
GSREG_TCR,
|
|
GSREG_TOCR,
|
|
GSREG_RWT,
|
|
};
|
|
|
|
uint32 GetRegister(const unsigned id, char* const special, const uint32 special_len);
|
|
void SetRegister(const unsigned id, const uint32 value) MDFN_COLD;
|
|
|
|
void CheckRWBreakpoints(void (*MRead)(unsigned len, uint32 addr), void (*MWrite)(unsigned len, uint32 addr)) const;
|
|
static void Disassemble(const uint16 instr, const uint32 PC, char* buffer, uint16 (*DisPeek16)(uint32), uint32 (*DisPeek32)(uint32));
|
|
private:
|
|
uint32 PC_IF, PC_ID; // Debug-related variables.
|
|
};
|
|
|
|
#endif
|