]> Shamusworld >> Repos - virtualjaguar/blob - src/event.cpp
Merging qt-experimental into trunk.
[virtualjaguar] / src / event.cpp
1 //
2 // System time handlers
3 //
4 // by James L. Hammons
5 // (C) 2010 Underground Software
6 //
7 // JLH = James L. 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 "log.h"
23
24 #define EVENT_LIST_SIZE       512
25
26 /*
27 // Current execution path:
28
29 do
30 {
31         HandleJoystick
32
33         // Execute one frame
34
35     for(int i=0; i<numLines; i++)
36     {
37                 ExecuteM68K
38                 ExecutePITs
39                 ExecuteGPU
40                 ExecuteDSP
41                 // Render Scanline
42                 CallObjectProcessor
43                 MoveLineBufToBackBuf
44     }
45
46         RenderBackbuffer
47 }
48 while (true)
49
50 // What we need to do:
51
52 Set up timers (frame [for native render--on vblank interval?], OP line, PITs)
53
54 do
55 {
56         double timeToNextEvent = GetTimeToNextEvent();
57
58         m68k_execute(USEC_TO_M68K_CYCLES(timeToNextEvent));
59         GPUExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
60         DSPExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
61
62         HandleNextEvent();
63 }
64 while (true)
65
66
67 */
68
69 // Now, a bit of weirdness: It seems that the number of lines displayed on the screen
70 // makes the effective refresh rate either 30 or 25 Hz!
71
72 // NOTE ABOUT TIMING SYSTEM DATA STRUCTURES:
73
74 // A queue won't work for this system because we can't guarantee that an event will go
75 // in with a time that is later than the ones already queued up. So we just use a simple
76 // list.
77
78 // Although if we used an insertion sort we could, but it wouldn't work for adjusting
79 // times...
80
81 struct Event
82 {
83     bool valid;
84     double eventTime;
85     void (* timerCallback)(void);
86 };
87
88 Event eventList[EVENT_LIST_SIZE];
89 uint32 nextEvent;
90
91 void InitializeEventList(void)
92 {
93     for(uint32 i=0; i<EVENT_LIST_SIZE; i++)
94         eventList[i].valid = false;
95 }
96
97 //We just slap the next event into the list, no checking, no nada...
98 void SetCallbackTime(void (* callback)(void), double time)
99 {
100     for(uint32 i=0; i<EVENT_LIST_SIZE; i++)
101     {
102         if (!eventList[i].valid)
103         {
104 //WriteLog("SCT: Found callback slot #%u...\n", i);
105             eventList[i].timerCallback = callback;
106             eventList[i].eventTime = time;
107             eventList[i].valid = true;
108
109             return;
110         }
111     }
112
113     WriteLog("SetCallbackTime() failed to find an empty slot in the list!\n");
114 }
115
116 void RemoveCallback(void (* callback)(void))
117 {
118     for(uint32 i=0; i<EVENT_LIST_SIZE; i++)
119     {
120         if (eventList[i].valid && eventList[i].timerCallback == callback)
121         {
122             eventList[i].valid = false;
123
124             return;
125         }
126     }
127 }
128
129 void AdjustCallbackTime(void (* callback)(void), double time)
130 {
131     for(uint32 i=0; i<EVENT_LIST_SIZE; i++)
132     {
133         if (eventList[i].valid && eventList[i].timerCallback == callback)
134         {
135             eventList[i].eventTime = time;
136
137             return;
138         }
139     }
140 }
141
142 double GetTimeToNextEvent(void)
143 {
144     double time = 0;
145     bool firstTime = true;
146
147     for(uint32 i=0; i<EVENT_LIST_SIZE; i++)
148     {
149         if (eventList[i].valid)
150         {
151             if (firstTime)
152                 time = eventList[i].eventTime, nextEvent = i, firstTime = false;
153             else
154             {
155                 if (eventList[i].eventTime < time)
156                     time = eventList[i].eventTime, nextEvent = i;
157             }
158         }
159     }
160
161     return time;
162 }
163
164 void HandleNextEvent(void)
165 {
166     double elapsedTime = eventList[nextEvent].eventTime;
167     void (* event)(void) = eventList[nextEvent].timerCallback;
168
169     for(uint32 i=0; i<EVENT_LIST_SIZE; i++)
170         if (eventList[i].valid)
171             eventList[i].eventTime -= elapsedTime;
172
173     eventList[nextEvent].valid = false;      // Remove event from list...
174
175     (*event)();
176 }
177
178
179 /*
180 void OPCallback(void)
181 {
182     DoFunkyOPStuffHere();
183
184     SetCallbackTime(OPCallback, HORIZ_PERIOD_IN_USEC);
185 }
186
187 void VICallback(void)
188 {
189     double oneFrameInUsec = 16666.66666666;
190     SetCallbackTime(VICallback, oneFrameInUsec / numberOfLines);
191 }
192
193 void JaguarInit(void)
194 {
195     double oneFrameInUsec = 16666.66666666;
196     SetCallbackTime(VICallback, oneFrameInUsec / numberOfLines);
197     SetCallbackTime(OPCallback, );
198 }
199
200 void JaguarExec(void)
201 {
202     while (true)
203     {
204         double timeToNextEvent = GetTimeToNextEvent();
205
206         m68k_execute(USEC_TO_M68K_CYCLES(timeToNextEvent));
207         GPUExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
208         DSPExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
209
210         if (!HandleNextEvent())
211             break;
212     }
213 }
214
215 // NOTES: The timers count RISC cycles, and when the dividers count down to zero they can interrupt either the DSP and/or CPU.
216
217 // NEW:
218 // TOM Programmable Interrupt Timer handler
219 // NOTE: TOM's PIT is only enabled if the prescaler is != 0
220 //       The PIT only generates an interrupt when it counts down to zero, not when loaded!
221
222 void TOMResetPIT()
223 {
224     // Need to remove previous timer from the queue, if it exists...
225     RemoveCallback(TOMPITCallback);
226
227     if (TOMPITPrescaler)
228     {
229         double usecs = (TOMPITPrescaler + 1) * (TOMPITDivider + 1) * RISC_CYCLE_IN_USEC;
230         SetCallbackTime(TOMPITCallback, usecs);
231     }
232 }
233
234 void TOMPITCallback(void)
235 {
236     INT1_RREG |= 0x08;                         // Set TOM PIT interrupt pending
237     GPUSetIRQLine(GPUIRQ_TIMER, ASSERT_LINE);  // It does the 'IRQ enabled' checking
238
239     if (INT1_WREG & 0x08)
240         m68k_set_irq(7);                       // Generate 68K NMI
241
242     TOMResetPIT();
243 }
244
245 // Small problem with this approach: If a timer interrupt is already pending,
246 // the pending timer needs to be replaced with the new one! (Taken care of above, BTW...)
247
248 TOMWriteWord(uint32 address, uint16 data)
249 {
250     if (address == PIT0)
251     {
252         TOMPITPrescaler = data;
253         TOMResetPIT();
254     }
255     else if (address == PIT1)
256     {
257         TOMPITDivider = data;
258         TOMResetPIT();
259     }
260 }
261
262 */