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.
Thanks a lot.
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:
.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"
.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.