mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
1c18812f47
commit
0cd0dcd811
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BASE_HPP
|
#ifndef BASE_HPP
|
||||||
#define BASE_HPP
|
#define BASE_HPP
|
||||||
|
|
||||||
static const char Version[] = "087.25";
|
static const char Version[] = "087.26";
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/algorithm.hpp>
|
#include <nall/algorithm.hpp>
|
||||||
|
|
|
@ -11,59 +11,67 @@ namespace GBA {
|
||||||
#include "noise.cpp"
|
#include "noise.cpp"
|
||||||
#include "sequencer.cpp"
|
#include "sequencer.cpp"
|
||||||
#include "fifo.cpp"
|
#include "fifo.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
APU apu;
|
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() {
|
void APU::main() {
|
||||||
while(true) {
|
for(unsigned n = 0; n < 128; n++) {
|
||||||
for(unsigned n = 0; n < 128; n++) {
|
runsequencer();
|
||||||
runsequencer();
|
|
||||||
}
|
|
||||||
|
|
||||||
signed lsample = regs.bias.level - 0x0200;
|
|
||||||
signed rsample = regs.bias.level - 0x0200;
|
|
||||||
|
|
||||||
//(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output
|
|
||||||
if(sequencer.masterenable) {
|
|
||||||
signed lsequence = 0;
|
|
||||||
if(sequencer.lenable[0]) lsequence += square1.output;
|
|
||||||
if(sequencer.lenable[1]) lsequence += square2.output;
|
|
||||||
if(sequencer.lenable[2]) lsequence += wave.output;
|
|
||||||
if(sequencer.lenable[3]) lsequence += noise.output;
|
|
||||||
|
|
||||||
signed rsequence = 0;
|
|
||||||
if(sequencer.renable[0]) rsequence += square1.output;
|
|
||||||
if(sequencer.renable[1]) rsequence += square2.output;
|
|
||||||
if(sequencer.renable[2]) rsequence += wave.output;
|
|
||||||
if(sequencer.renable[3]) rsequence += noise.output;
|
|
||||||
|
|
||||||
if(sequencer.volume < 3) {
|
|
||||||
lsample += lsequence * (sequencer.lvolume + 1) >> (2 - sequencer.volume);
|
|
||||||
rsample += rsequence * (sequencer.rvolume + 1) >> (2 - sequencer.volume);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output
|
|
||||||
signed fifo0 = fifo[0].output + (1 << fifo[0].volume);
|
|
||||||
signed fifo1 = fifo[1].output + (1 << fifo[1].volume);
|
|
||||||
|
|
||||||
if(fifo[0].lenable) lsample += fifo0;
|
|
||||||
if(fifo[1].lenable) lsample += fifo1;
|
|
||||||
|
|
||||||
if(fifo[0].renable) rsample += fifo0;
|
|
||||||
if(fifo[1].renable) rsample += fifo1;
|
|
||||||
|
|
||||||
lsample = sclamp<10>(lsample);
|
|
||||||
rsample = sclamp<10>(rsample);
|
|
||||||
|
|
||||||
if(regs.bias.amplitude == 1) lsample &= ~3, rsample &= ~3;
|
|
||||||
if(regs.bias.amplitude == 2) lsample &= ~7, rsample &= ~7;
|
|
||||||
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
|
|
||||||
|
|
||||||
interface->audioSample(lsample << 5, rsample << 5);
|
|
||||||
step(512);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signed lsample = regs.bias.level - 0x0200;
|
||||||
|
signed rsample = regs.bias.level - 0x0200;
|
||||||
|
|
||||||
|
//(4-bit x 4 -> 6-bit) + 3-bit volume = 9-bit output
|
||||||
|
if(sequencer.masterenable) {
|
||||||
|
signed lsequence = 0;
|
||||||
|
if(sequencer.lenable[0]) lsequence += square1.output;
|
||||||
|
if(sequencer.lenable[1]) lsequence += square2.output;
|
||||||
|
if(sequencer.lenable[2]) lsequence += wave.output;
|
||||||
|
if(sequencer.lenable[3]) lsequence += noise.output;
|
||||||
|
|
||||||
|
signed rsequence = 0;
|
||||||
|
if(sequencer.renable[0]) rsequence += square1.output;
|
||||||
|
if(sequencer.renable[1]) rsequence += square2.output;
|
||||||
|
if(sequencer.renable[2]) rsequence += wave.output;
|
||||||
|
if(sequencer.renable[3]) rsequence += noise.output;
|
||||||
|
|
||||||
|
if(sequencer.volume < 3) {
|
||||||
|
lsample += lsequence * (sequencer.lvolume + 1) >> (2 - sequencer.volume);
|
||||||
|
rsample += rsequence * (sequencer.rvolume + 1) >> (2 - sequencer.volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//(8-bit x 2 -> 7-bit) + 1-bit volume = 10-bit output
|
||||||
|
signed fifo0 = fifo[0].output + (1 << fifo[0].volume);
|
||||||
|
signed fifo1 = fifo[1].output + (1 << fifo[1].volume);
|
||||||
|
|
||||||
|
if(fifo[0].lenable) lsample += fifo0;
|
||||||
|
if(fifo[1].lenable) lsample += fifo1;
|
||||||
|
|
||||||
|
if(fifo[0].renable) rsample += fifo0;
|
||||||
|
if(fifo[1].renable) rsample += fifo1;
|
||||||
|
|
||||||
|
lsample = sclamp<10>(lsample);
|
||||||
|
rsample = sclamp<10>(rsample);
|
||||||
|
|
||||||
|
if(regs.bias.amplitude == 1) lsample &= ~3, rsample &= ~3;
|
||||||
|
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) {
|
void APU::step(unsigned clocks) {
|
||||||
|
|
|
@ -10,6 +10,8 @@ struct APU : Thread, MMIO {
|
||||||
void power();
|
void power();
|
||||||
|
|
||||||
void runsequencer();
|
void runsequencer();
|
||||||
|
|
||||||
|
void serialize(serializer&);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern APU apu;
|
extern APU apu;
|
||||||
|
|
|
@ -151,7 +151,7 @@ void APU::write(uint32 addr, uint8 byte) {
|
||||||
fifo[1].renable = byte >> 4;
|
fifo[1].renable = byte >> 4;
|
||||||
fifo[1].lenable = byte >> 5;
|
fifo[1].lenable = byte >> 5;
|
||||||
fifo[1].timer = byte >> 6;
|
fifo[1].timer = byte >> 6;
|
||||||
if(byte & 1 << 7) fifo[0].reset();
|
if(byte & 1 << 7) fifo[1].reset();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//NR52
|
//NR52
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,55 +3,119 @@
|
||||||
namespace GBA {
|
namespace GBA {
|
||||||
|
|
||||||
#include "eeprom.cpp"
|
#include "eeprom.cpp"
|
||||||
|
#include "flashrom.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
Cartridge cartridge;
|
Cartridge cartridge;
|
||||||
|
|
||||||
bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
|
bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
|
||||||
if(rom.data) delete[] rom.data;
|
XML::Document document(markup);
|
||||||
rom.data = new uint8[rom.size = 32 * 1024 * 1024];
|
|
||||||
for(unsigned addr = 0; addr < 32 * 1024 * 1024; addr++) {
|
for(unsigned addr = 0; addr < rom.size; addr++) {
|
||||||
rom.data[addr] = data[Bus::mirror(addr, size)];
|
rom.data[addr] = data[Bus::mirror(addr, size)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ram.data) delete[] ram.data;
|
has_sram = false;
|
||||||
ram.data = new uint8[ram.size = 64 * 1024]();
|
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);
|
sha256 = nall::sha256(rom.data, rom.size);
|
||||||
|
|
||||||
|
system.load();
|
||||||
return loaded = true;
|
return loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::unload() {
|
void Cartridge::unload() {
|
||||||
if(loaded) return;
|
if(loaded) return;
|
||||||
loaded = false;
|
loaded = false;
|
||||||
|
|
||||||
delete[] rom.data;
|
|
||||||
rom.data = nullptr;
|
|
||||||
rom.size = 0u;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::power() {
|
void Cartridge::power() {
|
||||||
eeprom.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) {
|
uint32 Cartridge::read(uint32 addr, uint32 size) {
|
||||||
if(has_eeprom && (addr & 0x0f000000) == 0x0d000000) return eeprom.read();
|
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((addr & 0x0e000000) == 0x08000000) return rom.read(addr & 0x01ffffff, size);
|
if(has_flashrom && (addr & 0x0f000000 ) == 0x0e000000 ) return flashrom.read(addr);
|
||||||
if((addr & 0x0e000000) == 0x0a000000) return rom.read(addr & 0x01ffffff, size);
|
if(addr < 0x0e000000) return read(rom.data, addr & 0x01ffffff, size);
|
||||||
if((addr & 0x0e000000) == 0x0c000000) return rom.read(addr & 0x01ffffff, size);
|
return cpu.pipeline.fetch.instruction;
|
||||||
if((addr & 0x0e000000) == 0x0e000000) return ram.read(addr & 0x0000ffff, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::write(uint32 addr, uint32 size, uint32 word) {
|
void Cartridge::write(uint32 addr, uint32 size, uint32 word) {
|
||||||
if((addr & 0x0f000000) == 0x0d000000) { has_eeprom = true; return eeprom.write(word & 1); }
|
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((addr & 0x0e000000) == 0x0e000000) return ram.write(addr & 0x0000ffff, size, word);
|
if(has_flashrom && (addr & 0x0f000000 ) == 0x0e000000 ) return flashrom.write(addr, word);
|
||||||
}
|
}
|
||||||
|
|
||||||
Cartridge::Cartridge() {
|
Cartridge::Cartridge() {
|
||||||
loaded = false;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,29 @@
|
||||||
struct Cartridge : property<Cartridge> {
|
struct Cartridge : property<Cartridge> {
|
||||||
StaticMemory rom;
|
|
||||||
StaticMemory ram;
|
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
|
||||||
readonly<bool> loaded;
|
readonly<bool> loaded;
|
||||||
readonly<string> sha256;
|
readonly<string> sha256;
|
||||||
|
|
||||||
|
readonly<bool> has_sram;
|
||||||
readonly<bool> has_eeprom;
|
readonly<bool> has_eeprom;
|
||||||
|
readonly<bool> has_flashrom;
|
||||||
|
|
||||||
bool load(const string &markup, const uint8_t *data, unsigned size);
|
bool load(const string &markup, const uint8_t *data, unsigned size);
|
||||||
void unload();
|
void unload();
|
||||||
void power();
|
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);
|
uint32 read(uint32 addr, uint32 size);
|
||||||
void write(uint32 addr, uint32 size, uint32 word);
|
void write(uint32 addr, uint32 size, uint32 word);
|
||||||
|
|
||||||
|
void serialize(serializer&);
|
||||||
Cartridge();
|
Cartridge();
|
||||||
|
~Cartridge();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Cartridge cartridge;
|
extern Cartridge cartridge;
|
||||||
|
|
|
@ -23,7 +23,7 @@ void Cartridge::EEPROM::write(bool bit) {
|
||||||
|
|
||||||
else if(mode == Mode::ReadAddress) {
|
else if(mode == Mode::ReadAddress) {
|
||||||
address = (address << 1) | bit;
|
address = (address << 1) | bit;
|
||||||
if(++offset == size) {
|
if(++offset == bits) {
|
||||||
mode = Mode::ReadValidate;
|
mode = Mode::ReadValidate;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ void Cartridge::EEPROM::write(bool bit) {
|
||||||
|
|
||||||
else if(mode == Mode::WriteAddress) {
|
else if(mode == Mode::WriteAddress) {
|
||||||
address = (address << 1) | bit;
|
address = (address << 1) | bit;
|
||||||
if(++offset == size) {
|
if(++offset == bits) {
|
||||||
mode = Mode::WriteData;
|
mode = Mode::WriteData;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
}
|
}
|
||||||
|
@ -56,9 +56,8 @@ void Cartridge::EEPROM::write(bool bit) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::EEPROM::power() {
|
void Cartridge::EEPROM::power() {
|
||||||
data.resize(64 * 1024);
|
data.resize(size);
|
||||||
data.clear();
|
bits = (size <= 512 ? 6 : 14);
|
||||||
size = 6;
|
|
||||||
|
|
||||||
mode = Mode::Wait;
|
mode = Mode::Wait;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -1,6 +1,14 @@
|
||||||
|
struct Memory {
|
||||||
|
uint8 *data;
|
||||||
|
unsigned size;
|
||||||
|
} rom, ram;
|
||||||
|
|
||||||
struct EEPROM {
|
struct EEPROM {
|
||||||
bitarray data;
|
bitarray data;
|
||||||
unsigned size;
|
unsigned size;
|
||||||
|
unsigned mask;
|
||||||
|
unsigned test;
|
||||||
|
unsigned bits;
|
||||||
|
|
||||||
enum class Mode : unsigned { Wait, Command, ReadAddress, ReadValidate, ReadData, WriteAddress, WriteData, WriteValidate } mode;
|
enum class Mode : unsigned { Wait, Command, ReadAddress, ReadValidate, ReadData, WriteAddress, WriteData, WriteValidate } mode;
|
||||||
unsigned offset;
|
unsigned offset;
|
||||||
|
@ -10,3 +18,21 @@ struct EEPROM {
|
||||||
void write(bool bit);
|
void write(bool bit);
|
||||||
void power();
|
void power();
|
||||||
} eeprom;
|
} 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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -4,40 +4,65 @@ namespace GBA {
|
||||||
|
|
||||||
#include "registers.cpp"
|
#include "registers.cpp"
|
||||||
#include "mmio.cpp"
|
#include "mmio.cpp"
|
||||||
|
#include "memory.cpp"
|
||||||
#include "dma.cpp"
|
#include "dma.cpp"
|
||||||
#include "timer.cpp"
|
#include "timer.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
CPU cpu;
|
CPU cpu;
|
||||||
|
|
||||||
void CPU::Enter() { cpu.enter(); }
|
void CPU::Enter() {
|
||||||
|
|
||||||
void CPU::enter() {
|
|
||||||
while(true) {
|
while(true) {
|
||||||
if(crash) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
|
||||||
print(cpsr().t ? disassemble_thumb_instruction(pipeline.execute.address)
|
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||||
: disassemble_arm_instruction(pipeline.execute.address), "\n");
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
print(disassemble_registers(), "\n");
|
|
||||||
print("Executed: ", instructions, "\n");
|
|
||||||
while(true) step(frequency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processor.irqline = regs.ime && (regs.irq.enable & regs.irq.flag);
|
cpu.main();
|
||||||
dma_run();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(regs.mode == Registers::Mode::Halt) {
|
void CPU::main() {
|
||||||
if((regs.irq.enable & regs.irq.flag) == 0) {
|
#if defined(DEBUG)
|
||||||
step(16);
|
if(crash) {
|
||||||
continue;
|
print(cpsr().t ? disassemble_thumb_instruction(pipeline.execute.address)
|
||||||
}
|
: disassemble_arm_instruction(pipeline.execute.address), "\n");
|
||||||
|
print(disassemble_registers(), "\n");
|
||||||
|
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;
|
regs.mode = Registers::Mode::Normal;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
exec();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dma_run();
|
||||||
|
|
||||||
|
if(regs.mode == Registers::Mode::Halt) {
|
||||||
|
if((regs.irq.enable & regs.irq.flag) == 0) {
|
||||||
|
step(16);
|
||||||
|
} else {
|
||||||
|
regs.mode = Registers::Mode::Normal;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::step(unsigned clocks) {
|
void CPU::step(unsigned clocks) {
|
||||||
timer_step(clocks);
|
timer_step(clocks);
|
||||||
|
sync_step(clocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPU::sync_step(unsigned clocks) {
|
||||||
ppu.clock -= clocks;
|
ppu.clock -= clocks;
|
||||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
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);
|
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() {
|
void CPU::power() {
|
||||||
create(CPU::Enter, 16777216);
|
create(CPU::Enter, 16777216);
|
||||||
|
|
||||||
ARM::power();
|
ARM::power();
|
||||||
for(unsigned n = 0; n < iwram.size; n++) iwram.data[n] = 0;
|
for(unsigned n = 0; n < 32 * 1024; n++) iwram[n] = 0;
|
||||||
for(unsigned n = 0; n < ewram.size; n++) ewram.data[n] = 0;
|
for(unsigned n = 0; n < 256 * 1024; n++) ewram[n] = 0;
|
||||||
|
|
||||||
for(auto &dma : regs.dma) {
|
for(auto &dma : regs.dma) {
|
||||||
dma.source = 0;
|
dma.source = 0;
|
||||||
|
@ -101,8 +139,13 @@ void CPU::power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
CPU::CPU() {
|
CPU::CPU() {
|
||||||
iwram.data = new uint8[iwram.size = 32 * 1024];
|
iwram = new uint8[ 32 * 1024];
|
||||||
ewram.data = new uint8[ewram.size = 256 * 1024];
|
ewram = new uint8[256 * 1024];
|
||||||
|
}
|
||||||
|
|
||||||
|
CPU::~CPU() {
|
||||||
|
delete[] iwram;
|
||||||
|
delete[] ewram;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,39 @@
|
||||||
struct CPU : Processor::ARM, Thread, MMIO {
|
struct CPU : Processor::ARM, Thread, MMIO {
|
||||||
StaticMemory iwram;
|
uint8 *iwram;
|
||||||
StaticMemory ewram;
|
uint8 *ewram;
|
||||||
#include "registers.hpp"
|
#include "registers.hpp"
|
||||||
#include "state.hpp"
|
#include "state.hpp"
|
||||||
|
|
||||||
static void Enter();
|
static void Enter();
|
||||||
void enter();
|
void main();
|
||||||
void step(unsigned clocks);
|
void step(unsigned clocks);
|
||||||
|
void sync_step(unsigned clocks);
|
||||||
|
|
||||||
void bus_idle(uint32 addr);
|
void bus_idle(uint32 addr);
|
||||||
uint32 bus_read(uint32 addr, uint32 size);
|
uint32 bus_read(uint32 addr, uint32 size);
|
||||||
void bus_write(uint32 addr, uint32 size, uint32 word);
|
void bus_write(uint32 addr, uint32 size, uint32 word);
|
||||||
|
|
||||||
|
void keypad_run();
|
||||||
void power();
|
void power();
|
||||||
|
|
||||||
uint8 read(uint32 addr);
|
uint8 read(uint32 addr);
|
||||||
void write(uint32 addr, uint8 byte);
|
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_run();
|
||||||
void dma_transfer(Registers::DMA &dma);
|
void dma_transfer(Registers::DMA &dma);
|
||||||
|
|
||||||
void timer_step(unsigned clocks);
|
void timer_step(unsigned clocks);
|
||||||
void timer_increment(unsigned n);
|
void timer_increment(unsigned n);
|
||||||
|
|
||||||
|
void serialize(serializer&);
|
||||||
CPU();
|
CPU();
|
||||||
|
~CPU();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CPU cpu;
|
extern CPU cpu;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -42,34 +42,34 @@ uint8 CPU::Registers::TimerControl::operator=(uint8 source) {
|
||||||
|
|
||||||
CPU::Registers::KeypadControl::operator uint16() const {
|
CPU::Registers::KeypadControl::operator uint16() const {
|
||||||
return (
|
return (
|
||||||
(a << 0)
|
(flag[0] << 0)
|
||||||
| (b << 1)
|
| (flag[1] << 1)
|
||||||
| (select << 2)
|
| (flag[2] << 2)
|
||||||
| (start << 3)
|
| (flag[3] << 3)
|
||||||
| (right << 4)
|
| (flag[4] << 4)
|
||||||
| (left << 5)
|
| (flag[5] << 5)
|
||||||
| (up << 6)
|
| (flag[6] << 6)
|
||||||
| (down << 7)
|
| (flag[7] << 7)
|
||||||
| (r << 8)
|
| (flag[8] << 8)
|
||||||
| (l << 9)
|
| (flag[9] << 9)
|
||||||
| (enable << 14)
|
| (enable << 14)
|
||||||
| (condition << 15)
|
| (condition << 15)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 CPU::Registers::KeypadControl::operator=(uint16 source) {
|
uint16 CPU::Registers::KeypadControl::operator=(uint16 source) {
|
||||||
a = (source >> 0) & 1;
|
flag[0] = source >> 0;
|
||||||
b = (source >> 1) & 1;
|
flag[1] = source >> 1;
|
||||||
select = (source >> 2) & 1;
|
flag[2] = source >> 2;
|
||||||
start = (source >> 3) & 1;
|
flag[3] = source >> 3;
|
||||||
right = (source >> 4) & 1;
|
flag[4] = source >> 4;
|
||||||
left = (source >> 5) & 1;
|
flag[5] = source >> 5;
|
||||||
up = (source >> 6) & 1;
|
flag[6] = source >> 6;
|
||||||
down = (source >> 7) & 1;
|
flag[7] = source >> 7;
|
||||||
r = (source >> 8) & 1;
|
flag[8] = source >> 8;
|
||||||
l = (source >> 9) & 1;
|
flag[9] = source >> 9;
|
||||||
enable = (source >> 14) & 1;
|
enable = source >> 14;
|
||||||
condition = (source >> 15) & 1;
|
condition = source >> 15;
|
||||||
return operator uint16();
|
return operator uint16();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,18 +46,9 @@ struct Registers {
|
||||||
} timer[4];
|
} timer[4];
|
||||||
|
|
||||||
struct KeypadControl {
|
struct KeypadControl {
|
||||||
bool a;
|
uint1 flag[10];
|
||||||
bool b;
|
uint1 enable;
|
||||||
bool select;
|
uint1 condition;
|
||||||
bool start;
|
|
||||||
bool right;
|
|
||||||
bool left;
|
|
||||||
bool up;
|
|
||||||
bool down;
|
|
||||||
bool r;
|
|
||||||
bool l;
|
|
||||||
bool enable;
|
|
||||||
bool condition;
|
|
||||||
|
|
||||||
operator uint16() const;
|
operator uint16() const;
|
||||||
uint16 operator=(uint16 source);
|
uint16 operator=(uint16 source);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
namespace GBA {
|
namespace GBA {
|
||||||
|
|
||||||
#include "mmio.cpp"
|
#include "mmio.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
Bus bus;
|
Bus bus;
|
||||||
|
|
||||||
struct UnmappedMemory : Memory {
|
struct UnmappedMemory : Memory {
|
||||||
|
@ -12,49 +13,6 @@ struct UnmappedMemory : Memory {
|
||||||
|
|
||||||
static UnmappedMemory unmappedMemory;
|
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 Bus::mirror(uint32 addr, uint32 size) {
|
||||||
uint32 base = 0;
|
uint32 base = 0;
|
||||||
if(size) {
|
if(size) {
|
||||||
|
@ -78,12 +36,20 @@ uint32 Bus::speed(uint32 addr, uint32 size) {
|
||||||
static unsigned timing[] = { 5, 4, 3, 9 };
|
static unsigned timing[] = { 5, 4, 3, 9 };
|
||||||
unsigned n = cpu.regs.wait.control.nwait[addr >> 25 & 3];
|
unsigned n = cpu.regs.wait.control.nwait[addr >> 25 & 3];
|
||||||
unsigned s = cpu.regs.wait.control.swait[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();
|
bool sequential = cpu.sequential();
|
||||||
if((addr & 0xffff << 1) == 0) 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;
|
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;
|
if(size == Word) n += s;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +57,7 @@ uint32 Bus::speed(uint32 addr, uint32 size) {
|
||||||
switch(addr >> 24 & 7) {
|
switch(addr >> 24 & 7) {
|
||||||
case 0: return 1;
|
case 0: return 1;
|
||||||
case 1: 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 3: return 1;
|
||||||
case 4: return 1;
|
case 4: return 1;
|
||||||
case 5: return 1 << (size == Word);
|
case 5: return 1 << (size == Word);
|
||||||
|
@ -111,12 +77,12 @@ uint32 Bus::read(uint32 addr, uint32 size) {
|
||||||
switch(addr >> 24 & 7) {
|
switch(addr >> 24 & 7) {
|
||||||
case 0: return bios.read(addr, size);
|
case 0: return bios.read(addr, size);
|
||||||
case 1: return bios.read(addr, size);
|
case 1: return bios.read(addr, size);
|
||||||
case 2: return cpu.ewram.read(addr & 0x3ffff, size);
|
case 2: return cpu.ewram_read(addr, size);
|
||||||
case 3: return cpu.iwram.read(addr & 0x7fff, size);
|
case 3: return cpu.iwram_read(addr, size);
|
||||||
case 4:
|
case 4:
|
||||||
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->read(addr, size);
|
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->read(addr, size);
|
||||||
if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).read(0x04000800 | (addr & 3), 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 5: return ppu.pram_read(addr, size);
|
||||||
case 6: return ppu.vram_read(addr, size);
|
case 6: return ppu.vram_read(addr, size);
|
||||||
case 7: return ppu.oam_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) {
|
switch(addr >> 24 & 7) {
|
||||||
case 0: return;
|
case 0: return;
|
||||||
case 1: return;
|
case 1: return;
|
||||||
case 2: return cpu.ewram.write(addr & 0x3ffff, size, word);
|
case 2: return cpu.ewram_write(addr, size, word);
|
||||||
case 3: return cpu.iwram.write(addr & 0x7fff, size, word);
|
case 3: return cpu.iwram_write(addr, size, word);
|
||||||
case 4:
|
case 4:
|
||||||
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->write(addr, size, word);
|
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);
|
if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).write(0x04000800 | (addr & 3), size, word);
|
||||||
|
|
|
@ -3,17 +3,6 @@ struct Memory {
|
||||||
virtual void write(uint32 addr, uint32 size, uint32 word) = 0;
|
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 {
|
struct MMIO : Memory {
|
||||||
virtual uint8 read(uint32 addr) = 0;
|
virtual uint8 read(uint32 addr) = 0;
|
||||||
virtual void write(uint32 addr, uint8 data) = 0;
|
virtual void write(uint32 addr, uint8 data) = 0;
|
||||||
|
@ -31,6 +20,8 @@ struct Bus : Memory {
|
||||||
uint32 read(uint32 addr, uint32 size);
|
uint32 read(uint32 addr, uint32 size);
|
||||||
void write(uint32 addr, uint32 size, uint32 word);
|
void write(uint32 addr, uint32 size, uint32 word);
|
||||||
void power();
|
void power();
|
||||||
|
|
||||||
|
void serialize(serializer&);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Bus bus;
|
extern Bus bus;
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
void Bus::serialize(serializer &s) {
|
||||||
|
s.integer(idleflag);
|
||||||
|
}
|
|
@ -18,16 +18,23 @@ namespace GBA {
|
||||||
#include "screen.cpp"
|
#include "screen.cpp"
|
||||||
#include "mmio.cpp"
|
#include "mmio.cpp"
|
||||||
#include "memory.cpp"
|
#include "memory.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
PPU ppu;
|
PPU ppu;
|
||||||
|
|
||||||
void PPU::Enter() { ppu.enter(); }
|
void PPU::Enter() {
|
||||||
|
|
||||||
void PPU::enter() {
|
|
||||||
while(true) {
|
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) {
|
void PPU::step(unsigned clocks) {
|
||||||
clock += clocks;
|
clock += clocks;
|
||||||
if(clock >= 0) co_switch(cpu.thread);
|
if(clock >= 0) co_switch(cpu.thread);
|
||||||
|
@ -80,6 +87,8 @@ void PPU::power() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::scanline() {
|
void PPU::scanline() {
|
||||||
|
cpu.keypad_run();
|
||||||
|
|
||||||
regs.status.vblank = regs.vcounter >= 160 && regs.vcounter <= 226;
|
regs.status.vblank = regs.vcounter >= 160 && regs.vcounter <= 226;
|
||||||
regs.status.vcoincidence = regs.vcounter == regs.status.vcompare;
|
regs.status.vcoincidence = regs.vcounter == regs.status.vcompare;
|
||||||
|
|
||||||
|
@ -103,7 +112,7 @@ void PPU::scanline() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(regs.vcounter < 160) {
|
if(regs.vcounter < 160) {
|
||||||
if(regs.control.forceblank) {
|
if(regs.control.forceblank || cpu.regs.mode == CPU::Registers::Mode::Stop) {
|
||||||
render_forceblank();
|
render_forceblank();
|
||||||
} else {
|
} else {
|
||||||
for(unsigned x = 0; x < 240; x++) {
|
for(unsigned x = 0; x < 240; x++) {
|
||||||
|
|
|
@ -7,7 +7,7 @@ struct PPU : Thread, MMIO {
|
||||||
uint16 *blur;
|
uint16 *blur;
|
||||||
|
|
||||||
static void Enter();
|
static void Enter();
|
||||||
void enter();
|
void main();
|
||||||
void step(unsigned clocks);
|
void step(unsigned clocks);
|
||||||
|
|
||||||
void power();
|
void power();
|
||||||
|
@ -40,6 +40,7 @@ struct PPU : Thread, MMIO {
|
||||||
void render_window(unsigned window);
|
void render_window(unsigned window);
|
||||||
unsigned blend(unsigned above, unsigned eva, unsigned below, unsigned evb);
|
unsigned blend(unsigned above, unsigned eva, unsigned below, unsigned evb);
|
||||||
|
|
||||||
|
void serialize(serializer&);
|
||||||
PPU();
|
PPU();
|
||||||
~PPU();
|
~PPU();
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
namespace GBA {
|
namespace GBA {
|
||||||
|
|
||||||
#include "bios.cpp"
|
#include "bios.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
BIOS bios;
|
BIOS bios;
|
||||||
System system;
|
System system;
|
||||||
|
|
||||||
|
@ -21,8 +22,35 @@ void System::power() {
|
||||||
scheduler.power();
|
scheduler.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::load() {
|
||||||
|
serialize_init();
|
||||||
|
}
|
||||||
|
|
||||||
void System::run() {
|
void System::run() {
|
||||||
scheduler.enter();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,20 @@ struct BIOS : Memory {
|
||||||
struct System {
|
struct System {
|
||||||
void init();
|
void init();
|
||||||
void term();
|
void term();
|
||||||
|
void load();
|
||||||
void power();
|
void power();
|
||||||
void run();
|
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;
|
extern BIOS bios;
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace Processor {
|
||||||
#include "instructions-arm.cpp"
|
#include "instructions-arm.cpp"
|
||||||
#include "instructions-thumb.cpp"
|
#include "instructions-thumb.cpp"
|
||||||
#include "disassembler.cpp"
|
#include "disassembler.cpp"
|
||||||
|
#include "serialization.cpp"
|
||||||
|
|
||||||
void ARM::power() {
|
void ARM::power() {
|
||||||
processor.power();
|
processor.power();
|
||||||
|
@ -76,7 +77,4 @@ void ARM::vector(uint32 addr, Processor::Mode mode) {
|
||||||
r(15) = addr;
|
r(15) = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARM::serialize(serializer &s) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ struct PSR {
|
||||||
m = d & 31;
|
m = d & 31;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serialize(serializer&);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Pipeline {
|
struct Pipeline {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ void Cartridge::load(Mode cartridge_mode, const char *markup) {
|
||||||
|
|
||||||
if(ram_size > 0) {
|
if(ram_size > 0) {
|
||||||
ram.map(allocate<uint8>(ram_size, 0xff), ram_size);
|
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);
|
rom.write_protect(true);
|
||||||
|
|
|
@ -9,7 +9,7 @@ uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
|
||||||
|
|
||||||
static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) {
|
static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) {
|
||||||
memory += addr & ~3;
|
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) {
|
switch(addr & 0xe0000000) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ void SPC7110::init() {
|
||||||
|
|
||||||
void SPC7110::load() {
|
void SPC7110::load() {
|
||||||
for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
|
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() {
|
void SPC7110::unload() {
|
||||||
|
|
|
@ -14,7 +14,7 @@ void SRTC::init() {
|
||||||
|
|
||||||
void SRTC::load() {
|
void SRTC::load() {
|
||||||
for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff;
|
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() {
|
void SRTC::unload() {
|
||||||
|
|
|
@ -79,7 +79,7 @@ else ifeq ($(platform),x)
|
||||||
|
|
||||||
mkdir -p ~/.config/$(name)
|
mkdir -p ~/.config/$(name)
|
||||||
cp data/cheats.xml ~/.config/$(name)/cheats.xml
|
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)
|
chmod -R 777 ~/.config/$(name)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &fil
|
||||||
|
|
||||||
if(GB::cartridge.ramsize) {
|
if(GB::cartridge.ramsize) {
|
||||||
filemap fp;
|
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()));
|
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() {
|
void InterfaceGB::unloadCartridge() {
|
||||||
if(GB::cartridge.ramsize) {
|
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();
|
GB::cartridge.unload();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
void InterfaceGBA::initialize() {
|
void InterfaceGBA::initialize() {
|
||||||
string filename = application->path("GBA.system/manifest.xml");
|
string filename = application->path("Game Boy Advance.system/manifest.xml");
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile(filename);
|
markup.readfile(filename);
|
||||||
XML::Document document(markup);
|
XML::Document document(markup);
|
||||||
|
@ -53,13 +53,25 @@ bool InterfaceGBA::loadCartridge(const string &filename) {
|
||||||
GBA::system.power();
|
GBA::system.power();
|
||||||
delete[] data;
|
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);
|
GBA::video.generate(GBA::Video::Format::RGB30);
|
||||||
interface->loadCartridge(::Interface::Mode::GBA);
|
interface->loadCartridge(::Interface::Mode::GBA);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterfaceGBA::unloadCartridge() {
|
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() {
|
void InterfaceGBA::power() {
|
||||||
|
@ -75,11 +87,12 @@ void InterfaceGBA::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer InterfaceGBA::serialize() {
|
serializer InterfaceGBA::serialize() {
|
||||||
return serializer();
|
GBA::system.runtosave();
|
||||||
|
return GBA::system.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceGBA::unserialize(serializer &s) {
|
bool InterfaceGBA::unserialize(serializer &s) {
|
||||||
return false;
|
return GBA::system.unserialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterfaceGBA::setCheats(const lstring &list) {
|
void InterfaceGBA::setCheats(const lstring &list) {
|
||||||
|
|
|
@ -62,7 +62,7 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
||||||
|
|
||||||
if(NES::cartridge.ram_size()) {
|
if(NES::cartridge.ram_size()) {
|
||||||
filemap fp;
|
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()));
|
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() {
|
void InterfaceNES::unloadCartridge() {
|
||||||
if(NES::cartridge.ram_size()) {
|
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();
|
NES::cartridge.unload();
|
||||||
interface->base.name = "";
|
interface->base.name = "";
|
||||||
|
|
|
@ -225,17 +225,17 @@ void InterfaceSNES::run() {
|
||||||
|
|
||||||
string InterfaceSNES::memoryName(SNES::Cartridge::NonVolatileRAM &memory) {
|
string InterfaceSNES::memoryName(SNES::Cartridge::NonVolatileRAM &memory) {
|
||||||
if(memory.slot == SNES::Cartridge::Slot::Base) {
|
if(memory.slot == SNES::Cartridge::Slot::Base) {
|
||||||
if(memory.id == "program.ram") return interface->base.filename("program.ram", ".srm");
|
if(memory.id == "save.ram") return interface->base.filename("save.ram", ".srm");
|
||||||
if(memory.id == "program.rtc") return interface->base.filename("program.rtc", ".rtc");
|
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 == "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.ram") return interface->base.filename("bsx.ram", ".bss");
|
||||||
if(memory.id == "bsx.psram") return interface->base.filename("bsx.psram", ".bsp");
|
if(memory.id == "bsx.psram") return interface->base.filename("bsx.psram", ".bsp");
|
||||||
}
|
}
|
||||||
if(memory.slot == SNES::Cartridge::Slot::SufamiTurboA) {
|
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.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 "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,7 @@ void InterfaceSNES::loadMemory() {
|
||||||
if(GB::cartridge.ramsize) {
|
if(GB::cartridge.ramsize) {
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
unsigned size;
|
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));
|
memcpy(GB::cartridge.ramdata, data, min(GB::cartridge.ramsize, size));
|
||||||
delete[] data;
|
delete[] data;
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ void InterfaceSNES::saveMemory() {
|
||||||
|
|
||||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
||||||
if(GB::cartridge.ramsize) {
|
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
|
GB::cartridge.ramdata, GB::cartridge.ramsize
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue