2019-09-09 07:01:26 +00:00
|
|
|
#pragma once
|
|
|
|
#include "common/bitfield.h"
|
|
|
|
#include "types.h"
|
|
|
|
#include <array>
|
2020-01-24 04:53:40 +00:00
|
|
|
#include <memory>
|
2019-11-27 15:55:33 +00:00
|
|
|
#include <vector>
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2019-09-14 10:28:47 +00:00
|
|
|
class StateWrapper;
|
|
|
|
|
2019-09-24 11:38:58 +00:00
|
|
|
class System;
|
2020-01-24 04:53:40 +00:00
|
|
|
class TimingEvent;
|
2019-09-09 07:01:26 +00:00
|
|
|
class Bus;
|
2019-09-24 09:43:10 +00:00
|
|
|
class InterruptController;
|
2019-09-09 07:01:26 +00:00
|
|
|
class GPU;
|
2019-09-21 15:12:16 +00:00
|
|
|
class CDROM;
|
2019-09-24 13:44:38 +00:00
|
|
|
class SPU;
|
2019-09-29 02:51:34 +00:00
|
|
|
class MDEC;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
|
|
|
class DMA
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum : u32
|
|
|
|
{
|
|
|
|
NUM_CHANNELS = 7
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class Channel : u32
|
|
|
|
{
|
|
|
|
MDECin = 0,
|
|
|
|
MDECout = 1,
|
|
|
|
GPU = 2,
|
|
|
|
CDROM = 3,
|
|
|
|
SPU = 4,
|
|
|
|
PIO = 5,
|
|
|
|
OTC = 6
|
|
|
|
};
|
|
|
|
|
|
|
|
DMA();
|
|
|
|
~DMA();
|
|
|
|
|
2019-11-11 08:19:57 +00:00
|
|
|
void Initialize(System* system, Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom, SPU* spu,
|
2019-10-13 04:16:49 +00:00
|
|
|
MDEC* mdec);
|
2019-09-09 07:01:26 +00:00
|
|
|
void Reset();
|
2019-09-14 10:28:47 +00:00
|
|
|
bool DoState(StateWrapper& sw);
|
2019-09-09 07:01:26 +00:00
|
|
|
|
|
|
|
u32 ReadRegister(u32 offset);
|
|
|
|
void WriteRegister(u32 offset, u32 value);
|
|
|
|
|
2019-09-11 04:01:19 +00:00
|
|
|
void SetRequest(Channel channel, bool request);
|
|
|
|
|
2019-10-26 02:55:56 +00:00
|
|
|
// changing interfaces
|
|
|
|
void SetGPU(GPU* gpu) { m_gpu = gpu; }
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
private:
|
2019-11-11 10:34:41 +00:00
|
|
|
static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF);
|
|
|
|
static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x001FFFFC);
|
2020-01-11 03:40:01 +00:00
|
|
|
static constexpr u32 TRANSFER_TICKS = 10;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
|
|
|
enum class SyncMode : u32
|
|
|
|
{
|
2019-09-11 04:01:19 +00:00
|
|
|
Manual = 0,
|
|
|
|
Request = 1,
|
2019-09-09 07:01:26 +00:00
|
|
|
LinkedList = 2,
|
|
|
|
Reserved = 3
|
|
|
|
};
|
|
|
|
|
2019-11-15 13:27:54 +00:00
|
|
|
/// Returns the number of ticks for a given channel's transfer.
|
|
|
|
TickCount GetTransferDelay(Channel channel) const;
|
|
|
|
|
2019-09-11 04:01:19 +00:00
|
|
|
// is everything enabled for a channel to operate?
|
2019-10-13 04:16:49 +00:00
|
|
|
bool CanTransferChannel(Channel channel) const;
|
2019-09-24 11:38:58 +00:00
|
|
|
bool CanRunAnyChannels() const;
|
2019-10-17 14:23:11 +00:00
|
|
|
void UpdateIRQ();
|
2019-09-11 04:01:19 +00:00
|
|
|
|
2020-01-24 04:53:40 +00:00
|
|
|
void UpdateChannelTransferEvent(Channel channel);
|
|
|
|
void TransferChannel(Channel channel, TickCount ticks_late);
|
2019-09-11 04:01:19 +00:00
|
|
|
|
|
|
|
// from device -> memory
|
2019-10-13 06:48:11 +00:00
|
|
|
void TransferDeviceToMemory(Channel channel, u32 address, u32 increment, u32 word_count);
|
2019-09-11 04:01:19 +00:00
|
|
|
|
|
|
|
// from memory -> device
|
2019-10-13 06:48:11 +00:00
|
|
|
void TransferMemoryToDevice(Channel channel, u32 address, u32 increment, u32 word_count);
|
2019-09-11 04:01:19 +00:00
|
|
|
|
2019-09-24 11:38:58 +00:00
|
|
|
System* m_system = nullptr;
|
2019-09-11 04:01:19 +00:00
|
|
|
Bus* m_bus = nullptr;
|
2019-09-24 09:43:10 +00:00
|
|
|
InterruptController* m_interrupt_controller = nullptr;
|
2019-09-11 04:01:19 +00:00
|
|
|
GPU* m_gpu = nullptr;
|
2019-09-21 15:12:16 +00:00
|
|
|
CDROM* m_cdrom = nullptr;
|
2019-09-24 13:44:38 +00:00
|
|
|
SPU* m_spu = nullptr;
|
2019-09-29 02:51:34 +00:00
|
|
|
MDEC* m_mdec = nullptr;
|
2019-09-11 04:01:19 +00:00
|
|
|
|
2019-10-13 06:48:11 +00:00
|
|
|
std::vector<u32> m_transfer_buffer;
|
|
|
|
|
2019-09-09 07:01:26 +00:00
|
|
|
struct ChannelState
|
|
|
|
{
|
2020-01-24 04:53:40 +00:00
|
|
|
std::unique_ptr<TimingEvent> transfer_event;
|
2020-01-24 06:44:13 +00:00
|
|
|
u32 base_address = 0;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
|
|
|
union BlockControl
|
|
|
|
{
|
|
|
|
u32 bits;
|
2019-09-12 15:09:44 +00:00
|
|
|
union
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
|
|
|
BitField<u32, u32, 0, 16> word_count;
|
2019-09-11 04:01:19 +00:00
|
|
|
|
|
|
|
u32 GetWordCount() const { return (word_count == 0) ? 0x10000 : word_count; }
|
|
|
|
} manual;
|
2019-09-12 15:09:44 +00:00
|
|
|
union
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
|
|
|
BitField<u32, u32, 0, 16> block_size;
|
|
|
|
BitField<u32, u32, 16, 16> block_count;
|
2019-09-11 04:01:19 +00:00
|
|
|
|
|
|
|
u32 GetBlockSize() const { return (block_size == 0) ? 0x10000 : block_size; }
|
|
|
|
u32 GetBlockCount() const { return (block_count == 0) ? 0x10000 : block_count; }
|
|
|
|
} request;
|
2020-01-24 06:44:13 +00:00
|
|
|
} block_control = {};
|
2019-09-09 07:01:26 +00:00
|
|
|
|
|
|
|
union ChannelControl
|
|
|
|
{
|
|
|
|
u32 bits;
|
2019-09-11 04:01:19 +00:00
|
|
|
BitField<u32, bool, 0, 1> copy_to_device;
|
2019-09-11 04:59:41 +00:00
|
|
|
BitField<u32, bool, 1, 1> address_step_reverse;
|
2019-09-09 07:01:26 +00:00
|
|
|
BitField<u32, bool, 8, 1> chopping_enable;
|
|
|
|
BitField<u32, SyncMode, 9, 2> sync_mode;
|
|
|
|
BitField<u32, u32, 16, 3> chopping_dma_window_size;
|
|
|
|
BitField<u32, u32, 20, 3> chopping_cpu_window_size;
|
2019-09-11 04:01:19 +00:00
|
|
|
BitField<u32, bool, 24, 1> enable_busy;
|
2019-09-09 07:01:26 +00:00
|
|
|
BitField<u32, bool, 28, 1> start_trigger;
|
2019-09-11 04:01:19 +00:00
|
|
|
|
|
|
|
static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011;
|
2020-01-24 06:44:13 +00:00
|
|
|
} channel_control = {};
|
2019-09-11 04:01:19 +00:00
|
|
|
|
|
|
|
bool request = false;
|
2019-09-09 07:01:26 +00:00
|
|
|
};
|
|
|
|
|
2020-01-24 06:44:13 +00:00
|
|
|
std::array<ChannelState, NUM_CHANNELS> m_state;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2019-09-24 09:43:10 +00:00
|
|
|
union DPCR
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
|
|
|
u32 bits;
|
|
|
|
|
2019-09-24 09:43:10 +00:00
|
|
|
BitField<u32, u8, 0, 3> MDECin_priority;
|
|
|
|
BitField<u32, bool, 3, 1> MDECin_master_enable;
|
|
|
|
BitField<u32, u8, 4, 3> MDECout_priority;
|
|
|
|
BitField<u32, bool, 7, 1> MDECout_master_enable;
|
|
|
|
BitField<u32, u8, 8, 3> GPU_priority;
|
|
|
|
BitField<u32, bool, 10, 1> GPU_master_enable;
|
|
|
|
BitField<u32, u8, 12, 3> CDROM_priority;
|
|
|
|
BitField<u32, bool, 15, 1> CDROM_master_enable;
|
|
|
|
BitField<u32, u8, 16, 3> SPU_priority;
|
|
|
|
BitField<u32, bool, 19, 1> SPU_master_enable;
|
|
|
|
BitField<u32, u8, 20, 3> PIO_priority;
|
|
|
|
BitField<u32, bool, 23, 1> PIO_master_enable;
|
|
|
|
BitField<u32, u8, 24, 3> OTC_priority;
|
|
|
|
BitField<u32, bool, 27, 1> OTC_master_enable;
|
|
|
|
BitField<u32, u8, 28, 3> priority_offset;
|
|
|
|
BitField<u32, bool, 31, 1> unused;
|
|
|
|
|
2019-09-11 04:01:19 +00:00
|
|
|
u8 GetPriority(Channel channel) const { return ((bits >> (static_cast<u8>(channel) * 4)) & u32(3)); }
|
|
|
|
bool GetMasterEnable(Channel channel) const
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
|
|
|
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(channel) * 4 + 3)) & u32(1));
|
|
|
|
}
|
2020-01-24 06:44:13 +00:00
|
|
|
} m_DPCR = {};
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2019-09-24 09:43:10 +00:00
|
|
|
static constexpr u32 DICR_WRITE_MASK = 0b00000000'11111111'10000000'00111111;
|
|
|
|
static constexpr u32 DICR_RESET_MASK = 0b01111111'00000000'00000000'00000000;
|
|
|
|
union DICR
|
|
|
|
{
|
|
|
|
u32 bits;
|
|
|
|
|
|
|
|
BitField<u32, bool, 15, 1> force_irq;
|
|
|
|
BitField<u32, bool, 16, 1> MDECin_irq_enable;
|
|
|
|
BitField<u32, bool, 17, 1> MDECout_irq_enable;
|
|
|
|
BitField<u32, bool, 18, 1> GPU_irq_enable;
|
|
|
|
BitField<u32, bool, 19, 1> CDROM_irq_enable;
|
|
|
|
BitField<u32, bool, 20, 1> SPU_irq_enable;
|
|
|
|
BitField<u32, bool, 21, 1> PIO_irq_enable;
|
|
|
|
BitField<u32, bool, 22, 1> OTC_irq_enable;
|
|
|
|
BitField<u32, bool, 23, 1> master_enable;
|
|
|
|
BitField<u32, bool, 24, 1> MDECin_irq_flag;
|
|
|
|
BitField<u32, bool, 25, 1> MDECout_irq_flag;
|
|
|
|
BitField<u32, bool, 26, 1> GPU_irq_flag;
|
|
|
|
BitField<u32, bool, 27, 1> CDROM_irq_flag;
|
|
|
|
BitField<u32, bool, 28, 1> SPU_irq_flag;
|
|
|
|
BitField<u32, bool, 29, 1> PIO_irq_flag;
|
|
|
|
BitField<u32, bool, 30, 1> OTC_irq_flag;
|
|
|
|
BitField<u32, bool, 31, 1> master_flag;
|
|
|
|
|
|
|
|
bool IsIRQEnabled(Channel channel) const
|
|
|
|
{
|
|
|
|
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(channel) + 16)) & u32(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GetIRQFlag(Channel channel) const
|
|
|
|
{
|
|
|
|
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(channel) + 24)) & u32(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetIRQFlag(Channel channel) { bits |= (u32(1) << (static_cast<u8>(channel) + 24)); }
|
|
|
|
void ClearIRQFlag(Channel channel) { bits &= ~(u32(1) << (static_cast<u8>(channel) + 24)); }
|
|
|
|
|
|
|
|
void UpdateMasterFlag()
|
|
|
|
{
|
|
|
|
master_flag = master_enable && ((((bits >> 16) & u32(0b1111111)) & ((bits >> 24) & u32(0b1111111))) != 0);
|
|
|
|
}
|
|
|
|
} m_DICR = {};
|
2019-09-09 07:01:26 +00:00
|
|
|
};
|