001
002 .proc evalMetaSprites
003
004 ; metasprite Object in RAM format, one record:
005 ; should be organized as one array of each element
006 ; (example, if there are $10 records, the first $10 bytes are all Ypos
007
008 ; Ypos : byte
009 ; Attribute modifier : byte
010
011 ; vhbcxypp --- Attribute modifier byte
012 ; ||||||||
013 ; ||||||++---- Sprite Colour toggle: EOR with metatile attribs
014 ; |||||+------ Y clipping bit
015 ; ||||+------- X clipping bit
016 ; |||+-------- Clipping or wrap : 0: clip sprites at screen edge, 1: allow wrap
017 ; ||+--------- Background priority bit
018 ; |+---------- Horizontal flipping toggle: EOR metatile attribs
019 ; +----------- Vertical flipping toggle: EOR metatile attribs
020
021 ; Metatile number : byte
022 ; Xpos : byte
023
024 ; --------------------------------------------------------------------------------------------
025
026 ; metasprite format to define metasprite in ROM data:
027
028 ; xxxxxxxx --- Sprite X coordinate (if required)
029 ; yyyyyyyy --- Sprite Y coordinate (if required)
030
031 ; tttttttt --- Tile number (if required)
032
033 ; vhytxxpp --- Attribute byte (always)
034 ; ||||||||
035 ; ||||||++---- Sprite Colour
036 ; |||||+------ Tile number of next sprite (0: required, 1 : Tile number of this sprite + 1, skip if X control was LF)
037 ; ||||+------- Y position of next sprite (0 : required, 0 : unchanged)
038 ; ||++-------- X position of next sprite (01 : required, 00 : X pos of this sprite + 8,
039 ; || 10 : X pos of the first sprite, Y + 8 (CR/LF), 11 : Escape code for last sprite)
040 ; |+---------- Horizontal flipping
041 ; +----------- Vertical flipping
042
043 ; static
044 declareBSS
045 OAMoffset .byte
046 endBSS
047
048 ; notallOnScreen use zeropage:
049
050 locals
051 sprModifier .byte
052 sprAttrib .byte
053 sprTile .byte
054 sprXpos .byte
055 sprYpos .byte
056
057 sprXbit8 .byte
058 sprYbit8 .byte
059
060 metaspritePointerLo .byte
061 metaspritePointerHi .byte
062
063 sprAdder .byte
064 sprXposStart .byte
065 mSpriteOffset .byte
066 carryYplusHeight .byte
067 carryXplusWidth .byte
068 endlocals
069
070 notallOnScreen = local::sprAdder
071 saveOAMOffset = local::sprTile
072 metaspritePointer = local::metaspritePointerLo
073
074 ; init:
075 ldy #::METASPRITE_COUNT
076 sty local::mSpriteOffset
077
078
079 ; skip reserved sprites
080 if lda M::OAMoffset < #::OAM_ROTATION_START
081 lda #::OAM_ROTATION_START
082 endif
083
084 sta M::OAMoffset
085 sta saveOAMOffset ; use as notallOnScreen: save OAMoffset
086
087 DoNextmetasprite:
088
089
090 ; loop through all entries and skip those with att = $FF
091 ; jump to OAM rotation and leave if no more entries
092 ldx local::mSpriteOffset
093
094 repeat
095
096 if dex == negative
097 jmp clearRemainingOAM
098 endif
099
100 mb a, local::sprModifier := mSpriteMemory[ x ]::att ; load Attrib/meta data which is modifier for all
101
102 until tay : iny != zero ; same as: until mSpriteMemory[ x ]::att <> #$FF
103
104 mb local::sprYbit8 := a >> 2 ; put Y 'bit8' into bit 0 local var (only bit0 matters)
105 mb local::sprXbit8 := a >> 1 ; put X 'bit8' into bit 0 local var (only bit0 matters)
106
107 stx local::mSpriteOffset
108
109 mb local::sprYpos := mSpriteMemory[ x ]::yPos
110 mb local::sprXpos := mSpriteMemory[ x ]::xPos
111 sta local::sprXposStart
112
113 mb a, x := mSpriteMemory[ x ]::mTile ; load metatile number
114
115 ; get metatile pointer
116 mb local::metaspritePointerLo := metaspriteNumbersLo[ x ]
117 mb local::metaspritePointerHi := metaspriteNumbersHi[ x ]
118
119 ; first data from metatile string is Xwidth, Yheight, Tile, and Attribs/command byte
120
121 ldy #0
122 sty notallOnScreen
123
124 mb a := local::sprXpos + (metaspritePointer)[ y ] ; add width ( is stored 8 less than actual )
125 tax ; x has right side of object X coord
126 rol local::carryXplusWidth
127
128 iny
129 mb a := local::sprYpos + (metaspritePointer)[ y ] ; add height ( is stored 8 less than actual )
130 tay ; y has bottom of object Y coord
131 rol local::carryYplusHeight
132
133 ; ----------- check if all onscreen
134
135 if local::sprModifier & #010000 goto AllOnScreen
136
137 tya ; y has bottom of object Y coord
138 cmp #238
139 rol
140 ora local::sprXbit8
141 ora local::sprYbit8
142 ora local::carryXplusWidth
143 ora local::carryYplusHeight
144 and #1
145
146 if sta notallOnScreen == zero goto AllOnscreen
147
148 ; ------ end check all onscreen
149
150 ; ----------- check if all offscreen
151
152 ; check if all offscreen for X
153 lda local::carryXplusWidth
154 eor #1
155 and local::sprXbit8
156 and #1
157 bne DoNextmetasprite
158
159 ; check all offscreen for Y
160 lda local::carryYplusHeight
161 eor #1
162 and local::sprYbit8
163 and #1
164 bne DoNextmetasprite
165
166 ; check offscreen special case, bottom of screen area:
167 lda local::sprYpos
168 cmp #239
169 rol
170 eor #1 ; b0 = 0 if y > 238
171 ora local::sprYbit8 ; b0 still = 0 if sprYbit8 = 0
172 and #1
173 beq DoNextmetasprite
174
175 ; ------------ end check offscreen
176
177 AllOnscreen:
178
179 bit local::sprModifier ; copy b7 and b6 into N, V
180
181 if V set ; H flipping:
182 mb local::sprXpos := x ; x has right side of object X coord
183 mb local::sprXposStart := x
184
185 if ror local::carryXplusWidth == carry set
186 mb local::sprModifier := local::sprModifier ^ # ( 1 << OFFSCREEN_XBIT )
187 endif
188 endif
189
190 iny
191
192 ; --------- end width
193 ; --------- process height:
194
195 ror local::carryYplusHeight
196 if lda local::sprModifier == N set ; V flipping:
197 mb local::sprYpos := y ; ; y holds Ypos + height..add height ( is stored 8 less than actual )
198 if carry set
199 mb a := a ^ #( 1 << OFFSCREEN_YBIT )
200 endif
201 endif
202
203 sta local::sprModifier
204
205 mb local::sprYbit8 := a >> 2 ; put Y 'bit8' into bit 0 local var (only bit0 matters)
206 mb local::sprXbit8 := a >> 1 ; put X 'bit8' into bit 0 local var (only bit0 matters)
207
208 ; ---------------------------
209
210 ldx saveOAMOffset ; X now has OAMoffset
211
212 ldy #2 ; start at tile entry
213
214 mb local::sprTile := (metaspritePointer)[ y ]
215 iny
216
217 ; if all sprites are onscreen, jump to sped up routine:
218
219 if notallOnScreen == zero
220 jmp DrawFast
221 endif
222
223 DoNextTile:
224
225 mb local::sprAttrib := (metaspritePointer)[ y ] ; read attrib and flags
226 iny
227
228 ; if both offscreen bits are clear OR No clipping mode is on
229
230 if ( local::sprYpos < #239 && ( local::sprXbit8 | local::sprYbit8 >> 1 == carry clear ))
231
232 mb OAM_SHADOW[ x + 2] := local::sprAttrib & #%11000011 ^ local::sprModifier
233 mb OAM_SHADOW[ x + 3] := local::sprXpos
234 mb OAM_SHADOW[ x + 0] := local::sprYpos
235 mb OAM_SHADOW[ x + 1] := local::sprTile
236
237 if x := x + 4 == zero
238 ldx #::OAM_ROTATION_START
239 endif
240
241 endif
242
243 sec ; sec carry to end making reg A hold #8
244 mb a := local::sprAttrib & #$F0
245 rol
246 asl
247 asl ; test bit 3, 2 as C, N
248
249 ; if X MODIFIER value is 00 or 01:
250 if C clear
251 asl ; A will have #8
252 if C set ; X MODIFIER IS 01
253 mb a := (metaspritePointer)[ y ]
254 iny
255 clc
256 endif
257
258 ; HERE. either way, carry clear
259
260 if { flagSet local::sprModifier, 6 } ; if bit6 (h mirror)
261 ; negate adder
262 mb a := a ^ #$FF
263 sec
264 endif
265
266 sta local::sprAdder
267
268 mb local::sprXpos := a +c local::sprXpos
269
270 ror
271 if a ^ local::sprAdder == N set ; if carry and adder sign are different:
272 inc local::sprXbit8 ; toggle offscreen bit
273 endif
274
275 else
276 if N clear ; X MODIFIER IS 10
277
278 ; this idea takes the same # of cycles as the clearer code:
279 ; -------------------------------------------------------
280 ; asl ; reg A holds #8
281 ; and local::sprModifier
282 ; cmp #1 ; only bit8 could be set, copy it into C
283 ; rol local::sprXbit8
284
285 mb local::sprXbit8 := local::sprModifier >> 3
286 mb local::sprXpos := local::sprXposStart
287
288 lda #8
289 jmp Add8toYPos
290 endif
291 ; else: X MODIFIER IS 11:
292 stx saveOAMOffset ; save OAM offset here
293 ;---------------
294 jmp DoNextmetasprite ; if = 0 then bail and do next RAm based metasprite
295
296 endif
297
298 ; -----------END X pos
299
300 ; ----------- Y pos
301
302 ; load Ypos? Skip all this if not 0
303 if ! local::sprAttrib & #001000
304
305 mb a := (metaspritePointer)[ y ]
306 iny
307
308 Add8toYPos: ; jump here if CR/LF code
309
310 clc
311 if flagSet local::sprModifier ; if bit7 (v mirror)
312 mb a := a ^ #$FF
313 sec
314 endif
315
316 sta local::sprAdder
317 mb local::sprYpos := a +c local::sprYpos
318
319 ror
320 if a ^ local::sprAdder == N set
321 inc local::sprYbit8
322 endif
323
324 endif
325 ; -----------END Y pos
326
327 ; -----------check tile:
328
329 ; BIT4: check if 0: load tile, else use next tile
330
331 inc local::sprTile ; inc by default
332
333 if ! local::sprAttrib & #000100
334 mb local::sprTile := (metaspritePointer)[ y ]
335 iny
336 endif
337
338 ; -------end tile processing
339
340 jmp DoNextTile
341
342 ; super fast, no clipping version:
343
344 DrawFast:
345
346 mb local::sprAttrib := (metaspritePointer)[ y ] ; read attrib and flags
347 iny
348
349 mb OAM_SHADOW[ x + 2] := local::sprAttrib & #%11000011 ^ local::sprModifier
350 mb OAM_SHADOW[ x + 3] := local::sprXpos
351 mb OAM_SHADOW[ x + 0] := local::sprYpos
352 mb OAM_SHADOW[ x + 1] := local::sprTile
353
354 if x := x + 4 == zero
355 ldx #::OAM_ROTATION_START
356 endif
357
358 sec ; sec carry to end making reg A hold #8
359 mb a := local::sprAttrib & #$F0
360 rol
361 asl
362 asl ; test bit 3, 2 as C, N
363
364 ; if X MODIFIER value is 00 or 01:
365 if C clear
366 asl ; A will have #8
367 if C set ; X MODIFIER IS 01
368 mb a := (metaspritePointer)[ y ]
369 iny
370 clc
371 endif
372
373 ; HERE. either way, carry clear
374
375 if { flagSet local::sprModifier, 6 } ; if bit6 (h mirror)
376 ; negate adder
377 mb a := a ^ #$FF
378 sec
379 endif
380
381 mb local::sprXpos := a +c local::sprXpos
382
383 else
384 if N clear ; X MODIFIER IS 10
385 mb local::sprXpos := local::sprXposStart
386
387 lda #8
388 jmp Add8toYPosFast
389 endif
390 ; else: X MODIFIER IS 11:
391 stx saveOAMOffset ; save OAM offset here
392 ;---------------
393 jmp DoNextmetasprite ; if = 0 then bail and do next RAm based metasprite
394
395 endif
396
397 ; -----------END X pos
398
399 ; ----------- Y pos
400
401 ; load Ypos? Skip all this if not 0
402 if ! local::sprAttrib & #001000
403
404 mb a := (metaspritePointer)[ y ]
405 iny
406
407 Add8toYPosFast: ; jump here if CR/LF code
408
409 clc
410 if flagSet local::sprModifier ; if bit7 (v mirror)
411 mb a := a ^ #$FF
412 sec
413 endif
414
415 mb local::sprYpos := a +c local::sprYpos
416 endif
417 ; -----------END Y pos
418
419 ; -----------check tile:
420
421 ; BIT4: check if 0: load tile, else use next tile
422
423 inc local::sprTile ; inc by default
424
425 if ! local::sprAttrib & #000100
426 mb local::sprTile := (metaspritePointer)[ y ]
427 iny
428 endif
429
430 ; -------end tile processing
431
432 jmp DrawFast
433
434 ; --------------------------------------------------------------------------------------------
435
436 clearRemainingOAM:
437 OAMnext = saveOAMOffset ; alias
438
439 ; --------------------------------------------------------------------------------------------
440
441 ldx OAMnext ; x holds start of next blank spot
442
443 ; take away blank spaces from total slots if we wrapped
444 if OAMnext <= M::OAMoffset
445 mb a := OAMnext - #::OAM_ROTATION_START
446 endif
447
448 mb a := a - M::OAMoffset ; bytes used
449 mb a := a >> 2 ; slots used
450 sta OAMnext ; use as notallOnScreen, since this value is in reg X
451
452 mb y := #(64 - ( ::OAM_ROTATION_START / 4 ) ) - OAMnext ; total avaliable slots MINUS slots used
453 lda #$FF
454
455 cpy #00
456 while not zero do
457
458 sta OAM_SHADOW, x
459 mb x := x + 4
460 if zero
461 ldx #::OAM_ROTATION_START
462 endif
463 dey
464 endwhile
465
466 mb M::OAMoffset := M::OAMoffset + #::OAM_ROTATION_AMOUNT
467
468 rts
469
470 .endproc
No comments:
Post a Comment