2 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
4 * (c) Copyright 1996 - 2001 Gary Henderson (gary.henderson@ntlworld.com) and
5 * Jerremy Koot (jkoot@snes9x.com)
7 * Super FX C emulator code
8 * (c) Copyright 1997 - 1999 Ivar (ivar@snes9x.com) and
10 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
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).
16 * DOS port code contains the works of other authors. See headers in
19 * Snes9x homepage: http://www.snes9x.com
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.
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.
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.
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
38 * Super NES and Super Nintendo Entertainment System are trademarks of
39 * Nintendo Co., Limited and its subsidiary companies.
57 #if !CONF_BUILD_ASM_CPU
66 #if CONF_BUILD_ASM_CPU
67 #include "os9x_asm_cpu.h"
72 #if defined(__showframe__)
76 void S9xMainLoop (void)
78 #if defined(__showframe__)
81 sprintf(stra,"framecpt : %d",framecpt);
85 #if CONF_BUILD_ASM_CPU
91 if (CPU.Flags & NMI_FLAG) {
92 if (--CPU.NMICycleCount == 0) {
93 CPU.Flags &= ~NMI_FLAG;
94 if (CPU.WaitingForInterrupt) {
95 CPU.WaitingForInterrupt = FALSE;
102 if (CPU.Flags & IRQ_PENDING_FLAG) {
103 if (CPU.IRQCycleCount == 0) {
104 if (CPU.WaitingForInterrupt) {
105 CPU.WaitingForInterrupt = FALSE;
108 if (CPU.IRQActive && !Settings.DisableIRQ) {
112 CPU.Flags &= ~IRQ_PENDING_FLAG;
119 if (CPU.Flags & SCAN_KEYS_FLAG)
124 CPU.PCAtOpcodeStart = CPU.PC;
126 CPU.Cycles += CPU.MemSpeed;
128 (*ICPU.S9xOpcodes[*CPU.PC++].S9xOpcode) ();
139 Registers.PC = CPU.PC - CPU.PCBase;
140 //S9xPackStatus (); // not needed
143 if (CPU.Flags & SCAN_KEYS_FLAG)
145 CPU.Flags &= ~SCAN_KEYS_FLAG;
148 if (CPU.BRKTriggered && Settings.SuperFX && !CPU.TriedInterleavedMode2)
150 CPU.TriedInterleavedMode2 = TRUE;
151 CPU.BRKTriggered = FALSE;
152 S9xDeinterleaveMode2 ();
156 void S9xSetIRQ (uint32 source)
158 CPU.IRQActive |= source;
159 CPU.Flags |= IRQ_PENDING_FLAG;
160 CPU.IRQCycleCount = 3;
161 if (CPU.WaitingForInterrupt)
163 // Force IRQ to trigger immediately after WAI -
164 // Final Fantasy Mystic Quest crashes without this.
165 CPU.IRQCycleCount = 0;
166 CPU.WaitingForInterrupt = FALSE;
171 void S9xClearIRQ (uint32 source)
173 CLEAR_IRQ_SOURCE (source);
177 void S9xDoHBlankProcessing ()
182 switch (CPU.WhichEvent)
184 case HBLANK_START_EVENT:
185 if (IPPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight)
186 IPPU.HDMA = S9xDoHDMA (IPPU.HDMA);
189 case HBLANK_END_EVENT:
190 APU_EXECUTE(3); // notaz: run spc700 in sound 'speed hack' mode
195 CPU.Cycles -= Settings.H_Max;
196 if (/*IAPU.APUExecuting*/CPU.APU_APUExecuting)
197 CPU.APU_Cycles -= Settings.H_Max;
204 if (++CPU.V_Counter > (Settings.PAL ? SNES_MAX_PAL_VCOUNTER : SNES_MAX_NTSC_VCOUNTER))
206 PPU.OAMAddr = PPU.SavedOAMAddr;
209 CPU.NMIActive = FALSE;
211 PPU.HVBeamCounterLatched = 0;
212 CPU.Flags |= SCAN_KEYS_FLAG;
216 if (PPU.VTimerEnabled && !PPU.HTimerEnabled &&
217 CPU.V_Counter == PPU.IRQVBeamPos)
219 S9xSetIRQ (PPU_V_BEAM_IRQ_SOURCE);
222 if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE)
225 S9xEndScreenRefresh ();
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;
233 Memory.FillRAM[0x4210] = 0x80;
234 if (Memory.FillRAM[0x4200] & 0x80)
236 CPU.NMIActive = TRUE;
237 CPU.Flags |= NMI_FLAG;
238 CPU.NMICycleCount = CPU.NMITriggerPoint;
241 #ifdef OLD_SNAPSHOT_CODE
242 if (CPU.Flags & SAVE_SNAPSHOT_FLAG)
244 CPU.Flags &= ~SAVE_SNAPSHOT_FLAG;
245 Registers.PC = CPU.PC - CPU.PCBase;
246 //S9xPackStatus (); // not needed
253 if (CPU.V_Counter == PPU.ScreenHeight + 3)
256 if (CPU.V_Counter == FIRST_VISIBLE_LINE)
258 Memory.FillRAM[0x4210] = 0;
259 CPU.Flags &= ~NMI_FLAG;
260 S9xStartScreenRefresh ();
262 if (CPU.V_Counter >= FIRST_VISIBLE_LINE &&
263 CPU.V_Counter < PPU.ScreenHeight + FIRST_VISIBLE_LINE)
265 RenderLine (CPU.V_Counter - FIRST_VISIBLE_LINE);
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;
277 if (APU.TimerEnabled [2])
280 while (APU.Timer [2] >= APU.TimerTarget [2])
282 IAPU.RAM [0xff] = (IAPU.RAM [0xff] + 1) & 0xf;
283 APU.Timer [2] -= APU.TimerTarget [2];
284 #ifdef SPC700_SHUTDOWN
286 /*IAPU.APUExecuting*/CPU.APU_APUExecuting= TRUE;
290 if (CPU.V_Counter & 1)
292 if (APU.TimerEnabled [0])
295 if (APU.Timer [0] >= APU.TimerTarget [0])
297 IAPU.RAM [0xfd] = (IAPU.RAM [0xfd] + 1) & 0xf;
299 #ifdef SPC700_SHUTDOWN
301 /*IAPU.APUExecuting*/CPU.APU_APUExecuting = TRUE;
305 if (APU.TimerEnabled [1])
308 if (APU.Timer [1] >= APU.TimerTarget [1])
310 IAPU.RAM [0xfe] = (IAPU.RAM [0xfe] + 1) & 0xf;
312 #ifdef SPC700_SHUTDOWN
314 /*IAPU.APUExecuting*/CPU.APU_APUExecuting = TRUE;
321 case HTIMER_BEFORE_EVENT:
322 case HTIMER_AFTER_EVENT:
323 if (PPU.HTimerEnabled &&
324 (!PPU.VTimerEnabled || CPU.V_Counter == PPU.IRQVBeamPos))
326 S9xSetIRQ (PPU_H_BEAM_IRQ_SOURCE);