processor 6502 include "vcs.h" include "macro.h" org $f000 ; Besides the two 8x1 sprites ("players") the TIA has ; two "missiles" and one "ball", which are just variable-length ; dots or dashes. They have similar positioning and display ; requirements, so we're going to make a subroutine that can ; set the horizontal position of any of them. ; But we can also use the HMPx/HMOVE registers directly to move the ; objects by small offsets without using this routine every time. counter equ $81 ; Initialize and set initial offsets of objects. start CLEAN_START lda #10 ldx #0 jsr SetHorizPos ; set player 0 horiz. pos inx lda #130 jsr SetHorizPos ; set player 1 horiz. pos inx lda #40 jsr SetHorizPos ; set missile 0 horiz. pos lda #$10 sta NUSIZ0 ; make missile 0 2x-wide inx lda #70 jsr SetHorizPos ; set missile 1 horiz. pos lda #$20 sta NUSIZ1 ; make missile 1 4x-wide inx lda #100 jsr SetHorizPos ; set ball horiz. pos lda #$30 sta CTRLPF ; set ball 8x-wide sta WSYNC sta HMOVE ; We've technically generated an invalid frame because ; these operations have generated superfluous WSYNCs. ; But it's just at the beginning of our program, so whatever. ; Next frame loop nextframe VERTICAL_SYNC ; 37 lines of VBLANK ldx #37 lvblank sta WSYNC dex bne lvblank ; Draw 192 scanlines ; We're going to draw both players, both missiles, and the ball ; straight down the screen. We can draw various kinds of vertical ; lines this way. ldx #192 stx COLUBK ; set the background color lda #0 ; A changes every scanline ldy #0 ; Y is sprite data index lvscan sta WSYNC ; wait for next scanline lda NUMBERS,y sta GRP0 ; set sprite 0 pixels sta GRP1 ; set sprite 1 pixels tya ; we'll use the Y position, only the 2nd bit matters sta ENAM0 ; enable/disable missile 0 sta ENAM1 ; enable/disable missile 1 sta ENABL ; enable/disable ball iny cpy #60 bne wrap1 ; wrap Y at 60 to 0 ldy #0 wrap1 dex bne lvscan ; repeat next scanline until finished ; Clear all colors to black before overscan stx COLUBK stx COLUP0 stx COLUP1 stx COLUPF ; 30 lines of overscan ldx #25 lvover sta WSYNC dex bne lvover ; Move all the objects by a different offset using HMP/HMOVE registers ; We'll hard-code the offsets in a table for now ldx #0 hmoveloop lda MOVEMENT,x sta HMCLR sta HMP0,x sta WSYNC sta HMOVE inx cpx #5 bcc hmoveloop ; This loop also gave us 5 extra scanlines = 30 total ; Cycle the sprite colors for the next frame inc counter lda counter sta COLUP0 clc ror sta COLUP1 clc ror sta COLUPF jmp nextframe ; SetHorizPos - Sets the horizontal position of an object. ; The X register contains the index of the desired object: ; X=0: player 0 ; X=1: player 1 ; X=2: missile 0 ; X=3: missile 1 ; X=4: ball ; This routine does a WSYNC and HMOVE before executing, ; so whatever you do here will not take effect until you ; call the routine again or do your own WSYNC and HMOVE. SetHorizPos sta WSYNC ; start a new line sta HMOVE ; apply the previous fine position(s) sta HMCLR ; reset the old horizontal position(s) sec ; set carry flag DivideLoop sbc #15 ; subtract 15 bcs DivideLoop ; branch until negative eor #7 ; calculate fine offset asl asl asl asl sta RESP0,x ; fix coarse position sta HMP0,x ; set fine offset rts ; return to caller ; Hard-coded values for movement registers MOVEMENT .byte $f0 ; +1 pixels .byte $e0 ; +2 pixels .byte $c0 ; +4 pixels .byte $10 ; -1 pixels .byte $20 ; -2 pixels ; Bitmap pattern for digits NUMBERS .byte $0E ; | XXX | $F5C5 Leading zero is not drawn .byte $0A ; | X X | $F5C6 because it's never used. .byte $0A ; | X X | $F5C7 .byte $0A ; | X X | $F5C8 .byte $0E ; | XXX | $F5C9 .byte $00 .byte $22 ; | X X | $F5CA .byte $22 ; | X X | $F5CB .byte $22 ; | X X | $F5CC .byte $22 ; | X X | $F5CD .byte $22 ; | X X | $F5CE .byte $00 .byte $EE ; |XXX XXX | $F5CF .byte $22 ; | X X | $F5D0 .byte $EE ; |XXX XXX | $F5D1 .byte $88 ; |X X | $F5D2 .byte $EE ; |XXX XXX | $F5D3 .byte $00 .byte $EE ; |XXX XXX | $F5D4 .byte $22 ; | X X | $F5D5 .byte $66 ; | XX XX | $F5D6 .byte $22 ; | X X | $F5D7 .byte $EE ; |XXX XXX | $F5D8 .byte $00 .byte $AA ; |X X X X | $F5D9 .byte $AA ; |X X X X | $F5DA .byte $EE ; |XXX XXX | $F5DB .byte $22 ; | X X | $F5DC .byte $22 ; | X X | $F5DD .byte $00 .byte $EE ; |XXX XXX | $F5DE .byte $88 ; |X X | $F5DF .byte $EE ; |XXX XXX | $F5E0 .byte $22 ; | X X | $F5E1 .byte $EE ; |XXX XXX | $F5E2 .byte $00 .byte $EE ; |XXX XXX | $F5E3 .byte $88 ; |X X | $F5E4 .byte $EE ; |XXX XXX | $F5E5 .byte $AA ; |X X X X | $F5E6 .byte $EE ; |XXX XXX | $F5E7 .byte $00 .byte $EE ; |XXX XXX | $F5E8 .byte $22 ; | X X | $F5E9 .byte $22 ; | X X | $F5EA .byte $22 ; | X X | $F5EB .byte $22 ; | X X | $F5EC .byte $00 .byte $EE ; |XXX XXX | $F5ED .byte $AA ; |X X X X | $F5EE .byte $EE ; |XXX XXX | $F5EF .byte $AA ; |X X X X | $F5F0 .byte $EE ; |XXX XXX | $F5F1 .byte $00 .byte $EE ; |XXX XXX | $F5F2 .byte $AA ; |X X X X | $F5F3 .byte $EE ; |XXX XXX | $F5F4 .byte $22 ; | X X | $F5F5 .byte $EE ; |XXX XXX | $F5F6 .byte $00 ; Epilogue org $fffc .word start .word start