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:
Tim Allen 2011-09-15 22:41:49 +10:00
parent 5f099b8ad0
commit e3c7bbfb63
65 changed files with 715 additions and 398 deletions

View File

@ -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/)

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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> {

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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/)

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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> {

View File

@ -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);

View File

@ -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");
}
}

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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";
}
}

View File

@ -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();
}

View File

@ -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() {

View File

@ -1,11 +1,9 @@
struct System {
Interface *interface;
void run();
void power();
void reset();
void init(Interface *interface);
void init();
void term();
};

View File

@ -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/)

View File

@ -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)
);

View File

@ -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." });
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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];

View File

@ -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)) {

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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; // ||

View File

@ -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);
}

View File

@ -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)) {

View File

@ -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;

View File

@ -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");
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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(

View File

@ -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

View File

@ -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();

View File

@ -45,6 +45,7 @@ struct MainWindow : Window {
Separator toolsSeparator;
Item toolsShrinkWindow;
Item toolsCheatEditor;
Item toolsStateManager;
CheckItem toolsTest;
Menu helpMenu;

View File

@ -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]];

View File

@ -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);
};

View File

@ -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

View File

@ -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();

View File

@ -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]];

View File

@ -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();

View File

@ -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]];

View File

@ -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);
};

View File

@ -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;

148
bsnes/ui/tools/state-manager.cpp Executable file
View File

@ -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();
}

View File

@ -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;

View File

@ -1,2 +1,3 @@
#include "../base.hpp"
#include "cheat-editor.cpp"
#include "state-manager.cpp"

View File

@ -1 +1,2 @@
#include "cheat-editor.hpp"
#include "state-manager.hpp"