#ifndef SNES_CONTROLLER_H #define SNES_CONTROLLER_H #include "../../io/GPIO.h" /** https://fpgalover.com/images/manuals/SNES/time.JPG */ // Pinout on the chinese version // left to right: gnd data latch clk +5v (also works with 3.3) template class SNESController { public: union State { uint16_t raw; State() : raw(0) {} State(uint16_t raw) : raw(raw) {} struct { uint8_t dummy1 : 1; uint8_t dummy2 : 1; uint8_t dummy3 : 1; uint8_t dummy4 : 1; uint8_t r : 1; uint8_t l : 1; uint8_t x : 1; uint8_t a : 1; uint8_t right : 1; uint8_t left : 1; uint8_t down : 1; uint8_t up : 1; uint8_t start : 1; uint8_t select : 1; uint8_t y : 1; uint8_t b : 1; }; bool operator != (const State o) const {return raw != o.raw;} /** write current state into the given 12+1 char* */ void toString(char* dst) { dst[ 0] = !r ? 'R' : ' '; dst[ 1] = !l ? 'L' : ' '; dst[ 2] = !x ? 'X' : ' '; dst[ 3] = !a ? 'A' : ' '; dst[ 4] = !right ? 'r' : ' '; dst[ 5] = !left ? 'l' : ' '; dst[ 6] = !down ? 'd' : ' '; dst[ 7] = !up ? 'u' : ' '; dst[ 8] = !start ? 'S' : ' '; dst[ 9] = !select ? 's' : ' '; dst[10] = !y ? 'Y' : ' '; dst[11] = !b ? 'B' : ' '; dst[12] = 0; } }; public: /** ctor */ SNESController() { MyGPIO::setInput(PIN_DATA); MyGPIO::setOutput(PIN_LATCH); MyGPIO::setOutput(PIN_CLK); } /** read the current state from the controller */ State read() { // latch the current data (remember state) latch(); // read all 16 data bits uint16_t res = 0; for (uint8_t i = 0; i < 16; ++i) { res <<= 1; res |= readBit(); } // wrap return State(res); } private: void latch() { // ensure CLK is HIGH MyGPIO::set(PIN_CLK); // send latch impulse MyGPIO::set(PIN_LATCH); wait(); MyGPIO::clear(PIN_LATCH); wait(); } uint8_t readBit() { MyGPIO::set(PIN_CLK); wait(); MyGPIO::clear(PIN_CLK); uint8_t res = MyGPIO::get(PIN_DATA); wait(); return res; } void wait() { for (uint8_t i = 0; i < 128; ++i) {asm("nop");} } }; #endif