|
|
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
1.1.1.2 root 22: cycles to CPU cycles, because it requires some floating points approximations
1.1 root 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"
1.1.1.2 root 56: #include "screen.h"
1.1 root 57: #include "video.h"
1.1.1.3 ! root 58: #include "sysReg.h"
1.1 root 59:
60:
61: void (*PendingInterruptFunction)(void);
62: int PendingInterruptCount;
63:
64: static int nCyclesOver;
65:
66: /* List of possible interrupt handlers to be store in 'PendingInterruptTable',
67: * used for 'MemorySnapShot' */
68: static void (* const pIntHandlerFunctions[MAX_INTERRUPTS])(void) =
69: {
70: NULL,
1.1.1.2 root 71: Video_InterruptHandler_VBL,
1.1.1.3 ! root 72: Hardclock_InterruptHandler
1.1 root 73: };
74:
75: /* Event timer structure - keeps next timer to occur in structure so don't need
76: * to check all entries */
77: typedef struct
78: {
79: bool bUsed; /* Is interrupt active? */
80: Sint64 Cycles;
81: void (*pFunction)(void);
82: } INTERRUPTHANDLER;
83:
84: static INTERRUPTHANDLER InterruptHandlers[MAX_INTERRUPTS];
85: static int ActiveInterrupt=0;
86:
87: static void CycInt_SetNewInterrupt(void);
88:
89: /*-----------------------------------------------------------------------*/
90: /**
91: * Reset interrupts, handlers
92: */
93: void CycInt_Reset(void)
94: {
95: int i;
96:
97: /* Reset counts */
98: PendingInterruptCount = 0;
99: ActiveInterrupt = 0;
100: nCyclesOver = 0;
101:
102: /* Reset interrupt table */
103: for (i=0; i<MAX_INTERRUPTS; i++)
104: {
105: InterruptHandlers[i].bUsed = false;
106: InterruptHandlers[i].Cycles = INT_MAX;
107: InterruptHandlers[i].pFunction = pIntHandlerFunctions[i];
108: }
109: }
110:
111:
112: /*-----------------------------------------------------------------------*/
113: /**
114: * Convert interrupt handler function pointer to ID, used for saving
115: */
116: static int CycInt_HandlerFunctionToID(void (*pHandlerFunction)(void))
117: {
118: int i;
119:
120: /* Scan for function match */
121: for (i=0; i<MAX_INTERRUPTS; i++)
122: {
123: if (pIntHandlerFunctions[i]==pHandlerFunction)
124: return i;
125: }
126:
127: /* Didn't find one! Oops */
128: fprintf(stderr, "\nError: didn't find interrupt function matching 0x%p\n",
129: pHandlerFunction);
130: return 0;
131: }
132:
133:
134: /*-----------------------------------------------------------------------*/
135: /**
136: * Convert ID back into interrupt handler function, used for restoring
137: */
138: static void *CycInt_IDToHandlerFunction(int ID)
139: {
140: /* Get function pointer */
141: return pIntHandlerFunctions[ID];
142: }
143:
144:
145: /*-----------------------------------------------------------------------*/
146: /**
147: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
148: */
149: void CycInt_MemorySnapShot_Capture(bool bSave)
150: {
151: int i,ID;
152:
153: /* Save/Restore details */
154: for (i=0; i<MAX_INTERRUPTS; i++)
155: {
156: MemorySnapShot_Store(&InterruptHandlers[i].bUsed, sizeof(InterruptHandlers[i].bUsed));
157: MemorySnapShot_Store(&InterruptHandlers[i].Cycles, sizeof(InterruptHandlers[i].Cycles));
158: if (bSave)
159: {
160: /* Convert function to ID */
161: ID = CycInt_HandlerFunctionToID(InterruptHandlers[i].pFunction);
162: MemorySnapShot_Store(&ID, sizeof(int));
163: }
164: else
165: {
166: /* Convert ID to function */
167: MemorySnapShot_Store(&ID, sizeof(int));
168: InterruptHandlers[i].pFunction = CycInt_IDToHandlerFunction(ID);
169: }
170: }
171: MemorySnapShot_Store(&nCyclesOver, sizeof(nCyclesOver));
172: MemorySnapShot_Store(&PendingInterruptCount, sizeof(PendingInterruptCount));
173: if (bSave)
174: {
175: /* Convert function to ID */
176: ID = CycInt_HandlerFunctionToID(PendingInterruptFunction);
177: MemorySnapShot_Store(&ID, sizeof(int));
178: }
179: else
180: {
181: /* Convert ID to function */
182: MemorySnapShot_Store(&ID, sizeof(int));
183: PendingInterruptFunction = CycInt_IDToHandlerFunction(ID);
184: }
185:
186:
187: if (!bSave)
188: CycInt_SetNewInterrupt(); /* when restoring snapshot, compute current state after */
189: }
190:
191:
192: /*-----------------------------------------------------------------------*/
193: /**
194: * Find next interrupt to occur, and store to global variables for decrement
195: * in instruction decode loop.
196: * Note: Although InterruptHandlers.Cycles and LowestCycleCount are 64 bit
197: * variables to get all the cycle counters right (e.g. the DMA sound counter
198: * can get very high), PendingInterruptCount is still a 32 bit variable for
199: * performance reasons (it's decremented after each CPU instruction).
200: * So we have to initialize LowestCycleCount with INT_MAX, not with INT64_MAX!
201: * Since there is always a VBL or HBL counter pending which fits fine into the
202: * 32 bit variable, we can be sure that we don't run into problems here.
203: */
204: static void CycInt_SetNewInterrupt(void)
205: {
206: Sint64 LowestCycleCount = INT_MAX;
207: interrupt_id LowestInterrupt = INTERRUPT_NULL, i;
208:
209: LOG_TRACE(TRACE_INT, "int set new in video_cyc=%d active_int=%d pending_count=%d\n",
210: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount);
211:
212: /* Find next interrupt to go off */
213: for (i = INTERRUPT_NULL+1; i < MAX_INTERRUPTS; i++)
214: {
215: /* Is interrupt pending? */
216: if (InterruptHandlers[i].bUsed)
217: {
218: if (InterruptHandlers[i].Cycles < LowestCycleCount)
219: {
220: LowestCycleCount = InterruptHandlers[i].Cycles;
221: LowestInterrupt = i;
222: }
223: }
224: }
225:
226: /* Set new counts, active interrupt */
227: PendingInterruptCount = InterruptHandlers[LowestInterrupt].Cycles;
228: PendingInterruptFunction = InterruptHandlers[LowestInterrupt].pFunction;
229: ActiveInterrupt = LowestInterrupt;
230:
231: LOG_TRACE(TRACE_INT, "int set new out video_cyc=%d active_int=%d pending_count=%d\n",
232: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount );
233: }
234:
235:
236: /*-----------------------------------------------------------------------*/
237: /**
238: * Adjust all interrupt timings, MUST call CycInt_SetNewInterrupt after this.
239: */
240: static void CycInt_UpdateInterrupt(void)
241: {
242: Sint64 CycleSubtract;
243: int i;
244:
245: /* Find out how many cycles we went over (<=0) */
246: nCyclesOver = PendingInterruptCount;
247: /* Calculate how many cycles have passed, included time we went over */
248: CycleSubtract = InterruptHandlers[ActiveInterrupt].Cycles - nCyclesOver;
249:
250: /* Adjust table */
251: for (i = 0; i < MAX_INTERRUPTS; i++)
252: {
253: if (InterruptHandlers[i].bUsed)
254: InterruptHandlers[i].Cycles -= CycleSubtract;
255: }
256:
257: LOG_TRACE(TRACE_INT, "int upd video_cyc=%d cycle_over=%d cycle_sub=%lld\n",
258: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), nCyclesOver,
259: (long long)CycleSubtract);
260: }
261:
262:
263: /*-----------------------------------------------------------------------*/
264: /**
265: * Adjust all interrupt timings as 'ActiveInterrupt' has occured, and
266: * remove from active list.
267: */
268: void CycInt_AcknowledgeInterrupt(void)
269: {
270: /* Update list cycle counts */
271: CycInt_UpdateInterrupt();
272:
273: /* Disable interrupt entry which has just occured */
274: InterruptHandlers[ActiveInterrupt].bUsed = false;
275:
276: /* Set new */
277: CycInt_SetNewInterrupt();
278:
279: LOG_TRACE(TRACE_INT, "int ack video_cyc=%d active_int=%d active_cyc=%d pending_count=%d\n",
280: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, (int)InterruptHandlers[ActiveInterrupt].Cycles, PendingInterruptCount );
281: }
282:
283:
284: /*-----------------------------------------------------------------------*/
285: /**
286: * Add interrupt from time last one occurred.
287: */
288: void CycInt_AddAbsoluteInterrupt(int CycleTime, int CycleType, interrupt_id Handler)
289: {
290: assert(CycleTime >= 0);
291:
1.1.1.2 root 292: /* Update list cycle counts with current PendingInterruptCount before adding a new int, */
293: /* because CycInt_SetNewInterrupt can change the active int / PendingInterruptCount */
294: if ( ActiveInterrupt > 0 )
1.1 root 295: CycInt_UpdateInterrupt();
296:
297: InterruptHandlers[Handler].bUsed = true;
298: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + nCyclesOver;
299:
1.1.1.2 root 300: /* Set new active int and compute a new value for PendingInterruptCount*/
1.1 root 301: CycInt_SetNewInterrupt();
302:
303: LOG_TRACE(TRACE_INT, "int add abs video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
304: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
305: (long long)InterruptHandlers[Handler].Cycles, PendingInterruptCount );
306: }
307:
308:
309: /*-----------------------------------------------------------------------*/
310: /**
311: * Add interrupt to occur from now.
312: */
313: void CycInt_AddRelativeInterrupt(int CycleTime, int CycleType, interrupt_id Handler)
314: {
315: CycInt_AddRelativeInterruptWithOffset(CycleTime, CycleType, Handler, 0);
316: }
317:
318:
319:
320:
321: /*-----------------------------------------------------------------------*/
322: /**
323: * Add interrupt to occur after CycleTime/CycleType + CycleOffset.
324: * CycleOffset can be used to add another delay to the resulting
325: * number of internal cycles (should be 0 most of the time, except in
326: * the MFP emulation to start timers precisely based on the number of
327: * cycles of the current instruction).
328: * This allows to restart an MFP timer just after it expired.
329: */
330: void CycInt_AddRelativeInterruptWithOffset(int CycleTime, int CycleType, interrupt_id Handler, int CycleOffset)
331: {
332: assert(CycleTime >= 0);
333:
1.1.1.2 root 334: /* Update list cycle counts with current PendingInterruptCount before adding a new int, */
335: /* because CycInt_SetNewInterrupt can change the active int / PendingInterruptCount */
336: if ( ActiveInterrupt > 0 )
1.1 root 337: CycInt_UpdateInterrupt();
338:
339: InterruptHandlers[Handler].bUsed = true;
340: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + CycleOffset;
341:
1.1.1.2 root 342: /* Set new active int and compute a new value for PendingInterruptCount*/
1.1 root 343: CycInt_SetNewInterrupt();
344:
345: LOG_TRACE(TRACE_INT, "int add rel offset video_cyc=%d handler=%d handler_cyc=%lld offset_cyc=%d pending_count=%d\n",
346: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
347: (long long)InterruptHandlers[Handler].Cycles, CycleOffset, PendingInterruptCount);
348: }
349:
350:
351: /*-----------------------------------------------------------------------*/
352: /**
353: * Remove a pending interrupt from our table
354: */
355: void CycInt_RemovePendingInterrupt(interrupt_id Handler)
356: {
357: /* Update list cycle counts, including the handler we want to remove */
358: /* to be able to resume it later (for MFP timers) */
359: CycInt_UpdateInterrupt();
360:
361: /* Stop interrupt after CycInt_UpdateInterrupt, for CycInt_ResumeStoppedInterrupt */
362: InterruptHandlers[Handler].bUsed = false;
363:
364: /* Set new */
365: CycInt_SetNewInterrupt();
366:
367: LOG_TRACE(TRACE_INT, "int remove pending video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
368: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
369: (long long)InterruptHandlers[Handler].Cycles, PendingInterruptCount);
370: }
371:
372:
373: /*-----------------------------------------------------------------------*/
374: /**
375: * Resume a stopped interrupt from its current cycle count (for MFP timers)
376: */
377: void CycInt_ResumeStoppedInterrupt(interrupt_id Handler)
378: {
379: /* Restart interrupt */
380: InterruptHandlers[Handler].bUsed = true;
381:
382: /* Update list cycle counts */
383: CycInt_UpdateInterrupt();
384: /* Set new */
385: CycInt_SetNewInterrupt();
386:
387: LOG_TRACE(TRACE_INT, "int resume stopped video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
388: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
389: (long long)InterruptHandlers[Handler].Cycles, PendingInterruptCount);
390: }
391:
392:
393: /*-----------------------------------------------------------------------*/
394: /**
395: * Return true if interrupt is active in list
396: */
397: bool CycInt_InterruptActive(interrupt_id Handler)
398: {
399: /* Is timer active? */
400: if (InterruptHandlers[Handler].bUsed)
401: return true;
402:
403: return false;
404: }
405:
406:
407: /*-----------------------------------------------------------------------*/
408: /**
409: * Return cycles passed for an interrupt handler
410: */
411: int CycInt_FindCyclesPassed(interrupt_id Handler, int CycleType)
412: {
413: Sint64 CyclesPassed, CyclesFromLastInterrupt;
414:
415: CyclesFromLastInterrupt = InterruptHandlers[ActiveInterrupt].Cycles - PendingInterruptCount;
416: CyclesPassed = InterruptHandlers[Handler].Cycles - CyclesFromLastInterrupt;
417:
418: LOG_TRACE(TRACE_INT, "int find passed cyc video_cyc=%d handler=%d last_cyc=%lld passed_cyc=%lld\n",
419: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
420: (long long)CyclesFromLastInterrupt, (long long)CyclesPassed);
421:
422: return INT_CONVERT_FROM_INTERNAL ( CyclesPassed , CycleType ) ;
423: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.