From 8db134843f2f8121161f2f25a31dd608e33c0e6c Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sun, 18 Mar 2012 12:04:22 +1100 Subject: [PATCH] Update to v087r03 release. byuu says: I wanted to keep this a secret, but unlike other recent additions, this will easily take several weeks, maybe months, to show anything. Assuming I can even pull it off. Nothing technically overwhelming here, I'm more worried about the near-impossibility of debugging the CPU. --- bsnes/Makefile | 16 +++- bsnes/base/base.hpp | 2 +- bsnes/gba/Makefile | 15 ++++ bsnes/gba/apu/apu.cpp | 30 +++++++ bsnes/gba/apu/apu.hpp | 9 ++ bsnes/gba/cartridge/cartridge.cpp | 35 ++++++++ bsnes/gba/cartridge/cartridge.hpp | 16 ++++ bsnes/gba/cpu/core/core.cpp | 3 + bsnes/gba/cpu/core/core.hpp | 8 ++ bsnes/gba/cpu/core/registers.hpp | 62 +++++++++++++ bsnes/gba/cpu/cpu.cpp | 32 +++++++ bsnes/gba/cpu/cpu.hpp | 11 +++ bsnes/gba/gba.hpp | 61 +++++++++++++ bsnes/gba/interface/interface.cpp | 21 +++++ bsnes/gba/interface/interface.hpp | 9 ++ bsnes/gba/memory/memory.cpp | 36 ++++++++ bsnes/gba/memory/memory.hpp | 6 ++ bsnes/gba/ppu/ppu.cpp | 38 ++++++++ bsnes/gba/ppu/ppu.hpp | 9 ++ bsnes/gba/scheduler/scheduler-inline.hpp | 17 ++++ bsnes/gba/scheduler/scheduler.cpp | 31 +++++++ bsnes/gba/scheduler/scheduler.hpp | 19 ++++ bsnes/gba/system/system.cpp | 53 +++++++++++ bsnes/gba/system/system.hpp | 23 +++++ bsnes/gba/video/video.cpp | 53 +++++++++++ bsnes/gba/video/video.hpp | 11 +++ bsnes/target-ui/Makefile | 3 +- bsnes/target-ui/base.hpp | 3 +- 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 | 11 ++- bsnes/target-ui/general/main-window.cpp | 8 ++ bsnes/target-ui/general/main-window.hpp | 1 + bsnes/target-ui/input/gba.cpp | 62 +++++++++++++ bsnes/target-ui/input/gba.hpp | 20 +++++ bsnes/target-ui/input/input.cpp | 2 + bsnes/target-ui/input/input.hpp | 2 + bsnes/target-ui/interface/gba/gba.cpp | 110 +++++++++++++++++++++++ bsnes/target-ui/interface/gba/gba.hpp | 20 +++++ bsnes/target-ui/interface/interface.cpp | 8 ++ bsnes/target-ui/interface/interface.hpp | 4 +- bsnes/target-ui/main.cpp | 1 + bsnes/target-ui/settings/audio.cpp | 13 ++- bsnes/target-ui/settings/audio.hpp | 1 + bsnes/target-ui/utility/utility.cpp | 6 ++ bsnes/target-ui/window/window.cpp | 6 ++ bsnes/target-ui/window/window.hpp | 1 + 48 files changed, 940 insertions(+), 51 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.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-inline.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 5ead105a..b6d89634 100755 --- a/bsnes/Makefile +++ b/bsnes/Makefile @@ -3,6 +3,8 @@ include nall/Makefile nes := nes snes := snes gameboy := gameboy +gba := gba + profile := accuracy target := ui @@ -11,28 +13,34 @@ target := ui # compiler c := $(compiler) -std=gnu99 cpp := $(subst cc,++,$(compiler)) -std=gnu++0x -flags := -I. -march=native -O3 -fomit-frame-pointer +flags := -I. -O3 -fomit-frame-pointer link := objects := libco # profile-guided optimization mode # pgo := instrument # pgo := optimize +# pgo := analyze ifeq ($(pgo),instrument) flags += -fprofile-generate link += -lgcov else ifeq ($(pgo),optimize) flags += -fprofile-use +else ifeq ($(pgo),analyze) + flags := $(subst -fomit-frame-pointer,,$(flags)) + flags += -pg + link += -pg -lgcov endif # platform ifeq ($(platform),x) - link += -s -ldl -lX11 -lXext + flags += -march=native + link += -ldl -lX11 -lXext else ifeq ($(platform),osx) else ifeq ($(platform),win) link += $(if $(findstring console,$(options)),-mconsole,-mwindows) - link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 + link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc else unknown_platform: help; @@ -91,6 +99,6 @@ sync: rm -r phoenix/test archive-all: - tar -cjf bsnes.tar.bz2 base data gameboy 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 gameboy 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 becfb158..e162ae84 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.02"; +const char Version[] = "087.03"; #include #include diff --git a/bsnes/gba/Makefile b/bsnes/gba/Makefile new file mode 100755 index 00000000..c138967e --- /dev/null +++ b/bsnes/gba/Makefile @@ -0,0 +1,15 @@ +gba_objects := gba-interface gba-system gba-scheduler +gba_objects += gba-cartridge gba-memory +gba_objects += gba-cpu gba-ppu gba-apu +gba_objects += gba-video +objects += $(gba_objects) + +obj/gba-interface.o: $(gba)/interface/interface.cpp $(call rwildcard,$(gba)/interface) +obj/gba-system.o: $(gba)/system/system.cpp $(call rwildcard,$(gba)/system) +obj/gba-scheduler.o: $(gba)/scheduler/scheduler.cpp $(call rwildcard,$(gba)/scheduler) +obj/gba-cartridge.o: $(gba)/cartridge/cartridge.cpp $(call rwildcard,$(gba)/cartridge) +obj/gba-memory.o: $(gba)/memory/memory.cpp $(call rwildcard,$(gba)/memory) +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) +obj/gba-video.o: $(gba)/video/video.cpp $(call rwildcard,$(gba)/video) diff --git a/bsnes/gba/apu/apu.cpp b/bsnes/gba/apu/apu.cpp new file mode 100755 index 00000000..f59df4af --- /dev/null +++ b/bsnes/gba/apu/apu.cpp @@ -0,0 +1,30 @@ +#include + +#define APU_CPP +namespace GBA { + +APU apu; + +void APU::Enter() { apu.enter(); } + +void APU::enter() { + while(true) { + //scheduler.synchronize_all(); + + interface->audioSample(0, 0); + step(1); + } +} + +void APU::step(unsigned clocks) { + clock += clocks << 9; //16777216hz / 32768hz = 512hz (1 << 9) + if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) { + 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..2979f37f --- /dev/null +++ b/bsnes/gba/cartridge/cartridge.cpp @@ -0,0 +1,35 @@ +#include + +#define CARTRIDGE_CPP +namespace GBA { + +Cartridge cartridge; + +void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) { + romdata = new uint8_t[romsize = size]; + memcpy(romdata, data, size); + sha256 = nall::sha256(data, size); + + loaded = true; +} + +void Cartridge::unload() { + if(loaded == false) return; + + delete[] romdata; + romdata = nullptr; + romsize = 0u; + + loaded = false; +} + +void Cartridge::power() { +} + +Cartridge::Cartridge() { + loaded = false; + romdata = nullptr; + romsize = 0u; +} + +} diff --git a/bsnes/gba/cartridge/cartridge.hpp b/bsnes/gba/cartridge/cartridge.hpp new file mode 100755 index 00000000..68b00c7d --- /dev/null +++ b/bsnes/gba/cartridge/cartridge.hpp @@ -0,0 +1,16 @@ +struct Cartridge : property { + readonly loaded; + readonly sha256; + + uint8_t *romdata; + unsigned romsize; + + void load(const string &markup, const uint8_t *data, unsigned size); + void unload(); + + void power(); + + 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..b0af1294 --- /dev/null +++ b/bsnes/gba/cpu/core/core.cpp @@ -0,0 +1,3 @@ +void ARM7TDMI::power() { + for(auto &gpr : r) gpr = 0; +} diff --git a/bsnes/gba/cpu/core/core.hpp b/bsnes/gba/cpu/core/core.hpp new file mode 100755 index 00000000..08eccf80 --- /dev/null +++ b/bsnes/gba/cpu/core/core.hpp @@ -0,0 +1,8 @@ +struct ARM7TDMI { + #include "registers.hpp" + GPR r[31]; + PSR cpsr; + PSR spsr[5]; + + void power(); +}; diff --git a/bsnes/gba/cpu/core/registers.hpp b/bsnes/gba/cpu/core/registers.hpp new file mode 100755 index 00000000..de6cc604 --- /dev/null +++ b/bsnes/gba/cpu/core/registers.hpp @@ -0,0 +1,62 @@ +struct GPR { + uint32 data; + + inline operator uint32() const { return data; } + inline GPR& operator=(uint32 n) { data = n; return *this; } +}; + +struct PSR { + struct Mode { + enum : unsigned { + User = 0x10, + FIQ = 0x11, + IRQ = 0x12, + SWI = 0x13, + Abort = 0x17, + Undefined = 0x1b, + System = 0x1f, + }; + }; + + bool n, z, c, v; + bool i, f, t; + unsigned mode; + + inline operator uint32() const { + return (n << 31) | (z << 30) | (c << 29) | (v << 28) + | (i << 7) | (f << 6) | (t << 5) | (mode << 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); + mode = d & 31; + return *this; + } +}; + +struct GPRs { + struct ID { + enum : unsigned { + R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, SP, LR, PC, + R8_FIQ, R9_FIQ, R10_FIQ, R11_FIQ, R12_FIQ, R13_FIQ, R14_FIQ, + R13_SWI, R14_SWI, + R13_Abort, R14_Abort, + R13_IRQ, R14_IRQ, + R13_Undefined, R14_Undefined, + }; + }; +}; + +struct SPSRs { + struct ID { + enum : unsigned { + FIQ, SWI, Abort, IRQ, Undefined, + }; + }; +}; diff --git a/bsnes/gba/cpu/cpu.cpp b/bsnes/gba/cpu/cpu.cpp new file mode 100755 index 00000000..150eff00 --- /dev/null +++ b/bsnes/gba/cpu/cpu.cpp @@ -0,0 +1,32 @@ +#include + +#define CPU_CPP +namespace GBA { + +#include "core/core.cpp" +CPU cpu; + +void CPU::Enter() { cpu.enter(); } + +void CPU::enter() { + while(true) { + //scheduler.synchronize_cpu(); + + step(4); + } +} + +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); + ARM7TDMI::power(); +} + +} diff --git a/bsnes/gba/cpu/cpu.hpp b/bsnes/gba/cpu/cpu.hpp new file mode 100755 index 00000000..5bb7e419 --- /dev/null +++ b/bsnes/gba/cpu/cpu.hpp @@ -0,0 +1,11 @@ +#include "core/core.hpp" + +struct CPU : Processor, ARM7TDMI { + static void Enter(); + void enter(); + void step(unsigned clocks); + + void power(); +}; + +extern CPU cpu; diff --git a/bsnes/gba/gba.hpp b/bsnes/gba/gba.hpp new file mode 100755 index 00000000..6f6953bb --- /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-17 +*/ + +#include + +namespace GBA { + 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 + + #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..4061f8fe --- /dev/null +++ b/bsnes/gba/memory/memory.cpp @@ -0,0 +1,36 @@ +#include + +#define MEMORY_CPP +namespace GBA { + +Bus bus; + +uint8 Bus::read(unsigned addr) { + if((addr & 0x0fffc000) == 0x00000000) { + //00000000-00003fff BIOS + return system.bios.data[addr & 0x3fff]; + } + + if((addr & 0x0e000000) == 0x08000000) { + //08000000-09ffffff ROM (wait state 0) + return cartridge.romdata[addr & 0x01ffffff]; + } + + if((addr & 0x0e000000) == 0x0a000000) { + //0a000000-0bffffff ROM (wait state 1) + return cartridge.romdata[addr & 0x01ffffff]; + } + + if((addr & 0x0e000000) == 0x0c000000) { + //0c000000-0dffffff ROM (wait state 2) + return cartridge.romdata[addr & 0x01ffffff]; + } + + //unmapped + return 0u; +} + +void Bus::write(unsigned addr, uint8 data) { +} + +} diff --git a/bsnes/gba/memory/memory.hpp b/bsnes/gba/memory/memory.hpp new file mode 100755 index 00000000..ef1540a0 --- /dev/null +++ b/bsnes/gba/memory/memory.hpp @@ -0,0 +1,6 @@ +struct Bus { + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern Bus bus; diff --git a/bsnes/gba/ppu/ppu.cpp b/bsnes/gba/ppu/ppu.cpp new file mode 100755 index 00000000..45c5d87e --- /dev/null +++ b/bsnes/gba/ppu/ppu.cpp @@ -0,0 +1,38 @@ +#include + +#define PPU_CPP +namespace GBA { + +PPU ppu; + +void PPU::Enter() { ppu.enter(); } + +void PPU::enter() { + while(true) { + //scheduler.synchronize_all(); + + step(16777216); + static uint16_t buffer[240 * 160]; + for(unsigned y = 0; y < 160; y++) { + uint16_t *dp = buffer + y * 240; + for(unsigned x = 0; x < 240; x++) { + *dp++ = x + y; + } + } + interface->videoRefresh(buffer); + scheduler.exit(Scheduler::ExitReason::FrameEvent); + } +} + +void PPU::step(unsigned clocks) { + clock += clocks; + if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) { + co_switch(cpu.thread); + } +} + +void PPU::power() { + create(PPU::Enter, 16777216); +} + +} diff --git a/bsnes/gba/ppu/ppu.hpp b/bsnes/gba/ppu/ppu.hpp new file mode 100755 index 00000000..15ba4c89 --- /dev/null +++ b/bsnes/gba/ppu/ppu.hpp @@ -0,0 +1,9 @@ +struct PPU : Processor { + static void Enter(); + void enter(); + void step(unsigned clocks); + + void power(); +}; + +extern PPU ppu; diff --git a/bsnes/gba/scheduler/scheduler-inline.hpp b/bsnes/gba/scheduler/scheduler-inline.hpp new file mode 100755 index 00000000..ae230466 --- /dev/null +++ b/bsnes/gba/scheduler/scheduler-inline.hpp @@ -0,0 +1,17 @@ +void Scheduler::swapto(Processor &p) { + active_thread = p.thread; + co_switch(active_thread); +} + +void Scheduler::synchronize_cpu() { + if(sync == SynchronizeMode::CPU) { + sync = SynchronizeMode::All; + exit(ExitReason::SynchronizeEvent); + } +} + +void Scheduler::synchronize_all() { + if(sync == SynchronizeMode::All) { + exit(ExitReason::SynchronizeEvent); + } +} diff --git a/bsnes/gba/scheduler/scheduler.cpp b/bsnes/gba/scheduler/scheduler.cpp new file mode 100755 index 00000000..42335f24 --- /dev/null +++ b/bsnes/gba/scheduler/scheduler.cpp @@ -0,0 +1,31 @@ +#include + +#define SCHEDULER_CPP +namespace GBA { + +Scheduler scheduler; + +void Scheduler::enter() { + host_thread = co_active(); + co_switch(active_thread); +} + +void Scheduler::exit(ExitReason reason) { + exit_reason = reason; + active_thread = co_active(); + co_switch(host_thread); +} + +void Scheduler::power() { + host_thread = co_active(); + active_thread = cpu.thread; +} + +Scheduler::Scheduler() { + sync = SynchronizeMode::None; + exit_reason = ExitReason::UnknownEvent; + host_thread = nullptr; + active_thread = nullptr; +} + +} diff --git a/bsnes/gba/scheduler/scheduler.hpp b/bsnes/gba/scheduler/scheduler.hpp new file mode 100755 index 00000000..9b7aca46 --- /dev/null +++ b/bsnes/gba/scheduler/scheduler.hpp @@ -0,0 +1,19 @@ +struct Scheduler : property { + enum class SynchronizeMode : unsigned { None, CPU, All } sync; + enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent }; + readonly exit_reason; + + cothread_t host_thread; + cothread_t active_thread; + + void enter(); + void exit(ExitReason); + alwaysinline void swapto(Processor&); + alwaysinline void synchronize_cpu(); + alwaysinline void synchronize_all(); + + 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..62a3f6f7 --- /dev/null +++ b/bsnes/gba/system/system.cpp @@ -0,0 +1,53 @@ +#include + +#define SYSTEM_CPP +namespace GBA { + +System system; + +void System::BIOS::load(const uint8_t *newdata, unsigned newsize) { + if(data) delete[] data; + data = new uint8[size = newsize]; + memcpy(data, newdata, newsize); + + string sha256 = nall::sha256(data, size); + if(size != 16384 || sha256 != "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570") { + interface->message("Warning: Game Boy Advance BIOS SHA256 sum is incorrect."); + } +} + +System::BIOS::BIOS() { + data = nullptr; + size = 0u; +} + +System::BIOS::~BIOS() { + delete[] data; +} + +void System::run() { + scheduler.enter(); +} + +void System::init() { +} + +void System::term() { +} + +void System::load() { +} + +void System::unload() { + cartridge.unload(); +} + +void System::power() { + cartridge.power(); + cpu.power(); + ppu.power(); + apu.power(); + scheduler.power(); +} + +} diff --git a/bsnes/gba/system/system.hpp b/bsnes/gba/system/system.hpp new file mode 100755 index 00000000..35ac26a8 --- /dev/null +++ b/bsnes/gba/system/system.hpp @@ -0,0 +1,23 @@ +enum class Input : unsigned { + A, B, Select, Start, Right, Left, Up, Down, R, L, +}; + +struct System : property { + struct BIOS { + uint8_t *data; + unsigned size; + void load(const uint8_t *data, unsigned size); + BIOS(); + ~BIOS(); + } bios; + + void run(); + + void init(); + void term(); + void load(); + void unload(); + void power(); +}; + +extern System system; diff --git a/bsnes/gba/video/video.cpp b/bsnes/gba/video/video.cpp new file mode 100755 index 00000000..7171ca1e --- /dev/null +++ b/bsnes/gba/video/video.cpp @@ -0,0 +1,53 @@ +#include + +#define VIDEO_CPP +namespace GBA { + +Video video; + +unsigned Video::color(unsigned color) const { + uint5_t b = color >> 10; + uint5_t g = color >> 5; + uint5_t r = color >> 0; + + uint10_t R = (r << 5) | (r << 0); + uint10_t G = (g << 5) | (g << 0); + uint10_t 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 unsigned[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/target-ui/Makefile b/bsnes/target-ui/Makefile index 8b9a1a69..8424ff54 100755 --- a/bsnes/target-ui/Makefile +++ b/bsnes/target-ui/Makefile @@ -1,6 +1,7 @@ include $(nes)/Makefile -include $(gameboy)/Makefile include $(snes)/Makefile +include $(gameboy)/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 02d44be3..7c224bda 100755 --- a/bsnes/target-ui/base.hpp +++ b/bsnes/target-ui/base.hpp @@ -1,6 +1,7 @@ #include -#include #include +#include +#include #include #include diff --git a/bsnes/target-ui/config/config.cpp b/bsnes/target-ui/config/config.cpp index 0b2c7b85..3c9a6d5e 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.frequencyGameBoy = 4194304, "Audio::Frequency::GameBoy"); + append(audio.frequency = 48000, "Audio::Frequency::Native"); + append(audio.frequencyNES = 1789772, "Audio::Frequency::NES"); + append(audio.frequencySNES = 32000, "Audio::Frequency::SNES"); + append(audio.frequencyGameBoy = 4194304, "Audio::Frequency::GameBoy"); + 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 b0016391..4822e10d 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 frequencyGameBoy; + 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..628ae401 100755 --- a/bsnes/target-ui/general/file-browser.hpp +++ b/bsnes/target-ui/general/file-browser.hpp @@ -9,7 +9,16 @@ 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 a109cbdc..f0094d40 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 ..."); @@ -103,6 +104,7 @@ MainWindow::MainWindow() { cartridgeMenu.append(cartridgeLoadSNES); cartridgeMenu.append(cartridgeLoadGameBoy); cartridgeMenu.append(cartridgeLoadGameBoyColor); + cartridgeMenu.append(cartridgeLoadGameBoyAdvance); cartridgeMenu.append(cartridgeSeparator); cartridgeMenu.append(cartridgeLoadSatellaviewSlotted); cartridgeMenu.append(cartridgeLoadSatellaview); @@ -215,6 +217,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(); }; diff --git a/bsnes/target-ui/general/main-window.hpp b/bsnes/target-ui/general/main-window.hpp index a29ff481..7a404f3c 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; diff --git a/bsnes/target-ui/input/gba.cpp b/bsnes/target-ui/input/gba.cpp new file mode 100755 index 00000000..897b212c --- /dev/null +++ b/bsnes/target-ui/input/gba.cpp @@ -0,0 +1,62 @@ +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..2ddb076e --- /dev/null +++ b/bsnes/target-ui/input/gba.hpp @@ -0,0 +1,20 @@ +struct GbaController : TertiaryInput { + DigitalInput up, down, left, right; + DigitalInput b, a, l, r, 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 495119b6..6fb5d74d 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 "gameboy.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(gameBoy); + 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 c4b57b73..b908366a 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 "gameboy.hpp" +#include "gba.hpp" #include "user-interface.hpp" struct InputManager { @@ -63,6 +64,7 @@ struct InputManager { NesInput nes; SnesInput snes; GameBoyInput gameBoy; + GbaInput gba; UserInterfaceInput userInterface; void scan(); diff --git a/bsnes/target-ui/interface/gba/gba.cpp b/bsnes/target-ui/interface/gba/gba.cpp new file mode 100755 index 00000000..9779edee --- /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"})) { + message("Error: Game Boy Advance BIOS (bios.bin) 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"})) { + message("Error: Game Boy Advance BIOS (gbabios.bin) 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() { + GBA::system.unload(); +} + +void InterfaceGBA::power() { + GBA::system.power(); +} + +void InterfaceGBA::reset() { + GBA::system.power(); //GBA lacks reset button +} + +void InterfaceGBA::run() { + 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 ff1a8407..106c810c 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 "gameboy/gameboy.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::GameBoy: return dspaudio.setFrequency(config->audio.frequencyGameBoy); + 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::GameBoy: return gameBoy.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::GameBoy: core = &gameBoy; break; + case Mode::GBA: core = &gba; break; default: core = nullptr; break; } @@ -124,6 +128,7 @@ void Interface::unloadCartridge() { case Mode::NES: nes.unloadCartridge(); break; case Mode::SNES: snes.unloadCartridge(); break; case Mode::GameBoy: gameBoy.unloadCartridge(); break; + case Mode::GBA: gba.unloadCartridge(); break; } cartridgeTitle = ""; @@ -184,6 +189,7 @@ void Interface::setCheatCodes(const lstring &list) { case Mode::NES: return nes.setCheats(list); case Mode::SNES: return snes.setCheats(list); case Mode::GameBoy: return gameBoy.setCheats(list); + case Mode::GBA: return gba.setCheats(list); } } @@ -192,6 +198,7 @@ string Interface::sha256() { case Mode::NES: return NES::cartridge.sha256(); case Mode::SNES: return SNES::cartridge.sha256(); case Mode::GameBoy: return GameBoy::cartridge.sha256(); + case Mode::GBA: return GBA::cartridge.sha256(); } return "{None}"; } @@ -201,6 +208,7 @@ Interface::Interface() : core(nullptr) { nes.initialize(); snes.initialize(); gameBoy.initialize(); + gba.initialize(); } //internal diff --git a/bsnes/target-ui/interface/interface.hpp b/bsnes/target-ui/interface/interface.hpp index 347b2b13..bd7380f5 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 "gameboy/gameboy.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, GameBoy }; + enum class Mode : unsigned { None, SNES, NES, GameBoy, GBA }; readonly mode; void bindControllers(); @@ -74,6 +75,7 @@ struct Interface : property { InterfaceNES nes; InterfaceSNES snes; InterfaceGameBoy gameBoy; + 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 38daab46..28927267 100755 --- a/bsnes/target-ui/settings/audio.cpp +++ b/bsnes/target-ui/settings/audio.cpp @@ -68,6 +68,11 @@ AudioSettings::AudioSettings() { gameBoy.base = 4194304; gameBoy.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(gameBoy, { ~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); gameBoy.setPosition(config->audio.frequencyGameBoy); + gba.setPosition(config->audio.frequencyGBA); - frequencySelection.onChange = latencySelection.onChange = resamplerSelection.onChange = - volume.slider.onChange = nes.slider.onChange = snes.slider.onChange = gameBoy.slider.onChange = + frequencySelection.onChange = latencySelection.onChange = resamplerSelection.onChange = volume.slider.onChange = + nes.slider.onChange = snes.slider.onChange = gameBoy.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.frequencyGameBoy = gameBoy.position(); + config->audio.frequencyGBA = gba.position(); nes.value.setText({ nes.position(), "hz" }); snes.value.setText({ snes.position(), "hz" }); gameBoy.value.setText({ gameBoy.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 a647d122..92643af2 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 gameBoy; + AudioSlider gba; void synchronize(); AudioSettings(); diff --git a/bsnes/target-ui/utility/utility.cpp b/bsnes/target-ui/utility/utility.cpp index 2670a3a6..c349e4e0 100755 --- a/bsnes/target-ui/utility/utility.cpp +++ b/bsnes/target-ui/utility/utility.cpp @@ -37,6 +37,11 @@ void Utility::setMode(Interface::Mode mode) { dspaudio.setChannels(2); } + else if(mode == Interface::Mode::GBA) { + mainWindow->setTitle(interface->cartridgeTitle); + dspaudio.setChannels(2); + } + interface->updateDSP(); mainWindow->synchronize(); resizeMainWindow(); @@ -51,6 +56,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::GameBoy: 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..fdea7002 100755 --- a/bsnes/target-ui/window/window.cpp +++ b/bsnes/target-ui/window/window.cpp @@ -44,3 +44,9 @@ void WindowManager::saveGeometry() { } config.save(application->path("geometry.cfg")); } + +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;