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