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 µ = 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 µ = 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 µ = 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 µ = 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 µ = 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 µ = 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 µ = 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