Ok, i have been working on programming on the nes for about a week now, and i really like it. I do have a few issues with how picky the hardware can be under some situations, but it seems like there are a lot of tools to make things easier.
Considering the amount of work that goes into a sound engine, i decided to use NSF for music, but as soon as i tried to implemented it, it went completely to hell.
Ok, so here is the deal. I set the NSF file up according to the Nerdtracker documentation (Used hex editor, wrote the LOAD_ADDR, INIT_ADDR, and PLAY_ADDR, then completely deleted the header (128bit, or anything under 0x80.) I then .org'ed the .incbin'ed binary straight to the LOAD_ADDR (because there is no header) and created EQU constants for INIT_ADDR and PLAY_ADDR to the addresses was listed in the NSF header (all addresses were reinterpreted from little endian, since that is how multi-byte values are stored by the 6502.)
Now when go through my initialization, i 'lda #0' and 'ldx #0' (for track 0, and ntsc speed respectively) then 'jsr INIT_ADDR' to set them. I then set a 'jsr PLAY_ADDR' Right in the NMI interrupt routine.
Originally, it would play but it would hijack my program. I figured it had to do with how i would set a flag in NMI to tell the main loop that VBlank was done refreshing. so i replaced that code with the simple NMI detection...
Now that it gets through my full loop before the next NMI interrupt, the music is only reminiscent of what i wrote. The drum beat bleeds through on an occasion, and most of the notes are wrong or distorted.
This is my Main.s file, it has everything i did towards implementing an NSF.
now, 'jsr SpriteSetup' just sets the starting position of the player sprite (into Player_Y1 and Player_X1) and picks the default sprite tiles.
'jsr Draw_Player' takes the player X1 and Y1 values and sets the other player sprite tiles accordingly, then does a DMA transfer from $04 ($0400) to the PPU.
'jsr Read_Controller1' was made to be reuseable, and just clears the P1 buttons, then reads the controller (1) and sets each P1 button to 1 (if pressed.)
lastly, 'jsr Move_Player' just handles the buttons pressed in 'Read_Controller1' by checking screen bounds and then moves the player if there are no collisions with (hardcoded) screen borders.
If this isn't enough information, i will post my full code when my server comes back online.
Any assistance would be apreciated...
EDIT:
Also, adding
causes the whole thing to not work.
I forgot to mention, this is all done in ASM6...
Considering the amount of work that goes into a sound engine, i decided to use NSF for music, but as soon as i tried to implemented it, it went completely to hell.
Ok, so here is the deal. I set the NSF file up according to the Nerdtracker documentation (Used hex editor, wrote the LOAD_ADDR, INIT_ADDR, and PLAY_ADDR, then completely deleted the header (128bit, or anything under 0x80.) I then .org'ed the .incbin'ed binary straight to the LOAD_ADDR (because there is no header) and created EQU constants for INIT_ADDR and PLAY_ADDR to the addresses was listed in the NSF header (all addresses were reinterpreted from little endian, since that is how multi-byte values are stored by the 6502.)
Now when go through my initialization, i 'lda #0' and 'ldx #0' (for track 0, and ntsc speed respectively) then 'jsr INIT_ADDR' to set them. I then set a 'jsr PLAY_ADDR' Right in the NMI interrupt routine.
Originally, it would play but it would hijack my program. I figured it had to do with how i would set a flag in NMI to tell the main loop that VBlank was done refreshing. so i replaced that code with the simple NMI detection...
Code:
- lda $2002
bpl -
bpl -
Now that it gets through my full loop before the next NMI interrupt, the music is only reminiscent of what i wrote. The drum beat bleeds through on an occasion, and most of the notes are wrong or distorted.
This is my Main.s file, it has everything i did towards implementing an NSF.
Code:
; Variables
.enum $0000
;This is for the Vblank function
VBlankOrNo .db 0 ;This WAS used as a VBlank flag.
PMove .db 0 ;Is Player Moving? T/F.
MCount .db 0 ;VBlank Movement Counter
;Music register constants...
INIT_ADDR EQU $B200
PLAY_ADDR EQU $B203
;These will hold a value if pressed, else 0
P1_A .db 0
P1_B .db 0
P1_Select .db 0
P1_Start .db 0
P1_Up .db 0
P1_Down .db 0
P1_Left .db 0
P1_Right .db 0
.ende
;Sprite DMA
.enum $0400
Player_Y1 .db 0 ; a X position for our sprite, start at 20
Player_T1 .db 0 ; Tile Number
Player_S1 .db 0 ; Special byte (lots of stuff)
Player_X1 .db 0 ; a Y position for our sprite, start at 20
Player_Y2 .db 0 ; a X position for our sprite, start at 20
Player_T2 .db 0
Player_S2 .db 0
Player_X2 .db 0 ; a Y position for our sprite, start at 20
Player_Y3 .db 0 ; a X position for our sprite, start at 20
Player_T3 .db 0
Player_S3 .db 0
Player_X3 .db 0 ; a Y position for our sprite, start at 20
Player_Y4 .db 0 ; a X position for our sprite, start at 20
Player_T4 .db 0
Player_S4 .db 0
Player_X4 .db 0 ; a Y position for our sprite, start at 20
Player_Y5 .db 0 ; a X position for our sprite, start at 20
Player_T5 .db 0
Player_S5 .db 0
Player_X5 .db 0 ; a Y position for our sprite, start at 20
Player_Y6 .db 0 ; a X position for our sprite, start at 20
Player_T6 .db 0
Player_S6 .db 0
Player_X6 .db 0 ; a Y position for our sprite, start at 20
.ende
;INES Header
.org $7ff0
.db "NES", $1a ;ID
.db $02 ;2 PRG-ROM pages
.db $01 ;1 CHR-ROM present
.db $00 ;No mapper (unrom) with horizontal mirroring
.dsb $09, $00 ;Clear the rest
;Begin Main Code Here \/
.base $8000
Start:
- lda $2002 ; give the PPU a little time to initialize
bpl - ; by waiting for a vblank
- lda $2002 ; wait for a second vblank to be safe
bpl - ; and now the PPU should be initialized
lda #%10001000 ;
sta $2000 ;
lda #%00011110 ; Our typical PPU Setup code.
sta $2001 ;
ldx #$00 ; clear X ;; start of pallete loading code
lda #$3F ; have $2006 tell
sta $2006 ; $2007 to start
lda #$00 ; at $3F00 (pallete).
sta $2006
loadpal: ; this is a freaky loop
lda tilepal, x ; that gives 32 numbers
sta $2007 ; to $2007, ending when
inx ; X is 32, meaning we
cpx #32 ; are done.
bne loadpal ; if X isn't =32, goto "loadpal:" line.
;; end of pallete loading code
;Chosing song
lda #$00 ;or whatever number song you want
ldx #$00 ;0 for NTSC/1 for PAL
jsr INIT_ADDR
jsr SpriteSetup ;This Function Sets the
;Staring position, Tiles and Special
infinite: ; a label to start our infinite Game loop
;NMI check
WaitForVBlank:
lda $2002 ; wait for a second vblank to be safe
bpl WaitForVBlank ; and now the PPU should be initialized
;now that vblank is ready...
jsr Draw_Player ;This subroutine Draws the player
;now do stuff in preperation of next VBlank
jsr Read_Controller1 ;This Subroutine Read the controller
jsr Move_Player ;This subroutine Moves the player
jmp infinite ;end of game loop,
;-----End of Main loop-----
;Start of Interupt Functions
NMI:
;Play the next Music note
jsr PLAY_ADDR
rti ;Return from interrupt
IRQ:
rti
;Start of includes
tilepal: .incbin "pallete.pal" ; a label for our pallete data
;Now the subroutines
.include "Draw_Player.s"
.include "Move_Player.s"
.include "Read_Controller1.s"
.include "SpriteSetup.s"
;This is where the Music file wants to be stored
.org $aeff ;Start at the LOAD ADDRESS location
.incbin "FirstTry.mus" ; '.mus' file = NSF w/o header
;Now we do the interupt Vector assignments...
.org $fffa
.dw NMI ; First our NMI/VBlank jump
.dw Start ; Next, is our address to execute on reset
.dw IRQ ; IRQ/Break location (nothing yet...)
;Now our binary inclusions (char, bkg, etc...)
.incbin "our.bkg"
.incbin "char1.spr"
.enum $0000
;This is for the Vblank function
VBlankOrNo .db 0 ;This WAS used as a VBlank flag.
PMove .db 0 ;Is Player Moving? T/F.
MCount .db 0 ;VBlank Movement Counter
;Music register constants...
INIT_ADDR EQU $B200
PLAY_ADDR EQU $B203
;These will hold a value if pressed, else 0
P1_A .db 0
P1_B .db 0
P1_Select .db 0
P1_Start .db 0
P1_Up .db 0
P1_Down .db 0
P1_Left .db 0
P1_Right .db 0
.ende
;Sprite DMA
.enum $0400
Player_Y1 .db 0 ; a X position for our sprite, start at 20
Player_T1 .db 0 ; Tile Number
Player_S1 .db 0 ; Special byte (lots of stuff)
Player_X1 .db 0 ; a Y position for our sprite, start at 20
Player_Y2 .db 0 ; a X position for our sprite, start at 20
Player_T2 .db 0
Player_S2 .db 0
Player_X2 .db 0 ; a Y position for our sprite, start at 20
Player_Y3 .db 0 ; a X position for our sprite, start at 20
Player_T3 .db 0
Player_S3 .db 0
Player_X3 .db 0 ; a Y position for our sprite, start at 20
Player_Y4 .db 0 ; a X position for our sprite, start at 20
Player_T4 .db 0
Player_S4 .db 0
Player_X4 .db 0 ; a Y position for our sprite, start at 20
Player_Y5 .db 0 ; a X position for our sprite, start at 20
Player_T5 .db 0
Player_S5 .db 0
Player_X5 .db 0 ; a Y position for our sprite, start at 20
Player_Y6 .db 0 ; a X position for our sprite, start at 20
Player_T6 .db 0
Player_S6 .db 0
Player_X6 .db 0 ; a Y position for our sprite, start at 20
.ende
;INES Header
.org $7ff0
.db "NES", $1a ;ID
.db $02 ;2 PRG-ROM pages
.db $01 ;1 CHR-ROM present
.db $00 ;No mapper (unrom) with horizontal mirroring
.dsb $09, $00 ;Clear the rest
;Begin Main Code Here \/
.base $8000
Start:
- lda $2002 ; give the PPU a little time to initialize
bpl - ; by waiting for a vblank
- lda $2002 ; wait for a second vblank to be safe
bpl - ; and now the PPU should be initialized
lda #%10001000 ;
sta $2000 ;
lda #%00011110 ; Our typical PPU Setup code.
sta $2001 ;
ldx #$00 ; clear X ;; start of pallete loading code
lda #$3F ; have $2006 tell
sta $2006 ; $2007 to start
lda #$00 ; at $3F00 (pallete).
sta $2006
loadpal: ; this is a freaky loop
lda tilepal, x ; that gives 32 numbers
sta $2007 ; to $2007, ending when
inx ; X is 32, meaning we
cpx #32 ; are done.
bne loadpal ; if X isn't =32, goto "loadpal:" line.
;; end of pallete loading code
;Chosing song
lda #$00 ;or whatever number song you want
ldx #$00 ;0 for NTSC/1 for PAL
jsr INIT_ADDR
jsr SpriteSetup ;This Function Sets the
;Staring position, Tiles and Special
infinite: ; a label to start our infinite Game loop
;NMI check
WaitForVBlank:
lda $2002 ; wait for a second vblank to be safe
bpl WaitForVBlank ; and now the PPU should be initialized
;now that vblank is ready...
jsr Draw_Player ;This subroutine Draws the player
;now do stuff in preperation of next VBlank
jsr Read_Controller1 ;This Subroutine Read the controller
jsr Move_Player ;This subroutine Moves the player
jmp infinite ;end of game loop,
;-----End of Main loop-----
;Start of Interupt Functions
NMI:
;Play the next Music note
jsr PLAY_ADDR
rti ;Return from interrupt
IRQ:
rti
;Start of includes
tilepal: .incbin "pallete.pal" ; a label for our pallete data
;Now the subroutines
.include "Draw_Player.s"
.include "Move_Player.s"
.include "Read_Controller1.s"
.include "SpriteSetup.s"
;This is where the Music file wants to be stored
.org $aeff ;Start at the LOAD ADDRESS location
.incbin "FirstTry.mus" ; '.mus' file = NSF w/o header
;Now we do the interupt Vector assignments...
.org $fffa
.dw NMI ; First our NMI/VBlank jump
.dw Start ; Next, is our address to execute on reset
.dw IRQ ; IRQ/Break location (nothing yet...)
;Now our binary inclusions (char, bkg, etc...)
.incbin "our.bkg"
.incbin "char1.spr"
now, 'jsr SpriteSetup' just sets the starting position of the player sprite (into Player_Y1 and Player_X1) and picks the default sprite tiles.
'jsr Draw_Player' takes the player X1 and Y1 values and sets the other player sprite tiles accordingly, then does a DMA transfer from $04 ($0400) to the PPU.
'jsr Read_Controller1' was made to be reuseable, and just clears the P1 buttons, then reads the controller (1) and sets each P1 button to 1 (if pressed.)
lastly, 'jsr Move_Player' just handles the buttons pressed in 'Read_Controller1' by checking screen bounds and then moves the player if there are no collisions with (hardcoded) screen borders.
If this isn't enough information, i will post my full code when my server comes back online.
Any assistance would be apreciated...
EDIT:
Also, adding
Code:
;Enable Play after init
lda #%10000000
sta $2000
lda #%10000000
sta $2000
causes the whole thing to not work.
I forgot to mention, this is all done in ASM6...