NSF ripper Guide Level 18


Level 0   Level 1   Level 2   Level 3   Level 4  

Level 5   Level 6   Level 7   Level 8   Level 9

Level 10   Level 11   Level 12   Level 13   Level 14

Level 15   Level 16   Level 17   Level 18   Level 19

Level 20


LEVEL 18 Bankswitching

  As many know, the address space for active loaded ROM memory is $8000 - $FFFF. When you have more banks and data than what can fit into this space is when you need to use bankswitching. Also there is a situation where you have a few banks that need to be loaded into a certain memory range, like $8000 $8FFF for example. Many games have done this, such as Metroid, Zelda, and many many more.

  However, all we need to do is use bankswitching for NSF files that have eliminated everything but the driver or music engine. Before we get into the process, I am going to describe bank sizes for you.

Bank Size Select Hex Size Address Range Mapper
4KB
1000h
Variable
NSF
8KB
2000h
Variable
MMC5
16KB
4000h
$8000 - $BFFF/$C000 - $FFFF
MMC1
32KB
8000h
$8000 - $FFFF
AOROM

  This chart will generically handle nearly any mapper. For example, MMC3 or mapper 4 is either 8KB or 16KB bankswize select. AOROM, which is listed is 32KB bankseized select, or what most people are familiar with, that's Mapper 7. There are other mappers that will not be handled well by the chart that I showed you. MMC2 or Mapper 9 is a good exmple. Mapper 9 has a 24KB fixed bank at $A000 - $FFFF, the 8KB bank is switched in and out at $8000 - $9FFF. Another example would be Mapper 42, this is a FDS port that has a fixed 32K bank at $8000 - $FFFF, a 8KB bank is switched in and out in address range $6000 - $7FFF. How you deal with these odd mappers depends on the sound driver and in some cases, takes some strategy in order to rip. MMC2 no longer has any games that need to be ripped. However, one may pop up in the future, so I mention it to be complete. For FDS port games, I suggest to set the FDS bit in the header and rip it as though it was a FDS game (if the sound data or code is $6000 - $7FFF).

  Now it's time to start digging into a game to locate the banks. First off, I suggest to make every available effort to prevent a NSF from bankswitching, if it's possible. If you only have a string of code, then it should be moved near the sound engine core, located in some freespace somewhere. For example, I have ripped a game called Golden KTV. The game is standard ripping fair and quite easy until you find out that only 3 tunes plays along with some sound effects. Since this is a large scale game with a lot of tune selections in the game, it's logical to assume this game will be bankswitching. Assuming that you made every available effort to locate the tunes. Now, here is how I done it and that's by a indirect jump routine so that I could simply plug in the banks and use the same index number for all the different tunes.

Basic Initialization Start


00:F450:48        PHA

00:F451:A9 40     LDA #$40

00:F453:8D 17 40  STA $4017 = #$FF

00:F456:A9 00     LDA #$00

00:F458:8D 10 40  STA $4010 = #$FF

00:F45B:A9 1F     LDA #$1F

00:F45D:8D 15 40  STA $4015 = #$FF  ; turn all channels on

00:F460:68        PLA

00:F461:0A        ASL

00:F462:AA        TAX

00:F463:BD 70 F4  LDA $F470,X @ $F470 = #$F0

00:F466:85 00     STA $0000 = #$00            ; store low jump byte

00:F468:BD 71 F4  LDA $F471,X @ $F471 = #$F4

00:F46B:85 01     STA $0001 = #$00            ; store high jump byte

00:F46D:6C 00 00  JMP ($0000) = $0000

Intialize 1


00:F4F0:A9 04     LDA #$04          

00:F4F2:85 02     STA $0002 = #$00  ; bankswitch variable 1

00:F4F4:20 20 F4  JSR $F420         ; bankswitch sub 1

00:F4F7:A9 09     LDA #$09          ; tune #

00:F4F9:4C 42 F4  JMP $F442

Bankswitch Sub-routine 1


00:F420:A5 02     LDA $0002 = #$00  ; bankswitch variable 1

00:F422:8D F8 5F  STA $5FF8 = #$FF  ; NSF reigster to load $8000 - $8FFF

00:F425:60        RTS

Intialize 2


00:F442:8D A6 07  STA $07A6 = #$00  ; tune variable

00:F445:20 9B CA  JSR $CA9B         ; jump to main sound driver initialization

00:F448:60        RTS

  This is how I set the NSF up after I realized that only the sequence data is being loaded at $8000 - $9FFF, the core of the sound driver is at $C000 - $DFFF, also has DPCM. I gave you a quick run down of the code first and then you start plugging in the banks. So, the game is UNROM or Mapper 2, which means 16KB banksize select, there are 64 - 16KB sized banks. So, safe to say, I used a file splitter and divided the ROM into 16KB sections and gradually worked on them to remove anything that wasn't needed. I also saved $F000 - $FFFF as my "hardwired" bank to write the code. Most of the tunes did not go over 4KB, those that don't use address range $8000 - $8FFF, a few others use $8000 - $9FFF. I loaded them using the following NSF bank registers, using them in the initialization code.

  You may want to know how to load the banks, it's simple, you use the NSF bankswitching registers to load the banks at certain times. I will now show you the registers. No matter what strategy you use, these registers will switch the banks.

Register Address Range
$5FF6
$6000 - $6FFF
$5FF7
$7000 - $7FFF
$5FF8
$8000 - $8FFF
$5FF9
$9000 - $9FFF
$5FFA
$A000 - $BFFF
$5FFB
$B000 - $BFFF
$5FFC
$C000 - $CFFF
$5FFD
$D000 - $DFFF
$5FFE
$E000 - $EFFF
$5FFF
$F000 - $FFFF

  Next is the bankswitch bytes in the header, you can use these to preload certain banks at reset of the NSF. You can learn more about them in Level 12. However, you can use them here too and that's to help you get rid of any banks that you don't need. If you use all of the core banks, you can sequencially number them 00h - 07h. Now, you may wonder how I tracked down these banks. That is also simple for this rip, is why I chose this game for the tutorial. Mapper 2 or UNROM switches banks by the following register, $8000 - $FFFF, you write to any address in this range to switch the banks, depending on the number written. So, set a write break point to $8000 - $FFFF, here is the code I got from it.


00:CFD4:A9 0F     LDA #$0F

00:CFD6:8D 15 40  STA $4015 = #$FF

00:CFD9:A9 C0     LDA #$C0

00:CFDB:8D 17 40  STA $4017 = #$FF

00:CFDE:A9 00     LDA #$00

00:CFE0:8D 00 C0  STA $C000 = #$4C

  As you can see, the sound is turned on and the frame sequencer is written to, as bank 00 is loaded by writing to $C000. We are on the right track, so we keep surfing the code as we track down banks one at a time and plug them in as I mentioned above. That's about it for this rip here.


  The next rip and that's for the game GLK Dance By FengLi, which was somewhat of a difficult rip since the 4 sound driver cores were located in different address ranges, and is also bankswitching. What I'll do is show you the code that I designed to handle the rip.

Initialize 1


00:B000:48        PHA

00:B001:A9 40     LDA #$40

00:B003:8D 17 40  STA $4017 = #$FF

00:B006:A9 1F     LDA #$1F

00:B008:8D 15 40  STA $4015 = #$FF

00:B00B:68        PLA

00:B00C:AA        TAX

00:B00D:BD 90 B0  LDA $B090,X @ $B090 = #$00

00:B010:85 00     STA $0000 = #$00           ; sound core variable

00:B012:8D 0E 01  STA $010E = #$00           ; Tune variable

00:B015:C9 09     CMP #$09

00:B017:B0 4D     BCS $B066                  ; Tune if greater than 09

00:B019:C9 06     CMP #$06

00:B01B:B0 29     BCS $B046

00:B01D:C9 03     CMP #$03

00:B01F:B0 03     BCS $B024

00:B021:4C 0C B9  JMP $B90C

00:B024:A9 05     LDA #$05                   ; tunes 4-6

00:B026:8D FC 5F  STA $5FFC = #$FF

00:B029:A9 06     LDA #$06

00:B02B:8D FD 5F  STA $5FFD = #$FF

00:B02E:A9 07     LDA #$07

00:B030:8D FE 5F  STA $5FFE = #$FF

00:B033:A9 08     LDA #$08

00:B035:8D FF 5F  STA $5FFF = #$FF

00:B038:AD 0E 01  LDA $010E = #$00

00:B03B:18        CLC

00:B03C:E9 02     SBC #$02

00:B03E:8D 0E 01  STA $010E = #$00

00:B041:4C 14 C0  JMP $C014

Initialize 2


00:B046:A9 09     LDA #$09                  ; tunes 7-9

00:B048:8D FC 5F  STA $5FFC = #$FF

00:B04B:A9 0A     LDA #$0A

00:B04D:8D FD 5F  STA $5FFD = #$FF

00:B050:A9 0B     LDA #$0B

00:B052:8D FE 5F  STA $5FFE = #$FF

00:B055:A9 0C     LDA #$0C

00:B057:8D FF 5F  STA $5FFF = #$FF

00:B05A:AD 0E 01  LDA $010E = #$00

00:B05D:18        CLC

00:B05E:E9 05     SBC #$05

00:B060:8D 0E 01  STA $010E = #$00

00:B063:4C CF C1  JMP $C1CF

Play Driver Select


00:B0D0:A5 00     LDA $0000 = #$00

00:B0D2:C9 09     CMP #$09

00:B0D4:B0 12     BCS $B0E8

00:B0D6:C9 06     CMP #$06

00:B0D8:B0 0B     BCS $B0E5

00:B0DA:C9 03     CMP #$03

00:B0DC:B0 04     BCS $B0E2

00:B0DE:4C 2D B9  JMP $B92D         ; sound core 1

00:B0E1:00        BRK

00:B0E2:4C 35 C0  JMP $C035         ; sound core 2

00:B0E5:4C F0 C1  JMP $C1F0         ; sound core 3

00:B0E8:4C 2D C0  JMP $C02D         ; sound core 4

  As you can see, that's the strategy I used to rip GLK Dance by Feng Li. It was a fun game to hack. What I have explained here is loaded banks by the init code, however, there are games that need to have banks loaded in during the play code and is timing sensitive and beyond the scope of this level, currently. Once you learn basic bankswitching by init, then you can move on to the harder bankswitching rips.