NES Rumble Controller

Discuss hardware-related topics, such as development cartridges, CopyNES, PowerPak, EPROMs, or whatever.

Moderator: Moderators

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

Re: NES Rumble Controller

Post by tepples »

Besides, all a glitch would do is increase intensity by one step for one frame, something the player is unlikely to notice.

Do the Nintendo 64 Rumble Pak and GameCube vibration feature even have an intensity setting?
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: NES Rumble Controller

Post by lidnariq »

No, both the Gamecube controller and Rumble Pak use a single bit to control the vibration motor.

Gamecube controller serial protocol consists of a query of three bytes: [0x40 X Y] where Y=even for rumble off and Y=odd for rumble on. (The controller then replies with a standard report). (Lots of other sources vary on what X and the upper bits of Y are. No-one seems to have tracked down what they do)
Rumble Pak protocol piggybacks on top of the protocol for the Transfer Pak: a single bit is mapped to the upper half of its address space, and writes [0x03 Ah Al <31 bytes of don't care> Z] with the ones bit on in the last byte turn the motor on, and vice versa. Oddly enough, the bit is readable too.

Apparently the Wiimote is also just on/off.
calima
Posts: 1745
Joined: Tue Oct 06, 2015 10:16 am

Re: NES Rumble Controller

Post by calima »

Apparently Nintendo's rumble motors are really low current too, but I've only found one source on the net for GC rumble measurement, 30 mA. I want to confirm that and measure a few controllers (orig, third party, new Smash Edition ones) for potential N64 use. Extension cable ordered.
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Re: NES Rumble Controller

Post by Memblers »

tepples wrote: Besides, all a glitch would do is increase intensity by one step for one frame, something the player is unlikely to notice.
Yeah, that's a good point. It makes sense to write to it every frame, and the occasional variation really shouldn't be noticeable. DPCM glitch should be pretty much irrelevant in that case.

When I was talking to kevtris about controlling a rumble motor like this, he suggested using the latch/OUT0 line. It's a good idea too, because games normally only leave it active for like 6 CPU cycles to latch the controller. You could also accept CLK pulses while OUT0 is active for sending those parameters. Normal games would pretty much never do that either, because it would only read the A button repeatedly.

Anyways, I'm fine with any control method, just figured I'd put that out there.
User avatar
Broke Studio
Formerly glutock
Posts: 181
Joined: Sat Aug 15, 2015 3:42 pm
Location: France
Contact:

Re: NES Rumble Controller

Post by Broke Studio »

I finally have some time to post here :)
Memblers wrote:When I was talking to kevtris about controlling a rumble motor like this, he suggested using the latch/OUT0 line.
Yep, I think that's what I would do.

One idea could be to use RS-232 with the OUT0 line.
The routine below works for me with both NTSC and PAL NES (front loaders), but it may be time consuming ...
The receiver must be set to 115400 bauds (not 115200).
If only 4 bits are used, we can reduce the routine, and set the RS-232 receiver to 5N1 or maybe 4N1 ?

Let me know what you think.

Code: Select all

; ZEROPAGE vars
temp:           .res 1
rumbleState:    .res 1
; ----bbaa
; aa : player 1 motor intensity (0 : stop / 1 : low / 2 : mid / 3 : high)
; bb : player 2 motor intensity (0 : stop / 1 : low / 2 : mid / 3 : high)
; ---- : unused bits ?

    lda rumbleState ; 2
    sta temp        ; 2
    lda #0          ; 2
    sta $4016       ; 4 ; start bit
    lda temp        ; 3
    nop             ; 2
    ;nop             ; 2 ; NTSC - works for me without this line on an NTSC and PAL NES front loader
    
    pha             ; 3
    and #$01        ; 2
    sta $4016       ; 4 - 16 ; bit 0
    pla             ; 4
    lsr             ; 2

    sta temp        ; 3
    and #$01        ; 2
    sta $4016       ; 4 - 15 ; bit 1
    lda temp        ; 3
    lsr             ; 2
    nop             ; 2
    
    pha             ; 3
    and #$01        ; 2
    sta $4016       ; 4 - 16 ; bit 2
    pla             ; 4
    lsr             ; 2
    
    sta temp        ; 3
    and #$01        ; 2
    sta $4016       ; 4 - 15 ; bit 3
    lda temp        ; 3
    lsr             ; 2
    nop             ; 2
    
    pha             ; 3
    and #$01        ; 2
    sta $4016       ; 4 - 16 ; bit 4
    pla             ; 4
    lsr             ; 2
    
    sta temp        ; 3
    and #$01        ; 2
    sta $4016       ; 4 - 15 ; bit 5
    lda temp        ; 3
    lsr             ; 2
    nop             ; 2
    
    pha             ; 3
    and #$01        ; 2
    sta $4016       ; 4 - 16 ; bit 6
    pla             ; 4
    lsr             ; 2
    
    sta temp        ; 3
    and #$01        ; 2
    sta $4016       ; 4 - 15 ; bit 7
    lda temp        ; 3
    lsr             ; 2
    nop             ; 2
    
    lda temp        ; 3
    lda #1          ; 2
    sta $4016       ; 4 - 16 ; stop bit
My first game : Twin Dragons available at Broke Studio.
emerson
Posts: 34
Joined: Fri Nov 18, 2016 7:29 am

Re: NES Rumble Controller

Post by emerson »

Memblers wrote:When I was talking to kevtris about controlling a rumble motor like this, he suggested using the latch/OUT0 line.
So sending the control byte via the latch line instead of counting clock cycles? That would keep timing equal regardless of the information being sent. The rs232 isn't a bad idea either but won't work with my current prototype. I feel since the clock and latch lines are both present on the microcontroller that simply bit-banging the control bytes in would be sufficient. I can utilize the rs232 code for some of my other projects so thank you glutock for posting that!
User avatar
Broke Studio
Formerly glutock
Posts: 181
Joined: Sat Aug 15, 2015 3:42 pm
Location: France
Contact:

Re: NES Rumble Controller

Post by Broke Studio »

One advantage of the rs232 solution is that dpcm glitch won't be a problem.
My first game : Twin Dragons available at Broke Studio.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: NES Rumble Controller

Post by lidnariq »

As long as the bitrate you use is slow enough that losing 4 cycles when a DPCM fetch steals the CPU, that'll be fine.

Other self-clocking encodings may work well, too.
emerson
Posts: 34
Joined: Fri Nov 18, 2016 7:29 am

Re: NES Rumble Controller

Post by emerson »

I came up with a new control scheme last night for the rumble controller. Now (in theory) you will be able to do the following:

-independently control the vibration strength each motor in the controller
-read the current vibration status of each motor
-detect that a rumble controller is present

Being able to control the vibration strength was mentioned before and I thought it was a good idea. I also added the ability to control each motor independently for situations like running into either side of the screen or getting shot at from the side. I'm not sure how effective it will be, but it's there. You can read details below on the new control scheme. The diagrams don't line up perfectly but you'll get the idea.

I haven't found a cost effective source for low current motors yet. The ones from precision microdrives will work but are fairly expensive and not much cheaper in bulk, but that might just be what it is.

Code: Select all

;*****This document was written in Notepad++ 

;----------------------------------------------------------------
;WRITING TO THE MOTORS
;
;		Each rumble controller has two motors that can be
;	controlled independently with a four bit value. This value
;	should be sent to the controller MSB first and is read by
;	the controller on the low clock pulse. Writing a non-zero
;	value to the motors will enable them, and writing zero
;	to the motors will turn them off. The value written to
;	the motors correlates to the strength of vibration with
;	'1111' being the strongest vibration. Set the respective
;	controller write flag to initiate a write cycle.
;
;
;
;
;	Vibration strength bit assignment:
;
;			p(#)_motors = 0000 0000
;						  ││││ ││││
;						  ││││ │││└──right motor bit 0
;						  ││││ ││└───right motor bit 1
;						  ││││ │└────right motor bit 2
;						  ││││ └─────right motor bit 3
;						  ││││
;						  │││└───────left motor bit 0
;						  ││└────────left motor bit 1
;						  │└─────────left motor bit 2
;						  └──────────left motor bit 3
;
;
;
;
;	Write flags bit assignment:
;
;			motor_flags = xxxx xx00
;								 ││
;								 │└──write controller 1
;								 └───write controller 2
;
;
;
;
;	Write cycle timing diagram:
;			
;				      0      1      2      3      4      5      6      7      8
;			  ─────┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌────
;			       │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │
;		clock     └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘ 
;			      ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
;			      │ WREN │ M1.3 │ M1.2 │ M1.1 │ M1.0 │ M2.3 │ M2.2 │ M2.1 │ M2.0 │
;		latch ───┘      └──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴───
;			
;			
;
;
;	Controller 1 write example:
;		
;		Cont1Write:
;		;-----check if controller write flag is set-----
;			lda motor_flags	;load motor flags
;			and #$01		;isolate controller 1 write flag
;			beq +			;skip if not set
;		
;		;-----send write latch pulse-----
;			lda #$01		;set latch line high
;			sta $4016		;write to port
;			ldy #$06		;wait for latch pulse to rise	(unnecessary?)
;		-
;			dey
;			bne -
;			lda $4016		;send clock pulse to controller while latch is high to indicate this is a write operation
;			ldx #$08		;set write loop counter
;		
;		;-----write motor data-----
;			lda p1_motors	;load motor values
;			sta motor_write	;and store in scratch variable
;		--	
;			asl motor_write	;shift bit into carry
;			rol a			;roll bit into accumulator
;			and #$01		;mask all other bits
;			sta $4016		;write bit to latch line
;			ldy #$06		;wait for latch pulse to rise	(unnecessary?)
;		-
;			dey
;			bne -
;			lda $4016		;clock the latch bit into controller
;			dex				;decrement loop counter
;			bne --
;		
;		;-----clear latch line-----
;			lda #$00		;return latch to
;			sta $4016		;normal state
;		
;		;-----erase the write flag-----
;			lda motor_flags	;load motor flags
;			and #$fe		;erase controller 1 write flag
;			sta motor_flags	;
;		+
;			rts
;
;
;
;READING CURRENT MOTOR STATUS
;
;		The status of each motor in the rumble controller can
;	be read by clocking in an additional 8 bits after reading
;	the button status of the controller. The motor status
;	byte is sent to the NES with the MSB being the first bit.
;	Reading a non-zero condition correlates to the current
;	vibration strength while reading a zero means the motors 
;	are off. Set the respective controller read flag to 
;	initiate a read cycle.
;
;
;
;
;	Motor status bit assignment:
;
;			motor_read = 0000 0000
;						 ││││ ││││
;						 ││││ │││└──right motor bit 0
;						 ││││ ││└───right motor bit 1
;						 ││││ │└────right motor bit 2
;						 ││││ └─────right motor bit 3
;						 ││││
;						 │││└───────left motor bit 0
;						 ││└────────left motor bit 1
;						 │└─────────left motor bit 2
;						 └──────────left motor bit 3
;
;
;
;
;	Read flags bit assignment:
;
;			motor_flags = xxxx 00xx
;							   ││
;							   │└──read controller 1
;							   └───read controller 2
;
;
;
;
;	Read cycle timing diagram:
;			
;				      0      1      2      3      4      5      6      7      8      9      A      B      C      D      E      F
;			  ─────┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌──┐   ┌────
;			       │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │  │   │
;		clock     └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘  └───┘ 
;			      ┌┐
;			      ││
;		latch ───┘└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
;			      ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
;			      │  A   │  B   │  SE  │  ST  │  U   │  D   │  L   │  R   │ M1.3 │ M1.2 │ M1.1 │ M1.0 │ M2.3 │ M2.2 │ M2.1 │ M2.0 │
;		 data ───┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴───
;
;
;
;	Controller 1 read example:
;
;		Cont1Read:
;		;-----send read latch pulse-----
;			lda #$01		;set latch line high
;			sta $4016		;write to port
;			lda #$00		;clear latch line before first clock pulse
;			sta $4016		;to indicate this is a read operation
;			ldx #$08		;set button read loop counter
;		
;		;-----read controller 1 data-----
;		-
;			lda $4016		;load data from controller port 1
;			lsr a			;shift bit 0 into carry
;			rol cont1		;and store carry in bit 0 of cont1 variable 
;			dex				;decrement x
;			bne -			;repeat 8 times for all buttons
;		
;		;-----check if controller 1 motor read flag is set-----
;			lda motor_flags	;load motor flags
;			and #$04		;isolate controller 1 read flag
;			beq +			;skip if not set
;		
;		;-----read controller 1 motor status-----
;			ldx #$08		;set motor status loop counter
;		-
;			lda $4016		;load data from controller port 1
;			lsr a			;shift bit 0 into carry
;			rol p1_status	;and shift carry in bit 0 of motor status byte
;			dex				;decrement x
;			bne -			;repeat 8 times for both motors
;			
;		;-----erase the read flag-----
;			lda motor_flags	;load motor flags
;			and #$fb		;erase controller 1 read flag
;			sta motor_flags	;
;		+					;skip to here
;			rts
;
;
;
;DETECTING THE RUMBLE CONTROLLER
;
;		On power-up, the rumble controller motor status will
;	read '11011011' even though the motors are off. This enables
;	you to detect whether the controller has rumble or not.
;	The motor status byte will be cleared after the first read.
;	To detect rumble controllers, a read cycle should be initiated
;	after the reset code and before your main game code.
;	The corresponding detect bit in motor_flags will be set 
;	if no rumble controller is detected.
;
;
;
;
;	 Detect flags bit assignment:
;
;			motor_flags = 00xx xxxx
;						  ││
;						  │└──controller 1 rumble detect (0=rumble present   1=no rumble)
;						  └───controller 2 rumble detect (0=rumble present   1=no rumble)	
;
;
;
;
;	Detect sequence example:
;		
;		;-----check if controller 1 is a rumble controller-----
;			lda #$04		;load controller 1 read flag
;			sta motor_flags	;write to call a read cycle
;			jsr ReadCont1	;read data from controller 1
;			lda p1_status	;load detect byte
;			cmp #$db		;compare to rumble present byte
;			beq +			;skip if rumble controller is present
;			lda #$40		;load no rumble flag
;			ora motor_flags	;logical or with current motor_flags
;			sta motor_flags	;write no rumble flag
;		+	
;			lda #$00		;clear status byte
;			sta p1_status
;
;----------------------------------------------------------------
[/size]
Post Reply