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