This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

hacking Battletoads to read input from the Famicom Exp port

hacking Battletoads to read input from the Famicom Exp port
by on (#208504)
Hi,

How can we make Battletoads read input from the Famicom expansion port, for player 1 and player 2? Neither P1 or P2 work, only the hard-wired controllers.

Surprisingly battletoads Japan doesn't work with the exp port either.

I can test hacks on my Everdrive using my Famicom Hori arcade stick that can switch between Player 1/Player 2.

Player 2 mode works on Mario Bros 1 and Mario Bros 3, but did not work on Battletoads or Mario Bros 2 (player 1 does not work for either of these as well).

Here is all I could pull from the USA game. This is the routine that handles reading the controls I think? I got this when setting a breakpoint for #$40 (B Button) on $0029. When I was monitoring the RAM, with each button press I was seeing $0029 get changed.

I didn't really understand much. Before this routine there is an AND #$40 which seems to impact the controls if changed to #$80 or #$10, etc.

Code:
00:8D78:A2 01        LDX #$01
 00:8D7A:8E 16 40  STX $4016 = #$FF
 00:8D7D:CA           DEX
 00:8D7E:8E 16 40  STX $4016 = #$FF
 00:8D81:A2 08       LDX #$08
 00:8D83:AD 16 40  LDA $4016 = #$FF
 00:8D86:6A          ROR
 00:8D87:26 15     ROL $0015 = #$00
 00:8D89:AD 17 40  LDA $4017 = #$FF
 00:8D8C:6A        ROR
 00:8D8D:26 16     ROL $0016 = #$00
 00:8D8F:CA        DEX
 00:8D90:D0 F1     BNE $8D83
 00:8D92:A5 15     LDA $0015 = #$00
 00:8D94:AA        TAX
 00:8D95:45 29     EOR $0029 = #$40
>00:8D97:86 29     STX $0029 = #$40
 00:8D99:25 15     AND $0015 = #$00
 00:8D9B:85 2B     STA $002B = #$40
 00:8D9D:A5 16     LDA $0016 = #$00
 00:8D9F:AA        TAX
 00:8DA0:45 2A     EOR $002A = #$00
 00:8DA2:86 2A     STX $002A = #$00
 00:8DA4:25 16     AND $0016 = #$00
 00:8DA6:85 2C     STA $002C = #$00
 00:8DA8:60        RTS -----------------------------------------
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208507)
Each ROR instruction at $8D86 and $8D8C needs to be replaced with these two instructions
Code:
AND #$03  ; A = 1 for hardwired, 2 for expansion, 0 for neither
CMP #$01  ; C = 1 iff this button on either controller is pressed


Unfortunately, each of these adds 3 bytes to the routine for a total of 6 bytes. My first recommendation would be to eliminate X using a ring counter, but that'd only save one byte. But I can reclaim a few more bytes by turning the next part, a calculation of which buttons on each controller are newly pressed this frame, into a loop:
Code:
P1 = $4016
P2 = $4017
cur_keys  = $15
last_keys = $29
new_keys  = $2B

.proc read_pads
  ldx #$01
  stx P1
  lda #$00        ; Use A for this because X=1 is needed later
  sta P1
  stx cur_keys+1  ; Once the 1 gets shifted into carry we're done
padbitloop:
  lda P1
  and #$03        ; A = 1 for hardwired, 2 for expansion, 0 for neither
  cmp #$01        ; C = 1 for some press, 0 for none
  rol cur_keys+0
  lda P2
  and #$03
  cmp #$01
  rol cur_keys+1  ; On eighth ROL, carry becomes set
  ; no DEX needed because cur_keys is our loop counter
  bcc padbitloop

  ; Because the above expanded by 6 bytes, compensate by eliminating
  ; the unrolling of last_keys and new_keys calculation
  ; (from 22 bytes to 15)
  ; X is still 1 from the above
fill_new_keys:
  lda last_keys,x  ; A = keys held last frame
  eor #$FF       ; A = keys NOT held last frame
  and cur_keys,x   ; A = keys held this frame but not last frame
  sta new_keys,x
  lda cur_keys,x
  sta last_keys,x
  dex
  bpl fill_new_keys

  ; Gained enough for it to fit
  rts
.endproc

I hope this isn't called in any cycle-timed portions of the game, as it probably won't take the same number of cycles.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208508)
tepples wrote:
Each ROR instruction at $8D86 and $8D8C needs to be replaced with these two instructions
Code:
AND #$03  ; A = 1 for hardwired, 2 for expansion, 0 for neither
CMP #$01  ; C = 1 iff this button on either controller is pressed


Unfortunately, each of these adds 3 bytes to the routine for a total of 6 bytes. My first recommendation would be to eliminate X using a ring counter, but that'd only save one byte. But I can reclaim a few more bytes by turning the next part, a calculation of which buttons on each controller are newly pressed this frame, into a loop:
Code:
P1 = $4016
P2 = $4017
cur_keys  = $15
last_keys = $29
new_keys  = $2B

.proc read_pads
  ldx #$01
  stx P1
  lda #$00        ; Use A for this because X=1 is needed later
  sta P1
  stx cur_keys+1  ; Once the 1 gets shifted into carry we're done
padbitloop:
  lda P1
  and #$03        ; A = 1 for hardwired, 2 for expansion, 0 for neither
  cmp #$01        ; C = 1 for some press, 0 for none
  rol cur_keys+0
  lda P2
  and #$03
  cmp #$01
  rol cur_keys+1  ; On eighth ROL, carry becomes set
  ; no DEX needed because cur_keys is our loop counter
  bcc padbitloop

  ; Because the above expanded by 6 bytes, compensate by eliminating
  ; the unrolling of last_keys and new_keys calculation
  ; (from 22 bytes to 15)
  ; X is still 1 from the above
fill_new_keys:
  lda last_keys,x
  eor #$FF
  and cur_keys,x
  sta new_keys,x
  lda cur_keys,x
  sta last_keys,x
  dex
  bpl fill_new_keys

  ; Gained enough for it to fit
  rts
.endproc


Can we JSR to unused space (stream of FFs maybe?) and replace some of the code so that we can fit those 3 extra bytes?

$9470 has a lot of FF entries it looks like.

Maybe JSR $9470 where ROR occurs which would also replace the ROL $0015 (26, 15).

Then when we jump, we do

AND #$03
CMP #$01
ROL $0015
RTS
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208510)
Long strings of $00 or $FF aren't always unused space, they may very well be the high byte portion of look-up tables containing lots of values between -256 and -1, or 0 and 255. Always use a code/data logger to be sure of what's actually unused.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208511)
JSR/RTS would add even more cycles than the size optimization that I did find (turning $2B/$2C computation into a loop). Try it; it should fit.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208512)
ReverendSA wrote:

Can we JSR to unused space (stream of FFs maybe?) and replace some of the code so that we can fit those 3 extra bytes?

$9470 has a lot of FF entries it looks like.

Maybe JSR $9470 where ROR occurs which would also replace the ROL $0015 (26, 15).

Then when we jump, we do

AND #$03
CMP #$01
ROL $0015
RTS


Here is my proposed modified routine (Psuedo):

Code:
 
LDX #$01
STX $4016
DEX
STX $4016
LDX #$08
LDA $4016
JSR $9470 <--- Changed
LDA $4017
JSR $9480 <--- Changed
DEX
BNE $8D83
LDA $0015
TAX
EOR $0029
STX $0029
AND $0015
STA $002B
LDA $0016
TAX
EOR $002A
STX $002A
AND $0016
STA $002C
RTS


At $9470

Code:
AND #$03
CMP #$01
ROL $0015
RTS


At $9480
Code:
AND #$03
CMP #$01
ROL $0016
RTS
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208513)
Let me restate what I wrote one more time:

You probably will not need to move things out to $9470 and $9480. Though I made the first part of the routine longer, I made the second part shorter to compensate.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208514)
tepples wrote:
Let me restate what I wrote one more time:

You probably will not need to move things out to $9470 and $9480. Though I made the first part of the routine longer, I made the second part shorter to compensate.


Here is what I have based on your post, to replace the entire routine.

But for BCC and BPL, I do not understand. Your code said BCC padbitloop, but for BCC all I could see was $90. I don't know if it takes arguments, or works automatically.

A2 01
8E 16 40
A9 00
8D 16 40
86 16
AD 16 40
29 03
C9 01
26 15
AD 17 40
29 03
C9 01
26 16
90
B5 29
49 FF
35 15
95 15
B5 15
95 29
CA
10
60


Edit:

Okay, I think the byte next to Branches is FF - X, X being whichever byte you put in there. I revised the above:

A2 01
8E 16 40
A9 00
8D 16 40
86 16
AD 16 40
29 03
C9 01
26 15
AD 17 40
29 03
C9 01
26 16
90 EC
B5 29
49 FF
35 15
95 15
B5 15
95 29
CA
10 F1
60

90 E7 should take us back to AD 16 40 (lda P1; padbitloop).

10 EC should take us back 13 bytes, to B5 29 (lda last_keys,x; fillnewkeys).
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208522)
tepples wrote:
Let me restate what I wrote one more time:

You probably will not need to move things out to $9470 and $9480. Though I made the first part of the routine longer, I made the second part shorter to compensate.


For some reason though, pushing the keys does nothing at all. The game is still running though.

I've implemented it as you wrote it (I hope). Here is what FCEUX shows:

00:8D78:A2 01 LDX #$01
00:8D7A:8E 16 40 STX $4016 = #$FF
00:8D7D:A9 00 LDA #$00
00:8D7F:8D 16 40 STA $4016 = #$FF
00:8D82:86 16 STX $0016 = #$FF
00:8D84:AD 16 40 LDA $4016 = #$FF
00:8D87:29 03 AND #$03
00:8D89:C9 01 CMP #$01
00:8D8B:26 15 ROL $0015 = #$00
00:8D8D:AD 17 40 LDA $4017 = #$FF
00:8D90:29 03 AND #$03
00:8D92:C9 01 CMP #$01
00:8D94:26 16 ROL $0016 = #$FF
00:8D96:90 EC BCC $8D84
00:8D98:B5 29 LDA $29,X @ $002B = #$00
00:8D9A:49 FF EOR #$FF
00:8D9C:35 15 AND $15,X @ $0017 = #$55
00:8D9E:95 15 STA $15,X @ $0017 = #$55
00:8DA0:B5 15 LDA $15,X @ $0017 = #$55
00:8DA2:95 29 STA $29,X @ $002B = #$00
00:8DA4:CA DEX
00:8DA5:10 F1 BPL $8D98
00:8DA7:60 RTS ---------------------------------------
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208523)
ReverendSA wrote:
tepples wrote:
Let me restate what I wrote one more time:

You probably will not need to move things out to $9470 and $9480. Though I made the first part of the routine longer, I made the second part shorter to compensate.


For some reason though, pushing the keys does nothing at all. The game is still running though.

I've implemented it as you wrote it (I hope). Here is what FCEUX shows:

00:8D78:A2 01 LDX #$01
00:8D7A:8E 16 40 STX $4016 = #$FF
00:8D7D:A9 00 LDA #$00
00:8D7F:8D 16 40 STA $4016 = #$FF
00:8D82:86 16 STX $0016 = #$FF
00:8D84:AD 16 40 LDA $4016 = #$FF
00:8D87:29 03 AND #$03
00:8D89:C9 01 CMP #$01
00:8D8B:26 15 ROL $0015 = #$00
00:8D8D:AD 17 40 LDA $4017 = #$FF
00:8D90:29 03 AND #$03
00:8D92:C9 01 CMP #$01
00:8D94:26 16 ROL $0016 = #$FF
00:8D96:90 EC BCC $8D84
00:8D98:B5 29 LDA $29,X @ $002B = #$00
00:8D9A:49 FF EOR #$FF
00:8D9C:35 15 AND $15,X @ $0017 = #$55
00:8D9E:95 15 STA $15,X @ $0017 = #$55
00:8DA0:B5 15 LDA $15,X @ $0017 = #$55
00:8DA2:95 29 STA $29,X @ $002B = #$00
00:8DA4:CA DEX
00:8DA5:10 F1 BPL $8D98
00:8DA7:60 RTS ---------------------------------------



Oh, my bad!

I was storing storing #$15 at 8D9E when I should have stored #$2B according to your code.

I changed it to #$2B and now my character can jump and punch!! I'm going to test this on my ED and report back.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208524)
Just in case it helps, in FCEUX's debugger if you left click in the grey column just to the left of the disassembly, you can use its built-in assembler to type a patch in assembly instead of trying to convert everything to hex.

There are no labels in this assembler, but you type the target address for branches, e.g. "BCC $8D96" will create a branch instruction with the correct relative value to get to $8D96.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208525)
Okay.

It works, holy crap.

Even more surprising, the hack allows player 2 to join via pushing start. I thought start was not applicable for player 2 on Famicom.

That's insane.

Thanks a lot tepples.

My friend and I have been meaning to play Battletoads USA on my famicom since forever and now we can do it.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208526)
rainwarrior wrote:
Just in case it helps, in FCEUX's debugger if you left click in the grey column just to the left of the disassembly, you can use its built-in assembler to type a patch in assembly instead of trying to convert everything to hex.

There are no labels in this assembler, but you type the target address for branches, e.g. "BCC $8D96" will create a branch instruction with the correct relative value to get to $8D96.


Thanks for that info. Would have saved me an extra hour when I was trying to learn branches. The labels were confusing me for a bit.

I'm going to publish this hack as it may very well help others such as myself. I will credit you for your help Tepples.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208527)
ReverendSA wrote:
Okay.
Even more surprising, the hack allows player 2 to join via pushing start. I thought start was not applicable for player 2 on Famicom.

I think it's possible that all the codes for P2 joining in with START are intact, just that the hardwired P2 pad does not have a physical START button so that you can never use this feature, so if you hack the game to recognise inputs from expansion port the START button on the extra P2 controller will work as intended. It may even be possible that if you modify the console by changing the hardwired P2 pad to some other controllers the START button may work too.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208549)
Gilbert wrote:
ReverendSA wrote:
Okay.
Even more surprising, the hack allows player 2 to join via pushing start. I thought start was not applicable for player 2 on Famicom.

I think it's possible that all the codes for P2 joining in with START are intact, just that the hardwired P2 pad does not have a physical START button so that you can never use this feature, so if you hack the game to recognise inputs from expansion port the START button on the extra P2 controller will work as intended. It may even be possible that if you modify the console by changing the hardwired P2 pad to some other controllers the START button may work too.

It would also work properly on an AV Famicom, which uses standard 7-pin NES controllers (which both have Select/Start buttons) instead of the hardwired ones.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208558)
Gilbert wrote:
it may even be possible that if you modify the console by changing the hardwired P2 pad to some other controllers the START button may work too.
Yeah probably it's just the shift register that spits out zeroes instead of the START and SELECT data in the hardwired controller II when reading it. If you somehow connect a controller I (or a NES controller) on the PCB connector START and SELECT will probably work like on an AV Famicom or NES. It would be interesting to add a microphone to a controller I and a PCB connector from a controller II to get a Famicom that has both a microphone and START/SELECT buttons.
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208561)
Yes the Famicom controller 2 has the same 4021 8-bit shift register as the others, it just has the pins for SELECT and START hard wired to 5V. You could relatively easily reconnect those to two new buttons.

I don't know what you mean about "add a microphone to controller 1", though. There's only 1 data line that's ever listened to for the mic (D2 on $4016), so having a second microphone won't be able to do anything. You'd also have to replace its cord with one that has more wires to accomodate the extra line. (The mic is a dedicated line, it's not part of the serial input from D0.)
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208566)
I don't mean add another microphone, the goal is to add START and SELECT buttons on the second controller while keeping the microphone.
I mean to use a spare controller I or NES controller and add a microphone to it and connect it instead of controller II, not instead of controller I. I figured it would be easier to add a microphone to a controller than to add good START and SELECT buttons to a controller II. You would need to steal the connector from a controller II in that case though (a controller I connector won't fit).
Re: hacking Battletoads to read input from the Famicom Exp p
by on (#208567)
Ah, yes that makes sense.