Use the SNES/NES controller in Assembly: Difference between revisions
m (Added description) |
(Finally past the initial draft) |
||
Line 1: | Line 1: | ||
== | == NES and SNES controllers == | ||
The F256 machines can use these controllers provided they are equipped with an optional [https://c256foenix.com/product/fnx4n4s/?v=5435c69ed3bc FNX4N4S breakout adapter]. | |||
The official gamepads of yesteryear work without issue with this and are plentiful in the used market. | |||
Third party modern recreations are sometimes flaky or not working at all. Share you discoveries in the discord! | |||
Super famicom gamepads work very well as substitute for regular SNES gamepads. | |||
=== Registers === | |||
{| class="wikitable" | |||
|+ | |||
!Register Name | |||
!R/W | |||
!Address | |||
!NES/SNES | |||
!Description | |||
|- | |||
|PAD_CTRL | |||
|W | |||
|0xD880 | |||
|both | |||
|Used to trigger a reading and setting the NES/SNES mode | |||
|- | |||
|PAD_STAT | |||
|R | |||
|0xD880 | |||
|both | |||
|Used to verify if the triggered polling of gamepad state is done yet | |||
|- | |||
|PAD0 | |||
|R | |||
|0xD884 | |||
|both | |||
|Buttons statuses: if cleared, they are pressed. If set, they are unpressed. | |||
|- | |||
|PAD0_S | |||
|R | |||
|0xD885 | |||
|SNES | |||
|Same deal; unused by NES. Consult button table below | |||
|- | |||
|PAD1 | |||
|R | |||
|0xD886 | |||
|both | |||
|Buttons statuses: if cleared, they are pressed. If set, they are unpressed. | |||
|- | |||
|PAD1_S | |||
|R | |||
|0xD887 | |||
|SNES | |||
|Same deal; unused by NES. Consult button table below | |||
|- | |||
|PAD2 | |||
|R | |||
|0xD888 | |||
|both | |||
|Buttons statuses: if cleared, they are pressed. If set, they are unpressed. | |||
|- | |||
|PAD2_S | |||
|R | |||
|0xD889 | |||
|SNES | |||
|Same deal; unused by NES. Consult button table below | |||
|- | |||
|PAD3 | |||
|R | |||
|0xD88A | |||
|both | |||
|Buttons statuses: if cleared, they are pressed. If set, they are unpressed. | |||
|- | |||
|PAD3_S | |||
|R | |||
|0xD88B | |||
|SNES | |||
|Same deal; unused by NES. Consult button table below | |||
|} | |||
NES_TRIG | === Control register === | ||
{| class="wikitable" | |||
|+ | |||
!Register Name | |||
!R/W | |||
!Address | |||
!Bit7 | |||
!Bit6 | |||
!Bit5 | |||
!Bit4 | |||
!Bit3 | |||
!Bit2 | |||
!Bit1 | |||
!Bit0 | |||
|- | |||
|PAD_CTRL | |||
|W | |||
|0xD880 | |||
|NES_TRIG | |||
| - | |||
| - | |||
| - | |||
| - | |||
|MODE | |||
| - | |||
|EN | |||
|- | |||
|PAD_STAT | |||
|R | |||
|0xD880 | |||
|NES_TRIG | |||
|DONE | |||
| - | |||
| - | |||
| - | |||
|MODE | |||
| - | |||
|EN | |||
|} | |||
==== Control register bit states: ==== | |||
{| class="wikitable" | |||
|+ | |||
!Bit Name | |||
!If set to 1 | |||
!If cleared to 0 | |||
|- | |||
|NES_TRIG | |||
|Launches the polling of the states of all buttons | |||
|Stops polling the states of all buttons | |||
|- | |||
|DONE | |||
|The states of the buttons are ready to be read | |||
|The states of the buttons are not ready | |||
|- | |||
|MODE | |||
|SNES data will be used to populate the PAD registers | |||
|NES data will be used to populate the PAD registers | |||
|- | |||
|EN | |||
|Enables the gamepads | |||
|Disables the gamepads | |||
|} | |||
=== Button table === | |||
{| class="wikitable" | |||
|+ | |||
!MODE | |||
!NES/SNES | |||
!Address | |||
!Pad | |||
!Bit7 | |||
!Bit6 | |||
!Bit5 | |||
!Bit4 | |||
!Bit3 | |||
!Bit2 | |||
!Bit1 | |||
!Bit0 | |||
|- | |||
|0 | |||
|NES | |||
|0xD884 | |||
|0 | |||
|A | |||
|B | |||
|Select | |||
|Start | |||
|Up | |||
|Down | |||
|Left | |||
|Right | |||
|- | |||
|0 | |||
|NES | |||
|0xD886 | |||
|1 | |||
|A | |||
|B | |||
|Select | |||
|Start | |||
|Up | |||
|Down | |||
|Left | |||
|Right | |||
|- | |||
|0 | |||
|NES | |||
|0xD888 | |||
|2 | |||
|A | |||
|B | |||
|Select | |||
|Start | |||
|Up | |||
|Down | |||
|Left | |||
|Right | |||
|- | |||
|0 | |||
|NES | |||
|0xD88A | |||
|3 | |||
|A | |||
|B | |||
|Select | |||
|Start | |||
|Up | |||
|Down | |||
|Left | |||
|Right | |||
|- | |||
|1 | |||
|SNES | |||
|0xD884 | |||
|0 | |||
|B | |||
|Y | |||
|Select | |||
|Start | |||
|Up | |||
|Down | |||
|Left | |||
|Right | |||
|- | |||
|1 | |||
|SNES | |||
|0xD885 | |||
|0 | |||
| | |||
| | |||
| | |||
| | |||
|A | |||
|X | |||
|L | |||
|R | |||
|- | |||
|1 | |||
|SNES | |||
|0xD886 | |||
|1 | |||
|B | |||
|Y | |||
|Select | |||
|Start | |||
|Up | |||
|Down | |||
|Left | |||
|Right | |||
|- | |||
|1 | |||
|SNES | |||
|0xD887 | |||
|1 | |||
| | |||
| | |||
| | |||
| | |||
|A | |||
|X | |||
|L | |||
|R | |||
|- | |||
|1 | |||
|SNES | |||
|0xD888 | |||
|2 | |||
|B | |||
|Y | |||
|Select | |||
|Start | |||
|Up | |||
|Down | |||
|Left | |||
|Right | |||
|- | |||
|1 | |||
|SNES | |||
|0xD889 | |||
|2 | |||
| | |||
| | |||
| | |||
| | |||
|A | |||
|X | |||
|L | |||
|R | |||
|- | |||
|1 | |||
|SNES | |||
|0xD88A | |||
|3 | |||
|B | |||
|Y | |||
|Select | |||
|Start | |||
|Up | |||
|Down | |||
|Left | |||
|Right | |||
|- | |||
|1 | |||
|SNES | |||
|0xD88B | |||
|3 | |||
| | |||
| | |||
| | |||
| | |||
|A | |||
|X | |||
|L | |||
|R | |||
|} | |||
Note: L refers to the left shoulder button and R refers to the right shoulder button. | |||
=== Usage === | |||
Example 1: polling a NES controller in PAD0 position and checking if A is pressed:<pre> | |||
POKE(PAD_CTRL, 0b10000001); //trigger, nes mode, enabled | |||
while((PEEK(PAD_STAT) & 0x40) == 0) //loop while this bit is cleared | |||
; | |||
if((PEEK(PAD0) & 0x80) == 0) reactToAPressed(); | |||
POKE(PAD_CTRL, 0b00000001); //clear trigger | |||
</pre>Example 2: polling a SNES controller in PAD2 position and checking if X is pressed:<pre> | |||
POKE(PAD_CTRL, 0b10000101); //trigger, snes mode, enabled | |||
while((PEEK(PAD_STAT) & 0x40) == 0) //loop while this bit is cleared | |||
; | |||
if((PEEK(PAD2_S) & 0x04) == 0) reactToXPressed(); | |||
POKE(PAD_CTRL, 0b00000101); //clear trigger | |||
</pre>Example 3: polling both types of controller in rapid succession (allows the usage of all 8 controllers!)<pre> | |||
//NES section | |||
POKE(PAD_CTRL, 0b10000001); //trigger, nes mode, enabled | |||
while((PEEK(PAD_STAT) & 0x40) == 0) //loop while this bit is cleared | |||
; | |||
//section where reactions are made to the various NES presses | |||
POKE(PAD_CTRL, 0b00000001); //clear trigger | |||
POKE(PAD_CTRL, 0b10000101); //trigger, snes mode, enabled | |||
while((PEEK(PAD_STAT) & 0x40) == 0) //loop while this bit is cleared | |||
; | |||
//section where reactions are made to the various SNES presses | |||
POKE(PAD_CTRL, 0b00000101); //clear trigger | |||
</pre> | |||
Latest revision as of 06:20, 12 March 2025
NES and SNES controllers
The F256 machines can use these controllers provided they are equipped with an optional FNX4N4S breakout adapter.
The official gamepads of yesteryear work without issue with this and are plentiful in the used market.
Third party modern recreations are sometimes flaky or not working at all. Share you discoveries in the discord!
Super famicom gamepads work very well as substitute for regular SNES gamepads.
Registers
Register Name | R/W | Address | NES/SNES | Description |
---|---|---|---|---|
PAD_CTRL | W | 0xD880 | both | Used to trigger a reading and setting the NES/SNES mode |
PAD_STAT | R | 0xD880 | both | Used to verify if the triggered polling of gamepad state is done yet |
PAD0 | R | 0xD884 | both | Buttons statuses: if cleared, they are pressed. If set, they are unpressed. |
PAD0_S | R | 0xD885 | SNES | Same deal; unused by NES. Consult button table below |
PAD1 | R | 0xD886 | both | Buttons statuses: if cleared, they are pressed. If set, they are unpressed. |
PAD1_S | R | 0xD887 | SNES | Same deal; unused by NES. Consult button table below |
PAD2 | R | 0xD888 | both | Buttons statuses: if cleared, they are pressed. If set, they are unpressed. |
PAD2_S | R | 0xD889 | SNES | Same deal; unused by NES. Consult button table below |
PAD3 | R | 0xD88A | both | Buttons statuses: if cleared, they are pressed. If set, they are unpressed. |
PAD3_S | R | 0xD88B | SNES | Same deal; unused by NES. Consult button table below |
Control register
Register Name | R/W | Address | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
---|---|---|---|---|---|---|---|---|---|---|
PAD_CTRL | W | 0xD880 | NES_TRIG | - | - | - | - | MODE | - | EN |
PAD_STAT | R | 0xD880 | NES_TRIG | DONE | - | - | - | MODE | - | EN |
Control register bit states:
Bit Name | If set to 1 | If cleared to 0 |
---|---|---|
NES_TRIG | Launches the polling of the states of all buttons | Stops polling the states of all buttons |
DONE | The states of the buttons are ready to be read | The states of the buttons are not ready |
MODE | SNES data will be used to populate the PAD registers | NES data will be used to populate the PAD registers |
EN | Enables the gamepads | Disables the gamepads |
Button table
MODE | NES/SNES | Address | Pad | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | NES | 0xD884 | 0 | A | B | Select | Start | Up | Down | Left | Right |
0 | NES | 0xD886 | 1 | A | B | Select | Start | Up | Down | Left | Right |
0 | NES | 0xD888 | 2 | A | B | Select | Start | Up | Down | Left | Right |
0 | NES | 0xD88A | 3 | A | B | Select | Start | Up | Down | Left | Right |
1 | SNES | 0xD884 | 0 | B | Y | Select | Start | Up | Down | Left | Right |
1 | SNES | 0xD885 | 0 | A | X | L | R | ||||
1 | SNES | 0xD886 | 1 | B | Y | Select | Start | Up | Down | Left | Right |
1 | SNES | 0xD887 | 1 | A | X | L | R | ||||
1 | SNES | 0xD888 | 2 | B | Y | Select | Start | Up | Down | Left | Right |
1 | SNES | 0xD889 | 2 | A | X | L | R | ||||
1 | SNES | 0xD88A | 3 | B | Y | Select | Start | Up | Down | Left | Right |
1 | SNES | 0xD88B | 3 | A | X | L | R |
Note: L refers to the left shoulder button and R refers to the right shoulder button.
Usage
Example 1: polling a NES controller in PAD0 position and checking if A is pressed:
POKE(PAD_CTRL, 0b10000001); //trigger, nes mode, enabled while((PEEK(PAD_STAT) & 0x40) == 0) //loop while this bit is cleared ; if((PEEK(PAD0) & 0x80) == 0) reactToAPressed(); POKE(PAD_CTRL, 0b00000001); //clear trigger
Example 2: polling a SNES controller in PAD2 position and checking if X is pressed:
POKE(PAD_CTRL, 0b10000101); //trigger, snes mode, enabled while((PEEK(PAD_STAT) & 0x40) == 0) //loop while this bit is cleared
;if((PEEK(PAD2_S) & 0x04) == 0) reactToXPressed(); POKE(PAD_CTRL, 0b00000101); //clear trigger
Example 3: polling both types of controller in rapid succession (allows the usage of all 8 controllers!)
//NES section POKE(PAD_CTRL, 0b10000001); //trigger, nes mode, enabled while((PEEK(PAD_STAT) & 0x40) == 0) //loop while this bit is cleared
;//section where reactions are made to the various NES presses POKE(PAD_CTRL, 0b00000001); //clear trigger
POKE(PAD_CTRL, 0b10000101); //trigger, snes mode, enabled while((PEEK(PAD_STAT) & 0x40) == 0) //loop while this bit is cleared
;//section where reactions are made to the various SNES presses POKE(PAD_CTRL, 0b00000101); //clear trigger