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;