disabling inline set/gets + enable sa-1 c cpu
[drnoksnes] / cpuexec.cpp
1 /*
2  * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3  *
4  * (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
5  *                           Jerremy Koot (jkoot@snes9x.com)
6  *
7  * Super FX C emulator code 
8  * (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
9  *                           Gary Henderson.
10  * Super FX     assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
11  *
12  * DSP1 emulator code (c) Copyright 1998 Ivar, _Demo_ and Gary Henderson.
13  * C4 asm and some C emulation code (c) Copyright 2000 zsKnight and _Demo_.
14  * C4 C code (c) Copyright 2001 Gary Henderson (gary.henderson@ntlworld.com).
15  *
16  * DOS port code contains the works of other authors. See headers in
17  * individual files.
18  *
19  * Snes9x homepage: http://www.snes9x.com
20  *
21  * Permission to use, copy, modify and distribute Snes9x in both binary and
22  * source form, for non-commercial purposes, is hereby granted without fee,
23  * providing that this license information and copyright notice appear with
24  * all copies and any derived work.
25  *
26  * This software is provided 'as-is', without any express or implied
27  * warranty. In no event shall the authors be held liable for any damages
28  * arising from the use of this software.
29  *
30  * Snes9x is freeware for PERSONAL USE only. Commercial users should
31  * seek permission of the copyright holders first. Commercial use includes
32  * charging money for Snes9x or software derived from Snes9x.
33  *
34  * The copyright holders request that bug fixes and improvements to the code
35  * should be forwarded to them so everyone can benefit from the modifications
36  * in future versions.
37  *
38  * Super NES and Super Nintendo Entertainment System are trademarks of
39  * Nintendo Co., Limited and its subsidiary companies.
40  */
41  
42
43  
44 #include "snes9x.h"
45
46 #include "memmap.h"
47 #include "ppu.h"
48 #include "cpuexec.h"
49 #include "debug.h"
50 #include "snapshot.h"
51 #include "gfx.h"
52 #include "missing.h"
53 #include "apu.h"
54 #include "dma.h"
55 #include "fxemu.h"
56
57 #if !CONF_BUILD_ASM_CPU
58 #include "cpuops.h"
59 #endif
60
61 #ifdef USE_SA1
62 #include "sa1.h"
63 #endif
64
65
66 #if CONF_BUILD_ASM_CPU
67 #include "os9x_asm_cpu.h"
68 // for asm core:
69 uint16 mem_check=0;
70 #endif
71
72 #if defined(__showframe__)
73 int framecpt=0;
74 #endif
75
76 void S9xMainLoop (void)
77 {       
78 #if defined(__showframe__)
79         framecpt++;
80         char stra[64];  
81         sprintf(stra,"framecpt : %d",framecpt);
82         S9xMessage(0,0,stra);
83 #endif  
84
85 #if CONF_BUILD_ASM_CPU
86         asmMainLoop(&CPU);
87 #else
88         for (;;) {
89                 APU_EXECUTE(1);
90                 if (CPU.Flags) {
91                         if (CPU.Flags & NMI_FLAG) {
92                                 if (--CPU.NMICycleCount == 0) {
93                                         CPU.Flags &= ~NMI_FLAG;
94                                         if (CPU.WaitingForInterrupt) {
95                                                 CPU.WaitingForInterrupt = FALSE;
96                                                 CPU.PC++;
97                                         }
98                                         S9xOpcode_NMI ();
99                                 }
100                         }
101
102                         if (CPU.Flags & IRQ_PENDING_FLAG) {
103                                 if (CPU.IRQCycleCount == 0)     {
104                                         if (CPU.WaitingForInterrupt) {
105                                                 CPU.WaitingForInterrupt = FALSE;
106                                                 CPU.PC++;
107                                         }
108                                         if (CPU.IRQActive && !Settings.DisableIRQ) {
109                                                 if (!CheckFlag(IRQ))
110                                                         S9xOpcode_IRQ ();
111                                         } else {
112                                                 CPU.Flags &= ~IRQ_PENDING_FLAG;
113                                         }
114                                 } else {
115                                         CPU.IRQCycleCount--;
116                                 }
117                         }
118
119                         if (CPU.Flags & SCAN_KEYS_FLAG)
120                                 break;
121                 }
122
123 #ifdef CPU_SHUTDOWN
124                 CPU.PCAtOpcodeStart = CPU.PC;
125 #endif
126                 CPU.Cycles += CPU.MemSpeed;
127
128                 (*ICPU.S9xOpcodes[*CPU.PC++].S9xOpcode) ();
129
130 #ifdef USE_SA1
131                 if (SA1.Executing)
132                         S9xSA1MainLoop ();
133 #endif
134
135                 DO_HBLANK_CHECK ();
136         }
137 #endif
138
139     Registers.PC = CPU.PC - CPU.PCBase;
140     //S9xPackStatus (); // not needed
141     S9xAPUPackStatus ();
142     
143     if (CPU.Flags & SCAN_KEYS_FLAG)
144     {
145                 CPU.Flags &= ~SCAN_KEYS_FLAG;
146     }
147     
148     if (CPU.BRKTriggered && Settings.SuperFX && !CPU.TriedInterleavedMode2)
149     {
150         CPU.TriedInterleavedMode2 = TRUE;
151         CPU.BRKTriggered = FALSE;
152         S9xDeinterleaveMode2 ();
153     }
154 }
155
156 void S9xSetIRQ (uint32 source)
157 {
158     CPU.IRQActive |= source;
159     CPU.Flags |= IRQ_PENDING_FLAG;
160     CPU.IRQCycleCount = 3;
161     if (CPU.WaitingForInterrupt)
162     {
163         // Force IRQ to trigger immediately after WAI - 
164         // Final Fantasy Mystic Quest crashes without this.
165         CPU.IRQCycleCount = 0;
166         CPU.WaitingForInterrupt = FALSE;
167         CPU.PC++;
168     }
169 }
170
171 void S9xClearIRQ (uint32 source)
172 {
173     CLEAR_IRQ_SOURCE (source);
174 }
175
176
177 void S9xDoHBlankProcessing ()
178 {
179 #ifdef CPU_SHUTDOWN
180     CPU.WaitCounter++;
181 #endif
182     switch (CPU.WhichEvent)
183     {
184     case HBLANK_START_EVENT:
185                 if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight)
186                         IPPU.HDMA = S9xDoHDMA (IPPU.HDMA);
187                 break;
188
189     case HBLANK_END_EVENT:
190                 APU_EXECUTE(3); // notaz: run spc700 in sound 'speed hack' mode
191
192                 if(Settings.SuperFX)
193                         S9xSuperFXExec ();
194
195                 CPU.Cycles -= Settings.H_Max;
196                 if (/*IAPU.APUExecuting*/CPU.APU_APUExecuting)
197                         CPU.APU_Cycles -= Settings.H_Max;
198                 else
199                         CPU.APU_Cycles = 0;
200
201                 CPU.NextEvent = -1;
202                 ICPU.Scanline++;
203
204                 if (++CPU.V_Counter > (Settings.PAL ? SNES_MAX_PAL_VCOUNTER : SNES_MAX_NTSC_VCOUNTER))
205                 {
206                         PPU.OAMAddr = PPU.SavedOAMAddr;
207                         PPU.OAMFlip = 0;
208                         CPU.V_Counter = 0;
209                         CPU.NMIActive = FALSE;
210                         ICPU.Frame++;
211                         PPU.HVBeamCounterLatched = 0;
212                         CPU.Flags |= SCAN_KEYS_FLAG;
213                         S9xStartHDMA ();
214                 }
215
216                 if (PPU.VTimerEnabled && !PPU.HTimerEnabled &&
217                         CPU.V_Counter == PPU.IRQVBeamPos)
218                 {
219                         S9xSetIRQ (PPU_V_BEAM_IRQ_SOURCE);
220                 }
221
222                 if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE)
223                 {
224                         // Start of V-blank
225                         S9xEndScreenRefresh ();
226                         PPU.FirstSprite = 0;
227                         IPPU.HDMA = 0;
228                         // Bits 7 and 6 of $4212 are computed when read in S9xGetPPU.
229                         missing.dma_this_frame = 0;
230                         IPPU.MaxBrightness = PPU.Brightness;
231                         PPU.ForcedBlanking = (Memory.FillRAM [0x2100] >> 7) & 1;
232
233                         Memory.FillRAM[0x4210] = 0x80;
234                         if (Memory.FillRAM[0x4200] & 0x80)
235                         {
236                         CPU.NMIActive = TRUE;
237                         CPU.Flags |= NMI_FLAG;
238                         CPU.NMICycleCount = CPU.NMITriggerPoint;
239                         }
240
241         #ifdef OLD_SNAPSHOT_CODE
242                         if (CPU.Flags & SAVE_SNAPSHOT_FLAG)
243                         {
244                         CPU.Flags &= ~SAVE_SNAPSHOT_FLAG;
245                         Registers.PC = CPU.PC - CPU.PCBase;
246                         //S9xPackStatus (); // not needed
247                         S9xAPUPackStatus ();
248                         Snapshot (NULL);
249                         }
250         #endif
251                         }
252
253                 if (CPU.V_Counter == PPU.ScreenHeight + 3)
254                         S9xUpdateJoypads ();
255
256                 if (CPU.V_Counter == FIRST_VISIBLE_LINE)
257                 {
258                         Memory.FillRAM[0x4210] = 0;
259                         CPU.Flags &= ~NMI_FLAG;
260                         S9xStartScreenRefresh ();
261                 }
262                 if (CPU.V_Counter >= FIRST_VISIBLE_LINE &&
263                         CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE)
264                 {
265                         RenderLine (CPU.V_Counter - FIRST_VISIBLE_LINE);
266                 }
267                 // Use TimerErrorCounter to skip update of SPC700 timers once
268                 // every 128 updates. Needed because this section of code is called
269                 // once every emulated 63.5 microseconds, which coresponds to
270                 // 15.750KHz, but the SPC700 timers need to be updated at multiples
271                 // of 8KHz, hence the error correction.
272         //      IAPU.TimerErrorCounter++;
273         //      if (IAPU.TimerErrorCounter >= )
274         //          IAPU.TimerErrorCounter = 0;
275         //      else
276                 {
277                         if (APU.TimerEnabled [2])
278                         {
279                         APU.Timer [2] += 4;
280                         while (APU.Timer [2] >= APU.TimerTarget [2])
281                         {
282                                 IAPU.RAM [0xff] = (IAPU.RAM [0xff] + 1) & 0xf;
283                                 APU.Timer [2] -= APU.TimerTarget [2];
284 #ifdef SPC700_SHUTDOWN          
285                                 IAPU.WaitCounter++;
286                                 /*IAPU.APUExecuting*/CPU.APU_APUExecuting= TRUE;
287 #endif          
288                         }
289                         }
290                         if (CPU.V_Counter & 1)
291                         {
292                         if (APU.TimerEnabled [0])
293                         {
294                                 APU.Timer [0]++;
295                                 if (APU.Timer [0] >= APU.TimerTarget [0])
296                                 {
297                                 IAPU.RAM [0xfd] = (IAPU.RAM [0xfd] + 1) & 0xf;
298                                 APU.Timer [0] = 0;
299 #ifdef SPC700_SHUTDOWN          
300                                 IAPU.WaitCounter++;
301                                 /*IAPU.APUExecuting*/CPU.APU_APUExecuting = TRUE;
302 #endif              
303                                 }
304                         }
305                         if (APU.TimerEnabled [1])
306                         {
307                                 APU.Timer [1]++;
308                                 if (APU.Timer [1] >= APU.TimerTarget [1])
309                                 {
310                                 IAPU.RAM [0xfe] = (IAPU.RAM [0xfe] + 1) & 0xf;
311                                 APU.Timer [1] = 0;
312 #ifdef SPC700_SHUTDOWN          
313                                 IAPU.WaitCounter++;
314                                 /*IAPU.APUExecuting*/CPU.APU_APUExecuting = TRUE;
315 #endif              
316                                 }
317                         }
318                         }
319                 }
320                 break;
321         case HTIMER_BEFORE_EVENT:
322         case HTIMER_AFTER_EVENT:
323                 if (PPU.HTimerEnabled &&
324                         (!PPU.VTimerEnabled || CPU.V_Counter == PPU.IRQVBeamPos))
325                 {
326                         S9xSetIRQ (PPU_H_BEAM_IRQ_SOURCE);
327                 }
328                 break;
329     }
330     S9xReschedule ();
331 }