summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/audio/segag80r.cpp
blob: 4acd7c2104a718ced5c855d31d094c8ee6000978 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11

                                








                                                                            
                
                              
 
                           
                      
                           
 
                    






                                       

                                                                           
 
                                                                                           
 
                                                                                                                           



                                                       



                                                   


                                                   

                                         
                                                                        
 
                               
                                                                    






                                                                                                                                                


                                                   
                                                

                                                   
                                                                                                                                                          
 
                                                                        
                                                                      


                                   
                                                  









                                                                                               
                                                                           
         


 
 



                                      



                                       






















































                                                                                                
                                                 

               






                               
               


  

                                                                


                                                                           
 
                            
 



                                                           
 

                                                                          

 





                                       
                                                    
 
                                               
                                

                                   
                                                                    

                                   
                                                                    

                                  
                                                                    

                                     
                                                                    

                                
                                                                    

                                   

                                                                                                    

                                

                                                                                                    


 
                                                       
 

                                                                
 
                                                                      

                                 
                              



                                                    
                                                       
                                                                         




                                                         
                                                       
                                                                                                                                                 



         
                                                    

          




                                                      
                                               
                                
 
                                                          

                                   
                                             

                         
                                                                    



                                                               

                                      



                                                                               
                                                                                     

                                   
                                    









                                       
 
 
                                                                 
 
                                                                        
                                                                               
                                   
                                                                                  
         
                                                                                                          
                                                   










                                       
                                                 

                   










                                
               


  

                                                                
                            
 




                                                           

 





                                       
                                                                 
 
                                                    
                                     




                                               

                                                                                                                    

                                                  
                                                                                    

                                                   
                                                                                    

                                                    
                                                                                     

                                               
                                                                                    

                                                 
                                                                                    



                                             
                                                                                    

                                                 
                                                                                    

                                             
                                                                                    

                                                       
                                                                                    

                                                    
                                                                                     











                                       









                                                                                 
                                                                                                       
 
                                                  

                    

                   
               

  
















                                                                                                                             
 
 





                                       
                                                   
 
                                                                     
                                              

                                                                                    

                                                                                               









                                       
                                                   
 
                                               
                                

                             
                                                                    

                             
                                                                    
 
                                                                    









                                       
                                               
 
                                 


 
                                                         

          
                                 
 


                                            
                                      
                                                                                       
                                                                                         


 

                                                          
 




                                                                                     
 
 









                                                                   


 
                                            

                           
                                                       


 
                                                


                                                                                                  
                                                      


 
                                                    
 
                                                
                                   
 

                                                                      
                                 
 








                                       

                                                                      
                                    






                                                                                          






                                                                                   



                                                            
 



                                                               
 

                                                                                                                            

                                                           

                                                  
 
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    Sega G-80 raster hardware

    Across these games, there's a mixture of discrete sound circuitry,
    speech boards, ADPCM samples, and a TMS3617 music chip.

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

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

#include "audio/segag80r.h"
#include "sound/dac.h"
#include "sound/volt_reg.h"

#include "speaker.h"

/*************************************
 *
 *  Constants
 *
 *************************************/

#define SEGA005_555_TIMER_FREQ      (1.44 / ((15000 + 2 * 4700) * 1.5e-6))
#define SEGA005_COUNTER_FREQ        (100000)    /* unknown, just a guess */

DEFINE_DEVICE_TYPE(SEGA005, sega005_sound_device, "sega005_sound", "Sega 005 Custom Sound")

sega005_sound_device::sega005_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SEGA005, tag, owner, clock)
	, device_sound_interface(mconfig, *this)
	, m_sega005_sound_timer(nullptr)
	, m_sega005_stream(nullptr)
{
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void sega005_sound_device::device_start()
{
	segag80r_state *state = machine().driver_data<segag80r_state>();

	/* create the stream */
	m_sega005_stream = stream_alloc(0, 1, SEGA005_COUNTER_FREQ);

	/* create a timer for the 555 */
	m_sega005_sound_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(sega005_sound_device::sega005_auto_timer), this));

	/* set the initial sound data */
	state->m_sound_data = 0x00;
	state->sega005_update_sound_data();
}

//-------------------------------------------------
//  sound_stream_update - handle a stream update
//-------------------------------------------------

void sega005_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
	segag80r_state *state = machine().driver_data<segag80r_state>();
	const uint8_t *sound_prom = state->memregion("proms")->base();
	int i;

	/* no implementation yet */
	for (i = 0; i < outputs[0].samples(); i++)
	{
		if (!(state->m_sound_state[1] & 0x10) && (++state->m_square_count & 0xff) == 0)
		{
			state->m_square_count = sound_prom[state->m_sound_data & 0x1f];

			/* hack - the RC should filter this out */
			if (state->m_square_count != 0xff)
				state->m_square_state += 2;
		}

		outputs[0].put(i, (state->m_square_state & 2) ? 1.0 : 0.0);
	}
}





/*************************************
 *
 *  005 sound hardware
 *
 *************************************/

/*
    005

    The Sound Board consists of the following:

    An 8255:
        Port A controls the sounds that use discrete circuitry
            A0 - Large Expl. Sound Trig
            A1 - Small Expl. Sound Trig
            A2 - Drop Sound Bomb Trig
            A3 - Shoot Sound Pistol Trig
            A4 - Missile Sound Trig
            A5 - Helicopter Sound Trig
            A6 - Whistle Sound Trig
            A7 - <unused>

      Port B controls the melody generator (described below)

      Port C is apparently unused


    Melody Generator:

        555 timer frequency = 1.44/((R1 + 2R2)*C)
        R1 = 15e3
        R2 = 4.7e3
        C=1.5e-6
        Frequency = 39.344 Hz

        Auto timer is enabled if port B & 0x20 == 1
        Auto timer is reset if 2716 value & 0x20 == 0

        Manual timer is enabled if port B & 0x20 == 0
        Manual timer is clocked if port B & 0x40 goes from 0 to 1

        Both auto and manual timers clock LS393 counter
        Counter is held to 0 if port B & 0x10 == 1

        Output of LS393 >> 1 selects low 7 bits of lookup in 2716.
        High 4 bits come from port B bits 0-3.

        Low 5 bits of output from 2716 look up value in 6331 PROM at U8 (32x8)

        8-bit output of 6331 at U8 is loaded into pair of LS161 counters whenever they overflow.
        LS161 counters are clocked somehow (not clear how)

        Carry output from LS161 counters (overflowing 8 bits) goes to the B
            input on the LS293 counter at U14.
        Rising edge of B input clocks bit 1 of counter (effectively adding 2).
        Output B (bit 1) is mixed with output D (bit 3) with different weights
            through a small RC circuit and fed into the 4391 input at U32.

        The 4391 output is the final output.
*/

static const char *const sega005_sample_names[] =
{
	"*005",
	"lexplode",     /* 0 */
	"sexplode",     /* 1 */
	"dropbomb",     /* 2 */
	"shoot",        /* 3 */
	"missile",      /* 4 */
	"helicopt",     /* 5 */
	"whistle",      /* 6 */
	nullptr
};


void segag80r_state::sega005_sound_board(machine_config &config)
{
	i8255_device &ppi(I8255A(config, "ppi8255"));
	ppi.out_pa_callback().set(FUNC(segag80r_state::sega005_sound_a_w));
	ppi.out_pb_callback().set(FUNC(segag80r_state::sega005_sound_b_w));

	/* sound hardware */

	SAMPLES(config, m_samples);
	m_samples->set_channels(7);
	m_samples->set_samples_names(sega005_sample_names);
	m_samples->add_route(ALL_OUTPUTS, "speaker", 0.25);

	SEGA005(config, "005", 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
}


/*************************************
 *
 *  005 sound triggers
 *
 *************************************/

void segag80r_state::sega005_sound_a_w(uint8_t data)
{
	uint8_t diff = data ^ m_sound_state[0];
	m_sound_state[0] = data;

	/* LARGE EXPL: channel 0 */
	if ((diff & 0x01) && !(data & 0x01)) m_samples->start(0, 0);

	/* SMALL EXPL: channel 1 */
	if ((diff & 0x02) && !(data & 0x02)) m_samples->start(1, 1);

	/* DROP BOMB: channel 2 */
	if ((diff & 0x04) && !(data & 0x04)) m_samples->start(2, 2);

	/* SHOOT PISTOL: channel 3 */
	if ((diff & 0x08) && !(data & 0x08)) m_samples->start(3, 3);

	/* MISSILE: channel 4 */
	if ((diff & 0x10) && !(data & 0x10)) m_samples->start(4, 4);

	/* HELICOPTER: channel 5 */
	if ((diff & 0x20) && !(data & 0x20) && !m_samples->playing(5)) m_samples->start(5, 5, true);
	if ((diff & 0x20) &&  (data & 0x20)) m_samples->stop(5);

	/* WHISTLE: channel 6 */
	if ((diff & 0x40) && !(data & 0x40) && !m_samples->playing(6)) m_samples->start(6, 6, true);
	if ((diff & 0x40) &&  (data & 0x40)) m_samples->stop(6);
}


inline void segag80r_state::sega005_update_sound_data()
{
	uint8_t newval = memregion("005")->base()[m_sound_addr];
	uint8_t diff = newval ^ m_sound_data;

	//osd_printf_debug("  [%03X] = %02X\n", m_sound_addr, newval);

	/* latch the new value */
	m_sound_data = newval;

	/* if bit 5 goes high, we reset the timer */
	if ((diff & 0x20) && !(newval & 0x20))
	{
		//osd_printf_debug("Stopping timer\n");
		m_005snd->m_sega005_sound_timer->adjust(attotime::never);
	}

	/* if bit 5 goes low, we start the timer again */
	if ((diff & 0x20) && (newval & 0x20))
	{
		//osd_printf_debug("Starting timer\n");
		m_005snd->m_sega005_sound_timer->adjust(attotime::from_hz(SEGA005_555_TIMER_FREQ), 0, attotime::from_hz(SEGA005_555_TIMER_FREQ));
	}
}


void segag80r_state::sega005_sound_b_w(uint8_t data)
{
	/*
	       D6: manual timer clock (0->1)
	       D5: 0 = manual timer, 1 = auto timer
	       D4: 1 = hold/reset address counter to 0
	    D3-D0: upper 4 bits of ROM address
	*/
	uint8_t diff = data ^ m_sound_state[1];
	m_sound_state[1] = data;

	//osd_printf_debug("sound[%d] = %02X\n", 1, data);

	/* force a stream update */
	m_005snd->m_sega005_stream->update();

	/* ROM address */
	m_sound_addr = ((data & 0x0f) << 7) | (m_sound_addr & 0x7f);

	/* reset both sound address and square wave counters */
	if (data & 0x10)
	{
		m_sound_addr &= 0x780;
		m_square_state = 0;
	}

	/* manual clock */
	if ((diff & 0x40) && (data & 0x40) && !(data & 0x20) && !(data & 0x10))
		m_sound_addr = (m_sound_addr & 0x780) | ((m_sound_addr + 1) & 0x07f);

	/* update the sound data */
	sega005_update_sound_data();
}



/*************************************
 *
 *  005 custom sound generation
 *
 *************************************/



TIMER_CALLBACK_MEMBER( sega005_sound_device::sega005_auto_timer )
{
	segag80r_state *state = machine().driver_data<segag80r_state>();
	/* force an update then clock the sound address if not held in reset */
	m_sega005_stream->update();
	if ((state->m_sound_state[1] & 0x20) && !(state->m_sound_state[1] & 0x10))
	{
		state->m_sound_addr = (state->m_sound_addr & 0x780) | ((state->m_sound_addr + 1) & 0x07f);
		state->sega005_update_sound_data();
	}
}



/*************************************
 *
 *  Space Odyssey sound hardware
 *
 *************************************/

static const char *const spaceod_sample_names[] =
{
	"*spaceod",
	"fire",         /* 0 */
	"bomb",         /* 1 */
	"eexplode",     /* 2 */
	"pexplode",     /* 3 */
	"warp",         /* 4 */
	"birth",        /* 5 */
	"scoreup",      /* 6 */
	"ssound",       /* 7 */
	"accel",        /* 8 */
	"damaged",      /* 9 */
	"erocket",      /* 10 */
	nullptr
};


void segag80r_state::spaceod_sound_board(machine_config &config)
{
	/* sound hardware */

	SAMPLES(config, m_samples);
	m_samples->set_channels(11);
	m_samples->set_samples_names(spaceod_sample_names);
	m_samples->add_route(ALL_OUTPUTS, "speaker", 0.25);
}


/*************************************
 *
 *  Space Odyssey sound triggers
 *
 *************************************/

void segag80r_state::spaceod_sound_w(offs_t offset, uint8_t data)
{
	uint8_t diff = data ^ m_sound_state[offset];
	m_sound_state[offset] = data;

	switch (offset)
	{
		case 0:
			/* BACK G: channel 0 */
			if ((diff & 0x01) && !(data & 0x01) && !m_samples->playing(0)) m_samples->start(0, 7, true);
			if ((diff & 0x01) &&  (data & 0x01)) m_samples->stop(0);

			/* SHORT EXP: channel 1 */
			if ((diff & 0x04) && !(data & 0x04)) m_samples->start(1, 2);

			/* ACCELERATE: channel 2 */
			if ((diff & 0x10) && !(data & 0x10)) m_samples->start(2, 8);

			/* BATTLE STAR: channel 3 */
			if ((diff & 0x20) && !(data & 0x20)) m_samples->start(3, 10);

			/* D BOMB: channel 4 */
			if ((diff & 0x40) && !(data & 0x40)) m_samples->start(4, 1);

			/* LONG EXP: channel 5 */
			if ((diff & 0x80) && !(data & 0x80)) m_samples->start(5, 3);
			break;

		case 1:
			/* SHOT: channel 6 */
			if ((diff & 0x01) && !(data & 0x01)) m_samples->start(6, 0);

			/* BONUS UP: channel 7 */
			if ((diff & 0x02) && !(data & 0x02)) m_samples->start(7, 6);

			/* WARP: channel 8 */
			if ((diff & 0x08) && !(data & 0x08)) m_samples->start(8, 4);

			/* APPEARANCE UFO: channel 9 */
			if ((diff & 0x40) && !(data & 0x40)) m_samples->start(9, 5);

			/* BLACK HOLE: channel 10 */
			if ((diff & 0x80) && !(data & 0x80)) m_samples->start(10, 9);
			break;
	}
}



/*************************************
 *
 *  Monster Bash sound hardware
 *
 *************************************/

/*
    Monster Bash

    The Sound Board is a fairly complex mixture of different components.
    An 8255A-5 controls the interface to/from the sound board.
    Port A connects to a TMS3617 (basic music synthesizer) circuit.
    Port B connects to two sounds generated by discrete circuitry.
    Port C connects to a NEC7751 (8048 CPU derivative) to control four "samples".
*/

DEFINE_DEVICE_TYPE(MONSTERB_SOUND, monsterb_sound_device, "monsterb_sound", "Monster Bash Sound Board")

static const char *const monsterb_sample_names[] =
{
	"*monsterb",
	"zap",
	"jumpdown",
	nullptr
};

monsterb_sound_device::monsterb_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, MONSTERB_SOUND, tag, owner, clock)
	, m_audiocpu(*this, "audiocpu")
	, m_audiocpu_region(*this, "n7751")
	, m_music(*this, "music")
	, m_samples(*this, "samples")
	, m_i8243(*this, "i8243")
{
}

void monsterb_sound_device::device_start()
{
	save_item(NAME(m_n7751_command));
	save_item(NAME(m_n7751_busy));
	save_item(NAME(m_sound_state));
	save_item(NAME(m_sound_addr));
}


/*************************************
 *
 *  TMS3617 access
 *
 *************************************/

void monsterb_sound_device::sound_a_w(uint8_t data)
{
	/* Lower four data lines get decoded into 13 control lines */
	m_music->tms36xx_note_w(0, data & 15);

	/* Top four data lines address an 82S123 ROM that enables/disables voices */
	int enable_val = machine().root_device().memregion("prom")->base()[(data & 0xF0) >> 4];
	m_music->tms3617_enable_w(enable_val >> 2);
}



/*************************************
 *
 *  Discrete sound triggers
 *
 *************************************/

void monsterb_sound_device::sound_b_w(uint8_t data)
{
	uint8_t diff = data ^ m_sound_state[1];
	m_sound_state[1] = data;

	/* SHOT: channel 0 */
	if ((diff & 0x01) && !(data & 0x01)) m_samples->start(0, 0);

	/* DIVE: channel 1 */
	if ((diff & 0x02) && !(data & 0x02)) m_samples->start(1, 1);

	/* TODO: D7 on Port B might affect TMS3617 output (mute?) */
}



/*************************************
 *
 *  N7751 connections
 *
 *************************************/

uint8_t monsterb_sound_device::n7751_status_r()
{
	return m_n7751_busy << 4;
}


void monsterb_sound_device::n7751_command_w(uint8_t data)
{
	/*
	    Z80 7751 control port

	    D0-D2 = connected to 7751 port C
	    D3    = /INT line
	*/
	m_n7751_command = data & 0x07;
	m_audiocpu->set_input_line(0, ((data & 0x08) == 0) ? ASSERT_LINE : CLEAR_LINE);
	machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(100));
}


template<int Shift>
void monsterb_sound_device::n7751_rom_addr_w(uint8_t data)
{
	// P4 - address lines 0-3
	// P5 - address lines 4-7
	// P5 - address lines 8-11
	m_sound_addr = (m_sound_addr & ~(0x00f << Shift)) | ((data & 0x0f) << Shift);
}


void monsterb_sound_device::n7751_rom_select_w(uint8_t data)
{
	// P7 - ROM selects
	m_sound_addr &= 0xfff;

	int numroms = m_audiocpu_region->bytes() / 0x1000;
	if (!(data & 0x01) && numroms >= 1) m_sound_addr |= 0x0000;
	if (!(data & 0x02) && numroms >= 2) m_sound_addr |= 0x1000;
	if (!(data & 0x04) && numroms >= 3) m_sound_addr |= 0x2000;
	if (!(data & 0x08) && numroms >= 4) m_sound_addr |= 0x3000;
}


uint8_t monsterb_sound_device::n7751_rom_r()
{
	/* read from BUS */
	return m_audiocpu_region->base()[m_sound_addr];
}


uint8_t monsterb_sound_device::n7751_command_r()
{
	/* read from P2 - 8255's PC0-2 connects to 7751's S0-2 (P24-P26 on an 8048) */
	/* bit 0x80 is an alternate way to control the sample on/off; doesn't appear to be used */
	return 0x80 | ((m_n7751_command & 0x07) << 4);
}


void monsterb_sound_device::n7751_p2_w(uint8_t data)
{
	/* write to P2; low 4 bits go to 8243 */
	m_i8243->p2_w(data & 0x0f);

	/* output of bit $80 indicates we are ready (1) or busy (0) */
	/* no other outputs are used */
	m_n7751_busy = data >> 7;
}



/*************************************
 *
 *  Machine driver
 *
 *************************************/

void monsterb_sound_device::device_add_mconfig(machine_config &config)
{
	/* basic machine hardware */
	N7751(config, m_audiocpu, 6000000);
	m_audiocpu->t1_in_cb().set_constant(0); // labelled as "TEST", connected to ground
	m_audiocpu->p2_in_cb().set(FUNC(monsterb_sound_device::n7751_command_r));
	m_audiocpu->bus_in_cb().set(FUNC(monsterb_sound_device::n7751_rom_r));
	m_audiocpu->p1_out_cb().set("dac", FUNC(dac_byte_interface::data_w));
	m_audiocpu->p2_out_cb().set(FUNC(monsterb_sound_device::n7751_p2_w));
	m_audiocpu->prog_out_cb().set(m_i8243, FUNC(i8243_device::prog_w));

	I8243(config, m_i8243);
	m_i8243->p4_out_cb().set(FUNC(monsterb_sound_device::n7751_rom_addr_w<0>));
	m_i8243->p5_out_cb().set(FUNC(monsterb_sound_device::n7751_rom_addr_w<4>));
	m_i8243->p6_out_cb().set(FUNC(monsterb_sound_device::n7751_rom_addr_w<8>));
	m_i8243->p7_out_cb().set(FUNC(monsterb_sound_device::n7751_rom_select_w));

	SAMPLES(config, m_samples);
	m_samples->set_channels(2);
	m_samples->set_samples_names(monsterb_sample_names);
	m_samples->add_route(ALL_OUTPUTS, "speaker", 0.25);

	TMS36XX(config, m_music, 247);
	m_music->set_subtype(tms36xx_device::subtype::TMS3617);
	m_music->set_decays(0.5, 0.5, 0.5, 0.5, 0.5, 0.5);
	m_music->add_route(ALL_OUTPUTS, "speaker", 0.5);

	DAC_8BIT_R2R(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.5); // 50K (R91-97)/100K (R98-106) ladder network
	voltage_regulator_device &vref(VOLTAGE_REGULATOR(config, "vref", 0));
	vref.add_route(0, "dac", 1.0, DAC_VREF_POS_INPUT);
	vref.add_route(0, "dac", -1.0, DAC_VREF_NEG_INPUT);

	SPEAKER(config, "speaker").front_center();
}