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