After looking through your code I figured that i should optimize it as much as possible. That way you can look through it and see what kind of optimizations can be done and compare it to your original 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
;;;;;;;;;;;;;;;
;; DECLARE SOME VARIABLES HERE
.rsset $0000 ;;start variables at ram location 0
gamestate .rs 1 ; .rs 1 means reserve one byte of space
ballx .rs 1 ; ball horizontal position
bally .rs 1 ; ball vertical position
balldirection .rs 1 ;direction the ball moves
ballspeedx .rs 1 ; ball horizontal speed per frame
ballspeedy .rs 1 ; ball vertical speed per frame
paddle1y .rs 1 ; paddle vertical position
paddle2y .rs 1 ; paddle vertical position
buttons1 .rs 1 ; player 1 gamepad buttons, one bit per button
buttons2 .rs 1 ; player 2 gamepad buttons, one bit per button
scoreOnes .rs 1 ; byte for each digit in the decimal score
scoreTens .rs 1
scoreHundreds .rs 1
scoreOnes2 .rs 1 ; byte for each digit in the decimal score
scoreTens2 .rs 1
scoreHundreds2 .rs 1
;; DECLARE SOME CONSTANTS HERE
STATETITLE = $00 ; displaying title screen
STATEPLAYING = $01 ; move paddles/ball, check for collisions
STATEGAMEOVER = $02 ; displaying game over screen
RIGHTWALL = $F4 ; when ball reaches one of these, do something
TOPWALL = $20
BOTTOMWALL = $E0
LEFTWALL = $04
PADDLE1X = $08 ; horizontal position for paddles, doesnt move
PADDLE2X = $F0
PADDLESPEED = $04
PADDLELENGTH = $10
INITALBALLSPEED = $02
A_BUTTON = %10000000
B_BUTTON = %01000000
SELECT_BUTTON = %00100000
START_BUTTON = %00010000
UP_BUTTON = %00001000
DOWN_BUTTON = %00000100
LEFT_BUTTON = %00000010
RIGHT_BUTTON = %00000001
;;;;;;;;;;;;;;;;;;
.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
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
vblankwait1: ; First wait for vblank to make sure PPU is ready
BIT $2002
BPL vblankwait1
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)
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
ClearNametable: ;writes #$24 to the nametable 1024 times to make a clear screen
LDX #$00 ;X is for the first loop
LDY #$04 ;Y is for the second loop
LDA $2002
LDA #$20
STA $2006
LDA #$00
STA $2006 ;set PPU to $2000
LDA #$24
ClearNametableLoop:
STA $2007 ;store the value
INX
BNE ClearNametableLoop ;this loop runs 256 times
DEY
BNE ClearNametableLoop ;this loop runs 4 times
LDA #$50 ;Set some initial ball stats
STA bally
LDA #$80
STA ballx
LDA #INITALBALLSPEED ;initalize ball speed
STA ballspeedx
STA ballspeedy
LDA #$50 ;initalize paddle positions
STA paddle1y
STA paddle2y
LDA #STATETITLE ;Set starting game state
STA gamestate
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, waiting for NMI
NMI:
LDA #$02
STA $4014 ; set the high byte (02) of the RAM address, start the transfer
JSR DrawScore
JSR DrawScore2
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
JSR ReadController1 ;;get the current button data for player 1
JSR ReadController2 ;;get the current button data for player 2
GameEngine: ;Game state selection (later you might want to make each state a subroutine)
LDA gamestate
CMP #STATETITLE
BEQ EngineTitle ;;game is displaying title screen
CMP #STATEPLAYING
BEQ EnginePlaying ;;game is playing
CMP #STATEGAMEOVER
BEQ EngineGameOver ;;game is displaying ending screen
GameEngineDone:
JSR UpdateSprites ;this is best done every frame
RTI
EngineTitle:
LDA buttons1 ;checks if start button was pressed
AND #START_BUTTON
BEQ EngineTitleBranch
lda #STATEPLAYING ;sets new state if pressed
sta gamestate
EngineTitleBranch:
jmp GameEngineDone ;goes back either way
EngineGameOver: ;similar to engine title but will reset values after start is pressed
LDA buttons1
AND #START_BUTTON
BEQ EngineGameOverBranch
lda #STATEPLAYING
sta gamestate
lda #$00
STA scoreOnes ;clears scores
STA scoreTens
STA scoreHundreds
STA scoreOnes2
STA scoreTens2
STA scoreHundreds2
LDA #$50 ;resets paddles
STA paddle1y
STA paddle2y
EngineGameOverBranch:
jmp GameEngineDone ;have all of your game states go back to somewhere that the rti triggers
EnginePlaying:
jsr MovePaddle1 ;the playing engine
jsr MovePaddle2
jsr MoveBall
jsr CheckBallCollision
jsr CheckPaddleCollision
jmp GameEngineDone
MovePaddle1: ;moves the paddle up or down
lda buttons1
and #UP_BUTTON
beq CheckPaddle1Down ;check if up button pressed otherwise check down pressed
lda paddle1y ;subtracts paddlespeed to move it up
sec
sbc #PADDLESPEED
cmp #TOPWALL ;if it passed through the top wall, fix it to the top wall, otherwise nevermind
bcs Paddle1NotTop
lda #TOPWALL
Paddle1NotTop:
sta paddle1y
rts
CheckPaddle1Down:
lda buttons1 ;same check as above
and #DOWN_BUTTON
beq CheckPaddle1Done
lda paddle1y ;moves the paddle down
clc
adc #PADDLESPEED
cmp #BOTTOMWALL-16 ;checks the bottom wall the same way (the -16 is due to the paddles coordinate being at the top of the paddle)
bcc Paddle1NotBottom
lda #BOTTOMWALL-16
Paddle1NotBottom:
sta paddle1y
CheckPaddle1Done:
rts
MovePaddle2: ;this is identical to above but with paddle 2 instead
lda buttons2
and #UP_BUTTON
beq CheckPaddle2Down
lda paddle2y
sec
sbc #PADDLESPEED
cmp #TOPWALL
bcs Paddle2NotTop
lda #TOPWALL
Paddle2NotTop:
sta paddle2y
rts
CheckPaddle2Down:
lda buttons2
and #DOWN_BUTTON
beq CheckPaddle2Done
lda paddle2y
clc
adc #PADDLESPEED
cmp #BOTTOMWALL-16
bcc Paddle2NotBottom
lda #BOTTOMWALL-16
Paddle2NotBottom:
sta paddle2y
CheckPaddle2Done:
rts
MoveBall: ;this routine simply moves that ball
lda balldirection ;ball direction is now 1 byte, bit 0 is the X direction, bit 1 is the y direction (way more efficent)
and #%00000001 ;teste the x direction (0 for right, 1 for left)
bne BallMoveRight
lda ballx ;moves the ball accordingly
clc
adc ballspeedx
sta ballx
jmp CheckBallY ;afterwards ckeck the y direction
BallMoveRight: ;same as above but in the other direction
lda ballx
sec
sbc ballspeedx
sta ballx
CheckBallY: ;same as above but for Y
lda balldirection
and #%00000010 ;checks the y direction (bit 0 for down, bit 1 for up)
bne BallMoveUp
lda bally
clc
adc ballspeedy
sta bally
jmp MoveBallDone
BallMoveUp:
lda bally
sec
sbc ballspeedy
sta bally
MoveBallDone:
rts
CheckBallCollision: ;this checks the four sides of the screen
lda bally
cmp #TOPWALL ;checks the top wall (similar to the paddles)
bcs CheckBottomWall
lda #TOPWALL ;fixes the ball's y position to the edge of the wall
sta bally
lda balldirection
eor #%00000010 ;switches the ball's y direction by toggling the bit
sta balldirection
jmp CheckLeftWall
CheckBottomWall:
cmp #BOTTOMWALL-8 ;same as top wall
bcc CheckLeftWall
lda #BOTTOMWALL-8
sta bally
lda balldirection
eor #%00000010
sta balldirection
CheckLeftWall: ;checks the side wall
lda ballx
cmp #LEFTWALL
bcs CheckRightWall
jsr IncrementScore2 ;if it hit the wall, increase the score
lda #$80 ;then rest the ball's position
sta ballx
sta bally
lda balldirection
eor #%00000001 ;and change it's direction
sta balldirection
rts
CheckRightWall: ;same as left wall
cmp #RIGHTWALL
bcc CheckBallCollisionDone
jsr IncrementScore1
lda #$80
sta ballx
sta bally
lda balldirection
eor #%00000001
sta balldirection
CheckBallCollisionDone:
rts
CheckPaddleCollision: ;this checks if the ball hit a paddle
lda ballx
cmp #PADDLE1X+8 ;test if the ballx is within the paddle area
bcs CheckOtherPaddle
lda paddle1y ;if so, test if bally is within the paddle's top coordinate
sec
sbc #$08 ;the sub 8 is to ensure that the bottom of the ball collides with the top of the paddle
cmp bally
bcs CheckOtherPaddle ;if this fails go test the other paddle
lda paddle1y
clc
adc #PADDLELENGTH ;this tests the bottom of the paddle (16 is added to ensure that the top of the ball collides with the bottom of the paddle
cmp bally
bcc CheckOtherPaddle
lda balldirection ;if the tests were sucessful then flip the x direction of the ball
eor #%00000001
sta balldirection
lda #PADDLE1X+8 ;and fix ballx to the edge of the paddle (otherwise causes wierd glitches)
sta ballx
CheckOtherPaddle: ;same as paddle 1
lda ballx
cmp #PADDLE2X-8
bcc SkipCollision
lda paddle2y
sec
sbc #$08
cmp bally
bcs SkipCollision
lda paddle2y
clc
adc #PADDLELENGTH
cmp bally
bcc SkipCollision
lda balldirection
eor #%00000001
sta balldirection
lda #PADDLE2X-8
sta ballx
SkipCollision:
rts
UpdateSprites: ;all sprite update should be done in one pass
LDA bally ;ball
STA $0200
LDA #$75
STA $0201
LDA #$00
STA $0202
LDA ballx
STA $0203
LDA paddle1y ;left paddle top tile
STA $0204
LDA #$7F
STA $0205
LDA #$00
STA $0206
LDA #PADDLE1X
STA $0207
LDA paddle1y ;left paddle bottom tile
CLC
ADC #$08 ;Add 8 to put it beneath the other tile
STA $0208
LDA #$7F
STA $0209
LDA #$00
STA $020A
LDA #PADDLE1X
STA $020B
LDA paddle2y ;right paddle top tile
STA $020C
LDA #$7F
STA $020D
LDA #$00
STA $020E
LDA #PADDLE2X
STA $020F
LDA paddle2y ;right paddle bottom tile
CLC
ADC #$08 ;Add 8 to put it beneath the other tile
STA $0210
LDA #$7F
STA $0211
LDA #$00
STA $0212
LDA #PADDLE2X
STA $0213
RTS
DrawScore:
LDA $2002
LDA #$20
STA $2006
LDA #$21
STA $2006 ; start drawing the score at PPU $2021
LDA scoreHundreds
STA $2007
LDA scoreTens
STA $2007
LDA scoreOnes
STA $2007
RTS
DrawScore2:
LDA $2002
LDA #$20
STA $2006
LDA #$3C
STA $2006 ; start drawing the score at PPU $203C
LDA scoreHundreds2
STA $2007
LDA scoreTens2
STA $2007
LDA scoreOnes2
STA $2007
RTS
IncrementScore1: ;increases the score by one and sets the gameover flag if it hits 100
inc scoreOnes
lda scoreOnes
cmp #$0A
bne IncrementScore1Done
lda #$00
sta scoreOnes
inc scoreTens
lda scoreTens
cmp #$0A
bne IncrementScore1Done
lda #$00
sta scoreTens
inc scoreHundreds
lda #STATEGAMEOVER
sta gamestate
IncrementScore1Done:
rts
IncrementScore2:
inc scoreOnes2
lda scoreOnes2
cmp #$0A
bne IncrementScore2Done
lda #$00
sta scoreOnes2
inc scoreTens2
lda scoreTens2
cmp #$0A
bne IncrementScore2Done
lda #$00
sta scoreTens2
inc scoreHundreds2
lda #STATEGAMEOVER
sta gamestate
IncrementScore2Done:
rts
ReadController1:
LDA #$01
STA $4016
LDA #$00
STA $4016
LDX #$08
ReadController1Loop:
LDA $4016
LSR A ; bit0 -> Carry
ROL buttons1 ; bit0 <- Carry
DEX
BNE ReadController1Loop
RTS
ReadController2:
LDA #$01
STA $4016
LDA #$00
STA $4016
LDX #$08
ReadController2Loop:
LDA $4017
LSR A ; bit0 -> Carry
ROL buttons2 ; bit0 <- Carry
DEX
BNE ReadController2Loop
RTS
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
.bank 1
.org $E000
.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 "mario.chr" ;includes 8KB graphics file from SMB