pcsx2/pcsx2/Dmac.h

569 lines
13 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/StringUtil.h"
// Useful enums for some of the fields.
enum pce_values
{
PCE_NOTHING = 0,
PCE_RESERVED,
PCE_DISABLED,
PCE_ENABLED
};
enum tag_id
{
TAG_CNTS = 0,
TAG_REFE = 0, // Transfer Packet According to ADDR field, clear STR, and end
TAG_CNT, // Transfer QWC following the tag.
TAG_NEXT, // Transfer QWC following tag. TADR = ADDR
TAG_REF, // Transfer QWC from ADDR field
TAG_REFS, // Transfer QWC from ADDR field (Stall Control)
TAG_CALL, // Transfer QWC following the tag, save succeeding tag
TAG_RET, // Transfer QWC following the tag, load next tag
TAG_END // Transfer QWC following the tag
};
enum mfd_type
{
NO_MFD = 0,
MFD_RESERVED,
MFD_VIF1,
MFD_GIF
};
enum sts_type
{
NO_STS = 0,
STS_SIF0,
STS_fromSPR,
STS_fromIPU
};
enum std_type
{
NO_STD = 0,
STD_VIF1,
STD_GIF,
STD_SIF1
};
enum LogicalTransferMode
{
NORMAL_MODE = 0,
CHAIN_MODE,
INTERLEAVE_MODE,
UNDEFINED_MODE
};
//
// --- DMA ---
//
// Doing double duty as both the top 32 bits *and* the lower 32 bits of a chain tag.
// Theoretically should probably both be in a u64 together, but with the way the
// code is layed out, this is easier for the moment.
union tDMA_TAG {
struct {
u32 QWC : 16;
u32 _reserved2 : 10;
u32 PCE : 2;
u32 ID : 3;
u32 IRQ : 1;
};
struct {
u32 ADDR : 31;
u32 SPR : 1;
};
u32 _u32;
tDMA_TAG() {}
tDMA_TAG(u32 val) { _u32 = val; }
u16 upper() const { return (_u32 >> 16); }
u16 lower() const { return (u16)_u32; }
std::string tag_to_str() const
{
switch(ID)
{
case TAG_REFE: return StringUtil::StdStringFromFormat("REFE %08X", _u32);
case TAG_CNT: return "CNT";
case TAG_NEXT: return StringUtil::StdStringFromFormat("NEXT %08X", _u32);
case TAG_REF: return StringUtil::StdStringFromFormat("REF %08X", _u32);
case TAG_REFS: return StringUtil::StdStringFromFormat("REFS %08X", _u32);
case TAG_CALL: return "CALL";
case TAG_RET: return "RET";
case TAG_END: return "END";
default: return "????";
}
}
void reset() { _u32 = 0; }
};
#define DMA_TAG(value) ((tDMA_TAG)(value))
union tDMA_CHCR {
struct {
u32 DIR : 1; // Direction: 0 - to memory, 1 - from memory. VIF1 & SIF2 only.
u32 _reserved1 : 1;
u32 MOD : 2; // Logical transfer mode. Normal, Chain, or Interleave (see LogicalTransferMode enum)
u32 ASP : 2; // ASP1 & ASP2; Address stack pointer. 0, 1, or 2 addresses.
u32 TTE : 1; // Tag Transfer Enable. 0 - Disable / 1 - Enable.
u32 TIE : 1; // Tag Interrupt Enable. 0 - Disable / 1 - Enable.
u32 STR : 1; // Start. 0 while stopping DMA, 1 while it's running.
u32 _reserved2 : 7;
u32 TAG : 16; // Maintains upper 16 bits of the most recently read DMAtag.
};
u32 _u32;
tDMA_CHCR( u32 val) { _u32 = val; }
bool test(u32 flags) const { return !!(_u32 & flags); }
void set(u32 value) { _u32 = value; }
void set_flags(u32 flags) { _u32 |= flags; }
void clear_flags(u32 flags) { _u32 &= ~flags; }
void reset() { _u32 = 0; }
u16 upper() const { return (_u32 >> 16); }
u16 lower() const { return (u16)_u32; }
std::string desc() const { return StringUtil::StdStringFromFormat("Chcr: 0x%x", _u32); }
tDMA_TAG tag() { return (tDMA_TAG)_u32; }
};
#define CHCR(value) ((tDMA_CHCR)(value))
union tDMA_SADR {
struct {
u32 ADDR : 14;
u32 reserved2 : 18;
};
u32 _u32;
tDMA_SADR(u32 val) { _u32 = val; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("Sadr: 0x%x", _u32); }
tDMA_TAG tag() const { return (tDMA_TAG)_u32; }
};
union tDMA_QWC {
struct {
u32 QWC;
};
u32 _u32;
tDMA_QWC(u32 val) { _u32 = val; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("QWC: 0x%04x", QWC); }
tDMA_TAG tag() const { return (tDMA_TAG)_u32; }
};
struct DMACh {
tDMA_CHCR chcr;
u32 _null0[3];
u32 madr;
u32 _null1[3];
u32 qwc;
u32 _null2[3];
u32 tadr;
u32 _null3[3];
u32 asr0;
u32 _null4[3];
u32 asr1;
u32 _null5[11];
u32 sadr;
void chcrTransfer(tDMA_TAG* ptag)
{
chcr.TAG = ptag[0].upper();
}
void qwcTransfer(tDMA_TAG* ptag)
{
qwc = ptag[0].QWC;
}
bool transfer(const char *s, tDMA_TAG* ptag);
void unsafeTransfer(tDMA_TAG* ptag);
tDMA_TAG *getAddr(u32 addr, u32 num, bool write);
tDMA_TAG *DMAtransfer(u32 addr, u32 num);
tDMA_TAG dma_tag();
std::string cmq_to_str() const;
std::string cmqt_to_str() const;
};
enum INTCIrqs
{
INTC_GS = 0,
INTC_SBUS,
INTC_VBLANK_S,
INTC_VBLANK_E,
INTC_VIF0,
INTC_VIF1,
INTC_VU0,
INTC_VU1,
INTC_IPU,
INTC_TIM0,
INTC_TIM1,
INTC_TIM2,
INTC_TIM3,
INTC_SFIFO,
INTVU0_WD
};
enum dmac_conditions
{
DMAC_STAT_SIS = (1<<13), // stall condition
DMAC_STAT_MEIS = (1<<14), // mfifo empty
DMAC_STAT_BEIS = (1<<15), // bus error
DMAC_STAT_SIM = (1<<29), // stall mask
DMAC_STAT_MEIM = (1<<30) // mfifo mask
};
//DMA interrupts & masks
enum DMAInter
{
BEISintr = 0x00008000,
VIF0intr = 0x00010001,
VIF1intr = 0x00020002,
GIFintr = 0x00040004,
IPU0intr = 0x00080008,
IPU1intr = 0x00100010,
SIF0intr = 0x00200020,
SIF1intr = 0x00400040,
SIF2intr = 0x00800080,
SPR0intr = 0x01000100,
SPR1intr = 0x02000200,
SISintr = 0x20002000,
MEISintr = 0x40004000
};
union tDMAC_QUEUE
{
struct
{
u16 VIF0 : 1;
u16 VIF1 : 1;
u16 GIF : 1;
u16 IPU0 : 1;
u16 IPU1 : 1;
u16 SIF0 : 1;
u16 SIF1 : 1;
u16 SIF2 : 1;
u16 SPR0 : 1;
u16 SPR1 : 1;
u16 SIS : 1;
u16 MEIS : 1;
u16 BEIS : 1;
};
u16 _u16;
tDMAC_QUEUE(u16 val) { _u16 = val; }
void reset() { _u16 = 0; }
bool empty() const { return (_u16 == 0); }
};
static __fi const char* ChcrName(u32 addr)
{
switch (addr)
{
case D0_CHCR: return "Vif 0";
case D1_CHCR: return "Vif 1";
case D2_CHCR: return "GIF";
case D3_CHCR: return "Ipu 0";
case D4_CHCR: return "Ipu 1";
case D5_CHCR: return "Sif 0";
case D6_CHCR: return "Sif 1";
case D7_CHCR: return "Sif 2";
case D8_CHCR: return "SPR 0";
case D9_CHCR: return "SPR 1";
default: return "???";
}
}
// Believe it or not, making this const can generate compiler warnings in gcc.
static __fi int ChannelNumber(u32 addr)
{
switch (addr)
{
case D0_CHCR: return 0;
case D1_CHCR: return 1;
case D2_CHCR: return 2;
case D3_CHCR: return 3;
case D4_CHCR: return 4;
case D5_CHCR: return 5;
case D6_CHCR: return 6;
case D7_CHCR: return 7;
case D8_CHCR: return 8;
case D9_CHCR: return 9;
default:
{
pxFailDev("Invalid DMA channel number");
return 51; // some value
}
}
}
union tDMAC_CTRL {
struct {
u32 DMAE : 1; // 0/1 - disables/enables all DMAs
u32 RELE : 1; // 0/1 - cycle stealing off/on
u32 MFD : 2; // Memory FIFO drain channel (mfd_type)
u32 STS : 2; // Stall Control source channel (sts type)
u32 STD : 2; // Stall Control drain channel (std_type)
u32 RCYC : 3; // Release cycle (8/16/32/64/128/256)
u32 _reserved1 : 21;
};
u32 _u32;
tDMAC_CTRL(u32 val) { _u32 = val; }
bool test(u32 flags) const { return !!(_u32 & flags); }
void set_flags(u32 flags) { _u32 |= flags; }
void clear_flags(u32 flags) { _u32 &= ~flags; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("Ctrl: 0x%x", _u32); }
};
union tDMAC_STAT {
struct {
u32 CIS : 10;
u32 _reserved1 : 3;
u32 SIS : 1;
u32 MEIS : 1;
u32 BEIS : 1;
u32 CIM : 10;
u32 _reserved2 : 3;
u32 SIM : 1;
u32 MEIM : 1;
u32 _reserved3 : 1;
};
u32 _u32;
u16 _u16[2];
tDMAC_STAT(u32 val) { _u32 = val; }
bool test(u32 flags) const { return !!(_u32 & flags); }
void set_flags(u32 flags) { _u32 |= flags; }
void clear_flags(u32 flags) { _u32 &= ~flags; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("Stat: 0x%x", _u32); }
bool TestForInterrupt() const
{
return ((_u16[0] & _u16[1]) != 0) || BEIS;
}
};
union tDMAC_PCR {
struct {
u32 CPC : 10;
u32 _reserved1 : 6;
u32 CDE : 10;
u32 _reserved2 : 5;
u32 PCE : 1;
};
u32 _u32;
tDMAC_PCR(u32 val) { _u32 = val; }
bool test(u32 flags) const { return !!(_u32 & flags); }
void set_flags(u32 flags) { _u32 |= flags; }
void clear_flags(u32 flags) { _u32 &= ~flags; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("Pcr: 0x%x", _u32); }
};
union tDMAC_SQWC {
struct {
u32 SQWC : 8;
u32 _reserved1 : 8;
u32 TQWC : 8;
u32 _reserved2 : 8;
};
u32 _u32;
tDMAC_SQWC(u32 val) { _u32 = val; }
bool test(u32 flags) const { return !!(_u32 & flags); }
void set_flags(u32 flags) { _u32 |= flags; }
void clear_flags(u32 flags) { _u32 &= ~flags; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("Sqwc: 0x%x", _u32); }
};
union tDMAC_RBSR {
struct {
u32 RMSK : 31;
u32 _reserved1 : 1;
};
u32 _u32;
tDMAC_RBSR(u32 val) { _u32 = val; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("Rbsr: 0x%x", _u32); }
};
union tDMAC_RBOR {
struct {
u32 ADDR : 31;
u32 _reserved1 : 1;
};
u32 _u32;
tDMAC_RBOR(u32 val) { _u32 = val; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("Rbor: 0x%x", _u32); }
};
// --------------------------------------------------------------------------------------
// tDMAC_ADDR
// --------------------------------------------------------------------------------------
// This struct is used for several DMA address types, including some that do not have
// effective SPR bit (the bit is ignored for all addresses that are not "allowed" to access
// the scratchpad, including STADR, toSPR.MADR, fromSPR.MADR, etc.).
//
union tDMAC_ADDR
{
struct {
u32 ADDR : 31; // Transfer memory address
u32 SPR : 1; // Memory/SPR Address (only effective for MADR and TADR of non-SPR DMAs)
};
u32 _u32;
tDMAC_ADDR() {}
tDMAC_ADDR(u32 val) { _u32 = val; }
void clear() { _u32 = 0; }
void AssignADDR(uint addr)
{
ADDR = addr;
if (SPR) ADDR &= (Ps2MemSize::Scratch-1);
}
void IncrementQWC(uint incval = 1)
{
ADDR += incval;
if (SPR) ADDR &= (Ps2MemSize::Scratch-1);
}
std::string ToString(bool sprIsValid=true) const
{
return StringUtil::StdStringFromFormat((sprIsValid && SPR) ? "0x%04X(SPR)" : "0x%08X", ADDR);
}
};
struct DMACregisters
{
tDMAC_CTRL ctrl;
u32 _padding[3];
tDMAC_STAT stat;
u32 _padding1[3];
tDMAC_PCR pcr;
u32 _padding2[3];
tDMAC_SQWC sqwc;
u32 _padding3[3];
tDMAC_RBSR rbsr;
u32 _padding4[3];
tDMAC_RBOR rbor;
u32 _padding5[3];
tDMAC_ADDR stadr;
u32 _padding6[3];
};
// Currently guesswork.
union tINTC_STAT {
struct {
u32 interrupts : 10;
u32 _placeholder : 22;
};
u32 _u32;
tINTC_STAT(u32 val) { _u32 = val; }
bool test(u32 flags) const { return !!(_u32 & flags); }
void set_flags(u32 flags) { _u32 |= flags; }
void clear_flags(u32 flags) { _u32 &= ~flags; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("Stat: 0x%x", _u32); }
};
union tINTC_MASK {
struct {
u32 int_mask : 10;
u32 _placeholder:22;
};
u32 _u32;
tINTC_MASK(u32 val) { _u32 = val; }
bool test(u32 flags) const { return !!(_u32 & flags); }
void set_flags(u32 flags) { _u32 |= flags; }
void clear_flags(u32 flags) { _u32 &= ~flags; }
void reset() { _u32 = 0; }
std::string desc() const { return StringUtil::StdStringFromFormat("Mask: 0x%x", _u32); }
};
struct INTCregisters
{
tINTC_STAT stat;
u32 _padding1[3];
tINTC_MASK mask;
u32 _padding2[3];
};
#define intcRegs ((INTCregisters*)(eeHw+0xF000))
static DMACregisters& dmacRegs = (DMACregisters&)eeHw[0xE000];
// Various useful locations
static DMACh& vif0ch = (DMACh&)eeHw[0x8000];
static DMACh& vif1ch = (DMACh&)eeHw[0x9000];
static DMACh& gifch = (DMACh&)eeHw[0xA000];
static DMACh& spr0ch = (DMACh&)eeHw[0xD000];
static DMACh& spr1ch = (DMACh&)eeHw[0xD400];
static DMACh& ipu0ch = (DMACh&)eeHw[0xb000];
static DMACh& ipu1ch = (DMACh&)eeHw[0xb400];
static DMACh& sif0ch = (DMACh&)eeHw[0xc000];
static DMACh& sif1ch = (DMACh&)eeHw[0xc400];
static DMACh& sif2dma = (DMACh&)eeHw[0xc800];
extern void throwBusError(const char *s);
extern void setDmacStat(u32 num);
extern tDMA_TAG *SPRdmaGetAddr(u32 addr, bool write);
extern tDMA_TAG *dmaGetAddr(u32 addr, bool write);
extern void hwIntcIrq(int n);
extern void hwDmacIrq(int n);
extern void FireMFIFOEmpty();
extern bool hwMFIFOWrite(u32 addr, const u128* data, uint size_qwc);
extern void hwMFIFOResume(u32 transferred);
extern void hwDmacSrcTadrInc(DMACh& dma);
extern bool hwDmacSrcChainWithStack(DMACh& dma, int id);
extern bool hwDmacSrcChain(DMACh& dma, int id);
template< uint page > u32 dmacRead32( u32 mem );
template< uint page > extern bool dmacWrite32( u32 mem, mem32_t& value );