// 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;
SET_TILE_INFO_MEMBER(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;
SET_TILE_INFO_MEMBER(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];
SET_TILE_INFO_MEMBER(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.min_y; y <= cliprect.max_y; y++)
{
const int dy = y - sy;
for (int x = cliprect.min_x; x <= cliprect.max_x; 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.min_y && (sy + y) <= cliprect.max_y)
{
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.min_y && sy <= cliprect.max_y)
for (x = 0; x < 4; x++)
{
if (sx >= cliprect.min_x && sx <= cliprect.max_x)
{
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.min_y && sy <= cliprect.max_y)
for (x = 0; x < 4; x++)
{
if (sx >= cliprect.min_x && sx <= cliprect.max_x)
{
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.min_y; y<=clip.max_y;y++)
{
int srcline = (y + scrolly) & 0x1ff;
uint16_t* src = &srcbitmap->pix16(srcline);
uint16_t* dst = &bitmap.pix16(y);
for (x=clip.min_x; x<=clip.max_x;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(FUNC(decocass_state::get_bg_l_tile_info),this), tilemap_mapper_delegate(FUNC(decocass_state::bgvideoram_scan_cols),this), 16, 16, 32, 32);
m_bg_tilemap_r = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(decocass_state::get_bg_r_tile_info),this), tilemap_mapper_delegate(FUNC(decocass_state::bgvideoram_scan_cols),this), 16, 16, 32, 32);
m_fg_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(decocass_state::get_fg_tile_info),this), tilemap_mapper_delegate(FUNC(decocass_state::fgvideoram_scan_cols),this), 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;
}