mirror of https://github.com/bsnes-emu/bsnes.git
Update to v094r35 release.
byuu says: GBA timings are *almost* perfect now. Off by 1-3 cycles on each test, sans a few DMA ones that seem to not run at all according to the numbers (crazy.)
This commit is contained in:
parent
7ff7f64482
commit
28a14198cb
|
@ -8,7 +8,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "094.34";
|
||||
static const string Version = "094.35";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -96,66 +96,56 @@ unsigned Cartridge::ram_size() {
|
|||
if(has_sram) return ram.size;
|
||||
if(has_eeprom) return eeprom.size;
|
||||
if(has_flashrom) return flashrom.size;
|
||||
return 0u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 Cartridge::read(uint8 *data, uint32 addr, uint32 size) {
|
||||
if(size == Word) addr &= ~3;
|
||||
if(size == Half) addr &= ~1;
|
||||
auto Cartridge::read(uint8 *data, unsigned mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) addr &= ~3;
|
||||
if(mode & 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];
|
||||
if(mode & Word) return data[0] << 0 | data[1] << 8 | data[2] << 16 | data[3] << 24;
|
||||
if(mode & Half) return data[0] << 0 | data[1] << 8;
|
||||
if(mode & Byte) return data[0];
|
||||
return 0; //should never occur
|
||||
}
|
||||
|
||||
void Cartridge::write(uint8 *data, uint32 addr, uint32 size, uint32 word) {
|
||||
if(size == Word) addr &= ~3;
|
||||
if(size == Half) addr &= ~1;
|
||||
auto Cartridge::write(uint8 *data, unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) addr &= ~3;
|
||||
if(mode & 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;
|
||||
if(mode & Word) {
|
||||
data[0] = word >> 0;
|
||||
data[1] = word >> 8;
|
||||
data[2] = word >> 16;
|
||||
data[3] = word >> 24;
|
||||
} else if(mode & Half) {
|
||||
data[0] = word >> 0;
|
||||
data[1] = word >> 8;
|
||||
} else if(mode & Byte) {
|
||||
data[0] = word >> 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define RAM_ANALYZE
|
||||
|
||||
uint32 Cartridge::read(uint32 addr, uint32 size) {
|
||||
#ifdef RAM_ANALYZE
|
||||
if((addr & 0x0e000000) == 0x0e000000) {
|
||||
static bool once = true;
|
||||
if(once) once = false, print("* SRAM/FlashROM read detected\n");
|
||||
}
|
||||
#endif
|
||||
auto Cartridge::rom_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.read();
|
||||
return read(rom.data, mode, addr & 0x01ff'ffff);
|
||||
}
|
||||
|
||||
if(has_sram && (addr & 0x0e000000 ) == 0x0e000000 ) return read(ram.data, addr & ram.mask, size);
|
||||
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.read();
|
||||
if(has_flashrom && (addr & 0x0e000000 ) == 0x0e000000 ) return flashrom.read(addr);
|
||||
if(addr < 0x0e000000) return read(rom.data, addr & 0x01ffffff, size);
|
||||
auto Cartridge::rom_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.write(word & 1);
|
||||
}
|
||||
|
||||
auto Cartridge::ram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
if(has_sram) return read(ram.data, mode, addr & ram.mask);
|
||||
if(has_flashrom) return flashrom.read(addr);
|
||||
return cpu.pipeline.fetch.instruction;
|
||||
}
|
||||
|
||||
void Cartridge::write(uint32 addr, uint32 size, uint32 word) {
|
||||
#ifdef RAM_ANALYZE
|
||||
if((addr & 0x0e000000) == 0x0e000000) {
|
||||
static bool once = true;
|
||||
if(once) once = false, print("* SRAM/FlashROM write detected\n");
|
||||
}
|
||||
if((addr & 0x0f000000) == 0x0d000000) {
|
||||
static bool once = true;
|
||||
if(once) once = false, print("* EEPROM write detected\n");
|
||||
}
|
||||
if((addr & 0x0e00ffff) == 0x0e005555 && (word & 0xff) == 0xaa) {
|
||||
static bool once = true;
|
||||
if(once) once = false, print("* FlashROM write detected\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
if(has_sram && (addr & 0x0e000000 ) == 0x0e000000 ) return write(ram.data, addr & ram.mask, size, word);
|
||||
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.write(word & 1);
|
||||
if(has_flashrom && (addr & 0x0e000000 ) == 0x0e000000 ) return flashrom.write(addr, word);
|
||||
auto Cartridge::ram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
if(has_sram) return write(ram.data, mode, addr & ram.mask, word);
|
||||
if(has_flashrom) return flashrom.write(addr, word);
|
||||
}
|
||||
|
||||
Cartridge::Cartridge() {
|
||||
|
|
|
@ -28,13 +28,17 @@ struct Cartridge : property<Cartridge> {
|
|||
uint8* ram_data();
|
||||
unsigned ram_size();
|
||||
|
||||
uint32 read(uint8* data, uint32 addr, uint32 size);
|
||||
void write(uint8* data, uint32 addr, uint32 size, uint32 word);
|
||||
auto read(uint8* data, unsigned mode, uint32 addr) -> uint32;
|
||||
auto write(uint8* data, unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
uint32 read(uint32 addr, uint32 size);
|
||||
void write(uint32 addr, uint32 size, uint32 word);
|
||||
auto rom_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto rom_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto ram_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto ram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
Cartridge();
|
||||
~Cartridge();
|
||||
};
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
auto CPU::bus_idle() -> void {
|
||||
prefetch_step(1);
|
||||
}
|
||||
|
||||
auto CPU::bus_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
addr &= 0x0fff'ffff;
|
||||
unsigned wait = bus_wait(mode, addr);
|
||||
unsigned word = pipeline.fetch.instruction;
|
||||
|
||||
if(addr & 0x0800'0000) {
|
||||
if(mode & Prefetch && regs.wait.control.prefetch) {
|
||||
prefetch_sync(addr);
|
||||
word = prefetch_read();
|
||||
if(mode & Word) word |= prefetch_read() << 16;
|
||||
} else {
|
||||
if(!active.dma) prefetch_wait();
|
||||
step(wait - 1);
|
||||
word = addr < 0x0e00'0000 ? cartridge.rom_read(mode, addr) : cartridge.ram_read(mode, addr);
|
||||
step(1);
|
||||
}
|
||||
} else {
|
||||
prefetch_step(wait - 1);
|
||||
if(addr < 0x0200'0000) word = bios.read(mode, addr);
|
||||
else if(addr < 0x0300'0000) word = ewram_read(mode, addr);
|
||||
else if(addr < 0x0400'0000) word = iwram_read(mode, addr);
|
||||
else if(addr >= 0x0700'0000) word = ppu.oam_read(mode, addr);
|
||||
else if(addr >= 0x0600'0000) word = ppu.vram_read(mode, addr);
|
||||
else if(addr >= 0x0500'0000) word = ppu.pram_read(mode, addr);
|
||||
else if((addr & 0xffff'fc00) == 0x0400'0000) word = bus.mmio[addr & 0x3ff]->read(mode, addr);
|
||||
else if((addr & 0xff00'ffff) == 0x0400'0800) word = ((MMIO*)this)->read(mode, 0x0400'0800 | (addr & 3));
|
||||
prefetch_step(1);
|
||||
}
|
||||
|
||||
return word;
|
||||
}
|
||||
|
||||
auto CPU::bus_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
addr &= 0x0fff'ffff;
|
||||
unsigned wait = bus_wait(mode, addr);
|
||||
|
||||
if(addr & 0x0800'0000) {
|
||||
if(!active.dma) prefetch_wait();
|
||||
step(wait);
|
||||
addr < 0x0e00'0000 ? cartridge.rom_write(mode, addr, word) : cartridge.ram_write(mode, addr, word);
|
||||
} else {
|
||||
prefetch_step(wait);
|
||||
if(addr < 0x0200'0000);
|
||||
else if(addr < 0x0300'0000) ewram_write(mode, addr, word);
|
||||
else if(addr < 0x0400'0000) iwram_write(mode, addr, word);
|
||||
else if(addr >= 0x0700'0000) ppu.oam_write(mode, addr, word);
|
||||
else if(addr >= 0x0600'0000) ppu.vram_write(mode, addr, word);
|
||||
else if(addr >= 0x0500'0000) ppu.pram_write(mode, addr, word);
|
||||
else if((addr & 0xffff'fc00) == 0x0400'0000) bus.mmio[addr & 0x3ff]->write(mode, addr, word);
|
||||
else if((addr & 0xff00'ffff) == 0x0400'0800) ((MMIO*)this)->write(mode, 0x0400'0800 | (addr & 3), word);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::bus_wait(unsigned mode, uint32 addr) -> unsigned {
|
||||
if(addr < 0x0200'0000) return 1;
|
||||
if(addr < 0x0300'0000) return (16 - regs.memory.control.ewramwait) * (mode & Word ? 2 : 1);
|
||||
if(addr < 0x0500'0000) return 1;
|
||||
if(addr < 0x0700'0000) return mode & Word ? 2 : 1;
|
||||
if(addr < 0x0800'0000) return 1;
|
||||
|
||||
static unsigned timings[] = {5, 4, 3, 9};
|
||||
unsigned n = timings[regs.wait.control.nwait[addr >> 25 & 3]];
|
||||
unsigned s = regs.wait.control.swait[addr >> 25 & 3];
|
||||
|
||||
switch(addr & 0x0e00'0000) {
|
||||
case 0x0800'0000: s = s ? 2 : 3; break;
|
||||
case 0x0a00'0000: s = s ? 2 : 5; break;
|
||||
case 0x0c00'0000: s = s ? 2 : 9; break;
|
||||
case 0x0e00'0000: s = n; break;
|
||||
}
|
||||
|
||||
bool sequential = (mode & Sequential);
|
||||
if((addr & 0x1fffe) == 0) sequential = false; //N cycle on 16-bit ROM crossing 128KB page boundary (RAM S==N)
|
||||
|
||||
unsigned clocks = sequential ? s : n;
|
||||
if(mode & Word) clocks += s; //16-bit bus requires two transfers for words
|
||||
return clocks;
|
||||
}
|
|
@ -4,6 +4,7 @@ namespace GameBoyAdvance {
|
|||
|
||||
#include "registers.cpp"
|
||||
#include "prefetch.cpp"
|
||||
#include "bus.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "dma.cpp"
|
||||
|
@ -63,72 +64,6 @@ auto CPU::step(unsigned clocks) -> void {
|
|||
sync_step(clocks);
|
||||
}
|
||||
|
||||
auto CPU::bus_idle(uint32 addr) -> void {
|
||||
step(1);
|
||||
prefetch_step(1);
|
||||
}
|
||||
|
||||
auto CPU::bus_read(uint32 addr, uint32 size, bool mode) -> uint32 {
|
||||
unsigned wait = bus.wait(addr, size, mode);
|
||||
|
||||
if(addr < 0x0800'0000) {
|
||||
unsigned word = bus.read(addr, size);
|
||||
step(wait);
|
||||
prefetch_step(wait);
|
||||
return word;
|
||||
}
|
||||
|
||||
if(addr < 0x0e00'0000) {
|
||||
if(regs.wait.control.prefetch) {
|
||||
if(mode == Nonsequential) prefetch_start(addr);
|
||||
unsigned word = prefetch_take();
|
||||
if(size == Byte) word = (addr & 1) ? (word >> 8) : (word & 0xff);
|
||||
if(size == Word) word |= prefetch_take() << 16;
|
||||
return word;
|
||||
}
|
||||
|
||||
unsigned word = cartridge.read(addr, size);
|
||||
step(wait);
|
||||
return word;
|
||||
}
|
||||
|
||||
if(addr < 0x1000'0000) {
|
||||
prefetch_stall();
|
||||
unsigned word = bus.read(addr, size);
|
||||
step(wait);
|
||||
return word;
|
||||
}
|
||||
|
||||
step(wait);
|
||||
prefetch_step(wait);
|
||||
return 0x0000'0000; //open bus?
|
||||
}
|
||||
|
||||
auto CPU::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
|
||||
unsigned wait = bus.wait(addr, size, mode);
|
||||
|
||||
if(addr < 0x0800'0000) {
|
||||
step(wait);
|
||||
prefetch_step(wait);
|
||||
return bus.write(addr, size, word);
|
||||
}
|
||||
|
||||
if(addr < 0x0e00'0000) {
|
||||
prefetch_stall();
|
||||
step(wait);
|
||||
return bus.write(addr, size, word);
|
||||
}
|
||||
|
||||
if(addr < 0x1000'0000) {
|
||||
prefetch_stall();
|
||||
step(wait);
|
||||
return bus.write(addr, size, word);
|
||||
}
|
||||
|
||||
step(wait);
|
||||
prefetch_step(wait);
|
||||
}
|
||||
|
||||
auto CPU::sync_step(unsigned clocks) -> void {
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
@ -187,6 +122,8 @@ auto CPU::power() -> void {
|
|||
pending.dma.hblank = 0;
|
||||
pending.dma.hdma = 0;
|
||||
|
||||
active.dma = false;
|
||||
|
||||
for(unsigned n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA
|
||||
for(unsigned n = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers
|
||||
for(unsigned n = 0x120; n <= 0x12b; n++) bus.mmio[n] = this; //Serial
|
||||
|
|
|
@ -14,9 +14,6 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||
auto main() -> void;
|
||||
|
||||
auto step(unsigned clocks) -> void override;
|
||||
auto bus_idle(uint32 addr) -> void override;
|
||||
auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32 override;
|
||||
auto bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void override;
|
||||
|
||||
auto sync_step(unsigned clocks) -> void;
|
||||
auto keypad_run() -> void;
|
||||
|
@ -25,15 +22,21 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||
CPU();
|
||||
~CPU();
|
||||
|
||||
//bus.cpp
|
||||
auto bus_idle() -> void override;
|
||||
auto bus_read(unsigned mode, uint32 addr) -> uint32 override;
|
||||
auto bus_write(unsigned mode, uint32 addr, uint32 word) -> void override;
|
||||
auto bus_wait(unsigned mode, uint32 addr) -> unsigned;
|
||||
|
||||
//mmio.cpp
|
||||
auto read(uint32 addr) -> uint8;
|
||||
auto write(uint32 addr, uint8 byte) -> void;
|
||||
|
||||
auto iwram_read(uint32 addr, uint32 size) -> uint32;
|
||||
auto iwram_write(uint32 addr, uint32 size, uint32 word) -> void;
|
||||
auto iwram_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto iwram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
auto ewram_read(uint32 addr, uint32 size) -> uint32;
|
||||
auto ewram_write(uint32 addr, uint32 size, uint32 word) -> void;
|
||||
auto ewram_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto ewram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
//dma.cpp
|
||||
auto dma_run() -> void;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
auto CPU::dma_run() -> void {
|
||||
active.dma = true;
|
||||
|
||||
while(true) {
|
||||
bool transferred = false;
|
||||
for(auto n : range(4)) {
|
||||
|
@ -13,20 +15,26 @@ auto CPU::dma_run() -> void {
|
|||
}
|
||||
if(!transferred) break;
|
||||
}
|
||||
|
||||
active.dma = false;
|
||||
}
|
||||
|
||||
auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
||||
unsigned size = dma.control.size ? Word : Half;
|
||||
unsigned mode = dma.run.length == dma.length ? Nonsequential : Sequential;
|
||||
unsigned seek = dma.control.size ? 4 : 2;
|
||||
unsigned mode = dma.control.size ? Word : Half;
|
||||
mode |= dma.run.length == dma.length ? Nonsequential : Sequential;
|
||||
|
||||
if(mode == Nonsequential) {
|
||||
idle();
|
||||
idle();
|
||||
if(mode & Nonsequential) {
|
||||
if((dma.source & 0x0800'0000) && (dma.target & 0x0800'0000)) {
|
||||
//ROM -> ROM transfer
|
||||
} else {
|
||||
idle();
|
||||
idle();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 word = bus_read(dma.run.source, size, mode);
|
||||
bus_write(dma.run.target, size, mode, word);
|
||||
uint32 word = bus_read(mode, dma.run.source);
|
||||
bus_write(mode, dma.run.target, word);
|
||||
|
||||
switch(dma.control.sourcemode) {
|
||||
case 0: dma.run.source += seek; break;
|
||||
|
|
|
@ -1,53 +1,53 @@
|
|||
auto CPU::iwram_read(uint32 addr, uint32 size) -> uint32 {
|
||||
auto CPU::iwram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
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;
|
||||
if(mode & Word) return iwram_read(Half, addr &~ 2) << 0 | iwram_read(Half, addr | 2) << 16;
|
||||
if(mode & Half) return iwram_read(Byte, addr &~ 1) << 0 | iwram_read(Byte, addr | 1) << 8;
|
||||
|
||||
return iwram[addr & 0x7fff];
|
||||
}
|
||||
|
||||
auto CPU::iwram_write(uint32 addr, uint32 size, uint32 word) -> void {
|
||||
auto CPU::iwram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
if(regs.memory.control.disable) return;
|
||||
|
||||
if(size == Word) {
|
||||
iwram_write(addr &~2, Half, word >> 0);
|
||||
iwram_write(addr | 2, Half, word >> 16);
|
||||
if(mode & Word) {
|
||||
iwram_write(Half, addr &~2, word >> 0);
|
||||
iwram_write(Half, addr | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(size == Half) {
|
||||
iwram_write(addr &~1, Byte, word >> 0);
|
||||
iwram_write(addr | 1, Byte, word >> 8);
|
||||
if(mode & Half) {
|
||||
iwram_write(Byte, addr &~1, word >> 0);
|
||||
iwram_write(Byte, addr | 1, word >> 8);
|
||||
return;
|
||||
}
|
||||
|
||||
iwram[addr & 0x7fff] = word;
|
||||
}
|
||||
|
||||
auto CPU::ewram_read(uint32 addr, uint32 size) -> uint32 {
|
||||
auto CPU::ewram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
|
||||
if(regs.memory.control.ewram == false) return iwram_read(addr, size);
|
||||
if(!regs.memory.control.ewram) return iwram_read(mode, addr);
|
||||
|
||||
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;
|
||||
if(mode & Word) return ewram_read(Half, addr &~ 2) << 0 | ewram_read(Half, addr | 2) << 16;
|
||||
if(mode & Half) return ewram_read(Byte, addr &~ 1) << 0 | ewram_read(Byte, addr | 1) << 8;
|
||||
|
||||
return ewram[addr & 0x3ffff];
|
||||
}
|
||||
|
||||
auto CPU::ewram_write(uint32 addr, uint32 size, uint32 word) -> void {
|
||||
auto CPU::ewram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
if(regs.memory.control.disable) return;
|
||||
if(regs.memory.control.ewram == false) return iwram_write(addr, size, word);
|
||||
if(!regs.memory.control.ewram) return iwram_write(mode, addr, word);
|
||||
|
||||
if(size == Word) {
|
||||
ewram_write(addr &~2, Half, word >> 0);
|
||||
ewram_write(addr | 2, Half, word >> 16);
|
||||
if(mode & Word) {
|
||||
ewram_write(Half, addr &~2, word >> 0);
|
||||
ewram_write(Half, addr | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(size == Half) {
|
||||
ewram_write(addr &~1, Byte, word >> 0);
|
||||
ewram_write(addr | 1, Byte, word >> 8);
|
||||
if(mode & Half) {
|
||||
ewram_write(Byte, addr &~1, word >> 0);
|
||||
ewram_write(Byte, addr | 1, word >> 8);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,43 +1,37 @@
|
|||
auto CPU::prefetch_stall() -> void {
|
||||
prefetch.stalled = true;
|
||||
}
|
||||
auto CPU::prefetch_sync(uint32 addr) -> void {
|
||||
if(addr == prefetch.addr) return;
|
||||
|
||||
auto CPU::prefetch_start(uint32 addr) -> void {
|
||||
prefetch.stalled = false;
|
||||
prefetch.slots = 0;
|
||||
prefetch.input = 0;
|
||||
prefetch.output = 0;
|
||||
prefetch.addr = addr;
|
||||
prefetch.wait = bus.wait(addr, Half, Nonsequential);
|
||||
prefetch.addr = addr;
|
||||
prefetch.load = addr;
|
||||
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
|
||||
}
|
||||
|
||||
auto CPU::prefetch_step(unsigned clocks) -> void {
|
||||
if(!regs.wait.control.prefetch || prefetch.stalled) return;
|
||||
step(clocks);
|
||||
if(!regs.wait.control.prefetch || active.dma) return;
|
||||
|
||||
prefetch.wait -= clocks;
|
||||
while(prefetch.wait <= 0) {
|
||||
if(prefetch.slots < 8) {
|
||||
prefetch.slot[prefetch.output++] = cartridge.read(prefetch.addr, Half);
|
||||
prefetch.slots++;
|
||||
prefetch.addr += 2;
|
||||
}
|
||||
prefetch.wait += bus.wait(prefetch.addr, Half, Sequential);
|
||||
while(!prefetch.full() && prefetch.wait <= 0) {
|
||||
prefetch.slot[prefetch.load >> 1 & 7] = cartridge.rom_read(Half, prefetch.load);
|
||||
prefetch.load += 2;
|
||||
prefetch.wait += bus_wait(Half | Sequential, prefetch.load);
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::prefetch_wait() -> void {
|
||||
step(prefetch.wait);
|
||||
if(!regs.wait.control.prefetch || prefetch.full()) return;
|
||||
|
||||
prefetch_step(prefetch.wait);
|
||||
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
|
||||
}
|
||||
|
||||
auto CPU::prefetch_take() -> uint16 {
|
||||
if(prefetch.slots) {
|
||||
step(1);
|
||||
prefetch_step(1);
|
||||
} else {
|
||||
prefetch_wait();
|
||||
}
|
||||
auto CPU::prefetch_read() -> uint16 {
|
||||
if(prefetch.empty()) prefetch_step(prefetch.wait);
|
||||
else prefetch_step(1);
|
||||
|
||||
prefetch.slots--;
|
||||
return prefetch.slot[prefetch.input++];
|
||||
if(prefetch.full()) prefetch.wait = bus_wait(Half | Sequential, prefetch.load);
|
||||
|
||||
uint16 half = prefetch.slot[prefetch.addr >> 1 & 7];
|
||||
prefetch.addr += 2;
|
||||
return half;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
struct Prefetch {
|
||||
struct {
|
||||
uint16 slot[8] = {0};
|
||||
uint3 input = 0;
|
||||
uint3 output = 0;
|
||||
uint32 addr = 0; //read location of slot buffer
|
||||
uint32 load = 0; //write location of slot buffer
|
||||
signed wait = 0; //number of clocks before next slot load
|
||||
|
||||
bool stalled = true;
|
||||
unsigned slots = 0;
|
||||
signed wait = 0;
|
||||
uint32 addr = 0;
|
||||
auto empty() const { return addr == load; }
|
||||
auto full() const { return load - addr == 16; }
|
||||
} prefetch;
|
||||
|
||||
auto prefetch_stall() -> void;
|
||||
auto prefetch_start(uint32 addr) -> void;
|
||||
auto prefetch_sync(uint32 addr) -> void;
|
||||
auto prefetch_step(unsigned clocks) -> void;
|
||||
auto prefetch_wait() -> void;
|
||||
auto prefetch_take() -> uint16;
|
||||
auto prefetch_read() -> uint16;
|
||||
|
|
|
@ -221,9 +221,6 @@ auto CPU::Registers::WaitControl::operator=(uint16 source) -> uint16 {
|
|||
prefetch = (source >> 14) & 1;
|
||||
gametype = (source >> 15) & 1;
|
||||
swait[3] = nwait[3];
|
||||
|
||||
cpu.prefetch.stalled = true;
|
||||
|
||||
return operator uint16();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,3 +5,7 @@ struct Pending {
|
|||
bool hdma;
|
||||
} dma;
|
||||
} pending;
|
||||
|
||||
struct Active {
|
||||
bool dma;
|
||||
} active;
|
||||
|
|
12
gba/gba.hpp
12
gba/gba.hpp
|
@ -21,8 +21,16 @@ namespace GameBoyAdvance {
|
|||
#include <libco/libco.h>
|
||||
|
||||
namespace GameBoyAdvance {
|
||||
enum : unsigned { Byte = 8, Half = 16, Word = 32 };
|
||||
enum : bool { Nonsequential = 0, Sequential = 1 };
|
||||
enum : unsigned { //mode flags for bus_read, bus_write:
|
||||
Nonsequential = 1, //N cycle
|
||||
Sequential = 2, //S cycle
|
||||
Prefetch = 4, //instruction fetch (eligible for prefetch)
|
||||
Byte = 8, //8-bit access
|
||||
Half = 16, //16-bit access
|
||||
Word = 32, //32-bit access
|
||||
Load = 64, //load operation
|
||||
Store = 128, //store operation
|
||||
};
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
namespace GameBoyAdvance {
|
||||
|
||||
#include "mmio.cpp"
|
||||
#include "serialization.cpp"
|
||||
Bus bus;
|
||||
|
||||
struct UnmappedMemory : Memory {
|
||||
auto read(uint32 addr, uint32 size) -> uint32 { return 0; }
|
||||
auto write(uint32 addr, uint32 size, uint32 word) -> void {}
|
||||
auto read(unsigned mode, uint32 addr) -> uint32 override { return 0; }
|
||||
auto write(unsigned mode, uint32 addr, uint32 word) -> void override {}
|
||||
};
|
||||
|
||||
static UnmappedMemory unmappedMemory;
|
||||
|
@ -31,74 +30,6 @@ auto Bus::mirror(uint32 addr, uint32 size) -> uint32 {
|
|||
return base;
|
||||
}
|
||||
|
||||
auto Bus::wait(uint32 addr, uint32 size, bool mode) -> unsigned {
|
||||
switch(addr & 0x0f000000) {
|
||||
case 0x00000000: return 1;
|
||||
case 0x01000000: return 1;
|
||||
case 0x02000000: return (16 - cpu.regs.memory.control.ewramwait) * (size == Word ? 2 : 1);
|
||||
case 0x03000000: return 1;
|
||||
case 0x04000000: return 1;
|
||||
case 0x05000000: return 1 + (size == Word);
|
||||
case 0x06000000: return 1 + (size == Word);
|
||||
case 0x07000000: return 1;
|
||||
default: {
|
||||
unsigned n = cpu.regs.wait.control.nwait[addr >> 25 & 3];
|
||||
unsigned s = cpu.regs.wait.control.swait[addr >> 25 & 3];
|
||||
|
||||
static unsigned timing[] = {5, 4, 3, 9};
|
||||
n = timing[n];
|
||||
|
||||
switch(addr & 0x0e000000) {
|
||||
case 0x08000000: s = s ? 2 : 3; break;
|
||||
case 0x0a000000: s = s ? 2 : 5; break;
|
||||
case 0x0c000000: s = s ? 2 : 9; break;
|
||||
case 0x0e000000: s = n; break;
|
||||
}
|
||||
|
||||
bool sequential = (mode == Sequential);
|
||||
if((addr & 0x1fffe) == 0) sequential = false; //N cycle on 16-bit ROM crossing 128KB page boundary (RAM S==N)
|
||||
|
||||
unsigned clocks = sequential ? s : n;
|
||||
if(size == Word) clocks += s; //16-bit bus requires two transfers for words
|
||||
return clocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Bus::read(uint32 addr, uint32 size) -> uint32 {
|
||||
switch(addr & 0x0f000000) {
|
||||
case 0x00000000: return bios.read(addr, size);
|
||||
case 0x01000000: return bios.read(addr, size);
|
||||
case 0x02000000: return cpu.ewram_read(addr, size);
|
||||
case 0x03000000: return cpu.iwram_read(addr, size);
|
||||
case 0x4000000:
|
||||
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->read(addr, size);
|
||||
if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).read(0x04000800 | (addr & 3), size);
|
||||
return cpu.pipeline.fetch.instruction;
|
||||
case 0x05000000: return ppu.pram_read(addr, size);
|
||||
case 0x06000000: return ppu.vram_read(addr, size);
|
||||
case 0x07000000: return ppu.oam_read(addr, size);
|
||||
default: return cartridge.read(addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
auto Bus::write(uint32 addr, uint32 size, uint32 word) -> void {
|
||||
switch(addr & 0x0f000000) {
|
||||
case 0x00000000: return;
|
||||
case 0x01000000: return;
|
||||
case 0x02000000: return cpu.ewram_write(addr, size, word);
|
||||
case 0x03000000: return cpu.iwram_write(addr, size, word);
|
||||
case 0x04000000:
|
||||
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);
|
||||
return;
|
||||
case 0x05000000: return ppu.pram_write(addr, size, word);
|
||||
case 0x06000000: return ppu.vram_write(addr, size, word);
|
||||
case 0x07000000: return ppu.oam_write(addr, size, word);
|
||||
default: return cartridge.write(addr, size, word);
|
||||
}
|
||||
}
|
||||
|
||||
auto Bus::power() -> void {
|
||||
for(auto n : range(0x400)) mmio[n] = &unmappedMemory;
|
||||
}
|
||||
|
|
|
@ -1,26 +1,21 @@
|
|||
struct Memory {
|
||||
virtual auto read(uint32 addr, uint32 size) -> uint32 = 0;
|
||||
virtual auto write(uint32 addr, uint32 size, uint32 word) -> void = 0;
|
||||
virtual auto read(unsigned mode, uint32 addr) -> uint32 = 0;
|
||||
virtual auto write(unsigned mode, uint32 addr, uint32 word) -> void = 0;
|
||||
};
|
||||
|
||||
struct MMIO : Memory {
|
||||
virtual auto read(uint32 addr) -> uint8 = 0;
|
||||
virtual auto write(uint32 addr, uint8 data) -> void = 0;
|
||||
auto read(uint32 addr, uint32 size) -> uint32;
|
||||
auto write(uint32 addr, uint32 size, uint32 word) -> void;
|
||||
auto read(unsigned mode, uint32 addr) -> uint32 final;
|
||||
auto write(unsigned mode, uint32 addr, uint32 word) -> void final;
|
||||
};
|
||||
|
||||
struct Bus : Memory {
|
||||
struct Bus {
|
||||
static auto mirror(uint32 addr, uint32 size) -> uint32;
|
||||
|
||||
auto wait(uint32 addr, uint32 size, bool mode) -> unsigned;
|
||||
auto read(uint32 addr, uint32 size) -> uint32;
|
||||
auto write(uint32 addr, uint32 size, uint32 word) -> void;
|
||||
auto power() -> void;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
Memory* mmio[0x400]{nullptr};
|
||||
Memory* mmio[0x400] = {nullptr};
|
||||
};
|
||||
|
||||
extern Bus bus;
|
||||
|
|
|
@ -1,43 +1,35 @@
|
|||
auto MMIO::read(uint32 addr, uint32 size) -> uint32 {
|
||||
auto MMIO::read(unsigned mode, uint32 addr) -> uint32 {
|
||||
uint32 word = 0;
|
||||
|
||||
switch(size) {
|
||||
case Word:
|
||||
if(mode & Word) {
|
||||
addr &= ~3;
|
||||
word |= read(addr + 0) << 0;
|
||||
word |= read(addr + 1) << 8;
|
||||
word |= read(addr + 2) << 16;
|
||||
word |= read(addr + 3) << 24;
|
||||
break;
|
||||
case Half:
|
||||
} else if(mode & Half) {
|
||||
addr &= ~1;
|
||||
word |= read(addr + 0) << 0;
|
||||
word |= read(addr + 1) << 8;
|
||||
break;
|
||||
case Byte:
|
||||
} else if(mode & Byte) {
|
||||
word |= read(addr + 0) << 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return word;
|
||||
}
|
||||
|
||||
auto MMIO::write(uint32 addr, uint32 size, uint32 word) -> void {
|
||||
switch(size) {
|
||||
case Word:
|
||||
auto MMIO::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
addr &= ~3;
|
||||
write(addr + 0, word >> 0);
|
||||
write(addr + 1, word >> 8);
|
||||
write(addr + 2, word >> 16);
|
||||
write(addr + 3, word >> 24);
|
||||
break;
|
||||
case Half:
|
||||
} else if(mode & Half) {
|
||||
addr &= ~1;
|
||||
write(addr + 0, word >> 0);
|
||||
write(addr + 1, word >> 8);
|
||||
break;
|
||||
case Byte:
|
||||
} else if(mode & Byte) {
|
||||
write(addr + 0, word >> 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
auto Bus::serialize(serializer& s) -> void {
|
||||
}
|
|
@ -48,7 +48,7 @@ void PPU::render_background_linear(Registers::Background& bg) {
|
|||
if(bg.control.screensize & 1) if(tx & 32) offset += 32 * 32;
|
||||
if(bg.control.screensize & 2) if(ty & 32) offset += 32 * 32 * (1 + (bg.control.screensize & 1));
|
||||
offset = basemap + offset * 2;
|
||||
uint16 mapdata = vram_read(offset, Half);
|
||||
uint16 mapdata = vram_read(Half, offset);
|
||||
|
||||
tile.character = mapdata >> 0;
|
||||
tile.hflip = mapdata >> 10;
|
||||
|
@ -57,12 +57,12 @@ void PPU::render_background_linear(Registers::Background& bg) {
|
|||
|
||||
if(bg.control.colormode == 0) {
|
||||
offset = basechr + tile.character * 32 + (py ^ (tile.vflip ? 7 : 0)) * 4;
|
||||
uint32 word = vram_read(offset, Word);
|
||||
uint32 word = vram_read(Word, offset);
|
||||
for(unsigned n = 0; n < 8; n++) data[n] = (word >> (n * 4)) & 15;
|
||||
} else {
|
||||
offset = basechr + tile.character * 64 + (py ^ (tile.vflip ? 7 : 0)) * 8;
|
||||
uint32 wordlo = vram_read(offset + 0, Word);
|
||||
uint32 wordhi = vram_read(offset + 4, Word);
|
||||
uint32 wordlo = vram_read(Word, offset + 0);
|
||||
uint32 wordhi = vram_read(Word, offset + 4);
|
||||
for(unsigned n = 0; n < 4; n++) data[0 + n] = (wordlo >> (n * 8)) & 255;
|
||||
for(unsigned n = 0; n < 4; n++) data[4 + n] = (wordhi >> (n * 8)) & 255;
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ void PPU::render_background_bitmap(Registers::Background& bg) {
|
|||
|
||||
unsigned width = regs.control.bgmode == 5 ? 160 : 240;
|
||||
unsigned height = regs.control.bgmode == 5 ? 128 : 160;
|
||||
unsigned size = depth ? Half : Byte;
|
||||
unsigned mode = depth ? Half : Byte;
|
||||
|
||||
if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) {
|
||||
bg.hmosaic = bg.lx;
|
||||
|
@ -138,7 +138,7 @@ void PPU::render_background_bitmap(Registers::Background& bg) {
|
|||
|
||||
if(px < width && py < height) {
|
||||
unsigned offset = py * width + px;
|
||||
unsigned color = vram_read(basemap + (offset << depth), size);
|
||||
unsigned color = vram_read(mode, basemap + (offset << depth));
|
||||
|
||||
if(depth || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
|
||||
if(depth == 0) color = pram[color];
|
||||
|
|
|
@ -1,65 +1,62 @@
|
|||
uint32 PPU::vram_read(uint32 addr, uint32 size) {
|
||||
auto PPU::vram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
|
||||
|
||||
switch(size) {
|
||||
case Word:
|
||||
if(mode & Word) {
|
||||
addr &= ~3;
|
||||
return vram[addr + 0] << 0 | vram[addr + 1] << 8 | vram[addr + 2] << 16 | vram[addr + 3] << 24;
|
||||
case Half:
|
||||
} else if(mode & Half) {
|
||||
addr &= ~1;
|
||||
return vram[addr + 0] << 0 | vram[addr + 1] << 8;
|
||||
case Byte:
|
||||
} else if(mode & Byte) {
|
||||
return vram[addr];
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::vram_write(uint32 addr, uint32 size, uint32 word) {
|
||||
auto PPU::vram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
|
||||
|
||||
switch(size) {
|
||||
case Word:
|
||||
if(mode & Word) {
|
||||
addr &= ~3;
|
||||
vram[addr + 0] = word >> 0;
|
||||
vram[addr + 1] = word >> 8;
|
||||
vram[addr + 2] = word >> 16;
|
||||
vram[addr + 3] = word >> 24;
|
||||
break;
|
||||
case Half:
|
||||
} else if(mode & Half) {
|
||||
addr &= ~1;
|
||||
vram[addr + 0] = word >> 0;
|
||||
vram[addr + 1] = word >> 8;
|
||||
break;
|
||||
case Byte:
|
||||
} else if(mode & Byte) {
|
||||
addr &= ~1;
|
||||
vram[addr + 0] = word;
|
||||
vram[addr + 1] = word;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 PPU::pram_read(uint32 addr, uint32 size) {
|
||||
if(size == Word) return pram_read(addr & ~2, Half) << 0 | pram_read(addr | 2, Half) << 16;
|
||||
if(size == Byte) return pram_read(addr, Half) >> ((addr & 1) * 8);
|
||||
auto PPU::pram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) return pram_read(Half, addr & ~2) << 0 | pram_read(Half, addr | 2) << 16;
|
||||
if(mode & Byte) return pram_read(Half, addr) >> ((addr & 1) * 8);
|
||||
return pram[addr >> 1 & 511];
|
||||
}
|
||||
|
||||
void PPU::pram_write(uint32 addr, uint32 size, uint32 word) {
|
||||
if(size == Word) {
|
||||
pram_write(addr & ~2, Half, word >> 0);
|
||||
pram_write(addr | 2, Half, word >> 16);
|
||||
auto PPU::pram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
pram_write(Half, addr & ~2, word >> 0);
|
||||
pram_write(Half, addr | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(size == Byte) {
|
||||
return pram_write(addr, Half, word << 8 | word << 0);
|
||||
if(mode & Byte) {
|
||||
return pram_write(Half, addr, word << 8 | word << 0);
|
||||
}
|
||||
|
||||
pram[addr >> 1 & 511] = word & 0x7fff;
|
||||
}
|
||||
|
||||
uint32 PPU::oam_read(uint32 addr, uint32 size) {
|
||||
if(size == Word) return oam_read(addr & ~2, Half) << 0 | oam_read(addr | 2, Half) << 16;
|
||||
if(size == Byte) return oam_read(addr, Half) >> ((addr & 1) * 8);
|
||||
auto PPU::oam_read(unsigned mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) return oam_read(Half, addr & ~2) << 0 | oam_read(Half, addr | 2) << 16;
|
||||
if(mode & Byte) return oam_read(Half, addr) >> ((addr & 1) * 8);
|
||||
|
||||
auto& obj = object[addr >> 3 & 127];
|
||||
auto& par = objectparam[addr >> 5 & 31];
|
||||
|
@ -101,15 +98,15 @@ uint32 PPU::oam_read(uint32 addr, uint32 size) {
|
|||
}
|
||||
}
|
||||
|
||||
void PPU::oam_write(uint32 addr, uint32 size, uint32 word) {
|
||||
if(size == Word) {
|
||||
oam_write(addr & ~2, Half, word >> 0);
|
||||
oam_write(addr | 2, Half, word >> 16);
|
||||
auto PPU::oam_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
if(mode & Word) {
|
||||
oam_write(Half, addr & ~2, word >> 0);
|
||||
oam_write(Half, addr | 2, word >> 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(size == Byte) {
|
||||
return oam_write(addr, Half, word << 8 | word << 0);
|
||||
if(mode & Byte) {
|
||||
return oam_write(Half, addr, word << 8 | word << 0);
|
||||
}
|
||||
|
||||
auto& obj = object[addr >> 3 & 127];
|
||||
|
|
|
@ -16,14 +16,14 @@ struct PPU : Thread, MMIO {
|
|||
uint8 read(uint32 addr);
|
||||
void write(uint32 addr, uint8 byte);
|
||||
|
||||
uint32 vram_read(uint32 addr, uint32 size);
|
||||
void vram_write(uint32 addr, uint32 size, uint32 word);
|
||||
auto vram_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto vram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
uint32 pram_read(uint32 addr, uint32 size);
|
||||
void pram_write(uint32 addr, uint32 size, uint32 word);
|
||||
auto pram_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto pram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
uint32 oam_read(uint32 addr, uint32 size);
|
||||
void oam_write(uint32 addr, uint32 size, uint32 word);
|
||||
auto oam_read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto oam_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
|
||||
void render_backgrounds();
|
||||
void render_background_linear(Registers::Background&);
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
uint32 BIOS::read(uint32 addr, uint32 size) {
|
||||
//GBA BIOS is read-protected; only the BIOS itself can read its own memory
|
||||
//when accessed elsewhere; this returns the last value read by the BIOS program
|
||||
if(cpu.r(15) >= 0x02000000) return mdr;
|
||||
|
||||
if(size == Word) return mdr = read(addr &~ 2, Half) << 0 | read(addr | 2, Half) << 16;
|
||||
if(size == Half) return mdr = read(addr &~ 1, Byte) << 0 | read(addr | 1, Byte) << 8;
|
||||
return mdr = data[addr & 0x3fff];
|
||||
}
|
||||
|
||||
void BIOS::write(uint32 addr, uint32 size, uint32 word) {
|
||||
}
|
||||
|
||||
BIOS::BIOS() {
|
||||
data = new uint8[size = 16384]();
|
||||
size = 16384;
|
||||
data = new uint8[size]();
|
||||
}
|
||||
|
||||
BIOS::~BIOS() {
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
auto BIOS::read(unsigned mode, uint32 addr) -> uint32 {
|
||||
//GBA BIOS is read-protected; only the BIOS itself can read its own memory
|
||||
//when accessed elsewhere; this returns the last value read by the BIOS program
|
||||
if(cpu.r(15) >= 0x02000000) return mdr;
|
||||
|
||||
if(mode & Word) return mdr = read(Half, addr &~ 2) << 0 | read(Half, addr | 2) << 16;
|
||||
if(mode & Half) return mdr = read(Byte, addr &~ 1) << 0 | read(Byte, addr | 1) << 8;
|
||||
|
||||
return mdr = data[addr & 0x3fff];
|
||||
}
|
||||
|
||||
auto BIOS::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ void System::serialize_all(serializer& s) {
|
|||
cpu.serialize(s);
|
||||
ppu.serialize(s);
|
||||
apu.serialize(s);
|
||||
bus.serialize(s);
|
||||
player.serialize(s);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,15 @@ enum class Input : unsigned {
|
|||
};
|
||||
|
||||
struct BIOS : Memory {
|
||||
uint8* data;
|
||||
unsigned size;
|
||||
uint32 mdr;
|
||||
|
||||
uint32 read(uint32 addr, uint32 size);
|
||||
void write(uint32 addr, uint32 size, uint32 word);
|
||||
|
||||
BIOS();
|
||||
~BIOS();
|
||||
|
||||
auto read(unsigned mode, uint32 addr) -> uint32 override;
|
||||
auto write(unsigned mode, uint32 addr, uint32 word) -> void override;
|
||||
|
||||
uint8* data = nullptr;
|
||||
unsigned size = 0;
|
||||
uint32 mdr = 0;
|
||||
};
|
||||
|
||||
struct System {
|
||||
|
|
|
@ -15,6 +15,7 @@ auto ARM::power() -> void {
|
|||
processor.power();
|
||||
vector(0x00000000, Processor::Mode::SVC);
|
||||
pipeline.reload = true;
|
||||
pipeline.nonsequential = true;
|
||||
crash = false;
|
||||
r(15).modify = [&] {
|
||||
pipeline.reload = true;
|
||||
|
@ -29,42 +30,41 @@ auto ARM::exec() -> void {
|
|||
}
|
||||
|
||||
auto ARM::idle() -> void {
|
||||
processor.nonsequential = true;
|
||||
bus_idle(r(15));
|
||||
pipeline.nonsequential = true;
|
||||
return bus_idle();
|
||||
}
|
||||
|
||||
auto ARM::read(uint32 addr, uint32 size, bool mode) -> uint32 {
|
||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
||||
return bus_read(addr, size, mode);
|
||||
auto ARM::read(unsigned mode, uint32 addr) -> uint32 {
|
||||
return bus_read(mode, addr);
|
||||
}
|
||||
|
||||
auto ARM::load(uint32 addr, uint32 size, bool mode) -> uint32 {
|
||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
||||
uint32 word = bus_read(addr, size, mode);
|
||||
auto ARM::load(unsigned mode, uint32 addr) -> uint32 {
|
||||
pipeline.nonsequential = true;
|
||||
uint32 word = bus_read(Load | mode, addr);
|
||||
|
||||
if(size == Half) { word &= 0xffff; word |= word << 16; }
|
||||
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
||||
if(mode & Half) { word &= 0xffff; word |= word << 16; }
|
||||
if(mode & Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
||||
|
||||
word = ror(word, 8 * (addr & 3));
|
||||
idle();
|
||||
|
||||
if(size == Half) word &= 0xffff;
|
||||
if(size == Byte) word &= 0xff;
|
||||
if(mode & Half) word &= 0xffff;
|
||||
if(mode & Byte) word &= 0xff;
|
||||
return word;
|
||||
}
|
||||
|
||||
auto ARM::write(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
|
||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
||||
return bus_write(addr, size, mode, word);
|
||||
auto ARM::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
pipeline.nonsequential = true;
|
||||
return bus_write(mode, addr, word);
|
||||
}
|
||||
|
||||
auto ARM::store(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
|
||||
if(size == Half) { word &= 0xffff; word |= word << 16; }
|
||||
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
||||
auto ARM::store(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||
pipeline.nonsequential = true;
|
||||
|
||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
||||
bus_write(addr, size, mode, word);
|
||||
processor.nonsequential = true;
|
||||
if(mode & Half) { word &= 0xffff; word |= word << 16; }
|
||||
if(mode & Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
||||
|
||||
return bus_write(Store | mode, addr, word);
|
||||
}
|
||||
|
||||
auto ARM::vector(uint32 addr, Processor::Mode mode) -> void {
|
||||
|
|
|
@ -4,12 +4,20 @@
|
|||
namespace Processor {
|
||||
|
||||
//Supported Models:
|
||||
//* ARMv3 (ST018)
|
||||
//* ARMv4 (ARM7TDMI)
|
||||
//* ARMv3 (ARM60)
|
||||
//* ARMv4T (ARM7TDMI)
|
||||
|
||||
struct ARM {
|
||||
enum : unsigned { Byte = 8, Half = 16, Word = 32 };
|
||||
enum : bool { Nonsequential = 0, Sequential = 1 };
|
||||
enum : unsigned { //mode flags for bus_read, bus_write:
|
||||
Nonsequential = 1, //N cycle
|
||||
Sequential = 2, //S cycle
|
||||
Prefetch = 4, //instruction fetch (eligible for prefetch)
|
||||
Byte = 8, //8-bit access
|
||||
Half = 16, //16-bit access
|
||||
Word = 32, //32-bit access
|
||||
Load = 64, //load operation
|
||||
Store = 128, //store operation
|
||||
};
|
||||
|
||||
#include "registers.hpp"
|
||||
#include "instructions-arm.hpp"
|
||||
|
@ -17,18 +25,18 @@ struct ARM {
|
|||
#include "disassembler.hpp"
|
||||
|
||||
virtual auto step(unsigned clocks) -> void = 0;
|
||||
virtual auto bus_idle(uint32 addr) -> void = 0;
|
||||
virtual auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32 = 0;
|
||||
virtual auto bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void = 0;
|
||||
virtual auto bus_idle() -> void = 0;
|
||||
virtual auto bus_read(unsigned mode, uint32 addr) -> uint32 = 0;
|
||||
virtual auto bus_write(unsigned mode, uint32 addr, uint32 word) -> void = 0;
|
||||
|
||||
//arm.cpp
|
||||
auto power() -> void;
|
||||
auto exec() -> void;
|
||||
auto idle() -> void;
|
||||
auto read(uint32 addr, uint32 size, bool mode) -> uint32;
|
||||
auto load(uint32 addr, uint32 size, bool mode) -> uint32;
|
||||
auto write(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
|
||||
auto store(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
|
||||
auto read(unsigned mode, uint32 addr) -> uint32;
|
||||
auto load(unsigned mode, uint32 addr) -> uint32;
|
||||
auto write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
auto store(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||
auto vector(uint32 addr, Processor::Mode mode) -> void;
|
||||
|
||||
//algorithms.cpp
|
||||
|
@ -44,14 +52,15 @@ struct ARM {
|
|||
auto rrx(uint32 source) -> uint32;
|
||||
|
||||
//step.cpp
|
||||
auto pipeline_step() -> void;
|
||||
auto arm_step() -> void;
|
||||
auto thumb_step() -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool trace{false};
|
||||
uintmax_t instructions{0};
|
||||
bool trace = false;
|
||||
uintmax_t instructions = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
|
||||
string output{hex<8>(pc), " "};
|
||||
|
||||
uint32 instruction = read(pc & ~3, Word, Nonsequential);
|
||||
uint32 instruction = read(Word | Nonsequential, pc & ~3);
|
||||
output.append(hex<8>(instruction), " ");
|
||||
|
||||
//multiply()
|
||||
|
@ -134,7 +134,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
if(pre == 1) output.append("]");
|
||||
if(pre == 0 || writeback == 1) output.append("!");
|
||||
|
||||
if(rn == 15) output.append(" =0x", hex<4>(read(pc + 8 + (up ? +immediate : -immediate), Half, Nonsequential)));
|
||||
if(rn == 15) output.append(" =0x", hex<4>(read(Half | Nonsequential, pc + 8 + (up ? +immediate : -immediate))));
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -184,8 +184,8 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
if(pre == 1) output.append("]");
|
||||
if(pre == 0 || writeback == 1) output.append("!");
|
||||
|
||||
if(rn == 15 && half == 1) output.append(" =0x", hex<4>(read(pc + 8 + (up ? +immediate : -immediate), Half, Nonsequential)));
|
||||
if(rn == 15 && half == 0) output.append(" =0x", hex<2>(read(pc + 8 + (up ? +immediate : -immediate), Byte, Nonsequential)));
|
||||
if(rn == 15 && half == 1) output.append(" =0x", hex<4>(read(Half | Nonsequential, pc + 8 + (up ? +immediate : -immediate))));
|
||||
if(rn == 15 && half == 0) output.append(" =0x", hex<2>(read(Byte | Nonsequential, pc + 8 + (up ? +immediate : -immediate))));
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -359,7 +359,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
if(pre == 1) output.append("]");
|
||||
if(pre == 0 || writeback == 1) output.append("!");
|
||||
|
||||
if(rn == 15) output.append(" =0x", hex<8>(read(pc + 8 + (up ? +immediate : -immediate), byte ? Byte : Word, Nonsequential)));
|
||||
if(rn == 15) output.append(" =0x", hex<8>(read((byte ? Byte : Word) | Nonsequential, pc + 8 + (up ? +immediate : -immediate))));
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -457,7 +457,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||
|
||||
string output{hex<8>(pc), " "};
|
||||
|
||||
uint16 instruction = read(pc & ~1, Half, Nonsequential);
|
||||
uint16 instruction = read(Half | Nonsequential, pc & ~1);
|
||||
output.append(hex<4>(instruction), " ");
|
||||
|
||||
//adjust_register()
|
||||
|
@ -571,7 +571,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||
|
||||
unsigned rm = ((pc + 4) & ~3) + displacement * 4;
|
||||
output.append("ldr ", registers[rd], ",[pc,#0x", hex<3>(rm), "]");
|
||||
output.append(" =0x", hex<8>(read(rm, Word, Nonsequential)));
|
||||
output.append(" =0x", hex<8>(read(Word | Nonsequential, rm)));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -740,7 +740,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||
//bl address
|
||||
if((instruction & 0xf800) == 0xf000) {
|
||||
uint11 offsethi = instruction;
|
||||
instruction = read((pc & ~1) + 2, Half, Nonsequential);
|
||||
instruction = read(Half | Nonsequential, (pc & ~1) + 2);
|
||||
uint11 offsetlo = instruction;
|
||||
|
||||
int22 displacement = (offsethi << 11) | (offsetlo << 0);
|
||||
|
|
|
@ -137,8 +137,8 @@ auto ARM::arm_op_memory_swap() {
|
|||
uint4 d = instruction() >> 12;
|
||||
uint4 m = instruction();
|
||||
|
||||
uint32 word = load(r(n), byte ? Byte : Word, Nonsequential);
|
||||
store(r(n), byte ? Byte : Word, Nonsequential, r(m));
|
||||
uint32 word = load((byte ? Byte : Word) | Nonsequential, r(n));
|
||||
store((byte ? Byte : Word) | Nonsequential, r(n), r(m));
|
||||
r(d) = word;
|
||||
}
|
||||
|
||||
|
@ -166,8 +166,8 @@ auto ARM::arm_op_move_half_register() {
|
|||
uint32 rm = r(m);
|
||||
|
||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||
if(l == 1) r(d) = load(rn, Half, Nonsequential);
|
||||
if(l == 0) store(rn, Half, Nonsequential, r(d));
|
||||
if(l == 1) r(d) = load(Half | Nonsequential, rn);
|
||||
if(l == 0) store(Half | Nonsequential, rn, r(d));
|
||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||
|
||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
||||
|
@ -199,8 +199,8 @@ auto ARM::arm_op_move_half_immediate() {
|
|||
uint8 immediate = (ih << 4) + (il << 0);
|
||||
|
||||
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
||||
if(l == 1) r(d) = load(rn, Half, Nonsequential);
|
||||
if(l == 0) store(rn, Half, Nonsequential, r(d));
|
||||
if(l == 1) r(d) = load(Half | Nonsequential, rn);
|
||||
if(l == 0) store(Half | Nonsequential, rn, r(d));
|
||||
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
||||
|
||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
||||
|
@ -230,7 +230,7 @@ auto ARM::arm_op_load_register() {
|
|||
uint32 rm = r(m);
|
||||
|
||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||
uint32 word = load(rn, half ? Half : Byte, Nonsequential);
|
||||
uint32 word = load((half ? Half : Byte) | Nonsequential, rn);
|
||||
r(d) = half ? (int16)word : (int8)word;
|
||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||
|
||||
|
@ -263,7 +263,7 @@ auto ARM::arm_op_load_immediate() {
|
|||
uint8 immediate = (ih << 4) + (il << 0);
|
||||
|
||||
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
||||
uint32 word = load(rn, half ? Half : Byte, Nonsequential);
|
||||
uint32 word = load((half ? Half : Byte) | Nonsequential, rn);
|
||||
r(d) = half ? (int16)word : (int8)word;
|
||||
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
||||
|
||||
|
@ -437,8 +437,8 @@ auto ARM::arm_op_move_immediate_offset() {
|
|||
auto& rd = r(d);
|
||||
|
||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||
if(l == 1) rd = load(rn, byte ? Byte : Word, Nonsequential);
|
||||
if(l == 0) store(rn, byte ? Byte : Word, Nonsequential, rd);
|
||||
if(l == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
|
||||
if(l == 0) store((byte ? Byte : Word) | Nonsequential, rn, rd);
|
||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||
|
||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
||||
|
@ -482,8 +482,8 @@ auto ARM::arm_op_move_register_offset() {
|
|||
if(mode == 3) rm = rs ? ror(rm, rs) : rrx(rm);
|
||||
|
||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||
if(l == 1) rd = load(rn, byte ? Byte : Word, Nonsequential);
|
||||
if(l == 0) store(rn, byte ? Byte : Word, Nonsequential, rd);
|
||||
if(l == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
|
||||
if(l == 0) store((byte ? Byte : Word) | Nonsequential, rn, rd);
|
||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||
|
||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
||||
|
@ -521,11 +521,13 @@ auto ARM::arm_op_move_multiple() {
|
|||
|
||||
if(usr) processor.setMode(Processor::Mode::USR);
|
||||
|
||||
unsigned sequential = Nonsequential;
|
||||
for(unsigned m = 0; m < 16; m++) {
|
||||
if(list & 1 << m) {
|
||||
if(l == 1) r(m) = read(rn, Word, Nonsequential);
|
||||
if(l == 0) write(rn, Word, Nonsequential, r(m));
|
||||
if(l == 1) r(m) = read(Word | sequential, rn);
|
||||
if(l == 0) write(Word | sequential, rn, r(m));
|
||||
rn += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,7 +542,7 @@ auto ARM::arm_op_move_multiple() {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
processor.nonsequential = true;
|
||||
pipeline.nonsequential = true;
|
||||
}
|
||||
|
||||
if(writeback) {
|
||||
|
|
|
@ -145,7 +145,7 @@ auto ARM::thumb_op_load_literal() {
|
|||
uint8 displacement = instruction();
|
||||
|
||||
unsigned rm = (r(15) & ~3) + displacement * 4;
|
||||
r(d) = load(rm, Word, Nonsequential);
|
||||
r(d) = load(Word | Nonsequential, rm);
|
||||
}
|
||||
|
||||
//(ld(r,s),str){b,h} rd,[rn,rm]
|
||||
|
@ -161,14 +161,14 @@ auto ARM::thumb_op_move_register_offset() {
|
|||
uint3 d = instruction() >> 0;
|
||||
|
||||
switch(opcode) {
|
||||
case 0: store(r(n) + r(m), Word, Nonsequential, r(d)); break; //STR
|
||||
case 1: store(r(n) + r(m), Half, Nonsequential, r(d)); break; //STRH
|
||||
case 2: store(r(n) + r(m), Byte, Nonsequential, r(d)); break; //STRB
|
||||
case 3: r(d) = (int8)load(r(n) + r(m), Byte, Nonsequential); break; //LDSB
|
||||
case 4: r(d) = load(r(n) + r(m), Word, Nonsequential); break; //LDR
|
||||
case 5: r(d) = load(r(n) + r(m), Half, Nonsequential); break; //LDRH
|
||||
case 6: r(d) = load(r(n) + r(m), Byte, Nonsequential); break; //LDRB
|
||||
case 7: r(d) = (int16)load(r(n) + r(m), Half, Nonsequential); break; //LDSH
|
||||
case 0: store(Word | Nonsequential, r(n) + r(m), r(d)); break; //STR
|
||||
case 1: store(Half | Nonsequential, r(n) + r(m), r(d)); break; //STRH
|
||||
case 2: store(Byte | Nonsequential, r(n) + r(m), r(d)); break; //STRB
|
||||
case 3: r(d) = (int8)load(Byte | Nonsequential, r(n) + r(m)); break; //LDSB
|
||||
case 4: r(d) = load(Word | Nonsequential, r(n) + r(m)); break; //LDR
|
||||
case 5: r(d) = load(Half | Nonsequential, r(n) + r(m)); break; //LDRH
|
||||
case 6: r(d) = load(Byte | Nonsequential, r(n) + r(m)); break; //LDRB
|
||||
case 7: r(d) = (int16)load(Half | Nonsequential, r(n) + r(m)); break; //LDSH
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,8 +184,8 @@ auto ARM::thumb_op_move_word_immediate() {
|
|||
uint3 n = instruction() >> 3;
|
||||
uint3 d = instruction() >> 0;
|
||||
|
||||
if(l == 1) r(d) = load(r(n) + offset * 4, Word, Nonsequential);
|
||||
if(l == 0) store(r(n) + offset * 4, Word, Nonsequential, r(d));
|
||||
if(l == 1) r(d) = load(Word | Nonsequential, r(n) + offset * 4);
|
||||
if(l == 0) store(Word | Nonsequential, r(n) + offset * 4, r(d));
|
||||
}
|
||||
|
||||
//(ldr,str)b rd,[rn,#offset]
|
||||
|
@ -200,8 +200,8 @@ auto ARM::thumb_op_move_byte_immediate() {
|
|||
uint3 n = instruction() >> 3;
|
||||
uint3 d = instruction() >> 0;
|
||||
|
||||
if(l == 1) r(d) = load(r(n) + offset, Byte, Nonsequential);
|
||||
if(l == 0) store(r(n) + offset, Byte, Nonsequential, r(d));
|
||||
if(l == 1) r(d) = load(Byte | Nonsequential, r(n) + offset);
|
||||
if(l == 0) store(Byte | Nonsequential, r(n) + offset, r(d));
|
||||
}
|
||||
|
||||
//(ldr,str)h rd,[rn,#offset]
|
||||
|
@ -216,8 +216,8 @@ auto ARM::thumb_op_move_half_immediate() {
|
|||
uint3 n = instruction() >> 3;
|
||||
uint3 d = instruction() >> 0;
|
||||
|
||||
if(l == 1) r(d) = load(r(n) + offset * 2, Half, Nonsequential);
|
||||
if(l == 0) store(r(n) + offset * 2, Half, Nonsequential, r(d));
|
||||
if(l == 1) r(d) = load(Half | Nonsequential, r(n) + offset * 2);
|
||||
if(l == 0) store(Half | Nonsequential, r(n) + offset * 2, r(d));
|
||||
}
|
||||
|
||||
//(ldr,str) rd,[sp,#immediate]
|
||||
|
@ -230,8 +230,8 @@ auto ARM::thumb_op_move_stack() {
|
|||
uint3 d = instruction() >> 8;
|
||||
uint8 immediate = instruction();
|
||||
|
||||
if(l == 1) r(d) = load(r(13) + immediate * 4, Word, Nonsequential);
|
||||
if(l == 0) store(r(13) + immediate * 4, Word, Nonsequential, r(d));
|
||||
if(l == 1) r(d) = load(Word | Nonsequential, r(13) + immediate * 4);
|
||||
if(l == 0) store(Word | Nonsequential, r(13) + immediate * 4, r(d));
|
||||
}
|
||||
|
||||
//add rd,{pc,sp},#immediate
|
||||
|
@ -275,24 +275,30 @@ auto ARM::thumb_op_stack_multiple() {
|
|||
if(l == 1) sp = r(13);
|
||||
if(l == 0) sp = r(13) - (bit::count(list) + branch) * 4;
|
||||
|
||||
unsigned sequential = Nonsequential;
|
||||
for(unsigned m = 0; m < 8; m++) {
|
||||
if(list & 1 << m) {
|
||||
if(l == 1) r(m) = read(sp, Word, Nonsequential); //POP
|
||||
if(l == 0) write(sp, Word, Nonsequential, r(m)); //PUSH
|
||||
if(l == 1) r(m) = read(Word | sequential, sp); //POP
|
||||
if(l == 0) write(Word | sequential, sp, r(m)); //PUSH
|
||||
sp += 4;
|
||||
sequential = Sequential;
|
||||
}
|
||||
}
|
||||
|
||||
if(branch) {
|
||||
//note: ARMv5+ POP sets cpsr().t
|
||||
if(l == 1) r(15) = read(sp, Word, Nonsequential); //POP
|
||||
if(l == 0) write(sp, Word, Nonsequential, r(14)); //PUSH
|
||||
if(l == 1) r(15) = read(Word | Nonsequential, sp); //POP
|
||||
if(l == 0) write(Word | Nonsequential, sp, r(14)); //PUSH
|
||||
sp += 4;
|
||||
}
|
||||
|
||||
if(l == 1) idle();
|
||||
if(l == 1) r(13) += (bit::count(list) + branch) * 4;
|
||||
if(l == 0) r(13) -= (bit::count(list) + branch) * 4;
|
||||
if(l == 1) {
|
||||
idle();
|
||||
r(13) += (bit::count(list) + branch) * 4;
|
||||
} else {
|
||||
pipeline.nonsequential = true;
|
||||
r(13) -= (bit::count(list) + branch) * 4;
|
||||
}
|
||||
}
|
||||
|
||||
//(ldmia,stmia) rn!,{r...}
|
||||
|
@ -308,8 +314,8 @@ auto ARM::thumb_op_move_multiple() {
|
|||
|
||||
for(unsigned m = 0; m < 8; m++) {
|
||||
if(list & 1 << m) {
|
||||
if(l == 1) r(m) = read(rn, Word, Nonsequential); //LDMIA
|
||||
if(l == 0) write(rn, Word, Nonsequential, r(m)); //STMIA
|
||||
if(l == 1) r(m) = read(Word | Nonsequential, rn); //LDMIA
|
||||
if(l == 0) write(Word | Nonsequential, rn, r(m)); //STMIA
|
||||
rn += 4;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ auto ARM::Processor::power() -> void {
|
|||
pc = 0;
|
||||
|
||||
carryout = false;
|
||||
nonsequential = false;
|
||||
irqline = false;
|
||||
|
||||
cpsr = 0;
|
||||
|
@ -61,19 +60,4 @@ auto ARM::Processor::setMode(Mode mode) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto ARM::pipeline_step() -> void {
|
||||
pipeline.execute = pipeline.decode;
|
||||
pipeline.decode = pipeline.fetch;
|
||||
|
||||
if(cpsr().t == 0) {
|
||||
r(15).data += 4;
|
||||
pipeline.fetch.address = r(15) & ~3;
|
||||
pipeline.fetch.instruction = read(pipeline.fetch.address, Word, Sequential);
|
||||
} else {
|
||||
r(15).data += 2;
|
||||
pipeline.fetch.address = r(15) & ~1;
|
||||
pipeline.fetch.instruction = read(pipeline.fetch.address, Half, Sequential);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -50,6 +50,7 @@ struct PSR {
|
|||
|
||||
struct Pipeline {
|
||||
bool reload = false;
|
||||
bool nonsequential = false;
|
||||
|
||||
struct Instruction {
|
||||
uint32 address = 0;
|
||||
|
@ -106,7 +107,6 @@ struct Processor {
|
|||
GPR pc;
|
||||
PSR cpsr;
|
||||
bool carryout = false;
|
||||
bool nonsequential = false;
|
||||
bool irqline = false;
|
||||
|
||||
GPR* r[16] = {nullptr};
|
||||
|
@ -120,8 +120,6 @@ Processor processor;
|
|||
Pipeline pipeline;
|
||||
bool crash = false;
|
||||
|
||||
auto pipeline_step() -> void;
|
||||
|
||||
alwaysinline auto r(unsigned n) -> GPR& { return *processor.r[n]; }
|
||||
alwaysinline auto cpsr() -> PSR& { return processor.cpsr; }
|
||||
alwaysinline auto spsr() -> PSR& { return *processor.spsr; }
|
||||
|
|
|
@ -55,10 +55,10 @@ auto ARM::serialize(serializer& s) -> void {
|
|||
s.integer(processor.pc.data);
|
||||
processor.cpsr.serialize(s);
|
||||
s.integer(processor.carryout);
|
||||
s.integer(processor.nonsequential);
|
||||
s.integer(processor.irqline);
|
||||
|
||||
s.integer(pipeline.reload);
|
||||
s.integer(pipeline.nonsequential);
|
||||
s.integer(pipeline.execute.address);
|
||||
s.integer(pipeline.execute.instruction);
|
||||
s.integer(pipeline.decode.address);
|
||||
|
|
|
@ -1,10 +1,31 @@
|
|||
auto ARM::pipeline_step() -> void {
|
||||
pipeline.execute = pipeline.decode;
|
||||
pipeline.decode = pipeline.fetch;
|
||||
|
||||
unsigned sequential = Sequential;
|
||||
if(pipeline.nonsequential) {
|
||||
pipeline.nonsequential = false;
|
||||
sequential = Nonsequential;
|
||||
}
|
||||
|
||||
if(cpsr().t == 0) {
|
||||
r(15).data += 4;
|
||||
pipeline.fetch.address = r(15) & ~3;
|
||||
pipeline.fetch.instruction = read(Prefetch | Word | sequential, pipeline.fetch.address);
|
||||
} else {
|
||||
r(15).data += 2;
|
||||
pipeline.fetch.address = r(15) & ~1;
|
||||
pipeline.fetch.instruction = read(Prefetch | Half | sequential, pipeline.fetch.address);
|
||||
}
|
||||
}
|
||||
|
||||
auto ARM::arm_step() -> void {
|
||||
if(pipeline.reload) {
|
||||
pipeline.reload = false;
|
||||
r(15).data &= ~3;
|
||||
|
||||
pipeline.fetch.address = r(15) & ~3;
|
||||
pipeline.fetch.instruction = read(pipeline.fetch.address, Word, Nonsequential);
|
||||
pipeline.fetch.instruction = read(Prefetch | Word | Nonsequential, pipeline.fetch.address);
|
||||
|
||||
pipeline_step();
|
||||
}
|
||||
|
@ -61,7 +82,7 @@ auto ARM::thumb_step() -> void {
|
|||
r(15).data &= ~1;
|
||||
|
||||
pipeline.fetch.address = r(15) & ~1;
|
||||
pipeline.fetch.instruction = read(pipeline.fetch.address, Half, Nonsequential);
|
||||
pipeline.fetch.instruction = read(Prefetch | Half | Nonsequential, pipeline.fetch.address);
|
||||
|
||||
pipeline_step();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//ARMv3 (ARM6)
|
||||
//ARMv3 (ARM60)
|
||||
|
||||
struct ArmDSP : Processor::ARM, Coprocessor {
|
||||
uint8* programROM;
|
||||
|
@ -11,9 +11,9 @@ struct ArmDSP : Processor::ARM, Coprocessor {
|
|||
void enter();
|
||||
|
||||
void step(unsigned clocks) override;
|
||||
void bus_idle(uint32 addr) override;
|
||||
uint32 bus_read(uint32 addr, uint32 size, bool mode) override;
|
||||
void bus_write(uint32 addr, uint32 size, bool mode, uint32 word) override;
|
||||
void bus_idle() override;
|
||||
uint32 bus_read(unsigned mode, uint32 addr) override;
|
||||
void bus_write(unsigned mode, uint32 addr, uint32 word) override;
|
||||
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
|
|
@ -3,32 +3,33 @@
|
|||
//note: timings are completely unverified
|
||||
//due to the ST018 chip design (on-die ROM), testing is nearly impossible
|
||||
|
||||
void ArmDSP::bus_idle(uint32 addr) {
|
||||
void ArmDSP::bus_idle() {
|
||||
step(1);
|
||||
}
|
||||
|
||||
uint32 ArmDSP::bus_read(uint32 addr, uint32 size, bool mode) {
|
||||
uint32 ArmDSP::bus_read(unsigned mode, uint32 addr) {
|
||||
step(1);
|
||||
|
||||
static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) -> uint32 {
|
||||
switch(size) {
|
||||
case Word:
|
||||
static auto memory = [&](const uint8 *memory, unsigned mode, uint32 addr) -> uint32 {
|
||||
if(mode & Word) {
|
||||
memory += addr & ~3;
|
||||
return memory[0] << 0 | memory[1] << 8 | memory[2] << 16 | memory[3] << 24;
|
||||
case Byte:
|
||||
} else if(mode & Byte) {
|
||||
return memory[addr];
|
||||
} else {
|
||||
return 0; //should never occur
|
||||
}
|
||||
};
|
||||
|
||||
switch(addr & 0xe0000000) {
|
||||
case 0x00000000: return memory(programROM, addr & 0x1ffff, size);
|
||||
case 0x00000000: return memory(programROM, mode, addr & 0x1ffff);
|
||||
case 0x20000000: return pipeline.fetch.instruction;
|
||||
case 0x40000000: break;
|
||||
case 0x60000000: return 0x40404001;
|
||||
case 0x80000000: return pipeline.fetch.instruction;
|
||||
case 0xa0000000: return memory(dataROM, addr & 0x7fff, size);
|
||||
case 0xa0000000: return memory(dataROM, mode, addr & 0x7fff);
|
||||
case 0xc0000000: return pipeline.fetch.instruction;
|
||||
case 0xe0000000: return memory(programRAM, addr & 0x3fff, size);
|
||||
case 0xe0000000: return memory(programRAM, mode, addr & 0x3fff);
|
||||
}
|
||||
|
||||
addr &= 0xe000003f;
|
||||
|
@ -44,25 +45,22 @@ uint32 ArmDSP::bus_read(uint32 addr, uint32 size, bool mode) {
|
|||
return bridge.status();
|
||||
}
|
||||
|
||||
return 0u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ArmDSP::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) {
|
||||
void ArmDSP::bus_write(unsigned mode, uint32 addr, uint32 word) {
|
||||
step(1);
|
||||
|
||||
static auto memory = [](uint8 *memory, uint32 addr, uint32 size, uint32 word) {
|
||||
switch(size) {
|
||||
case Word:
|
||||
static auto memory = [](uint8 *memory, unsigned mode, uint32 addr, uint32 word) {
|
||||
if(mode & Word) {
|
||||
memory += addr & ~3;
|
||||
*memory++ = word >> 0;
|
||||
*memory++ = word >> 8;
|
||||
*memory++ = word >> 16;
|
||||
*memory++ = word >> 24;
|
||||
break;
|
||||
case Byte:
|
||||
} else if(mode & Byte) {
|
||||
memory += addr;
|
||||
*memory++ = word >> 0;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -74,7 +72,7 @@ void ArmDSP::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) {
|
|||
case 0x80000000: return;
|
||||
case 0xa0000000: return;
|
||||
case 0xc0000000: return;
|
||||
case 0xe0000000: return memory(programRAM, addr & 0x3fff, size, word);
|
||||
case 0xe0000000: return memory(programRAM, mode, addr & 0x3fff, word);
|
||||
}
|
||||
|
||||
addr &= 0xe000003f;
|
||||
|
|
Loading…
Reference in New Issue