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