David Voswinkel a7270acdf1 o add monitor
2009-07-20 19:05:40 +02:00

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