summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/drivers/gba.cpp
blob: 6f81073cd0559f4c547a6759e2a24338faa9e5c0 (plain) (tree)
1
2
3
4
5
6
7
8
9
                       
                                          

                                                                            
         


                                                                   
                            
 


                                                                            
                         
 
                        

                              
                     
                           
                     

                    
 



                                                                                       
                                                                                                 
                                                                            
                                                                                                 















                                                                                                          
                                                                                                 







                                                                                                        
                                                                                                 
                                                                              
                                                                                                 


                                                                                     
                                                                                                 




                                                                                                    
                                                                                                 
                                                                                           








                                                                                                                                        
























                                                                  














                                      
                           
 
                                                                                                     







                                          
                                                                                        


         
                                                                                                                               
 
 
                                              
 
                                      
                         

                                              
                       


                                 
                            
                 

                                                                              



                 
                                              
 
                                                                                     
 
                             


                                        
                                                 
 
                                



                          
                                        





                                                                                                        
                                                                 





                                              
                                                     



                                                                             
                                                           




                         
                                
 
                                                            


                                     

                                     

                                
                     

                            
                                      
                    
                                     


                                      
                                                                


                           
                                                                   
                               


                                                                             









                                                                           
                                                                                                                                                

         
                                     


                                   

                                          

                                 
                                                                      

                                       
                                                    

                                                 
                                                    

                                                 
                                                     
                                              
                                                               




                                                 
                                                    

                                                 
                                                    

                                                 
                                                     
                                              
                                                                                                      








                                              
                                                                    

                                       
                                                    

                                                 
                                                    

                                                 
                                                     
                                              
                                                               




                                                 
                                                    

                                                 
                                                    

                                                 
                                                     
                                              
                                                                                      




                                              

                            
 
                                                                                             
                                                            
                                  

 
                                   
 
                                 
                       


                 
                                                
                 
                                               
                         
                                                 

                         
                                               
                         
                                                                       
                         
                                               
                         
                                                                       
                         
                                       


                              
                                                

                                                      
                                                                                           

                                                  
                                            
                         
                                                                                           

                                                  
                                            




                         
                                                
                 
                                               
                         
                                                 

                         
                                                
                         
                                                                       
                         
                                                
                         
                                                                       
                         
                                       

                 
                                                

                                                      
                                                                                           

                                                  
                                            
                         
                                                                                           

                                                  
                                            




                         
                                              
 
                                                                                                                       
                                          
 

                                                                                                                                
                                     


                                             


                                                                                                          
                                                                              
                                        
                                        
                                                
                                                          




                                                         
                                          
                                      
 
                                           
                                      



                     
                                       
                                      
 
                                        
                                      





                                 
                                              
                 

                                                                                                                                   
                         
                                                                     
                                                                                    
                                 
                                                                 
                                 
                                                                   
                                 

                                                                                                                                                   
                                         
                                                                                     
                                                                                                    
                                                 
                                                                                 
                                                 
                                                                                   
                                                 

                                                                                                                                                                   
                                                         
                                                                                                     
                                                                                                                    
                                                                 
                                                                                                 








                                                                 
                                              
                 

                                                                                                                                   
                         
                                                                     
                                                                                    
                                 
                                                                 
                                 
                                                                   
                                 

                                                                                                                                                   
                                         
                                                                                     
                                                                                                    
                                                 
                                                                                 






                                                 
                                              
                 

                                                                                                                                   
                         
                                                                     
                                                                                    
                                 
                                                                 






                                  
                                                         
         
                                           


         
                                            
 
                        
 
                                             

 
                                        













                                                                

                                  
                            
 
                                 
         
                              
                                                                                                               

                              
                                                                                  

                              
                                                                                  

                              
                                                                                  

                              
                                                                                                                     

                              
                                                                                      

                              
                                                                                        

                              
                                                                                        

                              
                                                                                        
                                                  
                         
                                                           


                              
                                                          
                              
                              
                                                                                                                                      

                              
                                                                                                                                      

                              
                                                                                                                                        

                              
                                                                                                                                          


                              
                                                                                    
                              





                                                
                         
                                            
                         
                                                 
                         
                                                            

                              





                                                
                         
                                            
                         
                                                 
                         
                                                            
                         
                              





                                                
                         
                                            
                         
                                                 
                         
                                                            
                         
                              







                                                                                                                                                               
                         
                                            
                         
                                                 
                         
                                                            

                              



                              
                         
                                                 
                                                   
                                                                      
 
                                                                              


















                                                                                                           
                                                                         










                                                                                                 

                              

                                                               
                         
                                                             
                         
                                                       
                         
                                                       

                              



                                                 
                                                 
                         
                                                                      
                         
                                                  
                         
                                                                      

                              

         
                                                                                                  

                                
         




                                                                                                                          






                                   


                                                                                      
 
                                      
 
                                                                                                  









                                                                                                                         
                                 
         
                              
                                                               
                         
                                                            
                         
                                                  
                         
                                                                                
                         
                                                  
                         
                                                                


                              
                                                               
                         
                                                            
                         
                                                 
                         
                                                                                


                              
                                                
                         
                                                            
                         
                                                 
                         
                                                               


                              
                                                
                         
                                                            
                         
                                                 
                         
                                                               

                              
                                                
                                                               
                         
                                                              
                         
                                                  
                         
                                                                                    
                         
                                                  
                         
                                                                  


                              
                                                
                         
                                                              
                         
                                                 
                         
                                                                 


                              
                                                
                         
                                                               
                         
                                                 
                         
                                                                  


                              
                                                
                         
                                                               
                         
                                                 
                         
                                                                  


                              
                                                
                         
                                                               
                         
                                                 
                         
                                                                  

                         
                                                 
                         
                                               
                                                      
                                 

                                                          

                                                          


                                               
                                                      
                                 

                                                          

                                                          



                                 
                                                
                         
                                                               
                                                                          


                                                                        



                                                          
                                 


                              
                                                
                         
                                                           
                         
                                                 
                         
                                                              
                         
                                                  
                         
                                                               
                         
                                                  
                         
                                                               


                              
                                                
                         
                                                           
                         
                                                 
                         
                                                              
                         
                                                  
                         
                                                               
                         
                                                  
                         
                                                               


                              
                                                
                         
                                                           
                         
                                                 
                         
                                                              
                         
                                                  
                         
                                                                 
                         
                                                  
                         
                                                                 


                              
                                                
                         
                                                             
                         
                                                 
                         
                                                                
                         
                                                  
                         
                                                                 
                         
                                                  
                         
                                                                 


                              



















                                                                          

                              



















                                                                          
                              
                              
                              
                              
                              
                                                  
                         
                                                                        
 
                                                    
 
                                                                                                                                   
 

                                                                                                                                                        
 


                                                                                
                                 


                                                                     
 

                                                                 
                                         

                                                             

                                         







                                                      
                                                        
 
                                                                      
 
                                                                     
 
                                                                                                                                          
 
                                                                                                    


                                                        

                                                                                                                                       








                                                                                                   
                                                                                                                                                

                                         
                                                                                        
 
                                                                                                        


                                                                
                                                                  
 
                                                                  
 
                                                                        




                                                                                                
                                                                                              



                                         
                              
                                                 
                         
                                                
                                                                         

                                                  
                                                                                  
                                         
                                                                     


                                                                      
                                                                             


                                                 


                              
                                                 
                         
     
                                            
                                 
                                                        


                                 
                                                  
                         
                                                                    

                                                                                       
                                       
                                 
                                                                                                


                                 
                              
                                                 
                         
                                       



                                                                            

                              
                                                 
                         

                                                                     

                              
         



                                    
                                         
                                
         



                                                                 
         
 
                                      
         
                                                                              

                         
 
                                    



                                        
                                                    

                                                    



                             
                                                        

                            
                                                 


            
                                                         

                                                  
                                                                                                                                                          


                    




































                                                                   


                                                                                              
                                                                                            


                                                                                                            
                                                                                             




                                                                                                                                                     
                                                                                                               
                                                                                                                     
 

                                 
                            
                                                     

                                                                                            



                                                                            

                                                                                 

                                                                                            




                                                                  

               
 
                               
 

                                          







                              
 




                                                   
                                                             
                                       

                             



                          

 
                               
 
                                          



                                                                                                                       



                                                   

                                           



                                                                                                                       



                                                   

                                       
                                                                                                                  
                                             
 


                                                                       


                                                                                               
 
                                       
                                                                                                                     
 




                                                            

                                                                                                                                                            
 



                                                               
                                                                                                                               
                 



                                                                                                                                                                   
                 
                                                                                                                                                                    

                                                                                                                      

                                                                                                                                                                   
                 


                                                      

                                                                                                                                                                   
                                      

                                                                                                                                                                    
                 
                                                                       
                                                                                                                                 
                 

                                                                                                                                                                   
                 
                                                                                               
                 

                                                                                                                                                                   
                 

                                                       
                                                                                                                                                                        
                                                                                                                                

                                                                       
 
         
 
                                


                                   









                                        

 
 

















                                                                                                           
 
 



                                                                
 





                                                                            
 

                                                  
















                                                                                                                  
 
 





                                                                                                               

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

  gba.cpp

  Driver file to handle emulation of the Nintendo Game Boy Advance.

  By R. Belmont & Ryan Holtz

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

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

#include "bus/gba/rom.h"
#include "cpu/arm7/arm7.h"
#include "cpu/arm7/arm7core.h"
#include "sound/gb.h"
#include "sound/volt_reg.h"
#include "softlist.h"
#include "speaker.h"


/* Sound Registers */
#define SOUNDCNT_L  HWLO(0x080)  /* 0x4000080  2  R/W   Control Stereo/Volume/Enable */
#define SOUNDCNT_H  HWHI(0x080)  /* 0x4000082  2  R/W   Control Mixing/DMA Control */
#define SOUNDCNT_X  HWLO(0x084)  /* 0x4000084  2  R/W   Control Sound on/off */
								 /* 0x4000086  2  -     Unused */
#define SOUNDBIAS   HWLO(0x088)  /* 0x4000088  2  BIOS  Sound PWM Control */
								 /* 0x400008A  2  -     Unused */

/* DMA Registers (4 Transfer Channels) */
#define DMASAD(c)   WORD(0x0B0 + c * 0xC) /* 0x40000B0  4  W     DMA 0 Source Address */
#define DMADAD(c)   WORD(0x0B4 + c * 0xC) /* 0x40000B4  4  W     DMA 0 Destination Address */
#define DMACNT_L(c) HWLO(0x0B8 + c * 0xC) /* 0x40000B8  2  W     DMA 0 Word Count */
#define DMACNT_H(c) HWHI(0x0B8 + c * 0xC) /* 0x40000BA  2  R/W   DMA 0 Control */

/* Serial Communication (1) Registers */
#define SIODATA32   WORD(0x120)  /* 0x4000120  4  R/W   SIO Data (Normal-32bit Mode; shared with below) */
#define SIOMULTI0   HWLO(0x120)  /* 0x4000120  2  R/W   SIO Data 0 (Parent)    (Multi-Player Mode) */
#define SIOMULTI1   HWHI(0x120)  /* 0x4000122  2  R/W   SIO Data 1 (1st Child) (Multi-Player Mode) */
#define SIOMULTI2   HWLO(0x124)  /* 0x4000124  2  R/W   SIO Data 2 (2nd Child) (Multi-Player Mode) */
#define SIOMULTI3   HWHI(0x124)  /* 0x4000126  2  R/W   SIO Data 3 (3rd Child) (Multi-Player Mode) */
#define SIOCNT      HWLO(0x128)  /* 0x4000128  2  R/W   SIO Control Register */
#define SIOMLT_SEND HWHI(0x128)  /* 0x400012A  2  R/W   SIO Data (Local of MultiPlayer; shared below) */
#define SIODATA8    HWHI(0x128)  /* 0x400012A  2  R/W   SIO Data (Normal-8bit and UART Mode) */
								 /* 0x400012C  2  -     Unused */

/* Keypad Input Registers */
#define KEYINPUT    HWLO(0x130)  /* 0x4000130  2  R     Key Status */
#define KEYCNT      HWHI(0x130)  /* 0x4000132  2  R/W   Key Interrupt Control */

/* Serial Communication (2) Registers */
#define RCNT        HWLO(0x134)  /* 0x4000134  2  R/W   SIO Mode Select/General Purpose Data */
#define IR          HWHI(0x134)  /* 0x4000136  2  R/W   Ancient - Infrared Register (Prototypes only) */
								 /* 0x4000138  8  -     Unused */
#define JOYCNT      HWLO(0x140)  /* 0x4000140  2  R/W   SIO JOY Bus Control */
								 /* 0x4000142  2  -     Unused */
#define JOY_RECV    WORD(0x150)  /* 0x4000150  4  R/W   SIO JOY Bus Receive Data */
#define JOY_TRANS   WORD(0x154)  /* 0x4000154  4  R/W   SIO JOY Bus Transmit Data */
#define JOYSTAT     HWLO(0x158)  /* 0x4000158  2  R/?   SIO JOY Bus Receive Status */
								 /* 0x400015A  2  -     Unused */

/* Interrupt, Waitstate, and Power-Down Control Registers */
#define IE          HWLO(0x200)  /* 0x4000200  2  R/W   Interrupt Enable Register */
#define IF          HWHI(0x200)  /* 0x4000202  2  R/W   Interrupt Request Flags / IRQ Acknowledge */
#define WAITCNT     HWLO(0x204)  /* 0x4000204  2  R/W   Game Pak Waitstate Control */
								 /* 0x4000206     -     Unused */
#define IME         HWLO(0x208)  /* 0x4000208  2  R/W   Interrupt Master Enable Register */
								 /* 0x400020A     -     Unused */
								 /* 0x4000300  1  R/W   Undocumented - Post Boot Flag */
								 /* 0x4000301  1  W     Undocumented - Power Down Control */
								 /* 0x4000302     -     Unused */
								 /* 0x4000410  ?  ?     Undocumented - Purpose Unknown / Bug ??? 0FFh */
								 /* 0x4000411     -     Unused */
								 /* 0x4000800  4  R/W   Undocumented - Internal Memory Control (R/W) */
								 /* 0x4000804     -     Unused */
								 /* 0x4xx0800  4  R/W   Mirrors of 4000800h (repeated each 64K) */

#define SOUNDBIAS_SET(val)      HWLO_SET(0x088, val)

#define DMASAD_SET(c, val)      WORD_SET(0x0B0 + (c * 0xC), val)
#define DMADAD_SET(c, val)      WORD_SET(0x0B4 + (c * 0xC), val)
#define DMACNT_L_SET(c, val)    HWLO_SET(0x0B8 + (c * 0xC), val)
#define DMACNT_H_SET(c, val)    HWHI_SET(0x0B8 + (c * 0xC), val)
#define DMACNT_H_RESET(c, val)  HWHI_RESET(0x0B8 + (c * 0xC), val)

#define SIOMULTI0_SET(val)      HWLO_SET(0x120, val)
#define SIOMULTI1_SET(val)      HWHI_SET(0x120, val)
#define SIOMULTI2_SET(val)      HWLO_SET(0x124, val)
#define SIOMULTI3_SET(val)      HWHI_SET(0x124, val)

#define SIOCNT_RESET(val)       HWLO_RESET(0x128, val)

#define KEYCNT_SET(val)         HWHI_SET(0x130, val)

#define RCNT_SET(val)           HWLO_SET(0x134, val)

#define JOYSTAT_SET(val)        HWLO_SET(0x158, val)

#define IF_SET(val)             HWHI_SET(0x200, val)
#define IF_RESET(val)           HWHI_RESET(0x200, val)

#define INT_VBL                 0x0001
#define INT_HBL                 0x0002
#define INT_VCNT                0x0004
#define INT_TM0_OVERFLOW        0x0008
#define INT_TM1_OVERFLOW        0x0010
#define INT_TM2_OVERFLOW        0x0020
#define INT_TM3_OVERFLOW        0x0040
#define INT_SIO                 0x0080
#define INT_DMA0                0x0100
#define INT_DMA1                0x0200
#define INT_DMA2                0x0400
#define INT_DMA3                0x0800
#define INT_KEYPAD              0x1000
#define INT_GAMEPAK             0x2000

#define VERBOSE_LEVEL   (0)

static inline void ATTR_PRINTF(3,4) verboselog(device_t &device, int n_level, const char *s_fmt, ...)
{
	if( VERBOSE_LEVEL >= n_level )
	{
		va_list v;
		char buf[ 32768 ];
		va_start( v, s_fmt );
		vsprintf( buf, s_fmt, v );
		va_end( v );
		device.logerror( "%08x: %s", device.machine().describe_context(), buf );
	}
}

static const XTAL timer_clks[4] = { XTAL(16'777'216), XTAL(16'777'216) / 64, XTAL(16'777'216) / 256, XTAL(16'777'216) / 1024 };


void gba_state::request_irq(uint32_t int_type)
{
	// set flag for later recovery
	IF_SET(int_type);

	// is this specific interrupt enabled?
	int_type &= IE;
	if (int_type != 0)
	{
		// master enable?
		if (IME & 1)
		{
			m_maincpu->set_input_line(ARM7_IRQ_LINE, ASSERT_LINE);
			m_maincpu->set_input_line(ARM7_IRQ_LINE, CLEAR_LINE);
		}
	}
}

TIMER_CALLBACK_MEMBER(gba_state::dma_complete)
{
	static const uint32_t ch_int[4] = { INT_DMA0, INT_DMA1, INT_DMA2, INT_DMA3 };

	uintptr_t ch = param;

//  printf("dma complete: ch %d\n", ch);

	m_dma_timer[ch]->adjust(attotime::never);

	int ctrl = DMACNT_H(ch);

	// IRQ
	if (ctrl & 0x4000)
	{
		request_irq(ch_int[ch]);
	}

	// if we're supposed to repeat, don't clear "active" and then the next vbl/hbl will retrigger us
	// always clear active for immediate DMAs though
	if (!((ctrl>>9) & 1) || ((ctrl & 0x3000) == 0))
	{
		DMACNT_H_RESET(ch, 0x8000); // clear "active" bit
	}
	else
	{
		// if repeat, reload the count
		if ((ctrl>>9) & 1)
		{
			m_dma_cnt[ch] = DMACNT_L(ch);

			// if increment & reload mode, reload the destination
			if (((ctrl>>5)&3) == 3)
			{
				m_dma_dst[ch] = DMADAD(ch);
			}
		}
	}
}

void gba_state::dma_exec(int ch)
{
	address_space &space = m_maincpu->space(AS_PROGRAM);
	uint32_t src = m_dma_src[ch];
	uint32_t dst = m_dma_dst[ch];
	uint16_t ctrl = DMACNT_H(ch);
	int srcadd = (ctrl >> 7) & 3;
	int dstadd = (ctrl >> 5) & 3;

	int cnt = m_dma_cnt[ch];
	if (cnt == 0)
	{
		if (ch == 3)
			cnt = 0x10000;
		else
			cnt = 0x4000;
	}

	// override special parameters
	if ((ctrl & 0x3000) == 0x3000)      // special xfer mode
	{
		switch (ch)
		{
			case 1:         // Ch 1&2 are for audio DMA
			case 2:
				dstadd = 2; // don't increment destination
				cnt = 4;    // always transfer 4 32-bit words
				ctrl |= 0x400;  // always 32-bit
				break;

			case 3:
				printf("Unsupported DMA 3 special mode\n");
				break;
		}
	}
	else
	{
//      if (dst >= 0x6000000 && dst <= 0x6017fff)
//      printf("DMA exec: ch %d from %08x to %08x, mode %04x, count %04x (%s)\n", (int)ch, src, dst, ctrl, cnt, ((ctrl>>10) & 1) ? "32" : "16");
	}

	for (int i = 0; i < cnt; i++)
	{
		if ((ctrl>>10) & 1)
		{
			src &= 0xfffffffe;
			dst &= 0xfffffffe;

			// 32-bit
			space.write_dword(dst, space.read_dword(src));
			switch (dstadd)
			{
				case 0: // increment
					dst += 4;
					break;
				case 1: // decrement
					dst -= 4;
					break;
				case 2: // don't move
					break;
				case 3: // increment and reload
					dst += 4;
					break;
			}
			switch (srcadd)
			{
				case 0: // increment
					src += 4;
					break;
				case 1: // decrement
					src -= 4;
					break;
				case 2: // don't move
					break;
				case 3: // not used ("Metal Max 2 Kai" expects no increment/decrement)
					break;
			}
		}
		else
		{
			src &= 0xfffffffe;
			dst &= 0xfffffffe;

			// 16-bit
			space.write_word(dst, space.read_word(src));
			switch (dstadd)
			{
				case 0: // increment
					dst += 2;
					break;
				case 1: // decrement
					dst -= 2;
					break;
				case 2: // don't move
					break;
				case 3: // increment and reload
					dst += 2;
					break;
			}
			switch (srcadd)
			{
				case 0: // increment
					src += 2;
					break;
				case 1: // decrement
					src -= 2;
					break;
				case 2: // don't move
					break;
				case 3: // not used (see note in 32-bit version above)
					break;
			}
		}
	}

	m_dma_src[ch] = src;
	m_dma_dst[ch] = dst;

//  printf("settng DMA timer %d for %d cycs (tmr %x)\n", ch, cnt, (uint32_t)m_dma_timer[ch]);
//  m_dma_timer[ch]->adjust(ATTOTIME_IN_CYCLES(0, cnt), ch);
	dma_complete(nullptr, ch);
}

void gba_state::audio_tick(int ref)
{
	if (!(SOUNDCNT_X & 0x80))
		return;

	if (!ref)
	{
		if (m_fifo_a_ptr != m_fifo_a_in)
		{
			if (m_fifo_a_ptr == 17)
			{
				m_fifo_a_ptr = 0;
			}

			if (SOUNDCNT_H & 0x200)
			{
				m_ldaca->write(m_fifo_a[m_fifo_a_ptr]);
			}
			if (SOUNDCNT_H & 0x100)
			{
				m_rdaca->write(m_fifo_a[m_fifo_a_ptr]);
			}
			m_fifo_a_ptr++;
		}

		// fifo empty?
		if (m_fifo_a_ptr == m_fifo_a_in)
		{
			// is a DMA set up to feed us?
			if ((DMADAD(1) == 0x40000a0) && ((DMACNT_H(1) & 0x3000) == 0x3000))
			{
				// channel 1 it is
				dma_exec(1);
			}
			if ((DMADAD(2) == 0x40000a0) && ((DMACNT_H(2) & 0x3000) == 0x3000))
			{
				// channel 2 it is
				dma_exec(2);
			}
		}
	}
	else
	{
		if (m_fifo_b_ptr != m_fifo_b_in)
		{
			if (m_fifo_b_ptr == 17)
			{
				m_fifo_b_ptr = 0;
			}

			if (SOUNDCNT_H & 0x2000)
			{
				m_ldacb->write(m_fifo_b[m_fifo_b_ptr]);
			}
			if (SOUNDCNT_H & 0x1000)
			{
				m_rdacb->write(m_fifo_b[m_fifo_b_ptr]);
			}
			m_fifo_b_ptr++;
		}

		if (m_fifo_b_ptr == m_fifo_b_in)
		{
			// is a DMA set up to feed us?
			if ((DMADAD(1) == 0x40000a4) && ((DMACNT_H(1) & 0x3000) == 0x3000))
			{
				// channel 1 it is
				dma_exec(1);
			}
			if ((DMADAD(2) == 0x40000a4) && ((DMACNT_H(2) & 0x3000) == 0x3000))
			{
				// channel 2 it is
				dma_exec(2);
			}
		}
	}
}

TIMER_CALLBACK_MEMBER(gba_state::timer_expire)
{
	static const uint32_t tmr_ints[4] = { INT_TM0_OVERFLOW, INT_TM1_OVERFLOW, INT_TM2_OVERFLOW, INT_TM3_OVERFLOW };
	uintptr_t tmr = (uintptr_t) param;

	// "The reload value is copied into the counter only upon following two situations: Automatically upon timer overflows,"
	// "or when the timer start bit becomes changed from 0 to 1."
	if (m_timer_recalc[tmr] != 0)
	{
		double rate, clocksel, final;
		attotime time;
		m_timer_recalc[tmr] = 0;
		m_timer_regs[tmr] = (m_timer_regs[tmr] & 0xFFFF0000) | (m_timer_reload[tmr] & 0x0000FFFF);
		rate = 0x10000 - (m_timer_regs[tmr] & 0xffff);
		clocksel = timer_clks[(m_timer_regs[tmr] >> 16) & 3].dvalue();
		final = clocksel / rate;
		m_timer_hz[tmr] = final;
		time = attotime::from_hz(final);
		m_tmr_timer[tmr]->adjust(time, tmr, time);
	}

	// check if timers 0 or 1 are feeding directsound
	if (tmr == 0)
	{
		if (!(SOUNDCNT_H & 0x400))
			audio_tick(0);

		if (!(SOUNDCNT_H & 0x4000))
			audio_tick(1);
	}

	if (tmr == 1)
	{
		if (SOUNDCNT_H & 0x400)
			audio_tick(0);

		if (SOUNDCNT_H & 0x4000)
			audio_tick(1);
	}

	// Handle count-up timing
	switch (tmr)
	{
	case 0:
		if (m_timer_regs[1] & 0x40000)
		{
			m_timer_regs[1] = (( ( m_timer_regs[1] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[1] & 0xffff0000);
			if( ( m_timer_regs[1] & 0x0000ffff ) == 0 )
			{
				m_timer_regs[1] |= m_timer_reload[1];
				if( ( m_timer_regs[1] & 0x400000 ) && ( IME != 0 ) )
				{
					request_irq(tmr_ints[1]);
				}
				if( ( m_timer_regs[2] & 0x40000 ) )
				{
					m_timer_regs[2] = (( ( m_timer_regs[2] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[2] & 0xffff0000);
					if( ( m_timer_regs[2] & 0x0000ffff ) == 0 )
					{
						m_timer_regs[2] |= m_timer_reload[2];
						if( ( m_timer_regs[2] & 0x400000 ) && ( IME != 0 ) )
						{
							request_irq(tmr_ints[2]);
						}
						if( ( m_timer_regs[3] & 0x40000 ) )
						{
							m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
							if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
							{
								m_timer_regs[3] |= m_timer_reload[3];
								if( ( m_timer_regs[3] & 0x400000 ) && ( IME != 0 ) )
								{
									request_irq(tmr_ints[3]);
								}
							}
						}
					}
				}
			}
		}
		break;
	case 1:
		if (m_timer_regs[2] & 0x40000)
		{
			m_timer_regs[2] = (( ( m_timer_regs[2] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[2] & 0xffff0000);
			if( ( m_timer_regs[2] & 0x0000ffff ) == 0 )
			{
				m_timer_regs[2] |= m_timer_reload[2];
				if( ( m_timer_regs[2] & 0x400000 ) && ( IME != 0 ) )
				{
					request_irq(tmr_ints[2]);
				}
				if( ( m_timer_regs[3] & 0x40000 ) )
				{
					m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
					if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
					{
						m_timer_regs[3] |= m_timer_reload[3];
						if( ( m_timer_regs[3] & 0x400000 ) && ( IME != 0 ) )
						{
							request_irq(tmr_ints[3]);
						}
					}
				}
			}
		}
		break;
	case 2:
		if (m_timer_regs[3] & 0x40000)
		{
			m_timer_regs[3] = (( ( m_timer_regs[3] & 0x0000ffff ) + 1 ) & 0x0000ffff) | (m_timer_regs[3] & 0xffff0000);
			if( ( m_timer_regs[3] & 0x0000ffff ) == 0 )
			{
				m_timer_regs[3] |= m_timer_reload[3];
				if( ( m_timer_regs[3] & 0x400000 ) && ( IME != 0 ) )
				{
					request_irq(tmr_ints[3]);
				}
			}
		}
		break;
	}

	// are we supposed to IRQ?
	if ((m_timer_regs[tmr] & 0x400000) && (IME != 0))
	{
		request_irq(tmr_ints[tmr]);
	}
}

TIMER_CALLBACK_MEMBER(gba_state::handle_irq)
{
	request_irq(IF);

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

static const char *const reg_names[] = {
	/* Sound Registers */
	"SOUND1CNT_L", "SOUND1CNT_H", "SOUND1CNT_X", "Unused",
	"SOUND2CNT_L", "Unused",      "SOUND2CNT_H", "Unused",
	"SOUND3CNT_L", "SOUND3CNT_H", "SOUND3CNT_X", "Unused",
	"SOUND4CNT_L", "Unused",      "SOUND4CNT_H", "Unused",
	"SOUNDCNT_L",  "SOUNDCNT_H",  "SOUNDCNT_X",  "Unused",
	"SOUNDBIAS",   "Unused",      "WAVE_RAM",    "WAVE_RAM",
	"WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",
	"WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",
	"WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",    "WAVE_RAM",
	"WAVE_RAM",    "WAVE_RAM",    "FIFO_A_L",    "FIFO_A_H",
	"FIFO_B_L",    "FIFO_B_H"
};

READ32_MEMBER(gba_state::gba_io_r)
{
	uint32_t retval = 0;

	switch( offset + 0x60/4 )
	{
		case 0x0060/4:
			retval = m_gbsound->sound_r(0) | m_gbsound->sound_r(1)<<16 | m_gbsound->sound_r(2)<<24;
			break;
		case 0x0064/4:
			retval = m_gbsound->sound_r(3) | m_gbsound->sound_r(4)<<8;
			break;
		case 0x0068/4:
			retval = m_gbsound->sound_r(6) | m_gbsound->sound_r(7)<<8;
			break;
		case 0x006c/4:
			retval = m_gbsound->sound_r(8) | m_gbsound->sound_r(9)<<8;
			break;
		case 0x0070/4:
			retval = m_gbsound->sound_r(0xa) | m_gbsound->sound_r(0xb)<<16 | m_gbsound->sound_r(0xc)<<24;
			break;
		case 0x0074/4:
			retval = m_gbsound->sound_r(0xd) | m_gbsound->sound_r(0xe)<<8;
			break;
		case 0x0078/4:
			retval = m_gbsound->sound_r(0x10) | m_gbsound->sound_r(0x11)<<8;
			break;
		case 0x007c/4:
			retval = m_gbsound->sound_r(0x12) | m_gbsound->sound_r(0x13)<<8;
			break;
		case 0x0080/4:
			retval = m_gbsound->sound_r(0x14) | m_gbsound->sound_r(0x15)<<8;
			if( ACCESSING_BITS_16_31 )
			{
				retval |= SOUNDCNT_H << 16;
			}
			break;
		case 0x0084/4:
			retval = m_gbsound->sound_r(0x16);
			break;
		case 0x0090/4:
			retval = m_gbsound->wave_r(0) | m_gbsound->wave_r(1)<<8 | m_gbsound->wave_r(2)<<16 | m_gbsound->wave_r(3)<<24;
			break;
		case 0x0094/4:
			retval = m_gbsound->wave_r(4) | m_gbsound->wave_r(5)<<8 | m_gbsound->wave_r(6)<<16 | m_gbsound->wave_r(7)<<24;
			break;
		case 0x0098/4:
			retval = m_gbsound->wave_r(8) | m_gbsound->wave_r(9)<<8 | m_gbsound->wave_r(10)<<16 | m_gbsound->wave_r(11)<<24;
			break;
		case 0x009c/4:
			retval = m_gbsound->wave_r(12) | m_gbsound->wave_r(13)<<8 | m_gbsound->wave_r(14)<<16 | m_gbsound->wave_r(15)<<24;
			break;
		case 0x00a0/4:
		case 0x00a4/4:
			retval = 0; // (does this actually do anything on real h/w?)
			break;
		case 0x00b0/4:
		case 0x00b4/4:
			// read only
			break;
		case 0x00b8/4:
			if (ACCESSING_BITS_0_15)
			{
				// read only
			}
			if (ACCESSING_BITS_16_31)
			{
				retval |= DMACNT_H(0) << 16;
			}
			break;
		case 0x00bc/4:
		case 0x00c0/4:
			// read only
			break;
		case 0x00c4/4:
			if (ACCESSING_BITS_0_15)
			{
				// read only
			}
			if (ACCESSING_BITS_16_31)
			{
				retval |= DMACNT_H(1) << 16;
			}
			break;
		case 0x00c8/4:
		case 0x00cc/4:
			// read only
			break;
		case 0x00d0/4:
			if (ACCESSING_BITS_0_15)
			{
				// read only
			}
			if (ACCESSING_BITS_16_31)
			{
				retval |= DMACNT_H(2) << 16;
			}
			break;
		case 0x00d4/4:
		case 0x00d8/4:
			// read only
			break;
		case 0x00dc/4:
				// no idea why here, but it matches VBA better
				// note: this suspicious piece of code crashes "Buffy The Vampire Slayer" (08008DB4) and "The Ant Bully", so disable it for now
			if (ACCESSING_BITS_0_15)
			{
				// read only
			}
			if (ACCESSING_BITS_16_31)
			{
				retval |= DMACNT_H(3) << 16;
			}
			break;
		case 0x0100/4:
		case 0x0104/4:
		case 0x0108/4:
		case 0x010c/4:
			{
				uint32_t elapsed;
				double time, ticks;
				int timer = offset + 0x60/4 - 0x100/4;

//              printf("Read timer reg %x (PC=%x)\n", timer, m_maincpu->pc());

				// update times for
				if (m_timer_regs[timer] & 0x800000)
				{
					if (m_timer_regs[timer] & 0x00040000)
					{
						elapsed = m_timer_regs[timer] & 0xffff;
					}
					else
					{
						time = m_tmr_timer[timer]->elapsed().as_double();

						ticks = (double)(0x10000 - (m_timer_regs[timer] & 0xffff));

	//                  printf("time %f ticks %f 1/hz %f\n", time, ticks, 1.0 / m_timer_hz[timer]);

						time *= ticks;
						time /= (1.0 / m_timer_hz[timer]);

						elapsed = (uint32_t)time;
					}

//                  printf("elapsed = %x\n", elapsed);
				}
				else
				{
//                  printf("Reading inactive timer!\n");
					elapsed = 0;
				}

				retval = (m_timer_regs[timer] & 0xffff0000) | (elapsed & 0xffff);
			}
			break;
		case 0x0130/4:
			if( ACCESSING_BITS_0_15 )   // KEYINPUT
			{
				retval = m_io_inputs->read();
			}
			else if( ACCESSING_BITS_16_31 )
			{
				retval |= KEYCNT << 16;
			}
			break;
		case 0x0200/4:
			retval = IE | (IF << 16);
			break;
		default:
			if( ACCESSING_BITS_0_15 )
			{
				retval |= m_regs[offset] & 0x0000ffff;
			}
			if( ACCESSING_BITS_16_31 )
			{
				retval |= m_regs[offset] & 0xffff0000;
			}
			break;
	}

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

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

	return retval;
}

WRITE32_MEMBER(gba_state::gba_io_w)
{
	uint8_t soundcnt_x = SOUNDCNT_X;
	uint16_t siocnt = SIOCNT;
	uint16_t dmachcnt[4] = { DMACNT_H(0), DMACNT_H(1), DMACNT_H(2), DMACNT_H(3) };

	COMBINE_DATA(&m_regs[offset]);

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

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

	switch( offset + 0x60/4 )
	{
		case 0x0060/4:
			if( ACCESSING_BITS_0_7 )   // SOUNDCNTL
			{
				m_gbsound->sound_w(0, data);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->sound_w(1, data>>16);  // SOUND1CNT_H
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->sound_w(2, data>>24);
			}
			break;
		case 0x0064/4:
			if( ACCESSING_BITS_0_7 )   // SOUNDCNTL
			{
				m_gbsound->sound_w(3, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(4, data>>8);   // SOUND1CNT_H
			}
			break;
		case 0x0068/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(6, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(7, data>>8);
			}
			break;
		case 0x006c/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(8, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(9, data>>8);
			}
			break;
		case 0x0070/4:  //SND3CNTL and H
			if( ACCESSING_BITS_0_7 )   // SOUNDCNTL
			{
				m_gbsound->sound_w(0xa, data);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->sound_w(0xb, data>>16);    // SOUND1CNT_H
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->sound_w(0xc, data>>24);
			}
			break;
		case 0x0074/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0xd, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(0xe, data>>8);
			}
			break;
		case 0x0078/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0x10, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(0x11, data>>8);
			}
			break;
		case 0x007c/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0x12, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(0x13, data>>8);
			}
			break;
		case 0x0080/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0x14, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->sound_w(0x15, data>>8);
			}

			if (ACCESSING_BITS_16_31)
			{
				// DAC A reset?
				if (data & 0x08000000)
				{
					m_fifo_a_ptr = 17;
					m_fifo_a_in = 17;
					m_ldaca->write(0);
					m_rdaca->write(0);
				}

				// DAC B reset?
				if (data & 0x80000000)
				{
					m_fifo_b_ptr = 17;
					m_fifo_b_in = 17;
					m_ldacb->write(0);
					m_rdacb->write(0);
				}
			}
			break;
		case 0x0084/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->sound_w(0x16, data);
				if ((data & 0x80) && !(soundcnt_x & 0x80))
				{
					m_fifo_a_ptr = m_fifo_a_in = 17;
					m_fifo_b_ptr = m_fifo_b_in = 17;
					m_ldaca->write(0);
					m_rdaca->write(0);
					m_ldacb->write(0);
					m_rdacb->write(0);
				}
			}
			break;
		case 0x0090/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->wave_w(0, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->wave_w(1, data>>8);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->wave_w(2, data>>16);
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->wave_w(3, data>>24);
			}
			break;
		case 0x0094/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->wave_w(4, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->wave_w(5, data>>8);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->wave_w(6, data>>16);
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->wave_w(7, data>>24);
			}
			break;
		case 0x0098/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->wave_w(8, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->wave_w(9, data>>8);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->wave_w(0xa, data>>16);
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->wave_w(0xb, data>>24);
			}
			break;
		case 0x009c/4:
			if( ACCESSING_BITS_0_7 )
			{
				m_gbsound->wave_w(0xc, data);
			}
			if( ACCESSING_BITS_8_15 )
			{
				m_gbsound->wave_w(0xd, data>>8);
			}
			if( ACCESSING_BITS_16_23 )
			{
				m_gbsound->wave_w(0xe, data>>16);
			}
			if( ACCESSING_BITS_24_31 )
			{
				m_gbsound->wave_w(0xf, data>>24);
			}
			break;
		case 0x00a0/4:
			if (ACCESSING_BITS_0_7)
			{
				m_fifo_a_in %= 17;
				m_fifo_a[m_fifo_a_in++] = (data)&0xff;
			}
			if (ACCESSING_BITS_8_15)
			{
				m_fifo_a_in %= 17;
				m_fifo_a[m_fifo_a_in++] = (data>>8)&0xff;
			}
			if (ACCESSING_BITS_16_23)
			{
				m_fifo_a_in %= 17;
				m_fifo_a[m_fifo_a_in++] = (data>>16)&0xff;
			}
			if (ACCESSING_BITS_24_31)
			{
				m_fifo_a_in %= 17;
				m_fifo_a[m_fifo_a_in++] = (data>>24)&0xff;
			}
			break;
		case 0x00a4/4:
			if (ACCESSING_BITS_0_7)
			{
				m_fifo_b_in %= 17;
				m_fifo_b[m_fifo_b_in++] = (data)&0xff;
			}
			if (ACCESSING_BITS_8_15)
			{
				m_fifo_b_in %= 17;
				m_fifo_b[m_fifo_b_in++] = (data>>8)&0xff;
			}
			if (ACCESSING_BITS_16_23)
			{
				m_fifo_b_in %= 17;
				m_fifo_b[m_fifo_b_in++] = (data>>16)&0xff;
			}
			if (ACCESSING_BITS_24_31)
			{
				m_fifo_b_in %= 17;
				m_fifo_b[m_fifo_b_in++] = (data>>24)&0xff;
			}
			break;
		case 0x00b8/4:
		case 0x00c4/4:
		case 0x00d0/4:
		case 0x00dc/4:
			if( ACCESSING_BITS_16_31 )
			{
				int ch = (offset + 0x60/4 - 0xb0/4) / 3;

				int ctrl = data>>16;

				// Note: Metroid Fusion fails if we enforce the "rising edge" requirement... (who wrote this note?)

				// Note: Caesar's Palace Advance fails if we DO NOT enforce the "rising edge" requirement
				// (value @ 0x3003F9C is accidentally incremented because DMA completion interrupt is accidentally triggered @ 08002F2A)

				// retrigger/restart on a rising edge.
				// also reload internal regs
				if ((ctrl & 0x8000) && !(dmachcnt[ch] & 0x8000))
				{
					m_dma_src[ch] = DMASAD(ch);
					m_dma_dst[ch] = DMADAD(ch);
					m_dma_cnt[ch] = DMACNT_L(ch);

					// immediate start
					if ((ctrl & 0x3000) == 0)
					{
						dma_exec(ch);
						return;
					}
				}
			}
			break;
		case 0x0100/4:
		case 0x0104/4:
		case 0x0108/4:
		case 0x010c/4:
			{
				double rate, clocksel;
				uint32_t old_timer_regs;

				int timer = offset + 0x60/4 - 0x100/4;

				old_timer_regs = m_timer_regs[timer];

				m_timer_regs[timer] = (m_timer_regs[timer] & ~(mem_mask & 0xFFFF0000)) | (data & (mem_mask & 0xFFFF0000));

//              printf("%x to timer %d (mask %x PC %x)\n", data, timer, ~mem_mask, m_maincpu->pc());

				if (ACCESSING_BITS_0_15)
				{
					m_timer_reload[timer] = ((m_timer_reload[timer] & ~mem_mask) | (data & mem_mask)) & 0x0000FFFF;
					m_timer_recalc[timer] = 1;
				}

				// enabling this timer?
				if ((ACCESSING_BITS_16_31) && (data & 0x800000))
				{
					double final;

					if ((old_timer_regs & 0x00800000) == 0) // start bit 0 -> 1
					{
						m_timer_regs[timer] = (m_timer_regs[timer] & 0xFFFF0000) | (m_timer_reload[timer] & 0x0000FFFF);
					}

					rate = 0x10000 - (m_timer_regs[timer] & 0xffff);

					clocksel = timer_clks[(m_timer_regs[timer] >> 16) & 3].dvalue();

					final = clocksel / rate;

					m_timer_hz[timer] = final;

					m_timer_recalc[timer] = 0;

//                  printf("Enabling timer %d @ %f Hz\n", timer, final);

					// enable the timer
					if( !(data & 0x40000) ) // if we're not in Count-Up mode
					{
						attotime time = attotime::from_hz(final);
						m_tmr_timer[timer]->adjust(time, timer, time);
					}
				}
			}
			break;
		case 0x0128/4:
			if( ACCESSING_BITS_0_15 )
			{
				// normal mode ?
				if (!(RCNT & 0x8000) && !(data & 0x2000))
				{
					// start ?
					if (!(siocnt & 0x0080) && (data & 0x0080))
					{
						SIOCNT_RESET(0x0080);
						// request interrupt ?
						if (data & 0x4000)
						{
							request_irq(INT_SIO);
						}
					}
				}
			}
			break;
		case 0x0200/4:
			if( ACCESSING_BITS_0_15 )
			{
#if 0
				if (IE & IF)
				{
					request_irq(IF);
				}
#endif
			}
			if( ACCESSING_BITS_16_31 )
			{
				IF_RESET(( data & mem_mask ) >> 16);

				// if we still have interrupts, yank the IRQ line again
				if (IF)
				{
					m_irq_timer->adjust(m_maincpu->clocks_to_attotime(120));
				}
			}
			break;
		case 0x0208/4:
			if( ACCESSING_BITS_0_15 )
			{
				if (IF)
				{
					m_irq_timer->adjust(attotime::zero);
				}
			}
			break;
		case 0x0300/4:
			if( ACCESSING_BITS_8_15 )
			{
				// power down commanded, halt the CPU
				m_maincpu->spin_until_interrupt();
			}
			break;
	}
}

READ32_MEMBER(gba_state::gba_bios_r)
{
	uint32_t *rom = m_region_maincpu;
	if (m_bios_hack->read())
	{
		// partially patch out logo and checksum checks
		// (useful to run some protos + to test homebrew)
		if (ACCESSING_BITS_0_15 && (offset == 0x6fc/4))
			return 0;
	}

	if (m_maincpu->pc() >= 0x4000)
	{
		//printf("GBA protection: blocking PC=%x\n", m_maincpu->pc());
		return 0;
	}

	return rom[offset & 0x3fff];
}

READ32_MEMBER(gba_state::gba_10000000_r)
{
	auto &mspace = m_maincpu->space(AS_PROGRAM);
	uint32_t data;
	uint32_t pc = m_maincpu->state_int(ARM7_PC);
	if (pc >= 0x10000000)
	{
		return 0;
	}
	uint32_t cpsr = m_maincpu->state_int(ARM7_CPSR);
	if (T_IS_SET( cpsr))
	{
		data = mspace.read_dword(pc + 8);
	}
	else
	{
		uint16_t insn = mspace.read_word(pc + 4);
		data = (insn << 16) | (insn << 0);
	}
	logerror("%s: unmapped program memory read from %08X = %08X & %08X\n", machine().describe_context( ), 0x10000000 + (offset << 2), data, mem_mask);
	return data;
}

WRITE_LINE_MEMBER(gba_state::int_hblank_callback)
{
	request_irq(INT_HBL);
}

WRITE_LINE_MEMBER(gba_state::int_vblank_callback)
{
	request_irq(INT_VBL);
}

WRITE_LINE_MEMBER(gba_state::int_vcount_callback)
{
	request_irq(INT_VCNT);
}

WRITE_LINE_MEMBER(gba_state::dma_hblank_callback)
{
	for (int ch = 0; ch < 4; ch++)
	{
		int ctrl = DMACNT_H(ch);

		if ((ctrl & 0x8000) && ((ctrl & 0x3000) == 0x2000))
			dma_exec(ch);
	}
}

WRITE_LINE_MEMBER(gba_state::dma_vblank_callback)
{
	for (int ch = 0; ch < 4; ch++)
	{
		int ctrl = DMACNT_H(ch);

		if ((ctrl & 0x8000) && ((ctrl & 0x3000) == 0x1000))
			dma_exec(ch);
	}
}

void gba_state::gba_map(address_map &map)
{
	map.unmap_value_high(); // for "Fruit Mura no Doubutsu Tachi" and "Classic NES Series"
	map(0x00000000, 0x00003fff).rom().mirror(0x01ffc000).r(FUNC(gba_state::gba_bios_r));
	map(0x02000000, 0x0203ffff).ram().mirror(0xfc0000);
	map(0x03000000, 0x03007fff).ram().mirror(0xff8000);
	map(0x04000000, 0x0400005f).rw("lcd", FUNC(gba_lcd_device::video_r), FUNC(gba_lcd_device::video_w));
	map(0x04000060, 0x040003ff).rw(FUNC(gba_state::gba_io_r), FUNC(gba_state::gba_io_w));
	map(0x04000400, 0x04ffffff).noprw();                                         // Not used
	map(0x05000000, 0x050003ff).mirror(0x00fffc00).rw("lcd", FUNC(gba_lcd_device::gba_pram_r), FUNC(gba_lcd_device::gba_pram_w));  // Palette RAM
	map(0x06000000, 0x06017fff).mirror(0x00fe0000).rw("lcd", FUNC(gba_lcd_device::gba_vram_r), FUNC(gba_lcd_device::gba_vram_w));  // VRAM
	map(0x06018000, 0x0601ffff).mirror(0x00fe0000).rw("lcd", FUNC(gba_lcd_device::gba_vram_r), FUNC(gba_lcd_device::gba_vram_w));  // VRAM
	map(0x07000000, 0x070003ff).mirror(0x00fffc00).rw("lcd", FUNC(gba_lcd_device::gba_oam_r), FUNC(gba_lcd_device::gba_oam_w));    // OAM
	//map(0x08000000, 0x0cffffff)  // cart ROM + mirrors, mapped here at machine_start if a cart is present
	map(0x10000000, 0xffffffff).r(FUNC(gba_state::gba_10000000_r)); // for "Justice League Chronicles" (game bug)
}

static INPUT_PORTS_START( gbadv )
	PORT_START("INPUTS")
	PORT_BIT( 0xfc00, IP_ACTIVE_HIGH, IPT_UNUSED)
	PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("P1 L") PORT_PLAYER(1) // L
	PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("P1 R") PORT_PLAYER(1) // R
	PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_START ) PORT_PLAYER(1) // START
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_SELECT ) PORT_PLAYER(1)    // SELECT
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("B") PORT_PLAYER(1)    // B
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("A") PORT_PLAYER(1)    // A

	PORT_START("SKIP_CHECK")
	PORT_CONFNAME( 0x01, 0x00, "[HACK] Skip BIOS Logo check" )
	PORT_CONFSETTING(    0x00, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x01, DEF_STR( On ) )
INPUT_PORTS_END


void gba_state::machine_reset()
{
	memset(m_regs, 0, sizeof(m_regs));

	SOUNDBIAS_SET(0x0200);
	SIOMULTI0_SET(0xffff);
	SIOMULTI1_SET(0xffff);
	SIOMULTI2_SET(0xffff);
	SIOMULTI3_SET(0xffff);
	KEYCNT_SET(0x03ff);
	RCNT_SET(0x8000);
	JOYSTAT_SET(0x0002);

	m_dma_timer[0]->adjust(attotime::never);
	m_dma_timer[1]->adjust(attotime::never, 1);
	m_dma_timer[2]->adjust(attotime::never, 2);
	m_dma_timer[3]->adjust(attotime::never, 3);

	m_fifo_a_ptr = m_fifo_b_ptr = 17;   // indicate empty
	m_fifo_a_in = m_fifo_b_in = 17;

	// and clear the DACs
	m_ldaca->write(0);
	m_rdaca->write(0);
	m_ldacb->write(0);
	m_rdacb->write(0);
}

void gba_state::machine_start()
{
	/* and one for each DMA channel */
	m_dma_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_state::dma_complete),this));
	m_dma_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_state::dma_complete),this));
	m_dma_timer[2] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_state::dma_complete),this));
	m_dma_timer[3] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_state::dma_complete),this));
	m_dma_timer[0]->adjust(attotime::never);
	m_dma_timer[1]->adjust(attotime::never, 1);
	m_dma_timer[2]->adjust(attotime::never, 2);
	m_dma_timer[3]->adjust(attotime::never, 3);

	/* also one for each timer (heh) */
	m_tmr_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_state::timer_expire),this));
	m_tmr_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_state::timer_expire),this));
	m_tmr_timer[2] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_state::timer_expire),this));
	m_tmr_timer[3] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_state::timer_expire),this));
	m_tmr_timer[0]->adjust(attotime::never);
	m_tmr_timer[1]->adjust(attotime::never, 1);
	m_tmr_timer[2]->adjust(attotime::never, 2);
	m_tmr_timer[3]->adjust(attotime::never, 3);

	/* and an IRQ handling timer */
	m_irq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(gba_state::handle_irq),this));
	m_irq_timer->adjust(attotime::never);

	// install the cart ROM & SRAM into the address map, if present
	if (m_cart->exists())
	{
		m_maincpu->space(AS_PROGRAM).install_read_bank(0x08000000, 0x09ffffff, "rom1");
		m_maincpu->space(AS_PROGRAM).install_read_bank(0x0a000000, 0x0bffffff, "rom2");
		m_maincpu->space(AS_PROGRAM).install_read_bank(0x0c000000, 0x0cffffff, "rom3");

		std::string region_tag;
		memory_region *cart_rom = memregion(region_tag.assign(m_cart->tag()).append(GBASLOT_ROM_REGION_TAG));

		// install ROM accesses
		membank("rom1")->set_base(cart_rom->base());
		membank("rom2")->set_base(cart_rom->base());
		membank("rom3")->set_base(cart_rom->base());

		m_maincpu->space(AS_PROGRAM).install_read_handler(0x80000c4, 0x80000cb, read32_delegate(*m_cart, FUNC(gba_cart_slot_device::read_gpio)));
		m_maincpu->space(AS_PROGRAM).install_write_handler(0x80000c4, 0x80000cb, write32_delegate(*m_cart, FUNC(gba_cart_slot_device::write_gpio)));

		// add nvram to save state
		m_cart->save_nvram();

		// install the cart NVRAM handlers if necessary
		if (m_cart->get_type() == GBA_SRAM || m_cart->get_type() == GBA_DRILLDOZ || m_cart->get_type() == GBA_WARIOTWS)
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe000000, 0xe00ffff, read32_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe000000, 0xe00ffff, write32_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe010000, 0xe01ffff, read32_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe010000, 0xe01ffff, write32_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
		}
		if (m_cart->get_type() == GBA_EEPROM || m_cart->get_type() == GBA_EEPROM4 || m_cart->get_type() == GBA_EEPROM64 || m_cart->get_type() == GBA_BOKTAI)
		{
			// for games larger than 16MB the actual range is smaller but read_ram/write_ram handles that!
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xd000000, 0xdffffff, read32_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xd000000, 0xdffffff, write32_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
		}
		if (m_cart->get_type() == GBA_YOSHIUG)
		{
			// EEPROM
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xd000000, 0xdffffff, read32_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xd000000, 0xdffffff, write32_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
			// Tilt Sensor
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe008000, 0xe0085ff, read32_delegate(*m_cart, FUNC(gba_cart_slot_device::read_tilt)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe008000, 0xe0085ff, write32_delegate(*m_cart, FUNC(gba_cart_slot_device::write_tilt)));
		}
		// merge the two flash and mask accesses in read_ram?!?
		if (m_cart->get_type() == GBA_FLASH || m_cart->get_type() == GBA_FLASH512 || m_cart->get_type() == GBA_FLASH_RTC)
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe000000, 0xe00ffff, read32_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe000000, 0xe00ffff, write32_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
		}
		if (m_cart->get_type() == GBA_FLASH1M || m_cart->get_type() == GBA_FLASH1M_RTC)
		{
			m_maincpu->space(AS_PROGRAM).install_read_handler(0xe000000, 0xe01ffff, read32_delegate(*m_cart, FUNC(gba_cart_slot_device::read_ram)));
			m_maincpu->space(AS_PROGRAM).install_write_handler(0xe000000, 0xe01ffff, write32_delegate(*m_cart, FUNC(gba_cart_slot_device::write_ram)));
		}
		if (m_cart->get_type() == GBA_3DMATRIX)
		{
			m_maincpu->space(AS_PROGRAM).install_write_handler(0x08800000, 0x088001ff, write32_delegate(*m_cart, FUNC(gba_cart_slot_device::write_mapper)));
			memory_region *cart_romhlp = memregion(region_tag.assign(m_cart->tag()).append(GBAHELP_ROM_REGION_TAG));
			membank("rom1")->set_base(cart_romhlp->base());
		}

	}

	save_item(NAME(m_regs));
	save_item(NAME(m_dma_src));
	save_item(NAME(m_dma_dst));
	save_item(NAME(m_dma_cnt));
	save_item(NAME(m_timer_regs));
	save_item(NAME(m_timer_reload));
	save_item(NAME(m_timer_recalc));
	save_item(NAME(m_timer_hz));
	save_item(NAME(m_fifo_a_ptr));
	save_item(NAME(m_fifo_b_ptr));
	save_item(NAME(m_fifo_a_in));
	save_item(NAME(m_fifo_b_in));
	save_item(NAME(m_fifo_a));
	save_item(NAME(m_fifo_b));
}


static void gba_cart(device_slot_interface &device)
{
	device.option_add_internal("gba_rom",          GBA_ROM_STD);
	device.option_add_internal("gba_sram",         GBA_ROM_SRAM);
	device.option_add_internal("gba_drilldoz",     GBA_ROM_DRILLDOZ);       // Rumble output unemulated
	device.option_add_internal("gba_wariotws",     GBA_ROM_WARIOTWS);       // Rumble output unemulated
	device.option_add_internal("gba_eeprom",       GBA_ROM_EEPROM);
	device.option_add_internal("gba_eeprom_4k",    GBA_ROM_EEPROM);
	device.option_add_internal("gba_yoshiug",      GBA_ROM_YOSHIUG);
	device.option_add_internal("gba_eeprom_64k",   GBA_ROM_EEPROM64);
	device.option_add_internal("gba_boktai",       GBA_ROM_BOKTAI);
	device.option_add_internal("gba_flash",        GBA_ROM_FLASH);          // Panasonic
	device.option_add_internal("gba_flash_rtc",    GBA_ROM_FLASH_RTC);      // Panasonic
	device.option_add_internal("gba_flash_512",    GBA_ROM_FLASH);          // Panasonic
	device.option_add_internal("gba_flash_1m",     GBA_ROM_FLASH1M);        // Sanyo
	device.option_add_internal("gba_flash_1m_rtc", GBA_ROM_FLASH1M_RTC);    // Sanyo
	device.option_add_internal("gba_3dmatrix",     GBA_ROM_3DMATRIX);
}


void gba_state::gbadv(machine_config &config)
{
	ARM7(config, m_maincpu, XTAL(16'777'216));
	m_maincpu->set_addrmap(AS_PROGRAM, &gba_state::gba_map);

	gba_lcd_device &lcd(GBA_LCD(config, "lcd", 0));
	lcd.int_hblank_callback().set(FUNC(gba_state::int_hblank_callback));
	lcd.int_vblank_callback().set(FUNC(gba_state::int_vblank_callback));
	lcd.int_vcount_callback().set(FUNC(gba_state::int_vcount_callback));
	lcd.dma_hblank_callback().set(FUNC(gba_state::dma_hblank_callback));
	lcd.dma_vblank_callback().set(FUNC(gba_state::dma_vblank_callback));

	SPEAKER(config, "lspeaker").front_left();
	SPEAKER(config, "rspeaker").front_right();
	CGB04_APU(config, m_gbsound, XTAL(16'777'216)/4);
	m_gbsound->add_route(0, "lspeaker", 0.5);
	m_gbsound->add_route(1, "rspeaker", 0.5);

	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, m_ldaca, 0).add_route(ALL_OUTPUTS, "lspeaker", 0.5); // unknown DAC
	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, m_rdaca, 0).add_route(ALL_OUTPUTS, "rspeaker", 0.5); // unknown DAC
	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, m_ldacb, 0).add_route(ALL_OUTPUTS, "lspeaker", 0.5); // unknown DAC
	DAC_8BIT_R2R_TWOS_COMPLEMENT(config, m_rdacb, 0).add_route(ALL_OUTPUTS, "rspeaker", 0.5); // unknown DAC
	voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref", 0));
	vref.add_route(0, "ldaca", 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, "ldaca", -1.0, DAC_VREF_NEG_INPUT);
	vref.add_route(0, "rdaca", 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, "rdaca", -1.0, DAC_VREF_NEG_INPUT);
	vref.add_route(0, "ldacb", 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, "ldacb", -1.0, DAC_VREF_NEG_INPUT);
	vref.add_route(0, "rdacb", 1.0, DAC_VREF_POS_INPUT); vref.add_route(0, "rdacb", -1.0, DAC_VREF_NEG_INPUT);

	GBA_CART_SLOT(config, m_cart, gba_cart, nullptr);
	SOFTWARE_LIST(config, "cart_list").set_original("gba");
}


ROM_START( gba )
	ROM_REGION( 0x4000, "maincpu", 0 )
	ROM_LOAD( "gba.bin", 0x000000, 0x004000, CRC(81977335) SHA1(300c20df6731a33952ded8c436f7f186d25d3492) )
ROM_END


//   YEAR  NAME  PARENT  COMPAT  MACHINE  INPUT  CLASS      INIT        COMPANY     FULLNAME            FLAGS
CONS(2001, gba,  0,      0,      gbadv,   gbadv, gba_state, empty_init, "Nintendo", "Game Boy Advance", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS | MACHINE_IMPERFECT_SOUND)