bsnes/higan/gba/cpu/cpu.cpp

177 lines
4.9 KiB
C++
Raw Normal View History

#include <gba/gba.hpp>
namespace GameBoyAdvance {
#include "prefetch.cpp"
#include "bus.cpp"
#include "mmio.cpp"
#include "memory.cpp"
#include "dma.cpp"
#include "timer.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 = regs.ime && (regs.irq.enable & regs.irq.flag);
if(regs.mode == Registers::Mode::Stop) {
if(!(regs.irq.enable & regs.irq.flag & Interrupt::Keypad)) {
sync_step(16); //STOP does not advance timers
} else {
regs.mode = Registers::Mode::Normal;
}
return;
}
dma_run();
if(regs.mode == Registers::Mode::Halt) {
if(!(regs.irq.enable & regs.irq.flag)) {
step(16);
} else {
regs.mode = Registers::Mode::Normal;
}
return;
}
exec();
}
auto CPU::step(uint clocks) -> void {
timer_step(clocks);
sync_step(clocks);
}
auto CPU::sync_step(uint clocks) -> void {
ppu.clock -= clocks;
if(ppu.clock < 0) co_switch(ppu.thread);
apu.clock -= clocks;
if(apu.clock < 0) co_switch(apu.thread);
}
auto CPU::keypad_run() -> void {
if(regs.keypad.control.enable == false) return;
bool test = regs.keypad.control.condition; //0 = OR, 1 = AND
for(auto n : range(10)) {
if(regs.keypad.control.flag[n] == false) continue;
bool input = interface->inputPoll(0, 0, 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;
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;
Update to v087r30 release. byuu says: Changelog: - DMA channel masks added (some are 27-bit source/target and some are 14-bit length -- hooray, varuint_t class.) - No more state.pending flags. Instead, we set dma.pending flag when we want a transfer (fixes GBA Video - Pokemon audio) [Cydrak] - fixed OBJ Vmosaic [Cydrak, krom] - OBJ cannot read <=0x13fff in BG modes 3-5 (fixes the garbled tile at the top-left of some games) - DMA timing should be much closer to hardware now, but probably not perfect - PPU frame blending uses blargg's bit-perfect, rounded method (slower, but what can you do?) - GBA carts really unload now - added nall/gba/cartridge.hpp: used when there is no manifest. Scans ROMs for library tags, and selects the first valid one found - added EEPROM auto-detection when EEPROM size=0. Forces disk/save state size to 8192 (otherwise states could crash between pre and post detect.) - detects first read after a set read address command when the size is zero, and sets all subsequent bit-lengths to that value, prints detected size to terminal - added nall/nes/cartridge.hpp: moves iNES detection out of emulation core. Important to note: long-term goal is to remove all nall/(system)/cartridge.hpp detections from the core and replace with databases. All in good time. Anyway, the GBA workarounds should work for ~98.5% of the library, if my pre-scanning was correct (~40 games with odd tags. I reject ones without numeric versions now, too.) I think we're basically at a point where we can release a new version now. Compatibility should be relatively high (at least for a first release), and fixes are only going to affect one or two games at a time. I'd like to start doing some major cleaning house internally (rename NES->Famicom, SNES->SuperFamicom and such.) Would be much wiser to do that on a .01 WIP to minimize regressions. The main problems with a release now: - speed is pretty bad, haven't really optimized much yet (not sure how much we can improve it yet, this usually isn't easy) - sound isn't -great-, but the GBA audio sucks anyway :P - couple of known bugs (Sonic X video, etc.)
2012-04-22 20:49:19 +10:00
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;
}
for(auto& flag : regs.keypad.control.flag) flag = 0;
regs.keypad.control.enable = 0;
regs.keypad.control.condition = 0;
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;
regs.postboot = 0;
regs.mode = Registers::Mode::Normal;
regs.clock = 0;
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;
pending.dma.vblank = 0;
pending.dma.hblank = 0;
pending.dma.hdma = 0;
active.dma = false;
for(uint n = 0x0b0; n <= 0x0df; n++) bus.mmio[n] = this; //DMA
for(uint n = 0x100; n <= 0x10f; n++) bus.mmio[n] = this; //Timers
for(uint n = 0x120; n <= 0x12b; n++) bus.mmio[n] = this; //Serial
for(uint n = 0x130; n <= 0x133; n++) bus.mmio[n] = this; //Keypad
for(uint n = 0x134; n <= 0x159; n++) bus.mmio[n] = this; //Serial
for(uint n = 0x200; n <= 0x209; n++) bus.mmio[n] = this; //System
for(uint n = 0x300; n <= 0x301; n++) bus.mmio[n] = this; //System
//0x080-0x083 mirrored via gba/memory/memory.cpp //System
}
}