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