SNES controller code to work properly.

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
User avatar
benjaminsantiago
Posts: 84
Joined: Mon Jan 20, 2014 9:40 pm
Location: Astoria, NY
Contact:

SNES controller code to work properly.

Post by benjaminsantiago »

Hey been having a bitch of a time trying to understand what's up with code for interacting with the controllers on the SNES. I've read bazz's tutorial (http://wiki.superfamicom.org/snes/show/ ... ller+Input) and the SNES Developer Manual which has some similar sample code.

The issue I am having is that the code seems to react slowly on the real hardware. I've separated out saving the value of the control registers during the vblank and then reacting to the button presses (only changing the bg color). It seems to work okay if I only poll one of the control registers ($4218 or $4219), but when I have more than one button from both control registers it starts to get weird.

The current on is trying to assign a different background color for L trigger, R trigger, R and L (on the d-pad).

I'm assuming it is the timing of the interrupts, but I am also experiencing different results in BSNES/ZSNES/SNES9x/the real hardware (via Power Pak)...
Attachments
joypad__bgColor.zip
(6.16 KiB) Downloaded 110 times
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: SNES controller code to work properly.

Post by koitsu »

Problems I see with this code:

1. EDIT: I misread which register you were reading (re: criticising polling of $4212), sorry about that. I'll leave my other comments though:

The SNES, like many consoles, supports the ability to run some 65816 code when VBlank begins. This is accomplished by setting the NMI vector to some code (which you've done), and then (outside of NMI of course) setting bit #7 of $4200 to 1, which you do. This "ties together" NMI execution when VBlank happens.

VBlank is the period when the electron gun is moving from the bottom of the screen (i.e. an entire screen has just been drawn, down to the last line/pixel, and is now resetting back up to the top to start drawing again).

Within your NMI routine (thus within VBlank) is where you should be doing updates to things like the background scroll registers (if panning around), CGRAM (palette) updates, and (of course) reading the joypad registers (storing the values in some area of RAM (ex. direct page) for use OUTSIDE of NMI).

If you need help understanding what a "proper" flow diagram should be, take a look at the SNES developers manual, specifically Chapter 23. Yes it's many pages, but if you print them out or review them slowly, you'll understand what you should be doing inside of NMI vs. outside of NMI. Although there's some stuff in there that's silly (IMO), like how it recommends re-setting "enable NMI" and some other stuff in your main routine constantly.

Remember: you have more CPU time outside of NMI than you do within NMI, but certain things should be done within NMI (with as minimal processing as possible) to ensure "proper visual updates" every frame. If you start having to do VRAM updates outside of NMI, you need to make sure (through software/logic, and it can be painful) that you're not changing something that you expect to be reflected somewhere above (i.e. before) where the electron gun has already been -- cuz they won't show up until after the next VBlank. Otherwise what you could end up with is a "mid-scanline" type-of effect, where, say, half the screen is blue and half the screen is green (if transitioning from blue->green) for a single frame.

That said: if you want your main (non-NMI) routine to wait for NMI (thus VBlank) to fire: use wai. That's mentioned in the tutorial but I don't see it in your code.

Hope this makes sense. :-)

2. Your NMI joypad reading routine makes absolutely no sense to me. I can't even figure out what the heck you're doing with all these __c and __p variables (EDIT: I guess they stand for "current" and "previous"). All you need to be doing is doing a 16-bit read from $4218 (controller #0), which will get you the "condition" (status) of all the buttons/directions being held at that moment in time (happening 60 times a second, remember -- it's in NMI), and store that result as I said above.

Then in your main loop, if you want to see if L or R (the top L/R buttons, not directional) is pressed, you simply look at the RAM contents (again, 16-bit), do a single bit against the bits you want (ex. bit #$0001 for R, bit #$0002 for L), and bne to some code that handles the situation where the button is pressed.

Alternately, and this is a time-wasting thing to do in NMI (IMO), but you can do an unrolled loop that stores into an individual RAM address the status of each individual button; it's easy using lda/sta/lsr/sta/lsr/sta/lsr/sta... but it's wasteful. The method I mentioned above is easiest, especially if you end up wanting to do something like "do thing X when person is holding up, but do thing X and Y when person is holding up + A".

TL;DR -- I don't understand the purpose behind your ldy/lda/sta/tya/eor/and/sta nonsense. Just do the lda $4212/and #$01/bne loop, read your registers, store them in RAM, do your VRAM/CGRAM/etc. updates, and get outta there.

EDIT: I took a look at the "tutorial" -- now I see what's going on there. Meh, I don't think all that logic is necessarily needed for what you're trying to do, meaning you don't need to do anything if someone releases a button -- all you want to know is if a button is being held and if so do something (ex. increment a colour value if L is being held, otherwise don't increment/do anything).

You should also be doing php/pha/phy at the start, and ply/pla/plp before the rti, because register sizes and contents don't get saved going in/out of NMI. (Add phx/plx in there if you end up using X of course) Part of me wonders if lack of register saving (including P/register sizes) is the cause of some unexpected behaviour.

3. Talking about your CGRAM updates (specifically adjusting $2121 then $2122 to change colour): you should be doing the actual $2121 and $2122 value writes within NMI, because you want to change VRAM (if at all possible) contents during NMI (before the electron gun, thus PPU, has a chance to start reading from things / drawing to the screen).

The way to do this is that in your main loop, when someone presses a button (L, R, etc.), set some flags in some RAM (ex. direct page) locations to indicate "L was pressed". Then in your NMI routine, check if L was pressed (simple lda/beq if you use a single boolean-like variable, e.g. value of 0 or 1, or just go for the lda/bit/bne (or beq to negate the compare of course)), and if so then you change the palette/colour/whatever.

I admit flat out I have not looked at your full main/non-NMI routine to figure out what all you're doing (for example I have no idea what this magical cmp #$56be is about). You're probably trying to do some "special range checking" on your R/G/B values.

4. Speculative: you may be "screwing around" too long in your main (non-NMI) loop and screwing things up. You are literally changing CGRAM while the electron gun draws, which based on timing could have some problems depending on how long things take. Again: this is speculative.

If you want some other example code to look at, you can download my old SNES docs (sndoc230.zip) and within that file is another file called test.zip which contains source code to a small demo/intro-like thing I wrote many years ago. It's commented (albeit badly and I think in one spot there's a mistake), but it's more linear and should help you understand how/when to poll the joypad, when to do graphical updates (although the code just does panning), and that sort of thing. No, it's not code I'm "proud" of per se, but I wrote it 21 years ago (I was 16 at the time), and I think it's an okay starting point.

P.S. -- It makes me happy to see people hacking on and learning the SNES/SFC in this day and age. I still think it's one of the easier 2D consoles to program for (all the "complex annoying stuff" of the NES/FC and 6502 were rectified more or less, so I find it easier to understand/work on. Emulator authors, however, get the shit end of the stick... :/ ).
DoNotWant
Posts: 83
Joined: Sun Sep 30, 2012 3:44 am

Re: SNES controller code to work properly.

Post by DoNotWant »

koitsu wrote: EDIT: I took a look at the "tutorial" -- now I see what's going on there. Meh, I don't think all that logic is necessarily needed for what you're trying to do, meaning you don't need to do anything if someone releases a button -- all you want to know is if a button is being held and if so do something (ex. increment a colour value if L is being held, otherwise don't increment/do anything).
What is wrong with it? It is very annoying if e.g. you are in a menu and have to lightly tap the buttons unless you want the cursor to blast through half the menu, or in games like MMX you usually don't want the users to be able to just hold a button to shoot.
User avatar
benjaminsantiago
Posts: 84
Joined: Mon Jan 20, 2014 9:40 pm
Location: Astoria, NY
Contact:

Re: SNES controller code to work properly.

Post by benjaminsantiago »

Hey guys thanks for the replies!
koitsu wrote:2. Your NMI joypad reading routine makes absolutely no sense to me.
Hey lol sorry, I made this post after a few days frustration and confusion. Usually I over-comment my code. The way the sequence of checks works after going through it line by line (in other examples), is to get anything that has changed since the previous button press (EOR will make any sequence of 0 --> 1 or 1--> 0 result in a 1). Then it filters out any "presses" with the AND(p was supposed to be previous but I changed it stand for "pressed", I think I left the old comments, my bad).

This is technically overkill for this example, but I'd like to be able to do most of the "common" controller stuff with a more sophisticated example at some point: press, release, hold/charge, tap, and sequences of buttons (hadouken, etc).
koitsu wrote: You should also be doing php/pha/phy at the start, and ply/pla/plp before the rti, because register sizes and contents don't get saved going in/out of NMI. (Add phx/plx in there if you end up using X of course) Part of me wonders if lack of register saving (including P/register sizes) is the cause of some unexpected behaviour.
forgot about this. I will check if this effects things
koitsu wrote: I admit flat out I have not looked at your full main/non-NMI routine to figure out what all you're doing (for example I have no idea what this magical cmp #$56be is about). You're probably trying to do some "special range checking" on your R/G/B values.
those were to check if the background already was the color specified by that button, to avoid multiple checks of the same button, I thought that was maybe the case based on what I was experiencing with bsnes.
koitsu wrote:4. Speculative: you may be "screwing around" too long in your main (non-NMI) loop and screwing things up. You are literally changing CGRAM while the electron gun draws, which based on timing could have some problems depending on how long things take. Again: this is speculative.
possible, I did a working example a while ago where I just made the background change colors in a rainbow fashion. I looked back, I was in fact WAI'ing
koitsu wrote:If you want some other example code to look at, you can download my old SNES docs (sndoc230.zip)
thanks! just found it.
koitsu wrote:P.S. -- It makes me happy to see people hacking on and learning the SNES/SFC in this day and age. I still think it's one of the easier 2D consoles to program for (all the "complex annoying stuff" of the NES/FC and 6502 were rectified more or less, so I find it easier to understand/work on. Emulator authors, however, get the shit end of the stick... :/ ).
Yeah! I wish I had a tad more EE/CS knowledge (I went to art school and did some programming-y stuff and then just read some books). Seeing the cool homebrew stuff that is happening keeps me from getting too frustrated. I think the SNES/SFC are about two steps away from a big resurgence in terms of hacking and homebrew, it's just the tools are not all the way there yet.
DoNotWant
Posts: 83
Joined: Sun Sep 30, 2012 3:44 am

Re: SNES controller code to work properly.

Post by DoNotWant »

I'm slowly learning SNES programming as well, and have been looking at the same tutorials as you have.
Here are 2 simple programs, if you want to have a look. Written for bass v14 by byuu.
The scrolling one have some dodgy clamping, but it was mostly just to see if I could scroll the screen at all.
https://www.dropbox.com/s/epr3nliq0v7px ... 283%29.rar
https://www.dropbox.com/s/v93oz8dba7he0 ... 282%29.rar
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: SNES controller code to work properly.

Post by koitsu »

DoNotWant wrote:
koitsu wrote: EDIT: I took a look at the "tutorial" -- now I see what's going on there. Meh, I don't think all that logic is necessarily needed for what you're trying to do, meaning you don't need to do anything if someone releases a button -- all you want to know is if a button is being held and if so do something (ex. increment a colour value if L is being held, otherwise don't increment/do anything).
What is wrong with it? It is very annoying if e.g. you are in a menu and have to lightly tap the buttons unless you want the cursor to blast through half the menu, or in games like MMX you usually don't want the users to be able to just hold a button to shoot.
Using your two examples:

1. "Blasting through half the menu" (ex. by holding Down) is addressed by using delays (usually waiting for VBlank N times) in your main routine. The necessary logic for menuing systems do not need to know "if you previously had the button held down or released it", all they need to know is "is the button being held down right now". In other words: you don't need to know if there was a state change for the Down button between the last frame and the current frame. My test.zip demo is an example of this (using VBlank delays but still having smooth 60fps background panning, while only checking the current state of the button).

2. MMX means Megaman X I assume, and what you're referring to (again assuming) is where Megaman can either shoot his weapon or if you hold down the fire button it charges up over time and then is fired when the button is released. In this case yes, you need to keep some kind of historic data laying around so you can know whether or not the button was simply tapped (standard fire) or is being held down for more than N frames (to start the powering-up of the weapon) and finally released after N frames (to release the powered-up weapon).

Fighting games with combos or timing-sensitive moves are another type which require a kind of "log" of button presses -- I've always wondered how those games (ex. Street Fighter II) actually implemented their joypad routines *and* their logic path (outside of NMI) to handle insane button combinations combined with frame timing. I bet someone somewhere has done a write-up of it and I bet it's scary. :-) But you gotta remember there's also delays that have to be considered (not waiting for VBlank but just overall timing delays), since per-frame analysis is going to be faster than a human being can actually push buttons.

My point here is that what the OP is doing in this particular case doesn't really warrant needing to track previous button states and compare current vs. previous. If he plans on adding something where tapping vs. holding a button is needed, then yeah, keeping a "log" of button presses (possibly just current vs. previous is enough, but then again maybe not, we don't know right now!) is the way to go.
DoNotWant
Posts: 83
Joined: Sun Sep 30, 2012 3:44 am

Re: SNES controller code to work properly.

Post by DoNotWant »

Yes, mega man x.

Thanks!
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES controller code to work properly.

Post by tepples »

koitsu wrote:1. "Blasting through half the menu" (ex. by holding Down) is addressed by using delays (usually waiting for VBlank N times) in your main routine. The necessary logic for menuing systems do not need to know "if you previously had the button held down or released it", all they need to know is "is the button being held down right now".
Let's say a game's menu samples the controller every 15 frames, triggering an action for each period during which the button was pressed. Sometimes I hold down the button 14 frames and get no actions. Sometimes I hold the button down 16 frames and get two actions. A lot of low-quality falling block games that I've played have logic like this. The way well-tuned games work in practice is to keep one previous frame of data. This way, every time I press down, I get an action. Autorepeat, like you see when you reply to a post and hold down a letter or arrow key, needs two extra historical values: the button that was held and how long it was held. The controller reading code (pads.s) in my NES project template has an example of how to do this on the NES; the Super NES isn't much different.
In other words: you don't need to know if there was a state change for the Down button between the last frame and the current frame. My test.zip demo is an example of this (using VBlank delays but still having smooth 60fps background panning, while only checking the current state of the button).
But how easily can you scroll to an individual pixel position? I imagine it'd take a lot of back and forth to get it aligned just so.
Fighting games with combos or timing-sensitive moves are another type which require a kind of "log" of button presses -- I've always wondered how those games (ex. Street Fighter II) actually implemented their joypad routines *and* their logic path (outside of NMI) to handle insane button combinations combined with frame timing. I bet someone somewhere has done a write-up of it and I bet it's scary. :-)
Charge attacks like Guile's Sonic Boom are essentially the same logic as autorepeat: store how long the player has pressed back or down. Key combo attacks are a ring buffer of (which key, what frame) pairs, which allows for logic like this:
  • If the last four keys were forward, down, forward, punch, and the oldest of these four was no less recent than 15 frames ago: do a leaping uppercut
  • If the last three keys were down, forward, punch, and the oldest of these three was no less recent than 15 frames ago: throw a fireball
  • If the last three keys were down, back, kick, and the oldest of these three was no less recent than 15 frames ago: do a spinning scissor kick
User avatar
benjaminsantiago
Posts: 84
Joined: Mon Jan 20, 2014 9:40 pm
Location: Astoria, NY
Contact:

Re: SNES controller code to work properly.

Post by benjaminsantiago »

DoNotWant wrote:I'm slowly learning SNES programming as well...if you want to have a look. Written for bass v14 by byuu.
These look tight, the syntax for bass is a bit arcane to me at the moment though. byuu hangs out in these forums right? Does bass support spc700 code as well? Bass appeals to me for being actively worked on and because he also does bsnes/higan. I also came across that dude lint's stuff (he made that snes version of Kung Fu) and looks like he uses an assembler from WDC (who makes the 65816)...I've been considering a switch from wla, but I think I am going to get my assembly game up first.
koitsu wrote: My point here is that what the OP is doing in this particular case doesn't really warrant needing to track previous button states and compare current vs. previous. If he plans on adding something where tapping vs. holding a button is needed, then yeah, keeping a "log" of button presses (possibly just current vs. previous is enough, but then again maybe not, we don't know right now!) is the way to go.
I am currently learning the controller stuff to add something in for learning sound coincidentally, but my end goal is to eventually add in sprites and do something like the control test of the SNES Test Program (graphic of a controller) + the control buffer in Chou Aniki. It's not useful now to have the previous states and stuff but will be at some point.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES controller code to work properly.

Post by tepples »

Blargg made macro packs to support SPC700 in ca65, both with Sony syntax and with MOS/WDC syntax.
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: SNES controller code to work properly.

Post by koitsu »

tepples wrote:
In other words: you don't need to know if there was a state change for the Down button between the last frame and the current frame. My test.zip demo is an example of this (using VBlank delays but still having smooth 60fps background panning, while only checking the current state of the button).
But how easily can you scroll to an individual pixel position? I imagine it'd take a lot of back and forth to get it aligned just so.
With regards to background/screen panning? It's easy -- you can track the X/Y coordinates (probably the "upper left corner") yourself. But if you mean something like: "the BG is already at 45,189 and you want it so when the player taps Up it automatically/smoothly pans to 45,0" or maybe even "...when the player taps Up it pans a little but, but as they hold Up longer the panning speed increases", then yeah that's a bit trickier. That's all done with mathematics at the software level, and while I've only written code like that one in my life (not for the SNES/SFC, but the IIGS, and how to make some graphics move in sine-wave pattern that resembles waves) and I remember having a lot of difficulty wrapping my brain around it (I needed a lot of help from the other guy in our group).

The technical stuff: the BG offset registers (ex. $210d / $210e for BG1) literally accept a raw 10-bit unsigned value of 0-1023 (written via two 8-bit writes) to represent their H/V scroll offset (or a 13-bit signed value of -4096 to 4095 for MODE 7). Interlaced modes and pseudo-512 change this a little bit (I haven't used those though, nor have I done MODE 7). Check out page A-10 in the developers manual for a visual representation of how it works (or A-11 for MODE 7); Nintendo could have done a better job with their diagram though, part of it is confusing as hell.

Screen panning is the one thing that has always impressed the hell out of me on video game consoles -- the premise of a memory-mapped register that just "pans the screen" and when done right its smooth as a baby's butt. The Amiga did a lot of this too (no idea how it works). I guess my interest stems from the fact that the IIGS had absolutely nothing like this, so the concept of smoothly panning around a background (especially with the SNES/SFC and its multiple backgrounds) was mindblowing. There's lots of features on the SNES/SFC that make me drool; that's one of them.
Kannagi
Posts: 100
Joined: Sun May 11, 2014 8:36 am
Location: France

Re: SNES controller code to work properly.

Post by Kannagi »

there with very good response.
I do not think it is a good idea to make a loop in the vblank

Code: Select all

    v_blank:
    ;---------------------------------------------------------------
    ;wait for joypad to be ready (during VBlank)
    lda $4212
    and #$01
    bne v_blank
If not for my part, I make a simple code in the game loop:

Code: Select all

          lda $4219	; read joypad 1 High
		AND #$08
		cmp #$08
		bne +
		
			;code
			
		+:
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: SNES controller code to work properly.

Post by psycopathicteen »

I like how somebody actually said the SNES is easy. Once you get the basics the work such as the interrupts, joypads and oam, it becomes a lot more straightforward. DMA animation became easier when I found out it was actually faster to use a table of DMA transfers than to use an unrolled loop updating 16x16 tile patterns individually.
User avatar
benjaminsantiago
Posts: 84
Joined: Mon Jan 20, 2014 9:40 pm
Location: Astoria, NY
Contact:

Re: SNES controller code to work properly.

Post by benjaminsantiago »

psycopathicteen wrote:I like how somebody actually said the SNES is easy. Once you get the basics the work such as the interrupts, joypads and oam, it becomes a lot more straightforward. DMA animation became easier when I found out it was actually faster to use a table of DMA transfers than to use an unrolled loop updating 16x16 tile patterns individually.
Yeah I kind of came to the realization that it's not unlike flash/jQuery/openFrameworks which are essentially just a bunch of objects that have to be set up correctly. And as you kind of implied there's not too much crazy timing stuff except stuff like what I am experiencing with the joypads. The problem for me has been the other realization that I don't know assembly that well.
psycopathicteen
Posts: 3001
Joined: Wed May 19, 2010 6:12 pm

Re: SNES controller code to work properly.

Post by psycopathicteen »

Here is a code that does most of the framework necessary for setting up the SNES. I set up a vblank.txt and main.txt, for people to write code without having to deal with all that other timing crap.
Attachments
snes initialization.zip
(101.18 KiB) Downloaded 109 times
Post Reply