From 0ddd7392c7bed9841930eef7b37815f486e17a46 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 17 Jun 2017 13:55:21 -0400 Subject: [PATCH] A7800Hawk Commit 5 - Almost done with DMA --- .../Atari/A7800Hawk/A7800Hawk.IEmulator.cs | 8 + .../Consoles/Atari/A7800Hawk/Maria.cs | 184 ++++++++++++++++-- 2 files changed, 180 insertions(+), 12 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs index d66887478e..e0c6169241 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs @@ -71,7 +71,15 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk if (cpu_cycle == (4 + (slow_access ? 2 : 0))) { if (!cpu_is_halted) + { cpu.ExecuteOne(); + } + else + { + // we still want to keep track of CPU time even if it is halted, so increment the counter here + // The basic 6502 has no halt state, this feature is specific to SALLY + cpu.TotalExecutedCycles++; + } cpu_cycle = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs index 25d717cb47..fd9f314194 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/Maria.cs @@ -1,6 +1,7 @@ using System; using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { @@ -9,6 +10,23 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { public A7800Hawk Core { get; set; } + struct GFX_Object + { + public byte palette; + public byte width; + public ushort addr; + public byte h_pos; + + // additional entries used only in 5-byte header mode + public bool write_mode; + public bool ind_mode; + public bool exp_mode; + } + + // technically there is no limit on he number of graphics objects, but since dma is automatically killed + // at the end of a scanline, we have an effective limit + GFX_Object[] GFX_Objects = new GFX_Object[64]; + public int _frameHz = 60; public int _screen_width = 320; public int _screen_height = 263; @@ -35,6 +53,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk public int cycle; public int scanline; public bool sl_DMA_complete; + public bool do_dma; public int DMA_phase = 0; public int DMA_phase_counter; @@ -46,10 +65,21 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk public static int DMA_SHUTDOWN_OTHER = 4; public static int DMA_SHUTDOWN_LAST = 5; - public byte list_low_byte; - public byte list_high_byte; + public int header_read_time = 8; // default for 4 byte headers (10 for 5 bytes ones) + public int DMA_phase_next; + public int base_scanline; + public ushort display_zone_pointer; + public int display_zone_counter; + public byte current_DLL_offset; + public ushort current_DLL_addr; + public bool current_DLL_DLI; + public bool current_DLL_H16; + public bool current_DLL_H8; + + public int header_counter; + public int header_pointer; // since headers could be 4 or 5 bytes, we need a seperate pointer // each frame contains 263 scanlines // each scanline consists of 113.5 CPU cycles (fast access) which equates to 454 Maria cycles @@ -80,8 +110,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // "The end of vblank is made up of a DMA startup plus a long shut down" sl_DMA_complete = false; - DMA_phase = DMA_START_UP; - DMA_phase_counter = 0; + do_dma = false; for (int i=0; i<454;i++) { @@ -89,14 +118,16 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { // DMA doesn't start until 7 CPU cycles into a scanline } - else if (i==28) + else if (i==28 && Core.Maria_regs[0x1C].Bit(6) && !Core.Maria_regs[0x1C].Bit(6)) { Core.cpu_halt_pending = true; + DMA_phase = DMA_START_UP; DMA_phase_counter = 0; + do_dma = true; } - else if (!sl_DMA_complete) + else if (!sl_DMA_complete && do_dma) { - RunDMA(i - 28, true); + RunDMA(true); } Core.RunCPUCycle(); @@ -104,14 +135,31 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk scanline++; cycle = 0; - - Core.Maria_regs[8] = 0; // we have now left VBLank + do_dma = false; + Core.Maria_regs[8] = 0; // we have now left VBLank' + base_scanline = 0; + sl_DMA_complete = false; // Now proceed with the remaining scanlines // the first one is a pre-render line, since we didn't actually put any data into the buffer yet while (scanline < 263) { - + + if (cycle < 28) + { + // DMA doesn't start until 7 CPU cycles into a scanline + } + else if (cycle == 28 && Core.Maria_regs[0x1C].Bit(6) && !Core.Maria_regs[0x1C].Bit(6)) + { + Core.cpu_halt_pending = true; + DMA_phase = DMA_START_UP; + DMA_phase_counter = 0; + do_dma = true; + } + else if (!sl_DMA_complete && do_dma) + { + RunDMA(false); + } Core.RunCPUCycle(); @@ -123,11 +171,13 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk cycle = 0; Core.tia._hsyncCnt = 0; Core.cpu.RDY = true; + do_dma = false; + sl_DMA_complete = false; } } } - public void RunDMA(int c, bool short_dma) + public void RunDMA(bool short_dma) { // During DMA the CPU is HALTED, This appears to happen on the falling edge of Phi2 // Current implementation is that a HALT request must be acknowledged in phi1 @@ -140,7 +190,14 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk { DMA_phase_counter = 0; if (short_dma) + { DMA_phase = DMA_SHUTDOWN_LAST; + + // also here we load up the display list list + // is the timing correct? + display_zone_pointer = (ushort)((Core.Maria_regs[0xC] << 8) | Core.Maria_regs[0x10]); + display_zone_counter = -1; + } else { DMA_phase = DMA_HEADER; @@ -154,16 +211,119 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk // get all the data from the display list header if (DMA_phase_counter==1) { + header_counter++; + GFX_Objects[header_counter].addr = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + header_pointer++; + byte temp = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + // if there is no width, then we must have an extended header + // or at the end of this list + if ((temp & 0x1F) == 0) + { + if ((temp & 0xE0) == 0) + { + // at the end of the list, time to end the DMA + // check if we are at the end of the zone + if (scanline == base_scanline + current_DLL_offset) + { + DMA_phase_next = DMA_SHUTDOWN_LAST; + } + else + { + DMA_phase_next = DMA_SHUTDOWN_OTHER; + } + header_read_time = 8; + } + else + { + // we are in 5 Byte header mode + GFX_Objects[header_counter].write_mode = temp.Bit(7); + GFX_Objects[header_counter].ind_mode = temp.Bit(5); + header_pointer++; + temp = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + GFX_Objects[header_counter].addr |= (ushort)(temp << 8); + header_pointer++; + temp = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + GFX_Objects[header_counter].width = (byte)(temp & 0x1F); + GFX_Objects[header_counter].palette = (byte)((temp & 0xE0) >> 5); + header_pointer++; + GFX_Objects[header_pointer].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + header_pointer++; + + GFX_Objects[header_pointer].exp_mode = true; + DMA_phase_next = DMA_GRAPHICS; + + header_read_time = 10; + } + } + else + { + GFX_Objects[header_counter].width = (byte)(temp & 0x1F); + GFX_Objects[header_counter].palette = (byte)((temp & 0xE0) >> 5); + header_pointer++; + temp = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + GFX_Objects[header_counter].addr |= (ushort)(temp << 8); + header_pointer++; + GFX_Objects[header_pointer].h_pos = ReadMemory((ushort)(current_DLL_addr + header_pointer)); + header_pointer++; + + GFX_Objects[header_pointer].exp_mode = false; + DMA_phase_next = DMA_GRAPHICS; + + header_read_time = 8; + } } + else if (DMA_phase_counter == header_read_time) + { + DMA_phase_counter = 0; + DMA_phase = DMA_phase_next; + } + return; + } + + if (DMA_phase == DMA_GRAPHICS) + { + if (DMA_phase_counter == 1) + { + // get all the graphics data + } + + if (DMA_phase_counter == 3) + { + DMA_phase = DMA_SHUTDOWN_OTHER; + DMA_phase_counter = 0; + } + return; + } + + if (DMA_phase == DMA_SHUTDOWN_OTHER) + { + Core.cpu_resume_pending = true; + sl_DMA_complete = true; + return; } if (DMA_phase == DMA_SHUTDOWN_LAST) { - if (DMA_phase_counter==4) + if (DMA_phase_counter==6) { Core.cpu_resume_pending = true; sl_DMA_complete = true; + + // on the last line of a zone, we load up the disply list list for the next zone. + display_zone_counter++; + ushort temp_addr = (ushort)(display_zone_pointer + 3 * display_zone_counter); + byte temp = ReadMemory(temp_addr); + current_DLL_addr = (ushort)(ReadMemory((ushort)(temp_addr + 1)) << 8); + current_DLL_addr |= ReadMemory((ushort)(temp_addr + 2)); + + current_DLL_offset = (byte)(temp & 0xF + 1); + current_DLL_DLI = temp.Bit(7); + current_DLL_H16 = temp.Bit(6); + current_DLL_H8 = temp.Bit(5); + + header_counter = -1; + header_pointer = 0; } return; }