205 lines
4.1 KiB
C++
205 lines
4.1 KiB
C++
![]() |
// Meteor - A Nintendo Gameboy Advance emulator
|
|||
|
// Copyright (C) 2009-2011 Philippe Daouadi
|
|||
|
//
|
|||
|
// This program is free software: you can redistribute it and/or modify
|
|||
|
// it under the terms of the GNU General Public License as published by
|
|||
|
// the Free Software Foundation, either version 3 of the License, or
|
|||
|
// (at your option) any later version.
|
|||
|
//
|
|||
|
// This program is distributed in the hope that it will be useful,
|
|||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
// GNU General 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;
|
|||
|
}
|
|||
|
}
|