summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/gt64xxx.cpp
blob: f2fafbc9daeb202280a45aa97538be8eeefdac61 (plain) (tree)
1
2
3
4
5

                                            
                

                    














                                                                              

                                      
                     

                                       
 
                                                      






                                                                                     



                                  












































































                                     

                                  
































                                                                                            
 



                                                 

              

                                              
                                                                                                       
 
 
                                                                                                               





                                                                                       
 

                                                       
                                         
         

 
                                                                                          
 
                                  
                            

 
                                                                                        
 



                                                                  




                                        


                                                   








                                         




                                                                                                                         
                      

                                                    
 
              
                                                

                                                                                               
                                                                                            
                                                                                                                                            




                                                                                                       
                                                                                                                                                       
         
 




                                                                                                                                 




                                           
                                        










                                                         



                                                                
                                    

 
                                       


                        







                                                    



                                  





























                                          
 



                                               
 


                              
                           
 




                                             



                                    
                                  











                                                                                                                      
                                                                                    
 


                                                        

                                                                                                                                              



                                                                           


                                                                                           

                                                                                                                                
                                                                                                      
                                                                                                                                               


                  
                                                     
         


                                                                                                                                             
                                                                                                    

         


                                                                                        

                                                                                                                        
                                                                                   



                                                                                            

                                                                                                                          
                                                                                     



                                                                                            

                                                                                                                          
                                                                                     
 



                                                                         

                                                                                                                                                                




                                                                                                                                                 
 

                                                 
                                                                            


                                                   
                                                                             





                                                                

 

                                                                                                                                                                      
 
                     
                                           
 
                                                                            

                     

                                                                                                                                      
                                        

                                                                                                                     
                                                                                                
 

                     

                                                                                                                                      
                                        

                                                                                                                     
                                                                                                
 

                     

                                                                                                                                      
                                        

                                                                                                                     
                                                                                                
 

                     

                                                                                                                                      
                                        

                                                                                                                     
                                                                                                






                                         








                                                                   










                                                                                                                                          

                                                             
                                                                  






                                  


                                            
                            
                                                                                                                                         



                                             
                                                                                                                                     



                                             
                                                                                                                   
                                                                                                                                                                        



                                              
                                









                                                                                                                                                    
                                                                                                                         



                                                                           

                       
                                                                                                        
                                                                                                                                                                     




                                             
                                                                                                                   
                                                                                                                                                                        



                                              
                                                                                                        
                                                                                                                                                                     




                                           
                                                                                                                    
                                    
                                     
                                                                                                                                                                                
         



                                            
                                                                                                         
                                    
                                     
                                                                                                                                                                             




                                      
                                           
                                                                                                                                     





                                        
                                                                                                                                  



                                      
                                           
                                                                                                                                     





                                        
                                                                                                                                  



                                      
                                           
                                                                                                                                     





                                        
                                                                                                                                  



                                      
                                           
                                                                                                                                     





                                        
                                                                                                                                  





                                        
                                        














                                                               
                                                                                                   


                                                                                     
                                                                                                     





                                                                                   


                                                                                                       


                                      
                                                              
                                                                                                                                                 
                              
 






                                                                 
                                                                                                                    


                              
                                                                                                                                


                              
                                                     
 





                                        

                                                      

         
                                        




                                                     



                                  







                                       
                                         

                                      
                                 
                                        
                                   
                                                                                                                                                              








                                               









                                                             






                                                                                                  
                                                                                                                 
                         
                                                                                                                                                














                                                               
                                                                                                                                                   






                                        
                                                                                                           












                                                                                                 
                                                                                                                                    


                                                                         
                                                                                                           


                                                                                                               
                                                                      





                                    
                                                                                                              





                                                                            
                                                                                                                                                              


                                         

                                                                                                  
                                         


















                                                                                              
                                                                                                                                                                                          






                                                                                                              
                                                                                                                                              


                              
                                                                                                                                              



















                                                              
                                                                                                                                                                                    







                                                     
                                             





















                                                                         
                                                                 







                                                                                                  



                                                                   
                      



































                                                                                    
                                                   
 






                                                                                                 
 







                                                                                                                                           


                                                                 
                                                                             


                                                                      

                                   

















                                                                    
                                                                                                                                               
 
                                   
                                       

                                                                    

                                              
                                                                                                                                                        





                                                                                                                        
                                                                                                   

                                                                                              





                                                                                                                                                                                                                       

                                    



                                                          
                                          
                         
                                                                                            



                                                  
                              






                                                                                              



                                                                                                        


                                                                   
                 
                               
                 





                                                                                    
 







                                                                                             
 
// license:BSD-3-Clause
// copyright-holders: Aaron Giles, Ted Green
#include "emu.h"
#include "gt64xxx.h"

#define LOG_GENERAL         (1U << 0)
#define LOG_GALILEO         (1U << 1)
#define LOG_TIMERS          (1U << 2)
#define LOG_DMA             (1U << 3)
#define LOG_PCI             (1U << 4)

//#define VERBOSE (LOG_GENERAL | LOG_GALILEO | LOG_TIMERS | LOG_DMA | LOG_PCI)
#include "logmacro.h"

#define LOGGALILEO(...)     LOGMASKED(LOG_GALILEO, __VA_ARGS__)
#define LOGTIMERS(...)      LOGMASKED(LOG_TIMERS, __VA_ARGS__)
#define LOGDMA(...)         LOGMASKED(LOG_DMA, __VA_ARGS__)
#define LOGPCI(...)         LOGMASKED(LOG_PCI, __VA_ARGS__)


/*************************************
 *
 *  Galileo constants
 *
 *************************************/

#define TIMER_PERIOD        attotime::from_hz(clock())
#define PCI_BUS_CLOCK       33000000
// Number of dma words to transfer at a time, real hardware configurable between 8-32
#define DMA_BURST_SIZE      32
#define DMA_TIMER_PERIOD    attotime::from_hz(PCI_BUS_CLOCK / 48)

/* Galileo registers - 0x000-0x3ff */
#define GREG_CPU_CONFIG     (0x000/4)
#define GREG_R1_0_LO     (0x008/4)
#define GREG_R1_0_HI     (0x010/4)
#define GREG_R3_2_LO     (0x018/4)
#define GREG_R3_2_HI     (0x020/4)
#define GREG_CS_2_0_LO      (0x028/4)
#define GREG_CS_2_0_HI      (0x030/4)
#define GREG_CS_3_BOOT_LO   (0x038/4)
#define GREG_CS_3_BOOT_HI   (0x040/4)
#define GREG_PCI_IO_LO      (0x048/4)
#define GREG_PCI_IO_HI      (0x050/4)
#define GREG_PCI_MEM0_LO    (0x058/4)
#define GREG_PCI_MEM0_HI    (0x060/4)
#define GREG_INTERNAL_SPACE (0x068/4)
#define GREG_BUSERR_LO      (0x070/4)
#define GREG_BUSERR_HI      (0x078/4)
// GT-64111 only
#define GREG_PCI_MEM1_LO    (0x080/4)
#define GREG_PCI_MEM1_HI    (0x088/4)

/* Galileo registers - 0x400-0x7ff */
#define GREG_RAS0_LO        (0x400/4)
#define GREG_RAS0_HI        (0x404/4)
#define GREG_RAS1_LO        (0x408/4)
#define GREG_RAS1_HI        (0x40c/4)
#define GREG_RAS2_LO        (0x410/4)
#define GREG_RAS2_HI        (0x414/4)
#define GREG_RAS3_LO        (0x418/4)
#define GREG_RAS3_HI        (0x41c/4)
#define GREG_CS0_LO         (0x420/4)
#define GREG_CS0_HI         (0x424/4)
#define GREG_CS1_LO         (0x428/4)
#define GREG_CS1_HI         (0x42c/4)
#define GREG_CS2_LO         (0x430/4)
#define GREG_CS2_HI         (0x434/4)
#define GREG_CS3_LO         (0x438/4)
#define GREG_CS3_HI         (0x43c/4)
#define GREG_CSBOOT_LO      (0x440/4)
#define GREG_CSBOOT_HI      (0x444/4)
#define GREG_DRAM_CONFIG    (0x448/4)
#define GREG_DRAM_BANK0     (0x44c/4)
#define GREG_DRAM_BANK1     (0x450/4)
#define GREG_DRAM_BANK2     (0x454/4)
#define GREG_DRAM_BANK3     (0x458/4)
#define GREG_DEVICE_BANK0   (0x45c/4)
#define GREG_DEVICE_BANK1   (0x460/4)
#define GREG_DEVICE_BANK2   (0x464/4)
#define GREG_DEVICE_BANK3   (0x468/4)
#define GREG_DEVICE_BOOT    (0x46c/4)
#define GREG_ADDRESS_ERROR  (0x470/4)

/* Galileo registers - 0x800-0xbff */
#define GREG_DMA0_COUNT     (0x800/4)
#define GREG_DMA1_COUNT     (0x804/4)
#define GREG_DMA2_COUNT     (0x808/4)
#define GREG_DMA3_COUNT     (0x80c/4)
#define GREG_DMA0_SOURCE    (0x810/4)
#define GREG_DMA1_SOURCE    (0x814/4)
#define GREG_DMA2_SOURCE    (0x818/4)
#define GREG_DMA3_SOURCE    (0x81c/4)
#define GREG_DMA0_DEST      (0x820/4)
#define GREG_DMA1_DEST      (0x824/4)
#define GREG_DMA2_DEST      (0x828/4)
#define GREG_DMA3_DEST      (0x82c/4)
#define GREG_DMA0_NEXT      (0x830/4)
#define GREG_DMA1_NEXT      (0x834/4)
#define GREG_DMA2_NEXT      (0x838/4)
#define GREG_DMA3_NEXT      (0x83c/4)
#define GREG_DMA0_CONTROL   (0x840/4)
#define GREG_DMA1_CONTROL   (0x844/4)
#define GREG_DMA2_CONTROL   (0x848/4)
#define GREG_DMA3_CONTROL   (0x84c/4)
#define GREG_TIMER0_COUNT   (0x850/4)
#define GREG_TIMER1_COUNT   (0x854/4)
#define GREG_TIMER2_COUNT   (0x858/4)
#define GREG_TIMER3_COUNT   (0x85c/4)
#define GREG_DMA_ARBITER    (0x860/4)
#define GREG_TIMER_CONTROL  (0x864/4)

/* Galileo registers - 0xc00-0xfff */
#define GREG_PCI_COMMAND    (0xc00/4)
#define GREG_PCI_TIMEOUT    (0xc04/4)
#define GREG_PCI_R1_0    (0xc08/4)
#define GREG_PCI_R3_2    (0xc0c/4)
#define GREG_PCI_CS_2_0     (0xc10/4)
#define GREG_PCI_CS_3_BOOT  (0xc14/4)
#define GREG_INT_STATE      (0xc18/4)
#define GREG_INT_MASK       (0xc1c/4)
#define GREG_PCI_INT_MASK   (0xc24/4)
#define GREG_CONFIG_ADDRESS (0xcf8/4)
#define GREG_CONFIG_DATA    (0xcfc/4)

/* Galileo interrupts */
#define GINT_SUMMARY_SHIFT  (0)
#define GINT_MEMOUT_SHIFT   (1)
#define GINT_DMAOUT_SHIFT   (2)
#define GINT_CPUOUT_SHIFT   (3)
#define GINT_DMA0COMP_SHIFT (4)
#define GINT_DMA1COMP_SHIFT (5)
#define GINT_DMA2COMP_SHIFT (6)
#define GINT_DMA3COMP_SHIFT (7)
#define GINT_T0EXP_SHIFT    (8)
#define GINT_T1EXP_SHIFT    (9)
#define GINT_T2EXP_SHIFT    (10)
#define GINT_T3EXP_SHIFT    (11)
#define GINT_MASRDERR_SHIFT (12)
#define GINT_SLVWRERR_SHIFT (13)
#define GINT_MASWRERR_SHIFT (14)
#define GINT_SLVRDERR_SHIFT (15)
#define GINT_ADDRERR_SHIFT  (16)
#define GINT_MEMERR_SHIFT   (17)
#define GINT_MASABORT_SHIFT (18)
#define GINT_TARABORT_SHIFT (19)
#define GINT_RETRYCTR_SHIFT (20)


DEFINE_DEVICE_TYPE(GT64XXX, gt64xxx_device, "gt64xxx", "Galileo GT-64XXX System Controller")

void gt64xxx_device::config_map(address_map &map)
{
	pci_device::config_map(map);
}

// cpu i/f map
void gt64xxx_device::cpu_map(address_map &map)
{
	map(0x00000000, 0x00000cff).rw(FUNC(gt64xxx_device::cpu_if_r), FUNC(gt64xxx_device::cpu_if_w));
}

gt64xxx_device::gt64xxx_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: pci_host_device(mconfig, GT64XXX, tag, owner, clock)
	, m_cpu(*this, finder_base::DUMMY_TAG), m_be(0), m_autoconfig(0), m_irq_num(-1)
	, m_mem_config("memory_space", ENDIANNESS_LITTLE, 32, 32)
	, m_io_config("io_space", ENDIANNESS_LITTLE, 32, 32)
	, m_romRegion(*this, "rom")
	, m_updateRegion(*this, "update")
{
	for (int csIndex = 0; csIndex < 4; csIndex++) {
		m_cs_devices[csIndex] = nullptr;
		m_simm_size[csIndex] = 0;
	}
}

void gt64xxx_device::set_map(int id, const address_map_constructor &map, device_t *device)
{
	m_cs_devices[id] = device;
	m_cs_maps[id] = map;
}

device_memory_interface::space_config_vector gt64xxx_device::memory_space_config() const
{
	auto r = pci_bridge_device::memory_space_config();
	r.emplace_back(std::make_pair(AS_PCI_MEM, &m_mem_config));
	r.emplace_back(std::make_pair(AS_PCI_IO, &m_io_config));
	return r;
}

void gt64xxx_device::device_start()
{
	pci_host_device::device_start();
	m_cpu_space = &m_cpu->space(AS_PCI_CONFIG);
	memory_space = &space(AS_PCI_MEM);
	io_space = &space(AS_PCI_IO);

	memory_window_start = 0;
	memory_window_end   = 0xffffffff;
	memory_offset       = 0;
	io_window_start = 0;
	io_window_end   = 0xffffffff;
	io_offset       = 0x00000000;
	status = 0x0;

	// DMA timer
	m_dma_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gt64xxx_device::perform_dma), this));
	// Leave the timer disabled.
	m_dma_timer->adjust(attotime::never, 0, DMA_TIMER_PERIOD);

	// Reserve RAM
	for (int i=0; i<4; i++)
		m_ram[i].resize(m_simm_size[i] / 4);

	// ROM
	uint32_t romSize = m_romRegion->bytes();
	m_cpu_space->install_rom   (0x1fc00000, 0x1fc00000 + romSize - 1, m_romRegion->base());
	// ROM MIPS DRC
	m_cpu->add_fastram(0x1fc00000, 0x1fc00000 + romSize - 1, true, m_romRegion->base());
	LOGGALILEO("gt64xxx_device::device_start ROM Mapped size: 0x%08X start: 0x1fc00000 end: %08X\n", romSize, 0x1fc00000 + romSize - 1);

	// Update region address is based on seattle driver
	if (m_updateRegion) {
		romSize = m_updateRegion->bytes();
		m_cpu_space->install_rom(0x1fd00000, 0x1fd00000 + romSize - 1, m_updateRegion->base());
		LOGGALILEO("gt64xxx_device::device_start UPDATE Mapped size: 0x%08X start: 0x1fd00000 end: %08X\n", romSize, 0x1fd00000 + romSize - 1);
	}

	/* allocate timers for the galileo */
	m_timer[0].timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gt64xxx_device::timer_callback), this));
	m_timer[1].timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gt64xxx_device::timer_callback), this));
	m_timer[2].timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gt64xxx_device::timer_callback), this));
	m_timer[3].timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gt64xxx_device::timer_callback), this));

	// Save states
	save_item(NAME(m_pci_stall_state));
	save_item(NAME(m_retry_count));
	save_item(NAME(m_pci_cpu_stalled));
	save_item(NAME(m_stall_windex));
	save_item(NAME(m_cpu_stalled_offset));
	save_item(NAME(m_cpu_stalled_data));
	save_item(NAME(m_cpu_stalled_mem_mask));
	save_item(NAME(m_prev_addr));
	save_item(NAME(m_reg));
	for (int i = 0; i < ARRAY_LENGTH(m_timer); i++) {
		save_item(NAME(m_timer[i].active), i);
		save_item(NAME(m_timer[i].count), i);
	}
	save_item(NAME(m_dma_active));
	// m_ram[4]
	save_pointer(NAME(m_ram[0].data()), m_simm_size[0] / 4);
	save_pointer(NAME(m_ram[1].data()), m_simm_size[1] / 4);
	save_pointer(NAME(m_ram[2].data()), m_simm_size[2] / 4);
	save_pointer(NAME(m_ram[3].data()), m_simm_size[3] / 4);
	save_item(NAME(m_last_dma));
}

void gt64xxx_device::device_post_load()
{
	map_cpu_space();
	remap_cb();
}

void gt64xxx_device::device_reset()
{
	pci_device::device_reset();

	// Configuration register defaults
	m_reg[GREG_CPU_CONFIG] = m_be ? 0 : (1<<12);
	m_reg[GREG_R1_0_LO] = 0x0;
	m_reg[GREG_R1_0_HI] = 0x7;
	m_reg[GREG_R3_2_LO] = 0x8;
	m_reg[GREG_R3_2_HI] = 0xf;
	m_reg[GREG_CS_2_0_LO] = 0xe0;
	m_reg[GREG_CS_2_0_HI] = 0x70;
	m_reg[GREG_CS_3_BOOT_LO] = 0xf8;
	m_reg[GREG_CS_3_BOOT_HI] = 0x7f;
	m_reg[GREG_PCI_IO_LO] = 0x80;
	m_reg[GREG_PCI_IO_HI] = 0xf;
	m_reg[GREG_PCI_MEM0_LO] = 0x90;
	m_reg[GREG_PCI_MEM0_HI] = 0x1f;
	m_reg[GREG_INTERNAL_SPACE] = 0xa0;
	m_reg[GREG_PCI_MEM1_LO] = 0x790;
	m_reg[GREG_PCI_MEM1_HI] = 0x1f;

	m_reg[GREG_RAS0_LO] = 0x0;
	m_reg[GREG_RAS0_HI] = 0x7;
	m_reg[GREG_RAS1_LO] = 0x8;
	m_reg[GREG_RAS1_HI] = 0xf;
	m_reg[GREG_RAS2_LO] = 0x10;
	m_reg[GREG_RAS2_HI] = 0x17;
	m_reg[GREG_RAS3_LO] = 0x18;
	m_reg[GREG_RAS3_HI] = 0x1f;
	m_reg[GREG_CS0_LO] = 0xc0;
	m_reg[GREG_CS0_HI] = 0xc7;
	m_reg[GREG_CS1_LO] = 0xc8;
	m_reg[GREG_CS1_HI] = 0xcf;
	m_reg[GREG_CS2_LO] = 0xd0;
	m_reg[GREG_CS2_HI] = 0xdf;
	m_reg[GREG_CS3_LO] = 0xf0;
	m_reg[GREG_CS3_HI] = 0xfb;
	m_reg[GREG_CSBOOT_LO] = 0xfc;
	m_reg[GREG_CSBOOT_HI] = 0xff;

	m_reg[GREG_PCI_COMMAND] = m_be ? 0 : 1;

	map_cpu_space();
	regenerate_config_mapping();

	m_pci_stall_state = 0;
	m_retry_count = 0;
	m_pci_cpu_stalled = 0;
	m_stall_windex = 0;

	m_dma_active = 0;
	m_dma_timer->adjust(attotime::never);
	m_last_dma = 0;

	m_prev_addr = 0;
}

void gt64xxx_device::map_cpu_space()
{
	uint32_t winStart, winEnd;

	// ROM region starts at 0x1fc00000
	m_cpu_space->unmap_readwrite(0x00000000, 0x1fbfffff);
	m_cpu_space->unmap_readwrite(0x20000000, 0xffffffff);

	// Clear fastram regions in cpu after rom
	m_cpu->clear_fastram(1);

	// CPU Regs
	winStart = m_reg[GREG_INTERNAL_SPACE]<<21;
	winEnd = winStart + sizeof(m_reg) - 1;
	m_cpu_space->install_device(winStart, winEnd, *static_cast<gt64xxx_device *>(this), &gt64xxx_device::cpu_map);
	logerror("map_cpu_space cpu_reg start: %08X end: %08X\n", winStart, winEnd);

	// RAS[0:3]
	for (int ramIndex = 0; ramIndex < 4; ++ramIndex)
	{
		winStart = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex/2)] << 21) | (m_reg[GREG_RAS0_LO + 0x8 / 4 * ramIndex] << 20);
		winEnd = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex / 2)] << 21) | (m_reg[GREG_RAS0_HI + 0x8 / 4 * ramIndex] << 20) | 0xfffff;
		// Cap window end at physical memory bounds
		uint32_t winSize = winEnd - winStart + 1;
		if (winSize > m_ram[ramIndex].size() * 4)
			winEnd = winStart + m_ram[ramIndex].size() * 4 - 1;
		//m_ram[ramIndex].resize((winEnd + 1 - winStart) / 4);
		if (m_ram[ramIndex].size()>0)
			m_cpu_space->install_ram(winStart, winEnd, m_ram[ramIndex].data());
		//m_cpu->add_fastram(winStart, m_ram[ramIndex].size() * sizeof(m_ram[ramIndex][0]), false, &m_ram[ramIndex][0]);
		//m_cpu->add_fastram(winStart, m_ram[ramIndex].size() * sizeof(uint32_t), false, m_ram[ramIndex].data());
		logerror("map_cpu_space ras[%i] start: %08X end: %08X\n", ramIndex, winStart, winEnd);
		//printf("%s: map_cpu_space ras[%i] start: %08X end: %08X size: %08X\n", tag(), ramIndex, winStart, winEnd, winEnd-winStart+1);
	}

	// CS[0:3]
	for (int csIndex = 0; csIndex < 4; ++csIndex)
	{
		winStart = (m_reg[GREG_CS_2_0_LO + 0x10 / 4 * (csIndex / 3)] << 21) | (m_reg[GREG_CS0_LO + 0x8 / 4 * csIndex] << 20);
		winEnd = (m_reg[GREG_CS_2_0_LO + 0x10 / 4 * (csIndex / 3)] << 21) | (m_reg[GREG_CS0_HI + 0x8 / 4 * csIndex] << 20) | 0xfffff;
		m_cpu_space->install_device_delegate(winStart, winEnd, *m_cs_devices[csIndex], m_cs_maps[csIndex]);
		logerror("map_cpu_space cs[%i] start: %08X end: %08X\n", csIndex, winStart, winEnd);
	}

	// PCI IO Window
	winStart = m_reg[GREG_PCI_IO_LO]<<21;
	winEnd =   (m_reg[GREG_PCI_IO_LO]<<21) | (m_reg[GREG_PCI_IO_HI]<<21) | 0x1fffff;
	m_cpu_space->install_read_handler(winStart, winEnd, read32_delegate(FUNC(gt64xxx_device::master_io_r), this));
	m_cpu_space->install_write_handler(winStart, winEnd, write32_delegate(FUNC(gt64xxx_device::master_io_w), this));
	logerror("map_cpu_space pci_io start: %08X end: %08X\n", winStart, winEnd);

	// PCI MEM0 Window
	winStart = m_reg[GREG_PCI_MEM0_LO]<<21;
	winEnd =   (m_reg[GREG_PCI_MEM0_LO]<<21) | (m_reg[GREG_PCI_MEM0_HI]<<21) | 0x1fffff;
	m_cpu_space->install_read_handler(winStart, winEnd, read32_delegate(FUNC(gt64xxx_device::master_mem0_r), this));
	m_cpu_space->install_write_handler(winStart, winEnd, write32_delegate(FUNC(gt64xxx_device::master_mem0_w), this));
	logerror("map_cpu_space pci_mem0 start: %08X end: %08X\n", winStart, winEnd);

	// PCI MEM1 Window
	winStart = m_reg[GREG_PCI_MEM1_LO]<<21;
	winEnd =   (m_reg[GREG_PCI_MEM1_LO]<<21) | (m_reg[GREG_PCI_MEM1_HI]<<21) | 0x1fffff;
	m_cpu_space->install_read_handler(winStart, winEnd, read32_delegate(FUNC(gt64xxx_device::master_mem1_r), this));
	m_cpu_space->install_write_handler(winStart, winEnd, write32_delegate(FUNC(gt64xxx_device::master_mem1_w), this));
	logerror("map_cpu_space pci_mem1 start: %08X end: %08X\n", winStart, winEnd);

	// Setup the address mapping table for DMA lookups
	for (size_t index = 0; index < proc_addr_bank::ADDR_NUM; ++index)
	{
		if (index < proc_addr_bank::ADDR_PCI_MEM1) {
			dma_addr_map[index].low_addr = (m_reg[GREG_R1_0_LO + 0x10 / 4 * index] << 21);
			dma_addr_map[index].high_addr = (dma_addr_map[index].low_addr & 0xf0000000) | (m_reg[GREG_R1_0_HI + 0x10 / 4 * index] << 21) | 0x1fffff;
		}
		else {
			dma_addr_map[index].low_addr = (m_reg[GREG_PCI_MEM1_LO] << 21);
			dma_addr_map[index].high_addr = (dma_addr_map[index].low_addr & 0xf0000000) | (m_reg[GREG_PCI_MEM1_HI] << 21) | 0x1fffff;
		}

	switch (index) {
		case proc_addr_bank::ADDR_PCI_IO:
			dma_addr_map[index].space = &this->space(AS_PCI_IO);
			break;
		case proc_addr_bank::ADDR_PCI_MEM0:
		case proc_addr_bank::ADDR_PCI_MEM1:
			dma_addr_map[index].space = &this->space(AS_PCI_MEM);
			break;
		default:
			dma_addr_map[index].space = m_cpu_space;
			break;
		}
	}
}

void gt64xxx_device::map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
									uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space)
{
	int ramIndex;
	uint32_t winStart, winEnd, winSize;

	// Not sure if GREG_R1_0_LO should be added on PCI address map side.
	// RAS0
	ramIndex = 0;
	winStart = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex / 2)] << 21) | (m_reg[GREG_RAS0_LO + 0x8 / 4 * ramIndex] << 20);
	winEnd = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex / 2)] << 21) | (m_reg[GREG_RAS0_HI + 0x8 / 4 * ramIndex] << 20) | 0xfffff;
	winSize = winEnd - winStart + 1;
	memory_space->install_read_handler(winStart, winEnd, read32_delegate(FUNC(gt64xxx_device::ras_0_r), this));
	memory_space->install_write_handler(winStart, winEnd, write32_delegate(FUNC(gt64xxx_device::ras_0_w), this));
	LOGGALILEO("map_extra RAS0 start=%08X end=%08X size=%08X\n", winStart, winEnd, winSize);

	// RAS1
	ramIndex = 1;
	winStart = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex / 2)] << 21) | (m_reg[GREG_RAS0_LO + 0x8 / 4 * ramIndex] << 20);
	winEnd = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex / 2)] << 21) | (m_reg[GREG_RAS0_HI + 0x8 / 4 * ramIndex] << 20) | 0xfffff;
	winSize = winEnd - winStart + 1;
	memory_space->install_read_handler(winStart, winEnd, read32_delegate(FUNC(gt64xxx_device::ras_1_r), this));
	memory_space->install_write_handler(winStart, winEnd, write32_delegate(FUNC(gt64xxx_device::ras_1_w), this));
	LOGGALILEO("map_extra RAS1 start=%08X end=%08X size=%08X\n", winStart, winEnd, winSize);

	// RAS2
	ramIndex = 2;
	winStart = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex / 2)] << 21) | (m_reg[GREG_RAS0_LO + 0x8 / 4 * ramIndex] << 20);
	winEnd = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex / 2)] << 21) | (m_reg[GREG_RAS0_HI + 0x8 / 4 * ramIndex] << 20) | 0xfffff;
	winSize = winEnd - winStart + 1;
	memory_space->install_read_handler(winStart, winEnd, read32_delegate(FUNC(gt64xxx_device::ras_2_r), this));
	memory_space->install_write_handler(winStart, winEnd, write32_delegate(FUNC(gt64xxx_device::ras_2_w), this));
	LOGGALILEO("map_extra RAS2 start=%08X end=%08X size=%08X\n", winStart, winEnd, winSize);

	// RAS3
	ramIndex = 3;
	winStart = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex / 2)] << 21) | (m_reg[GREG_RAS0_LO + 0x8 / 4 * ramIndex] << 20);
	winEnd = (m_reg[GREG_R1_0_LO + 0x10 / 4 * (ramIndex / 2)] << 21) | (m_reg[GREG_RAS0_HI + 0x8 / 4 * ramIndex] << 20) | 0xfffff;
	winSize = winEnd - winStart + 1;
	memory_space->install_read_handler(winStart, winEnd, read32_delegate(FUNC(gt64xxx_device::ras_3_r), this));
	memory_space->install_write_handler(winStart, winEnd, write32_delegate(FUNC(gt64xxx_device::ras_3_w), this));
	LOGGALILEO("map_extra RAS3 start=%08X end=%08X size=%08X\n", winStart, winEnd, winSize);
}

void gt64xxx_device::reset_all_mappings()
{
	pci_device::reset_all_mappings();
}

// PCI Stalling
WRITE_LINE_MEMBER(gt64xxx_device::pci_stall)
{
	// Reset the retry count once unstalled
	if (state==0 && m_pci_stall_state==1) {
		m_retry_count = 0;
		// Check if it is a stalled cpu access and re-issue
		if (m_pci_cpu_stalled) {
			m_pci_cpu_stalled = 0;
			int index = 0;
			// Should actually check for a stall after each write...
			while (m_stall_windex > 0) {
				// master_mem0_w -- Should actually be checking for master_mem1_w as well
				this->space(AS_PCI_MEM).write_dword((m_reg[GREG_PCI_MEM0_LO] << 21) | (m_cpu_stalled_offset[index] * 4),
					m_cpu_stalled_data[index], m_cpu_stalled_mem_mask[index]);
				LOGGALILEO("pci_stall: Writing index: %d offset: %08x data: %08x mask: %08x\n",
					index, m_cpu_stalled_offset[index] * 4, m_cpu_stalled_data[index], m_cpu_stalled_mem_mask[index]);
				m_stall_windex--;
				index++;
			}
			/* resume CPU execution */
			machine().scheduler().trigger(45678);
			LOGGALILEO("Resuming CPU on PCI Stall\n");
		}
	}

	/* set the new state */
	m_pci_stall_state = state;
}

// PCI bus control
READ32_MEMBER (gt64xxx_device::pci_config_r)
{
	uint32_t result = 0;
	LOGGALILEO("%s galileo pci_config_r from offset %02X = %08X & %08X\n", machine().describe_context(), offset*4, result, mem_mask);
	return result;
}
WRITE32_MEMBER (gt64xxx_device::pci_config_w)
{
	LOGGALILEO("%s galileo pci_config_w to offset %02X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
}
// PCI Master Window 0
READ32_MEMBER (gt64xxx_device::master_mem0_r)
{
	uint32_t result = this->space(AS_PCI_MEM).read_dword((m_reg[GREG_PCI_MEM0_LO]<<21) | (offset*4), mem_mask);
	LOGPCI("%s galileo pci mem0 read from offset %08X = %08X & %08X\n", machine().describe_context(), (m_reg[GREG_PCI_MEM0_LO]<<21) | (offset*4), result, mem_mask);
	return result;
}
WRITE32_MEMBER (gt64xxx_device::master_mem0_w)
{
	if (m_pci_stall_state) {
		if (m_stall_windex < 2) {
			// Save the write data and stall the cpu
			// For some reason sometimes two writes get through before the cpu is stalled (calspeed) so need to store multiple accesses.
			m_pci_cpu_stalled = 1;
			m_cpu_stalled_offset[m_stall_windex] = offset;
			m_cpu_stalled_data[m_stall_windex] = data;
			m_cpu_stalled_mem_mask[m_stall_windex] = mem_mask;
			m_stall_windex++;
			// Stall cpu until trigger
			m_cpu_space->device().execute().spin_until_trigger(45678);
			LOGMASKED(LOG_GALILEO | LOG_PCI, "%s Stalling CPU on PCI Stall\n", machine().describe_context());
		}
		else {
			fatalerror("master_mem0_w: m_stall_windex full\n");
		}
		return;
	}
	this->space(AS_PCI_MEM).write_dword((m_reg[GREG_PCI_MEM0_LO]<<21) | (offset*4), data, mem_mask);
	LOGPCI("%s galileo pci mem0 write to offset %08X = %08X & %08X\n", machine().describe_context(), (m_reg[GREG_PCI_MEM0_LO]<<21) | (offset*4), data, mem_mask);
}

// PCI Master Window 1
READ32_MEMBER (gt64xxx_device::master_mem1_r)
{
	uint32_t result = this->space(AS_PCI_MEM).read_dword((m_reg[GREG_PCI_MEM1_LO]<<21) | (offset*4), mem_mask);
	LOGPCI("%s galileo pci mem1 read from offset %08X = %08X & %08X\n", machine().describe_context(), (m_reg[GREG_PCI_MEM1_LO]<<21) | (offset*4), result, mem_mask);
	return result;
}
WRITE32_MEMBER (gt64xxx_device::master_mem1_w)
{
	this->space(AS_PCI_MEM).write_dword((m_reg[GREG_PCI_MEM1_LO]<<21) | (offset*4), data, mem_mask);
	LOGPCI("%s galileo pci mem1 write to offset %08X = %08X & %08X\n", machine().describe_context(), (m_reg[GREG_PCI_MEM1_LO]<<21) | (offset*4), data, mem_mask);
}

// PCI Master IO
READ32_MEMBER (gt64xxx_device::master_io_r)
{
	uint32_t result = this->space(AS_PCI_IO).read_dword((m_reg[GREG_PCI_IO_LO] << 21) | (offset * 4), mem_mask);
	if (m_prev_addr != offset) {
		m_prev_addr = offset;
		LOGPCI("%s galileo pci io read from offset %08X = %08X & %08X\n", machine().describe_context(), (m_reg[GREG_PCI_IO_LO] << 21) | (offset * 4), result, mem_mask);
	}
	return result;
}
WRITE32_MEMBER (gt64xxx_device::master_io_w)
{
	this->space(AS_PCI_IO).write_dword((m_reg[GREG_PCI_IO_LO] << 21) | (offset * 4), data, mem_mask);
	if (m_prev_addr != offset) {
		m_prev_addr = offset;
		LOGPCI("%s galileo pci io write to offset %08X = %08X & %08X\n", machine().describe_context(), (m_reg[GREG_PCI_IO_LO] << 21) | (offset * 4), data, mem_mask);
	}
}

READ32_MEMBER(gt64xxx_device::ras_0_r)
{
	uint32_t result = m_ram[0][offset];
	LOGPCI("%s galileo ras_0 read from offset %08X = %08X & %08X\n", machine().describe_context(), offset * 4, result, mem_mask);
	return result;
}

WRITE32_MEMBER(gt64xxx_device::ras_0_w)
{
	COMBINE_DATA(&m_ram[0][offset]);
	LOGPCI("%s galileo ras_0 write to offset %08X = %08X & %08X\n", machine().describe_context(), offset * 4, data, mem_mask);
}

READ32_MEMBER(gt64xxx_device::ras_1_r)
{
	uint32_t result = m_ram[1][offset];
	LOGPCI("%s galileo ras_0 read from offset %08X = %08X & %08X\n", machine().describe_context(), offset * 4, result, mem_mask);
	return result;
}

WRITE32_MEMBER(gt64xxx_device::ras_1_w)
{
	COMBINE_DATA(&m_ram[1][offset]);
	LOGPCI("%s galileo ras_0 write to offset %08X = %08X & %08X\n", machine().describe_context(), offset * 4, data, mem_mask);
}

READ32_MEMBER(gt64xxx_device::ras_2_r)
{
	uint32_t result = m_ram[2][offset];
	LOGPCI("%s galileo ras_0 read from offset %08X = %08X & %08X\n", machine().describe_context(), offset * 4, result, mem_mask);
	return result;
}

WRITE32_MEMBER(gt64xxx_device::ras_2_w)
{
	COMBINE_DATA(&m_ram[2][offset]);
	LOGPCI("%s galileo ras_0 write to offset %08X = %08X & %08X\n", machine().describe_context(), offset * 4, data, mem_mask);
}

READ32_MEMBER(gt64xxx_device::ras_3_r)
{
	uint32_t result = m_ram[3][offset];
	LOGPCI("%s galileo ras_0 read from offset %08X = %08X & %08X\n", machine().describe_context(), offset * 4, result, mem_mask);
	return result;
}

WRITE32_MEMBER(gt64xxx_device::ras_3_w)
{
	COMBINE_DATA(&m_ram[3][offset]);
	LOGPCI("%s galileo ras_0 write to offset %08X = %08X & %08X\n", machine().describe_context(), offset * 4, data, mem_mask);
}


// CPU I/F
READ32_MEMBER (gt64xxx_device::cpu_if_r)
{
	uint32_t result = m_reg[offset];

	/* switch off the offset for special cases */
	switch (offset)
	{
		case GREG_TIMER0_COUNT:
		case GREG_TIMER1_COUNT:
		case GREG_TIMER2_COUNT:
		case GREG_TIMER3_COUNT:
		{
			int which = offset % 4;
			galileo_timer *timer = &m_timer[which];

			result = timer->count;
			if (timer->active)
			{
				uint32_t elapsed = (timer->timer->elapsed() * clock()).as_double();
				result = (result > elapsed) ? (result - elapsed) : 0;
			}

			LOGTIMERS("%s hires_timer_r = %08X\n", machine().describe_context(), result);
			break;
		}

		case GREG_PCI_COMMAND:
			// code at 40188 loops until this returns non-zero in bit 0
			//result = 0x0001;
			// bit 0 => byte swap
			// bit 2:1 => SyncMode, 00 = PCLK=[0,33], 01 = PCLK>=TClk/2, 10 = PCLK = TCLK/2
			result = (result & ~0x1) | (m_be ^ 0x1);
			break;

		case GREG_CONFIG_DATA:
			result = config_data_r(space, offset);
			LOGGALILEO("%s Galileo GREG_CONFIG_DATA read from offset %03X = %08X\n", machine().describe_context(), offset*4, result);
			break;

		case GREG_CONFIG_ADDRESS:
			result = config_address_r(space, offset);
			break;

		case GREG_INT_STATE:
		case GREG_INT_MASK:
		case GREG_TIMER_CONTROL:
//          LOGGALILEO("%s Galileo read from offset %03X = %08X\n", machine().describe_context(), offset*4, result);
			break;

		default:
			LOGGALILEO("%s Galileo read from offset %03X = %08X\n", machine().describe_context(), offset*4, result);
			break;
	}

	if (m_be) result =  flipendian_int32(result);

	return result;
}

WRITE32_MEMBER(gt64xxx_device::cpu_if_w)
{
	if (m_be) {
		data = flipendian_int32(data);
		mem_mask = flipendian_int32(mem_mask);
	}

	uint32_t oldata = m_reg[offset];
	COMBINE_DATA(&m_reg[offset]);

	/* switch off the offset for special cases */
	switch (offset)
	{
		case GREG_R1_0_LO:
		case GREG_R1_0_HI:
		case GREG_R3_2_LO:
		case GREG_R3_2_HI:
		case GREG_CS_2_0_LO:
		case GREG_CS_2_0_HI:
		case GREG_CS_3_BOOT_LO:
		case GREG_CS_3_BOOT_HI:
		case GREG_PCI_IO_LO:
		case GREG_PCI_IO_HI:
		case GREG_PCI_MEM0_LO:
		case GREG_PCI_MEM0_HI:
		case GREG_INTERNAL_SPACE:
		case GREG_PCI_MEM1_LO:
		case GREG_PCI_MEM1_HI:
		case GREG_CS3_HI:
			map_cpu_space();
			remap_cb();
			LOGGALILEO("%s Galileo Memory Map data write to offset %03X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
			break;

		case GREG_DMA0_CONTROL:
		case GREG_DMA1_CONTROL:
		case GREG_DMA2_CONTROL:
		case GREG_DMA3_CONTROL:
		{
			int which = offset % 4;

			/* keep the read only activity bit */
			m_reg[offset] &= ~0x4000;
			m_reg[offset] |= (oldata & 0x4000);

			/* fetch next record */
			if (data & 0x2000)
				dma_fetch_next(space, which);
			m_reg[offset] &= ~0x2000;

			/* if enabling, start the DMA */
			if (!(oldata & 0x1000) && (data & 0x1000) && !(m_dma_active & (1<<which)))
			{
				// Trigger the timer if there are no dma's active
				if (m_dma_active==0)
					m_dma_timer->adjust(attotime::zero, 0, DMA_TIMER_PERIOD);
				m_dma_active |= (1<< which);
				//perform_dma(space, which);
				LOGDMA("%s Galileo starting DMA Chan %i\n", machine().describe_context(), which);
			}
			LOGGALILEO("%s Galileo write to offset %03X = %08X & %08X\n", machine().describe_context(), offset * 4, data, mem_mask);
			break;
		}

		case GREG_TIMER0_COUNT:
		case GREG_TIMER1_COUNT:
		case GREG_TIMER2_COUNT:
		case GREG_TIMER3_COUNT:
		{
			int which = offset % 4;
			galileo_timer *timer = &m_timer[which];

			if (which != 0)
				data &= 0xffffff;
			if (!timer->active)
				timer->count = data;
			LOGTIMERS("%s timer/counter %d count = %08X [start=%08X]\n", machine().describe_context(), offset % 4, data, timer->count);
			break;
		}

		case GREG_TIMER_CONTROL:
		{
			int which, mask;

			LOGTIMERS("%s timer/counter control = %08X\n", machine().describe_context(), data);
			for (which = 0, mask = 0x01; which < 4; which++, mask <<= 2)
			{
				galileo_timer *timer = &m_timer[which];
				if (!timer->active && (data & mask))
				{
					timer->active = 1;
					if (timer->count == 0)
					{
						timer->count = m_reg[GREG_TIMER0_COUNT + which];
						if (which != 0)
							timer->count &= 0xffffff;
					}
					timer->timer->adjust(TIMER_PERIOD * timer->count, which);
					LOGTIMERS("Adjusted timer to fire in %f secs\n", (TIMER_PERIOD * timer->count).as_double());
				}
				else if (timer->active && !(data & mask))
				{
					uint32_t elapsed = (timer->timer->elapsed() * clock()).as_double();
					timer->active = 0;
					timer->count = (timer->count > elapsed) ? (timer->count - elapsed) : 0;
					timer->timer->adjust(attotime::never, which);
					LOGTIMERS("Disabled timer\n");
				}
			}
			break;
		}

		case GREG_INT_STATE:
			LOGGALILEO("%s Galileo write to IRQ clear = %08X & %08X\n", offset*4, data, mem_mask);
			m_reg[offset] = oldata & data;
			update_irqs();
			break;

		case GREG_CONFIG_DATA:
			pci_host_device::config_data_w(space, offset, data);
			LOGGALILEO("%s Galileo PCI config data write to offset %03X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
			break;

		case GREG_CONFIG_ADDRESS:
			// Type 0 config transactions signalled by Bus Num = 0 and Device Num != 0
			// Bits 15:11 get mapped into device number for configuration
			uint32_t modData;
			if (0 && (data & 0xff0000) == 0x0 && (data & 0xf800)) {
				// Type 0 transaction
				modData = 0;
				// Select the device based on one hot bit
				for (int i = 11; i<16; i++) {
					if ((data >> i) & 0x1) {
						// One hot encoding, bit 11 will mean device 1
						modData = i - 10;
						break;
					}
				}
				// Re-organize into Type 1 transaction for bus 0 (local bus)
				modData = (modData << 11) | (data & 0x7ff) | (0x80000000);
			}
			else {
				// Type 1 transaction, no modification needed
				modData = data;
			}
			pci_host_device::config_address_w(space, offset, modData);
			LOGGALILEO("%s Galileo PCI config address write to offset %03X = %08X & %08X origData = %08X\n", machine().describe_context(), offset*4, modData, mem_mask, data);
			break;

		case GREG_DMA0_COUNT:   case GREG_DMA1_COUNT:   case GREG_DMA2_COUNT:   case GREG_DMA3_COUNT:
		case GREG_DMA0_SOURCE:  case GREG_DMA1_SOURCE:  case GREG_DMA2_SOURCE:  case GREG_DMA3_SOURCE:
		case GREG_DMA0_DEST:    case GREG_DMA1_DEST:    case GREG_DMA2_DEST:    case GREG_DMA3_DEST:
		case GREG_DMA0_NEXT:    case GREG_DMA1_NEXT:    case GREG_DMA2_NEXT:    case GREG_DMA3_NEXT:
		case GREG_INT_MASK:
			LOGGALILEO("%s Galileo write to offset %03X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
			break;

		default:
			LOGGALILEO("%s Galileo write to offset %03X = %08X & %08X\n", machine().describe_context(), offset*4, data, mem_mask);
			break;
	}
}

/*************************************
 *
 *  Galileo timers & interrupts
 *
 *************************************/

void gt64xxx_device::update_irqs()
{
	int state = CLEAR_LINE;

	/* if any unmasked interrupts are live, we generate */
	if (m_reg[GREG_INT_STATE] & m_reg[GREG_INT_MASK])
		state = ASSERT_LINE;
	if (m_irq_num != -1)
		m_cpu->set_input_line(m_irq_num, state);

	LOGGALILEO("Galileo IRQ %s irqNum: %i state = %08X mask = %08X\n", (state == ASSERT_LINE) ? "asserted" : "cleared", m_irq_num, m_reg[GREG_INT_STATE], m_reg[GREG_INT_MASK]);
}


TIMER_CALLBACK_MEMBER(gt64xxx_device::timer_callback)
{
	int which = param;
	galileo_timer *timer = &m_timer[which];

	LOGTIMERS("timer %d fired\n", which);

	/* copy the start value from the registers */
	timer->count = m_reg[GREG_TIMER0_COUNT + which];
	if (which != 0)
		timer->count &= 0xffffff;

	/* if we're a timer, adjust the timer to fire again */
	if (m_reg[GREG_TIMER_CONTROL] & (2 << (2 * which)))
		timer->timer->adjust(TIMER_PERIOD * timer->count, which);
	else
		timer->active = timer->count = 0;

	/* trigger the interrupt */
	m_reg[GREG_INT_STATE] |= 1 << (GINT_T0EXP_SHIFT + which);
	update_irqs();
}

/*************************************
 *
 *  Galileo DMA handler
 *
 *************************************/
address_space* gt64xxx_device::dma_decode_address(uint32_t &addr)
{
	for (size_t index = 0; index < proc_addr_bank::ADDR_NUM; ++index)
	{
		if (addr >= dma_addr_map[index].low_addr && addr <= dma_addr_map[index].high_addr)
			return dma_addr_map[index].space;
	}
	return nullptr;
}

int gt64xxx_device::dma_fetch_next(address_space &space, int which)
{
	offs_t address = 0;
	uint32_t data;

	/* no-op for unchained mode */
	if (!(m_reg[GREG_DMA0_CONTROL + which] & 0x200))
		address = m_reg[GREG_DMA0_NEXT + which];

	/* if we hit the end address, signal an interrupt */
	if (address == 0)
	{
		if (m_reg[GREG_DMA0_CONTROL + which] & 0x400)
		{
			m_reg[GREG_INT_STATE] |= 1 << (GINT_DMA0COMP_SHIFT + which);
			update_irqs();
		}
		m_reg[GREG_DMA0_CONTROL + which] &= ~0x5000;
		return 0;
	}

	/* fetch the byte count */
	data = space.read_dword(address); address += 4;
	m_reg[GREG_DMA0_COUNT + which] = data;

	/* fetch the source address */
	data = space.read_dword(address); address += 4;
	m_reg[GREG_DMA0_SOURCE + which] = data;

	/* fetch the dest address */
	data = space.read_dword(address); address += 4;
	m_reg[GREG_DMA0_DEST + which] = data;

	/* fetch the next record address */
	data = space.read_dword(address); address += 4;
	m_reg[GREG_DMA0_NEXT + which] = data;
	return 1;
}


TIMER_CALLBACK_MEMBER (gt64xxx_device::perform_dma)
{
	// Cycle through the channels
	int which = -1;
	for (int i = 1; i <= 4; i++)
	{
		which = (m_last_dma + i) % 4;
		if ((m_dma_active & (1 << which)) && (m_reg[GREG_DMA0_CONTROL + which] & 0x1000))
			break;

	}
	// Save which dma is processed for arbitration next time
	m_last_dma = which;

	if (which==-1)
	{
		logerror("gt64xxx_device::perform_dma Warning! DMA Timer called with no pending DMA. m_dma_active = %08X\n", m_dma_active);
	} else
	{
		offs_t srcaddr = m_reg[GREG_DMA0_SOURCE + which];
		offs_t dstaddr = m_reg[GREG_DMA0_DEST + which];
		uint32_t bytesleft = m_reg[GREG_DMA0_COUNT + which] & 0xffff;
		address_space* srcSpace = dma_decode_address(srcaddr);
		address_space* dstSpace = dma_decode_address(dstaddr);

		int srcinc, dstinc;

		m_reg[GREG_DMA0_CONTROL + which] |= 0x5000;

		/* determine src/dst inc */
		switch ((m_reg[GREG_DMA0_CONTROL + which] >> 2) & 3)
		{
			default:
			case 0:     srcinc = 1;     break;
			case 1:     srcinc = -1;    break;
			case 2:     srcinc = 0;     break;
		}
		switch ((m_reg[GREG_DMA0_CONTROL + which] >> 4) & 3)
		{
			default:
			case 0:     dstinc = 1;     break;
			case 1:     dstinc = -1;    break;
			case 2:     dstinc = 0;     break;
		}

		LOGDMA("Performing DMA%d: src=%08X dst=%08X bytes=%04X sinc=%d dinc=%d\n", which, srcaddr, dstaddr, bytesleft, srcinc, dstinc);

		int burstCount = 0;
		/* standard transfer */
		while (bytesleft > 0 && burstCount < DMA_BURST_SIZE)
		{
			if (m_pci_stall_state)
			{
				if (m_retry_count<4) LOGDMA("%s Stalling DMA on voodoo retry_count: %i\n", machine().describe_context(), m_retry_count);
				// Save info
				m_reg[GREG_DMA0_SOURCE + which] = srcaddr;
				m_reg[GREG_DMA0_DEST + which] = dstaddr;
				m_reg[GREG_DMA0_COUNT + which] = (m_reg[GREG_DMA0_COUNT + which] & ~0xffff) | bytesleft;

				m_retry_count++;
				uint32_t configRetryCount = (m_reg[GREG_PCI_TIMEOUT] >> 16) & 0xff;
				if (m_retry_count >= configRetryCount && configRetryCount > 0)
				{
					logerror("gt64xxx_device::perform_dma Error! Too many PCI retries. DMA%d: src=%08X dst=%08X bytes=%04X sinc=%d dinc=%d\n", which, srcaddr, dstaddr, bytesleft, srcinc, dstinc);
					// Signal error and abort DMA
					m_dma_active &= ~(1 << which);
					m_retry_count = 0;
					return;
				}
				else
				{
					// Come back later
					return;
				}
			}
			if (bytesleft < 4)
			{
				dstSpace->write_byte(dstaddr, srcSpace->read_byte(srcaddr));
				srcaddr += srcinc;
				dstaddr += dstinc;
				bytesleft--;
			}
			else {
				dstSpace->write_dword(dstaddr, srcSpace->read_dword(srcaddr));
				srcaddr += srcinc * 4;
				dstaddr += dstinc * 4;
				bytesleft -= 4;
			}
			burstCount++;
		}
		/* not verified, but seems logical these should be updated byte the end */
		m_reg[GREG_DMA0_SOURCE + which] = srcaddr;
		m_reg[GREG_DMA0_DEST + which] = dstaddr;
		m_reg[GREG_DMA0_COUNT + which] = (m_reg[GREG_DMA0_COUNT + which] & ~0xffff) | bytesleft;

		/* if we did not hit zero, punt and return later */
		if (bytesleft != 0)
		{
			return;
		}
		/* interrupt? */
		if (!(m_reg[GREG_DMA0_CONTROL + which] & 0x400))
		{
			m_reg[GREG_INT_STATE] |= 1 << (GINT_DMA0COMP_SHIFT + which);
			update_irqs();
		}

		// Fetch the next dma for this channel (to be performed next scheduled burst)
		if (dma_fetch_next(*m_cpu_space, which) == 0)
		{
			m_dma_active &= ~(1 << which);
			// Turn off the timer
			m_dma_timer->adjust(attotime::never);
		}
	}
}