Update to v087r26 release.

byuu says:

Changelog:
- fixed FIFO[1] reset behavior (fixes audio in Sword of Mana)
- added FlashROM emulation (both sizes)
- GBA parses RAM settings from manifest.xml now
- save RAM is written to disk now
- added save state support (it's currently broken, though)
- fixed ROM/RAM access timings
- open bus should mostly work (we don't do the PC+12 stuff yet)
- emulated the undocumented memory control register (mirror IWRAM,
  disable I+EWRAM, EWRAM wait state count)
- emulated keypad interrupts
- emulated STOP (freezes video, audio, DMA and timers; only breaks on
  keypad IRQs)
- probably a lot more, it was a long night ...

Show stoppers, missing things, broken things, etc:
- ST018 is still completely broken
- GBC audio sequencer apparently needs work
- GBA audio FIFO buffer seems too quiet
- PHI / ROM prefetch needs to be emulated (no idea on how to do this,
  especially PHI)
- SOUNDBIAS 64/128/256khz modes should output at that resolution
  (really, we need to simulate PWM properly, no idea on how to do this)
- object mosaic top-left coordinates are wrong (minor, fixing will
  actually make the effect look worse)
- need to emulate PPU greenswap and color palette distortion (no idea on
  how do this)
- need GBA save type database (I would also LIKE to blacklist
  / patch-out trainers, but that's a discussion for another day.)
- some ARM ops advance the prefetch buffer, so you can read PC+12 in
  some cases
This commit is contained in:
Tim Allen 2012-04-16 22:19:39 +10:00
parent 1c18812f47
commit 0cd0dcd811
39 changed files with 986 additions and 226 deletions

View File

@ -1,7 +1,7 @@
#ifndef BASE_HPP
#define BASE_HPP
static const char Version[] = "087.25";
static const char Version[] = "087.26";
#include <nall/platform.hpp>
#include <nall/algorithm.hpp>

View File

@ -11,12 +11,20 @@ namespace GBA {
#include "noise.cpp"
#include "sequencer.cpp"
#include "fifo.cpp"
#include "serialization.cpp"
APU apu;
void APU::Enter() { apu.main(); }
void APU::Enter() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
apu.main();
}
}
void APU::main() {
while(true) {
for(unsigned n = 0; n < 128; n++) {
runsequencer();
}
@ -61,9 +69,9 @@ void APU::main() {
if(regs.bias.amplitude == 2) lsample &= ~7, rsample &= ~7;
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
interface->audioSample(lsample << 5, rsample << 5);
step(512);
}
}
void APU::step(unsigned clocks) {

View File

@ -10,6 +10,8 @@ struct APU : Thread, MMIO {
void power();
void runsequencer();
void serialize(serializer&);
};
extern APU apu;

View File

@ -151,7 +151,7 @@ void APU::write(uint32 addr, uint8 byte) {
fifo[1].renable = byte >> 4;
fifo[1].lenable = byte >> 5;
fifo[1].timer = byte >> 6;
if(byte & 1 << 7) fifo[0].reset();
if(byte & 1 << 7) fifo[1].reset();
return;
//NR52

106
bsnes/gba/apu/serialization.cpp Executable file
View File

@ -0,0 +1,106 @@
void APU::serialize(serializer &s) {
Thread::serialize(s);
s.integer(regs.bias.level);
s.integer(regs.bias.amplitude);
s.integer(regs.clock);
s.integer(square1.sweep.shift);
s.integer(square1.sweep.direction);
s.integer(square1.sweep.frequency);
s.integer(square1.sweep.enable);
s.integer(square1.sweep.negate);
s.integer(square1.sweep.period);
s.integer(square1.envelope.frequency);
s.integer(square1.envelope.direction);
s.integer(square1.envelope.volume);
s.integer(square1.envelope.period);
s.integer(square1.enable);
s.integer(square1.length);
s.integer(square1.duty);
s.integer(square1.frequency);
s.integer(square1.counter);
s.integer(square1.initialize);
s.integer(square1.shadowfrequency);
s.integer(square1.signal);
s.integer(square1.output);
s.integer(square1.period);
s.integer(square1.phase);
s.integer(square1.volume);
s.integer(square2.envelope.frequency);
s.integer(square2.envelope.direction);
s.integer(square2.envelope.volume);
s.integer(square2.envelope.period);
s.integer(square2.enable);
s.integer(square2.length);
s.integer(square2.duty);
s.integer(square2.frequency);
s.integer(square2.counter);
s.integer(square2.initialize);
s.integer(square2.shadowfrequency);
s.integer(square2.signal);
s.integer(square2.output);
s.integer(square2.period);
s.integer(square2.phase);
s.integer(square2.volume);
s.integer(wave.mode);
s.integer(wave.bank);
s.integer(wave.dacenable);
s.integer(wave.length);
s.integer(wave.volume);
s.integer(wave.frequency);
s.integer(wave.counter);
s.integer(wave.initialize);
for(auto &value : wave.pattern) s.integer(value);
s.integer(wave.enable);
s.integer(wave.output);
s.integer(wave.patternaddr);
s.integer(wave.patternbank);
s.integer(wave.patternsample);
s.integer(wave.period);
s.integer(noise.envelope.frequency);
s.integer(noise.envelope.direction);
s.integer(noise.envelope.volume);
s.integer(noise.envelope.period);
s.integer(noise.length);
s.integer(noise.divisor);
s.integer(noise.narrowlfsr);
s.integer(noise.frequency);
s.integer(noise.counter);
s.integer(noise.initialize);
s.integer(noise.enable);
s.integer(noise.lfsr);
s.integer(noise.output);
s.integer(noise.period);
s.integer(noise.volume);
s.integer(sequencer.volume);
s.integer(sequencer.lvolume);
s.integer(sequencer.rvolume);
for(auto &flag : sequencer.lenable) s.integer(flag);
for(auto &flag : sequencer.renable) s.integer(flag);
for(auto &flag : sequencer.enable) s.integer(flag);
s.integer(sequencer.masterenable);
s.integer(sequencer.base);
s.integer(sequencer.step);
s.integer(sequencer.lsample);
s.integer(sequencer.rsample);
for(auto &f : fifo) {
for(auto &value : f.sample) s.integer(value);
s.integer(f.output);
s.integer(f.rdoffset);
s.integer(f.wroffset);
s.integer(f.size);
s.integer(f.volume);
s.integer(f.lenable);
s.integer(f.renable);
s.integer(f.timer);
}
}

View File

@ -3,55 +3,119 @@
namespace GBA {
#include "eeprom.cpp"
#include "flashrom.cpp"
#include "serialization.cpp"
Cartridge cartridge;
bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
if(rom.data) delete[] rom.data;
rom.data = new uint8[rom.size = 32 * 1024 * 1024];
for(unsigned addr = 0; addr < 32 * 1024 * 1024; addr++) {
XML::Document document(markup);
for(unsigned addr = 0; addr < rom.size; addr++) {
rom.data[addr] = data[Bus::mirror(addr, size)];
}
if(ram.data) delete[] ram.data;
ram.data = new uint8[ram.size = 64 * 1024]();
has_sram = false;
has_eeprom = false;
has_flashrom = false;
if(document["cartridge"]["ram"].exists()) {
auto &info = document["cartridge"]["ram"];
if(info["type"].data == "SRAM" || info["type"].data == "FRAM") {
has_sram = true;
ram.size = 32 * 1024;
}
if(info["type"].data == "EEPROM") {
has_eeprom = true;
eeprom.size = numeral(info["size"].data);
eeprom.mask = size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000;
eeprom.test = size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
}
if(info["type"].data == "FlashROM") {
has_flashrom = true;
flashrom.id = numeral(info["id"].data);
flashrom.size = numeral(info["size"].data);
}
}
sha256 = nall::sha256(rom.data, rom.size);
system.load();
return loaded = true;
}
void Cartridge::unload() {
if(loaded) return;
loaded = false;
delete[] rom.data;
rom.data = nullptr;
rom.size = 0u;
}
void Cartridge::power() {
eeprom.power();
flashrom.power();
}
has_eeprom = false;
uint8* Cartridge::ram_data() {
if(has_sram) return ram.data;
if(has_eeprom) return eeprom.data.data();
if(has_flashrom) return flashrom.data;
return nullptr;
}
unsigned Cartridge::ram_size() {
if(has_sram) return ram.size;
if(has_eeprom) return eeprom.size;
if(has_flashrom) return flashrom.size;
return 0u;
}
uint32 Cartridge::read(uint8 *data, uint32 addr, uint32 size) {
if(size == Word) addr &= ~3;
if(size == Half) addr &= ~1;
data += addr;
if(size == Word) return data[0] << 0 | data[1] << 8 | data[2] << 16 | data[3] << 24;
if(size == Half) return data[0] << 0 | data[1] << 8;
return data[0];
}
void Cartridge::write(uint8 *data, uint32 addr, uint32 size, uint32 word) {
if(size == Word) addr &= ~3;
if(size == Half) addr &= ~1;
data += addr;
switch(size) {
case Word: data[3] = word >> 24;
data[2] = word >> 16;
case Half: data[1] = word >> 8;
case Byte: data[0] = word >> 0;
}
}
uint32 Cartridge::read(uint32 addr, uint32 size) {
if(has_eeprom && (addr & 0x0f000000) == 0x0d000000) return eeprom.read();
if((addr & 0x0e000000) == 0x08000000) return rom.read(addr & 0x01ffffff, size);
if((addr & 0x0e000000) == 0x0a000000) return rom.read(addr & 0x01ffffff, size);
if((addr & 0x0e000000) == 0x0c000000) return rom.read(addr & 0x01ffffff, size);
if((addr & 0x0e000000) == 0x0e000000) return ram.read(addr & 0x0000ffff, size);
if(has_sram && (addr & 0x0e000000 ) == 0x0e000000 ) return read(ram.data, addr & 0x7fff, size);
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.read();
if(has_flashrom && (addr & 0x0f000000 ) == 0x0e000000 ) return flashrom.read(addr);
if(addr < 0x0e000000) return read(rom.data, addr & 0x01ffffff, size);
return cpu.pipeline.fetch.instruction;
}
void Cartridge::write(uint32 addr, uint32 size, uint32 word) {
if((addr & 0x0f000000) == 0x0d000000) { has_eeprom = true; return eeprom.write(word & 1); }
if((addr & 0x0e000000) == 0x0e000000) return ram.write(addr & 0x0000ffff, size, word);
if(has_sram && (addr & 0x0e000000 ) == 0x0e000000 ) return write(ram.data, addr & 0x7fff, size, word);
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.write(word & 1);
if(has_flashrom && (addr & 0x0f000000 ) == 0x0e000000 ) return flashrom.write(addr, word);
}
Cartridge::Cartridge() {
loaded = false;
rom.data = new uint8[rom.size = 32 * 1024 * 1024];
ram.data = new uint8[ram.size = 32 * 1024];
flashrom.data = new uint8[flashrom.size = 128 * 1024];
}
Cartridge::~Cartridge() {
delete[] rom.data;
delete[] ram.data;
delete[] flashrom.data;
}
}

View File

@ -1,20 +1,29 @@
struct Cartridge : property<Cartridge> {
StaticMemory rom;
StaticMemory ram;
#include "memory.hpp"
readonly<bool> loaded;
readonly<string> sha256;
readonly<bool> has_sram;
readonly<bool> has_eeprom;
readonly<bool> has_flashrom;
bool load(const string &markup, const uint8_t *data, unsigned size);
void unload();
void power();
uint8* ram_data();
unsigned ram_size();
uint32 read(uint8 *data, uint32 addr, uint32 size);
void write(uint8 *data, uint32 addr, uint32 size, uint32 word);
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
void serialize(serializer&);
Cartridge();
~Cartridge();
};
extern Cartridge cartridge;

View File

@ -23,7 +23,7 @@ void Cartridge::EEPROM::write(bool bit) {
else if(mode == Mode::ReadAddress) {
address = (address << 1) | bit;
if(++offset == size) {
if(++offset == bits) {
mode = Mode::ReadValidate;
offset = 0;
}
@ -36,7 +36,7 @@ void Cartridge::EEPROM::write(bool bit) {
else if(mode == Mode::WriteAddress) {
address = (address << 1) | bit;
if(++offset == size) {
if(++offset == bits) {
mode = Mode::WriteData;
offset = 0;
}
@ -56,9 +56,8 @@ void Cartridge::EEPROM::write(bool bit) {
}
void Cartridge::EEPROM::power() {
data.resize(64 * 1024);
data.clear();
size = 6;
data.resize(size);
bits = (size <= 512 ? 6 : 14);
mode = Mode::Wait;
offset = 0;

View File

@ -0,0 +1,90 @@
//Dev.ID Size Blocks Manufacturer
//====== ===== ======== ============
//0xd4bf 64KB 16x4096 SST
//0x1cc2 64KB 16x4096 Macronix
//0x1b32 64KB 16x4096 Panasonic
//0x3d1f 64KB 512x 128 Atmel
//0x1362 128KB 32x4096 Sanyo
//0x09c2 128KB 32x4096 Macronix
uint8 Cartridge::FlashROM::read(uint16 addr) {
if(idmode) {
if(addr == 0x0000) return id >> 0;
if(addr == 0x0001) return id >> 8;
return 0u;
}
return data[bank << 16 | addr];
}
void Cartridge::FlashROM::write(uint16 addr, uint8 byte) {
if(bankselect) {
bankselect = false;
//bank select is only applicable on 128KB chips
if(addr == 0x0000) bank = byte & (size > 64 * 1024);
return;
}
if(writeselect) {
//Atmel writes 128 bytes per command; all others write 1 byte per command
if(id != 0x3d1f || (addr & 0x007f) == 0x007f) writeselect = false;
data[bank << 16 | addr] = byte;
return;
}
if(byte == 0xaa && addr == 0x5555) { unlockhi = true; return; }
if(byte == 0x55 && addr == 0x2aaa) { unlocklo = true; return; }
if(unlockhi && unlocklo) {
unlockhi = false;
unlocklo = false;
if(byte == 0x10 && addr == 0x5555) {
if(erasemode) {
erasemode = false;
for(unsigned n = 0; n < size; n++) data[n] = 0xff;
}
}
if(byte == 0x30 && (addr & 0x0fff) == 0x0000) {
//command only valid for non-Atmel chips
if(erasemode && id != 0x3d1f) {
erasemode = false;
unsigned offset = bank << 16 | (addr & ~4095);
for(unsigned n = 0; n < 4096; n++) data[offset++] = 0xff;
}
}
if(byte == 0x80 && addr == 0x5555) {
erasemode = true;
}
if(byte == 0x90 && addr == 0x5555) {
idmode = true;
}
if(byte == 0xa0 && addr == 0x5555) {
writeselect = true;
}
if(byte == 0xb0 && addr == 0x5555) {
bankselect = true;
}
if(byte == 0xf0 && addr == 0x5555) {
idmode = false;
}
}
}
void Cartridge::FlashROM::power() {
id = 0x09c2;
size = 128 * 1024;
unlockhi = false;
unlocklo = false;
idmode = false;
bankselect = false;
writeselect = false;
bank = 0;
}

View File

@ -1,6 +1,14 @@
struct Memory {
uint8 *data;
unsigned size;
} rom, ram;
struct EEPROM {
bitarray data;
unsigned size;
unsigned mask;
unsigned test;
unsigned bits;
enum class Mode : unsigned { Wait, Command, ReadAddress, ReadValidate, ReadData, WriteAddress, WriteData, WriteValidate } mode;
unsigned offset;
@ -10,3 +18,21 @@ struct EEPROM {
void write(bool bit);
void power();
} eeprom;
struct FlashROM {
uint8 *data;
unsigned size;
uint16 id;
bool unlockhi;
bool unlocklo;
bool idmode;
bool erasemode;
bool bankselect;
bool writeselect;
bool bank;
uint8 read(uint16 addr);
void write(uint16 addr, uint8 byte);
void power();
} flashrom;

View File

@ -0,0 +1,5 @@
void Cartridge::serialize(serializer &s) {
if(has_sram) s.array(ram.data, ram.size);
if(has_eeprom) s.array(eeprom.data.data(), eeprom.size);
if(has_flashrom) s.array(flashrom.data, flashrom.size);
}

View File

@ -4,14 +4,25 @@ namespace GBA {
#include "registers.cpp"
#include "mmio.cpp"
#include "memory.cpp"
#include "dma.cpp"
#include "timer.cpp"
#include "serialization.cpp"
CPU cpu;
void CPU::Enter() { cpu.enter(); }
void CPU::enter() {
void CPU::Enter() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
cpu.main();
}
}
void CPU::main() {
#if defined(DEBUG)
if(crash) {
print(cpsr().t ? disassemble_thumb_instruction(pipeline.execute.address)
: disassemble_arm_instruction(pipeline.execute.address), "\n");
@ -19,25 +30,39 @@ void CPU::enter() {
print("Executed: ", instructions, "\n");
while(true) step(frequency);
}
#endif
processor.irqline = regs.ime && (regs.irq.enable & regs.irq.flag);
if(regs.mode == Registers::Mode::Stop) {
if((regs.irq.enable.keypad & regs.irq.flag.keypad) == 0) {
sync_step(16); //STOP does not advance timers
} else {
regs.mode = Registers::Mode::Normal;
}
return;
}
dma_run();
if(regs.mode == Registers::Mode::Halt) {
if((regs.irq.enable & regs.irq.flag) == 0) {
step(16);
continue;
}
} else {
regs.mode = Registers::Mode::Normal;
}
return;
}
exec();
}
}
void CPU::step(unsigned clocks) {
timer_step(clocks);
sync_step(clocks);
}
void CPU::sync_step(unsigned clocks) {
ppu.clock -= clocks;
if(ppu.clock < 0) co_switch(ppu.thread);
@ -60,12 +85,25 @@ void CPU::bus_write(uint32 addr, uint32 size, uint32 word) {
return bus.write(addr, size, word);
}
void CPU::keypad_run() {
if(regs.keypad.control.enable == false) return;
bool test = regs.keypad.control.condition; //0 = OR, 1 = AND
for(unsigned n = 0; n < 10; n++) {
if(regs.keypad.control.flag[n] == false) continue;
bool input = interface->inputPoll(n);
if(regs.keypad.control.condition == 0) test |= input;
if(regs.keypad.control.condition == 1) test &= input;
}
if(test) regs.irq.flag.keypad = true;
}
void CPU::power() {
create(CPU::Enter, 16777216);
ARM::power();
for(unsigned n = 0; n < iwram.size; n++) iwram.data[n] = 0;
for(unsigned n = 0; n < ewram.size; n++) ewram.data[n] = 0;
for(unsigned n = 0; n < 32 * 1024; n++) iwram[n] = 0;
for(unsigned n = 0; n < 256 * 1024; n++) ewram[n] = 0;
for(auto &dma : regs.dma) {
dma.source = 0;
@ -101,8 +139,13 @@ void CPU::power() {
}
CPU::CPU() {
iwram.data = new uint8[iwram.size = 32 * 1024];
ewram.data = new uint8[ewram.size = 256 * 1024];
iwram = new uint8[ 32 * 1024];
ewram = new uint8[256 * 1024];
}
CPU::~CPU() {
delete[] iwram;
delete[] ewram;
}
}

View File

@ -1,29 +1,39 @@
struct CPU : Processor::ARM, Thread, MMIO {
StaticMemory iwram;
StaticMemory ewram;
uint8 *iwram;
uint8 *ewram;
#include "registers.hpp"
#include "state.hpp"
static void Enter();
void enter();
void main();
void step(unsigned clocks);
void sync_step(unsigned clocks);
void bus_idle(uint32 addr);
uint32 bus_read(uint32 addr, uint32 size);
void bus_write(uint32 addr, uint32 size, uint32 word);
void keypad_run();
void power();
uint8 read(uint32 addr);
void write(uint32 addr, uint8 byte);
uint32 iwram_read(uint32 addr, uint32 size);
void iwram_write(uint32 addr, uint32 size, uint32 word);
uint32 ewram_read(uint32 addr, uint32 size);
void ewram_write(uint32 addr, uint32 size, uint32 word);
void dma_run();
void dma_transfer(Registers::DMA &dma);
void timer_step(unsigned clocks);
void timer_increment(unsigned n);
void serialize(serializer&);
CPU();
~CPU();
};
extern CPU cpu;

55
bsnes/gba/cpu/memory.cpp Executable file
View File

@ -0,0 +1,55 @@
uint32 CPU::iwram_read(uint32 addr, uint32 size) {
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
if(size == Word) return iwram_read(addr &~ 2, Half) << 0 | iwram_read(addr | 2, Half) << 16;
if(size == Half) return iwram_read(addr &~ 1, Byte) << 0 | iwram_read(addr | 1, Byte) << 8;
return iwram[addr & 0x7fff];
}
void CPU::iwram_write(uint32 addr, uint32 size, uint32 word) {
if(regs.memory.control.disable) return;
if(size == Word) {
iwram_write(addr &~2, Half, word >> 0);
iwram_write(addr | 2, Half, word >> 16);
return;
}
if(size == Half) {
iwram_write(addr &~1, Byte, word >> 0);
iwram_write(addr | 1, Byte, word >> 8);
return;
}
iwram[addr & 0x7fff] = word;
}
uint32 CPU::ewram_read(uint32 addr, uint32 size) {
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
if(regs.memory.control.ewram == false) return iwram_read(addr, size);
if(size == Word) return ewram_read(addr &~ 2, Half) << 0 | ewram_read(addr | 2, Half) << 16;
if(size == Half) return ewram_read(addr &~ 1, Byte) << 0 | ewram_read(addr | 1, Byte) << 8;
return ewram[addr & 0x3ffff];
}
void CPU::ewram_write(uint32 addr, uint32 size, uint32 word) {
if(regs.memory.control.disable) return;
if(regs.memory.control.ewram == false) return iwram_write(addr, size, word);
if(size == Word) {
ewram_write(addr &~2, Half, word >> 0);
ewram_write(addr | 2, Half, word >> 16);
return;
}
if(size == Half) {
ewram_write(addr &~1, Byte, word >> 0);
ewram_write(addr | 1, Byte, word >> 8);
return;
}
ewram[addr & 0x3ffff] = word;
}

View File

@ -42,34 +42,34 @@ uint8 CPU::Registers::TimerControl::operator=(uint8 source) {
CPU::Registers::KeypadControl::operator uint16() const {
return (
(a << 0)
| (b << 1)
| (select << 2)
| (start << 3)
| (right << 4)
| (left << 5)
| (up << 6)
| (down << 7)
| (r << 8)
| (l << 9)
(flag[0] << 0)
| (flag[1] << 1)
| (flag[2] << 2)
| (flag[3] << 3)
| (flag[4] << 4)
| (flag[5] << 5)
| (flag[6] << 6)
| (flag[7] << 7)
| (flag[8] << 8)
| (flag[9] << 9)
| (enable << 14)
| (condition << 15)
);
}
uint16 CPU::Registers::KeypadControl::operator=(uint16 source) {
a = (source >> 0) & 1;
b = (source >> 1) & 1;
select = (source >> 2) & 1;
start = (source >> 3) & 1;
right = (source >> 4) & 1;
left = (source >> 5) & 1;
up = (source >> 6) & 1;
down = (source >> 7) & 1;
r = (source >> 8) & 1;
l = (source >> 9) & 1;
enable = (source >> 14) & 1;
condition = (source >> 15) & 1;
flag[0] = source >> 0;
flag[1] = source >> 1;
flag[2] = source >> 2;
flag[3] = source >> 3;
flag[4] = source >> 4;
flag[5] = source >> 5;
flag[6] = source >> 6;
flag[7] = source >> 7;
flag[8] = source >> 8;
flag[9] = source >> 9;
enable = source >> 14;
condition = source >> 15;
return operator uint16();
}

View File

@ -46,18 +46,9 @@ struct Registers {
} timer[4];
struct KeypadControl {
bool a;
bool b;
bool select;
bool start;
bool right;
bool left;
bool up;
bool down;
bool r;
bool l;
bool enable;
bool condition;
uint1 flag[10];
uint1 enable;
uint1 condition;
operator uint16() const;
uint16 operator=(uint16 source);

77
bsnes/gba/cpu/serialization.cpp Executable file
View File

@ -0,0 +1,77 @@
void CPU::serialize(serializer &s) {
ARM::serialize(s);
Thread::serialize(s);
s.array(iwram, 32 * 1024);
s.array(ewram, 256 * 1024);
for(auto &dma : regs.dma) {
s.integer(dma.source);
s.integer(dma.target);
s.integer(dma.length);
s.integer(dma.control.targetmode);
s.integer(dma.control.sourcemode);
s.integer(dma.control.repeat);
s.integer(dma.control.size);
s.integer(dma.control.drq);
s.integer(dma.control.timingmode);
s.integer(dma.control.irq);
s.integer(dma.control.enable);
s.integer(dma.run.target);
s.integer(dma.run.source);
s.integer(dma.run.length);
}
for(auto &timer : regs.timer) {
s.integer(timer.period);
s.integer(timer.reload);
s.integer(timer.control.frequency);
s.integer(timer.control.cascade);
s.integer(timer.control.irq);
s.integer(timer.control.enable);
}
for(auto &flag : regs.keypad.control.flag) s.integer(flag);
s.integer(regs.keypad.control.enable);
s.integer(regs.keypad.control.condition);
s.integer(regs.ime);
s.integer(regs.irq.enable.vblank);
s.integer(regs.irq.enable.hblank);
s.integer(regs.irq.enable.vcoincidence);
for(auto &flag : regs.irq.enable.timer) s.integer(flag);
s.integer(regs.irq.enable.serial);
for(auto &flag : regs.irq.enable.dma) s.integer(flag);
s.integer(regs.irq.enable.keypad);
s.integer(regs.irq.enable.cartridge);
s.integer(regs.irq.flag.vblank);
s.integer(regs.irq.flag.hblank);
s.integer(regs.irq.flag.vcoincidence);
for(auto &flag : regs.irq.flag.timer) s.integer(flag);
s.integer(regs.irq.flag.serial);
for(auto &flag : regs.irq.flag.dma) s.integer(flag);
s.integer(regs.irq.flag.keypad);
s.integer(regs.irq.flag.cartridge);
for(auto &flag : regs.wait.control.nwait) s.integer(flag);
for(auto &flag : regs.wait.control.swait) s.integer(flag);
s.integer(regs.wait.control.phi);
s.integer(regs.wait.control.prefetch);
s.integer(regs.wait.control.gametype);
s.integer(regs.memory.control.disable);
s.integer(regs.memory.control.unknown1);
s.integer(regs.memory.control.ewram);
s.integer(regs.memory.control.ewramwait);
s.integer(regs.memory.control.unknown2);
s.integer(regs.postboot);
s.integer((unsigned&)regs.mode);
s.integer(regs.clock);
s.integer(pending.dma.vblank);
s.integer(pending.dma.hblank);
s.integer(pending.dma.hdma);
}

View File

@ -3,6 +3,7 @@
namespace GBA {
#include "mmio.cpp"
#include "serialization.cpp"
Bus bus;
struct UnmappedMemory : Memory {
@ -12,49 +13,6 @@ struct UnmappedMemory : Memory {
static UnmappedMemory unmappedMemory;
uint8& StaticMemory::operator[](uint32 addr) {
return data[addr];
}
uint32 StaticMemory::read(uint32 addr, uint32 size) {
switch(size) {
case Word: addr &= ~3; return (data[addr + 0] << 0) | (data[addr + 1] << 8) | (data[addr + 2] << 16) | (data[addr + 3] << 24);
case Half: addr &= ~1; return (data[addr + 0] << 0) | (data[addr + 1] << 8);
case Byte: return (data[addr + 0] << 0);
}
}
void StaticMemory::write(uint32 addr, uint32 size, uint32 word) {
switch(size) {
case Word:
addr &= ~3;
data[addr + 0] = word >> 0;
data[addr + 1] = word >> 8;
data[addr + 2] = word >> 16;
data[addr + 3] = word >> 24;
break;
case Half:
addr &= ~1;
data[addr + 0] = word >> 0;
data[addr + 1] = word >> 8;
break;
case Byte:
data[addr + 0] = word >> 0;
break;
}
}
StaticMemory::StaticMemory() {
data = nullptr;
size = 0u;
}
StaticMemory::~StaticMemory() {
if(data) delete[] data;
}
//
uint32 Bus::mirror(uint32 addr, uint32 size) {
uint32 base = 0;
if(size) {
@ -78,12 +36,20 @@ uint32 Bus::speed(uint32 addr, uint32 size) {
static unsigned timing[] = { 5, 4, 3, 9 };
unsigned n = cpu.regs.wait.control.nwait[addr >> 25 & 3];
unsigned s = cpu.regs.wait.control.swait[addr >> 25 & 3];
n = timing[n];
switch(addr >> 25 & 3) {
case 0: s = s ? 3 : 2; break;
case 1: s = s ? 5 : 2; break;
case 2: s = s ? 9 : 2; break;
case 3: s = n; break;
}
bool sequential = cpu.sequential();
if((addr & 0xffff << 1) == 0) sequential = false;
if(idleflag) sequential = false;
if((addr & 0xffff << 1) == 0) sequential = false; //N cycle on 16-bit ROM crossing page boundary (RAM S==N)
if(idleflag) sequential = false; //LDR/LDM interrupts instruction fetches
if(sequential) return s << (size == Word); //16-bit bus
if(sequential) return s << (size == Word); //16-bit bus requires two transfers for words
if(size == Word) n += s;
return n;
}
@ -91,7 +57,7 @@ uint32 Bus::speed(uint32 addr, uint32 size) {
switch(addr >> 24 & 7) {
case 0: return 1;
case 1: return 1;
case 2: return 3 << (size == Word);
case 2: return (1 + 15 - cpu.regs.memory.control.ewramwait) << (size == Word);
case 3: return 1;
case 4: return 1;
case 5: return 1 << (size == Word);
@ -111,12 +77,12 @@ uint32 Bus::read(uint32 addr, uint32 size) {
switch(addr >> 24 & 7) {
case 0: return bios.read(addr, size);
case 1: return bios.read(addr, size);
case 2: return cpu.ewram.read(addr & 0x3ffff, size);
case 3: return cpu.iwram.read(addr & 0x7fff, size);
case 2: return cpu.ewram_read(addr, size);
case 3: return cpu.iwram_read(addr, size);
case 4:
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->read(addr, size);
if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).read(0x04000800 | (addr & 3), size);
return 0u;
return cpu.pipeline.fetch.instruction;
case 5: return ppu.pram_read(addr, size);
case 6: return ppu.vram_read(addr, size);
case 7: return ppu.oam_read(addr, size);
@ -130,8 +96,8 @@ void Bus::write(uint32 addr, uint32 size, uint32 word) {
switch(addr >> 24 & 7) {
case 0: return;
case 1: return;
case 2: return cpu.ewram.write(addr & 0x3ffff, size, word);
case 3: return cpu.iwram.write(addr & 0x7fff, size, word);
case 2: return cpu.ewram_write(addr, size, word);
case 3: return cpu.iwram_write(addr, size, word);
case 4:
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->write(addr, size, word);
if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).write(0x04000800 | (addr & 3), size, word);

View File

@ -3,17 +3,6 @@ struct Memory {
virtual void write(uint32 addr, uint32 size, uint32 word) = 0;
};
struct StaticMemory : Memory {
uint8_t *data;
unsigned size;
uint8& operator[](uint32 addr);
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
StaticMemory();
~StaticMemory();
};
struct MMIO : Memory {
virtual uint8 read(uint32 addr) = 0;
virtual void write(uint32 addr, uint8 data) = 0;
@ -31,6 +20,8 @@ struct Bus : Memory {
uint32 read(uint32 addr, uint32 size);
void write(uint32 addr, uint32 size, uint32 word);
void power();
void serialize(serializer&);
};
extern Bus bus;

View File

@ -0,0 +1,3 @@
void Bus::serialize(serializer &s) {
s.integer(idleflag);
}

View File

@ -18,14 +18,21 @@ namespace GBA {
#include "screen.cpp"
#include "mmio.cpp"
#include "memory.cpp"
#include "serialization.cpp"
PPU ppu;
void PPU::Enter() { ppu.enter(); }
void PPU::enter() {
void PPU::Enter() {
while(true) {
scanline();
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
ppu.main();
}
}
void PPU::main() {
scanline();
}
void PPU::step(unsigned clocks) {
@ -80,6 +87,8 @@ void PPU::power() {
}
void PPU::scanline() {
cpu.keypad_run();
regs.status.vblank = regs.vcounter >= 160 && regs.vcounter <= 226;
regs.status.vcoincidence = regs.vcounter == regs.status.vcompare;
@ -103,7 +112,7 @@ void PPU::scanline() {
}
if(regs.vcounter < 160) {
if(regs.control.forceblank) {
if(regs.control.forceblank || cpu.regs.mode == CPU::Registers::Mode::Stop) {
render_forceblank();
} else {
for(unsigned x = 0; x < 240; x++) {

View File

@ -7,7 +7,7 @@ struct PPU : Thread, MMIO {
uint16 *blur;
static void Enter();
void enter();
void main();
void step(unsigned clocks);
void power();
@ -40,6 +40,7 @@ struct PPU : Thread, MMIO {
void render_window(unsigned window);
unsigned blend(unsigned above, unsigned eva, unsigned below, unsigned evb);
void serialize(serializer&);
PPU();
~PPU();
};

122
bsnes/gba/ppu/serialization.cpp Executable file
View File

@ -0,0 +1,122 @@
void PPU::serialize(serializer &s) {
Thread::serialize(s);
s.array(vram, 96 * 1024);
s.array(pram, 512);
s.integer(regs.control.bgmode);
s.integer(regs.control.cgbmode);
s.integer(regs.control.frame);
s.integer(regs.control.hblank);
s.integer(regs.control.objmapping);
s.integer(regs.control.forceblank);
for(auto &flag : regs.control.enable) s.integer(flag);
for(auto &flag : regs.control.enablewindow) s.integer(flag);
s.integer(regs.greenswap);
s.integer(regs.status.vblank);
s.integer(regs.status.hblank);
s.integer(regs.status.vcoincidence);
s.integer(regs.status.irqvblank);
s.integer(regs.status.irqhblank);
s.integer(regs.status.irqvcoincidence);
s.integer(regs.status.vcompare);
s.integer(regs.vcounter);
for(auto &bg : regs.bg) {
s.integer(bg.control.priority);
s.integer(bg.control.characterbaseblock);
s.integer(bg.control.mosaic);
s.integer(bg.control.colormode);
s.integer(bg.control.screenbaseblock);
s.integer(bg.control.affinewrap);
s.integer(bg.control.screensize);
s.integer(bg.hoffset);
s.integer(bg.voffset);
s.integer(bg.pa);
s.integer(bg.pb);
s.integer(bg.pc);
s.integer(bg.pd);
s.integer(bg.x);
s.integer(bg.y);
s.integer(bg.lx);
s.integer(bg.ly);
s.integer(bg.vmosaic);
s.integer(bg.hmosaic);
s.integer(bg.id);
}
for(auto &window : regs.window) {
s.integer(window.x1);
s.integer(window.x2);
s.integer(window.y1);
s.integer(window.y2);
}
for(auto &windowflags : regs.windowflags) {
for(auto &flag : windowflags.enable) s.integer(flag);
}
s.integer(regs.mosaic.bghsize);
s.integer(regs.mosaic.bgvsize);
s.integer(regs.mosaic.objhsize);
s.integer(regs.mosaic.objvsize);
for(auto &flag : regs.blend.control.above) s.integer(flag);
for(auto &flag : regs.blend.control.below) s.integer(flag);
s.integer(regs.blend.control.mode);
s.integer(regs.blend.eva);
s.integer(regs.blend.evb);
s.integer(regs.blend.evy);
for(unsigned l = 0; l < 6; l++) {
for(unsigned p = 0; p < 240; p++) {
auto &pixel = layer[l][p];
s.integer(pixel.enable);
s.integer(pixel.translucent);
s.integer(pixel.priority);
s.integer(pixel.color);
}
}
for(unsigned w = 0; w < 3; w++) {
for(unsigned p = 0; p < 240; p++) {
s.integer(windowmask[w][p]);
}
}
for(auto &value : vmosaic) s.integer(value);
for(auto &value : hmosaic) s.integer(value);
for(auto &obj : object) {
s.integer(obj.y);
s.integer(obj.affine);
s.integer(obj.affinesize);
s.integer(obj.mode);
s.integer(obj.mosaic);
s.integer(obj.colors);
s.integer(obj.shape);
s.integer(obj.x);
s.integer(obj.affineparam);
s.integer(obj.hflip);
s.integer(obj.vflip);
s.integer(obj.size);
s.integer(obj.character);
s.integer(obj.priority);
s.integer(obj.palette);
s.integer(obj.width);
s.integer(obj.height);
}
for(auto &par : objectparam) {
s.integer(par.pa);
s.integer(par.pb);
s.integer(par.pc);
s.integer(par.pd);
}
}

View File

@ -0,0 +1,60 @@
serializer System::serialize() {
serializer s(serialize_size);
unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = 0;
char description[512];
memset(&description, 0, sizeof description);
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
serialize_all(s);
return s;
}
bool System::unserialize(serializer &s) {
unsigned signature, version, crc32;
char description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
if(signature != 0x31545342) return false;
if(version != Info::SerializerVersion) return false;
//if(crc32 != 0) return false;
power();
serialize_all(s);
return true;
}
void System::serialize(serializer &s) {
}
void System::serialize_all(serializer &s) {
cartridge.serialize(s);
system.serialize(s);
cpu.serialize(s);
ppu.serialize(s);
apu.serialize(s);
bus.serialize(s);
}
void System::serialize_init() {
serializer s;
unsigned signature = 0, version = 0, crc32 = 0;
char description[512];
s.integer(signature);
s.integer(version);
s.integer(crc32);
s.array(description);
serialize_all(s);
serialize_size = s.size();
}

View File

@ -3,6 +3,7 @@
namespace GBA {
#include "bios.cpp"
#include "serialization.cpp"
BIOS bios;
System system;
@ -21,8 +22,35 @@ void System::power() {
scheduler.power();
}
void System::load() {
serialize_init();
}
void System::run() {
scheduler.enter();
}
void System::runtosave() {
scheduler.sync = Scheduler::SynchronizeMode::CPU;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.active = ppu.thread;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.active = apu.thread;
runthreadtosave();
scheduler.sync = Scheduler::SynchronizeMode::None;
}
void System::runthreadtosave() {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent);
}
}
}

View File

@ -18,8 +18,20 @@ struct BIOS : Memory {
struct System {
void init();
void term();
void load();
void power();
void run();
void runtosave();
void runthreadtosave();
unsigned serialize_size;
serializer serialize();
bool unserialize(serializer&);
void serialize(serializer&);
void serialize_all(serializer&);
void serialize_init();
};
extern BIOS bios;

View File

@ -8,6 +8,7 @@ namespace Processor {
#include "instructions-arm.cpp"
#include "instructions-thumb.cpp"
#include "disassembler.cpp"
#include "serialization.cpp"
void ARM::power() {
processor.power();
@ -76,7 +77,4 @@ void ARM::vector(uint32 addr, Processor::Mode mode) {
r(15) = addr;
}
void ARM::serialize(serializer &s) {
}
}

View File

@ -44,6 +44,8 @@ struct PSR {
m = d & 31;
return *this;
}
void serialize(serializer&);
};
struct Pipeline {

View File

@ -0,0 +1,70 @@
void ARM::PSR::serialize(serializer &s) {
s.integer(n);
s.integer(z);
s.integer(c);
s.integer(v);
s.integer(i);
s.integer(f);
s.integer(t);
s.integer(m);
}
void ARM::serialize(serializer &s) {
s.integer(processor.r0.data);
s.integer(processor.r1.data);
s.integer(processor.r2.data);
s.integer(processor.r3.data);
s.integer(processor.r4.data);
s.integer(processor.r5.data);
s.integer(processor.r6.data);
s.integer(processor.r7.data);
s.integer(processor.usr.r8.data);
s.integer(processor.usr.r9.data);
s.integer(processor.usr.r10.data);
s.integer(processor.usr.r11.data);
s.integer(processor.usr.r12.data);
s.integer(processor.usr.sp.data);
s.integer(processor.usr.lr.data);
s.integer(processor.fiq.r8.data);
s.integer(processor.fiq.r9.data);
s.integer(processor.fiq.r10.data);
s.integer(processor.fiq.r11.data);
s.integer(processor.fiq.r12.data);
s.integer(processor.fiq.sp.data);
s.integer(processor.fiq.lr.data);
processor.fiq.spsr.serialize(s);
s.integer(processor.irq.sp.data);
s.integer(processor.irq.lr.data);
processor.irq.spsr.serialize(s);
s.integer(processor.svc.sp.data);
s.integer(processor.svc.lr.data);
processor.svc.spsr.serialize(s);
s.integer(processor.abt.sp.data);
s.integer(processor.abt.lr.data);
processor.abt.spsr.serialize(s);
s.integer(processor.und.sp.data);
s.integer(processor.und.lr.data);
processor.und.spsr.serialize(s);
s.integer(processor.pc);
processor.cpsr.serialize(s);
s.integer(processor.carryout);
s.integer(processor.sequential);
s.integer(processor.irqline);
s.integer(pipeline.reload);
s.integer(pipeline.execute.address);
s.integer(pipeline.execute.instruction);
s.integer(pipeline.decode.address);
s.integer(pipeline.decode.instruction);
s.integer(pipeline.fetch.address);
s.integer(pipeline.fetch.instruction);
s.integer(crash);
}

View File

@ -38,7 +38,7 @@ void Cartridge::load(Mode cartridge_mode, const char *markup) {
if(ram_size > 0) {
ram.map(allocate<uint8>(ram_size, 0xff), ram_size);
nvram.append({ "program.ram", ram.data(), ram.size() });
nvram.append({ "save.ram", ram.data(), ram.size() });
}
rom.write_protect(true);

View File

@ -9,7 +9,7 @@ uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) {
memory += addr & ~3;
return (memory[0] << 0) | (memory[1] << 8) | (memory[2] << 16) | (memory[3] << 24);
return memory[0] << 0 | memory[1] << 8 | memory[2] << 16 | memory[3] << 24;
};
switch(addr & 0xe0000000) {

View File

@ -15,7 +15,7 @@ void SPC7110::init() {
void SPC7110::load() {
for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
if(cartridge.has_spc7110rtc()) cartridge.nvram.append({ "program.rtc", rtc, 20 });
if(cartridge.has_spc7110rtc()) cartridge.nvram.append({ "rtc.ram", rtc, 20 });
}
void SPC7110::unload() {

View File

@ -14,7 +14,7 @@ void SRTC::init() {
void SRTC::load() {
for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
cartridge.nvram.append({ "program.rtc", rtc, 20 });
cartridge.nvram.append({ "rtc.ram", rtc, 20 });
}
void SRTC::unload() {

View File

@ -79,7 +79,7 @@ else ifeq ($(platform),x)
mkdir -p ~/.config/$(name)
cp data/cheats.xml ~/.config/$(name)/cheats.xml
cp -R data/GBA.system ~/.config/$(name)
cp -R "data/Game Boy Advance.system" ~/.config/$(name)
chmod -R 777 ~/.config/$(name)
endif

View File

@ -37,7 +37,7 @@ bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &fil
if(GB::cartridge.ramsize) {
filemap fp;
if(fp.open(interface->base.filename("program.ram", ".sav"), filemap::mode::read)) {
if(fp.open(interface->base.filename("save.ram", ".sav"), filemap::mode::read)) {
memcpy(GB::cartridge.ramdata, fp.data(), min(GB::cartridge.ramsize, fp.size()));
}
}
@ -50,7 +50,7 @@ bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &fil
void InterfaceGB::unloadCartridge() {
if(GB::cartridge.ramsize) {
file::write(interface->base.filename("program.ram", ".sav"), GB::cartridge.ramdata, GB::cartridge.ramsize);
file::write(interface->base.filename("save.ram", ".sav"), GB::cartridge.ramdata, GB::cartridge.ramsize);
}
GB::cartridge.unload();

View File

@ -1,5 +1,5 @@
void InterfaceGBA::initialize() {
string filename = application->path("GBA.system/manifest.xml");
string filename = application->path("Game Boy Advance.system/manifest.xml");
string markup;
markup.readfile(filename);
XML::Document document(markup);
@ -53,13 +53,25 @@ bool InterfaceGBA::loadCartridge(const string &filename) {
GBA::system.power();
delete[] data;
if(GBA::cartridge.ram_size()) {
filemap fp;
if(fp.open(interface->base.filename("save.ram", ".sav"), filemap::mode::read)) {
memcpy(GBA::cartridge.ram_data(), fp.data(), min(GBA::cartridge.ram_size(), fp.size()));
}
}
GBA::video.generate(GBA::Video::Format::RGB30);
interface->loadCartridge(::Interface::Mode::GBA);
return true;
}
void InterfaceGBA::unloadCartridge() {
return GBA::cartridge.unload();
if(GBA::cartridge.ram_size()) {
file::write(interface->base.filename("save.ram", ".sav"), GBA::cartridge.ram_data(), GBA::cartridge.ram_size());
}
GBA::cartridge.unload();
interface->base.name = "";
}
void InterfaceGBA::power() {
@ -75,11 +87,12 @@ void InterfaceGBA::run() {
}
serializer InterfaceGBA::serialize() {
return serializer();
GBA::system.runtosave();
return GBA::system.serialize();
}
bool InterfaceGBA::unserialize(serializer &s) {
return false;
return GBA::system.unserialize(s);
}
void InterfaceGBA::setCheats(const lstring &list) {

View File

@ -62,7 +62,7 @@ bool InterfaceNES::loadCartridge(const string &filename) {
if(NES::cartridge.ram_size()) {
filemap fp;
if(fp.open(interface->base.filename("program.ram", ".sav"), filemap::mode::read)) {
if(fp.open(interface->base.filename("save.ram", ".sav"), filemap::mode::read)) {
memcpy(NES::cartridge.ram_data(), fp.data(), min(NES::cartridge.ram_size(), fp.size()));
}
}
@ -74,7 +74,7 @@ bool InterfaceNES::loadCartridge(const string &filename) {
void InterfaceNES::unloadCartridge() {
if(NES::cartridge.ram_size()) {
file::write(interface->base.filename("program.ram", ".sav"), NES::cartridge.ram_data(), NES::cartridge.ram_size());
file::write(interface->base.filename("save.ram", ".sav"), NES::cartridge.ram_data(), NES::cartridge.ram_size());
}
NES::cartridge.unload();
interface->base.name = "";

View File

@ -225,17 +225,17 @@ void InterfaceSNES::run() {
string InterfaceSNES::memoryName(SNES::Cartridge::NonVolatileRAM &memory) {
if(memory.slot == SNES::Cartridge::Slot::Base) {
if(memory.id == "program.ram") return interface->base.filename("program.ram", ".srm");
if(memory.id == "program.rtc") return interface->base.filename("program.rtc", ".rtc");
if(memory.id == "save.ram") return interface->base.filename("save.ram", ".srm");
if(memory.id == "rtc.ram") return interface->base.filename("rtc.ram", ".rtc");
if(memory.id == "upd96050.ram") return interface->base.filename("upd96050.ram", ".nec");
if(memory.id == "bsx.ram") return interface->base.filename("bsx.ram", ".bss");
if(memory.id == "bsx.psram") return interface->base.filename("bsx.psram", ".bsp");
}
if(memory.slot == SNES::Cartridge::Slot::SufamiTurboA) {
if(memory.id == "program.ram") return interface->slot[0].filename("program.ram", ".sts");
if(memory.id == "save.ram") return interface->slot[0].filename("save.ram", ".sts");
}
if(memory.slot == SNES::Cartridge::Slot::SufamiTurboB) {
if(memory.id == "program.ram") return interface->slot[1].filename("program.ram", ".sts");
if(memory.id == "save.ram") return interface->slot[1].filename("save.ram", ".sts");
}
return "";
}
@ -259,7 +259,7 @@ void InterfaceSNES::loadMemory() {
if(GB::cartridge.ramsize) {
uint8_t *data;
unsigned size;
if(file::read(interface->slot[0].filename("program.ram", ".sav"), data, size)) {
if(file::read(interface->slot[0].filename("save.ram", ".sav"), data, size)) {
memcpy(GB::cartridge.ramdata, data, min(GB::cartridge.ramsize, size));
delete[] data;
}
@ -279,7 +279,7 @@ void InterfaceSNES::saveMemory() {
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
if(GB::cartridge.ramsize) {
file::write(interface->slot[0].filename("program.ram", ".sav"),
file::write(interface->slot[0].filename("save.ram", ".sav"),
GB::cartridge.ramdata, GB::cartridge.ramsize
);
}