#pragma once #include "common/bitfield.h" #include "types.h" #include class StateWrapper; class Bus; class InterruptController; class GPU; class CDROM; 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(); bool Initialize(Bus* bus, InterruptController* interrupt_controller, GPU* gpu, CDROM* cdrom); void Reset(); bool DoState(StateWrapper& sw); u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); void SetRequest(Channel channel, bool request); private: static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x00FFFFFF); enum class SyncMode : u32 { Manual = 0, Request = 1, LinkedList = 2, Reserved = 3 }; // is everything enabled for a channel to operate? bool CanRunChannel(Channel channel) const; void RunDMA(Channel channel); // from device -> memory u32 DMARead(Channel channel, PhysicalMemoryAddress dst_address, u32 remaining_words); // from memory -> device void DMAWrite(Channel channel, u32 value, PhysicalMemoryAddress src_address, u32 remaining_words); Bus* m_bus = nullptr; InterruptController* m_interrupt_controller = nullptr; GPU* m_gpu = nullptr; CDROM* m_cdrom = nullptr; struct ChannelState { u32 base_address; union BlockControl { u32 bits; union { BitField word_count; u32 GetWordCount() const { return (word_count == 0) ? 0x10000 : word_count; } } manual; union { BitField block_size; BitField block_count; u32 GetBlockSize() const { return (block_size == 0) ? 0x10000 : block_size; } u32 GetBlockCount() const { return (block_count == 0) ? 0x10000 : block_count; } } request; } block_control; union ChannelControl { u32 bits; BitField copy_to_device; BitField address_step_reverse; BitField chopping_enable; BitField sync_mode; BitField chopping_dma_window_size; BitField chopping_cpu_window_size; BitField enable_busy; BitField start_trigger; static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011; } channel_control; bool request = false; bool irq = false; }; std::array m_state = {}; union DPCR { u32 bits; BitField MDECin_priority; BitField MDECin_master_enable; BitField MDECout_priority; BitField MDECout_master_enable; BitField GPU_priority; BitField GPU_master_enable; BitField CDROM_priority; BitField CDROM_master_enable; BitField SPU_priority; BitField SPU_master_enable; BitField PIO_priority; BitField PIO_master_enable; BitField OTC_priority; BitField OTC_master_enable; BitField priority_offset; BitField unused; u8 GetPriority(Channel channel) const { return ((bits >> (static_cast(channel) * 4)) & u32(3)); } bool GetMasterEnable(Channel channel) const { return ConvertToBoolUnchecked((bits >> (static_cast(channel) * 4 + 3)) & u32(1)); } } m_DPCR; 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 force_irq; BitField MDECin_irq_enable; BitField MDECout_irq_enable; BitField GPU_irq_enable; BitField CDROM_irq_enable; BitField SPU_irq_enable; BitField PIO_irq_enable; BitField OTC_irq_enable; BitField master_enable; BitField MDECin_irq_flag; BitField MDECout_irq_flag; BitField GPU_irq_flag; BitField CDROM_irq_flag; BitField SPU_irq_flag; BitField PIO_irq_flag; BitField OTC_irq_flag; BitField master_flag; bool IsIRQEnabled(Channel channel) const { return ConvertToBoolUnchecked((bits >> (static_cast(channel) + 16)) & u32(1)); } bool GetIRQFlag(Channel channel) const { return ConvertToBoolUnchecked((bits >> (static_cast(channel) + 24)) & u32(1)); } void SetIRQFlag(Channel channel) { bits |= (u32(1) << (static_cast(channel) + 24)); } void ClearIRQFlag(Channel channel) { bits &= ~(u32(1) << (static_cast(channel) + 24)); } void UpdateMasterFlag() { master_flag = master_enable && ((((bits >> 16) & u32(0b1111111)) & ((bits >> 24) & u32(0b1111111))) != 0); } } m_DICR = {}; };