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:
And a little tiny asm source that looks like
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?
I made a .cfg that looks like:
Code:
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;
}
# 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:
;;; 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"
.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"
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?