1 // license:BSD-3-Clause
2 // copyright-holders:Philip Bennett
3 /*************************************************************************
4 
5     Turrett Tower video hardware
6 
7 *************************************************************************/
8 
9 #include "emu.h"
10 #include "machine/idectrl.h"
11 #include "includes/turrett.h"
12 
13 
14 
clamp_5bit(int8_t val)15 inline uint8_t clamp_5bit(int8_t val)
16 {
17 	if (val < 0)
18 		return 0;
19 
20 	if (val > 31)
21 		return 31;
22 
23 	return val;
24 }
25 
26 
screen_update(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)27 uint32_t turrett_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
28 {
29 	int page = (m_video_ctrl & 1) ^ 1;
30 
31 	const uint16_t *vram = m_video_ram[page].get();
32 
33 	int8_t fade_b = m_video_fade & 0x1f;
34 	int8_t fade_g = (m_video_fade >> 5) & 0x1f;
35 	int8_t fade_r = (m_video_fade >> 10) & 0x1f;
36 
37 	if (m_video_fade & 0x8000)
38 	{
39 		fade_b = -fade_b;
40 		fade_g = -fade_g;
41 		fade_r = -fade_r;
42 	}
43 
44 	for (int y = cliprect.min_y; y <= cliprect.max_y; ++y)
45 	{
46 		const uint16_t *src = &vram[y * X_VISIBLE + cliprect.min_x];
47 		uint16_t *dest = &bitmap.pix(y, cliprect.min_x);
48 
49 		if (m_video_fade != 0)
50 		{
51 			for (int x = cliprect.min_x; x <= cliprect.max_x; ++x)
52 			{
53 				uint16_t srcpix = *src++;
54 
55 				uint8_t src_b = srcpix & 0x1f;
56 				uint8_t src_g = (srcpix >> 5) & 0x1f;
57 				uint8_t src_r = (srcpix >> 10) & 0x1f;
58 
59 				uint8_t dst_b = clamp_5bit(src_b + fade_b);
60 				uint8_t dst_g = clamp_5bit(src_g + fade_g);
61 				uint8_t dst_r = clamp_5bit(src_r + fade_r);
62 
63 				*dest++ = (dst_r << 10) | (dst_g << 5) | dst_b;
64 			}
65 		}
66 		else
67 		{
68 			for (int x = cliprect.min_x; x <= cliprect.max_x; ++x)
69 			{
70 				*dest++ = *src++ & 0x7fff;
71 			}
72 		}
73 	}
74 
75 	return 0;
76 }
77 
78 
write_video_ram(uint16_t data)79 uint32_t turrett_state::write_video_ram(uint16_t data)
80 {
81 	uint32_t clocks = 1;
82 
83 	if (!m_skip_x && !m_skip_y)
84 	{
85 		// Handle hot spot test
86 		if (m_x_pos == m_hotspot_x
87 		&&  m_y_pos == (m_hotspot_y & 0xfff))
88 		{
89 			m_hotspot_y |= 0x8000;
90 		}
91 
92 		if (m_x_pos >= 0 && m_x_pos < X_VISIBLE
93 		&&  m_y_pos >= 0 && m_y_pos < Y_VISIBLE)
94 		{
95 			int address = m_y_pos * X_VISIBLE + m_x_pos;
96 
97 			uint16_t *vramptr = &m_video_ram[m_video_ctrl & 1][address];
98 			uint16_t srcpix = data;
99 			uint16_t dstpix = data;
100 
101 			// Blending enabled?
102 			if (data & 0x8000)
103 			{
104 				dstpix = *vramptr;
105 
106 				uint8_t src_b = srcpix & 0x1f;
107 				uint8_t src_g = (srcpix >> 5) & 0x1f;
108 				uint8_t src_r = (srcpix >> 10) & 0x1f;
109 
110 				uint8_t dst_b = dstpix & 0x1f;
111 				uint8_t dst_g = (dstpix >> 5) & 0x1f;
112 				uint8_t dst_r = (dstpix >> 10) & 0x1f;
113 
114 				// Additive
115 				if (m_video_ctrl & 2)
116 				{
117 					dst_b = clamp_5bit(src_b + dst_b);
118 					dst_g = clamp_5bit(src_g + dst_g);
119 					dst_r = clamp_5bit(src_r + dst_r);
120 				}
121 				else
122 				{
123 					// R always seems to be 0 for blended pixels
124 					if ((src_g & 1) && (src_b & 1))
125 					{
126 						dst_b = clamp_5bit(dst_b - src_b);
127 						dst_g = clamp_5bit(dst_g - src_g);
128 						dst_r = clamp_5bit(dst_r - src_r);
129 					}
130 					else
131 					{
132 						// 75% source, 25% destination?
133 						dst_b = (src_b - (src_b >> 2)) + (dst_b >> 2);
134 						dst_g = (src_g - (src_g >> 2)) + (dst_g >> 2);
135 						dst_r = (src_r - (src_r >> 2)) + (dst_r >> 2);
136 					}
137 				}
138 				clocks += 2;
139 				*vramptr = (dst_r << 10) | (dst_g << 5) | dst_b;
140 			}
141 			else
142 			{
143 				clocks += 2;
144 				*vramptr = srcpix;
145 			}
146 		}
147 	}
148 	update_video_addr();
149 
150 	return clocks;
151 }
152 
153 
update_video_addr(void)154 void turrett_state::update_video_addr(void)
155 {
156 	// Handle auto-increment
157 	if (m_dx == m_x_mod)
158 	{
159 		m_dx = 0;
160 		m_scale_cnt_y += m_scale;
161 
162 		if (m_scale_cnt_y & 0x800)
163 		{
164 			m_skip_y = false;
165 			m_scale_cnt_y &= 0x7ff;
166 			--m_y_pos;
167 			m_x_pos = m_x_start;
168 		}
169 		else
170 		{
171 			m_skip_y = true;
172 		}
173 	}
174 	else
175 	{
176 		++m_dx;
177 		m_scale_cnt_x += m_scale;
178 
179 		if (m_scale_cnt_x & 0x800)
180 		{
181 			m_scale_cnt_x &= 0x7ff;
182 			m_skip_x = false;
183 			++m_x_pos;
184 		}
185 		else
186 		{
187 			m_skip_x = true;
188 		}
189 	}
190 }
191 
192 
video_r(offs_t offset,uint32_t mem_mask)193 uint32_t turrett_state::video_r(offs_t offset, uint32_t mem_mask)
194 {
195 	uint32_t ret = 0;
196 
197 	if (offset == 3 && mem_mask == 0x0000ffff)
198 	{
199 		// Collision detection flag
200 		ret = m_hotspot_y & 0x8000;
201 	}
202 	else
203 	{
204 		fatalerror("Unhandled video read (%x %x)!", offset, mem_mask);
205 	}
206 
207 	return ret;
208 }
209 
210 
video_w(offs_t offset,uint32_t data,uint32_t mem_mask)211 void turrett_state::video_w(offs_t offset, uint32_t data, uint32_t mem_mask)
212 {
213 	switch (offset)
214 	{
215 		case 0:
216 		{
217 			if (mem_mask == 0xffff0000)
218 			{
219 				data >>= 16;
220 
221 				// TODO: Merge with DMA code?
222 				if ((data & 0xc400) == 0xc400)
223 				{
224 					// RLE word
225 					int count = (data & 0x3ff) + 1;
226 
227 					// TODO: Cycle stalling
228 					while (count--)
229 						write_video_ram(m_last_pixel);
230 				}
231 				else
232 				{
233 					write_video_ram(data);
234 
235 					// Store current pixel
236 					m_last_pixel = data;
237 				}
238 			}
239 			else
240 			{
241 				m_video_ctrl = data & 3;
242 			}
243 			break;
244 		}
245 		case 1:
246 		{
247 			m_x_mod = data & 0xffff;
248 			m_scale = 0x800 - (data >> 16);
249 			break;
250 		}
251 		case 2:
252 		{
253 			m_y_pos = data & 0xffff;
254 			m_x_pos = data >> 16;
255 
256 			// Seems the logical place to set these
257 			m_x_start = m_x_pos;
258 			m_skip_x = false;
259 			m_skip_y = false;
260 			m_dx = 0;
261 			break;
262 		}
263 		case 3:
264 		{
265 			if (mem_mask == 0xffff0000)
266 			{
267 				m_video_fade = data >> 16;
268 			}
269 			else if (mem_mask == 0x0000ffff)
270 			{
271 				if (data & 0x4000)
272 					m_hotspot_y = data;
273 				else
274 					m_hotspot_x = data;
275 			}
276 			else
277 			{
278 				fatalerror("Unhandled");
279 			}
280 			break;
281 		}
282 		default:
283 			fatalerror("Unhandled video write: %x %x\n", offset, data);
284 	}
285 }
286 
287 
TIMER_CALLBACK_MEMBER(turrett_state::dma_complete)288 TIMER_CALLBACK_MEMBER( turrett_state::dma_complete )
289 {
290 	m_dma_idle = true;
291 }
292 
293 
dma_w(offs_t offset,uint32_t data)294 void turrett_state::dma_w(offs_t offset, uint32_t data)
295 {
296 	int bank = ((offset & 2) >> 1) ^ 1;
297 
298 	if ((offset & 1) == 0)
299 	{
300 		m_dma_addr[bank] = data;
301 	}
302 	else
303 	{
304 		uint32_t clocks = 0;
305 		uint32_t words = data & 0x0fffffff;
306 
307 		// IDE to DRAM
308 		if (data & 0x10000000)
309 		{
310 			uint32_t addr = m_dma_addr[bank];
311 			uint16_t *ram = bank ? m_bank_b : m_bank_a;
312 
313 			while (words--)
314 			{
315 				ram[addr & DIMM_BANK_MASK] = m_ata->cs0_r(0);
316 				++addr;
317 			}
318 
319 			clocks = 500; // TODO: May be too high
320 			m_dma_addr[bank] = addr;
321 		}
322 		// IDE to video RAM
323 		else if (data & 0x40000000)
324 		{
325 			while (words--)
326 			{
327 				uint16_t data = m_ata->cs0_r(0);
328 
329 				// TODO: Verify if this is correct
330 				if ((data & 0xc400) == 0xc400)
331 				{
332 					fatalerror("IDE RLE detected");
333 
334 					// RLE word
335 					int count = (data & 0x3ff) + 1;
336 
337 					while (count--)
338 						write_video_ram(m_last_pixel);
339 				}
340 				else
341 				{
342 					write_video_ram(data);
343 
344 					// Store current pixel
345 					m_last_pixel = data;
346 				}
347 			}
348 
349 			clocks = 500; // TODO
350 		}
351 		// RAM to video RAM
352 		else if (data & 0x80000000)
353 		{
354 			uint32_t addr = m_dma_addr[bank];
355 			uint16_t *ram = bank ? m_bank_b : m_bank_a;
356 
357 			//bool first = true; // Does it matter?
358 
359 			while (words--)
360 			{
361 				uint16_t val = ram[addr++];
362 				//++clocks;
363 
364 				switch (val & 0xc400)
365 				{
366 					// Transparent run
367 					case 0x8400:
368 					{
369 						int run = (((val & 0x3800) >> 1) | (val & 0x03ff)) + 1;
370 
371 						while (run--)
372 						{
373 							update_video_addr();
374 							//++clocks;
375 						}
376 
377 						break;
378 					}
379 
380 					case 0xc400:
381 					{
382 						int run = (((val & 0x3800) >> 1) | (val & 0x03ff)) + 1;
383 
384 						while (run--)
385 							clocks += write_video_ram(m_last_pixel);
386 
387 						break;
388 					}
389 
390 					default:
391 					{
392 						m_last_pixel = val;
393 						clocks += write_video_ram(val);
394 						break;
395 					}
396 				}
397 				//first = false;
398 			}
399 
400 		//   clocks =1;///= 2;
401 		}
402 		else
403 		{
404 			popmessage("Unhandled DMA case: %.8x, contact MAMEdev!\n", data);
405 		}
406 
407 		// Set the DMA completion timer
408 		m_dma_idle = false;
409 		m_dma_timer->adjust(attotime::from_nsec(10) * clocks, 0);
410 	}
411 }
412