1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4 
5     mconfig.cpp
6 
7     Machine configuration macros and functions.
8 
9 ***************************************************************************/
10 
11 #include "emu.h"
12 #include "emuopts.h"
13 #include "screen.h"
14 
15 #include <cctype>
16 
17 #include <cstring>
18 #include <numeric>
19 
20 
21 //**************************************************************************
22 //  MACHINE CONFIGURATIONS
23 //**************************************************************************
24 
25 class machine_config::current_device_stack
26 {
27 public:
28 	current_device_stack(current_device_stack const &) = delete;
current_device_stack(machine_config & host)29 	current_device_stack(machine_config &host) : m_host(host), m_device(host.m_current_device) { m_host.m_current_device = nullptr; }
~current_device_stack()30 	~current_device_stack() { assert(!m_host.m_current_device); m_host.m_current_device = m_device; }
31 private:
32 	machine_config &m_host;
33 	device_t *const m_device;
34 };
35 
36 
37 //-------------------------------------------------
38 //  machine_config - constructor
39 //-------------------------------------------------
40 
machine_config(const game_driver & gamedrv,emu_options & options)41 machine_config::machine_config(const game_driver &gamedrv, emu_options &options)
42 	: m_gamedrv(gamedrv)
43 	, m_options(options)
44 	, m_root_device()
45 	, m_default_layouts([] (char const *a, char const *b) { return 0 > std::strcmp(a, b); })
46 	, m_current_device(nullptr)
__anon69dc804b0202(char const *a, char const *b) 47 	, m_maximum_quantums([] (char const *a, char const *b) { return 0 > std::strcmp(a, b); })
48 	, m_perfect_quantum_device(nullptr, "")
49 {
50 	// add the root device
51 	device_add("root", gamedrv.type, 0);
52 
53 	// intialize slot devices - make sure that any required devices have been allocated
54 	for (device_slot_interface &slot : slot_interface_iterator(root_device()))
55 	{
56 		device_t &owner = slot.device();
57 		const char *slot_option_name = owner.tag() + 1;
58 
59 		// figure out which device goes into this slot
60 		bool const has_option = options.has_slot_option(slot_option_name);
61 		const char *selval;
62 		bool is_default;
63 		if (!has_option)
64 		{
65 			// The only time we should be getting here is when emuopts.cpp is invoking
66 			// us to evaluate slot/image options, and the internal state of emuopts.cpp has
67 			// not caught up yet
68 			selval = slot.default_option();
69 			is_default = true;
70 		}
71 		else
72 		{
73 			const slot_option &opt = options.slot_option(slot_option_name);
74 			selval = opt.value().c_str();
75 			is_default = !opt.specified();
76 		}
77 
78 		if (selval && *selval)
79 		{
80 			// TODO: make this thing more self-contained so it can apply itself - shouldn't need to know all this here
81 			device_slot_interface::slot_option const *option = slot.option(selval);
82 			if (option && (is_default || option->selectable()))
83 			{
84 				// create the device
85 				token const tok(begin_configuration(owner));
86 				device_t *const new_dev = device_add(option->name(), option->devtype(), option->clock());
87 				slot.set_card_device(new_dev);
88 
89 				char const *const default_bios = option->default_bios();
90 				if (default_bios != nullptr)
91 					new_dev->set_default_bios_tag(default_bios);
92 
93 				auto additions = option->machine_config();
94 				if (additions)
95 					additions(new_dev);
96 
97 				input_device_default const *const input_device_defaults = option->input_device_defaults();
98 				if (input_device_defaults)
99 					new_dev->set_input_default(input_device_defaults);
100 			}
101 			else
102 				throw emu_fatalerror("Unknown slot option '%s' in slot '%s'", selval, owner.tag()+1);
103 		}
104 	}
105 
106 	// then notify all devices that their configuration is complete
107 	for (device_t &device : device_iterator(root_device()))
108 		if (!device.configured())
109 			device.config_complete();
110 }
111 
112 
113 //-------------------------------------------------
114 //  ~machine_config - destructor
115 //-------------------------------------------------
116 
~machine_config()117 machine_config::~machine_config()
118 {
119 }
120 
121 
122 //-------------------------------------------------
123 //  maximum_quantum - get smallest configured
124 //  maximum quantum
125 //-------------------------------------------------
126 
maximum_quantum(attotime const & default_quantum) const127 attotime machine_config::maximum_quantum(attotime const &default_quantum) const
128 {
129 	return std::accumulate(
130 			m_maximum_quantums.begin(),
131 			m_maximum_quantums.end(),
132 			default_quantum,
133 			[] (attotime const &lhs, maximum_quantum_map::value_type const &rhs) { return (std::min)(lhs, rhs.second); });
134 }
135 
136 //-------------------------------------------------
137 //  perfect_quantum_device - get device configured
138 //  for perfect quantum if any
139 //-------------------------------------------------
140 
perfect_quantum_device() const141 device_execute_interface *machine_config::perfect_quantum_device() const
142 {
143 	if (!m_perfect_quantum_device.first)
144 		return nullptr;
145 
146 	device_t *const found(m_perfect_quantum_device.first->subdevice(m_perfect_quantum_device.second.c_str()));
147 	if (!found)
148 	{
149 		throw emu_fatalerror(
150 				"Device %s relative to %s specified for perfect interleave is not present!\n",
151 				m_perfect_quantum_device.second,
152 				m_perfect_quantum_device.first->tag());
153 	}
154 
155 	device_execute_interface *result;
156 	if (!found->interface(result))
157 	{
158 		throw emu_fatalerror("Device %s (%s) specified for perfect interleave does not implement device_execute_interface!\n",
159 				found->tag(),
160 				found->shortname());
161 	}
162 
163 	return result;
164 }
165 
166 
167 //-------------------------------------------------
168 //  set_default_layout - set layout for current
169 //  device
170 //-------------------------------------------------
171 
set_default_layout(internal_layout const & layout)172 void machine_config::set_default_layout(internal_layout const &layout)
173 {
174 	std::pair<default_layout_map::iterator, bool> const ins(m_default_layouts.emplace(current_device().tag(), &layout));
175 	if (!ins.second)
176 		ins.first->second = &layout;
177 }
178 
179 
180 //-------------------------------------------------
181 //  set_maximum_quantum - set maximum scheduling
182 //  quantum for current device device
183 //-------------------------------------------------
184 
set_maximum_quantum(attotime const & quantum)185 void machine_config::set_maximum_quantum(attotime const &quantum)
186 {
187 	std::pair<maximum_quantum_map::iterator, bool> const ins(m_maximum_quantums.emplace(current_device().tag(), quantum));
188 	if (!ins.second)
189 		ins.first->second = quantum;
190 }
191 
192 
193 //-------------------------------------------------
194 //  device_add - configuration helper to add a
195 //  new device
196 //-------------------------------------------------
197 
device_add(const char * tag,device_type type,u32 clock)198 device_t *machine_config::device_add(const char *tag, device_type type, u32 clock)
199 {
200 	std::pair<const char *, device_t *> const owner(resolve_owner(tag));
201 	return &add_device(type.create(*this, owner.first, owner.second, clock), owner.second);
202 }
203 
204 
205 //-------------------------------------------------
206 //  device_replace - configuration helper to
207 //  replace one device with a new device
208 //-------------------------------------------------
209 
device_replace(const char * tag,device_type type,u32 clock)210 device_t *machine_config::device_replace(const char *tag, device_type type, u32 clock)
211 {
212 	std::tuple<const char *, device_t *, device_t *> const existing(prepare_replace(tag));
213 	std::unique_ptr<device_t> device(type.create(*this, std::get<0>(existing), std::get<1>(existing), clock));
214 	return &replace_device(std::move(device), *std::get<1>(existing), std::get<2>(existing));
215 }
216 
217 
218 //-------------------------------------------------
219 //  device_remove - configuration helper to
220 //  remove a device
221 //-------------------------------------------------
222 
device_remove(const char * tag)223 device_t *machine_config::device_remove(const char *tag)
224 {
225 	// find the original device by relative tag (must exist)
226 	assert(m_current_device);
227 	device_t *const device = m_current_device->subdevice(tag);
228 	if (device == nullptr)
229 	{
230 		osd_printf_warning("Warning: attempting to remove non-existent device '%s'\n", tag);
231 	}
232 	else
233 	{
234 		// make sure we have the old device's actual owner
235 		device_t *const owner = device->owner();
236 		assert(owner);
237 
238 		// remove references to the old device
239 		remove_references(*device);
240 
241 		// let the device's owner do the work
242 		owner->subdevices().m_list.remove(*device);
243 	}
244 	return nullptr;
245 }
246 
247 
248 //-------------------------------------------------
249 //  resolve_owner - get the actual owner and base
250 //  tag given tag relative to current context
251 //-------------------------------------------------
252 
resolve_owner(const char * tag) const253 std::pair<const char *, device_t *> machine_config::resolve_owner(const char *tag) const
254 {
255 	assert(bool(m_current_device) == bool(m_root_device));
256 	char const *const orig_tag = tag;
257 	device_t *owner(m_current_device);
258 
259 	// if the device path is absolute, start from the root
260 	if (tag[0] == ':')
261 	{
262 		tag++;
263 		owner = m_root_device.get();
264 	}
265 
266 	// go down the path until we're done with it
267 	std::string part;
268 	while (strchr(tag, ':'))
269 	{
270 		const char *next = strchr(tag, ':');
271 		assert(next != tag);
272 		part.assign(tag, next - tag);
273 		owner = owner->subdevices().find(part);
274 		if (!owner)
275 			throw emu_fatalerror("Could not find %s when looking up path for device %s\n", part.c_str(), orig_tag);
276 		tag = next+1;
277 	}
278 	assert(tag[0] != '\0');
279 
280 	return std::make_pair(tag, owner);
281 }
282 
283 
284 //-------------------------------------------------
285 //  prepare_replace - ensure owner is present and
286 //  existing device is removed if necessary
287 //-------------------------------------------------
288 
prepare_replace(const char * tag)289 std::tuple<const char *, device_t *, device_t *> machine_config::prepare_replace(const char *tag)
290 {
291 	// make sure we have the old device's actual owner
292 	std::pair<const char *, device_t *> const owner(resolve_owner(tag));
293 	assert(owner.second);
294 
295 	// remove references to the old device
296 	device_t *const old_device(owner.second->subdevice(owner.first));
297 	if (old_device)
298 		remove_references(*old_device);
299 	else
300 		osd_printf_warning("Warning: attempting to replace non-existent device '%s'\n", tag);
301 
302 	return std::make_tuple(owner.first, owner.second, old_device);
303 }
304 
305 
306 //-------------------------------------------------
307 //  add_device - add a new device at the correct
308 //  point in the hierarchy
309 //-------------------------------------------------
310 
add_device(std::unique_ptr<device_t> && device,device_t * owner)311 device_t &machine_config::add_device(std::unique_ptr<device_t> &&device, device_t *owner)
312 {
313 	current_device_stack const context(*this);
314 	if (owner)
315 	{
316 		// allocate the new device and append it to the owner's list
317 		device_t &result(owner->subdevices().m_list.append(*device.release()));
318 		result.add_machine_configuration(*this);
319 		return result;
320 	}
321 	else
322 	{
323 		// allocate the root device directly
324 		assert(!m_root_device);
325 		m_root_device = std::move(device);
326 		m_root_device->add_machine_configuration(*this);
327 		return *m_root_device;
328 	}
329 }
330 
331 
332 //-------------------------------------------------
333 //  replace_device - substitute the new device for
334 //  the old one in the owner's list
335 //-------------------------------------------------
336 
replace_device(std::unique_ptr<device_t> && device,device_t & owner,device_t * existing)337 device_t &machine_config::replace_device(std::unique_ptr<device_t> &&device, device_t &owner, device_t *existing)
338 {
339 	current_device_stack const context(*this);
340 	device_t &result(existing
341 			? owner.subdevices().m_list.replace_and_remove(*device.release(), *existing)
342 			: owner.subdevices().m_list.append(*device.release()));
343 	result.add_machine_configuration(*this);
344 	return result;
345 }
346 
347 
348 //-------------------------------------------------
349 //  remove_references - globally remove references
350 //  to a device about to be removed from the tree
351 //-------------------------------------------------
352 
remove_references(device_t & device)353 void machine_config::remove_references(device_t &device)
354 {
355 	// sanity check
356 	if (m_perfect_quantum_device.first == &device)
357 	{
358 		throw emu_fatalerror(
359 				"Removing %s device %s would make the perfect quantum device target invalid\n",
360 				device.shortname(),
361 				device.tag());
362 	}
363 
364 	// remove default layouts and maximum quantum settings for subdevices
365 	char const *const tag(device.tag());
366 	std::size_t const taglen(std::strlen(tag));
367 	for (auto it = m_default_layouts.lower_bound(tag); (m_default_layouts.end() != it) && !std::strncmp(tag, it->first, taglen); )
368 	{
369 		if (!it->first[taglen] || (':' == it->first[taglen]))
370 			it = m_default_layouts.erase(it);
371 		else
372 			++it;
373 	}
374 	for (auto it = m_maximum_quantums.lower_bound(tag); (m_maximum_quantums.end() != it) && !std::strncmp(tag, it->first, taglen); )
375 	{
376 		if (!it->first[taglen] || (':' == it->first[taglen]))
377 			it = m_maximum_quantums.erase(it);
378 		else
379 			++it;
380 	}
381 
382 	// iterate over all devices and remove any references
383 	for (device_t &scan : device_iterator(root_device()))
384 		scan.subdevices().m_tagmap.clear();
385 }
386 
387 
388 //-------------------------------------------------
389 //  set_perfect_quantum - set device to base
390 //  scheduling interval on
391 //-------------------------------------------------
392 
set_perfect_quantum(device_t & device,std::string tag)393 void machine_config::set_perfect_quantum(device_t &device, std::string tag)
394 {
395 	if (!m_current_device)
396 	{
397 		throw emu_fatalerror(
398 				"Perfect quantum device can only be set during configuration (set to %s relative to %s)\n",
399 				tag,
400 				device.tag());
401 	}
402 
403 	if (m_current_device != m_root_device.get())
404 	{
405 		throw emu_fatalerror(
406 				"Perfect quantum device can only be set by the root device (set to %s relative to %s while configuring %s device %s)\n",
407 				tag,
408 				device.tag(),
409 				m_current_device->shortname(),
410 				m_current_device->tag());
411 	}
412 
413 	m_perfect_quantum_device.first = &device;
414 	m_perfect_quantum_device.second = std::move(tag);
415 }
416