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