Annotation of hatari/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 "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: }

unix.superglobalmegacorp.com

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