1 /***************************************************************************
2 
3 	Victory video system
4 
5 ***************************************************************************/
6 
7 #include "driver.h"
8 #include "vidhrdw/generic.h"
9 
10 
11 /* globally-accessible storage */
12 UINT8 *victory_charram;
13 
14 /* local allocated storage */
15 static UINT8 *bgbitmap;
16 static UINT8 *fgbitmap;
17 static UINT8 *bgdirty;
18 static UINT8 *chardirty;
19 static UINT8 *scandirty;
20 static UINT8 *rram, *gram, *bram;
21 
22 /* interrupt, collision, and control states */
23 static UINT8 vblank_irq;
24 static UINT8 fgcoll, fgcollx, fgcolly;
25 static UINT8 bgcoll, bgcollx, bgcolly;
26 static UINT8 scrollx, scrolly;
27 static UINT8 update_complete;
28 static UINT8 video_control;
29 
30 /* microcode state */
31 static struct
32 {
33 	UINT16	i;
34 	UINT16	pc;
35 	UINT8	r,g,b;
36 	UINT8	x,xp,y,yp;
37 	UINT8	cmd,cmdlo;
38 	void *	timer;
39 	UINT8	timer_active;
40 	double	endtime;
41 } micro;
42 
43 
44 /* number of ticks per clock of the microcode state machine   */
45 /* from what I can tell, this should be divided by 32, not 8  */
46 /* but the interrupt test does some precise timing, and fails */
47 /* if it's not 8 */
48 #define MICRO_STATE_CLOCK_PERIOD	TIME_IN_HZ(11827000/8)
49 
50 
51 /* debugging constants */
52 #define LOG_MICROCODE		0
53 #define LOG_COLLISION		0
54 
55 
56 /* function prototypes */
57 static int command2(void);
58 static int command3(void);
59 static int command4(void);
60 static int command5(void);
61 static int command6(void);
62 static int command7(void);
63 
64 
65 
66 /*************************************
67  *
68  *	Initialize the video system
69  *
70  *************************************/
71 
VIDEO_START(victory)72 VIDEO_START( victory )
73 {
74 	/* allocate bitmapram */
75 	rram = auto_malloc(0x4000);
76 	gram = auto_malloc(0x4000);
77 	bram = auto_malloc(0x4000);
78 
79 	/* allocate bitmaps */
80 	bgbitmap = auto_malloc(256 * 256);
81 	fgbitmap = auto_malloc(256 * 256);
82 
83 	/* allocate dirty maps */
84 	bgdirty = auto_malloc(32 * 32);
85 	chardirty = auto_malloc(256);
86 	scandirty = auto_malloc(512);
87 
88 	/* fail if something went wrong */
89 	if (!rram || !gram || !bram || !bgbitmap || !fgbitmap || !bgdirty || !chardirty || !scandirty)
90 		return 1;
91 
92 	/* mark everything dirty */
93 	memset(bgdirty, 1, 32 * 32);
94 	memset(chardirty, 1, 256);
95 	memset(scandirty, 1, 512);
96 
97 	/* reset globals */
98 	vblank_irq = 0;
99 	fgcoll = fgcollx = fgcolly = 0;
100 	bgcoll = bgcollx = bgcolly = 0;
101 	scrollx = scrolly = 0;
102 	update_complete = 0;
103 	video_control = 0;
104 	memset(&micro, 0, sizeof(micro));
105 	micro.timer = timer_alloc(NULL);
106 
107 	return 0;
108 }
109 
110 
111 
112 /*************************************
113  *
114  *	Interrupt generation
115  *
116  *************************************/
117 
victory_update_irq(void)118 void victory_update_irq(void)
119 {
120 	if (vblank_irq || fgcoll || (bgcoll && (video_control & 0x20)))
121 		cpu_set_irq_line(0, 0, ASSERT_LINE);
122 	else
123 		cpu_set_irq_line(0, 0, CLEAR_LINE);
124 }
125 
126 
INTERRUPT_GEN(victory_vblank_interrupt)127 INTERRUPT_GEN( victory_vblank_interrupt )
128 {
129 	vblank_irq = 1;
130 	victory_update_irq();
131 	log_cb(RETRO_LOG_DEBUG, LOGPRE "------------- VBLANK ----------------\n");
132 }
133 
134 
135 
136 /*************************************
137  *
138  *	Video RAM write
139  *
140  *************************************/
141 
WRITE_HANDLER(victory_videoram_w)142 WRITE_HANDLER( victory_videoram_w )
143 {
144 	if (videoram[offset] != data)
145 	{
146 		videoram[offset] = data;
147 		bgdirty[offset] = 1;
148 	}
149 }
150 
151 
152 
153 /*************************************
154  *
155  *	Character RAM write
156  *
157  *************************************/
158 
WRITE_HANDLER(victory_charram_w)159 WRITE_HANDLER( victory_charram_w )
160 {
161 	if (victory_charram[offset] != data)
162 	{
163 		victory_charram[offset] = data;
164 		chardirty[(offset / 8) % 256] = 1;
165 	}
166 }
167 
168 
169 
170 /*************************************
171  *
172  *	Palette RAM write
173  *
174  *************************************/
175 
WRITE_HANDLER(victory_paletteram_w)176 WRITE_HANDLER( victory_paletteram_w )
177 {
178 	int red = ((offset & 0x80) >> 5) | ((data & 0xc0) >> 6);
179 	int blue = (data & 0x38) >> 3;
180 	int green = data & 0x07;
181 
182 	/* shift up to 8 bits */
183 	red   = (red << 5)   | (red << 2)   | (red >> 1);
184 	green = (green << 5) | (green << 2) | (green >> 1);
185 	blue  = (blue << 5)  | (blue << 2)  | (blue >> 1);
186 
187 	/* set the color */
188 	palette_set_color(offset & 0x3f, red, green, blue);
189 }
190 
191 
192 
193 /*************************************
194  *
195  *	Video control read
196  *
197  *************************************/
198 
READ_HANDLER(victory_video_control_r)199 READ_HANDLER( victory_video_control_r )
200 {
201 	int result = 0;
202 
203 	switch (offset)
204 	{
205 		case 0:		/* 5XFIQ */
206 			result = fgcollx;
207 			if (LOG_COLLISION) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:5XFIQ read = %02X\n", activecpu_get_previouspc(), result);
208 			return result;
209 
210 		case 1:		/* 5CLFIQ */
211 			result = fgcolly;
212 			if (fgcoll)
213 			{
214 				fgcoll = 0;
215 				victory_update_irq();
216 			}
217 			if (LOG_COLLISION) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:5CLFIQ read = %02X\n", activecpu_get_previouspc(), result);
218 			return result;
219 
220 		case 2:		/* 5BACKX */
221 			result = bgcollx & 0xfc;
222 			if (LOG_COLLISION) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:5BACKX read = %02X\n", activecpu_get_previouspc(), result);
223 			return result;
224 
225 		case 3:		/* 5BACKY */
226 			result = bgcolly;
227 			if (bgcoll)
228 			{
229 				bgcoll = 0;
230 				victory_update_irq();
231 			}
232 			if (LOG_COLLISION) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:5BACKY read = %02X\n", activecpu_get_previouspc(), result);
233 			return result;
234 
235 		case 4:		/* 5STAT */
236 			/* D7 = BUSY (9A1) -- microcode*/
237 			/* D6 = 5FCIRQ (3B1)*/
238 			/* D5 = 5VIRQ*/
239 			/* D4 = 5BCIRQ (3B1)*/
240 			/* D3 = SL256*/
241 			if (micro.timer_active && timer_timeelapsed(micro.timer) < micro.endtime)
242 				result |= 0x80;
243 			result |= (~fgcoll & 1) << 6;
244 			result |= (~vblank_irq & 1) << 5;
245 			result |= (~bgcoll & 1) << 4;
246 			result |= (cpu_getscanline() & 0x100) >> 5;
247 			if (LOG_COLLISION) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:5STAT read = %02X\n", activecpu_get_previouspc(), result);
248 			return result;
249 
250 		default:
251 			log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:victory_video_control_r(%02X)\n", activecpu_get_previouspc(), offset);
252 			break;
253 	}
254 	return 0;
255 }
256 
257 
258 
259 /*************************************
260  *
261  *	Video control write
262  *
263  *************************************/
264 
WRITE_HANDLER(victory_video_control_w)265 WRITE_HANDLER( victory_video_control_w )
266 {
267 	switch (offset)
268 	{
269 		case 0:		/* LOAD IL */
270 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:IL=%02X\n", activecpu_get_previouspc(), data);
271 			micro.i = (micro.i & 0xff00) | (data & 0x00ff);
272 			break;
273 
274 		case 1:		/* LOAD IH */
275 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:IH=%02X\n", activecpu_get_previouspc(), data);
276 			micro.i = (micro.i & 0x00ff) | ((data << 8) & 0xff00);
277 			if (micro.cmdlo == 5)
278 			{
279 				if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "  Command 5 triggered by write to IH\n");
280 				command5();
281 			}
282 			break;
283 
284 		case 2:		/* LOAD CMD */
285 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:CMD=%02X\n", activecpu_get_previouspc(), data);
286 			micro.cmd = data;
287 			micro.cmdlo = data & 7;
288 			if (micro.cmdlo == 0)
289 				log_cb(RETRO_LOG_DEBUG, LOGPRE "  Command 0 triggered\n");
290 			else if (micro.cmdlo == 1)
291 				log_cb(RETRO_LOG_DEBUG, LOGPRE "  Command 1 triggered\n");
292 			else if (micro.cmdlo == 6)
293 			{
294 				if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "  Command 6 triggered\n");
295 				command6();
296 			}
297 			break;
298 
299 		case 3:		/* LOAD G */
300 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:G=%02X\n", activecpu_get_previouspc(), data);
301 			micro.g = data;
302 			break;
303 
304 		case 4:		/* LOAD X */
305 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:X=%02X\n", activecpu_get_previouspc(), data);
306 			micro.xp = data;
307 			if (micro.cmdlo == 3)
308 			{
309 				if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE " Command 3 triggered by write to X\n");
310 				command3();
311 			}
312 			break;
313 
314 		case 5:		/* LOAD Y */
315 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:Y=%02X\n", activecpu_get_previouspc(), data);
316 			micro.yp = data;
317 			if (micro.cmdlo == 4)
318 			{
319 				if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "  Command 4 triggered by write to Y\n");
320 				command4();
321 			}
322 			break;
323 
324 		case 6:		/* LOAD R */
325 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:R=%02X\n", activecpu_get_previouspc(), data);
326 			micro.r = data;
327 			break;
328 
329 		case 7:		/* LOAD B */
330 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:B=%02X\n", activecpu_get_previouspc(), data);
331 			micro.b = data;
332 			if (micro.cmdlo == 2)
333 			{
334 				if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "  Command 2 triggered by write to B\n");
335 				command2();
336 			}
337 			else if (micro.cmdlo == 7)
338 			{
339 				if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "  Command 7 triggered by write to B\n");
340 				command7();
341 			}
342 			break;
343 
344 		case 8:		/* SCROLLX */
345 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:SCROLLX write = %02X\n", activecpu_get_previouspc(), data);
346 			scrollx = data;
347 			break;
348 
349 		case 9:		/* SCROLLY */
350 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:SCROLLY write = %02X\n", activecpu_get_previouspc(), data);
351 			scrolly = data;
352 			break;
353 
354 		case 10:	/* CONTROL */
355 			/* D7 = HLMBK*/
356 			/* D6 = VLMBK*/
357 			/* D5 = BIRQEA*/
358 			/* D4 = SEL5060*/
359 			/* D3 = SINVERT*/
360 			/* D2 = BIR12*/
361 			/* D1 = SELOVER*/
362 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:CONTROL write = %02X\n", activecpu_get_previouspc(), data);
363 			video_control = data;
364 			break;
365 
366 		case 11:	/* CLRVIRQ */
367 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:CLRVIRQ write = %02X\n", activecpu_get_previouspc(), data);
368 			vblank_irq = 0;
369 			victory_update_irq();
370 			break;
371 
372 		default:
373 			if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "%04X:victory_video_control_w(%02X) = %02X\n", activecpu_get_previouspc(), offset, data);
374 			break;
375 	}
376 }
377 
378 
379 /***************************************************************************************************
380 
381 	Victory Microcode
382 	-----------------
383 
384 	The cool thing about this hardware is the use of microcode, which is like having a little
385 	graphics coprocessor around to do the hard stuff. The operations that can be performed by
386 	this bit of circuitry include pixel plotting, line drawing, sprite drawing, and data
387 	transfer, all with optional collision detection. In addition, data can be uploaded into
388 	the $2000-$21FF address range and then "executed" as mini subroutines.
389 
390 	Commands to the microcode are written to the command register at $C102, followed by
391 	whatever parameters are needed. Parameters are stored in registers. There are a number
392 	of registers, accessed at these addresses:
393 
394 		C100-C101:	I (16 bits)
395 		C102:		CMD (8 bits)
396 		C103:		G (8 bits)
397 		C104:		X' (8 bits)
398 		C105:		Y' (8 bits)
399 		C106:		R (8 bits)
400 		C107:		B (8 bits)
401 
402 	Writing the last parameter triggers the command. There are a total of 6 commands supported:
403 
404 		command 2: copy data
405 			when register B is written, take the bytes from R, G and B and transfer them
406 			into video RAM at address I
407 
408 		command 3: draw sprite
409 			when register X is written, draw a sprite at location (X,Y) using the data from
410 			video RAM address I; the width is given by (R >> 5) * 8, and then height is
411 			given by (R & 31) * 2; data is XORed with the current VRAM contents
412 
413 		command 4: execute program
414 			when register Y is written, copy Y * 2 to the PC and begin executing the commands
415 			at ($2000 + PC); each command loads 6 bytes from VRAM into registers CMD,X,Y,I and R;
416 			the program stops executing after it receives a command with the high bit off
417 
418 		command 5: draw vector
419 			when register IH is written, draw a vector of length IL starting at location (X,Y);
420 			IH serves as the bresenhem increment for the minor axis; bits 4-6 of the command
421 			select which octant to draw into; each VRAM write XORs the data from R,G and B
422 			with the current VRAM contents
423 
424 		command 6: copy data
425 			when the command is written, copy (R & 31) * 2 bytes of data from video RAM location
426 			I to video RAM location ($2000 + PC)
427 
428 		command 7: plot pixel
429 			when register B is written, take the bytes from R, G and B and XOR them with the
430 			video RAM contents at (X,Y)
431 
432 	The command register is actually broken down into bitfields as follows:
433 
434 		D7    -> must be high for a program to continue execution; otherwise, it will stop
435 		D4-D6 -> for non-vector commands, enables VRAM writes to the red, blue and green planes
436 		D3    -> enable collision detection for commands 3,5,7
437 		D0-D2 -> command
438 
439 	The microcode is actually a big state machine, driven by the 4 PROMs at 19B,19C,19D and 19E.
440 	Below are some of the gory details of the state machine.
441 
442 ***************************************************************************************************
443 
444 	19E:
445 		D7 -> inverter -> ZERO RAM [11C8, 13D8]
446 		D6 -> select on the mux at 18F
447 		D5 -> BUSY [4B6]
448 		D4 -> D on flip flop at 16E
449 		D3 -> D3 of alternate selection from mux at 18F
450 		D2 -> D2 of alternate selection from mux at 18F
451 		D1 -> D1 of alternate selection from mux at 18F
452 		D0 -> D0 of alternate selection from mux at 18F
453 
454 	19B:
455 		D7 -> S LOAD LH [11B8]
456 		D6 -> INC I (AND with WRITE EA) [8A8]
457 		D5 -> S INC Y (AND with WRITE EA) [8C8]
458 		D4 -> SXFERY (AND with WRITE EA) [8C8]
459 		D3 -> D on flip flop at 15E, output goes to SADDX [8C8]
460 		D2 -> S LOAD PC [8B8]
461 		D1 -> CPU0 [11C8, 13C7]
462 		D0 -> INC X (AND with WRITE EA) [8C8]
463 
464 	19C:
465 		D7 -> SXFERX/INC X (AND with WRITE EA) [8C8, 11B8, 12C8]
466 		D6 -> see D5
467 		D5 -> selects one of 4 with D6:
468 				0 -> SEA VDATA
469 				1 -> SEA BUFF
470 				2 -> SEA SR 1
471 				3 -> SEA SR 2
472 		D4 -> ADD 128 [11C8, 12C8]
473 		      also: S ACC CLEAR (AND with WRITE EA) [10B8]
474 		D3 -> S ACC CLK (AND with S SEQ CLK) [10B8]
475 		D2 -> INC PC [8B8]
476 		D1 -> INC L [11B8]
477 		D0 -> INC H [11B8]
478 
479 	19D:
480 		D7 -> S W VRAM (AND with WRITE EA) [14A8]
481 		D6 -> S WRITE BUSS1 (AND with WRITE EA) [7A8]
482 		D5 -> S WRITE BUSS2 (AND with WRITE EA) [7A8]
483 		D4 -> D2 of alternate selection from mux at 18E
484 		D3 -> D1 of alternate selection from mux at 18E
485 		D2 -> D0 of alternate selection from mux at 18E
486 		D1 -> ASEL1 (AND with WRITE EA) [8D8]
487 		D0 -> ASEL0 (AND with WRITE EA) [8D8]
488 
489 
490 	Always on in non-zero states: BUSY, CPU0
491 
492 	State	Next				ASEL SEA Interesting bits
493 	-----	----				---- --- --------------------------------------------
494 	  00	/SETRDY ? 00 : 01	  3   0  None
495 	  01	CM0-2				  0   0
496 	  02	00					  0   0  ZERORAM, INCI, SWVRAM
497 	  03	1C					  2   0  SLOADLH, SXFERY
498 	  04	1A					  2   0  SLOADPC
499 	  05	0A					  1   0  SXFERY, ADD128+SACCCLEAR, SACCCLK
500 	  06	0C					  0   0  SLOADLH, SLOADPC
501 	  07	08					  1   0  SXFERY, SXFERX+INCX
502 	  08	09					  1   2  INCX, SWVRAM
503 	  09	00					  1   3  SWVRAM
504 	  0A	VFIN ? 19 : 0B		  1   0  SXFERX+INCX
505 	  0B	0A					  1   2  INCI, SACCCLK, SWVRAM
506 	  0C	0D					  0   1  INCI, SXFERX+INCX, INCL
507 	  0D	/LTC ? 0C : 0E		  2   2  ZERORAM, INCPC, SWVRAM
508 	  0E	19					  2   2
509 
510 	  19	/CM7 ? 00 : 1A		  2   0
511 	  1A	1B					  2   0  INCPC, SWRITEBUSS1
512 	  1B	01					  2   0  INCPC, SWRITEBUSS2
513 	  1C	HTC ? 19 : 1D		  0   1
514 	  1D	1E					  1   2  INCX, SXFERX+INCX, INCL, SWVRAM
515 	  1E	/LTC ? 1C : 1F		  1   3  INCI, SINCY, SWVRAM
516 	  1F	1C					  1   0  ZERORAM, SXFERY, SADDX, INCH
517 
518 Registers:
519 
520 	X' = 8-bit value = 2 x 4-bit counters at 11B/13B
521 			SADDX  -> enables clock to count
522 			LF/RT  -> controls direction of counting
523 			SLDX   -> loads data from RED VRAM or D0-D7 into X'
524 			OUT    -> to X
525 
526 	X  = 8-bit value = 2 x 4-bit counters at 12D/13D
527 			SINCX  -> enables clock to count
528 			SXFERX -> loads data from X' into X, with an XOR of 7
529 			OUT    -> to X1-X128
530 
531 	Y' = 8-bit value = 8-bit latch
532 			SLDY   -> loads data from BLUE VRAM or D0-D7 into Y'
533 			OUT    -> to Y
534 
535 	Y  = 8-bit value = 2 x 4-bit counters at 10B/8B
536 			SINCY  -> enables clock to count
537 			SXFERY -> loads data from Y' into Y
538 			OUT    -> to Y1-Y128
539 
540 	I  = 16-bit value = 4 x 4-bit counters at 12C/11C/12B/14B
541 			INCI   -> enables clock to count
542 			SLDIH  -> loads data from BLUE VRAM or D0-D7 into upper 8 bits of I
543 			SLDIL  -> loads data from RED VRAM or D0-D7 into lower 8 bits of I
544 			OUT    -> to I1-I32000
545 
546 	PC = 9-bit value = 2 x 4-bit counters at 9B/7B plus JK flip-flop at 12E
547 			INCPC  -> toggles flip-flop and increments
548 			SLOADPC-> loads data from Y' into PC
549 
550 	L  = 5-bit value = 2 x 4-bit counters at 3H/4H
551 			INCL   -> enables clock to count
552 			SLOADLH-> loads data from SEA
553 
554 	H  = 3-bit value = 1 x 4-bit counter at 5H
555 			INCH   -> enables clock to count
556 			SLOADLH-> loads data from SEA
557 
558 	14-bit VRAM address comes from one of several sources, depending on ASEL
559 		ASEL0 -> I & 0x3fff
560 		ASEL1 -> ((Y & 0xff) << 5) | ((X & 0xff) >> 3)
561 		ASEL2 -> 0x2000 | (PC & 0x1ff)
562 		ASEL3 -> ((L & 0xff) << 5) | ((E & 0xff) >> 3) 	[video refresh]
563 
564 ***************************************************************************************************/
565 
566 
567 /*************************************
568  *
569  *	Microcode timing
570  *
571  *************************************/
572 
count_states(int states)573 static INLINE void count_states(int states)
574 {
575 	if (!micro.timer)
576 	{
577 		timer_adjust(micro.timer, TIME_NEVER, 0, 0);
578 		micro.timer_active = 1;
579 		micro.endtime = (double)states * MICRO_STATE_CLOCK_PERIOD;
580 	}
581 	else if (timer_timeelapsed(micro.timer) > micro.endtime)
582 	{
583 		timer_adjust(micro.timer, TIME_NEVER, 0, 0);
584 		micro.timer_active = 1;
585 		micro.endtime = (double)states * MICRO_STATE_CLOCK_PERIOD;
586 	}
587 	else
588 		micro.endtime += (double)states * MICRO_STATE_CLOCK_PERIOD;
589 }
590 
591 
592 /*************************************
593  *
594  *	Microcode command 2:
595  *		Load data from R/G/B
596  *
597  *************************************/
598 
command2(void)599 static int command2(void)
600 {
601 /*
602 	Actual microcode:
603 		  02	00					  0   0  ZERORAM, INCI, SWVRAM
604 
605 	Basic gist of things:
606 		WRITE
607 		I++
608 		goto state00
609 */
610 	int addr = micro.i++ & 0x3fff;
611 
612 	if (micro.cmd & 0x10)
613 		gram[addr] = micro.g;
614 	if (micro.cmd & 0x20)
615 		bram[addr] = micro.b;
616 	if (micro.cmd & 0x40)
617 		rram[addr] = micro.r;
618 
619 	scandirty[addr >> 5] = 1;
620 	count_states(3);
621 	return 0;
622 }
623 
624 
625 /*************************************
626  *
627  *	Microcode command 3:
628  *		Draw sprite from I to (X,Y)
629  *
630  *************************************/
631 
command3(void)632 static int command3(void)
633 {
634 /*
635 	Actual microcode:
636 		  03	1C					  2   0  SLOADLH, SXFERY
637 		  1C	HTC ? 19 : 1D		  0   1
638 		  1D	1E					  1   2  INCX, SXFERX+INCX, INCL, SWVRAM
639 		  1E	/LTC ? 1C : 1F		  1   3  INCI, SINCY, SWVRAM
640 		  1F	1C					  1   0  ZERORAM, SXFERY, SADDX, INCH
641 
642 	Basic gist of things:
643 		H = R >> 5
644 		L = (R & 0x1f) << 1
645 		Y = Y'
646 		state1C:
647 			if (H & 8) goto state19
648 			X = X'; L++
649 			WRITE
650 			I++; Y++
651 			if ((L & 0x20) == 0) goto state1C
652 			Y = Y'; X'++; H++
653 			goto state1C
654 */
655 	int ycount = 64 - (micro.r & 31) * 2;
656 	int xcount = 8 - (micro.r >> 5);
657 	int shift = micro.xp & 7;
658 	int nshift = 8 - shift;
659 	int x, y, sy;
660 
661 	for (x = 0; x < xcount; x++, micro.xp += 8)
662 	{
663 		sy = micro.yp;
664 
665 		for (y = 0; y < ycount; y++)
666 		{
667 			int srcoffs = micro.i++ & 0x3fff;
668 			int dstoffs = (sy++ & 0xff) * 32 + micro.xp / 8;
669 			UINT8 src;
670 
671 			/* non-collision-detect case */
672 			if (!(micro.cmd & 0x08) || fgcoll)
673 			{
674 				if (micro.cmd & 0x10)
675 				{
676 					src = gram[srcoffs];
677 					gram[dstoffs + 0] ^= src >> shift;
678 					gram[dstoffs + 1] ^= src << nshift;
679 				}
680 				if (micro.cmd & 0x20)
681 				{
682 					src = bram[srcoffs];
683 					bram[dstoffs + 0] ^= src >> shift;
684 					bram[dstoffs + 1] ^= src << nshift;
685 				}
686 				if (micro.cmd & 0x40)
687 				{
688 					src = rram[srcoffs];
689 					rram[dstoffs + 0] ^= src >> shift;
690 					rram[dstoffs + 1] ^= src << nshift;
691 				}
692 			}
693 
694 			/* collision-detect case */
695 			else
696 			{
697 				if (micro.cmd & 0x10)
698 				{
699 					src = gram[srcoffs];
700 					if ((gram[dstoffs + 0] & (src >> shift)) | (gram[dstoffs + 1] & (src << nshift)))
701 						fgcoll = 1, fgcollx = micro.xp, fgcolly = sy - 1;
702 					gram[dstoffs + 0] ^= src >> shift;
703 					gram[dstoffs + 1] ^= src << nshift;
704 				}
705 				if (micro.cmd & 0x20)
706 				{
707 					src = bram[srcoffs];
708 					if ((bram[dstoffs + 0] & (src >> shift)) | (bram[dstoffs + 1] & (src << nshift)))
709 						fgcoll = 1, fgcollx = micro.xp, fgcolly = sy - 1;
710 					bram[dstoffs + 0] ^= src >> shift;
711 					bram[dstoffs + 1] ^= src << nshift;
712 				}
713 				if (micro.cmd & 0x40)
714 				{
715 					src = rram[srcoffs];
716 					if ((rram[dstoffs + 0] & (src >> shift)) | (rram[dstoffs + 1] & (src << nshift)))
717 						fgcoll = 1, fgcollx = micro.xp, fgcolly = sy - 1;
718 					rram[dstoffs + 0] ^= src >> shift;
719 					rram[dstoffs + 1] ^= src << nshift;
720 				}
721 				if (fgcoll) victory_update_irq();
722 			}
723 		}
724 	}
725 
726 	/* mark scanlines dirty */
727 	sy = micro.yp;
728 	for (y = 0; y < ycount; y++)
729 		scandirty[sy++ & 0xff] = 1;
730 
731 	count_states(3 + (2 + 2 * ycount) * xcount);
732 
733 	return micro.cmd & 0x80;
734 }
735 
736 
737 /*************************************
738  *
739  *	Microcode command 4:
740  *		Execute commands at (Y * 2)
741  *
742  *************************************/
743 
command4(void)744 static int command4(void)
745 {
746 /*
747 	Actual microcode:
748 		  04	1A					  2   0  SLOADPC
749 		  1A	1B					  2   0  INCPC, SWRITEBUSS1
750 		  1B	01					  2   0  INCPC, SWRITEBUSS2
751 
752 	Basic gist of things:
753 		PC = Y' << 1
754 		CM = GREEN[PC]
755 		I = (BLUE[PC] << 8) + RED[PC]
756 		PC++
757 		R = GREEN[PC]
758 		X' = RED[PC]
759 		Y' = BLUE[PC]
760 		PC++
761 		goto state01
762 */
763 	int keep_going = 0;
764 
765 	if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "================= EXECUTE BEGIN\n");
766 
767 	count_states(4);
768 
769 	micro.pc = micro.yp << 1;
770 	do
771 	{
772 		micro.cmd = gram[0x2000 + micro.pc];
773 		micro.cmdlo = micro.cmd & 7;
774 		micro.i = (bram[0x2000 + micro.pc] << 8) | rram[0x2000 + micro.pc];
775 		micro.r = gram[0x2001 + micro.pc];
776 		micro.xp = rram[0x2001 + micro.pc];
777 		micro.yp = bram[0x2001 + micro.pc];
778 		if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "PC=%03X  CMD=%02X I=%04X R=%02X X=%02X Y=%02X\n", micro.pc, micro.cmd, micro.i, micro.r, micro.xp, micro.yp);
779 		micro.pc = (micro.pc + 2) & 0x1ff;
780 
781 		switch (micro.cmdlo)
782 		{
783 			case 0:												break;
784 			case 1:												break;
785 			case 2:	keep_going = command2();					break;
786 			case 3:	keep_going = command3();					break;
787 			case 4:	micro.pc = micro.yp << 1; keep_going = 1;	break;
788 			case 5:	keep_going = command5();					break;
789 			case 6:	keep_going = command6();					break;
790 			case 7:	keep_going = command7();					break;
791 		}
792 	} while (keep_going);
793 
794 	if (LOG_MICROCODE) log_cb(RETRO_LOG_DEBUG, LOGPRE "================= EXECUTE END\n");
795 
796 	return micro.cmd & 0x80;
797 }
798 
799 
800 /*************************************
801  *
802  *	Microcode command 5:
803  *		Draw vector from (X,Y)
804  *
805  *************************************/
806 
command5(void)807 static int command5(void)
808 {
809 /*
810 	Actual microcode:
811 		  05	0A					  1   0  SXFERY, ADD128+SACCCLEAR, SACCCLK
812 		  0A	VFIN ? 19 : 0B		  1   0  SXFERX+INCX
813 		  0B	0A					  1   2  INCI, SACCCLK, SWVRAM
814 
815 	Basic gist of things:
816 		Y = Y'; ACC = 128
817 		X = X'/CLOCK SR
818 		while (!(IL & 0x100))
819 		{
820 			IL++; ACC += IH
821 			adjust X,Y based on carry
822 			WRITE(X,Y)  [SR1]
823 		}
824 
825 	line draw: one of 8 cases based on VDATA
826 
827 				no carry			carry
828 				--------			-----
829 		case 0: 1011 -> X++, Y		1101 -> X++, Y--
830 		case 1:	0101 -> X, Y--		1101 -> X++, Y--
831 		case 2: 0101 -> X, Y--		1100 -> X--, Y--
832 		case 3:	1010 -> X--, Y		1100 -> X--, Y--
833 		case 4: 1010 -> X--, Y		1110 -> X--, Y++
834 		case 5: 0111 -> X, Y++		1110 -> X--, Y++
835 		case 6: 0111 -> X, Y++		1111 -> X++, Y++
836 		case 7: 1011 -> X++, Y		1111 -> X++, Y++
837 
838 */
839 	static const INT8 inctable[8][4] =
840 	{
841 		{  1, 0, 1,-1 },
842 		{  0,-1, 1,-1 },
843 		{  0,-1,-1,-1 },
844 		{ -1, 0,-1,-1 },
845 		{ -1, 0,-1, 1 },
846 		{  0, 1,-1, 1 },
847 		{  0, 1, 1, 1 },
848 		{  1, 0, 1, 1 }
849 	};
850 
851 	int xinc = inctable[(micro.cmd >> 4) & 7][0];
852 	int yinc = inctable[(micro.cmd >> 4) & 7][1];
853 	int xincc = inctable[(micro.cmd >> 4) & 7][2];
854 	int yincc = inctable[(micro.cmd >> 4) & 7][3];
855 	UINT8 x = micro.xp;
856 	UINT8 y = micro.yp;
857 	int acc = 0x80;
858 	int i = micro.i >> 8;
859 	int c;
860 
861 	/* non-collision-detect case */
862 	if (!(micro.cmd & 0x08) || fgcoll)
863 	{
864 		for (c = micro.i & 0xff; c < 0x100; c++)
865 		{
866 			int addr = y * 32 + x / 8;
867 			int shift = x & 7;
868 			int nshift = 8 - shift;
869 
870 			gram[addr + 0] ^= micro.g >> shift;
871 			gram[addr + 1] ^= micro.g << nshift;
872 			bram[addr + 0] ^= micro.b >> shift;
873 			bram[addr + 1] ^= micro.b << nshift;
874 			rram[addr + 0] ^= micro.r >> shift;
875 			rram[addr + 1] ^= micro.r << nshift;
876 			scandirty[y] = 1;
877 
878 			acc += i;
879 			if (acc & 0x100)
880 			{
881 				x += xincc;
882 				y += yincc;
883 			}
884 			else
885 			{
886 				x += xinc;
887 				y += yinc;
888 			}
889 			acc &= 0xff;
890 		}
891 	}
892 
893 	/* collision-detect case */
894 	else
895 	{
896 		for (c = micro.i & 0xff; c < 0x100; c++)
897 		{
898 			int addr = y * 32 + x / 8;
899 			int shift = x & 7;
900 			int nshift = 8 - shift;
901 
902 			if ((gram[addr + 0] & (micro.g >> shift)) | (gram[addr + 1] & (micro.g << nshift)) |
903 				(bram[addr + 0] & (micro.b >> shift)) | (bram[addr + 1] & (micro.b << nshift)) |
904 				(rram[addr + 0] & (micro.r >> shift)) | (rram[addr + 1] & (micro.r << nshift)))
905 				fgcoll = 1, fgcollx = x, fgcolly = y;
906 
907 			gram[addr + 0] ^= micro.g >> shift;
908 			gram[addr + 1] ^= micro.g << nshift;
909 			bram[addr + 0] ^= micro.b >> shift;
910 			bram[addr + 1] ^= micro.b << nshift;
911 			rram[addr + 0] ^= micro.r >> shift;
912 			rram[addr + 1] ^= micro.r << nshift;
913 			scandirty[y] = 1;
914 
915 			acc += i;
916 			if (acc & 0x100)
917 			{
918 				x += xincc;
919 				y += yincc;
920 			}
921 			else
922 			{
923 				x += xinc;
924 				y += yinc;
925 			}
926 			acc &= 0xff;
927 		}
928 		if (fgcoll) victory_update_irq();
929 	}
930 
931 	micro.xp = x;
932 
933 	count_states(3 + 2 * (0x100 - (micro.i & 0xff)));
934 
935 	return micro.cmd & 0x80;
936 }
937 
938 
939 /*************************************
940  *
941  *	Microcode command 6:
942  *		Copy data from I to (Y * 2)
943  *
944  *************************************/
945 
command6(void)946 static int command6(void)
947 {
948 /*
949 	Actual microcode:
950 		  06	0C					  0   0  SLOADLH, SLOADPC
951 		  0C	0D					  0   1  INCI, SXFERX+INCX, INCL
952 		  0D	/LTC ? 0C : 0E		  2   2  ZERORAM, INCPC, SWVRAM
953 		  0E	19					  2   2
954 
955 	Basic gist of things:
956 		H = R >> 5
957 		L = (R & 0x1f) << 1
958 		PC = Y'
959 		state0C:
960 			I++; X = X'; L++
961 			WRITE(I, *PC)
962 			PC++
963 			if ((L & 0x20) == 0) goto state1C
964 */
965 	int i;
966 
967 	micro.pc = micro.yp << 1;
968 	for (i = (micro.r & 31) << 1; i < 64; i++)
969 	{
970 		int saddr = micro.i++ & 0x3fff;
971 		int daddr = 0x2000 + micro.pc++;
972 		micro.pc &= 0x1ff;
973 
974 		if (micro.cmd & 0x10)
975 			gram[daddr] = gram[saddr];
976 		if (micro.cmd & 0x20)
977 			bram[daddr] = bram[saddr];
978 		if (micro.cmd & 0x40)
979 			rram[daddr] = rram[saddr];
980 
981 		scandirty[daddr >> 5] = 1;
982 	}
983 
984 	count_states(3 + 2 * (64 - (micro.r & 31) * 2));
985 
986 	return micro.cmd & 0x80;
987 }
988 
989 
990 /*************************************
991  *
992  *	Microcode command 7:
993  *		Draw pixels to (X,Y)
994  *
995  *************************************/
996 
command7(void)997 static int command7(void)
998 {
999 /*
1000 	Actual microcode:
1001 		  07	08					  1   0  SXFERY, SXFERX+INCX
1002 		  08	09					  1   2  INCX, SWVRAM
1003 		  09	00					  1   3  SWVRAM
1004 
1005 	Basic gist of things:
1006 		Y = Y'
1007 		X = X'/CLOCK SR
1008 		WRITE SR1
1009 		X++
1010 		WRITE SR2
1011 */
1012 	int addr = micro.yp * 32 + micro.xp / 8;
1013 	int shift = micro.xp & 7;
1014 	int nshift = 8 - shift;
1015 
1016 	/* non-collision-detect case */
1017 	if (!(micro.cmd & 0x08) || fgcoll)
1018 	{
1019 		if (micro.cmd & 0x10)
1020 		{
1021 			gram[addr + 0] ^= micro.g >> shift;
1022 			gram[addr + 1] ^= micro.g << nshift;
1023 		}
1024 		if (micro.cmd & 0x20)
1025 		{
1026 			bram[addr + 0] ^= micro.b >> shift;
1027 			bram[addr + 1] ^= micro.b << nshift;
1028 		}
1029 		if (micro.cmd & 0x40)
1030 		{
1031 			rram[addr + 0] ^= micro.r >> shift;
1032 			rram[addr + 1] ^= micro.r << nshift;
1033 		}
1034 	}
1035 
1036 	/* collision-detect case */
1037 	else
1038 	{
1039 		if (micro.cmd & 0x10)
1040 		{
1041 			if ((gram[addr + 0] & (micro.g >> shift)) | (gram[addr + 1] & (micro.g << nshift)))
1042 				fgcoll = 1, fgcollx = micro.xp + 8, fgcolly = micro.yp;
1043 			gram[addr + 0] ^= micro.g >> shift;
1044 			gram[addr + 1] ^= micro.g << nshift;
1045 		}
1046 		if (micro.cmd & 0x20)
1047 		{
1048 			if ((bram[addr + 0] & (micro.b >> shift)) | (bram[addr + 1] & (micro.b << nshift)))
1049 				fgcoll = 1, fgcollx = micro.xp + 8, fgcolly = micro.yp;
1050 			bram[addr + 0] ^= micro.b >> shift;
1051 			bram[addr + 1] ^= micro.b << nshift;
1052 		}
1053 		if (micro.cmd & 0x40)
1054 		{
1055 			if ((rram[addr + 0] & (micro.r >> shift)) | (rram[addr + 1] & (micro.r << nshift)))
1056 				fgcoll = 1, fgcollx = micro.xp + 8, fgcolly = micro.yp;
1057 			rram[addr + 0] ^= micro.r >> shift;
1058 			rram[addr + 1] ^= micro.r << nshift;
1059 		}
1060 		if (fgcoll) victory_update_irq();
1061 	}
1062 
1063 	count_states(4);
1064 
1065 	scandirty[micro.yp] = 1;
1066 	return micro.cmd & 0x80;
1067 }
1068 
1069 
1070 /*************************************
1071  *
1072  *	Background update
1073  *
1074  *************************************/
1075 
update_background(void)1076 static void update_background(void)
1077 {
1078 	int x, y, row, offs;
1079 
1080 	/* update the background and any dirty characters in it */
1081 	for (y = offs = 0; y < 32; y++)
1082 		for (x = 0; x < 32; x++, offs++)
1083 		{
1084 			int code = videoram[offs];
1085 
1086 			/* see if the videoram or character RAM has changed, redraw it */
1087 			if (bgdirty[offs] || chardirty[code])
1088 			{
1089 				for (row = 0; row < 8; row++)
1090 				{
1091 					UINT8 pix2 = victory_charram[0x0000 + 8 * code + row];
1092 					UINT8 pix1 = victory_charram[0x0800 + 8 * code + row];
1093 					UINT8 pix0 = victory_charram[0x1000 + 8 * code + row];
1094 					UINT8 *dst = &bgbitmap[(y * 8 + row) * 256 + x * 8];
1095 
1096 					*dst++ = ((pix2 & 0x80) >> 5) | ((pix1 & 0x80) >> 6) | ((pix0 & 0x80) >> 7);
1097 					*dst++ = ((pix2 & 0x40) >> 4) | ((pix1 & 0x40) >> 5) | ((pix0 & 0x40) >> 6);
1098 					*dst++ = ((pix2 & 0x20) >> 3) | ((pix1 & 0x20) >> 4) | ((pix0 & 0x20) >> 5);
1099 					*dst++ = ((pix2 & 0x10) >> 2) | ((pix1 & 0x10) >> 3) | ((pix0 & 0x10) >> 4);
1100 					*dst++ = ((pix2 & 0x08) >> 1) | ((pix1 & 0x08) >> 2) | ((pix0 & 0x08) >> 3);
1101 					*dst++ = ((pix2 & 0x04)     ) | ((pix1 & 0x04) >> 1) | ((pix0 & 0x04) >> 2);
1102 					*dst++ = ((pix2 & 0x02) << 1) | ((pix1 & 0x02)     ) | ((pix0 & 0x02) >> 1);
1103 					*dst++ = ((pix2 & 0x01) << 2) | ((pix1 & 0x01) << 1) | ((pix0 & 0x01)     );
1104 				}
1105 				bgdirty[offs] = 0;
1106 			}
1107 		}
1108 
1109 	/* reset the char dirty array */
1110 	memset(chardirty, 0, 256);
1111 }
1112 
1113 
1114 /*************************************
1115  *
1116  *	Foreground update
1117  *
1118  *************************************/
1119 
update_foreground(void)1120 static void update_foreground(void)
1121 {
1122 	int x, y;
1123 
1124 	/* update the foreground's dirty scanlines */
1125 	for (y = 0; y < 256; y++)
1126 		if (scandirty[y])
1127 		{
1128 			UINT8 *dst = &fgbitmap[y * 256];
1129 
1130 			/* assemble the RGB bits for each 8-pixel chunk */
1131 			for (x = 0; x < 256; x += 8)
1132 			{
1133 				UINT8 g = gram[y * 32 + x / 8];
1134 				UINT8 b = bram[y * 32 + x / 8];
1135 				UINT8 r = rram[y * 32 + x / 8];
1136 
1137 				*dst++ = ((r & 0x80) >> 5) | ((b & 0x80) >> 6) | ((g & 0x80) >> 7);
1138 				*dst++ = ((r & 0x40) >> 4) | ((b & 0x40) >> 5) | ((g & 0x40) >> 6);
1139 				*dst++ = ((r & 0x20) >> 3) | ((b & 0x20) >> 4) | ((g & 0x20) >> 5);
1140 				*dst++ = ((r & 0x10) >> 2) | ((b & 0x10) >> 3) | ((g & 0x10) >> 4);
1141 				*dst++ = ((r & 0x08) >> 1) | ((b & 0x08) >> 2) | ((g & 0x08) >> 3);
1142 				*dst++ = ((r & 0x04)     ) | ((b & 0x04) >> 1) | ((g & 0x04) >> 2);
1143 				*dst++ = ((r & 0x02) << 1) | ((b & 0x02)     ) | ((g & 0x02) >> 1);
1144 				*dst++ = ((r & 0x01) << 2) | ((b & 0x01) << 1) | ((g & 0x01)     );
1145 			}
1146 			scandirty[y] = 0;
1147 		}
1148 }
1149 
1150 
1151 /*************************************
1152  *
1153  *	Determine the time when the beam
1154  *	will intersect a given pixel
1155  *
1156  *************************************/
1157 
pixel_time(int x,int y)1158 static double pixel_time(int x, int y)
1159 {
1160 	/* assuming this is called at refresh time, compute how long until we
1161 	 * hit the given x,y position */
1162 	return cpu_getscanlinetime(y) + (cpu_getscanlineperiod() * (double)x * (1.0 / 256.0));
1163 }
1164 
1165 
bgcoll_irq_callback(int param)1166 static void bgcoll_irq_callback(int param)
1167 {
1168 	bgcollx = param & 0xff;
1169 	bgcolly = param >> 8;
1170 	bgcoll = 1;
1171 	victory_update_irq();
1172 }
1173 
1174 
1175 
1176 /*************************************
1177  *
1178  *	End-of-frame callback
1179  *
1180  *************************************/
1181 
VIDEO_EOF(victory)1182 VIDEO_EOF( victory )
1183 {
1184 	int bgcollmask = (video_control & 4) ? 4 : 7;
1185 	int count = 0;
1186 	int x, y;
1187 
1188 	/* if we already did it, skip it */
1189 	if (update_complete)
1190 	{
1191 		update_complete = 0;
1192 		return;
1193 	}
1194 	update_complete = 0;
1195 
1196 	/* update the foreground & background */
1197 	update_foreground();
1198 	update_background();
1199 
1200 	/* blend the bitmaps and do collision detection */
1201 	for (y = 0; y < 256; y++)
1202 	{
1203 		int sy = (scrolly + y) & 255;
1204 		UINT8 *fg = &fgbitmap[y * 256];
1205 		UINT8 *bg = &bgbitmap[sy * 256];
1206 
1207 		/* do the blending */
1208 		for (x = 0; x < 256; x++)
1209 		{
1210 			int fpix = *fg++;
1211 			int bpix = bg[(x + scrollx) & 255];
1212 			if (fpix && (bpix & bgcollmask) && count++ < 128)
1213 				timer_set(pixel_time(x, y), x | (y << 8), bgcoll_irq_callback);
1214 		}
1215 	}
1216 }
1217 
1218 
1219 
1220 /*************************************
1221  *
1222  *	Standard screen refresh callback
1223  *
1224  *************************************/
1225 
VIDEO_UPDATE(victory)1226 VIDEO_UPDATE( victory )
1227 {
1228 	int bgcollmask = (video_control & 4) ? 4 : 7;
1229 	int count = 0;
1230 	int x, y;
1231 
1232 	/* update the foreground & background */
1233 	update_foreground();
1234 	update_background();
1235 
1236 	/* blend the bitmaps and do collision detection */
1237 	for (y = 0; y < 256; y++)
1238 	{
1239 		int sy = (scrolly + y) & 255;
1240 		UINT8 *fg = &fgbitmap[y * 256];
1241 		UINT8 *bg = &bgbitmap[sy * 256];
1242 		UINT8 scanline[256];
1243 
1244 		/* do the blending */
1245 		for (x = 0; x < 256; x++)
1246 		{
1247 			int fpix = *fg++;
1248 			int bpix = bg[(x + scrollx) & 255];
1249 			scanline[x] = bpix | (fpix << 3);
1250 			if (fpix && (bpix & bgcollmask) && count++ < 128)
1251 				timer_set(pixel_time(x, y), x | (y << 8), bgcoll_irq_callback);
1252 		}
1253 
1254 		/* draw the scanline */
1255 		draw_scanline8(bitmap, 0, y, 256, scanline, Machine->pens, -1);
1256 	}
1257 
1258 	/* indicate that we already did collision detection */
1259 	update_complete = 1;
1260 }
1261