processor 6502 include "vcs.h" include "macro.h" include "xmacro.h" ; Given all the heavy lifting it requires to put a single ; sprite on the screen, we'd like to abstract away the process ; of putting multiple sprites on the screen so we can actually ; make a game. This is called the "kernel" because it is ; particularly timing-sensitive and the CPU will spend a lot ; of time in that loop. ; We'll start with drawing two overlapping sprites with full ; resolution (1 scanline per sprite line) and full color ; (1 color per scanline). To do this our kernel (DrawSprites) ; will have to update four TIA registers after each WSYNC, ; pulling from four lookup tables, and handling edge cases ; where sprites overlap partially in the Y-dimension. ; This will use up almost an entire scanline of CPU time. ; Right now we'll just hard-code the horizontal position ; of each sprite, limiting ourselves to two sprites. seg.u Variables org $80 Counter byte ; increments each frame Scanline byte ; scanline to draw next PData0 word ; pointer (lo/hi) to player 0 bitmap data PData1 word ; pointer to player 1 bitmap data PColr0 word ; pointer to player 0 color data PColr1 word ; pointer to player 1 color data SIndx0 byte ; index into player data (0 = inactive) SIndx1 byte ; index into player data (0 = inactive) SSize0 byte ; sprite size for each player SSize1 byte ; sprite size for each player ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; seg Code org $f000 ; Initialize and set initial offsets of objects. Start CLEAN_START ; Hard-code the horizontal player positions. lda #70 ldx #0 jsr SetHorizPos ; set player 0 horiz. pos lda #73 ldx #1 jsr SetHorizPos ; set player 1 horiz. pos sta WSYNC sta HMOVE ; Next frame loop NextFrame TIMER_SETUP 37 VERTICAL_SYNC ; Set some colors lda #$80 sta COLUBK ; set the background color ; Setup the two sprites lda #50 sta SIndx0 lda Counter ; changes every frame sta SIndx1 lda #17 sta SSize0 sta SSize1 lda #Frame0 sta PData0+1 lda #ColorFrame0 sta PColr0+1 lda #Frame1 sta PData1+1 lda #ColorFrame0 sta PColr1+1 TIMER_WAIT ; Scanline loop lda #0 sta Scanline NextScanline jsr DrawSprites inc Scanline ; WSYNC and then clear sprite data sta WSYNC lda #0 stx GRP0 stx GRP1 ; repeat until all scanlines drawn lda Scanline cmp #192 bcc NextScanline ; Clear all colors to black before overscan stx COLUBK stx COLUP0 stx COLUP1 stx COLUPF ; 30 lines of overscan TIMER_SETUP 30 TIMER_WAIT ; Jump to next frame inc Counter jmp NextFrame ; Here is our sprite kernel. ; Inputs: ; ssize0, ssize1 (size in lines of each sprite) ; sindx0, sindx1 (# scanlines to bottom of sprites) ; pdata0, pdata1 (ptr to sprite bitmaps) ; pcolr0, pcolr0 (ptr to sprite color tables) DrawSprites ; Find out the maximum # of lines to draw ; by taking the maximum of the two sprite heights lda SIndx0 cmp SIndx1 bpl Cmp1 ; sindx0 < sindx1 lda SIndx1 Cmp1 tax ; X = # of lines left to draw cmp #0 ; no lines? beq NoSprites ; add total draw height to scanline count clc adc Scanline sta Scanline DrawNextScanline ; Fetch next pixels/colors for sprite 0 ldy SIndx0 cpy SSize0 bcs Inactive0 ; index >= size? (unsigned) lda (PData0),y ; Do WSYNC and then quickly store pixels/colors for player 0 sta WSYNC sta GRP0 lda (PColr0),y sta COLUP0 DrawSprite1 ; Fetch next pixels/colors for sprite 1 ldy SIndx0+1 cpy SSize0+1 bcs Inactive1 ; index >= size? (unsigned) lda (PData1),y sta GRP1 lda (PColr1),y sta COLUP1 Inactive1 ; Decrement the two sprite indices dey sty SIndx0+1 dec SIndx0 dex bne DrawNextScanline NoSprites ; Clear player data on exit rts Inactive0 ; Alternate sprite 0 path when inactive lda #0 sta WSYNC sta GRP0 sta COLUP0 beq DrawSprite1 ; always taken due to lda #0 ; 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 ; To avoid nasty timing issues, ; we'll start the bitmap data at a page boundary org $f800 ; 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; .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