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 &params)
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