Friday, June 27, 2014

NES Controllers

The NES has serial ports for the controllers. The advantage of this is that it is possible to have different controllers and other peripherals. The downside is that you need to read the input one bit at a time. The serial ports are located at memory addresses $4016 and $4017. The order that the bits are read is dependent on the controller that is plugged in, but as most games use the normal NES controller, the bit order is A, B, Select, Start, Up, Down, Left, Right.

The controllers need to be told to send their state to the serial port. This is done by a method known as strobing a port. Essentially this is simply writing a 1 to the port you wish to read followed by a 0 to the port you want to read. You then read the port 8 times for a standard controller, possibly more for other controllers. The low order bit (1) is used to tell you the state of the button being read while the next bit (2) indicates if a controller is attached. Other bits may also have significance, but I have not been able to find any information on this.

It is possible to have controller reading routines that incorporate the logic as the bits are tested, but I prefer having the bits combined into a single byte that can then be tested using boolean operations. This is the code for the first controller, the second controller works the same except you use $4017 instead of $4016.

ReadController1:
; strobe the controllers so will send button bits
LDA #$01
STA $4016
LDA #$00
STA $4016
; now loop through buttons to build joystick value
; a,b,select,start,up,down,left,right
LDY #$00
LDX #$08
ReadController1_pollJoy1:
LDA $4016 ; read bit
AND #$01 ; check if set (button down)
BEQ ReadController1_adjustJoyBits
INY ; as bit 0 is 0 will set bit!
ReadController1_adjustJoyBits:
TYA
DEX ; loop through button bits
; done here so won't over-shift flagset
BEQ ReadController1_done
ASL A ; shift button flag bits over
TAY
JMP ReadController1_pollJoy1
ReadController1_done:
RTS

One potential problem with the controller is that it is possible for the sound chip to cause interference with the serial bits resulting in the wrong value. This is a fairly easy to solve problem. Simply read the controller until the value returned is the same as the previous read.

A couple of convenience functions that I like to have, which are handy to have for things such as title screens and other times where you need to wait for button presses, are WaitForButtonPress and WaitForButtonPressEnd. These simply wait for a button press or the end of a button press.

WaitForButtonPress:
JSR ReadController1
AND #$F0
BEQ WaitForButtonPress
RTS

WaitForButtonPressEnd:
JSR ReadController1
AND #$F0
BNE WaitForButtonPressEnd
RTS

I also wrote a function that displays what controller buttons are pressed but as this is fairly simple code, I am not including it here. If you are interested in the code, it is still part of the Trivia source code (NES Trivia included in the game zip file.) even though it is not actually used.

The next couple of weeks will take a look at my first RPG Maker VXAce game. First a postmortem of the game and then a look at my thoughts on RPG Maker and what I am hoping the next version of this tool would have to make it perfect for my needs (not that my wishes are likely to happen but you never know).

No comments: