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