/* 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