#pragma once #include "common/bitfield.h" #include "types.h" #include #include #include class StateWrapper; class TimingEvent; 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(); void Initialize(); void Shutdown(); void Reset(); bool DoState(StateWrapper& sw); u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); void SetRequest(Channel channel, bool request); // changing interfaces void SetMaxSliceTicks(TickCount ticks) { m_max_slice_ticks = ticks; } void SetHaltTicks(TickCount ticks) { m_halt_ticks = ticks; } void DrawDebugStateWindow(); private: static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF); static constexpr PhysicalMemoryAddress ADDRESS_MASK = UINT32_C(0x001FFFFC); static constexpr u32 TRANSFER_TICKS = 10; enum class SyncMode : u32 { Manual = 0, Request = 1, LinkedList = 2, Reserved = 3 }; // is everything enabled for a channel to operate? bool CanTransferChannel(Channel channel) const; bool IsTransferHalted() const; void UpdateIRQ(); // returns false if the DMA should now be halted bool TransferChannel(Channel channel); void HaltTransfer(TickCount duration); void UnhaltTransfer(TickCount ticks); // from device -> memory TickCount TransferDeviceToMemory(Channel channel, u32 address, u32 increment, u32 word_count); // from memory -> device TickCount TransferMemoryToDevice(Channel channel, u32 address, u32 increment, u32 word_count); // configuration TickCount m_max_slice_ticks = 1000; TickCount m_halt_ticks = 100; std::vector m_transfer_buffer; std::unique_ptr m_unhalt_event; TickCount m_halt_ticks_remaining = 0; struct ChannelState { u32 base_address = 0; 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; }; 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 = {}; }; extern DMA g_dma;