Help with noob problems in my noob code?

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

Moderator: Moderators

User avatar
Skidlz
Posts: 50
Joined: Sun Nov 11, 2007 5:24 pm

Help with noob problems in my noob code?

Post by Skidlz »

I've spent a lot of time away from NES programming and I'm trying to come back and get a better feel for it.
I'm working on a ball and paddle game and I'm running into problems right away.
My lame collision works as it is. But, if I add so much as a NOP, it hangs upon collision. It also fills the second ram page with what looks like nonsense. What could make it so fragile? I'm not doing a million things between vblanks.

Code: Select all

	.inesprg 1
	.inesmap 0
	.inesmir 1
	.ineschr 1

	.bank 1     
	.org $FFFA
	.dw VBlank_Routine ; address to execute on VBlank
	.dw Start    ; address to execute on reset
	.dw 0        ; no whatever

	.bank 0
	.org $0000
VBlankOrNo:		.db	0
Direction:		.db 0 ;ball direction 0=left
	.org $0300 ; OAM Copy location $0300
	
Sprite0_Y:     .db  0   ; sprite #1's Y value
Sprite0_T:     .db  0   ; sprite #1's Tile Number
Sprite0_S:     .db  0   ; sprite #1's special byte
Sprite0_X:     .db  0   ; sprite #1's X value

Sprite1_Y:     .db  0   ; sprite #1's Y value
Sprite1_T:     .db  0   ; sprite #1's Tile Number
Sprite1_S:     .db  0   ; sprite #1's special byte
Sprite1_X:     .db  0   ; sprite #1's X value

Sprite2_Y:     .db  0   ; sprite #1's Y value
Sprite2_T:     .db  0   ; sprite #1's Tile Number
Sprite2_S:     .db  0   ; sprite #1's special byte
Sprite2_X:     .db  0   ; sprite #1's X value

; this would just go on and on for however many sprites you have
	.org $8000  ; code starts at $8000 or $C000
	
;-------------------------------------------------------------------------
reset:
    sei        ; ignore 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
	stx $2005
	stx $2005

    ; Optional (omitted):
    ; Set up mapper and jmp to further init code here.

    ; First of two waits for vertical blank to make sure that the
    ; PPU has stabilized
vblankwait1:  
    bit $2002
    bpl vblankwait1

    ; We now have about 30,000 cycles to burn before the PPU stabilizes.
    ; Use it to clear RAM.  X is still 0...
    txa
clrmem:
    sta $000,x
    sta $100,x
    sta $200,x
    sta $300,x
    sta $400,x
    sta $500,x
    sta $600,x
    sta $700,x  ; Remove this if you're storing reset-persistent data
    inx
    bne clrmem
;-------------------------------------------------------------------------

VBlank_Routine:
	;start of function to execute on VBlank
	inc VBlankOrNo
	; add one (1) to VBlankOrNo, will be 1 if VBlank, 0 if not.
	rti  ; RTI is (Interrupt RETurn or ReTurn from Interrupt)
	
Start:
	
	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
	
	lda #$20
	sta $2006 ; give $2006 both parts of address $2020.
	sta $2006 

load_name_tables:
; Jam some text into the first name table (at $2400, thanks to mirroring)
        ldy #$00
        ldx #$04
		lda #low(ourMap)
		sta $10
		lda #high(ourMap)
		sta $11
        lda #$20
        sta $2006
        lda #$00
        sta $2006
load_name_tables_loop:
        lda [$10],y
        sta $2007
        iny
        bne load_name_tables_loop
		inc $11
        dex
        bne load_name_tables_loop

setup_Y_X:
	ldx #$0
	stx Sprite0_T
	stx Sprite0_S
	stx Sprite1_S
	stx Sprite2_S
	stx Sprite1_X
	inx
	stx Sprite1_T
	inx
	stx Sprite2_T
	lda #$70
	sta Sprite0_Y
	sta Sprite2_Y
	adc #$7
	sta Sprite1_Y
	lda #$80
	sta Sprite2_X
	
	
	lda #%10001000  ;
	sta $2000       ; 
	lda #%00011110  ; Our typical PPU Setup code.
	sta $2001       ; 
 ; ---------------------------------------------------
 ; Setup is done
 ; ---------------------------------------------------
 
infinite:  ; a label to start our infinite loop
WaitForVBlank:
	lda VBlankOrNo ; A = VBlankOrNO
	cmp #1         ; if A == 1 then is VBlank
	bne WaitForVBlank ; if not VBlank, then loop and do again
	dec VBlankOrNo ; 1-- or VBlankOrNO - 1 . VBlankOrNo will be 0 again.
	; vblank part is done

	lda #3 ; load sprite DMA offset
	sta $4014 ; write sprite offset to PPU

	lda #$01   ; these
	sta $4016  ; lines
	lda #$00   ; setup/strobe the 
	sta $4016  ; keypad.

	lda $4016  ; load Abutton Status 
	and #1
    lda $4016  ; load Bbutton Status
	and #1
	lda $4016  ; load Select Status
	and #1 
	lda $4016  ; load Start Status
	and #1 
	lda $4016  ; load UP Status
	and #1      ; just check the least significant bit aka 00000001
	beq Load_Down ; if it wasn't pushed go to the next button
	dec Sprite0_Y ; if it was pushed move the sprite UP
Load_Down:
	lda $4016  ; load DOWN Status
	and #1      ; just check the least significant bit aka 00000001
	beq Load_Left ; if it wasn't pushed go to the next button
	inc Sprite0_Y ; if it was pushed move the sprite Down
Load_Left:
	lda $4016  ; load LEFT Status
	and #1 
	lda $4016  ; load RIGHT Status
	and #1 
	
	LDA Direction ; zero on start
	BNE Go_Right  ; if 1 go_right
	LDA Sprite0_X
	ADC #8
	CMP Sprite2_X ; see if sprite 1 is on same x and sprite 0
	BNE NO_COL    ; if it isn't keep going left
	LDA Sprite0_Y
	SEC           ; set carry for subtraction
	SBC Sprite2_Y ; subtract sprite 1 y from sprite 0 y to see if it is greater or less
	BPL POS       ; if 1 is less than 0 jump POS
	CLC           ; clear carry for addition
	ADC #8        ; if greater see if it's within 7
	BPL COL       ; if it is, change direction to "right"
	JMP NO_COL    ; if not, keep on going left
POS:
	SBC #8        ; if less see if it's withing 7
	SEC           ; set carry for subtraction
	BMI COL       ; if it is, change direction to "right"
	JMP NO_COL    ; if not, keep on going left
COL:
	INC Direction ; set direction to "right"
	
Go_Right:
	INC Sprite2_X ; moves right
	LDA Sprite2_X
	CMP #247      ; see if we hit the right side of the screen
	BNE infinite  ; if we didn't start the loop over
	dec Direction ; if we did, set direction to "left"
	JMP infinite
NO_COL:
	DEC Sprite2_X ; moves left
	nop
	jmp infinite

tilepal:   .incbin "pong.pal"  ; a label for our pallete data
ourMap: .incbin "pong.map" ; assuming our.map is the binary map file.
	.bank 2
	.org $0000
	.incbin "pong.bkg"
	.incbin "pong.spr"
Thanks a lot.
JunoMan
Posts: 15
Joined: Fri Jul 17, 2009 12:00 am

Re: Help with noob problems in my noob code?

Post by JunoMan »

For a start you need to add a jump over the Vblank_Routine after the clrmem part or was that just a typo?

Juno
User avatar
Skidlz
Posts: 50
Joined: Sun Nov 11, 2007 5:24 pm

Post by Skidlz »

I just copied the start up portion from the web. So if it is a typo, it isn't mine.
I added "JMP Start" after the bne clrmem, but NOP's still crash it.
ccovell
Posts: 1041
Joined: Sun Mar 19, 2006 9:44 pm
Location: Japan
Contact:

Post by ccovell »

You don't have any reset vectors at $FFFA-$FFFF defined, do you?
JunoMan
Posts: 15
Joined: Fri Jul 17, 2009 12:00 am

Post by JunoMan »

I'm not sure if you need it as I see you are following the GBA guy tutorials.

in the NMI you probably need to push the status registers as the inc will change the flags

Code: Select all

VBlank_Routine:
   php
   ;start of function to execute on VBlank
   inc VBlankOrNo
   ; add one (1) to VBlankOrNo, will be 1 if VBlank, 0 if not.
   plp
   rti  ; RTI is (Interrupt RETurn or ReTurn from Interrupt)
User avatar
Skidlz
Posts: 50
Joined: Sun Nov 11, 2007 5:24 pm

Post by Skidlz »

ccovell wrote:You don't have any reset vectors at $FFFA-$FFFF defined, do you?
*My* code doesn't start until the controller checking starts. So, the resets are probably taken from GBA Guy.

This looks like the extent of it.

Code: Select all

	.bank 1     
	.org $FFFA
	.dw VBlank_Routine ; address to execute on VBlank
	.dw Start    ; address to execute on reset
	.dw 0        ; no whatever
I added the php and plp in vblank but it still hangs.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

JunoMan wrote:in the NMI you probably need to push the status registers as the inc will change the flags
Not necessary. On interrupts the flags are automatically pushed, and RTI restores them. You only need to push/restore A, X and Y, if you use them.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Skidlz wrote:

Code: Select all

	.bank 1     
	.org $FFFA
	.dw VBlank_Routine ; address to execute on VBlank
	.dw Start    ; address to execute on reset
	.dw 0        ; no whatever
You used "Start" as the reset point, so none of the code before it (i.e. all the initialization) is running. Change that "Start" to "reset", which is where you want the program to start. You do need that JMP to skip over the VBlank_Routine though. Better yet, move the VBlank_Routine to after the infinite loop.

EDIT: Also, pointing your NMI vector to "0" isn't a good idea either. In case an IRQ happens, the CPU will try to execute RAM variables as if they were code... not good. Just have it point to a location in ROM with an RTI instruction, so that in case of an IRQ it simply returns.
JunoMan
Posts: 15
Joined: Fri Jul 17, 2009 12:00 am

Post by JunoMan »

I can't believe I missed that obvious mistake. Explains why the NMI routine wasn't breaking it when it was being executed (in theory) after the start code.

I guess it would have been easier to debug if the rest of the files were supplied to assemble it.
User avatar
Skidlz
Posts: 50
Joined: Sun Nov 11, 2007 5:24 pm

Post by Skidlz »

JunoMan wrote:I can't believe I missed that obvious mistake. Explains why the NMI routine wasn't breaking it when it was being executed (in theory) after the start code.

I guess it would have been easier to debug if the rest of the files were supplied to assemble it.
I can upload the files somewhere if anyone can use them.
I changed "Start" to reset but the problem persists.
The code currently looks like this:

Code: Select all

	.inesprg 1
	.inesmap 0
	.inesmir 1
	.ineschr 1

	.bank 1     
	.org $FFFA
	.dw VBlank_Routine ; address to execute on VBlank
	.dw reset    ; address to execute on reset
	.dw 0        ; no whatever

	.bank 0
	.org $0000
VBlankOrNo:		.db	0
Direction:		.db 0 ;ball direction 0=left
	.org $0300 ; OAM Copy location $0300
	
Sprite0_Y:     .db  0   ; sprite #1's Y value
Sprite0_T:     .db  0   ; sprite #1's Tile Number
Sprite0_S:     .db  0   ; sprite #1's special byte
Sprite0_X:     .db  0   ; sprite #1's X value

Sprite1_Y:     .db  0   ; sprite #1's Y value
Sprite1_T:     .db  0   ; sprite #1's Tile Number
Sprite1_S:     .db  0   ; sprite #1's special byte
Sprite1_X:     .db  0   ; sprite #1's X value

Sprite2_Y:     .db  0   ; sprite #1's Y value
Sprite2_T:     .db  0   ; sprite #1's Tile Number
Sprite2_S:     .db  0   ; sprite #1's special byte
Sprite2_X:     .db  0   ; sprite #1's X value

; this would just go on and on for however many sprites you have
	.org $8000  ; code starts at $8000 or $C000
	
;-------------------------------------------------------------------------
reset:
    sei        ; ignore 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
	stx $2005
	stx $2005

    ; Optional (omitted):
    ; Set up mapper and jmp to further init code here.

    ; First of two waits for vertical blank to make sure that the
    ; PPU has stabilized
vblankwait1:  
    bit $2002
    bpl vblankwait1

    ; We now have about 30,000 cycles to burn before the PPU stabilizes.
    ; Use it to clear RAM.  X is still 0...
    txa
clrmem:
    sta $000,x
    sta $100,x
    sta $200,x
    sta $300,x
    sta $400,x
    sta $500,x
    sta $600,x
    sta $700,x  ; Remove this if you're storing reset-persistent data
    inx
    bne clrmem
;-------------------------------------------------------------------------
	JMP Start
VBlank_Routine:
	;start of function to execute on VBlank
	inc VBlankOrNo
	; add one (1) to VBlankOrNo, will be 1 if VBlank, 0 if not.
	rti  ; RTI is (Interrupt RETurn or ReTurn from Interrupt)
	
Start:
	
	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
	
	lda #$20
	sta $2006 ; give $2006 both parts of address $2020.
	sta $2006 

load_name_tables:
; Jam some text into the first name table (at $2400, thanks to mirroring)
        ldy #$00
        ldx #$04
		lda #low(ourMap)
		sta $10
		lda #high(ourMap)
		sta $11
        lda #$20
        sta $2006
        lda #$00
        sta $2006
load_name_tables_loop:
        lda [$10],y
        sta $2007
        iny
        bne load_name_tables_loop
		inc $11
        dex
        bne load_name_tables_loop

setup_Y_X:
	ldx #$0
	stx Sprite0_T
	stx Sprite0_S
	stx Sprite1_S
	stx Sprite2_S
	stx Sprite1_X
	inx
	stx Sprite1_T
	inx
	stx Sprite2_T
	lda #$70
	sta Sprite0_Y
	sta Sprite2_Y
	adc #$7
	sta Sprite1_Y
	lda #$80
	sta Sprite2_X
	
	
	lda #%10001000  ;
	sta $2000       ; 
	lda #%00011110  ; Our typical PPU Setup code.
	sta $2001       ; 
 ; ---------------------------------------------------
 ; Setup is done
 ; ---------------------------------------------------
 
infinite:  ; a label to start our infinite loop
WaitForVBlank:
	lda VBlankOrNo ; A = VBlankOrNO
	cmp #1         ; if A == 1 then is VBlank
	bne WaitForVBlank ; if not VBlank, then loop and do again
	dec VBlankOrNo ; 1-- or VBlankOrNO - 1 . VBlankOrNo will be 0 again.
	; vblank part is done

	lda #3 ; load sprite DMA offset
	sta $4014 ; write sprite offset to PPU

	;nop
	lda #$01   ; these
	sta $4016  ; lines
	lda #$00   ; setup/strobe the 
	sta $4016  ; keypad.

	lda $4016  ; load Abutton Status 
	and #1
    lda $4016  ; load Bbutton Status
	and #1
	lda $4016  ; load Select Status
	and #1 
	lda $4016  ; load Start Status
	and #1 
	lda $4016  ; load UP Status
	and #1      ; just check the least significant bit aka 00000001
	beq Load_Down ; if it wasn't pushed go to the next button
	dec Sprite0_Y ; if it was pushed move the sprite UP
Load_Down:
	lda $4016  ; load DOWN Status
	and #1      ; just check the least significant bit aka 00000001
	beq Load_Left ; if it wasn't pushed go to the next button
	inc Sprite0_Y ; if it was pushed move the sprite Down
Load_Left:
	lda $4016  ; load LEFT Status
	and #1 
	lda $4016  ; load RIGHT Status
	and #1 
	
	LDA Direction ; zero on start
	BNE Go_Right  ; if 1 go_right
	LDA Sprite0_X
	ADC #8
	CMP Sprite2_X ; see if sprite 1 is on same x and sprite 0
	BNE NO_COL    ; if it isn't keep going left
	LDA Sprite0_Y
	SEC           ; set carry for subtraction
	SBC Sprite2_Y ; subtract sprite 1 y from sprite 0 y to see if it is greater or less
	BPL POS       ; if 1 is less than 0 jump POS
	CLC           ; clear carry for addition
	ADC #8        ; if greater see if it's within 7
	BPL COL       ; if it is, change direction to "right"
	JMP NO_COL    ; if not, keep on going left
POS:
	SBC #8        ; if less see if it's withing 7
	SEC           ; set carry for subtraction
	BMI COL       ; if it is, change direction to "right"
	JMP NO_COL    ; if not, keep on going left
COL:
	INC Direction ; set direction to "right"
	
Go_Right:
	INC Sprite2_X ; moves right
	LDA Sprite2_X
	CMP #247      ; see if we hit the right side of the screen
	BNE infinite  ; if we didn't start the loop over
	dec Direction ; if we did, set direction to "left"
	JMP infinite
NO_COL:
	DEC Sprite2_X ; moves left
	nop
	jmp infinite

tilepal:   .incbin "pong.pal"  ; a label for our pallete data
ourMap: .incbin "pong.map" ; assuming our.map is the binary map file.
	.bank 2
	.org $0000
	.incbin "pong.bkg"
	.incbin "pong.spr"
Thanks for all the help so far.
JunoMan
Posts: 15
Joined: Fri Jul 17, 2009 12:00 am

Post by JunoMan »

Looks ok.. Can you stick the other files somewhere. That would make it much easier to fix.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Another thing that appears to be wrong (but I'm not sure because I'm not a NESASM user) is that you state that there is only one PRG bank (.inesprg 1) but in fact you have two. I can't see anything else wrong.

What exactly isn't working right when you run it in an emulator?
User avatar
Skidlz
Posts: 50
Joined: Sun Nov 11, 2007 5:24 pm

Post by Skidlz »

JunoMan wrote:Looks ok.. Can you stick the other files somewhere. That would make it much easier to fix.
http://www.mediafire.com/download.php?jymyigz4wyj
tokumaru wrote:Another thing that appears to be wrong (but I'm not sure because I'm not a NESASM user) is that you state that there is only one PRG bank (.inesprg 1) but in fact you have two. I can't see anything else wrong.

What exactly isn't working right when you run it in an emulator?
I saw something in another post about NESASM treating banks as 32K instead of 16K, or some such thing. It assembles so I would guess it's correct.

The demo consists of a ball and a paddle. The ball collides with the paddle and bounces in the unmodified code. If I add *any* opcode at seemingly any point in the code, the demo freezes when the ball hits the paddle.[/quote]
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Skidlz wrote:I saw something in another post about NESASM treating banks as 32K instead of 16K, or some such thing.
It doesn't really matter how NESASM treats them, iNES files always counts PRG banks in 16KB units. Anyway, it's a fact that you are trying to assemble 32KB of PRG ROM, and if ".inesprg 1" writes a 1 to the PRG bank count field in the header, it's wrong. Try using a 2 here and see if it makes any difference.
It assembles so I would guess it's correct.
Well, NESASM is notorious for outputting glitched binaries instead of showing errors. It is known to mess up with addressing modes and such and not telling the user anything. Don't assume it's correct just because it assembles.

BTW, what are you using to test your ROM? Make sure to test on different emulators, and that new and reliable emulators (such as Nestopia and Nintendulator) are in that list. Please tell us if different emulators behave differently.
User avatar
Skidlz
Posts: 50
Joined: Sun Nov 11, 2007 5:24 pm

Post by Skidlz »

It doesn't really matter how NESASM treats them, iNES files always counts PRG banks in 16KB units. Anyway, it's a fact that you are trying to assemble 32KB of PRG ROM, and if ".inesprg 1" writes a 1 to the PRG bank count field in the header, it's wrong. Try using a 2 here and see if it makes any difference.
I get it. If it's writing to the iNES header, all that matters is what ends up in the header.
Well I changed it to ".inesprg 2" and it wont do anything at all.
BTW, what are you using to test your ROM? Make sure to test on different emulators, and that new and reliable emulators (such as Nestopia and Nintendulator) are in that list. Please tell us if different emulators behave differently.
I use FCEUXD/FCEU and it has always worked. I know that doesn't mean much so I downloaded Nestopia.
Nestopia gives the error "CPU Jam!" when I add a NOP. It also refuses to run with ".inesprg 2"
Post Reply