mirror of https://github.com/bsnes-emu/bsnes.git
Update to v082r13 release.
byuu says: I've updated the {System}::Interface classes to encapsulate all common functionality, so they are C++ equivalents of libsnes now. The idea being, use the interface class and you'll never have to reach into core objects (unless you really want to.) Not guaranteeing as stable an API as I do with libsnes for that, though. C++ doesn't make for nice dynamic libraries, anyway. Added back the state manager, and it now works for both SNES and Game Boy. NES save states aren't in yet. Anyway, this should give you a pretty good feel for what the combined UI is going to be like: same as before, everything works the same. Only difference is the dynamic system menu and cartridge menu with more load options. The settings window will be mostly the same as well, but will obviously have options that only apply to some systems.
This commit is contained in:
parent
5f099b8ad0
commit
e3c7bbfb63
|
@ -1,9 +1,10 @@
|
|||
gameboy_objects := gameboy-system gameboy-scheduler
|
||||
gameboy_objects := gameboy-interface gameboy-system gameboy-scheduler
|
||||
gameboy_objects += gameboy-memory gameboy-cartridge
|
||||
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
|
||||
gameboy_objects += gameboy-cheat
|
||||
objects += $(gameboy_objects)
|
||||
|
||||
obj/gameboy-interface.o: $(gameboy)/interface/interface.cpp $(call rwildcard,$(gameboy)/interface/)
|
||||
obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
|
||||
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
|
||||
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
|
||||
|
|
|
@ -46,7 +46,7 @@ void APU::main() {
|
|||
noise.run();
|
||||
master.run();
|
||||
|
||||
system.interface->audio_sample(master.center, master.left, master.right);
|
||||
interface->audioSample(master.center, master.left, master.right);
|
||||
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,38 +54,8 @@ void Cheat::synchronize() {
|
|||
foreach(n, override) n = false;
|
||||
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
const CheatCode &code = operator[](n);
|
||||
|
||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
||||
override[code.addr[n]] = true;
|
||||
}
|
||||
override[operator[](n).addr] = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CheatCode::operator=(const string &s) {
|
||||
addr.reset(), data.reset(), comp.reset();
|
||||
|
||||
lstring list;
|
||||
list.split("+", s);
|
||||
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
unsigned codeaddr, codedata, codecomp;
|
||||
if(Cheat::decode(list[n], codeaddr, codedata, codecomp) == false) {
|
||||
addr.reset(), data.reset(), comp.reset();
|
||||
return false;
|
||||
}
|
||||
addr.append(codeaddr), data.append(codedata), comp.append(codecomp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CheatCode::CheatCode(const string &s) {
|
||||
operator=(s);
|
||||
}
|
||||
|
||||
CheatCode::CheatCode() {
|
||||
enable = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
struct CheatCode {
|
||||
bool enable;
|
||||
array<unsigned> addr;
|
||||
array<unsigned> data;
|
||||
array<unsigned> comp;
|
||||
|
||||
bool operator=(const string&);
|
||||
CheatCode(const string&);
|
||||
CheatCode();
|
||||
unsigned addr;
|
||||
unsigned data;
|
||||
unsigned comp;
|
||||
};
|
||||
|
||||
struct Cheat : public linear_vector<CheatCode> {
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
void CPU::mmio_joyp_poll() {
|
||||
unsigned button = 0, dpad = 0;
|
||||
|
||||
button |= system.interface->input_poll((unsigned)Input::Start) << 3;
|
||||
button |= system.interface->input_poll((unsigned)Input::Select) << 2;
|
||||
button |= system.interface->input_poll((unsigned)Input::B) << 1;
|
||||
button |= system.interface->input_poll((unsigned)Input::A) << 0;
|
||||
button |= interface->inputPoll((unsigned)Input::Start) << 3;
|
||||
button |= interface->inputPoll((unsigned)Input::Select) << 2;
|
||||
button |= interface->inputPoll((unsigned)Input::B) << 1;
|
||||
button |= interface->inputPoll((unsigned)Input::A) << 0;
|
||||
|
||||
dpad |= system.interface->input_poll((unsigned)Input::Down) << 3;
|
||||
dpad |= system.interface->input_poll((unsigned)Input::Up) << 2;
|
||||
dpad |= system.interface->input_poll((unsigned)Input::Left) << 1;
|
||||
dpad |= system.interface->input_poll((unsigned)Input::Right) << 0;
|
||||
dpad |= interface->inputPoll((unsigned)Input::Down) << 3;
|
||||
dpad |= interface->inputPoll((unsigned)Input::Up) << 2;
|
||||
dpad |= interface->inputPoll((unsigned)Input::Left) << 1;
|
||||
dpad |= interface->inputPoll((unsigned)Input::Right) << 0;
|
||||
|
||||
status.joyp = 0x0f;
|
||||
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
||||
|
@ -84,7 +84,7 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
|
|||
if(addr == 0xff00) { //JOYP
|
||||
status.p15 = data & 0x20;
|
||||
status.p14 = data & 0x10;
|
||||
system.interface->joyp_write(status.p15, status.p14);
|
||||
interface->joypWrite(status.p15, status.p14);
|
||||
mmio_joyp_poll();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
#include <gameboy/gameboy.hpp>
|
||||
|
||||
namespace GameBoy {
|
||||
|
||||
Interface *interface = 0;
|
||||
|
||||
void Interface::lcdScanline() {
|
||||
}
|
||||
|
||||
void Interface::joypWrite(bool p15, bool p14) {
|
||||
}
|
||||
|
||||
void Interface::videoRefresh(const uint8_t *data) {
|
||||
}
|
||||
|
||||
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {
|
||||
}
|
||||
|
||||
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(const string &xml, const uint8_t *data, unsigned size) {
|
||||
cartridge.load(xml, data, size);
|
||||
system.power();
|
||||
}
|
||||
|
||||
void Interface::unloadCartridge() {
|
||||
cartridge.unload();
|
||||
}
|
||||
|
||||
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();
|
||||
foreach(code, list) {
|
||||
lstring codelist;
|
||||
codelist.split("+", code);
|
||||
foreach(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,12 +1,27 @@
|
|||
class Interface {
|
||||
public:
|
||||
virtual void lcd_scanline() {}
|
||||
virtual void joyp_write(bool p15, bool p14) {}
|
||||
virtual void lcdScanline();
|
||||
virtual void joypWrite(bool p15, bool p14);
|
||||
|
||||
virtual void video_refresh(const uint8_t *data) {}
|
||||
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
|
||||
virtual void input_poll() {}
|
||||
virtual bool input_poll(unsigned id) {}
|
||||
virtual void videoRefresh(const uint8_t *data);
|
||||
virtual void audioSample(int16_t center, int16_t left, int16_t right);
|
||||
virtual bool inputPoll(unsigned id);
|
||||
|
||||
virtual void message(const string &text) { print(text, "\n"); }
|
||||
virtual void initialize(Interface*);
|
||||
|
||||
virtual bool cartridgeLoaded();
|
||||
virtual void loadCartridge(const string &xml, const uint8_t *data, unsigned size);
|
||||
virtual void unloadCartridge();
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
|
@ -57,8 +57,7 @@ void LCD::scanline() {
|
|||
}
|
||||
|
||||
void LCD::frame() {
|
||||
system.interface->video_refresh(screen);
|
||||
system.interface->input_poll();
|
||||
interface->videoRefresh(screen);
|
||||
cpu.mmio_joyp_poll();
|
||||
|
||||
status.ly = 0;
|
||||
|
@ -80,7 +79,7 @@ void LCD::render() {
|
|||
|
||||
uint8_t *output = screen + status.ly * 160;
|
||||
for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55;
|
||||
system.interface->lcd_scanline();
|
||||
interface->lcdScanline();
|
||||
}
|
||||
|
||||
uint16 LCD::read_tile(bool select, unsigned x, unsigned y) {
|
||||
|
|
|
@ -45,13 +45,10 @@ uint8 Bus::read(uint16 addr) {
|
|||
uint8 data = mmio[addr]->mmio_read(addr);
|
||||
|
||||
if(cheat.override[addr]) {
|
||||
for(unsigned x = 0; x < cheat.size(); x++) {
|
||||
const CheatCode &code = cheat[x];
|
||||
for(unsigned y = 0; y < code.addr.size(); y++) {
|
||||
if(code.addr[y] == addr) {
|
||||
if(code.comp[y] > 255 || code.comp[y] == data) {
|
||||
return code.data[y];
|
||||
}
|
||||
for(unsigned n = 0; n < cheat.size(); n++) {
|
||||
if(cheat[n].addr == addr) {
|
||||
if(cheat[n].comp > 255 || cheat[n].comp == data) {
|
||||
return cheat[n].data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ void System::runthreadtosave() {
|
|||
}
|
||||
}
|
||||
|
||||
void System::init(Interface *interface_) {
|
||||
interface = interface_;
|
||||
void System::init() {
|
||||
assert(interface != 0);
|
||||
}
|
||||
|
||||
void System::load() {
|
||||
|
@ -49,11 +49,6 @@ void System::power() {
|
|||
lcd.power();
|
||||
scheduler.init();
|
||||
|
||||
// cheat.reset();
|
||||
// cheat.append(CheatCode("3EB-81B-4CA"));
|
||||
// cheat[0].enable = true;
|
||||
// cheat.synchronize();
|
||||
|
||||
clocks_executed = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,11 +14,10 @@ struct System {
|
|||
void runtosave();
|
||||
void runthreadtosave();
|
||||
|
||||
void init(Interface*);
|
||||
void init();
|
||||
void load();
|
||||
void power();
|
||||
|
||||
Interface *interface;
|
||||
unsigned clocks_executed;
|
||||
|
||||
//serialization.cpp
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
nes_objects := nes-system nes-scheduler
|
||||
nes_objects := nes-interface nes-system nes-scheduler
|
||||
nes_objects += nes-mapper nes-cartridge nes-memory
|
||||
nes_objects += nes-cpu nes-apu nes-ppu
|
||||
nes_objects += nes-cheat
|
||||
objects += $(nes_objects)
|
||||
|
||||
obj/nes-interface.o: $(nes)/interface/interface.cpp $(call rwildcard,$(nes)/interface/)
|
||||
obj/nes-system.o: $(nes)/system/system.cpp $(call rwildcard,$(nes)/system/)
|
||||
obj/nes-scheduler.o: $(nes)/scheduler/scheduler.cpp $(call rwildcard,$(nes)/scheduler/)
|
||||
obj/nes-mapper.o: $(nes)/mapper/mapper.cpp $(call rwildcard,$(nes)/mapper/)
|
||||
|
|
|
@ -42,7 +42,7 @@ void APU::main() {
|
|||
clock_frame_counter_divider();
|
||||
|
||||
signed output = rectangle_dac[rectangle_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
|
||||
system.interface->audio_sample(output);
|
||||
interface->audioSample(output);
|
||||
|
||||
tick();
|
||||
}
|
||||
|
@ -257,8 +257,8 @@ void APU::Sweep::clock() {
|
|||
signed delta = rectangle_period >> shift;
|
||||
|
||||
if(decrement) {
|
||||
rectangle_period -= delta;
|
||||
//decrement first square wave by one extra
|
||||
//first rectangle decrements by one extra
|
||||
rectangle_period -= delta + (channel == 0);
|
||||
} else if((rectangle_period + delta) < 0x800) {
|
||||
rectangle_period += delta;
|
||||
}
|
||||
|
@ -328,7 +328,7 @@ void APU::Triangle::clock_length() {
|
|||
}
|
||||
}
|
||||
|
||||
void APU::Triangle::clock_length_counter() {
|
||||
void APU::Triangle::clock_linear_length() {
|
||||
if(reload_linear) {
|
||||
linear_length_counter = linear_length;
|
||||
} else if(linear_length_counter) {
|
||||
|
@ -339,7 +339,8 @@ void APU::Triangle::clock_length_counter() {
|
|||
}
|
||||
|
||||
uint8 APU::Triangle::clock() {
|
||||
uint8 result = (step_counter & 0x0f) ^ ((step_counter & 0x10) ? 0x0f : 0x00);
|
||||
uint8 result = step_counter & 0x0f;
|
||||
if(step_counter & 0x10) result ^= 0x0f;
|
||||
if(length_counter == 0 || linear_length_counter == 0) return result;
|
||||
|
||||
if(--period_counter == 0) {
|
||||
|
@ -397,7 +398,7 @@ void APU::clock_frame_counter() {
|
|||
|
||||
rectangle[0].envelope.clock();
|
||||
rectangle[1].envelope.clock();
|
||||
triangle.clock_length_counter();
|
||||
triangle.clock_linear_length();
|
||||
noise.envelope.clock();
|
||||
|
||||
if(frame.counter == 0) {
|
||||
|
@ -414,6 +415,9 @@ void APU::clock_frame_counter_divider() {
|
|||
}
|
||||
|
||||
APU::APU() {
|
||||
rectangle[0].sweep.channel = 0;
|
||||
rectangle[1].sweep.channel = 1;
|
||||
|
||||
for(unsigned amp = 0; amp < 32; amp++) {
|
||||
if(amp == 0) {
|
||||
rectangle_dac[amp] = 0;
|
||||
|
|
|
@ -25,6 +25,8 @@ struct APU : Processor {
|
|||
};
|
||||
|
||||
struct Sweep {
|
||||
bool channel;
|
||||
|
||||
uint8 shift;
|
||||
bool decrement;
|
||||
uint3 period;
|
||||
|
@ -67,7 +69,7 @@ struct APU : Processor {
|
|||
bool reload_linear;
|
||||
|
||||
void clock_length();
|
||||
void clock_length_counter();
|
||||
void clock_linear_length();
|
||||
uint8 clock();
|
||||
} triangle;
|
||||
|
||||
|
@ -113,10 +115,10 @@ struct APU : Processor {
|
|||
int16 dmc_triangle_noise_dac[128][16][16];
|
||||
|
||||
static const uint8 length_counter_table[32];
|
||||
static const uint16 ntsc_noise_period_table[16];
|
||||
static const uint16 pal_noise_period_table[16];
|
||||
static const uint16 ntsc_dmc_period_table[16];
|
||||
static const uint16 pal_dmc_period_table[16];
|
||||
static const uint16 ntsc_noise_period_table[16];
|
||||
static const uint16 pal_noise_period_table[16];
|
||||
};
|
||||
|
||||
extern APU apu;
|
||||
|
|
|
@ -51,38 +51,8 @@ void Cheat::synchronize() {
|
|||
foreach(n, override) n = false;
|
||||
|
||||
for(unsigned n = 0; n < size(); n++) {
|
||||
const CheatCode &code = operator[](n);
|
||||
|
||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
||||
override[code.addr[n]] = true;
|
||||
}
|
||||
override[operator[](n).addr] = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CheatCode::operator=(const string &s) {
|
||||
addr.reset(), data.reset(), comp.reset();
|
||||
|
||||
lstring list;
|
||||
list.split("+", s);
|
||||
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
unsigned codeaddr, codedata, codecomp;
|
||||
if(Cheat::decode(list[n], codeaddr, codedata, codecomp) == false) {
|
||||
addr.reset(), data.reset(), comp.reset();
|
||||
return false;
|
||||
}
|
||||
addr.append(codeaddr), data.append(codedata), comp.append(codecomp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CheatCode::CheatCode(const string &s) {
|
||||
operator=(s);
|
||||
}
|
||||
|
||||
CheatCode::CheatCode() {
|
||||
enable = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
struct CheatCode {
|
||||
bool enable;
|
||||
array<unsigned> addr;
|
||||
array<unsigned> data;
|
||||
array<unsigned> comp;
|
||||
|
||||
bool operator=(const string&);
|
||||
CheatCode(const string&);
|
||||
CheatCode();
|
||||
unsigned addr;
|
||||
unsigned data;
|
||||
unsigned comp;
|
||||
};
|
||||
|
||||
struct Cheat : public linear_vector<CheatCode> {
|
||||
|
|
|
@ -91,12 +91,12 @@ void CPU::ram_write(uint16 addr, uint8 data) {
|
|||
uint8 CPU::read(uint16 addr) {
|
||||
if(addr == 0x4016) {
|
||||
if(status.controller_port0 >= 8) return (mdr() & 0xc0) | 1;
|
||||
return system.interface->input_poll(0, 0u, status.controller_port0++);
|
||||
return interface->inputPoll(0, 0u, status.controller_port0++);
|
||||
}
|
||||
|
||||
if(addr == 0x4017) {
|
||||
if(status.controller_port1 >= 8) return (mdr() & 0xc0) | 1;
|
||||
return system.interface->input_poll(1, 0u, status.controller_port1++);
|
||||
return interface->inputPoll(1, 0u, status.controller_port1++);
|
||||
}
|
||||
|
||||
return apu.read(addr);
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
#include <nes/nes.hpp>
|
||||
|
||||
namespace NES {
|
||||
|
||||
Interface *interface = 0;
|
||||
|
||||
void Interface::videoRefresh(const uint16_t *data) {
|
||||
}
|
||||
|
||||
void Interface::audioSample(const int16_t sample) {
|
||||
}
|
||||
|
||||
int16_t Interface::inputPoll(bool port, unsigned device, unsigned id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Interface::initialize(Interface *derived_interface) {
|
||||
interface = derived_interface;
|
||||
system.init();
|
||||
}
|
||||
|
||||
bool Interface::cartridgeLoaded() {
|
||||
return cartridge.loaded();
|
||||
}
|
||||
|
||||
void Interface::loadCartridge(const string &xml, const uint8_t *data, unsigned size) {
|
||||
cartridge.load(xml, data, size);
|
||||
system.power();
|
||||
}
|
||||
|
||||
void Interface::unloadCartridge() {
|
||||
cartridge.unload();
|
||||
}
|
||||
|
||||
void Interface::power() {
|
||||
system.power();
|
||||
}
|
||||
|
||||
void Interface::reset() {
|
||||
system.reset();
|
||||
}
|
||||
|
||||
void Interface::run() {
|
||||
system.run();
|
||||
}
|
||||
|
||||
void Interface::setCheats(const lstring &list) {
|
||||
cheat.reset();
|
||||
foreach(code, list) {
|
||||
lstring codelist;
|
||||
codelist.split("+", code);
|
||||
foreach(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,7 +1,21 @@
|
|||
struct Interface {
|
||||
virtual void video_refresh(const uint16_t *data) {}
|
||||
virtual void audio_sample(int16_t sample) {}
|
||||
virtual int16_t input_poll(bool port, unsigned device, unsigned id) { return 0; }
|
||||
virtual void videoRefresh(const uint16_t *data);
|
||||
virtual void audioSample(int16_t sample);
|
||||
virtual int16_t inputPoll(bool port, unsigned device, unsigned id);
|
||||
|
||||
virtual void message(const string &text) { print(text, "\n"); }
|
||||
virtual void initialize(Interface*);
|
||||
|
||||
virtual bool cartridgeLoaded();
|
||||
virtual void loadCartridge(const string &xml, const uint8_t *data, unsigned size);
|
||||
virtual void unloadCartridge();
|
||||
|
||||
virtual void power();
|
||||
virtual void reset();
|
||||
virtual void run();
|
||||
|
||||
virtual void setCheats(const lstring &list = lstring{});
|
||||
|
||||
virtual void message(const string &text);
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
|
@ -18,13 +18,10 @@ uint8 Bus::read(uint16 addr) {
|
|||
if(addr <= 0x4017) return cpu.read(addr);
|
||||
|
||||
if(cheat.override[addr]) {
|
||||
for(unsigned x = 0; x < cheat.size(); x++) {
|
||||
const CheatCode &code = cheat[x];
|
||||
for(unsigned y = 0; y < code.addr.size(); y++) {
|
||||
if(code.addr[y] == addr) {
|
||||
if(code.comp[y] > 255 || code.comp[y] == data) {
|
||||
return code.data[y];
|
||||
}
|
||||
for(unsigned n = 0; n < cheat.size(); n++) {
|
||||
if(cheat[n].addr == addr) {
|
||||
if(cheat[n].comp > 255 || cheat[n].comp == data) {
|
||||
return cheat[n].data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
namespace NES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bnes";
|
||||
static const char Version[] = "000.08";
|
||||
static const char Version[] = "000.09";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ void PPU::scanline_edge() {
|
|||
|
||||
void PPU::frame_edge() {
|
||||
status.field ^= 1;
|
||||
system.interface->video_refresh(buffer);
|
||||
interface->videoRefresh(buffer);
|
||||
scheduler.exit();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,11 +14,6 @@ void System::power() {
|
|||
apu.power();
|
||||
ppu.power();
|
||||
scheduler.power();
|
||||
|
||||
// cheat.reset();
|
||||
// cheat.append(CheatCode("GXXZZLVI"));
|
||||
// cheat[0].enable = true;
|
||||
// cheat.synchronize();
|
||||
}
|
||||
|
||||
void System::reset() {
|
||||
|
@ -29,8 +24,8 @@ void System::reset() {
|
|||
scheduler.reset();
|
||||
}
|
||||
|
||||
void System::init(Interface *interface) {
|
||||
this->interface = interface;
|
||||
void System::init() {
|
||||
assert(interface != 0);
|
||||
}
|
||||
|
||||
void System::term() {
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
struct System {
|
||||
Interface *interface;
|
||||
|
||||
void run();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void init(Interface *interface);
|
||||
void init();
|
||||
void term();
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
snes_objects := snes-system snes-controller
|
||||
snes_objects := snes-interface snes-system snes-controller
|
||||
snes_objects += snes-cartridge snes-cheat
|
||||
snes_objects += snes-memory snes-cpucore snes-smpcore
|
||||
snes_objects += snes-cpu snes-smp snes-dsp snes-ppu
|
||||
|
@ -28,6 +28,7 @@ else ifeq ($(profile),performance)
|
|||
snesppu := $(snes)/alt/ppu-performance
|
||||
endif
|
||||
|
||||
obj/snes-interface.o : $(snes)/interface/interface.cpp $(call rwildcard,$(snes)/interface)
|
||||
obj/snes-system.o : $(snes)/system/system.cpp $(call rwildcard,$(snes)/system/) $(call rwildcard,$(snes)/video/) $(call rwildcard,$(snes)/audio/) $(call rwildcard,$(snes)/input/)
|
||||
obj/snes-controller.o: $(snes)/controller/controller.cpp $(call rwildcard,$(snes)/controller/)
|
||||
obj/snes-memory.o : $(snes)/memory/memory.cpp $(call rwildcard,$(snes)/memory/)
|
||||
|
|
|
@ -19,7 +19,7 @@ void Audio::coprocessor_frequency(double input_frequency) {
|
|||
|
||||
void Audio::sample(int16 left, int16 right) {
|
||||
if(coprocessor == false) {
|
||||
system.interface->audio_sample(left, right);
|
||||
interface->audioSample(left, right);
|
||||
} else {
|
||||
dsp_buffer[dsp_wroffset] = ((uint16)left << 0) + ((uint16)right << 16);
|
||||
dsp_wroffset = (dsp_wroffset + 1) & buffer_mask;
|
||||
|
@ -61,7 +61,7 @@ void Audio::flush() {
|
|||
int cop_left = (int16)(cop_sample >> 0);
|
||||
int cop_right = (int16)(cop_sample >> 16);
|
||||
|
||||
system.interface->audio_sample(
|
||||
interface->audioSample(
|
||||
sclamp<16>((dsp_left + cop_left ) / 2),
|
||||
sclamp<16>((dsp_right + cop_right) / 2)
|
||||
);
|
||||
|
|
|
@ -299,16 +299,16 @@ void Cartridge::xml_parse_necdsp(xml_element &root) {
|
|||
}
|
||||
}
|
||||
|
||||
string path = { dir(system.interface->path(Slot::Base, ".dsp")), firmware };
|
||||
string path = { dir(interface->path(Slot::Base, ".dsp")), firmware };
|
||||
unsigned promsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 2048 : 16384);
|
||||
unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048);
|
||||
unsigned filesize = promsize * 3 + dromsize * 2;
|
||||
|
||||
file fp;
|
||||
if(fp.open(path, file::mode::read) == false) {
|
||||
system.interface->message({ "Warning: NEC DSP firmware ", firmware, " is missing." });
|
||||
interface->message({ "Warning: NEC DSP firmware ", firmware, " is missing." });
|
||||
} else if(fp.size() != filesize) {
|
||||
system.interface->message({ "Warning: NEC DSP firmware ", firmware, " is of the wrong file size." });
|
||||
interface->message({ "Warning: NEC DSP firmware ", firmware, " is of the wrong file size." });
|
||||
fp.close();
|
||||
} else {
|
||||
for(unsigned n = 0; n < promsize; n++) necdsp.programROM[n] = fp.readm(3);
|
||||
|
@ -321,7 +321,7 @@ void Cartridge::xml_parse_necdsp(xml_element &root) {
|
|||
fp.read(data, filesize);
|
||||
|
||||
if(sha256 != nall::sha256(data, filesize)) {
|
||||
system.interface->message({ "Warning: Hitachi DSP firmware ", firmware, " SHA256 sum is incorrect." });
|
||||
interface->message({ "Warning: Hitachi DSP firmware ", firmware, " SHA256 sum is incorrect." });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,12 +381,12 @@ void Cartridge::xml_parse_hitachidsp(xml_element &root) {
|
|||
}
|
||||
}
|
||||
|
||||
string path = { dir(system.interface->path(Slot::Base, ".dsp")), firmware };
|
||||
string path = { dir(interface->path(Slot::Base, ".dsp")), firmware };
|
||||
file fp;
|
||||
if(fp.open(path, file::mode::read) == false) {
|
||||
system.interface->message({ "Warning: Hitachi DSP firmware ", firmware, " is missing." });
|
||||
interface->message({ "Warning: Hitachi DSP firmware ", firmware, " is missing." });
|
||||
} else if(fp.size() != 1024 * 3) {
|
||||
system.interface->message({ "Warning: Hitachi DSP firmware ", firmware, " is of the wrong file size." });
|
||||
interface->message({ "Warning: Hitachi DSP firmware ", firmware, " is of the wrong file size." });
|
||||
fp.close();
|
||||
} else {
|
||||
for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = fp.readl(3);
|
||||
|
@ -398,7 +398,7 @@ void Cartridge::xml_parse_hitachidsp(xml_element &root) {
|
|||
fp.read(data, 3072);
|
||||
|
||||
if(sha256 != nall::sha256(data, 3072)) {
|
||||
system.interface->message({ "Warning: Hitachi DSP firmware ", firmware, " SHA256 sum is incorrect." });
|
||||
interface->message({ "Warning: Hitachi DSP firmware ", firmware, " SHA256 sum is incorrect." });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,27 +16,22 @@ void Cheat::enable(bool state) {
|
|||
|
||||
void Cheat::synchronize() {
|
||||
memset(override, 0x00, 16 * 1024 * 1024);
|
||||
code_enabled = false;
|
||||
code_enabled = size() > 0;
|
||||
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
const CheatCode &code = operator[](i);
|
||||
if(code.enabled == false) continue;
|
||||
|
||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
||||
code_enabled = true;
|
||||
unsigned addr = mirror(code.addr);
|
||||
override[addr] = true;
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
|
||||
unsigned mirroraddr;
|
||||
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||
mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
override[mirroraddr] = true;
|
||||
|
||||
unsigned addr = mirror(code.addr[n]);
|
||||
override[addr] = true;
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
|
||||
unsigned mirroraddr;
|
||||
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||
mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
override[mirroraddr] = true;
|
||||
|
||||
mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||
override[mirroraddr] = true;
|
||||
}
|
||||
mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||
override[mirroraddr] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,12 +44,8 @@ uint8 Cheat::read(unsigned addr) const {
|
|||
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
const CheatCode &code = operator[](i);
|
||||
if(code.enabled == false) continue;
|
||||
|
||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
||||
if(addr == mirror(code.addr[n])) {
|
||||
return code.data[n];
|
||||
}
|
||||
if(addr == mirror(code.addr)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +69,7 @@ Cheat::~Cheat() {
|
|||
//encode / decode
|
||||
//===============
|
||||
|
||||
bool Cheat::decode(const char *s, unsigned &addr, uint8 &data, Type &type) {
|
||||
bool Cheat::decode(const char *s, unsigned &addr, unsigned &data, Type &type) {
|
||||
string t = s;
|
||||
t.lower();
|
||||
|
||||
|
@ -128,7 +119,7 @@ bool Cheat::decode(const char *s, unsigned &addr, uint8 &data, Type &type) {
|
|||
#undef ischr
|
||||
}
|
||||
|
||||
bool Cheat::encode(string &s, unsigned addr, uint8 data, Type type) {
|
||||
bool Cheat::encode(string &s, unsigned addr, unsigned data, Type type) {
|
||||
char t[16];
|
||||
|
||||
if(type == Type::ProActionReplay) {
|
||||
|
@ -166,36 +157,4 @@ unsigned Cheat::mirror(unsigned addr) const {
|
|||
return addr;
|
||||
}
|
||||
|
||||
//=========
|
||||
//CheatCode
|
||||
//=========
|
||||
|
||||
bool CheatCode::operator=(string s) {
|
||||
addr.reset();
|
||||
data.reset();
|
||||
|
||||
lstring list;
|
||||
list.split("+", s.replace(" ", ""));
|
||||
|
||||
for(unsigned i = 0; i < list.size(); i++) {
|
||||
unsigned addr_;
|
||||
uint8 data_;
|
||||
Cheat::Type type_;
|
||||
if(Cheat::decode(list[i], addr_, data_, type_) == false) {
|
||||
addr.reset();
|
||||
data.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
addr.append(addr_);
|
||||
data.append(data_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CheatCode::CheatCode() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
struct CheatCode {
|
||||
bool enabled;
|
||||
array<unsigned> addr;
|
||||
array<uint8> data;
|
||||
|
||||
bool operator=(string);
|
||||
CheatCode();
|
||||
unsigned addr;
|
||||
unsigned data;
|
||||
};
|
||||
|
||||
class Cheat : public linear_vector<CheatCode> {
|
||||
|
@ -21,8 +17,8 @@ public:
|
|||
Cheat();
|
||||
~Cheat();
|
||||
|
||||
static bool decode(const char*, unsigned&, uint8&, Type&);
|
||||
static bool encode(string&, unsigned, uint8, Type);
|
||||
static bool decode(const char*, unsigned&, unsigned&, Type&);
|
||||
static bool encode(string&, unsigned, unsigned, Type);
|
||||
|
||||
private:
|
||||
bool system_enabled;
|
||||
|
|
|
@ -69,8 +69,8 @@ void ICD2::reset() {
|
|||
joyp14lock = 0;
|
||||
pulselock = true;
|
||||
|
||||
GameBoy::system.init(this);
|
||||
GameBoy::system.power();
|
||||
GameBoy::Interface::initialize(this);
|
||||
GameBoy::Interface::power();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifdef ICD2_CPP
|
||||
|
||||
//called on rendered lines 0-143 (not on Vblank lines 144-153)
|
||||
void ICD2::lcd_scanline() {
|
||||
void ICD2::lcdScanline() {
|
||||
if((GameBoy::lcd.status.ly & 7) == 0) {
|
||||
lcd.row = (lcd.row + 1) & 3;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ void ICD2::lcd_scanline() {
|
|||
memcpy(lcd.buffer + offset, GameBoy::lcd.screen + GameBoy::lcd.status.ly * 160, 160);
|
||||
}
|
||||
|
||||
void ICD2::joyp_write(bool p15, bool p14) {
|
||||
void ICD2::joypWrite(bool p15, bool p14) {
|
||||
//joypad handling
|
||||
if(p15 == 1 && p14 == 1) {
|
||||
if(joyp15lock == 0 && joyp14lock == 0) {
|
||||
|
@ -80,17 +80,14 @@ void ICD2::joyp_write(bool p15, bool p14) {
|
|||
packetlock = true;
|
||||
}
|
||||
|
||||
void ICD2::video_refresh(const uint8_t *data) {
|
||||
void ICD2::videoRefresh(const uint8_t *data) {
|
||||
}
|
||||
|
||||
void ICD2::audio_sample(int16_t center, int16_t left, int16_t right) {
|
||||
void ICD2::audioSample(int16_t center, int16_t left, int16_t right) {
|
||||
audio.coprocessor_sample(left, right);
|
||||
}
|
||||
|
||||
void ICD2::input_poll() {
|
||||
}
|
||||
|
||||
bool ICD2::input_poll(unsigned id) {
|
||||
bool ICD2::inputPoll(unsigned id) {
|
||||
GameBoy::cpu.status.mlt_req = joyp_id & mlt_req;
|
||||
|
||||
unsigned data = 0x00;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
void lcd_scanline();
|
||||
void joyp_write(bool p15, bool p14);
|
||||
void video_refresh(const uint8_t *data);
|
||||
void audio_sample(int16_t center, int16_t left, int16_t right);
|
||||
void input_poll();
|
||||
bool input_poll(unsigned id);
|
||||
void lcdScanline();
|
||||
void joypWrite(bool p15, bool p14);
|
||||
void videoRefresh(const uint8_t *data);
|
||||
void audioSample(int16_t center, int16_t left, int16_t right);
|
||||
bool inputPoll(unsigned id);
|
||||
|
||||
struct Packet {
|
||||
uint8 data[16];
|
||||
|
|
|
@ -22,7 +22,7 @@ void Link::init() {
|
|||
|
||||
void Link::load() {
|
||||
if(opened()) close();
|
||||
string basename = system.interface->path(Cartridge::Slot::Base, "");
|
||||
string basename = interface->path(Cartridge::Slot::Base, "");
|
||||
string name = program != "" ? program : notdir(basename);
|
||||
string path = dir(basename);
|
||||
if(open(name, path)) {
|
||||
|
|
|
@ -50,7 +50,7 @@ void MSU1::init() {
|
|||
|
||||
void MSU1::load() {
|
||||
if(datafile.open()) datafile.close();
|
||||
datafile.open(system.interface->path(Cartridge::Slot::Base, ".msu"), file::mode::read);
|
||||
datafile.open(interface->path(Cartridge::Slot::Base, ".msu"), file::mode::read);
|
||||
}
|
||||
|
||||
void MSU1::unload() {
|
||||
|
@ -133,7 +133,7 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
|
|||
if(addr == 0x2005) {
|
||||
mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
|
||||
if(audiofile.open()) audiofile.close();
|
||||
if(audiofile.open(system.interface->path(Cartridge::Slot::Base, { "-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
|
||||
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
|
||||
uint32 header = audiofile.readm(4);
|
||||
if(header != 0x4d535531) { //verify 'MSU1' header
|
||||
audiofile.close();
|
||||
|
|
|
@ -16,12 +16,12 @@ void MSU1::serialize(serializer &s) {
|
|||
s.integer(mmio.audio_play);
|
||||
|
||||
if(datafile.open()) datafile.close();
|
||||
if(datafile.open(system.interface->path(Cartridge::Slot::Base, ".msu"), file::mode::read)) {
|
||||
if(datafile.open(interface->path(Cartridge::Slot::Base, ".msu"), file::mode::read)) {
|
||||
datafile.seek(mmio.data_offset);
|
||||
}
|
||||
|
||||
if(audiofile.open()) audiofile.close();
|
||||
if(audiofile.open(system.interface->path(Cartridge::Slot::Base, { "-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
|
||||
if(audiofile.open(interface->path(Cartridge::Slot::Base, { "-", (unsigned)mmio.audio_track, ".pcm" }), file::mode::read)) {
|
||||
audiofile.seek(mmio.audio_offset);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
uint2 Gamepad::data() {
|
||||
if(counter >= 16) return 1;
|
||||
uint2 result = system.interface->input_poll(port, Input::Device::Joypad, 0, counter);
|
||||
uint2 result = interface->inputPoll(port, Input::Device::Joypad, 0, counter);
|
||||
if(latched == 0) counter++;
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -18,16 +18,16 @@ void Justifier::enter() {
|
|||
}
|
||||
|
||||
if(next < prev) {
|
||||
int nx1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
|
||||
int ny1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
|
||||
int nx1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
|
||||
int ny1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
|
||||
nx1 += x1;
|
||||
ny1 += y1;
|
||||
x1 = max(-16, min(256 + 16, nx1));
|
||||
y1 = max(-16, min(240 + 16, ny1));
|
||||
|
||||
if(chained == true) {
|
||||
int nx2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
|
||||
int ny2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
|
||||
int nx2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
|
||||
int ny2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
|
||||
nx2 += x2;
|
||||
ny2 += y2;
|
||||
x2 = max(-16, min(256 + 16, nx2));
|
||||
|
@ -48,11 +48,11 @@ uint2 Justifier::data() {
|
|||
if(counter >= 32) return 1;
|
||||
|
||||
if(counter == 0) {
|
||||
trigger1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger);
|
||||
start1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start);
|
||||
trigger1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger);
|
||||
start1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start);
|
||||
if(chained) {
|
||||
trigger2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger);
|
||||
start2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start);
|
||||
trigger2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger);
|
||||
start2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
uint2 Mouse::data() {
|
||||
if(counter >= 32) return 1;
|
||||
|
||||
int position_x = system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
|
||||
int position_y = system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
|
||||
int position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
|
||||
int position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
|
||||
|
||||
bool direction_x = position_x < 0; //0 = right, 1 = left
|
||||
bool direction_y = position_y < 0; //0 = down, 1 = up
|
||||
|
@ -25,8 +25,8 @@ uint2 Mouse::data() {
|
|||
case 6: return 0;
|
||||
case 7: return 0;
|
||||
|
||||
case 8: return system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right);
|
||||
case 9: return system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Left);
|
||||
case 8: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right);
|
||||
case 9: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Left);
|
||||
case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
|
||||
case 11: return 0; // ||
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ uint2 Multitap::data() {
|
|||
port2 = 3; //controller 4
|
||||
}
|
||||
|
||||
bool data1 = system.interface->input_poll(port, Input::Device::Multitap, port1, index);
|
||||
bool data2 = system.interface->input_poll(port, Input::Device::Multitap, port2, index);
|
||||
bool data1 = interface->inputPoll(port, Input::Device::Multitap, port1, index);
|
||||
bool data2 = interface->inputPoll(port, Input::Device::Multitap, port2, index);
|
||||
return (data2 << 1) | (data1 << 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ void Serial::latch(bool data) {
|
|||
|
||||
Serial::Serial(bool port) : Controller(port) {
|
||||
enable = false;
|
||||
string basename = system.interface->path(Cartridge::Slot::Base, "");
|
||||
string basename = interface->path(Cartridge::Slot::Base, "");
|
||||
string name = notdir(basename);
|
||||
string path = dir(basename);
|
||||
if(open(name, path)) {
|
||||
|
|
|
@ -28,8 +28,8 @@ void SuperScope::enter() {
|
|||
|
||||
if(next < prev) {
|
||||
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
|
||||
int nx = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
|
||||
int ny = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
|
||||
int nx = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
|
||||
int ny = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
|
||||
nx += x;
|
||||
ny += y;
|
||||
x = max(-16, min(256 + 16, nx));
|
||||
|
@ -51,7 +51,7 @@ uint2 SuperScope::data() {
|
|||
|
||||
if(counter == 0) {
|
||||
//turbo is a switch; toggle is edge sensitive
|
||||
bool newturbo = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo);
|
||||
bool newturbo = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo);
|
||||
if(newturbo && !turbo) {
|
||||
turbo = !turbo; //toggle state
|
||||
turbolock = true;
|
||||
|
@ -62,7 +62,7 @@ uint2 SuperScope::data() {
|
|||
//trigger is a button
|
||||
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
|
||||
trigger = false;
|
||||
bool newtrigger = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger);
|
||||
bool newtrigger = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger);
|
||||
if(newtrigger && (turbo || !triggerlock)) {
|
||||
trigger = true;
|
||||
triggerlock = true;
|
||||
|
@ -71,11 +71,11 @@ uint2 SuperScope::data() {
|
|||
}
|
||||
|
||||
//cursor is a button; it is always level sensitive
|
||||
cursor = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor);
|
||||
cursor = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor);
|
||||
|
||||
//pause is a button; it is always edge sensitive
|
||||
pause = false;
|
||||
bool newpause = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause);
|
||||
bool newpause = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause);
|
||||
if(newpause && !pauselock) {
|
||||
pause = true;
|
||||
pauselock = true;
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
#include <snes/snes.hpp>
|
||||
|
||||
namespace SNES {
|
||||
|
||||
Interface *interface = 0;
|
||||
|
||||
void Interface::videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {
|
||||
}
|
||||
|
||||
void Interface::audioSample(int16_t l_sample, int16_t r_sample) {
|
||||
}
|
||||
|
||||
int16_t Interface::inputPoll(bool port, Input::Device device, unsigned index, unsigned id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Interface::initialize(Interface *derived_interface) {
|
||||
interface = derived_interface;
|
||||
system.init();
|
||||
}
|
||||
|
||||
bool Interface::cartridgeLoaded() {
|
||||
return cartridge.loaded();
|
||||
}
|
||||
|
||||
void Interface::loadCartridge(const string &xml, const uint8_t *data, unsigned size) {
|
||||
cartridge.rom.copy(data, size);
|
||||
cartridge.load(Cartridge::Mode::Normal, { xml });
|
||||
system.power();
|
||||
}
|
||||
|
||||
void Interface::unloadCartridge() {
|
||||
cartridge.unload();
|
||||
}
|
||||
|
||||
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();
|
||||
foreach(code, list) {
|
||||
lstring codelist;
|
||||
codelist.split("+", code);
|
||||
foreach(part, codelist) {
|
||||
unsigned addr, data;
|
||||
Cheat::Type type;
|
||||
if(Cheat::decode(part, addr, data, type)) {
|
||||
cheat.append({ addr, data });
|
||||
}
|
||||
}
|
||||
}
|
||||
cheat.synchronize();
|
||||
}
|
||||
|
||||
void Interface::message(const string &text) {
|
||||
print(text, "\n");
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,26 @@
|
|||
class Interface {
|
||||
public:
|
||||
virtual void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {}
|
||||
virtual void audio_sample(int16_t l_sample, int16_t r_sample) {}
|
||||
virtual int16_t input_poll(bool port, Input::Device device, unsigned index, unsigned id) { return 0; }
|
||||
virtual void videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan);
|
||||
virtual void audioSample(int16_t lsample, int16_t rsample);
|
||||
virtual int16_t inputPoll(bool port, Input::Device device, unsigned index, unsigned id);
|
||||
|
||||
virtual void message(const string &text) { print(text, "\n"); }
|
||||
virtual void initialize(Interface*);
|
||||
|
||||
virtual bool cartridgeLoaded();
|
||||
virtual void loadCartridge(const string &xml, const uint8_t *data, unsigned size);
|
||||
virtual void unloadCartridge();
|
||||
|
||||
virtual void power();
|
||||
virtual void reset();
|
||||
virtual void run();
|
||||
|
||||
virtual serializer serialize();
|
||||
virtual bool unserialize(serializer&);
|
||||
|
||||
void setCheats(const lstring &list = lstring{});
|
||||
|
||||
virtual void message(const string &text);
|
||||
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "082.12";
|
||||
static const unsigned SerializerVersion = 21;
|
||||
static const char Version[] = "082.13";
|
||||
static const unsigned SerializerVersion = 22;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,16 +4,16 @@ serializer System::serialize() {
|
|||
serializer s(serialize_size);
|
||||
|
||||
unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = cartridge.crc32();
|
||||
char profile[16], description[512];
|
||||
memset(&profile, 0, sizeof profile);
|
||||
char description[512], profile[16];
|
||||
memset(&description, 0, sizeof description);
|
||||
memset(&profile, 0, sizeof profile);
|
||||
strlcpy(profile, Info::Profile, sizeof profile);
|
||||
|
||||
s.integer(signature);
|
||||
s.integer(version);
|
||||
s.integer(crc32);
|
||||
s.array(profile);
|
||||
s.array(description);
|
||||
s.array(profile);
|
||||
|
||||
serialize_all(s);
|
||||
return s;
|
||||
|
@ -21,13 +21,13 @@ serializer System::serialize() {
|
|||
|
||||
bool System::unserialize(serializer &s) {
|
||||
unsigned signature, version, crc32;
|
||||
char profile[16], description[512];
|
||||
char description[512], profile[16];
|
||||
|
||||
s.integer(signature);
|
||||
s.integer(version);
|
||||
s.integer(crc32);
|
||||
s.array(profile);
|
||||
s.array(description);
|
||||
s.array(profile);
|
||||
|
||||
if(signature != 0x31545342) return false;
|
||||
if(version != Info::SerializerVersion) return false;
|
||||
|
|
|
@ -63,8 +63,7 @@ void System::runthreadtosave() {
|
|||
}
|
||||
}
|
||||
|
||||
void System::init(Interface *interface_) {
|
||||
interface = interface_;
|
||||
void System::init() {
|
||||
assert(interface != 0);
|
||||
|
||||
icd2.init();
|
||||
|
@ -241,7 +240,7 @@ void System::scanline() {
|
|||
void System::frame() {
|
||||
}
|
||||
|
||||
System::System() : interface(0) {
|
||||
System::System() {
|
||||
region = Region::Autodetect;
|
||||
expansion = ExpansionPortDevice::BSX;
|
||||
}
|
||||
|
|
|
@ -2,14 +2,13 @@ class Interface;
|
|||
|
||||
class System : property<System> {
|
||||
public:
|
||||
Interface *interface;
|
||||
enum class Region : unsigned { NTSC = 0, PAL = 1, Autodetect = 2 };
|
||||
enum class ExpansionPortDevice : unsigned { None = 0, BSX = 1 };
|
||||
|
||||
void run();
|
||||
void runtosave();
|
||||
|
||||
void init(Interface*);
|
||||
void init();
|
||||
void term();
|
||||
void load();
|
||||
void unload();
|
||||
|
|
|
@ -79,7 +79,7 @@ void Video::update() {
|
|||
}
|
||||
}
|
||||
|
||||
system.interface->video_refresh(ppu.surface, hires, ppu.interlace(), ppu.overscan());
|
||||
interface->videoRefresh(ppu.surface, hires, ppu.interlace(), ppu.overscan());
|
||||
|
||||
hires = false;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ struct Interface : public SNES::Interface {
|
|||
snes_input_state_t pinput_state;
|
||||
string basename;
|
||||
|
||||
void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {
|
||||
void videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {
|
||||
unsigned width = hires ? 512 : 256;
|
||||
unsigned height = overscan ? 239 : 224;
|
||||
if(interlace) height <<= 1;
|
||||
|
@ -21,15 +21,11 @@ struct Interface : public SNES::Interface {
|
|||
if(pinput_poll) pinput_poll();
|
||||
}
|
||||
|
||||
void audio_sample(int16_t left, int16_t right) {
|
||||
void audioSample(int16_t left, int16_t right) {
|
||||
if(paudio_sample) return paudio_sample(left, right);
|
||||
}
|
||||
|
||||
void input_poll() {
|
||||
if(pinput_poll) return pinput_poll();
|
||||
}
|
||||
|
||||
int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
|
||||
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
|
||||
if(pinput_state) return pinput_state(port, (unsigned)device, index, id);
|
||||
return 0;
|
||||
}
|
||||
|
@ -86,7 +82,7 @@ void snes_set_cartridge_basename(const char *basename) {
|
|||
}
|
||||
|
||||
void snes_init(void) {
|
||||
SNES::system.init(&interface);
|
||||
interface.initialize(&interface);
|
||||
SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Joypad);
|
||||
SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Joypad);
|
||||
}
|
||||
|
@ -124,15 +120,27 @@ bool snes_unserialize(const uint8_t *data, unsigned size) {
|
|||
return SNES::system.unserialize(s);
|
||||
}
|
||||
|
||||
struct CheatList {
|
||||
bool enable;
|
||||
string code;
|
||||
CheatList() : enable(false) {}
|
||||
};
|
||||
|
||||
static linear_vector<CheatList> cheatList;
|
||||
|
||||
void snes_cheat_reset(void) {
|
||||
SNES::cheat.reset();
|
||||
SNES::cheat.synchronize();
|
||||
cheatList.reset();
|
||||
interface.setCheats();
|
||||
}
|
||||
|
||||
void snes_cheat_set(unsigned index, bool enabled, const char *code) {
|
||||
SNES::cheat[index] = code;
|
||||
SNES::cheat[index].enabled = enabled;
|
||||
SNES::cheat.synchronize();
|
||||
void snes_cheat_set(unsigned index, bool enable, const char *code) {
|
||||
cheatList[index].enable = enable;
|
||||
cheatList[index].code = code;
|
||||
lstring list;
|
||||
for(unsigned n = 0; n < cheatList.size(); n++) {
|
||||
if(cheatList[n].enable) list.append(cheatList[n].code);
|
||||
}
|
||||
interface.setCheats(list);
|
||||
}
|
||||
|
||||
bool snes_load_cartridge_normal(
|
||||
|
|
|
@ -95,7 +95,7 @@ bool snes_serialize(uint8_t *data, unsigned size);
|
|||
bool snes_unserialize(const uint8_t *data, unsigned size);
|
||||
|
||||
void snes_cheat_reset(void);
|
||||
void snes_cheat_set(unsigned index, bool enabled, const char *code);
|
||||
void snes_cheat_set(unsigned index, bool enable, const char *code);
|
||||
|
||||
bool snes_load_cartridge_normal(
|
||||
const char *rom_xml, const uint8_t *rom_data, unsigned rom_size
|
||||
|
|
|
@ -26,7 +26,7 @@ MainWindow::MainWindow() {
|
|||
|
||||
settingsMenu.setText("Settings");
|
||||
settingsSynchronizeVideo.setText("Synchronize Video");
|
||||
settingsSynchronizeVideo.setChecked();
|
||||
settingsSynchronizeVideo.setChecked(false);
|
||||
settingsSynchronizeAudio.setText("Synchronize Audio");
|
||||
settingsSynchronizeAudio.setChecked();
|
||||
settingsMuteAudio.setText("Mute Audio");
|
||||
|
@ -46,6 +46,7 @@ MainWindow::MainWindow() {
|
|||
toolsStateLoad5.setText("Slot 5");
|
||||
toolsShrinkWindow.setText("Shrink Window");
|
||||
toolsCheatEditor.setText("Cheat Editor ...");
|
||||
toolsStateManager.setText("State Manager ...");
|
||||
toolsTest.setText("Test");
|
||||
|
||||
helpMenu.setText("Help");
|
||||
|
@ -94,6 +95,7 @@ MainWindow::MainWindow() {
|
|||
toolsMenu.append(toolsSeparator);
|
||||
toolsMenu.append(toolsShrinkWindow);
|
||||
toolsMenu.append(toolsCheatEditor);
|
||||
toolsMenu.append(toolsStateManager);
|
||||
toolsMenu.append(toolsTest);
|
||||
|
||||
append(helpMenu);
|
||||
|
@ -168,6 +170,7 @@ MainWindow::MainWindow() {
|
|||
toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); };
|
||||
|
||||
toolsCheatEditor.onTick = [&] { cheatEditor->setVisible(); };
|
||||
toolsStateManager.onTick = [&] { stateManager->setVisible(); };
|
||||
|
||||
toolsTest.onTick = [&] {
|
||||
NES::cpu.trace = toolsTest.checked();
|
||||
|
|
|
@ -45,6 +45,7 @@ struct MainWindow : Window {
|
|||
Separator toolsSeparator;
|
||||
Item toolsShrinkWindow;
|
||||
Item toolsCheatEditor;
|
||||
Item toolsStateManager;
|
||||
CheckItem toolsTest;
|
||||
|
||||
Menu helpMenu;
|
||||
|
|
|
@ -5,21 +5,19 @@ bool InterfaceGameBoy::loadCartridge(const string &filename) {
|
|||
|
||||
interface->baseName = nall::basename(filename);
|
||||
GameBoyCartridge info(data, size);
|
||||
GameBoy::cartridge.load(info.xml, data, size);
|
||||
GameBoy::system.power();
|
||||
GameBoy::Interface::loadCartridge(info.xml, data, size);
|
||||
|
||||
delete[] data;
|
||||
return true;
|
||||
}
|
||||
|
||||
void InterfaceGameBoy::unloadCartridge() {
|
||||
GameBoy::cartridge.unload();
|
||||
GameBoy::Interface::unloadCartridge();
|
||||
interface->baseName = "";
|
||||
}
|
||||
|
||||
bool InterfaceGameBoy::saveState(const string &filename) {
|
||||
GameBoy::system.runtosave();
|
||||
serializer s = GameBoy::system.serialize();
|
||||
serializer s = serialize();
|
||||
return file::write(filename, s.data(), s.size());
|
||||
}
|
||||
|
||||
|
@ -29,21 +27,12 @@ bool InterfaceGameBoy::loadState(const string &filename) {
|
|||
if(file::read(filename, data, size) == false) return false;
|
||||
serializer s(data, size);
|
||||
delete[] data;
|
||||
return GameBoy::system.unserialize(s);
|
||||
}
|
||||
|
||||
void InterfaceGameBoy::setCheatCodes(const lstring &list) {
|
||||
GameBoy::cheat.reset();
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
GameBoy::cheat[n] = list[n];
|
||||
GameBoy::cheat[n].enable = true;
|
||||
}
|
||||
GameBoy::cheat.synchronize();
|
||||
return unserialize(s);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void InterfaceGameBoy::video_refresh(const uint8_t *data) {
|
||||
void InterfaceGameBoy::videoRefresh(const uint8_t *data) {
|
||||
interface->video_refresh();
|
||||
|
||||
uint32_t *output;
|
||||
|
@ -63,7 +52,7 @@ void InterfaceGameBoy::video_refresh(const uint8_t *data) {
|
|||
}
|
||||
}
|
||||
|
||||
void InterfaceGameBoy::audio_sample(int16_t csample, int16_t lsample, int16_t rsample) {
|
||||
void InterfaceGameBoy::audioSample(int16_t csample, int16_t lsample, int16_t rsample) {
|
||||
dspaudio.sample(lsample, rsample);
|
||||
while(dspaudio.pending()) {
|
||||
signed lsample, rsample;
|
||||
|
@ -72,7 +61,7 @@ void InterfaceGameBoy::audio_sample(int16_t csample, int16_t lsample, int16_t rs
|
|||
}
|
||||
}
|
||||
|
||||
bool InterfaceGameBoy::input_poll(unsigned id) {
|
||||
bool InterfaceGameBoy::inputPoll(unsigned id) {
|
||||
switch((GameBoy::Input)id) {
|
||||
case GameBoy::Input::Up: return interface->inputState[keyboard(0)[Keyboard::Up]];
|
||||
case GameBoy::Input::Down: return interface->inputState[keyboard(0)[Keyboard::Down]];
|
||||
|
|
|
@ -4,9 +4,8 @@ struct InterfaceGameBoy : GameBoy::Interface {
|
|||
|
||||
bool saveState(const string &filename);
|
||||
bool loadState(const string &filename);
|
||||
void setCheatCodes(const lstring &list);
|
||||
|
||||
void video_refresh(const uint8_t *data);
|
||||
void audio_sample(int16_t csample, int16_t lsample, int16_t rsample);
|
||||
bool input_poll(unsigned id);
|
||||
void videoRefresh(const uint8_t *data);
|
||||
void audioSample(int16_t csample, int16_t lsample, int16_t rsample);
|
||||
bool inputPoll(unsigned id);
|
||||
};
|
||||
|
|
|
@ -6,21 +6,25 @@ Interface *interface = 0;
|
|||
|
||||
bool Interface::loaded() {
|
||||
switch(mode()) {
|
||||
case Mode::NES: return NES::cartridge.loaded();
|
||||
case Mode::SNES: return SNES::cartridge.loaded();
|
||||
case Mode::GameBoy: return GameBoy::cartridge.loaded();
|
||||
default: return false;
|
||||
case Mode::NES: return nes.cartridgeLoaded();
|
||||
case Mode::SNES: return snes.cartridgeLoaded();
|
||||
case Mode::GameBoy: return gameBoy.cartridgeLoaded();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Interface::loadCartridge(const string &filename) {
|
||||
bool result = false;
|
||||
setCheatCodes();
|
||||
unloadCartridge();
|
||||
if(filename.endswith(".nes")) result = loadCartridgeNES(filename);
|
||||
if(filename.endswith(".sfc")) result = loadCartridgeSNES(filename);
|
||||
if(filename.endswith(".gb" )) result = loadCartridgeGameBoy(filename);
|
||||
if(filename.endswith(".gbc")) result = loadCartridgeGameBoy(filename);
|
||||
if(result == true) cheatEditor->load({ baseName, ".cht" });
|
||||
if(result == true) {
|
||||
cheatEditor->load({ baseName, ".cht" });
|
||||
stateManager->load({ baseName, ".bsa" }, 0u);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -33,10 +37,11 @@ bool Interface::loadCartridgeNES(const string &filename) {
|
|||
void Interface::unloadCartridge() {
|
||||
if(loaded() == false) return;
|
||||
cheatEditor->save({ baseName, ".cht" });
|
||||
stateManager->save({ baseName, ".bsa" }, 0u);
|
||||
|
||||
switch(mode()) {
|
||||
case Mode::NES: nes.unloadCartridge(); break;
|
||||
case Mode::SNES: snes.unloadCartridge(); break;
|
||||
case Mode::NES: nes.unloadCartridge(); break;
|
||||
case Mode::SNES: snes.unloadCartridge(); break;
|
||||
case Mode::GameBoy: gameBoy.unloadCartridge(); break;
|
||||
}
|
||||
|
||||
|
@ -72,36 +77,44 @@ void Interface::unloadCartridgeGameBoy() {
|
|||
|
||||
void Interface::power() {
|
||||
switch(mode()) {
|
||||
case Mode::NES: return NES::system.power();
|
||||
case Mode::SNES: return SNES::system.power();
|
||||
case Mode::GameBoy: return GameBoy::system.power();
|
||||
case Mode::NES: return nes.power();
|
||||
case Mode::SNES: return snes.power();
|
||||
case Mode::GameBoy: return gameBoy.power();
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::reset() {
|
||||
switch(mode()) {
|
||||
case Mode::NES: return NES::system.reset();
|
||||
case Mode::SNES: return SNES::system.reset();
|
||||
case Mode::GameBoy: return GameBoy::system.power(); //Game Boy lacks reset button
|
||||
case Mode::NES: return nes.reset();
|
||||
case Mode::SNES: return snes.reset();
|
||||
case Mode::GameBoy: return gameBoy.power(); //Game Boy lacks reset button
|
||||
}
|
||||
}
|
||||
|
||||
void Interface::run() {
|
||||
switch(mode()) {
|
||||
case Mode::NES:
|
||||
return NES::system.run();
|
||||
|
||||
case Mode::SNES:
|
||||
return SNES::system.run();
|
||||
|
||||
case Mode::GameBoy:
|
||||
do {
|
||||
GameBoy::system.run();
|
||||
} while(GameBoy::scheduler.exit_reason() != GameBoy::Scheduler::ExitReason::FrameEvent);
|
||||
return;
|
||||
case Mode::NES: return nes.run();
|
||||
case Mode::SNES: return snes.run();
|
||||
case Mode::GameBoy: return gameBoy.run();
|
||||
}
|
||||
}
|
||||
|
||||
serializer Interface::serialize() {
|
||||
switch(mode()) {
|
||||
case Mode::SNES: return snes.serialize();
|
||||
case Mode::GameBoy: return gameBoy.serialize();
|
||||
}
|
||||
return serializer();
|
||||
}
|
||||
|
||||
bool Interface::unserialize(serializer &s) {
|
||||
switch(mode()) {
|
||||
case Mode::SNES: return snes.unserialize(s);
|
||||
case Mode::GameBoy: return gameBoy.unserialize(s);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Interface::saveState(const string &filename) {
|
||||
switch(mode()) {
|
||||
case Mode::SNES: return snes.saveState(filename);
|
||||
|
@ -120,17 +133,17 @@ bool Interface::loadState(const string &filename) {
|
|||
|
||||
void Interface::setCheatCodes(const lstring &list) {
|
||||
switch(mode()) {
|
||||
case Mode::NES: return nes.setCheatCodes(list);
|
||||
case Mode::SNES: return snes.setCheatCodes(list);
|
||||
case Mode::GameBoy: return gameBoy.setCheatCodes(list);
|
||||
case Mode::NES: return nes.setCheats(list);
|
||||
case Mode::SNES: return snes.setCheats(list);
|
||||
case Mode::GameBoy: return gameBoy.setCheats(list);
|
||||
}
|
||||
}
|
||||
|
||||
Interface::Interface() {
|
||||
mode = Mode::None;
|
||||
NES::system.init(&nes);
|
||||
SNES::system.init(&snes);
|
||||
GameBoy::system.init(&gameBoy);
|
||||
nes.initialize(&nes);
|
||||
snes.initialize(&snes);
|
||||
gameBoy.initialize(&gameBoy);
|
||||
}
|
||||
|
||||
//internal
|
||||
|
|
|
@ -22,9 +22,12 @@ struct Interface : property<Interface> {
|
|||
void reset();
|
||||
void run();
|
||||
|
||||
serializer serialize();
|
||||
bool unserialize(serializer&);
|
||||
|
||||
bool saveState(const string &filename);
|
||||
bool loadState(const string &filename);
|
||||
void setCheatCodes(const lstring &list);
|
||||
void setCheatCodes(const lstring &list = lstring{});
|
||||
|
||||
Interface();
|
||||
|
||||
|
|
|
@ -3,30 +3,20 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
|||
if(fp.open(filename, filemap::mode::read) == false) return false;
|
||||
|
||||
interface->baseName = nall::basename(filename);
|
||||
NES::cartridge.load("", fp.data(), fp.size());
|
||||
NES::system.power();
|
||||
NES::Interface::loadCartridge("", fp.data(), fp.size());
|
||||
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void InterfaceNES::unloadCartridge() {
|
||||
NES::cartridge.unload();
|
||||
NES::Interface::unloadCartridge();
|
||||
interface->baseName = "";
|
||||
}
|
||||
|
||||
void InterfaceNES::setCheatCodes(const lstring &list) {
|
||||
NES::cheat.reset();
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
NES::cheat[n] = list[n];
|
||||
NES::cheat[n].enable = true;
|
||||
}
|
||||
NES::cheat.synchronize();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void InterfaceNES::video_refresh(const uint16_t *data) {
|
||||
void InterfaceNES::videoRefresh(const uint16_t *data) {
|
||||
interface->video_refresh();
|
||||
|
||||
uint32_t *output;
|
||||
|
@ -45,7 +35,7 @@ void InterfaceNES::video_refresh(const uint16_t *data) {
|
|||
}
|
||||
}
|
||||
|
||||
void InterfaceNES::audio_sample(int16_t sample) {
|
||||
void InterfaceNES::audioSample(int16_t sample) {
|
||||
dspaudio.sample(sample, sample);
|
||||
while(dspaudio.pending()) {
|
||||
signed lsample, rsample;
|
||||
|
@ -54,7 +44,7 @@ void InterfaceNES::audio_sample(int16_t sample) {
|
|||
}
|
||||
}
|
||||
|
||||
int16_t InterfaceNES::input_poll(bool port, unsigned device, unsigned id) {
|
||||
int16_t InterfaceNES::inputPoll(bool port, unsigned device, unsigned id) {
|
||||
if(port == 0 && device == 0) {
|
||||
switch(id) {
|
||||
case 0: return interface->inputState[keyboard(0)[Keyboard::X]];
|
||||
|
|
|
@ -2,11 +2,9 @@ struct InterfaceNES : NES::Interface {
|
|||
bool loadCartridge(const string &filename);
|
||||
void unloadCartridge();
|
||||
|
||||
void setCheatCodes(const lstring &list);
|
||||
|
||||
void video_refresh(const uint16_t *data);
|
||||
void audio_sample(int16_t sample);
|
||||
int16_t input_poll(bool port, unsigned device, unsigned id);
|
||||
void videoRefresh(const uint16_t *data);
|
||||
void audioSample(int16_t sample);
|
||||
int16_t inputPoll(bool port, unsigned device, unsigned id);
|
||||
|
||||
InterfaceNES();
|
||||
|
||||
|
|
|
@ -5,22 +5,19 @@ bool InterfaceSNES::loadCartridge(const string &filename) {
|
|||
|
||||
interface->baseName = nall::basename(filename);
|
||||
string xml = SNESCartridge(data, size).xmlMemoryMap;
|
||||
SNES::cartridge.rom.copy(data, size);
|
||||
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, { xml });
|
||||
SNES::system.power();
|
||||
SNES::Interface::loadCartridge(xml, data, size);
|
||||
|
||||
delete[] data;
|
||||
return true;
|
||||
}
|
||||
|
||||
void InterfaceSNES::unloadCartridge() {
|
||||
SNES::cartridge.unload();
|
||||
SNES::Interface::unloadCartridge();
|
||||
interface->baseName = "";
|
||||
}
|
||||
|
||||
bool InterfaceSNES::saveState(const string &filename) {
|
||||
SNES::system.runtosave();
|
||||
serializer s = SNES::system.serialize();
|
||||
serializer s = serialize();
|
||||
return file::write(filename, s.data(), s.size());
|
||||
}
|
||||
|
||||
|
@ -30,21 +27,12 @@ bool InterfaceSNES::loadState(const string &filename) {
|
|||
if(file::read(filename, data, size) == false) return false;
|
||||
serializer s(data, size);
|
||||
delete[] data;
|
||||
return SNES::system.unserialize(s);
|
||||
}
|
||||
|
||||
void InterfaceSNES::setCheatCodes(const lstring &list) {
|
||||
SNES::cheat.reset();
|
||||
for(unsigned n = 0; n < list.size(); n++) {
|
||||
SNES::cheat[n] = list[n];
|
||||
SNES::cheat[n].enabled = true;
|
||||
}
|
||||
SNES::cheat.synchronize();
|
||||
return unserialize(s);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void InterfaceSNES::video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {
|
||||
void InterfaceSNES::videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {
|
||||
interface->video_refresh();
|
||||
|
||||
unsigned width = hires ? 512 : 256;
|
||||
|
@ -80,7 +68,7 @@ void InterfaceSNES::video_refresh(const uint16_t *data, bool hires, bool interla
|
|||
}
|
||||
}
|
||||
|
||||
void InterfaceSNES::audio_sample(int16_t lsample, int16_t rsample) {
|
||||
void InterfaceSNES::audioSample(int16_t lsample, int16_t rsample) {
|
||||
dspaudio.sample(lsample, rsample);
|
||||
while(dspaudio.pending()) {
|
||||
signed lsample, rsample;
|
||||
|
@ -89,7 +77,7 @@ void InterfaceSNES::audio_sample(int16_t lsample, int16_t rsample) {
|
|||
}
|
||||
}
|
||||
|
||||
int16_t InterfaceSNES::input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
|
||||
int16_t InterfaceSNES::inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
|
||||
if(port == 0 && device == SNES::Input::Device::Joypad) {
|
||||
switch((SNES::Input::JoypadID)id) {
|
||||
case SNES::Input::JoypadID::Up: return interface->inputState[keyboard(0)[Keyboard::Up]];
|
||||
|
|
|
@ -4,11 +4,10 @@ struct InterfaceSNES : SNES::Interface {
|
|||
|
||||
bool saveState(const string &filename);
|
||||
bool loadState(const string &filename);
|
||||
void setCheatCodes(const lstring &list);
|
||||
|
||||
void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan);
|
||||
void audio_sample(int16_t lsample, int16_t rsample);
|
||||
int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id);
|
||||
void videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan);
|
||||
void audioSample(int16_t lsample, int16_t rsample);
|
||||
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id);
|
||||
|
||||
string path(SNES::Cartridge::Slot slot, const string &hint);
|
||||
};
|
||||
|
|
|
@ -42,12 +42,13 @@ Application::Application(int argc, char **argv) : quit(false) {
|
|||
mainWindow = new MainWindow;
|
||||
fileBrowser = new FileBrowser;
|
||||
cheatEditor = new CheatEditor;
|
||||
stateManager = new StateManager;
|
||||
utility->setMode(Interface::Mode::None);
|
||||
mainWindow->setVisible();
|
||||
|
||||
video.driver(videoDriver);
|
||||
video.set(Video::Handle, mainWindow->viewport.handle());
|
||||
video.set(Video::Synchronize, true);
|
||||
video.set(Video::Synchronize, false);
|
||||
video.set(Video::Filter, 0u);
|
||||
video.init();
|
||||
|
||||
|
@ -79,6 +80,7 @@ Application::Application(int argc, char **argv) : quit(false) {
|
|||
}
|
||||
|
||||
Application::~Application() {
|
||||
delete stateManager;
|
||||
delete cheatEditor;
|
||||
delete fileBrowser;
|
||||
delete mainWindow;
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
StateManager *stateManager = 0;
|
||||
|
||||
StateManager::StateManager() {
|
||||
setTitle("State Manager");
|
||||
setGeometry({ 128, 128, 600, 360 });
|
||||
|
||||
stateList.setHeaderText("Slot", "Description");
|
||||
stateList.setHeaderVisible();
|
||||
descLabel.setText("Description:");
|
||||
loadButton.setText("Load");
|
||||
saveButton.setText("Save");
|
||||
eraseButton.setText("Erase");
|
||||
|
||||
append(layout);
|
||||
layout.setMargin(5);
|
||||
layout.append(stateList, ~0, ~0, 5);
|
||||
layout.append(descLayout, ~0, 0, 5);
|
||||
descLayout.append(descLabel, 0, 0, 5);
|
||||
descLayout.append(descEdit, ~0, 0);
|
||||
layout.append(controlLayout, ~0, 0);
|
||||
controlLayout.append(spacer, ~0, 0);
|
||||
controlLayout.append(loadButton, 80, 0, 5);
|
||||
controlLayout.append(saveButton, 80, 0, 5);
|
||||
controlLayout.append(eraseButton, 80, 0);
|
||||
|
||||
for(unsigned n = 0; n < 32; n++) stateList.append(decimal<2>(n + 1), "(empty)");
|
||||
stateList.autoSizeColumns();
|
||||
|
||||
synchronize();
|
||||
|
||||
stateList.onActivate = { &StateManager::slotLoad, this };
|
||||
stateList.onChange = { &StateManager::synchronize, this };
|
||||
descEdit.onChange = { &StateManager::slotSaveDescription, this };
|
||||
loadButton.onTick = { &StateManager::slotLoad, this };
|
||||
saveButton.onTick = { &StateManager::slotSave, this };
|
||||
eraseButton.onTick = { &StateManager::slotErase, this };
|
||||
}
|
||||
|
||||
void StateManager::synchronize() {
|
||||
layout.setEnabled(interface->loaded());
|
||||
|
||||
descEdit.setText("");
|
||||
descEdit.setEnabled(false);
|
||||
controlLayout.setEnabled(stateList.selected());
|
||||
if(stateList.selected() == false) return;
|
||||
|
||||
if(slot[stateList.selection()].capacity() > 0) {
|
||||
descEdit.setText(slotLoadDescription(stateList.selection()));
|
||||
descEdit.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void StateManager::refresh() {
|
||||
for(unsigned n = 0; n < 32; n++) {
|
||||
stateList.modify(n, decimal<2>(n + 1), slotLoadDescription(n));
|
||||
}
|
||||
stateList.autoSizeColumns();
|
||||
}
|
||||
|
||||
bool StateManager::load(const string &filename, unsigned revision) {
|
||||
for(unsigned n = 0; n < 32; n++) slot[n] = serializer();
|
||||
synchronize();
|
||||
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::read) == false) return false;
|
||||
|
||||
if(fp.readl(4) == 0x31415342) {
|
||||
if(fp.readl(4) == revision) { //'BSA1'
|
||||
for(unsigned n = 0; n < 32; n++) {
|
||||
if(fp.read() == false) continue;
|
||||
unsigned size = fp.readl(4);
|
||||
uint8_t *data = new uint8_t[size];
|
||||
fp.read(data, size);
|
||||
slot[n] = serializer(data, size);
|
||||
delete[] data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StateManager::save(const string &filename, unsigned revision) {
|
||||
bool hasSave = false;
|
||||
for(unsigned n = 0; n < 32; n++) {
|
||||
if(slot[n].capacity() > 0) hasSave = true;
|
||||
}
|
||||
|
||||
if(hasSave == false) {
|
||||
unlink(filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
file fp;
|
||||
if(fp.open(filename, file::mode::write) == false) return false;
|
||||
|
||||
fp.writel(0x31415342, 4); //'BSA1'
|
||||
fp.writel(revision, 4);
|
||||
for(unsigned n = 0; n < 32; n++) {
|
||||
if(slot[n].capacity() == 0) {
|
||||
fp.write(false);
|
||||
} else {
|
||||
fp.write(true);
|
||||
fp.writel(slot[n].size(), 4);
|
||||
fp.write(slot[n].data(), slot[n].size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StateManager::slotLoad() {
|
||||
if(stateList.selected() == false) return;
|
||||
serializer s(slot[stateList.selection()].data(), slot[stateList.selection()].capacity());
|
||||
interface->unserialize(s);
|
||||
}
|
||||
|
||||
void StateManager::slotSave() {
|
||||
if(stateList.selected()) {
|
||||
slot[stateList.selection()] = interface->serialize();
|
||||
}
|
||||
refresh();
|
||||
synchronize();
|
||||
descEdit.setFocused();
|
||||
}
|
||||
|
||||
void StateManager::slotErase() {
|
||||
if(stateList.selected()) {
|
||||
slot[stateList.selection()] = serializer();
|
||||
}
|
||||
refresh();
|
||||
synchronize();
|
||||
}
|
||||
|
||||
string StateManager::slotLoadDescription(unsigned n) {
|
||||
if(slot[n].capacity() == 0) return "(empty)";
|
||||
char text[DescriptionLength];
|
||||
strlcpy(text, (const char*)slot[n].data() + HeaderLength, DescriptionLength);
|
||||
return text;
|
||||
}
|
||||
|
||||
void StateManager::slotSaveDescription() {
|
||||
if(stateList.selected() == false) return;
|
||||
string text = descEdit.text();
|
||||
if(slot[stateList.selection()].capacity() > 0) {
|
||||
strlcpy((char*)slot[stateList.selection()].data() + HeaderLength, (const char*)text, DescriptionLength);
|
||||
}
|
||||
refresh();
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
struct StateManager : Window {
|
||||
VerticalLayout layout;
|
||||
ListView stateList;
|
||||
HorizontalLayout descLayout;
|
||||
Label descLabel;
|
||||
LineEdit descEdit;
|
||||
HorizontalLayout controlLayout;
|
||||
Widget spacer;
|
||||
Button loadButton;
|
||||
Button saveButton;
|
||||
Button eraseButton;
|
||||
|
||||
void synchronize();
|
||||
void refresh();
|
||||
|
||||
bool load(const string &filename, unsigned revision);
|
||||
bool save(const string &filename, unsigned revision);
|
||||
|
||||
void slotLoad();
|
||||
void slotSave();
|
||||
void slotErase();
|
||||
|
||||
string slotLoadDescription(unsigned n);
|
||||
void slotSaveDescription();
|
||||
|
||||
StateManager();
|
||||
|
||||
private:
|
||||
enum : unsigned {
|
||||
//these valus are standardized across all emulated platforms:
|
||||
//{ uint32 signature, version, checksum; char description[512]; ... }
|
||||
HeaderLength = 12,
|
||||
DescriptionLength = 512,
|
||||
};
|
||||
serializer slot[32];
|
||||
};
|
||||
|
||||
extern StateManager *stateManager;
|
|
@ -1,2 +1,3 @@
|
|||
#include "../base.hpp"
|
||||
#include "cheat-editor.cpp"
|
||||
#include "state-manager.cpp"
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#include "cheat-editor.hpp"
|
||||
#include "state-manager.hpp"
|
||||
|
|
Loading…
Reference in New Issue