diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 977dc494..5e61f4bd 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -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/"; diff --git a/gba/cartridge/cartridge.cpp b/gba/cartridge/cartridge.cpp index 68d6d80e..39f699ff 100644 --- a/gba/cartridge/cartridge.cpp +++ b/gba/cartridge/cartridge.cpp @@ -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() { diff --git a/gba/cartridge/cartridge.hpp b/gba/cartridge/cartridge.hpp index 3e480c39..f0e697c3 100644 --- a/gba/cartridge/cartridge.hpp +++ b/gba/cartridge/cartridge.hpp @@ -28,13 +28,17 @@ struct Cartridge : property { 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(); }; diff --git a/gba/cpu/bus.cpp b/gba/cpu/bus.cpp new file mode 100644 index 00000000..b6421c6a --- /dev/null +++ b/gba/cpu/bus.cpp @@ -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; +} diff --git a/gba/cpu/cpu.cpp b/gba/cpu/cpu.cpp index 1eda3368..2352aa80 100644 --- a/gba/cpu/cpu.cpp +++ b/gba/cpu/cpu.cpp @@ -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 diff --git a/gba/cpu/cpu.hpp b/gba/cpu/cpu.hpp index 33818a0c..734d9503 100644 --- a/gba/cpu/cpu.hpp +++ b/gba/cpu/cpu.hpp @@ -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; diff --git a/gba/cpu/dma.cpp b/gba/cpu/dma.cpp index c31b30e0..97f18860 100644 --- a/gba/cpu/dma.cpp +++ b/gba/cpu/dma.cpp @@ -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; diff --git a/gba/cpu/memory.cpp b/gba/cpu/memory.cpp index 1059fbe1..aecef462 100644 --- a/gba/cpu/memory.cpp +++ b/gba/cpu/memory.cpp @@ -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; } diff --git a/gba/cpu/prefetch.cpp b/gba/cpu/prefetch.cpp index ed3ee42a..56d12e7c 100644 --- a/gba/cpu/prefetch.cpp +++ b/gba/cpu/prefetch.cpp @@ -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; } diff --git a/gba/cpu/prefetch.hpp b/gba/cpu/prefetch.hpp index cd68acba..9b2e79cc 100644 --- a/gba/cpu/prefetch.hpp +++ b/gba/cpu/prefetch.hpp @@ -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; diff --git a/gba/cpu/registers.cpp b/gba/cpu/registers.cpp index aebc7b99..fff279ca 100644 --- a/gba/cpu/registers.cpp +++ b/gba/cpu/registers.cpp @@ -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(); } diff --git a/gba/cpu/state.hpp b/gba/cpu/state.hpp index 4926a3d8..f43c8340 100644 --- a/gba/cpu/state.hpp +++ b/gba/cpu/state.hpp @@ -5,3 +5,7 @@ struct Pending { bool hdma; } dma; } pending; + +struct Active { + bool dma; +} active; diff --git a/gba/gba.hpp b/gba/gba.hpp index 6336ea5a..19ec42b6 100644 --- a/gba/gba.hpp +++ b/gba/gba.hpp @@ -21,8 +21,16 @@ namespace GameBoyAdvance { #include 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() { diff --git a/gba/memory/memory.cpp b/gba/memory/memory.cpp index 16726c74..d5b1d8e0 100644 --- a/gba/memory/memory.cpp +++ b/gba/memory/memory.cpp @@ -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; } diff --git a/gba/memory/memory.hpp b/gba/memory/memory.hpp index f1852cea..ec9b9434 100644 --- a/gba/memory/memory.hpp +++ b/gba/memory/memory.hpp @@ -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; diff --git a/gba/memory/mmio.cpp b/gba/memory/mmio.cpp index 8ad6e897..29b8cbe7 100644 --- a/gba/memory/mmio.cpp +++ b/gba/memory/mmio.cpp @@ -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; } } diff --git a/gba/memory/serialization.cpp b/gba/memory/serialization.cpp deleted file mode 100644 index 1987ee2c..00000000 --- a/gba/memory/serialization.cpp +++ /dev/null @@ -1,2 +0,0 @@ -auto Bus::serialize(serializer& s) -> void { -} diff --git a/gba/ppu/background.cpp b/gba/ppu/background.cpp index 79515179..46178473 100644 --- a/gba/ppu/background.cpp +++ b/gba/ppu/background.cpp @@ -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]; diff --git a/gba/ppu/memory.cpp b/gba/ppu/memory.cpp index de319fcf..d87e5638 100644 --- a/gba/ppu/memory.cpp +++ b/gba/ppu/memory.cpp @@ -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]; diff --git a/gba/ppu/ppu.hpp b/gba/ppu/ppu.hpp index 7d801ebd..bc017fb1 100644 --- a/gba/ppu/ppu.hpp +++ b/gba/ppu/ppu.hpp @@ -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&); diff --git a/gba/system/bios.cpp b/gba/system/bios.cpp index 9f5514dc..3aebf873 100644 --- a/gba/system/bios.cpp +++ b/gba/system/bios.cpp @@ -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 { +} diff --git a/gba/system/serialization.cpp b/gba/system/serialization.cpp index a8d648e8..f3f044b8 100644 --- a/gba/system/serialization.cpp +++ b/gba/system/serialization.cpp @@ -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); } diff --git a/gba/system/system.hpp b/gba/system/system.hpp index 7e0686d2..6e82244d 100644 --- a/gba/system/system.hpp +++ b/gba/system/system.hpp @@ -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 { diff --git a/processor/arm/arm.cpp b/processor/arm/arm.cpp index 59656fd7..afce5e75 100644 --- a/processor/arm/arm.cpp +++ b/processor/arm/arm.cpp @@ -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 { diff --git a/processor/arm/arm.hpp b/processor/arm/arm.hpp index e9a93d15..3dcfee07 100644 --- a/processor/arm/arm.hpp +++ b/processor/arm/arm.hpp @@ -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; }; } diff --git a/processor/arm/disassembler.cpp b/processor/arm/disassembler.cpp index a8ac573e..f08d36e1 100644 --- a/processor/arm/disassembler.cpp +++ b/processor/arm/disassembler.cpp @@ -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); diff --git a/processor/arm/instructions-arm.cpp b/processor/arm/instructions-arm.cpp index 21a5de5a..27d8016d 100644 --- a/processor/arm/instructions-arm.cpp +++ b/processor/arm/instructions-arm.cpp @@ -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) { diff --git a/processor/arm/instructions-thumb.cpp b/processor/arm/instructions-thumb.cpp index a8587389..83f27d80 100644 --- a/processor/arm/instructions-thumb.cpp +++ b/processor/arm/instructions-thumb.cpp @@ -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; } } diff --git a/processor/arm/registers.cpp b/processor/arm/registers.cpp index efcf1621..da91d6f3 100644 --- a/processor/arm/registers.cpp +++ b/processor/arm/registers.cpp @@ -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 diff --git a/processor/arm/registers.hpp b/processor/arm/registers.hpp index 115268d9..ab332ced 100644 --- a/processor/arm/registers.hpp +++ b/processor/arm/registers.hpp @@ -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; } diff --git a/processor/arm/serialization.cpp b/processor/arm/serialization.cpp index cbf4ab6c..59c65a4d 100644 --- a/processor/arm/serialization.cpp +++ b/processor/arm/serialization.cpp @@ -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); diff --git a/processor/arm/step.cpp b/processor/arm/step.cpp index c070f515..c88bbf11 100644 --- a/processor/arm/step.cpp +++ b/processor/arm/step.cpp @@ -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(); } diff --git a/sfc/chip/armdsp/armdsp.hpp b/sfc/chip/armdsp/armdsp.hpp index 30ca130f..019c31aa 100644 --- a/sfc/chip/armdsp/armdsp.hpp +++ b/sfc/chip/armdsp/armdsp.hpp @@ -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); diff --git a/sfc/chip/armdsp/memory.cpp b/sfc/chip/armdsp/memory.cpp index 8a57d6ca..cfd8a304 100644 --- a/sfc/chip/armdsp/memory.cpp +++ b/sfc/chip/armdsp/memory.cpp @@ -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;