From 0cd0dcd811e01cd766c433671fe476e49b99483f Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 16 Apr 2012 22:19:39 +1000 Subject: [PATCH] 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 --- bsnes/base/base.hpp | 2 +- .../manifest.xml | 0 bsnes/gba/apu/apu.cpp | 104 ++++++++------- bsnes/gba/apu/apu.hpp | 2 + bsnes/gba/apu/mmio.cpp | 2 +- bsnes/gba/apu/serialization.cpp | 106 +++++++++++++++ bsnes/gba/cartridge/cartridge.cpp | 102 ++++++++++++--- bsnes/gba/cartridge/cartridge.hpp | 13 +- bsnes/gba/cartridge/eeprom.cpp | 9 +- bsnes/gba/cartridge/flashrom.cpp | 90 +++++++++++++ bsnes/gba/cartridge/memory.hpp | 26 ++++ bsnes/gba/cartridge/serialization.cpp | 5 + bsnes/gba/cpu/cpu.cpp | 87 +++++++++---- bsnes/gba/cpu/cpu.hpp | 16 ++- bsnes/gba/cpu/memory.cpp | 55 ++++++++ bsnes/gba/cpu/registers.cpp | 44 +++---- bsnes/gba/cpu/registers.hpp | 15 +-- bsnes/gba/cpu/serialization.cpp | 77 +++++++++++ bsnes/gba/memory/memory.cpp | 70 +++------- bsnes/gba/memory/memory.hpp | 13 +- bsnes/gba/memory/serialization.cpp | 3 + bsnes/gba/ppu/ppu.cpp | 19 ++- bsnes/gba/ppu/ppu.hpp | 3 +- bsnes/gba/ppu/serialization.cpp | 122 ++++++++++++++++++ bsnes/gba/system/serialization.cpp | 60 +++++++++ bsnes/gba/system/system.cpp | 28 ++++ bsnes/gba/system/system.hpp | 12 ++ bsnes/processor/arm/arm.cpp | 4 +- bsnes/processor/arm/registers.hpp | 2 + bsnes/processor/arm/serialization.cpp | 70 ++++++++++ bsnes/snes/cartridge/cartridge.cpp | 2 +- bsnes/snes/chip/armdsp/memory.cpp | 2 +- bsnes/snes/chip/spc7110/spc7110.cpp | 2 +- bsnes/snes/chip/srtc/srtc.cpp | 2 +- bsnes/target-ui/Makefile | 2 +- bsnes/target-ui/interface/gb/gb.cpp | 4 +- bsnes/target-ui/interface/gba/gba.cpp | 21 ++- bsnes/target-ui/interface/nes/nes.cpp | 4 +- bsnes/target-ui/interface/snes/snes.cpp | 12 +- 39 files changed, 986 insertions(+), 226 deletions(-) rename bsnes/data/{GBA.system => Game Boy Advance.system}/manifest.xml (100%) create mode 100755 bsnes/gba/apu/serialization.cpp create mode 100755 bsnes/gba/cartridge/flashrom.cpp create mode 100755 bsnes/gba/cartridge/serialization.cpp create mode 100755 bsnes/gba/cpu/memory.cpp create mode 100755 bsnes/gba/cpu/serialization.cpp create mode 100755 bsnes/gba/memory/serialization.cpp create mode 100755 bsnes/gba/ppu/serialization.cpp create mode 100755 bsnes/gba/system/serialization.cpp create mode 100755 bsnes/processor/arm/serialization.cpp diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index 2c04c072..6286f9c7 100755 --- a/bsnes/base/base.hpp +++ b/bsnes/base/base.hpp @@ -1,7 +1,7 @@ #ifndef BASE_HPP #define BASE_HPP -static const char Version[] = "087.25"; +static const char Version[] = "087.26"; #include #include diff --git a/bsnes/data/GBA.system/manifest.xml b/bsnes/data/Game Boy Advance.system/manifest.xml similarity index 100% rename from bsnes/data/GBA.system/manifest.xml rename to bsnes/data/Game Boy Advance.system/manifest.xml diff --git a/bsnes/gba/apu/apu.cpp b/bsnes/gba/apu/apu.cpp index 2a597606..54ed33b3 100755 --- a/bsnes/gba/apu/apu.cpp +++ b/bsnes/gba/apu/apu.cpp @@ -11,59 +11,67 @@ 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(); - } - - 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); + for(unsigned n = 0; n < 128; n++) { + 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; + + 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) { diff --git a/bsnes/gba/apu/apu.hpp b/bsnes/gba/apu/apu.hpp index 9074fe70..70c4ddc9 100755 --- a/bsnes/gba/apu/apu.hpp +++ b/bsnes/gba/apu/apu.hpp @@ -10,6 +10,8 @@ struct APU : Thread, MMIO { void power(); void runsequencer(); + + void serialize(serializer&); }; extern APU apu; diff --git a/bsnes/gba/apu/mmio.cpp b/bsnes/gba/apu/mmio.cpp index 104f9a18..39d7a22e 100755 --- a/bsnes/gba/apu/mmio.cpp +++ b/bsnes/gba/apu/mmio.cpp @@ -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 diff --git a/bsnes/gba/apu/serialization.cpp b/bsnes/gba/apu/serialization.cpp new file mode 100755 index 00000000..b2b277a4 --- /dev/null +++ b/bsnes/gba/apu/serialization.cpp @@ -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); + } +} diff --git a/bsnes/gba/cartridge/cartridge.cpp b/bsnes/gba/cartridge/cartridge.cpp index ee693390..8c20a534 100755 --- a/bsnes/gba/cartridge/cartridge.cpp +++ b/bsnes/gba/cartridge/cartridge.cpp @@ -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; } } diff --git a/bsnes/gba/cartridge/cartridge.hpp b/bsnes/gba/cartridge/cartridge.hpp index f771ba7e..8348f01d 100755 --- a/bsnes/gba/cartridge/cartridge.hpp +++ b/bsnes/gba/cartridge/cartridge.hpp @@ -1,20 +1,29 @@ struct Cartridge : property { - StaticMemory rom; - StaticMemory ram; #include "memory.hpp" readonly loaded; readonly sha256; + + readonly has_sram; readonly has_eeprom; + readonly 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; diff --git a/bsnes/gba/cartridge/eeprom.cpp b/bsnes/gba/cartridge/eeprom.cpp index efefbd92..33ffadec 100755 --- a/bsnes/gba/cartridge/eeprom.cpp +++ b/bsnes/gba/cartridge/eeprom.cpp @@ -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; diff --git a/bsnes/gba/cartridge/flashrom.cpp b/bsnes/gba/cartridge/flashrom.cpp new file mode 100755 index 00000000..cbb3408f --- /dev/null +++ b/bsnes/gba/cartridge/flashrom.cpp @@ -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; +} diff --git a/bsnes/gba/cartridge/memory.hpp b/bsnes/gba/cartridge/memory.hpp index 055ed475..3af3f33c 100755 --- a/bsnes/gba/cartridge/memory.hpp +++ b/bsnes/gba/cartridge/memory.hpp @@ -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; diff --git a/bsnes/gba/cartridge/serialization.cpp b/bsnes/gba/cartridge/serialization.cpp new file mode 100755 index 00000000..e1d8106c --- /dev/null +++ b/bsnes/gba/cartridge/serialization.cpp @@ -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); +} diff --git a/bsnes/gba/cpu/cpu.cpp b/bsnes/gba/cpu/cpu.cpp index 3a370207..afdd0108 100755 --- a/bsnes/gba/cpu/cpu.cpp +++ b/bsnes/gba/cpu/cpu.cpp @@ -4,40 +4,65 @@ 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(crash) { - 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); + if(scheduler.sync == Scheduler::SynchronizeMode::CPU) { + scheduler.sync = Scheduler::SynchronizeMode::All; + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); } - processor.irqline = regs.ime && (regs.irq.enable & regs.irq.flag); - dma_run(); + cpu.main(); + } +} - if(regs.mode == Registers::Mode::Halt) { - if((regs.irq.enable & regs.irq.flag) == 0) { - step(16); - continue; - } +void CPU::main() { + #if defined(DEBUG) + if(crash) { + 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; } - - exec(); + return; } + + 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) { 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; } } diff --git a/bsnes/gba/cpu/cpu.hpp b/bsnes/gba/cpu/cpu.hpp index d2c2cc6e..5d0aed33 100755 --- a/bsnes/gba/cpu/cpu.hpp +++ b/bsnes/gba/cpu/cpu.hpp @@ -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; diff --git a/bsnes/gba/cpu/memory.cpp b/bsnes/gba/cpu/memory.cpp new file mode 100755 index 00000000..daf83797 --- /dev/null +++ b/bsnes/gba/cpu/memory.cpp @@ -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; +} diff --git a/bsnes/gba/cpu/registers.cpp b/bsnes/gba/cpu/registers.cpp index 43ce04c2..bf016402 100755 --- a/bsnes/gba/cpu/registers.cpp +++ b/bsnes/gba/cpu/registers.cpp @@ -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(); } diff --git a/bsnes/gba/cpu/registers.hpp b/bsnes/gba/cpu/registers.hpp index 98b5cda8..6c5b31fd 100755 --- a/bsnes/gba/cpu/registers.hpp +++ b/bsnes/gba/cpu/registers.hpp @@ -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); diff --git a/bsnes/gba/cpu/serialization.cpp b/bsnes/gba/cpu/serialization.cpp new file mode 100755 index 00000000..d064771e --- /dev/null +++ b/bsnes/gba/cpu/serialization.cpp @@ -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); +} diff --git a/bsnes/gba/memory/memory.cpp b/bsnes/gba/memory/memory.cpp index b4c02f4e..f4d6ff1f 100755 --- a/bsnes/gba/memory/memory.cpp +++ b/bsnes/gba/memory/memory.cpp @@ -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); diff --git a/bsnes/gba/memory/memory.hpp b/bsnes/gba/memory/memory.hpp index 743b0caa..c4db4b9b 100755 --- a/bsnes/gba/memory/memory.hpp +++ b/bsnes/gba/memory/memory.hpp @@ -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; diff --git a/bsnes/gba/memory/serialization.cpp b/bsnes/gba/memory/serialization.cpp new file mode 100755 index 00000000..fc5418df --- /dev/null +++ b/bsnes/gba/memory/serialization.cpp @@ -0,0 +1,3 @@ +void Bus::serialize(serializer &s) { + s.integer(idleflag); +} diff --git a/bsnes/gba/ppu/ppu.cpp b/bsnes/gba/ppu/ppu.cpp index 12dd1445..dd713d7e 100755 --- a/bsnes/gba/ppu/ppu.cpp +++ b/bsnes/gba/ppu/ppu.cpp @@ -18,16 +18,23 @@ 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) { clock += clocks; if(clock >= 0) co_switch(cpu.thread); @@ -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++) { diff --git a/bsnes/gba/ppu/ppu.hpp b/bsnes/gba/ppu/ppu.hpp index f9597cef..36d40a59 100755 --- a/bsnes/gba/ppu/ppu.hpp +++ b/bsnes/gba/ppu/ppu.hpp @@ -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(); }; diff --git a/bsnes/gba/ppu/serialization.cpp b/bsnes/gba/ppu/serialization.cpp new file mode 100755 index 00000000..577b09f8 --- /dev/null +++ b/bsnes/gba/ppu/serialization.cpp @@ -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); + } +} diff --git a/bsnes/gba/system/serialization.cpp b/bsnes/gba/system/serialization.cpp new file mode 100755 index 00000000..14d2bb16 --- /dev/null +++ b/bsnes/gba/system/serialization.cpp @@ -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(); +} diff --git a/bsnes/gba/system/system.cpp b/bsnes/gba/system/system.cpp index 05bcda01..53e298f8 100755 --- a/bsnes/gba/system/system.cpp +++ b/bsnes/gba/system/system.cpp @@ -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); + } +} + } diff --git a/bsnes/gba/system/system.hpp b/bsnes/gba/system/system.hpp index 6f89488c..01caa09e 100755 --- a/bsnes/gba/system/system.hpp +++ b/bsnes/gba/system/system.hpp @@ -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; diff --git a/bsnes/processor/arm/arm.cpp b/bsnes/processor/arm/arm.cpp index a3734dc3..9621a347 100755 --- a/bsnes/processor/arm/arm.cpp +++ b/bsnes/processor/arm/arm.cpp @@ -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) { -} - } diff --git a/bsnes/processor/arm/registers.hpp b/bsnes/processor/arm/registers.hpp index 6c1fa426..1d83d241 100755 --- a/bsnes/processor/arm/registers.hpp +++ b/bsnes/processor/arm/registers.hpp @@ -44,6 +44,8 @@ struct PSR { m = d & 31; return *this; } + + void serialize(serializer&); }; struct Pipeline { diff --git a/bsnes/processor/arm/serialization.cpp b/bsnes/processor/arm/serialization.cpp new file mode 100755 index 00000000..774f8032 --- /dev/null +++ b/bsnes/processor/arm/serialization.cpp @@ -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); +} diff --git a/bsnes/snes/cartridge/cartridge.cpp b/bsnes/snes/cartridge/cartridge.cpp index d431f145..73456fe5 100755 --- a/bsnes/snes/cartridge/cartridge.cpp +++ b/bsnes/snes/cartridge/cartridge.cpp @@ -38,7 +38,7 @@ void Cartridge::load(Mode cartridge_mode, const char *markup) { if(ram_size > 0) { ram.map(allocate(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); diff --git a/bsnes/snes/chip/armdsp/memory.cpp b/bsnes/snes/chip/armdsp/memory.cpp index 29af732c..a14d55da 100755 --- a/bsnes/snes/chip/armdsp/memory.cpp +++ b/bsnes/snes/chip/armdsp/memory.cpp @@ -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) { diff --git a/bsnes/snes/chip/spc7110/spc7110.cpp b/bsnes/snes/chip/spc7110/spc7110.cpp index 27b8b778..82380430 100755 --- a/bsnes/snes/chip/spc7110/spc7110.cpp +++ b/bsnes/snes/chip/spc7110/spc7110.cpp @@ -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() { diff --git a/bsnes/snes/chip/srtc/srtc.cpp b/bsnes/snes/chip/srtc/srtc.cpp index 0044113e..88f8e06f 100755 --- a/bsnes/snes/chip/srtc/srtc.cpp +++ b/bsnes/snes/chip/srtc/srtc.cpp @@ -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() { diff --git a/bsnes/target-ui/Makefile b/bsnes/target-ui/Makefile index 34fc9ab9..610e7595 100755 --- a/bsnes/target-ui/Makefile +++ b/bsnes/target-ui/Makefile @@ -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 diff --git a/bsnes/target-ui/interface/gb/gb.cpp b/bsnes/target-ui/interface/gb/gb.cpp index a08e4d96..04d4aec1 100755 --- a/bsnes/target-ui/interface/gb/gb.cpp +++ b/bsnes/target-ui/interface/gb/gb.cpp @@ -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(); diff --git a/bsnes/target-ui/interface/gba/gba.cpp b/bsnes/target-ui/interface/gba/gba.cpp index 5f590113..bc202975 100755 --- a/bsnes/target-ui/interface/gba/gba.cpp +++ b/bsnes/target-ui/interface/gba/gba.cpp @@ -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) { diff --git a/bsnes/target-ui/interface/nes/nes.cpp b/bsnes/target-ui/interface/nes/nes.cpp index 5147c9e7..4232add7 100755 --- a/bsnes/target-ui/interface/nes/nes.cpp +++ b/bsnes/target-ui/interface/nes/nes.cpp @@ -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 = ""; diff --git a/bsnes/target-ui/interface/snes/snes.cpp b/bsnes/target-ui/interface/snes/snes.cpp index 36d4d32f..c92fcc03 100755 --- a/bsnes/target-ui/interface/snes/snes.cpp +++ b/bsnes/target-ui/interface/snes/snes.cpp @@ -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 ); }