mirror of
https://github.com/zoriya/ComSquare.git
synced 2025-12-21 06:35:10 +00:00
133 lines
2.9 KiB
C++
133 lines
2.9 KiB
C++
//
|
|
// Created by anonymus-raccoon on 5/26/20.
|
|
//
|
|
|
|
#include "DMA.hpp"
|
|
#include "Exceptions/InvalidAddress.hpp"
|
|
|
|
namespace ComSquare::CPU
|
|
{
|
|
DMA::DMA(Memory::IMemoryBus &bus)
|
|
: _bus(bus),
|
|
enabled(false)
|
|
{}
|
|
|
|
void DMA::setBus(Memory::IMemoryBus &bus)
|
|
{
|
|
this->_bus = bus;
|
|
}
|
|
|
|
uint8_t DMA::read(uint8_t addr) const
|
|
{
|
|
switch (addr) {
|
|
case 0x0:
|
|
return this->_controlRegister.raw;
|
|
case 0x1:
|
|
return this->_port;
|
|
case 0x2:
|
|
return this->_aAddress.bytes[0];
|
|
case 0x3:
|
|
return this->_aAddress.bytes[1];
|
|
case 0x4:
|
|
return this->_aAddress.bytes[2];
|
|
case 0x5:
|
|
return this->_count.bytes[0];
|
|
case 0x6:
|
|
return this->_count.bytes[1];
|
|
default:
|
|
throw InvalidAddress("DMA read", addr);
|
|
}
|
|
}
|
|
|
|
void DMA::write(uint8_t addr, uint8_t data)
|
|
{
|
|
switch (addr) {
|
|
case 0x0:
|
|
this->_controlRegister.raw = data;
|
|
break;
|
|
case 0x1:
|
|
this->_port = data;
|
|
break;
|
|
case 0x2:
|
|
this->_aAddress.bytes[0] = data;
|
|
break;
|
|
case 0x3:
|
|
this->_aAddress.bytes[1] = data;
|
|
break;
|
|
case 0x4:
|
|
this->_aAddress.bytes[2] = data;
|
|
break;
|
|
case 0x5:
|
|
this->_count.bytes[0] = data;
|
|
break;
|
|
case 0x6:
|
|
this->_count.bytes[1] = data;
|
|
break;
|
|
default:
|
|
throw InvalidAddress("DMA read", addr);
|
|
}
|
|
}
|
|
|
|
unsigned DMA::_writeOneByte(uint24_t aAddress, uint24_t bAddress)
|
|
{
|
|
// 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->getBus().getAccessor(aAddress);
|
|
if (accessor && accessor->getComponent() == WRam) {
|
|
// WRAM->$2180 The write is not performed but the time is consumed anyway.
|
|
if (this->_controlRegister.direction == AtoB)
|
|
return 8;
|
|
// $2180->WRAM No read is performed (so only 4 master cycles are needed) but the value written is invalid.
|
|
this->getBus().write(aAddress, 0xFF);
|
|
return 4;
|
|
}
|
|
}
|
|
if (this->_controlRegister.direction == AtoB) {
|
|
uint8_t data = this->getBus().read(aAddress);
|
|
this->getBus().write(bAddress, data);
|
|
} else {
|
|
uint8_t data = this->getBus().read(bAddress);
|
|
this->getBus().write(aAddress, data);
|
|
}
|
|
return 8;
|
|
}
|
|
|
|
unsigned DMA::run(unsigned int maxCycles)
|
|
{
|
|
unsigned cycles = 8;
|
|
int i = 0;
|
|
|
|
do {
|
|
cycles += this->_writeOneByte(this->_aAddress.raw, 0x2100 | (this->_port + this->_getModeOffset(i)));
|
|
if (!this->_controlRegister.fixed)
|
|
this->_aAddress.page += this->_controlRegister.increment ? -1 : 1;
|
|
this->_count.raw--;
|
|
i++;
|
|
} while (this->_count.raw > 0 && this->enabled);
|
|
this->enabled = false;
|
|
return cycles;
|
|
}
|
|
|
|
int DMA::_getModeOffset(int index) const
|
|
{
|
|
switch (this->_controlRegister.mode) {
|
|
case OneToOne:
|
|
case TwoToOne:
|
|
case TwoToOneBis:
|
|
return 0;
|
|
|
|
case TwoToTwo:
|
|
case TwoToTwoBis:
|
|
return index % 2;
|
|
|
|
case FourToTwo:
|
|
case FourToTwoBis:
|
|
return (index & 0b11) > 1;
|
|
|
|
case FourToFour:
|
|
return (index & 0b11);
|
|
}
|
|
return 0;
|
|
}
|
|
}// namespace ComSquare::CPU
|