3092 lines
70 KiB
NASM
3092 lines
70 KiB
NASM
/*
|
|
this is the sprite/oam routine file.
|
|
|
|
direct sprite features:
|
|
-setup sprite variables
|
|
-create and upload spritelist-buffer to oam-ram
|
|
-upload sprite palettes
|
|
|
|
object manager features:
|
|
-create and process object list
|
|
-create objects, each with own memory space
|
|
-delete objects
|
|
-routines for:
|
|
-cycling through animations
|
|
-moving objects along preset paths
|
|
|
|
areas needed:
|
|
OAMBuffer ds 200
|
|
oam memory format:
|
|
Byte 1 xxxxxxxx x: X coordinate
|
|
Byte 2 yyyyyyyy y: Y coordinate
|
|
Byte 3 cccccccc c: starting character (tile) number p: palette number
|
|
Byte 4 vhoopppc v: vertical flip h: horizontal flip o: priority bits
|
|
Note: the 'c' in byte 4 is the MOST significant bit in the 9-bit char #.
|
|
|
|
00110000
|
|
|
|
OAMPriorityBuffer ds 20
|
|
oam priority format:
|
|
2bits per sprite, 4 sprites per byte
|
|
bit0 = size toggle
|
|
bit1 = x coordinate msb
|
|
|
|
ObjectList ds $400
|
|
|
|
Maximum number of objects: 64
|
|
Object Format: 32bytes per object
|
|
.db %11101010 ;0 object type designation
|
|
;bit0=X position sign of sprite(usually 0)
|
|
;bit1=Object size flag
|
|
;bit2=collidable
|
|
;bit3=subroutine? if set, this object has its own subroutine that must be executed every frame
|
|
;bit4=animate? if set, this object is animated(a special table for each object specifies the exact animation with commands for tileloading, waiting, animation loop etc)
|
|
;bit5=bg-bound? if set, this object must move in accordance with background layer 0
|
|
;bit6=object active? if set, this object is active and needs to be drawn. if clear, this sprite doesnt need to be drawn, but must be processed, anyway
|
|
;bit7=object present? if set, this slot has an object. if clear, its considered empty and can be overwritten
|
|
.db %00000000 ;1 object type designation 2
|
|
;bit7=pseudo 3d sprite that needs to be moved according to z-value when background moves
|
|
;bit6=don't upload tiles for this sprite. useful for stuff like particles and such where lots of sprites share the same tiles.
|
|
.db 6 ;2 number of subroutine. executed if bit6 of object type is set
|
|
.db 14 ;3 tileset to use
|
|
.db 9 ;4 current "frame" of tileset to display
|
|
.db $cc ;5 starting tile in vram
|
|
.db %00111101 ;6 palette and config. vhoopppN; oo=priority. ppp=palette. N=nametable vh=flip
|
|
.dw $0 ;7 x position
|
|
.dw 201 ;9 y position
|
|
.db 0 ;11 current frame in animation list
|
|
.db 0 ;12 object command list to use
|
|
.db 0 ;13 object offset in object list.
|
|
.db 9 ;14 palette number to upload for this sprite
|
|
.dw OamTypeTextbox ;15 object number
|
|
|
|
.db 0 ;17 x-displacement
|
|
.db 0 ;18 y-displacement
|
|
.db 4 ;19 z-displacement
|
|
.db 0 ;20 z-value for pseudo-3d scenes
|
|
.db 0 ;21 collision subroutine
|
|
.db 0 ;22 animation repeat counter for nop, must not be set up
|
|
;23-25 left void for future expansion
|
|
|
|
;custom variables differing between sprites:
|
|
.db 0 ;26 spare variable
|
|
.db $80 ;27 target x-pos /npc walking distance
|
|
.db 32 ;28 target y-pos
|
|
.db 0 ;29 current x-pos, must not be set up
|
|
.db 8 ;30 current y-pos, must not be set up
|
|
.db 0 ;void
|
|
.db 0 ;void
|
|
.db 0 ;void
|
|
.db 0 ;void
|
|
.db 0 ;void
|
|
.db 0 ;void
|
|
.db 0 ;void
|
|
object command list format:
|
|
an entry is always 2 bytes long, mostly consisting of a command byte and a parameter
|
|
normally, one command from the list is executed per frame.
|
|
".." means no parameter.
|
|
if bit7 of command byte is set, execute the next command immediately.
|
|
this is used to execute multiple commands per frame.
|
|
|
|
control code: function:
|
|
-00NN create another object in object list. NN is the number of the object to create. searches for the next free slot in object list
|
|
-01.. deletes object from list(can only delete itself. if an object wants to delete another one, it has to do this via the subroutine)
|
|
-02XY adds signed xy-vector to objects position.
|
|
-03TF increment current tileset frame. TF=number of frames to advance
|
|
-04TF set tileset frame. TF=tileset frame to set
|
|
-05PC set palette and config PC=palette and config ;IMPORTANT!! if this is executed, the tile positions of an object bigger than 8x8 must be swapped!!
|
|
-06AF goto frame in animation list. can be used to create infinite looping objects. AF=frame in animation list
|
|
-07CL goto command list. always starts at position 0 in that command list and discards current command list. CL=command list to go to.
|
|
-08.b0 set/unset object screen-bound (with bit0 of parameter)
|
|
-09.b0 set/unset object subroutine enable (with bit0 of parameter)
|
|
-0aSS set object subroutine. SS=subroutine number
|
|
-0bTS set tileset. TS:number of tileset to use
|
|
-0cSE play soundeffect
|
|
-0dFN nop and wait for next frame(s). used for delays. FN specifies number of frames to wait. 0=wait one frame, 5=wait 6 frames etc
|
|
-0e.. inifite loop. object stays active
|
|
-0f.. terminate. used to end object list processing. marks object as inactive.
|
|
-10VS set target speed vector. bits0-5: speed (0-2subpixel precision) bits 6,7: accel type (0=direct, 1=linear slow, 2=linear fast, 3=smooth). speed 0 disables vector speed calculation
|
|
-11VD set target vector direction/angle. bits0-5: target direction ($00=up $10=right $20=down $30=left) bits 6,7: direction change type (0=direct, 1=linear slow, 2=linear fast, 3=smooth). clockwise/counterclockwise direction is determined by distance between current and target direction(alsways uses fastest/smallest angle)
|
|
-12NN create object and put it to the coordinates of the calling object. NN=number of object to create
|
|
|
|
sprite palettes start at cgram entry 128
|
|
|
|
|
|
oam registers:
|
|
$2101 - global sprite size select
|
|
sprite tile location select: 0,4000,8000,c000
|
|
$2102 - oam adress
|
|
$2103 - oam table and priority rotation select
|
|
$2104 - oam data. this is a doublewrite/read register.
|
|
|
|
free space for sprites:
|
|
$4000 bytes at $c000
|
|
$20 bytes per 8x8 sprite = 512 tiles for sprites
|
|
first sprite tile must always be blank
|
|
|
|
|
|
size: amount possible in terms of vram space:
|
|
8x8 512
|
|
16x16 128
|
|
32x32 32
|
|
|
|
for sprites bigger than 8x8, the vram tile data is seperated into horizontal rows, each 16 tiles (=512 bytes) apart.
|
|
they are always 16 tiles apart, no matter if the sprites are 16,32 or 64 pixels in size
|
|
heres a table what to upload where for different sized sprites:
|
|
|
|
xsize: ysize: number of lines to upload: number of bytes to transfer per line:
|
|
8 8 1 32
|
|
16 16 2 64
|
|
32 32 4 128
|
|
64 64 8 256
|
|
16 32 4 64
|
|
32 64 8 128
|
|
|
|
|
|
|
|
|
|
initial setting for register $2101 gets loaded with
|
|
graphics config file loader "SetBGMode" and put into variable "ObjSel", updated every vblank
|
|
|
|
variables:
|
|
ObjectListPointer dw ;pointer to current object in object list, used to set direct register
|
|
CurrentObjectNumber db ;number of currently selected object in list
|
|
OamBufferPointer dw ;pointer to current sprite in oam buffer
|
|
|
|
fixed variables:
|
|
.define ObjectFileSize 16 ;size of one object file
|
|
|
|
routines in detail:
|
|
InitOam:
|
|
-clears oam table buffer
|
|
-clears object memory
|
|
|
|
CreateObject:
|
|
-upload object file to first free slot in object list
|
|
-upload current frame of tileset to vram
|
|
-calculate transfer size by object size
|
|
-calculate transfer source by object size*frame number
|
|
-calculate transfer target by starting tile in vram
|
|
-upload palette to vram according to palette number config
|
|
-set object offset in object list variable
|
|
ObjectProcessor:
|
|
-check object list for active object
|
|
-write sprite(s) to oambuffer according to object file
|
|
-process animation list if enabled
|
|
-execute subroutine if enabled
|
|
|
|
*/
|
|
|
|
ObjectProcessSubroutine:
|
|
rep #$31
|
|
phx
|
|
ldx.b ObjectListPointerCurrent
|
|
lda.w ObjEntrySubRout,x
|
|
and.w #$ff ;max number of routines: 256
|
|
asl a
|
|
tax
|
|
|
|
php
|
|
phd
|
|
jsr (AniSubroutineJumpLUT,x)
|
|
pld
|
|
plp
|
|
plx
|
|
rts
|
|
|
|
|
|
|
|
|
|
|
|
ObjectProcessAniList:
|
|
php
|
|
ObjectProcessAniListYes:
|
|
rep #$31
|
|
;load pointer to current animation list:
|
|
lda.w ObjEntryAniList,x ;get number of list to use
|
|
and.w #$ff
|
|
asl a
|
|
phx
|
|
tax
|
|
; sta.b TempBuffer
|
|
lda.l (ObjectAnimationLUT+BaseAdress),x ;get relative pointer to object
|
|
plx
|
|
|
|
clc
|
|
adc.w #ObjectAnimationLUT ;calculate real pointer
|
|
sta.b ThreeBytePointerLo
|
|
|
|
sep #$20
|
|
lda.b #(:ObjectAnimationLUT+BaseAdress>>16)
|
|
sta.b ThreeBytePointerBank
|
|
|
|
;get pointer to currently active frame:
|
|
|
|
rep #$31
|
|
lda.w ObjEntryAniFrame,x
|
|
and.w #$ff
|
|
asl a
|
|
tay
|
|
|
|
;get command
|
|
phx
|
|
lda.b [ThreeBytePointerLo],y ;get current command
|
|
sta.b OamAniListStraightRepeatFlag ;store for immediate repeat checking
|
|
pha
|
|
and.w #$1f ;mask off repeat flag, maximum number of commands: 32
|
|
asl a
|
|
tax
|
|
|
|
pla
|
|
phy
|
|
php
|
|
phd
|
|
|
|
/*
|
|
rep #$31
|
|
pha
|
|
lda.b ObjectListPointerCurrent
|
|
clc
|
|
adc.w #ObjectList & $ffff
|
|
tcd
|
|
|
|
pla
|
|
*/
|
|
|
|
sep #$20
|
|
lda.b #0 ;clear high byte
|
|
xba ;and put parameter into a
|
|
;parameter is in a,8bit
|
|
;direct register points to current object
|
|
;bank is $7e
|
|
jsr (AniListJumpLUT,x)
|
|
|
|
|
|
pld
|
|
plp
|
|
ply
|
|
plx
|
|
sep #$20
|
|
lda.b OamAniListStraightRepeatFlag
|
|
bmi ObjectProcessAniListYes ;if bit7 is set, next command is executed immediately
|
|
|
|
plp
|
|
rts
|
|
|
|
AniListJumpLUT:
|
|
.dw AniListCreateObj ;0
|
|
.dw AniListDelete
|
|
.dw AniListMove
|
|
.dw AniListTileFrameInc
|
|
.dw AniListTileFrameSet
|
|
.dw AniListPalConfSet
|
|
.dw AniListGotoAniFrame
|
|
.dw AniListGotoAniList
|
|
.dw AniListMakeBGBound
|
|
.dw AniListSubroutineEnable
|
|
.dw AniListSetSubroutine ;10
|
|
.dw AniListTileSetSet
|
|
.dw AniListPlaySoundEffect
|
|
.dw AniListVoid
|
|
.dw AniListInfiniteLoop
|
|
.dw AniListEnd ;15
|
|
.dw AniListSetVectorSpeed
|
|
.dw AniListSetVectorDir
|
|
.dw AniListCreateObjPosition
|
|
|
|
;does nothing, doesn't proceed to next animation command
|
|
AniListInfiniteLoop:
|
|
rts
|
|
|
|
AniListVoid:
|
|
ldx.b ObjectListPointerCurrent
|
|
|
|
|
|
sta.w TempBuffer
|
|
lda.w TempBuffer
|
|
beq AniListVoidProceed ;if argument is 0, proceed to next frame directly
|
|
|
|
|
|
lda.w ObjEntryAniCmdRepeat,x ;if argument is not zero, but command list repeat is, we just encountered this void command. load new wait value.
|
|
bne AniListVoidDecrease ;if argument is not zero, we're on a repeated loop here. don't load new value, just decrease old one.
|
|
|
|
lda.w TempBuffer
|
|
sta.w ObjEntryAniCmdRepeat,x
|
|
rts
|
|
|
|
;argument was already loaded, decrease
|
|
AniListVoidDecrease:
|
|
dec a
|
|
sta.w ObjEntryAniCmdRepeat,x
|
|
beq AniListVoidProceed ;if repeat value just turned 0, we're done here, go to next command
|
|
|
|
rts
|
|
|
|
AniListVoidProceed:
|
|
; ldx.b ObjectListPointerCurrent
|
|
; stz.w ObjEntryAniCmdRepeat,x ;not needed, but do it anyway just to be safe
|
|
inc.w ObjEntryAniFrame,x
|
|
rts
|
|
|
|
AniListCreateObj:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x
|
|
jsr CreateObject
|
|
rts
|
|
|
|
AniListCreateObjPosition:
|
|
rep #$31
|
|
tay ;save number of object to load
|
|
ldx.b ObjectListPointerCurrent ;get position of calling object
|
|
lda.w ObjEntryXPos,x ;save to stack
|
|
adc.w #8*16 ;move one tile to the left
|
|
pha
|
|
lda.w ObjEntryYPos,x
|
|
clc
|
|
adc.w #8*16 ;move one tile to the bottom
|
|
pha
|
|
inc.w ObjEntryAniFrame,x
|
|
tya
|
|
jsr CreateObject
|
|
ldx.b ObjectListPointerCurrent ;get position of newly-created object
|
|
pla
|
|
sta.w ObjEntryYPos,x ;update position
|
|
pla
|
|
sta.w ObjEntryXPos,x
|
|
|
|
rts
|
|
|
|
|
|
AniListGotoAniFrame:
|
|
ldx.b ObjectListPointerCurrent
|
|
sta.w ObjEntryAniFrame,x ;store new animation frame
|
|
rts
|
|
|
|
AniListDelete:
|
|
ldx.b ObjectListPointerCurrent
|
|
sta.w ObjEntryType,x ;clear object type info and present flag
|
|
rts
|
|
|
|
AniListMove:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x
|
|
pha
|
|
and.b #$f0 ;get x-add
|
|
clc
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
bit.b #$08 ;check if negative
|
|
beq AniListMoveXNotNeg
|
|
|
|
and.b #$07
|
|
eor.b #$ff ;xor to substract value
|
|
inc a
|
|
AniListMoveXNotNeg:
|
|
rep #$31
|
|
asl a ;subpixel precision
|
|
asl a
|
|
asl a
|
|
asl a
|
|
adc.w ObjEntryXPos,x
|
|
sta.w ObjEntryXPos,x
|
|
sep #$20
|
|
pla
|
|
and.b #$0f ;get x-add
|
|
bit.b #$08 ;check if negative
|
|
beq AniListMoveYNotNeg
|
|
|
|
and.b #$07
|
|
eor.b #$ff ;xor to substract value
|
|
inc a
|
|
AniListMoveYNotNeg:
|
|
rep #$31
|
|
and.w #$ff ;mask off high byte from x-position calculation
|
|
asl a ;subpixel precision
|
|
asl a
|
|
asl a
|
|
asl a
|
|
|
|
adc.w ObjEntryYPos,x
|
|
sta.w ObjEntryYPos,x
|
|
sep #$20
|
|
rts
|
|
|
|
AniListTileFrameInc:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
clc
|
|
adc.w ObjEntryTilesetFrame,x ;add increment number to tileset frame
|
|
sta.w ObjEntryTilesetFrame,x ;store new tileset frame
|
|
lda.w ObjEntryType,x
|
|
bit.b #%01000000 ;check if active. if inactive/offscreen, dont waste time uploading a new frame
|
|
beq AniListTileFrameIncOffscreen
|
|
|
|
jsr CreateObjUploadSpriteFrame
|
|
AniListTileFrameIncOffscreen:
|
|
rts
|
|
|
|
AniListTileSetSet:
|
|
ldx.b ObjectListPointerCurrent
|
|
sta.w ObjEntryTileset,x ;set tileset frame
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
|
|
jsr CreateObjUploadSpriteFrame
|
|
rts
|
|
|
|
AniListTileFrameSet:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
sta.w ObjEntryTilesetFrame,x ;set tileset frame
|
|
jsr CreateObjUploadSpriteFrame
|
|
rts
|
|
|
|
AniListPalConfSet:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
sta.w ObjEntryPalConf,x ;set palette and priority config
|
|
rts
|
|
|
|
AniListGotoAniList:
|
|
ldx.b ObjectListPointerCurrent
|
|
sta.w ObjEntryAniList,x ;set animation list
|
|
stz.w ObjEntryAniFrame,x ;reset animation frame
|
|
stz.b TempBuffer+10 ;clear animation-command repeat flag
|
|
rts
|
|
|
|
AniListMakeBGBound:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
and.b #1 ;get bit 0
|
|
clc
|
|
asl a ;move to correct location
|
|
asl a
|
|
asl a
|
|
asl a
|
|
asl a
|
|
sta.b TempBuffer
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
and.b #$DF ;mask off bg bound flag
|
|
ora.b TempBuffer ;set flag according to command parameter
|
|
sta.w ObjEntryType,x ;get object type byte
|
|
rts
|
|
|
|
AniListSubroutineEnable:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
and.b #1 ;get bit 0
|
|
clc
|
|
asl a ;move to correct location
|
|
asl a
|
|
asl a
|
|
sta.b TempBuffer
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
and.b #$F7 ;mask off bg bound flag
|
|
ora.b TempBuffer ;set flag according to command parameter
|
|
sta.w ObjEntryType,x ;get object type byte
|
|
rts
|
|
|
|
AniListSetSubroutine:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
sta.w ObjEntrySubRout,x ;set new subroutine
|
|
rts
|
|
|
|
AniListPlaySoundEffect:
|
|
rep #$31
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
and.w #$ff
|
|
jsr SpcPlaySoundEffectSimple
|
|
rts
|
|
|
|
|
|
AniListEnd:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
lda.w ObjEntryType,x ;
|
|
and.b #$ef ;mask off animate flag
|
|
sta.w ObjEntryType,x ;
|
|
rts
|
|
|
|
AniListSetVectorSpeed:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
sta.w ObjEntryVectorTarSpeed,x
|
|
and.b #%111111
|
|
cmp.b #$3f
|
|
bne AniListSetVectorSpeedNoOver
|
|
|
|
dec.w ObjEntryVectorTarSpeed,x
|
|
|
|
AniListSetVectorSpeedNoOver:
|
|
lda.w ObjEntryVectorSpeed,x
|
|
and.b #%1111111 ;clear "target met" flag
|
|
sta.w ObjEntryVectorSpeed,x
|
|
rts
|
|
|
|
AniListSetVectorDir:
|
|
ldx.b ObjectListPointerCurrent
|
|
inc.w ObjEntryAniFrame,x ;increment animation frame
|
|
sta.w ObjEntryVectorTarDir,x
|
|
and.b #%111111
|
|
sta.b TempBuffer
|
|
lda.w ObjEntryVectorDir,x
|
|
and.b #%111111
|
|
sta.b TempBuffer+1 ;34
|
|
cmp.b TempBuffer ;28
|
|
beq AniListSetVectorDirEqual
|
|
bcs AniListSetVectorDirCurrBigger
|
|
|
|
lda.b TempBuffer
|
|
sec
|
|
sbc.b TempBuffer+1
|
|
cmp.b #32 ;check if that distance is bigger than 180 degrees
|
|
bcs AniListSetVectorDirTargetCounterClock
|
|
bra AniListSetVectorDirTargetClockwise
|
|
|
|
AniListSetVectorDirCurrBigger:
|
|
sec ;substract current from target to get distance between them
|
|
sbc.b TempBuffer ;c
|
|
cmp.b #32 ;check if that distance is bigger than 180 degrees
|
|
bcc AniListSetVectorDirTargetCounterClock
|
|
|
|
AniListSetVectorDirTargetClockwise:
|
|
lda.w ObjEntryVectorDir,x
|
|
and.b #%00111111 ;clear "target met"
|
|
ora.b #%01000000 ;set "clockwise"-flag
|
|
sta.w ObjEntryVectorDir,x
|
|
rts
|
|
|
|
|
|
AniListSetVectorDirTargetCounterClock:
|
|
lda.w ObjEntryVectorDir,x
|
|
and.b #%00111111 ;clear "target met" and turn direction flag(counter clockwise)
|
|
sta.w ObjEntryVectorDir,x
|
|
rts
|
|
|
|
|
|
AniListSetVectorDirEqual:
|
|
lda.w ObjEntryVectorDir,x
|
|
ora.b #$80
|
|
sta.w ObjEntryVectorDir,x
|
|
rts
|
|
|
|
;in: x,16bit:relative object pointer
|
|
;out: x,16bit:relative pointer of next active object
|
|
;if carry is set on return, all objects have been processed
|
|
CheckActiveObject:
|
|
; rep #$31
|
|
CheckActiveObjectLoop:
|
|
lda.l (ObjEntryType + $7e0000-1),x ;get object type byte (get -1 so that the bmi check still works in 16bit mode)
|
|
; xba
|
|
; nop
|
|
bmi CheckActiveObjectDone ;if a not-present object is found, end list processing
|
|
|
|
|
|
txa
|
|
clc
|
|
adc.w #ObjectFileSize ;increment slot pointer
|
|
tax
|
|
cpx.w #ObjectFileSize*63 ;check if maximum number of objects reached, then step out(maybe spit out an error if needed)
|
|
bne CheckActiveObjectLoop
|
|
|
|
CheckActiveObjectListDone:
|
|
; sep #$20
|
|
sec
|
|
rts
|
|
|
|
|
|
; bra CheckActiveObjectListDone ;this is to prevent a lockup if the list is full
|
|
; beq CheckActiveObjectListDone ;this is to prevent a lockup if the list is full
|
|
|
|
; bra CheckActiveObjectLoop
|
|
|
|
CheckActiveObjectDone:
|
|
; rep #$31
|
|
stx.b ObjectListPointerCurrent ;store currently found object so that routines can check this back later
|
|
; sep #$20
|
|
clc
|
|
rts
|
|
|
|
|
|
|
|
ObjectProcessor:
|
|
php
|
|
sep #$20
|
|
phb
|
|
lda.b #$7e
|
|
pha
|
|
plb
|
|
lda.b NMIOamUploadFlag ;don't process list if last frame wasn't uploaded first. needed to prevent sprite flicker due to dma'ing incomplete oam list(irq firing during oam processor)
|
|
bne ObjectProcessorExit
|
|
rep #$31
|
|
stz.w ColObjListPointer ;clear pointer in collision object list
|
|
; sep #$20
|
|
;check for active objects
|
|
jsr ClearOamBufferPart ;only clear the part of the buffer that was actually written to last frame
|
|
jsr ClearOamPriorityBuffer
|
|
; jsr ClearPriorityZBuffer
|
|
; jsr ClearColObjList
|
|
ldx.w #0 ;start at object 0
|
|
|
|
ObjectProcessorLoop:
|
|
jsr CheckActiveObject ;check for active object
|
|
bcc ObjectProcessorNoExit ;if carry is set, all objects were processed
|
|
|
|
jsr ZSortToOamBuffer
|
|
; rep #$31
|
|
stz.b ObjectListPointerCurrent
|
|
inc.b NMIOamUploadFlag ;initiate oam buffer upload
|
|
ObjectProcessorExit:
|
|
plb
|
|
plp
|
|
rts
|
|
|
|
ObjectProcessorNoExit:
|
|
|
|
;check if sprite needs to be drawn on screen:
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
; bit.b #%01000000
|
|
; beq ObjProcInactive ;skip the oam write if bit isnt set
|
|
|
|
bit.w #%00100000 ;check if sprite is screenbound. if it is, dont check if the sprite is offscreen.
|
|
bne ObjProcDontCheckOnscreen
|
|
|
|
ObjectProcessorCheckOnscreen:
|
|
; rep #$31
|
|
lda.w ObjEntryXPos,x ;remove precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sta.w TempBufferTest
|
|
|
|
lda.w ObjEntryYPos,x ;remove precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sta.w TempBufferTest+2
|
|
|
|
lda.w ObjEntryXDisplacement,x
|
|
and.w #$ff
|
|
eor.w #$ffff
|
|
|
|
; adc.w ObjEntryXPos,x ;check if sprite is left of screen
|
|
clc
|
|
adc.w TempBufferTest
|
|
clc
|
|
adc.w #32
|
|
cmp.b ScreenPixelPositionX
|
|
bcc ObjProcInactive
|
|
|
|
lda.w ObjEntryXDisplacement,x
|
|
and.w #$ff
|
|
clc
|
|
adc.b ScreenPixelPositionX
|
|
|
|
clc
|
|
adc.w #(TileBufferSizeX+1)*8 ;check if sprite is right of screen
|
|
; cmp.w ObjEntryXPos,x
|
|
cmp.w TempBufferTest
|
|
bcc ObjProcInactive
|
|
|
|
lda.w ObjEntryYDisplacement,x
|
|
and.w #$ff
|
|
eor.w #$ffff
|
|
clc
|
|
; adc.w ObjEntryYPos,x ;check if sprite is on top of screen
|
|
adc.w TempBufferTest+2
|
|
clc
|
|
adc.w #32
|
|
cmp.b ScreenPixelPositionY
|
|
bcc ObjProcInactive
|
|
|
|
lda.w ObjEntryYDisplacement,x
|
|
and.w #$ff
|
|
clc
|
|
adc.b ScreenPixelPositionY
|
|
clc
|
|
adc.w #TileBufferSizeX*8 ;check if sprite is below screen
|
|
; cmp.w ObjEntryYPos,x
|
|
cmp.w TempBufferTest+2
|
|
bcc ObjProcInactive
|
|
|
|
|
|
ObjProcDontCheckOnscreen:
|
|
jsr ObjectSingleSpriteToOam
|
|
|
|
; sep #$20
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
ora.w #%01000000
|
|
sta.w ObjEntryType,x ;set active flag
|
|
|
|
|
|
bra ObjProcActive
|
|
ObjProcInactive:
|
|
; sep #$20
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
and.w #%10111111
|
|
sta.w ObjEntryType,x ;clear active flag
|
|
|
|
ObjProcActive:
|
|
; sep #$20
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
phx
|
|
bit.w #%00010000
|
|
beq ObjProcNoAnim ;skip the oam write if bit isnt set
|
|
|
|
jsr ObjectProcessAniList
|
|
|
|
ObjProcNoAnim:
|
|
plx
|
|
stx.b ObjectListPointerCurrent ;must be saved cause ani list routines that create new sprites might rewrite it
|
|
phx
|
|
; sep #$20
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
bit.w #%00001000
|
|
beq ObjProcNoSub ;skip the oam write if bit isnt set
|
|
|
|
jsr ObjectProcessSubroutine
|
|
|
|
ObjProcNoSub:
|
|
; sep #$20
|
|
plx
|
|
stx.b ObjectListPointerCurrent ;must be saved cause ani list routines that create new sprites might rewrite it
|
|
phx
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
bit.w #%00000100
|
|
beq ObjProcNoColl ;skip the oam write if bit isnt set
|
|
|
|
jsr CreateColListEntry
|
|
|
|
ObjProcNoColl:
|
|
plx
|
|
stx.b ObjectListPointerCurrent ;must be saved cause ani list routines that create new sprites might rewrite it
|
|
/*
|
|
lda.w ObjEntryVectorTarSpeed,x ;get vector target speed
|
|
and.w #%111111 ;mask off target bit
|
|
beq ObjProcNoVectorMove ;skip vector move if vector speed is 0
|
|
|
|
jsr ObjVectorMoveHandler
|
|
|
|
ObjProcNoVectorMove:
|
|
*/
|
|
;check if object is of gravity decaying type
|
|
lda.w ObjEntryType2,x ;get object type byte
|
|
|
|
bit.w #%10000 ;check if sprite is screenbound. if it is, dont check if the sprite is offscreen.
|
|
beq ObjProcNoGravity
|
|
|
|
jsr ObjProcUpdateGravity
|
|
|
|
|
|
|
|
ObjProcNoGravity:
|
|
; rep #$31
|
|
txa
|
|
clc
|
|
adc.w #ObjectFileSize
|
|
tax
|
|
stx.b ObjectListPointerCurrent ;relative pointer to current object in objectlist
|
|
|
|
jmp ObjectProcessorLoop
|
|
|
|
plp
|
|
rts
|
|
|
|
;upload collidable object information to a special list
|
|
CreateColListEntry:
|
|
php
|
|
txy
|
|
ldx ColObjListPointer ;get pointer to currently active slot in col obj list
|
|
sep #$20
|
|
; lda.b #$80 ;load col-obj present flag
|
|
lda.w ObjEntryColliSub,y ;get subroutine number and
|
|
ora.b #$80 ;set enable flag
|
|
xba
|
|
lda.w ObjEntryListOffset,y ;load obj number in object list
|
|
rep #$31
|
|
sta.l ColObjList,x ;store in obj col list
|
|
|
|
lda.w ObjEntryXPos,y ;get x pos of obj and divide by 16
|
|
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
|
|
inx
|
|
inx
|
|
sta.l ColObjList,x ;store in obj col list
|
|
|
|
lda.w ObjEntryYPos,y ;get y pos of obj and divide by 16
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
inx
|
|
inx
|
|
sta.l ColObjList,x ;store in obj col list
|
|
inx
|
|
inx
|
|
stx ColObjListPointer ;update pointer to next entry in collision list
|
|
|
|
tyx
|
|
plp
|
|
rts
|
|
|
|
|
|
;in: a, 8bit: number of object to load
|
|
; x, 16bit: target low: x, high:y-position/8
|
|
;in y, 8bit: target z-position
|
|
;uses TempBuffer0,1. maybe convert to use own temp area for better flexibility?
|
|
CreateObjectPosition:
|
|
php
|
|
|
|
rep #$31
|
|
stx.b CollisionPixelX
|
|
sty.b CollisionPixelY
|
|
; sty.b CollisionPixelY
|
|
and.w #$ff ;2byte pointer
|
|
asl a
|
|
tax ;put into x
|
|
|
|
phd
|
|
lda.w #0
|
|
tcd
|
|
|
|
sep #$20
|
|
phb
|
|
lda.b #$7e
|
|
pha
|
|
plb
|
|
rep #$31
|
|
|
|
lda.l (ObjectLUT+BaseAdress),x ;get relative pointer to object
|
|
clc
|
|
adc.w #ObjectLUT ;calculate real pointer
|
|
sta.b ThreeBytePointerLo
|
|
|
|
sep #$20
|
|
lda.b #(:ObjectLUT+BaseAdress>>16)
|
|
sta.b ThreeBytePointerBank
|
|
|
|
;check for free object space in object list:
|
|
; rep #$31
|
|
ldx.w #0
|
|
|
|
CreateObjectPositionCheckFreeSpaceLoop:
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
bpl CreateObjectPositionCheckFreeSpaceDone ;if free, step out
|
|
|
|
rep #$31
|
|
txa
|
|
adc.w #ObjectFileSize ;increment slot pointer
|
|
tax
|
|
sep #$20
|
|
cpx.w #ObjectFileSize*63 ;check if maximum number of objects reached, then step out(maybe spit out an error if needed)
|
|
beq CreateObjectPositionCheckFreeSpaceDone ;this is to prevent a lockup if the list is full
|
|
|
|
bra CreateObjectPositionCheckFreeSpaceLoop
|
|
|
|
CreateObjectPositionCheckFreeSpaceDone:
|
|
rep #$31
|
|
stx.b ObjectListPointerCurrent ;relative pointer to current object in objectlist
|
|
txa
|
|
clc
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a ;divide objectlist pointer by 16 to get current object number
|
|
sep #$20
|
|
sta.b CurrentObjectNumber
|
|
|
|
rep #$31
|
|
ldy.w #0
|
|
|
|
CreateObjectPositionUploadLoop:
|
|
lda.b [ThreeBytePointerLo],y ;get first byte of object file in rom
|
|
sta.w ObjEntryType,x
|
|
|
|
inx
|
|
inx
|
|
iny
|
|
iny
|
|
cpy.w #(ObjectFileSize&$fffe) ;check if object file was uploaded, word-aligned
|
|
bne CreateObjectPositionUploadLoop
|
|
|
|
ldx.b ObjectListPointerCurrent
|
|
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
bit.w #%100000 ;check if screenbound
|
|
beq CreateObjectPositionNotScreenBound
|
|
|
|
lda.w ObjEntryType2-1,x ;pseudo-3d object?
|
|
bpl CreateObjectPositionNot3D
|
|
|
|
lda.b CollisionPixelX ;plot directly to loaded xy position if screenbound
|
|
and.w #$ff
|
|
clc
|
|
asl a ;multiply with 16, 3d high-precision value
|
|
asl a
|
|
asl a
|
|
asl a
|
|
; asl a
|
|
; asl a
|
|
|
|
asl a
|
|
asl a
|
|
asl a
|
|
sta.w ObjEntryXPos,x
|
|
lda.b CollisionPixelX
|
|
and.w #$ff00
|
|
xba
|
|
clc
|
|
asl a ;multiply with 16, 3d high-precision value
|
|
asl a
|
|
asl a
|
|
asl a
|
|
; asl a
|
|
; asl a
|
|
|
|
asl a
|
|
asl a
|
|
asl a
|
|
sta.w ObjEntryYPos,x
|
|
|
|
lda.b CollisionPixelY ;store z-value
|
|
sep #$20
|
|
sta.w ObjEntryZDisplacement,x
|
|
rep #$31
|
|
bra CreateObjectPositionNotScreenBoundSkip
|
|
|
|
|
|
|
|
CreateObjectPositionNot3D:
|
|
lda.b CollisionPixelX ;plot directly to loaded xy position if screenbound
|
|
and.w #$ff
|
|
clc
|
|
asl a ;multiply by 8
|
|
asl a
|
|
asl a
|
|
|
|
asl a ;add subpixel precision
|
|
asl a
|
|
asl a
|
|
asl a
|
|
|
|
sta.w ObjEntryXPos,x
|
|
lda.b CollisionPixelX
|
|
xba
|
|
and.w #$ff
|
|
clc
|
|
asl a ;multiply by 8
|
|
asl a
|
|
asl a
|
|
|
|
asl a ;add subpixel precision
|
|
asl a
|
|
asl a
|
|
asl a
|
|
|
|
sta.w ObjEntryYPos,x
|
|
|
|
|
|
bra CreateObjectPositionNotScreenBoundSkip
|
|
|
|
CreateObjectPositionNotScreenBound:
|
|
clc
|
|
lda.b CollisionPixelX
|
|
and.w #$ff
|
|
asl a ;multiply by 8
|
|
asl a
|
|
asl a
|
|
|
|
asl a ;add subpixel precision
|
|
asl a
|
|
asl a
|
|
asl a
|
|
|
|
sta.w ObjEntryXPos,x
|
|
clc
|
|
lda.b CollisionPixelX
|
|
and.w #$ff00
|
|
xba
|
|
asl a ;multiply by 8
|
|
asl a
|
|
asl a
|
|
|
|
asl a ;add subpixel precision
|
|
asl a
|
|
asl a
|
|
asl a
|
|
sta.w ObjEntryYPos,x
|
|
|
|
CreateObjectPositionNotScreenBoundSkip:
|
|
|
|
;upload object number:
|
|
; ldx.b ObjectListPointer
|
|
; clc
|
|
; adc.w #(ObjectList & $ffff)
|
|
; clc
|
|
; adc.w #13
|
|
; tax
|
|
lda.w #0
|
|
tay
|
|
; tax
|
|
; inx
|
|
sep #$20
|
|
lda.b CurrentObjectNumber
|
|
sta.w ObjEntryListOffset,x ;store in the object number offset in an object file
|
|
|
|
jsr CreateObjUploadSpriteFrame
|
|
|
|
jsr CreateObjUploadPalette
|
|
|
|
;the animation list has to be executed at once in case objects create additional objects
|
|
ldx.b ObjectListPointerCurrent
|
|
; stx.b ObjectListPointerCurrent
|
|
phx
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
bit.b #%00010000
|
|
beq ObjLoaderPositionNoAnim ;skip the oam write if bit isnt set
|
|
|
|
jsr ObjectProcessAniList
|
|
|
|
ObjLoaderPositionNoAnim:
|
|
|
|
lda.w ObjEntryType2,x ;increase max obj number if gravity particle present
|
|
bit.b #%10000
|
|
beq ObjLoaderPositionNoGravObj ;skip the oam write if bit isnt set
|
|
|
|
jsr ObjectProcessGravObjInc
|
|
|
|
ObjLoaderPositionNoGravObj:
|
|
|
|
|
|
|
|
plx
|
|
stx.b ObjectListPointerCurrent ;must be saved cause ani list routines that create new sprites might rewrite it
|
|
|
|
sep #$20
|
|
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
bit.b #%00001000
|
|
beq ObjLoaderPositionNoSub ;skip the oam write if bit isnt set
|
|
|
|
jsr ObjectProcessSubroutine
|
|
|
|
ObjLoaderPositionNoSub:
|
|
|
|
plb
|
|
pld
|
|
plp
|
|
rts
|
|
|
|
|
|
;upload palette:
|
|
CreateObjUploadPalette:
|
|
lda.w ObjEntryType2,x
|
|
bit.b #%00100000 ;don't upload palette if corresponding flag is set
|
|
bne CreateObjUploadPaletteCancel
|
|
|
|
rep #$31
|
|
lda.w ObjEntryPalConf,x ;get palette config
|
|
sta.b TempBuffer
|
|
lda.w ObjEntryPalNumber,x ;get palette number
|
|
and.w #$ff ;2byte pointer
|
|
asl a ;2byte length
|
|
asl a
|
|
tax ;put into x
|
|
lda.l (SpritePaletteLUT+BaseAdress),x ;get relative pointer to object
|
|
clc
|
|
adc.w #SpritePaletteLUT ;calculate real pointer
|
|
sta.b ThreeBytePointerLo
|
|
|
|
lda.l (SpritePaletteLUT+BaseAdress+2),x ;get length
|
|
and.w #$fe ;mask off bit0. palettes always have word entries. prevents uploader from crashing if invalid palette is being loaded
|
|
cmp.w #$20
|
|
bcs UploadObjPalNoOverflow
|
|
|
|
lda.w #$20 ;never load palettes bigger than 32 bytes
|
|
UploadObjPalNoOverflow:
|
|
|
|
|
|
sta.b TempBuffer+1
|
|
|
|
lda.w #0
|
|
sep #$20
|
|
lda.b #(:SpritePaletteLUT+BaseAdress>>16)
|
|
sta.b ThreeBytePointerBank
|
|
|
|
rep #$31
|
|
lda.b TempBuffer
|
|
and.w #$E ;only get three palette bits
|
|
clc
|
|
lsr a ;rotate right to get palette number
|
|
clc
|
|
adc.w #8 ;add sprite palette offset
|
|
|
|
|
|
asl a ;multiply with 16 to get real start adress in palette buffer
|
|
asl a
|
|
asl a
|
|
asl a
|
|
asl a
|
|
|
|
tax
|
|
|
|
ldy.w #0
|
|
CreateObjectPositionUploadPaletteLoop:
|
|
lda.b [ThreeBytePointerLo],y ;get first byte of object file in rom
|
|
sta.w PaletteBuffer & $ffff,x
|
|
|
|
inx
|
|
inx
|
|
iny
|
|
iny
|
|
cpy.b TempBuffer+1 ;check if object file was uploaded, word-aligned
|
|
bne CreateObjectPositionUploadPaletteLoop
|
|
|
|
sep #$20
|
|
inc.b NMIPaletteUploadFlag
|
|
|
|
CreateObjUploadPaletteCancel:
|
|
rts
|
|
|
|
;in: a, 8bit: number of object to load
|
|
CreateObject:
|
|
php
|
|
|
|
rep #$31
|
|
and.w #$ff ;2byte pointer
|
|
sta.b TempBuffer
|
|
asl a
|
|
tax ;put into x
|
|
|
|
phd
|
|
lda.w #0
|
|
tcd
|
|
|
|
|
|
|
|
|
|
sep #$20
|
|
phb
|
|
lda.b #$7e
|
|
pha
|
|
plb
|
|
rep #$31
|
|
|
|
lda.l (ObjectLUT+BaseAdress),x ;get relative pointer to object
|
|
clc
|
|
adc.w #ObjectLUT ;calculate real pointer
|
|
sta.b ThreeBytePointerLo
|
|
|
|
sep #$20
|
|
lda.b #(:ObjectLUT+BaseAdress>>16)
|
|
sta.b ThreeBytePointerBank
|
|
|
|
;check for free object space in object list:
|
|
; rep #$31
|
|
ldx.w #0
|
|
|
|
CreateObjectCheckFreeSpaceLoop:
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
bpl CreateObjectCheckFreeSpaceDone ;if free, step out
|
|
|
|
rep #$31
|
|
txa
|
|
adc.w #ObjectFileSize ;increment slot pointer
|
|
tax
|
|
sep #$20
|
|
cpx.w #ObjectFileSize*63 ;check if maximum number of objects reached, then step out(maybe spit out an error if needed)
|
|
beq CreateObjectCheckFreeSpaceDone ;this is to prevent a lockup if the list is full
|
|
|
|
bra CreateObjectCheckFreeSpaceLoop
|
|
|
|
CreateObjectCheckFreeSpaceDone:
|
|
rep #$31
|
|
stx.b ObjectListPointerCurrent ;relative pointer to current object in objectlist
|
|
txa
|
|
clc
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a ;divide objectlist pointer by 16 to get current object number
|
|
sep #$20
|
|
sta.b CurrentObjectNumber
|
|
|
|
rep #$31
|
|
ldy.w #0
|
|
CreateObjectUploadLoop:
|
|
lda.b [ThreeBytePointerLo],y ;get first byte of object file in rom
|
|
sta.w ObjEntryType,x
|
|
|
|
inx
|
|
inx
|
|
iny
|
|
iny
|
|
cpy.w #(ObjectFileSize&$fffe) ;check if object file was uploaded, word-aligned
|
|
bne CreateObjectUploadLoop
|
|
|
|
;upload object number:
|
|
ldx.b ObjectListPointerCurrent
|
|
; clc
|
|
; adc.w #(ObjectList & $ffff)
|
|
; clc
|
|
; adc.w #13
|
|
; tax
|
|
lda.w #0
|
|
tay
|
|
; tax
|
|
; inx
|
|
; lda.b TempBuffer ;store number of object so other routines can clearly identify it.
|
|
; sta.w ObjEntryObjectNumber,x
|
|
sep #$20
|
|
lda.b CurrentObjectNumber
|
|
sta.w ObjEntryListOffset,x ;store in the object number offset in an object file
|
|
|
|
jsr CreateObjUploadSpriteFrame
|
|
|
|
/*
|
|
;upload palette:
|
|
rep #$31
|
|
lda.w ObjEntryPalConf,x ;get palette config
|
|
sta.b TempBuffer
|
|
lda.w ObjEntryPalNumber,x ;get palette number
|
|
and.w #$ff ;2byte pointer
|
|
asl a ;2byte length
|
|
asl a
|
|
tax ;put into x
|
|
lda.l (SpritePaletteLUT+BaseAdress),x ;get relative pointer to object
|
|
clc
|
|
adc.w #SpritePaletteLUT ;calculate real pointer
|
|
sta.b ThreeBytePointerLo
|
|
|
|
lda.l (SpritePaletteLUT+BaseAdress+2),x ;get length
|
|
and.w #$fe ;word-align, no longer than 256 bytes
|
|
sta.b TempBuffer+1
|
|
|
|
lda.w #0
|
|
sep #$20
|
|
lda.b #(:SpritePaletteLUT+BaseAdress>>16)
|
|
sta.b ThreeBytePointerBank
|
|
|
|
rep #$31
|
|
lda.b TempBuffer
|
|
and.w #$E ;only get three palette bits
|
|
clc
|
|
lsr a ;rotate right to get palette number
|
|
clc
|
|
adc.w #8 ;add sprite palette offset
|
|
|
|
|
|
asl a ;multiply with 16 to get real start adress in palette buffer
|
|
asl a
|
|
asl a
|
|
asl a
|
|
asl a
|
|
|
|
tax
|
|
|
|
ldy.w #0
|
|
CreateObjectUploadPaletteLoop:
|
|
lda.b [ThreeBytePointerLo],y ;get first byte of object file in rom
|
|
sta.w PaletteBuffer & $ffff,x
|
|
|
|
inx
|
|
inx
|
|
iny
|
|
iny
|
|
cpy.b TempBuffer+1 ;check if object file was uploaded, word-aligned
|
|
bne CreateObjectUploadPaletteLoop
|
|
|
|
sep #$20
|
|
inc.b NMIPaletteUploadFlag
|
|
*/
|
|
jsr CreateObjUploadPalette
|
|
|
|
ldx.b ObjectListPointerCurrent
|
|
; stx.b ObjectListPointerCurrent
|
|
; sep #$20
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
bit.b #%00010000
|
|
beq ObjLoaderNoAnim ;skip the oam write if bit isnt set
|
|
|
|
jsr ObjectProcessAniList
|
|
|
|
ObjLoaderNoAnim:
|
|
lda.w ObjEntryType2,x ;increase max obj number if gravity particle present
|
|
bit.b #%10000
|
|
beq ObjLoaderPositionNoGravObj2 ;skip the oam write if bit isnt set
|
|
|
|
jsr ObjectProcessGravObjInc
|
|
|
|
ObjLoaderPositionNoGravObj2:
|
|
|
|
sep #$20
|
|
|
|
lda.w ObjEntryType,x ;get object type byte
|
|
bit.b #%00001000
|
|
beq ObjLoaderNoSub ;skip the oam write if bit isnt set
|
|
|
|
jsr ObjectProcessSubroutine
|
|
|
|
ObjLoaderNoSub:
|
|
rep #$30
|
|
; stz.b ObjectListPointerCurrent
|
|
|
|
plb
|
|
pld
|
|
plp
|
|
rts
|
|
|
|
;upload current frame to vram
|
|
;get sprite config and lookup transfer type
|
|
CreateObjUploadSpriteFrame:
|
|
php
|
|
sep #$20
|
|
lda.w ObjEntryType2,x
|
|
bit.b #%01000000
|
|
beq CreateObjUploadSpriteFrameDontCancel
|
|
|
|
;don't upload any sprite tiles if bit6 of object type 2 is set. this is used for the music notes and stuff like that where many sprites share the same tiles in order to minimize dma overhead.
|
|
plp
|
|
rts
|
|
|
|
CreateObjUploadSpriteFrameDontCancel:
|
|
rep #$31
|
|
lda.w ObjEntryType,x ;get sprite size bit
|
|
and.w #%10
|
|
clc
|
|
asl a ;put into bit3
|
|
asl a
|
|
sta.b TempBuffer
|
|
|
|
|
|
lda.b ObjSel
|
|
and.w #%11100000
|
|
clc
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
ora.b TempBuffer ;add sprite size bit
|
|
sta.b TempBuffer
|
|
asl a ;multiply by 3 to get pointer
|
|
clc
|
|
adc.b TempBuffer
|
|
phx
|
|
|
|
tax ;put into x
|
|
lda.l (ObjSizeLUT+BaseAdress),x ;get transfer length and number of bytes to transfer for this sprite
|
|
sta.b TempBuffer+8 ;+8 is length, +9 is transfer number
|
|
lda.l (ObjSizeLUT+BaseAdress+2),x ;get transfer type
|
|
sta.b TempBuffer+14 ;+14 is type
|
|
|
|
plx
|
|
; rep #$31
|
|
|
|
|
|
; lda.w #80
|
|
; sta.b TempBuffer ;this is the number of tiles of one frame
|
|
|
|
; stz.b TempBuffer+1
|
|
; sep #$20
|
|
clc
|
|
lda.b TempBuffer+8 ; length of one transfer and divide by 32 to get number of h-tiles
|
|
and.w #$ff
|
|
inc a ;increment once to get real length
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sta.b TempBuffer
|
|
|
|
lda.b TempBuffer+9 ; transfer number 16bit
|
|
and.w #$ff
|
|
sta.b TempBuffer+2
|
|
lda.w #0
|
|
tay
|
|
|
|
CreateObjCalcSizeLoop3:
|
|
cpy.b TempBuffer+2
|
|
beq CreateObjCalcSizeDone3
|
|
|
|
iny
|
|
adc.b TempBuffer
|
|
bra CreateObjCalcSizeLoop3
|
|
|
|
CreateObjCalcSizeDone3:
|
|
sta.b TempBuffer ;store total number of tiles
|
|
|
|
lda.w ObjEntryTilesetFrame,x ;get current tileset frame
|
|
and.w #$ff
|
|
sta.b TempBuffer+2
|
|
lda.w #0
|
|
tay
|
|
|
|
CreateObjCalcSizeLoop2:
|
|
cpy.b TempBuffer+2
|
|
beq CreateObjCalcSizeDone2
|
|
|
|
iny
|
|
adc.b TempBuffer
|
|
bra CreateObjCalcSizeLoop2
|
|
|
|
CreateObjCalcSizeDone2:
|
|
sta.b TempBuffer+2 ;this is the first tile to be uploaded
|
|
|
|
lda.w ObjEntryTileset,x ;get tileset number
|
|
and.w #$ff
|
|
sta.b TempBuffer+4 ;multiply with 3
|
|
asl a
|
|
clc
|
|
adc.b TempBuffer+4
|
|
|
|
txy
|
|
tax
|
|
lda.l (SpriteTilesetLUT+BaseAdress),x
|
|
sta.b TempBuffer+4
|
|
lda.l (SpriteTilesetLUT+1+BaseAdress),x
|
|
tyx
|
|
; and.w #$ff
|
|
sta.b TempBuffer+5
|
|
|
|
lda.b TempBuffer+2 ;get first tile and multiply with 32 to get actual byte offset
|
|
clc
|
|
asl a
|
|
asl a
|
|
asl a
|
|
asl a
|
|
asl a
|
|
clc
|
|
adc.b TempBuffer+4
|
|
bcc CreateObjCalcSourceNoWrap
|
|
|
|
inc.b TempBuffer+6 ;increment bank
|
|
|
|
CreateObjCalcSourceNoWrap:
|
|
sta.b TempBuffer+4 ;store transfer source offset
|
|
sep #$20
|
|
lda.w ObjEntryPalConf,x ;get nametable bit(msb of sprites first tile)
|
|
and.b #1
|
|
xba
|
|
lda.w ObjEntryVramTile,x ;get target tile number
|
|
; and.w #$ff
|
|
rep #$31
|
|
; clc
|
|
asl a ;multiply by 16(bytesize is 32, but vram is word-aligned)
|
|
asl a
|
|
asl a
|
|
asl a
|
|
clc
|
|
adc.b SpriteTileOffsetVram ;add initial sprite offset
|
|
sta.b TempBuffer+10 ;this is the vram target
|
|
phx ;push object list pointer
|
|
ldx.b DmaFifoPointer
|
|
|
|
lda.b TempBuffer+8 ;get transfer length and increase by 1 to get real length 16bit
|
|
and.w #$ff
|
|
inc a
|
|
sta.b TempBuffer+12
|
|
|
|
CreateObjUploadTilesLoop:
|
|
rep #$31
|
|
lda.b TempBuffer+14 ;transfer type
|
|
sta.l DmaFifoEntryType,x
|
|
|
|
;store transfer target
|
|
lda.b TempBuffer+10
|
|
sta.l DmaFifoEntryTarget,x ;vram target 2116
|
|
|
|
;store transfer source
|
|
lda.b TempBuffer+4
|
|
sta.l DmaFifoEntrySrcLo,x ;source 4202
|
|
lda.b TempBuffer+5
|
|
sta.l DmaFifoEntrySrcHi,x ;source 4203
|
|
|
|
;store transfer length
|
|
lda.b TempBuffer+12 ;get transfer length. not used except for 8x8 sprites, where it is 32 bytes
|
|
sta.l DmaFifoEntryCount,x ;length 4205
|
|
/*
|
|
;update vram target pointer:
|
|
lda.b TempBuffer+10
|
|
clc
|
|
adc.w #32*16/2 ;add one 16tile-line to vram target
|
|
sta.b TempBuffer+10
|
|
*/
|
|
;update fifo buffer target:
|
|
txa ;update fifo entry pointer
|
|
clc
|
|
adc.w #DmaFifoEntryLength
|
|
|
|
/*
|
|
tax
|
|
|
|
;update source pointer
|
|
lda.b TempBuffer+4
|
|
clc
|
|
adc.b TempBuffer+12
|
|
sta.b TempBuffer+4
|
|
|
|
sep #$20
|
|
dec.b TempBuffer+9 ;decrement number of transfers
|
|
bne CreateObjUploadTilesLoop
|
|
|
|
;sprite tile transfer done
|
|
rep #$31
|
|
txa ;update fifo entry pointer
|
|
; clc
|
|
; adc.w #DmaFifoEntryLength
|
|
*/
|
|
sta.b DmaFifoPointer
|
|
plx
|
|
plp ;fetch object list pointer
|
|
rts
|
|
|
|
|
|
/*
|
|
;old version which created multiple transfers in fifo buffer
|
|
CreateObjUploadSpriteFrame:
|
|
CreateObjLookupSpriteSize:
|
|
php
|
|
rep #$31
|
|
lda.w ObjEntryType,x ;get sprite size bit
|
|
and.w #%10
|
|
clc
|
|
asl a ;put into bit3
|
|
asl a
|
|
sta.b TempBuffer
|
|
|
|
|
|
lda.b ObjSel
|
|
and.w #%11100000
|
|
clc
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
ora.b TempBuffer ;add sprite size bit
|
|
asl a ;multiply by 2 to get pointer
|
|
phx
|
|
|
|
tax ;put into x
|
|
lda.l (ObjSizeLUT+BaseAdress),x ;get transfer length and number of bytes to transfer for this sprite
|
|
plx
|
|
sta.b TempBuffer+8 ;+8 is length, +9 is transfer number
|
|
; rep #$31
|
|
|
|
|
|
; lda.w #80
|
|
; sta.b TempBuffer ;this is the number of tiles of one frame
|
|
|
|
; stz.b TempBuffer+1
|
|
; sep #$20
|
|
clc
|
|
lda.b TempBuffer+8 ; length of one transfer and divide by 32 to get number of h-tiles
|
|
and.w #$ff
|
|
inc a ;increment once to get real length
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sta.b TempBuffer
|
|
|
|
lda.b TempBuffer+9 ; transfer number 16bit
|
|
and.w #$ff
|
|
sta.b TempBuffer+2
|
|
lda.w #0
|
|
tay
|
|
|
|
CreateObjCalcSizeLoop3:
|
|
cpy.b TempBuffer+2
|
|
beq CreateObjCalcSizeDone3
|
|
|
|
iny
|
|
adc.b TempBuffer
|
|
bra CreateObjCalcSizeLoop3
|
|
|
|
CreateObjCalcSizeDone3:
|
|
sta.b TempBuffer ;store total number of tiles
|
|
|
|
lda.w ObjEntryTilesetFrame,x ;get current tileset frame
|
|
and.w #$ff
|
|
sta.b TempBuffer+2
|
|
lda.w #0
|
|
tay
|
|
|
|
;this must be reprogrammed, too:
|
|
CreateObjCalcSizeLoop2:
|
|
cpy.b TempBuffer+2
|
|
beq CreateObjCalcSizeDone2
|
|
|
|
iny
|
|
adc.b TempBuffer
|
|
bra CreateObjCalcSizeLoop2
|
|
|
|
CreateObjCalcSizeDone2:
|
|
sta.b TempBuffer+2 ;this is the first tile to be uploaded
|
|
|
|
lda.w ObjEntryTileset,x ;get tileset number
|
|
and.w #$ff
|
|
sta.b TempBuffer+4 ;multiply with 3
|
|
asl a
|
|
clc
|
|
adc.b TempBuffer+4
|
|
|
|
txy
|
|
tax
|
|
lda.l (SpriteTilesetLUT+BaseAdress),x
|
|
sta.b TempBuffer+4
|
|
lda.l (SpriteTilesetLUT+1+BaseAdress),x
|
|
tyx
|
|
; and.w #$ff
|
|
sta.b TempBuffer+5
|
|
|
|
lda.b TempBuffer+2 ;get first tile and multiply with 32 to get actual byte offset
|
|
clc
|
|
asl a
|
|
asl a
|
|
asl a
|
|
asl a
|
|
asl a
|
|
clc
|
|
adc.b TempBuffer+4
|
|
bcc CreateObjCalcSourceNoWrap
|
|
|
|
inc.b TempBuffer+6 ;increment bank
|
|
|
|
CreateObjCalcSourceNoWrap:
|
|
sta.b TempBuffer+4 ;store transfer source offset
|
|
sep #$20
|
|
lda.w ObjEntryPalConf,x ;get nametable bit(msb of sprites first tile)
|
|
and.b #1
|
|
xba
|
|
lda.w ObjEntryVramTile,x ;get target tile number
|
|
; and.w #$ff
|
|
rep #$31
|
|
; clc
|
|
asl a ;multiply by 16(bytesize is 32, but vram is word-aligned)
|
|
asl a
|
|
asl a
|
|
asl a
|
|
clc
|
|
adc.b SpriteTileOffsetVram ;add initial sprite offset
|
|
sta.b TempBuffer+10 ;this is the vram target
|
|
phx ;push object list pointer
|
|
ldx.b DmaFifoPointer
|
|
|
|
lda.b TempBuffer+8 ;get transfer length and increase by 1 to get real length 16bit
|
|
and.w #$ff
|
|
inc a
|
|
sta.b TempBuffer+12
|
|
|
|
CreateObjUploadTilesLoop:
|
|
rep #$31
|
|
lda #1 ;transfer type normal dma
|
|
sta.l DmaFifoEntryType,x
|
|
|
|
;store transfer target
|
|
lda.b TempBuffer+10
|
|
sta.l DmaFifoEntryTarget,x ;vram target 2116
|
|
|
|
;store transfer source
|
|
lda.b TempBuffer+4
|
|
sta.l DmaFifoEntrySrcLo,x ;source 4202
|
|
lda.b TempBuffer+5
|
|
sta.l DmaFifoEntrySrcHi,x ;source 4203
|
|
|
|
;store transfer length
|
|
lda.b TempBuffer+12 ;get transfer length
|
|
sta.l DmaFifoEntryCount,x ;length 4205
|
|
|
|
;update vram target pointer:
|
|
lda.b TempBuffer+10
|
|
clc
|
|
adc.w #32*16/2 ;add one 16tile-line to vram target
|
|
sta.b TempBuffer+10
|
|
;update fifo buffer target:
|
|
txa ;update fifo entry pointer
|
|
clc
|
|
adc.w #DmaFifoEntryLength
|
|
tax
|
|
|
|
;update source pointer
|
|
lda.b TempBuffer+4
|
|
clc
|
|
adc.b TempBuffer+12
|
|
sta.b TempBuffer+4
|
|
|
|
sep #$20
|
|
dec.b TempBuffer+9 ;decrement number of transfers
|
|
bne CreateObjUploadTilesLoop
|
|
|
|
;sprite tile transfer done
|
|
rep #$31
|
|
txa ;update fifo entry pointer
|
|
; clc
|
|
; adc.w #DmaFifoEntryLength
|
|
sta.b DmaFifoPointer
|
|
plx
|
|
plp ;fetch object list pointer
|
|
rts
|
|
*/
|
|
|
|
;first writes a sorted list of object priorities, then transfers these to oam
|
|
ZSortToOamBuffer:
|
|
php
|
|
rep #$31
|
|
sep #$20
|
|
ldx.w #0 ;reset pointer to priority list
|
|
lda.b OamZsortSpriteNumber ;exit immediatly if there are no priority sprites on the current screen. else, it keeps on going on forever
|
|
bne ZSortToOamBufferNoExit
|
|
|
|
plp
|
|
rts
|
|
|
|
ZSortToOamBufferNoExit:
|
|
sta.b TempBuffer+2 ;counter for inner loop
|
|
sta.b TempBuffer+3 ;counter for outer loop
|
|
stz.b TempBuffer ;clear "highest priority" variable
|
|
|
|
|
|
; ldx.w #0 ;reset pointer to priority list
|
|
txy ;reset pointer to zsort list
|
|
|
|
|
|
|
|
|
|
ZsortSortingInnerLoop:
|
|
lda.w OamZSortBuffer & $FFFF+4,y ;entry present?
|
|
bpl ZsortNotPresent
|
|
|
|
;sprite present
|
|
lda.w OamZSortBuffer & $FFFF+5,y ;get priority
|
|
cmp.b TempBuffer ;check if bigger than last highest sprite
|
|
bcc ZsortNotBiggest
|
|
|
|
sta.b TempBuffer ;new highest priority
|
|
|
|
rep #$31
|
|
tya ;get pointer in sprite list, save to sort listing
|
|
sta.w OamZSortObjList & $FFFF,x
|
|
|
|
|
|
ZsortNotBiggest:
|
|
ZsortNotPresent:
|
|
rep #$31
|
|
tya
|
|
adc.w #OamZSortBufferSize ;get next entry
|
|
tay
|
|
sep #$20
|
|
|
|
dec.b TempBuffer+2 ;decrease inner counter
|
|
bne ZsortSortingInnerLoop
|
|
|
|
;sorted through all once
|
|
lda.b OamZsortSpriteNumber
|
|
sta.b TempBuffer+2 ;set counter for inner loop
|
|
stz.b TempBuffer ;clear highest
|
|
|
|
;clear "present"-bit of highest in this round
|
|
phy
|
|
lda.w OamZSortObjList & $FFFF,x ;get pointer to highest
|
|
tay
|
|
lda.w OamZSortBuffer & $FFFF+4,y ;clear "present"-bit
|
|
and.b #$7f
|
|
sta.w OamZSortBuffer & $FFFF+4,y
|
|
|
|
ply
|
|
|
|
inx ;figure out next one
|
|
inx
|
|
ldy.w #0 ;reset source buffer pointer
|
|
|
|
dec.b TempBuffer+3 ;decrease counter
|
|
bne ZsortSortingInnerLoop
|
|
|
|
ZSortToOamBufferDoneSorting:
|
|
; lda.b OamZsortSpriteNumber
|
|
stz.b TempBuffer+2 ;clear pointer in priority list, counter
|
|
ldx.b OamBufferPointer ;get oam pointer, write after priorityless sprites
|
|
; ldy.w #0
|
|
|
|
ZSortWriteToOamLoop:
|
|
rep #$31 ;get pointer to current priority list entry
|
|
lda.b TempBuffer+2
|
|
and.w #$ff
|
|
tay
|
|
|
|
lda.w OamZSortObjList & $FFFF,y ;get first pointer
|
|
tay
|
|
|
|
;copy data from zbuffer to oam buffer
|
|
lda.w OamZSortBuffer & $FFFF,y
|
|
sta.w OamBuffer & $ffff,x ;store in oam buffer
|
|
lda.w OamZSortBuffer & $FFFF+2,y
|
|
sta.w OamBuffer & $ffff+2,x ;store in oam buffer
|
|
|
|
phx
|
|
phy
|
|
|
|
txa
|
|
and.w #$01f0 ;get byte target of size buffer
|
|
; and.w #$0100 ;get byte target of size buffer
|
|
; xba ;divide by 16
|
|
|
|
lsr a ;divide by 16
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sta.b TempBuffer ;this is the 16bit pointer to the current byte in size buffer
|
|
|
|
;calculate bit offset in spritesize buffer:
|
|
txa
|
|
clc
|
|
and.w #$c ;get target bits of size buffer
|
|
lsr a ;shift right twice to get correct jump offset
|
|
tax
|
|
lda.w OamZSortBuffer & $FFFF+4,y
|
|
ldy.b TempBuffer
|
|
and.w #%11
|
|
jmp (PrioObjectProcessorSizeTableJTbl,x)
|
|
|
|
PrioObjectProcessorSizeTableJTbl:
|
|
.dw PrioObjProcSizeJtblBit0
|
|
.dw PrioObjProcSizeJtblBit1
|
|
.dw PrioObjProcSizeJtblBit2
|
|
.dw PrioObjProcSizeJtblBit3
|
|
|
|
PrioObjProcSizeJtblBit3:
|
|
asl a
|
|
asl a
|
|
PrioObjProcSizeJtblBit2:
|
|
asl a
|
|
asl a
|
|
PrioObjProcSizeJtblBit1:
|
|
asl a
|
|
asl a
|
|
PrioObjProcSizeJtblBit0:
|
|
|
|
ora.w OamPriorityBuffer & $ffff,y ;store in corresponding bits/bytes
|
|
sta.w OamPriorityBuffer & $ffff,y ;store in corresponding bits/bytes
|
|
ply
|
|
plx
|
|
lda.w #0
|
|
; sta.w OamZSortBuffer & $FFFF,y ;clear zsort buffer entries right after use so we dont have to clear the whole list each frame
|
|
; sta.w OamZSortBuffer & $FFFF+2,y
|
|
; sta.w OamZSortBuffer & $FFFF+3,y
|
|
|
|
inx
|
|
inx
|
|
inx
|
|
inx
|
|
|
|
sep #$20
|
|
lda.b TempBuffer+2 ;process next entry
|
|
inc a
|
|
inc a
|
|
sta.b TempBuffer+2 ;process next entry
|
|
lsr a
|
|
cmp.b OamZsortSpriteNumber ;done copying?
|
|
|
|
bne ZSortWriteToOamLoop
|
|
|
|
stx.b OamBufferPointer
|
|
|
|
plp
|
|
rts
|
|
|
|
;in: a,16bit: desired scanline to draw sprite to.
|
|
;out: y,16bit: relative pointer to correct entry in zsort buffer
|
|
ObjectZSortGetFreeSlot:
|
|
; php
|
|
; rep #$31
|
|
; tya
|
|
sta.b TempBuffer ;get desired scanline
|
|
asl a ;multiply by 5 to get pointer into zsort buffer
|
|
asl a
|
|
clc
|
|
adc.b TempBuffer
|
|
tay
|
|
|
|
ObjectZSortGetFreeSlotLoop:
|
|
lda.w OamZSortBuffer & $FFFF,y
|
|
beq ObjectZSortGetFreeSlotDone
|
|
|
|
tya
|
|
sec ;increase scanline by one if theres a sprite here already
|
|
sbc.w #OamZSortBufferSize
|
|
bcc ObjZSortGetSlotUnderrun
|
|
tay
|
|
bra ObjectZSortGetFreeSlotLoop
|
|
|
|
|
|
ObjZSortGetSlotUnderrun:
|
|
ldy.w #210*OamZSortBufferSize ;select max entry
|
|
bra ObjectZSortGetFreeSlotLoop
|
|
|
|
ObjectZSortGetFreeSlotDone:
|
|
; tya
|
|
; clc
|
|
; adc.w #OamZSortBuffer ;make this a direct pointer
|
|
; tay
|
|
; plp
|
|
rts
|
|
|
|
ObjProcScreenBound:
|
|
sep #$20
|
|
lda.w ObjEntryZDisplacement,x ;calculate priority?
|
|
; and.w #$ff ;only get z-priority
|
|
beq ObjProcScreenBoundNoPriorityCalc
|
|
|
|
;****************************************************
|
|
;write sprite to oam zsort buffer instead
|
|
ldy.b OamZsortBufferPointer ;get buffer pointer
|
|
|
|
|
|
|
|
; rep #$31
|
|
|
|
lda.w ObjEntryType2,x ;pseudo-3d sprite?
|
|
bmi ObjProc3dSprite
|
|
|
|
|
|
lda.w ObjEntryZDisplacement,x ;store z
|
|
|
|
|
|
sta.w OamZSortBuffer & $ffff+5,y
|
|
rep #$31
|
|
lda.w ObjEntryXPos,x ;get xy-coordinates
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
; sep #$20
|
|
sta.w OamZSortBuffer & $ffff,y ;store x coordinate
|
|
; rep #$31
|
|
lda.w ObjEntryYPos,x ;get xy-coordinates
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sep #$20
|
|
sta.w OamZSortBuffer & $ffff+1,y ;store x coordinate
|
|
jmp ObjProcZsortWriteExit
|
|
|
|
|
|
ObjProc3dSprite:
|
|
lda.w ObjEntryZDisplacement,x
|
|
eor.b #$ff ;invert priority for 3d sprites
|
|
|
|
sta.w OamZSortBuffer & $ffff+5,y
|
|
|
|
rep #$31
|
|
lda.w ObjEntryXPos,x ;get xy-coordinates
|
|
lsr a ;divide by 16, high precision position
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
; lsr a
|
|
; lsr a
|
|
sep #$20
|
|
sta.w OamZSortBuffer & $ffff,y ;store x coordinate
|
|
rep #$31
|
|
lda.w ObjEntryYPos,x ;get xy-coordinates
|
|
lsr a ;divide by 16, high precision position
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
; lsr a
|
|
; lsr a
|
|
sep #$20
|
|
sta.w OamZSortBuffer & $ffff+1,y ;store x coordinate
|
|
jmp ObjProcZsortWriteExit
|
|
|
|
|
|
|
|
ObjProcScreenBoundNoPriorityCalc:
|
|
|
|
rep #$31
|
|
lda.w ObjEntryXPos,x ;get xy-coordinates
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sta.w OamBuffer & $ffff,y ;store x coordinate
|
|
lda.w ObjEntryYPos,x ;get xy-coordinates
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sep #$20
|
|
sta.w OamBuffer & $ffff+1,y ;store y coordinate
|
|
|
|
jmp ObjProcDrawConfig
|
|
|
|
ObjectSingleSpriteToOam:
|
|
;dont upload directly to sprite buffer, but put sprite in a seperate 256entry-list according to y+ydisplacement value so the sprites get z-sorted. ;upload sprite to oam buffer:
|
|
|
|
; rep #$31
|
|
ldy.b OamBufferPointer ;get pointer to current oam buffer entry
|
|
|
|
lda.w ObjEntryType,x
|
|
bit.w #%00100000 ;check if screenbound
|
|
bne ObjProcScreenBound
|
|
|
|
|
|
lda.w ObjEntryZDisplacement,x
|
|
and.w #$ff
|
|
beq ObjProcNoPriorityCalc
|
|
|
|
ObjProcPriorityCalc:
|
|
;****************************************************
|
|
;write sprite to oam zsort buffer instead
|
|
ldy.b OamZsortBufferPointer ;get buffer pointer
|
|
lda.w ObjEntryYPos,x ;get xy-coordinates
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sta.b TempBuffer
|
|
|
|
lda.w ObjEntryZDisplacement,x
|
|
and.w #$ff
|
|
clc
|
|
; adc.w ObjEntryYPos,x ;get xy-coordinates
|
|
adc.b TempBuffer
|
|
sec
|
|
sbc.b ScreenPixelPositionY ;substract screen position to get sprite position on screen. before doing this, we made sure the sprite is actually onscreen.
|
|
|
|
sep #$20
|
|
sta.w OamZSortBuffer & $ffff+5,y
|
|
rep #$31
|
|
|
|
lda.w ObjEntryXPos,x ;get xy-coordinates
|
|
; clc
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
; adc.w #16 ;add 16 because of left border
|
|
sec
|
|
sbc.w ObjEntryXDisplacement,x
|
|
sec
|
|
sbc.b ScreenPixelPositionX ;substract screen position to get sprite position on screen. before doing this, we made sure the sprite is actually onscreen.
|
|
sta.w OamZSortBuffer & $ffff,y ;store x coordinate
|
|
bcs ObjProcPriorSprNoWrap1 ;if sprite wraps around left screen edge, carry is clear
|
|
|
|
lda.w ObjEntryType,x ;set x-position sign bit if sprite wraps around the edge
|
|
ora.w #%1
|
|
sta.w ObjEntryType,x
|
|
bra ObjProcPriorSprWrapDone
|
|
|
|
ObjProcPriorSprNoWrap1:
|
|
lda.w ObjEntryType,x ;clear x-position sign bit if sprite wraps around the edge
|
|
and.w #$fffe
|
|
sta.w ObjEntryType,x
|
|
|
|
ObjProcPriorSprWrapDone:
|
|
lda.w ObjEntryYPos,x ;get xy-coordinates
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sec
|
|
sbc.b ScreenPixelPositionY ;substract screen position to get sprite position on screen. before doing this, we made sure the sprite is actually onscreen.
|
|
sec
|
|
sbc.w ObjEntryYDisplacement,x
|
|
sep #$20
|
|
sta.w OamZSortBuffer & $ffff+1,y ;store x coordinate
|
|
|
|
ObjProcZsortWriteExit:
|
|
lda.w ObjEntryType,x ;get size/x-sign bits
|
|
and.b #%11
|
|
ora.b #$80 ;set "present" bit
|
|
sta.w OamZSortBuffer & $ffff+4,y
|
|
inc.b OamZsortSpriteNumber ;increment number of sortable sprites
|
|
rep #$31
|
|
lda.w ObjEntryVramTile,x ;starting tile and config
|
|
sta.w OamZSortBuffer & $ffff+2,y ;store tile and config
|
|
|
|
tya ;calc new value for buffer
|
|
adc.w #OamZSortBufferSize
|
|
sta.b OamZsortBufferPointer
|
|
|
|
rts
|
|
;****************************************************
|
|
|
|
|
|
|
|
|
|
ObjProcNoPriorityCalc:
|
|
lda.w ObjEntryXPos,x ;get xy-coordinates
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
clc
|
|
; adc.w #16 ;add 16 because of left border
|
|
sec
|
|
sbc.w ObjEntryXDisplacement,x
|
|
|
|
sec
|
|
sbc.b ScreenPixelPositionX ;substract screen position to get sprite position on screen. before doing this, we made sure the sprite is actually onscreen.
|
|
|
|
sta.w OamBuffer & $ffff,y ;store x coordinate
|
|
bcs ObjProcSprNoWrap1 ;if sprite wraps around left screen edge, carry is clear
|
|
|
|
lda.w ObjEntryType,x ;set x-position sign bit if sprite wraps around the edge
|
|
ora.w #%1
|
|
sta.w ObjEntryType,x
|
|
bra ObjProcSprWrapDone
|
|
|
|
ObjProcSprNoWrap1:
|
|
lda.w ObjEntryType,x ;clear x-position sign bit if sprite wraps around the edge
|
|
and.w #$fffe
|
|
sta.w ObjEntryType,x
|
|
|
|
|
|
ObjProcSprWrapDone:
|
|
lda.w ObjEntryYPos,x ;get xy-coordinates
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sec
|
|
sbc.b ScreenPixelPositionY ;substract screen position to get sprite position on screen. before doing this, we made sure the sprite is actually onscreen.
|
|
sec
|
|
sbc.w ObjEntryYDisplacement,x
|
|
|
|
sep #$20
|
|
sta.w OamBuffer & $ffff+1,y ;store x coordinate
|
|
|
|
|
|
|
|
ObjProcDrawConfig:
|
|
|
|
;calculate byte offset in spritesize buffer:
|
|
rep #$31
|
|
|
|
|
|
; ldy.b OamBufferPointer ;get pointer to current oam buffer entry
|
|
phx
|
|
phy
|
|
tya
|
|
and.w #$1f0 ;get byte target of size buffer
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sta.b TempBuffer ;this is the 16bit pointer to the current byte in size buffer
|
|
;calculate bit offset in spritesize buffer:
|
|
tya
|
|
clc
|
|
and.w #$c ;get target bits of size buffer
|
|
lsr a ;shift right twice to get correct jump offset
|
|
tay
|
|
lda.w ObjEntryType,x ;get size/x-sign bits
|
|
tyx
|
|
ldy.b TempBuffer
|
|
and.w #%11
|
|
jmp (ObjectProcessorSizeTableJTbl,x)
|
|
|
|
ObjectProcessorSizeTableJTbl:
|
|
.dw ObjProcSizeJtblBit0
|
|
.dw ObjProcSizeJtblBit1
|
|
.dw ObjProcSizeJtblBit2
|
|
.dw ObjProcSizeJtblBit3
|
|
|
|
ObjProcSizeJtblBit3:
|
|
asl a
|
|
asl a
|
|
ObjProcSizeJtblBit2:
|
|
asl a
|
|
asl a
|
|
ObjProcSizeJtblBit1:
|
|
asl a
|
|
asl a
|
|
ObjProcSizeJtblBit0:
|
|
|
|
ora.w OamPriorityBuffer & $ffff,y ;store in corresponding bits/bytes
|
|
sta.w OamPriorityBuffer & $ffff,y ;store in corresponding bits/bytes
|
|
|
|
ply
|
|
plx
|
|
|
|
; sep #$20
|
|
|
|
|
|
|
|
rep #$31
|
|
lda.w ObjEntryVramTile,x ;starting tile and config
|
|
sta.w OamBuffer & $ffff+2,y ;store tile and config
|
|
|
|
iny ;move pointer to next tile
|
|
iny
|
|
iny
|
|
iny
|
|
sty.b OamBufferPointer ;store new buffer to next oam object
|
|
|
|
rts
|
|
|
|
|
|
|
|
InitOam:
|
|
php
|
|
jsr ClearOamBuffer
|
|
jsr ClearOamPriorityBuffer
|
|
jsr ClearObjectList
|
|
|
|
sep #$20
|
|
stz.w GravObjectCounter ;reset amount of gravity objects
|
|
stz.b NMIOamUploadFlag ;clear this flag so that oam processor is executed at least once (in case irq oam uploader fucks up)
|
|
|
|
.IF DEBUG == 1
|
|
lda.b #1 ;create cpu-usage object in every scene if debug is enabled
|
|
jsr CreateObject
|
|
|
|
.endif
|
|
plp
|
|
rts
|
|
|
|
ClearOamPriorityBuffer:
|
|
php
|
|
|
|
rep #$31
|
|
sep #$20
|
|
lda.b #0 ;clear word: $0000
|
|
ldy.w #$20
|
|
ldx.w #OamPriorityBuffer&$ffff
|
|
jsr ClearWRAM
|
|
|
|
|
|
plp
|
|
rts
|
|
/*
|
|
ClearOamPriorityBuffer:
|
|
php
|
|
rep #$31
|
|
lda.w #$0000 ;clear with y-position at line 255
|
|
ldx.w #$0020
|
|
ClearOamPriorityBufferLoop:
|
|
sta.l (OamPriorityBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
bne ClearOamPriorityBufferLoop
|
|
plp
|
|
rts
|
|
*/
|
|
|
|
|
|
|
|
|
|
ClearOamBufferPart:
|
|
/*
|
|
php
|
|
rep #$31
|
|
lda.w #$c900 ;clear with y-position at line 201
|
|
ldx.b OamBufferPointer
|
|
bra ClearOamBufferLoop
|
|
*/
|
|
php
|
|
rep #$31
|
|
sep #$20
|
|
lda.b #3 ;clear word: $0000
|
|
ldy.b OamBufferPointer
|
|
beq ClearOamBufferPartNoTrans ;need to do this, else $ffff long transfer occurs
|
|
|
|
ldx.w #OamBuffer&$ffff
|
|
jsr ClearWRAM
|
|
ClearOamBufferPartNoTrans:
|
|
rep #$31
|
|
stz.b OamBufferPointer
|
|
stz.b OamZsortBufferPointer
|
|
stz.b OamZsortSpriteNumber
|
|
plp
|
|
rts
|
|
|
|
|
|
ClearOamBuffer:
|
|
|
|
/*
|
|
php
|
|
rep #$31
|
|
lda.w #$c900 ;clear with y-position at line 201
|
|
ldx.w #$0200
|
|
ClearOamBufferLoop:
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
sta.l (OamBuffer &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
|
|
|
|
bpl ClearOamBufferLoop
|
|
*/
|
|
|
|
|
|
php
|
|
|
|
rep #$31
|
|
sep #$20
|
|
lda.b #3 ;clear word: $0000
|
|
ldy.w #$200
|
|
ldx.w #OamBuffer&$ffff
|
|
jsr ClearWRAM
|
|
|
|
rep #$31
|
|
stz.b OamBufferPointer
|
|
stz.b OamZsortBufferPointer
|
|
stz.b OamZsortSpriteNumber
|
|
plp
|
|
rts
|
|
|
|
|
|
|
|
|
|
ClearColObjList:
|
|
php
|
|
|
|
rep #$31
|
|
sep #$20
|
|
lda.b #0 ;clear word: $0000
|
|
ldy.w #ColObjFileSize*32 ;old one was: ldx.w #ColObjFileSize*32-2, maybe -2 needed?
|
|
ldx.w #ColObjList&$ffff
|
|
jsr ClearWRAM
|
|
plp
|
|
rts
|
|
|
|
|
|
/*
|
|
php
|
|
rep #$31
|
|
|
|
lda.w #0 ;clear with y-position at line 201
|
|
ldx.w #ColObjFileSize*32-2
|
|
ClearColObjListLoop:
|
|
sta.l (ColObjList &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
bne ClearColObjListLoop
|
|
|
|
stz.w ColObjListPointer
|
|
plp
|
|
rts
|
|
*/
|
|
|
|
|
|
ClearZBuffer:
|
|
php
|
|
|
|
rep #$31
|
|
sep #$20
|
|
lda.b #0 ;clear word: $0000
|
|
ldy.w #OamZSortBufferSize*256
|
|
ldx.w #OamZSortBuffer&$ffff
|
|
jsr ClearWRAM
|
|
plp
|
|
rts
|
|
|
|
/*
|
|
php
|
|
rep #$31
|
|
|
|
lda.w #0 ;clear with y-position at line 201
|
|
ldx.w #OamZSortBufferSize*256
|
|
ClearZBufferLoop:
|
|
sta.l (OamZSortBuffer & $ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
bne ClearZBufferLoop
|
|
|
|
; stz.w ColObjListPointer
|
|
plp
|
|
rts
|
|
*/
|
|
|
|
ClearObjectList:
|
|
php
|
|
rep #$31
|
|
sep #$20
|
|
lda.b #0 ;clear word: $0000
|
|
ldy.w #ObjectFileSize*64
|
|
ldx.w #ObjectList&$ffff
|
|
jsr ClearWRAM
|
|
plp
|
|
rts
|
|
|
|
|
|
/*
|
|
php
|
|
rep #$31
|
|
lda.w #$0000
|
|
ldx.w #ObjectFileSize*64
|
|
ClearObjectListLoop:
|
|
sta.l (ObjectList &$ffff + $7e0000-2),x
|
|
dex
|
|
dex
|
|
bne ClearObjectListLoop
|
|
plp
|
|
rts
|
|
*/
|
|
|
|
|
|
;in: a,16bit: type of objects to delete
|
|
;uses TempBuffer
|
|
;takes around 11 scanlines
|
|
SeekDeleteObjects:
|
|
php
|
|
rep #$31
|
|
sta.b TempBuffer ;store 16bit compare number
|
|
ldx.w #0
|
|
|
|
SeekDeleteObjectsLoop:
|
|
lda.l ObjEntryObjectNumber+$7e0000,x ;get object number
|
|
cmp.b TempBuffer
|
|
bne SeekDeleteObjectsNoMatch
|
|
|
|
lda.w #0
|
|
sta.l ObjEntryType+$7e0000,x ;delete object if number matches.
|
|
|
|
SeekDeleteObjectsNoMatch:
|
|
txa
|
|
clc
|
|
adc.w #ObjectFileSize ;goto next entry
|
|
tax
|
|
cpx.w #ObjectFileSize*64 ;check all 64 entries.
|
|
bne SeekDeleteObjectsLoop
|
|
|
|
|
|
plp
|
|
rts
|
|
|
|
;in: a,16bit: type of object to seek
|
|
;uses TempBuffer
|
|
;takes around 11 scanlines max.
|
|
;searches for first object of selected type.
|
|
;returns relative pointer to object in x
|
|
;if object was not found, carry is set
|
|
SeekObject:
|
|
php
|
|
rep #$31
|
|
sta.b TempBuffer ;store 16bit compare number
|
|
ldx.w #0
|
|
|
|
SeekObjectLoop:
|
|
lda.l ObjEntryObjectNumber+$7e0000,x ;get object number
|
|
cmp.b TempBuffer
|
|
bne SeekObjectNoMatch
|
|
;match found:
|
|
plp
|
|
clc
|
|
rts
|
|
|
|
|
|
SeekObjectNoMatch:
|
|
txa
|
|
clc
|
|
adc.w #ObjectFileSize ;goto next entry
|
|
tax
|
|
cpx.w #ObjectFileSize*64 ;check all 64 entries.
|
|
bne SeekObjectLoop
|
|
|
|
|
|
plp
|
|
sec
|
|
rts
|
|
|
|
;same as above, but don't reset pointer into obj list. useful for finding multiple objects of the same type
|
|
SeekObjectContinue:
|
|
php
|
|
rep #$31
|
|
sta.b TempBuffer
|
|
bra SeekObjectLoop
|
|
|
|
|
|
|
|
|
|
ObjVectorMoveHandler:
|
|
php
|
|
rep #$31
|
|
sep #$20
|
|
ldx.b ObjectListPointerCurrent
|
|
lda.w ObjEntryVectorSpeed,x
|
|
bmi ObjVectorSpeedMet
|
|
|
|
and.b #%111111
|
|
sta.b TempBuffer
|
|
lda.w ObjEntryVectorTarSpeed,x
|
|
pha
|
|
and.b #%111111
|
|
sta.b TempBuffer+1
|
|
pla ;get speed change mode (upper 2 bits) and use it as pointer to jump LUT
|
|
rol a
|
|
rol a
|
|
rol a
|
|
asl a
|
|
rep #$31
|
|
and.w #%110
|
|
phx
|
|
tax
|
|
sep #$20
|
|
|
|
jsr (ObjVectorSpeedChangeModeLUT,x)
|
|
|
|
plx
|
|
sta.b TempBuffer+2 ;store inc/dec value
|
|
|
|
|
|
;speed inc/dec
|
|
lda.b TempBuffer+1
|
|
cmp.b TempBuffer ;check if target speed is bigger than actual speed
|
|
bcs ObjVectorTarSpeedBiggerLinear
|
|
|
|
;target speed is lower than actual speed
|
|
lda.b TempBuffer
|
|
sec
|
|
sbc.b TempBuffer+2 ;#%1000 ;decrease speed by one full unit
|
|
cmp.b TempBuffer+1 ;has actual speed become => target?
|
|
bmi ObjVectorTarSpeedMetLin ;has speed wrapped around to $ff? if yes, target has also been reached(only needed if target is smaller than 1.0)
|
|
bcs ObjVectorTarSpeedNotMetLinInc
|
|
|
|
;target speed met:
|
|
bra ObjVectorTarSpeedMetLin
|
|
|
|
|
|
;target speed is bigger than actual speed
|
|
ObjVectorTarSpeedBiggerLinear:
|
|
lda.b TempBuffer
|
|
clc
|
|
adc.b TempBuffer+2 ;#%1000 ;increase speed by one full unit
|
|
cmp.b TempBuffer+1 ;has actual speed become => target?
|
|
bcc ObjVectorTarSpeedNotMetLinInc
|
|
|
|
ObjVectorTarSpeedMetLin:
|
|
lda.b TempBuffer+1 ;if target speed has been met or exceeded, write target speed into current and set "speed met"-flag.
|
|
and.b #%111111
|
|
ora.b #$80
|
|
|
|
ObjVectorTarSpeedNotMetLinInc:
|
|
sta.w ObjEntryVectorSpeed,x
|
|
ObjVectorSpeedMet:
|
|
|
|
rep #$31
|
|
ldx.b ObjectListPointerCurrent
|
|
lda.w ObjEntryVectorDir-1,x
|
|
bmi ObjVectorTargetDirMet
|
|
|
|
|
|
asl a
|
|
asl a
|
|
and.w #%1111110000000000 ;put into high byte
|
|
sta.b TempBuffer
|
|
lda.w ObjEntryVectorTurnSpeed,x ;get subpixel precision, 5bits starting at bit3
|
|
asl a ;put into bits 5-9
|
|
asl a
|
|
and.w #%0000001111100000 ;mask off unwanted stuff
|
|
ora.b TempBuffer
|
|
sta.b TempBuffer
|
|
lda.w ObjEntryVectorTarDir,x
|
|
|
|
pha
|
|
xba
|
|
asl a
|
|
asl a
|
|
and.w #%1111110000000000 ;put into high byte
|
|
sta.b TempBuffer+2
|
|
|
|
pla
|
|
lsr a
|
|
lsr a ;get speed change mode (upper 2 bits) and use it as pointer to jump LUT
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
|
|
and.w #%110
|
|
phx
|
|
tax
|
|
|
|
jsr (ObjVectorDirChangeModeLUT,x)
|
|
|
|
plx
|
|
sta.b TempBuffer+4 ;store inc/dec value
|
|
|
|
|
|
; lda.b TempBuffer+1
|
|
; cmp.b TempBuffer ;check if target speed is bigger than actual speed
|
|
lda.w ObjEntryVectorDir,x
|
|
bit.w #%01000000
|
|
bne ObjVectorRotateClockwise ;bcs ObjVectorTarSpeedBiggerLinear
|
|
|
|
;rotate anti-clockwise to meet target dir
|
|
lda.b TempBuffer ;get current angle
|
|
cmp.b TempBuffer+2 ;is target angle smaller? (=must 0 be crossed to reach target?)
|
|
bcs AntiClockwiseNoZeroCross
|
|
|
|
;must cross 0 to reach target
|
|
sec
|
|
sbc.b TempBuffer+4 ;substract speed value
|
|
bcs AntiClockwiseTargetNotMet ;if 0 wasn't passed, target value couldn't have possibly been reached
|
|
cmp.b TempBuffer+2 ;compare target to new angle
|
|
bcc AntiClockwiseTargetMet ;target met if new angle is smaller than old
|
|
bra AntiClockwiseTargetNotMet
|
|
|
|
ObjVectorTargetDirMet:
|
|
jmp ObjVectorTargetDirMetLong
|
|
|
|
|
|
AntiClockwiseNoZeroCross:
|
|
sec
|
|
sbc.b TempBuffer+4 ;substract speed value
|
|
bcc AntiClockwiseTargetMet ;target met if angle wraps around through 0.
|
|
cmp.b TempBuffer+2 ;compare target to new angle
|
|
bcc AntiClockwiseTargetMet ;target met if new angle is smaller than old
|
|
bra AntiClockwiseTargetNotMet
|
|
|
|
ObjVectorRotateClockwise:
|
|
|
|
;rotate clockwise to meet target dir
|
|
lda.b TempBuffer ;get current angle
|
|
cmp.b TempBuffer+2 ;is target angle smaller? (=must 0 be crossed to reach target?)
|
|
bcc ClockwiseNoZeroCross
|
|
|
|
;must cross 0 to reach target
|
|
clc
|
|
adc.b TempBuffer+4 ;add speed value
|
|
bcc AntiClockwiseTargetNotMet ;if 0 wasn't passed, target value couldn't have possibly been reached
|
|
cmp.b TempBuffer+2 ;compare target to new angle
|
|
bcs AntiClockwiseTargetMet ;target met if new angle is bigger than old
|
|
bra AntiClockwiseTargetNotMet
|
|
|
|
|
|
|
|
ClockwiseNoZeroCross:
|
|
clc
|
|
adc.b TempBuffer+4 ;add speed value
|
|
bcs AntiClockwiseTargetMet ;target met if angle wraps around through 0.
|
|
cmp.b TempBuffer+2 ;compare target to new angle
|
|
bcs AntiClockwiseTargetMet ;target met if new angle is bigger than old
|
|
bra AntiClockwiseTargetNotMet
|
|
|
|
AntiClockwiseTargetMet:
|
|
lda.b TempBuffer+2 ;target met, set target without subpixel as new angle
|
|
and.w #%1111110000000000
|
|
lsr a
|
|
lsr a
|
|
ora.w #$8000 ;set "target met" flag
|
|
bra AntiClockwiseTargetWrite
|
|
|
|
AntiClockwiseTargetNotMet:
|
|
and.w #%1111111111100000 ;angle with subpixel precision
|
|
lsr a
|
|
lsr a
|
|
AntiClockwiseTargetWrite:
|
|
|
|
sta.b TempBuffer ;save new angle
|
|
sep #$20
|
|
lda.w ObjEntryVectorDir,x
|
|
and.b #%01000000
|
|
ora.b TempBuffer+1 ;save back new direction
|
|
sta.w ObjEntryVectorDir,x
|
|
|
|
lda.w ObjEntryVectorTurnSpeed,x
|
|
and.b #%111
|
|
ora.b TempBuffer
|
|
sta.w ObjEntryVectorTurnSpeed,x
|
|
|
|
|
|
|
|
ObjVectorTargetDirMetLong:
|
|
rep #$31 ;get current position in vector angle, 16 max
|
|
lda.w ObjEntryType2,x
|
|
and.w #$f
|
|
sta.b TempBuffer
|
|
lda.w ObjEntryVectorTurnSpeed,x ;get subpixel precision for later usage
|
|
and.w #%111
|
|
sta.b TempBuffer+2
|
|
lda.w ObjEntryVectorSpeed,x
|
|
and.w #%111111
|
|
pha
|
|
lda.w ObjEntryVectorDir,x ;get direction, multiply with 32, add current position in angle
|
|
and.w #%111111
|
|
asl a
|
|
asl a
|
|
asl a
|
|
asl a
|
|
asl a
|
|
adc.b TempBuffer
|
|
clc
|
|
adc.w #VectorAngleCodeLUT
|
|
tax ;source offset
|
|
ldy.w #VectorAngleSMCode&$ffff ;target offset
|
|
|
|
pla ;get vector speed
|
|
clc
|
|
adc.b TempBuffer+2 ;add sub-pixel buffer
|
|
pha
|
|
and.w #%111
|
|
sta.b TempBuffer+2 ;save back for next frame
|
|
|
|
pla
|
|
lsr a ;remove sub-pixel precision
|
|
lsr a
|
|
lsr a
|
|
and.w #%111
|
|
pha
|
|
beq VectorAngleNoSpeed ;triggers when vector move distance is lower than 1 and prevents ram thrashing.
|
|
|
|
dec a ;decrease length by one for mvn
|
|
|
|
.db $54 ;MVN
|
|
.db $7e ;destination bank
|
|
.db :VectorAngleCodeLUT+$c0 ;target bank
|
|
|
|
lda.w #$6b ;rtl opcode
|
|
sta.w $0,y ;store after sm-code. y conveniently points to the byte after the last one transferred by MVN.
|
|
|
|
ldx.b ObjectListPointerCurrent
|
|
lda.w ObjEntryYPos,x
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
|
|
tay
|
|
lda.w ObjEntryXPos,x
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
phx
|
|
tax
|
|
jsl VectorAngleSMCode
|
|
|
|
txa
|
|
plx
|
|
asl a ;add subpixel precision
|
|
asl a
|
|
asl a
|
|
asl a
|
|
sta.w ObjEntryXPos,x
|
|
tya
|
|
asl a ;add subpixel precision
|
|
asl a
|
|
asl a
|
|
asl a
|
|
sta.w ObjEntryYPos,x
|
|
|
|
VectorAngleNoSpeed:
|
|
pla ;update vector angle position
|
|
clc
|
|
adc.b TempBuffer
|
|
and.w #$f
|
|
sta.b TempBuffer
|
|
|
|
sep #$20
|
|
|
|
|
|
ldx.b ObjectListPointerCurrent
|
|
|
|
lda.w ObjEntryType2,x ;update vector angle position
|
|
and.b #%11110000
|
|
ora.b TempBuffer
|
|
sta.w ObjEntryType2,x
|
|
|
|
lda.w ObjEntryVectorTurnSpeed,x ;update vector speed sub-pixel position
|
|
and.b #%11111000
|
|
ora.b TempBuffer+2
|
|
sta.w ObjEntryVectorTurnSpeed,x
|
|
|
|
|
|
plp
|
|
rts
|
|
|
|
|
|
ObjVectorSpeedChangeModeLUT:
|
|
.dw ObjVectorSpeedChangeDirect
|
|
.dw ObjVectorSpeedChangeLinearSlow
|
|
.dw ObjVectorSpeedChangeLinearFast
|
|
.dw ObjVectorSpeedChangeLinearSmooth
|
|
|
|
ObjVectorSpeedChangeDirect:
|
|
lda.b #$40
|
|
rts
|
|
|
|
ObjVectorSpeedChangeLinearSlow:
|
|
lda.b #1 ;smallest possible unit, takes 64 frames max to reach target speed
|
|
rts
|
|
|
|
ObjVectorSpeedChangeLinearFast:
|
|
lda.b #8 ;one full unit, takes 8 frames max to reach target speed
|
|
rts
|
|
|
|
|
|
;smooth: divide the difference between target and current speed by two and use that to alter the current speed
|
|
ObjVectorSpeedChangeLinearSmooth:
|
|
lda.b TempBuffer+1
|
|
sec
|
|
sbc.b TempBuffer ;check if target speed is bigger than actual speed
|
|
bcc ObjVectorTarSpeedBiggerSmooth
|
|
|
|
bra ObjVectorTarSpeedSmooth
|
|
|
|
|
|
ObjVectorTarSpeedBiggerSmooth:
|
|
lda.b TempBuffer
|
|
sec
|
|
sbc.b TempBuffer+1
|
|
|
|
ObjVectorTarSpeedSmooth:
|
|
lsr a
|
|
bne ObjVectorTarSpeedMinOKSmooth
|
|
|
|
lda.b #1 ;minimum speed: 1
|
|
rts
|
|
|
|
ObjVectorTarSpeedMinOKSmooth:
|
|
cmp.b #8
|
|
bcc ObjVectorTarSpeedMaxOKSmooth
|
|
|
|
lda.b #8 ;maximum speed: 8
|
|
ObjVectorTarSpeedMaxOKSmooth:
|
|
rts
|
|
|
|
|
|
ObjVectorDirChangeModeLUT:
|
|
.dw ObjVectorDirChangeDirect
|
|
.dw ObjVectorDirChangeLinearSlow
|
|
.dw ObjVectorDirChangeLinearFast
|
|
.dw ObjVectorDirChangeLinearSmooth
|
|
|
|
|
|
ObjVectorDirChangeDirect:
|
|
lda.w #$fc00
|
|
rts
|
|
|
|
ObjVectorDirChangeLinearSlow:
|
|
lda.w #$20 ;smallest possible unit, takes 64 frames max to reach target speed
|
|
rts
|
|
|
|
ObjVectorDirChangeLinearFast:
|
|
lda.w #$400 ;one full unit, takes 8 frames max to reach target speed
|
|
rts
|
|
|
|
|
|
;smooth: divide the difference between target and current speed by two and use that to alter the current speed
|
|
ObjVectorDirChangeLinearSmooth:
|
|
lda.b TempBuffer+2
|
|
sec
|
|
sbc.b TempBuffer ;check if target speed is bigger than actual speed
|
|
bcc ObjVectorDirTarSpeedBiggerSmooth
|
|
|
|
bra ObjVectorDirTarSpeedSmooth
|
|
|
|
|
|
ObjVectorDirTarSpeedBiggerSmooth:
|
|
lda.b TempBuffer
|
|
sec
|
|
sbc.b TempBuffer+2
|
|
|
|
ObjVectorDirTarSpeedSmooth:
|
|
lsr a
|
|
cmp.w #$20 ;smaller than minimum speed?
|
|
bcs ObjVectorDirTarSpeedMinOKSmooth
|
|
|
|
lda.w #$20 ;minimum speed: 1
|
|
rts
|
|
|
|
ObjVectorDirTarSpeedMinOKSmooth:
|
|
cmp.w #$400
|
|
bcc ObjVectorDirTarSpeedMaxOKSmooth
|
|
|
|
lda.w #$400 ;maximum speed: 8
|
|
ObjVectorDirTarSpeedMaxOKSmooth:
|
|
rts
|
|
|
|
OamSubCreateParticles:
|
|
;create a couple of particles, position and palette same as victim:
|
|
;input: x=pointer to object that is emitting the particles
|
|
; a=number to add to the random number(0-3) of particles to generate. (valid ranges:0-3)
|
|
;modifies:paltemp
|
|
;calls: createobject (uses TempBuffers), updates random a couple of times
|
|
php
|
|
rep #$31
|
|
phx
|
|
sep #$20
|
|
pha
|
|
lda.b R4
|
|
and.b #%11
|
|
sta.b PalTemp
|
|
pla
|
|
and.b #%11
|
|
clc
|
|
adc.b PalTemp
|
|
beq ParticleCreateExit
|
|
sta.b PalTemp
|
|
|
|
CreateParticleLoop:
|
|
rep #$31
|
|
lda.w ObjEntryYPos,x
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
and.w #$ff
|
|
xba
|
|
sta.b TempBuffer
|
|
|
|
|
|
lda.w ObjEntryXDisplacement,x
|
|
and.w #$ff
|
|
sta.b TempBuffer+2
|
|
|
|
lda.w ObjEntryXPos,x
|
|
lsr a ;remove subpixel precision
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
sec
|
|
sbc.w #10
|
|
|
|
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
inc a
|
|
and.w #$ff
|
|
ora.b TempBuffer
|
|
tax
|
|
lda.w #54
|
|
jsr CreateObjectPosition
|
|
sep #$20
|
|
|
|
plx
|
|
lda.w ObjEntryPalConf,x
|
|
and.b #%1110
|
|
sta.b TempBuffer
|
|
phx
|
|
ldx.b ObjectListPointerCurrent
|
|
lda.w ObjEntryPalConf,x
|
|
and.b #%11110001
|
|
ora.b TempBuffer ;store palette of player for this particle
|
|
sta.w ObjEntryPalConf,x
|
|
jsr Random ;update random numbers so that all particles look different
|
|
jsr Random ;update random numbers so that all particles look different
|
|
dec.b PalTemp
|
|
bne CreateParticleLoop
|
|
|
|
ParticleCreateExit:
|
|
plx
|
|
plp
|
|
rts
|
|
|
|
ObjProcUpdateGravity:
|
|
lda.w ObjEntryLifeCounter,x
|
|
and.w #$ff
|
|
beq ObjProcGravityKill
|
|
|
|
dec.w ObjEntryLifeCounter,x ;decrease life counter
|
|
lda.w ObjEntryXSpeed,x ;add speed x-dir
|
|
and.w #$ff
|
|
clc
|
|
adc.w #$ff80 ;align in middle so that x-speed can be both positive and negative
|
|
clc
|
|
adc.w ObjEntryXPos,x
|
|
sta.w ObjEntryXPos,x
|
|
|
|
lda.w ObjEntryGravity,x
|
|
and.w #$ff
|
|
clc
|
|
adc.w ObjEntryYSpeed,x
|
|
sta.w ObjEntryYSpeed,x
|
|
clc
|
|
adc.w ObjEntryYPos,x
|
|
sta.w ObjEntryYPos,x
|
|
lsr a ;remove subpixel prec.
|
|
lsr a
|
|
lsr a
|
|
lsr a
|
|
cmp.w GravityCutOffYPos ;check if object is too low and should be deleted
|
|
bcs ObjProcGravityKill
|
|
rts
|
|
|
|
ObjProcGravityKill:
|
|
dec.w GravObjectCounter
|
|
stz.w ObjEntryType,x ;kill object if life is up
|
|
rts
|
|
|
|
|
|
ObjectProcessGravObjInc:
|
|
php
|
|
sep #$20
|
|
lda.w GravObjectCounter
|
|
cmp.w MaxGravObjCount ;number bigger than max count?
|
|
bcc ObjectProcessGravObjNoDel
|
|
|
|
stz.w ObjEntryType,x ;delete object if count is too high
|
|
stz.w ObjEntryType2,x
|
|
plp
|
|
rts
|
|
|
|
ObjectProcessGravObjNoDel:
|
|
inc.w GravObjectCounter
|
|
plp
|
|
rts
|
|
|