1 /*************************************************************************
2 
3 	avgdvg.c: Atari DVG and AVG simulators
4 
5 	Copyright 1991, 1992, 1996 Eric Smith
6 
7 	Modified for the MAME project 1997 by
8 	Brad Oliver, Bernd Wiebelt, Aaron Giles, Andrew Caldwell
9 
10 	971108 Disabled vector timing routines, introduced an ugly (but fast!)
11 			busy flag hack instead. BW
12 	980202 New anti aliasing code by Andrew Caldwell (.ac)
13 	980206 New (cleaner) busy flag handling.
14 			Moved LBO's buffered point into generic vector code. BW
15 	980212 Introduced timing code based on Aaron timer routines. BW
16 	980318 Better color handling, Bzone and MHavoc clipping. BW
17 
18 	Battlezone uses a red overlay for the top of the screen and a green one
19 	for the rest. There is a circuit to clip color 0 lines extending to the
20 	red zone. This is emulated now. Thanks to Neil Bradley for the info. BW
21 
22 	Frame and interrupt rates (Neil Bradley) BW
23 		~60 fps/4.0ms: Asteroid, Asteroid Deluxe
24 		~40 fps/4.0ms: Lunar Lander
25 		~40 fps/4.1ms: Battle Zone
26 		~45 fps/5.4ms: Space Duel, Red Baron
27 		~30 fps/5.4ms: StarWars
28 
29 	Games with self adjusting framerate
30 		4.1ms: Black Widow, Gravitar
31 		4.1ms: Tempest
32 		Major Havoc
33 		Quantum
34 
35 	TODO: accurate vector timing (need timing diagramm)
36 
37 ************************************************************************/
38 
39 #include "driver.h"
40 #include "avgdvg.h"
41 #include "vector.h"
42 
43 
44 //#define VG_DEBUG
45 #ifdef VG_DEBUG
46 #define VGLOG(x) logerror x
47 #else
48 #define VGLOG(x)
49 #endif
50 
51 
52 /*************************************
53  *
54  *	Constants
55  *
56  *************************************/
57 
58 #define BRIGHTNESS	12
59 
60 #define MAXSTACK	8		/* Tempest needs more than 4 */
61 
62 /* AVG commands */
63 #define VCTR		0
64 #define HALT		1
65 #define SVEC		2
66 #define STAT		3
67 #define CNTR		4
68 #define JSRL		5
69 #define RTSL		6
70 #define JMPL		7
71 #define SCAL		8
72 
73 /* DVG commands */
74 #define DVCTR		0x01
75 #define DLABS		0x0a
76 #define DHALT		0x0b
77 #define DJSRL		0x0c
78 #define DRTSL		0x0d
79 #define DJMPL		0x0e
80 #define DSVEC		0x0f
81 
82 
83 
84 /*************************************
85  *
86  *	Static variables
87  *
88  *************************************/
89 
90 static UINT8 vector_engine;
91 static UINT8 flipword;
92 static UINT8 busy;
93 static rgb_t colorram[32];
94 
95 static int width, height;
96 static int xcenter, ycenter;
97 static int xmin, xmax;
98 static int ymin, ymax;
99 
100 static int flip_x, flip_y, swap_xy;
101 
102 int vector_updates; /* avgdvg_go_w()'s per Mame frame, should be 1 */
103 
104 
105 #define BANK_SIZE (0x2000)
106 #define NUM_BANKS (2)
107 static unsigned char *vectorbank[NUM_BANKS];
108 
109 static rgb_t sparkle_callback(void);
110 
111 
112 
113 /*************************************
114  *
115  *	Compute 2's complement value
116  *
117  *************************************/
118 
twos_comp_val(int num,int bits)119 static INLINE int twos_comp_val(int num, int bits)
120 {
121 	return (INT32)(num << (32 - bits)) >> (32 - bits);
122 }
123 
124 
125 
126 /*************************************
127  *
128  *	Vector RAM accesses
129  *
130  *************************************/
131 
vector_word(UINT16 offset)132 static INLINE UINT16 vector_word(UINT16 offset)
133 {
134 	UINT8 *base;
135 
136 	/* convert from word offset to byte */
137 	offset *= 2;
138 
139 	/* get address of the word */
140 	base = &vectorbank[offset / BANK_SIZE][offset % BANK_SIZE];
141 
142 	/* result is based on flipword */
143 	if (flipword)
144 		return base[1] | (base[0] << 8);
145 	else
146 		return base[0] | (base[1] << 8);
147 }
148 
149 
150 
151 /*************************************
152  *
153  *	Vector timing
154  *
155  *************************************/
156 
vector_timer(int deltax,int deltay)157 static INLINE int vector_timer(int deltax, int deltay)
158 {
159 	deltax = abs(deltax);
160 	deltay = abs(deltay);
161 	if (deltax > deltay)
162 		return deltax >> 16;
163 	else
164 		return deltay >> 16;
165 }
166 
167 
dvg_vector_timer(int scale)168 static INLINE int dvg_vector_timer(int scale)
169 {
170 	return scale;
171 }
172 
173 
174 
175 /*************************************
176  *
177  *	AVG brightness computation
178  *
179  *************************************/
180 
effective_z(int z,int statz)181 static INLINE int effective_z(int z, int statz)
182 {
183 	/* Star Wars blends Z and an 8-bit STATZ */
184 	/* STATZ of 128 should give highest intensity */
185 	if (vector_engine == USE_AVG_SWARS)
186 	{
187 		z = (z * statz) / (options.translucency ? 12 : 8);
188 		if (z > 0xff)
189 			z = 0xff;
190 	}
191 
192 	/* everyone else uses this */
193 	else
194 	{
195 		/* special case for Alpha One -- no idea if this is right */
196 		if (vector_engine == USE_AVG_ALPHAONE)
197 		{
198 			if (z)
199 				z ^= 0x15;
200 		}
201 
202 		/* Z == 2 means use the value from STATZ */
203 		else if (z == 2)
204 			z = statz;
205 
206 		z *= (options.translucency) ? BRIGHTNESS : 16;
207 	}
208 
209 	return z;
210 }
211 
212 
213 
214 /*************************************
215  *
216  *	DVG vector generator
217  *
218  *************************************/
219 
dvg_generate_vector_list(void)220 static int dvg_generate_vector_list(void)
221 {
222 	static const char *dvg_mnem[] =
223 	{
224 		"????", "vct1", "vct2", "vct3",
225 		"vct4", "vct5", "vct6", "vct7",
226 		"vct8", "vct9", "labs", "halt",
227 		"jsrl", "rtsl", "jmpl", "svec"
228 	};
229 
230 	int stack[MAXSTACK];
231 	int pc = 0;
232 	int sp = 0;
233 	int scale = 0;
234 	int currentx = 0, currenty = 0;
235 	int total_length = 1;
236 	int done = 0;
237 
238 	int firstwd, secondwd = 0;
239 	int opcode;
240 	int x, y, z, temp, a;
241 	int deltax, deltay;
242 
243 	/* reset the vector list */
244 	vector_clear_list();
245 
246 	/* loop until finished */
247 	while (!done)
248 	{
249 		/* fetch the first word and get its opcode */
250 		firstwd = vector_word(pc++);
251 		opcode = firstwd >> 12;
252 
253 		/* the DVCTR and DLABS opcodes take two words */
254 		if (opcode >= 0 && opcode <= DLABS)
255 			secondwd = vector_word(pc++);
256 
257 		/* debugging */
258 		VGLOG(("%4x: %4x ", pc, firstwd));
259 		if (opcode <= DLABS)
260 		{
261 			(void)dvg_mnem;
262 			VGLOG(("%s ", dvg_mnem[opcode]));
263 			VGLOG(("%4x  ", secondwd));
264 		}
265 
266 		/* switch off the opcode */
267 		switch (opcode)
268 		{
269 			/* 0 is an invalid opcode */
270 			case 0:
271 	 			VGLOG(("Error: DVG opcode 0!  Addr %4x Instr %4x %4x\n", pc-2, firstwd, secondwd));
272 #ifdef VG_DEBUG
273 				done = 1;
274 				break;
275 #endif
276 
277 			/* 1-9 are DVCTR ops: draw a vector */
278 			case 1:
279 			case 2:
280 			case 3:
281 			case 4:
282 			case 5:
283 			case 6:
284 			case 7:
285 			case 8:
286 			case 9:
287 
288 				/* compute raw X and Y values */
289 	  			y = firstwd & 0x03ff;
290 				x = secondwd & 0x3ff;
291 				if (firstwd & 0x400)
292 					y = -y;
293 				if (secondwd & 0x400)
294 					x = -x;
295 
296 				/* determine the brightness */
297 				z = secondwd >> 12;
298 				VGLOG(("(%d,%d) z: %d scal: %d", x, y, z, opcode));
299 
300 				/* compute the effective brightness */
301 				z = effective_z(z, z);
302 
303 				/* determine the scale factor; scale 9 means -1 */
304 	  			temp = ((scale + opcode) & 0x0f);
305 	  			if (temp > 9)
306 					temp = -1;
307 
308 				/* compute the deltas */
309 	  			deltax = (x << 16) >> (9-temp);
310 				deltay = (y << 16) >> (9-temp);
311 
312 				/* adjust the current position and compute timing */
313 	  			currentx += deltax;
314 				currenty -= deltay;
315 				total_length += dvg_vector_timer(temp);
316 
317 				/* add the new point */
318 				vector_add_point(currentx, currenty, colorram[1], z);
319 				break;
320 
321 			/* DSVEC: draw a short vector */
322 			case DSVEC:
323 
324 				/* compute raw X and Y values */
325 				y = firstwd & 0x0300;
326 				x = (firstwd & 0x03) << 8;
327 				if (firstwd & 0x0400)
328 					y = -y;
329 				if (firstwd & 0x04)
330 					x = -x;
331 
332 				/* determine the brightness */
333 				z = (firstwd >> 4) & 0x0f;
334 
335 				/* compute the effective brightness */
336 				z = effective_z(z, z);
337 
338 				/* determine the scale factor; scale 9 means -1 */
339 				temp = 2 + ((firstwd >> 2) & 0x02) + ((firstwd >> 11) & 0x01);
340 	  			temp = (scale + temp) & 0x0f;
341 				if (temp > 9)
342 					temp = -1;
343 				VGLOG(("(%d,%d) z: %d scal: %d", x, y, z, temp));
344 
345 				/* compute the deltas */
346 				deltax = (x << 16) >> (9 - temp);
347 				deltay = (y << 16) >> (9 - temp);
348 
349 				/* adjust the current position and compute timing */
350 	  			currentx += deltax;
351 				currenty -= deltay;
352 				total_length += dvg_vector_timer(temp);
353 
354 				/* add the new point */
355 				vector_add_point(currentx, currenty, colorram[1], z);
356 				break;
357 
358 			/* DLABS: move to an absolute location */
359 			case DLABS:
360 
361 				/* extract the new X,Y coordinates */
362 				y = twos_comp_val(firstwd, 12);
363 				x = twos_comp_val(secondwd, 12);
364 
365 				/* global scale comes from upper 4 bits of second word */
366 	  			scale = secondwd >> 12;
367 
368 	  			/* set the current X,Y */
369 				currentx = (x - xmin) << 16;
370 				currenty = (ymax - y) << 16;
371 				VGLOG(("(%d,%d) scal: %d", x, y, secondwd >> 12));
372 				break;
373 
374 			/* DRTSL: return from subroutine */
375 			case DRTSL:
376 
377 				/* handle stack underflow */
378 				if (sp == 0)
379 	    		{
380 					logerror("\n*** Vector generator stack underflow! ***\n");
381 					done = 1;
382 					sp = MAXSTACK - 1;
383 				}
384 				else
385 					sp--;
386 
387 				/* pull the new PC from the stack */
388 				pc = stack[sp];
389 
390 				/* debugging */
391 				if (firstwd & 0x1fff)
392 					VGLOG(("(%d?)", firstwd & 0x1fff));
393 				break;
394 
395 			/* DHALT: all done! */
396 			case DHALT:
397 				done = 1;
398 
399 				/* debugging */
400 				if (firstwd & 0x1fff)
401       				VGLOG(("(%d?)", firstwd & 0x0fff));
402 				break;
403 
404 			/* DJMPL: jump to a new program location */
405 			case DJMPL:
406 				a = firstwd & 0x0fff;
407 				VGLOG(("%4x", a));
408 				pc = a;
409 
410 				if (!pc)
411 					done=1;
412 				break;
413 
414 			/* DJSRL: jump to a new program location as subroutine */
415 			case DJSRL:
416 				a = firstwd & 0x0fff;
417 				VGLOG(("%4x", a));
418 
419 				/* push the next PC on the stack */
420 				stack[sp] = pc;
421 
422 				/* check for stack overflows */
423 				if (sp == (MAXSTACK - 1))
424 	    		{
425 					logerror("\n*** Vector generator stack overflow! ***\n");
426 					done = 1;
427 					sp = 0;
428 				}
429 				else
430 					sp++;
431 
432 				/* jump to the new location */
433 				pc = a;
434 				break;
435 
436 			/* anything else gets caught here */
437 			default:
438 				logerror("Unknown DVG opcode found\n");
439 				done = 1;
440 		}
441    		VGLOG(("\n"));
442 	}
443 
444 	/* return the total length of everything drawn */
445 	return total_length;
446 }
447 
avg_set_flip_x(int flip)448 void avg_set_flip_x(int flip)
449 {
450 	if (flip)
451 		flip_x = 1;
452 }
453 
avg_set_flip_y(int flip)454 void avg_set_flip_y(int flip)
455 {
456 	if (flip)
457 		flip_y = 1;
458 }
459 
avg_apply_flipping_and_swapping(int * x,int * y)460 void avg_apply_flipping_and_swapping(int *x, int *y)
461 {
462 	if (flip_x)
463 		*x += (xcenter-*x)<<1;
464 	if (flip_y)
465 		*y += (ycenter-*y)<<1;
466 
467 	if (swap_xy)
468 	{
469 		int temp = *x;
470 		*x = *y - ycenter + xcenter;
471 		*y = temp - xcenter + ycenter;
472 	}
473 }
474 
avg_add_point(int x,int y,rgb_t color,int intensity)475 void avg_add_point(int x, int y, rgb_t color, int intensity)
476 {
477 	avg_apply_flipping_and_swapping(&x, &y);
478 	vector_add_point(x, y, color, intensity);
479 }
480 
avg_add_point_callback(int x,int y,rgb_t (* color_callback)(void),int intensity)481 void avg_add_point_callback(int x, int y, rgb_t (*color_callback)(void), int intensity)
482 {
483 	avg_apply_flipping_and_swapping(&x, &y);
484 	vector_add_point_callback(x, y, color_callback, intensity);
485 }
486 
487 /*************************************
488  *
489  *	AVG vector generator
490  *
491  *************************************
492 
493 	Atari Analog Vector Generator Instruction Set
494 
495 	Compiled from Atari schematics and specifications
496 	Eric Smith  7/2/92
497 	---------------------------------------------
498 
499 	NOTE: The vector generator is little-endian.  The instructions are 16 bit
500 	      words, which need to be stored with the least significant byte in the
501 	      lower (even) address.  They are shown here with the MSB on the left.
502 
503 	The stack allows four levels of subroutine calls in the TTL version, but only
504 	three levels in the gate array version.
505 
506 	inst  bit pattern          description
507 	----  -------------------  -------------------
508 	VCTR  000- yyyy yyyy yyyy  normal vector
509 	      zzz- xxxx xxxx xxxx
510 	HALT  001- ---- ---- ----  halt - does CNTR also on newer hardware
511 	SVEC  010y yyyy zzzx xxxx  short vector - don't use zero length
512 	STAT  0110 ---- zzzz cccc  status
513 	SCAL  0111 -bbb llll llll  scaling
514 	CNTR  100- ---- dddd dddd  center
515 	JSRL  101a aaaa aaaa aaaa  jump to subroutine
516 	RTSL  110- ---- ---- ----  return
517 	JMPL  111a aaaa aaaa aaaa  jump
518 
519 	-     unused bits
520 	x, y  relative x and y coordinates in two's complement (5 or 13 bit,
521 	      5 bit quantities are scaled by 2, so x=1 is really a length 2 vector.
522 	z     intensity, 0 = blank, 1 means use z from STAT instruction,  2-7 are
523 	      doubled for actual range of 4-14
524 	c     color
525 	b     binary scaling, multiplies all lengths by 2**(1-b), 0 is double size,
526 	      1 is normal, 2 is half, 3 is 1/4, etc.
527 	l     linear scaling, multiplies all lengths by 1-l/256, don't exceed $80
528 	d     delay time, use $40
529 	a     address (word address relative to base of vector memory)
530 
531 	Notes:
532 
533 	Quantum:
534 	        the VCTR instruction has a four bit Z field, that is not
535 	        doubled.  The value 2 means use Z from STAT instruction.
536 
537 	        the SVEC instruction can't be used
538 
539 	Major Havoc:
540 	        SCAL bit 11 is used for setting a Y axis window.
541 
542 	        STAT bit 11 is used to enable "sparkle" color.
543 	        STAT bit 10 inverts the X axis of vectors.
544 	        STAT bits 9 and 8 are the Vector ROM bank select.
545 
546 	Star Wars:
547 	        STAT bits 10, 9, and 8 are used directly for R, G, and B.
548 
549  *************************************/
550 
avg_generate_vector_list(void)551 static int avg_generate_vector_list(void)
552 {
553 	static const char *avg_mnem[] =
554 	{
555 		"vctr", "halt", "svec", "stat", "cntr",
556 		"jsrl", "rtsl", "jmpl", "scal"
557 	};
558 
559 	int stack[MAXSTACK];
560 	int pc = 0;
561 	int sp = 0;
562 	int scale = 0;
563 	int statz = 0;
564 	int sparkle = 0;
565 	int xflip = 0;
566 	int color = 0;
567 	int ywindow = 1;
568 	int currentx = xcenter;
569 	int currenty = ycenter;
570 	int total_length = 1;
571 	int done = 0;
572 
573 	int firstwd, secondwd = 0;
574 	int opcode;
575 	int x, y, z = 0, b, l, d, a;
576 	int deltax, deltay;
577 
578 	/* check for zeroed vector RAM */
579 	if (vector_word(pc) == 0 && vector_word(pc + 1) == 0)
580 	{
581 		logerror("VGO with zeroed vector memory\n");
582 		return total_length;
583 	}
584 
585 	/* kludge to bypass Major Havoc's empty frames */
586 	if ((vector_engine == USE_AVG_MHAVOC || vector_engine == USE_AVG_ALPHAONE) && vector_word(pc) == 0xafe2)
587 		return total_length;
588 
589 	/* reset the vector list */
590 	vector_clear_list();
591 
592 	/* loop until finished... */
593 	while (!done)
594 	{
595 		/* fetch the first word and get its opcode */
596 		firstwd = vector_word(pc++);
597 		opcode = firstwd >> 13;
598 
599 		/* the VCTR opcode takes two words */
600 		if (opcode == VCTR)
601 			secondwd = vector_word(pc++);
602 
603 		/* SCAL is a variant of STAT; convert it here */
604 		else if (opcode == STAT && (firstwd & 0x1000))
605 			opcode = SCAL;
606 
607 		/* debugging */
608 		(void)avg_mnem;
609 		VGLOG(("%4x: %4x ", pc, firstwd));
610 		if (opcode == VCTR)
611 			VGLOG(("%4x  ", secondwd));
612 		else
613 			VGLOG(("      "));
614 		VGLOG(("%s ", avg_mnem[opcode]));
615 
616 		/* switch off the opcode */
617 		switch (opcode)
618 		{
619 			/* VCTR: draw a long vector */
620 			case VCTR:
621 
622 				/* Quantum uses 12-bit vectors and a 4-bit Z value */
623 				if (vector_engine == USE_AVG_QUANTUM)
624 				{
625 					x = twos_comp_val(secondwd, 12);
626 					y = twos_comp_val(firstwd, 12);
627 					z = (secondwd >> 12) & 0x0f;
628 				}
629 
630 				/* everyone else uses 13-bit vectors and a 3-bit Z value */
631 				else
632 				{
633 					x = twos_comp_val(secondwd, 13);
634 					y = twos_comp_val(firstwd, 13);
635 					z = (secondwd >> 12) & 0x0e;
636 				}
637 
638 				/* compute the effective brightness */
639 				z = effective_z(z, statz);
640 
641 				/* compute the deltas */
642 				deltax = x * scale;
643 				deltay = y * scale;
644 				if (xflip) deltax = -deltax;
645 
646 				/* adjust the current position and compute timing */
647 				currentx += deltax;
648 				currenty -= deltay;
649 				total_length += vector_timer(deltax, deltay);
650 
651 				/* add the new point */
652 				if (sparkle)
653 					avg_add_point_callback(currentx, currenty, sparkle_callback, z);
654 				else
655 					avg_add_point(currentx, currenty, colorram[color], z);
656 				VGLOG(("VCTR x:%d y:%d z:%d statz:%d", x, y, z, statz));
657 				break;
658 
659 			/* SVEC: draw a short vector */
660 			case SVEC:
661 
662 				/* Quantum doesn't support this */
663 				if (vector_engine == USE_AVG_QUANTUM)
664 					break;
665 
666 				/* two 5-bit vectors plus a 3-bit Z value */
667 				x = twos_comp_val(firstwd, 5) * 2;
668 				y = twos_comp_val(firstwd >> 8, 5) * 2;
669 				z = (firstwd >> 4) & 0x0e;
670 
671 				/* compute the effective brightness */
672 				z = effective_z(z, statz);
673 
674 				/* compute the deltas */
675 				deltax = x * scale;
676 				deltay = y * scale;
677 				if (xflip) deltax = -deltax;
678 
679 				/* adjust the current position and compute timing */
680 				currentx += deltax;
681 				currenty -= deltay;
682 				total_length += vector_timer(deltax, deltay);
683 
684 				/* add the new point */
685 				if (sparkle)
686 					avg_add_point_callback(currentx, currenty, sparkle_callback, z);
687 				else
688 					avg_add_point(currentx, currenty, colorram[color], z);
689 				VGLOG(("SVEC x:%d y:%d z:%d statz:%d", x, y, z, statz));
690 				break;
691 
692 			/* STAT: control colors, clipping, sparkling, and flipping */
693 			case STAT:
694 
695 				/* Star Wars takes RGB directly and has an 8-bit brightness */
696 				if (vector_engine == USE_AVG_SWARS)
697 				{
698 					color = (firstwd >> 8) & 7;
699 					statz = firstwd & 0xff;
700 				}
701 
702 				/* everyone else has a 4-bit color and 4-bit brightness */
703 				else
704 				{
705 					color = firstwd & 0x0f;
706 					statz = (firstwd >> 4) & 0x0f;
707 				}
708 
709 				/* Tempest has the sparkle bit in bit 11 */
710 				if (vector_engine == USE_AVG_TEMPEST)
711 					sparkle = !(firstwd & 0x0800);
712 
713 				/* Major Havoc/Alpha One have sparkle bit, xflip, and banking */
714 				else if (vector_engine == USE_AVG_MHAVOC || vector_engine == USE_AVG_ALPHAONE)
715 				{
716 					sparkle = firstwd & 0x0800;
717 					xflip = firstwd & 0x0400;
718 					vectorbank[1] = &memory_region(REGION_CPU1)[0x18000 + ((firstwd >> 8) & 3) * 0x2000];
719 				}
720 
721 				/* BattleZone has a clipping circuit */
722 				else if (vector_engine == USE_AVG_BZONE)
723 				{
724 					int newymin = (color == 0) ? 0x0050 : ymin;
725 					vector_add_clip(xmin << 16, newymin << 16,
726 									xmax << 16, ymax << 16);
727 				}
728 
729 				/* debugging */
730 				VGLOG(("STAT: statz: %d color: %d", statz, color));
731 				if (xflip || sparkle)
732 					VGLOG(("xflip: %02x  sparkle: %02x\n", xflip, sparkle));
733 				break;
734 
735 			/* SCAL: set the scale factor */
736 			case SCAL:
737 				b = ((firstwd >> 8) & 7) + 8;
738 				l = ~firstwd & 0xff;
739 				scale = (l << 16) >> b;
740 
741 				/* Y-Window toggle for Major Havoc */
742 				if (vector_engine == USE_AVG_MHAVOC || vector_engine == USE_AVG_ALPHAONE)
743 					if (firstwd & 0x0800)
744 					{
745 						int newymin = ymin;
746 						logerror("CLIP %d\n", firstwd & 0x0800);
747 
748 						/* toggle the current state */
749 						ywindow = !ywindow;
750 
751 						/* adjust accordingly */
752 						if (ywindow)
753 							newymin = (vector_engine == USE_AVG_MHAVOC) ? 0x0048 : 0x0083;
754 						vector_add_clip(xmin << 16, newymin << 16,
755 										xmax << 16, ymax << 16);
756 					}
757 
758 				/* debugging */
759 				VGLOG(("bin: %d, lin: ", b));
760 				if (l > 0x80)
761 					VGLOG(("(%d?)", l));
762 				else
763 					VGLOG(("%d", l));
764 				VGLOG((" scale: %f", (scale/(float)(1<<16))));
765 				break;
766 
767 			/* CNTR: center the beam */
768 			case CNTR:
769 
770 				/* delay stored in low 8 bits; normally is 0x40 */
771 				d = firstwd & 0xff;
772 				if (d != 0x40) VGLOG(("%d", d));
773 
774 				/* move back to the middle */
775 				currentx = xcenter;
776 				currenty = ycenter;
777 				avg_add_point(currentx, currenty, 0, 0);
778 				break;
779 
780 			/* RTSL: return from subroutine */
781 			case RTSL:
782 
783 				/* handle stack underflow */
784 				if (sp == 0)
785 				{
786 					logerror("\n*** Vector generator stack underflow! ***\n");
787 					done = 1;
788 					sp = MAXSTACK - 1;
789 				}
790 				else
791 					sp--;
792 
793 				/* pull the new PC from the stack */
794 				pc = stack[sp];
795 
796 				/* debugging */
797 				if (firstwd & 0x1fff)
798 					VGLOG(("(%d?)", firstwd & 0x1fff));
799 				break;
800 
801 			/* HALT: all done! */
802 			case HALT:
803 				done = 1;
804 
805 				/* debugging */
806 				if (firstwd & 0x1fff)
807 					VGLOG(("(%d?)", firstwd & 0x1fff));
808 				break;
809 
810 			/* JMPL: jump to a new program location */
811 			case JMPL:
812 				a = firstwd & 0x1fff;
813 				VGLOG(("%4x", a));
814 
815 				/* if a = 0x0000, treat as HALT */
816 				if (a == 0x0000)
817 					done = 1;
818 				else
819 					pc = a;
820 				break;
821 
822 			/* JSRL: jump to a new program location as subroutine */
823 			case JSRL:
824 				a = firstwd & 0x1fff;
825 				VGLOG(("%4x", a));
826 
827 				/* if a = 0x0000, treat as HALT */
828 				if (a == 0x0000)
829 					done = 1;
830 				else
831 				{
832 					/* push the next PC on the stack */
833 					stack[sp] = pc;
834 
835 					/* check for stack overflows */
836 					if (sp == (MAXSTACK - 1))
837 					{
838 						logerror("\n*** Vector generator stack overflow! ***\n");
839 						done = 1;
840 						sp = 0;
841 					}
842 					else
843 						sp++;
844 
845 					/* jump to the new location */
846 					pc = a;
847 				}
848 				break;
849 
850 			/* anything else gets caught here */
851 			default:
852 				logerror("internal error\n");
853 		}
854 		VGLOG(("\n"));
855 	}
856 
857 	/* return the total length of everything drawn */
858 	return total_length;
859 }
860 
861 
862 
863 /*************************************
864  *
865  *	AVG execution/busy detection
866  *
867  ************************************/
868 
avgdvg_done(void)869 int avgdvg_done(void)
870 {
871 	return !busy;
872 }
873 
874 
avgdvg_clr_busy(int dummy)875 static void avgdvg_clr_busy(int dummy)
876 {
877 	busy = 0;
878 }
879 
880 
WRITE_HANDLER(avgdvg_go_w)881 WRITE_HANDLER( avgdvg_go_w )
882 {
883 	int total_length;
884 
885 	/* skip if already busy */
886 	if (busy)
887 		return;
888 
889 	/* count vector updates and mark ourselves busy */
890 	vector_updates++;
891 	busy = 1;
892 
893 	/* DVG case */
894 	if (vector_engine == USE_DVG)
895 	{
896 		total_length = dvg_generate_vector_list();
897 		timer_set(TIME_IN_NSEC(4500) * total_length, 0, avgdvg_clr_busy);
898 	}
899 
900 	/* AVG case */
901 	else
902 	{
903 		total_length = avg_generate_vector_list();
904 
905 		/* for Major Havoc, we need to look for empty frames */
906 		if (total_length > 1)
907 			timer_set(TIME_IN_NSEC(1500) * total_length, 0, avgdvg_clr_busy);
908 		else
909 		{
910 			vector_updates--;
911 			busy = 0;
912 		}
913 	}
914 }
915 
916 
WRITE16_HANDLER(avgdvg_go_word_w)917 WRITE16_HANDLER( avgdvg_go_word_w )
918 {
919 	avgdvg_go_w(offset, data);
920 }
921 
922 
923 
924 /*************************************
925  *
926  *	AVG reset
927  *
928  ************************************/
929 
WRITE_HANDLER(avgdvg_reset_w)930 WRITE_HANDLER( avgdvg_reset_w )
931 {
932 	avgdvg_clr_busy(0);
933 }
934 
935 
WRITE16_HANDLER(avgdvg_reset_word_w)936 WRITE16_HANDLER( avgdvg_reset_word_w )
937 {
938 	avgdvg_clr_busy(0);
939 }
940 
941 
942 
943 /*************************************
944  *
945  *	Vector generator init
946  *
947  ************************************/
948 
avgdvg_init(int vector_type)949 int avgdvg_init(int vector_type)
950 {
951 	int i;
952 
953 	/* 0 vector RAM size is invalid */
954 	if (vectorram_size == 0)
955 	{
956 		logerror("Error: vectorram_size not initialized\n");
957 		return 1;
958 	}
959 
960 	/* initialize the pages */
961 	for (i = 0; i < NUM_BANKS; i++)
962 		vectorbank[i] = vectorram + i * BANK_SIZE;
963 	if (vector_type == USE_AVG_MHAVOC || vector_type == USE_AVG_ALPHAONE)
964 		vectorbank[1] = &memory_region(REGION_CPU1)[0x18000];
965 
966 	/* set the engine type and validate it */
967 	vector_engine = vector_type;
968 	if (vector_engine < AVGDVG_MIN || vector_engine > AVGDVG_MAX)
969 	{
970 		logerror("Error: unknown Atari Vector Game Type\n");
971 		return 1;
972 	}
973 
974 	/* Star Wars is reverse-endian */
975 	if (vector_engine == USE_AVG_SWARS)
976 		flipword = 1;
977 
978 	/* Quantum may be reverse-endian depending on the platform */
979 #ifdef MSB_FIRST
980 	else if (vector_engine==USE_AVG_QUANTUM)
981 		flipword = 1;
982 #endif
983 
984 	/* everyone else is standard */
985 	else
986 		flipword = 0;
987 
988 	/* clear the busy state */
989 	busy = 0;
990 
991 	/* compute the min/max values */
992 	xmin = Machine->visible_area.min_x;
993 	ymin = Machine->visible_area.min_y;
994 	xmax = Machine->visible_area.max_x;
995 	ymax = Machine->visible_area.max_y;
996 	width = xmax - xmin;
997 	height = ymax - ymin;
998 
999 	/* determine the center points */
1000 	xcenter = ((xmax + xmin) / 2) << 16;
1001 	ycenter = ((ymax + ymin) / 2) << 16;
1002 
1003 	/* initialize to no avg flipping */
1004 	flip_x = flip_y = 0;
1005 
1006 	/* Tempest and Quantum have X and Y swapped */
1007 	if ((vector_type == USE_AVG_TEMPEST) ||
1008 		(vector_type == USE_AVG_QUANTUM))
1009 		swap_xy = 1;
1010 	else
1011 		swap_xy = 0;
1012 
1013 	return video_start_vector();
1014 }
1015 
1016 
1017 
1018 /*************************************
1019  *
1020  *	Video startup
1021  *
1022  ************************************/
1023 
VIDEO_START(dvg)1024 VIDEO_START( dvg )
1025 {
1026 	return avgdvg_init(USE_DVG);
1027 }
1028 
1029 
VIDEO_START(avg)1030 VIDEO_START( avg )
1031 {
1032 	return avgdvg_init(USE_AVG);
1033 }
1034 
1035 
VIDEO_START(avg_starwars)1036 VIDEO_START( avg_starwars )
1037 {
1038 	return avgdvg_init(USE_AVG_SWARS);
1039 }
1040 
1041 
VIDEO_START(avg_tempest)1042 VIDEO_START( avg_tempest )
1043 {
1044 	return avgdvg_init(USE_AVG_TEMPEST);
1045 }
1046 
1047 
VIDEO_START(avg_mhavoc)1048 VIDEO_START( avg_mhavoc )
1049 {
1050 	return avgdvg_init(USE_AVG_MHAVOC);
1051 }
1052 
1053 
VIDEO_START(avg_alphaone)1054 VIDEO_START( avg_alphaone )
1055 {
1056 	return avgdvg_init(USE_AVG_ALPHAONE);
1057 }
1058 
1059 
VIDEO_START(avg_bzone)1060 VIDEO_START( avg_bzone )
1061 {
1062 	return avgdvg_init(USE_AVG_BZONE);
1063 }
1064 
1065 
VIDEO_START(avg_quantum)1066 VIDEO_START( avg_quantum )
1067 {
1068 	return avgdvg_init(USE_AVG_QUANTUM);
1069 }
1070 
1071 
VIDEO_START(avg_redbaron)1072 VIDEO_START( avg_redbaron )
1073 {
1074 	return avgdvg_init(USE_AVG_RBARON);
1075 }
1076 
1077 
1078 
1079 /*************************************
1080  *
1081  *	Palette generation
1082  *
1083  ************************************/
1084 
1085 /* Black and White vector colors for Asteroids, Lunar Lander, Omega Race */
PALETTE_INIT(avg_white)1086 PALETTE_INIT( avg_white )
1087 {
1088 	int i;
1089 	for (i = 0; i < 32; i++)
1090 		colorram[i] = MAKE_RGB(0xff, 0xff, 0xff);
1091 }
1092 
1093 
1094 /* Basic 8 rgb vector colors for Tempest, Gravitar, Major Havoc etc. */
PALETTE_INIT(avg_multi)1095 PALETTE_INIT( avg_multi )
1096 {
1097 	int i;
1098 	for (i = 0; i < 32; i++)
1099 		colorram[i] = VECTOR_COLOR111(i);
1100 }
1101 
1102 
1103 
1104 /*************************************
1105  *
1106  *	Color RAM handling
1107  *
1108  ************************************/
1109 
WRITE_HANDLER(tempest_colorram_w)1110 WRITE_HANDLER( tempest_colorram_w )
1111 {
1112 	int bit3 = (~data >> 3) & 1;
1113 	int bit2 = (~data >> 2) & 1;
1114 	int bit1 = (~data >> 1) & 1;
1115 	int bit0 = (~data >> 0) & 1;
1116 	int r = bit1 * 0xee + bit0 * 0x11;
1117 	int g = bit3 * 0xee;
1118 	int b = bit2 * 0xee;
1119 
1120 	colorram[offset] = MAKE_RGB(r, g, b);
1121 }
1122 
1123 
WRITE_HANDLER(mhavoc_colorram_w)1124 WRITE_HANDLER( mhavoc_colorram_w )
1125 {
1126 	int bit3 = (~data >> 3) & 1;
1127 	int bit2 = (~data >> 2) & 1;
1128 	int bit1 = (~data >> 1) & 1;
1129 	int bit0 = (~data >> 0) & 1;
1130 	int r = bit3 * 0xee + bit2 * 0x11;
1131 	int g = bit1 * 0xee;
1132 	int b = bit0 * 0xee;
1133 
1134 	colorram[offset] = MAKE_RGB(r, g, b);
1135 }
1136 
1137 
WRITE16_HANDLER(quantum_colorram_w)1138 WRITE16_HANDLER( quantum_colorram_w )
1139 {
1140 	if (ACCESSING_LSB)
1141 	{
1142 		int bit3 = (~data >> 3) & 1;
1143 		int bit2 = (~data >> 2) & 1;
1144 		int bit1 = (~data >> 1) & 1;
1145 		int bit0 = (~data >> 0) & 1;
1146 		int r = bit3 * 0xee;
1147 		int g = bit1 * 0xee + bit0 * 0x11;
1148 		int b = bit2 * 0xee;
1149 
1150 		colorram[offset & 0x0f] = MAKE_RGB(r, g, b);
1151 	}
1152 }
1153 
1154 
sparkle_callback(void)1155 static rgb_t sparkle_callback(void)
1156 {
1157 	return colorram[16 + ((rand() >> 8) & 15)];
1158 }
1159