stuff, don't use
This commit is contained in:
parent
6bf640c4bc
commit
93ad387db8
|
@ -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" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -164,6 +164,10 @@ namespace BizHawk
|
|||
case ".CRT":
|
||||
Game.System = "C64";
|
||||
break;
|
||||
|
||||
case ".GBA":
|
||||
Game.System = "GBA";
|
||||
break;
|
||||
}
|
||||
|
||||
Game.Name = Path.GetFileNameWithoutExtension(fileName).Replace('_', ' ');
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -437,6 +437,10 @@ namespace BizHawk.MultiClient
|
|||
{
|
||||
return GetC64ControllersAsMnemonic();
|
||||
}
|
||||
else if (ControlType == "GBA Controller")
|
||||
{
|
||||
return "EAT AT JOE'S";
|
||||
}
|
||||
|
||||
StringBuilder input = new StringBuilder("|");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue