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-memory gameboy-cartridge
|
||||||
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
|
gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd
|
||||||
gameboy_objects += gameboy-cheat
|
gameboy_objects += gameboy-cheat
|
||||||
objects += $(gameboy_objects)
|
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-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/)
|
||||||
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
|
obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler.cpp $(call rwildcard,$(gameboy)/scheduler/)
|
||||||
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
|
obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge.cpp $(call rwildcard,$(gameboy)/cartridge/)
|
||||||
|
|
|
@ -46,7 +46,7 @@ void APU::main() {
|
||||||
noise.run();
|
noise.run();
|
||||||
master.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);
|
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,38 +54,8 @@ void Cheat::synchronize() {
|
||||||
foreach(n, override) n = false;
|
foreach(n, override) n = false;
|
||||||
|
|
||||||
for(unsigned n = 0; n < size(); n++) {
|
for(unsigned n = 0; n < size(); n++) {
|
||||||
const CheatCode &code = operator[](n);
|
override[operator[](n).addr] = true;
|
||||||
|
|
||||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
|
||||||
override[code.addr[n]] = 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 {
|
struct CheatCode {
|
||||||
bool enable;
|
unsigned addr;
|
||||||
array<unsigned> addr;
|
unsigned data;
|
||||||
array<unsigned> data;
|
unsigned comp;
|
||||||
array<unsigned> comp;
|
|
||||||
|
|
||||||
bool operator=(const string&);
|
|
||||||
CheatCode(const string&);
|
|
||||||
CheatCode();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Cheat : public linear_vector<CheatCode> {
|
struct Cheat : public linear_vector<CheatCode> {
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
void CPU::mmio_joyp_poll() {
|
void CPU::mmio_joyp_poll() {
|
||||||
unsigned button = 0, dpad = 0;
|
unsigned button = 0, dpad = 0;
|
||||||
|
|
||||||
button |= system.interface->input_poll((unsigned)Input::Start) << 3;
|
button |= interface->inputPoll((unsigned)Input::Start) << 3;
|
||||||
button |= system.interface->input_poll((unsigned)Input::Select) << 2;
|
button |= interface->inputPoll((unsigned)Input::Select) << 2;
|
||||||
button |= system.interface->input_poll((unsigned)Input::B) << 1;
|
button |= interface->inputPoll((unsigned)Input::B) << 1;
|
||||||
button |= system.interface->input_poll((unsigned)Input::A) << 0;
|
button |= interface->inputPoll((unsigned)Input::A) << 0;
|
||||||
|
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Down) << 3;
|
dpad |= interface->inputPoll((unsigned)Input::Down) << 3;
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Up) << 2;
|
dpad |= interface->inputPoll((unsigned)Input::Up) << 2;
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Left) << 1;
|
dpad |= interface->inputPoll((unsigned)Input::Left) << 1;
|
||||||
dpad |= system.interface->input_poll((unsigned)Input::Right) << 0;
|
dpad |= interface->inputPoll((unsigned)Input::Right) << 0;
|
||||||
|
|
||||||
status.joyp = 0x0f;
|
status.joyp = 0x0f;
|
||||||
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req;
|
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
|
if(addr == 0xff00) { //JOYP
|
||||||
status.p15 = data & 0x20;
|
status.p15 = data & 0x20;
|
||||||
status.p14 = data & 0x10;
|
status.p14 = data & 0x10;
|
||||||
system.interface->joyp_write(status.p15, status.p14);
|
interface->joypWrite(status.p15, status.p14);
|
||||||
mmio_joyp_poll();
|
mmio_joyp_poll();
|
||||||
return;
|
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 {
|
class Interface {
|
||||||
public:
|
public:
|
||||||
virtual void lcd_scanline() {}
|
virtual void lcdScanline();
|
||||||
virtual void joyp_write(bool p15, bool p14) {}
|
virtual void joypWrite(bool p15, bool p14);
|
||||||
|
|
||||||
virtual void video_refresh(const uint8_t *data) {}
|
virtual void videoRefresh(const uint8_t *data);
|
||||||
virtual void audio_sample(int16_t center, int16_t left, int16_t right) {}
|
virtual void audioSample(int16_t center, int16_t left, int16_t right);
|
||||||
virtual void input_poll() {}
|
virtual bool inputPoll(unsigned id);
|
||||||
virtual bool input_poll(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() {
|
void LCD::frame() {
|
||||||
system.interface->video_refresh(screen);
|
interface->videoRefresh(screen);
|
||||||
system.interface->input_poll();
|
|
||||||
cpu.mmio_joyp_poll();
|
cpu.mmio_joyp_poll();
|
||||||
|
|
||||||
status.ly = 0;
|
status.ly = 0;
|
||||||
|
@ -80,7 +79,7 @@ void LCD::render() {
|
||||||
|
|
||||||
uint8_t *output = screen + status.ly * 160;
|
uint8_t *output = screen + status.ly * 160;
|
||||||
for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55;
|
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) {
|
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);
|
uint8 data = mmio[addr]->mmio_read(addr);
|
||||||
|
|
||||||
if(cheat.override[addr]) {
|
if(cheat.override[addr]) {
|
||||||
for(unsigned x = 0; x < cheat.size(); x++) {
|
for(unsigned n = 0; n < cheat.size(); n++) {
|
||||||
const CheatCode &code = cheat[x];
|
if(cheat[n].addr == addr) {
|
||||||
for(unsigned y = 0; y < code.addr.size(); y++) {
|
if(cheat[n].comp > 255 || cheat[n].comp == data) {
|
||||||
if(code.addr[y] == addr) {
|
return cheat[n].data;
|
||||||
if(code.comp[y] > 255 || code.comp[y] == data) {
|
|
||||||
return code.data[y];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,8 @@ void System::runthreadtosave() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::init(Interface *interface_) {
|
void System::init() {
|
||||||
interface = interface_;
|
assert(interface != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::load() {
|
void System::load() {
|
||||||
|
@ -49,11 +49,6 @@ void System::power() {
|
||||||
lcd.power();
|
lcd.power();
|
||||||
scheduler.init();
|
scheduler.init();
|
||||||
|
|
||||||
// cheat.reset();
|
|
||||||
// cheat.append(CheatCode("3EB-81B-4CA"));
|
|
||||||
// cheat[0].enable = true;
|
|
||||||
// cheat.synchronize();
|
|
||||||
|
|
||||||
clocks_executed = 0;
|
clocks_executed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,10 @@ struct System {
|
||||||
void runtosave();
|
void runtosave();
|
||||||
void runthreadtosave();
|
void runthreadtosave();
|
||||||
|
|
||||||
void init(Interface*);
|
void init();
|
||||||
void load();
|
void load();
|
||||||
void power();
|
void power();
|
||||||
|
|
||||||
Interface *interface;
|
|
||||||
unsigned clocks_executed;
|
unsigned clocks_executed;
|
||||||
|
|
||||||
//serialization.cpp
|
//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-mapper nes-cartridge nes-memory
|
||||||
nes_objects += nes-cpu nes-apu nes-ppu
|
nes_objects += nes-cpu nes-apu nes-ppu
|
||||||
nes_objects += nes-cheat
|
nes_objects += nes-cheat
|
||||||
objects += $(nes_objects)
|
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-system.o: $(nes)/system/system.cpp $(call rwildcard,$(nes)/system/)
|
||||||
obj/nes-scheduler.o: $(nes)/scheduler/scheduler.cpp $(call rwildcard,$(nes)/scheduler/)
|
obj/nes-scheduler.o: $(nes)/scheduler/scheduler.cpp $(call rwildcard,$(nes)/scheduler/)
|
||||||
obj/nes-mapper.o: $(nes)/mapper/mapper.cpp $(call rwildcard,$(nes)/mapper/)
|
obj/nes-mapper.o: $(nes)/mapper/mapper.cpp $(call rwildcard,$(nes)/mapper/)
|
||||||
|
|
|
@ -42,7 +42,7 @@ void APU::main() {
|
||||||
clock_frame_counter_divider();
|
clock_frame_counter_divider();
|
||||||
|
|
||||||
signed output = rectangle_dac[rectangle_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
|
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();
|
tick();
|
||||||
}
|
}
|
||||||
|
@ -257,8 +257,8 @@ void APU::Sweep::clock() {
|
||||||
signed delta = rectangle_period >> shift;
|
signed delta = rectangle_period >> shift;
|
||||||
|
|
||||||
if(decrement) {
|
if(decrement) {
|
||||||
rectangle_period -= delta;
|
//first rectangle decrements by one extra
|
||||||
//decrement first square wave by one extra
|
rectangle_period -= delta + (channel == 0);
|
||||||
} else if((rectangle_period + delta) < 0x800) {
|
} else if((rectangle_period + delta) < 0x800) {
|
||||||
rectangle_period += delta;
|
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) {
|
if(reload_linear) {
|
||||||
linear_length_counter = linear_length;
|
linear_length_counter = linear_length;
|
||||||
} else if(linear_length_counter) {
|
} else if(linear_length_counter) {
|
||||||
|
@ -339,7 +339,8 @@ void APU::Triangle::clock_length_counter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 APU::Triangle::clock() {
|
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(length_counter == 0 || linear_length_counter == 0) return result;
|
||||||
|
|
||||||
if(--period_counter == 0) {
|
if(--period_counter == 0) {
|
||||||
|
@ -397,7 +398,7 @@ void APU::clock_frame_counter() {
|
||||||
|
|
||||||
rectangle[0].envelope.clock();
|
rectangle[0].envelope.clock();
|
||||||
rectangle[1].envelope.clock();
|
rectangle[1].envelope.clock();
|
||||||
triangle.clock_length_counter();
|
triangle.clock_linear_length();
|
||||||
noise.envelope.clock();
|
noise.envelope.clock();
|
||||||
|
|
||||||
if(frame.counter == 0) {
|
if(frame.counter == 0) {
|
||||||
|
@ -414,6 +415,9 @@ void APU::clock_frame_counter_divider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
APU::APU() {
|
APU::APU() {
|
||||||
|
rectangle[0].sweep.channel = 0;
|
||||||
|
rectangle[1].sweep.channel = 1;
|
||||||
|
|
||||||
for(unsigned amp = 0; amp < 32; amp++) {
|
for(unsigned amp = 0; amp < 32; amp++) {
|
||||||
if(amp == 0) {
|
if(amp == 0) {
|
||||||
rectangle_dac[amp] = 0;
|
rectangle_dac[amp] = 0;
|
||||||
|
|
|
@ -25,6 +25,8 @@ struct APU : Processor {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Sweep {
|
struct Sweep {
|
||||||
|
bool channel;
|
||||||
|
|
||||||
uint8 shift;
|
uint8 shift;
|
||||||
bool decrement;
|
bool decrement;
|
||||||
uint3 period;
|
uint3 period;
|
||||||
|
@ -67,7 +69,7 @@ struct APU : Processor {
|
||||||
bool reload_linear;
|
bool reload_linear;
|
||||||
|
|
||||||
void clock_length();
|
void clock_length();
|
||||||
void clock_length_counter();
|
void clock_linear_length();
|
||||||
uint8 clock();
|
uint8 clock();
|
||||||
} triangle;
|
} triangle;
|
||||||
|
|
||||||
|
@ -113,10 +115,10 @@ struct APU : Processor {
|
||||||
int16 dmc_triangle_noise_dac[128][16][16];
|
int16 dmc_triangle_noise_dac[128][16][16];
|
||||||
|
|
||||||
static const uint8 length_counter_table[32];
|
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 ntsc_dmc_period_table[16];
|
||||||
static const uint16 pal_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;
|
extern APU apu;
|
||||||
|
|
|
@ -51,38 +51,8 @@ void Cheat::synchronize() {
|
||||||
foreach(n, override) n = false;
|
foreach(n, override) n = false;
|
||||||
|
|
||||||
for(unsigned n = 0; n < size(); n++) {
|
for(unsigned n = 0; n < size(); n++) {
|
||||||
const CheatCode &code = operator[](n);
|
override[operator[](n).addr] = true;
|
||||||
|
|
||||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
|
||||||
override[code.addr[n]] = 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 {
|
struct CheatCode {
|
||||||
bool enable;
|
unsigned addr;
|
||||||
array<unsigned> addr;
|
unsigned data;
|
||||||
array<unsigned> data;
|
unsigned comp;
|
||||||
array<unsigned> comp;
|
|
||||||
|
|
||||||
bool operator=(const string&);
|
|
||||||
CheatCode(const string&);
|
|
||||||
CheatCode();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Cheat : public linear_vector<CheatCode> {
|
struct Cheat : public linear_vector<CheatCode> {
|
||||||
|
|
|
@ -91,12 +91,12 @@ void CPU::ram_write(uint16 addr, uint8 data) {
|
||||||
uint8 CPU::read(uint16 addr) {
|
uint8 CPU::read(uint16 addr) {
|
||||||
if(addr == 0x4016) {
|
if(addr == 0x4016) {
|
||||||
if(status.controller_port0 >= 8) return (mdr() & 0xc0) | 1;
|
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(addr == 0x4017) {
|
||||||
if(status.controller_port1 >= 8) return (mdr() & 0xc0) | 1;
|
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);
|
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 {
|
struct Interface {
|
||||||
virtual void video_refresh(const uint16_t *data) {}
|
virtual void videoRefresh(const uint16_t *data);
|
||||||
virtual void audio_sample(int16_t sample) {}
|
virtual void audioSample(int16_t sample);
|
||||||
virtual int16_t input_poll(bool port, unsigned device, unsigned id) { return 0; }
|
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(addr <= 0x4017) return cpu.read(addr);
|
||||||
|
|
||||||
if(cheat.override[addr]) {
|
if(cheat.override[addr]) {
|
||||||
for(unsigned x = 0; x < cheat.size(); x++) {
|
for(unsigned n = 0; n < cheat.size(); n++) {
|
||||||
const CheatCode &code = cheat[x];
|
if(cheat[n].addr == addr) {
|
||||||
for(unsigned y = 0; y < code.addr.size(); y++) {
|
if(cheat[n].comp > 255 || cheat[n].comp == data) {
|
||||||
if(code.addr[y] == addr) {
|
return cheat[n].data;
|
||||||
if(code.comp[y] > 255 || code.comp[y] == data) {
|
|
||||||
return code.data[y];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace NES {
|
namespace NES {
|
||||||
namespace Info {
|
namespace Info {
|
||||||
static const char Name[] = "bnes";
|
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() {
|
void PPU::frame_edge() {
|
||||||
status.field ^= 1;
|
status.field ^= 1;
|
||||||
system.interface->video_refresh(buffer);
|
interface->videoRefresh(buffer);
|
||||||
scheduler.exit();
|
scheduler.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,6 @@ void System::power() {
|
||||||
apu.power();
|
apu.power();
|
||||||
ppu.power();
|
ppu.power();
|
||||||
scheduler.power();
|
scheduler.power();
|
||||||
|
|
||||||
// cheat.reset();
|
|
||||||
// cheat.append(CheatCode("GXXZZLVI"));
|
|
||||||
// cheat[0].enable = true;
|
|
||||||
// cheat.synchronize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::reset() {
|
void System::reset() {
|
||||||
|
@ -29,8 +24,8 @@ void System::reset() {
|
||||||
scheduler.reset();
|
scheduler.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::init(Interface *interface) {
|
void System::init() {
|
||||||
this->interface = interface;
|
assert(interface != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::term() {
|
void System::term() {
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
struct System {
|
struct System {
|
||||||
Interface *interface;
|
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void power();
|
void power();
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void init(Interface *interface);
|
void init();
|
||||||
void term();
|
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-cartridge snes-cheat
|
||||||
snes_objects += snes-memory snes-cpucore snes-smpcore
|
snes_objects += snes-memory snes-cpucore snes-smpcore
|
||||||
snes_objects += snes-cpu snes-smp snes-dsp snes-ppu
|
snes_objects += snes-cpu snes-smp snes-dsp snes-ppu
|
||||||
|
@ -28,6 +28,7 @@ else ifeq ($(profile),performance)
|
||||||
snesppu := $(snes)/alt/ppu-performance
|
snesppu := $(snes)/alt/ppu-performance
|
||||||
endif
|
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-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-controller.o: $(snes)/controller/controller.cpp $(call rwildcard,$(snes)/controller/)
|
||||||
obj/snes-memory.o : $(snes)/memory/memory.cpp $(call rwildcard,$(snes)/memory/)
|
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) {
|
void Audio::sample(int16 left, int16 right) {
|
||||||
if(coprocessor == false) {
|
if(coprocessor == false) {
|
||||||
system.interface->audio_sample(left, right);
|
interface->audioSample(left, right);
|
||||||
} else {
|
} else {
|
||||||
dsp_buffer[dsp_wroffset] = ((uint16)left << 0) + ((uint16)right << 16);
|
dsp_buffer[dsp_wroffset] = ((uint16)left << 0) + ((uint16)right << 16);
|
||||||
dsp_wroffset = (dsp_wroffset + 1) & buffer_mask;
|
dsp_wroffset = (dsp_wroffset + 1) & buffer_mask;
|
||||||
|
@ -61,7 +61,7 @@ void Audio::flush() {
|
||||||
int cop_left = (int16)(cop_sample >> 0);
|
int cop_left = (int16)(cop_sample >> 0);
|
||||||
int cop_right = (int16)(cop_sample >> 16);
|
int cop_right = (int16)(cop_sample >> 16);
|
||||||
|
|
||||||
system.interface->audio_sample(
|
interface->audioSample(
|
||||||
sclamp<16>((dsp_left + cop_left ) / 2),
|
sclamp<16>((dsp_left + cop_left ) / 2),
|
||||||
sclamp<16>((dsp_right + cop_right) / 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 promsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 2048 : 16384);
|
||||||
unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048);
|
unsigned dromsize = (necdsp.revision == NECDSP::Revision::uPD7725 ? 1024 : 2048);
|
||||||
unsigned filesize = promsize * 3 + dromsize * 2;
|
unsigned filesize = promsize * 3 + dromsize * 2;
|
||||||
|
|
||||||
file fp;
|
file fp;
|
||||||
if(fp.open(path, file::mode::read) == false) {
|
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) {
|
} 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();
|
fp.close();
|
||||||
} else {
|
} else {
|
||||||
for(unsigned n = 0; n < promsize; n++) necdsp.programROM[n] = fp.readm(3);
|
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);
|
fp.read(data, filesize);
|
||||||
|
|
||||||
if(sha256 != nall::sha256(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;
|
file fp;
|
||||||
if(fp.open(path, file::mode::read) == false) {
|
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) {
|
} 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();
|
fp.close();
|
||||||
} else {
|
} else {
|
||||||
for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = fp.readl(3);
|
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);
|
fp.read(data, 3072);
|
||||||
|
|
||||||
if(sha256 != nall::sha256(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,16 +16,12 @@ void Cheat::enable(bool state) {
|
||||||
|
|
||||||
void Cheat::synchronize() {
|
void Cheat::synchronize() {
|
||||||
memset(override, 0x00, 16 * 1024 * 1024);
|
memset(override, 0x00, 16 * 1024 * 1024);
|
||||||
code_enabled = false;
|
code_enabled = size() > 0;
|
||||||
|
|
||||||
for(unsigned i = 0; i < size(); i++) {
|
for(unsigned i = 0; i < size(); i++) {
|
||||||
const CheatCode &code = operator[](i);
|
const CheatCode &code = operator[](i);
|
||||||
if(code.enabled == false) continue;
|
|
||||||
|
|
||||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
unsigned addr = mirror(code.addr);
|
||||||
code_enabled = true;
|
|
||||||
|
|
||||||
unsigned addr = mirror(code.addr[n]);
|
|
||||||
override[addr] = true;
|
override[addr] = true;
|
||||||
if((addr & 0xffe000) == 0x7e0000) {
|
if((addr & 0xffe000) == 0x7e0000) {
|
||||||
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
|
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
|
||||||
|
@ -39,7 +35,6 @@ void Cheat::synchronize() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cheat_enabled = system_enabled && code_enabled;
|
cheat_enabled = system_enabled && code_enabled;
|
||||||
}
|
}
|
||||||
|
@ -49,12 +44,8 @@ uint8 Cheat::read(unsigned addr) const {
|
||||||
|
|
||||||
for(unsigned i = 0; i < size(); i++) {
|
for(unsigned i = 0; i < size(); i++) {
|
||||||
const CheatCode &code = operator[](i);
|
const CheatCode &code = operator[](i);
|
||||||
if(code.enabled == false) continue;
|
if(addr == mirror(code.addr)) {
|
||||||
|
return code.data;
|
||||||
for(unsigned n = 0; n < code.addr.size(); n++) {
|
|
||||||
if(addr == mirror(code.addr[n])) {
|
|
||||||
return code.data[n];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +69,7 @@ Cheat::~Cheat() {
|
||||||
//encode / decode
|
//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;
|
string t = s;
|
||||||
t.lower();
|
t.lower();
|
||||||
|
|
||||||
|
@ -128,7 +119,7 @@ bool Cheat::decode(const char *s, unsigned &addr, uint8 &data, Type &type) {
|
||||||
#undef ischr
|
#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];
|
char t[16];
|
||||||
|
|
||||||
if(type == Type::ProActionReplay) {
|
if(type == Type::ProActionReplay) {
|
||||||
|
@ -166,36 +157,4 @@ unsigned Cheat::mirror(unsigned addr) const {
|
||||||
return addr;
|
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 {
|
struct CheatCode {
|
||||||
bool enabled;
|
unsigned addr;
|
||||||
array<unsigned> addr;
|
unsigned data;
|
||||||
array<uint8> data;
|
|
||||||
|
|
||||||
bool operator=(string);
|
|
||||||
CheatCode();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Cheat : public linear_vector<CheatCode> {
|
class Cheat : public linear_vector<CheatCode> {
|
||||||
|
@ -21,8 +17,8 @@ public:
|
||||||
Cheat();
|
Cheat();
|
||||||
~Cheat();
|
~Cheat();
|
||||||
|
|
||||||
static bool decode(const char*, unsigned&, uint8&, Type&);
|
static bool decode(const char*, unsigned&, unsigned&, Type&);
|
||||||
static bool encode(string&, unsigned, uint8, Type);
|
static bool encode(string&, unsigned, unsigned, Type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool system_enabled;
|
bool system_enabled;
|
||||||
|
|
|
@ -69,8 +69,8 @@ void ICD2::reset() {
|
||||||
joyp14lock = 0;
|
joyp14lock = 0;
|
||||||
pulselock = true;
|
pulselock = true;
|
||||||
|
|
||||||
GameBoy::system.init(this);
|
GameBoy::Interface::initialize(this);
|
||||||
GameBoy::system.power();
|
GameBoy::Interface::power();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifdef ICD2_CPP
|
#ifdef ICD2_CPP
|
||||||
|
|
||||||
//called on rendered lines 0-143 (not on Vblank lines 144-153)
|
//called on rendered lines 0-143 (not on Vblank lines 144-153)
|
||||||
void ICD2::lcd_scanline() {
|
void ICD2::lcdScanline() {
|
||||||
if((GameBoy::lcd.status.ly & 7) == 0) {
|
if((GameBoy::lcd.status.ly & 7) == 0) {
|
||||||
lcd.row = (lcd.row + 1) & 3;
|
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);
|
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
|
//joypad handling
|
||||||
if(p15 == 1 && p14 == 1) {
|
if(p15 == 1 && p14 == 1) {
|
||||||
if(joyp15lock == 0 && joyp14lock == 0) {
|
if(joyp15lock == 0 && joyp14lock == 0) {
|
||||||
|
@ -80,17 +80,14 @@ void ICD2::joyp_write(bool p15, bool p14) {
|
||||||
packetlock = true;
|
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);
|
audio.coprocessor_sample(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICD2::input_poll() {
|
bool ICD2::inputPoll(unsigned id) {
|
||||||
}
|
|
||||||
|
|
||||||
bool ICD2::input_poll(unsigned id) {
|
|
||||||
GameBoy::cpu.status.mlt_req = joyp_id & mlt_req;
|
GameBoy::cpu.status.mlt_req = joyp_id & mlt_req;
|
||||||
|
|
||||||
unsigned data = 0x00;
|
unsigned data = 0x00;
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
void lcd_scanline();
|
void lcdScanline();
|
||||||
void joyp_write(bool p15, bool p14);
|
void joypWrite(bool p15, bool p14);
|
||||||
void video_refresh(const uint8_t *data);
|
void videoRefresh(const uint8_t *data);
|
||||||
void audio_sample(int16_t center, int16_t left, int16_t right);
|
void audioSample(int16_t center, int16_t left, int16_t right);
|
||||||
void input_poll();
|
bool inputPoll(unsigned id);
|
||||||
bool input_poll(unsigned id);
|
|
||||||
|
|
||||||
struct Packet {
|
struct Packet {
|
||||||
uint8 data[16];
|
uint8 data[16];
|
||||||
|
|
|
@ -22,7 +22,7 @@ void Link::init() {
|
||||||
|
|
||||||
void Link::load() {
|
void Link::load() {
|
||||||
if(opened()) close();
|
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 name = program != "" ? program : notdir(basename);
|
||||||
string path = dir(basename);
|
string path = dir(basename);
|
||||||
if(open(name, path)) {
|
if(open(name, path)) {
|
||||||
|
|
|
@ -50,7 +50,7 @@ void MSU1::init() {
|
||||||
|
|
||||||
void MSU1::load() {
|
void MSU1::load() {
|
||||||
if(datafile.open()) datafile.close();
|
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() {
|
void MSU1::unload() {
|
||||||
|
@ -133,7 +133,7 @@ void MSU1::mmio_write(unsigned addr, uint8 data) {
|
||||||
if(addr == 0x2005) {
|
if(addr == 0x2005) {
|
||||||
mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
|
mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8);
|
||||||
if(audiofile.open()) audiofile.close();
|
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);
|
uint32 header = audiofile.readm(4);
|
||||||
if(header != 0x4d535531) { //verify 'MSU1' header
|
if(header != 0x4d535531) { //verify 'MSU1' header
|
||||||
audiofile.close();
|
audiofile.close();
|
||||||
|
|
|
@ -16,12 +16,12 @@ void MSU1::serialize(serializer &s) {
|
||||||
s.integer(mmio.audio_play);
|
s.integer(mmio.audio_play);
|
||||||
|
|
||||||
if(datafile.open()) datafile.close();
|
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);
|
datafile.seek(mmio.data_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(audiofile.open()) audiofile.close();
|
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);
|
audiofile.seek(mmio.audio_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
uint2 Gamepad::data() {
|
uint2 Gamepad::data() {
|
||||||
if(counter >= 16) return 1;
|
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++;
|
if(latched == 0) counter++;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,16 +18,16 @@ void Justifier::enter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(next < prev) {
|
if(next < prev) {
|
||||||
int nx1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
|
int nx1 = interface->inputPoll(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 ny1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
|
||||||
nx1 += x1;
|
nx1 += x1;
|
||||||
ny1 += y1;
|
ny1 += y1;
|
||||||
x1 = max(-16, min(256 + 16, nx1));
|
x1 = max(-16, min(256 + 16, nx1));
|
||||||
y1 = max(-16, min(240 + 16, ny1));
|
y1 = max(-16, min(240 + 16, ny1));
|
||||||
|
|
||||||
if(chained == true) {
|
if(chained == true) {
|
||||||
int nx2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
|
int nx2 = interface->inputPoll(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 ny2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
|
||||||
nx2 += x2;
|
nx2 += x2;
|
||||||
ny2 += y2;
|
ny2 += y2;
|
||||||
x2 = max(-16, min(256 + 16, nx2));
|
x2 = max(-16, min(256 + 16, nx2));
|
||||||
|
@ -48,11 +48,11 @@ uint2 Justifier::data() {
|
||||||
if(counter >= 32) return 1;
|
if(counter >= 32) return 1;
|
||||||
|
|
||||||
if(counter == 0) {
|
if(counter == 0) {
|
||||||
trigger1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger);
|
trigger1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger);
|
||||||
start1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start);
|
start1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start);
|
||||||
if(chained) {
|
if(chained) {
|
||||||
trigger2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger);
|
trigger2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger);
|
||||||
start2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start);
|
start2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
uint2 Mouse::data() {
|
uint2 Mouse::data() {
|
||||||
if(counter >= 32) return 1;
|
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_x = interface->inputPoll(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_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_x = position_x < 0; //0 = right, 1 = left
|
||||||
bool direction_y = position_y < 0; //0 = down, 1 = up
|
bool direction_y = position_y < 0; //0 = down, 1 = up
|
||||||
|
@ -25,8 +25,8 @@ uint2 Mouse::data() {
|
||||||
case 6: return 0;
|
case 6: return 0;
|
||||||
case 7: return 0;
|
case 7: return 0;
|
||||||
|
|
||||||
case 8: return system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right);
|
case 8: return interface->inputPoll(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 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 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
|
||||||
case 11: return 0; // ||
|
case 11: return 0; // ||
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@ uint2 Multitap::data() {
|
||||||
port2 = 3; //controller 4
|
port2 = 3; //controller 4
|
||||||
}
|
}
|
||||||
|
|
||||||
bool data1 = system.interface->input_poll(port, Input::Device::Multitap, port1, index);
|
bool data1 = interface->inputPoll(port, Input::Device::Multitap, port1, index);
|
||||||
bool data2 = system.interface->input_poll(port, Input::Device::Multitap, port2, index);
|
bool data2 = interface->inputPoll(port, Input::Device::Multitap, port2, index);
|
||||||
return (data2 << 1) | (data1 << 0);
|
return (data2 << 1) | (data1 << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ void Serial::latch(bool data) {
|
||||||
|
|
||||||
Serial::Serial(bool port) : Controller(port) {
|
Serial::Serial(bool port) : Controller(port) {
|
||||||
enable = false;
|
enable = false;
|
||||||
string basename = system.interface->path(Cartridge::Slot::Base, "");
|
string basename = interface->path(Cartridge::Slot::Base, "");
|
||||||
string name = notdir(basename);
|
string name = notdir(basename);
|
||||||
string path = dir(basename);
|
string path = dir(basename);
|
||||||
if(open(name, path)) {
|
if(open(name, path)) {
|
||||||
|
|
|
@ -28,8 +28,8 @@ void SuperScope::enter() {
|
||||||
|
|
||||||
if(next < prev) {
|
if(next < prev) {
|
||||||
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
|
//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 nx = interface->inputPoll(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 ny = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
|
||||||
nx += x;
|
nx += x;
|
||||||
ny += y;
|
ny += y;
|
||||||
x = max(-16, min(256 + 16, nx));
|
x = max(-16, min(256 + 16, nx));
|
||||||
|
@ -51,7 +51,7 @@ uint2 SuperScope::data() {
|
||||||
|
|
||||||
if(counter == 0) {
|
if(counter == 0) {
|
||||||
//turbo is a switch; toggle is edge sensitive
|
//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) {
|
if(newturbo && !turbo) {
|
||||||
turbo = !turbo; //toggle state
|
turbo = !turbo; //toggle state
|
||||||
turbolock = true;
|
turbolock = true;
|
||||||
|
@ -62,7 +62,7 @@ uint2 SuperScope::data() {
|
||||||
//trigger is a button
|
//trigger is a button
|
||||||
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
|
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
|
||||||
trigger = false;
|
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)) {
|
if(newtrigger && (turbo || !triggerlock)) {
|
||||||
trigger = true;
|
trigger = true;
|
||||||
triggerlock = true;
|
triggerlock = true;
|
||||||
|
@ -71,11 +71,11 @@ uint2 SuperScope::data() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//cursor is a button; it is always level sensitive
|
//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 is a button; it is always edge sensitive
|
||||||
pause = false;
|
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) {
|
if(newpause && !pauselock) {
|
||||||
pause = true;
|
pause = true;
|
||||||
pauselock = 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 {
|
class Interface {
|
||||||
public:
|
public:
|
||||||
virtual void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {}
|
virtual void videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan);
|
||||||
virtual void audio_sample(int16_t l_sample, int16_t r_sample) {}
|
virtual void audioSample(int16_t lsample, int16_t rsample);
|
||||||
virtual int16_t input_poll(bool port, Input::Device device, unsigned index, unsigned id) { return 0; }
|
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;
|
virtual string path(Cartridge::Slot slot, const string &hint) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern Interface *interface;
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
namespace SNES {
|
namespace SNES {
|
||||||
namespace Info {
|
namespace Info {
|
||||||
static const char Name[] = "bsnes";
|
static const char Name[] = "bsnes";
|
||||||
static const char Version[] = "082.12";
|
static const char Version[] = "082.13";
|
||||||
static const unsigned SerializerVersion = 21;
|
static const unsigned SerializerVersion = 22;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,16 @@ serializer System::serialize() {
|
||||||
serializer s(serialize_size);
|
serializer s(serialize_size);
|
||||||
|
|
||||||
unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = cartridge.crc32();
|
unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = cartridge.crc32();
|
||||||
char profile[16], description[512];
|
char description[512], profile[16];
|
||||||
memset(&profile, 0, sizeof profile);
|
|
||||||
memset(&description, 0, sizeof description);
|
memset(&description, 0, sizeof description);
|
||||||
|
memset(&profile, 0, sizeof profile);
|
||||||
strlcpy(profile, Info::Profile, sizeof profile);
|
strlcpy(profile, Info::Profile, sizeof profile);
|
||||||
|
|
||||||
s.integer(signature);
|
s.integer(signature);
|
||||||
s.integer(version);
|
s.integer(version);
|
||||||
s.integer(crc32);
|
s.integer(crc32);
|
||||||
s.array(profile);
|
|
||||||
s.array(description);
|
s.array(description);
|
||||||
|
s.array(profile);
|
||||||
|
|
||||||
serialize_all(s);
|
serialize_all(s);
|
||||||
return s;
|
return s;
|
||||||
|
@ -21,13 +21,13 @@ serializer System::serialize() {
|
||||||
|
|
||||||
bool System::unserialize(serializer &s) {
|
bool System::unserialize(serializer &s) {
|
||||||
unsigned signature, version, crc32;
|
unsigned signature, version, crc32;
|
||||||
char profile[16], description[512];
|
char description[512], profile[16];
|
||||||
|
|
||||||
s.integer(signature);
|
s.integer(signature);
|
||||||
s.integer(version);
|
s.integer(version);
|
||||||
s.integer(crc32);
|
s.integer(crc32);
|
||||||
s.array(profile);
|
|
||||||
s.array(description);
|
s.array(description);
|
||||||
|
s.array(profile);
|
||||||
|
|
||||||
if(signature != 0x31545342) return false;
|
if(signature != 0x31545342) return false;
|
||||||
if(version != Info::SerializerVersion) return false;
|
if(version != Info::SerializerVersion) return false;
|
||||||
|
|
|
@ -63,8 +63,7 @@ void System::runthreadtosave() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::init(Interface *interface_) {
|
void System::init() {
|
||||||
interface = interface_;
|
|
||||||
assert(interface != 0);
|
assert(interface != 0);
|
||||||
|
|
||||||
icd2.init();
|
icd2.init();
|
||||||
|
@ -241,7 +240,7 @@ void System::scanline() {
|
||||||
void System::frame() {
|
void System::frame() {
|
||||||
}
|
}
|
||||||
|
|
||||||
System::System() : interface(0) {
|
System::System() {
|
||||||
region = Region::Autodetect;
|
region = Region::Autodetect;
|
||||||
expansion = ExpansionPortDevice::BSX;
|
expansion = ExpansionPortDevice::BSX;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,13 @@ class Interface;
|
||||||
|
|
||||||
class System : property<System> {
|
class System : property<System> {
|
||||||
public:
|
public:
|
||||||
Interface *interface;
|
|
||||||
enum class Region : unsigned { NTSC = 0, PAL = 1, Autodetect = 2 };
|
enum class Region : unsigned { NTSC = 0, PAL = 1, Autodetect = 2 };
|
||||||
enum class ExpansionPortDevice : unsigned { None = 0, BSX = 1 };
|
enum class ExpansionPortDevice : unsigned { None = 0, BSX = 1 };
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void runtosave();
|
void runtosave();
|
||||||
|
|
||||||
void init(Interface*);
|
void init();
|
||||||
void term();
|
void term();
|
||||||
void load();
|
void load();
|
||||||
void unload();
|
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;
|
hires = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ struct Interface : public SNES::Interface {
|
||||||
snes_input_state_t pinput_state;
|
snes_input_state_t pinput_state;
|
||||||
string basename;
|
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 width = hires ? 512 : 256;
|
||||||
unsigned height = overscan ? 239 : 224;
|
unsigned height = overscan ? 239 : 224;
|
||||||
if(interlace) height <<= 1;
|
if(interlace) height <<= 1;
|
||||||
|
@ -21,15 +21,11 @@ struct Interface : public SNES::Interface {
|
||||||
if(pinput_poll) pinput_poll();
|
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);
|
if(paudio_sample) return paudio_sample(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_poll() {
|
int16_t inputPoll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
|
||||||
if(pinput_poll) return pinput_poll();
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) {
|
|
||||||
if(pinput_state) return pinput_state(port, (unsigned)device, index, id);
|
if(pinput_state) return pinput_state(port, (unsigned)device, index, id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -86,7 +82,7 @@ void snes_set_cartridge_basename(const char *basename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void snes_init(void) {
|
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::Port1, SNES::Input::Device::Joypad);
|
||||||
SNES::input.connect(SNES::Controller::Port2, 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);
|
return SNES::system.unserialize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CheatList {
|
||||||
|
bool enable;
|
||||||
|
string code;
|
||||||
|
CheatList() : enable(false) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static linear_vector<CheatList> cheatList;
|
||||||
|
|
||||||
void snes_cheat_reset(void) {
|
void snes_cheat_reset(void) {
|
||||||
SNES::cheat.reset();
|
cheatList.reset();
|
||||||
SNES::cheat.synchronize();
|
interface.setCheats();
|
||||||
}
|
}
|
||||||
|
|
||||||
void snes_cheat_set(unsigned index, bool enabled, const char *code) {
|
void snes_cheat_set(unsigned index, bool enable, const char *code) {
|
||||||
SNES::cheat[index] = code;
|
cheatList[index].enable = enable;
|
||||||
SNES::cheat[index].enabled = enabled;
|
cheatList[index].code = code;
|
||||||
SNES::cheat.synchronize();
|
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(
|
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);
|
bool snes_unserialize(const uint8_t *data, unsigned size);
|
||||||
|
|
||||||
void snes_cheat_reset(void);
|
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(
|
bool snes_load_cartridge_normal(
|
||||||
const char *rom_xml, const uint8_t *rom_data, unsigned rom_size
|
const char *rom_xml, const uint8_t *rom_data, unsigned rom_size
|
||||||
|
|
|
@ -26,7 +26,7 @@ MainWindow::MainWindow() {
|
||||||
|
|
||||||
settingsMenu.setText("Settings");
|
settingsMenu.setText("Settings");
|
||||||
settingsSynchronizeVideo.setText("Synchronize Video");
|
settingsSynchronizeVideo.setText("Synchronize Video");
|
||||||
settingsSynchronizeVideo.setChecked();
|
settingsSynchronizeVideo.setChecked(false);
|
||||||
settingsSynchronizeAudio.setText("Synchronize Audio");
|
settingsSynchronizeAudio.setText("Synchronize Audio");
|
||||||
settingsSynchronizeAudio.setChecked();
|
settingsSynchronizeAudio.setChecked();
|
||||||
settingsMuteAudio.setText("Mute Audio");
|
settingsMuteAudio.setText("Mute Audio");
|
||||||
|
@ -46,6 +46,7 @@ MainWindow::MainWindow() {
|
||||||
toolsStateLoad5.setText("Slot 5");
|
toolsStateLoad5.setText("Slot 5");
|
||||||
toolsShrinkWindow.setText("Shrink Window");
|
toolsShrinkWindow.setText("Shrink Window");
|
||||||
toolsCheatEditor.setText("Cheat Editor ...");
|
toolsCheatEditor.setText("Cheat Editor ...");
|
||||||
|
toolsStateManager.setText("State Manager ...");
|
||||||
toolsTest.setText("Test");
|
toolsTest.setText("Test");
|
||||||
|
|
||||||
helpMenu.setText("Help");
|
helpMenu.setText("Help");
|
||||||
|
@ -94,6 +95,7 @@ MainWindow::MainWindow() {
|
||||||
toolsMenu.append(toolsSeparator);
|
toolsMenu.append(toolsSeparator);
|
||||||
toolsMenu.append(toolsShrinkWindow);
|
toolsMenu.append(toolsShrinkWindow);
|
||||||
toolsMenu.append(toolsCheatEditor);
|
toolsMenu.append(toolsCheatEditor);
|
||||||
|
toolsMenu.append(toolsStateManager);
|
||||||
toolsMenu.append(toolsTest);
|
toolsMenu.append(toolsTest);
|
||||||
|
|
||||||
append(helpMenu);
|
append(helpMenu);
|
||||||
|
@ -168,6 +170,7 @@ MainWindow::MainWindow() {
|
||||||
toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); };
|
toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); };
|
||||||
|
|
||||||
toolsCheatEditor.onTick = [&] { cheatEditor->setVisible(); };
|
toolsCheatEditor.onTick = [&] { cheatEditor->setVisible(); };
|
||||||
|
toolsStateManager.onTick = [&] { stateManager->setVisible(); };
|
||||||
|
|
||||||
toolsTest.onTick = [&] {
|
toolsTest.onTick = [&] {
|
||||||
NES::cpu.trace = toolsTest.checked();
|
NES::cpu.trace = toolsTest.checked();
|
||||||
|
|
|
@ -45,6 +45,7 @@ struct MainWindow : Window {
|
||||||
Separator toolsSeparator;
|
Separator toolsSeparator;
|
||||||
Item toolsShrinkWindow;
|
Item toolsShrinkWindow;
|
||||||
Item toolsCheatEditor;
|
Item toolsCheatEditor;
|
||||||
|
Item toolsStateManager;
|
||||||
CheckItem toolsTest;
|
CheckItem toolsTest;
|
||||||
|
|
||||||
Menu helpMenu;
|
Menu helpMenu;
|
||||||
|
|
|
@ -5,21 +5,19 @@ bool InterfaceGameBoy::loadCartridge(const string &filename) {
|
||||||
|
|
||||||
interface->baseName = nall::basename(filename);
|
interface->baseName = nall::basename(filename);
|
||||||
GameBoyCartridge info(data, size);
|
GameBoyCartridge info(data, size);
|
||||||
GameBoy::cartridge.load(info.xml, data, size);
|
GameBoy::Interface::loadCartridge(info.xml, data, size);
|
||||||
GameBoy::system.power();
|
|
||||||
|
|
||||||
delete[] data;
|
delete[] data;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterfaceGameBoy::unloadCartridge() {
|
void InterfaceGameBoy::unloadCartridge() {
|
||||||
GameBoy::cartridge.unload();
|
GameBoy::Interface::unloadCartridge();
|
||||||
interface->baseName = "";
|
interface->baseName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceGameBoy::saveState(const string &filename) {
|
bool InterfaceGameBoy::saveState(const string &filename) {
|
||||||
GameBoy::system.runtosave();
|
serializer s = serialize();
|
||||||
serializer s = GameBoy::system.serialize();
|
|
||||||
return file::write(filename, s.data(), s.size());
|
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;
|
if(file::read(filename, data, size) == false) return false;
|
||||||
serializer s(data, size);
|
serializer s(data, size);
|
||||||
delete[] data;
|
delete[] data;
|
||||||
return GameBoy::system.unserialize(s);
|
return 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
void InterfaceGameBoy::video_refresh(const uint8_t *data) {
|
void InterfaceGameBoy::videoRefresh(const uint8_t *data) {
|
||||||
interface->video_refresh();
|
interface->video_refresh();
|
||||||
|
|
||||||
uint32_t *output;
|
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);
|
dspaudio.sample(lsample, rsample);
|
||||||
while(dspaudio.pending()) {
|
while(dspaudio.pending()) {
|
||||||
signed lsample, rsample;
|
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) {
|
switch((GameBoy::Input)id) {
|
||||||
case GameBoy::Input::Up: return interface->inputState[keyboard(0)[Keyboard::Up]];
|
case GameBoy::Input::Up: return interface->inputState[keyboard(0)[Keyboard::Up]];
|
||||||
case GameBoy::Input::Down: return interface->inputState[keyboard(0)[Keyboard::Down]];
|
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 saveState(const string &filename);
|
||||||
bool loadState(const string &filename);
|
bool loadState(const string &filename);
|
||||||
void setCheatCodes(const lstring &list);
|
|
||||||
|
|
||||||
void video_refresh(const uint8_t *data);
|
void videoRefresh(const uint8_t *data);
|
||||||
void audio_sample(int16_t csample, int16_t lsample, int16_t rsample);
|
void audioSample(int16_t csample, int16_t lsample, int16_t rsample);
|
||||||
bool input_poll(unsigned id);
|
bool inputPoll(unsigned id);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,21 +6,25 @@ Interface *interface = 0;
|
||||||
|
|
||||||
bool Interface::loaded() {
|
bool Interface::loaded() {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: return NES::cartridge.loaded();
|
case Mode::NES: return nes.cartridgeLoaded();
|
||||||
case Mode::SNES: return SNES::cartridge.loaded();
|
case Mode::SNES: return snes.cartridgeLoaded();
|
||||||
case Mode::GameBoy: return GameBoy::cartridge.loaded();
|
case Mode::GameBoy: return gameBoy.cartridgeLoaded();
|
||||||
default: return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Interface::loadCartridge(const string &filename) {
|
bool Interface::loadCartridge(const string &filename) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
setCheatCodes();
|
||||||
unloadCartridge();
|
unloadCartridge();
|
||||||
if(filename.endswith(".nes")) result = loadCartridgeNES(filename);
|
if(filename.endswith(".nes")) result = loadCartridgeNES(filename);
|
||||||
if(filename.endswith(".sfc")) result = loadCartridgeSNES(filename);
|
if(filename.endswith(".sfc")) result = loadCartridgeSNES(filename);
|
||||||
if(filename.endswith(".gb" )) result = loadCartridgeGameBoy(filename);
|
if(filename.endswith(".gb" )) result = loadCartridgeGameBoy(filename);
|
||||||
if(filename.endswith(".gbc")) 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +37,7 @@ bool Interface::loadCartridgeNES(const string &filename) {
|
||||||
void Interface::unloadCartridge() {
|
void Interface::unloadCartridge() {
|
||||||
if(loaded() == false) return;
|
if(loaded() == false) return;
|
||||||
cheatEditor->save({ baseName, ".cht" });
|
cheatEditor->save({ baseName, ".cht" });
|
||||||
|
stateManager->save({ baseName, ".bsa" }, 0u);
|
||||||
|
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: nes.unloadCartridge(); break;
|
case Mode::NES: nes.unloadCartridge(); break;
|
||||||
|
@ -72,36 +77,44 @@ void Interface::unloadCartridgeGameBoy() {
|
||||||
|
|
||||||
void Interface::power() {
|
void Interface::power() {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: return NES::system.power();
|
case Mode::NES: return nes.power();
|
||||||
case Mode::SNES: return SNES::system.power();
|
case Mode::SNES: return snes.power();
|
||||||
case Mode::GameBoy: return GameBoy::system.power();
|
case Mode::GameBoy: return gameBoy.power();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::reset() {
|
void Interface::reset() {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: return NES::system.reset();
|
case Mode::NES: return nes.reset();
|
||||||
case Mode::SNES: return SNES::system.reset();
|
case Mode::SNES: return snes.reset();
|
||||||
case Mode::GameBoy: return GameBoy::system.power(); //Game Boy lacks reset button
|
case Mode::GameBoy: return gameBoy.power(); //Game Boy lacks reset button
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::run() {
|
void Interface::run() {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES:
|
case Mode::NES: return nes.run();
|
||||||
return NES::system.run();
|
case Mode::SNES: return snes.run();
|
||||||
|
case Mode::GameBoy: return gameBoy.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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
bool Interface::saveState(const string &filename) {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::SNES: return snes.saveState(filename);
|
case Mode::SNES: return snes.saveState(filename);
|
||||||
|
@ -120,17 +133,17 @@ bool Interface::loadState(const string &filename) {
|
||||||
|
|
||||||
void Interface::setCheatCodes(const lstring &list) {
|
void Interface::setCheatCodes(const lstring &list) {
|
||||||
switch(mode()) {
|
switch(mode()) {
|
||||||
case Mode::NES: return nes.setCheatCodes(list);
|
case Mode::NES: return nes.setCheats(list);
|
||||||
case Mode::SNES: return snes.setCheatCodes(list);
|
case Mode::SNES: return snes.setCheats(list);
|
||||||
case Mode::GameBoy: return gameBoy.setCheatCodes(list);
|
case Mode::GameBoy: return gameBoy.setCheats(list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Interface::Interface() {
|
Interface::Interface() {
|
||||||
mode = Mode::None;
|
mode = Mode::None;
|
||||||
NES::system.init(&nes);
|
nes.initialize(&nes);
|
||||||
SNES::system.init(&snes);
|
snes.initialize(&snes);
|
||||||
GameBoy::system.init(&gameBoy);
|
gameBoy.initialize(&gameBoy);
|
||||||
}
|
}
|
||||||
|
|
||||||
//internal
|
//internal
|
||||||
|
|
|
@ -22,9 +22,12 @@ struct Interface : property<Interface> {
|
||||||
void reset();
|
void reset();
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
serializer serialize();
|
||||||
|
bool unserialize(serializer&);
|
||||||
|
|
||||||
bool saveState(const string &filename);
|
bool saveState(const string &filename);
|
||||||
bool loadState(const string &filename);
|
bool loadState(const string &filename);
|
||||||
void setCheatCodes(const lstring &list);
|
void setCheatCodes(const lstring &list = lstring{});
|
||||||
|
|
||||||
Interface();
|
Interface();
|
||||||
|
|
||||||
|
|
|
@ -3,30 +3,20 @@ bool InterfaceNES::loadCartridge(const string &filename) {
|
||||||
if(fp.open(filename, filemap::mode::read) == false) return false;
|
if(fp.open(filename, filemap::mode::read) == false) return false;
|
||||||
|
|
||||||
interface->baseName = nall::basename(filename);
|
interface->baseName = nall::basename(filename);
|
||||||
NES::cartridge.load("", fp.data(), fp.size());
|
NES::Interface::loadCartridge("", fp.data(), fp.size());
|
||||||
NES::system.power();
|
|
||||||
|
|
||||||
fp.close();
|
fp.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterfaceNES::unloadCartridge() {
|
void InterfaceNES::unloadCartridge() {
|
||||||
NES::cartridge.unload();
|
NES::Interface::unloadCartridge();
|
||||||
interface->baseName = "";
|
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();
|
interface->video_refresh();
|
||||||
|
|
||||||
uint32_t *output;
|
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);
|
dspaudio.sample(sample, sample);
|
||||||
while(dspaudio.pending()) {
|
while(dspaudio.pending()) {
|
||||||
signed lsample, rsample;
|
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) {
|
if(port == 0 && device == 0) {
|
||||||
switch(id) {
|
switch(id) {
|
||||||
case 0: return interface->inputState[keyboard(0)[Keyboard::X]];
|
case 0: return interface->inputState[keyboard(0)[Keyboard::X]];
|
||||||
|
|
|
@ -2,11 +2,9 @@ struct InterfaceNES : NES::Interface {
|
||||||
bool loadCartridge(const string &filename);
|
bool loadCartridge(const string &filename);
|
||||||
void unloadCartridge();
|
void unloadCartridge();
|
||||||
|
|
||||||
void setCheatCodes(const lstring &list);
|
void videoRefresh(const uint16_t *data);
|
||||||
|
void audioSample(int16_t sample);
|
||||||
void video_refresh(const uint16_t *data);
|
int16_t inputPoll(bool port, unsigned device, unsigned id);
|
||||||
void audio_sample(int16_t sample);
|
|
||||||
int16_t input_poll(bool port, unsigned device, unsigned id);
|
|
||||||
|
|
||||||
InterfaceNES();
|
InterfaceNES();
|
||||||
|
|
||||||
|
|
|
@ -5,22 +5,19 @@ bool InterfaceSNES::loadCartridge(const string &filename) {
|
||||||
|
|
||||||
interface->baseName = nall::basename(filename);
|
interface->baseName = nall::basename(filename);
|
||||||
string xml = SNESCartridge(data, size).xmlMemoryMap;
|
string xml = SNESCartridge(data, size).xmlMemoryMap;
|
||||||
SNES::cartridge.rom.copy(data, size);
|
SNES::Interface::loadCartridge(xml, data, size);
|
||||||
SNES::cartridge.load(SNES::Cartridge::Mode::Normal, { xml });
|
|
||||||
SNES::system.power();
|
|
||||||
|
|
||||||
delete[] data;
|
delete[] data;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterfaceSNES::unloadCartridge() {
|
void InterfaceSNES::unloadCartridge() {
|
||||||
SNES::cartridge.unload();
|
SNES::Interface::unloadCartridge();
|
||||||
interface->baseName = "";
|
interface->baseName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterfaceSNES::saveState(const string &filename) {
|
bool InterfaceSNES::saveState(const string &filename) {
|
||||||
SNES::system.runtosave();
|
serializer s = serialize();
|
||||||
serializer s = SNES::system.serialize();
|
|
||||||
return file::write(filename, s.data(), s.size());
|
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;
|
if(file::read(filename, data, size) == false) return false;
|
||||||
serializer s(data, size);
|
serializer s(data, size);
|
||||||
delete[] data;
|
delete[] data;
|
||||||
return SNES::system.unserialize(s);
|
return 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
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();
|
interface->video_refresh();
|
||||||
|
|
||||||
unsigned width = hires ? 512 : 256;
|
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);
|
dspaudio.sample(lsample, rsample);
|
||||||
while(dspaudio.pending()) {
|
while(dspaudio.pending()) {
|
||||||
signed lsample, rsample;
|
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) {
|
if(port == 0 && device == SNES::Input::Device::Joypad) {
|
||||||
switch((SNES::Input::JoypadID)id) {
|
switch((SNES::Input::JoypadID)id) {
|
||||||
case SNES::Input::JoypadID::Up: return interface->inputState[keyboard(0)[Keyboard::Up]];
|
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 saveState(const string &filename);
|
||||||
bool loadState(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 videoRefresh(const uint16_t *data, bool hires, bool interlace, bool overscan);
|
||||||
void audio_sample(int16_t lsample, int16_t rsample);
|
void audioSample(int16_t lsample, int16_t rsample);
|
||||||
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);
|
||||||
|
|
||||||
string path(SNES::Cartridge::Slot slot, const string &hint);
|
string path(SNES::Cartridge::Slot slot, const string &hint);
|
||||||
};
|
};
|
||||||
|
|
|
@ -42,12 +42,13 @@ Application::Application(int argc, char **argv) : quit(false) {
|
||||||
mainWindow = new MainWindow;
|
mainWindow = new MainWindow;
|
||||||
fileBrowser = new FileBrowser;
|
fileBrowser = new FileBrowser;
|
||||||
cheatEditor = new CheatEditor;
|
cheatEditor = new CheatEditor;
|
||||||
|
stateManager = new StateManager;
|
||||||
utility->setMode(Interface::Mode::None);
|
utility->setMode(Interface::Mode::None);
|
||||||
mainWindow->setVisible();
|
mainWindow->setVisible();
|
||||||
|
|
||||||
video.driver(videoDriver);
|
video.driver(videoDriver);
|
||||||
video.set(Video::Handle, mainWindow->viewport.handle());
|
video.set(Video::Handle, mainWindow->viewport.handle());
|
||||||
video.set(Video::Synchronize, true);
|
video.set(Video::Synchronize, false);
|
||||||
video.set(Video::Filter, 0u);
|
video.set(Video::Filter, 0u);
|
||||||
video.init();
|
video.init();
|
||||||
|
|
||||||
|
@ -79,6 +80,7 @@ Application::Application(int argc, char **argv) : quit(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
|
delete stateManager;
|
||||||
delete cheatEditor;
|
delete cheatEditor;
|
||||||
delete fileBrowser;
|
delete fileBrowser;
|
||||||
delete mainWindow;
|
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 "../base.hpp"
|
||||||
#include "cheat-editor.cpp"
|
#include "cheat-editor.cpp"
|
||||||
|
#include "state-manager.cpp"
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
#include "cheat-editor.hpp"
|
#include "cheat-editor.hpp"
|
||||||
|
#include "state-manager.hpp"
|
||||||
|
|
Loading…
Reference in New Issue