|
|
1.1 root 1: /*
1.1.1.5 root 2: Hatari - mfp.c
1.1 root 3:
1.1.1.5 root 4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: 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*/
71: /* occured. This could cause bus error when restoring snapshot */
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 */
96: /* completly correct as we should also emulate GPIO 3/4, but it */
97: /* helps running some programs (fix the game Erik). */
1.1.1.11 root 98:
1.1.1.12 root 99:
1.1.1.13 root 100: const char MFP_fileid[] = "Hatari mfp.c : " __DATE__ " " __TIME__;
1.1 root 101:
102: #include "main.h"
1.1.1.8 root 103: #include "configuration.h"
1.1.1.9 root 104: #include "dmaSnd.h"
1.1.1.15 root 105: #include "crossbar.h"
106: #include "dsp.h"
1.1 root 107: #include "fdc.h"
108: #include "ikbd.h"
1.1.1.15 root 109: #include "cycInt.h"
1.1.1.8 root 110: #include "ioMem.h"
1.1.1.9 root 111: #include "joy.h"
1.1 root 112: #include "m68000.h"
113: #include "memorySnapShot.h"
114: #include "mfp.h"
115: #include "psg.h"
1.1.1.8 root 116: #include "rs232.h"
1.1 root 117: #include "sound.h"
1.1.1.12 root 118: #include "stMemory.h"
1.1.1.8 root 119: #include "tos.h"
1.1.1.13 root 120: #include "vdi.h"
1.1.1.16 root 121: #include "screen.h"
1.1 root 122: #include "video.h"
1.1.1.3 root 123:
1.1 root 124:
125: /*
126: MFP interrupt channel circuit:-
127:
128: EdgeRegister EnableRegister MaskRegister SBit
129: | | | |
130: | | | | ------------------------
131: | | ------------------------ ---\ |---\ | |
132: | o--\ | | AND---o----------------AND---| S InterruptInService |
133: ---\ | AND---| S InterruptPending O |-------/ | |---/ | |
134: XOR----------)--/ | R | | | ------------------------
135: Input -----/ | ------------------------ | |
136: | | InterruptRequest |
137: NOT OR |
138: | | | |
139: -------------------- --------------------------------------o--- PassVector
140: */
141:
1.1.1.7 root 142:
143: /*-----------------------------------------------------------------------*/
1.1 root 144:
1.1.1.2 root 145: /* MFP Registers */
1.1.1.9 root 146: Uint8 MFP_GPIP; /* General Purpose Pins */
1.1.1.11 root 147: Uint8 MFP_VR; /* Vector Register 0xfffa17 */
1.1.1.9 root 148: Uint8 MFP_IERA,MFP_IERB; /* Interrupt Enable Registers A,B 0xfffa07,0xfffa09 */
149: Uint8 MFP_IPRA,MFP_IPRB; /* Interrupt Pending Registers A,B 0xfffa0b,0xfffa0d */
1.1.1.11 root 150: Uint8 MFP_TACR,MFP_TBCR; /* Timer A,B Control Registers */
1.1.1.10 root 151:
1.1.1.11 root 152: static Uint8 MFP_TCDCR; /* C+D Control Registers */
1.1.1.10 root 153: static Uint8 MFP_AER,MFP_DDR; /* Active Edge Register, Data Direction Register */
154: static Uint8 MFP_ISRA,MFP_ISRB; /* Interrupt In-Service Registers A,B 0xfffa0f,0xfffa11 */
155: static Uint8 MFP_IMRA,MFP_IMRB; /* Interrupt Mask Registers A,B 0xfffa13,0xfffa15 */
156: static Uint8 MFP_TADR,MFP_TBDR; /* Timer A,B Data Registers */
157: static Uint8 MFP_TCDR,MFP_TDDR; /* Timer C,D Data Registers */
158: static Uint8 MFP_TA_MAINCOUNTER; /* Timer A Main Counter (internal to MFP) */
159: static Uint8 MFP_TB_MAINCOUNTER; /* Timer B Main Counter */
160: static Uint8 MFP_TC_MAINCOUNTER; /* Timer C Main Counter (these are temp's, set when read as) */
161: static Uint8 MFP_TD_MAINCOUNTER; /* Timer D Main Counter (as done via interrupts) */
1.1 root 162:
163: /* CPU clock cycle counts for each timer */
1.1.1.7 root 164: static int TimerAClockCycles=0;
165: static int TimerBClockCycles=0;
166: static int TimerCClockCycles=0;
167: static int TimerDClockCycles=0;
168:
1.1.1.11 root 169: /* If a timer is stopped then restarted later without writing to the data register, */
170: /* we must resume the timer from where we left in the interrupts table, instead of */
171: /* computing a new number of clock cycles to restart the interrupt. */
1.1.1.14 root 172: static bool TimerACanResume = false;
173: static bool TimerBCanResume = false;
174: static bool TimerCCanResume = false;
175: static bool TimerDCanResume = false;
1.1.1.11 root 176:
1.1.1.14 root 177: bool bAppliedTimerDPatch; /* true if the Timer-D patch has been applied */
1.1.1.9 root 178: static int nTimerDFakeValue; /* Faked Timer-D data register for the Timer-D patch */
1.1.1.8 root 179:
1.1.1.11 root 180: static int PendingCyclesOver = 0; /* >= 0 value, used to "loop" a timer when data counter reaches 0 */
1.1 root 181:
1.1.1.11 root 182: static const Uint16 MFPDiv[] =
183: {
184: 0,
185: 4,
186: 10,
187: 16,
188: 50,
189: 64,
190: 100,
191: 200
1.1 root 192: };
193:
1.1.1.11 root 194: /* Convert data/ctrl register to a number of mfp cycles */
195: #define MFP_REG_TO_CYCLES(data,ctrl) ( data * MFPDiv[ ctrl&0x7 ] )
196: /* Determine the data register corresponding to a number of mfp cycles/ctrl register */
197: /* (we round to the closest higher integer) */
198: #define MFP_CYCLE_TO_REG(cyc,ctrl) ( ( cyc + MFPDiv[ ctrl&0x7 ] - 1 ) / MFPDiv[ ctrl&0x7 ] )
199: //#define MFP_CYCLE_TO_REG(cyc,ctrl) ( cyc / MFPDiv[ ctrl&0x7 ] )
200:
1.1 root 201:
1.1.1.2 root 202: /*-----------------------------------------------------------------------*/
1.1.1.11 root 203: /**
204: * Reset all MFP variables and start interrupts on their way!
205: */
1.1 root 206: void MFP_Reset(void)
207: {
1.1.1.11 root 208: /* Reset MFP internal variables */
1.1.1.7 root 209:
1.1.1.14 root 210: bAppliedTimerDPatch = false;
1.1.1.7 root 211:
1.1.1.11 root 212: MFP_GPIP = 0xff;
213: MFP_AER = MFP_DDR = 0;
214: MFP_IERA = MFP_IERB = 0;
215: MFP_IPRA = MFP_IPRB = 0;
216: MFP_ISRA = MFP_ISRB = 0;
217: MFP_IMRA = MFP_IMRB = 0;
218: MFP_VR = 0;
219: MFP_TACR = MFP_TBCR = MFP_TCDCR = 0;
220: MFP_TADR = MFP_TBDR = 0;
221: MFP_TCDR = MFP_TDDR = 0;
222: MFP_TA_MAINCOUNTER = MFP_TB_MAINCOUNTER = 0;
223: MFP_TC_MAINCOUNTER = MFP_TD_MAINCOUNTER = 0;
1.1 root 224:
1.1.1.11 root 225: /* Clear counters */
226: TimerAClockCycles = TimerBClockCycles = 0;
227: TimerCClockCycles = TimerDClockCycles = 0;
1.1 root 228: }
229:
1.1.1.2 root 230:
231: /*-----------------------------------------------------------------------*/
1.1.1.11 root 232: /**
233: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
234: */
1.1.1.12 root 235: void MFP_MemorySnapShot_Capture(bool bSave)
1.1 root 236: {
1.1.1.11 root 237: /* Save/Restore details */
238: MemorySnapShot_Store(&MFP_GPIP, sizeof(MFP_GPIP));
239: MemorySnapShot_Store(&MFP_AER, sizeof(MFP_AER));
240: MemorySnapShot_Store(&MFP_DDR, sizeof(MFP_DDR));
241: MemorySnapShot_Store(&MFP_IERA, sizeof(MFP_IERA));
242: MemorySnapShot_Store(&MFP_IERB, sizeof(MFP_IERB));
243: MemorySnapShot_Store(&MFP_IPRA, sizeof(MFP_IPRA));
244: MemorySnapShot_Store(&MFP_IPRB, sizeof(MFP_IPRB));
245: MemorySnapShot_Store(&MFP_ISRA, sizeof(MFP_ISRA));
246: MemorySnapShot_Store(&MFP_ISRB, sizeof(MFP_ISRB));
247: MemorySnapShot_Store(&MFP_IMRA, sizeof(MFP_IMRA));
248: MemorySnapShot_Store(&MFP_IMRB, sizeof(MFP_IMRB));
249: MemorySnapShot_Store(&MFP_VR, sizeof(MFP_VR));
250: MemorySnapShot_Store(&MFP_TACR, sizeof(MFP_TACR));
251: MemorySnapShot_Store(&MFP_TBCR, sizeof(MFP_TBCR));
252: MemorySnapShot_Store(&MFP_TCDCR, sizeof(MFP_TCDCR));
253: MemorySnapShot_Store(&MFP_TADR, sizeof(MFP_TADR));
254: MemorySnapShot_Store(&MFP_TBDR, sizeof(MFP_TBDR));
255: MemorySnapShot_Store(&MFP_TCDR, sizeof(MFP_TCDR));
256: MemorySnapShot_Store(&MFP_TDDR, sizeof(MFP_TDDR));
257: MemorySnapShot_Store(&MFP_TA_MAINCOUNTER, sizeof(MFP_TA_MAINCOUNTER));
258: MemorySnapShot_Store(&MFP_TB_MAINCOUNTER, sizeof(MFP_TB_MAINCOUNTER));
259: MemorySnapShot_Store(&MFP_TC_MAINCOUNTER, sizeof(MFP_TC_MAINCOUNTER));
260: MemorySnapShot_Store(&MFP_TD_MAINCOUNTER, sizeof(MFP_TD_MAINCOUNTER));
261: MemorySnapShot_Store(&TimerAClockCycles, sizeof(TimerAClockCycles));
262: MemorySnapShot_Store(&TimerBClockCycles, sizeof(TimerBClockCycles));
263: MemorySnapShot_Store(&TimerCClockCycles, sizeof(TimerCClockCycles));
264: MemorySnapShot_Store(&TimerDClockCycles, sizeof(TimerDClockCycles));
265: MemorySnapShot_Store(&TimerACanResume, sizeof(TimerACanResume));
266: MemorySnapShot_Store(&TimerBCanResume, sizeof(TimerBCanResume));
267: MemorySnapShot_Store(&TimerCCanResume, sizeof(TimerCCanResume));
268: MemorySnapShot_Store(&TimerDCanResume, sizeof(TimerDCanResume));
1.1 root 269: }
270:
1.1.1.2 root 271:
272: /*-----------------------------------------------------------------------*/
1.1.1.11 root 273: /**
274: * Call MFP interrupt - NOTE when the MFP is in Auto interrupt (AEI), the MFP
275: * puts the interrupt number on the data bus and then the 68000 reads it, multiplies
276: * it by 4 and adds in a base(usually 0x100) to give the vector. Some programs
277: * change this offset, eg RoboCod. This offset is stored in the top 4 bits of register
278: * 0xfffa17(0x40 is the default=0x100)
279: * Many thanks to Steve Bak for that one!
280: */
1.1.1.7 root 281: static void MFP_Exception(int Interrupt)
1.1 root 282: {
1.1.1.11 root 283: unsigned int Vec;
1.1 root 284:
1.1.1.11 root 285: Vec = (unsigned int)(MFP_VR&0xf0)<<2;
286: Vec += Interrupt<<2;
287:
1.1.1.14 root 288: if (LOG_TRACE_LEVEL(TRACE_MFP_EXCEPTION))
1.1.1.11 root 289: {
1.1.1.14 root 290: int FrameCycles, HblCounterVideo, LineCycles;
291: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
292: LOG_TRACE_PRINT("mfp excep int=%d vec=0x%x new_pc=0x%x video_cyc=%d %d@%d\n" ,
293: Interrupt, Vec, STMemory_ReadLong ( Vec ), FrameCycles, LineCycles, HblCounterVideo );
1.1.1.11 root 294: }
295:
1.1.1.15 root 296: M68000_Exception(Vec, M68000_EXC_SRC_INT_MFP);
1.1 root 297: }
298:
1.1.1.2 root 299:
300: /*-----------------------------------------------------------------------*/
1.1.1.11 root 301: /**
302: * This is called whenever the MFP_IPRA or MFP_IPRB registers are modified.
303: * We set the special flag SPCFLAG_MFP accordingly (to say if an MFP interrupt
304: * is to be checked) so we only have one compare during the decode
305: * instruction loop.
306: */
1.1.1.10 root 307: static void MFP_UpdateFlags(void)
308: {
1.1.1.11 root 309: if (MFP_IPRA|MFP_IPRB)
310: {
311: M68000_SetSpecial(SPCFLAG_MFP);
312: }
313: else
314: {
315: M68000_UnsetSpecial(SPCFLAG_MFP);
316: }
1.1.1.10 root 317: }
318:
319:
320: /*-----------------------------------------------------------------------*/
1.1.1.11 root 321: /**
1.1.1.14 root 322: * Test interrupt request to see if can cause exception.
323: * @return true if pass vector
1.1.1.11 root 324: */
1.1.1.12 root 325: static bool MFP_InterruptRequest(int nMfpException, Uint8 Bit, Uint8 *pPendingReg, Uint8 MaskRegister,
1.1.1.9 root 326: Uint8 PriorityMaskLow, Uint8 PriorityMaskHigh, Uint8 *pInServiceReg)
1.1 root 327: {
1.1.1.11 root 328: /* Are any higher priority interupts in service? */
329: if (((MFP_ISRA&PriorityMaskLow) == 0) && ((MFP_ISRB&PriorityMaskHigh) == 0))
330: {
331: /* Is masked? */
332: if (MaskRegister&Bit)
333: {
334: /* CPU allows interrupt of an MFP level? */
1.1.1.15 root 335: if (regs.intmask < 6)
1.1.1.11 root 336: {
337: *pPendingReg &= ~Bit; /* Clear pending bit */
338: MFP_UpdateFlags();
339:
340: /* Are we in 'auto' interrupt or 'manual'? */
341: if (MFP_VR&0x08) /* Software End-of-Interrupt (SEI) */
342: *pInServiceReg |= Bit; /* Set interrupt in service register */
343: else
344: *pInServiceReg &= ~Bit; /* Clear interrupt in service register */
345:
346: /* Call interrupt, adds in base (default 0x100) */
347: MFP_Exception(nMfpException);
1.1.1.14 root 348: return true;
1.1.1.11 root 349: }
350: }
351: }
1.1 root 352:
1.1.1.14 root 353: return false;
1.1 root 354: }
355:
1.1.1.2 root 356:
357: /*-----------------------------------------------------------------------*/
1.1.1.11 root 358: /**
359: * Check 'pending' registers to see if any MFP interrupts need servicing.
360: * Request interrupt if necessary.
1.1.1.14 root 361: * @return true if at least one MFP interrupt was allowed, else return false.
1.1.1.11 root 362: */
1.1.1.13 root 363: bool MFP_CheckPendingInterrupts(void)
1.1 root 364: {
1.1.1.13 root 365: int InterruptPossible;
366:
1.1.1.15 root 367: /* Handle Falcon DSP interrupt. Note: This interrupt is not wired to
368: * the MFP on a real Falcon, we just handle it with the SPCFLAG_MFP
369: * to avoid taking care of another special flag in the CPU core! */
370: if (bDspHostInterruptPending && regs.intmask < 6)
371: {
372: M68000_Exception(IoMem_ReadByte(0xffa203)*4, M68000_EXC_SRC_INT_DSP);
373: bDspHostInterruptPending = false;
374: return true;
375: }
1.1.1.13 root 376:
1.1.1.12 root 377: if ((MFP_IPRA & 0xb5) == 0 && (MFP_IPRB & 0xfb) == 0)
1.1.1.11 root 378: {
379: /* Should never get here, but if do just clear flag (see 'MFP_UpdateFlags') */
380: M68000_UnsetSpecial(SPCFLAG_MFP);
1.1.1.14 root 381: return false;
1.1.1.11 root 382: }
1.1 root 383:
1.1.1.12 root 384:
1.1.1.14 root 385: InterruptPossible = false;
1.1.1.13 root 386:
1.1.1.11 root 387: if (MFP_IPRA & MFP_TIMER_GPIP7_BIT) /* Check MFP GPIP7 interrupt (bit 7) */
1.1.1.13 root 388: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_GPIP7, MFP_TIMER_GPIP7_BIT, &MFP_IPRA, MFP_IMRA, 0x80, 0x00, &MFP_ISRA);
1.1.1.9 root 389:
1.1.1.11 root 390: if (MFP_IPRA & MFP_TIMER_A_BIT) /* Check Timer A (bit 5) */
1.1.1.13 root 391: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_TIMERA, MFP_TIMER_A_BIT, &MFP_IPRA, MFP_IMRA, 0xe0, 0x00, &MFP_ISRA);
1.1.1.2 root 392:
1.1.1.11 root 393: if (MFP_IPRA & MFP_RCVBUFFULL_BIT) /* Check Receive buffer full (bit 4) */
1.1.1.13 root 394: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_RECBUFFULL, MFP_RCVBUFFULL_BIT, &MFP_IPRA, MFP_IMRA, 0xf0, 0x00, &MFP_ISRA);
1.1.1.7 root 395:
1.1.1.11 root 396: if (MFP_IPRA & MFP_TRNBUFEMPTY_BIT) /* Check transmit buffer empty (bit 2) */
1.1.1.13 root 397: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_TRANSBUFFEMPTY, MFP_TRNBUFEMPTY_BIT, &MFP_IPRA, MFP_IMRA, 0xfc, 0x00, &MFP_ISRA);
1.1.1.7 root 398:
1.1.1.11 root 399: if (MFP_IPRA & MFP_TIMER_B_BIT) /* Check Timer B (bit 0) */
1.1.1.13 root 400: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_TIMERB, MFP_TIMER_B_BIT, &MFP_IPRA, MFP_IMRA, 0xff, 0x00, &MFP_ISRA);
1.1.1.7 root 401:
1.1.1.12 root 402:
1.1.1.11 root 403: if (MFP_IPRB & MFP_FDCHDC_BIT) /* Check FDC (bit 7) */
1.1.1.13 root 404: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_GPIP5, MFP_FDCHDC_BIT, &MFP_IPRB, MFP_IMRB, 0xff, 0x80, &MFP_ISRB);
1.1.1.7 root 405:
1.1.1.11 root 406: if (MFP_IPRB & MFP_ACIA_BIT) /* Check ACIA (Keyboard or MIDI) (bit 6) */
1.1.1.13 root 407: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_ACIA, MFP_ACIA_BIT, &MFP_IPRB, MFP_IMRB, 0xff, 0xc0, &MFP_ISRB);
1.1 root 408:
1.1.1.11 root 409: if (MFP_IPRB & MFP_TIMER_C_BIT) /* Check Timer C (bit 5) */
1.1.1.13 root 410: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_TIMERC, MFP_TIMER_C_BIT, &MFP_IPRB, MFP_IMRB, 0xff, 0xe0, &MFP_ISRB);
1.1 root 411:
1.1.1.11 root 412: if (MFP_IPRB & MFP_TIMER_D_BIT) /* Check Timer D (bit 4) */
1.1.1.13 root 413: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_TIMERD, MFP_TIMER_D_BIT, &MFP_IPRB, MFP_IMRB, 0xff, 0xf0, &MFP_ISRB);
1.1.1.12 root 414:
415: if (MFP_IPRB & MFP_GPU_DONE_BIT) /* Check GPU done (bit 3) */
1.1.1.13 root 416: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_GPIP3, MFP_GPU_DONE_BIT, &MFP_IPRB, MFP_IMRB, 0xff, 0xf8, &MFP_ISRB);
1.1.1.12 root 417:
418: if (MFP_IPRB & MFP_GPIP_1_BIT) /* Check (Falcon) Centronics ACK / (ST) RS232 DCD (bit 1) */
1.1.1.13 root 419: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_GPIP1, MFP_GPIP_1_BIT, &MFP_IPRB, MFP_IMRB, 0xff, 0xfe, &MFP_ISRB);
1.1.1.12 root 420:
421: if (MFP_IPRB & MFP_GPIP_0_BIT) /* Check Centronics BUSY (bit 0) */
1.1.1.13 root 422: InterruptPossible |= MFP_InterruptRequest(MFP_EXCEPT_GPIP0, MFP_GPIP_0_BIT, &MFP_IPRB, MFP_IMRB, 0xff, 0xff, &MFP_ISRB);
1.1.1.12 root 423:
1.1.1.13 root 424: return InterruptPossible;
1.1 root 425: }
426:
1.1.1.2 root 427:
428: /*-----------------------------------------------------------------------*/
1.1.1.11 root 429: /**
430: * Interrupt Channel is active, set pending bit so can be serviced
431: */
1.1.1.9 root 432: void MFP_InputOnChannel(Uint8 Bit, Uint8 EnableBit, Uint8 *pPendingReg)
1.1 root 433: {
1.1.1.11 root 434: /* Input has occurred on MFP channel, set interrupt pending to request interrupt when able */
435: if (EnableBit&Bit)
436: *pPendingReg |= Bit; /* Set bit */
437: else
438: *pPendingReg &= ~Bit; /* Clear bit */
439: MFP_UpdateFlags();
1.1 root 440: }
441:
1.1.1.2 root 442:
443: /*-----------------------------------------------------------------------*/
1.1.1.11 root 444: /**
445: * Generate Timer A Interrupt when in Event Count mode
446: */
1.1 root 447: void MFP_TimerA_EventCount_Interrupt(void)
448: {
1.1.1.12 root 449: if (MFP_TA_MAINCOUNTER == 1) /* Timer expired? If so, generate interrupt */
1.1.1.11 root 450: {
1.1.1.12 root 451: MFP_TA_MAINCOUNTER = MFP_TADR; /* Reload timer from data register */
1.1 root 452:
1.1.1.11 root 453: /* Acknowledge in MFP circuit, pass bit,enable,pending */
454: MFP_InputOnChannel(MFP_TIMER_A_BIT,MFP_IERA,&MFP_IPRA);
455: }
456: else
1.1.1.12 root 457: {
458: MFP_TA_MAINCOUNTER--; /* Decrement timer main counter */
459: /* As MFP_TA_MAINCOUNTER is Uint8, when we decrement MFP_TA_MAINCOUNTER=0 */
460: /* we go to MFP_TA_MAINCOUNTER=255, which is the wanted behaviour because */
461: /* data reg = 0 means 256 in fact. So, the next 2 lines are redundant. */
462: /* if ( MFP_TA_MAINCOUNTER < 0 )
463: MFP_TA_MAINCOUNTER = 255;
464: */
465: }
1.1 root 466: }
467:
1.1.1.2 root 468:
469: /*-----------------------------------------------------------------------*/
1.1.1.11 root 470: /**
471: * Generate Timer B Interrupt when in Event Count mode
472: */
1.1 root 473: void MFP_TimerB_EventCount_Interrupt(void)
474: {
1.1.1.14 root 475: LOG_TRACE(TRACE_VIDEO_HBL , "mfp/video timer B new event count %d\n" , MFP_TB_MAINCOUNTER-1 );
476:
1.1.1.12 root 477: if (MFP_TB_MAINCOUNTER == 1) /* Timer expired? If so, generate interrupt */
1.1.1.11 root 478: {
1.1.1.12 root 479: MFP_TB_MAINCOUNTER = MFP_TBDR; /* Reload timer from data register */
1.1 root 480:
1.1.1.11 root 481: /* Acknowledge in MFP circuit, pass bit,enable,pending */
482: MFP_InputOnChannel(MFP_TIMER_B_BIT,MFP_IERA,&MFP_IPRA);
483: }
484: else
1.1.1.12 root 485: {
486: MFP_TB_MAINCOUNTER--; /* Decrement timer main counter */
487: /* As MFP_TB_MAINCOUNTER is Uint8, when we decrement MFP_TB_MAINCOUNTER=0 */
488: /* we go to MFP_TB_MAINCOUNTER=255, which is the wanted behaviour because */
489: /* data reg = 0 means 256 in fact. So, the next 2 lines are redundant. */
490: /* if ( MFP_TB_MAINCOUNTER < 0 )
491: MFP_TB_MAINCOUNTER = 255;
492: */
493: }
1.1 root 494: }
495:
1.1.1.2 root 496:
497: /*-----------------------------------------------------------------------*/
1.1.1.11 root 498: /**
499: * Start Timer A or B - EventCount mode is done in HBL handler to time correctly
500: */
1.1.1.14 root 501: static int MFP_StartTimer_AB(Uint8 TimerControl, Uint16 TimerData, interrupt_id Handler,
1.1.1.12 root 502: bool bFirstTimer, bool *pTimerCanResume)
1.1 root 503: {
1.1.1.11 root 504: int TimerClockCycles = 0;
505:
1.1.1.16 root 506:
507: /* When in pulse width mode, handle as in delay mode */
508: /* (this is not completly correct, as we should also handle GPIO 3/4 in pulse mode) */
509: if ( TimerControl > 8 )
510: {
511: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
512: {
513: int FrameCycles, HblCounterVideo, LineCycles;
514: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
515: 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",
516: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
517: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
518: }
519:
520: TimerControl &= 0x07; /* clear bit 3, pulse width mode -> delay mode */
521: }
522:
523:
1.1.1.11 root 524: /* Is timer in delay mode (ctrl = 0-7) ? */
525: /* If we are in event-count mode (ctrl = 8) ignore this (done on HBL) */
526: if (TimerControl <= 7)
527: {
528: /* Find number of CPU cycles for when timer is due (include preset
529: * and counter). As timer occurs very often we multiply by counter
530: * to speed up emulator */
531: if (TimerData == 0) /* Data=0 is actually Data=256 */
532: TimerData = 256;
533: TimerClockCycles = MFP_REG_TO_CYCLES ( TimerData, TimerControl );
534:
1.1.1.14 root 535: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
1.1.1.11 root 536: {
1.1.1.14 root 537: int FrameCycles, HblCounterVideo, LineCycles;
538: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
539: 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",
540: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
541: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
542: bFirstTimer?"true":"false", *pTimerCanResume?"true":"false");
1.1.1.11 root 543: }
544:
545: /* And add to our internal interrupt list, if timer cycles is zero
546: * then timer is stopped */
1.1.1.15 root 547: CycInt_RemovePendingInterrupt(Handler);
1.1.1.11 root 548: if (TimerClockCycles)
549: {
1.1.1.14 root 550: if ((*pTimerCanResume == true) && (bFirstTimer == true)) /* we can't resume if the timer is auto restarting after an interrupt */
1.1.1.11 root 551: {
1.1.1.15 root 552: CycInt_ResumeStoppedInterrupt ( Handler );
1.1.1.11 root 553: }
554: else
555: {
556: int AddCurCycles = INT_CONVERT_TO_INTERNAL ( CurrentInstrCycles + nWaitStateCycles - 4 , INT_CPU_CYCLE );
557:
558: /* Start timer from now? If not continue timer using PendingCycleOver */
559: if (bFirstTimer)
1.1.1.15 root 560: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, AddCurCycles);
1.1.1.11 root 561: else
562: {
563: int TimerClockCyclesInternal = INT_CONVERT_TO_INTERNAL ( TimerClockCycles , INT_MFP_CYCLE );
564:
565: /* In case we miss more than one int, we must correct the delay for the next one */
566: if ( PendingCyclesOver > TimerClockCyclesInternal )
567: PendingCyclesOver = PendingCyclesOver % TimerClockCyclesInternal;
568:
1.1.1.15 root 569: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, -PendingCyclesOver);
1.1.1.11 root 570: }
1.1 root 571:
1.1.1.14 root 572: *pTimerCanResume = true; /* timer was set, resume is possible if stop/start it later */
1.1.1.11 root 573: }
574: }
1.1.1.12 root 575:
576: else /* Ctrl was 0 -> timer is stopped */
577: {
578: /* do nothing, only print some traces */
1.1.1.14 root 579: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
1.1.1.12 root 580: {
1.1.1.14 root 581: int FrameCycles, HblCounterVideo, LineCycles;
582: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
583: 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",
584: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
585: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
586: bFirstTimer?"true":"false", *pTimerCanResume?"true":"false");
1.1.1.12 root 587: }
588: }
1.1.1.11 root 589: }
1.1.1.12 root 590:
1.1.1.16 root 591:
592: else if (TimerControl == 8 ) /* event count mode */
1.1.1.11 root 593: {
594: /* Make sure no outstanding interrupts in list if channel is disabled */
1.1.1.15 root 595: CycInt_RemovePendingInterrupt(Handler);
1.1 root 596:
1.1.1.14 root 597: if ( Handler == INTERRUPT_MFP_TIMERB ) /* we're starting timer B event count mode */
598: {
599: /* Store start cycle for handling interrupt in video.c */
600: TimerBEventCountCycleStart = Cycles_GetCounterOnWriteAccess(CYCLES_COUNTER_VIDEO);
601: }
1.1.1.12 root 602:
1.1.1.14 root 603: if (LOG_TRACE_LEVEL(TRACE_MFP_START))
1.1.1.12 root 604: {
1.1.1.14 root 605: int FrameCycles, HblCounterVideo, LineCycles;
606: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
607: 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",
608: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
609: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
610: bFirstTimer?"true":"false", *pTimerCanResume?"true":"false");
1.1.1.12 root 611: }
612: }
613:
1.1.1.11 root 614: return TimerClockCycles;
1.1 root 615: }
616:
1.1.1.2 root 617:
618: /*-----------------------------------------------------------------------*/
1.1.1.11 root 619: /**
620: * Start Timer C or D
621: */
1.1.1.14 root 622: static int MFP_StartTimer_CD(Uint8 TimerControl, Uint16 TimerData, interrupt_id Handler,
1.1.1.12 root 623: bool bFirstTimer, bool *pTimerCanResume)
1.1 root 624: {
1.1.1.11 root 625: int TimerClockCycles = 0;
1.1 root 626:
1.1.1.11 root 627: /* Is timer in delay mode ? */
628: if ((TimerControl&0x7) != 0)
629: {
630: /* Find number of cycles for when timer is due (include preset and
631: * counter). As timer occurs very often we multiply by counter to
632: * speed up emulator */
633: if (TimerData == 0) /* Data=0 is actually Data=256 */
634: TimerData = 256;
635: TimerClockCycles = MFP_REG_TO_CYCLES ( TimerData, TimerControl );
636:
1.1.1.14 root 637: if ( LOG_TRACE_LEVEL( TRACE_MFP_START ) )
1.1.1.11 root 638: {
1.1.1.14 root 639: int FrameCycles, HblCounterVideo, LineCycles;
640: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
641: 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 642: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
1.1.1.14 root 643: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
1.1.1.11 root 644: bFirstTimer?"true":"false" , *pTimerCanResume?"true":"false" );
645: }
646:
647: /* And add to our internal interrupt list, if timer cycles is zero
648: * then timer is stopped */
1.1.1.15 root 649: CycInt_RemovePendingInterrupt(Handler);
1.1.1.11 root 650: if (TimerClockCycles)
651: {
1.1.1.14 root 652: if ((*pTimerCanResume == true) && (bFirstTimer == true)) /* we can't resume if the timer is auto restarting after an interrupt */
1.1.1.11 root 653: {
1.1.1.15 root 654: CycInt_ResumeStoppedInterrupt ( Handler );
1.1.1.11 root 655: }
656: else
657: {
658: int AddCurCycles = INT_CONVERT_TO_INTERNAL ( CurrentInstrCycles + nWaitStateCycles - 4 , INT_CPU_CYCLE );
659:
660: /* Start timer from now? If not continue timer using PendingCycleOver */
661: if (bFirstTimer)
1.1.1.15 root 662: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, AddCurCycles);
1.1.1.11 root 663: else
664: {
665: int TimerClockCyclesInternal = INT_CONVERT_TO_INTERNAL ( TimerClockCycles , INT_MFP_CYCLE );
666:
667: /* In case we miss more than one int, we must correct the delay for the next one */
668: if ( PendingCyclesOver > TimerClockCyclesInternal )
669: PendingCyclesOver = PendingCyclesOver % TimerClockCyclesInternal;
670:
1.1.1.15 root 671: CycInt_AddRelativeInterruptWithOffset(TimerClockCycles, INT_MFP_CYCLE, Handler, -PendingCyclesOver);
1.1.1.11 root 672: }
673:
1.1.1.14 root 674: *pTimerCanResume = true; /* timer was set, resume is possible if stop/start it later */
1.1.1.11 root 675: }
676: }
677: }
1.1.1.12 root 678:
679: else /* timer control is 0 */
1.1.1.11 root 680: {
1.1.1.14 root 681: if ( LOG_TRACE_LEVEL( TRACE_MFP_START ) )
1.1.1.12 root 682: {
1.1.1.14 root 683: int FrameCycles, HblCounterVideo, LineCycles;
684: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
685: 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 686: Handler, TimerData, TimerControl, TimerClockCycles, PendingCyclesOver,
1.1.1.14 root 687: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles,
1.1.1.12 root 688: bFirstTimer?"true":"false" , *pTimerCanResume?"true":"false" );
689: }
690:
1.1.1.11 root 691: /* Make sure no outstanding interrupts in list if channel is disabled */
1.1.1.15 root 692: CycInt_RemovePendingInterrupt(Handler);
1.1.1.11 root 693: }
1.1 root 694:
1.1.1.11 root 695: return TimerClockCycles;
1.1 root 696: }
697:
1.1.1.2 root 698:
699: /*-----------------------------------------------------------------------*/
1.1.1.11 root 700: /**
701: * Read Timer A or B - If in EventCount MainCounter already has correct value
702: */
1.1.1.14 root 703: static Uint8 MFP_ReadTimer_AB(Uint8 TimerControl, Uint8 MainCounter, int TimerCycles, interrupt_id Handler, bool TimerIsStopping)
1.1 root 704: {
1.1.1.12 root 705: // int TimerCyclesPassed;
1.1 root 706:
1.1.1.11 root 707: /* Find TimerAB count, if no interrupt or not in delay mode assume
708: * in Event Count mode so already up-to-date as kept by HBL */
1.1.1.15 root 709: if (CycInt_InterruptActive(Handler) && (TimerControl > 0) && (TimerControl <= 7))
1.1.1.11 root 710: {
711: /* Find cycles passed since last interrupt */
1.1.1.15 root 712: //TimerCyclesPassed = TimerCycles - CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE );
713: MainCounter = MFP_CYCLE_TO_REG ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ), TimerControl );
1.1.1.11 root 714: //fprintf ( stderr , "mfp read AB passed %d count %d\n" , TimerCyclesPassed, MainCounter );
715: }
716:
1.1.1.12 root 717: /* If the timer is stopped when the internal mfp data reg is already < 1 */
718: /* then the data reg will be 0 (=256) next time the timer will be restarted */
719: /* if no write is made to the data reg before */
720: if ( TimerIsStopping )
721: {
1.1.1.15 root 722: if ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ) < MFP_REG_TO_CYCLES ( 1 , TimerControl ) )
1.1.1.12 root 723: {
724: MainCounter = 0; /* internal mfp counter becomes 0 (=256) */
1.1.1.14 root 725: 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 726: Handler );
727: }
728: }
729:
1.1.1.14 root 730: if ( LOG_TRACE_LEVEL( TRACE_MFP_READ ) )
1.1.1.11 root 731: {
1.1.1.14 root 732: int FrameCycles, HblCounterVideo, LineCycles;
733: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
734: 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 735: Handler, MainCounter, TimerControl, TimerCycles,
1.1.1.14 root 736: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 737: }
1.1.1.6 root 738:
1.1.1.11 root 739: return MainCounter;
1.1 root 740: }
741:
1.1.1.2 root 742:
743: /*-----------------------------------------------------------------------*/
1.1.1.11 root 744: /**
745: * Read Timer C or D
746: */
1.1.1.14 root 747: static Uint8 MFP_ReadTimerCD(Uint8 TimerControl, Uint8 TimerData, Uint8 MainCounter, int TimerCycles, interrupt_id Handler, bool TimerIsStopping)
1.1 root 748: {
1.1.1.12 root 749: // int TimerCyclesPassed;
1.1 root 750:
1.1.1.11 root 751: /* Find TimerCD count. If timer is off, MainCounter already contains
752: * the latest value */
1.1.1.15 root 753: if (CycInt_InterruptActive(Handler))
1.1.1.11 root 754: {
755: /* Find cycles passed since last interrupt */
1.1.1.15 root 756: //TimerCyclesPassed = TimerCycles - CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE );
757: MainCounter = MFP_CYCLE_TO_REG ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ), TimerControl);
1.1.1.11 root 758: //fprintf ( stderr , "mfp read CD passed %d count %d\n" , TimerCyclesPassed, MainCounter );
759: }
760:
1.1.1.12 root 761: /* If the timer is stopped when the internal mfp data reg is already < 1 */
762: /* then the data reg will be 0 (=256) next time the timer will be restarted */
763: /* if no write is made to the data reg before */
764: if ( TimerIsStopping )
765: {
1.1.1.15 root 766: if ( CycInt_FindCyclesPassed ( Handler, INT_MFP_CYCLE ) < MFP_REG_TO_CYCLES ( 1 , TimerControl ) )
1.1.1.12 root 767: {
768: MainCounter = 0; /* internal mfp counter becomes 0 (=256) */
1.1.1.14 root 769: 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 770: Handler );
771: }
772: }
773:
1.1.1.14 root 774: if ( LOG_TRACE_LEVEL( TRACE_MFP_READ ) )
1.1.1.11 root 775: {
1.1.1.14 root 776: int FrameCycles, HblCounterVideo, LineCycles;
777: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
778: 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 779: Handler, MainCounter, TimerControl, TimerCycles,
1.1.1.14 root 780: FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 781: }
1.1 root 782:
1.1.1.11 root 783: return MainCounter;
1.1 root 784: }
785:
1.1.1.2 root 786:
787: /*-----------------------------------------------------------------------*/
1.1.1.11 root 788: /**
789: * Start Timer A
790: */
1.1.1.10 root 791: static void MFP_StartTimerA(void)
1.1 root 792: {
1.1.1.11 root 793: TimerAClockCycles = MFP_StartTimer_AB(MFP_TACR, MFP_TA_MAINCOUNTER,
1.1.1.14 root 794: INTERRUPT_MFP_TIMERA, true, &TimerACanResume);
1.1 root 795: }
796:
797:
1.1.1.2 root 798: /*-----------------------------------------------------------------------*/
1.1.1.11 root 799: /**
800: * Read Timer A
801: */
1.1.1.12 root 802: static void MFP_ReadTimerA(bool TimerIsStopping)
1.1 root 803: {
1.1.1.11 root 804: MFP_TA_MAINCOUNTER = MFP_ReadTimer_AB(MFP_TACR, MFP_TA_MAINCOUNTER,
1.1.1.12 root 805: TimerAClockCycles, INTERRUPT_MFP_TIMERA, TimerIsStopping);
1.1 root 806: }
807:
808:
1.1.1.2 root 809: /*-----------------------------------------------------------------------*/
1.1.1.11 root 810: /**
811: * Start Timer B
812: * (This does not start the EventCount mode time as this is taken care
813: * of by the HBL)
814: */
1.1.1.10 root 815: static void MFP_StartTimerB(void)
1.1 root 816: {
1.1.1.11 root 817: TimerBClockCycles = MFP_StartTimer_AB(MFP_TBCR, MFP_TB_MAINCOUNTER,
1.1.1.14 root 818: INTERRUPT_MFP_TIMERB, true, &TimerBCanResume);
1.1 root 819: }
820:
821:
1.1.1.2 root 822: /*-----------------------------------------------------------------------*/
1.1.1.11 root 823: /**
824: * Read Timer B
825: */
1.1.1.12 root 826: static void MFP_ReadTimerB(bool TimerIsStopping)
1.1 root 827: {
1.1.1.11 root 828: MFP_TB_MAINCOUNTER = MFP_ReadTimer_AB(MFP_TBCR, MFP_TB_MAINCOUNTER,
1.1.1.12 root 829: TimerBClockCycles, INTERRUPT_MFP_TIMERB, TimerIsStopping);
1.1 root 830: }
831:
832:
1.1.1.2 root 833: /*-----------------------------------------------------------------------*/
1.1.1.11 root 834: /**
835: * Start Timer C
836: */
1.1.1.10 root 837: static void MFP_StartTimerC(void)
1.1 root 838: {
1.1.1.11 root 839: TimerCClockCycles = MFP_StartTimer_CD((MFP_TCDCR>>4)&7, MFP_TC_MAINCOUNTER,
1.1.1.14 root 840: INTERRUPT_MFP_TIMERC , true, &TimerCCanResume);
1.1 root 841: }
842:
843:
1.1.1.2 root 844: /*-----------------------------------------------------------------------*/
1.1.1.11 root 845: /**
846: * Read Timer C
847: */
1.1.1.12 root 848: static void MFP_ReadTimerC(bool TimerIsStopping)
1.1 root 849: {
1.1.1.11 root 850: MFP_TC_MAINCOUNTER = MFP_ReadTimerCD((MFP_TCDCR>>4)&7, MFP_TCDR, MFP_TC_MAINCOUNTER,
1.1.1.12 root 851: TimerCClockCycles, INTERRUPT_MFP_TIMERC, TimerIsStopping);
1.1 root 852: }
853:
854:
1.1.1.2 root 855: /*-----------------------------------------------------------------------*/
1.1.1.11 root 856: /**
857: * Start Timer D
858: */
1.1.1.10 root 859: static void MFP_StartTimerD(void)
1.1 root 860: {
1.1.1.11 root 861: TimerDClockCycles = MFP_StartTimer_CD(MFP_TCDCR&7, MFP_TD_MAINCOUNTER,
1.1.1.14 root 862: INTERRUPT_MFP_TIMERD, true, &TimerDCanResume);
1.1 root 863: }
864:
865:
1.1.1.2 root 866: /*-----------------------------------------------------------------------*/
1.1.1.11 root 867: /**
868: * Read Timer D
869: */
1.1.1.12 root 870: static void MFP_ReadTimerD(bool TimerIsStopping)
1.1 root 871: {
1.1.1.11 root 872: MFP_TD_MAINCOUNTER = MFP_ReadTimerCD(MFP_TCDCR&7, MFP_TDDR, MFP_TD_MAINCOUNTER,
1.1.1.12 root 873: TimerDClockCycles, INTERRUPT_MFP_TIMERD, TimerIsStopping);
1.1 root 874: }
875:
876:
1.1.1.2 root 877: /*-----------------------------------------------------------------------*/
1.1.1.11 root 878: /**
879: * Handle Timer A Interrupt
880: */
1.1 root 881: void MFP_InterruptHandler_TimerA(void)
882: {
1.1.1.11 root 883: /* Number of internal cycles we went over for this timer ( <= 0 ),
884: * used when timer expires and needs to be restarted */
885: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
886:
887: /* Remove this interrupt from list and re-order */
1.1.1.15 root 888: CycInt_AcknowledgeInterrupt();
1.1 root 889:
1.1.1.11 root 890: /* Acknowledge in MFP circuit, pass bit,enable,pending */
891: if ((MFP_TACR&0xf) != 0) /* Is timer OK? */
892: MFP_InputOnChannel(MFP_TIMER_A_BIT, MFP_IERA, &MFP_IPRA);
1.1 root 893:
1.1.1.11 root 894: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 895: TimerAClockCycles = MFP_StartTimer_AB(MFP_TACR, MFP_TADR, INTERRUPT_MFP_TIMERA, false, &TimerACanResume);
1.1 root 896: }
897:
898:
1.1.1.2 root 899: /*-----------------------------------------------------------------------*/
1.1.1.11 root 900: /**
901: * Handle Timer B Interrupt
902: */
1.1 root 903: void MFP_InterruptHandler_TimerB(void)
904: {
1.1.1.11 root 905: /* Number of internal cycles we went over for this timer ( <= 0 ),
906: * used when timer expires and needs to be restarted */
907: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
908:
909: /* Remove this interrupt from list and re-order */
1.1.1.15 root 910: CycInt_AcknowledgeInterrupt();
1.1 root 911:
1.1.1.11 root 912: /* Acknowledge in MFP circuit, pass bit, enable, pending */
913: if ((MFP_TBCR&0xf) != 0) /* Is timer OK? */
914: MFP_InputOnChannel(MFP_TIMER_B_BIT, MFP_IERA, &MFP_IPRA);
1.1 root 915:
1.1.1.11 root 916: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 917: TimerBClockCycles = MFP_StartTimer_AB(MFP_TBCR, MFP_TBDR, INTERRUPT_MFP_TIMERB, false, &TimerBCanResume);
1.1 root 918: }
919:
920:
1.1.1.2 root 921: /*-----------------------------------------------------------------------*/
1.1.1.11 root 922: /**
923: * Handle Timer C Interrupt
924: */
1.1 root 925: void MFP_InterruptHandler_TimerC(void)
926: {
1.1.1.11 root 927: /* Number of internal cycles we went over for this timer ( <= 0 ),
928: * used when timer expires and needs to be restarted */
929: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1.1 root 930:
1.1.1.11 root 931: /* Remove this interrupt from list and re-order */
1.1.1.15 root 932: CycInt_AcknowledgeInterrupt();
1.1 root 933:
1.1.1.11 root 934: /* Acknowledge in MFP circuit, pass bit, enable, pending */
935: if ((MFP_TCDCR&0x70) != 0) /* Is timer OK? */
936: MFP_InputOnChannel(MFP_TIMER_C_BIT, MFP_IERB, &MFP_IPRB);
937:
938: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 939: TimerCClockCycles = MFP_StartTimer_CD((MFP_TCDCR>>4)&7, MFP_TCDR, INTERRUPT_MFP_TIMERC, false, &TimerCCanResume);
1.1 root 940: }
941:
942:
1.1.1.2 root 943: /*-----------------------------------------------------------------------*/
1.1.1.11 root 944: /**
945: * Handle Timer D Interrupt
946: */
1.1 root 947: void MFP_InterruptHandler_TimerD(void)
948: {
1.1.1.11 root 949: /* Number of internal cycles we went over for this timer ( <= 0 ),
950: * used when timer expires and needs to be restarted */
951: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
952:
953: /* Remove this interrupt from list and re-order */
1.1.1.15 root 954: CycInt_AcknowledgeInterrupt();
1.1.1.11 root 955:
956: /* Acknowledge in MFP circuit, pass bit, enable, pending */
957: if ((MFP_TCDCR&0x07) != 0) /* Is timer OK? */
958: MFP_InputOnChannel(MFP_TIMER_D_BIT, MFP_IERB, &MFP_IPRB);
959:
960: /* Start next interrupt, if need one - from current cycle count */
1.1.1.14 root 961: TimerDClockCycles = MFP_StartTimer_CD(MFP_TCDCR&7, MFP_TDDR, INTERRUPT_MFP_TIMERD, false, &TimerDCanResume);
1.1 root 962: }
963:
1.1.1.8 root 964:
965:
966: /*-----------------------------------------------------------------------*/
1.1.1.11 root 967: /**
968: * Handle read from GPIP pins register (0xfffa01).
969: *
970: * - Bit 0 is the BUSY signal of the printer port, it is SET if no printer
971: * is connected or on BUSY. Therefor we should assume it to be 0 in Hatari
972: * when a printer is emulated.
973: * - Bit 1 is used for RS232: DCD
974: * - Bit 2 is used for RS232: CTS
975: * - Bit 3 is used by the blitter for signalling when its done.
976: * - Bit 4 is used by the ACIAs.
977: * - Bit 5 is used by the floppy controller / ACSI DMA
978: * - Bit 6 is used for RS232: RI
979: * - Bit 7 is monochrome monitor detection signal. On STE it is also XORed with
980: * the DMA sound play bit.
981: */
1.1.1.8 root 982: void MFP_GPIP_ReadByte(void)
983: {
1.1.1.10 root 984: M68000_WaitState(4);
985:
1.1.1.8 root 986: if (!bUseHighRes)
1.1.1.11 root 987: MFP_GPIP |= 0x80; /* Color monitor -> set top bit */
1.1.1.9 root 988: else
989: MFP_GPIP &= ~0x80;
1.1.1.15 root 990:
1.1.1.9 root 991: if (nDmaSoundControl & DMASNDCTRL_PLAY)
1.1.1.15 root 992: MFP_GPIP ^= 0x80; /* Top bit is XORed with DMA sound control play bit (Ste/TT emulation mode)*/
993: if (nCbar_DmaSoundControl & CROSSBAR_SNDCTRL_PLAY || nCbar_DmaSoundControl & CROSSBAR_SNDCTRL_RECORD)
994: MFP_GPIP ^= 0x80; /* Top bit is XORed with Falcon crossbar DMA sound control play bit (Falcon emulation mode) */
1.1.1.9 root 995:
996: if (ConfigureParams.Printer.bEnablePrinting)
997: {
998: /* Signal that printer is not busy */
999: MFP_GPIP &= ~1;
1000: }
1001: else
1002: {
1003: MFP_GPIP |= 1;
1004:
1005: /* Printer BUSY bit is also used by parallel port joystick adapters as fire button */
1006: if (ConfigureParams.Joysticks.Joy[JOYID_PARPORT1].nJoystickMode != JOYSTICK_DISABLED)
1007: {
1008: /* Fire pressed? */
1009: if (Joy_GetStickData(JOYID_PARPORT1) & 0x80)
1010: MFP_GPIP &= ~1;
1011: }
1012: }
1.1.1.8 root 1013:
1.1.1.9 root 1014: IoMem[0xfffa01] = MFP_GPIP;
1.1.1.13 root 1015:
1.1.1.14 root 1016: if ( LOG_TRACE_LEVEL( TRACE_MFP_READ ) )
1.1.1.13 root 1017: {
1.1.1.14 root 1018: int FrameCycles, HblCounterVideo, LineCycles;
1019: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1020: LOG_TRACE_PRINT("mfp read gpip fa01=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1021: MFP_GPIP, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.13 root 1022: }
1.1.1.8 root 1023: }
1024:
1025: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1026: /**
1027: * Handle read from active edge register (0xfffa03).
1028: */
1.1.1.8 root 1029: void MFP_ActiveEdge_ReadByte(void)
1030: {
1.1.1.10 root 1031: M68000_WaitState(4);
1032:
1.1.1.8 root 1033: IoMem[0xfffa03] = MFP_AER;
1034: }
1035:
1036: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1037: /**
1038: * Handle read from data direction register (0xfffa05).
1039: */
1.1.1.8 root 1040: void MFP_DataDirection_ReadByte(void)
1041: {
1.1.1.10 root 1042: M68000_WaitState(4);
1043:
1.1.1.8 root 1044: IoMem[0xfffa05] = MFP_DDR;
1045: }
1046:
1047: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1048: /**
1049: * Handle read from interupt enable register A (0xfffa07).
1050: */
1.1.1.8 root 1051: void MFP_EnableA_ReadByte(void)
1052: {
1.1.1.10 root 1053: M68000_WaitState(4);
1054:
1.1.1.8 root 1055: IoMem[0xfffa07] = MFP_IERA;
1056: }
1057:
1058: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1059: /**
1060: * Handle read from interupt enable register B (0xfffa09).
1061: */
1.1.1.8 root 1062: void MFP_EnableB_ReadByte(void)
1063: {
1.1.1.10 root 1064: M68000_WaitState(4);
1065:
1.1.1.8 root 1066: IoMem[0xfffa09] = MFP_IERB;
1067: }
1068:
1069: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1070: /**
1071: * Handle read from interupt pending register A (0xfffa0b).
1072: */
1.1.1.8 root 1073: void MFP_PendingA_ReadByte(void)
1074: {
1.1.1.10 root 1075: M68000_WaitState(4);
1076:
1.1.1.8 root 1077: IoMem[0xfffa0b] = MFP_IPRA;
1078: }
1079:
1080: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1081: /**
1082: * Handle read from interupt pending register A (0xfffa0d).
1083: */
1.1.1.8 root 1084: void MFP_PendingB_ReadByte(void)
1085: {
1.1.1.10 root 1086: M68000_WaitState(4);
1087:
1.1.1.8 root 1088: IoMem[0xfffa0d] = MFP_IPRB;
1089: }
1090:
1091: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1092: /**
1093: * Handle read from interupt in service register A (0xfffa0f).
1094: */
1.1.1.8 root 1095: void MFP_InServiceA_ReadByte(void)
1096: {
1.1.1.10 root 1097: M68000_WaitState(4);
1098:
1.1.1.8 root 1099: IoMem[0xfffa0f] = MFP_ISRA;
1100: }
1101:
1102: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1103: /**
1104: * Handle read from interupt in service register B (0xfffa11).
1105: */
1.1.1.8 root 1106: void MFP_InServiceB_ReadByte(void)
1107: {
1.1.1.10 root 1108: M68000_WaitState(4);
1109:
1.1.1.8 root 1110: IoMem[0xfffa11] = MFP_ISRB;
1111: }
1112:
1113: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1114: /**
1115: * Handle read from interupt mask register A (0xfffa13).
1116: */
1.1.1.8 root 1117: void MFP_MaskA_ReadByte(void)
1118: {
1.1.1.10 root 1119: M68000_WaitState(4);
1120:
1.1.1.8 root 1121: IoMem[0xfffa13] = MFP_IMRA;
1122: }
1123:
1124: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1125: /**
1126: * Handle read from interupt mask register B (0xfffa15).
1127: */
1.1.1.8 root 1128: void MFP_MaskB_ReadByte(void)
1129: {
1.1.1.10 root 1130: M68000_WaitState(4);
1131:
1.1.1.8 root 1132: IoMem[0xfffa15] = MFP_IMRB;
1133: }
1134:
1135: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1136: /**
1137: * Handle read from MFP vector register (0xfffa17).
1138: */
1.1.1.8 root 1139: void MFP_VectorReg_ReadByte(void)
1140: {
1.1.1.10 root 1141: M68000_WaitState(4);
1142:
1.1.1.8 root 1143: IoMem[0xfffa17] = MFP_VR;
1144: }
1145:
1146: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1147: /**
1148: * Handle read from timer A control register (0xfffa19).
1149: */
1.1.1.8 root 1150: void MFP_TimerACtrl_ReadByte(void)
1151: {
1.1.1.10 root 1152: M68000_WaitState(4);
1153:
1.1.1.8 root 1154: IoMem[0xfffa19] = MFP_TACR;
1155: }
1156:
1157: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1158: /**
1159: * Handle read from timer B control register (0xfffa1b).
1160: */
1.1.1.8 root 1161: void MFP_TimerBCtrl_ReadByte(void)
1162: {
1.1.1.10 root 1163: M68000_WaitState(4);
1164:
1.1.1.8 root 1165: IoMem[0xfffa1b] = MFP_TBCR;
1166: }
1167:
1168: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1169: /**
1170: * Handle read from timer C/D control register (0xfffa1d).
1171: */
1.1.1.8 root 1172: void MFP_TimerCDCtrl_ReadByte(void)
1173: {
1.1.1.10 root 1174: M68000_WaitState(4);
1175:
1.1.1.8 root 1176: IoMem[0xfffa1d] = MFP_TCDCR;
1177: }
1178:
1179: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1180: /**
1181: * Handle read from timer A data register (0xfffa1f).
1182: */
1.1.1.8 root 1183: void MFP_TimerAData_ReadByte(void)
1184: {
1.1.1.10 root 1185: M68000_WaitState(4);
1186:
1.1.1.12 root 1187: if (MFP_TACR != 8) /* Is event count? Need to re-calculate counter */
1.1.1.14 root 1188: MFP_ReadTimerA(false); /* Stores result in 'MFP_TA_MAINCOUNTER' */
1.1.1.8 root 1189:
1190: IoMem[0xfffa1f] = MFP_TA_MAINCOUNTER;
1191: }
1192:
1193: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1194: /**
1195: * Handle read from timer B data register (0xfffa21).
1196: */
1.1.1.8 root 1197: void MFP_TimerBData_ReadByte(void)
1198: {
1.1.1.12 root 1199: Uint8 TB_count;
1200:
1.1.1.10 root 1201: M68000_WaitState(4);
1202:
1.1.1.13 root 1203: /* Is it event count mode or not? */
1204: if (MFP_TBCR != 8)
1205: {
1206: /* Not event count mode, so handle as normal timer
1207: * and store result in 'MFP_TB_MAINCOUNTER' */
1.1.1.14 root 1208: MFP_ReadTimerB(false);
1.1.1.13 root 1209: }
1210: else if (bUseVDIRes)
1211: {
1212: /* HBLs are disabled in VDI mode, but TOS expects to read a 1. */
1213: MFP_TB_MAINCOUNTER = 1;
1214: }
1.1.1.12 root 1215: /* Special case when reading $fffa21, we need to test if the current read instruction */
1216: /* overlaps the horizontal video position where $fffa21 is changed */
1217: else
1218: {
1.1.1.14 root 1219: int FrameCycles, HblCounterVideo;
1.1.1.12 root 1220: int pos_start , pos_read;
1221:
1222: /* Cycle position of the start of the current instruction */
1.1.1.14 root 1223: //pos_start = nFrameCycles % nCyclesPerLine;
1224: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &pos_start );
1.1.1.12 root 1225: /* Cycle position of the read for the current instruction (approximatively, we consider */
1226: /* the read happens after 4 cycles (due to MFP wait states in that case)) */
1227: /* This is quite a hack, but hard to do without proper 68000 read cycle emulation */
1228: if ( CurrentInstrCycles <= 8 ) /* move.b (a0),d0 / cmp.b (a0),d0 ... */
1229: pos_read = pos_start + 4; /* wait state */
1230: else /* cmp.b $fa21.w,d0 (BIG Demo) ... */
1231: pos_read = pos_start + 8; /* more time needed to compute the effective address */
1232:
1233: TB_count = MFP_TB_MAINCOUNTER; /* default value */
1234:
1235: /* If Timer B's change happens before the read cycle of the current instruction, we must return */
1236: /* the current value - 1 (because MFP_TimerB_EventCount_Interrupt was not called yet) */
1237: if ( (nHBL >= nStartHBL ) && ( nHBL < nEndHBL ) /* ensure display is ON and timer B can happen */
1238: && ( LineTimerBCycle > pos_start ) && ( LineTimerBCycle < pos_read ) )
1239: {
1.1.1.14 root 1240: LOG_TRACE(TRACE_MFP_READ , "mfp read TB overlaps pos_start=%d TB_pos=%d pos_read=%d nHBL=%d \n",
1241: pos_start, LineTimerBCycle, pos_read , HblCounterVideo );
1.1.1.12 root 1242:
1243: TB_count--;
1244: if ( TB_count == 0 ) /* going from 1 to 0 : timer restart, reload data reg */
1245: TB_count = MFP_TBDR;
1246: /* Going from 0 to -1 : data reg is in fact going from 256 to 255. As TB_count is Uint8, */
1247: /* this is already what we get when we decrement TB_count=0. So, the next 2 lines are redundant. */
1248: /* else if ( TB_count < 0 )
1249: TB_count = 255;
1250: */
1251: }
1252:
1.1.1.14 root 1253: LOG_TRACE(TRACE_MFP_READ , "mfp read TB data=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" ,
1254: TB_count, FrameCycles, pos_start, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1255: IoMem[0xfffa21] = TB_count;
1256: return;
1257: }
1.1.1.8 root 1258:
1259: IoMem[0xfffa21] = MFP_TB_MAINCOUNTER;
1260: }
1261:
1262: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1263: /**
1264: * Handle read from timer C data register (0xfffa23).
1265: */
1.1.1.8 root 1266: void MFP_TimerCData_ReadByte(void)
1267: {
1.1.1.10 root 1268: M68000_WaitState(4);
1269:
1.1.1.14 root 1270: MFP_ReadTimerC(false); /* Stores result in 'MFP_TC_MAINCOUNTER' */
1.1.1.8 root 1271:
1272: IoMem[0xfffa23] = MFP_TC_MAINCOUNTER;
1273: }
1274:
1275: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1276: /**
1277: * Handle read from timer D data register (0xfffa25).
1278: */
1.1.1.8 root 1279: void MFP_TimerDData_ReadByte(void)
1280: {
1.1.1.11 root 1281: Uint32 pc = M68000_GetPC();
1.1.1.8 root 1282:
1.1.1.10 root 1283: M68000_WaitState(4);
1284:
1.1.1.8 root 1285: if (ConfigureParams.System.bPatchTimerD && pc >= TosAddress && pc <= TosAddress + TosSize)
1286: {
1287: /* Trick the tos to believe it was changed: */
1288: IoMem[0xfffa25] = nTimerDFakeValue;
1289: }
1290: else
1291: {
1.1.1.14 root 1292: MFP_ReadTimerD(false); /* Stores result in 'MFP_TD_MAINCOUNTER' */
1.1.1.8 root 1293: IoMem[0xfffa25] = MFP_TD_MAINCOUNTER;
1294: }
1295: }
1296:
1297:
1298: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1299: /**
1300: * Handle write to GPIP register (0xfffa01).
1301: */
1.1.1.8 root 1302: void MFP_GPIP_WriteByte(void)
1303: {
1.1.1.10 root 1304: M68000_WaitState(4);
1305:
1.1.1.8 root 1306: /* Nothing... */
1307: /*fprintf(stderr, "Write to GPIP: %x\n", (int)IoMem[0xfffa01]);*/
1308: /*MFP_GPIP = IoMem[0xfffa01];*/ /* TODO: What are the GPIP pins good for? */
1309: }
1310:
1311: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1312: /**
1.1.1.14 root 1313: * Handle write to AER (0xfffa03)
1314: * Bit 3 of AER is linked to timer B in event count mode.
1315: * If bit 3=0, timer B triggers on end of line when display goes off.
1316: * If bit 3=1, timer B triggers on start of line when display goes on.
1.1.1.11 root 1317: */
1.1.1.8 root 1318: void MFP_ActiveEdge_WriteByte(void)
1319: {
1.1.1.14 root 1320: int FrameCycles, HblCounterVideo, LineCycles;
1321: int LineTimerBCycle_old = LineTimerBCycle;
1322:
1323: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1324:
1.1.1.10 root 1325: M68000_WaitState(4);
1326:
1.1.1.14 root 1327: /* 0 -> 1, timer B is now counting start of line events (cycle 56+28) */
1328: if ( ( ( MFP_AER & ( 1 << 3 ) ) == 0 ) && ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) != 1 ) )
1329: {
1330: LineTimerBCycle = Video_TimerB_GetPos ( HblCounterVideo );
1331:
1332: LOG_TRACE((TRACE_VIDEO_HBL | TRACE_MFP_WRITE),
1333: "mfp/video AER bit 3 0->1, timer B triggers on start of line,"
1334: " old_pos=%d new_pos=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n",
1335: LineTimerBCycle_old, LineTimerBCycle,
1336: FrameCycles, LineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles);
1337: }
1338:
1339: /* 1 -> 0, timer B is now counting end of line events (cycle 376+28) */
1340: else if ( ( ( MFP_AER & ( 1 << 3 ) ) != 0 ) && ( ( IoMem[0xfffa03] & ( 1 << 3 ) ) == 0 ) )
1341: {
1342: LineTimerBCycle = Video_TimerB_GetPos ( HblCounterVideo );
1343:
1344: LOG_TRACE((TRACE_VIDEO_HBL | TRACE_MFP_WRITE),
1345: "mfp/video AER bit 3 1->0, timer B triggers on end of line,"
1346: " old_pos=%d new_pos=%d video_cyc=%d %d@%d pc=%x instr_cyc=%d\n",
1347: LineTimerBCycle_old, LineTimerBCycle,
1348: FrameCycles, LineCycles, nHBL, M68000_GetPC(), CurrentInstrCycles);
1349: }
1350:
1351: /* Timer B position changed, update the next interrupt */
1352: if ( LineTimerBCycle_old != LineTimerBCycle )
1353: Video_AddInterruptTimerB ( LineTimerBCycle );
1354:
1.1.1.8 root 1355: MFP_AER = IoMem[0xfffa03];
1356: }
1357:
1358: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1359: /**
1360: * Handle write to data direction register (0xfffa05).
1361: */
1.1.1.8 root 1362: void MFP_DataDirection_WriteByte(void)
1363: {
1.1.1.10 root 1364: M68000_WaitState(4);
1365:
1.1.1.8 root 1366: MFP_DDR = IoMem[0xfffa05];
1367: }
1368:
1369: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1370: /**
1371: * Handle write to interrupt enable register A (0xfffa07).
1372: */
1.1.1.8 root 1373: void MFP_EnableA_WriteByte(void)
1374: {
1.1.1.10 root 1375: M68000_WaitState(4);
1376:
1.1.1.8 root 1377: MFP_IERA = IoMem[0xfffa07];
1378: MFP_IPRA &= MFP_IERA;
1379: MFP_UpdateFlags();
1380: /* We may have enabled Timer A or B, check */
1.1.1.11 root 1381: /* [NP] No check, restarting the timer is wrong */
1382: // MFP_StartTimerA();
1383: // MFP_StartTimerB();
1.1.1.8 root 1384: }
1385:
1386: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1387: /**
1388: * Handle write to interrupt enable register B (0xfffa09).
1389: */
1.1.1.8 root 1390: void MFP_EnableB_WriteByte(void)
1391: {
1.1.1.10 root 1392: M68000_WaitState(4);
1393:
1.1.1.8 root 1394: MFP_IERB = IoMem[0xfffa09];
1395: MFP_IPRB &= MFP_IERB;
1396: MFP_UpdateFlags();
1397: /* We may have enabled Timer C or D, check */
1.1.1.11 root 1398: /* [NP] No check, restarting the timer is wrong */
1399: // MFP_StartTimerC();
1400: // MFP_StartTimerD();
1.1.1.8 root 1401: }
1402:
1403: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1404: /**
1405: * Handle write to interrupt pending register A (0xfffa0b).
1406: */
1.1.1.8 root 1407: void MFP_PendingA_WriteByte(void)
1408: {
1.1.1.10 root 1409: M68000_WaitState(4);
1410:
1.1.1.8 root 1411: MFP_IPRA &= IoMem[0xfffa0b]; /* Cannot set pending bits - only clear via software */
1412: MFP_UpdateFlags(); /* Check if any interrupts pending */
1413: }
1414:
1415: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1416: /**
1417: * Handle write to interrupt pending register B (0xfffa0d).
1418: */
1.1.1.8 root 1419: void MFP_PendingB_WriteByte(void)
1420: {
1.1.1.10 root 1421: M68000_WaitState(4);
1422:
1.1.1.8 root 1423: MFP_IPRB &= IoMem[0xfffa0d];
1424: MFP_UpdateFlags(); /* Check if any interrupts pending */
1425: }
1426:
1427: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1428: /**
1429: * Handle write to interrupt in service register A (0xfffa0f).
1430: */
1.1.1.8 root 1431: void MFP_InServiceA_WriteByte(void)
1432: {
1.1.1.10 root 1433: M68000_WaitState(4);
1434:
1.1.1.8 root 1435: MFP_ISRA &= IoMem[0xfffa0f]; /* Cannot set in-service bits - only clear via software */
1436: }
1437:
1438: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1439: /**
1440: * Handle write to interrupt in service register B (0xfffa11).
1441: */
1.1.1.8 root 1442: void MFP_InServiceB_WriteByte(void)
1443: {
1.1.1.10 root 1444: M68000_WaitState(4);
1445:
1.1.1.8 root 1446: MFP_ISRB &= IoMem[0xfffa11]; /* Cannot set in-service bits - only clear via software */
1447: }
1448:
1449: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1450: /**
1451: * Handle write to interrupt mask register A (0xfffa13).
1452: */
1.1.1.8 root 1453: void MFP_MaskA_WriteByte(void)
1454: {
1.1.1.10 root 1455: M68000_WaitState(4);
1456:
1.1.1.8 root 1457: MFP_IMRA = IoMem[0xfffa13];
1458: }
1459:
1460: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1461: /**
1462: * Handle write to interrupt mask register B (0xfffa15).
1463: */
1.1.1.8 root 1464: void MFP_MaskB_WriteByte(void)
1465: {
1.1.1.10 root 1466: M68000_WaitState(4);
1467:
1.1.1.8 root 1468: MFP_IMRB = IoMem[0xfffa15];
1469: }
1470:
1471: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1472: /**
1473: * Handle write to MFP vector register (0xfffa17).
1474: */
1.1.1.8 root 1475: void MFP_VectorReg_WriteByte(void)
1476: {
1477: Uint8 old_vr;
1.1.1.10 root 1478:
1479: M68000_WaitState(4);
1480:
1.1.1.8 root 1481: old_vr = MFP_VR; /* Copy for checking if set mode */
1482: MFP_VR = IoMem[0xfffa17];
1.1.1.10 root 1483:
1.1.1.8 root 1484: if ((MFP_VR^old_vr) & 0x08) /* Test change in end-of-interrupt mode */
1485: {
1.1.1.10 root 1486: /* Mode did change but was it to automatic mode? (ie bit is a zero) */
1487: if (!(MFP_VR & 0x08))
1488: {
1489: /* We are now in automatic mode, so clear all in-service bits! */
1.1.1.8 root 1490: MFP_ISRA = 0;
1491: MFP_ISRB = 0;
1492: }
1493: }
1.1.1.11 root 1494:
1.1.1.14 root 1495: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.11 root 1496: {
1.1.1.14 root 1497: int FrameCycles, HblCounterVideo, LineCycles;
1498: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1499: LOG_TRACE_PRINT("mfp write vector reg fa17=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n" ,
1500: MFP_VR, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.11 root 1501: }
1502:
1.1.1.8 root 1503: }
1504:
1505: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1506: /**
1507: * Handle write to timer A control register (0xfffa19).
1508: */
1.1.1.8 root 1509: void MFP_TimerACtrl_WriteByte(void)
1510: {
1.1.1.11 root 1511: Uint8 new_tacr;
1.1.1.10 root 1512:
1513: M68000_WaitState(4);
1514:
1.1.1.11 root 1515: new_tacr = IoMem[0xfffa19] & 0x0f; /* FIXME : ignore bit 4 (reset) ? */
1.1.1.10 root 1516:
1.1.1.11 root 1517: if ( MFP_TACR != new_tacr ) /* Timer control changed */
1518: {
1519: /* If we stop a timer which was in delay mode, we need to store
1520: * the current value of the counter to be able to read it or to
1521: * continue from where we left if the timer is restarted later
1522: * without writing to the data register. */
1523: if ((new_tacr == 0) && (MFP_TACR >=1) && (MFP_TACR <= 7))
1.1.1.14 root 1524: MFP_ReadTimerA(true); /* Store result in 'MFP_TA_MAINCOUNTER' */
1.1.1.11 root 1525:
1526: MFP_TACR = new_tacr; /* set to new value before calling MFP_StartTimer */
1527: MFP_StartTimerA(); /* start/stop timer depending on control reg */
1528: }
1.1.1.8 root 1529: }
1530:
1531: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1532: /**
1533: * Handle write to timer B control register (0xfffa1b).
1534: */
1.1.1.8 root 1535: void MFP_TimerBCtrl_WriteByte(void)
1536: {
1.1.1.11 root 1537: Uint8 new_tbcr;
1.1.1.10 root 1538:
1539: M68000_WaitState(4);
1540:
1.1.1.11 root 1541: new_tbcr = IoMem[0xfffa1b] & 0x0f; /* FIXME : ignore bit 4 (reset) ? */
1542:
1543: if (MFP_TBCR != new_tbcr) /* Timer control changed */
1544: {
1545: /* If we stop a timer which was in delay mode, we need to store
1546: * the current value of the counter to be able to read it or to
1547: * continue from where we left if the timer is restarted later
1548: * without writing to the data register. */
1549: if ((new_tbcr == 0) && (MFP_TBCR >= 1) && (MFP_TBCR <= 7))
1.1.1.14 root 1550: MFP_ReadTimerB(true); /* Store result in 'MFP_TB_MAINCOUNTER' */
1.1.1.10 root 1551:
1.1.1.11 root 1552: MFP_TBCR = new_tbcr; /* set to new value before calling MFP_StartTimer */
1553: MFP_StartTimerB(); /* start/stop timer depending on control reg */
1554: }
1.1.1.8 root 1555: }
1556:
1557: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1558: /**
1559: * Handle write to timer C/D control register (0xfffa1d).
1560: */
1.1.1.8 root 1561: void MFP_TimerCDCtrl_WriteByte(void)
1562: {
1.1.1.11 root 1563: Uint8 new_tcdcr;
1.1.1.8 root 1564: Uint8 old_tcdcr;
1565:
1.1.1.10 root 1566: M68000_WaitState(4);
1567:
1.1.1.11 root 1568: new_tcdcr = IoMem[0xfffa1d];
1569: old_tcdcr = MFP_TCDCR;
1.1.1.12 root 1570: //fprintf ( stderr , "write fa1d new %x old %x\n" , IoMem[0xfffa1d] , MFP_TCDCR );
1.1.1.8 root 1571:
1.1.1.12 root 1572: if ((old_tcdcr & 0x70) != (new_tcdcr & 0x70)) /* Timer C control changed */
1.1.1.11 root 1573: {
1574: /* If we stop a timer which was in delay mode, we need to store
1575: * the current value of the counter to be able to read it or to
1576: * continue from where we left if the timer is restarted later
1577: * without writing to the data register. */
1578: if ((new_tcdcr & 0x70) == 0)
1.1.1.14 root 1579: MFP_ReadTimerC(true); /* Store result in 'MFP_TC_MAINCOUNTER' */
1.1.1.11 root 1580:
1.1.1.12 root 1581: 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 1582: MFP_StartTimerC(); /* start/stop timer depending on control reg */
1583: }
1.1.1.8 root 1584:
1.1.1.12 root 1585: if ((old_tcdcr & 0x07) != (new_tcdcr & 0x07)) /* Timer D control changed */
1.1.1.8 root 1586: {
1.1.1.11 root 1587: Uint32 pc = M68000_GetPC();
1.1.1.8 root 1588:
1589: /* Need to change baud rate of RS232 emulation? */
1590: if (ConfigureParams.RS232.bEnableRS232)
1591: {
1592: RS232_SetBaudRateFromTimerD();
1593: }
1594:
1595: if (ConfigureParams.System.bPatchTimerD && !bAppliedTimerDPatch
1.1.1.11 root 1596: && pc >= TosAddress && pc <= TosAddress + TosSize)
1.1.1.8 root 1597: {
1.1.1.11 root 1598: /* Slow down Timer-D if set from TOS for the first time to gain
1599: * more desktop performance.
1600: * Obviously, we need to emulate all timers correctly but TOS sets
1601: * up Timer-D at a very high rate (every couple of instructions).
1602: * The interrupt isn't enabled but the emulator still needs to
1603: * process the interrupt table and this HALVES our frame rate!!!
1.1.1.8 root 1604: * Some games actually reference this timer but don't set it up
1.1.1.11 root 1605: * (eg Paradroid, Speedball I) so we simply intercept the Timer-D
1606: * setup code in TOS and fix the numbers with more 'laid-back'
1607: * values. This still keeps 100% compatibility */
1.1.1.12 root 1608: if ( new_tcdcr & 0x07 ) /* apply patch only if timer D is being started */
1609: {
1610: new_tcdcr = IoMem[0xfffa1d] = (IoMem[0xfffa1d] & 0xf0) | 7;
1.1.1.14 root 1611: bAppliedTimerDPatch = true;
1.1.1.12 root 1612: }
1.1.1.8 root 1613: }
1.1.1.11 root 1614:
1615: /* If we stop a timer which was in delay mode, we need to store the current value */
1616: /* of the counter to be able to read it or to continue from where we left if the timer is */
1617: /* restarted later without writing to the data register. */
1618: if ((new_tcdcr & 0x07) == 0)
1.1.1.14 root 1619: MFP_ReadTimerD(true); /* Stores result in 'MFP_TD_MAINCOUNTER' */
1.1.1.11 root 1620:
1621: MFP_TCDCR = new_tcdcr; /* set to new value before calling MFP_StartTimer */
1.1.1.12 root 1622: MFP_StartTimerD(); /* start/stop timer depending on control reg */
1.1.1.8 root 1623: }
1624: }
1625:
1626: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1627: /**
1628: * Handle write to timer A data register (0xfffa1f).
1629: */
1.1.1.8 root 1630: void MFP_TimerAData_WriteByte(void)
1631: {
1.1.1.10 root 1632: M68000_WaitState(4);
1633:
1.1.1.8 root 1634: MFP_TADR = IoMem[0xfffa1f]; /* Store into data register */
1.1.1.10 root 1635:
1.1.1.8 root 1636: if (MFP_TACR == 0) /* Now check if timer is running - if so do not set */
1637: {
1638: MFP_TA_MAINCOUNTER = MFP_TADR; /* Timer is off, store to main counter */
1.1.1.14 root 1639: TimerACanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 1640: }
1.1.1.12 root 1641:
1.1.1.14 root 1642: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 1643: {
1.1.1.14 root 1644: int FrameCycles, HblCounterVideo, LineCycles;
1645: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1646: 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" ,
1647: MFP_TADR, MFP_TA_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1648: }
1.1.1.8 root 1649: }
1650:
1651: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1652: /**
1653: * Handle write to timer B data register (0xfffa21).
1654: */
1.1.1.8 root 1655: void MFP_TimerBData_WriteByte(void)
1656: {
1.1.1.10 root 1657: M68000_WaitState(4);
1658:
1.1.1.8 root 1659: MFP_TBDR = IoMem[0xfffa21]; /* Store into data register */
1.1.1.10 root 1660:
1.1.1.8 root 1661: if (MFP_TBCR == 0) /* Now check if timer is running - if so do not set */
1662: {
1663: MFP_TB_MAINCOUNTER = MFP_TBDR; /* Timer is off, store to main counter */
1.1.1.14 root 1664: TimerBCanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 1665: }
1.1.1.12 root 1666:
1.1.1.14 root 1667: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 1668: {
1.1.1.14 root 1669: int FrameCycles, HblCounterVideo, LineCycles;
1670: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1671: 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" ,
1672: MFP_TBDR, MFP_TB_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1673: }
1.1.1.8 root 1674: }
1675:
1676: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1677: /**
1678: * Handle write to timer C data register (0xfffa23).
1679: */
1.1.1.8 root 1680: void MFP_TimerCData_WriteByte(void)
1681: {
1.1.1.10 root 1682: M68000_WaitState(4);
1683:
1.1.1.8 root 1684: MFP_TCDR = IoMem[0xfffa23]; /* Store into data register */
1.1.1.10 root 1685:
1.1.1.8 root 1686: if ((MFP_TCDCR&0x70) == 0) /* Now check if timer is running - if so do not set */
1687: {
1.1.1.11 root 1688: MFP_TC_MAINCOUNTER = MFP_TCDR; /* Timer is off, store to main counter */
1.1.1.14 root 1689: TimerCCanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 1690: }
1.1.1.12 root 1691:
1.1.1.14 root 1692: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 1693: {
1.1.1.14 root 1694: int FrameCycles, HblCounterVideo, LineCycles;
1695: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1696: 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" ,
1697: MFP_TCDR, MFP_TC_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1698: }
1.1.1.8 root 1699: }
1700:
1701: /*-----------------------------------------------------------------------*/
1.1.1.11 root 1702: /**
1703: * Handle write to timer D data register (0xfffa25).
1704: */
1.1.1.8 root 1705: void MFP_TimerDData_WriteByte(void)
1706: {
1.1.1.11 root 1707: Uint32 pc = M68000_GetPC();
1.1.1.8 root 1708:
1.1.1.10 root 1709: M68000_WaitState(4);
1710:
1.1.1.8 root 1711: /* Need to change baud rate of RS232 emulation? */
1712: if (ConfigureParams.RS232.bEnableRS232 && (IoMem[0xfffa1d] & 0x07))
1713: {
1714: RS232_SetBaudRateFromTimerD();
1715: }
1716:
1717: /* Patch Timer-D for better performance? */
1718: if (ConfigureParams.System.bPatchTimerD && pc >= TosAddress && pc <= TosAddress + TosSize)
1719: {
1720: nTimerDFakeValue = IoMem[0xfffa25];
1721: IoMem[0xfffa25] = 0x64; /* Slow down the useless Timer-D setup from the bios */
1722: }
1723:
1724: MFP_TDDR = IoMem[0xfffa25]; /* Store into data register */
1725: if ((MFP_TCDCR&0x07) == 0) /* Now check if timer is running - if so do not set */
1726: {
1.1.1.11 root 1727: MFP_TD_MAINCOUNTER = MFP_TDDR; /* Timer is off, store to main counter */
1.1.1.14 root 1728: TimerDCanResume = false; /* we need to set a new int when timer start */
1.1.1.8 root 1729: }
1.1.1.12 root 1730:
1.1.1.14 root 1731: if ( LOG_TRACE_LEVEL( TRACE_MFP_WRITE ) )
1.1.1.12 root 1732: {
1.1.1.14 root 1733: int FrameCycles, HblCounterVideo, LineCycles;
1734: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1735: 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" ,
1736: MFP_TDDR, MFP_TD_MAINCOUNTER, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
1.1.1.12 root 1737: }
1.1.1.8 root 1738: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.