CHR bankswitching and NSF insertion in NES game

Started by pangenttech, April 22, 2022, 11:14:52 AM

Previous topic - Next topic


   This is a hack of the Dr. Mario prototype "Virus," released in 2020.

Amilgi has surprised me by writing NSF music for Dr. Garfield, four themes in total. I'd like to figure out how to get these into the NES and Game Boy ROMS.

Music selection #3 seems to exist in the ROM from 006ECF to 0072C0, and selection #4 from 75D2 to 78AD. The original music is left over from Tetris. I can copy/paste data into that section and get a bit of a mess of noise. Song 2 in the NSF starts at F0D probably.

Pasting from NSFs of Tetris and Dr. Mario also only gets nonsensical noise, at least where I've pasted them.

QuoteSome notes:
- I only used the 2a03
- I didn't use any DPCM (could be used for drums if we wanted)
- I made one simple instrument to handle the arpeggio in the gameplay music. Some drivers/converters prefer no instruments & just tracker effects, while others prefer exactly the opposite. I can rework parts of the module if needed for easier implementation.

QuoteMost straightforward way is to inject Famitone or a similar sound engine into the ROM somehow.

Tetris disassembly:

  Also, about CHR bankswitching:

   Being a prototype, there is a fair amount of empty space in the ROM and the title screens and backgrounds are basically drawn uncompressed in full. This made it easier to hack.

The backgrounds in 1P and 2P mode bankswitch between the 0 and 1 CHR registers, to create some animations with the viruses.

For 2P mode, I would like to switch between the 8 and 9 registers, so I can do more with the background.

I can about halfway understand what the code is doing, but not enough to make this happen easily.

(Searching for 9F63 leads to the associated code, I believe.)

The added 8 and 9 CHR registers are also not recognized by the ROM at this point. I think something needs to be changed in the header?

  (Several CHR banks were already added to allow for sprite bankswitching on the title screen.)

  I asked about this previously, and got a confusing answer:


00:86D6: A5 43     LDA $43 = #$9B
00:86D8: 29 08     AND #$08
00:86DA: 4A        LSR
00:86DB: 4A        LSR
00:86DC: 4A        LSR
00:86DD: 20 63 9F  JSR $9F63
00:86E0: 60        RTS -----------------------------------------

After all the calculations, you get 00/01 value before going to 9F63, where chr banks are selected by writing to specific registers.

In order to do what you want, you need to add a condition on "how many players" and replace 00/01 with 08/09 if needed, before going to 9F63.

If you expand chr rom size, you need to edit address 0x05 in header and add bytes at the end of the file. Size must be a power of 2.



Okay, I've saved a larger 81KB version of the NES ROM which does allow for four more CHR banks.

The existing code seems to use binary, only really allowing for 0 and 1, so I'm not quite sure how to translate that to 8 and 9.

RAM Offset $727 keeps track of what player mode is selected, apparently.


Quotehow to translate that to 8 and 9

There are several ways. The simplest one is to add 08 to A register. The result will be 08 or 09, depending on initial value.

LDA $43
AND #$08
JSR to a free space

; somewhere at free space
; A holds 00 or 01 for chr bankswitching
; X is not used and will be overwritten later, so we can use it here
LDX $0727 ; players counter
CPX #$01 ; check if equal to 1p mode
BEQ @skip ; skip addition if true (1p mode)
; add 08 if false (2p mode)
ADC #$08
; now A holds 08 or 09
JSR $9F63


  Thanks, that does make sense.

  I've updated the ROM accordingly, and the 2 player mode looks MUCH better now.

  Someone is also working on inserting the music, so more on that soon. There is already a test with "Friends Are There" working.


I know I can't BEQ a JSR or RTS, so how would I write this? This is supposed to write the correct palette (when coming out of pause mode) depending on player/difficulty mode.

Not even sure this is remotely right, but it's what I've got ...

LDX $0727 ; players counter
CPX #$01 ; check if equal to 1p mode
BEQ JSR $8232 ; jump to 1P palette
CPX #$01 ;
BEQ @skip
LDY $0726 ; difficulty counter
CPY #$03 ; check if equal to NERM mode
BEQ JSR $CA68 ; jump to NERM palette
CPY #$03
BNQ JSR $CA44 ; jump to 2P palette


By the looks of it, in case of 1p you want to write 1p pal and exit, but in case of 2p you want to write 2p pal and also write NERM pal if NERM is on. So the code should be something like this

: in case X can't be overwriten here, add this code
JSR my_code

LDX $0727 ; players counter
CPX #$01 ; check if equal to 1p mode
BNE 2p_mode ; if false
; if 1p
JSR $8232 ; 1P palette

JSR $CA44 ; 2P palette
LDX $0726 ; difficulty counter
CPX #$03 ; check if equal to NERM mode
BNE skip_nerm : if false
JSR $CA68 ; NERM palette


I actually just want to write the NERM palette if in NERM mode, not write both, but that's fine, thank you.

This is probably close, but I tried something like this in the code and it crashed the ROM .... from the start, when in theory it shouldn't be loading this info at that point ... which means I need to look at the rest of the code again ...

I sort of took it for granted that I had the right subroutines and ... I'm thinking I don't, now ... apart from the addresses I listed myself.

I think I was working off of bad info here unfortunately ...

00C8C0 (in ROM) - jump to CAB0 
CAB0 (CAA0 in ROM)

LDX $0727 ; players counter
CPX #$01 ; check if equal to 1p mode
BNE $CABB ; if false
; if 1p
JSR $8232 ; 1P palette

JSR $CA44 ; 2P palette
LDX $0726 ; difficulty counter
CPX #$03 ; check if equal to NERM mode
BNE $CAC8 ; if false
JSR $CA68 ; NERM palette


Maybe you JSR to wrong locations. You can't just JSR anywhere you want, you need to be careful with what's inside the subroutune.

Also, if you create a JSR to your new subroutune, you overwrite some of the original code in order to fit your JSR. So you need to make up for it by properly adding original code to your subroutune (if necessary).