|
|
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 ! 22: cyles to CPU cycles, because it requires some floating points approximation ! 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" ! 64: #include "video.h" ! 65: ! 66: ! 67: void (*PendingInterruptFunction)(void); ! 68: int PendingInterruptCount; ! 69: ! 70: static int nCyclesOver; ! 71: ! 72: /* List of possible interrupt handlers to be store in 'PendingInterruptTable', ! 73: * used for 'MemorySnapShot' */ ! 74: static void (* const pIntHandlerFunctions[MAX_INTERRUPTS])(void) = ! 75: { ! 76: NULL, ! 77: Video_InterruptHandler_VBL, ! 78: Video_InterruptHandler_HBL, ! 79: Video_InterruptHandler_EndLine, ! 80: MFP_InterruptHandler_TimerA, ! 81: MFP_InterruptHandler_TimerB, ! 82: MFP_InterruptHandler_TimerC, ! 83: MFP_InterruptHandler_TimerD, ! 84: IKBD_InterruptHandler_ResetTimer, ! 85: IKBD_InterruptHandler_ACIA, ! 86: IKBD_InterruptHandler_MFP, ! 87: IKBD_InterruptHandler_AutoSend, ! 88: DmaSnd_InterruptHandler, ! 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: ! 314: /* Update list cycle counts before adding a new one, */ ! 315: /* since CycInt_SetNewInterrupt can change the active int / PendingInterruptCount */ ! 316: if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) ) ! 317: CycInt_UpdateInterrupt(); ! 318: ! 319: InterruptHandlers[Handler].bUsed = true; ! 320: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + nCyclesOver; ! 321: ! 322: /* Set new */ ! 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: ! 379: /* Update list cycle counts before adding a new one, */ ! 380: /* since CycInt_SetNewInterrupt can change the active int / PendingInterruptCount */ ! 381: if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) ) ! 382: CycInt_UpdateInterrupt(); ! 383: ! 384: InterruptHandlers[Handler].bUsed = true; ! 385: InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + CycleOffset; ! 386: ! 387: /* Set new */ ! 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.