Update to v103r07 release.

byuu says:

Changelog:

  - gba/cpu: massive code cleanup effort
  - gba/cpu: DMA can run in between active instructions¹
  - gba/cpu: added two-cycle startup delay between DMA activation and
    DMA transfers²
  - processor/spc700: BBC, BBC, CBNE cycle 4 is an idle cycle
  - processor/spc700: ADDW, SUBW, MOVW (read) cycle 4 is an idle cycle

¹: unfortunately, this causes yet another performance penalty for the
poor GBA core =( Also, I think I may have missed disabling DMAs while
the CPU is stopped. I'll fix that in the next WIP.

²: I put the waiting counter decrement at the wrong place, so this
doesn't actually work. Needs to be more like
this:

    auto CPU::step(uint clocks) -> void {
      for(auto _ : range(clocks)) {
        for(auto& timer : this->timer) timer.run();
        for(auto& dma : this->dma) if(dma.active && dma.waiting) dma.waiting--;
        context.clock++;
      }
      ...

    auto CPU::DMA::run() -> bool {
      if(cpu.stopped() || !active || waiting) return false;

      transfer();
      if(irq) cpu.irq.flag |= CPU::Interrupt::DMA0 << id;
      if(drq && id == 3) cpu.irq.flag |= CPU::Interrupt::Cartridge;
      return true;
    }

Of course, the real fix will be restructuring how DMA works, so that
it's always running in parallel with the CPU instead of this weird
design where it tries to run all channels in some kind of loop until no
channels are active anymore whenever one channel is activated.

Not really sure how to design that yet, however.
This commit is contained in:
Tim Allen 2017-07-05 15:29:27 +10:00
parent 16f736307e
commit d4876a831f
24 changed files with 622 additions and 721 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "103.06";
static const string Version = "103.07";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -2,6 +2,7 @@
namespace GameBoyAdvance {
APU apu;
#include "io.cpp"
#include "square.cpp"
#include "square1.cpp"
@ -11,7 +12,6 @@ namespace GameBoyAdvance {
#include "sequencer.cpp"
#include "fifo.cpp"
#include "serialization.cpp"
APU apu;
auto APU::Enter() -> void {
while(true) scheduler.synchronize(), apu.main();
@ -65,7 +65,7 @@ auto APU::main() -> void {
if(regs.bias.amplitude == 2) lsample &= ~3, rsample &= ~3; //7-bit
if(regs.bias.amplitude == 3) lsample &= ~7, rsample &= ~7; //6-bit
if(cpu.regs.mode == CPU::Registers::Mode::Stop) lsample = 0, rsample = 0;
if(cpu.stopped()) lsample = 0, rsample = 0;
stream->sample((lsample << 5) / 32768.0, (rsample << 5) / 32768.0);
}
@ -75,7 +75,7 @@ auto APU::step(uint clocks) -> void {
}
auto APU::power() -> void {
create(APU::Enter, 16'777'216);
create(APU::Enter, system.frequency());
stream = Emulator::audio.createStream(2, frequency() / 64.0);
stream->addFilter(Emulator::Filter::Order::First, Emulator::Filter::Type::HighPass, 20.0);
stream->addFilter(Emulator::Filter::Order::Second, Emulator::Filter::Type::LowPass, 20000.0, 3);

View File

@ -2,12 +2,12 @@
namespace GameBoyAdvance {
Cartridge cartridge;
#include "mrom.cpp"
#include "sram.cpp"
#include "eeprom.cpp"
#include "flash.cpp"
#include "serialization.cpp"
Cartridge cartridge;
Cartridge::Cartridge() {
mrom.data = new uint8[mrom.size = 32 * 1024 * 1024];

View File

@ -3,24 +3,24 @@ auto CPU::_idle() -> void {
}
auto CPU::_read(uint mode, uint32 addr) -> uint32 {
uint wait = this->wait(mode, addr);
uint clocks = _wait(mode, addr);
uint word = pipeline.fetch.instruction;
if(addr >= 0x1000'0000) {
prefetchStep(wait);
prefetchStep(clocks);
} else if(addr & 0x0800'0000) {
if(mode & Prefetch && regs.wait.control.prefetch) {
if(mode & Prefetch && wait.prefetch) {
prefetchSync(addr);
word = prefetchRead();
if(mode & Word) word |= prefetchRead() << 16;
} else {
if(!active.dma) prefetchWait();
step(wait - 1);
if(!context.dmaActive) prefetchWait();
step(clocks - 1);
word = cartridge.read(mode, addr);
step(1);
}
} else {
prefetchStep(wait - 1);
prefetchStep(clocks - 1);
if(addr < 0x0200'0000) word = bios.read(mode, addr);
else if(addr < 0x0300'0000) word = readEWRAM(mode, addr);
else if(addr < 0x0400'0000) word = readIWRAM(mode, addr);
@ -36,16 +36,16 @@ auto CPU::_read(uint mode, uint32 addr) -> uint32 {
}
auto CPU::_write(uint mode, uint32 addr, uint32 word) -> void {
uint wait = this->wait(mode, addr);
uint clocks = _wait(mode, addr);
if(addr >= 0x1000'0000) {
prefetchStep(wait);
prefetchStep(clocks);
} else if(addr & 0x0800'0000) {
if(!active.dma) prefetchWait();
step(wait);
if(!context.dmaActive) prefetchWait();
step(clocks);
cartridge.write(mode, addr, word);
} else {
prefetchStep(wait);
prefetchStep(clocks);
if(addr < 0x0200'0000);
else if(addr < 0x0300'0000) writeEWRAM(mode, addr, word);
else if(addr < 0x0400'0000) writeIWRAM(mode, addr, word);
@ -57,17 +57,17 @@ auto CPU::_write(uint mode, uint32 addr, uint32 word) -> void {
}
}
auto CPU::wait(uint mode, uint32 addr) -> uint {
auto CPU::_wait(uint mode, uint32 addr) -> uint {
if(addr >= 0x1000'0000) return 1; //unmapped
if(addr < 0x0200'0000) return 1;
if(addr < 0x0300'0000) return (16 - regs.memory.control.ewramwait) * (mode & Word ? 2 : 1);
if(addr < 0x0300'0000) return (16 - memory.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 uint timings[] = {5, 4, 3, 9};
uint n = timings[regs.wait.control.nwait[addr >> 25 & 3]];
uint s = regs.wait.control.swait[addr >> 25 & 3];
uint n = timings[wait.nwait[addr >> 25 & 3]];
uint s = wait.swait[addr >> 25 & 3];
switch(addr & 0x0e00'0000) {
case 0x0800'0000: s = s ? 2 : 3; break;

View File

@ -2,172 +2,90 @@
namespace GameBoyAdvance {
CPU cpu;
#include "prefetch.cpp"
#include "bus.cpp"
#include "io.cpp"
#include "memory.cpp"
#include "dma.cpp"
#include "timer.cpp"
#include "keypad.cpp"
#include "serialization.cpp"
CPU cpu;
CPU::CPU() {
iwram = new uint8[ 32 * 1024];
ewram = new uint8[256 * 1024];
regs.dma[0].source.resize(27); regs.dma[0].run.source.resize(27);
regs.dma[0].target.resize(27); regs.dma[0].run.target.resize(27);
regs.dma[0].length.resize(14); regs.dma[0].run.length.resize(14);
regs.dma[1].source.resize(28); regs.dma[1].run.source.resize(28);
regs.dma[1].target.resize(27); regs.dma[1].run.target.resize(27);
regs.dma[1].length.resize(14); regs.dma[1].run.length.resize(14);
regs.dma[2].source.resize(28); regs.dma[2].run.source.resize(28);
regs.dma[2].target.resize(27); regs.dma[2].run.target.resize(27);
regs.dma[2].length.resize(14); regs.dma[2].run.length.resize(14);
regs.dma[3].source.resize(28); regs.dma[3].run.source.resize(28);
regs.dma[3].target.resize(28); regs.dma[3].run.target.resize(28);
regs.dma[3].length.resize(16); regs.dma[3].run.length.resize(16);
}
CPU::~CPU() {
delete[] iwram;
delete[] ewram;
}
auto CPU::Enter() -> void {
while(true) scheduler.synchronize(), cpu.main();
}
auto CPU::main() -> void {
#if defined(DEBUG)
if(crash) {
print(cpsr().t ? disassemble_thumb_instruction(pipeline.execute.address)
: disassemble_arm_instruction(pipeline.execute.address), "\n");
print(disassemble_registers(), "\n");
print("Executed: ", instructions, "\n");
while(true) step(frequency);
}
#endif
processor.irqline = irq.ime && (irq.enable & irq.flag);
processor.irqline = regs.ime && (regs.irq.enable & regs.irq.flag);
if(regs.mode == Registers::Mode::Stop) {
if(!(regs.irq.enable & regs.irq.flag & Interrupt::Keypad)) {
syncStep(16); //STOP does not advance timers
} else {
regs.mode = Registers::Mode::Normal;
}
return;
if(stopped()) {
if(!(irq.enable & irq.flag & Interrupt::Keypad)) return step(16);
context.stopped = false;
}
dmaRun();
if(regs.mode == Registers::Mode::Halt) {
if(!(regs.irq.enable & regs.irq.flag)) {
step(16);
} else {
regs.mode = Registers::Mode::Normal;
}
return;
if(halted()) {
if(!(irq.enable & irq.flag)) return step(16);
context.halted = false;
}
exec();
}
auto CPU::step(uint clocks) -> void {
timerStep(clocks);
syncStep(clocks);
}
if(!context.dmaActive) {
context.dmaActive = true;
while(true) {
bool transferred = false;
for(auto& dma : this->dma) transferred |= dma.run();
if(!transferred) break;
}
context.dmaActive = false;
}
for(auto _ : range(clocks)) {
for(auto& timer : this->timer) timer.run();
context.clock++;
}
auto CPU::syncStep(uint clocks) -> void {
Thread::step(clocks);
synchronize(ppu);
synchronize(apu);
}
auto CPU::keypadRun() -> void {
//lookup table to convert button indexes to Emulator::Interface indexes
static const uint lookup[] = {5, 4, 8, 9, 3, 2, 0, 1, 7, 6};
if(!regs.keypad.control.enable) return;
bool test = regs.keypad.control.condition; //0 = OR, 1 = AND
for(auto n : range(10)) {
if(!regs.keypad.control.flag[n]) continue;
bool input = platform->inputPoll(0, 0, lookup[n]);
if(regs.keypad.control.condition == 0) test |= input;
if(regs.keypad.control.condition == 1) test &= input;
}
if(test) regs.irq.flag |= Interrupt::Keypad;
}
auto CPU::power() -> void {
create(CPU::Enter, 16'777'216);
ARM::power();
for(auto n : range( 32 * 1024)) iwram[n] = 0;
for(auto n : range(256 * 1024)) ewram[n] = 0;
create(CPU::Enter, system.frequency());
for(auto& dma : regs.dma) {
dma.source = 0;
dma.target = 0;
dma.length = 0;
dma.data = 0;
dma.control.targetmode = 0;
dma.control.sourcemode = 0;
dma.control.repeat = 0;
dma.control.size = 0;
dma.control.drq = 0;
dma.control.timingmode = 0;
dma.control.irq = 0;
dma.control.enable = 0;
dma.pending = 0;
dma.run.target = 0;
dma.run.source = 0;
dma.run.length = 0;
}
for(auto& timer : regs.timer) {
timer.period = 0;
timer.reload = 0;
timer.pending = false;
timer.control.frequency = 0;
timer.control.cascade = 0;
timer.control.irq = 0;
timer.control.enable = 0;
}
regs.serial = {};
for(auto& flag : regs.keypad.control.flag) flag = 0;
regs.keypad.control.enable = 0;
regs.keypad.control.condition = 0;
regs.joybus = {};
regs.ime = 0;
regs.irq.enable = 0;
regs.irq.flag = 0;
for(auto& nwait : regs.wait.control.nwait) nwait = 0;
for(auto& swait : regs.wait.control.swait) swait = 0;
regs.wait.control.phi = 0;
regs.wait.control.prefetch = 0;
regs.wait.control.gametype = 0; //0 = GBA, 1 = GBC
regs.memory.control.disable = 0;
regs.memory.control.unknown1 = 0;
regs.memory.control.ewram = 1;
regs.memory.control.ewramwait = 13;
regs.memory.control.unknown2 = 0;
regs.postboot = 0;
regs.mode = Registers::Mode::Normal;
regs.clock = 0;
for(auto& byte : iwram) byte = 0x00;
for(auto& byte : ewram) byte = 0x00;
for(auto n : range(4)) dma[n] = {n};
for(auto n : range(4)) timer[n] = {n};
serial = {};
keypad = {};
joybus = {};
irq = {};
wait = {};
memory = {};
prefetch = {};
prefetch.wait = 1;
context = {};
pending.dma.vblank = 0;
pending.dma.hblank = 0;
pending.dma.hdma = 0;
dma[0].source.resize(27); dma[0].latch.source.resize(27);
dma[0].target.resize(27); dma[0].latch.target.resize(27);
dma[0].length.resize(14); dma[0].latch.length.resize(14);
active.dma = false;
dma[1].source.resize(28); dma[1].latch.source.resize(28);
dma[1].target.resize(27); dma[1].latch.target.resize(27);
dma[1].length.resize(14); dma[1].latch.length.resize(14);
dma[2].source.resize(28); dma[2].latch.source.resize(28);
dma[2].target.resize(27); dma[2].latch.target.resize(27);
dma[2].length.resize(14); dma[2].latch.length.resize(14);
dma[3].source.resize(28); dma[3].latch.source.resize(28);
dma[3].target.resize(28); dma[3].latch.target.resize(28);
dma[3].length.resize(16); dma[3].latch.length.resize(16);
for(uint n = 0x0b0; n <= 0x0df; n++) bus.io[n] = this; //DMA
for(uint n = 0x100; n <= 0x10f; n++) bus.io[n] = this; //Timers
@ -176,7 +94,7 @@ auto CPU::power() -> void {
for(uint n = 0x134; n <= 0x159; n++) bus.io[n] = this; //Serial
for(uint n = 0x200; n <= 0x209; n++) bus.io[n] = this; //System
for(uint n = 0x300; n <= 0x301; n++) bus.io[n] = this; //System
//0x080-0x083 mirrored via gba/memory/memory.cpp //System
//0x080-0x083 mirrored via gba/memory/memory.cpp //System
}
}

View File

@ -2,47 +2,46 @@ struct CPU : Processor::ARM, Thread, IO {
using ARM::read;
using ARM::write;
struct Interrupt {
enum : uint {
VBlank = 0x0001,
HBlank = 0x0002,
VCoincidence = 0x0004,
Timer0 = 0x0008,
Timer1 = 0x0010,
Timer2 = 0x0020,
Timer3 = 0x0040,
Serial = 0x0080,
DMA0 = 0x0100,
DMA1 = 0x0200,
DMA2 = 0x0400,
DMA3 = 0x0800,
Keypad = 0x1000,
Cartridge = 0x2000,
};
};
struct Interrupt { enum : uint {
VBlank = 0x0001,
HBlank = 0x0002,
VCoincidence = 0x0004,
Timer0 = 0x0008,
Timer1 = 0x0010,
Timer2 = 0x0020,
Timer3 = 0x0040,
Serial = 0x0080,
DMA0 = 0x0100,
DMA1 = 0x0200,
DMA2 = 0x0400,
DMA3 = 0x0800,
Keypad = 0x1000,
Cartridge = 0x2000,
};};
#include "registers.hpp"
#include "prefetch.hpp"
#include "state.hpp"
inline auto clock() const -> uint { return context.clock; }
inline auto halted() const -> bool { return context.halted; }
inline auto stopped() const -> bool { return context.stopped; }
//cpu.cpp
CPU();
~CPU();
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void override;
auto syncStep(uint clocks) -> void;
auto keypadRun() -> void;
auto power() -> void;
//prefetch.cpp
auto prefetchSync(uint32 addr) -> void;
auto prefetchStep(uint clocks) -> void;
auto prefetchWait() -> void;
auto prefetchRead() -> uint16;
//bus.cpp
auto _idle() -> void override;
auto _read(uint mode, uint32 addr) -> uint32 override;
auto _write(uint mode, uint32 addr, uint32 word) -> void override;
auto wait(uint mode, uint32 addr) -> uint;
auto _wait(uint mode, uint32 addr) -> uint;
//io.cpp
auto readIO(uint32 addr) -> uint8;
@ -55,22 +54,155 @@ struct CPU : Processor::ARM, Thread, IO {
auto writeEWRAM(uint mode, uint32 addr, uint32 word) -> void;
//dma.cpp
auto dmaRun() -> void;
auto dmaExecute(Registers::DMA& dma) -> void;
auto dmaVblank() -> void;
auto dmaHblank() -> void;
auto dmaHDMA() -> void;
//timer.cpp
auto timerStep(uint clocks) -> void;
auto timerIncrement(uint n) -> void;
auto timerRunFIFO(uint n) -> void;
auto runFIFO(uint n) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
uint8* iwram = nullptr;
uint8* ewram = nullptr;
uint8 iwram[ 32 * 1024];
uint8 ewram[256 * 1024];
//private:
struct DMA {
//dma.cpp
auto run() -> bool;
auto transfer() -> void;
uint2 id;
boolean active;
natural waiting;
uint2 targetMode;
uint2 sourceMode;
uint1 repeat;
uint1 size;
uint1 drq;
uint2 timingMode;
uint1 irq;
uint1 enable;
VariadicNatural source;
VariadicNatural target;
VariadicNatural length;
uint32 data;
struct Latch {
VariadicNatural target;
VariadicNatural source;
VariadicNatural length;
} latch;
} dma[4];
struct Timer {
//timer.cpp
auto run() -> void;
auto step() -> void;
uint2 id;
boolean pending;
uint16 period;
uint16 reload;
uint2 frequency;
uint1 cascade;
uint1 irq;
uint1 enable;
} timer[4];
struct Serial {
uint1 shiftClockSelect;
uint1 shiftClockFrequency;
uint1 transferEnableReceive;
uint1 transferEnableSend;
uint1 startBit;
uint1 transferLength;
uint1 irqEnable;
uint16 data[4];
uint8 data8;
} serial;
struct Keypad {
//auto keypad.cpp
auto run() -> void;
uint1 enable;
uint1 condition;
uint1 flag[10];
} keypad;
struct Joybus {
uint1 sc;
uint1 sd;
uint1 si;
uint1 so;
uint1 scMode;
uint1 sdMode;
uint1 siMode;
uint1 soMode;
uint1 siIRQEnable;
uint2 mode;
uint1 resetSignal;
uint1 receiveComplete;
uint1 sendComplete;
uint1 resetIRQEnable;
uint32 receive;
uint32 transmit;
uint1 receiveFlag;
uint1 sendFlag;
uint2 generalFlag;
} joybus;
struct IRQ {
uint1 ime;
uint16 enable;
uint16 flag;
} irq;
struct Wait {
uint2 nwait[4];
uint1 swait[4];
uint2 phi;
uint1 prefetch;
uint1 gameType;
} wait;
struct Memory {
uint1 disable;
uint3 unknown1;
uint1 ewram = 1;
uint4 ewramWait = 13;
uint4 unknown2;
} memory;
struct {
uint16 slot[8];
uint32 addr; //read location of slot buffer
uint32 load; //write location of slot buffer
integer wait = 1; //number of clocks before next slot load
auto empty() const { return addr == load; }
auto full() const { return load - addr == 16; }
} prefetch;
struct Context {
natural clock;
boolean halted;
boolean stopped;
boolean booted; //set to true by the GBA BIOS
boolean dmaActive;
} context;
};
extern CPU cpu;

View File

@ -1,88 +1,77 @@
auto CPU::dmaRun() -> void {
active.dma = true;
auto CPU::DMA::run() -> bool {
if(!active) return false;
if(waiting && --waiting) return false;
while(true) {
bool transferred = false;
for(auto n : range(4)) {
auto& dma = regs.dma[n];
if(dma.pending) {
dmaExecute(dma);
if(dma.control.irq) regs.irq.flag |= Interrupt::DMA0 << n;
if(dma.control.drq && n == 3) regs.irq.flag |= Interrupt::Cartridge;
transferred = true;
break;
}
}
if(!transferred) break;
}
active.dma = false;
transfer();
if(irq) cpu.irq.flag |= CPU::Interrupt::DMA0 << id;
if(drq && id == 3) cpu.irq.flag |= CPU::Interrupt::Cartridge;
return true;
}
auto CPU::dmaExecute(Registers::DMA& dma) -> void {
uint seek = dma.control.size ? 4 : 2;
uint mode = dma.control.size ? Word : Half;
mode |= dma.run.length == dma.length ? Nonsequential : Sequential;
auto CPU::DMA::transfer() -> void {
uint seek = size ? 4 : 2;
uint mode = size ? Word : Half;
mode |= latch.length == length ? Nonsequential : Sequential;
if(mode & Nonsequential) {
if((dma.source & 0x0800'0000) && (dma.target & 0x0800'0000)) {
if((source & 0x0800'0000) && (target & 0x0800'0000)) {
//ROM -> ROM transfer
} else {
idle();
idle();
cpu.idle();
cpu.idle();
}
}
if(dma.run.source < 0x0200'0000) {
idle(); //cannot access BIOS
if(latch.source < 0x0200'0000) {
cpu.idle(); //cannot access BIOS
} else {
uint32 addr = dma.run.source;
uint32 addr = latch.source;
if(mode & Word) addr &= ~3;
if(mode & Half) addr &= ~1;
dma.data = _read(mode, addr);
data = cpu._read(mode, addr);
}
if(dma.run.target < 0x0200'0000) {
idle(); //cannot access BIOS
if(latch.target < 0x0200'0000) {
cpu.idle(); //cannot access BIOS
} else {
uint32 addr = dma.run.target;
uint32 addr = latch.target;
if(mode & Word) addr &= ~3;
if(mode & Half) addr &= ~1;
_write(mode, addr, dma.data);
cpu._write(mode, addr, data);
}
switch(dma.control.sourcemode) {
case 0: dma.run.source += seek; break;
case 1: dma.run.source -= seek; break;
switch(sourceMode) {
case 0: latch.source += seek; break;
case 1: latch.source -= seek; break;
}
switch(dma.control.targetmode) {
case 0: dma.run.target += seek; break;
case 1: dma.run.target -= seek; break;
case 3: dma.run.target += seek; break;
switch(targetMode) {
case 0: latch.target += seek; break;
case 1: latch.target -= seek; break;
case 3: latch.target += seek; break;
}
if(--dma.run.length == 0) {
dma.pending = false;
if(dma.control.targetmode == 3) dma.run.target = dma.target;
if(dma.control.repeat == 1) dma.run.length = dma.length;
if(dma.control.repeat == 0) dma.control.enable = false;
if(--latch.length == 0) {
active = false;
if(targetMode == 3) latch.target = target;
if(repeat == 1) latch.length = length;
if(repeat == 0) enable = false;
}
}
auto CPU::dmaVblank() -> void {
for(auto& dma : regs.dma) {
if(dma.control.enable && dma.control.timingmode == 1) dma.pending = true;
for(auto& dma : this->dma) {
if(dma.enable && dma.timingMode == 1) dma.active = true;
}
}
auto CPU::dmaHblank() -> void {
for(auto& dma : regs.dma) {
if(dma.control.enable && dma.control.timingmode == 2) dma.pending = true;
for(auto& dma : this->dma) {
if(dma.enable && dma.timingMode == 2) dma.active = true;
}
}
auto CPU::dmaHDMA() -> void {
auto& dma = regs.dma[3];
if(dma.control.enable && dma.control.timingmode == 3) dma.pending = true;
auto& dma = this->dma[3];
if(dma.enable && dma.timingMode == 3) dma.active = true;
}

View File

@ -1,6 +1,6 @@
auto CPU::readIO(uint32 addr) -> uint8 {
auto dma = [&]() -> Registers::DMA& { return regs.dma[addr / 12 & 3]; };
auto timer = [&]() -> Registers::Timer& { return regs.timer[addr.bits(2,3)]; };
auto dma = [&]() -> DMA& { return this->dma[addr / 12 & 3]; };
auto timer = [&]() -> Timer& { return this->timer[addr.bits(2,3)]; };
switch(addr) {
@ -10,17 +10,17 @@ auto CPU::readIO(uint32 addr) -> uint8 {
//DMA0CNT_H, DMA1CNT_H, DMA2CNT_H, DMA3CNT_H
case 0x0400'00ba: case 0x0400'00c6: case 0x0400'00d2: case 0x0400'00de: return (
dma().control.targetmode << 5
| dma().control.sourcemode.bit(0) << 7
dma().targetMode << 5
| dma().sourceMode.bit(0) << 7
);
case 0x0400'00bb: case 0x0400'00c7: case 0x0400'00d3: case 0x0400'00df: return (
dma().control.sourcemode.bit(1) << 0
| dma().control.repeat << 1
| dma().control.size << 2
| dma().control.drq << 3
| dma().control.timingmode << 4
| dma().control.irq << 6
| dma().control.enable << 7
dma().sourceMode.bit(1) << 0
| dma().repeat << 1
| dma().size << 2
| dma().drq << 3
| dma().timingMode << 4
| dma().irq << 6
| dma().enable << 7
);
//TM0CNT_L, TM1CNT_L, TM2CNT_L, TM3CNT_L
@ -29,38 +29,38 @@ auto CPU::readIO(uint32 addr) -> uint8 {
//TM0CNT_H, TM1CNT_H, TM2CNT_H, TM3CNT_H
case 0x0400'0102: case 0x0400'0106: case 0x0400'010a: case 0x0400'010e: return (
timer().control.frequency << 0
| timer().control.cascade << 2
| timer().control.irq << 6
| timer().control.enable << 7
timer().frequency << 0
| timer().cascade << 2
| timer().irq << 6
| timer().enable << 7
);
case 0x0400'0103: case 0x0400'0107: case 0x0400'010b: case 0x0400'010f: return 0;
//SIOMULTI0 (SIODATA32_L), SIOMULTI1 (SIODATA32_H), SIOMULTI2, SIOMULTI3
case 0x0400'0120: case 0x0400'0122: case 0x0400'0124: case 0x0400'0126: {
if(auto data = player.read()) return data().byte(addr.bits(0,1));
return regs.serial.data[addr.bits(1,2)].byte(0);
return serial.data[addr.bits(1,2)].byte(0);
}
case 0x0400'0121: case 0x0400'0123: case 0x0400'0125: case 0x0400'0127: {
if(auto data = player.read()) return data().byte(addr.bits(0,1));
return regs.serial.data[addr.bits(1,2)].byte(1);
return serial.data[addr.bits(1,2)].byte(1);
}
//SIOCNT
case 0x0400'0128: return (
regs.serial.control.shiftclockselect << 0
| regs.serial.control.shiftclockfrequency << 1
| regs.serial.control.transferenablereceive << 2
| regs.serial.control.transferenablesend << 3
| regs.serial.control.startbit << 7
serial.shiftClockSelect << 0
| serial.shiftClockFrequency << 1
| serial.transferEnableReceive << 2
| serial.transferEnableSend << 3
| serial.startBit << 7
);
case 0x0400'0129: return (
regs.serial.control.transferlength << 4
| regs.serial.control.irqenable << 6
serial.transferLength << 4
| serial.irqEnable << 6
);
//SIOMLT_SEND (SIODATA8)
case 0x0400'012a: return regs.serial.data8;
case 0x0400'012a: return serial.data8;
case 0x0400'012b: return 0;
//KEYINPUT
@ -85,116 +85,116 @@ auto CPU::readIO(uint32 addr) -> uint8 {
//KEYCNT
case 0x0400'0132: return (
regs.keypad.control.flag[0] << 0
| regs.keypad.control.flag[1] << 1
| regs.keypad.control.flag[2] << 2
| regs.keypad.control.flag[3] << 3
| regs.keypad.control.flag[4] << 4
| regs.keypad.control.flag[5] << 5
| regs.keypad.control.flag[6] << 6
| regs.keypad.control.flag[7] << 7
keypad.flag[0] << 0
| keypad.flag[1] << 1
| keypad.flag[2] << 2
| keypad.flag[3] << 3
| keypad.flag[4] << 4
| keypad.flag[5] << 5
| keypad.flag[6] << 6
| keypad.flag[7] << 7
);
case 0x0400'0133: return (
regs.keypad.control.flag[8] << 0
| regs.keypad.control.flag[9] << 1
| regs.keypad.control.enable << 6
| regs.keypad.control.condition << 7
keypad.flag[8] << 0
| keypad.flag[9] << 1
| keypad.enable << 6
| keypad.condition << 7
);
//RCNT
case 0x0400'0134: return (
regs.joybus.settings.sc << 0
| regs.joybus.settings.sd << 1
| regs.joybus.settings.si << 2
| regs.joybus.settings.so << 3
| regs.joybus.settings.scmode << 4
| regs.joybus.settings.sdmode << 5
| regs.joybus.settings.simode << 6
| regs.joybus.settings.somode << 7
joybus.sc << 0
| joybus.sd << 1
| joybus.si << 2
| joybus.so << 3
| joybus.scMode << 4
| joybus.sdMode << 5
| joybus.siMode << 6
| joybus.soMode << 7
);
case 0x0400'0135: return (
regs.joybus.settings.irqenable << 0
| regs.joybus.settings.mode << 6
joybus.siIRQEnable << 0
| joybus.mode << 6
);
//JOYCNT
case 0x0400'0140: return (
regs.joybus.control.resetsignal << 0
| regs.joybus.control.receivecomplete << 1
| regs.joybus.control.sendcomplete << 2
| regs.joybus.control.irqenable << 6
joybus.resetSignal << 0
| joybus.receiveComplete << 1
| joybus.sendComplete << 2
| joybus.resetIRQEnable << 6
);
case 0x0400'0141: return 0;
case 0x0400'0142: return 0;
case 0x0400'0143: return 0;
//JOY_RECV_L, JOY_RECV_H
case 0x0400'0150: return regs.joybus.receive.byte(0);
case 0x0400'0151: return regs.joybus.receive.byte(1);
case 0x0400'0152: return regs.joybus.receive.byte(2);
case 0x0400'0153: return regs.joybus.receive.byte(3);
case 0x0400'0150: return joybus.receive.byte(0);
case 0x0400'0151: return joybus.receive.byte(1);
case 0x0400'0152: return joybus.receive.byte(2);
case 0x0400'0153: return joybus.receive.byte(3);
//JOY_TRANS_L, JOY_TRANS_H
case 0x0400'0154: return regs.joybus.transmit.byte(0);
case 0x0400'0155: return regs.joybus.transmit.byte(1);
case 0x0400'0156: return regs.joybus.transmit.byte(2);
case 0x0400'0157: return regs.joybus.transmit.byte(3);
case 0x0400'0154: return joybus.transmit.byte(0);
case 0x0400'0155: return joybus.transmit.byte(1);
case 0x0400'0156: return joybus.transmit.byte(2);
case 0x0400'0157: return joybus.transmit.byte(3);
//JOYSTAT
case 0x0400'0158: return (
regs.joybus.status.receiveflag << 1
| regs.joybus.status.sendflag << 3
| regs.joybus.status.generalflag << 4
joybus.receiveFlag << 1
| joybus.sendFlag << 3
| joybus.generalFlag << 4
);
case 0x0400'0159: return 0;
case 0x0400'015a: return 0;
case 0x0400'015b: return 0;
//IE
case 0x0400'0200: return regs.irq.enable.byte(0);
case 0x0400'0201: return regs.irq.enable.byte(1);
case 0x0400'0200: return irq.enable.byte(0);
case 0x0400'0201: return irq.enable.byte(1);
//IF
case 0x0400'0202: return regs.irq.flag.byte(0);
case 0x0400'0203: return regs.irq.flag.byte(1);
case 0x0400'0202: return irq.flag.byte(0);
case 0x0400'0203: return irq.flag.byte(1);
//WAITCNT
case 0x0400'0204: return (
regs.wait.control.nwait[3] << 0
| regs.wait.control.nwait[0] << 2
| regs.wait.control.swait[0] << 4
| regs.wait.control.nwait[1] << 5
| regs.wait.control.swait[1] << 7
wait.nwait[3] << 0
| wait.nwait[0] << 2
| wait.swait[0] << 4
| wait.nwait[1] << 5
| wait.swait[1] << 7
);
case 0x0400'0205: return (
regs.wait.control.nwait[2] << 0
| regs.wait.control.swait[2] << 2
| regs.wait.control.phi << 3
| regs.wait.control.prefetch << 6
| regs.wait.control.gametype << 7
wait.nwait[2] << 0
| wait.swait[2] << 2
| wait.phi << 3
| wait.prefetch << 6
| wait.gameType << 7
);
//IME
case 0x0400'0208: return regs.ime;
case 0x0400'0208: return irq.ime;
case 0x0400'0209: return 0;
//POSTFLG + HALTCNT
case 0x0400'0300: return regs.postboot;
case 0x0400'0300: return context.booted;
case 0x0400'0301: return 0;
//MEMCNT_L
case 0x0400'0800: return (
regs.memory.control.disable << 0
| regs.memory.control.unknown1 << 1
| regs.memory.control.ewram << 5
memory.disable << 0
| memory.unknown1 << 1
| memory.ewram << 5
);
case 0x0400'0801: return 0;
//MEMCNT_H
case 0x0400'0802: return 0;
case 0x0400'0803: return (
regs.memory.control.ewramwait << 0
| regs.memory.control.unknown2 << 4
memory.ewramWait << 0
| memory.unknown2 << 4
);
}
@ -203,8 +203,8 @@ auto CPU::readIO(uint32 addr) -> uint8 {
}
auto CPU::writeIO(uint32 addr, uint8 data) -> void {
auto dma = [&]() -> Registers::DMA& { return regs.dma[addr / 12 & 3]; };
auto timer = [&]() -> Registers::Timer& { return regs.timer[addr.bits(2,3)]; };
auto dma = [&]() -> DMA& { return this->dma[addr / 12 & 3]; };
auto timer = [&]() -> Timer& { return this->timer[addr.bits(2,3)]; };
switch(addr) {
@ -226,28 +226,31 @@ auto CPU::writeIO(uint32 addr, uint8 data) -> void {
//DMA0CNT_H, DMA1CNT_H, DMA2CNT_H, DMA3CNT_H
case 0x0400'00ba: case 0x0400'00c6: case 0x0400'00d2: case 0x0400'00de:
dma().control.targetmode = data.bits(5,6);
dma().control.sourcemode.bit(0) = data.bit (7);
dma().targetMode = data.bits(5,6);
dma().sourceMode.bit(0) = data.bit (7);
return;
case 0x0400'00bb: case 0x0400'00c7: case 0x0400'00d3: case 0x0400'00df: {
bool enable = dma().control.enable;
bool enable = dma().enable;
if(addr != 0x0400'00df) data.bit(3) = 0; //gamepad DRQ valid for DMA3 only
dma().control.sourcemode.bit(1) = data.bit (0);
dma().control.repeat = data.bit (1);
dma().control.size = data.bit (2);
dma().control.drq = data.bit (3);
dma().control.timingmode = data.bits(4,5);
dma().control.irq = data.bit (6);
dma().control.enable = data.bit (7);
dma().sourceMode.bit(1) = data.bit (0);
dma().repeat = data.bit (1);
dma().size = data.bit (2);
dma().drq = data.bit (3);
dma().timingMode = data.bits(4,5);
dma().irq = data.bit (6);
dma().enable = data.bit (7);
if(!enable && dma().control.enable) { //0->1 transition
if(dma().control.timingmode == 0) dma().pending = true; //immediate transfer mode
dma().run.target = dma().target;
dma().run.source = dma().source;
dma().run.length = dma().length;
} else if(!dma().control.enable) {
dma().pending = false;
if(!enable && dma().enable) { //0->1 transition
if(dma().timingMode == 0) {
dma().active = true; //immediate transfer mode
dma().waiting = 2;
}
dma().latch.target = dma().target;
dma().latch.source = dma().source;
dma().latch.length = dma().length;
} else if(!dma().enable) {
dma().active = false;
}
return;
}
@ -258,14 +261,14 @@ auto CPU::writeIO(uint32 addr, uint8 data) -> void {
//TM0CNT_H, TM1CNT_H, TM2CNT_H, TM3CNT_H
case 0x0400'0102: case 0x0400'0106: case 0x0400'010a: case 0x0400'010e: {
bool enable = timer().control.enable;
bool enable = timer().enable;
timer().control.frequency = data.bits(0,1);
timer().control.cascade = data.bit (2);
timer().control.irq = data.bit (6);
timer().control.enable = data.bit (7);
timer().frequency = data.bits(0,1);
timer().cascade = data.bit (2);
timer().irq = data.bit (6);
timer().enable = data.bit (7);
if(!enable && timer().control.enable) { //0->1 transition
if(!enable && timer().enable) { //0->1 transition
timer().pending = true;
}
return;
@ -276,70 +279,70 @@ auto CPU::writeIO(uint32 addr, uint8 data) -> void {
//SIOMULTI0 (SIODATA32_L), SIOMULTI1 (SIODATA32_H), SIOMULTI2, SIOMULTI3
case 0x0400'0120: case 0x0400'0122: case 0x0400'0124: case 0x0400'0126:
player.write(addr.bits(0,1), data);
regs.serial.data[addr.bits(1,2)].byte(0) = data;
serial.data[addr.bits(1,2)].byte(0) = data;
return;
case 0x0400'0121: case 0x0400'0123: case 0x0400'0125: case 0x0400'0127:
player.write(addr.bits(0,1), data);
regs.serial.data[addr.bits(1,2)].byte(1) = data;
serial.data[addr.bits(1,2)].byte(1) = data;
return;
//SIOCNT
case 0x0400'0128:
regs.serial.control.shiftclockselect = data.bit(0);
regs.serial.control.shiftclockfrequency = data.bit(1);
regs.serial.control.transferenablereceive = data.bit(2);
regs.serial.control.transferenablesend = data.bit(3);
regs.serial.control.startbit = data.bit(7);
serial.shiftClockSelect = data.bit(0);
serial.shiftClockFrequency = data.bit(1);
serial.transferEnableReceive = data.bit(2);
serial.transferEnableSend = data.bit(3);
serial.startBit = data.bit(7);
return;
case 0x0400'0129:
regs.serial.control.transferlength = data.bit(4);
regs.serial.control.irqenable = data.bit(6);
serial.transferLength = data.bit(4);
serial.irqEnable = data.bit(6);
return;
//SIOMLT_SEND (SIODATA8)
case 0x0400'012a: regs.serial.data8 = data; return;
case 0x0400'012a: serial.data8 = data; return;
case 0x0400'012b: return;
//KEYCNT
case 0x0400'0132:
regs.keypad.control.flag[0] = data.bit(0);
regs.keypad.control.flag[1] = data.bit(1);
regs.keypad.control.flag[2] = data.bit(2);
regs.keypad.control.flag[3] = data.bit(3);
regs.keypad.control.flag[4] = data.bit(4);
regs.keypad.control.flag[5] = data.bit(5);
regs.keypad.control.flag[6] = data.bit(6);
regs.keypad.control.flag[7] = data.bit(7);
keypad.flag[0] = data.bit(0);
keypad.flag[1] = data.bit(1);
keypad.flag[2] = data.bit(2);
keypad.flag[3] = data.bit(3);
keypad.flag[4] = data.bit(4);
keypad.flag[5] = data.bit(5);
keypad.flag[6] = data.bit(6);
keypad.flag[7] = data.bit(7);
return;
case 0x0400'0133:
regs.keypad.control.flag[8] = data.bit(0);
regs.keypad.control.flag[9] = data.bit(1);
regs.keypad.control.enable = data.bit(6);
regs.keypad.control.condition = data.bit(7);
keypad.flag[8] = data.bit(0);
keypad.flag[9] = data.bit(1);
keypad.enable = data.bit(6);
keypad.condition = data.bit(7);
return;
//RCNT
case 0x0400'0134:
regs.joybus.settings.sc = data.bit(0);
regs.joybus.settings.sd = data.bit(1);
regs.joybus.settings.si = data.bit(2);
regs.joybus.settings.so = data.bit(3);
regs.joybus.settings.scmode = data.bit(4);
regs.joybus.settings.sdmode = data.bit(5);
regs.joybus.settings.simode = data.bit(6);
regs.joybus.settings.somode = data.bit(7);
joybus.sc = data.bit(0);
joybus.sd = data.bit(1);
joybus.si = data.bit(2);
joybus.so = data.bit(3);
joybus.scMode = data.bit(4);
joybus.sdMode = data.bit(5);
joybus.siMode = data.bit(6);
joybus.soMode = data.bit(7);
return;
case 0x0400'0135:
regs.joybus.settings.irqenable = data.bit (0);
regs.joybus.settings.mode = data.bits(6,7);
joybus.siIRQEnable = data.bit (0);
joybus.mode = data.bits(6,7);
return;
//JOYCNT
case 0x0400'0140:
regs.joybus.control.resetsignal = data.bit(0);
regs.joybus.control.receivecomplete = data.bit(1);
regs.joybus.control.sendcomplete = data.bit(2);
regs.joybus.control.irqenable = data.bit(6);
joybus.resetSignal = data.bit(0);
joybus.receiveComplete = data.bit(1);
joybus.sendComplete = data.bit(2);
joybus.resetIRQEnable = data.bit(6);
return;
case 0x0400'0141: return;
case 0x0400'0142: return;
@ -347,75 +350,76 @@ auto CPU::writeIO(uint32 addr, uint8 data) -> void {
//JOY_RECV_L
//JOY_RECV_H
case 0x0400'0150: regs.joybus.receive.byte(0) = data; return;
case 0x0400'0151: regs.joybus.receive.byte(1) = data; return;
case 0x0400'0152: regs.joybus.receive.byte(2) = data; return;
case 0x0400'0153: regs.joybus.receive.byte(3) = data; return;
case 0x0400'0150: joybus.receive.byte(0) = data; return;
case 0x0400'0151: joybus.receive.byte(1) = data; return;
case 0x0400'0152: joybus.receive.byte(2) = data; return;
case 0x0400'0153: joybus.receive.byte(3) = data; return;
//JOY_TRANS_L
//JOY_TRANS_H
case 0x0400'0154: regs.joybus.transmit.byte(0) = data; return;
case 0x0400'0155: regs.joybus.transmit.byte(1) = data; return;
case 0x0400'0156: regs.joybus.transmit.byte(2) = data; return;
case 0x0400'0157: regs.joybus.transmit.byte(3) = data; return;
case 0x0400'0154: joybus.transmit.byte(0) = data; return;
case 0x0400'0155: joybus.transmit.byte(1) = data; return;
case 0x0400'0156: joybus.transmit.byte(2) = data; return;
case 0x0400'0157: joybus.transmit.byte(3) = data; return;
//JOYSTAT
case 0x0400'0158:
regs.joybus.status.receiveflag = data.bit (1);
regs.joybus.status.sendflag = data.bit (3);
regs.joybus.status.generalflag = data.bits(4,5);
joybus.receiveFlag = data.bit (1);
joybus.sendFlag = data.bit (3);
joybus.generalFlag = data.bits(4,5);
return;
case 0x0400'0159: return;
//IE
case 0x0400'0200: regs.irq.enable.byte(0) = data; return;
case 0x0400'0201: regs.irq.enable.byte(1) = data; return;
case 0x0400'0200: irq.enable.byte(0) = data; return;
case 0x0400'0201: irq.enable.byte(1) = data; return;
//IF
case 0x0400'0202: regs.irq.flag.byte(0) = regs.irq.flag.byte(0) & ~data; return;
case 0x0400'0203: regs.irq.flag.byte(1) = regs.irq.flag.byte(1) & ~data; return;
case 0x0400'0202: irq.flag.byte(0) = irq.flag.byte(0) & ~data; return;
case 0x0400'0203: irq.flag.byte(1) = irq.flag.byte(1) & ~data; return;
//WAITCNT
case 0x0400'0204:
regs.wait.control.swait[3] = data.bit (0); //todo: is this correct?
regs.wait.control.nwait[3] = data.bits(0,1);
regs.wait.control.nwait[0] = data.bits(2,3);
regs.wait.control.swait[0] = data.bit (4);
regs.wait.control.nwait[1] = data.bits(5,6);
regs.wait.control.swait[1] = data.bit (7);
wait.swait[3] = data.bit (0); //todo: is this correct?
wait.nwait[3] = data.bits(0,1);
wait.nwait[0] = data.bits(2,3);
wait.swait[0] = data.bit (4);
wait.nwait[1] = data.bits(5,6);
wait.swait[1] = data.bit (7);
return;
case 0x0400'0205:
regs.wait.control.nwait[2] = data.bits(0,1);
regs.wait.control.swait[2] = data.bit (2);
regs.wait.control.phi = data.bit (3);
regs.wait.control.prefetch = data.bit (6);
//regs.wait.control.gametype is read-only
wait.nwait[2] = data.bits(0,1);
wait.swait[2] = data.bit (2);
wait.phi = data.bit (3);
wait.prefetch = data.bit (6);
//wait.gameType is read-only
return;
//IME
case 0x0400'0208: regs.ime = data.bit(0); return;
case 0x0400'0208: irq.ime = data.bit(0); return;
case 0x0400'0209: return;
//POSTFLG, HALTCNT
case 0x0400'0300:
if(data.bit(0)) regs.postboot = 1;
if(data.bit(0)) context.booted = 1;
return;
case 0x0400'0301:
regs.mode = data.bit(7) ? Registers::Mode::Stop : Registers::Mode::Halt;
context.halted = data.bit(7) == 0;
context.stopped = data.bit(7) == 1;
return;
//MEMCNT_L
//MEMCNT_H
case 0x0400'0800:
regs.memory.control.disable = data.bit (0);
regs.memory.control.unknown1 = data.bits(1,3);
regs.memory.control.ewram = data.bit (5);
memory.disable = data.bit (0);
memory.unknown1 = data.bits(1,3);
memory.ewram = data.bit (5);
return;
case 0x0400'0801: return;
case 0x0400'0802: return;
case 0x0400'0803:
regs.memory.control.ewramwait = data.bits(0,3);
regs.memory.control.unknown2 = data.bits(4,7);
memory.ewramWait = data.bits(0,3);
memory.unknown2 = data.bits(4,7);
return;
}

15
higan/gba/cpu/keypad.cpp Normal file
View File

@ -0,0 +1,15 @@
auto CPU::Keypad::run() -> void {
//lookup table to convert button indexes to Emulator::Interface indexes
static const uint lookup[] = {5, 4, 8, 9, 3, 2, 0, 1, 7, 6};
if(!enable) return;
bool test = condition; //0 = OR, 1 = AND
for(auto n : range(10)) {
if(!flag[n]) continue;
bool input = platform->inputPoll(0, 0, lookup[n]);
if(condition == 0) test |= input;
if(condition == 1) test &= input;
}
if(test) cpu.irq.flag |= CPU::Interrupt::Keypad;
}

View File

@ -1,5 +1,5 @@
auto CPU::readIWRAM(uint mode, uint32 addr) -> uint32 {
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
if(memory.disable) return cpu.pipeline.fetch.instruction;
if(mode & Word) return readIWRAM(Half, addr &~ 2) << 0 | readIWRAM(Half, addr | 2) << 16;
if(mode & Half) return readIWRAM(Byte, addr &~ 1) << 0 | readIWRAM(Byte, addr | 1) << 8;
@ -8,7 +8,7 @@ auto CPU::readIWRAM(uint mode, uint32 addr) -> uint32 {
}
auto CPU::writeIWRAM(uint mode, uint32 addr, uint32 word) -> void {
if(regs.memory.control.disable) return;
if(memory.disable) return;
if(mode & Word) {
writeIWRAM(Half, addr &~2, word >> 0);
@ -26,8 +26,8 @@ auto CPU::writeIWRAM(uint mode, uint32 addr, uint32 word) -> void {
}
auto CPU::readEWRAM(uint mode, uint32 addr) -> uint32 {
if(regs.memory.control.disable) return cpu.pipeline.fetch.instruction;
if(!regs.memory.control.ewram) return readIWRAM(mode, addr);
if(memory.disable) return cpu.pipeline.fetch.instruction;
if(!memory.ewram) return readIWRAM(mode, addr);
if(mode & Word) return readEWRAM(Half, addr &~ 2) << 0 | readEWRAM(Half, addr | 2) << 16;
if(mode & Half) return readEWRAM(Byte, addr &~ 1) << 0 | readEWRAM(Byte, addr | 1) << 8;
@ -36,8 +36,8 @@ auto CPU::readEWRAM(uint mode, uint32 addr) -> uint32 {
}
auto CPU::writeEWRAM(uint mode, uint32 addr, uint32 word) -> void {
if(regs.memory.control.disable) return;
if(!regs.memory.control.ewram) return writeIWRAM(mode, addr, word);
if(memory.disable) return;
if(!memory.ewram) return writeIWRAM(mode, addr, word);
if(mode & Word) {
writeEWRAM(Half, addr &~2, word >> 0);

View File

@ -3,33 +3,33 @@ auto CPU::prefetchSync(uint32 addr) -> void {
prefetch.addr = addr;
prefetch.load = addr;
prefetch.wait = wait(Half | Nonsequential, prefetch.load);
prefetch.wait = _wait(Half | Nonsequential, prefetch.load);
}
auto CPU::prefetchStep(uint clocks) -> void {
step(clocks);
if(!regs.wait.control.prefetch || active.dma) return;
if(!wait.prefetch || context.dmaActive) return;
while(!prefetch.full() && clocks--) {
if(--prefetch.wait) continue;
prefetch.slot[prefetch.load >> 1 & 7] = cartridge.read(Half, prefetch.load);
prefetch.load += 2;
prefetch.wait = wait(Half | Sequential, prefetch.load);
prefetch.wait = _wait(Half | Sequential, prefetch.load);
}
}
auto CPU::prefetchWait() -> void {
if(!regs.wait.control.prefetch || active.dma || prefetch.full()) return;
if(!wait.prefetch || context.dmaActive || prefetch.full()) return;
prefetchStep(prefetch.wait);
prefetch.wait = wait(Half | Nonsequential, prefetch.load);
prefetch.wait = _wait(Half | Nonsequential, prefetch.load);
}
auto CPU::prefetchRead() -> uint16 {
if(prefetch.empty()) prefetchStep(prefetch.wait);
else prefetchStep(1);
if(prefetch.full()) prefetch.wait = wait(Half | Sequential, prefetch.load);
if(prefetch.full()) prefetch.wait = _wait(Half | Sequential, prefetch.load);
uint16 half = prefetch.slot[prefetch.addr >> 1 & 7];
prefetch.addr += 2;

View File

@ -1,14 +0,0 @@
struct {
uint16 slot[8] = {0};
uint32 addr = 0; //read location of slot buffer
uint32 load = 0; //write location of slot buffer
int wait = 0; //number of clocks before next slot load
auto empty() const { return addr == load; }
auto full() const { return load - addr == 16; }
} prefetch;
auto prefetchSync(uint32 addr) -> void;
auto prefetchStep(uint clocks) -> void;
auto prefetchWait() -> void;
auto prefetchRead() -> uint16;

View File

@ -1,119 +0,0 @@
struct Registers {
struct DMA {
VariadicNatural source;
VariadicNatural target;
VariadicNatural length;
uint32 data;
struct Control {
uint2 targetmode;
uint2 sourcemode;
uint1 repeat;
uint1 size;
uint1 drq;
uint2 timingmode;
uint1 irq;
uint1 enable;
} control;
//internal
bool pending;
struct Run {
VariadicNatural target;
VariadicNatural source;
VariadicNatural length;
} run;
} dma[4];
struct Timer {
uint16 period;
uint16 reload;
bool pending;
struct Control {
uint2 frequency;
uint1 cascade;
uint1 irq;
uint1 enable;
} control;
} timer[4];
struct Serial {
uint16 data[4];
struct Control {
uint1 shiftclockselect;
uint1 shiftclockfrequency;
uint1 transferenablereceive;
uint1 transferenablesend;
uint1 startbit;
uint1 transferlength;
uint1 irqenable;
} control;
uint8 data8;
} serial;
struct Keypad {
struct Control {
uint1 flag[10];
uint1 enable;
uint1 condition;
} control;
} keypad;
struct Joybus {
struct Settings {
uint1 sc;
uint1 sd;
uint1 si;
uint1 so;
uint1 scmode;
uint1 sdmode;
uint1 simode;
uint1 somode;
uint1 irqenable;
uint2 mode;
} settings;
struct Control {
uint1 resetsignal;
uint1 receivecomplete;
uint1 sendcomplete;
uint1 irqenable;
} control;
uint32 receive;
uint32 transmit;
struct Status {
uint1 receiveflag;
uint1 sendflag;
uint2 generalflag;
} status;
} joybus;
uint1 ime;
struct IRQ {
uint16 enable;
uint16 flag;
} irq;
struct Wait {
struct Control {
uint2 nwait[4];
uint1 swait[4];
uint2 phi;
uint1 prefetch;
uint1 gametype;
} control;
} wait;
struct Memory {
struct Control {
uint1 disable;
uint3 unknown1;
uint1 ewram;
uint4 ewramwait;
uint4 unknown2;
} control;
} memory;
uint1 postboot;
enum class Mode : uint { Normal, Halt, Stop } mode;
uint clock;
} regs;

View File

@ -2,104 +2,99 @@ auto CPU::serialize(serializer& s) -> void {
ARM::serialize(s);
Thread::serialize(s);
s.array(iwram, 32 * 1024);
s.array(ewram, 256 * 1024);
s.array(iwram);
s.array(ewram);
for(auto& dma : regs.dma) {
for(auto& dma : this->dma) {
s.integer(dma.id);
s.boolean(dma.active);
s.integer(dma.waiting);
s.integer(dma.targetMode);
s.integer(dma.sourceMode);
s.integer(dma.repeat);
s.integer(dma.size);
s.integer(dma.drq);
s.integer(dma.timingMode);
s.integer(dma.irq);
s.integer(dma.enable);
s.integer(dma.source);
s.integer(dma.target);
s.integer(dma.length);
s.integer(dma.data);
s.integer(dma.control.targetmode);
s.integer(dma.control.sourcemode);
s.integer(dma.control.repeat);
s.integer(dma.control.size);
s.integer(dma.control.drq);
s.integer(dma.control.timingmode);
s.integer(dma.control.irq);
s.integer(dma.control.enable);
s.integer(dma.pending);
s.integer(dma.run.target);
s.integer(dma.run.source);
s.integer(dma.run.length);
s.integer(dma.latch.target);
s.integer(dma.latch.source);
s.integer(dma.latch.length);
}
for(auto& timer : regs.timer) {
for(auto& timer : this->timer) {
s.integer(timer.id);
s.boolean(timer.pending);
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);
s.integer(timer.control.enable);
s.integer(timer.frequency);
s.integer(timer.cascade);
s.integer(timer.irq);
s.integer(timer.enable);
}
for(auto& value : regs.serial.data) s.integer(value);
s.integer(regs.serial.control.shiftclockselect);
s.integer(regs.serial.control.shiftclockfrequency);
s.integer(regs.serial.control.transferenablereceive);
s.integer(regs.serial.control.transferenablesend);
s.integer(regs.serial.control.startbit);
s.integer(regs.serial.control.transferlength);
s.integer(regs.serial.control.irqenable);
s.integer(regs.serial.data8);
s.integer(serial.shiftClockSelect);
s.integer(serial.shiftClockFrequency);
s.integer(serial.transferEnableReceive);
s.integer(serial.transferEnableSend);
s.integer(serial.startBit);
s.integer(serial.transferLength);
s.integer(serial.irqEnable);
for(auto& value : serial.data) s.integer(value);
s.integer(serial.data8);
for(auto& flag : regs.keypad.control.flag) s.integer(flag);
s.integer(regs.keypad.control.enable);
s.integer(regs.keypad.control.condition);
s.integer(keypad.enable);
s.integer(keypad.condition);
for(auto& flag : keypad.flag) s.integer(flag);
s.integer(regs.joybus.settings.sc);
s.integer(regs.joybus.settings.sd);
s.integer(regs.joybus.settings.si);
s.integer(regs.joybus.settings.so);
s.integer(regs.joybus.settings.scmode);
s.integer(regs.joybus.settings.sdmode);
s.integer(regs.joybus.settings.simode);
s.integer(regs.joybus.settings.somode);
s.integer(regs.joybus.settings.irqenable);
s.integer(regs.joybus.settings.mode);
s.integer(joybus.sc);
s.integer(joybus.sd);
s.integer(joybus.si);
s.integer(joybus.so);
s.integer(joybus.scMode);
s.integer(joybus.sdMode);
s.integer(joybus.siMode);
s.integer(joybus.soMode);
s.integer(joybus.siIRQEnable);
s.integer(joybus.mode);
s.integer(joybus.resetSignal);
s.integer(joybus.receiveComplete);
s.integer(joybus.sendComplete);
s.integer(joybus.resetIRQEnable);
s.integer(joybus.receive);
s.integer(joybus.transmit);
s.integer(joybus.receiveFlag);
s.integer(joybus.sendFlag);
s.integer(joybus.generalFlag);
s.integer(regs.joybus.control.resetsignal);
s.integer(regs.joybus.control.receivecomplete);
s.integer(regs.joybus.control.sendcomplete);
s.integer(regs.joybus.control.irqenable);
s.integer(irq.ime);
s.integer(irq.enable);
s.integer(irq.flag);
s.integer(regs.joybus.receive);
s.integer(regs.joybus.transmit);
for(auto& flag : wait.nwait) s.integer(flag);
for(auto& flag : wait.swait) s.integer(flag);
s.integer(wait.phi);
s.integer(wait.prefetch);
s.integer(wait.gameType);
s.integer(regs.joybus.status.receiveflag);
s.integer(regs.joybus.status.sendflag);
s.integer(regs.joybus.status.generalflag);
s.integer(regs.ime);
s.integer(regs.irq.enable);
s.integer(regs.irq.flag);
for(auto& flag : regs.wait.control.nwait) s.integer(flag);
for(auto& flag : regs.wait.control.swait) s.integer(flag);
s.integer(regs.wait.control.phi);
s.integer(regs.wait.control.prefetch);
s.integer(regs.wait.control.gametype);
s.integer(regs.memory.control.disable);
s.integer(regs.memory.control.unknown1);
s.integer(regs.memory.control.ewram);
s.integer(regs.memory.control.ewramwait);
s.integer(regs.memory.control.unknown2);
s.integer(regs.postboot);
s.integer((uint&)regs.mode);
s.integer(regs.clock);
s.integer(memory.disable);
s.integer(memory.unknown1);
s.integer(memory.ewram);
s.integer(memory.ewramWait);
s.integer(memory.unknown2);
s.array(prefetch.slot);
s.integer(prefetch.addr);
s.integer(prefetch.load);
s.integer(prefetch.wait);
s.integer(pending.dma.vblank);
s.integer(pending.dma.hblank);
s.integer(pending.dma.hdma);
s.integer(active.dma);
s.integer(context.clock);
s.boolean(context.halted);
s.boolean(context.stopped);
s.boolean(context.booted);
s.boolean(context.dmaActive);
}

View File

@ -1,11 +0,0 @@
struct Pending {
struct DMA {
bool vblank;
bool hblank;
bool hdma;
} dma;
} pending;
struct Active {
bool dma;
} active;

View File

@ -1,53 +1,43 @@
auto CPU::timerStep(uint clocks) -> void {
for(auto c : range(clocks)) {
for(auto n : range(4)) {
auto& timer = regs.timer[n];
auto CPU::Timer::run() -> void {
if(cpu.stopped()) return;
if(timer.pending) {
timer.pending = false;
if(timer.control.enable == 1) {
timer.period = timer.reload;
}
continue;
}
if(!timer.control.enable || timer.control.cascade) continue;
static uint mask[] = {0, 63, 255, 1023};
if((regs.clock & mask[timer.control.frequency]) == 0) {
timerIncrement(n);
}
}
regs.clock++;
if(pending) {
pending = false;
if(enable) period = reload;
return;
}
if(!enable || cascade) return;
static const uint mask[] = {0, 63, 255, 1023};
if((cpu.clock() & mask[frequency]) == 0) step();
}
auto CPU::timerIncrement(uint n) -> void {
auto& timer = regs.timer[n];
if(++timer.period == 0) {
timer.period = timer.reload;
auto CPU::Timer::step() -> void {
if(++period == 0) {
period = reload;
if(timer.control.irq) regs.irq.flag |= Interrupt::Timer0 << n;
if(irq) cpu.irq.flag |= CPU::Interrupt::Timer0 << id;
if(apu.fifo[0].timer == n) timerRunFIFO(0);
if(apu.fifo[1].timer == n) timerRunFIFO(1);
if(apu.fifo[0].timer == id) cpu.runFIFO(0);
if(apu.fifo[1].timer == id) cpu.runFIFO(1);
if(n < 3 && regs.timer[n + 1].control.enable && regs.timer[n + 1].control.cascade) {
timerIncrement(n + 1);
if(id < 3 && cpu.timer[id + 1].enable && cpu.timer[id + 1].cascade) {
cpu.timer[id + 1].step();
}
}
}
auto CPU::timerRunFIFO(uint n) -> void {
auto CPU::runFIFO(uint n) -> void {
apu.fifo[n].read();
if(apu.fifo[n].size > 16) return;
auto& dma = regs.dma[1 + n];
if(dma.control.enable && dma.control.timingmode == 3) {
dma.pending = true;
dma.control.targetmode = 2;
dma.control.size = 1;
dma.run.length = 4;
auto& dma = this->dma[1 + n];
if(dma.enable && dma.timingMode == 3) {
dma.active = true;
dma.waiting = 2;
dma.targetMode = 2;
dma.size = 1;
dma.latch.length = 4;
}
}

View File

@ -4,8 +4,8 @@ namespace GameBoyAdvance {
//Game Boy Player emulation
#include "serialization.cpp"
Player player;
#include "serialization.cpp"
auto Player::power() -> void {
status.enable = false;
@ -33,23 +33,23 @@ auto Player::frame() -> void {
//todo: verify which settings are actually required
//values were taken from observing GBP-compatible games
if(!cpu.regs.joybus.settings.sc
&& !cpu.regs.joybus.settings.sd
&& !cpu.regs.joybus.settings.si
&& !cpu.regs.joybus.settings.so
&& !cpu.regs.joybus.settings.scmode
&& !cpu.regs.joybus.settings.sdmode
&& !cpu.regs.joybus.settings.simode
&& !cpu.regs.joybus.settings.somode
&& !cpu.regs.joybus.settings.irqenable
&& !cpu.regs.joybus.settings.mode
&& !cpu.regs.serial.control.shiftclockselect
&& !cpu.regs.serial.control.shiftclockfrequency
&& !cpu.regs.serial.control.transferenablereceive
&& cpu.regs.serial.control.transferenablesend
&& cpu.regs.serial.control.startbit
&& cpu.regs.serial.control.transferlength
&& cpu.regs.serial.control.irqenable
if(!cpu.joybus.sc
&& !cpu.joybus.sd
&& !cpu.joybus.si
&& !cpu.joybus.so
&& !cpu.joybus.scMode
&& !cpu.joybus.sdMode
&& !cpu.joybus.siMode
&& !cpu.joybus.soMode
&& !cpu.joybus.siIRQEnable
&& !cpu.joybus.mode
&& !cpu.serial.shiftClockSelect
&& !cpu.serial.shiftClockFrequency
&& !cpu.serial.transferEnableReceive
&& cpu.serial.transferEnableSend
&& cpu.serial.startBit
&& cpu.serial.transferLength
&& cpu.serial.irqEnable
) {
status.packet = (status.packet + 1) % 17;
switch(status.packet) {
@ -71,7 +71,7 @@ auto Player::frame() -> void {
case 15: status.send = 0x30000003; break;
case 16: status.send = 0x30000003; break;
}
cpu.regs.irq.flag |= CPU::Interrupt::Serial;
cpu.irq.flag |= CPU::Interrupt::Serial;
}
}

View File

@ -22,7 +22,7 @@ PPU ppu;
#include "serialization.cpp"
auto PPU::blank() -> bool {
return io.forceBlank || cpu.regs.mode == CPU::Registers::Mode::Stop;
return io.forceBlank || cpu.stopped();
}
PPU::PPU() {
@ -43,7 +43,7 @@ auto PPU::step(uint clocks) -> void {
}
auto PPU::main() -> void {
cpu.keypadRun();
cpu.keypad.run();
io.vblank = io.vcounter >= 160 && io.vcounter <= 226;
io.vcoincidence = io.vcounter == io.vcompare;
@ -59,12 +59,12 @@ auto PPU::main() -> void {
}
if(io.vcounter == 160) {
if(io.irqvblank) cpu.regs.irq.flag |= CPU::Interrupt::VBlank;
if(io.irqvblank) cpu.irq.flag |= CPU::Interrupt::VBlank;
cpu.dmaVblank();
}
if(io.irqvcoincidence) {
if(io.vcoincidence) cpu.regs.irq.flag |= CPU::Interrupt::VCoincidence;
if(io.vcoincidence) cpu.irq.flag |= CPU::Interrupt::VCoincidence;
}
if(io.vcounter < 160) {
@ -93,7 +93,7 @@ auto PPU::main() -> void {
}
io.hblank = 1;
if(io.irqhblank) cpu.regs.irq.flag |= CPU::Interrupt::HBlank;
if(io.irqhblank) cpu.irq.flag |= CPU::Interrupt::HBlank;
if(io.vcounter < 160) cpu.dmaHblank();
step(240);
@ -114,7 +114,7 @@ auto PPU::refresh() -> void {
}
auto PPU::power() -> void {
create(PPU::Enter, 16'777'216);
create(PPU::Enter, system.frequency());
for(uint n = 0x000; n <= 0x055; n++) bus.io[n] = this;

View File

@ -1,3 +1,5 @@
BIOS bios;
BIOS::BIOS() {
size = 16384;
data = new uint8[size]();

View File

@ -2,12 +2,11 @@
namespace GameBoyAdvance {
System system;
Scheduler scheduler;
#include "bios.cpp"
#include "video.cpp"
#include "serialization.cpp"
BIOS bios;
System system;
Scheduler scheduler;
auto System::init() -> void {
}

View File

@ -16,6 +16,7 @@ struct BIOS {
struct System {
auto loaded() const -> bool { return _loaded; }
auto frequency() const -> double { return 16 * 1024 * 1024; }
auto init() -> void;
auto term() -> void;

View File

@ -225,7 +225,7 @@ auto SPC700::instruction() -> void {
op(0xdb, DirectIndexedWrite, Y, X)
op(0xdc, ImpliedModify, fp(DEC), Y)
op(0xdd, Transfer, Y, A)
op(0xde, BranchNotDirectX)
op(0xde, BranchNotDirectIndexed, X)
op(0xdf, DecimalAdjustAdd)
op(0xe0, OverflowClear)
op(0xe1, CallTable, 14)

View File

@ -93,7 +93,7 @@ auto SPC700::instructionBranch(bool take) -> void {
auto SPC700::instructionBranchBit(uint3 bit, bool match) -> void {
uint8 address = fetch();
uint8 data = load(address);
load(address);
idle();
uint8 displacement = fetch();
if(data.bit(bit) != match) return;
idle();
@ -104,7 +104,7 @@ auto SPC700::instructionBranchBit(uint3 bit, bool match) -> void {
auto SPC700::instructionBranchNotDirect() -> void {
uint8 address = fetch();
uint8 data = load(address);
load(address);
idle();
uint8 displacement = fetch();
if(A == data) return;
idle();
@ -123,10 +123,10 @@ auto SPC700::instructionBranchNotDirectDecrement() -> void {
PC += (int8)displacement;
}
auto SPC700::instructionBranchNotDirectX() -> void {
auto SPC700::instructionBranchNotDirectIndexed(uint8& index) -> void {
uint8 address = fetch();
idle();
uint8 data = load(address + X);
uint8 data = load(address + index);
idle();
uint8 displacement = fetch();
if(A == data) return;
@ -300,7 +300,7 @@ auto SPC700::instructionDirectCompareWord(fpw op) -> void {
auto SPC700::instructionDirectReadWord(fpw op) -> void {
uint8 address = fetch();
uint16 data = load(address + 0);
load(address + 0);
idle();
data |= load(address + 1) << 8;
YA = alu(YA, data);
}

View File

@ -58,7 +58,7 @@ struct SPC700 {
auto instructionBranchBit(uint3, bool) -> void;
auto instructionBranchNotDirect() -> void;
auto instructionBranchNotDirectDecrement() -> void;
auto instructionBranchNotDirectX() -> void;
auto instructionBranchNotDirectIndexed(uint8&) -> void;
auto instructionBranchNotYDecrement() -> void;
auto instructionBreak() -> void;
auto instructionCallAbsolute() -> void;