1 // license:BSD-3-Clause
2 // copyright-holders:Phil Stroffolino, David Haywood
3 /*************************************************************************
4 
5     Atari Tunnel Hunt hardware
6 
7 *************************************************************************/
8 
9 #include "emu.h"
10 #include "includes/tunhunt.h"
11 
12 
13 /****************************************************************************************/
14 
15 /* Video Hardware Addresses */
16 
17 /* Square Generator (13 bytes each) */
18 #define LINEV   0x1403  // LINES VERTICAL START
19 #define LINEVS  0x1483  // LINES VERT STOP
20 #define LINEH   0x1083  // LINES HORIZ START
21 #define LINEC   0x1283  // LINE COLOR, 4 BITS D0-D3
22 #define LINESH  0x1203  // LINES SLOPE 4 BITS D0-D3 (signed)
23 /* LINESH was used for rotation effects in an older version of the game */
24 
25 /* Shell Object0 */
26 #define SHEL0H  0x1800  // SHELL H POSITON (NORMAL SCREEN)
27 #define SHL0V   0x1400  // SHELL V START(NORMAL SCREEN)
28 #define SHL0VS  0x1480  // SHELL V STOP (NORMAL SCREEN)
29 #define SHL0ST  0x1200  // SHELL VSTRETCH (LIKE MST OBJ STRECTH)
30 #define SHL0PC  0x1280  // SHELL PICTURE CODE (D3-D0)
31 
32 /* Shell Object1 (see above) */
33 #define SHEL1H  0x1A00
34 #define SHL1V   0x1401
35 #define SHL1VS  0x1481
36 #define SHL1ST  0x1201
37 #define SHL1PC  0x1281
38 
39 /* Motion Object RAM */
40 #define MOBJV   0x1C00  // V POSITION (SCREEN ON SIDE)
41 #define MOBVS   0x1482  // V STOP OF MOTION OBJECT (NORMAL SCREEN)
42 #define MOBJH   0x1402  // H POSITON (SCREEN ON SIDE) (VSTART - NORMAL SCREEN)
43 #define MOBST   0x1082  // STARTING LINE FOR RAM SCAN ON MOBJ
44 #define VSTRLO  0x1202  // VERT (SCREEN ON SIDE) STRETCH MOJ OBJ
45 #define MOTT    0x2C00  // MOTION OBJECT RAM (00-0F NOT USED, BYT CLEARED)
46 #define MOBSC0  0x1080  // SCAN ROM START FOR MOBJ (unused?)
47 #define MOBSC1  0x1081  // (unused?)
48 
49 
50 
51 /****************************************************************************************/
52 
videoram_w(offs_t offset,uint8_t data)53 void tunhunt_state::videoram_w(offs_t offset, uint8_t data)
54 {
55 	m_videoram[offset] = data;
56 	m_fg_tilemap->mark_tile_dirty(offset);
57 }
58 
TILE_GET_INFO_MEMBER(tunhunt_state::get_fg_tile_info)59 TILE_GET_INFO_MEMBER(tunhunt_state::get_fg_tile_info)
60 {
61 	int attr = m_videoram[tile_index];
62 	int code = attr & 0x3f;
63 	int color = attr >> 6;
64 	int flags = color ? TILE_FORCE_LAYER0 : 0;
65 
66 	tileinfo.set(0, code, color, flags);
67 }
68 
video_start()69 void tunhunt_state::video_start()
70 {
71 	/*
72 	Motion Object RAM contains 64 lines of run-length encoded data.
73 	We keep track of dirty lines and cache the expanded bitmap.
74 	With max RLE expansion, bitmap size is 256x64.
75 	*/
76 
77 	m_tmpbitmap.allocate(256, 64, m_screen->format());
78 
79 	m_fg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tunhunt_state::get_fg_tile_info)), TILEMAP_SCAN_COLS, 8, 8, 32, 32);
80 
81 	m_fg_tilemap->set_transparent_pen(0);
82 	m_fg_tilemap->set_scrollx(0, 64);
83 
84 	save_item(NAME(m_control));
85 }
86 
tunhunt_palette(palette_device & palette) const87 void tunhunt_state::tunhunt_palette(palette_device &palette) const
88 {
89 	/* Tunnel Hunt uses a combination of color proms and palette RAM to specify a 16 color
90 	 * palette.  Here, we manage only the mappings for alphanumeric characters and SHELL
91 	 * graphics, which are unpacked ahead of time and drawn using MAME's drawgfx primitives.
92 	 */
93 
94 	/* motion objects/box */
95 	for (int i = 0; i < 0x10; i++)
96 		palette.set_pen_indirect(i, i);
97 
98 	/* AlphaNumerics (1bpp)
99 	 *  2 bits of hilite select from 4 different background colors
100 	 *  Foreground color is always pen#4
101 	 *  Background color is mapped as follows:
102 	 */
103 
104 	/* alpha hilite#0 */
105 	palette.set_pen_indirect(0x10, 0x0); // background color#0 (transparent)
106 	palette.set_pen_indirect(0x11, 0x4); // foreground color
107 
108 	/* alpha hilite#1 */
109 	palette.set_pen_indirect(0x12, 0x5); // background color#1
110 	palette.set_pen_indirect(0x13, 0x4); // foreground color
111 
112 	/* alpha hilite#2 */
113 	palette.set_pen_indirect(0x14, 0x6); // background color#2
114 	palette.set_pen_indirect(0x15, 0x4); // foreground color
115 
116 	/* alpha hilite#3 */
117 	palette.set_pen_indirect(0x16, 0xf); // background color#3
118 	palette.set_pen_indirect(0x17, 0x4); // foreground color
119 
120 	/* shell graphics; these are either 1bpp (2 banks) or 2bpp.  It isn't clear which.
121 	 * In any event, the following pens are associated with the shell graphics:
122 	 */
123 	palette.set_pen_indirect(0x18, 0);
124 	palette.set_pen_indirect(0x19, 4);//1;
125 }
126 
127 /*
128 Color Array Ram Assignments:
129     Location
130         0               Blanking, border
131         1               Mot Obj (10) (D), Shell (01)
132         2               Mot Obj (01) (G), Shell (10)
133         3               Mot Obj (00) (W)
134         4               Alpha & Shell (11) - shields
135         5               Hilight 1
136         6               Hilight 2
137         8-E             Lines (as normal) background
138         F               Hilight 3
139 */
set_pens()140 void tunhunt_state::set_pens()
141 {
142 /*
143     The actual contents of the color proms (unused by this driver)
144     are as follows:
145 
146     D11 "blue/green"
147     0000:   00 00 8b 0b fb 0f ff 0b
148             00 00 0f 0f fb f0 f0 ff
149 
150     C11 "red"
151     0020:   00 f0 f0 f0 b0 b0 00 f0
152             00 f0 f0 00 b0 00 f0 f0
153 */
154 	//const uint8_t *color_prom = memregion( "proms" )->base();
155 	int color;
156 	int shade;
157 	int red,green,blue;
158 
159 	for( int i=0; i<16; i++ )
160 	{
161 		color = m_generic_paletteram_8[i];
162 		shade = 0xf^(color>>4);
163 
164 		color &= 0xf; /* hue select */
165 		switch( color )
166 		{
167 		default:
168 		case 0x0: red = 0xff; green = 0xff; blue = 0xff; break; /* white */
169 		case 0x1: red = 0xff; green = 0x00; blue = 0xff; break; /* purple */
170 		case 0x2: red = 0x00; green = 0x00; blue = 0xff; break; /* blue */
171 		case 0x3: red = 0x00; green = 0xff; blue = 0xff; break; /* cyan */
172 		case 0x4: red = 0x00; green = 0xff; blue = 0x00; break; /* green */
173 		case 0x5: red = 0xff; green = 0xff; blue = 0x00; break; /* yellow */
174 		case 0x6: red = 0xff; green = 0x00; blue = 0x00; break; /* red */
175 		case 0x7: red = 0x00; green = 0x00; blue = 0x00; break; /* black? */
176 
177 		case 0x8: red = 0xff; green = 0x7f; blue = 0x00; break; /* orange */
178 		case 0x9: red = 0x7f; green = 0xff; blue = 0x00; break; /* ? */
179 		case 0xa: red = 0x00; green = 0xff; blue = 0x7f; break; /* ? */
180 		case 0xb: red = 0x00; green = 0x7f; blue = 0xff; break; /* ? */
181 		case 0xc: red = 0xff; green = 0x00; blue = 0x7f; break; /* ? */
182 		case 0xd: red = 0x7f; green = 0x00; blue = 0xff; break; /* ? */
183 		case 0xe: red = 0xff; green = 0xaa; blue = 0xaa; break; /* ? */
184 		case 0xf: red = 0xaa; green = 0xaa; blue = 0xff; break; /* ? */
185 		}
186 
187 	/* combine color components with shade value (0..0xf) */
188 		#define APPLY_SHADE( C,S ) ((C*S)/0xf)
189 		red     = APPLY_SHADE(red,shade);
190 		green   = APPLY_SHADE(green,shade);
191 		blue    = APPLY_SHADE(blue,shade);
192 
193 		m_palette->set_indirect_color( i,rgb_t(red,green,blue) );
194 	}
195 }
196 
draw_motion_object(bitmap_ind16 & bitmap,const rectangle & cliprect)197 void tunhunt_state::draw_motion_object(bitmap_ind16 &bitmap, const rectangle &cliprect)
198 {
199 /*
200  *      VSTRLO  0x1202
201  *          normally 0x02 (gameplay, attract1)
202  *          in attract2 (with "Tunnel Hunt" graphic), decrements from 0x2f down to 0x01
203  *          goes to 0x01 for some enemy shots
204  *
205  *      MOBSC0  0x1080
206  *      MOBSC1  0x1081
207  *          always 0x00?
208  */
209 
210 	bitmap_ind16 &tmpbitmap = m_tmpbitmap;
211 	//int skip = m_workram[MOBST];
212 	const int x0 = 255 - m_workram[MOBJV];
213 	const int y0 = 255 - m_workram[MOBJH];
214 
215 	for (int line = 0; line < 64; line++)
216 	{
217 		int x = 0;
218 		const uint8_t *const source = &m_spriteram[line * 0x10];
219 		for (int span = 0; span < 0x10; span++)
220 		{
221 			const int span_data = source[span];
222 			if (span_data == 0xff) break;
223 			const int color = ((span_data >> 6) & 0x3) ^ 0x3;
224 			int count = (span_data & 0x1f) + 1;
225 			while (count-- && x < 256)
226 				tmpbitmap.pix(line, x++) = color;
227 		}
228 		while (x < 256)
229 			tmpbitmap.pix(line, x++) = 0;
230 	}
231 
232 	int scaley;
233 	switch (m_workram[VSTRLO])
234 	{
235 	case 0x01:
236 		scaley = (1 << 16) * 0.33; // seems correct
237 		break;
238 
239 	case 0x02:
240 		scaley = (1 << 16) * 0.50; // seems correct
241 		break;
242 
243 	default:
244 		scaley = (1 << 16) * m_workram[VSTRLO] / 4; // ???
245 		break;
246 	}
247 	const int scalex = 1 << 16;
248 
249 	copyrozbitmap_trans(
250 			bitmap, cliprect, tmpbitmap,
251 			-x0 * scalex, // startx
252 			-y0 * scaley, // starty
253 			scalex, // incxx
254 			0, 0, // incxy, incyx
255 			scaley, // incyy
256 			false, // no wraparound
257 			0);
258 }
259 
draw_box(bitmap_ind16 & bitmap,const rectangle & cliprect)260 void tunhunt_state::draw_box(bitmap_ind16 &bitmap, const rectangle &cliprect)
261 {
262 /*
263     This is unnecessarily slow, but the box priorities aren't completely understood,
264     yet.  Once understood, this function should be converted to use bitmap_fill with
265     rectangular chunks instead of BITMAP_ADDR.
266 
267     Tunnels:
268         1080: 00 00 00      01  e7 18   ae 51   94 6b   88 77   83 7c   80 7f   x0
269         1480: 00 f0 17      00  22 22   5b 5b   75 75   81 81   86 86   89 89   y0
270         1400: 00 00 97      ff  f1 f1   b8 b8   9e 9e   92 92   8d 8d   8a 8a   y1
271         1280: 07 03 00      07  07 0c   0c 0d   0d 0e   0e 08   08 09   09 0a   palette select
272 
273     Color Bars:
274         1080: 00 00 00      01  00 20 40 60 80 a0 c0 e0     01 2a   50 7a       x0
275         1480: 00 f0 00      00  40 40 40 40 40 40 40 40     00 00   00 00       y0
276         1400: 00 00 00      ff  ff ff ff ff ff ff ff ff     40 40   40 40       y1
277         1280: 07 03 00      01  07 06 04 05 02 07 03 00     09 0a   0b 0c       palette select
278         ->hue 06 02 ff      60  06 05 03 04 01 06 02 ff     d2 00   c2 ff
279 */
280 	int span,x,y;
281 	int color;
282 //  rectangle bbox;
283 	int z;
284 	int x0,y0,y1;
285 
286 	for( y=0; y<256; y++ )
287 	{
288 		if (0xff-y >= cliprect.top() && 0xff-y <= cliprect.bottom())
289 			for( x=0; x<256; x++ )
290 			{
291 				color = 0;
292 				z = 0;
293 				for( span=3; span<16; span++ )
294 				{
295 					x0 = m_workram[span+0x1080];
296 					y0 = m_workram[span+0x1480];
297 					y1 = m_workram[span+0x1400];
298 
299 					if( y>=y0 && y<=y1 && x>=x0 && x0>=z )
300 					{
301 						color = m_workram[span+0x1280]&0xf;
302 						z = x0; /* give priority to rightmost spans */
303 					}
304 				}
305 				if (x >= cliprect.left() && x <= cliprect.right())
306 					bitmap.pix(0xff-y, x) = color;
307 			}
308 	}
309 }
310 
311 /* "shell" graphics are 16x16 pixel tiles used for player shots and targeting cursor */
draw_shell(bitmap_ind16 & bitmap,const rectangle & cliprect,int picture_code,int hposition,int vstart,int vstop,int vstretch,int hstretch)312 void tunhunt_state::draw_shell(bitmap_ind16 &bitmap,
313 		const rectangle &cliprect,
314 		int picture_code,
315 		int hposition,
316 		int vstart,
317 		int vstop,
318 		int vstretch,
319 		int hstretch )
320 {
321 	if( hstretch )
322 	{
323 		int sx,sy;
324 		for( sx=0; sx<256; sx+=16 )
325 		{
326 			for( sy=0; sy<256; sy+=16 )
327 			{
328 					m_gfxdecode->gfx(1)->transpen(bitmap,cliprect,
329 					picture_code,
330 					0, /* color */
331 					0,0, /* flip */
332 					sx,sy,0 );
333 			}
334 		}
335 	}
336 	else
337 	/*
338 	    vstretch is normally 0x01
339 
340 	    targeting cursor:
341 	        hposition   = 0x78
342 	        vstart      = 0x90
343 	        vstop       = 0x80
344 
345 	    during grid test:
346 	        vstretch    = 0xff
347 	        hposition   = 0xff
348 	        vstart      = 0xff
349 	        vstop       = 0x00
350 
351 	*/
352 
353 			m_gfxdecode->gfx(1)->transpen(bitmap,cliprect,
354 			picture_code,
355 			0, /* color */
356 			0,0, /* flip */
357 			255-hposition-16,vstart-32,0 );
358 }
359 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)360 uint32_t tunhunt_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
361 {
362 	set_pens();
363 
364 	draw_box(bitmap, cliprect);
365 
366 	draw_motion_object(bitmap, cliprect);
367 
368 	draw_shell(bitmap, cliprect,
369 		m_workram[SHL0PC],  /* picture code */
370 		m_workram[SHEL0H],  /* hposition */
371 		m_workram[SHL0V],   /* vstart */
372 		m_workram[SHL0VS],  /* vstop */
373 		m_workram[SHL0ST],  /* vstretch */
374 		m_control&0x08 ); /* hstretch */
375 
376 	draw_shell(bitmap, cliprect,
377 		m_workram[SHL1PC],  /* picture code */
378 		m_workram[SHEL1H],  /* hposition */
379 		m_workram[SHL1V],   /* vstart */
380 		m_workram[SHL1VS],  /* vstop */
381 		m_workram[SHL1ST],  /* vstretch */
382 		m_control&0x10 ); /* hstretch */
383 
384 	m_fg_tilemap->draw(screen, bitmap, cliprect, 0, 0);
385 	return 0;
386 }
387