1 // license:BSD-3-Clause
2 // copyright-holders:David Haywood
3
4 #include "emu.h"
5 #include "elan_eu3a05vid.h"
6
7 DEFINE_DEVICE_TYPE(ELAN_EU3A05_VID, elan_eu3a05vid_device, "elan_eu3a05vid", "Elan EU3A05 Video")
8
9 // tilemaps start at 0x0600 in mainram, sprites at 0x3e00, unlike eu3a14 these could be fixed addresses
10
elan_eu3a05vid_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)11 elan_eu3a05vid_device::elan_eu3a05vid_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
12 elan_eu3a05commonvid_device(mconfig, ELAN_EU3A05_VID, tag, owner, clock),
13 device_memory_interface(mconfig, *this),
14 m_cpu(*this, finder_base::DUMMY_TAG),
15 m_bank(*this, finder_base::DUMMY_TAG),
16 m_space_config("regs", ENDIANNESS_NATIVE, 8, 5, 0, address_map_constructor(FUNC(elan_eu3a05vid_device::map), this)),
17 m_bytes_per_tile_entry(4),
18 m_vrambase(0x600),
19 m_spritebase(0x3e00),
20 m_use_spritepages(false)
21 {
22 }
23
memory_space_config() const24 device_memory_interface::space_config_vector elan_eu3a05vid_device::memory_space_config() const
25 {
26 return space_config_vector {
27 std::make_pair(0, &m_space_config)
28 };
29 }
30
map(address_map & map)31 void elan_eu3a05vid_device::map(address_map &map)
32 {
33 map(0x00, 0x1f).rw(FUNC(elan_eu3a05vid_device::read_unmapped), FUNC(elan_eu3a05vid_device::write_unmapped));
34
35 map(0x00, 0x06).ram(); // unknown, space invaders sets these to fixed values, tetris has them as 00
36 map(0x07, 0x07).rw(FUNC(elan_eu3a05vid_device::elan_eu3a05_vidctrl_r), FUNC(elan_eu3a05vid_device::elan_eu3a05_vidctrl_w));
37 map(0x08, 0x08).ram(); // unknown
38 map(0x09, 0x09).rw(FUNC(elan_eu3a05vid_device::tile_gfxbase_lo_r), FUNC(elan_eu3a05vid_device::tile_gfxbase_lo_w));
39 map(0x0a, 0x0a).rw(FUNC(elan_eu3a05vid_device::tile_gfxbase_hi_r), FUNC(elan_eu3a05vid_device::tile_gfxbase_hi_w));
40 map(0x0b, 0x0b).rw(FUNC(elan_eu3a05vid_device::sprite_gfxbase_lo_r), FUNC(elan_eu3a05vid_device::sprite_gfxbase_lo_w));
41 map(0x0c, 0x0c).rw(FUNC(elan_eu3a05vid_device::sprite_gfxbase_hi_r), FUNC(elan_eu3a05vid_device::sprite_gfxbase_hi_w));
42 map(0x0d, 0x0e).rw(FUNC(elan_eu3a05vid_device::splitpos_r), FUNC(elan_eu3a05vid_device::splitpos_w));
43 map(0x0f, 0x16).rw(FUNC(elan_eu3a05vid_device::tile_scroll_r), FUNC(elan_eu3a05vid_device::tile_scroll_w));
44 map(0x17, 0x17).ram(); // unknown
45 map(0x18, 0x18).ram(); // unknown
46 // no other writes seen
47 }
48
device_start()49 void elan_eu3a05vid_device::device_start()
50 {
51 elan_eu3a05commonvid_device::device_start();
52
53 save_item(NAME(m_vidctrl));
54 save_item(NAME(m_tile_gfxbase_lo_data));
55 save_item(NAME(m_tile_gfxbase_hi_data));
56 save_item(NAME(m_sprite_gfxbase_lo_data));
57 save_item(NAME(m_sprite_gfxbase_hi_data));
58 save_item(NAME(m_tile_scroll));
59 save_item(NAME(m_splitpos));
60 }
61
device_reset()62 void elan_eu3a05vid_device::device_reset()
63 {
64 elan_eu3a05commonvid_device::device_reset();
65
66 m_vidctrl = 0x00; // need to default to an 8x8 mode for Space Invaders test mode at least
67
68 for (int i=0;i<4*2;i++)
69 m_tile_scroll[i] = 0x00;
70
71 m_tile_gfxbase_lo_data = 0x00;
72 m_tile_gfxbase_hi_data = 0x00;
73
74 m_sprite_gfxbase_lo_data = 0x00;
75 m_sprite_gfxbase_hi_data = 0x00;
76
77 for (int i=0;i<2;i++)
78 m_splitpos[i] = 0x00;
79
80 }
81
set_is_sudoku()82 void elan_eu3a05vid_device::set_is_sudoku()
83 {
84 m_bytes_per_tile_entry = 2;
85 m_vrambase = 0x200;
86 m_spritebase = 0x1000;
87 }
88
set_is_pvmilfin()89 void elan_eu3a05vid_device::set_is_pvmilfin()
90 {
91 m_bytes_per_tile_entry = 4;
92 m_vrambase = 0x200;
93 m_spritebase = 0x1000; // not verified
94 }
95
read_spriteram(int offset)96 uint8_t elan_eu3a05vid_device::read_spriteram(int offset)
97 {
98 address_space& cpuspace = m_cpu->space(AS_PROGRAM);
99 int realoffset = offset+m_spritebase;
100 if (realoffset < 0x4000)
101 {
102 return cpuspace.read_byte(realoffset);
103 }
104 else
105 return 0x00;
106 }
107
108
read_vram(int offset)109 uint8_t elan_eu3a05vid_device::read_vram(int offset)
110 {
111 address_space& cpuspace = m_cpu->space(AS_PROGRAM);
112 int realoffset = offset+m_vrambase;
113 if (realoffset < 0x4000)
114 {
115 return cpuspace.read_byte(realoffset);
116 }
117 else
118 return 0x00;
119 }
120
121
122
123 /* (m_tile_gfxbase_lo_data | (m_tile_gfxbase_hi_data << 8)) * 0x100
124 gives you the actual rom address, everything references the 3MByte - 4MByte region, like the banking so
125 the system can probably have up to a 4MByte rom, all games we have so far just use the upper 1MByte of
126 that space (Tetris seems to rely on mirroring? as it sets all addresses up for the lower 1MB instead)
127 */
128
draw_sprites(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)129 void elan_eu3a05vid_device::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
130 {
131 address_space& fullbankspace = m_bank->space(AS_PROGRAM);
132
133 /*
134 Sprites
135 FF yy xx AA XX YY aa bb
136
137 yy = y position
138 xx = x position
139 XX = texture x start
140 YY = texture y start
141 bb = sometimes set in invaders
142 AA = same as attr on tiles (colour / priority?)
143
144
145 aa = same as unk2 on tiles? ( --pp ---- )
146 p = page
147
148 FF = flags ( e-dD fFsS )
149 e = enable
150 D = ZoomX to double size (boss explosions on Air Blaster)
151 d = ZoomY to double size (boss explosions on Air Blaster)
152 S = SizeX
153 s = SizeY
154 F = FlipX
155 f = FlipY (assumed, not seen)
156
157 */
158
159 for (int i = 0; i < 512; i += 8)
160 {
161 uint8_t x = read_spriteram(i + 2);
162 uint8_t y = read_spriteram(i + 1);
163
164 /*
165 Space Invaders draws the player base with this specific y value before the start of each life
166 and expects it to NOT wrap around. There are no high priority tiles or anything else to hide
167 and it doesn't appear on real hardware.
168
169 it's possible sprites don't wrap around at all (but then you couldn't have smooth entry at the
170 top of the screen - there are no extra y co-ordinate bits. However there would have been easier
171 ways to hide this tho as there are a bunch of unseen lines at the bottom of the screen anyway!
172
173 Air Blaster Joystick seems to indicate there is no sprite wrapping - sprites abruptly enter
174 the screen in pieces on real hardware.
175
176 needs further investigation.
177 */
178 if (y==255)
179 continue;
180
181 uint8_t tex_x = read_spriteram(i + 4);
182 uint8_t tex_y = read_spriteram(i + 5);
183
184 uint8_t flags = read_spriteram(i + 0);
185 uint8_t attr = read_spriteram(i + 3);
186 uint8_t unk2 = read_spriteram(i + 6);
187
188 const int doubleX = (flags & 0x10)>>4;
189 const int doubleY = (flags & 0x20)>>5;
190
191 //int priority = attr & 0x0f;
192 int colour = attr & 0xf0;
193
194 // ? game select has this set to 0xff, but clearly doesn't want the palette to change!
195 // while in Space Invaders this has to be palette for the UFO to be red.
196 if (colour & 0x80)
197 colour = 0;
198
199 int transpen = 0;
200
201 /* HACK - how is this calculated
202 phoenix and the game select need it like this
203 it isn't a simple case of unk2 being transpen either because Qix has some elements set to 0x07
204 where the transpen needs to be 0x00 and Space Invaders has it set to 0x04
205 it could be a global register rather than something in the spritelist?
206 */
207 if ((attr == 0xff) && (unk2 == 0xff))
208 transpen = 0xff;
209
210
211 if (!(flags & 0x80))
212 continue;
213
214 int sizex = 8;
215 int sizey = 8;
216
217 if (flags & 0x01)
218 {
219 sizex = 16;
220 }
221
222 if (flags & 0x02)
223 {
224 sizey = 16;
225 }
226
227 int base = (m_sprite_gfxbase_lo_data | (m_sprite_gfxbase_hi_data << 8)) * 0x100;
228 int page = (unk2 & 0x30) >> 4;
229
230 // rad_sinv menu screen and phoenix don't agree with this, but carlecfg needs it
231 if (m_use_spritepages)
232 {
233 base += 0x10000 * page;
234 }
235
236 if (doubleX)
237 sizex = sizex * 2;
238
239 if (doubleY)
240 sizey = sizey * 2;
241
242 for (int yy = 0; yy < sizey; yy++)
243 {
244 uint16_t* row;
245
246 if (flags & 0x08) // guess flipy
247 {
248 row = &bitmap.pix((y + (sizey - 1 - yy)) & 0xff);
249 }
250 else
251 {
252 row = &bitmap.pix((y + yy) & 0xff);
253 }
254
255 for (int xx = 0; xx < sizex; xx++)
256 {
257 int realaddr;
258
259 if (!doubleX)
260 realaddr = base + ((tex_x + xx) & 0xff);
261 else
262 realaddr = base + ((tex_x + (xx>>1)) & 0xff);
263
264 if (!doubleY)
265 realaddr += ((tex_y + yy) & 0xff) * 256;
266 else
267 realaddr += ((tex_y + (yy>>1)) & 0xff) * 256;
268
269 uint8_t pix = fullbankspace.read_byte(realaddr);
270
271 if (pix != transpen)
272 {
273 if (flags & 0x04) // flipx
274 {
275 row[(x + (sizex - 1 - xx)) & 0xff] = (pix + ((colour & 0x70) << 1)) & 0xff;
276 }
277 else
278 {
279 row[(x + xx) & 0xff] = (pix + ((colour & 0x70) << 1)) & 0xff;
280 }
281 }
282 }
283 }
284 }
285 }
286
287
288 // a hacky mess for now
get_tile_data(int base,int drawpri,int & tile,int & attr,int & unk2)289 bool elan_eu3a05vid_device::get_tile_data(int base, int drawpri, int& tile, int &attr, int &unk2)
290 {
291 tile = read_vram(base * m_bytes_per_tile_entry) + (read_vram((base * m_bytes_per_tile_entry) + 1) << 8);
292
293 // these seem to be the basically the same as attr/unk2 in the sprites, which also make
294 // very little sense.
295 if (m_bytes_per_tile_entry == 4)
296 {
297 attr = read_vram((base * m_bytes_per_tile_entry) + 2);
298 unk2 = read_vram((base * m_bytes_per_tile_entry) + 3);
299 }
300 else
301 {
302 attr = 0;
303 unk2 = 0;
304 }
305
306 /* hack for phoenix title screens.. the have attr set to 0x3f which change the colour bank in ways we don't want
307 and also by our interpretation of 0x0f bits sets the tiles to priority over sprites (although in this case
308 that might tbe ok, because it looks like the sprites also have that set */
309 if (unk2 == 0x07)
310 attr = 0;
311
312 int priority = attr & 0x0f;
313
314 // likely wrong
315 if ((drawpri == 0 && priority == 0x0f) || (drawpri == 1 && priority != 0x0f))
316 return false;
317
318 return true;
319 }
draw_tilemaps(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect,int drawpri)320 void elan_eu3a05vid_device::draw_tilemaps(screen_device& screen, bitmap_ind16& bitmap, const rectangle& cliprect, int drawpri)
321 {
322 /*
323 this doesn't handle 8x8 4bpp (not used by anything yet)
324 */
325
326 int scroll = get_scroll(1);
327 address_space& fullbankspace = m_bank->space(AS_PROGRAM);
328
329 // Phoenix scrolling actually skips a pixel, jumping from 0x001 to 0x1bf, scroll 0x000 isn't used, maybe it has other meanings?
330
331 int totalyrow;
332 int totalxcol;
333 int mapyrowsbase;
334 int tileysize;
335 int tilexsize;
336 int startrow;
337
338 if (m_vidctrl & 0x40) // 16x16 tiles
339 {
340 totalyrow = 16;
341 totalxcol = 16;
342 mapyrowsbase = 14;
343 tileysize = 16;
344 tilexsize = 16;
345 startrow = (scroll >> 4) & 0x1f;
346 }
347 else
348 {
349 totalyrow = 32;
350 totalxcol = 32;
351 mapyrowsbase = 28;
352 tileysize = 8;
353 tilexsize = 8;
354 startrow = (scroll >> 3) & 0x3f;
355 }
356
357 for (int y = 0; y < totalyrow; y++)
358 {
359 for (int x = 0; x < totalxcol * 2; x++)
360 {
361 int realstartrow = (startrow + y);
362
363 int yrows;
364
365 if (m_vidctrl & 0x01)
366 yrows = mapyrowsbase;
367 else
368 yrows = mapyrowsbase * 2;
369
370 if (realstartrow >= yrows)
371 realstartrow -= yrows;
372
373 // in double width & double height mode the page addressing needs adjusting
374 if (!(m_vidctrl & 0x02))
375 {
376 if (!(m_vidctrl & 0x01))
377 {
378 if (realstartrow >= (yrows / 2))
379 {
380 realstartrow += yrows / 2;
381 }
382 }
383 }
384
385 for (int i = 0; i < tileysize; i++)
386 {
387 int drawline = (y * tileysize) + i;
388 drawline -= scroll & (tileysize - 1);
389
390 if ((drawline >= 0) && (drawline < 256))
391 {
392 int scrollx;
393
394 // split can be probably configured in more ways than this
395 // exact enable conditions unclear
396 if (drawline < m_splitpos[0])
397 {
398 scrollx = get_scroll(0);
399 }
400 else if (drawline < m_splitpos[1])
401 {
402 scrollx = get_scroll(2);
403 }
404 else
405 {
406 scrollx = get_scroll(3);
407 }
408
409 int base;
410
411 if (m_vidctrl & 0x40) // 16x16 tiles
412 {
413 base = (((realstartrow + y) & 0x3f) * 8) + x;
414 }
415 else
416 {
417 base = (((realstartrow) & 0x7f) * totalxcol) + (x & (totalxcol - 1));
418 }
419
420 if (!(m_vidctrl & 0x02))
421 {
422 if (x & totalxcol)
423 {
424 base += totalxcol * mapyrowsbase;
425 }
426 }
427
428 int tile, attr, unk2;
429
430 if (!get_tile_data(base, drawpri, tile, attr, unk2))
431 continue;
432
433 int colour = attr & 0xf0;
434
435 /* 'tiles' are organized / extracted from 'texture' lines that form a 'page' the length of the rom
436 each texture line in 8bpp mode is 256 bytes
437 each texture line in 4bpp mode is 128 bytes
438 in 8x8 mode these pages are 32 tiles wide
439 in 16x16 mode these pages are 16 tiles wide
440 tiles can start on any line
441
442 it is unclear what the behavior is if the tile starts at the far edge of a line (wrap around on line?)
443
444 this is eu3a05 specific, eu3a14 uses a more traditional approach
445 */
446
447 const int tilespersrcline = 256 / tilexsize;
448 const int tilespersrcline_mask = tilespersrcline - 1;
449
450 tile = (tile & tilespersrcline_mask) + ((tile & ~tilespersrcline_mask) * tilexsize);
451
452 if (!(m_vidctrl & 0x20)) // 8bpp
453 tile <<= 1;
454
455 if (!(m_vidctrl & 0x40)) // 8*8 tiles
456 tile >>= 1;
457
458 tile += ((m_tile_gfxbase_lo_data | m_tile_gfxbase_hi_data << 8) << 5);
459
460 uint16_t *const row = &bitmap.pix(drawline);
461
462 if (m_vidctrl & 0x20) // 4bpp
463 {
464 for (int xx = 0; xx < tilexsize; xx += 2)
465 {
466 int realaddr = ((tile + i * 16) << 3) + (xx >> 1);
467 uint8_t pix = fullbankspace.read_byte(realaddr);
468
469 int drawxpos;
470
471 drawxpos = x * tilexsize + xx + 0 - scrollx;
472 drawxpos &= 0x1ff;
473 if ((drawxpos >= 0) && (drawxpos < 256))
474 row[drawxpos] = ((pix & 0xf0) >> 4) + colour;
475
476 drawxpos = x * tilexsize + xx + 1 - scrollx;
477 drawxpos &= 0x1ff;
478 if ((drawxpos >= 0) && (drawxpos < 256))
479 row[drawxpos] = ((pix & 0x0f) >> 0) + colour;
480 }
481 }
482 else // 8bpp
483 {
484 for (int xx = 0; xx < tilexsize; xx++)
485 {
486 int realaddr = ((tile + i * 32) << 3) + xx;
487 uint8_t pix = fullbankspace.read_byte(realaddr);
488
489 int drawxpos = x * tilexsize + xx - scrollx;
490 drawxpos &= 0x1ff;
491 if ((drawxpos >= 0) && (drawxpos < 256))
492 row[drawxpos] = (pix + ((colour & 0x70) << 1)) & 0xff;
493 }
494 }
495 }
496 }
497 }
498 }
499 }
500
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)501 uint32_t elan_eu3a05vid_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
502 {
503 bitmap.fill(0, cliprect);
504
505 draw_tilemaps(screen,bitmap,cliprect,0);
506 draw_sprites(screen,bitmap,cliprect);
507 draw_tilemaps(screen,bitmap,cliprect,1);
508
509 return 0;
510 }
511
512 // Tile bases
513
tile_gfxbase_lo_w(uint8_t data)514 void elan_eu3a05vid_device::tile_gfxbase_lo_w(uint8_t data)
515 {
516 //logerror("%s: tile_gfxbase_lo_w (select GFX base lower) %02x\n", machine().describe_context(), data);
517 m_tile_gfxbase_lo_data = data;
518 }
519
tile_gfxbase_hi_w(uint8_t data)520 void elan_eu3a05vid_device::tile_gfxbase_hi_w(uint8_t data)
521 {
522 //logerror("%s: tile_gfxbase_hi_w (select GFX base upper) %02x\n", machine().describe_context(), data);
523 m_tile_gfxbase_hi_data = data;
524 }
525
tile_gfxbase_lo_r()526 uint8_t elan_eu3a05vid_device::tile_gfxbase_lo_r()
527 {
528 //logerror("%s: tile_gfxbase_lo_r (GFX base lower)\n", machine().describe_context());
529 return m_tile_gfxbase_lo_data;
530 }
531
tile_gfxbase_hi_r()532 uint8_t elan_eu3a05vid_device::tile_gfxbase_hi_r()
533 {
534 //logerror("%s: tile_gfxbase_hi_r (GFX base upper)\n", machine().describe_context());
535 return m_tile_gfxbase_hi_data;
536 }
537
538
539
540 // Sprite Tile bases
541
sprite_gfxbase_lo_w(uint8_t data)542 void elan_eu3a05vid_device::sprite_gfxbase_lo_w(uint8_t data)
543 {
544 //logerror("%s: sprite_gfxbase_lo_w (select Sprite GFX base lower) %02x\n", machine().describe_context(), data);
545 m_sprite_gfxbase_lo_data = data;
546 }
547
sprite_gfxbase_hi_w(uint8_t data)548 void elan_eu3a05vid_device::sprite_gfxbase_hi_w(uint8_t data)
549 {
550 //logerror("%s: sprite_gfxbase_hi_w (select Sprite GFX base upper) %02x\n", machine().describe_context(), data);
551 m_sprite_gfxbase_hi_data = data;
552 }
553
sprite_gfxbase_lo_r()554 uint8_t elan_eu3a05vid_device::sprite_gfxbase_lo_r()
555 {
556 //logerror("%s: sprite_gfxbase_lo_r (Sprite GFX base lower)\n", machine().describe_context());
557 return m_sprite_gfxbase_lo_data;
558 }
559
sprite_gfxbase_hi_r()560 uint8_t elan_eu3a05vid_device::sprite_gfxbase_hi_r()
561 {
562 //logerror("%s: sprite_gfxbase_hi_r (Sprite GFX base upper)\n", machine().describe_context());
563 return m_sprite_gfxbase_hi_data;
564 }
565
566
567
tile_scroll_r(offs_t offset)568 uint8_t elan_eu3a05vid_device::tile_scroll_r(offs_t offset)
569 {
570 return m_tile_scroll[offset];
571 }
572
tile_scroll_w(offs_t offset,uint8_t data)573 void elan_eu3a05vid_device::tile_scroll_w(offs_t offset, uint8_t data)
574 {
575 m_tile_scroll[offset] = data;
576 }
577
splitpos_r(offs_t offset)578 uint8_t elan_eu3a05vid_device::splitpos_r(offs_t offset)
579 {
580 return m_splitpos[offset];
581 }
582
splitpos_w(offs_t offset,uint8_t data)583 void elan_eu3a05vid_device::splitpos_w(offs_t offset, uint8_t data)
584 {
585 m_splitpos[offset] = data;
586 }
587
get_scroll(int which)588 uint16_t elan_eu3a05vid_device::get_scroll(int which)
589 {
590 switch (which)
591 {
592 case 0x0: return (m_tile_scroll[1] << 8) | (m_tile_scroll[0]); // xscroll
593 case 0x1: return (m_tile_scroll[3] << 8) | (m_tile_scroll[2]); // yscroll
594 case 0x2: return (m_tile_scroll[5] << 8) | (m_tile_scroll[4]); // xsplit 1 scroll
595 case 0x3: return (m_tile_scroll[7] << 8) | (m_tile_scroll[6]); // scplit 2 scroll
596 }
597
598 return 0x0000;
599 }
600
elan_eu3a05_vidctrl_r()601 uint8_t elan_eu3a05vid_device::elan_eu3a05_vidctrl_r()
602 {
603 return m_vidctrl;
604 }
605
elan_eu3a05_vidctrl_w(uint8_t data)606 void elan_eu3a05vid_device::elan_eu3a05_vidctrl_w(uint8_t data)
607 {
608 logerror("%s: elan_eu3a05_vidctrl_w %02x (video control?)\n", machine().describe_context(), data);
609 /*
610 c3 8bpp 16x16 1100 0011 abl logo
611 e3 4bpp 16x16 1110 0011
612 83 8bpp 8x8 1000 0011 air blaster logo
613 02 8bpp 8x8 (phoenix) 0000 0010 air blaster 2d normal
614 03 8bpp 8x8 0000 0011 air blaster 2d bosses
615 00 0000 0000 air blaster 3d stages
616
617 ?tb- --wh
618
619 ? = unknown
620 t = tile size (1 = 16x16, 0 = 8x8)
621 b = bpp (0 = 8bpp, 1 = 4bpp)
622 - = haven't seen used
623 h = tilemap height? (0 = double height)
624 w = tilemap width? (0 = double width)
625
626 space invaders test mode doesn't initialize this
627
628 */
629 m_vidctrl = data;
630 }
631
read_unmapped(offs_t offset)632 uint8_t elan_eu3a05vid_device::read_unmapped(offs_t offset)
633 {
634 logerror("%s: elan_eu3a05vid_device::read_unmapped (offset %02x)\n", machine().describe_context(), offset);
635 return 0x00;
636 }
637
write_unmapped(offs_t offset,uint8_t data)638 void elan_eu3a05vid_device::write_unmapped(offs_t offset, uint8_t data)
639 {
640 logerror("%s: elan_eu3a05vid_device::write_unmapped (offset %02x) (data %02x)\n", machine().describe_context(), offset, data);
641 }
642