Update to v094r32 release.

byuu says:

Lots more timing improvements to GBA emulation. We're now ahead of
everything but mGBA.

Mario & Luigi is still hanging in battles, so I guess my prefetch
simulation isn't as good as Cydrak's previous attempt, no surprise.
This commit is contained in:
Tim Allen 2015-06-27 12:38:08 +10:00
parent ea02f1e36a
commit 169e400437
26 changed files with 226 additions and 163 deletions

View File

@ -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.31"; static const string Version = "094.32";
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/";

View File

@ -3,6 +3,7 @@
namespace GameBoyAdvance { namespace GameBoyAdvance {
#include "registers.cpp" #include "registers.cpp"
#include "prefetch.cpp"
#include "mmio.cpp" #include "mmio.cpp"
#include "memory.cpp" #include "memory.cpp"
#include "dma.cpp" #include "dma.cpp"
@ -71,18 +72,40 @@ auto CPU::sync_step(unsigned clocks) -> void {
} }
auto CPU::bus_idle(uint32 addr) -> void { auto CPU::bus_idle(uint32 addr) -> void {
if(regs.wait.control.prefetch) prefetch_run();
step(1); step(1);
return bus.idle(addr);
} }
auto CPU::bus_read(uint32 addr, uint32 size) -> uint32 { auto CPU::bus_read(uint32 addr, uint32 size, bool mode) -> uint32 {
step(bus.wait(addr, size)); if(regs.wait.control.prefetch) {
return bus.read(addr, size); if((addr & 0x0fffffff) >= 0x08000000 && (addr & 0x0fffffff) <= 0x0dffffff) {
if(auto word = prefetch_read(addr, size)) {
step(1 + (size == Word));
return *word;
}
}
}
unsigned wait = bus.wait(addr, size, mode);
unsigned word = bus.read(addr, size);
step(wait);
return word;
} }
auto CPU::bus_write(uint32 addr, uint32 size, uint32 word) -> void { auto CPU::bus_load(uint32 addr, uint32 size, bool mode) -> uint32 {
step(bus.wait(addr, size)); if(regs.wait.control.prefetch) prefetch_run();
return bus.write(addr, size, word); return bus_read(addr, size, mode);
}
auto CPU::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
unsigned wait = bus.wait(addr, size, mode);
step(wait);
bus.write(addr, size, word);
}
auto CPU::bus_store(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
if(regs.wait.control.prefetch) prefetch_run();
return bus_write(addr, size, mode, word);
} }
auto CPU::keypad_run() -> void { auto CPU::keypad_run() -> void {
@ -118,6 +141,7 @@ auto CPU::power() -> void {
for(auto& timer : regs.timer) { for(auto& timer : regs.timer) {
timer.period = 0; timer.period = 0;
timer.reload = 0; timer.reload = 0;
timer.pending = false;
timer.control = 0; timer.control = 0;
} }
regs.keypad.control = 0; regs.keypad.control = 0;

View File

@ -1,7 +1,11 @@
struct CPU : Processor::ARM, Thread, MMIO { struct CPU : Processor::ARM, Thread, MMIO {
using ARM::read;
using ARM::write;
uint8* iwram = nullptr; uint8* iwram = nullptr;
uint8* ewram = nullptr; uint8* ewram = nullptr;
#include "registers.hpp" #include "registers.hpp"
#include "prefetch.hpp"
#include "state.hpp" #include "state.hpp"
//cpu.cpp //cpu.cpp
@ -12,8 +16,10 @@ struct CPU : Processor::ARM, Thread, MMIO {
auto sync_step(unsigned clocks) -> void; auto sync_step(unsigned clocks) -> void;
auto bus_idle(uint32 addr) -> void; auto bus_idle(uint32 addr) -> void;
auto bus_read(uint32 addr, uint32 size) -> uint32; auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32;
auto bus_write(uint32 addr, uint32 size, uint32 word) -> void; auto bus_load(uint32 addr, uint32 size, bool mode) -> uint32;
auto bus_write(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
auto bus_store(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
auto keypad_run() -> void; auto keypad_run() -> void;
auto power() -> void; auto power() -> void;

View File

@ -17,21 +17,16 @@ auto CPU::dma_run() -> void {
auto CPU::dma_exec(Registers::DMA& dma) -> void { auto CPU::dma_exec(Registers::DMA& dma) -> void {
unsigned size = dma.control.size ? Word : Half; 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;
if(dma.run.length == dma.length) { if(mode == Nonsequential) {
idle(); idle();
idle(); idle();
sequential() = false;
} else {
sequential() = true;
} }
step(bus.wait(dma.run.source, size)); uint32 word = bus_read(dma.run.source, size, mode);
uint32 word = bus.read(dma.run.source, size); bus_write(dma.run.target, size, mode, word);
step(bus.wait(dma.run.target, size));
bus.write(dma.run.target, size, word);
switch(dma.control.sourcemode) { switch(dma.control.sourcemode) {
case 0: dma.run.source += seek; break; case 0: dma.run.source += seek; break;

View File

@ -231,7 +231,7 @@ auto CPU::write(uint32 addr, uint8 byte) -> void {
bool enable = timer.control.enable; bool enable = timer.control.enable;
timer.control = byte; timer.control = byte;
if(enable == 0 && timer.control.enable == 1) { if(enable == 0 && timer.control.enable == 1) {
timer.period = timer.reload; timer.pending = true;
} }
return; return;
} }

17
gba/cpu/prefetch.cpp Normal file
View File

@ -0,0 +1,17 @@
auto CPU::prefetch_run() -> void {
if(prefetch.slots == 8) return;
prefetch.slots++;
}
auto CPU::prefetch_read(uint32 addr, uint32 size) -> maybe<uint32> {
if(prefetch.slots >= (size == Word ? 2 : 1)) {
prefetch.slots -= (size == Word ? 2 : 1);
return cartridge.read(addr, size);
}
prefetch.slots = 0;
return nothing;
}
auto CPU::prefetch_take() -> uint16 {
return 0;
}

13
gba/cpu/prefetch.hpp Normal file
View File

@ -0,0 +1,13 @@
struct Prefetch {
struct Slot {
uint32 addr;
uint16 half;
} slot[8];
unsigned slots = 0;
uint32 addr = 0;
} prefetch;
auto prefetch_run() -> void;
auto prefetch_read(uint32 addr, uint32 size) -> maybe<uint32>;
auto prefetch_take() -> uint16;

View File

@ -43,6 +43,7 @@ struct Registers {
struct Timer { struct Timer {
uint16 period; uint16 period;
uint16 reload; uint16 reload;
bool pending;
TimerControl control; TimerControl control;
} timer[4]; } timer[4];

View File

@ -25,6 +25,7 @@ auto CPU::serialize(serializer& s) -> void {
for(auto& timer : regs.timer) { for(auto& timer : regs.timer) {
s.integer(timer.period); s.integer(timer.period);
s.integer(timer.reload); s.integer(timer.reload);
s.integer(timer.pending);
s.integer(timer.control.frequency); s.integer(timer.control.frequency);
s.integer(timer.control.cascade); s.integer(timer.control.cascade);
s.integer(timer.control.irq); s.integer(timer.control.irq);

View File

@ -2,6 +2,15 @@ auto CPU::timer_step(unsigned clocks) -> void {
for(unsigned c = 0; c < clocks; c++) { for(unsigned c = 0; c < clocks; c++) {
for(unsigned n = 0; n < 4; n++) { for(unsigned n = 0; n < 4; n++) {
auto& timer = regs.timer[n]; auto& timer = regs.timer[n];
if(timer.pending) {
timer.pending = false;
if(timer.control.enable == 1) {
timer.period = timer.reload;
}
continue;
}
if(timer.control.enable == false || timer.control.cascade == true) continue; if(timer.control.enable == false || timer.control.cascade == true) continue;
static unsigned mask[] = {0, 63, 255, 1023}; static unsigned mask[] = {0, 63, 255, 1023};

View File

@ -22,6 +22,7 @@ namespace GameBoyAdvance {
namespace GameBoyAdvance { namespace GameBoyAdvance {
enum : unsigned { Byte = 8, Half = 16, Word = 32 }; enum : unsigned { Byte = 8, Half = 16, Word = 32 };
enum : bool { Nonsequential = 0, Sequential = 1 };
struct Thread { struct Thread {
~Thread() { ~Thread() {

View File

@ -31,13 +31,13 @@ auto Bus::mirror(uint32 addr, uint32 size) -> uint32 {
return base; return base;
} }
auto Bus::wait(uint32 addr, uint32 size) -> unsigned { auto Bus::wait(uint32 addr, uint32 size, bool mode) -> unsigned {
switch(addr & 0x0f000000) { switch(addr & 0x0f000000) {
case 0x00000000: return 1; case 0x00000000: return 1;
case 0x01000000: return 1; case 0x01000000: return 1;
case 0x02000000: return (16 - cpu.regs.memory.control.ewramwait) * (size == Word ? 2 : 1); case 0x02000000: return (16 - cpu.regs.memory.control.ewramwait) * (size == Word ? 2 : 1);
case 0x03000000: return 1; case 0x03000000: return 1;
case 0x04000000: return 2; case 0x04000000: return 1;
case 0x05000000: return 1 + (size == Word); case 0x05000000: return 1 + (size == Word);
case 0x06000000: return 1 + (size == Word); case 0x06000000: return 1 + (size == Word);
case 0x07000000: return 1; case 0x07000000: return 1;
@ -55,9 +55,8 @@ auto Bus::wait(uint32 addr, uint32 size) -> unsigned {
case 0x0e000000: s = n; break; case 0x0e000000: s = n; break;
} }
bool sequential = cpu.sequential(); bool sequential = (mode == Sequential);
if((addr & 0x1fffe) == 0) sequential = false; //N cycle on 16-bit ROM crossing 128KB page boundary (RAM S==N) if((addr & 0x1fffe) == 0) sequential = false; //N cycle on 16-bit ROM crossing 128KB page boundary (RAM S==N)
if(idleflag) sequential = false; //LDR/LDM interrupts instruction fetches
unsigned clocks = sequential ? s : n; unsigned clocks = sequential ? s : n;
if(size == Word) clocks += s; //16-bit bus requires two transfers for words if(size == Word) clocks += s; //16-bit bus requires two transfers for words
@ -66,51 +65,42 @@ auto Bus::wait(uint32 addr, uint32 size) -> unsigned {
} }
} }
auto Bus::idle(uint32 addr) -> void {
if(addr & 0x08000000) idleflag = true;
}
auto Bus::read(uint32 addr, uint32 size) -> uint32 { auto Bus::read(uint32 addr, uint32 size) -> uint32 {
idleflag = false; switch(addr & 0x0f000000) {
if(addr & 0x08000000) return cartridge.read(addr, size); case 0x00000000: return bios.read(addr, size);
case 0x01000000: return bios.read(addr, size);
switch(addr >> 24 & 7) { case 0x02000000: return cpu.ewram_read(addr, size);
case 0: return bios.read(addr, size); case 0x03000000: return cpu.iwram_read(addr, size);
case 1: return bios.read(addr, size); case 0x4000000:
case 2: return cpu.ewram_read(addr, size);
case 3: return cpu.iwram_read(addr, size);
case 4:
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->read(addr, size); if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->read(addr, size);
if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).read(0x04000800 | (addr & 3), size); if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).read(0x04000800 | (addr & 3), size);
return cpu.pipeline.fetch.instruction; return cpu.pipeline.fetch.instruction;
case 5: return ppu.pram_read(addr, size); case 0x05000000: return ppu.pram_read(addr, size);
case 6: return ppu.vram_read(addr, size); case 0x06000000: return ppu.vram_read(addr, size);
case 7: return ppu.oam_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 { auto Bus::write(uint32 addr, uint32 size, uint32 word) -> void {
idleflag = false; switch(addr & 0x0f000000) {
if(addr & 0x08000000) return cartridge.write(addr, size, word); case 0x00000000: return;
case 0x01000000: return;
switch(addr >> 24 & 7) { case 0x02000000: return cpu.ewram_write(addr, size, word);
case 0: return; case 0x03000000: return cpu.iwram_write(addr, size, word);
case 1: return; case 0x04000000:
case 2: return cpu.ewram_write(addr, size, word);
case 3: return cpu.iwram_write(addr, size, word);
case 4:
if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->write(addr, size, word); if((addr & 0xfffffc00) == 0x04000000) return mmio[addr & 0x3ff]->write(addr, size, word);
if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).write(0x04000800 | (addr & 3), size, word); if((addr & 0xff00ffff) == 0x04000800) return ((MMIO&)cpu).write(0x04000800 | (addr & 3), size, word);
return; return;
case 5: return ppu.pram_write(addr, size, word); case 0x05000000: return ppu.pram_write(addr, size, word);
case 6: return ppu.vram_write(addr, size, word); case 0x06000000: return ppu.vram_write(addr, size, word);
case 7: return ppu.oam_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(unsigned n = 0; n < 0x400; n++) mmio[n] = &unmappedMemory; for(auto n : range(0x400)) mmio[n] = &unmappedMemory;
idleflag = false;
} }
} }

View File

@ -13,8 +13,7 @@ struct MMIO : Memory {
struct Bus : Memory { struct Bus : Memory {
static auto mirror(uint32 addr, uint32 size) -> uint32; static auto mirror(uint32 addr, uint32 size) -> uint32;
auto wait(uint32 addr, uint32 size) -> unsigned; auto wait(uint32 addr, uint32 size, bool mode) -> unsigned;
auto idle(uint32 addr) -> void;
auto read(uint32 addr, uint32 size) -> uint32; auto read(uint32 addr, uint32 size) -> uint32;
auto write(uint32 addr, uint32 size, uint32 word) -> void; auto write(uint32 addr, uint32 size, uint32 word) -> void;
auto power() -> void; auto power() -> void;
@ -22,7 +21,6 @@ struct Bus : Memory {
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
Memory* mmio[0x400]{nullptr}; Memory* mmio[0x400]{nullptr};
bool idleflag{false};
}; };
extern Bus bus; extern Bus bus;

View File

@ -1,3 +1,2 @@
auto Bus::serialize(serializer& s) -> void { auto Bus::serialize(serializer& s) -> void {
s.integer(idleflag);
} }

View File

@ -29,18 +29,18 @@ auto ARM::exec() -> void {
} }
auto ARM::idle() -> void { auto ARM::idle() -> void {
processor.nonsequential = true;
bus_idle(r(15)); bus_idle(r(15));
} }
auto ARM::read(uint32 addr, uint32 size) -> uint32 { auto ARM::read(uint32 addr, uint32 size, bool mode) -> uint32 {
uint32 word = bus_read(addr, size); if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
sequential() = true; return bus_read(addr, size, mode);
return word;
} }
auto ARM::load(uint32 addr, uint32 size) -> uint32 { auto ARM::load(uint32 addr, uint32 size, bool mode) -> uint32 {
sequential() = false; if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
uint32 word = read(addr, size); uint32 word = bus_load(addr, size, mode);
if(size == Half) { word &= 0xffff; word |= word << 16; } if(size == Half) { word &= 0xffff; word |= word << 16; }
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; } if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
@ -53,18 +53,18 @@ auto ARM::load(uint32 addr, uint32 size) -> uint32 {
return word; return word;
} }
auto ARM::write(uint32 addr, uint32 size, uint32 word) -> void { auto ARM::write(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
bus_write(addr, size, word); if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
sequential() = true; return bus_write(addr, size, mode, word);
} }
auto ARM::store(uint32 addr, uint32 size, uint32 word) -> void { auto ARM::store(uint32 addr, uint32 size, bool mode, uint32 word) -> void {
if(size == Half) { word &= 0xffff; word |= word << 16; } if(size == Half) { word &= 0xffff; word |= word << 16; }
if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; } if(size == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
sequential() = false; if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
write(addr, size, word); bus_store(addr, size, mode, word);
sequential() = false; processor.nonsequential = true;
} }
auto ARM::vector(uint32 addr, Processor::Mode mode) -> void { auto ARM::vector(uint32 addr, Processor::Mode mode) -> void {

View File

@ -9,6 +9,7 @@ namespace Processor {
struct ARM { struct ARM {
enum : unsigned { Byte = 8, Half = 16, Word = 32 }; enum : unsigned { Byte = 8, Half = 16, Word = 32 };
enum : bool { Nonsequential = 0, Sequential = 1 };
#include "registers.hpp" #include "registers.hpp"
#include "instructions-arm.hpp" #include "instructions-arm.hpp"
@ -17,17 +18,19 @@ struct ARM {
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(uint32 addr) -> void = 0;
virtual auto bus_read(uint32 addr, uint32 size) -> uint32 = 0; virtual auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32 = 0;
virtual auto bus_write(uint32 addr, uint32 size, uint32 word) -> void = 0; virtual auto bus_load(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_store(uint32 addr, uint32 size, bool mode, 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) -> uint32; auto read(uint32 addr, uint32 size, bool mode) -> uint32;
auto load(uint32 addr, uint32 size) -> uint32; auto load(uint32 addr, uint32 size, bool mode) -> uint32;
auto write(uint32 addr, uint32 size, uint32 word) -> void; auto write(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
auto store(uint32 addr, uint32 size, uint32 word) -> void; auto store(uint32 addr, uint32 size, bool mode, uint32 word) -> void;
auto vector(uint32 addr, Processor::Mode mode) -> void; auto vector(uint32 addr, Processor::Mode mode) -> void;
//algorithms.cpp //algorithms.cpp

View File

@ -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); uint32 instruction = read(pc & ~3, Word, Nonsequential);
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))); if(rn == 15) output.append(" =0x", hex<4>(read(pc + 8 + (up ? +immediate : -immediate), Half, Nonsequential)));
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))); 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))); if(rn == 15 && half == 0) output.append(" =0x", hex<2>(read(pc + 8 + (up ? +immediate : -immediate), Byte, Nonsequential)));
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))); if(rn == 15) output.append(" =0x", hex<8>(read(pc + 8 + (up ? +immediate : -immediate), byte ? Byte : Word, Nonsequential)));
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); uint16 instruction = read(pc & ~1, Half, Nonsequential);
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))); output.append(" =0x", hex<8>(read(rm, Word, Nonsequential)));
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); instruction = read((pc & ~1) + 2, Half, Nonsequential);
uint11 offsetlo = instruction; uint11 offsetlo = instruction;
int22 displacement = (offsethi << 11) | (offsetlo << 0); int22 displacement = (offsethi << 11) | (offsetlo << 0);

View File

@ -27,7 +27,7 @@ auto ARM::arm_opcode(uint32 rm) {
case 15: r(d) = bit(~rm); break; //MVN case 15: r(d) = bit(~rm); break; //MVN
} }
if(exceptionmode() && d == 15 && save) { if(exceptionMode() && d == 15 && save) {
cpsr() = spsr(); cpsr() = spsr();
processor.setMode((Processor::Mode)cpsr().m); processor.setMode((Processor::Mode)cpsr().m);
} }
@ -45,7 +45,7 @@ auto ARM::arm_move_to_status(uint32 rm) {
PSR &psr = source ? spsr() : cpsr(); PSR &psr = source ? spsr() : cpsr();
if(field & 1) { if(field & 1) {
if(source == 1 || privilegedmode()) { if(source == 1 || privilegedMode()) {
psr.i = rm & 0x00000080; psr.i = rm & 0x00000080;
psr.f = rm & 0x00000040; psr.f = rm & 0x00000040;
psr.t = rm & 0x00000020; psr.t = rm & 0x00000020;
@ -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); uint32 word = load(r(n), byte ? Byte : Word, Nonsequential);
store(r(n), byte ? Byte : Word, r(m)); store(r(n), byte ? Byte : Word, Nonsequential, 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); if(l == 1) r(d) = load(rn, Half, Nonsequential);
if(l == 0) store(rn, Half, r(d)); if(l == 0) store(rn, Half, Nonsequential, 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); if(l == 1) r(d) = load(rn, Half, Nonsequential);
if(l == 0) store(rn, Half, r(d)); if(l == 0) store(rn, Half, Nonsequential, 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); uint32 word = load(rn, half ? Half : Byte, Nonsequential);
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); uint32 word = load(rn, half ? Half : Byte, Nonsequential);
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); if(l == 1) rd = load(rn, byte ? Byte : Word, Nonsequential);
if(l == 0) store(rn, byte ? Byte : Word, rd); if(l == 0) store(rn, byte ? Byte : Word, Nonsequential, 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); if(l == 1) rd = load(rn, byte ? Byte : Word, Nonsequential);
if(l == 0) store(rn, byte ? Byte : Word, rd); if(l == 0) store(rn, byte ? Byte : Word, Nonsequential, 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;
@ -522,10 +522,9 @@ auto ARM::arm_op_move_multiple() {
if(usr) processor.setMode(Processor::Mode::USR); if(usr) processor.setMode(Processor::Mode::USR);
for(unsigned m = 0; m < 16; m++) { for(unsigned m = 0; m < 16; m++) {
sequential() = false;
if(list & 1 << m) { if(list & 1 << m) {
if(l == 1) r(m) = read(rn, Word); if(l == 1) r(m) = read(rn, Word, Nonsequential);
if(l == 0) write(rn, Word, r(m)); if(l == 0) write(rn, Word, Nonsequential, r(m));
rn += 4; rn += 4;
} }
} }
@ -540,6 +539,8 @@ auto ARM::arm_op_move_multiple() {
processor.setMode((Processor::Mode)cpsr().m); processor.setMode((Processor::Mode)cpsr().m);
} }
} }
} else {
processor.nonsequential = true;
} }
if(writeback) { if(writeback) {

View File

@ -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); r(d) = load(rm, Word, Nonsequential);
} }
//(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, r(d)); break; //STR case 0: store(r(n) + r(m), Word, Nonsequential, r(d)); break; //STR
case 1: store(r(n) + r(m), Half, r(d)); break; //STRH case 1: store(r(n) + r(m), Half, Nonsequential, r(d)); break; //STRH
case 2: store(r(n) + r(m), Byte, r(d)); break; //STRB case 2: store(r(n) + r(m), Byte, Nonsequential, r(d)); break; //STRB
case 3: r(d) = (int8)load(r(n) + r(m), Byte); break; //LDSB case 3: r(d) = (int8)load(r(n) + r(m), Byte, Nonsequential); break; //LDSB
case 4: r(d) = load(r(n) + r(m), Word); break; //LDR case 4: r(d) = load(r(n) + r(m), Word, Nonsequential); break; //LDR
case 5: r(d) = load(r(n) + r(m), Half); break; //LDRH case 5: r(d) = load(r(n) + r(m), Half, Nonsequential); break; //LDRH
case 6: r(d) = load(r(n) + r(m), Byte); break; //LDRB case 6: r(d) = load(r(n) + r(m), Byte, Nonsequential); break; //LDRB
case 7: r(d) = (int16)load(r(n) + r(m), Half); break; //LDSH case 7: r(d) = (int16)load(r(n) + r(m), Half, Nonsequential); 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); if(l == 1) r(d) = load(r(n) + offset * 4, Word, Nonsequential);
if(l == 0) store(r(n) + offset * 4, Word, r(d)); if(l == 0) store(r(n) + offset * 4, Word, Nonsequential, 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); if(l == 1) r(d) = load(r(n) + offset, Byte, Nonsequential);
if(l == 0) store(r(n) + offset, Byte, r(d)); if(l == 0) store(r(n) + offset, Byte, Nonsequential, 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); if(l == 1) r(d) = load(r(n) + offset * 2, Half, Nonsequential);
if(l == 0) store(r(n) + offset * 2, Half, r(d)); if(l == 0) store(r(n) + offset * 2, Half, Nonsequential, 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); if(l == 1) r(d) = load(r(13) + immediate * 4, Word, Nonsequential);
if(l == 0) store(r(13) + immediate * 4, Word, r(d)); if(l == 0) store(r(13) + immediate * 4, Word, Nonsequential, r(d));
} }
//add rd,{pc,sp},#immediate //add rd,{pc,sp},#immediate
@ -275,19 +275,18 @@ 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;
sequential() = false;
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); //POP if(l == 1) r(m) = read(sp, Word, Nonsequential); //POP
if(l == 0) write(sp, Word, r(m)); //PUSH if(l == 0) write(sp, Word, Nonsequential, r(m)); //PUSH
sp += 4; sp += 4;
} }
} }
if(branch) { if(branch) {
//note: ARMv5+ POP sets cpsr().t //note: ARMv5+ POP sets cpsr().t
if(l == 1) r(15) = read(sp, Word); //POP if(l == 1) r(15) = read(sp, Word, Nonsequential); //POP
if(l == 0) write(sp, Word, r(14)); //PUSH if(l == 0) write(sp, Word, Nonsequential, r(14)); //PUSH
sp += 4; sp += 4;
} }
@ -307,11 +306,10 @@ auto ARM::thumb_op_move_multiple() {
uint8 list = instruction(); uint8 list = instruction();
uint32 rn = r(n); //rn may be in register list; so we must cache it uint32 rn = r(n); //rn may be in register list; so we must cache it
sequential() = false;
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); //LDMIA if(l == 1) r(m) = read(rn, Word, Nonsequential); //LDMIA
if(l == 0) write(rn, Word, r(m)); //STMIA if(l == 0) write(rn, Word, Nonsequential, r(m)); //STMIA
rn += 4; rn += 4;
} }
} }

View File

@ -11,7 +11,7 @@ auto ARM::Processor::power() -> void {
pc = 0; pc = 0;
carryout = false; carryout = false;
sequential = false; nonsequential = false;
irqline = false; irqline = false;
cpsr = 0; cpsr = 0;
@ -68,11 +68,11 @@ auto ARM::pipeline_step() -> void {
if(cpsr().t == 0) { if(cpsr().t == 0) {
r(15).data += 4; r(15).data += 4;
pipeline.fetch.address = r(15) & ~3; pipeline.fetch.address = r(15) & ~3;
pipeline.fetch.instruction = read(pipeline.fetch.address, Word); pipeline.fetch.instruction = read(pipeline.fetch.address, Word, Sequential);
} else { } else {
r(15).data += 2; r(15).data += 2;
pipeline.fetch.address = r(15) & ~1; pipeline.fetch.address = r(15) & ~1;
pipeline.fetch.instruction = read(pipeline.fetch.address, Half); pipeline.fetch.instruction = read(pipeline.fetch.address, Half, Sequential);
} }
} }

View File

@ -14,8 +14,8 @@ struct GPR {
inline auto operator<<=(uint32 n) { return operator=(data << n); } inline auto operator<<=(uint32 n) { return operator=(data << n); }
inline auto operator>>=(uint32 n) { return operator=(data >> n); } inline auto operator>>=(uint32 n) { return operator=(data >> n); }
uint32 data{0}; uint32 data = 0;
function<void ()> modify; function<auto () -> void> modify;
}; };
struct PSR { struct PSR {
@ -38,22 +38,22 @@ struct PSR {
auto serialize(serializer&) -> void; auto serialize(serializer&) -> void;
bool n{false}; //negative bool n = false; //negative
bool z{false}; //zero bool z = false; //zero
bool c{false}; //carry bool c = false; //carry
bool v{false}; //overflow bool v = false; //overflow
bool i{false}; //irq bool i = false; //irq
bool f{false}; //fiq bool f = false; //fiq
bool t{false}; //thumb bool t = false; //thumb
unsigned m{0}; //mode unsigned m = 0; //mode
}; };
struct Pipeline { struct Pipeline {
bool reload{false}; bool reload = false;
struct Instruction { struct Instruction {
uint32 address{0}; uint32 address = 0;
uint32 instruction{0}; uint32 instruction = 0;
}; };
Instruction execute; Instruction execute;
@ -105,12 +105,12 @@ struct Processor {
GPR pc; GPR pc;
PSR cpsr; PSR cpsr;
bool carryout{false}; bool carryout = false;
bool sequential{false}; bool nonsequential = false;
bool irqline{false}; bool irqline = false;
GPR* r[16]; GPR* r[16] = {nullptr};
PSR* spsr; PSR* spsr = nullptr;
auto power() -> void; auto power() -> void;
auto setMode(Mode) -> void; auto setMode(Mode) -> void;
@ -118,7 +118,7 @@ struct Processor {
Processor processor; Processor processor;
Pipeline pipeline; Pipeline pipeline;
bool crash{false}; bool crash = false;
auto pipeline_step() -> void; auto pipeline_step() -> void;
@ -126,8 +126,7 @@ 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; }
alwaysinline auto carryout() -> bool& { return processor.carryout; } alwaysinline auto carryout() -> bool& { return processor.carryout; }
alwaysinline auto sequential() -> bool& { return processor.sequential; }
alwaysinline auto instruction() -> uint32 { return pipeline.execute.instruction; } alwaysinline auto instruction() -> uint32 { return pipeline.execute.instruction; }
alwaysinline auto mode() -> Processor::Mode { return (Processor::Mode)processor.cpsr.m; } alwaysinline auto mode() -> Processor::Mode { return (Processor::Mode)processor.cpsr.m; }
alwaysinline auto privilegedmode() const -> bool { return (Processor::Mode)processor.cpsr.m != Processor::Mode::USR; } alwaysinline auto privilegedMode() const -> bool { return (Processor::Mode)processor.cpsr.m != Processor::Mode::USR; }
alwaysinline auto exceptionmode() const -> bool { return privilegedmode() && (Processor::Mode)processor.cpsr.m != Processor::Mode::SYS; } alwaysinline auto exceptionMode() const -> bool { return privilegedMode() && (Processor::Mode)processor.cpsr.m != Processor::Mode::SYS; }

View File

@ -55,7 +55,7 @@ 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.sequential); s.integer(processor.nonsequential);
s.integer(processor.irqline); s.integer(processor.irqline);
s.integer(pipeline.reload); s.integer(pipeline.reload);

View File

@ -3,9 +3,8 @@ auto ARM::arm_step() -> void {
pipeline.reload = false; pipeline.reload = false;
r(15).data &= ~3; r(15).data &= ~3;
sequential() = false;
pipeline.fetch.address = r(15) & ~3; pipeline.fetch.address = r(15) & ~3;
pipeline.fetch.instruction = read(pipeline.fetch.address, Word); pipeline.fetch.instruction = read(pipeline.fetch.address, Word, Nonsequential);
pipeline_step(); pipeline_step();
} }
@ -61,9 +60,8 @@ auto ARM::thumb_step() -> void {
pipeline.reload = false; pipeline.reload = false;
r(15).data &= ~1; r(15).data &= ~1;
sequential() = false;
pipeline.fetch.address = r(15) & ~1; pipeline.fetch.address = r(15) & ~1;
pipeline.fetch.instruction = read(pipeline.fetch.address, Half); pipeline.fetch.instruction = read(pipeline.fetch.address, Half, Nonsequential);
pipeline_step(); pipeline_step();
} }

View File

@ -12,8 +12,10 @@ struct ArmDSP : Processor::ARM, Coprocessor {
void step(unsigned clocks); void step(unsigned clocks);
void bus_idle(uint32 addr); void bus_idle(uint32 addr);
uint32 bus_read(uint32 addr, uint32 size); uint32 bus_read(uint32 addr, uint32 size, bool mode);
void bus_write(uint32 addr, uint32 size, uint32 word); uint32 bus_load(uint32 addr, uint32 size, bool mode);
void bus_write(uint32 addr, uint32 size, bool mode, uint32 word);
void bus_store(uint32 addr, uint32 size, bool mode, uint32 word);
uint8 mmio_read(unsigned addr); uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data); void mmio_write(unsigned addr, uint8 data);

View File

@ -4,7 +4,7 @@ void ArmDSP::bus_idle(uint32 addr) {
step(1); step(1);
} }
uint32 ArmDSP::bus_read(uint32 addr, uint32 size) { uint32 ArmDSP::bus_read(uint32 addr, uint32 size, bool mode) {
step(1); step(1);
static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) -> uint32 { static auto memory = [&](const uint8 *memory, uint32 addr, uint32 size) -> uint32 {
@ -44,7 +44,11 @@ uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
return 0u; return 0u;
} }
void ArmDSP::bus_write(uint32 addr, uint32 size, uint32 word) { uint32 ArmDSP::bus_load(uint32 addr, uint32 size, bool mode) {
return bus_read(addr, size, mode);
}
void ArmDSP::bus_write(uint32 addr, uint32 size, bool mode, uint32 word) {
step(1); step(1);
static auto memory = [](uint8 *memory, uint32 addr, uint32 size, uint32 word) { static auto memory = [](uint8 *memory, uint32 addr, uint32 size, uint32 word) {
@ -98,4 +102,8 @@ void ArmDSP::bus_write(uint32 addr, uint32 size, uint32 word) {
} }
} }
void ArmDSP::bus_store(uint32 addr, uint32 size, bool mode, uint32 word) {
return bus_write(addr, size, mode, word);
}
#endif #endif

View File

@ -106,6 +106,7 @@ Presentation::Presentation() {
setResizable(false); setResizable(false);
setBackgroundColor({0, 0, 0}); setBackgroundColor({0, 0, 0});
resizeViewport(); resizeViewport();
setCentered();
} }
auto Presentation::updateEmulator() -> void { auto Presentation::updateEmulator() -> void {
@ -161,7 +162,6 @@ auto Presentation::resizeViewport() -> void {
setSize({windowWidth, windowHeight}); setSize({windowWidth, windowHeight});
viewport.setGeometry({(windowWidth - width) / 2, (windowHeight - height) / 2, width, height}); viewport.setGeometry({(windowWidth - width) / 2, (windowHeight - height) / 2, width, height});
setPlacement(0.5, 0.5);
} else { } else {
auto desktop = Desktop::size(); auto desktop = Desktop::size();