NSF, CA65, MUSE

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Post Reply
lidnariq
Posts: 10677
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

NSF, CA65, MUSE

Post by lidnariq »

EDIT: this was originally a "help" post. The first response fixed where I was confused, so now it you can just consider it a "here's how I built an NSF file using MUSE and CA65".

So I was trying to make an NSF, given the original source to a specific game I helped with earlier. And I discovered there must be some subtlety to the unbanked NSF format that I'm completely misunderstanding.

I made a .cfg that looks like:

Code: Select all

MEMORY {
    # First, the parts that are specified in the image, in the order they're needed:

    # NSF Cartridge Header
    HEADER: start = $0, size = $80, file = %O, fill = yes;

    ROM0: start = $8000, size = $8000, file = %O, define = yes;

    # Then, RAM definitions:
    ZP: start = $0, size = $100, type = rw, define = yes;

    # standard 2k SRAM (-zeropage)
    SRAM: start = $0200, size = $0600, define = yes;
}

SEGMENTS {
    HEADER:   load = HEADER,          type = ro;
    CODE:     load = ROM0,            type = ro,  define = yes, align=256;
    MUSE:     load = ROM0,            type = ro,  define = yes, align=256;
    BSS:      load = SRAM,            type = bss, define = yes;
    ZEROPAGE: load = ZP,              type = zp;
}
And a little tiny asm source that looks like

Code: Select all

;;; cl65 -d -vm -l nsf.lst -g -t nes -C nsf.cfg -m nsf.map -Ln nsf.lbl -o driar.nsf nsf.asm

.segment "HEADER"
	.import __ROM0_START__
	.macro pad32 str
	 .if (.strlen(str) > 31)
	  .error "pad32 given too long input"
	 .endif
	 .byte str,0
	 .res 31-.strlen(str)
	.endmacro
	
	.byte "NESM",$1a
	.byte 1     ; version
	.byte 10    ; number of songs
	.byte 1     ; starting song
	.word __ROM0_START__ ;;; This is where I was wrong. loadaddr should be the start of the ROM0 segment
	.word initaddr
	.word playaddr
	;; ....0123456789a123456789b123456789c1
	pad32 "Driar Soundtrack"
	pad32 "David Eriksson"
	pad32 "©2012 David Eriksson"
	.word 16639 ; Real NTSC rate
	.byte 0,0,0,0,0,0,0,0 ; disable bankswitching
	.word 19997	; Real PAL rate
	.byte 3	; Prefer PAL, compatible with both
	.byte 0	; no expansion audio
	.word 0,0

.BSS
MUSE_RAM: .res 256
.ZEROPAGE
MUSE_ZEROPAGE: .res 7
savedA: .byte 0
savedX: .byte 0

.segment "CODE"
.proc initaddr
	stx savedX
	sta savedA

	lda #<musedata
	ldx #>musedata
	jsr MUSE_init
	
	lda #15
	jsr MUSE_setVolume
	;; X=1 if PAL, 0 if NTSC
	lda savedX
	eor #1
	asl
	asl
	asl
	asl
	;; now A=0 if PAL, 16 if NTSC
	jsr MUSE_setFlags
	;; A=desired song number
	lda savedA
	jsr MUSE_startMusic
	rts
.endproc

.proc playaddr
	jsr MUSE_update
	rts
.endproc
.segment "CODE"
.include "sound.inc"

.segment "MUSE"
	.align 256
	.include "muse-ca65.inc"
So there are two major things I don't understand:
1- What's the difference between loadaddr and initaddr in an unbanked NSF? I originally thought I should put MUSE_init in loadaddr, and MUSE_startMusic in initaddr, but then it doesn't work at all. What works is what I have above, where loadaddr is moot and initaddr does everything. Why would that be the case?
2- If I reorder the above segments, such that sound.inc or muse-ca65.inc or both come before my little stub code, and thus the address of loadaddr, initaddr, and playaddr are no longer at $80xx, once again, it doesn't work at all. Why does this break things?
Last edited by lidnariq on Sun Sep 29, 2013 3:34 pm, edited 1 time in total.
doppelganger
Posts: 183
Joined: Tue Apr 05, 2005 7:30 pm

Re: NSF, CA65, MUSE

Post by doppelganger »

Well, I can help you out with at least one of these things.

The load address in the NSF file tells the NSF player the beginning of that section of memory where the NSF code is loaded. The load address is *not* used as an address to any subroutine. The init address, on the other hand, is the address to the init routine, which is executed only once (when a song begins playing). Ideally, everything that prepares a song to be played should be executed in the init routine. These two addresses may or may not be the same depending on where the init routine is located.

Let's say, for example, you have all your sound data and code at $8000-$bfff. Let's say you have a bunch of music data at $8000, and your init routine starts at $a000. In this particular case, the load address would be set to $8000 and the init address would be set to $a000.
Be whatever the situation demands.
lidnariq
Posts: 10677
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: NSF, CA65, MUSE

Post by lidnariq »

And that explains everything. Letting loadaddr move around (rather than being fixed to $8000) was what was breaking everything. And now that I know what to look for, I found that in the wiki page on NSF

<sigh> Thanks!
Post Reply