Help with playing .nsf music in a homebrew project.

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Help with playing .nsf music in a homebrew project.
by on (#173138)
Hey guys. I need help with playing my .nsf song made in Famitracker in my project. I have followed all the instructions on http://forums.nesdev.com/viewtopic.php?t=2026. Including removing the 128-byte header of the NSF. I don't know if I did that right though so if anyone can give me a brief explanation on how to do that, that'd be great! My load, init, and play addresses are as follows, $E6A0, $99A9, and $9CA9. I'm following the background tutorial of Nerdy Nights for the base of my game just in case anybody needs to know that.

Here's my code,
Code:
  .inesprg 1   ; 1x 16KB PRG code
  .ineschr 1   ; 1x  8KB CHR data
  .inesmap 0   ; mapper 0 = NROM, no bank swapping
  .inesmir 1   ; background mirroring
 

;;;;;;;;;;;;;;;

  .org $E6A0
  .incbin "fnaf.nsf"
  .bank 0
  .org $C000
RESET:
  SEI          ; disable IRQs
  CLD          ; disable decimal mode
  LDX #$40
  STX $4017    ; disable APU frame IRQ
  LDX #$FF
  TXS          ; Set up stack
  INX          ; now X = 0
  STX $2000    ; disable NMI
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs
  lda #$01   ;or whatever number song you want
  jsr $99A9

vblankwait1:       ; First wait for vblank to make sure PPU is ready
  BIT $2002
  BPL vblankwait1

clrmem:
  LDA #$00
  STA $0000, x
  STA $0100, x
  STA $0300, x
  STA $0400, x
  STA $0500, x
  STA $0600, x
  STA $0700, x
  LDA #$FE
  STA $0200, x
  INX
  BNE clrmem
   
vblankwait2:      ; Second wait for vblank, PPU is ready after this
  BIT $2002
  BPL vblankwait2


LoadPalettes:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$3F
  STA $2006             ; write the high byte of $3F00 address
  LDA #$00
  STA $2006             ; write the low byte of $3F00 address
  LDX #$00              ; start out at 0
LoadPalettesLoop:
  LDA palette, x        ; load data from address (palette + the value in x)
                          ; 1st time through loop it will load palette+0
                          ; 2nd time through loop it will load palette+1
                          ; 3rd time through loop it will load palette+2
                          ; etc
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$20              ; Compare X to hex $10, decimal 16 - copying 16 bytes = 4 sprites
  BNE LoadPalettesLoop  ; Branch to LoadPalettesLoop if compare was Not Equal to zero
                        ; if compare was equal to 32, keep going down



LoadSprites:
  LDX #$00              ; start at 0
LoadSpritesLoop:
  LDA sprites, x        ; load data from address (sprites +  x)
  STA $0200, x          ; store into RAM address ($0200 + x)
  INX                   ; X = X + 1
  CPX #$10              ; Compare X to hex $10, decimal 16
  BNE LoadSpritesLoop   ; Branch to LoadSpritesLoop if compare was Not Equal to zero
                        ; if compare was equal to 16, keep going down
             
             
             
LoadBackground:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$20
  STA $2006             ; write the high byte of $2000 address
  LDA #$00
  STA $2006             ; write the low byte of $2000 address
  LDX #$00              ; start out at 0
LoadBackgroundLoop:
  LDA background, x     ; load data from address (background + the value in x)
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$80              ; Compare X to hex $80, decimal 128 - copying 128 bytes
  BNE LoadBackgroundLoop  ; Branch to LoadBackgroundLoop if compare was Not Equal to zero
                        ; if compare was equal to 128, keep going down
             
             
LoadAttribute:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$23
  STA $2006             ; write the high byte of $23C0 address
  LDA #$C0
  STA $2006             ; write the low byte of $23C0 address
  LDX #$00              ; start out at 0
LoadAttributeLoop:
  LDA attribute, x      ; load data from address (attribute + the value in x)
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$08              ; Compare X to hex $08, decimal 8 - copying 8 bytes
  BNE LoadAttributeLoop  ; Branch to LoadAttributeLoop if compare was Not Equal to zero
                        ; if compare was equal to 128, keep going down


             
             
             
  LDA #%10010000   ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
  STA $2000

  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001

Forever:
  JMP Forever     ;jump back to Forever, infinite loop
 
 

NMI:
  LDA #$00
  STA $2003       ; set the low byte (00) of the RAM address
  LDA #$02
  STA $4014       ; set the high byte (02) of the RAM address, start the transfer
  jsr $9CA9


LatchController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons


ReadA:
  LDA $4016       ; player 1 - A
  AND #%00000001  ; only look at bit 0
  BEQ ReadADone   ; branch to ReadADone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  CLC             ; make sure the carry flag is clear
  ADC #$01        ; A = A + 1
  STA $0203       ; save sprite X position
ReadADone:        ; handling this button is done
 

ReadB:
  LDA $4016       ; player 1 - B
  AND #%00000001  ; only look at bit 0
  BEQ ReadBDone   ; branch to ReadBDone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  SEC             ; make sure carry flag is set
  SBC #$01        ; A = A - 1
  STA $0203       ; save sprite X position
ReadBDone:        ; handling this button is done


  ;;This is the PPU clean up section, so rendering the next frame starts properly.
  LDA #%10010000   ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1
  STA $2000
  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001
  LDA #$00        ;;tell the ppu there is no background scrolling
  STA $2005
  STA $2005
 
  RTI             ; return from interrupt
 
;;;;;;;;;;;;;; 
 
 
 
  .bank 1
  .org $E000
palette:
  .db $22,$29,$1A,$0F,  $22,$36,$17,$0F,  $22,$30,$21,$0F,  $22,$27,$17,$0F   ;;background palette
  .db $22,$1C,$15,$14,  $22,$02,$38,$3C,  $22,$1C,$15,$14,  $22,$02,$38,$3C   ;;sprite palette

sprites:
     ;vert tile attr horiz
  .db $80, $32, $00, $80   ;sprite 0
  .db $80, $33, $00, $88   ;sprite 1
  .db $88, $34, $00, $80   ;sprite 2
  .db $88, $35, $00, $88   ;sprite 3


background:
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;row 1
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;all sky

  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;row 2
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;all sky

  .db $24,$24,$24,$24,$45,$45,$24,$24,$45,$45,$45,$45,$45,$45,$24,$24  ;;row 3
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$53,$54,$24,$24  ;;some brick tops

  .db $24,$24,$24,$24,$47,$47,$24,$24,$47,$47,$47,$47,$47,$47,$24,$24  ;;row 4
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$55,$56,$24,$24  ;;brick bottoms

attribute:
  .db %00000000, %00010000, %01010000, %00010000, %00000000, %00000000, %00000000, %00110000

  .db $24,$24,$24,$24, $47,$47,$24,$24 ,$47,$47,$47,$47, $47,$47,$24,$24 ,$24,$24,$24,$24 ,$24,$24,$24,$24, $24,$24,$24,$24, $55,$56,$24,$24  ;;brick bottoms



  .org $FFFA     ;first of the three vectors starts here
  .dw NMI        ;when an NMI happens (once per frame if enabled) the
                   ;processor will jump to the label NMI:
  .dw RESET      ;when the processor first turns on or is reset, it will jump
                   ;to the label RESET:
  .dw 0          ;external interrupt IRQ is not used in this tutorial
 
 
;;;;;;;;;;;;;; 
 
 
  .bank 2
  .org $0000
  .incbin "fnaf.chr"   ;includes 8KB graphics file from SMB1


Any sort of help would be much appreciated! :D

Spoiler Alert! I'm porting Five Nights at Freddy's to the NES! Even if you hate FNaF though, please still help me if you can because I'll be able to use this knowledge towards other projects as well. Thanks in advance!
Re: Help with playing .nsf music in a homebrew project.
by on (#173139)
mariogamemaster wrote:
My load, init, and play addresses are as follows, $E6A0, $99A9, and $9CA9.

You've misread all three of these with the wrong endian. Your LOAD/INIT/PLAY are: $A0E6, $A999, and $A99C.

The big hint here is that LOAD should be lower than INIT and PLAY, otherwise INIT/PLAY would be pointing to empty space. There is format reference here, if you need it: http://wiki.nesdev.com/w/index.php/NSF#Header_Overview
Re: Help with playing .nsf music in a homebrew project.
by on (#173140)
rainwarrior wrote:
mariogamemaster wrote:
My load, init, and play addresses are as follows, $E6A0, $99A9, and $9CA9.

You've misread all three of these with the wrong endian. Your LOAD/INIT/PLAY are: $A0E6, $A999, and $A99C.

The big hint here is that LOAD should be lower than INIT and PLAY, otherwise INIT/PLAY would be pointing to empty space. There is format reference here, if you need it: http://wiki.nesdev.com/w/index.php/NSF#Header_Overview


Thanks man! Gonna try out the new addresses right now! I'll tell ya if it works!
EDIT: It didn't work. :( Is there anything else I'm doing wrong?

Here's my new code,
Code:
  .inesprg 1   ; 1x 16KB PRG code
  .ineschr 1   ; 1x  8KB CHR data
  .inesmap 0   ; mapper 0 = NROM, no bank swapping
  .inesmir 1   ; background mirroring
 

;;;;;;;;;;;;;;;


  .bank 0
  .org $A0E6
  .incbin "fnaf.nsf"
  .org $C000
RESET:
  SEI          ; disable IRQs
  CLD          ; disable decimal mode
  LDX #$40
  STX $4017    ; disable APU frame IRQ
  LDX #$FF
  TXS          ; Set up stack
  INX          ; now X = 0
  STX $2000    ; disable NMI
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs
  lda #$01   ;or whatever number song you want
  jsr $A999


vblankwait1:       ; First wait for vblank to make sure PPU is ready
  BIT $2002
  BPL vblankwait1

clrmem:
  LDA #$00
  STA $0000, x
  STA $0100, x
  STA $0300, x
  STA $0400, x
  STA $0500, x
  STA $0600, x
  STA $0700, x
  LDA #$FE
  STA $0200, x
  INX
  BNE clrmem
   
vblankwait2:      ; Second wait for vblank, PPU is ready after this
  BIT $2002
  BPL vblankwait2


LoadPalettes:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$3F
  STA $2006             ; write the high byte of $3F00 address
  LDA #$00
  STA $2006             ; write the low byte of $3F00 address
  LDX #$00              ; start out at 0
LoadPalettesLoop:
  LDA palette, x        ; load data from address (palette + the value in x)
                          ; 1st time through loop it will load palette+0
                          ; 2nd time through loop it will load palette+1
                          ; 3rd time through loop it will load palette+2
                          ; etc
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$20              ; Compare X to hex $10, decimal 16 - copying 16 bytes = 4

sprites
  BNE LoadPalettesLoop  ; Branch to LoadPalettesLoop if compare was Not Equal to zero
                        ; if compare was equal to 32, keep going down



LoadSprites:
  LDX #$00              ; start at 0
LoadSpritesLoop:
  LDA sprites, x        ; load data from address (sprites +  x)
  STA $0200, x          ; store into RAM address ($0200 + x)
  INX                   ; X = X + 1
  CPX #$10              ; Compare X to hex $10, decimal 16
  BNE LoadSpritesLoop   ; Branch to LoadSpritesLoop if compare was Not Equal to zero
                        ; if compare was equal to 16, keep going down
             
             
             
LoadBackground:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$20
  STA $2006             ; write the high byte of $2000 address
  LDA #$00
  STA $2006             ; write the low byte of $2000 address
  LDX #$00              ; start out at 0
LoadBackgroundLoop:
  LDA background, x     ; load data from address (background + the value in x)
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$80              ; Compare X to hex $80, decimal 128 - copying 128 bytes
  BNE LoadBackgroundLoop  ; Branch to LoadBackgroundLoop if compare was Not Equal to

zero
                        ; if compare was equal to 128, keep going down
             
             
LoadAttribute:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$23
  STA $2006             ; write the high byte of $23C0 address
  LDA #$C0
  STA $2006             ; write the low byte of $23C0 address
  LDX #$00              ; start out at 0
LoadAttributeLoop:
  LDA attribute, x      ; load data from address (attribute + the value in x)
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$08              ; Compare X to hex $08, decimal 8 - copying 8 bytes
  BNE LoadAttributeLoop  ; Branch to LoadAttributeLoop if compare was Not Equal to zero
                        ; if compare was equal to 128, keep going down


             
             
             
  LDA #%10010000   ; enable NMI, sprites from Pattern Table 0, background from Pattern

Table 1
  STA $2000

  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001

Forever:
  JMP Forever     ;jump back to Forever, infinite loop
 
 

NMI:
  LDA #$00
  STA $2003       ; set the low byte (00) of the RAM address
  LDA #$02
  STA $4014       ; set the high byte (02) of the RAM address, start the transfer
  jsr $A99C


LatchController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons


ReadA:
  LDA $4016       ; player 1 - A
  AND #%00000001  ; only look at bit 0
  BEQ ReadADone   ; branch to ReadADone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  CLC             ; make sure the carry flag is clear
  ADC #$01        ; A = A + 1
  STA $0203       ; save sprite X position
ReadADone:        ; handling this button is done
 

ReadB:
  LDA $4016       ; player 1 - B
  AND #%00000001  ; only look at bit 0
  BEQ ReadBDone   ; branch to ReadBDone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  SEC             ; make sure carry flag is set
  SBC #$01        ; A = A - 1
  STA $0203       ; save sprite X position
ReadBDone:        ; handling this button is done


  ;;This is the PPU clean up section, so rendering the next frame starts properly.
  LDA #%10010000   ; enable NMI, sprites from Pattern Table 0, background from Pattern

Table 1
  STA $2000
  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001
  LDA #$00        ;;tell the ppu there is no background scrolling
  STA $2005
  STA $2005
 
  RTI             ; return from interrupt
 
;;;;;;;;;;;;;; 
 
 
 
  .bank 1
  .org $E000
palette:
  .db $22,$29,$1A,$0F,  $22,$36,$17,$0F,  $22,$30,$21,$0F,  $22,$27,$17,$0F   

;;background palette
  .db $22,$1C,$15,$14,  $22,$02,$38,$3C,  $22,$1C,$15,$14,  $22,$02,$38,$3C   ;;sprite

palette

sprites:
     ;vert tile attr horiz
  .db $80, $32, $00, $80   ;sprite 0
  .db $80, $33, $00, $88   ;sprite 1
  .db $88, $34, $00, $80   ;sprite 2
  .db $88, $35, $00, $88   ;sprite 3


background:
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;row 1
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;all sky

  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;row 2
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;all sky

  .db $24,$24,$24,$24,$45,$45,$24,$24,$45,$45,$45,$45,$45,$45,$24,$24  ;;row 3
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$53,$54,$24,$24  ;;some brick

tops

  .db $24,$24,$24,$24,$47,$47,$24,$24,$47,$47,$47,$47,$47,$47,$24,$24  ;;row 4
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$55,$56,$24,$24  ;;brick bottoms

attribute:
  .db %00000000, %00010000, %01010000, %00010000, %00000000, %00000000, %00000000,

%00110000

  .db $24,$24,$24,$24, $47,$47,$24,$24 ,$47,$47,$47,$47, $47,$47,$24,$24 ,$24,$24,$24,

$24 ,$24,$24,$24,$24, $24,$24,$24,$24, $55,$56,$24,$24  ;;brick bottoms



  .org $FFFA     ;first of the three vectors starts here
  .dw NMI        ;when an NMI happens (once per frame if enabled) the
                   ;processor will jump to the label NMI:
  .dw RESET      ;when the processor first turns on or is reset, it will jump
                   ;to the label RESET:
  .dw 0          ;external interrupt IRQ is not used in this tutorial
 
 
;;;;;;;;;;;;;; 
 
 
  .bank 2
  .org $0000
  .incbin "fnaf.chr"   ;includes 8KB graphics file from SMB1
Re: Help with playing .nsf music in a homebrew project.
by on (#173143)
You might clarify what "doesn't work" means. Do you know how to use a debugger?

At a glance, I would suggest moving your OAM page to $700 instead of $200. It's likely that Famitracker puts its variables there. I don't see any use of variables in RAM or ZP but similarly put them at the high end of the range; Famitracker will be using space at the low end of RAM and ZP.
Re: Help with playing .nsf music in a homebrew project.
by on (#173144)
NESASM, right?
mariogamemaster wrote:
Code:
  .inesprg 1   ; 1x 16KB PRG code
  .bank 0
  .org $A0E6
  .incbin "fnaf.nsf"
  .org $C000
  .bank 1
  .org $E000

Because NESASM, these look very suspicious to me.
Re: Help with playing .nsf music in a homebrew project.
by on (#173145)
Quote:
You might clarify what "doesn't work" means. Do you know how to use a debugger?

At a glance, I would suggest moving your OAM page to $700 instead of $200. It's likely that Famitracker puts its variables there. I don't see any use of variables in RAM or ZP but similarly put them at the high end of the range; Famitracker will be using space at the low end of RAM and ZP.


Thanks, I'll try that as well. What I mean by "doesn't work" is that when I load the ROM into fceux then It comes out as completely gray screen with none of my graphics. It does play that short sound initialization sound when I start the ROM up though. Nothing else. I do somewhat know how to use a debugger but don't know the basics really I guess.
Re: Help with playing .nsf music in a homebrew project.
by on (#173146)
lidnariq wrote:
NESASM, right?
mariogamemaster wrote:
Code:
  .inesprg 1   ; 1x 16KB PRG code
  .bank 0
  .org $A0E6
  .incbin "fnaf.nsf"
  .org $C000
  .bank 1
  .org $E000

Because NESASM, these look very suspicious to me.


Yes, NESASM I believe. I'm using the NESASM3 Compiler which I believe compiles NESASM.
Re: Help with playing .nsf music in a homebrew project.
by on (#173147)
rainwarrior wrote:
You might clarify what "doesn't work" means. Do you know how to use a debugger?

At a glance, I would suggest moving your OAM page to $700 instead of $200. It's likely that Famitracker puts its variables there. I don't see any use of variables in RAM or ZP but similarly put them at the high end of the range; Famitracker will be using space at the low end of RAM and ZP.


P.S. Where in my code might my OEM page be?
Re: Help with playing .nsf music in a homebrew project.
by on (#173148)
Your fnaf.nsf file seems to be too short.
FNaFSong.nsf is 9498 bytes in size, so removing the first 128 bytes it should be 9370 bytes (yours is 7450)

To edit files I use the HxD hex editor. Open the file - Edit - Select Block - Click the 'length' radio button and enter 128 and make sure dec is selected.
Select 'delete' from the edit menu.
Then 'save as' your new edited file.
Re: Help with playing .nsf music in a homebrew project.
by on (#173149)
Also, clear memory before calling INIT. See: http://wiki.nesdev.com/w/index.php/NSF#Initializing_a_tune
Re: Help with playing .nsf music in a homebrew project.
by on (#173150)
soiaf wrote:
Your fnaf.nsf file seems to be too short.
FNaFSong.nsf is 9498 bytes in size, so removing the first 128 bytes it should be 9370 bytes (yours is 7450)

To edit files I use the HxD hex editor. Open the file - Edit - Select Block - Click the 'length' radio button and enter 128 and make sure dec is selected.
Select 'delete' from the edit menu.
Then 'save as' your new edited file.


Thanks bro! I had a feeling I did something wrong there! Will do.
Re: Help with playing .nsf music in a homebrew project.
by on (#173151)
rainwarrior wrote:
Also, clear memory before calling INIT. See: http://wiki.nesdev.com/w/index.php/NSF#Initializing_a_tune


I see. Could you also tell me where in my code I have to change $200 to $700?

My New Code But Still No Luck,
Code:
  .inesprg 1   ; 1x 16KB PRG code
  .ineschr 1   ; 1x  8KB CHR data
  .inesmap 0   ; mapper 0 = NROM, no bank swapping
  .inesmir 1   ; background mirroring
 

;;;;;;;;;;;;;;;


  .bank 0
  .org $A0E6
  .incbin "fnaf.nsf"
  .org $C000
RESET:
  SEI          ; disable IRQs
  CLD          ; disable decimal mode
  LDX #$40
  STX $4017    ; disable APU frame IRQ
  LDX #$FF
  TXS          ; Set up stack
  INX          ; now X = 0
  STX $2000    ; disable NMI
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs
        ; Init registers for loading the tune
        lda #$00
        ldx #$00
       
        ; Call music init
        jsr $A999


vblankwait1:       ; First wait for vblank to make sure PPU is ready
  BIT $2002
  BPL vblankwait1

clrmem:
  LDA #$00
  STA $0000, x
  STA $0100, x
  STA $0300, x
  STA $0400, x
  STA $0500, x
  STA $0600, x
  STA $0700, x
  LDA #$FE
  STA $0200, x
  INX
  BNE clrmem
   
vblankwait2:      ; Second wait for vblank, PPU is ready after this
  BIT $2002
  BPL vblankwait2


LoadPalettes:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$3F
  STA $2006             ; write the high byte of $3F00 address
  LDA #$00
  STA $2006             ; write the low byte of $3F00 address
  LDX #$00              ; start out at 0
LoadPalettesLoop:
  LDA palette, x        ; load data from address (palette + the value in x)
                          ; 1st time through loop it will load palette+0
                          ; 2nd time through loop it will load palette+1
                          ; 3rd time through loop it will load palette+2
                          ; etc
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$20              ; Compare X to hex $10, decimal 16 - copying 16 bytes = 4

sprites
  BNE LoadPalettesLoop  ; Branch to LoadPalettesLoop if compare was Not Equal to zero
                        ; if compare was equal to 32, keep going down



LoadSprites:
  LDX #$00              ; start at 0
LoadSpritesLoop:
  LDA sprites, x        ; load data from address (sprites +  x)
  STA $0700, x          ; store into RAM address ($0700 + x)
  INX                   ; X = X + 1
  CPX #$10              ; Compare X to hex $10, decimal 16
  BNE LoadSpritesLoop   ; Branch to LoadSpritesLoop if compare was Not Equal to zero
                        ; if compare was equal to 16, keep going down
             
             
             
LoadBackground:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$20
  STA $2006             ; write the high byte of $2000 address
  LDA #$00
  STA $2006             ; write the low byte of $2000 address
  LDX #$00              ; start out at 0
LoadBackgroundLoop:
  LDA background, x     ; load data from address (background + the value in x)
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$80              ; Compare X to hex $80, decimal 128 - copying 128 bytes
  BNE LoadBackgroundLoop  ; Branch to LoadBackgroundLoop if compare was Not Equal to

zero
                        ; if compare was equal to 128, keep going down
             
             
LoadAttribute:
  LDA $2002             ; read PPU status to reset the high/low latch
  LDA #$23
  STA $2006             ; write the high byte of $23C0 address
  LDA #$C0
  STA $2006             ; write the low byte of $23C0 address
  LDX #$00              ; start out at 0
LoadAttributeLoop:
  LDA attribute, x      ; load data from address (attribute + the value in x)
  STA $2007             ; write to PPU
  INX                   ; X = X + 1
  CPX #$08              ; Compare X to hex $08, decimal 8 - copying 8 bytes
  BNE LoadAttributeLoop  ; Branch to LoadAttributeLoop if compare was Not Equal to zero
                        ; if compare was equal to 128, keep going down


             
             
             
  LDA #%10010000   ; enable NMI, sprites from Pattern Table 0, background from Pattern

Table 1
  STA $2000

  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001

Forever:
  JMP Forever     ;jump back to Forever, infinite loop
 
 

NMI:
  LDA #$00
  STA $2003       ; set the low byte (00) of the RAM address
  LDA #$07
  STA $4014       ; set the high byte (02) of the RAM address, start the transfer
  jsr $A99C


LatchController:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016       ; tell both the controllers to latch buttons


ReadA:
  LDA $4016       ; player 1 - A
  AND #%00000001  ; only look at bit 0
  BEQ ReadADone   ; branch to ReadADone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  CLC             ; make sure the carry flag is clear
  ADC #$01        ; A = A + 1
  STA $0203       ; save sprite X position
ReadADone:        ; handling this button is done
 

ReadB:
  LDA $4016       ; player 1 - B
  AND #%00000001  ; only look at bit 0
  BEQ ReadBDone   ; branch to ReadBDone if button is NOT pressed (0)
                  ; add instructions here to do something when button IS pressed (1)
  LDA $0203       ; load sprite X position
  SEC             ; make sure carry flag is set
  SBC #$01        ; A = A - 1
  STA $0203       ; save sprite X position
ReadBDone:        ; handling this button is done


  ;;This is the PPU clean up section, so rendering the next frame starts properly.
  LDA #%10010000   ; enable NMI, sprites from Pattern Table 0, background from Pattern

Table 1
  STA $2000
  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001
  LDA #$00        ;;tell the ppu there is no background scrolling
  STA $2005
  STA $2005
 
  RTI             ; return from interrupt
 
;;;;;;;;;;;;;; 
 
 
 
  .bank 1
  .org $E000
palette:
  .db $22,$29,$1A,$0F,  $22,$36,$17,$0F,  $22,$30,$21,$0F,  $22,$27,$17,$0F   

;;background palette
  .db $22,$1C,$15,$14,  $22,$02,$38,$3C,  $22,$1C,$15,$14,  $22,$02,$38,$3C   ;;sprite

palette

sprites:
     ;vert tile attr horiz
  .db $80, $32, $00, $80   ;sprite 0
  .db $80, $33, $00, $88   ;sprite 1
  .db $88, $34, $00, $80   ;sprite 2
  .db $88, $35, $00, $88   ;sprite 3


background:
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;row 1
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;all sky

  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;row 2
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24  ;;all sky

  .db $24,$24,$24,$24,$45,$45,$24,$24,$45,$45,$45,$45,$45,$45,$24,$24  ;;row 3
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$53,$54,$24,$24  ;;some brick

tops

  .db $24,$24,$24,$24,$47,$47,$24,$24,$47,$47,$47,$47,$47,$47,$24,$24  ;;row 4
  .db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$55,$56,$24,$24  ;;brick bottoms

attribute:
  .db %00000000, %00010000, %01010000, %00010000, %00000000, %00000000, %00000000,

%00110000

  .db $24,$24,$24,$24, $47,$47,$24,$24 ,$47,$47,$47,$47, $47,$47,$24,$24 ,$24,$24,$24,

$24 ,$24,$24,$24,$24, $24,$24,$24,$24, $55,$56,$24,$24  ;;brick bottoms



  .org $FFFA     ;first of the three vectors starts here
  .dw NMI        ;when an NMI happens (once per frame if enabled) the
                   ;processor will jump to the label NMI:
  .dw RESET      ;when the processor first turns on or is reset, it will jump
                   ;to the label RESET:
  .dw 0          ;external interrupt IRQ is not used in this tutorial
 
 
;;;;;;;;;;;;;; 
 
 
  .bank 2
  .org $0000
  ;.incbin "fnaf.chr"   ;includes 8KB graphics file from SMB1


P.S. If possible could someone just edit my code to work then explain to me what they had to do. I know that might be asking a little too much but I really want to add music. Everyone's been a big help so far by the way.
Re: Help with playing .nsf music in a homebrew project.
by on (#173152)
Quote:
moving your OAM page to $700 instead of $200
...
Where?

LoadSprites:
...
STA $0200, x (change to 0700, x)

....

NMI:
LDA #$00
STA $2003
LDA #$02 (change to #$07)
STA $4014
Re: Help with playing .nsf music in a homebrew project.
by on (#173153)
dougeff wrote:
Quote:
moving your OAM page to $700 instead of $200
...
Where?

LoadSprites:
...
STA $0200, x (change to 0700, x)

....

NMI:
LDA #$00
STA $2003
LDA #$02 (change to #$07)
STA $4014


Thanks man!
Re: Help with playing .nsf music in a homebrew project.
by on (#173154)
Can somebody help me figure out the code to do these things?

1. Write $00 to all RAM at $0000-$07FF and $6000-$7FFF.
2. Initialize the sound registers by writing $00 to $4000-$4013, $0F to $4015.
3. Initialize the frame counter to 4-step mode ($40 to $4017).
4. If the tune is bank switched, load the bank values from $070-$077 into $5FF8-$5FFF.
5. Set the A register for the desired song.
6. Set the X register for PAL or NTSC.

And if I'm doing it wrong,

7. Call the music INIT routine.

Thanks in advance
Re: Help with playing .nsf music in a homebrew project.
by on (#173156)
First, I want to make sure you get this bit right...
The reason this is suspicious...
Code:
.inesprg 1   ; 1x 16KB PRG code

  .bank 0
  .org $A0E6


Is because you're clearly using 2 banks of PRG, and it should start at $8000...

Something like this...
Code:
.inesprg 2   ; 2 x 16KB PRG code

  .bank 0
  .org $8000


And maybe some more 'bank' directives, since nesasm complains when you put more than $2000 bytes in a single 'bank'.

Code:

 .bank 0

  .org $8000

 .bank 1

  .org $a000

  .bank 2

  .org $c000


 .bank 3

  .org $e000

 .bank 4

 ;inc graphics file here



I can't remember, it's been a while since I used nesasm.
Re: Help with playing .nsf music in a homebrew project.
by on (#173157)
Mind if I suggest a quick change to ASM6? I don't think you'd have to modify anything other than your headers and we can help you set those up. (It looks like they need to be modified anyway)

NESASM has a lot of bizarre quirks and it's not really preferred around here. It's a bit easier to get help with a more familiar assembler.
Re: Help with playing .nsf music in a homebrew project.
by on (#173172)
dougeff wrote:
And maybe some more 'bank' directives, since nesasm complains when you put more than $2000 bytes in a single 'bank'.


Actually it's even worse, when you overflow a bank, NESASM will build the ROM without giving any error or warning about it. I was dealing with that kind of fun stuff recently, helping someone with their project. When it comes to error messages (or lack of), working with NESASM can be a real nightmare. With NESASM 3 (one of them anyways*) you actually can put more than $2000 bytes in a bank, but if you don't do it right, you're screwed (no error or warning, just a hosed ROM).

*And to make things even more fun, there are 2 different branches of NESASM with different extensions people have added, and I think they have the same version number.. good luck. :P
Re: Help with playing .nsf music in a homebrew project.
by on (#173173)
mariogamemaster wrote:
1. Write $00 to all RAM at $0000-$07FF and $6000-$7FFF.
...
7. Call the music INIT routine.


The code that clears RAM is at the location clrmem: in your code. This has to be done before you call INIT. If you clear RAM after INIT, you're erasing all the work the INIT routine just did and it's like you didn't call it at all.

Also note that the "clrmem" section is filling the region at $0200 with $FE instead of $00. This was for the OAM region at $200 which you have moved to $700, so you've got to swap what you're doing to these two locations. (i.e.: change the line that says STA $0700, x to STA $0200,x and vice versa.)


Memblers wrote:
...when you overflow a bank, NESASM will build the ROM without giving any error or warning about it.

This specifically is why I called it the "worst assembler I have ever used" in another thread. ;)

For mariogamemaster, though, this problem means that when you .incbin your NSF, it's likely that NESASM will silently cut off part of the file, ruining it.

Another piece of advice, instead of trying to remove 128 bytes from the beginning by hand, you might just put -128 on the end of your .org statement at the LOAD address, and include the original NSF file. (You don't need to save that 128 bytes of space.) By shifting the starting address back 128 bytes, you'll put the start of the data where it needs to be.