1 // license:GPL-2.0+
2 // copyright-holders:Couriersud
3 /***************************************************************************
4
5 netlist.c
6
7 Discrete netlist implementation.
8
9 ****************************************************************************/
10
11 #include "emu.h"
12 #include "netlist.h"
13
14 #include "netlist/nl_base.h"
15 #include "netlist/nl_setup.h"
16 #include "netlist/nl_factory.h"
17 #include "netlist/nl_parser.h"
18 #include "netlist/nl_interface.h"
19
20 #include "netlist/plib/palloc.h"
21 #include "netlist/plib/pmempool.h"
22 #include "netlist/plib/pdynlib.h"
23 #include "netlist/plib/pstonum.h"
24
25 #include "debugger.h"
26 #include "romload.h"
27 #include "emuopts.h"
28
29 #include <cmath>
30 #include <memory>
31 #include <string>
32 #include <utility>
33
34 #define LOG_GENERAL (1U << 0)
35 #define LOG_DEV_CALLS (1U << 1)
36 #define LOG_DEBUG (1U << 2)
37 #define LOG_TIMING (1U << 3)
38
39 //#define LOG_MASK (LOG_GENERAL | LOG_DEV_CALLS | LOG_DEBUG)
40 //#define LOG_MASK (LOG_TIMING)
41 #define LOG_MASK (0)
42
43 #define LOGDEVCALLS(...) LOGMASKED(LOG_DEV_CALLS, __VA_ARGS__)
44 #define LOGDEBUG(...) LOGMASKED(LOG_DEBUG, __VA_ARGS__)
45 #define LOGTIMING(...) LOGMASKED(LOG_TIMING, __VA_ARGS__)
46
47 #define LOG_OUTPUT_FUNC printf
48
49 #define LOGMASKED(mask, ...) do { if (LOG_MASK & (mask)) (LOG_OUTPUT_FUNC)(__VA_ARGS__); } while (false)
50
51 DEFINE_DEVICE_TYPE(NETLIST_CORE, netlist_mame_device, "netlist_core", "Netlist Core Device")
52 DEFINE_DEVICE_TYPE(NETLIST_CPU, netlist_mame_cpu_device, "netlist_cpu", "Netlist CPU Device")
53 DEFINE_DEVICE_TYPE(NETLIST_SOUND, netlist_mame_sound_device, "netlist_sound", "Netlist Sound Device")
54
55 /* subdevices */
56
57 DEFINE_DEVICE_TYPE(NETLIST_ANALOG_INPUT, netlist_mame_analog_input_device, "nl_analog_in", "Netlist Analog Input")
58 DEFINE_DEVICE_TYPE(NETLIST_INT_INPUT, netlist_mame_int_input_device, "nl_int_out", "Netlist Integer Output")
59 DEFINE_DEVICE_TYPE(NETLIST_RAM_POINTER, netlist_mame_ram_pointer_device, "nl_ram_ptr", "Netlist RAM Pointer")
60 DEFINE_DEVICE_TYPE(NETLIST_LOGIC_INPUT, netlist_mame_logic_input_device, "nl_logic_in", "Netlist Logic Input")
61 DEFINE_DEVICE_TYPE(NETLIST_STREAM_INPUT, netlist_mame_stream_input_device, "nl_stream_in", "Netlist Stream Input")
62
63 DEFINE_DEVICE_TYPE(NETLIST_LOGIC_OUTPUT, netlist_mame_logic_output_device, "nl_logic_out", "Netlist Logic Output")
64 DEFINE_DEVICE_TYPE(NETLIST_ANALOG_OUTPUT, netlist_mame_analog_output_device, "nl_analog_out", "Netlist Analog Output")
65 DEFINE_DEVICE_TYPE(NETLIST_STREAM_OUTPUT, netlist_mame_stream_output_device, "nl_stream_out", "Netlist Stream Output")
66
67 // ----------------------------------------------------------------------------------------
68 // Special netlist extension devices ....
69 // ----------------------------------------------------------------------------------------
70
71 extern const plib::dynlib_static_sym nl_static_solver_syms[];
72
nltime_from_attotime(attotime t)73 static netlist::netlist_time_ext nltime_from_attotime(attotime t)
74 {
75 netlist::netlist_time_ext nlmtime = netlist::netlist_time_ext::from_sec(t.seconds());
76 nlmtime += netlist::netlist_time_ext::from_raw(t.attoseconds() / (ATTOSECONDS_PER_SECOND / netlist::netlist_time_ext::resolution()));
77 return nlmtime;
78 }
79
80 #if 0
attotime_from_nltime(netlist::netlist_time_ext t)81 static attotime attotime_from_nltime(netlist::netlist_time_ext t)
82 {
83 return attotime(t.as_raw() / netlist::netlist_time_ext::resolution(),
84 (t.as_raw() % netlist::netlist_time_ext::resolution()) * (ATTOSECONDS_PER_SECOND / netlist::netlist_time_ext::resolution()));
85 }
86 #endif
87
88 class netlist_mame_device::netlist_mame_t : public netlist::netlist_state_t
89 {
90 public:
91
netlist_mame_t(netlist_mame_device & parent,const pstring & name)92 netlist_mame_t(netlist_mame_device &parent, const pstring &name)
93 : netlist::netlist_state_t(name, plib::plog_delegate(&netlist_mame_t::logger, this))
94 , m_parent(parent)
95 {
96 }
97
machine()98 running_machine &machine() { return m_parent.machine(); }
parent() const99 netlist_mame_device &parent() const { return m_parent; }
100
101 private:
logger(plib::plog_level l,const pstring & ls)102 void logger(plib::plog_level l, const pstring &ls)
103 {
104 switch (l)
105 {
106 case plib::plog_level::DEBUG:
107 m_parent.logerror("netlist DEBUG: %s\n", ls.c_str());
108 break;
109 case plib::plog_level::VERBOSE:
110 m_parent.logerror("netlist VERBOSE: %s\n", ls.c_str());
111 break;
112 case plib::plog_level::INFO:
113 m_parent.logerror("netlist INFO: %s\n", ls.c_str());
114 break;
115 case plib::plog_level::WARNING:
116 m_parent.logerror("netlist WARNING: %s\n", ls.c_str());
117 break;
118 case plib::plog_level::ERROR:
119 m_parent.logerror("netlist ERROR: %s\n", ls.c_str());
120 break;
121 case plib::plog_level::FATAL:
122 m_parent.logerror("netlist FATAL: %s\n", ls.c_str());
123 break;
124 }
125 }
126
127 netlist_mame_device &m_parent;
128 };
129
130
131 namespace {
132
133
134
135 // ----------------------------------------------------------------------------------------
136 // Extensions to interface netlist with MAME code ....
137 // ----------------------------------------------------------------------------------------
138
139 /*! Specific exception if memregion is not available.
140 * The exception is thrown if the memregions are not available.
141 * This may be the case in device_validity_check and needs
142 * to be ignored.
143 */
144 class memregion_not_set : public netlist::nl_exception
145 {
146 public:
147 /*! Constructor.
148 * Allows a descriptive text to be assed to the exception
149 */
memregion_not_set(const pstring & text)150 explicit memregion_not_set(const pstring &text //!< text to be passed
151 )
152 : netlist::nl_exception(text) { }
153
154 template<typename... Args>
memregion_not_set(const pstring & fmt,Args &&...args)155 explicit memregion_not_set(const pstring &fmt //!< format to be used
156 , Args&&... args //!< arguments to be passed
157 )
158 : netlist::nl_exception(plib::pfmt(fmt)(std::forward<Args>(args)...)) { }
159 };
160
161 class netlist_source_memregion_t : public netlist::source_netlist_t
162 {
163 public:
164
netlist_source_memregion_t(device_t & dev,pstring name)165 netlist_source_memregion_t(device_t &dev, pstring name)
166 : netlist::source_netlist_t(), m_dev(dev), m_name(name)
167 {
168 }
169
170 virtual plib::istream_uptr stream(const pstring &name) override;
171 private:
172 device_t &m_dev;
173 pstring m_name;
174 };
175
176 class netlist_data_memregions_t : public netlist::source_data_t
177 {
178 public:
179 netlist_data_memregions_t(const device_t &dev);
180
181 virtual plib::istream_uptr stream(const pstring &name) override;
182
183 private:
184 const device_t &m_dev;
185 };
186
187
188 // ----------------------------------------------------------------------------------------
189 // memregion source support
190 // ----------------------------------------------------------------------------------------
191
stream(const pstring & name)192 plib::istream_uptr netlist_source_memregion_t::stream(const pstring &name)
193 {
194 if (m_dev.has_running_machine())
195 {
196 memory_region *mem = m_dev.memregion(putf8string(m_name).c_str());
197 plib::istream_uptr ret(std::make_unique<std::istringstream>(putf8string(reinterpret_cast<char *>(mem->base()), mem->bytes())), name);
198 ret->imbue(std::locale::classic());
199 return ret;
200 }
201 else
202 throw memregion_not_set("memregion unavailable for {1} in source {2}", name, m_name);
203 //return plib::unique_ptr<plib::pimemstream>(nullptr);
204 }
205
netlist_data_memregions_t(const device_t & dev)206 netlist_data_memregions_t::netlist_data_memregions_t(const device_t &dev)
207 : netlist::source_data_t(), m_dev(dev)
208 {
209 }
210
rom_exists(device_t & root,pstring name)211 static bool rom_exists(device_t &root, pstring name)
212 {
213 // iterate, starting with the driver's ROMs and continuing with device ROMs
214 for (device_t &device : device_iterator(root))
215 {
216 // scan the ROM entries for this device
217 for (tiny_rom_entry const *romp = device.rom_region(); romp && !ROMENTRY_ISEND(romp); ++romp)
218 {
219 if (ROMENTRY_ISREGION(romp)) // if this is a region, check for rom
220 {
221 auto basetag(pstring(romp->name));
222 if (name == pstring(":") + basetag)
223 return true;
224 }
225 }
226 }
227 return false;
228 }
229
stream(const pstring & name)230 plib::istream_uptr netlist_data_memregions_t::stream(const pstring &name)
231 {
232 //memory_region *mem = static_cast<netlist_mame_device::netlist_mame_t &>(setup().setup().exec()).parent().memregion(name.c_str());
233 if (m_dev.has_running_machine())
234 {
235 memory_region *mem = m_dev.memregion(putf8string(name).c_str());
236 if (mem != nullptr)
237 {
238 plib::istream_uptr ret(std::make_unique<std::istringstream>(std::string(reinterpret_cast<char *>(mem->base()), mem->bytes()), std::ios_base::binary), name);
239 ret->imbue(std::locale::classic());
240 return ret;
241 }
242 else
243 return plib::istream_uptr();
244 }
245 else
246 {
247 /* validation */
248 if (rom_exists(m_dev.mconfig().root_device(), pstring(m_dev.tag()) + ":" + name))
249 {
250 // Create an empty stream.
251 plib::istream_uptr ret(std::make_unique<std::istringstream>(std::ios_base::binary), name);
252 ret->imbue(std::locale::classic());
253 return ret;
254 }
255 else
256 return plib::istream_uptr();
257 }
258 }
259
260 } // anonymous namespace
261
262 // ----------------------------------------------------------------------------------------
263 // sound_in
264 // ----------------------------------------------------------------------------------------
265
266 using sound_in_type = netlist::interface::NETLIB_NAME(buffered_param_setter)<netlist_mame_sound_input_buffer>;
267
268 class NETLIB_NAME(sound_in) : public sound_in_type
269 {
270 public:
271 using base_type = sound_in_type;
272 using base_type::base_type;
273 };
274
275 netlist::setup_t &netlist_mame_device::setup()
276 {
277 if (!m_netlist)
278 throw device_missing_dependencies();
279 return m_netlist->setup();
280 }
281
282 void netlist_mame_device::register_memregion_source(netlist::nlparse_t &parser, device_t &dev, const char *name)
283 {
284 parser.register_source<netlist_source_memregion_t>(dev, pstring(name));
285 }
286
287 void netlist_mame_analog_input_device::write(const double val)
288 {
289 m_value_for_device_timer = val * m_mult + m_offset;
290 if (m_value_for_device_timer != (*m_param)())
291 {
292 synchronize(0, 0, &m_value_for_device_timer);
293 }
294 }
295
296 void netlist_mame_analog_input_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
297 {
298 update_to_current_time();
299 #if NETLIST_CREATE_CSV
300 nl_owner().log_add(m_param_name, *((double *) ptr), true);
301 #endif
302 m_param->set(*((double *) ptr));
303 }
304
305 void netlist_mame_int_input_device::write(const uint32_t val)
306 {
307 const uint32_t v = (val >> m_shift) & m_mask;
308 if (v != (*m_param)())
309 {
310 LOGDEBUG("write %s\n", this->tag());
311 synchronize(0, v);
312 }
313 }
314
315 void netlist_mame_logic_input_device::write(const uint32_t val)
316 {
317 const uint32_t v = (val >> m_shift) & 1;
318 if (v != (*m_param)())
319 {
320 LOGDEBUG("write %s\n", this->tag());
321 synchronize(0, v);
322 }
323 }
324
325 void netlist_mame_int_input_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
326 {
327 update_to_current_time();
328 #if NETLIST_CREATE_CSV
329 nl_owner().log_add(m_param_name, param, false);
330 #endif
331 m_param->set(param);
332 }
333
334 void netlist_mame_logic_input_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
335 {
336 update_to_current_time();
337 #if NETLIST_CREATE_CSV
338 nl_owner().log_add(m_param_name, param, false);
339 #endif
340 m_param->set(param);
341 }
342
343 void netlist_mame_ram_pointer_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
344 {
345 m_data = (*m_param)();
346 }
347
348 device_memory_interface::space_config_vector netlist_mame_cpu_device::memory_space_config() const
349 {
350 return space_config_vector {
351 std::make_pair(AS_PROGRAM, &m_program_config)
352 };
353 }
354
355 void netlist_mame_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const
356 {
357 if (entry.index() >= 0)
358 {
359 if (entry.index() & 1)
360 str = string_format("%10.6f", *((double *)entry.dataptr()));
361 else
362 str = string_format("%d", *((netlist::netlist_sig_t *)entry.dataptr()));
363 }
364 }
365
366
367 // ----------------------------------------------------------------------------------------
368 // netlist_mame_analog_input_device
369 // ----------------------------------------------------------------------------------------
370
371 void netlist_mame_sub_interface::set_mult_offset(const double mult, const double offset)
372 {
373 m_mult = mult;
374 m_offset = offset;
375 }
376
377 #if NETLIST_CREATE_CSV
378 void netlist_mame_device::log_add(char const* param, double value, bool isfloat)
379 {
380 // skip if no file
381 if (m_csv_file == nullptr)
382 return;
383
384 // make a new entry
385 buffer_entry entry = { machine().scheduler().time(), isfloat, value, param };
386
387 // flush out half of the old entries if we hit the buffer limit
388 if (m_buffer.size() >= MAX_BUFFER_ENTRIES)
389 log_flush(MAX_BUFFER_ENTRIES / 2);
390
391 // fast common case: if we go at the end, just push_back
392 if (m_buffer.size() == 0 || entry.time >= m_buffer.back().time)
393 {
394 m_buffer.push_back(entry);
395 return;
396 }
397
398 // find our place in the queue
399 for (auto cur = m_buffer.rbegin(); cur != m_buffer.rend(); cur++)
400 if (entry.time >= cur->time)
401 {
402 m_buffer.insert(cur.base(), entry);
403 return;
404 }
405
406 // if we're too early, drop this entry rather than risk putting an out-of-order
407 // entry after the last one we flushed
408 }
409
410 void netlist_mame_device::log_flush(int count)
411 {
412 if (m_csv_file == nullptr)
413 return;
414 if (count > m_buffer.size())
415 count = m_buffer.size();
416 while (count--)
417 {
418 auto &entry = m_buffer.front();
419 if (entry.isfloat)
420 fprintf(m_csv_file, "%s,%s,%f\n", entry.time.as_string(), entry.string, entry.value);
421 else
422 fprintf(m_csv_file, "%s,%s,%d\n", entry.time.as_string(), entry.string, int(entry.value));
423 m_buffer.pop_front();
424 }
425 }
426 #endif
427
428 netlist_mame_analog_input_device::netlist_mame_analog_input_device(const machine_config &mconfig, const char *tag, device_t *owner, const char *param_name)
429 : device_t(mconfig, NETLIST_ANALOG_INPUT, tag, owner, 0)
430 , netlist_mame_sub_interface(*owner)
431 , m_param(nullptr)
432 , m_auto_port(true)
433 , m_param_name(param_name)
434 , m_value_for_device_timer(0)
435 {
436 }
437
438 netlist_mame_analog_input_device::netlist_mame_analog_input_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
439 : device_t(mconfig, NETLIST_ANALOG_INPUT, tag, owner, clock)
440 , netlist_mame_sub_interface(*owner)
441 , m_param(nullptr)
442 , m_auto_port(true)
443 , m_param_name("")
444 , m_value_for_device_timer(0)
445 {
446 }
447
448 void netlist_mame_analog_input_device::device_start()
449 {
450 LOGDEVCALLS("start\n");
451 netlist::param_ref_t p = this->nl_owner().setup().find_param(pstring(m_param_name));
452 // FIXME: m_param should be param_ref_t
453 m_param = dynamic_cast<netlist::param_fp_t *>(&p.param());
454 if (m_param == nullptr)
455 {
456 fatalerror("device %s wrong parameter type for %s\n", basetag(), m_param_name);
457 }
458 if (m_mult != 1.0 || m_offset != 0.0)
459 {
460 // disable automatic scaling for ioports
461 m_auto_port = false;
462 }
463 }
464
465 void netlist_mame_analog_input_device::validity_helper(validity_checker &valid,
466 netlist::netlist_state_t &nlstate) const
467 {
468 netlist::param_ref_t p = nlstate.setup().find_param(pstring(m_param_name));
469 auto *param = dynamic_cast<netlist::param_fp_t *>(&p.param());
470 if (param == nullptr)
471 {
472 osd_printf_warning("device %s wrong parameter type for %s\n", basetag(), m_param_name);
473 }
474 }
475
476 // ----------------------------------------------------------------------------------------
477 // netlist_mame_analog_output_device
478 // ----------------------------------------------------------------------------------------
479
480 netlist_mame_analog_output_device::netlist_mame_analog_output_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
481 : device_t(mconfig, NETLIST_ANALOG_OUTPUT, tag, owner, clock)
482 , netlist_mame_sub_interface(*owner)
483 , m_in("")
484 , m_delegate(*this)
485 {
486 }
487
488
489
490 void netlist_mame_analog_output_device::custom_netlist_additions(netlist::nlparse_t &parser)
491 {
492 /* ignore if no running machine -> called within device_validity_check context */
493 if (owner()->has_running_machine())
494 m_delegate.resolve();
495
496 const pstring pin(m_in);
497 pstring dname = pstring("OUT_") + pin;
498
499 parser.register_dev(dname, dname);
500 parser.register_link(dname + ".IN", pin);
501 }
502
503 void netlist_mame_analog_output_device::pre_parse_action(netlist::nlparse_t &parser)
504 {
505 const pstring pin(m_in);
506 pstring dname = pstring("OUT_") + pin;
507
508 const auto lambda = [this](auto &in, netlist::nl_fptype val)
509 {
510 this->cpu()->update_icount(in.exec().time());
511 this->m_delegate(val, this->cpu()->local_time());
512 this->cpu()->check_mame_abort_slice();
513 };
514
515 using lb_t = decltype(lambda);
516 using cb_t = netlist::interface::NETLIB_NAME(analog_callback)<lb_t>;
517
518 parser.factory().add<cb_t, netlist::nl_fptype, lb_t>(dname,
519 netlist::factory::properties("-", PSOURCELOC()), 1e-6, std::forward<lb_t>(lambda));
520 }
521
522 void netlist_mame_analog_output_device::device_start()
523 {
524 LOGDEVCALLS("start\n");
525 }
526
527
528 // ----------------------------------------------------------------------------------------
529 // netlist_mame_logic_output_device
530 // ----------------------------------------------------------------------------------------
531
532 netlist_mame_logic_output_device::netlist_mame_logic_output_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
533 : device_t(mconfig, NETLIST_LOGIC_OUTPUT, tag, owner, clock)
534 , netlist_mame_sub_interface(*owner)
535 , m_in("")
536 , m_delegate(*this)
537 {
538 }
539
540 void netlist_mame_logic_output_device::custom_netlist_additions(netlist::nlparse_t &parser)
541 {
542 /* ignore if no running machine -> called within device_validity_check context */
543 if (owner()->has_running_machine())
544 m_delegate.resolve();
545
546 const pstring pin(m_in);
547 pstring dname = pstring("OUT_") + pin;
548
549 parser.register_dev(dname, dname);
550 parser.register_link(dname + ".IN", pin);
551 }
552
553 void netlist_mame_logic_output_device::pre_parse_action(netlist::nlparse_t &parser)
554 {
555 const pstring pin(m_in);
556 pstring dname = pstring("OUT_") + pin;
557
558 const auto lambda = [this](auto &in, netlist::netlist_sig_t val)
559 {
560 this->cpu()->update_icount(in.exec().time());
561 this->m_delegate(val, this->cpu()->local_time());
562 this->cpu()->check_mame_abort_slice();
563 };
564
565 using lb_t = decltype(lambda);
566 using cb_t = netlist::interface::NETLIB_NAME(logic_callback)<lb_t>;
567
568 parser.factory().add<cb_t, lb_t>(dname,
569 netlist::factory::properties("-", PSOURCELOC()), std::forward<lb_t>(lambda));
570 }
571
572
573 void netlist_mame_logic_output_device::device_start()
574 {
575 LOGDEVCALLS("start\n");
576 }
577
578
579 // ----------------------------------------------------------------------------------------
580 // netlist_mame_int_input_device
581 // ----------------------------------------------------------------------------------------
582
583 netlist_mame_int_input_device::netlist_mame_int_input_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
584 : device_t(mconfig, NETLIST_INT_INPUT, tag, owner, clock)
585 , netlist_mame_sub_interface(*owner)
586 , m_param(nullptr)
587 , m_mask(0xffffffff)
588 , m_shift(0)
589 , m_param_name("")
590 {
591 }
592
593 void netlist_mame_int_input_device::set_params(const char *param_name, const uint32_t mask, const uint32_t shift)
594 {
595 LOGDEVCALLS("set_params\n");
596 m_param_name = param_name;
597 m_shift = shift;
598 m_mask = mask;
599 }
600
601 void netlist_mame_int_input_device::device_start()
602 {
603 LOGDEVCALLS("start\n");
604 netlist::param_ref_t p = downcast<netlist_mame_device *>(this->owner())->setup().find_param(pstring(m_param_name));
605 m_param = dynamic_cast<netlist::param_int_t *>(&p.param());
606 if (m_param == nullptr)
607 {
608 fatalerror("device %s wrong parameter type for %s\n", basetag(), m_param_name);
609 }
610 }
611
612 void netlist_mame_int_input_device::validity_helper(validity_checker &valid,
613 netlist::netlist_state_t &nlstate) const
614 {
615 netlist::param_ref_t p = nlstate.setup().find_param(pstring(m_param_name));
616 auto *param = dynamic_cast<netlist::param_int_t *>(&p.param());
617 if (param == nullptr)
618 {
619 osd_printf_warning("device %s wrong parameter type for %s\n", basetag(), m_param_name);
620 }
621 }
622
623 // ----------------------------------------------------------------------------------------
624 // netlist_mame_logic_input_device
625 // ----------------------------------------------------------------------------------------
626
627 netlist_mame_logic_input_device::netlist_mame_logic_input_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
628 : device_t(mconfig, NETLIST_LOGIC_INPUT, tag, owner, clock)
629 , netlist_mame_sub_interface(*owner)
630 , m_param(nullptr)
631 , m_shift(0)
632 , m_param_name("")
633 {
634 }
635
636 void netlist_mame_logic_input_device::set_params(const char *param_name, const uint32_t shift)
637 {
638 LOGDEVCALLS("set_params\n");
639 m_param_name = param_name;
640 m_shift = shift;
641 }
642
643 void netlist_mame_logic_input_device::device_start()
644 {
645 LOGDEVCALLS("start\n");
646 netlist::param_ref_t p = downcast<netlist_mame_device *>(this->owner())->setup().find_param(pstring(m_param_name));
647 m_param = dynamic_cast<netlist::param_logic_t *>(&p.param());
648 if (m_param == nullptr)
649 {
650 fatalerror("device %s wrong parameter type for %s\n", basetag(), m_param_name);
651 }
652 }
653
654 void netlist_mame_logic_input_device::validity_helper(validity_checker &valid,
655 netlist::netlist_state_t &nlstate) const
656 {
657 netlist::param_ref_t p = nlstate.setup().find_param(pstring(m_param_name));
658 auto *param = dynamic_cast<netlist::param_logic_t *>(&p.param());
659 if (param == nullptr)
660 {
661 osd_printf_warning("device %s wrong parameter type for %s\n", basetag(), m_param_name);
662 }
663 }
664
665
666 // ----------------------------------------------------------------------------------------
667 // netlist_mame_ram_pointer_device
668 // ----------------------------------------------------------------------------------------
669
670 netlist_mame_ram_pointer_device::netlist_mame_ram_pointer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
671 : device_t(mconfig, NETLIST_RAM_POINTER, tag, owner, clock)
672 , netlist_mame_sub_interface(*owner)
673 , m_param(nullptr)
674 , m_param_name("")
675 , m_data(nullptr)
676 {
677 }
678
679 netlist_mame_ram_pointer_device::netlist_mame_ram_pointer_device(const machine_config &mconfig, const char *tag, device_t *owner, const char *pname)
680 : device_t(mconfig, NETLIST_RAM_POINTER, tag, owner, 0)
681 , netlist_mame_sub_interface(*owner)
682 , m_param(nullptr)
683 , m_param_name(pname)
684 , m_data(nullptr)
685 {
686 }
687
688 void netlist_mame_ram_pointer_device::set_params(const char *param_name)
689 {
690 LOGDEVCALLS("set_params\n");
691 m_param_name = param_name;
692 }
693
694 void netlist_mame_ram_pointer_device::device_start()
695 {
696 LOGDEVCALLS("start\n");
697 netlist::param_ref_t p = downcast<netlist_mame_device *>(this->owner())->setup().find_param(pstring(m_param_name));
698 m_param = dynamic_cast<netlist::param_ptr_t *>(&p.param());
699 if (m_param == nullptr)
700 {
701 fatalerror("device %s wrong parameter type for %s\n", basetag(), m_param_name);
702 }
703
704 m_data = (*m_param)();
705 }
706
707 void netlist_mame_ram_pointer_device::validity_helper(validity_checker &valid,
708 netlist::netlist_state_t &nlstate) const
709 {
710 netlist::param_ref_t p = nlstate.setup().find_param(pstring(m_param_name));
711 auto *param = dynamic_cast<netlist::param_ptr_t *>(&p.param());
712 if (param == nullptr)
713 {
714 osd_printf_warning("device %s wrong parameter type for %s\n", basetag(), m_param_name);
715 }
716 }
717
718 // ----------------------------------------------------------------------------------------
719 // netlist_mame_stream_input_device
720 // ----------------------------------------------------------------------------------------
721
722 netlist_mame_stream_input_device::netlist_mame_stream_input_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
723 : device_t(mconfig, NETLIST_STREAM_INPUT, tag, owner, clock)
724 , netlist_mame_sub_interface(*owner)
725 , m_channel(0)
726 , m_param_name("")
727 {
728 }
729
730 void netlist_mame_stream_input_device::set_params(int channel, const char *param_name)
731 {
732 m_param_name = param_name;
733 m_channel = channel;
734 }
735
736 void netlist_mame_stream_input_device::device_start()
737 {
738 LOGDEVCALLS("start\n");
739 }
740
741 void netlist_mame_stream_input_device::custom_netlist_additions(netlist::nlparse_t &parser)
742 {
743 pstring name = plib::pfmt("STREAM_INPUT_{}")(m_channel);
744 parser.register_dev("NETDEV_SOUND_IN", name);
745
746 parser.register_param(name + ".CHAN", pstring(m_param_name));
747 parser.register_param(name + ".MULT", m_mult);
748 parser.register_param(name + ".OFFSET", m_offset);
749 parser.register_param(name + ".ID", m_channel);
750 }
751
752
753 // ----------------------------------------------------------------------------------------
754 // netlist_mame_stream_output_device
755 // ----------------------------------------------------------------------------------------
756
757 netlist_mame_stream_output_device::netlist_mame_stream_output_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
758 : device_t(mconfig, NETLIST_STREAM_OUTPUT, tag, owner, clock)
759 , netlist_mame_sub_interface(*owner)
760 , m_channel(0)
761 , m_out_name("")
762 , m_cur(0.0)
763 , m_sample_time(netlist::netlist_time::from_hz(1))
764 , m_last_buffer_time(netlist::netlist_time_ext::zero())
765 {
766 }
767
768 void netlist_mame_stream_output_device::set_params(int channel, const char *out_name)
769 {
770 m_out_name = out_name;
771 m_channel = channel;
772 sound()->register_stream_output(channel, this);
773 }
774
775 /// \brief save state helper for plib classes supporting the save_state interface
776 ///
777 struct save_helper
778 {
779 save_helper(device_t *dev, const std::string &prefix)
780 : m_device(dev), m_prefix(prefix)
781 {}
782
783 template<typename T, typename X = void *>
784 void save_item(T &&item, const std::string &name, X = nullptr)
785 {
786 m_device->save_item(item, (m_prefix + "_" + name).c_str());
787 }
788
789 template <typename X = void *>
790 std::enable_if_t<plib::compile_info::has_int128::value && std::is_pointer<X>::value, void>
791 save_item(INT128 &item, const std::string &name, X = nullptr)
792 {
793 auto *p = reinterpret_cast<std::uint64_t *>(&item);
794 m_device->save_item(p[0], (m_prefix + "_" + name + "_1").c_str());
795 m_device->save_item(p[1], (m_prefix + "_" + name + "_2").c_str());
796 }
797
798 private:
799 device_t *m_device;
800 std::string m_prefix;
801 };
802
803 void netlist_mame_stream_output_device::device_start()
804 {
805 LOGDEVCALLS("start %s\n", name());
806 m_cur = 0.0;
807 m_last_buffer_time = netlist::netlist_time_ext::zero();
808
809 save_item(NAME(m_cur));
810 m_sample_time.save_state(save_helper(this, "m_sample_time"));
811 m_last_buffer_time.save_state(save_helper(this, "m_last_buffer_time"));
812 }
813
814 void netlist_mame_stream_output_device::device_reset()
815 {
816 LOGDEVCALLS("reset %s\n", name());
817 #if 0
818 m_cur = 0.0;
819 m_last_buffer_time = netlist::netlist_time_ext::zero();
820 #endif
821 }
822
823 void netlist_mame_stream_output_device::sound_update_fill(write_stream_view &target)
824 {
825 if (target.samples() < m_buffer.size())
826 osd_printf_warning("sound %s: samples %d less bufsize %d\n", name(), target.samples(), m_buffer.size());
827
828 int sampindex;
829 for (sampindex = 0; sampindex < m_buffer.size(); sampindex++)
830 target.put(sampindex, m_buffer[sampindex]);
831 target.fill(m_cur, sampindex);
832 }
833
834
835 void netlist_mame_stream_output_device::pre_parse_action(netlist::nlparse_t &parser)
836 {
837 pstring dname = plib::pfmt("STREAM_OUT_{1}")(m_channel);
838
839 const auto lambda = [this](auto &in, netlist::nl_fptype val)
840 {
841 this->process(in.exec().time(), val);
842 };
843
844 using lb_t = decltype(lambda);
845 using cb_t = netlist::interface::NETLIB_NAME(analog_callback)<lb_t>;
846
847 parser.factory().add<cb_t, netlist::nl_fptype, lb_t>(dname,
848 netlist::factory::properties("-", PSOURCELOC()), 1e-9, std::forward<lb_t>(lambda));
849 }
850
851
852 void netlist_mame_stream_output_device::custom_netlist_additions(netlist::nlparse_t &parser)
853 {
854 pstring dname = plib::pfmt("STREAM_OUT_{1}")(m_channel);
855
856 parser.register_dev(dname, dname);
857 parser.register_link(dname + ".IN", pstring(m_out_name));
858 }
859
860 void netlist_mame_stream_output_device::process(netlist::netlist_time_ext tim, netlist::nl_fptype val)
861 {
862 val = val * m_mult + m_offset;
863
864 int pos = (tim - m_last_buffer_time) / m_sample_time;
865 //if (pos > m_bufsize)
866 // throw emu_fatalerror("sound %s: pos %d exceeded bufsize %d\n", name().c_str(), pos, m_bufsize);
867 while (m_buffer.size() < pos )
868 {
869 m_buffer.push_back(static_cast<stream_buffer::sample_t>(m_cur));
870 }
871
872 m_cur = val;
873 }
874
875
876 // ----------------------------------------------------------------------------------------
877 // netlist_mame_device
878 // ----------------------------------------------------------------------------------------
879
880 netlist_mame_device::netlist_mame_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
881 : netlist_mame_device(mconfig, NETLIST_CORE, tag, owner, clock)
882 {
883 }
884
885 netlist_mame_device::netlist_mame_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
886 : device_t(mconfig, type, tag, owner, clock)
887 , m_setup_func(nullptr)
888 , m_device_reset_called(false)
889 {
890 }
891
892 netlist_mame_device::~netlist_mame_device()
893 {
894 LOGDEVCALLS("~netlist_mame_device\n");
895 }
896
897 void netlist_mame_device::device_config_complete()
898 {
899 LOGDEVCALLS("device_config_complete %s\n", this->mconfig().gamedrv().name);
900
901 }
902
903 void netlist_mame_device::common_dev_start(netlist::netlist_state_t *lnetlist) const
904 {
905 auto &lsetup = lnetlist->setup();
906
907 // Override log statistics
908 pstring p = plib::util::environment("NL_STATS", "");
909 if (p != "")
910 {
911 bool err=false;
912 bool v = plib::pstonum_ne<bool>(p, err);
913 if (err)
914 lsetup.log().warning("NL_STATS: invalid value {1}", p);
915 else
916 lnetlist->exec().enable_stats(v);
917 }
918
919 // register additional devices
920
921 nl_register_devices(lsetup.parser());
922
923 /* let sub-devices add sources and do stuff prior to parsing */
924 for (device_t &d : subdevices())
925 {
926 netlist_mame_sub_interface *sdev = dynamic_cast<netlist_mame_sub_interface *>(&d);
927 if( sdev != nullptr )
928 {
929 LOGDEVCALLS("Preparse subdevice %s/%s\n", d.name(), d.shortname());
930 sdev->pre_parse_action(lsetup.parser());
931 }
932 }
933
934 // add default data provider for roms - if not in validity check
935 lsetup.parser().register_source<netlist_data_memregions_t>(*this);
936
937 // Read the netlist
938 m_setup_func(lsetup.parser());
939
940 // let sub-devices tweak the netlist
941 for (device_t &d : subdevices())
942 {
943 netlist_mame_sub_interface *sdev = dynamic_cast<netlist_mame_sub_interface *>(&d);
944 if( sdev != nullptr )
945 {
946 LOGDEVCALLS("Found subdevice %s/%s\n", d.name(), d.shortname());
947 sdev->custom_netlist_additions(lsetup.parser());
948 }
949 }
950 }
951
952 struct validity_logger
953 {
954 void log(plib::plog_level l, const pstring &ls)
955 {
956 putf8string ls8(ls);
957
958 switch (l)
959 {
960 case plib::plog_level::DEBUG:
961 break;
962 case plib::plog_level::VERBOSE:
963 break;
964 case plib::plog_level::INFO:
965 osd_printf_verbose("netlist INFO: %s\n", ls8);
966 break;
967 case plib::plog_level::WARNING:
968 osd_printf_warning("netlist WARNING: %s\n", ls8);
969 break;
970 case plib::plog_level::ERROR:
971 osd_printf_error("netlist ERROR: %s\n", ls8);
972 break;
973 case plib::plog_level::FATAL:
974 osd_printf_error("netlist FATAL: %s\n", ls8);
975 break;
976 }
977 }
978 };
979
980 std::unique_ptr<netlist::netlist_state_t> netlist_mame_device::base_validity_check(validity_checker &valid) const
981 {
982 try
983 {
984 validity_logger logger;
985 plib::chrono::timer<plib::chrono::system_ticks> t;
986 t.start();
987 auto lnetlist = std::make_unique<netlist::netlist_state_t>("netlist",
988 plib::plog_delegate(&validity_logger::log, &logger));
989 // enable validation mode
990
991 lnetlist->set_static_solver_lib(std::make_unique<plib::dynlib_static>(nullptr));
992
993 common_dev_start(lnetlist.get());
994 lnetlist->setup().prepare_to_run();
995
996 for (device_t &d : subdevices())
997 {
998 netlist_mame_sub_interface *sdev = dynamic_cast<netlist_mame_sub_interface *>(&d);
999 if( sdev != nullptr )
1000 {
1001 LOGDEVCALLS("Validity check on subdevice %s/%s\n", d.name(), d.shortname());
1002 sdev->validity_helper(valid, *lnetlist);
1003 }
1004 }
1005
1006 t.stop();
1007 //printf("time %s %f\n", this->mconfig().gamedrv().name, t.as_seconds<double>());
1008 return lnetlist;
1009 }
1010 catch (memregion_not_set &err)
1011 {
1012 // Do not report an error. Validity check has no access to ROM area.
1013 osd_printf_verbose("%s\n", err.what());
1014 }
1015 catch (emu_fatalerror &err)
1016 {
1017 osd_printf_error("%s\n", err.what());
1018 }
1019 catch (std::exception &err)
1020 {
1021 osd_printf_error("%s\n", err.what());
1022 }
1023 return std::unique_ptr<netlist::netlist_state_t>(nullptr);
1024 }
1025
1026 void netlist_mame_device::device_validity_check(validity_checker &valid) const
1027 {
1028
1029 base_validity_check(valid);
1030 //rom_exists(mconfig().root_device());
1031 LOGDEVCALLS("device_validity_check %s\n", this->mconfig().gamedrv().name);
1032 }
1033
1034
1035 void netlist_mame_device::device_start_common()
1036 {
1037 m_netlist = std::make_unique<netlist_mame_t>(*this, "netlist");
1038
1039 m_netlist->set_static_solver_lib(std::make_unique<plib::dynlib_static>(nl_static_solver_syms));
1040
1041 if (!machine().options().verbose())
1042 {
1043 m_netlist->log().verbose.set_enabled(false);
1044 m_netlist->log().debug.set_enabled(false);
1045 }
1046
1047 common_dev_start(m_netlist.get());
1048 m_netlist->setup().prepare_to_run();
1049
1050
1051 m_device_reset_called = false;
1052
1053 #if NETLIST_CREATE_CSV
1054 std::string name = machine().system().name;
1055 name += tag();
1056 for (int index = 0; index < name.size(); index++)
1057 if (name[index] == ':')
1058 name[index] = '_';
1059 name += ".csv";
1060 m_csv_file = fopen(name.c_str(), "wb");
1061 #endif
1062
1063 LOGDEVCALLS("device_start exit\n");
1064 }
1065
1066
1067 void netlist_mame_device::device_start()
1068 {
1069 LOGDEVCALLS("device_start entry\n");
1070
1071 device_start_common();
1072 save_state();
1073
1074 LOGDEVCALLS("device_start exit\n");
1075 }
1076
1077
1078 void netlist_mame_device::device_reset()
1079 {
1080 LOGDEVCALLS("device_reset\n");
1081 if (!m_device_reset_called)
1082 {
1083 // netlists don't have a reset line, doing a soft-reset is pointless
1084 // the only reason we call these here once after device_start
1085 // is that netlist input devices may be started after the netlist device
1086 // and because the startup code may trigger actions which need all
1087 // devices set up.
1088 netlist().free_setup_resources();
1089 netlist().exec().reset();
1090 m_device_reset_called = true;
1091 }
1092 }
1093
1094 void netlist_mame_device::device_stop()
1095 {
1096 LOGDEVCALLS("device_stop\n");
1097 if (m_netlist)
1098 netlist().exec().stop();
1099 #if NETLIST_CREATE_CSV
1100 if (m_csv_file != nullptr)
1101 {
1102 log_flush();
1103 fclose(m_csv_file);
1104 }
1105 #endif
1106 }
1107
1108 void netlist_mame_device::device_post_load()
1109 {
1110 LOGDEVCALLS("device_post_load\n");
1111
1112 netlist().run_state_manager().post_load();
1113 netlist().rebuild_lists();
1114 }
1115
1116 void netlist_mame_device::device_pre_save()
1117 {
1118 LOGDEVCALLS("device_pre_save\n");
1119
1120 netlist().run_state_manager().pre_save();
1121 }
1122
1123 void netlist_mame_cpu_device::update_icount(netlist::netlist_time_ext time) noexcept
1124 {
1125 const netlist::netlist_time_ext delta = (time - m_old).shl(MDIV_SHIFT) + m_rem;
1126 const uint64_t d = delta / m_div;
1127 m_old = time;
1128 m_rem = (delta - (m_div * d));
1129 //printf("d %d m_rem %d\n", (int) d, (int) m_rem.as_raw());
1130 m_icount -= d;
1131 }
1132
1133 void netlist_mame_cpu_device::check_mame_abort_slice() noexcept
1134 {
1135 if (m_icount <= 0)
1136 netlist().exec().abort_current_queue_slice();
1137 }
1138
1139 void netlist_mame_device::save_state()
1140 {
1141 for (auto const & s : netlist().run_state_manager().save_list())
1142 {
1143 putf8string u8name(s->name());
1144
1145 netlist().log().debug("saving state for {1}\n", u8name.c_str());
1146 if (s->dt().is_float())
1147 {
1148 if (s->dt().size() == sizeof(double))
1149 save_pointer((double *) s->ptr(), u8name.c_str(), s->count());
1150 else if (s->dt().size() == sizeof(float))
1151 save_pointer((float *) s->ptr(), u8name.c_str(), s->count());
1152 else
1153 netlist().log().fatal("Unknown floating type for {1}\n", u8name.c_str());
1154 }
1155 else if (s->dt().is_integral())
1156 {
1157 if (s->dt().size() == sizeof(int64_t))
1158 save_pointer((int64_t *) s->ptr(), u8name.c_str(), s->count());
1159 else if (s->dt().size() == sizeof(int32_t))
1160 save_pointer((int32_t *) s->ptr(), u8name.c_str(), s->count());
1161 else if (s->dt().size() == sizeof(int16_t))
1162 save_pointer((int16_t *) s->ptr(), u8name.c_str(), s->count());
1163 else if (s->dt().size() == sizeof(int8_t))
1164 save_pointer((int8_t *) s->ptr(), u8name.c_str(), s->count());
1165 else if (plib::compile_info::has_int128::value && s->dt().size() == sizeof(INT128))
1166 save_pointer((int64_t *) s->ptr(), u8name.c_str(), s->count() * 2);
1167 else
1168 netlist().log().fatal("Unknown integral type size {1} for {2}\n", s->dt().size(), u8name.c_str());
1169 }
1170 else if (s->dt().is_custom())
1171 {
1172 /* do nothing */
1173 }
1174 else
1175 netlist().log().fatal("found unsupported save element {1}\n", u8name);
1176 }
1177 }
1178
1179 // ----------------------------------------------------------------------------------------
1180 // netlist_mame_cpu_device
1181 // ----------------------------------------------------------------------------------------
1182
1183 netlist_mame_cpu_device::netlist_mame_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1184 : netlist_mame_device(mconfig, NETLIST_CPU, tag, owner, clock)
1185 , device_execute_interface(mconfig, *this)
1186 , device_state_interface(mconfig, *this)
1187 , device_disasm_interface(mconfig, *this)
1188 , device_memory_interface(mconfig, *this)
1189 , m_program_config("program", ENDIANNESS_LITTLE, 8, 12) // Interface is needed to keep debugger happy
1190 , m_icount(0)
1191 , m_old(netlist::netlist_time_ext::zero())
1192 , m_genPC(0)
1193 {
1194 }
1195
1196
1197 void netlist_mame_cpu_device::device_start()
1198 {
1199 LOGDEVCALLS("device_start entry\n");
1200
1201 device_start_common();
1202 // FIXME: use save_helper
1203 netlist().save(*this, m_rem, pstring(this->name()), "m_rem");
1204 netlist().save(*this, m_div, pstring(this->name()), "m_div");
1205 netlist().save(*this, m_old, pstring(this->name()), "m_old");
1206
1207 m_old = netlist::netlist_time_ext::zero();
1208 m_rem = netlist::netlist_time_ext::zero();
1209
1210 save_state();
1211
1212 state_add(STATE_GENPC, "GENPC", m_genPC).noshow();
1213 state_add(STATE_GENPCBASE, "CURPC", m_genPC).noshow();
1214
1215 int index = 0;
1216 for (auto &n : netlist().nets())
1217 {
1218 putf8string name(n->name()); //plib::replace_all(n->name(), ".", "_");
1219 if (n->is_logic())
1220 {
1221 auto nl = downcast<netlist::logic_net_t *>(n.get());
1222 state_add<netlist::netlist_sig_t>(index++, name.c_str(),
1223 [nl]() { return nl->Q(); },
1224 [nl](netlist::netlist_sig_t data) { nl->set_Q_and_push(data, netlist::netlist_time::quantum()); });
1225 }
1226 else
1227 {
1228 auto nl = downcast<netlist::analog_net_t *>(n.get());
1229 state_add<double>(
1230 index++,
1231 name.c_str(),
1232 [nl]() { return nl->Q_Analog(); },
1233 [nl](double data) { nl->set_Q_Analog(data); });
1234 }
1235 }
1236
1237 // set our instruction counter
1238 set_icountptr(m_icount);
1239
1240 LOGDEVCALLS("device_start exit\n");
1241 }
1242
1243 void netlist_mame_cpu_device::device_clock_changed()
1244 {
1245 m_div = static_cast<netlist::netlist_time_ext>(
1246 (netlist::netlist_time_ext::resolution() << MDIV_SHIFT) / clock());
1247 //printf("m_div %d\n", (int) m_div.as_raw());
1248 netlist().log().debug("Setting clock {1} and divisor {2}\n", clock(), m_div.as_double());
1249 }
1250
1251 void netlist_mame_cpu_device::nl_register_devices(netlist::nlparse_t &parser) const
1252 {
1253 }
1254
1255 uint64_t netlist_mame_cpu_device::execute_clocks_to_cycles(uint64_t clocks) const noexcept
1256 {
1257 return clocks;
1258 }
1259
1260 uint64_t netlist_mame_cpu_device::execute_cycles_to_clocks(uint64_t cycles) const noexcept
1261 {
1262 return cycles;
1263 }
1264
1265 void netlist_mame_cpu_device::execute_run()
1266 {
1267 //m_ppc = m_pc; // copy PC to previous PC
1268 if (debugger_enabled())
1269 {
1270 while (m_icount > 0)
1271 {
1272 m_genPC++;
1273 m_genPC &= 255;
1274 debugger_instruction_hook(m_genPC);
1275 netlist().exec().process_queue(nltime_ext_from_clocks(1));
1276 update_icount(netlist().exec().time());
1277 }
1278 }
1279 else
1280 {
1281 netlist().exec().process_queue(nltime_ext_from_clocks(m_icount));
1282 update_icount(netlist().exec().time());
1283 }
1284 }
1285
1286 std::unique_ptr<util::disasm_interface> netlist_mame_cpu_device::create_disassembler()
1287 {
1288 return std::make_unique<netlist_disassembler>(this);
1289 }
1290
1291 netlist_disassembler::netlist_disassembler(netlist_mame_cpu_device *dev) : m_dev(dev)
1292 {
1293 }
1294
1295 u32 netlist_disassembler::opcode_alignment() const
1296 {
1297 return 1;
1298 }
1299
1300 offs_t netlist_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms)
1301 {
1302 unsigned startpc = pc;
1303 int relpc = pc - m_dev->genPC();
1304 if (relpc >= 0 && relpc < m_dev->netlist().exec().queue().size())
1305 {
1306 int dpc = m_dev->netlist().exec().queue().size() - relpc - 1;
1307 util::stream_format(stream, "%c %s @%10.7f", (relpc == 0) ? '*' : ' ', m_dev->netlist().exec().queue()[dpc].object()->name().c_str(),
1308 m_dev->netlist().exec().queue()[dpc].exec_time().as_double());
1309 }
1310
1311 pc+=1;
1312 return (pc - startpc);
1313 }
1314
1315 // ----------------------------------------------------------------------------------------
1316 // netlist_mame_sound_device
1317 // ----------------------------------------------------------------------------------------
1318
1319 netlist_mame_sound_device::netlist_mame_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
1320 : netlist_mame_device(mconfig, NETLIST_SOUND, tag, owner, 0)
1321 , device_sound_interface(mconfig, *this)
1322 , m_stream(nullptr)
1323 , m_cur_time(attotime::zero)
1324 , m_sound_clock(clock)
1325 , m_attotime_per_clock(attotime::zero)
1326 , m_last_update_to_current_time(attotime::zero)
1327 {
1328 }
1329
1330 void netlist_mame_sound_device::device_validity_check(validity_checker &valid) const
1331 {
1332 LOGDEVCALLS("sound device_validity check\n");
1333 auto lnetlist = base_validity_check(valid);
1334 if (lnetlist)
1335 {
1336 /* Ok - do some more checks */
1337 if (m_out.size() == 0)
1338 osd_printf_error("No output devices\n");
1339 else
1340 {
1341 for (auto &outdev : m_out)
1342 {
1343 if (outdev.first < 0 || outdev.first >= m_out.size())
1344 osd_printf_error("illegal output channel number %d\n", outdev.first);
1345 }
1346 }
1347 std::vector<nld_sound_in *> indevs = lnetlist->get_device_list<nld_sound_in>();
1348 for (auto &e : indevs)
1349 {
1350 if (e->id() >= indevs.size())
1351 osd_printf_error("illegal input channel number %d\n", e->id());
1352 }
1353 }
1354 }
1355
1356 void netlist_mame_sound_device::device_start()
1357 {
1358 LOGDEVCALLS("sound device_start\n");
1359
1360 m_attotime_per_clock = attotime::from_hz(m_sound_clock);
1361
1362 save_item(NAME(m_cur_time));
1363 save_item(NAME(m_attotime_per_clock));
1364
1365 device_start_common();
1366 save_state();
1367
1368 m_cur_time = attotime::zero;
1369
1370 // Configure outputs
1371
1372 if (m_out.size() == 0)
1373 fatalerror("No output devices");
1374
1375 /* resort channels */
1376 for (auto &outdev : m_out)
1377 {
1378 if (outdev.first < 0 || outdev.first >= m_out.size())
1379 fatalerror("illegal output channel number %d", outdev.first);
1380 outdev.second->set_sample_time(netlist::netlist_time::from_hz(m_sound_clock));
1381 outdev.second->buffer_reset(netlist::netlist_time_ext::zero());
1382 }
1383
1384 // Configure inputs
1385
1386 m_in.clear();
1387
1388 std::vector<nld_sound_in *> indevs = netlist().get_device_list<nld_sound_in>();
1389 for (auto &e : indevs)
1390 {
1391 m_in.emplace(e->id(), e);
1392 const auto sample_time = netlist::netlist_time::from_raw(static_cast<netlist::netlist_time::internal_type>(nltime_from_attotime(m_attotime_per_clock).as_raw()));
1393 e->resolve_params(sample_time);
1394 }
1395 for (auto &e : m_in)
1396 {
1397 if (e.first < 0 || e.first >= m_in.size())
1398 fatalerror("illegal input channel number %d", e.first);
1399 }
1400 m_inbuffer.resize(m_in.size());
1401
1402 /* initialize the stream(s) */
1403 m_stream = stream_alloc(m_in.size(), m_out.size(), m_sound_clock, STREAM_DISABLE_INPUT_RESAMPLING);
1404
1405 LOGDEVCALLS("sound device_start exit\n");
1406 }
1407
1408
1409 void netlist_mame_sound_device::nl_register_devices(netlist::nlparse_t &parser) const
1410 {
1411 //parser.factory().add<nld_sound_out>("NETDEV_SOUND_OUT",
1412 // netlist::factory::properties("+CHAN", PSOURCELOC()));
1413 parser.factory().add<nld_sound_in>("NETDEV_SOUND_IN",
1414 netlist::factory::properties("-", PSOURCELOC()));
1415 }
1416
1417 void netlist_mame_sound_device::register_stream_output(int channel, netlist_mame_stream_output_device *so)
1418 {
1419 m_out[channel] = so;
1420 }
1421
1422 void netlist_mame_sound_device::update_to_current_time()
1423 {
1424 LOGDEBUG("before update\n");
1425
1426 get_stream()->update();
1427
1428 if (machine().time() < m_last_update_to_current_time)
1429 LOGTIMING("machine.time() decreased 2\n");
1430
1431 m_last_update_to_current_time = machine().time();
1432
1433 const auto mtime = nltime_from_attotime(machine().time());
1434 const auto cur(netlist().exec().time());
1435
1436 if (mtime > cur)
1437 {
1438 //expected don't log
1439 //LOGTIMING("%f us\n", (mtime - cur).as_double() * 1000000.0);
1440 netlist().exec().process_queue(mtime - cur);
1441 }
1442 else if (mtime < cur)
1443 LOGTIMING("%s : %f us before machine time\n", this->name(), (cur - mtime).as_double() * 1000000.0);
1444 }
1445
1446 void netlist_mame_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
1447 {
1448 for (auto &e : m_in)
1449 {
1450 auto clock_period = inputs[e.first].sample_period();
1451 auto sample_time = netlist::netlist_time::from_raw(static_cast<netlist::netlist_time::internal_type>(nltime_from_attotime(clock_period).as_raw()));
1452 m_inbuffer[e.first] = netlist_mame_sound_input_buffer(inputs[e.first]);
1453 e.second->buffer_reset(sample_time, m_inbuffer[e.first].samples(), &m_inbuffer[e.first]);
1454 }
1455
1456 int samples = outputs[0].samples();
1457 LOGDEBUG("samples %d\n", samples);
1458
1459 // end_time() is the time at the END of the last sample we're generating
1460 // however, the sample value is the value at the START of that last sample,
1461 // so subtract one sample period so that we only process up to the minimum
1462 auto nl_target_time = nltime_from_attotime(outputs[0].end_time() - outputs[0].sample_period());
1463
1464 auto nltime(netlist().exec().time());
1465 if (nltime < nl_target_time)
1466 {
1467 netlist().exec().process_queue(nl_target_time - nltime);
1468 }
1469
1470 for (auto &e : m_out)
1471 {
1472 e.second->sound_update_fill(outputs[e.first]);
1473 e.second->buffer_reset(nl_target_time);
1474 }
1475
1476 }
1477