/*************************************************************************************************************
-= IGS Lord Of Gun =-
driver by Luca Elia (l.elia@tin.it)
Note: if MAME_DEBUG is defined, pressing Z with:
Q / W / E / R Shows Layer 0 / 1 / 2 / 3
A Shows Sprites
Keys can be used together!
[ 4 Scrolling Layers ]
Tiles Layer size
8 x 8 x 6 0x800 x 0x200
8 x 8 x 6 0x200 x 0x100
16x16 x 6 0x800 x 0x200
32x32 x 6 0x800 x 0x200
[ 256 Sprites ]
Each sprite is made of N x M tiles (up to 16 x 16 tiles).
Tiles are 16 x 16 x 6
[ 2048 colors ]
[ Priorities ]
RAM based priorities, with a per tile priority code
(the same sprite goes below some parts, and above others, of the same layer)
*************************************************************************************************************/
#include "emu.h"
#include "includes/lordgun.h"
/***************************************************************************
Palette
***************************************************************************/
// xxxxBBBBGGGGRRRR, but repeat ecah color for each priority code (since we stuff it in the high bits of the pen)
WRITE16_HANDLER( lordgun_paletteram_w )
{
COMBINE_DATA(&space->machine().generic.paletteram.u16[offset]);
for (int pri = 0; pri < 8; pri++)
palette_set_color_rgb(space->machine(), offset+0x800*pri, pal4bit(data >> 0), pal4bit(data >> 4), pal4bit(data >> 8));
}
/***************************************************************************
Tilemaps
***************************************************************************/
INLINE void get_tile_info(running_machine &machine, tile_data *tileinfo, tilemap_memory_index tile_index, int _N_)
{
lordgun_state *state = machine.driver_data<lordgun_state>();
UINT16 attr = state->m_vram[_N_][tile_index * 2 + 0 ];
UINT16 code = state->m_vram[_N_][ tile_index * 2 + 1 ];
UINT16 pri = (attr & 0x0e00) >> 9;
SET_TILE_INFO( _N_, code, ((attr & 0x0030) >> 4) + 0x10 + 0x4 * ((_N_ + 1) & 3) + pri*0x800/0x40, TILE_FLIPXY(attr >> 14));
}
static TILE_GET_INFO( get_tile_info_0 ) { get_tile_info(machine, tileinfo, tile_index, 0); }
static TILE_GET_INFO( get_tile_info_1 ) { get_tile_info(machine, tileinfo, tile_index, 1); }
static TILE_GET_INFO( get_tile_info_2 ) { get_tile_info(machine, tileinfo, tile_index, 2); }
static TILE_GET_INFO( get_tile_info_3 ) { get_tile_info(machine, tileinfo, tile_index, 3); }
INLINE void lordgun_vram_w(address_space *space, offs_t offset, UINT16 data, UINT16 mem_mask, int _N_)
{
lordgun_state *state = space->machine().driver_data<lordgun_state>();
COMBINE_DATA(&state->m_vram[_N_][offset]);
tilemap_mark_tile_dirty(state->m_tilemap[_N_], offset/2);
}
WRITE16_HANDLER( lordgun_vram_0_w ) { lordgun_vram_w(space, offset, data, mem_mask, 0); }
WRITE16_HANDLER( lordgun_vram_1_w ) { lordgun_vram_w(space, offset, data, mem_mask, 1); }
WRITE16_HANDLER( lordgun_vram_2_w ) { lordgun_vram_w(space, offset, data, mem_mask, 2); }
WRITE16_HANDLER( lordgun_vram_3_w ) { lordgun_vram_w(space, offset, data, mem_mask, 3); }
/***************************************************************************
Video Init
***************************************************************************/
VIDEO_START( lordgun )
{
lordgun_state *state = machine.driver_data<lordgun_state>();
int i;
int w = machine.primary_screen->width();
int h = machine.primary_screen->height();
// 0x800 x 200
state->m_tilemap[0] = tilemap_create( machine, get_tile_info_0, tilemap_scan_rows,
8,8, 0x100, 0x40 );
// 0x800 x 200
state->m_tilemap[1] = tilemap_create( machine, get_tile_info_1, tilemap_scan_rows,
16,16, 0x80,0x20 );
// 0x800 x 200
state->m_tilemap[2] = tilemap_create( machine, get_tile_info_2, tilemap_scan_rows,
32,32, 0x40,0x10 );
// 0x200 x 100
state->m_tilemap[3] = tilemap_create( machine, get_tile_info_3, tilemap_scan_rows,
8,8, 0x40,0x20 );
tilemap_set_scroll_rows(state->m_tilemap[0],1);
tilemap_set_scroll_cols(state->m_tilemap[0],1);
tilemap_set_transparent_pen(state->m_tilemap[0],0x3f);
// Has line scroll
tilemap_set_scroll_rows(state->m_tilemap[1],0x200);
tilemap_set_scroll_cols(state->m_tilemap[1],1);
tilemap_set_transparent_pen(state->m_tilemap[1],0x3f);
tilemap_set_scroll_rows(state->m_tilemap[2],1);
tilemap_set_scroll_cols(state->m_tilemap[2],1);
tilemap_set_transparent_pen(state->m_tilemap[2],0x3f);
tilemap_set_scroll_rows(state->m_tilemap[3],1);
tilemap_set_scroll_cols(state->m_tilemap[3],1);
tilemap_set_transparent_pen(state->m_tilemap[3],0x3f);
// Buffer bitmaps for 4 tilemaps (0-3) + sprites (4)
for (i = 0; i < 5; i++)
state->m_bitmaps[i] = auto_bitmap_alloc(machine, w, h, BITMAP_FORMAT_INDEXED16);
}
/***************************************************************************
Gun screen position
***************************************************************************/
static const int lordgun_gun_x_table[] =
{
-100, 0x001,0x001,0x002,0x002,0x003,0x003,0x004,0x005,0x006,0x007,0x008,0x009,0x00A,0x00B,0x00C,
0x00D,0x00E,0x00F,0x010,0x011,0x012,0x013,0x014,0x015,0x016,0x017,0x018,0x019,0x01A,0x01B,0x01C,
0x01D,0x01E,0x01F,0x020,0x021,0x022,0x023,0x024,0x025,0x026,0x027,0x028,0x029,0x02A,0x02B,0x02C,
0x02D,0x02E,0x02F,0x030,0x031,0x032,0x033,0x034,0x035,0x036,0x037,0x038,0x039,0x03A,0x03B,0x03C,
0x03D,0x03E,0x03F,0x040,0x041,0x043,0x044,0x045,0x046,0x047,0x048,0x049,0x04A,0x04B,0x04C,0x04E,
0x04F,0x050,0x051,0x052,0x053,0x054,0x055,0x056,0x057,0x059,0x05A,0x05B,0x05C,0x05D,0x05E,0x05F,
0x060,0x061,0x05A,0x063,0x065,0x066,0x067,0x068,0x069,0x06A,0x06B,0x06C,0x06D,0x06E,0x06F,0x071,
0x072,0x074,0x075,0x077,0x078,0x07A,0x07B,0x07D,0x07E,0x080,0x081,0x083,0x085,0x087,0x089,0x08B,
0x08D,0x08E,0x08F,0x090,0x092,0x093,0x095,0x097,0x098,0x099,0x09A,0x09B,0x09C,0x09D,0x09E,0x0A0,
0x0A1,0x0A2,0x0A3,0x0A4,0x0A5,0x0A6,0x0A7,0x0A8,0x0A9,0x0AA,0x0AC,0x0AD,0x0AE,0x0AF,0x0B0,0x0B1,
0x0B2,0x0B3,0x0B4,0x0B5,0x0B6,0x0B8,0x0B9,0x0BA,0x0BB,0x0BC,0x0BD,0x0BE,0x0BF,0x0C0,0x0C1,0x0C2,
0x0C4,0x0C5,0x0C6,0x0C7,0x0C8,0x0C9,0x0CA,0x0CB,0x0CC,0x0CD,0x0CF,0x0D0,0x0D1,0x0D2,0x0D3,0x0D4,
0x0D5,0x0D6,0x0D7,0x0D8,0x0D9,0x0DB,0x0DC,0x0DD,0x0DE,0x0DF,0x0E0,0x0E1,0x0E2,0x0E3,0x0E4,0x0E5,
0x0E7,0x0E8,0x0E9,0x0EA,0x0EB,0x0EC,0x0ED,0x0EE,0x0EF,0x0F0,0x0F1,0x0F3,0x0F4,0x0F5,0x0F6,0x0F7,
0x0F8,0x0F9,0x0FA,0x0FB,0x0FC,0x0FE,0x0FF,0x100,0x101,0x102,0x103,0x104,0x105,0x106,0x107,0x108,
0x10A,0x10B,0x10C,0x10D,0x10E,0x10F,0x110,0x111,0x112,0x113,0x114,0x116,0x117,0x118,0x119,0x11A,
0x11B,0x11C,0x11D,0x11E,0x11F,0x120,0x122,0x123,0x124,0x125,0x126,0x127,0x128,0x129,0x12A,0x12B,
0x12C,0x12E,0x12F,0x130,0x131,0x132,0x133,0x134,0x135,0x136,0x137,0x139,0x13A,0x13B,0x13C,0x13D,
0x13E,0x13F,0x140,0x141,0x142,0x143,0x145,0x146,0x147,0x148,0x149,0x14A,0x14B,0x14C,0x14D,0x14E,
0x14F,0x151,0x152,0x153,0x154,0x155,0x156,0x157,0x158,0x159,0x15A,0x15B,0x15D,0x15E,0x15F,0x160,
0x161,0x162,0x163,0x164,0x165,0x166,0x167,0x169,0x16A,0x16B,0x16C,0x16D,0x16E,0x16F,0x170,0x171,
0x172,0x174,0x175,0x176,0x177,0x178,0x179,0x17A,0x17B,0x17C,0x17D,0x17E,0x17F,0x180,0x181,0x182,
0x183,0x184,0x185,0x186,0x187,0x188,0x189,0x18A,0x18B,0x18C,0x18D,0x18E,0x18F,0x190,0x191,0x192,
0x193,0x194,0x195,0x196,0x197,0x198,0x199,0x19A,0x19B,0x19C,0x19D,0x19E,0x19F,0x1A0,0x1A1,0x1A2,
0x1A3,0x1A4,0x1A5,0x1A6,0x1A7,0x1A8,0x1A9,0x1AA,0x1AB,0x1AC,0x1AD,0x1AE,0x1AF,0x1B0,0x1B1,0x1B2,
0x1B3,0x1B4,0x1B5,0x1B6,0x1B7,0x1B8,0x1B9,0x1BA,0x1BB,0x1BC,0x1BD,0x1BE,0x1BF,0x1BF
};
static const char *const gunnames[] = { "LIGHT0_X", "LIGHT1_X", "LIGHT0_Y", "LIGHT1_Y" };
float lordgun_crosshair_mapper(const input_field_config *field, float linear_value)
{
int x = linear_value - 0x3c;
if ( (x < 0) || (x > sizeof(lordgun_gun_x_table)/sizeof(lordgun_gun_x_table[0])) )
x = 0;
return lordgun_gun_x_table[x] * 1.0f / 0x1BF;
}
static void lorddgun_calc_gun_scr(running_machine &machine, int i)
{
lordgun_state *state = machine.driver_data<lordgun_state>();
// popmessage("%03x, %02x", input_port_read(machine, "LIGHT0_X"), input_port_read(machine, "LIGHT0_Y"));
int x = input_port_read(machine, gunnames[i]) - 0x3c;
if ( (x < 0) || (x > sizeof(lordgun_gun_x_table)/sizeof(lordgun_gun_x_table[0])) )
x = 0;
state->m_gun[i].scr_x = lordgun_gun_x_table[x];
state->m_gun[i].scr_y = input_port_read(machine, gunnames[i+2]);
}
void lordgun_update_gun(running_machine &machine, int i)
{
lordgun_state *state = machine.driver_data<lordgun_state>();
const rectangle &visarea = machine.primary_screen->visible_area();
state->m_gun[i].hw_x = input_port_read(machine, gunnames[i]);
state->m_gun[i].hw_y = input_port_read(machine, gunnames[i+2]);
lorddgun_calc_gun_scr(machine, i);
if ( (state->m_gun[i].scr_x < visarea.min_x) ||
(state->m_gun[i].scr_x > visarea.max_x) ||
(state->m_gun[i].scr_y < visarea.min_y) ||
(state->m_gun[i].scr_y > visarea.max_y) )
state->m_gun[i].hw_x = state->m_gun[i].hw_y = 0;
}
/***************************************************************************
Sprites
Offset: Bits: Value:
0.w fedc ---- ---- ---- Number of Y Tiles - 1
---- ba98 7654 3210 Y
2.w f--- ---- ---- ---- Flip X
-e-- ---- ---- ---- Flip Y
--dc ---- ---- ----
---- ba9- ---- ---- Priority
---- ---8 ---- ---- End of Sprite List
---- ---- 7654 ---- Color
---- ---- ---- 3210 Number of X Tiles - 1
4.w Tile Code
6.w X
***************************************************************************/
static void draw_sprites(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect)
{
lordgun_state *state = machine.driver_data<lordgun_state>();
UINT16 *s = state->m_spriteram;
UINT16 *end = state->m_spriteram + state->m_spriteram_size/2;
for ( ; s < end; s += 8/2 )
{
int attr, code, color, pri;
int sx, nx, x, x0, x1, dx, flipx;
int sy, ny, y, y0, y1, dy, flipy;
sy = s[ 0 ];
attr = s[ 1 ];
code = s[ 2 ];
sx = s[ 3 ];
// End of sprite list
if (attr & 0x0100)
break;
flipx = attr & 0x8000;
flipy = attr & 0x4000;
pri = (attr & 0x0e00) >> 9;
color = (attr & 0x00f0) >> 4;
nx = (attr & 0x000f) + 1;
ny = ((sy & 0xf000) >> 12) + 1;
if ( flipx ) { x0 = nx - 1; x1 = -1; dx = -1; }
else { x0 = 0; x1 = nx; dx = +1; }
if ( flipy ) { y0 = ny - 1; y1 = -1; dy = -1; }
else { y0 = 0; y1 = ny; dy = +1; }
// Sign extend the position
sx -= 0x18;
sy = (sy & 0x7ff) - (sy & 0x800);
for (y = y0; y != y1; y += dy)
{
for (x = x0; x != x1; x += dx)
{
drawgfx_transpen( bitmap, cliprect, machine.gfx[4],
code, color + pri * 0x800/0x40,
flipx, flipy,
sx + x * 0x10, sy + y * 0x10,
0x3f );
code += 0x10;
}
code += 1 - 0x10 * nx;
}
}
}
/***************************************************************************
Video Update
Priorities are similar to those in igs011.c
There are 4 scrolling layers, plus sprites, with a per tile priority
code (0-7).
Then there are 0x20000 bytes of priority RAM. Each word contains a
layer code (3-7), where 3 means sprites, and the rest are the tilemaps.
Actually, the layer code is repeated in both nibbles (e.g. 0x0033).
For each screen position, to determine which pixel to display, the video
chip associates a bit to the opacity of that pixel for each layer
(1 = transparent) to form an address into priority RAM.
So the bottom 5 bits of the priority RAM address depend on the layer opacities.
The higher order bits come from the priority of the pixels from each layer
(not all layers, actually, I guess to save on RAM size).
The priority RAM value at that address selects the topmost layer, that
gets sent to the screen.
***************************************************************************/
SCREEN_UPDATE( lordgun )
{
lordgun_state *state = screen->machine().driver_data<lordgun_state>();
int layers_ctrl = -1;
#ifdef MAME_DEBUG
if (screen->machine().input().code_pressed(KEYCODE_Z))
{
int msk = 0;
if (screen->machine().input().code_pressed(KEYCODE_Q)) msk |= 1;
if (screen->machine().input().code_pressed(KEYCODE_W)) msk |= 2;
if (screen->machine().input().code_pressed(KEYCODE_E)) msk |= 4;
if (screen->machine().input().code_pressed(KEYCODE_R)) msk |= 8;
if (screen->machine().input().code_pressed(KEYCODE_A)) msk |= 16;
if (msk != 0) layers_ctrl &= msk;
}
#endif
if (state->m_whitescreen)
{
bitmap_fill(bitmap, cliprect, get_white_pen(screen->machine()));
return 0;
}
// Scrolling
int x, y;
tilemap_set_scrollx( state->m_tilemap[0], 0, *state->m_scroll_x[0] );
tilemap_set_scrolly( state->m_tilemap[0], 0, *state->m_scroll_y[0] );
for (y = 0; y < 0x200; y++)
tilemap_set_scrollx( state->m_tilemap[1], y, (*state->m_scroll_x[1]) + state->m_scrollram[y * 4/2 + 2/2]);
tilemap_set_scrolly( state->m_tilemap[1], 0, *state->m_scroll_y[1] );
tilemap_set_scrollx( state->m_tilemap[2], 0, *state->m_scroll_x[2] );
tilemap_set_scrolly( state->m_tilemap[2], 0, *state->m_scroll_y[2] );
tilemap_set_scrollx( state->m_tilemap[3], 0, *state->m_scroll_x[3] );
tilemap_set_scrolly( state->m_tilemap[3], 0, *state->m_scroll_y[3] );
// Rendering:
// render each layer (0-3 tilemaps, 4 sprites) into a buffer bitmap.
// The priority code of each pixel will be stored into the high 3 bits of the pen
pen_t trans_pen = 0 * 0x800 + 0x3f; // pri = 0, pen = 3f (transparent)
int l;
for (l = 0; l < 5; l++)
bitmap_fill(state->m_bitmaps[l], cliprect, trans_pen);
if (layers_ctrl & 1) tilemap_draw(state->m_bitmaps[0], cliprect, state->m_tilemap[0], 0, 0);
if (layers_ctrl & 2) tilemap_draw(state->m_bitmaps[1], cliprect, state->m_tilemap[1], 0, 0);
if (layers_ctrl & 4) tilemap_draw(state->m_bitmaps[2], cliprect, state->m_tilemap[2], 0, 0);
if (layers_ctrl & 8) tilemap_draw(state->m_bitmaps[3], cliprect, state->m_tilemap[3], 0, 0);
if (layers_ctrl & 16) draw_sprites(screen->machine(), state->m_bitmaps[4], cliprect);
// copy to screen bitmap
// priority code (3-7) -> layer index (0-3, 4 for sprites)
const int pri2layer[8] = {0,0,0,4,3,0,1,2};
// layer index (0-3, 4 for sprites) -> priority address bit
const int layer2bit[5] = {0,1,2,4,3};
for (y = cliprect->min_y; y <= cliprect->max_y; y++)
{
for (x = cliprect->min_x; x <= cliprect->max_x; x++)
{
UINT16 pens[5];
int pri_addr = 0;
// bits 0-4: layer transparency
for (l = 0; l < 5; l++)
{
pens[l] = *BITMAP_ADDR16(state->m_bitmaps[l], y, x);
if (pens[l] == trans_pen)
pri_addr |= 1 << layer2bit[l];
}
// bits 05-07: layer 1 priority
pri_addr |= (pens[1]>>11) << 5;
// bits 08-10: sprites priority
pri_addr |= (pens[4]>>11) << 8;
// bits 11-13: layer 0 priority
pri_addr |= (pens[0]>>11) << 11;
// bit 14: layer 3 priority
pri_addr |= (pens[3]>>11) << 14;
pri_addr &= 0x7fff;
l = pri2layer[state->m_priority_ram[pri_addr] & 7];
*BITMAP_ADDR16(bitmap, y, x) = pens[l];
}
}
return 0;
}