In my emulator, when you start a DMA fill operation, it sets a "wait" flag, which prevents the DMA from running until you later write to the VDP data port, $c00000, which signals the byte to be used for the fill operation in the top eight bits.
However, Ballz 3D never writes to the data port. It instead enters a loop polling ($c00005).d1, or the VDP busy flag, which for higan maps to command.bit(5) or CD5. CD5 stays set because the DMA isn't running.
But I did notice at the very end, that last move.w (a0),(a1) ends up writing to the control port and hitting the commandPending block of code, where I clear the wait flag for VDP DMA copy operations, since there is no need to obtain a fill byte first. If I modify that code to clear the wait flag here, then the game ... gets past this point in code, but the graphics are still garbled all throughout the game.
So my first attempt:
Code: Select all
auto VDP::writeDataPort(uint16 data) -> void {
io.commandPending = false;
//DMA VRAM fill
if(dma.io.wait) {
dma.io.wait = false;
dma.io.fill = data >> 8;
//falls through to memory write
//causes extra transfer to occur on VRAM fill operations
}
Code: Select all
auto VDP::writeControlPort(uint16 data) -> void {
//command write (lo)
if(io.commandPending) {
io.commandPending = false;
io.command.bits(2,5) = data.bits(4,7);
io.address.bits(14,15) = data.bits(0,1);
if(!dma.io.enable) io.command.bit(5) = 0;
if(dma.io.mode == 3) dma.io.wait = false; //*** change this to if(dma.io.mode >= 2) ***
return;
}
//command write (hi)
if(data.bits(14,15) != 2) {
io.commandPending = true;
io.command.bits(0,1) = data.bits(14,15);
io.address.bits(0,13) = data.bits(0,13);
return;
}
//register write (d13 is ignored)
if(data.bits(14,15) == 2)
switch(data.bits(8,12)) {
...
//DMA source
case 0x17: {
dma.io.source.bits(16,21) = data.bits(0,5);
dma.io.mode = data.bits(6,7);
dma.io.wait = dma.io.mode.bit(1);
return;
}
}
}
Code: Select all
auto VDP::readControlPort() -> uint16 {
io.commandPending = false;
uint16 result = 0b0011'0100'0000'0000;
result |= Region::PAL() << 0;
result |= (io.command.bit(5) && !dma.io.wait) << 1; //DMA active
result |= (state.hcounter >= 1280) << 2; //horizontal blank
result |= (state.vcounter >= screenHeight()) << 3; //vertical blank
result |= io.interlaceMode.bit(0) ? state.field << 4 : 0;
result |= vip << 7;
result |= 1 << 9; //FIFO empty
return result;
}
A) will a write to the control port possibly clear the wait flag like it does for VDP DMA copy operations? or
B) will $c00004 reads return 0 for d1 (DMA active) when the wait flag is set? or
C) am I totally wrong and is there a third option here?
Ballz 3D trace log: you can't see the values because of reading from (a0), but suffice to say it's setting up and executing a DMA fill operation.
Code: Select all
00267a 2298 move.l (a0)+,(a1) 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvZnx 00ff125a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
00267c 2298 move.l (a0)+,(a1) 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvzNx 00ff125e 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
00267e 2298 move.l (a0)+,(a1) 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvzNx 00ff1262 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
002680 2298 move.l (a0)+,(a1) 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvzNx 00ff1266 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
002682 4e75 rts 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvzNx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
0025ea 3290 move.w (a0),(a1) 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvzNx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff6 00000000
0025ec 6100 bsr $002684 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvznx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff6 00000000
002684 08b9 bclr.b #$04,($ff125b) 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvznx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
00268c 0829 btst.b #$01,($c00005) 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvznx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
002692 66f8 bne $00268c 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvznx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
00268c 0829 btst.b #$01,($c00005) 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvznx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
002692 66f8 bne $00268c 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvznx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
00268c 0829 btst.b #$01,($c00005) 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvznx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000
002692 66f8 bne $00268c 00000000 00ff4000 471c0083 00000000 00000000 000094ff 0000ffff 00000018 tS7cvznx 00ff126a 00c00004 00ff9630 00c00000 00c00004 00000722 00ff2868 00fffff2 00000000