remove all meteor and garbo stuff completely. These two GBA cores were never up to par, really.

This commit is contained in:
nattthebear 2017-04-23 13:21:35 -04:00
parent cc5ff74689
commit 72808f01bb
106 changed files with 2 additions and 47778 deletions

View File

@ -413,7 +413,7 @@
<Compile Include="Consoles\Intellivision\ICart.cs" />
<Compile Include="Consoles\Intellivision\Intellicart.cs" />
<Compile Include="Consoles\Intellivision\Intellivision.IInputPollable.cs">
<DependentUpon>Intellivision.cs</DependentUpon>
<DependentUpon>Intellivision.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Intellivision\Intellivision.ISettable.cs">
<DependentUpon>Intellivision.cs</DependentUpon>
@ -491,26 +491,10 @@
<Compile Include="Consoles\Nintendo\Gameboy\GBDisassembler.cs" />
<Compile Include="Consoles\Nintendo\Gameboy\LibGambatte.cs" />
<Compile Include="Consoles\Nintendo\GBA\ArmV4Disassembler.cs" />
<Compile Include="Consoles\Nintendo\GBA\GBA.cs" />
<Compile Include="Consoles\Nintendo\GBA\IGBAGPUViewable.cs" />
<Compile Include="Consoles\Nintendo\GBA\LibMeteor.cs" />
<Compile Include="Consoles\Nintendo\GBA\LibmGBA.cs" />
<Compile Include="Consoles\Nintendo\GBA\LibVBANext.cs" />
<Compile Include="Consoles\Nintendo\GBA\Meteor.cs" />
<Compile Include="Consoles\Nintendo\GBA\Meteor.IGBAGPUViewable.cs">
<DependentUpon>Meteor.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\Meteor.IMemoryDomains.cs">
<DependentUpon>Meteor.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\Meteor.ISaveRam.cs">
<DependentUpon>Meteor.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\Meteor.IStatable.cs">
<DependentUpon>Meteor.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\Meteor.IVideoProvider.cs">
<DependentUpon>Meteor.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.cs" />
<Compile Include="Consoles\Nintendo\GBA\VBANext.cs" />
<Compile Include="Consoles\Nintendo\GBA\VBANext.IDebuggable.cs">

View File

@ -1,294 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
/// <summary>
/// bindings into libmeteor.dll
/// </summary>
public static class LibMeteor
{
/// <summary>
/// power cycle the emulation core
/// </summary>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_hardreset();
/// <summary>
/// signal that you are removing data from the sound buffer.
/// the next time frameadvance() is called, writing will start from the beginning
/// </summary>
/// <returns>the valid length of the buffer, in bytes</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint libmeteor_emptysound();
/// <summary>
/// set up buffers for libmeteor to dump data to. these must be valid before every frameadvance
/// </summary>
/// <param name="vid">buffer to hold video data as BGRA32</param>
/// <param name="vidlen">length in bytes. must be at least 240 * 160 * 4</param>
/// <param name="aud">buffer to hold audio data as stereo s16le</param>
/// <param name="audlen">length in bytes. must be 0 mod 4 (hold a full stereo sample set)</param>
/// <returns>false if some problem. buffers will not be valid in this case</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_setbuffers(IntPtr vid, uint vidlen, IntPtr aud, uint audlen);
/// <summary>
/// initialize the library
/// </summary>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_init();
/// <summary>
/// run emulation for one frame, updating sound and video along the way
/// </summary>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_frameadvance();
/// <summary>
/// load a rom image
/// </summary>
/// <param name="data">raw rom data. need not persist past this call</param>
/// <param name="datalen">length of data in bytes</param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_loadrom(byte[] data, uint datalen);
/// <summary>
/// load a bios image
/// </summary>
/// <param name="data">raw bios data. need not persist past this call</param>
/// <param name="datalen">length of data in bytes</param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_loadbios(byte[] data, uint datalen);
/// <summary>
/// core callback to print meaningful (or meaningless) log messages
/// </summary>
/// <param name="msg">message to be printed</param>
/// <param name="abort">true if emulation should be aborted</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MessageCallback(string msg, bool abort);
/// <summary>
/// set callback for log messages. this can (and should) be called first
/// </summary>
/// <param name="cb"></param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_setmessagecallback(MessageCallback cb);
/// <summary>
/// combination of button flags used by the key callback
/// </summary>
[Flags]
public enum Buttons : ushort
{
BTN_A = 0x001,
BTN_B = 0x002,
BTN_SELECT = 0x004,
BTN_START = 0x008,
BTN_RIGHT = 0x010,
BTN_LEFT = 0x020,
BTN_UP = 0x040,
BTN_DOWN = 0x080,
BTN_R = 0x100,
BTN_L = 0x200
}
/// <summary>
/// core callback to get input state
/// </summary>
/// <returns>buttons pressed bitfield</returns>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate Buttons InputCallback();
/// <summary>
/// set callback for whenever input is requested
/// </summary>
/// <param name="callback"></param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_setkeycallback(InputCallback callback);
/// <summary>
/// parameter to libmeteor_getmemoryarea
/// </summary>
public enum MemoryArea
{
/// <summary>
/// BIOS, may be invalid if bios not loaded. valid size: 16K. system bus: @00000000h
/// </summary>
bios = 0,
/// <summary>
/// external workram. valid size: 256K. system bus: @02000000h
/// </summary>
ewram = 1,
/// <summary>
/// internal workram. valid size: 32K. system bus: @03000000h
/// </summary>
iwram = 2,
/// <summary>
/// palettes. valid size: 1K. system bus: @05000000h
/// </summary>
palram = 3,
/// <summary>
/// video ram. valid size: 96K. system bus: @06000000h
/// </summary>
vram = 4,
/// <summary>
/// sprite attribute ram. valid size: 1K. system bus: @07000000h
/// </summary>
oam = 5,
/// <summary>
/// rom. always valid to full size, even if no rom or small rom loaded. valid size: 32M. system bus: @08000000h, others
/// </summary>
rom = 6,
/// <summary>
/// direct access to cached io port values. this should NEVER be modified! valid size: 4K. system bus: @04000000h (sort of)
/// </summary>
io = 7
}
/// <summary>
/// return a pointer to a memory area
/// </summary>
/// <param name="which"></param>
/// <returns>IntPtr.Zero if which is unrecognized</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr libmeteor_getmemoryarea(MemoryArea which);
/// <summary>
/// core callback for tracelogging
/// </summary>
/// <param name="msg">disassembly of an instruction about to be run</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TraceCallback(string msg);
/// <summary>
/// set callback to run before each instruction is executed
/// </summary>
/// <param name="callback">null to clear</param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_settracecallback(TraceCallback callback);
/// <summary>
/// load saveram from a byte buffer
/// </summary>
/// <param name="data"></param>
/// <param name="size"></param>
/// <returns>success</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_loadsaveram(byte[] data, uint size);
/// <summary>
/// save saveram to a byte buffer
/// </summary>
/// <param name="data">buffer generated by core. copy from, but do not modify</param>
/// <param name="size">length of buffer</param>
/// <returns>success</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_savesaveram(ref IntPtr data, ref uint size);
/// <summary>
/// destroy a buffer previously returned by libmeteor_savesaveram() to avoid leakage
/// </summary>
/// <param name="data"></param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_savesaveram_destroy(IntPtr data);
/// <summary>
/// return true if there is saveram installed on currently loaded cart
/// </summary>
/// <returns></returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_hassaveram();
/// <summary>
/// resets the current cart's saveram
/// </summary>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_clearsaveram();
/// <summary>
/// serialize state
/// </summary>
/// <param name="data">buffer generated by core</param>
/// <param name="size">size of buffer</param>
/// <returns>success</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_savestate(ref IntPtr data, ref uint size);
/// <summary>
/// destroy a buffer previously returned by libmeteor_savestate() to avoid leakage
/// </summary>
/// <param name="data"></param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_savestate_destroy(IntPtr data);
/// <summary>
/// unserialize state
/// </summary>
/// <param name="data"></param>
/// <param name="size"></param>
/// <returns>success</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_loadstate(byte[] data, uint size);
/// <summary>
/// read a byte off the system bus. guaranteed to have no side effects
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte libmeteor_peekbus(uint addr);
/// <summary>
/// write a byte to the system bus.
/// </summary>
/// <param name="addr"></param>
/// <param name="val"></param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_writebus(uint addr, byte val);
/// <summary>
/// type of the scanline callback
/// </summary>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ScanlineCallback();
/// <summary>
/// set a callback to coincide with vcount interrupts
/// </summary>
/// <param name="callback">null to clear</param>
/// <param name="scanline">0-227, 160 occurring first in a frame</param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_setscanlinecallback(ScanlineCallback callback, int scanline);
/// <summary>
/// get current cpu regs
/// </summary>
/// <param name="dest">length 18 please</param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_getregs(int[] dest);
public static readonly string[] regnames = new string[]
{
"r0",
"r1",
"r2",
"r3",
"r4",
"r5",
"r6",
"r7",
"r8",
"r9",
"r10",
"r11",
"r12",
"r13",
"r14",
"r15",
"cpsr",
"spsr"
};
}
}

View File

@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
public partial class GBA : IGBAGPUViewable
{
public GBAGPUMemoryAreas GetMemoryAreas()
{
IntPtr _vram = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.vram);
IntPtr _palram = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.palram);
IntPtr _oam = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.oam);
IntPtr _mmio = LibMeteor.libmeteor_getmemoryarea(LibMeteor.MemoryArea.io);
if (_vram == IntPtr.Zero || _palram == IntPtr.Zero || _oam == IntPtr.Zero || _mmio == IntPtr.Zero)
throw new Exception("libmeteor_getmemoryarea() failed!");
return new GBAGPUMemoryAreas
{
vram = _vram,
palram = _palram,
oam = _oam,
mmio = _mmio
};
}
public void SetScanlineCallback(Action callback, int scanline)
{
if (scanline < 0 || scanline > 227)
{
throw new ArgumentOutOfRangeException(nameof(scanline), "Scanline must be in [0, 227]!");
}
if (callback == null)
{
scanlinecb = null;
LibMeteor.libmeteor_setscanlinecallback(null, 0);
}
else
{
scanlinecb = new LibMeteor.ScanlineCallback(callback);
LibMeteor.libmeteor_setscanlinecallback(scanlinecb, scanline);
}
}
private LibMeteor.ScanlineCallback scanlinecb = null;
}
}

View File

@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
public partial class GBA
{
private List<MemoryDomain> _domainList = new List<MemoryDomain>();
private IMemoryDomains _memoryDomains;
private void AddMemoryDomain(LibMeteor.MemoryArea which, int size, string name)
{
IntPtr data = LibMeteor.libmeteor_getmemoryarea(which);
if (data == IntPtr.Zero)
throw new Exception("libmeteor_getmemoryarea() returned NULL??");
MemoryDomain md = MemoryDomain.FromIntPtr(name, size, MemoryDomain.Endian.Little, data);
_domainList.Add(md);
}
private void SetUpMemoryDomains()
{
_domainList.Clear();
// this must be first to coincide with "main memory"
// note that ewram could also be considered main memory depending on which hairs you split
AddMemoryDomain(LibMeteor.MemoryArea.iwram, 32 * 1024, "IWRAM");
AddMemoryDomain(LibMeteor.MemoryArea.ewram, 256 * 1024, "EWRAM");
AddMemoryDomain(LibMeteor.MemoryArea.bios, 16 * 1024, "BIOS");
AddMemoryDomain(LibMeteor.MemoryArea.palram, 1024, "PALRAM");
AddMemoryDomain(LibMeteor.MemoryArea.vram, 96 * 1024, "VRAM");
AddMemoryDomain(LibMeteor.MemoryArea.oam, 1024, "OAM");
// even if the rom is less than 32MB, the whole is still valid in meteor
AddMemoryDomain(LibMeteor.MemoryArea.rom, 32 * 1024 * 1024, "ROM");
// special domain for system bus
{
MemoryDomain sb = new MemoryDomainDelegate("System Bus", 1 << 28, MemoryDomain.Endian.Little,
delegate(long addr)
{
if (addr < 0 || addr >= 0x10000000)
throw new IndexOutOfRangeException();
return LibMeteor.libmeteor_peekbus((uint)addr);
},
delegate(long addr, byte val)
{
if (addr < 0 || addr >= 0x10000000)
throw new IndexOutOfRangeException();
LibMeteor.libmeteor_writebus((uint)addr, val);
}, 4);
_domainList.Add(sb);
}
// special combined ram memory domain
{
var ew = _domainList[1];
var iw = _domainList[0];
MemoryDomain cr = new MemoryDomainDelegate("Combined WRAM", (256 + 32) * 1024, MemoryDomain.Endian.Little,
delegate(long addr)
{
if (addr < 0 || addr >= (256 + 32) * 1024)
throw new IndexOutOfRangeException();
if (addr >= 256 * 1024)
return iw.PeekByte(addr & 32767);
else
return ew.PeekByte(addr);
},
delegate(long addr, byte val)
{
if (addr < 0 || addr >= (256 + 32) * 1024)
throw new IndexOutOfRangeException();
if (addr >= 256 * 1024)
iw.PokeByte(addr & 32767, val);
else
ew.PokeByte(addr, val);
}, 4);
_domainList.Add(cr);
}
_memoryDomains = new MemoryDomainList(_domainList);
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(_memoryDomains);
}
}
}

View File

@ -1,48 +0,0 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
public partial class GBA : ISaveRam
{
public bool SaveRamModified
{
get
{
if (disposed)
throw new ObjectDisposedException(this.GetType().ToString());
return LibMeteor.libmeteor_hassaveram();
}
}
public byte[] CloneSaveRam()
{
throw new Exception("This needs to be fixed to match the VBANext Core!");
#if false
if (disposed)
throw new ObjectDisposedException(this.GetType().ToString());
if (!LibMeteor.libmeteor_hassaveram())
return null;
IntPtr data = IntPtr.Zero;
uint size = 0;
if (!LibMeteor.libmeteor_savesaveram(ref data, ref size))
throw new Exception("libmeteor_savesaveram() returned false!");
byte[] ret = new byte[size];
Marshal.Copy(data, ret, 0, (int)size);
LibMeteor.libmeteor_savesaveram_destroy(data);
return ret;
#endif
}
public void StoreSaveRam(byte[] data)
{
throw new Exception("This needs to be fixed to match the VBANext Core!");
#if false
if (disposed)
throw new ObjectDisposedException(this.GetType().ToString());
if (!LibMeteor.libmeteor_loadsaveram(data, (uint)data.Length))
throw new Exception("libmeteor_loadsaveram() returned false!");
#endif
}
}
}

View File

@ -1,83 +0,0 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
public partial class GBA : IStatable
{
public bool BinarySaveStatesPreferred { get { return true; } }
public void SaveStateText(System.IO.TextWriter writer)
{
var temp = SaveStateBinary();
temp.SaveAsHex(writer);
// write extra copy of stuff we don't use
writer.WriteLine("Frame {0}", Frame);
}
public void LoadStateText(System.IO.TextReader reader)
{
string hex = reader.ReadLine();
byte[] state = new byte[hex.Length / 2];
state.ReadFromHex(hex);
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
}
public void SaveStateBinary(System.IO.BinaryWriter writer)
{
byte[] data = SaveCoreBinary();
writer.Write(data.Length);
writer.Write(data);
// other variables
writer.Write(IsLagFrame);
writer.Write(LagCount);
writer.Write(Frame);
}
public void LoadStateBinary(System.IO.BinaryReader reader)
{
int length = reader.ReadInt32();
byte[] data = reader.ReadBytes(length);
LoadCoreBinary(data);
// other variables
IsLagFrame = reader.ReadBoolean();
LagCount = reader.ReadInt32();
Frame = reader.ReadInt32();
}
public byte[] SaveStateBinary()
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
private byte[] SaveCoreBinary()
{
IntPtr ndata = IntPtr.Zero;
uint nsize = 0;
if (!LibMeteor.libmeteor_savestate(ref ndata, ref nsize))
throw new Exception("libmeteor_savestate() failed!");
if (ndata == IntPtr.Zero || nsize == 0)
throw new Exception("libmeteor_savestate() returned bad!");
byte[] ret = new byte[nsize];
Marshal.Copy(ndata, ret, 0, (int)nsize);
LibMeteor.libmeteor_savestate_destroy(ndata);
return ret;
}
private void LoadCoreBinary(byte[] data)
{
if (!LibMeteor.libmeteor_loadstate(data, (uint)data.Length))
throw new Exception("libmeteor_loadstate() failed!");
}
}
}

View File

@ -1,27 +0,0 @@
using System;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
public partial class GBA : IVideoProvider
{
public int VirtualWidth { get { return 240; } }
public int VirtualHeight { get { return 160; } }
public int BufferWidth { get { return 240; } }
public int BufferHeight { get { return 160; } }
public int BackgroundColor
{
get { return unchecked((int)0xff000000); }
}
public int[] GetVideoBuffer()
{
return videobuffer;
}
private int[] videobuffer;
private GCHandle videohandle;
}
}

View File

@ -1,336 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
[CoreAttributes(
"Meteor",
"blastrock",
isPorted: true,
isReleased: false,
singleInstance: true
)]
[ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))]
public partial class GBA : IEmulator, IVideoProvider, ISoundProvider, IGBAGPUViewable, ISaveRam, IStatable, IInputPollable
{
[CoreConstructor("GBA")]
public GBA(CoreComm comm, byte[] file)
{
ServiceProvider = new BasicServiceProvider(this);
Tracer = new TraceBuffer
{
Header = " -Addr--- -Opcode- -Instruction------------------- -R0----- -R1----- -R2----- -R3----- -R4----- -R5----- -R6----- -R7----- -R8----- -R9----- -R10---- -R11---- -R12---- -R13(SP) -R14(LR) -R15(PC) -CPSR--- -SPSR---"
};
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(Tracer);
CoreComm = comm;
comm.VsyncNum = 262144;
comm.VsyncDen = 4389;
comm.NominalWidth = 240;
comm.NominalHeight = 160;
byte[] bios = CoreComm.CoreFileProvider.GetFirmware("GBA", "Bios", true, "GBA bios file is mandatory.");
if (bios.Length != 16384)
throw new InvalidDataException("GBA bios must be exactly 16384 bytes!");
if (file.Length > 32 * 1024 * 1024)
throw new InvalidDataException("Rom file is too big! No GBA game is larger than 32MB");
Init();
LibMeteor.libmeteor_hardreset();
LibMeteor.libmeteor_loadbios(bios, (uint)bios.Length);
LibMeteor.libmeteor_loadrom(file, (uint)file.Length);
SetUpMemoryDomains();
}
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
var ret = new Dictionary<string, RegisterValue>();
int[] data = new int[LibMeteor.regnames.Length];
LibMeteor.libmeteor_getregs(data);
for (int i = 0; i < data.Length; i++)
ret.Add(LibMeteor.regnames[i], data[i]);
return ret;
}
public static readonly ControllerDefinition GBAController =
new ControllerDefinition
{
Name = "GBA Controller",
BoolButtons =
{
"Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "L", "R", "Power"
},
FloatControls =
{
"Tilt X", "Tilt Y", "Tilt Z", "Light Sensor"
},
FloatRanges =
{
new[] { -32767f, 0f, 32767f },
new[] { -32767f, 0f, 32767f },
new[] { -32767f, 0f, 32767f },
new[] { 0f, 100f, 200f },
}
};
public ControllerDefinition ControllerDefinition { get { return GBAController; } }
public IController Controller { get; set; }
public void FrameAdvance(bool render, bool rendersound = true)
{
Frame++;
IsLagFrame = true;
if (Controller.IsPressed("Power"))
LibMeteor.libmeteor_hardreset();
// due to the design of the tracing api, we have to poll whether it's active each frame
LibMeteor.libmeteor_settracecallback(Tracer.Enabled ? tracecallback : null);
if (!coredead)
LibMeteor.libmeteor_frameadvance();
if (IsLagFrame)
LagCount++;
}
public int Frame { get; private set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
// TODO: optimize managed to unmanaged using the ActiveChanged event
public IInputCallbackSystem InputCallbacks { [FeatureNotImplemented]get { return _inputCallbacks; } }
private ITraceable Tracer { get; set; }
public string SystemId { get { return "GBA"; } }
public bool DeterministicEmulation { get { return true; } }
// todo: information about the saveram type would be useful here.
public string BoardName { get { return null; } }
public void ResetCounters()
{
Frame = 0;
LagCount = 0;
IsLagFrame = false;
}
public CoreComm CoreComm { get; private set; }
/// <summary>like libsnes, the library is single-instance</summary>
static GBA attachedcore;
/// <summary>hold pointer to message callback so it won't get GCed</summary>
LibMeteor.MessageCallback messagecallback;
/// <summary>hold pointer to input callback so it won't get GCed</summary>
LibMeteor.InputCallback inputcallback;
/// <summary>true if libmeteor aborted</summary>
bool coredead = false;
/// <summary>hold pointer to trace callback so it won't get GCed</summary>
LibMeteor.TraceCallback tracecallback;
LibMeteor.Buttons GetInput()
{
InputCallbacks.Call();
// libmeteor bitflips everything itself, so 0 == off, 1 == on
IsLagFrame = false;
LibMeteor.Buttons ret = 0;
if (Controller.IsPressed("Up")) ret |= LibMeteor.Buttons.BTN_UP;
if (Controller.IsPressed("Down")) ret |= LibMeteor.Buttons.BTN_DOWN;
if (Controller.IsPressed("Left")) ret |= LibMeteor.Buttons.BTN_LEFT;
if (Controller.IsPressed("Right")) ret |= LibMeteor.Buttons.BTN_RIGHT;
if (Controller.IsPressed("Select")) ret |= LibMeteor.Buttons.BTN_SELECT;
if (Controller.IsPressed("Start")) ret |= LibMeteor.Buttons.BTN_START;
if (Controller.IsPressed("B")) ret |= LibMeteor.Buttons.BTN_B;
if (Controller.IsPressed("A")) ret |= LibMeteor.Buttons.BTN_A;
if (Controller.IsPressed("L")) ret |= LibMeteor.Buttons.BTN_L;
if (Controller.IsPressed("R")) ret |= LibMeteor.Buttons.BTN_R;
return ret;
}
#region messagecallbacks
void PrintMessage(string msg, bool abort)
{
Console.Write(msg.Replace("\n", "\r\n"));
if (abort)
StopCore(msg);
}
void StopCore(string msg)
{
coredead = true;
Console.WriteLine("Core stopped.");
for (int i = 0; i < soundbuffer.Length; i++)
soundbuffer[i] = 0;
var gz = new System.IO.Compression.GZipStream(
new MemoryStream(Convert.FromBase64String(dispfont), false),
System.IO.Compression.CompressionMode.Decompress);
byte[] font = new byte[2048];
gz.Read(font, 0, 2048);
gz.Dispose();
// cores aren't supposed to have bad dependencies like System.Drawing, right?
int scx = 0;
int scy = 0;
foreach (char c in msg)
{
if (scx == 240 || c == '\n')
{
scy += 8;
scx = 0;
}
if (scy == 160)
break;
if (c == '\r' || c == '\n')
continue;
if (c < 256 && c != ' ')
{
int fpos = c * 8;
for (int j = 0; j < 8; j++)
{
for (int i = 0; i < 8; i++)
{
if ((font[fpos] >> i & 1) != 0)
videobuffer[(scy + j) * 240 + scx + i] = unchecked((int)0xffff0000);
else
videobuffer[(scy + j) * 240 + scx + i] = unchecked((int)0xff000000);
}
fpos++;
}
}
scx += 8;
}
}
const string dispfont =
"H4sICAInrFACAGZvby5yYXcARVU9q9RAFL2gjK8IT+0GDGoh1oGFGHDYQvwL2hoQroXhsdUqGGbxZ/gD" +
"bKys7BRhIZVYLgurIghvG3ksCPKKJfGcm1nfSTJn750792smWUmIr9++/vjmdYzDZlhuh1guFotpfiRH" +
"+dQ4n+aLxfOj/MgUR7mID8GLDMN2CftBgj54oEGG5ZuPH98sh93P3afJZHIzqGrw0e+/7LPs+OqVvuu7" +
"7vTZJb8J223Y+MtZHvLsstwuqlAVt+E1eh+DV0JU+s3mx3q9luCChjoIsVgI7Wg2kAHBQ1mkqPu6EBhk" +
"feYFcM5F0B0d9A74WtX2QvRtdU0SrBp6kaZpKIJ7XI341oV66sVp4TOtJS/L/IN+k8pnQkCbZb4QPEVB" +
"nYYhKB16JHZwbsZRDuBEDWsnEnQeTzSIz60CyHWV6cg19LOXjfb1TqKb1pSrzE0VHBUOvIed8ia3dZGb" +
"c96JM0ZhfgzPBPCbkWEPEs/4j+fO1kd2HM55Q0bf4PdmCW15E/HdFI1M7Dg/Z1xN64InguxqpGn6kkvF" +
"GaJ0Z32/6jrRkxjntFciMB79mTwPM5NLm0ffWac3iCb7kbx0XbfqzzqhEGBPLe2i9TVKmxGtiGPFIm1N" +
"tNj+ppMLDDl7Ywh1q62gPEUKlJX1Yw3k1uTo2P9sCseQW3Y80B4QLznrNwaOnbMGUDK9SNOvVgxt9vQH" +
"gj51IPn7SdlRFDt4MoarIGvKwyoFd6tV34CtAWTLRySiAZF5Oq5DcHvyAvuO8/FtLgZrRNcf9tlp8l/4" +
"sc64HPuhMnLmR/Z3jA/9cbAzexVj2CU59SPYD+rJyU6VfsiIh5NtL+j+/b7cyzmOu+op1wXrjzHXG2Ow" +
"Qikba6pbgwL0P7J4y89XDRsY7ZxEXLcmkydP/zz9NVv74P2N4yLVVaT8wIxDNv9NaRtG1pM5kinLVqKY" +
"ERndzXhOgOicGNe1yPLp5NUXnezAm99//3ymoX0xodQvsMKoE5GH18fr3aPx+v5ivPwFbt1KIx9VffYM" +
"g30GyUkPbV1zJgGzJpt+sWAxGEWSHwH4izg/hwAeBjEMw0GPweTDfNLyUWzSqdroXN+L9L1z1Gy3tsKe" +
"7Zbzpj/oOE+9P8iq5j/Nj/HUQK+S4omkuMJIaqD3g5+xQ2KwvIcEKshXE3YJNkfgjbg7/8YNLbV0Lqo6" +
"AFEaQqJmPlM7n+l9VeDHJTm57wGJPtjRwhg53+LD1DRnMvNFO9q3q9WqFfncnq6+tm7mszbzM4QziERe" +
"h7+LyO+zz8AYfQGerdf+P27cOBYaeUubt1RNU138q4wg74qiuFeGKjQA5BwOgxABACX8A6+GHm0ACAAA";
#endregion
// Tracer refactor TODO - rehook up meteor, if it is worth it
//void Trace(string msg)
//{
// Tracer.Put(msg);
//}
private void Init()
{
if (attachedcore != null)
attachedcore.Dispose();
messagecallback = PrintMessage;
inputcallback = GetInput;
// Tracer refactor TODO - rehook up meteor, if it is worth it
//tracecallback = Trace; // don't set this callback now, only set if enabled
LibMeteor.libmeteor_setmessagecallback(messagecallback);
LibMeteor.libmeteor_setkeycallback(inputcallback);
LibMeteor.libmeteor_init();
videobuffer = new int[240 * 160];
videohandle = GCHandle.Alloc(videobuffer, GCHandleType.Pinned);
soundbuffer = new short[2048]; // nominal length of one frame is something like 1480 shorts?
soundhandle = GCHandle.Alloc(soundbuffer, GCHandleType.Pinned);
if (!LibMeteor.libmeteor_setbuffers
(videohandle.AddrOfPinnedObject(), (uint)(sizeof(int) * videobuffer.Length),
soundhandle.AddrOfPinnedObject(), (uint)(sizeof(short) * soundbuffer.Length)))
throw new Exception("libmeteor_setbuffers() returned false??");
attachedcore = this;
}
private bool disposed = false;
public void Dispose()
{
if (!disposed)
{
disposed = true;
videohandle.Free();
soundhandle.Free();
// guarantee crash if it gets accessed
LibMeteor.libmeteor_setbuffers(IntPtr.Zero, 240 * 160 * 4, IntPtr.Zero, 4);
messagecallback = null;
inputcallback = null;
tracecallback = null;
LibMeteor.libmeteor_setmessagecallback(messagecallback);
LibMeteor.libmeteor_setkeycallback(inputcallback);
LibMeteor.libmeteor_settracecallback(tracecallback);
_domainList.Clear();
}
}
#region ISoundProvider
short[] soundbuffer;
GCHandle soundhandle;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
uint nbytes = LibMeteor.libmeteor_emptysound();
samples = soundbuffer;
if (!coredead)
nsamp = (int)(nbytes / 4);
else
nsamp = 738;
}
public void DiscardSamples()
{
LibMeteor.libmeteor_emptysound();
}
public bool CanProvideAsync
{
get { return false; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
#endregion
}
}

View File

@ -1,501 +0,0 @@
namespace GarboDev
{
using System;
using System.Collections.Generic;
using System.Text;
public class Arm7Processor
{
private Memory memory = null;
private FastArmCore armCore = null;
private ThumbCore thumbCore = null;
private Dictionary<uint, bool> breakpoints = null;
private int cycles = 0;
private int timerCycles = 0;
private int soundCycles = 0;
// CPU mode definitions
public const uint USR = 0x10;
public const uint FIQ = 0x11;
public const uint IRQ = 0x12;
public const uint SVC = 0x13;
public const uint ABT = 0x17;
public const uint UND = 0x1B;
public const uint SYS = 0x1F;
// CPSR bit definitions
public const int N_BIT = 31;
public const int Z_BIT = 30;
public const int C_BIT = 29;
public const int V_BIT = 28;
public const int I_BIT = 7;
public const int F_BIT = 6;
public const int T_BIT = 5;
public const uint N_MASK = (uint)(1U << N_BIT);
public const uint Z_MASK = (uint)(1U << Z_BIT);
public const uint C_MASK = (uint)(1U << C_BIT);
public const uint V_MASK = (uint)(1U << V_BIT);
public const uint I_MASK = (uint)(1U << I_BIT);
public const uint F_MASK = (uint)(1U << F_BIT);
public const uint T_MASK = (uint)(1U << T_BIT);
// Standard registers
private uint[] registers = new uint[16];
private uint cpsr = 0;
// Banked registers
private uint[] bankedFIQ = new uint[7];
private uint[] bankedIRQ = new uint[2];
private uint[] bankedSVC = new uint[2];
private uint[] bankedABT = new uint[2];
private uint[] bankedUND = new uint[2];
// Saved CPSR's
private uint spsrFIQ = 0;
private uint spsrIRQ = 0;
private uint spsrSVC = 0;
private uint spsrABT = 0;
private uint spsrUND = 0;
private ushort keyState;
private bool breakpointHit = false;
private bool cpuHalted = false;
public ushort KeyState
{
set { this.keyState = value; }
}
public int Cycles
{
get { return this.cycles; }
set { this.cycles = value; }
}
public bool ArmState
{
get { return (this.cpsr & Arm7Processor.T_MASK) != Arm7Processor.T_MASK; }
}
public uint[] Registers
{
get { return this.registers; }
}
public uint CPSR
{
get { return this.cpsr; }
set { this.cpsr = value; }
}
public bool SPSRExists
{
get
{
switch (this.cpsr & 0x1F)
{
case USR:
case SYS:
return false;
case FIQ:
case SVC:
case ABT:
case IRQ:
case UND:
return true;
default:
return false;
}
}
}
public uint SPSR
{
get
{
switch (this.cpsr & 0x1F)
{
case USR:
case SYS:
return 0xFFFFFFFF;
case FIQ:
return this.spsrFIQ;
case SVC:
return this.spsrSVC;
case ABT:
return this.spsrABT;
case IRQ:
return this.spsrIRQ;
case UND:
return this.spsrUND;
default:
throw new Exception("Unhandled CPSR state...");
}
}
set
{
switch (this.cpsr & 0x1F)
{
case USR:
case SYS:
break;
case FIQ:
this.spsrFIQ = value;
break;
case SVC:
this.spsrSVC = value;
break;
case ABT:
this.spsrABT = value;
break;
case IRQ:
this.spsrIRQ = value;
break;
case UND:
this.spsrUND = value;
break;
default:
throw new Exception("Unhandled CPSR state...");
}
}
}
public Dictionary<uint, bool> Breakpoints
{
get
{
return this.breakpoints;
}
}
public bool BreakpointHit
{
get { return this.breakpointHit; }
set { this.breakpointHit = value; }
}
public Arm7Processor(Memory memory)
{
this.memory = memory;
this.memory.Processor = this;
this.armCore = new FastArmCore(this, this.memory);
this.thumbCore = new ThumbCore(this, this.memory);
this.breakpoints = new Dictionary<uint, bool>();
this.breakpointHit = false;
}
private void SwapRegsHelper(uint[] swapRegs)
{
for (int i = 14; i > 14 - swapRegs.Length; i--)
{
uint tmp = this.registers[i];
this.registers[i] = swapRegs[swapRegs.Length - (14 - i) - 1];
swapRegs[swapRegs.Length - (14 - i) - 1] = tmp;
}
}
private void SwapRegisters(uint bank)
{
switch (bank & 0x1F)
{
case FIQ:
this.SwapRegsHelper(this.bankedFIQ);
break;
case SVC:
this.SwapRegsHelper(this.bankedSVC);
break;
case ABT:
this.SwapRegsHelper(this.bankedABT);
break;
case IRQ:
this.SwapRegsHelper(this.bankedIRQ);
break;
case UND:
this.SwapRegsHelper(this.bankedUND);
break;
}
}
public void WriteCpsr(uint newCpsr)
{
if ((newCpsr & 0x1F) != (this.cpsr & 0x1F))
{
// Swap out the old registers
this.SwapRegisters(this.cpsr);
// Swap in the new registers
this.SwapRegisters(newCpsr);
}
this.cpsr = newCpsr;
}
public void EnterException(uint mode, uint vector, bool interruptsDisabled, bool fiqDisabled)
{
uint oldCpsr = this.cpsr;
if ((oldCpsr & Arm7Processor.T_MASK) != 0)
{
registers[15] += 2U;
}
// Clear T bit, and set mode
uint newCpsr = (oldCpsr & ~0x3FU) | mode;
if (interruptsDisabled) newCpsr |= 1 << 7;
if (fiqDisabled) newCpsr |= 1 << 6;
this.WriteCpsr(newCpsr);
this.SPSR = oldCpsr;
registers[14] = registers[15];
registers[15] = vector;
this.ReloadQueue();
}
public void RequestIrq(int irq)
{
ushort iflag = Memory.ReadU16(this.memory.IORam, Memory.IF);
iflag |= (ushort)(1 << irq);
Memory.WriteU16(this.memory.IORam, Memory.IF, iflag);
}
public void FireIrq()
{
ushort ime = Memory.ReadU16(this.memory.IORam, Memory.IME);
ushort ie = Memory.ReadU16(this.memory.IORam, Memory.IE);
ushort iflag = Memory.ReadU16(this.memory.IORam, Memory.IF);
if ((ie & (iflag)) != 0 && (ime & 1) != 0 && (this.cpsr & (1 << 7)) == 0)
{
// Off to the irq exception vector
this.EnterException(Arm7Processor.IRQ, 0x18, true, false);
}
}
public void Reset(bool skipBios)
{
this.breakpointHit = false;
this.cpuHalted = false;
// Default to ARM state
this.cycles = 0;
this.timerCycles = 0;
this.soundCycles = 0;
this.bankedSVC[0] = 0x03007FE0;
this.bankedIRQ[0] = 0x03007FA0;
this.cpsr = SYS;
this.spsrSVC = this.cpsr;
for (int i = 0; i < 15; i++) this.registers[i] = 0;
if (skipBios)
{
this.registers[15] = 0x8000000;
}
else
{
this.registers[15] = 0;
}
this.armCore.BeginExecution();
}
public void Halt()
{
this.cpuHalted = true;
this.cycles = 0;
}
public void Step()
{
this.breakpointHit = false;
if ((this.cpsr & Arm7Processor.T_MASK) == Arm7Processor.T_MASK)
{
this.thumbCore.Step();
}
else
{
this.armCore.Step();
}
this.UpdateTimers();
}
public void ReloadQueue()
{
if ((this.cpsr & Arm7Processor.T_MASK) == Arm7Processor.T_MASK)
{
this.thumbCore.BeginExecution();
}
else
{
this.armCore.BeginExecution();
}
}
private void UpdateTimer(int timer, int cycles, bool countUp)
{
ushort control = Memory.ReadU16(this.memory.IORam, Memory.TM0CNT + (uint)(timer * 4));
// Make sure timer is enabled, or count up is disabled
if ((control & (1 << 7)) == 0) return;
if (!countUp && (control & (1 << 2)) != 0) return;
if (!countUp)
{
switch (control & 3)
{
case 0: cycles *= 1 << 10; break;
case 1: cycles *= 1 << 4; break;
case 2: cycles *= 1 << 2; break;
// Don't need to do anything for case 3
}
}
this.memory.TimerCnt[timer] += (uint)cycles;
uint timerCnt = this.memory.TimerCnt[timer] >> 10;
if (timerCnt > 0xffff)
{
ushort soundCntX = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_X);
if ((soundCntX & (1 << 7)) != 0)
{
ushort soundCntH = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_H);
if (timer == ((soundCntH >> 10) & 1))
{
// FIFO A overflow
this.memory.SoundManager.DequeueA();
if (this.memory.SoundManager.QueueSizeA < 16)
{
this.memory.FifoDma(1);
// TODO
if (this.memory.SoundManager.QueueSizeA < 16)
{
}
}
}
if (timer == ((soundCntH >> 14) & 1))
{
// FIFO B overflow
this.memory.SoundManager.DequeueB();
if (this.memory.SoundManager.QueueSizeB < 16)
{
this.memory.FifoDma(2);
}
}
}
// Overflow, attempt to fire IRQ
if ((control & (1 << 6)) != 0)
{
this.RequestIrq(3 + timer);
}
if (timer < 3)
{
ushort control2 = Memory.ReadU16(this.memory.IORam, Memory.TM0CNT + (uint)((timer + 1) * 4));
if ((control2 & (1 << 2)) != 0)
{
// Count-up
this.UpdateTimer(timer + 1, (int)((timerCnt >> 16) << 10), true);
}
}
// Reset the original value
uint count = Memory.ReadU16(this.memory.IORam, Memory.TM0D + (uint)(timer * 4));
this.memory.TimerCnt[timer] = count << 10;
}
}
public void UpdateTimers()
{
int cycles = this.timerCycles - this.cycles;
for (int i = 0; i < 4; i++)
{
this.UpdateTimer(i, cycles, false);
}
this.timerCycles = this.cycles;
}
public void UpdateKeyState()
{
ushort KEYCNT = this.memory.ReadU16Debug(Memory.REG_BASE + Memory.KEYCNT);
if ((KEYCNT & (1 << 14)) != 0)
{
if ((KEYCNT & (1 << 15)) != 0)
{
KEYCNT &= 0x3FF;
if (((~this.keyState) & KEYCNT) == KEYCNT)
this.RequestIrq(12);
}
else
{
KEYCNT &= 0x3FF;
if (((~this.keyState) & KEYCNT) != 0)
this.RequestIrq(12);
}
}
this.memory.KeyState = this.keyState;
}
public void UpdateSound()
{
this.memory.SoundManager.Mix(this.soundCycles);
this.soundCycles = 0;
}
public void Execute(int cycles)
{
this.cycles += cycles;
this.timerCycles += cycles;
this.soundCycles += cycles;
this.breakpointHit = false;
if (this.cpuHalted)
{
ushort ie = Memory.ReadU16(this.memory.IORam, Memory.IE);
ushort iflag = Memory.ReadU16(this.memory.IORam, Memory.IF);
if ((ie & iflag) != 0)
{
this.cpuHalted = false;
}
else
{
this.cycles = 0;
this.UpdateTimers();
this.UpdateSound();
return;
}
}
while (this.cycles > 0)
{
if ((this.cpsr & Arm7Processor.T_MASK) == Arm7Processor.T_MASK)
{
this.thumbCore.Execute();
}
else
{
this.armCore.Execute();
}
this.UpdateTimers();
this.UpdateSound();
if (this.breakpointHit)
{
break;
}
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,469 +0,0 @@
//#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
}
}
}

View File

@ -1,13 +0,0 @@
namespace GarboDev
{
using System;
public interface IRenderer
{
Memory Memory { set; }
void Initialize(object data);
void Reset();
void RenderLine(int line);
uint[] ShowFrame();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,952 +0,0 @@
namespace GarboDev
{
using System;
using System.Collections.Generic;
using System.Text;
public partial class Renderer : IRenderer
{
private Memory memory;
private uint[] scanline = new uint[240];
private byte[] blend = new byte[240];
private byte[] windowCover = new byte[240];
private uint[] back = new uint[240 * 160];
//private uint[] front = new uint[240 * 160];
private const uint pitch = 240;
// Convenience variable as I use it everywhere, set once in RenderLine
private ushort dispCnt;
// Window helper variables
private byte win0x1, win0x2, win0y1, win0y2;
private byte win1x1, win1x2, win1y1, win1y2;
private byte win0Enabled, win1Enabled, winObjEnabled, winOutEnabled;
private bool winEnabled;
private byte blendSource, blendTarget;
private byte blendA, blendB, blendY;
private int blendType;
private int curLine = 0;
private static uint[] colorLUT;
static Renderer()
{
colorLUT = new uint[0x10000];
// Pre-calculate the color LUT
for (uint i = 0; i <= 0xFFFF; i++)
{
uint r = (i & 0x1FU);
uint g = (i & 0x3E0U) >> 5;
uint b = (i & 0x7C00U) >> 10;
r = (r << 3) | (r >> 2);
g = (g << 3) | (g >> 2);
b = (b << 3) | (b >> 2);
colorLUT[i] = (r << 16) | (g << 8) | b;
}
}
public Memory Memory
{
set { this.memory = value; }
}
public void Initialize(object data)
{
}
public void Reset()
{
}
public uint[] ShowFrame()
{
//Array.Copy(this.back, this.front, this.front.Length);
//return this.front;
return this.back;
}
public void RenderLine(int line)
{
this.curLine = line;
// Render the line
this.dispCnt = Memory.ReadU16(this.memory.IORam, Memory.DISPCNT);
if ((this.dispCnt & (1 << 7)) != 0)
{
uint bgColor = Renderer.GbaTo32((ushort)0x7FFF);
for (int i = 0; i < 240; i++) this.scanline[i] = bgColor;
}
else
{
this.winEnabled = false;
if ((this.dispCnt & (1 << 13)) != 0)
{
// Calculate window 0 information
ushort winy = Memory.ReadU16(this.memory.IORam, Memory.WIN0V);
this.win0y1 = (byte)(winy >> 8);
this.win0y2 = (byte)(winy & 0xff);
ushort winx = Memory.ReadU16(this.memory.IORam, Memory.WIN0H);
this.win0x1 = (byte)(winx >> 8);
this.win0x2 = (byte)(winx & 0xff);
if (this.win0x2 > 240 || this.win0x1 > this.win0x2)
{
this.win0x2 = 240;
}
if (this.win0y2 > 160 || this.win0y1 > this.win0y2)
{
this.win0y2 = 160;
}
this.win0Enabled = this.memory.IORam[Memory.WININ];
this.winEnabled = true;
}
if ((this.dispCnt & (1 << 14)) != 0)
{
// Calculate window 1 information
ushort winy = Memory.ReadU16(this.memory.IORam, Memory.WIN1V);
this.win1y1 = (byte)(winy >> 8);
this.win1y2 = (byte)(winy & 0xff);
ushort winx = Memory.ReadU16(this.memory.IORam, Memory.WIN1H);
this.win1x1 = (byte)(winx >> 8);
this.win1x2 = (byte)(winx & 0xff);
if (this.win1x2 > 240 || this.win1x1 > this.win1x2)
{
this.win1x2 = 240;
}
if (this.win1y2 > 160 || this.win1y1 > this.win1y2)
{
this.win1y2 = 160;
}
this.win1Enabled = this.memory.IORam[Memory.WININ + 1];
this.winEnabled = true;
}
if ((this.dispCnt & (1 << 15)) != 0 && (this.dispCnt & (1 << 12)) != 0)
{
// Object windows are enabled
this.winObjEnabled = this.memory.IORam[Memory.WINOUT + 1];
this.winEnabled = true;
}
if (this.winEnabled)
{
this.winOutEnabled = this.memory.IORam[Memory.WINOUT];
}
// Calculate blending information
ushort bldcnt = Memory.ReadU16(this.memory.IORam, Memory.BLDCNT);
this.blendType = (bldcnt >> 6) & 0x3;
this.blendSource = (byte)(bldcnt & 0x3F);
this.blendTarget = (byte)((bldcnt >> 8) & 0x3F);
ushort bldalpha = Memory.ReadU16(this.memory.IORam, Memory.BLDALPHA);
this.blendA = (byte)(bldalpha & 0x1F);
if (this.blendA > 0x10) this.blendA = 0x10;
this.blendB = (byte)((bldalpha >> 8) & 0x1F);
if (this.blendB > 0x10) this.blendB = 0x10;
this.blendY = (byte)(this.memory.IORam[Memory.BLDY] & 0x1F);
if (this.blendY > 0x10) this.blendY = 0x10;
switch (this.dispCnt & 0x7)
{
case 0: this.RenderMode0Line(); break;
case 1: this.RenderMode1Line(); break;
case 2: this.RenderMode2Line(); break;
case 3: this.RenderMode3Line(); break;
case 4: this.RenderMode4Line(); break;
case 5: this.RenderMode5Line(); break;
}
}
Array.Copy(this.scanline, 0, this.back, this.curLine * Renderer.pitch, Renderer.pitch);
}
private void DrawBackdrop()
{
byte[] palette = this.memory.PaletteRam;
// Initialize window coverage buffer if neccesary
if (this.winEnabled)
{
for (int i = 0; i < 240; i++)
{
this.windowCover[i] = this.winOutEnabled;
}
if ((this.dispCnt & (1 << 15)) != 0)
{
// Sprite window
this.DrawSpriteWindows();
}
if ((this.dispCnt & (1 << 14)) != 0)
{
// Window 1
if (this.curLine >= this.win1y1 && this.curLine < this.win1y2)
{
for (int i = this.win1x1; i < this.win1x2; i++)
{
this.windowCover[i] = this.win1Enabled;
}
}
}
if ((this.dispCnt & (1 << 13)) != 0)
{
// Window 0
if (this.curLine >= this.win0y1 && this.curLine < this.win0y2)
{
for (int i = this.win0x1; i < this.win0x2; i++)
{
this.windowCover[i] = this.win0Enabled;
}
}
}
}
// Draw backdrop first
uint bgColor = Renderer.GbaTo32((ushort)(palette[0] | (palette[1] << 8)));
uint modColor = bgColor;
if (this.blendType == 2 && (this.blendSource & (1 << 5)) != 0)
{
// Brightness increase
uint r = bgColor & 0xFF;
uint g = (bgColor >> 8) & 0xFF;
uint b = (bgColor >> 16) & 0xFF;
r = r + (((0xFF - r) * this.blendY) >> 4);
g = g + (((0xFF - g) * this.blendY) >> 4);
b = b + (((0xFF - b) * this.blendY) >> 4);
modColor = r | (g << 8) | (b << 16);
}
else if (this.blendType == 3 && (this.blendSource & (1 << 5)) != 0)
{
// Brightness decrease
uint r = bgColor & 0xFF;
uint g = (bgColor >> 8) & 0xFF;
uint b = (bgColor >> 16) & 0xFF;
r = r - ((r * this.blendY) >> 4);
g = g - ((g * this.blendY) >> 4);
b = b - ((b * this.blendY) >> 4);
modColor = r | (g << 8) | (b << 16);
}
if (this.winEnabled)
{
for (int i = 0; i < 240; i++)
{
if ((this.windowCover[i] & (1 << 5)) != 0)
{
this.scanline[i] = modColor;
}
else
{
this.scanline[i] = bgColor;
}
this.blend[i] = 1 << 5;
}
}
else
{
for (int i = 0; i < 240; i++)
{
this.scanline[i] = modColor;
this.blend[i] = 1 << 5;
}
}
}
private void RenderTextBg(int bg)
{
if (this.winEnabled)
{
switch (this.blendType)
{
case 0:
this.RenderTextBgWindow(bg);
break;
case 1:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgWindowBlend(bg);
else
this.RenderTextBgWindow(bg);
break;
case 2:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgWindowBrightInc(bg);
else
this.RenderTextBgWindow(bg);
break;
case 3:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgWindowBrightDec(bg);
else
this.RenderTextBgWindow(bg);
break;
}
}
else
{
switch (this.blendType)
{
case 0:
this.RenderTextBgNormal(bg);
break;
case 1:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgBlend(bg);
else
this.RenderTextBgNormal(bg);
break;
case 2:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgBrightInc(bg);
else
this.RenderTextBgNormal(bg);
break;
case 3:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderTextBgBrightDec(bg);
else
this.RenderTextBgNormal(bg);
break;
}
}
}
private void RenderRotScaleBg(int bg)
{
if (this.winEnabled)
{
switch (this.blendType)
{
case 0:
this.RenderRotScaleBgWindow(bg);
break;
case 1:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgWindowBlend(bg);
else
this.RenderRotScaleBgWindow(bg);
break;
case 2:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgWindowBrightInc(bg);
else
this.RenderRotScaleBgWindow(bg);
break;
case 3:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgWindowBrightDec(bg);
else
this.RenderRotScaleBgWindow(bg);
break;
}
}
else
{
switch (this.blendType)
{
case 0:
this.RenderRotScaleBgNormal(bg);
break;
case 1:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgBlend(bg);
else
this.RenderRotScaleBgNormal(bg);
break;
case 2:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgBrightInc(bg);
else
this.RenderRotScaleBgNormal(bg);
break;
case 3:
if ((this.blendSource & (1 << bg)) != 0)
this.RenderRotScaleBgBrightDec(bg);
else
this.RenderRotScaleBgNormal(bg);
break;
}
}
}
private void DrawSprites(int pri)
{
if (this.winEnabled)
{
switch (this.blendType)
{
case 0:
this.DrawSpritesWindow(pri);
break;
case 1:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesWindowBlend(pri);
else
this.DrawSpritesWindow(pri);
break;
case 2:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesWindowBrightInc(pri);
else
this.DrawSpritesWindow(pri);
break;
case 3:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesWindowBrightDec(pri);
else
this.DrawSpritesWindow(pri);
break;
}
}
else
{
switch (this.blendType)
{
case 0:
this.DrawSpritesNormal(pri);
break;
case 1:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesBlend(pri);
else
this.DrawSpritesNormal(pri);
break;
case 2:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesBrightInc(pri);
else
this.DrawSpritesNormal(pri);
break;
case 3:
if ((this.blendSource & (1 << 4)) != 0)
this.DrawSpritesBrightDec(pri);
else
this.DrawSpritesNormal(pri);
break;
}
}
}
private void RenderMode0Line()
{
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
for (int pri = 3; pri >= 0; pri--)
{
for (int i = 3; i >= 0; i--)
{
if ((this.dispCnt & (1 << (8 + i))) != 0)
{
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i);
if ((bgcnt & 0x3) == pri)
{
this.RenderTextBg(i);
}
}
}
this.DrawSprites(pri);
}
}
private void RenderMode1Line()
{
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
for (int pri = 3; pri >= 0; pri--)
{
if ((this.dispCnt & (1 << (8 + 2))) != 0)
{
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
if ((bgcnt & 0x3) == pri)
{
this.RenderRotScaleBg(2);
}
}
for (int i = 1; i >= 0; i--)
{
if ((this.dispCnt & (1 << (8 + i))) != 0)
{
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i);
if ((bgcnt & 0x3) == pri)
{
this.RenderTextBg(i);
}
}
}
this.DrawSprites(pri);
}
}
private void RenderMode2Line()
{
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
for (int pri = 3; pri >= 0; pri--)
{
for (int i = 3; i >= 2; i--)
{
if ((this.dispCnt & (1 << (8 + i))) != 0)
{
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i);
if ((bgcnt & 0x3) == pri)
{
this.RenderRotScaleBg(i);
}
}
}
this.DrawSprites(pri);
}
}
private void RenderMode3Line()
{
ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
byte blendMaskType = (byte)(1 << 2);
int bgPri = bg2Cnt & 0x3;
for (int pri = 3; pri > bgPri; pri--)
{
this.DrawSprites(pri);
}
if ((this.dispCnt & (1 << 10)) != 0)
{
// Background enabled, render it
int x = this.memory.Bgx[0];
int y = this.memory.Bgy[0];
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA);
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC);
for (int i = 0; i < 240; i++)
{
int ax = ((int)x) >> 8;
int ay = ((int)y) >> 8;
if (ax >= 0 && ax < 240 && ay >= 0 && ay < 160)
{
int curIdx = ((ay * 240) + ax) * 2;
this.scanline[i] = Renderer.GbaTo32((ushort)(vram[curIdx] | (vram[curIdx + 1] << 8)));
this.blend[i] = blendMaskType;
}
x += dx;
y += dy;
}
}
for (int pri = bgPri; pri >= 0; pri--)
{
this.DrawSprites(pri);
}
}
private void RenderMode4Line()
{
ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
byte blendMaskType = (byte)(1 << 2);
int bgPri = bg2Cnt & 0x3;
for (int pri = 3; pri > bgPri; pri--)
{
this.DrawSprites(pri);
}
if ((this.dispCnt & (1 << 10)) != 0)
{
// Background enabled, render it
int baseIdx = 0;
if ((this.dispCnt & (1 << 4)) == 1 << 4) baseIdx = 0xA000;
int x = this.memory.Bgx[0];
int y = this.memory.Bgy[0];
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA);
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC);
for (int i = 0; i < 240; i++)
{
int ax = ((int)x) >> 8;
int ay = ((int)y) >> 8;
if (ax >= 0 && ax < 240 && ay >= 0 && ay < 160)
{
int lookup = vram[baseIdx + (ay * 240) + ax];
if (lookup != 0)
{
this.scanline[i] = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
this.blend[i] = blendMaskType;
}
}
x += dx;
y += dy;
}
}
for (int pri = bgPri; pri >= 0; pri--)
{
this.DrawSprites(pri);
}
}
private void RenderMode5Line()
{
ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
this.DrawBackdrop();
byte blendMaskType = (byte)(1 << 2);
int bgPri = bg2Cnt & 0x3;
for (int pri = 3; pri > bgPri; pri--)
{
this.DrawSprites(pri);
}
if ((this.dispCnt & (1 << 10)) != 0)
{
// Background enabled, render it
int baseIdx = 0;
if ((this.dispCnt & (1 << 4)) == 1 << 4) baseIdx += 160 * 128 * 2;
int x = this.memory.Bgx[0];
int y = this.memory.Bgy[0];
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA);
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC);
for (int i = 0; i < 240; i++)
{
int ax = ((int)x) >> 8;
int ay = ((int)y) >> 8;
if (ax >= 0 && ax < 160 && ay >= 0 && ay < 128)
{
int curIdx = (int)(ay * 160 + ax) * 2;
this.scanline[i] = Renderer.GbaTo32((ushort)(vram[baseIdx + curIdx] | (vram[baseIdx + curIdx + 1] << 8)));
this.blend[i] = blendMaskType;
}
x += dx;
y += dy;
}
}
for (int pri = bgPri; pri >= 0; pri--)
{
this.DrawSprites(pri);
}
}
private void DrawSpriteWindows()
{
byte[] palette = this.memory.PaletteRam;
byte[] vram = this.memory.VideoRam;
// OBJ must be enabled in this.dispCnt
if ((this.dispCnt & (1 << 12)) == 0) return;
for (int oamNum = 127; oamNum >= 0; oamNum--)
{
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
// Not an object window, so continue
if (((attr0 >> 10) & 3) != 2) continue;
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
int x = attr1 & 0x1FF;
int y = attr0 & 0xFF;
int width = -1, height = -1;
switch ((attr0 >> 14) & 3)
{
case 0:
// Square
switch ((attr1 >> 14) & 3)
{
case 0: width = 8; height = 8; break;
case 1: width = 16; height = 16; break;
case 2: width = 32; height = 32; break;
case 3: width = 64; height = 64; break;
}
break;
case 1:
// Horizontal Rectangle
switch ((attr1 >> 14) & 3)
{
case 0: width = 16; height = 8; break;
case 1: width = 32; height = 8; break;
case 2: width = 32; height = 16; break;
case 3: width = 64; height = 32; break;
}
break;
case 2:
// Vertical Rectangle
switch ((attr1 >> 14) & 3)
{
case 0: width = 8; height = 16; break;
case 1: width = 8; height = 32; break;
case 2: width = 16; height = 32; break;
case 3: width = 32; height = 64; break;
}
break;
}
// Check double size flag here
int rwidth = width, rheight = height;
if ((attr0 & (1 << 8)) != 0)
{
// Rot-scale on
if ((attr0 & (1 << 9)) != 0)
{
rwidth *= 2;
rheight *= 2;
}
}
else
{
// Invalid sprite
if ((attr0 & (1 << 9)) != 0)
width = -1;
}
if (width == -1)
{
// Invalid sprite
continue;
}
// Y clipping
if (y > ((y + rheight) & 0xff))
{
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
}
else
{
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
}
int scale = 1;
if ((attr0 & (1 << 13)) != 0) scale = 2;
int spritey = this.curLine - y;
if (spritey < 0) spritey += 256;
if ((attr0 & (1 << 8)) == 0)
{
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
int baseSprite;
if ((this.dispCnt & (1 << 6)) != 0)
{
// 1 dimensional
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
}
else
{
// 2 dimensional
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
}
int baseInc = scale;
if ((attr1 & (1 << 12)) != 0)
{
baseSprite += ((width / 8) * scale) - scale;
baseInc = -baseInc;
}
if ((attr0 & (1 << 13)) != 0)
{
// 256 colors
for (int i = x; i < x + width; i++)
{
if ((i & 0x1ff) < 240)
{
int tx = (i - x) & 7;
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
int lookup = vram[0x10000 + curIdx];
if (lookup != 0)
{
this.windowCover[i & 0x1ff] = this.winObjEnabled;
}
}
if (((i - x) & 7) == 7) baseSprite += baseInc;
}
}
else
{
// 16 colors
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
for (int i = x; i < x + width; i++)
{
if ((i & 0x1ff) < 240)
{
int tx = (i - x) & 7;
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
int lookup = vram[0x10000 + curIdx];
if ((tx & 1) == 0)
{
lookup &= 0xf;
}
else
{
lookup >>= 4;
}
if (lookup != 0)
{
this.windowCover[i & 0x1ff] = this.winObjEnabled;
}
}
if (((i - x) & 7) == 7) baseSprite += baseInc;
}
}
}
else
{
int rotScaleParam = (attr1 >> 9) & 0x1F;
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
int cx = rwidth / 2;
int cy = rheight / 2;
int baseSprite = attr2 & 0x3FF;
int pitch;
if ((this.dispCnt & (1 << 6)) != 0)
{
// 1 dimensional
pitch = (width / 8) * scale;
}
else
{
// 2 dimensional
pitch = 0x20;
}
short rx = (short)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
short ry = (short)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
// Draw a rot/scale sprite
if ((attr0 & (1 << 13)) != 0)
{
// 256 colors
for (int i = x; i < x + rwidth; i++)
{
int tx = rx >> 8;
int ty = ry >> 8;
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height)
{
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
int lookup = vram[0x10000 + curIdx];
if (lookup != 0)
{
this.windowCover[i & 0x1ff] = this.winObjEnabled;
}
}
rx += dx;
ry += dy;
}
}
else
{
// 16 colors
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
for (int i = x; i < x + rwidth; i++)
{
int tx = rx >> 8;
int ty = ry >> 8;
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height)
{
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
int lookup = vram[0x10000 + curIdx];
if ((tx & 1) == 0)
{
lookup &= 0xf;
}
else
{
lookup >>= 4;
}
if (lookup != 0)
{
this.windowCover[i & 0x1ff] = this.winObjEnabled;
}
}
rx += dx;
ry += dy;
}
}
}
}
}
public static uint GbaTo32(ushort color)
{
// more accurate, but slower :(
// return colorLUT[color];
return ((color & 0x1FU) << 19) | ((color & 0x3E0U) << 6) | ((color & 0x7C00U) >> 7);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,191 +0,0 @@
namespace GarboDev
{
using System;
using System.Collections.Generic;
using System.Text;
public class SoundManager
{
private Memory memory = null;
private Queue<byte>[] soundQueue = new Queue<byte>[2];
private byte latchedA, latchedB;
private int frequency, cyclesPerSample;
private int leftover = 0;
private short[] soundBuffer = new short[40000];
private int soundBufferPos = 0;
private int lastSoundBufferPos = 0;
public SoundManager(Memory memory, int frequency)
{
this.Frequency = frequency;
this.memory = memory;
this.memory.SoundManager = this;
this.soundQueue[0] = new Queue<byte>(32);
this.soundQueue[1] = new Queue<byte>(32);
}
#region Public Properties
public int Frequency
{
get { return this.frequency; }
set
{
this.frequency = value;
this.cyclesPerSample = (GbaManager.cpuFreq << 5) / this.frequency;
}
}
public int QueueSizeA
{
get { return this.soundQueue[0].Count; }
}
public int QueueSizeB
{
get { return this.soundQueue[1].Count; }
}
public int SamplesMixed
{
get
{
int value = this.soundBufferPos - this.lastSoundBufferPos;
if (value < 0) value += this.soundBuffer.Length;
return value;
}
}
#endregion
#region Public Methods
public void GetSamples(short[] buffer, int length)
{
for (int i = 0; i < length; i++)
{
if (this.lastSoundBufferPos == this.soundBuffer.Length)
{
this.lastSoundBufferPos = 0;
}
buffer[i] = this.soundBuffer[this.lastSoundBufferPos++];
}
}
public void Mix(int cycles)
{
ushort soundCntH = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_H);
ushort soundCntX = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_X);
cycles <<= 5;
cycles += this.leftover;
if (cycles > 0)
{
// Precompute loop invariants
short directA = (short)(sbyte)(this.latchedA);
short directB = (short)(sbyte)(this.latchedB);
if ((soundCntH & (1 << 2)) == 0)
{
directA >>= 1;
}
if ((soundCntH & (1 << 3)) == 0)
{
directB >>= 1;
}
while (cycles > 0)
{
short l = 0, r = 0;
cycles -= this.cyclesPerSample;
// Mixing
if ((soundCntX & (1 << 7)) != 0)
{
if ((soundCntH & (1 << 8)) != 0)
{
r += directA;
}
if ((soundCntH & (1 << 9)) != 0)
{
l += directA;
}
if ((soundCntH & (1 << 12)) != 0)
{
r += directB;
}
if ((soundCntH & (1 << 13)) != 0)
{
l += directB;
}
}
if (this.soundBufferPos == this.soundBuffer.Length)
{
this.soundBufferPos = 0;
}
this.soundBuffer[this.soundBufferPos++] = (short)(l << 6);
this.soundBuffer[this.soundBufferPos++] = (short)(r << 6);
}
}
this.leftover = cycles;
}
public void ResetFifoA()
{
this.soundQueue[0].Clear();
this.latchedA = 0;
}
public void ResetFifoB()
{
this.soundQueue[1].Clear();
this.latchedB = 0;
}
public void IncrementFifoA()
{
for (int i = 0; i < 4; i++)
{
this.EnqueueDSoundSample(0, this.memory.IORam[Memory.FIFO_A_L + i]);
}
}
public void IncrementFifoB()
{
for (int i = 0; i < 4; i++)
{
this.EnqueueDSoundSample(1, this.memory.IORam[Memory.FIFO_B_L + i]);
}
}
public void DequeueA()
{
if (this.soundQueue[0].Count > 0)
{
this.latchedA = this.soundQueue[0].Dequeue();
}
}
public void DequeueB()
{
if (this.soundQueue[1].Count > 0)
{
this.latchedB = this.soundQueue[1].Dequeue();
}
}
#endregion Public Methods
private void EnqueueDSoundSample(int channel, byte sample)
{
if (this.soundQueue[channel].Count < 32)
{
this.soundQueue[channel].Enqueue(sample);
}
}
}
}

View File

@ -1,981 +0,0 @@
//#define ARM_DEBUG
namespace GarboDev
{
using System;
using System.Collections.Generic;
using System.Text;
public class ThumbCore
{
private const int COND_EQ = 0; // Z set
private const int COND_NE = 1; // Z clear
private const int COND_CS = 2; // C set
private const int COND_CC = 3; // C clear
private const int COND_MI = 4; // N set
private const int COND_PL = 5; // N clear
private const int COND_VS = 6; // V set
private const int COND_VC = 7; // V clear
private const int COND_HI = 8; // C set and Z clear
private const int COND_LS = 9; // C clear or Z set
private const int COND_GE = 10; // N equals V
private const int COND_LT = 11; // N not equal to V
private const int COND_GT = 12; // Z clear AND (N equals V)
private const int COND_LE = 13; // Z set OR (N not equal to V)
private const int COND_AL = 14; // Always
private const int COND_NV = 15; // Never execute
private const int OP_AND = 0x0;
private const int OP_EOR = 0x1;
private const int OP_LSL = 0x2;
private const int OP_LSR = 0x3;
private const int OP_ASR = 0x4;
private const int OP_ADC = 0x5;
private const int OP_SBC = 0x6;
private const int OP_ROR = 0x7;
private const int OP_TST = 0x8;
private const int OP_NEG = 0x9;
private const int OP_CMP = 0xA;
private const int OP_CMN = 0xB;
private const int OP_ORR = 0xC;
private const int OP_MUL = 0xD;
private const int OP_BIC = 0xE;
private const int OP_MVN = 0xF;
private Arm7Processor parent;
private Memory memory;
private uint[] registers;
// CPU flags
private uint zero, carry, negative, overflow;
private ushort curInstruction, instructionQueue;
private delegate void ExecuteInstruction();
private ExecuteInstruction[] NormalOps = null;
public ThumbCore(Arm7Processor parent, Memory memory)
{
this.parent = parent;
this.memory = memory;
this.registers = this.parent.Registers;
this.NormalOps = new ExecuteInstruction[256]
{
OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm,
OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm,
OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm,
OpAddRegReg, OpAddRegReg, OpSubRegReg, OpSubRegReg, OpAddRegImm, OpAddRegImm, OpSubRegImm, OpSubRegImm,
OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm,
OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm,
OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm,
OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm,
OpArith, OpArith, OpArith, OpArith, OpAddHi, OpCmpHi, OpMovHi, OpBx,
OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc,
OpStrReg, OpStrReg, OpStrhReg, OpStrhReg, OpStrbReg, OpStrbReg, OpLdrsbReg, OpLdrsbReg,
OpLdrReg, OpLdrReg, OpLdrhReg, OpLdrhReg, OpLdrbReg, OpLdrbReg, OpLdrshReg, OpLdrshReg,
OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm,
OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm,
OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm,
OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm,
OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm,
OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm,
OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp,
OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp,
OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc,
OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp,
OpSubSp, OpUnd, OpUnd, OpUnd, OpPush, OpPushLr, OpUnd, OpUnd,
OpUnd, OpUnd, OpUnd, OpUnd, OpPop, OpPopPc, OpUnd, OpUnd,
OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, OpStmia,
OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia,
OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond,
OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpUnd, OpSwi,
OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB,
OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, OpUnd,
OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, OpBl1,
OpBl2, OpBl2, OpBl2, OpBl2, OpBl2, OpBl2, OpBl2, OpBl2
};
}
public void BeginExecution()
{
this.FlushQueue();
}
public void Step()
{
this.UnpackFlags();
this.curInstruction = this.instructionQueue;
this.instructionQueue = this.memory.ReadU16(registers[15]);
registers[15] += 2;
// Execute the instruction
this.NormalOps[this.curInstruction >> 8]();
this.parent.Cycles -= this.memory.WaitCycles;
if ((this.parent.CPSR & Arm7Processor.T_MASK) != Arm7Processor.T_MASK)
{
if ((this.curInstruction >> 8) != 0xDF) this.parent.ReloadQueue();
}
this.PackFlags();
}
public void Execute()
{
this.UnpackFlags();
while (this.parent.Cycles > 0)
{
this.curInstruction = this.instructionQueue;
this.instructionQueue = this.memory.ReadU16(registers[15]);
registers[15] += 2;
// Execute the instruction
this.NormalOps[this.curInstruction >> 8]();
this.parent.Cycles -= this.memory.WaitCycles;
if ((this.parent.CPSR & Arm7Processor.T_MASK) != Arm7Processor.T_MASK)
{
if ((this.curInstruction >> 8) != 0xDF) this.parent.ReloadQueue();
break;
}
// Check the current PC
#if ARM_DEBUG
if (this.parent.Breakpoints.ContainsKey(registers[15] - 2U))
{
this.parent.BreakpointHit = true;
break;
}
#endif
}
this.PackFlags();
}
#region Flag helpers
public void OverflowCarryAdd(uint a, uint b, uint r)
{
overflow = ((a & b & ~r) | (~a & ~b & r)) >> 31;
carry = ((a & b) | (a & ~r) | (b & ~r)) >> 31;
}
public void OverflowCarrySub(uint a, uint b, uint r)
{
overflow = ((a & ~b & ~r) | (~a & b & r)) >> 31;
carry = ((a & ~b) | (a & ~r) | (~b & ~r)) >> 31;
}
#endregion
#region Opcodes
private void OpLslImm()
{
// 0x00 - 0x07
// lsl rd, rm, #immed
int rd = this.curInstruction & 0x7;
int rm = (this.curInstruction >> 3) & 0x7;
int immed = (this.curInstruction >> 6) & 0x1F;
if (immed == 0)
{
registers[rd] = registers[rm];
} else
{
carry = (registers[rm] >> (32 - immed)) & 0x1;
registers[rd] = registers[rm] << immed;
}
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpLsrImm()
{
// 0x08 - 0x0F
// lsr rd, rm, #immed
int rd = this.curInstruction & 0x7;
int rm = (this.curInstruction >> 3) & 0x7;
int immed = (this.curInstruction >> 6) & 0x1F;
if (immed == 0)
{
carry = registers[rm] >> 31;
registers[rd] = 0;
}
else
{
carry = (registers[rm] >> (immed - 1)) & 0x1;
registers[rd] = registers[rm] >> immed;
}
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpAsrImm()
{
// asr rd, rm, #immed
int rd = this.curInstruction & 0x7;
int rm = (this.curInstruction >> 3) & 0x7;
int immed = (this.curInstruction >> 6) & 0x1F;
if (immed == 0)
{
carry = registers[rm] >> 31;
if (carry == 1) registers[rd] = 0xFFFFFFFF;
else registers[rd] = 0;
}
else
{
carry = (registers[rm] >> (immed - 1)) & 0x1;
registers[rd] = (uint)(((int)registers[rm]) >> immed);
}
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpAddRegReg()
{
// add rd, rn, rm
int rd = this.curInstruction & 0x7;
int rn = (this.curInstruction >> 3) & 0x7;
int rm = (this.curInstruction >> 6) & 0x7;
uint orn = registers[rn];
uint orm = registers[rm];
registers[rd] = orn + orm;
this.OverflowCarryAdd(orn, orm, registers[rd]);
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpSubRegReg()
{
// sub rd, rn, rm
int rd = this.curInstruction & 0x7;
int rn = (this.curInstruction >> 3) & 0x7;
int rm = (this.curInstruction >> 6) & 0x7;
uint orn = registers[rn];
uint orm = registers[rm];
registers[rd] = orn - orm;
this.OverflowCarrySub(orn, orm, registers[rd]);
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpAddRegImm()
{
// add rd, rn, #immed
int rd = this.curInstruction & 0x7;
int rn = (this.curInstruction >> 3) & 0x7;
uint immed = (uint)((this.curInstruction >> 6) & 0x7);
uint orn = registers[rn];
registers[rd] = orn + immed;
this.OverflowCarryAdd(orn, immed, registers[rd]);
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpSubRegImm()
{
// sub rd, rn, #immed
int rd = this.curInstruction & 0x7;
int rn = (this.curInstruction >> 3) & 0x7;
uint immed = (uint)((this.curInstruction >> 6) & 0x7);
uint orn = registers[rn];
registers[rd] = orn - immed;
this.OverflowCarrySub(orn, immed, registers[rd]);
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpMovImm()
{
// mov rd, #immed
int rd = (this.curInstruction >> 8) & 0x7;
registers[rd] = (uint)(this.curInstruction & 0xFF);
negative = 0;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpCmpImm()
{
// cmp rn, #immed
int rn = (this.curInstruction >> 8) & 0x7;
uint alu = registers[rn] - (uint)(this.curInstruction & 0xFF);
this.OverflowCarrySub(registers[rn], (uint)(this.curInstruction & 0xFF), alu);
negative = alu >> 31;
zero = alu == 0 ? 1U : 0U;
}
private void OpAddImm()
{
// add rd, #immed
int rd = (this.curInstruction >> 8) & 0x7;
uint ord = registers[rd];
registers[rd] += (uint)(this.curInstruction & 0xFF);
this.OverflowCarryAdd(ord, (uint)(this.curInstruction & 0xFF), registers[rd]);
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpSubImm()
{
// sub rd, #immed
int rd = (this.curInstruction >> 8) & 0x7;
uint ord = registers[rd];
registers[rd] -= (uint)(this.curInstruction & 0xFF);
this.OverflowCarrySub(ord, (uint)(this.curInstruction & 0xFF), registers[rd]);
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
}
private void OpArith()
{
int rd = this.curInstruction & 0x7;
uint rn = registers[(this.curInstruction >> 3) & 0x7];
uint orig, alu;
int shiftAmt;
switch ((this.curInstruction >> 6) & 0xF)
{
case OP_ADC:
orig = registers[rd];
registers[rd] += rn + carry;
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
this.OverflowCarryAdd(orig, rn, registers[rd]);
break;
case OP_AND:
registers[rd] &= rn;
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_ASR:
shiftAmt = (int)(rn & 0xFF);
if (shiftAmt == 0)
{
// Do nothing
}
else if (shiftAmt < 32)
{
carry = (registers[rd] >> (shiftAmt - 1)) & 0x1;
registers[rd] = (uint)(((int)registers[rd]) >> shiftAmt);
}
else
{
carry = (registers[rd] >> 31) & 1;
if (carry == 1) registers[rd] = 0xFFFFFFFF;
else registers[rd] = 0;
}
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_BIC:
registers[rd] &= ~rn;
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_CMN:
alu = registers[rd] + rn;
negative = alu >> 31;
zero = alu == 0 ? 1U : 0U;
this.OverflowCarryAdd(registers[rd], rn, alu);
break;
case OP_CMP:
alu = registers[rd] - rn;
negative = alu >> 31;
zero = alu == 0 ? 1U : 0U;
this.OverflowCarrySub(registers[rd], rn, alu);
break;
case OP_EOR:
registers[rd] ^= rn;
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_LSL:
shiftAmt = (int)(rn & 0xFF);
if (shiftAmt == 0)
{
// Do nothing
}
else if (shiftAmt < 32)
{
carry = (registers[rd] >> (32 - shiftAmt)) & 0x1;
registers[rd] <<= shiftAmt;
}
else if (shiftAmt == 32)
{
carry = registers[rd] & 0x1;
registers[rd] = 0;
}
else
{
carry = 0;
registers[rd] = 0;
}
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_LSR:
shiftAmt = (int)(rn & 0xFF);
if (shiftAmt == 0)
{
// Do nothing
}
else if (shiftAmt < 32)
{
carry = (registers[rd] >> (shiftAmt - 1)) & 0x1;
registers[rd] >>= shiftAmt;
}
else if (shiftAmt == 32)
{
carry = (registers[rd] >> 31) & 0x1;
registers[rd] = 0;
}
else
{
carry = 0;
registers[rd] = 0;
}
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_MUL:
int mulCycles = 4;
// Multiply cycle calculations
if ((rn & 0xFFFFFF00) == 0 || (rn & 0xFFFFFF00) == 0xFFFFFF00)
{
mulCycles = 1;
}
else if ((rn & 0xFFFF0000) == 0 || (rn & 0xFFFF0000) == 0xFFFF0000)
{
mulCycles = 2;
}
else if ((rn & 0xFF000000) == 0 || (rn & 0xFF000000) == 0xFF000000)
{
mulCycles = 3;
}
this.parent.Cycles -= mulCycles;
registers[rd] *= rn;
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_MVN:
registers[rd] = ~rn;
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_NEG:
registers[rd] = 0 - rn;
this.OverflowCarrySub(0, rn, registers[rd]);
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_ORR:
registers[rd] |= rn;
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_ROR:
shiftAmt = (int)(rn & 0xFF);
if (shiftAmt == 0)
{
// Do nothing
}
else if ((shiftAmt & 0x1F) == 0)
{
carry = registers[rd] >> 31;
}
else
{
shiftAmt &= 0x1F;
carry = (registers[rd] >> (shiftAmt - 1)) & 0x1;
registers[rd] = (registers[rd] >> shiftAmt) | (registers[rd] << (32 - shiftAmt));
}
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
break;
case OP_SBC:
orig = registers[rd];
registers[rd] = (registers[rd] - rn) - (1U - carry);
negative = registers[rd] >> 31;
zero = registers[rd] == 0 ? 1U : 0U;
this.OverflowCarrySub(orig, rn, registers[rd]);
break;
case OP_TST:
alu = registers[rd] & rn;
negative = alu >> 31;
zero = alu == 0 ? 1U : 0U;
break;
default:
throw new Exception("The coder screwed up on the thumb alu op...");
}
}
private void OpAddHi()
{
int rd = ((this.curInstruction & (1 << 7)) >> 4) | (this.curInstruction & 0x7);
int rm = (this.curInstruction >> 3) & 0xF;
registers[rd] += registers[rm];
if (rd == 15)
{
registers[rd] &= ~1U;
this.FlushQueue();
}
}
private void OpCmpHi()
{
int rd = ((this.curInstruction & (1 << 7)) >> 4) | (this.curInstruction & 0x7);
int rm = (this.curInstruction >> 3) & 0xF;
uint alu = registers[rd] - registers[rm];
negative = alu >> 31;
zero = alu == 0 ? 1U : 0U;
this.OverflowCarrySub(registers[rd], registers[rm], alu);
}
private void OpMovHi()
{
int rd = ((this.curInstruction & (1 << 7)) >> 4) | (this.curInstruction & 0x7);
int rm = (this.curInstruction >> 3) & 0xF;
registers[rd] = registers[rm];
if (rd == 15)
{
registers[rd] &= ~1U;
this.FlushQueue();
}
}
private void OpBx()
{
int rm = (this.curInstruction >> 3) & 0xf;
this.PackFlags();
this.parent.CPSR &= ~Arm7Processor.T_MASK;
this.parent.CPSR |= (registers[rm] & 1) << Arm7Processor.T_BIT;
registers[15] = registers[rm] & (~1U);
this.UnpackFlags();
// Check for branch back to Arm Mode
if ((this.parent.CPSR & Arm7Processor.T_MASK) != Arm7Processor.T_MASK)
{
return;
}
this.FlushQueue();
}
private void OpLdrPc()
{
int rd = (this.curInstruction >> 8) & 0x7;
registers[rd] = this.memory.ReadU32((registers[15] & ~2U) + (uint)((this.curInstruction & 0xFF) * 4));
this.parent.Cycles--;
}
private void OpStrReg()
{
this.memory.WriteU32(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7],
registers[this.curInstruction & 0x7]);
}
private void OpStrhReg()
{
this.memory.WriteU16(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7],
(ushort)(registers[this.curInstruction & 0x7] & 0xFFFF));
}
private void OpStrbReg()
{
this.memory.WriteU8(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7],
(byte)(registers[this.curInstruction & 0x7] & 0xFF));
}
private void OpLdrsbReg()
{
registers[this.curInstruction & 0x7] =
this.memory.ReadU8(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
if ((registers[this.curInstruction & 0x7] & (1 << 7)) != 0)
{
registers[this.curInstruction & 0x7] |= 0xFFFFFF00;
}
this.parent.Cycles--;
}
private void OpLdrReg()
{
registers[this.curInstruction & 0x7] =
this.memory.ReadU32(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
this.parent.Cycles--;
}
private void OpLdrhReg()
{
registers[this.curInstruction & 0x7] =
this.memory.ReadU16(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
this.parent.Cycles--;
}
private void OpLdrbReg()
{
registers[this.curInstruction & 0x7] =
this.memory.ReadU8(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
this.parent.Cycles--;
}
private void OpLdrshReg()
{
registers[this.curInstruction & 0x7] =
this.memory.ReadU16(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
if ((registers[this.curInstruction & 0x7] & (1 << 15)) != 0)
{
registers[this.curInstruction & 0x7] |= 0xFFFF0000;
}
this.parent.Cycles--;
}
private void OpStrImm()
{
this.memory.WriteU32(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 4),
registers[this.curInstruction & 0x7]);
}
private void OpLdrImm()
{
registers[this.curInstruction & 0x7] =
this.memory.ReadU32(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 4));
this.parent.Cycles--;
}
private void OpStrbImm()
{
this.memory.WriteU8(registers[(this.curInstruction >> 3) & 0x7] + (uint)((this.curInstruction >> 6) & 0x1F),
(byte)(registers[this.curInstruction & 0x7] & 0xFF));
}
private void OpLdrbImm()
{
registers[this.curInstruction & 0x7] =
this.memory.ReadU8(registers[(this.curInstruction >> 3) & 0x7] + (uint)((this.curInstruction >> 6) & 0x1F));
this.parent.Cycles--;
}
private void OpStrhImm()
{
this.memory.WriteU16(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 2),
(ushort)(registers[this.curInstruction & 0x7] & 0xFFFF));
}
private void OpLdrhImm()
{
registers[this.curInstruction & 0x7] =
this.memory.ReadU16(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 2));
this.parent.Cycles--;
}
private void OpStrSp()
{
this.memory.WriteU32(registers[13] + (uint)((this.curInstruction & 0xFF) * 4),
registers[(this.curInstruction >> 8) & 0x7]);
}
private void OpLdrSp()
{
registers[(this.curInstruction >> 8) & 0x7] =
this.memory.ReadU32(registers[13] + (uint)((this.curInstruction & 0xFF) * 4));
}
private void OpAddPc()
{
registers[(this.curInstruction >> 8) & 0x7] =
(registers[15] & ~2U) + (uint)((this.curInstruction & 0xFF) * 4);
}
private void OpAddSp()
{
registers[(this.curInstruction >> 8) & 0x7] =
registers[13] + (uint)((this.curInstruction & 0xFF) * 4);
}
private void OpSubSp()
{
if ((this.curInstruction & (1 << 7)) != 0)
registers[13] -= (uint)((this.curInstruction & 0x7F) * 4);
else
registers[13] += (uint)((this.curInstruction & 0x7F) * 4);
}
private void OpPush()
{
for (int i = 7; i >= 0; i--)
{
if (((this.curInstruction >> i) & 1) != 0)
{
registers[13] -= 4;
this.memory.WriteU32(registers[13], registers[i]);
}
}
}
private void OpPushLr()
{
registers[13] -= 4;
this.memory.WriteU32(registers[13], registers[14]);
for (int i = 7; i >= 0; i--)
{
if (((this.curInstruction >> i) & 1) != 0)
{
registers[13] -= 4;
this.memory.WriteU32(registers[13], registers[i]);
}
}
}
private void OpPop()
{
for (int i = 0; i < 8; i++)
{
if (((this.curInstruction >> i) & 1) != 0)
{
registers[i] = this.memory.ReadU32(registers[13]);
registers[13] += 4;
}
}
this.parent.Cycles--;
}
private void OpPopPc()
{
for (int i = 0; i < 8; i++)
{
if (((this.curInstruction >> i) & 1) != 0)
{
registers[i] = this.memory.ReadU32(registers[13]);
registers[13] += 4;
}
}
registers[15] = this.memory.ReadU32(registers[13]) & (~1U);
registers[13] += 4;
// ARM9 check here
this.FlushQueue();
this.parent.Cycles--;
}
private void OpStmia()
{
int rn = (this.curInstruction >> 8) & 0x7;
for (int i = 0; i < 8; i++)
{
if (((this.curInstruction >> i) & 1) != 0)
{
this.memory.WriteU32(registers[rn] & (~3U), registers[i]);
registers[rn] += 4;
}
}
}
private void OpLdmia()
{
int rn = (this.curInstruction >> 8) & 0x7;
uint address = registers[rn];
for (int i = 0; i < 8; i++)
{
if (((this.curInstruction >> i) & 1) != 0)
{
registers[i] = this.memory.ReadU32Aligned(address & (~3U));
address += 4;
}
}
if (((this.curInstruction >> rn) & 1) == 0)
{
registers[rn] = address;
}
}
private void OpBCond()
{
uint cond = 0;
switch ((this.curInstruction >> 8) & 0xF)
{
case COND_AL: cond = 1; break;
case COND_EQ: cond = zero; break;
case COND_NE: cond = 1 - zero; break;
case COND_CS: cond = carry; break;
case COND_CC: cond = 1 - carry; break;
case COND_MI: cond = negative; break;
case COND_PL: cond = 1 - negative; break;
case COND_VS: cond = overflow; break;
case COND_VC: cond = 1 - overflow; break;
case COND_HI: cond = carry & (1 - zero); break;
case COND_LS: cond = (1 - carry) | zero; break;
case COND_GE: cond = (1 - negative) ^ overflow; break;
case COND_LT: cond = negative ^ overflow; break;
case COND_GT: cond = (1 - zero) & (negative ^ (1 - overflow)); break;
case COND_LE: cond = (negative ^ overflow) | zero; break;
}
if (cond == 1)
{
uint offset = (uint)(this.curInstruction & 0xFF);
if ((offset & (1 << 7)) != 0) offset |= 0xFFFFFF00;
registers[15] += offset << 1;
this.FlushQueue();
}
}
private void OpSwi()
{
registers[15] -= 4U;
this.parent.EnterException(Arm7Processor.SVC, 0x8, false, false);
}
private void OpB()
{
uint offset = (uint)(this.curInstruction & 0x7FF);
if ((offset & (1 << 10)) != 0) offset |= 0xFFFFF800;
registers[15] += offset << 1;
this.FlushQueue();
}
private void OpBl1()
{
uint offset = (uint)(this.curInstruction & 0x7FF);
if ((offset & (1 << 10)) != 0) offset |= 0xFFFFF800;
registers[14] = registers[15] + (offset << 12);
}
private void OpBl2()
{
uint tmp = registers[15];
registers[15] = registers[14] + (uint)((this.curInstruction & 0x7FF) << 1);
registers[14] = (tmp - 2U) | 1;
this.FlushQueue();
}
private void OpUnd()
{
throw new Exception("Unknown opcode");
}
#endregion
private void PackFlags()
{
this.parent.CPSR &= 0x0FFFFFFF;
this.parent.CPSR |= this.negative << Arm7Processor.N_BIT;
this.parent.CPSR |= this.zero << Arm7Processor.Z_BIT;
this.parent.CPSR |= this.carry << Arm7Processor.C_BIT;
this.parent.CPSR |= this.overflow << Arm7Processor.V_BIT;
}
private void UnpackFlags()
{
this.negative = (this.parent.CPSR >> Arm7Processor.N_BIT) & 1;
this.zero = (this.parent.CPSR >> Arm7Processor.Z_BIT) & 1;
this.carry = (this.parent.CPSR >> Arm7Processor.C_BIT) & 1;
this.overflow = (this.parent.CPSR >> Arm7Processor.V_BIT) & 1;
}
private void FlushQueue()
{
this.instructionQueue = this.memory.ReadU16(registers[15]);
registers[15] += 2;
}
}
}

View File

@ -1,164 +0,0 @@
namespace GarboDev
{
public class VideoManager
{
public delegate void OnPresent(uint[] data);
private Memory memory = null;
private IRenderer renderer = null;
private OnPresent presenter;
private GbaManager gbaManager;
private int curLine;
public Memory Memory
{
set{ this.memory = value; }
}
public IRenderer Renderer
{
set
{
this.renderer = value;
this.renderer.Memory = this.memory;
}
}
public OnPresent Presenter
{
set { this.presenter = value; }
}
public VideoManager(GbaManager gbaManager)
{
this.gbaManager = gbaManager;
}
public void Reset()
{
this.curLine = 0;
this.renderer.Memory = memory;
this.renderer.Reset();
}
private void EnterVBlank(Arm7Processor processor)
{
ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT);
dispstat |= 1;
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
// Render the frame
this.gbaManager.FramesRendered++;
this.presenter(this.renderer.ShowFrame());
if ((dispstat & (1 << 3)) != 0)
{
// Fire the vblank irq
processor.RequestIrq(0);
}
// Check for DMA triggers
this.memory.VBlankDma();
}
private void LeaveVBlank(Arm7Processor processor)
{
ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT);
dispstat &= 0xFFFE;
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
processor.UpdateKeyState();
// Update the rot/scale values
this.memory.Bgx[0] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG2X_L);
this.memory.Bgx[1] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG3X_L);
this.memory.Bgy[0] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG2Y_L);
this.memory.Bgy[1] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG3Y_L);
}
public void EnterHBlank(Arm7Processor processor)
{
ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT);
dispstat |= 1 << 1;
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
// Advance the bgx registers
for (int bg = 0; bg <= 1; bg++)
{
short dmx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PB + (uint)bg * 0x10);
short dmy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PD + (uint)bg * 0x10);
this.memory.Bgx[bg] += dmx;
this.memory.Bgy[bg] += dmy;
}
if (this.curLine < 160)
{
this.memory.HBlankDma();
// Trigger hblank irq
if ((dispstat & (1 << 4)) != 0)
{
processor.RequestIrq(1);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="processor"></param>
/// <returns>true if end of frame</returns>
public bool LeaveHBlank(Arm7Processor processor)
{
bool ret = false;
ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT);
dispstat &= 0xFFF9;
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
// Move to the next line
this.curLine++;
if (this.curLine >= 228)
{
// Start again at the beginning
this.curLine = 0;
}
// Update registers
Memory.WriteU16(this.memory.IORam, Memory.VCOUNT, (ushort)this.curLine);
// Check for vblank
if (this.curLine == 160)
{
this.EnterVBlank(processor);
ret = true;
}
else if (this.curLine == 0)
{
this.LeaveVBlank(processor);
}
// Check y-line trigger
if (((dispstat >> 8) & 0xff) == this.curLine)
{
dispstat = (ushort)(Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT) | (1 << 2));
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
if ((dispstat & (1 << 5)) != 0)
{
processor.RequestIrq(2);
}
}
return ret;
}
public void RenderLine()
{
if (this.curLine < 160)
{
this.renderer.RenderLine(this.curLine);
}
}
}
}

View File

@ -1,7 +0,0 @@
garbodev with a quick iemulator binding. license uncertain.
basic functionality works (gets ingame on a number of commercial titles).
no GB classic sound channels.
no savestates.
lots of things not hooked up (saveram, etc)

View File

@ -1,110 +0,0 @@
# this was modified at one point so that it would compile in our bizhawk project, but isn't in
# use at the moment. tread carefully
ifeq ($(platform),)
platform = unix
ifeq ($(shell uname -a),)
platform = win
else ifneq ($(findstring MINGW,$(shell uname -a)),)
platform = win
else ifneq ($(findstring Darwin,$(shell uname -a)),)
platform = osx
else ifneq ($(findstring win,$(shell uname -a)),)
platform = win
endif
endif
ifeq ($(platform), unix)
TARGET := libmeteor.so
fpic := -fPIC
SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined
else ifeq ($(platform), osx)
TARGET := libmeteor.dylib
fpic := -fPIC
SHARED := -dynamiclib
else
TARGET := libmeteor.dll
CXX = g++
# SHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=link.T -Wl,--no-undefined
SHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--no-undefined
CXXFLAGS += -DNO_MEMMEM
endif
#__LIBRETRO__ enables a slightly different saveram mechanism that doesn't seem to serve any useful purpose?
#CXXFLAGS += -Wall -pedantic -I. -I../ameteor/include -pipe -D__LIBRETRO__ -Wno-parentheses -fno-exceptions -fno-rtti
CXXFLAGS += -Wall -pedantic -I. -Iinclude -pipe -DX86_ASM -Wno-parentheses -fno-exceptions -fno-rtti
ifeq ($(DEBUG), 1)
CFLAGS += -O0 -g
CXXFLAGS += -O0 -g
else
CFLAGS += -O3
CXXFLAGS += -O3
endif
#SRCDIR := ../ameteor/source
SRCDIR := ./source
SOURCES := \
$(SRCDIR)/audio/dsound.cpp \
$(SRCDIR)/audio/sound1.cpp \
$(SRCDIR)/audio/sound2.cpp \
$(SRCDIR)/audio/sound4.cpp \
$(SRCDIR)/audio/speaker.cpp \
$(SRCDIR)/disassembler/argimmediate.cpp \
$(SRCDIR)/disassembler/argmulregisters.cpp \
$(SRCDIR)/disassembler/argpsr.cpp \
$(SRCDIR)/disassembler/argregister.cpp \
$(SRCDIR)/disassembler/argrelative.cpp \
$(SRCDIR)/disassembler/argshift.cpp \
$(SRCDIR)/disassembler/arguimmediate.cpp \
$(SRCDIR)/disassembler/arguments.cpp \
$(SRCDIR)/disassembler/instruction.cpp \
$(SRCDIR)/graphics/bglayer.cpp \
$(SRCDIR)/graphics/object.cpp \
$(SRCDIR)/graphics/objects.cpp \
$(SRCDIR)/graphics/renderer.cpp \
$(SRCDIR)/graphics/screen.cpp \
$(SRCDIR)/ameteor.cpp \
$(SRCDIR)/bios.cpp \
$(SRCDIR)/clock.cpp \
$(SRCDIR)/cpu.cpp \
$(SRCDIR)/debug.cpp \
$(SRCDIR)/dma.cpp \
$(SRCDIR)/eeprom.cpp \
$(SRCDIR)/flash.cpp \
$(SRCDIR)/cartmem.cpp \
$(SRCDIR)/interpreter.cpp \
$(SRCDIR)/interpreter_arm.cpp \
$(SRCDIR)/interpreter_thumb.cpp \
$(SRCDIR)/io.cpp \
$(SRCDIR)/keypad.cpp \
$(SRCDIR)/lcd.cpp \
$(SRCDIR)/memory.cpp \
$(SRCDIR)/sound.cpp \
$(SRCDIR)/sram.cpp \
$(SRCDIR)/timer.cpp \
cinterface.cpp
# video.cpp \
# audio.cpp \
# input.cpp \
# libretro.cpp
OBJ := $(SOURCES:.cpp=.o)
all: $(TARGET)
$(TARGET): $(OBJ)
@$(CXX) -o $@ $^ $(SHARED) $(LDFLAGS) $(LIBS)
@echo LD $(notdir $@)
%.o: %.cpp
@$(CXX) -o $@ -c $< $(CXXFLAGS) $(fpic)
@echo CXX $(notdir $<)
clean:
rm -f $(TARGET)
rm -f $(OBJ)
.PHONY: clean

View File

@ -1,246 +0,0 @@
#include "ameteor.hpp"
#include "ameteor/cartmem.hpp"
#include "source/debug.hpp"
#include <sstream>
#define EXPORT extern "C" __declspec(dllexport)
void (*messagecallback)(const char *msg, int abort) = NULL;
EXPORT void libmeteor_setmessagecallback(void (*callback)(const char *msg, int abort))
{
messagecallback = callback;
print_bizhawk("libmeteor message stream operational.");
}
void print_bizhawk(const char *msg)
{
if (messagecallback)
messagecallback(msg, 0);
}
void print_bizhawk(std::string &msg)
{
if (messagecallback)
messagecallback(msg.c_str(), 0);
}
void abort_bizhawk(const char *msg)
{
if (messagecallback)
messagecallback(msg, 1);
AMeteor::Stop(); // makes it easy to pick apart what happened
}
uint16_t (*keycallback)() = NULL;
void keyupdate_bizhawk()
{
if (keycallback)
AMeteor::_keypad.SetPadState(keycallback() ^ 0x3FF);
}
EXPORT void libmeteor_setkeycallback(uint16_t (*callback)())
{
keycallback = callback;
}
bool traceenabled = false;
void (*tracecallback)(const char *msg) = NULL;
EXPORT void libmeteor_settracecallback(void (*callback)(const char*msg))
{
tracecallback = callback;
traceenabled = tracecallback != NULL;
}
void trace_bizhawk(std::string msg)
{
if (tracecallback)
tracecallback(msg.c_str());
}
EXPORT void libmeteor_hardreset()
{
AMeteor::Reset(AMeteor::UNIT_ALL ^ (AMeteor::UNIT_MEMORY_BIOS | AMeteor::UNIT_MEMORY_ROM));
}
uint32_t *videobuff;
void videocb(const uint16_t *frame)
{
uint32_t *dest = videobuff;
const uint16_t *src = frame;
for (int i = 0; i < 240 * 160; i++, src++, dest++)
{
uint16_t c = *src;
uint16_t b = c >> 10 & 31;
uint16_t g = c >> 5 & 31;
uint16_t r = c & 31;
b = b << 3 | b >> 2;
g = g << 3 | g >> 2;
r = r << 3 | r >> 2;
*dest = b | g << 8 | r << 16 | 0xff000000;
}
AMeteor::Stop(); // to the end of frame only
}
int16_t *soundbuff;
int16_t *soundbuffcur;
int16_t *soundbuffend;
void soundcb(const int16_t *samples)
{
if (soundbuffcur < soundbuffend)
{
*soundbuffcur++ = *samples++;
*soundbuffcur++ = *samples++;
}
}
EXPORT unsigned libmeteor_emptysound()
{
unsigned ret = (soundbuffcur - soundbuff) * sizeof(int16_t);
soundbuffcur = soundbuff;
return ret;
}
EXPORT int libmeteor_setbuffers(uint32_t *vid, unsigned vidlen, int16_t *aud, unsigned audlen)
{
if (vidlen < 240 * 160 * sizeof(uint32_t))
return 0;
if (audlen < 4 || audlen % 4 != 0)
return 0;
videobuff = vid;
soundbuff = aud;
soundbuffend = soundbuff + audlen / sizeof(int16_t);
libmeteor_emptysound();
return 1;
}
EXPORT void libmeteor_init()
{
static bool first = true;
if (first)
{
AMeteor::_lcd.GetScreen().GetRenderer().SetFrameSlot(syg::ptr_fun(videocb));
AMeteor::_sound.GetSpeaker().SetFrameSlot(syg::ptr_fun(soundcb));
first = false;
}
}
EXPORT void libmeteor_frameadvance()
{
AMeteor::Run(10000000);
}
EXPORT void libmeteor_loadrom(const void *data, unsigned size)
{
AMeteor::_memory.LoadRom((const uint8_t*)data, size);
}
EXPORT void libmeteor_loadbios(const void *data, unsigned size)
{
AMeteor::_memory.LoadBios((const uint8_t*)data, size);
}
EXPORT uint8_t *libmeteor_getmemoryarea(int which)
{
if (which < 7)
return AMeteor::_memory.GetMemoryArea(which);
else if (which == 7)
return AMeteor::_io.GetIoPointer();
else
return NULL;
}
EXPORT int libmeteor_loadsaveram(const void *data, unsigned size)
{
return AMeteor::_memory.LoadCart((const uint8_t*)data, size);
}
EXPORT int libmeteor_savesaveram(void **data, unsigned *size)
{
return AMeteor::_memory.SaveCart((uint8_t **)data, size);
}
EXPORT void libmeteor_savesaveram_destroy(void *data)
{
AMeteor::_memory.SaveCartDestroy((uint8_t *)data);
}
EXPORT int libmeteor_hassaveram()
{
return AMeteor::_memory.HasCart();
}
EXPORT void libmeteor_clearsaveram()
{
AMeteor::_memory.DeleteCart();
}
EXPORT int libmeteor_savestate(void **data, unsigned *size)
{
if (!data || !size)
return 0;
std::ostringstream ss = std::ostringstream(std::ios_base::binary);
AMeteor::SaveState(ss);
std::string s = ss.str();
void *ret = std::malloc(s.size());
if (!ret)
return 0;
std::memcpy(ret, s.data(), s.size());
*data = ret;
*size = s.size();
return 1;
}
EXPORT void libmeteor_savestate_destroy(void *data)
{
std::free(data);
}
EXPORT int libmeteor_loadstate(const void *data, unsigned size)
{
std::istringstream ss = std::istringstream(std::string((const char*)data, size), std::ios_base::binary);
return AMeteor::LoadState(ss);
}
// TODO: cartram memory domain, cartram in system bus memory domain
EXPORT uint8_t libmeteor_peekbus(uint32_t addr)
{
return AMeteor::_memory.Peek8(addr);
}
EXPORT void libmeteor_writebus(uint32_t addr, uint8_t val)
{
AMeteor::_memory.Write8(addr, val);
}
int slcallbackline = 400;
void (*slcallback)() = NULL;
EXPORT void libmeteor_setscanlinecallback(void (*callback)(), int scanline)
{
if (!callback)
slcallbackline = 400;
else
slcallbackline = scanline;
slcallback = callback;
}
void scanlinecallback_bizhawk()
{
if (slcallback)
slcallback();
}
EXPORT void libmeteor_getregs(int *dest)
{
AMeteor::_cpu.UpdateCpsr();
for (int i = 0; i < 16; i++)
dest[i] = AMeteor::_cpu.Reg(i);
dest[16] = AMeteor::_cpu.Cpsr().dw;
dest[17] = AMeteor::_cpu.Spsr().dw;
}

View File

@ -1,80 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __AMETEOR_H__
#define __AMETEOR_H__
#include "ameteor/memory.hpp"
#include "ameteor/io.hpp"
#include "ameteor/dma.hpp"
#include "ameteor/interpreter.hpp"
#include "ameteor/lcd.hpp"
#include "ameteor/clock.hpp"
#include "ameteor/timer.hpp"
#include "ameteor/sound.hpp"
#include "ameteor/keypad.hpp"
namespace AMeteor
{
extern Clock _clock;
extern Io _io;
extern Interpreter _cpu;
extern Memory _memory;
extern Dma _dma;
extern Lcd _lcd;
extern Sound _sound;
extern Keypad _keypad;
extern Timer _timer0;
extern Timer _timer1;
extern Timer _timer2;
extern Timer _timer3;
const uint32_t UNIT_CLOCK = 0x0001;
const uint32_t UNIT_IO = 0x0002;
const uint32_t UNIT_CPU = 0x0004;
const uint32_t UNIT_MEMORY = 0x0008;
const uint32_t UNIT_DMA = 0x0010;
const uint32_t UNIT_LCD = 0x0020;
const uint32_t UNIT_SOUND = 0x0040;
const uint32_t UNIT_KEYPAD = 0x0080;
const uint32_t UNIT_TIMER0 = 0x0100;
const uint32_t UNIT_TIMER1 = 0x0200;
const uint32_t UNIT_TIMER2 = 0x0400;
const uint32_t UNIT_TIMER3 = 0x0800;
const uint32_t UNIT_MEMORY_ROM = 0x1000;
const uint32_t UNIT_MEMORY_BIOS = 0x2000;
const uint32_t UNIT_ALL = 0x3FFF;
void Reset (uint32_t units);
bool SaveState (const char* filename);
bool LoadState (const char* filename);
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
inline void Run (unsigned int cycles)
{
_cpu.Run(cycles);
}
inline void Stop ()
{
_cpu.Stop();
}
}
#endif

View File

@ -1,78 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_D_SOUND_H__
#define __AUDIO_D_SOUND_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Audio
{
class DSound
{
public :
static const int BUFFER_SIZE = 32;
DSound ();
void FillFifo (int8_t* buffer);
void FillFifo (int8_t sample);
void NextSample ()
{
// if the buffer is empty, there is nothing to do
if (m_size)
// if this was the last sample, we reset all and send 0 to all next
// GetSample()s until the buffer is refilled
if (--m_size == 0)
Reset();
// else, we go on to next sample and we go to the first if we got
// to the last
else if (++m_rpos >= BUFFER_SIZE)
m_rpos = 0;
}
void Reset ()
{
m_buffer[0] = m_size = m_rpos = m_wpos = 0;
}
int8_t GetSample()
{
return m_buffer[m_rpos];
}
uint8_t GetSize ()
{
return m_size;
};
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
int8_t m_buffer[BUFFER_SIZE];
uint8_t m_rpos, m_wpos;
uint8_t m_size;
};
}
}
#endif

View File

@ -1,75 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_SOUND_1_H__
#define __AUDIO_SOUND_1_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Audio
{
class Sound1
{
public :
Sound1 (uint16_t& cntl, uint16_t& cnth, uint16_t& cntx,
uint16_t freq);
void Reset ();
// call this at the frequence given in constructor
void SoundTick ();
void ResetSound ();
void ResetEnvelope ()
{
m_envelope = 0;
}
int8_t GetSample () const
{
return m_sample;
}
bool IsOn () const
{
return m_on;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
uint16_t &m_cntl, &m_cnth, &m_cntx;
bool m_on;
// positions in Period, Sweep time and Envelope step time
uint32_t m_posP, m_posS, m_posE;
int8_t m_sample;
// sample period in cycles
uint16_t m_speriod;
// envelope level
uint8_t m_envelope;
// sound length in cycles
uint32_t m_length;
bool m_timed;
};
}
}
#endif

View File

@ -1,70 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_SOUND_2_H__
#define __AUDIO_SOUND_2_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Audio
{
class Sound2
{
public :
Sound2 (uint16_t& cntl, uint16_t& cnth, uint16_t freq);
void Reset ();
// call this at the frequence given in constructor
void SoundTick ();
void ResetSound ();
void ResetEnvelope ()
{
m_envelope = 0;
}
int8_t GetSample () const
{
return m_sample;
}
bool IsOn () const
{
return m_on;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
uint16_t &m_cntl, &m_cnth;
bool m_on;
uint32_t m_posP, m_posE;
int8_t m_sample;
uint16_t m_speriod;
uint8_t m_envelope;
uint32_t m_length;
bool m_timed;
};
}
}
#endif

View File

@ -1,78 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_SOUND_4_H__
#define __AUDIO_SOUND_4_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Audio
{
void InitNoise ();
class Sound4
{
public :
Sound4 (uint16_t& cntl, uint16_t& cnth, uint16_t freq);
void Reset ();
// call this at the frequence given in constructor
void SoundTick ();
void ResetSound ();
void ResetEnvelope ()
{
m_envelope = 0;
}
int8_t GetSample () const
{
return m_sample;
}
bool IsOn () const
{
return m_on;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
uint16_t &m_cntl, &m_cnth;
bool m_on;
// positions in Period, position in noise and Envelope step time
uint32_t m_posP, m_posN, m_posE;
int8_t m_sample;
// sample period in cycles
uint16_t m_speriod;
// envelope level
uint8_t m_envelope;
// sound length in cycles
uint32_t m_length;
bool m_timed;
// clock divider
uint8_t m_div;
};
}
}
#endif

View File

@ -1,173 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_SPEAKER_H__
#define __AUDIO_SPEAKER_H__
#include <stdint.h>
#include <istream>
#include <ostream>
#include <syg/signal.hpp>
#include "sound1.hpp"
#include "sound2.hpp"
#include "sound4.hpp"
#include "dsound.hpp"
namespace AMeteor
{
namespace Audio
{
class Speaker
{
public :
typedef syg::slot1<void, const int16_t*> FrameSlot;
Speaker (uint16_t& cnt1l, uint16_t& cnt1h, uint16_t& cnt1x,
uint16_t& cnt2l, uint16_t& cnt2h,
uint16_t& cnt4l, uint16_t& cnt4h,
uint16_t& cntl, uint16_t& cnth, uint16_t& cntx, uint16_t& bias);
~Speaker ();
inline void SetFrameSlot (const FrameSlot& slot);
void Reset ();
inline void ResetSound1 ();
inline void ResetSound2 ();
inline void ResetSound4 ();
inline void ResetSound1Envelope ();
inline void ResetSound2Envelope ();
inline void ResetSound4Envelope ();
inline void FillFifoA (int8_t* buffer);
inline void FillFifoB (int8_t* buffer);
inline void FillFifoA (int8_t sample);
inline void FillFifoB (int8_t sample);
inline void ResetFifoA ();
inline void ResetFifoB ();
inline void NextSampleA ();
inline void NextSampleB ();
inline uint8_t GetSizeA();
inline uint8_t GetSizeB();
void SoundTick ();
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
Sound1 m_sound1;
Sound2 m_sound2;
Sound4 m_sound4;
DSound m_dsa, m_dsb;
uint16_t &m_cntl, &m_cnth, &m_cntx, &m_bias;
FrameSlot m_sig_frame;
int16_t MixSample (uint16_t cntl, uint8_t cnth);
};
inline void Speaker::SetFrameSlot (const FrameSlot& slot)
{
m_sig_frame = slot;
}
inline void Speaker::ResetSound1 ()
{
m_sound1.ResetSound ();
}
inline void Speaker::ResetSound2 ()
{
m_sound2.ResetSound ();
}
inline void Speaker::ResetSound4 ()
{
m_sound4.ResetSound ();
}
inline void Speaker::ResetSound1Envelope ()
{
m_sound1.ResetEnvelope();
}
inline void Speaker::ResetSound2Envelope ()
{
m_sound2.ResetEnvelope();
}
inline void Speaker::ResetSound4Envelope ()
{
m_sound4.ResetEnvelope();
}
inline void Speaker::FillFifoA (int8_t* buffer)
{
m_dsa.FillFifo(buffer);
}
inline void Speaker::FillFifoB (int8_t* buffer)
{
m_dsb.FillFifo(buffer);
}
inline void Speaker::FillFifoA (int8_t sample)
{
m_dsa.FillFifo(sample);
}
inline void Speaker::FillFifoB (int8_t sample)
{
m_dsb.FillFifo(sample);
}
inline void Speaker::ResetFifoA ()
{
m_dsa.Reset();
}
inline void Speaker::ResetFifoB ()
{
m_dsb.Reset();
}
inline void Speaker::NextSampleA ()
{
m_dsa.NextSample();
}
inline void Speaker::NextSampleB ()
{
m_dsb.NextSample();
}
inline uint8_t Speaker::GetSizeA()
{
return m_dsa.GetSize();
}
inline uint8_t Speaker::GetSizeB()
{
return m_dsb.GetSize();
}
}
}
#endif

View File

@ -1,59 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __BIOS_H__
#define __BIOS_H__
#include "ameteor.hpp"
namespace AMeteor
{
namespace Bios
{
// Entry point
void Bios000h ();
// Software IRQ
void Bios008h ();
void Bios168h ();
// Return from IntrWait (after the IRQ)
void Bios338h ();
// IRQ
void Bios018h ();
void Bios130h ();
void SoftReset (); // 00
void RegisterRamReset (); // 01
void Halt (); // 02
void IntrWait (); // 04
void VBlankIntrWait (); // 05
void Div (); // 06
void DivArm (); // 07
void Sqrt (); // 08
void ArcTan (); // 09
void ArcTan2 (); // 0A
void CpuSet (); // 0B
void CpuFastSet (); // 0C
void BgAffineSet (); // 0E
void ObjAffineSet (); // 0F
void LZ77UnCompWram (); // 11
void LZ77UnCompVram (); // 12
void HuffUnComp (); // 13
void RLUnCompWram (); // 14
void RLUnCompVram (); // 15
}
}
#endif

View File

@ -1,57 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __CART_MEM_H__
#define __CART_MEM_H__
#include <fstream>
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class CartMem
{
public:
static const unsigned int MAX_SIZE = 0x20000;
CartMem();
virtual ~CartMem();
virtual void Reset () = 0;
virtual bool Load (std::istream& stream) = 0;
virtual bool Save (std::ostream& stream) = 0;
virtual uint8_t Read (uint16_t add) = 0;
// returns true if memory has been updated
virtual bool Write (uint16_t add, uint8_t val) = 0;
virtual bool SaveState (std::ostream& stream);
virtual bool LoadState (std::istream& stream);
protected:
uint8_t* m_data;
uint32_t m_size;
};
#ifdef __LIBRETRO__
extern uint8_t CartMemData[CartMem::MAX_SIZE+4];
#endif
}
#endif

View File

@ -1,111 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __CLOCK_H__
#define __CLOCK_H__
#include <stdint.h>
#include <climits>
#include <vector>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Clock
{
public :
Clock ()
{
Reset();
}
void Reset ();
void ResetCounter()
{
m_count = 0;
}
unsigned int GetCounter() const
{
return m_count;
}
void TimePass (unsigned short cycles)
{
m_cycles += cycles;
}
void Commit ();
void WaitForNext ();
void AddLcd (uint32_t cycles)
{
// The lcd clock is always enabled
m_lcd += cycles;
SetFirst();
}
void AddTimer (uint8_t num, uint32_t cycles)
{
if (m_timer[num] == INT_MAX)
m_timer[num] = cycles;
else
m_timer[num] += cycles;
SetFirst();
}
void SetTimer (uint8_t num, uint32_t cycles)
{
m_timer[num] = cycles;
SetFirst();
}
void DisableTimer (uint8_t num)
{
m_timer[num] = INT_MAX;
SetFirst();
}
int GetTimer (uint8_t num)
{
return m_timer[num];
}
//void SetBattery (uint32_t cycles)
//{
// m_battery = cycles;
//}
//void DisableBattery ()
//{
// m_battery = INT_MAX;
// no need to SetFirst since battery will be disabled only in TimeEvent
//}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
// XXX freq
static const int SOUND_PERIOD = 380;
unsigned short m_cycles;
unsigned short m_first;
int m_lcd, m_timer[4], m_sound;//, m_battery;
unsigned int m_count;
void SetFirst ();
};
}
#endif

View File

@ -1,148 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __CPU_H__
#define __CPU_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Cpu
{
public :
union Psr
{
uint32_t dw;
struct
{
unsigned int mode : 5;
unsigned int thumb : 1;
unsigned int fiq_d : 1;
unsigned int irq_d : 1;
unsigned int reserved : 19;
unsigned int s_overflow : 1;
unsigned int f_overflow : 1;
unsigned int f_carry : 1;
unsigned int f_zero : 1;
unsigned int f_sign : 1;
} b;
};
struct IPsr
{
uint8_t mode;
bool thumb;
bool fiq_d;
bool irq_d;
bool s_overflow;
bool f_overflow;
bool f_carry;
bool f_zero;
bool f_sign;
};
enum Modes
{
M_USR = 0x10,
M_FIQ = 0x11,
M_IRQ = 0x12,
M_SVC = 0x13,
M_ABT = 0x17,
M_UND = 0x1B,
M_SYS = 0x1F
};
Cpu ();
virtual ~Cpu () {}
virtual void Reset ();
virtual void SoftReset ();
void UpdateICpsr ();
void UpdateCpsr ();
void SwitchToMode (uint8_t newmode);
void SwitchModeBack ();
virtual void SendInterrupt (uint16_t interrupt) = 0;
virtual void CheckInterrupt () = 0;
void Interrupt ();
void SoftwareInterrupt (uint32_t comment);
void SoftwareInterrupt ();
uint32_t& Reg(uint8_t r)
{
return m_st.r[r];
}
Psr& Cpsr()
{
return m_st.cpsr;
}
IPsr& ICpsr()
{
return m_st.icpsr;
}
Psr& Spsr()
{
return m_st.spsr;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
protected :
struct CPUState
{
// Current registers
uint32_t r[16];
Psr cpsr, spsr;
IPsr icpsr;
// System/User
uint32_t usr_r[7]; // from 8 to 14
// FIQ
uint32_t fiq_r[7]; // from 8 to 14
Psr fiq_spsr;
// Supervisor
uint32_t svc_r[2]; // 13 and 14
Psr svc_spsr;
// Abort
uint32_t abt_r[2]; // 13 and 14
Psr abt_spsr;
// IRQ
uint32_t irq_r[2]; // 13 and 14
Psr irq_spsr;
// Undefined
uint32_t und_r[2]; // 13 and 14
Psr und_spsr;
};
CPUState m_st;
virtual void SetInterrupt (bool interrupt) = 0;
private :
void SaveMode (uint8_t mode);
};
}
#endif

View File

@ -1,44 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __ARG_IMMEDIATE_H__
#define __ARG_IMMEDIATE_H__
#include "argument.hpp"
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class ArgImmediate : public Argument
{
public :
ArgImmediate (int32_t imm) :
m_imm(imm)
{ }
Argument* Clone () const;
std::string GetString () const;
private :
int32_t m_imm;
};
}
}
#endif

View File

@ -1,67 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __ARG_MUL_REGISTERS_H__
#define __ARG_MUL_REGISTERS_H__
#include "argument.hpp"
#include <stdint.h>
#include <vector>
namespace AMeteor
{
namespace Disassembler
{
enum SpecialRegister
{
SPREG_NONE = 0,
SPREG_LR = 1,
SPREG_PC = 2
};
class ArgMulRegisters : public Argument
{
public :
ArgMulRegisters (bool forceuser) :
m_lastreg(SPREG_NONE),
m_forceuser(forceuser)
{ }
Argument* Clone () const;
void AddRegister(uint8_t reg)
{
m_regs.push_back(reg);
}
void AddLastRegister(SpecialRegister reg)
{
m_lastreg = reg;
}
std::string GetString () const;
private :
typedef std::vector<uint8_t> Registers;
Registers m_regs;
SpecialRegister m_lastreg;
bool m_forceuser;
};
}
}
#endif

View File

@ -1,47 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __ARG_PSR_H__
#define __ARG_PSR_H__
#include "argument.hpp"
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class ArgPsr : public Argument
{
public :
ArgPsr (bool spsr, uint8_t fields = 0xFF) :
m_spsr(spsr),
m_fields(fields)
{ }
Argument* Clone () const;
std::string GetString () const;
private :
bool m_spsr;
uint8_t m_fields;
};
}
}
#endif

View File

@ -1,56 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __ARG_REGISTER_H__
#define __ARG_REGISTER_H__
#include "argument.hpp"
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class ArgRegister : public Argument
{
public :
ArgRegister (uint8_t reg, bool writeback = false,
bool special = false, bool memory = false) :
m_reg(reg),
m_writeback(writeback),
m_special(special),
m_memory(memory)
{ }
Argument* Clone () const;
std::string GetString () const;
uint8_t GetRegister () const
{
return m_reg;
}
private :
uint8_t m_reg;
bool m_writeback;
bool m_special;
bool m_memory;
};
}
}
#endif

View File

@ -1,50 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __ARG_RELATIVE_H__
#define __ARG_RELATIVE_H__
#include "argument.hpp"
#include "argregister.hpp"
#include "argimmediate.hpp"
namespace AMeteor
{
namespace Disassembler
{
class ArgRelative : public Argument
{
public :
ArgRelative (const ArgRegister& reg, const Argument& off,
bool pre, bool up, bool writeback);
ArgRelative (const ArgRelative& arg);
~ArgRelative ();
Argument* Clone () const;
std::string GetString () const;
private :
ArgRegister m_reg;
const Argument* m_off;
bool m_pre;
bool m_up;
bool m_writeback;
};
}
}
#endif

View File

@ -1,56 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __ARG_SHIFT_H__
#define __ARG_SHIFT_H__
#include "argument.hpp"
namespace AMeteor
{
namespace Disassembler
{
enum ShiftType
{
SHIFT_LSL = 0,
SHIFT_LSR,
SHIFT_ASR,
SHIFT_ROR,
SHIFT_RRX
};
class ArgShift : public Argument
{
public :
ArgShift (const Argument& arg1, const Argument& arg2,
ShiftType type, bool memory);
ArgShift (const ArgShift& arg);
~ArgShift ();
Argument* Clone () const;
std::string GetString () const;
private :
const Argument* m_arg1;
const Argument* m_arg2;
ShiftType m_type;
bool m_memory;
};
}
}
#endif

View File

@ -1,45 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __ARG_U_IMMEDIATE_H__
#define __ARG_U_IMMEDIATE_H__
#include "argument.hpp"
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class ArgUImmediate : public Argument
{
public :
ArgUImmediate (uint32_t imm) :
m_imm(imm)
{ }
Argument* Clone () const;
std::string GetString () const;
private :
uint32_t m_imm;
};
}
}
#endif

View File

@ -1,39 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __ARGUMENT_H__
#define __ARGUMENT_H__
#include <string>
namespace AMeteor
{
namespace Disassembler
{
class Argument
{
public :
virtual ~Argument ()
{ }
virtual Argument* Clone () const = 0;
virtual std::string GetString () const = 0;
};
}
}
#endif

View File

@ -1,48 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __ARGUMENTS_H__
#define __ARGUMENTS_H__
#include "argument.hpp"
#include <vector>
namespace AMeteor
{
namespace Disassembler
{
class Arguments
{
public :
~Arguments ();
void Clear ();
void AddArgument(const Argument& arg)
{
m_args.push_back(arg.Clone());
}
std::string GetString () const;
private :
std::vector<Argument*> m_args;
};
}
}
#endif

View File

@ -1,76 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __INSTRUCTION_H__
#define __INSTRUCTION_H__
#include "arguments.hpp"
#include <string>
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class Instruction
{
public :
Instruction ()
{
}
explicit Instruction (uint32_t offset, uint32_t code)
{
ParseArm (offset, code);
}
explicit Instruction (uint32_t offset, uint16_t code)
{
ParseThumb(offset, code);
}
void Clear ();
void ParseArm (uint32_t offset, uint32_t code);
void ParseThumb (uint32_t offset, uint16_t code);
const std::string& GetOperator () const
{
return m_operator;
}
std::string GetArguments () const
{
return m_args.GetString();
}
std::string ToString () const
{
return GetOperator() + ' ' + GetArguments();
}
private :
std::string m_operator;
Arguments m_args;
void ParseArmDataProc (uint32_t code);
void ParseArmCondition (uint32_t code);
};
}
}
#endif

View File

@ -1,113 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __DMA_H__
#define __DMA_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Dma
{
public :
enum Reason
{
Immediately = 0,
VBlank,
HBlank,
Special
};
Dma () :
m_graphic(false)
{ }
void Reset ();
bool GraphicDma () const
{
return false;
//return m_graphic;
}
void SetReload(uint8_t channum, uint16_t reload)
{
m_chans[channum].reload = reload;
}
void UpdateCnt (uint8_t channum);
void Check(uint8_t channum, uint8_t reason);
inline void CheckAll(uint8_t reason)
{
Check(0, reason);
Check(1, reason);
Check(2, reason);
Check(3, reason);
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
struct Channel
{
Channel () :
reload(0),
src(0),
dest(0),
count(0),
control(0)
{ }
uint16_t reload;
uint32_t src;
uint32_t dest;
uint16_t count;
union Control
{
Control(uint16_t v) :
w(v)
{ }
uint16_t w;
struct
{
unsigned int unused : 5;
unsigned int dest : 2;
unsigned int src : 2;
unsigned int repeat : 1;
unsigned int type : 1;
unsigned int drq : 1;
unsigned int start : 2;
unsigned int irq : 1;
unsigned int enable : 1;
} b;
} control;
};
Channel m_chans[4];
bool m_graphic;
void Process(uint8_t channel);
void Copy (uint32_t& src, uint32_t& dest, int8_t s_inc, int8_t d_inc,
uint32_t count, bool word);
};
}
#endif

View File

@ -1,79 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __EEPROM_H__
#define __EEPROM_H__
#include "cartmem.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Eeprom : public CartMem
{
public :
Eeprom (bool big);
void Reset ();
uint16_t GetSize () const
{
return m_size;
}
bool Load (std::istream& f);
bool Save (std::ostream& f);
uint8_t Read (uint16_t add);
bool Write (uint16_t add, uint8_t val);
uint16_t Read ();
//bool Write (uint16_t val);
bool Write (uint16_t* data, uint16_t size);
//XXX
#if 0
void Read (uint16_t* pOut);
#endif
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
enum State
{
IDLE,
//WAITING,
//READ_ADD,
//READ_END,
READ_GARBAGE,
READ_DATA
/*WRITE_ADD,
WRITE_DATA,
WRITE_END*/
};
uint8_t m_state;
uint16_t m_add;
uint8_t m_pos;
};
}
#endif

View File

@ -1,63 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __FLASH_H__
#define __FLASH_H__
#include "cartmem.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Flash : public CartMem
{
public :
Flash (bool big);
void Reset ();
bool Load (std::istream& f);
bool Save (std::ostream& f);
uint8_t Read (uint16_t add);
bool Write (uint16_t add, uint8_t val);
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
uint8_t m_device_id;
uint8_t m_manufacturer_id;
enum State
{
NORMAL,
CMD1,
CMD2,
ID,
ERASE1,
ERASE2,
ERASE3,
WRITE
};
State m_state;
};
}
#endif

View File

@ -1,100 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_BG_LAYER_H__
#define __GRAPHICS_BG_LAYER_H__
#include "ameteor/memory.hpp"
#include "ameteor/io.hpp"
#include <vector>
#include <stdint.h>
namespace AMeteor
{
namespace Graphics
{
class BgLayer
{
public :
BgLayer (int8_t num, Memory& memory, Io& io, uint16_t* pPalette);
~BgLayer ();
inline uint8_t GetNum () const;
inline uint8_t GetPriority () const;
void DrawLine0 (uint8_t line, uint16_t* ptr);
void DrawLine2 (uint16_t* ptr,
int32_t refX, int32_t refY,
int16_t dx, int16_t dy);
void DrawLine3 (uint16_t* ptr,
int32_t refX, int32_t refY,
int16_t dx, int16_t dy);
void DrawLine4 (uint8_t line, uint16_t* ptr,
int32_t curX, int32_t curY,
int16_t dx, int16_t dmx, int16_t dy, int16_t dmy, bool frame1);
void DrawLine5 (uint16_t* ptr,
int32_t refX, int32_t refY,
int16_t dx, int16_t dy, bool frame1);
void FillList ();
void UpdateCnt (uint16_t cnt);
inline void UpdateXOff (uint16_t off);
inline void UpdateYOff (uint16_t off);
private :
Memory& m_memory;
Io& m_io;
const uint8_t m_num;
uint8_t m_priority;
uint16_t m_cnt;
bool m_hicolor;
uint16_t m_xoff, m_yoff;
// in text mode
uint8_t m_tWidth, m_tHeight;
// in rotation/scale mode
uint8_t m_rWidth, m_rHeight;
uint32_t m_mapAdd;
uint32_t m_charAdd;
uint16_t* m_pPalette;
};
inline uint8_t BgLayer::GetNum () const
{
return m_num;
}
inline uint8_t BgLayer::GetPriority () const
{
return m_priority;
}
inline void BgLayer::UpdateXOff (uint16_t off)
{
m_xoff = off;
}
inline void BgLayer::UpdateYOff (uint16_t off)
{
m_yoff = off;
}
}
}
#endif

View File

@ -1,100 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_OBJECT_H__
#define __GRAPHICS_OBJECT_H__
#include <stdint.h>
#include <vector>
#include <list>
namespace AMeteor
{
namespace Graphics
{
class Object
{
public :
static const uint8_t FLIP_HORIZONTAL = 1;
static const uint8_t FLIP_VERTICAL = 2;
Object (uint16_t* pPalette, uint8_t* pChar);
// Warning : this copy constructor must not be used on an used object
// use it only on just created objects
Object (const Object& obj);
inline uint8_t GetPriority () const;
inline int8_t GetRotationParam () const;
inline uint16_t GetTileNum () const;
inline bool IsWindow () const;
void DrawLine (uint8_t line, uint32_t* surface, bool oneDim,
uint8_t mosaic);
void DrawLineRot (uint8_t line, uint32_t* surface, bool oneDim,
int16_t a, int16_t b, int16_t c, int16_t d, uint8_t mosaic);
void DrawWindow (uint8_t line, uint8_t* surface, bool oneDim,
uint8_t mask);
void DrawWindowRot (uint8_t line, uint8_t* surface,
bool oneDim, int16_t a, int16_t b, int16_t c, int16_t d,
uint8_t mask);
void UpdateAttrs (uint16_t attr0, uint16_t attr1, uint16_t attr2);
void UpdateAttr0 (uint16_t attr);
void UpdateAttr1 (uint16_t attr);
void UpdateAttr2 (uint16_t attr);
private :
inline void SetSize ();
enum Shape
{
SHAPE_SQUARE = 0,
SHAPE_HORIZONTAL,
SHAPE_VERTICAL,
SHAPE_PROHIBITED
};
uint16_t m_attr0, m_attr1, m_attr2;
uint8_t m_width, m_height;
uint16_t* m_pPalette;
uint8_t* m_pChar;
uint32_t m_charBegin;
uint32_t m_charEnd;
};
inline int8_t Object::GetRotationParam () const
{
return (m_attr0 & (0x1 << 8)) ? (m_attr1 >> 9) & 0x1F : -1;
}
inline uint8_t Object::GetPriority () const
{
return (m_attr2 >> 10) & 0x3;
}
inline bool Object::IsWindow () const
{
return (m_attr0 & (0x3 << 10)) == (0x2 << 10);
}
inline uint16_t Object::GetTileNum () const
{
return (m_attr2 & 0x3FF);
}
}
}
#endif

View File

@ -1,53 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_OBJECTS_H__
#define __GRAPHICS_OBJECTS_H__
#include "object.hpp"
#include "ameteor/memory.hpp"
#include "ameteor/io.hpp"
#include <vector>
namespace AMeteor
{
namespace Graphics
{
class Objects
{
public :
Objects (Memory& memory, Io& io, uint16_t* pPalette);
void DrawLine (uint8_t line, uint32_t* surface);
void DrawLineHighOnly (uint8_t line, uint32_t* surface);
void DrawWindow (uint8_t line, uint8_t* surface);
void OamWrite (uint32_t begin, uint32_t end);
void OamWrite16 (uint32_t add);
void OamWrite32 (uint32_t add);
private :
typedef std::vector<Object> Objs;
Io& m_io;
Objs m_objs;
uint16_t* m_pOam;
};
}
}
#endif

View File

@ -1,52 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_RENDERER_H__
#define __GRAPHICS_RENDERER_H__
#include <stdint.h>
#include <syg/signal.hpp>
namespace AMeteor
{
namespace Graphics
{
class Renderer
{
public:
typedef syg::slot1<void, const uint16_t*> FrameSlot;
Renderer(const uint16_t* surface);
inline void SetFrameSlot(const FrameSlot& slot);
void VBlank();
private :
const uint16_t* m_base;
FrameSlot m_sig_frame;
};
void Renderer::SetFrameSlot(const FrameSlot& slot)
{
m_sig_frame = slot;
}
}
}
#endif

View File

@ -1,148 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_SCREEN_H__
#define __GRAPHICS_SCREEN_H__
#include "bglayer.hpp"
#include "objects.hpp"
#include "renderer.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Graphics
{
class Screen
{
public :
static const uint8_t WIDTH = 240;
static const uint8_t HEIGHT = 160;
// we skip x frames every FRMSKIP_TOTAL frames
static const uint8_t FRMSKIP_TOTAL = 10;
Screen (Memory& memory, Io& io);
~Screen ();
Renderer& GetRenderer()
{
return m_renderer;
}
const Renderer& GetRenderer() const
{
return m_renderer;
}
const uint16_t* GetSurface () const
{
return m_surface;
}
void SetFrameskip (uint8_t skip)
{
m_frameskip = skip;
m_curframe = 0;
}
void DrawLine (uint8_t line);
void UpdateDispCnt (uint16_t dispcnt)
{
m_dispcnt = dispcnt;
}
#define UPDATE_BG_CNT(num) \
void UpdateBg##num##Cnt (uint16_t cnt) \
{ \
m_bgLayer##num.UpdateCnt(cnt); \
}
UPDATE_BG_CNT(0)
UPDATE_BG_CNT(1)
UPDATE_BG_CNT(2)
UPDATE_BG_CNT(3)
#undef UPDATE_BG_CNT
#define UPDATE_BG_OFF(num, coord) \
void UpdateBg##num##coord##Off (uint16_t off) \
{ \
m_bgLayer##num.Update##coord##Off(off); \
}
UPDATE_BG_OFF(0, X)
UPDATE_BG_OFF(0, Y)
UPDATE_BG_OFF(1, X)
UPDATE_BG_OFF(1, Y)
UPDATE_BG_OFF(2, X)
UPDATE_BG_OFF(2, Y)
UPDATE_BG_OFF(3, X)
UPDATE_BG_OFF(3, Y)
#undef UPDATE_BG_OFF
#define UPDATE_BG_REF(num, coord) \
void UpdateBg##num##Ref##coord(int32_t x) \
{ \
m_ref##coord##num = (x & (0x1 << 27)) ? x | 0xF0000000 : x & 0x07FFFFFF; \
}
UPDATE_BG_REF(2, X)
UPDATE_BG_REF(2, Y)
UPDATE_BG_REF(3, X)
UPDATE_BG_REF(3, Y)
#undef UPDATE_BG_REF
void OamWrite (uint32_t begin, uint32_t end)
{
m_objs.OamWrite (begin, end);
}
void OamWrite16 (uint32_t add)
{
m_objs.OamWrite16 (add);
}
void OamWrite32 (uint32_t add)
{
m_objs.OamWrite32 (add);
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
Io& m_io;
uint16_t* m_surface;
Renderer m_renderer;
uint8_t m_frameskip, m_curframe;
uint16_t m_dispcnt;
int32_t m_refX2, m_refY2, m_refX3, m_refY3;
uint16_t* m_pPalette;
// FIXME is this REALLY useful ?
static BgLayer Screen::* const BgLayers [4];
BgLayer m_bgLayer0, m_bgLayer1, m_bgLayer2, m_bgLayer3;
Objects m_objs;
void DrawWindow (uint8_t line, uint8_t* surface,
uint16_t win0v, uint16_t win0h, uint8_t mask);
};
}
}
#endif

View File

@ -1,133 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __INTERPRETER_H__
#define __INTERPRETER_H__
#include <stdint.h>
#include <istream>
#include <ostream>
#include "cpu.hpp"
#define ARM(name) \
inline void a##name ()
#define NIARM(name) \
void a##name ()
#define THUMB(name) \
inline void t##name ()
#define NITHUMB(name) \
void t##name ()
namespace AMeteor
{
class Interpreter : public Cpu
{
public :
Interpreter();
void Reset ()
{
m_interrupt = m_interrupt_ = false;
m_run = false;
Cpu::Reset();
}
void SoftReset ()
{
m_interrupt_ = m_interrupt = false;
Cpu::SoftReset();
}
void SendInterrupt (uint16_t interrupt);
void CheckInterrupt ();
void Run (unsigned int cycles);
void Stop ()
{
m_run = false;
}
bool IsRunning ()
{
return m_run;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
protected :
void SetInterrupt (bool interrupt)
{
m_interrupt = interrupt;
}
private :
bool m_run;
bool m_interrupt;
bool m_interrupt_;
uint32_t code;
uint8_t& m_haltcnt;
// only for use in halt mode, in normal mode we use m_interrupt
uint16_t& m_if;
uint16_t& m_ie;
NIARM(_Code);
inline bool a_CheckCondition (uint8_t cond);
inline void a_DataProcCore(uint8_t rd, uint32_t op1, uint32_t op2,
bool shiftcarry);
ARM(BXBLX);
ARM(BBL);
NIARM(_DataProcShiftImm);
NIARM(_DataProcShiftReg);
NIARM(_DataProcImm);
ARM(PSR);
ARM(_Multiply);
ARM(LDRSTR);
ARM(STRLDR_HD);
ARM(LDMSTM);
ARM(SWP);
ARM(SWI);
NITHUMB(_Code);
THUMB(_Shift);
THUMB(ADDSUB);
THUMB(_Imm);
THUMB(_ALU);
THUMB(_HiRegOp);
THUMB(LDRimm);
THUMB(STRLDRreg);
THUMB(STRLDRoff);
THUMB(LDRHSTRHoff);
THUMB(STRLDRsp);
THUMB(ADDpcsp);
THUMB(ADDsp);
THUMB(PUSHPOP);
THUMB(STMLDM);
THUMB(_CondBranch);
THUMB(SWI);
THUMB(B);
THUMB(_BL1);
THUMB(_BL2);
};
}
#undef ARM
#undef NIARM
#undef THUMB
#undef NITHUMB
#endif

View File

@ -1,219 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __IO_H__
#define __IO_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Io
{
public :
enum IoAddress
{
// LCD I/O Registers
DISPCNT = 0x000,
DISPSTAT = 0x004,
VCOUNT = 0x006,
BG0CNT = 0x008,
BG1CNT = 0x00A,
BG2CNT = 0x00C,
BG3CNT = 0x00E,
BG0HOFS = 0x010,
BG0VOFS = 0x012,
BG1HOFS = 0x014,
BG1VOFS = 0x016,
BG2HOFS = 0x018,
BG2VOFS = 0x01A,
BG3HOFS = 0x01C,
BG3VOFS = 0x01E,
BG2PA = 0x020,
BG2PB = 0x022,
BG2PC = 0x024,
BG2PD = 0x026,
BG2X_L = 0x028,
BG2X_H = 0x02A,
BG2Y_L = 0x02C,
BG2Y_H = 0x02E,
BG3PA = 0x030,
BG3PB = 0x032,
BG3PC = 0x034,
BG3PD = 0x036,
BG3X_L = 0x038,
BG3X_H = 0x03A,
BG3Y_L = 0x03C,
BG3Y_H = 0x03E,
WIN0H = 0x040,
WIN1H = 0x042,
WIN0V = 0x044,
WIN1V = 0x046,
WININ = 0x048,
WINOUT = 0x04A,
MOSAIC = 0x04C,
BLDCNT = 0x050,
BLDALPHA = 0x052,
BLDY = 0x054,
// Sound Registers
SOUND1CNT_L = 0x060, NR10 = 0x060,
SOUND1CNT_H = 0x062, NR11 = 0x062,
NR12 = 0x063,
SOUND1CNT_X = 0x064, NR13 = 0x064,
NR14 = 0x065,
SOUND2CNT_L = 0x068, NR21 = 0x068,
NR22 = 0x069,
SOUND2CNT_H = 0x06C, NR23 = 0x06C,
NR24 = 0x06D,
SOUND4CNT_L = 0x078, NR41 = 0x078,
NR42 = 0x079,
SOUND4CNT_H = 0x07C, NR43 = 0x07C,
NR44 = 0x07D,
SOUNDCNT_L = 0x080, NR50 = 0x080,
NR51 = 0x081,
SOUNDCNT_H = 0x082,
SOUNDCNT_X = 0x084, NR52 = 0x084,
SOUNDBIAS = 0x088,
FIFO_A = 0x0A0,
FIFO_B = 0x0A4,
// DMA Transfer Channels
DMA0SAD = 0x0B0,
DMA0DAD = 0x0B4,
DMA0CNT_L = 0x0B8,
DMA0CNT_H = 0x0BA,
DMA1SAD = 0x0BC,
DMA1DAD = 0x0C0,
DMA1CNT_L = 0x0C4,
DMA1CNT_H = 0x0C6,
DMA2SAD = 0x0C8,
DMA2DAD = 0x0CC,
DMA2CNT_L = 0x0D0,
DMA2CNT_H = 0x0D2,
DMA3SAD = 0x0D4,
DMA3DAD = 0x0D8,
DMA3CNT_L = 0x0DC,
DMA3CNT_H = 0x0DE,
// Timer Registers
TM0CNT_L = 0x100,
TM0CNT_H = 0x102,
TM1CNT_L = 0x104,
TM1CNT_H = 0x106,
TM2CNT_L = 0x108,
TM2CNT_H = 0x10A,
TM3CNT_L = 0x10C,
TM3CNT_H = 0x10E,
// Keypad Input
KEYINPUT = 0x130,
KEYCNT = 0x132,
// Serial Communication (2)
RCNT = 0x134,
// Interrupt, WaitState, and Power-Down Control
IE = 0x200,
IF = 0x202,
WAITCNT = 0x204,
IME = 0x208,
POSTFLG = 0x300,
HALTCNT = 0x301,
DMA_CHANSIZE = 0x00C,
TIMER_SIZE = 0x004,
// TODO make tests and everything in Write*() functions so that we can
// make IO_SIZE 0x804 (don't forget mirrors)
IO_SIZE = 0x1000
};
Io ();
~Io ();
void Reset ();
void ClearSio ();
void ClearSound ();
void ClearOthers ();
uint8_t Read8 (uint32_t add);
uint16_t Read16 (uint32_t add);
uint32_t Read32 (uint32_t add);
void Write8 (uint32_t add, uint8_t val);
void Write16 (uint32_t add, uint16_t val);
void Write32 (uint32_t add, uint32_t val);
// Direct read and write
// Using theses functions will write directly on IO memory without
// doing anything else (they won't call Dma::Check for example)
// add must be the real address & 0xFFF
// No check is done on the memory, these functions may segfault if
// you give them wrong values !
uint8_t DRead8 (uint16_t add)
{
return m_iomem[add];
}
uint16_t DRead16 (uint16_t add)
{
return *(uint16_t*)(m_iomem+add);
}
uint32_t DRead32 (uint16_t add)
{
return *(uint32_t*)(m_iomem+add);
}
void DWrite8 (uint16_t add, uint8_t val)
{
m_iomem[add] = val;
}
void DWrite16 (uint16_t add, uint16_t val)
{
*(uint16_t*)(m_iomem+add) = val;
}
void DWrite32 (uint16_t add, uint32_t val)
{
*(uint32_t*)(m_iomem+add) = val;
}
uint8_t& GetRef8 (uint16_t add)
{
return m_iomem[add];
}
uint16_t& GetRef16 (uint16_t add)
{
return *(uint16_t*)(m_iomem+add);
}
uint32_t& GetRef32 (uint16_t add)
{
return *(uint32_t*)(m_iomem+add);
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
// this should be considered very dangerous to use
uint8_t* GetIoPointer() {return m_iomem;}
private :
uint8_t* m_iomem;
};
}
#endif

View File

@ -1,105 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __KEYPAD_H__
#define __KEYPAD_H__
#include <stdint.h>
#include <map>
namespace AMeteor
{
class Keypad
{
public :
enum Button
{
BTN_A = 0x001,
BTN_B = 0x002,
BTN_SELECT = 0x004,
BTN_START = 0x008,
BTN_RIGHT = 0x010,
BTN_LEFT = 0x020,
BTN_UP = 0x040,
BTN_DOWN = 0x080,
BTN_R = 0x100,
BTN_L = 0x200
};
Keypad ();
void Reset ()
{
}
void BindKey(int code, Button btn)
{
m_keys[code] = (uint16_t)btn;
}
void UnbindKey(int code)
{
m_keys.erase(code);
}
void BindJoy(uint16_t joyid, uint16_t button, Button btn)
{
m_joys[((int)joyid) << 16 | button] = (uint16_t)btn;
}
void UnbindJoy(uint16_t joyid, uint16_t button)
{
m_joys.erase(((int)joyid) << 16 | button);
}
void BindAxis(uint16_t joyid, uint16_t axis, Button btn)
{
m_axis[((int)joyid) << 16 | axis] = (uint16_t)btn;
}
void UnbindAxis(uint16_t joyid, uint16_t axis)
{
m_axis.erase(((int)joyid) << 16 | axis);
}
void ResetBindings()
{
m_keys.clear();
m_joys.clear();
m_axis.clear();
}
inline void SetPadState(uint16_t keys);
void KeyPressed(int code);
void KeyReleased(int code);
void JoyButtonPressed (uint16_t joyid, uint16_t button);
void JoyButtonReleased (uint16_t joyid, uint16_t button);
void JoyMoved (uint16_t joyid, uint16_t axis, float pos);
void VBlank ();
private :
uint16_t& m_keyinput;
uint16_t& m_keycnt;
std::map<int, uint16_t> m_keys;
std::map<int, uint16_t> m_joys;
std::map<int, uint16_t> m_axis;
};
void Keypad::SetPadState(uint16_t keys)
{
m_keyinput = keys;
}
}
#endif

View File

@ -1,129 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __LCD_H__
#define __LCD_H__
#include "clock.hpp"
#include "memory.hpp"
#include "io.hpp"
#include "graphics/screen.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
#include <syg/signal.hpp>
namespace AMeteor
{
class Lcd
{
public :
Lcd ();
void Reset ();
const uint16_t* GetSurface () const
{
return m_screen.GetSurface();
}
Graphics::Screen& GetScreen()
{
return m_screen;
}
const Graphics::Screen& GetScreen() const
{
return m_screen;
}
void SetFrameskip (uint8_t skip)
{
m_screen.SetFrameskip(skip);
}
void UpdateDispCnt (uint16_t dispcnt)
{
m_screen.UpdateDispCnt(dispcnt);
}
#define UPDATE_BG_CNT(num) \
void UpdateBg##num##Cnt (uint16_t cnt) \
{ \
m_screen.UpdateBg##num##Cnt(cnt); \
}
UPDATE_BG_CNT(0)
UPDATE_BG_CNT(1)
UPDATE_BG_CNT(2)
UPDATE_BG_CNT(3)
#undef UPDATE_BG_CNT
#define UPDATE_BG_OFF(num, coord) \
void UpdateBg##num##coord##Off (uint16_t cnt) \
{ \
m_screen.UpdateBg##num##coord##Off(cnt); \
}
UPDATE_BG_OFF(0, X)
UPDATE_BG_OFF(0, Y)
UPDATE_BG_OFF(1, X)
UPDATE_BG_OFF(1, Y)
UPDATE_BG_OFF(2, X)
UPDATE_BG_OFF(2, Y)
UPDATE_BG_OFF(3, X)
UPDATE_BG_OFF(3, Y)
#undef UPDATE_BG_OFF
#define UPDATE_BG_REF(num, coord) \
void UpdateBg##num##Ref##coord (int32_t cnt) \
{ \
m_screen.UpdateBg##num##Ref##coord (cnt); \
}
UPDATE_BG_REF(2, X)
UPDATE_BG_REF(2, Y)
UPDATE_BG_REF(3, X)
UPDATE_BG_REF(3, Y)
#undef UPDATE_BG_REF
void OamWrite (uint32_t begin, uint32_t end)
{
m_screen.OamWrite(begin, end);
}
void OamWrite16 (uint32_t add)
{
m_screen.OamWrite16(add);
}
void OamWrite32 (uint32_t add)
{
m_screen.OamWrite32(add);
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
//syg::signal<void> sig_vblank;
private :
Graphics::Screen m_screen;
void TimeEvent ();
friend void Clock::Commit();
};
}
#endif

View File

@ -1,162 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __MEMORY_H__
#define __MEMORY_H__
#include "cartmem.hpp"
//XXX
#include "eeprom.hpp"
#include <stdint.h>
#include <string>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Memory
{
public :
// in cycles
static const uint32_t CART_SAVE_TIME = 16*1024*1024; // 1 second
enum CartType
{
CTYPE_UNKNOWN,
CTYPE_EEPROM512,
CTYPE_EEPROM8192,
CTYPE_FLASH64,
CTYPE_FLASH128,
CTYPE_SRAM
};
enum CartError
{
CERR_NO_ERROR,
CERR_NOT_FOUND,
CERR_FAIL
};
Memory ();
~Memory ();
uint8_t GetCartType () const
{
return m_carttype;
}
// erases cartridge memory
void SetCartTypeFromSize (uint32_t size);
void SetCartType (uint8_t type);
//void SetCartFile (const char* filename)
//{
// m_cartfile = filename;
//}
void Reset (uint32_t params = ~0);
void ClearWbram ();
void ClearWcram ();
void ClearPalette ();
void ClearVram ();
void ClearOam ();
void SoftReset ();
bool LoadBios (const char* filename);
void LoadBios (const uint8_t* data, uint32_t size);
void UnloadBios ()
{
if (m_brom)
{
delete [] m_brom;
m_brom = NULL;
}
}
bool LoadRom (const char* filename);
void LoadRom (const uint8_t* data, uint32_t size);
//CartError LoadCart ();
bool LoadCart (const uint8_t* data, uint32_t size);
bool SaveCart (uint8_t** data, uint32_t* size);
void SaveCartDestroy(uint8_t* data);
#ifdef __LIBRETRO__
bool LoadCartInferred ();
#endif
bool HasCart () const
{
return m_cart;
}
void DeleteCart();
bool HasBios () const
{
return m_brom;
}
uint8_t GetCycles16NoSeq (uint32_t add, uint32_t count);
uint8_t GetCycles16Seq (uint32_t add, uint32_t count);
uint8_t GetCycles32NoSeq (uint32_t add, uint32_t count);
uint8_t GetCycles32Seq (uint32_t add, uint32_t count);
void UpdateWaitStates (uint16_t waitcnt);
uint8_t* GetRealAddress(uint32_t add, uint8_t size = 0);
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
uint8_t Peek8 (uint32_t add);
// TODO make const members
uint8_t Read8 (uint32_t add);
uint16_t Read16 (uint32_t add);
uint32_t Read32 (uint32_t add);
void Write8 (uint32_t add, uint8_t val);
void Write16 (uint32_t add, uint16_t val);
void Write32 (uint32_t add, uint32_t val);
void WriteEepromDma (uint32_t src, uint16_t size);
//void ReadEepromDma (uint32_t dest, uint16_t size);
void TimeEvent ();
uint8_t* GetMemoryArea(int which);
private :
// times for a 8 or 16 bits access
uint8_t m_memtime[0xF];
// times for a sequential 8 or 16 bits access in GamePak ROM
uint8_t m_memtimeseq[0x3];
// General Internal Memory
uint8_t* m_brom; // BIOS - System ROM
uint8_t* m_wbram; // WRAM - On-board Work RAM
uint8_t* m_wcram; // WRAM - In-chip Work RAM
// Internal Display Memory
uint8_t* m_pram; // BG/OBJ Palette RAM
uint8_t* m_vram; // VRAM - Video RAM
uint8_t* m_oram; // OAM - OBJ Attributes
// External Memory (Game Pak)
uint8_t* m_rom; // Game Pake ROM/FlashROM (max 32MB)
uint8_t m_carttype;
CartMem* m_cart;
//std::string m_cartfile;
uint8_t ReadCart (uint16_t add);
void WriteCart (uint16_t add, uint8_t val);
};
}
#endif

View File

@ -1,117 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __SOUND_H__
#define __SOUND_H__
#include "audio/speaker.hpp"
#include "clock.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Sound
{
public :
Sound ();
void Reset ();
inline Audio::Speaker& GetSpeaker();
void UpdateCntH1 (uint8_t val);
inline void ResetSound1 ();
inline void ResetSound2 ();
inline void ResetSound4 ();
inline void ResetSound1Envelope ();
inline void ResetSound2Envelope ();
inline void ResetSound4Envelope ();
void TimerOverflow (uint8_t timernum);
inline void SendDigitalA (uint8_t* buffer);
inline void SendDigitalB (uint8_t* buffer);
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
Audio::Speaker m_speaker;
uint8_t m_fATimer, m_fBTimer;
inline void TimerOverflowA ();
inline void TimerOverflowB ();
void TimeEvent ()
{
m_speaker.SoundTick();
}
friend void Clock::Commit ();
};
inline Audio::Speaker& Sound::GetSpeaker()
{
return m_speaker;
}
inline void Sound::ResetSound1 ()
{
m_speaker.ResetSound1();
}
inline void Sound::ResetSound2 ()
{
m_speaker.ResetSound2();
}
inline void Sound::ResetSound4 ()
{
m_speaker.ResetSound4();
}
inline void Sound::ResetSound1Envelope ()
{
m_speaker.ResetSound1Envelope();
}
inline void Sound::ResetSound2Envelope ()
{
m_speaker.ResetSound2Envelope();
}
inline void Sound::ResetSound4Envelope ()
{
m_speaker.ResetSound4Envelope();
}
inline void Sound::SendDigitalA (uint8_t* buffer)
{
m_speaker.FillFifoA((int8_t*)buffer);
}
inline void Sound::SendDigitalB (uint8_t* buffer)
{
m_speaker.FillFifoB((int8_t*)buffer);
}
}
#endif

View File

@ -1,56 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __SRAM_H__
#define __SRAM_H__
#include "cartmem.hpp"
#include <stdint.h>
#include <fstream>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Sram : public CartMem
{
public :
Sram ();
void Reset ();
bool Load (std::istream& f);
bool Save (std::ostream& f);
uint8_t Read (uint16_t add)
{
return m_data[add % SIZE];
}
bool Write (uint16_t add, uint8_t val)
{
m_data[add % SIZE] = val;
return true;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
static const uint16_t SIZE = 0x8000;
};
}
#endif

View File

@ -1,36 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __SWAP_H__
#define __SWAP_H__
namespace AMeteor
{
inline uint16_t Swap16(uint16_t val)
{
return (val << 8) | (val >> 8);
}
inline uint32_t Swap32(uint32_t val)
{
return (val << 24) |
((val & 0x0000FF00) << 8) |
((val & 0x00FF0000) >> 8) |
(val >> 24);
}
}
#endif

View File

@ -1,86 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __TIMER_H__
#define __TIMER_H__
#include "clock.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Timer
{
public :
Timer (int8_t num, Timer* next) :
m_num(num),
m_reload(0),
m_count(0),
m_control(0),
m_next(next)
{
}
void Reset ();
void SetReload (uint16_t rel)
{
m_reload = rel;
}
void Reload ();
uint16_t GetCount () const;
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
union Control
{
Control(uint16_t v) :
w(v)
{ }
uint16_t w;
struct
{
unsigned int prescaler : 2;
unsigned int countup : 1;
unsigned int unused1 : 3;
unsigned int irq : 1;
unsigned int start : 1;
unsigned int unused2 : 8;
} b;
};
const int8_t m_num;
uint16_t m_reload;
uint32_t m_count;
Control m_control;
Timer* m_next;
void TimeEvent ();
void Countup ();
friend void Clock::Commit();
};
}
#endif

View File

@ -1,389 +0,0 @@
#ifndef SYG_connection_HPP
#define SYG_connection_HPP
#include <list>
namespace syg
{
//------------------------------
// connections
//------------------------------
template <typename Tret>
class connection_base
{
public:
virtual Tret call() const = 0;
virtual connection_base<Tret>* clone() const = 0;
virtual ~connection_base() {}
};
template <typename Tret, typename Targ0>
class connection_base1
{
public:
virtual Tret call(Targ0) const = 0;
virtual connection_base1<Tret, Targ0>* clone() const = 0;
virtual ~connection_base1() {}
};
template <typename Tret>
class connection_func : public connection_base<Tret>
{
public:
typedef Tret (*FuncPtr)();
connection_func(FuncPtr ptr):
_func(ptr)
{}
Tret call() const
{
return _func();
}
connection_base<Tret>* clone() const
{
return new connection_func<Tret>(*this);
}
private:
FuncPtr _func;
};
template <typename Tret, typename Targ0>
class connection_func1 : public connection_base1<Tret, Targ0>
{
public:
typedef Tret (*FuncPtr)(Targ0);
connection_func1(FuncPtr ptr):
_func(ptr)
{}
Tret call(Targ0 arg0) const
{
return _func(arg0);
}
connection_base1<Tret, Targ0>* clone() const
{
return new connection_func1<Tret, Targ0>(*this);
}
private:
FuncPtr _func;
};
template <typename Tobj, typename Tret>
class connection_meth : public connection_base<Tret>
{
public:
typedef Tobj TypeObj;
typedef Tret (Tobj::*FuncPtr)();
connection_meth(TypeObj& obj, FuncPtr ptr):
_obj(obj),
_meth(ptr)
{}
Tret call() const
{
return (_obj.*_meth)();
}
connection_base<Tret>* clone() const
{
return new connection_meth<Tobj, Tret>(*this);
}
private:
Tobj& _obj;
FuncPtr _meth;
};
template <typename Tobj, typename Tret, typename Targ0>
class connection_meth1 : public connection_base1<Tret, Targ0>
{
public:
typedef Tobj TypeObj;
typedef Tret (Tobj::*FuncPtr)(Targ0);
connection_meth1(TypeObj& obj, FuncPtr ptr):
_obj(obj),
_meth(ptr)
{}
Tret call(Targ0 arg0) const
{
return (_obj.*_meth)(arg0);
}
connection_base1<Tret, Targ0>* clone() const
{
return new connection_meth1<Tobj, Tret, Targ0>(*this);
}
private:
Tobj& _obj;
FuncPtr _meth;
};
//------------------------------
// slots
//------------------------------
template <typename Tret>
class slot;
template <typename Tret, typename Targ0>
class slot1;
template <typename Tret>
slot<Tret> ptr_fun(Tret (*fun)());
template <typename Tobj, typename Tret>
slot<Tret> mem_fun(Tobj& obj, Tret (Tobj::*fun)());
template <typename Tret, typename Targ0>
slot1<Tret, Targ0> ptr_fun(Tret (*fun)(Targ0));
template <typename Tobj, typename Tret, typename Targ0>
slot1<Tret, Targ0> mem_fun(Tobj& obj, Tret (Tobj::*fun)(Targ0));
template <typename Tret>
class slot
{
public:
slot():
_conn(0)
{}
slot(const slot<Tret>& s)
{
*this = s;
}
~slot()
{
delete _conn;
}
slot& operator=(const slot<Tret>& s)
{
if (s._conn)
_conn = s._conn->clone();
else
_conn = 0;
return *this;
}
Tret call() const
{
return _conn->call();
}
Tret operator()() const
{
return call();
}
operator bool() const
{
return _conn;
}
private:
connection_base<Tret>* _conn;
slot(const connection_base<Tret>& conn):
_conn(conn.clone())
{}
friend slot<Tret> ptr_fun<Tret>(Tret (*fun)());
template <typename Tobj2, typename Tret2>
friend slot<Tret2> mem_fun(Tobj2& obj, Tret2 (Tobj2::*fun)());
};
template <typename Tret, typename Targ0>
class slot1
{
public:
typedef slot1<Tret, Targ0> SlotType;
slot1():
_conn(0)
{}
slot1(const SlotType& s)
{
*this = s;
}
~slot1()
{
delete _conn;
}
slot1& operator=(const SlotType& s)
{
if (s._conn)
_conn = s._conn->clone();
else
_conn = 0;
return *this;
}
Tret call(Targ0 arg0) const
{
return _conn->call(arg0);
}
Tret operator()(Targ0 arg0) const
{
return call(arg0);
}
operator bool() const
{
return _conn;
}
private:
typedef connection_base1<Tret, Targ0> Connection;
Connection* _conn;
slot1(const Connection& conn):
_conn(conn.clone())
{}
friend SlotType ptr_fun<Tret, Targ0>(Tret (*fun)(Targ0));
template <typename Tobj2, typename Tret2, typename Targ02>
friend slot1<Tret2, Targ02> mem_fun(Tobj2& obj,
Tret2 (Tobj2::*fun)(Targ02));
};
template <typename Tret>
inline slot<Tret> ptr_fun(Tret (*fun)())
{
return slot<Tret>(connection_func<Tret>(fun));
}
template <typename Tobj, typename Tret>
inline slot<Tret> mem_fun(Tobj& obj, Tret (Tobj::*fun)())
{
return slot<Tret>(connection_meth<Tobj, Tret>(obj, fun));
}
template <typename Tret, typename Targ0>
inline slot1<Tret, Targ0> ptr_fun(Tret (*fun)(Targ0))
{
return slot1<Tret, Targ0>(connection_func1<Tret, Targ0>(fun));
}
template <typename Tobj, typename Tret, typename Targ0>
inline slot1<Tret, Targ0> mem_fun(Tobj& obj, Tret (Tobj::*fun)(Targ0))
{
return slot1<Tret, Targ0>(connection_meth1<Tobj, Tret, Targ0>(obj, fun));
}
//------------------------------
// signals
//------------------------------
/*
template <typename Tret>
class signal;
template <typename Tret>
class connection
{
public:
connection():
_list(0)
{
}
void disconnect()
{
_list->erase(_iter);
}
private:
typedef std::list<slot<Tret> > List;
typedef typename List::iterator Iterator;
List* _list;
Iterator _iter;
connection(List* list, Iterator iter):
_list(list),
_iter(iter)
{}
friend class signal<Tret>;
};
template <typename Tret>
class signal
{
public:
connection<Tret> connect(const slot<Tret> s)
{
_slots.push_back(s);
return connection<Tret>(&_slots, (++_slots.rbegin()).base());
}
Tret emit() const
{
for (typename Slots::const_iterator iter = _slots.begin(),
end = (++_slots.rbegin()).base();
iter != end; ++iter)
iter->call();
return _slots.back().call();
}
Tret operator()() const
{
return emit();
}
private:
typedef std::list<slot<Tret> > Slots;
Slots _slots;
};
template <typename Tret, typename Targ0>
class signal1
{
public:
void connect(const slot<Tret> s)
{
_slots.push_back(s);
}
Tret emit(Targ0 arg0) const
{
for (typename Slots::const_iterator iter = _slots.begin(); iter != _slots.end() - 1; ++iter)
iter->call();
return _slots.back().call(arg0);
}
Tret operator()(Targ0 arg0) const
{
return emit(arg0);
}
private:
typedef std::list<slot<Tret> > Slots;
Slots _slots;
};
*/
} // namespace syg
#endif

View File

@ -1,20 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmeteor", "libmeteor.vcxproj", "{EE01E7C1-6FC4-497F-82CC-C7C64E3BF9D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EE01E7C1-6FC4-497F-82CC-C7C64E3BF9D1}.Debug|Win32.ActiveCfg = Debug|Win32
{EE01E7C1-6FC4-497F-82CC-C7C64E3BF9D1}.Debug|Win32.Build.0 = Debug|Win32
{EE01E7C1-6FC4-497F-82CC-C7C64E3BF9D1}.Release|Win32.ActiveCfg = Release|Win32
{EE01E7C1-6FC4-497F-82CC-C7C64E3BF9D1}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -1,173 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{EE01E7C1-6FC4-497F-82CC-C7C64E3BF9D1}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libmeteor</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(DXSDK_DIR)Include;$(IncludePath);include;include\ameteor</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(DXSDK_DIR)Include;$(IncludePath);include;include\ameteor</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBMETEOR_EXPORTS;%(PreprocessorDefinitions);METDEBUG;METDEBUGLOG;_ITERATOR_DEBUG_LEVEL=0;NO_MEMMEM</PreprocessorDefinitions>
<DisableSpecificWarnings>4396;4800</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\output\dll\$(TargetFileName)</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBMETEOR_EXPORTS;%(PreprocessorDefinitions);NO_MEMMEM</PreprocessorDefinitions>
<DisableSpecificWarnings>4396;4800</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<PostBuildEvent>
<Command>copy /y $(TargetDir)$(TargetFileName) $(ProjectDir)..\output\dll\$(TargetFileName)</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="cinterface.cpp" />
<ClCompile Include="source\ameteor.cpp" />
<ClCompile Include="source\audio\dsound.cpp" />
<ClCompile Include="source\audio\sound1.cpp" />
<ClCompile Include="source\audio\sound2.cpp" />
<ClCompile Include="source\audio\sound4.cpp" />
<ClCompile Include="source\audio\speaker.cpp" />
<ClCompile Include="source\bios.cpp" />
<ClCompile Include="source\cartmem.cpp" />
<ClCompile Include="source\clock.cpp" />
<ClCompile Include="source\cpu.cpp" />
<ClCompile Include="source\debug.cpp" />
<ClCompile Include="source\disassembler\argimmediate.cpp" />
<ClCompile Include="source\disassembler\argmulregisters.cpp" />
<ClCompile Include="source\disassembler\argpsr.cpp" />
<ClCompile Include="source\disassembler\argregister.cpp" />
<ClCompile Include="source\disassembler\argrelative.cpp" />
<ClCompile Include="source\disassembler\argshift.cpp" />
<ClCompile Include="source\disassembler\arguimmediate.cpp" />
<ClCompile Include="source\disassembler\arguments.cpp" />
<ClCompile Include="source\disassembler\instruction.cpp" />
<ClCompile Include="source\dma.cpp" />
<ClCompile Include="source\eeprom.cpp" />
<ClCompile Include="source\flash.cpp" />
<ClCompile Include="source\graphics\bglayer.cpp" />
<ClCompile Include="source\graphics\object.cpp" />
<ClCompile Include="source\graphics\objects.cpp" />
<ClCompile Include="source\graphics\renderer.cpp" />
<ClCompile Include="source\graphics\screen.cpp" />
<ClCompile Include="source\interpreter.cpp" />
<ClCompile Include="source\interpreter_arm.cpp" />
<ClCompile Include="source\interpreter_thumb.cpp" />
<ClCompile Include="source\io.cpp" />
<ClCompile Include="source\keypad.cpp" />
<ClCompile Include="source\lcd.cpp" />
<ClCompile Include="source\memory.cpp" />
<ClCompile Include="source\sound.cpp" />
<ClCompile Include="source\sram.cpp" />
<ClCompile Include="source\timer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\ameteor.hpp" />
<ClInclude Include="include\ameteor\audio\dsound.hpp" />
<ClInclude Include="include\ameteor\audio\sound1.hpp" />
<ClInclude Include="include\ameteor\audio\sound2.hpp" />
<ClInclude Include="include\ameteor\audio\sound4.hpp" />
<ClInclude Include="include\ameteor\audio\speaker.hpp" />
<ClInclude Include="include\ameteor\bios.hpp" />
<ClInclude Include="include\ameteor\cartmem.hpp" />
<ClInclude Include="include\ameteor\clock.hpp" />
<ClInclude Include="include\ameteor\cpu.hpp" />
<ClInclude Include="include\ameteor\disassembler\argimmediate.hpp" />
<ClInclude Include="include\ameteor\disassembler\argmulregisters.hpp" />
<ClInclude Include="include\ameteor\disassembler\argpsr.hpp" />
<ClInclude Include="include\ameteor\disassembler\argregister.hpp" />
<ClInclude Include="include\ameteor\disassembler\argrelative.hpp" />
<ClInclude Include="include\ameteor\disassembler\argshift.hpp" />
<ClInclude Include="include\ameteor\disassembler\arguimmediate.hpp" />
<ClInclude Include="include\ameteor\disassembler\argument.hpp" />
<ClInclude Include="include\ameteor\disassembler\arguments.hpp" />
<ClInclude Include="include\ameteor\disassembler\instruction.hpp" />
<ClInclude Include="include\ameteor\dma.hpp" />
<ClInclude Include="include\ameteor\eeprom.hpp" />
<ClInclude Include="include\ameteor\flash.hpp" />
<ClInclude Include="include\ameteor\graphics\bglayer.hpp" />
<ClInclude Include="include\ameteor\graphics\object.hpp" />
<ClInclude Include="include\ameteor\graphics\objects.hpp" />
<ClInclude Include="include\ameteor\graphics\renderer.hpp" />
<ClInclude Include="include\ameteor\graphics\screen.hpp" />
<ClInclude Include="include\ameteor\interpreter.hpp" />
<ClInclude Include="include\ameteor\io.hpp" />
<ClInclude Include="include\ameteor\keypad.hpp" />
<ClInclude Include="include\ameteor\lcd.hpp" />
<ClInclude Include="include\ameteor\memory.hpp" />
<ClInclude Include="include\ameteor\sound.hpp" />
<ClInclude Include="include\ameteor\sram.hpp" />
<ClInclude Include="include\ameteor\swap.hpp" />
<ClInclude Include="include\ameteor\timer.hpp" />
<ClInclude Include="include\syg\signal.hpp" />
<ClInclude Include="source\cpu_globals.hpp" />
<ClInclude Include="source\debug.hpp" />
<ClInclude Include="source\globals.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -1,288 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Source Files\audio">
<UniqueIdentifier>{93df073d-6997-45f7-b85f-a285bca7b310}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\graphics">
<UniqueIdentifier>{f134ab18-d00a-4652-ad25-5179aac22dbc}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\disassembler">
<UniqueIdentifier>{b152e4ad-8658-48a8-a214-3d9d1282fc70}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\syg">
<UniqueIdentifier>{63c54b4d-45ec-46f4-923e-38f486e19909}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\ameteor">
<UniqueIdentifier>{e3ba08fe-cf72-4aed-857c-697b3e437472}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\ameteor\audio">
<UniqueIdentifier>{486541a1-445b-4012-8c31-218e65ea1e0b}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\ameteor\graphics">
<UniqueIdentifier>{66d5a4ba-681b-448c-8912-ecbb110d4896}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\ameteor\disassembler">
<UniqueIdentifier>{a989399f-ba01-4804-ae68-ba5f363a2f9f}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\cinterface">
<UniqueIdentifier>{05542396-9b85-441b-beb8-8c49f126a881}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\ameteor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\bios.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\cartmem.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\clock.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\cpu.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\debug.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\dma.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\eeprom.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\flash.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\interpreter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\interpreter_arm.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\interpreter_thumb.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\io.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\keypad.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\lcd.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\memory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\sound.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\sram.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\timer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\audio\dsound.cpp">
<Filter>Source Files\audio</Filter>
</ClCompile>
<ClCompile Include="source\audio\sound1.cpp">
<Filter>Source Files\audio</Filter>
</ClCompile>
<ClCompile Include="source\audio\sound2.cpp">
<Filter>Source Files\audio</Filter>
</ClCompile>
<ClCompile Include="source\audio\sound4.cpp">
<Filter>Source Files\audio</Filter>
</ClCompile>
<ClCompile Include="source\audio\speaker.cpp">
<Filter>Source Files\audio</Filter>
</ClCompile>
<ClCompile Include="source\graphics\bglayer.cpp">
<Filter>Source Files\graphics</Filter>
</ClCompile>
<ClCompile Include="source\graphics\object.cpp">
<Filter>Source Files\graphics</Filter>
</ClCompile>
<ClCompile Include="source\graphics\objects.cpp">
<Filter>Source Files\graphics</Filter>
</ClCompile>
<ClCompile Include="source\graphics\renderer.cpp">
<Filter>Source Files\graphics</Filter>
</ClCompile>
<ClCompile Include="source\graphics\screen.cpp">
<Filter>Source Files\graphics</Filter>
</ClCompile>
<ClCompile Include="source\disassembler\argimmediate.cpp">
<Filter>Source Files\disassembler</Filter>
</ClCompile>
<ClCompile Include="source\disassembler\argmulregisters.cpp">
<Filter>Source Files\disassembler</Filter>
</ClCompile>
<ClCompile Include="source\disassembler\argpsr.cpp">
<Filter>Source Files\disassembler</Filter>
</ClCompile>
<ClCompile Include="source\disassembler\argregister.cpp">
<Filter>Source Files\disassembler</Filter>
</ClCompile>
<ClCompile Include="source\disassembler\argrelative.cpp">
<Filter>Source Files\disassembler</Filter>
</ClCompile>
<ClCompile Include="source\disassembler\argshift.cpp">
<Filter>Source Files\disassembler</Filter>
</ClCompile>
<ClCompile Include="source\disassembler\arguimmediate.cpp">
<Filter>Source Files\disassembler</Filter>
</ClCompile>
<ClCompile Include="source\disassembler\arguments.cpp">
<Filter>Source Files\disassembler</Filter>
</ClCompile>
<ClCompile Include="source\disassembler\instruction.cpp">
<Filter>Source Files\disassembler</Filter>
</ClCompile>
<ClCompile Include="cinterface.cpp">
<Filter>Source Files\cinterface</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\ameteor.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\syg\signal.hpp">
<Filter>Header Files\syg</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\bios.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\cartmem.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\clock.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\cpu.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\dma.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\eeprom.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\flash.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\interpreter.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\io.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\keypad.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\lcd.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\memory.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\sound.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\sram.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\swap.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\timer.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\audio\dsound.hpp">
<Filter>Header Files\ameteor\audio</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\audio\sound1.hpp">
<Filter>Header Files\ameteor\audio</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\audio\sound2.hpp">
<Filter>Header Files\ameteor\audio</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\audio\sound4.hpp">
<Filter>Header Files\ameteor\audio</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\audio\speaker.hpp">
<Filter>Header Files\ameteor\audio</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\graphics\bglayer.hpp">
<Filter>Header Files\ameteor\graphics</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\graphics\object.hpp">
<Filter>Header Files\ameteor\graphics</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\graphics\objects.hpp">
<Filter>Header Files\ameteor\graphics</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\graphics\renderer.hpp">
<Filter>Header Files\ameteor\graphics</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\graphics\screen.hpp">
<Filter>Header Files\ameteor\graphics</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\argimmediate.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\argmulregisters.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\argpsr.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\argregister.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\argrelative.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\argshift.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\arguimmediate.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\argument.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\arguments.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="include\ameteor\disassembler\instruction.hpp">
<Filter>Header Files\ameteor\disassembler</Filter>
</ClInclude>
<ClInclude Include="source\cpu_globals.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="source\debug.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
<ClInclude Include="source\globals.hpp">
<Filter>Header Files\ameteor</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -1,204 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor.hpp"
#include "debug.hpp"
#include "globals.hpp"
#include <cstring>
#include <sstream>
// TODO add version
#define SS_MAGIC_STRING ("AMeteor SaveState")
#define SS_MS_SIZE (sizeof(SS_MAGIC_STRING)-1)
namespace AMeteor
{
namespace
{
class AMeteor
{
public :
AMeteor ()
{
Audio::InitNoise();
}
} __ameteor;
}
// the clock must be initialized first since there are devices like
// lcd which needs to set the timer
Clock _clock;
Io _io;
// the interpreter (which is in the cpu) takes io addresses, thus the
// cpu must be initialized after io
Interpreter _cpu;
Memory _memory;
Dma _dma;
// the lcd must be initialized after the memory since it takes
// pointers from it
Lcd _lcd;
// the sound must be initialized after the io since it takes references
// from it
Sound _sound;
// the keypad needs to take the vblank event from lcd, so it must be
// initialized after lcd
// it must also be initialized after io since it takes the keyinput
// reference
Keypad _keypad;
Timer _timer3(3, NULL);
Timer _timer2(2, &_timer3);
Timer _timer1(1, &_timer2);
Timer _timer0(0, &_timer1);
void Reset (uint32_t units)
{
#define RESET(u, e) \
if (units & UNIT_##e) \
_##u.Reset();
RESET(clock, CLOCK);
RESET(io, IO);
RESET(cpu, CPU);
RESET(dma, DMA);
RESET(lcd, LCD);
RESET(sound, SOUND);
RESET(keypad, KEYPAD);
RESET(timer0, TIMER0);
RESET(timer1, TIMER1);
RESET(timer2, TIMER2);
RESET(timer3, TIMER3);
#undef RESET
if (units & UNIT_MEMORY)
_memory.Reset(units);
}
bool SaveState (const char* filename)
{
if (_cpu.IsRunning())
return false;
std::ostringstream ss;
if (!SaveState(ss))
return false;
std::ofstream file(filename);
if (!file)
return false;
std::string buf = ss.str();
if (!file.write(buf.c_str(), buf.length()))
return false;
file.close();
if (file.bad())
return false;
return true;
}
bool LoadState (const char* filename)
{
if (_cpu.IsRunning())
return false;
std::istringstream ss;
{
std::ifstream file(filename);
if (!file)
return false;
// 1Mo
std::vector<uint8_t> buf(0x100000);
if (file.read((char*)&buf[0], 0x100000).bad())
return false;
int nread = file.gcount();
file.close();
if (file.bad())
return false;
ss.str(std::string((char*)&buf[0], nread));
}
return LoadState(ss);
}
bool SaveState (std::ostream& stream)
{
if (_cpu.IsRunning())
return false;
SS_WRITE_DATA(SS_MAGIC_STRING, SS_MS_SIZE);
#define SAVE(dev) \
if (!dev.SaveState(stream)) \
return false
SAVE(_clock);
SAVE(_io);
SAVE(_cpu);
SAVE(_memory);
SAVE(_dma);
SAVE(_lcd);
SAVE(_sound);
//SAVE(_keypad);
SAVE(_timer0);
SAVE(_timer1);
SAVE(_timer2);
SAVE(_timer3);
#undef SAVE
return true;
}
bool LoadState (std::istream& stream)
{
if (_cpu.IsRunning())
return false;
{
char buf[SS_MS_SIZE];
SS_READ_DATA(buf, SS_MS_SIZE);
if (std::memcmp(buf, SS_MAGIC_STRING, SS_MS_SIZE))
return false;
}
#define LOAD(dev) \
if (!dev.LoadState(stream)) \
return false
LOAD(_clock);
LOAD(_io);
LOAD(_cpu);
LOAD(_memory);
LOAD(_dma);
LOAD(_lcd);
LOAD(_sound);
//LOAD(_keypad);
LOAD(_timer0);
LOAD(_timer1);
LOAD(_timer2);
LOAD(_timer3);
#undef LOAD
uint8_t xxx;
// if there is garbage at end of file
if (stream.read((char*)&xxx, 1))
return false;
return true;
}
}

View File

@ -1,82 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/audio/dsound.hpp"
#include "../globals.hpp"
#include <cstring>
namespace AMeteor
{
namespace Audio
{
DSound::DSound () :
m_rpos(0),
m_wpos(0),
m_size(0)
{
std::memset(m_buffer, 0, sizeof(m_buffer));
}
void DSound::FillFifo (int8_t* buffer)
{
int8_t* pmbuf = m_buffer + m_wpos;
// we copy 16 bytes of data
for (int8_t* pbuf = buffer;
pbuf < buffer + BUFFER_SIZE/2 && m_size < 32; ++pbuf, ++pmbuf)
{
if (pmbuf >= m_buffer + BUFFER_SIZE)
pmbuf = m_buffer;
*pmbuf = *pbuf;
++m_size;
}
m_wpos = pmbuf - m_buffer;
}
void DSound::FillFifo (int8_t sample)
{
if (m_size == 32)
return;
if (m_wpos == BUFFER_SIZE)
m_wpos = 0;
m_buffer[m_wpos++] = sample;
++m_size;
}
bool DSound::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_rpos);
SS_WRITE_VAR(m_wpos);
SS_WRITE_VAR(m_size);
SS_WRITE_ARRAY(m_buffer);
return true;
}
bool DSound::LoadState (std::istream& stream)
{
SS_READ_VAR(m_rpos);
SS_READ_VAR(m_wpos);
SS_READ_VAR(m_size);
SS_READ_ARRAY(m_buffer);
return true;
}
}
}

View File

@ -1,202 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/audio/sound1.hpp"
#include "../globals.hpp"
#include <cmath>
namespace AMeteor
{
namespace Audio
{
Sound1::Sound1 (uint16_t& cntl, uint16_t& cnth, uint16_t& cntx,
uint16_t freq) :
m_cntl(cntl),
m_cnth(cnth),
m_cntx(cntx),
m_on(false),
m_posP(0),
m_posS(0),
m_posE(0),
m_sample(0),
m_speriod(16*1024*1024/freq),
m_envelope(0),
m_length(0),
m_timed(false)
{
}
void Sound1::Reset ()
{
m_on = false;
m_timed = false;
m_length = 0;
m_envelope = 0;
m_posP = m_posE = m_posS = 0;
m_sample = 0;
}
void Sound1::ResetSound ()
{
m_on = true;
m_timed = (m_cntx & (0x1 << 14));
m_length = (64 - (m_cnth & 0x3F)) * ((16*1024*1024)/256);
m_envelope = m_cnth >> 12;
m_posE = m_posS = 0;
}
void Sound1::SoundTick ()
{
// remember here that the processors runs at 16MHz = 16,777,216 cycles/s
// and this function is called normally at 44,100 Hz
m_posP += m_speriod;
m_posS += m_speriod;
m_posE += m_speriod;
if (m_length > m_speriod)
m_length -= m_speriod;
else
{
if (m_timed)
m_on = false;
m_length = 0;
}
// sweep time in cycles
// maximum is 917,504, so we need a 32 bits int
uint32_t sweeptime = ((m_cntl >> 4) & 0x7) * ((16*1024*1024)/128);
// period in cycles
// period = 16M/freq
// freq = 128K/(2048 - (SOUND1CNT_X & 0x7FF))
// maximum is 262,144, so we need a 32 bits int
uint32_t period =
((16*1024*1024) / (128*1024)) * (2048 - (m_cntx & 0x7FF));
// frequency as contained in SOUND1CNT_X
uint16_t freq = m_cntx & 0x7FF;
// we rewind posP
m_posP %= period;
// the envelope now
// envelope step time in cycles
uint32_t steptime = ((m_cnth >> 8) & 0x7) * ((16*1024*1024)/64);
// the envelope can't do two steps between to calls of SoundTick
if (steptime && m_posE > steptime)
{
if (m_cnth & (0x1 << 11))
{
if (m_envelope < 15)
++m_envelope;
}
else
{
if (m_envelope > 0)
--m_envelope;
}
m_posE -= steptime;
}
// if the envelope is null or the sound is finished, no need to calculate
// anything
if (m_on && m_envelope)
{
// we set the sample according to the position in the current period
// and the wave duty cycle
switch ((m_cnth >> 6) & 0x3)
{
case 0: // 12.5%
m_sample = m_posP < period/8 ? 112 : -16;
break;
case 1: // 25%
m_sample = m_posP < period/4 ? 96 : -32;
break;
case 2: // 50%
m_sample = m_posP < period/2 ? 64 : -64;
break;
case 3: // 75%
m_sample = m_posP < (3*period)/4 ? 32 : -96;
break;
}
m_sample = (((int16_t)m_sample) * m_envelope)/15;
}
else
m_sample = 0;
// there can't have been more than one sweep between two call of
// SoundTick since SoundTick is called at least at a frequency of 4,000Hz
// (alsa can't output at a lower samplerate on my sound card) and sweeps
// happen at maximum at a frequency of 128Hz
// if the channel is on and sweep is enabled and it's time to sweep
if (m_on && sweeptime && m_posS > sweeptime)
{
// n = sweep shifts (in SOUND1CNT_L)
if (m_cntl & (0x1 << 3))
// F(t+1) = F(t) - F(t) / 2^n
freq = freq - freq / (1 << (m_cntl & 0x7));
// freq won't go under 1 since when freq = 2, freq - freq / 2 (the
// minimum sweep shift) = 1 and then freq - freq / 2 = 1
// because 1/2 = 0
else
{
// F(t+1) = F(t) + F(t) / 2^n
freq = freq + freq / (1 << (m_cntl & 0x7));
if (freq > 2047)
{
m_on = false;
freq = 2047;
}
}
// we update the frequency in the cntx register
m_cntx = (m_cntx & 0xF800) | freq;
// now we rewind posS
m_posS -= sweeptime;
}
}
bool Sound1::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_on);
SS_WRITE_VAR(m_posP);
SS_WRITE_VAR(m_posS);
SS_WRITE_VAR(m_posE);
SS_WRITE_VAR(m_sample);
SS_WRITE_VAR(m_envelope);
SS_WRITE_VAR(m_length);
SS_WRITE_VAR(m_timed);
return true;
}
bool Sound1::LoadState (std::istream& stream)
{
SS_READ_VAR(m_on);
SS_READ_VAR(m_posP);
SS_READ_VAR(m_posS);
SS_READ_VAR(m_posE);
SS_READ_VAR(m_sample);
SS_READ_VAR(m_envelope);
SS_READ_VAR(m_length);
SS_READ_VAR(m_timed);
return true;
}
}
}

View File

@ -1,145 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/audio/sound2.hpp"
#include "../globals.hpp"
#include <cmath>
namespace AMeteor
{
namespace Audio
{
Sound2::Sound2 (uint16_t& cntl, uint16_t& cnth, uint16_t freq) :
m_cntl(cntl),
m_cnth(cnth),
m_on(false),
m_posP(0),
m_posE(0),
m_sample(0),
m_speriod(16*1024*1024/freq),
m_envelope(0),
m_length(0),
m_timed(false)
{
}
void Sound2::Reset ()
{
m_on = false;
m_timed = false;
m_length = 0;
m_envelope = 0;
m_posP = m_posE = 0;
m_sample = 0;
}
void Sound2::ResetSound ()
{
m_on = true;
m_timed = (m_cnth & (0x1 << 14));
m_length = (64 - (m_cntl & 0x3F)) * ((16*1024*1024)/256);
m_envelope = m_cntl >> 12;
m_posE = 0;
}
void Sound2::SoundTick ()
{
// refer at sound1 to know how sound2 works
m_posP += m_speriod;
m_posE += m_speriod;
if (m_length > m_speriod)
m_length -= m_speriod;
else
{
if (m_timed)
m_on = false;
m_length = 0;
}
uint32_t period =
((16*1024*1024) / (128*1024)) * (2048 - (m_cnth & 0x7FF));
m_posP %= period;
uint32_t steptime = ((m_cntl >> 8) & 0x7) * ((16*1024*1024)/64);
if (steptime && m_posE > steptime)
{
if (m_cntl & (0x1 << 11))
{
if (m_envelope < 15)
++m_envelope;
}
else
{
if (m_envelope > 0)
--m_envelope;
}
m_posE -= steptime;
}
if (m_on && m_envelope)
{
switch ((m_cntl >> 6) & 0x3)
{
case 0: // 12.5%
m_sample = m_posP < period/8 ? 112 : -16;
break;
case 1: // 25%
m_sample = m_posP < period/4 ? 96 : -32;
break;
case 2: // 50%
m_sample = m_posP < period/2 ? 64 : -64;
break;
case 3: // 75%
m_sample = m_posP < (3*period)/4 ? 32 : -96;
break;
}
m_sample = (((int16_t)m_sample) * m_envelope)/15;
}
else
m_sample = 0;
}
bool Sound2::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_on);
SS_WRITE_VAR(m_posP);
SS_WRITE_VAR(m_posE);
SS_WRITE_VAR(m_sample);
SS_WRITE_VAR(m_envelope);
SS_WRITE_VAR(m_length);
SS_WRITE_VAR(m_timed);
return true;
}
bool Sound2::LoadState (std::istream& stream)
{
SS_READ_VAR(m_on);
SS_READ_VAR(m_posP);
SS_READ_VAR(m_posE);
SS_READ_VAR(m_sample);
SS_READ_VAR(m_envelope);
SS_READ_VAR(m_length);
SS_READ_VAR(m_timed);
return true;
}
}
}

View File

@ -1,185 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/audio/sound4.hpp"
#include "../globals.hpp"
namespace AMeteor
{
namespace Audio
{
static bool Noise7Stages[127];
static bool Noise15Stages[32767];
void InitNoise ()
{
unsigned short i = 0x7f;
bool* pNoise = Noise7Stages;
do
{
*pNoise++ = i & 1;
i = (i >> 1) | (((i & 1) << 6) ^ ((i & 2) << 5));
} while (i != 0x7f);
i = 0x7fff;
pNoise = Noise15Stages;
do
{
*pNoise++ = i & 1;
i = (i >> 1) | (((i & 1) << 14) ^ ((i & 2) << 13));
} while (i != 0x7fff);
}
Sound4::Sound4 (uint16_t& cntl, uint16_t& cnth, uint16_t freq) :
m_cntl(cntl),
m_cnth(cnth),
m_on(false),
m_posP(0),
m_posN(0),
m_posE(0),
m_sample(0),
m_speriod(16*1024*1024/freq),
m_envelope(0),
m_length(0),
m_timed(false),
m_div(4*8/2)
{
}
void Sound4::Reset ()
{
m_on = false;
m_timed = false;
m_length = 0;
m_envelope = 0;
m_posP = m_posE = m_posN = 0;
m_sample = 0;
m_div = 4*8/2;
}
void Sound4::ResetSound ()
{
m_on = true;
m_timed = (m_cnth & (0x1 << 14));
m_length = (64 - (m_cntl & 0x3F)) * ((16*1024*1024)/256);
m_envelope = m_cntl >> 12;
m_div = ((m_cnth & 0x7) ? 4*8*(m_cnth & 0x7) : 4*8/2);
m_posE = m_posP = 0;
}
void Sound4::SoundTick ()
{
// rest is the number of processor clock ticks that were not yet taken by
// the noise clock divider (if the total divider is 8 and we have 10
// ticks of the processor clock, only 8 were taken by the noise clock),
// the rest will be taken by the next call of SoundTick()
uint16_t rest = m_posP + m_speriod;
// advance is the number of noise ticks that have passed
uint16_t advance = m_posP + m_speriod;
// time of one sound tick in cycles
uint32_t tick = m_div;
// if shift is 111X in binary
if (((m_cnth >> 5) & 0x7) == 0x7)
// not used
// assume 13
tick *= 1 << 14;
else
tick *= (2 << ((m_cnth >> 4) & 0xF));
rest %= tick;
advance /= tick;
m_posP = rest;
m_posN += advance;
// we have this modulo on posN so that when you switch from 15 stages to
// 7 stages and then you switch back, you won't restart the 15 stages
// pattern from the beginning
// don't know if GBA handle this like that
m_posN %= 32768;
m_posE += m_speriod;
if (m_length > m_speriod)
m_length -= m_speriod;
else
{
if (m_timed)
m_on = false;
m_length = 0;
}
uint32_t steptime = ((m_cntl >> 8) & 0x7) * ((16*1024*1024)/64);
if (steptime && m_posE > steptime)
{
if (m_cnth & (0x1 << 11))
{
if (m_envelope < 15)
++m_envelope;
}
else
{
if (m_envelope > 0)
--m_envelope;
}
m_posE -= steptime;
}
if (m_on && m_envelope)
{
if (m_cnth & (0x1 << 3))
m_sample = Noise7Stages[m_posN % 128];
else
m_sample = Noise15Stages[m_posN];
m_sample = m_sample ? 127 : -127;
m_sample = (((int16_t)m_sample) * m_envelope)/15;
}
else
m_sample = 0;
}
bool Sound4::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_on);
SS_WRITE_VAR(m_posP);
SS_WRITE_VAR(m_posE);
SS_WRITE_VAR(m_posN);
SS_WRITE_VAR(m_sample);
SS_WRITE_VAR(m_envelope);
SS_WRITE_VAR(m_length);
SS_WRITE_VAR(m_timed);
SS_WRITE_VAR(m_div);
return true;
}
bool Sound4::LoadState (std::istream& stream)
{
SS_READ_VAR(m_on);
SS_READ_VAR(m_posP);
SS_READ_VAR(m_posE);
SS_READ_VAR(m_posN);
SS_READ_VAR(m_sample);
SS_READ_VAR(m_envelope);
SS_READ_VAR(m_length);
SS_READ_VAR(m_timed);
SS_READ_VAR(m_div);
return true;
}
}
}

View File

@ -1,169 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/audio/speaker.hpp"
#include "../debug.hpp"
namespace AMeteor
{
namespace Audio
{
Speaker::Speaker (uint16_t& cnt1l, uint16_t& cnt1h, uint16_t& cnt1x,
uint16_t& cnt2l, uint16_t& cnt2h,
uint16_t& cnt4l, uint16_t& cnt4h,
uint16_t& cntl, uint16_t& cnth, uint16_t& cntx, uint16_t& bias) :
// XXX freq
m_sound1(cnt1l, cnt1h, cnt1x, 44100),
m_sound2(cnt2l, cnt2h, 44100),
m_sound4(cnt4l, cnt4h, 44100),
m_cntl(cntl),
m_cnth(cnth),
m_cntx(cntx),
m_bias(bias)
{
}
Speaker::~Speaker ()
{
}
void Speaker::Reset ()
{
m_sound1.Reset();
m_sound2.Reset();
m_sound4.Reset();
m_dsa.Reset();
m_dsb.Reset();
}
void Speaker::SoundTick ()
{
int16_t f[2];
// if master is enabled
if (m_cntx & (0x1 << 7))
{
m_sound1.SoundTick();
if (m_sound1.IsOn())
m_cntx |= 0x0001;
else
m_cntx &= 0xFFFE;
m_sound2.SoundTick();
if (m_sound2.IsOn())
m_cntx |= 0x0002;
else
m_cntx &= 0xFFFD;
m_sound4.SoundTick();
if (m_sound4.IsOn())
m_cntx |= 0x0008;
else
m_cntx &= 0xFFF7;
}
// left
f[0] = MixSample (m_cntl >> 4, m_cnth >> 9);
// right
f[1] = MixSample (m_cntl, m_cnth >> 8);
m_sig_frame(f);
}
int16_t Speaker::MixSample (uint16_t cntl, uint8_t cnth)
{
int16_t sample;
// if master is enabled
if (m_cntx & (0x1 << 7))
{
int8_t s1, s2, s4;
s1 = (cntl & (0x1 << 8)) ? m_sound1.GetSample() : 0;
s2 = (cntl & (0x1 << 9)) ? m_sound2.GetSample() : 0;
s4 = (cntl & (0x1 << 11)) ? m_sound4.GetSample() : 0;
int16_t dmg = s1 + s2 + s4;
dmg = (dmg * (cntl & 0x7)) / 7;
switch (m_cnth & 0x3)
{
case 0: // 25%
dmg /= 4;
break;
case 1: // 50%
dmg /= 2;
break;
case 2: // 100%
break;
case 3: // Prohibited
met_abort("Invalid SOUNDCNT_H sound # 1-4 volume");
break;
}
int16_t sA, sB;
sA = (cnth & (0x1 )) ? m_dsa.GetSample() : 0;
sB = (cnth & (0x1 << 4)) ? m_dsb.GetSample() : 0;
if (!(m_cnth & (0x1 << 2)))
sA /= 2;
if (!(m_cnth & (0x1 << 3)))
sB /= 2;
// TODO when finished put this all together on one line
sample = (sA + sB) * 4 + dmg;
}
else
sample = 0;
sample += m_bias & 0x3FF;
if (sample < 0)
sample = 0;
else if (sample >= 0x400)
sample = 0x3FF;
sample -= 0x200;
sample <<= 6;
return sample;
}
bool Speaker::SaveState (std::ostream& stream)
{
#define WRITE(var) \
if (!var.SaveState(stream)) \
return false
WRITE(m_sound1);
WRITE(m_sound2);
WRITE(m_sound4);
WRITE(m_dsa);
WRITE(m_dsb);
#undef WRITE
return true;
}
bool Speaker::LoadState (std::istream& stream)
{
#define READ(var) \
if (!var.LoadState(stream)) \
return false
READ(m_sound1);
READ(m_sound2);
READ(m_sound4);
READ(m_dsa);
READ(m_dsb);
#undef READ
return true;
}
}
}

View File

@ -1,919 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/bios.hpp"
#include "ameteor/cpu.hpp"
#include "ameteor/memory.hpp"
#include "globals.hpp"
#include "debug.hpp"
#include <cmath>
namespace AMeteor
{
namespace Bios
{
static const int16_t sineTable[256] = {
(int16_t)0x0000, (int16_t)0x0192, (int16_t)0x0323, (int16_t)0x04B5,
(int16_t)0x0645, (int16_t)0x07D5, (int16_t)0x0964, (int16_t)0x0AF1,
(int16_t)0x0C7C, (int16_t)0x0E05, (int16_t)0x0F8C, (int16_t)0x1111,
(int16_t)0x1294, (int16_t)0x1413, (int16_t)0x158F, (int16_t)0x1708,
(int16_t)0x187D, (int16_t)0x19EF, (int16_t)0x1B5D, (int16_t)0x1CC6,
(int16_t)0x1E2B, (int16_t)0x1F8B, (int16_t)0x20E7, (int16_t)0x223D,
(int16_t)0x238E, (int16_t)0x24DA, (int16_t)0x261F, (int16_t)0x275F,
(int16_t)0x2899, (int16_t)0x29CD, (int16_t)0x2AFA, (int16_t)0x2C21,
(int16_t)0x2D41, (int16_t)0x2E5A, (int16_t)0x2F6B, (int16_t)0x3076,
(int16_t)0x3179, (int16_t)0x3274, (int16_t)0x3367, (int16_t)0x3453,
(int16_t)0x3536, (int16_t)0x3612, (int16_t)0x36E5, (int16_t)0x37AF,
(int16_t)0x3871, (int16_t)0x392A, (int16_t)0x39DA, (int16_t)0x3A82,
(int16_t)0x3B20, (int16_t)0x3BB6, (int16_t)0x3C42, (int16_t)0x3CC5,
(int16_t)0x3D3E, (int16_t)0x3DAE, (int16_t)0x3E14, (int16_t)0x3E71,
(int16_t)0x3EC5, (int16_t)0x3F0E, (int16_t)0x3F4E, (int16_t)0x3F84,
(int16_t)0x3FB1, (int16_t)0x3FD3, (int16_t)0x3FEC, (int16_t)0x3FFB,
(int16_t)0x4000, (int16_t)0x3FFB, (int16_t)0x3FEC, (int16_t)0x3FD3,
(int16_t)0x3FB1, (int16_t)0x3F84, (int16_t)0x3F4E, (int16_t)0x3F0E,
(int16_t)0x3EC5, (int16_t)0x3E71, (int16_t)0x3E14, (int16_t)0x3DAE,
(int16_t)0x3D3E, (int16_t)0x3CC5, (int16_t)0x3C42, (int16_t)0x3BB6,
(int16_t)0x3B20, (int16_t)0x3A82, (int16_t)0x39DA, (int16_t)0x392A,
(int16_t)0x3871, (int16_t)0x37AF, (int16_t)0x36E5, (int16_t)0x3612,
(int16_t)0x3536, (int16_t)0x3453, (int16_t)0x3367, (int16_t)0x3274,
(int16_t)0x3179, (int16_t)0x3076, (int16_t)0x2F6B, (int16_t)0x2E5A,
(int16_t)0x2D41, (int16_t)0x2C21, (int16_t)0x2AFA, (int16_t)0x29CD,
(int16_t)0x2899, (int16_t)0x275F, (int16_t)0x261F, (int16_t)0x24DA,
(int16_t)0x238E, (int16_t)0x223D, (int16_t)0x20E7, (int16_t)0x1F8B,
(int16_t)0x1E2B, (int16_t)0x1CC6, (int16_t)0x1B5D, (int16_t)0x19EF,
(int16_t)0x187D, (int16_t)0x1708, (int16_t)0x158F, (int16_t)0x1413,
(int16_t)0x1294, (int16_t)0x1111, (int16_t)0x0F8C, (int16_t)0x0E05,
(int16_t)0x0C7C, (int16_t)0x0AF1, (int16_t)0x0964, (int16_t)0x07D5,
(int16_t)0x0645, (int16_t)0x04B5, (int16_t)0x0323, (int16_t)0x0192,
(int16_t)0x0000, (int16_t)0xFE6E, (int16_t)0xFCDD, (int16_t)0xFB4B,
(int16_t)0xF9BB, (int16_t)0xF82B, (int16_t)0xF69C, (int16_t)0xF50F,
(int16_t)0xF384, (int16_t)0xF1FB, (int16_t)0xF074, (int16_t)0xEEEF,
(int16_t)0xED6C, (int16_t)0xEBED, (int16_t)0xEA71, (int16_t)0xE8F8,
(int16_t)0xE783, (int16_t)0xE611, (int16_t)0xE4A3, (int16_t)0xE33A,
(int16_t)0xE1D5, (int16_t)0xE075, (int16_t)0xDF19, (int16_t)0xDDC3,
(int16_t)0xDC72, (int16_t)0xDB26, (int16_t)0xD9E1, (int16_t)0xD8A1,
(int16_t)0xD767, (int16_t)0xD633, (int16_t)0xD506, (int16_t)0xD3DF,
(int16_t)0xD2BF, (int16_t)0xD1A6, (int16_t)0xD095, (int16_t)0xCF8A,
(int16_t)0xCE87, (int16_t)0xCD8C, (int16_t)0xCC99, (int16_t)0xCBAD,
(int16_t)0xCACA, (int16_t)0xC9EE, (int16_t)0xC91B, (int16_t)0xC851,
(int16_t)0xC78F, (int16_t)0xC6D6, (int16_t)0xC626, (int16_t)0xC57E,
(int16_t)0xC4E0, (int16_t)0xC44A, (int16_t)0xC3BE, (int16_t)0xC33B,
(int16_t)0xC2C2, (int16_t)0xC252, (int16_t)0xC1EC, (int16_t)0xC18F,
(int16_t)0xC13B, (int16_t)0xC0F2, (int16_t)0xC0B2, (int16_t)0xC07C,
(int16_t)0xC04F, (int16_t)0xC02D, (int16_t)0xC014, (int16_t)0xC005,
(int16_t)0xC000, (int16_t)0xC005, (int16_t)0xC014, (int16_t)0xC02D,
(int16_t)0xC04F, (int16_t)0xC07C, (int16_t)0xC0B2, (int16_t)0xC0F2,
(int16_t)0xC13B, (int16_t)0xC18F, (int16_t)0xC1EC, (int16_t)0xC252,
(int16_t)0xC2C2, (int16_t)0xC33B, (int16_t)0xC3BE, (int16_t)0xC44A,
(int16_t)0xC4E0, (int16_t)0xC57E, (int16_t)0xC626, (int16_t)0xC6D6,
(int16_t)0xC78F, (int16_t)0xC851, (int16_t)0xC91B, (int16_t)0xC9EE,
(int16_t)0xCACA, (int16_t)0xCBAD, (int16_t)0xCC99, (int16_t)0xCD8C,
(int16_t)0xCE87, (int16_t)0xCF8A, (int16_t)0xD095, (int16_t)0xD1A6,
(int16_t)0xD2BF, (int16_t)0xD3DF, (int16_t)0xD506, (int16_t)0xD633,
(int16_t)0xD767, (int16_t)0xD8A1, (int16_t)0xD9E1, (int16_t)0xDB26,
(int16_t)0xDC72, (int16_t)0xDDC3, (int16_t)0xDF19, (int16_t)0xE075,
(int16_t)0xE1D5, (int16_t)0xE33A, (int16_t)0xE4A3, (int16_t)0xE611,
(int16_t)0xE783, (int16_t)0xE8F8, (int16_t)0xEA71, (int16_t)0xEBED,
(int16_t)0xED6C, (int16_t)0xEEEF, (int16_t)0xF074, (int16_t)0xF1FB,
(int16_t)0xF384, (int16_t)0xF50F, (int16_t)0xF69C, (int16_t)0xF82B,
(int16_t)0xF9BB, (int16_t)0xFB4B, (int16_t)0xFCDD, (int16_t)0xFE6E
};
void Bios000h ()
{
debug("Bios entry point");
R(13) = 0x03007FE0;
R(15) = 0x08000004;
CPU.SwitchToMode(Cpu::M_IRQ);
R(13) = 0x03007FA0;
CPU.SwitchToMode(Cpu::M_SYS);
R(13) = 0x03007F00;
ICPSR.irq_d = false;
IO.Write8(Io::POSTFLG, 0x01);
}
void Bios008h ()
{
// if we are here, we should be in SVC mode (0x13)
// store the spsr, r11, r12 and r14 on the stack
uint32_t baseadd = R(13) - (4*4), add = (baseadd & 0xFFFFFFFC);
MEM.Write32(add , SPSR);
MEM.Write32(add += 4, R(11));
MEM.Write32(add += 4, R(12));
MEM.Write32(add + 4, R(14));
R(13) = baseadd;
uint8_t swiComment = MEM.Read8(R(14) - 2);
// put 0x1F in cpsr but don't touch to the irq disable bit
CPU.SwitchToMode(0x1F);
CPSR = 0x0000001F | (CPSR & (0x1 << 7));
CPU.UpdateICpsr();
// store r11 and r14 (of the user mode) on the stack
baseadd = R(13) - (2*4); add = (baseadd & 0xFFFFFFFC);
MEM.Write32(add , R(11));
MEM.Write32(add + 4, R(14));
R(13) = baseadd;
R(14) = 0x168;
debug("Software IRQ start");
switch (swiComment)
{
case 0x04:
IntrWait();
break;
case 0x05:
VBlankIntrWait();
break;
default:
met_abort("not implemented : " << (int)swiComment);
break;
}
}
void Bios168h ()
{
uint32_t add = R(13) & 0xFFFFFFFC;
R( 2) = MEM.Read32(add );
R(14) = MEM.Read32(add += 4);
R(13) += 2*4;
// SVC with fiq and irq disabled
CPU.SwitchToMode(0x13); // SVC
CPSR = 0x000000D3;
CPU.UpdateICpsr();
add = R(13) & 0xFFFFFFFC;
SPSR = MEM.Read32(add);
R(11) = MEM.Read32(add += 4);
R(12) = MEM.Read32(add += 4);
R(14) = MEM.Read32(add + 4);
R(13) += 4*4;
// FIXME this works (for thumb) ?
if (CPU.Spsr().b.thumb)
R(15) = R(14) + 2;
else
R(15) = R(14) + 4;
debug("Software IRQ end");
CPU.SwitchModeBack();
}
void Bios018h ()
{
debug("IRQ start");
// stmfd r13!,r0-r3,r12,r14
uint32_t baseadd = R(13) - (6*4), add = (baseadd & 0xFFFFFFFC);
MEM.Write32(add , R( 0));
MEM.Write32(add += 4, R( 1));
MEM.Write32(add += 4, R( 2));
MEM.Write32(add += 4, R( 3));
MEM.Write32(add += 4, R(12));
MEM.Write32(add + 4, R(14));
R(13) = baseadd;
// add r14,r15,0h
R(14) = 0x00000130;
R(15) = MEM.Read32(0x03007FFC) + 4;
}
void Bios130h ()
{
debug("IRQ end");
// ldmfd r13!,r0-r3,r12,r14
uint32_t add = R(13) & 0xFFFFFFFC;
R( 0) = MEM.Read32(add );
R( 1) = MEM.Read32(add += 4);
R( 2) = MEM.Read32(add += 4);
R( 3) = MEM.Read32(add += 4);
R(12) = MEM.Read32(add += 4);
R(14) = MEM.Read32(add + 4);
R(13) += 6*4;
// subs r15,r14,4h
R(15) = R(14);
if (CPU.Spsr().b.thumb)
R(15) -= 2;
CPU.SwitchModeBack();
// XXX FIXME, usefull ? test on breath of fire !
/*if (FLAG_T)
R(15) &= 0xFFFFFFFE;
else
R(15) &= 0xFFFFFFFC;*/
}
void SoftReset ()
{
CPU.SoftReset ();
if (MEM.Read8(0x03007FFA))
R(15) = 0x02000004;
else
R(15) = 0x08000004;
MEM.SoftReset ();
}
void RegisterRamReset ()
{
IO.Write16(Io::DISPCNT, 0x0080);
uint8_t flagRes = R(0);
if (flagRes & (0x1 ))
MEM.ClearWbram();
if (flagRes & (0x1 << 1))
MEM.ClearWcram();
if (flagRes & (0x1 << 2))
MEM.ClearPalette();
if (flagRes & (0x1 << 3))
MEM.ClearVram();
if (flagRes & (0x1 << 4))
MEM.ClearOam();
if (flagRes & (0x1 << 5))
IO.ClearSio ();
if (flagRes & (0x1 << 6))
IO.ClearSound ();
if (flagRes & (0x1 << 7))
IO.ClearOthers ();
}
void Halt ()
{
IO.Write8(Io::HALTCNT, 0);
}
void IntrWait ()
{
// FIXME ugly
R(13) -= 8;
MEM.Write32(R(13) & 0xFFFFFFFC, R(4));
MEM.Write32((R(13)+4) & 0xFFFFFFFC, R(14));
uint16_t& intFlags = *(uint16_t*)MEM.GetRealAddress(0x03007FF8);
if (R(0))
{
if (intFlags & R(1))
intFlags = (intFlags & R(1)) ^ intFlags;
else
FLAG_Z = 1;
IO.Write16(Io::IME, 1);
}
IO.Write8(Io::HALTCNT, 0);
// return address (after IRQ)
R(15) = 0x33C;
debug("IntrWait start");
}
void Bios338h ()
{
uint16_t& intFlags = *(uint16_t*)MEM.GetRealAddress(0x03007FF8);
if (!(intFlags & R(1)))
{
IO.Write16(Io::IME, 1);
IO.Write8(Io::HALTCNT, 0);
}
else
{
intFlags = (intFlags & R(1)) ^ intFlags;
IO.Write16(Io::IME, 1);
// FIXME ugly
R(4) = MEM.Read32(R(13) & 0xFFFFFFFC);
R(14) = MEM.Read32((R(13)+4) & 0xFFFFFFFC);
R(13) += 8;
// should lead to 0x168
R(15) = R(14)+4;
}
debug("IntWait end");
}
void VBlankIntrWait ()
{
R(0) = 1;
R(1) = 1;
IntrWait();
}
void Div ()
{
if (!R(1))
met_abort("Div by 0");
int32_t number = R(0), denom = R(1);
int32_t div = number / denom;
R(0) = div;
R(1) = number % denom;
R(3) = div < 0 ? -div : div;
}
void DivArm ()
{
uint32_t tmp = R(0);
R(0) = R(1);
R(1) = tmp;
Div();
}
void Sqrt ()
{
R(0) = (uint16_t)sqrt((float)R(0));
}
void ArcTan ()
{
int32_t a = -(((int32_t)R(0) * R(0)) >> 14);
int32_t b = 0xA9;
b = ((a * b) >> 14) + 0x0390;
b = ((a * b) >> 14) + 0x091C;
b = ((a * b) >> 14) + 0x0FB6;
b = ((a * b) >> 14) + 0X16AA;
b = ((a * b) >> 14) + 0X2081;
b = ((a * b) >> 14) + 0X3651;
b = ((a * b) >> 14) + 0XA2F9;
R(0) = (R(0) * b) >> 16;
}
void ArcTan2 ()
{
int16_t x = R(0), y = R(1);
if (y)
if (x)
if (abs(x) < abs(y))
{
R(0) <<= 14;
Div();
ArcTan();
R(0) = 0x4000 - R(0);
if (y < 0)
R(0) += 0x8000;
}
else
{
uint32_t r1 = R(1);
R(1) = R(0);
R(0) = r1 << 14;
Div();
ArcTan();
if (x < 0)
R(0) += 0x8000;
else if (y < 0)
R(0) += 0x10000;
}
else
if (y < 0)
R(0) = 0xc000;
else
R(0) = 0x4000;
else
if (x < 0)
R(0) = 0x8000;
else
R(0) = 0x0000;
}
void CpuSet ()
{
if (R(2) & (0x1 << 26)) // 32 bits
{
if (R(2) & (0x1 << 24)) // fixed source address
{
uint32_t source = MEM.Read32(R(0) & 0xFFFFFFFC);
uint32_t address = R(1) & 0xFFFFFFFC;
for (uint32_t count = (R(2) & 0x001FFFFF); count; --count)
{
MEM.Write32(address, source);
address += 4;
}
}
else // copy
{
uint32_t src = R(0) & 0xFFFFFFFC;
uint32_t dest = R(1) & 0xFFFFFFFC;
for (uint32_t count = (R(2) & 0x001FFFFF); count; --count)
{
MEM.Write32(dest, MEM.Read32(src));
src += 4;
dest += 4;
}
}
}
else // 16 bits
{
if (R(2) & (0x1 << 24)) // fixed source address
{
uint16_t source = MEM.Read16(R(0));
uint32_t address = R(1);
for (uint32_t count = (R(2) & 0x001FFFFF); count; --count)
{
MEM.Write16(address, source);
address += 2;
}
}
else // copy
{
uint32_t src = R(0);
uint32_t dest = R(1);
for (uint32_t count = (R(2) & 0x001FFFFF); count; --count)
{
MEM.Write16(dest, MEM.Read16(src));
src += 2;
dest += 2;
}
}
}
}
void CpuFastSet ()
{
if (R(2) & (0x1 << 24)) // fixed source address
{
uint32_t source = MEM.Read32(R(0));
uint32_t address = R(1);
for (uint32_t count = (R(2) & 0x001FFFFF); count; --count)
{
MEM.Write32(address, source);
address += 4;
}
}
else // copy
{
uint32_t src = R(0);
uint32_t dest = R(1);
for (uint32_t count = (R(2) & 0x001FFFFF); count; --count)
{
MEM.Write32(dest, MEM.Read32(src));
src += 4;
dest += 4;
}
}
}
void BgAffineSet ()
{
uint32_t src = R(0);
uint32_t dest = R(1);
uint32_t num = R(2);
int32_t cx, cy;
int16_t dix, diy, rx, ry;
uint16_t alpha;
int32_t cos, sin;
int16_t dx, dmx, dy, dmy;
while (num--)
{
cx = MEM.Read32(src);
src += 4;
cy = MEM.Read32(src);
src += 4;
dix = MEM.Read16(src);
src += 2;
diy = MEM.Read16(src);
src += 2;
rx = MEM.Read16(src);
src += 2;
ry = MEM.Read16(src);
src += 2;
alpha = MEM.Read16(src) >> 8;
src += 2;
sin = sineTable[alpha];
cos = sineTable[(alpha + 0x40) & 0xFF];
dx = (rx * cos) >> 14;
dmx = -((rx * sin) >> 14);
dy = (ry * sin) >> 14;
dmy = (ry * cos) >> 14;
MEM.Write16(dest, dx);
dest += 2;
MEM.Write16(dest, dmx);
dest += 2;
MEM.Write16(dest, dy);
dest += 2;
MEM.Write16(dest, dmy);
dest += 2;
MEM.Write32(dest, cx - dx * dix - dmx * diy);
dest += 4;
MEM.Write32(dest, cy - dy * dix - dmy * diy);
dest += 4;
}
}
void ObjAffineSet ()
{
uint32_t src = R(0);
uint32_t dest = R(1);
uint32_t num = R(2);
uint32_t off = R(3);
int16_t rx, ry;
uint16_t alpha;
int32_t cos, sin;
int16_t dx, dmx, dy, dmy;
while (num--)
{
rx = MEM.Read16(src);
src += 2;
ry = MEM.Read16(src);
src += 2;
alpha = MEM.Read16(src) >> 8;
src += 4;
sin = sineTable[alpha];
cos = sineTable[(alpha + 0x40) & 0xFF];
dx = (rx * cos) >> 14;
dmx = -((rx * sin) >> 14);
dy = (ry * sin) >> 14;
dmy = (ry * cos) >> 14;
MEM.Write16(dest, dx);
dest += off;
MEM.Write16(dest, dmx);
dest += off;
MEM.Write16(dest, dy);
dest += off;
MEM.Write16(dest, dmy);
dest += off;
}
}
void LZ77UnCompWram ()
{
uint32_t src = R(0);
uint32_t header = MEM.Read32(src);
src += 4;
if (((header >> 4) & 0xF) != 1)
met_abort("This is not LZ77 data");
uint32_t size = header >> 8;
debug("LZ77UnCompWram from " << IOS_ADD << R(0) << " to " << IOS_ADD << R(1) << ", len : " << size);
uint32_t dest = R(1);
uint8_t flags;
uint16_t block;
uint8_t blocklen;
uint32_t realaddr;
// for each block of a flags byte + 8 blocks
while (true)
{
flags = MEM.Read8(src++);
for (uint8_t i = 0; i < 8; ++i)
{
// compressed block of 2 bytes
if (flags & 0x80)
{
block = MEM.Read8(src) << 8 | MEM.Read8(src+1);
src += 2;
blocklen = (block >> 12) + 3;
realaddr = dest - (block & 0x0FFF) - 1;
for(uint16_t j = 0; j < blocklen; ++j)
{
MEM.Write8(dest++, MEM.Read8(realaddr++));
--size;
if(size == 0)
{
size = header >> 8;
return;
}
}
}
// uncompressed block of 1 byte
else
{
MEM.Write8(dest++, MEM.Read8(src++));
--size;
if (size == 0)
{
size = header >> 8;
return;
}
}
flags <<= 1;
}
}
}
void LZ77UnCompVram ()
{
uint32_t src = R(0);
uint32_t header = MEM.Read32(src);
src += 4;
if (((header >> 4) & 0xF) != 1)
met_abort("This is not LZ77 data");
uint32_t size = header >> 8;
debug("LZ77UnCompVram from " << IOS_ADD << R(0) << " to " << IOS_ADD << R(1) << ", len : " << size);
uint32_t dest = R(1);
uint8_t flags;
uint16_t out = 0;
uint8_t shift = 0;
uint16_t block;
uint8_t blocklen;
uint32_t realaddr;
// for each block of a flags byte + 8 blocks
while (true)
{
flags = MEM.Read8(src++);
for (uint8_t i = 0; i < 8; ++i)
{
// compressed block of 2 bytes
if (flags & 0x80)
{
block = MEM.Read8(src) << 8 | MEM.Read8(src+1);
src += 2;
blocklen = (block >> 12) + 3;
realaddr = dest + (shift/8) - (block & 0x0FFF) - 1;
for(uint16_t j = 0; j < blocklen; ++j) {
out |= MEM.Read8(realaddr++) << shift;
shift += 8;
if(shift == 16) {
MEM.Write16(dest, out);
dest += 2;
out = 0;
shift = 0;
}
--size;
if(size == 0)
{
size = header >> 8;
return;
}
}
}
// uncompressed block of 1 byte
else
{
out |= MEM.Read8(src++) << shift;
shift += 8;
if (shift == 16)
{
MEM.Write16(dest, out);
dest += 2;
shift = 0;
out = 0;
}
--size;
if (size == 0)
{
size = header >> 8;
return;
}
}
flags <<= 1;
}
}
}
void HuffUnComp ()
{
uint32_t src = R(0) & 0xFFFFFFFC;
uint32_t dest = R(1);
uint32_t header = MEM.Read32(src);
src += 4;
if (((header >> 4) & 0xF) != 2)
met_abort("This is not Huffman data");
uint8_t blockLen = header & 0xF;
uint32_t size = header >> 8;
if (size % 4)
met_abort("Size not multiple of 4 in HuffUnComp");
uint32_t treeStart = src + 1;
src += 2 + MEM.Read8(src) * 2;
uint32_t cData = MEM.Read32(src);
src += 4;
uint32_t mask = 0x80000000;
uint32_t treePos = treeStart;
uint8_t node = MEM.Read8(treePos);
bool endNode = false;
uint32_t oData = 0;
uint8_t oShift = 0;
while (size)
{
treePos = (treePos & 0xFFFFFFFE) + (node & 0x3F) * 2 + 2;
if (cData & mask)
{
++treePos;
if (node & (0x1 << 6))
endNode = true;
}
else
{
if (node & (0x1 << 7))
endNode = true;
}
node = MEM.Read8(treePos);
if (endNode)
{
oData |= ((uint32_t)node) << oShift;
oShift += blockLen;
if (oShift >= 32)
{
MEM.Write32(dest, oData);
dest += 4;
size -= 4;
oShift -= 32;
if (oShift)
oData = node >> (8 - oShift);
else
oData = 0;
}
endNode = false;
treePos = treeStart;
node = MEM.Read8(treePos);
}
mask >>= 1;
if (!mask)
{
cData = MEM.Read32(src);
src += 4;
mask = 0x80000000;
}
}
}
void RLUnCompWram ()
{
uint32_t src = R(0);
uint32_t header = MEM.Read32(src);
src += 4;
if (((header >> 4) & 0xF) != 3)
met_abort("This is not RL data");
uint32_t size = header >> 8;
debug("RLUnCompWram from " << IOS_ADD << R(0) << " to " << IOS_ADD << R(1) << ", len : " << size);
uint32_t dest = R(1);
uint8_t flags;
uint8_t block;
uint8_t blocklen;
// for each block
while (true)
{
flags = MEM.Read8(src++);
blocklen = flags & 0x7F;
// compressed block
if (flags & 0x80)
{
blocklen += 3;
block = MEM.Read8(src++);
for(uint8_t i = 0; i < blocklen; ++i) {
MEM.Write8(dest++, block);
--size;
if(size == 0)
{
size = header >> 8;
return;
}
}
}
// uncompressed block
else
{
blocklen += 1;
for (uint8_t i = 0; i < blocklen; ++i)
{
MEM.Write8(dest++, MEM.Read8(src++));
--size;
if (size == 0)
{
size = header >> 8;
return;
}
}
}
}
}
void RLUnCompVram ()
{
uint32_t src = R(0);
uint32_t header = MEM.Read32(src);
src += 4;
if (((header >> 4) & 0xF) != 3)
met_abort("This is not RL data");
uint32_t size = header >> 8;
debug("RLUnCompVram from " << IOS_ADD << R(0) << " to " << IOS_ADD << R(1) << ", len : " << size);
uint32_t dest = R(1);
uint8_t flags;
uint16_t out = 0;
uint8_t shift = 0;
uint8_t block;
uint8_t blocklen;
// for each block
while (true)
{
flags = MEM.Read8(src++);
blocklen = flags & 0x7F;
// compressed block
if (flags & 0x80)
{
blocklen += 3;
block = MEM.Read8(src++);
for(uint8_t i = 0; i < blocklen; ++i) {
out |= block << shift;
shift += 8;
if(shift == 16) {
MEM.Write16(dest, out);
dest += 2;
out = 0;
shift = 0;
}
--size;
if(size == 0)
{
size = header >> 8;
return;
}
}
}
// uncompressed block
else
{
blocklen += 1;
for (uint8_t i = 0; i < blocklen; ++i)
{
out |= MEM.Read8(src++) << shift;
shift += 8;
if (shift == 16)
{
MEM.Write16(dest, out);
dest += 2;
shift = 0;
out = 0;
}
--size;
if (size == 0)
{
size = header >> 8;
return;
}
}
}
}
}
}
}

View File

@ -1,57 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/cartmem.hpp"
#include "globals.hpp"
namespace AMeteor
{
#ifdef __LIBRETRO__
uint8_t CartMemData[CartMem::MAX_SIZE+4];
#endif
CartMem::CartMem() :
#ifdef __LIBRETRO__
m_data(CartMemData)
#else
m_data(new uint8_t[MAX_SIZE+4])
#endif
{
}
CartMem::~CartMem()
{
#ifndef __LIBRETRO__
delete [] m_data;
#endif
}
bool CartMem::SaveState (std::ostream& stream)
{
stream.write((char*)m_data, MAX_SIZE);
SS_WRITE_VAR(m_size);
return true;
}
bool CartMem::LoadState (std::istream& stream)
{
stream.read((char*)m_data, MAX_SIZE);
SS_READ_VAR(m_size);
return true;
}
}

View File

@ -1,125 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/clock.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
namespace AMeteor
{
void Clock::Reset ()
{
// lcd is enabled by default
m_first = m_count = m_cycles = m_lcd = m_sound = 0;
// timers and battery are disabled by default
/*m_battery =*/ m_timer[0] = m_timer[1] = m_timer[2] = m_timer[3] =
INT_MAX;
}
void Clock::Commit ()
{
unsigned short tocommit;
//m_count += m_cycles;
// this loop is here because a timer can trigger a dma which will take a
// long time, during this time the lcd must draw and the timers continue
while (m_cycles >= m_first)
{
m_count += m_cycles;
tocommit = m_cycles;
m_cycles = 0;
m_lcd -= tocommit;
while (m_lcd <= 0)
LCD.TimeEvent();
m_sound -= tocommit;
while (m_sound <= 0)
{
SOUND.TimeEvent();
// XXX freq
m_sound += SOUND_PERIOD;
}
#define COMMIT(dev, obj) \
if (m_##dev != INT_MAX) \
{ \
m_##dev -= tocommit; \
while (m_##dev <= 0) \
obj.TimeEvent(); \
}
COMMIT(timer[0], TIMER0)
COMMIT(timer[1], TIMER1)
COMMIT(timer[2], TIMER2)
COMMIT(timer[3], TIMER3)
//COMMIT(battery, MEM)
#undef COMMIT
SetFirst();
}
}
void Clock::WaitForNext ()
{
m_cycles = m_first;
Commit();
}
#define SETFIRST(dev) \
if (m_##dev < m_first) \
m_first = m_##dev
void Clock::SetFirst ()
{
m_first = m_lcd;
SETFIRST(timer[0]);
SETFIRST(timer[1]);
SETFIRST(timer[2]);
SETFIRST(timer[3]);
SETFIRST(sound);
//SETFIRST(battery);
}
#undef SETFIRST
bool Clock::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_cycles);
SS_WRITE_VAR(m_first);
SS_WRITE_VAR(m_lcd);
SS_WRITE_VAR(m_sound);
//SS_WRITE_VAR(m_battery);
SS_WRITE_ARRAY(m_timer);
return true;
}
bool Clock::LoadState (std::istream& stream)
{
SS_READ_VAR(m_cycles);
SS_READ_VAR(m_first);
SS_READ_VAR(m_lcd);
SS_READ_VAR(m_sound);
//SS_READ_VAR(m_battery);
SS_READ_ARRAY(m_timer);
return true;
}
}

View File

@ -1,447 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/cpu.hpp"
#include "ameteor/bios.hpp"
#include "globals.hpp"
#include "cpu_globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
#include <cstring>
namespace AMeteor
{
Cpu::Cpu ()
{
Reset();
}
void Cpu::Reset ()
{
std::memset(&m_st, 0, sizeof(m_st));
R(15) = 0x00000004;
m_st.icpsr.mode = M_SVC;
m_st.icpsr.fiq_d = true;
m_st.icpsr.irq_d = true;
}
void Cpu::SoftReset ()
{
std::memset(&m_st, 0, sizeof(m_st));
R(13) = 0x03007F00;
R(15) = 0x08000004;
m_st.irq_r[0] = 0x03007FA0; // R13
m_st.svc_r[0] = 0x03007FE0; // R13
m_st.icpsr.mode = 0x1F;
m_st.icpsr.fiq_d = true;
}
void Cpu::UpdateICpsr ()
{
m_st.icpsr.mode = m_st.cpsr.b.mode;
m_st.icpsr.irq_d = m_st.cpsr.b.irq_d;
m_st.icpsr.fiq_d = m_st.cpsr.b.fiq_d;
m_st.icpsr.thumb = m_st.cpsr.b.thumb;
m_st.icpsr.s_overflow = m_st.cpsr.b.s_overflow;
m_st.icpsr.f_overflow = m_st.cpsr.b.f_overflow;
m_st.icpsr.f_carry = m_st.cpsr.b.f_carry;
m_st.icpsr.f_zero = m_st.cpsr.b.f_zero;
m_st.icpsr.f_sign = m_st.cpsr.b.f_sign;
}
void Cpu::UpdateCpsr ()
{
m_st.cpsr.b.mode = m_st.icpsr.mode;
m_st.cpsr.b.irq_d = m_st.icpsr.irq_d;
m_st.cpsr.b.fiq_d = m_st.icpsr.fiq_d;
m_st.cpsr.b.thumb = m_st.icpsr.thumb;
m_st.cpsr.b.s_overflow = m_st.icpsr.s_overflow;
m_st.cpsr.b.f_overflow = m_st.icpsr.f_overflow;
m_st.cpsr.b.f_carry = m_st.icpsr.f_carry;
m_st.cpsr.b.f_zero = m_st.icpsr.f_zero;
m_st.cpsr.b.f_sign = m_st.icpsr.f_sign;
}
void Cpu::SwitchToMode (uint8_t newmode)
{
SaveMode(m_st.icpsr.mode);
switch (newmode)
{
case 0x10: // User (non-privileged)
case 0x1F: // System (privileged 'User' mode)
switch (m_st.icpsr.mode)
{
case 0x10:
case 0x1F:
case 0x11:
std::memcpy(m_st.r + 8, m_st.usr_r, sizeof(m_st.usr_r));
break;
default:
std::memcpy(m_st.r + 13, m_st.usr_r + 5, 2*4);
break;
}
break;
case 0x11: // FIQ
std::memcpy(m_st.r + 8, m_st.fiq_r, sizeof(m_st.fiq_r));
break;
case 0x12: // IRQ
std::memcpy(m_st.r + 13, m_st.irq_r, sizeof(m_st.irq_r));
break;
case 0x13: // Supervisor (SWI)
std::memcpy(m_st.r + 13, m_st.svc_r, sizeof(m_st.svc_r));
break;
case 0x17: // Abort
std::memcpy(m_st.r + 13, m_st.abt_r, sizeof(m_st.abt_r));
break;
case 0x1B: // Undefined
std::memcpy(m_st.r + 13, m_st.und_r, sizeof(m_st.und_r));
break;
default:
met_abort("Unknown CPU mode : " << IOS_ADD << (int)newmode);
break;
}
UpdateCpsr();
m_st.spsr.dw = m_st.cpsr.dw;
m_st.icpsr.mode = newmode;
}
void Cpu::SwitchModeBack ()
{
// oldmode is the mode on which we want to switch back
uint8_t oldmode = m_st.spsr.b.mode, curmode = m_st.icpsr.mode;
// we don't care if the spsr of the mode we are using is modified
SaveMode(curmode);
m_st.cpsr.dw = m_st.spsr.dw;
UpdateICpsr();
CheckInterrupt();
switch (oldmode)
{
case 0x10: // User (non-privileged)
case 0x1F: // System (privileged 'User' mode)
switch (curmode)
{
case 0x10:
case 0x1F:
case 0x11:
std::memcpy(m_st.r + 8, m_st.usr_r, sizeof(m_st.usr_r));
break;
default:
std::memcpy(m_st.r + 13, m_st.usr_r + 5, 2*4);
break;
}
break;
case 0x11: // FIQ
std::memcpy(m_st.r + 8, m_st.fiq_r, sizeof(m_st.fiq_r));
m_st.spsr.dw = m_st.fiq_spsr.dw;
break;
case 0x12: // IRQ
std::memcpy(m_st.r + 13, m_st.irq_r, sizeof(m_st.irq_r));
m_st.spsr.dw = m_st.irq_spsr.dw;
break;
case 0x13: // Supervisor (SWI)
std::memcpy(m_st.r + 13, m_st.svc_r, sizeof(m_st.svc_r));
m_st.spsr.dw = m_st.svc_spsr.dw;
break;
case 0x17: // Abort
std::memcpy(m_st.r + 13, m_st.abt_r, sizeof(m_st.abt_r));
m_st.spsr.dw = m_st.abt_spsr.dw;
break;
case 0x1B: // Undefined
std::memcpy(m_st.r + 13, m_st.und_r, sizeof(m_st.und_r));
m_st.spsr.dw = m_st.und_spsr.dw;
break;
default:
met_abort("Unknown CPU mode : " << IOS_ADD << (int)oldmode);
break;
}
}
void Cpu::SaveMode (uint8_t mode)
{
switch (mode)
{
case 0x10: // User (non-privileged)
case 0x1F: // System (privileged 'User' mode)
std::memcpy(m_st.usr_r, m_st.r + 8, sizeof(m_st.usr_r));
break;
case 0x11: // FIQ
std::memcpy(m_st.fiq_r, m_st.r + 8, sizeof(m_st.fiq_r));
m_st.fiq_spsr.dw = m_st.spsr.dw;
break;
case 0x12: // IRQ
std::memcpy(m_st.irq_r, m_st.r + 13, sizeof(m_st.irq_r));
m_st.irq_spsr.dw = m_st.spsr.dw;
break;
case 0x13: // Supervisor (SWI)
std::memcpy(m_st.svc_r, m_st.r + 13, sizeof(m_st.svc_r));
m_st.svc_spsr.dw = m_st.spsr.dw;
break;
case 0x17: // Abort
std::memcpy(m_st.abt_r, m_st.r + 13, sizeof(m_st.abt_r));
m_st.abt_spsr.dw = m_st.spsr.dw;
break;
case 0x1B: // Undefined
std::memcpy(m_st.und_r, m_st.r + 13, sizeof(m_st.und_r));
m_st.und_spsr.dw = m_st.spsr.dw;
break;
default:
met_abort("Unknown CPU mode : " << IOS_ADD << (int)mode);
break;
}
}
void Cpu::Interrupt ()
{
// Switch mode
SwitchToMode(0x12); // IRQ
// Save PC
R(14) = R(15);
// FIXME : why ? this seems to be USELESS ! (look at bios irq end)
if (m_st.icpsr.thumb)
R(14) += 2;
// Switch to ARM
m_st.icpsr.thumb = false;
// Disable IRQ
m_st.icpsr.irq_d = true;
SetInterrupt(false);
// Branch on 0x18
R(15) = 0x1C;
}
void Cpu::SoftwareInterrupt ()
{
// Switch mode
SwitchToMode(0x13); // Supervisor
// Save PC
R(14) = R(15) - (m_st.icpsr.thumb ? 2 : 4);
// Switch to ARM
m_st.icpsr.thumb = false;
// Disable IRQ
m_st.icpsr.irq_d = true;
SetInterrupt(false);
// Branch on 0x8
R(15) = 0xC;
}
// TODO put this in Bios, no ?
void Cpu::SoftwareInterrupt (uint32_t comment)
{
if (true)
{
char buff[256];
int pos = 0;
pos += sprintf (buff + pos, "SWI %02xh : ", comment);
switch (comment) // no one cares about the sound driver
{
case 0x00: pos += sprintf (buff + pos, "SoftReset");
break;
case 0x01: pos += sprintf (buff + pos, "RegisterRamReset"); // todo: display flags
break;
case 0x02: pos += sprintf (buff + pos, "Halt");
break;
case 0x03: pos += sprintf (buff + pos, "Stop");
break;
case 0x04: pos += sprintf (buff + pos, "IntrWait"); // todo: display flags
break;
case 0x05: pos += sprintf (buff + pos, "VBlankIntrWait");
break;
case 0x06: pos += sprintf (buff + pos, "Div: ");
pos += sprintf (buff + pos, "%08xh/%08xh", R(0), R(1));
break;
case 0x07: pos += sprintf (buff + pos, "DivArm: ");
pos += sprintf (buff + pos, "%08xh/%08xh", R(1), R(0));
break;
case 0x08: pos += sprintf (buff + pos, "Sqrt: ");
pos += sprintf (buff + pos, "sqrt(%08xh)", R(0));
break;
case 0x09: pos += sprintf (buff + pos, "ArcTan: ");
pos += sprintf (buff + pos, "atan(%04xh)", R(0) & 0xffff);
break;
case 0x0a: pos += sprintf (buff + pos, "ArcTan2: ");
pos += sprintf (buff + pos, "atan2(%04xh,%04xh)", R(1) & 0xffff, R(0) & 0xffff);
break;
case 0x0b: pos += sprintf (buff + pos, "CpuSet: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh], wcnt=%xh, fixed=%c, size=%d", R(0), R(1), R(2) & 0x1fffff, R(2) & 0x1000000 ? 'Y' : 'N', R(2) & 0x4000000 ? 32 : 16);
break;
case 0x0c: pos += sprintf (buff + pos, "CpuFastSet: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh], wcnt=%xh, fixed=%c", R(0), R(1), R(2) & 0x1fffff, R(2) & 0x1000000 ? 'Y' : 'N');
break;
case 0x0d: pos += sprintf (buff + pos, "GetBiosChecksum");
break;
case 0x0e: pos += sprintf (buff + pos, "BgAffineSet: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh], cnt=%d", R(0), R(1), R(2));
break;
case 0x0f: pos += sprintf (buff + pos, "ObjAffineSet: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh], cnt=%d, offs=%d", R(0), R(1), R(2), R(3));
break;
case 0x10: pos += sprintf (buff + pos, "BitUnPack: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh], info=[%08xh]", R(0), R(1), R(2));
break;
case 0x11: pos += sprintf (buff + pos, "LZ77UnCompWram: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh]", R(0), R(1));
break;
case 0x12: pos += sprintf (buff + pos, "LZ77UnCompVram: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh]", R(0), R(1));
break;
case 0x13: pos += sprintf (buff + pos, "HuffUnComp: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh]", R(0), R(1));
break;
case 0x14: pos += sprintf (buff + pos, "RLUnCompWram: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh]", R(0), R(1));
break;
case 0x15: pos += sprintf (buff + pos, "RLUnCompVram: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh]", R(0), R(1));
break;
case 0x16: pos += sprintf (buff + pos, "Diff8bitUnFilterWram: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh]", R(0), R(1));
break;
case 0x17: pos += sprintf (buff + pos, "Diff8bitUnFilterVram: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh]", R(0), R(1));
break;
case 0x18: pos += sprintf (buff + pos, "Diff16bitUnFilter: ");
pos += sprintf (buff + pos, "src=[%08xh], dst=[%08xh]", R(0), R(1));
break;
case 0x19: pos += sprintf (buff + pos, "SoundBias");
break;
case 0x1a: pos += sprintf (buff + pos, "SoundDriverInit");
break;
case 0x1b: pos += sprintf (buff + pos, "SoundDriverMode");
break;
case 0x1c: pos += sprintf (buff + pos, "SoundDriverMain");
break;
case 0x1d: pos += sprintf (buff + pos, "SoundDriverVSync");
break;
case 0x1e: pos += sprintf (buff + pos, "SoundChannelClear");
break;
case 0x1f: pos += sprintf (buff + pos, "MidiKey2Freq");
break;
case 0x20: pos += sprintf (buff + pos, "SoundWhatever0");
break;
case 0x21: pos += sprintf (buff + pos, "SoundWhatever1");
break;
case 0x22: pos += sprintf (buff + pos, "SoundWhatever2");
break;
case 0x23: pos += sprintf (buff + pos, "SoundWhatever3");
break;
case 0x24: pos += sprintf (buff + pos, "SoundWhatever4");
break;
case 0x25: pos += sprintf (buff + pos, "MultiBoot: ");
pos += sprintf (buff + pos, "mbp=[%08xh], mode=%d", R(0), R(1));
break;
case 0x26: pos += sprintf (buff + pos, "HardReset");
break;
case 0x27: pos += sprintf (buff + pos, "CustomHalt: ");
pos += sprintf (buff + pos, "val=%02xh", R(2) & 0xff);
break;
case 0x28: pos += sprintf (buff + pos, "SoundDriverVSyncOff");
break;
case 0x29: pos += sprintf (buff + pos, "SoundDriverVSyncOnn");
break;
case 0x2a: pos += sprintf (buff + pos, "SoundGetJumpList");
break;
default: pos += sprintf (buff + pos, "UNKNOWN");
break;
}
pos += sprintf (buff + pos, "\n");
print_bizhawk(buff);
}
if (MEM.HasBios())
SoftwareInterrupt();
else
switch (comment)
{
case 0x00:
Bios::SoftReset();
break;
case 0x01:
Bios::RegisterRamReset();
break;
case 0x02:
Bios::Halt();
break;
case 0x04:
case 0x05:
SoftwareInterrupt();
break;
case 0x06:
Bios::Div();
break;
case 0x07:
Bios::DivArm();
break;
case 0x08:
Bios::Sqrt();
break;
case 0x09:
Bios::ArcTan();
break;
case 0x0A:
Bios::ArcTan2();
break;
case 0x0B:
Bios::CpuSet();
break;
case 0x0C:
Bios::CpuFastSet();
break;
case 0x0E:
Bios::BgAffineSet();
break;
case 0x0F:
Bios::ObjAffineSet();
break;
case 0x11:
Bios::LZ77UnCompWram();
break;
case 0x12:
Bios::LZ77UnCompVram();
break;
case 0x13:
Bios::HuffUnComp();
break;
case 0x14:
Bios::RLUnCompWram();
break;
case 0x15:
Bios::RLUnCompVram();
break;
default:
met_abort("Unknown software interrupt : " << IOS_ADD << comment);
break;
}
}
bool Cpu::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_st);
return true;
}
bool Cpu::LoadState (std::istream& stream)
{
SS_READ_VAR(m_st);
CheckInterrupt();
return true;
}
}

View File

@ -1,41 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __CPU_GLOBALS_H__
#define __CPU_GLOBALS_H__
#undef R
#define R(reg) m_st.r[reg]
#undef CPU
#define CPU (*this)
#undef CPSR
#undef SPSR
#undef FLAG_Z
#undef FLAG_N
#undef FLAG_C
#undef FLAG_V
#undef FLAG_T
#define CPSR (m_st.cpsr.dw)
#define SPSR (m_st.spsr.dw)
#define FLAG_Z (m_st.icpsr.f_zero)
#define FLAG_N (m_st.icpsr.f_sign)
#define FLAG_C (m_st.icpsr.f_carry)
#define FLAG_V (m_st.icpsr.f_overflow)
#define FLAG_T (m_st.icpsr.thumb)
#endif

View File

@ -1,85 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "debug.hpp"
#include "ameteor/cpu.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include <sstream>
#include <map>
namespace AMeteor
{
// TODO make this more guidelined (like the above assert)
void debug_bits(uint32_t u)
{
#if defined METDEBUG && defined METDEBUGLOG
for (register int8_t c = 31; c >= 0; --c)
{
STDBG << !!(u & (((uint32_t)0x1) << c));
if (!(c % 8))
STDBG << ' ';
}
STDBG << std::endl;
#else
(void)u;
#endif
}
void debug_bits_16(uint16_t u)
{
#if defined METDEBUG && defined METDEBUGLOG
for (register int8_t c = 15; c >= 0; --c)
{
STDBG << !!(u & (((uint32_t)0x1) << c));
if (!(c % 8))
STDBG << ' ';
}
STDBG << std::endl;
#else
(void)u;
#endif
}
#ifdef MET_REGS_DEBUG
void PrintRegs ()
{
static uint32_t regs[17] = {0};
for (uint8_t c = 0; c <= 15; ++c)
if (R(c) != regs[c])
{
STDBG << "R" << std::setbase(10) << (int)c << " = " << IOS_ADD << R(c) << '\n';
regs[c] = R(c);
}
CPU.UpdateCpsr();
if (CPSR != regs[16])
{
STDBG << "R16 = " << IOS_ADD << CPSR << '\n';
regs[16] = CPSR;
}
}
void PrintStack (uint32_t stackadd)
{
uint32_t add = stackadd;
debug("Stack : " << IOS_ADD << add);
for (; add < 0x03008000; add += 4)
debug(IOS_ADD << MEM.Read32(add));
}
#endif
}

View File

@ -1,109 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
// for abort macro
#include "ameteor.hpp"
// from cinterface.cpp
void print_bizhawk(const char *msg);
void print_bizhawk(std::string &msg);
void abort_bizhawk(const char *msg);
void keyupdate_bizhawk();
extern bool traceenabled;
void trace_bizhawk(std::string msg);
extern int slcallbackline;
void scanlinecallback_bizhawk();
#if 0
#define met_abort(str) \
{ \
std::cerr << IOS_NOR << "Fatal error :\n" << str << "\nFile : " \
<< __FILE__ << "\nLine : " << __LINE__ << "\nr15 = " \
<< IOS_ADD << ::AMeteor::_cpu.Reg(15) << "\n[r15] = " << IOS_ADD \
<< ::AMeteor::_memory.Read32(::AMeteor::_cpu.Reg(15)) \
<< "\nFlag T : " << ::AMeteor::_cpu.ICpsr().thumb << std::endl; \
abort(); \
}
#endif
#ifdef METDEBUG
#include <sstream>
//extern "C" int __stdcall MessageBoxA(int, const char *, const char *, int);
#define met_abort(_str) if(true)\
{ \
std::stringstream _zisrny; \
_zisrny << IOS_NOR << "Fatal error :\n" << _str << "\nFile : " \
<< __FILE__ << "\nLine : " << __LINE__ << "\nr15 = " \
<< IOS_ADD << ::AMeteor::_cpu.Reg(15) << "\n[r15] = " << IOS_ADD \
<< ::AMeteor::_memory.Read32(::AMeteor::_cpu.Reg(15)) \
<< "\nFlag T : " << ::AMeteor::_cpu.ICpsr().thumb << std::endl; \
abort_bizhawk(_zisrny.str().c_str()); \
}
#else
#define met_abort(str) {}
#endif
#define STDBG std::cout
//#define STDBG debug_stream
#if defined METDEBUG && defined METDEBUGLOG
//XXX
# define MYDEBUG
# define debug(_str) \
{ \
std::stringstream _zisrny; \
_zisrny << _str << std::endl; \
print_bizhawk(_zisrny.str()); \
}
//STDBG << str << std::endl
//# define debug_(str) \
// STDBG << str
#else
# define debug(s) {}
//# define debug_(s) {}
#endif
#define IOS_ADD \
"0x" << std::setbase(16) << std::setw(8) << std::setfill('0') \
<< std::uppercase
#define IOS_TRACE \
std::setbase(16) << std::setw(8) << std::setfill('0') << std::uppercase
#define IOS_NOR \
std::setbase(10) << std::setw(0) << std::setfill(' ')
namespace AMeteor
{
void debug_bits(uint32_t u);
void debug_bits_16(uint16_t u);
#if defined MET_REGS_DEBUG
void PrintRegs ();
void PrintStack (uint32_t stackadd);
#else
#define PrintRegs()
#define PrintStack(b)
#endif
}
#endif

View File

@ -1,39 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argimmediate.hpp"
#include <sstream>
#include <iomanip>
namespace AMeteor
{
namespace Disassembler
{
std::string ArgImmediate::GetString () const
{
std::ostringstream ss;
ss << '#';
ss << std::hex << std::setw(2) << std::setfill('0') << m_imm << 'h';
return ss.str();
}
Argument* ArgImmediate::Clone () const
{
return new ArgImmediate(*this);
}
}
}

View File

@ -1,83 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argmulregisters.hpp"
#include <sstream>
namespace AMeteor
{
namespace Disassembler
{
std::string ArgMulRegisters::GetString () const
{
std::ostringstream ss;
ss << '{';
bool open = false;
for (Registers::const_iterator iter = m_regs.begin();
iter != m_regs.end(); ++iter)
{
if (open &&
iter + 1 < m_regs.end() &&
*(iter + 1) == (*iter) + 1)
{
continue;
}
else if (iter + 2 < m_regs.end() &&
*(iter + 1) == (*iter) + 1 &&
*(iter + 2) == (*iter) + 2)
{
ss << 'r' << (int)*iter << '-';
open = true;
}
else
{
ss << 'r' << (int)*iter;
open = false;
if (iter + 1 != m_regs.end())
ss << ", ";
}
}
if (m_lastreg == SPREG_LR)
{
if (ss.width() != 1)
ss << ", ";
ss << "lr";
}
else if (m_lastreg == SPREG_PC)
{
if (ss.width() != 1)
ss << ", ";
ss << "pc";
}
ss << '}';
if (m_forceuser)
ss << '^';
return ss.str();
}
Argument* ArgMulRegisters::Clone () const
{
return new ArgMulRegisters(*this);
}
}
}

View File

@ -1,53 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argpsr.hpp"
namespace AMeteor
{
namespace Disassembler
{
std::string ArgPsr::GetString () const
{
std::string out;
if (m_spsr)
out = "SPSR";
else
out = "CPSR";
if (m_fields <= 0xF)
{
out += '_';
if (m_fields & 0x1)
out += 'c';
if (m_fields & 0x2)
out += 'x';
if (m_fields & 0x4)
out += 's';
if (m_fields & 0x8)
out += 'f';
}
return out;
}
Argument* ArgPsr::Clone () const
{
return new ArgPsr(*this);
}
}
}

View File

@ -1,53 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argregister.hpp"
#include <sstream>
namespace AMeteor
{
namespace Disassembler
{
std::string ArgRegister::GetString () const
{
static const char* SpeRegisters[] = {"SP", "LR", "PC"};
std::ostringstream ss;
if (m_memory)
ss << '[';
if (m_special)
ss << SpeRegisters[m_reg-13];
else
ss << 'r' << (int)m_reg;
if (m_memory)
ss << ']';
if (m_writeback)
ss << '!';
return ss.str();
}
Argument* ArgRegister::Clone () const
{
return new ArgRegister(*this);
}
}
}

View File

@ -1,71 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argrelative.hpp"
#include <sstream>
namespace AMeteor
{
namespace Disassembler
{
ArgRelative::ArgRelative (const ArgRegister& reg, const Argument& off,
bool pre, bool up, bool writeback) :
Argument(),
m_reg(reg),
m_off(off.Clone()),
m_pre(pre),
m_up(up),
m_writeback(writeback)
{ }
ArgRelative::ArgRelative (const ArgRelative& arg) :
Argument(),
m_reg(arg.m_reg),
m_off(arg.m_off->Clone()),
m_pre(arg.m_pre),
m_up(arg.m_up),
m_writeback(arg.m_writeback)
{ }
ArgRelative::~ArgRelative ()
{
delete m_off;
}
Argument* ArgRelative::Clone () const
{
return new ArgRelative(*this);
}
std::string ArgRelative::GetString () const
{
std::ostringstream ss;
ss << "[r" << (int)m_reg.GetRegister();
if (m_pre)
{
ss << (m_up ? ", +" : ", -") << m_off->GetString() << ']';
if (m_writeback)
ss << '!';
}
else
ss << (m_up ? "], +" : "], -") << m_off->GetString();
return ss.str();
}
}
}

View File

@ -1,71 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argshift.hpp"
#include <sstream>
namespace AMeteor
{
namespace Disassembler
{
ArgShift::ArgShift (const Argument& arg1, const Argument& arg2,
ShiftType type, bool memory) :
Argument(),
m_arg1(arg1.Clone()),
m_arg2(arg2.Clone()),
m_type(type),
m_memory(memory)
{ }
ArgShift::ArgShift (const ArgShift& arg) :
Argument(),
m_arg1(arg.m_arg1->Clone()),
m_arg2(arg.m_arg2->Clone()),
m_type(arg.m_type),
m_memory(arg.m_memory)
{ }
ArgShift::~ArgShift ()
{
delete m_arg1;
delete m_arg2;
}
Argument* ArgShift::Clone () const
{
return new ArgShift(*this);
}
std::string ArgShift::GetString () const
{
static const char* Shifts[] = {", LSL ", ", LSR ", ", ASR ", ", ROR ",
", RRX "};
std::ostringstream ss;
if (m_memory)
ss << '[';
ss << m_arg1->GetString() << Shifts[m_type] << m_arg2->GetString();
if (m_memory)
ss << ']';
return ss.str();
}
}
}

View File

@ -1,39 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/arguimmediate.hpp"
#include <sstream>
#include <iomanip>
namespace AMeteor
{
namespace Disassembler
{
std::string ArgUImmediate::GetString () const
{
std::ostringstream ss;
ss << '#';
ss << std::hex << std::setw(8) << std::setfill('0') << m_imm << 'h';
return ss.str();
}
Argument* ArgUImmediate::Clone () const
{
return new ArgUImmediate(*this);
}
}
}

View File

@ -1,51 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/arguments.hpp"
namespace AMeteor
{
namespace Disassembler
{
Arguments::~Arguments ()
{
Clear();
}
void Arguments::Clear ()
{
for (std::vector<Argument*>::iterator iter = m_args.begin();
iter != m_args.end(); ++iter)
{
delete *iter;
}
m_args.clear();
}
std::string Arguments::GetString () const
{
std::string out;
for (std::vector<Argument*>::const_iterator iter = m_args.begin();
iter != m_args.end(); ++iter)
{
if (!out.empty())
out += ", ";
out += (*iter)->GetString();
}
return out;
}
}
}

View File

@ -1,691 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/instruction.hpp"
#include "ameteor/disassembler/argregister.hpp"
#include "ameteor/disassembler/argrelative.hpp"
#include "ameteor/disassembler/argimmediate.hpp"
#include "ameteor/disassembler/arguimmediate.hpp"
#include "ameteor/disassembler/argshift.hpp"
#include "ameteor/disassembler/argpsr.hpp"
#include "ameteor/disassembler/argmulregisters.hpp"
#include "../globals.hpp" // for ROR
namespace AMeteor
{
namespace Disassembler
{
void Instruction::Clear ()
{
m_operator.clear();
m_args.Clear();
}
#define Rn ((code >> 16) & 0xF)
#define Rd ((code >> 12) & 0xF)
#define Rs ((code >> 8) & 0xF)
#define Rm (code & 0xF)
void Instruction::ParseArm (uint32_t offset, uint32_t code)
{
Clear();
if ((code & 0x0FFFFFD0) == 0x12FFF10)
{
if (code & (0x1 << 5))
m_operator = "BLX";
else
m_operator = "BX";
m_args.AddArgument(ArgRegister(Rm));
ParseArmCondition(code);
}
else if (((code >> 25) & 0x7) == 0x5)
{
if (((code >> 28) & 0xF) == 0xF)
{
m_operator = "BLX";
m_args.AddArgument(ArgImmediate(offset + 8 +
(((code & (0x1 << 23)) ? 0xFF000000 | (code & 0x00FFFFFF)
: (code & 0x00FFFFFF)) << 2)
+ ((code & (0x1 << 24)) ? 2 : 0)));
}
else
{
if (code & (0x1 << 24))
m_operator = "BL";
else
m_operator = "B";
m_args.AddArgument(ArgImmediate(offset + 8 +
(((code & (0x1 << 23)) ? 0xFF000000 | (code & 0x00FFFFFF)
: (code & 0x00FFFFFF)) << 2)));
ParseArmCondition(code);
}
}
else if (((code >> 25) & 0x7) == 0x1)
{
ParseArmDataProc(code);
}
else if (((code >> 26) & 0x3) == 0x1)
{
if ((code & 0xF0100000) != 0xF0100000) // not PLD
m_args.AddArgument(ArgRegister(Rd));
if (code & (0x1 << 25)) // register offset
{
switch ((code >> 5) & 0x3)
{
case 0: // Logical Shift Left
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_LSL, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
break;
case 1: // Logical Shift Right
if (code & (0x1F << 7))
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_LSR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
else
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate(32), SHIFT_LSR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
break;
case 2: // Arithmetic Shift Right
if (code & (0x1F << 7))
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_ASR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
else
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate(32), SHIFT_ASR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
break;
case 3: // ROtate Right
if (code & (0x1F << 7))
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_ROR, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
else
m_args.AddArgument(ArgRelative(Rn, ArgShift(ArgRegister(Rm),
ArgImmediate(1), SHIFT_RRX, false),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
break;
}
}
else // immediate offset
{
m_args.AddArgument(ArgRelative(Rn, ArgImmediate(code & 0xFFF),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
}
if ((code & 0xF0100000) == 0xF0100000)
m_operator = "PLD";
else
{
if (code & (0x1 << 20))
m_operator = "LDR";
else
m_operator = "STR";
if (code & (0x1 << 22))
m_operator += "B";
ParseArmCondition(code);
}
}
else if (((code >> 25) & 0x7) == 0x4)
{
if (code & (0x1 << 20))
m_operator = "LDM";
else
m_operator = "STM";
if (code & (0x1 << 23))
m_operator += 'I';
else
m_operator += 'D';
if (code & (0x1 << 24))
m_operator += 'B';
else
m_operator += 'A';
m_args.AddArgument(ArgRegister(Rn, code & (0x1 << 21)));
ArgMulRegisters argRegs(code & (0x1 << 22));
for (register uint8_t n = 0; n < 16; ++n)
if (code & (0x1 << n))
argRegs.AddRegister(n);
m_args.AddArgument(argRegs);
ParseArmCondition(code);
}
else if (((code >> 25) & 0x7) == 0x0)
{
if ((code & 0x0FC000F0) == 0x00000090 ||
(code & 0x0F8000F0) == 0x00800090 ||
(code & 0x0F900090) == 0x01000080)
{
// NOTE : In this instruction Rn and Rd are inverted
static const char* Instructions[] = {"MUL", "MLA", "Reserved",
"Reserved", "UMULL", "UMLAL", "SMULL", "SMLAL", "SMLAxy",
"", // This is for SMLAWy and SMULWy
"SMLALxy", "SMULxy", "Reserved", "Reserved", "Reserved",
"Reserved"};
uint8_t opcode = (code >> 21) & 0xF;
if (opcode == 0x9)
m_operator = (code & (0x1 << 5)) ? "SMULWy" : "SMLAWy";
else
m_operator = Instructions[opcode];
if (!(opcode & (0x1 << 4)) && (code & (0x1 << 20)))
m_operator += 'S';
ParseArmCondition(code);
if ((opcode & 0xC) == 0x4 || opcode == 0xA)
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rn));
m_args.AddArgument(ArgRegister(Rm));
m_args.AddArgument(ArgRegister(Rs));
if ((opcode & 0xE) == 0x8 || opcode == 0x1)
m_args.AddArgument(ArgRegister(Rd));
}
else if ((code & (0x1 << 7)) && (code & (0x1 << 4)))
{
if (((code >> 23) & 0x3) == 0x2 && ((code >> 20) & 0x3) == 0x0
&& ((code >> 4) & 0xFF) == 0x09)
{
if (code & (0x1 << 22)) // SWPB
m_operator = "SWPB";
else // SWP
m_operator = "SWP";
ParseArmCondition(code);
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rm));
m_args.AddArgument(ArgRegister(Rn, false, false, true));
}
else
{
static const char* Instructions[] = {"Reserved", "STRH", "LDRD",
"STRD", "Reserved", "LDRH", "LDRSB", "LDRSH"};
m_operator = Instructions[((code >> 18) & 0x4)
| ((code >> 5) & 0x3)];
ParseArmCondition(code);
m_args.AddArgument(ArgRegister(Rd));
if (code & (0x1 << 22)) // immediate
{
m_args.AddArgument(ArgRelative(ArgRegister(Rn),
ArgImmediate(((code >> 4) & 0xF0) | (code & 0xF)),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
}
else
{
m_args.AddArgument(ArgRelative(ArgRegister(Rn),
ArgRegister(code & 0xF),
code & (0x1 << 24), code & (0x1 << 23),
code & (0x1 << 21)));
}
}
}
else if (((code >> 23) & 0x3) == 0x2)
{
if (!((code >> 20) & 0x1))
{
if (code & (0x1 << 21))
{
m_operator = "MSR";
m_args.AddArgument(ArgPsr(code & (0x1 << 22),
(code >> 16) & 0xF));
if (code & (0x1 << 25)) // immediate
{
m_args.AddArgument(ArgUImmediate(
ROR(code & 0xFF, (code >> 8) & 0xF)));
}
else
{
m_args.AddArgument(ArgRegister(Rm));
}
}
else
{
m_operator = "MRS";
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgPsr(code & (0x1 << 22)));
}
ParseArmCondition(code);
}
else
{
ParseArmDataProc(code);
}
}
else
{
ParseArmDataProc(code);
}
}
else
{
m_operator = "Unknown";
}
}
void Instruction::ParseArmDataProc (uint32_t code)
{
static const char* ops[] = {"AND", "EOR", "SUB", "RSB", "ADD", "ADC",
"SBC", "RSC", "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"};
uint8_t opcode = (code >> 21) & 0xF;
if (opcode < 0x8 || opcode > 0xB)
m_args.AddArgument(ArgRegister(Rd));
if (opcode != 0xD && opcode != 0xF)
m_args.AddArgument(ArgRegister(Rn));
if (code & (0x1 << 25)) // Immediate operand 2
{
/*if (code & (0xF << 8))
m_args.AddArgument(ArgShift(ArgImmediate(code & 0xFF),
ArgImmediate(((code >> 8) & 0xF) << 1), SHIFT_ROR, false));
else
m_args.AddArgument(ArgImmediate(code & 0xFF));*/
m_args.AddArgument(ArgUImmediate(
ROR(code & 0xFF, (code >> 7) & 0x1E)));
}
else
{
switch ((code >> 5) & 0x3)
{
case 0: // Logical Shift Left
if (code & (0x1 << 4)) // Shift by register
{
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgRegister(Rs), SHIFT_LSL, false));
}
else // Shift by immediate
{
if (code & (0x1F << 7))
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_LSL, false));
else
m_args.AddArgument(ArgRegister(Rm));
}
break;
case 1: // Logical Shift Right
if (code & (0x1 << 4)) // Shift by register
{
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgRegister(Rs), SHIFT_LSR, false));
}
else // Shift by immediate
{
if (code & (0x1F << 7))
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_LSR, false));
else
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate(32), SHIFT_LSR, false));
}
break;
case 2: // Arithmetic Shift Right
if (code & (0x1 << 4)) // Shift by register
{
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgRegister(Rs), SHIFT_ASR, false));
}
else // Shift by immediate
{
if (code & (0x1F << 7))
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_ASR, false));
else
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate(32), SHIFT_ASR, false));
}
break;
case 3: // ROtate Right
if (code & (0x1 << 4)) // Shift by register
{
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgRegister(Rs), SHIFT_ROR, false));
}
else // Shift by immediate
{
if (code & (0x1F << 7))
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate((code >> 7) & 0x1F), SHIFT_ROR, false));
else
m_args.AddArgument(ArgShift(ArgRegister(Rm),
ArgImmediate(1), SHIFT_RRX, false));
}
break;
}
}
m_operator = ops[opcode];
if (code & (0x1 << 20) && (opcode < 0x8 || opcode > 0xB))
{
m_operator += "S";
}
ParseArmCondition(code);
}
void Instruction::ParseArmCondition (uint32_t code)
{
static const char* Conditions[] = {"EQ", "NE", "CS", "CC", "MI", "PL",
"VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "", "NV"};
m_operator += Conditions[code >> 28];
}
#undef Rn
#undef Rd
#undef Rs
#undef Rm
#define Rb ((code >> 8) & 0x7)
#define Ro ((code >> 6) & 0x7)
#define Rs ((code >> 3) & 0x7)
#define Rd ((code ) & 0x7)
#define Imm (code & 0xFF)
#define Off ((code >> 6) & 0x1F)
#define HiRs ((code >> 3) & 0xF)
#define HiRd (((code & (0x1 << 7)) >> 4) | Rd)
void Instruction::ParseThumb (uint32_t offset, uint16_t code)
{
Clear ();
if ((code >> 12) == 0xB && ((code >> 9) & 0x3) == 0x2) // 1011x10
{
if (code & (0x1 << 11))
m_operator = "POP";
else
m_operator = "PUSH";
ArgMulRegisters argRegs(false);
for (register uint8_t n = 0; n < 8; ++n)
if (Imm & (0x1 << n))
argRegs.AddRegister(n);
if (code & (0x1 << 8))
{
if (code & (0x1 << 11))
argRegs.AddLastRegister(SPREG_PC);
else
argRegs.AddLastRegister(SPREG_LR);
}
m_args.AddArgument(argRegs);
}
else if ((code >> 11) == 0x9) // 01001
{
m_operator = "LDR";
m_args.AddArgument(ArgRegister(Rb));
m_args.AddArgument(ArgRelative(15, ArgImmediate(Imm << 2),
true, true, false));
}
else if ((code >> 12) == 0x8) // 1000
{
if (code & (0x1 << 11))
m_operator = "LDRH";
else
m_operator = "STRH";
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRelative(Rs, ArgImmediate(Off << 1),
true, true, false));
}
else if ((code >> 10) == 0x10) // 010000
{
static const char* Instructions[] = {"AND", "EOR", "LSL", "LSR", "ASR",
"ADC", "SBC", "ROR", "TST", "NEG", "CMP", "CMN", "ORR", "MUL", "BIC",
"MVN"};
m_operator = Instructions[(code >> 6) & 0xF];
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rs));
}
else if ((code >> 10) == 0x11) // 010001
{
switch ((code >> 8) & 0x3)
{
case 0x0: // ADD
m_operator = "ADD";
m_args.AddArgument(ArgRegister(HiRd));
m_args.AddArgument(ArgRegister(HiRs));
break;
case 0x1: // CMP
m_operator = "CMP";
m_args.AddArgument(ArgRegister(HiRd));
m_args.AddArgument(ArgRegister(HiRs));
break;
case 0x2:
if (HiRd != 8 || HiRs != 8) // MOV
{
m_operator = "MOV";
m_args.AddArgument(ArgRegister(HiRd));
m_args.AddArgument(ArgRegister(HiRs));
}
else
m_operator = "NOP";
break;
case 0x3:
if (code & (0x1 << 7)) // BLX
{
m_operator = "BLX";
m_args.AddArgument(ArgRegister(HiRs));
}
else // BX
{
m_operator = "BX";
m_args.AddArgument(ArgRegister(HiRs));
}
break;
}
}
else if ((code >> 13) == 0x1) // 001
{
static const char* Instructions[] = {"MOV", "CMP", "ADD", "SUB"};
m_operator = Instructions[(code >> 11) & 0x3];
m_args.AddArgument(ArgRegister(Rb));
m_args.AddArgument(ArgImmediate(Imm));
}
else if ((code >> 13) == 0x3) // 011
{
static const char* Instructions[] = {"STR", "LDR", "STRB", "LDRB"};
m_operator = Instructions[(code >> 11) & 0x3];
m_args.AddArgument(ArgRegister(Rd));
if (code & (0x1 << 12))
m_args.AddArgument(ArgRelative(Rs, ArgImmediate(Off), true,
true, false));
else
m_args.AddArgument(ArgRelative(Rs, ArgImmediate(Off << 2), true,
true, false));
}
else if ((code >> 12) == 0xC) // 1100
{
if (code & (0x1 << 11))
m_operator = "LDMIA";
else
m_operator = "STMIA";
m_args.AddArgument(ArgRegister(Rb, true));
ArgMulRegisters argRegs(false);
for (register uint8_t n = 0; n < 8; ++n)
if (Imm & (0x1 << n))
argRegs.AddRegister(n);
m_args.AddArgument(argRegs);
}
else if ((code >> 13) == 0x0) // 000
{
if ((code >> 11) == 0x3) // 00011
{
if ((code >> 9) & 0x1)
m_operator = "SUB";
else
m_operator = "ADD";
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rs));
if ((code >> 10) & 0x1) // imm
m_args.AddArgument(ArgImmediate(Ro));
else // reg
m_args.AddArgument(ArgRegister(Ro));
}
else // 000
{
static const char* Instructions[] = {"LSL", "LSR", "ASR", "Reserved"};
m_operator = Instructions[(code >> 11) & 0x3];
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRegister(Rs));
m_args.AddArgument(ArgImmediate(Off));
}
}
else if ((code >> 11) == 0x1E) // 11110
{
m_operator = "BL.W1";
m_args.AddArgument(ArgImmediate(offset + 4 + ((code & 0x7FF) << 12)));
}
else if ((code >> 13) == 0x7 && (code & (0x1 << 11))) // 111x1
{
m_operator = "BL.W2";
m_args.AddArgument(ArgImmediate((code & 0x7FF) << 1));
}
else if ((code >> 11) == 0x1C) // 11100
{
m_operator = "B";
if (code & (0x1 << 10))
m_args.AddArgument(ArgUImmediate(offset + 4 +
((int32_t)(((code & 0x3FF) << 1) | 0xFFFFF800))));
else
m_args.AddArgument(ArgUImmediate(offset + 4 + ((code & 0x3FF) << 1)));
}
else if ((code >> 12) == 0xD) // 1101
{
if (((code >> 8) & 0xF) == 0xF) // 11011111
{
m_operator = "SWI";
m_args.AddArgument(ArgImmediate(code & 0xFF));
}
else // 1101
{
static const char* Conditions[] = {"EQ", "NE", "CS", "CC", "MI", "PL",
"VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "__", "**"};
m_operator = "B";
m_operator += Conditions[(code >> 8) & 0xF];
m_args.AddArgument(ArgUImmediate(offset + 4 +
(((int32_t)(int8_t)Imm) << 1)));
}
}
else if ((code >> 8) == 0xB0) // 10110000
{
m_operator = "ADD";
m_args.AddArgument(ArgRegister(13, false, true));
if (code & (0x1 << 7)) // substract
m_args.AddArgument(ArgImmediate(-((code & 0x7F) << 2)));
else // add
m_args.AddArgument(ArgImmediate((code & 0x7F) << 2));
}
else if ((code >> 12) == 0x5) // 0101
{
if (code & (0x1 << 11))
m_operator = "LDR";
else
m_operator = "STR";
if (code & (0x1 << 10))
m_operator += 'B';
m_args.AddArgument(ArgRegister(Rd));
m_args.AddArgument(ArgRelative(Rs, ArgRegister(Ro), true, true, false));
}
else if ((code >> 12) == 0x9) // 1001
{
if (code & (0x1 << 11))
m_operator = "LDR";
else
m_operator = "STR";
m_args.AddArgument(ArgRegister(Rb));
m_args.AddArgument(ArgRelative(ArgRegister(13, false, true),
ArgImmediate(Imm << 2), true, true, false));
}
else if ((code >> 12) == 0xA) // 1010
{
m_operator = "ADD";
m_args.AddArgument(ArgRegister(Rb));
if (code & (0x1 << 11)) // with SP
m_args.AddArgument(ArgRegister(13, false, true));
else // with PC
m_args.AddArgument(ArgRegister(15, false, true));
m_args.AddArgument(ArgImmediate(Imm << 2));
}
else
{
m_operator = "Unknown";
}
}
#undef Rb
#undef Ro
#undef Rs
#undef Rd
#undef Imm
#undef Off
#undef HiRs
#undef HiRd
}
}

View File

@ -1,317 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/dma.hpp"
#include "ameteor/io.hpp"
#include "ameteor/memory.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
#include <sstream>
namespace AMeteor
{
void Dma::Reset ()
{
for (Channel* chan = m_chans; chan < m_chans+4; ++chan)
{
chan->src = 0;
chan->dest = 0;
chan->count = 0;
chan->control.w = 0;
}
m_graphic = 0;
}
void Dma::UpdateCnt (uint8_t channum)
{
Channel& chan = m_chans[channum];
if (chan.control.w !=
IO.DRead16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE))
{
if (!chan.control.b.enable &&
(IO.DRead16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE)
& (0x1 << 15)))
// if we changed enable from 0 to 1
{
chan.dest = IO.DRead32(Io::DMA0DAD + channum * Io::DMA_CHANSIZE);
if (channum == 3)
chan.dest &= 0x0FFFFFFF;
else
chan.dest &= 0x07FFFFFF;
chan.src = IO.DRead32(Io::DMA0SAD + channum * Io::DMA_CHANSIZE);
if (channum == 0)
chan.src &= 0x07FFFFFF;
else
chan.src &= 0x0FFFFFFF;
chan.count = chan.reload;
if (channum != 3)
chan.count &= 0x3FFF;
chan.control.w =
IO.DRead16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE);
Check(channum, Immediately);
}
else
chan.control.w =
IO.DRead16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE);
if (chan.control.b.start == Special)
{
switch (channum)
{
case 0:
met_abort("prohibited !");
break;
case 1:
case 2:
// sound dma
if (chan.dest != 0x040000A0 && chan.dest != 0x040000A4)
met_abort("Special DMA 1 or 2 with unauthorized address : "
<< IOS_ADD << chan.dest);
if (!chan.control.b.repeat)
met_abort("Special DMA 1 or 2 without repeat");
// 4 words of 32 bits
chan.count = 4;
chan.control.b.type = 1;
// no increment
chan.control.b.dest = 2;
break;
case 3:
met_abort("not implemented");
break;
}
}
}
}
void Dma::Check (uint8_t channum, uint8_t reason)
{
register Channel::Control cnt = m_chans[channum].control;
if (cnt.b.enable &&
cnt.b.start == reason)
Process(channum);
}
void Dma::Process (uint8_t channum)
{
Channel& chan = m_chans[channum];
int8_t s_inc, d_inc;
s_inc = d_inc = 2; // increment or increment reload
if (chan.control.b.src == 1) // decrement
s_inc = -2;
else if (chan.control.b.src == 2) // fixed
s_inc = 0;
else if (chan.control.b.src == 3)
met_abort("prohibited");
if (chan.control.b.dest == 1)
d_inc = -2;
else if (chan.control.b.dest == 2)
d_inc = 0;
//else if (chan.control.b.dest == 3)
// same as 0, but we do something at the end
if (chan.control.b.type) // 32 bits transfer
{
s_inc <<= 1;
d_inc <<= 1;
}
if (chan.count == 0)
{
if (channum != 3)
chan.count = 0x4000;
// 0x10000 doesn't fill in 16 bits, we treat this case in the Copy() call
}
//printf("DMA%hhu from %08X to %08X of %hu %s\n", channum, chan.src, chan.dest, chan.count, chan.control.b.type ? "words":"halfwords");
//if (i > debut)
debug ("DMA" << IOS_NOR << (int)channum << ", from " << IOS_ADD << chan.src
<< " to " << IOS_ADD << chan.dest
<< " of " << IOS_NOR << (chan.count ? chan.count : 0x10000)
<< (chan.control.b.type ? " words" : " halfwords"));
if (traceenabled)
{
std::stringstream ss;
ss << "DMA" << IOS_NOR << (int)channum << ", from " << IOS_ADD << chan.src
<< " to " << IOS_ADD << chan.dest
<< " of " << IOS_NOR << (chan.count ? chan.count : 0x10000)
<< (chan.control.b.type ? " words" : " halfwords");
trace_bizhawk(ss.str());
}
#if 0
if (channum == 3 && (chan.dest >> 24) == 0x0D || (chan.src >> 24) == 0x0D)
{
if (chan.control.b.type)
met_abort("Word copy for EEPROM DMA3");
if (d_inc != 2 || s_inc != 2)
met_abort("Source or destination not incremeting in EEPROM DMA3");
if ((chan.dest >> 24) == 0x0D)
MEM.WriteEepromDma(chan.src, chan.count ? chan.count : 0x10000);
else if ((chan.src >> 24) == 0x0D)
MEM.ReadEepromDma(chan.dest, chan.count ? chan.count : 0x10000);
chan.src += chan.count * 2;
chan.dest += chan.count * 2;
}
else
#endif
if (channum == 3 && (chan.dest >> 24) == 0x0D)
{
if (chan.control.b.type)
met_abort("Word copy for EEPROM DMA3");
if (d_inc != 2 || s_inc != 2)
met_abort("Source or destination not incremeting in EEPROM DMA3");
MEM.WriteEepromDma(chan.src, chan.count);
chan.src += chan.count * 2;
chan.dest += chan.count * 2;
}
else
Copy(chan.src, chan.dest, s_inc, d_inc,
chan.count ? chan.count : 0x10000, chan.control.b.type);
if (chan.control.b.type)
{
CYCLES32NSeq(chan.src, chan.count);
CYCLES32NSeq(chan.dest, chan.count);
ICYCLES(2);
}
else
{
CYCLES16NSeq(chan.src, chan.count);
CYCLES16NSeq(chan.dest, chan.count);
ICYCLES(2);
}
uint32_t d = chan.dest >> 24;
uint32_t s = chan.src >> 24;
if (d >= 0x08 && d <= 0x0D && s >= 0x08 && s <= 0x0D)
// if both source and destination are in GamePak
ICYCLES(2);
if (chan.control.b.irq)
CPU.SendInterrupt(0x1 << (8 + channum));
if (chan.control.b.dest == 3)
{
chan.dest = IO.DRead32(Io::DMA0DAD + channum * Io::DMA_CHANSIZE);
if (channum == 3)
chan.dest &= 0x0FFFFFFF;
else
chan.dest &= 0x07FFFFFF;
}
// if repeat but not sound dma
//if (!((channum == 1 || channum == 2) && chan.control.b.start == Special) && chan.control.b.repeat)
if (((channum != 1 && channum != 2) || chan.control.b.start != Special)
&& chan.control.b.repeat)
{
chan.count = chan.reload;
}
if (!chan.control.b.repeat || chan.control.b.start == Immediately)
{
chan.control.b.enable = 0;
IO.GetRef16(Io::DMA0CNT_H + channum * Io::DMA_CHANSIZE) &= 0x7FFF;
}
}
void Dma::Copy (uint32_t& src, uint32_t& dest, int8_t s_inc, int8_t d_inc,
uint32_t count, bool word)
{
uint32_t basedest = dest;
if (word)
{
src &= 0xFFFFFFFC;
dest &= 0xFFFFFFFC;
}
else
{
src &= 0xFFFFFFFE;
dest &= 0xFFFFFFFE;
}
// sound
if (dest == 0x040000A0)
{
SOUND.SendDigitalA((uint8_t*)MEM.GetRealAddress(src));
src += 4*4;
if (d_inc != 0)
met_abort("dinc != 0 on dma sound, should not happen");
return;
}
if (dest == 0x040000A4)
{
SOUND.SendDigitalB((uint8_t*)MEM.GetRealAddress(src));
src += 4*4;
if (d_inc != 0)
met_abort("dinc != 0 on dma sound, should not happen");
return;
}
if ((dest >> 24) >= 0x05 &&
(dest >> 24) <= 0x07)
m_graphic = true;
if (word)
{
for (uint32_t cur = 0; cur < count; ++cur)
{
MEM.Write32(dest, MEM.Read32(src));
src += s_inc;
dest += d_inc;
}
}
else
{
for (uint32_t cur = 0; cur < count; ++cur)
{
MEM.Write16(dest, MEM.Read16(src));
src += s_inc;
dest += d_inc;
}
}
m_graphic = false;
//if ((basedest >> 24) == 0x07)
// LCD.OamWrite(basedest, dest);
}
bool Dma::SaveState (std::ostream& stream)
{
SS_WRITE_ARRAY(m_chans);
// no need to save or load m_graphic since we shouldn't save or load during
// a dma
return true;
}
bool Dma::LoadState (std::istream& stream)
{
SS_READ_ARRAY(m_chans);
return true;
}
}

View File

@ -1,272 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/eeprom.hpp"
#include "globals.hpp"
#include "debug.hpp"
#include <cstring>
namespace AMeteor
{
Eeprom::Eeprom (bool big) :
CartMem(),
m_state(IDLE),
m_add(0),
m_pos(0)
{
if (big)
m_size = 0x2000;
else
m_size = 0x0200;
*(uint32_t*)(m_data+MAX_SIZE) = m_size;
}
void Eeprom::Reset ()
{
std::memset(m_data, 0, m_size);
}
bool Eeprom::Load (std::istream& f)
{
f.read((char*)m_data, m_size);
return f.good();
}
bool Eeprom::Save (std::ostream& f)
{
f.write((char*)m_data, m_size);
return f.good();
}
uint8_t Eeprom::Read (uint16_t MET_UNUSED(add))
{
met_abort("8 bits write to EEPROM");
return 0;
}
uint16_t Eeprom::Read ()
{
switch (m_state)
{
case READ_GARBAGE:
++m_pos;
if (m_pos == 4)
{
m_pos = 0;
m_state = READ_DATA;
}
return 0;
case READ_DATA:
{
uint16_t ret =
(m_data[m_add + m_pos / 8] & (0x1 << (7 - (m_pos % 8)))) ? 1 : 0;
++m_pos;
if (m_pos == 64)
m_state = IDLE;
return ret;
}
default:
return 1;
}
}
bool Eeprom::Write (uint16_t MET_UNUSED(add), uint8_t MET_UNUSED(val))
{
met_abort("8 bits write to EEPROM");
return false;
}
//XXX
#if 0
bool Eeprom::Write (uint16_t val)
{
switch (m_state)
{
case IDLE:
if (val & 0x1)
m_state = WAITING;
else
met_abort("First bit is not 1");
return false;
case WAITING:
m_add = 0;
m_pos = 0;
if (val & 0x1)
m_state = READ_ADD;
else
m_state = WRITE_ADD;
return false;
case READ_ADD:
m_add <<= 1;
m_add |= val & 0x1;
++m_pos;
if (m_size == 0x0200 && m_pos == 6 ||
m_size == 0x2000 && m_pos == 14)
{
m_state = READ_END;
if (m_size == 0x2000 && (m_add & 0x3C000))
met_abort("In large EEPROM, 4 upper address bits are not 0");
}
return false;
case READ_END:
if (val & 0x1)
met_abort("Last bit of EEPROM read request is not 0");
m_pos = 0;
m_state = READ_GARBAGE;
return false;
case WRITE_ADD:
m_add <<= 1;
m_add |= val & 0x1;
++m_pos;
if (m_size == 0x0200 && m_pos == 6 ||
m_size == 0x2000 && m_pos == 14)
{
m_state = WRITE_DATA;
m_pos = 0;
if (m_size == 0x2000 && (m_add & 0x3C000))
met_abort("In large EEPROM, 4 upper address bits are not 0");
}
return false;
case WRITE_DATA:
{
uint8_t& d = m_data[m_add + m_pos / 8];
d <<= 1;
d |= val & 0x1;
}
++m_pos;
if (m_pos == 64)
m_state = WRITE_END;
return true;
case WRITE_END:
if (val & 0x1)
met_abort("Last bit of EEPROM write request is not 0");
return false;
}
}
#endif
bool Eeprom::Write (uint16_t* pData, uint16_t size)
{
if (!(*pData & 0x1))
met_abort("Bit 1 is not 1 in EEPROM DMA");
++pData;
uint16_t add = 0;
if (*pData & 0x1) // read
{
if (size != 9 && size != 17)
met_abort("Invalid size for read");
++pData;
// read address
for (uint8_t i = 0, end = (m_size == 0x0200) ? 6 : 14; i < end;
++i, ++pData)
add = (add << 1) | (*pData & 0x1);
if (m_size == 0x2000 && (add & 0x3C00))
met_abort("In large EEPROM, 4 upper address bits are not 0");
//XXX
/*if (*pData & 0x1)
met_abort("Last bit of EEPROM read request is not 0");*/
m_add = add*8;
m_state = READ_GARBAGE;
m_pos = 0;
return false;
}
else // write
{
if (size != 73 && size != 81)
met_abort("Invalid size for write");
++pData;
// read address
for (uint8_t i = 0, end = (m_size == 0x0200) ? 6 : 14; i < end;
++i, ++pData)
add = (add << 1) | (*pData & 0x1);
if (m_size == 0x2000 && (add & 0x3C00))
met_abort("In large EEPROM, 4 upper address bits are not 0");
// read data
uint8_t* pMem = m_data + add*8;
for (uint8_t i = 0; i < 8; ++i, ++pMem)
{
for (uint8_t j = 0; j < 8; ++j, ++pData)
{
*pMem <<= 1;
*pMem |= (*pData & 0x1);
}
}
if (*pData & 0x1)
met_abort("Last bit of EEPROM write request is not 0");
m_state = IDLE;
return true;
}
}
//XXX
#if 0
void Eeprom::Read (uint16_t* pOut)
{
if (m_state != READ)
met_abort("Read in invalid EEPROM state");
pOut += 4; // ignore these
uint8_t* pData = m_data + m_add;
uint8_t cur;
for (uint8_t i = 0; i < 8; ++i, ++pData)
{
cur = *pData;
pOut += 7;
for (uint8_t j = 0; j < 8; ++j, --pOut, cur >>= 1)
*pOut = cur & 0x1;
pOut += 9;
}
m_state = NORMAL;
}
#endif
bool Eeprom::SaveState (std::ostream& stream)
{
//XXX TODO
SS_WRITE_VAR(m_size);
SS_WRITE_VAR(m_state);
SS_WRITE_VAR(m_add);
SS_WRITE_DATA(m_data, m_size);
return true;
}
bool Eeprom::LoadState (std::istream& stream)
{
SS_READ_VAR(m_size);
SS_READ_VAR(m_state);
SS_READ_VAR(m_add);
SS_READ_DATA(m_data, m_size);
return true;
}
}

View File

@ -1,178 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/flash.hpp"
#include "globals.hpp"
#include <cstring>
#include <fstream>
namespace AMeteor
{
Flash::Flash (bool big) :
CartMem(),
m_state(NORMAL)
{
if (big)
{
m_device_id = 0x13;
m_manufacturer_id = 0x62;
m_size = 128*1024;
}
else
{
m_device_id = 0x1B;
m_manufacturer_id = 0x32;
m_size = 64*1024;
}
*(uint32_t*)(m_data+MAX_SIZE) = m_size;
}
void Flash::Reset ()
{
std::memset(m_data, 0, m_size);
}
bool Flash::Load (std::istream& f)
{
f.read((char*)m_data, m_size);
return f.good();
}
bool Flash::Save (std::ostream& f)
{
f.write((char*)m_data, m_size);
return f.good();
}
uint8_t Flash::Read (uint16_t add)
{
switch (m_state)
{
case NORMAL:
return m_data[add];
case ID:
switch (add)
{
case 0: return m_manufacturer_id;
case 1: return m_device_id;
default: return 0;
}
// FIXME vba returns 0xff after an erase regardless of the read address
default:
return 0;
}
}
bool Flash::Write (uint16_t add, uint8_t val)
{
switch (m_state)
{
case ID:
case NORMAL:
if (add == 0x5555 && val == 0xAA)
m_state = CMD1;
else
m_state = NORMAL; // for case ID
break;
case CMD1:
if (add == 0x2AAA && val == 0x55)
m_state = CMD2;
else
m_state = NORMAL;
break;
case CMD2:
if (add == 0x5555)
switch (val)
{
case 0x80: // erase mode
m_state = ERASE1;
break;
case 0x90: // id mode
m_state = ID;
break;
case 0xA0: // write byte
m_state = WRITE;
break;
case 0xF0:
m_state = NORMAL;
break;
default:
m_state = NORMAL;
break;
}
else
m_state = NORMAL;
break;
case ERASE1:
if (add == 0x5555 && val == 0xAA)
m_state = ERASE2;
else
m_state = NORMAL;
break;
case ERASE2:
if (add == 0x2AAA && val == 0x55)
m_state = ERASE3;
else
m_state = NORMAL;
break;
case ERASE3:
// according to vba, after a whole erase command we can juste repeat the
// last byte of the command to execute another erase command
switch (val)
{
case 0x10: // erase entire chip
if (add == 0x5555)
memset(m_data, 0xFF, m_size);
m_state = NORMAL;
break;
case 0x30: // erase sector
if (!(add & 0x0FFF))
memset(m_data+add, 0xFF, 0x1000);
m_state = NORMAL;
break;
default:
m_state = NORMAL;
break;
}
break;
case WRITE:
// I think this is how it works
m_data[add] &= val;
m_state = NORMAL;
return true;
}
return false;
}
bool Flash::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_state);
SS_WRITE_DATA(m_data, m_size);
return true;
}
bool Flash::LoadState (std::istream& stream)
{
SS_READ_VAR(m_state);
SS_READ_DATA(m_data, m_size);
return true;
}
}

View File

@ -1,140 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef __GLOBALS_H__
#define __GLOBALS_H__
#include <algorithm>
#include <stdint.h>
#define SPDUP_FRMSKIP 9
#define SPDUP_SNDSKIP 3969
#define R(reg) CPU.Reg(reg)
#define CPU _cpu
#define MEM _memory
#define IO _io
#define DMA _dma
#define LCD _lcd
#define SOUND _sound
#define KEYPAD _keypad
#define CLOCK _clock
#define TIMER0 _timer0
#define TIMER1 _timer1
#define TIMER2 _timer2
#define TIMER3 _timer3
#define CYCLES16NSeq(add, count) \
CLOCK.TimePass(MEM.GetCycles16NoSeq(add, count))
#define CYCLES16Seq(add, count) \
CLOCK.TimePass(MEM.GetCycles16Seq(add, count))
#define CYCLES32NSeq(add, count) \
CLOCK.TimePass(MEM.GetCycles32NoSeq(add, count))
#define CYCLES32Seq(add, count) \
CLOCK.TimePass(MEM.GetCycles32Seq(add, count))
#define ICYCLES(i) \
CLOCK.TimePass(i)
// we don't mind if the following code is slow since a MUL
// instruction is slow on a GBA too
#define MULICYCLES(reg) \
{ \
uint32_t rs = R(reg) >> 8; \
for (uint8_t i = 1; i <= 4; ++i, rs >>= 8) \
{ \
if (!rs || rs == (0xFFFFFFFF >> (i*8))) \
{ \
ICYCLES(i); \
break; \
} \
} \
}
#define CPSR (CPU.Cpsr().dw)
#define ICPSR (CPU.ICpsr())
#define SPSR (CPU.Spsr().dw)
#define FLAG_Z (CPU.ICpsr().f_zero)
#define FLAG_N (CPU.ICpsr().f_sign)
#define FLAG_C (CPU.ICpsr().f_carry)
#define FLAG_V (CPU.ICpsr().f_overflow)
#define FLAG_T (CPU.ICpsr().thumb)
#define SETF(flag, val) \
FLAG_##flag = (val) ? 1 : 0
#define SETFB(flag, val) \
FLAG_##flag = (val)
#define FZ(val) \
SETF(Z, !(val))
#define FN(val) \
SETF(N, NEG(val))
#define POS(a) ((~a) >> 31)
#define NEG(a) ((a) >> 31)
// inspired by VisualBoyAdvance
#define ADDCARRY(a, b, c) \
((NEG(a) & NEG(b)) | \
(NEG(a) & POS(c)) | \
(NEG(b) & POS(c)))
#define ADDOVERFLOW(a, b, c) \
((NEG(a) & NEG(b) & POS(c)) | \
(POS(a) & POS(b) & NEG(c)))
#define SUBCARRY(a, b, c) \
((NEG(a) & POS(b)) | \
(NEG(a) & POS(c)) | \
(POS(b) & POS(c)))
#define SUBOVERFLOW(a, b, c) \
((NEG(a) & POS(b) & POS(c)) | \
(POS(a) & NEG(b) & NEG(c)))
// Save states macros
#define SS_WRITE_VAR(var) \
if (!stream.write((char*)&var, sizeof(var))) \
return false
#define SS_WRITE_ARRAY(var) \
if (!stream.write((char*)var, sizeof(var))) \
return false
#define SS_WRITE_DATA(var, size) \
if (!stream.write((char*)var, size)) \
return false
#define SS_READ_VAR(var) \
if (!stream.read((char*)&var, sizeof(var))) \
return false
#define SS_READ_ARRAY(var) \
if (!stream.read((char*)var, sizeof(var))) \
return false
#define SS_READ_DATA(var, size) \
if (!stream.read((char*)var, size)) \
return false
// macro to avoid getting warnings about and unused parameter on GCC
#ifdef _MSC_VER
#define MET_UNUSED(v)
#else
#define MET_UNUSED(v) \
__attribute__((unused)) MET_UNUSED_##v
#endif
namespace AMeteor
{
// ROtate Right
inline uint32_t ROR(uint32_t val, uint8_t shift)
{
return (val >> shift) | (val << (32 - shift));
}
}
#endif

View File

@ -1,475 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/bglayer.hpp"
#include "ameteor/graphics/screen.hpp"
#include "../debug.hpp"
namespace AMeteor
{
namespace Graphics
{
BgLayer::BgLayer (int8_t num, Memory& memory, Io& io, uint16_t* pPalette) :
m_memory(memory),
m_io(io),
m_num(num),
m_priority(0),
m_cnt(0),
m_xoff(0),
m_yoff(0),
m_tWidth(32),
m_tHeight(32),
m_rWidth(16),
m_rHeight(16),
m_mapAdd(0x06000000),
m_charAdd(0x06000000),
m_pPalette(pPalette)
{
}
BgLayer::~BgLayer ()
{
}
void BgLayer::DrawLine0 (uint8_t line, uint16_t* ptr)
{
uint8_t mosH;
if (m_cnt & (0x1 << 6))
{
mosH = m_io.DRead8(Io::MOSAIC);
uint8_t mosV = mosH >> 4;
mosH &= 0x0F;
++mosV;
if (mosH) // let it be 0 if it's 0 (=> no mosaic)
++mosH;
line /= mosV;
line *= mosV;
}
else
mosH = 0;
uint16_t* pMap = (uint16_t*)m_memory.GetRealAddress(m_mapAdd), *pTile;
uint8_t* pChar = m_memory.GetRealAddress(m_charAdd), *tpChar;
uint8_t i = 0;
// theses are the coordinates on the layer from which we will draw a line
uint16_t xoff = m_xoff % (m_tWidth * 8);
uint16_t yoff = (m_yoff + line) % (m_tHeight * 8);
// theses are the coordinates on the tile of the pixels we are drawing
// NOTE : if the tile is horizontally flipped tileX = 8 - tileX, thus
// when we draw it is ALWAYS incremented no matter how the tile is
// flipped
// NOTE : tileY is NOT redefined if the tile is flipped vertically
int8_t tileX = xoff % 8, tileY;
bool flipH;
// sets pTile to the tile we must draw first
// +---+---+
// | 1 | 2 |
// +---+---+
// | 3 | 4 |
// +---+---+
if (yoff >= 256)
if (m_tWidth == 64)
if (xoff >= 256)
// zone 4
pTile = pMap + 32*32 + 32*32 + 32*32
+ 32 * ((yoff-256)/8) + (xoff-256)/8;
else
// zone 3 (large map)
pTile = pMap + 32*32 + 32*32 + 32 * ((yoff-256)/8) + xoff/8;
else
// zone 3 (no large map)
pTile = pMap + 32*32 + 32 * ((yoff-256)/8) + xoff/8;
else
if (xoff >= 256)
// zone 2
pTile = pMap + 32*32 + 32 * (yoff/8) + (xoff-256)/8;
else
// zone 1
pTile = pMap + 32 * (yoff/8) + xoff/8;
if (m_hicolor)
{
while (i < 240)
{
flipH = (bool)(*pTile & (0x1 << 10));
// if the tile is vertically fliped
if (*pTile & (0x1 << 11))
tileY = 7 - (yoff % 8);
else
tileY = yoff % 8;
if (flipH)
tpChar = pChar + (*pTile & 0x3FF)*8*8 + tileY*8 + 7 - tileX;
else
tpChar = pChar + (*pTile & 0x3FF)*8*8 + tileY*8 + tileX;
while (tileX < 8)
{
if (mosH && i % mosH)
*ptr = ptr[-1];
else
{
if (*tpChar)
*ptr = m_pPalette[*tpChar] | 0x8000;
else
*ptr = 0x0;
}
if (flipH)
--tpChar;
else
++tpChar;
++ptr;
++tileX;
++i;
if (i == 240)
return;
}
tileX = 0;
++pTile;
// support for big maps and wrap around
if (!((pTile - pMap) % 32))
if (m_tWidth == 32)
pTile -= 32;
else
if (yoff >= 256 && pTile - pMap > 32*32 * 3
|| yoff < 256 && pTile - pMap > 32*32)
pTile -= 32*33;
else
pTile += 32*31;
}
}
else
{
uint16_t* pPalette;
uint8_t colorInd;
// for each pixel of the line we are drawing
while (i < 240)
{
pPalette = m_pPalette + ((*pTile >> 12) & 0xF) * 16;
flipH = (bool)(*pTile & (0x1 << 10));
// if the tile is vertically fliped
if (*pTile & (0x1 << 11))
tileY = 7 - (yoff % 8);
else
tileY = yoff % 8;
if (flipH)
tpChar = pChar + ((*pTile & 0x3FF)*8*8 + tileY*8 + 7 - tileX)/2;
else
tpChar = pChar + ((*pTile & 0x3FF)*8*8 + tileY*8 + tileX)/2;
// we draw until the end of the tile or the line
while (tileX < 8)
{
if (flipH)
if (tileX % 2)
{
colorInd = *tpChar & 0xF;
--tpChar;
}
else
colorInd = *tpChar >> 4;
else
if (tileX % 2)
{
colorInd = *tpChar >> 4;
++tpChar;
}
else
colorInd = *tpChar & 0xF;
if (mosH && i % mosH)
*ptr = ptr[-1];
else
{
if (colorInd)
*ptr = pPalette[colorInd] | 0x8000;
else
*ptr = 0x0;
}
++ptr;
++tileX;
++i;
// if this was the last pixel of the line
if (i == 240)
return;
}
// we have finished drawing a tile, so we go to the next tile
tileX = 0;
++pTile;
// support for big maps and wrap around
if (!((pTile - pMap) % 32))
if (m_tWidth == 32)
pTile -= 32;
else
if (yoff >= 256 && pTile - pMap > 32*32 * 3
|| yoff < 256 && pTile - pMap > 32*32)
pTile -= 32*33;
else
pTile += 32*31;
}
}
}
void BgLayer::DrawLine2 (uint16_t* ptr,
int32_t curX, int32_t curY,
int16_t dx, int16_t dy)
{
if (!m_hicolor)
{
//FIXME is this possible ??
//this seems to be impossible, but we should not crash since some games
//do it
//puts("rotated layer with 16 colors");
//abort();
// XXX
//it seems now that we should not care about this flag, we draw in 256
//colors, that's all
//return;
}
int32_t intX, intY;
uint16_t colorInd;
uint8_t* pMap = (uint8_t*)m_memory.GetRealAddress(m_mapAdd);
uint8_t* pChar = m_memory.GetRealAddress(m_charAdd);
for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy)
{
intX = curX >> 8;
intY = curY >> 8;
// if we are off layer
if (intX < 0 || intX >= m_rWidth*8)
if (m_cnt & (0x1 << 13))
{
// NOTE : in C++, the modulus can be negative, this is because in
// x86, the IDIV instruction gives a remainder of the same sign of
// the dividend
curX %= m_rWidth*8 << 8;
if (curX < 0)
curX += m_rWidth*8 << 8;
intX = curX >> 8;
}
else
continue;
if (intY < 0 || intY >= m_rHeight*8)
if (m_cnt & (0x1 << 13))
{
curY %= m_rHeight*8 << 8;
if (curY < 0)
curY += m_rHeight*8 << 8;
intY = curY >> 8;
}
else
continue;
colorInd = pChar[pMap[intY / 8 * m_rWidth + intX / 8] * 8 * 8
+ (intY % 8) * 8 + intX % 8];
if (colorInd)
*ptr = m_pPalette[colorInd] | 0x8000;
else
*ptr = 0x0;
}
}
void BgLayer::DrawLine3 (uint16_t* ptr,
int32_t curX, int32_t curY,
int16_t dx, int16_t dy)
{
int32_t intX, intY;
uint16_t* pChar = (uint16_t*) m_memory.GetRealAddress(0x06000000);
for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy)
{
intX = curX >> 8;
intY = curY >> 8;
// if we are off layer
if (intX < 0 || intX >= 240)
/*
if (m_cnt & (0x1 << 13))
{
// NOTE : in C++, the modulus can be negative
intX %= 240;
if (intX < 0)
intX += 240;
}
else*/
continue;
if (intY < 0 || intY >= 160)
/*
if (m_cnt & (0x1 << 13))
{
intY %= 160;
if (intY < 0)
intY += 160;
}
else*/
continue;
*ptr = pChar[intY * 240 + intX] | 0x8000;
}
}
void BgLayer::DrawLine4 (uint8_t line, uint16_t* ptr,
int32_t curX, int32_t curY,
int16_t dx, int16_t dmx, int16_t dy, int16_t dmy, bool frame1)
{
uint8_t mosH;
if (m_cnt & (0x1 << 6))
{
// TODO horizontal mosaic not implemented
mosH = m_io.DRead8(Io::MOSAIC);
uint8_t mosV = mosH >> 4;
mosH &= 0x0F;
++mosV;
if (mosH) // let it be 0 if it's 0 (=> no mosaic)
++mosH;
uint8_t back = line % mosV;
curX -= back*dmx;
curY -= back*dmy;
}
else
mosH = 0;
int32_t intX, intY;
uint16_t colorInd;
uint8_t* pChar =
m_memory.GetRealAddress(frame1 ? 0x0600A000 : 0x06000000);
for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy)
{
if (mosH && x % mosH)
{
*ptr = ptr[-1];
continue;
}
intX = curX >> 8;
intY = curY >> 8;
// if we are off layer
if (intX < 0 || intX >= 240)
if (m_cnt & (0x1 << 13))
{
// NOTE : in C++, the modulus can be negative
intX %= 240;
if (intX < 0)
intX += 240;
}
else
continue;
if (intY < 0 || intY >= 160)
if (m_cnt & (0x1 << 13))
{
intY %= 160;
if (intY < 0)
intY += 160;
}
else
continue;
colorInd = pChar[intY * 240 + intX];
if (colorInd)
*ptr = m_pPalette[colorInd] | 0x8000;
else
*ptr = 0x0;
}
}
void BgLayer::DrawLine5 (uint16_t* ptr,
int32_t curX, int32_t curY,
int16_t dx, int16_t dy, bool frame1)
{
int32_t intX, intY;
uint16_t* pChar = (uint16_t*) m_memory.GetRealAddress(frame1 ? 0x0600A000 : 0x06000000);
for (uint8_t x = 0; x < 240; ++x, ++ptr, curX += dx, curY += dy)
{
intX = curX >> 8;
intY = curY >> 8;
// if we are off layer
if (intX < 0 || intX >= 160)
continue;
if (intY < 0 || intY >= 128)
continue;
*ptr = pChar[intY * 160 + intX] | 0x8000;
}
}
void BgLayer::UpdateCnt (uint16_t cnt)
{
if (m_cnt == cnt)
return;
switch (cnt >> 14)
{
case 0:
m_tWidth = m_tHeight = 32;
m_rWidth = m_rHeight = 16;
break;
case 1:
m_tWidth = 64;
m_tHeight = 32;
m_rWidth = m_rHeight = 32;
break;
case 2:
m_tWidth = 32;
m_tHeight = 64;
m_rWidth = m_rHeight = 64;
break;
case 3:
m_tWidth = m_tHeight = 64;
m_rWidth = m_rHeight = 128;
break;
}
m_priority = (cnt & 0x3);
m_charAdd = 0x06000000 + ((cnt >> 2) & 0x3) * 0x4000;
m_hicolor = cnt & (0x1 << 7);
m_mapAdd = 0x06000000 + ((cnt >> 8) & 0x1F) * 0x0800;
m_cnt = cnt;
}
}
}

View File

@ -1,698 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/object.hpp"
#include "ameteor/graphics/screen.hpp"
#include "../debug.hpp"
namespace AMeteor
{
namespace Graphics
{
Object::Object (uint16_t* pPalette, uint8_t* pChar) :
m_attr0(0),
m_attr1(0),
m_attr2(0),
m_width(1),
m_height(1),
m_pPalette(pPalette),
m_pChar(pChar),
m_charBegin(0x06010000),
m_charEnd(0x06010020)
{
}
Object::Object (const Object& obj) :
m_attr0(obj.m_attr0),
m_attr1(obj.m_attr1),
m_attr2(obj.m_attr2),
m_width(obj.m_width),
m_height(obj.m_height),
m_pPalette(obj.m_pPalette),
m_pChar(obj.m_pChar),
m_charBegin(obj.m_charBegin),
m_charEnd(obj.m_charEnd)
{
}
void Object::DrawLine (uint8_t line, uint32_t* surface, bool oneDim,
uint8_t mosaic)
{
// if the object is disabled or it's an obj window
if (m_attr0 & (0x1 << 9) || ((m_attr0 >> 10) & 0x3) == 2)
return;
// don't draw sprites with "prohibited" size
if (m_width == 0)
return;
int16_t yoff = (m_attr0 & 0xFF);
if (yoff > Screen::HEIGHT)
yoff -= 256;
// if the object does not appear on the line
if (yoff > line || yoff + m_height*8 <= line)
return;
uint8_t mosH;
if (m_attr0 & (0x1 << 12))
{
uint8_t mosV = mosaic >> 4;
mosH = mosaic & 0xF;
++mosV;
if (mosH) // let it be 0 if it's 0 (=> no mosaic)
++mosH;
line /= mosV;
line *= mosV;
}
else
mosH = 0;
int16_t xoff = (m_attr1 & 0x1FF);
if (xoff & (0x1 << 8))
xoff |= 0xFE00; // extend sign bit
uint32_t* ptr = surface + xoff;
uint32_t prio = (((uint32_t)m_attr2) << 6) & (0x3 << 16);
uint32_t mask = prio;
// if semi transparent
if (((m_attr0 >> 10) & 0x3) == 0x1)
mask |= (0x1 << 18);
uint8_t* pChar = m_pChar + (m_attr2 & 0x3FF) * 32;
bool flipH = m_attr1 & (0x1 << 12);
if (m_attr0 & (0x1 << 13)) // if 256 colors mode
{
// if the tile is vertically flipped
if (m_attr1 & (0x1 << 13))
{
// go to the tile
if (oneDim)
pChar += m_width * (8 * 8) * (m_height -1 -((line-yoff) / 8));
else
pChar += (16 * 8 * 8) * (m_height -1 -((line-yoff) / 8));
// advance to the right line
pChar += 8 * (7 - ((line-yoff) % 8));
}
else
{
// go to the tile
if (oneDim)
pChar += m_width * (8 * 8) * ((line-yoff) / 8);
else
pChar += (16 * 8 * 8) * ((line-yoff) / 8);
// advance to the right line
pChar += 8 * ((line-yoff) % 8);
}
// go to the end of the line if the object is flipped
if (flipH)
pChar += (m_width-1) * (8*8) + 8 - 1;
for (uint8_t j = 0; j < m_width*8; ++j, ++ptr)
{
// if we are on screen
if (ptr - surface < Screen::WIDTH && ptr >= surface)
{
if (mosH && (ptr - surface) % mosH)
*ptr = ptr[-1];
else
{
// if there is something to draw
if (*pChar)
{
// if we have priority or there is nothing behind
if (prio < (*ptr & (0x3 << 16)) || !(*ptr & 0x8000))
*ptr = m_pPalette[*pChar] | 0x8000 | mask;
}
// if we have nothing to draw
else
// if we have better priority
if (prio < (*ptr & (0x3 << 16)))
// we just modify priority and keep what was behind
*ptr = (*ptr & ~(0x3 << 16)) | (mask & (0x3 << 16));
}
}
if (flipH)
if ((j % 8) == 7)
// go to previous tile
pChar -= ((8*8) - (8) + 1);
else
--pChar;
else
if ((j % 8) == 7)
// go to next tile
pChar += ((8*8) - (8) + 1);
else
++pChar;
}
}
else
{
// if the tile is vertically flipped
if (m_attr1 & (0x1 << 13))
{
// go to the tile
if (oneDim)
pChar += m_width * (8 * 8 / 2) * (m_height -1 -((line-yoff) / 8));
else
pChar += (32 * 8 * 8 / 2) * (m_height -1 -((line-yoff) / 8));
// advance to the right line
pChar += (8/2) * (7 - ((line-yoff) % 8));
}
else
{
// go to the tile
if (oneDim)
pChar += m_width * (8 * 8 / 2) * ((line-yoff) / 8);
else
pChar += (32 * 8 * 8 / 2) * ((line-yoff) / 8);
// advance to the right line
pChar += (8/2) * ((line-yoff) % 8);
}
// go to the end of the line if the object is flipped
if (flipH)
pChar += (m_width-1) * (8*8/2) + 8/2 - 1;
uint16_t* pPalette = m_pPalette + 16 * (m_attr2 >> 12);
uint8_t colorInd;
for (uint8_t j = 0; j < m_width*8; ++j, ++ptr)
{
if (flipH)
if (j % 2)
{
colorInd = *pChar & 0xF;
if ((j % 8) == 7)
// go to next tile
// it doesn't matter if we are in one or two dimensions mapping
// since we never go on the next line
pChar -= ((8*8/2) - (8/2) + 1);
else
--pChar;
}
else
colorInd = *pChar >> 4;
else
if (j % 2)
{
colorInd = *pChar >> 4;
if ((j % 8) == 7)
// go to next tile
// it doesn't matter if we are in one or two dimensions mapping
// since we never go on the next line
pChar += ((8*8/2) - (8/2) + 1);
else
++pChar;
}
else
colorInd = *pChar & 0xF;
// if we are on screen
if (ptr - surface < Screen::WIDTH && ptr >= surface)
{
if (mosH && (ptr - surface) % mosH)
*ptr = ptr[-1];
else
{
// if there is something to draw
if (colorInd)
{
// if we have priority or there is nothing behind
if (prio < (*ptr & (0x3 << 16)) || !(*ptr & 0x8000))
*ptr = pPalette[colorInd] | 0x8000 | mask;
}
// if we have nothing to draw
else
// if we have better priority
if (prio < (*ptr & (0x3 << 16)))
// we just modify priority and keep what was behind
*ptr = (*ptr & ~(0x3 << 16)) | (mask & (0x3 << 16));
}
}
}
}
}
void Object::DrawLineRot (uint8_t line, uint32_t* surface, bool oneDim,
int16_t a, int16_t b, int16_t c, int16_t d, uint8_t mosaic)
{
// if it's an obj window
if (((m_attr0 >> 10) & 0x3) == 2)
return;
// don't draw sprites with "prohibited" size
if (m_width == 0)
return;
int16_t yoff = (m_attr0 & 0xFF);
if (yoff > Screen::HEIGHT)
yoff -= 256;
int16_t xoff = (m_attr1 & 0x1FF);
if (xoff & (0x1 << 8))
xoff |= 0xFE00; // extend sign bit
uint8_t fwidth = m_width*8, fheight = m_height*8;
if (m_attr0 & (0x1 << 9))
{
fwidth *= 2;
fheight *= 2;
}
// if the object does not appear on the line
if (yoff > line || yoff + fheight <= line)
return;
uint8_t mosH;
if (m_attr0 & (0x1 << 12))
{
uint8_t mosV = mosaic >> 4;
mosH = mosaic & 0xF;
++mosV;
if (mosH) // let it be 0 if it's 0 (=> no mosaic)
++mosH;
line /= mosV;
line *= mosV;
}
else
mosH = 0;
int32_t curX = ((m_width*8) << 8)/2
+ (-fwidth/2) * a + (line-yoff-fheight/2) * b;
int32_t curY = ((m_height*8) << 8)/2
+ (-fwidth/2) * c + (line-yoff-fheight/2) * d;
int32_t intX, intY;
uint32_t* ptr = surface + xoff;
uint32_t prio = (((uint32_t)m_attr2) << 6) & (0x3 << 16);
uint32_t mask = prio;
// if semi transparent
if (((m_attr0 >> 10) & 0x3) == 0x1)
mask |= (0x1 << 18);
uint8_t* pChar = m_pChar + (m_attr2 & 0x3FF) * 32;
uint8_t colorInd;
if (m_attr0 & (0x1 << 13)) // if 256 colors mode
{
for (uint8_t i = 0; i < fwidth; ++i)
{
intX = curX >> 8;
intY = curY >> 8;
// if we are on the object
if (intX >= 0 && intX < m_width*8 &&
intY >= 0 && intY < m_height*8 &&
// and we are on screen
ptr - surface < Screen::WIDTH && ptr >= surface)
{
if (mosH && (ptr - surface) % mosH)
*ptr = ptr[-1];
else
{
colorInd = pChar[
(intY/8) * (oneDim ? m_width : 16) * 8*8
+ (intX/8) * 8*8
+ (intY%8) * 8
+ (intX%8)];
// if there is something to draw
if (colorInd)
{
// if we have priority or there is nothing behind
if (prio < (*ptr & (0x3 << 16)) || !(*ptr & 0x8000))
*ptr = m_pPalette[colorInd] | 0x8000 | mask;
}
// if we have nothing to draw
else
// if we have better priority
if (prio < (*ptr & (0x3 << 16)))
// we just modify priority and keep what was behind
*ptr = (*ptr & ~(0x3 << 16)) | (mask & (0x3 << 16));
}
}
++ptr;
curX += a;
curY += c;
}
}
else
{
uint16_t* pPalette = m_pPalette + 16 * (m_attr2 >> 12);
for (uint8_t i = 0; i < fwidth; ++i)
{
intX = curX >> 8;
intY = curY >> 8;
// if we are on the object
if (intX >= 0 && intX < m_width*8 &&
intY >= 0 && intY < m_height*8 &&
// and we are on screen
ptr - surface < Screen::WIDTH && ptr >= surface)
{
if (mosH && (ptr - surface) % mosH)
*ptr = ptr[-1];
else
{
colorInd = pChar[(
(intY/8) * (oneDim ? m_width : 32) * 8*8
+ (intX/8) * 8*8
+ (intY%8) * 8
+ (intX%8)
) / 2];
if (intX % 2)
colorInd >>= 4;
else
colorInd &= 0xF;
// if there is something to draw
if (colorInd)
{
// if we have priority or there is nothing behind
if (prio < (*ptr & (0x3 << 16)) || !(*ptr & 0x8000))
*ptr = pPalette[colorInd] | 0x8000 | mask;
}
// if we have nothing to draw
else
// if we have better priority
if (prio < (*ptr & (0x3 << 16)))
// we just modify priority and keep what was behind
*ptr = (*ptr & ~(0x3 << 16)) | (mask & (0x3 << 16));
}
}
++ptr;
curX += a;
curY += c;
}
}
}
void Object::DrawWindow (uint8_t line, uint8_t* surface, bool oneDim,
uint8_t mask)
{
// if the object is disabled or it's not an obj window
if (m_attr0 & (0x1 << 9) || ((m_attr0 >> 10) & 0x3) != 2)
return;
// don't draw sprites with "prohibited" size
if (m_width == 0)
return;
int16_t yoff = (m_attr0 & 0xFF);
if (yoff > Screen::HEIGHT)
yoff -= 256;
// if the object does not appear on the line
if (yoff > line || yoff + m_height*8 <= line)
return;
int16_t xoff = (m_attr1 & 0x1FF);
if (xoff & (0x1 << 8))
xoff |= 0xFE00; // extend sign bit
bool flipH = m_attr1 & (0x1 << 12);
uint8_t* ptr = surface + xoff;
if (flipH)
ptr += m_width * 8 - 1;
uint8_t* pChar = m_pChar + (m_attr2 & 0x3FF) * 32;
if (m_attr0 & (0x1 << 13)) // if 256 colors mode
{
// TODO 2d map, vert flips
// we set pChar on the tile
if (oneDim)
pChar += m_width * (8 * 8) * ((line-yoff) / 8);
else
pChar += (16 * 8 * 8) * ((line-yoff) / 8);
// and we go to the first pixel we need to draw
pChar += 8 * ((line-yoff) % 8);
for (uint8_t j = 0; j < m_width*8; ++j)
{
if (ptr - surface < Screen::WIDTH && ptr >= surface && *pChar)
*ptr = mask;
if (flipH)
--ptr;
else
++ptr;
if ((j % 8) == 7)
// go to next tile
pChar += ((8*8) - (8) + 1);
else
++pChar;
}
}
else
{
// TODO verts flips
// we set pChar on the tile
if (oneDim)
pChar += m_width * (8 * 8 / 2) * ((line-yoff) / 8);
else
pChar += (32 * 8 * 8 / 2) * ((line-yoff) / 8);
// and we go to the first pixel we need to draw
pChar += (8/2) * ((line-yoff) % 8);
uint8_t colorInd;
for (uint8_t j = 0; j < m_width*8; ++j)
{
if (j % 2)
{
colorInd = *pChar >> 4;
if ((j % 8) == 7)
// go to next tile
// it doesn't matter if we are in one or two dimensions mapping
// since we never go on the next line
pChar += ((8*8/2) - (8/2) + 1);
else
++pChar;
}
else
colorInd = *pChar & 0xF;
// if we are not offscreen and there is a color
if (ptr - surface < Screen::WIDTH && ptr >= surface && colorInd)
*ptr = mask;
if (flipH)
--ptr;
else
++ptr;
}
}
}
void Object::DrawWindowRot (uint8_t line, uint8_t* surface,
bool oneDim, int16_t a, int16_t b, int16_t c, int16_t d, uint8_t mask)
{
// if it's not an obj window
if (((m_attr0 >> 10) & 0x3) != 2)
return;
// don't draw sprites with "prohibited" size
if (m_width == 0)
return;
int16_t yoff = (m_attr0 & 0xFF);
if (yoff > Screen::HEIGHT)
yoff -= 256;
int16_t xoff = (m_attr1 & 0x1FF);
if (xoff & (0x1 << 8))
xoff |= 0xFE00; // extend sign bit
uint8_t fwidth = m_width*8, fheight = m_height*8;
if (m_attr0 & (0x1 << 9))
{
fwidth *= 2;
fheight *= 2;
}
// if the object does not appear on the line
if (yoff > line || yoff + fheight <= line)
return;
int32_t curX = ((m_width*8) << 8)/2
+ (-fwidth/2) * a + (line-yoff-fheight/2) * b;
int32_t curY = ((m_height*8) << 8)/2
+ (-fwidth/2) * c + (line-yoff-fheight/2) * d;
int32_t intX, intY;
uint8_t* ptr = surface + xoff;
uint8_t* pChar = m_pChar + (m_attr2 & 0x3FF) * 32;
uint8_t colorInd;
if (m_attr0 & (0x1 << 13)) // if 256 colors mode
{
for (uint8_t i = 0; i < fwidth; ++i)
{
intX = curX >> 8;
intY = curY >> 8;
if (intX >= 0 && intX < m_width*8 &&
intY >= 0 && intY < m_height*8)
{
colorInd = pChar[
(intY/8) * (oneDim ? m_width : 32) * 8*8
+ (intX/8) * 8*8
+ (intY%8) * 8
+ (intX%8)];
if (ptr - surface < Screen::WIDTH && ptr >= surface && colorInd)
*ptr = mask;
}
++ptr;
curX += a;
curY += c;
}
}
else
{
for (uint8_t i = 0; i < fwidth; ++i)
{
intX = curX >> 8;
intY = curY >> 8;
if (intX >= 0 && intX < m_width*8 &&
intY >= 0 && intY < m_height*8)
{
colorInd = pChar[(
(intY/8) * (oneDim ? m_width : 32) * 8*8
+ (intX/8) * 8*8
+ (intY%8) * 8
+ (intX%8)
) / 2];
if (intX % 2)
colorInd >>= 4;
else
colorInd &= 0xF;
if (ptr - surface < Screen::WIDTH && ptr >= surface && colorInd)
*ptr = mask;
}
++ptr;
curX += a;
curY += c;
}
}
}
void Object::UpdateAttrs (uint16_t attr0, uint16_t attr1, uint16_t attr2)
{
bool setsize = false, reload = false;
if ((m_attr0 & 0xFF00) != (attr0 & 0xFF00))
{
reload = true;
setsize = true;
}
m_attr0 = attr0;
if ((m_attr1 & 0xF000) != (attr1 & 0xF000))
{
reload = true;
setsize = true;
}
m_attr1 = attr1;
if ((m_attr2 & 0xF1FF) != (attr2 & 0xF1FF))
reload = true;
m_attr2 = attr2;
if (setsize)
{
SetSize();
if (reload)
{
m_charBegin = 0x06010000 + (m_attr2 & 0x3FF)*32;
m_charEnd = m_charBegin + m_width*m_height*8*
((m_attr0 & (0x1 << 13)) ? 8 : 4);
}
}
}
void Object::UpdateAttr0 (uint16_t attr0)
{
// FIXME : we can do a more restrictive condition
if ((m_attr0 & 0xFF00) != (attr0 & 0xFF00))
{
m_attr0 = attr0;
SetSize();
m_charEnd = m_charBegin + m_width*m_height*8*
((m_attr0 & (0x1 << 13)) ? 8 : 4);
}
else
m_attr0 = attr0;
}
void Object::UpdateAttr1 (uint16_t attr1)
{
// if the size has changed
if ((m_attr1 & 0xC000) != (attr1 & 0xC000))
{
m_attr1 = attr1;
SetSize();
m_charEnd = m_charBegin + m_width*m_height*8*
((m_attr0 & (0x1 << 13)) ? 8 : 4);
}
else
m_attr1 = attr1;
}
void Object::UpdateAttr2 (uint16_t attr2)
{
if ((m_attr2 & 0xF1FF) != (attr2 & 0xF1FF))
{
m_attr2 = attr2;
m_charBegin = 0x06010000 + (m_attr2 & 0x3FF)*32;
m_charEnd = m_charBegin + m_width*m_height*8*
((m_attr0 & (0x1 << 13)) ? 8 : 4);
}
else
m_attr2 = attr2;
}
inline void Object::SetSize ()
{
static const uint8_t Width[4][4] = {
{1, 2, 4, 8}, // Square
{2, 4, 4, 8}, // Horizontal
{1, 1, 2, 4}, // Vertical
{0, 0, 0, 0} // Prohibited
};
static const uint8_t Height[4][4] = {
{1, 2, 4, 8}, // Square
{1, 1, 2, 4}, // Horizontal
{2, 4, 4, 8}, // Vertical
{0, 0, 0, 0} // Prohibited
};
m_width = Width[m_attr0 >> 14][m_attr1 >> 14];
m_height = Height[m_attr0 >> 14][m_attr1 >> 14];
}
}
}

View File

@ -1,168 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/objects.hpp"
#include "../debug.hpp"
#include <string.h>
namespace AMeteor
{
namespace Graphics
{
Objects::Objects (Memory& memory, Io& io, uint16_t* pPalette) :
m_io(io),
m_objs(128, Object(pPalette,
memory.GetRealAddress(0x06010000))),
m_pOam((uint16_t*)memory.GetRealAddress(0x07000000))
{
}
void Objects::DrawLine (uint8_t line, uint32_t* surface)
{
bool oneDim = m_io.DRead16(Io::DISPCNT) & (0x1 << 6);
uint8_t mosaic = m_io.DRead8(Io::MOSAIC+1);
int16_t rotSel;
for (Objs::iterator iter = m_objs.begin();
iter != m_objs.end(); ++iter)
{
rotSel = iter->GetRotationParam();
if (rotSel == -1)
iter->DrawLine (line, surface, oneDim, mosaic);
else
{
rotSel *= 16;
iter->DrawLineRot (line, surface, oneDim, m_pOam[rotSel + 3],
m_pOam[rotSel + 7], m_pOam[rotSel + 11], m_pOam[rotSel + 15],
mosaic);
}
}
}
void Objects::DrawLineHighOnly (uint8_t line, uint32_t* surface)
{
bool oneDim = m_io.DRead16(Io::DISPCNT) & (0x1 << 6);
uint8_t mosaic = m_io.DRead8(Io::MOSAIC+1);
int16_t rotSel;
for (Objs::iterator iter = m_objs.begin();
iter != m_objs.end(); ++iter)
if (iter->GetTileNum() >= 512)
{
rotSel = iter->GetRotationParam();
if (rotSel == -1)
iter->DrawLine (line, surface, oneDim, mosaic);
else
{
rotSel *= 16;
iter->DrawLineRot (line, surface, oneDim, m_pOam[rotSel + 3],
m_pOam[rotSel + 7], m_pOam[rotSel + 11], m_pOam[rotSel + 15],
mosaic);
}
}
}
void Objects::DrawWindow (uint8_t line, uint8_t* surface)
{
bool oneDim = m_io.DRead16(Io::DISPCNT) & (0x1 << 6);
int16_t rotSel;
uint8_t mask = (m_io.DRead16(Io::WINOUT) >> 8) & 0x3F;
for (Objs::iterator iter = m_objs.begin(); iter != m_objs.end(); ++iter)
if (iter->IsWindow())
{
rotSel = iter->GetRotationParam();
if (rotSel == -1)
iter->DrawWindow (line, surface, oneDim, mask);
else
{
rotSel *= 16;
iter->DrawWindowRot (line, surface, oneDim, m_pOam[rotSel + 3],
m_pOam[rotSel + 7], m_pOam[rotSel + 11], m_pOam[rotSel + 15],
mask);
}
}
}
void Objects::OamWrite (uint32_t begin, uint32_t end)
{
uint32_t objnum;
// FIXME is this possible ?
if (begin & 0x3)
met_abort("OamWrite not 4 byte aligned");
if (begin <= 0x07000000)
objnum = 0;
else
objnum = (begin - 0x07000000);
uint16_t* pOam = m_pOam + objnum/2;
objnum /= 8;
Objs::iterator iterStart;
iterStart = m_objs.begin() + objnum;
Objs::iterator iterEnd = m_objs.begin() + (end - 0x07000000 + 7)/8;
if (iterEnd > m_objs.end())
iterEnd = m_objs.end();
for (Objs::iterator iter = iterStart; iter != iterEnd; ++iter, ++objnum)
{
iter->UpdateAttrs(pOam[0], pOam[1], pOam[2]);
pOam += 4;
}
}
void Objects::OamWrite16 (uint32_t add)
{
uint16_t objnum = (add - 0x07000000) / 8;
uint16_t* pOam = m_pOam + objnum*4;
Objs::iterator iter = m_objs.begin() + objnum;
switch ((add - 0x07000000) % 8)
{
case 0:
iter->UpdateAttr0(pOam[0]);
break;
case 2:
iter->UpdateAttr1(pOam[1]);
break;
case 4:
iter->UpdateAttr2(pOam[2]);
break;
case 6:
break;
default :
met_abort("Oam access not 16 bits aligned");
break;
}
}
void Objects::OamWrite32 (uint32_t add)
{
add -= 0x07000000;
uint16_t objnum = add / 8;
uint16_t* pOam = m_pOam + objnum * 4;
Objs::iterator iter = m_objs.begin() + objnum;
switch (add % 8)
{
case 0:
iter->UpdateAttr0(pOam[0]);
iter->UpdateAttr1(pOam[1]);
break;
case 4:
iter->UpdateAttr2(pOam[2]);
break;
default :
met_abort("Oam access not 32 bits aligned");
break;
}
}
}
}

View File

@ -1,35 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/renderer.hpp"
#include "ameteor.hpp"
namespace AMeteor
{
namespace Graphics
{
Renderer::Renderer (const uint16_t* surface) :
m_base(surface)
{
}
void Renderer::VBlank ()
{
if (m_sig_frame)
m_sig_frame(m_base);
}
}
}

View File

@ -1,423 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/screen.hpp"
#include "../globals.hpp"
#include "../debug.hpp"
#include "ameteor.hpp"
#include <cstring>
namespace AMeteor
{
namespace Graphics
{
BgLayer Screen::* const Screen::BgLayers [4] =
{ &Screen::m_bgLayer0, &Screen::m_bgLayer1,
&Screen::m_bgLayer2, &Screen::m_bgLayer3 };
// TODO there is no more need to pass theses references
Screen::Screen (Memory& memory, Io& io) :
m_io(io),
m_surface(new uint16_t[WIDTH*HEIGHT]),
m_renderer(m_surface),
m_frameskip(0),
m_curframe(0),
m_dispcnt(0),
m_refX2(0), m_refY2(0), m_refX3(0), m_refY3(0),
m_pPalette((uint16_t*)memory.GetRealAddress(0x05000000)),
m_bgLayer0(0, memory, io, m_pPalette),
m_bgLayer1(1, memory, io, m_pPalette),
m_bgLayer2(2, memory, io, m_pPalette),
m_bgLayer3(3, memory, io, m_pPalette),
m_objs(memory, io, m_pPalette + 256)
{
}
Screen::~Screen ()
{
delete [] m_surface;
}
void Screen::DrawLine (uint8_t line)
{
if (m_curframe < m_frameskip)
{
// we skip this frame
// VBlank
if (line == 159)
// we don't update screen since we haven't drawn anything on it, it
// would show up a buffer with the previous image or maybe only
// garbage (we use double buffering)
m_curframe = (m_curframe + 1) % FRMSKIP_TOTAL;
return;
}
uint16_t* lineBg = new uint16_t[4*WIDTH];
// layers may draw transparent pixels, so we need to clear them
memset(lineBg, 0, 4*WIDTH*sizeof(uint16_t));
uint32_t* lineObj = new uint32_t[WIDTH];
for (uint32_t* p = lineObj + WIDTH - 1; p >= lineObj; --p)
*p = 0x00030000;
uint8_t prio[4];
prio[0] = m_bgLayer0.GetPriority();
prio[1] = m_bgLayer1.GetPriority();
prio[2] = m_bgLayer2.GetPriority();
prio[3] = m_bgLayer3.GetPriority();
uint8_t layersOn = (m_dispcnt >> 8) & 0x1F;
switch (m_dispcnt & 0x7)
{
case 0: // all in mode 0
// if the bg is enabled draw it
if (layersOn & (0x1 )) m_bgLayer0.DrawLine0(line, lineBg);
if (layersOn & (0x1 << 1)) m_bgLayer1.DrawLine0(line, lineBg+WIDTH);
if (layersOn & (0x1 << 2)) m_bgLayer2.DrawLine0(line, lineBg+2*WIDTH);
if (layersOn & (0x1 << 3)) m_bgLayer3.DrawLine0(line, lineBg+3*WIDTH);
// if objects are enabled draw them
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
break;
case 1: // bg0 and bg1 in mode 0 and bg2 in mode 2, no bg3
// disable layer 3
layersOn &= 0xF7;
// if the bg is enabled draw it
if (layersOn & (0x1 )) m_bgLayer0.DrawLine0(line, lineBg);
if (layersOn & (0x1 << 1)) m_bgLayer1.DrawLine0(line, lineBg+WIDTH);
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine2(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC));
// if objects are enabled draw them
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
break;
case 2: // bg2 and bg3 in mode 2, no bg0 and bg1
// disable layers 0 and 1
layersOn &= 0xFC;
// if the bg is enabled draw it
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine2(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC));
if (layersOn & (0x1 << 3))
m_bgLayer3.DrawLine2(lineBg+3*WIDTH,
m_refX3, m_refY3,
m_io.DRead16(Io::BG3PA),
m_io.DRead16(Io::BG3PC));
// if objects are enabled draw them
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
break;
case 3: // bg2 only 15 bit direct color 240x160
layersOn &= 0xF4;
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine3(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC));
if (layersOn & (0x1 << 4))
m_objs.DrawLineHighOnly(line, lineObj);
break;
// TODO (remember, HIGH ONLY for objs, don't make shitty copy paste)
case 4: // bg2 only in mode 4 (bitmap 256)
layersOn &= 0xF4;
// if bg2 is enabled
if (layersOn & (0x1 << 2))
// draw it
m_bgLayer2.DrawLine4(
line,
lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PB),
m_io.DRead16(Io::BG2PC),
m_io.DRead16(Io::BG2PD),
m_dispcnt & (0x1 << 4));
// if objs are enabled
if (layersOn & (0x1 << 4))
// all objs with the current priority
m_objs.DrawLineHighOnly(line, lineObj);
break;
case 5: // bg2 only 15 bit direct color 160x128 2 frames
layersOn &= 0xF4;
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine5(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC),
m_dispcnt & (0x1 << 4));
if (layersOn & (0x1 << 4))
m_objs.DrawLineHighOnly(line, lineObj);
break;
default :
met_abort("not supported : " << (m_dispcnt & 0x7));
break;
}
// windows
/* I got very little information for this, it may not be accurate. All
* the sources don't say the same thing */
uint8_t* window = NULL;
if (m_dispcnt >> 13)
{
window = new uint8_t[WIDTH];
// Outside window
memset(window, m_io.DRead16(Io::WINOUT) & 0x3F, WIDTH*sizeof(uint8_t));
// OBJ window
if (m_dispcnt & (0x1 << 15))
m_objs.DrawWindow(line, window);
// Window 1
if (m_dispcnt & (0x1 << 14))
DrawWindow(line, window,
m_io.DRead16(Io::WIN1V), m_io.DRead16(Io::WIN1H),
(m_io.DRead16(Io::WININ) >> 8) & 0x3F);
// Window 0
if (m_dispcnt & (0x1 << 13))
DrawWindow(line, window,
m_io.DRead16(Io::WIN0V), m_io.DRead16(Io::WIN0H),
m_io.DRead16(Io::WININ) & 0x3F);
}
// color effects
uint16_t bldcnt = m_io.DRead16(Io::BLDCNT);
uint8_t colorEffect = (bldcnt >> 6) & 0x3;
uint8_t eva = std::min(m_io.DRead8(Io::BLDALPHA) & 0x1F, 16);
uint8_t evb = std::min(m_io.DRead8(Io::BLDALPHA+1) & 0x1F, 16);
uint8_t evy = std::min(m_io.DRead8(Io::BLDY) & 0x1F, 16);
// blending
uint16_t* surface = m_surface + line*WIDTH;
uint16_t out, bout;
// top and back are formated as follow :
// 4 bits | 4 bits
// priority | layer
uint8_t top, back;
uint8_t curprio;
uint32_t* pObj = lineObj;
uint16_t* pBg = lineBg;
uint8_t* pWin = window;
uint8_t winmask;
// if window are disabled, we draw everything which is enabled by
// layersOn
if (!window)
winmask = 0xFF;
for (uint8_t x = 0; x < WIDTH; ++x, ++pBg, ++pObj, ++pWin)
{
if (window)
winmask = *pWin;
// backdrop
bout = out = m_pPalette[0];
back = top = 0xF5;
// for each layer
for (uint8_t l = 0; l < 4; ++l)
// if layer is enabled and
if ((layersOn & (0x1 << l)) &&
// pixel to draw is not transparent
(pBg[l*WIDTH] & 0x8000))
{
curprio = ((prio[l] << 4) | l);
if (curprio < back && curprio > top)
{
bout = pBg[l*WIDTH];
back = curprio;
}
else if (
// priority is lower than current top pixel and
curprio < top &&
// this layer should be drawn in current window
(winmask & (0x1 << l)))
{
bout = out;
out = pBg[l*WIDTH];
back = top;
top = curprio;
}
}
// now objects
// if objects are enabled
if ((layersOn & (0x1 << 4)) &&
// pixel to draw is not transparent
(*pObj & 0x8000))
{
curprio = ((*pObj >> (16 - 4)) & (0x3 << 4));
if (curprio <= (back & 0xF0) && curprio > (top & 0xF0))
{
bout = *pObj;
back = curprio | 4;
}
else if (// priority is lower than current top pixel and
// NOTE : objects are OVER bg with same priority
curprio <= (top & 0xF0) &&
// objects should be drawn in current window
(winmask & (0x1 << 4)))
{
bout = out;
out = *pObj;
back = top;
top = curprio | 4;
}
}
// if we have an object on top and it has semi transparency
if ((top & 0xF) == 4 && (*pObj & (0x1 << 18)))
{
// if second target is just behind
if (bldcnt & ((0x1 << 8) << (back & 0xF)))
// apply alpha blend
out =
std::min(((bout & 0x001F) * evb +
(out & 0x001F) * eva) / 16, 0x001F) |
std::min((((bout & 0x03E0) * evb +
(out & 0x03E0) * eva) / 16) & 0xFFE0, 0x03E0) |
// no need to take care of over flow since u16 & s32 = s32
std::min((((bout & 0x7C00) * evb +
(out & 0x7C00) * eva) / 16) & 0xFC00, 0x7C00);
}
// else if no window or window and effects are enabled in window
// and we have a first target on top
else if ((!window || (*pWin & (0x1 << 5)))
&& (bldcnt & (0x1 << (top & 0xF))))
switch (colorEffect)
{
case 1: // alpha blend
// if second target is just behind
// TODO optimization : special cases for eva = 0 or evb = 0
if (bldcnt & ((0x1 << 8) << (back & 0xF)))
// apply alpha blend
out =
std::min(((bout & 0x001F) * evb +
(out & 0x001F) * eva) / 16, 0x001F) |
std::min((((bout & 0x03E0) * evb +
(out & 0x03E0) * eva) / 16) & 0xFFE0, 0x03E0) |
// no need to take care of over flow since u16 & s32 = s32
std::min((((bout & 0x7C00) * evb +
(out & 0x7C00) * eva) / 16) & 0xFC00, 0x7C00);
break;
case 2: // brightness increase
// we don't need saturation since the formula makes it so it never
// goes above 0x1F
out =
(((out & 0x001F) +
((0x001F - (out & 0x001F)) * evy) / 16) & 0x001F) |
(((out & 0x03E0) +
((0x03E0 - (out & 0x03E0)) * evy) / 16) & 0x03E0) |
(((out & 0x7C00) +
((0x7C00 - (out & 0x7C00)) * evy) / 16) & 0x7C00);
break;
case 3: // brightness decrease
// we don't need saturation since the formula makes it so it never
// goes below 0
out =
((((out & 0x001F) * (16-evy)) / 16) & 0x001F) |
((((out & 0x03E0) * (16-evy)) / 16) & 0x03E0) |
((((out & 0x7C00) * (16-evy)) / 16) & 0x7C00);
break;
}
*surface = out;
++surface;
}
m_refX2 += (int16_t)m_io.DRead16(Io::BG2PB);
m_refY2 += (int16_t)m_io.DRead16(Io::BG2PD);
m_refX3 += (int16_t)m_io.DRead16(Io::BG3PB);
m_refY3 += (int16_t)m_io.DRead16(Io::BG3PD);
if (window)
delete [] window;
delete [] lineBg;
delete [] lineObj;
// VBlank
if (line == 159)
{
m_curframe = (m_curframe + 1) % FRMSKIP_TOTAL;
m_renderer.VBlank();
}
}
void Screen::DrawWindow (uint8_t line, uint8_t* surface,
uint16_t win0v, uint16_t win0h, uint8_t mask)
{
// the variables are called win0, but this function works also for win1
uint8_t win0t = win0v >> 8, win0b = win0v & 0xFF;
// VBA says that if t == b and they are greater than 228, we are in the
// window
// This is from Tonc documentation
if (win0t >= 227)
return;
else if (win0b > win0t && line >= win0t && line < win0b
// the above is the normal behaviour
|| win0b < win0t && (line >= win0t || line < win0b)
// the above is the "inverted" behaviour
)
{
uint8_t win0l, win0r;
uint8_t* ptr;
win0l = win0h >> 8;
win0r = win0h & 0xFF;
// this seems wrong
//if (win0l > 240)
// win0l = 240;
//if (win0r > 240)
// win0r = 240;
// if this is the normal behaviour
if (win0l <= win0r)
{
ptr = surface + win0l;
for (uint8_t i = win0l; i < win0r && i < 240; ++i, ++ptr)
*ptr = mask;
}
// else, this is the inverted behaviour
else
{
ptr = surface;
for (uint8_t i = 0; i < win0r && i < 240; ++i, ++ptr)
*ptr = mask;
ptr = surface + win0l;
for (uint8_t i = win0l; i < 240; ++i, ++ptr)
*ptr = mask;
}
}
}
bool Screen::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_refX2);
SS_WRITE_VAR(m_refY2);
SS_WRITE_VAR(m_refX3);
SS_WRITE_VAR(m_refY3);
return true;
}
bool Screen::LoadState (std::istream& stream)
{
SS_READ_VAR(m_refX2);
SS_READ_VAR(m_refY2);
SS_READ_VAR(m_refX3);
SS_READ_VAR(m_refY3);
return true;
}
}
}

View File

@ -1,222 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/interpreter.hpp"
#include "ameteor/cpu.hpp"
#include "ameteor/memory.hpp"
#include "ameteor/bios.hpp"
#include "ameteor/disassembler/instruction.hpp"
#include "globals.hpp"
#include "cpu_globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
#include <sstream>
namespace AMeteor
{
Interpreter::Interpreter() :
m_run(false),
m_interrupt_(false),
m_haltcnt(IO.GetRef8(Io::HALTCNT)),
m_if(IO.GetRef16(Io::IF)),
m_ie(IO.GetRef16(Io::IE))
{
}
void Interpreter::SendInterrupt (uint16_t interrupt)
{
IO.GetRef16(Io::IF) |= interrupt;
if ((interrupt & IO.DRead16(Io::IE)) &&
(IO.DRead16(Io::IME) & 0x1) &&
!m_st.icpsr.irq_d)
// irq are enabled and theses irq are enabled...
m_interrupt = true;
}
void Interpreter::CheckInterrupt ()
{
m_interrupt =
(IO.DRead16(Io::IF) & IO.DRead16(Io::IE)) &&
(IO.DRead16(Io::IME) & 0x1) &&
!m_st.icpsr.irq_d;
}
void Interpreter::Run (unsigned int cycles)
{
m_run = true;
CLOCK.ResetCounter();
while(m_run && CLOCK.GetCounter() < cycles)
{
switch (m_haltcnt)
{
case 255: // normal mode
PrintRegs();
if (FLAG_T)
{
if (R(15) & 0x1)
met_abort("PC not 16 bit aligned : " << IOS_ADD << R(15));
code = MEM.Read16(R(15)-2);
if (traceenabled)
{
std::stringstream ss;
ss << IOS_TRACE << R(15) - 2 << ':' << std::setw(4) << code << " ";
ss.setf(std::ios::left, std::ios::adjustfield);
ss << std::setw(32) << std::setfill(' ') << Disassembler::Instruction(R(15), (uint16_t)code).ToString();
ss.setf(std::ios::right, std::ios::adjustfield);
ss << IOS_TRACE;
for (int i = 0; i < 16; i++)
ss << std::setw(8) << R(i) << ' ';
UpdateCpsr();
ss << std::setw(8) << m_st.cpsr.dw << ' ';
ss << std::setw(8) << m_st.spsr.dw << ' ';
trace_bizhawk(ss.str());
}
R(15) += 2;
t_Code();
}
else
{
if (R(15) & 0x3)
met_abort("PC not 32 bit aligned : " << IOS_ADD << R(15));
if (R(15) < 0x01000000 && !MEM.HasBios())
{
switch (R(15))
{
case 0x004:
Bios::Bios000h();
break;
case 0x00C:
Bios::Bios008h();
break;
case 0x01C:
Bios::Bios018h();
break;
case 0x134:
Bios::Bios130h();
break;
case 0x33C:
Bios::Bios338h();
break;
case 0x16C:
Bios::Bios168h();
break;
default:
met_abort("Jump to " << IOS_ADD << R(15));
}
}
else
{
code = MEM.Read32(R(15)-4);
if (traceenabled)
{
std::stringstream ss;
ss << IOS_TRACE << R(15) - 4 << ':' << std::setw(8) << code << ' ';
ss.setf(std::ios::left, std::ios::adjustfield);
ss << std::setw(32) << std::setfill(' ') << Disassembler::Instruction(R(15), (uint32_t)code).ToString();
ss.setf(std::ios::right, std::ios::adjustfield);
ss << IOS_TRACE;
for (int i = 0; i < 16; i++)
ss << std::setw(8) << R(i) << ' ';
UpdateCpsr();
ss << std::setw(8) << m_st.cpsr.dw << ' ';
ss << std::setw(8) << m_st.spsr.dw << ' ';
trace_bizhawk(ss.str());
}
R(15) += 4;
a_Code();
}
}
if (R(15) < 0x01000000 && FLAG_T && !MEM.HasBios())
met_abort("Jump to " << IOS_ADD << R(15));
CLOCK.Commit();
if (m_interrupt)
// irq are enabled and there are irq waiting...
{
// FIXME : do we really need this ??
// if not, we can get rid of save and load state and reset
if (m_interrupt_)
{
m_interrupt_ = false;
// XXX we must be sure the cpu isn't halted when an interrupt
// occurs
// should be removed after since it takes no time to make a new
// iteration of the loop
m_haltcnt = 255;
CPU.Interrupt();
}
else
{
// XXX
if (m_haltcnt != 255)
{
m_haltcnt = 255;
CPU.Interrupt();
}
else
m_interrupt_ = true;
}
}
break;
case 0: // halt mode
if (m_if & m_ie) // interrupt occured
{
m_haltcnt = 255; // return to normal mode
CPU.Interrupt();
// XXX use an else
break;
}
CLOCK.WaitForNext();
// XXX remove this block
if (m_if & m_ie) // interrupt occured
{
m_haltcnt = 255; // return to normal mode
CPU.Interrupt();
}
break;
case 1: // stop mode
met_abort("Stop mode not implemented");
break;
default:
met_abort("Unknown HALTCNT value : " << (int)m_haltcnt);
break;
}
}
m_run = false;
}
bool Interpreter::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_interrupt_);
return Cpu::SaveState(stream);
}
bool Interpreter::LoadState (std::istream& stream)
{
SS_READ_VAR(m_interrupt_);
return Cpu::LoadState(stream);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,508 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/io.hpp"
#include "ameteor/dma.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include <cstring>
#include "debug.hpp"
#define W8(add, val) \
m_iomem[(add) & 0xFFF] = (val)
#define W16(add, val) \
*(uint16_t*)(m_iomem + ((add) & 0xFFF)) = (val)
#define W32(add, val) \
*(uint32_t*)(m_iomem + ((add) & 0xFFF)) = (val)
namespace AMeteor
{
Io::Io ()
{
m_iomem = new uint8_t[IO_SIZE];
Reset ();
}
Io::~Io ()
{
delete [] m_iomem;
}
void Io::Reset ()
{
std::memset(m_iomem, 0, IO_SIZE);
// TODO use clears intead
// TODO DISPCNT should be 0x80 by default
// TODO do it also for clears
// TODO lcd should draw white lines when hblank forced
// TODO when passing disabling hblank forced, lcd should start drawing from
// first line
W16(SOUNDBIAS, 0x0200); // default value
W16(KEYINPUT, 0x03FF); // all keys released
W8(HALTCNT, 0xFF); // normal mode (internal)
W16(DISPSTAT, 0x0004); // vcount match (since default vcount is 0)
W16(BG2PA, 0x0100);
W16(BG2PD, 0x0100);
W16(BG3PA, 0x0100);
W16(BG3PD, 0x0100);
W16(RCNT, 0x8000); // we start in general purpose mode
}
void Io::ClearSio ()
{
// TODO
W16(RCNT, 0x8000);
}
void Io::ClearSound ()
{
// TODO
}
void Io::ClearOthers ()
{
// FIXME !! shouldn't we call Write*() ?
// lcd
for (uint8_t i = 0x0; i < 0x56; i += 2)
Write16(i, 0x0000);
// dma
for (uint8_t i = 0xB0; i < 0xE0; i += 4)
Write32(i, 0x0000);
// FIXME : should timers be set to 0 too ? (vba does not)
W8(HALTCNT, 0xFF); // normal mode (internal)
W16(IE, 0x0000);
W16(IF, 0x0000);
W16(IME, 0x0000);
Write16(WAITCNT, 0x0000);
W16(BG2PA, 0x0100);
W16(BG2PD, 0x0100);
W16(BG3PA, 0x0100);
W16(BG3PD, 0x0100);
}
// TODO implement unreadable or write-only io
uint8_t Io::Read8 (uint32_t add)
{
if ((add & 0xFFE) == KEYINPUT)
keyupdate_bizhawk();
//debug ("IO Read8 at " << IOS_ADD << add << " of " << IOS_ADD << (int)*(uint8_t*)(m_iomem + (add & 0xFFF)));
if ((add & 0xFF0) == 0x100)
switch (add & 0xF)
{
case 0x0:
case 0x4:
case 0x8:
case 0xC:
met_abort("Misaligned reading of timers");
}
return m_iomem[add & 0xFFF];
}
uint16_t Io::Read16 (uint32_t add)
{
if ((add & 0xFFE) == KEYINPUT)
keyupdate_bizhawk();
//debug ("IO Read16 at " << IOS_ADD << add << " of " << IOS_ADD << *(uint16_t*)(m_iomem + (add & 0xFFF)));
// special case, reading timers
if ((add & 0xFF0) == 0x100)
switch (add & 0xF)
{
case 0x0: return TIMER0.GetCount();
case 0x4: return TIMER1.GetCount();
case 0x8: return TIMER2.GetCount();
case 0xC: return TIMER3.GetCount();
}
return *(uint16_t*)(m_iomem + (add & 0xFFF));
}
uint32_t Io::Read32 (uint32_t add)
{
if ((add & 0xFFC) == KEYINPUT)
keyupdate_bizhawk();
//debug ("IO Read32 at " << IOS_ADD << add << " of " << IOS_ADD << *(uint32_t*)(m_iomem + (add & 0xFFF)));
// special case, reading timers
if ((add & 0xFF0) == 0x100)
switch (add & 0xF)
{
case 0x0: return TIMER0.GetCount();
case 0x4: return TIMER1.GetCount();
case 0x8: return TIMER2.GetCount();
case 0xC: return TIMER3.GetCount();
}
return *(uint32_t*)(m_iomem + (add & 0xFFF));
}
void Io::Write8 (uint32_t add, uint8_t val)
{
//debug ("IO Write8 at " << IOS_ADD << add << " of " << IOS_ADD << (int)val);
switch (add & 0xFFF)
{
case NR10+1:
case NR52+1:
case NR52+2:
case NR52+3:
break;
case NR10:
case NR11:
case NR13:
case NR21:
case NR23:
case NR41:
case NR43:
case NR50:
case NR51:
case SOUNDCNT_H:
W8(add, val);
break;
case NR12:
W8(add, val);
if (!(val & (0xF << 4)))
SOUND.ResetSound1Envelope();
break;
case NR14:
W8(add, val & 0xC7);
if (val & (0x1 << 7))
SOUND.ResetSound1();
break;
case NR22:
W8(add, val);
if (!(val & (0xF << 4)))
SOUND.ResetSound2Envelope();
break;
case NR24:
W8(add, val & 0xC7);
if (val & (0x1 << 7))
SOUND.ResetSound2();
break;
case NR42:
W8(add, val);
if (!(val & (0xF << 4)))
SOUND.ResetSound4Envelope();
break;
case NR44:
W8(add, val & 0xC7);
if (val & (0x1 << 7))
SOUND.ResetSound4();
break;
case SOUNDCNT_H+1:
W8(add, val);
SOUND.UpdateCntH1(val);
break;
case NR52:
// this will also reset the sound on flags
W8(add, val & 0x80);
break;
case POSTFLG:
// FIXME is this right, i have no idea about why i should do that
if (val)
val &= 0xFE;
W8(add, val);
break;
case HALTCNT:
W8(add, val);
break;
default:
//W8(add, val);
// TODO make a function which will apply masks to IO memory and trigger
// the update functions, this function will be called by write8 and
// write16
#if 1
add &= 0xFFF;
if (add % 2)
Write16(add & ~0x1, (val << 8) | m_iomem[add & ~0x1]);
else
Write16(add, (m_iomem[add | 0x1] << 8) | val);
#endif
break;
}
}
void Io::Write16 (uint32_t add, uint16_t val)
{
//debug ("IO Write16 at " << IOS_ADD << add << " of " << IOS_ADD << val);
//*(uint16_t*)(m_iomem + (add & 0xFFF)) = val;
switch (add & 0xFFF)
{
case KEYINPUT:
case VCOUNT:
break;
case DMA0CNT_L:
case DMA1CNT_L:
case DMA2CNT_L:
case DMA3CNT_L:
//W16(add, val);
DMA.SetReload(((add & 0xFFF) - DMA0CNT_L) / DMA_CHANSIZE, val);
break;
case KEYCNT:
W16(add, val & 0xC3FF);
break;
case IME:
W16(add, val & 0x0001);
CPU.CheckInterrupt();
break;
case IE:
W16(add, val & 0x3FFF);
CPU.CheckInterrupt();
break;
case IF:
*((uint16_t*)(m_iomem+IF)) ^= (val & (*((uint16_t*)(m_iomem+IF))));
CPU.CheckInterrupt();
break;
case BG0CNT:
W16(add, val & 0xFFCF);
LCD.UpdateBg0Cnt(val & 0xFFCF);
break;
case BG1CNT:
W16(add, val & 0xFFCF);
LCD.UpdateBg1Cnt(val & 0xFFCF);
break;
case BG2CNT:
W16(add, val & 0xFFCF);
LCD.UpdateBg2Cnt(val & 0xFFCF);
break;
case BG3CNT:
W16(add, val & 0xFFCF);
LCD.UpdateBg3Cnt(val & 0xFFCF);
break;
case DISPSTAT:
// the first 3 bits are read only and they are used by the lcd
// NOTE : we are in LITTLE ENDIAN
// FIXME : if vcount setting has changed to current vcount, we should
// update the vcounter flag and eventually trigger an interrupt
W16(add, (val & 0xFFF8) | (m_iomem[add & 0xFFF] & 0x07));
break;
// The BG*OFS are write-only, we don't need to W16()
// You do if you're ever going to load them from a savestate...
case BG0HOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg0XOff(val & 0x1FF);
break;
case BG0VOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg0YOff(val & 0x1FF);
break;
case BG1HOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg1XOff(val & 0x1FF);
break;
case BG1VOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg1YOff(val & 0x1FF);
break;
case BG2HOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg2XOff(val & 0x1FF);
break;
case BG2VOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg2YOff(val & 0x1FF);
break;
case BG3HOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg3XOff(val & 0x1FF);
break;
case BG3VOFS:
W16(add, val & 0x1FF);
LCD.UpdateBg3YOff(val & 0x1FF);
break;
case BG2X_H:
val &= 0x0FFF;
case BG2X_L:
W16(add, val);
LCD.UpdateBg2RefX(IO.DRead32(Io::BG2X_L));
break;
case BG2Y_H:
val &= 0x0FFF;
case BG2Y_L:
W16(add, val);
LCD.UpdateBg2RefY(IO.DRead32(Io::BG2Y_L));
break;
case BG3X_H:
val &= 0x0FFF;
case BG3X_L:
W16(add, val);
LCD.UpdateBg3RefX(IO.DRead32(Io::BG3X_L));
break;
case BG3Y_H:
val &= 0x0FFF;
case BG3Y_L:
W16(add, val);
LCD.UpdateBg3RefY(IO.DRead32(Io::BG3Y_L));
break;
case WIN0H:
case WIN1H:
case WIN0V:
case WIN1V:
case WININ:
case WINOUT:
W16(add, val);
break;
case BLDCNT:
W16(add, val);
break;
case MOSAIC:
W16(add, val);
break;
case DISPCNT:
W16(add, val);
LCD.UpdateDispCnt(val);
break;
case DMA0CNT_H:
case DMA1CNT_H:
case DMA2CNT_H:
case DMA3CNT_H:
W16(add, val & 0xFFE0);
DMA.UpdateCnt(((add & 0xFFF) - DMA0CNT_H) / DMA_CHANSIZE);
break;
case WAITCNT:
W16(add, val & 0xDFFF);
MEM.UpdateWaitStates (val & 0xDFFF);
break;
case SOUND1CNT_L:
case SOUND1CNT_H:
case SOUND1CNT_X:
case SOUND2CNT_L:
case SOUND2CNT_H:
case SOUND4CNT_L:
case SOUND4CNT_H:
case SOUNDCNT_L:
case SOUNDCNT_H:
case SOUNDCNT_X:
case POSTFLG:
Write8(add, val & 0xFF);
Write8(add + 1, val >> 8);
break;
case TM0CNT_L:
TIMER0.SetReload(val);
break;
case TM1CNT_L:
TIMER1.SetReload(val);
break;
case TM2CNT_L:
TIMER2.SetReload(val);
break;
case TM3CNT_L:
TIMER3.SetReload(val);
break;
case TM0CNT_H:
W16(add, val & 0x00C7);
TIMER0.Reload();
break;
case TM1CNT_H:
W16(add, val & 0x00C7);
TIMER1.Reload();
break;
case TM2CNT_H:
W16(add, val & 0x00C7);
TIMER2.Reload();
break;
case TM3CNT_H:
W16(add, val & 0x00C7);
TIMER3.Reload();
break;
default:
//met_abort("Unknown IO at " << IOS_ADD << add);
W16(add, val);
break;
}
}
void Io::Write32 (uint32_t add, uint32_t val)
{
//debug ("IO Write32 at " << IOS_ADD << add << " of " << IOS_ADD << val);
switch (add & 0xFF)
{
case DMA1DAD:
case DMA0SAD:
case DMA1SAD:
case DMA2SAD:
case DMA3SAD:
case DMA0DAD:
case DMA2DAD:
case DMA3DAD:
W32(add, val);
break;
case BG0HOFS:
case BG1HOFS:
case BG2HOFS:
case BG3HOFS:
Write16(add, val & 0xFFFF);
Write16(add+2, val >> 16);
break;
case BG2X_L:
W32(add, val & 0x0FFFFFFF);
LCD.UpdateBg2RefX(IO.DRead32(Io::BG2X_L));
break;
case BG2Y_L:
W32(add, val & 0x0FFFFFFF);
LCD.UpdateBg2RefY(IO.DRead32(Io::BG2Y_L));
break;
case BG3X_L:
W32(add, val & 0x0FFFFFFF);
LCD.UpdateBg3RefX(IO.DRead32(Io::BG3X_L));
break;
case BG3Y_L:
W32(add, val & 0x0FFFFFFF);
LCD.UpdateBg3RefY(IO.DRead32(Io::BG3Y_L));
break;
case BG2PA:
case BG2PC:
case BG3PA:
case BG3PC:
case WIN0H:
case WIN0V:
case WININ:
Write16(add, val & 0xFFFF);
Write16(add+2, val >> 16);
break;
case DMA0CNT_L:
case DMA1CNT_L:
case DMA2CNT_L:
case DMA3CNT_L:
Write16(add, val & 0xFFFF);
Write16(add+2, val >> 16);
break;
case FIFO_A:
case FIFO_B:
// TODO
break;
default:
//met_abort("Unknown IO at " << IOS_ADD << add);
//*(uint32_t*)(m_iomem + (add & 0xFFF)) = val;
Write16(add, val & 0xFFFF);
Write16(add+2, val >> 16);
break;
}
}
bool Io::SaveState (std::ostream& stream)
{
SS_WRITE_DATA(m_iomem, IO_SIZE);
return true;
}
bool Io::LoadState (std::istream& stream)
{
SS_READ_DATA(m_iomem, IO_SIZE);
return true;
}
}

View File

@ -1,96 +0,0 @@
// Meteor - A Nintendo Gameboy Advance emulator
// Copyright (C) 2009-2011 Philippe Daouadi
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ameteor/keypad.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
namespace AMeteor
{
Keypad::Keypad () :
m_keyinput(IO.GetRef16(Io::KEYINPUT)),
m_keycnt(IO.GetRef16(Io::KEYCNT))
{
}
void Keypad::KeyPressed(int code)
{
if (m_keys.count(code))
m_keyinput &= ~m_keys[code];
}
void Keypad::KeyReleased(int code)
{
if (m_keys.count(code))
m_keyinput |= m_keys[code];
}
void Keypad::JoyButtonPressed (uint16_t joyid, uint16_t button)
{
uint32_t id = ((int)joyid) << 16 | button;
if (m_joys.count(id))
m_keyinput &= ~m_joys[id];
}
void Keypad::JoyButtonReleased (uint16_t joyid, uint16_t button)
{
uint32_t id = ((int)joyid) << 16 | button;
if (m_joys.count(id))
m_keyinput |= m_joys[id];
}
void Keypad::JoyMoved (uint16_t joyid, uint16_t axis, float pos)
{
uint32_t id = (((int)joyid) << 16) | ((pos < 0) << 15) | (axis & 0x7FFF);
// if pos is 0, we disable the positive and negative targets
if (pos == 0)
{
if (m_axis.count(id))
m_keyinput |= m_axis[id];
if (m_axis.count(id | (1 << 15)))
m_keyinput |= m_axis[id | (1 << 15)];
}
else
{
// we enable the corresponding button
if (m_axis.count(id))
m_keyinput &= ~((uint16_t)m_axis[id]);
// we disable the opposite button (we may have skipped 0)
if (m_axis.count(id ^ 0x8000))
m_keyinput |= m_axis[id ^ 0x8000];
}
}
void Keypad::VBlank ()
{
// if keypad IRQ are enabled
if (m_keycnt & (0x1 << 14))
// if irq condition is and
if (m_keycnt & (0x1 << 15))
{
// if condition is satisfied
if ((~m_keyinput & m_keycnt & 0x3FF) == (m_keycnt & 0x3FF))
CPU.SendInterrupt(0x1000);
}
// if irq condition is or
else
{
// if condition is satisfied
if (~m_keyinput & m_keycnt & 0x3FF)
CPU.SendInterrupt(0x1000);
}
}
}

Some files were not shown because too many files have changed in this diff Show More