Annotation of previous/src/cycInt.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.