mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
ea02f1e36a
commit
169e400437
|
@ -8,7 +8,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
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 License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace GameBoyAdvance {
|
||||
|
||||
#include "registers.cpp"
|
||||
#include "prefetch.cpp"
|
||||
#include "mmio.cpp"
|
||||
#include "memory.cpp"
|
||||
#include "dma.cpp"
|
||||
|
@ -71,18 +72,40 @@ auto CPU::sync_step(unsigned clocks) -> void {
|
|||
}
|
||||
|
||||
auto CPU::bus_idle(uint32 addr) -> void {
|
||||
if(regs.wait.control.prefetch) prefetch_run();
|
||||
step(1);
|
||||
return bus.idle(addr);
|
||||
}
|
||||
|
||||
auto CPU::bus_read(uint32 addr, uint32 size) -> uint32 {
|
||||
step(bus.wait(addr, size));
|
||||
return bus.read(addr, size);
|
||||
auto CPU::bus_read(uint32 addr, uint32 size, bool mode) -> uint32 {
|
||||
if(regs.wait.control.prefetch) {
|
||||
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 {
|
||||
step(bus.wait(addr, size));
|
||||
return bus.write(addr, size, word);
|
||||
auto CPU::bus_load(uint32 addr, uint32 size, bool mode) -> uint32 {
|
||||
if(regs.wait.control.prefetch) prefetch_run();
|
||||
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 {
|
||||
|
@ -118,6 +141,7 @@ auto CPU::power() -> void {
|
|||
for(auto& timer : regs.timer) {
|
||||
timer.period = 0;
|
||||
timer.reload = 0;
|
||||
timer.pending = false;
|
||||
timer.control = 0;
|
||||
}
|
||||
regs.keypad.control = 0;
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
struct CPU : Processor::ARM, Thread, MMIO {
|
||||
using ARM::read;
|
||||
using ARM::write;
|
||||
|
||||
uint8* iwram = nullptr;
|
||||
uint8* ewram = nullptr;
|
||||
#include "registers.hpp"
|
||||
#include "prefetch.hpp"
|
||||
#include "state.hpp"
|
||||
|
||||
//cpu.cpp
|
||||
|
@ -12,8 +16,10 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||
auto sync_step(unsigned clocks) -> void;
|
||||
|
||||
auto bus_idle(uint32 addr) -> void;
|
||||
auto bus_read(uint32 addr, uint32 size) -> uint32;
|
||||
auto bus_write(uint32 addr, uint32 size, uint32 word) -> void;
|
||||
auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32;
|
||||
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 power() -> void;
|
||||
|
|
|
@ -17,21 +17,16 @@ auto CPU::dma_run() -> void {
|
|||
|
||||
auto CPU::dma_exec(Registers::DMA& dma) -> void {
|
||||
unsigned size = dma.control.size ? Word : Half;
|
||||
unsigned mode = dma.run.length == dma.length ? Nonsequential : Sequential;
|
||||
unsigned seek = dma.control.size ? 4 : 2;
|
||||
|
||||
if(dma.run.length == dma.length) {
|
||||
if(mode == Nonsequential) {
|
||||
idle();
|
||||
idle();
|
||||
sequential() = false;
|
||||
} else {
|
||||
sequential() = true;
|
||||
}
|
||||
|
||||
step(bus.wait(dma.run.source, size));
|
||||
uint32 word = bus.read(dma.run.source, size);
|
||||
|
||||
step(bus.wait(dma.run.target, size));
|
||||
bus.write(dma.run.target, size, word);
|
||||
uint32 word = bus_read(dma.run.source, size, mode);
|
||||
bus_write(dma.run.target, size, mode, word);
|
||||
|
||||
switch(dma.control.sourcemode) {
|
||||
case 0: dma.run.source += seek; break;
|
||||
|
|
|
@ -231,7 +231,7 @@ auto CPU::write(uint32 addr, uint8 byte) -> void {
|
|||
bool enable = timer.control.enable;
|
||||
timer.control = byte;
|
||||
if(enable == 0 && timer.control.enable == 1) {
|
||||
timer.period = timer.reload;
|
||||
timer.pending = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -43,6 +43,7 @@ struct Registers {
|
|||
struct Timer {
|
||||
uint16 period;
|
||||
uint16 reload;
|
||||
bool pending;
|
||||
TimerControl control;
|
||||
} timer[4];
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ auto CPU::serialize(serializer& s) -> void {
|
|||
for(auto& timer : regs.timer) {
|
||||
s.integer(timer.period);
|
||||
s.integer(timer.reload);
|
||||
s.integer(timer.pending);
|
||||
s.integer(timer.control.frequency);
|
||||
s.integer(timer.control.cascade);
|
||||
s.integer(timer.control.irq);
|
||||
|
|
|
@ -2,6 +2,15 @@ auto CPU::timer_step(unsigned clocks) -> void {
|
|||
for(unsigned c = 0; c < clocks; c++) {
|
||||
for(unsigned n = 0; n < 4; 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;
|
||||
|
||||
static unsigned mask[] = {0, 63, 255, 1023};
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace GameBoyAdvance {
|
|||
|
||||
namespace GameBoyAdvance {
|
||||
enum : unsigned { Byte = 8, Half = 16, Word = 32 };
|
||||
enum : bool { Nonsequential = 0, Sequential = 1 };
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
|
|
|
@ -31,13 +31,13 @@ auto Bus::mirror(uint32 addr, uint32 size) -> uint32 {
|
|||
return base;
|
||||
}
|
||||
|
||||
auto Bus::wait(uint32 addr, uint32 size) -> unsigned {
|
||||
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 2;
|
||||
case 0x04000000: return 1;
|
||||
case 0x05000000: return 1 + (size == Word);
|
||||
case 0x06000000: return 1 + (size == Word);
|
||||
case 0x07000000: return 1;
|
||||
|
@ -55,9 +55,8 @@ auto Bus::wait(uint32 addr, uint32 size) -> unsigned {
|
|||
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(idleflag) sequential = false; //LDR/LDM interrupts instruction fetches
|
||||
|
||||
unsigned clocks = sequential ? s : n;
|
||||
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 {
|
||||
idleflag = false;
|
||||
if(addr & 0x08000000) return cartridge.read(addr, size);
|
||||
|
||||
switch(addr >> 24 & 7) {
|
||||
case 0: return bios.read(addr, size);
|
||||
case 1: return bios.read(addr, size);
|
||||
case 2: return cpu.ewram_read(addr, size);
|
||||
case 3: return cpu.iwram_read(addr, size);
|
||||
case 4:
|
||||
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 5: return ppu.pram_read(addr, size);
|
||||
case 6: return ppu.vram_read(addr, size);
|
||||
case 7: return ppu.oam_read(addr, size);
|
||||
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 {
|
||||
idleflag = false;
|
||||
if(addr & 0x08000000) return cartridge.write(addr, size, word);
|
||||
|
||||
switch(addr >> 24 & 7) {
|
||||
case 0: return;
|
||||
case 1: return;
|
||||
case 2: return cpu.ewram_write(addr, size, word);
|
||||
case 3: return cpu.iwram_write(addr, size, word);
|
||||
case 4:
|
||||
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 5: return ppu.pram_write(addr, size, word);
|
||||
case 6: return ppu.vram_write(addr, size, word);
|
||||
case 7: return ppu.oam_write(addr, size, word);
|
||||
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(unsigned n = 0; n < 0x400; n++) mmio[n] = &unmappedMemory;
|
||||
idleflag = false;
|
||||
for(auto n : range(0x400)) mmio[n] = &unmappedMemory;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,8 +13,7 @@ struct MMIO : Memory {
|
|||
struct Bus : Memory {
|
||||
static auto mirror(uint32 addr, uint32 size) -> uint32;
|
||||
|
||||
auto wait(uint32 addr, uint32 size) -> unsigned;
|
||||
auto idle(uint32 addr) -> void;
|
||||
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;
|
||||
|
@ -22,7 +21,6 @@ struct Bus : Memory {
|
|||
auto serialize(serializer&) -> void;
|
||||
|
||||
Memory* mmio[0x400]{nullptr};
|
||||
bool idleflag{false};
|
||||
};
|
||||
|
||||
extern Bus bus;
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
auto Bus::serialize(serializer& s) -> void {
|
||||
s.integer(idleflag);
|
||||
}
|
||||
|
|
|
@ -29,18 +29,18 @@ auto ARM::exec() -> void {
|
|||
}
|
||||
|
||||
auto ARM::idle() -> void {
|
||||
processor.nonsequential = true;
|
||||
bus_idle(r(15));
|
||||
}
|
||||
|
||||
auto ARM::read(uint32 addr, uint32 size) -> uint32 {
|
||||
uint32 word = bus_read(addr, size);
|
||||
sequential() = true;
|
||||
return word;
|
||||
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::load(uint32 addr, uint32 size) -> uint32 {
|
||||
sequential() = false;
|
||||
uint32 word = read(addr, size);
|
||||
auto ARM::load(uint32 addr, uint32 size, bool mode) -> uint32 {
|
||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
||||
uint32 word = bus_load(addr, size, mode);
|
||||
|
||||
if(size == Half) { word &= 0xffff; 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;
|
||||
}
|
||||
|
||||
auto ARM::write(uint32 addr, uint32 size, uint32 word) -> void {
|
||||
bus_write(addr, size, word);
|
||||
sequential() = true;
|
||||
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::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 == Byte) { word &= 0xff; word |= word << 8; word |= word << 16; }
|
||||
|
||||
sequential() = false;
|
||||
write(addr, size, word);
|
||||
sequential() = false;
|
||||
if(processor.nonsequential) processor.nonsequential = false, mode = Nonsequential;
|
||||
bus_store(addr, size, mode, word);
|
||||
processor.nonsequential = true;
|
||||
}
|
||||
|
||||
auto ARM::vector(uint32 addr, Processor::Mode mode) -> void {
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Processor {
|
|||
|
||||
struct ARM {
|
||||
enum : unsigned { Byte = 8, Half = 16, Word = 32 };
|
||||
enum : bool { Nonsequential = 0, Sequential = 1 };
|
||||
|
||||
#include "registers.hpp"
|
||||
#include "instructions-arm.hpp"
|
||||
|
@ -17,17 +18,19 @@ struct ARM {
|
|||
|
||||
virtual auto step(unsigned clocks) -> void = 0;
|
||||
virtual auto bus_idle(uint32 addr) -> void = 0;
|
||||
virtual auto bus_read(uint32 addr, uint32 size) -> uint32 = 0;
|
||||
virtual auto bus_write(uint32 addr, uint32 size, uint32 word) -> void = 0;
|
||||
virtual auto bus_read(uint32 addr, uint32 size, bool mode) -> uint32 = 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
|
||||
auto power() -> void;
|
||||
auto exec() -> void;
|
||||
auto idle() -> void;
|
||||
auto read(uint32 addr, uint32 size) -> uint32;
|
||||
auto load(uint32 addr, uint32 size) -> uint32;
|
||||
auto write(uint32 addr, uint32 size, uint32 word) -> void;
|
||||
auto store(uint32 addr, uint32 size, uint32 word) -> 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 vector(uint32 addr, Processor::Mode mode) -> void;
|
||||
|
||||
//algorithms.cpp
|
||||
|
|
|
@ -32,7 +32,7 @@ auto ARM::disassemble_arm_instruction(uint32 pc) -> string {
|
|||
|
||||
string output{hex<8>(pc), " "};
|
||||
|
||||
uint32 instruction = read(pc & ~3, Word);
|
||||
uint32 instruction = read(pc & ~3, Word, Nonsequential);
|
||||
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)));
|
||||
if(rn == 15) output.append(" =0x", hex<4>(read(pc + 8 + (up ? +immediate : -immediate), Half, Nonsequential)));
|
||||
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)));
|
||||
if(rn == 15 && half == 0) output.append(" =0x", hex<2>(read(pc + 8 + (up ? +immediate : -immediate), Byte)));
|
||||
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)));
|
||||
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)));
|
||||
if(rn == 15) output.append(" =0x", hex<8>(read(pc + 8 + (up ? +immediate : -immediate), byte ? Byte : Word, Nonsequential)));
|
||||
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);
|
||||
uint16 instruction = read(pc & ~1, Half, Nonsequential);
|
||||
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)));
|
||||
output.append(" =0x", hex<8>(read(rm, Word, Nonsequential)));
|
||||
|
||||
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);
|
||||
instruction = read((pc & ~1) + 2, Half, Nonsequential);
|
||||
uint11 offsetlo = instruction;
|
||||
|
||||
int22 displacement = (offsethi << 11) | (offsetlo << 0);
|
||||
|
|
|
@ -27,7 +27,7 @@ auto ARM::arm_opcode(uint32 rm) {
|
|||
case 15: r(d) = bit(~rm); break; //MVN
|
||||
}
|
||||
|
||||
if(exceptionmode() && d == 15 && save) {
|
||||
if(exceptionMode() && d == 15 && save) {
|
||||
cpsr() = spsr();
|
||||
processor.setMode((Processor::Mode)cpsr().m);
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ auto ARM::arm_move_to_status(uint32 rm) {
|
|||
PSR &psr = source ? spsr() : cpsr();
|
||||
|
||||
if(field & 1) {
|
||||
if(source == 1 || privilegedmode()) {
|
||||
if(source == 1 || privilegedMode()) {
|
||||
psr.i = rm & 0x00000080;
|
||||
psr.f = rm & 0x00000040;
|
||||
psr.t = rm & 0x00000020;
|
||||
|
@ -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);
|
||||
store(r(n), byte ? Byte : Word, r(m));
|
||||
uint32 word = load(r(n), byte ? Byte : Word, Nonsequential);
|
||||
store(r(n), byte ? Byte : Word, Nonsequential, 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);
|
||||
if(l == 0) store(rn, Half, r(d));
|
||||
if(l == 1) r(d) = load(rn, Half, Nonsequential);
|
||||
if(l == 0) store(rn, Half, Nonsequential, 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);
|
||||
if(l == 0) store(rn, Half, r(d));
|
||||
if(l == 1) r(d) = load(rn, Half, Nonsequential);
|
||||
if(l == 0) store(rn, Half, Nonsequential, 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);
|
||||
uint32 word = load(rn, half ? Half : Byte, Nonsequential);
|
||||
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);
|
||||
uint32 word = load(rn, half ? Half : Byte, Nonsequential);
|
||||
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);
|
||||
if(l == 0) store(rn, byte ? Byte : Word, rd);
|
||||
if(l == 1) rd = load(rn, byte ? Byte : Word, Nonsequential);
|
||||
if(l == 0) store(rn, byte ? Byte : Word, Nonsequential, 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);
|
||||
if(l == 0) store(rn, byte ? Byte : Word, rd);
|
||||
if(l == 1) rd = load(rn, byte ? Byte : Word, Nonsequential);
|
||||
if(l == 0) store(rn, byte ? Byte : Word, Nonsequential, rd);
|
||||
if(pre == 0) rn = up ? rn + rm : rn - rm;
|
||||
|
||||
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);
|
||||
|
||||
for(unsigned m = 0; m < 16; m++) {
|
||||
sequential() = false;
|
||||
if(list & 1 << m) {
|
||||
if(l == 1) r(m) = read(rn, Word);
|
||||
if(l == 0) write(rn, Word, r(m));
|
||||
if(l == 1) r(m) = read(rn, Word, Nonsequential);
|
||||
if(l == 0) write(rn, Word, Nonsequential, r(m));
|
||||
rn += 4;
|
||||
}
|
||||
}
|
||||
|
@ -540,6 +539,8 @@ auto ARM::arm_op_move_multiple() {
|
|||
processor.setMode((Processor::Mode)cpsr().m);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
processor.nonsequential = true;
|
||||
}
|
||||
|
||||
if(writeback) {
|
||||
|
|
|
@ -145,7 +145,7 @@ auto ARM::thumb_op_load_literal() {
|
|||
uint8 displacement = instruction();
|
||||
|
||||
unsigned rm = (r(15) & ~3) + displacement * 4;
|
||||
r(d) = load(rm, Word);
|
||||
r(d) = load(rm, Word, Nonsequential);
|
||||
}
|
||||
|
||||
//(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, r(d)); break; //STR
|
||||
case 1: store(r(n) + r(m), Half, r(d)); break; //STRH
|
||||
case 2: store(r(n) + r(m), Byte, r(d)); break; //STRB
|
||||
case 3: r(d) = (int8)load(r(n) + r(m), Byte); break; //LDSB
|
||||
case 4: r(d) = load(r(n) + r(m), Word); break; //LDR
|
||||
case 5: r(d) = load(r(n) + r(m), Half); break; //LDRH
|
||||
case 6: r(d) = load(r(n) + r(m), Byte); break; //LDRB
|
||||
case 7: r(d) = (int16)load(r(n) + r(m), Half); break; //LDSH
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
if(l == 0) store(r(n) + offset * 4, Word, r(d));
|
||||
if(l == 1) r(d) = load(r(n) + offset * 4, Word, Nonsequential);
|
||||
if(l == 0) store(r(n) + offset * 4, Word, Nonsequential, 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);
|
||||
if(l == 0) store(r(n) + offset, Byte, r(d));
|
||||
if(l == 1) r(d) = load(r(n) + offset, Byte, Nonsequential);
|
||||
if(l == 0) store(r(n) + offset, Byte, Nonsequential, 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);
|
||||
if(l == 0) store(r(n) + offset * 2, Half, r(d));
|
||||
if(l == 1) r(d) = load(r(n) + offset * 2, Half, Nonsequential);
|
||||
if(l == 0) store(r(n) + offset * 2, Half, Nonsequential, 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);
|
||||
if(l == 0) store(r(13) + immediate * 4, Word, r(d));
|
||||
if(l == 1) r(d) = load(r(13) + immediate * 4, Word, Nonsequential);
|
||||
if(l == 0) store(r(13) + immediate * 4, Word, Nonsequential, r(d));
|
||||
}
|
||||
|
||||
//add rd,{pc,sp},#immediate
|
||||
|
@ -275,19 +275,18 @@ auto ARM::thumb_op_stack_multiple() {
|
|||
if(l == 1) sp = r(13);
|
||||
if(l == 0) sp = r(13) - (bit::count(list) + branch) * 4;
|
||||
|
||||
sequential() = false;
|
||||
for(unsigned m = 0; m < 8; m++) {
|
||||
if(list & 1 << m) {
|
||||
if(l == 1) r(m) = read(sp, Word); //POP
|
||||
if(l == 0) write(sp, Word, r(m)); //PUSH
|
||||
if(l == 1) r(m) = read(sp, Word, Nonsequential); //POP
|
||||
if(l == 0) write(sp, Word, Nonsequential, r(m)); //PUSH
|
||||
sp += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if(branch) {
|
||||
//note: ARMv5+ POP sets cpsr().t
|
||||
if(l == 1) r(15) = read(sp, Word); //POP
|
||||
if(l == 0) write(sp, Word, r(14)); //PUSH
|
||||
if(l == 1) r(15) = read(sp, Word, Nonsequential); //POP
|
||||
if(l == 0) write(sp, Word, Nonsequential, r(14)); //PUSH
|
||||
sp += 4;
|
||||
}
|
||||
|
||||
|
@ -307,11 +306,10 @@ auto ARM::thumb_op_move_multiple() {
|
|||
uint8 list = instruction();
|
||||
uint32 rn = r(n); //rn may be in register list; so we must cache it
|
||||
|
||||
sequential() = false;
|
||||
for(unsigned m = 0; m < 8; m++) {
|
||||
if(list & 1 << m) {
|
||||
if(l == 1) r(m) = read(rn, Word); //LDMIA
|
||||
if(l == 0) write(rn, Word, r(m)); //STMIA
|
||||
if(l == 1) r(m) = read(rn, Word, Nonsequential); //LDMIA
|
||||
if(l == 0) write(rn, Word, Nonsequential, r(m)); //STMIA
|
||||
rn += 4;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ auto ARM::Processor::power() -> void {
|
|||
pc = 0;
|
||||
|
||||
carryout = false;
|
||||
sequential = false;
|
||||
nonsequential = false;
|
||||
irqline = false;
|
||||
|
||||
cpsr = 0;
|
||||
|
@ -68,11 +68,11 @@ auto ARM::pipeline_step() -> void {
|
|||
if(cpsr().t == 0) {
|
||||
r(15).data += 4;
|
||||
pipeline.fetch.address = r(15) & ~3;
|
||||
pipeline.fetch.instruction = read(pipeline.fetch.address, Word);
|
||||
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);
|
||||
pipeline.fetch.instruction = read(pipeline.fetch.address, Half, Sequential);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ struct GPR {
|
|||
inline auto operator<<=(uint32 n) { return operator=(data << n); }
|
||||
inline auto operator>>=(uint32 n) { return operator=(data >> n); }
|
||||
|
||||
uint32 data{0};
|
||||
function<void ()> modify;
|
||||
uint32 data = 0;
|
||||
function<auto () -> void> modify;
|
||||
};
|
||||
|
||||
struct PSR {
|
||||
|
@ -38,22 +38,22 @@ struct PSR {
|
|||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
bool n{false}; //negative
|
||||
bool z{false}; //zero
|
||||
bool c{false}; //carry
|
||||
bool v{false}; //overflow
|
||||
bool i{false}; //irq
|
||||
bool f{false}; //fiq
|
||||
bool t{false}; //thumb
|
||||
unsigned m{0}; //mode
|
||||
bool n = false; //negative
|
||||
bool z = false; //zero
|
||||
bool c = false; //carry
|
||||
bool v = false; //overflow
|
||||
bool i = false; //irq
|
||||
bool f = false; //fiq
|
||||
bool t = false; //thumb
|
||||
unsigned m = 0; //mode
|
||||
};
|
||||
|
||||
struct Pipeline {
|
||||
bool reload{false};
|
||||
bool reload = false;
|
||||
|
||||
struct Instruction {
|
||||
uint32 address{0};
|
||||
uint32 instruction{0};
|
||||
uint32 address = 0;
|
||||
uint32 instruction = 0;
|
||||
};
|
||||
|
||||
Instruction execute;
|
||||
|
@ -105,12 +105,12 @@ struct Processor {
|
|||
|
||||
GPR pc;
|
||||
PSR cpsr;
|
||||
bool carryout{false};
|
||||
bool sequential{false};
|
||||
bool irqline{false};
|
||||
bool carryout = false;
|
||||
bool nonsequential = false;
|
||||
bool irqline = false;
|
||||
|
||||
GPR* r[16];
|
||||
PSR* spsr;
|
||||
GPR* r[16] = {nullptr};
|
||||
PSR* spsr = nullptr;
|
||||
|
||||
auto power() -> void;
|
||||
auto setMode(Mode) -> void;
|
||||
|
@ -118,7 +118,7 @@ struct Processor {
|
|||
|
||||
Processor processor;
|
||||
Pipeline pipeline;
|
||||
bool crash{false};
|
||||
bool crash = false;
|
||||
|
||||
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 spsr() -> PSR& { return *processor.spsr; }
|
||||
alwaysinline auto carryout() -> bool& { return processor.carryout; }
|
||||
alwaysinline auto sequential() -> bool& { return processor.sequential; }
|
||||
alwaysinline auto instruction() -> uint32 { return pipeline.execute.instruction; }
|
||||
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 exceptionmode() const -> bool { return privilegedmode() && (Processor::Mode)processor.cpsr.m != Processor::Mode::SYS; }
|
||||
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; }
|
||||
|
|
|
@ -55,7 +55,7 @@ auto ARM::serialize(serializer& s) -> void {
|
|||
s.integer(processor.pc.data);
|
||||
processor.cpsr.serialize(s);
|
||||
s.integer(processor.carryout);
|
||||
s.integer(processor.sequential);
|
||||
s.integer(processor.nonsequential);
|
||||
s.integer(processor.irqline);
|
||||
|
||||
s.integer(pipeline.reload);
|
||||
|
|
|
@ -3,9 +3,8 @@ auto ARM::arm_step() -> void {
|
|||
pipeline.reload = false;
|
||||
r(15).data &= ~3;
|
||||
|
||||
sequential() = false;
|
||||
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();
|
||||
}
|
||||
|
@ -61,9 +60,8 @@ auto ARM::thumb_step() -> void {
|
|||
pipeline.reload = false;
|
||||
r(15).data &= ~1;
|
||||
|
||||
sequential() = false;
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@ struct ArmDSP : Processor::ARM, Coprocessor {
|
|||
|
||||
void step(unsigned clocks);
|
||||
void bus_idle(uint32 addr);
|
||||
uint32 bus_read(uint32 addr, uint32 size);
|
||||
void bus_write(uint32 addr, uint32 size, uint32 word);
|
||||
uint32 bus_read(uint32 addr, uint32 size, bool mode);
|
||||
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);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
|
|
@ -4,7 +4,7 @@ void ArmDSP::bus_idle(uint32 addr) {
|
|||
step(1);
|
||||
}
|
||||
|
||||
uint32 ArmDSP::bus_read(uint32 addr, uint32 size) {
|
||||
uint32 ArmDSP::bus_read(uint32 addr, uint32 size, bool mode) {
|
||||
step(1);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
|
|
|
@ -106,6 +106,7 @@ Presentation::Presentation() {
|
|||
setResizable(false);
|
||||
setBackgroundColor({0, 0, 0});
|
||||
resizeViewport();
|
||||
setCentered();
|
||||
}
|
||||
|
||||
auto Presentation::updateEmulator() -> void {
|
||||
|
@ -161,7 +162,6 @@ auto Presentation::resizeViewport() -> void {
|
|||
|
||||
setSize({windowWidth, windowHeight});
|
||||
viewport.setGeometry({(windowWidth - width) / 2, (windowHeight - height) / 2, width, height});
|
||||
setPlacement(0.5, 0.5);
|
||||
} else {
|
||||
auto desktop = Desktop::size();
|
||||
|
||||
|
|
Loading…
Reference in New Issue