mirror of https://github.com/bsnes-emu/bsnes.git
Update to v083r08 release.
byuu says: Fixed SA-1 IRQ regression for Super Mario RPG Added turbo B,A to NES+GB; B,A,X,Y to SNES (please don't ask for turbo L,R; you never use those keys rapidly.) Re-added video color adjustments, which are now done in full 8-bit colorspace for more precision Gamma ramp option is gone. It's now the gamma option, which now only affects the lower-half of the colors. A value of 1.0 gives you the original, washed out colors. 1.75 is what the gamma ramp checkbox used to do (roughly). The new default is 1.5, which still prevents color washout, but isn't as overly dark as before. I wanted to make the core/interface stuff abstract the complexity of setting up a new C++ class, but it really didn't make anything easier. It was all one-line stubs to internal functions, and there was just too many platform-specific things that needed to be captured, so I did away with that. Made a base class for the ui/interface stuff to get rid of a lot of switch(mode()) stuff, still a work in progress.
This commit is contained in:
parent
f3feaa3e86
commit
483f9f8f20
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace GameBoy {
|
namespace GameBoy {
|
||||||
|
|
||||||
Interface *interface = 0;
|
Interface *interface = nullptr;
|
||||||
|
|
||||||
void Interface::lcdScanline() {
|
void Interface::lcdScanline() {
|
||||||
}
|
}
|
||||||
|
@ -20,68 +20,6 @@ bool Interface::inputPoll(unsigned id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::initialize(Interface *derived_interface) {
|
|
||||||
interface = derived_interface;
|
|
||||||
system.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Interface::cartridgeLoaded() {
|
|
||||||
return cartridge.loaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::loadCartridge(GameBoy::System::Revision revision, const string &markup, const uint8_t *data, unsigned size) {
|
|
||||||
cartridge.load(revision, markup, data, size);
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::unloadCartridge() {
|
|
||||||
cartridge.unload();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned Interface::memorySize(Memory memory) {
|
|
||||||
if(memory == Memory::RAM) return cartridge.ramsize;
|
|
||||||
return 0u;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* Interface::memoryData(Memory memory) {
|
|
||||||
if(memory == Memory::RAM) return cartridge.ramdata;
|
|
||||||
return 0u;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::power() {
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::run() {
|
|
||||||
do {
|
|
||||||
system.run();
|
|
||||||
} while(scheduler.exit_reason() != Scheduler::ExitReason::FrameEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer Interface::serialize() {
|
|
||||||
system.runtosave();
|
|
||||||
return system.serialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Interface::unserialize(serializer &s) {
|
|
||||||
return system.unserialize(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::setCheats(const lstring &list) {
|
|
||||||
cheat.reset();
|
|
||||||
for(auto &code : list) {
|
|
||||||
lstring codelist;
|
|
||||||
codelist.split("+", code);
|
|
||||||
for(auto &part : codelist) {
|
|
||||||
unsigned addr, data, comp;
|
|
||||||
if(Cheat::decode(part, addr, data, comp)) {
|
|
||||||
cheat.append({ addr, data, comp });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cheat.synchronize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::message(const string &text) {
|
void Interface::message(const string &text) {
|
||||||
print(text, "\n");
|
print(text, "\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
class Interface {
|
struct Interface {
|
||||||
public:
|
|
||||||
virtual void lcdScanline();
|
virtual void lcdScanline();
|
||||||
virtual void joypWrite(bool p15, bool p14);
|
virtual void joypWrite(bool p15, bool p14);
|
||||||
|
|
||||||
|
@ -7,27 +6,6 @@ public:
|
||||||
virtual void audioSample(int16_t center, int16_t left, int16_t right);
|
virtual void audioSample(int16_t center, int16_t left, int16_t right);
|
||||||
virtual bool inputPoll(unsigned id);
|
virtual bool inputPoll(unsigned id);
|
||||||
|
|
||||||
virtual void initialize(Interface*);
|
|
||||||
|
|
||||||
virtual bool cartridgeLoaded();
|
|
||||||
virtual void loadCartridge(GameBoy::System::Revision revision, const string &markup, const uint8_t *data, unsigned size);
|
|
||||||
virtual void unloadCartridge();
|
|
||||||
|
|
||||||
enum class Memory : unsigned {
|
|
||||||
RAM,
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual unsigned memorySize(Memory);
|
|
||||||
virtual uint8_t* memoryData(Memory);
|
|
||||||
|
|
||||||
virtual void power();
|
|
||||||
virtual void run();
|
|
||||||
|
|
||||||
virtual serializer serialize();
|
|
||||||
virtual bool unserialize(serializer&);
|
|
||||||
|
|
||||||
virtual void setCheats(const lstring &list = lstring{});
|
|
||||||
|
|
||||||
virtual void message(const string &text);
|
virtual void message(const string &text);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace NES {
|
namespace NES {
|
||||||
|
|
||||||
Interface *interface = 0;
|
Interface *interface = nullptr;
|
||||||
|
|
||||||
void Interface::videoRefresh(const uint16_t *data) {
|
void Interface::videoRefresh(const uint16_t *data) {
|
||||||
}
|
}
|
||||||
|
@ -14,74 +14,6 @@ int16_t Interface::inputPoll(bool port, unsigned device, unsigned id) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::initialize(Interface *derived_interface) {
|
|
||||||
interface = derived_interface;
|
|
||||||
system.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::connect(bool port, Input::Device device) {
|
|
||||||
input.connect(port, device);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Interface::cartridgeLoaded() {
|
|
||||||
return cartridge.loaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::loadCartridge(const string &markup, const uint8_t *data, unsigned size) {
|
|
||||||
cartridge.load(markup, data, size);
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::unloadCartridge() {
|
|
||||||
cartridge.unload();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned Interface::memorySize(Memory memory) {
|
|
||||||
if(memory == Memory::RAM) return cartridge.ram_size();
|
|
||||||
return 0u;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* Interface::memoryData(Memory memory) {
|
|
||||||
if(memory == Memory::RAM) return cartridge.ram_data();
|
|
||||||
return 0u;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::power() {
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::reset() {
|
|
||||||
system.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::run() {
|
|
||||||
system.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer Interface::serialize() {
|
|
||||||
system.runtosave();
|
|
||||||
return system.serialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Interface::unserialize(serializer &s) {
|
|
||||||
return system.unserialize(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::setCheats(const lstring &list) {
|
|
||||||
cheat.reset();
|
|
||||||
for(auto &code : list) {
|
|
||||||
lstring codelist;
|
|
||||||
codelist.split("+", code);
|
|
||||||
for(auto &part : codelist) {
|
|
||||||
unsigned addr, data, comp;
|
|
||||||
if(Cheat::decode(part, addr, data, comp)) {
|
|
||||||
cheat.append({ addr, data, comp });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cheat.synchronize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::message(const string &text) {
|
void Interface::message(const string &text) {
|
||||||
print(text, "\n");
|
print(text, "\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,30 +3,6 @@ struct Interface {
|
||||||
virtual void audioSample(int16_t sample);
|
virtual void audioSample(int16_t sample);
|
||||||
virtual int16_t inputPoll(bool port, unsigned device, unsigned id);
|
virtual int16_t inputPoll(bool port, unsigned device, unsigned id);
|
||||||
|
|
||||||
virtual void connect(bool port, Input::Device device);
|
|
||||||
|
|
||||||
virtual void initialize(Interface*);
|
|
||||||
|
|
||||||
virtual bool cartridgeLoaded();
|
|
||||||
virtual void loadCartridge(const string &markup, const uint8_t *data, unsigned size);
|
|
||||||
virtual void unloadCartridge();
|
|
||||||
|
|
||||||
enum class Memory : unsigned {
|
|
||||||
RAM,
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual unsigned memorySize(Memory);
|
|
||||||
virtual uint8_t* memoryData(Memory);
|
|
||||||
|
|
||||||
virtual void power();
|
|
||||||
virtual void reset();
|
|
||||||
virtual void run();
|
|
||||||
|
|
||||||
virtual serializer serialize();
|
|
||||||
virtual bool unserialize(serializer&);
|
|
||||||
|
|
||||||
virtual void setCheats(const lstring &list = lstring{});
|
|
||||||
|
|
||||||
virtual void message(const string &text);
|
virtual void message(const string &text);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -69,8 +69,9 @@ void ICD2::reset() {
|
||||||
joyp14lock = 0;
|
joyp14lock = 0;
|
||||||
pulselock = true;
|
pulselock = true;
|
||||||
|
|
||||||
GameBoy::Interface::initialize(this);
|
GameBoy::interface = this;
|
||||||
GameBoy::Interface::power();
|
GameBoy::system.init();
|
||||||
|
GameBoy::system.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,27 +36,40 @@ void SA1::enter() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SA1::op_irq() {
|
||||||
|
op_read(regs.pc.d);
|
||||||
|
op_io();
|
||||||
|
if(!regs.e) op_writestack(regs.pc.b);
|
||||||
|
op_writestack(regs.pc.h);
|
||||||
|
op_writestack(regs.pc.l);
|
||||||
|
op_writestack(regs.e ? (regs.p & ~0x10) : regs.p);
|
||||||
|
regs.pc.w = regs.vector;
|
||||||
|
regs.pc.b = 0x00;
|
||||||
|
regs.p.i = 1;
|
||||||
|
regs.p.d = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void SA1::last_cycle() {
|
void SA1::last_cycle() {
|
||||||
if(mmio.sa1_nmi && !mmio.sa1_nmicl) {
|
if(mmio.sa1_nmi && !mmio.sa1_nmicl) {
|
||||||
status.interrupt_pending = true;
|
status.interrupt_pending = true;
|
||||||
regs.vector = mmio.cnv;
|
regs.vector = mmio.cnv;
|
||||||
mmio.sa1_nmifl = true;
|
mmio.sa1_nmifl = true;
|
||||||
mmio.sa1_nmicl = 1;
|
mmio.sa1_nmicl = 1;
|
||||||
regs.wai = false;
|
regs.wai = false;
|
||||||
} else if(!regs.p.i) {
|
} else if(!regs.p.i) {
|
||||||
if(mmio.timer_irqen && !mmio.timer_irqcl) {
|
if(mmio.timer_irqen && !mmio.timer_irqcl) {
|
||||||
status.interrupt_pending = true;
|
status.interrupt_pending = true;
|
||||||
regs.vector = mmio.civ;
|
regs.vector = mmio.civ;
|
||||||
mmio.timer_irqfl = true;
|
mmio.timer_irqfl = true;
|
||||||
regs.wai = false;
|
regs.wai = false;
|
||||||
} else if(mmio.dma_irqen && !mmio.dma_irqcl) {
|
} else if(mmio.dma_irqen && !mmio.dma_irqcl) {
|
||||||
status.interrupt_pending = true;
|
status.interrupt_pending = true;
|
||||||
regs.vector = mmio.civ;
|
regs.vector = mmio.civ;
|
||||||
mmio.dma_irqfl = true;
|
mmio.dma_irqfl = true;
|
||||||
regs.wai = false;
|
regs.wai = false;
|
||||||
} else if(mmio.sa1_irq && !mmio.sa1_irqcl) {
|
} else if(mmio.sa1_irq && !mmio.sa1_irqcl) {
|
||||||
status.interrupt_pending = true;
|
status.interrupt_pending = true;
|
||||||
regs.vector = mmio.civ;
|
regs.vector = mmio.civ;
|
||||||
mmio.sa1_irqfl = true;
|
mmio.sa1_irqfl = true;
|
||||||
regs.wai = false;
|
regs.wai = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ public:
|
||||||
static void Enter();
|
static void Enter();
|
||||||
void enter();
|
void enter();
|
||||||
void tick();
|
void tick();
|
||||||
|
void op_irq();
|
||||||
|
|
||||||
alwaysinline void trigger_irq();
|
alwaysinline void trigger_irq();
|
||||||
alwaysinline void last_cycle();
|
alwaysinline void last_cycle();
|
||||||
|
|
|
@ -12,14 +12,13 @@ struct CPUcore {
|
||||||
virtual void op_write(uint32_t addr, uint8_t data) = 0;
|
virtual void op_write(uint32_t addr, uint8_t data) = 0;
|
||||||
virtual void last_cycle() = 0;
|
virtual void last_cycle() = 0;
|
||||||
virtual bool interrupt_pending() = 0;
|
virtual bool interrupt_pending() = 0;
|
||||||
|
virtual void op_irq();
|
||||||
|
|
||||||
void op_io_irq();
|
void op_io_irq();
|
||||||
void op_io_cond2();
|
void op_io_cond2();
|
||||||
void op_io_cond4(uint16 x, uint16 y);
|
void op_io_cond4(uint16 x, uint16 y);
|
||||||
void op_io_cond6(uint16 addr);
|
void op_io_cond6(uint16 addr);
|
||||||
|
|
||||||
void op_irq();
|
|
||||||
|
|
||||||
void op_adc_b();
|
void op_adc_b();
|
||||||
void op_adc_w();
|
void op_adc_w();
|
||||||
void op_and_b();
|
void op_and_b();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace SNES {
|
namespace SNES {
|
||||||
|
|
||||||
Interface *interface = 0;
|
Interface *interface = nullptr;
|
||||||
|
|
||||||
void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
|
void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) {
|
||||||
}
|
}
|
||||||
|
@ -14,106 +14,6 @@ int16_t Interface::inputPoll(bool port, Input::Device device, unsigned index, un
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::initialize(Interface *derived_interface) {
|
|
||||||
interface = derived_interface;
|
|
||||||
system.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::connect(bool port, Input::Device device) {
|
|
||||||
input.connect(port, device);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Interface::cartridgeLoaded() {
|
|
||||||
return cartridge.loaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::loadCartridge(const CartridgeData &base) {
|
|
||||||
cartridge.rom.copy(base.data, base.size);
|
|
||||||
cartridge.load(Cartridge::Mode::Normal, base.markup);
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::loadSatellaviewSlottedCartridge(const CartridgeData &base, const CartridgeData &slot) {
|
|
||||||
cartridge.rom.copy(base.data, base.size);
|
|
||||||
if(slot.data) bsxflash.memory.copy(slot.data, slot.size);
|
|
||||||
cartridge.load(Cartridge::Mode::BsxSlotted, base.markup);
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::loadSatellaviewCartridge(const CartridgeData &base, const CartridgeData &slot) {
|
|
||||||
cartridge.rom.copy(base.data, base.size);
|
|
||||||
if(slot.data) bsxflash.memory.copy(slot.data, slot.size);
|
|
||||||
cartridge.load(Cartridge::Mode::Bsx, base.markup);
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::loadSufamiTurboCartridge(const CartridgeData &base, const CartridgeData &slotA, const CartridgeData &slotB) {
|
|
||||||
cartridge.rom.copy(base.data, base.size);
|
|
||||||
if(slotA.data) sufamiturbo.slotA.rom.copy(slotA.data, slotA.size);
|
|
||||||
if(slotB.data) sufamiturbo.slotB.rom.copy(slotB.data, slotB.size);
|
|
||||||
cartridge.load(Cartridge::Mode::SufamiTurbo, base.markup);
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::loadSuperGameBoyCartridge(const CartridgeData &base, const CartridgeData &slot) {
|
|
||||||
cartridge.rom.copy(base.data, base.size);
|
|
||||||
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, slot.markup, slot.data, slot.size);
|
|
||||||
cartridge.load(Cartridge::Mode::SuperGameBoy, base.markup);
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::unloadCartridge() {
|
|
||||||
cartridge.unload();
|
|
||||||
}
|
|
||||||
|
|
||||||
Cartridge::Information& Interface::information() {
|
|
||||||
return cartridge.information;
|
|
||||||
}
|
|
||||||
|
|
||||||
linear_vector<Cartridge::NonVolatileRAM>& Interface::memory() {
|
|
||||||
return cartridge.nvram;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::power() {
|
|
||||||
system.power();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::reset() {
|
|
||||||
system.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::run() {
|
|
||||||
system.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
serializer Interface::serialize() {
|
|
||||||
system.runtosave();
|
|
||||||
return system.serialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Interface::unserialize(serializer &s) {
|
|
||||||
return system.unserialize(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::setCheats(const lstring &list) {
|
|
||||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) {
|
|
||||||
return icd2.setCheats(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
cheat.reset();
|
|
||||||
for(auto &code : list) {
|
|
||||||
lstring codelist;
|
|
||||||
codelist.split("+", code);
|
|
||||||
for(auto &part : codelist) {
|
|
||||||
unsigned addr, data;
|
|
||||||
if(Cheat::decode(part, addr, data)) {
|
|
||||||
cheat.append({ addr, data });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cheat.synchronize();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Interface::message(const string &text) {
|
void Interface::message(const string &text) {
|
||||||
print(text, "\n");
|
print(text, "\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,36 +3,6 @@ struct Interface {
|
||||||
virtual void audioSample(int16_t lsample, int16_t rsample);
|
virtual void audioSample(int16_t lsample, int16_t rsample);
|
||||||
virtual int16_t inputPoll(bool port, Input::Device device, unsigned index, unsigned id);
|
virtual int16_t inputPoll(bool port, Input::Device device, unsigned index, unsigned id);
|
||||||
|
|
||||||
virtual void initialize(Interface*);
|
|
||||||
|
|
||||||
virtual void connect(bool port, Input::Device device);
|
|
||||||
|
|
||||||
struct CartridgeData {
|
|
||||||
string markup;
|
|
||||||
const uint8_t *data;
|
|
||||||
unsigned size;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual bool cartridgeLoaded();
|
|
||||||
virtual void loadCartridge(const CartridgeData &base);
|
|
||||||
virtual void loadSatellaviewSlottedCartridge(const CartridgeData &base, const CartridgeData &slot);
|
|
||||||
virtual void loadSatellaviewCartridge(const CartridgeData &base, const CartridgeData &slot);
|
|
||||||
virtual void loadSufamiTurboCartridge(const CartridgeData &base, const CartridgeData &slotA, const CartridgeData &slotB);
|
|
||||||
virtual void loadSuperGameBoyCartridge(const CartridgeData &base, const CartridgeData &slot);
|
|
||||||
virtual void unloadCartridge();
|
|
||||||
|
|
||||||
Cartridge::Information& information();
|
|
||||||
linear_vector<Cartridge::NonVolatileRAM>& memory();
|
|
||||||
|
|
||||||
virtual void power();
|
|
||||||
virtual void reset();
|
|
||||||
virtual void run();
|
|
||||||
|
|
||||||
virtual serializer serialize();
|
|
||||||
virtual bool unserialize(serializer&);
|
|
||||||
|
|
||||||
void setCheats(const lstring &list = lstring{});
|
|
||||||
|
|
||||||
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
|
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
|
||||||
virtual void message(const string &text);
|
virtual void message(const string &text);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,8 +14,8 @@ Config::Config() {
|
||||||
|
|
||||||
attach(video.brightness = 100, "Video::Brightness");
|
attach(video.brightness = 100, "Video::Brightness");
|
||||||
attach(video.contrast = 100, "Video::Contrast");
|
attach(video.contrast = 100, "Video::Contrast");
|
||||||
attach(video.gamma = 100, "Video::Gamma");
|
attach(video.gamma = 50, "Video::Gamma");
|
||||||
attach(video.gammaRamp = true, "Video::GammaRamp");
|
|
||||||
attach(video.fullScreenMode = 0, "Video::FullScreenMode");
|
attach(video.fullScreenMode = 0, "Video::FullScreenMode");
|
||||||
|
|
||||||
attach(video.startFullScreen = false, "Video::StartFullScreen");
|
attach(video.startFullScreen = false, "Video::StartFullScreen");
|
||||||
|
|
|
@ -13,7 +13,7 @@ struct Config : public configuration {
|
||||||
unsigned brightness;
|
unsigned brightness;
|
||||||
unsigned contrast;
|
unsigned contrast;
|
||||||
unsigned gamma;
|
unsigned gamma;
|
||||||
bool gammaRamp;
|
|
||||||
unsigned fullScreenMode;
|
unsigned fullScreenMode;
|
||||||
|
|
||||||
bool startFullScreen;
|
bool startFullScreen;
|
||||||
|
|
|
@ -28,7 +28,7 @@ void DipSwitches::load() {
|
||||||
if(interface->mode() != Interface::Mode::SNES || SNES::cartridge.has_nss_dip() == false) return;
|
if(interface->mode() != Interface::Mode::SNES || SNES::cartridge.has_nss_dip() == false) return;
|
||||||
application->pause = true;
|
application->pause = true;
|
||||||
|
|
||||||
auto info = interface->snes.information().nss;
|
auto info = SNES::cartridge.information.nss;
|
||||||
unsigned count = info.setting.size();
|
unsigned count = info.setting.size();
|
||||||
|
|
||||||
for(unsigned n = 0; n < min(8, count); n++) {
|
for(unsigned n = 0; n < min(8, count); n++) {
|
||||||
|
@ -55,7 +55,7 @@ void DipSwitches::load() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DipSwitches::accept() {
|
void DipSwitches::accept() {
|
||||||
auto info = interface->snes.information().nss;
|
auto info = SNES::cartridge.information.nss;
|
||||||
unsigned count = info.setting.size();
|
unsigned count = info.setting.size();
|
||||||
|
|
||||||
unsigned result = 0x0000;
|
unsigned result = 0x0000;
|
||||||
|
|
|
@ -4,8 +4,8 @@ int16_t GameBoyController::poll(unsigned n) {
|
||||||
case 1: return down.poll() & !up.poll();
|
case 1: return down.poll() & !up.poll();
|
||||||
case 2: return left.poll() & !right.poll();
|
case 2: return left.poll() & !right.poll();
|
||||||
case 3: return right.poll() & !left.poll();
|
case 3: return right.poll() & !left.poll();
|
||||||
case 4: return b.poll();
|
case 4: return b.poll() | bTurbo.poll();
|
||||||
case 5: return a.poll();
|
case 5: return a.poll() | aTurbo.poll();
|
||||||
case 6: return select.poll();
|
case 6: return select.poll();
|
||||||
case 7: return start.poll();
|
case 7: return start.poll();
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ GameBoyController::GameBoyController() {
|
||||||
a.name = "A";
|
a.name = "A";
|
||||||
select.name = "Select";
|
select.name = "Select";
|
||||||
start.name = "Start";
|
start.name = "Start";
|
||||||
|
bTurbo.name = "Turbo B";
|
||||||
|
aTurbo.name = "Turbo A";
|
||||||
|
|
||||||
up.mapping = "KB0::Up";
|
up.mapping = "KB0::Up";
|
||||||
down.mapping = "KB0::Down";
|
down.mapping = "KB0::Down";
|
||||||
|
@ -35,6 +37,7 @@ GameBoyController::GameBoyController() {
|
||||||
|
|
||||||
append(up); append(down); append(left); append(right);
|
append(up); append(down); append(left); append(right);
|
||||||
append(b); append(a); append(select); append(start);
|
append(b); append(a); append(select); append(start);
|
||||||
|
append(bTurbo); append(aTurbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
struct GameBoyController : TertiaryInput {
|
struct GameBoyController : TertiaryInput {
|
||||||
DigitalInput up, down, left, right;
|
DigitalInput up, down, left, right;
|
||||||
DigitalInput b, a, select, start;
|
DigitalInput b, a, select, start;
|
||||||
|
TurboInput bTurbo, aTurbo;
|
||||||
|
|
||||||
int16_t poll(unsigned n);
|
int16_t poll(unsigned n);
|
||||||
GameBoyController();
|
GameBoyController();
|
||||||
|
|
|
@ -121,6 +121,18 @@ int16_t DigitalInput::poll() {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
int16_t TurboInput::poll() {
|
||||||
|
int16_t result = DigitalInput::poll();
|
||||||
|
if(phase < 3) result = 0;
|
||||||
|
if(++phase >= 6) phase = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
TurboInput::TurboInput() : phase(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
void TertiaryInput::attach(const string &primaryName, const string &secondaryName) {
|
void TertiaryInput::attach(const string &primaryName, const string &secondaryName) {
|
||||||
for(unsigned n = 0; n < size(); n++) {
|
for(unsigned n = 0; n < size(); n++) {
|
||||||
operator[](n).attach(primaryName, secondaryName, name);
|
operator[](n).attach(primaryName, secondaryName, name);
|
||||||
|
|
|
@ -20,6 +20,13 @@ struct DigitalInput : AbstractInput {
|
||||||
int16_t poll();
|
int16_t poll();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TurboInput : DigitalInput {
|
||||||
|
unsigned phase;
|
||||||
|
|
||||||
|
int16_t poll();
|
||||||
|
TurboInput();
|
||||||
|
};
|
||||||
|
|
||||||
struct TertiaryInput : reference_array<AbstractInput&> {
|
struct TertiaryInput : reference_array<AbstractInput&> {
|
||||||
string name;
|
string name;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
int16_t NesGamepad::poll(unsigned n) {
|
int16_t NesGamepad::poll(unsigned n) {
|
||||||
switch(n) {
|
switch(n) {
|
||||||
case 0: return a.poll();
|
case 0: return a.poll() | aTurbo.poll();
|
||||||
case 1: return b.poll();
|
case 1: return b.poll() | bTurbo.poll();
|
||||||
case 2: return select.poll();
|
case 2: return select.poll();
|
||||||
case 3: return start.poll();
|
case 3: return start.poll();
|
||||||
case 4: return up.poll() & !down.poll();
|
case 4: return up.poll() & !down.poll();
|
||||||
|
@ -23,6 +23,8 @@ NesGamepad::NesGamepad() {
|
||||||
a.name = "A";
|
a.name = "A";
|
||||||
select.name = "Select";
|
select.name = "Select";
|
||||||
start.name = "Start";
|
start.name = "Start";
|
||||||
|
bTurbo.name = "Turbo B";
|
||||||
|
aTurbo.name = "Turbo A";
|
||||||
|
|
||||||
up.mapping = "KB0::Up";
|
up.mapping = "KB0::Up";
|
||||||
down.mapping = "KB0::Down";
|
down.mapping = "KB0::Down";
|
||||||
|
@ -35,6 +37,7 @@ NesGamepad::NesGamepad() {
|
||||||
|
|
||||||
append(up); append(down); append(left); append(right);
|
append(up); append(down); append(left); append(right);
|
||||||
append(b); append(a); append(select); append(start);
|
append(b); append(a); append(select); append(start);
|
||||||
|
append(bTurbo); append(aTurbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
struct NesGamepad : TertiaryInput {
|
struct NesGamepad : TertiaryInput {
|
||||||
DigitalInput up, down, left, right;
|
DigitalInput up, down, left, right;
|
||||||
DigitalInput b, a, select, start;
|
DigitalInput b, a, select, start;
|
||||||
|
TurboInput bTurbo, aTurbo;
|
||||||
|
|
||||||
int16_t poll(unsigned n);
|
int16_t poll(unsigned n);
|
||||||
NesGamepad();
|
NesGamepad();
|
||||||
|
|
|
@ -4,10 +4,10 @@ int16_t SnesGamepad::poll(unsigned n) {
|
||||||
case SNES::Input::JoypadID::Down: return down.poll() & !up.poll();
|
case SNES::Input::JoypadID::Down: return down.poll() & !up.poll();
|
||||||
case SNES::Input::JoypadID::Left: return left.poll() & !right.poll();
|
case SNES::Input::JoypadID::Left: return left.poll() & !right.poll();
|
||||||
case SNES::Input::JoypadID::Right: return right.poll() & !left.poll();
|
case SNES::Input::JoypadID::Right: return right.poll() & !left.poll();
|
||||||
case SNES::Input::JoypadID::B: return b.poll();
|
case SNES::Input::JoypadID::B: return b.poll() | bTurbo.poll();
|
||||||
case SNES::Input::JoypadID::A: return a.poll();
|
case SNES::Input::JoypadID::A: return a.poll() | aTurbo.poll();
|
||||||
case SNES::Input::JoypadID::Y: return y.poll();
|
case SNES::Input::JoypadID::Y: return y.poll() | yTurbo.poll();
|
||||||
case SNES::Input::JoypadID::X: return x.poll();
|
case SNES::Input::JoypadID::X: return x.poll() | xTurbo.poll();
|
||||||
case SNES::Input::JoypadID::L: return l.poll();
|
case SNES::Input::JoypadID::L: return l.poll();
|
||||||
case SNES::Input::JoypadID::R: return r.poll();
|
case SNES::Input::JoypadID::R: return r.poll();
|
||||||
case SNES::Input::JoypadID::Select: return select.poll();
|
case SNES::Input::JoypadID::Select: return select.poll();
|
||||||
|
@ -22,6 +22,7 @@ SnesGamepad::SnesGamepad(const string &name, bool defaultBindings) {
|
||||||
up.name = "Up", down.name = "Down", left.name = "Left", right.name = "Right";
|
up.name = "Up", down.name = "Down", left.name = "Left", right.name = "Right";
|
||||||
b.name = "B", a.name = "A", y.name = "Y", x.name = "X";
|
b.name = "B", a.name = "A", y.name = "Y", x.name = "X";
|
||||||
l.name = "L", r.name = "R", select.name = "Select", start.name = "Start";
|
l.name = "L", r.name = "R", select.name = "Select", start.name = "Start";
|
||||||
|
bTurbo.name = "Turbo B", aTurbo.name = "Turbo A", yTurbo.name = "Turbo Y", xTurbo.name = "Turbo X";
|
||||||
|
|
||||||
if(defaultBindings) {
|
if(defaultBindings) {
|
||||||
up.mapping = "KB0::Up";
|
up.mapping = "KB0::Up";
|
||||||
|
@ -41,6 +42,7 @@ SnesGamepad::SnesGamepad(const string &name, bool defaultBindings) {
|
||||||
append(up); append(down); append(left); append(right);
|
append(up); append(down); append(left); append(right);
|
||||||
append(b); append(a); append(y); append(x);
|
append(b); append(a); append(y); append(x);
|
||||||
append(l); append(r); append(select); append(start);
|
append(l); append(r); append(select); append(start);
|
||||||
|
append(bTurbo); append(aTurbo); append(yTurbo); append(xTurbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -2,6 +2,7 @@ struct SnesGamepad : TertiaryInput {
|
||||||
DigitalInput up, down, left, right;
|
DigitalInput up, down, left, right;
|
||||||
DigitalInput b, a, y, x;
|
DigitalInput b, a, y, x;
|
||||||
DigitalInput l, r, select, start;
|
DigitalInput l, r, select, start;
|
||||||
|
TurboInput bTurbo, aTurbo, yTurbo, xTurbo;
|
||||||
|
|
||||||
int16_t poll(unsigned n);
|
int16_t poll(unsigned n);
|
||||||
SnesGamepad(const string &name, bool defaultBindings);
|
SnesGamepad(const string &name, bool defaultBindings);
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
bool InterfaceGameBoy::loadCartridge(GameBoy::System::Revision revision, const string &filename) {
|
|
||||||
uint8_t *data;
|
|
||||||
unsigned size;
|
|
||||||
if(interface->loadFile(filename, data, size) == false) return false;
|
|
||||||
|
|
||||||
interface->unloadCartridge();
|
|
||||||
interface->baseName = nall::basename(filename);
|
|
||||||
|
|
||||||
GameBoyCartridge info(data, size);
|
|
||||||
GameBoy::Interface::loadCartridge(revision, info.markup, data, size);
|
|
||||||
delete[] data;
|
|
||||||
|
|
||||||
if(GameBoy::Interface::memorySize(GameBoy::Interface::Memory::RAM) > 0) {
|
|
||||||
filemap fp;
|
|
||||||
if(fp.open(string{ interface->baseName, ".sav" }, filemap::mode::read)) {
|
|
||||||
memcpy(GameBoy::Interface::memoryData(GameBoy::Interface::Memory::RAM), fp.data(),
|
|
||||||
min(GameBoy::Interface::memorySize(GameBoy::Interface::Memory::RAM), fp.size())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GameBoy::interface = this;
|
|
||||||
GameBoy::video.generate(GameBoy::Video::Format::RGB24);
|
|
||||||
interface->loadCartridge(::Interface::Mode::GameBoy);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterfaceGameBoy::unloadCartridge() {
|
|
||||||
if(GameBoy::Interface::memorySize(GameBoy::Interface::Memory::RAM) > 0) {
|
|
||||||
file::write({ interface->baseName, ".sav" },
|
|
||||||
GameBoy::Interface::memoryData(GameBoy::Interface::Memory::RAM),
|
|
||||||
GameBoy::Interface::memorySize(GameBoy::Interface::Memory::RAM)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
GameBoy::Interface::unloadCartridge();
|
|
||||||
interface->baseName = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InterfaceGameBoy::saveState(const string &filename) {
|
|
||||||
serializer s = serialize();
|
|
||||||
return file::write(filename, s.data(), s.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InterfaceGameBoy::loadState(const string &filename) {
|
|
||||||
uint8_t *data;
|
|
||||||
unsigned size;
|
|
||||||
if(file::read(filename, data, size) == false) return false;
|
|
||||||
serializer s(data, size);
|
|
||||||
delete[] data;
|
|
||||||
return unserialize(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
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 * 4, 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);
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
struct InterfaceGameBoy : GameBoy::Interface {
|
|
||||||
bool loadCartridge(GameBoy::System::Revision revision, const string &filename);
|
|
||||||
void unloadCartridge();
|
|
||||||
|
|
||||||
bool saveState(const string &filename);
|
|
||||||
bool loadState(const string &filename);
|
|
||||||
|
|
||||||
void videoRefresh(const uint16_t *data);
|
|
||||||
void audioSample(int16_t csample, int16_t lsample, int16_t rsample);
|
|
||||||
bool inputPoll(unsigned id);
|
|
||||||
};
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
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) {
|
||||||
|
uint8_t *data;
|
||||||
|
unsigned size;
|
||||||
|
if(interface->loadFile(filename, data, size) == false) return false;
|
||||||
|
|
||||||
|
interface->unloadCartridge();
|
||||||
|
interface->baseName = nall::basename(filename);
|
||||||
|
|
||||||
|
GameBoyCartridge info(data, size);
|
||||||
|
GameBoy::cartridge.load(revision, info.markup, data, size);
|
||||||
|
GameBoy::system.power();
|
||||||
|
delete[] data;
|
||||||
|
|
||||||
|
if(GameBoy::cartridge.ramsize) {
|
||||||
|
filemap fp;
|
||||||
|
if(fp.open(string{ interface->baseName, ".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::RGB24);
|
||||||
|
interface->loadCartridge(::Interface::Mode::GameBoy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceGameBoy::unloadCartridge() {
|
||||||
|
if(GameBoy::cartridge.ramsize) {
|
||||||
|
file::write({ interface->baseName, ".sav" }, GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameBoy::cartridge.unload();
|
||||||
|
interface->baseName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
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 * 4, 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,20 @@
|
||||||
|
struct InterfaceGameBoy : InterfaceCore, GameBoy::Interface {
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
bool cartridgeLoaded();
|
||||||
|
bool loadCartridge(GameBoy::System::Revision revision, 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 csample, int16_t lsample, int16_t rsample);
|
||||||
|
bool inputPoll(unsigned id);
|
||||||
|
};
|
|
@ -1,9 +1,9 @@
|
||||||
#include "../base.hpp"
|
#include "../base.hpp"
|
||||||
#include "palette.cpp"
|
#include "palette.cpp"
|
||||||
#include "nes.cpp"
|
#include "nes/nes.cpp"
|
||||||
#include "snes.cpp"
|
#include "snes/snes.cpp"
|
||||||
#include "gameboy.cpp"
|
#include "gameboy/gameboy.cpp"
|
||||||
Interface *interface = 0;
|
Interface *interface = nullptr;
|
||||||
|
|
||||||
Filter filter;
|
Filter filter;
|
||||||
|
|
||||||
|
@ -63,6 +63,13 @@ 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) {
|
||||||
|
case Mode::NES: core = &nes; break;
|
||||||
|
case Mode::SNES: core = &snes; break;
|
||||||
|
case Mode::GameBoy: core = &gameBoy; break;
|
||||||
|
default: core = nullptr; break;
|
||||||
|
}
|
||||||
|
|
||||||
bindControllers();
|
bindControllers();
|
||||||
cheatEditor->load({ baseName, ".cht" });
|
cheatEditor->load({ baseName, ".cht" });
|
||||||
stateManager->load({ baseName, ".bsa" }, 0u);
|
stateManager->load({ baseName, ".bsa" }, 0u);
|
||||||
|
@ -98,69 +105,50 @@ void Interface::unloadCartridge() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::power() {
|
void Interface::power() {
|
||||||
switch(mode()) {
|
if(core == nullptr) return;
|
||||||
case Mode::NES: nes.power(); break;
|
core->power();
|
||||||
case Mode::SNES: snes.power(); break;
|
|
||||||
case Mode::GameBoy: gameBoy.power(); break;
|
|
||||||
}
|
|
||||||
utility->showMessage("System power was cycled");
|
utility->showMessage("System power was cycled");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::reset() {
|
void Interface::reset() {
|
||||||
switch(mode()) {
|
if(core == nullptr) return;
|
||||||
case Mode::NES: nes.reset(); break;
|
core->reset();
|
||||||
case Mode::SNES: snes.reset(); break;
|
|
||||||
case Mode::GameBoy: gameBoy.power(); break; //Game Boy lacks reset button
|
|
||||||
}
|
|
||||||
utility->showMessage("System was reset");
|
utility->showMessage("System was reset");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::run() {
|
void Interface::run() {
|
||||||
switch(mode()) {
|
if(core == nullptr) return;
|
||||||
case Mode::NES: return nes.run();
|
core->run();
|
||||||
case Mode::SNES: return snes.run();
|
|
||||||
case Mode::GameBoy: return gameBoy.run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer Interface::serialize() {
|
serializer Interface::serialize() {
|
||||||
switch(mode()) {
|
if(core == nullptr) return serializer();
|
||||||
case Mode::NES: return nes.serialize();
|
return core->serialize();
|
||||||
case Mode::SNES: return snes.serialize();
|
|
||||||
case Mode::GameBoy: return gameBoy.serialize();
|
|
||||||
}
|
|
||||||
return serializer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::unserialize(serializer &s) {
|
bool Interface::unserialize(serializer &s) {
|
||||||
switch(mode()) {
|
if(core == nullptr) return false;
|
||||||
case Mode::NES: return nes.unserialize(s);
|
return core->unserialize(s);
|
||||||
case Mode::SNES: return snes.unserialize(s);
|
|
||||||
case Mode::GameBoy: return gameBoy.unserialize(s);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::saveState(unsigned slot) {
|
bool Interface::saveState(unsigned slot) {
|
||||||
string filename = { baseName, "-", slot, ".bst" };
|
string filename = { baseName, "-", slot, ".bst" };
|
||||||
bool result = false;
|
serializer s = serialize();
|
||||||
switch(mode()) {
|
bool result = file::write(filename, s.data(), s.size());
|
||||||
case Mode::NES: result = nes.saveState(filename); break;
|
|
||||||
case Mode::SNES: result = snes.saveState(filename); break;
|
|
||||||
case Mode::GameBoy: result = gameBoy.saveState(filename); break;
|
|
||||||
}
|
|
||||||
utility->showMessage(result == true ? string{ "Saved state ", slot } : "Failed to save state");
|
utility->showMessage(result == true ? string{ "Saved state ", slot } : "Failed to save state");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::loadState(unsigned slot) {
|
bool Interface::loadState(unsigned slot) {
|
||||||
string filename = { baseName, "-", slot, ".bst" };
|
string filename = { baseName, "-", slot, ".bst" };
|
||||||
bool result = false;
|
uint8_t *data;
|
||||||
switch(mode()) {
|
unsigned size;
|
||||||
case Mode::NES: result = nes.loadState(filename); break;
|
if(file::read(filename, data, size) == false) {
|
||||||
case Mode::SNES: result = snes.loadState(filename); break;
|
utility->showMessage(string{ "Slot ", slot, " save file not found" });
|
||||||
case Mode::GameBoy: result = gameBoy.loadState(filename); break;
|
return false;
|
||||||
}
|
}
|
||||||
|
serializer s(data, size);
|
||||||
|
bool result = unserialize(s);
|
||||||
utility->showMessage(result == true ? string{ "Loaded state ", slot } : "Failed to load state");
|
utility->showMessage(result == true ? string{ "Loaded state ", slot } : "Failed to load state");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -182,12 +170,12 @@ string Interface::sha256() {
|
||||||
return "{None}";
|
return "{None}";
|
||||||
}
|
}
|
||||||
|
|
||||||
Interface::Interface() {
|
Interface::Interface() : core(nullptr) {
|
||||||
mode = Mode::None;
|
mode = Mode::None;
|
||||||
palette.update();
|
palette.update();
|
||||||
nes.initialize(&nes);
|
nes.initialize();
|
||||||
snes.initialize(&snes);
|
snes.initialize();
|
||||||
gameBoy.initialize(&gameBoy);
|
gameBoy.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
//internal
|
//internal
|
||||||
|
@ -234,7 +222,8 @@ void Interface::videoRefresh(const uint32_t *input, unsigned inputPitch, unsigne
|
||||||
const uint32_t *sp = input + y * inputPitch;
|
const uint32_t *sp = input + y * inputPitch;
|
||||||
uint32_t *dp = output + y * outputPitch;
|
uint32_t *dp = output + y * outputPitch;
|
||||||
for(unsigned x = 0; x < width; x++) {
|
for(unsigned x = 0; x < width; x++) {
|
||||||
*dp++ = *sp++; //palette[*sp++];
|
uint32_t color = *sp++;
|
||||||
|
*dp++ = (palette[color >> 16] << 16) + (palette[color >> 8] << 8) + (palette[color >> 0] << 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
#include "palette.hpp"
|
#include "palette.hpp"
|
||||||
|
|
||||||
#include "nes.hpp"
|
struct InterfaceCore {
|
||||||
#include "snes.hpp"
|
virtual void power() = 0;
|
||||||
#include "gameboy.hpp"
|
virtual void reset() = 0;
|
||||||
|
virtual void run() = 0;
|
||||||
|
|
||||||
|
virtual serializer serialize() = 0;
|
||||||
|
virtual bool unserialize(serializer&) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "nes/nes.hpp"
|
||||||
|
#include "snes/snes.hpp"
|
||||||
|
#include "gameboy/gameboy.hpp"
|
||||||
|
|
||||||
struct Filter : public library {
|
struct Filter : public library {
|
||||||
function<void (unsigned&, unsigned&)> dl_size;
|
function<void (unsigned&, unsigned&)> dl_size;
|
||||||
|
@ -52,6 +61,7 @@ struct Interface : property<Interface> {
|
||||||
string baseName; // = "/path/to/cartridge" (no extension)
|
string baseName; // = "/path/to/cartridge" (no extension)
|
||||||
lstring slotName;
|
lstring slotName;
|
||||||
|
|
||||||
|
InterfaceCore *core;
|
||||||
InterfaceNES nes;
|
InterfaceNES nes;
|
||||||
InterfaceSNES snes;
|
InterfaceSNES snes;
|
||||||
InterfaceGameBoy gameBoy;
|
InterfaceGameBoy gameBoy;
|
||||||
|
|
|
@ -1,18 +1,27 @@
|
||||||
|
void InterfaceNES::initialize() {
|
||||||
|
NES::interface = this;
|
||||||
|
NES::system.init();
|
||||||
|
}
|
||||||
|
|
||||||
void InterfaceNES::setController(bool port, unsigned device) {
|
void InterfaceNES::setController(bool port, unsigned device) {
|
||||||
if(port == 0) config->nes.controllerPort1Device = device;
|
if(port == 0) config->nes.controllerPort1Device = device;
|
||||||
if(port == 1) config->nes.controllerPort2Device = device;
|
if(port == 1) config->nes.controllerPort2Device = device;
|
||||||
|
|
||||||
if(port == 0) switch(device) {
|
if(port == 0) switch(device) {
|
||||||
case 0: return connect(0, NES::Input::Device::None);
|
case 0: return NES::input.connect(0, NES::Input::Device::None);
|
||||||
case 1: return connect(0, NES::Input::Device::Joypad);
|
case 1: return NES::input.connect(0, NES::Input::Device::Joypad);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(port == 1) switch(device) {
|
if(port == 1) switch(device) {
|
||||||
case 0: return connect(1, NES::Input::Device::None);
|
case 0: return NES::input.connect(1, NES::Input::Device::None);
|
||||||
case 1: return connect(1, NES::Input::Device::Joypad);
|
case 1: return NES::input.connect(1, NES::Input::Device::Joypad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InterfaceNES::cartridgeLoaded() {
|
||||||
|
return NES::cartridge.loaded();
|
||||||
|
}
|
||||||
|
|
||||||
bool InterfaceNES::loadCartridge(const string &filename) {
|
bool InterfaceNES::loadCartridge(const string &filename) {
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
unsigned size;
|
unsigned size;
|
||||||
|
@ -24,15 +33,14 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile({ interface->baseName, ".bml" });
|
markup.readfile({ interface->baseName, ".bml" });
|
||||||
|
|
||||||
NES::Interface::loadCartridge(markup, data, size);
|
NES::cartridge.load(markup, data, size);
|
||||||
|
NES::system.power();
|
||||||
delete[] data;
|
delete[] data;
|
||||||
|
|
||||||
if(NES::Interface::memorySize(NES::Interface::Memory::RAM) > 0) {
|
if(NES::cartridge.ram_size()) {
|
||||||
filemap fp;
|
filemap fp;
|
||||||
if(fp.open(string{ interface->baseName, ".sav" }, filemap::mode::read)) {
|
if(fp.open(string{ interface->baseName, ".sav" }, filemap::mode::read)) {
|
||||||
memcpy(NES::Interface::memoryData(NES::Interface::Memory::RAM), fp.data(),
|
memcpy(NES::cartridge.ram_data(), fp.data(), min(NES::cartridge.ram_size(), fp.size()));
|
||||||
min(NES::Interface::memorySize(NES::Interface::Memory::RAM), fp.size())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,31 +50,53 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterfaceNES::unloadCartridge() {
|
void InterfaceNES::unloadCartridge() {
|
||||||
if(NES::Interface::memorySize(NES::Interface::Memory::RAM) > 0) {
|
if(NES::cartridge.ram_size()) {
|
||||||
file::write({ interface->baseName, ".sav" },
|
file::write({ interface->baseName, ".sav" }, NES::cartridge.ram_data(), NES::cartridge.ram_size());
|
||||||
NES::Interface::memoryData(NES::Interface::Memory::RAM),
|
|
||||||
NES::Interface::memorySize(NES::Interface::Memory::RAM)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
NES::cartridge.unload();
|
||||||
NES::Interface::unloadCartridge();
|
|
||||||
interface->baseName = "";
|
interface->baseName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
bool InterfaceNES::saveState(const string &filename) {
|
void InterfaceNES::power() {
|
||||||
serializer s = serialize();
|
NES::system.power();
|
||||||
return file::write(filename, s.data(), s.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceNES::loadState(const string &filename) {
|
void InterfaceNES::reset() {
|
||||||
uint8_t *data;
|
NES::system.reset();
|
||||||
unsigned size;
|
}
|
||||||
if(file::read(filename, data, size) == false) return false;
|
|
||||||
serializer s(data, size);
|
void InterfaceNES::run() {
|
||||||
delete[] data;
|
NES::system.run();
|
||||||
return unserialize(s);
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
serializer InterfaceNES::serialize() {
|
||||||
|
NES::system.runtosave();
|
||||||
|
return NES::system.serialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterfaceNES::unserialize(serializer &s) {
|
||||||
|
return NES::system.unserialize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
void InterfaceNES::setCheats(const lstring &list) {
|
||||||
|
NES::cheat.reset();
|
||||||
|
for(auto &code : list) {
|
||||||
|
lstring codelist;
|
||||||
|
codelist.split("+", code);
|
||||||
|
for(auto &part : codelist) {
|
||||||
|
unsigned addr, data, comp;
|
||||||
|
if(NES::Cheat::decode(part, addr, data, comp)) {
|
||||||
|
NES::cheat.append({ addr, data, comp });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NES::cheat.synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
|
@ -1,11 +1,19 @@
|
||||||
struct InterfaceNES : NES::Interface {
|
struct InterfaceNES : InterfaceCore, NES::Interface {
|
||||||
|
void initialize();
|
||||||
void setController(bool port, unsigned device);
|
void setController(bool port, unsigned device);
|
||||||
|
|
||||||
|
bool cartridgeLoaded();
|
||||||
bool loadCartridge(const string &filename);
|
bool loadCartridge(const string &filename);
|
||||||
void unloadCartridge();
|
void unloadCartridge();
|
||||||
|
|
||||||
bool saveState(const string &filename);
|
void power();
|
||||||
bool loadState(const string &filename);
|
void reset();
|
||||||
|
void run();
|
||||||
|
|
||||||
|
serializer serialize();
|
||||||
|
bool unserialize(serializer&);
|
||||||
|
|
||||||
|
void setCheats(const lstring &list = lstring{});
|
||||||
|
|
||||||
void videoRefresh(const uint16_t *data);
|
void videoRefresh(const uint16_t *data);
|
||||||
void audioSample(int16_t sample);
|
void audioSample(int16_t sample);
|
|
@ -1,15 +1,17 @@
|
||||||
Palette palette;
|
Palette palette;
|
||||||
|
|
||||||
uint32_t Palette::operator[](unsigned n) {
|
uint8_t Palette::operator[](uint8_t n) {
|
||||||
return color[n];
|
return color[n];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 5-bit -> 8-bit
|
||||||
const uint8_t Palette::gammaRamp[32] = {
|
const uint8_t Palette::gammaRamp[32] = {
|
||||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||||
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
uint8_t Palette::contrastAdjust(uint8_t input) {
|
uint8_t Palette::contrastAdjust(uint8_t input) {
|
||||||
signed contrast = config->video.contrast - 100;
|
signed contrast = config->video.contrast - 100;
|
||||||
|
@ -29,39 +31,17 @@ uint8_t Palette::gammaAdjust(uint8_t input) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Palette::update() {
|
void Palette::update() {
|
||||||
for(unsigned i = 0; i < 32768; i++) {
|
double exponent = 1.0 + (double)config->video.gamma * 0.01;
|
||||||
unsigned r = (i >> 10) & 31;
|
for(unsigned n = 0; n < 256; n++) {
|
||||||
unsigned g = (i >> 5) & 31;
|
unsigned result = (n < 128 ? 127 * pow(((double)n / 127), exponent) : n);
|
||||||
unsigned b = (i >> 0) & 31;
|
color[n] = result;
|
||||||
|
}
|
||||||
|
|
||||||
r = (r << 3) | (r >> 2);
|
for(unsigned n = 0; n < 256; n++) {
|
||||||
g = (g << 3) | (g >> 2);
|
color[n] = contrastAdjust(color[n]);
|
||||||
b = (b << 3) | (b >> 2);
|
}
|
||||||
|
|
||||||
if(config->video.gammaRamp) {
|
for(unsigned n = 0; n < 256; n++) {
|
||||||
r = gammaRamp[r >> 3];
|
color[n] = brightnessAdjust(color[n]);
|
||||||
g = gammaRamp[g >> 3];
|
|
||||||
b = gammaRamp[b >> 3];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config->video.contrast != 100) {
|
|
||||||
r = contrastAdjust(r);
|
|
||||||
g = contrastAdjust(g);
|
|
||||||
b = contrastAdjust(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config->video.brightness != 100) {
|
|
||||||
r = brightnessAdjust(r);
|
|
||||||
g = brightnessAdjust(g);
|
|
||||||
b = brightnessAdjust(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config->video.gamma != 100) {
|
|
||||||
r = gammaAdjust(r);
|
|
||||||
g = gammaAdjust(g);
|
|
||||||
b = gammaAdjust(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
color[i] = (r << 16) | (g << 8) | (b << 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
struct Palette {
|
struct Palette {
|
||||||
alwaysinline uint32_t operator[](unsigned n);
|
alwaysinline uint8_t operator[](uint8_t color);
|
||||||
|
|
||||||
uint8_t contrastAdjust(uint8_t);
|
uint8_t contrastAdjust(uint8_t);
|
||||||
uint8_t brightnessAdjust(uint8_t);
|
uint8_t brightnessAdjust(uint8_t);
|
||||||
|
@ -7,8 +7,7 @@ struct Palette {
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint8_t gammaRamp[32];
|
uint32_t color[256];
|
||||||
uint32_t color[32768];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Palette palette;
|
extern Palette palette;
|
||||||
|
|
|
@ -1,26 +1,35 @@
|
||||||
|
void InterfaceSNES::initialize() {
|
||||||
|
SNES::interface = this;
|
||||||
|
SNES::system.init();
|
||||||
|
}
|
||||||
|
|
||||||
void InterfaceSNES::setController(bool port, unsigned device) {
|
void InterfaceSNES::setController(bool port, unsigned device) {
|
||||||
if(port == 0) config->snes.controllerPort1Device = device;
|
if(port == 0) config->snes.controllerPort1Device = device;
|
||||||
if(port == 1) config->snes.controllerPort2Device = device;
|
if(port == 1) config->snes.controllerPort2Device = device;
|
||||||
|
|
||||||
if(port == 0) switch(device) {
|
if(port == 0) switch(device) {
|
||||||
case 0: return connect(0, SNES::Input::Device::None);
|
case 0: return SNES::input.connect(0, SNES::Input::Device::None);
|
||||||
case 1: return connect(0, SNES::Input::Device::Joypad);
|
case 1: return SNES::input.connect(0, SNES::Input::Device::Joypad);
|
||||||
case 2: return connect(0, SNES::Input::Device::Multitap);
|
case 2: return SNES::input.connect(0, SNES::Input::Device::Multitap);
|
||||||
case 3: return connect(0, SNES::Input::Device::Mouse);
|
case 3: return SNES::input.connect(0, SNES::Input::Device::Mouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(port == 1) switch(device) {
|
if(port == 1) switch(device) {
|
||||||
case 0: return connect(1, SNES::Input::Device::None);
|
case 0: return SNES::input.connect(1, SNES::Input::Device::None);
|
||||||
case 1: return connect(1, SNES::Input::Device::Joypad);
|
case 1: return SNES::input.connect(1, SNES::Input::Device::Joypad);
|
||||||
case 2: return connect(1, SNES::Input::Device::Multitap);
|
case 2: return SNES::input.connect(1, SNES::Input::Device::Multitap);
|
||||||
case 3: return connect(1, SNES::Input::Device::Mouse);
|
case 3: return SNES::input.connect(1, SNES::Input::Device::Mouse);
|
||||||
case 4: return connect(1, SNES::Input::Device::SuperScope);
|
case 4: return SNES::input.connect(1, SNES::Input::Device::SuperScope);
|
||||||
case 5: return connect(1, SNES::Input::Device::Justifier);
|
case 5: return SNES::input.connect(1, SNES::Input::Device::Justifier);
|
||||||
case 6: return connect(1, SNES::Input::Device::Justifiers);
|
case 6: return SNES::input.connect(1, SNES::Input::Device::Justifiers);
|
||||||
case 7: return connect(1, SNES::Input::Device::Serial);
|
case 7: return SNES::input.connect(1, SNES::Input::Device::Serial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InterfaceSNES::cartridgeLoaded() {
|
||||||
|
return SNES::cartridge.loaded();
|
||||||
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::loadCartridge(const string &basename) {
|
bool InterfaceSNES::loadCartridge(const string &basename) {
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
unsigned size;
|
unsigned size;
|
||||||
|
@ -34,7 +43,10 @@ bool InterfaceSNES::loadCartridge(const string &basename) {
|
||||||
markup.readfile({ interface->baseName, ".bml" });
|
markup.readfile({ interface->baseName, ".bml" });
|
||||||
if(markup == "") markup = SnesCartridge(data, size).markup;
|
if(markup == "") markup = SnesCartridge(data, size).markup;
|
||||||
|
|
||||||
SNES::Interface::loadCartridge({ markup, data, size });
|
SNES::cartridge.rom.copy(data, size);
|
||||||
|
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, markup);
|
||||||
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data;
|
delete[] data;
|
||||||
|
|
||||||
loadMemory();
|
loadMemory();
|
||||||
|
@ -58,7 +70,11 @@ bool InterfaceSNES::loadSatellaviewSlottedCartridge(const string &basename, cons
|
||||||
markup.readfile({ interface->baseName, ".bml" });
|
markup.readfile({ interface->baseName, ".bml" });
|
||||||
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
||||||
|
|
||||||
SNES::Interface::loadSatellaviewSlottedCartridge({ markup, data[0], size[0] }, { "", data[1], size[1] });
|
SNES::cartridge.rom.copy(data[0], size[0]);
|
||||||
|
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
|
||||||
|
SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, markup);
|
||||||
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data[0];
|
delete[] data[0];
|
||||||
if(data[1]) delete[] data[1];
|
if(data[1]) delete[] data[1];
|
||||||
|
|
||||||
|
@ -83,7 +99,11 @@ bool InterfaceSNES::loadSatellaviewCartridge(const string &basename, const strin
|
||||||
markup.readfile({ interface->baseName, ".bml" });
|
markup.readfile({ interface->baseName, ".bml" });
|
||||||
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
||||||
|
|
||||||
SNES::Interface::loadSatellaviewCartridge({ markup, data[0], size[0] }, { "", data[1], size[1] });
|
SNES::cartridge.rom.copy(data[0], size[0]);
|
||||||
|
if(data[1]) SNES::bsxflash.memory.copy(data[1], size[1]);
|
||||||
|
SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, markup);
|
||||||
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data[0];
|
delete[] data[0];
|
||||||
if(data[1]) delete[] data[1];
|
if(data[1]) delete[] data[1];
|
||||||
|
|
||||||
|
@ -111,7 +131,12 @@ bool InterfaceSNES::loadSufamiTurboCartridge(const string &basename, const strin
|
||||||
markup.readfile({ interface->baseName, ".bml" });
|
markup.readfile({ interface->baseName, ".bml" });
|
||||||
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
if(markup == "") markup = SnesCartridge(data[0], size[0]).markup;
|
||||||
|
|
||||||
SNES::Interface::loadSufamiTurboCartridge({ markup, data[0], size[0] }, { "", data[1], size[1] }, { "", data[2], size[2] });
|
SNES::cartridge.rom.copy(data[0], size[0]);
|
||||||
|
if(data[1]) SNES::sufamiturbo.slotA.rom.copy(data[1], size[1]);
|
||||||
|
if(data[2]) SNES::sufamiturbo.slotB.rom.copy(data[1], size[1]);
|
||||||
|
SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, markup);
|
||||||
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data[0];
|
delete[] data[0];
|
||||||
if(data[1]) delete[] data[1];
|
if(data[1]) delete[] data[1];
|
||||||
if(data[2]) delete[] data[2];
|
if(data[2]) delete[] data[2];
|
||||||
|
@ -140,7 +165,12 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const stri
|
||||||
string gbMarkup;
|
string gbMarkup;
|
||||||
gbMarkup.readfile({ nall::basename(slotname), ".bml" });
|
gbMarkup.readfile({ nall::basename(slotname), ".bml" });
|
||||||
if(gbMarkup == "") gbMarkup = GameBoyCartridge(data[1], size[1]).markup;
|
if(gbMarkup == "") gbMarkup = GameBoyCartridge(data[1], size[1]).markup;
|
||||||
SNES::Interface::loadSuperGameBoyCartridge({ markup, data[0], size[0] }, { gbMarkup, data[1], size[1] });
|
|
||||||
|
SNES::cartridge.rom.copy(data[0], size[0]);
|
||||||
|
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, gbMarkup, data[1], size[1]);
|
||||||
|
SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, markup);
|
||||||
|
SNES::system.power();
|
||||||
|
|
||||||
delete[] data[0];
|
delete[] data[0];
|
||||||
if(data[1]) delete[] data[1];
|
if(data[1]) delete[] data[1];
|
||||||
|
|
||||||
|
@ -152,16 +182,28 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const stri
|
||||||
|
|
||||||
void InterfaceSNES::unloadCartridge() {
|
void InterfaceSNES::unloadCartridge() {
|
||||||
saveMemory();
|
saveMemory();
|
||||||
SNES::Interface::unloadCartridge();
|
SNES::cartridge.unload();
|
||||||
interface->baseName = "";
|
interface->baseName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InterfaceSNES::power() {
|
||||||
|
SNES::system.power();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceSNES::reset() {
|
||||||
|
SNES::system.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceSNES::run() {
|
||||||
|
SNES::system.run();
|
||||||
|
}
|
||||||
|
|
||||||
//slot[] array = Cartridge::Slot to slot# conversion:
|
//slot[] array = Cartridge::Slot to slot# conversion:
|
||||||
//{ Base, Bsx, SufamiTurbo, SufamiTurboA, SufamiTurboB, GameBoy }
|
//{ Base, Bsx, SufamiTurbo, SufamiTurboA, SufamiTurboB, GameBoy }
|
||||||
|
|
||||||
void InterfaceSNES::loadMemory() {
|
void InterfaceSNES::loadMemory() {
|
||||||
static unsigned slot[] = { 0, 0, 0, 1, 2, 1 };
|
static unsigned slot[] = { 0, 0, 0, 1, 2, 1 };
|
||||||
for(auto &memory : SNES::Interface::memory()) {
|
for(auto &memory : SNES::cartridge.nvram) {
|
||||||
if(memory.size == 0) continue;
|
if(memory.size == 0) continue;
|
||||||
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
|
@ -175,25 +217,51 @@ void InterfaceSNES::loadMemory() {
|
||||||
|
|
||||||
void InterfaceSNES::saveMemory() {
|
void InterfaceSNES::saveMemory() {
|
||||||
static unsigned slot[] = { 0, 0, 0, 1, 2, 1 };
|
static unsigned slot[] = { 0, 0, 0, 1, 2, 1 };
|
||||||
for(auto &memory : SNES::Interface::memory()) {
|
for(auto &memory : SNES::cartridge.nvram) {
|
||||||
if(memory.size == 0) continue;
|
if(memory.size == 0) continue;
|
||||||
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
||||||
file::write(filename, memory.data, memory.size);
|
file::write(filename, memory.data, memory.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::saveState(const string &filename) {
|
serializer InterfaceSNES::serialize() {
|
||||||
serializer s = serialize();
|
SNES::system.runtosave();
|
||||||
return file::write(filename, s.data(), s.size());
|
return SNES::system.serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::loadState(const string &filename) {
|
bool InterfaceSNES::unserialize(serializer &s) {
|
||||||
uint8_t *data;
|
return SNES::system.unserialize(s);
|
||||||
unsigned size;
|
}
|
||||||
if(file::read(filename, data, size) == false) return false;
|
|
||||||
serializer s(data, size);
|
void InterfaceSNES::setCheats(const lstring &list) {
|
||||||
delete[] data;
|
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
||||||
return unserialize(s);
|
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();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SNES::cheat.reset();
|
||||||
|
for(auto &code : list) {
|
||||||
|
lstring codelist;
|
||||||
|
codelist.split("+", code);
|
||||||
|
for(auto &part : codelist) {
|
||||||
|
unsigned addr, data;
|
||||||
|
if(SNES::Cheat::decode(part, addr, data)) {
|
||||||
|
SNES::cheat.append({ addr, data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SNES::cheat.synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
|
@ -1,6 +1,9 @@
|
||||||
struct InterfaceSNES : SNES::Interface {
|
struct InterfaceSNES : InterfaceCore, SNES::Interface {
|
||||||
|
void initialize();
|
||||||
|
|
||||||
void setController(bool port, unsigned device);
|
void setController(bool port, unsigned device);
|
||||||
|
|
||||||
|
bool cartridgeLoaded();
|
||||||
bool loadCartridge(const string &filename);
|
bool loadCartridge(const string &filename);
|
||||||
bool loadSatellaviewSlottedCartridge(const string &basename, const string &slotname);
|
bool loadSatellaviewSlottedCartridge(const string &basename, const string &slotname);
|
||||||
bool loadSatellaviewCartridge(const string &basename, const string &slotname);
|
bool loadSatellaviewCartridge(const string &basename, const string &slotname);
|
||||||
|
@ -8,11 +11,17 @@ struct InterfaceSNES : SNES::Interface {
|
||||||
bool loadSuperGameBoyCartridge(const string &basename, const string &slotname);
|
bool loadSuperGameBoyCartridge(const string &basename, const string &slotname);
|
||||||
void unloadCartridge();
|
void unloadCartridge();
|
||||||
|
|
||||||
|
void power();
|
||||||
|
void reset();
|
||||||
|
void run();
|
||||||
|
|
||||||
void loadMemory();
|
void loadMemory();
|
||||||
void saveMemory();
|
void saveMemory();
|
||||||
|
|
||||||
bool saveState(const string &filename);
|
serializer serialize();
|
||||||
bool loadState(const string &filename);
|
bool unserialize(serializer&);
|
||||||
|
|
||||||
|
void setCheats(const lstring &list = lstring{});
|
||||||
|
|
||||||
void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
|
void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
|
||||||
void audioSample(int16_t lsample, int16_t rsample);
|
void audioSample(int16_t lsample, int16_t rsample);
|
|
@ -27,7 +27,7 @@ void Application::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::Application(int argc, char **argv) {
|
Application::Application(int argc, char **argv) {
|
||||||
title = "bsnes v083.07";
|
title = "bsnes v083.08";
|
||||||
|
|
||||||
application = this;
|
application = this;
|
||||||
quit = false;
|
quit = false;
|
||||||
|
@ -88,7 +88,7 @@ Application::Application(int argc, char **argv) {
|
||||||
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
|
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
|
||||||
dspaudio.setBalance(0.0);
|
dspaudio.setBalance(0.0);
|
||||||
dspaudio.setResampler(DSP::ResampleEngine::Sinc);
|
dspaudio.setResampler(DSP::ResampleEngine::Sinc);
|
||||||
dspaudio.setResamplerFrequency(48000.0);
|
dspaudio.setResamplerFrequency(config->audio.frequency);
|
||||||
|
|
||||||
input.driver(config->input.driver);
|
input.driver(config->input.driver);
|
||||||
input.set(Input::Handle, mainWindow->viewport.handle());
|
input.set(Input::Handle, mainWindow->viewport.handle());
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
VideoSettings *videoSettings = 0;
|
VideoSettings *videoSettings = 0;
|
||||||
|
|
||||||
VideoSlider::VideoSlider() {
|
VideoSlider::VideoSlider() {
|
||||||
slider.setLength(201);
|
|
||||||
|
|
||||||
append(name, 75, 0);
|
append(name, 75, 0);
|
||||||
append(value, 75, 0);
|
append(value, 75, 0);
|
||||||
append(slider, ~0, 0);
|
append(slider, ~0, 0);
|
||||||
|
@ -14,9 +12,11 @@ VideoSettings::VideoSettings() {
|
||||||
colorAdjustment.setFont(application->boldFont);
|
colorAdjustment.setFont(application->boldFont);
|
||||||
colorAdjustment.setText("Color adjustment:");
|
colorAdjustment.setText("Color adjustment:");
|
||||||
brightness.name.setText("Brightness:");
|
brightness.name.setText("Brightness:");
|
||||||
|
brightness.slider.setLength(201);
|
||||||
contrast.name.setText("Contrast:");
|
contrast.name.setText("Contrast:");
|
||||||
|
contrast.slider.setLength(201);
|
||||||
gamma.name.setText("Gamma:");
|
gamma.name.setText("Gamma:");
|
||||||
gammaRamp.setText("Enable gamma ramp simulation");
|
gamma.slider.setLength(101);
|
||||||
overscanAdjustment.setFont(application->boldFont);
|
overscanAdjustment.setFont(application->boldFont);
|
||||||
overscanAdjustment.setText("Overscan mask:");
|
overscanAdjustment.setText("Overscan mask:");
|
||||||
overscanHorizontal.name.setText("Horizontal:");
|
overscanHorizontal.name.setText("Horizontal:");
|
||||||
|
@ -31,13 +31,10 @@ VideoSettings::VideoSettings() {
|
||||||
RadioBox::group(fullScreen[0], fullScreen[1], fullScreen[2]);
|
RadioBox::group(fullScreen[0], fullScreen[1], fullScreen[2]);
|
||||||
|
|
||||||
append(title, ~0, 0, 5);
|
append(title, ~0, 0, 5);
|
||||||
#if 0
|
|
||||||
append(colorAdjustment, ~0, 0);
|
append(colorAdjustment, ~0, 0);
|
||||||
append(brightness, ~0, 0);
|
append(brightness, ~0, 0);
|
||||||
append(contrast, ~0, 0);
|
append(contrast, ~0, 0);
|
||||||
append(gamma, ~0, 0);
|
append(gamma, ~0, 0, 5);
|
||||||
append(gammaRamp, ~0, 0, 5);
|
|
||||||
#endif
|
|
||||||
append(overscanAdjustment, ~0, 0);
|
append(overscanAdjustment, ~0, 0);
|
||||||
append(overscanHorizontal, ~0, 0);
|
append(overscanHorizontal, ~0, 0);
|
||||||
append(overscanVertical, ~0, 0, 5);
|
append(overscanVertical, ~0, 0, 5);
|
||||||
|
@ -50,14 +47,13 @@ VideoSettings::VideoSettings() {
|
||||||
brightness.slider.setPosition(config->video.brightness);
|
brightness.slider.setPosition(config->video.brightness);
|
||||||
contrast.slider.setPosition(config->video.contrast);
|
contrast.slider.setPosition(config->video.contrast);
|
||||||
gamma.slider.setPosition(config->video.gamma);
|
gamma.slider.setPosition(config->video.gamma);
|
||||||
gammaRamp.setChecked(config->video.gammaRamp);
|
|
||||||
overscanHorizontal.slider.setPosition(config->video.maskOverscanHorizontal);
|
overscanHorizontal.slider.setPosition(config->video.maskOverscanHorizontal);
|
||||||
overscanVertical.slider.setPosition(config->video.maskOverscanVertical);
|
overscanVertical.slider.setPosition(config->video.maskOverscanVertical);
|
||||||
fullScreen[config->video.fullScreenMode].setChecked();
|
fullScreen[config->video.fullScreenMode].setChecked();
|
||||||
|
|
||||||
synchronize();
|
synchronize();
|
||||||
|
|
||||||
brightness.slider.onChange = contrast.slider.onChange = gamma.slider.onChange = gammaRamp.onTick =
|
brightness.slider.onChange = contrast.slider.onChange = gamma.slider.onChange =
|
||||||
overscanHorizontal.slider.onChange = overscanVertical.slider.onChange =
|
overscanHorizontal.slider.onChange = overscanVertical.slider.onChange =
|
||||||
fullScreen[0].onTick = fullScreen[1].onTick = fullScreen[2].onTick =
|
fullScreen[0].onTick = fullScreen[1].onTick = fullScreen[2].onTick =
|
||||||
{ &VideoSettings::synchronize, this };
|
{ &VideoSettings::synchronize, this };
|
||||||
|
@ -67,7 +63,6 @@ void VideoSettings::synchronize() {
|
||||||
config->video.brightness = brightness.slider.position();
|
config->video.brightness = brightness.slider.position();
|
||||||
config->video.contrast = contrast.slider.position();
|
config->video.contrast = contrast.slider.position();
|
||||||
config->video.gamma = gamma.slider.position();
|
config->video.gamma = gamma.slider.position();
|
||||||
config->video.gammaRamp = gammaRamp.checked();
|
|
||||||
config->video.maskOverscanHorizontal = overscanHorizontal.slider.position();
|
config->video.maskOverscanHorizontal = overscanHorizontal.slider.position();
|
||||||
config->video.maskOverscanVertical = overscanVertical.slider.position();
|
config->video.maskOverscanVertical = overscanVertical.slider.position();
|
||||||
if(fullScreen[0].checked()) config->video.fullScreenMode = 0;
|
if(fullScreen[0].checked()) config->video.fullScreenMode = 0;
|
||||||
|
@ -76,7 +71,7 @@ void VideoSettings::synchronize() {
|
||||||
|
|
||||||
brightness.value.setText({ config->video.brightness, "%" });
|
brightness.value.setText({ config->video.brightness, "%" });
|
||||||
contrast.value.setText({ config->video.contrast, "%" });
|
contrast.value.setText({ config->video.contrast, "%" });
|
||||||
gamma.value.setText({ config->video.gamma, "%" });
|
gamma.value.setText({ 100 + config->video.gamma, "%" });
|
||||||
|
|
||||||
overscanHorizontal.value.setText({ config->video.maskOverscanHorizontal, "px" });
|
overscanHorizontal.value.setText({ config->video.maskOverscanHorizontal, "px" });
|
||||||
overscanVertical.value.setText({ config->video.maskOverscanVertical, "px" });
|
overscanVertical.value.setText({ config->video.maskOverscanVertical, "px" });
|
||||||
|
|
|
@ -12,7 +12,6 @@ struct VideoSettings : SettingsLayout {
|
||||||
VideoSlider brightness;
|
VideoSlider brightness;
|
||||||
VideoSlider contrast;
|
VideoSlider contrast;
|
||||||
VideoSlider gamma;
|
VideoSlider gamma;
|
||||||
CheckBox gammaRamp;
|
|
||||||
Label overscanAdjustment;
|
Label overscanAdjustment;
|
||||||
VideoSlider overscanHorizontal;
|
VideoSlider overscanHorizontal;
|
||||||
VideoSlider overscanVertical;
|
VideoSlider overscanVertical;
|
||||||
|
|
Loading…
Reference in New Issue