A7800Hawk commit #4

-Start Maria DMA and frame execution
-Refactor CPU execution
-Expose more tools (Hex Editor, RAM Search, etc)
-Add Ready and Halt behaviours trigggered by Maria
This commit is contained in:
alyosha-tas 2017-06-16 16:44:57 -04:00 committed by GitHub
parent 93254b4b6e
commit 7bb76f9f03
6 changed files with 216 additions and 54 deletions

View File

@ -54,7 +54,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
}
}
public IMemoryCallbackSystem MemoryCallbacks { get; private set; }
public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem();
public bool CanStep(StepType type)
{

View File

@ -12,7 +12,10 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
//Maria related variables
public int cycle;
public int cpu_cycle;
public int scanline;
public bool cpu_is_haltable;
public bool cpu_is_halted;
public bool cpu_halt_pending;
public bool cpu_resume_pending;
// there are 4 maria cycles in a CPU cycle (fast access, both NTSC and PAL)
// if the 6532 or TIA are accessed (PC goes to one of those addresses) the next access will be slower by 1/2 a CPU cycle
@ -47,58 +50,67 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
// read the controller state here for now
GetControllerState(controller);
scanline = 0;
maria.RunFrame();
}
// actually execute the frame
while (scanline < 263)
public void RunCPUCycle()
{
cpu_cycle++;
tia._hsyncCnt++;
if (cpu_cycle <= (2 + (slow_access ? 1 : 0)))
{
maria.Execute(cycle, scanline);
cycle++;
cpu_cycle++;
tia._hsyncCnt++;
cpu_is_haltable = true;
} else
{
cpu_is_haltable = false;
}
// the time a cpu cycle takes depends on the status of the address bus
// any address in range of the TIA or m6532 takes an extra cycle to complete
if (cpu_cycle==(4 + (slow_access ? 2 : 0)))
{
// the time a cpu cycle takes depends on the status of the address bus
// any address in range of the TIA or m6532 takes an extra cycle to complete
if (cpu_cycle == (4 + (slow_access ? 2 : 0)))
{
if (!cpu_is_halted)
cpu.ExecuteOne();
cpu_cycle = 0;
}
// determine if the next access will be fast or slow
if (cpu.PC < 0x0400)
cpu_cycle = 0;
if (cpu_halt_pending)
{
if ((cpu.PC & 0xFF) < 0x20)
{
if ((A7800_control_register & 0x1) == 0 && (cpu.PC < 0x20))
{
slow_access = false;
}
else
{
slow_access = true;
}
}
else if (cpu.PC < 0x300)
{
slow_access = true;
}
else
{
slow_access = false;
}
cpu_halt_pending = false;
cpu_is_halted = true;
}
if (cycle == 454)
if (cpu_resume_pending)
{
scanline++;
cycle = 0;
tia._hsyncCnt = 0;
cpu_resume_pending = false;
cpu_is_halted = false;
}
}
// determine if the next access will be fast or slow
if (cpu.PC < 0x0400)
{
if ((cpu.PC & 0xFF) < 0x20)
{
if ((A7800_control_register & 0x1) == 0 && (cpu.PC < 0x20))
{
slow_access = false;
}
else
{
slow_access = true;
}
}
else if (cpu.PC < 0x300)
{
slow_access = true;
}
else
{
slow_access = false;
}
}
}
private void GetControllerState(IController controller)

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;

View File

@ -127,18 +127,20 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
if (_isPAL)
{
maria._frameHz = 50;
maria._screen_width = 454;
maria._screen_width = 320;
maria._screen_height = 313;
maria._palette = PALPalette;
}
else
{
maria._frameHz = 60;
maria._screen_width = 454;
maria._screen_width = 320;
maria._screen_height = 263;
maria._palette = NTSCPalette;
}
maria.Core = this;
ser.Register<IVideoProvider>(maria);
ser.Register<ISoundProvider>(tia);
ServiceProvider = ser;
@ -146,6 +148,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
_tracer = new TraceBuffer { Header = cpu.TraceHeader };
ser.Register<ITraceable>(_tracer);
SetupMemoryDomains();
HardReset();
}
@ -174,7 +177,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
private void ExecFetch(ushort addr)
{
//MemoryCallbacks.CallExecutes(addr);
MemoryCallbacks.CallExecutes(addr);
}
private void Reset_Mapper(string m)

View File

@ -7,8 +7,10 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
// Emulates the Atari 7800 Maria graphics chip
public class Maria : IVideoProvider
{
public A7800Hawk Core { get; set; }
public int _frameHz = 60;
public int _screen_width = 454;
public int _screen_width = 320;
public int _screen_height = 263;
public int[] _vidbuffer;
@ -19,9 +21,9 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
return _vidbuffer;
}
public int VirtualWidth => 454;
public int VirtualWidth => 320;
public int VirtualHeight => _screen_height;
public int BufferWidth => 454;
public int BufferWidth => 320;
public int BufferHeight => _screen_height;
public int BackgroundColor => unchecked((int)0xff000000);
public int VsyncNumerator => _frameHz;
@ -30,11 +32,145 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
// the Maria chip can directly access memory
public Func<ushort, byte> ReadMemory;
public int cycle;
public int scanline;
public bool sl_DMA_complete;
public int DMA_phase = 0;
public int DMA_phase_counter;
public static int DMA_START_UP = 0;
public static int DMA_HEADER = 1;
public static int DMA_GRAPHICS = 2;
public static int DMA_CHAR_MAP = 3;
public static int DMA_SHUTDOWN_OTHER = 4;
public static int DMA_SHUTDOWN_LAST = 5;
public byte list_low_byte;
public byte list_high_byte;
// each frame contains 263 scanlines
// each scanline consists of 113.5 CPU cycles (fast access) which equates to 454 Maria cycles
// In total there are 29850.5 CPU cycles (fast access) in a frame
public void Execute(int cycle, int scanline)
public void RunFrame()
{
scanline = 0;
Core.Maria_regs[8] = 0x80; // indicates VBlank state
// we start off in VBlank for 20 scanlines
// at the end of vblank is a DMA to set up the display for the start of drawing
// this is free time for the CPU to set up display lists
while (scanline < 19)
{
Core.RunCPUCycle();
cycle++;
if (cycle == 454)
{
scanline++;
cycle = 0;
Core.tia._hsyncCnt = 0;
Core.cpu.RDY = true;
}
}
// "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;
for (int i=0; i<454;i++)
{
if (i<28)
{
// DMA doesn't start until 7 CPU cycles into a scanline
}
else if (i==28)
{
Core.cpu_halt_pending = true;
DMA_phase_counter = 0;
}
else if (!sl_DMA_complete)
{
RunDMA(i - 28, true);
}
Core.RunCPUCycle();
}
scanline++;
cycle = 0;
Core.Maria_regs[8] = 0; // we have now left VBLank
// 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)
{
Core.RunCPUCycle();
cycle++;
if (cycle == 454)
{
scanline++;
cycle = 0;
Core.tia._hsyncCnt = 0;
Core.cpu.RDY = true;
}
}
}
public void RunDMA(int c, 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
// if the CPU is now in halted state, start DMA
if (Core.cpu_is_halted)
{
DMA_phase_counter++;
if (DMA_phase_counter==2 && DMA_phase==DMA_START_UP)
{
DMA_phase_counter = 0;
if (short_dma)
DMA_phase = DMA_SHUTDOWN_LAST;
else
{
DMA_phase = DMA_HEADER;
}
return;
}
if (DMA_phase == DMA_HEADER)
{
// get all the data from the display list header
if (DMA_phase_counter==1)
{
}
}
if (DMA_phase == DMA_SHUTDOWN_LAST)
{
if (DMA_phase_counter==4)
{
Core.cpu_resume_pending = true;
sl_DMA_complete = true;
}
return;
}
}
}

View File

@ -2,7 +2,6 @@
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.M6502;
namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
@ -10,6 +9,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
{
public byte ReadMemory(ushort addr)
{
MemoryCallbacks.CallReads(addr);
if (addr < 0x0400) {
if ((addr & 0xFF) < 0x20)
{
@ -85,6 +86,8 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
public void WriteMemory(ushort addr, byte value)
{
MemoryCallbacks.CallWrites(addr);
if (addr < 0x0400)
{
if ((addr & 0xFF) < 0x20)
@ -92,18 +95,25 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk
// return TIA registers or control register if it is still unlocked
if ((A7800_control_register & 0x1) == 0 && (addr < 0x20))
{
A7800_control_register = value; // TODO: what to return here?
A7800_control_register = value;
}
else
{
TIA_regs[addr] = value; // TODO: what to return here?
TIA_regs[addr] = value;
}
}
else if ((addr & 0xFF) < 0x40)
{
if ((A7800_control_register & 0x2) > 0)
{
Maria_regs[(addr & 0x3F) - 0x20] = value;
var temp = (addr & 0x3F) - 0x20;
// register 8 is read only and controlled by Maria
if (temp != 8)
Maria_regs[temp] = value;
if (temp==4) // WSYNC
cpu.RDY = false;
}
}
else if (addr < 0x100)