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