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
