mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
16f736307e
commit
d4876a831f
|
@ -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/";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
struct Pending {
|
||||
struct DMA {
|
||||
bool vblank;
|
||||
bool hblank;
|
||||
bool hdma;
|
||||
} dma;
|
||||
} pending;
|
||||
|
||||
struct Active {
|
||||
bool dma;
|
||||
} active;
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
BIOS bios;
|
||||
|
||||
BIOS::BIOS() {
|
||||
size = 16384;
|
||||
data = new uint8[size]();
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue