summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/sg1000_exp/fm_unit.cpp
blob: c43b143248c6d0ea4f10d7a45a74b5812531db00 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// license:BSD-3-Clause
// copyright-holders:Enik Land
/**********************************************************************

    Sega FM Sound Unit emulation


Release data from the Sega Retro project:

  Year: 1987    Country/region: JP    Model code: FM-70

Notes:

The FM Unit add-on was released for the Mark III, so compatible games were
released only for that console and the Japanese SMS, that has the FM chip
built-in. The games play FM sound only when detect the FM hardware. The normal
PSG sound is replaced, except when sampled voices, not supported by the FM
chip, are played. Some games released for western SMS versions also contain FM
code, that is played on a Mark III / SMSJ when connected through a cartridge
adapter.

The IO address map (read/write ports) is based on Charles MacDonald's analysis
of Enri's schematics of the FM unit hardware.

Any software that access controllers through IO ports $C0/$C1 instead $DC/$DD
(the game Fushigi no Oshiro Pit Pot is a known example) has control problems
when the FM Sound Unit is attached (real hardware behavior).

TODO:

- Confirm the assumption that PSG and FM sound are not mixed by the FM unit
and there is only FM sound when the unit is enabled. Two evidences that were
observed: every time a game with FM sound plays sampled voices through PSG,
the game disables/enables the FM chip before/after playing the PSG sound, and
a user reported, on the topic of the SMSPower Forums where the PSG/FM mixing
control of the SMSJ was discovered, that the hack that adds FM sound to Sonic
SMS version is not playing PSG sound on his Mark III with the FM unit.

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

#include "emu.h"
#include "fm_unit.h"



//**************************************************************************
//  DEVICE DEFINITIONS
//**************************************************************************

DEFINE_DEVICE_TYPE(SEGA_FM_UNIT, sega_fm_unit_device, "sega_fm_unit", "SG-1000 FM Sound Unit")


MACHINE_CONFIG_START(sega_fm_unit_device::device_add_mconfig)
	MCFG_SOUND_ADD("ym2413", YM2413, XTAL(10'738'635)/3)
	// if this output gain is changed, the gain set when unmute the output need
	// to be changed too, probably along the gain set for SMSJ/SMSKRFM drivers.
	MCFG_SOUND_ROUTE(ALL_OUTPUTS, ":mono", 1.00)
MACHINE_CONFIG_END


//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  sega_fm_unit_device - constructor
//-------------------------------------------------

sega_fm_unit_device::sega_fm_unit_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, SEGA_FM_UNIT, tag, owner, clock),
	device_sg1000_expansion_slot_interface(mconfig, *this),
	m_ym(*this, "ym2413"),
	m_psg(*this, ":segapsg"),
	m_audio_control(0)
{
}


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

void sega_fm_unit_device::device_start()
{
	/* register for state saving */
	save_item(NAME(m_audio_control));
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void sega_fm_unit_device::device_reset()
{
	set_audio_control(0x00);
}


//-------------------------------------------------
//  peripheral_r - fm unit read
//-------------------------------------------------

READ8_MEMBER(sega_fm_unit_device::peripheral_r)
{
	// the value previously written to the control port is returned on all
	// active read offsets.
	if (offset <= 3)
	{
		return m_audio_control & 0x01;
	}
	// will not be called for other offsets.
	return 0xff;
}

//-------------------------------------------------
//  peripheral_w - fm unit write
//-------------------------------------------------

WRITE8_MEMBER(sega_fm_unit_device::peripheral_w)
{
	switch (offset)
	{
		case 0: // register port
			m_ym->write(space, 0, data & 0x3f);
			break;
		case 1: // data port
			m_ym->write(space, 1, data);
			break;
		case 2: // control port
		case 3: // mirror
			set_audio_control(data);
			break;
		default:
			break;
	}
}


bool sega_fm_unit_device::is_readable(uint8_t offset)
{
	return (offset <= 3) ? true : false;
}


bool sega_fm_unit_device::is_writeable(uint8_t offset)
{
	return (offset <= 3) ? true : false;
}


void sega_fm_unit_device::set_audio_control(uint8_t data)
{
	m_audio_control = data & 0x01;
	if (m_audio_control == 0x01)
	{
		m_ym->set_output_gain(ALL_OUTPUTS, 1.0);
		// assume the PSG output is muted when FM is active.
		// Out Run need this. Needs confirmation (see TODO).
		if (m_psg.found())
			m_psg->set_output_gain(ALL_OUTPUTS, 0.0);
	}
	else
	{
		m_ym->set_output_gain(ALL_OUTPUTS, 0.0);
		if (m_psg.found())
			m_psg->set_output_gain(ALL_OUTPUTS, 1.0);
	}
}