|
|
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"
1.1.1.8 root 55: #include "configuration.h"
1.1 root 56: #include "blitter.h"
57: #include "dmaSnd.h"
58: #include "crossbar.h"
59: #include "fdc.h"
60: #include "ikbd.h"
61: #include "cycInt.h"
62: #include "m68000.h"
63: #include "mfp.h"
64: #include "midi.h"
65: #include "memorySnapShot.h"
66: #include "sound.h"
1.1.1.2 root 67: #include "screen.h"
1.1 root 68: #include "video.h"
1.1.1.5 root 69: #include "acia.h"
1.1 root 70:
71:
72: void (*PendingInterruptFunction)(void);
73: int PendingInterruptCount;
74:
75: static int nCyclesOver;
76:
77: /* List of possible interrupt handlers to be store in 'PendingInterruptTable',
78: * used for 'MemorySnapShot' */
79: static void (* const pIntHandlerFunctions[MAX_INTERRUPTS])(void) =
80: {
81: NULL,
82: Video_InterruptHandler_VBL,
83: Video_InterruptHandler_HBL,
84: Video_InterruptHandler_EndLine,
85: MFP_InterruptHandler_TimerA,
86: MFP_InterruptHandler_TimerB,
87: MFP_InterruptHandler_TimerC,
88: MFP_InterruptHandler_TimerD,
1.1.1.5 root 89: ACIA_InterruptHandler_IKBD,
1.1 root 90: IKBD_InterruptHandler_ResetTimer,
91: IKBD_InterruptHandler_AutoSend,
1.1.1.4 root 92: DmaSnd_InterruptHandler_Microwire, /* Used for both STE and Falcon Microwire emulation */
1.1 root 93: Crossbar_InterruptHandler_25Mhz,
94: Crossbar_InterruptHandler_32Mhz,
95: FDC_InterruptHandler_Update,
96: Blitter_InterruptHandler,
1.1.1.5 root 97: Midi_InterruptHandler_Update,
98:
1.1 root 99: };
100:
101: /* Event timer structure - keeps next timer to occur in structure so don't need
102: * to check all entries */
103: typedef struct
104: {
105: bool bUsed; /* Is interrupt active? */
106: Sint64 Cycles;
107: void (*pFunction)(void);
108: } INTERRUPTHANDLER;
109:
110: static INTERRUPTHANDLER InterruptHandlers[MAX_INTERRUPTS];
111: static int ActiveInterrupt=0;
112:
113: static void CycInt_SetNewInterrupt(void);
114:
115: /*-----------------------------------------------------------------------*/
116: /**
117: * Reset interrupts, handlers
118: */
119: void CycInt_Reset(void)
120: {
121: int i;
122:
123: /* Reset counts */
124: PendingInterruptCount = 0;
125: ActiveInterrupt = 0;
126: nCyclesOver = 0;
127:
128: /* Reset interrupt table */
129: for (i=0; i<MAX_INTERRUPTS; i++)
130: {
131: InterruptHandlers[i].bUsed = false;
132: InterruptHandlers[i].Cycles = INT_MAX;
133: InterruptHandlers[i].pFunction = pIntHandlerFunctions[i];
134: }
135: }
136:
137:
138: /*-----------------------------------------------------------------------*/
139: /**
140: * Convert interrupt handler function pointer to ID, used for saving
141: */
142: static int CycInt_HandlerFunctionToID(void (*pHandlerFunction)(void))
143: {
144: int i;
145:
146: /* Scan for function match */
147: for (i=0; i<MAX_INTERRUPTS; i++)
148: {
149: if (pIntHandlerFunctions[i]==pHandlerFunction)
150: return i;
151: }
152:
153: /* Didn't find one! Oops */
154: fprintf(stderr, "\nError: didn't find interrupt function matching 0x%p\n",
155: pHandlerFunction);
156: return 0;
157: }
158:
159:
160: /*-----------------------------------------------------------------------*/
161: /**
162: * Convert ID back into interrupt handler function, used for restoring
1.1.1.6 root 163: * We return a function pointer : void (*f)(void)
164: * (we could use typedef for better readability)
1.1 root 165: */
1.1.1.6 root 166: static void ( *CycInt_IDToHandlerFunction(int ID) )( void )
1.1 root 167: {
168: /* Get function pointer */
169: return pIntHandlerFunctions[ID];
170: }
171:
172:
173: /*-----------------------------------------------------------------------*/
174: /**
175: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
176: */
177: void CycInt_MemorySnapShot_Capture(bool bSave)
178: {
179: int i,ID;
180:
181: /* Save/Restore details */
182: for (i=0; i<MAX_INTERRUPTS; i++)
183: {
184: MemorySnapShot_Store(&InterruptHandlers[i].bUsed, sizeof(InterruptHandlers[i].bUsed));
185: MemorySnapShot_Store(&InterruptHandlers[i].Cycles, sizeof(InterruptHandlers[i].Cycles));
186: if (bSave)
187: {
188: /* Convert function to ID */
189: ID = CycInt_HandlerFunctionToID(InterruptHandlers[i].pFunction);
190: MemorySnapShot_Store(&ID, sizeof(int));
191: }
192: else
193: {
194: /* Convert ID to function */
195: MemorySnapShot_Store(&ID, sizeof(int));
196: InterruptHandlers[i].pFunction = CycInt_IDToHandlerFunction(ID);
197: }
198: }
199: MemorySnapShot_Store(&nCyclesOver, sizeof(nCyclesOver));
1.1.1.9 ! root 200: MemorySnapShot_Store(&ActiveInterrupt, sizeof(ActiveInterrupt));
1.1 root 201: MemorySnapShot_Store(&PendingInterruptCount, sizeof(PendingInterruptCount));
202: if (bSave)
203: {
204: /* Convert function to ID */
205: ID = CycInt_HandlerFunctionToID(PendingInterruptFunction);
206: MemorySnapShot_Store(&ID, sizeof(int));
207: }
208: else
209: {
210: /* Convert ID to function */
211: MemorySnapShot_Store(&ID, sizeof(int));
212: PendingInterruptFunction = CycInt_IDToHandlerFunction(ID);
213: }
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 after CycleTime/CycleType + CycleOffset.
346: * CycleOffset can be used to add another delay to the resulting
347: * number of internal cycles (should be 0 most of the time, except in
348: * the MFP emulation to start timers precisely based on the number of
349: * cycles of the current instruction).
350: * This allows to restart an MFP timer just after it expired.
351: */
352: void CycInt_AddRelativeInterruptWithOffset(int CycleTime, int CycleType, interrupt_id Handler, int CycleOffset)
353: {
1.1.1.8 root 354: //fprintf ( stderr , "int add rel %d type %d handler %d offset %d\n" , CycleTime,CycleType,Handler,CycleOffset );
1.1 root 355: assert(CycleTime >= 0);
356:
1.1.1.2 root 357: /* Update list cycle counts with current PendingInterruptCount before adding a new int, */
358: /* because CycInt_SetNewInterrupt can change the active int / PendingInterruptCount */
359: if ( ActiveInterrupt > 0 )
1.1 root 360: CycInt_UpdateInterrupt();
361:
362: InterruptHandlers[Handler].bUsed = true;
363: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + CycleOffset;
364:
1.1.1.2 root 365: /* Set new active int and compute a new value for PendingInterruptCount*/
1.1 root 366: CycInt_SetNewInterrupt();
367:
1.1.1.6 root 368: 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 369: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
1.1.1.6 root 370: InterruptHandlers[Handler].Cycles, CycleOffset, PendingInterruptCount);
371: }
372:
373:
374: /*-----------------------------------------------------------------------*/
375: /**
376: * Modify interrupt's Cycles to make it happen earlier or later.
377: * This will not restart the interrupt, but add CycleTime cycles to the
378: * current value of the counter.
379: * CycleTime can be <0 or >0
380: */
381: void CycInt_ModifyInterrupt(int CycleTime, int CycleType, interrupt_id Handler)
382: {
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 )
386: CycInt_UpdateInterrupt();
387:
388: InterruptHandlers[Handler].Cycles += INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType);
389:
390: /* Set new active int and compute a new value for PendingInterruptCount*/
391: CycInt_SetNewInterrupt();
392:
393: LOG_TRACE(TRACE_INT, "int modify video_cyc=%d handler=%d handler_cyc=%"PRId64" pending_count=%d\n",
394: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
395: InterruptHandlers[Handler].Cycles, PendingInterruptCount );
1.1 root 396: }
397:
398:
399: /*-----------------------------------------------------------------------*/
400: /**
401: * Remove a pending interrupt from our table
402: */
403: void CycInt_RemovePendingInterrupt(interrupt_id Handler)
404: {
405: /* Update list cycle counts, including the handler we want to remove */
406: /* to be able to resume it later (for MFP timers) */
407: CycInt_UpdateInterrupt();
408:
409: /* Stop interrupt after CycInt_UpdateInterrupt, for CycInt_ResumeStoppedInterrupt */
410: InterruptHandlers[Handler].bUsed = false;
411:
412: /* Set new */
413: CycInt_SetNewInterrupt();
414:
1.1.1.6 root 415: LOG_TRACE(TRACE_INT, "int remove pending video_cyc=%d handler=%d handler_cyc=%"PRId64" pending_count=%d\n",
1.1 root 416: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
1.1.1.6 root 417: InterruptHandlers[Handler].Cycles, PendingInterruptCount);
1.1 root 418: }
419:
420:
421: /*-----------------------------------------------------------------------*/
422: /**
423: * Resume a stopped interrupt from its current cycle count (for MFP timers)
424: */
425: void CycInt_ResumeStoppedInterrupt(interrupt_id Handler)
426: {
427: /* Restart interrupt */
428: InterruptHandlers[Handler].bUsed = true;
429:
430: /* Update list cycle counts */
431: CycInt_UpdateInterrupt();
432: /* Set new */
433: CycInt_SetNewInterrupt();
434:
1.1.1.6 root 435: LOG_TRACE(TRACE_INT, "int resume stopped video_cyc=%d handler=%d handler_cyc=%"PRId64" pending_count=%d\n",
1.1 root 436: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
1.1.1.6 root 437: InterruptHandlers[Handler].Cycles, PendingInterruptCount);
1.1 root 438: }
439:
440:
441: /*-----------------------------------------------------------------------*/
442: /**
443: * Return true if interrupt is active in list
444: */
445: bool CycInt_InterruptActive(interrupt_id Handler)
446: {
447: /* Is timer active? */
448: if (InterruptHandlers[Handler].bUsed)
449: return true;
450:
451: return false;
452: }
453:
454:
455: /*-----------------------------------------------------------------------*/
456: /**
1.1.1.8 root 457: * Return the number of the active interrupt (0 means no active int)
458: */
459: int CycInt_GetActiveInt(void)
460: {
461: return ActiveInterrupt;
462: }
463:
464:
465: /*-----------------------------------------------------------------------*/
466: /**
1.1 root 467: * Return cycles passed for an interrupt handler
468: */
469: int CycInt_FindCyclesPassed(interrupt_id Handler, int CycleType)
470: {
471: Sint64 CyclesPassed, CyclesFromLastInterrupt;
472:
473: CyclesFromLastInterrupt = InterruptHandlers[ActiveInterrupt].Cycles - PendingInterruptCount;
474: CyclesPassed = InterruptHandlers[Handler].Cycles - CyclesFromLastInterrupt;
475:
1.1.1.6 root 476: LOG_TRACE(TRACE_INT, "int find passed cyc video_cyc=%d handler=%d last_cyc=%"PRId64" passed_cyc=%"PRId64"\n",
1.1 root 477: Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
1.1.1.6 root 478: CyclesFromLastInterrupt, CyclesPassed);
1.1 root 479:
480: return INT_CONVERT_FROM_INTERNAL ( CyclesPassed , CycleType ) ;
481: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.