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