diff options
Diffstat (limited to 'src/devices/machine/gen_fifo.cpp')
-rw-r--r-- | src/devices/machine/gen_fifo.cpp | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/src/devices/machine/gen_fifo.cpp b/src/devices/machine/gen_fifo.cpp new file mode 100644 index 00000000000..639adacd571 --- /dev/null +++ b/src/devices/machine/gen_fifo.cpp @@ -0,0 +1,201 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#include "emu.h" +#include "gen_fifo.h" + +template<typename T> generic_fifo_device_base<T>::generic_fifo_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, type, tag, owner, clock), + m_empty_cb(*this), + m_full_cb(*this), + m_on_fifo_empty_pre_sync([](){}), + m_on_fifo_empty_post_sync([](){}), + m_on_fifo_unempty([](){}), + m_on_fifo_full_post_sync([](){}), + m_on_fifo_unfull([](){}), + m_on_push([](){}), + m_on_pop([](){}), + m_sync_empty(nullptr), + m_sync_full(nullptr), + m_size(0), + m_empty_triggered(false), + m_full_triggered(false) +{ +} + +template<typename T> void generic_fifo_device_base<T>::clear() +{ + bool was_empty = is_empty(); + bool was_full = m_size && is_full(); + m_values.clear(); + m_extra_values.clear(); + + if(m_sync_empty) { + m_sync_empty->adjust(attotime::never); + m_sync_full->adjust(attotime::never); + } + + if(was_full) + m_full_cb(false); + + if(m_full_triggered) { + m_full_triggered = false; + m_on_fifo_unfull(); + } + + if(!was_empty) + m_empty_cb(true); +} + +template<typename T> void generic_fifo_device_base<T>::device_start() +{ + m_empty_cb.resolve_safe(); + m_full_cb.resolve_safe(); + + m_sync_empty = timer_alloc(0); + m_sync_full = timer_alloc(1); + + // This is not saving the fifo, let's hope it's empty... + save_item(NAME(m_size)); + save_item(NAME(m_empty_triggered)); + save_item(NAME(m_full_triggered)); +} + +template<typename T> void generic_fifo_device_base<T>::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch(id) { + case T_FULL: { + // Add the extra values in if there's space + bool was_empty = is_empty(); + bool was_full = is_full(); + + while(!m_extra_values.empty() && !is_full()) { + T t(std::move(m_extra_values.front())); + m_extra_values.erase(m_extra_values.begin()); + m_values.emplace_back(std::move(t)); + } + + // Adjust devcb lines as needed + if(was_empty && !is_empty()) + set_empty_cb(false); + if(!was_full && is_full()) + set_full_cb(true); + + // Are there still values that don't fit? + if(!m_extra_values.empty()) { + // If yes, stop the source if not done yet + if(!m_full_triggered) { + m_full_triggered = true; + m_on_fifo_full_post_sync(); + } + } + break; + } + + case T_EMPTY: { + // Sync was called for a fifo empty, is it still empty? + if(is_empty()) { + // If yes, stop the destination if not done yet + if(!m_empty_triggered) { + m_empty_triggered = true; + m_on_fifo_empty_post_sync(); + } + } + break; + } + } +} + +template<typename T> T generic_fifo_device_base<T>::pop() +{ + // Are we empty? + if(is_empty()) { + // First, trigger the sync + m_sync_empty->adjust(attotime::zero); + // Then fire the callback + m_on_fifo_empty_pre_sync(); + + return T(); + + } else { + // Prepare the value + bool was_full = is_full(); + T t(std::move(m_values.front())); + m_values.erase(m_values.begin()); + + // If we have extra values and the source is stopped, + // push one in, and possibly restart the source. + if(!m_extra_values.empty()) { + T t1(std::move(m_extra_values.front())); + m_extra_values.erase(m_extra_values.begin()); + m_values.emplace_back(std::move(t1)); + + if(m_extra_values.empty()) { + // We don't have any extra values left, we can unblock + // the source. + if(m_full_triggered) { + m_full_triggered = false; + m_on_fifo_unfull(); + } + } + } + + // Update the devcb lines + if(is_empty()) + m_empty_cb(true); + if(was_full && !is_full()) + m_full_cb(false); + + // We did a pop + m_on_pop(); + return std::move(t); + } +} + +template<typename T> void generic_fifo_device_base<T>::push(T &&t) +{ + // Are we already overflowed? + if(!m_extra_values.empty()) + // If yes, just add to the overflow queue + m_extra_values.emplace_back(std::move(t)); + + else if(is_full()) { + // Otherwise, are we full? + + // If yes start the overflow queue + m_extra_values.emplace_back(std::move(t)); + + // And trigger the sync + m_sync_full->adjust(attotime::zero); + + } else { + // Add the value to the fifo + bool was_empty = is_empty(); + m_values.emplace_back(std::move(t)); + + // Update the devcb lines + if(was_empty) + m_empty_cb(false); + if(is_full()) + m_full_cb(true); + + // Unblock the destination if needed + if(m_empty_triggered) { + m_empty_triggered = false; + m_on_fifo_unempty(); + } + } + + // We did a push + m_on_push(); +} + +template class generic_fifo_device_base<u32>; + +generic_fifo_u32_device::generic_fifo_u32_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + generic_fifo_device_base<u32>(mconfig, GENERIC_FIFO_U32, tag, owner, clock) +{ +} + +DEFINE_DEVICE_TYPE(GENERIC_FIFO_U32, generic_fifo_u32_device, "generic_fifo_u32_device", "Generic fifo, u32 values") + |