1 // license:BSD-3-Clause
2 // copyright-holders:Stefan Jokisch
3 /***************************************************************************
4 
5 Atari Tank 8 video emulation
6 
7 ***************************************************************************/
8 
9 #include "emu.h"
10 #include "includes/tank8.h"
11 
12 
tank8_palette(palette_device & palette) const13 void tank8_state::tank8_palette(palette_device &palette) const
14 {
15 	palette.set_indirect_color(8, rgb_t(0x00, 0x00, 0x00));
16 	palette.set_indirect_color(9, rgb_t(0xff, 0xff, 0xff));
17 
18 	for (int i = 0; i < 8; i++)
19 	{
20 		palette.set_pen_indirect(2 * i + 0, 8);
21 		palette.set_pen_indirect(2 * i + 1, i);
22 	}
23 
24 	/* walls */
25 	palette.set_pen_indirect(0x10, 8);
26 	palette.set_pen_indirect(0x11, 9);
27 
28 	/* mines */
29 	palette.set_pen_indirect(0x12, 8);
30 	palette.set_pen_indirect(0x13, 9);
31 }
32 
33 
set_pens()34 void tank8_state::set_pens()
35 {
36 	if (*m_team & 0x01)
37 	{
38 		m_palette->set_indirect_color(0, rgb_t(0xff, 0x00, 0x00)); /* red     */
39 		m_palette->set_indirect_color(1, rgb_t(0x00, 0x00, 0xff)); /* blue    */
40 		m_palette->set_indirect_color(2, rgb_t(0xff, 0xff, 0x00)); /* yellow  */
41 		m_palette->set_indirect_color(3, rgb_t(0x00, 0xff, 0x00)); /* green   */
42 		m_palette->set_indirect_color(4, rgb_t(0xff, 0x00, 0xff)); /* magenta */
43 		m_palette->set_indirect_color(5, rgb_t(0xe0, 0xc0, 0x70)); /* puce    */
44 		m_palette->set_indirect_color(6, rgb_t(0x00, 0xff, 0xff)); /* cyan    */
45 		m_palette->set_indirect_color(7, rgb_t(0xff, 0xaa, 0xaa)); /* pink    */
46 	}
47 	else
48 	{
49 		m_palette->set_indirect_color(0, rgb_t(0xff, 0x00, 0x00)); /* red     */
50 		m_palette->set_indirect_color(2, rgb_t(0xff, 0x00, 0x00)); /* red     */
51 		m_palette->set_indirect_color(4, rgb_t(0xff, 0x00, 0x00)); /* red     */
52 		m_palette->set_indirect_color(6, rgb_t(0xff, 0x00, 0x00)); /* red     */
53 		m_palette->set_indirect_color(1, rgb_t(0x00, 0x00, 0xff)); /* blue    */
54 		m_palette->set_indirect_color(3, rgb_t(0x00, 0x00, 0xff)); /* blue    */
55 		m_palette->set_indirect_color(5, rgb_t(0x00, 0x00, 0xff)); /* blue    */
56 		m_palette->set_indirect_color(7, rgb_t(0x00, 0x00, 0xff)); /* blue    */
57 	}
58 }
59 
60 
video_ram_w(offs_t offset,uint8_t data)61 void tank8_state::video_ram_w(offs_t offset, uint8_t data)
62 {
63 	m_video_ram[offset] = data;
64 	m_tilemap->mark_tile_dirty(offset);
65 }
66 
67 
68 
TILE_GET_INFO_MEMBER(tank8_state::get_tile_info)69 TILE_GET_INFO_MEMBER(tank8_state::get_tile_info)
70 {
71 	uint8_t code = m_video_ram[tile_index];
72 
73 	int color = 0;
74 
75 	if ((code & 0x38) == 0x28)
76 	{
77 		if ((code & 7) != 3)
78 			color = 8; /* walls */
79 		else
80 			color = 9; /* mines */
81 	}
82 	else
83 	{
84 		if (tile_index & 0x010)
85 			color |= 1;
86 
87 		if (code & 0x80)
88 			color |= 2;
89 
90 		if (tile_index & 0x200)
91 			color |= 4;
92 	}
93 
94 	tileinfo.set(code >> 7, code, color, (code & 0x40) ? (TILE_FLIPX | TILE_FLIPY) : 0);
95 }
96 
97 
98 
video_start()99 void tank8_state::video_start()
100 {
101 	m_collision_timer = timer_alloc(TIMER_COLLISION);
102 
103 	m_screen->register_screen_bitmap(m_helper1);
104 	m_screen->register_screen_bitmap(m_helper2);
105 	m_screen->register_screen_bitmap(m_helper3);
106 
107 	m_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tank8_state::get_tile_info)), TILEMAP_SCAN_ROWS, 16, 16, 32, 32);
108 
109 	// VBLANK starts on scanline #256 and ends on scanline #24
110 	m_tilemap->set_scrolly(0, 2 * 24);
111 
112 	save_item(NAME(m_collision_index));
113 }
114 
115 
get_x_pos(int n)116 int tank8_state::get_x_pos(int n)
117 {
118 	return 498 - m_pos_h_ram[n] - 2 * (m_pos_d_ram[n] & 128); /* ? */
119 }
120 
121 
get_y_pos(int n)122 int tank8_state::get_y_pos(int n)
123 {
124 	return 2 * m_pos_v_ram[n] - 62;
125 }
126 
127 
draw_sprites(bitmap_ind16 & bitmap,const rectangle & cliprect)128 void tank8_state::draw_sprites(bitmap_ind16 &bitmap, const rectangle &cliprect)
129 {
130 	int i;
131 
132 	for (i = 0; i < 8; i++)
133 	{
134 		uint8_t code = ~m_pos_d_ram[i];
135 
136 		int x = get_x_pos(i);
137 		int y = get_y_pos(i);
138 
139 		m_gfxdecode->gfx((code & 0x04) ? 2 : 3)->transpen(bitmap,cliprect,
140 			code & 0x03,
141 			i,
142 			code & 0x10,
143 			code & 0x08,
144 			x,
145 			y, 0);
146 	}
147 }
148 
149 
draw_bullets(bitmap_ind16 & bitmap,const rectangle & cliprect)150 void tank8_state::draw_bullets(bitmap_ind16 &bitmap, const rectangle &cliprect)
151 {
152 	int i;
153 
154 	for (i = 0; i < 8; i++)
155 	{
156 		int x = get_x_pos(8 + i);
157 		int y = get_y_pos(8 + i);
158 
159 		x -= 4; /* ? */
160 
161 		rectangle rect(x, x + 3, y, y + 4);
162 		rect &= cliprect;
163 
164 		bitmap.fill((i << 1) | 0x01, rect);
165 	}
166 }
167 
168 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)169 void tank8_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
170 {
171 	switch (id)
172 	{
173 	case TIMER_COLLISION:
174 		set_collision(param);
175 		break;
176 	default:
177 		throw emu_fatalerror("Unknown id in tank8_state::device_timer");
178 	}
179 }
180 
181 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)182 uint32_t tank8_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
183 {
184 	set_pens();
185 	m_tilemap->draw(screen, bitmap, cliprect, 0, 0);
186 
187 	draw_sprites(bitmap, cliprect);
188 	draw_bullets(bitmap, cliprect);
189 	return 0;
190 }
191 
192 
WRITE_LINE_MEMBER(tank8_state::screen_vblank)193 WRITE_LINE_MEMBER(tank8_state::screen_vblank)
194 {
195 	// on falling edge
196 	if (!state)
197 	{
198 		const rectangle &visarea = m_screen->visible_area();
199 
200 		m_tilemap->draw(*m_screen, m_helper1, visarea, 0, 0);
201 
202 		m_helper2.fill(8, visarea);
203 		m_helper3.fill(8, visarea);
204 
205 		draw_sprites(m_helper2, visarea);
206 		draw_bullets(m_helper3, visarea);
207 
208 		for (int y = visarea.top(); y <= visarea.bottom(); y++)
209 		{
210 			int _state = 0;
211 
212 			uint16_t const *const p1 = &m_helper1.pix(y);
213 			uint16_t const *const p2 = &m_helper2.pix(y);
214 			uint16_t const *const p3 = &m_helper3.pix(y);
215 
216 			if ((m_screen->frame_number() ^ y) & 1)
217 				continue; /* video display is interlaced */
218 
219 			for (int x = visarea.left(); x <= visarea.right(); x++)
220 			{
221 				/* neither wall nor mine */
222 				if ((p1[x] != 0x11) && (p1[x] != 0x13))
223 				{
224 					_state = 0;
225 					continue;
226 				}
227 
228 				/* neither tank nor bullet */
229 				if ((p2[x] == 8) && (p3[x] == 8))
230 				{
231 					_state = 0;
232 					continue;
233 				}
234 
235 				/* bullets cannot hit mines */
236 				if ((p3[x] != 8) && (p1[x] == 0x13))
237 				{
238 					_state = 0;
239 					continue;
240 				}
241 
242 				if (_state)
243 					continue;
244 
245 				uint8_t index;
246 				if (p3[x] != 8)
247 				{
248 					index = ((p3[x] & ~0x01) >> 1) | 0x18;
249 
250 					if (1)
251 						index |= 0x20;
252 
253 					if (0)
254 						index |= 0x40;
255 
256 					if (1)
257 						index |= 0x80;
258 				}
259 				else
260 				{
261 					int sprite_num = (p2[x] & ~0x01) >> 1;
262 					index = sprite_num | 0x10;
263 
264 					if (p1[x] == 0x11)
265 						index |= 0x20;
266 
267 					if (y - get_y_pos(sprite_num) >= 8)
268 						index |= 0x40; /* collision on bottom side */
269 
270 					if (x - get_x_pos(sprite_num) >= 8)
271 						index |= 0x80; /* collision on right side */
272 				}
273 
274 				m_collision_timer->adjust(m_screen->time_until_pos(y, x), index);
275 
276 				_state = 1;
277 			}
278 		}
279 	}
280 }
281