|
|
1.1 root 1: /*
2: Hatari - cycInt.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: This code handles our table with callbacks for cycle accurate program
8: interruption. We add any pending callback handler into a table so that we do
9: not need to test for every possible interrupt event. We then scan
10: the list if used entries in the table and copy the one with the least cycle
11: count into the global 'PendingInterruptCount' variable. This is then
12: decremented by the execution loop - rather than decrement each and every
13: entry (as the others cannot occur before this one).
14: We have two methods of adding interrupts; Absolute and Relative.
15: Absolute will set values from the time of the previous interrupt (e.g., add
16: HBL every 512 cycles), and Relative will add from the current cycle time.
17: Note that interrupt may occur 'late'. I.e., if an interrupt is due in 4
18: cycles time but the current instruction takes 20 cycles we will be 16 cycles
19: late - this is handled in the adjust functions.
20:
21: In order to handle both CPU and MFP interrupt events, we don't convert MFP
22: cyles to CPU cycles, because it requires some floating points approximation
23: and accumulates some errors that could lead to bad results.
24: Instead, CPU and MFP cycles are converted to 'internal' cycles with the
25: following rule :
26: - 1 CPU cycle gives 9600 internal cycles
27: - 1 MFP cycle gives 31333 internal cycle
28:
29: All interrupt events are then handled in the 'internal' units and are
30: converted back to cpu or mfp units when needed. This allows very good
31: synchronisation between CPU and MFP, without the rounding errors of floating
32: points math.
33:
34: Thanks to Arnaud Carre (Leonard / Oxygene) for sharing this method used in
35: Saint (and also used in sc68).
36:
37: Conversions are based on these values :
38: real MFP frequency is 2457600 Hz
39: real CPU frequency is 8021247 Hz (PAL european STF), which we round to 8021248.
40:
41: Then :
42: 8021248 = ( 2^8 * 31333 )
43: 2457600 = ( 2^15 * 3 * 5^2 )
44:
45: So, the ratio 8021248 / 2457600 can be expressed as 31333 / 9600
46: */
47:
48: const char CycInt_fileid[] = "Hatari cycInt.c : " __DATE__ " " __TIME__;
49:
50: #include <stdint.h>
51: #include <assert.h>
52: #include "main.h"
53: #include "cycInt.h"
54: #include "m68000.h"
55: #include "memorySnapShot.h"
56: #include "video.h"
57:
58:
59: void (*PendingInterruptFunction)(void);
60: int PendingInterruptCount;
61:
62: static int nCyclesOver;
63:
64: /* List of possible interrupt handlers to be store in 'PendingInterruptTable',
65: * used for 'MemorySnapShot' */
66: static void (* const pIntHandlerFunctions[MAX_INTERRUPTS])(void) =
67: {
68: NULL,
69: Video_InterruptHandler_VBL
70: };
71:
72: /* Event timer structure - keeps next timer to occur in structure so don't need
73: * to check all entries */
74: typedef struct
75: {
76: bool bUsed; /* Is interrupt active? */
77: Sint64 Cycles;
78: void (*pFunction)(void);
79: } INTERRUPTHANDLER;
80:
81: static INTERRUPTHANDLER InterruptHandlers[MAX_INTERRUPTS];
82: static int ActiveInterrupt=0;
83:
84: static void CycInt_SetNewInterrupt(void);
85:
86: /*-----------------------------------------------------------------------*/
87: /**
88: * Reset interrupts, handlers
89: */
90: void CycInt_Reset(void)
91: {
92: int i;
93:
94: /* Reset counts */
95: PendingInterruptCount = 0;
96: ActiveInterrupt = 0;
97: nCyclesOver = 0;
98:
99: /* Reset interrupt table */
100: for (i=0; i<MAX_INTERRUPTS; i++)
101: {
102: InterruptHandlers[i].bUsed = false;
103: InterruptHandlers[i].Cycles = INT_MAX;
104: InterruptHandlers[i].pFunction = pIntHandlerFunctions[i];
105: }
106: }
107:
108:
109: /*-----------------------------------------------------------------------*/
110: /**
111: * Convert interrupt handler function pointer to ID, used for saving
112: */
113: static int CycInt_HandlerFunctionToID(void (*pHandlerFunction)(void))
114: {
115: int i;
116:
117: /* Scan for function match */
118: for (i=0; i<MAX_INTERRUPTS; i++)
119: {
120: if (pIntHandlerFunctions[i]==pHandlerFunction)
121: return i;
122: }
123:
124: /* Didn't find one! Oops */
125: fprintf(stderr, "\nError: didn't find interrupt function matching 0x%p\n",
126: pHandlerFunction);
127: return 0;
128: }
129:
130:
131: /*-----------------------------------------------------------------------*/
132: /**
133: * Convert ID back into interrupt handler function, used for restoring
134: */
135: static void *CycInt_IDToHandlerFunction(int ID)
136: {
137: /* Get function pointer */
138: return pIntHandlerFunctions[ID];
139: }
140:
141:
142: /*-----------------------------------------------------------------------*/
143: /**
144: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
145: */
146: void CycInt_MemorySnapShot_Capture(bool bSave)
147: {
148: int i,ID;
149:
150: /* Save/Restore details */
151: for (i=0; i<MAX_INTERRUPTS; i++)
152: {
153: MemorySnapShot_Store(&InterruptHandlers[i].bUsed, sizeof(InterruptHandlers[i].bUsed));
154: MemorySnapShot_Store(&InterruptHandlers[i].Cycles, sizeof(InterruptHandlers[i].Cycles));
155: if (bSave)
156: {
157: /* Convert function to ID */
158: ID = CycInt_HandlerFunctionToID(InterruptHandlers[i].pFunction);
159: MemorySnapShot_Store(&ID, sizeof(int));
160: }
161: else
162: {
163: /* Convert ID to function */
164: MemorySnapShot_Store(&ID, sizeof(int));
165: InterruptHandlers[i].pFunction = CycInt_IDToHandlerFunction(ID);
166: }
167: }
168: MemorySnapShot_Store(&nCyclesOver, sizeof(nCyclesOver));
169: MemorySnapShot_Store(&PendingInterruptCount, sizeof(PendingInterruptCount));
170: if (bSave)
171: {
172: /* Convert function to ID */
173: ID = CycInt_HandlerFunctionToID(PendingInterruptFunction);
174: MemorySnapShot_Store(&ID, sizeof(int));
175: }
176: else
177: {
178: /* Convert ID to function */
179: MemorySnapShot_Store(&ID, sizeof(int));
180: PendingInterruptFunction = CycInt_IDToHandlerFunction(ID);
181: }
182:
183:
184: if (!bSave)
185: CycInt_SetNewInterrupt(); /* when restoring snapshot, compute current state after */
186: }
187:
188:
189: /*-----------------------------------------------------------------------*/
190: /**
191: * Find next interrupt to occur, and store to global variables for decrement
192: * in instruction decode loop.
193: * Note: Although InterruptHandlers.Cycles and LowestCycleCount are 64 bit
194: * variables to get all the cycle counters right (e.g. the DMA sound counter
195: * can get very high), PendingInterruptCount is still a 32 bit variable for
196: * performance reasons (it's decremented after each CPU instruction).
197: * So we have to initialize LowestCycleCount with INT_MAX, not with INT64_MAX!
198: * Since there is always a VBL or HBL counter pending which fits fine into the
199: * 32 bit variable, we can be sure that we don't run into problems here.
200: */
201: static void CycInt_SetNewInterrupt(void)
202: {
203: Sint64 LowestCycleCount = INT_MAX;
204: interrupt_id LowestInterrupt = INTERRUPT_NULL, i;
205:
206: LOG_TRACE(TRACE_INT, "int set new in video_cyc=%d active_int=%d pending_count=%d\n",
207: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount);
208:
209: /* Find next interrupt to go off */
210: for (i = INTERRUPT_NULL+1; i < MAX_INTERRUPTS; i++)
211: {
212: /* Is interrupt pending? */
213: if (InterruptHandlers[i].bUsed)
214: {
215: if (InterruptHandlers[i].Cycles < LowestCycleCount)
216: {
217: LowestCycleCount = InterruptHandlers[i].Cycles;
218: LowestInterrupt = i;
219: }
220: }
221: }
222:
223: /* Set new counts, active interrupt */
224: PendingInterruptCount = InterruptHandlers[LowestInterrupt].Cycles;
225: PendingInterruptFunction = InterruptHandlers[LowestInterrupt].pFunction;
226: ActiveInterrupt = LowestInterrupt;
227:
228: LOG_TRACE(TRACE_INT, "int set new out video_cyc=%d active_int=%d pending_count=%d\n",
229: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount );
230: }
231:
232:
233: /*-----------------------------------------------------------------------*/
234: /**
235: * Adjust all interrupt timings, MUST call CycInt_SetNewInterrupt after this.
236: */
237: static void CycInt_UpdateInterrupt(void)
238: {
239: Sint64 CycleSubtract;
240: int i;
241:
242: /* Find out how many cycles we went over (<=0) */
243: nCyclesOver = PendingInterruptCount;
244: /* Calculate how many cycles have passed, included time we went over */
245: CycleSubtract = InterruptHandlers[ActiveInterrupt].Cycles - nCyclesOver;
246:
247: /* Adjust table */
248: for (i = 0; i < MAX_INTERRUPTS; i++)
249: {
250: if (InterruptHandlers[i].bUsed)
251: InterruptHandlers[i].Cycles -= CycleSubtract;
252: }
253:
254: LOG_TRACE(TRACE_INT, "int upd video_cyc=%d cycle_over=%d cycle_sub=%lld\n",
255: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), nCyclesOver,
256: (long long)CycleSubtract);
257: }
258:
259:
260: /*-----------------------------------------------------------------------*/
261: /**
262: * Adjust all interrupt timings as 'ActiveInterrupt' has occured, and
263: * remove from active list.
264: */
265: void CycInt_AcknowledgeInterrupt(void)
266: {
267: /* Update list cycle counts */
268: CycInt_UpdateInterrupt();
269:
270: /* Disable interrupt entry which has just occured */
271: InterruptHandlers[ActiveInterrupt].bUsed = false;
272:
273: /* Set new */
274: CycInt_SetNewInterrupt();
275:
276: LOG_TRACE(TRACE_INT, "int ack video_cyc=%d active_int=%d active_cyc=%d pending_count=%d\n",
277: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, (int)InterruptHandlers[ActiveInterrupt].Cycles, PendingInterruptCount );
278: }
279:
280:
281: /*-----------------------------------------------------------------------*/
282: /**
283: * Add interrupt from time last one occurred.
284: */
285: void CycInt_AddAbsoluteInterrupt(int CycleTime, int CycleType, interrupt_id Handler)
286: {
287: assert(CycleTime >= 0);
288:
289: /* Update list cycle counts before adding a new one, */
290: /* since CycInt_SetNewInterrupt can change the active int / PendingInterruptCount */
291: if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) )
292: CycInt_UpdateInterrupt();
293:
294: InterruptHandlers[Handler].bUsed = true;
295: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + nCyclesOver;
296:
297: /* Set new */
298: CycInt_SetNewInterrupt();
299:
300: LOG_TRACE(TRACE_INT, "int add abs video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
301: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
302: (long long)InterruptHandlers[Handler].Cycles, PendingInterruptCount );
303: }
304:
305:
306: /*-----------------------------------------------------------------------*/
307: /**
308: * Add interrupt to occur from now.
309: */
310: void CycInt_AddRelativeInterrupt(int CycleTime, int CycleType, interrupt_id Handler)
311: {
312: CycInt_AddRelativeInterruptWithOffset(CycleTime, CycleType, Handler, 0);
313: }
314:
315:
316:
317:
318: /*-----------------------------------------------------------------------*/
319: /**
320: * Add interrupt to occur after CycleTime/CycleType + CycleOffset.
321: * CycleOffset can be used to add another delay to the resulting
322: * number of internal cycles (should be 0 most of the time, except in
323: * the MFP emulation to start timers precisely based on the number of
324: * cycles of the current instruction).
325: * This allows to restart an MFP timer just after it expired.
326: */
327: void CycInt_AddRelativeInterruptWithOffset(int CycleTime, int CycleType, interrupt_id Handler, int CycleOffset)
328: {
329: assert(CycleTime >= 0);
330:
331: /* Update list cycle counts before adding a new one, */
332: /* since CycInt_SetNewInterrupt can change the active int / PendingInterruptCount */
333: if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) )
334: CycInt_UpdateInterrupt();
335:
336: InterruptHandlers[Handler].bUsed = true;
337: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + CycleOffset;
338:
339: /* Set new */
340: CycInt_SetNewInterrupt();
341:
342: LOG_TRACE(TRACE_INT, "int add rel offset video_cyc=%d handler=%d handler_cyc=%lld offset_cyc=%d pending_count=%d\n",
343: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
344: (long long)InterruptHandlers[Handler].Cycles, CycleOffset, PendingInterruptCount);
345: }
346:
347:
348: /*-----------------------------------------------------------------------*/
349: /**
350: * Remove a pending interrupt from our table
351: */
352: void CycInt_RemovePendingInterrupt(interrupt_id Handler)
353: {
354: /* Update list cycle counts, including the handler we want to remove */
355: /* to be able to resume it later (for MFP timers) */
356: CycInt_UpdateInterrupt();
357:
358: /* Stop interrupt after CycInt_UpdateInterrupt, for CycInt_ResumeStoppedInterrupt */
359: InterruptHandlers[Handler].bUsed = false;
360:
361: /* Set new */
362: CycInt_SetNewInterrupt();
363:
364: LOG_TRACE(TRACE_INT, "int remove pending video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
365: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
366: (long long)InterruptHandlers[Handler].Cycles, PendingInterruptCount);
367: }
368:
369:
370: /*-----------------------------------------------------------------------*/
371: /**
372: * Resume a stopped interrupt from its current cycle count (for MFP timers)
373: */
374: void CycInt_ResumeStoppedInterrupt(interrupt_id Handler)
375: {
376: /* Restart interrupt */
377: InterruptHandlers[Handler].bUsed = true;
378:
379: /* Update list cycle counts */
380: CycInt_UpdateInterrupt();
381: /* Set new */
382: CycInt_SetNewInterrupt();
383:
384: LOG_TRACE(TRACE_INT, "int resume stopped video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
385: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
386: (long long)InterruptHandlers[Handler].Cycles, PendingInterruptCount);
387: }
388:
389:
390: /*-----------------------------------------------------------------------*/
391: /**
392: * Return true if interrupt is active in list
393: */
394: bool CycInt_InterruptActive(interrupt_id Handler)
395: {
396: /* Is timer active? */
397: if (InterruptHandlers[Handler].bUsed)
398: return true;
399:
400: return false;
401: }
402:
403:
404: /*-----------------------------------------------------------------------*/
405: /**
406: * Return cycles passed for an interrupt handler
407: */
408: int CycInt_FindCyclesPassed(interrupt_id Handler, int CycleType)
409: {
410: Sint64 CyclesPassed, CyclesFromLastInterrupt;
411:
412: CyclesFromLastInterrupt = InterruptHandlers[ActiveInterrupt].Cycles - PendingInterruptCount;
413: CyclesPassed = InterruptHandlers[Handler].Cycles - CyclesFromLastInterrupt;
414:
415: LOG_TRACE(TRACE_INT, "int find passed cyc video_cyc=%d handler=%d last_cyc=%lld passed_cyc=%lld\n",
416: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
417: (long long)CyclesFromLastInterrupt, (long long)CyclesPassed);
418:
419: return INT_CONVERT_FROM_INTERNAL ( CyclesPassed , CycleType ) ;
420: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.