LIST OFF ; *** B A S K E T B A L L *** ; Copyright 1977 Atari, Inc. ; Designer: Alan Miller ; Analyzed, labeled and commented ; by Dennis Debro ; Last Update: October 1, 2016 ; ; NTSC ROM usage stats ; ------------------------------------------- ; *** 114 BYTES OF RAM USED 14 BYTES FREE ; *** 1 BYTE OF ROM FREE ; ; PAL50 ROM usage stats ; ------------------------------------------- ; *** 114 BYTES OF RAM USED 14 BYTES FREE ; *** 0 BYTES OF ROM FREE ; ; ============================================================================== ; = THIS REVERSE-ENGINEERING PROJECT IS BEING SUPPLIED TO THE PUBLIC DOMAIN = ; = FOR EDUCATIONAL PURPOSES ONLY. THOUGH THE CODE WILL ASSEMBLE INTO THE = ; = EXACT GAME ROM, THE LABELS AND COMMENTS ARE THE INTERPRETATION OF MY OWN = ; = AND MAY NOT REPRESENT THE ORIGINAL VISION OF THE AUTHOR. = ; = = ; = THE ASSEMBLED CODE IS © 1977, ATARI, INC. = ; = = ; ============================================================================== ; ; - RAM locations $84 - $86 and $8B - $8D are not used ; - RAM locations for variables changed for PAL50 release...not sure why ; - Computer jumping can be controlled by right port fire button processor 6502 ; ; NOTE: You must compile this with vcs.h version 105 or greater. ; TIA_BASE_READ_ADDRESS = $30 ; set the read address base so this runs on ; the real VCS and compiles to the exact ; ROM image include "vcs.h" include "macro.h" include "tia_constants.h" ; ; Make sure we are using vcs.h version 1.05 or greater. ; IF VERSION_VCS < 105 echo "" echo "*** ERROR: vcs.h file *must* be version 1.05 or higher!" echo "*** Updates to this file, DASM, and associated tools are" echo "*** available at https://dasm-assembler.github.io/" echo "" err ENDIF ; ; Make sure we are using macro.h version 1.01 or greater. ; IF VERSION_MACRO < 101 echo "" echo "*** ERROR: macro.h file *must* be version 1.01 or higher!" echo "*** Updates to this file, DASM, and associated tools are" echo "*** available at https://dasm-assembler.github.io/" echo "" err ENDIF LIST ON ;=============================================================================== ; A S S E M B L E R - S W I T C H E S ;=============================================================================== NTSC = 0 PAL50 = 1 PAL60 = 2 IFNCONST COMPILE_REGION COMPILE_REGION = NTSC ; change to compile for different regions ENDIF IF !(COMPILE_REGION = NTSC || COMPILE_REGION = PAL50 || COMPILE_REGION = PAL60) echo "" echo "*** ERROR: Invalid COMPILE_REGION value" echo "*** Valid values: NTSC = 0, PAL50 = 1, PAL60 = 2" echo "" err ENDIF ;=============================================================================== ; F R A M E T I M I N G S ;=============================================================================== IF COMPILE_REGION = NTSC || COMPILE_REGION = PAL60 FPS = 60 ; ~60 frames per second VBLANK_TIME = 51 OVERSCAN_SCANLINE_COUNT = 1 INIT_XPOS_LEFT_BOUNDARY = 50 ; horizontal position of left bound line INIT_XPOS_RIGHT_BOUNDARY = 110 ; horizontal position of right bound line ELSE FPS = 50 ; ~50 frames per second VBLANK_TIME = 67 OVERSCAN_SCANLINE_COUNT = 24 INIT_XPOS_LEFT_BOUNDARY = 52 ; horizontal position of left bound line INIT_XPOS_RIGHT_BOUNDARY = 107 ; horizontal position of right bound line ENDIF ;=============================================================================== ; C O L O R C O N S T A N T S ;=============================================================================== BLACK = $00 WHITE = $0E IF COMPILE_REGION = NTSC RED = $30 COBALT_BLUE = $60 BLUE = $80 DK_GREEN = $D0 PLAYER_1_COLOR = DK_GREEN + 10 PLAYER_2_COLOR = COBALT_BLUE + 4 CLOCK_COLOR = BLUE + 10 FLOOR_COLOR = RED + 6 ELSE YELLOW = $20 OLIVE_GREEN = $30 RED = $60 BLUE = $D0 PLAYER_1_COLOR = OLIVE_GREEN + 10 PLAYER_2_COLOR = RED + 10 CLOCK_COLOR = BLUE + 10 FLOOR_COLOR = YELLOW + 6 ENDIF ;=============================================================================== ; U S E R - C O N S T A N T S ;=============================================================================== ROM_BASE = $F000 VSYNC_TIME = 42 MAX_NUM_PLAYERS = 2 INIT_PLAYER2_XPOS = 84 INIT_PLAYER1_XPOS = 70 INIT_BALL_XPOS = 79 XPOS_MINUTES = 62 ; horizontal position of minutes XPOS_SECONDS = 78 ; horizontal position of seconds W_SCREEN = 159 XMIN = 23 XMAX = W_SCREEN - 18 YMIN = 90 YMAX = 180 Y_MID_COURT = 135 H_FONT = 5 H_BALL = 8 H_PLAYER = 16 SELECT_DELAY = 63 START_GAME_MINUTES = $40 ; 4 minutes (BCD) BW_HUE_MASK = $07 COLOR_HUE_MASK = $F7 ; Number fonts LSB values BLANK_LSB_VALUE = <(Blank - NumberFonts) / H_FONT ONE_LSB_VALUE = <(one - NumberFonts) / H_FONT TWO_LSB_VALUE = <(two - NumberFonts) / H_FONT THREE_LSB_VALUE = <(three - NumberFonts) / H_FONT FOUR_LSB_VALUE = <(four - NumberFonts) / H_FONT FIVE_LSB_VALUE = <(five - NumberFonts) / H_FONT SIX_LSB_VALUE = <(six - NumberFonts) / H_FONT SEVEN_LSB_VALUE = <(seven - NumberFonts) / H_FONT EIGHT_LSB_VALUE = <(eight - NumberFonts) / H_FONT NINE_LSB_VALUE = <(nine - NumberFonts) / H_FONT COLON_LSB_VALUE = <(colon - NumberFonts) / H_FONT ; game state values GAME_OVER = 0 ; ; Scoring basket status flags ; BASKET_MADE_STATUS = %10000000 BASKET_SCORED_MASK = %00000001 ; ; Ball possession status flags ; PLAYER_HAS_BALL_STATUS = %10000000 PLAYER_SHOOTING = %01000000 PLAYER_POSSESSION_MASK = %00000001 ; ; Bouncing ball direction status flags ; BALL_BOUNCING_HORIZ_MASK = %10000000 BALL_BOUNCING_VERT_MASK = %01000000 BALL_BOUNCING_LEFT = 1 << 7 BALL_BOUNCING_RIGHT = 0 << 7 BALL_BOUNCING_DOWN = 1 << 6 BALL_BOUNCING_UP = 0 << 6 ;=============================================================================== ; Z P - V A R I A B L E S ;=============================================================================== SEG.U variables .org $80 IF COMPILE_REGION = NTSC || COMPILE_REGION = PAL60 frameCount ds 1 ENDIF gameState ds 1 gameSelection ds 1 selectDebounce ds 1 unused_01 ds 3 ; 3 unused RAM bytes scoreBoardValues ds 4 ;-------------------------------------- gameClock = scoreBoardValues playerScores = gameClock + 2 ;-------------------------------------- player1Score = playerScores player2Score = playerScores + 1 unused_02 ds 3 ; 3 unused RAM bytes scanline ds 1 playerGraphics ds H_PLAYER * 2 ;-------------------------------------- player2Graphics = playerGraphics player1Graphics = player2Graphics + H_PLAYER ballBounceHeight ds 1 timerMinutesHorizPos ds 1 ;-------------------------------------- tmpPlayer2HorizPos = timerMinutesHorizPos timerSecondsHorizPos ds 1 leftBoundsHorizPos ds 1 ; missile 0 rightBoundsHorizPos ds 1 ; missile 1 ballHorizPos ds 1 fireButtonDebounceValues ds 2 ;-------------------------------------- player1FireButtonDebounce = fireButtonDebounceValues player2FireButtonDebounce = player1FireButtonDebounce + 1 bouncingBallVertDirection ds 1 allowToBlockBallStatus ds 1 shootingBallLift ds 1 colorEOR ds 1 IF COMPILE_REGION = PAL50 frameCount ds 1 ENDIF scoringBasketStatus ds 1 ballPossessionStatus ds 1 ballBouncingDirectionStatus ds 1 shootingAnimationCount ds 1 ballGravityValue ds 1 playerRunningAnimation ds 2 ;-------------------------------------- player1RunningAnimation = playerRunningAnimation player2RunningAnimation = player1RunningAnimation + 1 playerHorizontalDirection ds 2 ;-------------------------------------- player1HorizontalDirection = playerHorizontalDirection player2HorizontalDirection = player1HorizontalDirection + 1 playerJumpingValues ds 2 ;-------------------------------------- player1JumpingValue = playerJumpingValues player2JumpingValue = player1JumpingValue + 1 ballInHoopStatus ds 1 soundVolumeControl ds 2 ;-------------------------------------- leftSoundChannelVolumeControl = soundVolumeControl rightSoundChannelVolumeControl = leftSoundChannelVolumeControl + 1 ballHorizMotionDelay ds 1 decBallHorizMotionDelay ds 1 ballVertMotionDelay ds 1 decBallVertMotionDelay ds 1 playersVertPos ds 3 ;-------------------------------------- player2VertPos = playersVertPos player1VertPos = player2VertPos + 1 ballScanline = player1VertPos + 1 ballVerticalPosition ds 1 cpuPlayerShootingDelay ds 1 joystickValue ds 1 kernelFloorColor ds 1 ballDribblingCount ds 1 oldBallBounceHeight ds 1 playersHorizPos ds 2 ;-------------------------------------- player2HorizPos = playersHorizPos player1HorizPos = player2HorizPos + 1 playerReflectValues ds 2 ;-------------------------------------- player1ReflectValue = playerReflectValues player2ReflectValue = player1ReflectValue + 1 clockSpriteGraphics ds H_FONT * 2 ;-------------------------------------- minutesGraphics = clockSpriteGraphics secondsGraphics = minutesGraphics + H_FONT scoreSpriteGraphics ds H_FONT * 2 ;-------------------------------------- player1ScoreGraphics = scoreSpriteGraphics player2ScoreGraphics = player1ScoreGraphics + H_FONT newOffensivePlayerHorizPos ds 1 cpuPlayerAggression ds 1 tmpBallPossessionPlayerIdx ds 1 numberSpritePointer ds 2 ;-------------------------------------- tmpPlayerDistanceValues = numberSpritePointer ;-------------------------------------- tmpPlayerBasketHorizDistance = tmpPlayerDistanceValues tmpPlayerMidCourtVertDistance = tmpPlayerBasketHorizDistance + 1 hueMask = numberSpritePointer ;-------------------------------------- tmpOddEvenFrameCount = hueMask ;-------------------------------------- tmpJoystickValue = tmpOddEvenFrameCount ;-------------------------------------- loopCount = tmpJoystickValue ;-------------------------------------- tmpHorizPosDiv16 = loopCount ;-------------------------------------- tmpShootingBallLiftDiv4 = tmpHorizPosDiv16 ;-------------------------------------- tmpPlayerVertPosDiv4 = tmpShootingBallLiftDiv4 ;-------------------------------------- playerLegSpritePointer = tmpPlayerVertPosDiv4 ;-------------------------------------- tmpDefensivePlayerIndex = numberSpritePointer + 1 ;-------------------------------------- playerScoreColors = tmpDefensivePlayerIndex ;-------------------------------------- player1ScoreColor = playerScoreColors player2ScoreColor = player1ScoreColor + 1 ;-------------------------------------- tmpScoreBoardValue ds 1 ;-------------------------------------- tmpPlayerDistanceDiv4 = tmpScoreBoardValue ;-------------------------------------- player2JoystickValue = tmpPlayerDistanceDiv4 ;-------------------------------------- playerBodySpritePointer = player2JoystickValue ;-------------------------------------- clockColor ds 1 ;-------------------------------------- tmpFontIndex = clockColor spriteRAMPointer ds 2 ;-------------------------------------- tmpPlayerWithBall = spriteRAMPointer floorColor ds 1 echo "***",(* - $80 - 6)d, "BYTES OF RAM USED", ($100 - * + 6)d, "BYTES FREE" ;=============================================================================== ; R O M - C O D E (BANK 0) ;=============================================================================== SEG Bank0 .org ROM_BASE SetupFontKernelGraphics stx spriteRAMPointer + 1 ; x = 0 lda #>NumberFonts ; get the MSB of the number fonts sta numberSpritePointer + 1 ; set MSB of number sprite ldx #<[player2Score - gameClock] ; prepare to draw score board digits .setupFontKernelGraphicsData stx tmpFontIndex lda scoreBoardValues,x ; get the score board value and #$0F ; mask off the upper nybbles sta tmpScoreBoardValue ; save the value for later asl ; shift the value left to multiply by 4 asl clc ; add in original so it's multiplied by 5 adc tmpScoreBoardValue ; [i.e. x * 5 = (x * 4) + x] adc #> 4 beq .determinePlayerHorizChange ; branch if not moved sta playerRunningAnimation,x .determinePlayerHorizChange lsr ; move horizontal movement values to D1 - D0 lsr tay ; move movement value to y register lda PositionChangeValues,y ; get position delta for movement value beq .movePlayerHorizontally sta playerHorizontalDirection,x .movePlayerHorizontally clc adc playersHorizPos,x sta playersHorizPos,x ; set player's new horizontal position lda tmpJoystickValue ; get the temporary joystick value and #3 ; keep vertical movement values tay ; move movement value to y register lda PositionChangeValues,y ; get position delta for movement value clc adc playersVertPos,x ; change player vertical position cmp #YMAX bcs .determineNextPlayerMovement cmp #YMIN bcc .determineNextPlayerMovement sta playersVertPos,x ; set player's new vertical position .determineNextPlayerMovement dex beq .determinePlayerMovement ldx #1 .checkPlayerBallCollision lda CXP0FB,x ; get player collision value with PF or BALL and #$40 ; keep player BALL collision value beq .checkNextPlayerBallCollision; branch if player didn't hit ball sec lda playersVertPos,x ; get player's vertical position sbc ballVerticalPosition ; subtract ball vertical position bcs .checkForPlayerStealingBall eor #$FF ; get absolute value from overflow adc #1 .checkForPlayerStealingBall ldy allowToBlockBallStatus ; get status to see if player can block shot bne .checkNextPlayerBallCollision; branch if player cannot block shot bit ballPossessionStatus ; check ball possession status bpl .checkForJumpingPlayerBlockingShot;branch if player doesn't have ball and #~1 ; and distance with 2's complement of 1 bne .checkNextPlayerBallCollision; branch if not within 1 scan line distance lda frameCount ; get current frame count and #1 ; keep D0 value sta tmpOddEvenFrameCount cpx tmpOddEvenFrameCount bne .checkNextPlayerBallCollision; branch if not player's frame .setPlayerBallPossessionStatus txa ; move player index to accumulator stx tmpBallPossessionPlayerIdx ; set to index of player that has ball ora #PLAYER_HAS_BALL_STATUS sta ballPossessionStatus ; set status to show player has ball lda #BALL_BOUNCING_RIGHT | BALL_BOUNCING_DOWN sta ballBouncingDirectionStatus ; set to bounce ball downward sta ballDribblingCount bne .checkBallPossessionStatus ; unconditional branch .checkForJumpingPlayerBlockingShot ldy playerJumpingValues,x ; get player jumping value beq .checkForNonJumpingPlayerBlockingShot; branch if player not jumping and #~[(H_PLAYER * 2) - 1] beq .setPlayerBallPossessionStatus; block ball if player within 32 scan lines bne .checkNextPlayerBallCollision .checkForNonJumpingPlayerBlockingShot and #~(H_PLAYER - 1) beq .setPlayerBallPossessionStatus; branch if within 16 scan line distance .checkNextPlayerBallCollision dex bpl .checkPlayerBallCollision .checkBallPossessionStatus bit ballPossessionStatus ; check ball possession status bmi .determineBounceHeightForPossessedBall; branch if player has the ball jmp CheckBallForHittingBasket .determineBounceHeightForPossessedBall bvs .determineBallHeightForShootingAnimation; branch if preparing to shoot ldx tmpBallPossessionPlayerIdx ; get index of player that has ball lda playersVertPos,x ; get player vertical position sta ballVerticalPosition ; set ball vertical position sec lda playersHorizPos,x ; get the player horizontal position sbc BallHorizPositionOffsetValues,x sta ballHorizPos ; set ball horizontal position lda ballDribblingCount ; get ball dribbling count and #$0F cmp #8 bcc .setBallHeightForDribbling eor #$0F ; get 1's complement to reduce bounce height .setBallHeightForDribbling asl sta ballBounceHeight inc ballDribblingCount ; increment ball dribbling count jmp DetermineToAllowBlockedShot .determineBallHeightForShootingAnimation lda frameCount ; get current frame count and #3 ; shooting amimation updated every 4 frames bne DetermineBouncingBallDirectionStatus sta ballGravityValue ; clear ball gravity value inc shootingAnimationCount ; increment shooting animation count lda shootingAnimationCount ; get shooting animation count and #$0F cmp #8 bcc .setShootingAnimationBallHeight eor #$0F ; get 1's complement to reduce value .setShootingAnimationBallHeight tax clc adc #7 ; increment value by 7 sta shootingBallLift lda ShootingBallBounceHeightValues,x sta ballBounceHeight lda tmpBallPossessionPlayerIdx ; get index of player that has ball bne .setPlayer1BallHorizontalPosition; branch if not player 2 inc ballBounceHeight ; increment ball bounce height clc lda player2HorizPos ; get player2's horizontal position adc BallAndPlayerHorizOffsetValues,x sbc #2 bne .setBallHorizontalPosition ; unconditional branch .setPlayer1BallHorizontalPosition sec lda player1HorizPos ; get player1's horizontal position adc #10 - 1 ; carry set sbc BallAndPlayerHorizOffsetValues,x .setBallHorizontalPosition sta ballHorizPos DetermineBouncingBallDirectionStatus ldy #BALL_BOUNCING_LEFT ldx tmpBallPossessionPlayerIdx ; get index of player that has ball lda playersVertPos,x ; get player with ball vertical position sta ballVerticalPosition ; set ball's vertical position sec lda playersHorizPos,x ; get player with ball horizontal position sbc UnderBasketHorizontalPosition,x; subtract basket horizontal position bcs .setPlayerHorizBasketDistance eor #$FF ; determine absolute value distance adc #1 ldy #BALL_BOUNCING_RIGHT .setPlayerHorizBasketDistance sta tmpPlayerBasketHorizDistance ; save horizontal distance from basket sty ballBouncingDirectionStatus ; set ball horizontal bouncing direction ldy #BALL_BOUNCING_UP sec lda playersVertPos,x ; get player with ball vertical position sbc #Y_MID_COURT ; subtract mid court position bcs .setPlayerVertMidCountDistance eor #$FF ; determine absolute value distance adc #1 ldy #BALL_BOUNCING_DOWN .setPlayerVertMidCountDistance sty bouncingBallVertDirection sta tmpPlayerMidCourtVertDistance; save vertical distance from mid court ldx #2 .determineMinimalPlayerDistance lda tmpPlayerDistanceValues - 1,x cmp #4 bcs .setMinimalPlayerDistanceValue lda #4 .setMinimalPlayerDistanceValue sta tmpPlayerDistanceValues - 1,x dex bne .determineMinimalPlayerDistance .divPlaeryDistanceBy2 lsr tmpPlayerMidCourtVertDistance lsr tmpPlayerBasketHorizDistance ldx #1 .determineBallMotionDelay lda tmpPlayerDistanceValues,x cmp #2 beq .setBallMotionDelay cmp #3 beq DetermineBallMotionDelay dex beq .determineBallMotionDelay bne .divPlaeryDistanceBy2 ; unconditional branch DetermineBallMotionDelay lda #2 sta tmpPlayerDistanceValues,x txa ; move index to accumulator eor #1 ; flip value to get alternate index tax ; set x to alternate index lda tmpPlayerDistanceValues,x ; get player distance value lsr ; divide value by 4 lsr sta tmpPlayerDistanceDiv4 lda tmpPlayerDistanceValues,x sec sbc tmpPlayerDistanceDiv4 sta tmpPlayerDistanceValues,x ; player distance reduced by 25% .setBallMotionDelay lda tmpPlayerBasketHorizDistance sta ballVertMotionDelay sta decBallVertMotionDelay lda tmpPlayerMidCourtVertDistance sta ballHorizMotionDelay sta decBallHorizMotionDelay jmp DetermineToAllowBlockedShot CheckBallForHittingBasket bit CXBLPF ; check ball and playfield collision bpl .clearBallInHoopStatus ; branch if ball didn't hit basket lda ballVerticalPosition ; get the ball vertical position cmp #YMAX - 60 bcc .clearBallInHoopStatus cmp #YMAX - 30 bcs .clearBallInHoopStatus lda ballBounceHeight cmp #70 bcs .checkForBallInTheHoop cmp #54 bcc .clearBallInHoopStatus ldx #0 ; assume ball is on the left side of screen lda ballHorizPos ; get the ball horizontal position cmp #(W_SCREEN + 1) / 2 bcc .setScoringBasketStatus ; branch if ball on left side of the court inx ; x = 1 .setScoringBasketStatus txa ; move ball side value to accumulator ora #BASKET_MADE_STATUS sta scoringBasketStatus ; set to show made basket and which basket sta ballHorizMotionDelay sta ballVertMotionDelay lda UnderBasketHorizontalPosition,x sta ballHorizPos ; set ball horizontal position sta newOffensivePlayerHorizPos ; set for offensive player lda #Y_MID_COURT sta ballVerticalPosition lda #12 sta shootingBallLift .checkForBallInTheHoop bit ballInHoopStatus ; check ball in the hoop status bmi .checkForScoring ; branch if ball in the hoop lda #$80 sta ballInHoopStatus ; set to show ball in the hoop eor ballBouncingDirectionStatus ; flip ball horizontal bouncing direction sta ballBouncingDirectionStatus lda #2 sta leftSoundChannelVolumeControl bne .checkForScoring ; unconditional branch .clearBallInHoopStatus lda #0 sta ballInHoopStatus .checkForScoring lda scoringBasketStatus ; get scoring basket status bpl .checkToMoveBallHorizontally ; branch if ball not in basket ldy ballBounceHeight ; get ball bouncing height cpy #30 bcs .checkToMoveBallHorizontally and #BASKET_SCORED_MASK ; keep D0 value for which basket scored tax ; x holds which basket scored sta scoringBasketStatus ldy #$08 sty rightSoundChannelVolumeControl eor #1 ; flip D0 so it now represents other player tay lda newOffensivePlayerHorizPos ; get offensive player position value sta playersHorizPos,y ; set player's new horizontal position lda #Y_MID_COURT sta playersVertPos,y ; set offensive player vertical position ldy #(W_SCREEN + 1) / 2 sty playersHorizPos,x ; place defensive player at center court lda gameState ; get the current game state beq .checkToMoveBallHorizontally ; skip score increment if game over clc sed lda playerScores,x ; get the player's score adc #2 ; increment the player's score by 2 points sta playerScores,x ; no three point shoots here :-) cld txa ; move player number to accumulator beq .decreaseCPUPlayerAggression ; branch if this for player 1 lsr cpuPlayerAggression ; increase computer player aggression bne .checkToMoveBallHorizontally .decreaseCPUPlayerAggression sec ; set carry rol cpuPlayerAggression ; roll carry bit into D0 .checkToMoveBallHorizontally dec decBallHorizMotionDelay bne .checkToMoveBallVertically lda ballHorizMotionDelay sta decBallHorizMotionDelay dec ballHorizPos ; decrement ball horizontal position bit ballBouncingDirectionStatus ; check ball bouncing direction bmi .checkToMoveBallVertically ; branch if ball bouncing left inc ballHorizPos inc ballHorizPos ; bounce the ball to the right .checkToMoveBallVertically dec decBallVertMotionDelay bne DetermineBallLiftAndGravity lda ballVertMotionDelay sta decBallVertMotionDelay dec ballVerticalPosition ; move ball up the court bit ballBouncingDirectionStatus ; check ball bouncing direction bvc DetermineBallLiftAndGravity ; branch if ball bouncing up inc ballVerticalPosition inc ballVerticalPosition ; bounce ball down DetermineBallLiftAndGravity lda frameCount ; get the current frame count and #3 bne DetermineToAllowBlockedShot clc lda ballBounceHeight ; get the current ball bounce height sta oldBallBounceHeight ; set for comparing later adc shootingBallLift ; increment height by lift sec sbc ballGravityValue ; subtract current gravity value bcs .setBallBounceHeight lda shootingBallLift ; get shooting ball lift value lsr ; divide value by 4 lsr sta tmpShootingBallLiftDiv4 sec lda shootingBallLift ; get shooting ball lift value sbc tmpShootingBallLiftDiv4 ; subtract divide by 4 value sta shootingBallLift ; shooting lift value reduced by 25% lda #<-1 sta ballGravityValue lda #0 .setBallBounceHeight sta ballBounceHeight inc ballGravityValue DetermineToAllowBlockedShot sta CXCLR ; clear hardware collisions ldy #0 ; assume player allowed to block shot bit scoringBasketStatus ; check scoring basket status bmi .noGoalTendingAllowed ; branch if player made basket lda ballBounceHeight ; get the ball bounce height cmp oldBallBounceHeight ; compare with old ball bounce height bcs .setStatusToAllowBlockingShot; branch if greater than old height cmp #40 bcc .setStatusToAllowBlockingShot .noGoalTendingAllowed dey ; set to non-zero to not allow shot blocking .setStatusToAllowBlockingShot sty allowToBlockBallStatus sec lda ballVerticalPosition ; get ball vertical position sbc ballBounceHeight ; subtract ball bounce height sta ballScanline ; set ball scanline for kernel lda ballBounceHeight cmp #2 bcs PlayGameSounds lda #2 sta leftSoundChannelVolumeControl PlayGameSounds lda #$1F sta AUDF0 lda #$0F sta AUDC0 sta AUDF1 dec leftSoundChannelVolumeControl bpl .setLeftChannelVolume lda #0 ; set to turn off left channel volume inc leftSoundChannelVolumeControl; value now set to 0 .setLeftChannelVolume sta AUDV0 lda #8 dec rightSoundChannelVolumeControl bpl .setRightChannelVolume lda #0 ; set to turn off right channel volume inc rightSoundChannelVolumeControl; value now set to 0 .setRightChannelVolume sta AUDV1 lda gameState ; get the current game state bne SetupPlayerGraphics ; branch if the game is in progress sta AUDV0 ; turn off channel 0 volume sta AUDV1 ; turn off channel 1 volume SetupPlayerGraphics ldx #1 lda #>PlayerSprites sta playerLegSpritePointer + 1 ; set sprite MSB values sta playerBodySpritePointer + 1 lda #>playerGraphics sta spriteRAMPointer + 1 ; set the MSB value for the player graphics .determinePayerSpriteGraphics lda frameCount ; get current frame count and #4 bne .determineSpriteLegAnimation sta playerRunningAnimation,x .determineSpriteLegAnimation lda playerRunningAnimation,x beq .setPlayerSpriteLegsToStationary lda #