8bitworkshop/presets/examples/multisprite1.a

274 lines
6.2 KiB
Plaintext

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
lda #>Frame0
sta PData0+1
lda #<ColorFrame0
sta PColr0
lda #>ColorFrame0
sta PColr0+1
lda #<Frame1
sta PData1
lda #>Frame1
sta PData1+1
lda #<ColorFrame0
sta PColr1
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