470 lines
9.2 KiB
C#
470 lines
9.2 KiB
C#
//#define ARM_DEBUG
|
|
|
|
namespace GarboDev
|
|
{
|
|
using System;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Timers;
|
|
using System.Windows.Forms;
|
|
using System.Collections.Generic;
|
|
|
|
using BizHawk.Emulation;
|
|
using BizHawk;
|
|
|
|
public class GbaManager : IEmulator, ISyncSoundProvider, IVideoProvider
|
|
{
|
|
public const int cpuFreq = 16 * 1024 * 1024;
|
|
|
|
private int framesRendered;
|
|
|
|
private Arm7Processor arm7 = null;
|
|
private Memory memory = null;
|
|
private VideoManager videoManager = null;
|
|
private SoundManager soundManager = null;
|
|
|
|
private bool skipBios = false;
|
|
|
|
public delegate void CpuUpdateDelegate(Arm7Processor processor, Memory memory);
|
|
private event CpuUpdateDelegate onCpuUpdate = null;
|
|
|
|
public Arm7Processor Arm7
|
|
{
|
|
get { return this.arm7; }
|
|
}
|
|
|
|
public VideoManager VideoManager
|
|
{
|
|
get { return this.videoManager; }
|
|
}
|
|
|
|
public SoundManager SoundManager
|
|
{
|
|
get { return this.soundManager; }
|
|
}
|
|
|
|
public Memory Memory
|
|
{
|
|
get { return this.memory; }
|
|
}
|
|
|
|
public Dictionary<uint, bool> Breakpoints
|
|
{
|
|
get { return this.arm7.Breakpoints; }
|
|
}
|
|
|
|
public ushort KeyState
|
|
{
|
|
get
|
|
{
|
|
if (this.memory != null)
|
|
{
|
|
return this.memory.KeyState;
|
|
}
|
|
|
|
return 0x3FF;
|
|
}
|
|
|
|
set
|
|
{
|
|
this.arm7.KeyState = value;
|
|
}
|
|
}
|
|
|
|
public int FramesRendered
|
|
{
|
|
get { return this.framesRendered; }
|
|
set { this.framesRendered = value; }
|
|
}
|
|
|
|
public event CpuUpdateDelegate OnCpuUpdate
|
|
{
|
|
add
|
|
{
|
|
this.onCpuUpdate += value;
|
|
this.onCpuUpdate(this.arm7, this.memory);
|
|
}
|
|
remove
|
|
{
|
|
this.onCpuUpdate -= value;
|
|
}
|
|
}
|
|
|
|
public bool SkipBios
|
|
{
|
|
get { return this.skipBios; }
|
|
set { this.skipBios = value; }
|
|
}
|
|
|
|
public GbaManager(CoreComm comm)
|
|
{
|
|
_corecomm = comm;
|
|
|
|
this.memory = new Memory();
|
|
this.arm7 = new Arm7Processor(this.memory);
|
|
this.videoManager = new VideoManager(this);
|
|
this.videoManager.Memory = this.memory;
|
|
this.soundManager = new SoundManager(this.memory, 44100);
|
|
|
|
this.framesRendered = 0;
|
|
Renderer renderer = new Renderer();
|
|
renderer.Initialize(null);
|
|
VideoManager.Renderer = renderer;
|
|
|
|
videoManager.Presenter = delegate(uint[] data)
|
|
{
|
|
Buffer.BlockCopy(data, 0, this.vbuf, 0, 240 * 160 * 4);
|
|
};
|
|
}
|
|
|
|
public void Load(byte[] rom, byte[] bios)
|
|
{
|
|
LoadBios(bios);
|
|
LoadRom(rom);
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
//this.Halt();
|
|
|
|
this.arm7.Reset(this.skipBios);
|
|
this.memory.Reset();
|
|
this.videoManager.Reset();
|
|
}
|
|
|
|
public void LoadState(BinaryReader state)
|
|
{
|
|
}
|
|
|
|
public void SaveState(BinaryWriter state)
|
|
{
|
|
state.Write("GARB");
|
|
}
|
|
|
|
public void LoadBios(byte[] biosRom)
|
|
{
|
|
this.memory.LoadBios(biosRom);
|
|
|
|
if (this.onCpuUpdate != null)
|
|
{
|
|
this.onCpuUpdate(this.arm7, this.memory);
|
|
}
|
|
}
|
|
|
|
public void LoadRom(byte[] cartRom)
|
|
{
|
|
//this.Halt();
|
|
|
|
/*
|
|
byte[] logo = new byte[]
|
|
{
|
|
0x24,0xff,0xae,0x51,0x69,0x9a,0xa2,0x21,
|
|
0x3d,0x84,0x82,0x0a,0x84,0xe4,0x09,0xad,
|
|
0x11,0x24,0x8b,0x98,0xc0,0x81,0x7f,0x21,
|
|
0xa3,0x52,0xbe,0x19,0x93,0x09,0xce,0x20,
|
|
0x10,0x46,0x4a,0x4a,0xf8,0x27,0x31,0xec,
|
|
0x58,0xc7,0xe8,0x33,0x82,0xe3,0xce,0xbf,
|
|
0x85,0xf4,0xdf,0x94,0xce,0x4b,0x09,0xc1,
|
|
0x94,0x56,0x8a,0xc0,0x13,0x72,0xa7,0xfc,
|
|
0x9f,0x84,0x4d,0x73,0xa3,0xca,0x9a,0x61,
|
|
0x58,0x97,0xa3,0x27,0xfc,0x03,0x98,0x76,
|
|
0x23,0x1d,0xc7,0x61,0x03,0x04,0xae,0x56,
|
|
0xbf,0x38,0x84,0x00,0x40,0xa7,0x0e,0xfd,
|
|
0xff,0x52,0xfe,0x03,0x6f,0x95,0x30,0xf1,
|
|
0x97,0xfb,0xc0,0x85,0x60,0xd6,0x80,0x25,
|
|
0xa9,0x63,0xbe,0x03,0x01,0x4e,0x38,0xe2,
|
|
0xf9,0xa2,0x34,0xff,0xbb,0x3e,0x03,0x44,
|
|
0x78,0x00,0x90,0xcb,0x88,0x11,0x3a,0x94,
|
|
0x65,0xc0,0x7c,0x63,0x87,0xf0,0x3c,0xaf,
|
|
0xd6,0x25,0xe4,0x8b,0x38,0x0a,0xac,0x72,
|
|
0x21,0xd4,0xf8,0x07
|
|
};
|
|
|
|
Array.Copy(logo, 0, cartRom, 4, logo.Length);
|
|
cartRom[0xB2] = 0x96;
|
|
cartRom[0xBD] = 0;
|
|
for (int i = 0xA0; i <= 0xBC; i++) cartRom[0xBD] = (byte)(cartRom[0xBD] - cartRom[i]);
|
|
cartRom[0xBD] = (byte)((cartRom[0xBD] - 0x19) & 0xFF);
|
|
*/
|
|
this.memory.LoadCartridge(cartRom);
|
|
|
|
this.Reset();
|
|
|
|
if (this.onCpuUpdate != null)
|
|
{
|
|
this.onCpuUpdate(this.arm7, this.memory);
|
|
}
|
|
}
|
|
|
|
public void Step()
|
|
{
|
|
//this.Halt();
|
|
|
|
this.arm7.Step();
|
|
|
|
if (this.onCpuUpdate != null)
|
|
{
|
|
this.onCpuUpdate(this.arm7, this.memory);
|
|
}
|
|
}
|
|
|
|
public void StepScanline()
|
|
{
|
|
//this.Halt();
|
|
|
|
this.arm7.Execute(960);
|
|
this.videoManager.RenderLine();
|
|
this.videoManager.EnterHBlank(this.arm7);
|
|
this.arm7.Execute(272);
|
|
this.videoManager.LeaveHBlank(this.arm7);
|
|
|
|
if (this.onCpuUpdate != null)
|
|
{
|
|
this.onCpuUpdate(this.arm7, this.memory);
|
|
}
|
|
}
|
|
|
|
void UpdateInputState()
|
|
{
|
|
ushort ret = 0;
|
|
if (_controller["Up"]) ret |= 64;
|
|
if (_controller["Down"]) ret |= 128;
|
|
if (_controller["Left"]) ret |= 32;
|
|
if (_controller["Right"]) ret |= 16;
|
|
if (_controller["Select"]) ret |= 4;
|
|
if (_controller["Start"]) ret |= 8;
|
|
if (_controller["B"]) ret |= 2;
|
|
if (_controller["A"]) ret |= 1;
|
|
if (_controller["L"]) ret |= 512;
|
|
if (_controller["R"]) ret |= 256;
|
|
ret ^= 0x3ff;
|
|
KeyState = ret;
|
|
}
|
|
|
|
private void StepFrame()
|
|
{
|
|
UpdateInputState();
|
|
if (_controller["Power"])
|
|
Reset();
|
|
|
|
int vramCycles = 0;
|
|
bool inHblank = false;
|
|
|
|
//HighPerformanceTimer profileTimer = new HighPerformanceTimer();
|
|
|
|
while (true)
|
|
{
|
|
|
|
|
|
const int cycleStep = 123;
|
|
|
|
|
|
if (vramCycles <= 0)
|
|
{
|
|
if (inHblank)
|
|
{
|
|
vramCycles += 960;
|
|
bool HitVBlank = this.videoManager.LeaveHBlank(this.arm7);
|
|
inHblank = false;
|
|
if (HitVBlank)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
vramCycles += 272;
|
|
this.videoManager.RenderLine();
|
|
this.videoManager.EnterHBlank(this.arm7);
|
|
inHblank = true;
|
|
}
|
|
}
|
|
|
|
this.arm7.Execute(cycleStep);
|
|
#if ARM_DEBUG
|
|
if (this.arm7.BreakpointHit)
|
|
{
|
|
this.waitingToHalt = true;
|
|
Monitor.Wait(this);
|
|
}
|
|
#endif
|
|
vramCycles -= cycleStep;
|
|
this.arm7.FireIrq();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
IVideoProvider IEmulator.VideoProvider
|
|
{
|
|
get { return this; }
|
|
}
|
|
|
|
ISoundProvider IEmulator.SoundProvider
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
ISyncSoundProvider IEmulator.SyncSoundProvider
|
|
{
|
|
get { return this; }
|
|
}
|
|
|
|
bool IEmulator.StartAsyncSound()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void IEmulator.EndAsyncSound()
|
|
{
|
|
|
|
}
|
|
|
|
ControllerDefinition IEmulator.ControllerDefinition
|
|
{
|
|
get { return BizHawk.Emulation.Consoles.Nintendo.GBA.GBA.GBAController; }
|
|
}
|
|
|
|
IController _controller;
|
|
|
|
IController IEmulator.Controller
|
|
{
|
|
get { return _controller; }
|
|
set { _controller = value; }
|
|
}
|
|
|
|
void IEmulator.FrameAdvance(bool render, bool rendersound)
|
|
{
|
|
StepFrame();
|
|
}
|
|
|
|
int IEmulator.Frame
|
|
{
|
|
get { return 0; }
|
|
}
|
|
|
|
int IEmulator.LagCount
|
|
{
|
|
get
|
|
{
|
|
return 0;
|
|
}
|
|
set
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
bool IEmulator.IsLagFrame
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
string IEmulator.SystemId
|
|
{
|
|
get { return "GBA"; }
|
|
}
|
|
|
|
bool IEmulator.DeterministicEmulation
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
byte[] IEmulator.ReadSaveRam()
|
|
{
|
|
return new byte[0];
|
|
}
|
|
|
|
void IEmulator.StoreSaveRam(byte[] data)
|
|
{
|
|
|
|
}
|
|
|
|
void IEmulator.ClearSaveRam()
|
|
{
|
|
|
|
}
|
|
|
|
bool IEmulator.SaveRamModified
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
set
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
void IEmulator.ResetFrameCounter()
|
|
{
|
|
|
|
}
|
|
|
|
void IEmulator.SaveStateText(TextWriter writer)
|
|
{
|
|
|
|
}
|
|
|
|
void IEmulator.LoadStateText(TextReader reader)
|
|
{
|
|
|
|
}
|
|
|
|
void IEmulator.SaveStateBinary(BinaryWriter writer)
|
|
{
|
|
|
|
}
|
|
|
|
void IEmulator.LoadStateBinary(BinaryReader reader)
|
|
{
|
|
|
|
}
|
|
|
|
byte[] IEmulator.SaveStateBinary()
|
|
{
|
|
return new byte[16];
|
|
}
|
|
|
|
CoreComm _corecomm;
|
|
|
|
CoreComm IEmulator.CoreComm
|
|
{
|
|
get { return _corecomm; }
|
|
}
|
|
|
|
IList<MemoryDomain> IEmulator.MemoryDomains
|
|
{
|
|
get { return new List<MemoryDomain>(); }
|
|
}
|
|
|
|
MemoryDomain IEmulator.MainMemory
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
void IDisposable.Dispose()
|
|
{
|
|
|
|
}
|
|
|
|
int[] vbuf = new int[240 * 160];
|
|
int[] IVideoProvider.GetVideoBuffer() { return vbuf; }
|
|
int IVideoProvider.VirtualWidth { get { return 240; } }
|
|
int IVideoProvider.BufferWidth { get { return 240; } }
|
|
int IVideoProvider.BufferHeight { get { return 160; } }
|
|
int IVideoProvider.BackgroundColor { get { return unchecked((int)0xff000000); } }
|
|
|
|
void ISyncSoundProvider.GetSamples(out short[] samples, out int nsamp)
|
|
{
|
|
nsamp = soundManager.SamplesMixed / 2;
|
|
samples = new short[nsamp * 2];
|
|
soundManager.GetSamples(samples, nsamp * 2);
|
|
}
|
|
|
|
void ISyncSoundProvider.DiscardSamples()
|
|
{
|
|
// should implement
|
|
}
|
|
}
|
|
}
|