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


    Funtech Super A'Can
    -------------------

    Preliminary driver by Angelo Salese
    Improvements by Harmony


*******************************************************************************

INFO:

    The system unit contains a reset button.

    Controllers:
    - 4 directional buttons
    - A, B, X, Y, buttons
    - Start, select buttons
    - L, R shoulder buttons

STATUS:

    The driver is begging for a re-write or at least a split into video/supracan.c.  It will happen eventually.

    Sound CPU comms and sound chip are completely unknown.

    There are 6 interrupt sources on the 6502 side, all of which use the IRQ line.
    The register at 0x411 is bitmapped to indicate what source(s) are active.
    In priority order from most to least important, they are:

    411 value  How acked                     Notes
    0x40       read reg 0x16 of sound chip   likely timer. snd regs 0x16/0x17 are time constant, write 0 to reg 0x9f to start
    0x04       read at 0x405                 latch 1?  0xcd is magic value
    0x08       read at 0x404                 latch 2?  0xcd is magic value
    0x10       read at 0x409                 unknown, dispatched but not used in startup 6502 code
    0x20       read at 0x40a                 possible periodic like vblank?
    0x80       read reg 0x14 of sound chip   depends on reg 0x14 of sound chip & 0x40: if not set writes 0x8f to reg 0x14,
                                             otherwise writes 0x4f to reg 0x14 and performs additional processing

    Known unemulated graphical effects and issues:
    - All: Sprite sizing is still imperfect.
    - All: Sprites need to be converted to use scanline rendering for proper clipping.
    - All: Improperly-emulated 1bpp ROZ mode, used by the Super A'Can BIOS logo.
    - All: Unimplemented ROZ scaling tables, used by the Super A'Can BIOS logo and Speedy Dragon intro, among others.
    - All: Priorities are largely unknown.
    - C.U.G.: Gameplay backgrounds are broken.
    - Sango Fighter: Possible missing masking on the upper edges of the screen during gameplay.
    - Sango Fighter: Raster effects off by 1 line
    - Sango Fighter: Specifies tiles out of range of video ram??
    - Speedy Dragon: Backgrounds are broken (wrong tile bank/region).
    - Super Taiwanese Baseball League: Does not boot, uses an unemulated DMA type
    - Super Taiwanese Baseball League: Missing window effect applied on tilemaps?
    - The Son of Evil: Many graphical issues.
    - Visible area, looks like it should be 224 pixels high at most, most games need 8 off the top and 8 off the bottom (or a global scroll)
      Sango looks like it needs 16 off the bottom instead
      Visible area is almost certainly 224 as Son of Evil has an explicit check in the vblank handler

    - All: are ALL the layers ROZ capable??

DEBUG TRICKS:

    baseball game debug trick:
    wpset e90020,1f,w
    do pc=5ac40
    ...
    do pc=5acd4
    wpclear
    bp 0269E4
    [ff7be4] <- 0x269ec
    bpclear

***************************************************************************/
#define ADDRESS_MAP_MODERN

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m6502/m6502.h"
#include "imagedev/cartslot.h"
//#include "debugger.h"

#define SOUNDCPU_BOOT_HACK      (1)

#define DRAW_DEBUG_ROZ          (0)

#define DRAW_DEBUG_UNK_SPRITE   (0)

#define DEBUG_PRIORITY          (0)
#define DEBUG_PRIORITY_INDEX    (0) // 0-3

#define VERBOSE_LEVEL   (3)

#define ENABLE_VERBOSE_LOG (1)

typedef struct _acan_dma_regs_t acan_dma_regs_t;
struct _acan_dma_regs_t
{
	UINT32 source[2];
	UINT32 dest[2];
	UINT16 count[2];
	UINT16 control[2];
};

typedef struct _acan_sprdma_regs_t acan_sprdma_regs_t;
struct _acan_sprdma_regs_t
{
	UINT32 src;
	UINT16 src_inc;
	UINT32 dst;
	UINT16 dst_inc;
	UINT16 count;
	UINT16 control;
};

class supracan_state : public driver_device
{
public:
	supracan_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu")
	{
		m_m6502_reset = 0;
	}

	required_device<cpu_device> m_maincpu;
	DECLARE_READ16_MEMBER(supracan_68k_soundram_r);
	DECLARE_WRITE16_MEMBER(supracan_68k_soundram_w);
	DECLARE_READ8_MEMBER(supracan_6502_soundmem_r);
	DECLARE_WRITE8_MEMBER(supracan_6502_soundmem_w);
	DECLARE_WRITE16_MEMBER(supracan_dma_w);
	DECLARE_READ16_MEMBER(supracan_sound_r);
	DECLARE_WRITE16_MEMBER(supracan_sound_w);
	DECLARE_READ16_MEMBER(supracan_video_r);
	DECLARE_WRITE16_MEMBER(supracan_video_w);
	DECLARE_READ16_MEMBER(supracan_vram_r);
	DECLARE_WRITE16_MEMBER(supracan_vram_w);
	DECLARE_WRITE16_MEMBER(paletteram_w);
	acan_dma_regs_t m_acan_dma_regs;
	acan_sprdma_regs_t m_acan_sprdma_regs;

	UINT16 m_m6502_reset;
	UINT8 *m_soundram;
	UINT8 m_soundlatch;
	UINT8 m_soundcpu_irq_src;
	UINT8 m_sound_irq_enable_reg;
	UINT8 m_sound_irq_source_reg;
	UINT8 m_sound_cpu_68k_irq_reg;

	emu_timer *m_video_timer;
	emu_timer *m_hbl_timer;
	emu_timer *m_line_on_timer;
	emu_timer *m_line_off_timer;
	UINT16 *m_vram;
	UINT16 *m_vram_swapped;
	UINT8 *m_vram_addr_swapped;

	UINT16 *m_pram;

	UINT16 m_sprite_count;
	UINT32 m_sprite_base_addr;
	UINT8 m_sprite_flags;

	UINT32 m_tilemap_base_addr[3];
	int m_tilemap_scrollx[3];
	int m_tilemap_scrolly[3];
	UINT16 m_video_flags;
	UINT16 m_tilemap_flags[3];
	UINT16 m_tilemap_mode[3];
	UINT16 m_irq_mask;
	UINT16 m_hbl_mask;

	UINT32 m_roz_base_addr;
	UINT16 m_roz_mode;
	UINT32 m_roz_scrollx;
	UINT32 m_roz_scrolly;
	UINT16 m_roz_tile_bank;
	UINT32 m_roz_unk_base0;
	UINT32 m_roz_unk_base1;
	UINT32 m_roz_unk_base2;
	UINT16 m_roz_coeffa;
	UINT16 m_roz_coeffb;
	UINT16 m_roz_coeffc;
	UINT16 m_roz_coeffd;
	INT32 m_roz_changed;
	INT32 m_roz_cx;
	INT32 m_roz_cy;
	UINT16 m_unk_1d0;

	UINT16 m_video_regs[256];

	bool m_hack_68k_to_6502_access;

	tilemap_t *m_tilemap_sizes[4][4];
	bitmap_ind16 m_sprite_final_bitmap;
	void write_swapped_byte(int offset, UINT8 byte);
};



#if ENABLE_VERBOSE_LOG
INLINE void verboselog(const char *tag, running_machine &machine, int n_level, const char *s_fmt, ...)
{
	if( VERBOSE_LEVEL >= n_level )
	{
		va_list v;
		char buf[ 32768 ];
		va_start( v, s_fmt );
		vsprintf( buf, s_fmt, v );
		va_end( v );
		logerror( "%06x: %s: %s", cpu_get_pc(machine.device(tag)), tag, buf );
	}
}

#else
#define verboselog(w,x,y,z,...)
#endif

static int supracan_tilemap_get_region(running_machine &machine, int layer)
{
	supracan_state *state = machine.driver_data<supracan_state>();

	// HACK!!!
	if (layer==2)
	{
		return 2;
	}


	if (layer==3)
	{
		// roz layer

		int gfx_mode = (state->m_roz_mode & 3);

		switch(gfx_mode)
		{
			case 0:	return 4;
			case 1: return 2;
			case 2: return 1;
			case 3: return 0;
		}
		return 1;
	}
	else
	{
		// normal layers
		int gfx_mode = (state->m_tilemap_mode[layer] & 0x7000) >> 12;

		switch(gfx_mode)
		{
			case 7: return 2;
			case 4: return 1;
			case 2: return 1;
			case 0: return 1;
		}

		return 1;
	}

}

static void supracan_tilemap_get_info_common(running_machine &machine, int layer, tile_data &tileinfo, int count)
{
	supracan_state *state = machine.driver_data<supracan_state>();

	UINT16* supracan_vram = state->m_vram;

	UINT32 base = (state->m_tilemap_base_addr[layer]);
	int gfx_mode = (state->m_tilemap_mode[layer] & 0x7000) >> 12;
	int region = supracan_tilemap_get_region(machine, layer);

	count += base;

	UINT16 tile_bank = 0;
	UINT16 palette_bank = 0;
	switch(gfx_mode)
	{
		case 7:
			tile_bank = 0x1c00;
			palette_bank = 0x00;
			break;

		case 4:
			tile_bank = 0x800;
			palette_bank = 0x00;
			break;

		case 2:
			tile_bank = 0x400;
			palette_bank = 0x00;
			break;

		case 0:
			tile_bank = 0;
			palette_bank = 0x00;
			break;

		default:
			verboselog("maincpu", machine, 0, "Unsupported tilemap mode: %d\n", (state->m_tilemap_mode[layer] & 0x7000) >> 12);
			break;
	}


	if(layer == 2)
	{
		tile_bank = 0x1000;
	}

	int tile = (supracan_vram[count] & 0x03ff) + tile_bank;
	int flipxy = (supracan_vram[count] & 0x0c00)>>10;
	int palette = ((supracan_vram[count] & 0xf000) >> 12) + palette_bank;

	SET_TILE_INFO(region, tile, palette, TILE_FLIPXY(flipxy));
}

// I wonder how different this really is.. my guess, not at all.
static void supracan_tilemap_get_info_roz(running_machine &machine, int layer, tile_data &tileinfo, int count)
{
	supracan_state *state = machine.driver_data<supracan_state>();

	UINT16* supracan_vram = state->m_vram;

	UINT32 base = state->m_roz_base_addr;


	int region = 1;
	UINT16 tile_bank = 0;
	UINT16 palette_bank = 0;

	region = supracan_tilemap_get_region(machine, layer);

	switch(state->m_roz_mode & 3) //FIXME: fix gfx bpp order
	{
		case 0:
			// hack: case for startup logo
			// this isn't understood properly, it's rendering a single 64x64 tile, which for convenience we've rearranged and decoded as 8x8 for the tilemaps
			{
				int tile = 0x880 + ((count & 7)*2);
			//  tile += (count & 0x070) >> 2;

				if (count & 0x20) tile ^= 1;
				tile |= (count & 0xc0)>>2;

				SET_TILE_INFO(region, tile, 0, 0);
				return;
			}


		case 1:
			tile_bank = (state->m_roz_tile_bank & 0xf000) >> 3;
			break;

		case 2:
			tile_bank = (state->m_roz_tile_bank & 0xf000) >> 3;
			break;

		case 3:
			tile_bank = (state->m_roz_tile_bank & 0xf000) >> 3;
			break;
	}

	count += base;

	int tile = (supracan_vram[count] & 0x03ff) + tile_bank;
	int flipxy = (supracan_vram[count] & 0x0c00)>>10;
	int palette = ((supracan_vram[count] & 0xf000) >> 12) + palette_bank;

	SET_TILE_INFO(region, tile, palette, TILE_FLIPXY(flipxy));
}



static TILE_GET_INFO( get_supracan_tilemap0_tile_info )
{

	supracan_tilemap_get_info_common(machine, 0, tileinfo, tile_index);
}

static TILE_GET_INFO( get_supracan_tilemap1_tile_info )
{
	supracan_tilemap_get_info_common(machine, 1, tileinfo, tile_index);
}

static TILE_GET_INFO( get_supracan_tilemap2_tile_info )
{

	supracan_tilemap_get_info_common(machine, 2, tileinfo, tile_index);
}

static TILE_GET_INFO( get_supracan_roz_tile_info )
{

	supracan_tilemap_get_info_roz(machine, 3, tileinfo, tile_index);
}


static VIDEO_START( supracan )
{
	supracan_state *state = machine.driver_data<supracan_state>();
	state->m_sprite_final_bitmap.allocate(1024, 1024, BITMAP_FORMAT_IND16);

	state->m_vram = (UINT16*)machine.region("ram_gfx")->base();
	state->m_vram_swapped = (UINT16*)machine.region("ram_gfx2")->base();
	state->m_vram_addr_swapped = (UINT8*)machine.region("ram_gfx3")->base(); // hack for 1bpp layer at startup

	state->m_tilemap_sizes[0][0] = tilemap_create(machine, get_supracan_tilemap0_tile_info, tilemap_scan_rows, 8, 8, 32, 32);
	state->m_tilemap_sizes[0][1] = tilemap_create(machine, get_supracan_tilemap0_tile_info, tilemap_scan_rows, 8, 8, 64, 32);
	state->m_tilemap_sizes[0][2] = tilemap_create(machine, get_supracan_tilemap0_tile_info, tilemap_scan_rows, 8, 8, 128, 32);
	state->m_tilemap_sizes[0][3] = tilemap_create(machine, get_supracan_tilemap0_tile_info, tilemap_scan_rows, 8, 8, 64, 64);

	state->m_tilemap_sizes[1][0] = tilemap_create(machine, get_supracan_tilemap1_tile_info, tilemap_scan_rows, 8, 8, 32, 32);
	state->m_tilemap_sizes[1][1] = tilemap_create(machine, get_supracan_tilemap1_tile_info, tilemap_scan_rows, 8, 8, 64, 32);
	state->m_tilemap_sizes[1][2] = tilemap_create(machine, get_supracan_tilemap1_tile_info, tilemap_scan_rows, 8, 8, 128, 32);
	state->m_tilemap_sizes[1][3] = tilemap_create(machine, get_supracan_tilemap1_tile_info, tilemap_scan_rows, 8, 8, 64, 64);

	state->m_tilemap_sizes[2][0] = tilemap_create(machine, get_supracan_tilemap2_tile_info, tilemap_scan_rows, 8, 8, 32, 32);
	state->m_tilemap_sizes[2][1] = tilemap_create(machine, get_supracan_tilemap2_tile_info, tilemap_scan_rows, 8, 8, 64, 32);
	state->m_tilemap_sizes[2][2] = tilemap_create(machine, get_supracan_tilemap2_tile_info, tilemap_scan_rows, 8, 8, 128, 32);
	state->m_tilemap_sizes[2][3] = tilemap_create(machine, get_supracan_tilemap2_tile_info, tilemap_scan_rows, 8, 8, 64, 64);

	state->m_tilemap_sizes[3][0] = tilemap_create(machine, get_supracan_roz_tile_info, tilemap_scan_rows, 8, 8, 32, 32);
	state->m_tilemap_sizes[3][1] = tilemap_create(machine, get_supracan_roz_tile_info, tilemap_scan_rows, 8, 8, 64, 32);
	state->m_tilemap_sizes[3][2] = tilemap_create(machine, get_supracan_roz_tile_info, tilemap_scan_rows, 8, 8, 128, 32);
	state->m_tilemap_sizes[3][3] = tilemap_create(machine, get_supracan_roz_tile_info, tilemap_scan_rows, 8, 8, 64, 64);
}

static int get_tilemap_dimensions(running_machine &machine, int &xsize, int &ysize, int layer)
{
	supracan_state *state = (supracan_state *)machine.driver_data<supracan_state>();
	int select;

	xsize = 32;
	ysize = 32;

	if (layer==3) select = (state->m_roz_mode & 0x0f00);
	else select = state->m_tilemap_flags[layer] & 0x0f00;

	switch(select)
	{
		case 0x600:
			xsize = 64;
			ysize = 32;
			return 1;

		case 0xa00:
			xsize = 128;
			ysize = 32;
			return 2;

		case 0xc00:
			xsize = 64;
			ysize = 64;
			return 3;

		default:
			verboselog("maincpu", machine, 0, "Unsupported tilemap size for layer %d: %04x\n", layer, select);
			return 0;
	}
}




static void draw_sprites(running_machine &machine, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	supracan_state *state = machine.driver_data<supracan_state>();
	UINT16 *supracan_vram = state->m_vram;

//      [0]
//      -e-- ---- ---- ---- sprite enable?
//      ---h hhh- ---- ---- Y size (not always right)
//      ---- ---y yyyy yyyy Y position
//      [1]
//      bbbb ---- ---- ---- Tile bank
//      ---- h--- ---- ---- Horizontal flip
//      ---- -v-- ---- ---- Vertical flip
//      ---- --mm ---- ---- Masking mode
//      ---- ---- ---- -www X size
//      [2]
//      zzzz ---- ---- ---- X scale
//      ---- ---x xxxx xxxx X position
//      [3]
//      d--- ---- ---- ---- Direct Sprite (use details from here, not looked up in vram)
//      -ooo oooo oooo oooo Sprite address

	UINT32 skip_count = 0;
	UINT32 start_word = (state->m_sprite_base_addr >> 1) + skip_count * 4;
	UINT32 end_word = start_word + (state->m_sprite_count - skip_count) * 4;
	int region = (state->m_sprite_flags & 1) ? 0 : 1; //8bpp : 4bpp

//  printf("frame\n");
	#define VRAM_MASK (0xffff)

   for(int i = start_word; i < end_word; i += 4)
	{
		int x = supracan_vram[i+2] & 0x01ff;
		int y = supracan_vram[i+0] & 0x01ff;

		int sprite_offset = (supracan_vram[i+3])<< 1;

		int bank = (supracan_vram[i+1] & 0xf000) >> 12;
		//int mask = (supracan_vram[i+1] & 0x0300) >> 8;
		int sprite_xflip = (supracan_vram[i+1] & 0x0800) >> 11;
		int sprite_yflip = (supracan_vram[i+1] & 0x0400) >> 10;
		//int xscale = (supracan_vram[i+2] & 0xf000) >> 12;
		const gfx_element *gfx = machine.gfx[region];




		// wraparound
		if (y>=0x180) y-=0x200;
		if (x>=0x180) x-=0x200;

		if((supracan_vram[i+0] & 0x4000))
		{

		#if 0
			printf("%d (unk %02x) (enable %02x) (unk Y2 %02x, %02x) (y pos %02x) (bank %01x) (flip %01x) (unknown %02x) (x size %02x) (xscale %01x) (unk %01x) (xpos %02x) (code %04x)\n", i,
				(supracan_vram[i+0] & 0x8000) >> 15,
				(supracan_vram[i+0] & 0x4000) >> 14,
				(supracan_vram[i+0] & 0x2000) >> 13,
				(supracan_vram[i+0] & 0x1e00) >> 8,
				(supracan_vram[i+0] & 0x01ff),
				(supracan_vram[i+1] & 0xf000) >> 12,
				(supracan_vram[i+1] & 0x0c00) >> 10,
				(supracan_vram[i+1] & 0x03f0) >> 4,
				(supracan_vram[i+1] & 0x000f),
				(supracan_vram[i+2] & 0xf000) >> 12,
				(supracan_vram[i+2] & 0x0e00) >> 8,
				(supracan_vram[i+2] & 0x01ff) >> 0,
				(supracan_vram[i+3] & 0xffff));
		#endif


			if (supracan_vram[i+3] &0x8000)
			{
				UINT16 data = supracan_vram[i+3];
				int tile = (bank * 0x200) + (data & 0x03ff);

				int palette = (data & 0xf000) >> 12; // this might not be correct, due to the &0x8000 condition above this would force all single tile sprites to be using palette >=0x8 only

				//printf("sprite data %04x %04x %04x %04x\n", supracan_vram[i+0] , supracan_vram[i+1] , supracan_vram[i+2] ,supracan_vram[i+3]  );

				drawgfx_transpen(bitmap,cliprect,gfx,tile,palette,sprite_xflip,sprite_yflip,
					x,
					y,
					0);

			}
			else
			{
				int xsize = 1 << (supracan_vram[i+1] & 7);
				int ysize = ((supracan_vram[i+0] & 0x1e00) >> 9) + 1;

				// I think the xsize must influence the ysize somehow, there are too many conflicting cases otherwise
				// there don't appear to be any special markers in the actual looked up tile data to indicate skip / end of list

				for(int ytile = 0; ytile < ysize; ytile++)
				{
					for(int xtile = 0; xtile< xsize; xtile++)
					{
						UINT16 data = supracan_vram[(sprite_offset+ytile*xsize+xtile)&VRAM_MASK];
						int tile = (bank * 0x200) + (data & 0x03ff);
						int palette = (data & 0xf000) >> 12;

						int xpos, ypos;

						if (!sprite_yflip)
						{
							ypos = y + ytile*8;
						}
						else
						{
							ypos = y - (ytile+1)*8;
							ypos += ysize*8;
						}

						if (!sprite_xflip)
						{
							xpos = x + xtile*8;
						}
						else
						{
							xpos = x - (xtile+1)*8;
							xpos += xsize*8;
						}

						int tile_xflip = sprite_xflip ^ ((data & 0x0800)>>11);
						int tile_yflip = sprite_yflip ^ ((data & 0x0400)>>10);

						drawgfx_transpen(bitmap,cliprect,gfx,tile,palette,tile_xflip,tile_yflip,xpos,ypos,0);
					}
				}
			}

#if 0
			if(xscale == 0) continue;
			UINT32 delta = (1 << 17) / xscale;
			for(int sy = 0; sy < ysize*8; sy++)
			{
				UINT16 *src = &sprite_bitmap->pix16(sy);
				UINT16 *dst = &bitmap.pix16(y + sy);
				UINT32 dx = x << 16;
				for(int sx = 0; sx < xsize*8; sx++)
				{
					dst[dx >> 16] = src[sx];
					dx += delta;
				}
			}
#endif

		}
	}
}



static void mark_active_tilemap_all_dirty(running_machine &machine, int layer)
{
	supracan_state *state = (supracan_state *)machine.driver_data<supracan_state>();
	int xsize = 0;
	int ysize = 0;

	int which_tilemap_size;

	which_tilemap_size = get_tilemap_dimensions(machine, xsize, ysize, layer);
//  for (int i=0;i<4;i++)
//    tilemap_mark_all_tiles_dirty(state->m_tilemap_sizes[layer][i]);
	state->m_tilemap_sizes[layer][which_tilemap_size]->mark_all_dirty();
}



/* draws ROZ with linescroll OR columnscroll to 16-bit indexed bitmap */
static void supracan_suprnova_draw_roz(running_machine &machine, bitmap_ind16 &bitmap, const rectangle &cliprect, tilemap_t *tmap, UINT32 startx, UINT32 starty, int incxx, int incxy, int incyx, int incyy, int wraparound/*, int columnscroll, UINT32* scrollram*/, int transmask)
{
	//bitmap_ind16 *destbitmap = bitmap;
	bitmap_ind16 &srcbitmap = tmap->pixmap();
	//bitmap_ind16 &srcbitmapflags = tmap->flagsmap();
	const int xmask = srcbitmap.width()-1;
	const int ymask = srcbitmap.height()-1;
	const int widthshifted = srcbitmap.width() << 16;
	const int heightshifted = srcbitmap.height() << 16;
	UINT32 cx;
	UINT32 cy;
	int x;
	int sx;
	int sy;
	int ex;
	int ey;
	UINT16 *dest;
//  UINT8* destflags;
//  UINT8 *pri;
	//const UINT16 *src;
	//const UINT8 *maskptr;
	//int destadvance = destbitmap->bpp / 8;

	/* pre-advance based on the cliprect */
	startx += cliprect.min_x * incxx + cliprect.min_y * incyx;
	starty += cliprect.min_x * incxy + cliprect.min_y * incyy;

	/* extract start/end points */
	sx = cliprect.min_x;
	sy = cliprect.min_y;
	ex = cliprect.max_x;
	ey = cliprect.max_y;

	{
		/* loop over rows */
		while (sy <= ey)
		{

			/* initialize X counters */
			x = sx;
			cx = startx;
			cy = starty;

			/* get dest and priority pointers */
			dest = &bitmap.pix16(sy, sx);
			//destflags = &bitmapflags->pix8(sy, sx);

			/* loop over columns */
			while (x <= ex)
			{
				if ((wraparound) || (cx < widthshifted && cy < heightshifted)) // not sure how this will cope with no wraparound, but row/col scroll..
				{
					#if 0
					if (columnscroll)
					{
						int scroll = 0;//scrollram[(cx>>16)&0x3ff]);


						UINT16 data = &srcbitmap.pix16(
												((cy >> 16) - scroll) & ymask,
												(cx >> 16) & xmask)[0];

						if ((data & transmask)!=0)
							dest[0] = data;

						//destflags[0] = &srcbitmapflags.pix8(((cy >> 16) - scrollram[(cx>>16)&0x3ff]) & ymask, (cx >> 16) & xmask)[0];
					}
					else
					#endif
					{
						int scroll = 0;//scrollram[(cy>>16)&0x3ff]);
						UINT16 data =  srcbitmap.pix16(
											   (cy >> 16) & ymask,
											   ((cx >> 16) - scroll) & xmask);


						if ((data & transmask)!=0)
							dest[0] = data;

						//destflags[0] = &srcbitmapflags.pix8((cy >> 16) & ymask, ((cx >> 16) - scrollram[(cy>>16)&0x3ff]) & xmask)[0];
					}
				}

				/* advance in X */
				cx += incxx;
				cy += incxy;
				x++;
				dest++;
//            destflags++;
//            pri++;
			}

			/* advance in Y */
			startx += incyx;
			starty += incyy;
			sy++;
		}
	}
}


// VIDEO FLAGS                  ROZ MODE            TILEMAP FLAGS
//
//  Bit                         Bit                 Bit
// 15-9: Unknown                15-13: Priority?    15-13: Priority?
//    8: X ht. (256/320)        12: Unknown         12: Unknown
//    7: Tilemap 0 enable       11-8: Dims          11-8: Dims
//    6: Tilemap 1 enable       7-6: Unknown        7-6: Unknown
//    5: Tilemap 2 enable?      5: Wrap             5: Wrap
//    3: Sprite enable          4-2: Unknown        4-2: Mosaic
//    2: ROZ enable             1-0: Bit Depth      1-0: Bit Depth
//  1-0: Unknown

//                      Video Flags                 ROZ Mode                    Tilemap 0   Tilemap 1   Tilemap 2   VF Unk0
// A'Can logo:          120e: 0001 0010 0000 1110   4020: 0100 0000 0010 0000   4620        ----        ----        0x09
// Boomzoo Intro:       9a82: 1001 1010 1000 0010   0402: 0000 0100 0000 0010   6400        6400        4400        0x4d
// Boomzoo Title:       9acc: 1001 1010 1100 1100   0402: 0000 0100 0000 0010   6400        6400        4400        0x4d
// C.U.G. Intro:        11c8: 0001 0001 1100 1000   0402: 0000 0100 0000 0010   2400        4400        6400        0x08
// C.U.G. Title:        11cc: 0001 0001 1100 1100   0602: 0000 0110 0000 0010   2600        4600        ----        0x08
// Speedy Dragon Logo:  0388: 0000 0011 1000 1000   4020: 0100 0000 0010 0000   6c20        6c20        2600        0x01
// Speedy Dragon Title: 038c: 0000 0011 1000 1100   2603: 0010 0110 0000 0011   6c20        2c20        2600        0x01
// Sango Fighter Intro: 03c8: 0000 0011 1100 1000   ----: ---- ---- ---- ----   6c20        4620        ----        0x01
// Sango Fighter Game:  03ce: 0000 0011 1100 1110   0622: 0000 0110 0010 0010   2620        4620        ----        0x01

static SCREEN_UPDATE_IND16( supracan )
{
	supracan_state *state = (supracan_state *)screen.machine().driver_data<supracan_state>();



	// treat the sprites as frame-buffered and only update the buffer when drawing scanline 0 - this might not be true!

	if (0)
	{
		if (cliprect.min_y == 0x00)
		{
			const rectangle &visarea = screen.visible_area();

			state->m_sprite_final_bitmap.fill(0x00, visarea);
			bitmap.fill(0x80, visarea);

			draw_sprites(screen.machine(), state->m_sprite_final_bitmap, visarea);
		}
	}
	else
	{

		state->m_sprite_final_bitmap.fill(0x00, cliprect);
		bitmap.fill(0x80, cliprect);

		draw_sprites(screen.machine(), state->m_sprite_final_bitmap, cliprect);
	}



	// mix screen
	int xsize = 0, ysize = 0;
//  int tilemap_num;
	int which_tilemap_size;
	int priority = 0;


	for (int pri=7;pri>=0;pri--)
	{

		for (int layer = 3; layer >=0; layer--)
		{
		//  popmessage("%04x\n",state->m_video_flags);
			int enabled = 0;

			if(state->m_video_flags & 0x04)
				if (layer==3) enabled = 1;

			if(state->m_video_flags & 0x80)
				if (layer==0) enabled = 1;

			if(state->m_video_flags & 0x40)
				if (layer==1) enabled = 1;

			if(state->m_video_flags & 0x20)
				if (layer==2) enabled = 1;


			if (layer==3) priority = ((state->m_roz_mode >> 13) & 7); // roz case
			else priority = ((state->m_tilemap_flags[layer] >> 13) & 7); // normal cases


			if (priority==pri)
			{
//            tilemap_num = layer;
				which_tilemap_size = get_tilemap_dimensions(screen.machine(), xsize, ysize, layer);
				bitmap_ind16 &src_bitmap = state->m_tilemap_sizes[layer][which_tilemap_size]->pixmap();
				int gfx_region = supracan_tilemap_get_region(screen.machine(), layer);
				int transmask = 0xff;

				switch (gfx_region)
				{
					case 0:transmask = 0xff; break;
					case 1:transmask = 0x0f; break;
					case 2:transmask = 0x03; break;
					case 3:transmask = 0x01; break;
					case 4:transmask = 0x01; break;
				}

				if (enabled)
				{
					if (layer != 3) // standard layers, NOT roz
					{

						int wrap = (state->m_tilemap_flags[layer] & 0x20);

						int scrollx = state->m_tilemap_scrollx[layer];
						int scrolly = state->m_tilemap_scrolly[layer];

						if (scrollx&0x8000) scrollx-= 0x10000;
						if (scrolly&0x8000) scrolly-= 0x10000;

						int mosaic_count = (state->m_tilemap_flags[layer] & 0x001c) >> 2;
						int mosaic_mask = 0xffffffff << mosaic_count;

						int y,x;
						// yes, it will draw a single line if you specify a cliprect as such (partial updates...)

						for (y=cliprect.min_y;y<=cliprect.max_y;y++)
						{
							// these will have to change to ADDR32 etc. once alpha blending is supported
							UINT16* screen = &bitmap.pix16(y);

							int actualy = y&mosaic_mask;

							int realy = actualy+scrolly;

							if (!wrap)
								if (scrolly+y < 0 || scrolly+y > ((ysize*8)-1))
									continue;


							UINT16* src = &src_bitmap.pix16((realy)&((ysize*8)-1));

							for (x=cliprect.min_x;x<=cliprect.max_x;x++)
							{
								int actualx = x & mosaic_mask;
								int realx = actualx+scrollx;

								if (!wrap)
									if (scrollx+x < 0 || scrollx+x > ((xsize*8)-1))
										continue;

								UINT16 srcpix = src[(realx)&((xsize*8)-1)];

								if ((srcpix & transmask) != 0)
									screen[x] = srcpix;
							}
						}
					}
					else
					{
						int wrap = state->m_roz_mode & 0x20;

						int incxx = (state->m_roz_coeffa);
						int incyy = (state->m_roz_coeffd);

						int incxy = (state->m_roz_coeffc);
						int incyx = (state->m_roz_coeffb);

						int scrollx = (state->m_roz_scrollx);
						int scrolly = (state->m_roz_scrolly);






						if (incyx & 0x8000) incyx -= 0x10000;
						if (incxy & 0x8000) incxy -= 0x10000;

						if (incyy & 0x8000) incyy -= 0x10000;
						if (incxx & 0x8000) incxx -= 0x10000;

						//popmessage("%04x %04x\n",state->m_video_flags, state->m_roz_mode);

						// roz mode..
						//4020 = enabled speedyd
						//6c22 = enabled speedyd
						//2c22 = enabled speedyd
						//4622 = disabled jttlaugh
						//2602 = disabled monopoly
						//0402 = disabled (sango title)
						// or is it always enabled, and only corrupt because we don't clear ram properly?
						// (probably not this register?)

						if (!(state->m_roz_mode & 0x0200) && (state->m_roz_mode&0xf000) ) // HACK - Not Trusted, Acan Logo, Speedy Dragon Intro ,Speed Dragon Bonus stage need it.  Monopoly and JTT *don't* causes graphical issues
						{
							// NOT accurate, causes issues when the attract mode loops and the logo is shown the 2nd time in some games - investigate
							for (int y=cliprect.min_y;y<=cliprect.max_y;y++)
							{
								rectangle clip(cliprect.min_x, cliprect.max_x, y, y);

								scrollx = (state->m_roz_scrollx);
								scrolly = (state->m_roz_scrolly);
								incxx = (state->m_roz_coeffa);

								incxx += state->m_vram[state->m_roz_unk_base0/2 + y];

								scrollx += state->m_vram[state->m_roz_unk_base1/2 + y*2] << 16;
								scrollx += state->m_vram[state->m_roz_unk_base1/2 + y*2 + 1];

								scrolly += state->m_vram[state->m_roz_unk_base2/2 + y*2] << 16;
								scrolly += state->m_vram[state->m_roz_unk_base2/2 + y*2 + 1];

								if (incxx & 0x8000) incxx -= 0x10000;


								if (state->m_vram[state->m_roz_unk_base0/2 + y]) // incxx = 0, no draw?
									supracan_suprnova_draw_roz(screen.machine(), bitmap, clip, state->m_tilemap_sizes[layer][which_tilemap_size], scrollx<<8, scrolly<<8, incxx<<8, incxy<<8, incyx<<8, incyy<<8, wrap, transmask);
							}
						}
						else
						{
							supracan_suprnova_draw_roz(screen.machine(), bitmap, cliprect, state->m_tilemap_sizes[layer][which_tilemap_size], scrollx<<8, scrolly<<8, incxx<<8, incxy<<8, incyx<<8, incyy<<8, wrap, transmask);
						}
					}
				}
			}
		}
	}


	// just draw the sprites on top for now
	if(state->m_video_flags & 0x08)
	{
		for (int y=cliprect.min_y;y<=cliprect.max_y;y++)
		{
			UINT16* src = &state->m_sprite_final_bitmap.pix16(y);
			UINT16* dst = &bitmap.pix16(y);

			for (int x=cliprect.min_x;x<=cliprect.max_x;x++)
			{
				UINT16 dat = src[x];
				if (dat) dst[x] = dat;
			}
		}
	}

	return 0;
}


WRITE16_MEMBER( supracan_state::supracan_dma_w )
{
	acan_dma_regs_t *acan_dma_regs = &m_acan_dma_regs;
	int ch = (offset < 0x10/2) ? 0 : 1;
	address_space *mem = m_maincpu->memory().space(AS_PROGRAM);

	switch(offset)
	{
		case 0x00/2: // Source address MSW
		case 0x10/2:
			verboselog("maincpu", machine(), 0, "supracan_dma_w: source msw %d: %04x\n", ch, data);
			acan_dma_regs->source[ch] &= 0x0000ffff;
			acan_dma_regs->source[ch] |= data << 16;
			break;
		case 0x02/2: // Source address LSW
		case 0x12/2:
			verboselog("maincpu", machine(), 0, "supracan_dma_w: source lsw %d: %04x\n", ch, data);
			acan_dma_regs->source[ch] &= 0xffff0000;
			acan_dma_regs->source[ch] |= data;
			break;
		case 0x04/2: // Destination address MSW
		case 0x14/2:
			verboselog("maincpu", machine(), 0, "supracan_dma_w: dest msw %d: %04x\n", ch, data);
			acan_dma_regs->dest[ch] &= 0x0000ffff;
			acan_dma_regs->dest[ch] |= data << 16;
			break;
		case 0x06/2: // Destination address LSW
		case 0x16/2:
			verboselog("maincpu", machine(), 0, "supracan_dma_w: dest lsw %d: %04x\n", ch, data);
			acan_dma_regs->dest[ch] &= 0xffff0000;
			acan_dma_regs->dest[ch] |= data;
			break;
		case 0x08/2: // Byte count
		case 0x18/2:
			verboselog("maincpu", machine(), 0, "supracan_dma_w: count %d: %04x\n", ch, data);
			acan_dma_regs->count[ch] = data;
			break;
		case 0x0a/2: // Control
		case 0x1a/2:
			verboselog("maincpu", machine(), 0, "supracan_dma_w: control %d: %04x\n", ch, data);
			if(data & 0x8800)
			{
//            if(data & 0x2000)
//                acan_dma_regs->source-=2;
				logerror("supracan_dma_w: Kicking off a DMA from %08x to %08x, %d bytes (%04x)\n", acan_dma_regs->source[ch], acan_dma_regs->dest[ch], acan_dma_regs->count[ch] + 1, data);

				for(int i = 0; i <= acan_dma_regs->count[ch]; i++)
				{
					if(data & 0x1000)
					{
						mem->write_word(acan_dma_regs->dest[ch], mem->read_word(acan_dma_regs->source[ch]));
						acan_dma_regs->dest[ch]+=2;
						acan_dma_regs->source[ch]+=2;
						if(data & 0x0100)
							if((acan_dma_regs->dest[ch] & 0xf) == 0)
								acan_dma_regs->dest[ch]-=0x10;
					}
					else
					{
						mem->write_byte(acan_dma_regs->dest[ch], mem->read_byte(acan_dma_regs->source[ch]));
						acan_dma_regs->dest[ch]++;
						acan_dma_regs->source[ch]++;
					}
				}
			}
			else if(data != 0x0000) // fake DMA, used by C.U.G.
			{
				verboselog("maincpu", machine(), 0, "supracan_dma_w: Unknown DMA kickoff value of %04x (other regs %08x, %08x, %d)\n", data, acan_dma_regs->source[ch], acan_dma_regs->dest[ch], acan_dma_regs->count[ch] + 1);
				fatalerror("supracan_dma_w: Unknown DMA kickoff value of %04x (other regs %08x, %08x, %d)",data, acan_dma_regs->source[ch], acan_dma_regs->dest[ch], acan_dma_regs->count[ch] + 1);
			}
			break;
		default:
			verboselog("maincpu", machine(), 0, "supracan_dma_w: Unknown register: %08x = %04x & %04x\n", 0xe90020 + (offset << 1), data, mem_mask);
			break;
	}
}

#if 0
WRITE16_MEMBER( supracan_state::supracan_pram_w )
{
	m_pram[offset] &= ~mem_mask;
	m_pram[offset] |= data & mem_mask;
}
#endif

// swap address around so that 64x64 tile can be decoded as 8x8 tiles..
void supracan_state::write_swapped_byte( int offset, UINT8 byte )
{
	int swapped_offset = BITSWAP32(offset, 31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,2,1,0,6,5,4,3);

	m_vram_addr_swapped[swapped_offset] = byte;
}


READ16_MEMBER( supracan_state::supracan_vram_r )
{
	return m_vram[offset];
}


WRITE16_MEMBER( supracan_state::supracan_vram_w )
{
	COMBINE_DATA(&m_vram[offset]);

	// store a byteswapped vesrion for easier gfx-decode
	data = m_vram[offset];
	data = ((data & 0x00ff)<<8) | ((data & 0xff00)>>8);
	m_vram_swapped[offset] = data;

	// hack for 1bpp layer at startup
	write_swapped_byte(offset*2+1, (data & 0xff00)>>8);
	write_swapped_byte(offset*2, (data & 0x00ff));

	// mark tiles of each depth as dirty
	gfx_element_mark_dirty(machine().gfx[0], (offset*2)/(64));
	gfx_element_mark_dirty(machine().gfx[1], (offset*2)/(32));
	gfx_element_mark_dirty(machine().gfx[2], (offset*2)/(16));
	gfx_element_mark_dirty(machine().gfx[3], (offset*2)/(512));
	gfx_element_mark_dirty(machine().gfx[4], (offset*2)/(8));

}

static ADDRESS_MAP_START( supracan_mem, AS_PROGRAM, 16, supracan_state )
	AM_RANGE( 0x000000, 0x3fffff ) AM_ROM AM_REGION( "cart", 0 )
	AM_RANGE( 0xe80200, 0xe80201 ) AM_READ_PORT("P1")
	AM_RANGE( 0xe80202, 0xe80203 ) AM_READ_PORT("P2")
	AM_RANGE( 0xe80208, 0xe80209 ) AM_READ_PORT("P3")
	AM_RANGE( 0xe8020c, 0xe8020d ) AM_READ_PORT("P4")
	AM_RANGE( 0xe80000, 0xe8ffff ) AM_READWRITE( supracan_68k_soundram_r, supracan_68k_soundram_w )
	AM_RANGE( 0xe90000, 0xe9001f ) AM_READWRITE( supracan_sound_r, supracan_sound_w )
	AM_RANGE( 0xe90020, 0xe9003f ) AM_WRITE( supracan_dma_w )
	AM_RANGE( 0xf00000, 0xf001ff ) AM_READWRITE( supracan_video_r, supracan_video_w )
	AM_RANGE( 0xf00200, 0xf003ff ) AM_RAM AM_WRITE_LEGACY(paletteram16_xBBBBBGGGGGRRRRR_word_w) AM_BASE_GENERIC(paletteram)
	AM_RANGE( 0xf40000, 0xf5ffff ) AM_READWRITE(supracan_vram_r, supracan_vram_w)
	AM_RANGE( 0xfc0000, 0xfdffff ) AM_MIRROR(0x30000) AM_RAM /* System work ram */
ADDRESS_MAP_END

READ8_MEMBER( supracan_state::supracan_6502_soundmem_r )
{
	address_space *mem = m_maincpu->memory().space(AS_PROGRAM);
	UINT8 data = m_soundram[offset];

	switch(offset)
	{
#if SOUNDCPU_BOOT_HACK
		case 0x300: // HACK to make games think the sound CPU is always reporting 'OK'.
			return 0xff;
#endif

		case 0x410: // Sound IRQ enable
			data = m_sound_irq_enable_reg;
			if(!mem->debugger_access()) verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 0, "supracan_soundreg_r: IRQ enable: %04x\n", data);
			if(!mem->debugger_access())
			{
				if(m_sound_irq_enable_reg & m_sound_irq_source_reg)
				{
					device_set_input_line(machine().device("soundcpu"), 0, ASSERT_LINE);
				}
				else
				{
					device_set_input_line(machine().device("soundcpu"), 0, CLEAR_LINE);
				}
			}
			break;
		case 0x411: // Sound IRQ source
			data = m_sound_irq_source_reg;
			m_sound_irq_source_reg = 0;
			if(!mem->debugger_access()) verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 3, "supracan_soundreg_r: IRQ source: %04x\n", data);
			if(!mem->debugger_access())
			{
				device_set_input_line(machine().device("soundcpu"), 0, CLEAR_LINE);
			}
			break;
		case 0x420:
			if(!mem->debugger_access()) verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 3, "supracan_soundreg_r: Sound hardware status? (not yet implemented): %02x\n", 0);
			break;
		case 0x422:
			if(!mem->debugger_access()) verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 3, "supracan_soundreg_r: Sound hardware data? (not yet implemented): %02x\n", 0);
			break;
		case 0x404:
		case 0x405:
		case 0x409:
		case 0x414:
		case 0x416:
			// Intentional fall-through
		default:
			if(offset >= 0x300 && offset < 0x500)
			{
				if(!mem->debugger_access()) verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 0, "supracan_soundreg_r: Unknown register %04x\n", offset);
			}
			break;
	}

	return data;
}

WRITE8_MEMBER( supracan_state::supracan_6502_soundmem_w )
{
	switch(offset)
	{
		case 0x407:
			if(m_sound_cpu_68k_irq_reg &~ data)
			{
				verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 0, "supracan_soundreg_w: sound_cpu_68k_irq_reg: %04x: Triggering M68k IRQ\n", data);
				device_set_input_line(m_maincpu, 7, HOLD_LINE);
			}
			else
			{
				verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 0, "supracan_soundreg_w: sound_cpu_68k_irq_reg: %04x\n", data);
			}
			m_sound_cpu_68k_irq_reg = data;
			break;
		case 0x410:
			m_sound_irq_enable_reg = data;
			verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 0, "supracan_soundreg_w: IRQ enable: %02x\n", data);
			break;
		case 0x420:
			verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 3, "supracan_soundreg_w: Sound hardware reg data? (not yet implemented): %02x\n", data);
			break;
		case 0x422:
			verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 3, "supracan_soundreg_w: Sound hardware reg addr? (not yet implemented): %02x\n", data);
			break;
		default:
			if(offset >= 0x300 && offset < 0x500)
			{
				verboselog(m_hack_68k_to_6502_access ? "maincpu" : "soundcpu", machine(), 0, "supracan_soundreg_w: Unknown register %04x = %02x\n", offset, data);
			}
			m_soundram[offset] = data;
			break;
	}
}

static ADDRESS_MAP_START( supracan_sound_mem, AS_PROGRAM, 8, supracan_state )
	AM_RANGE( 0x0000, 0xffff ) AM_READWRITE(supracan_6502_soundmem_r, supracan_6502_soundmem_w) AM_BASE(m_soundram)
ADDRESS_MAP_END

static INPUT_PORTS_START( supracan )
	PORT_START("P1")
	PORT_DIPNAME( 0x01, 0x00, "SYSTEM" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("P1 Button R")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("P1 Button L")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("P1 Button Y")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("P1 Button X")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_NAME("P1 Joypad Right")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_NAME("P1 Joypad Left")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_NAME("P1 Joypad Down")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_NAME("P1 Joypad Up")
	PORT_DIPNAME( 0x1000, 0x0000, "SYSTEM" )
	PORT_DIPSETTING(    0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x1000, DEF_STR( On ) )
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_START1 )
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("P1 Button B")
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("P1 Button A")

	PORT_START("P2")
	PORT_DIPNAME( 0x01, 0x00, "SYSTEM" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_NAME("P2 Button R")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_NAME("P2 Button L")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_NAME("P2 Button Y")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("P2 Button X")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_NAME("P2 Joypad Right")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_NAME("P2 Joypad Left")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_NAME("P2 Joypad Down")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_NAME("P2 Joypad Up")
	PORT_DIPNAME( 0x1000, 0x0000, "SYSTEM" )
	PORT_DIPSETTING(    0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x1000, DEF_STR( On ) )
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_START2 )
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_NAME("P2 Button B")
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("P2 Button A")

	PORT_START("P3")
	PORT_DIPNAME( 0x01, 0x00, "SYSTEM" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(3) PORT_NAME("P3 Button R")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(3) PORT_NAME("P3 Button L")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(3) PORT_NAME("P3 Button Y")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(3) PORT_NAME("P3 Button X")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(3) PORT_NAME("P3 Joypad Right")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(3) PORT_NAME("P3 Joypad Left")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(3) PORT_NAME("P3 Joypad Down")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(3) PORT_NAME("P3 Joypad Up")
	PORT_DIPNAME( 0x1000, 0x0000, "SYSTEM" )
	PORT_DIPSETTING(    0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x1000, DEF_STR( On ) )
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_START3 )
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(3) PORT_NAME("P3 Button B")
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(3) PORT_NAME("P3 Button A")

	PORT_START("P4")
	PORT_DIPNAME( 0x01, 0x00, "SYSTEM" )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x01, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x02, DEF_STR( On ) )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x04, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unknown ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x08, DEF_STR( On ) )
	PORT_BIT(0x0010, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(4) PORT_NAME("P4 Button R")
	PORT_BIT(0x0020, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(4) PORT_NAME("P4 Button L")
	PORT_BIT(0x0040, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(4) PORT_NAME("P4 Button Y")
	PORT_BIT(0x0080, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(4) PORT_NAME("P4 Button X")
	PORT_BIT(0x0100, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(4) PORT_NAME("P4 Joypad Right")
	PORT_BIT(0x0200, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(4) PORT_NAME("P4 Joypad Left")
	PORT_BIT(0x0400, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(4) PORT_NAME("P4 Joypad Down")
	PORT_BIT(0x0800, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(4) PORT_NAME("P4 Joypad Up")
	PORT_DIPNAME( 0x1000, 0x0000, "SYSTEM" )
	PORT_DIPSETTING(    0x0000, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x1000, DEF_STR( On ) )
	PORT_BIT(0x2000, IP_ACTIVE_HIGH, IPT_START4 )
	PORT_BIT(0x4000, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(4) PORT_NAME("P4 Button B")
	PORT_BIT(0x8000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(4) PORT_NAME("P4 Button A")
INPUT_PORTS_END

static PALETTE_INIT( supracan )
{
	// Used for debugging purposes for now
	//#if 0
	int i, r, g, b;

	for( i = 0; i < 32768; i++ )
	{
		r = (i & 0x1f) << 3;
		g = ((i >> 5) & 0x1f) << 3;
		b = ((i >> 10) & 0x1f) << 3;
		palette_set_color_rgb( machine, i, r, g, b );
	}
	//#endif
}

WRITE16_MEMBER( supracan_state::supracan_68k_soundram_w )
{
	address_space *mem = m_maincpu->memory().space(AS_PROGRAM);
	m_soundram[offset*2 + 1] = data & 0xff;
	m_soundram[offset*2 + 0] = data >> 8;

	if(offset*2 < 0x500 && offset*2 >= 0x300)
	{
		if(mem_mask & 0xff00)
		{
			m_hack_68k_to_6502_access = true;
			supracan_6502_soundmem_w(*mem, offset*2, data >> 8);
			m_hack_68k_to_6502_access = false;
		}
		if(mem_mask & 0x00ff)
		{
			m_hack_68k_to_6502_access = true;
			supracan_6502_soundmem_w(*mem, offset*2 + 1, data & 0xff);
			m_hack_68k_to_6502_access = false;
		}
	}
}

READ16_MEMBER( supracan_state::supracan_68k_soundram_r )
{
	address_space *mem = m_maincpu->memory().space(AS_PROGRAM);
	UINT16 val = m_soundram[offset*2 + 0] << 8;
	val |= m_soundram[offset*2 + 1];

	if(offset*2 >= 0x300 && offset*2 < 0x500)
	{
		val = 0;
		if(mem_mask & 0xff00)
		{
			m_hack_68k_to_6502_access = true;
			val |= supracan_6502_soundmem_r(*mem, offset*2) << 8;
			m_hack_68k_to_6502_access = false;
		}
		if(mem_mask & 0x00ff)
		{
			m_hack_68k_to_6502_access = true;
			val |= supracan_6502_soundmem_r(*mem, offset*2 + 1);
			m_hack_68k_to_6502_access = false;
		}
	}

	return val;
}

READ16_MEMBER( supracan_state::supracan_sound_r )
{
	UINT16 data = 0;

	switch( offset )
	{
		default:
			verboselog("maincpu", machine(), 0, "supracan_sound_r: Unknown register: (%08x) & %04x\n", 0xe90000 + (offset << 1), mem_mask);
			break;
	}

	return data;
}

WRITE16_MEMBER( supracan_state::supracan_sound_w )
{
	switch ( offset )
	{
		case 0x000a/2:  /* Sound cpu IRQ request. */
			device_set_input_line(machine().device("soundcpu"), 0, ASSERT_LINE);
			break;
		case 0x001c/2:	/* Sound cpu control. Bit 0 tied to sound cpu RESET line */
			if(data & 0x01)
			{
				if(!m_m6502_reset)
				{
					/* Reset and enable the sound cpu */
#if !(SOUNDCPU_BOOT_HACK)
					cputag_set_input_line(machine(), "soundcpu", INPUT_LINE_HALT, CLEAR_LINE);
					machine().device("soundcpu")->reset();
#endif
				}
				m_m6502_reset = data & 0x01;
			}
			else
			{
				/* Halt the sound cpu */
				cputag_set_input_line(machine(), "soundcpu", INPUT_LINE_HALT, ASSERT_LINE);
			}
			verboselog("maincpu", machine(), 0, "sound cpu ctrl: %04x\n", data);
			break;
		default:
			verboselog("maincpu", machine(), 0, "supracan_sound_w: Unknown register: %08x = %04x & %04x\n", 0xe90000 + (offset << 1), data, mem_mask);
			break;
	}
}


READ16_MEMBER( supracan_state::supracan_video_r )
{
	address_space *mem = m_maincpu->memory().space(AS_PROGRAM);
	UINT16 data = m_video_regs[offset];

	switch(offset)
	{
		case 0x00/2: // Video IRQ flags
			if(!mem->debugger_access())
			{
				//verboselog("maincpu", machine(), 0, "read video IRQ flags (%04x)\n", data);
				device_set_input_line(m_maincpu, 7, CLEAR_LINE);
			}
			break;
		case 0x02/2: // Current scanline
			break;
		case 0x08/2: // Unknown (not video flags!)
			data = 0;
			break;
		case 0x100/2:
			if(!mem->debugger_access()) verboselog("maincpu", machine(), 0, "read tilemap_flags[0] (%04x)\n", data);
			break;
		case 0x106/2:
			if(!mem->debugger_access()) verboselog("maincpu", machine(), 0, "read tilemap_scrolly[0] (%04x)\n", data);
			break;
		case 0x120/2:
			if(!mem->debugger_access()) verboselog("maincpu", machine(), 0, "read tilemap_flags[1] (%04x)\n", data);
			break;
		default:
			if(!mem->debugger_access()) verboselog("maincpu", machine(), 0, "supracan_video_r: Unknown register: %08x (%04x & %04x)\n", 0xf00000 + (offset << 1), data, mem_mask);
			break;
	}

	return data;
}

static TIMER_CALLBACK( supracan_hbl_callback )
{
	supracan_state *state = machine.driver_data<supracan_state>();

	device_set_input_line(state->m_maincpu, 3, HOLD_LINE);

	state->m_hbl_timer->adjust(attotime::never);
}

static TIMER_CALLBACK( supracan_line_on_callback )
{
	supracan_state *state = machine.driver_data<supracan_state>();

	device_set_input_line(state->m_maincpu, 5, HOLD_LINE);

	state->m_line_on_timer->adjust(attotime::never);
}

static TIMER_CALLBACK( supracan_line_off_callback )
{
	supracan_state *state = machine.driver_data<supracan_state>();

	device_set_input_line(state->m_maincpu, 5, CLEAR_LINE);

	state->m_line_on_timer->adjust(attotime::never);
}

static TIMER_CALLBACK( supracan_video_callback )
{
	supracan_state *state = machine.driver_data<supracan_state>();
	int vpos = machine.primary_screen->vpos();

	state->m_video_regs[0] &= ~0x0002;

	switch( vpos )
	{
	case 0:
		state->m_video_regs[0] &= 0x7fff;

		// we really need better management of this
		mark_active_tilemap_all_dirty(machine, 0);
		mark_active_tilemap_all_dirty(machine, 1);
		mark_active_tilemap_all_dirty(machine, 2);
		mark_active_tilemap_all_dirty(machine, 3);


		break;

	case 224://FIXME: Son of Evil is pretty picky about this one, a timing of 240 makes it to crash
		state->m_video_regs[0] |= 0x8000;
		break;

	case 240:
		if(state->m_irq_mask & 1)
		{
			verboselog("maincpu", machine, 0, "Triggering VBL IRQ\n\n");
			device_set_input_line(state->m_maincpu, 7, HOLD_LINE);
		}
		break;
	}

	state->m_video_regs[1] = machine.primary_screen->vpos()-16; // for son of evil, wants vblank active around 224 instead...

	state->m_hbl_timer->adjust( machine.primary_screen->time_until_pos( vpos, 320 ) );
	state->m_video_timer->adjust( machine.primary_screen->time_until_pos( ( vpos + 1 ) % 256, 0 ) );
}

WRITE16_MEMBER( supracan_state::supracan_video_w )
{
	address_space *mem = m_maincpu->memory().space(AS_PROGRAM);
	acan_sprdma_regs_t *acan_sprdma_regs = &m_acan_sprdma_regs;
	int i;

	// if any of this changes we need a partial update (see sango fighters intro)
	machine().primary_screen->update_partial(machine().primary_screen->vpos());

	switch(offset)
	{
		case 0x10/2: // Byte count
			verboselog("maincpu", machine(), 0, "sprite dma word count: %04x\n", data);
			acan_sprdma_regs->count = data;
			break;
		case 0x12/2: // Destination address MSW
			acan_sprdma_regs->dst &= 0x0000ffff;
			acan_sprdma_regs->dst |= data << 16;
			verboselog("maincpu", machine(), 0, "sprite dma dest msw: %04x\n", data);
			break;
		case 0x14/2: // Destination address LSW
			acan_sprdma_regs->dst &= 0xffff0000;
			acan_sprdma_regs->dst |= data;
			verboselog("maincpu", machine(), 0, "sprite dma dest lsw: %04x\n", data);
			break;
		case 0x16/2: // Source word increment
			verboselog("maincpu", machine(), 0, "sprite dma dest word inc: %04x\n", data);
			acan_sprdma_regs->dst_inc = data;
			break;
		case 0x18/2: // Source address MSW
			acan_sprdma_regs->src &= 0x0000ffff;
			acan_sprdma_regs->src |= data << 16;
			verboselog("maincpu", machine(), 0, "sprite dma src msw: %04x\n", data);
			break;
		case 0x1a/2: // Source address LSW
			verboselog("maincpu", machine(), 0, "sprite dma src lsw: %04x\n", data);
			acan_sprdma_regs->src &= 0xffff0000;
			acan_sprdma_regs->src |= data;
			break;
		case 0x1c/2: // Source word increment
			verboselog("maincpu", machine(), 0, "sprite dma src word inc: %04x\n", data);
			acan_sprdma_regs->src_inc = data;
			break;
		case 0x1e/2:
			logerror( "supracan_video_w: Kicking off a DMA from %08x to %08x, %d bytes (%04x)\n", acan_sprdma_regs->src, acan_sprdma_regs->dst, acan_sprdma_regs->count, data);

			/* TODO: what's 0x2000 and 0x4000 for? */
			if(data & 0x8000)
			{
				if(data & 0x2000 || data & 0x4000)
				{
					acan_sprdma_regs->dst |= 0xf40000;
				}

				if(data & 0x2000)
				{
					//acan_sprdma_regs->count <<= 1;
				}

				for(i = 0; i <= acan_sprdma_regs->count; i++)
				{
					if(data & 0x0100) //dma 0x00 fill (or fixed value?)
					{
						mem->write_word(acan_sprdma_regs->dst, 0);
						acan_sprdma_regs->dst+=2 * acan_sprdma_regs->dst_inc;
						//memset(supracan_vram,0x00,0x020000);
					}
					else
					{
						mem->write_word(acan_sprdma_regs->dst, mem->read_word(acan_sprdma_regs->src));
						acan_sprdma_regs->dst+=2 * acan_sprdma_regs->dst_inc;
						acan_sprdma_regs->src+=2 * acan_sprdma_regs->src_inc;
					}
				}
			}
			else
			{
				verboselog("maincpu", machine(), 0, "supracan_dma_w: Attempting to kick off a DMA without bit 15 set! (%04x)\n", data);
			}
			break;
		case 0x08/2:
			{
				verboselog("maincpu", machine(), 3, "video_flags = %04x\n", data);
				m_video_flags = data;

				rectangle visarea = machine().primary_screen->visible_area();

				visarea.set(0, ((m_video_flags & 0x100) ? 320 : 256) - 1, 8, 232 - 1);
				machine().primary_screen->configure(348, 256, visarea, machine().primary_screen->frame_period().attoseconds);
			}
			break;
		case 0x0a/2:
			{
				// raster interrupt
				verboselog("maincpu", machine(), 0, "IRQ Trigger? = %04x\n", data);
				if(data & 0x8000)
				{
					m_line_on_timer->adjust(machine().primary_screen->time_until_pos((data & 0x00ff), 0));
				}
				else
				{
					m_line_on_timer->adjust(attotime::never);
				}
			}
			break;

		case 0x0c/2:
			{
				verboselog("maincpu", machine(), 0, "IRQ De-Trigger? = %04x\n", data);
				if(data & 0x8000)
				{
					m_line_off_timer->adjust(machine().primary_screen->time_until_pos(data & 0x00ff, 0));
				}
				else
				{
					m_line_off_timer->adjust(attotime::never);
				}
			}
			break;

		/* Sprites */
		case 0x20/2: m_sprite_base_addr = data << 2; verboselog("maincpu", machine(), 0, "sprite_base_addr = %04x\n", data); break;
		case 0x22/2: m_sprite_count = data+1; verboselog("maincpu", machine(), 0, "sprite_count = %d\n", data+1); break;
		case 0x26/2: m_sprite_flags = data; verboselog("maincpu", machine(), 0, "sprite_flags = %04x\n", data); break;

		/* Tilemap 0 */
		case 0x100/2: m_tilemap_flags[0] = data; verboselog("maincpu", machine(), 3, "tilemap_flags[0] = %04x\n", data); break;
		case 0x104/2: m_tilemap_scrollx[0] = data; verboselog("maincpu", machine(), 3, "tilemap_scrollx[0] = %04x\n", data); break;
		case 0x106/2: m_tilemap_scrolly[0] = data; verboselog("maincpu", machine(), 3, "tilemap_scrolly[0] = %04x\n", data); break;
		case 0x108/2: m_tilemap_base_addr[0] = (data) << 1; verboselog("maincpu", machine(), 3, "tilemap_base_addr[0] = %05x\n", data << 2); break;
		case 0x10a/2: m_tilemap_mode[0] = data; verboselog("maincpu", machine(), 3, "tilemap_mode[0] = %04x\n", data); break;

		/* Tilemap 1 */
		case 0x120/2: m_tilemap_flags[1] = data; verboselog("maincpu", machine(), 3, "tilemap_flags[1] = %04x\n", data); break;
		case 0x124/2: m_tilemap_scrollx[1] = data; verboselog("maincpu", machine(), 3, "tilemap_scrollx[1] = %04x\n", data); break;
		case 0x126/2: m_tilemap_scrolly[1] = data; verboselog("maincpu", machine(), 3, "tilemap_scrolly[1] = %04x\n", data); break;
		case 0x128/2: m_tilemap_base_addr[1] = (data) << 1; verboselog("maincpu", machine(), 3, "tilemap_base_addr[1] = %05x\n", data << 2); break;
		case 0x12a/2: m_tilemap_mode[1] = data; verboselog("maincpu", machine(), 3, "tilemap_mode[1] = %04x\n", data); break;

		/* Tilemap 2? */
		case 0x140/2: m_tilemap_flags[2] = data; verboselog("maincpu", machine(), 0, "tilemap_flags[2] = %04x\n", data); break;
		case 0x144/2: m_tilemap_scrollx[2] = data; verboselog("maincpu", machine(), 0, "tilemap_scrollx[2] = %04x\n", data); break;
		case 0x146/2: m_tilemap_scrolly[2] = data; verboselog("maincpu", machine(), 0, "tilemap_scrolly[2] = %04x\n", data); break;
		case 0x148/2: m_tilemap_base_addr[2] = (data) << 1; verboselog("maincpu", machine(), 0, "tilemap_base_addr[2] = %05x\n", data << 2); break;
		case 0x14a/2: m_tilemap_mode[2] = data; verboselog("maincpu", machine(), 0, "tilemap_mode[2] = %04x\n", data); break;

		/* ROZ */
		case 0x180/2: m_roz_mode = data; verboselog("maincpu", machine(), 3, "roz_mode = %04x\n", data); break;
		case 0x184/2: m_roz_scrollx = (data << 16) | (m_roz_scrollx & 0xffff); m_roz_changed |= 1; verboselog("maincpu", machine(), 3, "roz_scrollx = %08x\n", m_roz_scrollx); break;
		case 0x186/2: m_roz_scrollx = (data) | (m_roz_scrollx & 0xffff0000); m_roz_changed |= 1; verboselog("maincpu", machine(), 3, "roz_scrollx = %08x\n", m_roz_scrollx); break;
		case 0x188/2: m_roz_scrolly = (data << 16) | (m_roz_scrolly & 0xffff); m_roz_changed |= 2; verboselog("maincpu", machine(), 3, "roz_scrolly = %08x\n", m_roz_scrolly); break;
		case 0x18a/2: m_roz_scrolly = (data) | (m_roz_scrolly & 0xffff0000); m_roz_changed |= 2; verboselog("maincpu", machine(), 3, "roz_scrolly = %08x\n", m_roz_scrolly); break;
		case 0x18c/2: m_roz_coeffa = data; verboselog("maincpu", machine(), 3, "roz_coeffa = %04x\n", data); break;
		case 0x18e/2: m_roz_coeffb = data; verboselog("maincpu", machine(), 3, "roz_coeffb = %04x\n", data); break;
		case 0x190/2: m_roz_coeffc = data; verboselog("maincpu", machine(), 3, "roz_coeffc = %04x\n", data); break;
		case 0x192/2: m_roz_coeffd = data; verboselog("maincpu", machine(), 3, "roz_coeffd = %04x\n", data); break;
		case 0x194/2: m_roz_base_addr = (data) << 1; verboselog("maincpu", machine(), 3, "roz_base_addr = %05x\n", data << 2); break;
		case 0x196/2: m_roz_tile_bank = data; verboselog("maincpu", machine(), 3, "roz_tile_bank = %04x\n", data); break; //tile bank
		case 0x198/2: m_roz_unk_base0 = data << 2; verboselog("maincpu", machine(), 3, "roz_unk_base0 = %05x\n", data << 2); break;
		case 0x19a/2: m_roz_unk_base1 = data << 2; verboselog("maincpu", machine(), 3, "roz_unk_base1 = %05x\n", data << 2); break;
		case 0x19e/2: m_roz_unk_base2 = data << 2; verboselog("maincpu", machine(), 3, "roz_unk_base2 = %05x\n", data << 2); break;

		case 0x1d0/2: m_unk_1d0 = data; verboselog("maincpu", machine(), 3, "unk_1d0 = %04x\n", data); break;




		case 0x1f0/2: //FIXME: this register is mostly not understood
			m_irq_mask = data;//(data & 8) ? 0 : 1;
#if 0
			if(!m_irq_mask && !m_hbl_mask)
			{
				device_set_input_line(m_maincpu, 7, CLEAR_LINE);
			}
#endif
			verboselog("maincpu", machine(), 3, "irq_mask = %04x\n", data);
			break;
		default:
			verboselog("maincpu", machine(), 0, "supracan_video_w: Unknown register: %08x = %04x & %04x\n", 0xf00000 + (offset << 1), data, mem_mask);
			break;
	}
	m_video_regs[offset] = data;
}


static DEVICE_IMAGE_LOAD( supracan_cart )
{
	UINT8 *cart = image.device().machine().region("cart")->base();
	UINT32 size = 0;

	if (image.software_entry() == NULL)
	{
		size = image.length();

		if (size > 0x400000)
		{
			image.seterror(IMAGE_ERROR_UNSPECIFIED, "Unsupported cartridge size");
			return IMAGE_INIT_FAIL;
		}

		if (image.fread(cart, size) != size)
		{
			image.seterror(IMAGE_ERROR_UNSPECIFIED, "Unable to fully read from file");
			return IMAGE_INIT_FAIL;
		}
	}
	else
	{
		size = image.get_software_region_length("rom");
		memcpy(cart, image.get_software_region("rom"), size);
	}

	return IMAGE_INIT_PASS;
}


static MACHINE_START( supracan )
{
	supracan_state *state = machine.driver_data<supracan_state>();

	state->m_video_timer = machine.scheduler().timer_alloc(FUNC(supracan_video_callback));
	state->m_hbl_timer = machine.scheduler().timer_alloc(FUNC(supracan_hbl_callback));
	state->m_line_on_timer = machine.scheduler().timer_alloc(FUNC(supracan_line_on_callback));
	state->m_line_off_timer = machine.scheduler().timer_alloc(FUNC(supracan_line_off_callback));
}


static MACHINE_RESET( supracan )
{
	supracan_state *state = machine.driver_data<supracan_state>();

	cputag_set_input_line(machine, "soundcpu", INPUT_LINE_HALT, ASSERT_LINE);

	state->m_video_timer->adjust( machine.primary_screen->time_until_pos( 0, 0 ) );
	state->m_irq_mask = 0;
}

/* gfxdecode is retained for reference purposes but not otherwise used by the driver */
static const gfx_layout supracan_gfx8bpp =
{
	8,8,
	RGN_FRAC(1,1),
	8,
	{ 0,1,2,3,4,5,6,7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	{ STEP8(0,8*8) },
	8*8*8
};



static const gfx_layout supracan_gfx4bpp =
{
	8,8,
	RGN_FRAC(1,1),
	4,
	{ 0,1,2,3 },
	{ 0*4, 1*4, 2*4, 3*4, 4*4, 5*4, 6*4, 7*4 },
	{ 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32 },
	8*32
};

static const gfx_layout supracan_gfx2bpp =
{
	8,8,
	RGN_FRAC(1,1),
	2,
	{ 0,1 },
	{ 0*2, 1*2, 2*2, 3*2, 4*2, 5*2, 6*2, 7*2 },
	{ 0*16, 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16 },
	8*16
};


static const UINT32 xtexlayout_xoffset[64] = { 0,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 };

static const UINT32 xtexlayout_yoffset[64] = {  0*64,1*64,2*64,3*64,4*64,5*64,6*64,7*64,8*64,
                                                9*64,10*64,11*64,12*64,13*64,14*64,15*64,
                                                16*64,17*64,18*64,19*64,20*64,21*64,22*64,23*64,
                                                24*64,25*64,26*64,27*64,28*64,29*64,30*64,31*64,
                                                32*64,33*64,34*64,35*64,36*64,37*64,38*64,39*64,
                                                40*64,41*64,42*64,43*64,44*64,45*64,46*64,47*64,
						48*64,49*64,50*64,51*64,52*64,53*64,54*64,55*64,
                                                56*64,57*64,58*64,59*64,60*64,61*64,62*64,63*64 };
static const gfx_layout supracan_gfx1bpp =
{
	64,64,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	EXTENDED_XOFFS,
	EXTENDED_YOFFS,
	64*64,
	xtexlayout_xoffset,
	xtexlayout_yoffset
};

static const gfx_layout supracan_gfx1bpp_alt =
{
	8,8,
	RGN_FRAC(1,1),
	1,
	{ 0 },
	{ 0,1,2,3,4,5,6,7 },
	{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
	8*8
};


static GFXDECODE_START( supracan )
	GFXDECODE_ENTRY( "ram_gfx2",  0, supracan_gfx8bpp,   0, 1 )
	GFXDECODE_ENTRY( "ram_gfx2",  0, supracan_gfx4bpp,   0, 0x10 )
	GFXDECODE_ENTRY( "ram_gfx2",  0, supracan_gfx2bpp,   0, 0x40 )
	GFXDECODE_ENTRY( "ram_gfx2",  0, supracan_gfx1bpp,   0, 0x80 )
	GFXDECODE_ENTRY( "ram_gfx3",  0, supracan_gfx1bpp_alt,   0, 0x80 )
GFXDECODE_END

static INTERRUPT_GEN( supracan_irq )
{
#if 0
	supracan_state *state = (supracan_state *)device->machine().driver_data<supracan_state>();

	if(state->m_irq_mask)
	{
		device_set_input_line(device, 7, HOLD_LINE);
	}
#endif
}

static INTERRUPT_GEN( supracan_sound_irq )
{
	supracan_state *state = (supracan_state *)device->machine().driver_data<supracan_state>();

	state->m_sound_irq_source_reg |= 0x80;

	if(state->m_sound_irq_enable_reg & state->m_sound_irq_source_reg)
	{
		device_set_input_line(device->machine().device("soundcpu"), 0, ASSERT_LINE);
	}
	else
	{
		device_set_input_line(device->machine().device("soundcpu"), 0, CLEAR_LINE);
	}
}

static MACHINE_CONFIG_START( supracan, supracan_state )

	MCFG_CPU_ADD( "maincpu", M68000, XTAL_10_738635MHz )		/* Correct frequency unknown */
	MCFG_CPU_PROGRAM_MAP( supracan_mem )
	MCFG_CPU_VBLANK_INT("screen", supracan_irq)

	MCFG_CPU_ADD( "soundcpu", M6502, XTAL_3_579545MHz )		/* TODO: Verify actual clock */
	MCFG_CPU_PROGRAM_MAP( supracan_sound_mem )
	MCFG_CPU_VBLANK_INT("screen", supracan_sound_irq)

#if !(SOUNDCPU_BOOT_HACK)
	MCFG_QUANTUM_PERFECT_CPU("maincpu")
	MCFG_QUANTUM_PERFECT_CPU("soundcpu")
#endif

	MCFG_MACHINE_START( supracan )
	MCFG_MACHINE_RESET( supracan )

	MCFG_SCREEN_ADD( "screen", RASTER )
	MCFG_SCREEN_RAW_PARAMS(XTAL_10_738635MHz/2, 348, 0, 256, 256, 0, 240 )	/* No idea if this is correct */
	MCFG_SCREEN_UPDATE_STATIC( supracan )
	MCFG_VIDEO_START( supracan )
	MCFG_PALETTE_LENGTH( 32768 )
	MCFG_PALETTE_INIT( supracan )
	MCFG_GFXDECODE(supracan)

	MCFG_CARTSLOT_ADD("cart")
	MCFG_CARTSLOT_EXTENSION_LIST("bin")
	MCFG_CARTSLOT_INTERFACE("supracan_cart")
	MCFG_CARTSLOT_LOAD(supracan_cart)

	MCFG_SOFTWARE_LIST_ADD("cart_list","supracan")
MACHINE_CONFIG_END


ROM_START( supracan )
	ROM_REGION( 0x400000, "cart", ROMREGION_ERASEFF )
	ROM_REGION( 0x20000, "ram_gfx", ROMREGION_ERASEFF )
	ROM_REGION( 0x20000, "ram_gfx2", ROMREGION_ERASEFF )
	ROM_REGION( 0x20000, "ram_gfx3", ROMREGION_ERASEFF )
ROM_END


/*    YEAR  NAME        PARENT  COMPAT  MACHINE     INPUT     INIT    COMPANY                  FULLNAME        FLAGS */
CONS( 1995, supracan,   0,      0,      supracan,   supracan, 0,      "Funtech Entertainment", "Super A'Can",  GAME_NO_SOUND | GAME_IMPERFECT_GRAPHICS | GAME_NOT_WORKING )
