summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/gen_fifo.cpp
blob: bcbd3f54201a986fb0c8fb38cdcc4dc982588907 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// 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_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())
			m_empty_cb(false);
		if(!was_full && is_full())
			m_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")