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