GBA.Unity/Assets/emulator/Gba.cs

218 lines
6.7 KiB
C#

using System;
namespace OptimeGBA
{
public unsafe sealed class Gba
{
public ProviderGba Provider;
public Scheduler Scheduler;
public AudioCallback AudioCallback;
public MemoryGba Mem;
public Arm7 Cpu;
public GbaAudio GbaAudio;
public Keypad Keypad;
public PpuGba Ppu;
public HwControlGba HwControl;
public DmaGba Dma;
public Timers Timers;
public Gba(ProviderGba provider)
{
Provider = provider;
Scheduler = new Scheduler();
Mem = new MemoryGba(this, provider);
GbaAudio = new GbaAudio(this, Scheduler);
Ppu = new PpuGba(this, Scheduler);
Keypad = new Keypad();
Dma = new DmaGba(this);
Timers = new Timers(GbaAudio, HwControl, Scheduler, false, true);
HwControl = new HwControlGba(this);
Cpu = new Arm7(StateChange, Mem, false, false, null);
Cpu.SetTimingsTable(
Cpu.Timing8And16,
1, // BIOS
1, // Unused
3, // EWRAM
1, // IWRAM
1, // I/O Registers
1, // PPU Palettes
1, // PPU VRAM
1, // PPU OAM
5, // Game Pak ROM/FlashROM
5, // Game Pak ROM/FlashROM
5, // Game Pak ROM/FlashROM
5, // Game Pak ROM/FlashROM
5, // Game Pak ROM/FlashROM
5, // Game Pak ROM/FlashROM
5, // Game Pak SRAM/Flash
5 // Game Pak SRAM/Flash
);
Cpu.SetTimingsTable(
Cpu.Timing32,
1, // BIOS
1, // Unused
6, // EWRAM
1, // IWRAM
1, // I/O Registers
2, // PPU Palettes
2, // PPU VRAM
1, // PPU OAM
8, // Game Pak ROM/FlashROM
8, // Game Pak ROM/FlashROM
8, // Game Pak ROM/FlashROM
8, // Game Pak ROM/FlashROM
8, // Game Pak ROM/FlashROM
8, // Game Pak ROM/FlashROM
8, // Game Pak SRAM/Flash
8 // Game Pak SRAM/Flash
);
Cpu.SetTimingsTable(
Cpu.Timing8And16InstrFetch,
1, // BIOS
1, // Unused
3, // EWRAM
1, // IWRAM
1, // I/O Registers
1, // PPU Palettes
1, // PPU VRAM
1, // PPU OAM
// Compensate for no prefetch buffer 5 -> 2
2, // Game Pak ROM/FlashROM
2, // Game Pak ROM/FlashROM
2, // Game Pak ROM/FlashROM
2, // Game Pak ROM/FlashROM
2, // Game Pak ROM/FlashROM
2, // Game Pak ROM/FlashROM
5, // Game Pak SRAM/Flash
5 // Game Pak SRAM/Flash
);
Cpu.SetTimingsTable(
Cpu.Timing32InstrFetch,
1, // BIOS
1, // Unused
6, // EWRAM
1, // IWRAM
1, // I/O Registers
2, // PPU Palettes
2, // PPU VRAM
1, // PPU OAM
// Compensate for no prefetch buffer 8 -> 4
4, // Game Pak ROM/FlashROM
4, // Game Pak ROM/FlashROM
4, // Game Pak ROM/FlashROM
4, // Game Pak ROM/FlashROM
4, // Game Pak ROM/FlashROM
4, // Game Pak ROM/FlashROM
8, // Game Pak SRAM/Flash
8 // Game Pak SRAM/Flash
);
if (!provider.BootBios)
{
Cpu.SetModeReg(13, Arm7Mode.SVC, 0x03007FE0);
Cpu.SetModeReg(13, Arm7Mode.IRQ, 0x03007FA0);
Cpu.SetModeReg(13, Arm7Mode.USR, 0x03007F00);
// Default Stack Pointer
Cpu.R[13] = Cpu.GetModeReg(13, Arm7Mode.USR);
Cpu.R[15] = 0x08000000;
}
AudioCallback = provider.AudioCallback;
Mem.InitPageTables();
Cpu.InitFlushPipeline();
#if UNSAFE
Console.WriteLine("Starting in memory UNSAFE mode");
#else
Console.WriteLine("Starting in memory SAFE mode");
#endif
}
public uint Step()
{
Cpu.CheckInterrupts();
long beforeTicks = Scheduler.CurrentTicks;
if (!Cpu.ThumbState)
{
Scheduler.CurrentTicks += Cpu.ExecuteArm();
}
else
{
Scheduler.CurrentTicks += Cpu.ExecuteThumb();
}
while (Scheduler.CurrentTicks >= Scheduler.NextEventTicks)
{
long current = Scheduler.CurrentTicks;
long next = Scheduler.NextEventTicks;
Scheduler.PopFirstEvent().Callback(current - next);
}
return (uint)(Scheduler.CurrentTicks - beforeTicks);
}
public void DoNothing(long cyclesLate) { }
public void StateChange()
{
Scheduler.AddEventRelative(SchedulerId.None, 0, DoNothing);
}
public uint StateStep()
{
Cpu.CheckInterrupts();
long beforeTicks = Scheduler.CurrentTicks;
if (!Cpu.ThumbState)
{
while (Scheduler.CurrentTicks < Scheduler.NextEventTicks)
{
Scheduler.CurrentTicks += Cpu.ExecuteArm();
}
}
else
{
while (Scheduler.CurrentTicks < Scheduler.NextEventTicks)
{
Scheduler.CurrentTicks += Cpu.ExecuteThumb();
}
}
while (Scheduler.CurrentTicks >= Scheduler.NextEventTicks)
{
long current = Scheduler.CurrentTicks;
long next = Scheduler.NextEventTicks;
Scheduler.PopFirstEvent().Callback(current - next);
}
// Return cycles executed
return (uint)(Scheduler.CurrentTicks - beforeTicks);
}
public void Tick(uint cycles)
{
Scheduler.CurrentTicks += cycles;
}
public void HaltSkip(long cyclesLate)
{
long before = Scheduler.CurrentTicks;
while (!HwControl.Available)
{
long ticksPassed = Scheduler.NextEventTicks - Scheduler.CurrentTicks;
Scheduler.CurrentTicks = Scheduler.NextEventTicks;
Scheduler.PopFirstEvent().Callback(0);
}
}
}
}