summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/video/gba_lcd.cpp
blob: f72bef022f5bad31f83d888c3457d91031ae1723 (plain) (tree)
1
2
3
4
5
6
7
8
9
                       
                                          
                                                                            
 
             
 
                                                                          
 
                             



                                                                            

                    
                    
                   
 
 





                                                                                        


                                                                      




                                                                       


                                                                       




                                                                                                 
                                                                                           




                                                                                                 
                                                                                           




                                                                                         


                                                                                                    


                                                                                               
                                                                                                 
 


                                                      
                           
 
                                                                                                     
 
                                     

                          




                                                                                      


         


            
                                                          

                                  


                                               

         


                                                          
                                                            

                                                          
 



                                                                
 


                                                                 
 
                                  




                                
                                                                       









                                                                                              
 

                                                     










                                                                                                           


                         

  
                                                                 
 
                                                                                                               
                                                       





                                                
 

 
                                              
 
                                        
 




                                                                                                   
 
                    

 
                                                
 

                                                
 



                                              
 


                                                
 
 
                                                 
 

                                                 
 
                                                               


                                               
 
                                                           


                               
 
                                                             

                                                 

 
                                                               
 

                                                
 
                                                                                               





                                                                                                            
 

                                           
 

                                           

 
                                                           


                                                                    
 
                                                      
 
                                                

 
                                                          
 

                                                             
 
                                                             


                            
 

                                              
 

                                              
 
                                      
         
                                          
 







                                                          


         
                                         
 
                                                

                                          
         






                                              
                                       
 
                        


















                                                                                                
                 
                                                             

                 
 
                          

                     



















                                                                                                                                                   
         
 
                          
 

                                   
 
                         
         
                                                  
                                     




                                                 
 
                                                                       
 

                                      

                                          
 
                                            
                 




                                                                                              
                 
 

                                       
                                                        
                         

                                                         
 
                                                            
                                 




                                                                                                             
                                 
 
                                                                            
                                 
                                                                         
                                 
                                                                               
                                 
                                                            
                                         







                                                                                   



                                         
                                                                          
                 
                                                                  
                         
                                                    
                                 



                                                

                                                                 
 


                                                                                                                     
                                                 
                                                                               
                                                         

                                                                                        
                                                         
                                                 
                                         
 

                                                                                    
 







                                                                           


                                 
                                             
         
 
 
                                                                                                                                                                                                                                              
 


                               





                                                              
 
                                           
         
                               
                                       
 


                                            
 






                                            
 








                                        
 








                                        
 

                                     
 

                                           

                                                                    


                                    
 

                               
 


                                                                     
                 
                                       
                         
                                                                       

                                                                            


                            
                                                                             
                         
                 
 

                         
 


                               
 

                                           
                                                                
                                
                 
                                      
                                                      
                         


                                                              
                                 

                                              





                                 
                                                                                                                                                                                                                            
 


                               




                                                                  



                                                   
 






                                            
 
                            
         






                                        
 









                                        

                                     


                                           
                                                                    



                                           
 

                               




                                               
 









                                                                            
                 



                                                                                                            

                                                                                                      



                                                                          

                 


                         


                               
                                                       
                 

                                       
 

                                              
 

                                               
                 
         
 

                                           
                                                                
                                
                 

                                                      
                         


                                                              
                                 

                                              





                                 
                                                                                                                                   
 


                               






                                                                       
 
                          

                                                  

                                              
















                                                             
                                          
 
                                                                                        
 
                                      
         





                                                 
 

                                          
 

                                          
 
                                                     
                 

                                                                             
                 
                    
                 






                                                                                    
                 
 

                                                                           
 





                                       
                 






                                        
                         
                                                                  
                         
                            
                         

                                                          
                         






                                                  
 

                                                          
                              




                                                    
                         

                                      
                         


                 
 
                                                               



                                         

                                                







                                                               
                                                         



                                                               

                                         








                                        

                                            




                                             
                         
 
                                              


                                                   
 



                                                                  



                                                                    
 

                                                                                                   



                                              
                         





                                                                 
                                 

                                                             


                                                                                     
                                                                                                                                                                                         







                                                                         

                                 
                            
                         

                                                                 
 
                                                             
                                 

                                                             


                                                                                     
                                                                                                                                                                                                












                                                                         
                                 







                                                                                        
                                              











                                                                         
                                                                   




                                                                 
                                    

                                                             
                                                                                                                                                                     




                                                                
                                 

                                                     
                                                                             


































                                                                          

                                 
                            
                         

                                                                 
 
                                                                                                                                                                                  

                                                
                                 


                                                                             
                                         

                                                             
                                                                                     






























                                                                                 
                                                                                     























                                                                                 
                                         





                                 
                                                        
 

                                     
 





                                                         
 
                                                              
         
                                                  
 

                                                            
 

                                                                          



                                        
                                                         





                                                               

                                                 
 
                                     
                                          
 

                                            

                                             
                         

                                        
                         
 
                                              
 

                                                   
 

                                                                  
 
                                                           
 



                                                                         
 

                                                         
 

                                                                                                   
 
                                         




                                                                 
                                    
                                                             
 

                                                             

                                                               
 
                                                                                                                    
                                         
                                                                                                                                                                                           

                                                                                                        
                                                 

                                                                                                          
                                                         










                                                                                                                      
 


                                                                                              
 








                                                                 
 

                                                             

                                                               
 

                                                                                                                    
                                                                                                                                                                                                  
 










                                                                                                                      
                                                         




                                                                                                                   
                                                         
                                                                                                                      


                                                         






                                                                                      


                                 




                                                                                        
 

                                                 
 

                                          
 
                                              
 

                                                       
 

                                                                     
 
                                         
 

                                                         
 
                                                                     
 

                                                         
 





                                                                 
 
                                                                                                                                                                         
 

                                                 
 


                                                                
                                         
                                                                             
 



                                                                                                          
                                                         
                                                                                                                      
                                                         




                                                                                                          
                                                         



                                                                                                                      
 

                                                                                      
 
                                                            
 



                                                          
 




                                                                      
 






                                                                          
 



                                                                      
                                                 


                                                                          

                                         




                                                                 
 
                                                                                                                                                                                
 



                                                                             
                                         
                                                             
                                                 
                                                                                     
 



                                                                              
 



                                                                                                                  
                                                                 
                                                                                                                              
                                                                 




                                                                                                                           
                                                                 
                                                                                                                              
                                                                 

                                                         
 

                                                                    
 







                                                                      
 









                                                                          
                                                                                     
 



                                                                              
 



                                                                                                                  
                                                                 
                                                                                                                              

                                                                 
                                                                                                                  
                                                         

                                                                                                                           
                                                                 
                                                                                                                              
                                                                 

                                                         
 

                                                                                              
 

                                                                    
 

                                                                  
 



                                                                      
                                                 


                                                                          






                                         
                                                             
 
                                                     
 

                                  
 
                     
         

                                      


            

                                      

         
                     

 
                                                             
 
                                                     
 

                                  


                                       
 
                     
         

                                      
         
            



                                      
 

                     
 



                                                                      
 
                                                                             


                                               
 
                                        
         














                                                                        
         



                      
                                                                   

















                                                               
                                                                   















                                                               



















                                                     
                            
 
                       
         
                      
                                                            










                                                              






























                                                                                                                          
                       
         











                                     


         






                                          
                                      








                                          
                                      








                                         
                                     



                                                  
                                       
 








                                                                           




                                        

                                                     

         
                                            
         

                                                     
         
 
                              

                                             

 
                                                   
 

                                
 
                                       
 
                                                                                            

                                              
                                      



                                              
                                                            
                         

                                                                     

                         

                                                             



                 
                                        

         
                                             

                                                 
                                      
 
                                                    
                 

                                                             


                 

                                                                               

 

                                        
                                        
         
                                                
                 
                                                        
                         
                                                                                                                    




                         
                                                                                                              

                                                           
 

                 


                                   





                                  
 


                                                            
 
                                                  



                                                                                                                          
                                                            
 

                                


                                                
 












                                       





                                          



                              
 
                                                            


                                             
                                                        
                                      
                                                                              






                                                                             
// license:BSD-3-Clause
// copyright-holders:R. Belmont,Ryan Holtz
/***************************************************************************

    gba_lcd.c

    File to handle emulation of the video hardware of the Game Boy Advance

    By R. Belmont, Ryan Holtz

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

#include "emu.h"
#include "gba_lcd.h"

#include "rendlay.h"
#include "screen.h"


/* LCD I/O Registers */
#define DISPCNT     HWLO(0x000)  /* 0x4000000  2  R/W   LCD Control */
#define GRNSWAP     HWHI(0x000)  /* 0x4000002  2  R/W   Undocumented - Green Swap */
#define DISPSTAT    HWLO(0x004)  /* 0x4000004  2  R/W   General LCD Status (STAT,LYC) */
#define VCOUNT      HWHI(0x004)  /* 0x4000006  2  R     Vertical Counter (LY) */
#define BG0CNT      HWLO(0x008)  /* 0x4000008  2  R/W   BG0 Control */
#define BG1CNT      HWHI(0x008)  /* 0x400000a  2  R/W   BG1 Control */
#define BG2CNT      HWLO(0x00c)  /* 0x400000c  2  R/W   BG2 Control */
#define BG3CNT      HWHI(0x00c)  /* 0x400000e  2  R/W   BG3 Control */
#define BG0HOFS     HWLO(0x010)  /* 0x4000010  2  W     BG0 X-Offset */
#define BG0VOFS     HWHI(0x010)  /* 0x4000012  2  W     BG0 Y-Offset */
#define BG1HOFS     HWLO(0x014)  /* 0x4000014  2  W     BG1 X-Offset */
#define BG1VOFS     HWHI(0x014)  /* 0x4000016  2  W     BG1 Y-Offset */
#define BG2HOFS     HWLO(0x018)  /* 0x4000018  2  W     BG2 X-Offset */
#define BG2VOFS     HWHI(0x018)  /* 0x400001a  2  W     BG2 Y-Offset */
#define BG3HOFS     HWLO(0x01c)  /* 0x400001c  2  W     BG3 X-Offset */
#define BG3VOFS     HWHI(0x01c)  /* 0x400001e  2  W     BG3 Y-Offset */
#define BG2PA       HWLO(0x020)  /* 0x4000020  2  W     BG2 Rotation/Scaling Parameter A (dx) */
#define BG2PB       HWHI(0x020)  /* 0x4000022  2  W     BG2 Rotation/Scaling Parameter B (dmx) */
#define BG2PC       HWLO(0x024)  /* 0x4000024  2  W     BG2 Rotation/Scaling Parameter C (dy) */
#define BG2PD       HWHI(0x024)  /* 0x4000026  2  W     BG2 Rotation/Scaling Parameter D (dmy) */
#define BG2X        WORD(0x028)  /* 0x4000028  4  W     BG2 Reference Point X-Coordinate */
#define BG2Y        WORD(0x02c)  /* 0x400002c  4  W     BG2 Reference Point Y-Coordinate */
#define BG3PA       HWLO(0x030)  /* 0x4000030  2  W     BG3 Rotation/Scaling Parameter A (dx) */
#define BG3PB       HWHI(0x030)  /* 0x4000032  2  W     BG3 Rotation/Scaling Parameter B (dmx) */
#define BG3PC       HWLO(0x034)  /* 0x4000034  2  W     BG3 Rotation/Scaling Parameter C (dy) */
#define BG3PD       HWHI(0x034)  /* 0x4000036  2  W     BG3 Rotation/Scaling Parameter D (dmy) */
#define BG3X        WORD(0x038)  /* 0x4000038  4  W     BG3 Reference Point X-Coordinate */
#define BG3Y        WORD(0x03c)  /* 0x400003c  4  W     BG3 Reference Point Y-Coordinate */
#define WIN0H       HWLO(0x040)  /* 0x4000040  2  W     Window 0 Horizontal Dimensions */
#define WIN1H       HWHI(0x040)  /* 0x4000042  2  W     Window 1 Horizontal Dimensions */
#define WIN0V       HWLO(0x044)  /* 0x4000044  2  W     Window 0 Vertical Dimensions */
#define WIN1V       HWHI(0x044)  /* 0x4000046  2  W     Window 1 Vertical Dimensions */
#define WININ       HWLO(0x048)  /* 0x4000048  2  R/W   Inside of Window 0 and 1 */
#define WINOUT      HWHI(0x048)  /* 0x400004a  2  R/W   Inside of OBJ Window & Outside of Windows */
#define MOSAIC      HWLO(0x04c)  /* 0x400004c  2  W     Mosaic Size */
								 /* 0x400004e  2  -     Unused */
#define BLDCNT      HWLO(0x050)  /* 0x4000050  2  R/W   Color Special Effects Selection */
#define BLDALPHA    HWHI(0x050)  /* 0x4000052  2  W     Alpha Blending Coefficients */
#define BLDY        HWLO(0x054)  /* 0x4000054  2  W     Brightness (Fade-In/Out) Coefficient */
								 /* 0x4000056  2  -     Unused */

#define DISPSTAT_SET(val)       HWLO_SET(0x004, val)
#define DISPSTAT_RESET(val)     HWLO_RESET(0x004, val)

#define VERBOSE_LEVEL   (0)

static inline void ATTR_PRINTF(3,4) verboselog(device_t &device, 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);
		device.logerror("%08x: %s", device.machine().describe_context(), buf);
	}
}

class object
{
public:
	object(device_t &device, uint16_t *oam, int index)
		: m_device(device)
	{
		m_attr0 = oam[(4 * index) + 0];
		m_attr1 = oam[(4 * index) + 1];
		m_attr2 = oam[(4 * index) + 2];
	}

	int    pos_y()       { return  m_attr0 & 0x00ff; }
	bool   roz()         { return  m_attr0 & 0x0100; }
	bool   roz_double()  { return  m_attr0 & 0x0200; }
	uint16_t mode_mask()   { return  m_attr0 & 0x0c00; }
	bool   mosaic()      { return  m_attr0 & 0x1000; }
	bool   palette_256() { return  m_attr0 & 0x2000; }

	int    pos_x()       { return  m_attr1 & 0x01ff; }
	int    roz_param()   { return (m_attr1 & 0x3e00) >> 9; }
	bool   hflip()       { return  m_attr1 & 0x1000; }
	bool   vflip()       { return  m_attr1 & 0x2000; }

	int    tile_number() { return  m_attr2 & 0x03ff; }
	int    priority()    { return (m_attr2 & 0x0c00) >> 10; }
	int    palette()     { return (m_attr2 & 0xf000) >> 8; }

	enum class mode : uint16_t
	{
		normal = 0x0000,
		alpha  = 0x0400,
		window = 0x0800
	};
	mode mode_enum() { return enum_value<mode>(m_attr0 & 0x0c00); }

	void size(int &width, int &height)
	{
		static const int size_table[4][4][2] =
		{
			{ { 8,   8 }, { 16, 16 }, { 32, 32 }, { 64, 64 } }, // square
			{ { 16,  8 }, { 32,  8 }, { 32, 16 }, { 64, 32 } }, // horizontal rect
			{ { 8,  16 }, { 8,  32 }, { 16, 32 }, { 32, 64 } }, // vertical rect
			{ { 0,   0 }, { 0,   0 }, { 0,   0 }, { 0,   0 } }  // invalid
		};

		int shape = (m_attr0 & 0xc000) >> 14;
		int size  = (m_attr1 & 0xc000) >> 14;

		width  = size_table[shape][size][0];
		height = size_table[shape][size][1];

		if (shape == 4)
			verboselog(m_device, 0, "WARNING: attempted to draw an object of invalid shape\n");
	}

private:
	device_t &m_device;

	uint16_t m_attr0;
	uint16_t m_attr1;
	uint16_t m_attr2;
};

DEFINE_DEVICE_TYPE(GBA_LCD, gba_lcd_device, "gba_lcd", "GBA LCD")

gba_lcd_device::gba_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, GBA_LCD, tag, owner, clock)
	, device_video_interface(mconfig, *this)
	, m_int_hblank_cb(*this)
	, m_int_vblank_cb(*this)
	, m_int_vcount_cb(*this)
	, m_dma_hblank_cb(*this)
	, m_dma_vblank_cb(*this)
{
}

inline uint8_t gba_lcd_device::bg_video_mode()
{
	uint8_t mode = DISPCNT & 0x0007;

	if (mode > 5)
	{
		verboselog(*this, 0, "WARNING: attempted to set invalid BG video mode %d\n", mode);
		return 0;
	}

	return mode;
}

inline bool gba_lcd_device::is_set(dispcnt flag)
{
	return DISPCNT & underlying_value(flag);
}

inline void gba_lcd_device::set(dispstat flag)
{
	DISPSTAT_SET(underlying_value(flag));
}

inline void gba_lcd_device::clear(dispstat flag)
{
	DISPSTAT_RESET(underlying_value(flag));
}

inline bool gba_lcd_device::is_set(dispstat flag)
{
	return DISPSTAT & underlying_value(flag);
}

inline bool gba_lcd_device::is_set(uint16_t bgxcnt, bgcnt flag)
{
	return bgxcnt & underlying_value(flag);
}

inline uint8_t gba_lcd_device::bg_priority(uint16_t bgxcnt)
{
	return bgxcnt & 0x0003;
}

inline uint32_t gba_lcd_device::bg_char_base(uint16_t bgxcnt)
{
	return ((bgxcnt & 0x003c) >> 2) * 0x4000;
}

inline uint32_t gba_lcd_device::bg_screen_base(uint16_t bgxcnt)
{
	return ((bgxcnt & 0x1f00) >> 8) * 0x800;
}

inline void gba_lcd_device::bg_screen_size(uint16_t bgxcnt, bool text, int &width, int &height)
{
	static const int size_table[2][4][2] =
	{
		{ { 256, 256 }, { 512, 256 }, { 256, 512 }, { 512,   512 } }, // text mode
		{ { 128, 128 }, { 256, 256 }, { 512, 512 }, { 1024, 1024 } }  // rotation/scaling (roz) mode
	};

	int mode = text ? 0 : 1;
	int size = (bgxcnt & 0xc000) >> 14;

	width  = size_table[mode][size][0];
	height = size_table[mode][size][1];
}

inline uint16_t gba_lcd_device::mosaic_size(size_type type)
{
	return ((MOSAIC >> (4 * underlying_value(type))) & 0xf) + 1;
}

inline gba_lcd_device::sfx gba_lcd_device::color_sfx()
{
	return enum_value<sfx>(BLDCNT & 0x00c0);
}

inline uint8_t gba_lcd_device::color_sfx_target(target id)
{
	return (BLDCNT >> (8 * underlying_value(id))) & 0x3f;
}

inline void gba_lcd_device::update_mask(uint8_t* mask, int y)
{
	bool inwin0 = false;
	bool inwin1 = false;

	if (is_set(dispcnt::win0_en))
		inwin0 = is_in_window_v(y, 0);

	if (is_set(dispcnt::win1_en))
		inwin1 = is_in_window_v(y, 1);

	for (auto x = 0; x < 240; x++)
	{
		mask[x] = WINOUT & 0x00ff;

		if (m_scanline[5][x] != TRANSPARENT_PIXEL)
			mask[x] = WINOUT >> 8;

		if (inwin1 && is_in_window_h(x, 1))
			mask[x] = WININ >> 8;

		if (inwin0 && is_in_window_h(x, 0))
			mask[x] = WININ & 0x00ff;
	}
}

void gba_lcd_device::draw_scanline(int y)
{
	uint16_t *scanline = &m_bitmap.pix16(y);

	if (is_set(dispcnt::forced_blank))
	{
		// forced blank is white
		for (auto x = 0; x < 240; x++)
			scanline[x] = 0x7fff;

		return;
	}

	uint8_t mode = bg_video_mode();

	uint8_t submode;
	if (is_set(dispcnt::win0_en) || is_set(dispcnt::win1_en) || is_set(dispcnt::obj_win_en))
		submode = 2;
	else if (color_sfx() != sfx::none)
		submode = 1;
	else
		submode = 0;

	int depth = 0;
	if (mode == 3)
		depth = 16;
	else if (mode == 4)
		depth = 8;
	else if (mode == 5)
		depth = 4;

	// make all layers transparent at start
	for (auto l = 0; l < 6; l++)
	{
		for (auto x = 0; x < 240; x++)
		{
			m_scanline[l][x] = TRANSPARENT_PIXEL;
		}
	}

	// draw background
	switch (mode)
	{
	case 0:
		draw_bg_scanline(m_scanline[0], y, dispcnt::bg0_en, BG0CNT, BG0HOFS, BG0VOFS);
		draw_bg_scanline(m_scanline[1], y, dispcnt::bg1_en, BG1CNT, BG1HOFS, BG1VOFS);
		draw_bg_scanline(m_scanline[2], y, dispcnt::bg2_en, BG2CNT, BG2HOFS, BG2VOFS);
		draw_bg_scanline(m_scanline[3], y, dispcnt::bg3_en, BG3CNT, BG3HOFS, BG3VOFS);
		break;
	case 1:
		draw_bg_scanline(m_scanline[0], y, dispcnt::bg0_en, BG0CNT, BG0HOFS, BG0VOFS);
		draw_bg_scanline(m_scanline[1], y, dispcnt::bg1_en, BG1CNT, BG1HOFS, BG1VOFS);
		draw_roz_scanline(m_scanline[2], y, dispcnt::bg2_en, BG2CNT, BG2X, BG2Y, BG2PA, BG2PB, BG2PC, BG2PD, m_bg2x, m_bg2y);
		break;
	case 2:
		draw_roz_scanline(m_scanline[2], y, dispcnt::bg2_en, BG2CNT, BG2X, BG2Y, BG2PA, BG2PB, BG2PC, BG2PD, m_bg2x, m_bg2y);
		draw_roz_scanline(m_scanline[3], y, dispcnt::bg3_en, BG3CNT, BG3X, BG3Y, BG3PA, BG3PB, BG3PC, BG3PD, m_bg3x, m_bg3y);
		break;
	case 3:
	case 4:
	case 5:
		draw_roz_bitmap_scanline(m_scanline[2], y, dispcnt::bg2_en, BG2CNT, BG2X, BG2Y, BG2PA, BG2PB, BG2PC, BG2PD, m_bg2x, m_bg2y, depth);
		break;
	}

	uint8_t mask[240];

	// draw objects
	draw_oam(m_scanline[4], y);

	if (submode == 2)
	{
		draw_oam_window(m_scanline[5], y);
		update_mask(mask, y);
	}
	else
	{
		memset(mask, 0xff, sizeof(mask));
	}

	uint32_t backdrop = ((uint16_t *)m_pram.get())[0] | 0x30000000;

	for (auto x = 0; x < 240; x++)
	{
		uint32_t color = backdrop;
		uint8_t top = 0x20;

		for (auto l = 0; l < 5; l++)
		{
			if ((m_scanline[l][x] >> 24) < (color >> 24) && mask[x] & (0x01 << l))
			{
				color = m_scanline[l][x];
				top = (0x01 << l);
			}
		}

		if (color & 0x00010000)
		{
			if (submode != 0 || top == 0x10)
			{
				uint32_t back = backdrop;
				uint8_t top2 = 0x20;

				for (auto l = 0; l < 4; l++)
				{
					if ((m_scanline[l][x] >> 24) < (back >> 24) && mask[x] & (0x01 << l))
					{
						back = m_scanline[l][x];
						top2 = (0x01 << l);
					}
				}

				if (top2 & color_sfx_target(target::second))
				{
					color = alpha_blend(color, back);
				}
				else if (top & color_sfx_target(target::first))
				{
					switch (color_sfx())
					{
					case sfx::lighten:
						color = increase_brightness(color);
						break;
					case sfx::darken:
						color = decrease_brightness(color);
						break;
					default:
						break;
					}
				}
			}
		}
		else if (submode == 1 || (submode == 2 && mask[x] & 0x20))
		{
			if (top & color_sfx_target(target::first))
			{
				switch (color_sfx())
				{
				case sfx::none:
					break;
				case sfx::alpha:
				{
					uint32_t back = backdrop;
					uint8_t top2 = 0x20;

					for (auto l = 0; l < 5; l++)
					{
						if ((m_scanline[l][x] >> 24) < (back >> 24) && mask[x] & (0x01 << l))
						{
							if (top != (0x01 << l))
							{
								back = m_scanline[l][x];
								top2 = (0x01 << l);
							}
						}
					}

					if (top2 & color_sfx_target(target::second))
						color = alpha_blend(color, back);

					break;
				}
				case sfx::lighten:
					color = increase_brightness(color);
					break;
				case sfx::darken:
					color = decrease_brightness(color);
					break;
				}
			}
		}
		scanline[x] = color & 0x7fff;
	}
}

void gba_lcd_device::draw_roz_bitmap_scanline(uint32_t *scanline, int ypos, dispcnt bg_enable, uint32_t ctrl, int32_t X, int32_t Y, int32_t PA, int32_t PB, int32_t PC, int32_t PD, internal_reg &currentx, internal_reg &currenty, int depth)
{
	if (!is_set(bg_enable))
		return;

	uint8_t *src8 = (uint8_t *)m_vram.get();
	uint16_t *src16 = (uint16_t *)m_vram.get();
	uint16_t *palette = (uint16_t *)m_pram.get();
	int32_t sx = (depth == 4) ? 160 : 240;
	int32_t sy = (depth == 4) ? 128 : 160;
	uint32_t prio = (bg_priority(ctrl) << 25) + 0x1000000;

	if (is_set(dispcnt::alt_frame_sel))
	{
		if (depth == 8)
			src8 += 0xa000;

		if (depth == 4)
			src16 += 0xa000 / 2;
	}

	// sign extend roz parameters
	if (X & 0x08000000) X |= 0xf0000000;
	if (Y & 0x08000000) Y |= 0xf0000000;
	if (PA & 0x8000) PA |= 0xffff0000;
	if (PB & 0x8000) PB |= 0xffff0000;
	if (PC & 0x8000) PC |= 0xffff0000;
	if (PD & 0x8000) PD |= 0xffff0000;

	if (currentx.update)
	{
		currentx.status = X;
		currentx.update = false;
	}
	else
	{
		currentx.status += PB;
	}

	if (currenty.update)
	{
		currenty.status = Y;
		currenty.update = false;
	}
	else
	{
		currenty.status += PD;
	}

	int32_t cx = currentx.status;
	int32_t cy = currenty.status;

	if (is_set(ctrl, bgcnt::mosaic_en))
	{
		uint16_t mosaic_line = mosaic_size(size_type::bg_v);
		int32_t tempy = (ypos / mosaic_line) * mosaic_line;
		cx = X + tempy * PB;
		cy = Y + tempy * PD;
	}

	int32_t pixx = cx >> 8;
	int32_t pixy = cy >> 8;

	for (auto x = 0; x < 240; x++)
	{
		if (pixx >= 0 && pixy >= 0 && pixx < sx && pixy < sy)
		{
			if (depth == 8)
			{
				uint8_t color = src8[pixy * sx + pixx];
				if (color)
					scanline[x] = palette[color] | prio;
			}
			else
			{
				scanline[x] = src16[pixy * sx + pixx] | prio;
			}
		}

		cx += PA;
		cy += PC;

		pixx = cx >> 8;
		pixy = cy >> 8;
	}

	if (is_set(ctrl, bgcnt::mosaic_en))
	{
		uint16_t mosaicx = mosaic_size(size_type::bg_h);
		if (mosaicx > 1)
		{
			int32_t m = 1;
			for (auto x = 0; x < 239; x++)
			{
				scanline[x + 1] = scanline[x];
				m++;
				if (m == mosaicx)
				{
					m = 1;
					x++;
				}
			}
		}
	}
}

void gba_lcd_device::draw_roz_scanline(uint32_t *scanline, int ypos, dispcnt bg_enable, uint32_t ctrl, int32_t X, int32_t Y, int32_t PA, int32_t PB, int32_t PC, int32_t PD, internal_reg &currentx, internal_reg &currenty)
{
	if (!is_set(bg_enable))
		return;

	uint8_t *mgba_vram = (uint8_t *)m_vram.get();
	uint16_t *pgba_pram = (uint16_t *)m_pram.get();
	uint32_t priority = (bg_priority(ctrl) << 25) + 0x1000000;
	uint32_t base = bg_char_base(ctrl);
	uint32_t mapbase = bg_screen_base(ctrl);

	// size of map in submaps
	int width, height;
	bg_screen_size(ctrl, false, width, height);

	// sign extend roz parameters
	if (X & 0x08000000) X |= 0xf0000000;
	if (Y & 0x08000000) Y |= 0xf0000000;
	if (PA & 0x8000) PA |= 0xffff0000;
	if (PB & 0x8000) PB |= 0xffff0000;
	if (PC & 0x8000) PC |= 0xffff0000;
	if (PD & 0x8000) PD |= 0xffff0000;

	if (currentx.update)
	{
		currentx.status = X;
		currentx.update = false;
	}
	else
	{
		currentx.status += PB;
	}

	if (currenty.update)
	{
		currenty.status = Y;
		currenty.update = false;
	}
	else
	{
		currenty.status += PD;
	}

	int32_t cx = currentx.status;
	int32_t cy = currenty.status;

	if (is_set(ctrl, bgcnt::mosaic_en))
	{
		uint16_t mosaic_line = mosaic_size(size_type::bg_v);
		int y = ypos % mosaic_line;
		cx -= y * PB;
		cy -= y * PD;
	}

	int32_t pixx = cx >> 8;
	int32_t pixy = cy >> 8;

	if (is_set(ctrl, bgcnt::wraparound_en))
	{
		pixx %= width;
		pixy %= height;

		if (pixx < 0)
			pixx += width;

		if (pixy < 0)
			pixy += height;
	}

	for (auto x = 0; x < 240; x++)
	{
		if (pixx >= 0 && pixy >= 0 && pixx < width && pixy < height)
		{
			int tilex = pixx & 7;
			int tiley = pixy & 7;

			// shall we shift for is_set(ctrl, bgcnt::palette_256)? or is not effective for ROZ?
			uint32_t tile = mgba_vram[mapbase + (pixx >> 3) + (pixy >> 3) * (width >> 3)];
			uint16_t pixel = mgba_vram[base + (tile << 6) + (tiley << 3) + tilex];

			// plot it
			if (pixel)
				scanline[x] = pgba_pram[pixel] | priority;
		}

		cx += PA;
		cy += PC;

		pixx = cx >> 8;
		pixy = cy >> 8;

		if (is_set(ctrl, bgcnt::wraparound_en))
		{
			pixx %= width;
			pixy %= height;

			if (pixx < 0)
				pixx += width;

			if (pixy < 0)
				pixy += height;
		}
	}

	if (is_set(ctrl, bgcnt::mosaic_en))
	{
		uint16_t mosaicx = mosaic_size(size_type::bg_h);
		if (mosaicx > 1)
		{
			int m = 1;
			for (auto x = 0; x < 239; x++)
			{
				scanline[x + 1] = scanline[x];
				m++;
				if (m == mosaicx)
				{
					m = 1;
					x++;
				}
			}
		}
	}
}

void gba_lcd_device::draw_bg_scanline(uint32_t *scanline, int ypos, dispcnt bg_enable, uint32_t ctrl, uint32_t hofs, uint32_t vofs)
{
	if (!is_set(bg_enable))
		return;

	uint8_t *vram = (uint8_t*)m_vram.get();
	uint16_t *palette = (uint16_t *)m_pram.get();
	uint8_t *chardata = &vram[bg_char_base(ctrl)];
	uint16_t *screendata = (uint16_t *)&vram[bg_screen_base(ctrl)];
	uint32_t priority = (bg_priority(ctrl) << 25) + 0x1000000;
	uint16_t mosaicx = mosaic_size(size_type::bg_h);
	uint16_t mosaicy = mosaic_size(size_type::bg_v);

	int width, height;
	bg_screen_size(ctrl, true, width, height);

	int32_t pixx = hofs % width;
	int32_t pixy = (vofs + ypos) % height;

	if (is_set(ctrl, bgcnt::mosaic_en) && ypos % mosaicy)
	{
		mosaicy = (ypos / mosaicy) * mosaicy;
		pixy = (vofs + mosaicy) % height;
	}

	if (pixy > 255 && height > 256)
	{
		pixy &= 0x000000ff;
		screendata += 0x400;
		if (width > 256)
		{
			screendata += 0x400;
		}
	}

	int32_t stride = (pixy >> 3) << 5;

	uint16_t *src = screendata + 0x400 * (pixx >> 8) + ((pixx & 255) >> 3) + stride;

	for (auto x = 0; x < 240; x++)
	{
		uint16_t data = *src;
		int32_t tile = tile_number(data);
		int32_t tilex = pixx & 7;
		int32_t tiley = pixy & 7;
		uint8_t color;
		uint8_t palindex;

		if (tile_hflip(data))
			tilex = 7 - tilex;

		if (tile_vflip(data))
			tiley = 7 - tiley;

		if (is_set(ctrl, bgcnt::palette_256))
		{
			color = chardata[(tile << 6) + (tiley << 3) + tilex];
			palindex = 0;
		}
		else
		{
			color = chardata[(tile << 5) + (tiley << 2) + (tilex >> 1)];

			if (tilex & 1)
				color >>= 4;
			else
				color &= 0x0f;
			palindex = (data >> 8) & 0x00f0;
		}

		if (color)
			scanline[x] = palette[palindex + color] | priority;

		if (tile_hflip(data))
		{
			if (tilex == 0)
				src++;
		}
		else if (tilex == 7)
		{
			src++;
		}

		pixx++;
		if (pixx == 256)
		{
			if (width > 256)
			{
				src = screendata + 0x400 + stride;
			}
			else
			{
				src = screendata + stride;
				pixx = 0;
			}
		}
		else if (pixx >= width)
		{
			pixx = 0;
			src = screendata + stride;
		}
	}

	if (is_set(ctrl, bgcnt::mosaic_en) && mosaicx > 1)
	{
		int32_t m = 1;
		for (auto x = 0; x < 239; x++)
		{
			scanline[x+1] = scanline[x];
			m++;
			if (m == mosaicx)
			{
				m = 1;
				x++;
			}
		}
	}
}

void gba_lcd_device::draw_oam_window(uint32_t *scanline, int y)
{
	if (!is_set(dispcnt::obj_win_en))
		return;

	uint16_t *oam = (uint16_t *)m_oam.get();
	uint8_t *src = (uint8_t *)m_vram.get();

	for (auto obj_index = 127; obj_index >= 0; obj_index--)
	{
		object obj(*this, oam, obj_index);

		if (obj.mode_enum() != object::mode::window)
			continue;

		uint32_t tile_number = obj.tile_number();

		if (bg_video_mode() > 2 && tile_number < 0x200)
			continue;

		int32_t sx = obj.pos_x();
		int32_t sy = obj.pos_y();

		if (sy > 160)
			sy -= 256;

		int width, height;
		obj.size(width, height);

		if (obj.roz())
		{
			int32_t fx = width;
			int32_t fy = height;

			if (obj.roz_double())
			{
				fx *= 2;
				fy *= 2;
			}

			int32_t cury = y - sy;

			if (cury < 0 || cury >= fy)
				continue;

			if (sx >= 240 && ((sx + fx) % 512) >= 240)
				continue;

			int rot = obj.roz_param();
			int16_t dx  = (int16_t)oam[(rot << 4) + 3];
			int16_t dmx = (int16_t)oam[(rot << 4) + 7];
			int16_t dy  = (int16_t)oam[(rot << 4) + 11];
			int16_t dmy = (int16_t)oam[(rot << 4) + 15];

			int32_t rx = (width << 7) - (fx >> 1) * dx - (fy >> 1) * dmx + cury * dmx;
			int32_t ry = (height << 7) - (fx >> 1) * dy - (fy >> 1) * dmy + cury * dmy;

			int inc = 32;

			if (obj.palette_256())
			{
				if (is_set(dispcnt::vram_map_1d))
					inc = sx >> 2;
				else
					tile_number &= 0x3fe;

				for (auto x = 0; x < fx; x++)
				{
					int32_t ax = rx >> 8;
					int32_t ay = ry >> 8;

					if (ax >= 0 && ax < sx && ay >= 0 && ay < sy)
					{
						uint8_t color = src[0x10000 + ((((tile_number + (ay >> 3) * inc) << 5) + ((ay & 0x07) << 3) + ((ax >> 3) << 6) + (ax & 0x07)) & 0x7fff)];

						if (color)
							scanline[sx] = 1;
					}

					sx = (sx + 1) % 512;
					rx += dx;
					ry += dy;
				}
			}
			else
			{
				if (is_set(dispcnt::vram_map_1d))
					inc = sx >> 3;

				for (auto x = 0; x < fx; x++)
				{
					int32_t ax = rx >> 8;
					int32_t ay = ry >> 8;

					if (ax >= 0 && ax < sx && ay >= 0 && ay < sy)
					{
						uint8_t color = src[0x10000 + ((((tile_number + (ay >> 3) * inc) << 5) + ((ay & 0x07) << 2) + ((ax >> 3) << 5) + ((ax & 0x07) >> 1)) & 0x7fff)];

						if (ax & 1)
							color >>= 4;
						else
							color &= 0x0f;

						if (color)
							scanline[sx] = 1;
					}

					sx = (sx + 1) % 512;
					rx += dx;
					ry += dy;
				}
			}
		}
		else
		{
			// when roz bit is not set double roz bit means 'disable object'
			if (obj.roz_double())
				continue;

			int32_t cury = y - sy;

			if (cury < 0 || cury >= height)
				continue;

			if ((sx >= 240) && (((sx + width) % 512) >= 240))
				continue;

			int inc = 32;

			if (obj.vflip())
				cury = height - cury - 1;

			int32_t ax = obj.hflip() ? (width - 1) : 0;

			if (obj.palette_256())
			{
				if (is_set(dispcnt::vram_map_1d))
					inc = width >> 2;
				else
					tile_number &= 0x3fe;

				uint32_t address = 0x10000 + ((((tile_number + (cury >> 3) * inc) << 5) + ((cury & 7) << 3) + ((ax >> 3) << 6) + (ax & 7)) & 0x7fff);

				if (obj.hflip())
					ax = 7;

				for (auto x = 0; x < width; x++)
				{
					if (sx < 240)
					{
						uint8_t color = src[address];

						if (color)
							scanline[sx] = 1;
					}

					sx = (sx + 1) % 512;

					if (obj.hflip())
					{
						ax--;
						address--;

						if (ax == -1)
						{
							address -= 56;
							ax = 7;
						}

						if (address < 0x10000)
							address += 0x8000;
					}
					else
					{
						ax++;
						address++;

						if (ax == 8)
						{
							address += 56;
							ax = 0;
						}

						if (address > 0x17fff)
							address -= 0x8000;
					}
				}
			}
			else
			{
				if (is_set(dispcnt::vram_map_1d))
					inc = width >> 3;

				uint32_t address = 0x10000 + ((((tile_number + (cury >> 3) * inc) << 5) + ((cury & 0x07) << 2) + ((ax >> 3) << 5) + ((ax & 0x07) >> 1)) & 0x7fff);

				if (obj.hflip())
				{
					ax = 7;

					for (auto x = width - 1; x >= 0; x--)
					{
						if (sx < 240)
						{
							uint8_t color = src[address];
							if (x & 1)
								color >>= 4;
							else
								color &= 0x0f;

							if (color)
								scanline[sx] = 1;
						}

						sx = (sx + 1) % 512;
						ax--;

						if ((x & 1) == 0)
							address--;

						if (ax == -1)
						{
							ax = 7;
							address -= 28;
						}

						if (address < 0x10000)
							address += 0x8000;
					}
				}
				else
				{
					for (auto x = 0; x < width; x++)
					{
						if (sx < 240)
						{
							uint8_t color = src[address];

							if (x & 1)
								color >>= 4;
							else
								color &= 0x0f;

							if (color)
								scanline[sx] = 1;
						}

						sx = (sx + 1) % 512;
						ax++;

						if (x & 1)
							address++;

						if (ax == 8)
						{
							address += 28;
							ax = 0;
						}

						if (address > 0x17fff)
							address -= 0x8000;
					}
				}
			}
		}
	}
}

void gba_lcd_device::draw_oam(uint32_t *scanline, int y)
{
	if (!is_set(dispcnt::obj_en))
		return;

	int32_t mosaiccnt = 0;
	uint16_t mosaicx = mosaic_size(size_type::obj_h);
	uint16_t mosaicy = mosaic_size(size_type::obj_v);
	uint16_t *oam = (uint16_t *)m_oam.get();
	uint8_t *src = (uint8_t *)m_vram.get();
	uint16_t *palette = (uint16_t *)m_pram.get();

	for (auto obj_index = 0; obj_index < 128; obj_index++)
	{
		object obj(*this, oam, obj_index);

		if (obj.mode_enum() == object::mode::window)
			continue;

		uint32_t priority = obj.priority();
		uint32_t prio = (priority << 25) | (obj.mode_mask() << 6);

		int width, height;
		obj.size(width, height);

		uint32_t tile_number = obj.tile_number();

		if (bg_video_mode() > 2 && tile_number < 0x200)
			continue;

		if (obj.roz())
		{
			int32_t sx = obj.pos_x();
			int32_t sy = obj.pos_y();

			if (sy > 160)
				sy -= 256;

			int32_t fx = width;
			int32_t fy = height;

			if (obj.roz_double())
			{
				fx *= 2;
				fy *= 2;
			}

			int32_t cury = y - sy;

			if (cury < 0 || cury >= fy)
				continue;

			if (sx >= 240 && ((sx + fx) % 512) >= 240)
				continue;

			int32_t oamparam = obj.roz_param();

			int16_t dx  = (int16_t)oam[(oamparam << 4) + 3];
			int16_t dmx = (int16_t)oam[(oamparam << 4) + 7];
			int16_t dy  = (int16_t)oam[(oamparam << 4) + 11];
			int16_t dmy = (int16_t)oam[(oamparam << 4) + 15];

			if (obj.mosaic())
				cury -= (cury % mosaicy);

			int32_t rx = (width << 7) - (fx >> 1) * dx - (fy >> 1) * dmx + cury * dmx;
			int32_t ry = (height << 7) - (fx >> 1) * dy - (fy >> 1) * dmy + cury * dmy;

			int32_t inc = 32;

			if (obj.palette_256())
			{
				if (is_set(dispcnt::vram_map_1d))
					inc = width >> 2;
				else
					tile_number &= 0x3fe;

				for (auto x = 0; x < fx; x++)
				{
					int32_t pixx = rx >> 8;
					int32_t pixy = ry >> 8;

					if (!(pixx < 0 || pixx >= width || pixy < 0 || pixy >= height || sx >= 240))
					{
						uint8_t color = src[0x10000 + ((((tile_number + (pixy >> 3) * inc) << 5) + ((pixy & 7) << 3) + ((pixx >> 3) << 6) + (pixx & 7)) & 0x7fff)];

						if (color == 0 && priority < ((scanline[sx] >> 25) & 3))
						{
							scanline[sx] = (scanline[sx] & 0xf9ffffff) | prio;
							if (obj.mosaic() && mosaiccnt != 0)
							{
								scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
							}
						}
						else if (color != 0 && prio < (scanline[sx] & 0xff000000))
						{
							scanline[sx] = palette[256 + color] | prio;
							if (obj.mosaic() && mosaiccnt != 0)
							{
								scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
							}
						}

						if (obj.mosaic())
							mosaiccnt = (mosaiccnt + 1) % mosaicx;
					}

					sx = (sx + 1) % 512;
					rx += dx;
					ry += dy;
				}
			}
			else
			{
				if (is_set(dispcnt::vram_map_1d))
					inc = width >> 3;

				for (auto x = 0; x < fx; x++)
				{
					int32_t pixx = rx >> 8;
					int32_t pixy = ry >> 8;

					if (!(pixx < 0 || pixx >= width || pixy < 0 || pixy >= height || sx >= 240))
					{
						uint8_t color = src[0x10000 + ((((tile_number + (pixy >> 3) * inc) << 5) + ((pixy & 7) << 2) + ((pixx >> 3) << 5) + ((pixx & 7) >> 1)) & 0x7fff)];

						if (pixx & 1)
							color >>= 4;
						else
							color &= 0x0f;

						if (color == 0 && priority < ((scanline[sx] >> 25) & 3))
						{
							scanline[sx] = (scanline[sx] & 0xf9ffffff) | prio;
							if (obj.mosaic() && mosaiccnt != 0)
							{
								scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
							}
						}
						else if (color != 0 && prio < (scanline[sx] & 0xff000000))
						{
							scanline[sx] = palette[256 + obj.palette() + color] | prio;
							if (obj.mosaic() && mosaiccnt != 0)
							{
								scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
							}
						}
					}

					if (obj.mosaic())
						mosaiccnt = (mosaiccnt + 1) % mosaicx;

					sx = (sx + 1) % 512;
					rx += dx;
					ry += dy;
				}
			}
		}
		else
		{
			// when roz bit is not set double roz bit means 'disable object'
			if (obj.roz_double())
				continue;

			int32_t sx = obj.pos_x();
			int32_t sy = obj.pos_y();

			if (sy > 160)
				sy -= 256;

			int32_t cury = y - sy;

			if (cury < 0 || cury >= height)
				continue;

			if (sx >= 240 && ((sx + width) % 512) >= 240)
				continue;

			int32_t inc = 32;

			if (obj.vflip())
				cury = height - cury - 1;

			int32_t pixx = obj.hflip() ? (width - 1) : 0;

			if (obj.mosaic())
				cury -= (cury % mosaicy);

			if (obj.palette_256())
			{
				if (is_set(dispcnt::vram_map_1d))
					inc = width >> 2;
				else
					tile_number &= 0x3fe;

				uint32_t address = 0x10000 + ((((tile_number + (cury >> 3) * inc) << 5) + ((cury & 7) << 3) + ((pixx >> 3) << 6) + (pixx & 7)) & 0x7fff);

				if (obj.hflip())
					pixx = 7;

				for (auto x = 0; x < width; x++)
				{
					if (sx < 240)
					{
						uint8_t color = src[address];

						if (color == 0 && priority < ((scanline[sx] >> 25) & 3))
						{
							scanline[sx] = (scanline[sx] & 0xf9ffffff) | prio;
							if (obj.mosaic() && mosaiccnt != 0)
							{
								scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
							}
						}
						else if (color != 0 && prio < (scanline[sx] & 0xff000000))
						{
							scanline[sx] = palette[256 + color] | prio;
							if (obj.mosaic() && mosaiccnt != 0)
							{
								scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
							}
						}
					}

					if (obj.mosaic())
						mosaiccnt = (mosaiccnt + 1) % mosaicx;

					sx = (sx + 1) % 512;

					if (obj.hflip())
					{
						pixx--;
						address--;

						if (pixx == -1)
						{
							address -= 56;
							pixx = 7;
						}

						if (address < 0x10000)
							address += 0x8000;
					}
					else
					{
						pixx++;
						address++;

						if (pixx == 8)
						{
							address += 56;
							pixx = 0;
						}

						if (address > 0x17fff)
							address -= 0x8000;
					}
				}
			}
			else
			{
				if (is_set(dispcnt::vram_map_1d))
					inc = width >> 3;

				uint32_t address = 0x10000 + ((((tile_number + (cury >> 3) * inc) << 5) + ((cury & 7) << 2) + ((pixx >> 3) << 5) + ((pixx & 7) >> 1)) & 0x7fff);

				if (obj.hflip())
				{
					pixx = 7;
					for (auto x = width - 1; x >= 0; x--)
					{
						if (sx < 240)
						{
							uint8_t color = src[address];

							if (x & 1)
								color >>= 4;
							else
								color &= 0x0f;

							if (color == 0 && priority < ((scanline[sx] >> 25) & 3))
							{
								scanline[sx] = (scanline[sx] & 0xf9ffffff) | prio;
								if (obj.mosaic() && mosaiccnt != 0)
								{
									scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
								}
							}
							else if (color != 0 && prio < (scanline[sx] & 0xff000000))
							{
								scanline[sx] = palette[256 + obj.palette() + color] | prio;
								if (obj.mosaic() && mosaiccnt != 0)
								{
									scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
								}
							}
						}

						sx = (sx + 1) % 512;
						pixx--;

						if (!(x & 1))
							address--;

						if (pixx == -1)
						{
							address -= 28;
							pixx = 7;
						}

						if (address < 0x10000)
							address += 0x8000;
					}
				}
				else
				{
					for (auto x = 0; x < width; x++)
					{
						if (sx < 240)
						{
							uint8_t color = src[address];

							if (x & 1)
								color >>= 4;
							else
								color &= 0x0f;

							if (color == 0 && priority < ((scanline[sx] >> 25) & 3))
							{
								scanline[sx] = (scanline[sx] & 0xf9ffffff) | prio;
								if (obj.mosaic() && mosaiccnt != 0)
								{
									scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
								}
							}
							else if (color != 0 && prio < (scanline[sx] & 0xff000000))
							{
								scanline[sx] = palette[256 + obj.palette() + color] | prio;
								if (obj.mosaic() && mosaiccnt != 0)
								{
									scanline[sx] = (scanline[sx - 1] & 0xf9ffffff) | prio;
								}
							}
						}

						if (obj.mosaic())
							mosaiccnt = (mosaiccnt + 1) % mosaicx;

						sx = (sx + 1) % 512;
						pixx++;

						if (x & 1)
							address++;

						if (pixx == 8)
						{
							address += 28;
							pixx = 0;
						}

						if (address > 0x17fff)
							address -= 0x8000;
					}
				}
			}
		}
	}
}

inline bool gba_lcd_device::is_in_window_h(int x, int window)
{
	uint16_t reg = (window == 0) ? WIN0H : WIN1H;

	uint8_t x0 = reg >> 8;
	uint8_t x1 = reg & 0x00ff;

	if (x0 <= x1)
	{
		if (x >= x0 && x < x1)
			return true;
	}
	else
	{
		if (x >= x0 || x < x1)
			return true;
	}

	return false;
}

inline bool gba_lcd_device::is_in_window_v(int y, int window)
{
	uint16_t reg = (window == 0) ? WIN0V : WIN1V;

	uint8_t v0 = reg >> 8;
	uint8_t v1 = reg & 0x00ff;

	if ((v0 == v1) && (v0 >= 0xe8))
		return true;

	if (v1 >= v0)
	{
		if (y >= v0 && y < v1)
			return true;
	}
	else
	{
		if (y >= v0 || y < v1)
			return true;
	}

	return false;
}

static const int coeff[32] = {
	0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
	16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
};

inline uint32_t gba_lcd_device::alpha_blend(uint32_t color0, uint32_t color1)
{
	int ca = coeff[BLDALPHA & 0x1f];
	int cb = coeff[(BLDALPHA >> 8) & 0x1f];

	if (color0 != TRANSPARENT_PIXEL)
	{
		int r0 = (color0 >>  0) & 0x1f;
		int g0 = (color0 >>  5) & 0x1f;
		int b0 = (color0 >> 10) & 0x1f;
		int r1 = (color1 >>  0) & 0x1f;
		int g1 = (color1 >>  5) & 0x1f;
		int b1 = (color1 >> 10) & 0x1f;
		int r = ((r0 * ca) >> 4) + ((r1 * cb) >> 4);
		int g = ((g0 * ca) >> 4) + ((g1 * cb) >> 4);
		int b = ((b0 * ca) >> 4) + ((b1 * cb) >> 4);

		if (r > 0x1f) r = 0x1f;
		if (g > 0x1f) g = 0x1f;
		if (b > 0x1f) b = 0x1f;

		return (color0 & 0xffff0000) | (b << 10) | (g << 5) | r;
	}

	return color0;
}

inline uint32_t gba_lcd_device::increase_brightness(uint32_t color)
{
	int cc = coeff[BLDY & 0x1f];

	int r = (color >>  0) & 0x1f;
	int g = (color >>  5) & 0x1f;
	int b = (color >> 10) & 0x1f;

	r += ((0x1f - r) * cc) >> 4;
	g += ((0x1f - g) * cc) >> 4;
	b += ((0x1f - b) * cc) >> 4;

	if (r > 0x1f) r = 0x1f;
	if (g > 0x1f) g = 0x1f;
	if (b > 0x1f) b = 0x1f;

	return (color & 0xffff0000) | (b << 10) | (g << 5) | r;
}

inline uint32_t gba_lcd_device::decrease_brightness(uint32_t color)
{
	int cc = coeff[BLDY & 0x1f];

	int r = (color >>  0) & 0x1f;
	int g = (color >>  5) & 0x1f;
	int b = (color >> 10) & 0x1f;

	r -= (r * cc) >> 4;
	g -= (g * cc) >> 4;
	b -= (b * cc) >> 4;

	if (r < 0) r = 0;
	if (g < 0) g = 0;
	if (b < 0) b = 0;

	return (color & 0xffff0000) | (b << 10) | (g << 5) | r;
}

static const char *reg_names[] = {
	/* LCD I/O Registers */
	"DISPCNT", "GRNSWAP",  "DISPSTAT", "VCOUNT",
	"BG0CNT",  "BG1CNT",   "BG2CNT",   "BG3CNT",
	"BG0HOFS", "BG0VOFS",  "BG1HOFS",  "BG1VOFS",
	"BG2HOFS", "BG2VOFS",  "BG3HOFS",  "BG3VOFS",
	"BG2PA",   "BG2PB",    "BG2PC",    "BG2PD",
	"BG2X_L",  "BG2X_H",   "BG2Y_L",   "BG2Y_H",
	"BG3PA",   "BG3PB",    "BG3PC",    "BG3PD",
	"BG3X_L",  "BG3X_H",   "BG3Y_L",   "BG3Y_H",
	"WIN0H",   "WIN1H",    "WIN0V",    "WIN1V",
	"WININ",   "WINOUT",   "MOSAIC",   "Unused",
	"BLDCNT",  "BLDALPHA", "BLDY",     "Unused",
	"Unused",  "Unused",   "Unused",   "Unused",
};

READ32_MEMBER(gba_lcd_device::video_r)
{
	uint32_t retval = 0;

	switch (offset)
	{
	case 0x0004/4:
		retval = DISPSTAT | (screen().vpos() << 16);
		break;
	default:
		if (ACCESSING_BITS_0_15)
		{
			retval |= m_regs[offset] & 0x0000ffff;
		}
		if (ACCESSING_BITS_16_31)
		{
			retval |= m_regs[offset] & 0xffff0000;
		}
		break;
	}

	assert_always(offset < ARRAY_LENGTH(reg_names) / 2, "Not enough register names in gba_lcd_device");

	if (ACCESSING_BITS_0_15)
	{
		verboselog(*this, 2, "GBA I/O Read: %s = %04x\n", reg_names[offset * 2], retval & 0x0000ffff);
	}
	if (ACCESSING_BITS_16_31)
	{
		verboselog(*this, 2, "GBA I/O Read: %s = %04x\n", reg_names[offset * 2 + 1], (retval & 0xffff0000) >> 16);
	}

	return retval;
}

WRITE32_MEMBER(gba_lcd_device::video_w)
{
	COMBINE_DATA(&m_regs[offset]);

	assert_always(offset < ARRAY_LENGTH(reg_names) / 2, "Not enough register names in gba_lcd_device");

	if (ACCESSING_BITS_0_15)
	{
		verboselog(*this, 2, "GBA I/O Write: %s = %04x\n", reg_names[offset * 2], data & 0x0000ffff);
	}
	if (ACCESSING_BITS_16_31)
	{
		verboselog(*this, 2, "GBA I/O Write: %s = %04x\n", reg_names[offset * 2 + 1], (data & 0xffff0000) >> 16);
	}

	switch (offset)
	{
	case 0x0028/4:
		m_bg2x.update = true;
		break;
	case 0x002c/4:
		m_bg2y.update = true;
		break;
	case 0x0038/4:
		m_bg3x.update = true;
		break;
	case 0x003c/4:
		m_bg3y.update = true;
		break;
	}
}

READ32_MEMBER(gba_lcd_device::gba_pram_r)
{
	return m_pram[offset];
}

WRITE32_MEMBER(gba_lcd_device::gba_pram_w)
{
	COMBINE_DATA(&m_pram[offset]);
}

READ32_MEMBER(gba_lcd_device::gba_vram_r)
{
	return m_vram[offset];
}

WRITE32_MEMBER(gba_lcd_device::gba_vram_w)
{
	COMBINE_DATA(&m_vram[offset]);
}

READ32_MEMBER(gba_lcd_device::gba_oam_r)
{
	return m_oam[offset];
}

WRITE32_MEMBER(gba_lcd_device::gba_oam_w)
{
	COMBINE_DATA(&m_oam[offset]);
}

TIMER_CALLBACK_MEMBER(gba_lcd_device::perform_hbl)
{
	int scanline = screen().vpos();

	// reload LCD controller internal registers from I/O ones at vblank
	if (scanline == 0)
	{
		m_bg2x.update = true;
		m_bg2y.update = true;
		m_bg3x.update = true;
		m_bg3y.update = true;
	}

	// draw only visible scanlines
	if (scanline < 160)
	{
		draw_scanline(scanline);

		if (!m_dma_hblank_cb.isnull())
			m_dma_hblank_cb(ASSERT_LINE);
	}

	if (is_set(dispstat::hblank_irq_en))
	{
		if (!m_int_hblank_cb.isnull())
			m_int_hblank_cb(ASSERT_LINE);
	}

	set(dispstat::hblank);

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

TIMER_CALLBACK_MEMBER(gba_lcd_device::perform_scan)
{
	clear(dispstat::hblank);
	clear(dispstat::vcount);

	int scanline = screen().vpos();

	// VBLANK is set for scanlines 160 through 226 (but not 227, which is the last line)
	if (scanline >= 160 && scanline < 227)
	{
		set(dispstat::vblank);

		// VBL IRQ and DMA on line 160
		if (scanline == 160)
		{
			if (is_set(dispstat::vblank_irq_en))
			{
				if (!m_int_vblank_cb.isnull())
					m_int_vblank_cb(ASSERT_LINE);
			}

			if (!m_dma_vblank_cb.isnull())
				m_dma_vblank_cb(ASSERT_LINE);
		}
	}
	else
	{
		clear(dispstat::vblank);
	}

	// handle VCOUNT match interrupt flag
	if (scanline == ((DISPSTAT >> 8) & 0xff))
	{
		set(dispstat::vcount);

		if (is_set(dispstat::vcount_irq_en))
		{
			if (!m_int_vcount_cb.isnull())
				m_int_vcount_cb(ASSERT_LINE);
		}
	}

	m_hbl_timer->adjust(screen().time_until_pos(scanline, 240));
	m_scan_timer->adjust(screen().time_until_pos((scanline + 1) % 228, 0));
}

PALETTE_INIT_MEMBER(gba_lcd_device, gba)
{
	for (uint8_t b = 0; b < 32; b++)
	{
		for (uint8_t g = 0; g < 32; g++)
		{
			for (uint8_t r = 0; r < 32; r++)
			{
				palette.set_pen_color((b << 10) | (g << 5) | r, pal5bit(r), pal5bit(g), pal5bit(b));
			}
		}
	}
}

uint32_t gba_lcd_device::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);

	return 0;
}

void gba_lcd_device::device_start()
{
	/* resolve callbacks */
	m_int_hblank_cb.resolve();
	m_int_vblank_cb.resolve();
	m_int_vcount_cb.resolve();
	m_dma_hblank_cb.resolve();
	m_dma_vblank_cb.resolve();

	m_pram = make_unique_clear<uint32_t[]>(0x400 / 4);
	m_vram = make_unique_clear<uint32_t[]>(0x18000 / 4);
	m_oam = make_unique_clear<uint32_t[]>(0x400 / 4);

	screen().register_screen_bitmap(m_bitmap);

	/* create a timer to fire scanline functions */
	m_scan_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_lcd_device::perform_scan),this));
	m_hbl_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_lcd_device::perform_hbl),this));
	m_scan_timer->adjust(screen().time_until_pos(0, 0));

	save_item(NAME(m_regs));

	save_pointer(NAME(m_pram), 0x400 / 4);
	save_pointer(NAME(m_vram), 0x18000 / 4);
	save_pointer(NAME(m_oam), 0x400 / 4);

	save_item(NAME(m_bg2x.status));
	save_item(NAME(m_bg2x.update));

	save_item(NAME(m_bg2y.status));
	save_item(NAME(m_bg2y.update));

	save_item(NAME(m_bg3x.status));
	save_item(NAME(m_bg3x.update));

	save_item(NAME(m_bg3y.status));
	save_item(NAME(m_bg3y.update));

	save_item(NAME(m_scanline));
}

void gba_lcd_device::device_reset()
{
	memset(m_regs, 0, sizeof(m_regs));

	m_bg2x = { 0, false };
	m_bg2y = { 0, false };
	m_bg3x = { 0, false };
	m_bg3y = { 0, false };

	m_scan_timer->adjust(screen().time_until_pos(0, 0));
	m_hbl_timer->adjust(attotime::never);
}

MACHINE_CONFIG_START(gba_lcd_device::device_add_mconfig)
	MCFG_SCREEN_ADD("screen", LCD)
	MCFG_SCREEN_RAW_PARAMS(XTAL(16'777'216) / 4, 308, 0, 240, 228, 0, 160)
	MCFG_SCREEN_UPDATE_DEVICE(DEVICE_SELF, gba_lcd_device, screen_update)
	MCFG_SCREEN_PALETTE("palette")

	MCFG_DEFAULT_LAYOUT(layout_lcd)
	MCFG_PALETTE_ADD("palette", 32768)
	MCFG_PALETTE_INIT_OWNER(gba_lcd_device, gba)
MACHINE_CONFIG_END