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