; ; ;bugs ;-framerate problem on title? ;-have computer flap wing down graphic last longer ; ;todo ;-make sure things are reset better ;-change game by joystick hit, start game with button ;-move pterry up and down ;better logic when ball hits pterry ; JoustPong by Kirk Israel processor 6502 include vcs.h include macro.h ;-------------------------------------- ;CONSTANTS ;-------------------------------------- CEILING_HEIGHT = #88 SLOW_GRAV_LO_BYTE = #%11110000 SLOW_GRAV_HI_BYTE = #%11111111 SLOW_FLAP_LO_BYTE = #%11001000 SLOW_FLAP_HI_BYTE = #%00000000 SLOW_REBOUND_LO_BYTE = #%00000000 SLOW_REBOUND_HI_BYTE = #%11111111 FAST_FLAP_LO_BYTE = #%00000000 FAST_FLAP_HI_BYTE = #%00000001 SLOW_BALL_RIGHT_SPEED_LO_BYTE = #%10000000 SLOW_BALL_RIGHT_SPEED_HI_BYTE = #%00000000 SLOW_BALL_LEFT_SPEED_LO_BYTE = #%10000000 SLOW_BALL_LEFT_SPEED_HI_BYTE = #%11111111 GAMEFIELD_HEIGHT_IN_BRICKS = #22 SPRITEHEIGHT = #8 ;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 = #88 LENGTH_OF_FLAPSOUND = #15 PITCH_OF_FLAPSOUND = #15 ;2!,8-,15 all kind of worked TYPE_OF_FLAPSOUND = #2 VOLUME_OF_PONGHIT = #7 PITCH_OF_PONGHIT = #7 PITCH_OF_GOAL = #15 PITCH_OF_PONG_WALL_HIT = #25 ;was 6 pixel height, 6 scanlines per ; 74 - 36 = 38 PIXEL_HEIGHT_OF_TITLE = #30 PIXEL_HEIGHT_OF_TITLE_PONG = #7 SCANLINES_PER_TITLE_PIXEL = #2 WINNING_SCORE = #10 BALLPOS_LEFT = #5 ;had to hack so it didn't show up on right side before reset... BALLPOS_CENTER = #80 BALLPOS_RIGHT = #160 PTERRY_LEFT_BOUNDARY = #25 ;had to hack so it didn't show up on right side before reset... PTERRY_RIGHT_BOUNDARY = #125 MUSICRIFF_NOTECOUNT = #16 MUSICBEAT_NOTECOUNT = #12 PTERRY_LENGTH_OF_WINGCHANGE = #10 ;-------------------------------------- ;VARIABLES ;-------------------------------------- SEG.U VARS ORG $80 copyIntegerCoordP0 ds 1 copyIntegerCoordP1 ds 1 slowP0YCoordFromBottom ds 2 slowP0YSpeed ds 2 slowP1YCoordFromBottom ds 2 slowP1YSpeed ds 2 p0VisibleLine ds 1 p0DrawBuffer ds 1 p1VisibleLine ds 1 p1DrawBuffer ds 1 but0WasOn ds 1 but1WasOn ds 1 ballPosFromBot ds 1 ballVisibleLine ds 1 ballVertSpeed ds 1 ballBuffer ds 1 p0score ds 1 p1score ds 1 pointerP0Score ds 2 pointerP1Score ds 2 pointerP0Graphic ds 2 pointerP1Graphic ds 2 booleanBallRight ds 1 flapsoundRemaining ds 1 booleanGameIsTwoPlayers ds 1 ;variableGameMode ds 1 booleanSelectSwitchIsDown ds 1 booleanResetSwitchIsDown ds 1 booleanGameOver ds 1 booleanOverrideSelectChangeThisTime ds 1 bufferPFLeft ds 1;;WALL;; bufferPFRight ds 1;;WALL;; playfieldMatrixLeft ds 22;;WALL;; playfieldMatrixRight ds 22;;WALL;; booleanFujiFrame ds 1 musicRiffNotePointer ds 2 musicRiffNoteCounter ds 1 musicRiffNoteTimeLeft ds 1 musicBeatNotePointer ds 2 musicBeatNoteCounter ds 1 musicBeatNoteTimeLeft ds 1 ;for horiz pos... ;PlayerX ds 2 ;!!!temp PS_temp ds 1 ballXposition ds 2 booleanDisplayPterry ds 1 booleanSwitchP0AndPterry ds 1 cacheRefP0 ds 1 cacheRefP1 ds 1 cacheSizingP0 ds 1 cacheSizingP1 ds 1 booleanPterryGoesRight ds 1 booleanPterryWingIsUp ds 1 counterPterryWingChange ds 1 bufferPterryGraphic ds 1 pterryHorizPosition ds 1 cachePterryReflection ds 1 tempVar ds 1 physicalPlayerHorizPos ds 1 echo ($100 - *) , "bytes of RAM left" SEG CODE org $F000 ;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 #>Score0Graphic ;grab the hight byte of the graphic location for score graphics.... sta pointerP0Score+1 sta pointerP1Score+1 lda #33 sta COLUP0 ;Set P0 Reddish lda #66 sta COLUP1 ;Set P1 Purplish ;lda #%11111111 lda #0 sta booleanGameIsTwoPlayers lda #0 sta booleanBallRight ;start ball moving right lda #BALLPOS_CENTER sta pterryHorizPosition lda #%00100000 ;;WALL;; ;;wALL;; ldx #GAMEFIELD_HEIGHT_IN_BRICKS-1;;WALL;; InitTheBricks;;WALL;; ;every other lda #%00100000 ;;WALL;; ;every other sta playfieldMatrixLeft,X;;WALL;; ;every other sta playfieldMatrixRight,X;;WALL;; ;every other dex;;WALL;; ;every other beq NoMoBricks ;every other lda #%00000000 ;;WALL;; sta playfieldMatrixLeft,X;;WALL;; sta playfieldMatrixRight,X;;WALL;; dex;;WALL;; ;every other NoMoBricks bne InitTheBricks;;WALL;; ;-------------------------------------- ;START THE TITLE SCREEN ;-------------------------------------- TitleStart ;ok, now we're getting the usual 'just hit reset stuff' lda #$1F sta COLUPF ;colored playfield for title lda #0 sta CTRLPF ;playfield ain't reflected ;sta AUDV0 ;sta AUDV1 LDA #12 STA AUDC0 LDA #8;8 STA AUDC1 ;-------------------------------------- ;OBJECT POSITIONING--VERY PRIMITIVE! ;-------------------------------------- ;use NOPs to position the players lda #0 sta WSYNC SLEEP 20 ;Thomas's Sleep Macro sta RESM0 sta RESP0 SLEEP 37 ;Thomas's Sleep Macro sta RESP1 nop sta RESM1 ;-------------------------------------- ;-------------------------------------- ; TITLE SCREEN ;-------------------------------------- ;-------------------------------------- TitleMainLoop lda SWCHB ;read console switches and #%00000001 ;is game reset? bne TitleResetWasNotHit ;(no, sjip next jump) jmp MainGameStart ;yes, go to start of game TitleResetWasNotHit lda SWCHB ;read console switches again and #%00000010 ;is game select? bne TitleSelectIsNotDownNow ;(no, skip next jump) ; ; our key to only reacting to select once is to react when someone ; lets go of it ; if its down we record that its down in booleanSelectSwitchIsDown ; if its up, we see if it was down last go around TitleSelectIsDownNow lda #1 sta booleanSelectSwitchIsDown ;note in boolean (representing select switch STATE) jmp TitleDoneCheckingSelect ;we're done, since we're reacting to switch being released TitleSelectIsNotDownNow lda booleanSelectSwitchIsDown ;see if it was on before bne TitleSelectWasAlreadyOn ;if it's up now, but was down before, time to act... TitleSelectWasNotOn jmp TitleDoneCheckingSelect ; it wasn't on, it's still not on, what are we worried about? TitleSelectWasAlreadyOn ; NOW: it was down, is now up, time to act... lda #0 sta booleanSelectSwitchIsDown ;record that it's up for future reference lda booleanOverrideSelectChangeThisTime ;??? bne OverridingSelectChange lda booleanGameIsTwoPlayers eor #%11111111 ;toggle the boolean for # of players sta booleanGameIsTwoPlayers ;;;;;;;DEC variableGameMode OverridingSelectChange lda #0 sta booleanOverrideSelectChangeThisTime TitleDoneCheckingSelect ; ;MUSIC! ; DEC musicRiffNoteTimeLeft BPL DoneWithChangingNote DEC musicRiffNoteCounter BPL DoneCheckResetNoteCounter LDA #MUSICRIFF_NOTECOUNT-1 STA musicRiffNoteCounter DoneCheckResetNoteCounter LDY musicRiffNoteCounter LDA MusicLengthData,Y STA musicRiffNoteTimeLeft DEC musicRiffNoteTimeLeft ;off by one error... LDA MusicPitchData,Y BMI ZeroOutSound STA AUDF0 LDA #8 ;noise STA AUDV0 JMP DoneSettingPitchAndVolume ZeroOutSound LDA #0 ;silence STA AUDV0 DoneSettingPitchAndVolume DoneWithChangingNote DEC musicBeatNoteTimeLeft BPL DoneWithChangingBeat DEC musicBeatNoteCounter BPL DoneCheckResetBeatCounter LDA #MUSICBEAT_NOTECOUNT-1 STA musicBeatNoteCounter DoneCheckResetBeatCounter LDY musicBeatNoteCounter LDA BeatLengthData,Y STA musicBeatNoteTimeLeft DEC musicBeatNoteTimeLeft ;off by one error... LDA BeatPitchData,Y BMI ZeroOutBeatSound STA AUDF1 LDA #8 ;noise STA AUDV1 JMP DoneSettingBeatPitchAndVolume ZeroOutBeatSound LDA #0 ;silence STA AUDV1 DoneSettingBeatPitchAndVolume DoneWithChangingBeat ;MainLoop starts with usual VBLANK code, ;and the usual timer seeding lda #2 sta VSYNC sta WSYNC sta WSYNC sta WSYNC lda #43 sta TIM64T lda #0 sta VSYNC ;--------------------- ;alternate the fuji logo for kicks ;--------------------- lda booleanFujiFrame beq FujiWasZeroInBlank FujiWasNotZeroInBlank lda #0 sta booleanFujiFrame jmp DoneSettingFuji FujiWasZeroInBlank lda #1 sta booleanFujiFrame DoneSettingFuji TitleWaitForVblankEnd lda INTIM bne TitleWaitForVblankEnd sta VBLANK ;just burning scanlines....you could do something else ldy #20 ;20 scanlines ;FIRST WE DO JOUST TitlePreLoop sta WSYNC ;wait for sync for each one... dey bne TitlePreLoop lda #$1E sta COLUPF ldx #PIXEL_HEIGHT_OF_TITLE ; X will hold what letter pixel we're on ldy #SCANLINES_PER_TITLE_PIXEL ; Y will hold which scan line we're on for each pixel ; ;the next part is careful cycle counting from those ;who have gone before me to get full non-reflected playfield TitleShowLoop sta WSYNC lda PFDataTitleJoust0Left-1,X ;[0]+4 sta PF0 ;[4]+3 = *7* < 23 ;PF0 visible lda PFDataTitleJoust1Left-1,X ;[7]+4 sta PF1 ;[11]+3 = *14* < 29 ;PF1 visible lda PFDataTitleJoust2Left-1,X ;[14]+4 sta PF2 ;[18]+3 = *21* < 40 ;PF2 visible nop ;[21]+2 nop ;[23]+2 nop ;[25]+2 ;six cycles available Might be able to do something here lda PFDataTitleJoust0Right-1,X ;[27]+4 ;PF0 no longer visible, safe to rewrite sta PF0 ;[31]+3 = *34* lda PFDataTitleJoust1Right-1,X ;[34]+4 ;PF1 no longer visible, safe to rewrite sta PF1 ;[38]+3 = *41* lda PFDataTitleJoust2Right-1,X ;[41]+4 ;PF2 rewrite must begin at exactly cycle 45!!, no more, no less sta PF2 ;[45]+2 = *47* ; > dey ;ok, we've drawn one more scaneline for this 'pixel' bne NotChangingWhatTitlePixel ;go to not changing if we still have more to do for this pixel dex ; we *are* changing what title pixel we're on... beq DoneWithTitle ; ...unless we're done, of course ldy #SCANLINES_PER_TITLE_PIXEL ;...so load up Y with the count of how many scanlines for THIS pixel... NotChangingWhatTitlePixel jmp TitleShowLoop DoneWithTitle nop nop nop lda #$7C sta COLUPF ldx #PIXEL_HEIGHT_OF_TITLE_PONG ; X will hold what letter pixel we're on ldy #SCANLINES_PER_TITLE_PIXEL ; Y will hold which scan line we're on for each pixel ; ;THEN WE DO PONG PongTitleShowLoop sta WSYNC lda PFDataTitlePong0Left-1,X ;[0]+4 sta PF0 ;[4]+3 = *7* < 23 ;PF0 visible lda PFDataTitlePong1Left-1,X ;[7]+4 sta PF1 ;[11]+3 = *14* < 29 ;PF1 visible lda PFDataTitlePong2Left-1,X ;[14]+4 sta PF2 ;[18]+3 = *21* < 40 ;PF2 visible nop ;[21]+2 nop ;[23]+2 nop ;[25]+2 ;six cycles available Might be able to do something here lda PFDataTitlePong0Right-1,X ;[27]+4 ;PF0 no longer visible, safe to rewrite sta PF0 ;[31]+3 = *34* lda PFDataTitlePong1Right-1,X ;[34]+4 ;PF1 no longer visible, safe to rewrite sta PF1 ;[38]+3 = *41* lda PFDataTitlePong2Right-1,X ;[41]+4 ;PF2 rewrite must begin at exactly cycle 45!!, no more, no less sta PF2 ;[45]+2 = *47* ; > dey ;ok, we've drawn one more scaneline for this 'pixel' bne NotChangingWhatPongTitlePixel ;go to not changing if we still have more to do for this pixel dex ; we *are* changing what title pixel we're on... beq DoneWithPongTitle ; ...unless we're done, of course ldy #SCANLINES_PER_TITLE_PIXEL ;...so load up Y with the count of how many scanlines for THIS pixel... NotChangingWhatPongTitlePixel ; stx tempVar ; ; lda #7 ; clc ; cmp tempVar ; bcc DoneCheckingChangePFColor ; jmp PongTitleShowLoop DoneWithPongTitle ;clear out the playfield registers for obvious reasons lda #0 sta PF2 ;clear out PF2 first, I found out through experience sta PF0 sta PF1 ;just burning scanlines.... ldy #40 ;was 138 TitlePostLoop sta WSYNC dey bne TitlePostLoop ;mirror player 1 who's on the right lda #%00001000 sta REFP1 ldy #8 TitlePlayerLoop sta WSYNC lda WingUpGraphic-1,Y sta GRP0 lda booleanGameIsTwoPlayers beq TitleDrawPlayer1ForTwoPlayers lda booleanFujiFrame beq FujiWasZeroFrame FujiWasNotZeroFrame lda FujiGraphic0-1,Y jmp TitleDoneChooseDrawPlayer1 FujiWasZeroFrame lda FujiGraphic1-1,Y jmp TitleDoneChooseDrawPlayer1 TitleDrawPlayer1ForTwoPlayers lda WingUpGraphic-1,Y TitleDoneChooseDrawPlayer1 sta GRP1 dey sta WSYNC bne TitlePlayerLoop lda #0 sta GRP0 sta GRP1 ;just burning scanlines....you could do something else ldy #44 TitlePostPostLoop sta WSYNC dey bne TitlePostPostLoop ; usual vblank lda #2 sta VBLANK ldx #30 TitleOverScanWait sta WSYNC dex bne TitleOverScanWait jmp TitleMainLoop ;-------------------------------------- ;-------------------------------------- ; MAIN GAME ;-------------------------------------- ;-------------------------------------- MainGameStart lda #%00010001 sta CTRLPF lda #44 sta slowP0YCoordFromBottom + 1 ;44 in integer part of players position sta slowP1YCoordFromBottom + 1 ;('bout half way up) lda #0 sta slowP0YCoordFromBottom ;0 in fractional part of players position sta slowP1YCoordFromBottom ;0 in all player's speed, integer and fractional sta slowP0YSpeed + 1 sta slowP0YSpeed sta slowP1YSpeed + 1 sta slowP1YSpeed ;zero out scores and game being over sta p0score sta p1score sta booleanGameOver lda #>GraphicsPage ;grab the high byte of the graphic location sta pointerP0Graphic+1 ;2 byte memory lookup sta pointerP1Graphic+1 ;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 #99 sta COLUPF ;color here lda #BALLPOS_CENTER sta ballXposition+1 lda #1 sta ballVertSpeed ;!!!position ball lda #41 sta ballPosFromBot ;make missiles double wide lda #%00110000 sta NUSIZ0 ;seed the sound buffers lda #TYPE_OF_FLAPSOUND sta AUDC0 ;type of sound lda #PITCH_OF_FLAPSOUND sta AUDF0 ;pitch lda #4 sta AUDC1 ;type of sound ;-------------------------------------- ;-------------------------------------- ;START MAIN LOOP W/ VSYNC ;-------------------------------------- ;-------------------------------------- MainLoop lda SWCHB and #%00000001 ;is game reset? bne ResetWasNotHit ;if so jump to MainGame jmp MainGameStart ResetWasNotHit lda SWCHB and #%00000010 ;is game select hit? bne SelectWasNotHit ;if so jump to the title screen ;hopefully these are the only initialzations we have to perform? ;might need to change logic if not... lda #0 sta CTRLPF ;playfield ain't reflected LDA #12 STA AUDC0 LDA #8;8 STA AUDC1 lda #1 sta booleanOverrideSelectChangeThisTime jmp TitleSelectIsDownNow SelectWasNotHit lda #2 sta VSYNC sta WSYNC sta WSYNC sta WSYNC lda #43 sta TIM64T lda #0 sta VSYNC sta AUDV1 ;volume for dinger ; ; for now assume wings are up ; ;-------------------------------------- ;SEE IF BUTTON 0 IS NEWLY PRESSED ;-------------------------------------- CheckButton0 lda INPT4 bmi NoButton0 ;buttons down, graphic is down... lda #ScoreBlankGraphic ;grab the hight byte of the graphic location ; sta pointerP0Score+1 jmp CheckIfWeDrawScoreP1 OkToDrawScoreP0 lda p0score ;accumulator = score asl ;accumulator = score * 2 asl ;accumulator = score * 4 adc p0score ;accumulator = (score * 4) + score = score * 5 adc #Score0Graphic ;grab the hight byte of the graphic location ; sta pointerP0Score+1 CheckIfWeDrawScoreP1 lda booleanDisplayPterry beq OkToDrawScoreP1 lda booleanSwitchP0AndPterry bne OkToDrawScoreP1 lda #ScoreBlankGraphic ;grab the hight byte of the graphic location ; sta pointerP1Score+1 jmp DoneSeeingIfWeDrawScores OkToDrawScoreP1 lda p1score ;accumulator = score asl ;accumulator = score * 2 asl ;accumulator = score * 4 adc p1score ;accumulator = (score * 4) + score = score * 5 adc #Score0Graphic ;grab the hight byte of the graphic location ; sta pointerP1Score+1 DoneSeeingIfWeDrawScores ;-------------------------------------- ;DIMINISH FLAP SOUND ;-------------------------------------- lda flapsoundRemaining bmi NoFlapSound sta AUDV0 ;volume dec flapsoundRemaining NoFlapSound ldx #22;;WALL;; lda playfieldMatrixLeft,X;;WALL;; sta bufferPFLeft;;WALL;; lda playfieldMatrixRight,X;;WALL;; sta bufferPFRight;;WALL;; lda slowP0YCoordFromBottom+1 sta copyIntegerCoordP0 lda slowP1YCoordFromBottom+1 sta copyIntegerCoordP1 ;------------------------------------ ;PTERRY PTIME!!! ;------------------------------------ lda booleanPterryGoesRight beq PterryMustBeGoingLeft PterryMustBeGoingRight inc pterryHorizPosition lda #PTERRY_RIGHT_BOUNDARY cmp pterryHorizPosition bcs DoneNotReboundOffRightBoundary lda #0 sta booleanPterryGoesRight DoneNotReboundOffRightBoundary lda #%00000000 sta cachePterryReflection jmp DoneMovingPterry PterryMustBeGoingLeft dec pterryHorizPosition lda #PTERRY_LEFT_BOUNDARY clc cmp pterryHorizPosition bcc DoneNotReboundOffLeftBoundary lda #1 sta booleanPterryGoesRight DoneNotReboundOffLeftBoundary lda #%00001000 sta cachePterryReflection DoneMovingPterry ;make Pterry's wing flap.... lda counterPterryWingChange bne NotTimeToChangePterryWing lda #PTERRY_LENGTH_OF_WINGCHANGE sta counterPterryWingChange lda booleanPterryWingIsUp eor #%11111111 sta booleanPterryWingIsUp NotTimeToChangePterryWing dec counterPterryWingChange ;assume lda #kernal != >endkernal) echo "WARNING: Kernel crosses a page boundary!" endif