Loading NSFs into ROMs

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Loading NSFs into ROMs
by on (#3028)
Yeah, I am really annoying with my dumb questions, because I never seem to get WTF anyone's talking about, but I have a question. Could someone give me an example of loading NSFs into your asm code? I can't seem to figure it out. And the thing they said about it in the NT2 documentation is something I can't understand, it's too advanced for my stupid brain. I don't know what they mean about load address $80, or "jsr into the init address in the NSF header"? Could someone just give me an example of initiating all sound channels, and loading the NSF into the code? thanks. And also, is there any documentation about NSFs anywhere?:(

by on (#3029)
Here's the NSF specs:
http://www.tripoint.org/kevtris/nes/nsfspec.txt

So let's say there's an NSF (with it's header removed) with this:
load = $8000
init = $85a3
play = $8a44

You'd do this in the program:
(depending on the assembler)

Code:
init = $85a3
play = $8a44
.org $8000
.incbin "song.bin"
 lda #0 ; song 0
 ldx #0 ; NTSC speed
 jsr init
 ..

nmi:
 ..
 jsr play
 ..
 rti


by on (#3041)
If I incbin "song.nsf" into the program, it completely ruins everything! is there a reason for this? and why did you include "song.bin" and not "song.nsf"? I am dumb, and not sure about anything.

by on (#3047)
I am using NESASM if that helps at all...

by on (#3051)
if you .org to put the NSF at $8000, it will occupy your game's space at $8000. For example, assume the NSF is 16k with a load address of $8000. This means that $8000-$BFFF in your game will contain the NSF code, and CANNOT be used by your game (unless you're doing some bankswitching -- which I'm assuming you're not).

Also -- make sure the NSF does not do any bankswitching. Make sure the load address is above $8000 and all the 'bankswitch' bytes in the header are 00.

REMOVE THE HEADER before incbin'ing it to your game. The header will do nothing for your game and will just distort the init and play addresses. You'll probably want to keep a copy of the NSF with a header, but don't incbin the headered nsf (this is probably why Membler's example had a .bin extension and not a .nsf extension)

Finally -- don't use any RAM used by the NSF, or your music will screw up and your game may even crash. To see which areas of RAM are used by the game, load the NSF up in FCEUXD and open the memory viewer -- watch to see which addresses change as the NSF plays (do not use those addresses ever!)

by on (#3901)
Why does it never work when I load an NSF into FCEUXD Ultra? It doesn't make any sound. And I still don't get how to include an NSF into a rom. I tried this little code just to see if it would make any sound:

Code:
   .inesprg 2
   .ineschr 0
   .inesmir 0
   .inesmap 0

init = $85a3
play = $8a44
load = $8000

   .bank 1
   .org $FFFA
   .dw nmi
   .dw reset
   .dw 0

   .bank 0
   .org $8000

reset:
   cld
   sei

   lda #%00001111
   sta $4015

   .incbin "song.nsf"
    lda #0 ; song 0
    ldx #0 ; NTSC speed
    jsr init

nmi:
    jsr play
    rti

loop:
   jmp loop

yeah I know it's a dumb code, but do you have to do any writes to 4000-4003? or for any of the channels? or does the NSF have that info? I'm sorry, I just don't get it.[/code]

by on (#3902)
Two problems. The program would try to running the NSF (header and all) as code.

The other problem is the load address is $8000, and you had it loading at $8007 or so (because of your reset code's location). Another problem is the NSF header is 128 bytes, so you'll have to strip that off to make it really load at $8000 instead of $8080.

Your infinite loop was too far down also, the code would've ran into the NMI and messed up when it hit the RTI.


Code:
   .bank 0
   .org $8000
   .incbin "song.nsf"

reset:
   cld
   sei

   lda #%00001111
   sta $4015

    lda #0 ; song 0
    ldx #0 ; NTSC speed
    jsr init
loop:
   jmp loop


nmi:
    jsr play
    rti

by on (#3903)
Okay, and another thing, I don't know how to remove the header in that nt2 asm code. And I certainly don't know how to write my own code for making a NED into a NSF. If I just kind of you know erase that segment where NSF header is defined, yeah, it screws up. So any ideas on how to safely remove the header in that specific asm file?

by on (#3904)
A few different things you could do:

1. modify the nes.cfg file so
PRG: start = $8000, size = $8000
And remove the NSFHEADER segment fron the config too. And remove that from the source.

2. Same as above, but instead of removing NSFHEADER from the config and source, make it start at $8000 (and code start at $8080). Then you can include the NSF file in the ROM, and still be able to play it on an NSF player.

3. assemble it normally, and use a hex editor to remove the first $80 bytes.

by on (#3911)
Okay, I'm still very confused. So I did this to the nsf.cfg file:

Code:
MEMORY {
            ZP: start = $00, size = $100, type = rw;
            RAM: start = $200, size = $600, type = rw;
            PRG: start = $8000, size = $8000, type = ro, file = %O;
        }
        SEGMENTS {
            NSFHEADER: load = PRG, type = ro, start = $8000;
            CODE: load = PRG, type = ro, start = $8000;
            RODATA: load = PRG, type = ro;
            DATA: load = RAM, type = bss;
            ZEROPAGE: load = ZP, type = zp;
            BSS: load = RAM, type = bss, define = yes;
            SAMPLES: load = PRG, type = ro, start = $C080;
        }


I still get nothing, and I deleted the NSF segment in the nt2-22 code too, and still nothing happens. do you think you could show me what you mean? I'm sorry, I'm very dumb, and I'm confused, and I really want to know this already. Sorry...[/code]

by on (#3919)
also. to save some/lots of bytes you can allways open up the nsf file in a hex editor and remove all the zeros after the end of the nsf.
please note that 1 or 2 (or more) of them can be data.. I usually save like 10 zeros so I know the song will work.

edit: forgot to write something

by on (#3931)
Change the code segment's start address to $8080 in nes.cfg, see if that works.

by on (#4076)
Okay, I really screwed everything up, I think... Okay, I know I always drag it to this point, but I was wondering if someone could send me a sample of how to do this, because I really don't know what I'm doing wrong. So could someone send me a sample? that would be very helpful. Thanks, if anyone does it. send me a private message, and I will give you me email :).

by on (#4147)
Okay, I see that didn't really help me, but I will ask this. Could someone show me the segments you have to change? and a modified version of my code to play the nsf? I would highly appreciate it. If anyone does that, thanks in advance. :)

by on (#4176)
I meant for you to change it to this:

Code:
MEMORY {
            ZP: start = $00, size = $100, type = rw;
            RAM: start = $200, size = $600, type = rw;
            PRG: start = $8000, size = $8000, type = ro, file = %O;
        }
        SEGMENTS {
            NSFHEADER: load = PRG, type = ro, start = $8000;
            CODE: load = PRG, type = ro, start = $8080;
            RODATA: load = PRG, type = ro;
            DATA: load = RAM, type = bss;
            ZEROPAGE: load = ZP, type = zp;
            BSS: load = RAM, type = bss, define = yes;
            SAMPLES: load = PRG, type = ro, start = $C080;
        }

by on (#4199)
There is still nothing. I will show you my full code:

Code:
   .inesprg 2
   .ineschr 0
   .inesmir 0
   .inesmap 0

   .bank 1
   .org $FFFA
   .dw 0
   .dw reset
   .dw 0

init = $85a3
play = $8a44
load = $8000

   .bank 0
   .org $8000
   .incbin "song.nsf"

reset:
   cld
   sei
   lda #%10000000
   sta $2000

   lda #%00001111
   sta $4015

    lda #0 ; song 0
    ldx #0 ; NTSC speed
    jsr init

loop:
   jmp loop


nmi:
    jsr play
    rti


and for the nsf replay code:

Code:
...
;NED_SampleInst          = NED_STATIC_BASE + $40
;NED_SmpNum              = NED_STATIC_BASE + $41
;NED_Length              = NED_STATIC_BASE + $42

NED_TotalPatsDPCM       = NED_RAM_BASE + $E4

NED_ChnPatRowsToSkip    = NED_RAM_BASE + $8A
NED_ChnPatRowsTotal     = NED_RAM_BASE + $8F

NED_PAL_Counter = $FF



; no nsf header


.segment "SAMPLES"
.incbin "temp.dmc"

.segment "CODE"

loadstart:

jmp NSF_Play
jmp NSF_InitSong



ProgramEntry:   sei
                cld
                LDX     #$FF
                TXS

                lda     #$40
                sta     $2000


and for the nsf config file:

Code:
MEMORY {
            ZP: start = $00, size = $100, type = rw;
            RAM: start = $200, size = $600, type = rw;
            PRG: start = $8000, size = $8000, type = ro, file = %O;
        }
        SEGMENTS {
            NSFHEADER: load = PRG, type = ro, start = $8000;
            CODE: load = PRG, type = ro, start = $8080;
            RODATA: load = PRG, type = ro;
            DATA: load = RAM, type = bss;
            ZEROPAGE: load = ZP, type = zp;
            BSS: load = RAM, type = bss, define = yes;
            SAMPLES: load = PRG, type = ro, start = $C080;
        }


.... ????? What's the deal here?

by on (#4202)
Celius wrote:
There is still nothing. I will show you my full code:


There must be something. Narrow it down. First of all, will it play in an NSF player? It should.

by on (#4204)
Find an emulator with a good debugger and learn how to use it. If you aren't doing this you're shooting in the dark. When you do use one you'll be able to see if you've set up your vectors properly, and find when the code goes wrong.

by on (#4205)
It won't play in an nsf player, because it's an unknown format, most likely because I removed the nsf header. Hmm, maybe I accidentily put in empty data files that the nsf gets its sound info from, but I don't think I would do that. Are you sure there's nothing wrong? and by the way, why can't I get FCEUXD ultra to play an nsf? there's no sound when I try and play it. Even with commercial games NSF files. What the heck is wrong with all my stuff?

by on (#4206)
Ah sorry, I forgot about the NSF header being removed. Notice that in the linker config the code/data now starts at $8080. So you need to either add the header back into it so it fills that 128 byte gap, or in your nesasm program set the NSF's origin to $8080 instead of $8000.

I normally use normal FCEU's debugger, you don't need anything advanced. You just need to see how/if the program starts and what happens when it JSRs to the init/play code.

by on (#4214)
Okay, I'm sorry, but I'm still not getting anything in my rom. here's my code, again:

Code:
    .inesprg 2
   .ineschr 0
   .inesmir 0
   .inesmap 0

   .bank 1
   .org $FFFA
   .dw nmi
   .dw reset
   .dw 0

init = $85a3
play = $8a44
load = $8000

   .bank 0
   .org $8080
   .incbin "song.nsf"

reset:

   lda #%10000000
   sta $2000

   lda #%00001111
   sta $4015

    lda #0 ; song 0
    ldx #0 ; NTSC speed
    jsr init

loop:
   jmp loop


nmi:
    jsr play
    rti


and

Code:
MEMORY {
            ZP: start = $00, size = $100, type = rw;
            RAM: start = $200, size = $600, type = rw;
            PRG: start = $8000, size = $8000, type = ro, file = %O;
        }
        SEGMENTS {
            NSFHEADER: load = PRG, type = ro, start = $8000;
            CODE: load = PRG, type = ro, start = $8080;
            RODATA: load = PRG, type = ro;
            DATA: load = RAM, type = bss;
            ZEROPAGE: load = ZP, type = zp;
            BSS: load = RAM, type = bss, define = yes;
            SAMPLES: load = PRG, type = ro, start = $C080;
        }


and

Code:

.segment "NSFHEADER"

.byte 'N','E','S','M',$1A
.byte $01
.byte 1  ; number of songs
.byte 1
.word loadstart
.word NSF_InitSong
.word NSF_Play
.byte "title                          ",0
.byte "author                         ",0
.byte "copyright                      ",0
.word $411A
.byte 0,0,0,0,0,0,0,0
.word $4E20
.byte 2   ; Dual PAL/NTSC
.byte 0
.byte 0,0,0,0



and I still get nothing. there's no sound. none. it will play in an nsf player, but not a rom. i tried it on multiple emulators, and nothing happens. and can someone really tell me how to get sound when loading an nsf into FCEUXD ultra? And can someone fix and test this code for me?

by on (#4215)
If it plays in an NSF player, then it does work. I can't tell what the real init/play addresses should be in this case. If the NSF's init/play doesn't match what you're using, you could try using those JMPs that are at the beginning of the NT2 code.

Like this:

init = $8083
play = $8080

by on (#4218)
I'm sorry, I don't get what your saying. Can I see a modified version of my code?

by on (#4219)
Code:
   .bank 1
   .org $FFFA
   .dw nmi
   .dw reset
   .dw 0

init = $8083
play = $8080
load = $8000

   .bank 0
   .org $8080
   .incbin "song.nsf"

by on (#4221)
That's what I have, and still nothing! what is this!? Okay. Memblers, can you send me a compressed folder with the nsf stuff just the way it should be and a nesasm code that will run it? I know it may be a lot to ask, but I would really appreciate it. I don't know if it's anything wrong with my code, or what. Sorry, thanks, if you do it.

by on (#4226)
OK, I rewrote your nesasm program but haven't tested it. It really should work. NMIs might've been enabled too soon.
http://nesdev.com/wiki/?page=Nerdtracker+player+in+NESASM

by on (#4232)
hahahahaha! this is really funny, because it still doesn't work. What the hell is this? why doesn't it work!? It's gotta be my nsf file. And why do you say NMIs were enabled too soon? they were enable at the frickin' end, so they can't be enabled any later. Okay, I'm wondering if you can really test one for me that works, because I would be SO greatful, because making NES music on games would be SO much easier. I really don't know what the problem is here, and I'm really FRICKIN SICK OF IT! Can you test it and make sure it works for me? sorry, thanks.

by on (#4354)
Anybody..? :(

by on (#4921)
Okay, I am really upset, because I am so lost, and I need to start over with this. Okay, memblers, I just realized a couple things:

in your nesasm NT2 code, you never rti'd in you NMI routine
You never initiated sound registers

That might have something to do with it, but I still get nothing. I don't think it's my Nesasm code, really. I think it's the NSF code. I have NO idea what's wrong here, and I would really really really appreciate it if you helped me a little more. Would you please? I really need someone to help me here... :(

by on (#4922)
I took the NSF "Alloy Run Mix" and wrote some nesasm glue code to make it into a playable ROM. Please study the code and remember that it won't work with an NSF that uses bank switching.

nsf_in_rom.zip

by on (#4939)
Okay, thank you very much for sending me that, but I'm really sorry, but could you send me the NSF replay code for that? if it's not too much to ask, because it won't work with my NSF, but it works with yours. And how did you get your NSF to be 8K? whenever I make mine, they're 16K. I would appreciate if you put up the NSF replay code :).

by on (#4940)
Sorry, what I posted along with study of the NSF file spec will provide you with plenty of study.

by on (#4942)
I just want to ask you if you used the NT2 to NSF code that was supplied? with modifications? Because I don't see how I could study the NSF itself, without the NSF replay code. Tell me if I'm wrong. I don't mean to be a pain, like I always am, sorry... :(

by on (#4947)
All I did was write some glue code to run an NSF inside a NES ROM. The code demonstrates how to locate the NSF data at the proper address (based on looking at the NSF header with a hex editor) and then call the init and play routines. If you already have no problem doing this and are having trouble with specific NSF files only, then I wasn't following the thread closely enough.

I don't know much about the NT2 NSF engine, but if it doesn't use bank switching then an NSF generated by it should be usable with the above method.

by on (#4948)
If you cut out all the DPCM code then you can get the .ned file below 16 KB.

by on (#5012)
ugh... I HATE that NSF replay code! it's SO LONG, and SO HARD TO FIND ANYTHING! Okay, I know I always just take the easy way out, and ask everyone else to do everything for me, but I really really need to know something. Does the NSF NT2 code use bank switching? and where's the DPCM stuff? This should be so unhard, it's not even funny! It's so complicated it really irritates me! I'm sorry, I know I'm a pain.... :(

by on (#5022)
NT2 NSFs never use bankswitching. And usually the DPCM will be at $C000, it can be placed starting on a higher address too but noone hardly ever does that. Usually there'd be a lot of space between the NSF code/data and samples, so I'd split them into seperate files.

by on (#5029)
Or set it up so that it assembles the NT2 code right after the DPCM samples, and then use the initial bankswitch state bits in the header to map the 16 KB NSF into CPU$C000 space.

by on (#16038)
necro-bump :D

I used this thread to get nsf's running in a compiled rom. I know it is an old thread, and that the OP probably figured it out already, but I'll post what I did in case it can help other people.

#1. Changed from nesasm to dasm. Got rid of .bank problems (was more trouble than it's worth).

#2. "Padded" the NSF I wanted to use. I found no other way to align data properly in the rom. This is the area where advice would be welcome.

For example, I wanted to include Zelda.nsf in my program.

The size of the music data, when the header is taken off is $12C4 bytes.

It's load adress is $8D60, init is $A003 and play is $A000. For the rom to be the correct size (32 784 bytes?), I had to pad $0D60 worth of zeroes before the music data, and $1FDC worth after.

This was to make the $12C4 bytes file weight $4000 bytes, for the load offset to align properly(and fill an entire prg-rom bank).

Is it common practice to do this? Is it assumed that nsf data within a nes rom weights exactly 16kb?

#3. Cleared the ram from $0000 to $07FF ... It would NOT work at all without doing that. Nintendulator would just spew out some unorganized beeps! This is what happens when you don't follow chris's example closely enough...

Anyways, for other newbies having problems with this, I ended up with this code:

Code:
INIT EQU #$A003
PLAY EQU #$A000
   PROCESSOR 6502
   ORG $C000
.reset   
   sei
   cld

.waitv   
   bit $2002
   bpl .waitv

   lda #$0
   tax
.clearRam
   sta $0000,X
   sta $0100,X
   sta $0200,X
   sta $0300,X
   sta $0400,X
   sta $0500,X
   sta $0600,X
   sta $0700,X
   inx
   bne .clearRam    ;clear RAM

   lda #$0
   ldx #$0
   jsr INIT
   lda #%10000000
   sta $2000 ; <- enable play after init

.loop   
   jmp .loop

.nmi   
   jsr PLAY

.irq   
   rti

   ORG $FFFA,0
   dc.w .nmi
   dc.w .reset
   dc.w .irq


You can compile it with dasm using this syntax:

Code:
dasm asmfile.asm -f3


and then assemble the rom this way:

Code:
copy /b header.bin+music.bin+a.out rom.nes


where:
header.bin is the iNes header (2 prg banks, 0 chr-bank, mapper0)
music.bin is the padded music data (16kb)
a.out is the 16kb prg-rom outputted by dasm

Of course this code will only run the "First track" of the nsf.

If anyone can answer my interrogations it would be most appreciated!