1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4 
5     tilemap.cpp
6 
7     Generic tilemap management system.
8 
9 ***************************************************************************/
10 
11 #include "emu.h"
12 #include "tilemap.h"
13 
14 #include "screen.h"
15 
16 
17 //**************************************************************************
18 //  INLINE FUNCTIONS
19 //**************************************************************************
20 
21 //-------------------------------------------------
22 //  effective_rowscroll - return the effective
23 //  rowscroll value for a given index, taking into
24 //  account tilemap flip states
25 //-------------------------------------------------
26 
effective_rowscroll(int index,u32 screen_width)27 inline s32 tilemap_t::effective_rowscroll(int index, u32 screen_width)
28 {
29 	// if we're flipping vertically, adjust the row number
30 	if (m_attributes & TILEMAP_FLIPY)
31 		index = m_scrollrows - 1 - index;
32 
33 	// adjust final result based on the horizontal flip and dx values
34 	s32 value;
35 	if (!(m_attributes & TILEMAP_FLIPX))
36 		value = m_dx - m_rowscroll[index];
37 	else
38 		value = screen_width - m_width - (m_dx_flipped - m_rowscroll[index]);
39 
40 	// clamp to 0..width
41 	if (value < 0)
42 		value = m_width - (-value) % m_width;
43 	else
44 		value %= m_width;
45 	return value;
46 }
47 
48 
49 //-------------------------------------------------
50 //  effective_colscroll - return the effective
51 //  colscroll value for a given index, taking into
52 //  account tilemap flip states
53 //-------------------------------------------------
54 
effective_colscroll(int index,u32 screen_height)55 inline s32 tilemap_t::effective_colscroll(int index, u32 screen_height)
56 {
57 	// if we're flipping horizontally, adjust the column number
58 	if (m_attributes & TILEMAP_FLIPX)
59 		index = m_scrollcols - 1 - index;
60 
61 	// adjust final result based on the vertical flip and dx values
62 	s32 value;
63 	if (!(m_attributes & TILEMAP_FLIPY))
64 		value = m_dy - m_colscroll[index];
65 	else
66 		value = screen_height - m_height - (m_dy_flipped - m_colscroll[index]);
67 
68 	// clamp to 0..height
69 	if (value < 0)
70 		value = m_height - (-value) % m_height;
71 	else
72 		value %= m_height;
73 	return value;
74 }
75 
76 
77 //-------------------------------------------------
78 //  gfx_tiles_changed - return true if any
79 //  gfx_elements used by this tilemap have
80 //  changed
81 //-------------------------------------------------
82 
gfx_elements_changed()83 inline bool tilemap_t::gfx_elements_changed()
84 {
85 	u32 usedmask = m_gfx_used;
86 	bool isdirty = false;
87 
88 	// iterate over all used gfx types and set the dirty flag if any of them have changed
89 	for (int gfxnum = 0; usedmask != 0; usedmask >>= 1, gfxnum++)
90 		if ((usedmask & 1) != 0)
91 			if (m_gfx_dirtyseq[gfxnum] != m_tileinfo.decoder->gfx(gfxnum)->dirtyseq())
92 			{
93 				m_gfx_dirtyseq[gfxnum] = m_tileinfo.decoder->gfx(gfxnum)->dirtyseq();
94 				isdirty = true;
95 			}
96 
97 	return isdirty;
98 }
99 
100 
101 //**************************************************************************
102 //  SCANLINE RASTERIZERS
103 //**************************************************************************
104 
105 //-------------------------------------------------
106 //  scanline_draw_opaque_null - draw to a nullptr
107 //  bitmap, setting priority only
108 //-------------------------------------------------
109 
scanline_draw_opaque_null(int count,u8 * pri,u32 pcode)110 inline void tilemap_t::scanline_draw_opaque_null(int count, u8 *pri, u32 pcode)
111 {
112 	// skip entirely if not changing priority
113 	if (pcode == 0xff00)
114 		return;
115 
116 	// update priority across the scanline
117 	for (int i = 0; i < count; i++)
118 		pri[i] = (pri[i] & (pcode >> 8)) | pcode;
119 }
120 
121 
122 //-------------------------------------------------
123 //  scanline_draw_masked_null - draw to a nullptr
124 //  bitmap using a mask, setting priority only
125 //-------------------------------------------------
126 
scanline_draw_masked_null(const u8 * maskptr,int mask,int value,int count,u8 * pri,u32 pcode)127 inline void tilemap_t::scanline_draw_masked_null(const u8 *maskptr, int mask, int value, int count, u8 *pri, u32 pcode)
128 {
129 	// skip entirely if not changing priority
130 	if (pcode == 0xff00)
131 		return;
132 
133 	// update priority across the scanline, checking the mask
134 	for (int i = 0; i < count; i++)
135 		if ((maskptr[i] & mask) == value)
136 			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
137 }
138 
139 
140 
141 //-------------------------------------------------
142 //  scanline_draw_opaque_ind16 - draw to a 16bpp
143 //  indexed bitmap
144 //-------------------------------------------------
145 
scanline_draw_opaque_ind16(u16 * dest,const u16 * source,int count,u8 * pri,u32 pcode)146 inline void tilemap_t::scanline_draw_opaque_ind16(u16 *dest, const u16 *source, int count, u8 *pri, u32 pcode)
147 {
148 	// special case for no palette offset
149 	int pal = pcode >> 16;
150 	if (pal == 0)
151 	{
152 		// use memcpy which should be well-optimized for the platform
153 		memcpy(dest, source, count * 2);
154 
155 		// skip the rest if not changing priority
156 		if (pcode == 0xff00)
157 			return;
158 
159 		// update priority across the scanline
160 		for (int i = 0; i < count; i++)
161 			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
162 	}
163 
164 	// priority case
165 	else if ((pcode & 0xffff) != 0xff00)
166 	{
167 		for (int i = 0; i < count; i++)
168 		{
169 			dest[i] = source[i] + pal;
170 			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
171 		}
172 	}
173 
174 	// no priority case
175 	else
176 	{
177 		for (int i = 0; i < count; i++)
178 			dest[i] = source[i] + pal;
179 	}
180 }
181 
182 
183 //-------------------------------------------------
184 //  scanline_draw_masked_ind16 - draw to a 16bpp
185 //  indexed bitmap using a mask
186 //-------------------------------------------------
187 
scanline_draw_masked_ind16(u16 * dest,const u16 * source,const u8 * maskptr,int mask,int value,int count,u8 * pri,u32 pcode)188 inline void tilemap_t::scanline_draw_masked_ind16(u16 *dest, const u16 *source, const u8 *maskptr, int mask, int value, int count, u8 *pri, u32 pcode)
189 {
190 	int pal = pcode >> 16;
191 
192 	// priority case
193 	if ((pcode & 0xffff) != 0xff00)
194 	{
195 		for (int i = 0; i < count; i++)
196 			if ((maskptr[i] & mask) == value)
197 			{
198 				dest[i] = source[i] + pal;
199 				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
200 			}
201 	}
202 
203 	// no priority case
204 	else
205 	{
206 		for (int i = 0; i < count; i++)
207 			if ((maskptr[i] & mask) == value)
208 				dest[i] = source[i] + pal;
209 	}
210 }
211 
212 
213 
214 //-------------------------------------------------
215 //  scanline_draw_opaque_rgb32 - draw to a 32bpp
216 //  RGB bitmap
217 //-------------------------------------------------
218 
scanline_draw_opaque_rgb32(u32 * dest,const u16 * source,int count,const rgb_t * pens,u8 * pri,u32 pcode)219 inline void tilemap_t::scanline_draw_opaque_rgb32(u32 *dest, const u16 *source, int count, const rgb_t *pens, u8 *pri, u32 pcode)
220 {
221 	const rgb_t *clut = &pens[pcode >> 16];
222 
223 	// priority case
224 	if ((pcode & 0xffff) != 0xff00)
225 	{
226 		for (int i = 0; i < count; i++)
227 		{
228 			dest[i] = clut[source[i]];
229 			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
230 		}
231 	}
232 
233 	// no priority case
234 	else
235 	{
236 		for (int i = 0; i < count; i++)
237 			dest[i] = clut[source[i]];
238 	}
239 }
240 
241 
242 //-------------------------------------------------
243 //  scanline_draw_masked_rgb32 - draw to a 32bpp
244 //  RGB bitmap using a mask
245 //-------------------------------------------------
246 
scanline_draw_masked_rgb32(u32 * dest,const u16 * source,const u8 * maskptr,int mask,int value,int count,const rgb_t * pens,u8 * pri,u32 pcode)247 inline void tilemap_t::scanline_draw_masked_rgb32(u32 *dest, const u16 *source, const u8 *maskptr, int mask, int value, int count, const rgb_t *pens, u8 *pri, u32 pcode)
248 {
249 	const rgb_t *clut = &pens[pcode >> 16];
250 
251 	// priority case
252 	if ((pcode & 0xffff) != 0xff00)
253 	{
254 		for (int i = 0; i < count; i++)
255 			if ((maskptr[i] & mask) == value)
256 			{
257 				dest[i] = clut[source[i]];
258 				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
259 			}
260 	}
261 
262 	// no priority case
263 	else
264 	{
265 		for (int i = 0; i < count; i++)
266 			if ((maskptr[i] & mask) == value)
267 				dest[i] = clut[source[i]];
268 	}
269 }
270 
271 
272 //-------------------------------------------------
273 //  scanline_draw_opaque_rgb32_alpha - draw to a
274 //  32bpp RGB bitmap with alpha blending
275 //-------------------------------------------------
276 
scanline_draw_opaque_rgb32_alpha(u32 * dest,const u16 * source,int count,const rgb_t * pens,u8 * pri,u32 pcode,u8 alpha)277 inline void tilemap_t::scanline_draw_opaque_rgb32_alpha(u32 *dest, const u16 *source, int count, const rgb_t *pens, u8 *pri, u32 pcode, u8 alpha)
278 {
279 	const rgb_t *clut = &pens[pcode >> 16];
280 
281 	// priority case
282 	if ((pcode & 0xffff) != 0xff00)
283 	{
284 		for (int i = 0; i < count; i++)
285 		{
286 			dest[i] = alpha_blend_r32(dest[i], clut[source[i]], alpha);
287 			pri[i] = (pri[i] & (pcode >> 8)) | pcode;
288 		}
289 	}
290 
291 	// no priority case
292 	else
293 	{
294 		for (int i = 0; i < count; i++)
295 			dest[i] = alpha_blend_r32(dest[i], clut[source[i]], alpha);
296 	}
297 }
298 
299 
300 //-------------------------------------------------
301 //  scanline_draw_masked_rgb32_alpha - draw to a
302 //  32bpp RGB bitmap using a mask and alpha
303 //  blending
304 //-------------------------------------------------
305 
scanline_draw_masked_rgb32_alpha(u32 * dest,const u16 * source,const u8 * maskptr,int mask,int value,int count,const rgb_t * pens,u8 * pri,u32 pcode,u8 alpha)306 inline void tilemap_t::scanline_draw_masked_rgb32_alpha(u32 *dest, const u16 *source, const u8 *maskptr, int mask, int value, int count, const rgb_t *pens, u8 *pri, u32 pcode, u8 alpha)
307 {
308 	const rgb_t *clut = &pens[pcode >> 16];
309 
310 	// priority case
311 	if ((pcode & 0xffff) != 0xff00)
312 	{
313 		for (int i = 0; i < count; i++)
314 			if ((maskptr[i] & mask) == value)
315 			{
316 				dest[i] = alpha_blend_r32(dest[i], clut[source[i]], alpha);
317 				pri[i] = (pri[i] & (pcode >> 8)) | pcode;
318 			}
319 	}
320 
321 	// no priority case
322 	else
323 	{
324 		for (int i = 0; i < count; i++)
325 			if ((maskptr[i] & mask) == value)
326 				dest[i] = alpha_blend_r32(dest[i], clut[source[i]], alpha);
327 	}
328 }
329 
330 
331 //**************************************************************************
332 //  TILEMAP CREATION AND CONFIGURATION
333 //**************************************************************************
334 
335 //-------------------------------------------------
336 //  tilemap_t - constructor
337 //-------------------------------------------------
338 
tilemap_t(device_t & owner)339 tilemap_t::tilemap_t(device_t &owner)
340 	: m_mapper(owner)
341 	, m_tile_get_info(owner)
342 {
343 	// until init() is called, data is floating; this is deliberate
344 }
345 
346 
347 //-------------------------------------------------
348 //  init - initialize the tilemap
349 //-------------------------------------------------
350 
init_common(tilemap_manager & manager,device_gfx_interface & decoder,tilemap_get_info_delegate tile_get_info,u16 tilewidth,u16 tileheight,u32 cols,u32 rows)351 void tilemap_t::init_common(
352 		tilemap_manager &manager,
353 		device_gfx_interface &decoder,
354 		tilemap_get_info_delegate tile_get_info,
355 		u16 tilewidth,
356 		u16 tileheight,
357 		u32 cols,
358 		u32 rows)
359 {
360 	// populate managers and devices
361 	m_manager = &manager;
362 	m_device = dynamic_cast<device_t *>(this);
363 	m_palette = &decoder.palette();
364 	m_next = nullptr;
365 	m_user_data = nullptr;
366 
367 	// populate tilemap metrics
368 	m_rows = rows;
369 	m_cols = cols;
370 	m_tilewidth = tilewidth;
371 	m_tileheight = tileheight;
372 	m_width = cols * tilewidth;
373 	m_height = rows * tileheight;
374 
375 	// initialize tile information geters
376 	m_tile_get_info = tile_get_info;
377 
378 	// reset global states
379 	m_enable = true;
380 	m_attributes = 0;
381 	m_all_tiles_dirty = true;
382 	m_all_tiles_clean = false;
383 	m_palette_offset = 0;
384 	m_gfx_used = 0;
385 	memset(m_gfx_dirtyseq, 0, sizeof(m_gfx_dirtyseq));
386 
387 	// reset scroll information
388 	m_scrollrows = 1;
389 	m_scrollcols = 1;
390 	m_rowscroll.resize(m_height);
391 	memset(&m_rowscroll[0], 0, m_height*sizeof(m_rowscroll[0]));
392 	m_colscroll.resize(m_width);
393 	memset(&m_colscroll[0], 0, m_width*sizeof(m_colscroll[0]));
394 	m_dx = 0;
395 	m_dx_flipped = 0;
396 	m_dy = 0;
397 	m_dy_flipped = 0;
398 
399 	// allocate pixmap
400 	m_pixmap.allocate(m_width, m_height);
401 
402 	// allocate transparency mapping
403 	m_flagsmap.allocate(m_width, m_height);
404 	memset(m_pen_to_flags, 0, sizeof(m_pen_to_flags));
405 
406 	// create the initial mappings
407 	mappings_create();
408 
409 	// set up the default tile data
410 	memset(&m_tileinfo, 0, sizeof(m_tileinfo));
411 	m_tileinfo.decoder = &decoder;
412 	m_tileinfo.pen_mask = 0xff;
413 	m_tileinfo.gfxnum = 0xff;
414 
415 	// allocate transparency mapping data
416 	for (int group = 0; group < TILEMAP_NUM_GROUPS; group++)
417 		map_pens_to_layer(group, 0, 0, TILEMAP_PIXEL_LAYER0);
418 
419 	// save relevant state
420 	int instance = manager.alloc_instance();
421 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_enable));
422 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_attributes));
423 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_palette_offset));
424 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_scrollrows));
425 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_scrollcols));
426 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_rowscroll));
427 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_colscroll));
428 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_dx));
429 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_dx_flipped));
430 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_dy));
431 	machine().save().save_item(m_device, "tilemap", nullptr, instance, NAME(m_dy_flipped));
432 
433 	// reset everything after a load
434 	machine().save().register_postload(save_prepost_delegate(FUNC(tilemap_t::postload), this));
435 }
436 
init(tilemap_manager & manager,device_gfx_interface & decoder,tilemap_get_info_delegate tile_get_info,tilemap_mapper_delegate mapper,u16 tilewidth,u16 tileheight,u32 cols,u32 rows)437 tilemap_t &tilemap_t::init(
438 		tilemap_manager &manager,
439 		device_gfx_interface &decoder,
440 		tilemap_get_info_delegate tile_get_info,
441 		tilemap_mapper_delegate mapper,
442 		u16 tilewidth,
443 		u16 tileheight,
444 		u32 cols,
445 		u32 rows)
446 {
447 	// populate logical <-> memory mappings
448 	m_mapper = mapper;
449 
450 	init_common(manager, decoder, tile_get_info, tilewidth, tileheight, cols, rows);
451 
452 	return *this;
453 }
454 
init(tilemap_manager & manager,device_gfx_interface & decoder,tilemap_get_info_delegate tile_get_info,tilemap_standard_mapper mapper,u16 tilewidth,u16 tileheight,u32 cols,u32 rows)455 tilemap_t &tilemap_t::init(
456 		tilemap_manager &manager,
457 		device_gfx_interface &decoder,
458 		tilemap_get_info_delegate tile_get_info,
459 		tilemap_standard_mapper mapper,
460 		u16 tilewidth,
461 		u16 tileheight,
462 		u32 cols,
463 		u32 rows)
464 {
465 	// populate logical <-> memory mappings
466 	switch (mapper)
467 	{
468 		case TILEMAP_SCAN_ROWS:         m_mapper.set(*this, FUNC(tilemap_t::scan_rows));         break;
469 		case TILEMAP_SCAN_ROWS_FLIP_X:  m_mapper.set(*this, FUNC(tilemap_t::scan_rows_flip_x));  break;
470 		case TILEMAP_SCAN_ROWS_FLIP_Y:  m_mapper.set(*this, FUNC(tilemap_t::scan_rows_flip_y));  break;
471 		case TILEMAP_SCAN_ROWS_FLIP_XY: m_mapper.set(*this, FUNC(tilemap_t::scan_rows_flip_xy)); break;
472 		case TILEMAP_SCAN_COLS:         m_mapper.set(*this, FUNC(tilemap_t::scan_cols));         break;
473 		case TILEMAP_SCAN_COLS_FLIP_X:  m_mapper.set(*this, FUNC(tilemap_t::scan_cols_flip_x));  break;
474 		case TILEMAP_SCAN_COLS_FLIP_Y:  m_mapper.set(*this, FUNC(tilemap_t::scan_cols_flip_y));  break;
475 		case TILEMAP_SCAN_COLS_FLIP_XY: m_mapper.set(*this, FUNC(tilemap_t::scan_cols_flip_xy)); break;
476 		default: throw emu_fatalerror("Tilemap init unknown mapper %d", mapper);
477 	}
478 
479 	init_common(manager, decoder, tile_get_info, tilewidth, tileheight, cols, rows);
480 
481 	return *this;
482 }
483 
484 
485 //-------------------------------------------------
486 //  ~tilemap_t - destructor
487 //-------------------------------------------------
488 
~tilemap_t()489 tilemap_t::~tilemap_t()
490 {
491 }
492 
493 
494 //-------------------------------------------------
495 //  mark_tile_dirty - mark a single tile dirty
496 //  based on its memory index
497 //-------------------------------------------------
498 
mark_tile_dirty(tilemap_memory_index memindex)499 void tilemap_t::mark_tile_dirty(tilemap_memory_index memindex)
500 {
501 	// only mark if within range
502 	if (memindex < m_memory_to_logical.size())
503 	{
504 		// there may be no logical index for a given memory index
505 		logical_index logindex = m_memory_to_logical[memindex];
506 		if (logindex != INVALID_LOGICAL_INDEX)
507 		{
508 			m_tileflags[logindex] = TILE_FLAG_DIRTY;
509 			m_all_tiles_clean = false;
510 		}
511 	}
512 }
513 
514 
515 //-------------------------------------------------
516 //  map_pens_to_layer - specify the mapping of one
517 //  or more pens (where (<pen> & mask) == pen) to
518 //  a layer
519 //-------------------------------------------------
520 
map_pens_to_layer(int group,pen_t pen,pen_t mask,u8 layermask)521 void tilemap_t::map_pens_to_layer(int group, pen_t pen, pen_t mask, u8 layermask)
522 {
523 	assert(group < TILEMAP_NUM_GROUPS);
524 	assert((layermask & TILEMAP_PIXEL_CATEGORY_MASK) == 0);
525 
526 	// we start at the index where (pen & mask) == pen, and all other bits are 0
527 	pen_t start = pen & mask;
528 
529 	// we stop at the index where (pen & mask) == pen, and all other bits are 1
530 	pen_t stop = start | ~mask;
531 
532 	// clamp to the number of entries actually there
533 	stop = std::min(stop, MAX_PEN_TO_FLAGS - 1);
534 
535 	// iterate and set
536 	u8 *array = m_pen_to_flags + group * MAX_PEN_TO_FLAGS;
537 	bool changed = false;
538 	for (pen_t cur = start; cur <= stop; cur++)
539 		if ((cur & mask) == pen && array[cur] != layermask)
540 		{
541 			changed = true;
542 			array[cur] = layermask;
543 		}
544 
545 	// everything gets dirty if anything changed
546 	if (changed)
547 		mark_all_dirty();
548 }
549 
550 
551 //-------------------------------------------------
552 //  set_transparent_pen - set a single transparent
553 //  pen into the tilemap, mapping all other pens
554 //  to layer 0
555 //-------------------------------------------------
556 
set_transparent_pen(pen_t pen)557 void tilemap_t::set_transparent_pen(pen_t pen)
558 {
559 	// reset the whole pen map to opaque
560 	map_pens_to_layer(0, 0, 0, TILEMAP_PIXEL_LAYER0);
561 
562 	// set the single pen to transparent
563 	map_pen_to_layer(0, pen, TILEMAP_PIXEL_TRANSPARENT);
564 }
565 
566 
567 //-------------------------------------------------
568 //  set_transmask - set up the first 32 pens using
569 //  a foreground mask (mapping to layer 0) and a
570 //  background mask (mapping to layer 1)
571 //-------------------------------------------------
572 
set_transmask(int group,u32 fgmask,u32 bgmask)573 void tilemap_t::set_transmask(int group, u32 fgmask, u32 bgmask)
574 {
575 	// iterate over all 32 pens specified
576 	for (pen_t pen = 0; pen < 32; pen++)
577 	{
578 		u8 fgbits = ((fgmask >> pen) & 1) ? TILEMAP_PIXEL_TRANSPARENT : TILEMAP_PIXEL_LAYER0;
579 		u8 bgbits = ((bgmask >> pen) & 1) ? TILEMAP_PIXEL_TRANSPARENT : TILEMAP_PIXEL_LAYER1;
580 		map_pen_to_layer(group, pen, fgbits | bgbits);
581 	}
582 }
583 
584 
585 //-------------------------------------------------
586 //  configure_groups - configure groups so that
587 //  when group == color, pens whose indirect value
588 //  matches the given transcolor are transparent
589 //-------------------------------------------------
590 
configure_groups(gfx_element & gfx,indirect_pen_t transcolor)591 void tilemap_t::configure_groups(gfx_element &gfx, indirect_pen_t transcolor)
592 {
593 	assert(gfx.colors() <= TILEMAP_NUM_GROUPS);
594 
595 	// iterate over all colors in the tilemap
596 	for (u32 color = 0; color < gfx.colors(); color++)
597 		set_transmask(color, m_palette->transpen_mask(gfx, color, transcolor), 0);
598 }
599 
600 
601 
602 //**************************************************************************
603 //  COMMON LOGICAL-TO-MEMORY MAPPERS
604 //**************************************************************************
605 
606 //-------------------------------------------------
607 // scan_rows
608 // scan_rows_flip_x
609 // scan_rows_flip_y
610 // scan_rows_flip_xy - scan in row-major
611 //  order with optional flipping
612 //-------------------------------------------------
613 
scan_rows(u32 col,u32 row,u32 num_cols,u32 num_rows)614 tilemap_memory_index tilemap_t::scan_rows(u32 col, u32 row, u32 num_cols, u32 num_rows)
615 {
616 	return row * num_cols + col;
617 }
618 
scan_rows_flip_x(u32 col,u32 row,u32 num_cols,u32 num_rows)619 tilemap_memory_index tilemap_t::scan_rows_flip_x(u32 col, u32 row, u32 num_cols, u32 num_rows)
620 {
621 	return row * num_cols + (num_cols - 1 - col);
622 }
623 
scan_rows_flip_y(u32 col,u32 row,u32 num_cols,u32 num_rows)624 tilemap_memory_index tilemap_t::scan_rows_flip_y(u32 col, u32 row, u32 num_cols, u32 num_rows)
625 {
626 	return (num_rows - 1 - row) * num_cols + col;
627 }
628 
scan_rows_flip_xy(u32 col,u32 row,u32 num_cols,u32 num_rows)629 tilemap_memory_index tilemap_t::scan_rows_flip_xy(u32 col, u32 row, u32 num_cols, u32 num_rows)
630 {
631 	return (num_rows - 1 - row) * num_cols + (num_cols - 1 - col);
632 }
633 
634 
635 //-------------------------------------------------
636 //  scan_cols
637 //  scan_cols_flip_x
638 //  scan_cols_flip_y
639 //  scan_cols_flip_xy - scan in column-
640 //  major order with optional flipping
641 //-------------------------------------------------
642 
scan_cols(u32 col,u32 row,u32 num_cols,u32 num_rows)643 tilemap_memory_index tilemap_t::scan_cols(u32 col, u32 row, u32 num_cols, u32 num_rows)
644 {
645 	return col * num_rows + row;
646 }
647 
scan_cols_flip_x(u32 col,u32 row,u32 num_cols,u32 num_rows)648 tilemap_memory_index tilemap_t::scan_cols_flip_x(u32 col, u32 row, u32 num_cols, u32 num_rows)
649 {
650 	return (num_cols - 1 - col) * num_rows + row;
651 }
652 
scan_cols_flip_y(u32 col,u32 row,u32 num_cols,u32 num_rows)653 tilemap_memory_index tilemap_t::scan_cols_flip_y(u32 col, u32 row, u32 num_cols, u32 num_rows)
654 {
655 	return col * num_rows + (num_rows - 1 - row);
656 }
657 
scan_cols_flip_xy(u32 col,u32 row,u32 num_cols,u32 num_rows)658 tilemap_memory_index tilemap_t::scan_cols_flip_xy(u32 col, u32 row, u32 num_cols, u32 num_rows)
659 {
660 	return (num_cols - 1 - col) * num_rows + (num_rows - 1 - row);
661 }
662 
663 
664 //-------------------------------------------------
665 //  postload - after loading a save state
666 //  invalidate everything
667 //-------------------------------------------------
668 
postload()669 void tilemap_t::postload()
670 {
671 	mappings_update();
672 }
673 
674 
675 
676 //**************************************************************************
677 //  LOGICAL <-> MEMORY INDEX MAPPING
678 //**************************************************************************
679 
680 //-------------------------------------------------
681 //  mappings_create - allocate memory for the
682 //  mapping tables and compute their extents
683 //-------------------------------------------------
684 
mappings_create()685 void tilemap_t::mappings_create()
686 {
687 	// compute the maximum logical index
688 	const logical_index max_logical_index = m_rows * m_cols;
689 
690 	// compute the maximum memory index
691 	tilemap_memory_index max_memory_index = 0;
692 	for (u32 row = 0; row < m_rows; row++)
693 		for (u32 col = 0; col < m_cols; col++)
694 		{
695 			tilemap_memory_index memindex = memory_index(col, row);
696 			max_memory_index = std::max(max_memory_index, memindex);
697 		}
698 	max_memory_index++;
699 
700 	// allocate the necessary mappings
701 	m_memory_to_logical.resize(max_memory_index);
702 	m_logical_to_memory.resize(max_logical_index);
703 	m_tileflags.resize(max_logical_index);
704 
705 	// update the mappings
706 	mappings_update();
707 }
708 
709 
710 //-------------------------------------------------
711 //  mappings_update - update the mappings after
712 //  a major change (flip x/y changes)
713 //-------------------------------------------------
714 
mappings_update()715 void tilemap_t::mappings_update()
716 {
717 	// initialize all the mappings to invalid values
718 	memset(&m_memory_to_logical[0], 0xff, m_memory_to_logical.size() * sizeof(m_memory_to_logical[0]));
719 
720 	// now iterate over all logical indexes and populate the memory index
721 	for (logical_index logindex = 0; logindex < m_logical_to_memory.size(); logindex++)
722 	{
723 		u32 logical_col = logindex % m_cols;
724 		u32 logical_row = logindex / m_cols;
725 		tilemap_memory_index memindex = memory_index(logical_col, logical_row);
726 
727 		// apply tilemap flip to get the final location to store
728 		if (m_attributes & TILEMAP_FLIPX)
729 			logical_col = (m_cols - 1) - logical_col;
730 		if (m_attributes & TILEMAP_FLIPY)
731 			logical_row = (m_rows - 1) - logical_row;
732 		u32 flipped_logindex = logical_row * m_cols + logical_col;
733 
734 		// fill in entries in both arrays
735 		m_memory_to_logical[memindex] = flipped_logindex;
736 		m_logical_to_memory[flipped_logindex] = memindex;
737 	}
738 
739 	// mark the whole tilemap dirty
740 	mark_all_dirty();
741 }
742 
743 
744 
745 //**************************************************************************
746 //  TILE RENDERING
747 //**************************************************************************
748 
realize_all_dirty_tiles()749 inline void tilemap_t::realize_all_dirty_tiles()
750 {
751 	// if all the tiles are marked dirty, or something in the gfx has changed,
752 	// flush the dirty status to all tiles
753 	if (m_all_tiles_dirty || gfx_elements_changed())
754 	{
755 		memset(&m_tileflags[0], TILE_FLAG_DIRTY, m_tileflags.size());
756 		m_all_tiles_dirty = false;
757 		m_gfx_used = 0;
758 	}
759 }
760 
761 //-------------------------------------------------
762 //  pixmap_update - update the entire pixmap
763 //-------------------------------------------------
764 
pixmap_update()765 void tilemap_t::pixmap_update()
766 {
767 	// if the graphics changed, we need to mark everything dirty
768 	if (gfx_elements_changed())
769 		mark_all_dirty();
770 
771 	// if everything is clean, do nothing
772 	if (m_all_tiles_clean)
773 		return;
774 
775 g_profiler.start(PROFILER_TILEMAP_DRAW);
776 
777 	// flush the dirty state to all tiles as appropriate
778 	realize_all_dirty_tiles();
779 
780 	// iterate over rows and columns
781 	logical_index logindex = 0;
782 	for (u32 row = 0; row < m_rows; row++)
783 		for (u32 col = 0; col < m_cols; col++, logindex++)
784 			if (m_tileflags[logindex] == TILE_FLAG_DIRTY)
785 				tile_update(logindex, col, row);
786 
787 	// mark it all clean
788 	m_all_tiles_clean = true;
789 
790 g_profiler.stop();
791 }
792 
793 
794 //-------------------------------------------------
795 //  tile_update - update a single dirty tile
796 //-------------------------------------------------
797 
tile_update(logical_index logindex,u32 col,u32 row)798 void tilemap_t::tile_update(logical_index logindex, u32 col, u32 row)
799 {
800 g_profiler.start(PROFILER_TILEMAP_UPDATE);
801 
802 	// call the get info callback for the associated memory index
803 	tilemap_memory_index memindex = m_logical_to_memory[logindex];
804 	m_tile_get_info(*this, m_tileinfo, memindex);
805 
806 	// apply the global tilemap flip to the returned flip flags
807 	u32 flags = m_tileinfo.flags ^ (m_attributes & 0x03);
808 
809 	// draw the tile, using either direct or transparent
810 	u32 x0 = m_tilewidth * col;
811 	u32 y0 = m_tileheight * row;
812 	m_tileflags[logindex] = tile_draw(m_tileinfo.pen_data, x0, y0,
813 		m_tileinfo.palette_base, m_tileinfo.category, m_tileinfo.group, flags, m_tileinfo.pen_mask);
814 
815 	// if mask data is specified, apply it
816 	if ((flags & (TILE_FORCE_LAYER0 | TILE_FORCE_LAYER1 | TILE_FORCE_LAYER2)) == 0 && m_tileinfo.mask_data != nullptr)
817 		m_tileflags[logindex] = tile_apply_bitmask(m_tileinfo.mask_data, x0, y0, m_tileinfo.category, flags);
818 
819 	// track which gfx have been used for this tilemap
820 	if (m_tileinfo.gfxnum != 0xff && (m_gfx_used & (1 << m_tileinfo.gfxnum)) == 0)
821 	{
822 		m_gfx_used |= 1 << m_tileinfo.gfxnum;
823 		m_gfx_dirtyseq[m_tileinfo.gfxnum] = m_tileinfo.decoder->gfx(m_tileinfo.gfxnum)->dirtyseq();
824 	}
825 
826 g_profiler.stop();
827 }
828 
829 
830 //-------------------------------------------------
831 //  tile_draw - draw a single tile to the
832 //  tilemap's internal pixmap, using the pen as
833 //  the pen_to_flags lookup value, and adding
834 //  the palette_base
835 //-------------------------------------------------
836 
tile_draw(const u8 * pendata,u32 x0,u32 y0,u32 palette_base,u8 category,u8 group,u8 flags,u8 pen_mask)837 u8 tilemap_t::tile_draw(const u8 *pendata, u32 x0, u32 y0, u32 palette_base, u8 category, u8 group, u8 flags, u8 pen_mask)
838 {
839 	// OR in the force layer flags
840 	category |= flags & (TILE_FORCE_LAYER0 | TILE_FORCE_LAYER1 | TILE_FORCE_LAYER2);
841 
842 	// if we're vertically flipped, point to the bottom row and work backwards
843 	int dy0 = 1;
844 	if (flags & TILE_FLIPY)
845 	{
846 		y0 += m_tileheight - 1;
847 		dy0 = -1;
848 	}
849 
850 	// if we're horizontally flipped, point to the rightmost column and work backwards
851 	int dx0 = 1;
852 	if (flags & TILE_FLIPX)
853 	{
854 		x0 += m_tilewidth - 1;
855 		dx0 = -1;
856 	}
857 
858 	// iterate over rows
859 	const u8 *penmap = m_pen_to_flags + group * MAX_PEN_TO_FLAGS;
860 	u8 andmask = ~0, ormask = 0;
861 	for (u16 ty = 0; ty < m_tileheight; ty++)
862 	{
863 		u16 *pixptr = &m_pixmap.pix(y0, x0);
864 		u8 *flagsptr = &m_flagsmap.pix(y0, x0);
865 
866 		// pre-advance to the next row
867 		y0 += dy0;
868 
869 		// 8bpp data
870 		int xoffs = 0;
871 		for (u16 tx = 0; tx < m_tilewidth; tx++)
872 		{
873 			u8 pen = (*pendata++) & pen_mask;
874 			u8 map = penmap[pen];
875 			pixptr[xoffs] = palette_base + pen;
876 			flagsptr[xoffs] = map | category;
877 			andmask &= map;
878 			ormask |= map;
879 			xoffs += dx0;
880 		}
881 	}
882 	return andmask ^ ormask;
883 }
884 
885 
886 //-------------------------------------------------
887 //  tile_apply_bitmask - apply a bitmask to an
888 //  already-rendered tile by modifying the
889 //  flagsmap appropriately
890 //-------------------------------------------------
891 
tile_apply_bitmask(const u8 * maskdata,u32 x0,u32 y0,u8 category,u8 flags)892 u8 tilemap_t::tile_apply_bitmask(const u8 *maskdata, u32 x0, u32 y0, u8 category, u8 flags)
893 {
894 	// if we're vertically flipped, point to the bottom row and work backwards
895 	int dy0 = 1;
896 	if (flags & TILE_FLIPY)
897 	{
898 		y0 += m_tileheight - 1;
899 		dy0 = -1;
900 	}
901 
902 	// if we're horizontally flipped, point to the rightmost column and work backwards
903 	int dx0 = 1;
904 	if (flags & TILE_FLIPX)
905 	{
906 		x0 += m_tilewidth - 1;
907 		dx0 = -1;
908 	}
909 
910 	// iterate over rows
911 	u8 andmask = ~0, ormask = 0;
912 	int bitoffs = 0;
913 	for (u16 ty = 0; ty < m_tileheight; ty++)
914 	{
915 		// pre-advance to the next row
916 		u8 *flagsptr = &m_flagsmap.pix(y0, x0);
917 		y0 += dy0;
918 
919 		// anywhere the bitmask is 0 should be transparent
920 		int xoffs = 0;
921 		for (u16 tx = 0; tx < m_tilewidth; tx++)
922 		{
923 			u8 map = flagsptr[xoffs];
924 
925 			if ((maskdata[bitoffs / 8] & (0x80 >> (bitoffs & 7))) == 0)
926 				map = flagsptr[xoffs] = TILEMAP_PIXEL_TRANSPARENT | category;
927 			andmask &= map;
928 			ormask |= map;
929 			bitoffs++;
930 			xoffs += dx0;
931 		}
932 	}
933 	return andmask ^ ormask;
934 }
935 
936 
937 
938 //**************************************************************************
939 //  DRAWING HELPERS
940 //**************************************************************************
941 
942 //-------------------------------------------------
943 //  configure_blit_parameters - fill in the
944 //  standard blit parameters based on the input
945 //  data; this code is shared by normal, roz,
946 //  and indexed drawing code
947 //-------------------------------------------------
948 
configure_blit_parameters(blit_parameters & blit,bitmap_ind8 & priority_bitmap,const rectangle & cliprect,u32 flags,u8 priority,u8 priority_mask)949 void tilemap_t::configure_blit_parameters(blit_parameters &blit, bitmap_ind8 &priority_bitmap, const rectangle &cliprect, u32 flags, u8 priority, u8 priority_mask)
950 {
951 	// set the target bitmap
952 	blit.priority = &priority_bitmap;
953 	blit.cliprect = cliprect;
954 
955 	// set the priority code and alpha
956 	blit.tilemap_priority_code = priority | (priority_mask << 8) | (m_palette_offset << 16);
957 	blit.alpha = (flags & TILEMAP_DRAW_ALPHA_FLAG) ? (flags >> 24) : 0xff;
958 
959 	// tile priority; unless otherwise specified, draw anything in layer 0
960 	blit.mask = TILEMAP_PIXEL_CATEGORY_MASK;
961 	blit.value = flags & TILEMAP_PIXEL_CATEGORY_MASK;
962 
963 	// if no layers specified, draw layer 0
964 	if ((flags & (TILEMAP_DRAW_LAYER0 | TILEMAP_DRAW_LAYER1 | TILEMAP_DRAW_LAYER2)) == 0)
965 		flags |= TILEMAP_DRAW_LAYER0;
966 
967 	// OR in the bits from the draw masks
968 	blit.mask |= flags & (TILEMAP_DRAW_LAYER0 | TILEMAP_DRAW_LAYER1 | TILEMAP_DRAW_LAYER2);
969 	blit.value |= flags & (TILEMAP_DRAW_LAYER0 | TILEMAP_DRAW_LAYER1 | TILEMAP_DRAW_LAYER2);
970 
971 	// for all-opaque rendering, don't check any of the layer bits
972 	if (flags & TILEMAP_DRAW_OPAQUE)
973 	{
974 		blit.mask &= ~(TILEMAP_PIXEL_LAYER0 | TILEMAP_PIXEL_LAYER1 | TILEMAP_PIXEL_LAYER2);
975 		blit.value &= ~(TILEMAP_PIXEL_LAYER0 | TILEMAP_PIXEL_LAYER1 | TILEMAP_PIXEL_LAYER2);
976 	}
977 
978 	// don't check category if requested
979 	if (flags & TILEMAP_DRAW_ALL_CATEGORIES)
980 	{
981 		blit.mask &= ~TILEMAP_PIXEL_CATEGORY_MASK;
982 		blit.value &= ~TILEMAP_PIXEL_CATEGORY_MASK;
983 	}
984 }
985 
986 
987 //-------------------------------------------------
988 //  draw_common - draw a tilemap to the
989 //  destination with clipping; pixels apply
990 //  priority/priority_mask to the priority bitmap
991 //-------------------------------------------------
992 
993 template<class _BitmapClass>
draw_common(screen_device & screen,_BitmapClass & dest,const rectangle & cliprect,u32 flags,u8 priority,u8 priority_mask)994 void tilemap_t::draw_common(screen_device &screen, _BitmapClass &dest, const rectangle &cliprect, u32 flags, u8 priority, u8 priority_mask)
995 {
996 	// skip if disabled
997 	if (!m_enable)
998 		return;
999 
1000 g_profiler.start(PROFILER_TILEMAP_DRAW);
1001 	// configure the blit parameters based on the input parameters
1002 	blit_parameters blit;
1003 	configure_blit_parameters(blit, screen.priority(), cliprect, flags, priority, priority_mask);
1004 	assert(dest.cliprect().contains(cliprect));
1005 	assert(screen.cliprect().contains(cliprect) || blit.tilemap_priority_code == 0xff00);
1006 
1007 	// flush the dirty state to all tiles as appropriate
1008 	realize_all_dirty_tiles();
1009 
1010 	// flip the tilemap around the center of the visible area
1011 	rectangle const visarea = screen.visible_area();
1012 	u32 const xextent = visarea.right() + visarea.left() + 1; // x0 + x1 + 1 for calculating horizontal centre as (x0 + x1 + 1) >> 1
1013 	u32 const yextent = visarea.bottom() + visarea.top() + 1; // y0 + y1 + 1 for calculating vertical centre as (y0 + y1 + 1) >> 1
1014 
1015 	// XY scrolling playfield
1016 	if (m_scrollrows == 1 && m_scrollcols == 1)
1017 	{
1018 		// iterate to handle wraparound
1019 		int scrollx = effective_rowscroll(0, xextent);
1020 		int scrolly = effective_colscroll(0, yextent);
1021 		for (int ypos = scrolly - m_height; ypos <= blit.cliprect.bottom(); ypos += m_height)
1022 			for (int xpos = scrollx - m_width; xpos <= blit.cliprect.right(); xpos += m_width)
1023 				draw_instance(screen, dest, blit, xpos, ypos);
1024 	}
1025 
1026 	// scrolling rows + vertical scroll
1027 	else if (m_scrollcols == 1)
1028 	{
1029 		const rectangle original_cliprect = blit.cliprect;
1030 
1031 		// iterate over Y to handle wraparound
1032 		int rowheight = m_height / m_scrollrows;
1033 		int scrolly = effective_colscroll(0, yextent);
1034 		for (int ypos = scrolly - m_height; ypos <= original_cliprect.bottom(); ypos += m_height)
1035 		{
1036 			int const firstrow = std::max((original_cliprect.top() - ypos) / rowheight, 0);
1037 			int const lastrow = std::min((original_cliprect.bottom() - ypos) / rowheight, s32(m_scrollrows) - 1);
1038 
1039 			// iterate over rows in the tilemap
1040 			int nextrow;
1041 			for (int currow = firstrow; currow <= lastrow; currow = nextrow)
1042 			{
1043 				// scan forward until we find a non-matching row
1044 				int scrollx = effective_rowscroll(currow, xextent);
1045 				for (nextrow = currow + 1; nextrow <= lastrow; nextrow++)
1046 					if (effective_rowscroll(nextrow, xextent) != scrollx)
1047 						break;
1048 
1049 				// skip if disabled
1050 				if (scrollx == TILE_LINE_DISABLED)
1051 					continue;
1052 
1053 				// update the cliprect just for this set of rows
1054 				blit.cliprect.sety(currow * rowheight + ypos, nextrow * rowheight - 1 + ypos);
1055 				blit.cliprect &= original_cliprect;
1056 
1057 				// iterate over X to handle wraparound
1058 				for (int xpos = scrollx - m_width; xpos <= original_cliprect.right(); xpos += m_width)
1059 					draw_instance(screen, dest, blit, xpos, ypos);
1060 			}
1061 		}
1062 	}
1063 
1064 	// scrolling columns + horizontal scroll
1065 	else if (m_scrollrows == 1)
1066 	{
1067 		const rectangle original_cliprect = blit.cliprect;
1068 
1069 		// iterate over columns in the tilemap
1070 		int scrollx = effective_rowscroll(0, xextent);
1071 		int colwidth = m_width / m_scrollcols;
1072 		int nextcol;
1073 		for (int curcol = 0; curcol < m_scrollcols; curcol = nextcol)
1074 		{
1075 			// scan forward until we find a non-matching column
1076 			int scrolly = effective_colscroll(curcol, yextent);
1077 			for (nextcol = curcol + 1; nextcol < m_scrollcols; nextcol++)
1078 				if (effective_colscroll(nextcol, yextent) != scrolly)
1079 					break;
1080 
1081 			// skip if disabled
1082 			if (scrolly == TILE_LINE_DISABLED)
1083 				continue;
1084 
1085 			// iterate over X to handle wraparound
1086 			for (int xpos = scrollx - m_width; xpos <= original_cliprect.right(); xpos += m_width)
1087 			{
1088 				// update the cliprect just for this set of columns
1089 				blit.cliprect.setx(curcol * colwidth + xpos, nextcol * colwidth - 1 + xpos);
1090 				blit.cliprect &= original_cliprect;
1091 
1092 				// iterate over Y to handle wraparound
1093 				for (int ypos = scrolly - m_height; ypos <= original_cliprect.bottom(); ypos += m_height)
1094 					draw_instance(screen, dest, blit, xpos, ypos);
1095 			}
1096 		}
1097 	}
1098 g_profiler.stop();
1099 }
1100 
draw(screen_device & screen,bitmap_ind16 & dest,const rectangle & cliprect,u32 flags,u8 priority,u8 priority_mask)1101 void tilemap_t::draw(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect, u32 flags, u8 priority, u8 priority_mask)
1102 { draw_common(screen, dest, cliprect, flags, priority, priority_mask); }
1103 
draw(screen_device & screen,bitmap_rgb32 & dest,const rectangle & cliprect,u32 flags,u8 priority,u8 priority_mask)1104 void tilemap_t::draw(screen_device &screen, bitmap_rgb32 &dest, const rectangle &cliprect, u32 flags, u8 priority, u8 priority_mask)
1105 { draw_common(screen, dest, cliprect, flags, priority, priority_mask); }
1106 
1107 
1108 //-------------------------------------------------
1109 //  draw_roz - draw a tilemap to the destination
1110 //  with clipping and arbitrary rotate/zoom; pixels
1111 //  apply priority/priority_mask to the priority
1112 //  bitmap
1113 //-------------------------------------------------
1114 
1115 template<class _BitmapClass>
draw_roz_common(screen_device & screen,_BitmapClass & dest,const rectangle & cliprect,u32 startx,u32 starty,int incxx,int incxy,int incyx,int incyy,bool wraparound,u32 flags,u8 priority,u8 priority_mask)1116 void tilemap_t::draw_roz_common(screen_device &screen, _BitmapClass &dest, const rectangle &cliprect,
1117 		u32 startx, u32 starty, int incxx, int incxy, int incyx, int incyy,
1118 		bool wraparound, u32 flags, u8 priority, u8 priority_mask)
1119 {
1120 // notes:
1121 // - startx and starty MUST be u32 for calculations to work correctly
1122 // - srcbim_width and height are assumed to be a power of 2 to speed up wraparound
1123 
1124 	// skip if disabled
1125 	if (!m_enable)
1126 		return;
1127 
1128 	// see if this is just a regular render and if so, do a regular render
1129 	if (incxx == (1 << 16) && incxy == 0 && incyx == 0 && incyy == (1 << 16) && wraparound)
1130 	{
1131 		set_scrollx(0, startx >> 16);
1132 		set_scrolly(0, starty >> 16);
1133 		draw(screen, dest, cliprect, flags, priority, priority_mask);
1134 		return;
1135 	}
1136 
1137 g_profiler.start(PROFILER_TILEMAP_DRAW_ROZ);
1138 	// configure the blit parameters
1139 	blit_parameters blit;
1140 	configure_blit_parameters(blit, screen.priority(), cliprect, flags, priority, priority_mask);
1141 	assert(dest.cliprect().contains(cliprect));
1142 	assert(screen.cliprect().contains(cliprect) || blit.tilemap_priority_code == 0xff00);
1143 
1144 	// get the full pixmap for the tilemap
1145 	pixmap();
1146 
1147 	// then do the roz copy
1148 	draw_roz_core(screen, dest, blit, startx, starty, incxx, incxy, incyx, incyy, wraparound);
1149 g_profiler.stop();
1150 }
1151 
draw_roz(screen_device & screen,bitmap_ind16 & dest,const rectangle & cliprect,u32 startx,u32 starty,int incxx,int incxy,int incyx,int incyy,bool wraparound,u32 flags,u8 priority,u8 priority_mask)1152 void tilemap_t::draw_roz(screen_device &screen, bitmap_ind16 &dest, const rectangle &cliprect,
1153 		u32 startx, u32 starty, int incxx, int incxy, int incyx, int incyy,
1154 		bool wraparound, u32 flags, u8 priority, u8 priority_mask)
1155 { draw_roz_common(screen, dest, cliprect, startx, starty, incxx, incxy, incyx, incyy, wraparound, flags, priority, priority_mask); }
1156 
draw_roz(screen_device & screen,bitmap_rgb32 & dest,const rectangle & cliprect,u32 startx,u32 starty,int incxx,int incxy,int incyx,int incyy,bool wraparound,u32 flags,u8 priority,u8 priority_mask)1157 void tilemap_t::draw_roz(screen_device &screen, bitmap_rgb32 &dest, const rectangle &cliprect,
1158 		u32 startx, u32 starty, int incxx, int incxy, int incyx, int incyy,
1159 		bool wraparound, u32 flags, u8 priority, u8 priority_mask)
1160 { draw_roz_common(screen, dest, cliprect, startx, starty, incxx, incxy, incyx, incyy, wraparound, flags, priority, priority_mask); }
1161 
1162 
1163 //-------------------------------------------------
1164 //  draw_instance - draw a single instance of the
1165 //  tilemap to the internal pixmap at the given
1166 //  xpos,ypos
1167 //-------------------------------------------------
1168 
1169 template<class _BitmapClass>
draw_instance(screen_device & screen,_BitmapClass & dest,const blit_parameters & blit,int xpos,int ypos)1170 void tilemap_t::draw_instance(screen_device &screen, _BitmapClass &dest, const blit_parameters &blit, int xpos, int ypos)
1171 {
1172 	// clip destination coordinates to the tilemap
1173 	// note that x2/y2 are exclusive, not inclusive
1174 	int x1 = (std::max)(xpos, blit.cliprect.left());
1175 	int x2 = (std::min)(xpos + int(m_width), blit.cliprect.right() + 1);
1176 	int y1 = (std::max)(ypos, blit.cliprect.top());
1177 	int y2 = (std::min)(ypos + int(m_height), blit.cliprect.bottom() + 1);
1178 
1179 	// if totally clipped, stop here
1180 	if (x1 >= x2 || y1 >= y2)
1181 		return;
1182 
1183 	// look up priority and destination base addresses for y1
1184 	bitmap_ind8 &priority_bitmap = *blit.priority;
1185 	u8 *priority_baseaddr = &priority_bitmap.pix(y1, xpos);
1186 	typename _BitmapClass::pixel_t *dest_baseaddr = nullptr;
1187 	int dest_rowpixels = 0;
1188 	if (dest.valid())
1189 	{
1190 		dest_rowpixels = dest.rowpixels();
1191 		dest_baseaddr = &dest.pix(y1, xpos);
1192 	}
1193 
1194 	// convert screen coordinates to source tilemap coordinates
1195 	x1 -= xpos;
1196 	y1 -= ypos;
1197 	x2 -= xpos;
1198 	y2 -= ypos;
1199 
1200 	// get tilemap pixels
1201 	const u16 *source_baseaddr = &m_pixmap.pix(y1);
1202 	const u8 *mask_baseaddr = &m_flagsmap.pix(y1);
1203 
1204 	// get start/stop columns, rounding outward
1205 	int mincol = x1 / m_tilewidth;
1206 	int maxcol = (x2 + m_tilewidth - 1) / m_tilewidth;
1207 
1208 	// set up row counter
1209 	int y = y1;
1210 	int nexty = m_tileheight * (y1 / m_tileheight) + m_tileheight;
1211 	nexty = std::min(nexty, y2);
1212 
1213 	// loop over tilemap rows
1214 	for (;;)
1215 	{
1216 		int row = y / m_tileheight;
1217 		int x_start = x1;
1218 
1219 		// iterate across the applicable tilemap columns
1220 		trans_t prev_trans = WHOLLY_TRANSPARENT;
1221 		trans_t cur_trans;
1222 		for (int column = mincol; column <= maxcol; column++)
1223 		{
1224 			int x_end;
1225 
1226 			// we don't actually render the last column; it is always just used for flushing
1227 			if (column == maxcol)
1228 				cur_trans = WHOLLY_TRANSPARENT;
1229 
1230 			// for other columns we look up the transparency information
1231 			else
1232 			{
1233 				logical_index logindex = row * m_cols + column;
1234 
1235 				// if the current tile is dirty, fix it
1236 				if (m_tileflags[logindex] == TILE_FLAG_DIRTY)
1237 					tile_update(logindex, column, row);
1238 
1239 				// if the current summary data is non-zero, we must draw masked
1240 				if ((m_tileflags[logindex] & blit.mask) != 0)
1241 					cur_trans = MASKED;
1242 
1243 				// otherwise, our transparency state is constant across the tile; fetch it
1244 				else
1245 					cur_trans = ((mask_baseaddr[column * m_tilewidth] & blit.mask) == blit.value) ? WHOLLY_OPAQUE : WHOLLY_TRANSPARENT;
1246 			}
1247 
1248 			// if the transparency state is the same as last time, don't render yet
1249 			if (cur_trans == prev_trans)
1250 				continue;
1251 
1252 			// compute the end of this run, in pixels
1253 			x_end = column * m_tilewidth;
1254 			x_end = std::max(x_end, x1);
1255 			x_end = std::min(x_end, x2);
1256 
1257 			// if we're rendering something, compute the pointers
1258 			const rgb_t *clut = m_palette->palette()->entry_list_adjusted();
1259 			if (prev_trans != WHOLLY_TRANSPARENT)
1260 			{
1261 				const u16 *source0 = source_baseaddr + x_start;
1262 				typename _BitmapClass::pixel_t *dest0 = dest_baseaddr + x_start;
1263 				u8 *pmap0 = priority_baseaddr + x_start;
1264 
1265 				// if we were opaque, use the opaque renderer
1266 				if (prev_trans == WHOLLY_OPAQUE)
1267 				{
1268 					for (int cury = y; cury < nexty; cury++)
1269 					{
1270 						if (dest_baseaddr == nullptr)
1271 							scanline_draw_opaque_null(x_end - x_start, pmap0, blit.tilemap_priority_code);
1272 						else if (sizeof(*dest0) == 2)
1273 							scanline_draw_opaque_ind16(reinterpret_cast<u16 *>(dest0), source0, x_end - x_start, pmap0, blit.tilemap_priority_code);
1274 						else if (sizeof(*dest0) == 4 && blit.alpha >= 0xff)
1275 							scanline_draw_opaque_rgb32(reinterpret_cast<u32 *>(dest0), source0, x_end - x_start, clut, pmap0, blit.tilemap_priority_code);
1276 						else if (sizeof(*dest0) == 4)
1277 							scanline_draw_opaque_rgb32_alpha(reinterpret_cast<u32 *>(dest0), source0, x_end - x_start, clut, pmap0, blit.tilemap_priority_code, blit.alpha);
1278 
1279 						dest0 += dest_rowpixels;
1280 						source0 += m_pixmap.rowpixels();
1281 						pmap0 += priority_bitmap.rowpixels();
1282 					}
1283 				}
1284 
1285 				// otherwise use the masked renderer
1286 				else
1287 				{
1288 					const u8 *mask0 = mask_baseaddr + x_start;
1289 					for (int cury = y; cury < nexty; cury++)
1290 					{
1291 						if (dest_baseaddr == nullptr)
1292 							scanline_draw_masked_null(mask0, blit.mask, blit.value, x_end - x_start, pmap0, blit.tilemap_priority_code);
1293 						else if (sizeof(*dest0) == 2)
1294 							scanline_draw_masked_ind16(reinterpret_cast<u16 *>(dest0), source0, mask0, blit.mask, blit.value, x_end - x_start, pmap0, blit.tilemap_priority_code);
1295 						else if (sizeof(*dest0) == 4 && blit.alpha >= 0xff)
1296 							scanline_draw_masked_rgb32(reinterpret_cast<u32 *>(dest0), source0, mask0, blit.mask, blit.value, x_end - x_start, clut, pmap0, blit.tilemap_priority_code);
1297 						else if (sizeof(*dest0) == 4)
1298 							scanline_draw_masked_rgb32_alpha(reinterpret_cast<u32 *>(dest0), source0, mask0, blit.mask, blit.value, x_end - x_start, clut, pmap0, blit.tilemap_priority_code, blit.alpha);
1299 
1300 						dest0 += dest_rowpixels;
1301 						source0 += m_pixmap.rowpixels();
1302 						mask0 += m_flagsmap.rowpixels();
1303 						pmap0 += priority_bitmap.rowpixels();
1304 					}
1305 				}
1306 			}
1307 
1308 			// the new start is the end
1309 			x_start = x_end;
1310 			prev_trans = cur_trans;
1311 		}
1312 
1313 		// if this was the last row, stop
1314 		if (nexty == y2)
1315 			break;
1316 
1317 		// advance to the next row on all our bitmaps
1318 		priority_baseaddr += priority_bitmap.rowpixels() * (nexty - y);
1319 		source_baseaddr += m_pixmap.rowpixels() * (nexty - y);
1320 		mask_baseaddr += m_flagsmap.rowpixels() * (nexty - y);
1321 		dest_baseaddr += dest_rowpixels * (nexty - y);
1322 
1323 		// increment the Y counter
1324 		y = nexty;
1325 		nexty += m_tileheight;
1326 		nexty = std::min(nexty, y2);
1327 	}
1328 }
1329 
1330 
1331 //-------------------------------------------------
1332 //  tilemap_draw_roz_core - render the tilemap's
1333 //  pixmap to the destination with rotation
1334 //  and zoom
1335 //-------------------------------------------------
1336 
1337 #define ROZ_PLOT_PIXEL(INPUT_VAL)                                           \
1338 do {                                                                        \
1339 	if (sizeof(*dest) == 2)                                                 \
1340 		*dest = (INPUT_VAL) + (priority >> 16);                             \
1341 	else if (sizeof(*dest) == 4 && alpha >= 0xff)                           \
1342 		*dest = clut[INPUT_VAL];                                            \
1343 	else if (sizeof(*dest) == 4)                                            \
1344 		*dest = alpha_blend_r32(*dest, clut[INPUT_VAL], alpha);             \
1345 } while (0)
1346 
1347 template<class _BitmapClass>
draw_roz_core(screen_device & screen,_BitmapClass & destbitmap,const blit_parameters & blit,u32 startx,u32 starty,int incxx,int incxy,int incyx,int incyy,bool wraparound)1348 void tilemap_t::draw_roz_core(screen_device &screen, _BitmapClass &destbitmap, const blit_parameters &blit,
1349 		u32 startx, u32 starty, int incxx, int incxy, int incyx, int incyy, bool wraparound)
1350 {
1351 	// pre-cache all the inner loop values
1352 	const rgb_t *clut = m_palette->palette()->entry_list_adjusted() + (blit.tilemap_priority_code >> 16);
1353 	bitmap_ind8 &priority_bitmap = *blit.priority;
1354 	const int xmask = m_pixmap.width() - 1;
1355 	const int ymask = m_pixmap.height() - 1;
1356 	const int widthshifted = m_pixmap.width() << 16;
1357 	const int heightshifted = m_pixmap.height() << 16;
1358 	const u32 priority = blit.tilemap_priority_code;
1359 	u8 mask = blit.mask;
1360 	u8 value = blit.value;
1361 	u8 alpha = blit.alpha;
1362 
1363 	// pre-advance based on the cliprect
1364 	startx += blit.cliprect.left() * incxx + blit.cliprect.top() * incyx;
1365 	starty += blit.cliprect.left() * incxy + blit.cliprect.top() * incyy;
1366 
1367 	// extract start/end points
1368 	int sx = blit.cliprect.left();
1369 	int sy = blit.cliprect.top();
1370 	int ex = blit.cliprect.right();
1371 	int ey = blit.cliprect.bottom();
1372 
1373 	// optimized loop for the not rotated case
1374 	if (incxy == 0 && incyx == 0 && !wraparound)
1375 	{
1376 		// skip without drawing until we are within the bitmap
1377 		while (startx >= widthshifted && sx <= ex)
1378 		{
1379 			startx += incxx;
1380 			sx++;
1381 		}
1382 
1383 		// early exit if we're done already
1384 		if (sx > ex)
1385 			return;
1386 
1387 		// loop over rows
1388 		while (sy <= ey)
1389 		{
1390 			// only draw if Y is within range
1391 			if (starty < heightshifted)
1392 			{
1393 				// initialize X counters
1394 				int x = sx;
1395 				u32 cx = startx;
1396 				u32 cy = starty >> 16;
1397 
1398 				// get source and priority pointers
1399 				u8 *pri = (priority != 0xff00) ? &priority_bitmap.pix(sy, sx) : nullptr;
1400 				const u16 *src = &m_pixmap.pix(cy);
1401 				const u8 *maskptr = &m_flagsmap.pix(cy);
1402 				typename _BitmapClass::pixel_t *dest = &destbitmap.pix(sy, sx);
1403 
1404 				// loop over columns
1405 				while (x <= ex && cx < widthshifted)
1406 				{
1407 					// plot if we match the mask
1408 					if ((maskptr[cx >> 16] & mask) == value)
1409 					{
1410 						ROZ_PLOT_PIXEL(src[cx >> 16]);
1411 						if (priority != 0xff00)
1412 							*pri = (*pri & (priority >> 8)) | priority;
1413 					}
1414 
1415 					// advance in X
1416 					cx += incxx;
1417 					x++;
1418 					++dest;
1419 					if (priority != 0xff00)
1420 						pri++;
1421 				}
1422 			}
1423 
1424 			// advance in Y
1425 			starty += incyy;
1426 			sy++;
1427 		}
1428 	}
1429 
1430 	// wraparound case
1431 	else if (wraparound)
1432 	{
1433 		// loop over rows
1434 		while (sy <= ey)
1435 		{
1436 			// initialize X counters
1437 			int x = sx;
1438 			u32 cx = startx;
1439 			u32 cy = starty;
1440 
1441 			// get dest and priority pointers
1442 			typename _BitmapClass::pixel_t *dest = &destbitmap.pix(sy, sx);
1443 			u8 *pri = (priority != 0xff00) ? &priority_bitmap.pix(sy, sx) : nullptr;
1444 
1445 			// loop over columns
1446 			while (x <= ex)
1447 			{
1448 				// plot if we match the mask
1449 				if ((m_flagsmap.pix((cy >> 16) & ymask, (cx >> 16) & xmask) & mask) == value)
1450 				{
1451 					ROZ_PLOT_PIXEL(m_pixmap.pix((cy >> 16) & ymask, (cx >> 16) & xmask));
1452 					if (priority != 0xff00)
1453 						*pri = (*pri & (priority >> 8)) | priority;
1454 				}
1455 
1456 				// advance in X
1457 				cx += incxx;
1458 				cy += incxy;
1459 				x++;
1460 				++dest;
1461 				if (priority != 0xff00)
1462 					pri++;
1463 			}
1464 
1465 			// advance in Y
1466 			startx += incyx;
1467 			starty += incyy;
1468 			sy++;
1469 		}
1470 	}
1471 
1472 	// non-wraparound case
1473 	else
1474 	{
1475 		// loop over rows
1476 		while (sy <= ey)
1477 		{
1478 			// initialize X counters
1479 			int x = sx;
1480 			u32 cx = startx;
1481 			u32 cy = starty;
1482 
1483 			// get dest and priority pointers
1484 			typename _BitmapClass::pixel_t *dest = &destbitmap.pix(sy, sx);
1485 			u8 *pri = (priority != 0xff00) ? &priority_bitmap.pix(sy, sx) : nullptr;
1486 
1487 			// loop over columns
1488 			while (x <= ex)
1489 			{
1490 				// plot if we're within the bitmap and we match the mask
1491 				if (cx < widthshifted && cy < heightshifted)
1492 					if ((m_flagsmap.pix(cy >> 16, cx >> 16) & mask) == value)
1493 					{
1494 						ROZ_PLOT_PIXEL(m_pixmap.pix(cy >> 16, cx >> 16));
1495 						if (priority != 0xff00)
1496 							*pri = (*pri & (priority >> 8)) | priority;
1497 					}
1498 
1499 				// advance in X
1500 				cx += incxx;
1501 				cy += incxy;
1502 				x++;
1503 				++dest;
1504 				if (priority != 0xff00)
1505 					pri++;
1506 			}
1507 
1508 			// advance in Y
1509 			startx += incyx;
1510 			starty += incyy;
1511 			sy++;
1512 		}
1513 	}
1514 }
1515 
1516 
1517 //-------------------------------------------------
1518 //  draw_debug - draw a debug version without any
1519 //  rowscroll and with fixed parameters
1520 //-------------------------------------------------
1521 
draw_debug(screen_device & screen,bitmap_rgb32 & dest,u32 scrollx,u32 scrolly,u32 flags)1522 void tilemap_t::draw_debug(screen_device &screen, bitmap_rgb32 &dest, u32 scrollx, u32 scrolly, u32 flags)
1523 {
1524 	// set up for the blit, using hard-coded parameters (no priority, etc)
1525 	blit_parameters blit;
1526 	bitmap_ind8 dummy_priority;
1527 
1528 	// draw everything
1529 	flags |= TILEMAP_DRAW_OPAQUE;
1530 
1531 	configure_blit_parameters(blit, dummy_priority, dest.cliprect(), flags, 0, 0xff);
1532 
1533 	// compute the effective scroll positions
1534 	scrollx = m_width  - scrollx % m_width;
1535 	scrolly = m_height - scrolly % m_height;
1536 
1537 	// flush the dirty state to all tiles as appropriate
1538 	realize_all_dirty_tiles();
1539 
1540 	// iterate to handle wraparound
1541 	for (int ypos = scrolly - m_height; ypos <= blit.cliprect.bottom(); ypos += m_height)
1542 		for (int xpos = scrollx - m_width; xpos <= blit.cliprect.right(); xpos += m_width)
1543 			draw_instance(screen, dest, blit, xpos, ypos);
1544 }
1545 
1546 
1547 //-------------------------------------------------
1548 //  get_info_debug - extract info for one tile
1549 //-------------------------------------------------
1550 
get_info_debug(u32 col,u32 row,u8 & gfxnum,u32 & code,u32 & color)1551 void tilemap_t::get_info_debug(u32 col, u32 row, u8 &gfxnum, u32 &code, u32 &color)
1552 {
1553 	// first map to the memory index
1554 	tilemap_memory_index memindex = memory_index(col, row);
1555 
1556 	// next invoke the get info callback
1557 	m_tile_get_info(*this, m_tileinfo, memindex);
1558 
1559 	// get the GFX number and code
1560 	gfxnum = m_tileinfo.gfxnum;
1561 	code = m_tileinfo.code;
1562 
1563 	// work back from the palette base to get the color
1564 	const gfx_element &gfx = *m_tileinfo.decoder->gfx(gfxnum);
1565 	color = (m_tileinfo.palette_base - gfx.colorbase()) / gfx.granularity();
1566 }
1567 
1568 
1569 //**************************************************************************
1570 //  TILEMAP MANAGER
1571 //**************************************************************************
1572 
1573 //-------------------------------------------------
1574 //  tilemap_manager - constructor
1575 //-------------------------------------------------
1576 
tilemap_manager(running_machine & machine)1577 tilemap_manager::tilemap_manager(running_machine &machine)
1578 	: m_machine(machine),
1579 		m_instance(0)
1580 {
1581 }
1582 
1583 
1584 //-------------------------------------------------
1585 //  ~tilemap_manager - destructor
1586 //-------------------------------------------------
1587 
~tilemap_manager()1588 tilemap_manager::~tilemap_manager()
1589 {
1590 	// detach all device tilemaps since they will be destroyed as subdevices elsewhere
1591 	bool found = true;
1592 	while (found)
1593 	{
1594 		found = false;
1595 		for (tilemap_t &tmap : m_tilemap_list)
1596 			if (tmap.m_device)
1597 			{
1598 				found = true;
1599 				m_tilemap_list.detach(tmap);
1600 				break;
1601 			}
1602 	}
1603 }
1604 
1605 
1606 //-------------------------------------------------
1607 //  set_flip_all - set a global flip for all the
1608 //  tilemaps
1609 //-------------------------------------------------
1610 
create(device_gfx_interface & decoder,tilemap_get_info_delegate tile_get_info,tilemap_mapper_delegate mapper,u16 tilewidth,u16 tileheight,u32 cols,u32 rows,tilemap_t * allocated)1611 tilemap_t &tilemap_manager::create(device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_mapper_delegate mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows, tilemap_t *allocated)
1612 {
1613 	if (!allocated)
1614 		allocated = new tilemap_t(machine().root_device());
1615 	return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, mapper, tilewidth, tileheight, cols, rows));
1616 }
1617 
create(device_gfx_interface & decoder,tilemap_get_info_delegate tile_get_info,tilemap_standard_mapper mapper,u16 tilewidth,u16 tileheight,u32 cols,u32 rows,tilemap_t * allocated)1618 tilemap_t &tilemap_manager::create(device_gfx_interface &decoder, tilemap_get_info_delegate tile_get_info, tilemap_standard_mapper mapper, u16 tilewidth, u16 tileheight, u32 cols, u32 rows, tilemap_t *allocated)
1619 {
1620 	if (!allocated)
1621 		allocated = new tilemap_t(machine().root_device());
1622 	return m_tilemap_list.append(allocated->init(*this, decoder, tile_get_info, mapper, tilewidth, tileheight, cols, rows));
1623 }
1624 
1625 
1626 //-------------------------------------------------
1627 //  set_flip_all - set a global flip for all the
1628 //  tilemaps
1629 //-------------------------------------------------
1630 
set_flip_all(u32 attributes)1631 void tilemap_manager::set_flip_all(u32 attributes)
1632 {
1633 	for (tilemap_t &tmap : m_tilemap_list)
1634 		tmap.set_flip(attributes);
1635 }
1636 
1637 
1638 //-------------------------------------------------
1639 //  mark_all_dirty - mark all the tiles in all the
1640 //  tilemaps dirty
1641 //-------------------------------------------------
1642 
mark_all_dirty()1643 void tilemap_manager::mark_all_dirty()
1644 {
1645 	for (tilemap_t &tmap : m_tilemap_list)
1646 		tmap.mark_all_dirty();
1647 }
1648 
1649 
1650 
1651 //**************************************************************************
1652 //  TILEMAP DEVICE
1653 //**************************************************************************
1654 
1655 // device type definition
1656 DEFINE_DEVICE_TYPE(TILEMAP, tilemap_device, "tilemap", "Tilemap")
1657 
1658 //-------------------------------------------------
1659 //  tilemap_device - constructor
1660 //-------------------------------------------------
1661 
tilemap_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)1662 tilemap_device::tilemap_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
1663 	: device_t(mconfig, TILEMAP, tag, owner, clock)
1664 	, tilemap_t(static_cast<device_t &>(*this))
1665 	, m_gfxdecode(*this, finder_base::DUMMY_TAG)
1666 	, m_get_info(*this)
1667 	, m_standard_mapper(TILEMAP_STANDARD_COUNT)
1668 	, m_mapper(*this)
1669 	, m_bytes_per_entry(0)
1670 	, m_tile_width(8)
1671 	, m_tile_height(8)
1672 	, m_num_columns(64)
1673 	, m_num_rows(64)
1674 	, m_transparent_pen_set(false)
1675 	, m_transparent_pen(0)
1676 {
1677 }
1678 
1679 
1680 //-------------------------------------------------
1681 //  write: Main memory writes
1682 //-------------------------------------------------
1683 
write8(offs_t offset,u8 data)1684 void tilemap_device::write8(offs_t offset, u8 data)
1685 {
1686 	m_basemem.write8(offset, data);
1687 	offset /= m_bytes_per_entry;
1688 	mark_tile_dirty(offset);
1689 }
1690 
write16(offs_t offset,u16 data,u16 mem_mask)1691 void tilemap_device::write16(offs_t offset, u16 data, u16 mem_mask)
1692 {
1693 	m_basemem.write16(offset, data, mem_mask);
1694 	offset = offset * 2 / m_bytes_per_entry;
1695 	mark_tile_dirty(offset);
1696 	if (m_bytes_per_entry < 2)
1697 		mark_tile_dirty(offset + 1);
1698 }
1699 
write32(offs_t offset,u32 data,u32 mem_mask)1700 void tilemap_device::write32(offs_t offset, u32 data, u32 mem_mask)
1701 {
1702 	m_basemem.write32(offset, data, mem_mask);
1703 	offset = offset * 4 / m_bytes_per_entry;
1704 	mark_tile_dirty(offset);
1705 	if (m_bytes_per_entry < 4)
1706 	{
1707 		mark_tile_dirty(offset + 1);
1708 		if (m_bytes_per_entry < 2)
1709 		{
1710 			mark_tile_dirty(offset + 2);
1711 			mark_tile_dirty(offset + 3);
1712 		}
1713 	}
1714 }
1715 
1716 
1717 //-------------------------------------------------
1718 //  write_entry_ext: Extension memory writes
1719 //-------------------------------------------------
1720 
write8_ext(offs_t offset,u8 data)1721 void tilemap_device::write8_ext(offs_t offset, u8 data)
1722 {
1723 	m_extmem.write8(offset, data);
1724 	offset /= m_bytes_per_entry;
1725 	mark_tile_dirty(offset);
1726 }
1727 
write16_ext(offs_t offset,u16 data,u16 mem_mask)1728 void tilemap_device::write16_ext(offs_t offset, u16 data, u16 mem_mask)
1729 {
1730 	m_extmem.write16(offset, data, mem_mask);
1731 	offset = offset * 2 / m_bytes_per_entry;
1732 	mark_tile_dirty(offset);
1733 	if (m_bytes_per_entry < 2)
1734 		mark_tile_dirty(offset + 1);
1735 }
1736 
write32_ext(offs_t offset,u32 data,u32 mem_mask)1737 void tilemap_device::write32_ext(offs_t offset, u32 data, u32 mem_mask)
1738 {
1739 	m_extmem.write32(offset, data, mem_mask);
1740 	offset = offset * 4 / m_bytes_per_entry;
1741 	mark_tile_dirty(offset);
1742 	if (m_bytes_per_entry < 4)
1743 	{
1744 		mark_tile_dirty(offset + 1);
1745 		if (m_bytes_per_entry < 2)
1746 		{
1747 			mark_tile_dirty(offset + 2);
1748 			mark_tile_dirty(offset + 3);
1749 		}
1750 	}
1751 }
1752 
1753 
1754 //-------------------------------------------------
1755 //  device_start: Start up the device
1756 //-------------------------------------------------
1757 
device_start()1758 void tilemap_device::device_start()
1759 {
1760 	// check configuration
1761 	if (m_get_info.isnull())
1762 		throw emu_fatalerror("Tilemap device '%s' has no get info callback!", tag());
1763 	if (m_standard_mapper == TILEMAP_STANDARD_COUNT && m_mapper.isnull())
1764 		throw emu_fatalerror("Tilemap device '%s' has no mapper callback!", tag());
1765 
1766 	if(!m_gfxdecode->started())
1767 		throw device_missing_dependencies();
1768 
1769 	// bind our callbacks
1770 	m_get_info.resolve();
1771 	m_mapper.resolve();
1772 
1773 	// allocate the tilemap
1774 	if (m_standard_mapper == TILEMAP_STANDARD_COUNT)
1775 		machine().tilemap().create(*m_gfxdecode, m_get_info, m_mapper, m_tile_width, m_tile_height, m_num_columns, m_num_rows, *this);
1776 	else
1777 		machine().tilemap().create(*m_gfxdecode, m_get_info, m_standard_mapper, m_tile_width, m_tile_height, m_num_columns, m_num_rows, *this);
1778 
1779 	// find the memory, if present
1780 	const memory_share *share = memshare(tag());
1781 	if (share != nullptr)
1782 	{
1783 		m_basemem.set(*share, m_bytes_per_entry);
1784 
1785 		// look for an extension entry
1786 		std::string tag_ext = std::string(tag()).append("_ext");
1787 		share = memshare(tag_ext);
1788 		if (share != nullptr)
1789 			m_extmem.set(*share, m_bytes_per_entry);
1790 	}
1791 
1792 	// configure the device and set the pen
1793 	if (m_transparent_pen_set)
1794 		set_transparent_pen(m_transparent_pen);
1795 }
1796