mirror of https://github.com/bsnes-emu/bsnes.git
89 lines
2.2 KiB
C++
89 lines
2.2 KiB
C++
auto CPU::dmaRun() -> void {
|
|
active.dma = true;
|
|
|
|
while(true) {
|
|
bool transferred = false;
|
|
for(auto n : range(4)) {
|
|
auto& dma = regs.dma[n];
|
|
if(dma.pending) {
|
|
dmaExecute(dma);
|
|
if(dma.control.irq) regs.irq.flag |= Interrupt::DMA0 << n;
|
|
if(dma.control.drq && n == 3) regs.irq.flag |= Interrupt::Cartridge;
|
|
transferred = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!transferred) break;
|
|
}
|
|
|
|
active.dma = false;
|
|
}
|
|
|
|
auto CPU::dmaExecute(Registers::DMA& dma) -> void {
|
|
uint seek = dma.control.size ? 4 : 2;
|
|
uint mode = dma.control.size ? Word : Half;
|
|
mode |= dma.run.length == dma.length ? Nonsequential : Sequential;
|
|
|
|
if(mode & Nonsequential) {
|
|
if((dma.source & 0x0800'0000) && (dma.target & 0x0800'0000)) {
|
|
//ROM -> ROM transfer
|
|
} else {
|
|
idle();
|
|
idle();
|
|
}
|
|
}
|
|
|
|
if(dma.run.source < 0x0200'0000) {
|
|
idle(); //cannot access BIOS
|
|
} else {
|
|
uint32 addr = dma.run.source;
|
|
if(mode & Word) addr &= ~3;
|
|
if(mode & Half) addr &= ~1;
|
|
dma.data = _read(mode, addr);
|
|
}
|
|
|
|
if(dma.run.target < 0x0200'0000) {
|
|
idle(); //cannot access BIOS
|
|
} else {
|
|
uint32 addr = dma.run.target;
|
|
if(mode & Word) addr &= ~3;
|
|
if(mode & Half) addr &= ~1;
|
|
_write(mode, addr, dma.data);
|
|
}
|
|
|
|
switch(dma.control.sourcemode) {
|
|
case 0: dma.run.source += seek; break;
|
|
case 1: dma.run.source -= seek; break;
|
|
}
|
|
|
|
switch(dma.control.targetmode) {
|
|
case 0: dma.run.target += seek; break;
|
|
case 1: dma.run.target -= seek; break;
|
|
case 3: dma.run.target += seek; break;
|
|
}
|
|
|
|
if(--dma.run.length == 0) {
|
|
dma.pending = false;
|
|
if(dma.control.targetmode == 3) dma.run.target = dma.target;
|
|
if(dma.control.repeat == 1) dma.run.length = dma.length;
|
|
if(dma.control.repeat == 0) dma.control.enable = false;
|
|
}
|
|
}
|
|
|
|
auto CPU::dmaVblank() -> void {
|
|
for(auto& dma : regs.dma) {
|
|
if(dma.control.enable && dma.control.timingmode == 1) dma.pending = true;
|
|
}
|
|
}
|
|
|
|
auto CPU::dmaHblank() -> void {
|
|
for(auto& dma : regs.dma) {
|
|
if(dma.control.enable && dma.control.timingmode == 2) dma.pending = true;
|
|
}
|
|
}
|
|
|
|
auto CPU::dmaHDMA() -> void {
|
|
auto& dma = regs.dma[3];
|
|
if(dma.control.enable && dma.control.timingmode == 3) dma.pending = true;
|
|
}
|