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(µ, 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