summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/video/namcos22.cpp
blob: 19cabcc08bd020ec6d9570823fab4e96aa77ba97 (plain) (tree)
1
2
3
4
5
6
7
8
9
                       
                                                      


                                    
        
  
                       
                                                                                                                                    
                                                              

                                                                                                                                    
                                                                                                                          






                                                                                                                                 

                                                                                                                                   


                                                                                                                    

                                                                                                                                         
                                                                                                                     






                                                                                                                                 

                           

  
                                 
 
                
                              
 
 
                   



                                                                              
 
                               
 
                                                               
                                  
 


                                       

 

 
                                                                                               
 
                          
                                                                                                                                          










                                        
                                           





                                                             


                                              
                                                   

                           
                                            





                                                                        
 
                            
         
                                                        
                               
                                                  
         
                                 
         
                                                        
                               
                                                  

         


                                                                                              
                              
         
                                                                  

                                             



                                                                                                                                  

                                                                 
                                                                       
 
                                                   
                                              
                                                            
 

                                                 
                         

                                                                       
                                                                      
                                                                

                                                                  
                                 
                                                                               
                                                                              
                                 
                         
                                                             


                                                               
 
                                             


                                                               
 
                                               


                                                                 
 
                                                
                         
                                                                           
                         
 
                                                





                                                 
                 


            
                                                                  

                                             



                                                                                                                              
                                                                 
 
                                                                       
 




                                                                       
                                                                      
                                                                

                                                                                        


                                                                              
                         
                                                             


                                                               
 

                                                  
                                                            
 
                                             


                                                               
 
                                                
                                                 
 




                                
         
 
 
 
                                                                                                                                        








                                                            

                                             


                                                                                





                                                          
                                                

                                              


                                                               

                                               


                                                                 


                                                
                                                                           

                         
                                                










                                                                                                                     
 


                                                          

                      
 



                                                  
 





                                 
                                                                  



















                                                                                                                           



                                                         





                                                                         
                 


                                                                 

                                  
 


                                                                 

                                                                            






                                                                                 
                                                          




                                                         


                                                                                  
                                                  


                                                                                            


                 
                              
         
                              
                                              
                 
                                                                        
                                                                                                                          


                            
                                                                  
                                                                                                            
 
                                                         
 





































                                                                                                                                                  
 
                                                          




















                                                                                                                                                                                             

                                  

                                                                               
                         
                                                                                       
                                                                                                         
                                           
                                 
                                                                                       


                                                                          

                                                                                                   
                                         

                                                                                       
                                         
                                 
                                    
                                 

                                                                
                                                                                           
                                 


                         
 


                              
                                              
                 
                                                                          
                                                                                                                    

                 


                                  

                                                                                         
                                                                                                                                                             



                                                                  
                                                                               

                                                                  
                                                                                              


                            
                                                       
                                                                                           

                         
         
 
                                                                                                                                 


 


                                          

                       






                               
 
                                                       

                                                                           
                                                        


                               

                                              

                                                 













                                                                  
                                                                                                             

                                                                     
 

                                

                                 

                                      

                                      

                                      

                                       

                                      

                                       
 
                              
                                              
                 
                                                                        
                                                                                                                          

                 
                                                                                 
                                                                        
                 
                                                                                             
                                                    
                                                                                                 

                 
                                                                                                                              
         
 
 

                                                                                                                   
 









                                                                                                                               
         
                                                                      
                 





















                                                                                                                      

                 
 
 
 
 


                                                                                               
 


                                     
 


                                                                                                                        
         





                                                                      
         

                                       
 
 
                                                                                                                                   
 
                                                            
                                                  
                     
 
                                                         
         


                                                                                
                 



                                                                         
                 


                                              
         
 
                                                     

                                                          




                                  
                                                                                            
                                  
                                  
                                                     

                            
 
 
                                                                                                                        
 
                 
         
                                                             
                 
                                                                             
                         
                                                                                               
                         
                                             


                    
                                    
                         
                                                                             
 
                                                   
                                 









                                                                                      
                                 
                                                     



                                            
 
 
                                                                                 
 

                                                             
         
                                                                               
                                                     
         





                                       
 
 





                                                                                               
                                                           
 
                                        
                                                                      

                                          
         
                               


                           
 
 

                                                                    
 
                         
 
                                          
         
                                                 
                 





                                                             

                 
 
                                      
 
 
                                                     
 






                                                       

 
                                                                                    
 


                      
 


                                                          

 
                                                                                     
 


                      
 


                                                

 
                                                                  
 
                                   
         


                                                                                 
 




                                                                                    
 
                                                                                                       


         
 
                                                          
 

                                                
 
                                                                       
                                   
























                                                                   
                                                                     



                                                                                                           
         
                                                                                                           

                                                                


            

                                                                          
         





                                                                  
         

                                                               
                 

                                           


                    

                                               
                 
 
                                               
                                           

                                               
                 
                                               
                         
                                           
                                           
                         
                                      
                                          
                            
                                                 



                                            

                                                   
                         
                                           
                                           
                         
                                         

                 

                                        

                                     
         





                                   
 
 
































                                                      
                                                                                                                                                         
 
                                                    
                      


                                 
              























                                                                  
         









                                                         
                 


                                                                                      
                         

                                                            
                         













                                                                                                 
                 

                              
         
 

                                     
 


                              
                                              
                              
 
                           
                                              
                              
 
                        
                                                                

                              
 











                                                                  
 






                                                                      
 



                                                    
 






                                                                                                           
 









                                                               
 





                                         
 
 
                                                                                                



                                             
 

                                                  
 




                                                        
 













                                                   
                 










                                                                                                        
 









































                                                                                                                   
                 
                                     
         
 
 
                                                                                   
 
                                          

                              
 

                
                                                    


                                                   
         
 
 
 



























                                                                                             
 
                                                                                 
 







                         
                                                               

















                                                                       
          

                                           
 


                                                        
 
                                             

                                                 


                                                                      
 


                                                                
 


                                                                
 


                                                                
 

                                                                                 
 
                                                                                     



                                              
 
                                     
 


                                                            
 


                                                            
 


                                                            
 


                                              
 

                                                          
         
                                                          
         

                                                                                  
         
 
 
                                                               
 


                                                             
 


                                                             
 


                                                             
 
 
                                                               
 








                                           
          

                                       
 
 
                                                            
 

                                                                      
 
                                        
 
                      
         




                                         

         

                
                             
                                       
                                       
 




                                                                                  
 


                                                                                              
 


                                                                                        
 


                                                                                           
 
                                
                                                                                                                        









                                                                                     
 


                                                                           

                                                                                
                 

                                         
                 
         

 
                                                        
 
                                      
         

                                              


         
 
 
                                                                                               
 
                                                                                                                                                                                         

                                             
         



                                                                    
 


                                                                     
 










                                                                                               
 
























                                                                                                         
                                       


                                                        
                                                       







                                                                            



                                                                                  



















































                                                                                                                              
                 

                          
         

 
                                                                                  
 

                             
 






                                                                                                                                              
         


                                                                                                                                              
         
















                                                                                                                                                          
 



                                                                              
 



                                                          
 


                                                                
 






                                                                                   
 

                                                                                  
 

                                                                
 


                                                
 
                                                                 
         


                                                                                                      
         
 



                                                                                  
 

                                                        
 



                                                                                      
 

                                     
 

                                                                
 


                                                                                                                                   
 


                                                                                                         
 




                                                                                                      

         



                                                                                                                   
         

                                                                                                  

         
                                          
         


                                                                                                      
         
 
 
                                                       
 
                                              

                         
         




                                                                                   
 







                                                                       
         
                   
 
 
                                                        
 

                                              
 
 
 
                                                                                               
 

                                                        
                                                       




                                            
                                                                                               
 
 





                                                     
 


                                                
                                                   
 
 





                                                     
                                                                                     





                                                                                         
                 







                                                                                                       
         

                                     
 
 
                                                      
 











                                                                                                               
 
 
 






























                                                                                                   
 









                                                              

 
                                                   
 
                        
         





                                                                
                    















                                                                 
         

 

                                                                                                                                 
                                              


                       
 
                        



                                                       
 



                                                                   
 


                                                                        
                                                                                   
 










                                                                      
                                                            
 


                                                 
                                                                    

                                                                                                                      
                                                                                                   

                                         
 


                                                 
                                                                    
                                                               


                                                                                  
                                                                                                                                                                                                                   
                                                                           
                                         
 

                                                                                              


                                                                            
                                 
 
                                                 


                                                                           
 
                                                        



                         
 

                                                                                                                    
                                              


                       
 


                                                    
 


                                                                                                             
 




                                                                                                              
          








                                                                      
                                                 
 



                                                            
                                              






                                                                   
                                                                       
                                                                                                           



                                                                 
                                                                      

                                                      
 
                                                 


                                                                        
 


                                                                                                        
 
                                                      
                         


                                                                                                                             

                 
 
 
                                                                                                            
 

                                                        
 


                                                              
 



                                                                            
         
            
         

                                                                            
         
 
 
 
 
                                                                                               
 
                                                     
 

                                            
 
 
                                     
 









                                                                               
                                                                                




                                          
 
 


                                                 

                                                                                                        












































































                                                                                                      







                                                    
         

                                                                                                                                 
         

                                        
         





                                                                                                                                         
         

                              
 
                      
         






                                                                                                  
 





































                                                                                                                                                                       
 














                                                                    
 























                                                                                                                            
 

                                                                                                                                  
 

                                        
                 


                                                                           
                 
 
                                      
         
 
 
 


                                                                                               
                                                                                                                        
 



                                            
 
                           
                                                                                                      

                                                      
                                                                                           
                                                                        
         
                                                  
 
                 
                                               




                                                                             
 
                      


                                                                 

                                                              
                                                  








                                                                                             
 
                 

 
                                                                                                                       
 


                                            
 
                           
                                                      
 


                                             
                                                                                   
 
                 

 
 
 
                                                                                               
 
                                  
 
                                                           
                                              
                                        
 
                                                         
 

                                      
                                                                        
 

                                           
         

                                                       

                                                                                     

                                                    
         
 

                                                           



                                                                           



                                                                                                          
 
                                                            
 
                                                    
                                                                 
                                                 
 


                                                                         
 
                          

                                                                                         
                                         
         


                                                                
         
 
                                     
                                                                       


                                             
                 
                                                    
                         
                                                   
 






                                                     
                                 


                                                      
                                 
 

                                                                                                
                 
         
 

                                                      
         



                                             
                 










                                                                        
                 
 


                              

 
 
                                  
 
                                         
                      
 
                                                                
                                                                                                                                                                              
                                              
 
                                                                     
 
                                                                 
 
// license:BSD-3-Clause
// copyright-holders:Phil Stroffolino, hap, R. Belmont
/**
 * video hardware for Namco System22
 *
 * TODO:
 *
 * - emulate slave dsp!
 * - texture u/v mapping is often 1 pixel off, resulting in many glitch lines/gaps between textures. The glitch may be in MAME core:
 *       it used to be much worse with the legacy_poly_manager
 * - tokyowar tanks are not shootable, same for timecris helicopter, there's still a very small hitbox but almost impossible to hit.
 *       airco22b may have a similar problem. (is this related to dsp? or cpu?)
 * - find out how/where vics num_sprites is determined exactly, currently a workaround is needed for airco22b and dirtdash
 * - improve ss22 fogging:
 *       + scene changes too rapidly sometimes, eg. dirtdash snow level finish (see attract), or aquajet going down the waterfall
 *       + 100% fog if you start dirtdash at the hill level
 * - improve ss22 lighting, eg. mountains in alpinr2b selection screen
 * - improve ss22 spot:
 *       + dirtdash spotlight is opaque for a short time when exiting the jungle level
 *       + dirtdash speedometer has wrong colors when in the jungle level
 *       + dirtdash record time message creates a 'gap' in the spotlight when entering the jungle level (possibly just a game bug?)
 * - add layer enable in system 22, see bugs in cybrcomm and victlapw
 * - window clipping is wrong in acedrvrw, victlapw
 * - ridgerac waving flag title screen is missing, just an empty beach scenery instead
 * - global offset is wrong in non-super22 servicemode video test, and above that, it flickers in acedrvrw, victlapw
 * - dirtdash polys are broken at the start section of the mountain level, maybe bad rom?
 * - alpinr2b skiier selection screen should have mirrored models (easiest to see with cursor on the red pants guy). specular reflection?
 * - propcycl scoreboard sprite part should fade out in attract mode and just before game over, fader or fog related?
 * - ridgerac fogging isn't applied to the upper/side part of the sky (best seen when driving down a hill), it's fine in ridgera2
 *       czram contents is rather odd here and partly cleared (probably the cause?):
 *        $0000-$0d7f   - gradual increase from $00-$7c
 *        $0d80-$0fff   - $73, huh, why lower?
 *        $1000-$19ff   - $00, huh!? (it's specifically cleared, memsetting czram at boot does not fix the issue)
 *        $1a00-$0dff   - $77
 *        $1e00-$1fff   - $78
 *
 * - lots of smaller issues
 *
 *
 *******************************/

#include "emu.h"
#include "includes/namcos22.h"


// poly constructor
namcos22_renderer::namcos22_renderer(namcos22_state &state)
	: poly_manager<float, namcos22_object_data, 4, 8000>(state.machine()),
		m_state(state)
	{ }

void namcos22_renderer::reset()
{
	memset(&m_scenenode_root, 0, sizeof(m_scenenode_root));
	m_scenenode_cur = nullptr;

	m_clipx = 320;
	m_clipy = 240;
	m_cliprect.set(0, 639, 0, 479);
}



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

// poly scanline callbacks
void namcos22_renderer::renderscanline_uvi_full(int32_t scanline, const extent_t &extent, const namcos22_object_data &extra, int threadid)
{
	float z = extent.param[0].start;
	float u = extent.param[1].start;
	float v = extent.param[2].start;
	float i = extent.param[3].start;
	float dz = extent.param[0].dpdx;
	float du = extent.param[1].dpdx;
	float dv = extent.param[2].dpdx;
	float di = extent.param[3].dpdx;
	int bn = extra.bn * 0x1000;
	const pen_t *pens = extra.pens;
	const uint8_t *czram = extra.czram;
	int cz_adjust = extra.cz_adjust;
	int cz_sdelta = extra.cz_sdelta;
	int zfog_enabled = extra.zfog_enabled;
	int fogfactor = 0xff - extra.fogfactor;
	int fadefactor = 0xff - extra.fadefactor;
	int alphafactor = 0xff - m_state.m_poly_translucency;
	rgbaint_t fogcolor = extra.fogcolor;
	rgbaint_t fadecolor = extra.fadecolor;
	rgbaint_t polycolor = extra.polycolor;
	int polyfade_enabled = extra.pfade_enabled;
	int penmask = 0xff;
	int penshift = 0;
	int prioverchar = extra.prioverchar;
	uint32_t *dest = &extra.destbase->pix32(scanline);
	uint8_t *primap = &extra.primap->pix8(scanline);
	uint16_t *ttmap = m_state.m_texture_tilemap;
	uint8_t *ttattr = m_state.m_texture_tileattr.get();
	uint8_t *ttdata = m_state.m_texture_tiledata;
	uint8_t *tt_ayx_to_pixel = m_state.m_texture_ayx_to_pixel.get();

	if (extra.cmode & 4)
	{
		pens += 0xec + ((extra.cmode & 8) << 1);
		penmask = 0x03;
		penshift = 2 * (~extra.cmode & 3);
	}
	else if (extra.cmode & 2)
	{
		pens += 0xe0 + ((extra.cmode & 8) << 1);
		penmask = 0x0f;
		penshift = 4 * (~extra.cmode & 1);
	}

	// slight differences between super and non-super, do the branch here for optimization
	// normal: 1 fader, no alpha, shading after fog
	// super:  2 faders, alpha, shading before fog
	if (m_state.m_is_ss22)
	{
		for (int x = extent.startx; x < extent.stopx; x++)
		{
			float ooz = 1.0f / z;
			int32_t tx = (int)(u * ooz);
			int32_t ty = (int)(v * ooz) + bn;
			int32_t to = ((ty & 0xfff0) << 4) | ((tx & 0xff0) >> 4);
			int32_t pen = ttdata[(ttmap[to] << 8) | tt_ayx_to_pixel[ttattr[to] << 8 | (ty << 4 & 0xf0) | (tx & 0xf)]];
			// pen = 0x55; // debug: disable textures

			rgbaint_t rgb(pens[pen >> penshift & penmask]);

			// apply shading before fog
			int32_t shade = i*ooz;
			rgb.scale_imm_and_clamp(shade << 2);

			// per-z distance fogging
			if (zfog_enabled)
			{
				int cz = ooz + cz_adjust;
				// discard low byte and clamp to 0-1fff
				if ((uint32_t)cz < 0x200000) cz >>= 8;
				else cz = (cz < 0) ? 0 : 0x1fff;
				fogfactor = czram[cz] + cz_sdelta;
				if (fogfactor > 0)
				{
					if (fogfactor > 0xff) fogfactor = 0xff;
					rgb.blend(fogcolor, 0xff - fogfactor);
				}
			}
			else if (fogfactor != 0xff) // direct
			{
				rgb.blend(fogcolor, fogfactor);
			}

			if (polyfade_enabled)
			{
				rgb.scale_and_clamp(polycolor);
			}

			if (fadefactor != 0xff)
			{
				rgb.blend(fadecolor, fadefactor);
			}

			if (alphafactor != 0xff)
			{
				rgb.blend(rgbaint_t(dest[x]), alphafactor);
			}

			dest[x] = rgb.to_rgba();
			primap[x] |= prioverchar;

			u += du;
			v += dv;
			i += di;
			z += dz;
		}
	}
	else
	{
		for (int x = extent.startx; x < extent.stopx; x++)
		{
			float ooz = 1.0f / z;
			int tx = (int)(u * ooz);
			int ty = (int)(v * ooz) + bn;
			int to = ((ty & 0xfff0) << 4) | ((tx & 0xff0) >> 4);
			int pen = ttdata[(ttmap[to] << 8) | tt_ayx_to_pixel[ttattr[to] << 8 | (ty << 4 & 0xf0) | (tx & 0xf)]];
			// pen = 0x55; // debug: disable textures

			rgbaint_t rgb(pens[pen >> penshift & penmask]);

			// per-z distance fogging
			if (zfog_enabled)
			{
				int cz = ooz + cz_adjust;
				// discard low byte and clamp to 0-1fff
				if ((uint32_t)cz < 0x200000) cz >>= 8;
				else cz = (cz < 0) ? 0 : 0x1fff;
				fogfactor = czram[NATIVE_ENDIAN_VALUE_LE_BE(3, 0) ^ cz];
				if (fogfactor != 0)
				{
					rgb.blend(fogcolor, 0xff - fogfactor);
				}
			}
			else if (fogfactor != 0xff) // direct
			{
				rgb.blend(fogcolor, fogfactor);
			}

			// apply shading after fog
			int shade = i*ooz;
			rgb.scale_imm_and_clamp(shade << 2);

			if (polyfade_enabled)
			{
				rgb.scale_and_clamp(polycolor);
			}

			dest[x] = rgb.to_rgba();
			primap[x] |= prioverchar;

			u += du;
			v += dv;
			i += di;
			z += dz;
		}
	}
}


void namcos22_renderer::renderscanline_sprite(int32_t scanline, const extent_t &extent, const namcos22_object_data &extra, int threadid)
{
	int y_index = extent.param[1].start - extra.flipy;
	float x_index = extent.param[0].start - extra.flipx;
	float dx = extent.param[0].dpdx;
	const pen_t *pal = extra.pens;
	int prioverchar = extra.prioverchar;
	int alphafactor = extra.alpha;
	int fogfactor = 0xff - extra.fogfactor;
	int fadefactor = 0xff - extra.fadefactor;
	rgbaint_t fogcolor(extra.fogcolor);
	rgbaint_t fadecolor(extra.fadecolor);
	uint8_t *source = (uint8_t *)extra.source + y_index * extra.line_modulo;
	uint32_t *dest = &extra.destbase->pix32(scanline);
	uint8_t *primap = &extra.primap->pix8(scanline);

	for (int x = extent.startx; x < extent.stopx; x++)
	{
		int pen = source[(int)x_index];
		if (pen != 0xff)
		{
			rgbaint_t rgb(pal[pen]);

			if (fogfactor != 0xff)
			{
				rgb.blend(fogcolor, fogfactor);
			}

			if (fadefactor != 0xff)
			{
				rgb.blend(fadecolor, fadefactor);
			}

			if (alphafactor != 0xff)
			{
				rgb.blend(rgbaint_t(dest[x]), alphafactor);
			}

			dest[x] = rgb.to_rgba();
			primap[x] |= prioverchar;
		}
		x_index += dx;
	}
}



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

void namcos22_renderer::poly3d_drawquad(screen_device &screen, bitmap_rgb32 &bitmap, struct namcos22_scenenode *node)
{
	namcos22_object_data &extra = object_data_alloc();
	vertex_t v[4];
	vertex_t clipv[6];
	int clipverts;
	int vertnum;

	int direct = node->data.quad.direct;
	int flags = node->data.quad.flags;
	int color = node->data.quad.color;
	int cz_adjust = node->data.quad.cz_adjust;

	extra.destbase = &bitmap;
	extra.pfade_enabled = 0;
	extra.zfog_enabled = 0;
	extra.fadefactor = 0;
	extra.fogfactor = 0;

	extra.pens = &m_state.m_palette->pen((color & 0x7f) << 8);
	extra.primap = &screen.priority();
	extra.bn = node->data.quad.texturebank;
	extra.flags = flags;
	extra.cz_adjust = cz_adjust;
	extra.cmode = node->data.quad.cmode;
	extra.prioverchar = ((node->data.quad.cmode & 7) == 1) ? 1 : 0;
	extra.prioverchar |= m_state.m_is_ss22 ? 2 : 0;

	// scene clip
	int cx = 320 + node->data.quad.vx;
	int cy = 240 + node->data.quad.vy;
	m_clipx = cx;
	m_clipy = cy;
	m_cliprect.set(cx + node->data.quad.vw, cx - node->data.quad.vw, cy + node->data.quad.vh, cy - node->data.quad.vh);
	if (m_cliprect.min_x < 0)   m_cliprect.min_x = 0;
	if (m_cliprect.max_x > 639) m_cliprect.max_x = 639;
	if (m_cliprect.min_y < 0)   m_cliprect.min_y = 0;
	if (m_cliprect.max_y > 479) m_cliprect.max_y = 479;

	// non-direct case: project and z-clip
	if (!direct)
	{
		for (vertnum = 0; vertnum < 4; vertnum++)
		{
			v[vertnum].x = node->data.quad.v[vertnum].x;
			v[vertnum].y = node->data.quad.v[vertnum].y;
			v[vertnum].p[0] = node->data.quad.v[vertnum].z;
			v[vertnum].p[1] = node->data.quad.v[vertnum].u;
			v[vertnum].p[2] = node->data.quad.v[vertnum].v;
			v[vertnum].p[3] = node->data.quad.v[vertnum].bri;
		}

		clipverts = zclip_if_less(4, v, clipv, 4, 10.0f);
		assert(clipverts <= ARRAY_LENGTH(clipv));
		if (clipverts < 3)
			return;

		for (vertnum = 0; vertnum < clipverts; vertnum++)
		{
			float ooz = 1.0f / clipv[vertnum].p[0];
			clipv[vertnum].x = m_clipx + clipv[vertnum].x * ooz;
			clipv[vertnum].y = m_clipy - clipv[vertnum].y * ooz;
			clipv[vertnum].p[0] = ooz;
			clipv[vertnum].p[1] = (clipv[vertnum].p[1] + 0.5f) * ooz;
			clipv[vertnum].p[2] = (clipv[vertnum].p[2] + 0.5f) * ooz;
			clipv[vertnum].p[3] = (clipv[vertnum].p[3] + 0.5f) * ooz;
		}
	}

	// direct case: don't clip, and treat pv->z as 1/z
	else
	{
		clipverts = 4;
		for (vertnum = 0; vertnum < 4; vertnum++)
		{
			float ooz = node->data.quad.v[vertnum].z;
			clipv[vertnum].x = m_clipx + node->data.quad.v[vertnum].x;
			clipv[vertnum].y = m_clipy - node->data.quad.v[vertnum].y;
			clipv[vertnum].p[0] = ooz;
			clipv[vertnum].p[1] = (node->data.quad.v[vertnum].u + 0.5f) * ooz;
			clipv[vertnum].p[2] = (node->data.quad.v[vertnum].v + 0.5f) * ooz;
			clipv[vertnum].p[3] = (node->data.quad.v[vertnum].bri + 0.5f) * ooz;
		}
	}

	if (m_state.m_is_ss22)
	{
		// global fade
		if (m_state.m_mixer_flags & 1)
		{
			extra.fadefactor = m_state.m_screen_fade_factor;
			extra.fadecolor.set(0, m_state.m_screen_fade_r, m_state.m_screen_fade_g, m_state.m_screen_fade_b);
		}

		// poly fade
		extra.pfade_enabled = m_state.m_poly_fade_enabled;
		extra.polycolor.set(0, m_state.m_poly_fade_r, m_state.m_poly_fade_g, m_state.m_poly_fade_b);

		/* poly fog (not completely accurate yet)

		czram contents, it's basically a big cz compare table

		testmode:
		    o_16          0    1    2    3    4    5    6    7 <  >   f8   f9   fa   fb   fc   fd   fe   ff
		    czram[0] = 1fff 1fdf 1fbf 1f9f 1f7f 1f5f 1f3f 1f1f .... 00ff 00df 00bf 009f 007f 005f 003f 001f
		    czram[1] = 0000 0000 0000 0001 0002 0003 0005 0007 .... 1e45 1e83 1ec2 1f01 1f40 1f7f 1fbf 1fff
		    czram[2] = 003f 007f 00be 00fd 013c 017b 01b9 01f7 .... 1ff9 1ffb 1ffc 1ffd 1ffe 1fff 1fff 1fff
		    czram[3] = 0000 001f 003f 005f 007f 009f 00bf 00df .... 1eff 1f1f 1f3f 1f5f 1f7f 1f9f 1fbf 1fdf

		airco22b demo mode, fog color: 76 9a c3
		    o_16          0    1    2    3    4    5    6    7 <  >   f8   f9   fa   fb   fc   fd   fe   ff
		    czram[0] = 0000 00e4 0141 0189 01c6 01fb 022c 0258 .... 13bb 13c4 13cd 13d6 13df 13e7 13f0 13f9

		alpinerd (1st course), fog color: c8 c8 c8
		    o_16          0    1    2    3    4    5    6    7 <  >   ec   ed   ee   ef   f0   f1   f2 - ff
		    czram[0] = 00c8 00ca 00cc 00ce 00d0 00d2 00d4 00d6 .... 02a0 02a2 02a4 02a6 02a8 1fff 1fff ....

		alpinr2b (1st course), fog color: ff ff ff
		alpinr2b start of race: - gets gradually filled from left to right, initial contents filled with 1fff? - game should be foggy here
		    o_16          0    1    2    3    4    5    6    7 <  >   67   68   69   6a   6b   6c   6d - ff
		    czram[0] = 01cd 01d7 01e1 01eb 01f5 01ff 0209 0213 .... 05d3 05dd 05e7 05f1 05fb 1fff 1fff ....
		    other banks unused, zerofilled
		alpinr2b mid race: - gets gradually filled from right to left, initial contents above - game should not be foggy here
		    o_16          0    1    2    3    4    5    6    7 <  >   ec   ed   ee   ef   f0   f1   f2 - ff
		    czram[0] = 1ffe 1fff 1fff 1fff 1fff 1fff 1fff 1fff .... 1fff 1fff 1fff 1fff 1fff 1fff 1fff 1fff

		cybrcycc (1st course), fog color: 80 80 c0 - 2nd course has same cz table, but fog color 00 00 00
		    o_16          0    1    2    3    4    5    6    7 <  >   d4   d5   d6   d7   d8   d9   da - ff
		    czram[0] = 0000 0011 0021 0031 0041 0051 0060 0061 .... 04e0 04e4 04e7 04eb 04ee 1fff 1fff ....

		tokyowar, fog color: 80 c0 ff - it uses cztype 1 too by accident? (becomes fogfactor 0)
		    o_16          0    1    2    3    4    5    6    7 <  >   f8   f9   fa   fb   fc   fd   fe   ff
		    czram[0] = 0000 01c5 0244 029f 02e7 0325 035b 038b .... 0eaf 0ec7 0ee0 0efc 0f1b 0f3f 0f6a 0faa
		    czram[1] = 0000 0000 0000 0000 0000 0000 0000 0000 .... 0000 0000 0000 0000 0000 0000 0000 0000
		    czram[2] = 0000 0000 0000 0000 0000 0000 0000 0000 .... 0000 0000 0000 0000 0000 0000 0000 0000
		    czram[3] = 0000 00e8 0191 0206 0265 02b7 0301 0345 .... 1c7e 1cbc 1d00 1d4a 1d9c 1dfb 1e70 1f19

		*/

		/*  czattr: - assumed that it's write-only
		       0    2    4    6    8    a    c    e
		    ^^^^ ^^^^ ^^^^ ^^^^                        cz offset, signed16 per cztype 0,1,2,3
		                        ^^^^                   flags, nybble per cztype 3,2,1,0 - 4 probably means enable
		                             ^^^^              maincpu ram access bank
		                                  ^^^^         flags, nybble per cztype 3,2,1,0 - ?
		                                       ^^^^    ? (only set sometimes in timecris)
		    0000 0000 0000 0000 7555 0000 00e4 0000 // testmode normal - 0=white to black(mid), 1=white to black(weak), 2=white to black(strong), 3=black to white(mid, reverse of 0)
		    7fff 8000 7fff 8000 7555 0000 00e4 0000 // testmode offset - 0=black, 1=white, 2=black, 3=white
		    0000 0000 0000 0000 3111 0000 00e4 0000 // testmode off    - 0=white, 1=white, 2=white, 3=white
		    0000 0000 0000 0000 4444 0000 0000 0000 // propcycl solitar
		    0004 0004 0004 0004 4444 0000 0000 0000 // propcycl out pool
		    00a4 00a4 00a4 00a4 4444 0000 0000 0000 // propcycl in pool
		    ff80 ff80 ff80 ff80 4444 0000 0000 0000 // propcycl ending
		    ff80 ff80 ff80 ff80 0000 0000 0000 0000 // propcycl hs entry
		    0000 0000 0000 0000 0b6c 0000 00e4 0000 // cybrcycc
		    0000 0000 0000 0000 5554 0000 00e4 0000 // airco22b
		    ff01 ff01 0000 0000 4444 0000 0000 0000 // alpinerd
		    0000 0000 0000 0000 4455 0000 000a 0000 // alpinr2b
		    8001 8001 0000 0000 1111 0000 5555 0000 // aquajet (reg 8 is either 1111 or 5555, reg c is usually interlaced)
		    0000 0000 0000 0000 5554 0000 0000 0000 // tokyowar
		*/
		if (~color & 0x80)
		{
			int cztype = flags & 3;
			if (nthword(m_state.m_czattr, 4) & (4 << (cztype * 4)))
			{
				int delta = (int16_t)nthword(m_state.m_czattr, cztype);
				extra.fogcolor.set(0, m_state.m_fog_r, m_state.m_fog_g, m_state.m_fog_b);
				if (direct)
				{
					int cz = ((flags & 0x1fff00) + cz_adjust) >> 8;
					if (cz < 0) cz = 0;
					else if (cz > 0x1fff) cz = 0x1fff;

					int fogfactor = m_state.m_recalc_czram[cztype][cz] + delta;
					if (fogfactor > 0)
					{
						if (fogfactor > 0xff) fogfactor = 0xff;
						extra.fogfactor = fogfactor;
					}
				}
				else
				{
					extra.zfog_enabled = 1;
					extra.cz_sdelta = delta;
					extra.czram = m_state.m_recalc_czram[cztype].get();
				}
			}
		}
	}

	else
	{
		// global fade
		if (m_state.m_mixer_flags & 1)
		{
			extra.pfade_enabled = m_state.m_poly_fade_enabled;
			extra.polycolor.set(0, m_state.m_poly_fade_r, m_state.m_poly_fade_g, m_state.m_poly_fade_b);
		}

		// poly fog
		if (~color & 0x80)
		{
			int cztype = flags & 3;
			int czcolor = cztype & nthbyte(&m_state.m_fog_colormask, cztype);
			extra.fogcolor.set(0, m_state.m_fog_r_per_cztype[czcolor], m_state.m_fog_g_per_cztype[czcolor], m_state.m_fog_b_per_cztype[czcolor]);

			if (direct)
			{
				// direct case, cz value is preset
				int cz = ((flags & 0x1fff00) + cz_adjust) >> 8;
				if (cz < 0) cz = 0;
				else if (cz > 0x1fff) cz = 0x1fff;
				extra.fogfactor = nthbyte(m_state.m_czram, cztype << 13 | cz);
			}
			else
			{
				extra.zfog_enabled = 1;
				extra.czram = (uint8_t*)&m_state.m_czram[cztype << (13-2)];
			}
		}
	}

	render_triangle_fan(m_cliprect, render_delegate(&namcos22_renderer::renderscanline_uvi_full, this), 4, clipverts, clipv);
}


void namcos22_renderer::poly3d_drawsprite(
	screen_device &screen,
	bitmap_rgb32 &dest_bmp,
	uint32_t code,
	uint32_t color,
	int flipx, int flipy,
	int sx, int sy,
	int scalex, int scaley,
	int cz_factor,
	int prioverchar,
	int alpha
)
{
	gfx_element *gfx = m_state.m_gfxdecode->gfx(2);
	int sprite_screen_height = (scaley * gfx->height() + 0x8000) >> 16;
	int sprite_screen_width = (scalex * gfx->width() + 0x8000) >> 16;
	if (sprite_screen_width && sprite_screen_height)
	{
		float fsx = sx;
		float fsy = sy;
		float fwidth = gfx->width();
		float fheight = gfx->height();
		float fsw = sprite_screen_width;
		float fsh = sprite_screen_height;

		namcos22_object_data &extra = object_data_alloc();
		vertex_t vert[4];

		extra.fadefactor = 0;
		extra.fogfactor = 0;
		extra.flags = 0;

		extra.destbase = &dest_bmp;
		extra.alpha = alpha;
		extra.prioverchar = 2 | prioverchar;
		extra.line_modulo = gfx->rowbytes();
		extra.flipx = flipx;
		extra.flipy = flipy;
		extra.pens = &m_state.m_palette->pen(gfx->colorbase() + gfx->granularity() * (color & 0x7f));
		extra.primap = &screen.priority();
		extra.source = gfx->get_data(code % gfx->elements());

		vert[0].x = fsx;
		vert[0].y = fsy;
		vert[0].p[0] = 0;
		vert[0].p[1] = 0;
		vert[1].x = fsx + fsw;
		vert[1].y = fsy;
		vert[1].p[0] = fwidth;
		vert[1].p[1] = 0;
		vert[2].x = fsx + fsw;
		vert[2].y = fsy + fsh;
		vert[2].p[0] = fwidth;
		vert[2].p[1] = fheight;
		vert[3].x = fsx;
		vert[3].y = fsy + fsh;
		vert[3].p[0] = 0;
		vert[3].p[1] = fheight;

		// global fade
		if (m_state.m_mixer_flags & 2)
		{
			extra.fadefactor = m_state.m_screen_fade_factor;
			extra.fadecolor.set(0, m_state.m_screen_fade_r, m_state.m_screen_fade_g, m_state.m_screen_fade_b);
		}

		// fog, 0xfe is a special case for sprite priority over textlayer
		if (~color & 0x80 && cz_factor > 0 && cz_factor != 0xfe)
		{
			// or does it fetch from poly-cz ram? that will break timecris though
			extra.fogfactor = cz_factor;
			extra.fogcolor.set(0, m_state.m_fog_r, m_state.m_fog_g, m_state.m_fog_b);
		}

		render_triangle_fan(m_cliprect, render_delegate(&namcos22_renderer::renderscanline_sprite, this), 2, 4, vert);
	}
}


void namcos22_renderer::render_sprite(screen_device &screen, bitmap_rgb32 &bitmap, struct namcos22_scenenode *node)
{
	// scene clip
	m_cliprect.set(node->data.sprite.cx_min, node->data.sprite.cx_max, node->data.sprite.cy_min, node->data.sprite.cy_max);
	if (m_cliprect.min_x < 0)   m_cliprect.min_x = 0;
	if (m_cliprect.max_x > 639) m_cliprect.max_x = 639;
	if (m_cliprect.min_y < 0)   m_cliprect.min_y = 0;
	if (m_cliprect.max_y > 479) m_cliprect.max_y = 479;

	int offset = 0;

	for (int row = 0; row < node->data.sprite.rows; row++)
	{
		for (int col = 0; col < node->data.sprite.cols; col++)
		{
			int code = node->data.sprite.tile;
			if (node->data.sprite.linktype == 0xff)
				code += offset;
			else
				code += nthword(&m_state.m_spriteram[0x800/4], offset + node->data.sprite.linktype*4);

			poly3d_drawsprite(
				screen,
				bitmap,
				code,
				node->data.sprite.color,
				node->data.sprite.flipx,
				node->data.sprite.flipy,
				node->data.sprite.xpos + col * node->data.sprite.sizex,
				node->data.sprite.ypos + row * node->data.sprite.sizey,
				(node->data.sprite.sizex << 16) / 32,
				(node->data.sprite.sizey << 16) / 32,
				node->data.sprite.cz,
				node->data.sprite.pri,
				0xff - node->data.sprite.translucency
			);
			offset++;
		}
	}
}



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

void namcos22_renderer::free_scenenode(struct namcos22_scenenode *node)
{
	node->next = m_scenenode_cur;
	m_scenenode_cur = node;
}

struct namcos22_scenenode *namcos22_renderer::alloc_scenenode(running_machine &machine, struct namcos22_scenenode *node)
{
	if (node)
	{
		/* use free pool */
		m_scenenode_cur = node->next;
	}
	else
	{
		node = auto_alloc(machine, struct namcos22_scenenode);
	}
	memset(node, 0, sizeof(*node));
	return node;
}

struct namcos22_scenenode *namcos22_renderer::new_scenenode(running_machine &machine, uint32_t zsort, namcos22_scenenode_type type)
{
	struct namcos22_scenenode *node = &m_scenenode_root;
	struct namcos22_scenenode *prev = nullptr;
	int hash = 0;

	for (int i = 0; i < 24; i += NAMCOS22_RADIX_BITS)
	{
		hash = (zsort >> 20) & NAMCOS22_RADIX_MASK;
		struct namcos22_scenenode *next = node->data.nonleaf.next[hash];
		if (!next)
		{
			/* lazily allocate tree node for this radix */
			next = alloc_scenenode(machine, m_scenenode_cur);
			next->type = NAMCOS22_SCENENODE_NONLEAF;
			node->data.nonleaf.next[hash] = next;
		}
		prev = node;
		node = next;
		zsort <<= NAMCOS22_RADIX_BITS;
	}

	if (node->type == NAMCOS22_SCENENODE_NONLEAF)
	{
		/* first leaf allocation on this branch */
		node->type = type;
		return node;
	}
	else
	{
		struct namcos22_scenenode *leaf = alloc_scenenode(machine, m_scenenode_cur);
		leaf->type = type;
		leaf->next = node;
		prev->data.nonleaf.next[hash] = leaf;
		return leaf;
	}
}

void namcos22_renderer::render_scene_nodes(screen_device &screen, bitmap_rgb32 &bitmap, struct namcos22_scenenode *node)
{
	if (node)
	{
		if (node->type == NAMCOS22_SCENENODE_NONLEAF)
		{
			for (int i = NAMCOS22_RADIX_BUCKETS - 1; i >= 0; i--)
			{
				render_scene_nodes(screen, bitmap, node->data.nonleaf.next[i]);
			}
			free_scenenode(node);
		}
		else
		{
			while (node)
			{
				struct namcos22_scenenode *next = node->next;

				switch (node->type)
				{
					case NAMCOS22_SCENENODE_QUAD:
						poly3d_drawquad(screen, bitmap, node);
						break;

					case NAMCOS22_SCENENODE_SPRITE:
						render_sprite(screen, bitmap, node);
						break;

					default:
						fatalerror("invalid node->type\n");
				}
				free_scenenode(node);
				node = next;
			}
		}
	}
}

void namcos22_renderer::render_scene(screen_device &screen, bitmap_rgb32 &bitmap)
{
	struct namcos22_scenenode *node = &m_scenenode_root;
	for (int i = NAMCOS22_RADIX_BUCKETS - 1; i >= 0; i--)
	{
		render_scene_nodes(screen, bitmap, node->data.nonleaf.next[i]);
		node->data.nonleaf.next[i] = nullptr;
	}

	m_clipx = 320;
	m_clipy = 240;
	m_cliprect.set(0, 639, 0, 479);

	wait("render_scene");
}



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

// slave dsp render

float namcos22_state::dspfloat_to_nativefloat(uint32_t val)
{
	int16_t mantissa = (int16_t)val;
	float result = mantissa;//?((float)mantissa):((float)0x10000);
	int exponent = (val >> 16) & 0xff;
	while (exponent < 0x2e)
	{
		result /= 2.0f;
		exponent++;
	}
	return result;
}

/* modal rendering properties */
void namcos22_state::matrix3d_multiply(float a[4][4], float b[4][4])
{
	float temp[4][4];

	for (int row = 0; row < 4;  row++)
	{
		for (int col = 0; col < 4; col++)
		{
			float sum = 0.0f;
			for (int i = 0; i < 4; i++)
			{
				sum += a[row][i] * b[i][col];
			}
			temp[row][col] = sum;
		}
	}

	memcpy(a, temp, sizeof(temp));
}

void namcos22_state::matrix3d_identity(float m[4][4])
{
	for (int r = 0; r < 4; r++)
	{
		for (int c = 0; c < 4; c++)
		{
			m[r][c] = (r == c) ? 1.0 : 0.0;
		}
	}
}

void namcos22_state::transform_point(float *vx, float *vy, float *vz, float m[4][4])
{
	float x = *vx;
	float y = *vy;
	float z = *vz;

	*vx = m[0][0]*x + m[1][0]*y + m[2][0]*z + m[3][0];
	*vy = m[0][1]*x + m[1][1]*y + m[2][1]*z + m[3][1];
	*vz = m[0][2]*x + m[1][2]*y + m[2][2]*z + m[3][2];
}

void namcos22_state::transform_normal(float *nx, float *ny, float *nz, float m[4][4])
{
	float x = *nx;
	float y = *ny;
	float z = *nz;

	*nx = m[0][0]*x + m[1][0]*y + m[2][0]*z;
	*ny = m[0][1]*x + m[1][1]*y + m[2][1]*z;
	*nz = m[0][2]*x + m[1][2]*y + m[2][2]*z;
}

void namcos22_state::register_normals(int32_t addr, float m[4][4])
{
	for (int i = 0; i < 4; i++)
	{
		float nx = dspfixed_to_nativefloat(point_read(addr + i * 3 + 0));
		float ny = dspfixed_to_nativefloat(point_read(addr + i * 3 + 1));
		float nz = dspfixed_to_nativefloat(point_read(addr + i * 3 + 2));

		/* transform normal vector */
		transform_normal(&nx, &ny, &nz, m);
		float dotproduct = nx*m_camera_lx + ny*m_camera_ly + nz*m_camera_lz;
		if (dotproduct < 0.0f)
			dotproduct = 0.0f;

		m_LitSurfaceInfo[m_LitSurfaceCount++] = m_camera_ambient + m_camera_power * dotproduct;
	}
}


void namcos22_state::draw_direct_poly(const uint16_t *src)
{
	if (machine().video().skip_this_frame())
		return;

	int polys_enabled = m_is_ss22 ? nthbyte(m_mixer, 0x1f) & 1 : 1;
	if (!polys_enabled) return;
	/**
	* word#0:
	*    x--------------- end-of-display-list marker
	*    ----xxxxxxxxxxxx priority (lo)
	*
	* word#1:
	*    ----xxxxxxxxxxxx priority (hi)
	*
	* word#2:
	*    xxxxxxxx-------- PAL (high bit is fog enable)
	*    --------xxxx---- CMODE (color mode for texture unpack)
	*    ------------xxxx BN (texture bank)
	*
	* word#3:
	*    -xxxxxxxxxxxxx-- ZC
	*    --------------xx depth cueing table select
	*
	* for each vertex:
	*    xxxx xxxx // u,v
	*
	*    xxxx xxxx // sx,sy
	*
	*    xx-- ---- // BRI
	*    --xx xxxx // zpos
	*/
	uint32_t zsort = ((src[1] & 0xfff) << 12) | (src[0] & 0xfff);
	struct namcos22_scenenode *node = m_poly->new_scenenode(machine(), zsort, NAMCOS22_SCENENODE_QUAD);
	int cztype = src[3] & 3;

	if (m_is_ss22)
	{
		cztype ^= 3; // ? not sure, but this makes testmode look like on a pcb (only 1 pcb checked)
		node->data.quad.cmode = (src[2] & 0x00f0) >> 4;
		node->data.quad.texturebank = (src[2] & 0x000f);
	}
	else
	{
		node->data.quad.cmode = (src[0 + 4] & 0xf000) >> 12;
		node->data.quad.texturebank = (src[1 + 4] & 0xf000) >> 12;
	}
	node->data.quad.cz_adjust = m_cz_adjust;
	node->data.quad.flags = (src[3] << 6 & 0x1fff00) | cztype;
	node->data.quad.color = (src[2] & 0xff00) >> 8;
	src += 4;

	for (int i = 0; i < 4; i++)
	{
		namcos22_polyvertex *p = &node->data.quad.v[i];
		if (m_is_ss22)
		{
			p->u = src[0] >> 4;
			p->v = src[1] >> 4;
		}
		else
		{
			p->u = src[0] & 0x0fff;
			p->v = src[1] & 0x0fff;
		}

		int mantissa = (int16_t)src[5];
		float zf = (float)mantissa;
		int exponent = (src[4]) & 0xff;
		if (mantissa)
		{
			while (exponent < 0x2e)
			{
				zf /= 2.0f;
				exponent++;
			}
			if (m_is_ss22)
				p->z = zf;
			else
				p->z = 1.0f / zf;
		}
		else
		{
			zf = (float)0x10000;
			exponent = 0x40 - exponent;
			while (exponent < 0x2e)
			{
				zf /= 2.0f;
				exponent++;
			}
			p->z = 1.0f / zf;
		}

		p->x = (int16_t)src[2];
		p->y = -(int16_t)src[3];
		p->bri = src[4] >> 8;
		src += 6;
	}

	node->data.quad.direct = 1;
	node->data.quad.vx = 0;
	node->data.quad.vy = 0;
	node->data.quad.vw = -320;
	node->data.quad.vh = -240;
}

/**
 * @brief render a single quad
 *
 * @param flags
 *     00-1.----.01-0.001- ? (always set/clear)
 *     --x-.----.----.---- ?
 *     ----.xx--.----.---- cz table
 *     ----.--xx.----.---- representative z algorithm?
 *     ----.----.--x-.---- backface cull enable
 *     ----.----.----.---x ?
 *
 *      1163 // sky
 *      1262 // score (front)
 *      1242 // score (hinge)
 *      1243 // ?
 *      1063 // n/a
 *      1243 // various (2-sided?)
 *      1263 // everything else (1-sided?)
 *      1663 // ?
 *
 * @param color
 *      xxxxxxxx -------- -------- flat shading factor
 *      -------- x------- -------- fog enable
 *      -------- -xxxxxxx -------- palette select
 *      -------- -------- xxxxxxxx unused?
 *
 * @param polyshift
 *    0x1fbd0 - sky+sea
 *    0x0c350 - mountains
 *    0x09c40 - boats, surf, road, buildings
 *    0x07350 - guardrail
 *    0x061a8 - red car
 */
void namcos22_state::blit_single_quad(bitmap_rgb32 &bitmap, uint32_t color, uint32_t addr, float m[4][4], int32_t polyshift, int flags, int packetformat)
{
	int absolute_priority = m_absolute_priority;
	int32_t zsort;
	float zmin = 0.0f;
	float zmax = 0.0f;
	namcos22_polyvertex v[4];
	int i;

	for (i = 0; i < 4; i++)
	{
		namcos22_polyvertex *pv = &v[i];
		pv->x = point_read(0x8 + i * 3 + addr);
		pv->y = point_read(0x9 + i * 3 + addr);
		pv->z = point_read(0xa + i * 3 + addr);
		transform_point(&pv->x, &pv->y, &pv->z, m);
	}

	/* backface cull one-sided polygons */
	if (flags & 0x0020 &&
		(v[2].x*((v[0].z*v[1].y)-(v[0].y*v[1].z)))+
		(v[2].y*((v[0].x*v[1].z)-(v[0].z*v[1].x)))+
		(v[2].z*((v[0].y*v[1].x)-(v[0].x*v[1].y))) >= 0 &&

		(v[0].x*((v[2].z*v[3].y)-(v[2].y*v[3].z)))+
		(v[0].y*((v[2].x*v[3].z)-(v[2].z*v[3].x)))+
		(v[0].z*((v[2].y*v[3].x)-(v[2].x*v[3].y))) >= 0)
	{
		return;
	}

	for (i = 0; i < 4; i++)
	{
		namcos22_polyvertex *pv = &v[i];
		int bri;

		pv->u = point_read(0 + i * 2 + addr);
		pv->v = point_read(1 + i * 2 + addr);

		if (i == 0 || pv->z > zmax) zmax = pv->z;
		if (i == 0 || pv->z < zmin) zmin = pv->z;

		if (m_LitSurfaceCount)
		{
			// lighting (prelim)
			bri = m_LitSurfaceInfo[m_LitSurfaceIndex % m_LitSurfaceCount];
			if (m_SurfaceNormalFormat == 0x6666)
			{
				if (i == 3)
					m_LitSurfaceIndex++;
			}
			else if (m_SurfaceNormalFormat == 0x4000)
				m_LitSurfaceIndex++;
			else
				logerror("unknown normal format: 0x%x\n", m_SurfaceNormalFormat);
		}
		else if (packetformat & 0x40)
		{
			// gourad shading
			bri = (point_read(i + addr) >> 16) & 0xff;
		}
		else
		{
			// flat shading
			bri = color >> 16 & 0xff;
		}

		pv->bri = bri;
	}

	if (zmin < 0.0f) zmin = 0.0f;
	if (zmax < 0.0f) zmax = 0.0f;

	switch (flags & 0x300)
	{
		case 0x000:
			zsort = (int32_t)zmin;
			break;

		case 0x100:
			zsort = (int32_t)zmax;
			break;

		default:
			zsort = (int32_t)((zmin + zmax) / 2.0f);
			break;
	}

	/* relative: representative z + shift values
	* 1x.xxxx.xxxxxxxx.xxxxxxxx fixed z value
	* 0x.xx--.--------.-------- absolute priority shift
	* 0-.--xx.xxxxxxxx.xxxxxxxx z-representative value shift
	*/
	if (polyshift & 0x200000)
		zsort = polyshift & 0x1fffff;
	else
	{
		zsort += signed18(polyshift);
		absolute_priority += (polyshift & 0x1c0000) >> 18;
	}

	if (m_objectshift & 0x200000)
		zsort = m_objectshift & 0x1fffff;
	else
	{
		zsort += signed18(m_objectshift);
		absolute_priority += (m_objectshift & 0x1c0000) >> 18;
	}

	if (zsort < 0) zsort = 0;
	else if (zsort > 0x1fffff) zsort = 0x1fffff;
	absolute_priority &= 7;
	zsort |= (absolute_priority << 21);

	// allocate quad
	struct namcos22_scenenode *node = m_poly->new_scenenode(machine(), zsort, NAMCOS22_SCENENODE_QUAD);
	node->data.quad.cmode = (v[0].u >> 12) & 0xf;
	node->data.quad.texturebank = (v[0].v >> 12) & 0xf;
	node->data.quad.color = (color >> 8) & 0xff;
	node->data.quad.flags = flags >> 10 & 3;
	node->data.quad.cz_adjust = m_cz_adjust;

	for (i = 0; i < 4; i++)
	{
		namcos22_polyvertex *p = &node->data.quad.v[i];
		p->x = v[i].x * m_camera_zoom;
		p->y = v[i].y * m_camera_zoom;
		p->z = v[i].z;
		p->u = v[i].u & 0xfff;
		p->v = v[i].v & 0xfff;
		p->bri = v[i].bri;
	}

	node->data.quad.direct = 0;
	node->data.quad.vx = m_camera_vx;
	node->data.quad.vy = m_camera_vy;
	node->data.quad.vw = m_camera_vw;
	node->data.quad.vh = m_camera_vh;
}


void namcos22_state::blit_quads(bitmap_rgb32 &bitmap, int32_t addr, float m[4][4], int32_t base)
{
//  int additionalnormals = 0;
	int chunklength = point_read(addr++);
	int finish = addr + chunklength;

	if (chunklength > 0x100)
		fatalerror("bad packet length\n");

	while (addr < finish)
	{
		int packetlength = point_read(addr++);
		int packetformat = point_read(addr + 0);
		int flags, color, bias;

		/**
		* packetformat:
		*      800000 final packet in chunk
		*      080000 ?
		*      020000 color word exists?
		*      010000 z-offset word exists?
		*      002000 ?
		*      001000 z-offset word exists?
		*      000400 ?
		*      000080 tex# or UV or CMODE?
		*      000040 use I
		*      000001 ?
		*/
		switch (packetlength)
		{
			case 0x17:
				/**
				* word 0: opcode (8a24c0)
				* word 1: flags
				* word 2: color
				*/
				flags = point_read(addr + 1);
				color = point_read(addr + 2);
				bias = 0;
				blit_single_quad(bitmap, color, addr + 3, m, bias, flags, packetformat);
				break;

			case 0x18:
				/**
				* word 0: opcode (0b3480 for first N-1 quads or 8b3480 for final quad in primitive)
				* word 1: flags
				* word 2: color
				* word 3: depth bias
				*/
				flags = point_read(addr + 1);
				color = point_read(addr + 2);
				bias  = point_read(addr + 3);
				blit_single_quad(bitmap, color, addr + 4, m, bias, flags, packetformat);
				break;

			case 0x10: /* vertex lighting */
				/*
				333401 (opcode)
				000000  [count] [type]
				000000  000000  007fff // normal vector
				000000  000000  007fff // normal vector
				000000  000000  007fff // normal vector
				000000  000000  007fff // normal vector
				*/
//              additionalnormals = point_read(addr+2);
				m_SurfaceNormalFormat = point_read(addr + 3);
				m_LitSurfaceCount = 0;
				m_LitSurfaceIndex = 0;
				register_normals(addr + 4, m);
				break;

			case 0x0d: /* additional normals */
				/*
				300401 (opcode)
				007b09 ffdd04 0004c2
				007a08 ffd968 0001c1
				ff8354 ffe401 000790
				ff84f7 ffdd04 0004c2
				*/
				register_normals(addr + 1, m);
				break;

			default:
				break;
		}
		addr += packetlength;
	}
}

void namcos22_state::blit_polyobject(bitmap_rgb32 &bitmap, int code, float m[4][4])
{
	uint32_t addr1 = point_read(code);
	m_LitSurfaceCount = 0;
	m_LitSurfaceIndex = 0;

	for (;;)
	{
		int32_t addr2 = point_read(addr1++);
		if (addr2 < 0)
			break;
		blit_quads(bitmap, addr2, m, code);
	}
}


/**
 * master DSP can write directly to render device via port 0xc.
 * This is used for "direct drawn" polygons, and "direct draw from point rom"
 * feature - both opcodes exist in Ridge Racer's display-list processing
 *
 * record format:
 *  header (3 words)
 *      polyshift
 *      color
 *      flags
 *
 *  per-vertex data (4*6 words)
 *      u,v
 *      sx,sy
 *      intensity;z.exponent
 *      z.mantissa
 *
 * master DSP can specify 3d objects indirectly (along with view transforms),
 * via the "transmit" PDP opcode.  the "render device" sends quad data to the slave DSP
 * viewspace clipping and projection
 *
 * most "3d object" references are 0x45 and greater.  references less than 0x45 are "special"
 * commands, using a similar point rom format.  the point rom header may point to point ram.
 *
 * slave DSP reads records via port 4
 * its primary purpose is applying lighting calculations
 * the slave DSP forwards draw commands to a "draw device"
 */

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

/**
 * 0xfffd
 * 0x0: transform
 * 0x1
 * 0x2
 * 0x5: transform
 * >=0x45: draw primitive
 */
void namcos22_state::slavesim_handle_bb0003(const int32_t *src)
{
	/*
	    bb0003 or 3b0003

	    14.00c8            light.ambient     light.power
	    01.0000            ?                 light.dx
	    06.5a82            window priority   light.dy
	    00.a57e            ?                 light.dz

	    c8.0081            vx=200,vy=129
	    29.6092            zoom = 772.5625
	    1e.95f8 1e.95f8    0.5858154296875   0.5858154296875 // 452
	    1e.b079 1e.b079    0.6893463134765   0.6893463134765 // 532
	    29.58e8            711.25 (border? see time crisis)

	    7ffe 0000 0000
	    0000 7ffe 0000
	    0000 0000 7ffe
	*/
	m_camera_ambient = src[0x1] >> 16;
	m_camera_power = src[0x1] & 0xffff;

	m_camera_lx = dspfixed_to_nativefloat(src[0x2]);
	m_camera_ly = dspfixed_to_nativefloat(src[0x3]);
	m_camera_lz = dspfixed_to_nativefloat(src[0x4]);

	m_absolute_priority = src[0x3] >> 16;
	m_camera_vx = (int16_t)(src[5] >> 16);
	m_camera_vy = (int16_t)(src[5] & 0xffff);
	m_camera_zoom = dspfloat_to_nativefloat(src[6]);
	m_camera_vw = dspfloat_to_nativefloat(src[7]) * m_camera_zoom;
	m_camera_vh = dspfloat_to_nativefloat(src[9]) * m_camera_zoom;

	m_viewmatrix[0][0] = dspfixed_to_nativefloat(src[0x0c]);
	m_viewmatrix[1][0] = dspfixed_to_nativefloat(src[0x0d]);
	m_viewmatrix[2][0] = dspfixed_to_nativefloat(src[0x0e]);

	m_viewmatrix[0][1] = dspfixed_to_nativefloat(src[0x0f]);
	m_viewmatrix[1][1] = dspfixed_to_nativefloat(src[0x10]);
	m_viewmatrix[2][1] = dspfixed_to_nativefloat(src[0x11]);

	m_viewmatrix[0][2] = dspfixed_to_nativefloat(src[0x12]);
	m_viewmatrix[1][2] = dspfixed_to_nativefloat(src[0x13]);
	m_viewmatrix[2][2] = dspfixed_to_nativefloat(src[0x14]);

	transform_normal(&m_camera_lx, &m_camera_ly, &m_camera_lz, m_viewmatrix);
}

void namcos22_state::slavesim_handle_200002(bitmap_rgb32 &bitmap, const int32_t *src)
{
	if (m_PrimitiveID >= 0x45)
	{
		float m[4][4]; /* row major */

		matrix3d_identity(m);

		m[0][0] = dspfixed_to_nativefloat(src[0x1]);
		m[1][0] = dspfixed_to_nativefloat(src[0x2]);
		m[2][0] = dspfixed_to_nativefloat(src[0x3]);

		m[0][1] = dspfixed_to_nativefloat(src[0x4]);
		m[1][1] = dspfixed_to_nativefloat(src[0x5]);
		m[2][1] = dspfixed_to_nativefloat(src[0x6]);

		m[0][2] = dspfixed_to_nativefloat(src[0x7]);
		m[1][2] = dspfixed_to_nativefloat(src[0x8]);
		m[2][2] = dspfixed_to_nativefloat(src[0x9]);

		m[3][0] = src[0xa]; /* xpos */
		m[3][1] = src[0xb]; /* ypos */
		m[3][2] = src[0xc]; /* zpos */

		matrix3d_multiply(m, m_viewmatrix);
		blit_polyobject(bitmap, m_PrimitiveID, m);
	}
	else if (m_PrimitiveID != 0 && m_PrimitiveID != 2)
	{
		logerror("slavesim_handle_200002:unk code=0x%x\n", m_PrimitiveID);
		// ridgerac title screen waving flag: 0x5
	}
}

void namcos22_state::slavesim_handle_300000(const int32_t *src)
{
	m_viewmatrix[0][0] = dspfixed_to_nativefloat(src[1]);
	m_viewmatrix[1][0] = dspfixed_to_nativefloat(src[2]);
	m_viewmatrix[2][0] = dspfixed_to_nativefloat(src[3]);

	m_viewmatrix[0][1] = dspfixed_to_nativefloat(src[4]);
	m_viewmatrix[1][1] = dspfixed_to_nativefloat(src[5]);
	m_viewmatrix[2][1] = dspfixed_to_nativefloat(src[6]);

	m_viewmatrix[0][2] = dspfixed_to_nativefloat(src[7]);
	m_viewmatrix[1][2] = dspfixed_to_nativefloat(src[8]);
	m_viewmatrix[2][2] = dspfixed_to_nativefloat(src[9]);
}

void namcos22_state::slavesim_handle_233002(const int32_t *src)
{
	/*
	00233002
	   00000000 // cz adjust (signed24)
	   0003dd00 // z bias adjust
	   001fffff // far plane?
	   00007fff 00000000 00000000
	   00000000 00007fff 00000000
	   00000000 00000000 00007fff
	   00000000 00000000 00000000
	*/
	m_cz_adjust = signed24(src[1]);
	m_objectshift = src[2];
}

void namcos22_state::simulate_slavedsp(bitmap_rgb32 &bitmap)
{
	const int32_t *src = 0x300 + (int32_t *)m_polygonram.target();
	int16_t len;

	matrix3d_identity(m_viewmatrix);

	if (m_is_ss22)
	{
		src += 4; /* FFFE 0400 */
	}
	else
	{
		src--;
	}

	for (;;)
	{
		int16_t next;
		m_PrimitiveID = *src++;
		len  = (int16_t)*src++;

		switch (len)
		{
			case 0x15:
				slavesim_handle_bb0003(src); /* define viewport */
				break;

			case 0x10:
				slavesim_handle_233002(src); /* set modal rendering options */
				break;

			case 0x0a:
				slavesim_handle_300000(src); /* modify view transform */
				break;

			case 0x0d:
				slavesim_handle_200002(bitmap, src); /* render primitive */
				break;

			default:
				logerror("unk 3d data(%d) addr=0x%x!", len, (int)(src-(int32_t*)m_polygonram.target()));
				{
					int i;
					for (i = 0; i < len; i++)
					{
						logerror(" %06x", src[i] & 0xffffff);
					}
					logerror("\n");
				}
				return;
		}

		/* hackery! commands should be streamed, not parsed here */
		src += len;
		src++; /* always 0xffff */
		next = (int16_t)*src++; /* link to next command */
		if ((next & 0x7fff) != (src - (int32_t *)m_polygonram.target()))
		{
			/* end of list */
			break;
		}
	}
}

void namcos22_state::draw_polygons(bitmap_rgb32 &bitmap)
{
	if (m_slave_simulation_active)
	{
		simulate_slavedsp(bitmap);
		m_poly->wait("draw_polygons");
	}
}



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

void namcos22_state::draw_sprite_group(bitmap_rgb32 &bitmap, const rectangle &cliprect, const uint32_t *src, const uint32_t *attr, int num_sprites, int deltax, int deltay, int y_lowres)
{
	for (int i = 0; i < num_sprites; i++)
	{
		/*
		src[0]
		    xxxx.xxxx.xxxx.xxxx | ----.----.----.----  x pos
		    ----.----.----.---- | xxxx.xxxx.xxxx.xxxx  y pos

		src[1]
		    xxxx.xxxx.xxxx.xxxx | ----.----.----.----  x size
		    ----.----.----.---- | xxxx.xxxx.xxxx.xxxx  y size

		src[2]
		    xxxx.x---.----.---- | ----.----.----.----  no function
		    ----.-xxx.----.---- | ----.----.----.----  clip target
		    ----.----.xxxx.xxxx | ----.----.----.----  linktype
		    ----.----.----.---- | xxxx.xx--.----.----  no function(?) - set in airco22b
		    ----.----.----.---- | ----.--x-.----.----  right justify
		    ----.----.----.---- | ----.---x.----.----  bottom justify
		    ----.----.----.---- | ----.----.x---.----  flipx
		    ----.----.----.---- | ----.----.-xxx.----  cols
		    ----.----.----.---- | ----.----.----.x---  flipy
		    ----.----.----.---- | ----.----.----.-xxx  rows

		src[3]
		    xxxx.xxxx.xxxx.xxxx | ----.----.----.----  tile number
		    ----.----.----.---- | xxxx.xxxx.----.----  translucency
		    ----.----.----.---- | ----.----.xxxx.xxxx  no function(?) - set in timecris

		attr[0]
		    xxxx.xxxx.----.---- | ----.----.----.----  no function
		    ----.----.xxxx.xxxx | xxxx.xxxx.xxxx.xxxx  z pos

		attr[1]
		    xxxx.xxxx.----.---- | ----.----.----.----  no function
		    ----.----.x---.---- | ----.----.----.----  cz enable
		    ----.----.-xxx.xxxx | ----.----.----.----  color
		    ----.----.----.---- | xxxx.xxxx.----.----  no function(?) - set in airco22b, propcycl
		    ----.----.----.---- | ----.----.xxxx.xxxx  cz factor (fog aka depth cueing)
		*/
		int xpos = (src[0] >> 16) - deltax;
		int ypos = (src[0] & 0xffff) - deltay;
		int sizex = src[1] >> 16;
		int sizey = src[1] & 0xffff;
		int flipy = src[2] >> 3 & 0x1;
		int rows = src[2] & 0x7;
		int linktype = (src[2] & 0x00ff0000) >> 16;
		int flipx = (src[2] >> 7) & 0x1;
		int cols = (src[2] >> 4) & 0x7;
		uint32_t code = src[3];
		int tile = code >> 16;
		int translucency = (code & 0xff00) >> 8;

		uint32_t zcoord = attr[0] & 0x00ffffff;
		int color = attr[1] >> 16;
		int cz = attr[1] & 0xff;

		// priority over textlayer, trusted by testmode and timecris
		int pri = ((attr[1] & 0xffff) == 0x00fe);

		// set window clipping
		int clip = src[2] >> 23 & 0xe;
		int cx_min = -deltax + (int16_t)(m_spriteram[0x80|clip] >> 16);
		int cx_max = -deltax + (int16_t)(m_spriteram[0x80|clip] & 0xffff);
		int cy_min = -deltay + (int16_t)(m_spriteram[0x81|clip] >> 16);
		int cy_max = -deltay + (int16_t)(m_spriteram[0x81|clip] & 0xffff);

		if (rows == 0) rows = 8;
		if (cols == 0) cols = 8;

		/* right justify */
		if (src[2] & 0x0200)
			xpos -= sizex * cols - 1;

		/* bottom justify */
		if (src[2] & 0x0100)
			ypos -= sizey * rows - 1;

		if (flipy)
		{
			ypos += sizey * rows - 1;
			sizey = -sizey;
		}

		if (flipx)
		{
			xpos += sizex * cols - 1;
			sizex = -sizex;
		}

		if (y_lowres)
		{
			sizey *= 2;
			ypos *= 2;
		}

		if (sizex && sizey)
		{
			struct namcos22_scenenode *node = m_poly->new_scenenode(machine(), zcoord, NAMCOS22_SCENENODE_SPRITE);

			node->data.sprite.tile = tile;
			node->data.sprite.flipx = flipx;
			node->data.sprite.flipy = flipy;
			node->data.sprite.cols = cols;
			node->data.sprite.rows = rows;
			node->data.sprite.linktype = linktype;
			node->data.sprite.xpos = xpos;
			node->data.sprite.ypos = ypos;
			node->data.sprite.cx_min = cx_min;
			node->data.sprite.cx_max = cx_max;
			node->data.sprite.cy_min = cy_min;
			node->data.sprite.cy_max = cy_max;
			node->data.sprite.sizex = sizex;
			node->data.sprite.sizey = sizey;
			node->data.sprite.translucency = translucency;
			node->data.sprite.color = color;
			node->data.sprite.cz = cz;
			node->data.sprite.pri = pri;
		}
		src += 4;
		attr += 2;
	}
}

void namcos22_state::draw_sprites(bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const uint32_t *src;
	const uint32_t *attr;

#if 0 // show reg contents
	int i;
	char msg1[0x1000]={0}, msg2[0x1000]={0};
	// 980000-98023f (spriteram header)
	for (i=0x00;i<0x02;i++) {
		sprintf(msg2,"98%04X %08X %08X %08X %08X\n",i*16,m_spriteram[i*4+0],m_spriteram[i*4+1],m_spriteram[i*4+2],m_spriteram[i*4+3]);
		strcat(msg1,msg2);
	}
	for (i=0x20;i<0x24;i++) {
		sprintf(msg2,"98%04X %08X %08X %08X %08X\n",i*16,m_spriteram[i*4+0],m_spriteram[i*4+1],m_spriteram[i*4+2],m_spriteram[i*4+3]);
		strcat(msg1,msg2);
	}
	strcat(msg1,"\n");
	// 940000-94007c (vics control)
	for (i=0x00;i<0x08;i++) {
		sprintf(msg2,"94%04X %08X %08X %08X %08X\n",i*16,m_vics_control[i*4+0],m_vics_control[i*4+1],m_vics_control[i*4+2],m_vics_control[i*4+3]);
		strcat(msg1,msg2);
	}
	if (machine().input().code_pressed(KEYCODE_S))
		popmessage("%s",msg1);
	else popmessage("[S] shows sprite/vics regs");
#endif
	/*
	    0x980000:   00060000 00010000 02ff0000 000007ff
	                   ^                                 misc control
	                    ^^^^                             base
	                         ^^^^                        base + num sprites
	                             ^^^^     ^^^^           deltax
	                                           ^^^^      deltay

	    0x980010:   00200020 028004ff 032a0509 00000000
	                ^^^^^^^^                             character size?
	                         ^^^^^^^^                    window-x related?
	                                  ^^^^^^^^           window-y related?

	    0x980200-0x98023f:   window clipping registers
	    0x980400-0x9805ff:   hzoom table
	    0x980600-0x9807ff:   vzoom table
	    0x980800-0x980fff:   link table

	    eight words per sprite, start address at 0x984000
	    additional sorting/color data for sprite at 0x9a0000
	*/

	/* misc control bits function:
	    bit 0:      sprites on
	    bit 1:      ??? (always set, except in alpinr2b. it's not x-resolution)
	    bit 2:      y-resolution? (always set, except in cybrcycc)
	*/
	int sprites_on = (m_spriteram[0] >> 16 & 1) ? 0 : 1;
	int y_lowres = (m_spriteram[0] >> 16 & 4) ? 0 : 1;

	int deltax = (m_spriteram[1] & 0xffff) + (m_spriteram[2] & 0xffff) + 0x2d;
	int deltay = (m_spriteram[3] >> 16) + (0x2a >> y_lowres);

	int base = m_spriteram[0] & 0xffff; // alpinesa/alpinr2b
	int num_sprites = ((m_spriteram[1] >> 16) - base) + 1;

	// airco22b doesn't use spriteset #1
	if (m_gametype == NAMCOS22_AIR_COMBAT22)
		num_sprites = 0;

	if (sprites_on && num_sprites > 0 && num_sprites < 0x400)
	{
		src = &m_spriteram[0x04000/4 + base*4];
		attr = &m_spriteram[0x20000/4 + base*2];
		draw_sprite_group(bitmap, cliprect, src, attr, num_sprites, deltax, deltay, y_lowres);
	}

	/* VICS RAM provides two additional banks (also many unknown regs here) */
	/*
	0x940000 -x------       sprite chip busy?
	0x940018 xxxx----       clr.w   $940018.l

	0x940030 xxxxxxxx       0x0600000 - misc control
	0x940034 xxxxxxxx       0x3070b0f

	0x940040 xxxxxxxx       sprite attribute size             high bit means busy?
	0x940048 xxxxxxxx       sprite attribute list baseaddr    high bit means busy?
	0x940050 xxxxxxxx       sprite color size                 high bit means busy?
	0x940058 xxxxxxxx       sprite color list baseaddr        high bit means busy?

	0x940060..0x94007c      set#2
	*/

	sprites_on = (m_vics_control[0x30/4] >> 24 & 1) ? 0 : 1;
	y_lowres = (m_vics_control[0x30/4] >> 24 & 4) ? 0 : 1;

	// where do the games store the number of sprites to be processed by vics???
	// the current default implementation (using spritelist size) is clearly wrong and causes problems in dirtdash and airco22b
	num_sprites = m_vics_control[0x40/4] >> 4 & 0x1ff; // no +1

	// dirtdash sprite list starts at xxx4, number of sprites is stored in xxx0, it doesn't use set#2
	if (m_gametype == NAMCOS22_DIRT_DASH)
		num_sprites = (m_vics_data[(m_vics_control[0x48/4] & 0x4000)/4] & 0xff) + 1;

	if (sprites_on && num_sprites > 0)
	{
		src = &m_vics_data[(m_vics_control[0x48/4] & 0xffff)/4];
		attr = &m_vics_data[(m_vics_control[0x58/4] & 0xffff)/4];
		draw_sprite_group(bitmap, cliprect, src, attr, num_sprites, deltax, deltay, y_lowres);
	}

	num_sprites = m_vics_control[0x60/4] >> 4 & 0x1ff; // no +1

	// airco22b number of sprites for set#2 is stored in set#1 - it does not use set 1, or main set for sprites
	if (m_gametype == NAMCOS22_AIR_COMBAT22)
	{
		sprites_on = (m_vics_data[(m_vics_control[0x48/4] & 0xffff)/4] >> 16 & 1) ? 0 : 1;
		num_sprites = (m_vics_data[(m_vics_control[0x48/4] & 0xffff)/4+1] >> 16) + 1;
	}

	if (sprites_on && num_sprites > 0)
	{
		src = &m_vics_data[(m_vics_control[0x68/4] & 0xffff)/4];
		attr = &m_vics_data[(m_vics_control[0x78/4] & 0xffff)/4];
		draw_sprite_group(bitmap, cliprect, src, attr, num_sprites, deltax, deltay, y_lowres);
	}
}

READ32_MEMBER(namcos22_state::namcos22s_vics_control_r)
{
	uint32_t ret = m_vics_control[offset];

	switch (offset*4)
	{
		// reg 0, status register?
		// high byte is read in timecris and lower half is expected to be 0
		case 0x00:
			ret = 0;
			break;

		// sprite attr/color size regs: high bit is busy/ready?
		// dirtdash reads these and waits for it to become 0
		case 0x40: case 0x50: case 0x60: case 0x70:
			ret &= 0x7fffffff;
			break;

		default:
			break;
	}
	return ret;
}

WRITE32_MEMBER(namcos22_state::namcos22s_vics_control_w)
{
	COMBINE_DATA(&m_vics_control[offset]);
}



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

TILE_GET_INFO_MEMBER(namcos22_state::get_text_tile_info)
{
	uint16_t data = nthword(m_textram, tile_index);
	/**
	* xxxx.----.----.---- palette select
	* ----.xx--.----.---- flip
	* ----.--xx.xxxx.xxxx code
	*/
	SET_TILE_INFO_MEMBER(0, data & 0x03ff, data >> 12, TILE_FLIPYX((data & 0x0c00) >> 10));
}

WRITE32_MEMBER(namcos22_state::namcos22_textram_w)
{
	COMBINE_DATA(&m_textram[offset]);
	m_bgtilemap->mark_tile_dirty(offset * 2);
	m_bgtilemap->mark_tile_dirty(offset * 2 + 1);
}

WRITE32_MEMBER(namcos22_state::namcos22_cgram_w)
{
	COMBINE_DATA(&m_cgram[offset]);
	m_gfxdecode->gfx(0)->mark_dirty(offset/32);
}

READ32_MEMBER(namcos22_state::namcos22_tilemapattr_r)
{
	switch (offset)
	{
		case 2:
		{
			uint16_t lo, hi = (m_tilemapattr[offset] & 0xffff0000) >> 16;
			// assume current scanline, 0x1ff if in vblank (used in alpinesa)
			// or maybe relative to posirq?
			if (m_screen->vblank()) lo = 0x1ff;
			else lo = m_screen->vpos() >> 1;
			// dirtdash has slowdowns if high bit is clear, why??
			return hi << 16 | lo | 0x8000;
		}

		case 3:
			// don't know, maybe also scanline related
			// timecris reads it everytime the gun triggers and will decline if it's 0xffff
			return 0;

		default:
			break;
	}

	return m_tilemapattr[offset];
}

WRITE32_MEMBER(namcos22_state::namcos22_tilemapattr_w)
{
	/*
	0.hiword    R/W     x offset
	0.loword    R/W     y offset, bit 9 for interlacing?(cybrcomm, tokyowar)
	1.hiword    R/W     ??? always 0x006e?
	1.loword    ?       unused?
	2.hiword    R/W     posirq scanline? - not hooked up yet
	2.loword    R       assume current scanline
	3.hiword    ?       unused?
	3.loword    R       ???
	*/
	COMBINE_DATA(&m_tilemapattr[offset]);
//  popmessage("%08x\n%08x\n%08x\n%08x\n",m_tilemapattr[0],m_tilemapattr[1],m_tilemapattr[2],m_tilemapattr[3]);
}


/**
 * Spot RAM affects how the text layer is blended with the scene, it is not yet known exactly how.
 * It isn't directly memory mapped, but rather ports are used to populate and poll it.
 *
 * See Time Crisis "SPOT RAM" self test for sample use, maybe also used in-game, but where?
 * It is also used in Dirt Dash night section. Other games don't seem to use it.
 *
 * 0x860000: set read and write address (TRUSTED by Tokyo Wars POST)
 * 0x860002: write data
 * 0x860004: read data
 * 0x860006: enable
*/

// tokyowar and timecris test ram 000-4ff, but the only practically usable part seems to be 000-3ff
#define SPOTRAM_SIZE (0x800)

/*
RAM looks like it is a 256 * 4 words table
testmode:
 offs: 0000 0001 0002 0003 - 03f4 03f5 03f6 03f7 03f8 03f9 03fa 03fb 03fc 03fd 03fe 03ff
 data: 00fe 00fe 00fe 00fe - 0001 0001 0001 0001 0000 0000 0000 0000 ffff ffff ffff ffff

is the high byte of each word used? it's usually 00, and in dirtdash always 02

low byte of each word:
 byte 0 looks like a blend factor
 bytes 1,2,3 a secondary brightness factor per rgb channel(?)

*/

READ32_MEMBER(namcos22_state::namcos22s_spotram_r)
{
	if (offset == 1)
	{
		// read
		if (m_spot_read_address >= SPOTRAM_SIZE)
		{
			m_spot_read_address = 0;
		}
		return m_spotram[m_spot_read_address++] << 16;
	}
	return 0;
}

WRITE32_MEMBER(namcos22_state::namcos22s_spotram_w)
{
	if (offset == 0)
	{
		if (ACCESSING_BITS_16_31)
		{
			// set address
			m_spot_read_address  = data >> (16 + 1);
			m_spot_write_address = data >> (16 + 1);
		}
		else
		{
			// write
			if (m_spot_write_address >= SPOTRAM_SIZE)
			{
				m_spot_write_address = 0;
			}
			m_spotram[m_spot_write_address++] = data;
		}
	}
	else
	{
		if (ACCESSING_BITS_0_15)
		{
			// enable
			m_spot_enable = data & 1;
		}
	}
}

void namcos22_state::namcos22s_mix_text_layer(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect, int prival)
{
	const pen_t *pens = m_palette->pens();
	uint16_t *src;
	uint32_t *dest;
	uint8_t *pri;

	// prepare alpha
	uint8_t alpha_check12 = nthbyte(m_mixer, 0x12);
	uint8_t alpha_check13 = nthbyte(m_mixer, 0x13);
	uint8_t alpha_mask    = nthbyte(m_mixer, 0x14);
	uint8_t alpha_factor  = nthbyte(m_mixer, 0x15);

	// prepare spot
	int spot_flags = m_mixer_flags >> 16;
	bool spot_enabled = (spot_flags & 1) && (spot_flags & 0xc);
	int spot_limit = (spot_flags & 2) ? m_spot_limit : 0xff;

	// prepare fader
	bool fade_enabled = (m_mixer_flags & 2) && m_screen_fade_factor;
	int fade_factor = 0xff - m_screen_fade_factor;
	rgbaint_t fade_color(0, m_screen_fade_r, m_screen_fade_g, m_screen_fade_b);

	// mix textlayer with poly/sprites
	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		src = &m_mix_bitmap->pix16(y);
		dest = &bitmap.pix32(y);
		pri = &screen.priority().pix8(y);
		for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
		{
			// skip if transparent or under poly/sprite
			if (pri[x] == prival)
			{
				rgbaint_t rgb(pens[src[x]]);

				// apply alpha
				if (alpha_factor)
				{
					uint8_t pen = src[x] & 0xff;
					if ((pen & 0xf) == alpha_mask || pen == alpha_check12 || pen == alpha_check13)
					{
						rgb.blend(rgbaint_t(dest[x]), 0xff - alpha_factor);
					}
				}

				// apply spot
				if (spot_enabled)
				{
					uint8_t pen = src[x] & 0xff;
					rgbaint_t mix(dest[x]);
					if (spot_flags & 8)
					{
						// mix with per-channel brightness
						rgbaint_t shade(0, (0xff - (m_spotram[pen << 2 | 1] & 0xff)) << 2, (0xff - (m_spotram[pen << 2 | 2] & 0xff)) << 2, (0xff - (m_spotram[pen << 2 | 3] & 0xff)) << 2);
						mix.scale_and_clamp(shade);
					}

					int spot_factor = 0xff - (m_spotram[pen << 2] & 0xff);
					if (spot_factor < spot_limit)
					{
						rgb.blend(mix, spot_factor);
					}
				}

				if (fade_enabled)
				{
					rgb.blend(fade_color, fade_factor);
				}

				dest[x] = rgb.to_rgba();
			}
		}
	}
}

void namcos22_state::namcos22_mix_text_layer(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	const pen_t *pens = m_palette->pens();
	uint16_t *src;
	uint32_t *dest;
	uint8_t *pri;

	const uint8_t *rlut = &m_gamma_proms[0x000];
	const uint8_t *glut = &m_gamma_proms[0x100];
	const uint8_t *blut = &m_gamma_proms[0x200];

	// prepare fader and shadow factor
	bool fade_enabled = m_mixer_flags & 2 && m_poly_fade_enabled;
	bool shadow_enabled = (m_mixer_flags & 0x100) != 0; // ? (ridgerac is the only game not using shadow)

	rgbaint_t fade_color(0, m_poly_fade_r, m_poly_fade_g, m_poly_fade_b);
	rgbaint_t rgb_mix[3] = {
		rgbaint_t(0, nthbyte(m_mixer, 0x08), nthbyte(m_mixer, 0x09), nthbyte(m_mixer, 0x0a)), // pen c
		rgbaint_t(0, nthbyte(m_mixer, 0x0b), nthbyte(m_mixer, 0x0c), nthbyte(m_mixer, 0x0d)), // pen d
		rgbaint_t(0, nthbyte(m_mixer, 0x0e), nthbyte(m_mixer, 0x0f), nthbyte(m_mixer, 0x10))  // pen e
	};

	// mix textlayer with poly/sprites
	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		src = &m_mix_bitmap->pix16(y);
		dest = &bitmap.pix32(y);
		pri = &screen.priority().pix8(y);
		for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
		{
			uint32_t pixel = dest[x];

			// skip if transparent or under poly
			if (pri[x] == 2)
			{
				// apply shadow
				rgbaint_t rgb;
				switch (src[x] & 0xff)
				{
					case 0xfc:
					case 0xfd:
					case 0xfe:
						if (shadow_enabled)
						{
							rgb.set(pixel);
							rgb.scale_and_clamp(rgb_mix[(src[x] & 0xf) - 0xc]);
							break;
						}
						// (fall through)
					default:
						rgb.set(pens[src[x]]);
						break;
				}

				if (fade_enabled)
				{
					rgb.scale_and_clamp(fade_color);
				}

				// BTANB note: fading to white does not affect color channels set to 00,
				// eg. a rr-gg-bb of 3f-7f-00 will fade to ff-ff-00 and not ff-ff-ff
				// seen in victlapw attract mode

				pixel = rgb.to_rgba();
			}

			// final mix gamma here
			dest[x] = (rlut[(pixel >> 16) & 0xff] << 16) | (glut[(pixel >> 8) & 0xff] << 8) | blut[pixel & 0xff];
		}
	}
}

void namcos22_state::draw_text_layer(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	int scroll_x = (m_tilemapattr[0] >> 16) - 0x35c;
	int scroll_y = m_tilemapattr[0] & 0xffff;

	m_bgtilemap->set_scrollx(0, scroll_x & 0x3ff);
	m_bgtilemap->set_scrolly(0, scroll_y & 0x3ff);
	m_bgtilemap->set_palette_offset(m_text_palbase * 256);

	if (m_is_ss22)
	{
		m_bgtilemap->draw(screen, *m_mix_bitmap, cliprect, 0, 4, 4);
		namcos22s_mix_text_layer(screen, bitmap, cliprect, 4);
	}
	else
	{
		m_bgtilemap->draw(screen, *m_mix_bitmap, cliprect, 0, 2, 3);
		namcos22_mix_text_layer(screen, bitmap, cliprect);
	}
}



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

WRITE32_MEMBER(namcos22_state::namcos22_paletteram_w)
{
	COMBINE_DATA(&m_paletteram[offset]);
	m_dirtypal[offset & (0x7fff/4)] = 1;
}

void namcos22_state::update_palette()
{
	for (int i = 0; i < 0x8000/4; i++)
	{
		if (m_dirtypal[i])
		{
			for (int j = 0; j < 4; j++)
			{
				int which = i * 4 + j;
				int r = nthbyte(m_paletteram, which + 0x00000);
				int g = nthbyte(m_paletteram, which + 0x08000);
				int b = nthbyte(m_paletteram, which + 0x10000);
				m_palette->set_pen_color(which, rgb_t(r, g, b));
			}
			m_dirtypal[i] = 0;
		}
	}
}


WRITE32_MEMBER(namcos22_state::namcos22s_czram_w)
{
	int bank = nthword(m_czattr, 0xa/2) & 3;
	uint32_t prev = (m_banked_czram[bank][offset * 2] << 16) | m_banked_czram[bank][offset * 2 + 1];
	uint32_t temp = prev;
	COMBINE_DATA(&temp);
	data = temp;
	m_banked_czram[bank][offset * 2] = data >> 16;
	m_banked_czram[bank][offset * 2 + 1] = data & 0xffff;
	m_cz_was_written[bank] |= (prev ^ data);
}

READ32_MEMBER(namcos22_state::namcos22s_czram_r)
{
	int bank = nthword(m_czattr, 0xa/2) & 3;
	return (m_banked_czram[bank][offset * 2] << 16) | m_banked_czram[bank][offset * 2 + 1];
}

void namcos22_state::recalc_czram()
{
	for (int table = 0; table < 4; table++)
	{
		// as documented above, ss22 czram is 'just' a big compare table
		// this is very slow when emulating, so let's recalculate it to a simpler lookup table
		if (m_cz_was_written[table])
		{
			int small_val = 0x2000;
			int small_offset = 0;
			int large_val = 0;
			int large_offset = 0;
			int prev = 0x2000;

			for (int i = 0; i < 0x100; i++)
			{
				int val = m_banked_czram[table][i];

				// discard if larger than 1fff
				if (val > 0x1fff) val = prev;
				if (prev > 0x1fff)
				{
					prev = val;
					continue;
				}

				int start = prev;
				int end = val;
				if (start > end)
				{
					start = val;
					end = prev;
				}
				prev = val;

				// fill range
				for (int j = start; j < end; j++)
					m_recalc_czram[table][j] = i;

				// remember largest/smallest for later
				if (val < small_val)
				{
					small_val = val;
					small_offset = i;
				}
				if (val > large_val)
				{
					large_val = val;
					large_offset = i;
				}
			}

			// fill possible leftover ranges
			for (int j = 0; j < small_val; j++)
				m_recalc_czram[table][j] = small_offset;
			for (int j = large_val; j < 0x2000; j++)
				m_recalc_czram[table][j] = large_offset;

			m_cz_was_written[table] = 0;
		}
	}
}


void namcos22_state::update_mixer()
{
	int i;
	m_poly->wait("update_mixer");
#if 0 // show reg contents
	char msg1[0x1000] = {0}, msg2[0x1000] = {0};
	int st = 0x000 / 16;
	for (i = st; i < (st+3); i++)
	{
		sprintf(msg2,"%04X %08X %08X %08X %08X\n", i*16, m_mixer[i*4+0], m_mixer[i*4+1], m_mixer[i*4+2], m_mixer[i*4+3]);
		strcat(msg1,msg2);
	}
	if (1) // + other non-super regs
	if (!m_is_ss22)
	{
		strcat(msg1,"\n");
		for (i = 8; i <= 0x20; i += 8)
		{
			sprintf(msg2,"%04X %08X %08X %08X %08X\n", i*16, m_mixer[i*4+0], m_mixer[i*4+1], m_mixer[i*4+2], m_mixer[i*4+3]);
			strcat(msg1,msg2);
		}
	}
	popmessage("%s",msg1);
#endif

	if (m_is_ss22)
	{
/*
           0 1 2 3  4 5 6 7  8 9 a b  c d e f 10       14       18       1c
00824000: ffffff00 00000000 0000007f 00ff0000 1000ff00 0f000000 00ff007f 00010007 // time crisis
00824000: ffffff00 00000000 1830407f 00800000 0000007f 0f000000 0000037f 00010007 // trans sprite
00824000: ffffff00 00000000 3040307f 00000000 0080007f 0f000000 0000037f 00010007 // trans poly
00824000: ffffff00 00000000 1800187f 00800000 0080007f 0f000000 0000037f 00010007 // trans poly(2)
00824000: ffffff00 00000000 1800187f 00000000 0000007f 0f800000 0000037f 00010007 // trans text

    00,01,02        polygon fade rgb
    03
    04
    05,06,07        world fog rgb
    08,09,0a        background color
    0b
    0c
    0d              spot factor limit value
    0e              enable spot factor limit
    0f
    10
    11              global polygon alpha factor
    12,13           textlayer alpha pen comparison
    14              textlayer alpha pen mask?
    15              textlayer alpha factor
    16,17,18        global fade rgb
    19              global fade factor
    1a              fade target flags
    1b              textlayer palette base
    1c
    1d
    1e
    1f              layer enable
*/
		m_poly_fade_r        = nthbyte(m_mixer, 0x00);
		m_poly_fade_g        = nthbyte(m_mixer, 0x01);
		m_poly_fade_b        = nthbyte(m_mixer, 0x02); m_poly_fade_enabled = (m_poly_fade_r == 0xff && m_poly_fade_g == 0xff && m_poly_fade_b == 0xff) ? 0 : 1;
		m_fog_r              = nthbyte(m_mixer, 0x05);
		m_fog_g              = nthbyte(m_mixer, 0x06);
		m_fog_b              = nthbyte(m_mixer, 0x07);
		m_spot_limit         = nthbyte(m_mixer, 0x0d);
		m_poly_translucency  = nthbyte(m_mixer, 0x11);
		m_screen_fade_r      = nthbyte(m_mixer, 0x16);
		m_screen_fade_g      = nthbyte(m_mixer, 0x17);
		m_screen_fade_b      = nthbyte(m_mixer, 0x18);
		m_screen_fade_factor = nthbyte(m_mixer, 0x19);
		m_mixer_flags        = nthbyte(m_mixer, 0x1a);
		m_text_palbase       = nthbyte(m_mixer, 0x1b) & 0x7f;

		// put spot-specific flags into high word
		m_mixer_flags |= m_spot_enable << 16;
		m_mixer_flags |= (nthbyte(m_mixer, 0x0e) & 1) << 17;
		m_mixer_flags |= (m_chipselect & 0xc000) << 4;
	}
	else
	{
/*
90020000: 4f030000 7f00007f 4d4d4d42 0c00c0c0
90020010: c0010001 00010000 00000000 00000000
90020080: 00010101 01010102 00000000 00000000
900200c0: 00000000 00000000 00000000 03000000
90020100: fff35000 00000000 00000000 00000000
90020180: ff713700 00000000 00000000 00000000
90020200: ff100000 00000000 00000000 00000000

    00,01           display flags
    02
    03
    04
    05
    06
    07              textlayer palette base?
    08,09,0a        textlayer pen c shadow rgb
    0b,0c,0d        textlayer pen d shadow rgb
    0e,0f,10        textlayer pen e shadow rgb
    11,12           global fade factor red
    13,14           global fade factor green
    15,16           global fade factor blue
    80-87           fog color mask?
    100,180,200     fog rgb 0
    101,181,201     fog rgb 1
    102,182,202     fog rgb 2
    103,183,203     fog rgb 3
*/
		m_mixer_flags        = nthbyte(m_mixer, 0x00) << 8 | nthbyte(m_mixer, 0x01);
		m_poly_fade_r        = nthbyte(m_mixer, 0x11) << 8 | nthbyte(m_mixer, 0x12); // 0x0100 = 1.0
		m_poly_fade_g        = nthbyte(m_mixer, 0x13) << 8 | nthbyte(m_mixer, 0x14);
		m_poly_fade_b        = nthbyte(m_mixer, 0x15) << 8 | nthbyte(m_mixer, 0x16);
		m_poly_fade_enabled  = (m_poly_fade_r == 0x100 && m_poly_fade_g == 0x100 && m_poly_fade_b == 0x100) ? 0 : 1;

		// raveracw is the only game using multiple fog colors (city smog, cars under tunnels, brake disc in attract mode)
		m_fog_colormask      = m_mixer[0x84/4];

		// fog color per cz type
		for (i = 0; i < 4; i++)
		{
			m_fog_r_per_cztype[i] = nthbyte(m_mixer, 0x0100+i);
			m_fog_g_per_cztype[i] = nthbyte(m_mixer, 0x0180+i);
			m_fog_b_per_cztype[i] = nthbyte(m_mixer, 0x0200+i);
		}

		m_text_palbase = 0x7f;
	}
}



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

uint32_t namcos22_state::screen_update_namcos22s(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	update_mixer();
	update_palette();
	recalc_czram();
	screen.priority().fill(0, cliprect);

	// background color
	rgbaint_t bg_color(0, nthbyte(m_mixer, 0x08), nthbyte(m_mixer, 0x09), nthbyte(m_mixer, 0x0a));
	if (m_mixer_flags & 1 && m_screen_fade_factor)
	{
		rgbaint_t fade_color(0, m_screen_fade_r, m_screen_fade_g, m_screen_fade_b);
		bg_color.blend(fade_color, 0xff - m_screen_fade_factor);
	}
	bitmap.fill(bg_color.to_rgba(), cliprect);

	// layers
	uint8_t layer = nthbyte(m_mixer, 0x1f);
	if (layer & 4) draw_text_layer(screen, bitmap, cliprect);
	if (layer & 2) draw_sprites(bitmap, cliprect);
	if (layer & 1) draw_polygons(bitmap);
	m_poly->render_scene(screen, bitmap);
	if (layer & 4) namcos22s_mix_text_layer(screen, bitmap, cliprect, 6);

	// apply gamma
	const uint8_t *rlut = (const uint8_t *)&m_mixer[0x100/4];
	const uint8_t *glut = (const uint8_t *)&m_mixer[0x200/4];
	const uint8_t *blut = (const uint8_t *)&m_mixer[0x300/4];
	for (int y = cliprect.min_y; y <= cliprect.max_y; y++)
	{
		uint32_t *dest = &bitmap.pix32(y);
		for (int x = cliprect.min_x; x <= cliprect.max_x; x++)
		{
			int rgb = dest[x];
			int r = rlut[NATIVE_ENDIAN_VALUE_LE_BE(3, 0) ^ ((rgb >> 16) & 0xff)];
			int g = glut[NATIVE_ENDIAN_VALUE_LE_BE(3, 0) ^ ((rgb >> 8) & 0xff)];
			int b = blut[NATIVE_ENDIAN_VALUE_LE_BE(3, 0) ^ (rgb & 0xff)];
			dest[x] = (r << 16) | (g << 8) | b;
		}
	}

	return 0;
}

uint32_t namcos22_state::screen_update_namcos22(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
	update_mixer();
	update_palette();
	screen.priority().fill(0, cliprect);

	// background color
	bitmap.fill(m_palette->pen(0x7fff), cliprect);

	// layers
	draw_polygons(bitmap);
	m_poly->render_scene(screen, bitmap);
	draw_text_layer(screen, bitmap, cliprect); // text layer + final mix(gamma)

	return 0;
}



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

void namcos22_state::init_tables()
{
	m_dirtypal = std::make_unique<uint8_t[]>(0x8000/4);
	memset(m_dirtypal.get(), 1, 0x8000/4);
	memset(m_paletteram, 0, 0x8000);

	memset(m_polygonram, 0xcc, m_polygonram.bytes());

	// init spotram (super22 only)
	if (m_is_ss22)
		m_spotram = make_unique_clear<uint16_t[]>(SPOTRAM_SIZE);

	// init czram tables (super22 only)
	if (m_is_ss22)
	{
		for (int table = 0; table < 4; table++)
		{
			m_banked_czram[table] = make_unique_clear<uint16_t[]>(0x100);
			m_recalc_czram[table] = make_unique_clear<uint8_t[]>(0x2000);
			m_cz_was_written[table] = 1;
		}
	}

	// init pointrom
	m_pointrom_size = memregion("pointrom")->bytes()/3;
	m_pointrom = auto_alloc_array(machine(), int32_t, m_pointrom_size);
	uint8_t* pointrom_low = memregion("pointrom")->base();
	uint8_t* pointrom_mid = pointrom_low + m_pointrom_size;
	uint8_t* pointrom_high = pointrom_mid + m_pointrom_size;
	for (int i = 0; i < m_pointrom_size; i++)
	{
		m_pointrom[i] = signed24(pointrom_high[i] << 16 | pointrom_mid[i] << 8 | pointrom_low[i]);
	}

	m_pointram = make_unique_clear<uint32_t[]>(0x20000);

	// force all texture tiles to be decoded now
	for (int i = 0; i < m_gfxdecode->gfx(1)->elements(); i++)
		m_gfxdecode->gfx(1)->get_data(i);

	m_texture_tilemap = (uint16_t *)memregion("textilemap")->base();
	m_texture_tiledata = (uint8_t *)m_gfxdecode->gfx(1)->get_data(0);
	m_texture_tileattr = std::make_unique<uint8_t[]>(0x080000*2);

	// unpack textures
	uint8_t *packed_tileattr = 0x200000 + (uint8_t *)memregion("textilemap")->base();
	uint8_t *unpacked_tileattr = m_texture_tileattr.get();
	for (int i = 0; i < 0x80000; i++)
	{
		*unpacked_tileattr++ = (*packed_tileattr) >> 4;
		*unpacked_tileattr++ = (*packed_tileattr) & 0xf;
		packed_tileattr++;
	}

	// make attr/y/x lookup table
	m_texture_ayx_to_pixel = std::make_unique<uint8_t[]>(16*16*16);
	for (int attr = 0; attr < 16; attr++)
	{
		for (int y = 0; y < 16; y++)
		{
			for (int x = 0; x < 16; x++)
			{
				int ix = x, iy = y;

				if (attr & 4)
					ix = 15 - ix;

				if (attr & 2)
					iy = 15 - iy;

				if (attr & 8)
				{
					int temp = ix;
					ix = iy;
					iy = temp;
				}

				m_texture_ayx_to_pixel[attr << 8 | y << 4 | x] = (iy << 4) | ix;
			}
		}
	}

	// following setup is Namco System 22 specific
	switch (m_gametype)
	{
		case NAMCOS22_RIDGE_RACER:
		case NAMCOS22_RIDGE_RACER2:
		case NAMCOS22_ACE_DRIVER:
		case NAMCOS22_CYBER_COMMANDO:
		{
			for (int i = 0; i < 0x100000; i++)
			{
				int tile = m_texture_tilemap[i];
				int attr = m_texture_tileattr[i];
				if ((attr & 0x1) == 0)
				{
					tile = (tile & 0x3fff) | 0x8000;
					m_texture_tilemap[i] = tile;
				}
			}
			break;
		}

		default:
			break;
	}
}


void namcos22_state::video_start()
{
	m_is_ss22 = (m_iomcu == nullptr);
	init_tables();

	m_mix_bitmap = std::make_unique<bitmap_ind16>(640, 480);
	m_bgtilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(namcos22_state::get_text_tile_info), this), TILEMAP_SCAN_ROWS, 16, 16, 64, 64);
	m_bgtilemap->set_transparent_pen(0xf);

	m_gfxdecode->gfx(0)->set_source((uint8_t *)m_cgram.target());

	m_poly = auto_alloc(machine(), namcos22_renderer(*this));
}