processor 6502 include "vcs.h" include "macro.h" org $f000 ; Now, we'll finally put everything together and ; make a little person with a hat that can move ; back and forth and throw rocks. We'll use one player ; to generate a 8x16 sprite and one missile. ; ; We have two tables for the sprite, a bitmap table and ; a color table. We'll lookup from both of these tables on ; each scanline. ; ; Because VCS programming is all about thrift, we'll ; reuse the machine code for our program as the backdrop ; for the playfield, just to show we can fit it all on ; a scanline. ; ; Note: the Y coordinate goes bottom-top, not top-bottom ; as in the next section. counter equ $81 ; increments each frame yplyr equ $82 ; player y pos yrock equ $83 ; rock y pos animofs equ $84 ; sprite data offset, used for animation ; Initialize and set initial offsets of objects. start CLEAN_START lda #5 sta yplyr lda #70 ldx #0 jsr SetHorizPos ; set player 0 horiz. pos sta WSYNC sta HMOVE ; Next frame loop nextframe VERTICAL_SYNC ; in case the rock is on screen lda ColorFrame0 ; load 1st entry of color data sta COLUP0 ; set sprite 0 color ; 37 lines of VBLANK ldx #37 lvblank sta WSYNC dex bne lvblank ; set some colors lda #$80 sta COLUBK ; set the background color lda #1 sta CTRLPF ; playfield reflection ; Draw 192 scanlines with our sprite/missile kernel ldx #192 lvscan ; Draw sprite? txa sec ; make sure carry is set (meaning no borrow for subtraction) sbc yplyr ; get offset into sprite for this line cmp #SpriteHeight ; sprite is 16 pixels high + padding on either side bcc insprite lda #SpriteHeight-1 ; draw the padding insprite tay lda ColorFrame0,y ; load color data pha ; push color data onto stack clc ; clear carry flag adc animofs ; add animation offset (not for color though) lda Frame0,y ; load bitmap data sta WSYNC ; wait for next scanline (as late as possible!) sta GRP0 ; set sprite 0 pixels pla ; pull bitmap data from stack sta COLUP0 ; set sprite 0 color lda start,x ; store some random garbage into the playfield sta PF0 sta PF1 sta PF2 ; Draw rock? Player 0 and missile 0 share a color register, ; so they will have the same colors on the same scanline lda #%00000000 cpx yrock bne norock lda #%00000010 ; for ENAM0 the 2nd bit is enable norock sta ENAM0 ; enable missile 0 dex bne lvscan ; repeat next scanline until finished ; End of scanline loop ; Clear all colors to black before overscan stx COLUBK stx COLUP0 stx COLUP1 stx COLUPF ; 30 lines of overscan ldx #29 lvover sta WSYNC dex bne lvover ; Joystick movement ; For up and down, we INC or DEC the Y Position lda #%00010000 ;Up? bit SWCHA bne SkipMoveUp inc yplyr SkipMoveUp lda #%00100000 ;Down? bit SWCHA bne SkipMoveDown dec yplyr SkipMoveDown ; Note that the horizontal position is not contained in RAM, ; but inaccessibly inside the TIA's registers! Some games can ; get away with this if they use the collision registers. ldx #0 ; assume speed is 0 if no movement lda #%01000000 ;Left? bit SWCHA bne SkipMoveLeft ldx #$10 ;a 1 in the left nibble means go left SkipMoveLeft lda #%10000000 ;Right? bit SWCHA bne SkipMoveRight ldx #$F0 ;a -1 in the left nibble means go right... SkipMoveRight stx HMP0 ;set the move for plyr 0 sta WSYNC sta HMOVE ; Throw a rock when fire button pressed lda #0 sta animofs ldy #%00000000 ; use later to enable/disable missile lock on player lda INPT4 ;read button input bmi ButtonNotPressed ;skip if button not pressed lda yplyr sta yrock ; set rock vert pos to player's vert lda #SpriteHeight sta animofs ; while button pressed, use alternate player bitmap ldy #%00000010 ; for RESMP0 the 2nd bit is the enable bit ButtonNotPressed sty RESMP0 ; reset missile 0 to player 0's horiz position inc yrock ; rock moves no matter what (actually a boomerang) ; Goto next frame 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 ; Height of our sprite in lines SpriteHeight equ 18 ; Bitmap data "standing" position Frame0 .byte #0 .byte #%01101100;$F6 .byte #%00101000;$86 .byte #%00101000;$86 .byte #%00111000;$86 .byte #%10111010;$C2 .byte #%10111010;$C2 .byte #%01111100;$C2 .byte #%00111000;$C2 .byte #%00111000;$16 .byte #%01000100;$16 .byte #%01111100;$16 .byte #%01111100;$18 .byte #%01010100;$18 .byte #%01111100;$18 .byte #%11111110;$F2 .byte #%00111000;$F4 ; Bitmap data "throwing" position Frame1 .byte #0 .byte #%01101100;$F6 .byte #%01000100;$86 .byte #%00101000;$86 .byte #%00111000;$86 .byte #%10111010;$C2 .byte #%10111101;$C2 .byte #%01111101;$C2 .byte #%00111001;$C2 .byte #%00111000;$16 .byte #%01101100;$16 .byte #%01111100;$16 .byte #%01111100;$18 .byte #%01010100;$18 .byte #%01111100;$18 .byte #%11111110;$F2 .byte #%00111000;$F4 ; Color data for each line of sprite ColorFrame0 .byte #$FF ; rock color if not sharing line with player sprite .byte #$F6; .byte #$86; .byte #$86; .byte #$86; .byte #$C2; .byte #$C2; .byte #$C2; .byte #$C2; .byte #$16; .byte #$16; .byte #$16; .byte #$18; .byte #$18; .byte #$18; .byte #$F2; .byte #$F4; ; Epilogue org $fffc .word start .word start