1 // license:BSD-3-Clause
2 // copyright-holders:Alex Pasadyn, Zsolt Vasvari, Ernesto Corvi, Aaron Giles
3 // thanks-to:Kurt Mahan
4 /*************************************************************************
5 
6     Williams/Midway Y/Z-unit system
7 
8 **************************************************************************/
9 
10 #include "emu.h"
11 #include "includes/midyunit.h"
12 #include "screen.h"
13 
14 
15 /* compile-time options */
16 #define LOG_DMA             0       /* DMAs are logged if the 'L' key is pressed */
17 
18 
19 /* constants for the DMA chip */
20 enum
21 {
22 	DMA_COMMAND = 0,
23 	DMA_ROWBYTES,
24 	DMA_OFFSETLO,
25 	DMA_OFFSETHI,
26 	DMA_XSTART,
27 	DMA_YSTART,
28 	DMA_WIDTH,
29 	DMA_HEIGHT,
30 	DMA_PALETTE,
31 	DMA_COLOR
32 };
33 
34 
35 
36 /*************************************
37  *
38  *  Video startup
39  *
40  *************************************/
41 
VIDEO_START_MEMBER(midyunit_state,common)42 VIDEO_START_MEMBER(midyunit_state,common)
43 {
44 	/* allocate memory */
45 	m_cmos_ram = std::make_unique<uint16_t[]>((0x2000 * 4)/2);
46 	m_local_videoram = make_unique_clear<uint16_t[]>(0x80000/2);
47 	m_pen_map = std::make_unique<pen_t[]>(65536);
48 
49 	m_nvram->set_base(m_cmos_ram.get(), 0x2000 * 4);
50 
51 	m_dma_timer = timer_alloc(TIMER_DMA);
52 	m_autoerase_line_timer = timer_alloc(TIMER_AUTOERASE_LINE);
53 
54 	/* reset all the globals */
55 	m_cmos_page = 0;
56 	m_autoerase_enable = 0;
57 	m_yawdim_dma = 0;
58 
59 	/* reset DMA state */
60 	memset(m_dma_register, 0, sizeof(m_dma_register));
61 	memset(&m_dma_state, 0, sizeof(m_dma_state));
62 
63 	/* register for state saving */
64 	save_item(NAME(m_autoerase_enable));
65 	save_pointer(NAME(m_local_videoram), 0x80000/2);
66 	save_pointer(NAME(m_cmos_ram), (0x2000 * 4)/2);
67 	save_item(NAME(m_videobank_select));
68 	save_item(NAME(m_dma_register));
69 }
70 
71 
VIDEO_START_MEMBER(midyunit_state,midyunit_4bit)72 VIDEO_START_MEMBER(midyunit_state,midyunit_4bit)
73 {
74 	int i;
75 
76 	VIDEO_START_CALL_MEMBER(common);
77 
78 	/* init for 4-bit */
79 	for (i = 0; i < 65536; i++)
80 		m_pen_map[i] = ((i & 0xf000) >> 8) | (i & 0x000f);
81 	m_palette_mask = 0x00ff;
82 }
83 
84 
VIDEO_START_MEMBER(midyunit_state,midyunit_6bit)85 VIDEO_START_MEMBER(midyunit_state,midyunit_6bit)
86 {
87 	int i;
88 
89 	VIDEO_START_CALL_MEMBER(common);
90 
91 	/* init for 6-bit */
92 	for (i = 0; i < 65536; i++)
93 		m_pen_map[i] = ((i & 0xc000) >> 8) | (i & 0x0f3f);
94 	m_palette_mask = 0x0fff;
95 }
96 
97 
VIDEO_START_MEMBER(midyunit_state,mkyawdim)98 VIDEO_START_MEMBER(midyunit_state,mkyawdim)
99 {
100 	VIDEO_START_CALL_MEMBER(midyunit_6bit);
101 	m_yawdim_dma = 1;
102 }
103 
104 
VIDEO_START_MEMBER(midyunit_state,midzunit)105 VIDEO_START_MEMBER(midyunit_state,midzunit)
106 {
107 	int i;
108 
109 	VIDEO_START_CALL_MEMBER(common);
110 
111 	/* init for 8-bit */
112 	for (i = 0; i < 65536; i++)
113 		m_pen_map[i] = i & 0x1fff;
114 	m_palette_mask = 0x1fff;
115 }
116 
117 
118 
119 /*************************************
120  *
121  *  Banked graphics ROM access
122  *
123  *************************************/
124 
midyunit_gfxrom_r(offs_t offset)125 uint16_t midyunit_state::midyunit_gfxrom_r(offs_t offset)
126 {
127 	offset *= 2;
128 	if (m_palette_mask == 0x00ff)
129 		return m_gfx_rom[offset] | (m_gfx_rom[offset] << 4) |
130 				(m_gfx_rom[offset + 1] << 8) | (m_gfx_rom[offset + 1] << 12);
131 	else
132 		return m_gfx_rom[offset] | (m_gfx_rom[offset + 1] << 8);
133 }
134 
135 
136 
137 /*************************************
138  *
139  *  Video/color RAM read/write
140  *
141  *************************************/
142 
midyunit_vram_w(offs_t offset,uint16_t data,uint16_t mem_mask)143 void midyunit_state::midyunit_vram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
144 {
145 	offset *= 2;
146 	if (m_videobank_select)
147 	{
148 		if (ACCESSING_BITS_0_7)
149 			m_local_videoram[offset] = (data & 0x00ff) | (m_dma_register[DMA_PALETTE] << 8);
150 		if (ACCESSING_BITS_8_15)
151 			m_local_videoram[offset + 1] = (data >> 8) | (m_dma_register[DMA_PALETTE] & 0xff00);
152 	}
153 	else
154 	{
155 		if (ACCESSING_BITS_0_7)
156 			m_local_videoram[offset] = (m_local_videoram[offset] & 0x00ff) | (data << 8);
157 		if (ACCESSING_BITS_8_15)
158 			m_local_videoram[offset + 1] = (m_local_videoram[offset + 1] & 0x00ff) | (data & 0xff00);
159 	}
160 }
161 
162 
midyunit_vram_r(offs_t offset)163 uint16_t midyunit_state::midyunit_vram_r(offs_t offset)
164 {
165 	offset *= 2;
166 	if (m_videobank_select)
167 		return (m_local_videoram[offset] & 0x00ff) | (m_local_videoram[offset + 1] << 8);
168 	else
169 		return (m_local_videoram[offset] >> 8) | (m_local_videoram[offset + 1] & 0xff00);
170 }
171 
172 
173 
174 /*************************************
175  *
176  *  Shift register read/write
177  *
178  *************************************/
179 
TMS340X0_TO_SHIFTREG_CB_MEMBER(midyunit_state::to_shiftreg)180 TMS340X0_TO_SHIFTREG_CB_MEMBER(midyunit_state::to_shiftreg)
181 {
182 	memcpy(shiftreg, &m_local_videoram[address >> 3], 2 * 512 * sizeof(uint16_t));
183 }
184 
185 
TMS340X0_FROM_SHIFTREG_CB_MEMBER(midyunit_state::from_shiftreg)186 TMS340X0_FROM_SHIFTREG_CB_MEMBER(midyunit_state::from_shiftreg)
187 {
188 	memcpy(&m_local_videoram[address >> 3], shiftreg, 2 * 512 * sizeof(uint16_t));
189 }
190 
191 
192 
193 /*************************************
194  *
195  *  Y/Z-unit control register
196  *
197  *************************************/
198 
midyunit_control_w(offs_t offset,uint16_t data,uint16_t mem_mask)199 void midyunit_state::midyunit_control_w(offs_t offset, uint16_t data, uint16_t mem_mask)
200 {
201 	/*
202 	 * Narc system register
203 	 * ------------------
204 	 *
205 	 *   | Bit              | Use
206 	 * --+-FEDCBA9876543210-+------------
207 	 *   | xxxxxxxx-------- |   7 segment led on CPU board
208 	 *   | --------xx------ |   CMOS page
209 	 *   | ----------x----- | - OBJ PAL RAM select
210 	 *   | -----------x---- | - autoerase enable
211 	 *   | ---------------- | - watchdog
212 	 *
213 	 */
214 
215 	if (ACCESSING_BITS_0_7)
216 	{
217 		/* CMOS page is bits 6-7 */
218 		m_cmos_page = ((data >> 6) & 3) * 0x1000;
219 
220 		/* video bank select is bit 5 */
221 		m_videobank_select = (data >> 5) & 1;
222 
223 		/* handle autoerase disable (bit 4) */
224 		m_autoerase_enable = ((data & 0x10) == 0);
225 	}
226 }
227 
228 
229 
230 /*************************************
231  *
232  *  Palette handlers
233  *
234  *************************************/
235 
midyunit_paletteram_w(offs_t offset,uint16_t data,uint16_t mem_mask)236 void midyunit_state::midyunit_paletteram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
237 {
238 	int newword;
239 
240 	COMBINE_DATA(&m_generic_paletteram_16[offset]);
241 	newword = m_generic_paletteram_16[offset];
242 	m_palette->set_pen_color(offset & m_palette_mask, pal5bit(newword >> 10), pal5bit(newword >> 5), pal5bit(newword >> 0));
243 }
244 
245 
246 
247 /*************************************
248  *
249  *  DMA drawing routines
250  *
251  *************************************/
252 
dma_draw(uint16_t command)253 void midyunit_state::dma_draw(uint16_t command)
254 {
255 	struct dma_state_t &dma_state = m_dma_state;
256 	int dx = (command & 0x10) ? -1 : 1;
257 	int height = dma_state.height;
258 	int width = dma_state.width;
259 	uint8_t *base = m_gfx_rom;
260 	uint32_t offset = dma_state.offset >> 3;
261 	uint16_t pal = dma_state.palette;
262 	uint16_t color = pal | dma_state.color;
263 	int x, y;
264 
265 	/* we only need the low 4 bits of the command */
266 	command &= 0x0f;
267 
268 	/* loop over the height */
269 	for (y = 0; y < height; y++)
270 	{
271 		int tx = dma_state.xpos;
272 		int ty = dma_state.ypos;
273 		uint32_t o = offset;
274 		uint16_t *dest;
275 
276 		/* determine Y position */
277 		ty = (ty + y) & 0x1ff;
278 		offset += dma_state.rowbytes;
279 
280 		/* determine destination pointer */
281 		dest = &m_local_videoram[ty * 512];
282 
283 		/* check for overruns if they are relevant */
284 		if (o >= 0x06000000 && command < 0x0c)
285 			continue;
286 
287 		/* switch off the zero/non-zero options */
288 		switch (command)
289 		{
290 			case 0x00:  /* draw nothing */
291 				break;
292 
293 			case 0x01:  /* draw only 0 pixels */
294 				for (x = 0; x < width; x++, tx += dx)
295 					if (base[o++] == 0)
296 						dest[tx] = pal;
297 				break;
298 
299 			case 0x02:  /* draw only non-0 pixels */
300 				for (x = 0; x < width; x++, tx += dx)
301 				{
302 					int pixel = base[o++];
303 					if (pixel != 0)
304 						dest[tx] = pal | pixel;
305 				}
306 				break;
307 
308 			case 0x03:  /* draw all pixels */
309 				for (x = 0; x < width; x++, tx += dx)
310 					dest[tx] = pal | base[o++];
311 				break;
312 
313 			case 0x04:  /* color only 0 pixels */
314 			case 0x05:  /* color only 0 pixels */
315 				for (x = 0; x < width; x++, tx += dx)
316 					if (base[o++] == 0)
317 						dest[tx] = color;
318 				break;
319 
320 			case 0x06:  /* color only 0 pixels, copy the rest */
321 			case 0x07:  /* color only 0 pixels, copy the rest */
322 				for (x = 0; x < width; x++, tx += dx)
323 				{
324 					int pixel = base[o++];
325 					dest[tx] = (pixel == 0) ? color : (pal | pixel);
326 				}
327 				break;
328 
329 			case 0x08:  /* color only non-0 pixels */
330 			case 0x0a:  /* color only non-0 pixels */
331 				for (x = 0; x < width; x++, tx += dx)
332 					if (base[o++] != 0)
333 						dest[tx] = color;
334 				break;
335 
336 			case 0x09:  /* color only non-0 pixels, copy the rest */
337 			case 0x0b:  /* color only non-0 pixels, copy the rest */
338 				for (x = 0; x < width; x++, tx += dx)
339 				{
340 					int pixel = base[o++];
341 					dest[tx] = (pixel != 0) ? color : (pal | pixel);
342 				}
343 				break;
344 
345 			case 0x0c:  /* color all pixels */
346 			case 0x0d:  /* color all pixels */
347 			case 0x0e:  /* color all pixels */
348 			case 0x0f:  /* color all pixels */
349 				for (x = 0; x < width; x++, tx += dx)
350 					dest[tx] = color;
351 				break;
352 		}
353 	}
354 }
355 
356 
357 
358 /*************************************
359  *
360  *  Timer callbacks
361  *
362  *************************************/
363 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)364 void midyunit_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
365 {
366 	switch (id)
367 	{
368 	case TIMER_DMA:
369 		dma_callback(ptr, param);
370 		break;
371 	case TIMER_AUTOERASE_LINE:
372 		autoerase_line(ptr, param);
373 		break;
374 	default:
375 		throw emu_fatalerror("Unknown id in midyunit_state::device_timer");
376 	}
377 }
378 
TIMER_CALLBACK_MEMBER(midyunit_state::dma_callback)379 TIMER_CALLBACK_MEMBER(midyunit_state::dma_callback)
380 {
381 	m_dma_register[DMA_COMMAND] &= ~0x8000; /* tell the cpu we're done */
382 	m_maincpu->set_input_line(0, ASSERT_LINE);
383 }
384 
385 
386 
387 /*************************************
388  *
389  *  DMA reader
390  *
391  *************************************/
392 
midyunit_dma_r(offs_t offset)393 uint16_t midyunit_state::midyunit_dma_r(offs_t offset)
394 {
395 	return m_dma_register[offset];
396 }
397 
398 
399 
400 /*************************************
401  *
402  *  DMA write handler
403  *
404  *************************************/
405 
406 /*
407  * DMA registers
408  * ------------------
409  *
410  *  Register | Bit              | Use
411  * ----------+-FEDCBA9876543210-+------------
412  *     0     | x--------------- | trigger write (or clear if zero)
413  *           | ---184-1-------- | unknown
414  *           | ----------x----- | flip y
415  *           | -----------x---- | flip x
416  *           | ------------x--- | blit nonzero pixels as color
417  *           | -------------x-- | blit zero pixels as color
418  *           | --------------x- | blit nonzero pixels
419  *           | ---------------x | blit zero pixels
420  *     1     | xxxxxxxxxxxxxxxx | width offset
421  *     2     | xxxxxxxxxxxxxxxx | source address low word
422  *     3     | xxxxxxxxxxxxxxxx | source address high word
423  *     4     | xxxxxxxxxxxxxxxx | detination x
424  *     5     | xxxxxxxxxxxxxxxx | destination y
425  *     6     | xxxxxxxxxxxxxxxx | image columns
426  *     7     | xxxxxxxxxxxxxxxx | image rows
427  *     8     | xxxxxxxxxxxxxxxx | palette
428  *     9     | xxxxxxxxxxxxxxxx | color
429  */
430 
midyunit_dma_w(offs_t offset,uint16_t data,uint16_t mem_mask)431 void midyunit_state::midyunit_dma_w(offs_t offset, uint16_t data, uint16_t mem_mask)
432 {
433 	struct dma_state_t &dma_state = m_dma_state;
434 	uint32_t gfxoffset;
435 	int command;
436 
437 	/* blend with the current register contents */
438 	COMBINE_DATA(&m_dma_register[offset]);
439 
440 	/* only writes to DMA_COMMAND actually cause actions */
441 	if (offset != DMA_COMMAND)
442 		return;
443 
444 	/* high bit triggers action */
445 	command = m_dma_register[DMA_COMMAND];
446 	m_maincpu->set_input_line(0, CLEAR_LINE);
447 	if (!(command & 0x8000))
448 		return;
449 
450 if (LOG_DMA)
451 {
452 	if (machine().input().code_pressed(KEYCODE_L))
453 	{
454 		logerror("----\n");
455 		logerror("DMA command %04X: (xflip=%d yflip=%d)\n",
456 				command, (command >> 4) & 1, (command >> 5) & 1);
457 		logerror("  offset=%08X pos=(%d,%d) w=%d h=%d rb=%d\n",
458 				m_dma_register[DMA_OFFSETLO] | (m_dma_register[DMA_OFFSETHI] << 16),
459 				(int16_t)m_dma_register[DMA_XSTART], (int16_t)m_dma_register[DMA_YSTART],
460 				m_dma_register[DMA_WIDTH], m_dma_register[DMA_HEIGHT], (int16_t)m_dma_register[DMA_ROWBYTES]);
461 		logerror("  palette=%04X color=%04X\n",
462 				m_dma_register[DMA_PALETTE], m_dma_register[DMA_COLOR]);
463 	}
464 }
465 
466 	g_profiler.start(PROFILER_USER1);
467 
468 	/* fill in the basic data */
469 	dma_state.rowbytes = (int16_t)m_dma_register[DMA_ROWBYTES];
470 	dma_state.xpos = (int16_t)m_dma_register[DMA_XSTART];
471 	dma_state.ypos = (int16_t)m_dma_register[DMA_YSTART];
472 	dma_state.width = m_dma_register[DMA_WIDTH];
473 	dma_state.height = m_dma_register[DMA_HEIGHT];
474 	dma_state.palette = m_dma_register[DMA_PALETTE] << 8;
475 	dma_state.color = m_dma_register[DMA_COLOR] & 0xff;
476 
477 	/* determine the offset and adjust the rowbytes */
478 	gfxoffset = m_dma_register[DMA_OFFSETLO] | (m_dma_register[DMA_OFFSETHI] << 16);
479 	if (command & 0x10)
480 	{
481 		if (!m_yawdim_dma)
482 		{
483 			gfxoffset -= (dma_state.width - 1) * 8;
484 			dma_state.rowbytes = (dma_state.rowbytes - dma_state.width + 3) & ~3;
485 		}
486 		else
487 			dma_state.rowbytes = (dma_state.rowbytes + dma_state.width + 3) & ~3;
488 		dma_state.xpos += dma_state.width - 1;
489 	}
490 	else
491 		dma_state.rowbytes = (dma_state.rowbytes + dma_state.width + 3) & ~3;
492 
493 	/* apply Y clipping */
494 	if (dma_state.ypos < 0)
495 	{
496 		dma_state.height -= -dma_state.ypos;
497 		dma_state.offset += (-dma_state.ypos * dma_state.rowbytes) << 3;
498 		dma_state.ypos = 0;
499 	}
500 	if (dma_state.ypos + dma_state.height > 512)
501 		dma_state.height = 512 - dma_state.ypos;
502 
503 	/* apply X clipping */
504 	if (!(command & 0x10))
505 	{
506 		if (dma_state.xpos < 0)
507 		{
508 			dma_state.width -= -dma_state.xpos;
509 			dma_state.offset += -dma_state.xpos << 3;
510 			dma_state.xpos = 0;
511 		}
512 		if (dma_state.xpos + dma_state.width > 512)
513 			dma_state.width = 512 - dma_state.xpos;
514 	}
515 	else
516 	{
517 		if (dma_state.xpos >= 512)
518 		{
519 			dma_state.width -= dma_state.xpos - 511;
520 			dma_state.offset += (dma_state.xpos - 511) << 3;
521 			dma_state.xpos = 511;
522 		}
523 		if (dma_state.xpos - dma_state.width < 0)
524 			dma_state.width = dma_state.xpos;
525 	}
526 
527 	/* determine the location and draw */
528 	if (gfxoffset < 0x02000000)
529 		gfxoffset += 0x02000000;
530 	{
531 		dma_state.offset = gfxoffset - 0x02000000;
532 		dma_draw(command);
533 	}
534 
535 	/* signal we're done */
536 	m_dma_timer->adjust(attotime::from_nsec(41 * dma_state.width * dma_state.height));
537 
538 	g_profiler.stop();
539 }
540 
541 
542 
543 /*************************************
544  *
545  *  Core refresh routine
546  *
547  *************************************/
548 
TIMER_CALLBACK_MEMBER(midyunit_state::autoerase_line)549 TIMER_CALLBACK_MEMBER(midyunit_state::autoerase_line)
550 {
551 	int scanline = param;
552 
553 	if (m_autoerase_enable && scanline >= 0 && scanline < 510)
554 		memcpy(&m_local_videoram[512 * scanline], &m_local_videoram[512 * (510 + (scanline & 1))], 512 * sizeof(uint16_t));
555 }
556 
557 
TMS340X0_SCANLINE_IND16_CB_MEMBER(midyunit_state::scanline_update)558 TMS340X0_SCANLINE_IND16_CB_MEMBER(midyunit_state::scanline_update)
559 {
560 	uint16_t const *const src = &m_local_videoram[(params->rowaddr << 9) & 0x3fe00];
561 	uint16_t *const dest = &bitmap.pix(scanline);
562 	int coladdr = params->coladdr << 1;
563 
564 	/* adjust the display address to account for ignored bits */
565 	for (int x = params->heblnk; x < params->hsblnk; x++)
566 		dest[x] = m_pen_map[src[coladdr++ & 0x1ff]];
567 
568 	/* handle autoerase on the previous line */
569 	autoerase_line(nullptr, params->rowaddr - 1);
570 
571 	/* if this is the last update of the screen, set a timer to clear out the final line */
572 	/* (since we update one behind) */
573 	if (scanline == screen.visible_area().max_y)
574 		m_autoerase_line_timer->adjust(screen.time_until_pos(scanline + 1), params->rowaddr);
575 }
576