
/*
 *    Yamaha YGV608 - PVDC2 Pattern mode Video Display Controller 2
 *    - Mark McDougall
 *
 *    Notes:
 *    ======
 *
 *    This implementation is far from complete.
 *    There's enough here to emulate Namco ND-1 games.
 *    Some functionality is missing, some is incomplete.
 *    Also missing for ND-1 is rotation and scaling (cosmetic only).
 *
 *    It could also do with some optimisation for speed!
 *
 *    (Still lots of debugging info/options in here!)
 *
 *    TODO: soon
 *    create tilemaps at vh_start time and switch between at runtime
 *    mark tiles dirty when VRAM is written to instead of dirtying entire
 *       screen each frame
 *
 *    T.B.D. (not critical to ND-1)
 *    ======
 *
 *    Rotation
 *    Scaling
 *    Split-screen scrolling by row (by column supported) (see test mode)
 *    Everything else! :)
 */

#include "emu.h"
#include "video/ygv608.h"

#define _ENABLE_SPRITES
#define _ENABLE_SCROLLX
#define _ENABLE_SCROLLY
//#define _ENABLE_SCREEN_RESIZE
//#define _ENABLE_ROTATE_ZOOM
//#define _SHOW_VIDEO_DEBUG

#define GFX_8X8_4BIT    0
#define GFX_16X16_4BIT  1
#define GFX_32X32_4BIT  2
#define GFX_64X64_4BIT  3
#define GFX_8X8_8BIT    4
#define GFX_16X16_8BIT  5

static UINT8 namcond1_gfxbank;
static YGV608 ygv608;

static tilemap_t *tilemap_A_cache_8[3];
static tilemap_t *tilemap_A_cache_16[3];
static tilemap_t *tilemap_B_cache_8[3];
static tilemap_t *tilemap_B_cache_16[3];
static tilemap_t *tilemap_A = NULL;
static tilemap_t *tilemap_B = NULL;
static bitmap_t *work_bitmap = NULL;

static void HandleYGV608Reset( running_machine &machine );
static void HandleRomTransfers( running_machine &machine );
static void SetPreShortcuts( int reg, int data );
static void SetPostShortcuts( running_machine &machine, int reg );

#ifdef MAME_DEBUG
static void ShowYGV608Registers( void );
#endif

void ygv608_set_gfxbank(UINT8 gfxbank)
{
	namcond1_gfxbank = gfxbank;
}

/* interrupt generated every 1ms second */
INTERRUPT_GEN( ygv608_timed_interrupt )
{
/*
    this is not quite generic, because we trigger a 68k interrupt
    - if this chip is ever used by another driver, we should make
      this more generic - or move it into the machine driver
*/

	static int timer = 0;

	if( ++timer == 1000 )
		timer = 0;

    /* once every 60Hz, set the vertical border interval start flag */
	if( ( timer % (1000/60) ) == 0 )
	{
		ygv608.ports.s.p6 |= p6_fv;
		if (ygv608.regs.s.r14 & r14_iev)
			irq2_line_hold(device);
	}

	/* once every 60Hz, set the position detection flag (somewhere) */
	else if( ( timer % (1000/60) ) == 7 )
	{
		ygv608.ports.s.p6 |= p6_fp;
		if (ygv608.regs.s.r14 & r14_iep)
			irq2_line_hold(device);
	}
}


static TILEMAP_MAPPER( get_tile_offset )
{
    // this optimisation is not much good to us,
    // since we really need row,col in the get_tile_info() routines
    // - so just pack them into a UINT32

	return( ( col << 6 ) | row );
}

#define layout_total(x) \
(machine.config().m_gfxdecodeinfo[x].gfxlayout->total)

static TILE_GET_INFO( get_tile_info_A_8 )
{
	// extract row,col packed into tile_index
	int             col = tile_index >> 6;
	int             row = tile_index & 0x3f;

	UINT8   attr = 0;
	int             pattern_name_base = 0;
	int             set = ((ygv608.regs.s.r7 & r7_md) == MD_1PLANE_256COLOUR
			        	? GFX_8X8_8BIT : GFX_8X8_4BIT );
	int             base = row >> ygv608.base_y_shift;

	if( col >= ygv608.page_x )
	{
		SET_TILE_INFO( set, 0, 0, 0 );
	}
	else if( row >= ygv608.page_y )
	{
		SET_TILE_INFO( set, 0, 0, 0 );
	}
	else
	{
		int sx, sy, page;
		int i = pattern_name_base + (((row << ygv608.pny_shift) + col) << ygv608.bits16);
		int j = ygv608.pattern_name_table[i];
        int f = 0;

		if( ygv608.bits16 )
		{
			j += ((int)(ygv608.pattern_name_table[i+1] & ygv608.na8_mask )) << 8;
			// attribute only valid in 16 color mode
			if (set == GFX_8X8_4BIT)
				attr = ygv608.pattern_name_table[i+1] >> 4;

            if (ygv608.regs.s.r7 & r7_flip)
            {
                if (ygv608.pattern_name_table[i+1] & (1<<3)) f |= TILE_FLIPX;
                if (ygv608.pattern_name_table[i+1] & (1<<2)) f |= TILE_FLIPY;
            }
		}

		/* calculate page according to scroll data */
		/* - assuming full-screen scroll only for now... */
		sy = (int)ygv608.scroll_data_table[0][0x00] +
			(((int)ygv608.scroll_data_table[0][0x01] & 0x0f ) << 8);
		sx = (int)ygv608.scroll_data_table[0][0x80] +
			(((int)ygv608.scroll_data_table[0][0x81] & 0x0f ) << 8);

		if ((ygv608.regs.s.r7 & r7_md) == MD_2PLANE_16BIT)
		{
			page = ( ( sx + col * 8 ) % 1024 ) / 256;
			page += ( ( ( sy + row * 8 ) % 2048 ) / 256 ) * 4;
		}
		else if (ygv608.regs.s.r8 & r8_pgs)
		{
			page = ( ( sx + col * 8 ) % 2048 ) / 512;
			page += ( ( ( sy + row * 8 ) % 2048 ) / 256 ) * 4;
		}
		else
		{
			page = ( ( sx + col * 8 ) % 2048 ) / 256;
			page += ( ( ( sy + row * 8 ) % 2048 ) / 512 ) * 8;
		}

		/* add page, base address to pattern name */
		j += ( (int)ygv608.scroll_data_table[0][0xc0+page] << 10 );
		j += ( ygv608.base_addr[0][base] << 8 );

		if( j >= layout_total(set) )
		{
			logerror( "A_8X8: tilemap=%d\n", j );
			j = 0;
		}
		if ((ygv608.regs.s.r12 & r12_apf) != 0)
		{
			// attribute only valid in 16 color mode
			if( set == GFX_8X8_4BIT )
				attr = ( j >> ( ((ygv608.regs.s.r12 & r12_apf) - 1 ) * 2 ) ) & 0x0f;
		}
		// banking
		if (set == GFX_8X8_4BIT)
		{
			j += namcond1_gfxbank * 0x10000;
		}
		else // 8x8x8
		{
			j += namcond1_gfxbank * 0x8000;
		}

		SET_TILE_INFO( set, j, attr & 0x0F, f );
	}
}

static TILE_GET_INFO( get_tile_info_B_8 )
{
	// extract row,col packed into tile_index
	int             col = tile_index >> 6;
	int             row = tile_index & 0x3f;

	UINT8   attr = 0;
	int             pattern_name_base = ( ( ygv608.page_y << ygv608.pny_shift )
						<< ygv608.bits16 );
	int             set = GFX_8X8_4BIT;
	int             base = row >> ygv608.base_y_shift;

	if ((ygv608.regs.s.r7 & r7_md) & MD_1PLANE )
	{
		SET_TILE_INFO( set, 0, 0, 0 );
	}
	else if (col >= ygv608.page_x)
	{
		SET_TILE_INFO( set, 0, 0, 0 );
	}
	else if (row >= ygv608.page_y)
	{
		SET_TILE_INFO( set, 0, 0, 0 );
	}
	else
	{
		int sx, sy, page;
		int i = pattern_name_base + (((row << ygv608.pny_shift) + col) << ygv608.bits16);
		int j = ygv608.pattern_name_table[i];
        int f = 0;

		if (ygv608.bits16)
		{
			j += ((int)(ygv608.pattern_name_table[i+1] & ygv608.na8_mask )) << 8;
			attr = ygv608.pattern_name_table[i+1] >> 4; /*& 0x00; 0xf0;*/

            if (ygv608.regs.s.r7 & r7_flip)
            {
                if (ygv608.pattern_name_table[i+1] & (1<<3)) f |= TILE_FLIPX;
                if (ygv608.pattern_name_table[i+1] & (1<<2)) f |= TILE_FLIPY;
            }
		}

		/* calculate page according to scroll data */
		/* - assuming full-screen scroll only for now... */
		sy = (int)ygv608.scroll_data_table[1][0x00] +
			(((int)ygv608.scroll_data_table[1][0x01] & 0x0f ) << 8);
		sx = (int)ygv608.scroll_data_table[1][0x80] +
			(((int)ygv608.scroll_data_table[1][0x81] & 0x0f ) << 8);

		if ((ygv608.regs.s.r7 & r7_md) == MD_2PLANE_16BIT )
		{
			page = ( ( sx + col * 8 ) % 1024 ) / 256;
			page += ( ( ( sy + row * 8 ) % 2048 ) / 256 ) * 4;
		}
		else if (ygv608.regs.s.r8 & r8_pgs)
		{
			page = ( ( sx + col * 8 ) % 2048 ) / 512;
			page += ( ( ( sy + row * 8 ) % 2048 ) / 256 ) * 4;
		}
		else
		{
			page = ( ( sx + col * 8 ) % 2048 ) / 256;
			page += ( ( ( sy + row * 8 ) % 2048 ) / 512 ) * 8;
		}

		/* add page, base address to pattern name */
		j += ( (int)ygv608.scroll_data_table[1][0xc0+page] << 10 );
		j += ( ygv608.base_addr[1][base] << 8 );

		if( j >= layout_total(set) )
		{
			logerror( "B_8X8: tilemap=%d\n", j );
			j = 0;
		}
		if ((ygv608.regs.s.r12 & r12_bpf) != 0)
		{
			UINT8 color = (ygv608.regs.s.r12 & r12_bpf) >> 3;

			/* assume 16 colour mode for now... */
			attr = ( j >> ( (color - 1 ) * 2 ) ) & 0x0f;
		}

		// banking
		if (set == GFX_8X8_4BIT)
		{
			j += namcond1_gfxbank * 0x10000;
		}
		else // 8x8x8
		{
			j += namcond1_gfxbank * 0x8000;
		}

		SET_TILE_INFO( set, j, attr, f );
	}
}

static TILE_GET_INFO( get_tile_info_A_16 )
{
  // extract row,col packed into tile_index
  int             col = tile_index >> 6;
  int             row = tile_index & 0x3f;

  UINT8   attr = 0;
  int             pattern_name_base = 0;
  int             set = ((ygv608.regs.s.r7 & r7_md) == MD_1PLANE_256COLOUR
			            ? GFX_16X16_8BIT : GFX_16X16_4BIT );
  int             base = row >> ygv608.base_y_shift;

  if( col >= ygv608.page_x ) {
    SET_TILE_INFO( set, 0, 0, 0 );
  }
  else if( row >= ygv608.page_y ) {
    SET_TILE_INFO( set, 0, 0, 0 );
  }
  else {
    int sx, sy, page;
    int j;
    int i = ( ( ( row << ygv608.pny_shift ) + col ) << ygv608.bits16 );
    int f = 0;
    i += pattern_name_base;

    j = ygv608.pattern_name_table[i];
    if( ygv608.bits16 ) {
      j += ((int)(ygv608.pattern_name_table[i+1] & ygv608.na8_mask )) << 8;
      // attribute only valid in 16 color mode
      if( set == GFX_16X16_4BIT )
        attr = ygv608.pattern_name_table[i+1] >> 4;

      if (ygv608.regs.s.r7 & r7_flip)
      {
        if (ygv608.pattern_name_table[i+1] & (1<<3)) f |= TILE_FLIPX;
        if (ygv608.pattern_name_table[i+1] & (1<<2)) f |= TILE_FLIPY;
      }
    }

    /* calculate page according to scroll data */
    /* - assuming full-screen scroll only for now... */
    sy = (int)ygv608.scroll_data_table[0][0x00] +
         (((int)ygv608.scroll_data_table[0][0x01] & 0x0f ) << 8);
    sx = (int)ygv608.scroll_data_table[0][0x80] +
         (((int)ygv608.scroll_data_table[0][0x81] & 0x0f ) << 8);
    if((ygv608.regs.s.r7 & r7_md) == MD_2PLANE_16BIT ) {
      page = ( ( sx + col * 16 ) % 2048 ) / 512;
      page += ( ( sy + row * 16 ) / 512 ) * 4;
    }
    else if (ygv608.regs.s.r8 & r8_pgs) {
      page = ( sx + col * 16 ) / 512;
      page += ( ( sy + row * 16 ) / 1024 ) * 8;
    }
    else {
      page = ( sx + col * 16 ) / 1024;
      page += ( ( sy + row * 16 ) / 512 ) * 4;
    }

    /* add page, base address to pattern name */
    j += ( (int)ygv608.scroll_data_table[0][0xc0+page] << 8 );
    j += ( ygv608.base_addr[0][base] << 8 );

    if( j >= layout_total(set) ) {
	logerror( "A_16X16: tilemap=%d\n", j );
      j = 0;
    }

    if ((ygv608.regs.s.r12 & r12_apf) != 0)
    {
      // attribute only valid in 16 color mode
      if( set == GFX_16X16_4BIT )
        attr = ( j >> ( ((ygv608.regs.s.r12 & r12_apf)) * 2 ) ) & 0x0f;
    }

    // banking
    if (set == GFX_16X16_4BIT)
    {
      j += namcond1_gfxbank * 0x4000;
    }
    else // 8x8x8
    {
      j += namcond1_gfxbank * 0x2000;
    }


    SET_TILE_INFO( set, j, attr, f );
  }
}

static TILE_GET_INFO( get_tile_info_B_16 )
{
  // extract row,col packed into tile_index
  int             col = tile_index >> 6;
  int             row = tile_index & 0x3f;

  UINT8   attr = 0;
  int             pattern_name_base = ( ( ygv608.page_y << ygv608.pny_shift )
					<< ygv608.bits16 );
  int             set = GFX_16X16_4BIT;
  int             base = row >> ygv608.base_y_shift;

  if((ygv608.regs.s.r7 & r7_md) & MD_1PLANE ) {
    SET_TILE_INFO( set, 0, 0, 0 );
  }
  if( col >= ygv608.page_x ) {
    SET_TILE_INFO( set, 0, 0, 0 );
  }
  else if( row >= ygv608.page_y ) {
    SET_TILE_INFO( set, 0, 0, 0 );
  }
  else {
    int sx, sy, page;
    int j;
    int i = ( ( ( row << ygv608.pny_shift ) + col ) << ygv608.bits16 );
    int f = 0;
    i += pattern_name_base;

    j = ygv608.pattern_name_table[i];
    if( ygv608.bits16 ) {
      j += ((int)(ygv608.pattern_name_table[i+1] & ygv608.na8_mask )) << 8;
      attr = ygv608.pattern_name_table[i+1] >> 4; /*& 0x00; 0xf0;*/

      if (ygv608.regs.s.r7 & r7_flip)
      {
        if (ygv608.pattern_name_table[i+1] & (1<<3)) f |= TILE_FLIPX;
        if (ygv608.pattern_name_table[i+1] & (1<<2)) f |= TILE_FLIPY;
      }
    }

    /* calculate page according to scroll data */
    /* - assuming full-screen scroll only for now... */
    sy = (int)ygv608.scroll_data_table[1][0x00] +
         (((int)ygv608.scroll_data_table[1][0x01] & 0x0f ) << 8);
    sx = (int)ygv608.scroll_data_table[1][0x80] +
         (((int)ygv608.scroll_data_table[1][0x81] & 0x0f ) << 8);
    if((ygv608.regs.s.r7 & r7_md) == MD_2PLANE_16BIT ) {
      page = ( ( sx + col * 16 ) % 2048 ) / 512;
      page += ( ( sy + row * 16 ) / 512 ) * 4;
    }
    else if (ygv608.regs.s.r8 & r8_pgs) {
      page = ( sx + col * 16 ) / 512;
      page += ( ( sy + row * 16 ) / 1024 ) * 8;
    }
    else {
      page = ( sx + col * 16 ) / 1024;
      page += ( ( sy + row * 16 ) / 512 ) * 4;
    }

    /* add page, base address to pattern name */
    j += ( (int)ygv608.scroll_data_table[1][0xc0+page] << 8 );
    j += ( ygv608.base_addr[1][base] << 8 );

    if( j >= layout_total(set) ) {
	logerror( "B_16X16: tilemap=%d\n", j );
      j = 0;
    }

    if ((ygv608.regs.s.r12 & r12_bpf) != 0)
    {
      UINT8 color = (ygv608.regs.s.r12 & r12_bpf) >> 3;

      /* assume 16 colour mode for now... */
      attr = ( j >> (color * 2)) & 0x0f;
    }

    // banking
    if (set == GFX_16X16_4BIT)
    {
      j += namcond1_gfxbank * 0x4000;
    }
    else // 8x8x8
    {
      j += namcond1_gfxbank * 0x2000;
    }

    SET_TILE_INFO( set, j, attr, f );
  }
}

static void ygv608_postload(running_machine &machine)
{
	int i;

	ygv608.screen_resize = 1;
	ygv608.tilemap_resize = 1;

	for(i = 0; i < 50; i++)
		SetPostShortcuts(machine, i);
}

static void ygv608_register_state_save(running_machine &machine)
{
	state_save_register_item_array(machine, "ygv608", NULL, 0, ygv608.ports.b);
	state_save_register_item_array(machine, "ygv608", NULL, 0, ygv608.regs.b);
	state_save_register_item_array(machine, "ygv608", NULL, 0, ygv608.pattern_name_table);
	state_save_register_item_array(machine, "ygv608", NULL, 0, ygv608.sprite_attribute_table.b);
	state_save_register_item_2d_array(machine, "ygv608", NULL, 0, ygv608.scroll_data_table);
	state_save_register_item_2d_array(machine, "ygv608", NULL, 0, ygv608.colour_palette);

	machine.save().register_postload(save_prepost_delegate(FUNC(ygv608_postload), &machine));
}

static void ygv608_exit(running_machine &machine)
{
	work_bitmap = NULL;
}


VIDEO_START( ygv608 )
{
	memset( &ygv608, 0, sizeof(ygv608) );

	// flag rebuild of the tilemaps
	ygv608.screen_resize = 1;
	ygv608.tilemap_resize = 1;
	namcond1_gfxbank = 0;
	state_save_register_global(machine, namcond1_gfxbank);

	/* create tilemaps of all sizes and combinations */
	tilemap_A_cache_8[0] = tilemap_create(machine, get_tile_info_A_8, get_tile_offset,  8,8, 32,32);
	tilemap_A_cache_8[1] = tilemap_create(machine, get_tile_info_A_8, get_tile_offset,  8,8, 64,32);
	tilemap_A_cache_8[2] = tilemap_create(machine, get_tile_info_A_8, get_tile_offset,  8,8, 32,64);

	tilemap_A_cache_16[0] = tilemap_create(machine, get_tile_info_A_16, get_tile_offset,  16,16, 32,32);
	tilemap_A_cache_16[1] = tilemap_create(machine, get_tile_info_A_16, get_tile_offset,  16,16, 64,32);
	tilemap_A_cache_16[2] = tilemap_create(machine, get_tile_info_A_16, get_tile_offset,  16,16, 32,64);

	tilemap_B_cache_8[0] = tilemap_create(machine, get_tile_info_B_8, get_tile_offset,  8,8, 32,32);
	tilemap_B_cache_8[1] = tilemap_create(machine, get_tile_info_B_8, get_tile_offset,  8,8, 64,32);
	tilemap_B_cache_8[2] = tilemap_create(machine, get_tile_info_B_8, get_tile_offset,  8,8, 32,64);

	tilemap_B_cache_16[0] = tilemap_create(machine, get_tile_info_B_16, get_tile_offset,  16,16, 32,32);
	tilemap_B_cache_16[1] = tilemap_create(machine, get_tile_info_B_16, get_tile_offset,  16,16, 64,32);
	tilemap_B_cache_16[2] = tilemap_create(machine, get_tile_info_B_16, get_tile_offset,  16,16, 32,64);

	tilemap_A = NULL;
	tilemap_B = NULL;

	ygv608_register_state_save(machine);
	machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(ygv608_exit), &machine));
}

static void draw_sprites(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect)
{
#ifdef _ENABLE_SPRITES

  // sprites are always clipped to 512x512
  // - regardless of the visible display dimensions
  rectangle spriteClip = { 0, 512, 0, 512 };

  PSPRITE_ATTR sa;
  int flipx = 0, flipy = 0;
  int i;

  /* ensure that sprites are enabled */
  if( ((ygv608.regs.s.r7 & r7_dspe) == 0 ) || (ygv608.regs.s.r10 & r10_sprd))
    return;

  /* draw sprites */
  sect_rect(&spriteClip, cliprect);
  sa = &ygv608.sprite_attribute_table.s[YGV608_MAX_SPRITES-1];
  for( i=0; i<YGV608_MAX_SPRITES; i++, sa-- )
  {
    int code, color, sx, sy, size, attr, g_attr, spf;

    color = (sa->attr >> 4) & 0x0f;
    sx = ( (int)(sa->attr & 0x02) << 7 ) | (int)sa->sx;
    sy = ( ( ( (int)(sa->attr & 0x01) << 8 ) | (int)sa->sy ) + 1 ) & 0x1ff;
    attr = (sa->attr & 0x0c) >> 2;
    g_attr = (ygv608.regs.s.r10 & r10_spa) >> 6;
    spf = (ygv608.regs.s.r12 & r12_spf) >> 6;

    if ((ygv608.regs.s.r10 & r10_spas) == SPAS_SPRITESIZE )
    {
      size = g_attr;
      flipx = (attr & SZ_HORIZREVERSE) != 0;
      flipy = (attr & SZ_VERTREVERSE) != 0;
    }
    else
    {
      size = attr;
      flipx = (g_attr & SZ_HORIZREVERSE) != 0;
      flipy = (g_attr & SZ_VERTREVERSE) != 0;
    }

    switch( size ) {

    case SZ_8X8 :
      code = ( (int)ygv608.regs.s.sba << 8 ) | (int)sa->sn;
      if (spf != 0)
	    color = ( code >> ( (spf - 1) * 2 ) ) & 0x0f;
      if( code >= layout_total(GFX_8X8_4BIT) ) {
	    logerror( "SZ_8X8: sprite=%d\n", code );
	    code = 0;
      }
      drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_8X8_4BIT],
	       code+namcond1_gfxbank*0x10000,
	       color,
	       flipx,flipy,
	       sx,sy,0x00);
      // redraw with wrap-around
      if( sx > 512-8 )
        drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_8X8_4BIT],
	        code+namcond1_gfxbank*0x10000,
	        color,
	        flipx,flipy,
	        sx-512,sy,0x00);
      if( sy > 512-8 )
        drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_8X8_4BIT],
	        code+namcond1_gfxbank*0x10000,
	        color,
	        flipx,flipy,
	        sx,sy-512,0x00);
      // really should draw again for both wrapped!
      // - ignore until someone thinks it's required
      break;

    case SZ_16X16 :
      code = ( ( (int)ygv608.regs.s.sba & 0xfc ) << 6 ) | (int)sa->sn;
      if (spf != 0)
	    color = ( code >> (spf * 2) ) & 0x0f;
      if( code >= layout_total(GFX_16X16_4BIT) ) {
	    logerror( "SZ_8X8: sprite=%d\n", code );
	    code = 0;
      }
      drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_16X16_4BIT],
	       code+namcond1_gfxbank*0x4000,
	       color,
	       flipx,flipy,
	       sx,sy,0x00);
      // redraw with wrap-around
      if( sx > 512-16 )
        drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_16X16_4BIT],
	        code+namcond1_gfxbank*0x4000,
	        color,
	        flipx,flipy,
	        sx-512,sy,0x00);
      if( sy > 512-16 )
        drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_16X16_4BIT],
	        code+namcond1_gfxbank*0x4000,
	        color,
	        flipx,flipy,
	        sx,sy-512,0x00);
      // really should draw again for both wrapped!
      // - ignore until someone thinks it's required
      break;

    case SZ_32X32 :
      code = ( ( (int)ygv608.regs.s.sba & 0xf0 ) << 4 ) | (int)sa->sn;
      if (spf != 0)
	color = ( code >> ( (spf + 1) * 2 ) ) & 0x0f;
      if( code >= layout_total(GFX_32X32_4BIT) ) {
	  logerror( "SZ_32X32: sprite=%d\n", code );
	code = 0;
      }
      drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_32X32_4BIT],
	       code+namcond1_gfxbank*0x1000,
	       color,
	       flipx,flipy,
	       sx,sy,0x00);
      // redraw with wrap-around
      if( sx > 512-32 )
        drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_32X32_4BIT],
	        code+namcond1_gfxbank*0x1000,
	        color,
	        flipx,flipy,
	        sx-512,sy,0x00);
      if( sy > 512-32 )
        drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_32X32_4BIT],
	        code+namcond1_gfxbank*0x1000,
	        color,
	        flipx,flipy,
	        sx,sy-512,0x00);
      // really should draw again for both wrapped!
      // - ignore until someone thinks it's required
      break;

    case SZ_64X64 :
      code = ( ( (int)ygv608.regs.s.sba & 0xc0 ) << 2 ) | (int)sa->sn;
      if (spf != 0)
	    color = ( code >> ( (spf + 1) * 2 ) ) & 0x0f;
      if( code >= layout_total(GFX_64X64_4BIT) ) {
	    logerror( "SZ_64X64: sprite=%d\n", code );
	    code = 0;
      }
      drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_64X64_4BIT],
	       code+namcond1_gfxbank*0x400,
	       color,
	       flipx,flipy,
	       sx,sy,0x00);
      // redraw with wrap-around
      if( sx > 512-64 )
        drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_64X64_4BIT],
	        code+namcond1_gfxbank*0x400,
	        color,
	        flipx,flipy,
	        sx-512,sy,0x00);
      if( sy > 512-64 )
        drawgfx_transpen( bitmap, &spriteClip,machine.gfx[GFX_64X64_4BIT],
	        code+namcond1_gfxbank*0x400,
	        color,
	        flipx,flipy,
	        sx,sy-512,0x00);
      // really should draw again for both wrapped!
      // - ignore until someone thinks it's required
      break;

    default :
      break;
    }
  }

#endif
}

#ifdef _SHOW_VIDEO_DEBUG
static const char *const mode[] = {
	"2PLANE_8BIT",
	"2PLANE_16BIT",
	"1PLANE_16COLORS",
	"1PLANE_256COLORS"
};

static const char *const psize[] = { "8x8", "16x16", "32x32", "64x64" };
#endif

SCREEN_UPDATE( ygv608 )
{
#ifdef _SHOW_VIDEO_DEBUG
    char buffer[64];
#endif
#ifdef _ENABLE_SCROLLY
    int col;
#endif
#ifdef _ENABLE_ROTATE_ZOOM
    int xc, yc;
    double r, alpha, sin_theta, cos_theta;
#endif
	rectangle finalclip;
	const rectangle &visarea = screen->visible_area();

	// clip to the current bitmap
	finalclip.min_x = 0;
	finalclip.max_x = screen->width() - 1;
	finalclip.min_y = 0;
	finalclip.max_y = screen->height() - 1;
	sect_rect(&finalclip, cliprect);
	cliprect = &finalclip;

	// punt if not initialized
	if (ygv608.page_x == 0 || ygv608.page_y == 0)
	{
		bitmap_fill(bitmap, cliprect, 0);
		return 0;
	}

	if( ygv608.screen_resize )
	{
#ifdef _ENABLE_SCREEN_RESIZE
		// hdw should be scaled by 16, not 8
		// - is it something to do with double dot-clocks???
		screen->set_visible_area(0, ((int)(ygv608.regs.s.hdw)<<3/*4*/)-1,
						  0, ((int)(ygv608.regs.s.vdw)<<3)-1 );
#endif

		auto_free( screen->machine(), work_bitmap );
		work_bitmap = screen->alloc_compatible_bitmap();

		// reset resize flag
		ygv608.screen_resize = 0;
	}

	if( ygv608.tilemap_resize )
	{
		int index;

		/* based on the page sizes, pick an index */
		if (ygv608.page_x == 64)
			index = 1;
		else if (ygv608.page_y == 64)
			index = 2;
		else
			index = 0;

		if ((ygv608.regs.s.r9 & r9_pts) == PTS_8X8 )
			tilemap_A = tilemap_A_cache_8[index];
		else
			tilemap_A = tilemap_A_cache_16[index];
		tilemap_mark_all_tiles_dirty(tilemap_A);

		tilemap_set_transparent_pen( tilemap_A, 0 );
		// for NCV1 it's sufficient to scroll only columns
		tilemap_set_scroll_cols( tilemap_A, ygv608.page_x );

		if ((ygv608.regs.s.r9 & r9_pts) == PTS_8X8 )
			tilemap_B = tilemap_B_cache_8[index];
		else
			tilemap_B = tilemap_B_cache_16[index];
		tilemap_mark_all_tiles_dirty(tilemap_B);

		// for NCV1 it's sufficient to scroll only columns
		tilemap_set_scroll_cols( tilemap_B, ygv608.page_x );

		// now clear the screen in case we change to 1-plane mode
		bitmap_fill( work_bitmap, cliprect , 0);

		// reset resize flag
		ygv608.tilemap_resize = 0;
	}

#ifdef _ENABLE_SCROLLY

  for( col=0; col<ygv608.page_x; col++ )
  {
    tilemap_set_scrolly( tilemap_B, col,
			   ( (int)ygv608.scroll_data_table[1][(col>>ygv608.col_shift)<<1] +
			     ( (int)ygv608.scroll_data_table[1][((col>>ygv608.col_shift)<<1)+1] << 8 ) ) );

    tilemap_set_scrolly( tilemap_A, col,
			 ( (int)ygv608.scroll_data_table[0][(col>>ygv608.col_shift)<<1] +
			   ( (int)ygv608.scroll_data_table[0][((col>>ygv608.col_shift)<<1)+1] << 8 ) ) );
  }

#endif

#ifdef _ENABLE_SCROLLX

    tilemap_set_scrollx( tilemap_B, 0,
			   ( (int)ygv608.scroll_data_table[1][0x80] +
			     ( (int)ygv608.scroll_data_table[1][0x81] << 8 ) ) );

    tilemap_set_scrollx( tilemap_A, 0,
			 ( (int)ygv608.scroll_data_table[0][0x80] +
			   ( (int)ygv608.scroll_data_table[0][0x81] << 8 ) ) );

#endif

	tilemap_set_enable( tilemap_A, ygv608.regs.s.r7 & r7_dspe);
	if((ygv608.regs.s.r7 & r7_md) & MD_1PLANE )
		tilemap_set_enable( tilemap_B, 0 );
	else
		tilemap_set_enable( tilemap_B, ygv608.regs.s.r7 & r7_dspe);

	tilemap_mark_all_tiles_dirty( tilemap_A );
	tilemap_mark_all_tiles_dirty( tilemap_B );


	/*
     *    now we can render the screen
     */

#if 1
	// LBO - need to implement proper pen marking for sprites as well as set aside a non-transparent
	// pen to be used for background fills when plane B is disabled.
	if ((ygv608.regs.s.r7 & r7_md) & MD_1PLANE)
	{
		// If the background tilemap is disabled, we need to clear the bitmap to black
		bitmap_fill (work_bitmap,cliprect,0);
//      bitmap_fill (work_bitmap,visarea,1);
	}
	else
#endif
		tilemap_draw( work_bitmap,cliprect, tilemap_B, 0, 0 );

#ifdef _ENABLE_ROTATE_ZOOM

  /*
   *    fudge - translate ax,ay to startx, starty each time
   */

  xc = ygv608.ax >> 16;
  yc = ygv608.ay >> 16;
  r = sqrt( (double)( xc * xc + yc * yc ) );
  alpha = atan( (double)xc / (double)yc );
  sin_theta = (double)ygv608.dyx / (double)0x10000;
  cos_theta = (double)ygv608.dx / (double)0x10000;

  if( ygv608.regs.s.zron )
    copyrozbitmap( bitmap, cliprect, work_bitmap,
                   ( visarea.min_x << 16 ) +
                    ygv608.ax + 0x10000 * r *
                    ( -sin( alpha ) * cos_theta + cos( alpha ) * sin_theta ),
                   ( visarea.min_y << 16 ) +
                    ygv608.ay + 0x10000 * r *
                    ( cos( alpha ) * cos_theta + sin( alpha ) * sin_theta ),
                   ygv608.dx, ygv608.dxy, ygv608.dyx, ygv608.dy, 0);
  else
#endif
    copybitmap( bitmap, work_bitmap, 0, 0, 0, 0, cliprect);

  // for some reason we can't use an opaque tilemap_A
  // so use a transparent but clear the work bitmap first
  // - look at why this is the case?!?
  bitmap_fill( work_bitmap,&visarea ,0);

	if ((ygv608.regs.s.r11 & r11_prm) == PRM_ASBDEX ||
		(ygv608.regs.s.r11 & r11_prm) == PRM_ASEBDX )
		draw_sprites(screen->machine(), bitmap,cliprect );

	tilemap_draw( work_bitmap,cliprect, tilemap_A, 0, 0 );

#ifdef _ENABLE_ROTATE_ZOOM
  if( ygv608.regs.s.zron )
    copyrozbitmap_trans( bitmap, cliprect, work_bitmap,
                   ygv608.ax, // + ( visarea.min_x << 16 ),
                   ygv608.ay, // + ( visarea.min_y << 16 ),
                   ygv608.dx, ygv608.dxy, ygv608.dyx, ygv608.dy, 0,
                   0 );
  else
#endif
    copybitmap_trans( bitmap, work_bitmap, 0, 0, 0, 0, cliprect, 0 );

	if ((ygv608.regs.s.r11 & r11_prm) == PRM_SABDEX ||
		(ygv608.regs.s.r11 & r11_prm) == PRM_SEABDX)
		draw_sprites(screen->machine(), bitmap,cliprect );


#ifdef _SHOW_VIDEO_DEBUG
  /* show screen control information */
  ui_draw_text( mode[(ygv608.regs.s.r7 & r7_md) >> 1], 0, 0 );
  sprintf( buffer, "%02ux%02u", ygv608.page_x, ygv608.page_y );
  ui_draw_text( buffer, 0, 16 );
  ui_draw_text( psize[(ygv608.regs.s.r9 & r9_pts) >> 6], 0, 32 );
  sprintf( buffer, "A: SX:%d SY:%d",
	   (int)ygv608.scroll_data_table[0][0x80] +
	   ( ( (int)ygv608.scroll_data_table[0][0x81] & 0x0f ) << 8 ),
	   (int)ygv608.scroll_data_table[0][0x00] +
	   ( ( (int)ygv608.scroll_data_table[0][0x01] & 0x0f ) << 8 ) );
  ui_draw_text( buffer, 0, 48 );
  sprintf( buffer, "B: SX:%d SY:%d",
	   (int)ygv608.scroll_data_table[1][0x80] +
	   ( ( (int)ygv608.scroll_data_table[1][0x81] & 0x0f ) << 8 ),
	   (int)ygv608.scroll_data_table[1][0x00] +
	   ( ( (int)ygv608.scroll_data_table[1][0x01] & 0x0f ) << 8 ) );
  ui_draw_text( buffer, 0, 64 );
#endif
	return 0;
}

READ16_HANDLER( ygv608_r )
{
	static int p0_state = 0;
	static int p3_state = 0;
	static int pattern_name_base = 0;  /* pattern name table base address */
	int pn=0;
	UINT16  data = 0;

	switch (offset)
	{
		case 0x00: /* P#0 - pattern name table data port */
		{
			UINT8 xTile = ygv608.regs.s.r1 & r1_pnx;
			UINT8 yTile = ygv608.regs.s.r0 & r0_pny;

			switch (p0_state)
			{
				case 0:
					/* Are we reading from plane B? */
					if (!((ygv608.regs.s.r7 & r7_md) & MD_1PLANE) && (ygv608.regs.s.r0 & r0_b_a))
						pattern_name_base = ((ygv608.page_y << ygv608.pny_shift) << ygv608.bits16);

					/* read character from ram */
					pn = pattern_name_base + (((yTile << ygv608.pny_shift) + xTile) << ygv608.bits16);
					break;

				case 1:
					/* read character from ram */
					pn = pattern_name_base + (((yTile << ygv608.pny_shift) + xTile) << ygv608.bits16) + 1;
					break;
			}

			if (pn > 4095)
			{
				logerror( "attempt (%d) to read pattern name %d\n"
					"mode = %d, pgs = %d (%dx%d)\n"
					"pattern_name_base = %d\n"
					"pnx = %d, pny = %d, pny_shift = %d, bits16 = %d\n",
					p0_state,
					pn, ygv608.regs.s.r7 & r7_md, ygv608.regs.s.r8 & r8_pgs,
					ygv608.page_x, ygv608.page_y,
					pattern_name_base,
					xTile, yTile, ygv608.pny_shift,
					ygv608.bits16 );
				pn = 0;
			}
			data = ygv608.pattern_name_table[pn];

			p0_state++;
			if ((ygv608.regs.s.r7 & r7_md) == MD_2PLANE_8BIT )
				p0_state++;

			if (p0_state == 2)
			{
				if (ygv608.regs.s.r0 & r0_pnya)
				{
					if (yTile++ == (ygv608.page_y - 1))
					{
						yTile = 0;
						if (xTile++ == (ygv608.page_x - 1))
						{
							xTile = 0;
							// we're now off this tile plane, toggle planes
							ygv608.regs.s.r0 ^= r0_b_a;
						}
					}
					ygv608.regs.s.r0 &= ~r0_pny;
					ygv608.regs.s.r0 |= yTile;
					ygv608.regs.s.r1 &= ~r1_pnx;
					ygv608.regs.s.r1 |= xTile;
				}
				else if (ygv608.regs.s.r1 & r1_pnxa)
				{
					if (xTile++ == (ygv608.page_x - 1))
					{
						xTile = 0;
						if (yTile++ == (ygv608.page_y - 1))
						{
							yTile = 0;
							// we're now off this tile plane, toggle planes
							ygv608.regs.s.r0 ^= r0_b_a;
						}
					}
					ygv608.regs.s.r0 &= ~r0_pny;
					ygv608.regs.s.r0 |= yTile;
					ygv608.regs.s.r1 &= ~r1_pnx;
					ygv608.regs.s.r1 |= xTile;
				}
				p0_state = 0;
				pattern_name_base = 0;
			}
			return (data << 8);
		}
		break;

		case 0x01: /* P#1 - sprite data port */
			data = ygv608.sprite_attribute_table.b[ygv608.regs.s.saa];
			if (ygv608.regs.s.r2 & r2_saar)
				ygv608.regs.s.saa++;
			return (data << 8);

		case 0x02: /* P#2 - scroll data port */
			data = ygv608.scroll_data_table[(ygv608.regs.s.r2 & r2_b_a) >> 4][ygv608.regs.s.sca];
			if (ygv608.regs.s.r2 & r2_scar)
			{
				ygv608.regs.s.sca++;
				/* handle wrap to next plane */
				if (ygv608.regs.s.sca == 0)
					ygv608.regs.s.r2 ^= r2_b_a;
			}
			return( data << 8 );

		case 0x03: /* P#3 - color palette data port */
			data = ygv608.colour_palette[ygv608.regs.s.cc][p3_state];
			if( ++p3_state == 3 )
			{
				p3_state = 0;
				if( ygv608.regs.s.r2 & r2_cpar)
					ygv608.regs.s.cc++;
			}
			return( data << 8 );

		case 0x04: /* P#4 - register data port */
		{
			UINT8 regNum = (ygv608.ports.s.p5) & p5_rn;
    		data = ygv608.regs.b[regNum];
			if (ygv608.ports.s.p5 & p5_rrai)
			{
				regNum ++;
				if (regNum == 50)
				{
					regNum = 0;
					logerror( "warning: rn=50 after read increment\n" );
				}
				ygv608.ports.s.p5 &= ~p5_rn;
				ygv608.ports.s.p5 |= regNum;
			}
			return (data << 8);
		}
		break;

		case 0x05:
			break;

		case 0x06:
		case 0x07:
			return( (UINT16)(ygv608.ports.b[offset]) << 8 );

		default :
			logerror( "unknown ygv608 register (%d)\n", offset );
			break;
	}

	return( 0 );
}

WRITE16_HANDLER( ygv608_w )
{
	static int p0_state = 0;
	static int p3_state = 0;
	static int pattern_name_base = 0;  /* pattern name table base address */
	int pn=0;

	data = ( data >> 8 ) & 0xff;

	switch (offset)
	{
		case 0x00: /* P#0 - pattern name table data port */
		{
			UINT8 xTile = ygv608.regs.s.r1 & r1_pnx;
			UINT8 yTile = ygv608.regs.s.r0 & r0_pny;

			switch (p0_state)
			{
				case 0:
					/* Are we reading from plane B? */
					if (!((ygv608.regs.s.r7 & r7_md) & MD_1PLANE) && (ygv608.regs.s.r0 & r0_b_a))
						pattern_name_base = ((ygv608.page_y << ygv608.pny_shift) << ygv608.bits16);

					/* read character from ram */
					pn = pattern_name_base + (((yTile << ygv608.pny_shift) + xTile) << ygv608.bits16);
					break;

				case 1:
					/* read character from ram */
					pn = pattern_name_base + (((yTile << ygv608.pny_shift) + xTile) << ygv608.bits16) + 1;
					break;
			}

			if (pn > 4095)
			{
				logerror( "attempt (%d) to read pattern name %d\n"
					"mode = %d, pgs = %d (%dx%d)\n"
					"pattern_name_base = %d\n"
					"pnx = %d, pny = %d, pny_shift = %d, bits16 = %d\n",
					p0_state,
					pn, ygv608.regs.s.r7 & r7_md, ygv608.regs.s.r8 & r8_pgs,
					ygv608.page_x, ygv608.page_y,
					pattern_name_base,
					xTile, yTile, ygv608.pny_shift,
					ygv608.bits16 );
				pn = 0;
			}
			ygv608.pattern_name_table[pn] = data;

			p0_state++;
			if ((ygv608.regs.s.r7 & r7_md) == MD_2PLANE_8BIT )
				p0_state++;

			if (p0_state == 2)
			{
				if (ygv608.regs.s.r0 & r0_pnya)
				{
					if (yTile++ == (ygv608.page_y - 1))
					{
						yTile = 0;
						if (xTile++ == (ygv608.page_x - 1))
						{
							xTile = 0;
							ygv608.regs.s.r0 ^= r0_b_a;
						}
					}
					ygv608.regs.s.r0 &= ~r0_pny;
					ygv608.regs.s.r0 |= yTile;
					ygv608.regs.s.r1 &= ~r1_pnx;
					ygv608.regs.s.r1 |= xTile;
				}
				else if (ygv608.regs.s.r1 & r1_pnxa)
				{
					if (xTile++ == (ygv608.page_x - 1))
					{
						xTile = 0;
						if (yTile++ == (ygv608.page_y - 1))
						{
							yTile = 0;
							ygv608.regs.s.r0 ^= r0_b_a;
						}
					}
					ygv608.regs.s.r0 &= ~r0_pny;
					ygv608.regs.s.r0 |= yTile;
					ygv608.regs.s.r1 &= ~r1_pnx;
					ygv608.regs.s.r1 |= xTile;
				}
				p0_state = 0;
				pattern_name_base = 0;
			}
		}
		break;

		case 0x01: /* P#1 - sprite data port */
			ygv608.sprite_attribute_table.b[ygv608.regs.s.saa] = data;
			if( ygv608.regs.s.r2 & r2_saaw)
				ygv608.regs.s.saa++;
			break;

		case 0x02: /* P#2 - scroll data port */
			ygv608.scroll_data_table[(ygv608.regs.s.r2 & r2_b_a) >> 4][ygv608.regs.s.sca] = data;
			if (ygv608.regs.s.r2 & r2_scaw)
			{
				ygv608.regs.s.sca++;
				/* handle wrap to next plane */
				if (ygv608.regs.s.sca == 0)
					ygv608.regs.s.r2 ^= r2_b_a;
			}
			break;

		case 0x03: /* P#3 - colour palette data port */
			ygv608.colour_palette[ygv608.regs.s.cc][p3_state] = data;
			if (++p3_state == 3)
			{
				p3_state = 0;
				palette_set_color_rgb(space->machine(),ygv608.regs.s.cc,
			    	pal6bit(ygv608.colour_palette[ygv608.regs.s.cc][0]),
			    	pal6bit(ygv608.colour_palette[ygv608.regs.s.cc][1]),
			    	pal6bit(ygv608.colour_palette[ygv608.regs.s.cc][2]) );
				if (ygv608.regs.s.r2 & r2_cpaw)
					ygv608.regs.s.cc++;
			}
			break;

		case 0x04: /* P#4 - register data port */
		{
			UINT8 regNum = (ygv608.ports.s.p5) & p5_rn;
#if 0
			logerror( "R#%d = $%02X\n", regNum, data );
#endif
			SetPreShortcuts (regNum, data);
			ygv608.regs.b[regNum] = data;
			SetPostShortcuts (space->machine(), regNum);
			if (ygv608.ports.s.p5 & p5_rwai)
			{
				regNum ++;
				if (regNum == 50)
				{
					regNum = 0;
	    			logerror( "warning: rn=50 after write increment\n" );
				}
				ygv608.ports.s.p5 &= ~p5_rn;
				ygv608.ports.s.p5 |= regNum;
			}
		}
		break;

		case 0x05: /* P#5 - register select port */
			ygv608.ports.b[5] = data;
			break;

		case 0x06: /* P#6 - status port */
			/* writing a '1' resets that bit */
			ygv608.ports.b[6] &= ~data;
			break;

		case 0x07: /* P#7 - system control port */
			ygv608.ports.b[7] = data;
			if (ygv608.ports.b[7] & 0x3e)
				HandleRomTransfers(space->machine());
			if (ygv608.ports.b[7] & 0x01)
				HandleYGV608Reset(space->machine());
			break;

		default:
			logerror( "unknown ygv608 register (%d)\n", offset );
			break;
	}
}

static void HandleYGV608Reset( running_machine &machine )
{
    int i;

    /* Clear ports #0-7 */
    memset( &ygv608.ports.b[0], 0, 8 );

    /* Clear registers #0-38, #47-49 */
    memset( &ygv608.regs.b[0], 0, 39 );
    memset( &ygv608.regs.b[47], 0, 3 );

    /* Clear internal ram */
    memset( ygv608.pattern_name_table, 0, 4096 );
    memset( ygv608.sprite_attribute_table.b, 0,
            YGV608_SPRITE_ATTR_TABLE_SIZE );
    memset( ygv608.scroll_data_table, 0, 2*256 );
    memset( ygv608.colour_palette, 0, 256*3 );

    /* should set shortcuts here too */
    for( i=0; i<50; i++ ) {
      //SetPreShortcuts( i );
      SetPostShortcuts( machine, i );
    }
}

/*
    The YGV608 has a function to block-move data from the rom into
    internal tables. This function is not used in NCV1, but I used
    it for testing trojan ROM software.
    - So leave it in!
 */

static void HandleRomTransfers(running_machine &machine)
{
#if 0
  static UINT8 *sdt = (UINT8 *)ygv608.scroll_data_table;
  static UINT8 *sat = (UINT8 *)ygv608.sprite_attribute_table.b;

  /* fudge copy from sprite data for now... */
  UINT8 *RAM = machine.memory_region[0];
  int i;

  int src = ( ( (int)ygv608.regs.s.tb13 << 8 ) +
	      (int)ygv608.regs.s.tb5 ) << 5;
  int bytes = (int)ygv608.regs.s.tn4 << 4;

    logerror( "Transferring data from rom...\n" );

  /* pattern name table */
  if( ygv608.ports.s.tn ) {
  }

  /* scroll table */
  if( ygv608.ports.s.tl ) {

    int dest = (int)ygv608.regs.s.sca;
    if( ygv608.regs.s.p2_b_a )
      dest += 0x100;

    /* fudge a transfer for now... */
    for( i=0; i<bytes; i++ ) {
      sdt[(dest+i)%512] = RAM[src+(i^0x01)];

    }

    /* flag as finished */
    ygv608.ports.s.tl = 0;
  }

  /* sprite attribute table */
  if( ygv608.ports.s.ts ) {

    int dest = (int)ygv608.regs.s.saa;

    /* fudge a transfer for now... */
    for( i=0; i<bytes; i++ ) {
      sat[(dest+i)%256] = RAM[src+(i^0x01)];

    }

    /* flag as finished */
    ygv608.ports.s.ts = 0;
  }
#endif
}

#if 0
void nvsram( offs_t offset, UINT16 data )
{
  static int i = 0;

  data = ( data >> 8 ) & 0xff;

  if( 1 ) {
    static char ascii[16];
    if( i%16 == 0 )
      logerror( "%04X: ", offset );
    logerror( "%02X ", data );
    ascii[i%16] = (data > 0x20) ? data : '.';
    if( i%16 == 15 )
      logerror( "| %-16.16s\n", ascii );
  }

  i++;
}
#endif

// Set any "short-cut" variables before we update the YGV608 registers
// - these are used only in optimisation of the emulation

static void SetPreShortcuts( int reg, int data )
{
    switch( reg ) {

        case 7 :
            if( ( ( data >> MD_SHIFT ) & MD_MASK ) != (ygv608.regs.s.r7 & r7_md))
                ygv608.tilemap_resize = 1;
            break;

        case 8 :
            if( ( ( data >> PGS_SHIFT ) & PGS_MASK ) != (ygv608.regs.s.r8 & r8_pgs))
                ygv608.tilemap_resize = 1;
            break;

        case 9 :
            if( ( ( data >> PTS_SHIFT ) & PTS_MASK ) != (ygv608.regs.s.r9 & r9_pts))
                ygv608.tilemap_resize= 1;
            break;

        case 40 :
            if( ( ( data >> HDW_SHIFT ) & HDW_MASK ) != (ygv608.regs.s.r40 & r40_hdw))
                ygv608.screen_resize = 1;
            break;

        case 44 :
            if( ( ( data >> VDW_SHIFT ) & VDW_MASK ) != (ygv608.regs.s.r44 & r44_vdw))
                ygv608.screen_resize = 1;
            break;
    }
}

// Set any "short-cut" variables after we have updated the YGV608 registers
// - these are used only in optimisation of the emulation

static void SetPostShortcuts( running_machine &machine, int reg )
{
  int plane, addr;

  switch (reg)
  {
    case 0:
    {
      UINT8 yTile = ygv608.regs.s.r0 & r0_pny;

      if (yTile >= ygv608.page_y)
        logerror ("%s:setting pny(%d) >= page_y(%d)\n", machine.describe_context(),
            yTile, ygv608.page_y );
      yTile &= (ygv608.page_y - 1);
      ygv608.regs.s.r0 &= ~r0_pny;
      ygv608.regs.s.r0 |= yTile;
    }
    break;

    case 1:
    {
      UINT8 xTile = ygv608.regs.s.r1 & r1_pnx;

      if (xTile >= ygv608.page_x)
        logerror ("%s:setting pnx(%d) >= page_x(%d)\n", machine.describe_context(),
            xTile, ygv608.page_x );
      xTile &= (ygv608.page_x - 1);
      ygv608.regs.s.r1 &= ~r1_pnx;
      ygv608.regs.s.r1 |= xTile;
    }
    break;

    case 6:
#if 0
      logerror( "SBA = $%08X\n", (int)ygv608.regs.s.sba << 13 );
#endif
      break;

    case 7:
      ygv608.na8_mask = ((ygv608.regs.s.r7 & r7_flip) ? 0x03 : 0x0f );
      /* fall through */

  case 8 :
    ygv608.bits16 = ((ygv608.regs.s.r7 & r7_md) == MD_2PLANE_8BIT ? 0 : 1 );
    if((ygv608.regs.s.r7 & r7_md) == MD_2PLANE_16BIT )
      ygv608.page_x = ygv608.page_y = 32;
    else {
      if ((ygv608.regs.s.r8 & r8_pgs) == 0 ) {
        ygv608.page_x = 64;
        ygv608.page_y = 32;
      }
      else {
        ygv608.page_x = 32;
        ygv608.page_y = 64;
      }
    }
    ygv608.pny_shift = ( ygv608.page_x == 32 ? 5 : 6 );

    /* bits to shift pattern y coordinate to extract base */
    ygv608.base_y_shift = ( ygv608.page_y == 32 ? 2 : 3 );

    break;

  case 9 :
    switch (ygv608.regs.s.r9 & r9_slv)
    {
        case SLV_SCREEN :
            // always use scoll table entry #1
            ygv608.col_shift = 8;
            break;
        default :
            if ((ygv608.regs.s.r9 & r9_pts) == PTS_8X8 )
                ygv608.col_shift = (ygv608.regs.s.r9 & r9_slv) - 4;
            else
                ygv608.col_shift = (ygv608.regs.s.r9 & r9_slv) - 5;
            if( ygv608.col_shift < 0 )
            {
                // we can't handle certain conditions
                logerror( "Unhandled slv condition (pts=$%X,slv=$%X)\n",
                          ygv608.regs.s.r9 & r9_pts, ygv608.regs.s.r9 & r9_slv);
                ygv608.col_shift = 8;
            }
            break;
    }

    //if( ygv608.regs.s.slh != SLH_SCREEN )
    //    logerror( "SLH = %1X\n", ygv608.regs.s.slh );
    break;

  case 11 :
    //ShowYGV608Registers();
    break;

  case 17 : case 18 : case 19 : case 20 :
  case 21 : case 22 : case 23 : case 24 :
    plane = (reg-17) >> 2;
    addr = ( (reg-17) << 1 ) & 0x07;
    ygv608.base_addr[plane][addr] = ygv608.regs.b[reg] & 0x0f;
    ygv608.base_addr[plane][addr+1] = ygv608.regs.b[reg] >> 4;
    break;

  case 25 : case 26 : case 27 :
    ygv608.ax = (int)(ygv608.regs.s.ax16 & 0x1f) << 16 |
                (int)ygv608.regs.s.ax8 << 8 |
                (int)ygv608.regs.s.ax0;
    ygv608.ax <<= 7;
    if( ygv608.ax & 0x08000000 ) ygv608.ax |= 0xf8000000;   // 2s complement
    //logerror( "ygv608.ax = $%X\n", ygv608.ax );
    break;

  case 28 : case 29 :
    ygv608.dx = (int)(ygv608.regs.s.dx8 & 0x1f) << 8 | (int)ygv608.regs.s.dx0;
    ygv608.dx <<= 7;
    if( ygv608.dx & 0x00080000 ) ygv608.dx |= 0xfff80000;   // 2s complement
    break;

  case 30 : case 31 :
    ygv608.dxy = (int)(ygv608.regs.s.dxy8 & 0x1f) << 8 | (int)ygv608.regs.s.dxy0;
    ygv608.dxy <<= 7;
    if( ygv608.dxy & 0x00080000 ) ygv608.dxy |= 0xfff80000; // 2s complement
    break;

  case 32 : case 33 : case 34 :
    ygv608.ay = (int)(ygv608.regs.s.ay16 & 0x1f) << 16 |
                (int)ygv608.regs.s.ay8 << 8 |
                (int)ygv608.regs.s.ay0;
    ygv608.ay <<= 7;
    if( ygv608.ay & 0x08000000 ) ygv608.ay |= 0xf8000000;   // 2s complement
    //logerror( "ygv608.ay = $%X\n", ygv608.ay );
    break;

  case 35 : case 36 :
    ygv608.dy = (int)(ygv608.regs.s.dy8 & 0x1f) << 8 | (int)ygv608.regs.s.dy0;
    ygv608.dy <<= 7;
    if( ygv608.dy & 0x00080000 ) ygv608.dy |= 0xfff80000;   // 2s complement
    break;

  case 37 : case 38 :
    ygv608.dyx = (int)(ygv608.regs.s.dyx8 & 0x1f) << 8 | (int)ygv608.regs.s.dyx0;
    ygv608.dyx <<= 7;
    if( ygv608.dyx & 0x00080000 ) ygv608.dyx |= 0xfff80000; // 2s complement
    break;

  case 40 : case 41 : case 42 :
    //ShowYGV608Registers();
    break;

  }

}

/*
 *      The rest of this stuff is for debugging only!
 */

#ifdef MAME_DEBUG

//#define SHOW_SOURCE_MODE

#if 0
void dump_block( char *name, UINT8 *block, int len )
{
  int i;

  logerror( "UINT8 %s[] = {\n", name );
  for( i=0; i<len; i++ ) {
    if( i%8 == 0 )
      logerror( " " );
    logerror( "0x%02X, ", block[i] );
    if( i%8 == 7 )
      logerror( "\n" );
  }
  logerror( "};\n" );
}
#endif
READ16_HANDLER( ygv608_debug_trigger )
{
  static int oneshot = 0;

#ifndef SHOW_SOURCE_MODE
  int i;
#endif
  char ascii[16];

  if( oneshot )
    return( 0 );
  oneshot = 1;

  ShowYGV608Registers();

#ifdef SHOW_SOURCE_MODE
#if 0
  dump_block( "ygv608_regs",
	      (UINT8 *)ygv608.regs.b,
	      64 );
  dump_block( "ygv608_pnt",
	      (UINT8 *)ygv608.pattern_name_table,
	      4096 );
  dump_block( "ygv608_sat",
	      (UINT8 *)ygv608.sprite_attribute_table.b,
	      256 );
  dump_block( "ygv608_sdt",
	      (UINT8 *)ygv608.scroll_data_table,
	      512 );
  dump_block( "ygv608_cp",
	      (UINT8 *)ygv608.colour_palette,
	      768 );
#endif

#else

  /*
   *  Dump pattern name table ram
   */
#if 1
  logerror( "Pattern Name Table\n" );
  for( i=0; i<4096; i++ ) {
    if( i % 16 == 0 )
      logerror( "$%04X : ", i );
    logerror( "%02X ", ygv608.pattern_name_table[i] );
    if( ygv608.pattern_name_table[i] >= 0x20)
      ascii[i%16] = ygv608.pattern_name_table[i];
    else
      ascii[i%16] = '.';
    if( i % 16 == 15 )
      logerror( " | %-16.16s\n", ascii );
  }
  logerror( "\n" );
#endif

  /*
   *  Dump scroll table ram
   */

  logerror( "Scroll Table\n" );
  for( i=0; i<512; i++ ) {
    if( i % 16 == 0 )
      logerror( "$%04X : ", i );
    logerror( "%02X ", ygv608.scroll_data_table[0][i] );
    if( ygv608.scroll_data_table[0][i] >= 0x20 )
      ascii[i%16] = ygv608.scroll_data_table[0][i];
    else
      ascii[i%16] = '.';
    if( i % 16 == 15 )
      logerror( " | %-16.16s\n", ascii );
  }
  logerror( "\n" );

#endif

  return( 0 );
}

void ShowYGV608Registers( void )
{
  int p, b;

  logerror( "YGV608 Registers\n" );
  logerror(
       "\tR#00: $%02X : PNYA(%d),B/A(%c),PNY(%d)\n",
	   ygv608.regs.b[0],
	   ygv608.regs.s.r0 & r0_pnya,
	   ((ygv608.regs.s.r0 & r0_b_a) ? 'B' : 'A' ),
	   ygv608.regs.s.r0 & r0_pny);

  logerror(
       "\tR#01: $%02X : PNXA(%d),PNX(%d)\n",
	   ygv608.regs.b[1],
	   ygv608.regs.s.r1 & r1_pnxa,
	   ygv608.regs.s.r1 & r1_pnx);

  logerror(
       "\tR#02: $%02X : CPAW(%d),CPAR(%d),B/A(%d),SCAW(%d),SCAR(%d),SAAW(%d),SAAR(%d)\n",
	   ygv608.regs.b[2],
	   ygv608.regs.s.r2 & r2_cpaw,
	   ygv608.regs.s.r2 & r2_cpar,
	   ygv608.regs.s.r2 & r2_b_a,
	   ygv608.regs.s.r2 & r2_scaw,
	   ygv608.regs.s.r2 & r2_scar,
	   ygv608.regs.s.r2 & r2_saaw,
	   ygv608.regs.s.r2 & r2_saar);

  logerror(
	   "\tR#03: $%02X : SAA($%02X)\n",
	   ygv608.regs.b[3],
	   ygv608.regs.s.saa );

  logerror(
	   "\tR#04: $%02X : SCA($%02X)\n",
	   ygv608.regs.b[4],
	   ygv608.regs.s.sca );

  logerror(
	   "\tR#05: $%02X : CC($%02X)\n",
	   ygv608.regs.b[5],
	   ygv608.regs.s.cc );

  logerror(
	   "\tR#06: $%02X : SBA($%02X)\n",
	   ygv608.regs.b[6],
	   ygv608.regs.s.sba );

  logerror(
	   "\tR#07: $%02X : DSPE(%d),MD(%d),ZRON(%d),FLIP(%d),DCKM(%d)\n",
	   ygv608.regs.b[7],
	   ygv608.regs.s.r7 & r7_dspe,
	   ygv608.regs.s.r7 & r7_md,
	   ygv608.regs.s.r7 & r7_zron,
	   ygv608.regs.s.r7 & r7_flip,
	   ygv608.regs.s.r7 & r7_dckm);

  logerror(
	   "\tR#08: $%02X : HDS(%d),VDS(%d),RLRT(%d),RLSC(%d),PGS(%d)\n",
	   ygv608.regs.b[8],
	   ygv608.regs.s.r8 & r8_hds,
	   ygv608.regs.s.r8 & r8_vds,
	   ygv608.regs.s.r8 & r8_rlrt,
	   ygv608.regs.s.r8 & r8_rlsc,
	   ygv608.regs.s.r8 & r8_pgs);

  logerror(
	   "\tR#11: $%02X : CTPA(%d),CTPB(%d),PRM(%d),CBDR(%d),YSE(%d),SCM(%d)\n",
	   ygv608.regs.b[11],
	   ygv608.regs.s.r11 & r11_ctpa,
	   ygv608.regs.s.r11 & r11_ctpb,
	   ygv608.regs.s.r11 & r11_prm,
	   ygv608.regs.s.r11 & r11_cbdr,
	   ygv608.regs.s.r11 & r11_yse,
	   ygv608.regs.s.r11 & r11_scm);

  logerror(
	   "\tR#40: $%02X : HTL9:8($%02X)=$%06X,HDW(%d)\n",
	   ygv608.regs.b[40],
	   ygv608.regs.s.r40 & r40_htl89, (int)(ygv608.regs.s.r40 & r40_htl89) << 8,
       ygv608.regs.s.r40 & r40_hdw);

  logerror(
	   "\tR#41: $%02X : HDSP($%02X)\n",
	   ygv608.regs.b[41],
	   ygv608.regs.s.hdsp );

  logerror(
	   "\tR#42: $%02X : HTL7:0($%02X)\n",
	   ygv608.regs.b[42],
	   ygv608.regs.s.htl );

  logerror(
	   "\t              HTL=$%03X\n",
	   ( (int)(ygv608.regs.s.r40 & r40_htl89) << 8 ) |
	   ( (int)ygv608.regs.s.htl ) );

  logerror(
	   "\tR#47: $%02X : TB12:5($%02X) = $%06X\n",
	   ygv608.regs.b[47],
	   ygv608.regs.s.tb5, (int)ygv608.regs.s.tb5 << 5 );

  logerror(
	   "\tR#48: $%02X : TB20:13($%02X) = $%06X\n",
	   ygv608.regs.b[48],
	   ygv608.regs.s.tb13, (int)ygv608.regs.s.tb13 << 13 );

  logerror(
	   "\t              TB=$%06X\n",
	   ( (int)ygv608.regs.s.tb13 << 13 ) |
	   ( (int)ygv608.regs.s.tb5 << 5 ) );

  logerror(
	   "\tR#49: $%02X : TN11:4($%02X) = $%04X\n",
	   ygv608.regs.b[49],
	   ygv608.regs.s.tn4, (int)ygv608.regs.s.tn4 << 4 );

  logerror(
	   "ShortCuts:\n" );

  for( p=0; p<2; p++ ) {
    logerror( "\t" );
    for( b=0; b<8; b++ ) {
      logerror( "%02X ", ygv608.base_addr[p][b] );
    }
    logerror( "\n" );
  }
}

#endif  // MAME_DEBUG
