diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 00adc838..a1c884d6 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -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/"; diff --git a/higan/gba/apu/apu.cpp b/higan/gba/apu/apu.cpp index 8b944813..230a9bad 100644 --- a/higan/gba/apu/apu.cpp +++ b/higan/gba/apu/apu.cpp @@ -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); diff --git a/higan/gba/cartridge/cartridge.cpp b/higan/gba/cartridge/cartridge.cpp index 372bb58b..1ac46302 100644 --- a/higan/gba/cartridge/cartridge.cpp +++ b/higan/gba/cartridge/cartridge.cpp @@ -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]; diff --git a/higan/gba/cpu/bus.cpp b/higan/gba/cpu/bus.cpp index 1c89df6b..962e4637 100644 --- a/higan/gba/cpu/bus.cpp +++ b/higan/gba/cpu/bus.cpp @@ -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; diff --git a/higan/gba/cpu/cpu.cpp b/higan/gba/cpu/cpu.cpp index 6f6878ed..33a8f27b 100644 --- a/higan/gba/cpu/cpu.cpp +++ b/higan/gba/cpu/cpu.cpp @@ -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 } } diff --git a/higan/gba/cpu/cpu.hpp b/higan/gba/cpu/cpu.hpp index e24692f6..8d3e3870 100644 --- a/higan/gba/cpu/cpu.hpp +++ b/higan/gba/cpu/cpu.hpp @@ -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; diff --git a/higan/gba/cpu/dma.cpp b/higan/gba/cpu/dma.cpp index 1ea7d27a..8255de7a 100644 --- a/higan/gba/cpu/dma.cpp +++ b/higan/gba/cpu/dma.cpp @@ -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; } diff --git a/higan/gba/cpu/io.cpp b/higan/gba/cpu/io.cpp index ced7b304..cd7b24f4 100644 --- a/higan/gba/cpu/io.cpp +++ b/higan/gba/cpu/io.cpp @@ -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; } diff --git a/higan/gba/cpu/keypad.cpp b/higan/gba/cpu/keypad.cpp new file mode 100644 index 00000000..886b6b5e --- /dev/null +++ b/higan/gba/cpu/keypad.cpp @@ -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; +} diff --git a/higan/gba/cpu/memory.cpp b/higan/gba/cpu/memory.cpp index 0871149c..b3dbce41 100644 --- a/higan/gba/cpu/memory.cpp +++ b/higan/gba/cpu/memory.cpp @@ -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); diff --git a/higan/gba/cpu/prefetch.cpp b/higan/gba/cpu/prefetch.cpp index 303dda2f..e4b7cf4e 100644 --- a/higan/gba/cpu/prefetch.cpp +++ b/higan/gba/cpu/prefetch.cpp @@ -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; diff --git a/higan/gba/cpu/prefetch.hpp b/higan/gba/cpu/prefetch.hpp deleted file mode 100644 index d96fd6ef..00000000 --- a/higan/gba/cpu/prefetch.hpp +++ /dev/null @@ -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; diff --git a/higan/gba/cpu/registers.hpp b/higan/gba/cpu/registers.hpp deleted file mode 100644 index 6c3c72f7..00000000 --- a/higan/gba/cpu/registers.hpp +++ /dev/null @@ -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; diff --git a/higan/gba/cpu/serialization.cpp b/higan/gba/cpu/serialization.cpp index e9c90917..25794ad7 100644 --- a/higan/gba/cpu/serialization.cpp +++ b/higan/gba/cpu/serialization.cpp @@ -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); } diff --git a/higan/gba/cpu/state.hpp b/higan/gba/cpu/state.hpp deleted file mode 100644 index f43c8340..00000000 --- a/higan/gba/cpu/state.hpp +++ /dev/null @@ -1,11 +0,0 @@ -struct Pending { - struct DMA { - bool vblank; - bool hblank; - bool hdma; - } dma; -} pending; - -struct Active { - bool dma; -} active; diff --git a/higan/gba/cpu/timer.cpp b/higan/gba/cpu/timer.cpp index 24a66dd0..a75ca137 100644 --- a/higan/gba/cpu/timer.cpp +++ b/higan/gba/cpu/timer.cpp @@ -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; } } diff --git a/higan/gba/player/player.cpp b/higan/gba/player/player.cpp index efa7971e..35e65555 100644 --- a/higan/gba/player/player.cpp +++ b/higan/gba/player/player.cpp @@ -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; } } diff --git a/higan/gba/ppu/ppu.cpp b/higan/gba/ppu/ppu.cpp index e94345ba..e9d15680 100644 --- a/higan/gba/ppu/ppu.cpp +++ b/higan/gba/ppu/ppu.cpp @@ -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; diff --git a/higan/gba/system/bios.cpp b/higan/gba/system/bios.cpp index 19dbebbc..0e5989cf 100644 --- a/higan/gba/system/bios.cpp +++ b/higan/gba/system/bios.cpp @@ -1,3 +1,5 @@ +BIOS bios; + BIOS::BIOS() { size = 16384; data = new uint8[size](); diff --git a/higan/gba/system/system.cpp b/higan/gba/system/system.cpp index ea92fc1a..57645e61 100644 --- a/higan/gba/system/system.cpp +++ b/higan/gba/system/system.cpp @@ -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 { } diff --git a/higan/gba/system/system.hpp b/higan/gba/system/system.hpp index 6e491aad..b9236ada 100644 --- a/higan/gba/system/system.hpp +++ b/higan/gba/system/system.hpp @@ -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; diff --git a/higan/processor/spc700/instruction.cpp b/higan/processor/spc700/instruction.cpp index 907d917e..b4291e47 100644 --- a/higan/processor/spc700/instruction.cpp +++ b/higan/processor/spc700/instruction.cpp @@ -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) diff --git a/higan/processor/spc700/instructions.cpp b/higan/processor/spc700/instructions.cpp index 9f9b77f9..610eb7fb 100644 --- a/higan/processor/spc700/instructions.cpp +++ b/higan/processor/spc700/instructions.cpp @@ -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); } diff --git a/higan/processor/spc700/spc700.hpp b/higan/processor/spc700/spc700.hpp index 71e75956..f3a24565 100644 --- a/higan/processor/spc700/spc700.hpp +++ b/higan/processor/spc700/spc700.hpp @@ -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;