Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

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

Moderator: Moderators

Post Reply
justin-rwx
Posts: 12
Joined: Sun Jan 10, 2021 1:12 pm

Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

Post by justin-rwx » Mon Jan 18, 2021 9:11 pm

I've reached the point in the Nerdy-Night tutorial where I am let loose to go and develop a Pong clone using the tools provided so far. Also note that in addition the the Nerdy-Nights tutorial as a resource for developing the Pong clone, I am using the code I found here from a developer who was following the same tutorial: https://github.com/TomBrannan/NES-Pong

Right now my goal is to have the title screen appear as soon as the game starts and have it move into the playing phase after P1 presses start. I am having issues getting the title screen to appear at all. I've used the NES Screen Tool to develop the title screen shown below

Image

This is what I get from FCEUXDSP after compiling
Image

This is the result of the Nametable Viewer tool in FCEUXDSP
Image

For clarity, I won't put the entire code this first go-around if I don't need to, but below is the basic structure that might be applicable to the issue. I follow code with missing sections using "[...]". Am I missing something, or maybe misunderstanding Nametables/attributes?

Code: Select all

;--------------------------------------------------;
;                    Variables                     ;
;--------------------------------------------------;
  .rsset $0000  ; start variables at ram location 0
gamestate  .rs 1  ; .rs 1 means reserve one byte of space
[...]

;--------------------------------------------------;
;                    Constants                     ;
;--------------------------------------------------;
STATE_TITLE      = $00  ; displaying title screen
STATE_PLAYING    = $01  ; move paddles/ball, check for collisions
STATE_GAMEOVER   = $02  ; displaying game over screen

RESET: 
  [...]
  jsr VBlankWait
clrmem:
  [...]
  jsr VBlankWait

  ; Initialize game state
  LDA #STATE_TITLE
  STA gamestate

  ; Initialize Graphics
  jsr LoadPalettes
  jsr LoadNametable
  jsr LoadAttribute
  
  ; Initialize PPU Settings (enable Sprites and background)
  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

; Infinite Loop
Forever:
  jmp Forever     ;jump back to Forever, infinite loop, waiting for NMI

;--------------------------------------------------;
;                      NMI                         ;
;--------------------------------------------------;
NMI: [...]

;;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
  
  GameEngine:  
  LDA gamestate
  CMP #STATE_TITLE
  BEQ EngineTitle    ;;game is displaying title screen
  LDA gamestate
  CMP #STATE_GAMEOVER
  BEQ EngineGameOver  ;;game is displaying ending screen
  LDA gamestate
  CMP #STATE_PLAYING
  BEQ EnginePlaying   ;;game is playing
GameEngineDone:  
  
  RTI             ; return from interrupt

EngineTitle:
  jsr InitializeVariables ;;  if start button pressed
  lda #%00000000          ;;  turn screen off
  sta $2000 
  sta $2001 
  jsr LoadNametable
  jsr LoadAttribute
  jsr LoadPalettes        ;;  load game screen
  jsr LoadSprites
  lda #%10001000          ;;  turn screen on
  sta $2000

  lda #STATE_TITLE
  sta gamestate

  jsr CheckStart

  JMP GameEngineDone
  [...]
  
;---------------------------;
;     SUBROUTINES         ;
;---------------------------;
  CheckStart:
  lda buttons1
  and #START_PRESSED
  BEQ CheckStartDone
  LDA #STATE_PLAYING
  STA gamestate
CheckStartDone:
  RTS
  
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
.loop
  lda Palette_Title, x        ; load data from address (palette + the value in x)
  sta $2007             ; write to PPU
  inx                   ; X = X + 1
  cpx #$10             ; Compare X to hex $10, decimal 16 - copying 16 bytes = 4 sprites
  bne .loop  ; Branch to LoadPalettesLoop if compare was Not Equal to zero
  rts

LoadNametable:
  lda $2002
  lda #$20
  sta $2006
  lda #$00
  sta $2006
  ldx #$00
.loop
  lda Nametable_Title, x
  sta $2007
  inx
  cpx #$80
  bne .loop
  rts

LoadAttribute:
  lda $2002
  lda #$23
  sta $2006
  lda #$C0
  sta $2006
  ldx #$00
.loop
  lda Attribute_Title, x
  sta $2007
  inx
  cpx #$08
  bne .loop
  rts

;--------------------------------------------------;
;                   Nametables                           ;
;--------------------------------------------------;
Nametable_Title:
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$19,$24,$24,$24,$24,$18,$24,$24,$24,$24,$17,$24,$24,$24,$24,$10,$24,$24,$24,$24,$24,$24,$24,$24 ; the letters 'P', 'O', 'N', and 'G' here
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.db $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
Nametable_Title_End:

;--------------------------------------------------;
;                   Attributes                     ;
;--------------------------------------------------;
Attribute_Title:
  .db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
  .db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
  .db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
Attribute_Title_End:

;--------------------------------------------------;
;                   Palettes                       ;
;--------------------------------------------------;
Palette_Title:
  .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
Palette_Title_End

User avatar
Controllerhead
Posts: 218
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

Post by Controllerhead » Mon Jan 18, 2021 11:06 pm

If your current nametable is filled and your screen is blank, looks like PPU rendering might be disabled?

Writing 1 to bit 3 to $2001 should make those tiles show up (provided that is the issue)
https://wiki.nesdev.com/w/index.php/PPU ... rs#PPUMASK

Posting source would be helpful to get you up and going...
Image

justin-rwx
Posts: 12
Joined: Sun Jan 10, 2021 1:12 pm

Re: Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

Post by justin-rwx » Mon Jan 18, 2021 11:47 pm

Thanks for the response.

A bit of an update: In the EngineTitle block, I changed lda #%10001000 to lda #%10010000 - flipping the sprite/background pattern table
And Controllerhead - you were right. I needed to rewrite LDA #%00011110 in the EngineTitle block when I was turning the screen back on

Now my Title Screen is partially working... now the issue is that it's only partially loaded. I even set the LoadNametable subroutine to loop #$FF times, but the title screen only loads as far as shown below. What's the preferred method for loading an entire title screen, or for that matter, filling the whole screen with the Nametable tiles as saved in the Nametable memory location? It seems that the method described by Nerdy-Nights will only load part of a background onto the screen.
Image

The source code without redactions is below.

Code: Select all

;--------------------------------------------------;
;                   iNES Headers                   ;
;--------------------------------------------------;
  .inesprg 1   ; 1x 16KB PRG code
  .ineschr 1   ; 1x  8KB CHR data
  .inesmap 0   ; mapper 0 = NROM, no bank swapping
  .inesmir 1   ; background mirroring

;--------------------------------------------------;
;                    Variables                     ;
;--------------------------------------------------;
  .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
ballup     .rs 1  ; 1 = ball moving up
balldown   .rs 1  ; 1 = ball moving down
ballleft   .rs 1  ; 1 = ball moving left
ballright  .rs 1  ; 1 = ball moving right
ballspeedx .rs 1  ; ball horizontal speed per frame
ballspeedy .rs 1  ; ball vertical speed per frame
paddle1ytop .rs 1 ; player 1 paddle top vertical position
paddle2ytop .rs 1 ; Player 2 paddle top vertical position
paddle1ybot .rs 1 ; Player 1 paddle bottom vertical position
paddle2ybot .rs 1 ; player 2 paddle bottom vertical position
buttons1   .rs 1  ; player 1 gamepad buttons, one bit per button
buttons2   .rs 1  ; player 2 gamepad buttons, one bit per button
score1     .rs 1  ; player 1 score, 0-15
score2     .rs 1  ; player 2 score, 0-15

;--------------------------------------------------;
;                    Constants                     ;
;--------------------------------------------------;
STATE_TITLE      = $00  ; displaying title screen
STATE_PLAYING    = $01  ; move paddles/ball, check for collisions
STATE_GAMEOVER   = $02  ; displaying game over screen
SPRITE_LOCATION = $0200  
RIGHTWALL       = $F4  ; when ball reaches one of these, do something
TOPWALL         = $06
BOTTOMWALL      = $EB
LEFTWALL        = $04
PADDLE1X        = $08  ; horizontal position for paddles, doesnt move
PADDLE2X        = $ED
PADDLE_SPEED    = $05
SPRITE_SIZE     = $08
A_PRESSED       = $80
B_PRESSED       = $40
SEL_PRESSED     = $20
START_PRESSED   = $10
UP_PRESSED      = $08
DOWN_PRESSED    = $04
LEFT_PRESSED    = $02
RIGHT_PRESSED   = $01

;--------------------------------------------------;
;                    Bank 0                        ;
;--------------------------------------------------;
  .bank 0
  .org $C000 

;--------------------------------------------------;
;                     RESET                        ;
;--------------------------------------------------;
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 - I assume to allow for memory clearing
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs

  jsr VBlankWait

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
  
  jsr VBlankWait

  ; Initialize Variables
  jsr InitializeVariables

  ; Initialize game state
  LDA #STATE_TITLE
  STA gamestate

  ; Initialize Graphics
  jsr LoadPalettes
  jsr LoadSprites
  jsr LoadNametable
  jsr LoadAttribute
  
  ; Initialize PPU Settings (enable Sprites and background)
  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

; Infinite Loop
Forever:
  jmp Forever     ;jump back to Forever, infinite loop, waiting for NMI
  
;--------------------------------------------------;
;                      NMI                         ;
;--------------------------------------------------;
NMI:
  LDA #$00        ; Sprite location low byte
  STA $2003       ; set the low byte (00) of the RAM address
  LDA #$02        ; Sprite location high byte
  STA $4014       ; set the high byte (02) of the RAM address, start the transfer

  ;jsr DrawScore

  ;;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

  JSR ReadController1  ; get the current button data for player 1
  JSR ReadController2  ; get the current button data for player 2
  
GameEngine:  
  LDA gamestate
  CMP #STATE_TITLE
  BEQ EngineTitle    ;;game is displaying title screen
  LDA gamestate
  CMP #STATE_GAMEOVER
  BEQ EngineGameOver  ;;game is displaying ending screen
  LDA gamestate
  CMP #STATE_PLAYING
  BEQ EnginePlaying   ;;game is playing
GameEngineDone:  
  
  JSR UpdateSprites  ;;set ball/paddle sprites from positions
  RTI             ; return from interrupt
 
EngineTitle:
  jsr InitializeVariables ;;  if start button pressed
  lda #%00000000          ;;  turn screen off
  sta $2000 
  sta $2001 
  jsr LoadNametable
  jsr LoadAttribute
  jsr LoadPalettes        ;;  load game screen
  jsr LoadSprites
  lda #%10010000          ;;  turn screen on
  sta $2000
  LDA #%00011110   ; enable sprites, enable background, no clipping on left side
  STA $2001

  lda #STATE_TITLE
  sta gamestate

  jsr CheckStart

  JMP GameEngineDone
;;;;;;;;; 
 
EngineGameOver:
  ;;if start button pressed
  ;;  turn screen off
  ;;  load title screen
  ;;  go to Title State
  ;;  turn screen on 
  JMP GameEngineDone
 
;;;;;;;;;;;
 
EnginePlaying:
  jsr MoveBallRight
  jsr MoveBallLeft
  jsr MoveBallUp
  jsr MoveBallDown
  jsr MovePaddle1Up
  jsr MovePaddle1Down
  jsr MovePaddle2Up
  jsr MovePaddle2Down
  jsr CheckPaddle1Collision
  jsr CheckPaddle2Collision
  jmp GameEngineDone
  
;---------------------------;
;     SUBROUTINES           ;
;---------------------------;
MoveBallRight:
  LDA ballright
  BEQ MoveBallRightDone   ;;if ballright=0, skip this section

  LDA ballx
  CLC
  ADC ballspeedx        ;;ballx position = ballx + ballspeedx
  STA ballx

  LDA ballx
  CMP #RIGHTWALL
  BCC MoveBallRightDone      ;;if ball x < right wall, still on screen, skip next section
  LDA #$00
  STA ballright
  LDA #$01
  STA ballleft         ;;bounce, ball now moving left
  ;;in real game, give point to player 1, reset ball
MoveBallRightDone:
  rts

MoveBallLeft:
  LDA ballleft
  BEQ MoveBallLeftDone   ;;if ballleft=0, skip this section

  LDA ballx
  SEC
  SBC ballspeedx        ;;ballx position = ballx - ballspeedx
  STA ballx

  LDA ballx
  CMP #LEFTWALL
  BCS MoveBallLeftDone      ;;if ball x > left wall, still on screen, skip next section
  LDA #$01
  STA ballright
  LDA #$00
  STA ballleft         ;;bounce, ball now moving right
  ;;in real game, give point to player 2, reset ball
MoveBallLeftDone:
  rts

MoveBallUp:
  LDA ballup
  BEQ MoveBallUpDone   ;;if ballup=0, skip this section

  LDA bally
  SEC
  SBC ballspeedy        ;;bally position = bally - ballspeedy
  STA bally

  LDA bally
  CMP #TOPWALL
  BCS MoveBallUpDone      ;;if ball y > top wall, still on screen, skip next section
  LDA #$01
  STA balldown
  LDA #$00
  STA ballup         ;;bounce, ball now moving down
MoveBallUpDone:
  rts

MoveBallDown:
  LDA balldown
  BEQ MoveBallDownDone   ;;if ballup=0, skip this section

  LDA bally
  CLC
  ADC ballspeedy        ;;bally position = bally + ballspeedy
  STA bally

  LDA bally
  CMP #BOTTOMWALL
  BCC MoveBallDownDone      ;;if ball y < bottom wall, still on screen, skip next section
  LDA #$00
  STA balldown
  LDA #$01
  STA ballup         ;;bounce, ball now moving down
MoveBallDownDone:
  rts

MovePaddle1Up:
  lda buttons1
  and #UP_PRESSED ; masks all bits except the up button. Sets zero flag to 1 if true
  beq MovePaddle1UpDone ;skip all below if zero flag is 0 (the above logic is not true)
  lda paddle1ytop ;if paddle top > top wall
  cmp #TOPWALL ;sets carry flag if A >= M and sets zero flag if A = M
  bcc MovePaddle1UpDone ; branches if A <= M
  sec   ; Update paddle sprite
  lda paddle1ytop
  sbc #PADDLE_SPEED ; sbc subtracts from the paddle's current top y position
  sta paddle1ytop
  sec
  lda paddle1ybot
  sbc #PADDLE_SPEED
  sta paddle1ybot
MovePaddle1UpDone:
  rts

MovePaddle1Down:
  lda buttons1
  and #DOWN_PRESSED ; masks all bits except the up button. Sets zero flag to 1 if true
  beq MovePaddle1DownDone ;skip all below if zero flag is 0 (the above logic is not true)
  lda paddle1ybot ;if paddle top > top wall
  cmp #BOTTOMWALL ;sets carry flag if A >= M and sets zero flag if A = M 
  bcs MovePaddle1DownDone ; branches if A >= M
  clc
  lda paddle1ytop
  adc #PADDLE_SPEED ; adc adds to the paddle's current top y position
  sta paddle1ytop
  clc
  lda paddle1ybot ; paddle bottom position must be maintained to ensure the ball knows where the paddle boundaries are
  adc #PADDLE_SPEED
  sta paddle1ybot
MovePaddle1DownDone:
  rts

MovePaddle2Up:
  lda buttons2
  and #UP_PRESSED ; masks all bits except the up button. Sets zero flag to 1 if true
  beq MovePaddle2UpDone ;skip all below if zero flag is 0 (the above logic is not true)
  lda paddle2ytop ;if paddle top > top wall
  cmp #TOPWALL ;sets carry flag if A >= M and sets zero flag if A = M ; 
  bcc MovePaddle2UpDone ; branches if A <= M
  sec   ; Update paddle sprite
  lda paddle2ytop
  sbc #PADDLE_SPEED ; sbc subtracts from the paddle's current top y position
  sta paddle2ytop
  sec
  lda paddle2ybot
  sbc #PADDLE_SPEED
  sta paddle2ybot
MovePaddle2UpDone:
  rts

MovePaddle2Down:
  lda buttons2
  and #DOWN_PRESSED ; masks all bits except the up button. Sets zero flag to 1 if true
  beq MovePaddle2DownDone ;skip all below if zero flag is 0 (the above logic is not true)
  lda paddle2ybot ;if paddle top > top wall
  cmp #BOTTOMWALL ;sets carry flag if A >= M and sets zero flag if A = M ;
  bcs MovePaddle2DownDone ; branches if A >= M
  clc
  lda paddle2ytop
  adc #PADDLE_SPEED ; adc adds to the paddle's current top y position
  sta paddle2ytop
  clc
  lda paddle2ybot ; paddle bottom position must be maintained to ensure the ball knows where the paddle boundaries are
  adc #PADDLE_SPEED
  sta paddle2ybot
MovePaddle2DownDone:
  rts

CheckPaddle1Collision:
  lda ballx ; if ball x < paddle1x
  cmp #PADDLE1X+SPRITE_SIZE ; adds the sprite size to the paddle's x position so it appears that the ball is bouncing off of the paddle
  bcs CheckPaddle1CollisionDone ; continue if the ballx is <= PADDLE1X
  lda bally
  cmp paddle1ytop
  bcc CheckPaddle1CollisionDone ; continue if the bally is >= paddle1ytop (lower on the screen)
  cmp paddle1ybot
  bcs CheckPaddle1CollisionDone ; continue if the bally is <= paddle1ybot (higher on the screen)
  lda #$01  ; ball changes direction to the right
  sta ballright
  lda #$00
  sta ballleft
CheckPaddle1CollisionDone:
  rts

CheckPaddle2Collision:
  lda ballx ; if ball x < paddle1x
  cmp #PADDLE2X-SPRITE_SIZE ; adds the sprite size to the paddle's x position so it appears that the ball is bouncing off of the paddle
  bcc CheckPaddle2CollisionDone ; continue if the ballx is <= PADDLE1X
  lda bally
  cmp paddle2ytop
  bcc CheckPaddle2CollisionDone ; continue if the bally is >= paddle1ytop (lower on the screen)
  cmp paddle2ybot
  bcs CheckPaddle2CollisionDone ; continue if the bally is <= paddle1ybot (higher on the screen)
  lda #$00  ; ball changes direction to the right
  sta ballright
  lda #$01
  sta ballleft
CheckPaddle2CollisionDone:
  rts

UpdateSprites: ; Updates all sprite info
  lda bally
  sta SPRITE_LOCATION + (Sprite_Ball - Sprites) ; $0200 + (the quantity of addresses between Sprite_Ball and Sprites)
  lda ballx
  sta SPRITE_LOCATION + (Sprite_Ball - Sprites) + 3 ; Adds 3 to the address to reach the last byte (x coordinate) in the ball sprite

  ldy #$00
  ldx #$00
  lda paddle1ytop
.loop1
  clc
  sta SPRITE_LOCATION + (Sprite_Paddle1 - Sprites), y 
  adc #$08 ; 8 pixels below the top of the previous tile, draw the next tile (starting from the top). This keeps the all tiles of the sprite together
  iny
  iny
  iny
  iny ; four iny will cycle through each 4 bytes of a tile, accessing the byte for the vertical position
  inx 
  cpx #((Sprite_Paddle1_End - Sprite_Paddle1)/4) ; compare x with the number of tiles in the sprite
  bne .loop1 ; if more tiles still need to be counted, return to .loop1

  ldy #$00
  ldx #$00
  lda paddle2ytop
.loop2
  clc
  sta SPRITE_LOCATION + (Sprite_Paddle2 - Sprites), y
  adc #$08
  iny
  iny
  iny
  iny
  inx
  cpx #((Sprite_Paddle2_End - Sprite_Paddle2)/4)
  bne .loop2
  rts
 
;DrawScore:
  ;;draw score on screen using background tiles
  ;;or using many sprites
  ;RTS
 
ReadController1:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016
  LDX #$08
.loop
  LDA $4016
  LSR A            ; bit0 -> Carry
  ROL buttons1     ; bit0 <- Carry - absSuplr
  DEX
  BNE .loop
  RTS
  
ReadController2:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016
  LDX #$08
.loop
  LDA $4017
  LSR A            ; bit0 -> Carry
  ROL buttons2     ; bit0 <- Carry
  DEX
  BNE .loop
  RTS  

CheckStart:
  lda buttons1
  and #START_PRESSED
  BEQ CheckStartDone
  LDA #STATE_PLAYING
  STA gamestate
CheckStartDone:
  RTS
  
LoadSprites:
  ldx #$00
.loop
  lda Sprites, x
  sta SPRITE_LOCATION, x
  inx
  cpx #(Sprites_End-Sprites)
  bne .loop
  rts

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
.loop
  lda Palette_Title, x        ; load data from address (palette + the value in x)
  sta $2007             ; write to PPU
  inx                   ; X = X + 1
  cpx #$10             ; Compare X to hex $10, decimal 16 - copying 16 bytes = 4 sprites
  bne .loop  ; Branch to LoadPalettesLoop if compare was Not Equal to zero
  rts

LoadNametable:
  lda $2002
  lda #$20
  sta $2006
  lda #$00
  sta $2006
  ldx #$00
.loop
  lda Nametable_Title, x
  sta $2007
  inx
  ;cpx #(Nametable_Title_End-Nametable_Title)
  cpx #$80
  bne .loop
  rts

LoadAttribute:
  lda $2002
  lda #$23
  sta $2006
  lda #$C0
  sta $2006
  ldx #$00
.loop
  lda Attribute_Title, x
  sta $2007
  inx
  ;cpx #(Attribute_Title_End-Attribute_Title)
  cpx #$08
  bne .loop
  rts

InitializeVariables: ;Set some initial ball and paddle stats
  LDA #$01
  STA balldown
  STA ballright
  LDA #$00
  STA ballup
  STA ballleft
  LDA #$50
  STA bally
  LDA #$80
  STA ballx
  LDA #$02
  STA ballspeedx
  STA ballspeedy
  lda #$80
  sta paddle1ytop
  sta paddle2ytop
  lda #$80+8*4
  sta paddle1ybot
  sta paddle2ybot;sta paddle2ybot
  rts

VBlankWait
  bit $2002
  bpl VBlankWait
  rts
        
;---------------------------;
;     Bank 1                ;
;---------------------------; 
  .bank 1
  .org $A000

;--------------------------------------------------;
;                   Nametables                     ;
;--------------------------------------------------;
Nametable_Title:
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$b0,$91,$91,$91,$91,$91,$91,$91,$91,$91,$91
	.byte $91,$91,$91,$91,$91,$91,$91,$91,$91,$91,$b2,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$96,$96,$96,$96,$96,$96,$96,$96,$96,$96,$96
	.byte $96,$96,$96,$96,$96,$96,$96,$96,$96,$96,$96,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$96,$96,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$96,$96,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$24,$19,$24,$24,$24,$24,$18,$24,$24
	.byte $24,$24,$17,$24,$24,$24,$24,$10,$24,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$96,$96,$96,$96,$96,$96,$96,$96,$96
	.byte $96,$96,$96,$96,$96,$96,$96,$96,$96,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6
	.byte $f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6
	.byte $f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6
	.byte $f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
  	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6
	.byte $f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
Nametable_Title_End:

;--------------------------------------------------;
;                   Attributes                     ;
;--------------------------------------------------;
Attribute_Title:
        .byte $00,$f0,$f0,$f0,$f0,$f0,$f0,$00,$00,$cf,$00,$00,$00,$00,$3f,$00
	.byte $00,$cc,$ff,$ff,$ff,$ff,$33,$00,$00,$0c,$0f,$0f,$cf,$ff,$33,$00
	.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$0c,$0f,$0f,$0f,$0f,$03,$00
	.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
Attribute_Title_End:

;--------------------------------------------------;
;                   Palettes                       ;
;--------------------------------------------------;
Palette_Title:
  .byte $2c,$02,$15,$27,$2c,$29,$1b,$09,$2c,$06,$16,$26,$2c,$09,$02,$29
  .byte $2c,$02,$15,$27,$2c,$29,$1b,$09,$2c,$06,$16,$26,$2c,$09,$02,$29
Palette_Title_End

;---------------------------;
;     SPRITES               ;
;---------------------------; 
Sprites:
Sprite_Paddle1:
     ;vert tile attr horiz
  .db $80, $86, $00, PADDLE1X   ;sprite 0
  .db $88, $86, $00, PADDLE1X   ;sprite 1
  .db $90, $86, $00, PADDLE1X   ;sprite 2
  .db $98, $86, $00, PADDLE1X   ;sprite 3
Sprite_Paddle1_End:
Sprite_Paddle2:
  .db $80, $86, $00, PADDLE2X
  .db $88, $86, $00, PADDLE2X
  .db $90, $86, $00, PADDLE2X
  .db $98, $86, $00, PADDLE2X
Sprite_Paddle2_End:
Sprite_Ball
  .db $80, $75, $00, $80
Sprite_Ball_End:
Sprites_End:

;---------------------------;
;     VECTORS               ;
;---------------------------; 
  .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                ;
;---------------------------; 

  .bank 2
  .org $0000
  .incbin "mario.chr"   ;includes 8KB graphics file from SMB1

User avatar
Controllerhead
Posts: 218
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

Post by Controllerhead » Tue Jan 19, 2021 1:12 am

justin-rwx wrote:
Mon Jan 18, 2021 11:47 pm
What's the preferred method for loading an entire title screen, or for that matter, filling the whole screen with the Nametable tiles as saved in the Nametable memory location?
Well, if you ask 1000 people, you'll get 1000 preferred methods =p

First things first though: a label with more than 256 entries won't do you much good, you can only offset up to 256 with either the X or the Y register. You're going to have to split up your Nametable_title data.

Now, if it were me (using your current data and setup), id make a temporary pointer in zero page to set before you call your LoadNametable routine, and use that pointer in your loop with indirect Y indexing, and call the routine multiple times. As a simple example, something like:

Variable declaration:

Code: Select all

pointerLowByte     .rs 1  ; LB
pointerHighByte    .rs 1  ; HB
Initialization calls:

Code: Select all

; Initialize Graphics
  jsr LoadPalettes
  jsr LoadSprites
  
  ; Load $2000-20FF
  lda $2002
  lda #$20
  sta $2006
  lda #$00
  sta $2006
  lda #low(Nametable_Title0)
  sta pointerLowByte
  lda #high(Nametable_Title0)
  sta pointerHighByte
  jsr LoadNametable
  
  ; Load $2100-21FF
  lda #low(Nametable_Title1)
  sta pointerLowByte
  lda #high(Nametable_Title1)
  sta pointerHighByte
  jsr LoadNametable
  
  ; Load $2200-22FF
  lda #low(Nametable_Title2)
  sta pointerLowByte
  lda #high(Nametable_Title2)
  sta pointerHighByte
  jsr LoadNametable
  
  ; Load $2300-23FF 
  lda #low(Nametable_Title3)
  sta pointerLowByte
  lda #high(Nametable_Title3)
  sta pointerHighByte
  jsr LoadNametable
  
  ; The previous JSR will collide with the attribute region... but
  jsr LoadAttribute
The loop:

Code: Select all

LoadNametable:
  ldy #$00 ; Use the Y register here for (indirect),Y indexing
.loop
  lda (pointerLowByte), y
  sta $2007
  iny
  bne .loop
  rts
Anyway, this is a very unoptimized example, but, i hope you understand the theory here.
Let me know if this makes sense or if you have questions. Good luck!
Image

User avatar
Gilbert
Posts: 454
Joined: Sun Dec 12, 2010 10:27 pm
Location: Hong Kong
Contact:

Re: Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

Post by Gilbert » Tue Jan 19, 2021 1:25 am

That's because each name table is 1024 bytes, and the X/Y/A registers of the 8-bit CPU are, literally, 8 bit, so if you use only a register to loop through all its values it's at most only 256 times, not enough to copy the whole screen. There are a number of ways. One is to use 4 loops like this (not optimised and not tested):

Code: Select all

LoadNametable:
  lda $2002
  lda #$20
  sta $2006
  lda #$00
  sta $2006
  ldx #$00
.loopa
  lda Nametable_Title, x
  sta $2007
  inx
  bne .loopa
.loopb
  lda Nametable_Title+$100, x
  sta $2007
  inx
  bne .loopb
.loopc
  lda Nametable_Title+$200, x
  sta $2007
  inx
  bne .loopc
.loopd
  lda Nametable_Title+$300, x
  sta $2007
  inx
  bne .loopd
  rts
You may also use a MACRO if the assembler supports it to make the codes easier to read. For example, this is what I did in filling two name tables in my old demo (assembler used was asm6):

Code: Select all


MACRO fillnt addy
- LDA addy, X
  STA $2007
  INX
  BNE -  
ENDM

...

  ;copy name tables
  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 $32000 address
  LDX #$00
  fillnt nt1
  fillnt nt1+$100
  fillnt nt1+$200
  fillnt nt1+$300
  fillnt nt2
  fillnt nt2+$100
  fillnt nt2+$200
  fillnt nt2+$300  
Edit:
Didn't notice I was ninja'd by Controllerhead. Anyway I think we're just talking about the same thing.

justin-rwx
Posts: 12
Joined: Sun Jan 10, 2021 1:12 pm

Re: Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

Post by justin-rwx » Sat Jan 23, 2021 8:38 pm

All - thanks for your input. After going further into the Nerdy-Nights tutorial I found another method that I kind of liked and thought was relatively elegant. I guess you were right Controllerhead - 3 people, 3 different methods :)

I was wondering if someone could help explain why asl A is used, and how NametablePointerTable is being used to load the correct Nametable. I have a general idea on how the loop works as a whole - the inside loop continues until Y rolls back over to $00, then the outside loop increments X, then takes it back around to the inside loop. I just don't understand the stuff before the loop.

Thanks in advance

Code: Select all

;--------------------------------------------------;
;                    Variables                     ;
;--------------------------------------------------;
  .rsset $0000  ;;start variables at ram location 0
gamestate   .rs 1  ; .rs 1 means reserve one byte of space
tempword    .rs 2

;--------------------------------------------------;
;                    Constants                     ;
;--------------------------------------------------;
STATE_TITLE       = $00  ; displaying title screen
STATE_PLAYING     = $01  ; move paddles/ball, check for collisions

  ; Initialize game state
  LDA #STATE_TITLE
  STA gamestate

;---------------------------;
;     SUBROUTINES           ;
;---------------------------;
LoadNametable:
  lda $2002
  lda #$20
  sta $2006
  lda #$00
  sta $2006
  lda gamestate ;
  
  ; What's going on here?;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  asl A 
  tay
  lda NametablePointerTable,y ; What is this addressing?
  sta tempword
  lda NametablePointerTable+1,y
  sta tempword+1
  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
  ldx #$00
  ldy #$00
.outsideloop
.insideloop
  lda [tempword],y
  sta $2007
  iny
  cpy #$00
  bne .insideloop
  inc tempword+1
  inx
  cpx #$04
  bne .outsideloop
  rts
 
 ;--------------------------------------------------;
;                   Nametables                     ;
;--------------------------------------------------;
NametablePointerTable:
  .dw Nametable_Title
  .dw Nametable_Playing

Nametable_Title:
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.
	.
	.
Nametable_Playing
	.byte $a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6
	.
	.
	.
The entire code is below

Code: Select all

;--------------------------------------------------;
;                   iNES Headers                   ;
;--------------------------------------------------;
  .inesprg 1   ; 1x 16KB PRG code
  .ineschr 1   ; 1x  8KB CHR data
  .inesmap 0   ; mapper 0 = NROM, no bank swapping
  .inesmir 1   ; background mirroring

;--------------------------------------------------;
;                    Variables                     ;
;--------------------------------------------------;
  .rsset $0000  ;;start variables at ram location 0
pointerlo   .rs 1
pointerhi   .rs 1
gamestate   .rs 1  ; .rs 1 means reserve one byte of space
ballx       .rs 1  ; ball horizontal position
bally       .rs 1  ; ball vertical position
ballup      .rs 1  ; 1 = ball moving up
balldown    .rs 1  ; 1 = ball moving down
ballleft    .rs 1  ; 1 = ball moving left
ballright   .rs 1  ; 1 = ball moving right
ballspeedx  .rs 1  ; ball horizontal speed per frame
ballspeedy  .rs 1  ; ball vertical speed per frame
paddle1ytop .rs 1 ; player 1 paddle top vertical position
paddle2ytop .rs 1 ; Player 2 paddle top vertical position
paddle1ybot .rs 1 ; Player 1 paddle bottom vertical position
paddle2ybot .rs 1 ; player 2 paddle bottom vertical position
buttons1    .rs 1  ; player 1 gamepad buttons, one bit per button
buttons2    .rs 1  ; player 2 gamepad buttons, one bit per button
score1      .rs 1  ; player 1 score, 0-15
score2      .rs 1  ; player 2 score, 0-15
tempword    .rs 2

;--------------------------------------------------;
;                    Constants                     ;
;--------------------------------------------------;
STATE_TITLE       = $00  ; displaying title screen
STATE_PLAYING     = $01  ; move paddles/ball, check for collisions
STATE_GAMEOVER    = $02  ; displaying game over screen
SPRITE_LOCATION   = $0200  
RIGHTWALL         = $F4  ; when ball reaches one of these, do something
TOPWALL           = $06
BOTTOMWALL        = $EB
LEFTWALL          = $04
PADDLE1X          = $08  ; horizontal position for paddles, doesnt move
PADDLE2X          = $ED
PADDLE_SPEED      = $05
SPRITE_SIZE       = $08
A_PRESSED         = $80
B_PRESSED         = $40
SEL_PRESSED       = $20
START_PRESSED     = $10
UP_PRESSED        = $08
DOWN_PRESSED      = $04
LEFT_PRESSED      = $02
RIGHT_PRESSED     = $01

;--------------------------------------------------;
;                    Bank 0                        ;
;--------------------------------------------------;
  .bank 0
  .org $C000 

;--------------------------------------------------;
;                     RESET                        ;
;--------------------------------------------------;
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 - I assume to allow for memory clearing
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs

  jsr VBlankWait

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
  
  jsr VBlankWait

  ; Initialize Variables
  jsr InitializeVariables

  ; Initialize game state
  LDA #STATE_TITLE
  STA gamestate

  ; Initialize Graphics
  jsr LoadPalettes
  ;jsr LoadSprites
  jsr LoadNametable
  jsr LoadAttribute
  
  ; Initialize PPU Settings (enable Sprites and background)
  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

; Infinite Loop
Forever:
  jmp Forever     ;jump back to Forever, infinite loop, waiting for NMI
  
;--------------------------------------------------;
;                      NMI                         ;
;--------------------------------------------------;
NMI:
  LDA #$00        ; Sprite location low byte
  STA $2003       ; set the low byte (00) of the RAM address
  LDA #$02        ; Sprite location high byte
  STA $4014       ; set the high byte (02) of the RAM address, start the transfer

  ;jsr DrawScore

  ;;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

  JSR ReadController1  ; get the current button data for player 1
  JSR ReadController2  ; get the current button data for player 2
  
GameEngine:  
  LDA gamestate
  CMP #STATE_TITLE
  BEQ EngineTitle    ;;game is displaying title screen
  LDA gamestate
  CMP #STATE_GAMEOVER
  BEQ EngineGameOver  ;;game is displaying ending screen
  LDA gamestate
  CMP #STATE_PLAYING
  BEQ EnginePlaying   ;;game is playing
GameEngineDone:  
  
  ;JSR UpdateSprites  ;;set ball/paddle sprites from positions
  RTI             ; return from interrupt
 
EngineTitle: ; 1/17/2021 The title screen doesn't show 'Pong', but will start when P1 presses Start. Nametable viewer in FCEUX shows table 0 tiles instead of background tiles
  ;jsr InitializeVariables ;;  if start button pressed
  ; lda #%00000000          ;;  turn screen off
  ; sta $2000 
  ; sta $2001 
  ;jsr LoadNametable
  ;jsr LoadAttribute
  ;jsr LoadPalettes        ;;  load game screen
  ;jsr LoadSprites
  ; lda #%10010000          ;;  turn screen on
  ; 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

  lda #STATE_TITLE
  sta gamestate

  jsr CheckStart

  JMP GameEngineDone
;;;;;;;;; 
 
EngineGameOver:
  ;;if start button pressed
  ;;  turn screen off
  ;;  load title screen
  ;;  go to Title State
  ;;  turn screen on 
  JMP GameEngineDone
 
;;;;;;;;;;;
 
EnginePlaying:
  jsr MoveBallRight
  jsr MoveBallLeft
  jsr MoveBallUp
  jsr MoveBallDown
  jsr MovePaddle1Up
  jsr MovePaddle1Down
  jsr MovePaddle2Up
  jsr MovePaddle2Down
  jsr CheckPaddle1Collision
  jsr CheckPaddle2Collision
  jmp GameEngineDone
  
;---------------------------;
;     SUBROUTINES           ;
;---------------------------;
MoveBallRight:
  LDA ballright
  BEQ MoveBallRightDone   ;;if ballright=0, skip this section

  LDA ballx
  CLC
  ADC ballspeedx        ;;ballx position = ballx + ballspeedx
  STA ballx

  LDA ballx
  CMP #RIGHTWALL
  BCC MoveBallRightDone      ;;if ball x < right wall, still on screen, skip next section
  LDA #$00
  STA ballright
  LDA #$01
  STA ballleft         ;;bounce, ball now moving left
  ;;in real game, give point to player 1, reset ball
MoveBallRightDone:
  rts

MoveBallLeft:
  LDA ballleft
  BEQ MoveBallLeftDone   ;;if ballleft=0, skip this section

  LDA ballx
  SEC
  SBC ballspeedx        ;;ballx position = ballx - ballspeedx
  STA ballx

  LDA ballx
  CMP #LEFTWALL
  BCS MoveBallLeftDone      ;;if ball x > left wall, still on screen, skip next section
  LDA #$01
  STA ballright
  LDA #$00
  STA ballleft         ;;bounce, ball now moving right
  ;;in real game, give point to player 2, reset ball
MoveBallLeftDone:
  rts

MoveBallUp:
  LDA ballup
  BEQ MoveBallUpDone   ;;if ballup=0, skip this section

  LDA bally
  SEC
  SBC ballspeedy        ;;bally position = bally - ballspeedy
  STA bally

  LDA bally
  CMP #TOPWALL
  BCS MoveBallUpDone      ;;if ball y > top wall, still on screen, skip next section
  LDA #$01
  STA balldown
  LDA #$00
  STA ballup         ;;bounce, ball now moving down
MoveBallUpDone:
  rts

MoveBallDown:
  LDA balldown
  BEQ MoveBallDownDone   ;;if ballup=0, skip this section

  LDA bally
  CLC
  ADC ballspeedy        ;;bally position = bally + ballspeedy
  STA bally

  LDA bally
  CMP #BOTTOMWALL
  BCC MoveBallDownDone      ;;if ball y < bottom wall, still on screen, skip next section
  LDA #$00
  STA balldown
  LDA #$01
  STA ballup         ;;bounce, ball now moving down
MoveBallDownDone:
  rts

MovePaddle1Up:
  lda buttons1
  and #UP_PRESSED ; masks all bits except the up button. Sets zero flag to 1 if true
  beq MovePaddle1UpDone ;skip all below if zero flag is 0 (the above logic is not true)
  lda paddle1ytop ;if paddle top > top wall
  cmp #TOPWALL ;sets carry flag if A >= M and sets zero flag if A = M ; I DONT UNDERSTAND THIS LOGIC???
  bcc MovePaddle1UpDone ; branches if A <= M
  sec   ; Update paddle sprite
  lda paddle1ytop
  sbc #PADDLE_SPEED ; sbc subtracts from the paddle's current top y position
  sta paddle1ytop
  sec
  lda paddle1ybot
  sbc #PADDLE_SPEED
  sta paddle1ybot
MovePaddle1UpDone:
  rts

MovePaddle1Down:
  lda buttons1
  and #DOWN_PRESSED ; masks all bits except the up button. Sets zero flag to 1 if true
  beq MovePaddle1DownDone ;skip all below if zero flag is 0 (the above logic is not true)
  lda paddle1ybot ;if paddle top > top wall
  cmp #BOTTOMWALL ;sets carry flag if A >= M and sets zero flag if A = M ; I DONT UNDERSTAND THIS LOGIC???
  bcs MovePaddle1DownDone ; branches if A >= M
  clc
  lda paddle1ytop
  adc #PADDLE_SPEED ; adc adds to the paddle's current top y position
  sta paddle1ytop
  clc
  lda paddle1ybot ; paddle bottom position must be maintained to ensure the ball knows where the paddle boundaries are
  adc #PADDLE_SPEED
  sta paddle1ybot
MovePaddle1DownDone:
  rts

MovePaddle2Up:
  lda buttons2
  and #UP_PRESSED ; masks all bits except the up button. Sets zero flag to 1 if true
  beq MovePaddle2UpDone ;skip all below if zero flag is 0 (the above logic is not true)
  lda paddle2ytop ;if paddle top > top wall
  cmp #TOPWALL ;sets carry flag if A >= M and sets zero flag if A = M ; I DONT UNDERSTAND THIS LOGIC???
  bcc MovePaddle2UpDone ; branches if A <= M
  sec   ; Update paddle sprite
  lda paddle2ytop
  sbc #PADDLE_SPEED ; sbc subtracts from the paddle's current top y position
  sta paddle2ytop
  sec
  lda paddle2ybot
  sbc #PADDLE_SPEED
  sta paddle2ybot
MovePaddle2UpDone:
  rts

MovePaddle2Down:
  lda buttons2
  and #DOWN_PRESSED ; masks all bits except the up button. Sets zero flag to 1 if true
  beq MovePaddle2DownDone ;skip all below if zero flag is 0 (the above logic is not true)
  lda paddle2ybot ;if paddle top > top wall
  cmp #BOTTOMWALL ;sets carry flag if A >= M and sets zero flag if A = M ; I DONT UNDERSTAND THIS LOGIC???
  bcs MovePaddle2DownDone ; branches if A >= M
  clc
  lda paddle2ytop
  adc #PADDLE_SPEED ; adc adds to the paddle's current top y position
  sta paddle2ytop
  clc
  lda paddle2ybot ; paddle bottom position must be maintained to ensure the ball knows where the paddle boundaries are
  adc #PADDLE_SPEED
  sta paddle2ybot
MovePaddle2DownDone:
  rts

CheckPaddle1Collision:
  lda ballx ; if ball x < paddle1x
  cmp #PADDLE1X+SPRITE_SIZE ; adds the sprite size to the paddle's x position so it appears that the ball is bouncing off of the paddle
  bcs CheckPaddle1CollisionDone ; continue if the ballx is <= PADDLE1X
  lda bally
  cmp paddle1ytop
  bcc CheckPaddle1CollisionDone ; continue if the bally is >= paddle1ytop (lower on the screen)
  cmp paddle1ybot
  bcs CheckPaddle1CollisionDone ; continue if the bally is <= paddle1ybot (higher on the screen)
  lda #$01  ; ball changes direction to the right
  sta ballright
  lda #$00
  sta ballleft
CheckPaddle1CollisionDone:
  rts

CheckPaddle2Collision:
  lda ballx ; if ball x < paddle1x
  cmp #PADDLE2X-SPRITE_SIZE ; adds the sprite size to the paddle's x position so it appears that the ball is bouncing off of the paddle
  bcc CheckPaddle2CollisionDone ; continue if the ballx is <= PADDLE1X
  lda bally
  cmp paddle2ytop
  bcc CheckPaddle2CollisionDone ; continue if the bally is >= paddle1ytop (lower on the screen)
  cmp paddle2ybot
  bcs CheckPaddle2CollisionDone ; continue if the bally is <= paddle1ybot (higher on the screen)
  lda #$00  ; ball changes direction to the right
  sta ballright
  lda #$01
  sta ballleft
CheckPaddle2CollisionDone:
  rts

UpdateSprites: ; Updates all sprite info
  lda bally
  sta SPRITE_LOCATION + (Sprite_Ball - Sprites) ; $0200 + (the quantity of addresses between Sprite_Ball and Sprites)
  lda ballx
  sta SPRITE_LOCATION + (Sprite_Ball - Sprites) + 3 ; Adds 3 to the address to reach the last byte (x coordinate) in the ball sprite

  ldy #$00
  ldx #$00
  lda paddle1ytop
.loop1
  clc
  sta SPRITE_LOCATION + (Sprite_Paddle1 - Sprites), y 
  adc #$08 ; 8 pixels below the top of the previous tile, draw the next tile (starting from the top). This keeps the all tiles of the sprite together
  iny
  iny
  iny
  iny ; four iny will cycle through each 4 bytes of a tile, accessing the byte for the vertical position
  inx 
  cpx #((Sprite_Paddle1_End - Sprite_Paddle1)/4) ; compare x with the number of tiles in the sprite
  bne .loop1 ; if more tiles still need to be counted, return to .loop1

  ldy #$00
  ldx #$00
  lda paddle2ytop
.loop2
  clc
  sta SPRITE_LOCATION + (Sprite_Paddle2 - Sprites), y
  adc #$08
  iny
  iny
  iny
  iny
  inx
  cpx #((Sprite_Paddle2_End - Sprite_Paddle2)/4)
  bne .loop2
  rts
 
;DrawScore:
  ;;draw score on screen using background tiles
  ;;or using many sprites
  ;RTS
 
ReadController1:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016
  LDX #$08
.loop
  LDA $4016
  LSR A            ; bit0 -> Carry
  ROL buttons1     ; bit0 <- Carry - absSuplr
  DEX
  BNE .loop
  RTS
  
ReadController2:
  LDA #$01
  STA $4016
  LDA #$00
  STA $4016
  LDX #$08
.loop
  LDA $4017
  LSR A            ; bit0 -> Carry
  ROL buttons2     ; bit0 <- Carry
  DEX
  BNE .loop
  RTS  

CheckStart:
  lda buttons1
  and #START_PRESSED
  BEQ CheckStartDone
  LDA #STATE_PLAYING
  STA gamestate
CheckStartDone:
  RTS
  
LoadSprites:
  ldx #$00
.loop
  lda Sprites, x
  sta SPRITE_LOCATION, x
  inx
  cpx #(Sprites_End-Sprites)
  bne .loop
  rts

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
.loop
  lda Palette_Title, 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 .loop  ; Branch to LoadPalettesLoop if compare was Not Equal to zero
  rts

LoadNametable:
  lda $2002
  lda #$20
  sta $2006
  lda #$00
  sta $2006
  lda gamestate ; load the gamestate
  asl A
  tay
  lda NametablePointerTable,y
  sta tempword
  lda NametablePointerTable+1,y
  sta tempword+1
  ldx #$00
  ldy #$00
.outsideloop
.insideloop
  lda [tempword],y
  sta $2007
  iny
  cpy #$00
  bne .insideloop
  inc tempword+1
  inx
  cpx #$04
  bne .outsideloop
  rts

LoadAttribute:
  lda $2002
  lda #$23
  sta $2006
  lda #$C0
  sta $2006
  ldx #$00
.loop
  lda Attribute_Title, x
  sta $2007
  inx
  ;cpx #(Attribute_Title_End-Attribute_Title)
  cpx #$08
  bne .loop
  rts

InitializeVariables: ;Set some initial ball and paddle stats
  LDA #$01
  STA balldown
  STA ballright
  LDA #$00
  STA ballup
  STA ballleft
  LDA #$50
  STA bally
  LDA #$80
  STA ballx
  LDA #$02
  STA ballspeedx
  STA ballspeedy
  lda #$80
  sta paddle1ytop
  sta paddle2ytop
  lda #$80+8*4
  sta paddle1ybot
  sta paddle2ybot;sta paddle2ybot
  rts

VBlankWait
  bit $2002
  bpl VBlankWait
  rts
        
;---------------------------;
;     Bank 1                ;
;---------------------------; 
  .bank 1
  .org $E000

;--------------------------------------------------;
;                   Nametables                     ;
;--------------------------------------------------;
NametablePointerTable:
  .dw Nametable_Title
  .dw Nametable_Playing

Nametable_Title:
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$b0,$91,$91,$91,$91,$91,$91,$91,$91,$91,$91
	.byte $91,$91,$91,$91,$91,$91,$91,$91,$91,$91,$b2,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$96,$96,$96,$96,$96,$96,$96,$96,$96,$96,$96
	.byte $96,$96,$96,$96,$96,$96,$96,$96,$96,$96,$96,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$96,$96,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$96,$96,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$24,$19,$24,$24,$24,$24,$18,$24,$24
	.byte $24,$24,$17,$24,$24,$24,$24,$10,$24,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$96,$96,$96,$96,$96,$96,$96,$96,$96,$96
	.byte $96,$96,$96,$96,$96,$96,$96,$96,$96,$96,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6
	.byte $f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6
	.byte $f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6
	.byte $f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6
	.byte $f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$f6,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24

Nametable_Playing:
	.byte $a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6
	.byte $a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6,$a5,$a6
	.byte $a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8
	.byte $a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8,$a7,$a8
	.byte $44,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45
	.byte $45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$45,$49
	.byte $5f,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47
	.byte $47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$47,$7a
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24
	.byte $6b,$6c,$6d,$6c,$6d,$6c,$6d,$6c,$6d,$6c,$6d,$6c,$6e,$6c,$6d,$6c
	.byte $6d,$6c,$6d,$6c,$6d,$6c,$6d,$6c,$6d,$6c,$6d,$6e,$6c,$6d,$6e,$6f
	.byte $70,$71,$72,$71,$72,$73,$72,$72,$73,$71,$72,$73,$73,$71,$72,$71
	.byte $72,$71,$72,$71,$72,$71,$72,$71,$72,$71,$72,$73,$71,$72,$73,$74
	.byte $24,$ba,$25,$25,$25,$25,$25,$25,$25,$25,$25,$25,$25,$25,$25,$25
	.byte $25,$25,$25,$25,$25,$25,$25,$25,$25,$25,$25,$25,$25,$25,$bb,$24

;--------------------------------------------------;
;                   Attributes                     ;
;--------------------------------------------------;
Attribute_Title:
  .byte $00,$f0,$f0,$f0,$f0,$f0,$f0,$00,$00,$cf,$00,$00,$00,$00,$3f,$00
	.byte $00,$cc,$ff,$ff,$ff,$ff,$33,$00,$00,$0c,$0f,$0f,$cf,$ff,$33,$00
	.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$0c,$0f,$0f,$0f,$0f,$03,$00
	.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
Attribute_Title_End:

;--------------------------------------------------;
;                   Palettes                       ;
;--------------------------------------------------;
Palette_Title:
  .byte $2c,$02,$15,$27,$2c,$29,$1b,$09,$2c,$06,$16,$26,$2c,$09,$02,$29
  .byte $2c,$02,$15,$27,$2c,$29,$1b,$09,$2c,$06,$16,$26,$2c,$09,$02,$29
Palette_Title_End

;---------------------------;
;     SPRITES               ;
;---------------------------; 
Sprites:
Sprite_Paddle1:
     ;vert tile attr horiz
  .db $80, $86, $00, PADDLE1X   ;sprite 0
  .db $88, $86, $00, PADDLE1X   ;sprite 1
  .db $90, $86, $00, PADDLE1X   ;sprite 2
  .db $98, $86, $00, PADDLE1X   ;sprite 3
Sprite_Paddle1_End:
Sprite_Paddle2:
  .db $80, $86, $00, PADDLE2X
  .db $88, $86, $00, PADDLE2X
  .db $90, $86, $00, PADDLE2X
  .db $98, $86, $00, PADDLE2X
Sprite_Paddle2_End:
Sprite_Ball
  .db $80, $75, $00, $80
Sprite_Ball_End:
Sprites_End:

;---------------------------;
;     VECTORS               ;
;---------------------------; 
  .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                ;
;---------------------------; 

  .bank 2
  .org $0000
  .incbin "mario.chr"   ;includes 8KB graphics file from SMB1

tepples
Posts: 22288
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

Post by tepples » Sat Jan 23, 2021 8:58 pm

justin-rwx wrote:
Sat Jan 23, 2021 8:38 pm
All - thanks for your input. After going further into the Nerdy-Nights tutorial I found another method that I kind of liked and thought was relatively elegant. I guess you were right Controllerhead - 3 people, 3 different methods :)

I was wondering if someone could help explain why asl A is used, and how NametablePointerTable is being used to load the correct Nametable.
In the structure of this particular program, nametable IDs are 0, 1, 2, 3, ...
Each element of NametablePointerTable contains the starting address in PRG ROM of each nametable.
Because each element of NametablePointerTable is 2 bytes, valid byte offsets into NametablePointerTable are 0, 2, 4, 6, ...
The asl A instruction multiplies A by 2 in order to convert a nametable ID into a byte offset into NametablePointerTable.

justin-rwx
Posts: 12
Joined: Sun Jan 10, 2021 1:12 pm

Re: Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

Post by justin-rwx » Sun Jan 24, 2021 12:03 am

Thanks for the response.

I think most of my confusion lies in this code here:

Code: Select all

  lda NametablePointerTable,y 
  sta tempword
  lda NametablePointerTable+1,y
  sta tempword+1
.dw Nametable_Title stores the address of Nametable_Title. Therefore the address at .dw contains an address. Is it correct that this address can be accessed as two individual bytes - one LSB and one MSB?

lda NametablePointerTable,y I assume takes the LSB part of the Nametable_Title address and stores it in tempword. If that's a correct assumption, then lda NametablePointerTable+1,y takes the MSB part of the Nametable_Title address and stores it in tempword+1

Can someone factcheck these assumptions? I feel like this type of table could be a powerful tool in the future but I want to make sure I completely understand it.

tepples
Posts: 22288
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Nerdy-Nights Week 7 (Pong Clone): Unable to Display A Title Screen

Post by tepples » Sun Jan 24, 2021 7:06 am

This is correct.

Post Reply