;how do i get scan line count? ;how do I find out how much ROM I still have to play with before 4K?? ; ;improvements ; ; ; better constant vs variable vs label naming schemes ; computer AI! ; softer bounces on ceiling ; computer player wing flap graphic ; enabled "bufferstuffer" logic for ball enable, not just graphic ;todo: ;*scoring, ball reset ; ; *variable vert ball speed?? ; ;* restore color background, see if junk at top = outta time ;* use missiles to cover background "goal posts"? ; ; ;; longer term: ;* select computer or human for p2 ;**2 computer players = lockstep... ;* title screen ;* sounds ; ; JoustPong by Kirk Israel processor 6502 include vcs.h org $F000 ;-------------------------------------- ;VARIABLES ;-------------------------------------- p0YPosFromBot = $80 p0VisibleLine = $81 p0DrawBuffer = $82 p0VertSpeed = $83 p1YPosFromBot = $84 p1VisibleLine = $85 p1DrawBuffer = $86 p1VertSpeed = $87 but0WasOn = $88 but1WasOn = $8A gravTimer = $8B ballPosFromBot = $8C ballVisibleLine = $8D ballVertSpeed = $8E ballBuffer = $8F p0score = $90 p1score = $91 ;-------------------------------------- ;CONSTANTS ;-------------------------------------- FLAP_STRENGTH = #3 ;how strong up is a flap? GRAV_DELAY = #8 ;How often does gravity pull 'em down? CEILING_HEIGHT = #80 ;floor heights are different, because heights are actually ;relative to the 'top' of the player or ball, but we want to ;make sure that the bottoms are hitting the floor FLOOR_HEIGHT_FOR_BALL = #4 FLOOR_HEIGHT_FOR_PLAYERS = #10 STRENGTH_OF_CEILING_REBOUND = #3 SCORE_KERNAL_LENGTH = #5 GAME_KERNAL_LENGTH = #80 ;MAXIMUM_SPEED = #6 ;-------------------------------------- ;BOILER PLATE STARTUP ;-------------------------------------- Start SEI CLD TXS LDX #$FF LDA #0 ClearMem STA 0,X DEX BNE ClearMem LDA #$00 STA COLUBK ;-------------------------------------- ;OTHER INITIALIZATIONS ;-------------------------------------- LDA #GRAV_DELAY STA gravTimer ;initialize gravity timer (only 1 in N ticks do we pull) LDA #33 STA COLUP0 ;Set P0 Reddish LDA #66 STA COLUP1 ;Set P1 Purplish LDA #120 STA p0YPosFromBot ;P0 Initial Y Position STA p1YPosFromBot ;P1 Initial Y Position LDA #0 STA p0VertSpeed STA p1VertSpeed LDA #6 STA p0score LDA #7 STA p1score ;generate the background ;this is just one dot on the edge ;reflected. we'll use it for collision ;detection to know when a goal ;is scored ;-------------------------------------- ;SETTING UP PLAYFIELD AND BALL ETC ;-------------------------------------- LDA #%00100000 STA PF0 LDA #99 STA COLUPF ;color here LDA #%00010001 STA CTRLPF LDA #%00000010 STA ENABL LDA #%00010000 STA HMBL ;TODO: don't just oddly put in ball sped ;LDA #%11111111 ;LDA #%00000001 LDA #1 STA ballVertSpeed ;position ball LDA #20 STA ballPosFromBot ;make missiles double wide LDA #%00110000 STA NUSIZ0 ;-------------------------------------- ;OBJECT POSITIONING--VERY PRIMITIVE! ;-------------------------------------- ;use NOPs to position the players LDA #0 STA WSYNC NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP STA RESM0 STA RESP0 NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP STA RESBL NOP NOP NOP NOP NOP NOP NOP STA RESP1 NOP STA RESM1 LDA #%10101010 STA COLUPF ;-------------------------------------- ;START MAIN LOOP W/ VSYNC ;-------------------------------------- MainLoop LDA #2 STA VSYNC STA WSYNC STA WSYNC STA WSYNC LDA #43 STA TIM64T LDA #0 STA VSYNC ;-------------------------------------- ;SEE IF BUTTON 0 IS NEWLY PRESSED ;-------------------------------------- CheckButton0 LDA INPT4 BMI NoButton0 ;Check to see if the button was already down LDA but0WasOn BNE Button0WasAlreadyDown ;New Button Pressed Time to Flap! LDA p0VertSpeed SEC SBC #FLAP_STRENGTH STA p0VertSpeed LDA #1 STA but0WasOn Button0WasAlreadyDown JMP EndButton0 NoButton0 ;button wasn't pressed, remember that LDA #0 STA but0WasOn EndButton0 ;;-------------------------------------- ;;SEE IF BUTTON 1 IS NEWLY PRESSED ;;-------------------------------------- ; ; ;CheckButton1 ; LDA INPT5 ; BMI NoButton1 ; ; ;Check to see if the button was already down ; LDA but1WasOn ; BNE Button1WasAlreadyDown ; ; ;New Button Pressed Time to Flap! ; LDA p1VertSpeed ; SEC ; SBC #FLAP_STRENGTH ; STA p1VertSpeed ; ; LDA #1 ; STA but1WasOn ;Button1WasAlreadyDown ; JMP EndButton1 ;NoButton1 ;button wasn't pressed, remember that ; LDA #0 ; STA but1WasOn ;EndButton1 ; ;-------------------------------------- ;AI for Player 1 ;-------------------------------------- ;is p1 lower than the ball? LDA p1YPosFromBot CMP ballPosFromBot BCS ResetP1WingGraphic ;P1 is lower, give it a flap LDA p1VertSpeed SEC SBC #FLAP_STRENGTH STA p1VertSpeed LDA #1 STA but1WasOn ;needed to make wing down appear JMP DoneCheckingP1BeneathBall ResetP1WingGraphic LDA #0 STA but1WasOn ;needed to make wing up appear DoneCheckingP1BeneathBall ;-------------------------------------- ;SEE IF ITS TIME TO ADD 1 TO PLAYER'S SPEEDS ;THANKS TO GRAVITY ;-------------------------------------- DEC gravTimer BNE DoneWithGravity INC p0VertSpeed INC p1VertSpeed LDA #GRAV_DELAY STA gravTimer DoneWithGravity ;-------------------------------------- ;SEE IF BALL HIT PLAYER 0 ;-------------------------------------- LDA #%01000000 BIT CXP0FB BEQ NoCollisionBallP0 ;skip if not hitting... ;we have a hit LDA #%11110000 ;ball goes right STA HMBL ;adjust ball vert speed w/ players speed ; LDA p0VertSpeed ;; CLC ;; ADC ballVertSpeed ; STA ballVertSpeed NoCollisionBallP0 ;-------------------------------------- ;SEE IF BALL HIT PLAYER 1 ;-------------------------------------- LDA #%01000000 BIT CXP1FB BEQ NoCollisionBallP1 ;skip if not hitting... ;we have a hit LDA #%00010000 ;ball goes left STA HMBL ;adjust ball vert speed w/ players speed ; LDA p1VertSpeed ;; CLC ;; ADC ballVertSpeed ; STA ballVertSpeed NoCollisionBallP1 STA CXCLR ;reset the collision detection for next time ;-------------------------------------- ;ADJUST BALL VERTICAL POSITION ;-------------------------------------- ;add negative of ball speed to ball to "0" LDA #0 SEC SBC ballVertSpeed ;and add old psition to that CLC ADC ballPosFromBot STA ballPosFromBot ;-------------------------------------- ;SEE IF BALL HIT FLOOR ;-------------------------------------- LDA #FLOOR_HEIGHT_FOR_BALL CLC CMP ballPosFromBot BCC DoneCheckingHitFloorBall ;ball speed is negative of previous ball speed LDA #0; SEC SBC ballVertSpeed STA ballVertSpeed ;place ball on floor LDA #FLOOR_HEIGHT_FOR_BALL ; floor for ball? STA ballPosFromBot DoneCheckingHitFloorBall ;-------------------------------------- ;SEE IF BALL HIT CEILING ;-------------------------------------- ;check if player 0 hit ceiling - full rebound LDA #CEILING_HEIGHT ;#was 180 before 2 line kernal CMP ballPosFromBot BCS DoneCheckingHitCeilingBall ;ball speed is negative of previous ball speed LDA #0; SEC SBC ballVertSpeed STA ballVertSpeed ;place ball on floor LDA #CEILING_HEIGHT ;#was 180 STA ballPosFromBot DoneCheckingHitCeilingBall ;-------------------------------------- ;MOVE THE PLAYERS ;-------------------------------------- ;Add the negative of the Player 0 Vert Speed to 0 in A LDA #0 SEC SBC p0VertSpeed ;Then add the current position of p0 CLC ADC p0YPosFromBot STA p0YPosFromBot ;Add the negative of the Player 1 Vert Speed to 0 in A LDA #0 SEC SBC p1VertSpeed ;Then add the current position p1 CLC ADC p1YPosFromBot STA p1YPosFromBot ;-------------------------------------- ;SEE IF PLAYER 0 HIT FLOOR ;-------------------------------------- ;check if player 0 hit floor LDA #FLOOR_HEIGHT_FOR_PLAYERS ;10 is floor CLC CMP p0YPosFromBot BCC DoneCheckingHitFloorP0 ;speed should be positve; let's divide it my two and then ;subtract it from zero to get the new speed (i.e. a half rebound) LDA p0VertSpeed CLC ROR STA p0VertSpeed LDA #0 SEC SBC p0VertSpeed STA p0VertSpeed LDA #FLOOR_HEIGHT_FOR_PLAYERS STA p0YPosFromBot ;putplayer on floor DoneCheckingHitFloorP0 ;-------------------------------------- ;SEE IF PLAYER 1 HIT FLOOR ;-------------------------------------- ;check if player 1 hit floor LDA #FLOOR_HEIGHT_FOR_PLAYERS ;10 is floor CLC CMP p1YPosFromBot BCC DoneCheckingHitFloorP1 ;we need a better bounce routine, like reducing the speed? ;speed should be positve; let's divide it my two and then ;subtract it from zero to get the new speed (i.e. a half rebound) LDA p1VertSpeed CLC ROR STA p1VertSpeed LDA #0 SEC SBC p1VertSpeed STA p1VertSpeed LDA #FLOOR_HEIGHT_FOR_PLAYERS STA p1YPosFromBot ;putplayer on floor DoneCheckingHitFloorP1 ;-------------------------------------- ;SEE IF PLAYER 0 HIT CEILING ;-------------------------------------- ;check if player 0 hit ceiling - LDA #CEILING_HEIGHT CMP p0YPosFromBot BCS DoneCheckingHitCeilingP0 LDA #STRENGTH_OF_CEILING_REBOUND STA p0VertSpeed LDA #CEILING_HEIGHT ;#was 180 STA p0YPosFromBot DoneCheckingHitCeilingP0 ;-------------------------------------- ;SEE IF PLAYER 1 HIT CEILING ;-------------------------------------- ;check if player 1 hit ceiling - LDA #CEILING_HEIGHT CMP p1YPosFromBot BCS DoneCheckingHitCeilingP1 LDA #STRENGTH_OF_CEILING_REBOUND STA p1VertSpeed LDA #CEILING_HEIGHT ;#was 180 STA p1YPosFromBot DoneCheckingHitCeilingP1 ;;-------------------------------------- ;;SEE IF PLAYER 0 HIT CEILING ;;-------------------------------------- ; ;;check if player 0 hit ceiling - ; LDA #CEILING_HEIGHT ;#was 180 before 2 line kernal ; CMP p0YPosFromBot ; BCS DoneCheckingHitCeilingP0 ; ; LDA #0; ; SEC ; ; SBC p0VertSpeed ; STA p0VertSpeed ; LDA #CEILING_HEIGHT ;#was 180 ; STA p0YPosFromBot ; ;DoneCheckingHitCeilingP0 ; ;;-------------------------------------- ;;SEE IF PLAYER 1 HIT CEILING ;;-------------------------------------- ; ;;check if player 1 hit ceiling - full rebound ; LDA #CEILING_HEIGHT ;#was 180 before 2 line kernal ; CMP p1YPosFromBot ; BCS DoneCheckingHitCeilingP1 ; ; LDA #0; ; SEC ; ; SBC p1VertSpeed ; STA p1VertSpeed ; LDA #CEILING_HEIGHT ; was 180 ; STA p1YPosFromBot ; ;DoneCheckingHitCeilingP1 ; ;-------------------------------------- ;WAIT FOR VBLANK TO END ;-------------------------------------- WaitForVblankEnd LDA INTIM BNE WaitForVblankEnd STA VBLANK STA WSYNC STA HMOVE ;-------------------------------------- ;SCORE DISPLAY KERNAL ;-------------------------------------- LDY #SCORE_KERNAL_LENGTH ;make sure p1 isn't reversed LDA #%00000000 STA REFP1 ScoreDisplayLoop LDA p0score ;accumulator = score ROL ;accumulator = score * 2 ROL ;accumulator = score * 4 CLC ADC p0score ;accumulator = (score * 4) + score = score * 5 CLC ADC Score0Graphic STA GRP0 STA GRP1 STA WSYNC STA WSYNC DEY BNE ScoreDisplayLoop LDA #0 STA GRP0 STA GRP1 STA WSYNC STA WSYNC STA WSYNC STA WSYNC ;flip p1 before game kernal LDA #%00001000 STA REFP1 STA WSYNC ;-------------------------------------- ;MAIN GAME KERNAL ;-------------------------------------- LDY #GAME_KERNAL_LENGTH ;-------------------------------------- ;MAIN SCANLINE LOOP ;-------------------------------------- MainGameLoop STA WSYNC ;-------------------------------------- ;PLAYER BUFFER STUFFER LOGIC ; the idea is that we've already calculated ; the graphic for the players (and the enable ; of the ball) during the ; last scan line, so all we have to do now ; is jam that predrawn buffer into the right ; graphics register--which we have time for ; during the horizontal blank ;-------------------------------------- LDA ballBuffer STA ENABL LDA p0DrawBuffer ;[0]+3 STA GRP0 ;[3]+3 LDA p1DrawBuffer ;[6]+3 STA GRP1 ;[9]+3 ; here the idea is that p0VisibleLine ; is zero if the line isn't being drawn now, ; otherwise it's however many lines we have to go CheckActivatePlayer0 CPY p0YPosFromBot ;[12]+4 BNE SkipActivatePlayer0 ;[16]+3 LDA #8 ;8 lines tall ;[19]+3 STA p0VisibleLine ;[22]+3 SkipActivatePlayer0 ;turn player off then see if it should be on LDA #00 ;[25]+3 ; ;if the p0VisibleLine is non zero, ;we're drawing it ; LDX p0VisibleLine ;[28]+3 BEQ FinishPlayer0 ;[31]+3 IsPlayer0On LDA but0WasOn ;[34]+3 DoWing0 BNE DrawWing0Down ;[37]+3 DrawWing0Up LDA WingUpGraphic-1,X ;[40]+3 JMP Wing0Finish ;[43]+3 DrawWing0Down LDA WingDownGraphic-1,X Wing0Finish DEC p0VisibleLine ;[46]+3 FinishPlayer0 STA p0DrawBuffer ;[49]+3 ; here the idea is that p1VisibleLine ; is zero if the line isn't being drawn now, ; otherwise it's however many lines we have to go CheckActivatePlayer1 CPY p1YPosFromBot BNE SkipActivatePlayer1 LDA #8 ;8 lines tall STA p1VisibleLine SkipActivatePlayer1 ;turn player off then see if it should be on LDA #00 ; ;if the p0VisibleLine is non zero, ;we're drawing it ; LDX p1VisibleLine BEQ FinishPlayer1 IsPlayer1On LDA but1WasOn DoWing1 BNE DrawWing1Down DrawWing1Up LDA WingUpGraphic-1,X JMP Wing1Finish DrawWing1Down LDA WingDownGraphic-1,X Wing1Finish DEC p1VisibleLine FinishPlayer1 STA p1DrawBuffer CheckActivateBall CPY ballPosFromBot ;compare Y to the YPosFromBot... BNE SkipActivateBall ;if not equal, skip this... LDA #2 ;otherwise say that this should go STA ballVisibleLine ;on for 2 lines SkipActivateBall LDA #0 STA ballBuffer LDA ballVisibleLine ;load the value of what missile line we're showing BEQ FinishBall ;if zero we aren't showing, skip it BallMustBeOn LDA #2 ;otherwise STA ballBuffer ;showit DEC ballVisibleLine ;and decrement the missile line thing FinishBall ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP ; NOP NOP NOP NOP NOP NOP ;now just finish counting of scanloop DEY BNE MainGameLoop LDA #2 STA WSYNC STA VBLANK LDX #30 OverScanWait STA WSYNC DEX BNE OverScanWait JMP MainLoop org $FF00 ;-------------------------------------- ;GRAPHICS ;-------------------------------------- WingUpGraphic .byte #%00001100 .byte #%00001100 .byte #%10001100 .byte #%11011100 .byte #%11111100 .byte #%01111100 .byte #%00101100 .byte #%00001100 WingDownGraphic .byte #%00001100 .byte #%00011100 .byte #%00111100 .byte #%01111100 .byte #%01111100 .byte #%00111100 .byte #%00001100 .byte #%00001100 Score0Graphic .byte #%00111100 .byte #%01000010 .byte #%01000010 .byte #%01000010 .byte #%00111100 Score1Graphic .byte #%00111110 .byte #%00001000 .byte #%00001000 .byte #%00101000 .byte #%00011000 Score2Graphic .byte #%01111110 .byte #%01100000 .byte #%00011100 .byte #%01000010 .byte #%00111100 Score3Graphic .byte #%01111100 .byte #%00000010 .byte #%00011100 .byte #%00000010 .byte #%01111100 Score4Graphic .byte #%00000100 .byte #%00000100 .byte #%01111110 .byte #%01000100 .byte #%01000100 Score5Graphic .byte #%01111100 .byte #%00000010 .byte #%01111100 .byte #%01000000 .byte #%01111110 Score6Graphic .byte #%00111100 .byte #%01000010 .byte #%01111100 .byte #%01100000 .byte #%00011110 Score7Graphic .byte #%00010000 .byte #%00001000 .byte #%00000100 .byte #%00000010 .byte #%01111110 Score8Graphic .byte #%00111100 .byte #%01000010 .byte #%00111100 .byte #%01000010 .byte #%00111100 Score9Graphic .byte #%00000010 .byte #%00000010 .byte #%00011110 .byte #%00100010 .byte #%00011100 org $FFFC .word Start .word Start ;;;;THE JUNKYARD ;;See if we're going too darn fast ; LDA MaximumSpeed ; SEC ; ;;;;SBC MaximumSpeed ; Maximum Speed ; ; CMP p0VertSpeed ; BCS SpeedNotMaxxed ; ; ;; BMI SpeedNotMaxxed ;if speed - maxspeed is positive, we need to slow down ; LDA MaximumSpeed ; STA p0VertSpeed ; ;SpeedNotMaxxed ;;assum horiz movement will be zero ; LDX #$00 ; LDA #$40 ;Left? ; BIT SWCHA ; BNE SkipMoveLeftP0 ; LDX #$10 ; LDA #%00001000 ; STA REFP0 ;show reflected version ;SkipMoveLeftP0 ; ; LDA #$80 ;Right? ; BIT SWCHA ; BNE SkipMoveRightP0 ; LDX #$F0 ; LDA %00000000 ; STA REFP0 ;SkipMoveRightP0 ; ; STX HMP0 ;set horiz movement for player 0 ; ; ;;assum horiz movement will be zero ; LDX #$00 ; LDA #$04 ;Left? ; BIT SWCHA ; BNE SkipMoveLeftP1 ; LDX #$10 ; LDA #%00001000 ; STA REFP1 ;SkipMoveLeftP1 ; ; LDA #$08 ;Right? ; BIT SWCHA ; BNE SkipMoveRightP1 ; LDX #$F0 ; LDA %00000000 ; STA REFP1 ;SkipMoveRightP1 ; ; STX HMP1 ;set horiz movement for player 0 ;BigHeadGraphic ; .byte %00111100 ; .byte %01111110 ; .byte %11000001 ; .byte %10111111 ; .byte %11111111 ; .byte %11101011 ; .byte %01111110 ; .byte %00111100