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