218 lines
6.7 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
} |