summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/video/imagetek_i4100.cpp
blob: 5de099ef28ae33693ac0c09c7694e1088e757617 (plain) (tree)
1
2
3
4



                                                                            










                                                                         








                                                                                                   





































                                                                                    
 




                                                                            
                    









                                                                                   

                                                 




                                                                                                                                                                     
                                                                                                                          




                                                                                                                                        
                          


















                                                                                                                                     
 

                                                                

                                                    




                                                                                                                                                                     
                                                                                                                          
















                                                                                                                                        

                          






                                                                                                                                     
                                                      
                                                                                                                           
                                                  
                                                                                   




                                                                                                                                     
 

                                                                     

                                                    




                                                                                                                                                                     
                                                                                                                          
















                                                                                                                                        

                          






                                                                                                                                     
 




























                                                                                                                                                                   

                                     
 






















                                                                                                                             
                                                               





                                                   







                                                                                                                              
 



                                                              
 

























                                                    

                              

                                        


                                                                           

                                                                                            
 
                                         
 

                                                        
 

































                                                                                                    
                                                                   






































































                                                                                                      


                                                                                         
























                                                                                                          
 







                                                                                                            
                                                             
  
                                                                              

























                                                                                            
                                                    







                                                   
 









































































































                                                                                                                                          
                                                                                                             
































































                                                                                                
                                                                                                               








                                      
                                                                                                                       








                                                  
                                                                                                     










































































                                                                                                                
                                                                                                                                  












































































































































































                                                                                                                                       
 






































































































                                                                                                                           
 




                                                                                                                                                 
                                                         








                                        
 




                                                                      
 
                                       
 









                                       
 









                                       
 
                                        












                                                                     
                                                        






















                                                                                                                                                     
                                                        




















                                                                                                                                                     
 


























                                                                                                                          
 


                                                                                                                     
 








                                                           
 

                 
// license:BSD-3-Clause
// copyright-holders:Luca Elia,David Haywood,Angelo Salese
/***************************************************************************

    Imagetek I4100 / I4220 / I4300 device files

    device emulation by Angelo Salese, based off from original metro.cpp
    implementation by Luca Elia & David Haywood

    TODO:
    - interrupt enable/acknowledge/vector;
    - soundlatch delegate;
    - inputs for i4300;
    - hyprduel.cpp uses scanline attribute which crawls to unusable state
      with current video routines here;
    - CRT Controller, also understand why it needs so many writes before actual parameters;
    - Wrong color bars in service mode (e.g. balcube, toride2g). They use solid color tiles (80xx),
      but the right palette is not at 00-ff.
      Related to the unknown table in the RAM mapped just before the palette?
      Update: the colors should have a common bank of 0xb (so 0x8bxx), it's unknown why the values
      diverges, the blitter is responsible of that upload fwiw;
    - Some gfx problems in ladykill, 3kokushi, puzzli, gakusai, seem related to how we handle
      windows, wrapping, read-modify-write areas;
    - puzzli: emulate hblank irq and fix video routines here (water effect not emulated?);

============================================================================

                    driver by   Luca Elia (l.elia@tin.it)

                            [ 3 Scrolling Layers ]

        There is memory for a huge layer, but the actual tilemap
        is a smaller window (of fixed size) carved from anywhere
        inside that layer.

        Tile Size:                  8 x 8 x 4
        (later games can switch to  8 x 8 x 8, 16 x 16 x 4/8 at run time)

        Big Layer Size:         2048 x 2048 (8x8 tiles) or 4096 x 4096 (16x16 tiles)

        Tilemap Window Size:    512 x 256 (8x8 tiles) or 1024 x 512 (16x16 tiles)

        The tile codes in memory do not map directly to tiles. They
        are indexes into a table (with 0x200 entries) that defines
        a virtual set of tiles for the 3 layers. Each entry in that
        table adds 16 tiles to the set of available tiles, and decides
        their color code.

        Tile code with their msbit set are different as they mean:
        draw a tile filled with a single color (0-fff)


                            [ 512 Zooming Sprites ]

        The sprites are NOT tile based: the "tile" size can vary from
        8 to 64 (independently for width and height) with an 8 pixel
        granularity. The "tile" address is a multiple of 8x8 pixels.

        Each sprite can be shrinked to ~1/4 or enlarged to ~32x following
        an exponential curve of sizes (with one zoom value for both width
        and height)


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

#include "emu.h"
#include "imagetek_i4100.h"

#include <algorithm>

//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

// device type definition
DEFINE_DEVICE_TYPE(I4100, imagetek_i4100_device, "i4100", "Imagetek I4100 052 VDP")
DEFINE_DEVICE_TYPE(I4220, imagetek_i4220_device, "i4220", "Imagetek I4220 071 VDP")
DEFINE_DEVICE_TYPE(I4300, imagetek_i4300_device, "i4300", "Imagetek I4300 095 VDP")

void imagetek_i4100_device::map(address_map &map)
{
	map(0x00000, 0x1ffff).rw(FUNC(imagetek_i4100_device::vram_0_r), FUNC(imagetek_i4100_device::vram_0_w)).share("vram_0");
	map(0x20000, 0x3ffff).rw(FUNC(imagetek_i4100_device::vram_1_r), FUNC(imagetek_i4100_device::vram_1_w)).share("vram_1");
	map(0x40000, 0x5ffff).rw(FUNC(imagetek_i4100_device::vram_2_r), FUNC(imagetek_i4100_device::vram_2_w)).share("vram_2");
	map(0x60000, 0x6ffff).r(FUNC(imagetek_i4100_device::gfxrom_r));
	map(0x70000, 0x71fff).rw(FUNC(imagetek_i4100_device::scratchram_r), FUNC(imagetek_i4100_device::scratchram_w)).share("scratchram"); // unknown, maybe palette
	map(0x72000, 0x73fff).rw("palette", FUNC(palette_device::read16), FUNC(palette_device::write16)).share("palette");
	map(0x74000, 0x74fff).rw(FUNC(imagetek_i4100_device::spriteram_r), FUNC(imagetek_i4100_device::spriteram_w)).share("spriteram");
	map(0x75000, 0x75fff).rw(FUNC(imagetek_i4100_device::rmw_vram_0_r), FUNC(imagetek_i4100_device::rmw_vram_0_w));
	map(0x76000, 0x76fff).rw(FUNC(imagetek_i4100_device::rmw_vram_1_r), FUNC(imagetek_i4100_device::rmw_vram_1_w));
	map(0x77000, 0x77fff).rw(FUNC(imagetek_i4100_device::rmw_vram_2_r), FUNC(imagetek_i4100_device::rmw_vram_2_w));
	map(0x78000, 0x787ff).rw(FUNC(imagetek_i4100_device::tiletable_r), FUNC(imagetek_i4100_device::tiletable_w)).share("tiletable");
	// video registers
	map(0x78800, 0x78801).rw(FUNC(imagetek_i4100_device::sprite_count_r), FUNC(imagetek_i4100_device::sprite_count_w));
	map(0x78802, 0x78803).rw(FUNC(imagetek_i4100_device::sprite_priority_r), FUNC(imagetek_i4100_device::sprite_priority_w));
	map(0x78804, 0x78805).rw(FUNC(imagetek_i4100_device::sprite_yoffset_r), FUNC(imagetek_i4100_device::sprite_yoffset_w));
	map(0x78806, 0x78807).rw(FUNC(imagetek_i4100_device::sprite_xoffset_r), FUNC(imagetek_i4100_device::sprite_xoffset_w));
	map(0x78808, 0x78809).rw(FUNC(imagetek_i4100_device::sprite_color_code_r), FUNC(imagetek_i4100_device::sprite_color_code_w));
	map(0x78810, 0x78811).rw(FUNC(imagetek_i4100_device::layer_priority_r), FUNC(imagetek_i4100_device::layer_priority_w));
	map(0x78812, 0x78813).rw(FUNC(imagetek_i4100_device::background_color_r), FUNC(imagetek_i4100_device::background_color_w));

	map(0x78840, 0x7884d).w(FUNC(imagetek_i4100_device::blitter_w)).share("blitter_regs");
	map(0x78850, 0x78851).rw(FUNC(imagetek_i4100_device::screen_yoffset_r), FUNC(imagetek_i4100_device::screen_yoffset_w));
	map(0x78852, 0x78853).rw(FUNC(imagetek_i4100_device::screen_xoffset_r), FUNC(imagetek_i4100_device::screen_xoffset_w));
	map(0x78860, 0x7886b).rw(FUNC(imagetek_i4100_device::window_r), FUNC(imagetek_i4100_device::window_w)).share("windowregs");
	map(0x78870, 0x7887b).rw(FUNC(imagetek_i4100_device::scroll_r), FUNC(imagetek_i4100_device::scroll_w)).share("scrollregs");

	map(0x78880, 0x78881).w(FUNC(imagetek_i4100_device::crtc_vert_w));
	map(0x78890, 0x78891).w(FUNC(imagetek_i4100_device::crtc_horz_w));
	map(0x788a0, 0x788a1).w(FUNC(imagetek_i4100_device::crtc_unlock_w));
	map(0x788aa, 0x788ab).w(FUNC(imagetek_i4100_device::rombank_w));
	map(0x788ac, 0x788ad).w(FUNC(imagetek_i4100_device::screen_ctrl_w));
}

// same as above but with moved video registers (now at 0x797**)
void imagetek_i4220_device::v2_map(address_map &map)
{
	map(0x00000, 0x1ffff).rw(FUNC(imagetek_i4220_device::vram_0_r), FUNC(imagetek_i4220_device::vram_0_w)).share("vram_0");
	map(0x20000, 0x3ffff).rw(FUNC(imagetek_i4220_device::vram_1_r), FUNC(imagetek_i4220_device::vram_1_w)).share("vram_1");
	map(0x40000, 0x5ffff).rw(FUNC(imagetek_i4220_device::vram_2_r), FUNC(imagetek_i4220_device::vram_2_w)).share("vram_2");
	map(0x60000, 0x6ffff).r(FUNC(imagetek_i4220_device::gfxrom_r));
	map(0x70000, 0x71fff).rw(FUNC(imagetek_i4220_device::scratchram_r), FUNC(imagetek_i4220_device::scratchram_w)).share("scratchram"); // unknown, maybe palette
	map(0x72000, 0x73fff).rw("palette", FUNC(palette_device::read16), FUNC(palette_device::write16)).share("palette");
	map(0x74000, 0x74fff).rw(FUNC(imagetek_i4220_device::spriteram_r), FUNC(imagetek_i4220_device::spriteram_w)).share("spriteram");
	map(0x75000, 0x75fff).rw(FUNC(imagetek_i4220_device::rmw_vram_0_r), FUNC(imagetek_i4220_device::rmw_vram_0_w));
	map(0x76000, 0x76fff).rw(FUNC(imagetek_i4220_device::rmw_vram_1_r), FUNC(imagetek_i4220_device::rmw_vram_1_w));
	map(0x77000, 0x77fff).rw(FUNC(imagetek_i4220_device::rmw_vram_2_r), FUNC(imagetek_i4220_device::rmw_vram_2_w));
	map(0x78000, 0x787ff).rw(FUNC(imagetek_i4220_device::tiletable_r), FUNC(imagetek_i4220_device::tiletable_w)).share("tiletable");

	map(0x78840, 0x7884d).w(FUNC(imagetek_i4220_device::blitter_w)).share("blitter_regs");
	map(0x78850, 0x78851).rw(FUNC(imagetek_i4220_device::screen_yoffset_r), FUNC(imagetek_i4220_device::screen_yoffset_w));
	map(0x78852, 0x78853).rw(FUNC(imagetek_i4220_device::screen_xoffset_r), FUNC(imagetek_i4220_device::screen_xoffset_w));
	map(0x78860, 0x7886b).rw(FUNC(imagetek_i4220_device::window_r), FUNC(imagetek_i4220_device::window_w)).share("windowregs");
	map(0x78870, 0x7887b).rw(FUNC(imagetek_i4220_device::scroll_r), FUNC(imagetek_i4220_device::scroll_w)).share("scrollregs");

	map(0x78880, 0x78881).w(FUNC(imagetek_i4220_device::crtc_vert_w));
	map(0x78890, 0x78891).w(FUNC(imagetek_i4220_device::crtc_horz_w));
	map(0x788a0, 0x788a1).w(FUNC(imagetek_i4220_device::crtc_unlock_w));
	map(0x788aa, 0x788ab).w(FUNC(imagetek_i4220_device::rombank_w));
	map(0x788ac, 0x788ad).w(FUNC(imagetek_i4220_device::screen_ctrl_w));

	// video registers
	map(0x79700, 0x79701).rw(FUNC(imagetek_i4220_device::sprite_count_r), FUNC(imagetek_i4220_device::sprite_count_w));
	map(0x79702, 0x79703).rw(FUNC(imagetek_i4220_device::sprite_priority_r), FUNC(imagetek_i4220_device::sprite_priority_w));
	map(0x79704, 0x79705).rw(FUNC(imagetek_i4220_device::sprite_yoffset_r), FUNC(imagetek_i4220_device::sprite_yoffset_w));
	map(0x79706, 0x79707).rw(FUNC(imagetek_i4220_device::sprite_xoffset_r), FUNC(imagetek_i4220_device::sprite_xoffset_w));
	map(0x79708, 0x79709).rw(FUNC(imagetek_i4220_device::sprite_color_code_r), FUNC(imagetek_i4220_device::sprite_color_code_w));
	map(0x79710, 0x79711).rw(FUNC(imagetek_i4220_device::layer_priority_r), FUNC(imagetek_i4220_device::layer_priority_w));
	map(0x79712, 0x79713).rw(FUNC(imagetek_i4220_device::background_color_r), FUNC(imagetek_i4220_device::background_color_w));
	// repeated here in Puzzlet compatibility mode
	map(0x78800, 0x78801).rw(FUNC(imagetek_i4220_device::sprite_count_r), FUNC(imagetek_i4220_device::sprite_count_w));
	// ... this one breaks Blazing Tornado tho
//  AM_RANGE(0x78802, 0x78803) AM_READWRITE(sprite_priority_r,   sprite_priority_w)
	map(0x78804, 0x78805).rw(FUNC(imagetek_i4220_device::sprite_yoffset_r), FUNC(imagetek_i4220_device::sprite_yoffset_w));
	map(0x78806, 0x78807).rw(FUNC(imagetek_i4220_device::sprite_xoffset_r), FUNC(imagetek_i4220_device::sprite_xoffset_w));
	map(0x78808, 0x78809).rw(FUNC(imagetek_i4220_device::sprite_color_code_r), FUNC(imagetek_i4220_device::sprite_color_code_w));
	map(0x78810, 0x78811).rw(FUNC(imagetek_i4220_device::layer_priority_r), FUNC(imagetek_i4220_device::layer_priority_w));
	map(0x78812, 0x78813).rw(FUNC(imagetek_i4220_device::background_color_r), FUNC(imagetek_i4220_device::background_color_w));
}

// more changes around, namely the screen offsets being reversed here
void imagetek_i4300_device::v3_map(address_map &map)
{
	map(0x00000, 0x1ffff).rw(FUNC(imagetek_i4300_device::vram_0_r), FUNC(imagetek_i4300_device::vram_0_w)).share("vram_0");
	map(0x20000, 0x3ffff).rw(FUNC(imagetek_i4300_device::vram_1_r), FUNC(imagetek_i4300_device::vram_1_w)).share("vram_1");
	map(0x40000, 0x5ffff).rw(FUNC(imagetek_i4300_device::vram_2_r), FUNC(imagetek_i4300_device::vram_2_w)).share("vram_2");
	map(0x60000, 0x6ffff).r(FUNC(imagetek_i4300_device::gfxrom_r));
	map(0x70000, 0x71fff).rw(FUNC(imagetek_i4300_device::scratchram_r), FUNC(imagetek_i4300_device::scratchram_w)).share("scratchram"); // unknown, maybe palette
	map(0x72000, 0x73fff).rw("palette", FUNC(palette_device::read16), FUNC(palette_device::write16)).share("palette");
	map(0x74000, 0x74fff).rw(FUNC(imagetek_i4300_device::spriteram_r), FUNC(imagetek_i4300_device::spriteram_w)).share("spriteram");
	map(0x75000, 0x75fff).rw(FUNC(imagetek_i4300_device::rmw_vram_0_r), FUNC(imagetek_i4300_device::rmw_vram_0_w));
	map(0x76000, 0x76fff).rw(FUNC(imagetek_i4300_device::rmw_vram_1_r), FUNC(imagetek_i4300_device::rmw_vram_1_w));
	map(0x77000, 0x77fff).rw(FUNC(imagetek_i4300_device::rmw_vram_2_r), FUNC(imagetek_i4300_device::rmw_vram_2_w));
	map(0x78000, 0x787ff).rw(FUNC(imagetek_i4300_device::tiletable_r), FUNC(imagetek_i4300_device::tiletable_w)).share("tiletable");
	map(0x78808, 0x78809).rw(FUNC(imagetek_i4300_device::screen_xoffset_r), FUNC(imagetek_i4300_device::screen_xoffset_w));
	map(0x7880a, 0x7880b).rw(FUNC(imagetek_i4300_device::screen_yoffset_r), FUNC(imagetek_i4300_device::screen_yoffset_w));
	map(0x7880e, 0x7880f).w(FUNC(imagetek_i4300_device::screen_ctrl_w)); // TODO: can be read back here (gakusai)

	map(0x78800, 0x78801).w(FUNC(imagetek_i4300_device::crtc_unlock_w));
	map(0x78802, 0x78803).w(FUNC(imagetek_i4300_device::crtc_horz_w));
	map(0x78804, 0x78805).w(FUNC(imagetek_i4300_device::crtc_vert_w));

	map(0x78840, 0x7884d).w(FUNC(imagetek_i4300_device::blitter_w)).share("blitter_regs");
	map(0x78850, 0x7885b).rw(FUNC(imagetek_i4300_device::scroll_r), FUNC(imagetek_i4300_device::scroll_w)).share("scrollregs");
	map(0x78860, 0x7886b).rw(FUNC(imagetek_i4300_device::window_r), FUNC(imagetek_i4300_device::window_w)).share("windowregs");
	map(0x78870, 0x78871).w(FUNC(imagetek_i4300_device::rombank_w));

	// video registers
	map(0x79700, 0x79701).rw(FUNC(imagetek_i4300_device::sprite_count_r), FUNC(imagetek_i4300_device::sprite_count_w));
	map(0x79702, 0x79703).rw(FUNC(imagetek_i4300_device::sprite_priority_r), FUNC(imagetek_i4300_device::sprite_priority_w));
	map(0x79704, 0x79705).rw(FUNC(imagetek_i4300_device::sprite_yoffset_r), FUNC(imagetek_i4300_device::sprite_yoffset_w));
	map(0x79706, 0x79707).rw(FUNC(imagetek_i4300_device::sprite_xoffset_r), FUNC(imagetek_i4300_device::sprite_xoffset_w));
	map(0x79708, 0x79709).rw(FUNC(imagetek_i4300_device::sprite_color_code_r), FUNC(imagetek_i4300_device::sprite_color_code_w));
	map(0x79710, 0x79711).rw(FUNC(imagetek_i4300_device::layer_priority_r), FUNC(imagetek_i4300_device::layer_priority_w));
	map(0x79712, 0x79713).rw(FUNC(imagetek_i4300_device::background_color_r), FUNC(imagetek_i4300_device::background_color_w));
}


//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  i4100_device - constructor
//-------------------------------------------------


imagetek_i4100_device::imagetek_i4100_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool has_ext_tiles)
	: device_t(mconfig, type, tag, owner, clock)
	, device_video_interface(mconfig, *this)
	, m_vram_0(*this, "vram_0")
	, m_vram_1(*this, "vram_1")
	, m_vram_2(*this, "vram_2")
	, m_scratchram(*this, "scratchram")
	, m_blitter_regs(*this, "blitter_regs")
	, m_spriteram(*this, "spriteram")
	, m_tiletable(*this, "tiletable")
	, m_window(*this, "windowregs")
	, m_scroll(*this, "scrollregs")
	, m_palette(*this, "palette")
	, m_gfxdecode(*this, finder_base::DUMMY_TAG)
	, m_gfxrom(*this, "^gfx1")
	, m_blit_irq_cb(*this)
	, m_support_8bpp( has_ext_tiles )
	, m_support_16x16( has_ext_tiles )
	, m_tilemap_scrolldx{0, 0, 0}
	, m_tilemap_scrolldy{0, 0, 0}
{
}

imagetek_i4100_device::imagetek_i4100_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: imagetek_i4100_device(mconfig, I4100, tag, owner, clock, false)
{
}


imagetek_i4220_device::imagetek_i4220_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: imagetek_i4100_device(mconfig, I4220, tag, owner, clock, true)
{
}

imagetek_i4300_device::imagetek_i4300_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: imagetek_i4100_device(mconfig, I4300, tag, owner, clock, true)
{
}

//-------------------------------------------------
//  device_add_mconfig - device-specific machine
//  configuration addiitons
//-------------------------------------------------

MACHINE_CONFIG_START(imagetek_i4100_device::device_add_mconfig)
	MCFG_PALETTE_ADD("palette", 0x1000)
	MCFG_PALETTE_FORMAT(GGGGGRRRRRBBBBBx)
MACHINE_CONFIG_END


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void imagetek_i4100_device::expand_gfx1()
{
	// TODO: remove from device_reset (otherwise you get broken sprites in i4220+ games because gfx rom isn't yet inited!)
	if(m_inited_hack == true)
		return;

	m_inited_hack = true;
	uint32_t length   =   m_gfxrom_size * 2;

	m_expanded_gfx1 = std::make_unique<uint8_t[]>(length);

	for (int i = 0; i < length; i += 2)
	{
		uint8_t src = m_gfxrom[i / 2];

		m_expanded_gfx1[i + 0] = src & 0xf;
		m_expanded_gfx1[i + 1] = src >> 4;
	}
}

void imagetek_i4100_device::device_start()
{
	if(!m_gfxdecode->started())
		throw device_missing_dependencies();

	m_inited_hack = false;
	save_item(NAME(m_rombank));
	save_item(NAME(m_crtc_unlock));
	save_item(NAME(m_sprite_count));
	save_item(NAME(m_sprite_priority));
	save_item(NAME(m_sprite_color_code));
	save_item(NAME(m_sprite_xoffset));
	save_item(NAME(m_sprite_yoffset));
	save_item(NAME(m_screen_xoffset));
	save_item(NAME(m_screen_yoffset));
	save_item(NAME(m_layer_priority));
	save_item(NAME(m_background_color));
//  save_item(NAME(m_window));
//  save_item(NAME(m_scroll));
	save_item(NAME(m_screen_blank));
	save_item(NAME(m_screen_flip));

//  memory_region *devregion =  machine().root_device().memregion(":gfx1");
//  m_gfxrom = devregion->base();
	if (m_gfxrom == nullptr)
		fatalerror("Imagetek i4100 %s: \"gfx1\" memory base not found",this->tag());

	m_gfxrom_size = m_gfxrom.bytes();

	m_blit_irq_cb.resolve_safe();
	m_blit_done_timer = timer_alloc(TIMER_BLIT_END);

}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void imagetek_i4100_device::device_reset()
{
	expand_gfx1();
}


void imagetek_i4100_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		case TIMER_BLIT_END:
			m_blit_irq_cb(ASSERT_LINE);
			break;
	}
}

//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

READ16_MEMBER(imagetek_i4100_device::vram_0_r){ return m_vram_0[offset]; }
READ16_MEMBER(imagetek_i4100_device::vram_1_r){ return m_vram_1[offset]; }
READ16_MEMBER(imagetek_i4100_device::vram_2_r){ return m_vram_2[offset]; }
WRITE16_MEMBER(imagetek_i4100_device::vram_0_w){ COMBINE_DATA(&m_vram_0[offset]); }
WRITE16_MEMBER(imagetek_i4100_device::vram_1_w){ COMBINE_DATA(&m_vram_1[offset]); }
WRITE16_MEMBER(imagetek_i4100_device::vram_2_w){ COMBINE_DATA(&m_vram_2[offset]); }

/* Some game uses almost only the blitter to write to the tilemaps.
   The CPU can only access a "window" of 512x256 pixels in the upper
   left corner of the big tilemap */
// TODO: Puzzlet, Sankokushi & Lady Killer contradicts with aformentioned description (more like RMW?)

#define RMW_OFFS( _x_ ) ((_x_) & (0x3f)) + (((_x_) & ~(0x3f)) * (0x100 / 0x40))

READ16_MEMBER(imagetek_i4100_device::rmw_vram_0_r){ return m_vram_0[RMW_OFFS(offset)]; }
READ16_MEMBER(imagetek_i4100_device::rmw_vram_1_r){ return m_vram_1[RMW_OFFS(offset)]; }
READ16_MEMBER(imagetek_i4100_device::rmw_vram_2_r){ return m_vram_2[RMW_OFFS(offset)]; }
WRITE16_MEMBER(imagetek_i4100_device::rmw_vram_0_w){ COMBINE_DATA(&m_vram_0[RMW_OFFS(offset)]); }
WRITE16_MEMBER(imagetek_i4100_device::rmw_vram_1_w){ COMBINE_DATA(&m_vram_1[RMW_OFFS(offset)]); }
WRITE16_MEMBER(imagetek_i4100_device::rmw_vram_2_w){ COMBINE_DATA(&m_vram_2[RMW_OFFS(offset)]); }

#undef RMW_OFFS

READ16_MEMBER(imagetek_i4100_device::scratchram_r ) { return m_scratchram[offset]; }
WRITE16_MEMBER(imagetek_i4100_device::scratchram_w ) { COMBINE_DATA(&m_scratchram[offset]); }
READ16_MEMBER(imagetek_i4100_device::spriteram_r ) { return m_spriteram[offset]; }
WRITE16_MEMBER(imagetek_i4100_device::spriteram_w ) { COMBINE_DATA(&m_spriteram[offset]); }
READ16_MEMBER(imagetek_i4100_device::tiletable_r ) { return m_tiletable[offset]; }
WRITE16_MEMBER(imagetek_i4100_device::tiletable_w ) { COMBINE_DATA(&m_tiletable[offset]); }

// video registers
/*************************************************************
 *
 * 0.w  ---- ---- ---- ----     Number Of Sprites To Draw
 *
 ************************************************************/
READ16_MEMBER(imagetek_i4100_device::sprite_count_r) { return m_sprite_count; }
WRITE16_MEMBER(imagetek_i4100_device::sprite_count_w) { COMBINE_DATA(&m_sprite_count); }

/*************************************************************
 *
 * 2.w  f--- ---- ---- ----     Disable Sprites Layer Priority
 *      -edc ---- ---- ----
 *      ---- ba-- ---- ----     Sprites Masked Layer
 *      ---- --98 ---- ----     Sprites Priority
 *      ---- ---- 765- ----
 *      ---- ---- ---4 3210     Sprites Masked Number
 *
 *************************************************************/
READ16_MEMBER(imagetek_i4100_device::sprite_priority_r) { return m_sprite_priority; }
WRITE16_MEMBER(imagetek_i4100_device::sprite_priority_w) { COMBINE_DATA(&m_sprite_priority); }

/*************************************************************
 *
 * 4.w  ---- ---- ---- ----     Sprites Y Offset
 * 6.w  ---- ---- ---- ----     Sprites X Offset
 *
 ************************************************************/
READ16_MEMBER(imagetek_i4100_device::sprite_xoffset_r) { return m_sprite_xoffset; }
WRITE16_MEMBER(imagetek_i4100_device::sprite_xoffset_w) { COMBINE_DATA(&m_sprite_xoffset); }
READ16_MEMBER(imagetek_i4100_device::sprite_yoffset_r) { return m_sprite_yoffset; }
WRITE16_MEMBER(imagetek_i4100_device::sprite_yoffset_w) { COMBINE_DATA(&m_sprite_yoffset); }

/*************************************************************
 *
 * 8.w  ---- ---- ---- ----     Sprites Color Codes Start
 *
 ************************************************************/
READ16_MEMBER(imagetek_i4100_device::sprite_color_code_r) { return m_sprite_color_code; }
WRITE16_MEMBER(imagetek_i4100_device::sprite_color_code_w) { COMBINE_DATA(&m_sprite_color_code); }

/*************************************************************
 *
 * 10.w ---- ---- --54 ----     Layer 2 Priority (3 backmost, 0 frontmost)
 *      ---- ---- ---- 32--     Layer 1 Priority
 *      ---- ---- ---- --10     Layer 0 Priority
 *
 ************************************************************/

READ16_MEMBER(imagetek_i4100_device::layer_priority_r)
{
	return (m_layer_priority[2]<<4) | (m_layer_priority[1]<<2) | m_layer_priority[0];
}

WRITE16_MEMBER(imagetek_i4100_device::layer_priority_w)
{
	m_layer_priority[2] = (data >> 4) & 3;
	m_layer_priority[1] = (data >> 2) & 3;
	m_layer_priority[0] = (data >> 0) & 3;
	if((data >> 6) != 0)
		logerror("%s warning: layer_priority_w write with %04x %04x\n",this->tag(),data,mem_mask);
}

/*************************************************************
 *
 * 12.w ---- cccc cccc cccc     Background Color
 *
 ************************************************************/

READ16_MEMBER(imagetek_i4100_device::background_color_r)
{
	return m_background_color;
}

WRITE16_MEMBER(imagetek_i4100_device::background_color_w)
{
	COMBINE_DATA(&m_background_color);

	m_background_color &= 0x0fff;
	if(data & 0xf000)
		logerror("%s warning: background_color_w write with %04x %04x\n",this->tag(),data,mem_mask);
}

/***************************************************************************
 *
 *  0.w                                 Sprite Y center point
 *  2.w                                 Sprite X center point
 *
 * Appears to apply only for sprites, maybe they applies to tilemaps too under
 * certain conditions
 *
 ***************************************************************************/
READ16_MEMBER(imagetek_i4100_device::screen_xoffset_r) { return m_screen_xoffset; }
WRITE16_MEMBER(imagetek_i4100_device::screen_xoffset_w) { COMBINE_DATA(&m_screen_xoffset); }
READ16_MEMBER(imagetek_i4100_device::screen_yoffset_r) { return m_screen_yoffset; }
WRITE16_MEMBER(imagetek_i4100_device::screen_yoffset_w) { COMBINE_DATA(&m_screen_yoffset); }

READ16_MEMBER(imagetek_i4100_device::window_r) { return m_window[offset]; }
WRITE16_MEMBER(imagetek_i4100_device::window_w) { COMBINE_DATA(&m_window[offset]); }
READ16_MEMBER(imagetek_i4100_device::scroll_r) { return m_scroll[offset]; }
WRITE16_MEMBER(imagetek_i4100_device::scroll_w) { COMBINE_DATA(&m_scroll[offset]); }

/****************************************************
 *
 * Screen Control Register:
 *
 * f--- ---- ---- ----     ?
 * -edc b--- ---- ----
 * ---- -a98 ---- ----     ? Leds (see gakusai attract)
 * ---- ---- 765- ----     16x16 Tiles  (Layer 2-1-0)
 * ---- ---- ---4 32--
 * ---- ---- ---- --1-     Blank Screen
 * ---- ---- ---- ---0     Flip  Screen
 *
 ****************************************************/
WRITE16_MEMBER(imagetek_i4100_device::screen_ctrl_w)
{
	m_layer_tile_select[2] = BIT(data,7);
	m_layer_tile_select[1] = BIT(data,6);
	m_layer_tile_select[0] = BIT(data,5);

	// TODO: some of these must be externalized
	m_screen_blank = BIT(data,1);
	m_screen_flip = BIT(data,0);

	if(data & 0xff1c)
		logerror("%s warning: screen_ctrl_w write with %04x %04x\n",this->tag(),data,mem_mask);

}


/*
    The main CPU has access to the ROMs that hold the graphics through
    a banked window of 64k. Those ROMs also usually store the tables for
    the virtual tiles set. The tile codes to be written to the tilemap
    memory to render the backgrounds are also stored here, in a format
    that the blitter can readily use (which is a form of compression)
*/

READ16_MEMBER(imagetek_i4100_device::gfxrom_r)
{
	offset = offset * 2 + 0x10000 * (m_rombank);

	if (offset < m_gfxrom_size)
		return ((m_gfxrom[offset + 0] << 8) + m_gfxrom[offset + 1]);
	else
		return 0xffff;
}

WRITE16_MEMBER( imagetek_i4100_device::rombank_w )
{
	COMBINE_DATA(&m_rombank);
}

WRITE16_MEMBER( imagetek_i4100_device::crtc_horz_w )
{
	if(m_crtc_unlock == true)
	{
		//logerror("%s CRTC horizontal %04x %04x\n",this->tag(),data,mem_mask);
	}
}

WRITE16_MEMBER( imagetek_i4100_device::crtc_vert_w )
{
	if(m_crtc_unlock == true)
	{
		//logerror("%s CRTC vertical %04x %04x\n",this->tag(),data,mem_mask);
	}
}

WRITE16_MEMBER( imagetek_i4100_device::crtc_unlock_w )
{
	m_crtc_unlock = BIT(data,0);
	if(data & ~1)
		logerror("%s warning: unlock register write with %04x %04x\n",this->tag(),data,mem_mask);
}

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


                                    Blitter

    [ Registers ]

        Offset:     Value:

        0.l         Destination Tilemap      (1,2,3)
        4.l         Blitter Data Address     (byte offset into the gfx ROMs)
        8.l         Destination Address << 7 (byte offset into the tilemap)

        The Blitter reads a byte and looks at the most significative
        bits for the opcode, while the remaining bits define a value
        (usually how many bytes to write). The opcode byte may be
        followed by a number of other bytes:

            76------            Opcode
            --543210            N
            (at most N+1 bytes follow)


        The blitter is designed to write every other byte (e.g. it
        writes a byte and skips the next). Hence 2 blits are needed
        to fill a tilemap (first even, then odd addresses)

    [ Opcodes ]

            0       Copy the following N+1 bytes. If the whole byte
                    is $00: stop and generate an IRQ

            1       Fill N+1 bytes with a sequence, starting with
                    the  value in the following byte

            2       Fill N+1 bytes with the value in the following
                    byte

            3       Skip N+1 bytes. If the whole byte is $C0:
                    skip to the next row of the tilemap (+0x200 bytes)
                    but preserve the column passed at the start of the
                    blit (destination address % 0x200)


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

void imagetek_i4100_device::blt_write( address_space &space, const int tmap, const offs_t offs, const uint16_t data, const uint16_t mask )
{
	switch(tmap)
	{
		case 1: vram_0_w(space, offs, data, mask);    break;
		case 2: vram_1_w(space, offs, data, mask);    break;
		case 3: vram_2_w(space, offs, data, mask);    break;
	}
//  logerror("%s : Blitter %X] %04X <- %04X & %04X\n", machine().describe_context(), tmap, offs, data, mask);
}

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


                                    Blitter

    [ Registers ]

        Offset:     Value:

        0.l         Destination Tilemap      (1,2,3)
        4.l         Blitter Data Address     (byte offset into the gfx ROMs)
        8.l         Destination Address << 7 (byte offset into the tilemap)

        The Blitter reads a byte and looks at the most significative
        bits for the opcode, while the remaining bits define a value
        (usually how many bytes to write). The opcode byte may be
        followed by a number of other bytes:

            76------            Opcode
            --543210            N
            (at most N+1 bytes follow)


        The blitter is designed to write every other byte (e.g. it
        writes a byte and skips the next). Hence 2 blits are needed
        to fill a tilemap (first even, then odd addresses)

    [ Opcodes ]

            0       Copy the following N+1 bytes. If the whole byte
                    is $00: stop and generate an IRQ

            1       Fill N+1 bytes with a sequence, starting with
                    the  value in the following byte

            2       Fill N+1 bytes with the value in the following
                    byte

            3       Skip N+1 bytes. If the whole byte is $C0:
                    skip to the next row of the tilemap (+0x200 bytes)
                    but preserve the column passed at the start of the
                    blit (destination address % 0x200)


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


// TODO: clean this up
WRITE16_MEMBER( imagetek_i4100_device::blitter_w )
{
	COMBINE_DATA(&m_blitter_regs[offset]);

	if (offset == 0x0c / 2)
	{
		//uint8_t *src     = memregion("gfx1")->base();

		uint32_t tmap     = (m_blitter_regs[0x00 / 2] << 16) + m_blitter_regs[0x02 / 2];
		uint32_t src_offs = (m_blitter_regs[0x04 / 2] << 16) + m_blitter_regs[0x06 / 2];
		uint32_t dst_offs = (m_blitter_regs[0x08 / 2] << 16) + m_blitter_regs[0x0a / 2];

		int shift   = (dst_offs & 0x80) ? 0 : 8;
		uint16_t mask = (dst_offs & 0x80) ? 0x00ff : 0xff00;

//      logerror("%s Blitter regs %08X, %08X, %08X\n", machine().describe_context(), tmap, src_offs, dst_offs);

		dst_offs >>= 7 + 1;
		switch (tmap)
		{
			case 1:
			case 2:
			case 3:
				break;
			default:
				logerror("%s Blitter unknown destination: %08X\n", machine().describe_context(), tmap);
				return;
		}

		while (1)
		{
			uint16_t b1, b2, count;

			src_offs %= m_gfxrom_size;
			b1 = m_gfxrom[src_offs];
//          logerror("%s Blitter opcode %02X at %06X\n", machine().describe_context(), b1, src_offs);
			src_offs++;

			count = ((~b1) & 0x3f) + 1;

			switch ((b1 & 0xc0) >> 6)
			{
			case 0:
				/* Stop and Generate an IRQ. We can't generate it now
				       both because it's unlikely that the blitter is so
				       fast and because some games (e.g. lastfort) need to
				       complete the blitter irq service routine before doing
				       another blit. */
				if (b1 == 0)
				{
					m_blit_done_timer->adjust(attotime::from_usec(500));
					return;
				}

				/* Copy */
				while (count--)
				{
					src_offs %= m_gfxrom_size;
					b2 = m_gfxrom[src_offs] << shift;
					src_offs++;

					dst_offs &= 0xffff;
					blt_write(space, tmap, dst_offs, b2, mask);
					dst_offs = ((dst_offs + 1) & (0x100 - 1)) | (dst_offs & (~(0x100 - 1)));
				}
				break;

			case 1:
				/* Fill with an increasing value */
				src_offs %= m_gfxrom_size;
				b2 = m_gfxrom[src_offs];
				src_offs++;

				while (count--)
				{
					dst_offs &= 0xffff;
					blt_write(space, tmap, dst_offs, b2 << shift, mask);
					dst_offs = ((dst_offs + 1) & (0x100 - 1)) | (dst_offs & (~(0x100 - 1)));
					b2++;
				}
				break;

			case 2:
				/* Fill with a fixed value */
				src_offs %= m_gfxrom_size;
				b2 = m_gfxrom[src_offs] << shift;
				src_offs++;

				while (count--)
				{
					dst_offs &= 0xffff;
					blt_write(space, tmap, dst_offs, b2, mask);
					dst_offs = ((dst_offs + 1) & (0x100 - 1)) | (dst_offs & (~(0x100 - 1)));
				}
				break;

			case 3:
				/* Skip to the next line ?? */
				if (b1 == 0xc0)
				{
					dst_offs +=   0x100;
					dst_offs &= ~(0x100 - 1);
					dst_offs |=  (0x100 - 1) & (m_blitter_regs[0x0a / 2] >> (7 + 1));
				}
				else
				{
					dst_offs += count;
				}
				break;

			default:
				//logerror("%s Blitter unknown opcode %02X at %06X\n",machine().describe_context(),b1,src_offs-1);
				return;
			}

		}
	}
}



/***************************************************************************
 *
 * Screen Drawing
 *
 ***************************************************************************/

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


                                Sprites Drawing


        Offset:     Bits:                   Value:

        0.w         fedc b--- ---- ----     Priority (0 = Max)
                    ---- -a98 7654 3210     X

        2.w         fedc ba-- ---- ----     Zoom (Both X & Y)
                    ---- --98 7654 3210     Y

        4.w         f--- ---- ---- ----     Flip X
                    -e-- ---- ---- ----     Flip Y
                    --dc b--- ---- ----     Size X *
                    ---- -a98 ---- ----     Size Y *
                    ---- ---- 7654 ----     Color
                    ---- ---- ---- 3210     Code High Bits **

        6.w                                 Code Low Bits  **

*  8 pixel increments
** 8x8 pixel increments

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

void imagetek_i4100_device::draw_sprites( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect )
{
	uint8_t *base_gfx4 = m_expanded_gfx1.get();
	uint8_t *base_gfx8 = m_gfxrom;
	uint32_t gfx_size = m_gfxrom_size;

	int max_x = (m_screen_xoffset+1)*2;
	int max_y = (m_screen_yoffset+1)*2;
	int m_sprite_xoffs = m_sprite_xoffset - (m_screen_xoffset+1);
	int m_sprite_yoffs = m_sprite_yoffset - (m_screen_yoffset+1);

	int max_sprites = m_spriteram.bytes() / 8;
	int sprites     = m_sprite_count % max_sprites;

	int color_start = (m_sprite_color_code & 0x0f) << 4;

	int i, j, pri;
	static const int primask[4] = { 0x0000, 0xff00, 0xff00 | 0xf0f0, 0xff00 | 0xf0f0 | 0xcccc };

	uint16_t *src;
	int inc;

	if (sprites == 0)
		return;

	for (i = 0; i < 0x20; i++)
	{
		if (!(m_sprite_priority & 0x8000))
		{
			src = m_spriteram + (sprites - 1) * (8 / 2);
			inc = -(8 / 2);
		} else {
			src = m_spriteram;
			inc = (8 / 2);
		}

		for (j = 0; j < sprites; j++)
		{
			int x, y, attr, code, color, flipx, flipy, zoom, curr_pri, width, height;

			// Exponential zoom table extracted from daitoride
			static const int zoomtable[0x40] =
			{   0xAAC,0x800,0x668,0x554,0x494,0x400,0x390,0x334,
				0x2E8,0x2AC,0x278,0x248,0x224,0x200,0x1E0,0x1C8,
				0x1B0,0x198,0x188,0x174,0x164,0x154,0x148,0x13C,
				0x130,0x124,0x11C,0x110,0x108,0x100,0x0F8,0x0F0,
				0x0EC,0x0E4,0x0DC,0x0D8,0x0D4,0x0CC,0x0C8,0x0C4,
				0x0C0,0x0BC,0x0B8,0x0B4,0x0B0,0x0AC,0x0A8,0x0A4,
				0x0A0,0x09C,0x098,0x094,0x090,0x08C,0x088,0x080,
				0x078,0x070,0x068,0x060,0x058,0x050,0x048,0x040 };

			x = src[0];
			curr_pri = (x & 0xf800) >> 11;

			if ((curr_pri == 0x1f) || (curr_pri != i))
			{
				src += inc;
				continue;
			}

			pri = (m_sprite_priority & 0x0300) >> 8;

			if (!(m_sprite_priority & 0x8000))
			{
				if (curr_pri > (m_sprite_priority & 0x1f))
					pri = (m_sprite_priority & 0x0c00) >> 10;
			}

			y     = src[1];
			attr  = src[2];
			code  = src[3];

			flipx =  attr & 0x8000;
			flipy =  attr & 0x4000;
			color = (attr & 0xf0) >> 4;

			zoom = zoomtable[(y & 0xfc00) >> 10] << (16 - 8);

			x = (x & 0x07ff) - m_sprite_xoffs;
			y = (y & 0x03ff) - m_sprite_yoffs;

			width  = (((attr >> 11) & 0x7) + 1) * 8;
			height = (((attr >>  8) & 0x7) + 1) * 8;

			uint32_t gfxstart = (8 * 8 * 4 / 8) * (((attr & 0x000f) << 16) + code);

			if (m_screen_flip)
			{
				flipx = !flipx;     x = max_x - x - width;
				flipy = !flipy;     y = max_y - y - height;
			}

			if (m_support_8bpp == true && color == 0xf)  /* 8bpp */
			{
				/* Bounds checking */
				if ((gfxstart + width * height - 1) >= gfx_size)
					continue;

				gfx_element gfx(m_palette, base_gfx8 + gfxstart, width, height, width, m_palette->entries(), 0, 256);

				gfx.prio_zoom_transpen(bitmap,cliprect,
								0,
								color_start >> 4,
								flipx, flipy,
								x, y,
								zoom, zoom,
								screen.priority(),primask[pri], 255);
			}
			else
			{
				/* Bounds checking */
				if ((gfxstart + width / 2 * height - 1) >= gfx_size)
					continue;

				gfx_element gfx(m_palette, base_gfx4 + 2 * gfxstart, width, height, width, m_palette->entries(),0, 16);

				gfx.prio_zoom_transpen(bitmap,cliprect,
								0,
								color + color_start,
								flipx, flipy,
								x, y,
								zoom, zoom,
								screen.priority(),primask[pri], 15);
			}

			src += inc;
		}
	}
}


 inline uint8_t imagetek_i4100_device::get_tile_pix( uint16_t code, uint8_t x, uint8_t y, bool big, uint16_t *pix )
{
	int table_index;
	uint32_t tile;

	// Use code as an index into the tiles set table
	table_index = ((code & 0x1ff0) >> 4) * 2;
	tile = (m_tiletable[table_index + 0] << 16) + m_tiletable[table_index + 1];

	if (code & 0x8000) // Special: draw a tile of a single color (i.e. not from the gfx ROMs)
	{
		*pix = code & 0x0fff;

		if ((*pix & 0xf) != 0xf)
			return 1;
		else
			return 0;
	}
	else if (((tile & 0x00f00000) == 0x00f00000) && (m_support_8bpp == true)) /* draw tile as 8bpp (e.g. balcube bg) */
	{
		gfx_element *gfx1 = m_gfxdecode->gfx(big?3:1);
		uint32_t tile2 = big ? ((tile & 0xfffff) + 8*(code & 0xf)) :
								((tile & 0xfffff) + 2*(code & 0xf));
		const uint8_t* data;
		uint8_t flipxy = (code & 0x6000) >> 13;

		if (tile2 < gfx1->elements())
			data = gfx1->get_data(tile2);
		else
		{
			*pix = 0;
			return 0;
		}

		switch (flipxy)
		{
			default:
			case 0x0: *pix = data[(y              * (big?16:8)) + x];              break;
			case 0x1: *pix = data[(((big?15:7)-y) * (big?16:8)) + x];              break;
			case 0x2: *pix = data[(y              * (big?16:8)) + ((big?15:7)-x)]; break;
			case 0x3: *pix = data[(((big?15:7)-y) * (big?16:8)) + ((big?15:7)-x)]; break;
		}

		*pix |= ((tile & 0x0f000000) >> 24) * 0x100;

		if ((*pix & 0xff) != 0xff)
			return 1;
		else
			return 0;
	}
	else
	{
		gfx_element *gfx1 = m_gfxdecode->gfx(big?2:0);
		uint32_t tile2 = big ? ((tile & 0xfffff) + 4*(code & 0xf)) :
								((tile & 0xfffff) +   (code & 0xf));
		const uint8_t* data;
		uint8_t flipxy = (code & 0x6000) >> 13;

		if (tile2 < gfx1->elements())
			data = gfx1->get_data(tile2);
		else
		{
			*pix = 0;
			return 0;
		}

		switch (flipxy)
		{
			default:
			case 0x0: *pix = data[(y              * (big?16:8)) + x];             break;
			case 0x1: *pix = data[(((big?15:7)-y) * (big?16:8)) + x];             break;
			case 0x2: *pix = data[(y              * (big?16:8)) + ((big?15:7)-x)]; break;
			case 0x3: *pix = data[(((big?15:7)-y) * (big?16:8)) + ((big?15:7)-x)]; break;
		}

		*pix |= (((tile & 0x0ff00000) >> 20)) * 0x10;

		if ((*pix & 0xf) != 0xf)
			return 1;
		else
			return 0;
	}
}

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

                        Tilemaps: Tiles Set & Window

    Each entry in the Tiles Set RAM uses 2 words to specify a starting
    tile code and a color code. This adds 16 consecutive tiles with
    that color code to the set of available tiles.

        Offset:     Bits:                   Value:

        0.w         fedc ---- ---- ----
                    ---- ba98 7654 ----     Color Code*
                    ---- ---- ---- 3210     Code High Bits

        2.w                                 Code Low Bits

* 00-ff, but on later chips supporting it, xf means 256 color tile and palette x

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

void imagetek_i4100_device::draw_tilemap( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, uint32_t flags, uint32_t pcode,
							int sx, int sy, int wx, int wy, bool big, uint16_t *tilemapram, int layer )
{
	int y;

	bitmap_ind8 &priority_bitmap = screen.priority();

	int width  = big ? 4096 : 2048;
	int height = big ? 4096 : 2048;

	int scrwidth  = bitmap.width();
	int scrheight = bitmap.height();

	int windowwidth  = width >> 2;
	int windowheight = height >> 3;

	int dx = m_tilemap_scrolldx[layer] * (m_screen_flip ? 1 : -1);
	int dy = m_tilemap_scrolldy[layer] * (m_screen_flip ? 1 : -1);

	sx += dx;
	sy += dy;

	int min_x, max_x, min_y, max_y;

	if (dx != 0)
	{
		min_x = 0;
		max_x = scrwidth-1;
	}
	else
	{
		min_x = cliprect.min_x;
		max_x = cliprect.max_x;
	}

	if (dy != 0)
	{
		min_y = 0;
		max_y = scrheight-1;
	}
	else
	{
		min_y = cliprect.min_y;
		max_y = cliprect.max_y;
	}

	for (y = min_y; y <= max_y; y++)
	{
		int scrolly = (sy+y-wy)&(windowheight-1);
		int x;
		uint16_t *dst;
		uint8_t *priority_baseaddr;
		int srcline = (wy+scrolly)&(height-1);
		int srctilerow = srcline >> (big ? 4 : 3);

		if (!m_screen_flip)
		{
			dst = &bitmap.pix16(y);
			priority_baseaddr = &priority_bitmap.pix8(y);

			for (x = min_x; x <= max_x; x++)
			{
				int scrollx = (sx+x-wx)&(windowwidth-1);
				int srccol = (wx+scrollx)&(width-1);
				int srctilecol = srccol >> (big ? 4 : 3);
				int tileoffs = srctilecol + srctilerow * BIG_NX;

				uint16_t dat = 0;

				uint16_t tile = tilemapram[tileoffs];
				uint8_t draw = get_tile_pix(tile, big ? (srccol&0xf) : (srccol&0x7), big ? (srcline&0xf) : (srcline&0x7), big, &dat);

				if (draw)
				{
					dst[x] = dat;
					priority_baseaddr[x] = (priority_baseaddr[x] & (pcode >> 8)) | pcode;
				}
			}
		}
		else // flipped case
		{
			dst = &bitmap.pix16(scrheight-y-1);
			priority_baseaddr = &priority_bitmap.pix8(scrheight-y-1);

			for (x = min_x; x <= max_x; x++)
			{
				int scrollx = (sx+x-wx)&(windowwidth-1);
				int srccol = (wx+scrollx)&(width-1);
				int srctilecol = srccol >> (big ? 4 : 3);
				int tileoffs = srctilecol + srctilerow * BIG_NX;

				uint16_t dat = 0;

				uint16_t tile = tilemapram[tileoffs];
				uint8_t draw = get_tile_pix(tile, big ? (srccol&0xf) : (srccol&0x7), big ? (srcline&0xf) : (srcline&0x7), big, &dat);

				if (draw)
				{
					dst[scrwidth-x-1] = dat;
					priority_baseaddr[scrwidth-x-1] = (priority_baseaddr[scrwidth-x-1] & (pcode >> 8)) | pcode;
				}
			}
		}
	}
}


void imagetek_i4100_device::draw_layers( screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int pri )
{
	// Draw all the layers with priority == pri
	for (int layer = 2; layer >= 0; layer--)
	{
		if (pri == m_layer_priority[layer])
		{
			// Scroll and Window values
			uint16_t sy = m_scroll[layer * 2 + 0]; uint16_t sx = m_scroll[layer * 2 + 1];
			uint16_t wy = m_window[layer * 2 + 0]; uint16_t wx = m_window[layer * 2 + 1];

			uint16_t *tilemapram = nullptr;

			switch (layer)
			{
				case 0: tilemapram = m_vram_0;   break;
				case 1: tilemapram = m_vram_1;   break;
				case 2: tilemapram = m_vram_2;   break;
			}

			bool big = (m_support_16x16 && m_layer_tile_select[layer]) == 1;

			draw_tilemap(screen, bitmap, cliprect, 0, 1 << (3 - pri), sx, sy, wx, wy, big, tilemapram, layer);
		}
	}
}


uint32_t imagetek_i4100_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	screen.priority().fill(0, cliprect);

	bitmap.fill(m_background_color, cliprect);

	if (m_screen_blank == true)
		return 0;

	for (int pri = 3; pri >= 0; pri--)
		draw_layers(screen, bitmap, cliprect, pri);

	draw_sprites(screen, bitmap, cliprect);

	return 0;
}