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:
Tim Allen 2015-07-01 20:58:42 +10:00
parent 7ff7f64482
commit 28a14198cb
34 changed files with 444 additions and 487 deletions

View File

@ -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/";

View File

@ -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() {

View File

@ -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();
};

82
gba/cpu/bus.cpp Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -5,3 +5,7 @@ struct Pending {
bool hdma;
} dma;
} pending;
struct Active {
bool dma;
} active;

View File

@ -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() {

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -1,2 +0,0 @@
auto Bus::serialize(serializer& s) -> void {
}

View File

@ -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];

View File

@ -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];

View File

@ -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&);

View File

@ -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 {
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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

View File

@ -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; }

View File

@ -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);

View File

@ -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();
}

View File

@ -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);

View File

@ -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;