Controller Input Codes

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
Roth
Posts: 400
Joined: Wed Aug 03, 2005 3:15 pm
Contact:

Controller Input Codes

Post by Roth »

Hey all! I sat down to try and figure out a basic way to do some controller codes that people could use to do things like the Konami code. I thought I'd share what I ended up using, and ask how some of you might've or have handled it. This is what I came up with:

EDIT: Here's a ROM with it working on the title screen.

Code: Select all

code_count:	 .res 1		; Number of frames between each button press	
code_offset:	.res 1		; Which byte to check in the code_check table

code_check:
	.byte up_punch, up_punch, down_punch, down_punch
	.byte left_punch, right_punch, left_punch, right_punch
	.byte b_punch, a_punch

test_code:
	ldx code_offset			; Get the offset ready
	lda control_pad			; Check buttons for only one
	eor control_old			;  button press at a time
	and control_pad			;
	and code_check, x		 ; Check the button press against
	beq :+						;  the code_check table
		lda #$10				 ; When buttons match, reset the
		sta code_count		 ;  code count and increase the
		lda code_offset		;  offset by one.
		clc						;
		adc #$01				 ;
		sta code_offset		;
		cmp #$0a				 ; Compare accumulator to max
		bne :+					;  offset in code_check table
			jsr PPU_off		 ; Do successful code input stuff
									;  here. Be sure to jump over
									;  everything else to the end
									;  of the routine when you
									;  actually implement this.
:	lda code_count			; If code_count is NOT zero...
	 beq :+					  ;
		sec					   ;  ... then subtract it by one
		sbc #$01				 ;  and return.
		sta code_count		 ;
		rts					   ;
:	lda #$00				   ; If code_count was zero then
	 sta code_offset		  ;  set code_offset back to zero,
	 rts						  ;  which means the code input
								   ;  failed, and must start from
								   ;  the beginning.
[/url]
Last edited by Roth on Sat Mar 17, 2012 2:41 pm, edited 1 time in total.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

Not that it really matters too terribly much, but this:

Code: Select all

      lda code_offset
      clc                  ;
      adc #$01             ;
      sta code_offset      ;
      cmp #$0a             ; Compare accumulator to max
      bne :+    
Can be this:

Code: Select all

      inx
      stx code_offset      ;
      cpx #$0a             ; Compare accumulator to max
      bne :+    
I haven't tried the actual code, but from the look of it if you pressed every button at once it would always advance the counter.

Code: Select all

test_code:
   ldx code_offset         ; Get the offset ready
   lda control_pad         ; All buttons pressed #%11111111
   eor control_old         ; No buttons were pressed last frames #%00000000
   and control_pad         ;#%11111111
   and code_check, x       ; Check the button press against 
   beq :+
That last and should probably be a cmp.

Even then the code input sort of fails. I just have to hammer buttons while the code input counter is above zero, and as long I press the right one at some point, it works, order be damned.

Could be wrong here since I haven't tested it, though. That's just how I understand it's working.

Instead of using a timer, I'd wait for no buttons to be pressed before advancing to the next input. It seems you're trying to support multiple inputs (like up+A as a single part of the code) which is noble. In that case, I'd keep the timer for the case where the input partially matches, but count down for a much shorter time period (like 4 frames.) If the full input doesn't match by then, the code resets.

Edit: Though... I guess that has a similar problem. As long as one of the possible buttons is held down, you can hammer others and the code doesn't reset. I may edit this later with a 6502 try of my own.
Roth
Posts: 400
Joined: Wed Aug 03, 2005 3:15 pm
Contact:

Post by Roth »

The first post has a ROM included now if anyone would like to check it out.

EDIT: Oops! Forgot to reply to the inx thing you pointed out. Yeah, I think I had been doing something else when writing the code at first, and never thought to mess with that after it had started working.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

I just TAS'd the rom you posted, and pressing all the buttons every other frame DOES make the code work. So does pressing random buttons in the middle of the code. The only sequence I tried was up, down, up, b, down, down, start, left, right, left, A, right, B, A, but any buttons can probably be pressed in between while the timer is counting down.

Here's my admittedly crazy (and untested) solution. It allows for multiple button inputs (even all eight at once), and fails if anything that's not part of an input is pressed at any time. It will also wait as long as possible for you to press the rest of the buttons in a multibutton input which I think is nicer than a timer.

Apologies in advance if it's hard to read. My coding style is basically garbage. It's also not super optimized, but it may work even though it's untested. Yell at me if it doesn't work, I would like to fix it if it doesn't.

I may be able to do something less hardcore if multiple button inputs aren't needed. I realized after the post you didn't care about multiple buttons at all. I just sorta read (right_punch) as something like right+A, when you really just had the Konami code verbatim.

Edit: Doesn't work! :lol: Figuring it out now.
Edit 2: It plays at least somewhat nice now. Further stress testing now.

Code: Select all

;NoButtons = Contains whether we are currently waiting for no buttons to be checked
;Non Zero means we are.
;IntNoButtons = Contains whether the first check is waiting for no buttons to be checked
;Non Zero means it is
;code_offset = code_offset
;InputStarted = Used to check for no buttons being pressed after starting a complex input.
;Nonzero means complex input was started

up_punch = %00001000
down_punch = %00000100
left_punch = %00000010
right_punch = %00000001
a_punch = %10000000
b_punch = %01000000

code_check:
   .byte up_punch, up_punch, down_punch, down_punch
   .byte left_punch, right_punch, left_punch, right_punch
   .byte b_punch, a_punch
;code_check:
   .byte up_punch | a_punch, down_punch | b_punch, left_punch, right_punch
   .byte down_punch | left_punch, down_punch | right_punch, b_punch, a_punch
   .byte up_punch, right_punch
test_code:
	ldx code_offset;Because when code_offset is 0, old inputs may still be pressed from when the code was reset
	beq nobuttonstartcheck;We go to an additional check
truestart:
	lda NoButtons;If we're waiting for no buttons to be pressed
	bne ComplexCheck;Branch
	lda buttonsheld
	beq complexinputstartcheck
complexinputreentry:
	cmp code_check,x;If the current input matches
	beq SetNoButtons;The input we want perfectly, we are now waiting for no buttons to be pressed to advance the offset
	;If not...
	lda code_check,x;We now reverse the buttons the current input requires
	eor #$FF;We do this, so we can check if any of THOSE buttons we don't want are pressed
	and buttonsheld;And reset code_offset if so
	
	bne resetcode;If any are pressed, we reset the code
	;Else, we go through the loop on the next frame waiting for a perfect match to our input
	;And set InputStarted so if the player releases all of the current "good" buttons
	;the code will be reset
	lda buttonsheld
	beq code_rts
	lda #$FF
	sta <InputStarted
	
	rts
complexinputstartcheck:
	ldy InputStarted
	beq complexinputreentry
	bne resetcode
	
nobuttonstartcheck:
	lda IntNoButtons;If this variable is 0, 
	beq truestart;we're safe to start the code as normal
	lda #$00
	sta NoButtons
	sta InputStarted
	lda buttonsheld;Otherwise we RTS if the current input is non zero
	bne code_rts
	sta IntNoButtons;or store zero in this variable so the code can safely continue on the next frame
code_rts:
	rts
	
SetNoButtons:
	lda #$FF
	sta NoButtons
	
	lda #$00
	sta InputStarted
	
	rts
	
ComplexCheck:
	lda code_check,x
	eor #$FF
	sta temp
	
	lda buttonsheld
	beq AdvanceCode;If there are no buttons held, we can move the next input
	and temp;If a button that was NOT part of our input is pressed
	bne resetcode;While other buttons related to the current input are, we reset the code.
	
	rts;If neither of those things are true, we're still waiting for the buttons to be released
	
AdvanceCode:
	inx
	cpx #$0A
	beq codesucceeded
	stx code_offset
	lda #$00
	sta NoButtons
	sta InputStarted
	
	rts
	
resetcode:
	lda #$00
	sta NoButtons
	sta code_offset
	sta InputStarted
	
	lda #$FF
	sta IntNoButtons
	
	rts
	
codesucceeded:
;Do code succeeded things
	
	jmp resetcode
	rts
Konami Test Rom: http://www.mediafire.com/?4y232b65qmr3071
Multi Button Test Rom: http://www.mediafire.com/download.php?v891d08t1va5da7

Multibutton's code is: up+A, down+B, left, right, down+left, (remember you gotta release all buttons before next input) down+right, B, A, Up, Right

The background turns blue on success.

I definitely should have chosen a code that didn't have something like the down+left, down+right sequence. Still pretty proud of how it works with wrong button inputs even when the current input requires multiple buttons. Roth's is better for rapid fire entry, though. Mine is too strict to enter the code quickly.
Last edited by Kasumi on Sat Mar 17, 2012 5:20 pm, edited 4 times in total.
User avatar
MottZilla
Posts: 2835
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla »

Similar to codes like this, games like Street Fighter and Mortal Kombat will keep a queue of player input for performing special moves. So it has other uses than adding secret codes. Personally I find long button sequence codes to be rather annoying. There are better ways to hide cheats or debug functions.
Roth
Posts: 400
Joined: Wed Aug 03, 2005 3:15 pm
Contact:

Post by Roth »

Kasumi wrote:I just TAS'd the rom you posted, and pressing all the buttons every other frame DOES make the code work. So does pressing random buttons in the middle of the code. The only sequence I tried was up, down, up, b, down, down, start, left, right, left, A, right, B, A, but any buttons can probably be pressed in between while the timer is counting down.
Testing a basic code like this against TASing isn't really a concern of mine. The counter is fast enough that with normal player input on an NES it pans out just right, I think.

I don't have time right now to look over the code you posted, but I will be sure to check it out later. Thanks for the critique and the response!
User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Post by cpow »

MottZilla wrote:Personally I find long button sequence codes to be rather annoying. There are better ways to hide cheats or debug functions.
Amen to this. I still get a chuckle remembering my friend with the notebook full of moves for the MK series. "Hold on, I've got that fatality somewhere...<flip,flip,flip>YOU LOSE."
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

Mortal Kombat also has a STUPIDLY short window for entering those fatalities.

Re: My CRAZY input code. It sorta works now (Version one did not), but it still has bugs. It's also not great for rapid fire entry. I'mma keep fixing it up, though. It's a fun exercise.
3gengames
Formerly 65024U
Posts: 2281
Joined: Sat Mar 27, 2010 12:57 pm

Post by 3gengames »

Here's what I did for my game:

Code: Select all

In RAM:
EasterEgggSteps: .rs 1 ;1Byte RAM for Easter Egg current button check.
Data:
EasterEggButtonCombo: (Button values array here, one button at a time)

Program:
  LDA #$10
  BIT Settings ;Check to see if Easter Egg is already set
  BNE .AfterEasterEggCheck ;It was, skip everything.
  LDA ControllerData0NewlyPressed ;Get buttons newly pressed.
  BEQ .AfterEasterEggCheck ;No new button pressed, skip rest.
  LDX EasterEggSteps ;Current buttton check.
  CMP EasterEggButtonCombo,X Check array.
  BNE .ClearEasterEggSoFar ;Clear button check value, wrong key.
  INC EasterEggSteps ;Next button check next time.
  CPX #$(NumberOfButtonPressesNeeded) ;Check to see if last press done.
  BNE .AfterEasterEggCheck ;Nope, not yet.
  LDA Settings ;Settings byte for my game engine, this just sets a flag for my main program to do stuff with.
  ORA #$10
  STA Settings ;Store easter egg hit flag for my engine.
.ClearEasterEggSoFar:
  LDA #$00
  STA EasterEggSteps ;Clear steps, wrong code or easter egg activated.
.AfterEasterEggCheck:
  Code after check is done.
Post Reply