From 291426a3928a2ffe86176ec9fe6f23d177471fab Mon Sep 17 00:00:00 2001 From: Anonymus Raccoon Date: Wed, 27 May 2020 18:52:11 +0200 Subject: [PATCH] Handling a basic DMA --- sources/CPU/CPU.cpp | 20 ++++++++++++++------ sources/CPU/CPU.hpp | 3 --- sources/CPU/DMA/DMA.cpp | 42 +++++++++++++++++++++++++++++++++++++++-- sources/CPU/DMA/DMA.hpp | 39 ++++++++++++++++++++++++++++++-------- 4 files changed, 85 insertions(+), 19 deletions(-) diff --git a/sources/CPU/CPU.cpp b/sources/CPU/CPU.cpp index e507265..5962413 100644 --- a/sources/CPU/CPU.cpp +++ b/sources/CPU/CPU.cpp @@ -12,9 +12,12 @@ namespace ComSquare::CPU { CPU::CPU(std::shared_ptr bus, Cartridge::Header &cartridgeHeader) - : _bus(std::move(bus)), _cartridgeHeader(cartridgeHeader) + : _bus(std::move(bus)), + _cartridgeHeader(cartridgeHeader) { this->RESB(); + for (DMA &channel : this->_dmaChannels) + channel.setBus(_bus); } bool CPU::isDebugger() @@ -30,6 +33,8 @@ namespace ComSquare::CPU //! @bref The CPU's internal registers starts at $4200 and finish at $421F. uint8_t CPU::read(uint24_t addr) { + uint8_t tmp = 0; + switch (addr) { case 0x0: return this->_internalRegisters.nmitimen; @@ -54,7 +59,9 @@ namespace ComSquare::CPU case 0xA: return this->_internalRegisters.vtimeh; case 0xB: - return this->_internalRegisters.dmaEnableRegister; + for (int i = 0; i < 8; i++) + tmp |= this->_dmaChannels[i].enabled << i; + return tmp; case 0xC: return this->_internalRegisters.hdmaen; case 0xD: @@ -135,7 +142,8 @@ namespace ComSquare::CPU this->_internalRegisters.vtimeh = data; break; case 0xB: - this->_internalRegisters.dmaEnableRegister = data; + for (int i = 0; i < 8; i++) + this->_dmaChannels[i].enabled = data & (0b1 << i); break; case 0xC: this->_internalRegisters.hdmaen = data; @@ -211,10 +219,10 @@ namespace ComSquare::CPU unsigned cycles = 0; const unsigned maxCycles = 0x17; - for (int i = 0; i < 8; i++) { - if (!(this->_internalRegisters.dmaEnableRegister & (0xF << i))) + for (DMA &channel : this->_dmaChannels) { + if (!channel.enabled) continue; - cycles += this->_dmaChannels[i].run(maxCycles - cycles); + cycles += channel.run(maxCycles - cycles); } for (unsigned i = 0; i < maxCycles; i++) { if (this->_isStopped) { diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index f4af358..8aec2fa 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -131,9 +131,6 @@ namespace ComSquare::CPU //! @brief IRQ Timer Registers (Vertical - High) uint8_t vtimeh; - //! @brief DMA Enable Register - uint8_t dmaEnableRegister; - //! @brief HDMA Enable Register uint8_t hdmaen; diff --git a/sources/CPU/DMA/DMA.cpp b/sources/CPU/DMA/DMA.cpp index eda81d6..a9eee1d 100644 --- a/sources/CPU/DMA/DMA.cpp +++ b/sources/CPU/DMA/DMA.cpp @@ -7,6 +7,13 @@ namespace ComSquare::CPU { + DMA::DMA(std::shared_ptr bus) : _bus(std::move(bus)) {} + + void DMA::setBus(std::shared_ptr bus) + { + this->_bus = std::move(bus); + } + uint8_t DMA::read(uint8_t addr) { switch (addr) { @@ -58,8 +65,39 @@ namespace ComSquare::CPU } } - uint8_t DMA::run(unsigned int cycles) + unsigned DMA::_writeOneByte() { - return 0; + // Address $2180 refers to the WRam data register. Write to/Read from this port when the a address is on the vram cause different behaviors. + if (this->port == 0x80) { + auto accessor = this->_bus->getAccessor(this->aAddress.raw); + if (accessor && accessor->getComponent() == WRam) { + if (this->controlRegister.direction == AToB) + return 8; + this->_bus->write(this->aAddress.raw, 0xFF); + return 4; + } + } + if (this->controlRegister.direction == AToB) { + uint8_t data = this->_bus->read(this->aAddress.raw); + this->_bus->write(0x2100 | this->port, data); + } else { + uint8_t data = this->_bus->read(0x2100 | this->port); + this->_bus->write(this->aAddress.raw, data); + } + return 8; + } + + uint8_t DMA::run(unsigned int maxCycles) + { + unsigned cycles = 8; + + do { + cycles += this->_writeOneByte(); + if (!this->controlRegister.fixed) + this->aAddress.page += this->controlRegister.increment ? -1 : 1; + this->count.raw--; + } while (this->count.raw > 0 && cycles < maxCycles); + this->enabled = false; + return cycles; } } \ No newline at end of file diff --git a/sources/CPU/DMA/DMA.hpp b/sources/CPU/DMA/DMA.hpp index f152757..da9e8be 100644 --- a/sources/CPU/DMA/DMA.hpp +++ b/sources/CPU/DMA/DMA.hpp @@ -6,7 +6,9 @@ #define COMSQUARE_DMA_HPP #include +#include #include "../../Models/Int24.hpp" +#include "../../Memory/MemoryBus.hpp" namespace ComSquare::CPU { @@ -24,22 +26,31 @@ namespace ComSquare::CPU FourToFour = 0b100 }; + enum Direction { + AToB, + BToA + }; + //! @brief Class handling all DMA/HDMA transfers (Direct Memory Access or H-Blank Direct Memory Access) class DMA { + private: + //! @brief Write one byte using the A address, the port and the direction. Handle special cases where no write occurs. + //! @return The number of cycles used. + unsigned _writeOneByte(); public: //! @brief DMA Control register (various information about the transfer) union { struct { - //! @brief The direction of the transfer (0: CPU to PPU aka A to B, 1: PPU to CPU aka B to A). - bool direction: 1; - //! @brief Two unused bites. - bool _: 2; - //! @brief if this flag is 0: increment. Else: decrement. (The A address) - bool increment: 1; + //! @brief DMA's mode: how many bytes/registers there is, how many writes... + DMAMode mode: 3; //! @brief If this flag is set, no increment/decrement will be done. bool fixed: 1; - //! @brief DMA's mode: how many bytes/registers there is, how many writes... - enum DMAMode mode: 3; + //! @brief if this flag is 0: increment. Else: decrement. (The A address) + bool increment: 1; + //! @brief Two unused bites. + bool _: 2; + //! @brief The direction of the transfer. + Direction direction: 1; }; uint8_t raw; } controlRegister; @@ -48,6 +59,10 @@ namespace ComSquare::CPU //! @brief The absolute long address of the data from the A bus. union { uint8_t bytes[3]; + struct { + uint16_t page; + uint8_t bank; + }; uint24_t raw: 24; } aAddress; //! @brief The number of bytes to be transferred. @@ -55,6 +70,13 @@ namespace ComSquare::CPU uint8_t bytes[2]; uint16_t raw; } count; + //! @brief Is this channel set to run? + bool enabled; + + //! @brief The memory bus to use for read/write. + std::shared_ptr _bus; + //! @brief Set the memory bus used by this dma channel. + void setBus(std::shared_ptr bus); //! @brief Bus helper to read from this channel. uint8_t read(uint8_t addr); @@ -67,6 +89,7 @@ namespace ComSquare::CPU uint8_t run(unsigned cycles); DMA() = default; + DMA(std::shared_ptr bus); DMA(const DMA &) = default; DMA &operator=(const DMA &) = default; ~DMA() = default;