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