mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-08-16 16:34:03 +02:00
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:
@@ -8,7 +8,7 @@ using namespace nall;
|
|||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
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 Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
@@ -96,66 +96,56 @@ unsigned Cartridge::ram_size() {
|
|||||||
if(has_sram) return ram.size;
|
if(has_sram) return ram.size;
|
||||||
if(has_eeprom) return eeprom.size;
|
if(has_eeprom) return eeprom.size;
|
||||||
if(has_flashrom) return flashrom.size;
|
if(has_flashrom) return flashrom.size;
|
||||||
return 0u;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Cartridge::read(uint8 *data, uint32 addr, uint32 size) {
|
auto Cartridge::read(uint8 *data, unsigned mode, uint32 addr) -> uint32 {
|
||||||
if(size == Word) addr &= ~3;
|
if(mode & Word) addr &= ~3;
|
||||||
if(size == Half) addr &= ~1;
|
if(mode & Half) addr &= ~1;
|
||||||
data += addr;
|
data += addr;
|
||||||
if(size == Word) return data[0] << 0 | data[1] << 8 | data[2] << 16 | data[3] << 24;
|
if(mode & Word) return data[0] << 0 | data[1] << 8 | data[2] << 16 | data[3] << 24;
|
||||||
if(size == Half) return data[0] << 0 | data[1] << 8;
|
if(mode & Half) return data[0] << 0 | data[1] << 8;
|
||||||
return data[0];
|
if(mode & Byte) return data[0];
|
||||||
|
return 0; //should never occur
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::write(uint8 *data, uint32 addr, uint32 size, uint32 word) {
|
auto Cartridge::write(uint8 *data, unsigned mode, uint32 addr, uint32 word) -> void {
|
||||||
if(size == Word) addr &= ~3;
|
if(mode & Word) addr &= ~3;
|
||||||
if(size == Half) addr &= ~1;
|
if(mode & Half) addr &= ~1;
|
||||||
data += addr;
|
data += addr;
|
||||||
switch(size) {
|
if(mode & Word) {
|
||||||
case Word: data[3] = word >> 24;
|
data[0] = word >> 0;
|
||||||
data[2] = word >> 16;
|
data[1] = word >> 8;
|
||||||
case Half: data[1] = word >> 8;
|
data[2] = word >> 16;
|
||||||
case Byte: data[0] = word >> 0;
|
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
|
#define RAM_ANALYZE
|
||||||
|
|
||||||
uint32 Cartridge::read(uint32 addr, uint32 size) {
|
auto Cartridge::rom_read(unsigned mode, uint32 addr) -> uint32 {
|
||||||
#ifdef RAM_ANALYZE
|
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.read();
|
||||||
if((addr & 0x0e000000) == 0x0e000000) {
|
return read(rom.data, mode, addr & 0x01ff'ffff);
|
||||||
static bool once = true;
|
}
|
||||||
if(once) once = false, print("* SRAM/FlashROM read detected\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(has_sram && (addr & 0x0e000000 ) == 0x0e000000 ) return read(ram.data, addr & ram.mask, size);
|
auto Cartridge::rom_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||||
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.read();
|
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.write(word & 1);
|
||||||
if(has_flashrom && (addr & 0x0e000000 ) == 0x0e000000 ) return flashrom.read(addr);
|
}
|
||||||
if(addr < 0x0e000000) return read(rom.data, addr & 0x01ffffff, size);
|
|
||||||
|
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;
|
return cpu.pipeline.fetch.instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cartridge::write(uint32 addr, uint32 size, uint32 word) {
|
auto Cartridge::ram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||||
#ifdef RAM_ANALYZE
|
if(has_sram) return write(ram.data, mode, addr & ram.mask, word);
|
||||||
if((addr & 0x0e000000) == 0x0e000000) {
|
if(has_flashrom) return flashrom.write(addr, word);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cartridge::Cartridge() {
|
Cartridge::Cartridge() {
|
||||||
|
@@ -28,13 +28,17 @@ struct Cartridge : property<Cartridge> {
|
|||||||
uint8* ram_data();
|
uint8* ram_data();
|
||||||
unsigned ram_size();
|
unsigned ram_size();
|
||||||
|
|
||||||
uint32 read(uint8* data, uint32 addr, uint32 size);
|
auto read(uint8* data, unsigned mode, uint32 addr) -> uint32;
|
||||||
void write(uint8* data, uint32 addr, uint32 size, uint32 word);
|
auto write(uint8* data, unsigned mode, uint32 addr, uint32 word) -> void;
|
||||||
|
|
||||||
uint32 read(uint32 addr, uint32 size);
|
auto rom_read(unsigned mode, uint32 addr) -> uint32;
|
||||||
void write(uint32 addr, uint32 size, uint32 word);
|
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&);
|
void serialize(serializer&);
|
||||||
|
|
||||||
Cartridge();
|
Cartridge();
|
||||||
~Cartridge();
|
~Cartridge();
|
||||||
};
|
};
|
||||||
|
82
gba/cpu/bus.cpp
Normal file
82
gba/cpu/bus.cpp
Normal 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;
|
||||||
|
}
|
@@ -4,6 +4,7 @@ namespace GameBoyAdvance {
|
|||||||
|
|
||||||
#include "registers.cpp"
|
#include "registers.cpp"
|
||||||
#include "prefetch.cpp"
|
#include "prefetch.cpp"
|
||||||
|
#include "bus.cpp"
|
||||||
#include "mmio.cpp"
|
#include "mmio.cpp"
|
||||||
#include "memory.cpp"
|
#include "memory.cpp"
|
||||||
#include "dma.cpp"
|
#include "dma.cpp"
|
||||||
@@ -63,72 +64,6 @@ auto CPU::step(unsigned clocks) -> void {
|
|||||||
sync_step(clocks);
|
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 {
|
auto CPU::sync_step(unsigned clocks) -> void {
|
||||||
ppu.clock -= clocks;
|
ppu.clock -= clocks;
|
||||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||||
@@ -187,6 +122,8 @@ auto CPU::power() -> void {
|
|||||||
pending.dma.hblank = 0;
|
pending.dma.hblank = 0;
|
||||||
pending.dma.hdma = 0;
|
pending.dma.hdma = 0;
|
||||||
|
|
||||||
|
active.dma = false;
|
||||||
|
|
||||||
for(unsigned n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA
|
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 = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers
|
||||||
for(unsigned n = 0x120; n <= 0x12b; n++) bus.mmio[n] = this; //Serial
|
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 main() -> void;
|
||||||
|
|
||||||
auto step(unsigned clocks) -> void override;
|
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 sync_step(unsigned clocks) -> void;
|
||||||
auto keypad_run() -> void;
|
auto keypad_run() -> void;
|
||||||
@@ -25,15 +22,21 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||||||
CPU();
|
CPU();
|
||||||
~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
|
//mmio.cpp
|
||||||
auto read(uint32 addr) -> uint8;
|
auto read(uint32 addr) -> uint8;
|
||||||
auto write(uint32 addr, uint8 byte) -> void;
|
auto write(uint32 addr, uint8 byte) -> void;
|
||||||
|
|
||||||
auto iwram_read(uint32 addr, uint32 size) -> uint32;
|
auto iwram_read(unsigned mode, uint32 addr) -> uint32;
|
||||||
auto iwram_write(uint32 addr, uint32 size, uint32 word) -> void;
|
auto iwram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||||
|
|
||||||
auto ewram_read(uint32 addr, uint32 size) -> uint32;
|
auto ewram_read(unsigned mode, uint32 addr) -> uint32;
|
||||||
auto ewram_write(uint32 addr, uint32 size, uint32 word) -> void;
|
auto ewram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||||
|
|
||||||
//dma.cpp
|
//dma.cpp
|
||||||
auto dma_run() -> void;
|
auto dma_run() -> void;
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
auto CPU::dma_run() -> void {
|
auto CPU::dma_run() -> void {
|
||||||
|
active.dma = true;
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
bool transferred = false;
|
bool transferred = false;
|
||||||
for(auto n : range(4)) {
|
for(auto n : range(4)) {
|
||||||
@@ -13,20 +15,26 @@ auto CPU::dma_run() -> void {
|
|||||||
}
|
}
|
||||||
if(!transferred) break;
|
if(!transferred) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
active.dma = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
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 seek = dma.control.size ? 4 : 2;
|
||||||
|
unsigned mode = dma.control.size ? Word : Half;
|
||||||
|
mode |= dma.run.length == dma.length ? Nonsequential : Sequential;
|
||||||
|
|
||||||
if(mode == Nonsequential) {
|
if(mode & Nonsequential) {
|
||||||
idle();
|
if((dma.source & 0x0800'0000) && (dma.target & 0x0800'0000)) {
|
||||||
idle();
|
//ROM -> ROM transfer
|
||||||
|
} else {
|
||||||
|
idle();
|
||||||
|
idle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 word = bus_read(dma.run.source, size, mode);
|
uint32 word = bus_read(mode, dma.run.source);
|
||||||
bus_write(dma.run.target, size, mode, word);
|
bus_write(mode, dma.run.target, word);
|
||||||
|
|
||||||
switch(dma.control.sourcemode) {
|
switch(dma.control.sourcemode) {
|
||||||
case 0: dma.run.source += seek; break;
|
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(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(mode & Word) return iwram_read(Half, addr &~ 2) << 0 | iwram_read(Half, addr | 2) << 16;
|
||||||
if(size == Half) return iwram_read(addr &~ 1, Byte) << 0 | iwram_read(addr | 1, Byte) << 8;
|
if(mode & Half) return iwram_read(Byte, addr &~ 1) << 0 | iwram_read(Byte, addr | 1) << 8;
|
||||||
|
|
||||||
return iwram[addr & 0x7fff];
|
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(regs.memory.control.disable) return;
|
||||||
|
|
||||||
if(size == Word) {
|
if(mode & Word) {
|
||||||
iwram_write(addr &~2, Half, word >> 0);
|
iwram_write(Half, addr &~2, word >> 0);
|
||||||
iwram_write(addr | 2, Half, word >> 16);
|
iwram_write(Half, addr | 2, word >> 16);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(size == Half) {
|
if(mode & Half) {
|
||||||
iwram_write(addr &~1, Byte, word >> 0);
|
iwram_write(Byte, addr &~1, word >> 0);
|
||||||
iwram_write(addr | 1, Byte, word >> 8);
|
iwram_write(Byte, addr | 1, word >> 8);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
iwram[addr & 0x7fff] = word;
|
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.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(mode & Word) return ewram_read(Half, addr &~ 2) << 0 | ewram_read(Half, addr | 2) << 16;
|
||||||
if(size == Half) return ewram_read(addr &~ 1, Byte) << 0 | ewram_read(addr | 1, Byte) << 8;
|
if(mode & Half) return ewram_read(Byte, addr &~ 1) << 0 | ewram_read(Byte, addr | 1) << 8;
|
||||||
|
|
||||||
return ewram[addr & 0x3ffff];
|
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.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) {
|
if(mode & Word) {
|
||||||
ewram_write(addr &~2, Half, word >> 0);
|
ewram_write(Half, addr &~2, word >> 0);
|
||||||
ewram_write(addr | 2, Half, word >> 16);
|
ewram_write(Half, addr | 2, word >> 16);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(size == Half) {
|
if(mode & Half) {
|
||||||
ewram_write(addr &~1, Byte, word >> 0);
|
ewram_write(Byte, addr &~1, word >> 0);
|
||||||
ewram_write(addr | 1, Byte, word >> 8);
|
ewram_write(Byte, addr | 1, word >> 8);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,43 +1,37 @@
|
|||||||
auto CPU::prefetch_stall() -> void {
|
auto CPU::prefetch_sync(uint32 addr) -> void {
|
||||||
prefetch.stalled = true;
|
if(addr == prefetch.addr) return;
|
||||||
}
|
|
||||||
|
|
||||||
auto CPU::prefetch_start(uint32 addr) -> void {
|
prefetch.addr = addr;
|
||||||
prefetch.stalled = false;
|
prefetch.load = addr;
|
||||||
prefetch.slots = 0;
|
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
|
||||||
prefetch.input = 0;
|
|
||||||
prefetch.output = 0;
|
|
||||||
prefetch.addr = addr;
|
|
||||||
prefetch.wait = bus.wait(addr, Half, Nonsequential);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::prefetch_step(unsigned clocks) -> void {
|
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;
|
prefetch.wait -= clocks;
|
||||||
while(prefetch.wait <= 0) {
|
while(!prefetch.full() && prefetch.wait <= 0) {
|
||||||
if(prefetch.slots < 8) {
|
prefetch.slot[prefetch.load >> 1 & 7] = cartridge.rom_read(Half, prefetch.load);
|
||||||
prefetch.slot[prefetch.output++] = cartridge.read(prefetch.addr, Half);
|
prefetch.load += 2;
|
||||||
prefetch.slots++;
|
prefetch.wait += bus_wait(Half | Sequential, prefetch.load);
|
||||||
prefetch.addr += 2;
|
|
||||||
}
|
|
||||||
prefetch.wait += bus.wait(prefetch.addr, Half, Sequential);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::prefetch_wait() -> void {
|
auto CPU::prefetch_wait() -> void {
|
||||||
step(prefetch.wait);
|
if(!regs.wait.control.prefetch || prefetch.full()) return;
|
||||||
|
|
||||||
prefetch_step(prefetch.wait);
|
prefetch_step(prefetch.wait);
|
||||||
|
prefetch.wait = bus_wait(Half | Nonsequential, prefetch.load);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::prefetch_take() -> uint16 {
|
auto CPU::prefetch_read() -> uint16 {
|
||||||
if(prefetch.slots) {
|
if(prefetch.empty()) prefetch_step(prefetch.wait);
|
||||||
step(1);
|
else prefetch_step(1);
|
||||||
prefetch_step(1);
|
|
||||||
} else {
|
|
||||||
prefetch_wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
prefetch.slots--;
|
if(prefetch.full()) prefetch.wait = bus_wait(Half | Sequential, prefetch.load);
|
||||||
return prefetch.slot[prefetch.input++];
|
|
||||||
|
uint16 half = prefetch.slot[prefetch.addr >> 1 & 7];
|
||||||
|
prefetch.addr += 2;
|
||||||
|
return half;
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,14 @@
|
|||||||
struct Prefetch {
|
struct {
|
||||||
uint16 slot[8] = {0};
|
uint16 slot[8] = {0};
|
||||||
uint3 input = 0;
|
uint32 addr = 0; //read location of slot buffer
|
||||||
uint3 output = 0;
|
uint32 load = 0; //write location of slot buffer
|
||||||
|
signed wait = 0; //number of clocks before next slot load
|
||||||
|
|
||||||
bool stalled = true;
|
auto empty() const { return addr == load; }
|
||||||
unsigned slots = 0;
|
auto full() const { return load - addr == 16; }
|
||||||
signed wait = 0;
|
|
||||||
uint32 addr = 0;
|
|
||||||
} prefetch;
|
} prefetch;
|
||||||
|
|
||||||
auto prefetch_stall() -> void;
|
auto prefetch_sync(uint32 addr) -> void;
|
||||||
auto prefetch_start(uint32 addr) -> void;
|
|
||||||
auto prefetch_step(unsigned clocks) -> void;
|
auto prefetch_step(unsigned clocks) -> void;
|
||||||
auto prefetch_wait() -> 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;
|
prefetch = (source >> 14) & 1;
|
||||||
gametype = (source >> 15) & 1;
|
gametype = (source >> 15) & 1;
|
||||||
swait[3] = nwait[3];
|
swait[3] = nwait[3];
|
||||||
|
|
||||||
cpu.prefetch.stalled = true;
|
|
||||||
|
|
||||||
return operator uint16();
|
return operator uint16();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,3 +5,7 @@ struct Pending {
|
|||||||
bool hdma;
|
bool hdma;
|
||||||
} dma;
|
} dma;
|
||||||
} pending;
|
} pending;
|
||||||
|
|
||||||
|
struct Active {
|
||||||
|
bool dma;
|
||||||
|
} active;
|
||||||
|
12
gba/gba.hpp
12
gba/gba.hpp
@@ -21,8 +21,16 @@ namespace GameBoyAdvance {
|
|||||||
#include <libco/libco.h>
|
#include <libco/libco.h>
|
||||||
|
|
||||||
namespace GameBoyAdvance {
|
namespace GameBoyAdvance {
|
||||||
enum : unsigned { Byte = 8, Half = 16, Word = 32 };
|
enum : unsigned { //mode flags for bus_read, bus_write:
|
||||||
enum : bool { Nonsequential = 0, Sequential = 1 };
|
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 {
|
struct Thread {
|
||||||
~Thread() {
|
~Thread() {
|
||||||
|
@@ -3,12 +3,11 @@
|
|||||||
namespace GameBoyAdvance {
|
namespace GameBoyAdvance {
|
||||||
|
|
||||||
#include "mmio.cpp"
|
#include "mmio.cpp"
|
||||||
#include "serialization.cpp"
|
|
||||||
Bus bus;
|
Bus bus;
|
||||||
|
|
||||||
struct UnmappedMemory : Memory {
|
struct UnmappedMemory : Memory {
|
||||||
auto read(uint32 addr, uint32 size) -> uint32 { return 0; }
|
auto read(unsigned mode, uint32 addr) -> uint32 override { return 0; }
|
||||||
auto write(uint32 addr, uint32 size, uint32 word) -> void {}
|
auto write(unsigned mode, uint32 addr, uint32 word) -> void override {}
|
||||||
};
|
};
|
||||||
|
|
||||||
static UnmappedMemory unmappedMemory;
|
static UnmappedMemory unmappedMemory;
|
||||||
@@ -31,74 +30,6 @@ auto Bus::mirror(uint32 addr, uint32 size) -> uint32 {
|
|||||||
return base;
|
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 {
|
auto Bus::power() -> void {
|
||||||
for(auto n : range(0x400)) mmio[n] = &unmappedMemory;
|
for(auto n : range(0x400)) mmio[n] = &unmappedMemory;
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +1,21 @@
|
|||||||
struct Memory {
|
struct Memory {
|
||||||
virtual auto read(uint32 addr, uint32 size) -> uint32 = 0;
|
virtual auto read(unsigned mode, uint32 addr) -> uint32 = 0;
|
||||||
virtual auto write(uint32 addr, uint32 size, uint32 word) -> void = 0;
|
virtual auto write(unsigned mode, uint32 addr, uint32 word) -> void = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MMIO : Memory {
|
struct MMIO : Memory {
|
||||||
virtual auto read(uint32 addr) -> uint8 = 0;
|
virtual auto read(uint32 addr) -> uint8 = 0;
|
||||||
virtual auto write(uint32 addr, uint8 data) -> void = 0;
|
virtual auto write(uint32 addr, uint8 data) -> void = 0;
|
||||||
auto read(uint32 addr, uint32 size) -> uint32;
|
auto read(unsigned mode, uint32 addr) -> uint32 final;
|
||||||
auto write(uint32 addr, uint32 size, uint32 word) -> void;
|
auto write(unsigned mode, uint32 addr, uint32 word) -> void final;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Bus : Memory {
|
struct Bus {
|
||||||
static auto mirror(uint32 addr, uint32 size) -> uint32;
|
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 power() -> void;
|
||||||
|
|
||||||
auto serialize(serializer&) -> void;
|
Memory* mmio[0x400] = {nullptr};
|
||||||
|
|
||||||
Memory* mmio[0x400]{nullptr};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Bus bus;
|
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;
|
uint32 word = 0;
|
||||||
|
|
||||||
switch(size) {
|
if(mode & Word) {
|
||||||
case Word:
|
|
||||||
addr &= ~3;
|
addr &= ~3;
|
||||||
word |= read(addr + 0) << 0;
|
word |= read(addr + 0) << 0;
|
||||||
word |= read(addr + 1) << 8;
|
word |= read(addr + 1) << 8;
|
||||||
word |= read(addr + 2) << 16;
|
word |= read(addr + 2) << 16;
|
||||||
word |= read(addr + 3) << 24;
|
word |= read(addr + 3) << 24;
|
||||||
break;
|
} else if(mode & Half) {
|
||||||
case Half:
|
|
||||||
addr &= ~1;
|
addr &= ~1;
|
||||||
word |= read(addr + 0) << 0;
|
word |= read(addr + 0) << 0;
|
||||||
word |= read(addr + 1) << 8;
|
word |= read(addr + 1) << 8;
|
||||||
break;
|
} else if(mode & Byte) {
|
||||||
case Byte:
|
|
||||||
word |= read(addr + 0) << 0;
|
word |= read(addr + 0) << 0;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto MMIO::write(uint32 addr, uint32 size, uint32 word) -> void {
|
auto MMIO::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||||
switch(size) {
|
if(mode & Word) {
|
||||||
case Word:
|
|
||||||
addr &= ~3;
|
addr &= ~3;
|
||||||
write(addr + 0, word >> 0);
|
write(addr + 0, word >> 0);
|
||||||
write(addr + 1, word >> 8);
|
write(addr + 1, word >> 8);
|
||||||
write(addr + 2, word >> 16);
|
write(addr + 2, word >> 16);
|
||||||
write(addr + 3, word >> 24);
|
write(addr + 3, word >> 24);
|
||||||
break;
|
} else if(mode & Half) {
|
||||||
case Half:
|
|
||||||
addr &= ~1;
|
addr &= ~1;
|
||||||
write(addr + 0, word >> 0);
|
write(addr + 0, word >> 0);
|
||||||
write(addr + 1, word >> 8);
|
write(addr + 1, word >> 8);
|
||||||
break;
|
} else if(mode & Byte) {
|
||||||
case Byte:
|
|
||||||
write(addr + 0, word >> 0);
|
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 & 1) if(tx & 32) offset += 32 * 32;
|
||||||
if(bg.control.screensize & 2) if(ty & 32) offset += 32 * 32 * (1 + (bg.control.screensize & 1));
|
if(bg.control.screensize & 2) if(ty & 32) offset += 32 * 32 * (1 + (bg.control.screensize & 1));
|
||||||
offset = basemap + offset * 2;
|
offset = basemap + offset * 2;
|
||||||
uint16 mapdata = vram_read(offset, Half);
|
uint16 mapdata = vram_read(Half, offset);
|
||||||
|
|
||||||
tile.character = mapdata >> 0;
|
tile.character = mapdata >> 0;
|
||||||
tile.hflip = mapdata >> 10;
|
tile.hflip = mapdata >> 10;
|
||||||
@@ -57,12 +57,12 @@ void PPU::render_background_linear(Registers::Background& bg) {
|
|||||||
|
|
||||||
if(bg.control.colormode == 0) {
|
if(bg.control.colormode == 0) {
|
||||||
offset = basechr + tile.character * 32 + (py ^ (tile.vflip ? 7 : 0)) * 4;
|
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;
|
for(unsigned n = 0; n < 8; n++) data[n] = (word >> (n * 4)) & 15;
|
||||||
} else {
|
} else {
|
||||||
offset = basechr + tile.character * 64 + (py ^ (tile.vflip ? 7 : 0)) * 8;
|
offset = basechr + tile.character * 64 + (py ^ (tile.vflip ? 7 : 0)) * 8;
|
||||||
uint32 wordlo = vram_read(offset + 0, Word);
|
uint32 wordlo = vram_read(Word, offset + 0);
|
||||||
uint32 wordhi = vram_read(offset + 4, Word);
|
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[0 + n] = (wordlo >> (n * 8)) & 255;
|
||||||
for(unsigned n = 0; n < 4; n++) data[4 + n] = (wordhi >> (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 width = regs.control.bgmode == 5 ? 160 : 240;
|
||||||
unsigned height = regs.control.bgmode == 5 ? 128 : 160;
|
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) {
|
if(bg.control.mosaic == false || (regs.vcounter % (1 + regs.mosaic.bgvsize)) == 0) {
|
||||||
bg.hmosaic = bg.lx;
|
bg.hmosaic = bg.lx;
|
||||||
@@ -138,7 +138,7 @@ void PPU::render_background_bitmap(Registers::Background& bg) {
|
|||||||
|
|
||||||
if(px < width && py < height) {
|
if(px < width && py < height) {
|
||||||
unsigned offset = py * width + px;
|
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 || color) { //8bpp color 0 is transparent; 15bpp color is always opaque
|
||||||
if(depth == 0) color = pram[color];
|
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;
|
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
|
||||||
|
|
||||||
switch(size) {
|
if(mode & Word) {
|
||||||
case Word:
|
|
||||||
addr &= ~3;
|
addr &= ~3;
|
||||||
return vram[addr + 0] << 0 | vram[addr + 1] << 8 | vram[addr + 2] << 16 | vram[addr + 3] << 24;
|
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;
|
addr &= ~1;
|
||||||
return vram[addr + 0] << 0 | vram[addr + 1] << 8;
|
return vram[addr + 0] << 0 | vram[addr + 1] << 8;
|
||||||
case Byte:
|
} else if(mode & Byte) {
|
||||||
return vram[addr];
|
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;
|
addr &= (addr & 0x10000) ? 0x17fff : 0x0ffff;
|
||||||
|
|
||||||
switch(size) {
|
if(mode & Word) {
|
||||||
case Word:
|
|
||||||
addr &= ~3;
|
addr &= ~3;
|
||||||
vram[addr + 0] = word >> 0;
|
vram[addr + 0] = word >> 0;
|
||||||
vram[addr + 1] = word >> 8;
|
vram[addr + 1] = word >> 8;
|
||||||
vram[addr + 2] = word >> 16;
|
vram[addr + 2] = word >> 16;
|
||||||
vram[addr + 3] = word >> 24;
|
vram[addr + 3] = word >> 24;
|
||||||
break;
|
} else if(mode & Half) {
|
||||||
case Half:
|
|
||||||
addr &= ~1;
|
addr &= ~1;
|
||||||
vram[addr + 0] = word >> 0;
|
vram[addr + 0] = word >> 0;
|
||||||
vram[addr + 1] = word >> 8;
|
vram[addr + 1] = word >> 8;
|
||||||
break;
|
} else if(mode & Byte) {
|
||||||
case Byte:
|
|
||||||
addr &= ~1;
|
addr &= ~1;
|
||||||
vram[addr + 0] = word;
|
vram[addr + 0] = word;
|
||||||
vram[addr + 1] = word;
|
vram[addr + 1] = word;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 PPU::pram_read(uint32 addr, uint32 size) {
|
auto PPU::pram_read(unsigned mode, uint32 addr) -> uint32 {
|
||||||
if(size == Word) return pram_read(addr & ~2, Half) << 0 | pram_read(addr | 2, Half) << 16;
|
if(mode & Word) return pram_read(Half, addr & ~2) << 0 | pram_read(Half, addr | 2) << 16;
|
||||||
if(size == Byte) return pram_read(addr, Half) >> ((addr & 1) * 8);
|
if(mode & Byte) return pram_read(Half, addr) >> ((addr & 1) * 8);
|
||||||
return pram[addr >> 1 & 511];
|
return pram[addr >> 1 & 511];
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::pram_write(uint32 addr, uint32 size, uint32 word) {
|
auto PPU::pram_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||||
if(size == Word) {
|
if(mode & Word) {
|
||||||
pram_write(addr & ~2, Half, word >> 0);
|
pram_write(Half, addr & ~2, word >> 0);
|
||||||
pram_write(addr | 2, Half, word >> 16);
|
pram_write(Half, addr | 2, word >> 16);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(size == Byte) {
|
if(mode & Byte) {
|
||||||
return pram_write(addr, Half, word << 8 | word << 0);
|
return pram_write(Half, addr, word << 8 | word << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pram[addr >> 1 & 511] = word & 0x7fff;
|
pram[addr >> 1 & 511] = word & 0x7fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 PPU::oam_read(uint32 addr, uint32 size) {
|
auto PPU::oam_read(unsigned mode, uint32 addr) -> uint32 {
|
||||||
if(size == Word) return oam_read(addr & ~2, Half) << 0 | oam_read(addr | 2, Half) << 16;
|
if(mode & Word) return oam_read(Half, addr & ~2) << 0 | oam_read(Half, addr | 2) << 16;
|
||||||
if(size == Byte) return oam_read(addr, Half) >> ((addr & 1) * 8);
|
if(mode & Byte) return oam_read(Half, addr) >> ((addr & 1) * 8);
|
||||||
|
|
||||||
auto& obj = object[addr >> 3 & 127];
|
auto& obj = object[addr >> 3 & 127];
|
||||||
auto& par = objectparam[addr >> 5 & 31];
|
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) {
|
auto PPU::oam_write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||||
if(size == Word) {
|
if(mode & Word) {
|
||||||
oam_write(addr & ~2, Half, word >> 0);
|
oam_write(Half, addr & ~2, word >> 0);
|
||||||
oam_write(addr | 2, Half, word >> 16);
|
oam_write(Half, addr | 2, word >> 16);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(size == Byte) {
|
if(mode & Byte) {
|
||||||
return oam_write(addr, Half, word << 8 | word << 0);
|
return oam_write(Half, addr, word << 8 | word << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& obj = object[addr >> 3 & 127];
|
auto& obj = object[addr >> 3 & 127];
|
||||||
|
@@ -16,14 +16,14 @@ struct PPU : Thread, MMIO {
|
|||||||
uint8 read(uint32 addr);
|
uint8 read(uint32 addr);
|
||||||
void write(uint32 addr, uint8 byte);
|
void write(uint32 addr, uint8 byte);
|
||||||
|
|
||||||
uint32 vram_read(uint32 addr, uint32 size);
|
auto vram_read(unsigned mode, uint32 addr) -> uint32;
|
||||||
void vram_write(uint32 addr, uint32 size, uint32 word);
|
auto vram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||||
|
|
||||||
uint32 pram_read(uint32 addr, uint32 size);
|
auto pram_read(unsigned mode, uint32 addr) -> uint32;
|
||||||
void pram_write(uint32 addr, uint32 size, uint32 word);
|
auto pram_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||||
|
|
||||||
uint32 oam_read(uint32 addr, uint32 size);
|
auto oam_read(unsigned mode, uint32 addr) -> uint32;
|
||||||
void oam_write(uint32 addr, uint32 size, uint32 word);
|
auto oam_write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||||
|
|
||||||
void render_backgrounds();
|
void render_backgrounds();
|
||||||
void render_background_linear(Registers::Background&);
|
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() {
|
BIOS::BIOS() {
|
||||||
data = new uint8[size = 16384]();
|
size = 16384;
|
||||||
|
data = new uint8[size]();
|
||||||
}
|
}
|
||||||
|
|
||||||
BIOS::~BIOS() {
|
BIOS::~BIOS() {
|
||||||
delete[] data;
|
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);
|
cpu.serialize(s);
|
||||||
ppu.serialize(s);
|
ppu.serialize(s);
|
||||||
apu.serialize(s);
|
apu.serialize(s);
|
||||||
bus.serialize(s);
|
|
||||||
player.serialize(s);
|
player.serialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,15 +3,15 @@ enum class Input : unsigned {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BIOS : Memory {
|
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();
|
||||||
~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 {
|
struct System {
|
||||||
|
@@ -15,6 +15,7 @@ auto ARM::power() -> void {
|
|||||||
processor.power();
|
processor.power();
|
||||||
vector(0x00000000, Processor::Mode::SVC);
|
vector(0x00000000, Processor::Mode::SVC);
|
||||||
pipeline.reload = true;
|
pipeline.reload = true;
|
||||||
|
pipeline.nonsequential = true;
|
||||||
crash = false;
|
crash = false;
|
||||||
r(15).modify = [&] {
|
r(15).modify = [&] {
|
||||||
pipeline.reload = true;
|
pipeline.reload = true;
|
||||||
@@ -29,42 +30,41 @@ auto ARM::exec() -> void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto ARM::idle() -> void {
|
auto ARM::idle() -> void {
|
||||||
processor.nonsequential = true;
|
pipeline.nonsequential = true;
|
||||||
bus_idle(r(15));
|
return bus_idle();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ARM::read(uint32 addr, uint32 size, bool mode) -> uint32 {
|
auto ARM::read(unsigned mode, uint32 addr) -> uint32 {
|
||||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
return bus_read(mode, addr);
|
||||||
return bus_read(addr, size, mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ARM::load(uint32 addr, uint32 size, bool mode) -> uint32 {
|
auto ARM::load(unsigned mode, uint32 addr) -> uint32 {
|
||||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
pipeline.nonsequential = true;
|
||||||
uint32 word = bus_read(addr, size, mode);
|
uint32 word = bus_read(Load | mode, addr);
|
||||||
|
|
||||||
if(size == Half) { word &= 0xffff; word |= word << 16; }
|
if(mode & Half) { word &= 0xffff; word |= word << 16; }
|
||||||
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
if(mode & Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
||||||
|
|
||||||
word = ror(word, 8 * (addr & 3));
|
word = ror(word, 8 * (addr & 3));
|
||||||
idle();
|
idle();
|
||||||
|
|
||||||
if(size == Half) word &= 0xffff;
|
if(mode & Half) word &= 0xffff;
|
||||||
if(size == Byte) word &= 0xff;
|
if(mode & Byte) word &= 0xff;
|
||||||
return word;
|
return word;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ARM::write(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
|
auto ARM::write(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
pipeline.nonsequential = true;
|
||||||
return bus_write(addr, size, mode, word);
|
return bus_write(mode, addr, word);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ARM::store(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
|
auto ARM::store(unsigned mode, uint32 addr, uint32 word) -> void {
|
||||||
if(size == Half) { word &= 0xffff; word |= word << 16; }
|
pipeline.nonsequential = true;
|
||||||
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
|
||||||
|
|
||||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
if(mode & Half) { word &= 0xffff; word |= word << 16; }
|
||||||
bus_write(addr, size, mode, word);
|
if(mode & Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
||||||
processor.nonsequential = true;
|
|
||||||
|
return bus_write(Store | mode, addr, word);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ARM::vector(uint32 addr, Processor::Mode mode) -> void {
|
auto ARM::vector(uint32 addr, Processor::Mode mode) -> void {
|
||||||
|
@@ -4,12 +4,20 @@
|
|||||||
namespace Processor {
|
namespace Processor {
|
||||||
|
|
||||||
//Supported Models:
|
//Supported Models:
|
||||||
//* ARMv3 (ST018)
|
//* ARMv3 (ARM60)
|
||||||
//* ARMv4 (ARM7TDMI)
|
//* ARMv4T (ARM7TDMI)
|
||||||
|
|
||||||
struct ARM {
|
struct ARM {
|
||||||
enum : unsigned { Byte = 8, Half = 16, Word = 32 };
|
enum : unsigned { //mode flags for bus_read, bus_write:
|
||||||
enum : bool { Nonsequential = 0, Sequential = 1 };
|
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 "registers.hpp"
|
||||||
#include "instructions-arm.hpp"
|
#include "instructions-arm.hpp"
|
||||||
@@ -17,18 +25,18 @@ struct ARM {
|
|||||||
#include "disassembler.hpp"
|
#include "disassembler.hpp"
|
||||||
|
|
||||||
virtual auto step(unsigned clocks) -> void = 0;
|
virtual auto step(unsigned clocks) -> void = 0;
|
||||||
virtual auto bus_idle(uint32 addr) -> void = 0;
|
virtual auto bus_idle() -> void = 0;
|
||||||
virtual auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32 = 0;
|
virtual auto bus_read(unsigned mode, uint32 addr) -> uint32 = 0;
|
||||||
virtual auto bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void = 0;
|
virtual auto bus_write(unsigned mode, uint32 addr, uint32 word) -> void = 0;
|
||||||
|
|
||||||
//arm.cpp
|
//arm.cpp
|
||||||
auto power() -> void;
|
auto power() -> void;
|
||||||
auto exec() -> void;
|
auto exec() -> void;
|
||||||
auto idle() -> void;
|
auto idle() -> void;
|
||||||
auto read(uint32 addr, uint32 size, bool mode) -> uint32;
|
auto read(unsigned mode, uint32 addr) -> uint32;
|
||||||
auto load(uint32 addr, uint32 size, bool mode) -> uint32;
|
auto load(unsigned mode, uint32 addr) -> uint32;
|
||||||
auto write(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
|
auto write(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||||
auto store(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
|
auto store(unsigned mode, uint32 addr, uint32 word) -> void;
|
||||||
auto vector(uint32 addr, Processor::Mode mode) -> void;
|
auto vector(uint32 addr, Processor::Mode mode) -> void;
|
||||||
|
|
||||||
//algorithms.cpp
|
//algorithms.cpp
|
||||||
@@ -44,14 +52,15 @@ struct ARM {
|
|||||||
auto rrx(uint32 source) -> uint32;
|
auto rrx(uint32 source) -> uint32;
|
||||||
|
|
||||||
//step.cpp
|
//step.cpp
|
||||||
|
auto pipeline_step() -> void;
|
||||||
auto arm_step() -> void;
|
auto arm_step() -> void;
|
||||||
auto thumb_step() -> void;
|
auto thumb_step() -> void;
|
||||||
|
|
||||||
//serialization.cpp
|
//serialization.cpp
|
||||||
auto serialize(serializer&) -> void;
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
bool trace{false};
|
bool trace = false;
|
||||||
uintmax_t instructions{0};
|
uintmax_t instructions = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||||||
|
|
||||||
string output{hex<8>(pc), " "};
|
string output{hex<8>(pc), " "};
|
||||||
|
|
||||||
uint32 instruction = read(pc & ~3, Word, Nonsequential);
|
uint32 instruction = read(Word | Nonsequential, pc & ~3);
|
||||||
output.append(hex<8>(instruction), " ");
|
output.append(hex<8>(instruction), " ");
|
||||||
|
|
||||||
//multiply()
|
//multiply()
|
||||||
@@ -134,7 +134,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||||||
if(pre == 1) output.append("]");
|
if(pre == 1) output.append("]");
|
||||||
if(pre == 0 || writeback == 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;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,8 +184,8 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||||||
if(pre == 1) output.append("]");
|
if(pre == 1) output.append("]");
|
||||||
if(pre == 0 || writeback == 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 == 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(pc + 8 + (up ? +immediate : -immediate), Byte, Nonsequential)));
|
if(rn == 15 && half == 0) output.append(" =0x", hex<2>(read(Byte | Nonsequential, pc + 8 + (up ? +immediate : -immediate))));
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +359,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||||||
if(pre == 1) output.append("]");
|
if(pre == 1) output.append("]");
|
||||||
if(pre == 0 || writeback == 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;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||||||
|
|
||||||
string output{hex<8>(pc), " "};
|
string output{hex<8>(pc), " "};
|
||||||
|
|
||||||
uint16 instruction = read(pc & ~1, Half, Nonsequential);
|
uint16 instruction = read(Half | Nonsequential, pc & ~1);
|
||||||
output.append(hex<4>(instruction), " ");
|
output.append(hex<4>(instruction), " ");
|
||||||
|
|
||||||
//adjust_register()
|
//adjust_register()
|
||||||
@@ -571,7 +571,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||||||
|
|
||||||
unsigned rm = ((pc + 4) & ~3) + displacement * 4;
|
unsigned rm = ((pc + 4) & ~3) + displacement * 4;
|
||||||
output.append("ldr ", registers[rd], ",[pc,#0x", hex<3>(rm), "]");
|
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;
|
return output;
|
||||||
}
|
}
|
||||||
@@ -740,7 +740,7 @@ auto ARM::disassemble_thumb_instruction(uint32 pc) -> string {
|
|||||||
//bl address
|
//bl address
|
||||||
if((instruction & 0xf800) == 0xf000) {
|
if((instruction & 0xf800) == 0xf000) {
|
||||||
uint11 offsethi = instruction;
|
uint11 offsethi = instruction;
|
||||||
instruction = read((pc & ~1) + 2, Half, Nonsequential);
|
instruction = read(Half | Nonsequential, (pc & ~1) + 2);
|
||||||
uint11 offsetlo = instruction;
|
uint11 offsetlo = instruction;
|
||||||
|
|
||||||
int22 displacement = (offsethi << 11) | (offsetlo << 0);
|
int22 displacement = (offsethi << 11) | (offsetlo << 0);
|
||||||
|
@@ -137,8 +137,8 @@ auto ARM::arm_op_memory_swap() {
|
|||||||
uint4 d = instruction() >> 12;
|
uint4 d = instruction() >> 12;
|
||||||
uint4 m = instruction();
|
uint4 m = instruction();
|
||||||
|
|
||||||
uint32 word = load(r(n), byte ? Byte : Word, Nonsequential);
|
uint32 word = load((byte ? Byte : Word) | Nonsequential, r(n));
|
||||||
store(r(n), byte ? Byte : Word, Nonsequential, r(m));
|
store((byte ? Byte : Word) | Nonsequential, r(n), r(m));
|
||||||
r(d) = word;
|
r(d) = word;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,8 +166,8 @@ auto ARM::arm_op_move_half_register() {
|
|||||||
uint32 rm = r(m);
|
uint32 rm = r(m);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||||
if(l == 1) r(d) = load(rn, Half, Nonsequential);
|
if(l == 1) r(d) = load(Half | Nonsequential, rn);
|
||||||
if(l == 0) store(rn, Half, Nonsequential, r(d));
|
if(l == 0) store(Half | Nonsequential, rn, r(d));
|
||||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||||
|
|
||||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
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);
|
uint8 immediate = (ih << 4) + (il << 0);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
||||||
if(l == 1) r(d) = load(rn, Half, Nonsequential);
|
if(l == 1) r(d) = load(Half | Nonsequential, rn);
|
||||||
if(l == 0) store(rn, Half, Nonsequential, r(d));
|
if(l == 0) store(Half | Nonsequential, rn, r(d));
|
||||||
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
||||||
|
|
||||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
if(pre == 0 || writeback == 1) r(n) = rn;
|
||||||
@@ -230,7 +230,7 @@ auto ARM::arm_op_load_register() {
|
|||||||
uint32 rm = r(m);
|
uint32 rm = r(m);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
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;
|
r(d) = half ? (int16)word : (int8)word;
|
||||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
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);
|
uint8 immediate = (ih << 4) + (il << 0);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + immediate : rn - immediate;
|
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;
|
r(d) = half ? (int16)word : (int8)word;
|
||||||
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
if(pre == 0) rn = up ? rn + immediate : rn - immediate;
|
||||||
|
|
||||||
@@ -437,8 +437,8 @@ auto ARM::arm_op_move_immediate_offset() {
|
|||||||
auto& rd = r(d);
|
auto& rd = r(d);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||||
if(l == 1) rd = load(rn, byte ? Byte : Word, Nonsequential);
|
if(l == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
|
||||||
if(l == 0) store(rn, byte ? Byte : Word, Nonsequential, rd);
|
if(l == 0) store((byte ? Byte : Word) | Nonsequential, rn, rd);
|
||||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||||
|
|
||||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
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(mode == 3) rm = rs ? ror(rm, rs) : rrx(rm);
|
||||||
|
|
||||||
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
if(pre == 1) rn = up ? rn + rm : rn - rm;
|
||||||
if(l == 1) rd = load(rn, byte ? Byte : Word, Nonsequential);
|
if(l == 1) rd = load((byte ? Byte : Word) | Nonsequential, rn);
|
||||||
if(l == 0) store(rn, byte ? Byte : Word, Nonsequential, rd);
|
if(l == 0) store((byte ? Byte : Word) | Nonsequential, rn, rd);
|
||||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||||
|
|
||||||
if(pre == 0 || writeback == 1) r(n) = rn;
|
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);
|
if(usr) processor.setMode(Processor::Mode::USR);
|
||||||
|
|
||||||
|
unsigned sequential = Nonsequential;
|
||||||
for(unsigned m = 0; m < 16; m++) {
|
for(unsigned m = 0; m < 16; m++) {
|
||||||
if(list & 1 << m) {
|
if(list & 1 << m) {
|
||||||
if(l == 1) r(m) = read(rn, Word, Nonsequential);
|
if(l == 1) r(m) = read(Word | sequential, rn);
|
||||||
if(l == 0) write(rn, Word, Nonsequential, r(m));
|
if(l == 0) write(Word | sequential, rn, r(m));
|
||||||
rn += 4;
|
rn += 4;
|
||||||
|
sequential = Sequential;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,7 +542,7 @@ auto ARM::arm_op_move_multiple() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
processor.nonsequential = true;
|
pipeline.nonsequential = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(writeback) {
|
if(writeback) {
|
||||||
|
@@ -145,7 +145,7 @@ auto ARM::thumb_op_load_literal() {
|
|||||||
uint8 displacement = instruction();
|
uint8 displacement = instruction();
|
||||||
|
|
||||||
unsigned rm = (r(15) & ~3) + displacement * 4;
|
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]
|
//(ld(r,s),str){b,h} rd,[rn,rm]
|
||||||
@@ -161,14 +161,14 @@ auto ARM::thumb_op_move_register_offset() {
|
|||||||
uint3 d = instruction() >> 0;
|
uint3 d = instruction() >> 0;
|
||||||
|
|
||||||
switch(opcode) {
|
switch(opcode) {
|
||||||
case 0: store(r(n) + r(m), Word, Nonsequential, r(d)); break; //STR
|
case 0: store(Word | Nonsequential, r(n) + r(m), r(d)); break; //STR
|
||||||
case 1: store(r(n) + r(m), Half, Nonsequential, r(d)); break; //STRH
|
case 1: store(Half | Nonsequential, r(n) + r(m), r(d)); break; //STRH
|
||||||
case 2: store(r(n) + r(m), Byte, Nonsequential, r(d)); break; //STRB
|
case 2: store(Byte | Nonsequential, r(n) + r(m), r(d)); break; //STRB
|
||||||
case 3: r(d) = (int8)load(r(n) + r(m), Byte, Nonsequential); break; //LDSB
|
case 3: r(d) = (int8)load(Byte | Nonsequential, r(n) + r(m)); break; //LDSB
|
||||||
case 4: r(d) = load(r(n) + r(m), Word, Nonsequential); break; //LDR
|
case 4: r(d) = load(Word | Nonsequential, r(n) + r(m)); break; //LDR
|
||||||
case 5: r(d) = load(r(n) + r(m), Half, Nonsequential); break; //LDRH
|
case 5: r(d) = load(Half | Nonsequential, r(n) + r(m)); break; //LDRH
|
||||||
case 6: r(d) = load(r(n) + r(m), Byte, Nonsequential); break; //LDRB
|
case 6: r(d) = load(Byte | Nonsequential, r(n) + r(m)); break; //LDRB
|
||||||
case 7: r(d) = (int16)load(r(n) + r(m), Half, Nonsequential); break; //LDSH
|
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 n = instruction() >> 3;
|
||||||
uint3 d = instruction() >> 0;
|
uint3 d = instruction() >> 0;
|
||||||
|
|
||||||
if(l == 1) r(d) = load(r(n) + offset * 4, Word, Nonsequential);
|
if(l == 1) r(d) = load(Word | Nonsequential, r(n) + offset * 4);
|
||||||
if(l == 0) store(r(n) + offset * 4, Word, Nonsequential, r(d));
|
if(l == 0) store(Word | Nonsequential, r(n) + offset * 4, r(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
//(ldr,str)b rd,[rn,#offset]
|
//(ldr,str)b rd,[rn,#offset]
|
||||||
@@ -200,8 +200,8 @@ auto ARM::thumb_op_move_byte_immediate() {
|
|||||||
uint3 n = instruction() >> 3;
|
uint3 n = instruction() >> 3;
|
||||||
uint3 d = instruction() >> 0;
|
uint3 d = instruction() >> 0;
|
||||||
|
|
||||||
if(l == 1) r(d) = load(r(n) + offset, Byte, Nonsequential);
|
if(l == 1) r(d) = load(Byte | Nonsequential, r(n) + offset);
|
||||||
if(l == 0) store(r(n) + offset, Byte, Nonsequential, r(d));
|
if(l == 0) store(Byte | Nonsequential, r(n) + offset, r(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
//(ldr,str)h rd,[rn,#offset]
|
//(ldr,str)h rd,[rn,#offset]
|
||||||
@@ -216,8 +216,8 @@ auto ARM::thumb_op_move_half_immediate() {
|
|||||||
uint3 n = instruction() >> 3;
|
uint3 n = instruction() >> 3;
|
||||||
uint3 d = instruction() >> 0;
|
uint3 d = instruction() >> 0;
|
||||||
|
|
||||||
if(l == 1) r(d) = load(r(n) + offset * 2, Half, Nonsequential);
|
if(l == 1) r(d) = load(Half | Nonsequential, r(n) + offset * 2);
|
||||||
if(l == 0) store(r(n) + offset * 2, Half, Nonsequential, r(d));
|
if(l == 0) store(Half | Nonsequential, r(n) + offset * 2, r(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
//(ldr,str) rd,[sp,#immediate]
|
//(ldr,str) rd,[sp,#immediate]
|
||||||
@@ -230,8 +230,8 @@ auto ARM::thumb_op_move_stack() {
|
|||||||
uint3 d = instruction() >> 8;
|
uint3 d = instruction() >> 8;
|
||||||
uint8 immediate = instruction();
|
uint8 immediate = instruction();
|
||||||
|
|
||||||
if(l == 1) r(d) = load(r(13) + immediate * 4, Word, Nonsequential);
|
if(l == 1) r(d) = load(Word | Nonsequential, r(13) + immediate * 4);
|
||||||
if(l == 0) store(r(13) + immediate * 4, Word, Nonsequential, r(d));
|
if(l == 0) store(Word | Nonsequential, r(13) + immediate * 4, r(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
//add rd,{pc,sp},#immediate
|
//add rd,{pc,sp},#immediate
|
||||||
@@ -275,24 +275,30 @@ auto ARM::thumb_op_stack_multiple() {
|
|||||||
if(l == 1) sp = r(13);
|
if(l == 1) sp = r(13);
|
||||||
if(l == 0) sp = r(13) - (bit::count(list) + branch) * 4;
|
if(l == 0) sp = r(13) - (bit::count(list) + branch) * 4;
|
||||||
|
|
||||||
|
unsigned sequential = Nonsequential;
|
||||||
for(unsigned m = 0; m < 8; m++) {
|
for(unsigned m = 0; m < 8; m++) {
|
||||||
if(list & 1 << m) {
|
if(list & 1 << m) {
|
||||||
if(l == 1) r(m) = read(sp, Word, Nonsequential); //POP
|
if(l == 1) r(m) = read(Word | sequential, sp); //POP
|
||||||
if(l == 0) write(sp, Word, Nonsequential, r(m)); //PUSH
|
if(l == 0) write(Word | sequential, sp, r(m)); //PUSH
|
||||||
sp += 4;
|
sp += 4;
|
||||||
|
sequential = Sequential;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(branch) {
|
if(branch) {
|
||||||
//note: ARMv5+ POP sets cpsr().t
|
//note: ARMv5+ POP sets cpsr().t
|
||||||
if(l == 1) r(15) = read(sp, Word, Nonsequential); //POP
|
if(l == 1) r(15) = read(Word | Nonsequential, sp); //POP
|
||||||
if(l == 0) write(sp, Word, Nonsequential, r(14)); //PUSH
|
if(l == 0) write(Word | Nonsequential, sp, r(14)); //PUSH
|
||||||
sp += 4;
|
sp += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(l == 1) idle();
|
if(l == 1) {
|
||||||
if(l == 1) r(13) += (bit::count(list) + branch) * 4;
|
idle();
|
||||||
if(l == 0) r(13) -= (bit::count(list) + branch) * 4;
|
r(13) += (bit::count(list) + branch) * 4;
|
||||||
|
} else {
|
||||||
|
pipeline.nonsequential = true;
|
||||||
|
r(13) -= (bit::count(list) + branch) * 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//(ldmia,stmia) rn!,{r...}
|
//(ldmia,stmia) rn!,{r...}
|
||||||
@@ -308,8 +314,8 @@ auto ARM::thumb_op_move_multiple() {
|
|||||||
|
|
||||||
for(unsigned m = 0; m < 8; m++) {
|
for(unsigned m = 0; m < 8; m++) {
|
||||||
if(list & 1 << m) {
|
if(list & 1 << m) {
|
||||||
if(l == 1) r(m) = read(rn, Word, Nonsequential); //LDMIA
|
if(l == 1) r(m) = read(Word | Nonsequential, rn); //LDMIA
|
||||||
if(l == 0) write(rn, Word, Nonsequential, r(m)); //STMIA
|
if(l == 0) write(Word | Nonsequential, rn, r(m)); //STMIA
|
||||||
rn += 4;
|
rn += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,6 @@ auto ARM::Processor::power() -> void {
|
|||||||
pc = 0;
|
pc = 0;
|
||||||
|
|
||||||
carryout = false;
|
carryout = false;
|
||||||
nonsequential = false;
|
|
||||||
irqline = false;
|
irqline = false;
|
||||||
|
|
||||||
cpsr = 0;
|
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
|
#endif
|
||||||
|
@@ -50,6 +50,7 @@ struct PSR {
|
|||||||
|
|
||||||
struct Pipeline {
|
struct Pipeline {
|
||||||
bool reload = false;
|
bool reload = false;
|
||||||
|
bool nonsequential = false;
|
||||||
|
|
||||||
struct Instruction {
|
struct Instruction {
|
||||||
uint32 address = 0;
|
uint32 address = 0;
|
||||||
@@ -106,7 +107,6 @@ struct Processor {
|
|||||||
GPR pc;
|
GPR pc;
|
||||||
PSR cpsr;
|
PSR cpsr;
|
||||||
bool carryout = false;
|
bool carryout = false;
|
||||||
bool nonsequential = false;
|
|
||||||
bool irqline = false;
|
bool irqline = false;
|
||||||
|
|
||||||
GPR* r[16] = {nullptr};
|
GPR* r[16] = {nullptr};
|
||||||
@@ -120,8 +120,6 @@ Processor processor;
|
|||||||
Pipeline pipeline;
|
Pipeline pipeline;
|
||||||
bool crash = false;
|
bool crash = false;
|
||||||
|
|
||||||
auto pipeline_step() -> void;
|
|
||||||
|
|
||||||
alwaysinline auto r(unsigned n) -> GPR& { return *processor.r[n]; }
|
alwaysinline auto r(unsigned n) -> GPR& { return *processor.r[n]; }
|
||||||
alwaysinline auto cpsr() -> PSR& { return processor.cpsr; }
|
alwaysinline auto cpsr() -> PSR& { return processor.cpsr; }
|
||||||
alwaysinline auto spsr() -> PSR& { return *processor.spsr; }
|
alwaysinline auto spsr() -> PSR& { return *processor.spsr; }
|
||||||
|
@@ -55,10 +55,10 @@ auto ARM::serialize(serializer& s) -> void {
|
|||||||
s.integer(processor.pc.data);
|
s.integer(processor.pc.data);
|
||||||
processor.cpsr.serialize(s);
|
processor.cpsr.serialize(s);
|
||||||
s.integer(processor.carryout);
|
s.integer(processor.carryout);
|
||||||
s.integer(processor.nonsequential);
|
|
||||||
s.integer(processor.irqline);
|
s.integer(processor.irqline);
|
||||||
|
|
||||||
s.integer(pipeline.reload);
|
s.integer(pipeline.reload);
|
||||||
|
s.integer(pipeline.nonsequential);
|
||||||
s.integer(pipeline.execute.address);
|
s.integer(pipeline.execute.address);
|
||||||
s.integer(pipeline.execute.instruction);
|
s.integer(pipeline.execute.instruction);
|
||||||
s.integer(pipeline.decode.address);
|
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 {
|
auto ARM::arm_step() -> void {
|
||||||
if(pipeline.reload) {
|
if(pipeline.reload) {
|
||||||
pipeline.reload = false;
|
pipeline.reload = false;
|
||||||
r(15).data &= ~3;
|
r(15).data &= ~3;
|
||||||
|
|
||||||
pipeline.fetch.address = r(15) & ~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();
|
pipeline_step();
|
||||||
}
|
}
|
||||||
@@ -61,7 +82,7 @@ auto ARM::thumb_step() -> void {
|
|||||||
r(15).data &= ~1;
|
r(15).data &= ~1;
|
||||||
|
|
||||||
pipeline.fetch.address = r(15) & ~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();
|
pipeline_step();
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
//ARMv3 (ARM6)
|
//ARMv3 (ARM60)
|
||||||
|
|
||||||
struct ArmDSP : Processor::ARM, Coprocessor {
|
struct ArmDSP : Processor::ARM, Coprocessor {
|
||||||
uint8* programROM;
|
uint8* programROM;
|
||||||
@@ -11,9 +11,9 @@ struct ArmDSP : Processor::ARM, Coprocessor {
|
|||||||
void enter();
|
void enter();
|
||||||
|
|
||||||
void step(unsigned clocks) override;
|
void step(unsigned clocks) override;
|
||||||
void bus_idle(uint32 addr) override;
|
void bus_idle() override;
|
||||||
uint32 bus_read(uint32 addr, uint32 size, bool mode) override;
|
uint32 bus_read(unsigned mode, uint32 addr) override;
|
||||||
void bus_write(uint32 addr, uint32 size, bool mode, uint32 word) override;
|
void bus_write(unsigned mode, uint32 addr, uint32 word) override;
|
||||||
|
|
||||||
uint8 mmio_read(unsigned addr);
|
uint8 mmio_read(unsigned addr);
|
||||||
void mmio_write(unsigned addr, uint8 data);
|
void mmio_write(unsigned addr, uint8 data);
|
||||||
|
@@ -3,32 +3,33 @@
|
|||||||
//note: timings are completely unverified
|
//note: timings are completely unverified
|
||||||
//due to the ST018 chip design (on-die ROM), testing is nearly impossible
|
//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);
|
step(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 ArmDSP::bus_read(uint32 addr, uint32 size, bool mode) {
|
uint32 ArmDSP::bus_read(unsigned mode, uint32 addr) {
|
||||||
step(1);
|
step(1);
|
||||||
|
|
||||||
static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) -> uint32 {
|
static auto memory = [&](const uint8 *memory, unsigned mode, uint32 addr) -> uint32 {
|
||||||
switch(size) {
|
if(mode & Word) {
|
||||||
case Word:
|
|
||||||
memory += addr & ~3;
|
memory += addr & ~3;
|
||||||
return memory[0] << 0 | memory[1] << 8 | memory[2] << 16 | memory[3] << 24;
|
return memory[0] << 0 | memory[1] << 8 | memory[2] << 16 | memory[3] << 24;
|
||||||
case Byte:
|
} else if(mode & Byte) {
|
||||||
return memory[addr];
|
return memory[addr];
|
||||||
|
} else {
|
||||||
|
return 0; //should never occur
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
switch(addr & 0xe0000000) {
|
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 0x20000000: return pipeline.fetch.instruction;
|
||||||
case 0x40000000: break;
|
case 0x40000000: break;
|
||||||
case 0x60000000: return 0x40404001;
|
case 0x60000000: return 0x40404001;
|
||||||
case 0x80000000: return pipeline.fetch.instruction;
|
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 0xc0000000: return pipeline.fetch.instruction;
|
||||||
case 0xe0000000: return memory(programRAM, addr & 0x3fff, size);
|
case 0xe0000000: return memory(programRAM, mode, addr & 0x3fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
addr &= 0xe000003f;
|
addr &= 0xe000003f;
|
||||||
@@ -44,25 +45,22 @@ uint32 ArmDSP::bus_read(uint32 addr, uint32 size, bool mode) {
|
|||||||
return bridge.status();
|
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);
|
step(1);
|
||||||
|
|
||||||
static auto memory = [](uint8 *memory, uint32 addr, uint32 size, uint32 word) {
|
static auto memory = [](uint8 *memory, unsigned mode, uint32 addr, uint32 word) {
|
||||||
switch(size) {
|
if(mode & Word) {
|
||||||
case Word:
|
|
||||||
memory += addr & ~3;
|
memory += addr & ~3;
|
||||||
*memory++ = word >> 0;
|
*memory++ = word >> 0;
|
||||||
*memory++ = word >> 8;
|
*memory++ = word >> 8;
|
||||||
*memory++ = word >> 16;
|
*memory++ = word >> 16;
|
||||||
*memory++ = word >> 24;
|
*memory++ = word >> 24;
|
||||||
break;
|
} else if(mode & Byte) {
|
||||||
case Byte:
|
|
||||||
memory += addr;
|
memory += addr;
|
||||||
*memory++ = word >> 0;
|
*memory++ = word >> 0;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -74,7 +72,7 @@ void ArmDSP::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) {
|
|||||||
case 0x80000000: return;
|
case 0x80000000: return;
|
||||||
case 0xa0000000: return;
|
case 0xa0000000: return;
|
||||||
case 0xc0000000: return;
|
case 0xc0000000: return;
|
||||||
case 0xe0000000: return memory(programRAM, addr & 0x3fff, size, word);
|
case 0xe0000000: return memory(programRAM, mode, addr & 0x3fff, word);
|
||||||
}
|
}
|
||||||
|
|
||||||
addr &= 0xe000003f;
|
addr &= 0xe000003f;
|
||||||
|
Reference in New Issue
Block a user