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