|
|
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 */
1.1.1.23! root 956: if ( ( M68000_GetPC() == 0x14d72 ) && ( STMemory_ReadLong ( 0x14d6c ) == 0x11faff75 ) )
1.1.1.19 root 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: int AddCurCycles = INT_CONVERT_TO_INTERNAL ( Cycles_GetInternalCycleOnWriteAccess() , INT_CPU_CYCLE );
1.1.1.11 root 984:
985: /* Start timer from now? If not continue timer using PendingCycleOver */
986: if (bFirstTimer)
1.1.1.15 root 987: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, AddCurCycles);
1.1.1.11 root 988: else
989: {
1.1.1.23! root 990: Sint64 TimerClockCyclesInternal = INT_CONVERT_TO_INTERNAL ( (Sint64)TimerClockCycles , INT_MFP_CYCLE );
1.1.1.11 root 991:
992: /* In case we miss more than one int, we must correct the delay for the next one */
1.1.1.23! root 993: if ( (Sint64)PendingCyclesOver > TimerClockCyclesInternal )
1.1.1.11 root 994: PendingCyclesOver = PendingCyclesOver % TimerClockCyclesInternal;
995:
1.1.1.15 root 996: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, -PendingCyclesOver);
1.1.1.11 root 997: }
1.1 root 998:
1.1.1.14 root 999: *pTimerCanResume = true; /* timer was set, resume is possible if stop/start it later */
1.1.1.11 root 1000: }
1001: }
1.1.1.12 root 1002:
1003: else /* Ctrl was 0 -> timer is stopped */
1004: {
1005: /* do nothing, only print some traces */
1.1.1.14 root 1006: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
1.1.1.12 root 1007: {
1.1.1.14 root 1008: int FrameCycles, HblCounterVideo, LineCycles;
1009: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1010: 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",
1011: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
1012: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
1013: bFirstTimer?"true":"false", *pTimerCanResume?"true":"false");
1.1.1.12 root 1014: }
1015: }
1.1.1.11 root 1016: }
1.1.1.12 root 1017:
1.1.1.16 root 1018:
1019: else if (TimerControl == 8 ) /* event count mode */
1.1.1.11 root 1020: {
1021: /* Make sure no outstanding interrupts in list if channel is disabled */
1.1.1.15 root 1022: CycInt_RemovePendingInterrupt(Handler);
1.1 root 1023:
1.1.1.14 root 1024: if ( Handler == INTERRUPT_MFP_TIMERB ) /* we're starting timer B event count mode */
1025: {
1026: /* Store start cycle for handling interrupt in video.c */
1027: TimerBEventCountCycleStart = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
1028: }
1.1.1.12 root 1029:
1.1.1.14 root 1030: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
1.1.1.12 root 1031: {
1.1.1.14 root 1032: int FrameCycles, HblCounterVideo, LineCycles;
1033: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1034: 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",
1035: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
1036: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
1037: bFirstTimer?"true":"false", *pTimerCanResume?"true":"false");
1.1.1.12 root 1038: }
1039: }
1040:
1.1.1.11 root 1041: return TimerClockCycles;
1.1 root 1042: }
1043:
1.1.1.2 root 1044:
1045: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1046: /**
1047: * Start Timer C or D
1048: */
1.1.1.14 root 1049: static int MFP_StartTimer_CD(Uint8 TimerControl, Uint16 TimerData, interrupt_id Handler,
1.1.1.12 root 1050: bool bFirstTimer, bool *pTimerCanResume)
1.1 root 1051: {
1.1.1.11 root 1052: int TimerClockCycles = 0;
1.1 root 1053:
1.1.1.11 root 1054: /* Is timer in delay mode ? */
1055: if ((TimerControl&0x7) != 0)
1056: {
1057: /* Find number of cycles for when timer is due (include preset and
1058: * counter). As timer occurs very often we multiply by counter to
1059: * speed up emulator */
1060: if (TimerData == 0) /* Data=0 is actually Data=256 */
1061: TimerData = 256;
1062: TimerClockCycles = MFP_REG_TO_CYCLES ( TimerData, TimerControl );
1063:
1.1.1.14 root 1064: if ( LOG_TRACE_LEVEL( TRACE_MFP_START ) )
1.1.1.11 root 1065: {
1.1.1.14 root 1066: int FrameCycles, HblCounterVideo, LineCycles;
1067: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1068: 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 1069: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
1.1.1.14 root 1070: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
1.1.1.11 root 1071: bFirstTimer?"true":"false" , *pTimerCanResume?"true":"false" );
1072: }
1073:
1074: /* And add to our internal interrupt list, if timer cycles is zero
1075: * then timer is stopped */
1.1.1.15 root 1076: CycInt_RemovePendingInterrupt(Handler);
1.1.1.11 root 1077: if (TimerClockCycles)
1078: {
1.1.1.14 root 1079: if ((*pTimerCanResume == true) && (bFirstTimer == true)) /* we can't resume if the timer is auto restarting after an interrupt */
1.1.1.11 root 1080: {
1.1.1.15 root 1081: CycInt_ResumeStoppedInterrupt ( Handler );
1.1.1.11 root 1082: }
1083: else
1084: {
1.1.1.21 root 1085: int AddCurCycles = INT_CONVERT_TO_INTERNAL ( Cycles_GetInternalCycleOnWriteAccess() , INT_CPU_CYCLE );
1.1.1.11 root 1086:
1087: /* Start timer from now? If not continue timer using PendingCycleOver */
1088: if (bFirstTimer)
1.1.1.15 root 1089: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, AddCurCycles);
1.1.1.11 root 1090: else
1091: {
1.1.1.23! root 1092: Sint64 TimerClockCyclesInternal = INT_CONVERT_TO_INTERNAL ( (Sint64)TimerClockCycles , INT_MFP_CYCLE );
1.1.1.11 root 1093:
1094: /* In case we miss more than one int, we must correct the delay for the next one */
1.1.1.23! root 1095: if ( (Sint64)PendingCyclesOver > TimerClockCyclesInternal )
1.1.1.11 root 1096: PendingCyclesOver = PendingCyclesOver % TimerClockCyclesInternal;
1097:
1.1.1.15 root 1098: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, -PendingCyclesOver);
1.1.1.11 root 1099: }
1100:
1.1.1.14 root 1101: *pTimerCanResume = true; /* timer was set, resume is possible if stop/start it later */
1.1.1.11 root 1102: }
1103: }
1104: }
1.1.1.12 root 1105:
1106: else /* timer control is 0 */
1.1.1.11 root 1107: {
1.1.1.14 root 1108: if ( LOG_TRACE_LEVEL( TRACE_MFP_START ) )
1.1.1.12 root 1109: {
1.1.1.14 root 1110: int FrameCycles, HblCounterVideo, LineCycles;
1111: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1112: 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 1113: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
1.1.1.14 root 1114: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
1.1.1.12 root 1115: bFirstTimer?"true":"false" , *pTimerCanResume?"true":"false" );
1116: }
1117:
1.1.1.11 root 1118: /* Make sure no outstanding interrupts in list if channel is disabled */
1.1.1.15 root 1119: CycInt_RemovePendingInterrupt(Handler);
1.1.1.11 root 1120: }
1.1 root 1121:
1.1.1.11 root 1122: return TimerClockCycles;
1.1 root 1123: }
1124:
1.1.1.2 root 1125:
1126: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1127: /**
1128: * Read Timer A or B - If in EventCount MainCounter already has correct value
1129: */
1.1.1.14 root 1130: static Uint8 MFP_ReadTimer_AB(Uint8 TimerControl, Uint8 MainCounter, int TimerCycles, interrupt_id Handler, bool TimerIsStopping)
1.1 root 1131: {
1.1.1.12 root 1132: // int TimerCyclesPassed;
1.1 root 1133:
1.1.1.11 root 1134: /* Find TimerAB count, if no interrupt or not in delay mode assume
1135: * in Event Count mode so already up-to-date as kept by HBL */
1.1.1.15 root 1136: if (CycInt_InterruptActive(Handler) && (TimerControl > 0) && (TimerControl <= 7))
1.1.1.11 root 1137: {
1138: /* Find cycles passed since last interrupt */
1.1.1.15 root 1139: //TimerCyclesPassed = TimerCycles - CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE );
1140: MainCounter = MFP_CYCLE_TO_REG ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ), TimerControl );
1.1.1.11 root 1141: //fprintf ( stderr , "mfp read AB passed %d count %d\n" , TimerCyclesPassed, MainCounter );
1142: }
1143:
1.1.1.12 root 1144: /* If the timer is stopped when the internal mfp data reg is already < 1 */
1145: /* then the data reg will be 0 (=256) next time the timer will be restarted */
1146: /* if no write is made to the data reg before */
1147: if ( TimerIsStopping )
1148: {
1.1.1.15 root 1149: if ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ) < MFP_REG_TO_CYCLES ( 1 , TimerControl ) )
1.1.1.12 root 1150: {
1151: MainCounter = 0; /* internal mfp counter becomes 0 (=256) */
1.1.1.14 root 1152: 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 1153: Handler );
1154: }
1155: }
1156:
1.1.1.14 root 1157: if ( LOG_TRACE_LEVEL( TRACE_MFP_READ ) )
1.1.1.11 root 1158: {
1.1.1.14 root 1159: int FrameCycles, HblCounterVideo, LineCycles;
1160: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1161: 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 1162: Handler, MainCounter, TimerControl, TimerCycles,
1.1.1.14 root 1163: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 1164: }
1.1.1.6 root 1165:
1.1.1.11 root 1166: return MainCounter;
1.1 root 1167: }
1168:
1.1.1.2 root 1169:
1170: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1171: /**
1172: * Read Timer C or D
1173: */
1.1.1.14 root 1174: static Uint8 MFP_ReadTimerCD(Uint8 TimerControl, Uint8 TimerData, Uint8 MainCounter, int TimerCycles, interrupt_id Handler, bool TimerIsStopping)
1.1 root 1175: {
1.1.1.12 root 1176: // int TimerCyclesPassed;
1.1 root 1177:
1.1.1.11 root 1178: /* Find TimerCD count. If timer is off, MainCounter already contains
1179: * the latest value */
1.1.1.15 root 1180: if (CycInt_InterruptActive(Handler))
1.1.1.11 root 1181: {
1182: /* Find cycles passed since last interrupt */
1.1.1.15 root 1183: //TimerCyclesPassed = TimerCycles - CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE );
1184: MainCounter = MFP_CYCLE_TO_REG ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ), TimerControl);
1.1.1.11 root 1185: //fprintf ( stderr , "mfp read CD passed %d count %d\n" , TimerCyclesPassed, MainCounter );
1186: }
1187:
1.1.1.12 root 1188: /* If the timer is stopped when the internal mfp data reg is already < 1 */
1189: /* then the data reg will be 0 (=256) next time the timer will be restarted */
1190: /* if no write is made to the data reg before */
1191: if ( TimerIsStopping )
1192: {
1.1.1.15 root 1193: if ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ) < MFP_REG_TO_CYCLES ( 1 , TimerControl ) )
1.1.1.12 root 1194: {
1195: MainCounter = 0; /* internal mfp counter becomes 0 (=256) */
1.1.1.14 root 1196: 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 1197: Handler );
1198: }
1199: }
1200:
1.1.1.14 root 1201: if ( LOG_TRACE_LEVEL( TRACE_MFP_READ ) )
1.1.1.11 root 1202: {
1.1.1.14 root 1203: int FrameCycles, HblCounterVideo, LineCycles;
1204: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1205: 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 1206: Handler, MainCounter, TimerControl, TimerCycles,
1.1.1.14 root 1207: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 1208: }
1.1 root 1209:
1.1.1.11 root 1210: return MainCounter;
1.1 root 1211: }
1212:
1.1.1.2 root 1213:
1214: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1215: /**
1216: * Start Timer A
1217: */
1.1.1.10 root 1218: static void MFP_StartTimerA(void)
1.1 root 1219: {
1.1.1.11 root 1220: TimerAClockCycles = MFP_StartTimer_AB(MFP_TACR, MFP_TA_MAINCOUNTER,
1.1.1.14 root 1221: INTERRUPT_MFP_TIMERA, true, &TimerACanResume);
1.1 root 1222: }
1223:
1224:
1.1.1.2 root 1225: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1226: /**
1227: * Read Timer A
1228: */
1.1.1.12 root 1229: static void MFP_ReadTimerA(bool TimerIsStopping)
1.1 root 1230: {
1.1.1.11 root 1231: MFP_TA_MAINCOUNTER = MFP_ReadTimer_AB(MFP_TACR, MFP_TA_MAINCOUNTER,
1.1.1.12 root 1232: TimerAClockCycles, INTERRUPT_MFP_TIMERA, TimerIsStopping);
1.1 root 1233: }
1234:
1235:
1.1.1.2 root 1236: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1237: /**
1238: * Start Timer B
1239: * (This does not start the EventCount mode time as this is taken care
1240: * of by the HBL)
1241: */
1.1.1.10 root 1242: static void MFP_StartTimerB(void)
1.1 root 1243: {
1.1.1.11 root 1244: TimerBClockCycles = MFP_StartTimer_AB(MFP_TBCR, MFP_TB_MAINCOUNTER,
1.1.1.14 root 1245: INTERRUPT_MFP_TIMERB, true, &TimerBCanResume);
1.1 root 1246: }
1247:
1248:
1.1.1.2 root 1249: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1250: /**
1251: * Read Timer B
1252: */
1.1.1.12 root 1253: static void MFP_ReadTimerB(bool TimerIsStopping)
1.1 root 1254: {
1.1.1.11 root 1255: MFP_TB_MAINCOUNTER = MFP_ReadTimer_AB(MFP_TBCR, MFP_TB_MAINCOUNTER,
1.1.1.12 root 1256: TimerBClockCycles, INTERRUPT_MFP_TIMERB, TimerIsStopping);
1.1 root 1257: }
1258:
1259:
1.1.1.2 root 1260: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1261: /**
1262: * Start Timer C
1263: */
1.1.1.10 root 1264: static void MFP_StartTimerC(void)
1.1 root 1265: {
1.1.1.11 root 1266: TimerCClockCycles = MFP_StartTimer_CD((MFP_TCDCR>>4)&7, MFP_TC_MAINCOUNTER,
1.1.1.14 root 1267: INTERRUPT_MFP_TIMERC , true, &TimerCCanResume);
1.1 root 1268: }
1269:
1270:
1.1.1.2 root 1271: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1272: /**
1273: * Read Timer C
1274: */
1.1.1.12 root 1275: static void MFP_ReadTimerC(bool TimerIsStopping)
1.1 root 1276: {
1.1.1.11 root 1277: MFP_TC_MAINCOUNTER = MFP_ReadTimerCD((MFP_TCDCR>>4)&7, MFP_TCDR, MFP_TC_MAINCOUNTER,
1.1.1.12 root 1278: TimerCClockCycles, INTERRUPT_MFP_TIMERC, TimerIsStopping);
1.1 root 1279: }
1280:
1281:
1.1.1.2 root 1282: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1283: /**
1284: * Start Timer D
1285: */
1.1.1.10 root 1286: static void MFP_StartTimerD(void)
1.1 root 1287: {
1.1.1.11 root 1288: TimerDClockCycles = MFP_StartTimer_CD(MFP_TCDCR&7, MFP_TD_MAINCOUNTER,
1.1.1.14 root 1289: INTERRUPT_MFP_TIMERD, true, &TimerDCanResume);
1.1 root 1290: }
1291:
1292:
1.1.1.2 root 1293: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1294: /**
1295: * Read Timer D
1296: */
1.1.1.12 root 1297: static void MFP_ReadTimerD(bool TimerIsStopping)
1.1 root 1298: {
1.1.1.11 root 1299: MFP_TD_MAINCOUNTER = MFP_ReadTimerCD(MFP_TCDCR&7, MFP_TDDR, MFP_TD_MAINCOUNTER,
1.1.1.12 root 1300: TimerDClockCycles, INTERRUPT_MFP_TIMERD, TimerIsStopping);
1.1 root 1301: }
1302:
1303:
1.1.1.2 root 1304: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1305: /**
1306: * Handle Timer A Interrupt
1307: */
1.1 root 1308: void MFP_InterruptHandler_TimerA(void)
1309: {
1.1.1.11 root 1310: /* Number of internal cycles we went over for this timer ( <= 0 ),
1311: * used when timer expires and needs to be restarted */
1312: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1313:
1314: /* Remove this interrupt from list and re-order */
1.1.1.15 root 1315: CycInt_AcknowledgeInterrupt();
1.1 root 1316:
1.1.1.11 root 1317: /* Acknowledge in MFP circuit, pass bit,enable,pending */
1318: if ((MFP_TACR&0xf) != 0) /* Is timer OK? */
1.1.1.20 root 1319: MFP_InputOnChannel ( MFP_INT_TIMER_A , INT_CONVERT_FROM_INTERNAL ( PendingCyclesOver , INT_CPU_CYCLE ) );
1.1 root 1320:
1.1.1.11 root 1321: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 1322: TimerAClockCycles = MFP_StartTimer_AB(MFP_TACR, MFP_TADR, INTERRUPT_MFP_TIMERA, false, &TimerACanResume);
1.1 root 1323: }
1324:
1325:
1.1.1.2 root 1326: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1327: /**
1328: * Handle Timer B Interrupt
1329: */
1.1 root 1330: void MFP_InterruptHandler_TimerB(void)
1331: {
1.1.1.11 root 1332: /* Number of internal cycles we went over for this timer ( <= 0 ),
1333: * used when timer expires and needs to be restarted */
1334: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1335:
1336: /* Remove this interrupt from list and re-order */
1.1.1.15 root 1337: CycInt_AcknowledgeInterrupt();
1.1 root 1338:
1.1.1.11 root 1339: /* Acknowledge in MFP circuit, pass bit, enable, pending */
1340: if ((MFP_TBCR&0xf) != 0) /* Is timer OK? */
1.1.1.20 root 1341: MFP_InputOnChannel ( MFP_INT_TIMER_B , INT_CONVERT_FROM_INTERNAL ( PendingCyclesOver , INT_CPU_CYCLE ) );
1.1 root 1342:
1.1.1.11 root 1343: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 1344: TimerBClockCycles = MFP_StartTimer_AB(MFP_TBCR, MFP_TBDR, INTERRUPT_MFP_TIMERB, false, &TimerBCanResume);
1.1 root 1345: }
1346:
1347:
1.1.1.2 root 1348: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1349: /**
1350: * Handle Timer C Interrupt
1351: */
1.1 root 1352: void MFP_InterruptHandler_TimerC(void)
1353: {
1.1.1.11 root 1354: /* Number of internal cycles we went over for this timer ( <= 0 ),
1355: * used when timer expires and needs to be restarted */
1356: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1.1 root 1357:
1.1.1.11 root 1358: /* Remove this interrupt from list and re-order */
1.1.1.15 root 1359: CycInt_AcknowledgeInterrupt();
1.1 root 1360:
1.1.1.11 root 1361: /* Acknowledge in MFP circuit, pass bit, enable, pending */
1362: if ((MFP_TCDCR&0x70) != 0) /* Is timer OK? */
1.1.1.21 root 1363: MFP_InputOnChannel ( MFP_INT_TIMER_C , INT_CONVERT_FROM_INTERNAL ( PendingCyclesOver , INT_CPU_CYCLE ) );
1.1.1.11 root 1364:
1365: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 1366: TimerCClockCycles = MFP_StartTimer_CD((MFP_TCDCR>>4)&7, MFP_TCDR, INTERRUPT_MFP_TIMERC, false, &TimerCCanResume);
1.1 root 1367: }
1368:
1369:
1.1.1.2 root 1370: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1371: /**
1372: * Handle Timer D Interrupt
1373: */
1.1 root 1374: void MFP_InterruptHandler_TimerD(void)
1375: {
1.1.1.11 root 1376: /* Number of internal cycles we went over for this timer ( <= 0 ),
1377: * used when timer expires and needs to be restarted */
1378: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1379:
1380: /* Remove this interrupt from list and re-order */
1.1.1.15 root 1381: CycInt_AcknowledgeInterrupt();
1.1.1.11 root 1382:
1383: /* Acknowledge in MFP circuit, pass bit, enable, pending */
1384: if ((MFP_TCDCR&0x07) != 0) /* Is timer OK? */
1.1.1.20 root 1385: MFP_InputOnChannel ( MFP_INT_TIMER_D , INT_CONVERT_FROM_INTERNAL ( PendingCyclesOver , INT_CPU_CYCLE ) );
1.1.1.11 root 1386:
1387: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 1388: TimerDClockCycles = MFP_StartTimer_CD(MFP_TCDCR&7, MFP_TDDR, INTERRUPT_MFP_TIMERD, false, &TimerDCanResume);
1.1 root 1389: }
1390:
1.1.1.8 root 1391:
1392:
1393: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1394: /**
1395: * Handle read from GPIP pins register (0xfffa01).
1396: *
1397: * - Bit 0 is the BUSY signal of the printer port, it is SET if no printer
1.1.1.20 root 1398: * is connected or on BUSY. Therefore we should assume it to be 0 in Hatari
1.1.1.11 root 1399: * when a printer is emulated.
1400: * - Bit 1 is used for RS232: DCD
1401: * - Bit 2 is used for RS232: CTS
1.1.1.20 root 1402: * - Bit 3 is used by the blitter (busy/idle state)
1403: * - Bit 4 is used by the ACIAs (keyboard and midi)
1404: * - Bit 5 is used by the FDC / HDC
1.1.1.11 root 1405: * - Bit 6 is used for RS232: RI
1406: * - Bit 7 is monochrome monitor detection signal. On STE it is also XORed with
1407: * the DMA sound play bit.
1.1.1.20 root 1408: *
1409: * When reading GPIP, output lines (DDR=1) should return the last value that was written,
1410: * only input lines (DDR=0) should be updated.
1.1.1.11 root 1411: */
1.1.1.8 root 1412: void MFP_GPIP_ReadByte(void)
1413: {
1.1.1.20 root 1414: Uint8 gpip_new;
1415:
1.1.1.10 root 1416: M68000_WaitState(4);
1417:
1.1.1.20 root 1418: gpip_new = MFP_GPIP;
1419:
1.1.1.8 root 1420: if (!bUseHighRes)
1.1.1.20 root 1421: gpip_new |= 0x80; /* Color monitor -> set top bit */
1.1.1.9 root 1422: else
1.1.1.20 root 1423: gpip_new &= ~0x80;
1.1.1.15 root 1424:
1.1.1.9 root 1425: if (nDmaSoundControl & DMASNDCTRL_PLAY)
1.1.1.20 root 1426: gpip_new ^= 0x80; /* Top bit is XORed with DMA sound control play bit (Ste/TT emulation mode)*/
1.1.1.15 root 1427: if (nCbar_DmaSoundControl & CROSSBAR_SNDCTRL_PLAY || nCbar_DmaSoundControl & CROSSBAR_SNDCTRL_RECORD)
1.1.1.20 root 1428: gpip_new ^= 0x80; /* Top bit is XORed with Falcon crossbar DMA sound control play bit (Falcon emulation mode) */
1.1.1.9 root 1429:
1430: if (ConfigureParams.Printer.bEnablePrinting)
1431: {
1432: /* Signal that printer is not busy */
1.1.1.20 root 1433: gpip_new &= ~1;
1.1.1.9 root 1434: }
1435: else
1436: {
1.1.1.20 root 1437: gpip_new |= 1;
1.1.1.9 root 1438:
1439: /* Printer BUSY bit is also used by parallel port joystick adapters as fire button */
1440: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT1].nJoystickMode != JOYSTICK_DISABLED)
1441: {
1442: /* Fire pressed? */
1443: if (Joy_GetStickData(JOYID_PARPORT1) & 0x80)
1.1.1.20 root 1444: gpip_new &= ~1;
1.1.1.9 root 1445: }
1446: }
1.1.1.8 root 1447:
1.1.1.20 root 1448: gpip_new &= ~MFP_DDR; /* New input bits */
1449:
1450: MFP_GPIP = ( MFP_GPIP & MFP_DDR ) | gpip_new; /* Keep output bits unchanged and update input bits */
1451:
1.1.1.9 root 1452: IoMem[0xfffa01] = MFP_GPIP;
1.1.1.13 root 1453:
1.1.1.14 root 1454: if ( LOG_TRACE_LEVEL( TRACE_MFP_READ ) )
1.1.1.13 root 1455: {
1.1.1.14 root 1456: int FrameCycles, HblCounterVideo, LineCycles;
1457: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1458: LOG_TRACE_PRINT("mfp read gpip fa01=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1459: MFP_GPIP, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.13 root 1460: }
1.1.1.8 root 1461: }
1462:
1463: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1464: /**
1465: * Handle read from active edge register (0xfffa03).
1466: */
1.1.1.8 root 1467: void MFP_ActiveEdge_ReadByte(void)
1468: {
1.1.1.10 root 1469: M68000_WaitState(4);
1470:
1.1.1.8 root 1471: IoMem[0xfffa03] = MFP_AER;
1472: }
1473:
1474: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1475: /**
1476: * Handle read from data direction register (0xfffa05).
1477: */
1.1.1.8 root 1478: void MFP_DataDirection_ReadByte(void)
1479: {
1.1.1.10 root 1480: M68000_WaitState(4);
1481:
1.1.1.8 root 1482: IoMem[0xfffa05] = MFP_DDR;
1483: }
1484:
1485: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1486: /**
1.1.1.18 root 1487: * Handle read from interrupt enable register A (0xfffa07).
1.1.1.11 root 1488: */
1.1.1.8 root 1489: void MFP_EnableA_ReadByte(void)
1490: {
1.1.1.10 root 1491: M68000_WaitState(4);
1492:
1.1.1.8 root 1493: IoMem[0xfffa07] = MFP_IERA;
1494: }
1495:
1496: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1497: /**
1.1.1.18 root 1498: * Handle read from interrupt enable register B (0xfffa09).
1.1.1.11 root 1499: */
1.1.1.8 root 1500: void MFP_EnableB_ReadByte(void)
1501: {
1.1.1.10 root 1502: M68000_WaitState(4);
1503:
1.1.1.8 root 1504: IoMem[0xfffa09] = MFP_IERB;
1505: }
1506:
1507: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1508: /**
1.1.1.18 root 1509: * Handle read from interrupt pending register A (0xfffa0b).
1.1.1.11 root 1510: */
1.1.1.8 root 1511: void MFP_PendingA_ReadByte(void)
1512: {
1.1.1.10 root 1513: M68000_WaitState(4);
1514:
1.1.1.8 root 1515: IoMem[0xfffa0b] = MFP_IPRA;
1516: }
1517:
1518: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1519: /**
1.1.1.18 root 1520: * Handle read from interrupt pending register A (0xfffa0d).
1.1.1.11 root 1521: */
1.1.1.8 root 1522: void MFP_PendingB_ReadByte(void)
1523: {
1.1.1.10 root 1524: M68000_WaitState(4);
1525:
1.1.1.8 root 1526: IoMem[0xfffa0d] = MFP_IPRB;
1527: }
1528:
1529: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1530: /**
1.1.1.18 root 1531: * Handle read from interrupt in service register A (0xfffa0f).
1.1.1.11 root 1532: */
1.1.1.8 root 1533: void MFP_InServiceA_ReadByte(void)
1534: {
1.1.1.10 root 1535: M68000_WaitState(4);
1536:
1.1.1.8 root 1537: IoMem[0xfffa0f] = MFP_ISRA;
1538: }
1539:
1540: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1541: /**
1.1.1.18 root 1542: * Handle read from interrupt in service register B (0xfffa11).
1.1.1.11 root 1543: */
1.1.1.8 root 1544: void MFP_InServiceB_ReadByte(void)
1545: {
1.1.1.10 root 1546: M68000_WaitState(4);
1547:
1.1.1.8 root 1548: IoMem[0xfffa11] = MFP_ISRB;
1549: }
1550:
1551: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1552: /**
1.1.1.18 root 1553: * Handle read from interrupt mask register A (0xfffa13).
1.1.1.11 root 1554: */
1.1.1.8 root 1555: void MFP_MaskA_ReadByte(void)
1556: {
1.1.1.10 root 1557: M68000_WaitState(4);
1558:
1.1.1.8 root 1559: IoMem[0xfffa13] = MFP_IMRA;
1560: }
1561:
1562: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1563: /**
1.1.1.18 root 1564: * Handle read from interrupt mask register B (0xfffa15).
1.1.1.11 root 1565: */
1.1.1.8 root 1566: void MFP_MaskB_ReadByte(void)
1567: {
1.1.1.10 root 1568: M68000_WaitState(4);
1569:
1.1.1.8 root 1570: IoMem[0xfffa15] = MFP_IMRB;
1571: }
1572:
1573: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1574: /**
1575: * Handle read from MFP vector register (0xfffa17).
1576: */
1.1.1.8 root 1577: void MFP_VectorReg_ReadByte(void)
1578: {
1.1.1.10 root 1579: M68000_WaitState(4);
1580:
1.1.1.8 root 1581: IoMem[0xfffa17] = MFP_VR;
1582: }
1583:
1584: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1585: /**
1586: * Handle read from timer A control register (0xfffa19).
1587: */
1.1.1.8 root 1588: void MFP_TimerACtrl_ReadByte(void)
1589: {
1.1.1.10 root 1590: M68000_WaitState(4);
1591:
1.1.1.8 root 1592: IoMem[0xfffa19] = MFP_TACR;
1593: }
1594:
1595: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1596: /**
1597: * Handle read from timer B control register (0xfffa1b).
1598: */
1.1.1.8 root 1599: void MFP_TimerBCtrl_ReadByte(void)
1600: {
1.1.1.10 root 1601: M68000_WaitState(4);
1602:
1.1.1.8 root 1603: IoMem[0xfffa1b] = MFP_TBCR;
1604: }
1605:
1606: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1607: /**
1608: * Handle read from timer C/D control register (0xfffa1d).
1609: */
1.1.1.8 root 1610: void MFP_TimerCDCtrl_ReadByte(void)
1611: {
1.1.1.10 root 1612: M68000_WaitState(4);
1613:
1.1.1.8 root 1614: IoMem[0xfffa1d] = MFP_TCDCR;
1615: }
1616:
1617: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1618: /**
1619: * Handle read from timer A data register (0xfffa1f).
1620: */
1.1.1.8 root 1621: void MFP_TimerAData_ReadByte(void)
1622: {
1.1.1.10 root 1623: M68000_WaitState(4);
1624:
1.1.1.12 root 1625: if (MFP_TACR != 8) /* Is event count? Need to re-calculate counter */
1.1.1.14 root 1626: MFP_ReadTimerA(false); /* Stores result in 'MFP_TA_MAINCOUNTER' */
1.1.1.8 root 1627:
1628: IoMem[0xfffa1f] = MFP_TA_MAINCOUNTER;
1629: }
1630:
1631: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1632: /**
1633: * Handle read from timer B data register (0xfffa21).
1634: */
1.1.1.8 root 1635: void MFP_TimerBData_ReadByte(void)
1636: {
1.1.1.12 root 1637: Uint8 TB_count;
1638:
1.1.1.10 root 1639: M68000_WaitState(4);
1640:
1.1.1.13 root 1641: /* Is it event count mode or not? */
1642: if (MFP_TBCR != 8)
1643: {
1644: /* Not event count mode, so handle as normal timer
1645: * and store result in 'MFP_TB_MAINCOUNTER' */
1.1.1.14 root 1646: MFP_ReadTimerB(false);
1.1.1.13 root 1647: }
1648: else if (bUseVDIRes)
1649: {
1650: /* HBLs are disabled in VDI mode, but TOS expects to read a 1. */
1651: MFP_TB_MAINCOUNTER = 1;
1652: }
1.1.1.12 root 1653: /* Special case when reading $fffa21, we need to test if the current read instruction */
1654: /* overlaps the horizontal video position where $fffa21 is changed */
1655: else
1656: {
1.1.1.14 root 1657: int FrameCycles, HblCounterVideo;
1.1.1.12 root 1658: int pos_start , pos_read;
1659:
1660: /* Cycle position of the start of the current instruction */
1.1.1.14 root 1661: //pos_start = nFrameCycles % nCyclesPerLine;
1662: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &pos_start );
1.1.1.21 root 1663: pos_start >>= nCpuFreqShift;
1.1.1.22 root 1664:
1.1.1.12 root 1665: /* Cycle position of the read for the current instruction (approximatively, we consider */
1666: /* the read happens after 4 cycles (due to MFP wait states in that case)) */
1667: /* This is quite a hack, but hard to do without proper 68000 read cycle emulation */
1668: if ( CurrentInstrCycles <= 8 ) /* move.b (a0),d0 / cmp.b (a0),d0 ... */
1669: pos_read = pos_start + 4; /* wait state */
1670: else /* cmp.b $fa21.w,d0 (BIG Demo) ... */
1671: pos_read = pos_start + 8; /* more time needed to compute the effective address */
1672:
1673: TB_count = MFP_TB_MAINCOUNTER; /* default value */
1674:
1675: /* If Timer B's change happens before the read cycle of the current instruction, we must return */
1676: /* the current value - 1 (because MFP_TimerB_EventCount_Interrupt was not called yet) */
1677: if ( (nHBL >= nStartHBL ) && ( nHBL < nEndHBL ) /* ensure display is ON and timer B can happen */
1.1.1.21 root 1678: && ( LineTimerBPos > pos_start ) && ( LineTimerBPos < pos_read ) )
1.1.1.12 root 1679: {
1.1.1.14 root 1680: 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 1681: pos_start, LineTimerBPos, pos_read , HblCounterVideo );
1.1.1.12 root 1682:
1683: TB_count--;
1684: if ( TB_count == 0 ) /* going from 1 to 0 : timer restart, reload data reg */
1685: TB_count = MFP_TBDR;
1686: /* Going from 0 to -1 : data reg is in fact going from 256 to 255. As TB_count is Uint8, */
1687: /* this is already what we get when we decrement TB_count=0. So, the next 2 lines are redundant. */
1688: /* else if ( TB_count < 0 )
1689: TB_count = 255;
1690: */
1691: }
1692:
1.1.1.14 root 1693: LOG_TRACE(TRACE_MFP_READ , "mfp read TB data=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" ,
1694: TB_count, FrameCycles, pos_start, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1695: IoMem[0xfffa21] = TB_count;
1696: return;
1697: }
1.1.1.8 root 1698:
1699: IoMem[0xfffa21] = MFP_TB_MAINCOUNTER;
1700: }
1701:
1702: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1703: /**
1704: * Handle read from timer C data register (0xfffa23).
1705: */
1.1.1.8 root 1706: void MFP_TimerCData_ReadByte(void)
1707: {
1.1.1.10 root 1708: M68000_WaitState(4);
1709:
1.1.1.14 root 1710: MFP_ReadTimerC(false); /* Stores result in 'MFP_TC_MAINCOUNTER' */
1.1.1.8 root 1711:
1712: IoMem[0xfffa23] = MFP_TC_MAINCOUNTER;
1713: }
1714:
1715: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1716: /**
1717: * Handle read from timer D data register (0xfffa25).
1718: */
1.1.1.8 root 1719: void MFP_TimerDData_ReadByte(void)
1720: {
1.1.1.11 root 1721: Uint32 pc = M68000_GetPC();
1.1.1.8 root 1722:
1.1.1.10 root 1723: M68000_WaitState(4);
1724:
1.1.1.8 root 1725: if (ConfigureParams.System.bPatchTimerD && pc >= TosAddress && pc <= TosAddress + TosSize)
1726: {
1727: /* Trick the tos to believe it was changed: */
1728: IoMem[0xfffa25] = nTimerDFakeValue;
1729: }
1730: else
1731: {
1.1.1.14 root 1732: MFP_ReadTimerD(false); /* Stores result in 'MFP_TD_MAINCOUNTER' */
1.1.1.8 root 1733: IoMem[0xfffa25] = MFP_TD_MAINCOUNTER;
1734: }
1735: }
1736:
1737:
1738: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1739: /**
1740: * Handle write to GPIP register (0xfffa01).
1.1.1.20 root 1741: *
1742: * Only line configured as ouput in DDR can be changed (0=input 1=output)
1743: * When reading GPIP, output lines should return the last value that was written,
1744: * only input lines should be updated.
1.1.1.11 root 1745: */
1.1.1.8 root 1746: void MFP_GPIP_WriteByte(void)
1747: {
1.1.1.20 root 1748: Uint8 GPIP_new;
1749: Uint8 GPIP_old = MFP_GPIP;
1750:
1.1.1.10 root 1751: M68000_WaitState(4);
1752:
1.1.1.20 root 1753: GPIP_new = IoMem[0xfffa01] & MFP_DDR; /* New output bits */
1754:
1755: MFP_GPIP = ( MFP_GPIP & ~MFP_DDR ) | GPIP_new; /* Keep input bits unchanged and update output bits */
1756:
1757: /* Update possible interrupts after changing GPIP */
1758: MFP_GPIP_Update_Interrupt ( GPIP_old , MFP_GPIP , MFP_AER , MFP_AER , MFP_DDR , MFP_DDR );
1.1.1.8 root 1759: }
1760:
1761: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1762: /**
1.1.1.14 root 1763: * Handle write to AER (0xfffa03)
1.1.1.20 root 1764: *
1765: * Special case for bit 3 :
1.1.1.14 root 1766: * Bit 3 of AER is linked to timer B in event count mode.
1.1.1.20 root 1767: * - If bit 3=0, timer B triggers on end of line when display goes off.
1768: * - If bit 3=1, timer B triggers on start of line when display goes on.
1.1.1.11 root 1769: */
1.1.1.8 root 1770: void MFP_ActiveEdge_WriteByte(void)
1771: {
1.1.1.20 root 1772: Uint8 AER_old;
1.1.1.14 root 1773:
1.1.1.10 root 1774: M68000_WaitState(4);
1775:
1.1.1.20 root 1776: AER_old = MFP_AER;
1777: MFP_AER = IoMem[0xfffa03];
1.1.1.14 root 1778:
1.1.1.20 root 1779: /* Update possible interrupts after changing AER */
1780: MFP_GPIP_Update_Interrupt ( MFP_GPIP , MFP_GPIP , AER_old , MFP_AER , MFP_DDR , MFP_DDR );
1.1.1.14 root 1781:
1.1.1.20 root 1782:
1783: /* Special case when changing bit 3 : we need to update the position of the timer B interrupt for 'event count' mode */
1784: if ( ( AER_old & ( 1 << 3 ) ) != ( MFP_AER & ( 1 << 3 ) ) )
1.1.1.14 root 1785: {
1.1.1.20 root 1786: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.21 root 1787: int LineTimerBPos_old = LineTimerBPos;
1.1.1.14 root 1788:
1.1.1.20 root 1789: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.14 root 1790:
1.1.1.20 root 1791: /* 0 -> 1, timer B is now counting start of line events (cycle 56+28) */
1792: if ( ( AER_old & ( 1 << 3 ) ) == 0 )
1793: {
1.1.1.21 root 1794: LineTimerBPos = Video_TimerB_GetPos ( HblCounterVideo );
1.1.1.14 root 1795:
1.1.1.20 root 1796: LOG_TRACE((TRACE_VIDEO_HBL | TRACE_MFP_WRITE),
1797: "mfp/video AER bit 3 0->1, timer B triggers on start of line,"
1798: " old_pos=%d new_pos=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n",
1.1.1.21 root 1799: LineTimerBPos_old, LineTimerBPos,
1.1.1.20 root 1800: FrameCycles, LineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles);
1801: }
1802:
1803: /* 1 -> 0, timer B is now counting end of line events (cycle 376+28) */
1804: else if ( ( AER_old & ( 1 << 3 ) ) != 0 )
1805: {
1.1.1.21 root 1806: LineTimerBPos = Video_TimerB_GetPos ( HblCounterVideo );
1.1.1.20 root 1807:
1808: LOG_TRACE((TRACE_VIDEO_HBL | TRACE_MFP_WRITE),
1809: "mfp/video AER bit 3 1->0, timer B triggers on end of line,"
1810: " old_pos=%d new_pos=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n",
1.1.1.21 root 1811: LineTimerBPos_old, LineTimerBPos,
1.1.1.20 root 1812: FrameCycles, LineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles);
1813: }
1814:
1815: /* Timer B position changed, update the next interrupt */
1.1.1.21 root 1816: if ( LineTimerBPos_old != LineTimerBPos )
1817: Video_AddInterruptTimerB ( HblCounterVideo , LineCycles , LineTimerBPos );
1.1.1.20 root 1818: }
1.1.1.8 root 1819: }
1820:
1.1.1.20 root 1821:
1.1.1.8 root 1822: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1823: /**
1824: * Handle write to data direction register (0xfffa05).
1825: */
1.1.1.8 root 1826: void MFP_DataDirection_WriteByte(void)
1827: {
1.1.1.20 root 1828: Uint8 DDR_old;
1829:
1.1.1.10 root 1830: M68000_WaitState(4);
1831:
1.1.1.20 root 1832: DDR_old = MFP_DDR;
1.1.1.8 root 1833: MFP_DDR = IoMem[0xfffa05];
1.1.1.20 root 1834:
1835: /* Update possible interrupts after changing AER */
1836: MFP_GPIP_Update_Interrupt ( MFP_GPIP , MFP_GPIP , MFP_AER , MFP_AER , DDR_old , MFP_DDR );
1.1.1.8 root 1837: }
1838:
1.1.1.20 root 1839:
1.1.1.8 root 1840: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1841: /**
1842: * Handle write to interrupt enable register A (0xfffa07).
1843: */
1.1.1.8 root 1844: void MFP_EnableA_WriteByte(void)
1845: {
1.1.1.10 root 1846: M68000_WaitState(4);
1847:
1.1.1.8 root 1848: MFP_IERA = IoMem[0xfffa07];
1849: MFP_IPRA &= MFP_IERA;
1.1.1.18 root 1850: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1851: }
1852:
1853: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1854: /**
1855: * Handle write to interrupt enable register B (0xfffa09).
1856: */
1.1.1.8 root 1857: void MFP_EnableB_WriteByte(void)
1858: {
1.1.1.10 root 1859: M68000_WaitState(4);
1860:
1.1.1.8 root 1861: MFP_IERB = IoMem[0xfffa09];
1862: MFP_IPRB &= MFP_IERB;
1.1.1.18 root 1863: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1864: }
1865:
1866: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1867: /**
1868: * Handle write to interrupt pending register A (0xfffa0b).
1869: */
1.1.1.8 root 1870: void MFP_PendingA_WriteByte(void)
1871: {
1.1.1.10 root 1872: M68000_WaitState(4);
1873:
1.1.1.18 root 1874: MFP_IPRA &= IoMem[0xfffa0b]; /* Cannot set pending bits - only clear via software */
1875: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1876: }
1877:
1878: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1879: /**
1880: * Handle write to interrupt pending register B (0xfffa0d).
1881: */
1.1.1.8 root 1882: void MFP_PendingB_WriteByte(void)
1883: {
1.1.1.10 root 1884: M68000_WaitState(4);
1885:
1.1.1.18 root 1886: MFP_IPRB &= IoMem[0xfffa0d]; /* Cannot set pending bits - only clear via software */
1887: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1888: }
1889:
1890: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1891: /**
1892: * Handle write to interrupt in service register A (0xfffa0f).
1893: */
1.1.1.8 root 1894: void MFP_InServiceA_WriteByte(void)
1895: {
1.1.1.10 root 1896: M68000_WaitState(4);
1897:
1.1.1.18 root 1898: MFP_ISRA &= IoMem[0xfffa0f]; /* Cannot set in-service bits - only clear via software */
1899: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1900: }
1901:
1902: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1903: /**
1904: * Handle write to interrupt in service register B (0xfffa11).
1905: */
1.1.1.8 root 1906: void MFP_InServiceB_WriteByte(void)
1907: {
1.1.1.10 root 1908: M68000_WaitState(4);
1909:
1.1.1.18 root 1910: MFP_ISRB &= IoMem[0xfffa11]; /* Cannot set in-service bits - only clear via software */
1911: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1912: }
1913:
1914: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1915: /**
1916: * Handle write to interrupt mask register A (0xfffa13).
1917: */
1.1.1.8 root 1918: void MFP_MaskA_WriteByte(void)
1919: {
1.1.1.10 root 1920: M68000_WaitState(4);
1921:
1.1.1.8 root 1922: MFP_IMRA = IoMem[0xfffa13];
1.1.1.18 root 1923: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1924: }
1925:
1926: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1927: /**
1928: * Handle write to interrupt mask register B (0xfffa15).
1929: */
1.1.1.8 root 1930: void MFP_MaskB_WriteByte(void)
1931: {
1.1.1.10 root 1932: M68000_WaitState(4);
1933:
1.1.1.8 root 1934: MFP_IMRB = IoMem[0xfffa15];
1.1.1.18 root 1935: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1936: }
1937:
1938: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1939: /**
1940: * Handle write to MFP vector register (0xfffa17).
1941: */
1.1.1.8 root 1942: void MFP_VectorReg_WriteByte(void)
1943: {
1944: Uint8 old_vr;
1.1.1.10 root 1945:
1946: M68000_WaitState(4);
1947:
1.1.1.8 root 1948: old_vr = MFP_VR; /* Copy for checking if set mode */
1949: MFP_VR = IoMem[0xfffa17];
1.1.1.10 root 1950:
1.1.1.8 root 1951: if ((MFP_VR^old_vr) & 0x08) /* Test change in end-of-interrupt mode */
1952: {
1.1.1.10 root 1953: /* Mode did change but was it to automatic mode? (ie bit is a zero) */
1954: if (!(MFP_VR & 0x08))
1955: {
1956: /* We are now in automatic mode, so clear all in-service bits! */
1.1.1.8 root 1957: MFP_ISRA = 0;
1958: MFP_ISRB = 0;
1.1.1.18 root 1959: MFP_UpdateIRQ ( Cycles_GetClockCounterOnWriteAccess() );
1.1.1.8 root 1960: }
1961: }
1.1.1.11 root 1962:
1.1.1.14 root 1963: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.11 root 1964: {
1.1.1.14 root 1965: int FrameCycles, HblCounterVideo, LineCycles;
1966: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1967: LOG_TRACE_PRINT("mfp write vector reg fa17=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1968: MFP_VR, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 1969: }
1970:
1.1.1.8 root 1971: }
1972:
1973: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1974: /**
1975: * Handle write to timer A control register (0xfffa19).
1976: */
1.1.1.8 root 1977: void MFP_TimerACtrl_WriteByte(void)
1978: {
1.1.1.11 root 1979: Uint8 new_tacr;
1.1.1.10 root 1980:
1981: M68000_WaitState(4);
1982:
1.1.1.11 root 1983: new_tacr = IoMem[0xfffa19] & 0x0f; /* FIXME : ignore bit 4 (reset) ? */
1.1.1.10 root 1984:
1.1.1.11 root 1985: if ( MFP_TACR != new_tacr ) /* Timer control changed */
1986: {
1987: /* If we stop a timer which was in delay mode, we need to store
1988: * the current value of the counter to be able to read it or to
1989: * continue from where we left if the timer is restarted later
1990: * without writing to the data register. */
1991: if ((new_tacr == 0) && (MFP_TACR >=1) && (MFP_TACR <= 7))
1.1.1.14 root 1992: MFP_ReadTimerA(true); /* Store result in 'MFP_TA_MAINCOUNTER' */
1.1.1.11 root 1993:
1994: MFP_TACR = new_tacr; /* set to new value before calling MFP_StartTimer */
1995: MFP_StartTimerA(); /* start/stop timer depending on control reg */
1996: }
1.1.1.8 root 1997: }
1998:
1999: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2000: /**
2001: * Handle write to timer B control register (0xfffa1b).
2002: */
1.1.1.8 root 2003: void MFP_TimerBCtrl_WriteByte(void)
2004: {
1.1.1.11 root 2005: Uint8 new_tbcr;
1.1.1.10 root 2006:
2007: M68000_WaitState(4);
2008:
1.1.1.11 root 2009: new_tbcr = IoMem[0xfffa1b] & 0x0f; /* FIXME : ignore bit 4 (reset) ? */
2010:
2011: if (MFP_TBCR != new_tbcr) /* Timer control changed */
2012: {
2013: /* If we stop a timer which was in delay mode, we need to store
2014: * the current value of the counter to be able to read it or to
2015: * continue from where we left if the timer is restarted later
2016: * without writing to the data register. */
2017: if ((new_tbcr == 0) && (MFP_TBCR >= 1) && (MFP_TBCR <= 7))
1.1.1.14 root 2018: MFP_ReadTimerB(true); /* Store result in 'MFP_TB_MAINCOUNTER' */
1.1.1.10 root 2019:
1.1.1.11 root 2020: MFP_TBCR = new_tbcr; /* set to new value before calling MFP_StartTimer */
2021: MFP_StartTimerB(); /* start/stop timer depending on control reg */
2022: }
1.1.1.8 root 2023: }
2024:
2025: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2026: /**
2027: * Handle write to timer C/D control register (0xfffa1d).
2028: */
1.1.1.8 root 2029: void MFP_TimerCDCtrl_WriteByte(void)
2030: {
1.1.1.11 root 2031: Uint8 new_tcdcr;
1.1.1.8 root 2032: Uint8 old_tcdcr;
2033:
1.1.1.10 root 2034: M68000_WaitState(4);
2035:
1.1.1.11 root 2036: new_tcdcr = IoMem[0xfffa1d];
2037: old_tcdcr = MFP_TCDCR;
1.1.1.12 root 2038: //fprintf ( stderr , "write fa1d new %x old %x\n" , IoMem[0xfffa1d] , MFP_TCDCR );
1.1.1.8 root 2039:
1.1.1.12 root 2040: if ((old_tcdcr & 0x70) != (new_tcdcr & 0x70)) /* Timer C control changed */
1.1.1.11 root 2041: {
2042: /* If we stop a timer which was in delay mode, we need to store
2043: * the current value of the counter to be able to read it or to
2044: * continue from where we left if the timer is restarted later
2045: * without writing to the data register. */
2046: if ((new_tcdcr & 0x70) == 0)
1.1.1.14 root 2047: MFP_ReadTimerC(true); /* Store result in 'MFP_TC_MAINCOUNTER' */
1.1.1.11 root 2048:
1.1.1.12 root 2049: 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 2050: MFP_StartTimerC(); /* start/stop timer depending on control reg */
2051: }
1.1.1.8 root 2052:
1.1.1.12 root 2053: if ((old_tcdcr & 0x07) != (new_tcdcr & 0x07)) /* Timer D control changed */
1.1.1.8 root 2054: {
1.1.1.11 root 2055: Uint32 pc = M68000_GetPC();
1.1.1.8 root 2056:
2057: /* Need to change baud rate of RS232 emulation? */
2058: if (ConfigureParams.RS232.bEnableRS232)
2059: {
2060: RS232_SetBaudRateFromTimerD();
2061: }
2062:
2063: if (ConfigureParams.System.bPatchTimerD && !bAppliedTimerDPatch
1.1.1.11 root 2064: && pc >= TosAddress && pc <= TosAddress + TosSize)
1.1.1.8 root 2065: {
1.1.1.11 root 2066: /* Slow down Timer-D if set from TOS for the first time to gain
2067: * more desktop performance.
2068: * Obviously, we need to emulate all timers correctly but TOS sets
2069: * up Timer-D at a very high rate (every couple of instructions).
2070: * The interrupt isn't enabled but the emulator still needs to
2071: * process the interrupt table and this HALVES our frame rate!!!
1.1.1.8 root 2072: * Some games actually reference this timer but don't set it up
1.1.1.11 root 2073: * (eg Paradroid, Speedball I) so we simply intercept the Timer-D
2074: * setup code in TOS and fix the numbers with more 'laid-back'
2075: * values. This still keeps 100% compatibility */
1.1.1.12 root 2076: if ( new_tcdcr & 0x07 ) /* apply patch only if timer D is being started */
2077: {
2078: new_tcdcr = IoMem[0xfffa1d] = (IoMem[0xfffa1d] & 0xf0) | 7;
1.1.1.14 root 2079: bAppliedTimerDPatch = true;
1.1.1.12 root 2080: }
1.1.1.8 root 2081: }
1.1.1.11 root 2082:
2083: /* If we stop a timer which was in delay mode, we need to store the current value */
2084: /* of the counter to be able to read it or to continue from where we left if the timer is */
2085: /* restarted later without writing to the data register. */
2086: if ((new_tcdcr & 0x07) == 0)
1.1.1.14 root 2087: MFP_ReadTimerD(true); /* Stores result in 'MFP_TD_MAINCOUNTER' */
1.1.1.11 root 2088:
2089: MFP_TCDCR = new_tcdcr; /* set to new value before calling MFP_StartTimer */
1.1.1.12 root 2090: MFP_StartTimerD(); /* start/stop timer depending on control reg */
1.1.1.8 root 2091: }
2092: }
2093:
2094: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2095: /**
2096: * Handle write to timer A data register (0xfffa1f).
2097: */
1.1.1.8 root 2098: void MFP_TimerAData_WriteByte(void)
2099: {
1.1.1.10 root 2100: M68000_WaitState(4);
2101:
1.1.1.8 root 2102: MFP_TADR = IoMem[0xfffa1f]; /* Store into data register */
1.1.1.10 root 2103:
1.1.1.8 root 2104: if (MFP_TACR == 0) /* Now check if timer is running - if so do not set */
2105: {
2106: MFP_TA_MAINCOUNTER = MFP_TADR; /* Timer is off, store to main counter */
1.1.1.14 root 2107: TimerACanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 2108: }
1.1.1.12 root 2109:
1.1.1.14 root 2110: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 2111: {
1.1.1.14 root 2112: int FrameCycles, HblCounterVideo, LineCycles;
2113: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2114: 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" ,
2115: MFP_TADR, MFP_TA_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 2116: }
1.1.1.8 root 2117: }
2118:
2119: /*-----------------------------------------------------------------------*/
1.1.1.11 root 2120: /**
2121: * Handle write to timer B data register (0xfffa21).
2122: */
1.1.1.8 root 2123: void MFP_TimerBData_WriteByte(void)
2124: {
1.1.1.10 root 2125: M68000_WaitState(4);
2126:
1.1.1.8 root 2127: MFP_TBDR = IoMem[0xfffa21]; /* Store into data register */
1.1.1.10 root 2128:
1.1.1.8 root 2129: if (MFP_TBCR == 0) /* Now check if timer is running - if so do not set */
2130: {
2131: MFP_TB_MAINCOUNTER = MFP_TBDR; /* Timer is off, store to main counter */
1.1.1.14 root 2132: TimerBCanResume = 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 B fa21=0x%x new counter=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
2140: MFP_TBDR, MFP_TB_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 C data register (0xfffa23).
2147: */
1.1.1.8 root 2148: void MFP_TimerCData_WriteByte(void)
2149: {
1.1.1.10 root 2150: M68000_WaitState(4);
2151:
1.1.1.8 root 2152: MFP_TCDR = IoMem[0xfffa23]; /* Store into data register */
1.1.1.10 root 2153:
1.1.1.8 root 2154: if ((MFP_TCDCR&0x70) == 0) /* Now check if timer is running - if so do not set */
2155: {
1.1.1.11 root 2156: MFP_TC_MAINCOUNTER = MFP_TCDR; /* Timer is off, store to main counter */
1.1.1.14 root 2157: TimerCCanResume = 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 C fa23=0x%x new counter=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
2165: MFP_TCDR, MFP_TC_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 D data register (0xfffa25).
2172: */
1.1.1.8 root 2173: void MFP_TimerDData_WriteByte(void)
2174: {
1.1.1.11 root 2175: Uint32 pc = M68000_GetPC();
1.1.1.8 root 2176:
1.1.1.10 root 2177: M68000_WaitState(4);
2178:
1.1.1.8 root 2179: /* Need to change baud rate of RS232 emulation? */
2180: if (ConfigureParams.RS232.bEnableRS232 && (IoMem[0xfffa1d] & 0x07))
2181: {
2182: RS232_SetBaudRateFromTimerD();
2183: }
2184:
2185: /* Patch Timer-D for better performance? */
2186: if (ConfigureParams.System.bPatchTimerD && pc >= TosAddress && pc <= TosAddress + TosSize)
2187: {
2188: nTimerDFakeValue = IoMem[0xfffa25];
2189: IoMem[0xfffa25] = 0x64; /* Slow down the useless Timer-D setup from the bios */
2190: }
2191:
2192: MFP_TDDR = IoMem[0xfffa25]; /* Store into data register */
2193: if ((MFP_TCDCR&0x07) == 0) /* Now check if timer is running - if so do not set */
2194: {
1.1.1.11 root 2195: MFP_TD_MAINCOUNTER = MFP_TDDR; /* Timer is off, store to main counter */
1.1.1.14 root 2196: TimerDCanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 2197: }
1.1.1.12 root 2198:
1.1.1.14 root 2199: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 2200: {
1.1.1.14 root 2201: int FrameCycles, HblCounterVideo, LineCycles;
2202: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2203: 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" ,
2204: MFP_TDDR, MFP_TD_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 2205: }
1.1.1.8 root 2206: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.