From 67014037454b20f395dc3c1681f84874622a0cab Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 19 Mar 2012 22:19:53 +1100 Subject: [PATCH] Update to v087r04 release. byuu says: GBA stuff re-added. Only thing missing that was there before is the ARM branch opcode. Since we're going to be staring at it for a very long time, I added a more interesting test video pattern. Went from 6fps to 912fps. Amazing what being able to divide can do for a frame rate. --- bsnes/Makefile | 3 +- bsnes/base/base.hpp | 2 +- bsnes/gb/video/video.cpp | 2 +- bsnes/gba/Makefile | 14 +++ bsnes/gba/apu/apu.cpp | 25 +++++ bsnes/gba/apu/apu.hpp | 9 ++ bsnes/gba/cartridge/cartridge.cpp | 30 ++++++ bsnes/gba/cartridge/cartridge.hpp | 14 +++ bsnes/gba/cpu/core/core.cpp | 8 ++ bsnes/gba/cpu/core/core.hpp | 4 + bsnes/gba/cpu/core/registers.cpp | 58 +++++++++++ bsnes/gba/cpu/core/registers.hpp | 117 +++++++++++++++++++++++ bsnes/gba/cpu/cpu.cpp | 37 +++++++ bsnes/gba/cpu/cpu.hpp | 16 ++++ bsnes/gba/gba.hpp | 61 ++++++++++++ bsnes/gba/interface/interface.cpp | 21 ++++ bsnes/gba/interface/interface.hpp | 9 ++ bsnes/gba/memory/memory.cpp | 104 ++++++++++++++++++++ bsnes/gba/memory/memory.hpp | 21 ++++ bsnes/gba/ppu/ppu.cpp | 55 +++++++++++ bsnes/gba/ppu/ppu.hpp | 16 ++++ bsnes/gba/scheduler/scheduler.cpp | 30 ++++++ bsnes/gba/scheduler/scheduler.hpp | 16 ++++ bsnes/gba/system/system.cpp | 37 +++++++ bsnes/gba/system/system.hpp | 17 ++++ bsnes/gba/video/video.cpp | 52 ++++++++++ bsnes/gba/video/video.hpp | 11 +++ bsnes/nes/cartridge/ines.cpp | 6 +- bsnes/snes/chip/armdsp/registers.hpp | 9 -- bsnes/target-ui/Makefile | 1 + bsnes/target-ui/base.hpp | 1 + bsnes/target-ui/config/config.cpp | 67 ++++++------- bsnes/target-ui/config/config.hpp | 1 + bsnes/target-ui/general/file-browser.cpp | 15 +-- bsnes/target-ui/general/file-browser.hpp | 2 +- bsnes/target-ui/general/main-window.cpp | 20 ++++ bsnes/target-ui/general/main-window.hpp | 6 ++ bsnes/target-ui/input/gba.cpp | 64 +++++++++++++ bsnes/target-ui/input/gba.hpp | 21 ++++ bsnes/target-ui/input/input.cpp | 2 + bsnes/target-ui/input/input.hpp | 2 + bsnes/target-ui/input/nes.cpp | 2 +- bsnes/target-ui/interface/gba/gba.cpp | 110 +++++++++++++++++++++ bsnes/target-ui/interface/gba/gba.hpp | 20 ++++ bsnes/target-ui/interface/interface.cpp | 9 ++ bsnes/target-ui/interface/interface.hpp | 4 +- bsnes/target-ui/main.cpp | 1 + bsnes/target-ui/settings/audio.cpp | 15 ++- bsnes/target-ui/settings/audio.hpp | 1 + bsnes/target-ui/utility/utility.cpp | 8 ++ bsnes/target-ui/window/window.cpp | 7 ++ bsnes/target-ui/window/window.hpp | 1 + 52 files changed, 1123 insertions(+), 61 deletions(-) create mode 100755 bsnes/gba/Makefile create mode 100755 bsnes/gba/apu/apu.cpp create mode 100755 bsnes/gba/apu/apu.hpp create mode 100755 bsnes/gba/cartridge/cartridge.cpp create mode 100755 bsnes/gba/cartridge/cartridge.hpp create mode 100755 bsnes/gba/cpu/core/core.cpp create mode 100755 bsnes/gba/cpu/core/core.hpp create mode 100755 bsnes/gba/cpu/core/registers.cpp create mode 100755 bsnes/gba/cpu/core/registers.hpp create mode 100755 bsnes/gba/cpu/cpu.cpp create mode 100755 bsnes/gba/cpu/cpu.hpp create mode 100755 bsnes/gba/gba.hpp create mode 100755 bsnes/gba/interface/interface.cpp create mode 100755 bsnes/gba/interface/interface.hpp create mode 100755 bsnes/gba/memory/memory.cpp create mode 100755 bsnes/gba/memory/memory.hpp create mode 100755 bsnes/gba/ppu/ppu.cpp create mode 100755 bsnes/gba/ppu/ppu.hpp create mode 100755 bsnes/gba/scheduler/scheduler.cpp create mode 100755 bsnes/gba/scheduler/scheduler.hpp create mode 100755 bsnes/gba/system/system.cpp create mode 100755 bsnes/gba/system/system.hpp create mode 100755 bsnes/gba/video/video.cpp create mode 100755 bsnes/gba/video/video.hpp create mode 100755 bsnes/target-ui/input/gba.cpp create mode 100755 bsnes/target-ui/input/gba.hpp create mode 100755 bsnes/target-ui/interface/gba/gba.cpp create mode 100755 bsnes/target-ui/interface/gba/gba.hpp diff --git a/bsnes/Makefile b/bsnes/Makefile index 9e8c5ac6..c4c5387c 100755 --- a/bsnes/Makefile +++ b/bsnes/Makefile @@ -3,6 +3,7 @@ include nall/Makefile nes := nes snes := snes gb := gb +gba := gba profile := accuracy target := ui @@ -92,6 +93,6 @@ sync: rm -r phoenix/test archive-all: - tar -cjf bsnes.tar.bz2 base data gb libco nall nes obj out phoenix ruby snes target-debugger target-libsnes target-ui Makefile cc.bat purge.bat + tar -cjf bsnes.tar.bz2 base data gb gba libco nall nes obj out phoenix ruby snes target-debugger target-libsnes target-ui Makefile cc.bat purge.bat help:; diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index e162ae84..aaa69754 100755 --- a/bsnes/base/base.hpp +++ b/bsnes/base/base.hpp @@ -1,7 +1,7 @@ #ifndef BASE_HPP #define BASE_HPP -const char Version[] = "087.03"; +const char Version[] = "087.04"; #include #include diff --git a/bsnes/gb/video/video.cpp b/bsnes/gb/video/video.cpp index d35437b8..6d61c757 100755 --- a/bsnes/gb/video/video.cpp +++ b/bsnes/gb/video/video.cpp @@ -65,7 +65,7 @@ void Video::generate(Format format) { } Video::Video() { - palette = new unsigned[1 << 15]; + palette = new unsigned[1 << 15](); } Video::~Video() { diff --git a/bsnes/gba/Makefile b/bsnes/gba/Makefile new file mode 100755 index 00000000..946e69d9 --- /dev/null +++ b/bsnes/gba/Makefile @@ -0,0 +1,14 @@ +gba_objects := gba-memory gba-interface gba-scheduler gba-system +gba_objects += gba-video gba-cartridge +gba_objects += gba-cpu gba-ppu gba-apu +objects += $(gba_objects) + +obj/gba-memory.o: $(gba)/memory/memory.cpp $(call rwildcard,$(gba)/memory) +obj/gba-interface.o: $(gba)/interface/interface.cpp $(call rwildcard,$(gba)/interface) +obj/gba-scheduler.o: $(gba)/scheduler/scheduler.cpp $(call rwildcard,$(gba)/scheduler) +obj/gba-system.o: $(gba)/system/system.cpp $(call rwildcard,$(gba)/system) +obj/gba-video.o: $(gba)/video/video.cpp $(call rwildcard,$(gba)/video) +obj/gba-cartridge.o: $(gba)/cartridge/cartridge.cpp $(call rwildcard,$(gba)/cartridge) +obj/gba-cpu.o: $(gba)/cpu/cpu.cpp $(call rwildcard,$(gba)/cpu) +obj/gba-ppu.o: $(gba)/ppu/ppu.cpp $(call rwildcard,$(gba)/ppu) +obj/gba-apu.o: $(gba)/apu/apu.cpp $(call rwildcard,$(gba)/apu) diff --git a/bsnes/gba/apu/apu.cpp b/bsnes/gba/apu/apu.cpp new file mode 100755 index 00000000..6d6eecdf --- /dev/null +++ b/bsnes/gba/apu/apu.cpp @@ -0,0 +1,25 @@ +#include + +namespace GBA { + +APU apu; + +void APU::Enter() { apu.enter(); } + +void APU::enter() { + while(true) { + interface->audioSample(0, 0); + step(512); + } +} + +void APU::step(unsigned clocks) { + clock += clocks; + if(clock >= 0) co_switch(cpu.thread); +} + +void APU::power() { + create(APU::Enter, 16777216); +} + +} diff --git a/bsnes/gba/apu/apu.hpp b/bsnes/gba/apu/apu.hpp new file mode 100755 index 00000000..4df22978 --- /dev/null +++ b/bsnes/gba/apu/apu.hpp @@ -0,0 +1,9 @@ +struct APU : Processor { + static void Enter(); + void enter(); + void step(unsigned clocks); + + void power(); +}; + +extern APU apu; diff --git a/bsnes/gba/cartridge/cartridge.cpp b/bsnes/gba/cartridge/cartridge.cpp new file mode 100755 index 00000000..498359ea --- /dev/null +++ b/bsnes/gba/cartridge/cartridge.cpp @@ -0,0 +1,30 @@ +#include + +namespace GBA { + +Cartridge cartridge; + +bool Cartridge::load(const string &markup, const uint8_t *data, unsigned size) { + if(cartridge.rom.data) delete[] cartridge.rom.data; + cartridge.rom.data = new uint8[cartridge.rom.size = size]; + memcpy(cartridge.rom.data, data, size); + + sha256 = nall::sha256(cartridge.rom.data, cartridge.rom.size); + + return loaded = true; +} + +void Cartridge::unload() { + if(loaded) return; + loaded = false; + + delete[] cartridge.rom.data; + cartridge.rom.data = nullptr; + cartridge.rom.size = 0u; +} + +Cartridge::Cartridge() { + loaded = false; +} + +} diff --git a/bsnes/gba/cartridge/cartridge.hpp b/bsnes/gba/cartridge/cartridge.hpp new file mode 100755 index 00000000..1e508540 --- /dev/null +++ b/bsnes/gba/cartridge/cartridge.hpp @@ -0,0 +1,14 @@ +struct Cartridge : property { + StaticMemory rom; + StaticMemory ram; + + readonly loaded; + readonly sha256; + + bool load(const string &markup, const uint8_t *data, unsigned size); + void unload(); + + Cartridge(); +}; + +extern Cartridge cartridge; diff --git a/bsnes/gba/cpu/core/core.cpp b/bsnes/gba/cpu/core/core.cpp new file mode 100755 index 00000000..dc972730 --- /dev/null +++ b/bsnes/gba/cpu/core/core.cpp @@ -0,0 +1,8 @@ +#include "registers.cpp" + +void ARM::power() { + processor.power(); + pipeline.reload = true; + exception = false; + r(15).modify = [&] { pipeline.reload = true; }; +} diff --git a/bsnes/gba/cpu/core/core.hpp b/bsnes/gba/cpu/core/core.hpp new file mode 100755 index 00000000..2a54eb11 --- /dev/null +++ b/bsnes/gba/cpu/core/core.hpp @@ -0,0 +1,4 @@ +struct ARM { + #include "registers.hpp" + void power(); +}; diff --git a/bsnes/gba/cpu/core/registers.cpp b/bsnes/gba/cpu/core/registers.cpp new file mode 100755 index 00000000..12f4dd38 --- /dev/null +++ b/bsnes/gba/cpu/core/registers.cpp @@ -0,0 +1,58 @@ +void ARM::Processor::power() { + r0 = r1 = r2 = r3 = r4 = r5 = r6 = r7 = 0; + usr.r8 = usr.r9 = usr.r10 = usr.r11 = usr.r12 = usr.sp = usr.lr = 0; + fiq.r8 = fiq.r9 = fiq.r10 = fiq.r11 = fiq.r12 = fiq.sp = fiq.lr = 0; + irq.sp = irq.lr = 0; + svc.sp = svc.lr = 0; + abt.sp = abt.lr = 0; + und.sp = und.lr = 0; + pc = 0; + + cpsr = 0; + spsr = nullptr; + fiq.spsr = 0; + irq.spsr = 0; + svc.spsr = 0; + abt.spsr = 0; + und.spsr = 0; + + r[0] = &r0; + r[1] = &r1; + r[2] = &r2; + r[3] = &r3; + r[4] = &r4; + r[5] = &r5; + r[6] = &r6; + r[7] = &r7; + + r[15] = &pc; + + setMode(Mode::SYS); +} + +void ARM::Processor::setMode(Mode mode) { + cpsr.m = (unsigned)mode; + + if(mode == Mode::FIQ) { + r[ 8] = &fiq.r8; + r[ 9] = &fiq.r9; + r[10] = &fiq.r10; + r[11] = &fiq.r11; + r[12] = &fiq.r12; + } else { + r[ 8] = &usr.r8; + r[ 9] = &usr.r9; + r[10] = &usr.r10; + r[11] = &usr.r11; + r[12] = &usr.r12; + } + + switch(mode) { + case Mode::FIQ: r[13] = &fiq.sp; r[14] = &fiq.lr; spsr = &fiq.spsr; break; + case Mode::IRQ: r[13] = &irq.sp; r[14] = &irq.lr; spsr = &irq.spsr; break; + case Mode::SVC: r[13] = &svc.sp; r[14] = &svc.lr; spsr = &svc.spsr; break; + case Mode::ABT: r[13] = &abt.sp; r[14] = &abt.lr; spsr = &abt.spsr; break; + case Mode::UND: r[13] = &und.sp; r[14] = &und.lr; spsr = &und.spsr; break; + default: r[13] = &usr.sp; r[14] = &usr.lr; spsr = nullptr; break; + } +} diff --git a/bsnes/gba/cpu/core/registers.hpp b/bsnes/gba/cpu/core/registers.hpp new file mode 100755 index 00000000..f422dd52 --- /dev/null +++ b/bsnes/gba/cpu/core/registers.hpp @@ -0,0 +1,117 @@ +struct GPR { + uint32 data; + function modify; + + inline operator uint32() const { return data; } + inline GPR& operator=(uint32 n) { data = n; if(modify) modify(); return *this; } + + inline GPR& operator &=(uint32 n) { return operator=(data & n); } + inline GPR& operator |=(uint32 n) { return operator=(data | n); } + inline GPR& operator ^=(uint32 n) { return operator=(data ^ n); } + inline GPR& operator +=(uint32 n) { return operator=(data + n); } + inline GPR& operator -=(uint32 n) { return operator=(data - n); } + inline GPR& operator *=(uint32 n) { return operator=(data * n); } + inline GPR& operator /=(uint32 n) { return operator=(data / n); } + inline GPR& operator %=(uint32 n) { return operator=(data % n); } + inline GPR& operator<<=(uint32 n) { return operator=(data << n); } + inline GPR& operator>>=(uint32 n) { return operator=(data >> n); } +}; + +struct PSR { + bool n; //negative + bool z; //zero + bool c; //carry + bool v; //overflow + bool i; //irq + bool f; //fiq + bool t; //thumb + unsigned m; //mode + + inline operator uint32() const { + return (n << 31) + (z << 30) + (c << 29) + (v << 28) + + (i << 7) + (f << 6) + (t << 5) + (m << 0); + } + + inline PSR& operator=(uint32 d) { + n = d & (1 << 31); + z = d & (1 << 30); + c = d & (1 << 29); + v = d & (1 << 28); + i = d & (1 << 7); + f = d & (1 << 6); + t = d & (1 << 5); + m = d & 31; + return *this; + } +}; + +struct Pipeline { + bool reload; + struct Instruction { + uint32 opcode; + uint32 address; + }; + Instruction execute; + Instruction decode; + Instruction fetch; +}; + +struct Processor { + enum class Mode : unsigned { + USR = 0x10, //user + FIQ = 0x11, //fast interrupt request + IRQ = 0x12, //interrupt request + SVC = 0x13, //supervisor (software interrupt) + ABT = 0x17, //abort + UND = 0x1b, //undefined + SYS = 0x1f, //system + }; + + GPR r0, r1, r2, r3, r4, r5, r6, r7; + + struct USR { + GPR r8, r9, r10, r11, r12, sp, lr; + } usr; + + struct FIQ { + GPR r8, r9, r10, r11, r12, sp, lr; + PSR spsr; + } fiq; + + struct IRQ { + GPR sp, lr; + PSR spsr; + } irq; + + struct SVC { + GPR sp, lr; + PSR spsr; + } svc; + + struct ABT { + GPR sp, lr; + PSR spsr; + } abt; + + struct UND { + GPR sp, lr; + PSR spsr; + } und; + + GPR pc; + PSR cpsr; + + GPR *r[16]; + PSR *spsr; + + void power(); + void setMode(Mode); +}; + +Processor processor; +Pipeline pipeline; +bool exception; + +alwaysinline GPR& r(unsigned n) { return *processor.r[n]; } +alwaysinline PSR& cpsr() { return processor.cpsr; } +alwaysinline PSR& spsr() { return *processor.spsr; } diff --git a/bsnes/gba/cpu/cpu.cpp b/bsnes/gba/cpu/cpu.cpp new file mode 100755 index 00000000..7f92d99a --- /dev/null +++ b/bsnes/gba/cpu/cpu.cpp @@ -0,0 +1,37 @@ +#include + +namespace GBA { + +#include "core/core.cpp" +CPU cpu; + +void CPU::Enter() { cpu.enter(); } + +void CPU::enter() { + while(true) { + step(2); + } +} + +void CPU::step(unsigned clocks) { + ppu.clock -= clocks; + if(ppu.clock < 0) co_switch(ppu.thread); + + apu.clock -= clocks; + if(apu.clock < 0) co_switch(apu.thread); +} + +void CPU::power() { + create(CPU::Enter, 16777216); + + ARM::power(); + for(unsigned n = 0; n < iram.size; n++) iram.data[n] = 0; + for(unsigned n = 0; n < eram.size; n++) eram.data[n] = 0; +} + +CPU::CPU() { + iram.data = new uint8[iram.size = 32 * 1024]; + eram.data = new uint8[eram.size = 256 * 1024]; +} + +} diff --git a/bsnes/gba/cpu/cpu.hpp b/bsnes/gba/cpu/cpu.hpp new file mode 100755 index 00000000..8d94bf53 --- /dev/null +++ b/bsnes/gba/cpu/cpu.hpp @@ -0,0 +1,16 @@ +#include "core/core.hpp" + +struct CPU : Processor, ARM { + StaticMemory iram; + StaticMemory eram; + + static void Enter(); + void enter(); + void step(unsigned clocks); + + void power(); + + CPU(); +}; + +extern CPU cpu; diff --git a/bsnes/gba/gba.hpp b/bsnes/gba/gba.hpp new file mode 100755 index 00000000..a3e20214 --- /dev/null +++ b/bsnes/gba/gba.hpp @@ -0,0 +1,61 @@ +#ifndef GBA_HPP +#define GBA_HPP + +#include + +namespace GBA { + namespace Info { + static const char Name[] = "bgba"; + static const unsigned SerializerVersion = 1; + } +} + +/* + bgba - Game Boy Advance emulator + author: byuu + license: GPLv3 + project started: 2012-03-19 +*/ + +#include + +namespace GBA { + enum : unsigned { Byte = 8, Half = 16, Word = 32 }; + + struct Processor { + cothread_t thread; + unsigned frequency; + signed clock; + + inline void create(void (*entrypoint)(), unsigned frequency) { + if(thread) co_delete(thread); + thread = co_create(65536 * sizeof(void*), entrypoint); + this->frequency = frequency; + clock = 0; + } + + inline void serialize(serializer &s) { + s.integer(frequency); + s.integer(clock); + } + + inline Processor() : thread(nullptr) { + } + + inline ~Processor() { + if(thread) co_delete(thread); + } + }; + + #include + #include + #include + #include + #include + #include + #include + #include + #include +} + +#endif diff --git a/bsnes/gba/interface/interface.cpp b/bsnes/gba/interface/interface.cpp new file mode 100755 index 00000000..b9177c5c --- /dev/null +++ b/bsnes/gba/interface/interface.cpp @@ -0,0 +1,21 @@ +#include + +namespace GBA { + +Interface *interface = nullptr; + +void Interface::videoRefresh(const uint16_t *data) { +} + +void Interface::audioSample(int16_t lsample, int16_t rsample) { +} + +bool Interface::inputPoll(unsigned id) { + return false; +} + +void Interface::message(const string &text) { + print(text, "\n"); +} + +} diff --git a/bsnes/gba/interface/interface.hpp b/bsnes/gba/interface/interface.hpp new file mode 100755 index 00000000..d33517a5 --- /dev/null +++ b/bsnes/gba/interface/interface.hpp @@ -0,0 +1,9 @@ +struct Interface { + virtual void videoRefresh(const uint16_t *data); + virtual void audioSample(int16_t lsample, int16_t rsample); + virtual bool inputPoll(unsigned id); + + virtual void message(const string &text); +}; + +extern Interface *interface; diff --git a/bsnes/gba/memory/memory.cpp b/bsnes/gba/memory/memory.cpp new file mode 100755 index 00000000..9cc99f89 --- /dev/null +++ b/bsnes/gba/memory/memory.cpp @@ -0,0 +1,104 @@ +#include + +namespace GBA { + +Bus bus; + +uint32 StaticMemory::read(uint32 addr, uint32 size) { + unsigned bits = addr & 3; + addr &= ~3; + + uint32 word = 0; + switch(size) { + case Word: word |= data[addr + 3] << 24; + word |= data[addr + 2] << 16; + case Half: word |= data[addr + 1] << 8; + case Byte: word |= data[addr + 0] << 0; + } + + if(bits) { + unsigned rotate = bits << 3; + word = (word >> rotate) | (word << (32 - rotate)); + } + + switch(size) { + case Word: return word; + case Half: return word & 0xffff; + case Byte: return word & 0xff; + } +} + +void StaticMemory::write(uint32 addr, uint32 size, uint32 word) { + switch(size) { + case Word: + addr &= ~3; + data[addr + 3] = word >> 24; + data[addr + 2] = word >> 16; + data[addr + 1] = word >> 8; + data[addr + 0] = word >> 0; + break; + case Half: + addr &= ~1; + data[addr + 1] = word >> 8; + data[addr + 0] = word >> 0; + break; + case Byte: + data[addr + 0] = word >> 0; + break; + } +} + +StaticMemory::StaticMemory() { + data = nullptr; + size = 0u; +} + +StaticMemory::~StaticMemory() { + if(data) delete[] data; +} + +// + +uint32 Bus::read(uint32 addr, uint32 size) { + switch(addr & 0x0f000000) { + case 0x00000000: return system.bios.read(addr & 0x3fff, size); + case 0x01000000: return system.bios.read(addr & 0x3fff, size); + case 0x02000000: return cpu.eram.read(addr & 0x3ffff, size); + case 0x03000000: return cpu.iram.read(addr & 0x7fff, size); + case 0x04000000: return 0u; //MMIO [0x400] + case 0x05000000: return ppu.pram.read(addr & 0x3ff, size); + case 0x06000000: return ppu.vram.read(addr & 0x10000 ? (0x10000 + (addr & 0x7fff)) : (addr & 0xffff), size); + case 0x07000000: return ppu.oam.read(addr & 0x3ff, size); + case 0x08000000: return cartridge.rom.read(addr & 0x1ffffff, size); + case 0x09000000: return cartridge.rom.read(addr & 0x1ffffff, size); + case 0x0a000000: return cartridge.rom.read(addr & 0x1ffffff, size); + case 0x0b000000: return cartridge.rom.read(addr & 0x1ffffff, size); + case 0x0c000000: return cartridge.rom.read(addr & 0x1ffffff, size); + case 0x0d000000: return cartridge.rom.read(addr & 0x1ffffff, size); + case 0x0e000000: return cartridge.ram.read(addr & 0xffff, size); + case 0x0f000000: return cartridge.ram.read(addr & 0xffff, size); + } +} + +void Bus::write(uint32 addr, uint32 size, uint32 word) { + switch(addr & 0x0f000000) { + case 0x00000000: return; + case 0x01000000: return; + case 0x02000000: return cpu.eram.write(addr & 0x3ffff, size, word); + case 0x03000000: return cpu.iram.write(addr & 0x7fff, size, word); + case 0x04000000: return; //MMIO [0x400] + case 0x05000000: return ppu.pram.write(addr & 0x3ff, size, word); + case 0x06000000: return ppu.vram.write(addr & 0x10000 ? (0x10000 + (addr & 0x7fff)) : (addr & 0xffff), size, word); + case 0x07000000: return ppu.oam.write(addr & 0x3ff, size, word); + case 0x08000000: return; + case 0x09000000: return; + case 0x0a000000: return; + case 0x0b000000: return; + case 0x0c000000: return; + case 0x0d000000: return; + case 0x0e000000: return cartridge.ram.write(addr & 0xffff, size, word); + case 0x0f000000: return cartridge.ram.write(addr & 0xffff, size, word); + } +} + +} diff --git a/bsnes/gba/memory/memory.hpp b/bsnes/gba/memory/memory.hpp new file mode 100755 index 00000000..69c20dc2 --- /dev/null +++ b/bsnes/gba/memory/memory.hpp @@ -0,0 +1,21 @@ +struct Memory { + virtual uint32 read(uint32 addr, uint32 size) = 0; + virtual void write(uint32 addr, uint32 size, uint32 word) = 0; +}; + +struct StaticMemory { + uint8_t *data; + unsigned size; + + uint32 read(uint32 addr, uint32 size); + void write(uint32 addr, uint32 size, uint32 word); + StaticMemory(); + ~StaticMemory(); +}; + +struct Bus : Memory { + uint32 read(uint32 addr, uint32 size); + void write(uint32 addr, uint32 size, uint32 word); +}; + +extern Bus bus; diff --git a/bsnes/gba/ppu/ppu.cpp b/bsnes/gba/ppu/ppu.cpp new file mode 100755 index 00000000..72dfd2a1 --- /dev/null +++ b/bsnes/gba/ppu/ppu.cpp @@ -0,0 +1,55 @@ +#include + +namespace GBA { + +PPU ppu; + +void PPU::Enter() { ppu.enter(); } + +void PPU::enter() { + while(true) { + frame(); + step(279620); + } +} + +void PPU::step(unsigned clocks) { + clock += clocks; + if(clock >= 0) co_switch(cpu.thread); +} + +void PPU::power() { + create(PPU::Enter, 16777216); + + for(unsigned n = 0; n < vram.size; n++) vram.data[n] = 0; + for(unsigned n = 0; n < oam.size; n++) oam.data[n] = 0; + for(unsigned n = 0; n < pram.size; n++) pram.data[n] = 0; +} + +void PPU::frame() { + static uint16_t output[240 * 160]; + static bool once = true; + + if(once) { + once = false; + for(signed y = 0; y < 160; y++) { + uint16_t *dp = output + y * 240; + for(signed x = 0; x < 240; x++) { + uint16_t color = sin((x - 60) * 6.283 / 240) * 16 + cos((y - 80) * 6.283 / 160) * 16; + if(color >= 16) color = 31 - color; + *dp++ = color; + } + } + } + + interface->videoRefresh(output); + scheduler.exit(Scheduler::ExitReason::FrameEvent); +} + +PPU::PPU() { + vram.data = new uint8[vram.size = 96 * 1024]; + oam.data = new uint8[oam.size = 1024]; + pram.data = new uint8[pram.size = 1024]; +} + +} diff --git a/bsnes/gba/ppu/ppu.hpp b/bsnes/gba/ppu/ppu.hpp new file mode 100755 index 00000000..6d410ef6 --- /dev/null +++ b/bsnes/gba/ppu/ppu.hpp @@ -0,0 +1,16 @@ +struct PPU : Processor { + StaticMemory vram; + StaticMemory oam; + StaticMemory pram; + + static void Enter(); + void enter(); + void step(unsigned clocks); + + void power(); + void frame(); + + PPU(); +}; + +extern PPU ppu; diff --git a/bsnes/gba/scheduler/scheduler.cpp b/bsnes/gba/scheduler/scheduler.cpp new file mode 100755 index 00000000..3847b1c1 --- /dev/null +++ b/bsnes/gba/scheduler/scheduler.cpp @@ -0,0 +1,30 @@ +#include + +namespace GBA { + +Scheduler scheduler; + +void Scheduler::enter() { + host = co_active(); + co_switch(active); +} + +void Scheduler::exit(ExitReason reason) { + exit_reason = reason; + active = co_active(); + co_switch(host); +} + +void Scheduler::power() { + host = co_active(); + active = cpu.thread; +} + +Scheduler::Scheduler() { + sync = SynchronizeMode::None; + exit_reason = ExitReason::UnknownEvent; + host = nullptr; + active = nullptr; +} + +} diff --git a/bsnes/gba/scheduler/scheduler.hpp b/bsnes/gba/scheduler/scheduler.hpp new file mode 100755 index 00000000..d6643164 --- /dev/null +++ b/bsnes/gba/scheduler/scheduler.hpp @@ -0,0 +1,16 @@ +struct Scheduler : property { + enum class SynchronizeMode : unsigned { None, CPU, All } sync; + enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent }; + readonly exit_reason; + + cothread_t host; + cothread_t active; + + void enter(); + void exit(ExitReason); + + void power(); + Scheduler(); +}; + +extern Scheduler scheduler; diff --git a/bsnes/gba/system/system.cpp b/bsnes/gba/system/system.cpp new file mode 100755 index 00000000..93b8dcd8 --- /dev/null +++ b/bsnes/gba/system/system.cpp @@ -0,0 +1,37 @@ +#include + +namespace GBA { + +System system; + +void System::BIOS::load(const uint8_t *biosdata, unsigned biossize) { + memcpy(data, biosdata, min(size, biossize)); + + string sha256 = nall::sha256(data, size); + if(sha256 != "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570") { + interface->message("Warning: Game Boy Advance BIOS SHA256 sum is incorrect."); + } +} + +System::BIOS::BIOS() { + data = new uint8[size = 16384](); +} + +void System::init() { +} + +void System::term() { +} + +void System::power() { + cpu.power(); + ppu.power(); + apu.power(); + scheduler.power(); +} + +void System::run() { + scheduler.enter(); +} + +} diff --git a/bsnes/gba/system/system.hpp b/bsnes/gba/system/system.hpp new file mode 100755 index 00000000..4315ccf7 --- /dev/null +++ b/bsnes/gba/system/system.hpp @@ -0,0 +1,17 @@ +enum class Input : unsigned { + A, B, Select, Start, Right, Left, Up, Down, R, L, +}; + +struct System { + struct BIOS : StaticMemory { + void load(const uint8_t *data, unsigned size); + BIOS(); + } bios; + + void init(); + void term(); + void power(); + void run(); +}; + +extern System system; diff --git a/bsnes/gba/video/video.cpp b/bsnes/gba/video/video.cpp new file mode 100755 index 00000000..c1d70ef2 --- /dev/null +++ b/bsnes/gba/video/video.cpp @@ -0,0 +1,52 @@ +#include + +namespace GBA { + +Video video; + +unsigned Video::color(unsigned color) const { + uint5 b = color >> 10; + uint5 g = color >> 5; + uint5 r = color >> 0; + + uint10 R = (r << 5) | (r << 0); + uint10 G = (g << 5) | (g << 0); + uint10 B = (b << 5) | (b << 0); + + return (R << 20) | (G << 10) | (B << 0); +} + +void Video::generate(Format format) { + for(unsigned n = 0; n < (1 << 15); n++) palette[n] = color(n); + + if(format == Format::RGB24) { + for(unsigned n = 0; n < (1 << 15); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff); + } + } + + if(format == Format::RGB16) { + for(unsigned n = 0; n < (1 << 15); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f); + } + } + + if(format == Format::RGB15) { + for(unsigned n = 0; n < (1 << 15); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f); + } + } +} + +Video::Video() { + palette = new uint32[1 << 15](); +} + +Video::~Video() { + delete[] palette; +} + +} diff --git a/bsnes/gba/video/video.hpp b/bsnes/gba/video/video.hpp new file mode 100755 index 00000000..5856930b --- /dev/null +++ b/bsnes/gba/video/video.hpp @@ -0,0 +1,11 @@ +struct Video { + enum class Format : unsigned { RGB30, RGB24, RGB16, RGB15 }; + unsigned *palette; + + unsigned color(unsigned color) const; + void generate(Format format); + Video(); + ~Video(); +}; + +extern Video video; diff --git a/bsnes/nes/cartridge/ines.cpp b/bsnes/nes/cartridge/ines.cpp index 902ef440..3bf66794 100755 --- a/bsnes/nes/cartridge/ines.cpp +++ b/bsnes/nes/cartridge/ines.cpp @@ -111,12 +111,12 @@ static string iNES(const uint8_t *data, unsigned size) { case 34: output.append(" \n"); - output.append(" \n"); + output.append(" \n"); break; case 66: output.append(" \n"); - output.append(" \n"); + output.append(" \n"); break; case 69: @@ -128,7 +128,7 @@ static string iNES(const uint8_t *data, unsigned size) { case 73: output.append(" \n"); output.append(" \n"); - output.append(" \n"); + output.append(" \n"); prgram = 8192; break; diff --git a/bsnes/snes/chip/armdsp/registers.hpp b/bsnes/snes/chip/armdsp/registers.hpp index de0dec3e..a320e655 100755 --- a/bsnes/snes/chip/armdsp/registers.hpp +++ b/bsnes/snes/chip/armdsp/registers.hpp @@ -1,12 +1,3 @@ -//Exceptions: -//00000000 = reset -//00000004 = undefined instruction -//00000008 = software interrupt -//0000000c = prefetch abort -//00000010 = data abort -//00000018 = IRQ (interrupt) -//0000001c = FIQ (fast interrupt) - struct Bridge { struct Buffer { bool ready; diff --git a/bsnes/target-ui/Makefile b/bsnes/target-ui/Makefile index 2dafd693..d4cb320f 100755 --- a/bsnes/target-ui/Makefile +++ b/bsnes/target-ui/Makefile @@ -1,6 +1,7 @@ include $(nes)/Makefile include $(snes)/Makefile include $(gb)/Makefile +include $(gba)/Makefile name := bsnes ui_objects := ui-main ui-config ui-interface ui-input ui-utility diff --git a/bsnes/target-ui/base.hpp b/bsnes/target-ui/base.hpp index a8c807a2..1cb41bfd 100755 --- a/bsnes/target-ui/base.hpp +++ b/bsnes/target-ui/base.hpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include diff --git a/bsnes/target-ui/config/config.cpp b/bsnes/target-ui/config/config.cpp index ae659092..7f16707d 100755 --- a/bsnes/target-ui/config/config.cpp +++ b/bsnes/target-ui/config/config.cpp @@ -2,49 +2,50 @@ Config *config = nullptr; Config::Config() { - attach(video.driver = "", "Video::Driver"); - attach(video.filter = "None", "Video::Filter"); - attach(video.shader = "Blur", "Video::Shader"); - attach(video.synchronize = true, "Video::Synchronize"); - attach(video.correctAspectRatio = true, "Video::CorrectAspectRatio"); + append(video.driver = "", "Video::Driver"); + append(video.filter = "None", "Video::Filter"); + append(video.shader = "Blur", "Video::Shader"); + append(video.synchronize = true, "Video::Synchronize"); + append(video.correctAspectRatio = true, "Video::CorrectAspectRatio"); - attach(video.maskOverscan = false, "Video::MaskOverscan"); - attach(video.maskOverscanHorizontal = 8, "Video::MaskOverscanHorizontal"); - attach(video.maskOverscanVertical = 8, "Video::MaskOverscanVertical"); + append(video.maskOverscan = false, "Video::MaskOverscan"); + append(video.maskOverscanHorizontal = 8, "Video::MaskOverscanHorizontal"); + append(video.maskOverscanVertical = 8, "Video::MaskOverscanVertical"); - attach(video.brightness = 100, "Video::Brightness"); - attach(video.contrast = 100, "Video::Contrast"); - attach(video.gamma = 50, "Video::Gamma"); + append(video.brightness = 100, "Video::Brightness"); + append(video.contrast = 100, "Video::Contrast"); + append(video.gamma = 50, "Video::Gamma"); - attach(video.fullScreenMode = 0, "Video::FullScreenMode"); + append(video.fullScreenMode = 0, "Video::FullScreenMode"); - attach(video.startFullScreen = false, "Video::StartFullScreen"); - attach(video.compositionMode = 0, "Video::CompositionMode"); + append(video.startFullScreen = false, "Video::StartFullScreen"); + append(video.compositionMode = 0, "Video::CompositionMode"); - attach(audio.driver = "", "Audio::Driver"); - attach(audio.synchronize = true, "Audio::Synchronize"); - attach(audio.mute = false, "Audio::Mute"); - attach(audio.volume = 100, "Audio::Volume"); - attach(audio.latency = 60, "Audio::Latency"); - attach(audio.resampler = "sinc", "Audio::Resampler"); + append(audio.driver = "", "Audio::Driver"); + append(audio.synchronize = true, "Audio::Synchronize"); + append(audio.mute = false, "Audio::Mute"); + append(audio.volume = 100, "Audio::Volume"); + append(audio.latency = 60, "Audio::Latency"); + append(audio.resampler = "sinc", "Audio::Resampler"); - attach(audio.frequency = 48000, "Audio::Frequency::Native"); - attach(audio.frequencyNES = 1789772, "Audio::Frequency::NES"); - attach(audio.frequencySNES = 32000, "Audio::Frequency::SNES"); - attach(audio.frequencyGB = 4194304, "Audio::Frequency::GB"); + append(audio.frequency = 48000, "Audio::Frequency::Native"); + append(audio.frequencyNES = 1789772, "Audio::Frequency::NES"); + append(audio.frequencySNES = 32000, "Audio::Frequency::SNES"); + append(audio.frequencyGB = 4194304, "Audio::Frequency::GB"); + append(audio.frequencyGBA = 32768, "Audio::Frequency::GBA"); - attach(input.driver = "", "Input::Driver"); - attach(input.focusPolicy = 1, "Input::FocusPolicy"); + append(input.driver = "", "Input::Driver"); + append(input.focusPolicy = 1, "Input::FocusPolicy"); - attach(path.bios.satellaview = "", "Path::BIOS::Satellaview"); - attach(path.bios.sufamiTurbo = "", "Path::BIOS::SufamiTurbo"); - attach(path.bios.superGameBoy = "", "Path::BIOS::SuperGameBoy"); + append(path.bios.satellaview = "", "Path::BIOS::Satellaview"); + append(path.bios.sufamiTurbo = "", "Path::BIOS::SufamiTurbo"); + append(path.bios.superGameBoy = "", "Path::BIOS::SuperGameBoy"); - attach(nes.controllerPort1Device = 1, "NES::Controller::Port1"); - attach(nes.controllerPort2Device = 0, "NES::Controller::Port2"); + append(nes.controllerPort1Device = 1, "NES::Controller::Port1"); + append(nes.controllerPort2Device = 0, "NES::Controller::Port2"); - attach(snes.controllerPort1Device = 1, "SNES::Controller::Port1"); - attach(snes.controllerPort2Device = 0, "SNES::Controller::Port2"); + append(snes.controllerPort1Device = 1, "SNES::Controller::Port1"); + append(snes.controllerPort2Device = 0, "SNES::Controller::Port2"); load(application->path("settings.cfg")); save(application->path("settings.cfg")); diff --git a/bsnes/target-ui/config/config.hpp b/bsnes/target-ui/config/config.hpp index 07a7d09f..dbbc3769 100755 --- a/bsnes/target-ui/config/config.hpp +++ b/bsnes/target-ui/config/config.hpp @@ -34,6 +34,7 @@ struct Config : public configuration { unsigned frequencyNES; unsigned frequencySNES; unsigned frequencyGB; + unsigned frequencyGBA; } audio; struct Input { diff --git a/bsnes/target-ui/general/file-browser.cpp b/bsnes/target-ui/general/file-browser.cpp index cf51b675..9966678e 100755 --- a/bsnes/target-ui/general/file-browser.cpp +++ b/bsnes/target-ui/general/file-browser.cpp @@ -42,13 +42,14 @@ FileBrowser::FileBrowser() { fileList.onChange = { &FileBrowser::synchronize, this }; fileList.onActivate = openButton.onActivate = { &FileBrowser::fileListActivate, this }; - filterModes.append({ "Default", "", { "*" } }); - filterModes.append({ "NES", "", { "*.fc", "*.nes" } }); - filterModes.append({ "SNES", "", { "*.sfc" } }); - filterModes.append({ "GameBoy", "", { "*.gb", "*.gbc" } }); - filterModes.append({ "GameBoyColor", "", { "*.gbc" } }); - filterModes.append({ "Satellaview", "", { "*.bs" } }); - filterModes.append({ "SufamiTurbo", "", { "*.st" } }); + filterModes.append({ "Default", "", { "*" } }); + filterModes.append({ "NES", "", { "*.fc", "*.nes" } }); + filterModes.append({ "SNES", "", { "*.sfc" } }); + filterModes.append({ "GameBoy", "", { "*.gb", "*.gbc" } }); + filterModes.append({ "GameBoyColor", "", { "*.gbc" } }); + filterModes.append({ "GameBoyAdvance", "", { "*.gba" } }); + filterModes.append({ "Satellaview", "", { "*.bs" } }); + filterModes.append({ "SufamiTurbo", "", { "*.st" } }); mode = &filterModes[Mode::Default]; for(auto &mode : filterModes) config.attach(mode.path, mode.name); diff --git a/bsnes/target-ui/general/file-browser.hpp b/bsnes/target-ui/general/file-browser.hpp index 93ce2fef..8092cb3a 100755 --- a/bsnes/target-ui/general/file-browser.hpp +++ b/bsnes/target-ui/general/file-browser.hpp @@ -9,7 +9,7 @@ struct FileBrowser : Window { Label filterLabel; Button openButton; - struct Mode { enum : unsigned { Default, NES, SNES, GameBoy, GameBoyColor, Satellaview, SufamiTurbo }; }; + struct Mode { enum : unsigned { Default, NES, SNES, GameBoy, GameBoyColor, GameBoyAdvance, Satellaview, SufamiTurbo }; }; void open(const string &title, unsigned mode, function callback); FileBrowser(); diff --git a/bsnes/target-ui/general/main-window.cpp b/bsnes/target-ui/general/main-window.cpp index ee5aac82..9eb6f10e 100755 --- a/bsnes/target-ui/general/main-window.cpp +++ b/bsnes/target-ui/general/main-window.cpp @@ -11,6 +11,7 @@ MainWindow::MainWindow() { cartridgeLoadNES.setText("Load &NES Cartridge ..."); cartridgeLoadGameBoy.setText("Load &Game Boy Cartridge ..."); cartridgeLoadGameBoyColor.setText("Load Game Boy &Color Cartridge ..."); + cartridgeLoadGameBoyAdvance.setText("Load Game Boy &Advance Cartridge ..."); cartridgeLoadSatellaviewSlotted.setText("Load Satellaview-Slotted Cartridge ..."); cartridgeLoadSatellaview.setText("Load Satellaview Cartridge ..."); cartridgeLoadSufamiTurbo.setText("Load Sufami Turbo Cartridge ..."); @@ -61,6 +62,10 @@ MainWindow::MainWindow() { gameBoyPower.setText("&Power Cycle"); gameBoyCartridgeUnload.setText("&Unload Cartridge"); + gameBoyAdvanceMenu.setText("&Game Boy Advance"); + gameBoyAdvancePower.setText("&Power Cycle"); + gameBoyAdvanceCartridgeUnload.setText("&Unload Cartridge"); + settingsMenu.setText("S&ettings"); settingsVideoFilter.setText("Video &Filter"); settingsVideoFilterNone.setText("None"); @@ -103,6 +108,7 @@ MainWindow::MainWindow() { cartridgeMenu.append(cartridgeLoadSNES); cartridgeMenu.append(cartridgeLoadGameBoy); cartridgeMenu.append(cartridgeLoadGameBoyColor); + cartridgeMenu.append(cartridgeLoadGameBoyAdvance); cartridgeMenu.append(cartridgeSeparator); cartridgeMenu.append(cartridgeLoadSatellaviewSlotted); cartridgeMenu.append(cartridgeLoadSatellaview); @@ -138,6 +144,11 @@ MainWindow::MainWindow() { gameBoyMenu.append(gameBoySeparator); gameBoyMenu.append(gameBoyCartridgeUnload); + append(gameBoyAdvanceMenu); + gameBoyAdvanceMenu.append(gameBoyAdvancePower); + gameBoyAdvanceMenu.append(gameBoyAdvanceSeparator); + gameBoyAdvanceMenu.append(gameBoyAdvanceCartridgeUnload); + append(settingsMenu); settingsMenu.append(settingsVideoFilter); settingsVideoFilter.append(settingsVideoFilterNone); @@ -215,6 +226,12 @@ MainWindow::MainWindow() { }); }; + cartridgeLoadGameBoyAdvance.onActivate = [&] { + fileBrowser->open("Load Cartridge - Game Boy Advance", FileBrowser::Mode::GameBoyAdvance, [](string filename) { + interface->gba.loadCartridge(filename); + }); + }; + cartridgeLoadSatellaviewSlotted.onActivate = [&] { slotLoader->loadSatellaviewSlotted(); }; cartridgeLoadSatellaview.onActivate = [&] { slotLoader->loadSatellaview(); }; cartridgeLoadSufamiTurbo.onActivate = [&] { slotLoader->loadSufamiTurbo(); }; @@ -254,6 +271,9 @@ MainWindow::MainWindow() { gameBoyPower.onActivate = { &Interface::power, interface }; gameBoyCartridgeUnload.onActivate = { &Interface::unloadCartridge, interface }; + gameBoyAdvancePower.onActivate = { &Interface::power, interface }; + gameBoyAdvanceCartridgeUnload.onActivate = { &Interface::unloadCartridge, interface }; + settingsVideoFilterNone.onActivate = [&] { config->video.filter = "None"; utility->bindVideoFilter(); diff --git a/bsnes/target-ui/general/main-window.hpp b/bsnes/target-ui/general/main-window.hpp index a29ff481..66241e44 100755 --- a/bsnes/target-ui/general/main-window.hpp +++ b/bsnes/target-ui/general/main-window.hpp @@ -7,6 +7,7 @@ struct MainWindow : Window { Item cartridgeLoadNES; Item cartridgeLoadGameBoy; Item cartridgeLoadGameBoyColor; + Item cartridgeLoadGameBoyAdvance; Separator cartridgeSeparator; Item cartridgeLoadSatellaviewSlotted; Item cartridgeLoadSatellaview; @@ -40,6 +41,11 @@ struct MainWindow : Window { Separator gameBoySeparator; Item gameBoyCartridgeUnload; + Menu gameBoyAdvanceMenu; + Item gameBoyAdvancePower; + Separator gameBoyAdvanceSeparator; + Item gameBoyAdvanceCartridgeUnload; + Menu settingsMenu; Menu settingsVideoFilter; RadioItem settingsVideoFilterNone; diff --git a/bsnes/target-ui/input/gba.cpp b/bsnes/target-ui/input/gba.cpp new file mode 100755 index 00000000..43440070 --- /dev/null +++ b/bsnes/target-ui/input/gba.cpp @@ -0,0 +1,64 @@ +int16_t GbaController::poll(unsigned n) { + switch((GBA::Input)n) { + case GBA::Input::Up: return up.poll() & !down.poll(); + case GBA::Input::Down: return down.poll() & !up.poll(); + case GBA::Input::Left: return left.poll() & !right.poll(); + case GBA::Input::Right: return right.poll() & !left.poll(); + case GBA::Input::B: return b.poll() | bTurbo.poll(); + case GBA::Input::A: return a.poll() | aTurbo.poll(); + case GBA::Input::L: return l.poll() | lTurbo.poll(); + case GBA::Input::R: return r.poll() | rTurbo.poll(); + case GBA::Input::Select: return select.poll(); + case GBA::Input::Start: return start.poll(); + } + + return 0; //never reached +} + +GbaController::GbaController() { + name = "Controller"; + + up.name = "Up"; + down.name = "Down"; + left.name = "Left"; + right.name = "Right"; + b.name = "B"; + a.name = "A"; + l.name = "L"; + r.name = "R"; + select.name = "Select"; + start.name = "Start"; + + bTurbo.name = "Turbo B"; + aTurbo.name = "Turbo A"; + lTurbo.name = "Turbo L"; + rTurbo.name = "Turbo R"; + + up.mapping = "KB0::Up"; + down.mapping = "KB0::Down"; + left.mapping = "KB0::Left"; + right.mapping = "KB0::Right"; + b.mapping = "KB0::Z"; + a.mapping = "KB0::X"; + l.mapping = "KB0::A"; + r.mapping = "KB0::S"; + select.mapping = "KB0::Apostrophe"; + start.mapping = "KB0::Return"; + + append(up, down, left, right, b, a, l, r, select, start); + append(bTurbo, aTurbo, lTurbo, rTurbo); +} + +// + +GbaDevice::GbaDevice() { + name = "Device"; + append(controller); +} + +// + +GbaInput::GbaInput() { + name = "Game Boy Advance"; + append(device); +} diff --git a/bsnes/target-ui/input/gba.hpp b/bsnes/target-ui/input/gba.hpp new file mode 100755 index 00000000..726b4cb6 --- /dev/null +++ b/bsnes/target-ui/input/gba.hpp @@ -0,0 +1,21 @@ +struct GbaController : TertiaryInput { + DigitalInput up, down, left, right; + DigitalInput b, a, l, r; + DigitalInput select, start; + TurboInput bTurbo, aTurbo, lTurbo, rTurbo; + + int16_t poll(unsigned n); + GbaController(); +}; + +struct GbaDevice : SecondaryInput { + GbaController controller; + + GbaDevice(); +}; + +struct GbaInput : PrimaryInput { + GbaDevice device; + + GbaInput(); +}; diff --git a/bsnes/target-ui/input/input.cpp b/bsnes/target-ui/input/input.cpp index 00086b6c..33cd49d3 100755 --- a/bsnes/target-ui/input/input.cpp +++ b/bsnes/target-ui/input/input.cpp @@ -2,6 +2,7 @@ #include "nes.cpp" #include "snes.cpp" #include "gb.cpp" +#include "gba.cpp" #include "user-interface.cpp" InputManager *inputManager = nullptr; @@ -200,6 +201,7 @@ InputManager::InputManager() { inputList.append(nes); inputList.append(snes); inputList.append(gb); + inputList.append(gba); inputList.append(userInterface); for(unsigned n = 0; n < inputList.size(); n++) inputList[n].attach(); diff --git a/bsnes/target-ui/input/input.hpp b/bsnes/target-ui/input/input.hpp index a7011baf..ec83e960 100755 --- a/bsnes/target-ui/input/input.hpp +++ b/bsnes/target-ui/input/input.hpp @@ -52,6 +52,7 @@ struct PrimaryInput : array { #include "nes.hpp" #include "snes.hpp" #include "gb.hpp" +#include "gba.hpp" #include "user-interface.hpp" struct InputManager { @@ -63,6 +64,7 @@ struct InputManager { NesInput nes; SnesInput snes; GbInput gb; + GbaInput gba; UserInterfaceInput userInterface; void scan(); diff --git a/bsnes/target-ui/input/nes.cpp b/bsnes/target-ui/input/nes.cpp index 60703a0e..e5ef5246 100755 --- a/bsnes/target-ui/input/nes.cpp +++ b/bsnes/target-ui/input/nes.cpp @@ -56,5 +56,5 @@ NesPort2Input::NesPort2Input() { NesInput::NesInput() { name = "NES"; - append(port1, port1); + append(port1, port2); } diff --git a/bsnes/target-ui/interface/gba/gba.cpp b/bsnes/target-ui/interface/gba/gba.cpp new file mode 100755 index 00000000..f8633f74 --- /dev/null +++ b/bsnes/target-ui/interface/gba/gba.cpp @@ -0,0 +1,110 @@ +void InterfaceGBA::initialize() { + GBA::interface = this; + GBA::system.init(); +} + +bool InterfaceGBA::cartridgeLoaded() { + return GBA::cartridge.loaded(); +} + +bool InterfaceGBA::loadCartridge(const string &filename) { + interface->unloadCartridge(); + + uint8_t *biosdata; + unsigned biossize; + + uint8_t *cartdata; + unsigned cartsize; + + if(filename.endswith("/")) { + if(file::exists({filename, "bios.rom"}) == false) { + message("Error: Game Boy Advance BIOS (bios.rom) not found."); + return false; + } + if(file::read({filename, "bios.rom"}, biosdata, biossize) == false) return false; + if(file::read({filename, "program.rom"}, cartdata, cartsize) == false) return false; + interface->base = {true, filename}; + } else { + if(file::exists({dir(filename), "gbabios.rom"}) == false) { + message("Error: Game Boy Advance BIOS (gbabios.rom) not found."); + return false; + } + if(file::read({dir(filename), "gbabios.rom"}, biosdata, biossize) == false) return false; + if(file::read(filename, cartdata, cartsize) == false) return false; + interface->base = {false, filename}; + } + + interface->game = interface->base; + interface->cartridgeTitle = interface->base.title(); + interface->applyPatch(interface->base, cartdata, cartsize); + + string markup; + markup.readfile(interface->base.filename("manifest.xml", ".xml")); + + GBA::system.bios.load(biosdata, biossize); + GBA::cartridge.load(markup, cartdata, cartsize); + GBA::system.power(); + delete[] biosdata; + delete[] cartdata; + + GBA::video.generate(GBA::Video::Format::RGB30); + interface->loadCartridge(::Interface::Mode::GBA); + return true; +} + +void InterfaceGBA::unloadCartridge() { + return GBA::cartridge.unload(); +} + +void InterfaceGBA::power() { + return GBA::system.power(); +} + +void InterfaceGBA::reset() { + return GBA::system.power(); //GBA has no reset button +} + +void InterfaceGBA::run() { + return GBA::system.run(); +} + +serializer InterfaceGBA::serialize() { + return serializer(); +} + +bool InterfaceGBA::unserialize(serializer &s) { + return false; +} + +void InterfaceGBA::setCheats(const lstring &list) { +} + +// + +void InterfaceGBA::videoRefresh(const uint16_t *data) { + static uint32_t output[240 * 160]; + + for(unsigned y = 0; y < 160; y++) { + const uint16_t *sp = data + y * 240; + uint32_t *dp = output + y * 240; + for(unsigned x = 0; x < 240; x++) { + uint16_t color = *sp++; + *dp++ = GBA::video.palette[color]; + } + } + + interface->videoRefresh(output, 240 * sizeof(uint32_t), 240, 160); +} + +void InterfaceGBA::audioSample(int16_t lsample, int16_t rsample) { + signed samples[] = { lsample, rsample }; + dspaudio.sample(samples); + while(dspaudio.pending()) { + dspaudio.read(samples); + audio.sample(samples[0], samples[1]); + } +} + +bool InterfaceGBA::inputPoll(unsigned id) { + return inputManager->gba.device.controller.poll(id); +} diff --git a/bsnes/target-ui/interface/gba/gba.hpp b/bsnes/target-ui/interface/gba/gba.hpp new file mode 100755 index 00000000..dd313f2f --- /dev/null +++ b/bsnes/target-ui/interface/gba/gba.hpp @@ -0,0 +1,20 @@ +struct InterfaceGBA : InterfaceCore, GBA::Interface { + void initialize(); + + bool cartridgeLoaded(); + bool loadCartridge(const string &filename); + void unloadCartridge(); + + void power(); + void reset(); + void run(); + + serializer serialize(); + bool unserialize(serializer&); + + void setCheats(const lstring &list = lstring{}); + + void videoRefresh(const uint16_t *data); + void audioSample(int16_t lsample, int16_t rsample); + bool inputPoll(unsigned id); +}; diff --git a/bsnes/target-ui/interface/interface.cpp b/bsnes/target-ui/interface/interface.cpp index 50f110af..cb814391 100755 --- a/bsnes/target-ui/interface/interface.cpp +++ b/bsnes/target-ui/interface/interface.cpp @@ -3,6 +3,7 @@ #include "nes/nes.cpp" #include "snes/snes.cpp" #include "gb/gb.cpp" +#include "gba/gba.cpp" Interface *interface = nullptr; Filter filter; @@ -74,6 +75,7 @@ void Interface::updateDSP() { case Mode::NES: return dspaudio.setFrequency(config->audio.frequencyNES); case Mode::SNES: return dspaudio.setFrequency(config->audio.frequencySNES); case Mode::GB: return dspaudio.setFrequency(config->audio.frequencyGB); + case Mode::GBA: return dspaudio.setFrequency(config->audio.frequencyGBA); } } @@ -82,6 +84,7 @@ bool Interface::cartridgeLoaded() { case Mode::NES: return nes.cartridgeLoaded(); case Mode::SNES: return snes.cartridgeLoaded(); case Mode::GB: return gb.cartridgeLoaded(); + case Mode::GBA: return gba.cartridgeLoaded(); } return false; } @@ -92,6 +95,7 @@ void Interface::loadCartridge(Mode mode) { case Mode::NES: core = &nes; break; case Mode::SNES: core = &snes; break; case Mode::GB: core = &gb; break; + case Mode::GBA: core = &gba; break; default: core = nullptr; break; } @@ -110,6 +114,7 @@ bool Interface::loadCartridge(string filename) { if(filename.endswith(".sfc") || filename.endswith(".sfc/")) result = snes.loadCartridge(filename); if(filename.endswith(".gb" ) || filename.endswith(".gb/" )) result = gb.loadCartridge(GB::System::Revision::GameBoy, filename); if(filename.endswith(".gbc") || filename.endswith(".gbc/")) result = gb.loadCartridge(GB::System::Revision::GameBoyColor, filename); + if(filename.endswith(".gba") || filename.endswith(".gba/")) result = gba.loadCartridge(filename); return result; } @@ -124,6 +129,7 @@ void Interface::unloadCartridge() { case Mode::NES: nes.unloadCartridge(); break; case Mode::SNES: snes.unloadCartridge(); break; case Mode::GB: gb.unloadCartridge(); break; + case Mode::GBA: gba.unloadCartridge(); break; } cartridgeTitle = ""; @@ -184,6 +190,7 @@ void Interface::setCheatCodes(const lstring &list) { case Mode::NES: return nes.setCheats(list); case Mode::SNES: return snes.setCheats(list); case Mode::GB: return gb.setCheats(list); + case Mode::GBA: return gba.setCheats(list); } } @@ -192,6 +199,7 @@ string Interface::sha256() { case Mode::NES: return NES::cartridge.sha256(); case Mode::SNES: return SNES::cartridge.sha256(); case Mode::GB: return GB::cartridge.sha256(); + case Mode::GBA: return GBA::cartridge.sha256(); } return "{None}"; } @@ -201,6 +209,7 @@ Interface::Interface() : core(nullptr) { nes.initialize(); snes.initialize(); gb.initialize(); + gba.initialize(); } //internal diff --git a/bsnes/target-ui/interface/interface.hpp b/bsnes/target-ui/interface/interface.hpp index 4484590e..d336d2a7 100755 --- a/bsnes/target-ui/interface/interface.hpp +++ b/bsnes/target-ui/interface/interface.hpp @@ -19,6 +19,7 @@ struct CartridgePath { #include "nes/nes.hpp" #include "snes/snes.hpp" #include "gb/gb.hpp" +#include "gba/gba.hpp" struct Filter : public library { function dl_size; @@ -36,7 +37,7 @@ struct Filter : public library { extern Filter filter; struct Interface : property { - enum class Mode : unsigned { None, SNES, NES, GB }; + enum class Mode : unsigned { None, SNES, NES, GB, GBA }; readonly mode; void bindControllers(); @@ -74,6 +75,7 @@ struct Interface : property { InterfaceNES nes; InterfaceSNES snes; InterfaceGB gb; + InterfaceGBA gba; }; extern Interface *interface; diff --git a/bsnes/target-ui/main.cpp b/bsnes/target-ui/main.cpp index 483fea67..2c2d7c2c 100755 --- a/bsnes/target-ui/main.cpp +++ b/bsnes/target-ui/main.cpp @@ -125,6 +125,7 @@ Application::Application(int argc, char **argv) { interface->unloadCartridge(); windowManager->saveGeometry(); + windowManager->hideAll(); if(compositionEnable) compositor::enable(true); } diff --git a/bsnes/target-ui/settings/audio.cpp b/bsnes/target-ui/settings/audio.cpp index 37a5fd51..62938c05 100755 --- a/bsnes/target-ui/settings/audio.cpp +++ b/bsnes/target-ui/settings/audio.cpp @@ -1,7 +1,7 @@ AudioSettings *audioSettings = nullptr; AudioSlider::AudioSlider() { - append(name, { 45, 0 }); + append(name, { 50, 0 }); append(value, { 75, 0 }); append(slider, { ~0, 0 }); } @@ -68,6 +68,11 @@ AudioSettings::AudioSettings() { gb.base = 4194304; gb.step = 131; + gba.name.setText("GBA:"); + gba.slider.setLength(2001); + gba.base = 32768; + gba.step = 1; + append(title, { ~0, 0 }, 5); append(outputLabel, { ~0, 0 }, 0); append(outputLayout, { ~0, 0 }, 5); @@ -82,6 +87,7 @@ AudioSettings::AudioSettings() { append(nes, { ~0, 0 }, 0); append(snes, { ~0, 0 }, 0); append(gb, { ~0, 0 }, 0); + append(gba, { ~0, 0 }, 0); frequencySelection.setSelection( config->audio.frequency == 32000 ? 0 : @@ -109,9 +115,10 @@ AudioSettings::AudioSettings() { nes.setPosition(config->audio.frequencyNES); snes.setPosition(config->audio.frequencySNES); gb.setPosition(config->audio.frequencyGB); + gba.setPosition(config->audio.frequencyGBA); - frequencySelection.onChange = latencySelection.onChange = resamplerSelection.onChange = - volume.slider.onChange = nes.slider.onChange = snes.slider.onChange = gb.slider.onChange = + frequencySelection.onChange = latencySelection.onChange = resamplerSelection.onChange = volume.slider.onChange = + nes.slider.onChange = snes.slider.onChange = gb.slider.onChange = gba.slider.onChange = { &AudioSettings::synchronize, this }; synchronize(); @@ -141,10 +148,12 @@ void AudioSettings::synchronize() { config->audio.frequencyNES = nes.position(); config->audio.frequencySNES = snes.position(); config->audio.frequencyGB = gb.position(); + config->audio.frequencyGBA = gba.position(); nes.value.setText({ nes.position(), "hz" }); snes.value.setText({ snes.position(), "hz" }); gb.value.setText({ gb.position(), "hz" }); + gba.value.setText({ gba.position(), "hz" }); volume.value.setText({ volume.position(), "%" }); interface->updateDSP(); diff --git a/bsnes/target-ui/settings/audio.hpp b/bsnes/target-ui/settings/audio.hpp index a988d118..dfff2db3 100755 --- a/bsnes/target-ui/settings/audio.hpp +++ b/bsnes/target-ui/settings/audio.hpp @@ -26,6 +26,7 @@ struct AudioSettings : SettingsLayout { AudioSlider nes; AudioSlider snes; AudioSlider gb; + AudioSlider gba; void synchronize(); AudioSettings(); diff --git a/bsnes/target-ui/utility/utility.cpp b/bsnes/target-ui/utility/utility.cpp index 74541206..b8833968 100755 --- a/bsnes/target-ui/utility/utility.cpp +++ b/bsnes/target-ui/utility/utility.cpp @@ -8,6 +8,7 @@ void Utility::setMode(Interface::Mode mode) { mainWindow->nesMenu.setVisible(false); mainWindow->snesMenu.setVisible(false); mainWindow->gameBoyMenu.setVisible(false); + mainWindow->gameBoyAdvanceMenu.setVisible(false); if(mode == Interface::Mode::None) { mainWindow->setTitle(application->title); @@ -37,6 +38,12 @@ void Utility::setMode(Interface::Mode mode) { dspaudio.setChannels(2); } + else if(mode == Interface::Mode::GBA) { + mainWindow->setTitle(interface->cartridgeTitle); + mainWindow->gameBoyAdvanceMenu.setVisible(true); + dspaudio.setChannels(2); + } + interface->updateDSP(); mainWindow->synchronize(); resizeMainWindow(); @@ -51,6 +58,7 @@ void Utility::resizeMainWindow(bool shrink) { case Interface::Mode::NES: width = 256, height = 240; break; case Interface::Mode::SNES: width = 256, height = 240; break; case Interface::Mode::GB: width = 160, height = 144; break; + case Interface::Mode::GBA: width = 240, height = 160; break; } if(config->video.correctAspectRatio) { diff --git a/bsnes/target-ui/window/window.cpp b/bsnes/target-ui/window/window.cpp index 26f85cd7..6fe91a70 100755 --- a/bsnes/target-ui/window/window.cpp +++ b/bsnes/target-ui/window/window.cpp @@ -44,3 +44,10 @@ void WindowManager::saveGeometry() { } config.save(application->path("geometry.cfg")); } + +//phoenix can destruct windows that are hidden faster +void WindowManager::hideAll() { + for(auto &window : windowList) { + window.window->setVisible(false); + } +} diff --git a/bsnes/target-ui/window/window.hpp b/bsnes/target-ui/window/window.hpp index e67ab928..41184cd3 100755 --- a/bsnes/target-ui/window/window.hpp +++ b/bsnes/target-ui/window/window.hpp @@ -13,6 +13,7 @@ struct WindowManager { void loadGeometry(); void saveGeometry(); + void hideAll(); private: configuration config;