diff --git a/ares/fc/controller/controller.cpp b/ares/fc/controller/controller.cpp index 0cb3864d25..73d2fbe98e 100644 --- a/ares/fc/controller/controller.cpp +++ b/ares/fc/controller/controller.cpp @@ -4,5 +4,6 @@ namespace ares::Famicom { #include "port.cpp" #include "gamepad/gamepad.cpp" +#include "sfcgamepad/sfcgamepad.cpp" } diff --git a/ares/fc/controller/controller.hpp b/ares/fc/controller/controller.hpp index aee0198ab4..bc84975d4a 100644 --- a/ares/fc/controller/controller.hpp +++ b/ares/fc/controller/controller.hpp @@ -31,3 +31,4 @@ struct Controller { #include "port.hpp" #include "gamepad/gamepad.hpp" +#include "sfcgamepad/sfcgamepad.hpp" diff --git a/ares/fc/controller/port.cpp b/ares/fc/controller/port.cpp index 2797be5c3c..9b5103755c 100644 --- a/ares/fc/controller/port.cpp +++ b/ares/fc/controller/port.cpp @@ -11,7 +11,7 @@ auto ControllerPort::load(Node::Object parent) -> void { port->setHotSwappable(true); port->setAllocate([&](auto name) { return allocate(name); }); port->setDisconnect([&] { device.reset(); }); - port->setSupported({"Gamepad"}); + port->setSupported({"Gamepad","Super Famicom Gamepad"}); } auto ControllerPort::unload() -> void { @@ -21,6 +21,7 @@ auto ControllerPort::unload() -> void { auto ControllerPort::allocate(string name) -> Node::Peripheral { if(name == "Gamepad") device = new Gamepad(port); + if(name == "Super Famicom Gamepad") device = new SFC_Gamepad(port); if(device) return device->node; return {}; } diff --git a/ares/fc/controller/sfcgamepad/sfcgamepad.cpp b/ares/fc/controller/sfcgamepad/sfcgamepad.cpp new file mode 100644 index 0000000000..e4bce10b1e --- /dev/null +++ b/ares/fc/controller/sfcgamepad/sfcgamepad.cpp @@ -0,0 +1,94 @@ +SFC_Gamepad::SFC_Gamepad(Node::Port parent) { + node = parent->append("Super Famicom Gamepad"); + + up = node->append("Up"); + down = node->append("Down"); + left = node->append("Left"); + right = node->append("Right"); + b = node->append("B"); + a = node->append("A"); + y = node->append("Y"); + x = node->append("X"); + l = node->append("L"); + r = node->append("R"); + select = node->append("Select"); + start = node->append("Start"); +} + +auto SFC_Gamepad::data() -> n3 { + if(latched == 1) { + platform->input(b); + return b->value(); + } + + //note: D-pad physically prevents up+down and left+right from being pressed at the same time + switch(counter++) { + case 0: return b->value(); + case 1: return y->value(); + case 2: return select->value(); + case 3: return start->value(); + case 4: return upLatch; + case 5: return downLatch; + case 6: return leftLatch; + case 7: return rightLatch; + case 8: return a->value(); + case 9: return x->value(); + case 10: return l->value(); + case 11: return r->value(); + + case 12: return 0; //4-bit device signature + case 13: return 0; + case 14: return 0; + case 15: return 0; + } + + counter = 16; + return 1; +} + +auto SFC_Gamepad::latch(n1 data) -> void { + if(latched == data) return; + latched = data; + counter = 0; + + if(latched == 0) { + platform->input(b); + platform->input(y); + platform->input(select); + platform->input(start); + platform->input(up); + platform->input(down); + platform->input(left); + platform->input(right); + platform->input(a); + platform->input(x); + platform->input(l); + platform->input(r); + + if(!(up->value() & down->value())) { + yHold = 0, upLatch = up->value(), downLatch = down->value(); + } else if(!yHold) { + yHold = 1, swap(upLatch, downLatch); + } + + if(!(left->value() & right->value())) { + xHold = 0, leftLatch = left->value(), rightLatch = right->value(); + } else if(!xHold) { + xHold = 1, swap(leftLatch, rightLatch); + } + } +} + + +auto SFC_Gamepad::serialize(serializer& s) -> void { + s(latched); + s(counter); + + s(yHold); + s(upLatch); + s(downLatch); + s(xHold); + s(leftLatch); + s(rightLatch); +} + diff --git a/ares/fc/controller/sfcgamepad/sfcgamepad.hpp b/ares/fc/controller/sfcgamepad/sfcgamepad.hpp new file mode 100644 index 0000000000..287ca9ff6c --- /dev/null +++ b/ares/fc/controller/sfcgamepad/sfcgamepad.hpp @@ -0,0 +1,31 @@ +struct SFC_Gamepad : Controller { + Node::Input::Button up; + Node::Input::Button down; + Node::Input::Button left; + Node::Input::Button right; + Node::Input::Button b; + Node::Input::Button a; + Node::Input::Button y; + Node::Input::Button x; + Node::Input::Button l; + Node::Input::Button r; + Node::Input::Button select; + Node::Input::Button start; + + SFC_Gamepad(Node::Port); + + auto data() -> n3 override; + auto latch(n1 data) -> void override; + auto serialize(serializer&) -> void override; + +private: + n1 latched; + n8 counter; + + n1 yHold; + n1 upLatch; + n1 downLatch; + n1 xHold; + n1 leftLatch; + n1 rightLatch; +}; diff --git a/desktop-ui/emulator/famicom.cpp b/desktop-ui/emulator/famicom.cpp index 24247ad78f..c95d141d73 100644 --- a/desktop-ui/emulator/famicom.cpp +++ b/desktop-ui/emulator/famicom.cpp @@ -23,6 +23,21 @@ Famicom::Famicom() { device.digital("Start", virtualPorts[id].pad.start); device.digital("Microphone", virtualPorts[id].pad.north); port.append(device); } + + { InputDevice device{"Super Famicom Gamepad"}; + device.digital("Up", virtualPorts[id].pad.up); + device.digital("Down", virtualPorts[id].pad.down); + device.digital("Left", virtualPorts[id].pad.left); + device.digital("Right", virtualPorts[id].pad.right); + device.digital("B", virtualPorts[id].pad.south); + device.digital("A", virtualPorts[id].pad.east); + device.digital("Y", virtualPorts[id].pad.west); + device.digital("X", virtualPorts[id].pad.north); + device.digital("L", virtualPorts[id].pad.l_bumper); + device.digital("R", virtualPorts[id].pad.r_bumper); + device.digital("Select", virtualPorts[id].pad.select); + device.digital("Start", virtualPorts[id].pad.start); + port.append(device); } ports.append(port); }