workaround a problem with the harmattan gcc
[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 #if !CONF_BUILD_ASM_CPU
141     S9xPackStatus ();
142 #endif
143     S9xAPUPackStatus ();
144     
145     if (CPU.Flags & SCAN_KEYS_FLAG)
146     {
147                 CPU.Flags &= ~SCAN_KEYS_FLAG;
148     }
149     
150     if (CPU.BRKTriggered && Settings.SuperFX && !CPU.TriedInterleavedMode2)
151     {
152         CPU.TriedInterleavedMode2 = TRUE;
153         CPU.BRKTriggered = FALSE;
154         S9xDeinterleaveMode2 ();
155     }
156 }
157
158 void S9xSetIRQ (uint32 source)
159 {
160     CPU.IRQActive |= source;
161     CPU.Flags |= IRQ_PENDING_FLAG;
162     CPU.IRQCycleCount = 3;
163     if (CPU.WaitingForInterrupt)
164     {
165         // Force IRQ to trigger immediately after WAI - 
166         // Final Fantasy Mystic Quest crashes without this.
167         CPU.IRQCycleCount = 0;
168         CPU.WaitingForInterrupt = FALSE;
169         CPU.PC++;
170     }
171 }
172
173 void S9xClearIRQ (uint32 source)
174 {
175     CLEAR_IRQ_SOURCE (source);
176 }
177
178
179 void S9xDoHBlankProcessing ()
180 {
181 #ifdef CPU_SHUTDOWN
182     CPU.WaitCounter++;
183 #endif
184     switch (CPU.WhichEvent)
185     {
186     case HBLANK_START_EVENT:
187                 if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight)
188                         IPPU.HDMA = S9xDoHDMA (IPPU.HDMA);
189                 break;
190
191     case HBLANK_END_EVENT:
192                 APU_EXECUTE(3); // notaz: run spc700 in sound 'speed hack' mode
193
194                 if(Settings.SuperFX)
195                         S9xSuperFXExec ();
196
197                 CPU.Cycles -= Settings.H_Max;
198                 if (/*IAPU.APUExecuting*/CPU.APU_APUExecuting)
199                         CPU.APU_Cycles -= Settings.H_Max;
200                 else
201                         CPU.APU_Cycles = 0;
202
203                 CPU.NextEvent = -1;
204                 ICPU.Scanline++;
205
206                 if (++CPU.V_Counter > (Settings.PAL ? SNES_MAX_PAL_VCOUNTER : SNES_MAX_NTSC_VCOUNTER))
207                 {
208                         PPU.OAMAddr = PPU.SavedOAMAddr;
209                         PPU.OAMFlip = 0;
210                         CPU.V_Counter = 0;
211                         CPU.NMIActive = FALSE;
212                         ICPU.Frame++;
213                         PPU.HVBeamCounterLatched = 0;
214                         CPU.Flags |= SCAN_KEYS_FLAG;
215                         S9xStartHDMA ();
216                 }
217
218                 if (PPU.VTimerEnabled && !PPU.HTimerEnabled &&
219                         CPU.V_Counter == PPU.IRQVBeamPos)
220                 {
221                         S9xSetIRQ (PPU_V_BEAM_IRQ_SOURCE);
222                 }
223
224                 if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE)
225                 {
226                         // Start of V-blank
227                         S9xEndScreenRefresh ();
228                         PPU.FirstSprite = 0;
229                         IPPU.HDMA = 0;
230                         // Bits 7 and 6 of $4212 are computed when read in S9xGetPPU.
231                         missing.dma_this_frame = 0;
232                         IPPU.MaxBrightness = PPU.Brightness;
233                         PPU.ForcedBlanking = (Memory.FillRAM [0x2100] >> 7) & 1;
234
235                         Memory.FillRAM[0x4210] = 0x80;
236                         if (Memory.FillRAM[0x4200] & 0x80)
237                         {
238                         CPU.NMIActive = TRUE;
239                         CPU.Flags |= NMI_FLAG;
240                         CPU.NMICycleCount = CPU.NMITriggerPoint;
241                         }
242                         }
243
244                 if (CPU.V_Counter == PPU.ScreenHeight + 3)
245                         S9xUpdateJoypads ();
246
247                 if (CPU.V_Counter == FIRST_VISIBLE_LINE)
248                 {
249                         Memory.FillRAM[0x4210] = 0;
250                         CPU.Flags &= ~NMI_FLAG;
251                         S9xStartScreenRefresh ();
252                 }
253                 if (CPU.V_Counter >= FIRST_VISIBLE_LINE &&
254                         CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE)
255                 {
256                         RenderLine (CPU.V_Counter - FIRST_VISIBLE_LINE);
257                 }
258                 // Use TimerErrorCounter to skip update of SPC700 timers once
259                 // every 128 updates. Needed because this section of code is called
260                 // once every emulated 63.5 microseconds, which coresponds to
261                 // 15.750KHz, but the SPC700 timers need to be updated at multiples
262                 // of 8KHz, hence the error correction.
263         //      IAPU.TimerErrorCounter++;
264         //      if (IAPU.TimerErrorCounter >= )
265         //          IAPU.TimerErrorCounter = 0;
266         //      else
267                 {
268                         if (APU.TimerEnabled [2])
269                         {
270                         APU.Timer [2] += 4;
271                         while (APU.Timer [2] >= APU.TimerTarget [2])
272                         {
273                                 IAPU.RAM [0xff] = (IAPU.RAM [0xff] + 1) & 0xf;
274                                 APU.Timer [2] -= APU.TimerTarget [2];
275 #ifdef SPC700_SHUTDOWN          
276                                 IAPU.WaitCounter++;
277                                 /*IAPU.APUExecuting*/CPU.APU_APUExecuting= TRUE;
278 #endif          
279                         }
280                         }
281                         if (CPU.V_Counter & 1)
282                         {
283                         if (APU.TimerEnabled [0])
284                         {
285                                 APU.Timer [0]++;
286                                 if (APU.Timer [0] >= APU.TimerTarget [0])
287                                 {
288                                 IAPU.RAM [0xfd] = (IAPU.RAM [0xfd] + 1) & 0xf;
289                                 APU.Timer [0] = 0;
290 #ifdef SPC700_SHUTDOWN          
291                                 IAPU.WaitCounter++;
292                                 /*IAPU.APUExecuting*/CPU.APU_APUExecuting = TRUE;
293 #endif              
294                                 }
295                         }
296                         if (APU.TimerEnabled [1])
297                         {
298                                 APU.Timer [1]++;
299                                 if (APU.Timer [1] >= APU.TimerTarget [1])
300                                 {
301                                 IAPU.RAM [0xfe] = (IAPU.RAM [0xfe] + 1) & 0xf;
302                                 APU.Timer [1] = 0;
303 #ifdef SPC700_SHUTDOWN          
304                                 IAPU.WaitCounter++;
305                                 /*IAPU.APUExecuting*/CPU.APU_APUExecuting = TRUE;
306 #endif              
307                                 }
308                         }
309                         }
310                 }
311                 break;
312         case HTIMER_BEFORE_EVENT:
313         case HTIMER_AFTER_EVENT:
314                 if (PPU.HTimerEnabled &&
315                         (!PPU.VTimerEnabled || CPU.V_Counter == PPU.IRQVBeamPos))
316                 {
317                         S9xSetIRQ (PPU_H_BEAM_IRQ_SOURCE);
318                 }
319                 break;
320     }
321     S9xReschedule ();
322 }