1 // license:BSD-3-Clause
2 // copyright-holders:Luca Elia, David Haywood
3 /* Kaneko Sprites */
4 
5 /*
6     Kaneko 16-bit sprites
7        VU-002 052 151021   ('type 0') (blazeon, berlwall etc., confirmed)
8        KC-002 L0002 023 9321EK702 ('type 1') (gtmr, bloodwar etc., confirmed)
9 
10 
11     [ 1024 Sprites ]
12 
13         Sprites are 16 x 16 x 4 in the older games, 16 x 16 x 8 in
14         gtmr & gtmr2.
15         Sprite type 0 also has a simple effect for keeping
16         sprites on the screen
17 
18 
19     Notes:
20      - Blaze On has 2 sprite chips, with 2 sets of sprite registers, the
21        existing code here just handles it as one, ignoring the second
22        set of registers, we should really be producing 2 sprite bitmaps
23        and manually mixing them.
24 
25 */
26 
27 #include "emu.h"
28 #include "kaneko_spr.h"
29 #include "screen.h"
30 
31 DEFINE_DEVICE_TYPE(KANEKO_VU002_SPRITE, kaneko_vu002_sprite_device, "kaneko_vu002", "Kaneko VU002 Sprites")
32 DEFINE_DEVICE_TYPE(KANEKO_KC002_SPRITE, kaneko_kc002_sprite_device, "kaneko_kc002", "Kaneko KC002 Sprites")
33 
kaneko16_sprite_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,u32 clock)34 kaneko16_sprite_device::kaneko16_sprite_device(
35 		const machine_config &mconfig,
36 		device_type type,
37 		const char *tag,
38 		device_t *owner,
39 		u32 clock)
40 	: device_t(mconfig, type, tag, owner, clock)
41 	, device_gfx_interface(mconfig, *this, nullptr)
42 	, device_video_interface(mconfig, *this)
43 	, m_gfx_region(*this, DEVICE_SELF)
44 	, m_colbase(0)
45 {
46 	m_keep_sprites = 0; // default disabled for games not using it
47 
48 
49 	m_sprite_xoffs = 0;
50 	m_sprite_yoffs = 0;
51 
52 	m_sprite_fliptype = 0;
53 /*
54     Sx = Sprites with priority x, x = tiles with priority x,
55     Sprites - Tiles Order (bottom -> top):
56 
57     0   S0  1   2   3
58     0   1   S1  2   3
59     0   1   2   S2  3
60     0   1   2   3   S3
61 */
62 	m_priority.sprite[0] = 1;   // above tile[0],   below the others
63 	m_priority.sprite[1] = 2;   // above tile[0-1], below the others
64 	m_priority.sprite[2] = 3;   // above tile[0-2], below the others
65 	m_priority.sprite[3] = 8;   // above all
66 }
67 
device_start()68 void kaneko16_sprite_device::device_start()
69 {
70 	m_first_sprite = std::make_unique<struct tempsprite_t[]>(0x400);
71 	m_sprites_regs = make_unique_clear<u16[]>(0x20/2);
72 	screen().register_screen_bitmap(m_sprites_bitmap);
73 	screen().register_screen_bitmap(m_sprites_maskmap);
74 
75 	save_item(NAME(m_sprite_flipx));
76 	save_item(NAME(m_sprite_flipy));
77 	save_pointer(NAME(m_sprites_regs), 0x20/2);
78 	save_item(NAME(m_keep_sprites));
79 	save_item(NAME(m_sprites_bitmap));
80 	save_item(NAME(m_sprites_maskmap));
81 }
82 
83 
device_start()84 void kaneko_vu002_sprite_device::device_start()
85 {
86 	/*
87 	    16x16x4 made of 4 8x8x4 blocks arrenged like:   01
88 	                                                    23
89 	*/
90 	gfx_layout layout_16x16x4 =
91 	{
92 		16,16,
93 		0,
94 		4,
95 		{ STEP4(0,1) },
96 		{ STEP8(8*8*4*0,4),   STEP8(8*8*4*1,4)   },
97 		{ STEP8(8*8*4*0,8*4), STEP8(8*8*4*2,8*4) },
98 		16*16*4
99 	};
100 	layout_16x16x4.total = m_gfx_region->bytes() / ((16*16*4) / 8);
101 	kaneko16_sprite_device::device_start();
102 	set_gfx(0, std::make_unique<gfx_element>(&palette(), layout_16x16x4, m_gfx_region->base(), 0, 0x40, m_colbase));
103 }
104 
105 
device_start()106 void kaneko_kc002_sprite_device::device_start()
107 {
108 	/*
109 	    16x16x8 made of 4 8x8x8 blocks arrenged like:   01
110 	                                                    23
111 	*/
112 	gfx_layout layout_16x16x8 =
113 	{
114 		16,16,
115 		0,
116 		8,
117 		{ STEP8(0,1) },
118 		{ STEP8(0,8),   STEP8(8*8*8*1,8)   },
119 		{ STEP8(0,8*8), STEP8(8*8*8*2,8*8) },
120 		16*16*8
121 	};
122 	layout_16x16x8.total = m_gfx_region->bytes() / ((16*16*8) / 8);
123 	kaneko16_sprite_device::device_start();
124 	set_gfx(0, std::make_unique<gfx_element>(&palette(), layout_16x16x8, m_gfx_region->base(), 0, 0x40, m_colbase));
125 }
126 
127 
device_reset()128 void kaneko16_sprite_device::device_reset()
129 {
130 	m_sprite_flipx = 0;
131 	m_sprite_flipy = 0;
132 }
133 
134 /***************************************************************************
135 
136                                 Sprites Drawing
137 
138     Sprite data is layed out in RAM in different ways for different games
139     (type 0,1). This basically involves the bits in the attribute
140     word to be shuffled around and/or the words being in different order.
141 
142     Each sprite is always stuffed in 4 words. There may be some extra
143     padding words though
144 
145     Examples are:
146 
147     Type 0: shogwarr, blazeon, bakubrkr.
148     Type 1: gtmr.
149 
150 Offset:         Format:                     Value:
151 
152 0000.w          Attribute (type 0 & 2)
153 
154                     f--- ---- ---- ----     Multisprite: Use Latched Code + 1
155                     -e-- ---- ---- ----     Multisprite: Use Latched Color (And Flip?)
156                     --d- ---- ---- ----     Multisprite: Use Latched X,Y As Offsets
157                     ---c b--- ---- ----     Index Of XY Offset
158                     ---- -a-- ---- ----
159                     ---- --9- ---- ----     High Priority (vs FG Tiles Of High Priority)
160                     ---- ---8 ---- ----     High Priority (vs BG Tiles Of High Priority)
161                     ---- ---- 7654 32--     Color
162                     ---- ---- ---- --1-     X Flip
163                     ---- ---- ---- ---0     Y Flip
164 
165                 Attribute (type 1)
166 
167                     f--- ---- ---- ----     Multisprite: Use Latched Code + 1
168                     -e-- ---- ---- ----     Multisprite: Use Latched Color (And Flip?)
169                     --d- ---- ---- ----     Multisprite: Use Latched X,Y As Offsets
170                     ---c b--- ---- ----     Index Of XY Offset
171                     ---- -a-- ---- ----
172                     ---- --9- ---- ----     X Flip
173                     ---- ---8 ---- ----     Y Flip
174                     ---- ---- 7--- ----     High Priority (vs FG Tiles Of High Priority)
175                     ---- ---- -6-- ----     High Priority (vs BG Tiles Of High Priority)
176                     ---- ---- --54 3210     Color
177 
178 0002.w                                      Code
179 0004.w                                      X Position << 6
180 0006.w                                      Y Position << 6
181 
182 ***************************************************************************/
183 
184 #define USE_LATCHED_XY      1
185 #define USE_LATCHED_CODE    2
186 #define USE_LATCHED_COLOR   4
187 
get_sprite_attributes(struct tempsprite_t * s,u16 attr)188 void kaneko_kc002_sprite_device::get_sprite_attributes(struct tempsprite_t *s, u16 attr)
189 {
190 	s->color    = (attr & 0x003f);
191 	s->priority = (attr & 0x00c0) >> 6;
192 	s->flipy    = (attr & 0x0100);
193 	s->flipx    = (attr & 0x0200);
194 	s->code    += (s->y & 1) << 16;   // bloodwar
195 }
196 
get_sprite_attributes(struct tempsprite_t * s,u16 attr)197 void kaneko_vu002_sprite_device::get_sprite_attributes(struct tempsprite_t *s, u16 attr)
198 {
199 	s->flipy    = (attr & 0x0001);
200 	s->flipx    = (attr & 0x0002);
201 	s->color    = (attr & 0x00fc) >> 2;
202 	s->priority = (attr & 0x0300) >> 8;
203 }
204 
205 
parse_sprite_type012(int i,struct tempsprite_t * s,u16 * spriteram16,int spriteram16_bytes)206 int kaneko16_sprite_device::parse_sprite_type012(int i, struct tempsprite_t *s, u16* spriteram16, int spriteram16_bytes)
207 {
208 	const int offs = i * 8 / 2;
209 
210 	if (offs >= (spriteram16_bytes / 2))  return -1;
211 
212 	const u16 attr = spriteram16[offs + 0];
213 	s->code        = spriteram16[offs + 1];
214 	s->x           = spriteram16[offs + 2];
215 	s->y           = spriteram16[offs + 3];
216 
217 	// this differs between each chip type
218 	get_sprite_attributes(s, attr);
219 
220 	const u16 xoffs = (attr & 0x1800) >> 11;
221 	s->yoffs        = m_sprites_regs[0x10 / 2 + xoffs * 2 + 1];
222 	s->xoffs        = m_sprites_regs[0x10 / 2 + xoffs * 2 + 0];
223 
224 	if (m_sprite_flipy)
225 	{
226 		s->yoffs -= m_sprites_regs[0x2/2];
227 		s->yoffs -= screen().visible_area().min_y << 6;
228 	}
229 	else
230 	{
231 		s->yoffs -= m_sprites_regs[0x2/2];
232 		s->yoffs += screen().visible_area().min_y << 6;
233 	}
234 
235 	return  ((attr & 0x2000) ? USE_LATCHED_XY    : 0) |
236 			((attr & 0x4000) ? USE_LATCHED_COLOR : 0) |
237 			((attr & 0x8000) ? USE_LATCHED_CODE  : 0) ;
238 }
239 
240 // custom function to draw a single sprite. needed to keep correct sprites - sprites and sprites - tilemaps priorities
241 
242 
draw_sprites_custom(const rectangle & clip,gfx_element * gfx,u32 code,u32 color,bool flipx,bool flipy,int sx,int sy,int priority)243 void kaneko16_sprite_device::draw_sprites_custom(const rectangle &clip,gfx_element *gfx,
244 		u32 code,u32 color,bool flipx,bool flipy,int sx,int sy,
245 		int priority)
246 {
247 	const pen_t pen_base = gfx->granularity() * (color % gfx->colors());
248 	const u8 *source_base = gfx->get_data(code % gfx->elements());
249 	int dx, dy;
250 
251 	int ex = sx+gfx->width();
252 	int ey = sy+gfx->height();
253 
254 	int x_index_base;
255 	int y_index;
256 
257 	if (flipx)
258 	{
259 		x_index_base = gfx->width() - 1;
260 		dx = -1;
261 	}
262 	else
263 	{
264 		x_index_base = 0;
265 		dx = 1;
266 	}
267 
268 	if (flipy)
269 	{
270 		y_index = gfx->height() - 1;
271 		dy = -1;
272 	}
273 	else
274 	{
275 		y_index = 0;
276 		dy = 1;
277 	}
278 
279 	if (sx < clip.min_x)
280 	{ /* clip left */
281 		int pixels = clip.min_x - sx;
282 		sx += pixels;
283 		x_index_base += pixels * dx;
284 	}
285 	if (sy < clip.min_y)
286 	{ /* clip top */
287 		int pixels = clip.min_y - sy;
288 		sy += pixels;
289 		y_index += pixels * dy;
290 	}
291 	/* NS 980211 - fixed incorrect clipping */
292 	if (ex > clip.max_x + 1)
293 	{ /* clip right */
294 		int pixels = ex-clip.max_x - 1;
295 		ex -= pixels;
296 	}
297 	if (ey > clip.max_y + 1)
298 	{ /* clip bottom */
299 		int pixels = ey-clip.max_y - 1;
300 		ey -= pixels;
301 	}
302 
303 	if (ex > sx)
304 	{ /* skip if inner loop doesn't draw anything */
305 		for (int y = sy; y < ey; y++)
306 		{
307 			u8 const *const source = source_base + y_index * gfx->rowbytes();
308 			u16 *const dest = &m_sprites_bitmap.pix(y);
309 			u8 *const pri = &m_sprites_maskmap.pix(y);
310 
311 			int x_index = x_index_base;
312 			for (int x = sx; x < ex; x++)
313 			{
314 				const u8 c = source[x_index];
315 				if (c != 0)
316 				{
317 					if (pri[x] == 0)
318 						dest[x] = ((pen_base + c) & 0x3fff) + ((priority & 3) << 14);
319 
320 					pri[x] = 0xff; // mark it "already drawn"
321 				}
322 				x_index += dx;
323 			}
324 			y_index += dy;
325 		}
326 	}
327 }
328 
329 
330 /* Build a list of sprites to display & draw them */
draw_sprites(const rectangle & cliprect,u16 * spriteram16,int spriteram16_bytes)331 void kaneko16_sprite_device::draw_sprites(const rectangle &cliprect, u16* spriteram16, int spriteram16_bytes)
332 {
333 	/* Sprites *must* be parsed from the first in RAM to the last,
334 	   because of the multisprite feature. But they *must* be drawn
335 	   from the last in RAM (frontmost) to the first in order to
336 	   cope with priorities using pdrawgfx.
337 
338 	   Hence we parse them from first to last and put the result
339 	   in a temp buffer, then draw the buffer's contents from last
340 	   to first. */
341 
342 	const int max =   (screen().width() > 0x100) ? (0x200 << 6) : (0x100 << 6);
343 
344 	int i = 0;
345 	struct tempsprite_t *s = m_first_sprite.get();
346 
347 	/* These values are latched from the last sprite. */
348 	int x           =   0;
349 	int y           =   0;
350 	int code        =   0;
351 	int color       =   0;
352 	int priority    =   0;
353 	int xoffs       =   0;
354 	int yoffs       =   0;
355 	int flipx       =   0;
356 	int flipy       =   0;
357 
358 	while (1)
359 	{
360 		int flags = parse_sprite_type012(i,s, spriteram16, spriteram16_bytes);
361 
362 		if (flags == -1)    // End of Sprites
363 			break;
364 
365 		if (flags & USE_LATCHED_CODE)
366 			s->code = ++code;   // Use the latched code + 1 ..
367 		else
368 			code = s->code;     // .. or latch this value
369 
370 		if (flags & USE_LATCHED_COLOR)
371 		{
372 			s->color    = color;
373 			s->priority = priority;
374 			s->xoffs    = xoffs;
375 			s->yoffs    = yoffs;
376 
377 			if (m_sprite_fliptype==0)
378 			{
379 				s->flipx = flipx;
380 				s->flipy = flipy;
381 			}
382 		}
383 		else
384 		{
385 			color    = s->color;
386 			priority = s->priority;
387 			xoffs    = s->xoffs;
388 			yoffs    = s->yoffs;
389 
390 			if (m_sprite_fliptype==0)
391 			{
392 				flipx = s->flipx;
393 				flipy = s->flipy;
394 			}
395 		}
396 
397 		// brap boys explicitly doesn't want the flip to be latched, maybe there is a different bit to enable that behavior?
398 		if (m_sprite_fliptype==1)
399 		{
400 			flipx = s->flipx;
401 			flipy = s->flipy;
402 		}
403 
404 		if (flags & USE_LATCHED_XY)
405 		{
406 			s->x += x;
407 			s->y += y;
408 		}
409 		// Always latch the latest result
410 		x = s->x;
411 		y = s->y;
412 
413 		/* We can now buffer this sprite */
414 
415 		s->x  = s->xoffs + s->x;
416 		s->y  = s->yoffs + s->y;
417 
418 		s->x += m_sprite_xoffs;
419 		s->y += m_sprite_yoffs;
420 
421 		if (m_sprite_flipx) { s->x = max - s->x - (16 << 6); s->flipx = !s->flipx; }
422 		if (m_sprite_flipy) { s->y = max - s->y - (16 << 6); s->flipy = !s->flipy; }
423 
424 		s->x  = ((s->x & 0x7fc0) - (s->x & 0x8000)) / 0x40;
425 		s->y  = ((s->y & 0x7fc0) - (s->y & 0x8000)) / 0x40;
426 
427 		i++;
428 		s++;
429 	}
430 
431 
432 	/* Let's finally draw the sprites we buffered, in reverse order
433 	   (for pdrawgfx) */
434 
435 	for (s--; s >= m_first_sprite.get(); s--)
436 	{
437 		draw_sprites_custom(
438 										cliprect,gfx(0),
439 										s->code,
440 										s->color,
441 										s->flipx, s->flipy,
442 										s->x, s->y,
443 										s->priority);
444 	}
445 }
446 
447 
448 
449 /***************************************************************************
450 
451 
452                             Sprites Registers
453 
454     Offset:         Format:                     Value:
455 
456     0000.w          f--- ---- ---- ----         Sprites Disable?? (see blazeon)
457                     -edc ba98 7654 3---
458                     ---- ---- ---- -2--         Keep sprites on screen (only sprites type 0?)
459                     ---- ---- ---- --1-         Flip X
460                     ---- ---- ---- ---0         Flip Y
461 
462     0002.w                                      Y Offset << 6 (Global)
463 
464 
465     0004..000e.w                                ?
466 
467 
468     0010.w                                      X Offset << 6 #0
469     0012.w                                      Y Offset << 6 #0
470 
471     0014.w                                      X Offset << 6 #1
472     0016.w                                      Y Offset << 6 #1
473 
474     0018.w                                      X Offset << 6 #2
475     001a.w                                      Y Offset << 6 #2
476 
477     001c.w                                      X Offset << 6 #3
478     001e.w                                      Y Offset << 6 #3
479 
480 ***************************************************************************/
481 
482 /*
483 [gtmr]
484 
485 Initial self test:
486 600000: 4BC0 94C0 4C40 94C0-0404 0002 0000 0000     (Layers 1 regs)
487 680000: 4BC0 94C0 4C40 94C0-1C1C 0002 0000 0000     (Layers 2 regs)
488 Race start:
489 600000: DC00 7D00 DC80 7D00-0404 0002 0000 0000     (Layers 1 regs)
490 680000: DC00 7D00 DC80 7D00-1C1C 0002 0000 0000     (Layers 2 regs)
491 
492 [gtmr]
493 700000: 0040 0000 0001 0180-0000 0000 0000 0000     (Sprites  regs)
494 700010: 0040 0000 0040 0000-0040 0000 2840 1E00     ; 1,0 .. a1,78
495                                                     ; a0*2=screenx/2
496                                                     ; 78*2=screeny/2
497 FLIP ON:
498 700000: 0043 FFC0 0001 0180-0000 0000 0000 0000     (Sprites  regs)
499 700010: 2FC0 4400 2FC0 4400-2FC0 4400 57C0 6200     ; bf,110 .. 15f,188
500                                                     ; 15f-bf=a0! 188-110=78!
501 
502 [berlwall]
503 600000: 48CC 03C0 0001 0100-0000 0000 0000 0000     (Sprites  regs)
504 600010: 0000 0000 0000 0000-0000 0000 0000 0000
505 FLIP ON:
506 600000: 48CF FC00 0001 0100-0000 0000 0000 0000     (Sprites  regs)
507 600010: 0000 0000 0000 0000-0000 0000 0000 0000
508 
509 [mgcrystl]
510 900000: 4FCC 0000 0040 00C0-xxxx 0001 0001 0001     (Sprites  regs)
511 900010: 0000 FC40 A000 9C40-1E00 1A40 0000 FC40
512 FLIP ON:
513 900000: 4FCF 0000 0040 00C0-xxxx 0001 0001 0001     (Sprites  regs)
514 900010: 0000 0400 A000 A400-1E00 2200 0000 0400     ; +1f<<6 on y
515 */
516 
regs_r(offs_t offset)517 u16 kaneko16_sprite_device::regs_r(offs_t offset)
518 {
519 	return m_sprites_regs[offset];
520 }
521 
regs_w(offs_t offset,u16 data,u16 mem_mask)522 void kaneko16_sprite_device::regs_w(offs_t offset, u16 data, u16 mem_mask)
523 {
524 	COMBINE_DATA(&m_sprites_regs[offset]);
525 	const u16 new_data  = m_sprites_regs[offset];
526 
527 	switch (offset)
528 	{
529 		case 0:
530 			if (ACCESSING_BITS_0_7)
531 			{
532 				m_sprite_flipx = new_data & 2;
533 				m_sprite_flipy = new_data & 1;
534 
535 				if (get_sprite_type() == 0)
536 					m_keep_sprites = ~new_data & 4;
537 			}
538 
539 			break;
540 	}
541 
542 //  logerror("%s : Warning, sprites reg %04X <- %04X\n",m_maincpu->pc(),offset*2,data);
543 }
544 
545 
copybitmap(bitmap_ind16 & bitmap,const rectangle & cliprect,bitmap_ind8 & priority_bitmap)546 void kaneko16_sprite_device::copybitmap(bitmap_ind16 &bitmap, const rectangle &cliprect, bitmap_ind8 &priority_bitmap) { copybitmap_common(bitmap, cliprect, priority_bitmap); }
copybitmap(bitmap_rgb32 & bitmap,const rectangle & cliprect,bitmap_ind8 & priority_bitmap)547 void kaneko16_sprite_device::copybitmap(bitmap_rgb32 &bitmap, const rectangle &cliprect, bitmap_ind8 &priority_bitmap) { copybitmap_common(bitmap, cliprect, priority_bitmap); }
548 
549 template<class BitmapClass>
copybitmap_common(BitmapClass & bitmap,const rectangle & cliprect,bitmap_ind8 & priority_bitmap)550 void kaneko16_sprite_device::copybitmap_common(BitmapClass &bitmap, const rectangle &cliprect, bitmap_ind8 &priority_bitmap)
551 {
552 	pen_t const *const pal = gfx(0)->palette().pens();
553 
554 	constexpr bool rgb = sizeof(typename BitmapClass::pixel_t) != 2;
555 
556 	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
557 	{
558 		typename BitmapClass::pixel_t *const dstbitmap = &bitmap.pix(y);
559 		u8 *const dstprimap = &priority_bitmap.pix(y);
560 		u16 *const srcbitmap = &m_sprites_bitmap.pix(y);
561 
562 		for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
563 		{
564 			const u16 pri = (srcbitmap[x] & 0xc000) >> 14;
565 			const u16 pix = srcbitmap[x] & 0x3fff;
566 			if (m_priority.sprite[pri] > dstprimap[x])
567 			{
568 				if (pix & 0x3fff)
569 				{
570 					if (!rgb) dstbitmap[x] = m_colbase + pix;
571 					else dstbitmap[x] = pal[m_colbase + pix];
572 				}
573 			}
574 		}
575 	}
576 }
577 
578 
render_sprites(const rectangle & cliprect,u16 * spriteram16,int spriteram16_bytes)579 void kaneko16_sprite_device::render_sprites(const rectangle &cliprect, u16* spriteram16, int spriteram16_bytes)
580 {
581 	/* Sprites last (rendered with pdrawgfx, so they can slip
582 	   in between the layers) */
583 
584 	m_sprites_maskmap.fill(0, cliprect);
585 	/* keep sprites on screen - used by mgcrystl when you get the first gem and it shows instructions */
586 	if (!m_keep_sprites)
587 		m_sprites_bitmap.fill(0, cliprect);
588 
589 	draw_sprites(cliprect, spriteram16, spriteram16_bytes);
590 }
591 
kaneko_vu002_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)592 kaneko_vu002_sprite_device::kaneko_vu002_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
593 	: kaneko16_sprite_device(mconfig, KANEKO_VU002_SPRITE, tag, owner, clock)
594 {
595 }
596 
kaneko_kc002_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)597 kaneko_kc002_sprite_device::kaneko_kc002_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
598 	: kaneko16_sprite_device(mconfig, KANEKO_KC002_SPRITE, tag, owner, clock)
599 {
600 }
601 
602 // this is a bootleg implementation, used by Gals Hustler and Zip Zap, the latter not really working at all well with the original
603 // link features (assuming the bad program roms aren't the cause)  it's clearly derived from this sprite system tho.
bootleg_draw_sprites(bitmap_ind16 & bitmap,const rectangle & cliprect,u16 * spriteram16,int spriteram16_bytes)604 void kaneko16_sprite_device::bootleg_draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect, u16* spriteram16, int spriteram16_bytes)
605 {
606 //  u16 *spriteram16 = m_spriteram;
607 	int sx = 0, sy = 0;
608 
609 	for (int offs = 0; offs < spriteram16_bytes / 2; offs += 4)
610 	{
611 		const u32 code   =  spriteram16[offs + 1] & 0x1fff;
612 		const u32 color  = (spriteram16[offs] & 0x003c) >> 2;
613 		const bool flipx =  spriteram16[offs] & 0x0002;
614 		const bool flipy =  spriteram16[offs] & 0x0001;
615 
616 		if ((spriteram16[offs] & 0x6000) == 0x6000) /* Link bits */
617 		{
618 			sx += spriteram16[offs + 2] >> 6;
619 			sy += spriteram16[offs + 3] >> 6;
620 		}
621 		else
622 		{
623 			sx = spriteram16[offs + 2] >> 6;
624 			sy = spriteram16[offs + 3] >> 6;
625 		}
626 
627 		sx = (sx & 0x1ff) - (sx & 0x200);
628 		sy = (sy & 0x1ff) - (sy & 0x200);
629 
630 		gfx(0)->transpen(bitmap,cliprect,
631 				code,
632 				color,
633 				flipx,flipy,
634 				sx,sy,0);
635 	}
636 }
637