This commit is contained in:
Themaister 2011-06-26 13:43:01 +02:00
parent 301c39c601
commit 3bb6889efd
53 changed files with 1009 additions and 773 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
*.so
*.o
*.a
*.dll

View File

@ -25,6 +25,16 @@ namespace nall {
inline string& append(unsigned int value);
inline string& append(double value);
/*
inline bool equals(const char*) const;
inline bool iequals(const char*) const;
inline bool wildcard(const char*) const;
inline bool iwildcard(const char*) const;
*/
inline bool beginswith(const char*) const;
template<typename T> inline string& operator= (const T& value);
template<typename T> inline string& operator<<(const T& value);

View File

@ -34,6 +34,8 @@ string& string::append(signed int value) { append(integer(value)); return *this;
string& string::append(unsigned int value) { append(decimal(value)); return *this; }
string& string::append(double value) { append(fp(value)); return *this; }
bool string::beginswith(const char *str) const { return strstr(data, str) == data; }
string::operator const char*() const {
return data;
}

View File

@ -1,12 +1,12 @@
snes_objects := libco
snes_objects += snes-system
snes_objects += snes-system snes-controller
snes_objects += snes-cartridge snes-cheat
snes_objects += snes-memory snes-cpucore snes-smpcore
snes_objects += snes-cpu snes-smp snes-dsp snes-ppu
snes_objects += snes-nss snes-icd2 snes-superfx snes-sa1 snes-necdsp snes-hitachidsp
snes_objects += snes-bsx snes-srtc snes-sdd1 snes-spc7110 snes-sufamiturbo
snes_objects += snes-obc1 snes-st0018
snes_objects += snes-msu1 snes-serial snes-link
snes_objects += snes-msu1 snes-link
snes_objects += $(gameboy_objects)
objects += $(snes_objects)
@ -34,16 +34,17 @@ obj/libsnes.o: $(snes)/libsnes/libsnes.cpp $(snes)/libsnes/*
obj/libco.o : libco/libco.c libco/*
obj/snes-system.o : $(snes)/system/system.cpp $(call rwildcard,$(snes)/system/) $(call rwildcard,$(snes)/video/)
obj/snes-memory.o : $(snes)/memory/memory.cpp $(call rwildcard,$(snes)/memory/)
obj/snes-cpucore.o : $(snes)/cpu/core/core.cpp $(call rwildcard,$(snes)/cpu/core/)
obj/snes-smpcore.o : $(snes)/smp/core/core.cpp $(call rwildcard,$(snes)/smp/core/)
obj/snes-cpu.o : $(snescpu)/cpu.cpp $(call rwildcard,$(snescpu)/)
obj/snes-smp.o : $(snessmp)/smp.cpp $(call rwildcard,$(snessmp)/)
obj/snes-dsp.o : $(snesdsp)/dsp.cpp $(call rwildcard,$(snesdsp)/)
obj/snes-ppu.o : $(snesppu)/ppu.cpp $(call rwildcard,$(snesppu)/)
obj/snes-cartridge.o: $(snes)/cartridge/cartridge.cpp $(snes)/cartridge/*
obj/snes-cheat.o : $(snes)/cheat/cheat.cpp $(snes)/cheat/*
obj/snes-system.o : $(snes)/system/system.cpp $(call rwildcard,$(snes)/system/) $(call rwildcard,$(snes)/video/) $(call rwildcard,$(snes)/audio/) $(call rwildcard,$(snes)/input/)
obj/snes-controller.o: $(snes)/controller/controller.cpp $(call rwildcard,$(snes)/controller/)
obj/snes-memory.o : $(snes)/memory/memory.cpp $(call rwildcard,$(snes)/memory/)
obj/snes-cpucore.o : $(snes)/cpu/core/core.cpp $(call rwildcard,$(snes)/cpu/core/)
obj/snes-smpcore.o : $(snes)/smp/core/core.cpp $(call rwildcard,$(snes)/smp/core/)
obj/snes-cpu.o : $(snescpu)/cpu.cpp $(call rwildcard,$(snescpu)/)
obj/snes-smp.o : $(snessmp)/smp.cpp $(call rwildcard,$(snessmp)/)
obj/snes-dsp.o : $(snesdsp)/dsp.cpp $(call rwildcard,$(snesdsp)/)
obj/snes-ppu.o : $(snesppu)/ppu.cpp $(call rwildcard,$(snesppu)/)
obj/snes-cartridge.o : $(snes)/cartridge/cartridge.cpp $(snes)/cartridge/*
obj/snes-cheat.o : $(snes)/cheat/cheat.cpp $(snes)/cheat/*
obj/snes-nss.o : $(snes)/chip/nss/nss.cpp $(call rwildcard,$(snes)/chip/nss/)
obj/snes-icd2.o : $(snes)/chip/icd2/icd2.cpp $(call rwildcard,$(snes)/chip/icd2/)
@ -58,7 +59,6 @@ obj/snes-spc7110.o : $(snes)/chip/spc7110/spc7110.cpp $(snes)/chip/spc7110/*
obj/snes-obc1.o : $(snes)/chip/obc1/obc1.cpp $(snes)/chip/obc1/*
obj/snes-st0018.o : $(snes)/chip/st0018/st0018.cpp $(snes)/chip/st0018/*
obj/snes-msu1.o : $(snes)/chip/msu1/msu1.cpp $(snes)/chip/msu1/*
obj/snes-serial.o : $(snes)/chip/serial/serial.cpp $(snes)/chip/serial/*
obj/snes-link.o : $(snes)/chip/link/link.cpp $(snes)/chip/link/*
obj/snes-sufamiturbo.o: $(snes)/chip/sufamiturbo/sufamiturbo.cpp $(snes)/chip/sufamiturbo/*

View File

@ -23,6 +23,9 @@ void CPU::step(unsigned clocks) {
Processor &chip = *coprocessors[i];
chip.clock -= clocks * (uint64)chip.frequency;
}
input.port1->clock -= clocks * (uint64)input.port1->frequency;
input.port2->clock -= clocks * (uint64)input.port2->frequency;
synchronize_controllers();
}
void CPU::synchronize_smp() {
@ -41,13 +44,18 @@ void CPU::synchronize_ppu() {
}
}
void CPU::synchronize_coprocessor() {
void CPU::synchronize_coprocessors() {
for(unsigned i = 0; i < coprocessors.size(); i++) {
Processor &chip = *coprocessors[i];
if(chip.clock < 0) co_switch(chip.thread);
}
}
void CPU::synchronize_controllers() {
if(input.port1->clock < 0) co_switch(input.port1->thread);
if(input.port2->clock < 0) co_switch(input.port2->thread);
}
void CPU::Enter() { cpu.enter(); }
void CPU::enter() {

View File

@ -7,7 +7,8 @@ public:
alwaysinline void step(unsigned clocks);
alwaysinline void synchronize_smp();
void synchronize_ppu();
void synchronize_coprocessor();
void synchronize_coprocessors();
void synchronize_controllers();
uint8 pio();
bool joylatch();
@ -41,7 +42,6 @@ private:
enum{
DramRefresh,
HdmaRun,
ControllerLatch,
};
};
nall::priority_queue<unsigned> queue;

View File

@ -15,13 +15,13 @@ uint8 CPU::mmio_read(unsigned addr) {
case 0x4016: {
uint8 result = regs.mdr & 0xfc;
result |= input.port_read(0) & 3;
result |= input.port1->data() & 3;
return result;
}
case 0x4017: {
uint8 result = (regs.mdr & 0xe0) | 0x1c;
result |= input.port_read(1) & 3;
result |= input.port2->data() & 3;
return result;
}
@ -127,10 +127,8 @@ void CPU::mmio_write(unsigned addr, uint8 data) {
}
case 0x4016: {
bool old_latch = status.joypad_strobe_latch;
bool new_latch = data & 1;
status.joypad_strobe_latch = new_latch;
if(old_latch != new_latch) input.poll();
input.port1->latch(data & 1);
input.port2->latch(data & 1);
return;
}

View File

@ -4,7 +4,6 @@ void CPU::queue_event(unsigned id) {
switch(id) {
case QueueEvent::DramRefresh: return add_clocks(40);
case QueueEvent::HdmaRun: return hdma_run();
case QueueEvent::ControllerLatch: return ppu.latch_counters();
}
}
@ -62,7 +61,7 @@ void CPU::add_clocks(unsigned clocks) {
void CPU::scanline() {
synchronize_smp();
synchronize_ppu();
synchronize_coprocessor();
synchronize_coprocessors();
system.scanline();
if(vcounter() == 0) hdma_init();
@ -73,10 +72,6 @@ void CPU::scanline() {
queue.enqueue(1104 + 8, QueueEvent::HdmaRun);
}
if(vcounter() == input.latchy) {
queue.enqueue(input.latchx, QueueEvent::ControllerLatch);
}
bool nmi_valid = status.nmi_valid;
status.nmi_valid = vcounter() >= (ppu.overscan() == false ? 225 : 240);
if(!nmi_valid && status.nmi_valid) {
@ -87,16 +82,20 @@ void CPU::scanline() {
}
if(status.auto_joypad_poll_enabled && vcounter() == (ppu.overscan() == false ? 227 : 242)) {
input.poll();
run_auto_joypad_poll();
}
}
void CPU::run_auto_joypad_poll() {
input.port1->latch(1);
input.port2->latch(1);
input.port1->latch(0);
input.port2->latch(0);
uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0;
for(unsigned i = 0; i < 16; i++) {
uint8 port0 = input.port_read(0);
uint8 port1 = input.port_read(1);
uint8 port0 = input.port1->data();
uint8 port1 = input.port2->data();
joy1 |= (port0 & 1) ? (0x8000 >> i) : 0;
joy2 |= (port1 & 1) ? (0x8000 >> i) : 0;

View File

@ -29,7 +29,6 @@ void Cartridge::load(Mode::e cartridge_mode, const lstring &xml_list) {
has_obc1 = false;
has_st0018 = false;
has_msu1 = false;
has_serial = false;
has_link = false;
nvram.reset();

View File

@ -49,7 +49,6 @@ public:
readonly<bool> has_obc1;
readonly<bool> has_st0018;
readonly<bool> has_msu1;
readonly<bool> has_serial;
readonly<bool> has_link;
struct NonVolatileRAM {
@ -118,9 +117,10 @@ private:
void xml_parse_obc1(xml_element&);
void xml_parse_setarisc(xml_element&);
void xml_parse_msu1(xml_element&);
void xml_parse_serial(xml_element&);
void xml_parse_link(xml_element&);
unsigned xml_parse_hex(const string&);
unsigned xml_parse_unsigned(const string&);
void xml_parse_address(Mapping&, const string&);
void xml_parse_mode(Mapping&, const string&);
};

View File

@ -1,5 +1,8 @@
#ifdef CARTRIDGE_CPP
#define READ_FUNC uint8 (unsigned)
#define WRITE_FUNC void (unsigned, uint8)
void Cartridge::parse_xml(const lstring &list) {
mapping.reset();
parse_xml_cartridge(list[0]);
@ -49,7 +52,6 @@ void Cartridge::parse_xml_cartridge(const char *data) {
if(node.name == "obc1") xml_parse_obc1(node);
if(node.name == "setarisc") xml_parse_setarisc(node);
if(node.name == "msu1") xml_parse_msu1(node);
if(node.name == "serial") xml_parse_serial(node);
if(node.name == "link") xml_parse_link(node);
}
}
@ -73,8 +75,8 @@ void Cartridge::xml_parse_rom(xml_element &root) {
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
if(m.size == 0) m.size = rom.size();
mapping.append(m);
@ -84,7 +86,7 @@ void Cartridge::xml_parse_rom(xml_element &root) {
void Cartridge::xml_parse_ram(xml_element &root) {
foreach(attr, root.attribute) {
if(attr.name == "size") ram_size = hex(attr.content);
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
}
foreach(leaf, root.element) {
@ -93,8 +95,8 @@ void Cartridge::xml_parse_ram(xml_element &root) {
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
if(m.size == 0) m.size = ram_size;
mapping.append(m);
@ -121,7 +123,7 @@ void Cartridge::xml_parse_nss(xml_element &root) {
unsigned value = 0x0000;
foreach(attr, leaf.attribute) {
if(attr.name == "name") name = attr.parse();
if(attr.name == "value") value = (uint16)hex(attr.content);
if(attr.name == "value") value = (uint16)xml_parse_hex(attr.content);
}
information.nss.option[number].append(string( hex<4>(value), ":", name ));
}
@ -142,7 +144,7 @@ void Cartridge::xml_parse_icd2(xml_element &root) {
foreach(node, root.element) {
if(node.name == "map") {
Mapping m(function<uint8 (unsigned)>( &ICD2::read, &icd2 ), function<void (unsigned, uint8)>( &ICD2::write, &icd2 ));
Mapping m(function<READ_FUNC>( &ICD2::read, &icd2 ), function<WRITE_FUNC>( &ICD2::write, &icd2 ));
foreach(attr, node.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -162,15 +164,15 @@ void Cartridge::xml_parse_superfx(xml_element &root) {
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
mapping.append(m);
}
}
} else if(node.name == "ram") {
foreach(attr, node.attribute) {
if(attr.name == "size") ram_size = hex(attr.content);
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
}
foreach(leaf, node.element) {
@ -179,8 +181,8 @@ void Cartridge::xml_parse_superfx(xml_element &root) {
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
if(m.size == 0) m.size = ram_size;
mapping.append(m);
@ -189,7 +191,7 @@ void Cartridge::xml_parse_superfx(xml_element &root) {
} else if(node.name == "mmio") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m( function<uint8(unsigned)>( &SuperFX::mmio_read, &superfx ), function<void(unsigned, uint8)>( &SuperFX::mmio_write, &superfx ));
Mapping m(function<READ_FUNC>( &SuperFX::mmio_read, &superfx ), function<WRITE_FUNC>( &SuperFX::mmio_write, &superfx ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -209,12 +211,12 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
if(subnode.name == "rom") {
foreach(leaf, subnode.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>(&SA1::mmc_read, &sa1), function<void(unsigned, uint8)>( &SA1::mmc_write, &sa1 ));
Mapping m(function<READ_FUNC>( &SA1::mmc_read, &sa1 ), function<WRITE_FUNC>( &SA1::mmc_write, &sa1 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
mapping.append(m);
}
@ -222,7 +224,7 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
} else if(subnode.name == "ram") {
foreach(leaf, subnode.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &SA1::mmc_cpu_read, &sa1 ), function<void(unsigned,uint8)>( &SA1::mmc_cpu_write, &sa1 ));
Mapping m(function<READ_FUNC>( &SA1::mmc_cpu_read, &sa1 ), function<WRITE_FUNC>( &SA1::mmc_cpu_write, &sa1 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -238,8 +240,8 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
if(m.size == 0) m.size = 2048;
mapping.append(m);
@ -247,7 +249,7 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
}
} else if(node.name == "bwram") {
foreach(attr, node.attribute) {
if(attr.name == "size") ram_size = hex(attr.content);
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
}
foreach(leaf, node.element) {
@ -256,8 +258,8 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
if(m.size == 0) m.size = ram_size;
mapping.append(m);
@ -266,7 +268,7 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
} else if(node.name == "mmio") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &SA1::mmio_read, &sa1 ), function<void(unsigned,uint8)>( &SA1::mmio_write, &sa1 ));
Mapping m(function<READ_FUNC>( &SA1::mmio_read, &sa1 ), function<WRITE_FUNC>( &SA1::mmio_write, &sa1 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -289,11 +291,11 @@ void Cartridge::xml_parse_necdsp(xml_element &root) {
string sha256;
foreach(attr, root.attribute) {
if(attr.name == "revision") {
if(attr.content == "upd7725" ) necdsp.revision.i = NECDSP::Revision::uPD7725;
if(attr.content == "upd96050") necdsp.revision.i = NECDSP::Revision::uPD96050;
if(attr.name == "model") {
if(attr.content == "uPD7725" ) necdsp.revision.i = NECDSP::Revision::uPD7725;
if(attr.content == "uPD96050") necdsp.revision.i = NECDSP::Revision::uPD96050;
} else if(attr.name == "frequency") {
necdsp.frequency = decimal(attr.content);
necdsp.frequency = xml_parse_unsigned(attr.content);
} else if(attr.name == "program") {
program = attr.content;
} else if(attr.name == "sha256") {
@ -331,7 +333,7 @@ void Cartridge::xml_parse_necdsp(xml_element &root) {
if(node.name == "dr") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &NECDSP::dr_read, &necdsp ), function<void(unsigned, uint8)>( &NECDSP::dr_write, &necdsp ));
Mapping m(function<READ_FUNC>( &NECDSP::dr_read, &necdsp ), function<WRITE_FUNC>( &NECDSP::dr_write, &necdsp ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -341,7 +343,7 @@ void Cartridge::xml_parse_necdsp(xml_element &root) {
} else if(node.name == "sr") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &NECDSP::sr_read, &necdsp ), function<void (unsigned, uint8)>( &NECDSP::sr_write, &necdsp ));
Mapping m(function<READ_FUNC>( &NECDSP::sr_read, &necdsp ), function<WRITE_FUNC>( &NECDSP::sr_write, &necdsp ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -351,7 +353,7 @@ void Cartridge::xml_parse_necdsp(xml_element &root) {
} else if(node.name == "dp") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &NECDSP::dp_read, &necdsp ), function<void(unsigned, uint8)>( &NECDSP::dp_write, &necdsp ));
Mapping m(function<READ_FUNC>( &NECDSP::dp_read, &necdsp ), function<WRITE_FUNC>( &NECDSP::dp_write, &necdsp ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -378,24 +380,24 @@ void Cartridge::xml_parse_hitachidsp(xml_element &root) {
for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = 0x000000;
string program, sha256;
string dataROM, sha256;
foreach(attr, root.attribute) {
if(attr.name == "frequency") {
hitachidsp.frequency = decimal(attr.content);
} else if(attr.name == "program") {
program = attr.content;
hitachidsp.frequency = xml_parse_unsigned(attr.content);
} else if(attr.name == "data") {
dataROM = attr.content;
} else if(attr.name == "sha256") {
sha256 = attr.content;
}
}
string path = string( dir(system.interface->path(Slot::Base, ".dsp")), program );
string path(dir(system.interface->path(Slot::Base, ".dsp")), dataROM);
file fp;
if(fp.open(path, file::mode_read) == false) {
system.interface->message(string( "Warning: Hitachi DSP program ", program, " is missing." ));
system.interface->message(string( "Warning: Hitachi DSP data ", dataROM, " is missing." ));
} else if(fp.size() != 1024 * 3) {
system.interface->message(string( "Warning: Hitachi DSP program ", program, " is of the wrong file size." ));
system.interface->message(string( "Warning: Hitachi DSP data ", dataROM, " is of the wrong file size." ));
fp.close();
} else {
for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = fp.readl(3);
@ -417,7 +419,7 @@ void Cartridge::xml_parse_hitachidsp(xml_element &root) {
foreach(n, hash) filehash.append(hex<2>(n));
if(sha256 != filehash) {
system.interface->message(string( "Warning: Hitachi DSP program ", program, " SHA256 sum is incorrect." ));
system.interface->message(string( "Warning: Hitachi DSP data ", dataROM, " SHA256 sum is incorrect." ));
}
}
@ -425,25 +427,29 @@ void Cartridge::xml_parse_hitachidsp(xml_element &root) {
}
foreach(node, root.element) {
if(node.name == "rom") foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8 (unsigned)>( &HitachiDSP::rom_read, &hitachidsp ), function<void (unsigned, uint8)>( &HitachiDSP::rom_write, &hitachidsp ));
if(node.name == "rom") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<READ_FUNC>( &HitachiDSP::rom_read, &hitachidsp ), function<WRITE_FUNC>( &HitachiDSP::rom_write, &hitachidsp ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
mapping.append(m);
}
}
}
if(node.name == "mmio") {
foreach(leaf, node.element) {
Mapping m(function<READ_FUNC>( &HitachiDSP::dsp_read, &hitachidsp ), function<WRITE_FUNC>( &HitachiDSP::dsp_write, &hitachidsp ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
}
mapping.append(m);
}
}
if(node.name == "mmio") foreach(leaf, node.element) {
Mapping m(function<uint8 (unsigned)>( &HitachiDSP::dsp_read, &hitachidsp ), function<void (unsigned, uint8)>( &HitachiDSP::dsp_write, &hitachidsp ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
mapping.append(m);
}
}
}
@ -458,8 +464,8 @@ void Cartridge::xml_parse_bsx(xml_element &root) {
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
mapping.append(m);
}
@ -467,7 +473,7 @@ void Cartridge::xml_parse_bsx(xml_element &root) {
} else if(node.name == "mcu") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &BSXCartridge::mcu_read, &bsxcartridge ), function<void(unsigned,uint8)>( &BSXCartridge::mcu_write, &bsxcartridge ));
Mapping m(function<READ_FUNC>( &BSXCartridge::mcu_read, &bsxcartridge ), function<WRITE_FUNC>( &BSXCartridge::mcu_write, &bsxcartridge ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -477,7 +483,7 @@ void Cartridge::xml_parse_bsx(xml_element &root) {
} else if(node.name == "mmio") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &BSXCartridge::mmio_read, &bsxcartridge ), function<void (unsigned, uint8)>( &BSXCartridge::mmio_write, &bsxcartridge ));
Mapping m(function<READ_FUNC>( &BSXCartridge::mmio_read, &bsxcartridge ), function<WRITE_FUNC>( &BSXCartridge::mmio_write, &bsxcartridge ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -510,8 +516,8 @@ void Cartridge::xml_parse_sufamiturbo(xml_element &root) {
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
if(m.size == 0) m.size = memory.size();
if(m.size) mapping.append(m);
@ -521,7 +527,7 @@ void Cartridge::xml_parse_sufamiturbo(xml_element &root) {
unsigned ram_size = 0;
foreach(attr, slot.attribute) {
if(attr.name == "size") ram_size = hex(attr.content);
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
}
foreach(leaf, slot.element) {
@ -531,8 +537,8 @@ void Cartridge::xml_parse_sufamiturbo(xml_element &root) {
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
if(m.size == 0) m.size = ram_size;
if(m.size) mapping.append(m);
@ -549,7 +555,7 @@ void Cartridge::xml_parse_srtc(xml_element &root) {
foreach(node, root.element) {
if(node.name == "map") {
Mapping m(function<uint8(unsigned)>( &SRTC::read, &srtc ), function<void(unsigned, uint8)>( &SRTC::write, &srtc ));
Mapping m(function<READ_FUNC>( &SRTC::read, &srtc ), function<WRITE_FUNC>( &SRTC::write, &srtc ));
foreach(attr, node.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -565,7 +571,7 @@ void Cartridge::xml_parse_sdd1(xml_element &root) {
if(node.name == "mcu") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &SDD1::mcu_read, &sdd1 ), function<void(unsigned, uint8)>( &SDD1::mcu_write, &sdd1 ));
Mapping m(function<READ_FUNC>( &SDD1::mcu_read, &sdd1 ), function<WRITE_FUNC>( &SDD1::mcu_write, &sdd1 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -575,7 +581,7 @@ void Cartridge::xml_parse_sdd1(xml_element &root) {
} else if(node.name == "mmio") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &SDD1::mmio_read, &sdd1 ), function<void(unsigned, uint8)>( &SDD1::mmio_write, &sdd1 ));
Mapping m(function<READ_FUNC>( &SDD1::mmio_read, &sdd1 ), function<WRITE_FUNC>( &SDD1::mmio_write, &sdd1 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -594,7 +600,7 @@ void Cartridge::xml_parse_spc7110(xml_element &root) {
if(node.name == "dcu") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &SPC7110::dcu_read, &spc7110 ), function<void(unsigned, uint8)>( &SPC7110::dcu_write, &spc7110 ));
Mapping m(function<READ_FUNC>( &SPC7110::dcu_read, &spc7110 ), function<WRITE_FUNC>( &SPC7110::dcu_write, &spc7110 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -604,10 +610,10 @@ void Cartridge::xml_parse_spc7110(xml_element &root) {
} else if(node.name == "mcu") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &SPC7110::mcu_read, &spc7110 ), function<void(unsigned, uint8)>( &SPC7110::mcu_write, &spc7110 ));
Mapping m(function<READ_FUNC>( &SPC7110::mcu_read, &spc7110 ), function<WRITE_FUNC>( &SPC7110::mcu_write, &spc7110 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "offset") spc7110.data_rom_offset = hex(attr.content);
if(attr.name == "offset") spc7110.data_rom_offset = xml_parse_hex(attr.content);
}
mapping.append(m);
}
@ -615,7 +621,7 @@ void Cartridge::xml_parse_spc7110(xml_element &root) {
} else if(node.name == "mmio") {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &SPC7110::mmio_read, &spc7110 ), function<void(unsigned, uint8)>( &SPC7110::mmio_write, &spc7110 ));
Mapping m(function<READ_FUNC>( &SPC7110::mmio_read, &spc7110 ), function<WRITE_FUNC>( &SPC7110::mmio_write, &spc7110 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -624,17 +630,17 @@ void Cartridge::xml_parse_spc7110(xml_element &root) {
}
} else if(node.name == "ram") {
foreach(attr, node.attribute) {
if(attr.name == "size") ram_size = hex(attr.content);
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
}
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &SPC7110::ram_read, &spc7110 ), function<void(unsigned,uint8)>( &SPC7110::ram_write, &spc7110 ));
Mapping m(function<READ_FUNC>( &SPC7110::ram_read, &spc7110 ), function<WRITE_FUNC>( &SPC7110::ram_write, &spc7110 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
if(attr.name == "mode") xml_parse_mode(m, attr.content);
if(attr.name == "offset") m.offset = hex(attr.content);
if(attr.name == "size") m.size = hex(attr.content);
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
}
mapping.append(m);
}
@ -644,7 +650,7 @@ void Cartridge::xml_parse_spc7110(xml_element &root) {
foreach(leaf, node.element) {
if(leaf.name == "map") {
Mapping m(function<uint8(unsigned)>( &SPC7110::mmio_read, &spc7110 ), function<void(unsigned, uint8)>( &SPC7110::mmio_write, &spc7110 ));
Mapping m(function<READ_FUNC>( &SPC7110::mmio_read, &spc7110 ), function<WRITE_FUNC>( &SPC7110::mmio_write, &spc7110 ));
foreach(attr, leaf.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -660,7 +666,7 @@ void Cartridge::xml_parse_obc1(xml_element &root) {
foreach(node, root.element) {
if(node.name == "map") {
Mapping m(function<uint8(unsigned)>( &OBC1::read, &obc1 ), function<void(unsigned, uint8)>( &OBC1::write, &obc1 ));
Mapping m(function<READ_FUNC>( &OBC1::read, &obc1 ), function<WRITE_FUNC>( &OBC1::write, &obc1 ));
foreach(attr, node.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -674,7 +680,7 @@ void Cartridge::xml_parse_setarisc(xml_element &root) {
foreach(node, root.element) {
if(node.name == "map") {
Mapping m(function<uint8(unsigned)>( &ST0018::mmio_read, &st0018 ), function<void(unsigned, uint8)>( &ST0018::mmio_write, &st0018 ));
Mapping m(function<READ_FUNC>( &ST0018::mmio_read, &st0018 ), function<WRITE_FUNC>( &ST0018::mmio_write, &st0018 ));
foreach(attr, node.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -688,7 +694,7 @@ void Cartridge::xml_parse_msu1(xml_element &root) {
foreach(node, root.element) {
if(node.name == "map") {
Mapping m(function<uint8(unsigned)>( &MSU1::mmio_read, &msu1 ), function<void(unsigned, uint8)>( &MSU1::mmio_write, &msu1 ));
Mapping m(function<READ_FUNC>( &MSU1::mmio_read, &msu1 ), function<WRITE_FUNC>( &MSU1::mmio_write, &msu1 ));
foreach(attr, node.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -697,23 +703,19 @@ void Cartridge::xml_parse_msu1(xml_element &root) {
}
}
void Cartridge::xml_parse_serial(xml_element &root) {
has_serial = true;
}
void Cartridge::xml_parse_link(xml_element &root) {
has_link = true;
link.frequency = 1;
link.program = "";
foreach(attr, root.attribute) {
if(attr.name == "frequency") link.frequency = decimal(attr.content);
if(attr.name == "frequency") link.frequency = xml_parse_unsigned(attr.content);
if(attr.name == "program") link.program = attr.content;
}
foreach(node, root.element) {
if(node.name == "map") {
Mapping m(function<uint8(unsigned)>(&Link::read, &link), function<void (unsigned, uint8)>(&Link::write, &link));
Mapping m(function<READ_FUNC>( &Link::read, &link ), function<WRITE_FUNC>( &Link::write, &link ));
foreach(attr, node.attribute) {
if(attr.name == "address") xml_parse_address(m, attr.content);
}
@ -722,6 +724,15 @@ void Cartridge::xml_parse_link(xml_element &root) {
}
}
unsigned Cartridge::xml_parse_hex(const string &s) {
return hex(s);
}
unsigned Cartridge::xml_parse_unsigned(const string &s) {
if(s.beginswith("0x")) return hex(s);
return integer(s);
}
void Cartridge::xml_parse_address(Mapping &m, const string &data) {
lstring part;
part.split(":", data);
@ -759,13 +770,13 @@ Cartridge::Mapping::Mapping() {
}
Cartridge::Mapping::Mapping(Memory &memory) {
read = function<uint8(unsigned)>( &Memory::read, &memory );
write = function<void(unsigned,uint8)>( &Memory::write, &memory );
read = function<READ_FUNC>( &Memory::read, &memory );
write = function<WRITE_FUNC>( &Memory::write, &memory );
mode.i = Bus::MapMode::Direct;
banklo = bankhi = addrlo = addrhi = offset = size = 0;
}
Cartridge::Mapping::Mapping(const function<uint8 (unsigned)> &read_, const function<void (unsigned, uint8)> &write_) {
Cartridge::Mapping::Mapping(const function<READ_FUNC> &read_, const function<WRITE_FUNC> &write_) {
read = read_;
write = write_;
mode.i = Bus::MapMode::Direct;

View File

@ -15,7 +15,7 @@ void Cheat::enable(bool state) {
}
void Cheat::synchronize() {
memcpy(bus.lookup, lookup, 16 * 1024 * 1024);
memset(override, 0x00, 16 * 1024 * 1024);
code_enabled = false;
for(unsigned i = 0; i < size(); i++) {
@ -26,16 +26,16 @@ void Cheat::synchronize() {
code_enabled = true;
unsigned addr = mirror(code.addr[n]);
bus.lookup[addr] = 0xff;
override[addr] = true;
if((addr & 0xffe000) == 0x7e0000) {
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
unsigned mirroraddr;
for(unsigned x = 0; x <= 0x3f; x++) {
mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff);
bus.lookup[mirroraddr] = 0xff;
override[mirroraddr] = true;
mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff);
bus.lookup[mirroraddr] = 0xff;
override[mirroraddr] = true;
}
}
}
@ -61,29 +61,17 @@ uint8 Cheat::read(unsigned addr) const {
return 0x00;
}
uint8 Cheat::default_reader(unsigned addr) {
bus.reader[cheat.lookup[addr]](bus.target[addr]);
return cheat.read(addr);
}
void Cheat::default_writer(unsigned addr, uint8 data) {
return bus.writer[cheat.lookup[addr]](bus.target[addr], data);
}
void Cheat::init() {
bus.reader[0xff] = function<uint8(unsigned)>(&Cheat::default_reader);
bus.writer[0xff] = function<void(unsigned, uint8)>(&Cheat::default_writer);
memcpy(lookup, bus.lookup, 16 * 1024 * 1024);
memset(override, 0x00, 16 * 1024 * 1024);
}
Cheat::Cheat() {
lookup = new uint8[16 * 1024 * 1024];
override = new uint8[16 * 1024 * 1024];
system_enabled = true;
}
Cheat::~Cheat() {
delete[] lookup;
delete[] override;
}
//===============

View File

@ -9,7 +9,8 @@ struct CheatCode {
class Cheat : public linear_vector<CheatCode> {
public:
struct Type{ enum e{ ProActionReplay, GameGenie } i; };
struct Type { enum e { ProActionReplay, GameGenie } i; };
uint8 *override;
bool enabled() const;
void enable(bool);
@ -24,14 +25,10 @@ public:
static bool encode(string&, unsigned, uint8, Type);
private:
uint8 *lookup;
bool system_enabled;
bool code_enabled;
bool cheat_enabled;
unsigned mirror(unsigned) const;
static uint8 default_reader(unsigned);
static void default_writer(unsigned, uint8);
};
extern Cheat cheat;

View File

@ -17,7 +17,6 @@ struct Coprocessor : Processor {
#include <snes/chip/st0018/st0018.hpp>
#include <snes/chip/sufamiturbo/sufamiturbo.hpp>
#include <snes/chip/msu1/msu1.hpp>
#include <snes/chip/serial/serial.hpp>
#include <snes/chip/link/link.hpp>
void Coprocessor::step(unsigned clocks) {

View File

@ -78,7 +78,7 @@ void HitachiDSP::exec() {
else if((opcode & 0xdd00) == 0x1400) {
//00.1 01.0 .... ....
//jumpmi
//jumpmi i
if(regs.n) {
if(opcode & 0x2000) push();
regs.pc = np();
@ -87,7 +87,7 @@ void HitachiDSP::exec() {
else if((opcode & 0xffff) == 0x1c00) {
//0001 1100 0000 0000
//loop/wait?
//loop?
}
else if((opcode & 0xfffe) == 0x2500) {
@ -110,6 +110,7 @@ void HitachiDSP::exec() {
else if((opcode & 0xffff) == 0x3c00) {
//0011 1100 0000 0000
//ret
pull();
}
@ -121,7 +122,7 @@ void HitachiDSP::exec() {
else if((opcode & 0xf800) == 0x4800) {
//0100 1... .... ....
//rcmp a<<n,ri
//cmpr a<<n,ri
int result = ri() - sa();
regs.n = result & 0x800000;
regs.z = (uint24)result == 0;
@ -195,11 +196,13 @@ void HitachiDSP::exec() {
else if((opcode & 0xff00) == 0x7c00) {
//0111 1100 .... ....
//ld pl,i
regs.p = (regs.p & 0xff00) | ((opcode & 0xff) << 0);
}
else if((opcode & 0xff00) == 0x7d00) {
//0111 1101 .... ....
//ld ph,i
regs.p = (regs.p & 0x00ff) | ((opcode & 0xff) << 8);
}
@ -215,7 +218,7 @@ void HitachiDSP::exec() {
else if((opcode & 0xf800) == 0x8800) {
//1000 1... .... ....
//rsb a<<n,ri
//subr a<<n,ri
int result = ri() - sa();
regs.a = result;
regs.n = regs.a & 0x800000;
@ -247,7 +250,7 @@ void HitachiDSP::exec() {
else if((opcode & 0xf800) == 0xa800) {
//1010 1... .... ....
//xor a,ri
//xor a<<n,ri
regs.a = sa() ^ ri();
regs.n = regs.a & 0x800000;
regs.z = regs.a == 0;

View File

@ -9,7 +9,7 @@ void Link::Enter() { link.enter(); }
void Link::enter() {
while(true) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
unsigned clocks = 1;
if(link_run) clocks = link_run();
step(clocks);

View File

@ -1,16 +1,16 @@
#ifdef NECDSP_CPP
uint8 NECDSP::sr_read(unsigned) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
return regs.sr >> 8;
}
void NECDSP::sr_write(unsigned, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
}
uint8 NECDSP::dr_read(unsigned) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
if(regs.sr.drc == 0) {
//16-bit
if(regs.sr.drs == 0) {
@ -29,7 +29,7 @@ uint8 NECDSP::dr_read(unsigned) {
}
void NECDSP::dr_write(unsigned, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
if(regs.sr.drc == 0) {
//16-bit
if(regs.sr.drs == 0) {
@ -48,7 +48,7 @@ void NECDSP::dr_write(unsigned, uint8 data) {
}
uint8 NECDSP::dp_read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
bool hi = addr & 1;
addr = (addr >> 1) & 2047;
@ -60,7 +60,7 @@ uint8 NECDSP::dp_read(unsigned addr) {
}
void NECDSP::dp_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
bool hi = addr & 1;
addr = (addr >> 1) & 2047;

View File

@ -7,12 +7,12 @@ unsigned SA1::CPUIRAM::size() const {
}
uint8 SA1::CPUIRAM::read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
return sa1.iram.read(addr);
}
void SA1::CPUIRAM::write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
sa1.iram.write(addr, data);
}
@ -21,13 +21,13 @@ unsigned SA1::CPUBWRAM::size() const {
}
uint8 SA1::CPUBWRAM::read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
if(dma) return sa1.dma_cc1_read(addr);
return cartridge.ram.read(addr);
}
void SA1::CPUBWRAM::write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
cartridge.ram.write(addr, data);
}

View File

@ -182,13 +182,13 @@ void SA1::mmc_write(unsigned addr, uint8 data) {
}
uint8 SA1::mmc_cpu_read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
return cpubwram.read(addr);
}
void SA1::mmc_cpu_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
cpubwram.write(addr, data);
}

View File

@ -444,7 +444,7 @@ uint8 SA1::mmio_r230e() {
}
uint8 SA1::mmio_read(unsigned addr) {
(co_active() == cpu.thread ? cpu.synchronize_coprocessor() : synchronize_cpu());
(co_active() == cpu.thread ? cpu.synchronize_coprocessors() : synchronize_cpu());
addr &= 0xffff;
switch(addr) {
@ -469,7 +469,7 @@ uint8 SA1::mmio_read(unsigned addr) {
}
void SA1::mmio_write(unsigned addr, uint8 data) {
(co_active() == cpu.thread ? cpu.synchronize_coprocessor() : synchronize_cpu());
(co_active() == cpu.thread ? cpu.synchronize_coprocessors() : synchronize_cpu());
addr &= 0xffff;
switch(addr) {

View File

@ -1,109 +0,0 @@
#include <snes/snes.hpp>
#define SERIAL_CPP
namespace SNES {
Serial serial;
#include "serialization.cpp"
static void snesserial_tick(unsigned clocks) { serial.add_clocks(clocks * 8); }
static uint8 snesserial_read() { return serial.read(); }
static void snesserial_write(uint8 data) { serial.write(data); }
void Serial::Enter() { serial.enter(); }
void Serial::enter() {
data1 = 0;
data2 = 0;
add_clocks(256 * 8); //warm-up
if(flowcontrol()) data2 = 1;
if(main) main(snesserial_tick, snesserial_read, snesserial_write);
while(true) add_clocks(frequency); //snesserial_main() fallback
}
void Serial::add_clocks(unsigned clocks) {
step(clocks);
synchronize_cpu();
}
uint8 Serial::read() {
while(cpu.joylatch() == 0) add_clocks(1);
while(cpu.joylatch() == 1) add_clocks(1);
add_clocks(4);
uint8 data = 0;
for(unsigned i = 0; i < 8; i++) {
add_clocks(8);
data = (cpu.joylatch() << 7) | (data >> 1);
}
return data;
}
void Serial::write(uint8 data) {
if(flowcontrol()) while(cpu.pio() & 0x80) add_clocks(1);
add_clocks(8);
data1 = 1;
add_clocks(8);
for(unsigned i = 0; i < 8; i++) {
data1 = (data & 1) ^ 1;
data >>= 1;
add_clocks(8);
}
data1 = 0;
add_clocks(8);
}
uint8 Serial::mmio_read(unsigned addr) {
cpu.synchronize_coprocessor();
switch(addr & 1) { default:
case 0: return cpu.mmio_read(addr);
case 1: return cpu.mmio_read(addr);
}
}
void Serial::mmio_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
switch(addr & 1) { default:
case 0: cpu.mmio_write(addr, data); break;
case 1: cpu.mmio_write(addr, data); break;
}
}
void Serial::init() {
}
void Serial::load() {
if(opened()) close();
string basename = system.interface->path(Cartridge::Slot::Base, "");
string name = notdir(basename);
string path = dir(basename);
if(open(name, path)) {
baudrate = sym("snesserial_baudrate");
flowcontrol = sym("snesserial_flowcontrol");
main = sym("snesserial_main");
}
function<uint8(unsigned)> reader(&Serial::mmio_read, &serial);
function<void(unsigned, uint8)> writer(&Serial::mmio_write, &serial);
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, reader, writer);
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, reader, writer);
}
void Serial::unload() {
if(opened()) close();
}
void Serial::power() {
reset();
}
void Serial::reset() {
create(Serial::Enter, baudrate() * 8);
}
}

View File

@ -1,28 +0,0 @@
class Serial : public Coprocessor, public library, public property<Serial> {
public:
static void Enter();
void enter();
void init();
void load();
void unload();
void power();
void reset();
void serialize(serializer&);
readonly<bool> data1;
readonly<bool> data2;
void add_clocks(unsigned clocks);
uint8 read();
void write(uint8 data);
uint8 mmio_read(unsigned addr);
void mmio_write(unsigned addr, uint8 data);
private:
function<unsigned ()> baudrate;
function<bool ()> flowcontrol;
function<void (void (*)(unsigned), uint8_t (*)(), void (*)(uint8_t))> main;
};
extern Serial serial;

View File

@ -1,9 +0,0 @@
#ifdef SERIAL_CPP
void Serial::serialize(serializer &s) {
Processor::serialize(s);
s.integer((bool&)data1);
s.integer((bool&)data2);
}
#endif

View File

@ -1,7 +1,7 @@
#ifdef SUPERFX_CPP
uint8 SuperFX::mmio_read(unsigned addr) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
addr &= 0xffff;
if(addr >= 0x3100 && addr <= 0x32ff) {
@ -53,7 +53,7 @@ uint8 SuperFX::mmio_read(unsigned addr) {
}
void SuperFX::mmio_write(unsigned addr, uint8 data) {
cpu.synchronize_coprocessor();
cpu.synchronize_coprocessors();
addr &= 0xffff;
if(addr >= 0x3100 && addr <= 0x32ff) {

View File

@ -0,0 +1,55 @@
#include <snes/snes.hpp>
#define CONTROLLER_CPP
namespace SNES {
#include "gamepad/gamepad.cpp"
#include "multitap/multitap.cpp"
#include "mouse/mouse.cpp"
#include "superscope/superscope.cpp"
#include "justifier/justifier.cpp"
#include "serial/serial.cpp"
void Controller::Enter() {
if(co_active() == input.port1->thread) input.port1->enter();
if(co_active() == input.port2->thread) input.port2->enter();
}
void Controller::enter() {
while(true) step(1);
}
void Controller::step(unsigned clocks) {
clock += clocks * (uint64)cpu.frequency;
synchronize_cpu();
}
void Controller::synchronize_cpu() {
if(CPU::Threaded == true) {
if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
} else {
while(clock >= 0) cpu.enter();
}
}
bool Controller::iobit() {
switch(port ? 1 : 0) {
case Controller::Port1: return cpu.pio() & 0x40;
case Controller::Port2: return cpu.pio() & 0x80;
}
return false;
}
void Controller::iobit(bool data) {
switch(port ? 1 : 0) {
case Controller::Port1: bus.write(0x4201, (cpu.pio() & ~0x40) | (data << 6)); break;
case Controller::Port2: bus.write(0x4201, (cpu.pio() & ~0x80) | (data << 7)); break;
}
}
Controller::Controller(bool port) : port(port) {
if(!thread) create(Controller::Enter, 1);
}
}

View File

@ -0,0 +1,35 @@
// SNES controller port pinout:
// -------------------------------
// | (1) (2) (3) (4) | (5) (6) (7) )
// -------------------------------
// pin name port1 port2
// 1: +5v
// 2: clock $4016 read $4017 read
// 3: latch $4016.d0 write $4016.d0 write
// 4: data1 $4016.d0 read $4017.d0 read
// 5: data2 $4016.d1 read $4017.d1 read
// 6: iobit $4201.d6 write; $4213.d6 read $4201.d7 write; $4213.d7 read
// 7: gnd
struct Controller : Processor {
enum { Port1 = 0, Port2 = 1 };
const bool port;
static void Enter();
virtual void enter();
void step(unsigned clocks);
void synchronize_cpu();
bool iobit();
void iobit(bool data);
virtual uint2 data() { return 0; }
virtual void latch(bool data) {}
Controller(bool port);
};
#include "gamepad/gamepad.hpp"
#include "multitap/multitap.hpp"
#include "mouse/mouse.hpp"
#include "superscope/superscope.hpp"
#include "justifier/justifier.hpp"
#include "serial/serial.hpp"

View File

@ -0,0 +1,21 @@
#ifdef CONTROLLER_CPP
uint2 Gamepad::data() {
if(counter >= 16) return 1;
uint2 result = system.interface->input_poll(port, Input::Device::Joypad, 0, counter);
if(latched == 0) counter++;
return result;
}
void Gamepad::latch(bool data) {
if(latched == data) return;
latched = data;
counter = 0;
}
Gamepad::Gamepad(bool port) : Controller(port) {
latched = 0;
counter = 0;
}
#endif

View File

@ -0,0 +1,9 @@
struct Gamepad : Controller {
uint2 data();
void latch(bool data);
Gamepad(bool port);
private:
bool latched;
unsigned counter;
};

View File

@ -0,0 +1,133 @@
#ifdef CONTROLLER_CPP
void Justifier::enter() {
unsigned prev = 0;
while(true) {
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
signed x = (active == 0 ? x1 : x2), y = (active == 0 ? y1 : y2);
bool offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
if(offscreen == false) {
unsigned target = y * 1364 + (x + 24) * 4;
if(next >= target && prev < target) {
//CRT raster detected, toggle iobit to latch counters
iobit(0);
iobit(1);
}
}
if(next < prev) {
int nx1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
int ny1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
nx1 += x1;
ny1 += y1;
x1 = max(-16, min(256 + 16, nx1));
y1 = max(-16, min(240 + 16, ny1));
if(chained == true) {
int nx2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
int ny2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
nx2 += x2;
ny2 += y2;
x2 = max(-16, min(256 + 16, nx2));
y2 = max(-16, min(240 + 16, ny2));
}
} else {
//sleep until PPU counters are close to latch position
unsigned diff = abs((signed)y - cpu.vcounter());
if(diff >= 2) step((diff - 2) * 1364);
}
prev = next;
step(2);
}
}
uint2 Justifier::data() {
if(counter >= 32) return 1;
if(counter == 0) {
trigger1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger);
start1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start);
if(chained) {
trigger2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger);
start2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start);
}
}
switch(counter++) {
case 0: return 0;
case 1: return 0;
case 2: return 0;
case 3: return 0;
case 4: return 0;
case 5: return 0;
case 6: return 0;
case 7: return 0;
case 8: return 0;
case 9: return 0;
case 10: return 0;
case 11: return 0;
case 12: return 1; //signature
case 13: return 1; // ||
case 14: return 1; // ||
case 15: return 0; // ||
case 16: return 0;
case 17: return 1;
case 18: return 0;
case 19: return 1;
case 20: return 0;
case 21: return 1;
case 22: return 0;
case 23: return 1;
case 24: return trigger1;
case 25: return trigger2;
case 26: return start1;
case 27: return start2;
case 28: return active;
case 29: return 0;
case 30: return 0;
case 31: return 0;
}
return 0;
}
void Justifier::latch(bool data) {
if(latched == data) return;
latched = data;
counter = 0;
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
}
Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chained) {
create(Controller::Enter, 21477272);
latched = 0;
counter = 0;
active = 0;
if(chained == false) {
x1 = 256 / 2;
y1 = 240 / 2;
x2 = -1;
y2 = -1;
} else {
x1 = 256 / 2 - 16;
y1 = 240 / 2;
x2 = 256 / 2 + 16;
y2 = 240 / 2;
}
trigger1 = false;
trigger2 = false;
start1 = false;
start2 = false;
}
#endif

View File

@ -0,0 +1,17 @@
struct Justifier : Controller {
void enter();
uint2 data();
void latch(bool data);
Justifier(bool port, bool chained);
//private:
const bool chained; //true if the second justifier is attached to the first
bool latched;
unsigned counter;
bool active;
signed x1, x2;
signed y1, y2;
bool trigger1, trigger2;
bool start1, start2;
};

View File

@ -0,0 +1,69 @@
#ifdef CONTROLLER_CPP
uint2 Mouse::data() {
if(counter >= 32) return 1;
int position_x = system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
int position_y = system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
bool direction_x = position_x < 0; //0 = right, 1 = left
bool direction_y = position_y < 0; //0 = down, 1 = up
if(position_x < 0) position_x = -position_x; //abs(position_x)
if(position_y < 0) position_y = -position_y; //abs(position_y)
position_x = min(127, position_x); //range = 0 - 127
position_y = min(127, position_y);
switch(counter++) { default:
case 0: return 0;
case 1: return 0;
case 2: return 0;
case 3: return 0;
case 4: return 0;
case 5: return 0;
case 6: return 0;
case 7: return 0;
case 8: return system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right);
case 9: return system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Left);
case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
case 11: return 0; // ||
case 12: return 0; //signature
case 13: return 0; // ||
case 14: return 0; // ||
case 15: return 1; // ||
case 16: return (direction_y);
case 17: return (position_y >> 6) & 1;
case 18: return (position_y >> 5) & 1;
case 19: return (position_y >> 4) & 1;
case 20: return (position_y >> 3) & 1;
case 21: return (position_y >> 2) & 1;
case 22: return (position_y >> 1) & 1;
case 23: return (position_y >> 0) & 1;
case 24: return (direction_x);
case 25: return (position_x >> 6) & 1;
case 26: return (position_x >> 5) & 1;
case 27: return (position_x >> 4) & 1;
case 28: return (position_x >> 3) & 1;
case 29: return (position_x >> 2) & 1;
case 30: return (position_x >> 1) & 1;
case 31: return (position_x >> 0) & 1;
}
}
void Mouse::latch(bool data) {
if(latched == data) return;
latched = data;
counter = 0;
}
Mouse::Mouse(bool port) : Controller(port) {
latched = 0;
counter = 0;
}
#endif

View File

@ -0,0 +1,9 @@
struct Mouse : Controller {
uint2 data();
void latch(bool data);
Mouse(bool port);
private:
bool latched;
unsigned counter;
};

View File

@ -0,0 +1,39 @@
#ifdef CONTROLLER_CPP
uint2 Multitap::data() {
if(latched) return 2; //multitap detection
unsigned index, port1, port2;
if(iobit()) {
index = counter1;
if(index >= 16) return 3;
counter1++;
port1 = 0; //controller 1
port2 = 1; //controller 2
} else {
index = counter2;
if(index >= 16) return 3;
counter2++;
port1 = 2; //controller 3
port2 = 3; //controller 4
}
bool data1 = system.interface->input_poll(port, Input::Device::Multitap, port1, index);
bool data2 = system.interface->input_poll(port, Input::Device::Multitap, port2, index);
return (data2 << 1) | (data1 << 0);
}
void Multitap::latch(bool data) {
if(latched == data) return;
latched = data;
counter1 = 0;
counter2 = 0;
}
Multitap::Multitap(bool port) : Controller(port) {
latched = 0;
counter1 = 0;
counter2 = 0;
}
#endif

View File

@ -0,0 +1,10 @@
struct Multitap : Controller {
uint2 data();
void latch(bool data);
Multitap(bool port);
private:
bool latched;
unsigned counter1;
unsigned counter2;
};

View File

@ -0,0 +1,148 @@
#ifdef CONTROLLER_CPP
//Serial communications cable emulation:
//The SNES controller ports can be used for bi-directional serial communication
//when wired to a specialized controller. This class implements said controller,
//for the primary purpose of testing code outside of real hardware.
//The basic idea is to wire the SNES controller pins to a UART, such as
//the MAX232N; or a serial->USB cable, such as the FTDI TTL-232R-5V.
//Connection Diagram:
//[SNES] [UART] [Purpose]
// Latch RXD Data transfer
// Data1 TXD Data transfer
// Data2 RTS Flow control (optional)
// IOBit CTS Flow control (optional)
// GND GND Circuit completion
//The SNES software program will have to use specially timed code to send and
//receive data at a specific baud-rate; whereas the PC handles timing via the
//UART.
//The emulator implementation is designed so that the same PC-side program can
//be used both under emulation and on real hardware. It does this by linking to
//a dynamic library for timing, read and write operations. This library is
//responsible for setting both the baud-rate and flow control setting. The
//SNES-side software program must know about and respect the library setting.
static void snesserial_tick(unsigned clocks);
static uint8 snesserial_read();
static void snesserial_write(uint8 data);
void Serial::enter() {
if(enable == false) while(true) step(1); //fallback, in case library was not found
step(256 * 8); //simulate warm-up delay
if(flowcontrol()) data2 = 1;
main(snesserial_tick, snesserial_read, snesserial_write); //stubs for Serial::step, Serial::read, Serial::write
while(true) step(1); //fallback, in case snesserial_main() returns (it should never do so)
}
uint8 Serial::read() {
while(latched == 0) step(1);
while(latched == 1) step(1);
step(4);
uint8 data = 0;
for(unsigned i = 0; i < 8; i++) {
step(8);
data = (latched << 7) | (data >> 1);
}
return data;
}
void Serial::write(uint8 data) {
if(flowcontrol()) while(iobit()) step(1);
step(8);
data1 = 1;
step(8);
for(unsigned i = 0; i < 8; i++) {
data1 = (data & 1) ^ 1;
data >>= 1;
step(8);
}
data1 = 0;
step(8);
}
uint2 Serial::data() {
return (data2 << 1) | (data1 << 0);
}
void Serial::latch(bool data) {
latched = data;
}
Serial::Serial(bool port) : Controller(port) {
enable = false;
string basename = system.interface->path(Cartridge::Slot::Base, "");
string name = notdir(basename);
string path = dir(basename);
if(open(name, path)) {
baudrate = sym("snesserial_baudrate");
flowcontrol = sym("snesserial_flowcontrol");
main = sym("snesserial_main");
if(baudrate && flowcontrol && main) enable = true;
}
create(Controller::Enter, enable ? baudrate() * 8 : 1);
latched = false;
data1 = 0;
data2 = 0;
}
Serial::~Serial() {
if(opened()) close();
}
//stubs needed to call into class objects from global function pointers
static void snesserial_tick(unsigned clocks) {
if(co_active() == input.port1->thread) {
if(dynamic_cast<Serial*>(input.port1)) {
return ((Serial*)input.port1)->step(clocks);
}
}
if(co_active() == input.port2->thread) {
if(dynamic_cast<Serial*>(input.port2)) {
return ((Serial*)input.port2)->step(clocks);
}
}
}
static uint8 snesserial_read() {
if(co_active() == input.port1->thread) {
if(dynamic_cast<Serial*>(input.port1)) {
return ((Serial*)input.port1)->read();
}
}
if(co_active() == input.port2->thread) {
if(dynamic_cast<Serial*>(input.port2)) {
return ((Serial*)input.port2)->read();
}
}
return 0;
}
static void snesserial_write(uint8 data) {
if(co_active() == input.port1->thread) {
if(dynamic_cast<Serial*>(input.port1)) {
return ((Serial*)input.port1)->write(data);
}
}
if(co_active() == input.port2->thread) {
if(dynamic_cast<Serial*>(input.port2)) {
return ((Serial*)input.port2)->write(data);
}
}
}
#endif

View File

@ -0,0 +1,19 @@
struct Serial : Controller, public library {
void enter();
uint8 read();
void write(uint8 data);
uint2 data();
void latch(bool data);
Serial(bool port);
~Serial();
private:
bool enable;
function<unsigned ()> baudrate;
function<bool ()> flowcontrol;
function<void (void (*)(unsigned), uint8_t (*)(), void (*)(uint8_t))> main;
bool latched;
bool data1;
bool data2;
};

View File

@ -0,0 +1,129 @@
#ifdef CONTROLLER_CPP
//The Super Scope is a light-gun: it detects the CRT beam cannon position,
//and latches the counters by toggling iobit. This only works on controller
//port 2, as iobit there is connected to the PPU H/V counter latch.
//(PIO $4201.d7)
//It is obviously not possible to perfectly simulate an IR light detecting
//a CRT beam cannon, hence this class will read the PPU raster counters.
//A Super Scope can still technically be used in port 1, however it would
//require manual polling of PIO ($4201.d6) to determine when iobit was written.
//Note that no commercial game ever utilizes a Super Scope in port 1.
void SuperScope::enter() {
unsigned prev = 0;
while(true) {
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
if(offscreen == false) {
unsigned target = y * 1364 + (x + 24) * 4;
if(next >= target && prev < target) {
//CRT raster detected, toggle iobit to latch counters
iobit(0);
iobit(1);
}
}
if(next < prev) {
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
int nx = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
int ny = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
nx += x;
ny += y;
x = max(-16, min(256 + 16, nx));
y = max(-16, min(240 + 16, ny));
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
} else {
//sleep until PPU counters are close to latch position
unsigned diff = abs((signed)y - cpu.vcounter());
if(diff >= 2) step((diff - 2) * 1364);
}
prev = next;
step(2);
}
}
uint2 SuperScope::data() {
if(counter >= 8) return 1;
if(counter == 0) {
//turbo is a switch; toggle is edge sensitive
bool newturbo = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo);
if(newturbo && !turbo) {
turbo = !turbo; //toggle state
turbolock = true;
} else {
turbolock = false;
}
//trigger is a button
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
trigger = false;
bool newtrigger = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger);
if(newtrigger && (turbo || !triggerlock)) {
trigger = true;
triggerlock = true;
} else if(!newtrigger) {
triggerlock = false;
}
//cursor is a button; it is always level sensitive
cursor = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor);
//pause is a button; it is always edge sensitive
pause = false;
bool newpause = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause);
if(newpause && !pauselock) {
pause = true;
pauselock = true;
} else if(!newpause) {
pauselock = false;
}
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
}
switch(counter++) {
case 0: return offscreen ? 0 : trigger;
case 1: return cursor;
case 2: return turbo;
case 3: return pause;
case 4: return 0;
case 5: return 0;
case 6: return offscreen;
case 7: return 0; //noise (1 = yes)
}
return 0;
}
void SuperScope::latch(bool data) {
if(latched == data) return;
latched = data;
counter = 0;
}
SuperScope::SuperScope(bool port) : Controller(port) {
create(Controller::Enter, 21477272);
latched = 0;
counter = 0;
//center cursor onscreen
x = 256 / 2;
y = 240 / 2;
trigger = false;
cursor = false;
turbo = false;
pause = false;
offscreen = false;
turbolock = false;
triggerlock = false;
pauselock = false;
}
#endif

View File

@ -0,0 +1,22 @@
struct SuperScope : Controller {
void enter();
uint2 data();
void latch(bool data);
SuperScope(bool port);
//private:
bool latched;
unsigned counter;
signed x, y;
bool trigger;
bool cursor;
bool turbo;
bool pause;
bool offscreen;
bool turbolock;
bool triggerlock;
bool pauselock;
};

View File

@ -23,6 +23,9 @@ void CPU::step(unsigned clocks) {
Processor &chip = *coprocessors[i];
chip.clock -= clocks * (uint64)chip.frequency;
}
input.port1->clock -= clocks * (uint64)input.port1->frequency;
input.port2->clock -= clocks * (uint64)input.port2->frequency;
synchronize_controllers();
}
void CPU::synchronize_smp() {
@ -41,13 +44,18 @@ void CPU::synchronize_ppu() {
}
}
void CPU::synchronize_coprocessor() {
void CPU::synchronize_coprocessors() {
for(unsigned i = 0; i < coprocessors.size(); i++) {
Processor &chip = *coprocessors[i];
if(chip.clock < 0) co_switch(chip.thread);
}
}
void CPU::synchronize_controllers() {
if(input.port1->clock < 0) co_switch(input.port1->thread);
if(input.port2->clock < 0) co_switch(input.port2->thread);
}
void CPU::Enter() { cpu.enter(); }
void CPU::enter() {

View File

@ -7,7 +7,8 @@ public:
alwaysinline void step(unsigned clocks);
alwaysinline void synchronize_smp();
void synchronize_ppu();
void synchronize_coprocessor();
void synchronize_coprocessors();
void synchronize_controllers();
uint8 port_read(uint2 port) const;
void port_write(uint2 port, uint8 data);

View File

@ -33,10 +33,8 @@ void CPU::mmio_w2183(uint8 data) {
//strobing $4016.d0 affects both controller port latches.
//$4017 bit 0 writes are ignored.
void CPU::mmio_w4016(uint8 data) {
bool old_latch = status.joypad_strobe_latch;
bool new_latch = data & 1;
status.joypad_strobe_latch = new_latch;
if(old_latch != new_latch) input.poll();
input.port1->latch(data & 1);
input.port2->latch(data & 1);
}
//JOYSER0
@ -44,7 +42,7 @@ void CPU::mmio_w4016(uint8 data) {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4016() {
uint8 r = regs.mdr & 0xfc;
r |= input.port_read(0) & 3;
r |= input.port1->data();
return r;
}
@ -54,7 +52,7 @@ uint8 CPU::mmio_r4016() {
//1-0 = Joypad serial data
uint8 CPU::mmio_r4017() {
uint8 r = (regs.mdr & 0xe0) | 0x1c;
r |= input.port_read(1) & 3;
r |= input.port2->data();
return r;
}

View File

@ -6,10 +6,15 @@ void CPU::step_auto_joypad_poll() {
status.auto_joypad_active = status.auto_joypad_counter <= 15;
if(status.auto_joypad_active && status.auto_joypad_poll) {
if(status.auto_joypad_counter == 0) input.poll();
if(status.auto_joypad_counter == 0) {
input.port1->latch(1);
input.port2->latch(1);
input.port1->latch(0);
input.port2->latch(0);
}
uint8 port0 = input.port_read(0);
uint8 port1 = input.port_read(1);
uint2 port0 = input.port1->data();
uint2 port1 = input.port2->data();
status.joy1 = (status.joy1 << 1) | (bool)(port0 & 1);
status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1);

View File

@ -12,10 +12,7 @@ void CPU::add_clocks(unsigned clocks) {
unsigned ticks = clocks >> 1;
while(ticks--) {
tick();
if(hcounter() & 2) {
input.tick();
poll_interrupts();
}
if(hcounter() & 2) poll_interrupts();
}
step(clocks);
@ -40,7 +37,7 @@ void CPU::scanline() {
//forcefully sync S-CPU to other processors, in case chips are not communicating
synchronize_ppu();
synchronize_smp();
synchronize_coprocessor();
synchronize_coprocessors();
system.scanline();
if(vcounter() == 0) {

View File

@ -2,348 +2,38 @@
Input input;
uint8 Input::port_read(bool portnumber) {
if(cartridge.has_serial() && portnumber == 1) {
return (serial.data2() << 1) | (serial.data1() << 0);
void Input::connect(bool port, Input::Device::e id) {
Controller *&controller = ((port ? 1 : 0) == Controller::Port1 ? port1 : port2);
if(controller) {
delete controller;
controller = 0;
}
port_t &p = port[portnumber];
switch(p.device.i) {
case Device::Joypad: {
if(cpu.joylatch() == 0) {
if(p.counter0 >= 16) return 1;
return system.interface->input_poll(portnumber, p.device.i, 0, p.counter0++);
} else {
return system.interface->input_poll(portnumber, p.device.i, 0, 0);
}
} //case Device::Joypad
case Device::Multitap: {
if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0
unsigned deviceidx, deviceindex0, deviceindex1;
uint8 mask = (portnumber == 0 ? 0x40 : 0x80);
if(cpu.pio() & mask) {
deviceidx = p.counter0;
if(deviceidx >= 16) return 3;
p.counter0++;
deviceindex0 = 0; //controller 1
deviceindex1 = 1; //controller 2
} else {
deviceidx = p.counter1;
if(deviceidx >= 16) return 3;
p.counter1++;
deviceindex0 = 2; //controller 3
deviceindex1 = 3; //controller 4
}
return (system.interface->input_poll(portnumber, p.device.i, deviceindex0, deviceidx) << 0)
| (system.interface->input_poll(portnumber, p.device.i, deviceindex1, deviceidx) << 1);
} //case Device::Multitap
case Device::Mouse: {
if(p.counter0 >= 32) return 1;
int position_x = system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)MouseID::X); //-n = left, 0 = center, +n = right
int position_y = system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)MouseID::Y); //-n = up, 0 = center, +n = right
bool direction_x = position_x < 0; //0 = right, 1 = left
bool direction_y = position_y < 0; //0 = down, 1 = up
if(position_x < 0) position_x = -position_x; //abs(position_x)
if(position_y < 0) position_y = -position_y; //abs(position_x)
position_x = min(127, position_x); //range = 0 - 127
position_y = min(127, position_y); //range = 0 - 127
switch(p.counter0++) { default:
case 0: return 0;
case 1: return 0;
case 2: return 0;
case 3: return 0;
case 4: return 0;
case 5: return 0;
case 6: return 0;
case 7: return 0;
case 8: return system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)MouseID::Right);
case 9: return system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)MouseID::Left);
case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
case 11: return 0; // ||
case 12: return 0; //signature
case 13: return 0; // ||
case 14: return 0; // ||
case 15: return 1; // ||
case 16: return (direction_y) & 1;
case 17: return (position_y >> 6) & 1;
case 18: return (position_y >> 5) & 1;
case 19: return (position_y >> 4) & 1;
case 20: return (position_y >> 3) & 1;
case 21: return (position_y >> 2) & 1;
case 22: return (position_y >> 1) & 1;
case 23: return (position_y >> 0) & 1;
case 24: return (direction_x) & 1;
case 25: return (position_x >> 6) & 1;
case 26: return (position_x >> 5) & 1;
case 27: return (position_x >> 4) & 1;
case 28: return (position_x >> 3) & 1;
case 29: return (position_x >> 2) & 1;
case 30: return (position_x >> 1) & 1;
case 31: return (position_x >> 0) & 1;
}
} //case Device::Mouse
case Device::SuperScope: {
if(portnumber == 0) break; //Super Scope in port 1 not supported ...
if(p.counter0 >= 8) return 1;
if(p.counter0 == 0) {
//turbo is a switch; toggle is edge sensitive
bool turbo = system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)SuperScopeID::Turbo);
if(turbo && !p.superscope.turbolock) {
p.superscope.turbo = !p.superscope.turbo; //toggle state
p.superscope.turbolock = true;
} else if(!turbo) {
p.superscope.turbolock = false;
}
//trigger is a button
//if turbo is active, trigger is level sensitive; otherwise it is edge sensitive
p.superscope.trigger = false;
bool trigger = system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)SuperScopeID::Trigger);
if(trigger && (p.superscope.turbo || !p.superscope.triggerlock)) {
p.superscope.trigger = true;
p.superscope.triggerlock = true;
} else if(!trigger) {
p.superscope.triggerlock = false;
}
//cursor is a button; it is always level sensitive
p.superscope.cursor = system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)SuperScopeID::Cursor);
//pause is a button; it is always edge sensitive
p.superscope.pause = false;
bool pause = system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)SuperScopeID::Pause);
if(pause && !p.superscope.pauselock) {
p.superscope.pause = true;
p.superscope.pauselock = true;
} else if(!pause) {
p.superscope.pauselock = false;
}
p.superscope.offscreen =
p.superscope.x < 0 || p.superscope.x >= 256
|| p.superscope.y < 0 || p.superscope.y >= (ppu.overscan() ? 240 : 225);
}
switch(p.counter0++) {
case 0: return p.superscope.trigger;
case 1: return p.superscope.cursor;
case 2: return p.superscope.turbo;
case 3: return p.superscope.pause;
case 4: return 0;
case 5: return 0;
case 6: return p.superscope.offscreen;
case 7: return 0; //noise (1 = yes)
}
} //case Device::SuperScope
case Device::Justifier:
case Device::Justifiers: {
if(portnumber == 0) break; //Justifier in port 1 not supported ...
if(p.counter0 >= 32) return 1;
if(p.counter0 == 0) {
p.justifier.trigger1 = system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)JustifierID::Trigger);
p.justifier.start1 = system.interface->input_poll(portnumber, p.device.i, 0, (unsigned)JustifierID::Start);
if(p.device.i == Device::Justifiers) {
p.justifier.trigger2 = system.interface->input_poll(portnumber, p.device.i, 1, (unsigned)JustifierID::Trigger);
p.justifier.start2 = system.interface->input_poll(portnumber, p.device.i, 1, (unsigned)JustifierID::Start);
} else {
p.justifier.x2 = -1;
p.justifier.y2 = -1;
p.justifier.trigger2 = false;
p.justifier.start2 = false;
}
}
switch(p.counter0++) {
case 0: return 0;
case 1: return 0;
case 2: return 0;
case 3: return 0;
case 4: return 0;
case 5: return 0;
case 6: return 0;
case 7: return 0;
case 8: return 0;
case 9: return 0;
case 10: return 0;
case 11: return 0;
case 12: return 1; //signature
case 13: return 1; // ||
case 14: return 1; // ||
case 15: return 0; // ||
case 16: return 0;
case 17: return 1;
case 18: return 0;
case 19: return 1;
case 20: return 0;
case 21: return 1;
case 22: return 0;
case 23: return 1;
case 24: return p.justifier.trigger1;
case 25: return p.justifier.trigger2;
case 26: return p.justifier.start1;
case 27: return p.justifier.start2;
case 28: return p.justifier.active;
case 29: return 0;
case 30: return 0;
case 31: return 0;
}
default:
break;
} //case Device::Justifier(s)
} //switch(p.device.i)
//no device connected
return 0;
}
//scan all input; update cursor positions if needed
void Input::update() {
system.interface->input_poll();
port_t &p = port[1];
switch(p.device.i) {
case Device::SuperScope: {
int x = system.interface->input_poll(1, p.device.i, 0, (unsigned)SuperScopeID::X);
int y = system.interface->input_poll(1, p.device.i, 0, (unsigned)SuperScopeID::Y);
x += p.superscope.x;
y += p.superscope.y;
p.superscope.x = max(-16, min(256 + 16, x));
p.superscope.y = max(-16, min(240 + 16, y));
latchx = p.superscope.x;
latchy = p.superscope.y;
} break;
case Device::Justifier:
case Device::Justifiers: {
int x1 = system.interface->input_poll(1, p.device.i, 0, (unsigned)JustifierID::X);
int y1 = system.interface->input_poll(1, p.device.i, 0, (unsigned)JustifierID::Y);
x1 += p.justifier.x1;
y1 += p.justifier.y1;
p.justifier.x1 = max(-16, min(256 + 16, x1));
p.justifier.y1 = max(-16, min(240 + 16, y1));
int x2 = system.interface->input_poll(1, p.device.i, 1, (unsigned)JustifierID::X);
int y2 = system.interface->input_poll(1, p.device.i, 1, (unsigned)JustifierID::Y);
x2 += p.justifier.x2;
y2 += p.justifier.y2;
p.justifier.x2 = max(-16, min(256 + 16, x2));
p.justifier.y2 = max(-16, min(240 + 16, y2));
if(p.justifier.active == 0) {
latchx = p.justifier.x1;
latchy = p.justifier.y1;
} else {
latchx = (p.device.i == Device::Justifiers ? p.justifier.x2 : -1);
latchy = (p.device.i == Device::Justifiers ? p.justifier.y2 : -1);
}
} break;
default:
break;
switch(id) { default:
case Device::None: controller = new Controller(port); break;
case Device::Joypad: controller = new Gamepad(port); break;
case Device::Multitap: controller = new Multitap(port); break;
case Device::Mouse: controller = new Mouse(port); break;
case Device::SuperScope: controller = new SuperScope(port); break;
case Device::Justifier: controller = new Justifier(port, false); break;
case Device::Justifiers: controller = new Justifier(port, true); break;
case Device::Serial: controller = new Serial(port); break;
}
if(latchy < 0 || latchy >= (ppu.overscan() ? 240 : 225) || latchx < 0 || latchx >= 256) {
//cursor is offscreen, set to invalid position so counters are not latched
latchx = ~0;
latchy = ~0;
} else {
//cursor is onscreen
latchx += 40; //offset trigger position to simulate hardware latching delay
latchx <<= 2; //dot -> clock conversion
latchx += 2; //align trigger on half-dot ala interrupts (speed optimization for sCPU::add_clocks)
switch(port ? 1 : 0) {
case Controller::Port1: config.controller_port1.i = id; break;
case Controller::Port2: config.controller_port2.i = id; break;
}
}
void Input::port_set_device(bool portnumber, Device::e device) {
port_t &p = port[portnumber];
p.device.i = device;
p.counter0 = 0;
p.counter1 = 0;
//set iobit to true if device is capable of latching PPU counters
iobit = port[1].device.i == Device::SuperScope
|| port[1].device.i == Device::Justifier
|| port[1].device.i == Device::Justifiers;
latchx = -1;
latchy = -1;
if(device == Device::SuperScope) {
p.superscope.x = 256 / 2;
p.superscope.y = 240 / 2;
p.superscope.trigger = false;
p.superscope.cursor = false;
p.superscope.turbo = false;
p.superscope.pause = false;
p.superscope.offscreen = false;
p.superscope.turbolock = false;
p.superscope.triggerlock = false;
p.superscope.pauselock = false;
} else if(device == Device::Justifier) {
p.justifier.active = 0;
p.justifier.x1 = 256 / 2;
p.justifier.y1 = 240 / 2;
p.justifier.x2 = -1;
p.justifier.y2 = -1;
p.justifier.trigger1 = false;
p.justifier.trigger2 = false;
p.justifier.start1 = false;
p.justifier.start2 = false;
} else if(device == Device::Justifiers) {
p.justifier.active = 0;
p.justifier.x1 = 256 / 2 - 16;
p.justifier.y1 = 240 / 2;
p.justifier.x2 = 256 / 2 + 16;
p.justifier.y2 = 240 / 2;
p.justifier.trigger1 = false;
p.justifier.trigger2 = false;
p.justifier.start1 = false;
p.justifier.start2 = false;
}
Input::Input() {
connect(Controller::Port1, Input::Device::Joypad);
connect(Controller::Port2, Input::Device::Joypad);
}
void Input::poll() {
port[0].counter0 = 0;
port[0].counter1 = 0;
port[1].counter0 = 0;
port[1].counter1 = 0;
port[1].justifier.active = !port[1].justifier.active;
}
void Input::init() {
Input::~Input() {
if(port1) delete port1;
if(port2) delete port2;
}
#endif

View File

@ -1,98 +1,49 @@
class Input {
public:
struct Input {
struct Device {
enum e {
None,
Joypad,
Multitap,
Mouse,
SuperScope,
Justifier,
Justifiers,
} i;
enum e {
None,
Joypad,
Multitap,
Mouse,
SuperScope,
Justifier,
Justifiers,
Serial,
} i;
};
struct JoypadID {
enum e {
B = 0, Y = 1, Select = 2, Start = 3,
Up = 4, Down = 5, Left = 6, Right = 7,
A = 8, X = 9, L = 10, R = 11,
} i;
enum e {
B = 0, Y = 1, Select = 2, Start = 3,
Up = 4, Down = 5, Left = 6, Right = 7,
A = 8, X = 9, L = 10, R = 11,
} i;
};
struct MouseID {
enum e {
X = 0, Y = 1, Left = 2, Right = 3,
} i;
enum e {
X = 0, Y = 1, Left = 2, Right = 3,
} i;
};
struct SuperScopeID {
enum e {
X = 0, Y = 1, Trigger = 2, Cursor = 3, Turbo = 4, Pause = 5,
} i;
enum e {
X = 0, Y = 1, Trigger = 2, Cursor = 3, Turbo = 4, Pause = 5,
} i;
};
struct JustifierID {
enum e {
X = 0, Y = 1, Trigger = 2, Start = 3,
} i;
enum e {
X = 0, Y = 1, Trigger = 2, Start = 3,
} i;
};
uint8 port_read(bool port);
void port_set_device(bool port, Device::e device);
void init();
void poll();
void update();
Controller *port1;
Controller *port2;
//light guns (Super Scope, Justifier(s)) strobe IOBit whenever the CRT
//beam cannon is detected. this needs to be tested at the cycle level
//(hence inlining here for speed) to avoid 'dead space' during DRAM refresh.
//iobit is updated during port_set_device(),
//latchx, latchy are updated during update() (once per frame)
alwaysinline void tick() {
//only test if Super Scope or Justifier is connected
if(iobit && cpu.vcounter() == latchy && cpu.hcounter() == latchx) {
ppu.latch_counters();
}
}
private:
bool iobit;
int16_t latchx, latchy;
struct port_t {
Device device;
unsigned counter0; //read counters
unsigned counter1;
struct superscope_t {
int x, y;
bool trigger;
bool cursor;
bool turbo;
bool pause;
bool offscreen;
bool turbolock;
bool triggerlock;
bool pauselock;
} superscope;
struct justifier_t {
bool active;
int x1, x2;
int y1, y2;
bool trigger1, trigger2;
bool start1, start2;
} justifier;
} port[2];
friend class System;
friend class Video;
friend class CPU;
void connect(bool port, Input::Device::e id);
Input();
~Input();
};
extern Input input;

View File

@ -1,9 +1,9 @@
class Interface {
public:
virtual void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan) {}
virtual void audio_sample(uint16_t l_sample, uint16_t r_sample) {}
virtual void input_poll() {}
virtual int16_t input_poll(bool port, Input::Device::e device, unsigned index, unsigned id) { return 0; }
virtual void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan) = 0;
virtual void audio_sample(uint16_t l_sample, uint16_t r_sample) = 0;
virtual void input_poll() = 0;
virtual int16_t input_poll(bool port, Input::Device::e device, unsigned index, unsigned id) = 0;
virtual void message(const string &text) { print(text, "\n"); }
virtual string path(Cartridge::Slot::e slot, const string &hint) = 0;

View File

@ -77,7 +77,7 @@ void snes_set_input_state(snes_input_state_t input_state) {
}
void snes_set_controller_port_device(bool port, unsigned device) {
SNES::input.port_set_device(port, (SNES::Input::Device::e)device);
SNES::input.connect(port, (SNES::Input::Device::e)device);
}
void snes_set_cartridge_basename(const char *basename) {
@ -86,8 +86,8 @@ void snes_set_cartridge_basename(const char *basename) {
void snes_init(void) {
SNES::system.init(&interface);
SNES::input.port_set_device(0, SNES::Input::Device::Joypad);
SNES::input.port_set_device(1, SNES::Input::Device::Joypad);
SNES::input.connect(0, SNES::Input::Device::Joypad);
SNES::input.connect(1, SNES::Input::Device::Joypad);
}
void snes_term(void) {

View File

@ -61,6 +61,7 @@ extern "C" {
#define SNES_DEVICE_SUPER_SCOPE 4
#define SNES_DEVICE_JUSTIFIER 5
#define SNES_DEVICE_JUSTIFIERS 6
#define SNES_DEVICE_SERIAL_CABLE 7
// These constants represent the button and axis inputs on various controllers,
// for use with the snes_input_state_t callback.

View File

@ -52,6 +52,7 @@ MappedRAM::MappedRAM() : data_(0), size_(0), write_protect_(false) {}
//Bus
uint8 Bus::read(unsigned addr) {
if(cheat.override[addr]) return cheat.read(addr);
return reader[lookup[addr]](target[addr]);
}

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "079.04";
static const char Version[] = "080";
static const unsigned SerializerVersion = 21;
}
}
@ -129,6 +129,7 @@ namespace SNES {
#include "profile-performance.hpp"
#endif
#include <snes/controller/controller.hpp>
#include <snes/system/system.hpp>
#include <snes/chip/chip.hpp>
#include <snes/cartridge/cartridge.hpp>

View File

@ -68,7 +68,6 @@ void System::serialize_all(serializer &s) {
if(cartridge.has_spc7110()) spc7110.serialize(s);
if(cartridge.has_obc1()) obc1.serialize(s);
if(cartridge.has_msu1()) msu1.serialize(s);
if(cartridge.has_serial()) serial.serialize(s);
}
//perform dry-run state save:

View File

@ -21,7 +21,6 @@ void System::run() {
scheduler.enter();
if(scheduler.exit_reason.i == Scheduler::ExitReason::FrameEvent) {
input.update();
video.update();
}
}
@ -59,7 +58,6 @@ void System::runthreadtosave() {
scheduler.enter();
if(scheduler.exit_reason.i == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason.i == Scheduler::ExitReason::FrameEvent) {
input.update();
video.update();
}
}
@ -84,15 +82,13 @@ void System::init(Interface *interface_) {
obc1.init();
st0018.init();
msu1.init();
serial.init();
link.init();
video.init();
audio.init();
input.init();
input.port_set_device(0, config.controller_port1.i);
input.port_set_device(1, config.controller_port2.i);
input.connect(0, config.controller_port1.i);
input.connect(1, config.controller_port2.i);
}
void System::term() {
@ -124,7 +120,6 @@ void System::load() {
if(cartridge.has_obc1()) obc1.load();
if(cartridge.has_st0018()) st0018.load();
if(cartridge.has_msu1()) msu1.load();
if(cartridge.has_serial()) serial.load();
if(cartridge.has_link()) link.load();
serialize_init();
@ -149,7 +144,6 @@ void System::unload() {
if(cartridge.has_obc1()) obc1.unload();
if(cartridge.has_st0018()) st0018.unload();
if(cartridge.has_msu1()) msu1.unload();
if(cartridge.has_serial()) serial.unload();
if(cartridge.has_link()) link.unload();
}
@ -186,7 +180,6 @@ void System::power() {
if(cartridge.has_obc1()) obc1.power();
if(cartridge.has_st0018()) st0018.power();
if(cartridge.has_msu1()) msu1.power();
if(cartridge.has_serial()) serial.power();
if(cartridge.has_link()) link.power();
if(cartridge.mode.i == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&icd2);
@ -195,11 +188,11 @@ void System::power() {
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp);
if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1);
if(cartridge.has_serial()) cpu.coprocessors.append(&serial);
if(cartridge.has_link()) cpu.coprocessors.append(&link);
scheduler.init();
input.update();
input.connect(0, config.controller_port1.i);
input.connect(1, config.controller_port2.i);
}
void System::reset() {
@ -225,7 +218,6 @@ void System::reset() {
if(cartridge.has_obc1()) obc1.reset();
if(cartridge.has_st0018()) st0018.reset();
if(cartridge.has_msu1()) msu1.reset();
if(cartridge.has_serial()) serial.reset();
if(cartridge.has_link()) link.reset();
if(cartridge.mode.i == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&icd2);
@ -234,13 +226,11 @@ void System::reset() {
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp);
if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1);
if(cartridge.has_serial()) cpu.coprocessors.append(&serial);
if(cartridge.has_link()) cpu.coprocessors.append(&link);
scheduler.init();
input.port_set_device(0, config.controller_port1.i);
input.port_set_device(1, config.controller_port2.i);
input.update();
input.connect(0, config.controller_port1.i);
input.connect(1, config.controller_port2.i);
}
void System::scanline() {

View File

@ -47,14 +47,24 @@ void Video::draw_cursor(uint16_t color, int x, int y) {
}
void Video::update() {
switch(input.port[1].device.i) {
case Input::Device::SuperScope: draw_cursor(0x001f, input.port[1].superscope.x, input.port[1].superscope.y); break;
case Input::Device::Justifiers: draw_cursor(0x02e0, input.port[1].justifier.x2, input.port[1].justifier.y2); //fallthrough
case Input::Device::Justifier: draw_cursor(0x001f, input.port[1].justifier.x1, input.port[1].justifier.y1); break;
case Input::Device::None:
case Input::Device::Joypad:
case Input::Device::Mouse:
case Input::Device::Multitap: break;
switch(config.controller_port2.i) {
case Input::Device::SuperScope:
if(dynamic_cast<SuperScope*>(input.port2)) {
SuperScope &device = (SuperScope&)*input.port2;
draw_cursor(0x7c00, device.x, device.y);
}
break;
case Input::Device::Justifier:
case Input::Device::Justifiers:
if(dynamic_cast<Justifier*>(input.port2)) {
Justifier &device = (Justifier&)*input.port2;
draw_cursor(0x001f, device.x1, device.y1);
if(device.chained == false) break;
draw_cursor(0x02e0, device.x2, device.y2);
}
break;
default:
break;
}
uint16_t *data = (uint16_t*)ppu.output;