1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#ifndef TIA_DELAY_QUEUE
19#define TIA_DELAY_QUEUE
20
21#include "Serializable.hxx"
22#include "bspf.hxx"
23#include "smartmod.hxx"
24#include "DelayQueueMember.hxx"
25
26template<unsigned length, unsigned capacity>
27class DelayQueueIteratorImpl;
28
29template<unsigned length, unsigned capacity>
30class DelayQueue : public Serializable
31{
32 public:
33 friend DelayQueueIteratorImpl<length, capacity>;
34
35 public:
36 DelayQueue();
37
38 public:
39
40 void push(uInt8 address, uInt8 value, uInt8 delay);
41
42 void reset();
43
44 template<class T> void execute(T executor);
45
46 /**
47 Serializable methods (see that class for more information).
48 */
49 bool save(Serializer& out) const override;
50 bool load(Serializer& in) override;
51
52 private:
53 DelayQueueMember<capacity> myMembers[length];
54 uInt8 myIndex;
55 std::array<uInt8, 0xFF> myIndices;
56
57 private:
58 DelayQueue(const DelayQueue&) = delete;
59 DelayQueue(DelayQueue&&) = delete;
60 DelayQueue& operator=(const DelayQueue&) = delete;
61 DelayQueue& operator=(DelayQueue&&) = delete;
62};
63
64// ############################################################################
65// Implementation
66// ############################################################################
67
68// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
69template<unsigned length, unsigned capacity>
70DelayQueue<length, capacity>::DelayQueue()
71 : myIndex(0)
72{
73 myIndices.fill(0xFF);
74}
75
76// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
77template<unsigned length, unsigned capacity>
78void DelayQueue<length, capacity>::push(uInt8 address, uInt8 value, uInt8 delay)
79{
80 if (delay >= length)
81 throw runtime_error("delay exceeds queue length");
82
83 uInt8 currentIndex = myIndices[address];
84
85 if (currentIndex < length)
86 myMembers[currentIndex].remove(address);
87
88 uInt8 index = smartmod<length>(myIndex + delay);
89 myMembers[index].push(address, value);
90
91 myIndices[address] = index;
92}
93
94// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
95template<unsigned length, unsigned capacity>
96void DelayQueue<length, capacity>::reset()
97{
98 for (uInt8 i = 0; i < length; ++i)
99 myMembers[i].clear();
100
101 myIndex = 0;
102 myIndices.fill(0xFF);
103}
104
105// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
106template<unsigned length, unsigned capacity>
107template<class T>
108void DelayQueue<length, capacity>::execute(T executor)
109{
110 DelayQueueMember<capacity>& currentMember = myMembers[myIndex];
111
112 for (uInt8 i = 0; i < currentMember.mySize; ++i) {
113 executor(currentMember.myEntries[i].address, currentMember.myEntries[i].value);
114 myIndices[currentMember.myEntries[i].address] = 0xFF;
115 }
116
117 currentMember.clear();
118
119 myIndex = smartmod<length>(myIndex + 1);
120}
121
122// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
123template<unsigned length, unsigned capacity>
124bool DelayQueue<length, capacity>::save(Serializer& out) const
125{
126 try
127 {
128 out.putInt(length);
129
130 for (uInt8 i = 0; i < length; ++i)
131 myMembers[i].save(out);
132
133 out.putByte(myIndex);
134 out.putByteArray(myIndices.data(), myIndices.size());
135 }
136 catch(...)
137 {
138 cerr << "ERROR: TIA_DelayQueue::save" << endl;
139 return false;
140 }
141
142 return true;
143}
144
145// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
146template<unsigned length, unsigned capacity>
147bool DelayQueue<length, capacity>::load(Serializer& in)
148{
149 try
150 {
151 if (in.getInt() != length) throw runtime_error("delay queue length mismatch");
152
153 for (uInt8 i = 0; i < length; ++i)
154 myMembers[i].load(in);
155
156 myIndex = in.getByte();
157 in.getByteArray(myIndices.data(), myIndices.size());
158 }
159 catch(...)
160 {
161 cerr << "ERROR: TIA_DelayQueue::load" << endl;
162 return false;
163 }
164
165 return true;
166}
167
168#endif // TIA_DELAY_QUEUE
169