mirror of
https://github.com/libretro/bsnes-libretro-cplusplus98.git
synced 2025-04-12 07:07:02 +00:00
v80
This commit is contained in:
parent
301c39c601
commit
3bb6889efd
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
*.so
|
||||
*.o
|
||||
*.a
|
||||
*.dll
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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/*
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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&);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
//===============
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
@ -1,9 +0,0 @@
|
||||
#ifdef SERIAL_CPP
|
||||
|
||||
void Serial::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
s.integer((bool&)data1);
|
||||
s.integer((bool&)data2);
|
||||
}
|
||||
|
||||
#endif
|
@ -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) {
|
||||
|
55
snes/controller/controller.cpp
Normal file
55
snes/controller/controller.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
35
snes/controller/controller.hpp
Normal file
35
snes/controller/controller.hpp
Normal 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"
|
21
snes/controller/gamepad/gamepad.cpp
Normal file
21
snes/controller/gamepad/gamepad.cpp
Normal 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
|
9
snes/controller/gamepad/gamepad.hpp
Normal file
9
snes/controller/gamepad/gamepad.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
struct Gamepad : Controller {
|
||||
uint2 data();
|
||||
void latch(bool data);
|
||||
Gamepad(bool port);
|
||||
|
||||
private:
|
||||
bool latched;
|
||||
unsigned counter;
|
||||
};
|
133
snes/controller/justifier/justifier.cpp
Normal file
133
snes/controller/justifier/justifier.cpp
Normal 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
|
17
snes/controller/justifier/justifier.hpp
Normal file
17
snes/controller/justifier/justifier.hpp
Normal 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;
|
||||
};
|
69
snes/controller/mouse/mouse.cpp
Normal file
69
snes/controller/mouse/mouse.cpp
Normal 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
|
9
snes/controller/mouse/mouse.hpp
Normal file
9
snes/controller/mouse/mouse.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
struct Mouse : Controller {
|
||||
uint2 data();
|
||||
void latch(bool data);
|
||||
Mouse(bool port);
|
||||
|
||||
private:
|
||||
bool latched;
|
||||
unsigned counter;
|
||||
};
|
39
snes/controller/multitap/multitap.cpp
Normal file
39
snes/controller/multitap/multitap.cpp
Normal 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
|
10
snes/controller/multitap/multitap.hpp
Normal file
10
snes/controller/multitap/multitap.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
struct Multitap : Controller {
|
||||
uint2 data();
|
||||
void latch(bool data);
|
||||
Multitap(bool port);
|
||||
|
||||
private:
|
||||
bool latched;
|
||||
unsigned counter1;
|
||||
unsigned counter2;
|
||||
};
|
148
snes/controller/serial/serial.cpp
Normal file
148
snes/controller/serial/serial.cpp
Normal 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
|
19
snes/controller/serial/serial.hpp
Normal file
19
snes/controller/serial/serial.hpp
Normal 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;
|
||||
};
|
129
snes/controller/superscope/superscope.cpp
Normal file
129
snes/controller/superscope/superscope.cpp
Normal 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
|
22
snes/controller/superscope/superscope.hpp
Normal file
22
snes/controller/superscope/superscope.hpp
Normal 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;
|
||||
};
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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:
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user