summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/machine/saturn.cpp
blob: 1de768692e63e0cb6cb4a1791cea9bce884b6b3c (plain) (tree)
1
2
3
4
5
6
7
                    
                                                                                                   

                                                                                       

                             
                                                      








                                                                                        


                                                    











                                                                                         


                                         



                                                                              

                                                                                    
                                                                                       
                     


                                                                                       
                
                            
                       
                              






                                         



                                                                                        
                                                                                     





                                                              
                                                       






                                                                                     
                                         
 
                                                                                 


                                                                                                            
                                   

 
                                         
 
                                                                                 

                                                                                                            
                                     




















                                                                                                    
                                                
 
                                                                                 







                                                                                                                    
                                   

 
                                                
 
                                                                                 




                                                                                                                    
                                     


 
                                                       






                                                                      
                                                                  






                                        
 
                                                    
 
                                         



                                                
                                                        
 
                                           
                      



                       
                        
         
                                                                   
                                                         
         

            
                                                                   


         

































                                                                                                                           

                                
                                       


                                               
                                      
 

                                    

                                                                                                                                                                              


                                                                          
                                      

         






                                                                              



                                                                                                                                                        
                                     
         
 
                                                       










                                                                  
                                                                                
                                                                          
                                                                                



























































                                                                                    
                          



                                                                               
             
 













                                                                                      
                            













                                                                                       
                                                                             
                                                                                        
                       


















                                                                                      
                                                                       
 

                                              




                                      
 
// license:LGPL-2.1+
// copyright-holders:David Haywood, Angelo Salese, Olivier Galibert, Mariusz Wojcieszek, R. Belmont
/**************************************************************************************

    Sega Saturn (c) 1994 Sega

    @todo List of things that needs to be implemented:
    - There's definitely an ack mechanism in SCU irqs. This is almost surely done via
      the ISM register (i.e. going 0->1 to the given bit acks it).
    - There might be a delay to exactly when SCU irqs happens. This is due to the basic
      fact that SCU runs at 14-ish MHz, so it needs some time before actually firing the
      irq.
    - Vblank-Out actually happens at the last screen line, not at 0.
    - VDP2 V counter has a similar roll-back as MD correspondent register:
      vpos line 0 == 0x1ff (Vblank-Out happens here)
      vpos line 1 == 0
      ...
      vpos line 241 == 0xf0 (Vblank-In happens here)
      vpos line 246 == 0xf5
      vpos line 247 == 0x1ef (rolls back here)
      vpos line 263 == 0x1ff again
    - HBlank bit seems to follow a normal logic instead.
    - Timer 0 doesn't work if the TENB bit isn't enabled (documentation is a bit fussy
      over this).
    - Timer 0 fires at the HBlank-In signal, not before.
    - VDP2 H Counter actually counts x2 in non Hi-Res mode.
    - Timer 1 is definitely annoying. Starts from H-Blank signal and starts counting from
      that position.
      H counter value 0x282 (642) -> timer 1 fires at setting 1
      H counter value 0x284 (644) -> 2
      H counter value 0x2a0 (672) -> 0x10
      H counter value 0x2c0 (704) -> 0x20
      H counter value 0x300 (768) -> 0x40
      H counter value 0x340 (832) -> 0x60
      H counter value 0x352 (850) -> 0x69
      H counter value 0x000 (0)   -> 0x6a, V counter goes +1 here (max range?)
      H counter value 0x02c (44)  -> 0x80
      H counter value 0x0ec (236) -> 0xe0
      H counter value 0x12c (300) -> 0x100
    - Timer 1 seems to count backwards compared to Timer 0 from setting 0x6b onward.
    - Yabause claims that if VDP2 DISP bit isn't enabled then vblank irqs (hblank too?)
      doesn't happen.

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

#include "emu.h"
#include "includes/saturn.h"
#include "cpu/sh/sh2.h"
#include "cpu/scudsp/scudsp.h"

/* TODO: do this in a verboselog style */
#define LOG_CDB  0
#define LOG_SCU  1
#define LOG_IRQ  0
#define LOG_IOGA 0



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

void saturn_state::saturn_soundram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	//machine().scheduler().synchronize(); // force resync

	COMBINE_DATA(&m_sound_ram[offset]);
}

uint16_t saturn_state::saturn_soundram_r(offs_t offset)
{
	//machine().scheduler().synchronize(); // force resync

	return m_sound_ram[offset];
}

/* communication,SLAVE CPU acquires data from the MASTER CPU and triggers an irq.  */
void saturn_state::minit_w(uint32_t data)
{
	//logerror("%s MINIT write = %08x\n", machine().describe_context(),data);
	machine().scheduler().boost_interleave(m_minit_boost_timeslice, attotime::from_usec(m_minit_boost));
	machine().scheduler().trigger(1000);
	machine().scheduler().synchronize(); // force resync
	m_slave->pulse_frt_input();
}

void saturn_state::sinit_w(uint32_t data)
{
	//logerror("%s SINIT write = %08x\n", machine().describe_context(),data);
	machine().scheduler().boost_interleave(m_sinit_boost_timeslice, attotime::from_usec(m_sinit_boost));
	machine().scheduler().synchronize(); // force resync
	m_maincpu->pulse_frt_input();
}

/*
TODO:
Some games seems to not like either MAME's interleave system and/or SH-2 DRC, causing an hard crash.
Reported games are:
Blast Wind (before FMV)
Choro Q Park (car selection)
060311E4: MOV.L R14,@-SP ;R14 = 0x60ffba0 / R15 = 0x60ffba0
060311E6: MOV SP,R14 ;R14 = 0x60ffba0 / R15 = 0x60ffb9c / [0x60ffb9c] <- 0x60ffba0
060311E8: MOV.L @SP+,R14 ;R14 = 0x60ffb9c / R15 = 0x60ffb9c / [0x60ffb9c] -> R14
060311EA: RTS ;R14 = 0x60ffba0 / R15 = 0x60ffba0
060311EC: NOP
06031734: MULS.W R9, R8 ;R14 = 0x60ffba0 / R15 = 0x60ffba0 / EA = 0x60311E4
on DRC this becomes:
R14 0x6031b78 (cause of the crash later on), R15 = 0x60ffba4 and EA = 0

Shinrei Jusatsushi Taromaru (options menu)

*/

void saturn_state::saturn_minit_w(uint32_t data)
{
	//logerror("%s MINIT write = %08x\n", machine().describe_context(),data);
	if(m_fake_comms->read() & 1)
		machine().scheduler().synchronize(); // force resync
	else
	{
		machine().scheduler().boost_interleave(m_minit_boost_timeslice, attotime::from_usec(m_minit_boost));
		machine().scheduler().trigger(1000);
	}

	m_slave->pulse_frt_input();
}

void saturn_state::saturn_sinit_w(uint32_t data)
{
	//logerror("%s SINIT write = %08x\n", machine().describe_context(),data);
	if(m_fake_comms->read() & 1)
		machine().scheduler().synchronize(); // force resync
	else
		machine().scheduler().boost_interleave(m_sinit_boost_timeslice, attotime::from_usec(m_sinit_boost));

	m_maincpu->pulse_frt_input();
}


uint8_t saturn_state::saturn_backupram_r(offs_t offset)
{
	if(!(offset & 1))
		return 0; // yes, it makes sure the "holes" are there.

	return m_backupram[offset >> 1] & 0xff;
}

void saturn_state::saturn_backupram_w(offs_t offset, uint8_t data)
{
	if(!(offset & 1))
		return;

	m_backupram[offset >> 1] = data;
}


WRITE_LINE_MEMBER(saturn_state::m68k_reset_callback)
{
	m_smpc_hle->m68k_reset_trigger();

	printf("m68k RESET opcode triggered\n");
}

void saturn_state::scsp_irq(offs_t offset, uint8_t data)
{
	// don't bother the 68k if it's off
	if (!m_en_68k)
	{
		return;
	}

	if (offset != 0)
	{
		if (data == ASSERT_LINE) m_scsp_last_line = offset;
		m_audiocpu->set_input_line(offset, data);
	}
	else
	{
		m_audiocpu->set_input_line(m_scsp_last_line, data);
	}
}


/*
(Preliminary) explanation about this:
VBLANK-OUT is used at the start of the vblank period.It also sets the timer zero
variable to 0.
If the Timer Compare register is zero too,the Timer 0 irq is triggered.

HBLANK-IN is used at the end of each scanline except when in VBLANK-IN/OUT periods.

The timer 0 is also incremented by one at each HBLANK and checked with the value
of the Timer Compare register;if equal,the timer 0 irq is triggered here too.
Notice that the timer 0 compare register can be more than the VBLANK maximum range,in
this case the timer 0 irq is simply never triggered.This is a known Sega Saturn/ST-V "bug".

VBLANK-IN is used at the end of the vblank period.

SCU register[36] is the timer zero compare register.
SCU register[40] is for IRQ masking.

TODO:
- VDP1 timing and CEF emulation isn't accurate at all.
*/


TIMER_DEVICE_CALLBACK_MEMBER(saturn_state::saturn_scanline)
{
	int scanline = param;
	int y_step,vblank_line;

	vblank_line = get_vblank_start_position();
	y_step = get_ystep_count();

	//popmessage("%08x %d T0 %d T1 %d %08x",m_scu.ism ^ 0xffffffff,max_y,m_scu_regs[36],m_scu_regs[37],m_scu_regs[38]);

	if(scanline == 0*y_step)
	{
		m_scu->vblank_out_w(1);
	}
	else if(scanline == vblank_line*y_step)
	{
		m_scu->vblank_in_w(1);

		// flip odd bit here
		m_vdp2.odd ^= 1;
		/* TODO: when Automatic Draw actually happens? Night Striker S is very fussy on this, and it looks like that VDP1 starts at more or less vblank-in time ... */
		video_update_vdp1();
	}
	else if((scanline % y_step) == 0 && scanline < vblank_line*y_step)
	{
		m_scu->hblank_in_w(1);
	}

	if(scanline == (vblank_line+1)*y_step)
	{
		/* docs mentions that VBE happens one line after vblank-in. */
		if(STV_VDP1_VBE)
			m_vdp1.framebuffer_clear_on_next_frame = 1;
	}

	// TODO: temporary for Batman Forever, presumably anonymous timer not behaving well.
	//       VDP1 timing needs some HW work anyway so I'm currently firing VDP1 after 8 scanlines for now, will de-anon the timers in a later stage.
	if(scanline == (vblank_line+8)*y_step)
	{
		m_scu->vdp1_end_w(1);
	}

	m_scu->check_scanline_timers(scanline, y_step);
}

TIMER_DEVICE_CALLBACK_MEMBER(saturn_state::saturn_slave_scanline )
{
	int scanline = param;
	int y_step,vblank_line;

	vblank_line = get_vblank_start_position();
	y_step = get_ystep_count();

	if(scanline == vblank_line*y_step)
		m_slave->set_input_line_and_vector(0x6, HOLD_LINE, 0x43); // SH2
	else if((scanline % y_step) == 0 && scanline < vblank_line*y_step)
		m_slave->set_input_line_and_vector(0x2, HOLD_LINE, 0x41); // SH2
}

static const gfx_layout tiles8x8x4_layout =
{
	8,8,
	0x100000/(32*8/8),
	4,
	{ 0, 1, 2, 3 },
	{ 0, 4, 8, 12, 16, 20, 24, 28 },
	{ 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32 },
	32*8
};

static const gfx_layout tiles16x16x4_layout =
{
	16,16,
	0x100000/(32*32/8),
	4,
	{ 0, 1, 2, 3 },
	{ 0, 4, 8, 12, 16, 20, 24, 28,
		32*8+0, 32*8+4, 32*8+8, 32*8+12, 32*8+16, 32*8+20, 32*8+24, 32*8+28,

		},
	{ 0*32, 1*32, 2*32, 3*32, 4*32, 5*32, 6*32, 7*32,
		32*16, 32*17,32*18, 32*19,32*20,32*21,32*22,32*23

		},
	32*32
};

static const gfx_layout tiles8x8x8_layout =
{
	8,8,
	0x100000/(32*8/8),
	8,
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0, 8, 16, 24, 32, 40, 48, 56 },
	{ 0*64, 1*64, 2*64, 3*64, 4*64, 5*64, 6*64, 7*64 },
	32*8    /* really 64*8, but granularity is 32 bytes */
};

static const gfx_layout tiles16x16x8_layout =
{
	16,16,
	0x100000/(64*16/8),
	8,
	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0, 8, 16, 24, 32, 40, 48, 56,
	64*8+0, 65*8, 66*8, 67*8, 68*8, 69*8, 70*8, 71*8

	},
	{ 0*64, 1*64, 2*64, 3*64, 4*64, 5*64, 6*64, 7*64,
	64*16, 64*17, 64*18, 64*19, 64*20, 64*21, 64*22, 64*23
	},
	64*16   /* really 128*16, but granularity is 32 bytes */
};




GFXDECODE_START( gfx_stv )
	GFXDECODE_ENTRY( nullptr, 0, tiles8x8x4_layout,   0x00, (0x80*(2+1))  )
	GFXDECODE_ENTRY( nullptr, 0, tiles16x16x4_layout, 0x00, (0x80*(2+1))  )
	GFXDECODE_ENTRY( nullptr, 0, tiles8x8x8_layout,   0x00, (0x08*(2+1))  )
	GFXDECODE_ENTRY( nullptr, 0, tiles16x16x8_layout, 0x00, (0x08*(2+1))  )
GFXDECODE_END


WRITE_LINE_MEMBER( saturn_state::master_sh2_reset_w )
{
	m_maincpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);
}

WRITE_LINE_MEMBER(saturn_state::master_sh2_nmi_w)
{
	m_maincpu->set_input_line(INPUT_LINE_NMI, state ? ASSERT_LINE : CLEAR_LINE);
}

WRITE_LINE_MEMBER( saturn_state::slave_sh2_reset_w )
{
	m_slave->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);
//  m_smpc.slave_on = state;
}

WRITE_LINE_MEMBER( saturn_state::sound_68k_reset_w )
{
	m_audiocpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);
	m_en_68k = state ^ 1;
}

// TODO: edge triggered?
WRITE_LINE_MEMBER( saturn_state::system_reset_w )
{
	if(!state)
		return;

	// TODO: actually send a device reset signal to the connected devices
	/*Only backup ram and SMPC ram are retained after that this command is issued.*/
	m_scu->reset();
	memset(m_sound_ram,0x00,0x080000);
	memset(m_workram_h,0x00,0x100000);
	memset(m_workram_l,0x00,0x100000);
	memset(m_vdp2_regs.get(),0x00,0x040000);
	memset(m_vdp2_vram.get(),0x00,0x100000);
	memset(m_vdp2_cram.get(),0x00,0x080000);
	memset(m_vdp1_vram.get(),0x00,0x100000);
	//A-Bus
}

WRITE_LINE_MEMBER(saturn_state::system_halt_w)
{
	m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
	m_slave->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
	m_audiocpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
}

WRITE_LINE_MEMBER(saturn_state::dot_select_w)
{
	const XTAL &xtal = state ? MASTER_CLOCK_320 : MASTER_CLOCK_352;

	m_maincpu->set_unscaled_clock(xtal/2);
	m_slave->set_unscaled_clock(xtal/2);

	m_vdp2.dotsel = state ^ 1;
	stv_vdp2_dynamic_res_change();
}