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