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