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

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

unix.superglobalmegacorp.com

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