|
|
1.1 root 1: /*
1.1.1.5 root 2: Hatari - mfp.c
1.1 root 3:
1.1.1.18 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1.1.5 root 6:
7: MFP - Multi Functional Peripheral. In emulation terms it's the 'chip from
8: hell' - most differences between a real machine and an emulator are down to
9: this chip. It seems very simple at first but the implementation is very
10: difficult.
11: The following code is the very accurate for an ST emulator as it is able to
12: perform Spectrum 512 raster effects as well as simulate the quirks found in
13: the chip. The easiest way to 'see' the MFP chip is to look at the diagram.
14: It shows the main details of the chip's behaviour with regard to interrupts
15: and pending/service bits.
1.1 root 16: */
1.1.1.11 root 17:
18: /* 2007/04/18 [NP] - Better values for MFPTimerToCPUCycleTable. */
19: /* - Don't restart the timers in MFP_EnableA_WriteByte and */
20: /* MFP_EnableB_WriteByte, this gives wrong results. */
21: /* 2007/05/05 [NP] - When a timer is looping (counter reaches 0), we must use */
22: /* PendingCyclesOver to restart it with Int_AddRelativeInterrupt. */
23: /* PendingCyclesOver is the value of PendingInterruptCount when */
24: /* the timer expired. */
25: /* - MFP_ReadTimer_AB/CD was wrong (returned the elapsed counter */
1.1.1.12 root 26: /* changes since start, instead of the remaining counter value). */
1.1.1.11 root 27: /* (ULM DSOTS Demos and Overscan Demos). */
28: /* 2007/09/25 [NP] Replace printf by calls to HATARI_TRACE. */
29: /* 2007/10/21 [NP] Use 'Int_AddRelativeInterruptWithOffset' when an MFP timer is */
30: /* looping. Gives better accuracy when using '4' as a divisor. */
31: /* (fix ULM DSOTS Demos and Overscan Demos). */
32: /* 2007/10/24 [NP] Handle the possibility to resume a timer after stopping it. */
33: /* After writing 0 to ctrl, writing a >0 in ctrl should continue */
34: /* the timer with the value that was stored in data reg when timer */
35: /* was stopped. The value is saved in MFP_Tx_MAINCOUNTER whenever */
36: /* 0 is written in ctrl reg (Froggies Over The Fence by STCNX). */
37: /* 2007/10/28 [NP] Function 'Int_ResumeStoppedInterrupt' to better handle the */
38: /* possibility to resume a timer that was stopped with ctrl=0 */
39: /* (ST CNX screen in Punish Your Machine). */
40: /* 2007/12/27 [NP] When adding a new MFP interrupt (ctrl != 0 ), we must take */
41: /* into account the number of cycles of the current instruction, as*/
42: /* well as the accumulated wait state cycles, else the int counter */
43: /* will be started between 8 - 20 cycles earlier, which can break */
44: /* some too strict code : the int counter must start after the */
45: /* current instruction is processed, not before. The write is */
46: /* considered effective 4 cycles before the end of the current */
47: /* instruction. */
48: /* (fix ULM Dark Side Of The Spoon and Decade Demo's Wow Scroll 2).*/
49: /* 2008/02/06 [NP] Handle "fast" timers as those started by the TOS for the RS232 */
50: /* baud rate generator. In that case, the timers could be too fast */
51: /* to be handled by the CPU, which means PendingCyclesOver can be */
52: /* >= INT_CONVERT_TO_INTERNAL ( TimerClockCycles , INT_MFP_CYCLE ) */
53: /* and this will give wrong results when the timer restarts if */
54: /* we call Int_AddRelativeInterruptWithOffset. We use a modulo to */
55: /* limit PendingCyclesOver to not more than the number of cycles */
56: /* of one int (which means we "skip" the ints that could not be */
57: /* processed). */
58: /* 2008/03/08 [NP] Add traces when writing to vector register fffa17. */
59: /* Use M68000_INT_MFP when calling M68000_Exception(). */
1.1.1.12 root 60: /* 2008/04/17 [NP] Handle the case where Timer B is in event count mode and the */
61: /* content of $fffa21 is updated by the end of line signal while a */
62: /* read instruction at addr $fffa21 occurs at the same time (before*/
63: /* calling MFP_TimerB_EventCount_Interrupt). */
64: /* In that case, we need to return MFP_TB_MAINCOUNTER - 1. */
65: /* (fix B.I.G. Demo Screen 1). */
66: /* FIXME : this should be handled by Cycles_GetCounterOnReadAccess */
67: /* but it's not correctly implemented at the moment. */
68: /* 2008/04/20 [NP] In the TRACE call in 'MFP_Exception', replace 'get_long' by */
69: /* 'STMemory_ReadLong' because 'get_long' produced a bus error */
70: /* if we were not already in supervisor mode when the mfp exception*/
1.1.1.18 root 71: /* occurred. This could cause bus error when restoring snapshot */
1.1.1.12 root 72: /* of a gemdos program for example if trace mode was activated. */
73: /* 2008/07/12 [NP] When stopping an active timer just when the internal data */
74: /* counter is going from 1 to 0, the internal data counter will be */
75: /* set to 0 (=256) instead of being reloaded with the original */
76: /* data value. In case no new value is written to the data reg, */
77: /* this means a write > 0 to the control reg will restart the timer*/
78: /* with a counter of 256 ! (fix timer saving routine used by */
79: /* ST Cnx in the Punish Your Machine and the Froggies Over The */
80: /* Fence (although this routine is in fact buggy)). */
81: /* 2008/09/13 [NP] Add some traces when stopping a timer and changing data reg. */
82: /* Don't apply timer D patch if timer D ctrl reg is 0. */
83: /* 2008/10/04 [NP] In MFP_TimerBData_ReadByte, test for overlap only when nHBL */
84: /* is between nStartHBL and nEndHBL (fix Wolfenstein 3D intro). */
85: /* In event count mode for timer A and B, set data reg to 255 when */
86: /* data reg was 0 (which in fact means 256). */
87: /* 2008/10/16 [NP] No need to set data reg to 255 when decrementing a data reg that*/
88: /* was 0, this is already what is implicitly done, because data */
89: /* reg for timer A/B is Uint8 (revert 2008/10/04 changes). */
1.1.1.14 root 90: /* 2008/12/11 [NP] In MFP_CheckPendingInterrupts(), returns true or false instead */
1.1.1.13 root 91: /* of void, depending on whether at least one MFP interrupt was */
92: /* allowed or not. */
1.1.1.14 root 93: /* 2009/03/28 [NP] Handle bit 3 of AER for timer B (fix Seven Gates Of Jambala). */
1.1.1.16 root 94: /* 2010/07/26 [NP] In MFP_StartTimer_AB, when ctrl reg is in pulse width mode, */
95: /* clear bit 3 to emulate it as in delay mode. This is not */
1.1.1.18 root 96: /* completely correct as we should also emulate GPIO 3/4, but it */
1.1.1.16 root 97: /* helps running some programs (fix the game Erik). */
1.1.1.18 root 98: /* 2013/02/24 [NP] - In MFP_CheckPendingInterrupts, don't check all the MFP ints, */
99: /* stop as soon as the highest interrupt is found (simultaneous */
100: /* interrupts could be processed during the same cycle and were */
101: /* stacked/executed in the reverse order, from lowest to highest */
102: /* priority, which was wrong). */
103: /* - Use MFP_ProcessIRQ to separate the MFP's IRQ signal handling */
104: /* and the exception processing at the CPU level. */
105: /* 2013/03/01 [NP] When MFP_IRQ goes from 0 to 1, the resulting signal is visible */
106: /* to the CPU only 4 cycles later (fix Audio Artistic Demo by */
107: /* Big Alec and the games Super Hang On, Super Monaco GP, Bolo). */
108: /* 2013/03/10 [NP] Improve the MFP_IRQ 4 cycle delay by taking into account the */
109: /* time at which the timer expired during the CPU instruction */
110: /* (fix Reset part in Decade Demo, High Fidelity Dreams by Aura). */
111: /* 2013/03/14 [NP] When writing to the MFP's registers, take the write cycles into */
112: /* account when updating MFP_IRQ_Time (properly fix Super Hang On).*/
113: /* 2013/04/11 [NP] Handle the IACK cycles, interrupts can change during the first */
114: /* 12 cycles of an MFP exception (fix Anomaly Demo Menu by MJJ Prod*/
115: /* and sample intro in the game The Final Conflict). */
116: /* 2013/04/21 [NP] Handle the case where several MFP interrupts happen during the */
117: /* same CPU instruction but at different sub-cycles. We must take */
118: /* into account only the oldest interrupts to choose the highest */
119: /* one (fix Fuzion CD Menus 77, 78, 84). */
1.1.1.11 root 120:
1.1.1.12 root 121:
1.1.1.13 root 122: const char MFP_fileid[] = "Hatari mfp.c : " __DATE__ " " __TIME__;
1.1 root 123:
124: #include "main.h"
1.1.1.8 root 125: #include "configuration.h"
1.1.1.9 root 126: #include "dmaSnd.h"
1.1.1.15 root 127: #include "crossbar.h"
1.1 root 128: #include "fdc.h"
129: #include "ikbd.h"
1.1.1.15 root 130: #include "cycInt.h"
1.1.1.8 root 131: #include "ioMem.h"
1.1.1.9 root 132: #include "joy.h"
1.1 root 133: #include "m68000.h"
134: #include "memorySnapShot.h"
135: #include "mfp.h"
136: #include "psg.h"
1.1.1.8 root 137: #include "rs232.h"
1.1 root 138: #include "sound.h"
1.1.1.12 root 139: #include "stMemory.h"
1.1.1.8 root 140: #include "tos.h"
1.1.1.13 root 141: #include "vdi.h"
1.1.1.16 root 142: #include "screen.h"
1.1 root 143: #include "video.h"
1.1.1.3 root 144:
1.1 root 145:
146: /*
147: MFP interrupt channel circuit:-
148:
149: EdgeRegister EnableRegister MaskRegister SBit
150: | | | |
151: | | | | ------------------------
152: | | ------------------------ ---\ |---\ | |
153: | o--\ | | AND---o----------------AND---| S InterruptInService |
154: ---\ | AND---| S InterruptPending O |-------/ | |---/ | |
155: XOR----------)--/ | R | | | ------------------------
156: Input -----/ | ------------------------ | |
157: | | InterruptRequest |
158: NOT OR |
159: | | | |
160: -------------------- --------------------------------------o--- PassVector
161: */
162:
1.1.1.7 root 163:
1.1.1.18 root 164: /*
165: Emulation Note :
166: - MFP emulation doesn't run in parallel with the CPU emulation as it would take too much resources.
167: Instead, MFP emulation is called each time a CPU instruction is completely processed.
168: The drawback is that several MFP interrupts can happen during a single CPU instruction (especially
169: for long ones like MOVEM or DIV). In that case, we should not choose the highest priority interrupt
170: among all the interrupts, but we should keep only the interrupts that chronologically happened first
171: during this CPU instruction (and ignore the other interrupts' requests for this CPU instruction).
1.1.1.19! root 172: - When the MFP's main IRQ signal goes from 0 to 1, the signal is not immediately visible to the CPU, but only
1.1.1.18 root 173: 4 cycles later. This 4 cycle delay should be taken into account, depending at what time the signal
174: went to 1 in the corresponding CPU instruction (the 4 cycle delay can be "included" in the CPU instruction
175: in some cases)
176: - When an interrupt happens in the MFP, an exception will be started in the CPU. Then after 12 cycles an IACK
177: sequence will be started by the CPU to request the interrupt vector from the MFP. During those 12 cycles,
178: it is possible that a new higher priority MFP interrupt happens and in that case we must replace the MFP
179: vector number that was initially computed at the start of the exception with the new one.
180: This is also after the IACK sequence that in service / pending bits must be handled for this MFP's interrupt.
181: */
182:
1.1.1.7 root 183: /*-----------------------------------------------------------------------*/
1.1 root 184:
1.1.1.2 root 185: /* MFP Registers */
1.1.1.9 root 186: Uint8 MFP_GPIP; /* General Purpose Pins */
1.1.1.11 root 187: Uint8 MFP_VR; /* Vector Register 0xfffa17 */
1.1.1.9 root 188: Uint8 MFP_IERA,MFP_IERB; /* Interrupt Enable Registers A,B 0xfffa07,0xfffa09 */
189: Uint8 MFP_IPRA,MFP_IPRB; /* Interrupt Pending Registers A,B 0xfffa0b,0xfffa0d */
1.1.1.11 root 190: Uint8 MFP_TACR,MFP_TBCR; /* Timer A,B Control Registers */
1.1.1.10 root 191:
1.1.1.11 root 192: static Uint8 MFP_TCDCR; /* C+D Control Registers */
1.1.1.10 root 193: static Uint8 MFP_AER,MFP_DDR; /* Active Edge Register, Data Direction Register */
194: static Uint8 MFP_ISRA,MFP_ISRB; /* Interrupt In-Service Registers A,B 0xfffa0f,0xfffa11 */
195: static Uint8 MFP_IMRA,MFP_IMRB; /* Interrupt Mask Registers A,B 0xfffa13,0xfffa15 */
196: static Uint8 MFP_TADR,MFP_TBDR; /* Timer A,B Data Registers */
197: static Uint8 MFP_TCDR,MFP_TDDR; /* Timer C,D Data Registers */
198: static Uint8 MFP_TA_MAINCOUNTER; /* Timer A Main Counter (internal to MFP) */
199: static Uint8 MFP_TB_MAINCOUNTER; /* Timer B Main Counter */
200: static Uint8 MFP_TC_MAINCOUNTER; /* Timer C Main Counter (these are temp's, set when read as) */
201: static Uint8 MFP_TD_MAINCOUNTER; /* Timer D Main Counter (as done via interrupts) */
1.1 root 202:
203: /* CPU clock cycle counts for each timer */
1.1.1.7 root 204: static int TimerAClockCycles=0;
205: static int TimerBClockCycles=0;
206: static int TimerCClockCycles=0;
207: static int TimerDClockCycles=0;
208:
1.1.1.11 root 209: /* If a timer is stopped then restarted later without writing to the data register, */
210: /* we must resume the timer from where we left in the interrupts table, instead of */
211: /* computing a new number of clock cycles to restart the interrupt. */
1.1.1.14 root 212: static bool TimerACanResume = false;
213: static bool TimerBCanResume = false;
214: static bool TimerCCanResume = false;
215: static bool TimerDCanResume = false;
1.1.1.11 root 216:
1.1.1.14 root 217: bool bAppliedTimerDPatch; /* true if the Timer-D patch has been applied */
1.1.1.9 root 218: static int nTimerDFakeValue; /* Faked Timer-D data register for the Timer-D patch */
1.1.1.8 root 219:
1.1.1.11 root 220: static int PendingCyclesOver = 0; /* >= 0 value, used to "loop" a timer when data counter reaches 0 */
1.1 root 221:
1.1.1.18 root 222:
223: #define MFP_IRQ_DELAY_TO_CPU 4 /* When MFP_IRQ is set, it takes 4 CPU cycles before it's visible to the CPU */
224:
225: static int MFP_Current_Interrupt = -1;
226: static Uint8 MFP_IRQ = 0;
227: static Uint64 MFP_IRQ_Time = 0;
228: bool MFP_UpdateNeeded = false; /* When set to true, main CPU loop should call MFP_UpdateIRQ() */
229: static Uint64 MFP_Pending_Time_Min; /* Clock value of the oldest pending int since last MFP_UpdateIRQ() */
230: static Uint64 MFP_Pending_Time[ MFP_INT_MAX+1 ]; /* Clock value when pending is set to 1 for each non-masked int */
231:
1.1.1.11 root 232: static const Uint16 MFPDiv[] =
233: {
234: 0,
235: 4,
236: 10,
237: 16,
238: 50,
239: 64,
240: 100,
241: 200
1.1 root 242: };
243:
1.1.1.11 root 244: /* Convert data/ctrl register to a number of mfp cycles */
245: #define MFP_REG_TO_CYCLES(data,ctrl) ( data * MFPDiv[ ctrl&0x7 ] )
246: /* Determine the data register corresponding to a number of mfp cycles/ctrl register */
247: /* (we round to the closest higher integer) */
248: #define MFP_CYCLE_TO_REG(cyc,ctrl) ( ( cyc + MFPDiv[ ctrl&0x7 ] - 1 ) / MFPDiv[ ctrl&0x7 ] )
249: //#define MFP_CYCLE_TO_REG(cyc,ctrl) ( cyc / MFPDiv[ ctrl&0x7 ] )
250:
1.1 root 251:
1.1.1.18 root 252:
253:
254: /*--------------------------------------------------------------*/
255: /* Local functions prototypes */
256: /*--------------------------------------------------------------*/
257:
258: static Uint8 MFP_ConvertIntNumber ( int Interrupt , Uint8 **pMFP_IER , Uint8 **pMFP_IPR , Uint8 **pMFP_ISR , Uint8 **pMFP_IMR );
259: static void MFP_Exception ( int Interrupt );
260: static bool MFP_InterruptRequest ( int Int , Uint8 Bit , Uint8 IPRx , Uint8 IMRx , Uint8 PriorityMaskA , Uint8 PriorityMaskB );
261: static int MFP_CheckPendingInterrupts ( void );
262:
263:
264:
1.1.1.2 root 265: /*-----------------------------------------------------------------------*/
1.1.1.11 root 266: /**
267: * Reset all MFP variables and start interrupts on their way!
268: */
1.1 root 269: void MFP_Reset(void)
270: {
1.1.1.18 root 271: int i;
272:
1.1.1.11 root 273: /* Reset MFP internal variables */
1.1.1.7 root 274:
1.1.1.14 root 275: bAppliedTimerDPatch = false;
1.1.1.7 root 276:
1.1.1.11 root 277: MFP_GPIP = 0xff;
278: MFP_AER = MFP_DDR = 0;
279: MFP_IERA = MFP_IERB = 0;
280: MFP_IPRA = MFP_IPRB = 0;
281: MFP_ISRA = MFP_ISRB = 0;
282: MFP_IMRA = MFP_IMRB = 0;
283: MFP_VR = 0;
284: MFP_TACR = MFP_TBCR = MFP_TCDCR = 0;
285: MFP_TADR = MFP_TBDR = 0;
286: MFP_TCDR = MFP_TDDR = 0;
287: MFP_TA_MAINCOUNTER = MFP_TB_MAINCOUNTER = 0;
288: MFP_TC_MAINCOUNTER = MFP_TD_MAINCOUNTER = 0;
1.1 root 289:
1.1.1.11 root 290: /* Clear counters */
291: TimerAClockCycles = TimerBClockCycles = 0;
292: TimerCClockCycles = TimerDClockCycles = 0;
1.1.1.18 root 293:
294: /* Clear IRQ */
295: MFP_Current_Interrupt = -1;
296: MFP_IRQ = 0;
297: MFP_IRQ_Time = 0;
298: MFP_UpdateNeeded = false;
299: MFP_Pending_Time_Min = UINT64_MAX;
300: for ( i=0 ; i<=MFP_INT_MAX ; i++ )
301: MFP_Pending_Time[ i ] = UINT64_MAX;
1.1 root 302: }
303:
1.1.1.2 root 304:
305: /*-----------------------------------------------------------------------*/
1.1.1.11 root 306: /**
307: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
308: */
1.1.1.12 root 309: void MFP_MemorySnapShot_Capture(bool bSave)
1.1 root 310: {
1.1.1.11 root 311: /* Save/Restore details */
312: MemorySnapShot_Store(&MFP_GPIP, sizeof(MFP_GPIP));
313: MemorySnapShot_Store(&MFP_AER, sizeof(MFP_AER));
314: MemorySnapShot_Store(&MFP_DDR, sizeof(MFP_DDR));
315: MemorySnapShot_Store(&MFP_IERA, sizeof(MFP_IERA));
316: MemorySnapShot_Store(&MFP_IERB, sizeof(MFP_IERB));
317: MemorySnapShot_Store(&MFP_IPRA, sizeof(MFP_IPRA));
318: MemorySnapShot_Store(&MFP_IPRB, sizeof(MFP_IPRB));
319: MemorySnapShot_Store(&MFP_ISRA, sizeof(MFP_ISRA));
320: MemorySnapShot_Store(&MFP_ISRB, sizeof(MFP_ISRB));
321: MemorySnapShot_Store(&MFP_IMRA, sizeof(MFP_IMRA));
322: MemorySnapShot_Store(&MFP_IMRB, sizeof(MFP_IMRB));
323: MemorySnapShot_Store(&MFP_VR, sizeof(MFP_VR));
324: MemorySnapShot_Store(&MFP_TACR, sizeof(MFP_TACR));
325: MemorySnapShot_Store(&MFP_TBCR, sizeof(MFP_TBCR));
326: MemorySnapShot_Store(&MFP_TCDCR, sizeof(MFP_TCDCR));
327: MemorySnapShot_Store(&MFP_TADR, sizeof(MFP_TADR));
328: MemorySnapShot_Store(&MFP_TBDR, sizeof(MFP_TBDR));
329: MemorySnapShot_Store(&MFP_TCDR, sizeof(MFP_TCDR));
330: MemorySnapShot_Store(&MFP_TDDR, sizeof(MFP_TDDR));
331: MemorySnapShot_Store(&MFP_TA_MAINCOUNTER, sizeof(MFP_TA_MAINCOUNTER));
332: MemorySnapShot_Store(&MFP_TB_MAINCOUNTER, sizeof(MFP_TB_MAINCOUNTER));
333: MemorySnapShot_Store(&MFP_TC_MAINCOUNTER, sizeof(MFP_TC_MAINCOUNTER));
334: MemorySnapShot_Store(&MFP_TD_MAINCOUNTER, sizeof(MFP_TD_MAINCOUNTER));
335: MemorySnapShot_Store(&TimerAClockCycles, sizeof(TimerAClockCycles));
336: MemorySnapShot_Store(&TimerBClockCycles, sizeof(TimerBClockCycles));
337: MemorySnapShot_Store(&TimerCClockCycles, sizeof(TimerCClockCycles));
338: MemorySnapShot_Store(&TimerDClockCycles, sizeof(TimerDClockCycles));
339: MemorySnapShot_Store(&TimerACanResume, sizeof(TimerACanResume));
340: MemorySnapShot_Store(&TimerBCanResume, sizeof(TimerBCanResume));
341: MemorySnapShot_Store(&TimerCCanResume, sizeof(TimerCCanResume));
342: MemorySnapShot_Store(&TimerDCanResume, sizeof(TimerDCanResume));
1.1.1.18 root 343: MemorySnapShot_Store(&MFP_Current_Interrupt, sizeof(MFP_Current_Interrupt));
344: MemorySnapShot_Store(&MFP_IRQ, sizeof(MFP_IRQ));
345: MemorySnapShot_Store(&MFP_IRQ_Time, sizeof(MFP_IRQ_Time));
346: MemorySnapShot_Store(&MFP_UpdateNeeded, sizeof(MFP_UpdateNeeded));
347: MemorySnapShot_Store(&MFP_Pending_Time_Min, sizeof(MFP_Pending_Time_Min));
348: MemorySnapShot_Store(&MFP_Pending_Time, sizeof(MFP_Pending_Time));
349: }
350:
351:
352:
353: /*-----------------------------------------------------------------------*/
354: /**
355: * Given an MFP interrupt number, return a pointer to the corresponding
356: * registers handling this interrupt, as well as the binary value
357: * to set/clear these registers.
358: * If an input pointer is NULL, we don't return the corresponding register.
359: */
360: static Uint8 MFP_ConvertIntNumber ( int Interrupt , Uint8 **pMFP_IER , Uint8 **pMFP_IPR , Uint8 **pMFP_ISR , Uint8 **pMFP_IMR )
361: {
362: Uint8 Bit;
363:
364: if ( Interrupt > 7 )
365: {
366: Bit = 1 << ( Interrupt - 8 );
367: if ( pMFP_IER ) *pMFP_IER = &MFP_IERA;
368: if ( pMFP_IPR ) *pMFP_IPR = &MFP_IPRA;
369: if ( pMFP_ISR ) *pMFP_ISR = &MFP_ISRA;
370: if ( pMFP_IMR ) *pMFP_IMR = &MFP_IMRA;
371: }
372: else
373: {
374: Bit = 1 << Interrupt;
375: if ( pMFP_IER ) *pMFP_IER = &MFP_IERB;
376: if ( pMFP_IPR ) *pMFP_IPR = &MFP_IPRB;
377: if ( pMFP_ISR ) *pMFP_ISR = &MFP_ISRB;
378: if ( pMFP_IMR ) *pMFP_IMR = &MFP_IMRB;
379: }
380:
381: return Bit;
1.1 root 382: }
383:
1.1.1.2 root 384:
385: /*-----------------------------------------------------------------------*/
1.1.1.11 root 386: /**
1.1.1.18 root 387: * Call the MFP exception associated to the current MFP interrupt 0-15.
388: * When the MFP sets its IRQ signal, it will put the interrupt vector number
389: * on the data bus ; the 68000 will read it during the IACK cycle
390: * and multiply it by 4 to get the address of the exception handler.
391: * The upper 4 bits of the vector number are stored in the VR register 0xfffa17
392: * (default value is 0x40, which gives exceptions' handlers located at 0x100 in RAM)
1.1.1.11 root 393: */
1.1.1.18 root 394: static void MFP_Exception ( int Interrupt )
1.1 root 395: {
1.1.1.18 root 396: unsigned int VecNr;
1.1 root 397:
1.1.1.18 root 398: VecNr = ( MFP_VR & 0xf0 ) + Interrupt;
1.1.1.11 root 399:
1.1.1.14 root 400: if (LOG_TRACE_LEVEL(TRACE_MFP_EXCEPTION))
1.1.1.11 root 401: {
1.1.1.14 root 402: int FrameCycles, HblCounterVideo, LineCycles;
403: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
404: LOG_TRACE_PRINT("mfp excep int=%d vec=0x%x new_pc=0x%x video_cyc=%d %d@%d\n" ,
1.1.1.18 root 405: Interrupt, VecNr * 4, STMemory_ReadLong ( VecNr * 4 ), FrameCycles, LineCycles, HblCounterVideo );
1.1.1.11 root 406: }
407:
1.1.1.18 root 408: M68000_Exception(VecNr * 4, M68000_EXC_SRC_INT_MFP);
1.1 root 409: }
410:
1.1.1.2 root 411:
1.1.1.18 root 412:
413:
1.1.1.2 root 414: /*-----------------------------------------------------------------------*/
1.1.1.11 root 415: /**
1.1.1.18 root 416: * Return the vector number associated to the current MFP interrupt.
417: * MFP_ProcessIACK is called 12 cycles after the start of the 68000 exception.
418: * We must call MFP_UpdateIRQ just before the IACK cycles to update
419: * MFP_Current_Interrupt in case a higher MFP interrupt happened
420: * or pending bit was set twice for the same interrupt during those 12 cycles (rare case)
1.1.1.11 root 421: */
1.1.1.18 root 422: int MFP_ProcessIACK ( int OldVecNr )
1.1.1.10 root 423: {
1.1.1.18 root 424: Uint8 *pPendingReg;
425: Uint8 *pInServiceReg;
426: Uint8 Bit;
427: int NewVecNr;
428:
429:
430: /* Check if MFP interrupt vector number changed before IACK */
431: MFP_UpdateIRQ ( CyclesGlobalClockCounter );
432:
433: NewVecNr = ( MFP_VR & 0xf0 ) + MFP_Current_Interrupt;
434:
435: /* Print traces if VecNr changed just before IACK */
436: if ( LOG_TRACE_LEVEL(TRACE_MFP_EXCEPTION) && ( OldVecNr != NewVecNr ) )
1.1.1.11 root 437: {
1.1.1.18 root 438: int FrameCycles, HblCounterVideo, LineCycles;
439: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
440: LOG_TRACE_PRINT("mfp iack change old_vec=0x%x new_vec=0x%x new_pc=0x%x video_cyc=%d %d@%d\n" ,
441: OldVecNr * 4, NewVecNr * 4, STMemory_ReadLong ( NewVecNr * 4 ) , FrameCycles, LineCycles, HblCounterVideo );
1.1.1.11 root 442: }
1.1.1.18 root 443:
444: Bit = MFP_ConvertIntNumber ( MFP_Current_Interrupt , NULL , &pPendingReg , &pInServiceReg , NULL );
445:
446: *pPendingReg &= ~Bit; /* Clear pending bit */
447:
448: /* Are we in 'auto' interrupt or 'manual' ? */
449: if ( MFP_VR & 0x08 ) /* Software End-of-Interrupt (SEI) */
450: *pInServiceReg |= Bit; /* Set interrupt in service register */
1.1.1.11 root 451: else
1.1.1.18 root 452: *pInServiceReg &= ~Bit; /* Clear interrupt in service register */
453:
454: MFP_UpdateIRQ ( CyclesGlobalClockCounter );
455:
456: return NewVecNr; /* Vector number */
1.1.1.10 root 457: }
458:
459:
1.1.1.18 root 460:
461:
1.1.1.10 root 462: /*-----------------------------------------------------------------------*/
1.1.1.11 root 463: /**
1.1.1.18 root 464: * This function is called from the CPU emulation part when SPCFLAG_MFP is set.
465: * If the MFP's IRQ signal is set, we check that SR allows a level 6 interrupt,
466: * and if so, we call MFP_Exception.
467: * If SR doesn't allow an MFP interrupt, MFP's pending requests will be
468: * processed later when SR allows it.
469: *
470: * Important timing note : when the MFP's IRQ signal is set, it's visible to
471: * the CPU only 4 cycles later. Depending if the signal happens during a CPU
472: * instruction or just before processing a new instruction, this delay will
473: * not always be necessary.
474: *
475: * Instead of using CycInt_AddRelativeInterrupt to simulate this 4 cycles delay,
476: * we use MFP_IRQ_Time to delay the exception processing until 4 cycles have
477: * passed.
1.1.1.11 root 478: */
1.1.1.18 root 479: bool MFP_ProcessIRQ ( void )
1.1 root 480: {
1.1.1.18 root 481: //fprintf ( stderr , "process irq %d %lld %lld - ipr %x %x imr %x %x isr %x %x\n" , MFP_IRQ , CyclesGlobalClockCounter , MFP_IRQ_Time , MFP_IPRA , MFP_IPRB , MFP_IMRA , MFP_IMRB , MFP_ISRA , MFP_ISRB );
482:
483: if ( MFP_IRQ == 1 )
1.1.1.11 root 484: {
1.1.1.18 root 485: if ( CyclesGlobalClockCounter - MFP_IRQ_Time < MFP_IRQ_DELAY_TO_CPU ) /* Is it time to trigger the exception ? */
1.1.1.11 root 486: {
1.1.1.18 root 487: return false; /* For now, return without calling an exception (and try again later) */
488: }
1.1.1.11 root 489:
1.1.1.18 root 490: if (regs.intmask < 6)
491: {
492: /* The exception is possible ; pending / in service bits will be handled in MFP_ProcessIACK() */
493: MFP_Exception ( MFP_Current_Interrupt );
494: return true;
1.1.1.11 root 495: }
496: }
1.1 root 497:
1.1.1.14 root 498: return false;
1.1 root 499: }
500:
1.1.1.2 root 501:
1.1.1.18 root 502:
1.1.1.2 root 503: /*-----------------------------------------------------------------------*/
1.1.1.11 root 504: /**
1.1.1.18 root 505: * Update the MFP IRQ signal when IERx, IPRx, ISRx or IMRx are modified.
506: * We set the special flag SPCFLAG_MFP accordingly (to say if an MFP interrupt
507: * is to be processed) so we only have one compare to call MFP_ProcessIRQ
508: * during the CPU's decode instruction loop.
509: * If MFP_IRQ goes from 0 to 1, we update MFP_IRQ_Time to correctly emulate
510: * the 4 cycle delay before MFP_IRQ is visible to the CPU.
511: *
512: * When MFP_UpdateIRQ() is called after writing to an MFP's register, Event_Time
513: * will be the time of the write cycle.
514: * When MFP_UpdateIRQ is called from the main CPU loop after processing the
515: * internal timers, Event_Time will be 0 and we must use MFP_Pending_Time[ NewInt ].
516: * This way, MFP_IRQ_Time should always be correct to check the delay in MFP_ProcessIRQ().
1.1.1.11 root 517: */
1.1.1.18 root 518: void MFP_UpdateIRQ ( Uint64 Event_Time )
1.1 root 519: {
1.1.1.18 root 520: int NewInt;
521:
522: //fprintf ( stderr , "updirq0 %d - ipr %x %x imr %x %x isr %x %x\n" , MFP_IRQ , MFP_IPRA , MFP_IPRB , MFP_IMRA , MFP_IMRB , MFP_ISRA , MFP_ISRB );
523:
524: if ( ( MFP_IPRA & MFP_IMRA ) | ( MFP_IPRB & MFP_IMRB ) )
525: {
526: NewInt = MFP_CheckPendingInterrupts ();
527:
528: if ( NewInt >= 0 )
529: {
530: if ( MFP_IRQ == 0 ) /* MFP IRQ goes from 0 to 1 */
531: {
532: if ( Event_Time != 0 )
533: MFP_IRQ_Time = Event_Time;
534: else
535: MFP_IRQ_Time = MFP_Pending_Time[ NewInt ];
536: }
1.1.1.13 root 537:
1.1.1.18 root 538: MFP_IRQ = 1;
539: MFP_Current_Interrupt = NewInt;
540: }
541: else
542: MFP_IRQ = 0; /* Pending interrupts are blocked by in-service interrupts */
543: }
544: else
1.1.1.15 root 545: {
1.1.1.18 root 546: MFP_IRQ = 0;
1.1.1.15 root 547: }
1.1.1.13 root 548:
1.1.1.18 root 549: //fprintf ( stderr , "updirq1 %d - ipr %x %x imr %x %x isr %x %x\n" , MFP_IRQ , MFP_IPRA , MFP_IPRB , MFP_IMRA , MFP_IMRB , MFP_ISRA , MFP_ISRB );
550: if ( MFP_IRQ == 1 )
1.1.1.11 root 551: {
1.1.1.18 root 552: M68000_SetSpecial(SPCFLAG_MFP);
1.1.1.11 root 553: }
1.1.1.18 root 554: else
555: M68000_UnsetSpecial(SPCFLAG_MFP);
1.1 root 556:
1.1.1.18 root 557: /* Update IRQ is done, reset Time_Min and UpdateNeeded */
558: MFP_Pending_Time_Min = UINT64_MAX;
559: MFP_UpdateNeeded = false;
560: }
1.1.1.12 root 561:
1.1.1.13 root 562:
1.1.1.18 root 563: /*-----------------------------------------------------------------------*/
564: /**
565: * Test if interrupt 'Bit' is set in pending and mask register.
566: * Also check that no higher priority interrupt is in service.
567: * Depending on the interrupt, we check either IPRA/IMRA or IPRB/IMRB
568: * @return true if the MFP interrupt request is allowed
569: */
570: static bool MFP_InterruptRequest ( int Int , Uint8 Bit , Uint8 IPRx , Uint8 IMRx , Uint8 PriorityMaskA , Uint8 PriorityMaskB )
571: {
572: //fprintf ( stderr , "mfp int req %d %x %x %X %x %x\n" , Int , Bit , IPRx , IMRx , PriorityMaskA , PriorityMaskB );
1.1.1.9 root 573:
1.1.1.18 root 574: if ( ( IPRx & IMRx & Bit ) /* Interrupt is pending and not masked */
575: && ( MFP_Pending_Time[ Int ] <= MFP_Pending_Time_Min ) ) /* Process pending requests in chronological time */
576: {
577: /* Are any higher priority interrupts in service ? */
578: if ( ( ( MFP_ISRA & PriorityMaskA ) == 0 ) && ( ( MFP_ISRB & PriorityMaskB ) == 0 ) )
579: return true; /* No higher int in service */
580: }
1.1.1.2 root 581:
1.1.1.18 root 582: return false;
583: }
1.1.1.7 root 584:
585:
1.1.1.18 root 586: /*-----------------------------------------------------------------------*/
587: /**
588: * Check if any MFP interrupts can be serviced.
589: * @return MFP interrupt number for the highest interrupt allowed, else return -1.
590: */
591: static int MFP_CheckPendingInterrupts ( void )
592: {
593: if ( MFP_InterruptRequest ( MFP_INT_GPIP7 , MFP_GPIP7_BIT, MFP_IPRA, MFP_IMRA, 0x80, 0x00 ) ) /* Check MFP GPIP7 interrupt (bit 7) */
594: return MFP_INT_GPIP7;
595:
596: if ( MFP_InterruptRequest ( MFP_INT_TIMER_A , MFP_TIMER_A_BIT, MFP_IPRA, MFP_IMRA, 0xe0, 0x00 ) ) /* Check Timer A (bit 5) */
597: return MFP_INT_TIMER_A;
598:
599: if ( MFP_InterruptRequest ( MFP_INT_RCV_BUF_FULL , MFP_RCV_BUF_FULL_BIT, MFP_IPRA, MFP_IMRA, 0xf0, 0x00 ) ) /* Check Receive buffer full (bit 4) */
600: return MFP_INT_RCV_BUF_FULL;
1.1.1.7 root 601:
1.1.1.18 root 602: if ( MFP_InterruptRequest ( MFP_INT_TRN_BUF_EMPTY , MFP_TRN_BUF_EMPTY_BIT, MFP_IPRA, MFP_IMRA, 0xfc, 0x00 ) ) /* Check transmit buffer empty (bit 2) */
603: return MFP_INT_TRN_BUF_EMPTY;
1.1.1.12 root 604:
1.1.1.18 root 605: if ( MFP_InterruptRequest ( MFP_INT_TIMER_B , MFP_TIMER_B_BIT, MFP_IPRA, MFP_IMRA, 0xff, 0x00 ) ) /* Check Timer B (bit 0) */
606: return MFP_INT_TIMER_B;
1.1.1.7 root 607:
1.1.1.18 root 608: if ( MFP_InterruptRequest ( MFP_INT_GPIP5 , MFP_FDCHDC_BIT, MFP_IPRB, MFP_IMRB, 0xff, 0x80 ) ) /* Check FDC (bit 7) */
609: return MFP_INT_GPIP5;
1.1 root 610:
1.1.1.18 root 611: if ( MFP_InterruptRequest ( MFP_INT_ACIA , MFP_ACIA_BIT, MFP_IPRB, MFP_IMRB, 0xff, 0xc0 ) ) /* Check ACIA (Keyboard or MIDI) (bit 6) */
612: return MFP_INT_ACIA;
1.1 root 613:
1.1.1.18 root 614: if ( MFP_InterruptRequest ( MFP_INT_TIMER_C , MFP_TIMER_C_BIT, MFP_IPRB, MFP_IMRB, 0xff, 0xe0 ) ) /* Check Timer C (bit 5) */
615: return MFP_INT_TIMER_C;
1.1.1.12 root 616:
1.1.1.18 root 617: if ( MFP_InterruptRequest ( MFP_INT_TIMER_D , MFP_TIMER_D_BIT, MFP_IPRB, MFP_IMRB, 0xff, 0xf0 ) ) /* Check Timer D (bit 4) */
618: return MFP_INT_TIMER_D;
1.1.1.12 root 619:
1.1.1.18 root 620: if ( MFP_InterruptRequest ( MFP_INT_GPU_DONE , MFP_GPU_DONE_BIT, MFP_IPRB, MFP_IMRB, 0xff, 0xf8 ) ) /* Check GPU done (bit 3) */
621: return MFP_INT_GPU_DONE;
1.1.1.12 root 622:
1.1.1.18 root 623: if ( MFP_InterruptRequest ( MFP_INT_GPIP1 , MFP_GPIP1_BIT, MFP_IPRB, MFP_IMRB, 0xff, 0xfe ) ) /* Check (Falcon) Centronics ACK / (ST) RS232 DCD (bit 1) */
624: return MFP_INT_GPIP1;
1.1.1.12 root 625:
1.1.1.18 root 626: if ( MFP_InterruptRequest ( MFP_INT_GPIP0 , MFP_GPIP0_BIT, MFP_IPRB, MFP_IMRB, 0xff, 0xff ) ) /* Check Centronics BUSY (bit 0) */
627: return MFP_INT_GPIP0;
628:
629: return -1; /* No pending interrupt */
1.1 root 630: }
631:
1.1.1.2 root 632:
633: /*-----------------------------------------------------------------------*/
1.1.1.11 root 634: /**
1.1.1.18 root 635: * If interrupt channel is active, set pending bit so it can be serviced
636: * later.
637: * As internal timers are processed after the current CPU instruction was
638: * emulated, we use Interrupt_Delayed_Cycles to compute the precise time
639: * at which the timer expired (it could be during the previous instruction).
640: * This allows to correctly handle the 4 cycle MFP_IRQ delay in MFP_ProcessIRQ().
641: *
642: * As we can have several inputs during one CPU instruction, not necessarily
643: * sorted by Interrupt_Delayed_Cycles, we must call MFP_UpdateIRQ() only later
644: * in the main CPU loop, when all inputs were received, to choose the oldest
645: * input's event time.
646: */
647: void MFP_InputOnChannel ( int Interrupt , int Interrupt_Delayed_Cycles )
648: {
649: Uint8 *pEnableReg;
650: Uint8 *pPendingReg;
651: Uint8 *pMaskReg;
652: Uint8 Bit;
653:
654: //fprintf ( stderr , "mfp input %d delay %d clock %lld\n" , Interrupt , Interrupt_Delayed_Cycles , CyclesGlobalClockCounter );
655: Bit = MFP_ConvertIntNumber ( Interrupt , &pEnableReg , &pPendingReg , NULL , &pMaskReg );
656:
657: /* Input has occurred on MFP channel, set interrupt pending to request service when able */
658: if ( *pEnableReg & Bit )
659: {
660: /* Print traces if pending bits changed just before IACK */
661: if ( LOG_TRACE_LEVEL(TRACE_MFP_EXCEPTION) && ( CPU_IACK == true ) )
662: {
663: int FrameCycles, HblCounterVideo, LineCycles;
664: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
665: if ( *pPendingReg & Bit )
666: LOG_TRACE_PRINT("mfp input, pending set again during iack for int=%d, skip one interrupt video_cyc=%d %d@%d\n" ,
667: Interrupt , FrameCycles, LineCycles, HblCounterVideo );
668: else
669: LOG_TRACE_PRINT("mfp input, new pending set during iack for int=%d video_cyc=%d %d@%d\n" ,
670: Interrupt , FrameCycles, LineCycles, HblCounterVideo );
671: }
672:
673: /* Set pending bit and event's time */
674: *pPendingReg |= Bit;
675: MFP_Pending_Time[ Interrupt ] = CyclesGlobalClockCounter - Interrupt_Delayed_Cycles;
676:
677: /* Store the time of the most ancient non-masked pending=1 event */
678: if ( ( *pMaskReg & Bit ) && ( MFP_Pending_Time[ Interrupt ] < MFP_Pending_Time_Min ) )
679: MFP_Pending_Time_Min = MFP_Pending_Time[ Interrupt ];
680: }
1.1.1.11 root 681: else
1.1.1.18 root 682: *pPendingReg &= ~Bit; /* Clear bit */
683:
684: MFP_UpdateNeeded = true; /* Tell main CPU loop to call MFP_UpdateIRQ() */
1.1 root 685: }
686:
1.1.1.2 root 687:
688: /*-----------------------------------------------------------------------*/
1.1.1.11 root 689: /**
690: * Generate Timer A Interrupt when in Event Count mode
691: */
1.1 root 692: void MFP_TimerA_EventCount_Interrupt(void)
693: {
1.1.1.12 root 694: if (MFP_TA_MAINCOUNTER == 1) /* Timer expired? If so, generate interrupt */
1.1.1.11 root 695: {
1.1.1.12 root 696: MFP_TA_MAINCOUNTER = MFP_TADR; /* Reload timer from data register */
1.1 root 697:
1.1.1.11 root 698: /* Acknowledge in MFP circuit, pass bit,enable,pending */
1.1.1.18 root 699: MFP_InputOnChannel ( MFP_INT_TIMER_A , 0 );
1.1.1.11 root 700: }
701: else
1.1.1.12 root 702: {
703: MFP_TA_MAINCOUNTER--; /* Decrement timer main counter */
704: /* As MFP_TA_MAINCOUNTER is Uint8, when we decrement MFP_TA_MAINCOUNTER=0 */
705: /* we go to MFP_TA_MAINCOUNTER=255, which is the wanted behaviour because */
706: /* data reg = 0 means 256 in fact. So, the next 2 lines are redundant. */
707: /* if ( MFP_TA_MAINCOUNTER < 0 )
708: MFP_TA_MAINCOUNTER = 255;
709: */
710: }
1.1 root 711: }
712:
1.1.1.2 root 713:
714: /*-----------------------------------------------------------------------*/
1.1.1.11 root 715: /**
716: * Generate Timer B Interrupt when in Event Count mode
717: */
1.1.1.18 root 718: void MFP_TimerB_EventCount_Interrupt ( int Delayed_Cycles )
1.1 root 719: {
1.1.1.18 root 720: LOG_TRACE(TRACE_VIDEO_HBL , "mfp/video timer B new event count %d, delay=%d\n" , MFP_TB_MAINCOUNTER-1 , Delayed_Cycles );
1.1.1.14 root 721:
1.1.1.12 root 722: if (MFP_TB_MAINCOUNTER == 1) /* Timer expired? If so, generate interrupt */
1.1.1.11 root 723: {
1.1.1.12 root 724: MFP_TB_MAINCOUNTER = MFP_TBDR; /* Reload timer from data register */
1.1 root 725:
1.1.1.11 root 726: /* Acknowledge in MFP circuit, pass bit,enable,pending */
1.1.1.18 root 727: MFP_InputOnChannel ( MFP_INT_TIMER_B , Delayed_Cycles );
1.1.1.11 root 728: }
729: else
1.1.1.12 root 730: {
731: MFP_TB_MAINCOUNTER--; /* Decrement timer main counter */
732: /* As MFP_TB_MAINCOUNTER is Uint8, when we decrement MFP_TB_MAINCOUNTER=0 */
733: /* we go to MFP_TB_MAINCOUNTER=255, which is the wanted behaviour because */
734: /* data reg = 0 means 256 in fact. So, the next 2 lines are redundant. */
735: /* if ( MFP_TB_MAINCOUNTER < 0 )
736: MFP_TB_MAINCOUNTER = 255;
737: */
738: }
1.1 root 739: }
740:
1.1.1.2 root 741:
742: /*-----------------------------------------------------------------------*/
1.1.1.11 root 743: /**
744: * Start Timer A or B - EventCount mode is done in HBL handler to time correctly
745: */
1.1.1.14 root 746: static int MFP_StartTimer_AB(Uint8 TimerControl, Uint16 TimerData, interrupt_id Handler,
1.1.1.12 root 747: bool bFirstTimer, bool *pTimerCanResume)
1.1 root 748: {
1.1.1.11 root 749: int TimerClockCycles = 0;
750:
1.1.1.16 root 751:
752: /* When in pulse width mode, handle as in delay mode */
1.1.1.18 root 753: /* (this is not completely correct, as we should also handle GPIO 3/4 in pulse mode) */
1.1.1.16 root 754: if ( TimerControl > 8 )
755: {
756: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
757: {
758: int FrameCycles, HblCounterVideo, LineCycles;
759: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
760: LOG_TRACE_PRINT("mfp start AB handler=%d data=%d ctrl=%d timer_cyc=%d pending_cyc=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d pulse mode->delay mode\n",
761: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
762: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
763: }
764:
765: TimerControl &= 0x07; /* clear bit 3, pulse width mode -> delay mode */
766: }
767:
768:
1.1.1.11 root 769: /* Is timer in delay mode (ctrl = 0-7) ? */
770: /* If we are in event-count mode (ctrl = 8) ignore this (done on HBL) */
771: if (TimerControl <= 7)
772: {
773: /* Find number of CPU cycles for when timer is due (include preset
774: * and counter). As timer occurs very often we multiply by counter
775: * to speed up emulator */
776: if (TimerData == 0) /* Data=0 is actually Data=256 */
777: TimerData = 256;
778: TimerClockCycles = MFP_REG_TO_CYCLES ( TimerData, TimerControl );
779:
1.1.1.19! root 780: /* [NP] FIXME : Temporary fix for Lethal Xcess calibration routine to remove top border : */
! 781: /* the routine expects that the delay is not always stable, there must be a small */
! 782: /* jitter due to the clock difference between CPU and MFP */
! 783: if ( ( M68000_GetPC() == 0x14d78 ) && ( STMemory_ReadLong ( 0x14d6c ) == 0x11faff75 ) )
! 784: {
! 785: // fprintf ( stderr , "mfp add jitter %d\n" , TimerClockCycles );
! 786: TimerClockCycles += rand()%5-2; /* add jitter for wod2 */
! 787: }
! 788:
1.1.1.14 root 789: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
1.1.1.11 root 790: {
1.1.1.14 root 791: int FrameCycles, HblCounterVideo, LineCycles;
792: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
793: LOG_TRACE_PRINT("mfp start AB handler=%d data=%d ctrl=%d timer_cyc=%d pending_cyc=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d first=%s resume=%s\n",
794: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
795: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
796: bFirstTimer?"true":"false", *pTimerCanResume?"true":"false");
1.1.1.11 root 797: }
798:
799: /* And add to our internal interrupt list, if timer cycles is zero
800: * then timer is stopped */
1.1.1.15 root 801: CycInt_RemovePendingInterrupt(Handler);
1.1.1.11 root 802: if (TimerClockCycles)
803: {
1.1.1.14 root 804: if ((*pTimerCanResume == true) && (bFirstTimer == true)) /* we can't resume if the timer is auto restarting after an interrupt */
1.1.1.11 root 805: {
1.1.1.15 root 806: CycInt_ResumeStoppedInterrupt ( Handler );
1.1.1.11 root 807: }
808: else
809: {
810: int AddCurCycles = INT_CONVERT_TO_INTERNAL ( CurrentInstrCycles + nWaitStateCycles - 4 , INT_CPU_CYCLE );
811:
812: /* Start timer from now? If not continue timer using PendingCycleOver */
813: if (bFirstTimer)
1.1.1.15 root 814: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, AddCurCycles);
1.1.1.11 root 815: else
816: {
817: int TimerClockCyclesInternal = INT_CONVERT_TO_INTERNAL ( TimerClockCycles , INT_MFP_CYCLE );
818:
819: /* In case we miss more than one int, we must correct the delay for the next one */
820: if ( PendingCyclesOver > TimerClockCyclesInternal )
821: PendingCyclesOver = PendingCyclesOver % TimerClockCyclesInternal;
822:
1.1.1.15 root 823: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, -PendingCyclesOver);
1.1.1.11 root 824: }
1.1 root 825:
1.1.1.14 root 826: *pTimerCanResume = true; /* timer was set, resume is possible if stop/start it later */
1.1.1.11 root 827: }
828: }
1.1.1.12 root 829:
830: else /* Ctrl was 0 -> timer is stopped */
831: {
832: /* do nothing, only print some traces */
1.1.1.14 root 833: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
1.1.1.12 root 834: {
1.1.1.14 root 835: int FrameCycles, HblCounterVideo, LineCycles;
836: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
837: LOG_TRACE_PRINT("mfp stop AB handler=%d data=%d ctrl=%d timer_cyc=%d pending_cyc=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d first=%s resume=%s\n",
838: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
839: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
840: bFirstTimer?"true":"false", *pTimerCanResume?"true":"false");
1.1.1.12 root 841: }
842: }
1.1.1.11 root 843: }
1.1.1.12 root 844:
1.1.1.16 root 845:
846: else if (TimerControl == 8 ) /* event count mode */
1.1.1.11 root 847: {
848: /* Make sure no outstanding interrupts in list if channel is disabled */
1.1.1.15 root 849: CycInt_RemovePendingInterrupt(Handler);
1.1 root 850:
1.1.1.14 root 851: if ( Handler == INTERRUPT_MFP_TIMERB ) /* we're starting timer B event count mode */
852: {
853: /* Store start cycle for handling interrupt in video.c */
854: TimerBEventCountCycleStart = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
855: }
1.1.1.12 root 856:
1.1.1.14 root 857: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
1.1.1.12 root 858: {
1.1.1.14 root 859: int FrameCycles, HblCounterVideo, LineCycles;
860: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
861: LOG_TRACE_PRINT("mfp start AB handler=%d data=%d ctrl=%d timer_cyc=%d pending_cyc=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d first=%s resume=%s\n",
862: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
863: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
864: bFirstTimer?"true":"false", *pTimerCanResume?"true":"false");
1.1.1.12 root 865: }
866: }
867:
1.1.1.11 root 868: return TimerClockCycles;
1.1 root 869: }
870:
1.1.1.2 root 871:
872: /*-----------------------------------------------------------------------*/
1.1.1.11 root 873: /**
874: * Start Timer C or D
875: */
1.1.1.14 root 876: static int MFP_StartTimer_CD(Uint8 TimerControl, Uint16 TimerData, interrupt_id Handler,
1.1.1.12 root 877: bool bFirstTimer, bool *pTimerCanResume)
1.1 root 878: {
1.1.1.11 root 879: int TimerClockCycles = 0;
1.1 root 880:
1.1.1.11 root 881: /* Is timer in delay mode ? */
882: if ((TimerControl&0x7) != 0)
883: {
884: /* Find number of cycles for when timer is due (include preset and
885: * counter). As timer occurs very often we multiply by counter to
886: * speed up emulator */
887: if (TimerData == 0) /* Data=0 is actually Data=256 */
888: TimerData = 256;
889: TimerClockCycles = MFP_REG_TO_CYCLES ( TimerData, TimerControl );
890:
1.1.1.14 root 891: if ( LOG_TRACE_LEVEL( TRACE_MFP_START ) )
1.1.1.11 root 892: {
1.1.1.14 root 893: int FrameCycles, HblCounterVideo, LineCycles;
894: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
895: LOG_TRACE_PRINT("mfp start CD handler=%d data=%d ctrl=%d timer_cyc=%d pending_cyc=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d first=%s resume=%s\n" ,
1.1.1.11 root 896: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
1.1.1.14 root 897: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
1.1.1.11 root 898: bFirstTimer?"true":"false" , *pTimerCanResume?"true":"false" );
899: }
900:
901: /* And add to our internal interrupt list, if timer cycles is zero
902: * then timer is stopped */
1.1.1.15 root 903: CycInt_RemovePendingInterrupt(Handler);
1.1.1.11 root 904: if (TimerClockCycles)
905: {
1.1.1.14 root 906: if ((*pTimerCanResume == true) && (bFirstTimer == true)) /* we can't resume if the timer is auto restarting after an interrupt */
1.1.1.11 root 907: {
1.1.1.15 root 908: CycInt_ResumeStoppedInterrupt ( Handler );
1.1.1.11 root 909: }
910: else
911: {
912: int AddCurCycles = INT_CONVERT_TO_INTERNAL ( CurrentInstrCycles + nWaitStateCycles - 4 , INT_CPU_CYCLE );
913:
914: /* Start timer from now? If not continue timer using PendingCycleOver */
915: if (bFirstTimer)
1.1.1.15 root 916: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, AddCurCycles);
1.1.1.11 root 917: else
918: {
919: int TimerClockCyclesInternal = INT_CONVERT_TO_INTERNAL ( TimerClockCycles , INT_MFP_CYCLE );
920:
921: /* In case we miss more than one int, we must correct the delay for the next one */
922: if ( PendingCyclesOver > TimerClockCyclesInternal )
923: PendingCyclesOver = PendingCyclesOver % TimerClockCyclesInternal;
924:
1.1.1.15 root 925: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, -PendingCyclesOver);
1.1.1.11 root 926: }
927:
1.1.1.14 root 928: *pTimerCanResume = true; /* timer was set, resume is possible if stop/start it later */
1.1.1.11 root 929: }
930: }
931: }
1.1.1.12 root 932:
933: else /* timer control is 0 */
1.1.1.11 root 934: {
1.1.1.14 root 935: if ( LOG_TRACE_LEVEL( TRACE_MFP_START ) )
1.1.1.12 root 936: {
1.1.1.14 root 937: int FrameCycles, HblCounterVideo, LineCycles;
938: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
939: LOG_TRACE_PRINT("mfp stop CD handler=%d data=%d ctrl=%d timer_cyc=%d pending_cyc=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d first=%s resume=%s\n" ,
1.1.1.12 root 940: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
1.1.1.14 root 941: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
1.1.1.12 root 942: bFirstTimer?"true":"false" , *pTimerCanResume?"true":"false" );
943: }
944:
1.1.1.11 root 945: /* Make sure no outstanding interrupts in list if channel is disabled */
1.1.1.15 root 946: CycInt_RemovePendingInterrupt(Handler);
1.1.1.11 root 947: }
1.1 root 948:
1.1.1.11 root 949: return TimerClockCycles;
1.1 root 950: }
951:
1.1.1.2 root 952:
953: /*-----------------------------------------------------------------------*/
1.1.1.11 root 954: /**
955: * Read Timer A or B - If in EventCount MainCounter already has correct value
956: */
1.1.1.14 root 957: static Uint8 MFP_ReadTimer_AB(Uint8 TimerControl, Uint8 MainCounter, int TimerCycles, interrupt_id Handler, bool TimerIsStopping)
1.1 root 958: {
1.1.1.12 root 959: // int TimerCyclesPassed;
1.1 root 960:
1.1.1.11 root 961: /* Find TimerAB count, if no interrupt or not in delay mode assume
962: * in Event Count mode so already up-to-date as kept by HBL */
1.1.1.15 root 963: if (CycInt_InterruptActive(Handler) && (TimerControl > 0) && (TimerControl <= 7))
1.1.1.11 root 964: {
965: /* Find cycles passed since last interrupt */
1.1.1.15 root 966: //TimerCyclesPassed = TimerCycles - CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE );
967: MainCounter = MFP_CYCLE_TO_REG ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ), TimerControl );
1.1.1.11 root 968: //fprintf ( stderr , "mfp read AB passed %d count %d\n" , TimerCyclesPassed, MainCounter );
969: }
970:
1.1.1.12 root 971: /* If the timer is stopped when the internal mfp data reg is already < 1 */
972: /* then the data reg will be 0 (=256) next time the timer will be restarted */
973: /* if no write is made to the data reg before */
974: if ( TimerIsStopping )
975: {
1.1.1.15 root 976: if ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ) < MFP_REG_TO_CYCLES ( 1 , TimerControl ) )
1.1.1.12 root 977: {
978: MainCounter = 0; /* internal mfp counter becomes 0 (=256) */
1.1.1.14 root 979: LOG_TRACE(TRACE_MFP_READ , "mfp read AB handler=%d stopping timer while data reg between 1 and 0 : forcing data to 256\n" ,
1.1.1.12 root 980: Handler );
981: }
982: }
983:
1.1.1.14 root 984: if ( LOG_TRACE_LEVEL( TRACE_MFP_READ ) )
1.1.1.11 root 985: {
1.1.1.14 root 986: int FrameCycles, HblCounterVideo, LineCycles;
987: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
988: LOG_TRACE_PRINT("mfp read AB handler=%d data=%d ctrl=%d timer_cyc=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" ,
1.1.1.11 root 989: Handler, MainCounter, TimerControl, TimerCycles,
1.1.1.14 root 990: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 991: }
1.1.1.6 root 992:
1.1.1.11 root 993: return MainCounter;
1.1 root 994: }
995:
1.1.1.2 root 996:
997: /*-----------------------------------------------------------------------*/
1.1.1.11 root 998: /**
999: * Read Timer C or D
1000: */
1.1.1.14 root 1001: static Uint8 MFP_ReadTimerCD(Uint8 TimerControl, Uint8 TimerData, Uint8 MainCounter, int TimerCycles, interrupt_id Handler, bool TimerIsStopping)
1.1 root 1002: {
1.1.1.12 root 1003: // int TimerCyclesPassed;
1.1 root 1004:
1.1.1.11 root 1005: /* Find TimerCD count. If timer is off, MainCounter already contains
1006: * the latest value */
1.1.1.15 root 1007: if (CycInt_InterruptActive(Handler))
1.1.1.11 root 1008: {
1009: /* Find cycles passed since last interrupt */
1.1.1.15 root 1010: //TimerCyclesPassed = TimerCycles - CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE );
1011: MainCounter = MFP_CYCLE_TO_REG ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ), TimerControl);
1.1.1.11 root 1012: //fprintf ( stderr , "mfp read CD passed %d count %d\n" , TimerCyclesPassed, MainCounter );
1013: }
1014:
1.1.1.12 root 1015: /* If the timer is stopped when the internal mfp data reg is already < 1 */
1016: /* then the data reg will be 0 (=256) next time the timer will be restarted */
1017: /* if no write is made to the data reg before */
1018: if ( TimerIsStopping )
1019: {
1.1.1.15 root 1020: if ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ) < MFP_REG_TO_CYCLES ( 1 , TimerControl ) )
1.1.1.12 root 1021: {
1022: MainCounter = 0; /* internal mfp counter becomes 0 (=256) */
1.1.1.14 root 1023: LOG_TRACE(TRACE_MFP_READ , "mfp read CD handler=%d stopping timer while data reg between 1 and 0 : forcing data to 256\n" ,
1.1.1.12 root 1024: Handler );
1025: }
1026: }
1027:
1.1.1.14 root 1028: if ( LOG_TRACE_LEVEL( TRACE_MFP_READ ) )
1.1.1.11 root 1029: {
1.1.1.14 root 1030: int FrameCycles, HblCounterVideo, LineCycles;
1031: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1032: LOG_TRACE_PRINT("mfp read CD handler=%d data=%d ctrl=%d timer_cyc=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" ,
1.1.1.11 root 1033: Handler, MainCounter, TimerControl, TimerCycles,
1.1.1.14 root 1034: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 1035: }
1.1 root 1036:
1.1.1.11 root 1037: return MainCounter;
1.1 root 1038: }
1039:
1.1.1.2 root 1040:
1041: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1042: /**
1043: * Start Timer A
1044: */
1.1.1.10 root 1045: static void MFP_StartTimerA(void)
1.1 root 1046: {
1.1.1.11 root 1047: TimerAClockCycles = MFP_StartTimer_AB(MFP_TACR, MFP_TA_MAINCOUNTER,
1.1.1.14 root 1048: INTERRUPT_MFP_TIMERA, true, &TimerACanResume);
1.1 root 1049: }
1050:
1051:
1.1.1.2 root 1052: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1053: /**
1054: * Read Timer A
1055: */
1.1.1.12 root 1056: static void MFP_ReadTimerA(bool TimerIsStopping)
1.1 root 1057: {
1.1.1.11 root 1058: MFP_TA_MAINCOUNTER = MFP_ReadTimer_AB(MFP_TACR, MFP_TA_MAINCOUNTER,
1.1.1.12 root 1059: TimerAClockCycles, INTERRUPT_MFP_TIMERA, TimerIsStopping);
1.1 root 1060: }
1061:
1062:
1.1.1.2 root 1063: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1064: /**
1065: * Start Timer B
1066: * (This does not start the EventCount mode time as this is taken care
1067: * of by the HBL)
1068: */
1.1.1.10 root 1069: static void MFP_StartTimerB(void)
1.1 root 1070: {
1.1.1.11 root 1071: TimerBClockCycles = MFP_StartTimer_AB(MFP_TBCR, MFP_TB_MAINCOUNTER,
1.1.1.14 root 1072: INTERRUPT_MFP_TIMERB, true, &TimerBCanResume);
1.1 root 1073: }
1074:
1075:
1.1.1.2 root 1076: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1077: /**
1078: * Read Timer B
1079: */
1.1.1.12 root 1080: static void MFP_ReadTimerB(bool TimerIsStopping)
1.1 root 1081: {
1.1.1.11 root 1082: MFP_TB_MAINCOUNTER = MFP_ReadTimer_AB(MFP_TBCR, MFP_TB_MAINCOUNTER,
1.1.1.12 root 1083: TimerBClockCycles, INTERRUPT_MFP_TIMERB, TimerIsStopping);
1.1 root 1084: }
1085:
1086:
1.1.1.2 root 1087: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1088: /**
1089: * Start Timer C
1090: */
1.1.1.10 root 1091: static void MFP_StartTimerC(void)
1.1 root 1092: {
1.1.1.11 root 1093: TimerCClockCycles = MFP_StartTimer_CD((MFP_TCDCR>>4)&7, MFP_TC_MAINCOUNTER,
1.1.1.14 root 1094: INTERRUPT_MFP_TIMERC , true, &TimerCCanResume);
1.1 root 1095: }
1096:
1097:
1.1.1.2 root 1098: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1099: /**
1100: * Read Timer C
1101: */
1.1.1.12 root 1102: static void MFP_ReadTimerC(bool TimerIsStopping)
1.1 root 1103: {
1.1.1.11 root 1104: MFP_TC_MAINCOUNTER = MFP_ReadTimerCD((MFP_TCDCR>>4)&7, MFP_TCDR, MFP_TC_MAINCOUNTER,
1.1.1.12 root 1105: TimerCClockCycles, INTERRUPT_MFP_TIMERC, TimerIsStopping);
1.1 root 1106: }
1107:
1108:
1.1.1.2 root 1109: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1110: /**
1111: * Start Timer D
1112: */
1.1.1.10 root 1113: static void MFP_StartTimerD(void)
1.1 root 1114: {
1.1.1.11 root 1115: TimerDClockCycles = MFP_StartTimer_CD(MFP_TCDCR&7, MFP_TD_MAINCOUNTER,
1.1.1.14 root 1116: INTERRUPT_MFP_TIMERD, true, &TimerDCanResume);
1.1 root 1117: }
1118:
1119:
1.1.1.2 root 1120: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1121: /**
1122: * Read Timer D
1123: */
1.1.1.12 root 1124: static void MFP_ReadTimerD(bool TimerIsStopping)
1.1 root 1125: {
1.1.1.11 root 1126: MFP_TD_MAINCOUNTER = MFP_ReadTimerCD(MFP_TCDCR&7, MFP_TDDR, MFP_TD_MAINCOUNTER,
1.1.1.12 root 1127: TimerDClockCycles, INTERRUPT_MFP_TIMERD, TimerIsStopping);
1.1 root 1128: }
1129:
1130:
1.1.1.2 root 1131: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1132: /**
1133: * Handle Timer A Interrupt
1134: */
1.1 root 1135: void MFP_InterruptHandler_TimerA(void)
1136: {
1.1.1.11 root 1137: /* Number of internal cycles we went over for this timer ( <= 0 ),
1138: * used when timer expires and needs to be restarted */
1139: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1140:
1141: /* Remove this interrupt from list and re-order */
1.1.1.15 root 1142: CycInt_AcknowledgeInterrupt();
1.1 root 1143:
1.1.1.11 root 1144: /* Acknowledge in MFP circuit, pass bit,enable,pending */
1145: if ((MFP_TACR&0xf) != 0) /* Is timer OK? */
1.1.1.18 root 1146: MFP_InputOnChannel ( MFP_INT_TIMER_A , 0 );
1.1 root 1147:
1.1.1.11 root 1148: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 1149: TimerAClockCycles = MFP_StartTimer_AB(MFP_TACR, MFP_TADR, INTERRUPT_MFP_TIMERA, false, &TimerACanResume);
1.1 root 1150: }
1151:
1152:
1.1.1.2 root 1153: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1154: /**
1155: * Handle Timer B Interrupt
1156: */
1.1 root 1157: void MFP_InterruptHandler_TimerB(void)
1158: {
1.1.1.11 root 1159: /* Number of internal cycles we went over for this timer ( <= 0 ),
1160: * used when timer expires and needs to be restarted */
1161: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1162:
1163: /* Remove this interrupt from list and re-order */
1.1.1.15 root 1164: CycInt_AcknowledgeInterrupt();
1.1 root 1165:
1.1.1.11 root 1166: /* Acknowledge in MFP circuit, pass bit, enable, pending */
1167: if ((MFP_TBCR&0xf) != 0) /* Is timer OK? */
1.1.1.18 root 1168: MFP_InputOnChannel ( MFP_INT_TIMER_B , 0 );
1.1 root 1169:
1.1.1.11 root 1170: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 1171: TimerBClockCycles = MFP_StartTimer_AB(MFP_TBCR, MFP_TBDR, INTERRUPT_MFP_TIMERB, false, &TimerBCanResume);
1.1 root 1172: }
1173:
1174:
1.1.1.2 root 1175: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1176: /**
1177: * Handle Timer C Interrupt
1178: */
1.1 root 1179: void MFP_InterruptHandler_TimerC(void)
1180: {
1.1.1.11 root 1181: /* Number of internal cycles we went over for this timer ( <= 0 ),
1182: * used when timer expires and needs to be restarted */
1183: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1.1 root 1184:
1.1.1.11 root 1185: /* Remove this interrupt from list and re-order */
1.1.1.15 root 1186: CycInt_AcknowledgeInterrupt();
1.1 root 1187:
1.1.1.11 root 1188: /* Acknowledge in MFP circuit, pass bit, enable, pending */
1189: if ((MFP_TCDCR&0x70) != 0) /* Is timer OK? */
1.1.1.18 root 1190: MFP_InputOnChannel ( MFP_INT_TIMER_C , 0 );
1.1.1.11 root 1191:
1192: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 1193: TimerCClockCycles = MFP_StartTimer_CD((MFP_TCDCR>>4)&7, MFP_TCDR, INTERRUPT_MFP_TIMERC, false, &TimerCCanResume);
1.1 root 1194: }
1195:
1196:
1.1.1.2 root 1197: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1198: /**
1199: * Handle Timer D Interrupt
1200: */
1.1 root 1201: void MFP_InterruptHandler_TimerD(void)
1202: {
1.1.1.11 root 1203: /* Number of internal cycles we went over for this timer ( <= 0 ),
1204: * used when timer expires and needs to be restarted */
1205: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1206:
1207: /* Remove this interrupt from list and re-order */
1.1.1.15 root 1208: CycInt_AcknowledgeInterrupt();
1.1.1.11 root 1209:
1210: /* Acknowledge in MFP circuit, pass bit, enable, pending */
1211: if ((MFP_TCDCR&0x07) != 0) /* Is timer OK? */
1.1.1.18 root 1212: MFP_InputOnChannel ( MFP_INT_TIMER_D , 0 );
1.1.1.11 root 1213:
1214: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 1215: TimerDClockCycles = MFP_StartTimer_CD(MFP_TCDCR&7, MFP_TDDR, INTERRUPT_MFP_TIMERD, false, &TimerDCanResume);
1.1 root 1216: }
1217:
1.1.1.8 root 1218:
1219:
1220: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1221: /**
1222: * Handle read from GPIP pins register (0xfffa01).
1223: *
1224: * - Bit 0 is the BUSY signal of the printer port, it is SET if no printer
1225: * is connected or on BUSY. Therefor we should assume it to be 0 in Hatari
1226: * when a printer is emulated.
1227: * - Bit 1 is used for RS232: DCD
1228: * - Bit 2 is used for RS232: CTS
1229: * - Bit 3 is used by the blitter for signalling when its done.
1230: * - Bit 4 is used by the ACIAs.
1231: * - Bit 5 is used by the floppy controller / ACSI DMA
1232: * - Bit 6 is used for RS232: RI
1233: * - Bit 7 is monochrome monitor detection signal. On STE it is also XORed with
1234: * the DMA sound play bit.
1235: */
1.1.1.8 root 1236: void MFP_GPIP_ReadByte(void)
1237: {
1.1.1.10 root 1238: M68000_WaitState(4);
1239:
1.1.1.8 root 1240: if (!bUseHighRes)
1.1.1.11 root 1241: MFP_GPIP |= 0x80; /* Color monitor -> set top bit */
1.1.1.9 root 1242: else
1243: MFP_GPIP &= ~0x80;
1.1.1.15 root 1244:
1.1.1.9 root 1245: if (nDmaSoundControl & DMASNDCTRL_PLAY)
1.1.1.15 root 1246: MFP_GPIP ^= 0x80; /* Top bit is XORed with DMA sound control play bit (Ste/TT emulation mode)*/
1247: if (nCbar_DmaSoundControl & CROSSBAR_SNDCTRL_PLAY || nCbar_DmaSoundControl & CROSSBAR_SNDCTRL_RECORD)
1248: MFP_GPIP ^= 0x80; /* Top bit is XORed with Falcon crossbar DMA sound control play bit (Falcon emulation mode) */
1.1.1.9 root 1249:
1250: if (ConfigureParams.Printer.bEnablePrinting)
1251: {
1252: /* Signal that printer is not busy */
1253: MFP_GPIP &= ~1;
1254: }
1255: else
1256: {
1257: MFP_GPIP |= 1;
1258:
1259: /* Printer BUSY bit is also used by parallel port joystick adapters as fire button */
1260: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT1].nJoystickMode != JOYSTICK_DISABLED)
1261: {
1262: /* Fire pressed? */
1263: if (Joy_GetStickData(JOYID_PARPORT1) & 0x80)
1264: MFP_GPIP &= ~1;
1265: }
1266: }
1.1.1.8 root 1267:
1.1.1.9 root 1268: IoMem[0xfffa01] = MFP_GPIP;
1.1.1.13 root 1269:
1.1.1.14 root 1270: if ( LOG_TRACE_LEVEL( TRACE_MFP_READ ) )
1.1.1.13 root 1271: {
1.1.1.14 root 1272: int FrameCycles, HblCounterVideo, LineCycles;
1273: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1274: LOG_TRACE_PRINT("mfp read gpip fa01=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1275: MFP_GPIP, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.13 root 1276: }
1.1.1.8 root 1277: }
1278:
1279: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1280: /**
1281: * Handle read from active edge register (0xfffa03).
1282: */
1.1.1.8 root 1283: void MFP_ActiveEdge_ReadByte(void)
1284: {
1.1.1.10 root 1285: M68000_WaitState(4);
1286:
1.1.1.8 root 1287: IoMem[0xfffa03] = MFP_AER;
1288: }
1289:
1290: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1291: /**
1292: * Handle read from data direction register (0xfffa05).
1293: */
1.1.1.8 root 1294: void MFP_DataDirection_ReadByte(void)
1295: {
1.1.1.10 root 1296: M68000_WaitState(4);
1297:
1.1.1.8 root 1298: IoMem[0xfffa05] = MFP_DDR;
1299: }
1300:
1301: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1302: /**
1.1.1.18 root 1303: * Handle read from interrupt enable register A (0xfffa07).
1.1.1.11 root 1304: */
1.1.1.8 root 1305: void MFP_EnableA_ReadByte(void)
1306: {
1.1.1.10 root 1307: M68000_WaitState(4);
1308:
1.1.1.8 root 1309: IoMem[0xfffa07] = MFP_IERA;
1310: }
1311:
1312: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1313: /**
1.1.1.18 root 1314: * Handle read from interrupt enable register B (0xfffa09).
1.1.1.11 root 1315: */
1.1.1.8 root 1316: void MFP_EnableB_ReadByte(void)
1317: {
1.1.1.10 root 1318: M68000_WaitState(4);
1319:
1.1.1.8 root 1320: IoMem[0xfffa09] = MFP_IERB;
1321: }
1322:
1323: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1324: /**
1.1.1.18 root 1325: * Handle read from interrupt pending register A (0xfffa0b).
1.1.1.11 root 1326: */
1.1.1.8 root 1327: void MFP_PendingA_ReadByte(void)
1328: {
1.1.1.10 root 1329: M68000_WaitState(4);
1330:
1.1.1.8 root 1331: IoMem[0xfffa0b] = MFP_IPRA;
1332: }
1333:
1334: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1335: /**
1.1.1.18 root 1336: * Handle read from interrupt pending register A (0xfffa0d).
1.1.1.11 root 1337: */
1.1.1.8 root 1338: void MFP_PendingB_ReadByte(void)
1339: {
1.1.1.10 root 1340: M68000_WaitState(4);
1341:
1.1.1.8 root 1342: IoMem[0xfffa0d] = MFP_IPRB;
1343: }
1344:
1345: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1346: /**
1.1.1.18 root 1347: * Handle read from interrupt in service register A (0xfffa0f).
1.1.1.11 root 1348: */
1.1.1.8 root 1349: void MFP_InServiceA_ReadByte(void)
1350: {
1.1.1.10 root 1351: M68000_WaitState(4);
1352:
1.1.1.8 root 1353: IoMem[0xfffa0f] = MFP_ISRA;
1354: }
1355:
1356: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1357: /**
1.1.1.18 root 1358: * Handle read from interrupt in service register B (0xfffa11).
1.1.1.11 root 1359: */
1.1.1.8 root 1360: void MFP_InServiceB_ReadByte(void)
1361: {
1.1.1.10 root 1362: M68000_WaitState(4);
1363:
1.1.1.8 root 1364: IoMem[0xfffa11] = MFP_ISRB;
1365: }
1366:
1367: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1368: /**
1.1.1.18 root 1369: * Handle read from interrupt mask register A (0xfffa13).
1.1.1.11 root 1370: */
1.1.1.8 root 1371: void MFP_MaskA_ReadByte(void)
1372: {
1.1.1.10 root 1373: M68000_WaitState(4);
1374:
1.1.1.8 root 1375: IoMem[0xfffa13] = MFP_IMRA;
1376: }
1377:
1378: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1379: /**
1.1.1.18 root 1380: * Handle read from interrupt mask register B (0xfffa15).
1.1.1.11 root 1381: */
1.1.1.8 root 1382: void MFP_MaskB_ReadByte(void)
1383: {
1.1.1.10 root 1384: M68000_WaitState(4);
1385:
1.1.1.8 root 1386: IoMem[0xfffa15] = MFP_IMRB;
1387: }
1388:
1389: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1390: /**
1391: * Handle read from MFP vector register (0xfffa17).
1392: */
1.1.1.8 root 1393: void MFP_VectorReg_ReadByte(void)
1394: {
1.1.1.10 root 1395: M68000_WaitState(4);
1396:
1.1.1.8 root 1397: IoMem[0xfffa17] = MFP_VR;
1398: }
1399:
1400: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1401: /**
1402: * Handle read from timer A control register (0xfffa19).
1403: */
1.1.1.8 root 1404: void MFP_TimerACtrl_ReadByte(void)
1405: {
1.1.1.10 root 1406: M68000_WaitState(4);
1407:
1.1.1.8 root 1408: IoMem[0xfffa19] = MFP_TACR;
1409: }
1410:
1411: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1412: /**
1413: * Handle read from timer B control register (0xfffa1b).
1414: */
1.1.1.8 root 1415: void MFP_TimerBCtrl_ReadByte(void)
1416: {
1.1.1.10 root 1417: M68000_WaitState(4);
1418:
1.1.1.8 root 1419: IoMem[0xfffa1b] = MFP_TBCR;
1420: }
1421:
1422: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1423: /**
1424: * Handle read from timer C/D control register (0xfffa1d).
1425: */
1.1.1.8 root 1426: void MFP_TimerCDCtrl_ReadByte(void)
1427: {
1.1.1.10 root 1428: M68000_WaitState(4);
1429:
1.1.1.8 root 1430: IoMem[0xfffa1d] = MFP_TCDCR;
1431: }
1432:
1433: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1434: /**
1435: * Handle read from timer A data register (0xfffa1f).
1436: */
1.1.1.8 root 1437: void MFP_TimerAData_ReadByte(void)
1438: {
1.1.1.10 root 1439: M68000_WaitState(4);
1440:
1.1.1.12 root 1441: if (MFP_TACR != 8) /* Is event count? Need to re-calculate counter */
1.1.1.14 root 1442: MFP_ReadTimerA(false); /* Stores result in 'MFP_TA_MAINCOUNTER' */
1.1.1.8 root 1443:
1444: IoMem[0xfffa1f] = MFP_TA_MAINCOUNTER;
1445: }
1446:
1447: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1448: /**
1449: * Handle read from timer B data register (0xfffa21).
1450: */
1.1.1.8 root 1451: void MFP_TimerBData_ReadByte(void)
1452: {
1.1.1.12 root 1453: Uint8 TB_count;
1454:
1.1.1.10 root 1455: M68000_WaitState(4);
1456:
1.1.1.13 root 1457: /* Is it event count mode or not? */
1458: if (MFP_TBCR != 8)
1459: {
1460: /* Not event count mode, so handle as normal timer
1461: * and store result in 'MFP_TB_MAINCOUNTER' */
1.1.1.14 root 1462: MFP_ReadTimerB(false);
1.1.1.13 root 1463: }
1464: else if (bUseVDIRes)
1465: {
1466: /* HBLs are disabled in VDI mode, but TOS expects to read a 1. */
1467: MFP_TB_MAINCOUNTER = 1;
1468: }
1.1.1.12 root 1469: /* Special case when reading $fffa21, we need to test if the current read instruction */
1470: /* overlaps the horizontal video position where $fffa21 is changed */
1471: else
1472: {
1.1.1.14 root 1473: int FrameCycles, HblCounterVideo;
1.1.1.12 root 1474: int pos_start , pos_read;
1475:
1476: /* Cycle position of the start of the current instruction */
1.1.1.14 root 1477: //pos_start = nFrameCycles % nCyclesPerLine;
1478: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &pos_start );
1.1.1.12 root 1479: /* Cycle position of the read for the current instruction (approximatively, we consider */
1480: /* the read happens after 4 cycles (due to MFP wait states in that case)) */
1481: /* This is quite a hack, but hard to do without proper 68000 read cycle emulation */
1482: if ( CurrentInstrCycles <= 8 ) /* move.b (a0),d0 / cmp.b (a0),d0 ... */
1483: pos_read = pos_start + 4; /* wait state */
1484: else /* cmp.b $fa21.w,d0 (BIG Demo) ... */
1485: pos_read = pos_start + 8; /* more time needed to compute the effective address */
1486:
1487: TB_count = MFP_TB_MAINCOUNTER; /* default value */
1488:
1489: /* If Timer B's change happens before the read cycle of the current instruction, we must return */
1490: /* the current value - 1 (because MFP_TimerB_EventCount_Interrupt was not called yet) */
1491: if ( (nHBL >= nStartHBL ) && ( nHBL < nEndHBL ) /* ensure display is ON and timer B can happen */
1492: && ( LineTimerBCycle > pos_start ) && ( LineTimerBCycle < pos_read ) )
1493: {
1.1.1.14 root 1494: LOG_TRACE(TRACE_MFP_READ , "mfp read TB overlaps pos_start=%d TB_pos=%d pos_read=%d nHBL=%d \n",
1495: pos_start, LineTimerBCycle, pos_read , HblCounterVideo );
1.1.1.12 root 1496:
1497: TB_count--;
1498: if ( TB_count == 0 ) /* going from 1 to 0 : timer restart, reload data reg */
1499: TB_count = MFP_TBDR;
1500: /* Going from 0 to -1 : data reg is in fact going from 256 to 255. As TB_count is Uint8, */
1501: /* this is already what we get when we decrement TB_count=0. So, the next 2 lines are redundant. */
1502: /* else if ( TB_count < 0 )
1503: TB_count = 255;
1504: */
1505: }
1506:
1.1.1.14 root 1507: LOG_TRACE(TRACE_MFP_READ , "mfp read TB data=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" ,
1508: TB_count, FrameCycles, pos_start, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1509: IoMem[0xfffa21] = TB_count;
1510: return;
1511: }
1.1.1.8 root 1512:
1513: IoMem[0xfffa21] = MFP_TB_MAINCOUNTER;
1514: }
1515:
1516: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1517: /**
1518: * Handle read from timer C data register (0xfffa23).
1519: */
1.1.1.8 root 1520: void MFP_TimerCData_ReadByte(void)
1521: {
1.1.1.10 root 1522: M68000_WaitState(4);
1523:
1.1.1.14 root 1524: MFP_ReadTimerC(false); /* Stores result in 'MFP_TC_MAINCOUNTER' */
1.1.1.8 root 1525:
1526: IoMem[0xfffa23] = MFP_TC_MAINCOUNTER;
1527: }
1528:
1529: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1530: /**
1531: * Handle read from timer D data register (0xfffa25).
1532: */
1.1.1.8 root 1533: void MFP_TimerDData_ReadByte(void)
1534: {
1.1.1.11 root 1535: Uint32 pc = M68000_GetPC();
1.1.1.8 root 1536:
1.1.1.10 root 1537: M68000_WaitState(4);
1538:
1.1.1.8 root 1539: if (ConfigureParams.System.bPatchTimerD && pc >= TosAddress && pc <= TosAddress + TosSize)
1540: {
1541: /* Trick the tos to believe it was changed: */
1542: IoMem[0xfffa25] = nTimerDFakeValue;
1543: }
1544: else
1545: {
1.1.1.14 root 1546: MFP_ReadTimerD(false); /* Stores result in 'MFP_TD_MAINCOUNTER' */
1.1.1.8 root 1547: IoMem[0xfffa25] = MFP_TD_MAINCOUNTER;
1548: }
1549: }
1550:
1551:
1552: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1553: /**
1554: * Handle write to GPIP register (0xfffa01).
1555: */
1.1.1.8 root 1556: void MFP_GPIP_WriteByte(void)
1557: {
1.1.1.10 root 1558: M68000_WaitState(4);
1559:
1.1.1.8 root 1560: /* Nothing... */
1561: /*fprintf(stderr, "Write to GPIP: %x\n", (int)IoMem[0xfffa01]);*/
1562: /*MFP_GPIP = IoMem[0xfffa01];*/ /* TODO: What are the GPIP pins good for? */
1563: }
1564:
1565: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1566: /**
1.1.1.14 root 1567: * Handle write to AER (0xfffa03)
1568: * Bit 3 of AER is linked to timer B in event count mode.
1569: * If bit 3=0, timer B triggers on end of line when display goes off.
1570: * If bit 3=1, timer B triggers on start of line when display goes on.
1.1.1.11 root 1571: */
1.1.1.8 root 1572: void MFP_ActiveEdge_WriteByte(void)
1573: {
1.1.1.14 root 1574: int FrameCycles, HblCounterVideo, LineCycles;
1575: int LineTimerBCycle_old = LineTimerBCycle;
1576:
1577: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1578:
1.1.1.10 root 1579: M68000_WaitState(4);
1580:
1.1.1.14 root 1581: /* 0 -> 1, timer B is now counting start of line events (cycle 56+28) */
1582: if ( ( ( MFP_AER & ( 1 << 3 ) ) == 0 ) && ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) != 1 ) )
1583: {
1584: LineTimerBCycle = Video_TimerB_GetPos ( HblCounterVideo );
1585:
1586: LOG_TRACE((TRACE_VIDEO_HBL | TRACE_MFP_WRITE),
1587: "mfp/video AER bit 3 0->1, timer B triggers on start of line,"
1588: " old_pos=%d new_pos=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n",
1589: LineTimerBCycle_old, LineTimerBCycle,
1590: FrameCycles, LineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles);
1591: }
1592:
1593: /* 1 -> 0, timer B is now counting end of line events (cycle 376+28) */
1594: else if ( ( ( MFP_AER & ( 1 << 3 ) ) != 0 ) && ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 ) )
1595: {
1596: LineTimerBCycle = Video_TimerB_GetPos ( HblCounterVideo );
1597:
1598: LOG_TRACE((TRACE_VIDEO_HBL | TRACE_MFP_WRITE),
1599: "mfp/video AER bit 3 1->0, timer B triggers on end of line,"
1600: " old_pos=%d new_pos=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n",
1601: LineTimerBCycle_old, LineTimerBCycle,
1602: FrameCycles, LineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles);
1603: }
1604:
1605: /* Timer B position changed, update the next interrupt */
1606: if ( LineTimerBCycle_old != LineTimerBCycle )
1607: Video_AddInterruptTimerB ( LineTimerBCycle );
1608:
1.1.1.8 root 1609: MFP_AER = IoMem[0xfffa03];
1610: }
1611:
1612: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1613: /**
1614: * Handle write to data direction register (0xfffa05).
1615: */
1.1.1.8 root 1616: void MFP_DataDirection_WriteByte(void)
1617: {
1.1.1.10 root 1618: M68000_WaitState(4);
1619:
1.1.1.8 root 1620: MFP_DDR = IoMem[0xfffa05];
1621: }
1622:
1623: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1624: /**
1625: * Handle write to interrupt enable register A (0xfffa07).
1626: */
1.1.1.8 root 1627: void MFP_EnableA_WriteByte(void)
1628: {
1.1.1.10 root 1629: M68000_WaitState(4);
1630:
1.1.1.8 root 1631: MFP_IERA = IoMem[0xfffa07];
1632: MFP_IPRA &= MFP_IERA;
1.1.1.18 root 1633: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1634: }
1635:
1636: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1637: /**
1638: * Handle write to interrupt enable register B (0xfffa09).
1639: */
1.1.1.8 root 1640: void MFP_EnableB_WriteByte(void)
1641: {
1.1.1.10 root 1642: M68000_WaitState(4);
1643:
1.1.1.8 root 1644: MFP_IERB = IoMem[0xfffa09];
1645: MFP_IPRB &= MFP_IERB;
1.1.1.18 root 1646: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1647: }
1648:
1649: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1650: /**
1651: * Handle write to interrupt pending register A (0xfffa0b).
1652: */
1.1.1.8 root 1653: void MFP_PendingA_WriteByte(void)
1654: {
1.1.1.10 root 1655: M68000_WaitState(4);
1656:
1.1.1.18 root 1657: MFP_IPRA &= IoMem[0xfffa0b]; /* Cannot set pending bits - only clear via software */
1658: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1659: }
1660:
1661: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1662: /**
1663: * Handle write to interrupt pending register B (0xfffa0d).
1664: */
1.1.1.8 root 1665: void MFP_PendingB_WriteByte(void)
1666: {
1.1.1.10 root 1667: M68000_WaitState(4);
1668:
1.1.1.18 root 1669: MFP_IPRB &= IoMem[0xfffa0d]; /* Cannot set pending bits - only clear via software */
1670: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1671: }
1672:
1673: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1674: /**
1675: * Handle write to interrupt in service register A (0xfffa0f).
1676: */
1.1.1.8 root 1677: void MFP_InServiceA_WriteByte(void)
1678: {
1.1.1.10 root 1679: M68000_WaitState(4);
1680:
1.1.1.18 root 1681: MFP_ISRA &= IoMem[0xfffa0f]; /* Cannot set in-service bits - only clear via software */
1682: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1683: }
1684:
1685: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1686: /**
1687: * Handle write to interrupt in service register B (0xfffa11).
1688: */
1.1.1.8 root 1689: void MFP_InServiceB_WriteByte(void)
1690: {
1.1.1.10 root 1691: M68000_WaitState(4);
1692:
1.1.1.18 root 1693: MFP_ISRB &= IoMem[0xfffa11]; /* Cannot set in-service bits - only clear via software */
1694: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1695: }
1696:
1697: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1698: /**
1699: * Handle write to interrupt mask register A (0xfffa13).
1700: */
1.1.1.8 root 1701: void MFP_MaskA_WriteByte(void)
1702: {
1.1.1.10 root 1703: M68000_WaitState(4);
1704:
1.1.1.8 root 1705: MFP_IMRA = IoMem[0xfffa13];
1.1.1.18 root 1706: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1707: }
1708:
1709: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1710: /**
1711: * Handle write to interrupt mask register B (0xfffa15).
1712: */
1.1.1.8 root 1713: void MFP_MaskB_WriteByte(void)
1714: {
1.1.1.10 root 1715: M68000_WaitState(4);
1716:
1.1.1.8 root 1717: MFP_IMRB = IoMem[0xfffa15];
1.1.1.18 root 1718: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1719: }
1720:
1721: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1722: /**
1723: * Handle write to MFP vector register (0xfffa17).
1724: */
1.1.1.8 root 1725: void MFP_VectorReg_WriteByte(void)
1726: {
1727: Uint8 old_vr;
1.1.1.10 root 1728:
1729: M68000_WaitState(4);
1730:
1.1.1.8 root 1731: old_vr = MFP_VR; /* Copy for checking if set mode */
1732: MFP_VR = IoMem[0xfffa17];
1.1.1.10 root 1733:
1.1.1.8 root 1734: if ((MFP_VR^old_vr) & 0x08) /* Test change in end-of-interrupt mode */
1735: {
1.1.1.10 root 1736: /* Mode did change but was it to automatic mode? (ie bit is a zero) */
1737: if (!(MFP_VR & 0x08))
1738: {
1739: /* We are now in automatic mode, so clear all in-service bits! */
1.1.1.8 root 1740: MFP_ISRA = 0;
1741: MFP_ISRB = 0;
1.1.1.18 root 1742: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1743: }
1744: }
1.1.1.11 root 1745:
1.1.1.14 root 1746: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.11 root 1747: {
1.1.1.14 root 1748: int FrameCycles, HblCounterVideo, LineCycles;
1749: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1750: LOG_TRACE_PRINT("mfp write vector reg fa17=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1751: MFP_VR, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 1752: }
1753:
1.1.1.8 root 1754: }
1755:
1756: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1757: /**
1758: * Handle write to timer A control register (0xfffa19).
1759: */
1.1.1.8 root 1760: void MFP_TimerACtrl_WriteByte(void)
1761: {
1.1.1.11 root 1762: Uint8 new_tacr;
1.1.1.10 root 1763:
1764: M68000_WaitState(4);
1765:
1.1.1.11 root 1766: new_tacr = IoMem[0xfffa19] & 0x0f; /* FIXME : ignore bit 4 (reset) ? */
1.1.1.10 root 1767:
1.1.1.11 root 1768: if ( MFP_TACR != new_tacr ) /* Timer control changed */
1769: {
1770: /* If we stop a timer which was in delay mode, we need to store
1771: * the current value of the counter to be able to read it or to
1772: * continue from where we left if the timer is restarted later
1773: * without writing to the data register. */
1774: if ((new_tacr == 0) && (MFP_TACR >=1) && (MFP_TACR <= 7))
1.1.1.14 root 1775: MFP_ReadTimerA(true); /* Store result in 'MFP_TA_MAINCOUNTER' */
1.1.1.11 root 1776:
1777: MFP_TACR = new_tacr; /* set to new value before calling MFP_StartTimer */
1778: MFP_StartTimerA(); /* start/stop timer depending on control reg */
1779: }
1.1.1.8 root 1780: }
1781:
1782: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1783: /**
1784: * Handle write to timer B control register (0xfffa1b).
1785: */
1.1.1.8 root 1786: void MFP_TimerBCtrl_WriteByte(void)
1787: {
1.1.1.11 root 1788: Uint8 new_tbcr;
1.1.1.10 root 1789:
1790: M68000_WaitState(4);
1791:
1.1.1.11 root 1792: new_tbcr = IoMem[0xfffa1b] & 0x0f; /* FIXME : ignore bit 4 (reset) ? */
1793:
1794: if (MFP_TBCR != new_tbcr) /* Timer control changed */
1795: {
1796: /* If we stop a timer which was in delay mode, we need to store
1797: * the current value of the counter to be able to read it or to
1798: * continue from where we left if the timer is restarted later
1799: * without writing to the data register. */
1800: if ((new_tbcr == 0) && (MFP_TBCR >= 1) && (MFP_TBCR <= 7))
1.1.1.14 root 1801: MFP_ReadTimerB(true); /* Store result in 'MFP_TB_MAINCOUNTER' */
1.1.1.10 root 1802:
1.1.1.11 root 1803: MFP_TBCR = new_tbcr; /* set to new value before calling MFP_StartTimer */
1804: MFP_StartTimerB(); /* start/stop timer depending on control reg */
1805: }
1.1.1.8 root 1806: }
1807:
1808: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1809: /**
1810: * Handle write to timer C/D control register (0xfffa1d).
1811: */
1.1.1.8 root 1812: void MFP_TimerCDCtrl_WriteByte(void)
1813: {
1.1.1.11 root 1814: Uint8 new_tcdcr;
1.1.1.8 root 1815: Uint8 old_tcdcr;
1816:
1.1.1.10 root 1817: M68000_WaitState(4);
1818:
1.1.1.11 root 1819: new_tcdcr = IoMem[0xfffa1d];
1820: old_tcdcr = MFP_TCDCR;
1.1.1.12 root 1821: //fprintf ( stderr , "write fa1d new %x old %x\n" , IoMem[0xfffa1d] , MFP_TCDCR );
1.1.1.8 root 1822:
1.1.1.12 root 1823: if ((old_tcdcr & 0x70) != (new_tcdcr & 0x70)) /* Timer C control changed */
1.1.1.11 root 1824: {
1825: /* If we stop a timer which was in delay mode, we need to store
1826: * the current value of the counter to be able to read it or to
1827: * continue from where we left if the timer is restarted later
1828: * without writing to the data register. */
1829: if ((new_tcdcr & 0x70) == 0)
1.1.1.14 root 1830: MFP_ReadTimerC(true); /* Store result in 'MFP_TC_MAINCOUNTER' */
1.1.1.11 root 1831:
1.1.1.12 root 1832: MFP_TCDCR = ( new_tcdcr & 0x70 ) | ( old_tcdcr & 0x07 ); /* we set TCCR and keep old TDDR in case we need to read it below */
1.1.1.11 root 1833: MFP_StartTimerC(); /* start/stop timer depending on control reg */
1834: }
1.1.1.8 root 1835:
1.1.1.12 root 1836: if ((old_tcdcr & 0x07) != (new_tcdcr & 0x07)) /* Timer D control changed */
1.1.1.8 root 1837: {
1.1.1.11 root 1838: Uint32 pc = M68000_GetPC();
1.1.1.8 root 1839:
1840: /* Need to change baud rate of RS232 emulation? */
1841: if (ConfigureParams.RS232.bEnableRS232)
1842: {
1843: RS232_SetBaudRateFromTimerD();
1844: }
1845:
1846: if (ConfigureParams.System.bPatchTimerD && !bAppliedTimerDPatch
1.1.1.11 root 1847: && pc >= TosAddress && pc <= TosAddress + TosSize)
1.1.1.8 root 1848: {
1.1.1.11 root 1849: /* Slow down Timer-D if set from TOS for the first time to gain
1850: * more desktop performance.
1851: * Obviously, we need to emulate all timers correctly but TOS sets
1852: * up Timer-D at a very high rate (every couple of instructions).
1853: * The interrupt isn't enabled but the emulator still needs to
1854: * process the interrupt table and this HALVES our frame rate!!!
1.1.1.8 root 1855: * Some games actually reference this timer but don't set it up
1.1.1.11 root 1856: * (eg Paradroid, Speedball I) so we simply intercept the Timer-D
1857: * setup code in TOS and fix the numbers with more 'laid-back'
1858: * values. This still keeps 100% compatibility */
1.1.1.12 root 1859: if ( new_tcdcr & 0x07 ) /* apply patch only if timer D is being started */
1860: {
1861: new_tcdcr = IoMem[0xfffa1d] = (IoMem[0xfffa1d] & 0xf0) | 7;
1.1.1.14 root 1862: bAppliedTimerDPatch = true;
1.1.1.12 root 1863: }
1.1.1.8 root 1864: }
1.1.1.11 root 1865:
1866: /* If we stop a timer which was in delay mode, we need to store the current value */
1867: /* of the counter to be able to read it or to continue from where we left if the timer is */
1868: /* restarted later without writing to the data register. */
1869: if ((new_tcdcr & 0x07) == 0)
1.1.1.14 root 1870: MFP_ReadTimerD(true); /* Stores result in 'MFP_TD_MAINCOUNTER' */
1.1.1.11 root 1871:
1872: MFP_TCDCR = new_tcdcr; /* set to new value before calling MFP_StartTimer */
1.1.1.12 root 1873: MFP_StartTimerD(); /* start/stop timer depending on control reg */
1.1.1.8 root 1874: }
1875: }
1876:
1877: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1878: /**
1879: * Handle write to timer A data register (0xfffa1f).
1880: */
1.1.1.8 root 1881: void MFP_TimerAData_WriteByte(void)
1882: {
1.1.1.10 root 1883: M68000_WaitState(4);
1884:
1.1.1.8 root 1885: MFP_TADR = IoMem[0xfffa1f]; /* Store into data register */
1.1.1.10 root 1886:
1.1.1.8 root 1887: if (MFP_TACR == 0) /* Now check if timer is running - if so do not set */
1888: {
1889: MFP_TA_MAINCOUNTER = MFP_TADR; /* Timer is off, store to main counter */
1.1.1.14 root 1890: TimerACanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 1891: }
1.1.1.12 root 1892:
1.1.1.14 root 1893: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 1894: {
1.1.1.14 root 1895: int FrameCycles, HblCounterVideo, LineCycles;
1896: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1897: LOG_TRACE_PRINT("mfp write data reg A fa1f=0x%x new counter=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1898: MFP_TADR, MFP_TA_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1899: }
1.1.1.8 root 1900: }
1901:
1902: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1903: /**
1904: * Handle write to timer B data register (0xfffa21).
1905: */
1.1.1.8 root 1906: void MFP_TimerBData_WriteByte(void)
1907: {
1.1.1.10 root 1908: M68000_WaitState(4);
1909:
1.1.1.8 root 1910: MFP_TBDR = IoMem[0xfffa21]; /* Store into data register */
1.1.1.10 root 1911:
1.1.1.8 root 1912: if (MFP_TBCR == 0) /* Now check if timer is running - if so do not set */
1913: {
1914: MFP_TB_MAINCOUNTER = MFP_TBDR; /* Timer is off, store to main counter */
1.1.1.14 root 1915: TimerBCanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 1916: }
1.1.1.12 root 1917:
1.1.1.14 root 1918: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 1919: {
1.1.1.14 root 1920: int FrameCycles, HblCounterVideo, LineCycles;
1921: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1922: LOG_TRACE_PRINT("mfp write data reg B fa21=0x%x new counter=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1923: MFP_TBDR, MFP_TB_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1924: }
1.1.1.8 root 1925: }
1926:
1927: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1928: /**
1929: * Handle write to timer C data register (0xfffa23).
1930: */
1.1.1.8 root 1931: void MFP_TimerCData_WriteByte(void)
1932: {
1.1.1.10 root 1933: M68000_WaitState(4);
1934:
1.1.1.8 root 1935: MFP_TCDR = IoMem[0xfffa23]; /* Store into data register */
1.1.1.10 root 1936:
1.1.1.8 root 1937: if ((MFP_TCDCR&0x70) == 0) /* Now check if timer is running - if so do not set */
1938: {
1.1.1.11 root 1939: MFP_TC_MAINCOUNTER = MFP_TCDR; /* Timer is off, store to main counter */
1.1.1.14 root 1940: TimerCCanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 1941: }
1.1.1.12 root 1942:
1.1.1.14 root 1943: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 1944: {
1.1.1.14 root 1945: int FrameCycles, HblCounterVideo, LineCycles;
1946: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1947: LOG_TRACE_PRINT("mfp write data reg C fa23=0x%x new counter=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1948: MFP_TCDR, MFP_TC_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1949: }
1.1.1.8 root 1950: }
1951:
1952: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1953: /**
1954: * Handle write to timer D data register (0xfffa25).
1955: */
1.1.1.8 root 1956: void MFP_TimerDData_WriteByte(void)
1957: {
1.1.1.11 root 1958: Uint32 pc = M68000_GetPC();
1.1.1.8 root 1959:
1.1.1.10 root 1960: M68000_WaitState(4);
1961:
1.1.1.8 root 1962: /* Need to change baud rate of RS232 emulation? */
1963: if (ConfigureParams.RS232.bEnableRS232 && (IoMem[0xfffa1d] & 0x07))
1964: {
1965: RS232_SetBaudRateFromTimerD();
1966: }
1967:
1968: /* Patch Timer-D for better performance? */
1969: if (ConfigureParams.System.bPatchTimerD && pc >= TosAddress && pc <= TosAddress + TosSize)
1970: {
1971: nTimerDFakeValue = IoMem[0xfffa25];
1972: IoMem[0xfffa25] = 0x64; /* Slow down the useless Timer-D setup from the bios */
1973: }
1974:
1975: MFP_TDDR = IoMem[0xfffa25]; /* Store into data register */
1976: if ((MFP_TCDCR&0x07) == 0) /* Now check if timer is running - if so do not set */
1977: {
1.1.1.11 root 1978: MFP_TD_MAINCOUNTER = MFP_TDDR; /* Timer is off, store to main counter */
1.1.1.14 root 1979: TimerDCanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 1980: }
1.1.1.12 root 1981:
1.1.1.14 root 1982: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 1983: {
1.1.1.14 root 1984: int FrameCycles, HblCounterVideo, LineCycles;
1985: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1986: LOG_TRACE_PRINT("mfp write data reg D fa25=0x%x new counter=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1987: MFP_TDDR, MFP_TD_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1988: }
1.1.1.8 root 1989: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.