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