BlueRetro
BlueRetro copied to clipboard
snes multitap not working propperly
Super Bomberman games work perfect, but Top Gear 3000 detects just 2p correctly, 3p and 4p are intermittent, not tested it yet in other games
Multitap on SNES is a shitshow TBH, it's a mix of the standard HW base polling with the extra controller being polled via software.
The timing of the software side are extremely brutal and since it's software base not a single game do it the same way which prevent me from coming up with any way to cheat it.
It is unlikely to be possible to fix it.
Two possible solution:
- Develop a solution for the ESP32 to use a CPLD or FPGA to handle the low level protocol.
- Hack the games roms to make the software polling more forgiving.
- Hack the games roms to make the software polling more forgiving.
Is it possible to handle this inside the ESP32 anyway? I've been reading your code and found this:
...
static unsigned npiso_sfc_snes_5p_isr(unsigned cause) {
const uint32_t low_io = GPIO.acpu_int;
const uint32_t high_io = GPIO.acpu_int1.intr;
/* reset bit counter, set first bit */
if (high_io & NPISO_LATCH_MASK) {
if (GPIO.in1.val & NPISO_LATCH_MASK) {
if (GPIO.in & P2_SEL_MASK) {
//set_data(1, 0, 0);
set_data(1, 1, 0);
}
}
else {
if (GPIO.in & P2_SEL_MASK) {
set_data(1, 0, wired_adapter.data[1].output[0] & 0x80);
set_data(1, 1, wired_adapter.data[2].output[0] & 0x80);
}
else {
set_data(1, 0, wired_adapter.data[3].output[0] & 0x80);
set_data(1, 1, wired_adapter.data[4].output[0] & 0x80);
}
cnts.val = 0x01010101;
idx0 = 0;
idx1 = 0;
mask[0] = 0x40;
mask[1] = 0x40;
}
if (!(GPIO.in1.val & NPISO_LATCH_MASK)) {
/* Help for games with very short latch that don't trigger falling edge intr */
if (GPIO.in & P2_SEL_MASK) {
set_data(1, 0, wired_adapter.data[1].output[0] & 0x80);
set_data(1, 1, wired_adapter.data[2].output[0] & 0x80);
}
else {
set_data(1, 0, wired_adapter.data[3].output[0] & 0x80);
set_data(1, 1, wired_adapter.data[4].output[0] & 0x80);
}
cnts.val = 0x01010101;
idx0 = 0;
idx1 = 0;
mask[0] = 0x40;
mask[1] = 0x40;
}
set_data(0, 0, wired_adapter.data[0].output[0] & 0x80);
}
if (low_io & P2_SEL_MASK) {
if (GPIO.in & P2_SEL_MASK) {
//set_data(1, 0, wired_adapter.data[1].output[0] & 0x80);
//set_data(1, 1, wired_adapter.data[2].output[0] & 0x80);
cnt0 = &cnts.cnt[0];
cnt1 = &cnts.cnt[1];
}
else {
set_data(1, 0, wired_adapter.data[3].output[0] & 0x80);
set_data(1, 1, wired_adapter.data[4].output[0] & 0x80);
cnt0 = &cnts.cnt[2];
cnt1 = &cnts.cnt[3];
}
idx0 = *cnt0 >> 3;
idx1 = *cnt1 >> 3;
mask[0] = 0x40;
mask[1] = 0x40;
}
/* Update port 0 */
if (!(GPIO.in1.val & NPISO_LATCH_MASK)) {
if (low_io & P1_CLK_MASK) {
switch (idx0) {
case 0:
while (!(GPIO.in & P1_CLK_MASK)); /* Wait rising edge */
set_data(0, 0, wired_adapter.data[0].output[0] & mask[0]);
break;
case 1:
while (!(GPIO.in & P1_CLK_MASK)); /* Wait rising edge */
set_data(0, 0, wired_adapter.data[0].output[1] & mask[0]);
break;
default:
while (!(GPIO.in & P1_CLK_MASK)); /* Wait rising edge */
set_data(0, 0, 0);
break;
}
(*cnt0)++;
idx0 = *cnt0 >> 3;
mask[0] >>= 1;
if (!mask[0]) {
mask[0] = 0x80;
}
}
}
/* Update port 1 */
if (low_io & P2_CLK_MASK) {
if (GPIO.in1.val & NPISO_LATCH_MASK) {
/* P2-D0 load B when clocked while Latch is set. */
while (!(GPIO.in & P2_CLK_MASK)); /* Wait rising edge */
set_data(1, 0, wired_adapter.data[1].output[0] & 0x80);
}
else {
if (GPIO.in & P2_SEL_MASK) {
switch (idx1) {
case 0:
while (!(GPIO.in & P2_CLK_MASK)); /* Wait rising edge */
set_data(1, 0, wired_adapter.data[1].output[0] & mask[1]);
set_data(1, 1, wired_adapter.data[2].output[0] & mask[1]);
break;
case 1:
while (!(GPIO.in & P2_CLK_MASK)); /* Wait rising edge */
set_data(1, 0, wired_adapter.data[1].output[1] & mask[1]);
set_data(1, 1, wired_adapter.data[2].output[1] & mask[1]);
break;
default:
if (*cnt1 == 17) {
/* Hack to help for games reading too fast on SEL transition */
/* Set B following previous SEL controllers detection */
while (!(GPIO.in & P2_CLK_MASK)); /* Wait rising edge */
delay_us(2);
set_data(1, 0, wired_adapter.data[3].output[0] & 0x80);
set_data(1, 1, wired_adapter.data[4].output[0] & 0x80);
}
else {
while (!(GPIO.in & P2_CLK_MASK)); /* Wait rising edge */
set_data(1, 0, 0);
set_data(1, 1, 0);
}
break;
}
}
else {
switch (idx1) {
case 0:
while (!(GPIO.in & P2_CLK_MASK)); /* Wait rising edge */
set_data(1, 0, wired_adapter.data[3].output[0] & mask[1]);
set_data(1, 1, wired_adapter.data[4].output[0] & mask[1]);
break;
case 1:
while (!(GPIO.in & P2_CLK_MASK)); /* Wait rising edge */
set_data(1, 0, wired_adapter.data[3].output[1] & mask[1]);
set_data(1, 1, wired_adapter.data[4].output[1] & mask[1]);
break;
default:
//if (*cnt1 == 17) {
/* Hack to help for games reading too fast on SEL transition */
/* Set B following previous SEL controllers detection */
// set_data(1, 0, wired_adapter.data[1].output[0] & 0x80);
// set_data(1, 1, wired_adapter.data[2].output[0] & 0x80);
//}
//else {
while (!(GPIO.in & P2_CLK_MASK)); /* Wait rising edge */
set_data(1, 0, 0);
set_data(1, 1, 0);
//}
break;
}
}
(*cnt1)++;
idx1 = *cnt1 >> 3;
mask[1] >>= 1;
if (!mask[1]) {
mask[1] = 0x80;
}
}
}
/* EA games Latch sometimes glitch and we can't detect 2nd rising */
if (GPIO.in1.val & NPISO_LATCH_MASK) {
if (GPIO.in & P2_SEL_MASK) {
set_data(1, 1, 0);
}
}
if (high_io) GPIO.status1_w1tc.intr_st = high_io;
if (low_io) GPIO.status_w1tc = low_io;
return 0;
}
...
I found all of this VERY interesting, I'd like to know how can I help 😎 there's a delay_us(2);
hack and some unused hack code. In this particular game (Top Gear 3000) the available controllers (3 & 4) blink erraticaly, like connecting and disconnecting at random times on menu, didn't try it on game yet because was hard to even put my player name on it. Maybe something to do with P2-D0 and P2-D1 lines not rising on time?
I wouldn't waste time on this. You could make it work for a game but then break others.
Yes its all related to the rise and fall time. TTL chip state transition is expected by the games (in ~10 ns) you could probably get away with 100 ns. ESP32 is very far away from the IO some at best you can do 300 -500 ns transition.
I wouldn't waste time on this. You could make it work for a game but then break others.
Yes its all related to the rise and fall time. TTL chip state transition is expected by the games (in ~10 ns) you could probably get away with 100 ns. ESP32 is very far away from the IO some at best you can do 300 -500 ns transition.
sorry to hear that! Thanks for the information 👍
@darthcloud hey, I've made a pull request, Finally fixed the blinking controllers and the menu in top gear 3000.