1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4
5 Sega 16-bit sprite hardware
6
7 ***************************************************************************/
8
9 #include "emu.h"
10 #include "sega16sp.h"
11 #include "segaic16.h"
12
13
14
15 //****************************************************************************
16 // CONSTANTS
17 //****************************************************************************
18
19 // device type definition
20 DEFINE_DEVICE_TYPE(SEGA_HANGON_SPRITES, sega_hangon_sprite_device, "sega_hangon_sprite", "Sega Custom Sprites (Hang On)")
21 DEFINE_DEVICE_TYPE(SEGA_SHARRIER_SPRITES, sega_sharrier_sprite_device, "sega_sharrier_sprite", "Sega Custom Sprites (Space Harrier)")
22 DEFINE_DEVICE_TYPE(SEGA_OUTRUN_SPRITES, sega_outrun_sprite_device, "sega_outrun_sprite", "Sega Custom Sprites (Out Run)")
23 DEFINE_DEVICE_TYPE(SEGA_SYS16A_SPRITES, sega_sys16a_sprite_device, "sega_sys16a_sprite", "Sega System 16A Sprites")
24 DEFINE_DEVICE_TYPE(BOOTLEG_SYS16A_SPRITES, bootleg_sys16a_sprite_device, "bootleg_sys16a_sprite", "Sega System 16A Sprites (bootleg)")
25 DEFINE_DEVICE_TYPE(SEGA_SYS16B_SPRITES, sega_sys16b_sprite_device, "sega_sys16b_sprite", "Sega System 16B Sprites")
26 DEFINE_DEVICE_TYPE(SEGA_XBOARD_SPRITES, sega_xboard_sprite_device, "sega_xboard_sprite", "Sega X-Board Sprites")
27 DEFINE_DEVICE_TYPE(SEGA_YBOARD_SPRITES, sega_yboard_sprite_device, "sega_yboard_sprite", "Sega Y-Board Sprites")
28
29
30
31 //****************************************************************************
32 // DEVICE INTERFACE
33 //****************************************************************************
34
35 //-------------------------------------------------
36 // sega_16bit_sprite_device -- core constructor
37 //-------------------------------------------------
38
sega_16bit_sprite_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner)39 sega_16bit_sprite_device::sega_16bit_sprite_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner)
40 : sprite16_device_ind16(mconfig, type, tag, owner)
41 , m_flip(false)
42 {
43 // default to 1:1 bank mapping
44 for (int bank = 0; bank < ARRAY_LENGTH(m_bank); bank++)
45 m_bank[bank] = bank;
46 }
47
48
49 //-------------------------------------------------
50 // device_start -- device startup
51 //-------------------------------------------------
52
device_start()53 void sega_16bit_sprite_device::device_start()
54 {
55 // let the parent do its work
56 sprite16_device_ind16::device_start();
57
58 // save states
59 save_item(NAME(m_flip));
60 save_item(NAME(m_bank));
61 }
62
63
64 //-------------------------------------------------
65 // draw_write -- trigger a buffer flip
66 //-------------------------------------------------
67
draw_write(uint16_t data)68 void sega_16bit_sprite_device::draw_write(uint16_t data)
69 {
70 uint32_t *src = reinterpret_cast<uint32_t *>(spriteram());
71 uint32_t *dst = reinterpret_cast<uint32_t *>(buffer());
72
73 // swap the halves of the sprite RAM
74 for (int i = 0; i < spriteram_bytes()/4; i++)
75 {
76 uint32_t temp = *src;
77 *src++ = *dst;
78 *dst++ = temp;
79 }
80
81 // hack for thunderblade
82 *spriteram() = 0xffff;
83
84 // we will render the sprites when the video update happens
85 }
86
87
88
89 //****************************************************************************
90 // HANG ON-STYLE SPRITES
91 //****************************************************************************
92
93 //-------------------------------------------------
94 // sega_hangon_sprite_device -- constructor
95 //-------------------------------------------------
96
sega_hangon_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)97 sega_hangon_sprite_device::sega_hangon_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
98 : sega_16bit_sprite_device(mconfig, SEGA_HANGON_SPRITES, tag, owner)
99 , m_sprite_region_ptr(*this, DEVICE_SELF)
100 {
101 set_local_origin(189, -1);
102 }
103
104
105 //-------------------------------------------------
106 // draw -- render the sprites within the cliprect
107 //-------------------------------------------------
108
draw(bitmap_ind16 & bitmap,const rectangle & cliprect)109 void sega_hangon_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
110 {
111 //
112 // Hang On-style sprites
113 //
114 // Offs Bits Usage
115 // +0 bbbbbbbb -------- Bottom scanline of sprite - 1
116 // +0 -------- tttttttt Top scanline of sprite - 1
117 // +2 bbbb---- -------- Sprite bank
118 // +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
119 // +4 pppppppp pppppppp Signed 16-bit pitch value between scanlines
120 // +6 -ooooooo oooooooo Offset within selected sprite bank
121 // +6 f------- -------- Horizontal flip: read the data backwards if set
122 // +8 --cccccc -------- Sprite color palette
123 // +8 -------- zzzzzz-- Zoom factor
124 // +8 -------- ------pp Sprite priority
125 // +E dddddddd dddddddd Scratch space for current address
126 //
127 // Final bitmap format:
128 //
129 // ----pp-- -------- Sprite priority
130 // ------cc cccc---- Sprite color palette
131 // -------- ----llll 4-bit pixel data
132 //
133 // Special notes:
134 //
135 // There is an interaction between the horizonal flip bit and the offset.
136 // The offset is maintained as a 16-bit value, even though only the lower
137 // 15 bits are used for the address. The top bit is used to control flipping.
138 // This means that if the low 15 bits overflow during rendering, the sprite
139 // data will be read backwards after the overflow. This is important to
140 // emulate correctly as many games make use of this feature to render sprites
141 // at the beginning of a bank.
142 //
143
144 // render the sprites in order
145 const uint16_t *spritebase = &m_sprite_region_ptr[0];
146 uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x10000;
147 const uint8_t *zoom = memregion("zoom")->base();
148 uint16_t *ramend = spriteram() + spriteram_elements();
149 for (uint16_t *data = spriteram(); data < ramend; data += 8)
150 {
151 // fetch the bottom; stop when we get something out of range
152 int bottom = data[0] >> 8;
153 if (bottom > 0xf0)
154 break;
155
156 // extract remaining parameters
157 int top = data[0] & 0xff;
158 int bank = m_bank[(data[1] >> 12) & 0xf];
159 int xpos = data[1] & 0x1ff;
160 int pitch = int16_t(data[2]);
161 uint16_t addr = data[3];
162 int colpri = (((data[4] >> 8) & 0x3f) << 4) | (((data[4] >> 0) & 0x3) << 10);
163 int vzoom = (data[4] >> 2) & 0x3f;
164 int hzoom = vzoom << 1;
165
166 // initialize the end address to the start address
167 data[7] = addr;
168
169 // if top greater than/equal to bottom, or invalid bank, punt
170 if (top >= bottom || bank == 255)
171 continue;
172
173 // clamp to within the memory region size
174 if (numbanks)
175 bank %= numbanks;
176 const uint16_t *spritedata = spritebase + 0x8000 * bank;
177
178 // determine the starting zoom address and mask
179 int zaddr = (vzoom & 0x38) << 5;
180 int zmask = 1 << (vzoom & 7);
181
182 // loop from top to bottom
183 int minx = xpos;
184 int maxx = cliprect.min_x - 1;
185 int miny = cliprect.max_y + 1;
186 int maxy = cliprect.min_y - 1;
187 for (int y = top; y < bottom; y++)
188 {
189 // advance a row
190 addr += pitch;
191
192 // if the zoom bit says so, add pitch a second time
193 if (zoom[zaddr++] & zmask)
194 addr += pitch;
195
196 // skip drawing if not within the cliprect
197 if (y >= cliprect.min_y && y <= cliprect.max_y)
198 {
199 uint16_t *dest = &bitmap.pix(y);
200 int xacc = 0x00;
201 int x;
202
203 // note that the System 16A sprites have a design flaw that allows the address
204 // to carry into the flip flag, which is the topmost bit -- it is very important
205 // to emulate this as the games compensate for it
206
207 // non-flipped case
208 if (!(addr & 0x8000))
209 {
210 // start at the word before because we preincrement below
211 data[7] = addr - 1;
212 for (x = xpos; x <= cliprect.max_x; )
213 {
214 uint16_t pixels = spritedata[++data[7] & 0x7fff];
215
216 // draw four pixels
217 int pix;
218 pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
219 pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
220 pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
221 pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
222
223 // stop if the last pixel in the group was 0xf
224 if (pix == 15)
225 break;
226 }
227 }
228
229 // flipped case
230 else
231 {
232 // start at the word after because we predecrement below
233 data[7] = addr + 1;
234 for (x = xpos; x <= cliprect.max_x; )
235 {
236 uint16_t pixels = spritedata[--data[7] & 0x7fff];
237
238 // draw four pixels
239 int pix;
240 pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
241 pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
242 pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
243 pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
244
245 // stop if the last pixel in the group was 0xf
246 if (pix == 15)
247 break;
248 }
249 }
250
251 // update bounds
252 if (x > maxx) maxx = x;
253 if (y < miny) miny = y;
254 maxy = y;
255 }
256 }
257
258 // mark dirty
259 if (minx <= maxx && miny <= maxy)
260 mark_dirty(minx, maxx, miny, maxy);
261 }
262 }
263
264
265
266 //****************************************************************************
267 // SPACE HARRIER-STYLE SPRITES
268 //****************************************************************************
269
270 //-------------------------------------------------
271 // sega_sharrier_sprite_device -- constructor
272 //-------------------------------------------------
273
sega_sharrier_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)274 sega_sharrier_sprite_device::sega_sharrier_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
275 : sega_16bit_sprite_device(mconfig, SEGA_SHARRIER_SPRITES, tag, owner)
276 , m_sprite_region_ptr(*this, DEVICE_SELF)
277 {
278 set_local_origin(189, -1);
279 }
280
281
282 //-------------------------------------------------
283 // draw -- render the sprites within the cliprect
284 //-------------------------------------------------
285
draw(bitmap_ind16 & bitmap,const rectangle & cliprect)286 void sega_sharrier_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
287 {
288 //
289 // Space Harrier-style sprites
290 //
291 // Offs Bits Usage
292 // +0 bbbbbbbb -------- Bottom scanline of sprite - 1
293 // +0 -------- tttttttt Top scanline of sprite - 1
294 // +2 bbbb---- -------- Sprite bank
295 // +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
296 // +4 s------- -------- Sprite shadow disable (0=enable, 1=disable)
297 // +4 -p------ -------- Sprite priority
298 // +4 --cccccc -------- Sprite color palette
299 // +4 -------- -ppppppp Signed 7-bit pitch value between scanlines
300 // +6 f------- -------- Horizontal flip: read the data backwards if set
301 // +6 -ooooooo oooooooo Offset within selected sprite bank
302 // +8 --zzzzzz -------- Horizontal zoom factor
303 // +8 -------- --zzzzzz Vertical zoom factor
304 // +E dddddddd dddddddd Scratch space for current address
305 //
306 // Final bitmap format:
307 //
308 // ----s--- -------- Sprite shadow disable
309 // -----p-- -------- Sprite priority
310 // ------cc cccc---- Sprite color palette
311 // -------- ----llll 4-bit pixel data
312 //
313 // Special notes:
314 //
315 // There is an interaction between the horizonal flip bit and the offset.
316 // The offset is maintained as a 16-bit value, even though only the lower
317 // 15 bits are used for the address. The top bit is used to control flipping.
318 // This means that if the low 15 bits overflow during rendering, the sprite
319 // data will be read backwards after the overflow. This is important to
320 // emulate correctly as many games make use of this feature to render sprites
321 // at the beginning of a bank.
322 //
323
324 // render the sprites in order
325 const uint32_t *spritebase = &m_sprite_region_ptr[0];
326 uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x20000;
327 const uint8_t *zoom = memregion("zoom")->base();
328 uint16_t *ramend = spriteram() + spriteram_elements();
329 for (uint16_t *data = spriteram(); data < ramend; data += 8)
330 {
331 // fetch the bottom; stop when we get something out of range
332 int bottom = data[0] >> 8;
333 if (bottom > 0xf0)
334 break;
335
336 // extract remaining parameters
337 int top = data[0] & 0xff;
338 int bank = m_bank[(data[1] >> 12) & 0x7];
339 int xpos = data[1] & 0x1ff;
340 int colpri = ((data[2] >> 8) & 0xff) << 4;
341 int pitch = int16_t(data[2] << 9) >> 9;
342 uint16_t addr = data[3];
343 int hzoom = ((data[4] >> 8) & 0x3f) << 1;
344 int vzoom = (data[4] >> 0) & 0x3f;
345
346 // initialize the end address to the start address
347 data[7] = addr;
348
349 // if top greater than/equal to bottom, or invalid bank, punt
350 if (top >= bottom || bank == 255)
351 continue;
352
353 // clamp to within the memory region size
354 if (numbanks)
355 bank %= numbanks;
356 const uint32_t *spritedata = spritebase + 0x8000 * bank;
357
358 // determine the starting zoom address and mask
359 int zaddr = (vzoom & 0x38) << 5;
360 int zmask = 1 << (vzoom & 7);
361
362 // loop from top to bottom
363 int minx = xpos;
364 int maxx = cliprect.min_x - 1;
365 int miny = cliprect.max_y + 1;
366 int maxy = cliprect.min_y - 1;
367 for (int y = top; y < bottom; y++)
368 {
369 // advance a row
370 addr += pitch;
371
372 // if the zoom bit says so, add pitch a second time
373 if (zoom[zaddr++] & zmask)
374 addr += pitch;
375
376 // skip drawing if not within the cliprect
377 if (y >= cliprect.min_y && y <= cliprect.max_y)
378 {
379 uint16_t *dest = &bitmap.pix(y);
380 int xacc = 0x00;
381 int x;
382
383 // note that the System 16A sprites have a design flaw that allows the address
384 // to carry into the flip flag, which is the topmost bit -- it is very important
385 // to emulate this as the games compensate for it
386
387 // non-flipped case
388 if (!(addr & 0x8000))
389 {
390 // start at the word before because we preincrement below
391 data[7] = addr - 1;
392 for (x = xpos; x <= cliprect.max_x; )
393 {
394 uint32_t pixels = spritedata[++data[7] & 0x7fff];
395
396 // draw 8 pixels
397 int pix;
398 pix = (pixels >> 28) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
399 pix = (pixels >> 24) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
400 pix = (pixels >> 20) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
401 pix = (pixels >> 16) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
402 pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
403 pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
404 pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
405 pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
406
407 // stop if the last pixel in the group was 0xf
408 if (pix == 15)
409 break;
410 }
411 }
412
413 // flipped case
414 else
415 {
416 // start at the word after because we predecrement below
417 data[7] = addr + 1;
418 for (x = xpos; x <= cliprect.max_x; )
419 {
420 uint32_t pixels = spritedata[--data[7] & 0x7fff];
421
422 // draw 8 pixels
423 int pix;
424 pix = (pixels >> 0) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
425 pix = (pixels >> 4) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
426 pix = (pixels >> 8) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
427 pix = (pixels >> 12) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
428 pix = (pixels >> 16) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
429 pix = (pixels >> 20) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
430 pix = (pixels >> 24) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
431 pix = (pixels >> 28) & 0xf; xacc = (xacc & 0xff) + hzoom; if (xacc < 0x100) { if (x >= cliprect.min_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x++; }
432
433 // stop if the last pixel in the group was 0xf
434 if (pix == 15)
435 break;
436 }
437 }
438
439 // update bounds
440 if (x > maxx) maxx = x;
441 if (y < miny) miny = y;
442 maxy = y;
443 }
444 }
445
446 // mark dirty
447 if (minx <= maxx && miny <= maxy)
448 mark_dirty(minx, maxx, miny, maxy);
449 }
450 }
451
452
453
454 //****************************************************************************
455 // SYSTEM 16A-STYLE SPRITES
456 //****************************************************************************
457
458 //-------------------------------------------------
459 // sega_sys16a_sprite_device -- constructor
460 //-------------------------------------------------
461
sega_sys16a_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)462 sega_sys16a_sprite_device::sega_sys16a_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
463 : sega_16bit_sprite_device(mconfig, SEGA_SYS16A_SPRITES, tag, owner)
464 , m_sprite_region_ptr(*this, DEVICE_SELF)
465 {
466 set_local_origin(189, -1, -189, -1);
467 }
468
469
470 //-------------------------------------------------
471 // draw -- render the sprites within the cliprect
472 //-------------------------------------------------
473
draw(bitmap_ind16 & bitmap,const rectangle & cliprect)474 void sega_sys16a_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
475 {
476 //
477 // System 16A-style sprites
478 //
479 // Offs Bits Usage
480 // +0 bbbbbbbb -------- Bottom scanline of sprite - 1
481 // +0 -------- tttttttt Top scanline of sprite - 1
482 // +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
483 // +4 pppppppp pppppppp Signed 16-bit pitch value between scanlines
484 // +6 -ooooooo oooooooo Offset within selected sprite bank
485 // +6 f------- -------- Horizontal flip: read the data backwards if set
486 // +8 --cccccc -------- Sprite color palette
487 // +8 -------- -bbb---- Sprite bank
488 // +8 -------- ------pp Sprite priority
489 // +E dddddddd dddddddd Scratch space for current address
490 //
491 // Final bitmap format:
492 //
493 // ----pp-- -------- Sprite priority
494 // ------cc cccc---- Sprite color palette
495 // -------- ----llll 4-bit pixel data
496 //
497 // Special notes:
498 //
499 // There is an interaction between the horizonal flip bit and the offset.
500 // The offset is maintained as a 16-bit value, even though only the lower
501 // 15 bits are used for the address. The top bit is used to control flipping.
502 // This means that if the low 15 bits overflow during rendering, the sprite
503 // data will be read backwards after the overflow. This is important to
504 // emulate correctly as many games make use of this feature to render sprites
505 // at the beginning of a bank.
506 //
507
508 // render the sprites in order
509 const uint16_t *spritebase = &m_sprite_region_ptr[0];
510 uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x10000;
511 uint16_t *ramend = spriteram() + spriteram_elements();
512 for (uint16_t *data = spriteram(); data < ramend; data += 8)
513 {
514 // fetch the bottom; stop when we get something out of range
515 int bottom = data[0] >> 8;
516 if (bottom > 0xf0)
517 break;
518
519 // extract remaining parameters
520 int top = data[0] & 0xff;
521 int xpos = data[1] & 0x1ff;
522 int pitch = int16_t(data[2]);
523 uint16_t addr = data[3];
524 int colpri = (((data[4] >> 8) & 0x3f) << 4) | (((data[4] >> 0) & 0x3) << 10);
525 int bank = m_bank[(data[4] >> 4) & 0x7];
526
527 // initialize the end address to the start address
528 data[7] = addr;
529
530 // if top greater than/equal to bottom, or invalid bank, punt
531 if (top >= bottom || bank == 255)
532 continue;
533
534 // clamp to within the memory region size
535 if (numbanks)
536 bank %= numbanks;
537 const uint16_t *spritedata = spritebase + 0x8000 * bank;
538
539 // adjust positions for screen flipping
540 int xdelta = 1;
541 if (m_flip)
542 {
543 int temp = top;
544 top = 224 - bottom;
545 bottom = 224 - temp;
546 xpos = 320 - xpos;
547 xdelta = -1;
548 set_origin(m_xoffs_flipped, m_yoffs_flipped);
549 }
550 else
551 {
552 set_origin(m_xoffs, m_yoffs);
553 }
554
555 // loop from top to bottom
556 int minx = xpos;
557 int maxx = xpos;
558 int miny = cliprect.max_y + 1;
559 int maxy = cliprect.min_y - 1;
560 for (int y = top; y < bottom; y++)
561 {
562 // advance a row
563 addr += pitch;
564
565 // skip drawing if not within the cliprect
566 if (y >= cliprect.min_y && y <= cliprect.max_y)
567 {
568 uint16_t *dest = &bitmap.pix(y);
569 int x;
570
571 // note that the System 16A sprites have a design flaw that allows the address
572 // to carry into the flip flag, which is the topmost bit -- it is very important
573 // to emulate this as the games compensate for it
574
575 // non-flipped case
576 if (!(addr & 0x8000))
577 {
578 // start at the word before because we preincrement below
579 data[7] = addr - 1;
580 for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
581 {
582 uint16_t pixels = spritedata[++data[7] & 0x7fff];
583
584 // draw four pixels
585 int pix;
586 pix = (pixels >> 12) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
587 pix = (pixels >> 8) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
588 pix = (pixels >> 4) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
589 pix = (pixels >> 0) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
590
591 // stop if the last pixel in the group was 0xf
592 if (pix == 15)
593 break;
594 }
595 }
596
597 // flipped case
598 else
599 {
600 // start at the word after because we predecrement below
601 data[7] = addr + 1;
602 for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
603 {
604 uint16_t pixels = spritedata[--data[7] & 0x7fff];
605
606 // draw four pixels
607 int pix;
608 pix = (pixels >> 0) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
609 pix = (pixels >> 4) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
610 pix = (pixels >> 8) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
611 pix = (pixels >> 12) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
612
613 // stop if the last pixel in the group was 0xf
614 if (pix == 15)
615 break;
616 }
617 }
618
619 // update bounds
620 if (x > maxx) maxx = x;
621 if (x < minx) minx = x;
622 if (y < miny) miny = y;
623 maxy = y;
624 }
625 }
626
627 // mark dirty
628 if (minx <= maxx && miny <= maxy)
629 mark_dirty(minx, maxx, miny, maxy);
630 }
631 }
632
633
634 //****************************************************************************
635 // BOOTLEG SYSTEM 16A-STYLE SPRITES
636 //****************************************************************************
637
638 //-------------------------------------------------
639 // bootleg_sys16a_sprite_device -- constructor
640 //-------------------------------------------------
641
bootleg_sys16a_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)642 bootleg_sys16a_sprite_device::bootleg_sys16a_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
643 : sega_16bit_sprite_device(mconfig, BOOTLEG_SYS16A_SPRITES, tag, owner)
644 , m_sprite_region_ptr(*this, DEVICE_SELF)
645 {
646 m_addrmap[0] = 0;
647 m_addrmap[1] = 1;
648 m_addrmap[2] = 2;
649 m_addrmap[3] = 3;
650 m_addrmap[4] = 4;
651 m_addrmap[5] = 5;
652 m_addrmap[6] = 6;
653 m_addrmap[7] = 7;
654 set_local_origin(189, -1);
655 }
656
657
658 //-------------------------------------------------
659 // set_remap -- configure sprite address
660 // remapping
661 //-------------------------------------------------
662
set_remap(uint8_t offs0,uint8_t offs1,uint8_t offs2,uint8_t offs3,uint8_t offs4,uint8_t offs5,uint8_t offs6,uint8_t offs7)663 void bootleg_sys16a_sprite_device::set_remap(uint8_t offs0, uint8_t offs1, uint8_t offs2, uint8_t offs3, uint8_t offs4, uint8_t offs5, uint8_t offs6, uint8_t offs7)
664 {
665 m_addrmap[0] = offs0;
666 m_addrmap[1] = offs1;
667 m_addrmap[2] = offs2;
668 m_addrmap[3] = offs3;
669 m_addrmap[4] = offs4;
670 m_addrmap[5] = offs5;
671 m_addrmap[6] = offs6;
672 m_addrmap[7] = offs7;
673 }
674
675
676 //-------------------------------------------------
677 // draw -- render the sprites within the cliprect
678 //-------------------------------------------------
679
draw(bitmap_ind16 & bitmap,const rectangle & cliprect)680 void bootleg_sys16a_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
681 {
682 //
683 // Bootleg System 16A-style sprites
684 //
685 // These are identical to regular System 16A sprites (see above), with two exceptions:
686 //
687 // 1. Addresses within each sprite entry are generally shuffled relative
688 // to the original, and
689 //
690 // 2. The pitch increment happens at the end, not at the beginning of
691 // the loop.
692 //
693
694 // render the sprites in order
695 const uint16_t *spritebase = &m_sprite_region_ptr[0];
696 uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x10000;
697 uint16_t *ramend = spriteram() + spriteram_elements();
698 for (uint16_t *data = spriteram(); data < ramend; data += 8)
699 {
700 // fetch the bottom; stop when we get something out of range
701 int bottom = data[m_addrmap[0]] >> 8;
702 if (bottom > 0xf0)
703 break;
704
705 // extract remaining parameters
706 int top = data[m_addrmap[0]] & 0xff;
707 int xpos = data[m_addrmap[1]] & 0x1ff;
708 int pitch = int16_t(data[m_addrmap[2]]);
709 uint16_t addr = data[m_addrmap[3]];
710 int colpri = (((data[m_addrmap[4]] >> 8) & 0x3f) << 4) | (((data[m_addrmap[4]] >> 0) & 0x3) << 10);
711 int bank = m_bank[(data[m_addrmap[4]] >> 4) & 0x7];
712
713 // initialize the end address to the start address
714 uint16_t &data7 = data[m_addrmap[7]];
715 data7 = addr;
716
717 // if top greater than/equal to bottom, or invalid bank, punt
718 if (top >= bottom || bank == 255)
719 continue;
720
721 // clamp to within the memory region size
722 if (numbanks)
723 bank %= numbanks;
724 const uint16_t *spritedata = spritebase + 0x8000 * bank;
725
726 // adjust positions for screen flipping
727 int xdelta = 1;
728 if (m_flip)
729 {
730 int temp = top;
731 top = 224 - bottom;
732 bottom = 224 - temp;
733 xpos = 320 - xpos;
734 xdelta = -1;
735 set_origin(m_xoffs_flipped, m_yoffs_flipped);
736 }
737 else
738 {
739 set_origin(m_xoffs, m_yoffs);
740 }
741
742 // loop from top to bottom
743 int minx = xpos;
744 int maxx = xpos;
745 int miny = cliprect.max_y + 1;
746 int maxy = cliprect.min_y - 1;
747 for (int y = top; y < bottom; y++)
748 {
749 // skip drawing if not within the cliprect
750 if (y >= cliprect.min_y && y <= cliprect.max_y)
751 {
752 uint16_t *dest = &bitmap.pix(y);
753 int x;
754
755 // note that the System 16A sprites have a design flaw that allows the address
756 // to carry into the flip flag, which is the topmost bit -- it is very important
757 // to emulate this as the games compensate for it
758
759 // non-flipped case
760 if (!(addr & 0x8000))
761 {
762 // start at the word before because we preincrement below
763 data7 = addr - 1;
764 for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
765 {
766 uint16_t pixels = spritedata[++data7 & 0x7fff];
767
768 // draw four pixels
769 int pix;
770 pix = (pixels >> 12) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
771 pix = (pixels >> 8) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
772 pix = (pixels >> 4) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
773 pix = (pixels >> 0) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
774
775 // stop if the last pixel in the group was 0xf
776 if (pix == 15)
777 break;
778 }
779 }
780
781 // flipped case
782 else
783 {
784 // start at the word after because we predecrement below
785 data7 = addr + 1;
786 for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
787 {
788 uint16_t pixels = spritedata[--data7 & 0x7fff];
789
790 // draw four pixels
791 int pix;
792 pix = (pixels >> 0) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
793 pix = (pixels >> 4) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
794 pix = (pixels >> 8) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
795 pix = (pixels >> 12) & 0xf; if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta;
796
797 // stop if the last pixel in the group was 0xf
798 if (pix == 15)
799 break;
800 }
801 }
802
803 // update bounds
804 if (x > maxx) maxx = x;
805 if (x < minx) minx = x;
806 if (y < miny) miny = y;
807 maxy = y;
808 }
809
810 // advance a row - must be done at the end on the bootlegs!
811 addr += pitch;
812 }
813
814 // mark dirty
815 if (minx <= maxx && miny <= maxy)
816 mark_dirty(minx, maxx, miny, maxy);
817 }
818 }
819
820
821
822 //****************************************************************************
823 // SYSTEM 16B-STYLE SPRITES
824 //****************************************************************************
825
826 //-------------------------------------------------
827 // sega_sys16b_sprite_device -- constructor
828 //-------------------------------------------------
829
sega_sys16b_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)830 sega_sys16b_sprite_device::sega_sys16b_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
831 : sega_16bit_sprite_device(mconfig, SEGA_SYS16B_SPRITES, tag, owner)
832 , m_sprite_region_ptr(*this, DEVICE_SELF)
833 {
834 set_local_origin(184, 0x00, -184, 0);
835 }
836
837
838 //-------------------------------------------------
839 // draw -- render the sprites within the cliprect
840 //-------------------------------------------------
841
draw(bitmap_ind16 & bitmap,const rectangle & cliprect)842 void sega_sys16b_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
843 {
844 //
845 // System 16B-style sprites
846 //
847 // Offs Bits Usage
848 // +0 bbbbbbbb -------- Bottom scanline of sprite - 1
849 // +0 -------- tttttttt Top scanline of sprite - 1
850 // +2 -------x xxxxxxxx X position of sprite (position $BD is screen position 0)
851 // +2 ---iiii- -------- Sprite/sprite priority for Y-board
852 // +4 e------- -------- Signify end of sprite list
853 // +4 -h------ -------- Hide this sprite
854 // +4 -------f -------- Horizontal flip: read the data backwards if set
855 // +4 -------- pppppppp Signed 8-bit pitch value between scanlines
856 // +6 oooooooo oooooooo Offset within selected sprite bank
857 // +8 ----bbbb -------- Sprite bank
858 // +8 -------- pp------ Sprite priority, relative to tilemaps
859 // +8 -------- --cccccc Sprite color palette
860 // +A ------vv vvv----- Vertical zoom factor (0 = full size, 0x10 = half size)
861 // +A -------- ---hhhhh Horizontal zoom factor (0 = full size, 0x10 = half size)
862 // +E dddddddd dddddddd Scratch space for current address
863 //
864 // Final bitmap format:
865 //
866 // iiii---- -------- Sprite/sprite priority for Y-board
867 // ----pp-- -------- Sprite priority
868 // ------cc cccc---- Sprite color palette
869 // -------- ----llll 4-bit pixel data
870 //
871 // Note that the zooming described below is 100% accurate to the real board.
872 //
873
874 // render the sprites in order
875 const uint16_t *spritebase = &m_sprite_region_ptr[0];
876 uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x20000;
877 uint16_t *ramend = spriteram() + spriteram_elements();
878 for (uint16_t *data = spriteram(); data < ramend; data += 8)
879 {
880 // stop when we hit the end of sprite list
881 if (data[2] & 0x8000)
882 break;
883
884 // extract parameters
885 int bottom = data[0] >> 8;
886 int top = data[0] & 0xff;
887 int xpos = data[1] & 0x1ff;
888 int hide = data[2] & 0x4000;
889 int flip = data[2] & 0x100;
890 int pitch = int8_t(data[2] & 0xff);
891 uint16_t addr = data[3];
892 int bank = m_bank[(data[4] >> 8) & 0xf];
893 int colpri = ((data[4] & 0xff) << 4) | (((data[1] >> 9) & 0xf) << 12);
894 int vzoom = (data[5] >> 5) & 0x1f;
895 int hzoom = data[5] & 0x1f;
896 const uint16_t *spritedata;
897
898 xpos &= 0x1ff;
899
900 // initialize the end address to the start address
901 data[7] = addr;
902
903 // if hidden, or top greater than/equal to bottom, or invalid bank, punt
904 if (hide || top >= bottom || bank == 255)
905 continue;
906
907 // clamp to within the memory region size
908 if (numbanks)
909 bank %= numbanks;
910 spritedata = spritebase + 0x10000 * bank;
911
912 // reset the yzoom counter
913 data[5] &= 0x03ff;
914
915 // adjust positions for screen flipping
916 int xdelta = 1;
917 if (m_flip)
918 {
919 int temp = top;
920 top = 224 - bottom;
921 bottom = 224 - temp;
922 xpos = 320 - xpos;
923 xdelta = -1;
924 set_origin(m_xoffs_flipped, m_yoffs_flipped);
925 }
926 else
927 {
928 set_origin(m_xoffs, m_yoffs);
929 }
930
931 // loop from top to bottom
932 int minx = xpos;
933 int maxx = xpos;
934 int miny = cliprect.max_y + 1;
935 int maxy = cliprect.min_y - 1;
936 for (int y = top; y < bottom; y++)
937 {
938 // advance a row
939 addr += pitch;
940
941 // accumulate zoom factors; if we carry into the high bit, skip an extra row
942 data[5] += vzoom << 10;
943 if (data[5] & 0x8000)
944 {
945 addr += pitch;
946 data[5] &= ~0x8000;
947 }
948
949 // skip drawing if not within the cliprect
950 if (y >= cliprect.min_y && y <= cliprect.max_y)
951 {
952 uint16_t *dest = &bitmap.pix(y);
953 int x;
954
955 // compute the initial X zoom accumulator; this is verified on the real PCB
956 int xacc = 4 * hzoom;
957
958 // non-flipped case
959 if (!flip)
960 {
961 // start at the word before because we preincrement below
962 data[7] = addr - 1;
963 for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
964 {
965 uint16_t pixels = spritedata[++data[7]];
966
967 // draw four pixels
968 int pix;
969 pix = (pixels >> 12) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
970 pix = (pixels >> 8) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
971 pix = (pixels >> 4) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
972 pix = (pixels >> 0) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
973
974 // stop if the last pixel in the group was 0xf
975 if (pix == 15)
976 break;
977 }
978 }
979
980 // flipped case
981 else
982 {
983 // start at the word after because we predecrement below
984 data[7] = addr + 1;
985 for (x = xpos; ((xpos - x) & 0x1ff) != 1; )
986 {
987 uint16_t pixels = spritedata[--data[7]];
988
989 // draw four pixels
990 int pix;
991 pix = (pixels >> 0) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
992 pix = (pixels >> 4) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
993 pix = (pixels >> 8) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
994 pix = (pixels >> 12) & 0xf; xacc = (xacc & 0x3f) + hzoom; if (xacc < 0x40) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; }
995
996 // stop if the last pixel in the group was 0xf
997 if (pix == 15)
998 break;
999 }
1000 }
1001
1002 // update bounds
1003 if (x > maxx) maxx = x;
1004 if (x < minx) minx = x;
1005 if (y < miny) miny = y;
1006 maxy = y;
1007 }
1008 }
1009
1010 // mark dirty
1011 if (minx <= maxx && miny <= maxy)
1012 mark_dirty(minx, maxx, miny, maxy);
1013 }
1014 }
1015
1016
1017
1018 //****************************************************************************
1019 // OUT RUN/X-BOARD-STYLE SPRITES
1020 //****************************************************************************
1021
1022 //-------------------------------------------------
1023 // sega_outrun_sprite_device -- constructor
1024 //-------------------------------------------------
1025
sega_outrun_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1026 sega_outrun_sprite_device::sega_outrun_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1027 : sega_outrun_sprite_device(mconfig, SEGA_OUTRUN_SPRITES, tag, owner, clock, false)
1028 {
1029 }
1030
sega_outrun_sprite_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock,bool xboard_variant)1031 sega_outrun_sprite_device::sega_outrun_sprite_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool xboard_variant)
1032 : sega_16bit_sprite_device(mconfig, type, tag, owner)
1033 , m_is_xboard(xboard_variant)
1034 , m_sprite_region_ptr(*this, DEVICE_SELF)
1035 {
1036 set_local_origin(xboard_variant ? 190 : 189, 0x00);
1037 }
1038
1039
1040 //-------------------------------------------------
1041 // sega_xboard_sprite_device -- constructor
1042 //-------------------------------------------------
1043
sega_xboard_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1044 sega_xboard_sprite_device::sega_xboard_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1045 : sega_outrun_sprite_device(mconfig, SEGA_XBOARD_SPRITES, tag, owner, clock, true)
1046 {
1047 }
1048
1049
1050 //-------------------------------------------------
1051 // draw -- render the sprites within the cliprect
1052 //-------------------------------------------------
1053
draw(bitmap_ind16 & bitmap,const rectangle & cliprect)1054 void sega_outrun_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
1055 {
1056 //
1057 // Out Run/X-Board-style sprites
1058 //
1059 // Offs Bits Usage
1060 // +0 e------- -------- Signify end of sprite list
1061 // +0 -h-h---- -------- Hide this sprite if either bit is set
1062 // +0 ----bbb- -------- Sprite bank
1063 // +0 -------t tttttttt Top scanline of sprite + 256
1064 // +2 oooooooo oooooooo Offset within selected sprite bank
1065 // +4 ppppppp- -------- Signed 7-bit pitch value between scanlines
1066 // +4 -------x xxxxxxxx X position of sprite (position $BE is screen position 0)
1067 // +6 -s------ -------- Enable shadows
1068 // +6 --pp---- -------- Sprite priority, relative to tilemaps
1069 // +6 ------vv vvvvvvvv Vertical zoom factor (0x200 = full size, 0x100 = half size, 0x300 = 2x size)
1070 // +8 y------- -------- Render from top-to-bottom (1) or bottom-to-top (0) on screen
1071 // +8 -f------ -------- Horizontal flip: read the data backwards if set
1072 // +8 --x----- -------- Render from left-to-right (1) or right-to-left (0) on screen
1073 // +8 ------hh hhhhhhhh Horizontal zoom factor (0x200 = full size, 0x100 = half size, 0x300 = 2x size)
1074 // +E dddddddd dddddddd Scratch space for current address
1075 //
1076 // Out Run only:
1077 // +A hhhhhhhh -------- Height in scanlines - 1
1078 // +A -------- -ccccccc Sprite color palette
1079 //
1080 // X-Board only:
1081 // +A ----hhhh hhhhhhhh Height in scanlines - 1
1082 // +C -------- cccccccc Sprite color palette
1083 //
1084 // Final bitmap format:
1085 //
1086 // -s------ -------- Shadow control
1087 // --pp---- -------- Sprite priority
1088 // ----cccc cccc---- Sprite color palette
1089 // -------- ----llll 4-bit pixel data
1090 //
1091
1092 set_origin(m_xoffs, m_yoffs);
1093
1094 // render the sprites in order
1095 const uint32_t *spritebase = &m_sprite_region_ptr[0];
1096 uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x40000;
1097 uint16_t *ramend = buffer() + spriteram_elements();
1098 for (uint16_t *data = buffer(); data < ramend; data += 8)
1099 {
1100 // stop when we hit the end of sprite list
1101 if (data[0] & 0x8000)
1102 break;
1103
1104 // extract parameters
1105 int hide = (data[0] & 0x5000);
1106 int bank = (data[0] >> 9) & 7;
1107 int top = (data[0] & 0x1ff) - 0x100;
1108 uint16_t addr = data[1];
1109 int pitch = int16_t((data[2] >> 1) | ((data[4] & 0x1000) << 3)) >> 8;
1110 int xpos = data[2] & 0x1ff;
1111 int vzoom = data[3] & 0x7ff;
1112 int ydelta = (data[4] & 0x8000) ? 1 : -1;
1113 int flip = (~data[4] >> 14) & 1;
1114 int xdelta = (data[4] & 0x2000) ? 1 : -1;
1115 int hzoom = data[4] & 0x7ff;
1116 int height = (m_is_xboard ? (data[5] & 0xfff) : (data[5] >> 8)) + 1;
1117 int colpri = ((m_is_xboard ? (data[6] & 0xff) : (data[5] & 0x7f)) << 4) | (((data[3] >> 12) & 7) << 12);
1118
1119 // adjust X coordinate
1120 // note: the threshhold below is a guess. If it is too high, rachero will draw garbage
1121 // If it is too low, smgp won't draw the bottom part of the road
1122 if (xpos < 0x80 && xdelta < 0)
1123 xpos += 0x200;
1124
1125 // initialize the end address to the start address
1126 data[7] = addr;
1127
1128 // if hidden, punt
1129 if (hide)
1130 continue;
1131
1132 // clamp to within the memory region size
1133 if (numbanks)
1134 bank %= numbanks;
1135 const uint32_t *spritedata = spritebase + 0x10000 * bank;
1136
1137 // clamp to a maximum of 8x (not 100% confirmed)
1138 if (vzoom < 0x40) vzoom = 0x40;
1139 if (hzoom < 0x40) hzoom = 0x40;
1140
1141 // loop from top to bottom
1142 int minx = xpos;
1143 int maxx = xpos;
1144 int miny = cliprect.max_y + 1;
1145 int maxy = cliprect.min_y - 1;
1146 int yacc = 0;
1147 int ytarget = top + ydelta * height;
1148 for (int y = top; y != ytarget; y += ydelta)
1149 {
1150 // skip drawing if not within the cliprect
1151 if (y >= cliprect.min_y && y <= cliprect.max_y)
1152 {
1153 uint16_t *dest = &bitmap.pix(y);
1154 int xacc = 0;
1155 int x;
1156
1157 // non-flipped case
1158 if (!flip)
1159 {
1160 // start at the word before because we preincrement below
1161 data[7] = addr - 1;
1162 for (x = xpos; (xdelta > 0 && x <= cliprect.max_x) || (xdelta < 0 && x >= cliprect.min_x); )
1163 {
1164 uint32_t pixels = spritedata[++data[7]];
1165
1166 // draw four pixels
1167 int pix;
1168 pix = (pixels >> 28) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1169 pix = (pixels >> 24) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1170 pix = (pixels >> 20) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1171 pix = (pixels >> 16) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1172 pix = (pixels >> 12) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1173 pix = (pixels >> 8) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1174 pix = (pixels >> 4) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1175 pix = (pixels >> 0) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1176
1177 // stop if the second-to-last pixel in the group was 0xf
1178 if ((pixels & 0x000000f0) == 0x000000f0)
1179 break;
1180 }
1181 }
1182
1183 // flipped case
1184 else
1185 {
1186 // start at the word after because we predecrement below
1187 data[7] = addr + 1;
1188 for (x = xpos; (xdelta > 0 && x <= cliprect.max_x) || (xdelta < 0 && x >= cliprect.min_x); )
1189 {
1190 uint32_t pixels = spritedata[--data[7]];
1191
1192 // draw four pixels
1193 int pix;
1194 pix = (pixels >> 0) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1195 pix = (pixels >> 4) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1196 pix = (pixels >> 8) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1197 pix = (pixels >> 12) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1198 pix = (pixels >> 16) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1199 pix = (pixels >> 20) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1200 pix = (pixels >> 24) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1201 pix = (pixels >> 28) & 0xf; while (xacc < 0x200) { if (x >= cliprect.min_x && x <= cliprect.max_x && pix != 0 && pix != 15) dest[x] = colpri | pix; x += xdelta; xacc += hzoom; } xacc -= 0x200;
1202
1203 // stop if the second-to-last pixel in the group was 0xf
1204 if ((pixels & 0x0f000000) == 0x0f000000)
1205 break;
1206 }
1207 }
1208
1209 // update bounds
1210 if (x > maxx) maxx = x;
1211 if (x < minx) minx = x;
1212 if (y < miny) miny = y;
1213 if (y > maxy) maxy = y;
1214 }
1215
1216 // accumulate zoom factors; if we carry into the high bit, skip an extra row
1217 yacc += vzoom;
1218 addr += pitch * (yacc >> 9);
1219 yacc &= 0x1ff;
1220 }
1221
1222 // mark dirty
1223 if (minx <= maxx && miny <= maxy)
1224 mark_dirty(minx, maxx, miny, maxy);
1225 }
1226 }
1227
1228
1229
1230 //****************************************************************************
1231 // Y BOARD-STYLE SPRITES
1232 //****************************************************************************
1233
1234 //-------------------------------------------------
1235 // sega_yboard_sprite_device -- constructor
1236 //-------------------------------------------------
1237
sega_yboard_sprite_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)1238 sega_yboard_sprite_device::sega_yboard_sprite_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1239 : sega_16bit_sprite_device(mconfig, SEGA_YBOARD_SPRITES, tag, owner)
1240 , m_sprite_region_ptr(*this, DEVICE_SELF)
1241 {
1242 set_local_origin(0x600, 0x600);
1243 }
1244
1245
1246 //-------------------------------------------------
1247 // draw -- render the sprites within the cliprect
1248 //-------------------------------------------------
1249
draw(bitmap_ind16 & bitmap,const rectangle & cliprect)1250 void sega_yboard_sprite_device::draw(bitmap_ind16 &bitmap, const rectangle &cliprect)
1251 {
1252 //
1253 // Y-Board-style sprites
1254 //
1255 // Offs Bits Usage
1256 // +0 e------- -------- Signify end of sprite list
1257 // +0 -----iii iiiiiiii Address of indirection table (/16)
1258 // +2 bbbb---- -------- Upper 4 bits of bank index
1259 // +2 ----xxxx xxxxxxxx X position of sprite (position $600 is screen position 0)
1260 // +4 bbbb---- -------- Lower 4 bits of bank index
1261 // +4 ----yyyy yyyyyyyy Y position of sprite (position $600 is screen position 0)
1262 // +6 oooooooo oooooooo Offset within selected sprite bank
1263 // +8 hhhhhhhh hhhhhhhh Height of sprite
1264 // +A -y------ -------- Render from top-to-bottom (1) or bottom-to-top (0) on screen
1265 // +A --f----- -------- Horizontal flip: read the data backwards if set
1266 // +A ---x---- -------- Render from left-to-right (1) or right-to-left (0) on screen
1267 // +A -----zzz zzzzzzzz Zoom factor
1268 // +C -ccc---- -------- Sprite color
1269 // +C ----rrrr -------- Sprite priority
1270 // +C -------- pppppppp Signed 8-bit pitch value between scanlines
1271 // +E ----nnnn nnnnnnnn Index of next sprite
1272 //
1273 // Final bitmap format:
1274 //
1275 // ccc----- -------- Sprite color
1276 // ---rrrr- -------- Sprite priority
1277 // -------i iiiiiiii Indirected color data
1278 //
1279 // In addition to these parameters, the sprite area is clipped using scanline extents
1280 // stored for every pair of scanlines in the rotation RAM. It's a bit of a cheat for us
1281 // to poke our nose into the rotation structure, but there are no known cases of Y-board
1282 // sprites without rotation RAM.
1283 //
1284
1285 set_origin(m_xoffs, m_yoffs);
1286
1287 // clear out any scanlines we might be using
1288 const uint16_t *rotatebase = m_segaic16_rotate[0].buffer ? m_segaic16_rotate[0].buffer.get() : m_segaic16_rotate[0].rotateram;
1289 rotatebase -= yorigin();
1290 for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
1291 if (!(rotatebase[y & ~1] & 0xc000))
1292 memset(&bitmap.pix(y, cliprect.min_x), 0xff, cliprect.width() * sizeof(uint16_t));
1293
1294 // reset the visited list
1295 uint8_t visited[0x1000];
1296 memset(visited, 0, sizeof(visited));
1297
1298 // render the sprites in order
1299 const uint64_t *spritebase = &m_sprite_region_ptr[0];
1300 uint8_t numbanks = m_sprite_region_ptr.bytes() / 0x80000;
1301 int next = 0;
1302 for (uint16_t *data = spriteram(); !(data[0] & 0x8000) && !visited[next]; data = spriteram() + next * 8)
1303 {
1304 int hide = (data[0] & 0x5000);
1305 const uint16_t *indirect = spriteram() + ((data[0] & 0x7ff) << 4);
1306 int bank = ((data[1] >> 8) & 0x10) | ((data[2] >> 12) & 0x0f);
1307 int xpos = data[1] & 0xfff;
1308 int top = data[2] & 0xfff;
1309 uint16_t addr = data[3];
1310 int height = data[4];
1311 int ydelta = (data[5] & 0x4000) ? 1 : -1;
1312 int flip = (~data[5] >> 13) & 1;
1313 int xdelta = (data[5] & 0x1000) ? 1 : -1;
1314 int zoom = data[5] & 0x7ff;
1315 int colpri = (data[6] << 1) & 0xfe00;
1316 int pitch = int8_t(data[6]);
1317
1318 // note that we've visited this entry and get the offset of the next one
1319 visited[next] = 1;
1320 next = data[7] & 0xfff;
1321
1322 // if hidden, or invalid height, punt
1323 if (hide || height == 0)
1324 continue;
1325
1326 // clamp to within the memory region size
1327 if (numbanks)
1328 bank %= numbanks;
1329 const uint64_t *spritedata = spritebase + 0x10000 * bank;
1330
1331 // clamp to a maximum of 8x (not 100% confirmed)
1332 if (zoom == 0) zoom = 1;
1333
1334 // loop from top to bottom
1335 int dminx = xpos;
1336 int dmaxx = xpos;
1337 int dminy = cliprect.max_y + 1;
1338 int dmaxy = cliprect.min_y - 1;
1339 int ytarget = top + ydelta * height;
1340 int yacc = 0;
1341 for (int y = top; y != ytarget; y += ydelta)
1342 {
1343 // skip drawing if not within the cliprect
1344 if (y >= cliprect.min_y && y <= cliprect.max_y)
1345 {
1346 uint16_t *dest = &bitmap.pix(y);
1347 int minx = rotatebase[y & ~1];
1348 int maxx = rotatebase[y | 1];
1349 int xacc = 0;
1350
1351 // bit 0x8000 from rotate RAM means that Y is above the top of the screen
1352 if ((minx & 0x8000) && ydelta < 0)
1353 break;
1354
1355 // bit 0x4000 from rotate RAM means that Y is below the bottom of the screen
1356 if ((minx & 0x4000) && ydelta > 0)
1357 break;
1358
1359 // if either bit is set, skip the rest for this scanline
1360 if (!(minx & 0xc000))
1361 {
1362 // clamp min/max to the cliprect
1363 if (minx < cliprect.min_x)
1364 minx = cliprect.min_x;
1365 if (maxx > cliprect.max_x)
1366 maxx = cliprect.max_x;
1367
1368 // non-flipped case
1369 int x;
1370 if (!flip)
1371 {
1372 // start at the word before because we preincrement below
1373 uint16_t offs = addr - 1;
1374 for (x = xpos; (xdelta > 0 && x <= maxx) || (xdelta < 0 && x >= minx); )
1375 {
1376 uint64_t pixels = spritedata[++offs];
1377
1378 // draw 16 pixels
1379 int pix, ind;
1380 pix = (pixels >> 60) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1381 pix = (pixels >> 56) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1382 pix = (pixels >> 52) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1383 pix = (pixels >> 48) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1384 pix = (pixels >> 44) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1385 pix = (pixels >> 40) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1386 pix = (pixels >> 36) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1387 pix = (pixels >> 32) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1388 pix = (pixels >> 28) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1389 pix = (pixels >> 24) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1390 pix = (pixels >> 20) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1391 pix = (pixels >> 16) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1392 pix = (pixels >> 12) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1393 pix = (pixels >> 8) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1394 pix = (pixels >> 4) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1395 pix = (pixels >> 0) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1396
1397 // stop if the last pixel in the group was 0xf
1398 if (pix == 0x0f)
1399 break;
1400 }
1401 }
1402
1403 // flipped case
1404 else
1405 {
1406 // start at the word after because we predecrement below
1407 uint16_t offs = addr + 1;
1408 for (x = xpos; (xdelta > 0 && x <= maxx) || (xdelta < 0 && x >= minx); )
1409 {
1410 uint64_t pixels = spritedata[--offs];
1411
1412 // draw 16 pixels
1413 int pix, ind;
1414 pix = (pixels >> 0) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1415 pix = (pixels >> 4) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1416 pix = (pixels >> 8) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1417 pix = (pixels >> 12) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1418 pix = (pixels >> 16) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1419 pix = (pixels >> 20) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1420 pix = (pixels >> 24) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1421 pix = (pixels >> 28) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1422 pix = (pixels >> 32) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1423 pix = (pixels >> 36) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1424 pix = (pixels >> 40) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1425 pix = (pixels >> 44) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1426 pix = (pixels >> 48) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1427 pix = (pixels >> 52) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1428 pix = (pixels >> 56) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1429 pix = (pixels >> 60) & 0xf; ind = indirect[pix]; while (xacc < 0x200) { if (x >= minx && x <= maxx && ind < 0x1fe) dest[x] = colpri | ind; x += xdelta; xacc += zoom; } xacc -= 0x200;
1430
1431 // stop if the last pixel in the group was 0xf
1432 if (pix == 0x0f)
1433 break;
1434 }
1435 }
1436
1437 // update bounds
1438 if (x > dmaxx) dmaxx = x;
1439 if (x < dminx) dminx = x;
1440 if (y < dminy) dminy = y;
1441 if (y > dmaxy) dmaxy = y;
1442 }
1443 }
1444
1445 // accumulate zoom factors; if we carry into the high bit, skip an extra row
1446 yacc += zoom;
1447 addr += pitch * (yacc >> 9);
1448 yacc &= 0x1ff;
1449 }
1450
1451 // mark dirty
1452 if (dminx <= dmaxx && dminy <= dmaxy)
1453 mark_dirty(dminx, dmaxx, dminy, dmaxy);
1454 }
1455 }
1456