* Added a Keybindings dialog tabs.
[clinton/Virtual-Jaguar-Rx.git] / src / event.cpp
1 //
2 // System time handlers
3 //
4 // by James Hammons
5 // (C) 2010 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // Who When What
10 // --- ---------- -------------------------------------------------------------
11 // JLH 01/16/2010 Created this log ;-)
12 //
13
14 //
15 // STILL TO DO:
16 //
17 // - Handling for an event that occurs NOW
18 //
19
20 #include "event.h"
21
22 #include <stdint.h>
23 #include "log.h"
24
25
26 //#define EVENT_LIST_SIZE 512
27 #define EVENT_LIST_SIZE 32
28
29
30 // Now, a bit of weirdness: It seems that the number of lines displayed on the screen
31 // makes the effective refresh rate either 30 or 25 Hz!
32
33 // NOTE ABOUT TIMING SYSTEM DATA STRUCTURES:
34
35 // A queue won't work for this system because we can't guarantee that an event will go
36 // in with a time that is later than the ones already queued up. So we just use a simple
37 // list.
38
39 // Although if we used an insertion sort we could, but it wouldn't work for adjusting
40 // times... (For that, you would have to remove the event then reinsert it.)
41
42 struct Event
43 {
44 bool valid;
45 int eventType;
46 double eventTime;
47 void (* timerCallback)(void);
48 };
49
50
51 static Event eventList[EVENT_LIST_SIZE];
52 static Event eventListJERRY[EVENT_LIST_SIZE];
53 static uint32_t nextEvent;
54 static uint32_t nextEventJERRY;
55 static uint32_t numberOfEvents;
56
57
58 void InitializeEventList(void)
59 {
60 for(uint32_t i=0; i<EVENT_LIST_SIZE; i++)
61 {
62 eventList[i].valid = false;
63 eventListJERRY[i].valid = false;
64 }
65
66 numberOfEvents = 0;
67 WriteLog("EVENT: Cleared event list.\n");
68 }
69
70
71 // Set callback time in µs. This is fairly arbitrary, but works well enough for our purposes.
72 //We just slap the next event into the list in the first available slot, no checking, no nada...
73 void SetCallbackTime(void (* callback)(void), double time, int type/*= EVENT_MAIN*/)
74 {
75 if (type == EVENT_MAIN)
76 {
77 for(uint32_t i=0; i<EVENT_LIST_SIZE; i++)
78 {
79 if (!eventList[i].valid)
80 {
81 //WriteLog("EVENT: Found callback slot #%u...\n", i);
82 eventList[i].timerCallback = callback;
83 eventList[i].eventTime = time;
84 eventList[i].eventType = type;
85 eventList[i].valid = true;
86 numberOfEvents++;
87
88 return;
89 }
90 }
91
92 WriteLog("EVENT: SetCallbackTime() failed to find an empty slot in the main list (%u events)!\n", numberOfEvents);
93 }
94 else
95 {
96 for(uint32_t i=0; i<EVENT_LIST_SIZE; i++)
97 {
98 if (!eventListJERRY[i].valid)
99 {
100 //WriteLog("EVENT: Found callback slot #%u...\n", i);
101 eventListJERRY[i].timerCallback = callback;
102 eventListJERRY[i].eventTime = time;
103 eventListJERRY[i].eventType = type;
104 eventListJERRY[i].valid = true;
105 numberOfEvents++;
106
107 return;
108 }
109 }
110
111 WriteLog("EVENT: SetCallbackTime() failed to find an empty slot in the main list (%u events)!\n", numberOfEvents);
112 }
113 }
114
115
116 void RemoveCallback(void (* callback)(void))
117 {
118 for(uint32_t i=0; i<EVENT_LIST_SIZE; i++)
119 {
120 if (eventList[i].valid && eventList[i].timerCallback == callback)
121 {
122 eventList[i].valid = false;
123 numberOfEvents--;
124
125 return;
126 }
127 else if (eventListJERRY[i].valid && eventListJERRY[i].timerCallback == callback)
128 {
129 eventListJERRY[i].valid = false;
130 numberOfEvents--;
131
132 return;
133 }
134 }
135 }
136
137
138 void AdjustCallbackTime(void (* callback)(void), double time)
139 {
140 for(uint32_t i=0; i<EVENT_LIST_SIZE; i++)
141 {
142 if (eventList[i].valid && eventList[i].timerCallback == callback)
143 {
144 eventList[i].eventTime = time;
145
146 return;
147 }
148 else if (eventListJERRY[i].valid && eventListJERRY[i].timerCallback == callback)
149 {
150 eventListJERRY[i].eventTime = time;
151
152 return;
153 }
154 }
155 }
156
157
158 //
159 // Since our list is unordered WRT time, we have to search it to find the next event
160 // Returns time to next event & sets nextEvent to that event
161 //
162 double GetTimeToNextEvent(int type/*= EVENT_MAIN*/)
163 {
164 #if 0
165 double time = 0;
166 bool firstTime = true;
167
168 for(uint32 i=0; i<EVENT_LIST_SIZE; i++)
169 {
170 if (eventList[i].valid)
171 {
172 if (firstTime)
173 time = eventList[i].eventTime, nextEvent = i, firstTime = false;
174 else
175 {
176 if (eventList[i].eventTime < time)
177 time = eventList[i].eventTime, nextEvent = i;
178 }
179 }
180 }
181 #else
182 if (type == EVENT_MAIN)
183 {
184 double time = eventList[0].eventTime;
185 nextEvent = 0;
186
187 for(uint32_t i=1; i<EVENT_LIST_SIZE; i++)
188 {
189 if (eventList[i].valid && (eventList[i].eventTime < time))
190 {
191 time = eventList[i].eventTime;
192 nextEvent = i;
193 }
194 }
195
196 return time;
197 }
198 else
199 {
200 double time = eventListJERRY[0].eventTime;
201 nextEventJERRY = 0;
202
203 for(uint32_t i=1; i<EVENT_LIST_SIZE; i++)
204 {
205 if (eventListJERRY[i].valid && (eventListJERRY[i].eventTime < time))
206 {
207 time = eventListJERRY[i].eventTime;
208 nextEventJERRY = i;
209 }
210 }
211
212 return time;
213 }
214 #endif
215 }
216
217
218 void HandleNextEvent(int type/*= EVENT_MAIN*/)
219 {
220 if (type == EVENT_MAIN)
221 {
222 double elapsedTime = eventList[nextEvent].eventTime;
223 void (* event)(void) = eventList[nextEvent].timerCallback;
224
225 for(uint32_t i=0; i<EVENT_LIST_SIZE; i++)
226 {
227 //We can skip the check & just subtract from everything, since the check is probably
228 //just as heavy as the code after and we won't use the elapsed time from an invalid event anyway.
229 // if (eventList[i].valid)
230 eventList[i].eventTime -= elapsedTime;
231 }
232
233 eventList[nextEvent].valid = false; // Remove event from list...
234 numberOfEvents--;
235
236 (*event)();
237 }
238 else
239 {
240 double elapsedTime = eventListJERRY[nextEventJERRY].eventTime;
241 void (* event)(void) = eventListJERRY[nextEventJERRY].timerCallback;
242
243 for(uint32_t i=0; i<EVENT_LIST_SIZE; i++)
244 {
245 //We can skip the check & just subtract from everything, since the check is probably
246 //just as heavy as the code after and we won't use the elapsed time from an invalid event anyway.
247 // if (eventList[i].valid)
248 eventListJERRY[i].eventTime -= elapsedTime;
249 }
250
251 eventListJERRY[nextEventJERRY].valid = false; // Remove event from list...
252 numberOfEvents--;
253
254 (*event)();
255 }
256 }
257
258
259 /*
260 void OPCallback(void)
261 {
262 DoFunkyOPStuffHere();
263
264 SetCallbackTime(OPCallback, HORIZ_PERIOD_IN_USEC);
265 }
266
267 void VICallback(void)
268 {
269 double oneFrameInUsec = 16666.66666666;
270 SetCallbackTime(VICallback, oneFrameInUsec / numberOfLines);
271 }
272
273 void JaguarInit(void)
274 {
275 double oneFrameInUsec = 16666.66666666;
276 SetCallbackTime(VICallback, oneFrameInUsec / numberOfLines);
277 SetCallbackTime(OPCallback, );
278 }
279
280 void JaguarExec(void)
281 {
282 while (true)
283 {
284 double timeToNextEvent = GetTimeToNextEvent();
285
286 m68k_execute(USEC_TO_M68K_CYCLES(timeToNextEvent));
287 GPUExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
288 DSPExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
289
290 if (!HandleNextEvent())
291 break;
292 }
293 }
294
295 // NOTES: The timers count RISC cycles, and when the dividers count down to zero they can interrupt either the DSP and/or CPU.
296
297 // NEW:
298 // TOM Programmable Interrupt Timer handler
299 // NOTE: TOM's PIT is only enabled if the prescaler is != 0
300 // The PIT only generates an interrupt when it counts down to zero, not when loaded!
301
302 void TOMResetPIT()
303 {
304 // Need to remove previous timer from the queue, if it exists...
305 RemoveCallback(TOMPITCallback);
306
307 if (TOMPITPrescaler)
308 {
309 double usecs = (TOMPITPrescaler + 1) * (TOMPITDivider + 1) * RISC_CYCLE_IN_USEC;
310 SetCallbackTime(TOMPITCallback, usecs);
311 }
312 }
313
314 void TOMPITCallback(void)
315 {
316 INT1_RREG |= 0x08; // Set TOM PIT interrupt pending
317 GPUSetIRQLine(GPUIRQ_TIMER, ASSERT_LINE); // It does the 'IRQ enabled' checking
318
319 if (INT1_WREG & 0x08)
320 m68k_set_irq(2); // Generate 68K NMI
321
322 TOMResetPIT();
323 }
324
325 // Small problem with this approach: If a timer interrupt is already pending,
326 // the pending timer needs to be replaced with the new one! (Taken care of above, BTW...)
327
328 TOMWriteWord(uint32 address, uint16 data)
329 {
330 if (address == PIT0)
331 {
332 TOMPITPrescaler = data;
333 TOMResetPIT();
334 }
335 else if (address == PIT1)
336 {
337 TOMPITDivider = data;
338 TOMResetPIT();
339 }
340 }
341
342 */