step 11 - add the ball object
The 2LK has been revised to display the ball object. Like player0, the ball needs to be primed before the ArenaLoop begins:
; prime ENABL so ball can appear on topmost scanline of Arena ldx #1 ; 2 2 - D1=0, so ball will be off lda #BOX_HEIGHT-1 ; 2 4 - height of box graphic dcp BallDraw ; 5 9 - Decrement BallDraw and compare with height bcs DoEnablPre ; 2 11 - (3 12) if Carry is Set, then ball is on current scanline .byte $24 ; 3 14 - $24 = BIT with zero page addressing, trick that ; causes the inx to be skipped DoEnablPre: ; 12 - from bcs DoEnablPre inx ; 2 14 - D1=1, so ball will be ON stx ENABL ; 3 17 ; prime GRP0 so player0 can appear on topmost scanline of the Arena lda #HUMAN_HEIGHT-1 ; 2 19 - height of player0 graphics, dcp Player0Draw ; 5 24 - Decrement Player0Draw and compare with height bcs DoDrawGrp0pre ; 2 26 - (3 27) if Carry is Set, then player0 is on current scanline lda #0 ; 2 28 - otherwise use 0 to turn off player0 .byte $2C ; 4 32 - $2C = BIT with absolute addressing, trick that ; causes the lda (Player0Ptr),y to be skipped DoDrawGrp0pre: ; 27 - from bcs DoDrawGRP0pre lda (Player0Ptr),y ; 5 32 - load the shape for player0 sta GRP0 ; 3 35 - @0-22, update player0 graphics dey ; 2 37 ArenaLoop: ; 37 - (currently 11 from bpl ArenaLoop) tya ; 2 39 - 2LK loop counter in A for testing and #%11 ; 2 41 - test for every 4th time through the loop, bne SkipX ; 2 43 - (3 44) branch if not 4th time inc ArenaIndex ; 5 48 - if 4th time, increase index so new playfield data is used SkipX: ; 48 - use 48 as it's the longest path here ; continuation of line 2 of the 2LK ; this precalculates data that's used on line 1 of the 2LK lda #HUMAN_HEIGHT-1 ; 2 50 - height of the humanoid graphics, subtract 1 due to starting with 0 dcp Player1Draw ; 5 55 - Decrement Player1Draw and compare with height bcs DoDrawGrp1 ; 2 57 - (3 58) if Carry is Set, then player1 is on current scanline lda #0 ; 2 59 - otherwise use 0 to turn off player1 .byte $2C ; 4 63 - $2C = BIT with absolute addressing, trick that ; causes the lda (Player1Ptr),y to be skipped DoDrawGrp1: ; 58 - from bcs DoDrawGrp1 lda (Player1Ptr),y ; 5 63 - load the shape for player1 sta WSYNC ; 3 66 ;--------------------------------------- ; start of line 1 of the 2LK sta GRP1 ; 3 3 - @0-22, update player1 graphics ldx ArenaIndex ; 3 6 lda ArenaPF0,x ; 4 10 - get current scanline's playfield pattern sta PF0 ; 3 13 - @0-22 and update it lda ArenaPF1,x ; 4 17 - get current scanline's playfield pattern sta PF1 ; 3 20 - @71-28 and update it lda ArenaPF2,x ; 4 24 - get current scanline's playfield pattern sta PF2 ; 3 27 - @60-39 ; precalculate data that's needed for line 2 of the 2LK ldx #1 ; 2 29 - D1=0, so ball will be off lda #BOX_HEIGHT-1 ; 2 31 - height of box graphic dcp BallDraw ; 5 36 - Decrement BallDraw and compare with height bcs DoEnabl ; 2 38 - (3 39) if Carry is Set, then ball is on current scanline .byte $24 ; 3 41 - $24 = BIT with zero page addressing, trick that ; causes the inx to be skipped DoEnabl: ; 39 - from bcs DoEnablPre inx ; 2 41 - D1=1, so ball will be ON lda #HUMAN_HEIGHT-1 ; 2 43 - height of the box graphics, dcp Player0Draw ; 5 48 - Decrement Player0Draw and compare with height bcs DoDrawGrp0 ; 2 50 - (3 51) if Carry is Set then player0 is on current scanline lda #0 ; 2 52 - otherwise use 0 to turn off player0 .byte $2C ; 4 56 - $2C = BIT with absolute addressing, trick that ; causes the lda (Player0Ptr),y to be skipped DoDrawGrp0: ; 51 - from bcs DoDrawGRP0 lda (Player0Ptr),y ; 5 56 - load the shape for player0 sta WSYNC ; 3 59 ;--------------------------------------- ; start of line 2 of the 2LK sta GRP0 ; 3 3 - @0-22, update player0 graphics stx ENABL ; 3 6 - @0-22, update ball graphics dey ; 2 8 - decrease the 2LK loop counter bne ArenaLoop ; 2 10 - (3 11) branch if there's more Arena to draw sty PF0 ; 3 13 - Y is 0, blank out playfield sty PF1 ; 3 16 - Y is 0, blank out playfield sty PF2 ; 3 19 - Y is 0, blank out playfield rts ; 6 25 - ReTurn from Subroutine
I then modified RandomLocation to set all objects to the same location for comparision:
RandomLocation: ... ; for alignment test, set to (100, 100) lda #100 sta ObjectX,x sta ObjectY,x rts
This revealed a minor quirk with TIA - namely that when objects are set to the same X position, missiles and the ball end up 1 pixel to the left of where a player ends up (player1 is the green square and it's directly on top of the red ball).
This is a known issue and the solution is to increase the X value by 1 to compensate. I did so by adding this to the end of RandomLocation:
RandomLocation: ... cpx #2 bcc RLdone inc ObjectX,x ; missile and ball objects need their X adjusted RLdone: rts
And now player1 and the ball line up:
The NewGame routine revised last time already sets the ball to a random X-Y location, so all that's left to make it show up is to revise PositionObjects. Two changes are needed, first is to set the X position of the ball object by starting the POloop with X=4 (in the prior build X was initialized to 1), then prep a new variable BallDraw that's used by the 2LK to draw the ball object on the correct scanlines.
PositionObjects: ldx #4 ; position all objects POloop lda ObjectX,x ; get the object's X position jsr PosObject ; set coarse X position and fine-tune amount dex ; DEcrement X bpl POloop ; Branch PLus so we position all objects sta WSYNC ; wait for end of scanline sta HMOVE ; use fine-tune values to set final X positions ... ; prep ball's Y position for 2LK ldx #1 ; preload X for setting VDELBL lda ObjectY+4 ; get the balls's Y position clc adc #1 ; add 1 to compensate for priming of ball lsr ; divide by 2 for the 2LK position sta Temp ; save for position calculations bcs NoDelayBL ; if carry is set we don't need Vertical Delay stx VDELBL ; carry was clear, so set Vertical Delay NoDelayBL: ; BallDraw = ARENA_HEIGHT + BOX_HEIGHT - Y position + 1 ; the + 1 compensates for priming of ENABL lda #(ARENA_HEIGHT + BOX_HEIGHT + 1) sec sbc Temp sta BallDraw rts
Lastly, Overscan's been updated to process collisions with the ball.
OverScan: ... TestCollisions: ; Test left player collisions ... bit CXP0FB ; N = player0/playfield, V=player0/ball ... notP0PF: ; oVerflow flag is not affected by lda or sta bvc notP0BL ; if V is off, then player0 did not collide with ball ldx #4 ; which box was collected jsr CollectBox ; update score and reposition box notP0BL: ... RightPlayer: ; Test right player collisions ... bit CXP1FB ; N = player1/playfield, V=player1/ball ... notP1PF: ; oVerflow flag is not affected by lda or sta bvc notP1BL ; if V is off, then player1 did not collide with ball ldx #4 ; which box was collected jsr CollectBox ; update score and reposition box notP1BL:
2 player games are now playable:
and 1 player games have 2 boxes to collect:
ROM
Source
3 Comments
Recommended Comments