]> Shamusworld >> Repos - virtualjaguar/blob - src/clock.cpp
Misc. small fixes/changes.
[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 OPCallback(void)
180 {
181     DoFunkyOPStuffHere();
182
183     SetCallbackTime(OPCallback, HORIZ_PERIOD_IN_USEC);
184 }
185
186 void VICallback(void)
187 {
188     double oneFrameInUsec = 16666.66666666;
189     SetCallbackTime(VICallback, oneFrameInUsec / numberOfLines);
190 }
191
192 void JaguarInit(void)
193 {
194     double oneFrameInUsec = 16666.66666666;
195     SetCallbackTime(VICallback, oneFrameInUsec / numberOfLines);
196     SetCallbackTime(OPCallback, );
197 }
198
199 void JaguarExec(void)
200 {
201     while (true)
202     {
203         double timeToNextEvent = GetTimeToNextEvent();
204
205         m68k_execute(USEC_TO_M68K_CYCLES(timeToNextEvent));
206         GPUExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
207         DSPExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
208
209         if (!HandleNextEvent())
210             break;
211     }
212 }
213
214 // NOTES: The timers count RISC cycles, and when the dividers count down to zero they can interrupt either the DSP and/or CPU.
215
216 // NEW:
217 // TOM Programmable Interrupt Timer handler
218 // NOTE: TOM's PIT is only enabled if the prescaler is != 0
219 //       The PIT only generates an interrupt when it counts down to zero, not when loaded!
220
221 void TOMResetPIT()
222 {
223     // Need to remove previous timer from the queue, if it exists...
224     RemoveCallback(TOMPITCallback);
225
226     if (TOMPITPrescaler)
227     {
228         double usecs = (TOMPITPrescaler + 1) * (TOMPITDivider + 1) * RISC_CYCLE_IN_USEC;
229         SetCallbackTime(TOMPITCallback, usecs);
230     }
231 }
232
233 void TOMPITCallback(void)
234 {
235     INT1_RREG |= 0x08;                         // Set TOM PIT interrupt pending
236     GPUSetIRQLine(GPUIRQ_TIMER, ASSERT_LINE);  // It does the 'IRQ enabled' checking
237
238     if (INT1_WREG & 0x08)
239         m68k_set_irq(7);                       // Generate 68K NMI
240
241     TOMResetPIT();
242 }
243
244 // Small problem with this approach: If a timer interrupt is already pending,
245 // the pending timer needs to be replaced with the new one! (Taken care of above, BTW...)
246
247 TOMWriteWord(uint32 address, uint16 data)
248 {
249     if (address == PIT0)
250     {
251         TOMPITPrescaler = data;
252         TOMResetPIT();
253     }
254     else if (address == PIT1)
255     {
256         TOMPITDivider = data;
257         TOMResetPIT();
258     }
259 }
260
261 */