stuff, don't use

This commit is contained in:
goyuken 2012-11-19 22:43:34 +00:00
parent 6bf640c4bc
commit 93ad387db8
86 changed files with 15143 additions and 0 deletions

View File

@ -216,6 +216,8 @@
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.cs" />
<Compile Include="Consoles\Nintendo\Gameboy\GBColors.cs" />
<Compile Include="Consoles\Nintendo\Gameboy\LibGambatte.cs" />
<Compile Include="Consoles\Nintendo\GBA\LibMeteor.cs" />
<Compile Include="Consoles\Nintendo\GBA\Meteor.cs" />
<Compile Include="Consoles\Nintendo\NES\APU.cs" />
<Compile Include="Consoles\Nintendo\NES\BoardSystem.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\AVE-NINA.cs" />

View File

@ -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
{
/// <summary>
/// bindings into libmeteor.dll
/// </summary>
public static class LibMeteor
{
/// <summary>
/// reset the emulation core
/// </summary>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_reset();
/// <summary>
/// signal that you are removing data from the sound buffer.
/// the next time frameadvance() is called, writing will start from the beginning
/// </summary>
/// <returns>the valid length of the buffer, in bytes</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint libmeteor_emptysound();
/// <summary>
/// set up buffers for libmeteor to dump data to. these must be valid before every frameadvance
/// </summary>
/// <param name="vid">buffer to hold video data as BGRA32</param>
/// <param name="vidlen">length in bytes. must be at least 240 * 160 * 4</param>
/// <param name="aud">buffer to hold audio data as stereo s16le</param>
/// <param name="audlen">length in bytes. must be 0 mod 4 (hold a full stereo sample set)</param>
/// <returns>false if some problem. buffers will not be valid in this case</returns>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool libmeteor_setbuffers(IntPtr vid, uint vidlen, IntPtr aud, uint audlen);
/// <summary>
/// initialize the library
/// </summary>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_init();
/// <summary>
/// run emulation for one frame, updating sound and video along the way
/// </summary>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_frameadvance();
/// <summary>
/// load a rom image
/// </summary>
/// <param name="data">raw rom data. need not persist past this call</param>
/// <param name="datalen">length of data in bytes</param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_loadrom(byte[] data, uint datalen);
/// <summary>
/// load a bios image
/// </summary>
/// <param name="data">raw bios data. need not persist past this call</param>
/// <param name="datalen">length of data in bytes</param>
[DllImport("libmeteor.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void libmeteor_loadbios(byte[] data, uint datalen);
}
}

View File

@ -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<MemoryDomain> 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
}
}

View File

@ -164,6 +164,10 @@ namespace BizHawk
case ".CRT":
Game.System = "C64";
break;
case ".GBA":
Game.System = "GBA";
break;
}
Game.Name = Path.GetFileNameWithoutExtension(fileName).Replace('_', ' ');

View File

@ -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;
}
}

View File

@ -437,6 +437,10 @@ namespace BizHawk.MultiClient
{
return GetC64ControllersAsMnemonic();
}
else if (ControlType == "GBA Controller")
{
return "EAT AT JOE'S";
}
StringBuilder input = new StringBuilder("|");

96
libmeteor/cinterface.cpp Normal file
View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __AMETEOR_H__
#define __AMETEOR_H__
#include "ameteor/memory.hpp"
#include "ameteor/io.hpp"
#include "ameteor/dma.hpp"
#include "ameteor/interpreter.hpp"
#include "ameteor/lcd.hpp"
#include "ameteor/clock.hpp"
#include "ameteor/timer.hpp"
#include "ameteor/sound.hpp"
#include "ameteor/keypad.hpp"
namespace AMeteor
{
extern Clock _clock;
extern Io _io;
extern Interpreter _cpu;
extern Memory _memory;
extern Dma _dma;
extern Lcd _lcd;
extern Sound _sound;
extern Keypad _keypad;
extern Timer _timer0;
extern Timer _timer1;
extern Timer _timer2;
extern Timer _timer3;
const uint32_t UNIT_CLOCK = 0x0001;
const uint32_t UNIT_IO = 0x0002;
const uint32_t UNIT_CPU = 0x0004;
const uint32_t UNIT_MEMORY = 0x0008;
const uint32_t UNIT_DMA = 0x0010;
const uint32_t UNIT_LCD = 0x0020;
const uint32_t UNIT_SOUND = 0x0040;
const uint32_t UNIT_KEYPAD = 0x0080;
const uint32_t UNIT_TIMER0 = 0x0100;
const uint32_t UNIT_TIMER1 = 0x0200;
const uint32_t UNIT_TIMER2 = 0x0400;
const uint32_t UNIT_TIMER3 = 0x0800;
const uint32_t UNIT_MEMORY_ROM = 0x1000;
const uint32_t UNIT_MEMORY_BIOS = 0x2000;
const uint32_t UNIT_ALL = 0x3FFF;
void Reset (uint32_t units);
bool SaveState (const char* filename);
bool LoadState (const char* filename);
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
inline void Run (unsigned int cycles)
{
_cpu.Run(cycles);
}
inline void Stop ()
{
_cpu.Stop();
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_D_SOUND_H__
#define __AUDIO_D_SOUND_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Audio
{
class DSound
{
public :
static const int BUFFER_SIZE = 32;
DSound ();
void FillFifo (int8_t* buffer);
void FillFifo (int8_t sample);
void NextSample ()
{
// if the buffer is empty, there is nothing to do
if (m_size)
// if this was the last sample, we reset all and send 0 to all next
// GetSample()s until the buffer is refilled
if (--m_size == 0)
Reset();
// else, we go on to next sample and we go to the first if we got
// to the last
else if (++m_rpos >= BUFFER_SIZE)
m_rpos = 0;
}
void Reset ()
{
m_buffer[0] = m_size = m_rpos = m_wpos = 0;
}
int8_t GetSample()
{
return m_buffer[m_rpos];
}
uint8_t GetSize ()
{
return m_size;
};
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
int8_t m_buffer[BUFFER_SIZE];
uint8_t m_rpos, m_wpos;
uint8_t m_size;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_SOUND_1_H__
#define __AUDIO_SOUND_1_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Audio
{
class Sound1
{
public :
Sound1 (uint16_t& cntl, uint16_t& cnth, uint16_t& cntx,
uint16_t freq);
void Reset ();
// call this at the frequence given in constructor
void SoundTick ();
void ResetSound ();
void ResetEnvelope ()
{
m_envelope = 0;
}
int8_t GetSample () const
{
return m_sample;
}
bool IsOn () const
{
return m_on;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
uint16_t &m_cntl, &m_cnth, &m_cntx;
bool m_on;
// positions in Period, Sweep time and Envelope step time
uint32_t m_posP, m_posS, m_posE;
int8_t m_sample;
// sample period in cycles
uint16_t m_speriod;
// envelope level
uint8_t m_envelope;
// sound length in cycles
uint32_t m_length;
bool m_timed;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_SOUND_2_H__
#define __AUDIO_SOUND_2_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Audio
{
class Sound2
{
public :
Sound2 (uint16_t& cntl, uint16_t& cnth, uint16_t freq);
void Reset ();
// call this at the frequence given in constructor
void SoundTick ();
void ResetSound ();
void ResetEnvelope ()
{
m_envelope = 0;
}
int8_t GetSample () const
{
return m_sample;
}
bool IsOn () const
{
return m_on;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
uint16_t &m_cntl, &m_cnth;
bool m_on;
uint32_t m_posP, m_posE;
int8_t m_sample;
uint16_t m_speriod;
uint8_t m_envelope;
uint32_t m_length;
bool m_timed;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_SOUND_4_H__
#define __AUDIO_SOUND_4_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Audio
{
void InitNoise ();
class Sound4
{
public :
Sound4 (uint16_t& cntl, uint16_t& cnth, uint16_t freq);
void Reset ();
// call this at the frequence given in constructor
void SoundTick ();
void ResetSound ();
void ResetEnvelope ()
{
m_envelope = 0;
}
int8_t GetSample () const
{
return m_sample;
}
bool IsOn () const
{
return m_on;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
uint16_t &m_cntl, &m_cnth;
bool m_on;
// positions in Period, position in noise and Envelope step time
uint32_t m_posP, m_posN, m_posE;
int8_t m_sample;
// sample period in cycles
uint16_t m_speriod;
// envelope level
uint8_t m_envelope;
// sound length in cycles
uint32_t m_length;
bool m_timed;
// clock divider
uint8_t m_div;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __AUDIO_SPEAKER_H__
#define __AUDIO_SPEAKER_H__
#include <stdint.h>
#include <istream>
#include <ostream>
#include <syg/signal.hpp>
#include "sound1.hpp"
#include "sound2.hpp"
#include "sound4.hpp"
#include "dsound.hpp"
namespace AMeteor
{
namespace Audio
{
class Speaker
{
public :
typedef syg::slot1<void, const int16_t*> FrameSlot;
Speaker (uint16_t& cnt1l, uint16_t& cnt1h, uint16_t& cnt1x,
uint16_t& cnt2l, uint16_t& cnt2h,
uint16_t& cnt4l, uint16_t& cnt4h,
uint16_t& cntl, uint16_t& cnth, uint16_t& cntx, uint16_t& bias);
~Speaker ();
inline void SetFrameSlot (const FrameSlot& slot);
void Reset ();
inline void ResetSound1 ();
inline void ResetSound2 ();
inline void ResetSound4 ();
inline void ResetSound1Envelope ();
inline void ResetSound2Envelope ();
inline void ResetSound4Envelope ();
inline void FillFifoA (int8_t* buffer);
inline void FillFifoB (int8_t* buffer);
inline void FillFifoA (int8_t sample);
inline void FillFifoB (int8_t sample);
inline void ResetFifoA ();
inline void ResetFifoB ();
inline void NextSampleA ();
inline void NextSampleB ();
inline uint8_t GetSizeA();
inline uint8_t GetSizeB();
void SoundTick ();
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
Sound1 m_sound1;
Sound2 m_sound2;
Sound4 m_sound4;
DSound m_dsa, m_dsb;
uint16_t &m_cntl, &m_cnth, &m_cntx, &m_bias;
FrameSlot m_sig_frame;
int16_t MixSample (uint16_t cntl, uint8_t cnth);
};
inline void Speaker::SetFrameSlot (const FrameSlot& slot)
{
m_sig_frame = slot;
}
inline void Speaker::ResetSound1 ()
{
m_sound1.ResetSound ();
}
inline void Speaker::ResetSound2 ()
{
m_sound2.ResetSound ();
}
inline void Speaker::ResetSound4 ()
{
m_sound4.ResetSound ();
}
inline void Speaker::ResetSound1Envelope ()
{
m_sound1.ResetEnvelope();
}
inline void Speaker::ResetSound2Envelope ()
{
m_sound2.ResetEnvelope();
}
inline void Speaker::ResetSound4Envelope ()
{
m_sound4.ResetEnvelope();
}
inline void Speaker::FillFifoA (int8_t* buffer)
{
m_dsa.FillFifo(buffer);
}
inline void Speaker::FillFifoB (int8_t* buffer)
{
m_dsb.FillFifo(buffer);
}
inline void Speaker::FillFifoA (int8_t sample)
{
m_dsa.FillFifo(sample);
}
inline void Speaker::FillFifoB (int8_t sample)
{
m_dsb.FillFifo(sample);
}
inline void Speaker::ResetFifoA ()
{
m_dsa.Reset();
}
inline void Speaker::ResetFifoB ()
{
m_dsb.Reset();
}
inline void Speaker::NextSampleA ()
{
m_dsa.NextSample();
}
inline void Speaker::NextSampleB ()
{
m_dsb.NextSample();
}
inline uint8_t Speaker::GetSizeA()
{
return m_dsa.GetSize();
}
inline uint8_t Speaker::GetSizeB()
{
return m_dsb.GetSize();
}
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __BIOS_H__
#define __BIOS_H__
#include "ameteor.hpp"
namespace AMeteor
{
namespace Bios
{
// Entry point
void Bios000h ();
// Software IRQ
void Bios008h ();
void Bios168h ();
// Return from IntrWait (after the IRQ)
void Bios338h ();
// IRQ
void Bios018h ();
void Bios130h ();
void SoftReset (); // 00
void RegisterRamReset (); // 01
void Halt (); // 02
void IntrWait (); // 04
void VBlankIntrWait (); // 05
void Div (); // 06
void DivArm (); // 07
void Sqrt (); // 08
void ArcTan (); // 09
void ArcTan2 (); // 0A
void CpuSet (); // 0B
void CpuFastSet (); // 0C
void BgAffineSet (); // 0E
void ObjAffineSet (); // 0F
void LZ77UnCompWram (); // 11
void LZ77UnCompVram (); // 12
void HuffUnComp (); // 13
void RLUnCompWram (); // 14
void RLUnCompVram (); // 15
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __CART_MEM_H__
#define __CART_MEM_H__
#include <fstream>
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class CartMem
{
public:
static const unsigned int MAX_SIZE = 0x20000;
CartMem();
virtual ~CartMem();
virtual void Reset () = 0;
virtual bool Load (std::istream& stream) = 0;
virtual bool Save (std::ostream& stream) = 0;
virtual uint8_t Read (uint16_t add) = 0;
// returns true if memory has been updated
virtual bool Write (uint16_t add, uint8_t val) = 0;
virtual bool SaveState (std::ostream& stream);
virtual bool LoadState (std::istream& stream);
protected:
uint8_t* m_data;
uint32_t m_size;
};
#ifdef __LIBRETRO__
extern uint8_t CartMemData[CartMem::MAX_SIZE+4];
#endif
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __CLOCK_H__
#define __CLOCK_H__
#include <stdint.h>
#include <climits>
#include <vector>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Clock
{
public :
Clock ()
{
Reset();
}
void Reset ();
void ResetCounter()
{
m_count = 0;
}
unsigned int GetCounter() const
{
return m_count;
}
void TimePass (unsigned short cycles)
{
m_cycles += cycles;
}
void Commit ();
void WaitForNext ();
void AddLcd (uint32_t cycles)
{
// The lcd clock is always enabled
m_lcd += cycles;
SetFirst();
}
void AddTimer (uint8_t num, uint32_t cycles)
{
if (m_timer[num] == INT_MAX)
m_timer[num] = cycles;
else
m_timer[num] += cycles;
SetFirst();
}
void SetTimer (uint8_t num, uint32_t cycles)
{
m_timer[num] = cycles;
SetFirst();
}
void DisableTimer (uint8_t num)
{
m_timer[num] = INT_MAX;
SetFirst();
}
int GetTimer (uint8_t num)
{
return m_timer[num];
}
void SetBattery (uint32_t cycles)
{
m_battery = cycles;
}
void DisableBattery ()
{
m_battery = INT_MAX;
// no need to SetFirst since battery will be disabled only in TimeEvent
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
// XXX freq
static const int SOUND_PERIOD = 380;
unsigned short m_cycles;
unsigned short m_first;
int m_lcd, m_timer[4], m_sound, m_battery;
unsigned int m_count;
void SetFirst ();
};
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __CPU_H__
#define __CPU_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Cpu
{
public :
union Psr
{
uint32_t dw;
struct
{
unsigned int mode : 5;
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

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __ARG_IMMEDIATE_H__
#define __ARG_IMMEDIATE_H__
#include "argument.hpp"
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class ArgImmediate : public Argument
{
public :
ArgImmediate (int32_t imm) :
m_imm(imm)
{ }
Argument* Clone () const;
std::string GetString () const;
private :
int32_t m_imm;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __ARG_MUL_REGISTERS_H__
#define __ARG_MUL_REGISTERS_H__
#include "argument.hpp"
#include <stdint.h>
#include <vector>
namespace AMeteor
{
namespace Disassembler
{
enum SpecialRegister
{
SPREG_NONE = 0,
SPREG_LR = 1,
SPREG_PC = 2
};
class ArgMulRegisters : public Argument
{
public :
ArgMulRegisters (bool forceuser) :
m_lastreg(SPREG_NONE),
m_forceuser(forceuser)
{ }
Argument* Clone () const;
void AddRegister(uint8_t reg)
{
m_regs.push_back(reg);
}
void AddLastRegister(SpecialRegister reg)
{
m_lastreg = reg;
}
std::string GetString () const;
private :
typedef std::vector<uint8_t> Registers;
Registers m_regs;
SpecialRegister m_lastreg;
bool m_forceuser;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __ARG_PSR_H__
#define __ARG_PSR_H__
#include "argument.hpp"
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class ArgPsr : public Argument
{
public :
ArgPsr (bool spsr, uint8_t fields = 0xFF) :
m_spsr(spsr),
m_fields(fields)
{ }
Argument* Clone () const;
std::string GetString () const;
private :
bool m_spsr;
uint8_t m_fields;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __ARG_REGISTER_H__
#define __ARG_REGISTER_H__
#include "argument.hpp"
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class ArgRegister : public Argument
{
public :
ArgRegister (uint8_t reg, bool writeback = false,
bool special = false, bool memory = false) :
m_reg(reg),
m_writeback(writeback),
m_special(special),
m_memory(memory)
{ }
Argument* Clone () const;
std::string GetString () const;
uint8_t GetRegister () const
{
return m_reg;
}
private :
uint8_t m_reg;
bool m_writeback;
bool m_special;
bool m_memory;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __ARG_RELATIVE_H__
#define __ARG_RELATIVE_H__
#include "argument.hpp"
#include "argregister.hpp"
#include "argimmediate.hpp"
namespace AMeteor
{
namespace Disassembler
{
class ArgRelative : public Argument
{
public :
ArgRelative (const ArgRegister& reg, const Argument& off,
bool pre, bool up, bool writeback);
ArgRelative (const ArgRelative& arg);
~ArgRelative ();
Argument* Clone () const;
std::string GetString () const;
private :
ArgRegister m_reg;
const Argument* m_off;
bool m_pre;
bool m_up;
bool m_writeback;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __ARG_SHIFT_H__
#define __ARG_SHIFT_H__
#include "argument.hpp"
namespace AMeteor
{
namespace Disassembler
{
enum ShiftType
{
SHIFT_LSL = 0,
SHIFT_LSR,
SHIFT_ASR,
SHIFT_ROR,
SHIFT_RRX
};
class ArgShift : public Argument
{
public :
ArgShift (const Argument& arg1, const Argument& arg2,
ShiftType type, bool memory);
ArgShift (const ArgShift& arg);
~ArgShift ();
Argument* Clone () const;
std::string GetString () const;
private :
const Argument* m_arg1;
const Argument* m_arg2;
ShiftType m_type;
bool m_memory;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __ARG_U_IMMEDIATE_H__
#define __ARG_U_IMMEDIATE_H__
#include "argument.hpp"
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class ArgUImmediate : public Argument
{
public :
ArgUImmediate (uint32_t imm) :
m_imm(imm)
{ }
Argument* Clone () const;
std::string GetString () const;
private :
uint32_t m_imm;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __ARGUMENT_H__
#define __ARGUMENT_H__
#include <string>
namespace AMeteor
{
namespace Disassembler
{
class Argument
{
public :
virtual ~Argument ()
{ }
virtual Argument* Clone () const = 0;
virtual std::string GetString () const = 0;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __ARGUMENTS_H__
#define __ARGUMENTS_H__
#include "argument.hpp"
#include <vector>
namespace AMeteor
{
namespace Disassembler
{
class Arguments
{
public :
~Arguments ();
void Clear ();
void AddArgument(const Argument& arg)
{
m_args.push_back(arg.Clone());
}
std::string GetString () const;
private :
std::vector<Argument*> m_args;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __INSTRUCTION_H__
#define __INSTRUCTION_H__
#include "arguments.hpp"
#include <string>
#include <stdint.h>
namespace AMeteor
{
namespace Disassembler
{
class Instruction
{
public :
Instruction ()
{
}
explicit Instruction (uint32_t offset, uint32_t code)
{
ParseArm (offset, code);
}
explicit Instruction (uint32_t offset, uint16_t code)
{
ParseThumb(offset, code);
}
void Clear ();
void ParseArm (uint32_t offset, uint32_t code);
void ParseThumb (uint32_t offset, uint16_t code);
const std::string& GetOperator () const
{
return m_operator;
}
std::string GetArguments () const
{
return m_args.GetString();
}
std::string ToString () const
{
return GetOperator() + ' ' + GetArguments();
}
private :
std::string m_operator;
Arguments m_args;
void ParseArmDataProc (uint32_t code);
void ParseArmCondition (uint32_t code);
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __DMA_H__
#define __DMA_H__
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Dma
{
public :
enum Reason
{
Immediately = 0,
VBlank,
HBlank,
Special
};
Dma () :
m_graphic(false)
{ }
void Reset ();
bool GraphicDma () const
{
return m_graphic;
}
void SetReload(uint8_t channum, uint16_t reload)
{
m_chans[channum].reload = reload;
}
void UpdateCnt (uint8_t channum);
void Check(uint8_t channum, uint8_t reason);
inline void CheckAll(uint8_t reason)
{
Check(0, reason);
Check(1, reason);
Check(2, reason);
Check(3, reason);
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
struct Channel
{
Channel () :
reload(0),
src(0),
dest(0),
count(0),
control(0)
{ }
uint16_t reload;
uint32_t src;
uint32_t dest;
uint16_t count;
union Control
{
Control(uint16_t v) :
w(v)
{ }
uint16_t w;
struct
{
unsigned int unused : 5;
unsigned int dest : 2;
unsigned int src : 2;
unsigned int repeat : 1;
unsigned int type : 1;
unsigned int drq : 1;
unsigned int start : 2;
unsigned int irq : 1;
unsigned int enable : 1;
} b;
} control;
};
Channel m_chans[4];
bool m_graphic;
void Process(uint8_t channel);
void Copy (uint32_t& src, uint32_t& dest, int8_t s_inc, int8_t d_inc,
uint32_t count, bool word);
};
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __EEPROM_H__
#define __EEPROM_H__
#include "cartmem.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Eeprom : public CartMem
{
public :
Eeprom (bool big);
void Reset ();
uint16_t GetSize () const
{
return m_size;
}
bool Load (std::istream& f);
bool Save (std::ostream& f);
uint8_t Read (uint16_t add);
bool Write (uint16_t add, uint8_t val);
uint16_t Read ();
//bool Write (uint16_t val);
bool Write (uint16_t* data, uint16_t size);
//XXX
#if 0
void Read (uint16_t* pOut);
#endif
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
enum State
{
IDLE,
//WAITING,
//READ_ADD,
//READ_END,
READ_GARBAGE,
READ_DATA
/*WRITE_ADD,
WRITE_DATA,
WRITE_END*/
};
uint8_t m_state;
uint16_t m_add;
uint8_t m_pos;
};
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __FLASH_H__
#define __FLASH_H__
#include "cartmem.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Flash : public CartMem
{
public :
Flash (bool big);
void Reset ();
bool Load (std::istream& f);
bool Save (std::ostream& f);
uint8_t Read (uint16_t add);
bool Write (uint16_t add, uint8_t val);
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
uint8_t m_device_id;
uint8_t m_manufacturer_id;
enum State
{
NORMAL,
CMD1,
CMD2,
ID,
ERASE1,
ERASE2,
ERASE3,
WRITE
};
State m_state;
};
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_BG_LAYER_H__
#define __GRAPHICS_BG_LAYER_H__
#include "ameteor/memory.hpp"
#include "ameteor/io.hpp"
#include <vector>
#include <stdint.h>
namespace AMeteor
{
namespace Graphics
{
class BgLayer
{
public :
BgLayer (int8_t num, Memory& memory, Io& io, uint16_t* pPalette);
~BgLayer ();
inline uint8_t GetNum () const;
inline uint8_t GetPriority () const;
void DrawLine0 (uint8_t line, uint16_t* ptr);
void DrawLine2 (uint16_t* ptr,
int32_t refX, int32_t refY,
int16_t dx, int16_t dy);
void DrawLine3 (uint16_t* ptr,
int32_t refX, int32_t refY,
int16_t dx, int16_t dy);
void DrawLine4 (uint8_t line, uint16_t* ptr,
int32_t curX, int32_t curY,
int16_t dx, int16_t dmx, int16_t dy, int16_t dmy, bool frame1);
void FillList ();
void UpdateCnt (uint16_t cnt);
inline void UpdateXOff (uint16_t off);
inline void UpdateYOff (uint16_t off);
private :
Memory& m_memory;
Io& m_io;
const uint8_t m_num;
uint8_t m_priority;
uint16_t m_cnt;
bool m_hicolor;
uint16_t m_xoff, m_yoff;
// in text mode
uint8_t m_tWidth, m_tHeight;
// in rotation/scale mode
uint8_t m_rWidth, m_rHeight;
uint32_t m_mapAdd;
uint32_t m_charAdd;
uint16_t* m_pPalette;
};
inline uint8_t BgLayer::GetNum () const
{
return m_num;
}
inline uint8_t BgLayer::GetPriority () const
{
return m_priority;
}
inline void BgLayer::UpdateXOff (uint16_t off)
{
m_xoff = off;
}
inline void BgLayer::UpdateYOff (uint16_t off)
{
m_yoff = off;
}
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_OBJECT_H__
#define __GRAPHICS_OBJECT_H__
#include <stdint.h>
#include <vector>
#include <list>
namespace AMeteor
{
namespace Graphics
{
class Object
{
public :
static const uint8_t FLIP_HORIZONTAL = 1;
static const uint8_t FLIP_VERTICAL = 2;
Object (uint16_t* pPalette, uint8_t* pChar);
// Warning : this copy constructor must not be used on an used object
// use it only on just created objects
Object (const Object& obj);
inline uint8_t GetPriority () const;
inline int8_t GetRotationParam () const;
inline uint16_t GetTileNum () const;
inline bool IsWindow () const;
void DrawLine (uint8_t line, uint32_t* surface, bool oneDim,
uint8_t mosaic);
void DrawLineRot (uint8_t line, uint32_t* surface, bool oneDim,
int16_t a, int16_t b, int16_t c, int16_t d, uint8_t mosaic);
void DrawWindow (uint8_t line, uint8_t* surface, bool oneDim,
uint8_t mask);
void DrawWindowRot (uint8_t line, uint8_t* surface,
bool oneDim, int16_t a, int16_t b, int16_t c, int16_t d,
uint8_t mask);
void UpdateAttrs (uint16_t attr0, uint16_t attr1, uint16_t attr2);
void UpdateAttr0 (uint16_t attr);
void UpdateAttr1 (uint16_t attr);
void UpdateAttr2 (uint16_t attr);
private :
inline void SetSize ();
enum Shape
{
SHAPE_SQUARE = 0,
SHAPE_HORIZONTAL,
SHAPE_VERTICAL,
SHAPE_PROHIBITED
};
uint16_t m_attr0, m_attr1, m_attr2;
uint8_t m_width, m_height;
uint16_t* m_pPalette;
uint8_t* m_pChar;
uint32_t m_charBegin;
uint32_t m_charEnd;
};
inline int8_t Object::GetRotationParam () const
{
return (m_attr0 & (0x1 << 8)) ? (m_attr1 >> 9) & 0x1F : -1;
}
inline uint8_t Object::GetPriority () const
{
return (m_attr2 >> 10) & 0x3;
}
inline bool Object::IsWindow () const
{
return (m_attr0 & (0x3 << 10)) == (0x2 << 10);
}
inline uint16_t Object::GetTileNum () const
{
return (m_attr2 & 0x3FF);
}
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_OBJECTS_H__
#define __GRAPHICS_OBJECTS_H__
#include "object.hpp"
#include "ameteor/memory.hpp"
#include "ameteor/io.hpp"
#include <vector>
namespace AMeteor
{
namespace Graphics
{
class Objects
{
public :
Objects (Memory& memory, Io& io, uint16_t* pPalette);
void DrawLine (uint8_t line, uint32_t* surface);
void DrawLineHighOnly (uint8_t line, uint32_t* surface);
void DrawWindow (uint8_t line, uint8_t* surface);
void OamWrite (uint32_t begin, uint32_t end);
void OamWrite16 (uint32_t add);
void OamWrite32 (uint32_t add);
private :
typedef std::vector<Object> Objs;
Io& m_io;
Objs m_objs;
uint16_t* m_pOam;
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_RENDERER_H__
#define __GRAPHICS_RENDERER_H__
#include <stdint.h>
#include <syg/signal.hpp>
namespace AMeteor
{
namespace Graphics
{
class Renderer
{
public:
typedef syg::slot1<void, const uint16_t*> FrameSlot;
Renderer(const uint16_t* surface);
inline void SetFrameSlot(const FrameSlot& slot);
void VBlank();
private :
const uint16_t* m_base;
FrameSlot m_sig_frame;
};
void Renderer::SetFrameSlot(const FrameSlot& slot)
{
m_sig_frame = slot;
}
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __GRAPHICS_SCREEN_H__
#define __GRAPHICS_SCREEN_H__
#include "bglayer.hpp"
#include "objects.hpp"
#include "renderer.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
namespace Graphics
{
class Screen
{
public :
static const uint8_t WIDTH = 240;
static const uint8_t HEIGHT = 160;
// we skip x frames every FRMSKIP_TOTAL frames
static const uint8_t FRMSKIP_TOTAL = 10;
Screen (Memory& memory, Io& io);
~Screen ();
Renderer& GetRenderer()
{
return m_renderer;
}
const Renderer& GetRenderer() const
{
return m_renderer;
}
const uint16_t* GetSurface () const
{
return m_surface;
}
void SetFrameskip (uint8_t skip)
{
m_frameskip = skip;
m_curframe = 0;
}
void DrawLine (uint8_t line);
void UpdateDispCnt (uint16_t dispcnt)
{
m_dispcnt = dispcnt;
}
#define UPDATE_BG_CNT(num) \
void UpdateBg##num##Cnt (uint16_t cnt) \
{ \
m_bgLayer##num.UpdateCnt(cnt); \
}
UPDATE_BG_CNT(0)
UPDATE_BG_CNT(1)
UPDATE_BG_CNT(2)
UPDATE_BG_CNT(3)
#undef UPDATE_BG_CNT
#define UPDATE_BG_OFF(num, coord) \
void UpdateBg##num##coord##Off (uint16_t off) \
{ \
m_bgLayer##num.Update##coord##Off(off); \
}
UPDATE_BG_OFF(0, X)
UPDATE_BG_OFF(0, Y)
UPDATE_BG_OFF(1, X)
UPDATE_BG_OFF(1, Y)
UPDATE_BG_OFF(2, X)
UPDATE_BG_OFF(2, Y)
UPDATE_BG_OFF(3, X)
UPDATE_BG_OFF(3, Y)
#undef UPDATE_BG_OFF
#define UPDATE_BG_REF(num, coord) \
void UpdateBg##num##Ref##coord(int32_t x) \
{ \
m_ref##coord##num = (x & (0x1 << 27)) ? x | 0xF0000000 : x & 0x07FFFFFF; \
}
UPDATE_BG_REF(2, X)
UPDATE_BG_REF(2, Y)
UPDATE_BG_REF(3, X)
UPDATE_BG_REF(3, Y)
#undef UPDATE_BG_REF
void OamWrite (uint32_t begin, uint32_t end)
{
m_objs.OamWrite (begin, end);
}
void OamWrite16 (uint32_t add)
{
m_objs.OamWrite16 (add);
}
void OamWrite32 (uint32_t add)
{
m_objs.OamWrite32 (add);
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
Io& m_io;
uint16_t* m_surface;
Renderer m_renderer;
uint8_t m_frameskip, m_curframe;
uint16_t m_dispcnt;
int32_t m_refX2, m_refY2, m_refX3, m_refY3;
uint16_t* m_pPalette;
// FIXME is this REALLY useful ?
static BgLayer Screen::* const BgLayers [4];
BgLayer m_bgLayer0, m_bgLayer1, m_bgLayer2, m_bgLayer3;
Objects m_objs;
void DrawWindow (uint8_t line, uint8_t* surface,
uint16_t win0v, uint16_t win0h, uint8_t mask);
};
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __INTERPRETER_H__
#define __INTERPRETER_H__
#include <stdint.h>
#include <istream>
#include <ostream>
#include "cpu.hpp"
#define ARM(name) \
inline void a##name ()
#define NIARM(name) \
void a##name ()
#define THUMB(name) \
inline void t##name ()
#define NITHUMB(name) \
void t##name ()
namespace AMeteor
{
class Interpreter : public Cpu
{
public :
Interpreter();
void Reset ()
{
m_interrupt = m_interrupt_ = false;
m_run = false;
Cpu::Reset();
}
void SoftReset ()
{
m_interrupt_ = m_interrupt = false;
Cpu::SoftReset();
}
void SendInterrupt (uint16_t interrupt);
void CheckInterrupt ();
void Run (unsigned int cycles);
void Stop ()
{
m_run = false;
}
bool IsRunning ()
{
return m_run;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
protected :
void SetInterrupt (bool interrupt)
{
m_interrupt = interrupt;
}
private :
bool m_run;
bool m_interrupt;
bool m_interrupt_;
uint32_t code;
uint8_t& m_haltcnt;
// only for use in halt mode, in normal mode we use m_interrupt
uint16_t& m_if;
uint16_t& m_ie;
NIARM(_Code);
inline bool a_CheckCondition (uint8_t cond);
inline void a_DataProcCore(uint8_t rd, uint32_t op1, uint32_t op2,
bool shiftcarry);
ARM(BXBLX);
ARM(BBL);
NIARM(_DataProcShiftImm);
NIARM(_DataProcShiftReg);
NIARM(_DataProcImm);
ARM(PSR);
ARM(_Multiply);
ARM(LDRSTR);
ARM(STRLDR_HD);
ARM(LDMSTM);
ARM(SWP);
ARM(SWI);
NITHUMB(_Code);
THUMB(_Shift);
THUMB(ADDSUB);
THUMB(_Imm);
THUMB(_ALU);
THUMB(_HiRegOp);
THUMB(LDRimm);
THUMB(STRLDRreg);
THUMB(STRLDRoff);
THUMB(LDRHSTRHoff);
THUMB(STRLDRsp);
THUMB(ADDpcsp);
THUMB(ADDsp);
THUMB(PUSHPOP);
THUMB(STMLDM);
THUMB(_CondBranch);
THUMB(SWI);
THUMB(B);
THUMB(_BL1);
THUMB(_BL2);
};
}
#undef ARM
#undef NIARM
#undef THUMB
#undef NITHUMB
#endif

View File

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

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __KEYPAD_H__
#define __KEYPAD_H__
#include <stdint.h>
#include <map>
namespace AMeteor
{
class Keypad
{
public :
enum Button
{
BTN_A = 0x001,
BTN_B = 0x002,
BTN_SELECT = 0x004,
BTN_START = 0x008,
BTN_RIGHT = 0x010,
BTN_LEFT = 0x020,
BTN_UP = 0x040,
BTN_DOWN = 0x080,
BTN_R = 0x100,
BTN_L = 0x200
};
Keypad ();
void Reset ()
{
}
void BindKey(int code, Button btn)
{
m_keys[code] = (uint16_t)btn;
}
void UnbindKey(int code)
{
m_keys.erase(code);
}
void BindJoy(uint16_t joyid, uint16_t button, Button btn)
{
m_joys[((int)joyid) << 16 | button] = (uint16_t)btn;
}
void UnbindJoy(uint16_t joyid, uint16_t button)
{
m_joys.erase(((int)joyid) << 16 | button);
}
void BindAxis(uint16_t joyid, uint16_t axis, Button btn)
{
m_axis[((int)joyid) << 16 | axis] = (uint16_t)btn;
}
void UnbindAxis(uint16_t joyid, uint16_t axis)
{
m_axis.erase(((int)joyid) << 16 | axis);
}
void ResetBindings()
{
m_keys.clear();
m_joys.clear();
m_axis.clear();
}
inline void SetPadState(uint16_t keys);
void KeyPressed(int code);
void KeyReleased(int code);
void JoyButtonPressed (uint16_t joyid, uint16_t button);
void JoyButtonReleased (uint16_t joyid, uint16_t button);
void JoyMoved (uint16_t joyid, uint16_t axis, float pos);
void VBlank ();
private :
uint16_t& m_keyinput;
uint16_t& m_keycnt;
std::map<int, uint16_t> m_keys;
std::map<int, uint16_t> m_joys;
std::map<int, uint16_t> m_axis;
};
void Keypad::SetPadState(uint16_t keys)
{
m_keyinput = keys;
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __LCD_H__
#define __LCD_H__
#include "clock.hpp"
#include "memory.hpp"
#include "io.hpp"
#include "graphics/screen.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
#include <syg/signal.hpp>
namespace AMeteor
{
class Lcd
{
public :
Lcd ();
void Reset ();
const uint16_t* GetSurface () const
{
return m_screen.GetSurface();
}
Graphics::Screen& GetScreen()
{
return m_screen;
}
const Graphics::Screen& GetScreen() const
{
return m_screen;
}
void SetFrameskip (uint8_t skip)
{
m_screen.SetFrameskip(skip);
}
void UpdateDispCnt (uint16_t dispcnt)
{
m_screen.UpdateDispCnt(dispcnt);
}
#define UPDATE_BG_CNT(num) \
void UpdateBg##num##Cnt (uint16_t cnt) \
{ \
m_screen.UpdateBg##num##Cnt(cnt); \
}
UPDATE_BG_CNT(0)
UPDATE_BG_CNT(1)
UPDATE_BG_CNT(2)
UPDATE_BG_CNT(3)
#undef UPDATE_BG_CNT
#define UPDATE_BG_OFF(num, coord) \
void UpdateBg##num##coord##Off (uint16_t cnt) \
{ \
m_screen.UpdateBg##num##coord##Off(cnt); \
}
UPDATE_BG_OFF(0, X)
UPDATE_BG_OFF(0, Y)
UPDATE_BG_OFF(1, X)
UPDATE_BG_OFF(1, Y)
UPDATE_BG_OFF(2, X)
UPDATE_BG_OFF(2, Y)
UPDATE_BG_OFF(3, X)
UPDATE_BG_OFF(3, Y)
#undef UPDATE_BG_OFF
#define UPDATE_BG_REF(num, coord) \
void UpdateBg##num##Ref##coord (int32_t cnt) \
{ \
m_screen.UpdateBg##num##Ref##coord (cnt); \
}
UPDATE_BG_REF(2, X)
UPDATE_BG_REF(2, Y)
UPDATE_BG_REF(3, X)
UPDATE_BG_REF(3, Y)
#undef UPDATE_BG_REF
void OamWrite (uint32_t begin, uint32_t end)
{
m_screen.OamWrite(begin, end);
}
void OamWrite16 (uint32_t add)
{
m_screen.OamWrite16(add);
}
void OamWrite32 (uint32_t add)
{
m_screen.OamWrite32(add);
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
//syg::signal<void> sig_vblank;
private :
Graphics::Screen m_screen;
void TimeEvent ();
friend void Clock::Commit();
};
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __MEMORY_H__
#define __MEMORY_H__
#include "cartmem.hpp"
//XXX
#include "eeprom.hpp"
#include <stdint.h>
#include <string>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Memory
{
public :
// in cycles
static const uint32_t CART_SAVE_TIME = 16*1024*1024; // 1 second
enum CartType
{
CTYPE_UNKNOWN,
CTYPE_EEPROM512,
CTYPE_EEPROM8192,
CTYPE_FLASH64,
CTYPE_FLASH128,
CTYPE_SRAM
};
enum CartError
{
CERR_NO_ERROR,
CERR_NOT_FOUND,
CERR_FAIL
};
Memory ();
~Memory ();
uint8_t GetCartType () const
{
return m_carttype;
}
// erases cartridge memory
void SetCartTypeFromSize (uint32_t size);
void SetCartType (uint8_t type);
void SetCartFile (const char* filename)
{
m_cartfile = filename;
}
void Reset (uint32_t params = ~0);
void ClearWbram ();
void ClearWcram ();
void ClearPalette ();
void ClearVram ();
void ClearOam ();
void SoftReset ();
bool LoadBios (const char* filename);
void LoadBios (const uint8_t* data, uint32_t size);
void UnloadBios ()
{
if (m_brom)
{
delete [] m_brom;
m_brom = NULL;
}
}
bool LoadRom (const char* filename);
void LoadRom (const uint8_t* data, uint32_t size);
CartError LoadCart ();
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __SOUND_H__
#define __SOUND_H__
#include "audio/speaker.hpp"
#include "clock.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Sound
{
public :
Sound ();
void Reset ();
inline Audio::Speaker& GetSpeaker();
void UpdateCntH1 (uint8_t val);
inline void ResetSound1 ();
inline void ResetSound2 ();
inline void ResetSound4 ();
inline void ResetSound1Envelope ();
inline void ResetSound2Envelope ();
inline void ResetSound4Envelope ();
void TimerOverflow (uint8_t timernum);
inline void SendDigitalA (uint8_t* buffer);
inline void SendDigitalB (uint8_t* buffer);
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
Audio::Speaker m_speaker;
uint8_t m_fATimer, m_fBTimer;
inline void TimerOverflowA ();
inline void TimerOverflowB ();
void TimeEvent ()
{
m_speaker.SoundTick();
}
friend void Clock::Commit ();
};
inline Audio::Speaker& Sound::GetSpeaker()
{
return m_speaker;
}
inline void Sound::ResetSound1 ()
{
m_speaker.ResetSound1();
}
inline void Sound::ResetSound2 ()
{
m_speaker.ResetSound2();
}
inline void Sound::ResetSound4 ()
{
m_speaker.ResetSound4();
}
inline void Sound::ResetSound1Envelope ()
{
m_speaker.ResetSound1Envelope();
}
inline void Sound::ResetSound2Envelope ()
{
m_speaker.ResetSound2Envelope();
}
inline void Sound::ResetSound4Envelope ()
{
m_speaker.ResetSound4Envelope();
}
inline void Sound::SendDigitalA (uint8_t* buffer)
{
m_speaker.FillFifoA((int8_t*)buffer);
}
inline void Sound::SendDigitalB (uint8_t* buffer)
{
m_speaker.FillFifoB((int8_t*)buffer);
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __SRAM_H__
#define __SRAM_H__
#include "cartmem.hpp"
#include <stdint.h>
#include <fstream>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Sram : public CartMem
{
public :
Sram ();
void Reset ();
bool Load (std::istream& f);
bool Save (std::ostream& f);
uint8_t Read (uint16_t add)
{
return m_data[add % SIZE];
}
bool Write (uint16_t add, uint8_t val)
{
m_data[add % SIZE] = val;
return true;
}
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
static const uint16_t SIZE = 0x8000;
};
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __SWAP_H__
#define __SWAP_H__
namespace AMeteor
{
inline uint16_t Swap16(uint16_t val)
{
return (val << 8) | (val >> 8);
}
inline uint32_t Swap32(uint32_t val)
{
return (val << 24) |
((val & 0x0000FF00) << 8) |
((val & 0x00FF0000) >> 8) |
(val >> 24);
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __TIMER_H__
#define __TIMER_H__
#include "clock.hpp"
#include <stdint.h>
#include <istream>
#include <ostream>
namespace AMeteor
{
class Timer
{
public :
Timer (int8_t num, Timer* next) :
m_num(num),
m_reload(0),
m_count(0),
m_control(0),
m_next(next)
{
}
void Reset ();
void SetReload (uint16_t rel)
{
m_reload = rel;
}
void Reload ();
uint16_t GetCount () const;
bool SaveState (std::ostream& stream);
bool LoadState (std::istream& stream);
private :
union Control
{
Control(uint16_t v) :
w(v)
{ }
uint16_t w;
struct
{
unsigned int prescaler : 2;
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

View File

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

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor.hpp"
#include "debug.hpp"
#include "globals.hpp"
#include <cstring>
#include <sstream>
// TODO add version
#define SS_MAGIC_STRING ("AMeteor SaveState")
#define SS_MS_SIZE (sizeof(SS_MAGIC_STRING)-1)
namespace AMeteor
{
namespace
{
class AMeteor
{
public :
AMeteor ()
{
Audio::InitNoise();
}
} __ameteor;
}
// the clock must be initialized first since there are devices like
// lcd which needs to set the timer
Clock _clock;
Io _io;
// the interpreter (which is in the cpu) takes io addresses, thus the
// cpu must be initialized after io
Interpreter _cpu;
Memory _memory;
Dma _dma;
// the lcd must be initialized after the memory since it takes
// pointers from it
Lcd _lcd;
// the sound must be initialized after the io since it takes references
// from it
Sound _sound;
// the keypad needs to take the vblank event from lcd, so it must be
// initialized after lcd
// it must also be initialized after io since it takes the keyinput
// reference
Keypad _keypad;
Timer _timer3(3, NULL);
Timer _timer2(2, &_timer3);
Timer _timer1(1, &_timer2);
Timer _timer0(0, &_timer1);
void Reset (uint32_t units)
{
#define RESET(u, e) \
if (units & UNIT_##e) \
_##u.Reset();
RESET(clock, CLOCK);
RESET(io, IO);
RESET(cpu, CPU);
RESET(dma, DMA);
RESET(lcd, LCD);
RESET(sound, SOUND);
RESET(keypad, KEYPAD);
RESET(timer0, TIMER0);
RESET(timer1, TIMER1);
RESET(timer2, TIMER2);
RESET(timer3, TIMER3);
#undef RESET
if (units & UNIT_MEMORY)
_memory.Reset(units);
}
bool SaveState (const char* filename)
{
if (_cpu.IsRunning())
return false;
std::ostringstream ss;
if (!SaveState(ss))
return false;
std::ofstream file(filename);
if (!file)
return false;
std::string buf = ss.str();
if (!file.write(buf.c_str(), buf.length()))
return false;
file.close();
if (file.bad())
return false;
return true;
}
bool LoadState (const char* filename)
{
if (_cpu.IsRunning())
return false;
std::istringstream ss;
{
std::ifstream file(filename);
if (!file)
return false;
// 1Mo
std::vector<uint8_t> buf(0x100000);
if (file.read((char*)&buf[0], 0x100000).bad())
return false;
int nread = file.gcount();
file.close();
if (file.bad())
return false;
ss.str(std::string((char*)&buf[0], nread));
}
return LoadState(ss);
}
bool SaveState (std::ostream& stream)
{
if (_cpu.IsRunning())
return false;
SS_WRITE_DATA(SS_MAGIC_STRING, SS_MS_SIZE);
#define SAVE(dev) \
if (!dev.SaveState(stream)) \
return false
SAVE(_clock);
SAVE(_io);
SAVE(_cpu);
SAVE(_memory);
SAVE(_dma);
SAVE(_lcd);
SAVE(_sound);
//SAVE(_keypad);
SAVE(_timer0);
SAVE(_timer1);
SAVE(_timer2);
SAVE(_timer3);
#undef SAVE
return true;
}
bool LoadState (std::istream& stream)
{
if (_cpu.IsRunning())
return false;
{
char buf[SS_MS_SIZE];
SS_READ_DATA(buf, SS_MS_SIZE);
if (std::memcmp(buf, SS_MAGIC_STRING, SS_MS_SIZE))
return false;
}
#define LOAD(dev) \
if (!dev.LoadState(stream)) \
return false
LOAD(_clock);
LOAD(_io);
LOAD(_cpu);
LOAD(_memory);
LOAD(_dma);
LOAD(_lcd);
LOAD(_sound);
//LOAD(_keypad);
LOAD(_timer0);
LOAD(_timer1);
LOAD(_timer2);
LOAD(_timer3);
#undef LOAD
uint8_t xxx;
// if there is garbage at end of file
if (stream.read((char*)&xxx, 1))
return false;
return true;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/audio/dsound.hpp"
#include "../globals.hpp"
#include <cstring>
namespace AMeteor
{
namespace Audio
{
DSound::DSound () :
m_rpos(0),
m_wpos(0),
m_size(0)
{
std::memset(m_buffer, 0, sizeof(m_buffer));
}
void DSound::FillFifo (int8_t* buffer)
{
int8_t* pmbuf = m_buffer + m_wpos;
// we copy 16 bytes of data
for (int8_t* pbuf = buffer;
pbuf < buffer + BUFFER_SIZE/2 && m_size < 32; ++pbuf, ++pmbuf)
{
if (pmbuf >= m_buffer + BUFFER_SIZE)
pmbuf = m_buffer;
*pmbuf = *pbuf;
++m_size;
}
m_wpos = pmbuf - m_buffer;
}
void DSound::FillFifo (int8_t sample)
{
if (m_size == 32)
return;
if (m_wpos == BUFFER_SIZE)
m_wpos = 0;
m_buffer[m_wpos++] = sample;
++m_size;
}
bool DSound::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_rpos);
SS_WRITE_VAR(m_wpos);
SS_WRITE_VAR(m_size);
SS_WRITE_ARRAY(m_buffer);
return true;
}
bool DSound::LoadState (std::istream& stream)
{
SS_READ_VAR(m_rpos);
SS_READ_VAR(m_wpos);
SS_READ_VAR(m_size);
SS_READ_ARRAY(m_buffer);
return true;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/audio/sound1.hpp"
#include "../globals.hpp"
#include <cmath>
namespace AMeteor
{
namespace Audio
{
Sound1::Sound1 (uint16_t& cntl, uint16_t& cnth, uint16_t& cntx,
uint16_t freq) :
m_cntl(cntl),
m_cnth(cnth),
m_cntx(cntx),
m_on(false),
m_posP(0),
m_posS(0),
m_posE(0),
m_sample(0),
m_speriod(16*1024*1024/freq),
m_envelope(0),
m_length(0),
m_timed(false)
{
}
void Sound1::Reset ()
{
m_on = false;
m_timed = false;
m_length = 0;
m_envelope = 0;
m_posP = m_posE = m_posS = 0;
m_sample = 0;
}
void Sound1::ResetSound ()
{
m_on = true;
m_timed = (m_cntx & (0x1 << 14));
m_length = (64 - (m_cnth & 0x3F)) * ((16*1024*1024)/256);
m_envelope = m_cnth >> 12;
m_posE = m_posS = 0;
}
void Sound1::SoundTick ()
{
// remember here that the processors runs at 16MHz = 16,777,216 cycles/s
// and this function is called normally at 44,100 Hz
m_posP += m_speriod;
m_posS += m_speriod;
m_posE += m_speriod;
if (m_length > m_speriod)
m_length -= m_speriod;
else
{
if (m_timed)
m_on = false;
m_length = 0;
}
// sweep time in cycles
// maximum is 917,504, so we need a 32 bits int
uint32_t sweeptime = ((m_cntl >> 4) & 0x7) * ((16*1024*1024)/128);
// period in cycles
// period = 16M/freq
// freq = 128K/(2048 - (SOUND1CNT_X & 0x7FF))
// maximum is 262,144, so we need a 32 bits int
uint32_t period =
((16*1024*1024) / (128*1024)) * (2048 - (m_cntx & 0x7FF));
// frequency as contained in SOUND1CNT_X
uint16_t freq = m_cntx & 0x7FF;
// we rewind posP
m_posP %= period;
// the envelope now
// envelope step time in cycles
uint32_t steptime = ((m_cnth >> 8) & 0x7) * ((16*1024*1024)/64);
// the envelope can't do two steps between to calls of SoundTick
if (steptime && m_posE > steptime)
{
if (m_cnth & (0x1 << 11))
{
if (m_envelope < 15)
++m_envelope;
}
else
{
if (m_envelope > 0)
--m_envelope;
}
m_posE -= steptime;
}
// if the envelope is null or the sound is finished, no need to calculate
// anything
if (m_on && m_envelope)
{
// we set the sample according to the position in the current period
// and the wave duty cycle
switch ((m_cnth >> 6) & 0x3)
{
case 0: // 12.5%
m_sample = m_posP < period/8 ? 112 : -16;
break;
case 1: // 25%
m_sample = m_posP < period/4 ? 96 : -32;
break;
case 2: // 50%
m_sample = m_posP < period/2 ? 64 : -64;
break;
case 3: // 75%
m_sample = m_posP < (3*period)/4 ? 32 : -96;
break;
}
m_sample = (((int16_t)m_sample) * m_envelope)/15;
}
else
m_sample = 0;
// there can't have been more than one sweep between two call of
// SoundTick since SoundTick is called at least at a frequency of 4,000Hz
// (alsa can't output at a lower samplerate on my sound card) and sweeps
// happen at maximum at a frequency of 128Hz
// if the channel is on and sweep is enabled and it's time to sweep
if (m_on && sweeptime && m_posS > sweeptime)
{
// n = sweep shifts (in SOUND1CNT_L)
if (m_cntl & (0x1 << 3))
// F(t+1) = F(t) - F(t) / 2^n
freq = freq - freq / (1 << (m_cntl & 0x7));
// freq won't go under 1 since when freq = 2, freq - freq / 2 (the
// minimum sweep shift) = 1 and then freq - freq / 2 = 1
// because 1/2 = 0
else
{
// F(t+1) = F(t) + F(t) / 2^n
freq = freq + freq / (1 << (m_cntl & 0x7));
if (freq > 2047)
{
m_on = false;
freq = 2047;
}
}
// we update the frequency in the cntx register
m_cntx = (m_cntx & 0xF800) | freq;
// now we rewind posS
m_posS -= sweeptime;
}
}
bool Sound1::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_on);
SS_WRITE_VAR(m_posP);
SS_WRITE_VAR(m_posS);
SS_WRITE_VAR(m_posE);
SS_WRITE_VAR(m_sample);
SS_WRITE_VAR(m_envelope);
SS_WRITE_VAR(m_length);
SS_WRITE_VAR(m_timed);
return true;
}
bool Sound1::LoadState (std::istream& stream)
{
SS_READ_VAR(m_on);
SS_READ_VAR(m_posP);
SS_READ_VAR(m_posS);
SS_READ_VAR(m_posE);
SS_READ_VAR(m_sample);
SS_READ_VAR(m_envelope);
SS_READ_VAR(m_length);
SS_READ_VAR(m_timed);
return true;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/audio/sound2.hpp"
#include "../globals.hpp"
#include <cmath>
namespace AMeteor
{
namespace Audio
{
Sound2::Sound2 (uint16_t& cntl, uint16_t& cnth, uint16_t freq) :
m_cntl(cntl),
m_cnth(cnth),
m_on(false),
m_posP(0),
m_posE(0),
m_sample(0),
m_speriod(16*1024*1024/freq),
m_envelope(0),
m_length(0),
m_timed(false)
{
}
void Sound2::Reset ()
{
m_on = false;
m_timed = false;
m_length = 0;
m_envelope = 0;
m_posP = m_posE = 0;
m_sample = 0;
}
void Sound2::ResetSound ()
{
m_on = true;
m_timed = (m_cnth & (0x1 << 14));
m_length = (64 - (m_cntl & 0x3F)) * ((16*1024*1024)/256);
m_envelope = m_cntl >> 12;
m_posE = 0;
}
void Sound2::SoundTick ()
{
// refer at sound1 to know how sound2 works
m_posP += m_speriod;
m_posE += m_speriod;
if (m_length > m_speriod)
m_length -= m_speriod;
else
{
if (m_timed)
m_on = false;
m_length = 0;
}
uint32_t period =
((16*1024*1024) / (128*1024)) * (2048 - (m_cnth & 0x7FF));
m_posP %= period;
uint32_t steptime = ((m_cntl >> 8) & 0x7) * ((16*1024*1024)/64);
if (steptime && m_posE > steptime)
{
if (m_cntl & (0x1 << 11))
{
if (m_envelope < 15)
++m_envelope;
}
else
{
if (m_envelope > 0)
--m_envelope;
}
m_posE -= steptime;
}
if (m_on && m_envelope)
{
switch ((m_cntl >> 6) & 0x3)
{
case 0: // 12.5%
m_sample = m_posP < period/8 ? 112 : -16;
break;
case 1: // 25%
m_sample = m_posP < period/4 ? 96 : -32;
break;
case 2: // 50%
m_sample = m_posP < period/2 ? 64 : -64;
break;
case 3: // 75%
m_sample = m_posP < (3*period)/4 ? 32 : -96;
break;
}
m_sample = (((int16_t)m_sample) * m_envelope)/15;
}
else
m_sample = 0;
}
bool Sound2::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_on);
SS_WRITE_VAR(m_posP);
SS_WRITE_VAR(m_posE);
SS_WRITE_VAR(m_sample);
SS_WRITE_VAR(m_envelope);
SS_WRITE_VAR(m_length);
SS_WRITE_VAR(m_timed);
return true;
}
bool Sound2::LoadState (std::istream& stream)
{
SS_READ_VAR(m_on);
SS_READ_VAR(m_posP);
SS_READ_VAR(m_posE);
SS_READ_VAR(m_sample);
SS_READ_VAR(m_envelope);
SS_READ_VAR(m_length);
SS_READ_VAR(m_timed);
return true;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/audio/sound4.hpp"
#include "../globals.hpp"
namespace AMeteor
{
namespace Audio
{
static bool Noise7Stages[127];
static bool Noise15Stages[32767];
void InitNoise ()
{
unsigned short i = 0x7f;
bool* pNoise = Noise7Stages;
do
{
*pNoise++ = i & 1;
i = (i >> 1) | (((i & 1) << 6) ^ ((i & 2) << 5));
} while (i != 0x7f);
i = 0x7fff;
pNoise = Noise15Stages;
do
{
*pNoise++ = i & 1;
i = (i >> 1) | (((i & 1) << 14) ^ ((i & 2) << 13));
} while (i != 0x7fff);
}
Sound4::Sound4 (uint16_t& cntl, uint16_t& cnth, uint16_t freq) :
m_cntl(cntl),
m_cnth(cnth),
m_on(false),
m_posP(0),
m_posN(0),
m_posE(0),
m_sample(0),
m_speriod(16*1024*1024/freq),
m_envelope(0),
m_length(0),
m_timed(false),
m_div(4*8/2)
{
}
void Sound4::Reset ()
{
m_on = false;
m_timed = false;
m_length = 0;
m_envelope = 0;
m_posP = m_posE = m_posN = 0;
m_sample = 0;
m_div = 4*8/2;
}
void Sound4::ResetSound ()
{
m_on = true;
m_timed = (m_cnth & (0x1 << 14));
m_length = (64 - (m_cntl & 0x3F)) * ((16*1024*1024)/256);
m_envelope = m_cntl >> 12;
m_div = ((m_cnth & 0x7) ? 4*8*(m_cnth & 0x7) : 4*8/2);
m_posE = m_posP = 0;
}
void Sound4::SoundTick ()
{
// rest is the number of processor clock ticks that were not yet taken by
// the noise clock divider (if the total divider is 8 and we have 10
// ticks of the processor clock, only 8 were taken by the noise clock),
// the rest will be taken by the next call of SoundTick()
uint16_t rest = m_posP + m_speriod;
// advance is the number of noise ticks that have passed
uint16_t advance = m_posP + m_speriod;
// time of one sound tick in cycles
uint32_t tick = m_div;
// if shift is 111X in binary
if (((m_cnth >> 5) & 0x7) == 0x7)
// not used
// assume 13
tick *= 1 << 14;
else
tick *= (2 << ((m_cnth >> 4) & 0xF));
rest %= tick;
advance /= tick;
m_posP = rest;
m_posN += advance;
// we have this modulo on posN so that when you switch from 15 stages to
// 7 stages and then you switch back, you won't restart the 15 stages
// pattern from the beginning
// don't know if GBA handle this like that
m_posN %= 32768;
m_posE += m_speriod;
if (m_length > m_speriod)
m_length -= m_speriod;
else
{
if (m_timed)
m_on = false;
m_length = 0;
}
uint32_t steptime = ((m_cntl >> 8) & 0x7) * ((16*1024*1024)/64);
if (steptime && m_posE > steptime)
{
if (m_cnth & (0x1 << 11))
{
if (m_envelope < 15)
++m_envelope;
}
else
{
if (m_envelope > 0)
--m_envelope;
}
m_posE -= steptime;
}
if (m_on && m_envelope)
{
if (m_cnth & (0x1 << 3))
m_sample = Noise7Stages[m_posN % 128];
else
m_sample = Noise15Stages[m_posN];
m_sample = m_sample ? 127 : -127;
m_sample = (((int16_t)m_sample) * m_envelope)/15;
}
else
m_sample = 0;
}
bool Sound4::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_on);
SS_WRITE_VAR(m_posP);
SS_WRITE_VAR(m_posE);
SS_WRITE_VAR(m_posN);
SS_WRITE_VAR(m_sample);
SS_WRITE_VAR(m_envelope);
SS_WRITE_VAR(m_length);
SS_WRITE_VAR(m_timed);
SS_WRITE_VAR(m_div);
return true;
}
bool Sound4::LoadState (std::istream& stream)
{
SS_READ_VAR(m_on);
SS_READ_VAR(m_posP);
SS_READ_VAR(m_posE);
SS_READ_VAR(m_posN);
SS_READ_VAR(m_sample);
SS_READ_VAR(m_envelope);
SS_READ_VAR(m_length);
SS_READ_VAR(m_timed);
SS_READ_VAR(m_div);
return true;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/audio/speaker.hpp"
#include "../debug.hpp"
namespace AMeteor
{
namespace Audio
{
Speaker::Speaker (uint16_t& cnt1l, uint16_t& cnt1h, uint16_t& cnt1x,
uint16_t& cnt2l, uint16_t& cnt2h,
uint16_t& cnt4l, uint16_t& cnt4h,
uint16_t& cntl, uint16_t& cnth, uint16_t& cntx, uint16_t& bias) :
// XXX freq
m_sound1(cnt1l, cnt1h, cnt1x, 44100),
m_sound2(cnt2l, cnt2h, 44100),
m_sound4(cnt4l, cnt4h, 44100),
m_cntl(cntl),
m_cnth(cnth),
m_cntx(cntx),
m_bias(bias)
{
}
Speaker::~Speaker ()
{
}
void Speaker::Reset ()
{
m_sound1.Reset();
m_sound2.Reset();
m_sound4.Reset();
m_dsa.Reset();
m_dsb.Reset();
}
void Speaker::SoundTick ()
{
int16_t f[2];
// if master is enabled
if (m_cntx & (0x1 << 7))
{
m_sound1.SoundTick();
if (m_sound1.IsOn())
m_cntx |= 0x0001;
else
m_cntx &= 0xFFFE;
m_sound2.SoundTick();
if (m_sound2.IsOn())
m_cntx |= 0x0002;
else
m_cntx &= 0xFFFD;
m_sound4.SoundTick();
if (m_sound4.IsOn())
m_cntx |= 0x0008;
else
m_cntx &= 0xFFF7;
}
// left
f[0] = MixSample (m_cntl >> 4, m_cnth >> 9);
// right
f[1] = MixSample (m_cntl, m_cnth >> 8);
m_sig_frame(f);
}
int16_t Speaker::MixSample (uint16_t cntl, uint8_t cnth)
{
int16_t sample;
// if master is enabled
if (m_cntx & (0x1 << 7))
{
int8_t s1, s2, s4;
s1 = (cntl & (0x1 << 8)) ? m_sound1.GetSample() : 0;
s2 = (cntl & (0x1 << 9)) ? m_sound2.GetSample() : 0;
s4 = (cntl & (0x1 << 11)) ? m_sound4.GetSample() : 0;
int16_t dmg = s1 + s2 + s4;
dmg = (dmg * (cntl & 0x7)) / 7;
switch (m_cnth & 0x3)
{
case 0: // 25%
dmg /= 4;
break;
case 1: // 50%
dmg /= 2;
break;
case 2: // 100%
break;
case 3: // Prohibited
met_abort("Invalid SOUNDCNT_H sound # 1-4 volume");
break;
}
int16_t sA, sB;
sA = (cnth & (0x1 )) ? m_dsa.GetSample() : 0;
sB = (cnth & (0x1 << 4)) ? m_dsb.GetSample() : 0;
if (!(m_cnth & (0x1 << 2)))
sA /= 2;
if (!(m_cnth & (0x1 << 3)))
sB /= 2;
// TODO when finished put this all together on one line
sample = (sA + sB) * 4 + dmg;
}
else
sample = 0;
sample += m_bias & 0x3FF;
if (sample < 0)
sample = 0;
else if (sample >= 0x400)
sample = 0x3FF;
sample -= 0x200;
sample <<= 6;
return sample;
}
bool Speaker::SaveState (std::ostream& stream)
{
#define WRITE(var) \
if (!var.SaveState(stream)) \
return false
WRITE(m_sound1);
WRITE(m_sound2);
WRITE(m_sound4);
WRITE(m_dsa);
WRITE(m_dsb);
#undef WRITE
return true;
}
bool Speaker::LoadState (std::istream& stream)
{
#define READ(var) \
if (!var.LoadState(stream)) \
return false
READ(m_sound1);
READ(m_sound2);
READ(m_sound4);
READ(m_dsa);
READ(m_dsb);
#undef READ
return true;
}
}
}

919
libmeteor/source/bios.cpp Normal file
View File

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

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/cartmem.hpp"
#include "globals.hpp"
namespace AMeteor
{
#ifdef __LIBRETRO__
uint8_t CartMemData[CartMem::MAX_SIZE+4];
#endif
CartMem::CartMem() :
#ifdef __LIBRETRO__
m_data(CartMemData)
#else
m_data(new uint8_t[MAX_SIZE+4])
#endif
{
}
CartMem::~CartMem()
{
#ifndef __LIBRETRO__
delete [] m_data;
#endif
}
bool CartMem::SaveState (std::ostream& stream)
{
stream.write((char*)m_data, MAX_SIZE);
SS_WRITE_VAR(m_size);
return true;
}
bool CartMem::LoadState (std::istream& stream)
{
stream.read((char*)m_data, MAX_SIZE);
SS_READ_VAR(m_size);
return true;
}
}

123
libmeteor/source/clock.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/clock.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
namespace AMeteor
{
void Clock::Reset ()
{
// lcd is enabled by default
m_first = m_count = m_cycles = m_lcd = m_sound = 0;
// timers and battery are disabled by default
m_battery = m_timer[0] = m_timer[1] = m_timer[2] = m_timer[3] =
INT_MAX;
}
void Clock::Commit ()
{
unsigned short tocommit;
m_count += m_cycles;
// this loop is here because a timer can trigger a dma which will take a
// long time, during this time the lcd must draw and the timers continue
while (m_cycles >= m_first)
{
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;
}
}

328
libmeteor/source/cpu.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/cpu.hpp"
#include "ameteor/bios.hpp"
#include "globals.hpp"
#include "cpu_globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
#include <cstring>
namespace AMeteor
{
Cpu::Cpu ()
{
Reset();
}
void Cpu::Reset ()
{
std::memset(&m_st, 0, sizeof(m_st));
R(15) = 0x00000004;
m_st.icpsr.mode = M_SVC;
m_st.icpsr.fiq_d = true;
m_st.icpsr.irq_d = true;
}
void Cpu::SoftReset ()
{
std::memset(&m_st, 0, sizeof(m_st));
R(13) = 0x03007F00;
R(15) = 0x08000004;
m_st.irq_r[0] = 0x03007FA0; // R13
m_st.svc_r[0] = 0x03007FE0; // R13
m_st.icpsr.mode = 0x1F;
m_st.icpsr.fiq_d = true;
}
void Cpu::UpdateICpsr ()
{
m_st.icpsr.mode = m_st.cpsr.b.mode;
m_st.icpsr.irq_d = m_st.cpsr.b.irq_d;
m_st.icpsr.fiq_d = m_st.cpsr.b.fiq_d;
m_st.icpsr.thumb = m_st.cpsr.b.thumb;
m_st.icpsr.s_overflow = m_st.cpsr.b.s_overflow;
m_st.icpsr.f_overflow = m_st.cpsr.b.f_overflow;
m_st.icpsr.f_carry = m_st.cpsr.b.f_carry;
m_st.icpsr.f_zero = m_st.cpsr.b.f_zero;
m_st.icpsr.f_sign = m_st.cpsr.b.f_sign;
}
void Cpu::UpdateCpsr ()
{
m_st.cpsr.b.mode = m_st.icpsr.mode;
m_st.cpsr.b.irq_d = m_st.icpsr.irq_d;
m_st.cpsr.b.fiq_d = m_st.icpsr.fiq_d;
m_st.cpsr.b.thumb = m_st.icpsr.thumb;
m_st.cpsr.b.s_overflow = m_st.icpsr.s_overflow;
m_st.cpsr.b.f_overflow = m_st.icpsr.f_overflow;
m_st.cpsr.b.f_carry = m_st.icpsr.f_carry;
m_st.cpsr.b.f_zero = m_st.icpsr.f_zero;
m_st.cpsr.b.f_sign = m_st.icpsr.f_sign;
}
void Cpu::SwitchToMode (uint8_t newmode)
{
SaveMode(m_st.icpsr.mode);
switch (newmode)
{
case 0x10: // User (non-privileged)
case 0x1F: // System (privileged 'User' mode)
switch (m_st.icpsr.mode)
{
case 0x10:
case 0x1F:
case 0x11:
std::memcpy(m_st.r + 8, m_st.usr_r, sizeof(m_st.usr_r));
break;
default:
std::memcpy(m_st.r + 13, m_st.usr_r + 5, 2*4);
break;
}
break;
case 0x11: // FIQ
std::memcpy(m_st.r + 8, m_st.fiq_r, sizeof(m_st.fiq_r));
break;
case 0x12: // IRQ
std::memcpy(m_st.r + 13, m_st.irq_r, sizeof(m_st.irq_r));
break;
case 0x13: // Supervisor (SWI)
std::memcpy(m_st.r + 13, m_st.svc_r, sizeof(m_st.svc_r));
break;
case 0x17: // Abort
std::memcpy(m_st.r + 13, m_st.abt_r, sizeof(m_st.abt_r));
break;
case 0x1B: // Undefined
std::memcpy(m_st.r + 13, m_st.und_r, sizeof(m_st.und_r));
break;
default:
met_abort("Unknown CPU mode : " << IOS_ADD << (int)newmode);
break;
}
UpdateCpsr();
m_st.spsr.dw = m_st.cpsr.dw;
m_st.icpsr.mode = newmode;
}
void Cpu::SwitchModeBack ()
{
// oldmode is the mode on which we want to switch back
uint8_t oldmode = m_st.spsr.b.mode, curmode = m_st.icpsr.mode;
// we don't care if the spsr of the mode we are using is modified
SaveMode(curmode);
m_st.cpsr.dw = m_st.spsr.dw;
UpdateICpsr();
CheckInterrupt();
switch (oldmode)
{
case 0x10: // User (non-privileged)
case 0x1F: // System (privileged 'User' mode)
switch (curmode)
{
case 0x10:
case 0x1F:
case 0x11:
std::memcpy(m_st.r + 8, m_st.usr_r, sizeof(m_st.usr_r));
break;
default:
std::memcpy(m_st.r + 13, m_st.usr_r + 5, 2*4);
break;
}
break;
case 0x11: // FIQ
std::memcpy(m_st.r + 8, m_st.fiq_r, sizeof(m_st.fiq_r));
m_st.spsr.dw = m_st.fiq_spsr.dw;
break;
case 0x12: // IRQ
std::memcpy(m_st.r + 13, m_st.irq_r, sizeof(m_st.irq_r));
m_st.spsr.dw = m_st.irq_spsr.dw;
break;
case 0x13: // Supervisor (SWI)
std::memcpy(m_st.r + 13, m_st.svc_r, sizeof(m_st.svc_r));
m_st.spsr.dw = m_st.svc_spsr.dw;
break;
case 0x17: // Abort
std::memcpy(m_st.r + 13, m_st.abt_r, sizeof(m_st.abt_r));
m_st.spsr.dw = m_st.abt_spsr.dw;
break;
case 0x1B: // Undefined
std::memcpy(m_st.r + 13, m_st.und_r, sizeof(m_st.und_r));
m_st.spsr.dw = m_st.und_spsr.dw;
break;
default:
met_abort("Unknown CPU mode : " << IOS_ADD << (int)oldmode);
break;
}
}
void Cpu::SaveMode (uint8_t mode)
{
switch (mode)
{
case 0x10: // User (non-privileged)
case 0x1F: // System (privileged 'User' mode)
std::memcpy(m_st.usr_r, m_st.r + 8, sizeof(m_st.usr_r));
break;
case 0x11: // FIQ
std::memcpy(m_st.fiq_r, m_st.r + 8, sizeof(m_st.fiq_r));
m_st.fiq_spsr.dw = m_st.spsr.dw;
break;
case 0x12: // IRQ
std::memcpy(m_st.irq_r, m_st.r + 13, sizeof(m_st.irq_r));
m_st.irq_spsr.dw = m_st.spsr.dw;
break;
case 0x13: // Supervisor (SWI)
std::memcpy(m_st.svc_r, m_st.r + 13, sizeof(m_st.svc_r));
m_st.svc_spsr.dw = m_st.spsr.dw;
break;
case 0x17: // Abort
std::memcpy(m_st.abt_r, m_st.r + 13, sizeof(m_st.abt_r));
m_st.abt_spsr.dw = m_st.spsr.dw;
break;
case 0x1B: // Undefined
std::memcpy(m_st.und_r, m_st.r + 13, sizeof(m_st.und_r));
m_st.und_spsr.dw = m_st.spsr.dw;
break;
default:
met_abort("Unknown CPU mode : " << IOS_ADD << (int)mode);
break;
}
}
void Cpu::Interrupt ()
{
// Switch mode
SwitchToMode(0x12); // IRQ
// Save PC
R(14) = R(15);
// FIXME : why ? this seems to be USELESS ! (look at bios irq end)
if (m_st.icpsr.thumb)
R(14) += 2;
// Switch to ARM
m_st.icpsr.thumb = false;
// Disable IRQ
m_st.icpsr.irq_d = true;
SetInterrupt(false);
// Branch on 0x18
R(15) = 0x1C;
}
void Cpu::SoftwareInterrupt ()
{
// Switch mode
SwitchToMode(0x13); // Supervisor
// Save PC
R(14) = R(15) - (m_st.icpsr.thumb ? 2 : 4);
// Switch to ARM
m_st.icpsr.thumb = false;
// Disable IRQ
m_st.icpsr.irq_d = true;
SetInterrupt(false);
// Branch on 0x8
R(15) = 0xC;
}
// TODO put this in Bios, no ?
void Cpu::SoftwareInterrupt (uint32_t comment)
{
if (MEM.HasBios())
SoftwareInterrupt();
else
switch (comment)
{
case 0x00:
Bios::SoftReset();
break;
case 0x01:
Bios::RegisterRamReset();
break;
case 0x02:
Bios::Halt();
break;
case 0x04:
case 0x05:
SoftwareInterrupt();
break;
case 0x06:
Bios::Div();
break;
case 0x07:
Bios::DivArm();
break;
case 0x08:
Bios::Sqrt();
break;
case 0x09:
Bios::ArcTan();
break;
case 0x0A:
Bios::ArcTan2();
break;
case 0x0B:
Bios::CpuSet();
break;
case 0x0C:
Bios::CpuFastSet();
break;
case 0x0E:
Bios::BgAffineSet();
break;
case 0x0F:
Bios::ObjAffineSet();
break;
case 0x11:
Bios::LZ77UnCompWram();
break;
case 0x12:
Bios::LZ77UnCompVram();
break;
case 0x13:
Bios::HuffUnComp();
break;
case 0x14:
Bios::RLUnCompWram();
break;
case 0x15:
Bios::RLUnCompVram();
break;
default:
met_abort("Unknown software interrupt : " << IOS_ADD << comment);
break;
}
}
bool Cpu::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_st);
return true;
}
bool Cpu::LoadState (std::istream& stream)
{
SS_READ_VAR(m_st);
CheckInterrupt();
return true;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __CPU_GLOBALS_H__
#define __CPU_GLOBALS_H__
#undef R
#define R(reg) m_st.r[reg]
#undef CPU
#define CPU (*this)
#undef CPSR
#undef SPSR
#undef FLAG_Z
#undef FLAG_N
#undef FLAG_C
#undef FLAG_V
#undef FLAG_T
#define CPSR (m_st.cpsr.dw)
#define SPSR (m_st.spsr.dw)
#define FLAG_Z (m_st.icpsr.f_zero)
#define FLAG_N (m_st.icpsr.f_sign)
#define FLAG_C (m_st.icpsr.f_carry)
#define FLAG_V (m_st.icpsr.f_overflow)
#define FLAG_T (m_st.icpsr.thumb)
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "debug.hpp"
#include "ameteor/cpu.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include <sstream>
#include <map>
namespace AMeteor
{
// TODO make this more guidelined (like the above assert)
void debug_bits(uint32_t u)
{
#if defined METDEBUG && defined METDEBUGLOG
for (register int8_t c = 31; c >= 0; --c)
{
STDBG << !!(u & (((uint32_t)0x1) << c));
if (!(c % 8))
STDBG << ' ';
}
STDBG << std::endl;
#else
(void)u;
#endif
}
void debug_bits_16(uint16_t u)
{
#if defined METDEBUG && defined METDEBUGLOG
for (register int8_t c = 15; c >= 0; --c)
{
STDBG << !!(u & (((uint32_t)0x1) << c));
if (!(c % 8))
STDBG << ' ';
}
STDBG << std::endl;
#else
(void)u;
#endif
}
#ifdef MET_REGS_DEBUG
void PrintRegs ()
{
static uint32_t regs[17] = {0};
for (uint8_t c = 0; c <= 15; ++c)
if (R(c) != regs[c])
{
STDBG << "R" << std::setbase(10) << (int)c << " = " << IOS_ADD << R(c) << '\n';
regs[c] = R(c);
}
CPU.UpdateCpsr();
if (CPSR != regs[16])
{
STDBG << "R16 = " << IOS_ADD << CPSR << '\n';
regs[16] = CPSR;
}
}
void PrintStack (uint32_t stackadd)
{
uint32_t add = stackadd;
debug("Stack : " << IOS_ADD << add);
for (; add < 0x03008000; add += 4)
debug(IOS_ADD << MEM.Read32(add));
}
#endif
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
// for abort macro
#include "ameteor.hpp"
#if 0
#define met_abort(str) \
{ \
std::cerr << IOS_NOR << "Fatal error :\n" << str << "\nFile : " \
<< __FILE__ << "\nLine : " << __LINE__ << "\nr15 = " \
<< IOS_ADD << ::AMeteor::_cpu.Reg(15) << "\n[r15] = " << IOS_ADD \
<< ::AMeteor::_memory.Read32(::AMeteor::_cpu.Reg(15)) \
<< "\nFlag T : " << ::AMeteor::_cpu.ICpsr().thumb << std::endl; \
abort(); \
}
#endif
#ifdef METDEBUG
#include <sstream>
extern "C" int __stdcall MessageBoxA(int, const char *, const char *, int);
#define met_abort(_str) if(true)\
{ \
std::stringstream _zisrny; \
_zisrny << IOS_NOR << "Fatal error :\n" << _str << "\nFile : " \
<< __FILE__ << "\nLine : " << __LINE__ << "\nr15 = " \
<< IOS_ADD << ::AMeteor::_cpu.Reg(15) << "\n[r15] = " << IOS_ADD \
<< ::AMeteor::_memory.Read32(::AMeteor::_cpu.Reg(15)) \
<< "\nFlag T : " << ::AMeteor::_cpu.ICpsr().thumb << std::endl; \
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

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argimmediate.hpp"
#include <sstream>
#include <iomanip>
namespace AMeteor
{
namespace Disassembler
{
std::string ArgImmediate::GetString () const
{
std::ostringstream ss;
ss << '#';
ss << std::hex << std::setw(2) << std::setfill('0') << m_imm << 'h';
return ss.str();
}
Argument* ArgImmediate::Clone () const
{
return new ArgImmediate(*this);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argmulregisters.hpp"
#include <sstream>
namespace AMeteor
{
namespace Disassembler
{
std::string ArgMulRegisters::GetString () const
{
std::ostringstream ss;
ss << '{';
bool open = false;
for (Registers::const_iterator iter = m_regs.begin();
iter != m_regs.end(); ++iter)
{
if (open &&
iter + 1 < m_regs.end() &&
*(iter + 1) == (*iter) + 1)
{
continue;
}
else if (iter + 2 < m_regs.end() &&
*(iter + 1) == (*iter) + 1 &&
*(iter + 2) == (*iter) + 2)
{
ss << 'r' << (int)*iter << '-';
open = true;
}
else
{
ss << 'r' << (int)*iter;
open = false;
if (iter + 1 != m_regs.end())
ss << ", ";
}
}
if (m_lastreg == SPREG_LR)
{
if (ss.width() != 1)
ss << ", ";
ss << "lr";
}
else if (m_lastreg == SPREG_PC)
{
if (ss.width() != 1)
ss << ", ";
ss << "pc";
}
ss << '}';
if (m_forceuser)
ss << '^';
return ss.str();
}
Argument* ArgMulRegisters::Clone () const
{
return new ArgMulRegisters(*this);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argpsr.hpp"
namespace AMeteor
{
namespace Disassembler
{
std::string ArgPsr::GetString () const
{
std::string out;
if (m_spsr)
out = "SPSR";
else
out = "CPSR";
if (m_fields <= 0xF)
{
out += '_';
if (m_fields & 0x1)
out += 'c';
if (m_fields & 0x2)
out += 'x';
if (m_fields & 0x4)
out += 's';
if (m_fields & 0x8)
out += 'f';
}
return out;
}
Argument* ArgPsr::Clone () const
{
return new ArgPsr(*this);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argregister.hpp"
#include <sstream>
namespace AMeteor
{
namespace Disassembler
{
std::string ArgRegister::GetString () const
{
static const char* SpeRegisters[] = {"SP", "LR", "PC"};
std::ostringstream ss;
if (m_memory)
ss << '[';
if (m_special)
ss << SpeRegisters[m_reg-13];
else
ss << 'r' << (int)m_reg;
if (m_memory)
ss << ']';
if (m_writeback)
ss << '!';
return ss.str();
}
Argument* ArgRegister::Clone () const
{
return new ArgRegister(*this);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argrelative.hpp"
#include <sstream>
namespace AMeteor
{
namespace Disassembler
{
ArgRelative::ArgRelative (const ArgRegister& reg, const Argument& off,
bool pre, bool up, bool writeback) :
Argument(),
m_reg(reg),
m_off(off.Clone()),
m_pre(pre),
m_up(up),
m_writeback(writeback)
{ }
ArgRelative::ArgRelative (const ArgRelative& arg) :
Argument(),
m_reg(arg.m_reg),
m_off(arg.m_off->Clone()),
m_pre(arg.m_pre),
m_up(arg.m_up),
m_writeback(arg.m_writeback)
{ }
ArgRelative::~ArgRelative ()
{
delete m_off;
}
Argument* ArgRelative::Clone () const
{
return new ArgRelative(*this);
}
std::string ArgRelative::GetString () const
{
std::ostringstream ss;
ss << "[r" << (int)m_reg.GetRegister();
if (m_pre)
{
ss << (m_up ? ", +" : ", -") << m_off->GetString() << ']';
if (m_writeback)
ss << '!';
}
else
ss << (m_up ? "], +" : "], -") << m_off->GetString();
return ss.str();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/argshift.hpp"
#include <sstream>
namespace AMeteor
{
namespace Disassembler
{
ArgShift::ArgShift (const Argument& arg1, const Argument& arg2,
ShiftType type, bool memory) :
Argument(),
m_arg1(arg1.Clone()),
m_arg2(arg2.Clone()),
m_type(type),
m_memory(memory)
{ }
ArgShift::ArgShift (const ArgShift& arg) :
Argument(),
m_arg1(arg.m_arg1->Clone()),
m_arg2(arg.m_arg2->Clone()),
m_type(arg.m_type),
m_memory(arg.m_memory)
{ }
ArgShift::~ArgShift ()
{
delete m_arg1;
delete m_arg2;
}
Argument* ArgShift::Clone () const
{
return new ArgShift(*this);
}
std::string ArgShift::GetString () const
{
static const char* Shifts[] = {", LSL ", ", LSR ", ", ASR ", ", ROR ",
", RRX "};
std::ostringstream ss;
if (m_memory)
ss << '[';
ss << m_arg1->GetString() << Shifts[m_type] << m_arg2->GetString();
if (m_memory)
ss << ']';
return ss.str();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/arguimmediate.hpp"
#include <sstream>
#include <iomanip>
namespace AMeteor
{
namespace Disassembler
{
std::string ArgUImmediate::GetString () const
{
std::ostringstream ss;
ss << '#';
ss << std::hex << std::setw(8) << std::setfill('0') << m_imm << 'h';
return ss.str();
}
Argument* ArgUImmediate::Clone () const
{
return new ArgUImmediate(*this);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/disassembler/arguments.hpp"
namespace AMeteor
{
namespace Disassembler
{
Arguments::~Arguments ()
{
Clear();
}
void Arguments::Clear ()
{
for (std::vector<Argument*>::iterator iter = m_args.begin();
iter != m_args.end(); ++iter)
{
delete *iter;
}
m_args.clear();
}
std::string Arguments::GetString () const
{
std::string out;
for (std::vector<Argument*>::const_iterator iter = m_args.begin();
iter != m_args.end(); ++iter)
{
if (!out.empty())
out += ", ";
out += (*iter)->GetString();
}
return out;
}
}
}

View File

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

307
libmeteor/source/dma.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/dma.hpp"
#include "ameteor/io.hpp"
#include "ameteor/memory.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
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;
}
}

272
libmeteor/source/eeprom.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/eeprom.hpp"
#include "globals.hpp"
#include "debug.hpp"
#include <cstring>
namespace AMeteor
{
Eeprom::Eeprom (bool big) :
CartMem(),
m_state(IDLE),
m_add(0),
m_pos(0)
{
if (big)
m_size = 0x2000;
else
m_size = 0x0200;
*(uint32_t*)(m_data+MAX_SIZE) = m_size;
}
void Eeprom::Reset ()
{
std::memset(m_data, 0, m_size);
}
bool Eeprom::Load (std::istream& f)
{
f.read((char*)m_data, m_size);
return f.good();
}
bool Eeprom::Save (std::ostream& f)
{
f.write((char*)m_data, m_size);
return f.good();
}
uint8_t Eeprom::Read (uint16_t MET_UNUSED(add))
{
met_abort("8 bits write to EEPROM");
return 0;
}
uint16_t Eeprom::Read ()
{
switch (m_state)
{
case READ_GARBAGE:
++m_pos;
if (m_pos == 4)
{
m_pos = 0;
m_state = READ_DATA;
}
return 0;
case READ_DATA:
{
uint16_t ret =
(m_data[m_add + m_pos / 8] & (0x1 << (7 - (m_pos % 8)))) ? 1 : 0;
++m_pos;
if (m_pos == 64)
m_state = IDLE;
return ret;
}
default:
return 1;
}
}
bool Eeprom::Write (uint16_t MET_UNUSED(add), uint8_t MET_UNUSED(val))
{
met_abort("8 bits write to EEPROM");
return false;
}
//XXX
#if 0
bool Eeprom::Write (uint16_t val)
{
switch (m_state)
{
case IDLE:
if (val & 0x1)
m_state = WAITING;
else
met_abort("First bit is not 1");
return false;
case WAITING:
m_add = 0;
m_pos = 0;
if (val & 0x1)
m_state = READ_ADD;
else
m_state = WRITE_ADD;
return false;
case READ_ADD:
m_add <<= 1;
m_add |= val & 0x1;
++m_pos;
if (m_size == 0x0200 && m_pos == 6 ||
m_size == 0x2000 && m_pos == 14)
{
m_state = READ_END;
if (m_size == 0x2000 && (m_add & 0x3C000))
met_abort("In large EEPROM, 4 upper address bits are not 0");
}
return false;
case READ_END:
if (val & 0x1)
met_abort("Last bit of EEPROM read request is not 0");
m_pos = 0;
m_state = READ_GARBAGE;
return false;
case WRITE_ADD:
m_add <<= 1;
m_add |= val & 0x1;
++m_pos;
if (m_size == 0x0200 && m_pos == 6 ||
m_size == 0x2000 && m_pos == 14)
{
m_state = WRITE_DATA;
m_pos = 0;
if (m_size == 0x2000 && (m_add & 0x3C000))
met_abort("In large EEPROM, 4 upper address bits are not 0");
}
return false;
case WRITE_DATA:
{
uint8_t& d = m_data[m_add + m_pos / 8];
d <<= 1;
d |= val & 0x1;
}
++m_pos;
if (m_pos == 64)
m_state = WRITE_END;
return true;
case WRITE_END:
if (val & 0x1)
met_abort("Last bit of EEPROM write request is not 0");
return false;
}
}
#endif
bool Eeprom::Write (uint16_t* pData, uint16_t size)
{
if (!(*pData & 0x1))
met_abort("Bit 1 is not 1 in EEPROM DMA");
++pData;
uint16_t add = 0;
if (*pData & 0x1) // read
{
if (size != 9 && size != 17)
met_abort("Invalid size for read");
++pData;
// read address
for (uint8_t i = 0, end = (m_size == 0x0200) ? 6 : 14; i < end;
++i, ++pData)
add = (add << 1) | (*pData & 0x1);
if (m_size == 0x2000 && (add & 0x3C00))
met_abort("In large EEPROM, 4 upper address bits are not 0");
//XXX
/*if (*pData & 0x1)
met_abort("Last bit of EEPROM read request is not 0");*/
m_add = add*8;
m_state = READ_GARBAGE;
m_pos = 0;
return false;
}
else // write
{
if (size != 73 && size != 81)
met_abort("Invalid size for write");
++pData;
// read address
for (uint8_t i = 0, end = (m_size == 0x0200) ? 6 : 14; i < end;
++i, ++pData)
add = (add << 1) | (*pData & 0x1);
if (m_size == 0x2000 && (add & 0x3C00))
met_abort("In large EEPROM, 4 upper address bits are not 0");
// read data
uint8_t* pMem = m_data + add*8;
for (uint8_t i = 0; i < 8; ++i, ++pMem)
{
for (uint8_t j = 0; j < 8; ++j, ++pData)
{
*pMem <<= 1;
*pMem |= (*pData & 0x1);
}
}
if (*pData & 0x1)
met_abort("Last bit of EEPROM write request is not 0");
m_state = IDLE;
return true;
}
}
//XXX
#if 0
void Eeprom::Read (uint16_t* pOut)
{
if (m_state != READ)
met_abort("Read in invalid EEPROM state");
pOut += 4; // ignore these
uint8_t* pData = m_data + m_add;
uint8_t cur;
for (uint8_t i = 0; i < 8; ++i, ++pData)
{
cur = *pData;
pOut += 7;
for (uint8_t j = 0; j < 8; ++j, --pOut, cur >>= 1)
*pOut = cur & 0x1;
pOut += 9;
}
m_state = NORMAL;
}
#endif
bool Eeprom::SaveState (std::ostream& stream)
{
//XXX TODO
SS_WRITE_VAR(m_size);
SS_WRITE_VAR(m_state);
SS_WRITE_VAR(m_add);
SS_WRITE_DATA(m_data, m_size);
return true;
}
bool Eeprom::LoadState (std::istream& stream)
{
SS_READ_VAR(m_size);
SS_READ_VAR(m_state);
SS_READ_VAR(m_add);
SS_READ_DATA(m_data, m_size);
return true;
}
}

178
libmeteor/source/flash.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/flash.hpp"
#include "globals.hpp"
#include <cstring>
#include <fstream>
namespace AMeteor
{
Flash::Flash (bool big) :
CartMem(),
m_state(NORMAL)
{
if (big)
{
m_device_id = 0x13;
m_manufacturer_id = 0x62;
m_size = 128*1024;
}
else
{
m_device_id = 0x1B;
m_manufacturer_id = 0x32;
m_size = 64*1024;
}
*(uint32_t*)(m_data+MAX_SIZE) = m_size;
}
void Flash::Reset ()
{
std::memset(m_data, 0, m_size);
}
bool Flash::Load (std::istream& f)
{
f.read((char*)m_data, m_size);
return f.good();
}
bool Flash::Save (std::ostream& f)
{
f.write((char*)m_data, m_size);
return f.good();
}
uint8_t Flash::Read (uint16_t add)
{
switch (m_state)
{
case NORMAL:
return m_data[add];
case ID:
switch (add)
{
case 0: return m_manufacturer_id;
case 1: return m_device_id;
default: return 0;
}
// FIXME vba returns 0xff after an erase regardless of the read address
default:
return 0;
}
}
bool Flash::Write (uint16_t add, uint8_t val)
{
switch (m_state)
{
case ID:
case NORMAL:
if (add == 0x5555 && val == 0xAA)
m_state = CMD1;
else
m_state = NORMAL; // for case ID
break;
case CMD1:
if (add == 0x2AAA && val == 0x55)
m_state = CMD2;
else
m_state = NORMAL;
break;
case CMD2:
if (add == 0x5555)
switch (val)
{
case 0x80: // erase mode
m_state = ERASE1;
break;
case 0x90: // id mode
m_state = ID;
break;
case 0xA0: // write byte
m_state = WRITE;
break;
case 0xF0:
m_state = NORMAL;
break;
default:
m_state = NORMAL;
break;
}
else
m_state = NORMAL;
break;
case ERASE1:
if (add == 0x5555 && val == 0xAA)
m_state = ERASE2;
else
m_state = NORMAL;
break;
case ERASE2:
if (add == 0x2AAA && val == 0x55)
m_state = ERASE3;
else
m_state = NORMAL;
break;
case ERASE3:
// according to vba, after a whole erase command we can juste repeat the
// last byte of the command to execute another erase command
switch (val)
{
case 0x10: // erase entire chip
if (add == 0x5555)
memset(m_data, 0xFF, m_size);
m_state = NORMAL;
break;
case 0x30: // erase sector
if (!(add & 0x0FFF))
memset(m_data+add, 0xFF, 0x1000);
m_state = NORMAL;
break;
default:
m_state = NORMAL;
break;
}
break;
case WRITE:
// I think this is how it works
m_data[add] &= val;
m_state = NORMAL;
return true;
}
return false;
}
bool Flash::SaveState (std::ostream& stream)
{
SS_WRITE_VAR(m_state);
SS_WRITE_DATA(m_data, m_size);
return true;
}
bool Flash::LoadState (std::istream& stream)
{
SS_READ_VAR(m_state);
SS_READ_DATA(m_data, m_size);
return true;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#ifndef __GLOBALS_H__
#define __GLOBALS_H__
#include <algorithm>
#include <stdint.h>
#define SPDUP_FRMSKIP 9
#define SPDUP_SNDSKIP 3969
#define R(reg) CPU.Reg(reg)
#define CPU _cpu
#define MEM _memory
#define IO _io
#define DMA _dma
#define LCD _lcd
#define SOUND _sound
#define KEYPAD _keypad
#define CLOCK _clock
#define TIMER0 _timer0
#define TIMER1 _timer1
#define TIMER2 _timer2
#define TIMER3 _timer3
#define CYCLES16NSeq(add, count) \
CLOCK.TimePass(MEM.GetCycles16NoSeq(add, count))
#define CYCLES16Seq(add, count) \
CLOCK.TimePass(MEM.GetCycles16Seq(add, count))
#define CYCLES32NSeq(add, count) \
CLOCK.TimePass(MEM.GetCycles32NoSeq(add, count))
#define CYCLES32Seq(add, count) \
CLOCK.TimePass(MEM.GetCycles32Seq(add, count))
#define ICYCLES(i) \
CLOCK.TimePass(i)
// we don't mind if the following code is slow since a MUL
// instruction is slow on a GBA too
#define MULICYCLES(reg) \
{ \
uint32_t rs = R(reg) >> 8; \
for (uint8_t i = 1; i <= 4; ++i, rs >>= 8) \
{ \
if (!rs || rs == (0xFFFFFFFF >> (i*8))) \
{ \
ICYCLES(i); \
break; \
} \
} \
}
#define CPSR (CPU.Cpsr().dw)
#define ICPSR (CPU.ICpsr())
#define SPSR (CPU.Spsr().dw)
#define FLAG_Z (CPU.ICpsr().f_zero)
#define FLAG_N (CPU.ICpsr().f_sign)
#define FLAG_C (CPU.ICpsr().f_carry)
#define FLAG_V (CPU.ICpsr().f_overflow)
#define FLAG_T (CPU.ICpsr().thumb)
#define SETF(flag, val) \
FLAG_##flag = (val) ? 1 : 0
#define SETFB(flag, val) \
FLAG_##flag = (val)
#define FZ(val) \
SETF(Z, !(val))
#define FN(val) \
SETF(N, NEG(val))
#define POS(a) ((~a) >> 31)
#define NEG(a) ((a) >> 31)
// inspired by VisualBoyAdvance
#define ADDCARRY(a, b, c) \
((NEG(a) & NEG(b)) | \
(NEG(a) & POS(c)) | \
(NEG(b) & POS(c)))
#define ADDOVERFLOW(a, b, c) \
((NEG(a) & NEG(b) & POS(c)) | \
(POS(a) & POS(b) & NEG(c)))
#define SUBCARRY(a, b, c) \
((NEG(a) & POS(b)) | \
(NEG(a) & POS(c)) | \
(POS(b) & POS(c)))
#define SUBOVERFLOW(a, b, c) \
((NEG(a) & POS(b) & POS(c)) | \
(POS(a) & NEG(b) & NEG(c)))
// Save states macros
#define SS_WRITE_VAR(var) \
if (!stream.write((char*)&var, sizeof(var))) \
return false
#define SS_WRITE_ARRAY(var) \
if (!stream.write((char*)var, sizeof(var))) \
return false
#define SS_WRITE_DATA(var, size) \
if (!stream.write((char*)var, size)) \
return false
#define SS_READ_VAR(var) \
if (!stream.read((char*)&var, sizeof(var))) \
return false
#define SS_READ_ARRAY(var) \
if (!stream.read((char*)var, sizeof(var))) \
return false
#define SS_READ_DATA(var, size) \
if (!stream.read((char*)var, size)) \
return false
// macro to avoid getting warnings about and unused parameter on GCC
#ifdef _MSC_VER
#define MET_UNUSED(v)
#else
#define MET_UNUSED(v) \
__attribute__((unused)) MET_UNUSED_##v
#endif
namespace AMeteor
{
// ROtate Right
inline uint32_t ROR(uint32_t val, uint8_t shift)
{
return (val >> shift) | (val << (32 - shift));
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/bglayer.hpp"
#include "ameteor/graphics/screen.hpp"
#include "../debug.hpp"
namespace AMeteor
{
namespace Graphics
{
BgLayer::BgLayer (int8_t num, Memory& memory, Io& io, uint16_t* pPalette) :
m_memory(memory),
m_io(io),
m_num(num),
m_priority(0),
m_cnt(0),
m_xoff(0),
m_yoff(0),
m_tWidth(32),
m_tHeight(32),
m_rWidth(16),
m_rHeight(16),
m_mapAdd(0x06000000),
m_charAdd(0x06000000),
m_pPalette(pPalette)
{
}
BgLayer::~BgLayer ()
{
}
void BgLayer::DrawLine0 (uint8_t line, uint16_t* ptr)
{
uint8_t mosH;
if (m_cnt & (0x1 << 6))
{
mosH = m_io.DRead8(Io::MOSAIC);
uint8_t mosV = mosH >> 4;
mosH &= 0x0F;
++mosV;
if (mosH) // let it be 0 if it's 0 (=> no mosaic)
++mosH;
line /= mosV;
line *= mosV;
}
else
mosH = 0;
uint16_t* pMap = (uint16_t*)m_memory.GetRealAddress(m_mapAdd), *pTile;
uint8_t* pChar = m_memory.GetRealAddress(m_charAdd), *tpChar;
uint8_t i = 0;
// theses are the coordinates on the layer from which we will draw a line
uint16_t xoff = m_xoff % (m_tWidth * 8);
uint16_t yoff = (m_yoff + line) % (m_tHeight * 8);
// theses are the coordinates on the tile of the pixels we are drawing
// NOTE : if the tile is horizontally flipped tileX = 8 - tileX, thus
// when we draw it is ALWAYS incremented no matter how the tile is
// flipped
// NOTE : tileY is NOT redefined if the tile is flipped vertically
int8_t tileX = xoff % 8, tileY;
bool flipH;
// sets pTile to the tile we must draw first
// +---+---+
// | 1 | 2 |
// +---+---+
// | 3 | 4 |
// +---+---+
if (yoff >= 256)
if (m_tWidth == 64)
if (xoff >= 256)
// zone 4
pTile = pMap + 32*32 + 32*32 + 32*32
+ 32 * ((yoff-256)/8) + (xoff-256)/8;
else
// zone 3 (large map)
pTile = pMap + 32*32 + 32*32 + 32 * ((yoff-256)/8) + xoff/8;
else
// zone 3 (no large map)
pTile = pMap + 32*32 + 32 * ((yoff-256)/8) + xoff/8;
else
if (xoff >= 256)
// zone 2
pTile = pMap + 32*32 + 32 * (yoff/8) + (xoff-256)/8;
else
// zone 1
pTile = pMap + 32 * (yoff/8) + xoff/8;
if (m_hicolor)
{
while (i < 240)
{
flipH = (bool)(*pTile & (0x1 << 10));
// if the tile is vertically fliped
if (*pTile & (0x1 << 11))
tileY = 7 - (yoff % 8);
else
tileY = yoff % 8;
if (flipH)
tpChar = pChar + (*pTile & 0x3FF)*8*8 + tileY*8 + 7 - tileX;
else
tpChar = pChar + (*pTile & 0x3FF)*8*8 + tileY*8 + tileX;
while (tileX < 8)
{
if (mosH && i % mosH)
*ptr = ptr[-1];
else
{
if (*tpChar)
*ptr = m_pPalette[*tpChar] | 0x8000;
else
*ptr = 0x0;
if (flipH)
--tpChar;
else
++tpChar;
}
++ptr;
++tileX;
++i;
if (i == 240)
return;
}
tileX = 0;
++pTile;
// support for big maps and wrap around
if (!((pTile - pMap) % 32))
if (m_tWidth == 32)
pTile -= 32;
else
if (yoff >= 256 && pTile - pMap > 32*32 * 3
|| yoff < 256 && pTile - pMap > 32*32)
pTile -= 32*33;
else
pTile += 32*31;
}
}
else
{
uint16_t* pPalette;
uint8_t colorInd;
// for each pixel of the line we are drawing
while (i < 240)
{
pPalette = m_pPalette + ((*pTile >> 12) & 0xF) * 16;
flipH = (bool)(*pTile & (0x1 << 10));
// if the tile is vertically fliped
if (*pTile & (0x1 << 11))
tileY = 7 - (yoff % 8);
else
tileY = yoff % 8;
if (flipH)
tpChar = pChar + ((*pTile & 0x3FF)*8*8 + tileY*8 + 7 - tileX)/2;
else
tpChar = pChar + ((*pTile & 0x3FF)*8*8 + tileY*8 + tileX)/2;
// we draw until the end of the tile or the line
while (tileX < 8)
{
if (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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/object.hpp"
#include "ameteor/graphics/screen.hpp"
#include "../debug.hpp"
namespace AMeteor
{
namespace Graphics
{
Object::Object (uint16_t* pPalette, uint8_t* pChar) :
m_attr0(0),
m_attr1(0),
m_attr2(0),
m_width(1),
m_height(1),
m_pPalette(pPalette),
m_pChar(pChar),
m_charBegin(0x06010000),
m_charEnd(0x06010020)
{
}
Object::Object (const Object& obj) :
m_attr0(obj.m_attr0),
m_attr1(obj.m_attr1),
m_attr2(obj.m_attr2),
m_width(obj.m_width),
m_height(obj.m_height),
m_pPalette(obj.m_pPalette),
m_pChar(obj.m_pChar),
m_charBegin(obj.m_charBegin),
m_charEnd(obj.m_charEnd)
{
}
void Object::DrawLine (uint8_t line, uint32_t* surface, bool oneDim,
uint8_t mosaic)
{
// if the object is disabled or it's an obj window
if (m_attr0 & (0x1 << 9) || ((m_attr0 >> 10) & 0x3) == 2)
return;
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];
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/objects.hpp"
#include "../debug.hpp"
#include <string.h>
namespace AMeteor
{
namespace Graphics
{
Objects::Objects (Memory& memory, Io& io, uint16_t* pPalette) :
m_io(io),
m_objs(128, Object(pPalette,
memory.GetRealAddress(0x06010000))),
m_pOam((uint16_t*)memory.GetRealAddress(0x07000000))
{
}
void Objects::DrawLine (uint8_t line, uint32_t* surface)
{
bool oneDim = m_io.DRead16(Io::DISPCNT) & (0x1 << 6);
uint8_t mosaic = m_io.DRead8(Io::MOSAIC+1);
int16_t rotSel;
for (Objs::iterator iter = m_objs.begin();
iter != m_objs.end(); ++iter)
{
rotSel = iter->GetRotationParam();
if (rotSel == -1)
iter->DrawLine (line, surface, oneDim, mosaic);
else
{
rotSel *= 16;
iter->DrawLineRot (line, surface, oneDim, m_pOam[rotSel + 3],
m_pOam[rotSel + 7], m_pOam[rotSel + 11], m_pOam[rotSel + 15],
mosaic);
}
}
}
void Objects::DrawLineHighOnly (uint8_t line, uint32_t* surface)
{
bool oneDim = m_io.DRead16(Io::DISPCNT) & (0x1 << 6);
uint8_t mosaic = m_io.DRead8(Io::MOSAIC+1);
int16_t rotSel;
for (Objs::iterator iter = m_objs.begin();
iter != m_objs.end(); ++iter)
if (iter->GetTileNum() >= 512)
{
rotSel = iter->GetRotationParam();
if (rotSel == -1)
iter->DrawLine (line, surface, oneDim, mosaic);
else
{
rotSel *= 16;
iter->DrawLineRot (line, surface, oneDim, m_pOam[rotSel + 3],
m_pOam[rotSel + 7], m_pOam[rotSel + 11], m_pOam[rotSel + 15],
mosaic);
}
}
}
void Objects::DrawWindow (uint8_t line, uint8_t* surface)
{
bool oneDim = m_io.DRead16(Io::DISPCNT) & (0x1 << 6);
int16_t rotSel;
uint8_t mask = (m_io.DRead16(Io::WINOUT) >> 8) & 0x3F;
for (Objs::iterator iter = m_objs.begin(); iter != m_objs.end(); ++iter)
if (iter->IsWindow())
{
rotSel = iter->GetRotationParam();
if (rotSel == -1)
iter->DrawWindow (line, surface, oneDim, mask);
else
{
rotSel *= 16;
iter->DrawWindowRot (line, surface, oneDim, m_pOam[rotSel + 3],
m_pOam[rotSel + 7], m_pOam[rotSel + 11], m_pOam[rotSel + 15],
mask);
}
}
}
void Objects::OamWrite (uint32_t begin, uint32_t end)
{
uint32_t objnum;
// FIXME is this possible ?
if (begin & 0x3)
met_abort("OamWrite not 4 byte aligned");
if (begin <= 0x07000000)
objnum = 0;
else
objnum = (begin - 0x07000000);
uint16_t* pOam = m_pOam + objnum/2;
objnum /= 8;
Objs::iterator iterStart;
iterStart = m_objs.begin() + objnum;
Objs::iterator iterEnd = m_objs.begin() + (end - 0x07000000 + 7)/8;
if (iterEnd > m_objs.end())
iterEnd = m_objs.end();
for (Objs::iterator iter = iterStart; iter != iterEnd; ++iter, ++objnum)
{
iter->UpdateAttrs(pOam[0], pOam[1], pOam[2]);
pOam += 4;
}
}
void Objects::OamWrite16 (uint32_t add)
{
uint16_t objnum = (add - 0x07000000) / 8;
uint16_t* pOam = m_pOam + objnum*4;
Objs::iterator iter = m_objs.begin() + objnum;
switch ((add - 0x07000000) % 8)
{
case 0:
iter->UpdateAttr0(pOam[0]);
break;
case 2:
iter->UpdateAttr1(pOam[1]);
break;
case 4:
iter->UpdateAttr2(pOam[2]);
break;
case 6:
break;
default :
met_abort("Oam access not 16 bits aligned");
break;
}
}
void Objects::OamWrite32 (uint32_t add)
{
add -= 0x07000000;
uint16_t objnum = add / 8;
uint16_t* pOam = m_pOam + objnum * 4;
Objs::iterator iter = m_objs.begin() + objnum;
switch (add % 8)
{
case 0:
iter->UpdateAttr0(pOam[0]);
iter->UpdateAttr1(pOam[1]);
break;
case 4:
iter->UpdateAttr2(pOam[2]);
break;
default :
met_abort("Oam access not 32 bits aligned");
break;
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/renderer.hpp"
#include "ameteor.hpp"
namespace AMeteor
{
namespace Graphics
{
Renderer::Renderer (const uint16_t* surface) :
m_base(surface)
{
}
void Renderer::VBlank ()
{
if (m_sig_frame)
m_sig_frame(m_base);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/graphics/screen.hpp"
#include "../globals.hpp"
#include "../debug.hpp"
#include "ameteor.hpp"
#include <cstring>
namespace AMeteor
{
namespace Graphics
{
BgLayer Screen::* const Screen::BgLayers [4] =
{ &Screen::m_bgLayer0, &Screen::m_bgLayer1,
&Screen::m_bgLayer2, &Screen::m_bgLayer3 };
// TODO there is no more need to pass theses references
Screen::Screen (Memory& memory, Io& io) :
m_io(io),
m_surface(new uint16_t[WIDTH*HEIGHT]),
m_renderer(m_surface),
m_frameskip(0),
m_curframe(0),
m_dispcnt(0),
m_refX2(0), m_refY2(0), m_refX3(0), m_refY3(0),
m_pPalette((uint16_t*)memory.GetRealAddress(0x05000000)),
m_bgLayer0(0, memory, io, m_pPalette),
m_bgLayer1(1, memory, io, m_pPalette),
m_bgLayer2(2, memory, io, m_pPalette),
m_bgLayer3(3, memory, io, m_pPalette),
m_objs(memory, io, m_pPalette + 256)
{
}
Screen::~Screen ()
{
delete [] m_surface;
}
void Screen::DrawLine (uint8_t line)
{
if (m_curframe < m_frameskip)
{
// we skip this frame
// VBlank
if (line == 159)
// we don't update screen since we haven't drawn anything on it, it
// would show up a buffer with the previous image or maybe only
// garbage (we use double buffering)
m_curframe = (m_curframe + 1) % FRMSKIP_TOTAL;
return;
}
uint16_t* lineBg = new uint16_t[4*WIDTH];
// layers may draw transparent pixels, so we need to clear them
memset(lineBg, 0, 4*WIDTH*sizeof(uint16_t));
uint32_t* lineObj = new uint32_t[WIDTH];
for (uint32_t* p = lineObj + WIDTH - 1; p >= lineObj; --p)
*p = 0x00030000;
uint8_t prio[4];
prio[0] = m_bgLayer0.GetPriority();
prio[1] = m_bgLayer1.GetPriority();
prio[2] = m_bgLayer2.GetPriority();
prio[3] = m_bgLayer3.GetPriority();
uint8_t layersOn = (m_dispcnt >> 8) & 0x1F;
switch (m_dispcnt & 0x7)
{
case 0: // all in mode 0
// if the bg is enabled draw it
if (layersOn & (0x1 )) m_bgLayer0.DrawLine0(line, lineBg);
if (layersOn & (0x1 << 1)) m_bgLayer1.DrawLine0(line, lineBg+WIDTH);
if (layersOn & (0x1 << 2)) m_bgLayer2.DrawLine0(line, lineBg+2*WIDTH);
if (layersOn & (0x1 << 3)) m_bgLayer3.DrawLine0(line, lineBg+3*WIDTH);
// if objects are enabled draw them
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
break;
case 1: // bg0 and bg1 in mode 0 and bg2 in mode 2, no bg3
// disable layer 3
layersOn &= 0xF7;
// if the bg is enabled draw it
if (layersOn & (0x1 )) m_bgLayer0.DrawLine0(line, lineBg);
if (layersOn & (0x1 << 1)) m_bgLayer1.DrawLine0(line, lineBg+WIDTH);
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine2(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC));
// if objects are enabled draw them
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
break;
case 2: // bg2 and bg3 in mode 2, no bg0 and bg1
// disable layers 0 and 1
layersOn &= 0xFC;
// if the bg is enabled draw it
if (layersOn & (0x1 << 2))
m_bgLayer2.DrawLine2(lineBg+2*WIDTH,
m_refX2, m_refY2,
m_io.DRead16(Io::BG2PA),
m_io.DRead16(Io::BG2PC));
if (layersOn & (0x1 << 3))
m_bgLayer3.DrawLine2(lineBg+3*WIDTH,
m_refX3, m_refY3,
m_io.DRead16(Io::BG3PA),
m_io.DRead16(Io::BG3PC));
// if objects are enabled draw them
if (layersOn & (0x1 << 4)) m_objs.DrawLine(line, lineObj);
break;
// 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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/interpreter.hpp"
#include "ameteor/cpu.hpp"
#include "ameteor/memory.hpp"
#include "ameteor/bios.hpp"
#include "ameteor/disassembler/instruction.hpp"
#include "globals.hpp"
#include "cpu_globals.hpp"
#include "ameteor.hpp"
#include "debug.hpp"
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);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

493
libmeteor/source/io.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/io.hpp"
#include "ameteor/dma.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
#include <cstring>
#include "debug.hpp"
#define W8(add, val) \
m_iomem[(add) & 0xFFF] = (val)
#define W16(add, val) \
*(uint16_t*)(m_iomem + ((add) & 0xFFF)) = (val)
#define W32(add, val) \
*(uint32_t*)(m_iomem + ((add) & 0xFFF)) = (val)
namespace AMeteor
{
Io::Io ()
{
m_iomem = new uint8_t[IO_SIZE];
Reset ();
}
Io::~Io ()
{
delete [] m_iomem;
}
void Io::Reset ()
{
std::memset(m_iomem, 0, IO_SIZE);
// TODO use clears intead
// TODO DISPCNT should be 0x80 by default
// TODO do it also for clears
// TODO lcd should draw white lines when hblank forced
// TODO when passing disabling hblank forced, lcd should start drawing from
// first line
W16(SOUNDBIAS, 0x0200); // default value
W16(KEYINPUT, 0x03FF); // all keys released
W8(HALTCNT, 0xFF); // normal mode (internal)
W16(DISPSTAT, 0x0004); // vcount match (since default vcount is 0)
W16(BG2PA, 0x0100);
W16(BG2PD, 0x0100);
W16(BG3PA, 0x0100);
W16(BG3PD, 0x0100);
W16(RCNT, 0x8000); // we start in general purpose mode
}
void Io::ClearSio ()
{
// TODO
W16(RCNT, 0x8000);
}
void Io::ClearSound ()
{
// TODO
}
void Io::ClearOthers ()
{
// FIXME !! shouldn't we call Write*() ?
// lcd
for (uint8_t i = 0x0; i < 0x56; i += 2)
Write16(i, 0x0000);
// dma
for (uint8_t i = 0xB0; i < 0xE0; i += 4)
Write32(i, 0x0000);
// FIXME : should timers be set to 0 too ? (vba does not)
W8(HALTCNT, 0xFF); // normal mode (internal)
W16(IE, 0x0000);
W16(IF, 0x0000);
W16(IME, 0x0000);
Write16(WAITCNT, 0x0000);
W16(BG2PA, 0x0100);
W16(BG2PD, 0x0100);
W16(BG3PA, 0x0100);
W16(BG3PD, 0x0100);
}
// TODO implement unreadable or write-only io
uint8_t Io::Read8 (uint32_t add)
{
//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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/keypad.hpp"
#include "globals.hpp"
#include "ameteor.hpp"
namespace AMeteor
{
Keypad::Keypad () :
m_keyinput(IO.GetRef16(Io::KEYINPUT)),
m_keycnt(IO.GetRef16(Io::KEYCNT))
{
}
void Keypad::KeyPressed(int code)
{
if (m_keys.count(code))
m_keyinput &= ~m_keys[code];
}
void Keypad::KeyReleased(int code)
{
if (m_keys.count(code))
m_keyinput |= m_keys[code];
}
void Keypad::JoyButtonPressed (uint16_t joyid, uint16_t button)
{
uint32_t id = ((int)joyid) << 16 | button;
if (m_joys.count(id))
m_keyinput &= ~m_joys[id];
}
void Keypad::JoyButtonReleased (uint16_t joyid, uint16_t button)
{
uint32_t id = ((int)joyid) << 16 | button;
if (m_joys.count(id))
m_keyinput |= m_joys[id];
}
void Keypad::JoyMoved (uint16_t joyid, uint16_t axis, float pos)
{
uint32_t id = (((int)joyid) << 16) | ((pos < 0) << 15) | (axis & 0x7FFF);
// if pos is 0, we disable the positive and negative targets
if (pos == 0)
{
if (m_axis.count(id))
m_keyinput |= m_axis[id];
if (m_axis.count(id | (1 << 15)))
m_keyinput |= m_axis[id | (1 << 15)];
}
else
{
// we enable the corresponding button
if (m_axis.count(id))
m_keyinput &= ~((uint16_t)m_axis[id]);
// we disable the opposite button (we may have skipped 0)
if (m_axis.count(id ^ 0x8000))
m_keyinput |= m_axis[id ^ 0x8000];
}
}
void Keypad::VBlank ()
{
// if keypad IRQ are enabled
if (m_keycnt & (0x1 << 14))
// if irq condition is and
if (m_keycnt & (0x1 << 15))
{
// if condition is satisfied
if ((~m_keyinput & m_keycnt & 0x3FF) == (m_keycnt & 0x3FF))
CPU.SendInterrupt(0x1000);
}
// if irq condition is or
else
{
// if condition is satisfied
if (~m_keyinput & m_keycnt & 0x3FF)
CPU.SendInterrupt(0x1000);
}
}
}

148
libmeteor/source/lcd.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#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;
}
}

823
libmeteor/source/memory.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#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 <cstring>
#include <fstream>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cerrno>
#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<Eeprom*>(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<Eeprom*>(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<Eeprom*>(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);
}
}

111
libmeteor/source/sound.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#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;
}
}

62
libmeteor/source/sram.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include "ameteor/sram.hpp"
#include <cstring>
#include <fstream>
#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;
}
}

146
libmeteor/source/timer.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#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;
}
}