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