1 /*##########################################################################
2
3 atarimo.c
4
5 Common motion object management functions for Atari raster games.
6
7 ##########################################################################*/
8
9 #include "tiles_generic.h"
10 #include "atarimo.h"
11
12 #define MAX_GFX_ELEMENTS MAX_GFX
13
sect_rect(rectangle * rect,const rectangle * cliprect)14 void sect_rect(rectangle *rect, const rectangle *cliprect)
15 {
16 if (rect->min_x < cliprect->min_x) rect->min_x = cliprect->min_x;
17 if (rect->min_y < cliprect->min_y) rect->min_y = cliprect->min_y;
18 if (rect->max_x > cliprect->max_x) rect->max_x = cliprect->max_x;
19 if (rect->max_y > cliprect->max_y) rect->max_y = cliprect->max_y;
20 if (rect->min_y >= rect->max_y) rect->min_y = rect->max_y;
21 if (rect->min_x >= rect->max_x) rect->min_x = rect->max_x;
22
23 if (cliprect) return; // iq_132
24 }
25
26 /*##########################################################################
27 TYPES & STRUCTURES
28 ##########################################################################*/
29
30 /* internal structure containing a word index, shift and mask */
31 struct atarimo_mask
32 {
33 UINT16 word; /* word index */
34 UINT16 shift; /* shift amount */
35 UINT16 mask; /* final mask */
36 };
37
38 /* internal structure containing the state of the motion objects */
39 struct atarimo_data
40 {
41 int gfxchanged; /* true if the gfx info has changed */
42 GenericTilesGfx gfxelement[MAX_GFX_ELEMENTS]; /* local copy of graphics elements */
43 int gfxgranularity[MAX_GFX_ELEMENTS];
44
45 UINT16 *bitmap; /* temporary bitmap to render to */
46
47 int linked; /* are the entries linked? */
48 int split; /* are entries split or together? */
49 int reverse; /* render in reverse order? */
50 int swapxy; /* render in swapped X/Y order? */
51 UINT8 nextneighbor; /* does the neighbor bit affect the next object? */
52 int slipshift; /* log2(pixels_per_SLIP) */
53 int slipoffset; /* pixel offset for SLIP */
54
55 int entrycount; /* number of entries per bank */
56 int entrybits; /* number of bits needed to represent entrycount */
57 int bankcount; /* number of banks */
58
59 int tilewidth; /* width of non-rotated tile */
60 int tileheight; /* height of non-rotated tile */
61 int tilexshift; /* bits to shift X coordinate when drawing */
62 int tileyshift; /* bits to shift Y coordinate when drawing */
63 int bitmapwidth; /* width of the full playfield bitmap */
64 int bitmapheight; /* height of the full playfield bitmap */
65 int bitmapxmask; /* x coordinate mask for the playfield bitmap */
66 int bitmapymask; /* y coordinate mask for the playfield bitmap */
67
68 int spriterammask; /* combined mask when accessing sprite RAM with raw addresses */
69 int spriteramsize; /* total size of sprite RAM, in entries */
70 int sliprammask; /* combined mask when accessing SLIP RAM with raw addresses */
71 int slipramsize; /* total size of SLIP RAM, in entries */
72
73 UINT32 palettebase; /* base palette entry */
74 int maxcolors; /* maximum number of colors */
75 int transpen; /* transparent pen index */
76
77 UINT32 bank; /* current bank number */
78 UINT32 xscroll; /* current x scroll offset */
79 UINT32 yscroll; /* current y scroll offset */
80
81 int maxperline; /* maximum number of entries/line */
82
83 atarimo_mask linkmask; /* mask for the link */
84 atarimo_mask gfxmask; /* mask for the graphics bank */
85 atarimo_mask codemask; /* mask for the code index */
86 atarimo_mask codehighmask; /* mask for the upper code index */
87 atarimo_mask colormask; /* mask for the color */
88 atarimo_mask xposmask; /* mask for the X position */
89 atarimo_mask yposmask; /* mask for the Y position */
90 atarimo_mask widthmask; /* mask for the width, in tiles*/
91 atarimo_mask heightmask; /* mask for the height, in tiles */
92 atarimo_mask hflipmask; /* mask for the horizontal flip */
93 atarimo_mask vflipmask; /* mask for the vertical flip */
94 atarimo_mask prioritymask; /* mask for the priority */
95 atarimo_mask neighbormask; /* mask for the neighbor */
96 atarimo_mask absolutemask; /* mask for absolute coordinates */
97
98 atarimo_mask specialmask; /* mask for the special value */
99 int specialvalue; /* resulting value to indicate "special" */
100 atarimo_special_cb specialcb; /* callback routine for special entries */
101 int codehighshift; /* shift count for the upper code */
102
103 atarimo_entry * spriteram; /* pointer to sprite RAM */
104 UINT16 * slipram; /* pointer to the SLIP RAM pointer */
105 UINT16 * codelookup; /* lookup table for codes */
106 UINT8 * colorlookup; /* lookup table for colors */
107 UINT8 * gfxlookup; /* lookup table for graphics */
108
109 atarimo_entry * activelist[ATARIMO_MAXPERBANK]; /* pointers to active motion objects */
110 atarimo_entry ** activelast; /* pointer to the last pointer in the active list */
111 int last_link; /* previous starting point */
112
113 UINT8 * dirtygrid; /* grid of dirty rects for blending */
114 int dirtywidth; /* width of dirty grid */
115 int dirtyheight; /* height of dirty grid */
116
117 rectangle rectlist[ATARIMO_MAXPERBANK]; /* list of bounding rectangles */
118 int rectcount;
119
120 UINT32 last_xpos; /* (during processing) the previous X position */
121 UINT32 next_xpos; /* (during processing) the next X position */
122 };
123
124
125
126 /*##########################################################################
127 MACROS
128 ##########################################################################*/
129
130 /* data extraction */
131 #define EXTRACT_DATA(_input, _mask) (((_input)->data[(_mask).word] >> (_mask).shift) & (_mask).mask)
132
133
134
135 /*##########################################################################
136 GLOBAL VARIABLES
137 ##########################################################################*/
138
139 UINT16 *atarimo_0_spriteram = NULL;
140 UINT16 *atarimo_0_slipram = NULL;
141
142 UINT16 *atarimo_1_spriteram = NULL;
143 UINT16 *atarimo_1_slipram = NULL;
144
145
146
147 /*##########################################################################
148 STATIC VARIABLES
149 ##########################################################################*/
150
151 static atarimo_data atarimo[ATARIMO_MAX];
152
153 static rectangle mainclippy; // holds the clipping info when AtariMoRender() was called.
154
155
156
157 /*##########################################################################
158 STATIC FUNCTION DECLARATIONS
159 ##########################################################################*/
160
161 static int mo_render_object(atarimo_data *mo, const atarimo_entry *entry, const rectangle *cliprect);
162
163
164
165 /*##########################################################################
166 inline FUNCTIONS
167 ##########################################################################*/
168
169 /*---------------------------------------------------------------
170 compute_log: Computes the number of bits necessary to
171 hold a given value. The input must be an even power of
172 two.
173 ---------------------------------------------------------------*/
174
compute_log(int value)175 inline int compute_log(int value)
176 {
177 int log = 0;
178
179 if (value == 0)
180 return -1;
181 while (!(value & 1))
182 log++, value >>= 1;
183 if (value != 1)
184 return -1;
185 return log;
186 }
187
188
189 /*---------------------------------------------------------------
190 round_to_powerof2: Rounds a number up to the nearest
191 power of 2. Even powers of 2 are rounded up to the
192 next greatest power (e.g., 4 returns 8).
193 ---------------------------------------------------------------*/
194
round_to_powerof2(int value)195 inline int round_to_powerof2(int value)
196 {
197 int log = 0;
198
199 if (value == 0)
200 return 1;
201 while ((value >>= 1) != 0)
202 log++;
203 return 1 << (log + 1);
204 }
205
206
207 /*---------------------------------------------------------------
208 convert_mask: Converts a 4-word mask into a word index,
209 shift, and adjusted mask. Returns 0 if invalid.
210 ---------------------------------------------------------------*/
211
convert_mask(const atarimo_entry * input,atarimo_mask * result)212 inline int convert_mask(const atarimo_entry *input, atarimo_mask *result)
213 {
214 /* determine the word and make sure it's only 1 */
215 result->word = 0xffff;
216 for (int i = 0; i < 4; i++)
217 if (input->data[i])
218 {
219 if (result->word == 0xffff)
220 result->word = i;
221 else
222 return 0;
223 }
224
225 /* if all-zero, it's valid */
226 if (result->word == 0xffff)
227 {
228 result->word = result->shift = result->mask = 0;
229 return 1;
230 }
231
232 /* determine the shift and final mask */
233 result->shift = 0;
234 UINT16 temp = input->data[result->word];
235 while (!(temp & 1))
236 {
237 result->shift++;
238 temp >>= 1;
239 }
240 result->mask = temp;
241 return 1;
242 }
243
244
245 /*---------------------------------------------------------------
246 init_gfxelement: Make a copy of the main gfxelement that
247 gives us full control over colors.
248 ---------------------------------------------------------------*/
249
init_gfxelement(atarimo_data * mo,int idx)250 inline void init_gfxelement(atarimo_data *mo, int idx)
251 {
252 memcpy ((void*)&mo->gfxelement[idx], (void*)&GenericGfxData[idx], sizeof(GenericTilesGfx));
253 mo->gfxgranularity[idx] = 1<<mo->gfxelement[idx].depth;
254 // mo->gfxelement[idx].depth = 1;
255 // mo->gfxelement[idx].colortable = Machine->remapped_colortable;
256 mo->gfxelement[idx].color_mask = 65536;
257 }
258
259
260
261 /*##########################################################################
262 GLOBAL FUNCTIONS
263 ##########################################################################*/
264
atarimo_apply_stain(UINT16 * pf,UINT16 * mo,INT32 x,INT32,INT32 maxx)265 void atarimo_apply_stain(UINT16 *pf, UINT16 *mo, INT32 x, INT32 /*y*/, INT32 maxx)
266 {
267 const UINT16 START_MARKER = ((4 << 12) | 2);
268 const UINT16 END_MARKER = ((4 << 12) | 4);
269 bool offnext = false;
270
271 for ( ; x < maxx; x++)
272 {
273 pf[x] |= 0x400;
274 if (offnext && ((mo[x] == 0xffff) || (mo[x] & START_MARKER) != START_MARKER))
275 break;
276 offnext = (mo[x] != 0xffff) && ((mo[x] & END_MARKER) == END_MARKER);
277 }
278 }
279
AtariMoApplyStain(UINT16 * pf,UINT16 * mo,INT32 x)280 void AtariMoApplyStain(UINT16 *pf, UINT16 *mo, INT32 x)
281 {
282 const UINT16 START_MARKER = ((4 << 12) | 2);
283 const UINT16 END_MARKER = ((4 << 12) | 4);
284 bool offnext = false;
285
286 for ( ; x < nScreenWidth; x++)
287 {
288 pf[x] |= 0x400;
289 if (offnext && ((mo[x] == 0xffff) || (mo[x] & START_MARKER) != START_MARKER))
290 break;
291 offnext = (mo[x] != 0xffff) && ((mo[x] & END_MARKER) == END_MARKER);
292 }
293 }
294
295
296 #if 0 // iq_132
297 static void force_update(int scanline)
298 {
299 if (scanline > 0)
300 video_screen_update_partial(0, scanline - 1);
301
302 scanline += 64;
303 if (scanline >= Machine->screen[0].visarea.max_y)
304 scanline = 0;
305 timer_set(cpu_getscanlinetime(scanline), scanline, force_update);
306 }
307 #endif
308
309 /*---------------------------------------------------------------
310 atarimo_init: Configures the motion objects using the input
311 description. Allocates all memory necessary and generates
312 the attribute lookup table.
313 ---------------------------------------------------------------*/
314
atarimo_init(int map,const atarimo_desc * desc)315 int atarimo_init(int map, const atarimo_desc *desc)
316 {
317 GenericTilesGfx *gfx = &GenericGfxData[desc->gfxindex];
318 atarimo_data *mo = &atarimo[map];
319 int i;
320
321 if (map == 0)
322 memset(&atarimo, 0, sizeof(atarimo));
323
324 /* determine the masks first */
325 convert_mask(&desc->linkmask, &mo->linkmask);
326 convert_mask(&desc->gfxmask, &mo->gfxmask);
327 convert_mask(&desc->codemask, &mo->codemask);
328 convert_mask(&desc->codehighmask, &mo->codehighmask);
329 convert_mask(&desc->colormask, &mo->colormask);
330 convert_mask(&desc->xposmask, &mo->xposmask);
331 convert_mask(&desc->yposmask, &mo->yposmask);
332 convert_mask(&desc->widthmask, &mo->widthmask);
333 convert_mask(&desc->heightmask, &mo->heightmask);
334 convert_mask(&desc->hflipmask, &mo->hflipmask);
335 convert_mask(&desc->vflipmask, &mo->vflipmask);
336 convert_mask(&desc->prioritymask, &mo->prioritymask);
337 convert_mask(&desc->neighbormask, &mo->neighbormask);
338 convert_mask(&desc->absolutemask, &mo->absolutemask);
339
340 /* copy in the basic data */
341 mo->gfxchanged = 0;
342
343 mo->linked = desc->linked;
344 mo->split = desc->split;
345 mo->reverse = desc->reverse;
346 mo->swapxy = desc->swapxy;
347 mo->nextneighbor = desc->nextneighbor;
348 mo->slipshift = desc->slipheight ? compute_log(desc->slipheight) : 0;
349 mo->slipoffset = desc->slipoffset;
350
351 mo->entrycount = round_to_powerof2(mo->linkmask.mask);
352 mo->entrybits = compute_log(mo->entrycount);
353 mo->bankcount = desc->banks;
354
355 mo->tilewidth = gfx->width;
356 mo->tileheight = gfx->height;
357 mo->tilexshift = compute_log(mo->tilewidth);
358 mo->tileyshift = compute_log(mo->tileheight);
359 mo->bitmapwidth = round_to_powerof2(mo->xposmask.mask);
360 mo->bitmapheight = round_to_powerof2(mo->yposmask.mask);
361 mo->bitmapxmask = mo->bitmapwidth - 1;
362 mo->bitmapymask = mo->bitmapheight - 1;
363
364 mo->spriteramsize = mo->bankcount * mo->entrycount;
365 mo->spriterammask = mo->spriteramsize - 1;
366 mo->slipramsize = mo->bitmapheight >> mo->slipshift;
367 mo->sliprammask = mo->slipramsize - 1;
368
369 mo->palettebase = desc->palettebase;
370 mo->maxcolors = desc->maxcolors / (1<<gfx->depth);
371 mo->transpen = desc->transpen;
372
373 mo->bank = 0;
374 mo->xscroll = 0;
375 mo->yscroll = 0;
376
377 mo->maxperline = desc->maxlinks ? desc->maxlinks : 0x400;
378
379 convert_mask(&desc->specialmask, &mo->specialmask);
380 mo->specialvalue = desc->specialvalue;
381 mo->specialcb = desc->specialcb;
382 mo->codehighshift = compute_log(round_to_powerof2(mo->codemask.mask));
383
384 mo->slipram = (map == 0) ? atarimo_0_slipram : atarimo_1_slipram;
385
386 mo->last_link = -1;
387
388 /* allocate the temp bitmap */
389 BurnBitmapAllocate(31, nScreenWidth, nScreenHeight, false);
390 mo->bitmap = BurnBitmapGetBitmap(31);
391
392 BurnBitmapFill(31, 0xffff); // why not transpen?? -dink. because. -dink
393
394 /* allocate the spriteram */
395 mo->spriteram = (atarimo_entry*)BurnMalloc(sizeof(atarimo_entry) * mo->spriteramsize);
396
397 /* clear it to zero */
398 memset(mo->spriteram, 0, sizeof(atarimo_entry) * mo->spriteramsize);
399
400 /* allocate the code lookup */
401 mo->codelookup = (UINT16*)BurnMalloc(sizeof(UINT16) * round_to_powerof2(mo->codemask.mask));
402
403 /* initialize it 1:1 */
404 for (i = 0; i < round_to_powerof2(mo->codemask.mask); i++)
405 mo->codelookup[i] = i;
406
407 /* allocate the color lookup */
408 mo->colorlookup = BurnMalloc(sizeof(UINT8) * round_to_powerof2(mo->colormask.mask));
409
410 /* initialize it 1:1 */
411 for (i = 0; i < round_to_powerof2(mo->colormask.mask); i++)
412 mo->colorlookup[i] = i;
413
414 /* allocate dirty grid */
415 mo->dirtywidth = (mo->bitmapwidth >> mo->tilexshift) + 2;
416 mo->dirtyheight = (mo->bitmapheight >> mo->tileyshift) + 2;
417 mo->dirtygrid = BurnMalloc(mo->dirtywidth * mo->dirtyheight);
418 memset(mo->dirtygrid, 0x00, mo->dirtywidth * mo->dirtyheight);
419
420 /* allocate the gfx lookup */
421 mo->gfxlookup = BurnMalloc(sizeof(UINT8) * round_to_powerof2(mo->gfxmask.mask));
422
423 /* initialize it with the gfxindex we were passed in */
424 for (i = 0; i < round_to_powerof2(mo->gfxmask.mask); i++)
425 mo->gfxlookup[i] = desc->gfxindex;
426
427 /* initialize the gfx elements so we have full control over colors */
428 init_gfxelement(mo, desc->gfxindex);
429
430 /* start a timer to update a few times during refresh */
431 // timer_set(cpu_getscanlinetime(0), 0, force_update);
432
433 //bprintf(0, L"atarimo_init:\n");
434 //bprintf(0, L" width=%d (shift=%d), height=%d (shift=%d)\n", mo->tilewidth, mo->tilexshift, mo->tileheight, mo->tileyshift);
435 //bprintf(0, L" spriteram mask=%X, size=%d\n", mo->spriterammask, mo->spriteramsize);
436 //bprintf(0, L" slipram mask=%X, size=%d\n", mo->sliprammask, mo->slipramsize);
437 //bprintf(0, L" bitmap size=%dx%d\n", mo->bitmapwidth, mo->bitmapheight);
438 //bprintf(0, L" entrycount %X entrybits %X\n", mo->entrycount, mo->entrybits);
439 return 1;
440 }
441
AtariMoInit(INT32 map,const atarimo_desc * desc)442 void AtariMoInit(INT32 map, const atarimo_desc *desc)
443 {
444 atarimo_init(map, desc);
445 }
446
atarimo_exit()447 void atarimo_exit()
448 {
449 for (INT32 i = 0; i < 2; i++)
450 {
451 atarimo_data *mo = &atarimo[i];
452
453 if (mo->tilewidth)
454 {
455 BurnFree(mo->spriteram);
456 BurnFree(mo->codelookup);
457 BurnFree(mo->colorlookup);
458 BurnFree(mo->dirtygrid);
459 BurnFree(mo->gfxlookup);
460 }
461
462 memset (mo, 0, sizeof(atarimo_data));
463 }
464 }
465
AtariMoExit()466 void AtariMoExit()
467 {
468 atarimo_exit();
469 }
470
AtariMoScan(INT32 nAction,INT32 * pnMin)471 INT32 AtariMoScan(INT32 nAction, INT32 *pnMin)
472 {
473 if (nAction & ACB_VOLATILE)
474 {
475 for (INT32 i = 0; i < 2; i++)
476 {
477 atarimo_data *mo = &atarimo[i];
478
479 if (mo->tilewidth)
480 {
481 ScanVar(mo->spriteram, sizeof(atarimo_entry) * mo->spriteramsize, "AtariMO RAM");
482 SCAN_VAR(mo->bank);
483 SCAN_VAR(mo->xscroll);
484 SCAN_VAR(mo->yscroll);
485
486 }
487 }
488 }
489
490 return 0;
491 }
492
493 /*---------------------------------------------------------------
494 atarimo_get_code_lookup: Returns a pointer to the code
495 lookup table.
496 ---------------------------------------------------------------*/
497
atarimo_get_code_lookup(int map,int * size)498 UINT16 *atarimo_get_code_lookup(int map, int *size)
499 {
500 atarimo_data *mo = &atarimo[map];
501
502 if (size)
503 *size = round_to_powerof2(mo->codemask.mask);
504 return mo->codelookup;
505 }
506
507
508 /*---------------------------------------------------------------
509 atarimo_get_color_lookup: Returns a pointer to the color
510 lookup table.
511 ---------------------------------------------------------------*/
512
atarimo_get_color_lookup(int map,int * size)513 UINT8 *atarimo_get_color_lookup(int map, int *size)
514 {
515 atarimo_data *mo = &atarimo[map];
516
517 if (size)
518 *size = round_to_powerof2(mo->colormask.mask);
519 return mo->colorlookup;
520 }
521
522
523 /*---------------------------------------------------------------
524 atarimo_get_gfx_lookup: Returns a pointer to the graphics
525 lookup table.
526 ---------------------------------------------------------------*/
527
atarimo_get_gfx_lookup(int map,int * size)528 UINT8 *atarimo_get_gfx_lookup(int map, int *size)
529 {
530 atarimo_data *mo = &atarimo[map];
531
532 mo->gfxchanged = 1;
533 if (size)
534 *size = round_to_powerof2(mo->gfxmask.mask);
535 return mo->gfxlookup;
536 }
537
538
539 /*---------------------------------------------------------------
540 build_active_list: Build a list of active objects.
541 ---------------------------------------------------------------*/
542
build_active_list(atarimo_data * mo,int link)543 static void build_active_list(atarimo_data *mo, int link)
544 {
545 atarimo_entry *bankbase = &mo->spriteram[mo->bank << mo->entrybits];
546 UINT8 movisit[ATARIMO_MAXPERBANK];
547 atarimo_entry **current;
548 int i;
549
550 /* reset the visit map */
551 memset(movisit, 0, mo->entrycount);
552
553 /* remember the last link */
554 mo->last_link = link;
555
556 /* visit all the motion objects and copy their data into the display list */
557 for (i = 0, current = mo->activelist; i < mo->maxperline && !movisit[link]; i++)
558 {
559 atarimo_entry *modata = &bankbase[link];
560
561 /* copy the current entry into the list */
562 *current++ = modata;
563
564 /* link to the next object */
565 movisit[link] = 1;
566 if (mo->linked)
567 link = EXTRACT_DATA(modata, mo->linkmask);
568 else
569 link = (link + 1) & mo->linkmask.mask;
570 }
571
572 /* note the last entry */
573 mo->activelast = current;
574 }
575
576
577 /*---------------------------------------------------------------
578 get_dirty_base: Return the dirty grid pointer for a given
579 X and Y position.
580 ---------------------------------------------------------------*/
581
get_dirty_base(atarimo_data * mo,int x,int y)582 inline UINT8 *get_dirty_base(atarimo_data *mo, int x, int y)
583 {
584 UINT8 *result = mo->dirtygrid;
585 result += ((y >> mo->tileyshift) + 1) * mo->dirtywidth;
586 result += (x >> mo->tilexshift) + 1;
587 return result;
588 }
589
590
591 /*---------------------------------------------------------------
592 erase_dirty_grid: Erases the dirty grid within a given
593 cliprect.
594 ---------------------------------------------------------------*/
595
erase_dirty_grid(atarimo_data * mo,const rectangle * cliprect)596 static void erase_dirty_grid(atarimo_data *mo, const rectangle *cliprect)
597 {
598 int sx = cliprect->min_x >> mo->tilexshift;
599 int ex = cliprect->max_x >> mo->tilexshift;
600 int sy = cliprect->min_y >> mo->tileyshift;
601 int ey = cliprect->max_y >> mo->tileyshift;
602 int y;
603
604 /* loop over all grid rows that intersect our cliprect */
605 for (y = sy; y <= ey; y++)
606 {
607 /* get the base pointer and memset the row */
608 UINT8 *dirtybase = get_dirty_base(mo, cliprect->min_x, y << mo->tileyshift);
609 memset(dirtybase, 0, ex - sx + 1);
610 }
611 }
612
613
614 /*---------------------------------------------------------------
615 convert_dirty_grid_to_rects: Converts a dirty grid into a
616 series of cliprects.
617 ---------------------------------------------------------------*/
618
convert_dirty_grid_to_rects(atarimo_data * mo,const rectangle * cliprect,struct atarimo_rect_list * rectlist)619 static void convert_dirty_grid_to_rects(atarimo_data *mo, const rectangle *cliprect, struct atarimo_rect_list *rectlist)
620 {
621 int sx = cliprect->min_x >> mo->tilexshift;
622 int ex = cliprect->max_x >> mo->tilexshift;
623 int sy = cliprect->min_y >> mo->tileyshift;
624 int ey = cliprect->max_y >> mo->tileyshift;
625 int tilewidth = 1 << mo->tilexshift;
626 int tileheight = 1 << mo->tileyshift;
627 rectangle *rect;
628 int x, y;
629
630 /* initialize the rect list */
631 rectlist->numrects = 0;
632 rectlist->rect = mo->rectlist;
633 rect = NULL;
634
635 /* loop over all grid rows that intersect our cliprect */
636 for (y = sy; y <= ey; y++)
637 {
638 UINT8 *dirtybase = get_dirty_base(mo, cliprect->min_x, y << mo->tileyshift);
639 int can_add_to_existing = 0;
640
641 /* loop over all grid columns that intersect our cliprect */
642 for (x = sx; x <= ex; x++)
643 {
644 /* if this tile is dirty, add that to our rectlist */
645 if (*dirtybase++)
646 {
647 /* if we can't add to an existing rect, create a new one */
648 if (!can_add_to_existing)
649 {
650 /* advance pointers */
651 rectlist->numrects++;
652 if (rect == NULL) {
653 rect = &mo->rectlist[0];
654 } else {
655 rect++;
656 }
657
658 /* make a rect describing this grid square */
659 rect->min_x = x << mo->tilexshift;
660 rect->max_x = rect->min_x + tilewidth - 1;
661 rect->min_y = y << mo->tileyshift;
662 rect->max_y = rect->min_y + tileheight - 1;
663
664 /* neighboring grid squares can add to this one */
665 can_add_to_existing = 1;
666 }
667
668 /* if we can add to the previous rect, just expand its width */
669 else
670 rect->max_x += tilewidth;
671 }
672
673 /* once we hit a non-dirty square, we can no longer add on */
674 else
675 can_add_to_existing = 0;
676 }
677 }
678 }
679
680
681 /*---------------------------------------------------------------
682 atarimo_render: Render the motion objects to the
683 destination bitmap.
684 ---------------------------------------------------------------*/
685
atarimo_render(int map,const rectangle * cliprect,struct atarimo_rect_list * rectlist)686 UINT16 *atarimo_render(int map, const rectangle *cliprect, struct atarimo_rect_list *rectlist)
687 {
688 atarimo_data *mo = &atarimo[map];
689 int startband, stopband, band, i;
690 rectangle *rect;
691
692 /* if the graphics info has changed, recompute */
693 if (mo->gfxchanged)
694 {
695 mo->gfxchanged = 0;
696 for (i = 0; i < round_to_powerof2(mo->gfxmask.mask); i++)
697 init_gfxelement(mo, mo->gfxlookup[i]);
698 }
699
700 /* compute start/stop bands */
701 startband = ((cliprect->min_y + mo->yscroll - mo->slipoffset) & mo->bitmapymask) >> mo->slipshift;
702 stopband = ((cliprect->max_y + mo->yscroll - mo->slipoffset) & mo->bitmapymask) >> mo->slipshift;
703 if (startband > stopband)
704 startband -= mo->bitmapheight >> mo->slipshift;
705 if (!mo->slipshift)
706 stopband = startband;
707
708 /* erase the dirty grid */
709 erase_dirty_grid(mo, cliprect);
710
711 /* loop over SLIP bands */
712 for (band = startband; band <= stopband; band++)
713 {
714 atarimo_entry **first, **current, **last;
715 rectangle bandclip = *cliprect;
716 int link = 0;
717 int step;
718
719 /* otherwise, grab the SLIP and compute the bandrect */
720 if (mo->slipshift != 0)
721 {
722 link = (mo->slipram[band & mo->sliprammask] >> mo->linkmask.shift) & mo->linkmask.mask;
723
724 /* compute minimum Y and wrap around if necessary */
725 bandclip.min_y = ((band << mo->slipshift) - mo->yscroll + mo->slipoffset) & mo->bitmapymask;
726 if (bandclip.min_y >= nScreenHeight)
727 bandclip.min_y -= mo->bitmapheight;
728
729 /* maximum Y is based on the minimum */
730 bandclip.max_y = bandclip.min_y + (1 << mo->slipshift) - 1;
731
732 /* keep within the cliprect */
733 sect_rect(&bandclip, cliprect);
734 }
735
736 /* if this matches the last link, we don't need to re-process the list */
737 if (link != mo->last_link)
738 build_active_list(mo, link);
739
740 /* set the start and end points */
741 if (mo->reverse)
742 {
743 first = mo->activelast - 1;
744 last = mo->activelist - 1;
745 step = -1;
746 }
747 else
748 {
749 first = mo->activelist;
750 last = mo->activelast;
751 step = 1;
752 }
753
754 /* initialize the parameters */
755 mo->next_xpos = 123456;
756
757 /* render the mos */
758 for (current = first; current != last; current += step)
759 mo_render_object(mo, *current, &bandclip);
760 }
761
762 /* convert the dirty grid to a rectlist */
763 convert_dirty_grid_to_rects(mo, cliprect, rectlist);
764
765 /* clip the rectlist */
766 for (i = 0, rect = rectlist->rect; i < rectlist->numrects; i++, rect++)
767 sect_rect(rect, cliprect);
768
769 /* return the bitmap */
770 return mo->bitmap;
771 }
772
AtariMoRender(INT32 map,struct atarimo_rect_list * rectlist)773 void AtariMoRender(INT32 map, struct atarimo_rect_list *rectlist)
774 {
775 rectangle cliprect;
776 GenericTilesGetClip(&cliprect.min_x, &cliprect.max_x, &cliprect.min_y, &cliprect.max_y);
777 mainclippy = cliprect; // cache the clipping config. for later (mo_render_object()!) -dink
778
779 atarimo_render(map, &cliprect, rectlist);
780 }
781
AtariMoRender(INT32 map)782 void AtariMoRender(INT32 map)
783 {
784 rectangle cliprect;
785 GenericTilesGetClip(&cliprect.min_x, &cliprect.max_x, &cliprect.min_y, &cliprect.max_y);
786 mainclippy = cliprect;
787
788 struct atarimo_rect_list rectlist;
789
790 atarimo_render(map, &cliprect, &rectlist);
791 }
792
793 /*---------------------------------------------------------------
794 mo_render_object: Internal processing callback that
795 renders to the backing bitmap and then copies the result
796 to the destination.
797 ---------------------------------------------------------------*/
798
mo_render_object(atarimo_data * mo,const atarimo_entry * entry,const rectangle * cliprect)799 static int mo_render_object(atarimo_data *mo, const atarimo_entry *entry, const rectangle *cliprect)
800 {
801 int gfxindex = mo->gfxlookup[EXTRACT_DATA(entry, mo->gfxmask)];
802 const GenericTilesGfx *gfx = &mo->gfxelement[gfxindex];
803 UINT16 *bitmap = mo->bitmap;
804 int x, y, sx, sy;
805
806 /* extract data from the various words */
807 int code = mo->codelookup[EXTRACT_DATA(entry, mo->codemask)] | (EXTRACT_DATA(entry, mo->codehighmask) << mo->codehighshift);
808 int color = mo->colorlookup[EXTRACT_DATA(entry, mo->colormask)];
809 int xpos = EXTRACT_DATA(entry, mo->xposmask);
810 int ypos = -EXTRACT_DATA(entry, mo->yposmask);
811 int hflip = EXTRACT_DATA(entry, mo->hflipmask);
812 int vflip = EXTRACT_DATA(entry, mo->vflipmask);
813 int width = EXTRACT_DATA(entry, mo->widthmask) + 1;
814 int height = EXTRACT_DATA(entry, mo->heightmask) + 1;
815 int priority = EXTRACT_DATA(entry, mo->prioritymask);
816 int xadv, yadv, rendered = 0;
817 UINT8 *dirtybase;
818
819 #ifdef TEMPDEBUG
820 int temp = EXTRACT_DATA(entry, mo->codemask);
821 if ((temp & 0xff00) == 0xc800)
822 {
823 static UINT8 hits[256];
824 if (!hits[temp & 0xff])
825 {
826 fprintf(stderr, "code = %04X\n", temp);
827 hits[temp & 0xff] = 1;
828 }
829 }
830 #endif
831
832 /* compute the effective color, merging in priority */
833 color = (color * mo->gfxgranularity[gfxindex]) | (priority << ATARIMO_PRIORITY_SHIFT);
834 color += mo->palettebase;
835
836 /* add in the scroll positions if we're not in absolute coordinates */
837 if (!EXTRACT_DATA(entry, mo->absolutemask))
838 {
839 xpos -= mo->xscroll;
840 ypos -= mo->yscroll;
841 }
842
843 /* adjust for height */
844 ypos -= height << mo->tileyshift;
845
846 /* handle previous hold bits */
847 if (mo->next_xpos != 123456)
848 xpos = mo->next_xpos;
849 mo->next_xpos = 123456;
850
851 /* check for the hold bit */
852 if (EXTRACT_DATA(entry, mo->neighbormask))
853 {
854 if (!mo->nextneighbor)
855 xpos = mo->last_xpos + mo->tilewidth;
856 else
857 mo->next_xpos = xpos + mo->tilewidth;
858 }
859 mo->last_xpos = xpos;
860
861 /* adjust the final coordinates */
862 xpos &= mo->bitmapxmask;
863 ypos &= mo->bitmapymask;
864 if (xpos >= nScreenWidth) xpos -= mo->bitmapwidth;
865 if (ypos >= nScreenHeight) ypos -= mo->bitmapheight;
866
867 /* is this one special? */
868 if (mo->specialmask.mask != 0 && EXTRACT_DATA(entry, mo->specialmask) == mo->specialvalue)
869 {
870 if (mo->specialcb)
871 return (*mo->specialcb)(bitmap, cliprect, code, color, xpos, ypos, NULL);
872 return 0;
873 }
874
875 /* adjust for h flip */
876 xadv = mo->tilewidth;
877 if (hflip)
878 {
879 xpos += (width - 1) << mo->tilexshift;
880 xadv = -xadv;
881 }
882
883 /* adjust for v flip */
884 yadv = mo->tileheight;
885 if (vflip)
886 {
887 ypos += (height - 1) << mo->tileyshift;
888 yadv = -yadv;
889 }
890
891 /* standard order is: loop over Y first, then X */
892 if (!mo->swapxy)
893 {
894 /* loop over the height */
895 for (y = 0, sy = ypos; y < height; y++, sy += yadv)
896 {
897 /* clip the Y coordinate */
898 if (sy <= cliprect->min_y - mo->tileheight)
899 {
900 code += width;
901 continue;
902 }
903 else if (sy > cliprect->max_y)
904 break;
905
906 /* loop over the width */
907 for (x = 0, sx = xpos; x < width; x++, sx += xadv, code++)
908 {
909 /* clip the X coordinate */
910 if (sx <= -cliprect->min_x - mo->tilewidth || sx > cliprect->max_x)
911 continue;
912
913 /* draw the sprite */
914 // 1. set clipping to this sprite's bandclip:
915 GenericTilesSetClip(cliprect->min_x, cliprect->max_x, cliprect->min_y, (cliprect->max_y+1 > nScreenHeight) ? nScreenHeight : cliprect->max_y+1);
916 // 2: draw sprite:
917 DrawCustomMaskTile(bitmap, gfx->width, gfx->height, code, sx, sy, hflip, vflip, color, 0, mo->transpen, 0, gfx->gfxbase);
918 // 3: set clipping back to cached value @ AtariMoRender()
919 GenericTilesSetClip(mainclippy.min_x, mainclippy.max_x, mainclippy.min_y, mainclippy.max_y);
920 rendered = 1;
921
922 /* mark the grid dirty */
923 dirtybase = get_dirty_base(mo, sx, sy);
924 dirtybase[0] = 1;
925 dirtybase[1] = 1;
926 dirtybase[mo->dirtywidth] = 1;
927 dirtybase[mo->dirtywidth + 1] = 1;
928 }
929 }
930 }
931
932 /* alternative order is swapped */
933 else
934 {
935 /* loop over the width */
936 for (x = 0, sx = xpos; x < width; x++, sx += xadv)
937 {
938 /* clip the X coordinate */
939 if (sx <= cliprect->min_x - mo->tilewidth)
940 {
941 code += height;
942 continue;
943 }
944 else if (sx > cliprect->max_x)
945 break;
946
947 /* loop over the height */
948 dirtybase = get_dirty_base(mo, sx, ypos);
949 for (y = 0, sy = ypos; y < height; y++, sy += yadv, code++)
950 {
951 /* clip the X coordinate */
952 if (sy <= -cliprect->min_y - mo->tileheight || sy > cliprect->max_y)
953 continue;
954
955 /* draw the sprite */
956 GenericTilesSetClip(cliprect->min_x, cliprect->max_x, cliprect->min_y, (cliprect->max_y+1 > nScreenHeight) ? nScreenHeight : cliprect->max_y+1);
957 DrawCustomMaskTile(bitmap, gfx->width, gfx->height, code, sx, sy, hflip, vflip, color, 0, mo->transpen, 0, gfx->gfxbase);
958 GenericTilesSetClip(mainclippy.min_x, mainclippy.max_x, mainclippy.min_y, mainclippy.max_y);
959 rendered = 1;
960
961 /* mark the grid dirty */
962 dirtybase = get_dirty_base(mo, sx, sy);
963 dirtybase[0] = 1;
964 dirtybase[1] = 1;
965 dirtybase[mo->dirtywidth] = 1;
966 dirtybase[mo->dirtywidth + 1] = 1;
967 }
968 }
969 }
970
971 return rendered;
972 }
973
974
975 /*---------------------------------------------------------------
976 atarimo_set_bank: Set the banking value for
977 the motion objects.
978 ---------------------------------------------------------------*/
979
atarimo_set_bank(int map,int bank)980 void atarimo_set_bank(int map, int bank)
981 {
982 atarimo_data *mo = &atarimo[map];
983 if (mo->bank != bank)
984 {
985 mo->bank = bank;
986 mo->last_link = -1;
987 }
988 }
989
990
991 /*---------------------------------------------------------------
992 atarimo_set_palettebase: Set the palette base for
993 the motion objects.
994 ---------------------------------------------------------------*/
995
atarimo_set_palettebase(int map,int base)996 void atarimo_set_palettebase(int map, int base)
997 {
998 atarimo_data *mo = &atarimo[map];
999
1000 mo->palettebase = base;
1001
1002 // iq_132
1003 // for (INT32 i = 0; i < MAX_GFX_ELEMENTS; i++)
1004 // mo->gfxelement[i].colortable = &Machine->remapped_colortable[base];
1005 }
1006
1007
1008 /*---------------------------------------------------------------
1009 atarimo_set_xscroll: Set the horizontal scroll value for
1010 the motion objects.
1011 ---------------------------------------------------------------*/
1012
atarimo_set_xscroll(int map,int xscroll)1013 void atarimo_set_xscroll(int map, int xscroll)
1014 {
1015 atarimo_data *mo = &atarimo[map];
1016 mo->xscroll = xscroll;
1017 }
1018
1019
1020 /*---------------------------------------------------------------
1021 atarimo_set_yscroll: Set the vertical scroll value for
1022 the motion objects.
1023 ---------------------------------------------------------------*/
1024
atarimo_set_yscroll(int map,int yscroll)1025 void atarimo_set_yscroll(int map, int yscroll)
1026 {
1027 atarimo_data *mo = &atarimo[map];
1028 mo->yscroll = yscroll;
1029 }
1030
1031
1032 /*---------------------------------------------------------------
1033 atarimo_get_bank: Returns the banking value
1034 for the motion objects.
1035 ---------------------------------------------------------------*/
1036
atarimo_get_bank(int map)1037 int atarimo_get_bank(int map)
1038 {
1039 return atarimo[map].bank;
1040 }
1041
1042
1043 /*---------------------------------------------------------------
1044 atarimo_get_palettebase: Returns the palette base
1045 for the motion objects.
1046 ---------------------------------------------------------------*/
1047
atarimo_get_palettebase(int map)1048 int atarimo_get_palettebase(int map)
1049 {
1050 return atarimo[map].palettebase;
1051 }
1052
1053
1054 /*---------------------------------------------------------------
1055 atarimo_get_xscroll: Returns the horizontal scroll value
1056 for the motion objects.
1057 ---------------------------------------------------------------*/
1058
atarimo_get_xscroll(int map)1059 int atarimo_get_xscroll(int map)
1060 {
1061 return atarimo[map].xscroll;
1062 }
1063
1064
1065 /*---------------------------------------------------------------
1066 atarimo_get_yscroll: Returns the vertical scroll value for
1067 the motion objects.
1068 ---------------------------------------------------------------*/
1069
atarimo_get_yscroll(int map)1070 int atarimo_get_yscroll(int map)
1071 {
1072 return atarimo[map].yscroll;
1073 }
1074
1075
1076 /*---------------------------------------------------------------
1077 atarimo_0_spriteram_w: Write handler for the spriteram.
1078 ---------------------------------------------------------------*/
1079
AtariMoWrite(INT32 map,INT32 offset,UINT16 data)1080 void AtariMoWrite(INT32 map, INT32 offset, UINT16 data)
1081 {
1082 int entry, idx, bank;
1083 // COMBINE_DATA(&atarimo_0_spriteram[offset]);
1084 if (atarimo[map].split)
1085 {
1086 entry = offset & atarimo[map].linkmask.mask;
1087 idx = (offset >> atarimo[map].entrybits) & 3;
1088 }
1089 else
1090 {
1091 entry = (offset >> 2) & atarimo[map].linkmask.mask;
1092 idx = offset & 3;
1093 }
1094 bank = offset >> (2 + atarimo[map].entrybits);
1095 atarimo[map].spriteram[(bank << atarimo[map].entrybits) + entry].data[idx] = data;
1096 atarimo[map].last_link = -1;
1097 }
1098
1099
1100 /*---------------------------------------------------------------
1101 atarimo_0_spriteram_expanded_w: Write handler for the
1102 expanded form of spriteram.
1103 ---------------------------------------------------------------*/
1104
AtariMoExpandedWrite(INT32 map,INT32 offset,UINT16 data)1105 void AtariMoExpandedWrite(INT32 map, INT32 offset, UINT16 data)
1106 {
1107 int entry, idx, bank;
1108
1109 // COMBINE_DATA(&atarimo_0_spriteram[offset]);
1110 if (!(offset & 1))
1111 {
1112 offset >>= 1;
1113 if (atarimo[map].split)
1114 {
1115 entry = offset & atarimo[map].linkmask.mask;
1116 idx = (offset >> atarimo[map].entrybits) & 3;
1117 }
1118 else
1119 {
1120 entry = (offset >> 2) & atarimo[map].linkmask.mask;
1121 idx = offset & 3;
1122 }
1123 bank = offset >> (2 + atarimo[map].entrybits);
1124 atarimo[map].spriteram[(bank << atarimo[map].entrybits) + entry].data[idx] = data;
1125 atarimo[map].last_link = -1;
1126 }
1127 }
1128
1129 #if 0 // iq_132
1130 /*---------------------------------------------------------------
1131 atarimo_0_slipram_w: Write handler for the slipram.
1132 ---------------------------------------------------------------*/
1133
1134 WRITE16_HANDLER( atarimo_0_slipram_w )
1135 {
1136 COMBINE_DATA(&atarimo_0_slipram[offset]);
1137 }
1138
1139
1140 /*---------------------------------------------------------------
1141 atarimo_1_slipram_w: Write handler for the slipram.
1142 ---------------------------------------------------------------*/
1143
1144 WRITE16_HANDLER( atarimo_1_slipram_w )
1145 {
1146 COMBINE_DATA(&atarimo_1_slipram[offset]);
1147 }
1148
1149 #endif
1150