1 // Psikyo hardware sprites
2 #include "psikyo.h"
3
4 UINT8* PsikyoSpriteROM = NULL;
5 UINT8* PsikyoSpriteRAM = NULL;
6 UINT8* PsikyoSpriteLUT = NULL;
7
8 struct PsikyoSprite {
9 INT8 flip;
10 INT8 priority;
11 INT16 palette;
12 INT32 x; INT32 y;
13 INT32 xsize; INT32 ysize;
14 INT32 xzoom; INT32 yzoom;
15 INT32 address;
16 };
17
18 static INT32 nFrame;
19
20 static INT8* PsikyoSpriteAttrib = NULL;
21 static INT32 nSpriteAddressMask;
22
23 static PsikyoSprite* pSpriteLists = NULL;
24 static PsikyoSprite* pSpriteList = NULL;
25
26 static INT32* PsikyoZoomXTable = NULL;
27 static INT32* PsikyoZoomYTable = NULL;
28
29 static UINT8* pTile;
30 static UINT8* pTileData;
31 static UINT32* pTilePalette;
32
33 static UINT16* pZBuffer = NULL;
34 static UINT16* pZTile;
35
36 static INT32 *pXZoomInfo, *pYZoomInfo;
37
38 static INT32 nTileXPos, nTileYPos, nZPos, nTileXSize, nTileYSize;
39 static INT32 nXSize, nYSize;
40
41 static INT32 nFirstSprites[8], nLastSprites[8];
42 static INT32 *nFirstSprite, *nLastSprite;
43
44 static INT32 nTopSprite;
45 static INT32 nZOffset;
46
47 typedef void (*RenderSpriteFunction)();
48
49 // Include the tile rendering functions
50 #include "psikyo_sprite_func.h"
51
GetBuffers(INT32 nBuffer)52 static void GetBuffers(INT32 nBuffer)
53 {
54 pSpriteList = pSpriteLists + (nBuffer << 10);
55 nFirstSprite = nFirstSprites + (nBuffer << 2);
56 nLastSprite = nLastSprites + (nBuffer << 2);
57 }
58
PsikyoSpriteRender(INT32 nLowPriority,INT32 nHighPriority)59 INT32 PsikyoSpriteRender(INT32 nLowPriority, INT32 nHighPriority)
60 {
61 static INT32 nMaskLeft, nMaskRight, nMaskTop, nMaskBottom;
62 PsikyoSprite* pBuffer;
63 INT32 nRenderFunction;
64
65 UINT8* pStart;
66 UINT16* pZStart = NULL;
67
68 INT32 nAddress, nTransColour, nNextTileX, nNextTileY, nXZoom, nYZoom, nStartXPos;
69 INT32 nPriorityMask = 0;
70 INT32 nMaxZPos = -1;
71 INT32 nMinZPos = 0x00010000;
72 INT32 nUseBuffer = 0x00010000;
73 INT32 nCurrentZPos;
74
75 if ((nBurnLayer & 1) == 0) {
76 return 0;
77 }
78
79 if (nLowPriority == 0) {
80 nZPos = -1;
81 nTopSprite = -1;
82
83 nMaskLeft = nMaskTop = 9999;
84 nMaskRight = nMaskBottom = -1;
85
86 GetBuffers(nFrame ^ 1);
87 }
88
89 if (nHighPriority < 3) {
90 for (INT32 i = nHighPriority + 1; i < 4; i++) {
91 if (nUseBuffer > nFirstSprite[i]) {
92 nUseBuffer = nFirstSprite[i];
93 }
94 }
95 }
96
97 nRenderFunction = (BURN_ENDIAN_SWAP_INT16(((UINT16*)PsikyoSpriteRAM)[0x0FFF]) & 4) << 4;
98 nTransColour = (nRenderFunction & 64) ? 0 : 15;
99
100 for (INT32 i = nLowPriority; i <= nHighPriority; i++) {
101 if (nMinZPos > nFirstSprite[i]) {
102 nMinZPos = nFirstSprite[i];
103 }
104 if (nMaxZPos < nLastSprite[i]) {
105 nMaxZPos = nLastSprite[i];
106 }
107 nPriorityMask |= 1 << i;
108 }
109
110 nCurrentZPos = nMinZPos;
111
112 nPriorityMask &= nSpriteEnable;
113 if (nPriorityMask == 0) {
114 return 0;
115 }
116
117 for (pBuffer = pSpriteList + nCurrentZPos; nCurrentZPos <= nMaxZPos; pBuffer++, nCurrentZPos++) {
118
119 if ((pBuffer->priority & nPriorityMask) == 0) {
120 continue;
121 }
122
123 nTileXPos = pBuffer->x;
124 nTileYPos = pBuffer->y;
125
126 pTilePalette = PsikyoPalette + pBuffer->palette;
127
128 nXSize = pBuffer->xsize;
129 nYSize = pBuffer->ysize;
130
131 nRenderFunction &= 64;
132 nRenderFunction |= pBuffer->flip << 3;
133
134 nAddress = pBuffer->address;
135
136 switch (pBuffer->flip) {
137 case 1:
138 nNextTileX = -1;
139 nNextTileY = nXSize << 1;
140 nAddress -= nXSize + 1;
141 break;
142 case 2:
143 nNextTileX = 1;
144 nNextTileY = -nXSize << 1;
145 nAddress += (nYSize + 1) * nXSize;
146 break;
147 case 3:
148 nNextTileX = -1;
149 nNextTileY = 0;
150 nAddress += nYSize * nXSize - 1;
151 break;
152 default:
153 nNextTileX = 1;
154 nNextTileY = 0;
155 }
156
157 if (pBuffer->xzoom == 0 && pBuffer->yzoom == 0) { // This sprite doesn't use zooming
158
159 if (nTopSprite > nCurrentZPos) { // Test ZBuffer
160 if (nTileXPos < nMaskRight && (nTileXPos + nXSize * 16) >= nMaskLeft && nTileYPos < nMaskBottom && (nTileYPos + nYSize * 16) >= nMaskTop) {
161 nRenderFunction |= 2;
162 }
163 }
164
165 if (nUseBuffer < nCurrentZPos) { // Write ZBuffer
166 nRenderFunction |= 4;
167
168 if (nTileXPos < nMaskLeft) {
169 nMaskLeft = nTileXPos;
170 }
171 if ((nTileXPos + nXSize) > nMaskRight) {
172 nMaskRight = nTileXPos + nXSize * 16;
173 }
174 if (nTileYPos < nMaskTop) {
175 nMaskTop = nTileYPos;
176 }
177 if ((nTileYPos + nYSize) > nMaskBottom) {
178 nMaskBottom = nTileYPos + nYSize * 16;
179 }
180 }
181
182 if (nRenderFunction & 6) {
183 pZStart = pZBuffer + (nTileYPos * 320) + nTileXPos;
184 nZPos = nZOffset + nCurrentZPos;
185 }
186
187 pStart = pBurnDraw + (nTileYPos * nBurnPitch) + (nTileXPos * nBurnBpp);
188
189 for (INT32 y = 0; y < nYSize; y++, nTileYPos += 16, pStart += 16 * nBurnPitch, pZStart += 16 * 320) {
190 nTileXPos = pBuffer->x;
191
192 pTile = pStart;
193 pZTile = pZStart;
194
195 nAddress += nNextTileY;
196
197 for (INT32 x = 0; x < nXSize; x++, nTileXPos += 16, nAddress += nNextTileX, pTile += nBurnBpp << 4, pZTile += 16) {
198
199 if (PsikyoSpriteAttrib[BURN_ENDIAN_SWAP_INT16(((UINT16*)PsikyoSpriteLUT)[nAddress])] == nTransColour) {
200 continue;
201 }
202
203 pTileData = PsikyoSpriteROM + ((BURN_ENDIAN_SWAP_INT16(((UINT16*)PsikyoSpriteLUT)[nAddress]) << 8) & nSpriteAddressMask);
204 if (nTileYPos < 0 || nTileYPos > 208 || nTileXPos < 0 || nTileXPos > 304) {
205 if (nTileYPos > -16 && nTileYPos < 224 && nTileXPos > -16 && nTileXPos < 320) {
206 RenderSprite[nRenderFunction + 1]();
207 }
208 } else {
209 RenderSprite[nRenderFunction + 0]();
210 }
211 }
212 }
213 } else { // This sprite uses zooming
214 nXZoom = pBuffer->xzoom;
215 nYZoom = pBuffer->yzoom;
216
217 nTileXPos += (nXSize * nXZoom + 3) >> 2;
218 nTileYPos += (nYSize * nYZoom + 3) >> 2;
219
220 nXZoom = 32 - nXZoom;
221 nYZoom = 32 - nYZoom;
222
223 if (nTopSprite > nCurrentZPos) { // Test ZBuffer
224 if (nTileXPos < nMaskRight && (nTileXPos + nXSize * nXZoom / 2) >= nMaskLeft && nTileYPos < nMaskBottom && (nTileYPos + nYSize * nXZoom / 2) >= nMaskTop) {
225 nRenderFunction |= 2;
226 }
227 }
228
229 if (nUseBuffer < nCurrentZPos) { // Write ZBuffer
230 nRenderFunction |= 4;
231
232 if (nTileXPos < nMaskLeft) {
233 nMaskLeft = nTileXPos;
234 }
235 if ((nTileXPos + nXSize) > nMaskRight) {
236 nMaskRight = nTileXPos + nXSize * nXZoom / 2;
237 }
238 if (nTileYPos < nMaskTop) {
239 nMaskTop = nTileYPos;
240 }
241 if ((nTileYPos + nYSize) > nMaskBottom) {
242 nMaskBottom = nTileYPos + nYSize * nYZoom / 2;
243 }
244 }
245
246 if (nRenderFunction & 6) {
247 pZStart = pZBuffer + (nTileYPos * 320) + nTileXPos;
248 nZPos = nZOffset + nCurrentZPos;
249 }
250
251 pStart = pBurnDraw + (nTileYPos * nBurnPitch) + (nTileXPos * nBurnBpp);
252 nStartXPos = nTileXPos;
253
254 for (INT32 y = 0; y < nYSize; y++, nTileYPos += nTileYSize, pStart += nTileYSize * nBurnPitch, pZStart += nTileYSize * 320) {
255 nTileXPos = nStartXPos;
256
257 pTile = pStart;
258 pZTile = pZStart;
259
260 nAddress += nNextTileY;
261
262 nTileYSize = (y + 1) * nYZoom / 2 - (y * nYZoom / 2);
263 pYZoomInfo = PsikyoZoomYTable + (nTileYSize << 4);
264
265 for (INT32 x = 0; x < nXSize; x++, nTileXPos += nTileXSize, nAddress += nNextTileX, pTile += nBurnBpp * nTileXSize, pZTile += nTileXSize) {
266
267 nTileXSize = (x + 1) * nXZoom / 2 - (x * nXZoom / 2);
268
269 if (PsikyoSpriteAttrib[BURN_ENDIAN_SWAP_INT16(((UINT16*)PsikyoSpriteLUT)[nAddress])] == nTransColour) {
270 continue;
271 }
272
273 pXZoomInfo = PsikyoZoomXTable + (nTileXSize << 4);
274 pTileData = PsikyoSpriteROM + ((BURN_ENDIAN_SWAP_INT16(((UINT16*)PsikyoSpriteLUT)[nAddress]) << 8) & nSpriteAddressMask);
275 if (nTileYPos < 0 || nTileYPos > (224 - nTileYSize) || nTileXPos < 0 || nTileXPos > (320 - nTileXSize)) {
276 if (nTileYPos > -nTileYSize && nTileYPos < 224 && nTileXPos > -nTileXSize && nTileXPos < 320) {
277 RenderSprite[nRenderFunction + 33]();
278 }
279 } else {
280 RenderSprite[nRenderFunction + 32]();
281 }
282 }
283 }
284 }
285 }
286
287 if (nMaxZPos > nTopSprite) {
288 nTopSprite = nMaxZPos;
289 }
290
291 if (nHighPriority == 3) {
292 if (nZPos >= 0) {
293 nZOffset += nTopSprite;
294 if (nZOffset > 0xFC00) {
295 memset(pZBuffer, 0, 320 * 224 * sizeof(UINT16));
296 nZOffset = 0;
297 }
298 }
299 }
300
301 return 0;
302 }
303
PsikyoSpriteBuffer()304 INT32 PsikyoSpriteBuffer()
305 {
306 UINT16* pSprite;
307 PsikyoSprite* pBuffer;
308 INT32 nPriority;
309
310 UINT16 word;
311 INT32 x, y, xs, ys;
312
313 nFrame ^= 1;
314
315 GetBuffers(nFrame);
316
317 pBuffer = pSpriteList;
318
319 nFirstSprite[0] = 0x00010000;
320 nFirstSprite[1] = 0x00010000;
321 nFirstSprite[2] = 0x00010000;
322 nFirstSprite[3] = 0x00010000;
323
324 nLastSprite[0] = -1;
325 nLastSprite[1] = -1;
326 nLastSprite[2] = -1;
327 nLastSprite[3] = -1;
328
329 // Check if sprites are disabled
330 if (BURN_ENDIAN_SWAP_INT16(((UINT16*)PsikyoSpriteRAM)[0x0FFF]) & 1) {
331 return 0;
332 }
333
334 for (INT32 i = 0x0C00, z = 0; i < 0x0FFF; i++) {
335
336 word = BURN_ENDIAN_SWAP_INT16(((UINT16*)PsikyoSpriteRAM)[i]);
337
338 // Check for end-marker
339 if (word == 0xFFFF) {
340 break;
341 }
342
343 if (word >= 0x0300) {
344 continue;
345 }
346
347 // Point to sprite
348 pSprite = &(((UINT16*)PsikyoSpriteRAM)[word * 4]);
349
350 x = BURN_ENDIAN_SWAP_INT16(pSprite[1]) & 0x01FF;
351 y = BURN_ENDIAN_SWAP_INT16(pSprite[0]) & 0x01FF;
352
353 xs = ((BURN_ENDIAN_SWAP_INT16(pSprite[1]) >> 9) & 0x0007) + 1;
354 ys = ((BURN_ENDIAN_SWAP_INT16(pSprite[0]) >> 9) & 0x0007) + 1;
355
356 if (x >= 320) {
357 x -= 512;
358 if (x + (xs << 4) < 0) {
359 continue;
360 }
361 }
362 if (y >= 224) {
363 y -= 512;
364 if (y + (ys << 4) < 0) {
365 continue;
366 }
367 }
368
369 // Sprite is active and most likely on screen, so add it to the buffer
370
371 word = BURN_ENDIAN_SWAP_INT16(pSprite[2]);
372
373 nPriority = 3 - ((word >> 6) & 0x03);
374
375 if (nLastSprite[nPriority] == -1) {
376 nFirstSprite[nPriority] = z;
377 }
378 nLastSprite[nPriority] = z;
379
380 pBuffer->priority = 1 << nPriority;
381
382 pBuffer->xzoom = BURN_ENDIAN_SWAP_INT16(pSprite[1]) >> 12;
383 pBuffer->yzoom = BURN_ENDIAN_SWAP_INT16(pSprite[0]) >> 12;
384
385 pBuffer->xsize = xs;
386 pBuffer->ysize = ys;
387
388 pBuffer->x = x;
389 pBuffer->y = y;
390
391 pBuffer->flip = (word >> 14) & 0x03;
392 pBuffer->palette = (word >> 4) & 0x01F0;
393
394 pBuffer->address = BURN_ENDIAN_SWAP_INT16(pSprite[3]) | ((word & 1) << 16);
395
396 pBuffer++;
397 z++;
398 }
399
400 return 0;
401 }
402
PsikyoSpriteExit()403 void PsikyoSpriteExit()
404 {
405 BurnFree(PsikyoZoomXTable);
406 BurnFree(PsikyoZoomYTable);
407 BurnFree(PsikyoSpriteAttrib);
408 BurnFree(pSpriteLists);
409 BurnFree(pZBuffer);
410
411 return;
412 }
413
PsikyoSpriteInit(INT32 nROMSize)414 INT32 PsikyoSpriteInit(INT32 nROMSize)
415 {
416 const INT32 nTileSize = 256;
417 INT32 nNumTiles = nROMSize / nTileSize;
418
419 if (pSpriteLists) {
420 BurnFree(pSpriteLists);
421 }
422 pSpriteLists = (PsikyoSprite*)BurnMalloc(0x0800 * sizeof(PsikyoSprite));
423 if (pSpriteLists == NULL) {
424 PsikyoSpriteExit();
425 return 1;
426 }
427
428 for (INT32 i = 0; i < 8; i++) {
429 nFirstSprites[i] = 0x00010000;
430 nLastSprites[i] = -1;
431 }
432
433 if (pZBuffer) {
434 BurnFree(pZBuffer);
435 }
436 pZBuffer = (UINT16*)BurnMalloc(320 * 224 * sizeof(UINT16));
437 if (pZBuffer == NULL) {
438 PsikyoSpriteExit();
439 return 1;
440 }
441
442 memset(pZBuffer, 0, 320 * 224 * sizeof(UINT16));
443 nZOffset = 0;
444
445 for (nSpriteAddressMask = 1; nSpriteAddressMask < nROMSize; nSpriteAddressMask <<= 1) {}
446 nSpriteAddressMask--;
447
448 if (PsikyoSpriteAttrib) {
449 BurnFree(PsikyoSpriteAttrib);
450 }
451 PsikyoSpriteAttrib = (INT8*)BurnMalloc(nSpriteAddressMask + 1);
452 if (PsikyoSpriteAttrib == NULL) {
453 return 1;
454 }
455
456 for (INT32 i = 0; i < nNumTiles; i++) {
457 bool bTransparent0 = true;
458 bool bTransparent15 = true;
459 for (INT32 j = i * nTileSize; j < (i + 1) * nTileSize; j++) {
460 if (PsikyoSpriteROM[j] != 0x00) {
461 bTransparent0 = false;
462 if (!bTransparent15) {
463 break;
464 }
465 }
466 if (PsikyoSpriteROM[j] != 0xFF) {
467 bTransparent15 = false;
468 if (!bTransparent0) {
469 break;
470 }
471 }
472 }
473 PsikyoSpriteAttrib[i] = (INT8)0xFF;
474 if (bTransparent0) {
475 PsikyoSpriteAttrib[i] = 0;
476 }
477 if (bTransparent15) {
478 PsikyoSpriteAttrib[i] = 15;
479 }
480 }
481
482 for (INT32 i = nNumTiles; i <= nSpriteAddressMask; i++) {
483 PsikyoSpriteAttrib[i] = (INT8)0xFF;
484 }
485
486 PsikyoZoomXTable = (INT32*)BurnMalloc(272 * sizeof(INT32));
487 PsikyoZoomYTable = (INT32*)BurnMalloc(272 * sizeof(INT32));
488 if (PsikyoZoomXTable == NULL || PsikyoZoomYTable == NULL) {
489 PsikyoSpriteExit();
490 return 1;
491 }
492
493 memset(PsikyoZoomXTable, 0, 272 * sizeof(INT32));
494 memset(PsikyoZoomYTable, 0, 272 * sizeof(INT32));
495
496 for (INT32 z = 8; z < 16; z++) {
497 for (INT32 i = 0, x = 0; i < z; i++, x += 0x100000 / z) {
498 PsikyoZoomXTable[(z << 4) + i] = (x + 0x8000) >> 16;
499 }
500 for (INT32 i = 1; i < z; i++) {
501 PsikyoZoomYTable[(z << 4) + i - 1] = (PsikyoZoomXTable[(z << 4) + i] - PsikyoZoomXTable[(z << 4) + i - 1]) << 4;
502 // bprintf(PRINT_NORMAL, "%2i:%2i - ", PsikyoZoomXTable[(z << 4) + i - 1], PsikyoZoomYTable[(z << 4) + i - 1]);
503 }
504 PsikyoZoomYTable[(z << 4) + z - 1] = PsikyoZoomYTable[(z << 4)];
505 // bprintf(PRINT_NORMAL, "%2i:%2i\n", PsikyoZoomXTable[(z << 4) + z - 1], PsikyoZoomYTable[(z << 4) + z - 1]);
506 }
507 for (INT32 i = 0; i < 16; i++) {
508 PsikyoZoomXTable[256 + i] = i;
509 PsikyoZoomYTable[256 + i] = 16;
510 }
511
512 nFrame = 0;
513
514 return 0;
515 }
516