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
Wednesday, March 27, 2013
Monday, March 25, 2013
MetaSprite Routine Updated
001 .proc evalMetaSprites
002
003 ; metasprite Object in RAM format, one record:
004 ; should be organized as one array of each element
005 ; (example, if there are $10 records, the first $10 bytes are all Ypos
006
007 ; Ypos : byte
008 ; Attribute modifier : byte
009
010 ; vhbcxypp --- Attribute modifier byte (always)
011 ; ||||||||
012 ; ||||||++---- Sprite Colour toggle: EOR with metatile attribs
013 ; |||||+------ Y clipping bit
014 ; ||||+------- X clipping bit
015 ; |||+-------- Clipping or wrap : 0: clip sprites at screen edge, 1: allow wrap
016 ; ||+--------- Background priority bit
017 ; |+---------- Horizontal flipping toggle: EOR metatile attribs
018 ; +----------- Vertical flipping toggle: EOR metatile attribs
019
020 ; Metatile number : byte
021 ; Xpos : byte
022
023 ; --------------------------------------------------------------------------------------------
024
025 ; metasprite format to define metasprite in ROM data:
026
027 ; xxxxxxxx --- Sprite X coordinate (if required)
028 ; yyyyyyyy --- Sprite Y coordinate (if required)
029
030 ; tttttttt --- Tile number (if required)
031
032 ; vhytxxpp --- Attribute byte (always)
033 ; ||||||||
034 ; ||||||++---- Sprite Colour
035 ; |||||+------ Tile number of next sprite (0: required, 1 : Tile number of this sprite + 1, skip if X control was LF)
036 ; ||||+------- Y position of next sprite (0 : required, 0 : unchanged)
037 ; ||++-------- X position of next sprite (01 : required, 00 : X pos of this sprite + 8,
038 ; || 10 : X pos of the first sprite, Y + 8 (CR/LF), 11 : Escape code for last sprite)
039 ; |+---------- Horizontal flipping
040 ; +----------- Vertical flipping
041
042 ; static
043 declareBSS
044 OAMoffset .byte
045 endBSS
046
047 ; temp use zeropage:
048
049 locals
050 sprModifier .byte
051 sprAttrib .byte
052 sprTile .byte
053 sprXpos .byte
054 sprYpos .byte
055
056 sprXbit8 .byte
057 sprYbit8 .byte
058
059 metaspritePointerLo .byte
060 metaspritePointerHi .byte
061
062 sprAdder .byte
063 sprXposStart .byte
064 mSpriteOffset .byte
065 endlocals
066
067 metaspritePointer = local::metaspritePointerLo
068
069 ; init:
070 ldy #::METASPRITE_COUNT
071 sty local::mSpriteOffset
072
073
074 ; skip reserved sprites
075 if lda M::OAMoffset < #::OAM_ROTATION_START
076 lda #::OAM_ROTATION_START
077 endif
078
079 sta M::OAMoffset
080 sta local::sprAdder ; use as temp: save OAMoffset
081
082 DoNextmetasprite:
083
084
085 ; loop through all entries and skip those with att = $FF
086 ; jump to OAM rotation and leave if no more entries
087 ldx local::mSpriteOffset
088
089 repeat
090
091 if dex == negative
092 jmp clearRemainingOAM
093 endif
094
095 mb a, local::sprModifier := mSpriteMemory[ x ]::att ; load Attrib/meta data which is modifier for all
096
097 until tay : iny != zero ; same as: until mSpriteMemory[ x ]::att <> #$FF
098
099 stx local::mSpriteOffset
100
101 mb local::sprYpos := mSpriteMemory[ x ]::yPos
102 mb local::sprXpos := mSpriteMemory[ x ]::xPos
103 sta local::sprXposStart
104
105 mb a, x := mSpriteMemory[ x ]::mTile ; load metatile number
106
107 ; get metatile pointer
108 mb local::metaspritePointerLo := metaspriteNumbersLo[ x ]
109 mb local::metaspritePointerHi := metaspriteNumbersHi[ x ]
110
111 ; from here, Y indexes into ROM data
112 ; first data is Xwidth, Yheight, Tile, and Attribs/command byte
113
114 ldy #0
115
116 ; --------- process width:
117
118 bit local::sprModifier ; copy b7 and b6 into N, V
119
120 if V set ; H flipping:
121 mb local::sprXpos := local::sprXpos + (metaspritePointer)[ y ] ; add width ( is stored 8 less than actual )
122 sta local::sprXposStart
123 if carry set
124 mb local::sprModifier := local::sprModifier ^ # ( 1 << OFFSCREEN_XBIT )
125 endif
126 endif
127
128 iny
129
130 ; --------- end width
131 ; --------- process height:
132
133 if lda local::sprModifier == N set ; V flipping:
134 mb local::sprYpos := local::sprYpos + (metaspritePointer)[ y ] ; add height ( is stored 8 less than actual )
135 lda local::sprModifier
136 if carry set
137 mb a := a ^ #( 1 << OFFSCREEN_YBIT )
138 endif
139 endif
140
141 iny
142
143 ; --------- end height:
144
145 ; set offscreen bits:
146 sta local::sprModifier
147
148 mb local::sprYbit8 := a >> 2 ; put Y 'bit8' into bit 0 local var (only bit0 matters)
149 mb local::sprXbit8 := a >> 1 ; put X 'bit8' into bit 0 local var (only bit0 matters)
150
151 ; ----------------
152
153 mb local::sprTile := (metaspritePointer)[ y ]
154 iny
155
156 ldx local::sprAdder ; X now has OAMoffset
157
158 DoNextTile:
159
160 mb local::sprAttrib := (metaspritePointer)[ y ] ; read attrib and flags
161 iny
162
163 ; if both offscreen bits are clear OR No clipping mode is on
164 if local::sprXbit8 | local::sprYbit8 >> 1 == carry clear || local::sprModifier & #%00010000
165
166 mb OAM_SHADOW[ x + 2] := local::sprAttrib & #%11000011 ^ local::sprModifier
167 mb OAM_SHADOW[ x + 3] := local::sprXpos
168 mb OAM_SHADOW[ x + 0] := local::sprYpos
169 mb OAM_SHADOW[ x + 1] := local::sprTile
170
171 if x := x + 4 == zero
172 ldx #::OAM_ROTATION_START
173 endif
174
175 endif
176
177 sec ; sec carry to end making reg A hold #8
178 mb a := local::sprAttrib & #$F0
179 rol
180 asl
181 asl ; test bit 3, 2 as C, N
182
183 ; if X MODIFIER value is 00 or 01:
184 if C clear
185 asl ; A will have #8
186 if C set ; X MODIFIER IS 01
187 mb a := (metaspritePointer)[ y ]
188 iny
189 clc
190 endif
191
192 ; HERE. either way, carry clear
193
194 if { flagSet local::sprModifier, 6 } ; if bit6 (h mirror)
195 ; negate adder
196 mb a := a ^ #$FF
197 sec
198 endif
199
200 sta local::sprAdder
201
202 mb local::sprXpos := a +c local::sprXpos
203
204 ror
205 if a ^ local::sprAdder == N set ; if carry and adder sign are different:
206 inc local::sprXbit8 ; toggle offscreen bit
207 endif
208
209 else
210 if N clear ; X MODIFIER IS 10 ; 9615 cycles
211
212 ; this idea takes the same # of cycles as the clearer code:
213 ; -------------------------------------------------------
214 ; asl ; reg A holds #8
215 ; and local::sprModifier
216 ; cmp #1 ; only bit8 could be set, copy it into C
217 ; rol local::sprXbit8
218
219 mb local::sprXbit8 := local::sprModifier >> 3
220 mb local::sprXpos := local::sprXposStart
221
222 lda #8
223 jmp Add8toYPos
224 endif
225 ; else: X MODIFIER IS 11:
226 stx local::sprAdder ; save OAM offset here
227 ;---------------
228 jmp DoNextmetasprite ; if = 0 then bail and do next RAm based metasprite
229
230 endif
231
232 ; -----------END X pos
233
234 ; ----------- Y pos
235
236 ; load Ypos? Skip all this if not 0
237 if ! local::sprAttrib & #%00001000
238
239 mb a := (metaspritePointer)[ y ]
240 iny
241
242 Add8toYPos: ; jump here if CR/LF code
243
244 clc
245 if flagSet local::sprModifier ; if bit7 (v mirror)
246 mb a := a ^ #$FF
247 sec
248 endif
249
250 sta local::sprAdder
251
252 mb local::sprYpos := a +c local::sprYpos
253
254 ror
255 if a ^ local::sprAdder == N set
256 inc local::sprYbit8
257 endif
258
259 endif
260 ; -----------END Y pos
261
262 ; -----------check tile:
263
264 ; BIT4: check if 0: load tile, else use next tile
265
266 inc local::sprTile ; inc by default
267
268 if ! local::sprAttrib & #%00000100
269 mb local::sprTile := (metaspritePointer)[ y ]
270 iny
271 endif
272
273 ; -------end tile processing
274
275 jmp DoNextTile
276
277 ; --------------------------------------------------------------------------------------------
278
279 clearRemainingOAM:
280
281 OAMnext = local::sprAdder ; alias
282
283 ; --------------------------------------------------------------------------------------------
284
285 ldx OAMnext ; x holds start of next blank spot
286
287 ; take away blank spaces from total slots if we wrapped
288 if OAMnext <= M::OAMoffset
289 mb a := OAMnext - #::OAM_ROTATION_START
290 endif
291
292 mb a := a - M::OAMoffset ; bytes used
293 mb a := a >> 2 ; slots used
294 sta OAMnext ; use as temp, since this value is in reg X
295
296 mb y := #(64 - ( ::OAM_ROTATION_START / 4 ) ) - OAMnext ; total avaliable slots MINUS slots used
297 lda #$FF
298
299 cpy #00
300 while not zero do
301
302 sta OAM_SHADOW, x
303 mb x := x + 4
304 if zero
305 ldx #::OAM_ROTATION_START
306 endif
307 dey
308 endwhile
309
310 mb M::OAMoffset := M::OAMoffset + #::OAM_ROTATION_AMOUNT
311
312 rts
313
314 .endproc
Subscribe to:
Posts (Atom)