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