summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/video/v9938.cpp
blob: 4549dae9e6e7817711c80582c15a3f614f278c86 (plain) (tree)
1
2
3
4
5
6
7

                                              


                                                                            
                       
 


















                                                                               


                                                                            
     
 



                                          
                        

  
                

                  











                                         
                     
 













                             
 


                       
                             
 
                                   
 
                                          

                                                                      

                 
 



                                                                            



                                                                         




                                           

 
          

                                                                    
 
 

                                                                                                                                        
                                                
                                                 
                                               
                                                      
                       
                            








                             
                           


                           
                              


                         


                          
                           
                              
                     
 
                                                                                        

 
                                                                                                           
                                                              
 

 
                                                                                                           
                                                              
 

 
                                                                                      
 
                                    



                                                        
 














                                                                                                     
                                                                                     


 
                                                                                           
 
                                                                      


                          
                    
                                                          


                                       
                                                                             




                                      

                                                                         

                                   
                                                                          
         



                                         
 
                    

                                    



                                         

                                  
                                        
         
                                       

         

                                     
                               






                                             
                                          





















                                                                    
 








                                                                                                              
                                                                                                  






                                                                                                               
                                                                                                   

                                                                                     

 
 
  


                                                             
  
                                                 
 
                                           
         
                                       








                                                         
                                       

                                                         


         




                                                                                         
 


                                                                            
                 





                                                                            
                                                                                  












                                                                           
                                       
 
                      







                                                              
                                 
 







                                                                                   
                                                                            



                                                                
                                             
 
                                 
 
                              
 
                           
                                                                                               
 

                                                                
                                      

                                                        






                                                          
                                                                                                                            
         

 
                                                                                                            




                                                           
                                         

                           
         

                                  
         

                    
 
                                                     
 

                           



                                          
         
 
 
                              
 
                    
                    
 
                                                                
 
                              
 
                           
 
                                                       

                                                                                         
                                                   


                                                                                        




                                            
                                                  

         




                                                                         
 

                   
 
                                

                
                    
 
                              
 





                                    






                                      
                                               
                                                   
                                                     



                                      






                                                                                 





                                                                
                                                       







                                                   
                                                       












                                                                

         
                                                                 




                     
                                          



                              
         



                                                         
 
                                 
                                                                                                                                          



                                                               
            


                                      
         
 
 
                                       
 

                    
                              
 
                              
 
                                                                
 

                                  
                                                                                         
                                                   






                                                                                
 



                                                                         
         
 
 
                                          
 
                              
         
                                
                 
                                           
                                                                          
                 
                    
                 
                                         
                                                                       
                                                                       

                 





                                      
         
 
 
                                           
 



                                    
                                                    


                                                             

 





                                                                            
                                 
 
                                      
                            
                               
 
                                                  
 
                                                         
                                       
 

                                
 
                                  
         
                                              
                                                                                                          

         

                                               

                       











                                           
                                    
                                        
                                                        



                                       


                                        

















                                         




                                          


                                 


              

                       
                       










                                                       









                                                                                   

                             





                                                                                                                  

 
 
                                  
 
                                                                               
                                            
















                                                
                   
 
                          
         



                                                                    
                                                                                                                 

         

                                         
         
                                                        
 
                                                                                                                 
         
 


                                                                            
                


                                                                            
                                                   
 
                      
 

                                                                                 
                                                                 
                                            
                                                                  



                                         

                                                               

 
                                       
 
                                                                                 
                                                                                     
            
                                                       

 
                              
 
                  

                                                                                      
                                                             
 
             



                                 
              



                                
                                                                       


          



                                                                              
                          

 






                                                                            
 
                                         









                                                               
                                            




                               
                                                                                      


                       
                              

                      
                                                                   

               
                                       

                            
                                                                        



                      

                                       

                                                                        


                      
                                      

                      
                                                        


                
                                                                                                    



                      
                                           
                 
                                                                                                                 

                                 




                                                              


                      
                                  


                      
                                      



                      
                                                                           
 




                                                                            
                         


                                                                            



                                                                              
 
 
                                               
 
                  
              
 
                                          
                             
                                

 
                                                
 
                  

              
                                    
                             
                                

 
                                                

              

                   
 

                                                  
                                 
                                                   
 
 
                                                     

                                      
                          
                                          
 

                                              
 

                                       


                           
                                          
 
                                   



                                  

                                                                                                                        
                                    
                 
                                                           
                                                           





                                                                                
                                          
                                  

 
                                                     

                                                                     
                                    
                                                          
 


                                                                                 
                                                                                                  
             
                                                                     

                                                      

                                                                              



                                         


                           
                                   
                                          



                                  
                                                                                      

                            
                                                                                                  

                                                             

                                                                                                     
 





                                                                     


                                         
                         

                 

                                                                                     
 





                                                   



                       
                                        
                                  

 
                                                     
 
                                                  
                               
                          
 

                                                



                                              
                                             
                            



                                    
                                                                                                                                       
                                         




                                  



                            
                                         




                                  



                            


                       
                                   
                                    

 
                                                        
 
                          
                                                          

                                                               


                                                                       




                                              
                                          
                             



                                  

                                                                              

                                        
                                                                                                  



                                                           
                                                           




                                      
                                   
                                 

 
                                                         
 
                          
                                                          
                                                  
                                             
 

                                                            
 


                                                                                 



                                              
                                          
                             



                                  


                                                                                                          

                                        


                                                           
                                                           
                                      



                       
                                   
                                 

 
                                                        
 
                                 
                                   
                          




                                                           


                                                                    
 
                                             
                            



                                    
                                                                 
                                         
                            
                            
                                         
                            
                            

         
                                   
                                    

 
                                                        
 
                                 
                                   

                         




                                                           


                                                                    
 

                                                        
 

                                                                
 
                                           
 
                      
         

                                      
         
 


                                                                 
 



                                               
         
 

                                                        

                                                                

 
                                                        
 
                       
                                                 

                          
 
                                                     


                                                           


                                                             
 
                                             
                            





                                    

                                                                                                       

                                                 



                                                                           
                                          





                                   
                                                                                                       

                                                   

                                       

         
                                   
                                    

 
                                                        
 
                       
                                                 
                          




                                                           


                                                             
 
                                       
                            

                                    




                                                                                          
                                








                                                                                                          

                                                                             

                                                                            
                                                                            

                                                                            
                                                                            

                                                                            
                                                                            

                                                                            
                                                                            








                                                                                                          
                                
 







                                                                                                          

                                                                             
 

                                                                                                                    
 

                                                                                                                    
 

                                                                                                                    
 

                                                                                                                    




                                       


                                  

                                                                                                       
                                             



                                                 



                                                 
                                       
                 



                                   

                                                                                                       
                                             
                                    
                                    

                                       
         
 
                                   
                                    
 
 
                                                       
 
                     

              

                                       
 

                               
 

                               
 

                                  

 
                                                                  
 
              
                             
 



                                  

                                                     


                    
                                


                 
 
                                                                   

              
                             
 



                                  

                                                            


                    
                                


                 
 
 
                                                                   
 
                                              
                                                 
                                              
              
 
                             
 
                           
         
                                  
                 


                                                                                                                                                            


                    
                                




                 
                                                        
 
                                                        

                                                  
                            
 

                                         
 

                                                                     
 



                                              


                   
         
                                                          
                                    
                                               




                                      
                                                      
                                                             
                 
                                    



                                                                                          
 
                                      

                                

                                                                                      
 
                                      
                                                                            
                                              

                                                

                                                                                                                          
 
                                     
                                                                             
 
                                         

                                 
                         

                                                                                                                                 




                                                 
                                 
                                                           
                                         
                                                                            
                                                 
                                                                          

                                                                                       
                                                                           

                                                                                              
                                                                               

                                                                                                 


                                                                                           
                                                         
 


                                                                                        
                                                                                    

                                                                                               
                                                                                   
                                                                                                      



                                                                                                         


                                                                                                     
                                                                 

                                                         
                                         

                                                                                
                                 
                         

                             
                 


                                   
                                  

         



                                                           
                                                        
 
                                                                        

                                                                             
                            
 

                                         
 


                                                                                  
                                                                      
 



                                              


                                   
         
                                            
                                    
                                               




                                      
                                                      
                                                             
                 
                                    



                                                                                          
 
                                      
                         
 

                                                                    
                                                                                  
 
                                                                               
                                                            
                                     
                         

                                                               
                         


                                                  
                                      
                                                              
                                              
                                                

                                                                                                
 
                                
                                                        

                                              
                                                         
                                   

                                                                    
                                 
                                                                    
                                         
                                                                                             

                                                                                                 
                                                         
                                                                                  
                                                                 


                                                                                                          
                                                                 



                                                                                         
                                                         
                                                 
                                                    
                                                 

                                                                                             
                                                 

                                                                                        
                                                 
                                                                          

                                                                                    
                                                                           

                                                                                              

                                                                               
                                                 

                                                    
                                         
                                 

                                              
                         
 
                                  
                             
                 


                                   
                                  

         



                                                           
 
                                                          
               

                                              

                        
          
               

                                              
                                            

                                                  
               

                                              
                                            

                                                  
               

                                              
                                            

                                                  
               

                                              
                                            

                                                  
               

                                              
                                            

                                                  
               

                                               
                                            

                                                   
               

                                              
                                            

                                                  
               

                                               
                                            

                                                   
               

                                              

                        
          
               

                                              

                        
         

  
                             
 

                
                                                                            
                      

                                                                           
         













                                                                                 
                                                                            



                                       

                   
 
                                       
 
                                  
                         
                                     
 
                                 
         
                                                                          
         
            
         

                                                      

                                    
 

                                                                
                                                       
         
            
         
                                                              
                                            
                 
                                                                    
                                                                         
                 
         

                         
                                                          
 
 
                                         
 
                            
 

                           
 

                                                                         

                                                          
         
 
                         
 

                                                                         

                                      
         
 









































































































                                                                              
                                           
 
             
                                                      
         
                                                                                        
         
              
 

                                                                      
 




                                          
            




                                                                       
                 


                                                                           
                            
                                                                             
                 
         



                                                                            
            


                                                                            

















                                                               





                                                                                                                                                                                  


















                                                              





                                                              
                  
                          

                           
 
                   
                       

                                           




                         

         
 
                       
                       

                                                           


                         

         
 
                           
                       

                                                           





                         

         

                                                               

                                                               
                                                       

                                                
 

                           
                                 

                                      

                                 

                               

                                 

                              

                                 

                              

                                 

                              

                                 

                              

                                 

                               
  
 


                                                               
                                                              
 






                                         
 
                 




                                                                
                                                               
 


                                                                  




                                                                
                                                               
 


                                                                  




                                                                
                                                               
 


                                                                  




                                                                
                                                               
 
                                                               




                                                                
                                                                          
 






                                            
 
                  





                                                               
                                                                                      


                                                                               
                                                    












                                                           
                                                                          


                                            




                                                               
                                                                                   
 
                                  
                                                                         




                                                               
                                                                                   
 
                                  
 
                                                                        




                                                               
                                                                                   
 
                                  
 
                                                                         




                                                               
                                                                                   
 
                                                           




                                                               
                                                                                              
 
                     




                                                     

 
                                                                  

                                                               
                                                                
 
                                                                                               




                                                               





                                
                            








                                                               
                                                                                      










                                                                                                           
 


                                         
                                       






                                               




                                                               









                                

                            






















































                                                                                            
 


                                         
                                     











                                                                

                                                               









                                

                            





















                                                                                         
                                     















                                                                

                                                               












                                
                            






















                                                                                                              
                                     
























                                                                

                                                               


                                         






                                                                                                                                          
                                                     












                                                                    

 
                                                                

                                                               


                                         
                                                                             








                                                                                       
                                                     












                                                                    

 
                                                                

                                                               









                                
                            







                                                  













                                                                                                                
                                     












                                              


                                                                

                                                               



































                                                                                                                                                              
                                     
























                                                                

                                                               































                                                                                                                                                              
                                     






















                                                                

                                                               


                                         






                                                                                                                                                      
                                                     












                                                                    


                                                               
                                                             
                                                               
                                        
 


                                                                      




                                                               
                                  
 


                                                                      

 
                                                                 

                                                               
                                                 
 
                                          



                                                                        
                                               




                                                                                
                           
                                
 
                          






                                                              


                     
                                                                                                
                                          
                                                                   

                                                     
                  




                                                               
                                                






                                             
                                                        








                                                                 


                                                                                         


                                    
                                     


                                    
                                     






                                                                 
                                     




































                                                                
                                                                      















                                                                    
                                                       






























                                                                                      
                                           

















                                                                              
 
// license:BSD-3-Clause
// copyright-holders:Aaron Giles, Nathan Woods

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

v9938 / v9958 emulation

Vertical display parameters from Yamaha V9938 Technical Data Book.
NTSC: page 146, Table 7-2
PAL: page 147, Table 7-3

Vertical timing:
                                       PAL                 NTSC
                                       192(LN=0) 212(LN=1) 192(LN=0) 212(LN=1)
                                       ------------------- --------------------
1. Top erase (top blanking)                13        13        13        13
2. Top border                              53        43        26        16
3. Active display                         192       212       192       212
4. Bottom border                           49        39        25        15
5. Bottom erase (bottom blanking)           3         3         3         3
6. Vertical sync (bottom blanking)          3         3         3         3
7. Total                                  313       313       262       262

   Refresh rate                           50.158974           59.922743


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

/*
todo:

- sprite collision
- vdp engine -- make run at correct speed
- vr/hr/fh flags: double-check all of that
- make vdp engine work in exp. ram
- fix save state support
*/

#include "emu.h"
#include "v9938.h"

#define LOG_WARN     (1U<<1)
#define LOG_INT      (1U<<2)
#define LOG_STATUS   (1U<<3)
#define LOG_REGWRITE (1U<<4)
#define LOG_COMMAND  (1U<<5)
#define LOG_MODE     (1U<<6)
#define LOG_NOTIMP   (1U<<7)
#define LOG_DETAIL   (1U<<8)

// Minimum log should be warnings
#define VERBOSE (LOG_GENERAL | LOG_WARN )

#include "logmacro.h"

enum
{
	V9938_MODE_TEXT1 = 0,
	V9938_MODE_MULTI,
	V9938_MODE_GRAPHIC1,
	V9938_MODE_GRAPHIC2,
	V9938_MODE_GRAPHIC3,
	V9938_MODE_GRAPHIC4,
	V9938_MODE_GRAPHIC5,
	V9938_MODE_GRAPHIC6,
	V9938_MODE_GRAPHIC7,
	V9938_MODE_TEXT2,
	V9938_MODE_UNKNOWN
};

#define MODEL_V9938 (0)
#define MODEL_V9958 (1)

#define EXPMEM_OFFSET 0x20000

#define V9938_LONG_WIDTH (512 + 32)

static const char *const v9938_modes[] = {
	"TEXT 1", "MULTICOLOR", "GRAPHIC 1", "GRAPHIC 2", "GRAPHIC 3",
	"GRAPHIC 4", "GRAPHIC 5", "GRAPHIC 6", "GRAPHIC 7", "TEXT 2",
	"UNKNOWN"
};

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

/*
Similar to the TMS9928, the V9938 has an own address space. It can handle
at most 192 KiB RAM (128 KiB base, 64 KiB expansion).
*/
void v99x8_device::memmap(address_map &map)
{
	map.global_mask(0x3ffff);
	map(0x00000, 0x2ffff).ram();
}


// devices
DEFINE_DEVICE_TYPE(V9938, v9938_device, "v9938", "Yamaha V9938 VDP")
DEFINE_DEVICE_TYPE(V9958, v9958_device, "v9958", "Yamaha V9958 VDP")


v99x8_device::v99x8_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int model)
:   device_t(mconfig, type, tag, owner, clock),
	device_memory_interface(mconfig, *this),
	device_palette_interface(mconfig, *this),
	device_video_interface(mconfig, *this),
	m_space_config("vram", ENDIANNESS_BIG, 8, 18),
	m_model(model),
	m_pal_config(false),
	m_offset_x(0),
	m_offset_y(0),
	m_visible_y(0),
	m_mode(0),
	m_pal_write_first(0),
	m_cmd_write_first(0),
	m_pal_write(0),
	m_cmd_write(0),
	m_read_ahead(0),
	m_v9958_sp_mode(0),
	m_address_latch(0),
	m_vram_size(0),
	m_int_state(0),
	m_int_callback(*this),
	m_scanline(0),
	m_blink(0),
	m_blink_count(0),
	m_mx_delta(0),
	m_my_delta(0),
	m_button_state(0),
	m_vdp_ops_count(0),
	m_vdp_engine(nullptr),
	m_pal_ntsc(0)
{
	set_addrmap(AS_DATA, address_map_constructor(FUNC(v99x8_device::memmap), this));
}

v9938_device::v9938_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: v99x8_device(mconfig, V9938, tag, owner, clock, MODEL_V9938)
{
}

v9958_device::v9958_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: v99x8_device(mconfig, V9958, tag, owner, clock, MODEL_V9958)
{
}

device_memory_interface::space_config_vector v99x8_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(AS_DATA, &m_space_config)
	};
}


void v99x8_device::device_config_complete()
{
	if (!has_screen())
		return;

	if (!screen().refresh_attoseconds())
		screen().set_raw(clock(),
			HTOTAL,
			0,
			HVISIBLE - 1,
			(m_pal_config ? VTOTAL_PAL : VTOTAL_NTSC) * 2,
			VERTICAL_ADJUST * 2,
			(m_pal_config ? VVISIBLE_PAL : VVISIBLE_NTSC) * 2 - 1 - VERTICAL_ADJUST * 2);

	if (!screen().has_screen_update())
		screen().set_screen_update(*this, FUNC(v99x8_device::screen_update));
}


void v99x8_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	int scanline = (m_scanline - (m_scanline_start + m_offset_y));

	update_command ();

	// set flags
	if (m_scanline == (m_scanline_start + m_offset_y))
	{
		m_stat_reg[2] &= ~0x40;
	}
	else if (m_scanline == (m_scanline_start + m_offset_y + m_visible_y))
	{
		m_stat_reg[2] |= 0x40;
		m_stat_reg[0] |= 0x80;
	}

	if ( (scanline >= 0) && (scanline <= m_scanline_max) &&
		(((scanline + m_cont_reg[23]) & 255) == m_cont_reg[19]) )
	{
		m_stat_reg[1] |= 1;
		LOGMASKED(LOG_INT, "Scanline interrupt (%d)\n", scanline);
	}
	else if (!(m_cont_reg[0] & 0x10))
	{
		m_stat_reg[1] &= 0xfe;
	}

	check_int();

	// check for start of vblank
	if (m_scanline == m_vblank_start)
	{
		interrupt_start_vblank();
	}

	// render the current line
	if (m_scanline < m_vblank_start)
	{
		refresh_line(scanline);
	}

	if (++m_scanline >= m_height)
	{
		m_scanline = 0;
		// PAL/NTSC changed?
		int pal = m_cont_reg[9] & 2;
		if (m_pal_ntsc != pal)
		{
			m_pal_ntsc = pal;
			configure_pal_ntsc();
		}
		//screen().reset_origin();
		m_offset_y = position_offset(m_cont_reg[18] >> 4);
		set_screen_parameters();
	}
}


void v99x8_device::set_screen_parameters()
{
	if (m_pal_ntsc)
	{
		// PAL
		m_scanline_start = (m_cont_reg[9] & 0x80) ? 43 : 53;
		m_scanline_max = 255;
	}
	else
	{
		// NYSC
		m_scanline_start = (m_cont_reg[9] & 0x80) ? 16 : 26;
		m_scanline_max = (m_cont_reg[9] & 0x80) ? 234 : 244;
	}
	m_visible_y = (m_cont_reg[9] & 0x80) ? 212 : 192;
}


void v99x8_device::configure_pal_ntsc()
{
	if (m_pal_ntsc)
	{
		// PAL
		m_height = VTOTAL_PAL;
		rectangle visible;
		visible.set(0, HVISIBLE - 1, VERTICAL_ADJUST * 2, VVISIBLE_PAL * 2 - 1 - VERTICAL_ADJUST * 2);
		screen().configure(HTOTAL, VTOTAL_PAL * 2, visible, HZ_TO_ATTOSECONDS(50.158974));
	}
	else
	{
		// NTSC
		m_height = VTOTAL_NTSC;
		rectangle visible;
		visible.set(0, HVISIBLE - 1, VERTICAL_ADJUST * 2, VVISIBLE_NTSC * 2 - 1 - VERTICAL_ADJUST * 2);
		screen().configure(HTOTAL, VTOTAL_NTSC * 2, visible, HZ_TO_ATTOSECONDS(59.922743));
	}
	m_vblank_start = m_height - VERTICAL_SYNC - TOP_ERASE; /* Sync + top erase */
}


/*
    Colorbus inputs
    vdp will process mouse deltas only if it is in mouse mode
    Reg 8: MS LP x x x x x x
*/
void v99x8_device::colorbus_x_input(int mx_delta)
{
	if ((m_cont_reg[8] & 0xc0) == 0x80)
	{
		m_mx_delta += mx_delta;
		if (m_mx_delta < -127) m_mx_delta = -127;
		if (m_mx_delta > 127) m_mx_delta = 127;
	}
}

void v99x8_device::colorbus_y_input(int my_delta)
{
	if ((m_cont_reg[8] & 0xc0) == 0x80)
	{
		m_my_delta += my_delta;
		if (m_my_delta < -127) m_my_delta = -127;
		if (m_my_delta > 127) m_my_delta = 127;
	}
}

void v99x8_device::colorbus_button_input(bool switch1_pressed, bool switch2_pressed)
{
	// save button state
	m_button_state = (switch2_pressed? 0x80 : 0x00) | (switch1_pressed? 0x40 : 0x00);
}


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

Palette functions

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

/*
About the colour burst registers:

The color burst registers will only have effect on the composite video output from
the V9938. but the output is only NTSC (Never The Same Color ,so the
effects are already present) . this system is not used in europe
the european machines use a separate PAL  (Phase Alternating Line) encoder
or no encoder at all , only RGB output.

Erik de Boer.

--
Right now they're not emulated. For completeness sake they should -- with
a dip-switch to turn them off. I really don't know how they work though. :(
*/

/*
In screen 8, the colors are encoded as:

7  6  5  4  3  2  1  0
+--+--+--+--+--+--+--+--+
|g2|g1|g0|r2|r1|r0|b2|b1|
+--+--+--+--+--+--+--+--+

b0 is set if b2 and b1 are set (remember, color bus is 3 bits)

*/

void v9938_device::palette_init()
{
}

/*

The v9958 can display up to 19286 colours. For this we need a larger palette.

The colours are encoded in 17 bits; however there are just 19268 different colours.
Here we calculate the palette and a 2^17 reference table to the palette,
which is: s_pal_indYJK. It's 256K in size, but I can't think of a faster way
to emulate this. Also it keeps the palette a reasonable size. :)

*/

uint32_t v99x8_device::s_pal_indYJK[0x20000];

void v9958_device::palette_init()
{
	int r,g,b,y,j,k,k0,j0;

	// set up YJK table
	LOGMASKED(LOG_DETAIL, "Building YJK table for V9958 screens, may take a while ... \n");

	for (y=0;y<32;y++) for (k=0;k<64;k++) for (j=0;j<64;j++)
	{
		// calculate the color
		if (k >= 32) k0 = (k - 64); else k0 = k;
		if (j >= 32) j0 = (j - 64); else j0 = j;
		r = y + j0;
		b = (y * 5 - 2 * j0 - k0) / 4;
		g = y + k0;
		if (r < 0) r = 0; else if (r > 31) r = 31;
		if (g < 0) g = 0; else if (g > 31) g = 31;
		if (b < 0) b = 0; else if (b > 31) b = 31;

		v99x8_device::s_pal_indYJK[y | j << 5 | k << (5 + 6)] = uint32_t(rgb_t(pal5bit(r), pal5bit(g), pal5bit(b)));
	}
}

uint32_t v99x8_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}

uint8_t v99x8_device::read(offs_t offset)
{
	switch (offset & 3)
	{
	case 0: return vram_r();
	case 1: return status_r();
	}
	return 0xff;
}

void v99x8_device::write(offs_t offset, uint8_t data)
{
	switch (offset & 3)
	{
	case 0: vram_w(data);       break;
	case 1: command_w(data);    break;
	case 2: palette_w(data);    break;
	case 3: register_w(data);   break;
	}
}

uint8_t v99x8_device::vram_r()
{
	uint8_t ret;
	int address;

	address = ((int)m_cont_reg[14] << 14) | m_address_latch;

	m_cmd_write_first = 0;

	ret = m_read_ahead;

	if (m_cont_reg[45] & 0x40)  // Expansion memory
	{
		if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
			address >>= 1;  // correct?
		// Expansion memory only offers 64 K
		if (m_vram_size > 0x20000 && ((address & 0x10000)==0))
			m_read_ahead = m_vram_space->read_byte(address + EXPMEM_OFFSET);
		else
			m_read_ahead = 0xff;
	}
	else
	{
		m_read_ahead = vram_read(address);
	}

	m_address_latch = (m_address_latch + 1) & 0x3fff;
	if ((!m_address_latch) && (m_cont_reg[0] & 0x0c) ) // correct ???
	{
		m_cont_reg[14] = (m_cont_reg[14] + 1) & 7;
	}

	return ret;
}

uint8_t v99x8_device::status_r()
{
	int reg;
	uint8_t ret;

	m_cmd_write_first = 0;

	reg = m_cont_reg[15] & 0x0f;
	if (reg > 9)
		return 0xff;

	switch (reg)
	{
	case 0:
		ret = m_stat_reg[0];
		m_stat_reg[0] &= 0x1f;
		break;
	case 1:
		ret = m_stat_reg[1];
		m_stat_reg[1] &= 0xfe;
		// mouse mode: add button state
		if ((m_cont_reg[8] & 0xc0) == 0x80)
			ret |= m_button_state & 0xc0;
		break;
	case 2:
		/*update_command ();*/
		/*
		WTF is this? Whatever this was intended to do, it is nonsensical.
		Might as well pick a random number....
		This was an attempt to emulate H-Blank flag ;)
		n = cycles_currently_ran ();
		if ( (n < 28) || (n > 199) ) vdp.statReg[2] |= 0x20;
		else vdp.statReg[2] &= ~0x20;
		*/
		if (machine().rand() & 1) m_stat_reg[2] |= 0x20;
		else m_stat_reg[2] &= ~0x20;
		ret = m_stat_reg[2];
		break;
	case 3:
		if ((m_cont_reg[8] & 0xc0) == 0x80)
		{   // mouse mode: return x mouse delta
			ret = m_mx_delta;
			m_mx_delta = 0;
		}
		else
			ret = m_stat_reg[3];
		break;
	case 5:
		if ((m_cont_reg[8] & 0xc0) == 0x80)
		{   // mouse mode: return y mouse delta
			ret = m_my_delta;
			m_my_delta = 0;
		}
		else
			ret = m_stat_reg[5];
		break;
	case 7:
		ret = m_stat_reg[7];
		m_stat_reg[7] = m_cont_reg[44] = vdp_to_cpu () ;
		break;
	default:
		ret = m_stat_reg[reg];
		break;
	}

	LOGMASKED(LOG_STATUS, "Read %02x from S#%d\n", ret, reg);
	check_int ();

	return ret;
}

void v99x8_device::palette_w(uint8_t data)
{
	int indexp;

	if (m_pal_write_first)
	{
		// store in register
		indexp = m_cont_reg[0x10] & 15;
		m_pal_reg[indexp*2] = m_pal_write & 0x77;
		m_pal_reg[indexp*2+1] = data & 0x07;

		// update palette
		set_pen16(indexp, uint32_t(rgb_t(pal3bit((m_pal_write & 0x70) >> 4), pal3bit(data & 0x07), pal3bit(m_pal_write & 0x07))));

		m_cont_reg[0x10] = (m_cont_reg[0x10] + 1) & 15;
		m_pal_write_first = 0;
	}
	else
	{
		m_pal_write = data;
		m_pal_write_first = 1;
	}
}

void v99x8_device::vram_w(uint8_t data)
{
	int address;

	/*update_command ();*/

	m_cmd_write_first = 0;

	address = ((int)m_cont_reg[14] << 14) | m_address_latch;

	if (m_cont_reg[45] & 0x40)
	{
		if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
			address >>= 1;  // correct?
		if (m_vram_size > 0x20000 && ((address & 0x10000)==0))
			m_vram_space->write_byte(EXPMEM_OFFSET + address, data);
	}
	else
	{
		vram_write(address, data);
	}

	m_address_latch = (m_address_latch + 1) & 0x3fff;
	if ((!m_address_latch) && (m_cont_reg[0] & 0x0c) ) // correct ???
	{
		m_cont_reg[14] = (m_cont_reg[14] + 1) & 7;
	}
}

void v99x8_device::command_w(uint8_t data)
{
	if (m_cmd_write_first)
	{
		if (data & 0x80)
		{
			if (!(data & 0x40))
				register_write (data & 0x3f, m_cmd_write);
		}
		else
		{
			m_address_latch =
			(((uint16_t)data << 8) | m_cmd_write) & 0x3fff;
			if ( !(data & 0x40) ) vram_r (); // read ahead!
		}

		m_cmd_write_first = 0;
	}
	else
	{
		m_cmd_write = data;
		m_cmd_write_first = 1;
	}
}

void v99x8_device::register_w(uint8_t data)
{
	int reg;

	reg = m_cont_reg[17] & 0x3f;
	if (reg != 17)
		register_write(reg, data); // true ?

	if (!(m_cont_reg[17] & 0x80))
		m_cont_reg[17] = (m_cont_reg[17] + 1) & 0x3f;
}

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

    Init/stop/reset/Interrupt functions

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

void v99x8_device::device_start()
{
	m_int_callback.resolve_safe();
	m_vdp_ops_count = 1;
	m_vdp_engine = nullptr;

	screen().register_screen_bitmap(m_bitmap);

	// Video RAM is allocated as an own address space
	m_vram_space = &space(AS_DATA);

	// allocate VRAM
	assert(m_vram_size > 0);

	if (m_vram_size < 0x20000)
	{
		// set unavailable RAM to 0xff
		for (int addr = m_vram_size; addr < 0x30000; addr++) m_vram_space->write_byte(addr, 0xff);
	}

	m_line_timer = timer_alloc(TIMER_LINE);

	palette_init();

	save_item(NAME(m_offset_x));
	save_item(NAME(m_offset_y));
	save_item(NAME(m_visible_y));
	save_item(NAME(m_mode));
	save_item(NAME(m_pal_write_first));
	save_item(NAME(m_cmd_write_first));
	save_item(NAME(m_pal_write));
	save_item(NAME(m_cmd_write));
	save_item(NAME(m_pal_reg));
	save_item(NAME(m_stat_reg));
	save_item(NAME(m_cont_reg));
	save_item(NAME(m_read_ahead));
	//  save_item(NAME(m_vram));
	//  if ( m_vram_exp != nullptr )
	//      save_pointer(NAME(m_vram_exp), 0x10000);
	save_item(NAME(m_int_state));
	save_item(NAME(m_scanline));
	save_item(NAME(m_blink));
	save_item(NAME(m_blink_count));
	save_item(NAME(m_mx_delta));
	save_item(NAME(m_my_delta));
	save_item(NAME(m_button_state));
	save_item(NAME(m_mmc.SX));
	save_item(NAME(m_mmc.SY));
	save_item(NAME(m_mmc.DX));
	save_item(NAME(m_mmc.DY));
	save_item(NAME(m_mmc.TX));
	save_item(NAME(m_mmc.TY));
	save_item(NAME(m_mmc.NX));
	save_item(NAME(m_mmc.NY));
	save_item(NAME(m_mmc.MX));
	save_item(NAME(m_mmc.ASX));
	save_item(NAME(m_mmc.ADX));
	save_item(NAME(m_mmc.ANX));
	save_item(NAME(m_mmc.CL));
	save_item(NAME(m_mmc.LO));
	save_item(NAME(m_mmc.CM));
	save_item(NAME(m_mmc.MXS));
	save_item(NAME(m_mmc.MXD));
	save_item(NAME(m_vdp_ops_count));
	save_item(NAME(m_pal_ntsc));
	save_item(NAME(m_scanline_start));
	save_item(NAME(m_vblank_start));
	save_item(NAME(m_scanline_max));
	save_item(NAME(m_height));
}

void v99x8_device::device_reset()
{
	int i;

	// offset reset
	m_offset_x = 8;
	m_offset_y = 0;
	m_visible_y = 192;
	// register reset
	reset_palette (); // palette registers
	for (i=0;i<10;i++) m_stat_reg[i] = 0;
	m_stat_reg[2] = 0x0c;
	if (m_model == MODEL_V9958) m_stat_reg[1] |= 4;
	for (i=0;i<48;i++) m_cont_reg[i] = 0;
	m_cmd_write_first = m_pal_write_first = 0;
	m_int_state = 0;
	m_read_ahead = 0; m_address_latch = 0; // ???
	m_scanline = 0;
	// MZ: The status registers 4 and 6 hold the high bits of the sprite
	// collision location. The unused bits are set to 1.
	// SR3: x x x x x x x x
	// SR4: 1 1 1 1 1 1 1 x
	// SR5: y y y y y y y y
	// SR6: 1 1 1 1 1 1 y y
	// Note that status register 4 is used in detection algorithms to tell
	// apart the tms9929 from the v99x8.

	// TODO: SR3-S6 do not yet store the information about the sprite collision
	m_stat_reg[4] = 0xfe;
	m_stat_reg[6] = 0xfc;

	// Start the timer
	m_line_timer->adjust(attotime::from_ticks(HTOTAL*2, m_clock), 0, attotime::from_ticks(HTOTAL*2, m_clock));

	configure_pal_ntsc();
	set_screen_parameters();
}


void v99x8_device::reset_palette()
{
	// taken from V9938 Technical Data book, page 148. it's in G-R-B format
	static const uint8_t pal16[16*3] = {
		0, 0, 0, // 0: black/transparent
		0, 0, 0, // 1: black
		6, 1, 1, // 2: medium green
		7, 3, 3, // 3: light green
		1, 1, 7, // 4: dark blue
		3, 2, 7, // 5: light blue
		1, 5, 1, // 6: dark red
		6, 2, 7, // 7: cyan
		1, 7, 1, // 8: medium red
		3, 7, 3, // 9: light red
		6, 6, 1, // 10: dark yellow
		6, 6, 4, // 11: light yellow
		4, 1, 1, // 12: dark green
		2, 6, 5, // 13: magenta
		5, 5, 5, // 14: gray
		7, 7, 7  // 15: white
	};
	int i, red;

	for (i=0;i<16;i++)
	{
		// set the palette registers
		m_pal_reg[i*2+0] = pal16[i*3+1] << 4 | pal16[i*3+2];
		m_pal_reg[i*2+1] = pal16[i*3];
		// set the reference table
		set_pen16(i, uint32_t(rgb_t(pal3bit(pal16[i*3+1]), pal3bit(pal16[i*3]), pal3bit(pal16[i*3+2]))));
	}

	// set internal palette GRAPHIC 7
	for (i=0;i<256;i++)
	{
		red = (i << 1) & 6; if (red == 6) red++;

		set_pen256(i, uint32_t(rgb_t(pal3bit((i & 0x1c) >> 2), pal3bit((i & 0xe0) >> 5), pal3bit(red))));
	}
}

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

Memory functions

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

void v99x8_device::vram_write(int offset, int data)
{
	int newoffset;

	if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
	{
		newoffset = ((offset & 1) << 16) | (offset >> 1);
		if (newoffset < m_vram_size)
			m_vram_space->write_byte(newoffset, data);
	}
	else
	{
		if (offset < m_vram_size)
			m_vram_space->write_byte(offset, data);
	}
}

int v99x8_device::vram_read(int offset)
{
	if ( (m_mode == V9938_MODE_GRAPHIC6) || (m_mode == V9938_MODE_GRAPHIC7) )
		return m_vram_space->read_byte(((offset & 1) << 16) | (offset >> 1));
	else
		return m_vram_space->read_byte(offset);
}

void v99x8_device::check_int()
{
	uint8_t n;

	n = ( (m_cont_reg[1] & 0x20) && (m_stat_reg[0] & 0x80) /*&& m_vblank_int*/) ||
	( (m_stat_reg[1] & 0x01) && (m_cont_reg[0] & 0x10) );

	#if 0
	if(n && m_vblank_int)
	{
		m_vblank_int = 0;
	}
	#endif

	if (n != m_int_state)
	{
		m_int_state = n;
		LOGMASKED(LOG_INT, "IRQ line %s\n", n ? "up" : "down");
	}

	/*
	** Somehow the IRQ request is going down without cpu_irq_line () being
	** called; because of this Mr. Ghost, Xevious and SD Snatcher don't
	** run. As a patch it's called every scanline
	*/
	m_int_callback(n);
}

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

    Register functions

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

void v99x8_device::register_write (int reg, int data)
{
	static uint8_t const reg_mask[] =
	{
		0x7e, 0x7b, 0x7f, 0xff, 0x3f, 0xff, 0x3f, 0xff,
		0xfb, 0xbf, 0x07, 0x03, 0xff, 0xff, 0x07, 0x0f,
		0x0f, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0x00, 0x7f, 0x3f, 0x07
	};

	if (reg <= 27)
	{
		data &= reg_mask[reg];
		if (m_cont_reg[reg] == data)
			return;
	}

	if (reg > 46)
	{
		LOGMASKED(LOG_WARN, "Attempted to write to non-existent R#%d\n", reg);
		return;
	}

	/*update_command ();*/

	switch (reg) {
		// registers that affect interrupt and display mode
	case 0:
	case 1:
		m_cont_reg[reg] = data;
		set_mode();
		check_int();
		LOGMASKED(LOG_MODE, "Mode = %s\n", v9938_modes[m_mode]);
		break;

	case 18:
	case 9:
		m_cont_reg[reg] = data;
		// recalc offset
		m_offset_x = 8 + position_offset(m_cont_reg[18] & 0x0f);
		// Y offset is only applied once per frame?
		break;

	case 15:
		m_pal_write_first = 0;
		break;

		// color burst registers aren't emulated
	case 20:
	case 21:
	case 22:
		LOGMASKED(LOG_NOTIMP, "Write %02xh to R#%d; color burst not emulated\n", data, reg);
		break;
	case 25:
	case 26:
	case 27:
		if (m_model != MODEL_V9958)
		{
			LOGMASKED(LOG_WARN, "Attempting to write %02xh to R#%d (invalid on v9938)\n", data, reg);
			data = 0;
		}
		else
		{
			if(reg == 25)
				m_v9958_sp_mode = data & 0x18;
		}
		break;

	case 44:
		cpu_to_vdp (data);
		break;

	case 46:
		command_unit_w (data);
		break;
	}

	if (reg != 15)
		LOGMASKED(LOG_REGWRITE, "Write %02x to R#%d\n", data, reg);

	m_cont_reg[reg] = data;
}

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

Refresh / render function

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

inline bool v99x8_device::v9938_second_field()
{
	return !(((m_cont_reg[9] & 0x04) && !(m_stat_reg[2] & 2)) || m_blink);
}


void v99x8_device::default_border(uint32_t *ln)
{
	pen_t pen;
	int i;

	pen = pen16(m_cont_reg[7] & 0x0f);
	i = V9938_LONG_WIDTH;
	while (i--) *ln++ = pen;
}

void v99x8_device::graphic7_border(uint32_t *ln)
{
	pen_t pen;
	int i;

	pen = pen256(m_cont_reg[7]);
	i = V9938_LONG_WIDTH;
	while (i--) *ln++ = pen;
}

void v99x8_device::graphic5_border(uint32_t *ln)
{
	int i;
	pen_t pen0;
	pen_t pen1;

	pen1 = pen16(m_cont_reg[7] & 0x03);
	pen0 = pen16((m_cont_reg[7] >> 2) & 0x03);
	i = V9938_LONG_WIDTH / 2;
	while (i--) { *ln++ = pen0; *ln++ = pen1; }
}

void v99x8_device::mode_text1(uint32_t *ln, int line)
{
	int pattern, x, xx, name, xxx;
	pen_t fg, bg, pen;
	int nametbl_addr, patterntbl_addr;

	patterntbl_addr = m_cont_reg[4] << 11;
	nametbl_addr = m_cont_reg[2] << 10;

	fg = pen16(m_cont_reg[7] >> 4);
	bg = pen16(m_cont_reg[7] & 15);

	name = (line/8)*40;

	pen = pen16(m_cont_reg[7] & 0x0f);

	xxx = (m_offset_x + 8) * 2;
	while (xxx--) *ln++ = pen;

	for (x=0;x<40;x++)
	{
		pattern = m_vram_space->read_byte(patterntbl_addr + (m_vram_space->read_byte(nametbl_addr + name) * 8) +
			((line + m_cont_reg[23]) & 7));
		for (xx=0;xx<6;xx++)
		{
			*ln++ = (pattern & 0x80) ? fg : bg;
			*ln++ = (pattern & 0x80) ? fg : bg;
			pattern <<= 1;
		}
		/* width height 212, characters start repeating at the bottom */
		name = (name + 1) & 0x3ff;
	}

	xxx = ((16 - m_offset_x) + 8) * 2;
	while (xxx--) *ln++ = pen;
}

void v99x8_device::mode_text2(uint32_t *ln, int line)
{
	int pattern, x, charcode, name, xxx, patternmask, colourmask;
	pen_t fg, bg, fg0, bg0, pen;
	int nametbl_addr, patterntbl_addr, colourtbl_addr;

	patterntbl_addr = m_cont_reg[4] << 11;
	colourtbl_addr =  ((m_cont_reg[3] & 0xf8) << 6) + (m_cont_reg[10] << 14);
	#if 0
	colourmask = ((m_cont_reg[3] & 7) << 5) | 0x1f; /* cause a bug in Forth+ v1.0 on Geneve */
	#else
	colourmask = ((m_cont_reg[3] & 7) << 6) | 0x3f; /* verify! */
	#endif
	nametbl_addr = ((m_cont_reg[2] & 0xfc) << 10);
	patternmask = ((m_cont_reg[2] & 3) << 10) | 0x3ff; /* seems correct */

	fg = pen16(m_cont_reg[7] >> 4);
	bg = pen16(m_cont_reg[7] & 15);
	fg0 = pen16(m_cont_reg[12] >> 4);
	bg0 = pen16(m_cont_reg[12] & 15);

	name = (line/8)*80;

	xxx = (m_offset_x + 8) * 2;
	pen = pen16(m_cont_reg[7] & 0x0f);
	while (xxx--) *ln++ = pen;

	for (x=0;x<80;x++)
	{
		charcode = m_vram_space->read_byte(nametbl_addr + (name&patternmask));
		if (m_blink)
		{
			pattern = m_vram_space->read_byte(colourtbl_addr + ((name/8)&colourmask));
			if (pattern & (0x80 >> (name & 7) ) )
			{
				pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode * 8) +
					((line + m_cont_reg[23]) & 7)));

				*ln++ = (pattern & 0x80) ? fg0 : bg0;
				*ln++ = (pattern & 0x40) ? fg0 : bg0;
				*ln++ = (pattern & 0x20) ? fg0 : bg0;
				*ln++ = (pattern & 0x10) ? fg0 : bg0;
				*ln++ = (pattern & 0x08) ? fg0 : bg0;
				*ln++ = (pattern & 0x04) ? fg0 : bg0;

				name++;
				continue;
			}
		}

		pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode * 8) +
			((line + m_cont_reg[23]) & 7)));

		*ln++ = (pattern & 0x80) ? fg : bg;
		*ln++ = (pattern & 0x40) ? fg : bg;
		*ln++ = (pattern & 0x20) ? fg : bg;
		*ln++ = (pattern & 0x10) ? fg : bg;
		*ln++ = (pattern & 0x08) ? fg : bg;
		*ln++ = (pattern & 0x04) ? fg : bg;

		name++;
	}

	xxx = (16 - m_offset_x + 8) * 2;
	while (xxx--) *ln++ = pen;
}

void v99x8_device::mode_multi(uint32_t *ln, int line)
{
	int nametbl_addr, patterntbl_addr, colour;
	int name, line2, x, xx;
	pen_t pen, pen_bg;

	nametbl_addr = (m_cont_reg[2] << 10);
	patterntbl_addr = (m_cont_reg[4] << 11);

	line2 = (line - m_cont_reg[23]) & 255;
	name = (line2/8)*32;

	pen_bg = pen16(m_cont_reg[7] & 0x0f);
	xx = m_offset_x * 2;
	while (xx--) *ln++ = pen_bg;

	for (x=0;x<32;x++)
	{
		colour = m_vram_space->read_byte(patterntbl_addr + (m_vram_space->read_byte(nametbl_addr + name) * 8) + ((line2/4)&7));
		pen = pen16(colour >> 4);
		/* eight pixels */
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		pen = pen16(colour & 15);
		/* eight pixels */
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		*ln++ = pen;
		name++;
	}

	xx = (16 - m_offset_x) * 2;
	while (xx--) *ln++ = pen_bg;
}

void v99x8_device::mode_graphic1(uint32_t *ln, int line)
{
	pen_t fg, bg, pen;
	int nametbl_addr, patterntbl_addr, colourtbl_addr;
	int pattern, x, xx, line2, name, charcode, colour, xxx;

	nametbl_addr = (m_cont_reg[2] << 10);
	colourtbl_addr = (m_cont_reg[3] << 6) + (m_cont_reg[10] << 14);
	patterntbl_addr = (m_cont_reg[4] << 11);

	line2 = (line - m_cont_reg[23]) & 255;

	name = (line2/8)*32;

	pen = pen16(m_cont_reg[7] & 0x0f);
	xxx = m_offset_x * 2;
	while (xxx--) *ln++ = pen;

	for (x=0;x<32;x++)
	{
		charcode = m_vram_space->read_byte(nametbl_addr + name);
		colour = m_vram_space->read_byte(colourtbl_addr + charcode/8);
		fg = pen16(colour >> 4);
		bg = pen16(colour & 15);
		pattern = m_vram_space->read_byte(patterntbl_addr + (charcode * 8 + (line2 & 7)));

		for (xx=0;xx<8;xx++)
		{
			*ln++ = (pattern & 0x80) ? fg : bg;
			*ln++ = (pattern & 0x80) ? fg : bg;
			pattern <<= 1;
		}
		name++;
	}

	xx = (16 - m_offset_x) * 2;
	while (xx--) *ln++ = pen;
}

void v99x8_device::mode_graphic23(uint32_t *ln, int line)
{
	pen_t fg, bg, pen;
	int nametbl_addr, patterntbl_addr, colourtbl_addr;
	int pattern, x, xx, line2, name, charcode,
	colour, colourmask, patternmask, xxx;

	colourmask = ((m_cont_reg[3] & 0x7f) * 8) | 7;
	patternmask = ((m_cont_reg[4] & 0x03) * 256) | 0xff;

	nametbl_addr =  (m_cont_reg[2] << 10);
	colourtbl_addr =  ((m_cont_reg[3] & 0x80) << 6) + (m_cont_reg[10] << 14);
	patterntbl_addr = ((m_cont_reg[4] & 0x3c) << 11);

	line2 = (line + m_cont_reg[23]) & 255;
	name = (line2/8)*32;

	pen = pen16(m_cont_reg[7] & 0x0f);
	xxx = m_offset_x * 2;
	while (xxx--) *ln++ = pen;

	for (x=0;x<32;x++)
	{
		charcode = m_vram_space->read_byte(nametbl_addr + name) + (line2&0xc0)*4;
		colour = m_vram_space->read_byte(colourtbl_addr + ((charcode&colourmask)*8+(line2&7)));
		pattern = m_vram_space->read_byte(patterntbl_addr + ((charcode&patternmask)*8+(line2&7)));
		fg = pen16(colour >> 4);
		bg = pen16(colour & 15);
		for (xx=0;xx<8;xx++)
		{
			*ln++ = (pattern & 0x80) ? fg : bg;
			*ln++ = (pattern & 0x80) ? fg : bg;
			pattern <<= 1;
		}
		name++;
	}

	xx = (16 - m_offset_x) * 2;
	while (xx--) *ln++ = pen;
}

void v99x8_device::mode_graphic4(uint32_t *ln, int line)
{
	int nametbl_addr, colour;
	int line2, linemask, x, xx;
	pen_t pen, pen_bg;

	linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;

	line2 = ((line + m_cont_reg[23]) & linemask) & 255;

	nametbl_addr = ((m_cont_reg[2] & 0x40) << 10) + line2 * 128;
	if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
		nametbl_addr += 0x8000;

	pen_bg = pen16(m_cont_reg[7] & 0x0f);
	xx = m_offset_x * 2;
	while (xx--) *ln++ = pen_bg;

	for (x=0;x<128;x++)
	{
		colour = m_vram_space->read_byte(nametbl_addr++);
		pen = pen16(colour >> 4);
		*ln++ = pen;
		*ln++ = pen;
		pen = pen16(colour & 15);
		*ln++ = pen;
		*ln++ = pen;
	}

	xx = (16 - m_offset_x) * 2;
	while (xx--) *ln++ = pen_bg;
}

void v99x8_device::mode_graphic5(uint32_t *ln, int line)
{
	int nametbl_addr, colour;
	int line2, linemask, x, xx;
	pen_t pen_bg0[4];
	pen_t pen_bg1[4];

	linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;

	line2 = ((line + m_cont_reg[23]) & linemask) & 255;

	nametbl_addr = ((m_cont_reg[2] & 0x40) << 10) + line2 * 128;
	if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
		nametbl_addr += 0x8000;

	pen_bg1[0] = pen16(m_cont_reg[7] & 0x03);
	pen_bg0[0] = pen16((m_cont_reg[7] >> 2) & 0x03);

	xx = m_offset_x;
	while (xx--) { *ln++ = pen_bg0[0]; *ln++ = pen_bg1[0]; }

	x = (m_cont_reg[8] & 0x20) ? 0 : 1;

	for (;x<4;x++)
	{
		pen_bg0[x] = pen16(x);
		pen_bg1[x] = pen16(x);
	}

	for (x=0;x<128;x++)
	{
		colour = m_vram_space->read_byte(nametbl_addr++);

		*ln++ = pen_bg0[colour>>6];
		*ln++ = pen_bg1[(colour>>4)&3];
		*ln++ = pen_bg0[(colour>>2)&3];
		*ln++ = pen_bg1[(colour&3)];
	}

	pen_bg1[0] = pen16(m_cont_reg[7] & 0x03);
	pen_bg0[0] = pen16((m_cont_reg[7] >> 2) & 0x03);
	xx = 16 - m_offset_x;
	while (xx--) { *ln++ = pen_bg0[0]; *ln++ = pen_bg1[0]; }
}

void v99x8_device::mode_graphic6(uint32_t *ln, int line)
{
	uint8_t colour;
	int line2, linemask, x, xx, nametbl_addr;
	pen_t pen_bg, fg0;
	pen_t fg1;

	linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;

	line2 = ((line + m_cont_reg[23]) & linemask) & 255;

	nametbl_addr = line2 << 8 ;
	if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
		nametbl_addr += 0x10000;

	pen_bg = pen16(m_cont_reg[7] & 0x0f);
	xx = m_offset_x * 2;
	while (xx--) *ln++ = pen_bg;

	if (m_cont_reg[2] & 0x40)
	{
		for (x=0;x<32;x++)
		{
			nametbl_addr++;
			colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			fg0 = pen16(colour >> 4);
			fg1 = pen16(colour & 15);
			*ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
			*ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
			*ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
			*ln++ = fg0; *ln++ = fg1; *ln++ = fg0; *ln++ = fg1;
			nametbl_addr += 7;
		}
	}
	else
	{
		for (x=0;x<256;x++)
		{
			colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			*ln++ = pen16(colour >> 4);
			*ln++ = pen16(colour & 15);
			nametbl_addr++;
		}
	}

	xx = (16 - m_offset_x) * 2;
	while (xx--) *ln++ = pen_bg;
}

void v99x8_device::mode_graphic7(uint32_t *ln, int line)
{
	uint8_t colour;
	int line2, linemask, x, xx, nametbl_addr;
	pen_t pen, pen_bg;

	linemask = ((m_cont_reg[2] & 0x1f) << 3) | 7;

	line2 = ((line + m_cont_reg[23]) & linemask) & 255;

	nametbl_addr = line2 << 8;
	if ( (m_cont_reg[2] & 0x20) && v9938_second_field() )
		nametbl_addr += 0x10000;

	pen_bg = pen256(m_cont_reg[7]);
	xx = m_offset_x * 2;
	while (xx--) *ln++ = pen_bg;

	if ((m_v9958_sp_mode & 0x18) == 0x08) // v9958 screen 12, puzzle star title screen
	{
		for (x=0;x<64;x++)
		{
			int colour[4];
			int ind;

			colour[0] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			nametbl_addr++;
			colour[1] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			nametbl_addr++;
			colour[2] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			nametbl_addr++;
			colour[3] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));

			ind = (colour[0] & 7) << 11 | (colour[1] & 7) << 14 |
			(colour[2] & 7) << 5 | (colour[3] & 7) << 8;

			*ln++ = s_pal_indYJK[ind | ((colour[0] >> 3) & 31)];
			*ln++ = s_pal_indYJK[ind | ((colour[0] >> 3) & 31)];

			*ln++ = s_pal_indYJK[ind | ((colour[1] >> 3) & 31)];
			*ln++ = s_pal_indYJK[ind | ((colour[1] >> 3) & 31)];

			*ln++ = s_pal_indYJK[ind | ((colour[2] >> 3) & 31)];
			*ln++ = s_pal_indYJK[ind | ((colour[2] >> 3) & 31)];

			*ln++ = s_pal_indYJK[ind | ((colour[3] >> 3) & 31)];
			*ln++ = s_pal_indYJK[ind | ((colour[3] >> 3) & 31)];

			nametbl_addr++;
		}
	}
	else if ((m_v9958_sp_mode & 0x18) == 0x18) // v9958 screen 10/11, puzzle star & sexy boom gameplay
	{
		for (x=0;x<64;x++)
		{
			int colour[4];
			int ind;

			colour[0] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			nametbl_addr++;
			colour[1] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			nametbl_addr++;
			colour[2] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			nametbl_addr++;
			colour[3] = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));

			ind = (colour[0] & 7) << 11 | (colour[1] & 7) << 14 |
			(colour[2] & 7) << 5 | (colour[3] & 7) << 8;

			*ln++ = colour[0] & 8 ? pen16(colour[0] >> 4) : s_pal_indYJK[ind | ((colour[0] >> 3) & 30)];
			*ln++ = colour[0] & 8 ? pen16(colour[0] >> 4) : s_pal_indYJK[ind | ((colour[0] >> 3) & 30)];

			*ln++ = colour[1] & 8 ? pen16(colour[1] >> 4) : s_pal_indYJK[ind | ((colour[1] >> 3) & 30)];
			*ln++ = colour[1] & 8 ? pen16(colour[1] >> 4) : s_pal_indYJK[ind | ((colour[1] >> 3) & 30)];

			*ln++ = colour[2] & 8 ? pen16(colour[2] >> 4) : s_pal_indYJK[ind | ((colour[2] >> 3) & 30)];
			*ln++ = colour[2] & 8 ? pen16(colour[2] >> 4) : s_pal_indYJK[ind | ((colour[2] >> 3) & 30)];

			*ln++ = colour[3] & 8 ? pen16(colour[3] >> 4) : s_pal_indYJK[ind | ((colour[3] >> 3) & 30)];
			*ln++ = colour[3] & 8 ? pen16(colour[3] >> 4) : s_pal_indYJK[ind | ((colour[3] >> 3) & 30)];

			nametbl_addr++;
		}
	}
	else if (m_cont_reg[2] & 0x40)
	{
		for (x=0;x<32;x++)
		{
			nametbl_addr++;
			colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			pen = pen256(colour);
			*ln++ = pen; *ln++ = pen;
			*ln++ = pen; *ln++ = pen;
			*ln++ = pen; *ln++ = pen;
			*ln++ = pen; *ln++ = pen;
			*ln++ = pen; *ln++ = pen;
			*ln++ = pen; *ln++ = pen;
			*ln++ = pen; *ln++ = pen;
			*ln++ = pen; *ln++ = pen;
			nametbl_addr++;
		}
	}
	else
	{
		for (x=0;x<256;x++)
		{
			colour = m_vram_space->read_byte(((nametbl_addr&1) << 16) | (nametbl_addr>>1));
			pen = pen256(colour);
			*ln++ = pen;
			*ln++ = pen;
			nametbl_addr++;
		}
	}

	xx = (16 - m_offset_x) * 2;
	while (xx--) *ln++ = pen_bg;
}

void v99x8_device::mode_unknown(uint32_t *ln, int line)
{
	pen_t fg, bg;
	int x;

	fg = pen16(m_cont_reg[7] >> 4);
	bg = pen16(m_cont_reg[7] & 15);

	x = m_offset_x * 2;
	while (x--) *ln++ = bg;

	x = 512;
	while (x--) *ln++ = fg;

	x = (16 - m_offset_x) * 2;
	while (x--) *ln++ = bg;
}

void v99x8_device::default_draw_sprite(uint32_t *ln, uint8_t *col)
{
	int i;
	ln += m_offset_x * 2;

	for (i=0;i<256;i++)
	{
		if (col[i] & 0x80)
		{
			*ln++ = pen16(col[i] & 0x0f);
			*ln++ = pen16(col[i] & 0x0f);
		}
		else
		{
			ln += 2;
		}
	}
}

void v99x8_device::graphic5_draw_sprite(uint32_t *ln, uint8_t *col)
{
	int i;
	ln += m_offset_x * 2;

	for (i=0;i<256;i++)
	{
		if (col[i] & 0x80)
		{
			*ln++ = pen16((col[i] >> 2) & 0x03);
			*ln++ = pen16(col[i] & 0x03);
		}
		else
		{
			ln += 2;
		}
	}
}


void v99x8_device::graphic7_draw_sprite(uint32_t *ln, uint8_t *col)
{
	static const uint16_t g7_ind16[16] = {
		0, 2, 192, 194, 48, 50, 240, 242,
	482, 7, 448, 455, 56, 63, 504, 511  };
	int i;

	ln += m_offset_x * 2;

	for (i=0;i<256;i++)
	{
		if (col[i] & 0x80)
		{
			rgb_t color = rgb_t(pal3bit(g7_ind16[col[i] & 0x0f] >> 6), pal3bit(g7_ind16[col[i] & 0x0f] >> 3), pal3bit(g7_ind16[col[i] & 0x0f]));
			*ln++ = uint32_t(color);
			*ln++ = uint32_t(color);
		}
		else
		{
			ln += 2;
		}
	}
}


void v99x8_device::sprite_mode1 (int line, uint8_t *col)
{
	int attrtbl_addr, patterntbl_addr, pattern_addr;
	int x, y, p, height, c, p2, i, n, pattern;

	memset(col, 0, 256);

	// are sprites disabled?
	if (m_cont_reg[8] & 0x02) return;

	attrtbl_addr = (m_cont_reg[5] << 7) + (m_cont_reg[11] << 15);
	patterntbl_addr = (m_cont_reg[6] << 11);

	// 16x16 or 8x8 sprites
	height = (m_cont_reg[1] & 2) ? 16 : 8;
	// magnified sprites (zoomed)
	if (m_cont_reg[1] & 1) height *= 2;

	p2 = p = 0;
	while (1)
	{
		y = m_vram_space->read_byte(attrtbl_addr);
		if (y == 208) break;
		y = (y - m_cont_reg[23]) & 255;
		if (y > 208)
			y = -(~y&255);
		else
			y++;

		// if sprite in range, has to be drawn
		if ( (line >= y) && (line  < (y + height) ) )
		{
			if (p2 == 4)
			{
				// max maximum sprites per line!
				if ( !(m_stat_reg[0] & 0x40) )
					m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | 0x40 | p;

				break;
			}
			// get x
			x = m_vram_space->read_byte(attrtbl_addr + 1);
			if (m_vram_space->read_byte(attrtbl_addr + 3) & 0x80) x -= 32;

			// get pattern
			pattern = m_vram_space->read_byte(attrtbl_addr + 2);
			if (m_cont_reg[1] & 2)
				pattern &= 0xfc;
			n = line - y;
			pattern_addr = patterntbl_addr + pattern * 8 + ((m_cont_reg[1] & 1) ? n/2  : n);
			pattern = (m_vram_space->read_byte(pattern_addr) << 8) | m_vram_space->read_byte(pattern_addr+16);

			// get colour
			c = m_vram_space->read_byte(attrtbl_addr + 3) & 0x0f;

			// draw left part
			n = 0;
			while (1)
			{
				if (n == 0) pattern = m_vram_space->read_byte(pattern_addr);
				else if ( (n == 1) && (m_cont_reg[1] & 2) ) pattern = m_vram_space->read_byte(pattern_addr + 16);
				else break;

				n++;

				for (i=0;i<8;i++)
				{
					if (pattern & 0x80)
					{
						if ( (x >= 0) && (x < 256) )
						{
							if (col[x] & 0x40)
							{
								// we have a collision!
								if (p2 < 4)
									m_stat_reg[0] |= 0x20;
							}
							if ( !(col[x] & 0x80) )
							{
								if (c || (m_cont_reg[8] & 0x20) )
									col[x] |= 0xc0 | c;
								else
									col[x] |= 0x40;
							}

							// if zoomed, draw another pixel
							if (m_cont_reg[1] & 1)
							{
								if (col[x+1] & 0x40)
								{
									// we have a collision!
									if (p2 < 4)
										m_stat_reg[0] |= 0x20;
								}
								if ( !(col[x+1] & 0x80) )
								{
									if (c || (m_cont_reg[8] & 0x20) )
										col[x+1] |= 0xc0 | c;
									else
										col[x+1] |= 0x80;
								}
							}
						}
					}
					if (m_cont_reg[1] & 1) x += 2; else x++;
					pattern <<= 1;
				}
			}

			p2++;
		}

		if (p >= 31) break;
		p++;
		attrtbl_addr += 4;
	}

	if ( !(m_stat_reg[0] & 0x40) )
		m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | p;
}

void v99x8_device::sprite_mode2 (int line, uint8_t *col)
{
	int attrtbl_addr, patterntbl_addr, pattern_addr, colourtbl_addr;
	int x, i, y, p, height, c, p2, n, pattern, colourmask, first_cc_seen;

	memset(col, 0, 256);

	// are sprites disabled?
	if (m_cont_reg[8] & 0x02) return;

	attrtbl_addr = ( (m_cont_reg[5] & 0xfc) << 7) + (m_cont_reg[11] << 15);
	colourtbl_addr =  ( (m_cont_reg[5] & 0xf8) << 7) + (m_cont_reg[11] << 15);
	patterntbl_addr = (m_cont_reg[6] << 11);
	colourmask = ( (m_cont_reg[5] & 3) << 3) | 0x7; // check this!

	// 16x16 or 8x8 sprites
	height = (m_cont_reg[1] & 2) ? 16 : 8;
	// magnified sprites (zoomed)
	if (m_cont_reg[1] & 1) height *= 2;

	p2 = p = first_cc_seen = 0;
	while (1)
	{
		y = vram_read(attrtbl_addr);
		if (y == 216) break;
		y = (y - m_cont_reg[23]) & 255;
		if (y > 216)
			y = -(~y&255);
		else
			y++;

		// if sprite in range, has to be drawn
		if ( (line >= y) && (line  < (y + height) ) )
		{
			if (p2 == 8)
			{
				// max maximum sprites per line!
				if ( !(m_stat_reg[0] & 0x40) )
					m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | 0x40 | p;

				break;
			}

			n = line - y; if (m_cont_reg[1] & 1) n /= 2;
			// get colour
			c = vram_read(colourtbl_addr + (((p&colourmask)*16) + n));

			// don't draw all sprite with CC set before any sprites
			// with CC = 0 are seen on this line
			if (c & 0x40)
			{
				if (!first_cc_seen)
					goto skip_first_cc_set;
			}
			else
				first_cc_seen = 1;

			// get pattern
			pattern = vram_read(attrtbl_addr + 2);
			if (m_cont_reg[1] & 2)
				pattern &= 0xfc;
			pattern_addr = patterntbl_addr + pattern * 8 + n;
			pattern = (vram_read(pattern_addr) << 8) | vram_read(pattern_addr + 16);

			// get x
			x = vram_read(attrtbl_addr + 1);
			if (c & 0x80) x -= 32;

			n = (m_cont_reg[1] & 2) ? 16 : 8;
			while (n--)
			{
				for (i=0;i<=(m_cont_reg[1] & 1);i++)
				{
					if ( (x >= 0) && (x < 256) )
					{
						if ( (pattern & 0x8000) && !(col[x] & 0x10) )
						{
							if ( (c & 15) || (m_cont_reg[8] & 0x20) )
							{
								if ( !(c & 0x40) )
								{
									if (col[x] & 0x20) col[x] |= 0x10;
									else
										col[x] |= 0x20 | (c & 15);
								}
								else
									col[x] |= c & 15;

								col[x] |= 0x80;
							}
						}
						else
						{
							if ( !(c & 0x40) && (col[x] & 0x20) )
								col[x] |= 0x10;
						}

						if ( !(c & 0x60) && (pattern & 0x8000) )
						{
							if (col[x] & 0x40)
							{
								// sprite collision!
								if (p2 < 8)
									m_stat_reg[0] |= 0x20;
							}
							else
								col[x] |= 0x40;
						}

						x++;
					}
				}

				pattern <<= 1;
			}

		skip_first_cc_set:
			p2++;
		}

		if (p >= 31) break;
		p++;
		attrtbl_addr += 4;
	}

	if ( !(m_stat_reg[0] & 0x40) )
		m_stat_reg[0] = (m_stat_reg[0] & 0xa0) | p;
}


const v99x8_device::v99x8_mode v99x8_device::s_modes[] = {
	{ 0x02,
		&v99x8_device::mode_text1,
		&v99x8_device::default_border,
		nullptr,
		nullptr
	},
	{ 0x01,
		&v99x8_device::mode_multi,
		&v99x8_device::default_border,
		&v99x8_device::sprite_mode1,
		&v99x8_device::default_draw_sprite
	},
	{ 0x00,
		&v99x8_device::mode_graphic1,
		&v99x8_device::default_border,
		&v99x8_device::sprite_mode1,
		&v99x8_device::default_draw_sprite
	},
	{ 0x04,
		&v99x8_device::mode_graphic23,
		&v99x8_device::default_border,
		&v99x8_device::sprite_mode1,
		&v99x8_device::default_draw_sprite
	},
	{ 0x08,
		&v99x8_device::mode_graphic23,
		&v99x8_device::default_border,
		&v99x8_device::sprite_mode2,
		&v99x8_device::default_draw_sprite
	},
	{ 0x0c,
		&v99x8_device::mode_graphic4,
		&v99x8_device::default_border,
		&v99x8_device::sprite_mode2,
		&v99x8_device::default_draw_sprite
	},
	{ 0x10,
		&v99x8_device::mode_graphic5,
		&v99x8_device::graphic5_border,
		&v99x8_device::sprite_mode2,
		&v99x8_device::graphic5_draw_sprite
	},
	{ 0x14,
		&v99x8_device::mode_graphic6,
		&v99x8_device::default_border,
		&v99x8_device::sprite_mode2,
		&v99x8_device::default_draw_sprite
	},
	{ 0x1c,
		&v99x8_device::mode_graphic7,
		&v99x8_device::graphic7_border,
		&v99x8_device::sprite_mode2,
		&v99x8_device::graphic7_draw_sprite
	},
	{ 0x0a,
		&v99x8_device::mode_text2,
		&v99x8_device::default_border,
		nullptr,
		nullptr
	},
	{ 0xff,
		&v99x8_device::mode_unknown,
		&v99x8_device::default_border,
		nullptr,
		nullptr
	}
};

void v99x8_device::set_mode()
{
	int n,i;

	n = (((m_cont_reg[0] & 0x0e) << 1) | ((m_cont_reg[1] & 0x18) >> 3));
	for (i=0;;i++)
	{
		if ( (s_modes[i].m == n) || (s_modes[i].m == 0xff) ) break;
	}

	// MZ: What happens when the mode is changed during command execution?
	// This is left unspecified in the docs. On a Geneve, experiments showed
	// that the command is not aborted (the CE flag is still 1) and runs for
	// about 90% of the nominal execution time, but VRAM is only correctly
	// filled up to the time of switching, and after that, isolated locations
	// within the normally affected area are changed, but inconsistently.
	// Obviously, it depends on the time when the switch happened.
	// This behavior occurs on every switch from a mode Graphics4 and higher
	// to another mode, e.g. also from Graphics7 to Graphics6.
	// Due to the lack of more information, we simply abort the command.

	if (m_vdp_engine && m_mode != i)
	{
		LOGMASKED(LOG_WARN, "Command aborted due to mode change\n");
		m_vdp_engine = nullptr;
		m_stat_reg[2] &= 0xFE;
	}

	m_mode = i;
}

void v99x8_device::refresh_32(int line)
{
	bool double_lines = false;
	uint8_t col[256];
	uint32_t *ln, *ln2 = nullptr;

	if (m_cont_reg[9] & 0x08)
	{
		ln = &m_bitmap.pix32(m_scanline*2+((m_stat_reg[2]>>1)&1));
	}
	else
	{
		ln = &m_bitmap.pix32(m_scanline*2);
		ln2 = &m_bitmap.pix32(m_scanline*2+1);
		double_lines = true;
	}

	if ( !(m_cont_reg[1] & 0x40) || (m_stat_reg[2] & 0x40) )
	{
		(this->*s_modes[m_mode].border_32)(ln);
	}
	else
	{
		(this->*s_modes[m_mode].visible_32)(ln, line);
		if (s_modes[m_mode].sprites)
		{
			(this->*s_modes[m_mode].sprites)(line, col);
			(this->*s_modes[m_mode].draw_sprite_32)(ln, col);
		}
	}

	if (double_lines)
		memcpy(ln2, ln, (512 + 32) * sizeof(*ln));
}

void v99x8_device::refresh_line(int line)
{
	pen_t ind16, ind256;

	ind16 = pen16(0);
	ind256 = pen256(0);

	if ( !(m_cont_reg[8] & 0x20) && (m_mode != V9938_MODE_GRAPHIC5) )
	{
		set_pen16(0, pen16(m_cont_reg[7] & 0x0f));
		set_pen256(0, pen256(m_cont_reg[7]));
	}

	refresh_32(line);

	if ( !(m_cont_reg[8] & 0x20) && (m_mode != V9938_MODE_GRAPHIC5) )
	{
		set_pen16(0, ind16);
		set_pen256(0, ind256);
	}
}

/*

From: awulms@inter.nl.net (Alex Wulms)
*** About the HR/VR topic: this is how it works according to me:

*** HR:
HR is very straightforward:
-HR=1 during 'display time'
-HR=0 during 'horizontal border, horizontal retrace'
I have put 'display time' and 'horizontal border, horizontal retrace' between
quotes because HR does not only flip between 0 and 1 during the display of
the 192/212 display lines, but also during the vertical border and during the
vertical retrace.

*** VR:
VR is a little bit tricky
-VR always gets set to 0 when the VDP starts with display line 0
-VR gets set to 1 when the VDP reaches display line (192 if LN=0) or (212 if
LN=1)
-The VDP displays contents of VRAM as long as VR=0

As a consequence of this behaviour, it is possible to program the famous
overscan trick, where VRAM contents is shown in the borders:
Generate an interrupt at line 230 (or so) and on this interrupt: set LN=1
Generate an interrupt at line 200 (or so) and on this interrupt: set LN=0
Repeat the above two steps

*** The top/bottom border contents during overscan:
On screen 0:
1) The VDP keeps increasing the name table address pointer during bottom
border, vertical retrace and top border
2) The VDP resets the name table address pointer when the first display line
is reached

On the other screens:
1) The VDP keeps increasing the name table address pointer during the bottom
border
2) The VDP resets the name table address pointer such that the top border
contents connects up with the first display line. E.g., when the top border
is 26 lines high, the VDP will take:
'logical'      vram line
TOPB000  256-26
...
TOPB025  256-01
DISPL000 000
...
DISPL211 211
BOTB000  212
...
BOTB024  236



*** About the horizontal interrupt

All relevant definitions on a row:
-FH: Bit 0 of status register 1
-IE1: Bit 4 of mode register 0
-IL: Line number in mode register 19
-DL: The line that the VDP is going to display (corrected for vertical scroll)
-IRQ: Interrupt request line of VDP to Z80

At the *start* of every new line (display, bottom border, part of vertical
display), the VDP does:
-FH = (FH && IE1) || (IL==DL)

After reading of status register 1 by the CPU, the VDP does:
-FH = 0

Furthermore, the following is true all the time:
-IRQ = FH && IE1

The resulting behaviour:
When IE1=0:
-FH will be set as soon as display of line IL starts
-FH will be reset as soon as status register 1 is read
-FH will be reset as soon as the next display line is reached

When IE=1:
-FH and IRQ will be set as soon as display line IL is reached
-FH and IRQ will be reset as soon as status register 1 is read

Another subtile result:
If, while FH and IRQ are set, IE1 gets reset, the next happens:
-IRQ is reset immediately (since IRQ is always FH && IE1)
-FH will be reset as soon as display of the next line starts (unless the next
line is line IL)


*** About the vertical interrupt:
Another relevant definition:
-FV: Bit 7 of status register 0
-IE0: Bit 5 of mode register 1

I only know for sure the behaviour when IE0=1:
-FV and IRQ will be set as soon as VR changes from 0 to 1
-FV and IRQ will be reset as soon as status register 0 is read

A consequence is that NO vertical interrupts will be generated during the
overscan trick, described in the VR section above.

I do not know the behaviour of FV when IE0=0. That is the part that I still
have to test.
*/

void v99x8_device::interrupt_start_vblank()
{
	#if 0
	if (machine.input().code_pressed (KEYCODE_D) )
	{
		for (i=0;i<24;i++) osd_printf_debug ("R#%d = %02x\n", i, m_cont_reg[i]);
	}
	#endif

	// at every frame, vdp switches fields
	m_stat_reg[2] = (m_stat_reg[2] & 0xfd) | (~m_stat_reg[2] & 2);

	// color blinking
	if (!(m_cont_reg[13] & 0xf0))
		m_blink = 0;
	else if (!(m_cont_reg[13] & 0x0f))
		m_blink = 1;
	else
	{
		// both on and off counter are non-zero: timed blinking
		if (m_blink_count)
			m_blink_count--;
		if (!m_blink_count)
		{
			m_blink = !m_blink;
			if (m_blink)
				m_blink_count = (m_cont_reg[13] >> 4) * 10;
			else
				m_blink_count = (m_cont_reg[13] & 0x0f) * 10;
		}
	}
}

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

Command unit

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

/*************************************************************/
/** Completely rewritten by Alex Wulms:                     **/
/**  - VDP Command execution 'in parallel' with CPU         **/
/**  - Corrected behaviour of VDP commands                  **/
/**  - Made it easier to implement correct S7/8 mapping     **/
/**    by concentrating VRAM access in one single place     **/
/**  - Made use of the 'in parallel' VDP command exec       **/
/**    and correct timing. You must call the function       **/
/**    LoopVDP() from LoopZ80 in MSX.c. You must call it    **/
/**    exactly 256 times per screen refresh.                **/
/** Started on       : 11-11-1999                           **/
/** Beta release 1 on:  9-12-1999                           **/
/** Beta release 2 on: 20-01-2000                           **/
/**  - Corrected behaviour of VRM <-> Z80 transfer          **/
/**  - Improved performance of the code                     **/
/** Public release 1.0: 20-04-2000                          **/
/*************************************************************/

#define VDP_VRMP5(MX, X, Y) ((!MX) ? (((Y&1023)<<7) + ((X&255)>>1)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&255)>>1)))
#define VDP_VRMP6(MX, X, Y) ((!MX) ? (((Y&1023)<<7) + ((X&511)>>2)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&511)>>2)))
//#define VDP_VRMP7(MX, X, Y) ((!MX) ? (((Y&511)<<8) + ((X&511)>>1)) : (EXPMEM_OFFSET + ((Y&255)<<8) + ((X&511)>>1)))
#define VDP_VRMP7(MX, X, Y) ((!MX) ? (((X&2)<<15) + ((Y&511)<<7) + ((X&511)>>2)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X&511)>>2))/*(EXPMEM_OFFSET + ((Y&255)<<8) + ((X&511)>>1))*/)
//#define VDP_VRMP8(MX, X, Y) ((!MX) ? (((Y&511)<<8) + (X&255)) : (EXPMEM_OFFSET + ((Y&255)<<8) + (X&255)))
#define VDP_VRMP8(MX, X, Y) ((!MX) ? (((X&1)<<16) + ((Y&511)<<7) + ((X>>1)&127)) : (EXPMEM_OFFSET + ((Y&511)<<7) + ((X>>1)&127))/*(EXPMEM_OFFSET + ((Y&255)<<8) + (X&255))*/)

#define VDP_VRMP(M, MX, X, Y) VDPVRMP(M, MX, X, Y)
#define VDP_POINT(M, MX, X, Y) VDPpoint(M, MX, X, Y)
#define VDP_PSET(M, MX, X, Y, C, O) VDPpset(M, MX, X, Y, C, O)

#define CM_ABRT  0x0
#define CM_POINT 0x4
#define CM_PSET  0x5
#define CM_SRCH  0x6
#define CM_LINE  0x7
#define CM_LMMV  0x8
#define CM_LMMM  0x9
#define CM_LMCM  0xA
#define CM_LMMC  0xB
#define CM_HMMV  0xC
#define CM_HMMM  0xD
#define CM_YMMM  0xE
#define CM_HMMC  0xF

/*************************************************************
Many VDP commands are executed in some kind of loop but
essentially, there are only a few basic loop structures
that are re-used. We define the loop structures that are
re-used here so that they have to be entered only once
*************************************************************/
#define pre_loop \
while ((cnt-=delta) > 0) {
	#define post_loop \
}

// Loop over DX, DY
#define post__x_y(MX) \
if (!--ANX || ((ADX+=TX)&MX)) { \
	if (!(--NY&1023) || (DY+=TY)==-1) \
		break; \
	else { \
		ADX=DX; \
		ANX=NX; \
	} \
} \
post_loop

// Loop over DX, SY, DY
#define post__xyy(MX) \
if ((ADX+=TX)&MX) { \
	if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \
		break; \
	else \
		ADX=DX; \
} \
post_loop

// Loop over SX, DX, SY, DY
#define post_xxyy(MX) \
if (!--ANX || ((ASX+=TX)&MX) || ((ADX+=TX)&MX)) { \
	if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \
		break; \
	else { \
		ASX=SX; \
		ADX=DX; \
		ANX=NX; \
	} \
} \
post_loop

/*************************************************************/
/** Variables visible only in this module                   **/
/*************************************************************/
static const uint8_t Mask[4] = { 0x0F,0x03,0x0F,0xFF };
static const int  PPB[4]  = { 2,4,2,1 };
static const int  PPL[4]  = { 256,512,512,256 };

//  SprOn SprOn SprOf SprOf
//  ScrOf ScrOn ScrOf ScrOn
static const int srch_timing[8]={
	818, 1025,  818,  830, // ntsc
	696,  854,  696,  684  // pal
};
static const int line_timing[8]={
	1063, 1259, 1063, 1161,
	904,  1026, 904,  953
};
static const int hmmv_timing[8]={
	439,  549,  439,  531,
	366,  439,  366,  427
};
static const int lmmv_timing[8]={
	873,  1135, 873, 1056,
	732,  909,  732,  854
};
static const int ymmm_timing[8]={
	586,  952,  586,  610,
	488,  720,  488,  500
};
static const int hmmm_timing[8]={
	818,  1111, 818,  854,
	684,  879,  684,  708
};
static const int lmmm_timing[8]={
	1160, 1599, 1160, 1172,
	964,  1257, 964,  977
};

/** VDPVRMP() **********************************************/
/** Calculate addr of a pixel in vram                       **/
/*************************************************************/
inline int v99x8_device::VDPVRMP(uint8_t M,int MX,int X,int Y)
{
	switch(M)
	{
	case 0: return VDP_VRMP5(MX,X,Y);
	case 1: return VDP_VRMP6(MX,X,Y);
	case 2: return VDP_VRMP7(MX,X,Y);
	case 3: return VDP_VRMP8(MX,X,Y);
	}

	return 0;
}

/** VDPpoint5() ***********************************************/
/** Get a pixel on screen 5                                 **/
/*************************************************************/
inline uint8_t v99x8_device::VDPpoint5(int MXS, int SX, int SY)
{
	return (m_vram_space->read_byte(VDP_VRMP5(MXS, SX, SY)) >>
		(((~SX)&1)<<2)
		)&15;
}

/** VDPpoint6() ***********************************************/
/** Get a pixel on screen 6                                 **/
/*************************************************************/
inline uint8_t v99x8_device::VDPpoint6(int MXS, int SX, int SY)
{
	return (m_vram_space->read_byte(VDP_VRMP6(MXS, SX, SY)) >>
		(((~SX)&3)<<1)
		)&3;
}

/** VDPpoint7() ***********************************************/
/** Get a pixel on screen 7                                 **/
/*************************************************************/
inline uint8_t v99x8_device::VDPpoint7(int MXS, int SX, int SY)
{
	return (m_vram_space->read_byte(VDP_VRMP7(MXS, SX, SY)) >>
		(((~SX)&1)<<2)
		)&15;
}

/** VDPpoint8() ***********************************************/
/** Get a pixel on screen 8                                 **/
/*************************************************************/
inline uint8_t v99x8_device::VDPpoint8(int MXS, int SX, int SY)
{
	return m_vram_space->read_byte(VDP_VRMP8(MXS, SX, SY));
}

/** VDPpoint() ************************************************/
/** Get a pixel on a screen                                 **/
/*************************************************************/
inline uint8_t v99x8_device::VDPpoint(uint8_t SM, int MXS, int SX, int SY)
{
	switch(SM)
	{
	case 0: return VDPpoint5(MXS,SX,SY);
	case 1: return VDPpoint6(MXS,SX,SY);
	case 2: return VDPpoint7(MXS,SX,SY);
	case 3: return VDPpoint8(MXS,SX,SY);
	}

	return(0);
}

/** VDPpsetlowlevel() ****************************************/
/** Low level function to set a pixel on a screen           **/
/** Make it inline to make it fast                          **/
/*************************************************************/
inline void v99x8_device::VDPpsetlowlevel(int addr, uint8_t CL, uint8_t M, uint8_t OP)
{
	// If this turns out to be too slow, get a pointer to the address space
	// and work directly on it.
	uint8_t val = m_vram_space->read_byte(addr);
	switch (OP)
	{
	case 0: val = (val & M) | CL; break;
	case 1: val = val & (CL | M); break;
	case 2: val |= CL; break;
	case 3: val ^= CL; break;
	case 4: val = (val & M) | ~(CL | M); break;
	case 8: if (CL) val = (val & M) | CL; break;
	case 9: if (CL) val = val & (CL | M); break;
	case 10: if (CL) val |= CL; break;
	case 11:  if (CL) val ^= CL; break;
	case 12:  if (CL) val = (val & M) | ~(CL|M); break;
	default:
		LOGMASKED(LOG_WARN, "Invalid operation %d in pset\n", OP);
	}

	m_vram_space->write_byte(addr, val);
}

/** VDPpset5() ***********************************************/
/** Set a pixel on screen 5                                 **/
/*************************************************************/
inline void v99x8_device::VDPpset5(int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
{
	uint8_t SH = ((~DX)&1)<<2;
	VDPpsetlowlevel(VDP_VRMP5(MXD, DX, DY), CL << SH, ~(15<<SH), OP);
}

/** VDPpset6() ***********************************************/
/** Set a pixel on screen 6                                 **/
/*************************************************************/
inline void v99x8_device::VDPpset6(int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
{
	uint8_t SH = ((~DX)&3)<<1;

	VDPpsetlowlevel(VDP_VRMP6(MXD, DX, DY), CL << SH, ~(3<<SH), OP);
}

/** VDPpset7() ***********************************************/
/** Set a pixel on screen 7                                 **/
/*************************************************************/
inline void v99x8_device::VDPpset7(int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
{
	uint8_t SH = ((~DX)&1)<<2;

	VDPpsetlowlevel(VDP_VRMP7(MXD, DX, DY), CL << SH, ~(15<<SH), OP);
}

/** VDPpset8() ***********************************************/
/** Set a pixel on screen 8                                 **/
/*************************************************************/
inline void v99x8_device::VDPpset8(int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
{
	VDPpsetlowlevel(VDP_VRMP8(MXD, DX, DY), CL, 0, OP);
}

/** VDPpset() ************************************************/
/** Set a pixel on a screen                                 **/
/*************************************************************/
inline void v99x8_device::VDPpset(uint8_t SM, int MXD, int DX, int DY, uint8_t CL, uint8_t OP)
{
	switch (SM) {
	case 0: VDPpset5(MXD, DX, DY, CL, OP); break;
	case 1: VDPpset6(MXD, DX, DY, CL, OP); break;
	case 2: VDPpset7(MXD, DX, DY, CL, OP); break;
	case 3: VDPpset8(MXD, DX, DY, CL, OP); break;
	}
}

/** get_vdp_timing_value() **************************************/
/** Get timing value for a certain VDP command              **/
/*************************************************************/
int v99x8_device::get_vdp_timing_value(const int *timing_values)
{
	return(timing_values[((m_cont_reg[1]>>6)&1)|(m_cont_reg[8]&2)|((m_cont_reg[9]<<1)&4)]);
}

/** SrchEgine()** ********************************************/
/** Search a dot                                            **/
/*************************************************************/
void v99x8_device::srch_engine()
{
	int SX=m_mmc.SX;
	int SY=m_mmc.SY;
	int TX=m_mmc.TX;
	int ANX=m_mmc.ANX;
	uint8_t CL=m_mmc.CL;
	int MXD = m_mmc.MXD;
	int cnt;
	int delta;

	delta = get_vdp_timing_value(srch_timing);
	cnt = m_vdp_ops_count;

	#define post_srch(MX) \
	{ m_stat_reg[2]|=0x10; /* Border detected */ break; } \
	if ((SX+=TX) & MX) { m_stat_reg[2] &= 0xEF; /* Border not detected */ break; }
	switch (m_mode) {
	default:
	case V9938_MODE_GRAPHIC4: pre_loop if ((VDPpoint5(MXD, SX, SY)==CL) ^ANX)  post_srch(256) post_loop
			break;
	case V9938_MODE_GRAPHIC5: pre_loop if ((VDPpoint6(MXD, SX, SY)==CL) ^ANX)  post_srch(512) post_loop
			break;
	case V9938_MODE_GRAPHIC6: pre_loop if ((VDPpoint7(MXD, SX, SY)==CL) ^ANX)  post_srch(512) post_loop
			break;
	case V9938_MODE_GRAPHIC7: pre_loop if ((VDPpoint8(MXD, SX, SY)==CL) ^ANX)  post_srch(256) post_loop
			break;
	}

	if ((m_vdp_ops_count=cnt)>0) {
		// Command execution done
		m_stat_reg[2] &= 0xFE;
		m_vdp_engine = nullptr;
		// Update SX in VDP registers
		m_stat_reg[8] = SX & 0xFF;
		m_stat_reg[9] = (SX>>8) | 0xFE;
	}
	else {
		m_mmc.SX=SX;
	}
}

/** LineEgine()** ********************************************/
/** Draw a line                                             **/
/*************************************************************/
void v99x8_device::line_engine()
{
	int DX=m_mmc.DX;
	int DY=m_mmc.DY;
	int TX=m_mmc.TX;
	int TY=m_mmc.TY;
	int NX=m_mmc.NX;
	int NY=m_mmc.NY;
	int ASX=m_mmc.ASX;
	int ADX=m_mmc.ADX;
	uint8_t CL=m_mmc.CL;
	uint8_t LO=m_mmc.LO;
	int MXD = m_mmc.MXD;
	int cnt;
	int delta;

	delta = get_vdp_timing_value(line_timing);
	cnt = m_vdp_ops_count;

	#define post_linexmaj(MX) \
	DX+=TX; \
	if ((ASX-=NY)<0) { \
		ASX+=NX; \
		DY+=TY; \
	} \
	ASX&=1023; /* Mask to 10 bits range */\
	if (ADX++==NX || (DX&MX)) \
		break; \
	post_loop

	#define post_lineymaj(MX) \
	DY+=TY; \
	if ((ASX-=NY)<0) { \
		ASX+=NX; \
		DX+=TX; \
	} \
	ASX&=1023; /* Mask to 10 bits range */\
	if (ADX++==NX || (DX&MX)) \
		break; \
	post_loop

	if ((m_cont_reg[45]&0x01)==0)
		// X-Axis is major direction
	switch (m_mode) {
	default:
	case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, DX, DY, CL, LO); post_linexmaj(256)
		break;
	case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, DX, DY, CL, LO); post_linexmaj(512)
		break;
	case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, DX, DY, CL, LO); post_linexmaj(512)
		break;
	case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, DX, DY, CL, LO); post_linexmaj(256)
		break;
	}
	else
		// Y-Axis is major direction
	switch (m_mode) {
	default:
	case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, DX, DY, CL, LO); post_lineymaj(256)
		break;
	case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, DX, DY, CL, LO); post_lineymaj(512)
		break;
	case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, DX, DY, CL, LO); post_lineymaj(512)
		break;
	case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, DX, DY, CL, LO); post_lineymaj(256)
		break;
	}

	if ((m_vdp_ops_count=cnt)>0) {
		// Command execution done
		m_stat_reg[2]&=0xFE;
		m_vdp_engine=nullptr;
		m_cont_reg[38]=DY & 0xFF;
		m_cont_reg[39]=(DY>>8) & 0x03;
	}
	else {
		m_mmc.DX=DX;
		m_mmc.DY=DY;
		m_mmc.ASX=ASX;
		m_mmc.ADX=ADX;
	}
}

/** lmmv_engine() *********************************************/
/** VDP -> Vram                                             **/
/*************************************************************/
void v99x8_device::lmmv_engine()
{
	int DX=m_mmc.DX;
	int DY=m_mmc.DY;
	int TX=m_mmc.TX;
	int TY=m_mmc.TY;
	int NX=m_mmc.NX;
	int NY=m_mmc.NY;
	int ADX=m_mmc.ADX;
	int ANX=m_mmc.ANX;
	uint8_t CL=m_mmc.CL;
	uint8_t LO=m_mmc.LO;
	int MXD = m_mmc.MXD;
	int cnt;
	int delta;

	delta = get_vdp_timing_value(lmmv_timing);
	cnt = m_vdp_ops_count;

	switch (m_mode) {
	default:
	case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, ADX, DY, CL, LO); post__x_y(256)
		break;
	case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, ADX, DY, CL, LO); post__x_y(512)
		break;
	case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, ADX, DY, CL, LO); post__x_y(512)
		break;
	case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, ADX, DY, CL, LO); post__x_y(256)
		break;
	}

	if ((m_vdp_ops_count=cnt)>0) {
		// Command execution done
		m_stat_reg[2]&=0xFE;
		m_vdp_engine=nullptr;
		if (!NY)
			DY+=TY;
		m_cont_reg[38]=DY & 0xFF;
		m_cont_reg[39]=(DY>>8) & 0x03;
		m_cont_reg[42]=NY & 0xFF;
		m_cont_reg[43]=(NY>>8) & 0x03;
	}
	else {
		m_mmc.DY=DY;
		m_mmc.NY=NY;
		m_mmc.ANX=ANX;
		m_mmc.ADX=ADX;
	}
}

/** lmmm_engine() *********************************************/
/** Vram -> Vram                                            **/
/*************************************************************/
void v99x8_device::lmmm_engine()
{
	int SX=m_mmc.SX;
	int SY=m_mmc.SY;
	int DX=m_mmc.DX;
	int DY=m_mmc.DY;
	int TX=m_mmc.TX;
	int TY=m_mmc.TY;
	int NX=m_mmc.NX;
	int NY=m_mmc.NY;
	int ASX=m_mmc.ASX;
	int ADX=m_mmc.ADX;
	int ANX=m_mmc.ANX;
	uint8_t LO=m_mmc.LO;
	int MXS = m_mmc.MXS;
	int MXD = m_mmc.MXD;
	int cnt;
	int delta;

	delta = get_vdp_timing_value(lmmm_timing);
	cnt = m_vdp_ops_count;

	switch (m_mode) {
	default:
	case V9938_MODE_GRAPHIC4: pre_loop VDPpset5(MXD, ADX, DY, VDPpoint5(MXS, ASX, SY), LO); post_xxyy(256)
		break;
	case V9938_MODE_GRAPHIC5: pre_loop VDPpset6(MXD, ADX, DY, VDPpoint6(MXS, ASX, SY), LO); post_xxyy(512)
		break;
	case V9938_MODE_GRAPHIC6: pre_loop VDPpset7(MXD, ADX, DY, VDPpoint7(MXS, ASX, SY), LO); post_xxyy(512)
		break;
	case V9938_MODE_GRAPHIC7: pre_loop VDPpset8(MXD, ADX, DY, VDPpoint8(MXS, ASX, SY), LO); post_xxyy(256)
		break;
	}

	if ((m_vdp_ops_count=cnt)>0) {
		// Command execution done
		m_stat_reg[2]&=0xFE;
		m_vdp_engine=nullptr;
		if (!NY) {
			SY+=TY;
			DY+=TY;
		}
		else
			if (SY==-1)
			DY+=TY;
		m_cont_reg[42]=NY & 0xFF;
		m_cont_reg[43]=(NY>>8) & 0x03;
		m_cont_reg[34]=SY & 0xFF;
		m_cont_reg[35]=(SY>>8) & 0x03;
		m_cont_reg[38]=DY & 0xFF;
		m_cont_reg[39]=(DY>>8) & 0x03;
	}
	else {
		m_mmc.SY=SY;
		m_mmc.DY=DY;
		m_mmc.NY=NY;
		m_mmc.ANX=ANX;
		m_mmc.ASX=ASX;
		m_mmc.ADX=ADX;
	}
}

/** lmcm_engine() *********************************************/
/** Vram -> CPU                                             **/
/*************************************************************/
void v99x8_device::lmcm_engine()
{
	if ((m_stat_reg[2]&0x80)!=0x80) {
		m_stat_reg[7]=m_cont_reg[44]=VDP_POINT(((m_mode >= 5) && (m_mode <= 8)) ? (m_mode-5) : 0, m_mmc.MXS, m_mmc.ASX, m_mmc.SY);
		m_vdp_ops_count-=get_vdp_timing_value(lmmv_timing);
		m_stat_reg[2]|=0x80;

		if (!--m_mmc.ANX || ((m_mmc.ASX+=m_mmc.TX)&m_mmc.MX)) {
			if (!(--m_mmc.NY & 1023) || (m_mmc.SY+=m_mmc.TY)==-1) {
				m_stat_reg[2]&=0xFE;
				m_vdp_engine=nullptr;
				if (!m_mmc.NY)
					m_mmc.DY+=m_mmc.TY;
				m_cont_reg[42]=m_mmc.NY & 0xFF;
				m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
				m_cont_reg[34]=m_mmc.SY & 0xFF;
				m_cont_reg[35]=(m_mmc.SY>>8) & 0x03;
			}
			else {
				m_mmc.ASX=m_mmc.SX;
				m_mmc.ANX=m_mmc.NX;
			}
		}
	}
}

/** lmmc_engine() *********************************************/
/** CPU -> Vram                                             **/
/*************************************************************/
void v99x8_device::lmmc_engine()
{
	if ((m_stat_reg[2]&0x80)!=0x80) {
		uint8_t SM=((m_mode >= 5) && (m_mode <= 8)) ? (m_mode-5) : 0;

		m_stat_reg[7]=m_cont_reg[44]&=Mask[SM];
		VDP_PSET(SM, m_mmc.MXD, m_mmc.ADX, m_mmc.DY, m_cont_reg[44], m_mmc.LO);
		m_vdp_ops_count-=get_vdp_timing_value(lmmv_timing);
		m_stat_reg[2]|=0x80;

		if (!--m_mmc.ANX || ((m_mmc.ADX+=m_mmc.TX)&m_mmc.MX)) {
			if (!(--m_mmc.NY&1023) || (m_mmc.DY+=m_mmc.TY)==-1) {
				m_stat_reg[2]&=0xFE;
				m_vdp_engine=nullptr;
				if (!m_mmc.NY)
					m_mmc.DY+=m_mmc.TY;
				m_cont_reg[42]=m_mmc.NY & 0xFF;
				m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
				m_cont_reg[38]=m_mmc.DY & 0xFF;
				m_cont_reg[39]=(m_mmc.DY>>8) & 0x03;
			}
			else {
				m_mmc.ADX=m_mmc.DX;
				m_mmc.ANX=m_mmc.NX;
			}
		}
	}
}

/** hmmv_engine() *********************************************/
/** VDP --> Vram                                            **/
/*************************************************************/
void v99x8_device::hmmv_engine()
{
	int DX=m_mmc.DX;
	int DY=m_mmc.DY;
	int TX=m_mmc.TX;
	int TY=m_mmc.TY;
	int NX=m_mmc.NX;
	int NY=m_mmc.NY;
	int ADX=m_mmc.ADX;
	int ANX=m_mmc.ANX;
	uint8_t CL=m_mmc.CL;
	int MXD = m_mmc.MXD;
	int cnt;
	int delta;

	delta = get_vdp_timing_value(hmmv_timing);
	cnt = m_vdp_ops_count;

	switch (m_mode) {
	default:
	case V9938_MODE_GRAPHIC4: pre_loop m_vram_space->write_byte(VDP_VRMP5(MXD, ADX, DY), CL); post__x_y(256)
		break;
	case V9938_MODE_GRAPHIC5: pre_loop m_vram_space->write_byte(VDP_VRMP6(MXD, ADX, DY), CL); post__x_y(512)
		break;
	case V9938_MODE_GRAPHIC6: pre_loop m_vram_space->write_byte(VDP_VRMP7(MXD, ADX, DY), CL); post__x_y(512)
		break;
	case V9938_MODE_GRAPHIC7: pre_loop m_vram_space->write_byte(VDP_VRMP8(MXD, ADX, DY), CL); post__x_y(256)
		break;
	}

	if ((m_vdp_ops_count=cnt)>0) {
		// Command execution done
		m_stat_reg[2]&=0xFE;
		m_vdp_engine=nullptr;
		if (!NY)
			DY+=TY;
		m_cont_reg[42]=NY & 0xFF;
		m_cont_reg[43]=(NY>>8) & 0x03;
		m_cont_reg[38]=DY & 0xFF;
		m_cont_reg[39]=(DY>>8) & 0x03;
	}
	else {
		m_mmc.DY=DY;
		m_mmc.NY=NY;
		m_mmc.ANX=ANX;
		m_mmc.ADX=ADX;
	}
}

/** hmmm_engine() *********************************************/
/** Vram -> Vram                                            **/
/*************************************************************/
void v99x8_device::hmmm_engine()
{
	int SX=m_mmc.SX;
	int SY=m_mmc.SY;
	int DX=m_mmc.DX;
	int DY=m_mmc.DY;
	int TX=m_mmc.TX;
	int TY=m_mmc.TY;
	int NX=m_mmc.NX;
	int NY=m_mmc.NY;
	int ASX=m_mmc.ASX;
	int ADX=m_mmc.ADX;
	int ANX=m_mmc.ANX;
	int MXS = m_mmc.MXS;
	int MXD = m_mmc.MXD;
	int cnt;
	int delta;

	delta = get_vdp_timing_value(hmmm_timing);
	cnt = m_vdp_ops_count;

	switch (m_mode) {
	default:
	case V9938_MODE_GRAPHIC4: pre_loop m_vram_space->write_byte(VDP_VRMP5(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP5(MXS, ASX, SY))); post_xxyy(256)
		break;
	case V9938_MODE_GRAPHIC5: pre_loop m_vram_space->write_byte(VDP_VRMP6(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP6(MXS, ASX, SY))); post_xxyy(512)
		break;
	case V9938_MODE_GRAPHIC6: pre_loop m_vram_space->write_byte(VDP_VRMP7(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP7(MXS, ASX, SY))); post_xxyy(512)
		break;
	case V9938_MODE_GRAPHIC7: pre_loop m_vram_space->write_byte(VDP_VRMP8(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP8(MXS, ASX, SY))); post_xxyy(256)
		break;
	}

	if ((m_vdp_ops_count=cnt)>0) {
		// Command execution done
		m_stat_reg[2]&=0xFE;
		m_vdp_engine=nullptr;
		if (!NY) {
			SY+=TY;
			DY+=TY;
		}
		else
			if (SY==-1)
			DY+=TY;
		m_cont_reg[42]=NY & 0xFF;
		m_cont_reg[43]=(NY>>8) & 0x03;
		m_cont_reg[34]=SY & 0xFF;
		m_cont_reg[35]=(SY>>8) & 0x03;
		m_cont_reg[38]=DY & 0xFF;
		m_cont_reg[39]=(DY>>8) & 0x03;
	}
	else {
		m_mmc.SY=SY;
		m_mmc.DY=DY;
		m_mmc.NY=NY;
		m_mmc.ANX=ANX;
		m_mmc.ASX=ASX;
		m_mmc.ADX=ADX;
	}
}

/** ymmm_engine() *********************************************/
/** Vram -> Vram                                            **/
/*************************************************************/

void v99x8_device::ymmm_engine()
{
	int SY=m_mmc.SY;
	int DX=m_mmc.DX;
	int DY=m_mmc.DY;
	int TX=m_mmc.TX;
	int TY=m_mmc.TY;
	int NY=m_mmc.NY;
	int ADX=m_mmc.ADX;
	int MXD = m_mmc.MXD;
	int cnt;
	int delta;

	delta = get_vdp_timing_value(ymmm_timing);
	cnt = m_vdp_ops_count;

	switch (m_mode) {
	default:
	case V9938_MODE_GRAPHIC4: pre_loop m_vram_space->write_byte(VDP_VRMP5(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP5(MXD, ADX, SY))); post__xyy(256)
		break;
	case V9938_MODE_GRAPHIC5: pre_loop m_vram_space->write_byte(VDP_VRMP6(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP6(MXD, ADX, SY))); post__xyy(512)
		break;
	case V9938_MODE_GRAPHIC6: pre_loop m_vram_space->write_byte(VDP_VRMP7(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP7(MXD, ADX, SY))); post__xyy(512)
		break;
	case V9938_MODE_GRAPHIC7: pre_loop m_vram_space->write_byte(VDP_VRMP8(MXD, ADX, DY), m_vram_space->read_byte(VDP_VRMP8(MXD, ADX, SY))); post__xyy(256)
		break;
	}

	if ((m_vdp_ops_count=cnt)>0) {
		// Command execution done
		m_stat_reg[2]&=0xFE;
		m_vdp_engine=nullptr;
		if (!NY) {
			SY+=TY;
			DY+=TY;
		}
		else
			if (SY==-1)
			DY+=TY;
		m_cont_reg[42]=NY & 0xFF;
		m_cont_reg[43]=(NY>>8) & 0x03;
		m_cont_reg[34]=SY & 0xFF;
		m_cont_reg[35]=(SY>>8) & 0x03;
		m_cont_reg[38]=DY & 0xFF;
		m_cont_reg[39]=(DY>>8) & 0x03;
	}
	else {
		m_mmc.SY=SY;
		m_mmc.DY=DY;
		m_mmc.NY=NY;
		m_mmc.ADX=ADX;
	}
}

/** hmmc_engine() *********************************************/
/** CPU -> Vram                                             **/
/*************************************************************/
void v99x8_device::hmmc_engine()
{
	if ((m_stat_reg[2]&0x80)!=0x80) {
		m_vram_space->write_byte(VDP_VRMP(((m_mode >= 5) && (m_mode <= 8)) ? (m_mode-5) : 0, m_mmc.MXD, m_mmc.ADX, m_mmc.DY), m_cont_reg[44]);
		m_vdp_ops_count -= get_vdp_timing_value(hmmv_timing);
		m_stat_reg[2]|=0x80;

		if (!--m_mmc.ANX || ((m_mmc.ADX+=m_mmc.TX)&m_mmc.MX)) {
			if (!(--m_mmc.NY&1023) || (m_mmc.DY+=m_mmc.TY)==-1) {
				m_stat_reg[2]&=0xFE;
				m_vdp_engine=nullptr;
				if (!m_mmc.NY)
					m_mmc.DY+=m_mmc.TY;
				m_cont_reg[42]=m_mmc.NY & 0xFF;
				m_cont_reg[43]=(m_mmc.NY>>8) & 0x03;
				m_cont_reg[38]=m_mmc.DY & 0xFF;
				m_cont_reg[39]=(m_mmc.DY>>8) & 0x03;
			}
			else {
				m_mmc.ADX=m_mmc.DX;
				m_mmc.ANX=m_mmc.NX;
			}
		}
	}
}

/** VDPWrite() ***********************************************/
/** Use this function to transfer pixel(s) from CPU to m_ **/
/*************************************************************/
void v99x8_device::cpu_to_vdp(uint8_t V)
{
	m_stat_reg[2]&=0x7F;
	m_stat_reg[7]=m_cont_reg[44]=V;
	if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
}

/** VDPRead() ************************************************/
/** Use this function to transfer pixel(s) from VDP to CPU. **/
/*************************************************************/
uint8_t v99x8_device::vdp_to_cpu()
{
	m_stat_reg[2]&=0x7F;
	if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
	return(m_cont_reg[44]);
}

/** report_vdp_command() ***************************************/
/** Report VDP Command to be executed                       **/
/*************************************************************/
void v99x8_device::report_vdp_command(uint8_t Op)
{
	static const char *const Ops[16] =
	{
		"SET ","AND ","OR  ","XOR ","NOT ","NOP ","NOP ","NOP ",
		"TSET","TAND","TOR ","TXOR","TNOT","NOP ","NOP ","NOP "
	};
	static const char *const Commands[16] =
	{
		" ABRT"," ????"," ????"," ????","POINT"," PSET"," SRCH"," LINE",
		" LMMV"," LMMM"," LMCM"," LMMC"," HMMV"," HMMM"," YMMM"," HMMC"
	};

	uint8_t CL, CM, LO;
	int SX,SY, DX,DY, NX,NY;

	// Fetch arguments
	CL = m_cont_reg[44];
	SX = (m_cont_reg[32]+((int)m_cont_reg[33]<<8)) & 511;
	SY = (m_cont_reg[34]+((int)m_cont_reg[35]<<8)) & 1023;
	DX = (m_cont_reg[36]+((int)m_cont_reg[37]<<8)) & 511;
	DY = (m_cont_reg[38]+((int)m_cont_reg[39]<<8)) & 1023;
	NX = (m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023;
	NY = (m_cont_reg[42]+((int)m_cont_reg[43]<<8)) & 1023;
	CM = Op>>4;
	LO = Op&0x0F;

	LOGMASKED(LOG_COMMAND, "Opcode %02x %s-%s s=(%d,%d), d=(%d,%d), c=%02x, wh=[%d,%d]%s\n",
		Op, Commands[CM], Ops[LO],
		SX,SY, DX,DY, CL&0xff, m_cont_reg[45]&0x04? -NX:NX,
		m_cont_reg[45]&0x08? -NY:NY,
		m_cont_reg[45]&0x70? " on ExtVRAM":""
		);
}

/** VDPDraw() ************************************************/
/** Perform a given V9938 operation Op.                     **/
/*************************************************************/
uint8_t v99x8_device::command_unit_w(uint8_t Op)
{
	int SM;

	// V9938 ops only work in SCREENs 5-8
	if (m_mode<5)
		return(0);

	SM = m_mode-5;         // Screen mode index 0..3

	m_mmc.CM = Op>>4;
	if ((m_mmc.CM & 0x0C) != 0x0C && m_mmc.CM != 0)
		// Dot operation: use only relevant bits of color
	m_stat_reg[7]=(m_cont_reg[44]&=Mask[SM]);

	//  if(Verbose&0x02)
	report_vdp_command(Op);

	if ((m_vdp_engine != nullptr) && (m_mmc.CM != CM_ABRT))
		LOGMASKED(LOG_WARN, "Command overrun; previous command not completed\n");

	switch(Op>>4) {
	case CM_ABRT:
		m_stat_reg[2]&=0xFE;
		m_vdp_engine=nullptr;
		return 1;
	case CM_POINT:
		m_stat_reg[2]&=0xFE;
		m_vdp_engine=nullptr;
		m_stat_reg[7]=m_cont_reg[44]=
		VDP_POINT(SM, (m_cont_reg[45] & 0x10) != 0,
			m_cont_reg[32]+((int)m_cont_reg[33]<<8),
			m_cont_reg[34]+((int)m_cont_reg[35]<<8));
		return 1;
	case CM_PSET:
		m_stat_reg[2]&=0xFE;
		m_vdp_engine=nullptr;
		VDP_PSET(SM, (m_cont_reg[45] & 0x20) != 0,
			m_cont_reg[36]+((int)m_cont_reg[37]<<8),
			m_cont_reg[38]+((int)m_cont_reg[39]<<8),
			m_cont_reg[44],
			Op&0x0F);
		return 1;
	case CM_SRCH:
		m_vdp_engine=&v99x8_device::srch_engine;
		break;
	case CM_LINE:
		m_vdp_engine=&v99x8_device::line_engine;
		break;
	case CM_LMMV:
		m_vdp_engine=&v99x8_device::lmmv_engine;
		break;
	case CM_LMMM:
		m_vdp_engine=&v99x8_device::lmmm_engine;
		break;
	case CM_LMCM:
		m_vdp_engine=&v99x8_device::lmcm_engine;
		break;
	case CM_LMMC:
		m_vdp_engine=&v99x8_device::lmmc_engine;
		break;
	case CM_HMMV:
		m_vdp_engine=&v99x8_device::hmmv_engine;
		break;
	case CM_HMMM:
		m_vdp_engine=&v99x8_device::hmmm_engine;
		break;
	case CM_YMMM:
		m_vdp_engine=&v99x8_device::ymmm_engine;
		break;
	case CM_HMMC:
		m_vdp_engine=&v99x8_device::hmmc_engine;
		break;
	default:
		LOGMASKED(LOG_WARN, "Unrecognized opcode %02Xh\n",Op);
		return(0);
	}

	// Fetch unconditional arguments
	m_mmc.SX = (m_cont_reg[32]+((int)m_cont_reg[33]<<8)) & 511;
	m_mmc.SY = (m_cont_reg[34]+((int)m_cont_reg[35]<<8)) & 1023;
	m_mmc.DX = (m_cont_reg[36]+((int)m_cont_reg[37]<<8)) & 511;
	m_mmc.DY = (m_cont_reg[38]+((int)m_cont_reg[39]<<8)) & 1023;
	m_mmc.NY = (m_cont_reg[42]+((int)m_cont_reg[43]<<8)) & 1023;
	m_mmc.TY = m_cont_reg[45]&0x08? -1:1;
	m_mmc.MX = PPL[SM];
	m_mmc.CL = m_cont_reg[44];
	m_mmc.LO = Op&0x0F;
	m_mmc.MXS = (m_cont_reg[45] & 0x10) != 0;
	m_mmc.MXD = (m_cont_reg[45] & 0x20) != 0;

	// Argument depends on uint8_t or dot operation
	if ((m_mmc.CM & 0x0C) == 0x0C) {
		m_mmc.TX = m_cont_reg[45]&0x04? -PPB[SM]:PPB[SM];
		m_mmc.NX = ((m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023)/PPB[SM];
	}
	else {
		m_mmc.TX = m_cont_reg[45]&0x04? -1:1;
		m_mmc.NX = (m_cont_reg[40]+((int)m_cont_reg[41]<<8)) & 1023;
	}

	// X loop variables are treated specially for LINE command
	if (m_mmc.CM == CM_LINE) {
		m_mmc.ASX=((m_mmc.NX-1)>>1);
		m_mmc.ADX=0;
	}
	else {
		m_mmc.ASX = m_mmc.SX;
		m_mmc.ADX = m_mmc.DX;
	}

	// NX loop variable is treated specially for SRCH command
	if (m_mmc.CM == CM_SRCH)
		m_mmc.ANX=(m_cont_reg[45]&0x02)!=0; // Do we look for "==" or "!="?
	else
		m_mmc.ANX = m_mmc.NX;

	// Command execution started
	m_stat_reg[2]|=0x01;

	// Start execution if we still have time slices
	if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();

	// Operation successfully initiated
	return(1);
}

/** LoopVDP() ************************************************
Run X steps of active VDP command
*************************************************************/
void v99x8_device::update_command()
{
	if(m_vdp_ops_count<=0)
	{
		m_vdp_ops_count+=13662;
		if(m_vdp_engine&&(m_vdp_ops_count>0)) (this->*m_vdp_engine)();
	}
	else
	{
		m_vdp_ops_count=13662;
		if(m_vdp_engine) (this->*m_vdp_engine)();
	}
}