Custom Controller Communicating with a NES

I’m in the middle of making my own NES controller, and I’m having difficulties syncing up with the clock/pulse pin.

I’ve tried 2 different methods of sending the inputs, one of them I wait for the latch pulse(explanation below) with a while loop, and the other one I tried using interrupts.

I’ve been following this info to program it. I’m not sure how accurate it is, but I know it was used by someone else to turn a NES controller into a USB with an arduino.

If you don’t want to read it I’ll explain briefly, the NES(console) sends a pulse on the Latch pin, the (original)controller then saves the state of all the buttons, and immediately sends the A button on the data pin. 6 microseconds later, 8 clock pulses will be sent from the nes to the clock pin, each cycle lasting 12us (i found it to be closer to 24us), and on every rise the next button will be sent, in the order A(already sent), B, Select, Start, Up, Down, Left, Right. The last clock pulse doesn’t actually seem to be sending anything, or the link is wrong.

Here is my code, they are both on the same script, but I’ll seperate them into 3 code blocks, shared, while, timed, and interrupt.

The while loop method is almost playable, and I can probably tweak the delays to make it more accurate, but I just don’t think relying on delays to sync up is the best way of doing it. I liked the idea of using interrupts to trigger when the latch/clock pulse is on a rise, but its not really working at all.
I’m pretty new to micro controllers, and just found out about interrupts last night, so can someone tell me if I am even doing it right?

Shared Code

#define dataPin   4
#define clockPin  3   //Also known as pulse pin
#define latchPin  2

#define greenPin  5   //A Button :  PIND5
#define redPin    6   //B Button :  PIND6

#define selectPin 7   //            PIND7
#define startPin  8   //            PINB0

#define upPin     9   //            PINB1
#define downPin   10  //            PINB2
#define leftPin   11  //            PINB3
#define rightPin  12  //            PINB4


//::Start::Only for while version
#define clockState digitalRead(clockPin)
#define latchState digitalRead(latchPin)
//::End::Only for while version


volatile int sendIndex = 0;   //The current input being sent

//::Start::Only for while version
int sendInputs[8];            //Value for each input
byte pinStates[2];            //Value for all pins | 0=0-7 / 1=8-13
//::End::Only for while version


void setup()
{
  Serial.begin(9600);

  pinMode(ledPin, OUTPUT);

  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, INPUT);
  pinMode(latchPin, INPUT);

  pinMode(upPin, INPUT);
  pinMode(rightPin, INPUT);
  pinMode(downPin, INPUT);
  pinMode(leftPin, INPUT);

  pinMode(startPin, INPUT);
  pinMode(selectPin, INPUT);

  pinMode(redPin, INPUT);
  pinMode(greenPin, INPUT);

  //Only in interrupt mode, comment when using While Loop A
  attachInterrupt(digitalPinToInterrupt(latchPin), InterruptMethod, RISING);
}

//the NES counts a LOW signal as a button press, so I have to invert each input.
int InvertState(int state) {
	return (state * -1) + 1;
}

While Loop A

void loop()
{
  WhileMethod();
}

void WhileMethod() {

	while (latchState == LOW) {
		//Do nothing
	}	

	//Save the all the buttons states, and send the A button immediately after.
	//After an 18 microsecond delay we will loop through the rest of the buttons
	//with a 24 second delay between each write.	

	LatchControls();	
	PORTD = sendInputs[0] << 4;
	delayMicroseconds(18);

	for (int i = 1; i < 8; i++) {
		PORTD = sendInputs[i] << 4;
		delayMicroseconds(24);
	}

	//https://tresi.github.io/nes/
	//*The Link that I left shows that each button is a 12us cycle, but from trial and error, 
	//as well checking with my $40 oscilliscope it seems to be a different story,
	//I get a much better result with these delays.
}

void LatchControls() {
 
  pinStates[0] = PIND; //Save PIND to pinStates[0] this may be unneccesary
  pinStates[1] = PINB; //Save PINB to pinStates[1] this may be unneccesary

  sendInputs[0] = InvertState((pinStates[0] & 32) >> 5);    //A
  sendInputs[1] = InvertState((pinStates[0] & 64) >> 6);    //B
  sendInputs[2] = InvertState((pinStates[0] & 128) >> 7);   //Select
  sendInputs[4] = InvertState(pinStates[1]  & 1);            /Start
  sendInputs[3] = InvertState((pinStates[1] & 2) >> 1);     //Up
  sendInputs[5] = InvertState((pinStates[1] & 4) >> 2);     //Down
  sendInputs[6] = InvertState((pinStates[1] & 8) >> 3);     //Left
  sendInputs[7] = InvertState((pinStates[1] & 16) >> 4);    //Right
}

I did try many different variations of the while loop(also using a while loop on the clock pin), but this one turned out the best by far, its almost playable.

I have super mario bros 2 in, and when I press…
right, he jumps, but also runs right, and start is sometimes randomly pressed. (semi-correct)
left, he runs to the left.(correct)
down, he crouches. (correct)
up, it pauses(start button), unless I switch games I won’t know if its pressing up at all. (wrong)

A(green), he jumps, but it does not hold like its supposed to.(semi-correct)
B(red), he runs (correct).

Select, right now its doing nothing, but last test it was pausing(it normally does nothing in this game) (possibly correct).

Start, nothing. (wrong).

Although I got the best results with this method, I’d really rather do something that triggers on the pulse, instead of just using a delay to hopefully land on the right button.
So that lead me to this method(as well as trying a bunch of variations that did nothing).

Interrupt

void InterruptMethod() {
  
  //As soon as we recieve a latch pulse grab and send the A button.
  GrabAndSend();

  //Now attach an interrupt to the clock pin, and continue for the rest of the buttons.
  attachInterrupt(digitalPinToInterrupt(clockPin), GrabAndSend, RISING);  
}

int offset = 0;

void GrabAndSend() {

  //Find out which Pin set we are using.
  //Getting and setting pins this way is much faster then digitalWrite/Read,
  //but we there is 2 registers we need to access, and we need to do bitwise operations.
  //Because of which pins the buttons are on, when we get to the third input we are sending, we are using a different register.
  switch (sendIndex > 2) {
  
  //A(5), B(6), Select(7) | PORTD is Pin 0 to 7
  case false: 
    //First calculate the offset, since our first input we want to send is pin 5, we just add 5 to the current index.
    //We get the Pin States and shift the pin we want to the right so its on bit 1, then perform an AND to get a 1 or a 0.
    //Then shift it 4 left which is the data pin.
    //The nes counts a LOW as a button pressed, so we need to invert the state.
    offset = 5 + sendIndex;
    PORTD = InvertState(  ((PIND >> offset) & 1)  << 4);
    break;

  //Start(8), Up(9), Down(10), Left(11), Right(12) | PORTB is Pin 8 to 13
  case true:
    //Again calculate the offset, this time we are starting on bit 0 and index 3, so we just subtract 3 from the index to get the correct bit.
    //And just like before, we do the same bitwise operations, this time on register B (PINB)
    offset = sendIndex - 3;
    PORTB = InvertState(  ((PINB >> offset) & 1)  << 4);
    break;
  }

  //Increase the send index so we actually send a different input
  sendIndex++;

  if (sendIndex == 7) {
    //We have sent the last input, so we will detach the interrupt from the clock,
    //set the send index back to 0, and wait for the next latch to repeat the cycle.
    detachInterrupt(digitalPinToInterrupt(clockPin));
    sendIndex = 0;
  }
}

On this version, the only button that seems to work is the B button, and its actually pressing start, which pauses the game. The other variations I tried that were similar had… similar results, most of them paused the game when I pressed right instead of B.

You would think this method would work better…

Thanks for any help.