diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj
index 02c5015e4d..d7aa635616 100644
--- a/BizHawk.Emulation/BizHawk.Emulation.csproj
+++ b/BizHawk.Emulation/BizHawk.Emulation.csproj
@@ -216,6 +216,8 @@
+
+
diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBA/LibMeteor.cs b/BizHawk.Emulation/Consoles/Nintendo/GBA/LibMeteor.cs
new file mode 100644
index 0000000000..9c73e83766
--- /dev/null
+++ b/BizHawk.Emulation/Consoles/Nintendo/GBA/LibMeteor.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace BizHawk.Emulation.Consoles.Nintendo.GBA
+{
+ ///
+ /// bindings into libmeteor.dll
+ ///
+ public static class LibMeteor
+ {
+ ///
+ /// reset the emulation core
+ ///
+ [DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void libmeteor_reset();
+
+ ///
+ /// signal that you are removing data from the sound buffer.
+ /// the next time frameadvance() is called, writing will start from the beginning
+ ///
+ /// the valid length of the buffer, in bytes
+ [DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern uint libmeteor_emptysound();
+
+ ///
+ /// set up buffers for libmeteor to dump data to. these must be valid before every frameadvance
+ ///
+ /// buffer to hold video data as BGRA32
+ /// length in bytes. must be at least 240 * 160 * 4
+ /// buffer to hold audio data as stereo s16le
+ /// length in bytes. must be 0 mod 4 (hold a full stereo sample set)
+ /// false if some problem. buffers will not be valid in this case
+ [DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern bool libmeteor_setbuffers(IntPtr vid, uint vidlen, IntPtr aud, uint audlen);
+
+ ///
+ /// initialize the library
+ ///
+ [DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void libmeteor_init();
+
+ ///
+ /// run emulation for one frame, updating sound and video along the way
+ ///
+ [DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void libmeteor_frameadvance();
+
+ ///
+ /// load a rom image
+ ///
+ /// raw rom data. need not persist past this call
+ /// length of data in bytes
+ [DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void libmeteor_loadrom(byte[] data, uint datalen);
+
+ ///
+ /// load a bios image
+ ///
+ /// raw bios data. need not persist past this call
+ /// length of data in bytes
+ [DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void libmeteor_loadbios(byte[] data, uint datalen);
+ }
+}
diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs b/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs
new file mode 100644
index 0000000000..a0f3da4b41
--- /dev/null
+++ b/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs
@@ -0,0 +1,199 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.IO;
+
+namespace BizHawk.Emulation.Consoles.Nintendo.GBA
+{
+ public class GBA : IEmulator, IVideoProvider, ISyncSoundProvider
+ {
+ public static readonly ControllerDefinition GBAController =
+ new ControllerDefinition
+ {
+ Name = "GBA Controller",
+ BoolButtons =
+ {
+ "Up", "Down", "Left", "Right", "Select", "Start", "B", "A", "L", "R"//, "Reset", "Power",
+ }
+ };
+ public ControllerDefinition ControllerDefinition { get { return GBAController; } }
+ public IController Controller { get; set; }
+
+ public void Load(byte[] rom)
+ {
+ Init();
+ LibMeteor.libmeteor_reset();
+ LibMeteor.libmeteor_loadbios(File.ReadAllBytes("gbabios.rom"), 16384);
+ LibMeteor.libmeteor_loadrom(rom, (uint)rom.Length);
+ }
+
+ public void FrameAdvance(bool render, bool rendersound = true)
+ {
+ LibMeteor.libmeteor_frameadvance();
+ }
+
+ public int Frame
+ {
+ get { return 0; }
+ }
+
+ public int LagCount
+ {
+ get;
+ set;
+ }
+
+ public bool IsLagFrame
+ {
+ get { return false; }
+ }
+
+ public string SystemId
+ {
+ get { return "GBA"; }
+ }
+
+ public bool DeterministicEmulation
+ {
+ get { return true; }
+ }
+
+ public byte[] ReadSaveRam()
+ {
+ return new byte[0];
+ }
+
+ public void StoreSaveRam(byte[] data)
+ {
+ }
+
+ public void ClearSaveRam()
+ {
+ }
+
+ public bool SaveRamModified { get { return false; } set { } }
+
+ public void ResetFrameCounter()
+ {
+ }
+
+ public void SaveStateText(System.IO.TextWriter writer)
+ {
+ }
+
+ public void LoadStateText(System.IO.TextReader reader)
+ {
+ }
+
+ public void SaveStateBinary(System.IO.BinaryWriter writer)
+ {
+ }
+
+ public void LoadStateBinary(System.IO.BinaryReader reader)
+ {
+ }
+
+ public byte[] SaveStateBinary()
+ {
+ MemoryStream ms = new MemoryStream();
+ BinaryWriter bw = new BinaryWriter(ms);
+ SaveStateBinary(bw);
+ bw.Flush();
+ return ms.ToArray();
+ }
+
+ public CoreInputComm CoreInputComm { get; set; }
+
+ CoreOutputComm _CoreOutputComm = new CoreOutputComm
+ {
+ VsyncNum = 262144,
+ VsyncDen = 4389
+ };
+
+ public CoreOutputComm CoreOutputComm { get { return _CoreOutputComm; } }
+
+ public IList MemoryDomains
+ {
+ get { return null; }
+ }
+
+ public MemoryDomain MainMemory
+ {
+ get { return null; }
+ }
+
+ static GBA attachedcore;
+
+ void Init()
+ {
+ if (attachedcore != null)
+ attachedcore.Dispose();
+
+ LibMeteor.libmeteor_init();
+ videobuffer = new int[240 * 160];
+ videohandle = GCHandle.Alloc(videobuffer, GCHandleType.Pinned);
+ soundbuffer = new short[2048];
+ 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;
+ }
+
+ bool disposed = false;
+ public void Dispose()
+ {
+ if (!disposed)
+ {
+ disposed = true;
+ videohandle.Free();
+ soundhandle.Free();
+ }
+ }
+
+ #region IVideoProvider
+
+ public IVideoProvider VideoProvider { get { return this; } }
+
+ int[] videobuffer;
+ GCHandle videohandle;
+
+ public int[] GetVideoBuffer() { return videobuffer; }
+ public int VirtualWidth { get { return 240; } }
+ public int BufferWidth { get { return 240; } }
+ public int BufferHeight { get { return 160; } }
+ public int BackgroundColor { get { return unchecked((int)0xff000000); } }
+
+ #endregion
+
+ #region ISoundProvider
+
+ short[] soundbuffer;
+ GCHandle soundhandle;
+
+ public ISoundProvider SoundProvider { get { return null; } }
+ public ISyncSoundProvider SyncSoundProvider { get { return this; } }
+ public bool StartAsyncSound() { return false; }
+ public void EndAsyncSound() { }
+
+ public void GetSamples(out short[] samples, out int nsamp)
+ {
+ uint nbytes = LibMeteor.libmeteor_emptysound();
+ samples = soundbuffer;
+ nsamp = (int)(nbytes / 4);
+
+ }
+
+ public void DiscardSamples()
+ {
+ LibMeteor.libmeteor_emptysound();
+ }
+
+ #endregion
+ }
+}
diff --git a/BizHawk.Emulation/Database/Database.cs b/BizHawk.Emulation/Database/Database.cs
index f6da3bfe52..2cb9be03b5 100644
--- a/BizHawk.Emulation/Database/Database.cs
+++ b/BizHawk.Emulation/Database/Database.cs
@@ -164,6 +164,10 @@ namespace BizHawk
case ".CRT":
Game.System = "C64";
break;
+
+ case ".GBA":
+ Game.System = "GBA";
+ break;
}
Game.Name = Path.GetFileNameWithoutExtension(fileName).Replace('_', ' ');
diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs
index 03526ba038..d416acbb8d 100644
--- a/BizHawk.MultiClient/MainForm.cs
+++ b/BizHawk.MultiClient/MainForm.cs
@@ -17,6 +17,7 @@ using BizHawk.MultiClient.tools;
using System.Collections.Generic;
using BizHawk.Emulation.Consoles.Intellivision;
using BizHawk.Emulation.Consoles.GB;
+using BizHawk.Emulation.Consoles.Nintendo.GBA;
using BizHawk.Emulation.Computers.Commodore64;
namespace BizHawk.MultiClient
@@ -1301,6 +1302,7 @@ namespace BizHawk.MultiClient
case "A78": str += "Atari 7800"; break;
case "C64": str += "Commodore 64"; break;
case "Coleco": str += "ColecoVision"; break;
+ case "GBA": str += "Game Boy Advance"; break;
}
if (INTERIM) str += " (interim)";
@@ -1822,6 +1824,11 @@ namespace BizHawk.MultiClient
c64.HardReset();
nextEmulator = c64;
break;
+ case "GBA":
+ GBA gba = new GBA();
+ gba.Load(rom.RomData);
+ nextEmulator = gba;
+ break;
}
}
diff --git a/BizHawk.MultiClient/movie/InputAdapters.cs b/BizHawk.MultiClient/movie/InputAdapters.cs
index c86d892a25..fae5a3cfbd 100644
--- a/BizHawk.MultiClient/movie/InputAdapters.cs
+++ b/BizHawk.MultiClient/movie/InputAdapters.cs
@@ -437,6 +437,10 @@ namespace BizHawk.MultiClient
{
return GetC64ControllersAsMnemonic();
}
+ else if (ControlType == "GBA Controller")
+ {
+ return "EAT AT JOE'S";
+ }
StringBuilder input = new StringBuilder("|");
diff --git a/libmeteor/cinterface.cpp b/libmeteor/cinterface.cpp
new file mode 100644
index 0000000000..aea61202ba
--- /dev/null
+++ b/libmeteor/cinterface.cpp
@@ -0,0 +1,96 @@
+#include "ameteor.hpp"
+#include "ameteor/cartmem.hpp"
+
+#define EXPORT extern "C" __declspec(dllexport)
+
+EXPORT void libmeteor_reset()
+{
+ AMeteor::Reset(AMeteor::UNIT_ALL ^ AMeteor::UNIT_MEMORY_BIOS);
+}
+
+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 = *frame;
+ 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;
+ libmeteor_emptysound();
+ return 1;
+}
+
+EXPORT void libmeteor_init()
+{
+ static bool first = true;
+ if (first)
+ {
+ //AMeteor::_memory.LoadCartInferred();
+ AMeteor::_lcd.GetScreen().GetRenderer().SetFrameSlot(syg::ptr_fun(videocb));
+ AMeteor::_sound.GetSpeaker().SetFrameSlot(syg::ptr_fun(soundcb));
+ // TODO: input
+ first = false;
+ }
+}
+
+EXPORT void libmeteor_frameadvance()
+{
+ AMeteor::_keypad.SetPadState(0x3ff);
+ AMeteor::Run(10000000);
+}
+
+// TODO: serialize, unserialize
+
+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);
+}
+
+// TODO: memory domains
+
diff --git a/libmeteor/include/ameteor.hpp b/libmeteor/include/ameteor.hpp
new file mode 100644
index 0000000000..27dafb1fd8
--- /dev/null
+++ b/libmeteor/include/ameteor.hpp
@@ -0,0 +1,80 @@
+// 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 .
+
+#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
diff --git a/libmeteor/include/ameteor/audio/dsound.hpp b/libmeteor/include/ameteor/audio/dsound.hpp
new file mode 100644
index 0000000000..85dc320b13
--- /dev/null
+++ b/libmeteor/include/ameteor/audio/dsound.hpp
@@ -0,0 +1,78 @@
+// 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 .
+
+#ifndef __AUDIO_D_SOUND_H__
+#define __AUDIO_D_SOUND_H__
+
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/audio/sound1.hpp b/libmeteor/include/ameteor/audio/sound1.hpp
new file mode 100644
index 0000000000..282ff2c125
--- /dev/null
+++ b/libmeteor/include/ameteor/audio/sound1.hpp
@@ -0,0 +1,75 @@
+// 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 .
+
+#ifndef __AUDIO_SOUND_1_H__
+#define __AUDIO_SOUND_1_H__
+
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/audio/sound2.hpp b/libmeteor/include/ameteor/audio/sound2.hpp
new file mode 100644
index 0000000000..d4c18d8aaa
--- /dev/null
+++ b/libmeteor/include/ameteor/audio/sound2.hpp
@@ -0,0 +1,70 @@
+// 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 .
+
+#ifndef __AUDIO_SOUND_2_H__
+#define __AUDIO_SOUND_2_H__
+
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/audio/sound4.hpp b/libmeteor/include/ameteor/audio/sound4.hpp
new file mode 100644
index 0000000000..ab51d627d9
--- /dev/null
+++ b/libmeteor/include/ameteor/audio/sound4.hpp
@@ -0,0 +1,78 @@
+// 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 .
+
+#ifndef __AUDIO_SOUND_4_H__
+#define __AUDIO_SOUND_4_H__
+
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/audio/speaker.hpp b/libmeteor/include/ameteor/audio/speaker.hpp
new file mode 100644
index 0000000000..15d37f955b
--- /dev/null
+++ b/libmeteor/include/ameteor/audio/speaker.hpp
@@ -0,0 +1,173 @@
+// 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 .
+
+#ifndef __AUDIO_SPEAKER_H__
+#define __AUDIO_SPEAKER_H__
+
+#include
+#include
+#include
+#include
+#include "sound1.hpp"
+#include "sound2.hpp"
+#include "sound4.hpp"
+#include "dsound.hpp"
+
+namespace AMeteor
+{
+ namespace Audio
+ {
+ class Speaker
+ {
+ public :
+ typedef syg::slot1 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
diff --git a/libmeteor/include/ameteor/bios.hpp b/libmeteor/include/ameteor/bios.hpp
new file mode 100644
index 0000000000..05a3b7db70
--- /dev/null
+++ b/libmeteor/include/ameteor/bios.hpp
@@ -0,0 +1,59 @@
+// 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 .
+
+#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
diff --git a/libmeteor/include/ameteor/cartmem.hpp b/libmeteor/include/ameteor/cartmem.hpp
new file mode 100644
index 0000000000..e7ee43678a
--- /dev/null
+++ b/libmeteor/include/ameteor/cartmem.hpp
@@ -0,0 +1,57 @@
+// 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 .
+
+#ifndef __CART_MEM_H__
+#define __CART_MEM_H__
+
+#include
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/clock.hpp b/libmeteor/include/ameteor/clock.hpp
new file mode 100644
index 0000000000..8d001f962b
--- /dev/null
+++ b/libmeteor/include/ameteor/clock.hpp
@@ -0,0 +1,111 @@
+// 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 .
+
+#ifndef __CLOCK_H__
+#define __CLOCK_H__
+
+#include
+#include
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/cpu.hpp b/libmeteor/include/ameteor/cpu.hpp
new file mode 100644
index 0000000000..3f8e820ab5
--- /dev/null
+++ b/libmeteor/include/ameteor/cpu.hpp
@@ -0,0 +1,148 @@
+// 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 .
+
+#ifndef __CPU_H__
+#define __CPU_H__
+
+#include
+#include
+#include
+
+namespace AMeteor
+{
+ class Cpu
+ {
+ public :
+ union Psr
+ {
+ uint32_t dw;
+ struct
+ {
+ unsigned int mode : 5;
+ bool thumb : 1;
+ bool fiq_d : 1;
+ bool irq_d : 1;
+ unsigned int reserved : 19;
+ bool s_overflow : 1;
+ bool f_overflow : 1;
+ bool f_carry : 1;
+ bool f_zero : 1;
+ bool 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
diff --git a/libmeteor/include/ameteor/disassembler/argimmediate.hpp b/libmeteor/include/ameteor/disassembler/argimmediate.hpp
new file mode 100644
index 0000000000..cb372f1067
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/argimmediate.hpp
@@ -0,0 +1,44 @@
+// 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 .
+
+#ifndef __ARG_IMMEDIATE_H__
+#define __ARG_IMMEDIATE_H__
+
+#include "argument.hpp"
+#include
+
+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
diff --git a/libmeteor/include/ameteor/disassembler/argmulregisters.hpp b/libmeteor/include/ameteor/disassembler/argmulregisters.hpp
new file mode 100644
index 0000000000..73a6c241a1
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/argmulregisters.hpp
@@ -0,0 +1,67 @@
+// 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 .
+
+#ifndef __ARG_MUL_REGISTERS_H__
+#define __ARG_MUL_REGISTERS_H__
+
+#include "argument.hpp"
+
+#include
+#include
+
+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 Registers;
+
+ Registers m_regs;
+ SpecialRegister m_lastreg;
+ bool m_forceuser;
+ };
+ }
+}
+
+#endif
diff --git a/libmeteor/include/ameteor/disassembler/argpsr.hpp b/libmeteor/include/ameteor/disassembler/argpsr.hpp
new file mode 100644
index 0000000000..5557615a99
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/argpsr.hpp
@@ -0,0 +1,47 @@
+// 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 .
+
+#ifndef __ARG_PSR_H__
+#define __ARG_PSR_H__
+
+#include "argument.hpp"
+
+#include
+
+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
diff --git a/libmeteor/include/ameteor/disassembler/argregister.hpp b/libmeteor/include/ameteor/disassembler/argregister.hpp
new file mode 100644
index 0000000000..78a26b44e1
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/argregister.hpp
@@ -0,0 +1,56 @@
+// 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 .
+
+#ifndef __ARG_REGISTER_H__
+#define __ARG_REGISTER_H__
+
+#include "argument.hpp"
+
+#include
+
+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
diff --git a/libmeteor/include/ameteor/disassembler/argrelative.hpp b/libmeteor/include/ameteor/disassembler/argrelative.hpp
new file mode 100644
index 0000000000..c7ccc9e212
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/argrelative.hpp
@@ -0,0 +1,50 @@
+// 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 .
+
+#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
diff --git a/libmeteor/include/ameteor/disassembler/argshift.hpp b/libmeteor/include/ameteor/disassembler/argshift.hpp
new file mode 100644
index 0000000000..1ba586b6d7
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/argshift.hpp
@@ -0,0 +1,56 @@
+// 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 .
+
+#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
diff --git a/libmeteor/include/ameteor/disassembler/arguimmediate.hpp b/libmeteor/include/ameteor/disassembler/arguimmediate.hpp
new file mode 100644
index 0000000000..450322eba3
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/arguimmediate.hpp
@@ -0,0 +1,45 @@
+// 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 .
+
+#ifndef __ARG_U_IMMEDIATE_H__
+#define __ARG_U_IMMEDIATE_H__
+
+#include "argument.hpp"
+
+#include
+
+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
diff --git a/libmeteor/include/ameteor/disassembler/argument.hpp b/libmeteor/include/ameteor/disassembler/argument.hpp
new file mode 100644
index 0000000000..de007d0d7f
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/argument.hpp
@@ -0,0 +1,39 @@
+// 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 .
+
+#ifndef __ARGUMENT_H__
+#define __ARGUMENT_H__
+
+#include
+
+namespace AMeteor
+{
+ namespace Disassembler
+ {
+ class Argument
+ {
+ public :
+ virtual ~Argument ()
+ { }
+
+ virtual Argument* Clone () const = 0;
+
+ virtual std::string GetString () const = 0;
+ };
+ }
+}
+
+#endif
diff --git a/libmeteor/include/ameteor/disassembler/arguments.hpp b/libmeteor/include/ameteor/disassembler/arguments.hpp
new file mode 100644
index 0000000000..dd078da2ef
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/arguments.hpp
@@ -0,0 +1,48 @@
+// 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 .
+
+#ifndef __ARGUMENTS_H__
+#define __ARGUMENTS_H__
+
+#include "argument.hpp"
+
+#include
+
+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 m_args;
+ };
+ }
+}
+
+#endif
diff --git a/libmeteor/include/ameteor/disassembler/instruction.hpp b/libmeteor/include/ameteor/disassembler/instruction.hpp
new file mode 100644
index 0000000000..3da87b8be1
--- /dev/null
+++ b/libmeteor/include/ameteor/disassembler/instruction.hpp
@@ -0,0 +1,76 @@
+// 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 .
+
+#ifndef __INSTRUCTION_H__
+#define __INSTRUCTION_H__
+
+#include "arguments.hpp"
+
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/dma.hpp b/libmeteor/include/ameteor/dma.hpp
new file mode 100644
index 0000000000..571c44c27a
--- /dev/null
+++ b/libmeteor/include/ameteor/dma.hpp
@@ -0,0 +1,112 @@
+// 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 .
+
+#ifndef __DMA_H__
+#define __DMA_H__
+
+#include
+#include
+#include
+
+namespace AMeteor
+{
+ class Dma
+ {
+ public :
+ enum Reason
+ {
+ Immediately = 0,
+ VBlank,
+ HBlank,
+ Special
+ };
+
+ Dma () :
+ m_graphic(false)
+ { }
+
+ void Reset ();
+
+ bool GraphicDma () const
+ {
+ 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
diff --git a/libmeteor/include/ameteor/eeprom.hpp b/libmeteor/include/ameteor/eeprom.hpp
new file mode 100644
index 0000000000..8052a5b393
--- /dev/null
+++ b/libmeteor/include/ameteor/eeprom.hpp
@@ -0,0 +1,79 @@
+// 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 .
+
+#ifndef __EEPROM_H__
+#define __EEPROM_H__
+
+#include "cartmem.hpp"
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/flash.hpp b/libmeteor/include/ameteor/flash.hpp
new file mode 100644
index 0000000000..3c56d89747
--- /dev/null
+++ b/libmeteor/include/ameteor/flash.hpp
@@ -0,0 +1,63 @@
+// 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 .
+
+#ifndef __FLASH_H__
+#define __FLASH_H__
+
+#include "cartmem.hpp"
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/graphics/bglayer.hpp b/libmeteor/include/ameteor/graphics/bglayer.hpp
new file mode 100644
index 0000000000..1409929041
--- /dev/null
+++ b/libmeteor/include/ameteor/graphics/bglayer.hpp
@@ -0,0 +1,97 @@
+// 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 .
+
+#ifndef __GRAPHICS_BG_LAYER_H__
+#define __GRAPHICS_BG_LAYER_H__
+
+#include "ameteor/memory.hpp"
+#include "ameteor/io.hpp"
+
+#include
+#include
+
+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 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
diff --git a/libmeteor/include/ameteor/graphics/object.hpp b/libmeteor/include/ameteor/graphics/object.hpp
new file mode 100644
index 0000000000..c1611c149b
--- /dev/null
+++ b/libmeteor/include/ameteor/graphics/object.hpp
@@ -0,0 +1,100 @@
+// 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 .
+
+#ifndef __GRAPHICS_OBJECT_H__
+#define __GRAPHICS_OBJECT_H__
+
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/graphics/objects.hpp b/libmeteor/include/ameteor/graphics/objects.hpp
new file mode 100644
index 0000000000..a398080bd7
--- /dev/null
+++ b/libmeteor/include/ameteor/graphics/objects.hpp
@@ -0,0 +1,53 @@
+// 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 .
+
+#ifndef __GRAPHICS_OBJECTS_H__
+#define __GRAPHICS_OBJECTS_H__
+
+#include "object.hpp"
+#include "ameteor/memory.hpp"
+#include "ameteor/io.hpp"
+
+#include
+
+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 Objs;
+
+ Io& m_io;
+ Objs m_objs;
+ uint16_t* m_pOam;
+ };
+ }
+}
+
+#endif
diff --git a/libmeteor/include/ameteor/graphics/renderer.hpp b/libmeteor/include/ameteor/graphics/renderer.hpp
new file mode 100644
index 0000000000..d94b63c253
--- /dev/null
+++ b/libmeteor/include/ameteor/graphics/renderer.hpp
@@ -0,0 +1,52 @@
+// 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 .
+
+#ifndef __GRAPHICS_RENDERER_H__
+#define __GRAPHICS_RENDERER_H__
+
+#include
+
+#include
+
+namespace AMeteor
+{
+ namespace Graphics
+ {
+ class Renderer
+ {
+ public:
+ typedef syg::slot1 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
diff --git a/libmeteor/include/ameteor/graphics/screen.hpp b/libmeteor/include/ameteor/graphics/screen.hpp
new file mode 100644
index 0000000000..4482a7ac68
--- /dev/null
+++ b/libmeteor/include/ameteor/graphics/screen.hpp
@@ -0,0 +1,148 @@
+// 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 .
+
+#ifndef __GRAPHICS_SCREEN_H__
+#define __GRAPHICS_SCREEN_H__
+
+#include "bglayer.hpp"
+#include "objects.hpp"
+#include "renderer.hpp"
+
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/interpreter.hpp b/libmeteor/include/ameteor/interpreter.hpp
new file mode 100644
index 0000000000..5f02df4686
--- /dev/null
+++ b/libmeteor/include/ameteor/interpreter.hpp
@@ -0,0 +1,133 @@
+// 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 .
+
+#ifndef __INTERPRETER_H__
+#define __INTERPRETER_H__
+
+#include
+#include
+#include
+#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
diff --git a/libmeteor/include/ameteor/io.hpp b/libmeteor/include/ameteor/io.hpp
new file mode 100644
index 0000000000..5f553c016c
--- /dev/null
+++ b/libmeteor/include/ameteor/io.hpp
@@ -0,0 +1,216 @@
+// 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 .
+
+#ifndef __IO_H__
+#define __IO_H__
+
+#include
+#include
+#include
+
+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);
+
+ private :
+ uint8_t* m_iomem;
+ };
+}
+
+#endif
diff --git a/libmeteor/include/ameteor/keypad.hpp b/libmeteor/include/ameteor/keypad.hpp
new file mode 100644
index 0000000000..9c6493c55b
--- /dev/null
+++ b/libmeteor/include/ameteor/keypad.hpp
@@ -0,0 +1,105 @@
+// 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 .
+
+#ifndef __KEYPAD_H__
+#define __KEYPAD_H__
+
+#include
+#include
+
+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 m_keys;
+ std::map m_joys;
+ std::map m_axis;
+ };
+
+ void Keypad::SetPadState(uint16_t keys)
+ {
+ m_keyinput = keys;
+ }
+}
+
+#endif
diff --git a/libmeteor/include/ameteor/lcd.hpp b/libmeteor/include/ameteor/lcd.hpp
new file mode 100644
index 0000000000..0068aad295
--- /dev/null
+++ b/libmeteor/include/ameteor/lcd.hpp
@@ -0,0 +1,129 @@
+// 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 .
+
+#ifndef __LCD_H__
+#define __LCD_H__
+
+#include "clock.hpp"
+
+#include "memory.hpp"
+#include "io.hpp"
+#include "graphics/screen.hpp"
+
+#include
+#include
+#include
+
+#include
+
+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 sig_vblank;
+
+ private :
+ Graphics::Screen m_screen;
+
+ void TimeEvent ();
+
+ friend void Clock::Commit();
+ };
+}
+
+#endif
diff --git a/libmeteor/include/ameteor/memory.hpp b/libmeteor/include/ameteor/memory.hpp
new file mode 100644
index 0000000000..b3b919aaee
--- /dev/null
+++ b/libmeteor/include/ameteor/memory.hpp
@@ -0,0 +1,149 @@
+// 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 .
+
+#ifndef __MEMORY_H__
+#define __MEMORY_H__
+
+#include "cartmem.hpp"
+//XXX
+#include "eeprom.hpp"
+
+#include
+#include
+#include
+#include
+
+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 ();
+#ifdef __LIBRETRO__
+ bool LoadCartInferred ();
+#endif
+
+ 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);
+
+ // 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 ();
+
+ 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
diff --git a/libmeteor/include/ameteor/sound.hpp b/libmeteor/include/ameteor/sound.hpp
new file mode 100644
index 0000000000..5575048970
--- /dev/null
+++ b/libmeteor/include/ameteor/sound.hpp
@@ -0,0 +1,117 @@
+// 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 .
+
+#ifndef __SOUND_H__
+#define __SOUND_H__
+
+#include "audio/speaker.hpp"
+#include "clock.hpp"
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/sram.hpp b/libmeteor/include/ameteor/sram.hpp
new file mode 100644
index 0000000000..d937d75df7
--- /dev/null
+++ b/libmeteor/include/ameteor/sram.hpp
@@ -0,0 +1,56 @@
+// 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 .
+
+#ifndef __SRAM_H__
+#define __SRAM_H__
+
+#include "cartmem.hpp"
+#include
+#include
+#include
+#include
+
+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
diff --git a/libmeteor/include/ameteor/swap.hpp b/libmeteor/include/ameteor/swap.hpp
new file mode 100644
index 0000000000..ec9d992626
--- /dev/null
+++ b/libmeteor/include/ameteor/swap.hpp
@@ -0,0 +1,36 @@
+// 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 .
+
+#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
diff --git a/libmeteor/include/ameteor/timer.hpp b/libmeteor/include/ameteor/timer.hpp
new file mode 100644
index 0000000000..4d51730258
--- /dev/null
+++ b/libmeteor/include/ameteor/timer.hpp
@@ -0,0 +1,86 @@
+// 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 .
+
+#ifndef __TIMER_H__
+#define __TIMER_H__
+
+#include "clock.hpp"
+
+#include
+#include
+#include
+
+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;
+ bool countup : 1;
+ unsigned int unused1 : 3;
+ bool irq : 1;
+ bool 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
diff --git a/libmeteor/include/syg/signal.hpp b/libmeteor/include/syg/signal.hpp
new file mode 100644
index 0000000000..2a3dab12d8
--- /dev/null
+++ b/libmeteor/include/syg/signal.hpp
@@ -0,0 +1,389 @@
+#ifndef SYG_connection_HPP
+#define SYG_connection_HPP
+
+#include
+
+namespace syg
+{
+
+//------------------------------
+// connections
+//------------------------------
+
+template
+class connection_base
+{
+ public:
+ virtual Tret call() const = 0;
+ virtual connection_base* clone() const = 0;
+ virtual ~connection_base() {}
+};
+
+template
+class connection_base1
+{
+ public:
+ virtual Tret call(Targ0) const = 0;
+ virtual connection_base1* clone() const = 0;
+ virtual ~connection_base1() {}
+};
+
+template
+class connection_func : public connection_base
+{
+ public:
+ typedef Tret (*FuncPtr)();
+
+ connection_func(FuncPtr ptr):
+ _func(ptr)
+ {}
+
+ Tret call() const
+ {
+ return _func();
+ }
+
+ connection_base* clone() const
+ {
+ return new connection_func(*this);
+ }
+
+ private:
+ FuncPtr _func;
+};
+
+template
+class connection_func1 : public connection_base1
+{
+ public:
+ typedef Tret (*FuncPtr)(Targ0);
+
+ connection_func1(FuncPtr ptr):
+ _func(ptr)
+ {}
+
+ Tret call(Targ0 arg0) const
+ {
+ return _func(arg0);
+ }
+
+ connection_base1* clone() const
+ {
+ return new connection_func1(*this);
+ }
+
+ private:
+ FuncPtr _func;
+};
+
+template
+class connection_meth : public connection_base
+{
+ 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* clone() const
+ {
+ return new connection_meth(*this);
+ }
+
+ private:
+ Tobj& _obj;
+ FuncPtr _meth;
+};
+
+template
+class connection_meth1 : public connection_base1
+{
+ 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* clone() const
+ {
+ return new connection_meth1(*this);
+ }
+
+ private:
+ Tobj& _obj;
+ FuncPtr _meth;
+};
+
+//------------------------------
+// slots
+//------------------------------
+
+template
+class slot;
+
+template
+class slot1;
+
+template
+slot ptr_fun(Tret (*fun)());
+
+template
+slot mem_fun(Tobj& obj, Tret (Tobj::*fun)());
+
+template
+slot1 ptr_fun(Tret (*fun)(Targ0));
+
+template
+slot1 mem_fun(Tobj& obj, Tret (Tobj::*fun)(Targ0));
+
+template
+class slot
+{
+ public:
+ slot():
+ _conn(0)
+ {}
+ slot(const slot& s)
+ {
+ *this = s;
+ }
+ ~slot()
+ {
+ delete _conn;
+ }
+
+ slot& operator=(const slot& 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* _conn;
+
+ slot(const connection_base& conn):
+ _conn(conn.clone())
+ {}
+
+ friend slot ptr_fun(Tret (*fun)());
+
+ template
+ friend slot mem_fun(Tobj2& obj, Tret2 (Tobj2::*fun)());
+};
+
+template
+class slot1
+{
+ public:
+ typedef slot1 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 Connection;
+
+ Connection* _conn;
+
+ slot1(const Connection& conn):
+ _conn(conn.clone())
+ {}
+
+ friend SlotType ptr_fun(Tret (*fun)(Targ0));
+
+ template
+ friend slot1 mem_fun(Tobj2& obj,
+ Tret2 (Tobj2::*fun)(Targ02));
+};
+
+template
+inline slot ptr_fun(Tret (*fun)())
+{
+ return slot(connection_func(fun));
+}
+
+template
+inline slot mem_fun(Tobj& obj, Tret (Tobj::*fun)())
+{
+ return slot(connection_meth(obj, fun));
+}
+
+template
+inline slot1 ptr_fun(Tret (*fun)(Targ0))
+{
+ return slot1(connection_func1(fun));
+}
+
+template
+inline slot1 mem_fun(Tobj& obj, Tret (Tobj::*fun)(Targ0))
+{
+ return slot1(connection_meth1(obj, fun));
+}
+
+//------------------------------
+// signals
+//------------------------------
+/*
+template
+class signal;
+
+template
+class connection
+{
+ public:
+ connection():
+ _list(0)
+ {
+ }
+
+ void disconnect()
+ {
+ _list->erase(_iter);
+ }
+
+ private:
+ typedef std::list > List;
+ typedef typename List::iterator Iterator;
+
+ List* _list;
+ Iterator _iter;
+
+ connection(List* list, Iterator iter):
+ _list(list),
+ _iter(iter)
+ {}
+
+ friend class signal;
+};
+
+template
+class signal
+{
+ public:
+ connection connect(const slot s)
+ {
+ _slots.push_back(s);
+ return connection(&_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 > Slots;
+
+ Slots _slots;
+};
+
+template
+class signal1
+{
+ public:
+ void connect(const slot 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 > Slots;
+
+ Slots _slots;
+};
+*/
+} // namespace syg
+
+#endif
diff --git a/libmeteor/source/ameteor.cpp b/libmeteor/source/ameteor.cpp
new file mode 100644
index 0000000000..76d9b187e4
--- /dev/null
+++ b/libmeteor/source/ameteor.cpp
@@ -0,0 +1,204 @@
+// 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 .
+
+#include "ameteor.hpp"
+#include "debug.hpp"
+#include "globals.hpp"
+#include
+#include
+
+// 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 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;
+ }
+}
diff --git a/libmeteor/source/audio/dsound.cpp b/libmeteor/source/audio/dsound.cpp
new file mode 100644
index 0000000000..1f06f6a690
--- /dev/null
+++ b/libmeteor/source/audio/dsound.cpp
@@ -0,0 +1,82 @@
+// 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 .
+
+#include "ameteor/audio/dsound.hpp"
+#include "../globals.hpp"
+#include
+
+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;
+ }
+ }
+}
diff --git a/libmeteor/source/audio/sound1.cpp b/libmeteor/source/audio/sound1.cpp
new file mode 100644
index 0000000000..da7b587e51
--- /dev/null
+++ b/libmeteor/source/audio/sound1.cpp
@@ -0,0 +1,202 @@
+// 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 .
+
+#include "ameteor/audio/sound1.hpp"
+#include "../globals.hpp"
+#include
+
+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;
+ }
+ }
+}
diff --git a/libmeteor/source/audio/sound2.cpp b/libmeteor/source/audio/sound2.cpp
new file mode 100644
index 0000000000..fe1dd05a4b
--- /dev/null
+++ b/libmeteor/source/audio/sound2.cpp
@@ -0,0 +1,145 @@
+// 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 .
+
+#include "ameteor/audio/sound2.hpp"
+#include "../globals.hpp"
+#include
+
+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;
+ }
+ }
+}
diff --git a/libmeteor/source/audio/sound4.cpp b/libmeteor/source/audio/sound4.cpp
new file mode 100644
index 0000000000..ef963ecd6e
--- /dev/null
+++ b/libmeteor/source/audio/sound4.cpp
@@ -0,0 +1,185 @@
+// 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 .
+
+#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;
+ }
+ }
+}
diff --git a/libmeteor/source/audio/speaker.cpp b/libmeteor/source/audio/speaker.cpp
new file mode 100644
index 0000000000..13e6f1a3d3
--- /dev/null
+++ b/libmeteor/source/audio/speaker.cpp
@@ -0,0 +1,169 @@
+// 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 .
+
+#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;
+ }
+ }
+}
diff --git a/libmeteor/source/bios.cpp b/libmeteor/source/bios.cpp
new file mode 100644
index 0000000000..2480fb692c
--- /dev/null
+++ b/libmeteor/source/bios.cpp
@@ -0,0 +1,919 @@
+// 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 .
+
+#include "ameteor/bios.hpp"
+#include "ameteor/cpu.hpp"
+#include "ameteor/memory.hpp"
+
+#include "globals.hpp"
+
+#include "debug.hpp"
+
+#include
+
+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;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/libmeteor/source/cartmem.cpp b/libmeteor/source/cartmem.cpp
new file mode 100644
index 0000000000..37fa9d78ad
--- /dev/null
+++ b/libmeteor/source/cartmem.cpp
@@ -0,0 +1,57 @@
+// 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 .
+
+#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;
+ }
+}
diff --git a/libmeteor/source/clock.cpp b/libmeteor/source/clock.cpp
new file mode 100644
index 0000000000..7f21980ed3
--- /dev/null
+++ b/libmeteor/source/clock.cpp
@@ -0,0 +1,123 @@
+// 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 .
+
+#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)
+ {
+ 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;
+ }
+}
diff --git a/libmeteor/source/cpu.cpp b/libmeteor/source/cpu.cpp
new file mode 100644
index 0000000000..bb405b709a
--- /dev/null
+++ b/libmeteor/source/cpu.cpp
@@ -0,0 +1,328 @@
+// 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 .
+
+#include "ameteor/cpu.hpp"
+#include "ameteor/bios.hpp"
+#include "globals.hpp"
+#include "cpu_globals.hpp"
+#include "ameteor.hpp"
+
+#include "debug.hpp"
+
+#include
+
+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 (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;
+ }
+}
diff --git a/libmeteor/source/cpu_globals.hpp b/libmeteor/source/cpu_globals.hpp
new file mode 100644
index 0000000000..e0f73bdd77
--- /dev/null
+++ b/libmeteor/source/cpu_globals.hpp
@@ -0,0 +1,41 @@
+// 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 .
+
+#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
diff --git a/libmeteor/source/debug.cpp b/libmeteor/source/debug.cpp
new file mode 100644
index 0000000000..50365d1a59
--- /dev/null
+++ b/libmeteor/source/debug.cpp
@@ -0,0 +1,85 @@
+// 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 .
+
+#include "debug.hpp"
+#include "ameteor/cpu.hpp"
+#include "globals.hpp"
+#include "ameteor.hpp"
+
+#include
+#include
+
+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
+}
diff --git a/libmeteor/source/debug.hpp b/libmeteor/source/debug.hpp
new file mode 100644
index 0000000000..350cacd45c
--- /dev/null
+++ b/libmeteor/source/debug.hpp
@@ -0,0 +1,92 @@
+// 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 .
+
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+#include
+#include
+#include
+#include
+
+// for abort macro
+#include "ameteor.hpp"
+
+#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
+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; \
+ MessageBoxA(NULL, _zisrny.str().c_str(), "FUCK!", 0); \
+ }
+
+#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) \
+ 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_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
diff --git a/libmeteor/source/disassembler/argimmediate.cpp b/libmeteor/source/disassembler/argimmediate.cpp
new file mode 100644
index 0000000000..cf17a5ccf1
--- /dev/null
+++ b/libmeteor/source/disassembler/argimmediate.cpp
@@ -0,0 +1,39 @@
+// 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 .
+
+#include "ameteor/disassembler/argimmediate.hpp"
+
+#include
+#include
+
+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);
+ }
+ }
+}
diff --git a/libmeteor/source/disassembler/argmulregisters.cpp b/libmeteor/source/disassembler/argmulregisters.cpp
new file mode 100644
index 0000000000..5e0bae0822
--- /dev/null
+++ b/libmeteor/source/disassembler/argmulregisters.cpp
@@ -0,0 +1,83 @@
+// 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 .
+
+#include "ameteor/disassembler/argmulregisters.hpp"
+
+#include
+
+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);
+ }
+ }
+}
diff --git a/libmeteor/source/disassembler/argpsr.cpp b/libmeteor/source/disassembler/argpsr.cpp
new file mode 100644
index 0000000000..c03a5e20d6
--- /dev/null
+++ b/libmeteor/source/disassembler/argpsr.cpp
@@ -0,0 +1,53 @@
+// 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 .
+
+#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);
+ }
+ }
+}
diff --git a/libmeteor/source/disassembler/argregister.cpp b/libmeteor/source/disassembler/argregister.cpp
new file mode 100644
index 0000000000..5b00ba1937
--- /dev/null
+++ b/libmeteor/source/disassembler/argregister.cpp
@@ -0,0 +1,53 @@
+// 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 .
+
+#include "ameteor/disassembler/argregister.hpp"
+
+#include
+
+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);
+ }
+ }
+}
diff --git a/libmeteor/source/disassembler/argrelative.cpp b/libmeteor/source/disassembler/argrelative.cpp
new file mode 100644
index 0000000000..16355d104c
--- /dev/null
+++ b/libmeteor/source/disassembler/argrelative.cpp
@@ -0,0 +1,71 @@
+// 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 .
+
+#include "ameteor/disassembler/argrelative.hpp"
+
+#include
+
+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();
+ }
+ }
+}
diff --git a/libmeteor/source/disassembler/argshift.cpp b/libmeteor/source/disassembler/argshift.cpp
new file mode 100644
index 0000000000..5fa4522537
--- /dev/null
+++ b/libmeteor/source/disassembler/argshift.cpp
@@ -0,0 +1,71 @@
+// 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 .
+
+#include "ameteor/disassembler/argshift.hpp"
+
+#include
+
+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();
+ }
+ }
+}
diff --git a/libmeteor/source/disassembler/arguimmediate.cpp b/libmeteor/source/disassembler/arguimmediate.cpp
new file mode 100644
index 0000000000..9d4ce1fd05
--- /dev/null
+++ b/libmeteor/source/disassembler/arguimmediate.cpp
@@ -0,0 +1,39 @@
+// 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 .
+
+#include "ameteor/disassembler/arguimmediate.hpp"
+
+#include
+#include
+
+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);
+ }
+ }
+}
diff --git a/libmeteor/source/disassembler/arguments.cpp b/libmeteor/source/disassembler/arguments.cpp
new file mode 100644
index 0000000000..714ac9a881
--- /dev/null
+++ b/libmeteor/source/disassembler/arguments.cpp
@@ -0,0 +1,51 @@
+// 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 .
+
+#include "ameteor/disassembler/arguments.hpp"
+
+namespace AMeteor
+{
+ namespace Disassembler
+ {
+ Arguments::~Arguments ()
+ {
+ Clear();
+ }
+
+ void Arguments::Clear ()
+ {
+ for (std::vector::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::const_iterator iter = m_args.begin();
+ iter != m_args.end(); ++iter)
+ {
+ if (!out.empty())
+ out += ", ";
+ out += (*iter)->GetString();
+ }
+ return out;
+ }
+ }
+}
diff --git a/libmeteor/source/disassembler/instruction.cpp b/libmeteor/source/disassembler/instruction.cpp
new file mode 100644
index 0000000000..eef4248219
--- /dev/null
+++ b/libmeteor/source/disassembler/instruction.cpp
@@ -0,0 +1,691 @@
+// 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 .
+
+#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
+ }
+}
diff --git a/libmeteor/source/dma.cpp b/libmeteor/source/dma.cpp
new file mode 100644
index 0000000000..15a3a6d1ec
--- /dev/null
+++ b/libmeteor/source/dma.cpp
@@ -0,0 +1,307 @@
+// 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 .
+
+#include "ameteor/dma.hpp"
+#include "ameteor/io.hpp"
+#include "ameteor/memory.hpp"
+#include "globals.hpp"
+#include "ameteor.hpp"
+
+#include "debug.hpp"
+
+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 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;
+ }
+}
diff --git a/libmeteor/source/eeprom.cpp b/libmeteor/source/eeprom.cpp
new file mode 100644
index 0000000000..3dd99f07d3
--- /dev/null
+++ b/libmeteor/source/eeprom.cpp
@@ -0,0 +1,272 @@
+// 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 .
+
+#include "ameteor/eeprom.hpp"
+#include "globals.hpp"
+#include "debug.hpp"
+
+#include
+
+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;
+ }
+}
diff --git a/libmeteor/source/flash.cpp b/libmeteor/source/flash.cpp
new file mode 100644
index 0000000000..1493cc0715
--- /dev/null
+++ b/libmeteor/source/flash.cpp
@@ -0,0 +1,178 @@
+// 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 .
+
+#include "ameteor/flash.hpp"
+#include "globals.hpp"
+#include
+#include
+
+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;
+ }
+}
diff --git a/libmeteor/source/globals.hpp b/libmeteor/source/globals.hpp
new file mode 100644
index 0000000000..393b597438
--- /dev/null
+++ b/libmeteor/source/globals.hpp
@@ -0,0 +1,140 @@
+// 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 .
+
+#ifndef __GLOBALS_H__
+#define __GLOBALS_H__
+
+#include
+#include
+
+#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
diff --git a/libmeteor/source/graphics/bglayer.cpp b/libmeteor/source/graphics/bglayer.cpp
new file mode 100644
index 0000000000..72eea709c9
--- /dev/null
+++ b/libmeteor/source/graphics/bglayer.cpp
@@ -0,0 +1,451 @@
+// 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 .
+
+#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 (mosH && i % mosH)
+ *ptr = ptr[-1];
+ else
+ {
+ 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 (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;
+
+ uint8_t* pChar = 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 * 2 + intX * 2] | 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::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;
+ }
+ }
+}
diff --git a/libmeteor/source/graphics/object.cpp b/libmeteor/source/graphics/object.cpp
new file mode 100644
index 0000000000..bfcaeaa0c9
--- /dev/null
+++ b/libmeteor/source/graphics/object.cpp
@@ -0,0 +1,680 @@
+// 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 .
+
+#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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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[3][4] = {
+ {1, 2, 4, 8}, // Square
+ {2, 4, 4, 8}, // Horizontal
+ {1, 1, 2, 4} // Vertical
+ };
+ static const uint8_t Height[3][4] = {
+ {1, 2, 4, 8}, // Square
+ {1, 1, 2, 4}, // Horizontal
+ {2, 4, 4, 8} // Vertical
+ };
+
+ m_width = Width[m_attr0 >> 14][m_attr1 >> 14];
+ m_height = Height[m_attr0 >> 14][m_attr1 >> 14];
+ }
+ }
+}
diff --git a/libmeteor/source/graphics/objects.cpp b/libmeteor/source/graphics/objects.cpp
new file mode 100644
index 0000000000..6de598a3c7
--- /dev/null
+++ b/libmeteor/source/graphics/objects.cpp
@@ -0,0 +1,168 @@
+// 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 .
+
+#include "ameteor/graphics/objects.hpp"
+#include "../debug.hpp"
+#include
+
+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;
+ }
+ }
+ }
+}
diff --git a/libmeteor/source/graphics/renderer.cpp b/libmeteor/source/graphics/renderer.cpp
new file mode 100644
index 0000000000..2d689aaf08
--- /dev/null
+++ b/libmeteor/source/graphics/renderer.cpp
@@ -0,0 +1,35 @@
+// 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 .
+
+#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);
+ }
+ }
+}
diff --git a/libmeteor/source/graphics/screen.cpp b/libmeteor/source/graphics/screen.cpp
new file mode 100644
index 0000000000..4c5628b33a
--- /dev/null
+++ b/libmeteor/source/graphics/screen.cpp
@@ -0,0 +1,402 @@
+// 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 .
+
+#include "ameteor/graphics/screen.hpp"
+#include "../globals.hpp"
+#include "../debug.hpp"
+#include "ameteor.hpp"
+
+#include
+
+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;
+ // 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;
+ 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;
+ }
+ }
+}
diff --git a/libmeteor/source/interpreter.cpp b/libmeteor/source/interpreter.cpp
new file mode 100644
index 0000000000..bd33e34267
--- /dev/null
+++ b/libmeteor/source/interpreter.cpp
@@ -0,0 +1,193 @@
+// 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 .
+
+#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"
+
+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);
+ //std::cerr << IOS_ADD << R(15) << ' ' << Disassembler::Instruction(R(15), (uint16_t)code).ToString() << std::endl;
+ 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);
+ //std::cerr << IOS_ADD << R(15) << ' ' << Disassembler::Instruction(R(15), (uint32_t)code).ToString() << std::endl;
+ 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);
+ }
+}
diff --git a/libmeteor/source/interpreter_arm.cpp b/libmeteor/source/interpreter_arm.cpp
new file mode 100644
index 0000000000..731972848d
--- /dev/null
+++ b/libmeteor/source/interpreter_arm.cpp
@@ -0,0 +1,1477 @@
+// 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 .
+
+#ifndef __INTERPRETER_ARM_H__
+#define __INTERPRETER_ARM_H__
+
+/*
+
+- From GBATEK -
+
+ARM Binary Opcode Format
+
+|..3 ..................2 ..................1 ..................0|
+|1_0_9_8_7_6_5_4_3_2_1_0_9_8_7_6_5_4_3_2_1_0_9_8_7_6_5_4_3_2_1_0|
+|_Cond__|0_0_0|___Op__|S|__Rn___|__Rd___|__Shift__|Typ|0|__Rm___| DataProc
+|_Cond__|0_0_0|___Op__|S|__Rn___|__Rd___|__Rs___|0|Typ|1|__Rm___| DataProc
+|_Cond__|0_0_1|___Op__|S|__Rn___|__Rd___|_Shift_|___Immediate___| DataProc
+|_Cond__|0_0_1_1_0|P|1|0|_Field_|__Rd___|_Shift_|___Immediate___| PSR Imm
+|_Cond__|0_0_0_1_0|P|L|0|_Field_|__Rd___|0_0_0_0|0_0_0_0|__Rm___| PSR Reg
+|_Cond__|0_0_0_1_0_0_1_0_1_1_1_1_1_1_1_1_1_1_1_1|0_0|L|1|__Rn___| BX,BLX
+|1_1_1_0|0_0_0_1_0_0_1_0|_____immediate_________|0_1_1_1|_immed_| BKPT ARM9
+|_Cond__|0_0_0_1_0_1_1_0_1_1_1_1|__Rd___|1_1_1_1|0_0_0_1|__Rm___| CLZ ARM9
+|_Cond__|0_0_0_1_0|Op_|0|__Rn___|__Rd___|0_0_0_0|0_1_0_1|__Rm___| QALU ARM9
+|_Cond__|0_0_0_0_0_0|A|S|__Rd___|__Rn___|__Rs___|1_0_0_1|__Rm___| Multiply
+|_Cond__|0_0_0_0_1|U|A|S|_RdHi__|_RdLo__|__Rs___|1_0_0_1|__Rm___| MulLong
+|_Cond__|0_0_0_1_0|Op_|0|Rd/RdHi|Rn/RdLo|__Rs___|1|y|x|0|__Rm___| MulHalf
+|_Cond__|0_0_0_1_0|B|0_0|__Rn___|__Rd___|0_0_0_0|1_0_0_1|__Rm___| TransSwp12
+|_Cond__|0_0_0|P|U|0|W|L|__Rn___|__Rd___|0_0_0_0|1|S|H|1|__Rm___| TransReg10
+|_Cond__|0_0_0|P|U|1|W|L|__Rn___|__Rd___|OffsetH|1|S|H|1|OffsetL| TransImm10
+|_Cond__|0_1_0|P|U|B|W|L|__Rn___|__Rd___|_________Offset________| TransImm9
+|_Cond__|0_1_1|P|U|B|W|L|__Rn___|__Rd___|__Shift__|Typ|0|__Rm___| TransReg9
+|_Cond__|0_1_1|________________xxx____________________|1|__xxx__| Undefined
+|_Cond__|1_0_0|P|U|S|W|L|__Rn___|__________Register_List________| BlockTrans
+|_Cond__|1_0_1|L|___________________Offset______________________| B,BL,BLX
+|_Cond__|1_1_0|P|U|N|W|L|__Rn___|__CRd__|__CP#__|____Offset_____| CoDataTrans
+|_Cond__|1_1_0_0_0_1_0|L|__Rn___|__Rd___|__CP#__|_CPopc_|__CRm__| CoRR ARM9
+|_Cond__|1_1_1_0|_CPopc_|__CRn__|__CRd__|__CP#__|_CP__|0|__CRm__| CoDataOp
+|_Cond__|1_1_1_0|CPopc|L|__CRn__|__Rd___|__CP#__|_CP__|1|__CRm__| CoRegTrans
+|_Cond__|1_1_1_1|_____________Ignored_by_Processor______________| SWI
+
+*/
+
+#include "ameteor/interpreter.hpp"
+#include "globals.hpp"
+#include "cpu_globals.hpp"
+#include "ameteor.hpp"
+
+#include "debug.hpp"
+
+#define Rn ((code >> 16) & 0xF)
+#define Rd ((code >> 12) & 0xF)
+#define Rs ((code >> 8) & 0xF)
+#define Rm (code & 0xF)
+
+// Load/Store immediate offset
+#define LSOff (code & 0xFFF)
+
+#ifdef METDEBUG
+# define NOT_PC(reg) \
+ if (reg == 15) \
+ met_abort("Register is PC")
+# define NOT_PC_ALL() \
+ NOT_PC(Rn); \
+ NOT_PC(Rd); \
+ NOT_PC(Rs); \
+ NOT_PC(Rm)
+# define NOT_SAME2(reg1, reg2) \
+ if (reg1 == reg2) \
+ met_abort("Two same registers")
+# define NOT_SAME3(reg1, reg2, reg3) \
+ NOT_SAME2(reg1, reg2); \
+ NOT_SAME2(reg2, reg3); \
+ NOT_SAME2(reg1, reg3)
+#else
+# define NOT_PC(reg) {}
+# define NOT_PC_ALL() {}
+# define NOT_SAME2(reg1, reg2) {}
+# define NOT_SAME3(reg1, reg2, reg3) {}
+#endif
+
+#define ARM(name) \
+ inline void Interpreter::a##name ()
+#define NIARM(name) \
+ void Interpreter::a##name ()
+
+namespace AMeteor
+{
+ // Branch and Exchange
+ ARM(BXBLX)
+ {
+ if ((code & 0x0FFFFF00) != 0x012FFF00)
+ met_abort("Bits 8-27 must be 0001 00101111 11111111 for BX/BLX instructions");
+
+ if (Rm == 15)
+ met_abort("Branching on PC is undefined");
+
+ if (code & (0x1 << 5)) // BLX
+ {
+ R(14) = R(15);
+ met_abort("BLX not completly implemented");
+ }
+ // else BX
+
+ if (R(Rm) & 0x1)
+ {
+ FLAG_T = 1;
+ R(15) = R(Rm) + 1;
+ CYCLES32NSeq(R(15), 3);
+ }
+ else
+ {
+ if (R(Rm) & 0x3)
+ met_abort("BX with ARM on non 32 bit aligned address");
+ R(15) = R(Rm)+4;
+ CYCLES32NSeq(R(15), 3);
+ }
+ }
+
+ // Branch and Branch with Link
+ ARM(BBL)
+ {
+ if (((code >> 25) & 0x7) != 0x5)
+ met_abort("Bits 25-27 must be 101 for B/BL/BLX instructions");
+
+ if (((code >> 28) & 0xF) == 0xF)
+ met_abort("BLX not implemented");
+
+ int32_t off = code & 0x00FFFFFF;
+ // Extend the sign bit
+ off <<= 8;
+ off >>= 8;
+ // off is in steps of 4
+ off *= 4;
+
+ if (code & (0x1 << 24)) // BL
+ {
+ // R(15) points two instructions later, R(14) should point to next
+ // instruction
+ // LR = PC + 4 and R15 = PC + 8
+ R(14) = R(15) - 4 ;
+ }
+ // else B
+ R(15) += off + 4;
+ CYCLES32NSeq(R(15), 3);
+ }
+
+ // Data Processing
+ NIARM(_DataProcShiftImm)
+ {
+ if ((code >> 26) & 0x3)
+ met_abort("Bits 26-27 must be 00 for DataProc instructions");
+
+ uint8_t shift;
+ uint32_t op2 = 0; // to avoid a warning
+ bool shiftcarry = FLAG_C;
+ uint8_t rd = Rd;
+
+ uint8_t rm = Rm;
+ if (rm == 15 && !(code & (0x1 << 20)) && (code & (0x1 << 4)))
+ met_abort("Rm = 15, not implemented");
+
+ shift = (code >> 7) & 0x1F;
+ switch ((code >> 5) & 0x3)
+ {
+ case 0: // Logical Shift Left
+ if (shift)
+ {
+ op2 = R(rm) << shift;
+ shiftcarry = (R(rm) >> (32-shift)) & 0x1;
+ }
+ else // LSL#0
+ op2 = R(rm);
+ break;
+ case 1: // Logical Shift Right
+ if (shift)
+ {
+ op2 = R(rm) >> shift;
+ shiftcarry = (R(rm) >> (shift-1)) & 0x1;
+ }
+ else // LSR#32
+ {
+ op2 = 0;
+ shiftcarry = R(rm) >> 31;
+ }
+ break;
+ case 2: // Arithmetic Shift Right
+ if (shift)
+ {
+ op2 = ((int32_t)R(rm)) >> shift;
+ shiftcarry = (((int32_t)R(rm)) >> (shift-1)) & 0x1;
+ }
+ else // ASR#32
+ {
+ op2 = ((int32_t)R(rm)) >> 31;
+ shiftcarry = op2 & 0x1;
+ }
+ break;
+ case 3: // ROtate Right
+ if (shift)
+ {
+ op2 = ROR(R(rm), shift);
+ shiftcarry = op2 >> 31;
+ }
+ else // RRX#1
+ {
+ shiftcarry = R(rm) & 0x1;
+ op2 = (FLAG_C << 31) | (R(rm) >> 1);
+ }
+ break;
+ }
+
+ a_DataProcCore(rd, R(Rn), op2, shiftcarry);
+ }
+
+ NIARM(_DataProcShiftReg)
+ {
+ if ((code >> 26) & 0x3)
+ met_abort("Bits 26-27 must be 00 for DataProc instructions");
+ if (code & (0x1 << 7))
+ met_abort("Bit 7 must be 0 for DataProc with shift by register instructions");
+
+ uint8_t shift;
+ uint32_t op1, op2 = 0; // to avoid a warning
+ bool shiftcarry = FLAG_C;
+ uint8_t rd = Rd;
+
+ uint8_t rm = Rm;
+ if (rm == 15 && !(code & (0x1 << 20)) && (code & (0x1 << 4)))
+ met_abort("Rm = 15, not implemented");
+
+ op1 = R(Rn);
+
+ NOT_PC(Rs);
+ ICYCLES(1);
+ if (Rn == 15)
+ op1 += 4;
+
+ shift = R(Rs) & 0xFF; // only first byte used
+ if (shift)
+ switch ((code >> 5) & 0x3)
+ {
+ case 0: // Logical Shift Left
+ if (shift == 32)
+ {
+ op2 = 0;
+ shiftcarry = R(rm) & 0x1;
+ }
+ else if (shift < 32)
+ {
+ op2 = R(rm) << shift;
+ shiftcarry = (R(rm) >> (32-shift)) & 0x1;
+ }
+ else
+ {
+ op2 = 0;
+ shiftcarry = 0;
+ }
+ break;
+ case 1: // Logical Shift Right
+ if (shift == 32)
+ {
+ op2 = 0;
+ shiftcarry = R(rm) >> 31;
+ }
+ else if (shift < 32)
+ {
+ op2 = R(rm) >> shift;
+ shiftcarry = (R(rm) >> (shift-1)) & 0x1;
+ }
+ else
+ {
+ op2 = 0;
+ shiftcarry = 0;
+ }
+ break;
+ case 2: // Arithmetic Shift Right
+ if (shift >= 32)
+ {
+ op2 = ((int32_t)R(rm)) >> 31;
+ shiftcarry = op2 & 0x1;
+ }
+ else
+ {
+ op2 = ((int32_t)R(rm)) >> shift;
+ shiftcarry = (((int32_t)R(rm)) >> (shift-1)) & 0x1;
+ }
+ break;
+ case 3: // ROtate Right
+ op2 = ROR(R(rm), shift % 32);
+ shiftcarry = op2 >> 31;
+ break;
+ }
+ else
+ op2 = R(rm);
+
+ a_DataProcCore(rd, op1, op2, shiftcarry);
+ }
+
+ NIARM(_DataProcImm)
+ {
+ if ((code >> 26) & 0x3)
+ met_abort("Bits 26-27 must be 00 for DataProc instructions");
+
+ uint32_t op2;
+ bool shiftcarry = FLAG_C;
+ uint8_t rd = Rd;
+
+ uint8_t shift = ((code >> 8) & 0xF);
+ if (shift)
+ {
+ op2 = ROR(code & 0xFF, shift * 2);
+ shiftcarry = op2 >> 31;
+ }
+ else
+ op2 = code & 0xFF;
+
+ a_DataProcCore(rd, R(Rn), op2, shiftcarry);
+ }
+
+ inline void Interpreter::a_DataProcCore(uint8_t rd,
+ uint32_t op1, uint32_t op2, bool shiftcarry)
+ {
+ uint8_t opcode = (code >> 21) & 0xF;
+
+ if (opcode < 0x8 || opcode > 0xB)
+ {
+ }
+ else if (!((code >> 20) & 0x1) || (rd != 0x0 && rd != 0xF))
+ met_abort("Set condition bit not set for test operation or Rd not acceptable for a test");
+ if ((opcode == 0xD || opcode == 0xF) && Rn)
+ met_abort("Rn not null for MOV or MVN");
+
+#ifndef X86_ASM
+ uint32_t res;
+#endif
+
+ if (code & (0x1 << 20)) // if set condition
+ {
+ switch (opcode)
+ {
+ case 0x0 : // AND
+#ifdef X86_ASM
+ asm("andl %4, %3\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N)
+ :"0"(op1), "r"(op2));
+#else
+ res = R(rd) = op1 & op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ FLAG_C = shiftcarry;
+ break;
+ case 0x1 : // EOR
+#ifdef X86_ASM
+ asm("xorl %4, %3\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N)
+ :"0"(op1), "r"(op2));
+#else
+ res = R(rd) = op1 ^ op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ FLAG_C = shiftcarry;
+ break;
+ case 0x2 : // SUB
+#ifdef X86_ASM
+ asm("subl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setncb %3\n"
+ "setob %4\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C),
+ "=m"(FLAG_V)
+ :"0"(op1), "r"(op2));
+#else
+ res = R(rd) = op1 - op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+#endif
+ break;
+ case 0x3 : // RSB
+#ifdef X86_ASM
+ asm("subl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setncb %3\n"
+ "setob %4\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C),
+ "=m"(FLAG_V)
+ :"0"(op2), "r"(op1));
+#else
+ res = R(rd) = op2 - op1;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+#endif
+ break;
+ case 0x4 : // ADD
+#ifdef X86_ASM
+ asm("addl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setcb %3\n"
+ "setob %4\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C),
+ "=m"(FLAG_V)
+ :"0"(op1), "r"(op2));
+#else
+ res = R(rd) = op1 + op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = ADDCARRY(op1, op2, res);
+ FLAG_V = ADDOVERFLOW(op1, op2, res);
+#endif
+ break;
+ case 0x5 : // ADC
+ // TODO test on hardware how overflow and carry work for this
+ // instruction
+#ifdef X86_ASM
+ asm("addl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setcb %3\n"
+ "setob %4\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C),
+ "=m"(FLAG_V)
+ :"0"(op1+FLAG_C), "r"(op2));
+#else
+ res = R(rd) = op1 + op2 + FLAG_C;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = ADDCARRY(op1, op2, res);
+ FLAG_V = ADDOVERFLOW(op1, op2, res);
+#endif
+ break;
+ case 0x6 : // SBC
+ // TODO test on hardware how overflow and carry work for this
+ // instruction
+#ifdef X86_ASM
+ asm("subl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setncb %3\n"
+ "setob %4\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C),
+ "=m"(FLAG_V)
+ :"0"(op1+FLAG_C-1), "r"(op2));
+#else
+ res = R(rd) = op1 - op2 + FLAG_C - 1;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+#endif
+ break;
+ case 0x7 : // RSC
+#ifdef X86_ASM
+ asm("subl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setncb %3\n"
+ "setob %4\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C),
+ "=m"(FLAG_V)
+ :"0"(op2+FLAG_C-1), "r"(op1));
+#else
+ res = R(rd) = op2 - op1 + FLAG_C - 1;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+#endif
+ break;
+ case 0x8 : // TST
+#ifdef X86_ASM
+ asm("testl %3, %2\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N)
+ :"r"(op1), "r"(op2));
+#else
+ res = op1 & op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ SETFB(C, shiftcarry);
+ break;
+ case 0x9 : // TEQ
+#ifdef X86_ASM
+ asm("xorl %3, %2\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N)
+ :"r"(op1), "r"(op2)
+ :"2");
+#else
+ res = op1 ^ op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ SETFB(C, shiftcarry);
+ break;
+ case 0xA : // CMP
+#ifdef X86_ASM
+ asm("cmpl %5, %4\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ "setncb %2\n"
+ "setob %3\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"r"(op1), "r"(op2));
+#else
+ res = op1 - op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+#endif
+ break;
+ case 0xB : // CMN
+#ifdef X86_ASM
+ asm("addl %5, %4\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ "setcb %2\n"
+ "setob %3\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"r"(op1), "r"(op2)
+ :"4");
+#else
+ res = op1 + op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = ADDCARRY(op1, op2, res);
+ FLAG_V = ADDOVERFLOW(op1, op2, res);
+#endif
+ break;
+ case 0xC : // ORR
+#ifdef X86_ASM
+ asm("orl %4, %3\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N)
+ :"0"(op1), "r"(op2));
+#else
+ res = R(rd) = op1 | op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ FLAG_C = shiftcarry;
+ break;
+ case 0xD : // MOV
+#ifdef X86_ASM
+ R(rd) = op2;
+ asm("testl %2, %2\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N)
+ :"r"(op2));
+#else
+ res = R(rd) = op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ FLAG_C = shiftcarry;
+ break;
+ case 0xE : // BIC
+#ifdef X86_ASM
+ asm("not %3\n"
+ "andl %4, %3\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N)
+ :"0"(op2), "r"(op1));
+#else
+ res = R(rd) = op1 & ~op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ FLAG_C = shiftcarry;
+ break;
+ case 0xF : // MVN
+#ifdef X86_ASM
+ asm("xorl $0xffffffff, %3\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ :"=r"(R(rd)), "=m"(FLAG_Z), "=m"(FLAG_N)
+ :"0"(op2));
+#else
+ res = R(rd) = ~op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ FLAG_C = shiftcarry;
+ break;
+ }
+
+ if (rd == 15)
+ CPU.SwitchModeBack();
+ }
+ else
+ switch (opcode)
+ {
+ case 0x0 : // AND
+ R(rd) = op1 & op2;
+ break;
+ case 0x1 : // EOR
+ R(rd) = op1 ^ op2;
+ break;
+ case 0x2 : // SUB
+ R(rd) = op1 - op2;
+ break;
+ case 0x3 : // RSB
+ R(rd) = op2 - op1;
+ break;
+ case 0x4 : // ADD
+ R(Rd) = op1 + op2;
+ break;
+ case 0x5 : // ADC
+ R(rd) = op1 + op2 + FLAG_C;
+ break;
+ case 0x6 : // SBC
+ R(rd) = op1 - op2 + FLAG_C - 1;
+ break;
+ case 0x7 : // RSC
+ R(rd) = op2 - op1 + FLAG_C - 1;
+ break;
+ case 0x8 : // TST
+ case 0x9 : // TEQ
+ case 0xA : // CMP
+ case 0xB : // CMN
+ met_abort("Comparison or test without set flags bit");
+ break;
+ case 0xC : // ORR
+ R(rd) = op1 | op2;
+ break;
+ case 0xD : // MOV
+ R(rd) = op2;
+ break;
+ case 0xE : // BIC
+ R(rd) = op1 & ~op2;
+ break;
+ case 0xF : // MVN
+ R(rd) = ~op2;
+ break;
+ }
+
+ if (rd == 15 && (opcode < 0x8 || opcode > 0xB))
+ {
+ if (FLAG_T)
+ {
+ CYCLES16NSeq(R(15), 3);
+ R(15) += 2;
+ }
+ else
+ {
+ CYCLES32NSeq(R(15), 3);
+ R(15) += 4;
+ }
+ }
+ else
+ CYCLES32Seq(R(15), 1);
+
+ if (opcode >= 0x8 && opcode <= 0xB && rd == 0xF)
+ met_abort("P test instruction (not implemented)");
+ }
+
+ // PSR Transfer (MRS, MSR)
+ ARM(PSR)
+ {
+ if ((code >> 26) & 0x3)
+ met_abort("Bits 26-27 must be 00 for PSR instructions");
+ if (((code >> 23) & 0x3) != 0x2)
+ met_abort("Bits 23-24 must be 10 for PSR instructions");
+ if (code & (0x1 << 20))
+ met_abort("Bit 20 must be 0 for PSR instructions");
+
+ bool oncpsr = !(code & (0x1 << 22));
+ if (oncpsr)
+ CPU.UpdateCpsr();
+ uint32_t& psr = oncpsr ? CPSR : SPSR;
+
+ if (code & (0x1 << 21)) // MSR
+ {
+ if (((code >> 12) & 0xF) != 0xF)
+ met_abort("Bits 12-15 must be 0xF for MSR instruction");
+
+ uint32_t val;
+ if (code & (0x1 << 25))
+ {
+ //val = ROR(code & 0xF, ((code >> 8) & 0x4) * 2);
+ val = ROR(code & 0xF, (code >> 7) & 0x6);
+ }
+ else
+ {
+ if ((code >> 4) & 0xFF)
+ met_abort("Bits 4-11 must be 0 for MSR instruction");
+ val = R(Rm);
+ }
+ if (!(code & (0x1 << 19)))
+ val = (val & 0x00FFFFFF) | (psr & 0xFF000000);
+ if (!(code & (0x1 << 18)))
+ val = (val & 0xFF00FFFF) | (psr & 0x00FF0000);
+ if (!(code & (0x1 << 17)))
+ val = (val & 0xFFFF00FF) | (psr & 0x0000FF00);
+ if (!(code & (0x1 << 16)))
+ val = (val & 0xFFFFFF00) | (psr & 0x000000FF);
+ else if (oncpsr &&
+ (psr & 0x1F) != (val & 0x1F)) // have we changed mode ?
+ CPU.SwitchToMode(val & 0x1F);
+ psr = val;
+ if (oncpsr)
+ {
+ CPU.UpdateICpsr();
+ CPU.CheckInterrupt();
+ }
+ }
+ else // MRS
+ {
+ if ((code >> 25) & 0x1)
+ met_abort("Bit 25 must be 0 for MRS instruction");
+ if (((code >> 16) & 0xF) != 0xF)
+ met_abort("Bits 16-19 must be 0xF for MRS instruction");
+ if (code & 0xFFF)
+ met_abort("Bits 0-11 must be 0 for MRS instruction");
+ R(Rd) = psr;
+ }
+
+ CYCLES32Seq(R(15), 1);
+ }
+
+ // Multiply and Multiply-Accumulate (MUL,MLA)
+ ARM(_Multiply)
+ {
+ // NOTE : In this instruction Rn and Rd are inverted
+ if ((code >> 25) & 0x7)
+ met_abort("Bits 25-27 must be 000 for Multiply instructions");
+ if (code & (0x1 << 24))
+ {
+ if (!(code & (0x1 << 7)))
+ met_abort("Bit 7 must be 1 for halfword multiply");
+ if (code & (0x1 << 4))
+ met_abort("Bit 7 must be 0 for halfword multiply");
+ }
+ else
+ {
+ if (((code >> 4) & 0xF) != 0x9)
+ met_abort("Bits 4-7 must be 1001 for non halfword multiplies");
+ }
+ NOT_PC_ALL();
+
+ switch ((code >> 21) & 0xF)
+ {
+ case 0x0 : // MUL
+ if (Rd != 0)
+ met_abort("Rd must be 0 for MUL instructions");
+ NOT_SAME2(Rn, Rm);
+ R(Rn) = R(Rm)*R(Rs);
+ if (code & (0x1 << 20))
+ {
+ FZ(R(Rn));
+ FN(R(Rn));
+ }
+ MULICYCLES(Rs);
+ CYCLES32Seq(R(15), 1);
+ break;
+ case 0x1 : // MLA
+ NOT_SAME2(Rn, Rm);
+ R(Rn) = R(Rm)*R(Rs)+R(Rd);
+ if (code & (0x1 << 20))
+ {
+ FZ(R(Rn));
+ FN(R(Rn));
+ }
+ MULICYCLES(Rs);
+ ICYCLES(1);
+ CYCLES32Seq(R(15), 1);
+ break;
+ case 0x4 : // UMULL
+ {
+ NOT_SAME3(Rn, Rd, Rm);
+ uint64_t out = ((uint64_t)R(Rm))*((uint64_t)R(Rs));
+ R(Rn) = out >> 32;
+ R(Rd) = out & 0xFFFFFFFF;
+ if (code & (0x1 << 20))
+ {
+ FZ(out);
+ FN(R(Rn));
+ }
+ }
+ MULICYCLES(Rs);
+ ICYCLES(1);
+ CYCLES32Seq(R(15), 1);
+ break;
+ case 0x5 : // UMLAL
+ {
+ NOT_SAME3(Rn, Rd, Rm);
+ uint64_t out =
+ ((uint64_t)R(Rm)) * ((uint64_t)R(Rs)) +
+ ((((uint64_t)R(Rn)) << 32) | ((uint64_t)R(Rd)));
+ R(Rn) = out >> 32;
+ R(Rd) = out & 0xFFFFFFFF;
+ if (code & (0x1 << 20))
+ {
+ FZ(out);
+ FN(R(Rn));
+ }
+ }
+ MULICYCLES(Rs);
+ ICYCLES(2);
+ CYCLES32Seq(R(15), 1);
+ break;
+ case 0x6 : // SMULL
+ {
+ NOT_SAME3(Rn, Rd, Rm);
+ int64_t out = ((int64_t)(int32_t)R(Rm)) * ((int64_t)(int32_t)R(Rs));
+ R(Rn) = out >> 32;
+ R(Rd) = out & 0xFFFFFFFF;
+ if (code & (0x1 << 20))
+ {
+ FZ(out);
+ FN(R(Rn));
+ }
+ }
+ MULICYCLES(Rs);
+ ICYCLES(1);
+ CYCLES32Seq(R(15), 1);
+ break;
+ case 0x7 : // SMLAL
+ {
+ NOT_SAME3(Rn, Rd, Rm);
+ int64_t out = ((int64_t)(int32_t)R(Rm)) * ((int64_t)(int32_t)R(Rs))
+ + ((((int64_t)R(Rn)) << 32) | ((int64_t)R(Rd)));
+ R(Rn) = out >> 32;
+ R(Rd) = out & 0xFFFFFFFF;
+ if (code & (0x1 << 20))
+ {
+ FZ(out);
+ FN(R(Rn));
+ }
+ }
+ MULICYCLES(Rs);
+ ICYCLES(2);
+ CYCLES32Seq(R(15), 1);
+ break;
+ case 0x8 : // SMLAxy
+ case 0x9 : // SMLAW/SMULW
+ case 0xA : // SMLALxy
+ case 0xB : // SMULxy
+ default :
+ met_abort("Not implemented multiply instruction or unknown");
+ }
+ }
+
+ // Single Data Transfer (LDR, STR, PLD)
+
+ // Load and store
+ // FIXME : should this support Prepare Cache for Load instructions ?
+ ARM(LDRSTR)
+ {
+ if (((code >> 28) & 0xF) == 0xF)
+ met_abort("PLD instructions not implemented");
+ if (((code >> 26) & 0x3) != 0x1)
+ met_abort("Bits 26-27 must be 01 for LDR/STR instructions");
+
+ uint32_t offset;
+
+ if (code & (0x1 << 25)) // register offset
+ {
+ if (code & (0x1 << 4))
+ met_abort("Bit 4 must be 0 for LDR or STR instruction with register offset");
+ offset = (code >> 7) & 0x1F;
+ switch ((code >> 5) & 0x3)
+ {
+ case 0: // Logical Shift Left
+ if (offset)
+ offset = R(Rm) << offset;
+ else
+ offset = R(Rm);
+ break;
+ case 1: // Logical Shift Right
+ if (offset)
+ offset = R(Rm) >> offset;
+ else
+ offset = 0;
+ break;
+ case 2: // Arithmetic Shift Right
+ if (offset)
+ offset = ((int32_t)R(Rm)) >> offset;
+ else
+ {
+ if (R(Rm) >> 31)
+ offset = 0xFFFFFFFF;
+ else
+ offset = 0;
+ }
+ break;
+ case 3: // ROtate Right
+ if (offset)
+ offset = ROR(R(Rm), offset);
+ else
+ offset = (FLAG_C << 31) | (R(Rm) >> 1);
+ break;
+ }
+ }
+ else // immediate offset
+ {
+ offset = code & 0xFFF;
+ }
+
+ // bit 24 : 0 = add offset after and write-back, 1 = add offset before
+ uint32_t add = R(Rn);
+ if (code & (0x1 << 24))
+ {
+ if (code & (0x1 << 23))
+ add += offset;
+ else
+ add -= offset;
+ }
+
+ /* bit 22 : 0 = write word, 1 = write byte
+ * bit 20 : 0 = store, 1 = load */
+ if (code & (0x1 << 22))
+ {
+ if (code & (0x1 << 20)) // LDRB
+ {
+ R(Rd) = MEM.Read8(add);
+ if (Rd == 15)
+ met_abort("LDRB to R15 !");
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES32Seq(R(15), 1);
+ }
+ else // STRB
+ {
+ MEM.Write8(add, R(Rd));
+ CYCLES16NSeq(add, 1);
+ CYCLES32NSeq(R(15), 1);
+ }
+ }
+ else
+ {
+ if (code & (0x1 << 20)) // LDR
+ {
+ R(Rd) = MEM.Read32(add);
+ CYCLES32NSeq(add, 1);
+ ICYCLES(1);
+ if (Rd == 15)
+ {
+ CYCLES32NSeq(R(15), 3);
+ R(Rd) += 4;
+ }
+ else
+ CYCLES32Seq(R(15), 1);
+ }
+ else // STR
+ {
+ MEM.Write32(add, R(Rd));
+ CYCLES32NSeq(add, 1);
+ CYCLES32NSeq(R(15), 1);
+ }
+ }
+
+ // bit 21 if write before : 0 = nothing, 1 = write-back
+ if (!(code & (0x1 << 24))) // in post, writeback is always enabled
+ {
+ if (code & (0x1 << 23))
+ R(Rn) = add + offset;
+ else
+ R(Rn) = add - offset;
+ }
+ else if (code & (0x1 << 21))
+ R(Rn) = add;
+ }
+
+ // Halfword, Doubleword, and Signed Data Transfer
+ ARM(STRLDR_HD)
+ {
+ if ((code >> 25) & 0x7)
+ met_abort("Bits 25-27 must be 000 for halfword transfer instructions");
+ if (!(code & (0x1 << 7)) || !(code & (0x1 << 4)))
+ met_abort("Bits 4 and 7 must be 1 for halfword transfer instructions");
+ if (Rd == 15)
+ met_abort("operation on r15, not implemented");
+
+ uint8_t rd = Rd;
+
+ uint32_t off;
+ if (code & (0x1 << 22)) // immediate offset
+ off = ((code >> 4) & 0xF0) | (code & 0xF);
+ else // register offset
+ {
+ if ((code >> 8) & 0xF)
+ met_abort("Bits 8-11 must be 0 for halfword transfer with register offset instructions");
+ NOT_PC(Rm);
+ off = R(Rm);
+ }
+
+ uint32_t add = R(Rn);
+ if (code & (0x1 << 24))
+ {
+ if (code & (0x1 << 23))
+ add += off;
+ else
+ add -= off;
+ }
+ else if (code & (0x1 << 21))
+ met_abort("Bit 21 must be 0 for post indexed halfword transfers instructions");
+
+ switch (((code >> 18) & 0x4) | ((code >> 5) & 0x3))
+ {
+ case 0x0:
+ met_abort("Reserved for SWP instruction !");
+ break;
+ case 0x1: // STRH
+ MEM.Write16(add, rd == 15 ? R(15) + 4 : R(rd));
+ CYCLES16NSeq(add, 1);
+ CYCLES32NSeq(R(15), 1);
+ break;
+ case 0x2: // LDRD
+ if (rd % 2)
+ met_abort("Register number not even for double word transfer");
+ if (add % 8)
+ met_abort("Address not double word aligned");
+ if (rd == 15)
+ met_abort("Rd is 15 for double word transfer !");
+ R(rd) = MEM.Read32(add);
+ R(rd+1) = MEM.Read32(add+4);
+ CYCLES32NSeq(add, 2);
+ ICYCLES(1);
+ CYCLES32Seq(R(15), 1);
+ break;
+ case 0x3: // STRD
+ if (rd % 2)
+ met_abort("Register number not even for double word transfer");
+ if (add % 8)
+ met_abort("Address not double word aligned");
+ if (rd == 15)
+ met_abort("Rd is 15 for double word transfer !");
+ MEM.Write32(add, R(rd));
+ MEM.Write32(add + 4, rd == 14 ? R(15) + 4 : R(rd+1));
+ CYCLES32NSeq(add, 2);
+ CYCLES32NSeq(R(15), 1);
+ break;
+ case 0x4:
+ met_abort("Reserved !");
+ break;
+ case 0x5: // LDRH
+ R(rd) = MEM.Read16(add);
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES32Seq(R(15), 1);
+ break;
+ case 0x6: // LDRSB
+ R(rd) = MEM.Read8(add);
+ // sign-extend
+ R(rd) <<= 24;
+ R(rd) = ((int32_t)R(rd)) >> 24;
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES32Seq(R(15), 1);
+ break;
+ case 0x7: // LDRSH
+ R(rd) = MEM.Read16(add);
+ // sign-extend
+ R(rd) <<= 16;
+ R(rd) = ((int32_t)R(rd)) >> 16;
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES32Seq(R(15), 1);
+ break;
+ }
+
+ if (!(code & (0x1 << 24))) // in post, writeback is always enabled
+ {
+ if (code & (0x1 << 23))
+ R(Rn) = add + off;
+ else
+ R(Rn) = add - off;
+ }
+ else if (code & (0x1 << 21))
+ R(Rn) = add;
+ }
+
+ // Block Data Transfer (LDM,STM)
+ ARM(LDMSTM)
+ {
+ if (((code >> 25) & 0x7) != 0x4)
+ met_abort("Bits 25-27 must be 100 for LDM/STM instructions");
+ if (code & (0x1 << 22))
+ met_abort("not implemented");
+
+ static const uint8_t NumBits[] =
+ {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
+
+ // Works like LDR and STR, look at above comments
+ uint8_t numregs =
+ NumBits[(code >> 12) & 0xF] +
+ NumBits[(code >> 8) & 0xF] +
+ NumBits[(code >> 4) & 0xF] +
+ NumBits[ code & 0xF];
+ uint16_t regs = code & 0xFFFF;
+
+ uint32_t add, baseadd;
+ baseadd = add = R(Rn);
+ if (code & (0x1 << 24))
+ if (code & (0x1 << 23))
+ add += 4; // increment before
+ else
+ add -= numregs * 4; // decrement before
+ else
+ if (!(code & (0x1 << 23)))
+ add -= (numregs-1) * 4; // decrement after
+ add &= 0xFFFFFFFC;
+
+ if (code & (0x1 << 20)) // LDM
+ {
+ CYCLES32NSeq(add, numregs);
+ ICYCLES(1);
+ for (register uint8_t n = 0; n < 16; ++n)
+ if (regs & (0x1 << n))
+ {
+ R(n) = MEM.Read32 (add);
+ if (n == 15)
+ R(15) += 4;
+ add += 4;
+ }
+ if (regs & (0x1 << 15))
+ CYCLES32NSeq(R(15), 3);
+ else
+ CYCLES32Seq(R(15), 1);
+ }
+ else // STM
+ {
+ CYCLES32NSeq(add, numregs);
+ CYCLES32NSeq(R(15), 1);
+
+ for (register uint8_t n = 0; n < 16; ++n)
+ if (regs & (0x1 << n))
+ {
+ if (n == 15)
+ MEM.Write32 (add, R(15) + 4);
+ else
+ MEM.Write32 (add, R(n));
+ add += 4;
+ }
+ }
+
+ if (code & (0x1 << 21))
+ if (code & (0x1 << 23))
+ R(Rn) = baseadd + numregs * 4;
+ else
+ R(Rn) = baseadd - numregs * 4;
+ }
+
+ // Single Data Swap (SWP)
+ ARM(SWP)
+ {
+ if (((code >> 23) & 0x1F) != 0x02)
+ met_abort("Bits 23-27 must be 00010 for SWP instructions");
+ if ((code >> 20) & 0x3)
+ met_abort("Bits 20-21 must be 00 for SWP instructions");
+ if (((code >> 4) & 0xFF) != 0x09)
+ met_abort("Bits 4-11 must be 00001001 for SWP instructions");
+
+ if (code & (0x1 << 22)) // SWPB
+ {
+ R(Rd) = MEM.Read8(R(Rn));
+ MEM.Write8(R(Rn), R(Rm));
+ }
+ else // SWP
+ {
+ R(Rd) = MEM.Read32(R(Rn));
+ MEM.Write32(R(Rn), R(Rm));
+ }
+ CYCLES32NSeq(R(Rn), 1);
+ CYCLES32NSeq(R(Rn), 1);
+ ICYCLES(1);
+ CYCLES32Seq(R(15), 1);
+ }
+
+ // Software Interrupt (SWI,BKPT)
+ ARM(SWI)
+ {
+ if (((code >> 24) & 0xF) != 0xF)
+ met_abort("Bits 24-27 must be 1111 for SWI instructions");
+
+ CPU.SoftwareInterrupt((code >> 16) & 0xFF);
+
+ // FIXME seems wrong !
+ CYCLES32NSeq(0, 3);
+ }
+
+ inline bool Interpreter::a_CheckCondition (uint8_t cond)
+ {
+ if (cond == 0xE)
+ return true;
+
+ switch (cond)
+ {
+ case 0x0 : // EQ
+ if (!FLAG_Z)
+ return false;
+ break;
+ case 0x1 : // NE
+ if (FLAG_Z)
+ return false;
+ break;
+ case 0x2 : // CS
+ if (!FLAG_C)
+ return false;
+ break;
+ case 0x3 : // CC
+ if (FLAG_C)
+ return false;
+ break;
+ case 0x4 : // MI
+ if (!FLAG_N)
+ return false;
+ break;
+ case 0x5 : // PL
+ if (FLAG_N)
+ return false;
+ break;
+ case 0x6 : // VS
+ if (!FLAG_V)
+ return false;
+ break;
+ case 0x7 : // VC
+ if (FLAG_V)
+ return false;
+ break;
+ case 0x8 : // HI
+ if (!FLAG_C || FLAG_Z)
+ return false;
+ break;
+ case 0x9 : // LS
+ if (FLAG_C && !FLAG_Z)
+ return false;
+ break;
+ case 0xA : // GE
+ if (FLAG_N != FLAG_V)
+ return false;
+ break;
+ case 0xB : // LT
+ if (FLAG_N == FLAG_V)
+ return false;
+ break;
+ case 0xC : // GT
+ if (FLAG_Z || FLAG_N != FLAG_V)
+ return false;
+ break;
+ case 0xD : // LE
+ if (!FLAG_Z && FLAG_N == FLAG_V)
+ return false;
+ break;
+ case 0xE : // AL
+ break;
+ case 0xF : // reserved
+ break;
+ }
+
+ return true;
+ }
+
+ NIARM(_Code)
+ {
+ if (!a_CheckCondition(code >> 28)) // condition failed
+ CYCLES32Seq(R(15), 1);
+ else
+ switch ((code >> 25) & 0x7)
+ {
+ case 0x0:
+ switch ((code >> 18) & 0x60 | (code >> 16) & 0x10 |
+ (code >> 4) & 0x0F)
+ {
+ case 0x40:
+ aPSR();
+ break;
+ case 0x00:
+ case 0x02:
+ case 0x04:
+ case 0x06:
+ case 0x08:
+ case 0x0A:
+ case 0x0C:
+ case 0x0E:
+ case 0x10:
+ case 0x12:
+ case 0x14:
+ case 0x16:
+ case 0x18:
+ case 0x1A:
+ case 0x1C:
+ case 0x1E:
+ case 0x20:
+ case 0x22:
+ case 0x24:
+ case 0x26:
+ case 0x28:
+ case 0x2A:
+ case 0x2C:
+ case 0x2E:
+ case 0x30:
+ case 0x32:
+ case 0x34:
+ case 0x36:
+ case 0x38:
+ case 0x3A:
+ case 0x3C:
+ case 0x3E:
+ case 0x50:
+ case 0x52:
+ case 0x54:
+ case 0x56:
+ case 0x58:
+ case 0x5A:
+ case 0x5C:
+ case 0x5E:
+ case 0x60:
+ case 0x62:
+ case 0x64:
+ case 0x66:
+ case 0x68:
+ case 0x6A:
+ case 0x6C:
+ case 0x6E:
+ case 0x70:
+ case 0x72:
+ case 0x74:
+ case 0x76:
+ case 0x78:
+ case 0x7A:
+ case 0x7C:
+ case 0x7E:
+ a_DataProcShiftImm();
+ break;
+ case 0x01:
+ case 0x03:
+ case 0x05:
+ case 0x07:
+ case 0x11:
+ case 0x13:
+ case 0x15:
+ case 0x17:
+ case 0x21:
+ case 0x23:
+ case 0x25:
+ case 0x27:
+ case 0x31:
+ case 0x33:
+ case 0x35:
+ case 0x37:
+ case 0x51:
+ case 0x53:
+ case 0x55:
+ case 0x57:
+ case 0x61:
+ case 0x63:
+ case 0x65:
+ case 0x67:
+ case 0x71:
+ case 0x73:
+ case 0x75:
+ case 0x77:
+ a_DataProcShiftReg();
+ break;
+ case 0x09:
+ case 0x19:
+ case 0x29:
+ case 0x39:
+ case 0x48:
+ case 0x4A:
+ case 0x4C:
+ case 0x4E:
+ a_Multiply();
+ break;
+ case 0x0B:
+ case 0x0D:
+ case 0x0F:
+ case 0x1B:
+ case 0x1D:
+ case 0x1F:
+ case 0x2B:
+ case 0x2D:
+ case 0x2F:
+ case 0x3B:
+ case 0x3D:
+ case 0x3F:
+ case 0x4B:
+ case 0x4D:
+ case 0x4F:
+ case 0x5B:
+ case 0x5D:
+ case 0x5F:
+ case 0x6B:
+ case 0x6D:
+ case 0x6F:
+ case 0x7B:
+ case 0x7D:
+ case 0x7F:
+ aSTRLDR_HD();
+ break;
+ case 0x49:
+ aSWP();
+ break;
+ case 0x41:
+ case 0x43:
+ aBXBLX();
+ break;
+ default:
+ met_abort("unknown");
+ break;
+ }
+ break;
+ case 0x1:
+ // TODO PSR
+ a_DataProcImm();
+ break;
+ case 0x2:
+ case 0x3:
+ aLDRSTR();
+ break;
+ case 0x4:
+ aLDMSTM();
+ break;
+ case 0x5:
+ aBBL();
+ break;
+ case 0x7:
+ if (code & (0x1 << 24))
+ aSWI();
+ else
+ met_abort("unknown");
+ break;
+ default:
+ { std::cerr << IOS_ADD << R(15)-8 << " : " << IOS_ADD << code << " : "; debug_bits(code); met_abort("not implemented"); }
+ break;
+ }
+ }
+}
+
+#undef Rn
+#undef Rd
+#undef Rs
+#undef Rm
+
+#undef LSOff
+
+#undef NOT_PC
+#undef NOT_PC_ALL
+#undef NOT_SAME2
+#undef NOT_SAME3
+
+#undef ARM
+
+#endif
diff --git a/libmeteor/source/interpreter_thumb.cpp b/libmeteor/source/interpreter_thumb.cpp
new file mode 100644
index 0000000000..0beb374086
--- /dev/null
+++ b/libmeteor/source/interpreter_thumb.cpp
@@ -0,0 +1,1222 @@
+// 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 .
+
+#ifndef __INTERPRETER_THUMB_H__
+#define __INTERPRETER_THUMB_H__
+
+/*
+
+- From GBATEK -
+
+THUMB Binary Opcode Format
+
+Fo|_15|_14|_13|_12|_11|_10|_9_|_8_|_7_|_6_|_5_|_4_|_3_|_2_|_1_|_0_|
+_1|_0___0___0_|__Op___|_______Offset______|____Rs_____|____Rd_____|Shifted
+_2|_0___0___0___1___1_|_I,_Op_|___Rn/nn___|____Rs_____|____Rd_____|ADD/SUB
+_3|_0___0___1_|__Op___|____Rd_____|_____________Offset____________|Immedi.
+_4|_0___1___0___0___0___0_|______Op_______|____Rs_____|____Rd_____|AluOp
+_5|_0___1___0___0___0___1_|__Op___|Hd_|Hs_|____Rs_____|____Rd_____|HiReg/BX
+_6|_0___1___0___0___1_|____Rd_____|_____________Word______________|LDR PC
+_7|_0___1___0___1_|__Op___|_0_|___Ro______|____Rb_____|____Rd_____|LDR/STR
+_8|_0___1___0___1_|__Op___|_1_|___Ro______|____Rb_____|____Rd_____|""H/SB/SH
+_9|_0___1___1_|__Op___|_______Offset______|____Rb_____|____Rd_____|""{B}
+10|_1___0___0___0_|Op_|_______Offset______|____Rb_____|____Rd_____|""H
+11|_1___0___0___1_|Op_|____Rd_____|_____________Word______________|"" SP
+12|_1___0___1___0_|Op_|____Rd_____|_____________Word______________|ADD PC/SP
+13|_1___0___1___1___0___0___0___0_|_S_|___________Word____________|ADD SP,nn
+14|_1___0___1___1_|Op_|_1___0_|_R_|____________Rlist______________|PUSH/POP
+17|_1___0___1___1___1___1___1___0_|___________User_Data___________|BKPT ARM9
+15|_1___1___0___0_|Op_|____Rb_____|____________Rlist______________|STM/LDM
+16|_1___1___0___1_|_____Cond______|_________Signed_Offset_________|B{cond}
+U_|_1___1___0___1___1___1___1___0_|_____________var_______________|UNDEF ARM9
+17|_1___1___0___1___1___1___1___1_|___________User_Data___________|SWI
+18|_1___1___1___0___0_|________________Offset_____________________|B
+19|_1___1___1___0___1_|_________________________var___________|_0_|BLXsuf ARM9
+U_|_1___1___1___0___1_|_________________________var___________|_1_|UNDEF ARM9
+19|_1___1___1___1_|_H_|______________Offset_Low/High______________|BL (BLX ARM9)
+
+*/
+
+#include "ameteor/interpreter.hpp"
+#include "globals.hpp"
+#include "cpu_globals.hpp"
+#include "ameteor/memory.hpp"
+#include "ameteor.hpp"
+
+#include "debug.hpp"
+
+#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 & (0x1 << 6)) >> 3) | Rs)
+#define HiRs ((code >> 3) & 0xF)
+#define HiRd (((code & (0x1 << 7)) >> 4) | Rd)
+
+#ifdef METDEBUG
+# define NOT_PC(reg) \
+ if (reg == 15) \
+ met_abort("Register is PC")
+#else
+# define NOT_PC(reg) {}
+#endif
+
+#define THUMB(name) \
+ inline void Interpreter::t##name ()
+#define NITHUMB(name) \
+ void Interpreter::t##name ()
+
+namespace AMeteor
+{
+ // move shifted register
+ THUMB(_Shift)
+ {
+ if ((code >> 13) & 0x7)
+ {
+ met_abort("Bits 13-15 must be 000 for shift instructions");
+ }
+
+ uint8_t off = Off;
+ switch ((code >> 11) & 0x3)
+ {
+ case 0x0: // LSL
+ if (off)
+ {
+ SETF(C, R(Rs) & (0x1 << (32-off)));
+ R(Rd) = R(Rs) << off;
+ FZ(R(Rd));
+ FN(R(Rd));
+ }
+ else
+ {
+ R(Rd) = R(Rs);
+ FZ(R(Rd));
+ FN(R(Rd));
+ }
+ break;
+ case 0x1: // LSR
+ if (off)
+ {
+ SETF(C, R(Rs) & (0x1 << (off-1)));
+ R(Rd) = R(Rs) >> off;
+ FZ(R(Rd));
+ FN(R(Rd));
+ }
+ else
+ {
+ SETF(C, R(Rs) & (0x1 << 31));
+ R(Rd) = 0;
+ FLAG_Z = 1;
+ FLAG_N = 0;
+ }
+ break;
+ case 0x2: // ASR
+ if (off)
+ {
+ SETF(C, (((int32_t)R(Rs)) >> (off - 1)) & 0x1);
+ R(Rd) = ((int32_t)R(Rs)) >> off;
+ FZ(R(Rd));
+ FN(R(Rd));
+ }
+ else
+ {
+ if (R(Rd) & (0x1 << 31))
+ {
+ R(Rd) = 0xFFFFFFFF;
+ FLAG_Z = 0;
+ FLAG_N = 1;
+ FLAG_C = 1;
+ }
+ else
+ {
+ R(Rd) = 0;
+ FLAG_Z = 1;
+ FLAG_N = 0;
+ FLAG_C = 0;
+ }
+ }
+ break;
+ case 0x3: // reserved
+ met_abort("Bits 11-12 must not be 11 for shift instructions");
+ break;
+ }
+
+ CYCLES16Seq(R(15), 1);
+ }
+
+ // add/substract
+ THUMB(ADDSUB)
+ {
+ if (((code >> 11) & 0x1F) != 0x3)
+ {
+ met_abort("Bits 11-15 should be 00011 for add/substract");
+ }
+
+ uint32_t op2;
+ if ((code >> 10) & 0x1) // imm
+ op2 = Ro;
+ else // reg
+ op2 = R(Ro);
+
+ if ((code >> 9) & 0x1) // SUB
+ {
+#ifdef X86_ASM
+ asm("subl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setncb %3\n"
+ "setob %4\n"
+ :"=r"(R(Rd)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"0"(R(Rs)), "r"(op2));
+#else
+ uint32_t op1 = R(Rs), res = R(Rd) = op1 - op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+#endif
+ }
+ else // ADD
+ {
+#ifdef X86_ASM
+ asm("addl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setcb %3\n"
+ "setob %4\n"
+ :"=r"(R(Rd)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"0"(R(Rs)), "r"(op2));
+#else
+ uint32_t op1 = R(Rs), res = R(Rd) = op1 + op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = ADDCARRY(op1, op2, res);
+ FLAG_V = ADDOVERFLOW(op1, op2, res);
+#endif
+ }
+
+ CYCLES16Seq(R(15), 1);
+ }
+
+ // move/compare/add/substact immediate
+ THUMB(_Imm)
+ {
+ if (((code >> 13) & 0x7) != 0x1)
+ {
+ met_abort("Bits 13-15 must be 001 for immediate instructions");
+ }
+
+ switch ((code >> 11) & 0x3)
+ {
+ case 0x0: // MOV
+ R(Rb) = Imm;
+ FLAG_Z = !Imm;
+ FLAG_N = 0;
+ break;
+ case 0x1: // CMP
+#ifdef X86_ASM
+ asm("cmpl %5, %4\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ "setncb %2\n"
+ "setob %3\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"r"(R(Rb)), "r"(Imm));
+#else
+ {
+ uint32_t op1 = R(Rb), op2 = Imm, res = op1 - op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+ }
+#endif
+ break;
+ case 0x2: // ADD
+#ifdef X86_ASM
+ asm("addl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setcb %3\n"
+ "setob %4\n"
+ :"=r"(R(Rb)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"0"(R(Rb)), "r"(Imm));
+#else
+ {
+ uint32_t op1 = R(Rb), op2 = Imm, res = R(Rb) = op1 + op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = ADDCARRY(op1, op2, res);
+ FLAG_V = ADDOVERFLOW(op1, op2, res);
+ }
+#endif
+ break;
+ case 0x3: // SUB
+#ifdef X86_ASM
+ asm("subl %6, %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setncb %3\n"
+ "setob %4\n"
+ :"=r"(R(Rb)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"0"(R(Rb)), "r"(Imm));
+#else
+ {
+ uint32_t op1 = R(Rb), op2 = Imm, res = R(Rb) = op1 - op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+ }
+#endif
+ break;
+ }
+
+ CYCLES16Seq(R(15), 1);
+ }
+
+ // ALU operations
+ THUMB(_ALU)
+ {
+ if (((code >> 10) & 0x3F) != 0x10)
+ {
+ met_abort("Bits 10-15 must be 010000 for ALU instructions");
+ }
+
+ uint8_t opcode = (code >> 6) & 0xF;
+ // TODO put a ifndef X86_ASM here around this declaration
+ uint32_t res = 0; // to avoid a warning
+ uint8_t shift;
+
+ switch (opcode)
+ {
+ case 0x0: // AND
+#ifdef X86_ASM
+ asm("andl %4, %3\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ :"=r"(R(Rd)), "=m"(FLAG_Z), "=m"(FLAG_N)
+ :"0"(R(Rd)), "r"(R(Rs)));
+#else
+ res = R(Rd) &= R(Rs);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ break;
+ case 0x1: // EOR
+#ifdef X86_ASM
+ asm("xorl %4, %3\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ :"=r"(R(Rd)), "=m"(FLAG_Z), "=m"(FLAG_N)
+ :"0"(R(Rd)), "r"(R(Rs)));
+#else
+ res = R(Rd) ^= R(Rs);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ break;
+ case 0x2: // LSL
+ ICYCLES(1);
+ shift = R(Rs) & 0xFF;
+ if (shift)
+ {
+ if (shift == 32)
+ {
+ SETFB(C, R(Rd) & 0x1);
+ R(Rd) = 0;
+ FLAG_Z = FLAG_N = 0;
+ }
+ else if (shift < 32)
+ {
+ SETFB(C, (R(Rd) >> (32-shift)) & 0x1);
+ res = R(Rd) <<= shift;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ }
+ else
+ {
+ R(Rd) = 0;
+ FLAG_C = FLAG_Z = FLAG_N = 0;
+ }
+ }
+ else
+ {
+ res = R(Rd);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ }
+ break;
+ case 0x3: // LSR
+ ICYCLES(1);
+ shift = R(Rs) & 0xFF;
+ if (shift)
+ {
+ if (shift == 32)
+ {
+ SETFB(C, R(Rd) >> 31);
+ R(Rd) = 0;
+ FLAG_Z = FLAG_N = 0;
+ }
+ else if (shift < 32)
+ {
+ SETFB(C, (R(Rd) >> (shift-1)) & 0x1);
+ res = R(Rd) >>= shift;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ }
+ else
+ {
+ R(Rd) = 0;
+ FLAG_C = FLAG_Z = FLAG_N = 0;
+ }
+ }
+ else
+ {
+ res = R(Rd);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ }
+ break;
+ case 0x4: // ASR
+ ICYCLES(1);
+ shift = R(Rs) & 0xFF;
+ if (shift)
+ {
+ if (shift >= 32)
+ {
+ R(Rd) = ((int32_t)R(Rs)) >> 31;
+ FLAG_C = FLAG_Z = FLAG_N = R(Rd) & 0x1;
+ }
+ else
+ {
+ SETFB(C, (((int32_t)R(Rd)) >> (shift-1)) & 0x1);
+ res = R(Rd) = ((int32_t)R(Rd)) >> shift;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ }
+ }
+ else
+ {
+ res = R(Rd);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ }
+ break;
+ case 0x5: // ADC
+ res = R(Rd) + R(Rs) + FLAG_C;
+ FLAG_C = ADDCARRY(R(Rd), R(Rs), res);
+ FLAG_V = ADDOVERFLOW(R(Rd), R(Rs), res);
+ R(Rd) = res;
+ break;
+ case 0x6: // SBC
+ res = R(Rd) - R(Rs) + FLAG_C - 1;
+ FLAG_C = SUBCARRY(R(Rd), R(Rs), res);
+ FLAG_V = SUBOVERFLOW(R(Rd), R(Rs), res);
+ R(Rd) = res;
+ break;
+ case 0x7: // ROR
+ ICYCLES(1);
+ shift = R(Rs) & 0xFF;
+ if (shift)
+ {
+ shift %= 32;
+ res = R(Rd);
+ SETFB(C, (res >> (shift - 1)) & 0x1);
+ res = R(Rd) = ROR(res, shift);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ }
+ else
+ {
+ res = R(Rd);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ }
+ break;
+ case 0x8: // TST
+#ifdef X86_ASM
+ asm("testl %3, %2\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N)
+ :"r"(R(Rd)), "r"(R(Rs)));
+#else
+ res = R(Rd) & R(Rs);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ break;
+ case 0x9: // NEG
+#ifdef X86_ASM
+ asm("negl %5\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ "setncb %3\n"
+ "setob %4\n"
+ :"=r"(R(Rd)), "=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"0"(R(Rs)));
+#else
+ {
+ uint32_t op2;
+ op2 = R(Rs);
+ res = R(Rd) = -op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ // we overflow only if op2 == INT_MIN
+ FLAG_V = SUBOVERFLOW(0, op2, res);
+ // we have a carry only if op2 == 0
+ FLAG_C = SUBCARRY(0, op2, res);
+ }
+#endif
+ break;
+ case 0xA: // CMP
+#ifdef X86_ASM
+ asm("cmpl %5, %4\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ "setncb %2\n"
+ "setob %3\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"r"(R(Rd)), "r"(R(Rs)));
+#else
+ {
+ uint32_t op1, op2;
+ op1 = R(Rd);
+ op2 = R(Rs);
+ res = op1 - op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+ }
+#endif
+ break;
+ case 0xB: // CMN
+#ifdef X86_ASM
+ asm("addl %5, %4\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ "setcb %2\n"
+ "setob %3\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"r"(R(Rd)), "r"(R(Rs))
+ :"4");
+#else
+ {
+ uint32_t op1, op2;
+ op1 = R(Rd);
+ op2 = R(Rs);
+ res = op1 + op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = ADDCARRY(op1, op2, res);
+ FLAG_V = ADDOVERFLOW(op1, op2, res);
+ }
+#endif
+ break;
+ case 0xC: // ORR
+#ifdef X86_ASM
+ asm("orl %4, %3\n"
+ "setzb %1\n"
+ "setsb %2\n"
+ :"=r"(R(Rd)), "=m"(FLAG_Z), "=m"(FLAG_N)
+ :"0"(R(Rd)), "r"(R(Rs)));
+#else
+ res = R(Rd) |= R(Rs);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+#endif
+ break;
+ case 0xD: // MUL
+ MULICYCLES(Rs);
+ res = R(Rd) *= R(Rs);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ break;
+ case 0xE: // BIC
+ res = R(Rd) &= ~R(Rs);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ break;
+ case 0xF: // MVN
+ res = R(Rd) = ~R(Rs);
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ break;
+ }
+
+ CYCLES16Seq(R(15), 1);
+ }
+
+ // Hi register operations/branch exchange
+ THUMB(_HiRegOp)
+ {
+ if (((code >> 10) & 0x3F) != 0x11)
+ {
+ met_abort("Bits 10-15 must be 010001 for HiReg instructions");
+ }
+
+ uint8_t rd = HiRd, rs = HiRs;
+ switch ((code >> 8) & 0x3)
+ {
+ case 0x0: // ADD
+ R(rd) += R(rs);
+ if (rd == 15)
+ {
+ R(15) &= 0xFFFFFFFE;
+ CYCLES16NSeq(R(15), 3);
+ R(15) += 2;
+ }
+ else
+ CYCLES16Seq(R(15), 1);
+ break;
+ case 0x1: // CMP
+#ifdef X86_ASM
+ asm("cmpl %5, %4\n"
+ "setzb %0\n"
+ "setsb %1\n"
+ "setncb %2\n"
+ "setob %3\n"
+ :"=m"(FLAG_Z), "=m"(FLAG_N), "=m"(FLAG_C), "=m"(FLAG_V)
+ :"r"(R(rd)), "r"(R(rs)));
+#else
+ {
+ uint32_t op1 = R(rd), op2 = R(rs), res = op1 - op2;
+ FLAG_Z = !res;
+ FLAG_N = res >> 31;
+ FLAG_C = SUBCARRY(op1, op2, res);
+ FLAG_V = SUBOVERFLOW(op1, op2, res);
+ }
+#endif
+ CYCLES16Seq(R(15), 1);
+ break;
+ case 0x2:
+ if (rd != 8 || rs != 8) // MOV
+ {
+ R(rd) = R(rs);
+ if (rd == 15)
+ {
+ R(15) &= 0xFFFFFFFE;
+ CYCLES16NSeq(R(15), 3);
+ R(15) += 2;
+ }
+ else
+ CYCLES16Seq(R(15), 1);
+ }
+ else // NOP
+ CYCLES16Seq(R(15), 1);
+ break;
+ case 0x3:
+ if (Rd)
+ met_abort("Rd must be 0 for BX/BLX instructions");
+ if (code & (0x1 << 7)) // BLX
+ {
+ NOT_PC(rs);
+ met_abort("BLX not implemented");
+ }
+ else // BX
+ {
+ if (R(rs) & 0x1)
+ {
+ R(15) = (R(rs) & 0xFFFFFFFE) + 2;
+ CYCLES16NSeq(R(15), 3);
+ }
+ else
+ {
+ // switch to arm
+ FLAG_T = 0;
+ R(15) = (R(rs) & 0xFFFFFFFC) + 4;
+ CYCLES32NSeq(R(15), 3);
+ }
+ }
+ break;
+ }
+ }
+
+ // load PC-relative
+ THUMB(LDRimm)
+ {
+ if (((code >> 11) & 0x1F) != 0x9)
+ {
+ met_abort("Bits 11-15 must be 01001 for LDR with imm instructions");
+ }
+
+ uint32_t add = (R(15) & 0xFFFFFFFC) + (Imm << 2);
+ R(Rb) = MEM.Read32(add);
+
+ CYCLES32NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ }
+
+ // load/store with register offset
+ // and load/store sign-extended byte/halfword
+ THUMB(STRLDRreg)
+ {
+ if (((code >> 12) & 0xF) != 0x5)
+ {
+ met_abort("Bits 12-15 must be 0101 for LDR/STR with reg instructions");
+ }
+
+ uint32_t add = R(Ro) + R(Rs);
+ switch ((code >> 9) & 0x7)
+ {
+ // load/store with register offset
+ case 0x0: // STR
+ MEM.Write32(add, R(Rd));
+ CYCLES32NSeq(add, 1);
+ CYCLES16NSeq(R(15), 1);
+ break;
+ case 0x2: // STRB
+ MEM.Write8(add, R(Rd));
+ CYCLES16NSeq(add, 1);
+ CYCLES16NSeq(R(15), 1);
+ break;
+ case 0x4: // LDR
+ R(Rd) = MEM.Read32(add);
+ CYCLES32NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ break;
+ case 0x6: // LDRB
+ R(Rd) = MEM.Read8(add);
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ break;
+
+ // load/store sign-extended byte/halfword
+ case 0x1: // STRH
+ MEM.Write16(add, R(Rd));
+ CYCLES16NSeq(add, 1);
+ CYCLES16NSeq(R(15), 1);
+ break;
+ case 0x3: // LDSB
+ R(Rd) = (int8_t)MEM.Read8(add);
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ break;
+ case 0x5: // LDRH
+ R(Rd) = MEM.Read16(add);
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ break;
+ case 0x7: // LDSH
+ R(Rd) = (int16_t)MEM.Read16(add);
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ break;
+ }
+ }
+
+ // load/store with immediate offset
+ THUMB(STRLDRoff)
+ {
+ if (((code >> 13) & 0x7) != 0x3)
+ {
+ met_abort("Bits 13-15 must be 011 for LDR/STR with off instructions");
+ }
+
+ uint32_t add;
+ switch ((code >> 11) & 0x3)
+ {
+ case 0x0: // STR
+ add = R(Rs) + (Off << 2);
+ MEM.Write32(add, R(Rd));
+ CYCLES32NSeq(add, 1);
+ CYCLES16NSeq(R(15), 1);
+ break;
+ case 0x1: // LDR
+ add = R(Rs) + (Off << 2);
+ R(Rd) = MEM.Read32(add);
+ CYCLES32NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ break;
+ case 0x2: // STRB
+ add = R(Rs) + Off;
+ MEM.Write8(add, R(Rd));
+ CYCLES16NSeq(add, 1);
+ CYCLES16NSeq(R(15), 1);
+ break;
+ case 0x3: // LDRB
+ add = R(Rs) + Off;
+ R(Rd) = MEM.Read8(add);
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ break;
+ }
+ }
+
+ // load/store halfword
+ THUMB(LDRHSTRHoff)
+ {
+ if (((code >> 12) & 0xF) != 0x8)
+ {
+ met_abort("Bits 12-15 must be 1000 for LDRH/STRH with off instructions");
+ }
+
+ uint32_t add = R(Rs) + (Off * 2);
+ if (code & (0x1 << 11)) // LDRH
+ {
+ R(Rd) = MEM.Read16(add);
+ CYCLES16NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ }
+ else // STRH
+ {
+ MEM.Write16(add, R(Rd));
+ CYCLES16NSeq(add, 1);
+ CYCLES16NSeq(R(15), 1);
+ }
+ }
+
+ // load/store SP-relative
+ THUMB(STRLDRsp)
+ {
+ if (((code >> 12) & 0xF) != 0x9)
+ {
+ met_abort("Bits 12-15 must be 1001 for LDR/STR SP-relative instructions");
+ }
+
+ uint32_t add = R(13) + (Imm << 2);
+ if (code & (0x1 << 11)) // LDR
+ {
+ R(Rb) = MEM.Read32(add);
+ CYCLES32NSeq(add, 1);
+ ICYCLES(1);
+ CYCLES16Seq(R(15), 1);
+ }
+ else // STR
+ {
+ MEM.Write32(add, R(Rb));
+ CYCLES32NSeq(add, 1);
+ CYCLES16NSeq(R(15), 1);
+ }
+ }
+
+ // get relative address
+ THUMB(ADDpcsp)
+ {
+ if (((code >> 12) & 0xF) != 0xA)
+ {
+ met_abort("Bits 12-15 must be 1010 for ADD relative instructions");
+ }
+
+ if (code & (0x1 << 11)) // with SP
+ {
+ R(Rb) = R(13) + (Imm << 2);
+ }
+ else // with PC
+ {
+ R(Rb) = (R(15) & 0xFFFFFFFC) + (Imm << 2);
+ }
+
+ CYCLES16Seq(R(15), 1);
+ }
+
+ // add offset to stack pointer
+ THUMB(ADDsp)
+ {
+ if (((code >> 8) & 0xFF) != 0xB0)
+ {
+ met_abort("Bits 8-15 must be 10110000 for ADD to SP instructions");
+ }
+
+ if (code & (0x1 << 7)) // substract
+ {
+ R(13) -= ((code & 0x7F) << 2);
+ }
+ else // add
+ {
+ R(13) += ((code & 0x7F) << 2);
+ }
+
+ CYCLES16Seq(R(15), 1);
+ }
+
+ // push/pop registers
+ THUMB(PUSHPOP)
+ {
+ if (((code >> 12) & 0xF) != 0xB)
+ {
+ met_abort("Bits 12-15 must be 1011 for PUSH/POP instructions");
+ }
+ if (((code >> 9) & 0x3) != 0x2)
+ {
+ met_abort("Bits 9-10 must be 10 for PUSH/POP instructions");
+ }
+
+ static const uint8_t NumBits[] =
+ {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
+
+ uint8_t numregs =
+ NumBits[(code >> 4) & 0xF] +
+ NumBits[ code & 0xF];
+ if (code & (0x1 << 8))
+ ++numregs;
+
+ uint32_t add, newsp;
+ if (code & (0x1 << 11)) // POP
+ {
+ newsp = R(13) + 4 * numregs;
+ add = R(13) & 0xFFFFFFFC;
+
+ CYCLES32NSeq(add, numregs);
+ ICYCLES(1);
+
+ for (register uint8_t n = 0; n < 8; ++n)
+ if (code & (0x1 << n))
+ {
+ R(n) = MEM.Read32 (add);
+ add += 4;
+ }
+ if (code & (0x1 << 8)) // POP PC
+ {
+ R(15) = (MEM.Read32(add) + 2) & 0xFFFFFFFE;
+ CYCLES16NSeq(R(15), 3);
+ }
+ else
+ CYCLES16Seq(R(15), 1);
+ }
+ else // PUSH
+ {
+ newsp = R(13) - 4 * numregs;
+ add = newsp & 0xFFFFFFFC;
+
+ CYCLES32NSeq(add, numregs);
+ CYCLES16NSeq(R(15), 1);
+
+ for (register uint8_t n = 0; n < 8; ++n)
+ if (code & (0x1 << n))
+ {
+ MEM.Write32 (add, R(n));
+ add += 4;
+ }
+ if (code & (0x1 << 8)) // PUSH LR
+ {
+ MEM.Write32 (add, R(14));
+ }
+ }
+
+ R(13) = newsp;
+ }
+
+ // multiple load/store
+ THUMB(STMLDM)
+ {
+ if (((code >> 12) & 0xF) != 0xC)
+ {
+ met_abort("Bits 12-15 must be 1100 for STM/LDM instructions");
+ }
+
+ static const uint8_t NumBits[] =
+ {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
+
+ uint8_t numregs =
+ NumBits[(code >> 4) & 0xF] +
+ NumBits[ code & 0xF];
+
+ uint32_t add = R(Rb) & 0xFFFFFFFC;
+
+ if (code & (0x1 << 11)) // LDMIA
+ {
+ CYCLES32NSeq(add, numregs);
+ ICYCLES(1);
+
+ for (register uint8_t n = 0; n < 8; ++n)
+ if (code & (0x1 << n))
+ {
+ R(n) = MEM.Read32 (add);
+ add += 4;
+ }
+
+ CYCLES16Seq(R(15), 1);
+ }
+ else // STMIA
+ {
+ CYCLES32NSeq(add, numregs);
+ CYCLES16NSeq(R(15), 1);
+
+ for (register uint8_t n = 0; n < 8; ++n)
+ if (code & (0x1 << n))
+ {
+ MEM.Write32 (add, R(n));
+ add += 4;
+ }
+ }
+
+ if (!(code & (0x1 << Rb)))
+ R(Rb) = R(Rb) + 4 * numregs;
+ }
+
+ // conditional branch
+ THUMB(_CondBranch)
+ {
+ if (((code >> 12) & 0xF) != 0xD)
+ {
+ met_abort("Bits 12-15 must be 1101 for branch on condition instructions");
+ }
+
+ bool branch = false;
+ switch ((code >> 8) & 0xF)
+ {
+ case 0x0: // BEQ
+ if (FLAG_Z)
+ branch = true;
+ break;
+ case 0x1: // BNE
+ if (!FLAG_Z)
+ branch = true;
+ break;
+ case 0x2: // BCS
+ if (FLAG_C)
+ branch = true;
+ break;
+ case 0x3: // BCC
+ if (!FLAG_C)
+ branch = true;
+ break;
+ case 0x4: // BMI
+ if (FLAG_N)
+ branch = true;
+ break;
+ case 0x5: // BPL
+ if (!FLAG_N)
+ branch = true;
+ break;
+ case 0x6: // BVS
+ if (FLAG_V)
+ branch = true;
+ break;
+ case 0x7: // BVC
+ if (!FLAG_V)
+ branch = true;
+ break;
+ case 0x8: // BHI
+ if (FLAG_C && !FLAG_Z)
+ branch = true;
+ break;
+ case 0x9: // BLS
+ if (!FLAG_C || FLAG_Z)
+ branch = true;
+ break;
+ case 0xA: // BGE
+ if (FLAG_N == FLAG_V)
+ branch = true;
+ break;
+ case 0xB: // BLT
+ if (FLAG_N != FLAG_V)
+ branch = true;
+ break;
+ case 0xC: // BGT
+ if (!FLAG_Z && FLAG_N == FLAG_V)
+ branch = true;
+ break;
+ case 0xD: // BLE
+ if (FLAG_Z || FLAG_N != FLAG_V)
+ branch = true;
+ break;
+ case 0xE: // undefined
+ met_abort("Undefined branch on condition");
+ break;
+ case 0xF: // reserved for SWI instruction
+ met_abort("Bits 8-11 must not be 1111 for branch instruction");
+ break;
+ }
+
+ if (branch)
+ {
+ R(15) += (((int32_t)(int8_t)Imm) << 1) + 2;
+
+ CYCLES16NSeq(R(15), 3);
+ }
+ else
+ CYCLES16Seq(R(15), 1);
+ }
+
+ // software interrupt and breakpoint
+ THUMB(SWI)
+ {
+ CPU.SoftwareInterrupt(code & 0xFF);
+
+ // FIXME seems wrong !
+ CYCLES32NSeq(0, 3);
+ }
+
+ // unconditional branch
+ THUMB(B)
+ {
+ if (((code >> 11) & 0x1F) != 0x1C)
+ {
+ met_abort("Bits 11-15 must be 11100 for branch instructions");
+ }
+
+ int32_t off = (code & 0x7FF) << 1;
+ if (off & 0x800)
+ off |= 0xFFFFF000; // extends sign bit
+ R(15) += off + 2;
+
+ CYCLES16NSeq(R(15), 3);
+ }
+
+ // long branch with link
+ THUMB(_BL1)
+ {
+ if (((code >> 11) & 0x1F) != 0x1E)
+ {
+ met_abort("Bits 11-15 must be 11110 for long branch instructions");
+ }
+
+ if (code & (0x1 << 10)) // negative offset
+ R(14) = R(15) + (((int32_t)((code & 0x7FF) << 12)) | 0xFF800000);
+ else
+ R(14) = R(15) + ((code & 0x7FF) << 12);
+
+ CYCLES16Seq(R(15), 1);
+ }
+
+ THUMB(_BL2)
+ {
+ // XXX we dont do check here to be sure this instruction is called after
+ // BL1
+ uint32_t add = R(14) + ((code & 0x7FF) << 1);
+
+ if (code & (0x1 << 11)) // BL
+ {
+ R(14) = (R(15)-2) | 0x1;
+ R(15) = (add & 0xFFFFFFFE) + 2;
+ }
+ else // BLX
+ {
+ met_abort("BLX not implemented");
+ if (code & 0x1)
+ met_abort("BLX with odd address");
+ FLAG_T = 0;
+ }
+
+ CYCLES16NSeq(R(15), 3);
+ }
+
+ NITHUMB(_Code)
+ {
+ switch (code >> 13)
+ {
+ case 0: // 000
+ if ((code & 0x1800) == 0x1800) // 00011
+ tADDSUB();
+ else // 000
+ t_Shift();
+ break;
+ case 1: // 001
+ t_Imm();
+ break;
+ case 2: // 010
+ switch ((code >> 10) & 0x7)
+ {
+ case 0: // 010000
+ t_ALU();
+ break;
+ case 1: // 010001
+ t_HiRegOp();
+ break;
+ case 2:
+ case 3: // 01001x
+ tLDRimm();
+ break;
+ default:
+ tSTRLDRreg();
+ break;
+ }
+ break;
+ case 3: // 011
+ tSTRLDRoff();
+ break;
+ case 4: // 100
+ if (code & (0x1 << 12)) // 1001
+ tSTRLDRsp();
+ else // 100
+ tLDRHSTRHoff();
+ break;
+ case 5: // 101
+ if (code & (0x1 << 12)) // 1011
+ {
+ switch (code & 0x0600)
+ {
+ case 0x0000: // 1011x00
+ tADDsp();
+ break;
+ case 0x0400: // 1011x10
+ tPUSHPOP();
+ break;
+ default:
+ met_abort("not implemented or unknown");
+ break;
+ }
+ }
+ else // 101
+ tADDpcsp();
+ break;
+ case 6: // 110
+ if (code & (0x1 << 12)) // 1101
+ {
+ if ((code & 0x0F00) == 0x0F00) // 11011111
+ tSWI();
+ else // 1101xxxx
+ t_CondBranch();
+ }
+ else // 1100
+ tSTMLDM();
+ break;
+ case 7: // 111
+ switch ((code >> 11) & 0x3)
+ {
+ case 0: // 11100
+ tB();
+ break;
+ case 2: // 11110
+ t_BL1();
+ break;
+ case 3: // 11111
+ t_BL2();
+ break;
+ default:
+ met_abort("not implemented or unknown");
+ break;
+ }
+ break;
+ }
+ }
+}
+
+#undef Rb
+#undef Ro
+#undef Rs
+#undef Rd
+#undef Imm
+
+#undef THUMB
+
+#endif
diff --git a/libmeteor/source/io.cpp b/libmeteor/source/io.cpp
new file mode 100644
index 0000000000..c0cd244ab1
--- /dev/null
+++ b/libmeteor/source/io.cpp
@@ -0,0 +1,493 @@
+// 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 .
+
+#include "ameteor/io.hpp"
+#include "ameteor/dma.hpp"
+#include "globals.hpp"
+#include "ameteor.hpp"
+#include
+
+#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)
+ {
+ //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)
+ {
+ //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)
+ {
+ //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()
+ case BG0HOFS:
+ LCD.UpdateBg0XOff(val & 0x1FF);
+ break;
+ case BG0VOFS:
+ LCD.UpdateBg0YOff(val & 0x1FF);
+ break;
+ case BG1HOFS:
+ LCD.UpdateBg1XOff(val & 0x1FF);
+ break;
+ case BG1VOFS:
+ LCD.UpdateBg1YOff(val & 0x1FF);
+ break;
+ case BG2HOFS:
+ LCD.UpdateBg2XOff(val & 0x1FF);
+ break;
+ case BG2VOFS:
+ LCD.UpdateBg2YOff(val & 0x1FF);
+ break;
+ case BG3HOFS:
+ LCD.UpdateBg3XOff(val & 0x1FF);
+ break;
+ case BG3VOFS:
+ 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;
+ }
+}
diff --git a/libmeteor/source/keypad.cpp b/libmeteor/source/keypad.cpp
new file mode 100644
index 0000000000..6e53a4f155
--- /dev/null
+++ b/libmeteor/source/keypad.cpp
@@ -0,0 +1,96 @@
+// 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 .
+
+#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);
+ }
+ }
+}
diff --git a/libmeteor/source/lcd.cpp b/libmeteor/source/lcd.cpp
new file mode 100644
index 0000000000..d54375a5f8
--- /dev/null
+++ b/libmeteor/source/lcd.cpp
@@ -0,0 +1,148 @@
+// 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 .
+
+#include "ameteor/lcd.hpp"
+#include "ameteor/io.hpp"
+#include "globals.hpp"
+#include "ameteor.hpp"
+
+#include "debug.hpp"
+
+namespace AMeteor
+{
+ Lcd::Lcd () :
+ m_screen(MEM, IO)
+ {
+ Reset();
+ }
+
+ void Lcd::Reset ()
+ {
+ CLOCK.AddLcd(960); // call me at first H-Blank
+ }
+
+ void Lcd::TimeEvent ()
+ {
+ uint16_t& dispstat = IO.GetRef16(Io::DISPSTAT);
+ uint16_t& vcount = IO.GetRef16(Io::VCOUNT);
+
+ if (dispstat & 0x2) // if we were H-Blanking
+ {
+ debug("hblank end");
+ // we are not anymore, we're on next line
+ dispstat ^= 0x2;
+ // call me when we are H-Blanking again
+ CLOCK.AddLcd(960);
+
+ // we have finished drawing a line, do our stuff...
+ if (vcount == 227) // this was the last line
+ {
+ vcount = 0; // we're now at first
+ // we reload the reference points
+ m_screen.UpdateBg2RefX(IO.DRead32(Io::BG2X_L));
+ m_screen.UpdateBg2RefY(IO.DRead32(Io::BG2Y_L));
+ m_screen.UpdateBg3RefX(IO.DRead32(Io::BG3X_L));
+ m_screen.UpdateBg3RefY(IO.DRead32(Io::BG3Y_L));
+ // and we draw the line 0
+ m_screen.DrawLine(0);
+ // FIXME see below, vblank finished
+ dispstat ^= 0x1;
+ }
+ else
+ {
+ ++vcount; // we're on next line
+ if (vcount < 160) // we draw normally
+ m_screen.DrawLine(vcount);
+ else if (vcount == 160) // We enter V-Blank
+ {
+ dispstat |= 0x1;
+ if (dispstat & (0x1 << 3)) // if V-Blank irq is enabled
+ CPU.SendInterrupt(0x1);
+ DMA.CheckAll(Dma::VBlank);
+
+ KEYPAD.VBlank();
+
+ // we send the vblank signal
+ //sig_vblank.emit();
+ }
+ // NOTE : v-blank finishes on line 227, not 0
+ // FIXME on vba, it finishes on 0
+ //if (vcount == 227) // V-Blank finished
+ //dispstat ^= 0x1;
+ }
+
+ // check for vcount match
+ if (vcount == ((dispstat >> 8) & 0xFF)) // vcount match
+ {
+ dispstat |= 0x4; // enable vcount match bit
+ if (dispstat & (0x1 << 5)) // if V-Counter irq is enabled
+ CPU.SendInterrupt(0x4);
+ }
+ else // no vcount match
+ dispstat &= ~(uint16_t)0x4;
+ }
+ else // if we were not H-Blanking
+ {
+ debug("hblank vcount : " << std::dec << vcount);
+ // now, we are
+ dispstat |= 0x2;
+ // call me when we are not H-Blanking anymore
+ CLOCK.AddLcd(272);
+
+ // NOTE : H-Blank interrupts are not generated during V-Blank
+ // FIXME vba generates hblank interrupts even during vblank
+ // if H-Blank irq is enabled //and we're not in V-Blank
+ if ((dispstat & 0x10) == 0x10)
+ CPU.SendInterrupt(0x2);
+ // NOTE : hblank DMAs are not triggered during vblank
+ // (seen on vba)
+ if (!(dispstat & 0x1))
+ // if we're not vblanking
+ DMA.CheckAll(Dma::HBlank);
+ }
+ }
+
+ bool Lcd::SaveState (std::ostream& stream)
+ {
+ if (!m_screen.SaveState(stream))
+ return false;
+
+ return true;
+ }
+
+ bool Lcd::LoadState (std::istream& stream)
+ {
+ if (!m_screen.LoadState(stream))
+ return false;
+
+ UpdateDispCnt (IO.DRead16(Io::DISPCNT));
+ UpdateBg0Cnt (IO.DRead16(Io::BG0CNT));
+ UpdateBg1Cnt (IO.DRead16(Io::BG1CNT));
+ UpdateBg2Cnt (IO.DRead16(Io::BG2CNT));
+ UpdateBg3Cnt (IO.DRead16(Io::BG3CNT));
+ UpdateBg0XOff (IO.DRead16(Io::BG0HOFS));
+ UpdateBg0YOff (IO.DRead16(Io::BG0VOFS));
+ UpdateBg1XOff (IO.DRead16(Io::BG1HOFS));
+ UpdateBg1YOff (IO.DRead16(Io::BG1VOFS));
+ UpdateBg2XOff (IO.DRead16(Io::BG2HOFS));
+ UpdateBg2YOff (IO.DRead16(Io::BG2VOFS));
+ UpdateBg3XOff (IO.DRead16(Io::BG3HOFS));
+ UpdateBg3YOff (IO.DRead16(Io::BG3VOFS));
+ OamWrite (0x07000000, 0x07000400);
+
+ return true;
+ }
+}
diff --git a/libmeteor/source/memory.cpp b/libmeteor/source/memory.cpp
new file mode 100644
index 0000000000..3bcab840ff
--- /dev/null
+++ b/libmeteor/source/memory.cpp
@@ -0,0 +1,823 @@
+// 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 .
+
+#include "ameteor/memory.hpp"
+#include "ameteor/io.hpp"
+#include "ameteor/eeprom.hpp"
+#include "ameteor/flash.hpp"
+#include "ameteor/sram.hpp"
+#include "globals.hpp"
+#include "ameteor.hpp"
+
+#include "debug.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define RET_ADD(mem, low, high, size) \
+ if (add >= low && (add+size) <= high) \
+ { \
+ return mem + (add - low); \
+ }
+
+#define debugm debug
+
+namespace AMeteor
+{
+ Memory::Memory () :
+ m_brom(NULL),
+ m_carttype(CTYPE_UNKNOWN),
+ m_cart(NULL)
+ {
+ m_wbram = new uint8_t[0x00040000];
+ m_wcram = new uint8_t[0x00008000];
+ m_pram = new uint8_t[0x00000400];
+ m_vram = new uint8_t[0x00018000];
+ m_oram = new uint8_t[0x00000400];
+ m_rom = new uint8_t[0x02000000];
+
+ Reset();
+ }
+
+ Memory::~Memory ()
+ {
+ if (m_brom)
+ delete [] m_brom;
+ delete [] m_wbram;
+ delete [] m_wcram;
+ delete [] m_pram;
+ delete [] m_vram;
+ delete [] m_oram;
+ delete [] m_rom;
+ if (m_cart)
+ delete m_cart;
+ }
+
+ void Memory::SetCartTypeFromSize (uint32_t size)
+ {
+ switch (size)
+ {
+ case 0x0200:
+ SetCartType(CTYPE_EEPROM512);
+ break;
+ case 0x2000:
+ SetCartType(CTYPE_EEPROM8192);
+ break;
+ case 0x8000:
+ SetCartType(CTYPE_SRAM);
+ break;
+ case 0x10000:
+ SetCartType(CTYPE_FLASH64);
+ break;
+ case 0x20000:
+ SetCartType(CTYPE_FLASH128);
+ break;
+ default:
+ met_abort("Unknown cartridge memory size");
+ break;
+ }
+ }
+
+ void Memory::SetCartType (uint8_t type)
+ {
+ if (m_cart)
+ delete m_cart;
+ switch (type)
+ {
+ case CTYPE_UNKNOWN:
+ m_cart = NULL;
+ break;
+ case CTYPE_FLASH64:
+ m_cart = new Flash(false);
+ break;
+ case CTYPE_FLASH128:
+ m_cart = new Flash(true);
+ break;
+ case CTYPE_EEPROM512:
+ m_cart = new Eeprom(false);
+ break;
+ case CTYPE_EEPROM8192:
+ m_cart = new Eeprom(true);
+ break;
+ case CTYPE_SRAM:
+ m_cart = new Sram();
+ break;
+ default:
+ met_abort("Unknown cartridge memory type");
+ break;
+ }
+ m_carttype = type;
+ }
+
+ void Memory::Reset (uint32_t params)
+ {
+ static const uint8_t InitMemoryTime[0xF] = {
+ 1, // 00 - BIOS
+ 0, // 01 - Not used
+ 3, // 02 - Work RAM 256K
+ 1, // 03 - Work RAM 32K
+ 1, // 04 - I/O Registers
+ 1, // 05 - Palette RAM
+ 1, // 06 - VRAM
+ 1, // 07 - OAM
+ 5, // 08 - ROM, Wait0
+ 5, // 09 - ROM, Wait0
+ 5, // 0A - ROM, Wait1
+ 5, // 0B - ROM, Wait1
+ 5, // 0C - ROM, Wait2
+ 5, // 0D - ROM, Wait2
+ 5 // 0E - SRAM
+ };
+
+ static const uint8_t InitMemoryTimeSeq[0x3] = {
+ 3, // 08-09 - ROM, Wait0
+ 5, // 0A-0B - ROM, Wait1
+ 9 // 0C-0D - ROM, Wait2
+ };
+
+ if (m_brom && (params & UNIT_MEMORY_BIOS))
+ {
+ delete [] m_brom;
+ m_brom = NULL;
+ }
+ std::memcpy(m_memtime, InitMemoryTime, sizeof(m_memtime));
+ std::memcpy(m_memtimeseq, InitMemoryTimeSeq, sizeof(m_memtimeseq));
+ std::memset(m_wbram, 0, 0x00040000);
+ std::memset(m_wcram, 0, 0x00008000);
+ std::memset(m_pram , 0, 0x00000400);
+ std::memset(m_vram , 0, 0x00018000);
+ std::memset(m_oram , 0, 0x00000400);
+ if (params & UNIT_MEMORY_ROM)
+ std::memset(m_rom , 0, 0x02000000);
+ SetCartType(CTYPE_UNKNOWN);
+ m_cartfile.clear();
+ }
+
+ void Memory::ClearWbram ()
+ {
+ std::memset(m_wbram, 0, 0x00040000);
+ }
+
+ void Memory::ClearWcram ()
+ {
+ std::memset(m_wcram, 0, 0x00008000);
+ }
+
+ void Memory::ClearPalette ()
+ {
+ std::memset(m_pram , 0, 0x00000400);
+ }
+
+ void Memory::ClearVram ()
+ {
+ std::memset(m_vram , 0, 0x00018000);
+ }
+
+ void Memory::ClearOam ()
+ {
+ std::memset(m_oram , 0, 0x00000400);
+ LCD.OamWrite(0x07000000, 0x07000400);
+ }
+
+ void Memory::SoftReset ()
+ {
+ std::memset(m_wcram+0x7E00, 0, 0x200);
+ }
+
+ void Memory::TimeEvent ()
+ {
+ if (!m_cartfile.empty())
+ {
+ // FIXME, this may fail, we should do something to inform user
+ std::ofstream f(m_cartfile.c_str());
+ m_cart->Save(f);
+ }
+ CLOCK.DisableBattery();
+ }
+
+ bool Memory::LoadBios (const char* filename)
+ {
+ std::ifstream file(filename);
+ if (!m_brom)
+ m_brom = new uint8_t[0x00004000];
+ memset(m_brom, 0, 0x00004000);
+ file.read((char*)m_brom, 0x00004000);
+ if (file.fail())
+ return false;
+ return true;
+ }
+
+ void Memory::LoadBios (const uint8_t* data, uint32_t size)
+ {
+ if (!m_brom)
+ m_brom = new uint8_t[0x00004000];
+ uint32_t until = std::min(size, 0x00004000u);
+ std::memcpy(m_brom, data, until);
+ std::memset(m_brom+until, 0, 0x00004000-until);
+ }
+
+ bool Memory::LoadRom (const char* filename)
+ {
+ std::ifstream file(filename);
+ std::memset(m_rom, 0, 0x02000000);
+ file.read((char*)m_rom, 0x02000000);
+ if (file.bad())
+ return false;
+ return true;
+ }
+
+ void Memory::LoadRom (const uint8_t* data, uint32_t size)
+ {
+ uint32_t until = std::min(size, 0x02000000u);
+ std::memcpy(m_rom, data, until);
+ std::memset(m_rom+until, 0, 0x02000000-until);
+ }
+
+ Memory::CartError Memory::LoadCart ()
+ {
+ struct stat buf;
+ if (stat(m_cartfile.c_str(), &buf) == -1)
+ return errno == ENOENT ? CERR_NOT_FOUND : CERR_FAIL;
+ SetCartTypeFromSize(buf.st_size);
+ std::ifstream f(m_cartfile.c_str());
+ if (!m_cart->Load(f))
+ return CERR_FAIL;
+ return CERR_NO_ERROR;
+ }
+
+#ifdef __LIBRETRO__
+ bool Memory::LoadCartInferred ()
+ {
+ uint32_t size = *(uint32_t*)(CartMemData+CartMem::MAX_SIZE);
+ if (!size)
+ return false;
+ SetCartTypeFromSize(size);
+ std::istringstream ss;
+ ss.str(std::string((char*)CartMemData, CartMem::MAX_SIZE));
+ if (!m_cart->Load(ss))
+ return false;
+ return true;
+ }
+#endif
+
+#if 0
+ uint8_t* Memory::GetRealAddress (uint32_t add, uint8_t size)
+ {
+ RET_ADD(m_brom , 0x00000000u, 0x00004000u, size);
+ RET_ADD(m_wbram, 0x02000000u, 0x02040000u, size);
+ RET_ADD(m_wbram, 0x02040000u, 0x02080000u, size); // mirror
+ RET_ADD(m_wcram, 0x03000000u, 0x03008000u, size);
+ RET_ADD(m_wcram, 0x03008000u, 0x03010000u, size); // mirror
+ RET_ADD(m_wcram, 0x03010000u, 0x03018000u, size); // mirror
+ RET_ADD(m_wcram, 0x03FF8000u, 0x04000000u, size); // mirror
+ RET_ADD(m_pram , 0x05000000u, 0x05000400u, size);
+ RET_ADD(m_vram , 0x06000000u, 0x06018000u, size);
+ RET_ADD(m_oram , 0x07000000u, 0x07000400u, size);
+ RET_ADD(m_oram , 0x07000400u, 0x07000800u, size);
+ RET_ADD(m_rom , 0x08000000u, 0x0A000000u, size);
+ RET_ADD(m_rom , 0x0A000000u, 0x0C000000u, size);
+ RET_ADD(m_rom , 0x0C000000u, 0x0E000000u, size);
+ RET_ADD(m_sram , 0x0E000000u, 0x0E010000u, size);
+
+ return NULL;
+ }
+#else
+ uint8_t* Memory::GetRealAddress (uint32_t add, uint8_t MET_UNUSED(size))
+ {
+ uint32_t loadd = add & 0x00FFFFFF;
+ switch (add >> 24)
+ {
+ case 0x0:
+ if (m_brom)
+ return m_brom+(loadd & 0x3FFF);
+ else
+ return NULL;
+ case 0x2:
+ return m_wbram+(loadd & 0x3FFFF);
+ case 0x3:
+ return m_wcram+(loadd & 0x7FFF);
+ case 0x5:
+ return m_pram+(loadd & 0x3FF);
+ case 0x6:
+ // we have 64K+32K+(32K mirror) and this whole thing is mirrored
+ // TODO : can't we simplify this with an AND ?
+ if ((loadd % 0x20000) > 0x18000)
+ loadd -= 0x8000;
+ return m_vram+(loadd & 0x1FFFF);
+ case 0x7:
+ return m_oram+(loadd & 0x3FF);
+ case 0x8:
+ return m_rom+loadd;
+ case 0x9:
+ return m_rom+0x01000000+loadd;
+ case 0xA:
+ return m_rom+loadd;
+ case 0xB:
+ return m_rom+0x01000000+loadd;
+ case 0xC:
+ return m_rom+loadd;
+ case 0xD:
+ return m_rom+0x01000000+loadd;
+ default:
+ return NULL;
+ }
+ }
+#endif
+
+ void Memory::UpdateWaitStates (uint16_t waitcnt)
+ {
+ static const uint8_t GamePakTimeFirstAccess[] = { 5, 4, 3, 9 };
+
+ // SRAM
+ m_memtime[0xE] = GamePakTimeFirstAccess[waitcnt & 0x3];
+
+ // Second access
+ if (waitcnt & (0x1 << 4))
+ m_memtimeseq[0] = 2;
+ else
+ m_memtimeseq[0] = 3;
+ if (waitcnt & (0x1 << 7))
+ m_memtimeseq[1] = 2;
+ else
+ m_memtimeseq[1] = 5;
+ if (waitcnt & (0x1 << 10))
+ m_memtimeseq[2] = 2;
+ else
+ m_memtimeseq[2] = 9;
+
+ // First access
+ m_memtime[0x8] = m_memtime[0x9] =
+ GamePakTimeFirstAccess[(waitcnt >> 2) & 0x3];
+ m_memtime[0xA] = m_memtime[0xB] =
+ GamePakTimeFirstAccess[(waitcnt >> 5) & 0x3];
+ m_memtime[0xC] = m_memtime[0xD] =
+ GamePakTimeFirstAccess[(waitcnt >> 8) & 0x3];
+ }
+
+ uint8_t Memory::GetCycles16NoSeq (uint32_t add, uint32_t count)
+ {
+ add >>= 24;
+ switch (add)
+ {
+ case 0x00 :
+ case 0x03 :
+ case 0x04 :
+ case 0x07 :
+ // 32 bits bus
+ return m_memtime[add] * count;
+ case 0x08 :
+ case 0x09 :
+ case 0x0A :
+ case 0x0B :
+ case 0x0C :
+ case 0x0D :
+ // 16 bits bus
+ // sequencial and non sequencial reads don't take the same time
+ return m_memtime[add] + m_memtimeseq[(add-0x08) & 0xFE] * (count-1);
+ default :
+ // 16 bits bus (and 8 for SRAM, but only 8 bits accesses are
+ // authorized in this area, so we don't care about 16 and 32 bits
+ // accesses)
+ return m_memtime[add] * count;
+ }
+ }
+
+ uint8_t Memory::GetCycles16Seq (uint32_t add, uint32_t count)
+ {
+ add >>= 24;
+ switch (add)
+ {
+ case 0x00 :
+ case 0x03 :
+ case 0x04 :
+ case 0x07 :
+ return m_memtime[add] * count;
+ case 0x08 :
+ case 0x09 :
+ case 0x0A :
+ case 0x0B :
+ case 0x0C :
+ case 0x0D :
+ return m_memtimeseq[(add-0x08) & 0xFE] * count;
+ default :
+ return m_memtime[add] * count;
+ }
+ }
+
+ uint8_t Memory::GetCycles32NoSeq (uint32_t add, uint32_t count)
+ {
+ add >>= 24;
+ switch (add)
+ {
+ case 0x00 :
+ case 0x03 :
+ case 0x04 :
+ case 0x07 :
+ return m_memtime[add] * count;
+ case 0x08 :
+ case 0x09 :
+ case 0x0A :
+ case 0x0B :
+ case 0x0C :
+ case 0x0D :
+ return m_memtime[add] + m_memtimeseq[(add-0x08) & 0xFE] * (count*2-1);
+ default :
+ return m_memtime[add] * count * 2;
+ }
+ }
+
+ uint8_t Memory::GetCycles32Seq (uint32_t add, uint32_t count)
+ {
+ add >>= 24;
+ switch (add)
+ {
+ case 0x00 :
+ case 0x03 :
+ case 0x04 :
+ case 0x07 :
+ return m_memtime[add] * count;
+ case 0x08 :
+ case 0x09 :
+ case 0x0A :
+ case 0x0B :
+ case 0x0C :
+ case 0x0D :
+ return m_memtimeseq[(add-0x08) & 0xFE] * count * 2;
+ default :
+ return m_memtime[add] * count * 2;
+ }
+ }
+
+ bool Memory::SaveState (std::ostream& stream)
+ {
+ SS_WRITE_ARRAY(m_memtime);
+ SS_WRITE_ARRAY(m_memtimeseq);
+ // write if we have a custom bios and write it too
+ bool b = m_brom;
+ SS_WRITE_VAR(b);
+ if (b)
+ SS_WRITE_DATA(m_brom, 0x00004000);
+ SS_WRITE_DATA(m_wbram, 0x00040000);
+ SS_WRITE_DATA(m_wcram, 0x00008000);
+ SS_WRITE_DATA(m_pram , 0x00000400);
+ SS_WRITE_DATA(m_vram , 0x00018000);
+ SS_WRITE_DATA(m_oram , 0x00000400);
+
+ SS_WRITE_VAR(m_carttype);
+
+ if (m_cart)
+ if (!m_cart->SaveState(stream))
+ return false;
+
+ return true;
+ }
+
+ bool Memory::LoadState (std::istream& stream)
+ {
+ Reset(0);
+
+ SS_READ_ARRAY(m_memtime);
+ SS_READ_ARRAY(m_memtimeseq);
+ // read if we have a custom bios and write it too
+ bool b;
+ SS_READ_VAR(b);
+ if (b)
+ SS_READ_DATA(m_brom , 0x00004000);
+ else
+ UnloadBios();
+ SS_READ_DATA(m_wbram, 0x00040000);
+ SS_READ_DATA(m_wcram, 0x00008000);
+ SS_READ_DATA(m_pram , 0x00000400);
+ SS_READ_DATA(m_vram , 0x00018000);
+ SS_READ_DATA(m_oram , 0x00000400);
+
+ SS_READ_VAR(m_carttype);
+
+ this->SetCartType(m_carttype);
+ if (m_cart)
+ if (!m_cart->LoadState(stream))
+ return false;
+
+ return true;
+ }
+
+////////////////////////////////////////////////////////////////////////////////
+// Read
+////////////////////////////////////////////////////////////////////////////////
+
+ uint8_t Memory::Read8 (uint32_t add)
+ {
+ switch (add >> 24)
+ {
+ case 0x00:
+ if (R(15) < 0x01000000)
+ return m_brom[add & 0x3FFF];
+ else
+ return 0x0E;
+ case 0x04:
+ return IO.Read8(add);
+ case 0x0E:
+ return ReadCart(add);
+ default:
+ uint8_t *r = (uint8_t*)GetRealAddress(add, 1);
+ if (!r)
+ {
+ debugm("Unknown address for Read8 : " << IOS_ADD << add);
+ // FIXME : in arm state, vba returns read8(r15 + (add & 3))
+ // and in thumb read8(r15 + (add & 1))
+ return Read8(R(15));
+ }
+ return *r;
+ }
+ }
+
+ uint16_t Memory::Read16 (uint32_t add)
+ {
+ switch (add >> 24)
+ {
+ case 0x00:
+ if (R(15) < 0x01000000)
+ return *(uint16_t*)(m_brom+(add & 0x3FFE));
+ else
+ return 0xF00E;
+ case 0x04:
+ return IO.Read16(add);
+ case 0x0D:
+ if (m_carttype == CTYPE_EEPROM512 || m_carttype == CTYPE_EEPROM8192)
+ return static_cast(m_cart)->Read();
+ default:
+ uint16_t *r = (uint16_t*)GetRealAddress(add, 2);
+ if (!r)
+ {
+ debugm("Unknown address for Read16 : " << IOS_ADD << add);
+ if (R(15) == add)
+ met_abort("Illegal PC");
+ // FIXME : in arm state, vba returns read16(r15 + (add & 2))
+ return Read16(R(15));
+ }
+ return *r;
+ }
+ }
+
+ uint32_t Memory::Read32 (uint32_t add)
+ {
+ switch (add >> 24)
+ {
+ case 0x00:
+ if (R(15) < 0x01000000)
+ return *(uint32_t*)(m_brom+(add & 0x3FFC));
+ else
+ // TODO a better bios protection than this one (read8 and read16 too)
+ // this value corresponds to MOVS r15, r14
+ return 0xE1B0F00E;
+ case 0x04:
+ return IO.Read32(add);
+ default:
+ uint32_t *r = (uint32_t*)GetRealAddress(add, 4);
+ if (!r)
+ {
+ debugm("Unknown address for Read32 : " << IOS_ADD << add);
+ if (R(15) == add)
+ met_abort("Illegal PC");
+ if (FLAG_T)
+ {
+ uint16_t o = Read16(R(15));
+ return o | o << 16;
+ }
+ else
+ return Read32(R(15));
+ }
+ return *r;
+ }
+ }
+
+ uint8_t Memory::ReadCart (uint16_t add)
+ {
+ if (m_cart)
+ return m_cart->Read(add);
+ else
+ return 0;
+ }
+
+////////////////////////////////////////////////////////////////////////////////
+// Write
+////////////////////////////////////////////////////////////////////////////////
+
+ void Memory::Write8 (uint32_t add, uint8_t val)
+ {
+ uint8_t baseadd = add >> 24;
+ switch (baseadd)
+ {
+ case 0x04:
+ IO.Write8(add, val);
+ break;
+ case 0x00:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ debugm("Writing on read only memory");
+ break;
+ case 0x0E:
+ WriteCart(add & 0xFFFF, val);
+ break;
+ default:
+ uint8_t *r = (uint8_t*)GetRealAddress(add, 1);
+ if (!r)
+ {
+ debugm("Unknown address for Write8 : " << IOS_ADD << add);
+ }
+ else
+ {
+ *r = val;
+ if (baseadd == 5 || baseadd == 6)
+ r[1] = val;
+ else if (baseadd == 7)
+ met_abort("not implemented");
+ }
+ break;
+ }
+ }
+
+ void Memory::Write16 (uint32_t add, uint16_t val)
+ {
+ add &= 0xFFFFFFFE;
+ uint8_t baseadd = add >> 24;
+ switch (baseadd)
+ {
+ case 0x04:
+ IO.Write16(add, val);
+ break;
+ case 0x00:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ debugm("Writing on read only memory");
+ break;
+ case 0x0E:
+ met_abort("Writing 16 bytes in SRAM/Flash");
+ break;
+ default:
+ uint16_t *r = (uint16_t*)GetRealAddress(add, 2);
+ if (!r)
+ {
+ debugm("Unknown address for Write16 : " << IOS_ADD << add);
+ }
+ else
+ {
+ *r = val;
+ if (!DMA.GraphicDma() && baseadd == 7)
+ LCD.OamWrite16((add & 0x3FF) | 0x07000000);
+ }
+ break;
+ }
+ }
+
+ void Memory::Write32 (uint32_t add, uint32_t val)
+ {
+ add &= 0xFFFFFFFC;
+ uint8_t baseadd = add >> 24;
+ switch (baseadd)
+ {
+ case 0x04:
+ IO.Write32(add, val);
+ break;
+ case 0x00:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ debugm("Writing on read only memory");
+ break;
+ case 0x0E:
+ met_abort("Writing 32 bytes in SRAM/Flash");
+ break;
+ default:
+ uint32_t *r = (uint32_t*)GetRealAddress(add, 4);
+ if (!r)
+ {
+ debugm("Unknown address for Write32 : " << IOS_ADD << add);
+ }
+ else
+ {
+ *r = val;
+ if (!DMA.GraphicDma() && baseadd == 7)
+ LCD.OamWrite32((add & 0x3FF) | 0x07000000);
+ }
+ break;
+ }
+ }
+
+ void Memory::WriteEepromDma (uint32_t src, uint16_t size)
+ {
+ if (m_carttype == CTYPE_UNKNOWN)
+ {
+ if (size == 17 || size == 81)
+ SetCartType(CTYPE_EEPROM8192);
+ else if (size == 9 || size == 73)
+ SetCartType(CTYPE_EEPROM512);
+ else
+ met_abort("Unknown DMA3 size for EEPROM");
+ }
+ else if (m_carttype != CTYPE_EEPROM512 && m_carttype != CTYPE_EEPROM8192)
+ met_abort("EEPROM DMA3 on non EEPROM cartridge");
+
+ Eeprom* eeprom = static_cast(m_cart);
+ if (size == 17 || size == 81)
+ {
+ if (eeprom->GetSize() != 0x2000)
+ met_abort("DMA3 size not corresponding to EEPROM size");
+ }
+ else if (size == 9 || size == 73)
+ {
+ if (eeprom->GetSize() != 0x0200)
+ met_abort("DMA3 size not corresponding to EEPROM size");
+ }
+ else
+ met_abort("Unknown size for EEPROM DMA");
+
+ if (eeprom->Write((uint16_t*)GetRealAddress(src), size))
+ CLOCK.SetBattery(CART_SAVE_TIME);
+ }
+
+#if 0
+ void Memory::ReadEepromDma (uint32_t dest, uint16_t size)
+ {
+ if (m_carttype != CTYPE_EEPROM)
+ met_abort("EEPROM DMA3 on non EEPROM or unknown cartridge");
+ if (size != 68)
+ met_abort("EEPROM DMA3 read with invalid size");
+ Eeprom* eeprom = static_cast(m_cart);
+ eeprom->Read((uint16_t*)GetRealAddress(dest));
+ }
+#endif
+
+#define NO_MEMMEM
+#ifdef NO_MEMMEM // memmem() is a GNU extension, and does not exist in at least MinGW.
+#define memmem memmem_compat
+ // Implementation from Git.
+ static void *memmem_compat(const void *haystack, size_t haystack_len,
+ const void *needle, size_t needle_len)
+ {
+ const char *begin = (const char*)haystack;
+ const char *last_possible = begin + haystack_len - needle_len;
+
+ if (needle_len == 0)
+ return (void *)begin;
+
+ if (haystack_len < needle_len)
+ return NULL;
+
+ for (; begin <= last_possible; begin++)
+ {
+ if (!memcmp(begin, needle, needle_len))
+ return (void *)begin;
+ }
+
+ return NULL;
+ }
+#endif
+
+ void Memory::WriteCart (uint16_t add, uint8_t val)
+ {
+ if (m_carttype == CTYPE_EEPROM512 || m_carttype == CTYPE_EEPROM8192)
+ met_abort("Writing in SRAM/FLASH while using EEPROM");
+ if (!m_cart)
+ if (add == 0x5555)
+ {
+ if (memmem((char*)m_rom, 0x02000000, "FLASH1M_V", 9))
+ SetCartType(CTYPE_FLASH128);
+ else
+ SetCartType(CTYPE_FLASH64);
+ }
+ else
+ SetCartType(CTYPE_SRAM);
+ if (m_cart->Write(add, val))
+ CLOCK.SetBattery(CART_SAVE_TIME);
+ }
+}
diff --git a/libmeteor/source/sound.cpp b/libmeteor/source/sound.cpp
new file mode 100644
index 0000000000..d5d9e38ebd
--- /dev/null
+++ b/libmeteor/source/sound.cpp
@@ -0,0 +1,111 @@
+// 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 .
+
+#include "ameteor/sound.hpp"
+#include "globals.hpp"
+#include "ameteor.hpp"
+
+#include "debug.hpp"
+
+namespace AMeteor
+{
+ Sound::Sound () :
+ m_speaker(IO.GetRef16(Io::SOUND1CNT_L), IO.GetRef16(Io::SOUND1CNT_H),
+ IO.GetRef16(Io::SOUND1CNT_X),
+ IO.GetRef16(Io::SOUND2CNT_L), IO.GetRef16(Io::SOUND2CNT_H),
+ IO.GetRef16(Io::SOUND4CNT_L), IO.GetRef16(Io::SOUND4CNT_H),
+ IO.GetRef16(Io::SOUNDCNT_L), IO.GetRef16(Io::SOUNDCNT_H),
+ IO.GetRef16(Io::SOUNDCNT_X), IO.GetRef16(Io::SOUNDBIAS)),
+ m_fATimer(0),
+ m_fBTimer(0)
+ {
+ }
+
+ void Sound::Reset ()
+ {
+ m_fATimer = m_fBTimer = 0;
+ m_speaker.Reset();
+ }
+
+ void Sound::UpdateCntH1 (uint8_t val)
+ {
+ m_fATimer = (val & (0x1 << 10)) ? 1 : 0;
+ m_fBTimer = (val & (0x1 << 14)) ? 1 : 0;
+ if (val & (0x1 << 3))
+ m_speaker.ResetFifoA();
+ if (val & (0x1 << 7))
+ m_speaker.ResetFifoB();
+ }
+
+ void Sound::TimerOverflow (uint8_t timernum)
+ {
+ // both fifo may be triggered by the same timer
+ if (m_fATimer == timernum)
+ TimerOverflowA();
+ if (m_fBTimer == timernum)
+ TimerOverflowB();
+ }
+
+ inline void Sound::TimerOverflowA ()
+ {
+ if (m_speaker.GetSizeA() <= 16)
+ {
+ DMA.Check(1, Dma::Special);
+ if (m_speaker.GetSizeA() <= 16)
+ {
+ int8_t buf[16] = {0};
+ m_speaker.FillFifoA(buf);
+ }
+ }
+ m_speaker.NextSampleA ();
+ }
+
+ inline void Sound::TimerOverflowB ()
+ {
+ if (m_speaker.GetSizeB() <= 16)
+ {
+ DMA.Check(2, Dma::Special);
+ if (m_speaker.GetSizeB() <= 16)
+ {
+ int8_t buf[16] = {0};
+ m_speaker.FillFifoB(buf);
+ }
+ }
+ m_speaker.NextSampleB ();
+ }
+
+ bool Sound::SaveState (std::ostream& stream)
+ {
+ SS_WRITE_VAR(m_fATimer);
+ SS_WRITE_VAR(m_fBTimer);
+
+ if (!m_speaker.SaveState(stream))
+ return false;
+
+ return true;
+ }
+
+ bool Sound::LoadState (std::istream& stream)
+ {
+ SS_READ_VAR(m_fATimer);
+ SS_READ_VAR(m_fBTimer);
+
+ if (!m_speaker.LoadState(stream))
+ return false;
+
+ return true;
+ }
+}
diff --git a/libmeteor/source/sram.cpp b/libmeteor/source/sram.cpp
new file mode 100644
index 0000000000..d98edfedc4
--- /dev/null
+++ b/libmeteor/source/sram.cpp
@@ -0,0 +1,62 @@
+// 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 .
+
+#include "ameteor/sram.hpp"
+#include
+#include
+#include "globals.hpp"
+
+namespace AMeteor
+{
+ Sram::Sram () :
+ CartMem()
+ {
+ m_size = SIZE;
+
+ *(uint32_t*)(m_data+MAX_SIZE) = m_size;
+ }
+
+ void Sram::Reset ()
+ {
+ std::memset(m_data, 0, SIZE);
+ }
+
+ bool Sram::Load (std::istream& f)
+ {
+ f.read((char*)m_data, SIZE);
+ return f.good();
+ }
+
+ bool Sram::Save (std::ostream& f)
+ {
+ f.write((char*)m_data, SIZE);
+ return f.good();
+ }
+
+ bool Sram::SaveState (std::ostream& stream)
+ {
+ SS_WRITE_DATA(m_data, SIZE);
+
+ return true;
+ }
+
+ bool Sram::LoadState (std::istream& stream)
+ {
+ SS_READ_DATA(m_data, SIZE);
+
+ return true;
+ }
+}
diff --git a/libmeteor/source/timer.cpp b/libmeteor/source/timer.cpp
new file mode 100644
index 0000000000..b287f79649
--- /dev/null
+++ b/libmeteor/source/timer.cpp
@@ -0,0 +1,146 @@
+// 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 .
+
+#include "ameteor/timer.hpp"
+#include "globals.hpp"
+#include "ameteor.hpp"
+
+#include "debug.hpp"
+
+namespace AMeteor
+{
+ static const uint16_t Prescalers[] = {1, 64, 256, 1024};
+
+ void Timer::Reset ()
+ {
+ m_reload = 0;
+ m_count = 0;
+ m_control.w = 0;
+ }
+
+ void Timer::Reload ()
+ {
+ // FIXME to test on hardware, vba update the prescaler even if the timer
+ // didn't restart
+ // if the timer is at 33, 63 cycles have passed since the last timer
+ // increment and prescaler is 1/64, setting TM0CNT even to the same value
+ // it had will reset the prescaler part, so the timer would be at 33 and we
+ // will have to wait another 64 cycles before having it go to 34
+ //
+ // the current behaviour for the above example is that it will reach 34 in
+ // 1 cycle instead of 64
+ if (!m_control.b.start &&
+ (IO.DRead16(Io::TM0CNT_H + m_num * Io::TIMER_SIZE)
+ & (0x1 << 7)))
+ // if start has changed from 0 to 1
+ {
+ m_control.w = IO.DRead16(Io::TM0CNT_H + m_num * Io::TIMER_SIZE);
+ m_count = 65536 - m_reload;
+ if (!m_control.b.countup)
+ {
+ m_count *= Prescalers[m_control.b.prescaler];
+
+ // here, the str instruction which have triggered this function
+ // will be taken in account by this timer
+ // in other words, if the str instruction takes 3 cycles, the
+ // timer will be incremented by 3 cycles just after its start
+ CLOCK.SetTimer(m_num, m_count);
+ }
+ }
+ else
+ {
+ uint16_t cnt = IO.DRead16(Io::TM0CNT_H + m_num * Io::TIMER_SIZE);
+ if (m_control.b.start && (cnt & (0x1 << 7))
+ && m_control.b.prescaler != (cnt & 0x3))
+ met_abort("Prescaler changed while timer " << (int)m_num << " was up");
+
+ m_control.w = IO.DRead16(Io::TM0CNT_H + m_num * Io::TIMER_SIZE);
+
+ if (!m_control.b.start)
+ CLOCK.DisableTimer(m_num);
+ }
+
+ if (m_num == 0 && m_control.b.countup)
+ met_abort("Count-up on first timer !");
+ }
+
+ uint16_t Timer::GetCount () const
+ {
+ if (m_control.b.countup)
+ return 65536 - m_count;
+ else
+ return 65536 -
+ CLOCK.GetTimer(m_num) / Prescalers[m_control.b.prescaler];
+ }
+
+ void Timer::TimeEvent ()
+ {
+ debug("Timer" << (int)m_num << " overflow");
+ SOUND.TimerOverflow(m_num);
+
+ m_count = 65536 - m_reload;
+ if (!m_control.b.countup)
+ {
+ m_count *= Prescalers[m_control.b.prescaler];
+
+ // GetTimer should be zero or less since this function was called
+ if (m_count >= (unsigned short)-CLOCK.GetTimer(m_num))
+ {
+ m_count += CLOCK.GetTimer(m_num);
+
+ CLOCK.SetTimer(m_num, m_count);
+ }
+ else
+ {
+ CLOCK.AddTimer(m_num, m_count);
+ }
+ }
+
+ if (m_control.b.irq)
+ CPU.SendInterrupt(0x1 << (3 + m_num));
+
+ if (m_num != 3)
+ m_next->Countup();
+ }
+
+ void Timer::Countup ()
+ {
+ if (m_control.b.countup)
+ {
+ --m_count;
+ if (m_count == 0)
+ TimeEvent();
+ }
+ }
+
+ bool Timer::SaveState (std::ostream& stream)
+ {
+ SS_WRITE_VAR(m_reload);
+ SS_WRITE_VAR(m_count);
+ SS_WRITE_VAR(m_control);
+
+ return true;
+ }
+
+ bool Timer::LoadState (std::istream& stream)
+ {
+ SS_READ_VAR(m_reload);
+ SS_READ_VAR(m_count);
+ SS_READ_VAR(m_control);
+
+ return true;
+ }
+}