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