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 {
|
||||
|
||||
Interface *interface = 0;
|
||||
Interface *interface = nullptr;
|
||||
|
||||
void Interface::lcdScanline() {
|
||||
}
|
||||
|
@ -20,68 +20,6 @@ bool Interface::inputPoll(unsigned id) {
|
|||
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) {
|
||||
print(text, "\n");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
class Interface {
|
||||
public:
|
||||
struct Interface {
|
||||
virtual void lcdScanline();
|
||||
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 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);
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace NES {
|
||||
|
||||
Interface *interface = 0;
|
||||
Interface *interface = nullptr;
|
||||
|
||||
void Interface::videoRefresh(const uint16_t *data) {
|
||||
}
|
||||
|
@ -14,74 +14,6 @@ int16_t Interface::inputPoll(bool port, unsigned device, unsigned id) {
|
|||
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) {
|
||||
print(text, "\n");
|
||||
}
|
||||
|
|
|
@ -3,30 +3,6 @@ struct Interface {
|
|||
virtual void audioSample(int16_t sample);
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
|
@ -69,8 +69,9 @@ void ICD2::reset() {
|
|||
joyp14lock = 0;
|
||||
pulselock = true;
|
||||
|
||||
GameBoy::Interface::initialize(this);
|
||||
GameBoy::Interface::power();
|
||||
GameBoy::interface = this;
|
||||
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() {
|
||||
if(mmio.sa1_nmi && !mmio.sa1_nmicl) {
|
||||
status.interrupt_pending = true;
|
||||
regs.vector = mmio.cnv;
|
||||
regs.vector = mmio.cnv;
|
||||
mmio.sa1_nmifl = true;
|
||||
mmio.sa1_nmicl = 1;
|
||||
regs.wai = false;
|
||||
} else if(!regs.p.i) {
|
||||
if(mmio.timer_irqen && !mmio.timer_irqcl) {
|
||||
status.interrupt_pending = true;
|
||||
regs.vector = mmio.civ;
|
||||
regs.vector = mmio.civ;
|
||||
mmio.timer_irqfl = true;
|
||||
regs.wai = false;
|
||||
} else if(mmio.dma_irqen && !mmio.dma_irqcl) {
|
||||
status.interrupt_pending = true;
|
||||
regs.vector = mmio.civ;
|
||||
regs.vector = mmio.civ;
|
||||
mmio.dma_irqfl = true;
|
||||
regs.wai = false;
|
||||
} else if(mmio.sa1_irq && !mmio.sa1_irqcl) {
|
||||
status.interrupt_pending = true;
|
||||
regs.vector = mmio.civ;
|
||||
regs.vector = mmio.civ;
|
||||
mmio.sa1_irqfl = true;
|
||||
regs.wai = false;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ public:
|
|||
static void Enter();
|
||||
void enter();
|
||||
void tick();
|
||||
void op_irq();
|
||||
|
||||
alwaysinline void trigger_irq();
|
||||
alwaysinline void last_cycle();
|
||||
|
|
|
@ -12,14 +12,13 @@ struct CPUcore {
|
|||
virtual void op_write(uint32_t addr, uint8_t data) = 0;
|
||||
virtual void last_cycle() = 0;
|
||||
virtual bool interrupt_pending() = 0;
|
||||
virtual void op_irq();
|
||||
|
||||
void op_io_irq();
|
||||
void op_io_cond2();
|
||||
void op_io_cond4(uint16 x, uint16 y);
|
||||
void op_io_cond6(uint16 addr);
|
||||
|
||||
void op_irq();
|
||||
|
||||
void op_adc_b();
|
||||
void op_adc_w();
|
||||
void op_and_b();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace SNES {
|
||||
|
||||
Interface *interface = 0;
|
||||
Interface *interface = nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
print(text, "\n");
|
||||
}
|
||||
|
|
|
@ -3,36 +3,6 @@ struct Interface {
|
|||
virtual void audioSample(int16_t lsample, int16_t rsample);
|
||||
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 void message(const string &text);
|
||||
};
|
||||
|
|
|
@ -14,8 +14,8 @@ Config::Config() {
|
|||
|
||||
attach(video.brightness = 100, "Video::Brightness");
|
||||
attach(video.contrast = 100, "Video::Contrast");
|
||||
attach(video.gamma = 100, "Video::Gamma");
|
||||
attach(video.gammaRamp = true, "Video::GammaRamp");
|
||||
attach(video.gamma = 50, "Video::Gamma");
|
||||
|
||||
attach(video.fullScreenMode = 0, "Video::FullScreenMode");
|
||||
|
||||
attach(video.startFullScreen = false, "Video::StartFullScreen");
|
||||
|
|
|
@ -13,7 +13,7 @@ struct Config : public configuration {
|
|||
unsigned brightness;
|
||||
unsigned contrast;
|
||||
unsigned gamma;
|
||||
bool gammaRamp;
|
||||
|
||||
unsigned fullScreenMode;
|
||||
|
||||
bool startFullScreen;
|
||||
|
|
|
@ -28,7 +28,7 @@ void DipSwitches::load() {
|
|||
if(interface->mode() != Interface::Mode::SNES || SNES::cartridge.has_nss_dip() == false) return;
|
||||
application->pause = true;
|
||||
|
||||
auto info = interface->snes.information().nss;
|
||||
auto info = SNES::cartridge.information.nss;
|
||||
unsigned count = info.setting.size();
|
||||
|
||||
for(unsigned n = 0; n < min(8, count); n++) {
|
||||
|
@ -55,7 +55,7 @@ void DipSwitches::load() {
|
|||
}
|
||||
|
||||
void DipSwitches::accept() {
|
||||
auto info = interface->snes.information().nss;
|
||||
auto info = SNES::cartridge.information.nss;
|
||||
unsigned count = info.setting.size();
|
||||
|
||||
unsigned result = 0x0000;
|
||||
|
|
|
@ -4,8 +4,8 @@ int16_t GameBoyController::poll(unsigned n) {
|
|||
case 1: return down.poll() & !up.poll();
|
||||
case 2: return left.poll() & !right.poll();
|
||||
case 3: return right.poll() & !left.poll();
|
||||
case 4: return b.poll();
|
||||
case 5: return a.poll();
|
||||
case 4: return b.poll() | bTurbo.poll();
|
||||
case 5: return a.poll() | aTurbo.poll();
|
||||
case 6: return select.poll();
|
||||
case 7: return start.poll();
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ GameBoyController::GameBoyController() {
|
|||
a.name = "A";
|
||||
select.name = "Select";
|
||||
start.name = "Start";
|
||||
bTurbo.name = "Turbo B";
|
||||
aTurbo.name = "Turbo A";
|
||||
|
||||
up.mapping = "KB0::Up";
|
||||
down.mapping = "KB0::Down";
|
||||
|
@ -35,6 +37,7 @@ GameBoyController::GameBoyController() {
|
|||
|
||||
append(up); append(down); append(left); append(right);
|
||||
append(b); append(a); append(select); append(start);
|
||||
append(bTurbo); append(aTurbo);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
struct GameBoyController : TertiaryInput {
|
||||
DigitalInput up, down, left, right;
|
||||
DigitalInput b, a, select, start;
|
||||
TurboInput bTurbo, aTurbo;
|
||||
|
||||
int16_t poll(unsigned n);
|
||||
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) {
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
operator[](n).attach(primaryName, secondaryName, name);
|
||||
|
|
|
@ -20,6 +20,13 @@ struct DigitalInput : AbstractInput {
|
|||
int16_t poll();
|
||||
};
|
||||
|
||||
struct TurboInput : DigitalInput {
|
||||
unsigned phase;
|
||||
|
||||
int16_t poll();
|
||||
TurboInput();
|
||||
};
|
||||
|
||||
struct TertiaryInput : reference_array<AbstractInput&> {
|
||||
string name;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
int16_t NesGamepad::poll(unsigned n) {
|
||||
switch(n) {
|
||||
case 0: return a.poll();
|
||||
case 1: return b.poll();
|
||||
case 0: return a.poll() | aTurbo.poll();
|
||||
case 1: return b.poll() | bTurbo.poll();
|
||||
case 2: return select.poll();
|
||||
case 3: return start.poll();
|
||||
case 4: return up.poll() & !down.poll();
|
||||
|
@ -23,6 +23,8 @@ NesGamepad::NesGamepad() {
|
|||
a.name = "A";
|
||||
select.name = "Select";
|
||||
start.name = "Start";
|
||||
bTurbo.name = "Turbo B";
|
||||
aTurbo.name = "Turbo A";
|
||||
|
||||
up.mapping = "KB0::Up";
|
||||
down.mapping = "KB0::Down";
|
||||
|
@ -35,6 +37,7 @@ NesGamepad::NesGamepad() {
|
|||
|
||||
append(up); append(down); append(left); append(right);
|
||||
append(b); append(a); append(select); append(start);
|
||||
append(bTurbo); append(aTurbo);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
struct NesGamepad : TertiaryInput {
|
||||
DigitalInput up, down, left, right;
|
||||
DigitalInput b, a, select, start;
|
||||
TurboInput bTurbo, aTurbo;
|
||||
|
||||
int16_t poll(unsigned n);
|
||||
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::Left: return left.poll() & !right.poll();
|
||||
case SNES::Input::JoypadID::Right: return right.poll() & !left.poll();
|
||||
case SNES::Input::JoypadID::B: return b.poll();
|
||||
case SNES::Input::JoypadID::A: return a.poll();
|
||||
case SNES::Input::JoypadID::Y: return y.poll();
|
||||
case SNES::Input::JoypadID::X: return x.poll();
|
||||
case SNES::Input::JoypadID::B: return b.poll() | bTurbo.poll();
|
||||
case SNES::Input::JoypadID::A: return a.poll() | aTurbo.poll();
|
||||
case SNES::Input::JoypadID::Y: return y.poll() | yTurbo.poll();
|
||||
case SNES::Input::JoypadID::X: return x.poll() | xTurbo.poll();
|
||||
case SNES::Input::JoypadID::L: return l.poll();
|
||||
case SNES::Input::JoypadID::R: return r.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";
|
||||
b.name = "B", a.name = "A", y.name = "Y", x.name = "X";
|
||||
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) {
|
||||
up.mapping = "KB0::Up";
|
||||
|
@ -41,6 +42,7 @@ SnesGamepad::SnesGamepad(const string &name, bool defaultBindings) {
|
|||
append(up); append(down); append(left); append(right);
|
||||
append(b); append(a); append(y); append(x);
|
||||
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 b, a, y, x;
|
||||
DigitalInput l, r, select, start;
|
||||
TurboInput bTurbo, aTurbo, yTurbo, xTurbo;
|
||||
|
||||
int16_t poll(unsigned n);
|
||||
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 "palette.cpp"
|
||||
#include "nes.cpp"
|
||||
#include "snes.cpp"
|
||||
#include "gameboy.cpp"
|
||||
Interface *interface = 0;
|
||||
#include "nes/nes.cpp"
|
||||
#include "snes/snes.cpp"
|
||||
#include "gameboy/gameboy.cpp"
|
||||
Interface *interface = nullptr;
|
||||
|
||||
Filter filter;
|
||||
|
||||
|
@ -63,6 +63,13 @@ bool Interface::cartridgeLoaded() {
|
|||
|
||||
void Interface::loadCartridge(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();
|
||||
cheatEditor->load({ baseName, ".cht" });
|
||||
stateManager->load({ baseName, ".bsa" }, 0u);
|
||||
|
@ -98,69 +105,50 @@ void Interface::unloadCartridge() {
|
|||
}
|
||||
|
||||
void Interface::power() {
|
||||
switch(mode()) {
|
||||
case Mode::NES: nes.power(); break;
|
||||
case Mode::SNES: snes.power(); break;
|
||||
case Mode::GameBoy: gameBoy.power(); break;
|
||||
}
|
||||
if(core == nullptr) return;
|
||||
core->power();
|
||||
utility->showMessage("System power was cycled");
|
||||
}
|
||||
|
||||
void Interface::reset() {
|
||||
switch(mode()) {
|
||||
case Mode::NES: nes.reset(); break;
|
||||
case Mode::SNES: snes.reset(); break;
|
||||
case Mode::GameBoy: gameBoy.power(); break; //Game Boy lacks reset button
|
||||
}
|
||||
if(core == nullptr) return;
|
||||
core->reset();
|
||||
utility->showMessage("System was reset");
|
||||
}
|
||||
|
||||
void Interface::run() {
|
||||
switch(mode()) {
|
||||
case Mode::NES: return nes.run();
|
||||
case Mode::SNES: return snes.run();
|
||||
case Mode::GameBoy: return gameBoy.run();
|
||||
}
|
||||
if(core == nullptr) return;
|
||||
core->run();
|
||||
}
|
||||
|
||||
serializer Interface::serialize() {
|
||||
switch(mode()) {
|
||||
case Mode::NES: return nes.serialize();
|
||||
case Mode::SNES: return snes.serialize();
|
||||
case Mode::GameBoy: return gameBoy.serialize();
|
||||
}
|
||||
return serializer();
|
||||
if(core == nullptr) return serializer();
|
||||
return core->serialize();
|
||||
}
|
||||
|
||||
bool Interface::unserialize(serializer &s) {
|
||||
switch(mode()) {
|
||||
case Mode::NES: return nes.unserialize(s);
|
||||
case Mode::SNES: return snes.unserialize(s);
|
||||
case Mode::GameBoy: return gameBoy.unserialize(s);
|
||||
}
|
||||
return false;
|
||||
if(core == nullptr) return false;
|
||||
return core->unserialize(s);
|
||||
}
|
||||
|
||||
bool Interface::saveState(unsigned slot) {
|
||||
string filename = { baseName, "-", slot, ".bst" };
|
||||
bool result = false;
|
||||
switch(mode()) {
|
||||
case Mode::NES: result = nes.saveState(filename); break;
|
||||
case Mode::SNES: result = snes.saveState(filename); break;
|
||||
case Mode::GameBoy: result = gameBoy.saveState(filename); break;
|
||||
}
|
||||
serializer s = serialize();
|
||||
bool result = file::write(filename, s.data(), s.size());
|
||||
utility->showMessage(result == true ? string{ "Saved state ", slot } : "Failed to save state");
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Interface::loadState(unsigned slot) {
|
||||
string filename = { baseName, "-", slot, ".bst" };
|
||||
bool result = false;
|
||||
switch(mode()) {
|
||||
case Mode::NES: result = nes.loadState(filename); break;
|
||||
case Mode::SNES: result = snes.loadState(filename); break;
|
||||
case Mode::GameBoy: result = gameBoy.loadState(filename); break;
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
if(file::read(filename, data, size) == false) {
|
||||
utility->showMessage(string{ "Slot ", slot, " save file not found" });
|
||||
return false;
|
||||
}
|
||||
serializer s(data, size);
|
||||
bool result = unserialize(s);
|
||||
utility->showMessage(result == true ? string{ "Loaded state ", slot } : "Failed to load state");
|
||||
return result;
|
||||
}
|
||||
|
@ -182,12 +170,12 @@ string Interface::sha256() {
|
|||
return "{None}";
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
Interface::Interface() : core(nullptr) {
|
||||
mode = Mode::None;
|
||||
palette.update();
|
||||
nes.initialize(&nes);
|
||||
snes.initialize(&snes);
|
||||
gameBoy.initialize(&gameBoy);
|
||||
nes.initialize();
|
||||
snes.initialize();
|
||||
gameBoy.initialize();
|
||||
}
|
||||
|
||||
//internal
|
||||
|
@ -234,7 +222,8 @@ void Interface::videoRefresh(const uint32_t *input, unsigned inputPitch, unsigne
|
|||
const uint32_t *sp = input + y * inputPitch;
|
||||
uint32_t *dp = output + y * outputPitch;
|
||||
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 "nes.hpp"
|
||||
#include "snes.hpp"
|
||||
#include "gameboy.hpp"
|
||||
struct InterfaceCore {
|
||||
virtual void power() = 0;
|
||||
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 {
|
||||
function<void (unsigned&, unsigned&)> dl_size;
|
||||
|
@ -52,6 +61,7 @@ struct Interface : property<Interface> {
|
|||
string baseName; // = "/path/to/cartridge" (no extension)
|
||||
lstring slotName;
|
||||
|
||||
InterfaceCore *core;
|
||||
InterfaceNES nes;
|
||||
InterfaceSNES snes;
|
||||
InterfaceGameBoy gameBoy;
|
||||
|
|
|
@ -1,18 +1,27 @@
|
|||
void InterfaceNES::initialize() {
|
||||
NES::interface = this;
|
||||
NES::system.init();
|
||||
}
|
||||
|
||||
void InterfaceNES::setController(bool port, unsigned device) {
|
||||
if(port == 0) config->nes.controllerPort1Device = device;
|
||||
if(port == 1) config->nes.controllerPort2Device = device;
|
||||
|
||||
if(port == 0) switch(device) {
|
||||
case 0: return connect(0, NES::Input::Device::None);
|
||||
case 1: return connect(0, NES::Input::Device::Joypad);
|
||||
case 0: return NES::input.connect(0, NES::Input::Device::None);
|
||||
case 1: return NES::input.connect(0, NES::Input::Device::Joypad);
|
||||
}
|
||||
|
||||
if(port == 1) switch(device) {
|
||||
case 0: return connect(1, NES::Input::Device::None);
|
||||
case 1: return connect(1, NES::Input::Device::Joypad);
|
||||
case 0: return NES::input.connect(1, NES::Input::Device::None);
|
||||
case 1: return NES::input.connect(1, NES::Input::Device::Joypad);
|
||||
}
|
||||
}
|
||||
|
||||
bool InterfaceNES::cartridgeLoaded() {
|
||||
return NES::cartridge.loaded();
|
||||
}
|
||||
|
||||
bool InterfaceNES::loadCartridge(const string &filename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
@ -24,15 +33,14 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
|||
string markup;
|
||||
markup.readfile({ interface->baseName, ".bml" });
|
||||
|
||||
NES::Interface::loadCartridge(markup, data, size);
|
||||
NES::cartridge.load(markup, data, size);
|
||||
NES::system.power();
|
||||
delete[] data;
|
||||
|
||||
if(NES::Interface::memorySize(NES::Interface::Memory::RAM) > 0) {
|
||||
if(NES::cartridge.ram_size()) {
|
||||
filemap fp;
|
||||
if(fp.open(string{ interface->baseName, ".sav" }, filemap::mode::read)) {
|
||||
memcpy(NES::Interface::memoryData(NES::Interface::Memory::RAM), fp.data(),
|
||||
min(NES::Interface::memorySize(NES::Interface::Memory::RAM), fp.size())
|
||||
);
|
||||
memcpy(NES::cartridge.ram_data(), fp.data(), min(NES::cartridge.ram_size(), fp.size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,31 +50,53 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
|||
}
|
||||
|
||||
void InterfaceNES::unloadCartridge() {
|
||||
if(NES::Interface::memorySize(NES::Interface::Memory::RAM) > 0) {
|
||||
file::write({ interface->baseName, ".sav" },
|
||||
NES::Interface::memoryData(NES::Interface::Memory::RAM),
|
||||
NES::Interface::memorySize(NES::Interface::Memory::RAM)
|
||||
);
|
||||
if(NES::cartridge.ram_size()) {
|
||||
file::write({ interface->baseName, ".sav" }, NES::cartridge.ram_data(), NES::cartridge.ram_size());
|
||||
}
|
||||
|
||||
NES::Interface::unloadCartridge();
|
||||
NES::cartridge.unload();
|
||||
interface->baseName = "";
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
bool InterfaceNES::saveState(const string &filename) {
|
||||
serializer s = serialize();
|
||||
return file::write(filename, s.data(), s.size());
|
||||
void InterfaceNES::power() {
|
||||
NES::system.power();
|
||||
}
|
||||
|
||||
bool InterfaceNES::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 InterfaceNES::reset() {
|
||||
NES::system.reset();
|
||||
}
|
||||
|
||||
void InterfaceNES::run() {
|
||||
NES::system.run();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
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);
|
||||
|
||||
bool cartridgeLoaded();
|
||||
bool loadCartridge(const string &filename);
|
||||
void unloadCartridge();
|
||||
|
||||
bool saveState(const string &filename);
|
||||
bool loadState(const string &filename);
|
||||
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 sample);
|
|
@ -1,15 +1,17 @@
|
|||
Palette palette;
|
||||
|
||||
uint32_t Palette::operator[](unsigned n) {
|
||||
uint8_t Palette::operator[](uint8_t n) {
|
||||
return color[n];
|
||||
}
|
||||
|
||||
/* 5-bit -> 8-bit
|
||||
const uint8_t Palette::gammaRamp[32] = {
|
||||
0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
|
||||
0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
|
||||
0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
|
||||
0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
|
||||
};
|
||||
*/
|
||||
|
||||
uint8_t Palette::contrastAdjust(uint8_t input) {
|
||||
signed contrast = config->video.contrast - 100;
|
||||
|
@ -29,39 +31,17 @@ uint8_t Palette::gammaAdjust(uint8_t input) {
|
|||
}
|
||||
|
||||
void Palette::update() {
|
||||
for(unsigned i = 0; i < 32768; i++) {
|
||||
unsigned r = (i >> 10) & 31;
|
||||
unsigned g = (i >> 5) & 31;
|
||||
unsigned b = (i >> 0) & 31;
|
||||
double exponent = 1.0 + (double)config->video.gamma * 0.01;
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
unsigned result = (n < 128 ? 127 * pow(((double)n / 127), exponent) : n);
|
||||
color[n] = result;
|
||||
}
|
||||
|
||||
r = (r << 3) | (r >> 2);
|
||||
g = (g << 3) | (g >> 2);
|
||||
b = (b << 3) | (b >> 2);
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
color[n] = contrastAdjust(color[n]);
|
||||
}
|
||||
|
||||
if(config->video.gammaRamp) {
|
||||
r = gammaRamp[r >> 3];
|
||||
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);
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
color[n] = brightnessAdjust(color[n]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
struct Palette {
|
||||
alwaysinline uint32_t operator[](unsigned n);
|
||||
alwaysinline uint8_t operator[](uint8_t color);
|
||||
|
||||
uint8_t contrastAdjust(uint8_t);
|
||||
uint8_t brightnessAdjust(uint8_t);
|
||||
|
@ -7,8 +7,7 @@ struct Palette {
|
|||
void update();
|
||||
|
||||
private:
|
||||
static const uint8_t gammaRamp[32];
|
||||
uint32_t color[32768];
|
||||
uint32_t color[256];
|
||||
};
|
||||
|
||||
extern Palette palette;
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
void InterfaceSNES::initialize() {
|
||||
SNES::interface = this;
|
||||
SNES::system.init();
|
||||
}
|
||||
|
||||
void InterfaceSNES::setController(bool port, unsigned device) {
|
||||
if(port == 0) config->snes.controllerPort1Device = device;
|
||||
if(port == 1) config->snes.controllerPort2Device = device;
|
||||
|
||||
if(port == 0) switch(device) {
|
||||
case 0: return connect(0, SNES::Input::Device::None);
|
||||
case 1: return connect(0, SNES::Input::Device::Joypad);
|
||||
case 2: return connect(0, SNES::Input::Device::Multitap);
|
||||
case 3: return connect(0, SNES::Input::Device::Mouse);
|
||||
case 0: return SNES::input.connect(0, SNES::Input::Device::None);
|
||||
case 1: return SNES::input.connect(0, SNES::Input::Device::Joypad);
|
||||
case 2: return SNES::input.connect(0, SNES::Input::Device::Multitap);
|
||||
case 3: return SNES::input.connect(0, SNES::Input::Device::Mouse);
|
||||
}
|
||||
|
||||
if(port == 1) switch(device) {
|
||||
case 0: return connect(1, SNES::Input::Device::None);
|
||||
case 1: return connect(1, SNES::Input::Device::Joypad);
|
||||
case 2: return connect(1, SNES::Input::Device::Multitap);
|
||||
case 3: return connect(1, SNES::Input::Device::Mouse);
|
||||
case 4: return connect(1, SNES::Input::Device::SuperScope);
|
||||
case 5: return connect(1, SNES::Input::Device::Justifier);
|
||||
case 6: return connect(1, SNES::Input::Device::Justifiers);
|
||||
case 7: return connect(1, SNES::Input::Device::Serial);
|
||||
case 0: return SNES::input.connect(1, SNES::Input::Device::None);
|
||||
case 1: return SNES::input.connect(1, SNES::Input::Device::Joypad);
|
||||
case 2: return SNES::input.connect(1, SNES::Input::Device::Multitap);
|
||||
case 3: return SNES::input.connect(1, SNES::Input::Device::Mouse);
|
||||
case 4: return SNES::input.connect(1, SNES::Input::Device::SuperScope);
|
||||
case 5: return SNES::input.connect(1, SNES::Input::Device::Justifier);
|
||||
case 6: return SNES::input.connect(1, SNES::Input::Device::Justifiers);
|
||||
case 7: return SNES::input.connect(1, SNES::Input::Device::Serial);
|
||||
}
|
||||
}
|
||||
|
||||
bool InterfaceSNES::cartridgeLoaded() {
|
||||
return SNES::cartridge.loaded();
|
||||
}
|
||||
|
||||
bool InterfaceSNES::loadCartridge(const string &basename) {
|
||||
uint8_t *data;
|
||||
unsigned size;
|
||||
|
@ -34,7 +43,10 @@ bool InterfaceSNES::loadCartridge(const string &basename) {
|
|||
markup.readfile({ interface->baseName, ".bml" });
|
||||
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;
|
||||
|
||||
loadMemory();
|
||||
|
@ -58,7 +70,11 @@ bool InterfaceSNES::loadSatellaviewSlottedCartridge(const string &basename, cons
|
|||
markup.readfile({ interface->baseName, ".bml" });
|
||||
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];
|
||||
if(data[1]) delete[] data[1];
|
||||
|
||||
|
@ -83,7 +99,11 @@ bool InterfaceSNES::loadSatellaviewCartridge(const string &basename, const strin
|
|||
markup.readfile({ interface->baseName, ".bml" });
|
||||
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];
|
||||
if(data[1]) delete[] data[1];
|
||||
|
||||
|
@ -111,7 +131,12 @@ bool InterfaceSNES::loadSufamiTurboCartridge(const string &basename, const strin
|
|||
markup.readfile({ interface->baseName, ".bml" });
|
||||
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];
|
||||
if(data[1]) delete[] data[1];
|
||||
if(data[2]) delete[] data[2];
|
||||
|
@ -140,7 +165,12 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const stri
|
|||
string gbMarkup;
|
||||
gbMarkup.readfile({ nall::basename(slotname), ".bml" });
|
||||
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];
|
||||
if(data[1]) delete[] data[1];
|
||||
|
||||
|
@ -152,16 +182,28 @@ bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const stri
|
|||
|
||||
void InterfaceSNES::unloadCartridge() {
|
||||
saveMemory();
|
||||
SNES::Interface::unloadCartridge();
|
||||
SNES::cartridge.unload();
|
||||
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:
|
||||
//{ Base, Bsx, SufamiTurbo, SufamiTurboA, SufamiTurboB, GameBoy }
|
||||
|
||||
void InterfaceSNES::loadMemory() {
|
||||
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;
|
||||
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
||||
uint8_t *data;
|
||||
|
@ -175,25 +217,51 @@ void InterfaceSNES::loadMemory() {
|
|||
|
||||
void InterfaceSNES::saveMemory() {
|
||||
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;
|
||||
string filename = { interface->slotName[slot[(unsigned)memory.slot]], memory.id };
|
||||
file::write(filename, memory.data, memory.size);
|
||||
}
|
||||
}
|
||||
|
||||
bool InterfaceSNES::saveState(const string &filename) {
|
||||
serializer s = serialize();
|
||||
return file::write(filename, s.data(), s.size());
|
||||
serializer InterfaceSNES::serialize() {
|
||||
SNES::system.runtosave();
|
||||
return SNES::system.serialize();
|
||||
}
|
||||
|
||||
bool InterfaceSNES::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);
|
||||
bool InterfaceSNES::unserialize(serializer &s) {
|
||||
return SNES::system.unserialize(s);
|
||||
}
|
||||
|
||||
void InterfaceSNES::setCheats(const lstring &list) {
|
||||
if(SNES::cartridge.mode() == SNES::Cartridge::Mode::SuperGameBoy) {
|
||||
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);
|
||||
|
||||
bool cartridgeLoaded();
|
||||
bool loadCartridge(const string &filename);
|
||||
bool loadSatellaviewSlottedCartridge(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);
|
||||
void unloadCartridge();
|
||||
|
||||
void power();
|
||||
void reset();
|
||||
void run();
|
||||
|
||||
void loadMemory();
|
||||
void saveMemory();
|
||||
|
||||
bool saveState(const string &filename);
|
||||
bool loadState(const string &filename);
|
||||
serializer serialize();
|
||||
bool unserialize(serializer&);
|
||||
|
||||
void setCheats(const lstring &list = lstring{});
|
||||
|
||||
void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan);
|
||||
void audioSample(int16_t lsample, int16_t rsample);
|
|
@ -27,7 +27,7 @@ void Application::run() {
|
|||
}
|
||||
|
||||
Application::Application(int argc, char **argv) {
|
||||
title = "bsnes v083.07";
|
||||
title = "bsnes v083.08";
|
||||
|
||||
application = this;
|
||||
quit = false;
|
||||
|
@ -88,7 +88,7 @@ Application::Application(int argc, char **argv) {
|
|||
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
|
||||
dspaudio.setBalance(0.0);
|
||||
dspaudio.setResampler(DSP::ResampleEngine::Sinc);
|
||||
dspaudio.setResamplerFrequency(48000.0);
|
||||
dspaudio.setResamplerFrequency(config->audio.frequency);
|
||||
|
||||
input.driver(config->input.driver);
|
||||
input.set(Input::Handle, mainWindow->viewport.handle());
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
VideoSettings *videoSettings = 0;
|
||||
|
||||
VideoSlider::VideoSlider() {
|
||||
slider.setLength(201);
|
||||
|
||||
append(name, 75, 0);
|
||||
append(value, 75, 0);
|
||||
append(slider, ~0, 0);
|
||||
|
@ -14,9 +12,11 @@ VideoSettings::VideoSettings() {
|
|||
colorAdjustment.setFont(application->boldFont);
|
||||
colorAdjustment.setText("Color adjustment:");
|
||||
brightness.name.setText("Brightness:");
|
||||
brightness.slider.setLength(201);
|
||||
contrast.name.setText("Contrast:");
|
||||
contrast.slider.setLength(201);
|
||||
gamma.name.setText("Gamma:");
|
||||
gammaRamp.setText("Enable gamma ramp simulation");
|
||||
gamma.slider.setLength(101);
|
||||
overscanAdjustment.setFont(application->boldFont);
|
||||
overscanAdjustment.setText("Overscan mask:");
|
||||
overscanHorizontal.name.setText("Horizontal:");
|
||||
|
@ -31,13 +31,10 @@ VideoSettings::VideoSettings() {
|
|||
RadioBox::group(fullScreen[0], fullScreen[1], fullScreen[2]);
|
||||
|
||||
append(title, ~0, 0, 5);
|
||||
#if 0
|
||||
append(colorAdjustment, ~0, 0);
|
||||
append(brightness, ~0, 0);
|
||||
append(contrast, ~0, 0);
|
||||
append(gamma, ~0, 0);
|
||||
append(gammaRamp, ~0, 0, 5);
|
||||
#endif
|
||||
append(gamma, ~0, 0, 5);
|
||||
append(overscanAdjustment, ~0, 0);
|
||||
append(overscanHorizontal, ~0, 0);
|
||||
append(overscanVertical, ~0, 0, 5);
|
||||
|
@ -50,14 +47,13 @@ VideoSettings::VideoSettings() {
|
|||
brightness.slider.setPosition(config->video.brightness);
|
||||
contrast.slider.setPosition(config->video.contrast);
|
||||
gamma.slider.setPosition(config->video.gamma);
|
||||
gammaRamp.setChecked(config->video.gammaRamp);
|
||||
overscanHorizontal.slider.setPosition(config->video.maskOverscanHorizontal);
|
||||
overscanVertical.slider.setPosition(config->video.maskOverscanVertical);
|
||||
fullScreen[config->video.fullScreenMode].setChecked();
|
||||
|
||||
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 =
|
||||
fullScreen[0].onTick = fullScreen[1].onTick = fullScreen[2].onTick =
|
||||
{ &VideoSettings::synchronize, this };
|
||||
|
@ -67,7 +63,6 @@ void VideoSettings::synchronize() {
|
|||
config->video.brightness = brightness.slider.position();
|
||||
config->video.contrast = contrast.slider.position();
|
||||
config->video.gamma = gamma.slider.position();
|
||||
config->video.gammaRamp = gammaRamp.checked();
|
||||
config->video.maskOverscanHorizontal = overscanHorizontal.slider.position();
|
||||
config->video.maskOverscanVertical = overscanVertical.slider.position();
|
||||
if(fullScreen[0].checked()) config->video.fullScreenMode = 0;
|
||||
|
@ -76,7 +71,7 @@ void VideoSettings::synchronize() {
|
|||
|
||||
brightness.value.setText({ config->video.brightness, "%" });
|
||||
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" });
|
||||
overscanVertical.value.setText({ config->video.maskOverscanVertical, "px" });
|
||||
|
|
|
@ -12,7 +12,6 @@ struct VideoSettings : SettingsLayout {
|
|||
VideoSlider brightness;
|
||||
VideoSlider contrast;
|
||||
VideoSlider gamma;
|
||||
CheckBox gammaRamp;
|
||||
Label overscanAdjustment;
|
||||
VideoSlider overscanHorizontal;
|
||||
VideoSlider overscanVertical;
|
||||
|
|
Loading…
Reference in New Issue