Getting around the DPCM bug in a ROM hack

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

User avatar
za909
Posts: 224
Joined: Fri Jan 24, 2014 9:05 am
Location: Hungary

Getting around the DPCM bug in a ROM hack

Post by za909 » Sat Jan 25, 2014 7:14 am

Hey people! I'm fairly new to 6502 programming in a sense, that I've never actually used proper tools to make anything, hopefully that'll change!
I've been into ROM hacking for quite a while though, and the next step requires me to be here pretty much. I want to modifiy the sound engine made by Capcom, which is used in a ton of their MMC3 games, to include DPCM sample playback. That's not difficult by any means, as I can simply replace one of the less useful effect handlers with my DPCM subroutine. The things is, that this would very likely introduce the glitched controller reads as an artifact.

Since I have a working ROM here by default, I don't want to go all the way of replacing the entire routine for the controller. Can I do something about this and include it in the code for the sample playback?

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

Re: Getting around the DPCM bug in a ROM hack

Post by tepples » Sat Jan 25, 2014 8:12 am

The best way to go about it would involve replacing the routine that actually reads 8 bits from the controller. This wouldn't affect the code that interprets the value read from the controller, which is what I think you're worried about. The best way to hack this in without causing unpredictable slowdown is to read the controller twice, compare the values, and use the values read in the previous frame if they don't match.

Do you have a disassembled listing of the existing controller reading routine, starting where it writes to $4016?

User avatar
za909
Posts: 224
Joined: Fri Jan 24, 2014 9:05 am
Location: Hungary

Re: Getting around the DPCM bug in a ROM hack

Post by za909 » Sat Jan 25, 2014 8:48 am

tepples wrote:Do you have a disassembled listing of the existing controller reading routine, starting where it writes to $4016?
Yeah I have a "complete" but mostly undocumented disassembly for Megaman 3 that I found. I hope this is the right thing. I just simply located where it writes to $4016 (and reads from afterwards) and apparently, this is supposed to be it, in bank $1E.
It also seems to be reading the second controller, but I could care less about fixing the input from there.

Code: Select all

L1E_C545:
	ldx #$01
	stx $4016
	dex
	stx $4016
	ldx #$08
L1E_C550:
	lda $4016
	lsr
	rol $14
	lsr
	rol $00
	lda $4017
	lsr
	rol $15
	lsr
	rol $01
	dex
	bne L1E_C550
	lda $00
	ora $14
	sta $14
	lda $01
	ora $15
	sta $15
	ldx #$01
L1E_C573:
	lda $14,x
	tay
	eor $16,x
	and $14,x
	sta $14,x
	sty $16,x
	dex
	bpl L1E_C573
	ldx #$03
L1E_C583:
	lda $14,x
	and #$0c
	cmp #$0c
	beq L1E_C593
	lda $14,x
	and #$03
	cmp #$03
	bne L1E_C599
L1E_C593:
	lda $14,x
	and #$f0
	sta $14,x
L1E_C599:
	dex
	bpl L1E_C583
	rts
Last edited by za909 on Sat Jan 25, 2014 9:27 am, edited 1 time in total.

User avatar
Jarhmander
Formerly ~J-@D!~
Posts: 516
Joined: Sun Mar 12, 2006 12:36 am
Location: Rive nord de Montréal

Re: Getting around the DPCM bug in a ROM hack

Post by Jarhmander » Sat Jan 25, 2014 9:17 am

za909 wrote:It also seems to be reading the second controller, but I could care less about fixing the input from there.
It does read from controller #2, this is likely some test code left by the developers. It is possible to do mega jumps if one hold a certain button on the controller #2.
You could simply eliminate the code that reads the second controller and act as if no button is ever pressed.
((λ (x) (x x)) (λ (x) (x x)))

User avatar
rainwarrior
Posts: 7979
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Getting around the DPCM bug in a ROM hack

Post by rainwarrior » Sat Jan 25, 2014 9:33 am

I'm kinda wondering, modifying the controller code and adding some DPCM playback commands to the sound engine doesn't sound too tough at all, but how are you going to find space for always-resident DPCM samples? For everything but DPCM you can find space in any bank and swap it in only when needed, but DPCM is always playing! Is this for a romhack, or are you trying to transplant the music engine into your own homebrew? (I'm presuming a romhack since you're asking about modifying the controller routines.) MMC3 does have a banking scheme that suits DPCM well, but if there's vital code running in those banking regions it may be difficult to rearrange.

User avatar
za909
Posts: 224
Joined: Fri Jan 24, 2014 9:05 am
Location: Hungary

Re: Getting around the DPCM bug in a ROM hack

Post by za909 » Sat Jan 25, 2014 10:04 am

This would be for a romhack basically.

It's quite simple. Bank $1F is always loaded to $E000-$FFFF and never once swapped. There's enough unused junk data here for me to replace, although most of the are in very inconvenient spots, as I can only read from every 64th byte.
There's enough space for a 64 byte sample, a 32 byte sample, and two 289 byte samples (I want these to be really short bass notes, a C and a C# note, so I can cover two octaves with the low pitches, except the upper E and B would be missing)
The game Bee 52 by Codemasters had a very similar approach. I found that slap bass works alright even at such a low quality.

But I could ultimately use drum samples and never worry about the pitch.

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

Re: Getting around the DPCM bug in a ROM hack

Post by tepples » Sat Jan 25, 2014 10:24 am

First I did a couple microoptimizations for space, using the ring counter trick to kill use of X and the CMP trick to read both hardwired controllers (bit 0 of $4016 and $4017) and expansion controllers without needing $00 and $14. Then I used the extra space to add logic that reads the controller twice and uses the previous frame's keypresses if they don't match. I haven't bothered to count the bytes from both versions nor to test it, and I'm open to corrections.

Code: Select all

cur_keys = $16
new_keys = $14
tmp_keys = $00

read_pads_once:    ; Corresponds to L1E_C545
   lda #$01
   sta $4016       ; Load button states into controller's shift register
   sta tmp_keys+1  ; End the loop once the 1 gets shifted up to carry
   lsr a
   sta $4016       ; Finish loading button states
bitloop:
   lda $4016       ; read the controller
   and #$03        ; remove open bus bits
   cmp #$01        ; C = true if either button is pressed
   rol tmp_keys+0
   lda $4017
   and #$03
   cmp #$01
   rol tmp_keys+1  ; C = true if this is the last button
   bcc bitloop     ; look ma, no X!
   rts

read_pads:
   jsr read_pads_once
   lda tmp_keys+0
   sta new_keys+0
   lda tmp_keys+1
   sta new_keys+1
   jsr read_pads_once
   ldx #1
fixloop:
   ; Sanity check 1: Make sure the two reads match
   ; (DPCM double clock compensation)
   ldy cur_keys,x
   lda tmp_keys,x
   cmp new_keys,x
   beq frame_not_glitched
   lda cur_keys,x  ; If glitched, use the previous frame
frame_was_glitched:
   ; At this point, A holds the current buttons and Y holds
   ; the previous buttons

   sta cur_keys,x

   ; Sanity check 2: Left+Right or Up+Down cancel each other out.
   ; Corresponds to L1E_C583
   and #$0C
   cmp #$0C
   bne not_updown
   eor cur_keys,x
   sta cur_keys,x
not_updown:
   lda cur_keys,x
   and #$03
   cmp #$03
   bne not_leftright
   eor cur_keys,x
   sta cur_keys,x
not_leftright:

   ; Calculate newly pressed buttons
   ; Corresponds to L1E_C573
   tya
   eor #$FF
   and cur_keys,x
   sta new_keys,x
   dex
   bpl fixloop
   rts

User avatar
Bregalad
Posts: 8008
Joined: Fri Nov 12, 2004 2:49 pm
Location: Chexbres, VD, Switzerland

Re: Getting around the DPCM bug in a ROM hack

Post by Bregalad » Sat Jan 25, 2014 10:37 am

The best way to hack this in without causing unpredictable slowdown is to read the controller twice, compare the values, and use the values read in the previous frame if they don't match.
I'm pretty sure most commercial games does it by reading twice, and repeat until you get two consecutive reads that match. In theory it could create major slowdowns, but in practice it doesn't.

User avatar
za909
Posts: 224
Joined: Fri Jan 24, 2014 9:05 am
Location: Hungary

Re: Getting around the DPCM bug in a ROM hack

Post by za909 » Sat Jan 25, 2014 1:32 pm

Alright guys, sorry for wasting everyone's time. This is clearly not for me. I've spent the last 3 hours fiddling around and nothing worked.
I don't have proper assembly tools, nothing's working on my computer, I can't translate any code to hex properly, and I wish I could punch someone right now. I wouldn't have time to keep doing this hacking stuff anyway. Sorry again.

User avatar
Bregalad
Posts: 8008
Joined: Fri Nov 12, 2004 2:49 pm
Location: Chexbres, VD, Switzerland

Re: Getting around the DPCM bug in a ROM hack

Post by Bregalad » Sat Jan 25, 2014 1:36 pm

Har come on don't give up romhacking because of this !
You should have started by something simpler, like just palette or graphics hack.

unregistered
Posts: 1112
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: Getting around the DPCM bug in a ROM hack

Post by unregistered » Sat Jan 25, 2014 3:55 pm

za909 wrote:Alright guys, sorry for wasting everyone's time. This is clearly not for me. I've spent the last 3 hours fiddling around and nothing worked.
I don't have proper assembly tools, nothing's working on my computer, I can't translate any code to hex properly, and I wish I could punch someone right now. I wouldn't have time to keep doing this hacking stuff anyway. Sorry again.
When I'm stumped I pray. : )

User avatar
tokumaru
Posts: 11944
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Getting around the DPCM bug in a ROM hack

Post by tokumaru » Sat Jan 25, 2014 7:52 pm

unregistered wrote:When I'm stumped I pray. : )
I'm not a religious person, but if I were I wouldn't bother God with my NES ROM Hacking problems while so much bad stuff is happening all around the world!

User avatar
thefox
Posts: 3141
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: Getting around the DPCM bug in a ROM hack

Post by thefox » Sat Jan 25, 2014 10:31 pm

tokumaru wrote:
unregistered wrote:When I'm stumped I pray. : )
I'm not a religious person, but if I were I wouldn't bother God with my NES ROM Hacking problems while so much bad stuff is happening all around the world!
God is omnipotent though, so he wouldn't care.

(P.S. I'm not religious either.)
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi

User avatar
za909
Posts: 224
Joined: Fri Jan 24, 2014 9:05 am
Location: Hungary

Re: Getting around the DPCM bug in a ROM hack

Post by za909 » Sun Jan 26, 2014 1:12 am

The problem isn't that I have no idea what I'm doing. I've been hacking for what...4 years now? The problem is that when I wanted to actually practice 6502 asm, I could never find any assembler that worked for me. They're either all DOS or x32 based, so I can't really run them at all, or they're like nesticle, which just intimidate me so bad at first glance. I mean there so much stuff, and I don't know where to begin. All I'd want is just getting straight to the point and start writing my code. I know what the opcodes mean and do (except that I don't get what the difference is between LSR and ROR) and I understand how to control the other peripherals with the CPU.

Hacking now suddenly seems much more difficult than making a new game. I have to make sure I don't change anything, not the stack, not the CPU status before I end my part of the code, and I can't exceed the original amount of data.
In my programs there'd be huge lag anyway because of pushing and pulling every millisecond to make sure the next operations have a clean status register.

I'll come back to this once I have some time.

User avatar
Kasumi
Posts: 1292
Joined: Wed Apr 02, 2008 2:09 pm

Re: Getting around the DPCM bug in a ROM hack

Post by Kasumi » Sun Jan 26, 2014 6:07 am

Hacking now suddenly seems much more difficult than making a new game.
Absolutely, for anything more than the simplest of changes.

Rant edit: Also, making original stuff is a good thing to do. I never learned how to hack. One day I just realized I could after making original stuff for so long. I feel like people who get into hacking first skip a lot of the programming fundamentals and end up banging their head against the wall over really dumb stuff. (Not you specifically, just a thing I see in general.) Whatever game is in question already does things like read the joypad and whatever, so when you hack you avoid learning how to do stuff like that. In fact, many spend more time looking for ways to avoid learning a thing than would take to just learn it. Don't be that guy, please. If the end hacked result and not the knowledge is all you care about, I imagine you won't get it done.

I used to like making free, simple hacks for group hacked-end-result, but I've gradually lost my taste for that.
I could never find any assembler that worked for me.
As unregistered said, get asm6: http://home.comcast.net/~olimar/NES/
It's even open source if you happen to not be on Windows.

It'll take a very simple input. Here's a piece of a thing I assembled. Basically just needs a .org statement and your code. Will give you a binary, which you then throw into the rom.

Code: Select all

pointerlo = $02
pointerhi = $03

;Initialize Pointer
;Must find two free bytes of zero page RAM
	.org $FF30
	lda #$01
	sta pointerlo;Unused, reserved for player 2
	lda #$60
	sta pointerhi;Unused, reserved for player 2
	
	jmp $8095
Edit: Ah! And if you're working with a disassembly, yes, you have to get working whatever assembler was used by who made the disassembly or modify the disassembly to work with the assembler YOU choose. Thems the breaks. The latter generally isn't too tough if you actually understand your assembler's syntax.

If you want to practice 6502 code from scratch in a test setting, there's the 6502 macroassembler: http://exifpro.com/utils.html

Edit:
(except that I don't get what the difference is between LSR and ROR)

Code: Select all

clc
lda #$01
lsr a
;#$00 is in A

Code: Select all

sec
lda #$01
lsr a
;#$00 is in A

Code: Select all

clc
lda #$01
ror a
;#$00 is in A

Code: Select all

sec
lda #$01
ror a
;#$80 is in A
Ror puts the carry bit into bit 7 of memory (or A). Lsr ignores the carry, and always shifts a zero into bit 7.

Edit: Hopefully the final edit... Tepples, your routine is missing the label "frame_not_glitched", and clocks in at 93 bytes. The original was 88. There might be other problems too, I might look at it more when I have some more time.

Post Reply