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