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
|
||||
#define BASE_HPP
|
||||
|
||||
static const char Version[] = "087.25";
|
||||
static const char Version[] = "087.26";
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/algorithm.hpp>
|
||||
|
|
|
@ -11,12 +11,20 @@ namespace GBA {
|
|||
#include "noise.cpp"
|
||||
#include "sequencer.cpp"
|
||||
#include "fifo.cpp"
|
||||
#include "serialization.cpp"
|
||||
APU apu;
|
||||
|
||||
void APU::Enter() { apu.main(); }
|
||||
void APU::Enter() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
apu.main();
|
||||
}
|
||||
}
|
||||
|
||||
void APU::main() {
|
||||
while(true) {
|
||||
for(unsigned n = 0; n < 128; n++) {
|
||||
runsequencer();
|
||||
}
|
||||
|
@ -61,9 +69,9 @@ void APU::main() {
|
|||
if(regs.bias.amplitude == 2) lsample &= ~7, rsample &= ~7;
|
||||
if(regs.bias.amplitude == 3) lsample &= ~15, rsample &= ~15;
|
||||
|
||||
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
|
||||
interface->audioSample(lsample << 5, rsample << 5);
|
||||
step(512);
|
||||
}
|
||||
}
|
||||
|
||||
void APU::step(unsigned clocks) {
|
||||
|
|
|
@ -10,6 +10,8 @@ struct APU : Thread, MMIO {
|
|||
void power();
|
||||
|
||||
void runsequencer();
|
||||
|
||||
void serialize(serializer&);
|
||||
};
|
||||
|
||||
extern APU apu;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
struct Cartridge : property<Cartridge> {
|
||||
StaticMemory rom;
|
||||
StaticMemory ram;
|
||||
#include "memory.hpp"
|
||||
|
||||
readonly<bool> loaded;
|
||||
readonly<string> sha256;
|
||||
|
||||
readonly<bool> has_sram;
|
||||
readonly<bool> has_eeprom;
|
||||
readonly<bool> has_flashrom;
|
||||
|
||||
bool load(const string &markup, const uint8_t *data, unsigned size);
|
||||
void unload();
|
||||
void power();
|
||||
|
||||
uint8* ram_data();
|
||||
unsigned ram_size();
|
||||
|
||||
uint32 read(uint8 *data, uint32 addr, uint32 size);
|
||||
void write(uint8 *data, uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
uint32 read(uint32 addr, uint32 size);
|
||||
void write(uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
void serialize(serializer&);
|
||||
Cartridge();
|
||||
~Cartridge();
|
||||
};
|
||||
|
||||
extern Cartridge cartridge;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
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;
|
||||
|
|
|
@ -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,14 +4,25 @@ namespace GBA {
|
|||
|
||||
#include "registers.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "dma.cpp"
|
||||
#include "timer.cpp"
|
||||
#include "serialization.cpp"
|
||||
CPU cpu;
|
||||
|
||||
void CPU::Enter() { cpu.enter(); }
|
||||
|
||||
void CPU::enter() {
|
||||
void CPU::Enter() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::All;
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
cpu.main();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::main() {
|
||||
#if defined(DEBUG)
|
||||
if(crash) {
|
||||
print(cpsr().t ? disassemble_thumb_instruction(pipeline.execute.address)
|
||||
: disassemble_arm_instruction(pipeline.execute.address), "\n");
|
||||
|
@ -19,25 +30,39 @@ void CPU::enter() {
|
|||
print("Executed: ", instructions, "\n");
|
||||
while(true) step(frequency);
|
||||
}
|
||||
#endif
|
||||
|
||||
processor.irqline = regs.ime && (regs.irq.enable & regs.irq.flag);
|
||||
|
||||
if(regs.mode == Registers::Mode::Stop) {
|
||||
if((regs.irq.enable.keypad & regs.irq.flag.keypad) == 0) {
|
||||
sync_step(16); //STOP does not advance timers
|
||||
} else {
|
||||
regs.mode = Registers::Mode::Normal;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dma_run();
|
||||
|
||||
if(regs.mode == Registers::Mode::Halt) {
|
||||
if((regs.irq.enable & regs.irq.flag) == 0) {
|
||||
step(16);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
regs.mode = Registers::Mode::Normal;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
exec();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::step(unsigned clocks) {
|
||||
timer_step(clocks);
|
||||
sync_step(clocks);
|
||||
}
|
||||
|
||||
void CPU::sync_step(unsigned clocks) {
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
|
@ -60,12 +85,25 @@ void CPU::bus_write(uint32 addr, uint32 size, uint32 word) {
|
|||
return bus.write(addr, size, word);
|
||||
}
|
||||
|
||||
void CPU::keypad_run() {
|
||||
if(regs.keypad.control.enable == false) return;
|
||||
|
||||
bool test = regs.keypad.control.condition; //0 = OR, 1 = AND
|
||||
for(unsigned n = 0; n < 10; n++) {
|
||||
if(regs.keypad.control.flag[n] == false) continue;
|
||||
bool input = interface->inputPoll(n);
|
||||
if(regs.keypad.control.condition == 0) test |= input;
|
||||
if(regs.keypad.control.condition == 1) test &= input;
|
||||
}
|
||||
if(test) regs.irq.flag.keypad = true;
|
||||
}
|
||||
|
||||
void CPU::power() {
|
||||
create(CPU::Enter, 16777216);
|
||||
|
||||
ARM::power();
|
||||
for(unsigned n = 0; n < iwram.size; n++) iwram.data[n] = 0;
|
||||
for(unsigned n = 0; n < ewram.size; n++) ewram.data[n] = 0;
|
||||
for(unsigned n = 0; n < 32 * 1024; n++) iwram[n] = 0;
|
||||
for(unsigned n = 0; n < 256 * 1024; n++) ewram[n] = 0;
|
||||
|
||||
for(auto &dma : regs.dma) {
|
||||
dma.source = 0;
|
||||
|
@ -101,8 +139,13 @@ void CPU::power() {
|
|||
}
|
||||
|
||||
CPU::CPU() {
|
||||
iwram.data = new uint8[iwram.size = 32 * 1024];
|
||||
ewram.data = new uint8[ewram.size = 256 * 1024];
|
||||
iwram = new uint8[ 32 * 1024];
|
||||
ewram = new uint8[256 * 1024];
|
||||
}
|
||||
|
||||
CPU::~CPU() {
|
||||
delete[] iwram;
|
||||
delete[] ewram;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
||||
#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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
void Bus::serialize(serializer &s) {
|
||||
s.integer(idleflag);
|
||||
}
|
|
@ -18,14 +18,21 @@ namespace GBA {
|
|||
#include "screen.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "serialization.cpp"
|
||||
PPU ppu;
|
||||
|
||||
void PPU::Enter() { ppu.enter(); }
|
||||
|
||||
void PPU::enter() {
|
||||
void PPU::Enter() {
|
||||
while(true) {
|
||||
scanline();
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
ppu.main();
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::main() {
|
||||
scanline();
|
||||
}
|
||||
|
||||
void PPU::step(unsigned clocks) {
|
||||
|
@ -80,6 +87,8 @@ void PPU::power() {
|
|||
}
|
||||
|
||||
void PPU::scanline() {
|
||||
cpu.keypad_run();
|
||||
|
||||
regs.status.vblank = regs.vcounter >= 160 && regs.vcounter <= 226;
|
||||
regs.status.vcoincidence = regs.vcounter == regs.status.vcompare;
|
||||
|
||||
|
@ -103,7 +112,7 @@ void PPU::scanline() {
|
|||
}
|
||||
|
||||
if(regs.vcounter < 160) {
|
||||
if(regs.control.forceblank) {
|
||||
if(regs.control.forceblank || cpu.regs.mode == CPU::Registers::Mode::Stop) {
|
||||
render_forceblank();
|
||||
} else {
|
||||
for(unsigned x = 0; x < 240; x++) {
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ struct PSR {
|
|||
m = d & 31;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void serialize(serializer&);
|
||||
};
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 = "";
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue