123 lines
2.1 KiB
C++
123 lines
2.1 KiB
C++
#ifndef SNES_CONTROLLER_H
|
|
#define SNES_CONTROLLER_H
|
|
|
|
#include "../../io/GPIO.h"
|
|
|
|
|
|
/** https://fpgalover.com/images/manuals/SNES/time.JPG */
|
|
template <int PIN_LATCH, int PIN_DATA, int PIN_CLK> 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
|