mirror of https://github.com/bsnes-emu/bsnes.git
Update to v087r03 release.
byuu says: Fixing the PPU stepping increased FPS to 250. Promising, at least, since the ARM core is still severely overclocked. However, I reverted back to r02. This one patches gameboy/ and GameBoy:: to gb/ and GB:: and that's it. Sorry, I just couldn't shake this bad feeling about the code. There were some poorly hacked-together constructs. I'd rather just redo two days of work than feel bad about the codebase for the next several years. Going to attempt the GBA bridge again. Third time's a charm, I suppose (there was a pre-r03 WIP I abandoned as well.) This isn't unprecedented, GB core took a few attempts like this as well.
This commit is contained in:
parent
0f54be93b7
commit
95c62f92ac
|
@ -1,46 +1,39 @@
|
||||||
include nall/Makefile
|
include nall/Makefile
|
||||||
|
|
||||||
nes := nes
|
nes := nes
|
||||||
snes := snes
|
snes := snes
|
||||||
gameboy := gameboy
|
gb := gb
|
||||||
gba := gba
|
|
||||||
|
|
||||||
profile := accuracy
|
profile := accuracy
|
||||||
target := ui
|
target := ui
|
||||||
|
|
||||||
# options += console
|
# options += console
|
||||||
|
|
||||||
# compiler
|
# compiler
|
||||||
c := $(compiler) -std=gnu99
|
c := $(compiler) -std=gnu99
|
||||||
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
cpp := $(subst cc,++,$(compiler)) -std=gnu++0x
|
||||||
flags := -I. -O3 -fomit-frame-pointer
|
flags := -I. -march=native -O3 -fomit-frame-pointer
|
||||||
link :=
|
link :=
|
||||||
objects := libco
|
objects := libco
|
||||||
|
|
||||||
# profile-guided optimization mode
|
# profile-guided optimization mode
|
||||||
# pgo := instrument
|
# pgo := instrument
|
||||||
# pgo := optimize
|
# pgo := optimize
|
||||||
# pgo := analyze
|
|
||||||
|
|
||||||
ifeq ($(pgo),instrument)
|
ifeq ($(pgo),instrument)
|
||||||
flags += -fprofile-generate
|
flags += -fprofile-generate
|
||||||
link += -lgcov
|
link += -lgcov
|
||||||
else ifeq ($(pgo),optimize)
|
else ifeq ($(pgo),optimize)
|
||||||
flags += -fprofile-use
|
flags += -fprofile-use
|
||||||
else ifeq ($(pgo),analyze)
|
|
||||||
flags := $(subst -fomit-frame-pointer,,$(flags))
|
|
||||||
flags += -pg
|
|
||||||
link += -pg -lgcov
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# platform
|
# platform
|
||||||
ifeq ($(platform),x)
|
ifeq ($(platform),x)
|
||||||
flags += -march=native
|
link += -s -ldl -lX11 -lXext
|
||||||
link += -ldl -lX11 -lXext
|
|
||||||
else ifeq ($(platform),osx)
|
else ifeq ($(platform),osx)
|
||||||
else ifeq ($(platform),win)
|
else ifeq ($(platform),win)
|
||||||
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
|
link += $(if $(findstring console,$(options)),-mconsole,-mwindows)
|
||||||
link += -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
|
link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32
|
||||||
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||||
else
|
else
|
||||||
unknown_platform: help;
|
unknown_platform: help;
|
||||||
|
@ -99,6 +92,6 @@ sync:
|
||||||
rm -r phoenix/test
|
rm -r phoenix/test
|
||||||
|
|
||||||
archive-all:
|
archive-all:
|
||||||
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
|
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
|
||||||
|
|
||||||
help:;
|
help:;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef GAMEBOY_HPP
|
#ifndef GB_HPP
|
||||||
#define GAMEBOY_HPP
|
#define GB_HPP
|
||||||
|
|
||||||
#include <base/base.hpp>
|
#include <base/base.hpp>
|
||||||
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
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)
|
|
|
@ -1,30 +0,0 @@
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
struct APU : Processor {
|
|
||||||
static void Enter();
|
|
||||||
void enter();
|
|
||||||
void step(unsigned clocks);
|
|
||||||
|
|
||||||
void power();
|
|
||||||
};
|
|
||||||
|
|
||||||
extern APU apu;
|
|
|
@ -1,35 +0,0 @@
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
struct Cartridge : property<Cartridge> {
|
|
||||||
readonly<bool> loaded;
|
|
||||||
readonly<string> 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;
|
|
|
@ -1,3 +0,0 @@
|
||||||
void ARM7TDMI::power() {
|
|
||||||
for(auto &gpr : r) gpr = 0;
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
struct ARM7TDMI {
|
|
||||||
#include "registers.hpp"
|
|
||||||
GPR r[31];
|
|
||||||
PSR cpsr;
|
|
||||||
PSR spsr[5];
|
|
||||||
|
|
||||||
void power();
|
|
||||||
};
|
|
|
@ -1,62 +0,0 @@
|
||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,32 +0,0 @@
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
#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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
#include "core/core.hpp"
|
|
||||||
|
|
||||||
struct CPU : Processor, ARM7TDMI {
|
|
||||||
static void Enter();
|
|
||||||
void enter();
|
|
||||||
void step(unsigned clocks);
|
|
||||||
|
|
||||||
void power();
|
|
||||||
};
|
|
||||||
|
|
||||||
extern CPU cpu;
|
|
|
@ -1,61 +0,0 @@
|
||||||
#ifndef GBA_HPP
|
|
||||||
#define GBA_HPP
|
|
||||||
|
|
||||||
#include <base/base.hpp>
|
|
||||||
|
|
||||||
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 <libco/libco.h>
|
|
||||||
|
|
||||||
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 <gba/interface/interface.hpp>
|
|
||||||
#include <gba/system/system.hpp>
|
|
||||||
#include <gba/scheduler/scheduler.hpp>
|
|
||||||
#include <gba/cartridge/cartridge.hpp>
|
|
||||||
#include <gba/memory/memory.hpp>
|
|
||||||
#include <gba/cpu/cpu.hpp>
|
|
||||||
#include <gba/ppu/ppu.hpp>
|
|
||||||
#include <gba/apu/apu.hpp>
|
|
||||||
#include <gba/video/video.hpp>
|
|
||||||
|
|
||||||
#include <gba/scheduler/scheduler-inline.hpp>
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,21 +0,0 @@
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
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;
|
|
|
@ -1,36 +0,0 @@
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
#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) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
struct Bus {
|
|
||||||
uint8 read(unsigned addr);
|
|
||||||
void write(unsigned addr, uint8 data);
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Bus bus;
|
|
|
@ -1,38 +0,0 @@
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
struct PPU : Processor {
|
|
||||||
static void Enter();
|
|
||||||
void enter();
|
|
||||||
void step(unsigned clocks);
|
|
||||||
|
|
||||||
void power();
|
|
||||||
};
|
|
||||||
|
|
||||||
extern PPU ppu;
|
|
|
@ -1,17 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
struct Scheduler : property<Scheduler> {
|
|
||||||
enum class SynchronizeMode : unsigned { None, CPU, All } sync;
|
|
||||||
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent };
|
|
||||||
readonly<ExitReason> 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;
|
|
|
@ -1,53 +0,0 @@
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
#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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
enum class Input : unsigned {
|
|
||||||
A, B, Select, Start, Right, Left, Up, Down, R, L,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct System : property<System> {
|
|
||||||
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;
|
|
|
@ -1,53 +0,0 @@
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
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;
|
|
|
@ -59,7 +59,7 @@ void Cartridge::load(Mode cartridge_mode, const char *markup) {
|
||||||
break;
|
break;
|
||||||
case Mode::SuperGameBoy:
|
case Mode::SuperGameBoy:
|
||||||
#if defined(GAMEBOY)
|
#if defined(GAMEBOY)
|
||||||
sha256 = GameBoy::cartridge.sha256();
|
sha256 = GB::cartridge.sha256();
|
||||||
#else
|
#else
|
||||||
throw "Game Boy support not present";
|
throw "Game Boy support not present";
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,14 +15,14 @@ void ICD2::Enter() { icd2.enter(); }
|
||||||
void ICD2::enter() {
|
void ICD2::enter() {
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||||
GameBoy::system.runtosave();
|
GB::system.runtosave();
|
||||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r6003 & 0x80) {
|
if(r6003 & 0x80) {
|
||||||
GameBoy::system.run();
|
GB::system.run();
|
||||||
step(GameBoy::system.clocks_executed);
|
step(GB::system.clocks_executed);
|
||||||
GameBoy::system.clocks_executed = 0;
|
GB::system.clocks_executed = 0;
|
||||||
} else { //DMG halted
|
} else { //DMG halted
|
||||||
audio.coprocessor_sample(0x0000, 0x0000);
|
audio.coprocessor_sample(0x0000, 0x0000);
|
||||||
step(1);
|
step(1);
|
||||||
|
@ -69,9 +69,9 @@ void ICD2::reset() {
|
||||||
joyp14lock = 0;
|
joyp14lock = 0;
|
||||||
pulselock = true;
|
pulselock = true;
|
||||||
|
|
||||||
GameBoy::interface = this;
|
GB::interface = this;
|
||||||
GameBoy::system.init();
|
GB::system.init();
|
||||||
GameBoy::system.power();
|
GB::system.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class ICD2 : public GameBoy::Interface, public Coprocessor {
|
class ICD2 : public GB::Interface, public Coprocessor {
|
||||||
public:
|
public:
|
||||||
unsigned revision;
|
unsigned revision;
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
//called on rendered lines 0-143 (not on Vblank lines 144-153)
|
//called on rendered lines 0-143 (not on Vblank lines 144-153)
|
||||||
void ICD2::lcdScanline() {
|
void ICD2::lcdScanline() {
|
||||||
if((GameBoy::lcd.status.ly & 7) == 0) {
|
if((GB::lcd.status.ly & 7) == 0) {
|
||||||
lcd.row = (lcd.row + 1) & 3;
|
lcd.row = (lcd.row + 1) & 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned offset = (lcd.row * 160 * 8) + ((GameBoy::lcd.status.ly & 7) * 160);
|
unsigned offset = (lcd.row * 160 * 8) + ((GB::lcd.status.ly & 7) * 160);
|
||||||
memcpy(lcd.buffer + offset, GameBoy::lcd.screen + GameBoy::lcd.status.ly * 160, 160 * sizeof(uint16));
|
memcpy(lcd.buffer + offset, GB::lcd.screen + GB::lcd.status.ly * 160, 160 * sizeof(uint16));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICD2::joypWrite(bool p15, bool p14) {
|
void ICD2::joypWrite(bool p15, bool p14) {
|
||||||
|
@ -88,7 +88,7 @@ void ICD2::audioSample(int16_t center, int16_t left, int16_t right) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ICD2::inputPoll(unsigned id) {
|
bool ICD2::inputPoll(unsigned id) {
|
||||||
GameBoy::cpu.status.mlt_req = joyp_id & mlt_req;
|
GB::cpu.status.mlt_req = joyp_id & mlt_req;
|
||||||
|
|
||||||
unsigned data = 0x00;
|
unsigned data = 0x00;
|
||||||
switch(joyp_id & mlt_req) {
|
switch(joyp_id & mlt_req) {
|
||||||
|
@ -98,15 +98,15 @@ bool ICD2::inputPoll(unsigned id) {
|
||||||
case 3: data = ~r6007; break;
|
case 3: data = ~r6007; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch((GameBoy::Input)id) {
|
switch((GB::Input)id) {
|
||||||
case GameBoy::Input::Start: return data & 0x80;
|
case GB::Input::Start: return data & 0x80;
|
||||||
case GameBoy::Input::Select: return data & 0x40;
|
case GB::Input::Select: return data & 0x40;
|
||||||
case GameBoy::Input::B: return data & 0x20;
|
case GB::Input::B: return data & 0x20;
|
||||||
case GameBoy::Input::A: return data & 0x10;
|
case GB::Input::A: return data & 0x10;
|
||||||
case GameBoy::Input::Down: return data & 0x08;
|
case GB::Input::Down: return data & 0x08;
|
||||||
case GameBoy::Input::Up: return data & 0x04;
|
case GB::Input::Up: return data & 0x04;
|
||||||
case GameBoy::Input::Left: return data & 0x02;
|
case GB::Input::Left: return data & 0x02;
|
||||||
case GameBoy::Input::Right: return data & 0x01;
|
case GB::Input::Right: return data & 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -19,7 +19,7 @@ uint8 ICD2::read(unsigned addr) {
|
||||||
|
|
||||||
//LY counter
|
//LY counter
|
||||||
if(addr == 0x6000) {
|
if(addr == 0x6000) {
|
||||||
r6000_ly = GameBoy::lcd.status.ly;
|
r6000_ly = GB::lcd.status.ly;
|
||||||
r6000_row = lcd.row;
|
r6000_row = lcd.row;
|
||||||
return r6000_ly;
|
return r6000_ly;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
void ICD2::serialize(serializer &s) {
|
void ICD2::serialize(serializer &s) {
|
||||||
Processor::serialize(s);
|
Processor::serialize(s);
|
||||||
GameBoy::system.serialize_all(s);
|
GB::system.serialize_all(s);
|
||||||
|
|
||||||
for(unsigned n = 0; n < 64; n++) s.array(packet[n].data);
|
for(unsigned n = 0; n < 64; n++) s.array(packet[n].data);
|
||||||
s.integer(packetsize);
|
s.integer(packetsize);
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace SNES {
|
||||||
#include <libco/libco.h>
|
#include <libco/libco.h>
|
||||||
|
|
||||||
#if defined(GAMEBOY)
|
#if defined(GAMEBOY)
|
||||||
#include <gameboy/gameboy.hpp>
|
#include <gb/gb.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace SNES {
|
namespace SNES {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
include $(snes)/Makefile
|
include $(snes)/Makefile
|
||||||
include $(gameboy)/Makefile
|
include $(gb)/Makefile
|
||||||
output := libsnes
|
output := libsnes
|
||||||
|
|
||||||
ifeq ($(platform),x)
|
ifeq ($(platform),x)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
include $(nes)/Makefile
|
include $(nes)/Makefile
|
||||||
include $(snes)/Makefile
|
include $(snes)/Makefile
|
||||||
include $(gameboy)/Makefile
|
include $(gb)/Makefile
|
||||||
include $(gba)/Makefile
|
|
||||||
name := bsnes
|
name := bsnes
|
||||||
|
|
||||||
ui_objects := ui-main ui-config ui-interface ui-input ui-utility
|
ui_objects := ui-main ui-config ui-interface ui-input ui-utility
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include <nes/nes.hpp>
|
#include <nes/nes.hpp>
|
||||||
#include <snes/snes.hpp>
|
#include <snes/snes.hpp>
|
||||||
#include <gameboy/gameboy.hpp>
|
#include <gb/gb.hpp>
|
||||||
#include <gba/gba.hpp>
|
|
||||||
|
|
||||||
#include <nall/compositor.hpp>
|
#include <nall/compositor.hpp>
|
||||||
#include <nall/config.hpp>
|
#include <nall/config.hpp>
|
||||||
|
|
|
@ -2,50 +2,49 @@
|
||||||
Config *config = nullptr;
|
Config *config = nullptr;
|
||||||
|
|
||||||
Config::Config() {
|
Config::Config() {
|
||||||
append(video.driver = "", "Video::Driver");
|
attach(video.driver = "", "Video::Driver");
|
||||||
append(video.filter = "None", "Video::Filter");
|
attach(video.filter = "None", "Video::Filter");
|
||||||
append(video.shader = "Blur", "Video::Shader");
|
attach(video.shader = "Blur", "Video::Shader");
|
||||||
append(video.synchronize = true, "Video::Synchronize");
|
attach(video.synchronize = true, "Video::Synchronize");
|
||||||
append(video.correctAspectRatio = true, "Video::CorrectAspectRatio");
|
attach(video.correctAspectRatio = true, "Video::CorrectAspectRatio");
|
||||||
|
|
||||||
append(video.maskOverscan = false, "Video::MaskOverscan");
|
attach(video.maskOverscan = false, "Video::MaskOverscan");
|
||||||
append(video.maskOverscanHorizontal = 8, "Video::MaskOverscanHorizontal");
|
attach(video.maskOverscanHorizontal = 8, "Video::MaskOverscanHorizontal");
|
||||||
append(video.maskOverscanVertical = 8, "Video::MaskOverscanVertical");
|
attach(video.maskOverscanVertical = 8, "Video::MaskOverscanVertical");
|
||||||
|
|
||||||
append(video.brightness = 100, "Video::Brightness");
|
attach(video.brightness = 100, "Video::Brightness");
|
||||||
append(video.contrast = 100, "Video::Contrast");
|
attach(video.contrast = 100, "Video::Contrast");
|
||||||
append(video.gamma = 50, "Video::Gamma");
|
attach(video.gamma = 50, "Video::Gamma");
|
||||||
|
|
||||||
append(video.fullScreenMode = 0, "Video::FullScreenMode");
|
attach(video.fullScreenMode = 0, "Video::FullScreenMode");
|
||||||
|
|
||||||
append(video.startFullScreen = false, "Video::StartFullScreen");
|
attach(video.startFullScreen = false, "Video::StartFullScreen");
|
||||||
append(video.compositionMode = 0, "Video::CompositionMode");
|
attach(video.compositionMode = 0, "Video::CompositionMode");
|
||||||
|
|
||||||
append(audio.driver = "", "Audio::Driver");
|
attach(audio.driver = "", "Audio::Driver");
|
||||||
append(audio.synchronize = true, "Audio::Synchronize");
|
attach(audio.synchronize = true, "Audio::Synchronize");
|
||||||
append(audio.mute = false, "Audio::Mute");
|
attach(audio.mute = false, "Audio::Mute");
|
||||||
append(audio.volume = 100, "Audio::Volume");
|
attach(audio.volume = 100, "Audio::Volume");
|
||||||
append(audio.latency = 60, "Audio::Latency");
|
attach(audio.latency = 60, "Audio::Latency");
|
||||||
append(audio.resampler = "sinc", "Audio::Resampler");
|
attach(audio.resampler = "sinc", "Audio::Resampler");
|
||||||
|
|
||||||
append(audio.frequency = 48000, "Audio::Frequency::Native");
|
attach(audio.frequency = 48000, "Audio::Frequency::Native");
|
||||||
append(audio.frequencyNES = 1789772, "Audio::Frequency::NES");
|
attach(audio.frequencyNES = 1789772, "Audio::Frequency::NES");
|
||||||
append(audio.frequencySNES = 32000, "Audio::Frequency::SNES");
|
attach(audio.frequencySNES = 32000, "Audio::Frequency::SNES");
|
||||||
append(audio.frequencyGameBoy = 4194304, "Audio::Frequency::GameBoy");
|
attach(audio.frequencyGB = 4194304, "Audio::Frequency::GB");
|
||||||
append(audio.frequencyGBA = 32768, "Audio::Frequency::GBA");
|
|
||||||
|
|
||||||
append(input.driver = "", "Input::Driver");
|
attach(input.driver = "", "Input::Driver");
|
||||||
append(input.focusPolicy = 1, "Input::FocusPolicy");
|
attach(input.focusPolicy = 1, "Input::FocusPolicy");
|
||||||
|
|
||||||
append(path.bios.satellaview = "", "Path::BIOS::Satellaview");
|
attach(path.bios.satellaview = "", "Path::BIOS::Satellaview");
|
||||||
append(path.bios.sufamiTurbo = "", "Path::BIOS::SufamiTurbo");
|
attach(path.bios.sufamiTurbo = "", "Path::BIOS::SufamiTurbo");
|
||||||
append(path.bios.superGameBoy = "", "Path::BIOS::SuperGameBoy");
|
attach(path.bios.superGameBoy = "", "Path::BIOS::SuperGameBoy");
|
||||||
|
|
||||||
append(nes.controllerPort1Device = 1, "NES::Controller::Port1");
|
attach(nes.controllerPort1Device = 1, "NES::Controller::Port1");
|
||||||
append(nes.controllerPort2Device = 0, "NES::Controller::Port2");
|
attach(nes.controllerPort2Device = 0, "NES::Controller::Port2");
|
||||||
|
|
||||||
append(snes.controllerPort1Device = 1, "SNES::Controller::Port1");
|
attach(snes.controllerPort1Device = 1, "SNES::Controller::Port1");
|
||||||
append(snes.controllerPort2Device = 0, "SNES::Controller::Port2");
|
attach(snes.controllerPort2Device = 0, "SNES::Controller::Port2");
|
||||||
|
|
||||||
load(application->path("settings.cfg"));
|
load(application->path("settings.cfg"));
|
||||||
save(application->path("settings.cfg"));
|
save(application->path("settings.cfg"));
|
||||||
|
|
|
@ -33,8 +33,7 @@ struct Config : public configuration {
|
||||||
unsigned frequency;
|
unsigned frequency;
|
||||||
unsigned frequencyNES;
|
unsigned frequencyNES;
|
||||||
unsigned frequencySNES;
|
unsigned frequencySNES;
|
||||||
unsigned frequencyGameBoy;
|
unsigned frequencyGB;
|
||||||
unsigned frequencyGBA;
|
|
||||||
} audio;
|
} audio;
|
||||||
|
|
||||||
struct Input {
|
struct Input {
|
||||||
|
|
|
@ -42,14 +42,13 @@ FileBrowser::FileBrowser() {
|
||||||
fileList.onChange = { &FileBrowser::synchronize, this };
|
fileList.onChange = { &FileBrowser::synchronize, this };
|
||||||
fileList.onActivate = openButton.onActivate = { &FileBrowser::fileListActivate, this };
|
fileList.onActivate = openButton.onActivate = { &FileBrowser::fileListActivate, this };
|
||||||
|
|
||||||
filterModes.append({ "Default", "", { "*" } });
|
filterModes.append({ "Default", "", { "*" } });
|
||||||
filterModes.append({ "NES", "", { "*.fc", "*.nes" } });
|
filterModes.append({ "NES", "", { "*.fc", "*.nes" } });
|
||||||
filterModes.append({ "SNES", "", { "*.sfc" } });
|
filterModes.append({ "SNES", "", { "*.sfc" } });
|
||||||
filterModes.append({ "GameBoy", "", { "*.gb", "*.gbc" } });
|
filterModes.append({ "GameBoy", "", { "*.gb", "*.gbc" } });
|
||||||
filterModes.append({ "GameBoyColor", "", { "*.gbc" } });
|
filterModes.append({ "GameBoyColor", "", { "*.gbc" } });
|
||||||
filterModes.append({ "GameBoyAdvance", "", { "*.gba" } });
|
filterModes.append({ "Satellaview", "", { "*.bs" } });
|
||||||
filterModes.append({ "Satellaview", "", { "*.bs" } });
|
filterModes.append({ "SufamiTurbo", "", { "*.st" } });
|
||||||
filterModes.append({ "SufamiTurbo", "", { "*.st" } });
|
|
||||||
mode = &filterModes[Mode::Default];
|
mode = &filterModes[Mode::Default];
|
||||||
|
|
||||||
for(auto &mode : filterModes) config.attach(mode.path, mode.name);
|
for(auto &mode : filterModes) config.attach(mode.path, mode.name);
|
||||||
|
|
|
@ -9,16 +9,7 @@ struct FileBrowser : Window {
|
||||||
Label filterLabel;
|
Label filterLabel;
|
||||||
Button openButton;
|
Button openButton;
|
||||||
|
|
||||||
struct Mode { enum : unsigned {
|
struct Mode { enum : unsigned { Default, NES, SNES, GameBoy, GameBoyColor, Satellaview, SufamiTurbo }; };
|
||||||
Default,
|
|
||||||
NES,
|
|
||||||
SNES,
|
|
||||||
GameBoy,
|
|
||||||
GameBoyColor,
|
|
||||||
GameBoyAdvance,
|
|
||||||
Satellaview,
|
|
||||||
SufamiTurbo
|
|
||||||
}; };
|
|
||||||
void open(const string &title, unsigned mode, function<void (string)> callback);
|
void open(const string &title, unsigned mode, function<void (string)> callback);
|
||||||
|
|
||||||
FileBrowser();
|
FileBrowser();
|
||||||
|
|
|
@ -11,7 +11,6 @@ MainWindow::MainWindow() {
|
||||||
cartridgeLoadNES.setText("Load &NES Cartridge ...");
|
cartridgeLoadNES.setText("Load &NES Cartridge ...");
|
||||||
cartridgeLoadGameBoy.setText("Load &Game Boy Cartridge ...");
|
cartridgeLoadGameBoy.setText("Load &Game Boy Cartridge ...");
|
||||||
cartridgeLoadGameBoyColor.setText("Load Game Boy &Color Cartridge ...");
|
cartridgeLoadGameBoyColor.setText("Load Game Boy &Color Cartridge ...");
|
||||||
cartridgeLoadGameBoyAdvance.setText("Load Game Boy &Advance Cartridge ...");
|
|
||||||
cartridgeLoadSatellaviewSlotted.setText("Load Satellaview-Slotted Cartridge ...");
|
cartridgeLoadSatellaviewSlotted.setText("Load Satellaview-Slotted Cartridge ...");
|
||||||
cartridgeLoadSatellaview.setText("Load Satellaview Cartridge ...");
|
cartridgeLoadSatellaview.setText("Load Satellaview Cartridge ...");
|
||||||
cartridgeLoadSufamiTurbo.setText("Load Sufami Turbo Cartridge ...");
|
cartridgeLoadSufamiTurbo.setText("Load Sufami Turbo Cartridge ...");
|
||||||
|
@ -104,7 +103,6 @@ MainWindow::MainWindow() {
|
||||||
cartridgeMenu.append(cartridgeLoadSNES);
|
cartridgeMenu.append(cartridgeLoadSNES);
|
||||||
cartridgeMenu.append(cartridgeLoadGameBoy);
|
cartridgeMenu.append(cartridgeLoadGameBoy);
|
||||||
cartridgeMenu.append(cartridgeLoadGameBoyColor);
|
cartridgeMenu.append(cartridgeLoadGameBoyColor);
|
||||||
cartridgeMenu.append(cartridgeLoadGameBoyAdvance);
|
|
||||||
cartridgeMenu.append(cartridgeSeparator);
|
cartridgeMenu.append(cartridgeSeparator);
|
||||||
cartridgeMenu.append(cartridgeLoadSatellaviewSlotted);
|
cartridgeMenu.append(cartridgeLoadSatellaviewSlotted);
|
||||||
cartridgeMenu.append(cartridgeLoadSatellaview);
|
cartridgeMenu.append(cartridgeLoadSatellaview);
|
||||||
|
@ -207,19 +205,13 @@ MainWindow::MainWindow() {
|
||||||
|
|
||||||
cartridgeLoadGameBoy.onActivate = [&] {
|
cartridgeLoadGameBoy.onActivate = [&] {
|
||||||
fileBrowser->open("Load Cartridge - Game Boy", FileBrowser::Mode::GameBoy, [](string filename) {
|
fileBrowser->open("Load Cartridge - Game Boy", FileBrowser::Mode::GameBoy, [](string filename) {
|
||||||
interface->gameBoy.loadCartridge(GameBoy::System::Revision::GameBoy, filename);
|
interface->gb.loadCartridge(GB::System::Revision::GameBoy, filename);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
cartridgeLoadGameBoyColor.onActivate = [&] {
|
cartridgeLoadGameBoyColor.onActivate = [&] {
|
||||||
fileBrowser->open("Load Cartridge - Game Boy Color", FileBrowser::Mode::GameBoyColor, [](string filename) {
|
fileBrowser->open("Load Cartridge - Game Boy Color", FileBrowser::Mode::GameBoyColor, [](string filename) {
|
||||||
interface->gameBoy.loadCartridge(GameBoy::System::Revision::GameBoyColor, filename);
|
interface->gb.loadCartridge(GB::System::Revision::GameBoyColor, filename);
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
cartridgeLoadGameBoyAdvance.onActivate = [&] {
|
|
||||||
fileBrowser->open("Load Cartridge - Game Boy Advance", FileBrowser::Mode::GameBoyAdvance, [](string filename) {
|
|
||||||
interface->gba.loadCartridge(filename);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ struct MainWindow : Window {
|
||||||
Item cartridgeLoadNES;
|
Item cartridgeLoadNES;
|
||||||
Item cartridgeLoadGameBoy;
|
Item cartridgeLoadGameBoy;
|
||||||
Item cartridgeLoadGameBoyColor;
|
Item cartridgeLoadGameBoyColor;
|
||||||
Item cartridgeLoadGameBoyAdvance;
|
|
||||||
Separator cartridgeSeparator;
|
Separator cartridgeSeparator;
|
||||||
Item cartridgeLoadSatellaviewSlotted;
|
Item cartridgeLoadSatellaviewSlotted;
|
||||||
Item cartridgeLoadSatellaview;
|
Item cartridgeLoadSatellaview;
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
struct GameBoyController : TertiaryInput {
|
|
||||||
DigitalInput up, down, left, right;
|
|
||||||
DigitalInput b, a, select, start;
|
|
||||||
TurboInput bTurbo, aTurbo;
|
|
||||||
|
|
||||||
int16_t poll(unsigned n);
|
|
||||||
GameBoyController();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GameBoyDevice : SecondaryInput {
|
|
||||||
GameBoyController controller;
|
|
||||||
|
|
||||||
GameBoyDevice();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GameBoyInput : PrimaryInput {
|
|
||||||
GameBoyDevice device;
|
|
||||||
|
|
||||||
GameBoyInput();
|
|
||||||
};
|
|
|
@ -1,4 +1,4 @@
|
||||||
int16_t GameBoyController::poll(unsigned n) {
|
int16_t GbController::poll(unsigned n) {
|
||||||
switch(n) {
|
switch(n) {
|
||||||
case 0: return up.poll() & !down.poll();
|
case 0: return up.poll() & !down.poll();
|
||||||
case 1: return down.poll() & !up.poll();
|
case 1: return down.poll() & !up.poll();
|
||||||
|
@ -12,7 +12,7 @@ int16_t GameBoyController::poll(unsigned n) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameBoyController::GameBoyController() {
|
GbController::GbController() {
|
||||||
name = "Controller";
|
name = "Controller";
|
||||||
|
|
||||||
up.name = "Up";
|
up.name = "Up";
|
||||||
|
@ -40,14 +40,14 @@ GameBoyController::GameBoyController() {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
GameBoyDevice::GameBoyDevice() {
|
GbDevice::GbDevice() {
|
||||||
name = "Device";
|
name = "Device";
|
||||||
append(controller);
|
append(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
GameBoyInput::GameBoyInput() {
|
GbInput::GbInput() {
|
||||||
name = "Game Boy";
|
name = "Game Boy";
|
||||||
append(device);
|
append(device);
|
||||||
}
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
struct GbController : TertiaryInput {
|
||||||
|
DigitalInput up, down, left, right;
|
||||||
|
DigitalInput b, a, select, start;
|
||||||
|
TurboInput bTurbo, aTurbo;
|
||||||
|
|
||||||
|
int16_t poll(unsigned n);
|
||||||
|
GbController();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GbDevice : SecondaryInput {
|
||||||
|
GbController controller;
|
||||||
|
|
||||||
|
GbDevice();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GbInput : PrimaryInput {
|
||||||
|
GbDevice device;
|
||||||
|
|
||||||
|
GbInput();
|
||||||
|
};
|
|
@ -1,62 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
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();
|
|
||||||
};
|
|
|
@ -1,8 +1,7 @@
|
||||||
#include "../base.hpp"
|
#include "../base.hpp"
|
||||||
#include "nes.cpp"
|
#include "nes.cpp"
|
||||||
#include "snes.cpp"
|
#include "snes.cpp"
|
||||||
#include "gameboy.cpp"
|
#include "gb.cpp"
|
||||||
#include "gba.cpp"
|
|
||||||
#include "user-interface.cpp"
|
#include "user-interface.cpp"
|
||||||
InputManager *inputManager = nullptr;
|
InputManager *inputManager = nullptr;
|
||||||
|
|
||||||
|
@ -200,8 +199,7 @@ InputManager::InputManager() {
|
||||||
|
|
||||||
inputList.append(nes);
|
inputList.append(nes);
|
||||||
inputList.append(snes);
|
inputList.append(snes);
|
||||||
inputList.append(gameBoy);
|
inputList.append(gb);
|
||||||
inputList.append(gba);
|
|
||||||
inputList.append(userInterface);
|
inputList.append(userInterface);
|
||||||
|
|
||||||
for(unsigned n = 0; n < inputList.size(); n++) inputList[n].attach();
|
for(unsigned n = 0; n < inputList.size(); n++) inputList[n].attach();
|
||||||
|
|
|
@ -51,8 +51,7 @@ struct PrimaryInput : array<SecondaryInput&> {
|
||||||
|
|
||||||
#include "nes.hpp"
|
#include "nes.hpp"
|
||||||
#include "snes.hpp"
|
#include "snes.hpp"
|
||||||
#include "gameboy.hpp"
|
#include "gb.hpp"
|
||||||
#include "gba.hpp"
|
|
||||||
#include "user-interface.hpp"
|
#include "user-interface.hpp"
|
||||||
|
|
||||||
struct InputManager {
|
struct InputManager {
|
||||||
|
@ -63,8 +62,7 @@ struct InputManager {
|
||||||
array<PrimaryInput&> inputList;
|
array<PrimaryInput&> inputList;
|
||||||
NesInput nes;
|
NesInput nes;
|
||||||
SnesInput snes;
|
SnesInput snes;
|
||||||
GameBoyInput gameBoy;
|
GbInput gb;
|
||||||
GbaInput gba;
|
|
||||||
UserInterfaceInput userInterface;
|
UserInterfaceInput userInterface;
|
||||||
|
|
||||||
void scan();
|
void scan();
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
void InterfaceGameBoy::initialize() {
|
|
||||||
GameBoy::interface = this;
|
|
||||||
GameBoy::system.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InterfaceGameBoy::cartridgeLoaded() {
|
|
||||||
return GameBoy::cartridge.loaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InterfaceGameBoy::loadCartridge(GameBoy::System::Revision revision, const string &filename) {
|
|
||||||
interface->unloadCartridge();
|
|
||||||
|
|
||||||
uint8_t *data;
|
|
||||||
unsigned size;
|
|
||||||
|
|
||||||
if(filename.endswith("/")) {
|
|
||||||
if(file::read({ filename, "program.rom" }, data, size) == false) return false;
|
|
||||||
interface->base = { true, filename };
|
|
||||||
} else {
|
|
||||||
if(file::read(filename, data, size) == false) return false;
|
|
||||||
interface->base = { false, nall::basename(filename) };
|
|
||||||
}
|
|
||||||
|
|
||||||
interface->game = interface->base;
|
|
||||||
interface->cartridgeTitle = interface->base.title();
|
|
||||||
interface->applyPatch(interface->base, data, size);
|
|
||||||
|
|
||||||
string markup;
|
|
||||||
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
|
||||||
|
|
||||||
GameBoyCartridge info(data, size);
|
|
||||||
if(markup.empty()) markup = info.markup;
|
|
||||||
|
|
||||||
GameBoy::cartridge.load(revision, markup, data, size);
|
|
||||||
GameBoy::system.power();
|
|
||||||
delete[] data;
|
|
||||||
|
|
||||||
if(GameBoy::cartridge.ramsize) {
|
|
||||||
filemap fp;
|
|
||||||
if(fp.open(interface->base.filename("program.ram", ".sav"), filemap::mode::read)) {
|
|
||||||
memcpy(GameBoy::cartridge.ramdata, fp.data(), min(GameBoy::cartridge.ramsize, fp.size()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GameBoy::interface = this;
|
|
||||||
GameBoy::video.generate(GameBoy::Video::Format::RGB30);
|
|
||||||
interface->loadCartridge(::Interface::Mode::GameBoy);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterfaceGameBoy::unloadCartridge() {
|
|
||||||
if(GameBoy::cartridge.ramsize) {
|
|
||||||
file::write(interface->base.filename("program.ram", ".sav"), GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
GameBoy::cartridge.unload();
|
|
||||||
interface->base.name = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterfaceGameBoy::power() {
|
|
||||||
GameBoy::system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterfaceGameBoy::reset() {
|
|
||||||
GameBoy::system.power(); //Game Boy lacks reset button
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterfaceGameBoy::run() {
|
|
||||||
do {
|
|
||||||
GameBoy::system.run();
|
|
||||||
} while(GameBoy::scheduler.exit_reason() != GameBoy::Scheduler::ExitReason::FrameEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer InterfaceGameBoy::serialize() {
|
|
||||||
GameBoy::system.runtosave();
|
|
||||||
return GameBoy::system.serialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InterfaceGameBoy::unserialize(serializer &s) {
|
|
||||||
return GameBoy::system.unserialize(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterfaceGameBoy::setCheats(const lstring &list) {
|
|
||||||
GameBoy::cheat.reset();
|
|
||||||
for(auto &code : list) {
|
|
||||||
lstring codelist;
|
|
||||||
codelist.split("+", code);
|
|
||||||
for(auto &part : codelist) {
|
|
||||||
unsigned addr, data, comp;
|
|
||||||
if(GameBoy::Cheat::decode(part, addr, data, comp)) {
|
|
||||||
GameBoy::cheat.append({ addr, data, comp });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GameBoy::cheat.synchronize();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
void InterfaceGameBoy::videoRefresh(const uint16_t *data) {
|
|
||||||
static uint32_t output[160 * 144];
|
|
||||||
|
|
||||||
for(unsigned y = 0; y < 144; y++) {
|
|
||||||
const uint16_t *sp = data + y * 160;
|
|
||||||
uint32_t *dp = output + y * 160;
|
|
||||||
for(unsigned x = 0; x < 160; x++) {
|
|
||||||
uint16_t color = *sp++;
|
|
||||||
*dp++ = GameBoy::video.palette[color];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface->videoRefresh(output, 160 * sizeof(uint32_t), 160, 144);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterfaceGameBoy::audioSample(int16_t csample, 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 InterfaceGameBoy::inputPoll(unsigned id) {
|
|
||||||
return inputManager->gameBoy.device.controller.poll(id);
|
|
||||||
}
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
void InterfaceGB::initialize() {
|
||||||
|
GB::interface = this;
|
||||||
|
GB::system.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterfaceGB::cartridgeLoaded() {
|
||||||
|
return GB::cartridge.loaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterfaceGB::loadCartridge(GB::System::Revision revision, const string &filename) {
|
||||||
|
interface->unloadCartridge();
|
||||||
|
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
|
||||||
|
if(filename.endswith("/")) {
|
||||||
|
if(file::read({ filename, "program.rom" }, data, size) == false) return false;
|
||||||
|
interface->base = { true, filename };
|
||||||
|
} else {
|
||||||
|
if(file::read(filename, data, size) == false) return false;
|
||||||
|
interface->base = { false, nall::basename(filename) };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface->game = interface->base;
|
||||||
|
interface->cartridgeTitle = interface->base.title();
|
||||||
|
interface->applyPatch(interface->base, data, size);
|
||||||
|
|
||||||
|
string markup;
|
||||||
|
markup.readfile(interface->base.filename("manifest.xml", ".xml"));
|
||||||
|
|
||||||
|
GameBoyCartridge info(data, size);
|
||||||
|
if(markup.empty()) markup = info.markup;
|
||||||
|
|
||||||
|
GB::cartridge.load(revision, markup, data, size);
|
||||||
|
GB::system.power();
|
||||||
|
delete[] data;
|
||||||
|
|
||||||
|
if(GB::cartridge.ramsize) {
|
||||||
|
filemap fp;
|
||||||
|
if(fp.open(interface->base.filename("program.ram", ".sav"), filemap::mode::read)) {
|
||||||
|
memcpy(GB::cartridge.ramdata, fp.data(), min(GB::cartridge.ramsize, fp.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GB::interface = this;
|
||||||
|
GB::video.generate(GB::Video::Format::RGB30);
|
||||||
|
interface->loadCartridge(::Interface::Mode::GB);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceGB::unloadCartridge() {
|
||||||
|
if(GB::cartridge.ramsize) {
|
||||||
|
file::write(interface->base.filename("program.ram", ".sav"), GB::cartridge.ramdata, GB::cartridge.ramsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
GB::cartridge.unload();
|
||||||
|
interface->base.name = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceGB::power() {
|
||||||
|
GB::system.power();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceGB::reset() {
|
||||||
|
GB::system.power(); //Game Boy lacks reset button
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceGB::run() {
|
||||||
|
do {
|
||||||
|
GB::system.run();
|
||||||
|
} while(GB::scheduler.exit_reason() != GB::Scheduler::ExitReason::FrameEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
serializer InterfaceGB::serialize() {
|
||||||
|
GB::system.runtosave();
|
||||||
|
return GB::system.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterfaceGB::unserialize(serializer &s) {
|
||||||
|
return GB::system.unserialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceGB::setCheats(const lstring &list) {
|
||||||
|
GB::cheat.reset();
|
||||||
|
for(auto &code : list) {
|
||||||
|
lstring codelist;
|
||||||
|
codelist.split("+", code);
|
||||||
|
for(auto &part : codelist) {
|
||||||
|
unsigned addr, data, comp;
|
||||||
|
if(GB::Cheat::decode(part, addr, data, comp)) {
|
||||||
|
GB::cheat.append({ addr, data, comp });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GB::cheat.synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
void InterfaceGB::videoRefresh(const uint16_t *data) {
|
||||||
|
static uint32_t output[160 * 144];
|
||||||
|
|
||||||
|
for(unsigned y = 0; y < 144; y++) {
|
||||||
|
const uint16_t *sp = data + y * 160;
|
||||||
|
uint32_t *dp = output + y * 160;
|
||||||
|
for(unsigned x = 0; x < 160; x++) {
|
||||||
|
uint16_t color = *sp++;
|
||||||
|
*dp++ = GB::video.palette[color];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface->videoRefresh(output, 160 * sizeof(uint32_t), 160, 144);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceGB::audioSample(int16_t csample, 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 InterfaceGB::inputPoll(unsigned id) {
|
||||||
|
return inputManager->gb.device.controller.poll(id);
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
struct InterfaceGameBoy : InterfaceCore, GameBoy::Interface {
|
struct InterfaceGB : InterfaceCore, GB::Interface {
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
bool cartridgeLoaded();
|
bool cartridgeLoaded();
|
||||||
bool loadCartridge(GameBoy::System::Revision revision, const string &filename);
|
bool loadCartridge(GB::System::Revision revision, const string &filename);
|
||||||
void unloadCartridge();
|
void unloadCartridge();
|
||||||
|
|
||||||
void power();
|
void power();
|
|
@ -1,110 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
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);
|
|
||||||
};
|
|
|
@ -2,8 +2,7 @@
|
||||||
#include "palette.cpp"
|
#include "palette.cpp"
|
||||||
#include "nes/nes.cpp"
|
#include "nes/nes.cpp"
|
||||||
#include "snes/snes.cpp"
|
#include "snes/snes.cpp"
|
||||||
#include "gameboy/gameboy.cpp"
|
#include "gb/gb.cpp"
|
||||||
#include "gba/gba.cpp"
|
|
||||||
Interface *interface = nullptr;
|
Interface *interface = nullptr;
|
||||||
|
|
||||||
Filter filter;
|
Filter filter;
|
||||||
|
@ -72,19 +71,17 @@ void Interface::updateDSP() {
|
||||||
dspaudio.setVolume(config->audio.mute == false ? (double)config->audio.volume / 100.0 : 0.0);
|
dspaudio.setVolume(config->audio.mute == false ? (double)config->audio.volume / 100.0 : 0.0);
|
||||||
|
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: return dspaudio.setFrequency(config->audio.frequencyNES);
|
case Mode::NES: return dspaudio.setFrequency(config->audio.frequencyNES);
|
||||||
case Mode::SNES: return dspaudio.setFrequency(config->audio.frequencySNES);
|
case Mode::SNES: return dspaudio.setFrequency(config->audio.frequencySNES);
|
||||||
case Mode::GameBoy: return dspaudio.setFrequency(config->audio.frequencyGameBoy);
|
case Mode::GB: return dspaudio.setFrequency(config->audio.frequencyGB);
|
||||||
case Mode::GBA: return dspaudio.setFrequency(config->audio.frequencyGBA);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::cartridgeLoaded() {
|
bool Interface::cartridgeLoaded() {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: return nes.cartridgeLoaded();
|
case Mode::NES: return nes.cartridgeLoaded();
|
||||||
case Mode::SNES: return snes.cartridgeLoaded();
|
case Mode::SNES: return snes.cartridgeLoaded();
|
||||||
case Mode::GameBoy: return gameBoy.cartridgeLoaded();
|
case Mode::GB: return gb.cartridgeLoaded();
|
||||||
case Mode::GBA: return gba.cartridgeLoaded();
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -92,11 +89,10 @@ bool Interface::cartridgeLoaded() {
|
||||||
void Interface::loadCartridge(Mode mode) {
|
void Interface::loadCartridge(Mode mode) {
|
||||||
utility->setMode(this->mode = mode);
|
utility->setMode(this->mode = mode);
|
||||||
switch(mode) {
|
switch(mode) {
|
||||||
case Mode::NES: core = &nes; break;
|
case Mode::NES: core = &nes; break;
|
||||||
case Mode::SNES: core = &snes; break;
|
case Mode::SNES: core = &snes; break;
|
||||||
case Mode::GameBoy: core = &gameBoy; break;
|
case Mode::GB: core = &gb; break;
|
||||||
case Mode::GBA: core = &gba; break;
|
default: core = nullptr; break;
|
||||||
default: core = nullptr; break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bindControllers();
|
bindControllers();
|
||||||
|
@ -112,8 +108,8 @@ bool Interface::loadCartridge(string filename) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if(filename.endswith(".nes") || filename.endswith(".nes/")) result = nes.loadCartridge(filename);
|
if(filename.endswith(".nes") || filename.endswith(".nes/")) result = nes.loadCartridge(filename);
|
||||||
if(filename.endswith(".sfc") || filename.endswith(".sfc/")) result = snes.loadCartridge(filename);
|
if(filename.endswith(".sfc") || filename.endswith(".sfc/")) result = snes.loadCartridge(filename);
|
||||||
if(filename.endswith(".gb" ) || filename.endswith(".gb/" )) result = gameBoy.loadCartridge(GameBoy::System::Revision::GameBoy, filename);
|
if(filename.endswith(".gb" ) || filename.endswith(".gb/" )) result = gb.loadCartridge(GB::System::Revision::GameBoy, filename);
|
||||||
if(filename.endswith(".gbc") || filename.endswith(".gbc/")) result = gameBoy.loadCartridge(GameBoy::System::Revision::GameBoyColor, filename);
|
if(filename.endswith(".gbc") || filename.endswith(".gbc/")) result = gb.loadCartridge(GB::System::Revision::GameBoyColor, filename);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,10 +121,9 @@ void Interface::unloadCartridge() {
|
||||||
setCheatCodes();
|
setCheatCodes();
|
||||||
|
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: nes.unloadCartridge(); break;
|
case Mode::NES: nes.unloadCartridge(); break;
|
||||||
case Mode::SNES: snes.unloadCartridge(); break;
|
case Mode::SNES: snes.unloadCartridge(); break;
|
||||||
case Mode::GameBoy: gameBoy.unloadCartridge(); break;
|
case Mode::GB: gb.unloadCartridge(); break;
|
||||||
case Mode::GBA: gba.unloadCartridge(); break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cartridgeTitle = "";
|
cartridgeTitle = "";
|
||||||
|
@ -186,19 +181,17 @@ bool Interface::loadState(unsigned slot) {
|
||||||
|
|
||||||
void Interface::setCheatCodes(const lstring &list) {
|
void Interface::setCheatCodes(const lstring &list) {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: return nes.setCheats(list);
|
case Mode::NES: return nes.setCheats(list);
|
||||||
case Mode::SNES: return snes.setCheats(list);
|
case Mode::SNES: return snes.setCheats(list);
|
||||||
case Mode::GameBoy: return gameBoy.setCheats(list);
|
case Mode::GB: return gb.setCheats(list);
|
||||||
case Mode::GBA: return gba.setCheats(list);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string Interface::sha256() {
|
string Interface::sha256() {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: return NES::cartridge.sha256();
|
case Mode::NES: return NES::cartridge.sha256();
|
||||||
case Mode::SNES: return SNES::cartridge.sha256();
|
case Mode::SNES: return SNES::cartridge.sha256();
|
||||||
case Mode::GameBoy: return GameBoy::cartridge.sha256();
|
case Mode::GB: return GB::cartridge.sha256();
|
||||||
case Mode::GBA: return GBA::cartridge.sha256();
|
|
||||||
}
|
}
|
||||||
return "{None}";
|
return "{None}";
|
||||||
}
|
}
|
||||||
|
@ -207,8 +200,7 @@ Interface::Interface() : core(nullptr) {
|
||||||
mode = Mode::None;
|
mode = Mode::None;
|
||||||
nes.initialize();
|
nes.initialize();
|
||||||
snes.initialize();
|
snes.initialize();
|
||||||
gameBoy.initialize();
|
gb.initialize();
|
||||||
gba.initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//internal
|
//internal
|
||||||
|
|
|
@ -18,8 +18,7 @@ struct CartridgePath {
|
||||||
|
|
||||||
#include "nes/nes.hpp"
|
#include "nes/nes.hpp"
|
||||||
#include "snes/snes.hpp"
|
#include "snes/snes.hpp"
|
||||||
#include "gameboy/gameboy.hpp"
|
#include "gb/gb.hpp"
|
||||||
#include "gba/gba.hpp"
|
|
||||||
|
|
||||||
struct Filter : public library {
|
struct Filter : public library {
|
||||||
function<void (unsigned&, unsigned&)> dl_size;
|
function<void (unsigned&, unsigned&)> dl_size;
|
||||||
|
@ -37,7 +36,7 @@ struct Filter : public library {
|
||||||
extern Filter filter;
|
extern Filter filter;
|
||||||
|
|
||||||
struct Interface : property<Interface> {
|
struct Interface : property<Interface> {
|
||||||
enum class Mode : unsigned { None, SNES, NES, GameBoy, GBA };
|
enum class Mode : unsigned { None, SNES, NES, GB };
|
||||||
readonly<Mode> mode;
|
readonly<Mode> mode;
|
||||||
|
|
||||||
void bindControllers();
|
void bindControllers();
|
||||||
|
@ -74,8 +73,7 @@ struct Interface : property<Interface> {
|
||||||
InterfaceCore *core;
|
InterfaceCore *core;
|
||||||
InterfaceNES nes;
|
InterfaceNES nes;
|
||||||
InterfaceSNES snes;
|
InterfaceSNES snes;
|
||||||
InterfaceGameBoy gameBoy;
|
InterfaceGB gb;
|
||||||
InterfaceGBA gba;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Interface *interface;
|
extern Interface *interface;
|
||||||
|
|
|
@ -193,7 +193,7 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(string basename, string slotname)
|
||||||
if(gbMarkup.empty()) gbMarkup = GameBoyCartridge(data[1], size[1]).markup;
|
if(gbMarkup.empty()) gbMarkup = GameBoyCartridge(data[1], size[1]).markup;
|
||||||
|
|
||||||
SNES::cartridge.rom.copy(data[0], size[0]);
|
SNES::cartridge.rom.copy(data[0], size[0]);
|
||||||
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, gbMarkup, data[1], size[1]);
|
GB::cartridge.load(GB::System::Revision::SuperGameBoy, gbMarkup, data[1], size[1]);
|
||||||
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, markup);
|
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, markup);
|
||||||
SNES::system.power();
|
SNES::system.power();
|
||||||
|
|
||||||
|
@ -256,11 +256,11 @@ void InterfaceSNES::loadMemory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
||||||
if(GameBoy::cartridge.ramsize) {
|
if(GB::cartridge.ramsize) {
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
unsigned size;
|
unsigned size;
|
||||||
if(file::read(interface->slot[0].filename("program.ram", ".sav"), data, size)) {
|
if(file::read(interface->slot[0].filename("program.ram", ".sav"), data, size)) {
|
||||||
memcpy(GameBoy::cartridge.ramdata, data, min(GameBoy::cartridge.ramsize, size));
|
memcpy(GB::cartridge.ramdata, data, min(GB::cartridge.ramsize, size));
|
||||||
delete[] data;
|
delete[] data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,9 +278,9 @@ void InterfaceSNES::saveMemory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
||||||
if(GameBoy::cartridge.ramsize) {
|
if(GB::cartridge.ramsize) {
|
||||||
file::write(interface->slot[0].filename("program.ram", ".sav"),
|
file::write(interface->slot[0].filename("program.ram", ".sav"),
|
||||||
GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize
|
GB::cartridge.ramdata, GB::cartridge.ramsize
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,18 +297,18 @@ bool InterfaceSNES::unserialize(serializer &s) {
|
||||||
|
|
||||||
void InterfaceSNES::setCheats(const lstring &list) {
|
void InterfaceSNES::setCheats(const lstring &list) {
|
||||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
||||||
GameBoy::cheat.reset();
|
GB::cheat.reset();
|
||||||
for(auto &code : list) {
|
for(auto &code : list) {
|
||||||
lstring codelist;
|
lstring codelist;
|
||||||
codelist.split("+", code);
|
codelist.split("+", code);
|
||||||
for(auto &part : codelist) {
|
for(auto &part : codelist) {
|
||||||
unsigned addr, data, comp;
|
unsigned addr, data, comp;
|
||||||
if(GameBoy::Cheat::decode(part, addr, data, comp)) {
|
if(GB::Cheat::decode(part, addr, data, comp)) {
|
||||||
GameBoy::cheat.append({ addr, data, comp });
|
GB::cheat.append({ addr, data, comp });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GameBoy::cheat.synchronize();
|
GB::cheat.synchronize();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,6 @@ Application::Application(int argc, char **argv) {
|
||||||
|
|
||||||
interface->unloadCartridge();
|
interface->unloadCartridge();
|
||||||
windowManager->saveGeometry();
|
windowManager->saveGeometry();
|
||||||
windowManager->hideAll();
|
|
||||||
if(compositionEnable) compositor::enable(true);
|
if(compositionEnable) compositor::enable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
AudioSettings *audioSettings = nullptr;
|
AudioSettings *audioSettings = nullptr;
|
||||||
|
|
||||||
AudioSlider::AudioSlider() {
|
AudioSlider::AudioSlider() {
|
||||||
append(name, { 75, 0 });
|
append(name, { 45, 0 });
|
||||||
append(value, { 75, 0 });
|
append(value, { 75, 0 });
|
||||||
append(slider, { ~0, 0 });
|
append(slider, { ~0, 0 });
|
||||||
}
|
}
|
||||||
|
@ -63,15 +63,10 @@ AudioSettings::AudioSettings() {
|
||||||
snes.base = 32000;
|
snes.base = 32000;
|
||||||
snes.step = 1;
|
snes.step = 1;
|
||||||
|
|
||||||
gameBoy.name.setText("Game Boy:");
|
gb.name.setText("GB:");
|
||||||
gameBoy.slider.setLength(2001);
|
gb.slider.setLength(2001);
|
||||||
gameBoy.base = 4194304;
|
gb.base = 4194304;
|
||||||
gameBoy.step = 131;
|
gb.step = 131;
|
||||||
|
|
||||||
gba.name.setText("GBA:");
|
|
||||||
gba.slider.setLength(2001);
|
|
||||||
gba.base = 32768;
|
|
||||||
gba.step = 1;
|
|
||||||
|
|
||||||
append(title, { ~0, 0 }, 5);
|
append(title, { ~0, 0 }, 5);
|
||||||
append(outputLabel, { ~0, 0 }, 0);
|
append(outputLabel, { ~0, 0 }, 0);
|
||||||
|
@ -86,8 +81,7 @@ AudioSettings::AudioSettings() {
|
||||||
append(frequencyAdjustmentLabel, { ~0, 0 }, 0);
|
append(frequencyAdjustmentLabel, { ~0, 0 }, 0);
|
||||||
append(nes, { ~0, 0 }, 0);
|
append(nes, { ~0, 0 }, 0);
|
||||||
append(snes, { ~0, 0 }, 0);
|
append(snes, { ~0, 0 }, 0);
|
||||||
append(gameBoy, { ~0, 0 }, 0);
|
append(gb, { ~0, 0 }, 0);
|
||||||
append(gba, { ~0, 0 }, 0);
|
|
||||||
|
|
||||||
frequencySelection.setSelection(
|
frequencySelection.setSelection(
|
||||||
config->audio.frequency == 32000 ? 0 :
|
config->audio.frequency == 32000 ? 0 :
|
||||||
|
@ -114,11 +108,10 @@ AudioSettings::AudioSettings() {
|
||||||
|
|
||||||
nes.setPosition(config->audio.frequencyNES);
|
nes.setPosition(config->audio.frequencyNES);
|
||||||
snes.setPosition(config->audio.frequencySNES);
|
snes.setPosition(config->audio.frequencySNES);
|
||||||
gameBoy.setPosition(config->audio.frequencyGameBoy);
|
gb.setPosition(config->audio.frequencyGB);
|
||||||
gba.setPosition(config->audio.frequencyGBA);
|
|
||||||
|
|
||||||
frequencySelection.onChange = latencySelection.onChange = resamplerSelection.onChange = volume.slider.onChange =
|
frequencySelection.onChange = latencySelection.onChange = resamplerSelection.onChange =
|
||||||
nes.slider.onChange = snes.slider.onChange = gameBoy.slider.onChange = gba.slider.onChange =
|
volume.slider.onChange = nes.slider.onChange = snes.slider.onChange = gb.slider.onChange =
|
||||||
{ &AudioSettings::synchronize, this };
|
{ &AudioSettings::synchronize, this };
|
||||||
|
|
||||||
synchronize();
|
synchronize();
|
||||||
|
@ -147,13 +140,11 @@ void AudioSettings::synchronize() {
|
||||||
|
|
||||||
config->audio.frequencyNES = nes.position();
|
config->audio.frequencyNES = nes.position();
|
||||||
config->audio.frequencySNES = snes.position();
|
config->audio.frequencySNES = snes.position();
|
||||||
config->audio.frequencyGameBoy = gameBoy.position();
|
config->audio.frequencyGB = gb.position();
|
||||||
config->audio.frequencyGBA = gba.position();
|
|
||||||
|
|
||||||
nes.value.setText({ nes.position(), "hz" });
|
nes.value.setText({ nes.position(), "hz" });
|
||||||
snes.value.setText({ snes.position(), "hz" });
|
snes.value.setText({ snes.position(), "hz" });
|
||||||
gameBoy.value.setText({ gameBoy.position(), "hz" });
|
gb.value.setText({ gb.position(), "hz" });
|
||||||
gba.value.setText({ gba.position(), "hz" });
|
|
||||||
volume.value.setText({ volume.position(), "%" });
|
volume.value.setText({ volume.position(), "%" });
|
||||||
|
|
||||||
interface->updateDSP();
|
interface->updateDSP();
|
||||||
|
|
|
@ -25,8 +25,7 @@ struct AudioSettings : SettingsLayout {
|
||||||
Label frequencyAdjustmentLabel;
|
Label frequencyAdjustmentLabel;
|
||||||
AudioSlider nes;
|
AudioSlider nes;
|
||||||
AudioSlider snes;
|
AudioSlider snes;
|
||||||
AudioSlider gameBoy;
|
AudioSlider gb;
|
||||||
AudioSlider gba;
|
|
||||||
|
|
||||||
void synchronize();
|
void synchronize();
|
||||||
AudioSettings();
|
AudioSettings();
|
||||||
|
|
|
@ -28,20 +28,15 @@ void Utility::setMode(Interface::Mode mode) {
|
||||||
dspaudio.setChannels(2);
|
dspaudio.setChannels(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(mode == Interface::Mode::GameBoy) {
|
else if(mode == Interface::Mode::GB) {
|
||||||
mainWindow->gameBoyMenu.setText(
|
mainWindow->gameBoyMenu.setText(
|
||||||
GameBoy::system.cgb() == false ? "Game Boy" : "Game Boy Color"
|
GB::system.cgb() == false ? "Game Boy" : "Game Boy Color"
|
||||||
);
|
);
|
||||||
mainWindow->setTitle(interface->cartridgeTitle);
|
mainWindow->setTitle(interface->cartridgeTitle);
|
||||||
mainWindow->gameBoyMenu.setVisible(true);
|
mainWindow->gameBoyMenu.setVisible(true);
|
||||||
dspaudio.setChannels(2);
|
dspaudio.setChannels(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(mode == Interface::Mode::GBA) {
|
|
||||||
mainWindow->setTitle(interface->cartridgeTitle);
|
|
||||||
dspaudio.setChannels(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface->updateDSP();
|
interface->updateDSP();
|
||||||
mainWindow->synchronize();
|
mainWindow->synchronize();
|
||||||
resizeMainWindow();
|
resizeMainWindow();
|
||||||
|
@ -52,11 +47,10 @@ void Utility::resizeMainWindow(bool shrink) {
|
||||||
unsigned width = geometry.width, height = geometry.height;
|
unsigned width = geometry.width, height = geometry.height;
|
||||||
|
|
||||||
switch(interface->mode()) {
|
switch(interface->mode()) {
|
||||||
case Interface::Mode::None: return mainWindow->viewport.setGeometry({ 0, 0, 1, 1 });
|
case Interface::Mode::None: return mainWindow->viewport.setGeometry({ 0, 0, 1, 1 });
|
||||||
case Interface::Mode::NES: width = 256, height = 240; break;
|
case Interface::Mode::NES: width = 256, height = 240; break;
|
||||||
case Interface::Mode::SNES: 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::GB: width = 160, height = 144; break;
|
||||||
case Interface::Mode::GBA: width = 240, height = 160; break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config->video.correctAspectRatio) {
|
if(config->video.correctAspectRatio) {
|
||||||
|
|
|
@ -44,9 +44,3 @@ void WindowManager::saveGeometry() {
|
||||||
}
|
}
|
||||||
config.save(application->path("geometry.cfg"));
|
config.save(application->path("geometry.cfg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::hideAll() {
|
|
||||||
for(auto &window : windowList) {
|
|
||||||
window.window->setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ struct WindowManager {
|
||||||
|
|
||||||
void loadGeometry();
|
void loadGeometry();
|
||||||
void saveGeometry();
|
void saveGeometry();
|
||||||
void hideAll();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
configuration config;
|
configuration config;
|
||||||
|
|
Loading…
Reference in New Issue