Use the SNES/NES controller in Assembly: Difference between revisions

From F256 Foenix
Jump to navigationJump to search
m (Added description)
(Finally past the initial draft)
 
Line 1: Line 1:
=== '''; Code contributed by Stefany Allaire on Discord''' ===
== 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].


=== ; This is test code that goes through all the ports and mux the NES and SNES... ===
The official gamepads of yesteryear work without issue with this and are plentiful in the used market.  
NES_CTRL = $D880


NES_STAT = $D880
Third party modern recreations are sometimes flaky or not working at all. Share you discoveries in the discord!


NES_EN   = $01
Super famicom gamepads work very well as substitute for regular SNES gamepads.


NES_MODE = $04
=== 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 = $80
=== 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
|}


NES_DONE = $40
==== 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
|}


NES_JOY0 = $D884
=== 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.


NES_JOY1 = $D886
=== 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


NES_JOY2 = $D888
POKE(PAD_CTRL, 0b10000101); //trigger, snes mode, enabled
 
while((PEEK(PAD_STAT) & 0x40) == 0) //loop while this bit is cleared
NES_JOY3 = $D88A
      ;
 
//section where reactions are made to the various SNES presses
SNES_JOY0_LO = $D884
POKE(PAD_CTRL, 0b00000101); //clear trigger
 
</pre>
SNES_JOY0_HI = $D885
 
SNES_JOY1_LO = $D886
 
SNES_JOY1_HI = $D887
 
SNES_JOY2_LO = $D888
 
SNES_JOY2_HI = $D889
 
SNES_JOY3_LO = $D88A
 
SNES_JOY3_HI = $D88B
 
Test_4N4S:
 
                jsr SetIOPage0
 
                '''; Test NES First'''
 
                '''; Enable and Begin a Sample'''
 
NES_Start_Again:                 
 
                lda #NES_EN
 
                sta NES_CTRL
 
                lda NES_CTRL
 
                ora #NES_TRIG
 
                sta NES_CTRL
 
                and #~NES_TRIG
 
                sta NES_CTRL               
 
NESWait2beDone               
 
                lda NES_STAT
 
                and #NES_DONE
 
                cmp #NES_DONE
 
                bne NESWait2beDone
 
                '''; When here, the Joystick Sampling is done'''
 
                lda NES_JOY0
 
                eor #$FF
 
                ldx #$00
 
                jsr Byte2Hex
 
                lda NES_JOY1
 
                eor #$FF
 
                ldx #$03
 
                jsr Byte2Hex
 
                lda NES_JOY2
 
                eor #$FF           
 
                ldx #$06
 
                jsr Byte2Hex
 
                lda NES_JOY3  
 
                eor #$FF                           
 
                ldx #$09
 
                jsr Byte2Hex
 
                lda NES_JOY3
 
                eor #$FF               
 
                and #$C0
 
                cmp #$C0
 
                beq NESDoneWithTest
 
                '''; Let's do the SNES'''
 
                lda #NES_EN | NES_MODE
 
                sta NES_CTRL
 
                lda NES_CTRL
 
                ora #NES_TRIG
 
                sta NES_CTRL
 
                and #~NES_TRIG
 
                sta NES_CTRL               
 
SNESWait2beDone               
 
                lda NES_STAT
 
                and #NES_DONE
 
                cmp #NES_DONE
 
                bne SNESWait2beDone
 
                '''; When here, the Joystick Sampling is done'''
 
                lda SNES_JOY0_HI
 
                and #$0F
 
                eor #$0F
 
                ldx #$50
 
                jsr Byte2Hex
 
                lda SNES_JOY0_LO
 
                eor #$FF
 
                ldx #$52
 
                jsr Byte2Hex
 
                lda SNES_JOY1_HI
 
                and #$0F
 
                eor #$0F
 
                ldx #$55
 
                jsr Byte2Hex
 
                lda SNES_JOY1_LO
 
                eor #$FF
 
                ldx #$57
 
                jsr Byte2Hex
 
                lda SNES_JOY2_HI
 
                and #$0F
 
                eor #$0F
 
                ldx #$5A
 
                jsr Byte2Hex
 
                lda SNES_JOY2_LO
 
                eor #$FF
 
                ldx #$5C
 
                jsr Byte2Hex
 
                lda SNES_JOY3_HI
 
                and #$0F
 
                eor #$0F
 
                ldx #$5F
 
                jsr Byte2Hex
 
                lda SNES_JOY3_LO
 
                eor #$FF
 
                ldx #$61
 
                jsr Byte2Hex
 
                jmp NES_Start_Again
 
NESDoneWithTest:
 
                lda #$00
 
                sta NES_CTRL
 
                rts
 
Byte2Hex:
 
                sta $50
 
                stx $51
 
                jsr SetIOPage2
 
                '''; Hi Part'''
 
                lda $50                             
 
                and #$F0
 
                lsr a
 
                lsr a
 
                lsr a
 
                lsr a
 
                tax
 
                lda HEX, x  ;
 
                ldx $51
 
                sta $c320,x
 
                '''; Low Part'''
 
                lda $50                 
 
                and #$0F
 
                tax
 
                lda HEX, x
 
                ldx $51
 
                sta $c321,x
 
                jsr SetIOPage0
 
                rts

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