summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/video/decocass.cpp
blob: 476603d7e1030a6d8d8e86d8c79c6e2d3fd38d4a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller, David Haywood
/***********************************************************************

    DECO Cassette System video

    The video system has clearly been designed for Highway Chase,
    which unsurprisingly is the first game on the system.  The
    background 'tilemap' is very much like the road layer on the
    standalone Highway Chase with a concept of left/right edges
    with semi-independent scrolling, and the ability to transition
    between scrolling different sections.

    Additionally it supports the headlight and tunnel effects also
    needed for a Highway Chase style game. These both produce a pen
    modification effect mapped to bit 4 of the palette, unless bit
    5 of the mode register is set.

    ---

    Notes with reference to video recordings.
    These video recordings should be from real hardware because
    they predate the emulation and include undumped games, so
    unless they're from some other private / unreleased emulator
    then they can't be anything else.

    Mirrored from NicoVideo
    -=====================-

    mamedev.emulab.it/haze/reference/sm18976245-DISCO_NO1.mp4
        - no notes

    mamedev.emulab.it/haze/reference/sm18976058-ASTRO_FANTASIA.mp4
        - different game revision to emulated version, main
          boss enemy shown at the top of the scoreboard differs
          so notes below could be invalid
        - bullets should be white, not black (OK)
        - BG layer changes to orange colours for first level, but
          reverts to red when player explodes

    mamedev.emulab.it/haze/reference/sm18975592-HWY_CHASE.mp4
        - road / bg colour should be darkish blue outside of tunnels (OK)
        - road / bg colour should be black in tunnels (OK)
        - headlight should be the same darkish blue as the road
          at all times, so only visible in tunnels (OK)
        - center line of road does not exist on hw!
        - enemies are hidden in tunnels (like madalien) (OK)
        - road / bg flashs regular blue when enemy is hit revealing
          them (OK)
        - some glitchy enemies visible even over tunnel bg for
          some frames
        - colours of BG tilemap are glitchy even on hardware eg.
          Pink desert after first tunnel, Green water after 2nd
          tunnel even when the right palettes exist!
        - player bullets are yellow, enemy bullets are red (OK)

    mamedev.emulab.it/haze/reference/sm17433759-PRO_BOWLING.mp4
        - no notes

    mamedev.emulab.it/haze/reference/sm17401258-GRAPLOP.mp4
        - different game revision to emulated version, this
          seems to be a more finished version of cgralop2, the
          emulated version lacks a title screen (the parent
          cgraplop has a cluster buster title, but that is
          again different)


    mamedev.emulab.it/haze/reference/sm17387280-BURNIN_RUBBER.mp4
        - seems to be the cburnrub2 set, or close to it, plain
          white title text

    mamedev.emulab.it/haze/reference/sm17370209-NIGHT_STAR.mp4
        - no notes

    mamedev.emulab.it/haze/reference/sm17203184-HAMBURGER.mp4
        - no notes

    mamedev.emulab.it/haze/reference/sm17202585-SUPER_DOUBLE_TENNIS.mp4
        - background colours during high-score / title ar shades of
          blue, not green as in previous emulation (OK now)

    mamedev.emulab.it/haze/reference/sm17202201-SKATER.mp4
        - the game turns on the 'cross' bit, why?

    mamedev.emulab.it/haze/reference/sm17201813-ZEROIZE.mp4
        - no notes

    mamedev.emulab.it/haze/reference/sm17183561-FISHING.mp4
        - first title screen has no background, this is not a bug in
          our emulation
        - can show glitchy tiles when constructing the background,
          this is not a bug in our emulation either

    mamedev.emulab.it/haze/reference/sm17181931-MISSION_X.mp4
        - no notes

    mamedev.emulab.it/haze/reference/sm17180950-DSTELEJAN.mp4
        - no notes

    Mirrored from YouTube
    -===================-

    mamedev.emulab.it/haze/reference/manhattan01-04.webm
    mamedev.emulab.it/haze/reference/manhattan05-08.webm
    mamedev.emulab.it/haze/reference/manhattan09-12.webm
        - the BG tilemap pen ordering changes between levels, most of
          the arrangements aren't supported by our current code, and
          I can't find the writes to change it!  Level 1 in the
          video uses what we have as palette 1, not 0.
          ToDo: list combinations

    mamedev.emulab.it/haze/reference/flashboy.webm
        - game is not emulated


    ---

    I'd quite like a reference video for the Cassette Pro Soccer,
    the game appears ugly (center circle vanishing as soon as you do
    a long ball etc.) but the ugly backwards scrolling after you kick
    the ball still happens even on the standalone version, so I'm
    wondering if it's just a glitchy game.

 ***********************************************************************/

#include "emu.h"
#include "includes/decocass.h"


static const uint32_t tile_offset[32*32] = {
	0x078,0x079,0x07a,0x07b,0x07c,0x07d,0x07e,0x07f,0x0ff,0x0fe,0x0fd,0x0fc,0x0fb,0x0fa,0x0f9,0x0f8,0x278,0x279,0x27a,0x27b,0x27c,0x27d,0x27e,0x27f,0x2ff,0x2fe,0x2fd,0x2fc,0x2fb,0x2fa,0x2f9,0x2f8,
	0x070,0x071,0x072,0x073,0x074,0x075,0x076,0x077,0x0f7,0x0f6,0x0f5,0x0f4,0x0f3,0x0f2,0x0f1,0x0f0,0x270,0x271,0x272,0x273,0x274,0x275,0x276,0x277,0x2f7,0x2f6,0x2f5,0x2f4,0x2f3,0x2f2,0x2f1,0x2f0,
	0x068,0x069,0x06a,0x06b,0x06c,0x06d,0x06e,0x06f,0x0ef,0x0ee,0x0ed,0x0ec,0x0eb,0x0ea,0x0e9,0x0e8,0x268,0x269,0x26a,0x26b,0x26c,0x26d,0x26e,0x26f,0x2ef,0x2ee,0x2ed,0x2ec,0x2eb,0x2ea,0x2e9,0x2e8,
	0x060,0x061,0x062,0x063,0x064,0x065,0x066,0x067,0x0e7,0x0e6,0x0e5,0x0e4,0x0e3,0x0e2,0x0e1,0x0e0,0x260,0x261,0x262,0x263,0x264,0x265,0x266,0x267,0x2e7,0x2e6,0x2e5,0x2e4,0x2e3,0x2e2,0x2e1,0x2e0,
	0x058,0x059,0x05a,0x05b,0x05c,0x05d,0x05e,0x05f,0x0df,0x0de,0x0dd,0x0dc,0x0db,0x0da,0x0d9,0x0d8,0x258,0x259,0x25a,0x25b,0x25c,0x25d,0x25e,0x25f,0x2df,0x2de,0x2dd,0x2dc,0x2db,0x2da,0x2d9,0x2d8,
	0x050,0x051,0x052,0x053,0x054,0x055,0x056,0x057,0x0d7,0x0d6,0x0d5,0x0d4,0x0d3,0x0d2,0x0d1,0x0d0,0x250,0x251,0x252,0x253,0x254,0x255,0x256,0x257,0x2d7,0x2d6,0x2d5,0x2d4,0x2d3,0x2d2,0x2d1,0x2d0,
	0x048,0x049,0x04a,0x04b,0x04c,0x04d,0x04e,0x04f,0x0cf,0x0ce,0x0cd,0x0cc,0x0cb,0x0ca,0x0c9,0x0c8,0x248,0x249,0x24a,0x24b,0x24c,0x24d,0x24e,0x24f,0x2cf,0x2ce,0x2cd,0x2cc,0x2cb,0x2ca,0x2c9,0x2c8,
	0x040,0x041,0x042,0x043,0x044,0x045,0x046,0x047,0x0c7,0x0c6,0x0c5,0x0c4,0x0c3,0x0c2,0x0c1,0x0c0,0x240,0x241,0x242,0x243,0x244,0x245,0x246,0x247,0x2c7,0x2c6,0x2c5,0x2c4,0x2c3,0x2c2,0x2c1,0x2c0,
	0x038,0x039,0x03a,0x03b,0x03c,0x03d,0x03e,0x03f,0x0bf,0x0be,0x0bd,0x0bc,0x0bb,0x0ba,0x0b9,0x0b8,0x238,0x239,0x23a,0x23b,0x23c,0x23d,0x23e,0x23f,0x2bf,0x2be,0x2bd,0x2bc,0x2bb,0x2ba,0x2b9,0x2b8,
	0x030,0x031,0x032,0x033,0x034,0x035,0x036,0x037,0x0b7,0x0b6,0x0b5,0x0b4,0x0b3,0x0b2,0x0b1,0x0b0,0x230,0x231,0x232,0x233,0x234,0x235,0x236,0x237,0x2b7,0x2b6,0x2b5,0x2b4,0x2b3,0x2b2,0x2b1,0x2b0,
	0x028,0x029,0x02a,0x02b,0x02c,0x02d,0x02e,0x02f,0x0af,0x0ae,0x0ad,0x0ac,0x0ab,0x0aa,0x0a9,0x0a8,0x228,0x229,0x22a,0x22b,0x22c,0x22d,0x22e,0x22f,0x2af,0x2ae,0x2ad,0x2ac,0x2ab,0x2aa,0x2a9,0x2a8,
	0x020,0x021,0x022,0x023,0x024,0x025,0x026,0x027,0x0a7,0x0a6,0x0a5,0x0a4,0x0a3,0x0a2,0x0a1,0x0a0,0x220,0x221,0x222,0x223,0x224,0x225,0x226,0x227,0x2a7,0x2a6,0x2a5,0x2a4,0x2a3,0x2a2,0x2a1,0x2a0,
	0x018,0x019,0x01a,0x01b,0x01c,0x01d,0x01e,0x01f,0x09f,0x09e,0x09d,0x09c,0x09b,0x09a,0x099,0x098,0x218,0x219,0x21a,0x21b,0x21c,0x21d,0x21e,0x21f,0x29f,0x29e,0x29d,0x29c,0x29b,0x29a,0x299,0x298,
	0x010,0x011,0x012,0x013,0x014,0x015,0x016,0x017,0x097,0x096,0x095,0x094,0x093,0x092,0x091,0x090,0x210,0x211,0x212,0x213,0x214,0x215,0x216,0x217,0x297,0x296,0x295,0x294,0x293,0x292,0x291,0x290,
	0x008,0x009,0x00a,0x00b,0x00c,0x00d,0x00e,0x00f,0x08f,0x08e,0x08d,0x08c,0x08b,0x08a,0x089,0x088,0x208,0x209,0x20a,0x20b,0x20c,0x20d,0x20e,0x20f,0x28f,0x28e,0x28d,0x28c,0x28b,0x28a,0x289,0x288,
	0x000,0x001,0x002,0x003,0x004,0x005,0x006,0x007,0x087,0x086,0x085,0x084,0x083,0x082,0x081,0x080,0x200,0x201,0x202,0x203,0x204,0x205,0x206,0x207,0x287,0x286,0x285,0x284,0x283,0x282,0x281,0x280,
	0x178,0x179,0x17a,0x17b,0x17c,0x17d,0x17e,0x17f,0x1ff,0x1fe,0x1fd,0x1fc,0x1fb,0x1fa,0x1f9,0x1f8,0x378,0x379,0x37a,0x37b,0x37c,0x37d,0x37e,0x37f,0x3ff,0x3fe,0x3fd,0x3fc,0x3fb,0x3fa,0x3f9,0x3f8,
	0x170,0x171,0x172,0x173,0x174,0x175,0x176,0x177,0x1f7,0x1f6,0x1f5,0x1f4,0x1f3,0x1f2,0x1f1,0x1f0,0x370,0x371,0x372,0x373,0x374,0x375,0x376,0x377,0x3f7,0x3f6,0x3f5,0x3f4,0x3f3,0x3f2,0x3f1,0x3f0,
	0x168,0x169,0x16a,0x16b,0x16c,0x16d,0x16e,0x16f,0x1ef,0x1ee,0x1ed,0x1ec,0x1eb,0x1ea,0x1e9,0x1e8,0x368,0x369,0x36a,0x36b,0x36c,0x36d,0x36e,0x36f,0x3ef,0x3ee,0x3ed,0x3ec,0x3eb,0x3ea,0x3e9,0x3e8,
	0x160,0x161,0x162,0x163,0x164,0x165,0x166,0x167,0x1e7,0x1e6,0x1e5,0x1e4,0x1e3,0x1e2,0x1e1,0x1e0,0x360,0x361,0x362,0x363,0x364,0x365,0x366,0x367,0x3e7,0x3e6,0x3e5,0x3e4,0x3e3,0x3e2,0x3e1,0x3e0,
	0x158,0x159,0x15a,0x15b,0x15c,0x15d,0x15e,0x15f,0x1df,0x1de,0x1dd,0x1dc,0x1db,0x1da,0x1d9,0x1d8,0x358,0x359,0x35a,0x35b,0x35c,0x35d,0x35e,0x35f,0x3df,0x3de,0x3dd,0x3dc,0x3db,0x3da,0x3d9,0x3d8,
	0x150,0x151,0x152,0x153,0x154,0x155,0x156,0x157,0x1d7,0x1d6,0x1d5,0x1d4,0x1d3,0x1d2,0x1d1,0x1d0,0x350,0x351,0x352,0x353,0x354,0x355,0x356,0x357,0x3d7,0x3d6,0x3d5,0x3d4,0x3d3,0x3d2,0x3d1,0x3d0,
	0x148,0x149,0x14a,0x14b,0x14c,0x14d,0x14e,0x14f,0x1cf,0x1ce,0x1cd,0x1cc,0x1cb,0x1ca,0x1c9,0x1c8,0x348,0x349,0x34a,0x34b,0x34c,0x34d,0x34e,0x34f,0x3cf,0x3ce,0x3cd,0x3cc,0x3cb,0x3ca,0x3c9,0x3c8,
	0x140,0x141,0x142,0x143,0x144,0x145,0x146,0x147,0x1c7,0x1c6,0x1c5,0x1c4,0x1c3,0x1c2,0x1c1,0x1c0,0x340,0x341,0x342,0x343,0x344,0x345,0x346,0x347,0x3c7,0x3c6,0x3c5,0x3c4,0x3c3,0x3c2,0x3c1,0x3c0,
	0x138,0x139,0x13a,0x13b,0x13c,0x13d,0x13e,0x13f,0x1bf,0x1be,0x1bd,0x1bc,0x1bb,0x1ba,0x1b9,0x1b8,0x338,0x339,0x33a,0x33b,0x33c,0x33d,0x33e,0x33f,0x3bf,0x3be,0x3bd,0x3bc,0x3bb,0x3ba,0x3b9,0x3b8,
	0x130,0x131,0x132,0x133,0x134,0x135,0x136,0x137,0x1b7,0x1b6,0x1b5,0x1b4,0x1b3,0x1b2,0x1b1,0x1b0,0x330,0x331,0x332,0x333,0x334,0x335,0x336,0x337,0x3b7,0x3b6,0x3b5,0x3b4,0x3b3,0x3b2,0x3b1,0x3b0,
	0x128,0x129,0x12a,0x12b,0x12c,0x12d,0x12e,0x12f,0x1af,0x1ae,0x1ad,0x1ac,0x1ab,0x1aa,0x1a9,0x1a8,0x328,0x329,0x32a,0x32b,0x32c,0x32d,0x32e,0x32f,0x3af,0x3ae,0x3ad,0x3ac,0x3ab,0x3aa,0x3a9,0x3a8,
	0x120,0x121,0x122,0x123,0x124,0x125,0x126,0x127,0x1a7,0x1a6,0x1a5,0x1a4,0x1a3,0x1a2,0x1a1,0x1a0,0x320,0x321,0x322,0x323,0x324,0x325,0x326,0x327,0x3a7,0x3a6,0x3a5,0x3a4,0x3a3,0x3a2,0x3a1,0x3a0,
	0x118,0x119,0x11a,0x11b,0x11c,0x11d,0x11e,0x11f,0x19f,0x19e,0x19d,0x19c,0x19b,0x19a,0x199,0x198,0x318,0x319,0x31a,0x31b,0x31c,0x31d,0x31e,0x31f,0x39f,0x39e,0x39d,0x39c,0x39b,0x39a,0x399,0x398,
	0x110,0x111,0x112,0x113,0x114,0x115,0x116,0x117,0x197,0x196,0x195,0x194,0x193,0x192,0x191,0x190,0x310,0x311,0x312,0x313,0x314,0x315,0x316,0x317,0x397,0x396,0x395,0x394,0x393,0x392,0x391,0x390,
	0x108,0x109,0x10a,0x10b,0x10c,0x10d,0x10e,0x10f,0x18f,0x18e,0x18d,0x18c,0x18b,0x18a,0x189,0x188,0x308,0x309,0x30a,0x30b,0x30c,0x30d,0x30e,0x30f,0x38f,0x38e,0x38d,0x38c,0x38b,0x38a,0x389,0x388,
	0x100,0x101,0x102,0x103,0x104,0x105,0x106,0x107,0x187,0x186,0x185,0x184,0x183,0x182,0x181,0x180,0x300,0x301,0x302,0x303,0x304,0x305,0x306,0x307,0x387,0x386,0x385,0x384,0x383,0x382,0x381,0x380
};

/********************************************
    state saving setup
 ********************************************/
void decocass_state::decocass_video_state_save_init()
{
	save_item(NAME(m_watchdog_count));
	save_item(NAME(m_watchdog_flip));
	save_item(NAME(m_color_missiles));
	save_item(NAME(m_color_center_bot));
	save_item(NAME(m_mode_set));
	save_item(NAME(m_back_h_shift));
	save_item(NAME(m_back_vl_shift));
	save_item(NAME(m_back_vr_shift));
	save_item(NAME(m_part_h_shift));
	save_item(NAME(m_part_v_shift));
	save_item(NAME(m_center_h_shift_space));
	save_item(NAME(m_center_v_shift));
}

/********************************************
    tilemap callbacks
 ********************************************/

TILEMAP_MAPPER_MEMBER(decocass_state::fgvideoram_scan_cols )
{
	/* logical (col,row) -> memory offset */
	return (num_cols - 1 - col) * num_rows + row;
}

TILEMAP_MAPPER_MEMBER(decocass_state::bgvideoram_scan_cols )
{
	/* logical (col,row) -> memory offset */
	return tile_offset[col * num_rows + row];
}

TILE_GET_INFO_MEMBER(decocass_state::get_bg_l_tile_info)
{
	int color = (m_color_center_bot >> 7) & 1;
	tileinfo.set(2,
			m_bgvideoram[tile_index] >> 4,
			color * 4 + 1,
			0);
	if (tile_index & 0x80)
		tileinfo.pen_data = m_empty_tile;
}

TILE_GET_INFO_MEMBER(decocass_state::get_bg_r_tile_info )
{
	int color = (m_color_center_bot >> 7) & 1;
	tileinfo.set(2,
			m_bgvideoram[tile_index] >> 4,
			color * 4 + 1,
			TILE_FLIPY);
	if (!(tile_index & 0x80))
		tileinfo.pen_data = m_empty_tile;
}

TILE_GET_INFO_MEMBER(decocass_state::get_fg_tile_info )
{
	uint8_t code = m_fgvideoram[tile_index];
	uint8_t attr = m_colorram[tile_index];
	tileinfo.set(0,
			256 * (attr & 3) + code,
			BIT(m_color_center_bot, 0),
			0);
}

/********************************************
    big object
 ********************************************/

void decocass_state::draw_special_priority(bitmap_ind16 &bitmap, bitmap_ind8 &priority, const rectangle &cliprect)
{
	int crossing = m_mode_set & 3;

	// in daylight or during explosion
	if ((crossing == 0 || BIT(m_mode_set, 6)) && !BIT(m_mode_set, 5))
		return;

	int color = (bitswap<8>(m_color_center_bot, 0, 1, 7, 2, 3, 4, 5, 6) & 0x27) | 0x08;

	int sy = 64 - m_part_v_shift + 1;
	if (sy < 0)
		sy += 256;
	int sx = m_part_h_shift - 128;

	const uint8_t *objdata0 = m_gfxdecode->gfx(3)->get_data(0);
	const uint8_t *objdata1 = m_gfxdecode->gfx(3)->get_data(1);
	assert(m_gfxdecode->gfx(3)->rowbytes() == 64);

	for (int y = cliprect.top(); y <= cliprect.bottom(); y++)
	{
		const int dy = y - sy;
		for (int x = cliprect.left(); x <= cliprect.right(); x++)
		{
			const int dx = x - sx;

			bool pri2 = false;
			int scroll = m_back_h_shift;

			switch (crossing)
			{
				case 0: pri2 = true; break; // outside tunnel (for reference; not usually handled in this loop)
				case 1: pri2 = (x >= scroll); break; // exiting tunnel
				case 2: break; // inside tunnel
				case 3: pri2 = (x < scroll); break; // entering tunnel
			}

			if (BIT(m_mode_set, 7))
			{
				// check coordinates against object data
				if ((dy >= -64 && dy < 0 && dx >= 64 && dx < 128 && objdata0[(-1 - dy) * 64 + dx - 64] != 0) ||
					(dy >= 0 && dy < 64 && dx >= 64 && dx < 128 && objdata0[dy * 64 + dx - 64] != 0) ||
					(dy >= -64 && dy < 0 && dx >= 0 && dx < 64 && objdata1[(-1 - dy) * 64 + dx] != 0) ||
					(dy >= 0 && dy < 64 && dx >= 0 && dx < 64 && objdata1[dy * 64 + dx] != 0))
				{
					pri2 = true;
					if (BIT(m_mode_set, 5) && priority.pix8(y, x) == 0) // least priority?
						bitmap.pix16(y, x) = color;
				}
			}

			if (!pri2)
				bitmap.pix16(y, x) |= 0x10;
		}
	}
}

void decocass_state::draw_center(bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	int sx, sy, x, y, color;

	color = 0;
	if (m_color_center_bot & 0x10)
		color |= 4;
	if (m_color_center_bot & 0x20)
		color |= 2;
	if (m_color_center_bot & 0x40)
		color |= 1;
	if (m_color_center_bot & 0x80)
		color = (color & 4) + ((color << 1) & 2) + ((color >> 1) & 1);

	sy = m_center_v_shift;
	sx = (m_center_h_shift_space >> 2) & 0x3c;

	for (y = 0; y < 4; y++)
		if ((sy + y) >= cliprect.top() && (sy + y) <= cliprect.bottom())
		{
			if (((sy + y) & m_color_center_bot & 3) == (sy & m_color_center_bot & 3))
				for (x = 0; x < 256; x++)
					if (0 != (x & 16) || 0 != (m_center_h_shift_space & 1))
						bitmap.pix16(sy + y, (sx + x) & 255) = color;
		}
}

/********************************************
    memory handlers
 ********************************************/

WRITE8_MEMBER(decocass_state::decocass_paletteram_w )
{
	/*
	 * RGB output is inverted and A4 is inverted too
	 * (ME/ input on 1st paletteram, inverter -> ME/ on 2nd)
	 */
	m_paletteram[offset] = data;

	offset = (offset & 31) ^ 16;
	m_palette->set_indirect_color(offset, rgb_t(pal3bit(~data >> 0), pal3bit(~data >> 3), pal2bit(~data >> 6)));
}

WRITE8_MEMBER(decocass_state::decocass_charram_w )
{
	m_charram[offset] = data;
	/* dirty sprite */
	m_gfxdecode->gfx(1)->mark_dirty((offset >> 5) & 255);
	/* dirty char */
	m_gfxdecode->gfx(0)->mark_dirty((offset >> 3) & 1023);
}


WRITE8_MEMBER(decocass_state::decocass_fgvideoram_w )
{
	m_fgvideoram[offset] = data;
	m_fg_tilemap->mark_tile_dirty(offset);
}

WRITE8_MEMBER(decocass_state::decocass_colorram_w )
{
	m_colorram[offset] = data;
	m_fg_tilemap->mark_tile_dirty(offset);
}

void decocass_state::mark_bg_tile_dirty(offs_t offset )
{
	m_bg_tilemap_r->mark_tile_dirty(offset);
	m_bg_tilemap_l->mark_tile_dirty(offset);
}

WRITE8_MEMBER(decocass_state::decocass_tileram_w )
{
	m_tileram[offset] = data;
	/* dirty tile (64 bytes per tile) */
	m_gfxdecode->gfx(2)->mark_dirty((offset / 64) & 15);
	/* first 1KB of tile RAM is shared with tilemap RAM */
	if (offset < m_bgvideoram_size)
		mark_bg_tile_dirty(offset);
}

WRITE8_MEMBER(decocass_state::decocass_objectram_w )
{
	m_objectram[offset] = data;
	/* dirty the object */
	m_gfxdecode->gfx(3)->mark_dirty(0);
	m_gfxdecode->gfx(3)->mark_dirty(1);
}

WRITE8_MEMBER(decocass_state::decocass_bgvideoram_w )
{
	m_bgvideoram[offset] = data;
	mark_bg_tile_dirty(offset);
}

/* The watchdog is a 4bit counter counting down every frame */
WRITE8_MEMBER(decocass_state::decocass_watchdog_count_w )
{
	LOG(1,("decocass_watchdog_count_w: $%02x\n", data));
	m_watchdog_count = data & 0x0f;
}

WRITE8_MEMBER(decocass_state::decocass_watchdog_flip_w )
{
	LOG(1,("decocass_watchdog_flip_w: $%02x\n", data));
	m_watchdog_flip = data;
}

WRITE8_MEMBER(decocass_state::decocass_color_missiles_w )
{
	LOG(1,("decocass_color_missiles_w: $%02x\n", data));
	/* only bits D0-D2 and D4-D6 are connected to
	 * the color RAM demux:
	 * D0-D2 to the IC0 inputs
	 * D4-D6 to the IC1 inputs
	 */
	m_color_missiles = data & 0x77;
}

/*
 * D0 - bg paging
 * D1 - bg paging
 * D2 - ptn 1/2
 * D3 - BKG ena
 * D4 - center L on
 * D5 - cross on
 * D6 - tunnel
 * D7 - part h enable
 */
WRITE8_MEMBER(decocass_state::decocass_mode_set_w )
{
	if (data == m_mode_set)
		return;
	LOG(1,("decocass_mode_set_w: $%02x (%s%s%s%s%s%s%s%s)\n", data,
		(data & 0x01) ? "D0?" : "",
		(data & 0x02) ? " D1?" : "",
		(data & 0x04) ? " ptn1/2" : "",
		(data & 0x08) ? " bkg_ena" : "",
		(data & 0x10) ? " center_l_on" : "",
		(data & 0x20) ? " cross_on" : "",  /* skater enables this, why? */
		(data & 0x40) ? " tunnel" : "",
		(data & 0x80) ? " part_h_enable" : ""));

	m_mode_set = data;
}

WRITE8_MEMBER(decocass_state::decocass_color_center_bot_w )
{
	if (data == m_color_center_bot)
		return;
	LOG(1,("decocass_color_center_bot_w: $%02x (color:%d, center_bot:%d)\n", data, data & 3, data >> 4));
	/*
	 * D7   CL3/4 (swap 2+4)
	 * D6   CL1
	 * D5   CL2
	 * D4   CL4
	 * D3   nc
	 * D2   nc
	 * D1   CLD4
	 * D0   CLD3
	 */

	if ((m_color_center_bot ^ data) & 0x80)
	{
		m_bg_tilemap_r->mark_all_dirty();
		m_bg_tilemap_l->mark_all_dirty();
	}
	if ((m_color_center_bot ^ data) & 0x01)
		m_fg_tilemap->mark_all_dirty();
	m_color_center_bot = data;
}

WRITE8_MEMBER(decocass_state::decocass_back_h_shift_w )
{
	if (data == m_back_h_shift)
		return;
	LOG(1,("decocass_back_h_shift_w: $%02x\n", data));
	m_back_h_shift = data;
}

WRITE8_MEMBER(decocass_state::decocass_back_vl_shift_w )
{
	if (data == m_back_vl_shift)
		return;
	LOG(1,("decocass_back_vl_shift_w: $%02x\n", data));
	m_back_vl_shift = data;
}

WRITE8_MEMBER(decocass_state::decocass_back_vr_shift_w )
{
	if (data == m_back_vr_shift)
		return;
	LOG(1,("decocass_back_vr_shift_w: $%02x\n", data));
	m_back_vr_shift = data;
}

WRITE8_MEMBER(decocass_state::decocass_part_h_shift_w )
{
	if (data == m_part_h_shift )
		return;
	LOG(1,("decocass_part_h_shift_w: $%02x\n", data));
	m_part_h_shift = data;
}

WRITE8_MEMBER(decocass_state::decocass_part_v_shift_w )
{
	if (data == m_part_v_shift )
		return;
	LOG(1,("decocass_part_v_shift_w: $%02x\n", data));
	m_part_v_shift = data;
}

WRITE8_MEMBER(decocass_state::decocass_center_h_shift_space_w )
{
	if (data == m_center_h_shift_space)
		return;
	LOG(1,("decocass_center_h_shift_space_w: $%02x\n", data));
	m_center_h_shift_space = data;
}

WRITE8_MEMBER(decocass_state::decocass_center_v_shift_w )
{
	LOG(1,("decocass_center_v_shift_w: $%02x\n", data));
	m_center_v_shift = data;
}

/********************************************
    memory handlers
 ********************************************/

void decocass_state::draw_sprites(bitmap_ind16 &bitmap, bitmap_ind8 &priority, const rectangle &cliprect, int color,
						int sprite_y_adjust, int sprite_y_adjust_flip_screen,
						uint8_t *sprite_ram, int interleave)
{
	/* Draw the sprites */
	int offs = 28 * interleave;
	for (int i = 0; i < 8; i++, offs -= 4 * interleave)
	{
		int sx, sy, flipx, flipy;

		if (!(sprite_ram[offs + 0] & 0x01))
			continue;

		sx = 240 - sprite_ram[offs + 3 * interleave];
		sy = 240 - sprite_ram[offs + 2 * interleave];

		flipx = sprite_ram[offs + 0] & 0x04;
		flipy = sprite_ram[offs + 0] & 0x02;

		if (flip_screen())
		{
			sx = 240 - sx;
			sy = 240 - sy + sprite_y_adjust_flip_screen;

			flipx = !flipx;
			flipy = !flipy;
		}

		sy -= sprite_y_adjust;

		m_gfxdecode->gfx(1)->prio_transpen(bitmap,cliprect,
				sprite_ram[offs + interleave],
				color,
				flipx,flipy,
				sx,sy, priority, 1 << 1, 0);

		sy += (flip_screen() ? -256 : 256);

		// Wrap around
		m_gfxdecode->gfx(1)->prio_transpen(bitmap,cliprect,
				sprite_ram[offs + interleave],
				color,
				flipx,flipy,
				sx,sy, priority, 1 << 1, 0);
	}
}


void decocass_state::draw_missiles(bitmap_ind16 &bitmap, bitmap_ind8 &priority, const rectangle &cliprect,
						int missile_y_adjust, int missile_y_adjust_flip_screen,
						uint8_t *missile_ram, int interleave)
{
	int i, offs, x;

	/* Draw the missiles (16 of them) seemingly with alternating colors
	 * from the E302 latch (color_missiles) */
	for (i = 0, offs = 0; i < 8; i++, offs += 4 * interleave)
	{
		int sx, sy;

		sy = 255 - missile_ram[offs + 0 * interleave];
		sx = 255 - missile_ram[offs + 2 * interleave];
		if (flip_screen())
		{
			sx = 240 - sx;
			sy = 240 - sy + missile_y_adjust_flip_screen;
		}
		sy -= missile_y_adjust;
		if (sy >= cliprect.top() && sy <= cliprect.bottom())
			for (x = 0; x < 4; x++)
			{
				if (sx >= cliprect.left() && sx <= cliprect.right())
				{
					bitmap.pix16(sy, sx) = (m_color_missiles & 7) | 8;
					priority.pix8(sy, sx) |= 1 << 2;
				}
				sx++;
			}

		sy = 255 - missile_ram[offs + 1 * interleave];
		sx = 255 - missile_ram[offs + 3 * interleave];
		if (flip_screen())
		{
			sx = 240 - sx;
			sy = 240 - sy + missile_y_adjust_flip_screen;
		}
		sy -= missile_y_adjust;
		if (sy >= cliprect.top() && sy <= cliprect.bottom())
			for (x = 0; x < 4; x++)
			{
				if (sx >= cliprect.left() && sx <= cliprect.right())
				{
					bitmap.pix16(sy, sx) = ((m_color_missiles >> 4) & 7) | 8;
					priority.pix8(sy, sx) |= 1 << 3;
				}
				sx++;
			}
	}
}

// we could still massively clean up the background tilemap handling, I have a feeling the
// code to ignore tiles with 0x80 set on the left tilemap etc. is just a convenient hack
// to stop garbage tiles appearing when scrolling due to the weird ram layout and clipping rules

// 0 is left edge, 1 is right edge
void decocass_state::draw_edge(bitmap_ind16 &bitmap, const rectangle &cliprect, int which, bool opaque)
{
	rectangle clip;
	bitmap_ind16* srcbitmap;

	int scrolly_l = m_back_vl_shift;
	int scrolly_r = 256 - m_back_vr_shift;

	// bit 0x04 of the mode select effectively selects between two banks of data
	if (0 == (m_mode_set & 0x04))
		scrolly_r += 256;
	else
		scrolly_l += 256;

	int scrollx = 256 - m_back_h_shift;
	int scrolly;

	if (which==0)
	{
		clip = m_bg_tilemap_l_clip;
		clip &= cliprect;
		scrolly = scrolly_l;
		srcbitmap = &m_bg_tilemap_l->pixmap();
	}
	else
	{
		clip = m_bg_tilemap_r_clip;
		clip &= cliprect;
		scrolly = scrolly_r;
		srcbitmap = &m_bg_tilemap_r->pixmap();
	}

	int y,x;

//  printf("m_mode_set %d\n", m_mode_set & 0x3);

	// technically our y drawing probably shouldn't wrap / mask, but simply draw the 128pixel high 'edge' at the requested position
	//  see note above this funciton
	for (y=clip.top(); y<=clip.bottom(); y++)
	{
		int srcline = (y + scrolly) & 0x1ff;
		uint16_t* src = &srcbitmap->pix16(srcline);
		uint16_t* dst = &bitmap.pix16(y);

		for (x=clip.left(); x<=clip.right(); x++)
		{
			int srccol = 0;

			// 2 bits control the x scroll mode, allowing it to wrap either half of the tilemap, or transition one way or the other between the two halves

			switch (m_mode_set & 3)
			{
				case 0x00: srccol = ((x + scrollx) & 0xff); break; // hwy normal case
				case 0x01: srccol = (x + scrollx + 0x100) & 0x1ff; break; // manhattan building top
				case 0x02: srccol = ((x + scrollx) & 0xff) + 0x100; break; // manhattan normal case
				case 0x03: srccol = (x + scrollx) & 0x1ff; break; // hwy, burnrub etc.
			}

			uint16_t pix = src[srccol];

			if ((pix & 0x3) || opaque)
				dst[x] = pix;
		}
	}

}


void decocass_state::video_start()
{
	m_bg_tilemap_l = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(decocass_state::get_bg_l_tile_info)), tilemap_mapper_delegate(*this, FUNC(decocass_state::bgvideoram_scan_cols)), 16, 16, 32, 32);
	m_bg_tilemap_r = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(decocass_state::get_bg_r_tile_info)), tilemap_mapper_delegate(*this, FUNC(decocass_state::bgvideoram_scan_cols)), 16, 16, 32, 32);
	m_fg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(decocass_state::get_fg_tile_info)), tilemap_mapper_delegate(*this, FUNC(decocass_state::fgvideoram_scan_cols)), 8, 8, 32, 32);

	m_bg_tilemap_l->set_transparent_pen(0);
	m_bg_tilemap_r->set_transparent_pen(0);
	m_fg_tilemap->set_transparent_pen(0);

	m_bg_tilemap_l_clip = m_screen->visible_area();
	m_bg_tilemap_l_clip.max_y = 256 / 2 - 1;

	m_bg_tilemap_r_clip = m_screen->visible_area();
	m_bg_tilemap_r_clip.min_y = 256 / 2;

	/* background videoram bits D0-D3 are shared with the tileram */
	m_bgvideoram = m_tileram;
	m_bgvideoram_size = 0x0400; /* d000-d3ff */

	m_gfxdecode->gfx(0)->set_source(m_charram);
	m_gfxdecode->gfx(1)->set_source(m_charram);
	m_gfxdecode->gfx(2)->set_source(m_tileram);
	m_gfxdecode->gfx(3)->set_source(m_objectram);

	/* create an empty tile */
	memset(m_empty_tile, 0, sizeof(m_empty_tile));
}

uint32_t decocass_state::screen_update_decocass(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	/* THIS CODE SHOULD NOT BE IN SCREEN UPDATE !! */

	if (0xc0 != (ioport("IN2")->read() & 0xc0))  /* coin slots assert an NMI */
		m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);

	if (0 == (m_watchdog_flip & 0x04))
		m_watchdog->watchdog_reset();
	else if (m_watchdog_count-- > 0)
		m_watchdog->watchdog_reset();

	/* (end) THIS CODE SHOULD NOT BE IN SCREEN UPDATE !! */


#ifdef MAME_DEBUG
	{
		if (machine().input().code_pressed_once(KEYCODE_I))
			m_showmsg ^= 1;
		if (m_showmsg)
			popmessage("mode:$%02x cm:$%02x ccb:$%02x h:$%02x vl:$%02x vr:$%02x ph:$%02x pv:$%02x ch:$%02x cv:$%02x",
				m_mode_set,
				m_color_missiles,
				m_color_center_bot,
				m_back_h_shift,
				m_back_vl_shift,
				m_back_vr_shift,
				m_part_h_shift,
				m_part_v_shift,
				m_center_h_shift_space,
				m_center_v_shift);
	}
#endif

	bitmap_ind8 &priority = screen.priority();
	bitmap.fill(8, cliprect);
	priority.fill(0, cliprect);

	if (m_mode_set & 0x08)  /* bkg_ena on ? */
	{
		draw_edge(bitmap, cliprect, 0, true);
		draw_edge(bitmap, cliprect, 1, true);
	}

	if (m_mode_set & 0x20)
	{
		draw_center(bitmap, cliprect);
	}
	else
	{
		draw_center(bitmap, cliprect);
		if (m_mode_set & 0x08)  /* bkg_ena on ? */
		{
			draw_edge(bitmap, cliprect, 0, false);
			draw_edge(bitmap, cliprect, 1, false);
		}
	}

	// LS148 @ 2B (DSP-8 board) sets pen priority

	// priority 0: foreground - should be drawn last in some cases? what breaks with it here?
	// DSP-8 schematics indicate that this should be priority 4, but that breaks Tornado and Pro Tennis and Mission X (priority vs boats)
	m_fg_tilemap->draw(screen, bitmap, cliprect, 0, 0);

	// priority 1: sprites
	draw_sprites(bitmap, priority, cliprect, (m_color_center_bot >> 1) & 1, 0, 0, m_fgvideoram, 0x20);

	// priority 2 & 3: missiles
	draw_missiles(bitmap, priority, cliprect, 1, 0, m_colorram, 0x20);

	// PRI1 & PRI2: special handling for 1bpp object and tunnel sections
	draw_special_priority(bitmap, priority, cliprect);

	return 0;
}