SNES gamepad rumble/analog support proposal

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
SNES gamepad rumble/analog support proposal
by on (#242321)
I want to offer an extension for ROM hacks and homebrew to be able to add rumble support to SNES gamepads.

Obviously, this'll be optional, but designed in a way it could work on real hardware if an adapter for a PC gamepad were made.

As for why? Well, why not?

My thought is to add it to the IObit of the gamepads:

Whenever the latch is cleared (1->0 transition), the IObit value, eg $4201.(d7 player 1, d6 player 2) goes into an 8-bit shift register (shift left: x=x<<1|iobit). So basically: set the latch, set IObit, clear the latch to clock it. Repeat eight times. Whenever the shift register == 'F' (for Force Feedback), 0x46, 0b0010'0110, the controller will begin to rumble. Stopping the rumble will be just writing another bit into the register. Whenever the shift register != 'F', rumble stops. I don't know if we should have an automatic timeout (and if so after how long?) or if that just adds to the complexity.

The choice of 'F' is so that no matter what is written in the next six writes, it won't start another rumble:

Code:
01000110
1000110x
000110xx
00110xxx
0110xxxx
110xxxxx
10xxxxxx
0xxxxxxx


And when you write 'F' again, it won't activate until the eighth and final write:

Code:
01000110
10001100
00011001
00110010
01100100
11001000
10010001
00100011


Another option we could potentially do while we're at it would be to support analog read-out.

While reading the joypad 16 times to get the controller state from data1, analog axis data would be output to data2. The first 8-bits would be the X-axis, the next 8-bits the Y-axis, in little-endian ordering, so in this way, the results from auto-joypad polling would place X into $4219 (player 1) or $421b (player 2), and Y into $421d (player 1) or $421f (player 2.)

Since this bit reads as 0 with stock controllers, a value of 0x00 will be considered "no analog support present." 0x80 will represent a centered axis, 0x01 will represent up/left, 0xff will represent down/right.

If we're worried about this interfering with existing games, we could require a one-time write of 'A' (for Analog) to the shift-register to unlock the support. But I feel that'd be annoying to have to hook the reset vector of games to enable it in hacks.

Obviously, this isn't compatible with multitaps, so no five-player Super Rumbleman hack.

Is this agreeable? Any feedback (no pun intended)? I just posted v109, so we should have at least a month to work on this with bsnes WIPs and make changes if needed.

...

In the past, Zsnexbox did this via a form of cheat codes. It'd have a list of memory address watchpoints, and whenever those RAM addresses were written, the gamepad would rumble. The upside of it was that the author was able to add support for dozens of games very rapidly.

But the downsides were that it couldn't ever work on real hardware, and there were tons of false positives (games clearing RAM at reset, rumble during other parts of the game like end-of-level scoring when it deducts your health to add to your score, etc etc.)

Like MSU1 and the proposed widescreen, I think if we're going to do it, it needs to be done right to leave a good impression.
Re: SNES gamepad rumble/analog support proposal
by on (#242330)
byuu wrote:
Whenever the latch is cleared (1->0 transition), the IObit value, eg $4201.(d7 player 1, d6 player 2) goes into an 8-bit shift register (shift left: x=x<<1|iobit). So basically: set the latch, set IObit, clear the latch to clock it. Repeat eight times. Whenever the shift register == 'F' (for Force Feedback), 0x46, 0b0010'0110, the controller will begin to rumble. Stopping the rumble will be just writing another bit into the register. Whenever the shift register != 'F', rumble stops. I don't know if we should have an automatic timeout (and if so after how long?) or if that just adds to the complexity.
I am tempted to prefer some discrete logic option so that it doesn't require a programmer ... but then again, one can get an ATtiny for awfully cheap.

Implementing exactly the above using discrete logic requires three ICs... but maybe a design that only cared about six of the eight bits would be a tolerable variation? That would require only a 74'138 and a 74'164, both of which are inexpensive.

Quote:
Another option we could potentially do while we're at it would be to support analog read-out.
[...]
If we're worried about this interfering with existing games, we could require a one-time write of 'A' (for Analog) to the shift-register to unlock the support. But I feel that'd be annoying to have to hook the reset vector of games to enable it in hacks.
I agree that it should be unnecessarily, and reserving the value of 0 for "no analog" is better.

Is there any merit at all in just repurposing the existing mouse protocol, but redefining it to encode absolute position instead of relative motion? This is basically exactly the experience of using a Trackpoint or older software (e.g. Deluxe Paint) that supported using a joystick as a pointing device.
Re: SNES gamepad rumble/analog support proposal
by on (#242333)
More or less standard for modern controllers is a rumble interface is for two weighted motors (low and high frequency) with two intensity inputs.

Usually with rumble you want a mix of both to break up the monotony of a single frequency but there's a spectrum going from the heavy/chunky feeling low frequency rumble to the buzzier high frequency rumble.

For example, here's Microsoft's current API for it:
https://docs.microsoft.com/en-us/windows/uwp/gaming/gamepad-and-vibration#vibration-and-impulse-triggers-overview

I think in practice the intensity steps tend to be pretty granular on a lot of controllers, but it's still a pretty important expressive ability besides just "rumble off, rumble on". 1-bit rumble control would be very monotonous, IMO.


So, if you want to do something that can easily/transparently be mapped to modern controllers you might want more than just a 1 bit input here. 4-bits (2 x 2-bits) for 0-3 on each motor would give you something that is very close to a modern rumble interface.


I think a lot of modern controllers or their driver/APIs have an automatic timeout that returns the intensity to 0 if it hasn't been set in the last second or two. I forget the details of schemes I've seen for this... I remember the original XBox 360 controllers neglected this and had a tendency to rumble all night when they went off spuriously.

In general, I don't a timeout necessarily needs to be part of the API itself. If you're connecting with a modern controller I think it's likely to have its own internal timeout? It's good advice not to have rumble for long durations, of course, but I dunno if that needs to be anything more than a recommendation.
Re: SNES gamepad rumble/analog support proposal
by on (#242335)
I actually have a game that allowed me to test various rumble intensities since I was configuring them on the go... it was RPG Maker 2 for the PS2. Not sure from a hardware standpoint how that got implemented, though.
Re: SNES gamepad rumble/analog support proposal
by on (#242336)
I seem to remember that vibration on at least one platform is 1-bit, but games vary perceived intensity by feeding PWM/PDM into the motor. They'll do 1 frame on 3 frames off, 1 frame on 1 frame off, 3 frames on 1 frame off, or other ratios.
Re: SNES gamepad rumble/analog support proposal
by on (#242337)
N64, GCN, and Wii are all single bit. The PS2's dualshock apparently is a single bit for the "small" motor and an 8-bit value for the larger motor (see command 0x4D).

The bandwidth of a vibration motor is really low; PWM or PDM really should yield acceptable results.
Re: SNES gamepad rumble/analog support proposal
by on (#242338)
I'd agree, at least if you're connecting directly to specific specialized hardware. The inertia of the device definitely keeps their effective switching frequency low.

However, if you're an emulator connecting through the OS' API, or connecting to a modern gamepad through some interface converting to USB, etc. I have much less faith that you're going to get desirable results out of trying to manually PDM it.

I could be wrong. Maybe if you try it most modern controllers will respond well to sending PDM 1.0s and 0.0s through the API, but my confidence in it is low. On the other hand, using the modern API as-is (i.e. 2 variable intensity inputs) I've personally tested and seen work well with a large variety of common controllers, with experience across several years and platforms.

With the layer of indirection you get from API / drivers / hardware interface, it's just a big unknown to me whether PDM techniques would work. I've never thought to test it personally, because there's never been a need to when you're already going through an API with intensity input. Might be fine? Might not. The robustness of this is a research question I'm not prepared to answer, but someone else out there may have done it before.


What I'm saying is: if your goal is to reuse modern controllers that people already have, it might be worth creating an interface that looks like the one they already have, rather than compromising on some subset or risking compatibility on an experimental idea.
Re: SNES gamepad rumble/analog support proposal
by on (#242340)
Quote:
Implementing exactly the above using discrete logic requires three ICs... but maybe a design that only cared about six of the eight bits would be a tolerable variation? That would require only a 74'138 and a 74'164, both of which are inexpensive.


I'm not opposed to that. The main annoyance is writing IObit doesn't clock anything, so we have to use $4016 writes to simulate clocking. That'll add to the time it takes, and so if you do the rumble outside NMI and it crosses over an NMI boundary it might interrupt the transfer when auto-joypad polling strobes $4016 itself. But I don't know of another way to do this that won't possibly trigger false-positives on officially licensed games.

Six bits is faster than eight bits, and probably well within the "won't trigger false-positives" window.

Quote:
Is there any merit at all in just repurposing the existing mouse protocol


Main limitation there is games would have to have their entire input handling rewritten instead of just adding rumble where you want it. So I was trying to keep the gamepad protocol as much as possible.

Quote:
More or less standard for modern controllers is a rumble interface is for two weighted motors (low and high frequency) with two intensity inputs.


Well yeah but we're trying to keep the complexity down a bit. Having to send motor controls for two motors and controlling their frequency seems a bit like overkill, right? And the added overhead of sending all that additional data is going to make turning the motor on and off take longer.

The Game Boy Player and the Game Boy rumble paks were just simple on-off affairs and in practice they work fine. Zsnexbox was also a simple rumble and nobody to my knowledge ever felt it was lacking.

The limitation here is that the protocol to send data to controllers is *very limited* manual bit-banging and clocking, and auto joypad polling gets in our way too. Having a protocol where you need to send 24+ bits of information is a bit extreme.

If we had an 8-bit interface and could just write whole bytes to a register? Heck yeah I'd be all for extended controls, 16-bit axes, analog trigger/shoulder buttons, etc. But we don't have that if we want this to be a real thing that works on real hardware.

Quote:
I seem to remember that vibration on at least one platform is 1-bit, but games vary perceived intensity by feeding PWM/PDM into the motor. They'll do 1 frame on 3 frames off, 1 frame on 1 frame off, 3 frames on 1 frame off, or other ratios.


Thought about having the rumble latch be just 1-bit instead of 8-bits so it was super fast to turn on and off, but games like Tales of Phantasia clear IObit to zero (overzealous boot initialization clearing too many registers.) So you'd end up with rumble always on in a game like that.
Re: SNES gamepad rumble/analog support proposal
by on (#242342)
byuu wrote:
I'm not opposed to that. The main annoyance is writing IObit doesn't clock anything, so we have to use $4016 writes to simulate clocking. That'll add to the time it takes, and so if you do the rumble outside NMI and it crosses over an NMI boundary it might interrupt the transfer when auto-joypad polling strobes $4016 itself. But I don't know of another way to do this that won't possibly trigger false-positives on officially licensed games.
For no good reason, I misread and assumed that the shift register would be clocked by the same "read from $4016/$4017" strobe that faking SPI on the NES does, instead of what you suggested (manually driving the external clock line with writes to $4016).

I have no opinion about which you actually decide on, but on the off chance that someone else made the same incorrect assumption I figure I should explicitly call this out.
Re: SNES gamepad rumble/analog support proposal
by on (#242343)
byuu wrote:
The Game Boy Player and the Game Boy rumble paks were just simple on-off affairs and in practice they work fine. Zsnexbox was also a simple rumble and nobody to my knowledge ever felt it was lacking.

I don't know what Zsnexbox is, but I personally found rumble in various consoles very much lacking until around PS2. With 360/PS3 it suddenly got a lot better. I more or less universally hated the feature until then.

Really though I'm talking about a rumble capability that has been kinda standardized and commonly available for 15 years or so and continuing up to present. If you wanna go back further than that it gets a bit more minimal in the earlier iterations... but this is what we already have so why not use it as-is instead of cutting it down?

byuu wrote:
The limitation here is that the protocol to send data to controllers is *very limited* manual bit-banging and clocking, and auto joypad polling gets in our way too. Having a protocol where you need to send 24+ bits of information is a bit extreme.

Okay, but my actual suggestion was 4 bits, not 24.

It's a bit of a ballpark, but I think having 3 levels of intensity per motor is more or less sufficient to cover the effective expression these devices have, based on past experience.

I believe some form of intensity control is critical for any rumble implementation. The suggested PDM is a potential way to do it, but I'm skeptical that this will work well/easily/conveniently if we're trying to interface with modern controllers. If you can accept that intensity is important, ultimately the complexity of choosing intensity has to go somewhere. Here's the 3 options I see:

  • 1. Having to implement PDM in the SNES game software. (May require #3 as well.)
  • 2. More than 1 bit of data in the proposed interface protocol.
  • 3. A translator/filter in the external hardware interface or emulator converts PDM signal into modern intensity API. (Also adds lag, but maybe not too significant.)

The second part, frequency control, maybe it sounds frivolous to you, but I think it does a lot for the experience. Yeah rumble is kind of a vague and subtle effect, and I can't even post video examples to demonstrate since it's non-visual non-audio... but if you're going to be using it, there's something to gain from having these properly separated. Hard, soft, chunky, smooth, there's utility in these different modes of haptic feedback. I've seen a bunch of games put it to very appropriate use, and I think it's a valuable feature.


So... 2 bits would cover individual motor control, and maybe also intensity via PDM technique, provided that actually works as expected. 4 bits would give a pretty good range of control, IMO. 6 bits is probably overkill? I definitely wasn't suggesting 24.
Re: SNES gamepad rumble/analog support proposal
by on (#242344)
It has to be more than 4-bits to prevent false-positives. So you need signature bits in the frame as well. Then you also wanted left+right motor settings.
Re: SNES gamepad rumble/analog support proposal
by on (#242345)
Encoding 1-bit PDM on a 6502 family CPU isn't hard. It's less involved, for example, than translating 192 gray levels to 4 with dithering on the last place, which one of my Game Boy projects does for smooth fades to and from white.
Code:
  sep #$20                ; 8-bit A/M
  clc
  lda rumble_intensity    ; 0 to 255
  adc rumble_pdm_residue
  sta rumble_pdm_residue
  ; bit to send is in carry


On the decoding side, to recover an approximation of rumble_intensity for use with "modern" receivers, apply a 1-pole low pass filter, also called a leaky integrator. I don't yet know assembly for popular microcontroller cores (PIC, AVR), so I'll use more 6502:
Code:
  ; Leaky integrator: intensity - floor(intensity/8) - 1
  lda rumble_intensity
  beq @is_zero
    lsr a
    lsr a
    lsr a
    eor #$FF
    clc
    adc rumble_intensity
  @is_zero:
  bit rumble_last_bit  ; received bit in bit 7
  bpl @last_bit_off
    clc
    adc prefs_rumble_strength  ; 1 to 32
  @last_bit_off:
  sta rumble_intensity


A theoretically closer approximation could be had by using the same low-pass filter formula on both sides, but I doubt that at this resolution, it'd matter much.

To prevent false positives, the hacked game could send 2 whole bytes of unlock through pseudo-SPI, and once the MCU receives those, it begins to honor vibration commands. Sending the vibration commands in the vblank NMI handler, after all DMA completes, should help vibration stay out of the way of autoreading.
Re: SNES gamepad rumble/analog support proposal
by on (#242346)
byuu wrote:
Then you also wanted left+right motor settings.

To clarify again, when I said 4 bits I meant: 2 bits for each motor.

And when I said 2 bits as a more minimal alternative I meant: 1 bit for each motor. (+ deal with the complexity/lag of PDM upstream/downstream to get intensity.)

Anyhow, if nobody else things separate motors is valuable then go ahead and ignore me, but I personally feel it makes the difference between rumble as a "cheap" gimmick and something more valuable.
Re: SNES gamepad rumble/analog support proposal
by on (#242386)
There was a thread about this for NES, a little while back:
https://forums.nesdev.com/viewtopic.php?f=9&t=17720
As usual, something that's simple on the SNES requires some tricks on the NES. But I think the last post in that thread by emerson shows a good scheme for sending data with just latch and clock.

Since controllers normally aren't clocked with the latch high, it makes a good "start of frame" marker for writing to a controller. Then the receiver knows that for the next N clocks, latch is the data input.

Another way would be to send COBS-encoded data. When the console reads the controller, it sends null bytes, which are simply ignored.

Just mentioning that because I think for peripherals especially, anything that can give it wider adoption would be helpful, NES compatibility would (potentially) do that.