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