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