From fd8a742cd95a685fd663726278dd782f6146de3b Mon Sep 17 00:00:00 2001 From: dborth Date: Thu, 24 Mar 2011 01:07:47 +0000 Subject: [PATCH] revert zones' improved IRQ and SA-1 changes committed in r661 - these are detrimental to performance --- source/snes9x/cheats2.cpp | 6 + source/snes9x/controls.cpp | 6 + source/snes9x/cpu.cpp | 14 +- source/snes9x/cpuexec.cpp | 226 ++++++++------ source/snes9x/cpuexec.h | 101 +++++-- source/snes9x/cpumacro.h | 13 + source/snes9x/cpuops.cpp | 174 ++++++++++- source/snes9x/cpuops.h | 9 +- source/snes9x/dma.cpp | 18 +- source/snes9x/fxemu.cpp | 10 +- source/snes9x/getset.h | 66 ++++- source/snes9x/memmap.cpp | 256 +++++++++++++++- source/snes9x/movie.cpp | 16 +- source/snes9x/ppu.cpp | 282 ++++++++++++++++-- source/snes9x/ppu.h | 12 + source/snes9x/sa1.cpp | 582 ++++++++++++++++++++----------------- source/snes9x/sa1.h | 34 ++- source/snes9x/sa1cpu.cpp | 150 ++-------- source/snes9x/snapshot.cpp | 82 +----- source/snes9x/snapshot.h | 2 +- source/snes9x/snes9x.h | 38 ++- 21 files changed, 1414 insertions(+), 683 deletions(-) diff --git a/source/snes9x/cheats2.cpp b/source/snes9x/cheats2.cpp index fc076b5..d174c5c 100644 --- a/source/snes9x/cheats2.cpp +++ b/source/snes9x/cheats2.cpp @@ -186,9 +186,12 @@ static void S9xSetByteFree (uint8, uint32); static uint8 S9xGetByteFree (uint32 address) { uint32 Cycles = CPU.Cycles; + uint32 WaitAddress = CPU.WaitAddress; uint8 byte; byte = S9xGetByte(address); + + CPU.WaitAddress = WaitAddress; CPU.Cycles = Cycles; return (byte); @@ -197,8 +200,11 @@ static uint8 S9xGetByteFree (uint32 address) static void S9xSetByteFree (uint8 byte, uint32 address) { uint32 Cycles = CPU.Cycles; + uint32 WaitAddress = CPU.WaitAddress; S9xSetByte(byte, address); + + CPU.WaitAddress = WaitAddress; CPU.Cycles = Cycles; } diff --git a/source/snes9x/controls.cpp b/source/snes9x/controls.cpp index 267d07f..b94a402 100644 --- a/source/snes9x/controls.cpp +++ b/source/snes9x/controls.cpp @@ -429,6 +429,7 @@ static const int ptrspeeds[4] = { 1, 1, 4, 8 }; S(ToggleBG2), \ S(ToggleBG3), \ S(ToggleEmuTurbo), \ + S(ToggleHDMA), \ S(ToggleSprites), \ S(ToggleTransparency) \ @@ -2457,6 +2458,11 @@ void S9xApplyCommand (s9xcommand_t cmd, int16 data1, int16 data2) DisplayStateChange("Sprites", !(Settings.BG_Forced & 16)); break; + case ToggleHDMA: + Settings.DisableHDMA = !Settings.DisableHDMA; + DisplayStateChange("HDMA emulation", !Settings.DisableHDMA); + break; + case ToggleTransparency: Settings.Transparency = !Settings.Transparency; DisplayStateChange("Transparency effects", Settings.Transparency); diff --git a/source/snes9x/cpu.cpp b/source/snes9x/cpu.cpp index 547a446..f2efb4c 100644 --- a/source/snes9x/cpu.cpp +++ b/source/snes9x/cpu.cpp @@ -208,16 +208,12 @@ static void S9xResetCPU (void) static void S9xSoftResetCPU (void) { CPU.Cycles = 182; // Or 188. This is the cycle count just after the jump to the Reset Vector. - CPU.PrevCycles = CPU.Cycles; + CPU.PrevCycles = -1; CPU.V_Counter = 0; CPU.Flags = CPU.Flags & (DEBUG_MODE_FLAG | TRACE_FLAG); CPU.PCBase = NULL; - CPU.NMILine = FALSE; - CPU.IRQLine = FALSE; - CPU.IRQTransition = FALSE; - CPU.IRQLastState = FALSE; - CPU.IRQExternal = FALSE; - CPU.IRQPending = Timings.IRQPendCount; + CPU.IRQActive = FALSE; + CPU.IRQPending = 0; CPU.MemSpeed = SLOW_ONE_CYCLE; CPU.MemSpeedx2 = SLOW_ONE_CYCLE * 2; CPU.FastROMSpeed = SLOW_ONE_CYCLE; @@ -230,6 +226,9 @@ static void S9xSoftResetCPU (void) CPU.WhichEvent = HC_RENDER_EVENT; CPU.NextEvent = Timings.RenderPos; CPU.WaitingForInterrupt = FALSE; + CPU.WaitAddress = 0xffffffff; + CPU.WaitCounter = 0; + CPU.PBPCAtOpcodeStart = 0xffffffff; CPU.AutoSaveTimer = 0; CPU.SRAMModified = FALSE; @@ -262,6 +261,7 @@ static void S9xSoftResetCPU (void) ICPU.S9xOpcodes = S9xOpcodesE1; ICPU.S9xOpLengths = S9xOpLengthsM1X1; + ICPU.CPUExecuting = TRUE; S9xUnpackStatus(); } diff --git a/source/snes9x/cpuexec.cpp b/source/snes9x/cpuexec.cpp index 5b1656e..4bd4c25 100644 --- a/source/snes9x/cpuexec.cpp +++ b/source/snes9x/cpuexec.cpp @@ -187,91 +187,102 @@ #include "missing.h" #endif -static inline void S9xReschedule (void); - void S9xMainLoop (void) { for (;;) { - if (CPU.NMILine) + if (CPU.Flags) { - if (Timings.NMITriggerPos <= CPU.Cycles) + if (CPU.Flags & NMI_FLAG) { - CPU.NMILine = FALSE; - Timings.NMITriggerPos = 0xffff; - if (CPU.WaitingForInterrupt) + if (Timings.NMITriggerPos <= CPU.Cycles) { - CPU.WaitingForInterrupt = FALSE; - Registers.PCw++; + CPU.Flags &= ~NMI_FLAG; + Timings.NMITriggerPos = 0xffff; + if (CPU.WaitingForInterrupt) + { + CPU.WaitingForInterrupt = FALSE; + Registers.PCw++; + } + + S9xOpcode_NMI(); } - - S9xOpcode_NMI(); } - } - if (CPU.IRQTransition || CPU.IRQExternal) - { - if (CPU.IRQPending) - CPU.IRQPending--; - else + #ifdef DEBUGGER + if ((CPU.Flags & BREAK_FLAG) && !(CPU.Flags & SINGLE_STEP_FLAG)) { - if (CPU.WaitingForInterrupt) + for (int Break = 0; Break != 6; Break++) { - CPU.WaitingForInterrupt = FALSE; - Registers.PCw++; + if (S9xBreakpoint[Break].Enabled && + S9xBreakpoint[Break].Bank == Registers.PB && + S9xBreakpoint[Break].Address == Registers.PCw) + { + if (S9xBreakpoint[Break].Enabled == 2) + S9xBreakpoint[Break].Enabled = TRUE; + else + CPU.Flags |= DEBUG_MODE_FLAG; + } } - - CPU.IRQTransition = FALSE; - CPU.IRQPending = Timings.IRQPendCount; - - if (!CheckFlag(IRQ)) - S9xOpcode_IRQ(); } - } + #endif - #ifdef DEBUGGER - if ((CPU.Flags & BREAK_FLAG) && !(CPU.Flags & SINGLE_STEP_FLAG)) - { - for (int Break = 0; Break != 6; Break++) + if (CPU.Flags & IRQ_FLAG) { - if (S9xBreakpoint[Break].Enabled && - S9xBreakpoint[Break].Bank == Registers.PB && - S9xBreakpoint[Break].Address == Registers.PCw) + if (CPU.IRQPending) + // FIXME: In case of IRQ during WRAM refresh + CPU.IRQPending--; + else { - if (S9xBreakpoint[Break].Enabled == 2) - S9xBreakpoint[Break].Enabled = TRUE; + if (CPU.WaitingForInterrupt) + { + CPU.WaitingForInterrupt = FALSE; + Registers.PCw++; + } + + if (CPU.IRQActive && !Settings.DisableIRQ) + { + if (!CheckFlag(IRQ)) + // in IRQ handler $4211 is supposed to be read, so IRQ_FLAG should be cleared. + S9xOpcode_IRQ(); + } else - CPU.Flags |= DEBUG_MODE_FLAG; + CPU.Flags &= ~IRQ_FLAG; } } + + if (CPU.Flags & SCAN_KEYS_FLAG) + break; + + #ifdef DEBUGGER + if (CPU.Flags & DEBUG_MODE_FLAG) + break; + + if (CPU.Flags & TRACE_FLAG) + S9xTrace(); + + if (CPU.Flags & SINGLE_STEP_FLAG) + { + CPU.Flags &= ~SINGLE_STEP_FLAG; + CPU.Flags |= DEBUG_MODE_FLAG; + } + #endif } - if (CPU.Flags & DEBUG_MODE_FLAG) - break; - - if (CPU.Flags & TRACE_FLAG) - S9xTrace(); - - if (CPU.Flags & SINGLE_STEP_FLAG) - { - CPU.Flags &= ~SINGLE_STEP_FLAG; - CPU.Flags |= DEBUG_MODE_FLAG; - } + #ifdef CPU_SHUTDOWN + CPU.PBPCAtOpcodeStart = Registers.PBPC; #endif - if (CPU.Flags & SCAN_KEYS_FLAG) - break; - register uint8 Op; register struct SOpcodes *Opcodes; + CPU.PrevCycles = CPU.Cycles; + if (CPU.PCBase) { Op = CPU.PCBase[Registers.PCw]; - CPU.PrevCycles = CPU.Cycles; CPU.Cycles += CPU.MemSpeed; - S9xCheckInterrupts(); Opcodes = ICPU.S9xOpcodes; } else @@ -293,8 +304,13 @@ void S9xMainLoop (void) Registers.PCw++; (*Opcodes[Op].S9xOpcode)(); - if (Settings.SA1) + if (SA1.Executing) S9xSA1MainLoop(); + + #if (S9X_ACCURACY_LEVEL <= 2) + while (CPU.Cycles >= CPU.NextEvent) + S9xDoHEventProcessing(); + #endif } S9xPackStatus(); @@ -309,54 +325,54 @@ void S9xMainLoop (void) } } -static inline void S9xReschedule (void) +void S9xSetIRQ (uint32 source) { - switch (CPU.WhichEvent) + CPU.IRQActive |= source; + CPU.IRQPending = Timings.IRQPendCount; + CPU.Flags |= IRQ_FLAG; + + if (CPU.WaitingForInterrupt) { - case HC_HBLANK_START_EVENT: - CPU.WhichEvent = HC_HDMA_START_EVENT; - CPU.NextEvent = Timings.HDMAStart; - break; - - case HC_HDMA_START_EVENT: - CPU.WhichEvent = HC_HCOUNTER_MAX_EVENT; - CPU.NextEvent = Timings.H_Max; - break; - - case HC_HCOUNTER_MAX_EVENT: - CPU.WhichEvent = HC_HDMA_INIT_EVENT; - CPU.NextEvent = Timings.HDMAInit; - break; - - case HC_HDMA_INIT_EVENT: - CPU.WhichEvent = HC_RENDER_EVENT; - CPU.NextEvent = Timings.RenderPos; - break; - - case HC_RENDER_EVENT: - CPU.WhichEvent = HC_WRAM_REFRESH_EVENT; - CPU.NextEvent = Timings.WRAMRefreshPos; - break; - - case HC_WRAM_REFRESH_EVENT: - CPU.WhichEvent = HC_HBLANK_START_EVENT; - CPU.NextEvent = Timings.HBlankStart; - break; + // Force IRQ to trigger immediately after WAI - + // Final Fantasy Mystic Quest crashes without this. + CPU.WaitingForInterrupt = FALSE; + Registers.PCw++; } + +#ifdef DEBUGGER + S9xTraceMessage("--- /IRQ low"); +#endif +} + +void S9xClearIRQ (uint32 source) +{ + CPU.IRQActive &= ~source; + if (!CPU.IRQActive) + CPU.Flags &= ~IRQ_FLAG; + +#ifdef DEBUGGER + S9xTraceMessage("--- /IRQ high"); +#endif } void S9xDoHEventProcessing (void) { #ifdef DEBUGGER - static char eventname[7][32] = + static char eventname[13][32] = { "", "HC_HBLANK_START_EVENT", + "HC_IRQ_1_3_EVENT ", "HC_HDMA_START_EVENT ", + "HC_IRQ_3_5_EVENT ", "HC_HCOUNTER_MAX_EVENT", + "HC_IRQ_5_7_EVENT ", "HC_HDMA_INIT_EVENT ", + "HC_IRQ_7_9_EVENT ", "HC_RENDER_EVENT ", - "HC_WRAM_REFRESH_EVENT" + "HC_IRQ_9_A_EVENT ", + "HC_WRAM_REFRESH_EVENT", + "HC_IRQ_A_1_EVENT " }; #endif @@ -366,13 +382,19 @@ void S9xDoHEventProcessing (void) eventname[CPU.WhichEvent], CPU.NextEvent, CPU.Cycles); #endif +#ifdef CPU_SHUTDOWN + CPU.WaitCounter++; +#endif + switch (CPU.WhichEvent) { case HC_HBLANK_START_EVENT: + S9xCheckMissingHTimerPosition(Timings.HBlankStart); S9xReschedule(); break; case HC_HDMA_START_EVENT: + S9xCheckMissingHTimerPosition(Timings.HDMAStart); S9xReschedule(); if (PPU.HDMA && CPU.V_Counter <= PPU.ScreenHeight) @@ -395,7 +417,6 @@ void S9xDoHEventProcessing (void) S9xAPUEndScanline(); CPU.Cycles -= Timings.H_Max; - CPU.PrevCycles -= Timings.H_Max; S9xAPUSetReferenceTime(CPU.Cycles); if ((Timings.NMITriggerPos != 0xffff) && (Timings.NMITriggerPos >= Timings.H_Max)) @@ -424,7 +445,7 @@ void S9xDoHEventProcessing (void) // FIXME: reading $4210 will wait 2 cycles, then perform reading, then wait 4 more cycles. Memory.FillRAM[0x4210] = Model->_5A22; - CPU.NMILine = FALSE; + CPU.Flags &= ~NMI_FLAG; Timings.NMITriggerPos = 0xffff; ICPU.Frame++; @@ -456,6 +477,8 @@ void S9xDoHEventProcessing (void) else Timings.WRAMRefreshPos = SNES_WRAM_REFRESH_HC_v1; + S9xCheckMissingHTimerPosition(0); + if (CPU.V_Counter == PPU.ScreenHeight + FIRST_VISIBLE_LINE) // VBlank starts from V=225(240). { S9xEndScreenRefresh(); @@ -490,7 +513,7 @@ void S9xDoHEventProcessing (void) { // FIXME: triggered at HC=6, checked just before the final CPU cycle, // then, when to call S9xOpcode_NMI()? - CPU.NMILine = TRUE; + CPU.Flags |= NMI_FLAG; Timings.NMITriggerPos = 6 + 6; } @@ -505,11 +528,13 @@ void S9xDoHEventProcessing (void) if (CPU.V_Counter == FIRST_VISIBLE_LINE) // V=1 S9xStartScreenRefresh(); + CPU.NextEvent = -1; S9xReschedule(); break; case HC_HDMA_INIT_EVENT: + S9xCheckMissingHTimerPosition(Timings.HDMAInit); S9xReschedule(); if (CPU.V_Counter == 0) @@ -526,6 +551,7 @@ void S9xDoHEventProcessing (void) if (CPU.V_Counter >= FIRST_VISIBLE_LINE && CPU.V_Counter <= PPU.ScreenHeight) RenderLine((uint8) (CPU.V_Counter - FIRST_VISIBLE_LINE)); + S9xCheckMissingHTimerPosition(Timings.RenderPos); S9xReschedule(); break; @@ -535,13 +561,28 @@ void S9xDoHEventProcessing (void) S9xTraceFormattedMessage("*** WRAM Refresh HC:%04d", CPU.Cycles); #endif - CPU.PrevCycles = CPU.Cycles; + S9xCheckMissingHTimerHalt(Timings.WRAMRefreshPos, SNES_WRAM_REFRESH_CYCLES); CPU.Cycles += SNES_WRAM_REFRESH_CYCLES; - S9xCheckInterrupts(); + S9xCheckMissingHTimerPosition(Timings.WRAMRefreshPos); S9xReschedule(); break; + + case HC_IRQ_1_3_EVENT: + case HC_IRQ_3_5_EVENT: + case HC_IRQ_5_7_EVENT: + case HC_IRQ_7_9_EVENT: + case HC_IRQ_9_A_EVENT: + case HC_IRQ_A_1_EVENT: + if (PPU.HTimerEnabled && (!PPU.VTimerEnabled || (CPU.V_Counter == PPU.VTimerPosition))) + S9xSetIRQ(PPU_IRQ_SOURCE); + else + if (PPU.VTimerEnabled && (CPU.V_Counter == PPU.VTimerPosition)) + S9xSetIRQ(PPU_IRQ_SOURCE); + + S9xReschedule(); + break; } #ifdef DEBUGGER @@ -550,3 +591,4 @@ void S9xDoHEventProcessing (void) eventname[CPU.WhichEvent], CPU.NextEvent, CPU.Cycles); #endif } + diff --git a/source/snes9x/cpuexec.h b/source/snes9x/cpuexec.h index ccfadd5..b11cff6 100644 --- a/source/snes9x/cpuexec.h +++ b/source/snes9x/cpuexec.h @@ -179,9 +179,6 @@ #define _CPUEXEC_H_ #include "ppu.h" -#ifdef DEBUGGER -#include "debug.h" -#endif struct SOpcodes { @@ -196,6 +193,7 @@ struct SICPU uint8 _Zero; uint8 _Negative; uint8 _Overflow; + bool8 CPUExecuting; uint32 ShiftedPB; uint32 ShiftedDB; uint32 Frame; @@ -219,6 +217,8 @@ void S9xMainLoop (void); void S9xReset (void); void S9xSoftReset (void); void S9xDoHEventProcessing (void); +void S9xClearIRQ (uint32); +void S9xSetIRQ (uint32); static inline void S9xUnpackStatus (void) { @@ -270,43 +270,84 @@ static inline void S9xFixCycles (void) } } -static inline void S9xCheckInterrupts (void) +static inline void S9xReschedule (void) { - bool8 thisIRQ = PPU.HTimerEnabled || PPU.VTimerEnabled; + uint8 next = 0; + int32 hpos = 0; - if (CPU.IRQLine && thisIRQ) - CPU.IRQTransition = TRUE; - - if (PPU.HTimerEnabled) + switch (CPU.WhichEvent) { - int32 htimepos = PPU.HTimerPosition; - if (CPU.Cycles >= Timings.H_Max) - htimepos += Timings.H_Max; + case HC_HBLANK_START_EVENT: + case HC_IRQ_1_3_EVENT: + next = HC_HDMA_START_EVENT; + hpos = Timings.HDMAStart; + break; - if (CPU.PrevCycles >= htimepos || CPU.Cycles < htimepos) - thisIRQ = FALSE; + case HC_HDMA_START_EVENT: + case HC_IRQ_3_5_EVENT: + next = HC_HCOUNTER_MAX_EVENT; + hpos = Timings.H_Max; + break; + + case HC_HCOUNTER_MAX_EVENT: + case HC_IRQ_5_7_EVENT: + next = HC_HDMA_INIT_EVENT; + hpos = Timings.HDMAInit; + break; + + case HC_HDMA_INIT_EVENT: + case HC_IRQ_7_9_EVENT: + next = HC_RENDER_EVENT; + hpos = Timings.RenderPos; + break; + + case HC_RENDER_EVENT: + case HC_IRQ_9_A_EVENT: + next = HC_WRAM_REFRESH_EVENT; + hpos = Timings.WRAMRefreshPos; + break; + + case HC_WRAM_REFRESH_EVENT: + case HC_IRQ_A_1_EVENT: + next = HC_HBLANK_START_EVENT; + hpos = Timings.HBlankStart; + break; } - if (PPU.VTimerEnabled) + if (((int32) PPU.HTimerPosition > CPU.NextEvent) && ((int32) PPU.HTimerPosition < hpos)) { - int32 vcounter = CPU.V_Counter; - if (CPU.Cycles >= Timings.H_Max) - vcounter++; + hpos = (int32) PPU.HTimerPosition; - if (vcounter != PPU.VTimerPosition) - thisIRQ = FALSE; + switch (next) + { + case HC_HDMA_START_EVENT: + next = HC_IRQ_1_3_EVENT; + break; + + case HC_HCOUNTER_MAX_EVENT: + next = HC_IRQ_3_5_EVENT; + break; + + case HC_HDMA_INIT_EVENT: + next = HC_IRQ_5_7_EVENT; + break; + + case HC_RENDER_EVENT: + next = HC_IRQ_7_9_EVENT; + break; + + case HC_WRAM_REFRESH_EVENT: + next = HC_IRQ_9_A_EVENT; + break; + + case HC_HBLANK_START_EVENT: + next = HC_IRQ_A_1_EVENT; + break; + } } - if (!CPU.IRQLastState && thisIRQ) - { -#ifdef DEBUGGER - S9xTraceFormattedMessage("--- /IRQ High->Low prev HC:%04d curr HC:%04d HTimer:%d Pos:%04d VTimer:%d Pos:%03d", - CPU.PrevCycles, CPU.Cycles, PPU.HTimerEnabled, PPU.HTimerPosition, PPU.VTimerEnabled, PPU.VTimerPosition); -#endif - CPU.IRQLine = TRUE; - } - - CPU.IRQLastState = thisIRQ; + CPU.NextEvent = hpos; + CPU.WhichEvent = next; } #endif diff --git a/source/snes9x/cpumacro.h b/source/snes9x/cpumacro.h index 73c4340..9b1fc4c 100644 --- a/source/snes9x/cpumacro.h +++ b/source/snes9x/cpumacro.h @@ -280,6 +280,7 @@ static void Op##OP (void) \ S9xSetPCBase(ICPU.ShiftedPB + newPC.W); \ else \ Registers.PCw = newPC.W; \ + CPUShutdown(); \ } \ } @@ -514,6 +515,9 @@ static inline void CPY (uint8 val) static inline void DEC16 (uint32 OpAddress, s9xwrap_t w) { +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif uint16 Work16 = S9xGetWord(OpAddress, w) - 1; AddCycles(ONE_CYCLE); S9xSetWord(Work16, OpAddress, w, WRITE_10); @@ -523,6 +527,9 @@ static inline void DEC16 (uint32 OpAddress, s9xwrap_t w) static inline void DEC8 (uint32 OpAddress) { +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif uint8 Work8 = S9xGetByte(OpAddress) - 1; AddCycles(ONE_CYCLE); S9xSetByte(Work8, OpAddress); @@ -544,6 +551,9 @@ static inline void EOR (uint8 val) static inline void INC16 (uint32 OpAddress, s9xwrap_t w) { +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif uint16 Work16 = S9xGetWord(OpAddress, w) + 1; AddCycles(ONE_CYCLE); S9xSetWord(Work16, OpAddress, w, WRITE_10); @@ -553,6 +563,9 @@ static inline void INC16 (uint32 OpAddress, s9xwrap_t w) static inline void INC8 (uint32 OpAddress) { +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif uint8 Work8 = S9xGetByte(OpAddress) + 1; AddCycles(ONE_CYCLE); S9xSetByte(Work8, OpAddress); diff --git a/source/snes9x/cpuops.cpp b/source/snes9x/cpuops.cpp index d68cd37..5d1cee2 100644 --- a/source/snes9x/cpuops.cpp +++ b/source/snes9x/cpuops.cpp @@ -188,9 +188,13 @@ #endif #ifdef SA1_OPCODES -#define AddCycles(n) { SA1.Cycles += (n); } +#define AddCycles(n) { } #else -#define AddCycles(n) { CPU.PrevCycles = CPU.Cycles; CPU.Cycles += (n); S9xCheckInterrupts(); while (CPU.Cycles >= CPU.NextEvent) S9xDoHEventProcessing(); } +#if (S9X_ACCURACY_LEVEL >= 3) +#define AddCycles(n) { CPU.Cycles += (n); while (CPU.Cycles >= CPU.NextEvent) S9xDoHEventProcessing(); } +#else +#define AddCycles(n) { CPU.Cycles += (n); } +#endif #endif #include "cpuaddr.h" @@ -655,6 +659,9 @@ rOPX (CCSlow, AbsoluteSlow, WRAP_NONE, CPY) static void Op3AM1 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.AL--; SetZN(Registers.AL); } @@ -662,6 +669,9 @@ static void Op3AM1 (void) static void Op3AM0 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.A.W--; SetZN(Registers.A.W); } @@ -669,6 +679,9 @@ static void Op3AM0 (void) static void Op3ASlow (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif if (CheckMemory()) { @@ -800,6 +813,9 @@ rOPM (53Slow, StackRelativeIndirectIndexedSlow, WRAP_NONE, EOR) static void Op1AM1 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.AL++; SetZN(Registers.AL); } @@ -807,6 +823,9 @@ static void Op1AM1 (void) static void Op1AM0 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.A.W++; SetZN(Registers.A.W); } @@ -814,6 +833,9 @@ static void Op1AM0 (void) static void Op1ASlow (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif if (CheckMemory()) { @@ -1528,6 +1550,54 @@ mOPM (0CSlow, AbsoluteSlow, WRAP_BANK, TSB) /* Branch Instructions ***************************************************** */ +#ifdef CPU_SHUTDOWN + +#ifndef SA1_OPCODES + +inline void CPUShutdown (void) +{ + if (Settings.Shutdown && Registers.PBPC == CPU.WaitAddress) + { + // Don't skip cycles with a pending NMI or IRQ - could cause delayed interrupt. + if (CPU.WaitCounter == 0 && !(CPU.Flags & (IRQ_FLAG | NMI_FLAG))) + { + CPU.WaitAddress = 0xffffffff; + if (Settings.SA1) + S9xSA1ExecuteDuringSleep(); + CPU.Cycles = CPU.NextEvent; + ICPU.CPUExecuting = FALSE; + S9xAPUExecute(); + ICPU.CPUExecuting = TRUE; + } + else + if (CPU.WaitCounter >= 2) + CPU.WaitCounter = 1; + else + CPU.WaitCounter--; + } +} + +#else + +inline void CPUShutdown (void) +{ + if (Settings.Shutdown && Registers.PBPC == CPU.WaitAddress) + { + if (CPU.WaitCounter >= 1) + SA1.Executing = FALSE; + else + CPU.WaitCounter++; + } +} + +#endif + +#else + +#define CPUShutdown() + +#endif + // BCC bOP(90E0, Relative, !CheckCarry(), 0, 0) bOP(90E1, Relative, !CheckCarry(), 0, 1) @@ -1622,7 +1692,7 @@ static void Op58 (void) { ClearIRQ(); AddCycles(ONE_CYCLE); - CHECK_FOR_IRQ(); + //CHECK_FOR_IRQ(); } // SEI @@ -1644,6 +1714,9 @@ static void OpB8 (void) static void OpCAX1 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.XL--; SetZN(Registers.XL); } @@ -1651,6 +1724,9 @@ static void OpCAX1 (void) static void OpCAX0 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.X.W--; SetZN(Registers.X.W); } @@ -1658,6 +1734,9 @@ static void OpCAX0 (void) static void OpCASlow (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif if (CheckIndex()) { @@ -1674,6 +1753,9 @@ static void OpCASlow (void) static void Op88X1 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.YL--; SetZN(Registers.YL); } @@ -1681,6 +1763,9 @@ static void Op88X1 (void) static void Op88X0 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.Y.W--; SetZN(Registers.Y.W); } @@ -1688,6 +1773,9 @@ static void Op88X0 (void) static void Op88Slow (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif if (CheckIndex()) { @@ -1706,6 +1794,9 @@ static void Op88Slow (void) static void OpE8X1 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.XL++; SetZN(Registers.XL); } @@ -1713,6 +1804,9 @@ static void OpE8X1 (void) static void OpE8X0 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.X.W++; SetZN(Registers.X.W); } @@ -1720,6 +1814,9 @@ static void OpE8X0 (void) static void OpE8Slow (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif if (CheckIndex()) { @@ -1736,6 +1833,9 @@ static void OpE8Slow (void) static void OpC8X1 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.YL++; SetZN(Registers.YL); } @@ -1743,6 +1843,9 @@ static void OpC8X1 (void) static void OpC8X0 (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif Registers.Y.W++; SetZN(Registers.Y.W); } @@ -1750,6 +1853,9 @@ static void OpC8X0 (void) static void OpC8Slow (void) { AddCycles(ONE_CYCLE); +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif if (CheckIndex()) { @@ -2265,7 +2371,7 @@ static void Op28E1 (void) SetFlags(MemoryFlag | IndexFlag); S9xUnpackStatus(); S9xFixCycles(); - CHECK_FOR_IRQ(); + //CHECK_FOR_IRQ(); } static void Op28E0 (void) @@ -2282,7 +2388,7 @@ static void Op28E0 (void) } S9xFixCycles(); - CHECK_FOR_IRQ(); + //CHECK_FOR_IRQ(); } static void Op28Slow (void) @@ -2310,7 +2416,7 @@ static void Op28Slow (void) } S9xFixCycles(); - CHECK_FOR_IRQ(); + //CHECK_FOR_IRQ(); } // PLX @@ -2879,7 +2985,7 @@ void S9xOpcode_NMI (void) AddCycles(2 * SLOW_ONE_CYCLE); S9xSA1SetPCBase(Memory.FillRAM[0x2205] | (Memory.FillRAM[0x2206] << 8)); #else - if (Settings.SA1 && (Memory.FillRAM[0x2209] & 0x10)) + if (Settings.SA1 && (Memory.FillRAM[0x2209] & 0x20)) { OpenBus = Memory.FillRAM[0x220d]; AddCycles(2 * SLOW_ONE_CYCLE); @@ -2963,11 +3069,17 @@ static void Op5CSlow (void) static void Op4C (void) { S9xSetPCBase(ICPU.ShiftedPB + ((uint16) Absolute(JUMP))); +#if defined(CPU_SHUTDOWN) && defined(SA1_OPCODES) + CPUShutdown(); +#endif } static void Op4CSlow (void) { S9xSetPCBase(ICPU.ShiftedPB + ((uint16) AbsoluteSlow(JUMP))); +#if defined(CPU_SHUTDOWN) && defined(SA1_OPCODES) + CPUShutdown(); +#endif } static void Op6C (void) @@ -3313,7 +3425,7 @@ static void OpC2 (void) } S9xFixCycles(); - CHECK_FOR_IRQ(); + //CHECK_FOR_IRQ(); } static void OpC2Slow (void) @@ -3342,7 +3454,7 @@ static void OpC2Slow (void) } S9xFixCycles(); - CHECK_FOR_IRQ(); + //CHECK_FOR_IRQ(); } static void OpE2 (void) @@ -3450,7 +3562,7 @@ static void Op40Slow (void) } S9xFixCycles(); - CHECK_FOR_IRQ(); + //CHECK_FOR_IRQ(); } /* STP/WAI ***************************************************************** */ @@ -3458,15 +3570,44 @@ static void Op40Slow (void) // WAI static void OpCB (void) { + // Ok, let's just C-ify the ASM versions separately. #ifdef SA1_OPCODES SA1.WaitingForInterrupt = TRUE; Registers.PCw--; - AddCycles(TWO_CYCLES); -#else - CPU.WaitingForInterrupt = TRUE; - Registers.PCw--; - AddCycles(TWO_CYCLES); +#if 0 + // XXX: FIXME + if (Settings.Shutdown) + { + SA1.Cycles = SA1.NextEvent; + SA1.Executing = FALSE; + //S9xAPUExecute(); // FIXME + SA1.Executing = TRUE; + } #endif +#else // SA1_OPCODES +#if 0 + if (CPU.IRQActive) + AddCycles(TWO_CYCLES); + else +#endif + { + CPU.WaitingForInterrupt = TRUE; + Registers.PCw--; + #ifdef CPU_SHUTDOWN + if (Settings.Shutdown) + { + CPU.Cycles = CPU.NextEvent; + ICPU.CPUExecuting = FALSE; + S9xAPUExecute(); + ICPU.CPUExecuting = TRUE; + } + else + AddCycles(TWO_CYCLES); + #else + AddCycles(TWO_CYCLES); +#endif + } +#endif // SA1_OPCODES } // STP @@ -3509,7 +3650,7 @@ static void Op42 (void) S9xMessage(S9X_DEBUG, S9X_DEBUG_OUTPUT, buf); if (trace != NULL) fclose(trace); - ENSURE_TRACE_OPEN(trace,"WDMtrace.log","ab") + trace = fopen("WDMtrace.log", "ab"); } break; @@ -3883,3 +4024,4 @@ struct SOpcodes S9xOpcodesSlow[256] = { OpFASlow }, { OpFB }, { OpFCSlow }, { OpFDSlow }, { OpFESlow }, { OpFFSlow } }; + diff --git a/source/snes9x/cpuops.h b/source/snes9x/cpuops.h index d9bfcc1..acb3406 100644 --- a/source/snes9x/cpuops.h +++ b/source/snes9x/cpuops.h @@ -181,9 +181,8 @@ void S9xOpcode_NMI (void); void S9xOpcode_IRQ (void); -#ifndef SA1_OPCODES -#define CHECK_FOR_IRQ() {} // if (CPU.IRQLine) S9xOpcode_IRQ(); } -#else -#define CHECK_FOR_IRQ() {} -#endif +#define CHECK_FOR_IRQ() \ +if (CPU.IRQActive && !CheckFlag(IRQ) && !Settings.DisableIRQ) \ + S9xOpcode_IRQ() + #endif diff --git a/source/snes9x/dma.cpp b/source/snes9x/dma.cpp index bf17109..bd2c6f2 100644 --- a/source/snes9x/dma.cpp +++ b/source/snes9x/dma.cpp @@ -185,7 +185,7 @@ #include "missing.h" #endif -#define ADD_CYCLES(n) { CPU.PrevCycles = CPU.Cycles; CPU.Cycles += (n); S9xCheckInterrupts(); } +#define ADD_CYCLES(n) CPU.Cycles += (n) extern uint8 *HDMAMemPointers[8]; extern int HDMA_ModeByteCounts[8]; @@ -741,6 +741,9 @@ bool8 S9xDoDMA (uint8 Channel) break; case 0x18: // VMDATAL + #ifndef CORRECT_VRAM_READS + IPPU.FirstVRAMRead = TRUE; + #endif if (!PPU.VMA.FullGraphicCount) { do @@ -763,6 +766,9 @@ bool8 S9xDoDMA (uint8 Channel) break; case 0x19: // VMDATAH + #ifndef CORRECT_VRAM_READS + IPPU.FirstVRAMRead = TRUE; + #endif if (!PPU.VMA.FullGraphicCount) { do @@ -831,6 +837,9 @@ bool8 S9xDoDMA (uint8 Channel) if (d->BAddress == 0x18) { // VMDATAL + #ifndef CORRECT_VRAM_READS + IPPU.FirstVRAMRead = TRUE; + #endif if (!PPU.VMA.FullGraphicCount) { switch (b) @@ -1273,7 +1282,7 @@ bool8 S9xDoDMA (uint8 Channel) } } - if (CPU.NMILine && (Timings.NMITriggerPos != 0xffff)) + if ((CPU.Flags & NMI_FLAG) && (Timings.NMITriggerPos != 0xffff)) { Timings.NMITriggerPos = CPU.Cycles + Timings.NMIDMADelay; if (Timings.NMITriggerPos >= Timings.H_Max) @@ -1364,7 +1373,10 @@ static inline bool8 HDMAReadLineCount (int d) void S9xStartHDMA (void) { - PPU.HDMA = Memory.FillRAM[0x420c]; + if (Settings.DisableHDMA) + PPU.HDMA = 0; + else + PPU.HDMA = Memory.FillRAM[0x420c]; #ifdef DEBUGGER missing.hdma_this_frame = PPU.HDMA; diff --git a/source/snes9x/fxemu.cpp b/source/snes9x/fxemu.cpp index ce1af63..b3237ae 100644 --- a/source/snes9x/fxemu.cpp +++ b/source/snes9x/fxemu.cpp @@ -202,7 +202,6 @@ void S9xResetSuperFX (void) SuperFX.speedPerLine = (uint32) (0.417 * 10.5e6 * ((1.0 / (float) Memory.ROMFramesPerSecond) / ((float) (Timings.V_Max)))); SuperFX.oneLineDone = FALSE; SuperFX.vFlags = 0; - CPU.IRQExternal = FALSE; FxReset(&SuperFX); } @@ -300,10 +299,13 @@ uint8 S9xGetSuperFX (uint16 address) uint8 byte; byte = Memory.FillRAM[address]; - +#ifdef CPU_SHUTDOWN + if (address == 0x3030) + CPU.WaitAddress = CPU.PBPCAtOpcodeStart; +#endif if (address == 0x3031) { - CPU.IRQExternal = FALSE; + S9xClearIRQ(GSU_IRQ_SOURCE); Memory.FillRAM[0x3031] = byte & 0x7f; } @@ -318,7 +320,7 @@ void S9xSuperFXExec (void) uint16 GSUStatus = Memory.FillRAM[0x3000 + GSU_SFR] | (Memory.FillRAM[0x3000 + GSU_SFR + 1] << 8); if ((GSUStatus & (FLG_G | FLG_IRQ)) == FLG_IRQ) - CPU.IRQExternal = TRUE; + S9xSetIRQ(GSU_IRQ_SOURCE); } } diff --git a/source/snes9x/getset.h b/source/snes9x/getset.h index b7dda2b..bf5557b 100644 --- a/source/snes9x/getset.h +++ b/source/snes9x/getset.h @@ -187,12 +187,12 @@ #include "seta.h" #include "bsx.h" +#if (S9X_ACCURACY_LEVEL >= 2) + #define addCyclesInMemoryAccess \ if (!CPU.InDMAorHDMA) \ { \ - CPU.PrevCycles = CPU.Cycles; \ CPU.Cycles += speed; \ - S9xCheckInterrupts(); \ while (CPU.Cycles >= CPU.NextEvent) \ S9xDoHEventProcessing(); \ } @@ -200,13 +200,23 @@ #define addCyclesInMemoryAccess_x2 \ if (!CPU.InDMAorHDMA) \ { \ - CPU.PrevCycles = CPU.Cycles; \ CPU.Cycles += speed << 1; \ - S9xCheckInterrupts(); \ while (CPU.Cycles >= CPU.NextEvent) \ S9xDoHEventProcessing(); \ } +#else + +#define addCyclesInMemoryAccess \ + if (!CPU.InDMAorHDMA) \ + CPU.Cycles += speed; + +#define addCyclesInMemoryAccess_x2 \ + if (!CPU.InDMAorHDMA) \ + CPU.Cycles += speed << 1; + +#endif + extern uint8 OpenBus; static inline int32 memory_speed (uint32 address) @@ -237,6 +247,10 @@ inline uint8 S9xGetByte (uint32 Address) if (GetAddress >= (uint8 *) CMemory::MAP_LAST) { + #ifdef CPU_SHUTDOWN + if (Memory.BlockIsRAM[block]) + CPU.WaitAddress = CPU.PBPCAtOpcodeStart; + #endif byte = *(GetAddress + (Address & 0xffff)); addCyclesInMemoryAccess; return (byte); @@ -365,6 +379,10 @@ inline uint16 S9xGetWord (uint32 Address, enum s9xwrap_t w = WRAP_NONE) if (GetAddress >= (uint8 *) CMemory::MAP_LAST) { + #ifdef CPU_SHUTDOWN + if (Memory.BlockIsRAM[block]) + CPU.WaitAddress = CPU.PBPCAtOpcodeStart; + #endif word = READ_WORD(GetAddress + (Address & 0xffff)); addCyclesInMemoryAccess_x2; return (word); @@ -492,14 +510,33 @@ inline uint16 S9xGetWord (uint32 Address, enum s9xwrap_t w = WRAP_NONE) inline void S9xSetByte (uint8 Byte, uint32 Address) { +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif + int block = (Address & 0xffffff) >> MEMMAP_SHIFT; uint8 *SetAddress = Memory.WriteMap[block]; int32 speed = memory_speed(Address); if (SetAddress >= (uint8 *) CMemory::MAP_LAST) { + #ifdef CPU_SHUTDOWN + SetAddress += (Address & 0xffff); + *SetAddress = Byte; + addCyclesInMemoryAccess; + + if (Settings.SA1) + { + if (SetAddress == SA1.WaitByteAddress1 || SetAddress == SA1.WaitByteAddress2) + { + SA1.Executing = SA1.S9xOpcodes != NULL; + SA1.WaitCounter = 0; + } + } + #else *(SetAddress + (Address & 0xffff)) = Byte; addCyclesInMemoryAccess; + #endif return; } @@ -556,6 +593,7 @@ inline void S9xSetByte (uint8 Byte, uint32 Address) case CMemory::MAP_SA1RAM: *(Memory.SRAM + (Address & 0xffff)) = Byte; + SA1.Executing = !SA1.Waiting; addCyclesInMemoryAccess; return; @@ -632,14 +670,33 @@ inline void S9xSetWord (uint16 Word, uint32 Address, enum s9xwrap_t w = WRAP_NON return; } +#ifdef CPU_SHUTDOWN + CPU.WaitAddress = 0xffffffff; +#endif + int block = (Address & 0xffffff) >> MEMMAP_SHIFT; uint8 *SetAddress = Memory.WriteMap[block]; int32 speed = memory_speed(Address); if (SetAddress >= (uint8 *) CMemory::MAP_LAST) { + #ifdef CPU_SHUTDOWN + SetAddress += (Address & 0xffff); + WRITE_WORD(SetAddress, Word); + addCyclesInMemoryAccess_x2; + + if (Settings.SA1) + { + if (SetAddress == SA1.WaitByteAddress1 || SetAddress == SA1.WaitByteAddress2) + { + SA1.Executing = SA1.S9xOpcodes != NULL; + SA1.WaitCounter = 0; + } + } + #else WRITE_WORD(SetAddress + (Address & 0xffff), Word); addCyclesInMemoryAccess_x2; + #endif return; } @@ -749,6 +806,7 @@ inline void S9xSetWord (uint16 Word, uint32 Address, enum s9xwrap_t w = WRAP_NON case CMemory::MAP_SA1RAM: WRITE_WORD(Memory.SRAM + (Address & 0xffff), Word); + SA1.Executing = !SA1.Waiting; addCyclesInMemoryAccess_x2; return; diff --git a/source/snes9x/memmap.cpp b/source/snes9x/memmap.cpp index 74af9e0..d7c2a9e 100644 --- a/source/snes9x/memmap.cpp +++ b/source/snes9x/memmap.cpp @@ -2270,9 +2270,11 @@ void CMemory::InitROM (void) Settings.SETA = 0; Settings.SRTC = FALSE; Settings.BS = FALSE; - + SuperFX.nRomBanks = CalculatedSize >> 15; + SA1.Executing = FALSE; + //// Parse ROM header and read ROM informatoin CompanyId = -1; @@ -3496,6 +3498,7 @@ bool8 CMemory::match_id (const char *str) void CMemory::ApplyROMFixes (void) { + Settings.Shutdown = Settings.ShutdownMaster; Settings.BlockInvalidVRAMAccess = Settings.BlockInvalidVRAMAccessMaster; //// Warnings @@ -3573,7 +3576,6 @@ void CMemory::ApplyROMFixes (void) Timings.HDMAStart = SNES_HDMA_START_HC + Settings.HDMATimingHack - 100; Timings.HBlankStart = SNES_HBLANK_START_HC + Timings.HDMAStart - SNES_HDMA_START_HC; - Timings.IRQTriggerCycles = 10; if (!Settings.DisableGameSpecificHacks) { @@ -3588,6 +3590,14 @@ void CMemory::ApplyROMFixes (void) if (!Settings.DisableGameSpecificHacks) { + // Opcode-based emulators cannot escape from "reading $4211/BPL" infinite loop... + // The true IRQ can be triggered inside an opcode. + if (match_na("TRAVERSE")) // Traverse - Starlight & Prairie + { + Timings.IRQPendCount = 1; + printf("IRQ count hack: %d\n", Timings.IRQPendCount); + } + // An infinite loop reads $4212 and waits V-blank end, whereas VIRQ is set V=0. // If Snes9x succeeds to escape from the loop before jumping into the IRQ handler, the game goes further. // If Snes9x jumps into the IRQ handler before escaping from the loop, @@ -3597,6 +3607,12 @@ void CMemory::ApplyROMFixes (void) Timings.IRQPendCount = 2; printf("IRQ count hack: %d\n", Timings.IRQPendCount); } + + if (match_na("BATTLE BLAZE")) + { + Timings.IRQPendCount = 1; + printf("IRQ count hack: %d\n", Timings.IRQPendCount); + } } if (!Settings.DisableGameSpecificHacks) @@ -3609,6 +3625,242 @@ void CMemory::ApplyROMFixes (void) } } + //// CPU speed-ups (CPU_Shutdown()) + + // Force disabling a speed-up hack + // Games which spool sound samples between the SNES and sound CPU using + // H-DMA as the sample is playing. + if (match_na("EARTHWORM JIM 2") || // Earth Worm Jim 2 + match_na("PRIMAL RAGE") || // Primal Rage + match_na("CLAY FIGHTER") || // Clay Fighter + match_na("ClayFighter 2") || // Clay Fighter 2 + match_na("WeaponLord") || // Weapon Lord + match_nn("WAR 2410") || // War 2410 + match_id("ARF") || // Star Ocean + match_id("A4WJ") || // Mini Yonku Shining Scorpion - Let's & Go!! + match_nn("NHL") || + match_nc("MADDEN")) + { + if (Settings.Shutdown) + printf("Disabled CPU shutdown hack.\n"); + Settings.Shutdown = FALSE; + } + + // SA-1 + SA1.WaitAddress = 0xffffffff; + SA1.WaitByteAddress1 = NULL; + SA1.WaitByteAddress2 = NULL; + + if (Settings.SA1) + { + // Itoi Shigesato no Bass Tsuri No.1 (J) + if (match_id("ZBPJ")) + { + SA1.WaitAddress = 0x0093f1; + SA1.WaitByteAddress1 = FillRAM + 0x304a; + } + + // Daisenryaku Expert WWII (J) + if (match_id("AEVJ")) + { + SA1.WaitAddress = 0x0ed18d; + SA1.WaitByteAddress1 = FillRAM + 0x3000; + } + + // Derby Jockey 2 (J) + if (match_id("A2DJ")) + { + SA1.WaitAddress = 0x008b62; + } + + // Dragon Ball Z - Hyper Dimension (J) + if (match_id("AZIJ")) + { + SA1.WaitAddress = 0x008083; + SA1.WaitByteAddress1 = FillRAM + 0x3020; + } + + // SD Gundam G NEXT (J) + if (match_id("ZX3J")) + { + SA1.WaitAddress = 0x0087f2; + SA1.WaitByteAddress1 = FillRAM + 0x30c4; + } + + // Shougi no Hanamichi (J) + if (match_id("AARJ")) + { + SA1.WaitAddress = 0xc1f85a; + SA1.WaitByteAddress1 = SRAM + 0x0c64; + SA1.WaitByteAddress2 = SRAM + 0x0c66; + } + + // Asahi Shinbun Rensai Katou Hifumi Kudan Shougi Shingiryu (J) + if (match_id("A23J")) + { + SA1.WaitAddress = 0xc25037; + SA1.WaitByteAddress1 = SRAM + 0x0c06; + SA1.WaitByteAddress2 = SRAM + 0x0c08; + } + + // Taikyoku Igo - Idaten (J) + if (match_id("AIIJ")) + { + SA1.WaitAddress = 0xc100be; + SA1.WaitByteAddress1 = SRAM + 0x1002; + SA1.WaitByteAddress2 = SRAM + 0x1004; + } + + // Takemiya Masaki Kudan no Igo Taishou (J) + if (match_id("AITJ")) + { + SA1.WaitAddress = 0x0080b7; + } + + // J. League '96 Dream Stadium (J) + if (match_id("AJ6J")) + { + SA1.WaitAddress = 0xc0f74a; + } + + // Jumpin' Derby (J) + if (match_id("AJUJ")) + { + SA1.WaitAddress = 0x00d926; + } + + // Kakinoki Shougi (J) + if (match_id("AKAJ")) + { + SA1.WaitAddress = 0x00f070; + } + + // Hoshi no Kirby 3 (J), Kirby's Dream Land 3 (U) + if (match_id("AFJJ") || match_id("AFJE")) + { + SA1.WaitAddress = 0x0082d4; + SA1.WaitByteAddress1 = SRAM + 0x72a4; + } + + // Hoshi no Kirby - Super Deluxe (J) + if (match_id("AKFJ")) + { + SA1.WaitAddress = 0x008c93; + SA1.WaitByteAddress1 = FillRAM + 0x300a; + SA1.WaitByteAddress2 = FillRAM + 0x300e; + } + + // Kirby Super Star (U) + if (match_id("AKFE")) + { + SA1.WaitAddress = 0x008cb8; + SA1.WaitByteAddress1 = FillRAM + 0x300a; + SA1.WaitByteAddress2 = FillRAM + 0x300e; + } + + // Super Mario RPG (J), (U) + if (match_id("ARWJ") || match_id("ARWE")) + { + SA1.WaitAddress = 0xc0816f; + SA1.WaitByteAddress1 = FillRAM + 0x3000; + } + + // Marvelous (J) + if (match_id("AVRJ")) + { + SA1.WaitAddress = 0x0085f2; + SA1.WaitByteAddress1 = FillRAM + 0x3024; + } + + // Harukanaru Augusta 3 - Masters New (J) + if (match_id("AO3J")) + { + SA1.WaitAddress = 0x00dddb; + SA1.WaitByteAddress1 = FillRAM + 0x37b4; + } + + // Jikkyou Oshaberi Parodius (J) + if (match_id("AJOJ")) + { + SA1.WaitAddress = 0x8084e5; + } + + // Super Bomberman - Panic Bomber W (J) + if (match_id("APBJ")) + { + SA1.WaitAddress = 0x00857a; + } + + // Pebble Beach no Hatou New - Tournament Edition (J) + if (match_id("AONJ")) + { + SA1.WaitAddress = 0x00df33; + SA1.WaitByteAddress1 = FillRAM + 0x37b4; + } + + // PGA European Tour (U) + if (match_id("AEPE")) + { + SA1.WaitAddress = 0x003700; + SA1.WaitByteAddress1 = FillRAM + 0x3102; + } + + // PGA Tour 96 (U) + if (match_id("A3GE")) + { + SA1.WaitAddress = 0x003700; + SA1.WaitByteAddress1 = FillRAM + 0x3102; + } + + // Power Rangers Zeo - Battle Racers (U) + if (match_id("A4RE")) + { + SA1.WaitAddress = 0x009899; + SA1.WaitByteAddress1 = FillRAM + 0x3000; + } + + // SD F-1 Grand Prix (J) + if (match_id("AGFJ")) + { + SA1.WaitAddress = 0x0181bc; + } + + // Saikousoku Shikou Shougi Mahjong (J) + if (match_id("ASYJ")) + { + SA1.WaitAddress = 0x00f2cc; + SA1.WaitByteAddress1 = SRAM + 0x7ffe; + SA1.WaitByteAddress2 = SRAM + 0x7ffc; + } + + // Shougi Saikyou II (J) + if (match_id("AX2J")) + { + SA1.WaitAddress = 0x00d675; + } + + // Mini Yonku Shining Scorpion - Let's & Go!! (J) + if (match_id("A4WJ")) + { + SA1.WaitAddress = 0xc048be; + } + + // Shin Shougi Club (J) + if (match_id("AHJJ")) + { + SA1.WaitAddress = 0xc1002a; + SA1.WaitByteAddress1 = SRAM + 0x0806; + SA1.WaitByteAddress2 = SRAM + 0x0808; + } + + // rest games: + // Habu Meijin no Omoshiro Shougi (J) + // Hayashi Kaihou Kudan no Igo Taidou (J) + // Shougi Saikyou (J) + // Super Robot Wars Gaiden (J) + // Super Shougi 3 - Kitaihei (J) + } + //// SRAM initial value if (!Settings.DisableGameSpecificHacks) diff --git a/source/snes9x/movie.cpp b/source/snes9x/movie.cpp index f4b018b..f8044dd 100644 --- a/source/snes9x/movie.cpp +++ b/source/snes9x/movie.cpp @@ -745,8 +745,7 @@ int S9xMovieUnfreeze (uint8 *buf, uint32 size) } else { - uint32 space_processed = (Movie.BytesPerSample * (current_sample + 1)); - if (current_frame > Movie.MaxFrame || current_sample > Movie.MaxSample || memcmp(Movie.InputBuffer, ptr, space_processed)) + if (current_frame > Movie.MaxFrame || current_sample > Movie.MaxSample || memcmp(Movie.InputBuffer, ptr, space_needed)) return (SNAPSHOT_INCONSISTENT); change_state(MOVIE_STATE_PLAY); @@ -787,8 +786,8 @@ int S9xMovieOpen (const char *filename, bool8 read_only) read_movie_extrarominfo(fd, &Movie); - fflush(fd); - fn = fileno(fd); + fn = dup(fileno(fd)); + fclose(fd); store_previous_settings(); restore_movie_settings(); @@ -854,6 +853,7 @@ int S9xMovieCreate (const char *filename, uint8 controllers_mask, uint8 opts, co { FILE *fd; STREAM stream; + int fn; if (controllers_mask == 0) return (WRONG_FORMAT); @@ -902,9 +902,10 @@ int S9xMovieCreate (const char *filename, uint8 controllers_mask, uint8 opts, co write_movie_extrarominfo(fd, &Movie); + fn = dup(fileno(fd)); fclose(fd); - stream = OPEN_STREAM(filename, "ab"); + stream = REOPEN_STREAM(fn, "ab"); if (!stream) return (FILE_NOT_FOUND); @@ -1008,10 +1009,9 @@ int S9xMovieGetInfo (const char *filename, struct MovieInfo *info) strncpy(info->ROMName, local_movie.ROMName, 23); fclose(fd); - if ((fd = fopen(filename, "r+")) == NULL) + + if (access(filename, W_OK)) info->ReadOnly = true; - else - fclose(fd); return (SUCCESS); } diff --git a/source/snes9x/ppu.cpp b/source/snes9x/ppu.cpp index 95ad4b4..898c6f4 100644 --- a/source/snes9x/ppu.cpp +++ b/source/snes9x/ppu.cpp @@ -204,6 +204,9 @@ static inline void S9xLatchCounters (bool force) #ifdef DEBUGGER missing.h_v_latch = 1; #endif + #if 0 // #ifdef CPU_SHUTDOWN + CPU.WaitAddress = CPU.PCAtOpcodeStart; + #endif PPU.HVBeamCounterLatched = 1; PPU.VBeamPosLatched = (uint16) CPU.V_Counter; @@ -242,6 +245,9 @@ static inline void S9xTryGunLatch (bool force) #ifdef DEBUGGER missing.h_v_latch = 1; #endif + #if 0 // #ifdef CPU_SHUTDOWN + CPU.WaitAddress = CPU.PCAtOpcodeStart; + #endif PPU.HVBeamCounterLatched = 1; PPU.VBeamPosLatched = (uint16) PPU.GunVLatch; @@ -254,16 +260,72 @@ static inline void S9xTryGunLatch (bool force) } } +void S9xCheckMissingHTimerPosition (int32 hc) +{ + if (PPU.HTimerPosition == hc) + { + if (PPU.HTimerEnabled && (!PPU.VTimerEnabled || (CPU.V_Counter == PPU.VTimerPosition))) + S9xSetIRQ(PPU_IRQ_SOURCE); + else + if (PPU.VTimerEnabled && (CPU.V_Counter == PPU.VTimerPosition)) + S9xSetIRQ(PPU_IRQ_SOURCE); + } +} + +void S9xCheckMissingHTimerHalt (int32 hc_from, int32 range) +{ + if ((PPU.HTimerPosition >= hc_from) && (PPU.HTimerPosition < (hc_from + range))) + { + if (PPU.HTimerEnabled && (!PPU.VTimerEnabled || (CPU.V_Counter == PPU.VTimerPosition))) + CPU.IRQPending = 1; + else + if (PPU.VTimerEnabled && (CPU.V_Counter == PPU.VTimerPosition)) + CPU.IRQPending = 1; + } +} + +void S9xCheckMissingHTimerRange (int32 hc_from, int32 range) +{ + if ((PPU.HTimerPosition >= hc_from) && (PPU.HTimerPosition < (hc_from + range))) + { + if (PPU.HTimerEnabled && (!PPU.VTimerEnabled || (CPU.V_Counter == PPU.VTimerPosition))) + S9xSetIRQ(PPU_IRQ_SOURCE); + else + if (PPU.VTimerEnabled && (CPU.V_Counter == PPU.VTimerPosition)) + S9xSetIRQ(PPU_IRQ_SOURCE); + } +} + void S9xUpdateHVTimerPosition (void) { - PPU.HTimerPosition = PPU.IRQHBeamPos * ONE_DOT_CYCLE + Timings.IRQTriggerCycles; - if (Timings.H_Max == Timings.H_Max_Master) // 1364 + if (PPU.HTimerEnabled) { - if (PPU.IRQHBeamPos > 322) - PPU.HTimerPosition += (ONE_DOT_CYCLE / 2); - if (PPU.IRQHBeamPos > 326) - PPU.HTimerPosition += (ONE_DOT_CYCLE / 2); + #ifdef DEBUGGER + missing.hirq_pos = PPU.IRQHBeamPos; + #endif + if (PPU.IRQHBeamPos != 0) + { + // IRQ_read + PPU.HTimerPosition = PPU.IRQHBeamPos * ONE_DOT_CYCLE; + if (Timings.H_Max == Timings.H_Max_Master) // 1364 + { + if (PPU.IRQHBeamPos > 322) + PPU.HTimerPosition += (ONE_DOT_CYCLE / 2); + if (PPU.IRQHBeamPos > 326) + PPU.HTimerPosition += (ONE_DOT_CYCLE / 2); + } + + PPU.HTimerPosition += 14; + // /IRQ + PPU.HTimerPosition += 4; + // after CPU executing + PPU.HTimerPosition += 6; + } + else + PPU.HTimerPosition = 10 + 4 + 6; } + else + PPU.HTimerPosition = 10 + 4 + 6; PPU.VTimerPosition = PPU.IRQVBeamPos; @@ -276,9 +338,111 @@ void S9xUpdateHVTimerPosition (void) PPU.VTimerPosition = 0; } + if (PPU.HTimerPosition < CPU.Cycles) + { + switch (CPU.WhichEvent) + { + case HC_IRQ_1_3_EVENT: + CPU.WhichEvent = HC_HDMA_START_EVENT; + CPU.NextEvent = Timings.HDMAStart; + break; + + case HC_IRQ_3_5_EVENT: + CPU.WhichEvent = HC_HCOUNTER_MAX_EVENT; + CPU.NextEvent = Timings.H_Max; + break; + + case HC_IRQ_5_7_EVENT: + CPU.WhichEvent = HC_HDMA_INIT_EVENT; + CPU.NextEvent = Timings.HDMAInit; + break; + + case HC_IRQ_7_9_EVENT: + CPU.WhichEvent = HC_RENDER_EVENT; + CPU.NextEvent = Timings.RenderPos; + break; + + case HC_IRQ_9_A_EVENT: + CPU.WhichEvent = HC_WRAM_REFRESH_EVENT; + CPU.NextEvent = Timings.WRAMRefreshPos; + break; + + case HC_IRQ_A_1_EVENT: + CPU.WhichEvent = HC_HBLANK_START_EVENT; + CPU.NextEvent = Timings.HBlankStart; + break; + } + } + else + if ((PPU.HTimerPosition < CPU.NextEvent) || (!(CPU.WhichEvent & 1) && (PPU.HTimerPosition == CPU.NextEvent))) + { + CPU.NextEvent = PPU.HTimerPosition; + + switch (CPU.WhichEvent) + { + case HC_HDMA_START_EVENT: + CPU.WhichEvent = HC_IRQ_1_3_EVENT; + break; + + case HC_HCOUNTER_MAX_EVENT: + CPU.WhichEvent = HC_IRQ_3_5_EVENT; + break; + + case HC_HDMA_INIT_EVENT: + CPU.WhichEvent = HC_IRQ_5_7_EVENT; + break; + + case HC_RENDER_EVENT: + CPU.WhichEvent = HC_IRQ_7_9_EVENT; + break; + + case HC_WRAM_REFRESH_EVENT: + CPU.WhichEvent = HC_IRQ_9_A_EVENT; + break; + + case HC_HBLANK_START_EVENT: + CPU.WhichEvent = HC_IRQ_A_1_EVENT; + break; + } + } + else + { + switch (CPU.WhichEvent) + { + case HC_IRQ_1_3_EVENT: + CPU.WhichEvent = HC_HDMA_START_EVENT; + CPU.NextEvent = Timings.HDMAStart; + break; + + case HC_IRQ_3_5_EVENT: + CPU.WhichEvent = HC_HCOUNTER_MAX_EVENT; + CPU.NextEvent = Timings.H_Max; + break; + + case HC_IRQ_5_7_EVENT: + CPU.WhichEvent = HC_HDMA_INIT_EVENT; + CPU.NextEvent = Timings.HDMAInit; + break; + + case HC_IRQ_7_9_EVENT: + CPU.WhichEvent = HC_RENDER_EVENT; + CPU.NextEvent = Timings.RenderPos; + break; + + case HC_IRQ_9_A_EVENT: + CPU.WhichEvent = HC_WRAM_REFRESH_EVENT; + CPU.NextEvent = Timings.WRAMRefreshPos; + break; + + case HC_IRQ_A_1_EVENT: + CPU.WhichEvent = HC_HBLANK_START_EVENT; + CPU.NextEvent = Timings.HBlankStart; + break; + } + } + #ifdef DEBUGGER - S9xTraceFormattedMessage("--- IRQ Timer set HTimer:%d Pos:%04d VTimer:%d Pos:%03d", - PPU.HTimerEnabled, PPU.HTimerPosition, PPU.VTimerEnabled, PPU.VTimerPosition); + S9xTraceFormattedMessage("--- IRQ settings: H:%d V:%d (%04d, %03d)", PPU.HTimerEnabled, PPU.VTimerEnabled, PPU.HTimerPosition, PPU.VTimerPosition); #endif } @@ -620,7 +784,7 @@ void S9xSetPPU (uint8 Byte, uint16 Address) case 0x2116: // VMADDL PPU.VMA.Address &= 0xff00; PPU.VMA.Address |= Byte; - + #ifdef CORRECT_VRAM_READS if (PPU.VMA.FullGraphicCount) { uint32 addr = PPU.VMA.Address; @@ -630,13 +794,15 @@ void S9xSetPPU (uint8 Byte, uint16 Address) } else IPPU.VRAMReadBuffer = READ_WORD(Memory.VRAM + ((PPU.VMA.Address << 1) & 0xffff)); - + #else + IPPU.FirstVRAMRead = TRUE; + #endif break; case 0x2117: // VMADDH PPU.VMA.Address &= 0x00ff; PPU.VMA.Address |= Byte << 8; - + #ifdef CORRECT_VRAM_READS if (PPU.VMA.FullGraphicCount) { uint32 addr = PPU.VMA.Address; @@ -646,14 +812,22 @@ void S9xSetPPU (uint8 Byte, uint16 Address) } else IPPU.VRAMReadBuffer = READ_WORD(Memory.VRAM + ((PPU.VMA.Address << 1) & 0xffff)); - + #else + IPPU.FirstVRAMRead = TRUE; + #endif break; case 0x2118: // VMDATAL + #ifndef CORRECT_VRAM_READS + IPPU.FirstVRAMRead = TRUE; + #endif REGISTER_2118(Byte); break; case 0x2119: // VMDATAH + #ifndef CORRECT_VRAM_READS + IPPU.FirstVRAMRead = TRUE; + #endif REGISTER_2119(Byte); break; @@ -1204,6 +1378,7 @@ uint8 S9xGetPPU (uint16 Address) return (PPU.OpenBus1 = byte); case 0x2139: // VMDATALREAD + #ifdef CORRECT_VRAM_READS byte = IPPU.VRAMReadBuffer & 0xff; if (!PPU.VMA.High) { @@ -1219,13 +1394,33 @@ uint8 S9xGetPPU (uint16 Address) PPU.VMA.Address += PPU.VMA.Increment; } + #else + if (IPPU.FirstVRAMRead) + byte = Memory.VRAM[(PPU.VMA.Address << 1) & 0xffff]; + else + if (PPU.VMA.FullGraphicCount) + { + uint32 addr = PPU.VMA.Address - 1; + uint32 rem = addr & PPU.VMA.Mask1; + uint32 address = (addr & ~PPU.VMA.Mask1) + (rem >> PPU.VMA.Shift) + ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3); + byte = Memory.VRAM[((address << 1) - 2) & 0xffff]; + } + else + byte = Memory.VRAM[((PPU.VMA.Address << 1) - 2) & 0xffff]; + if (!PPU.VMA.High) + { + PPU.VMA.Address += PPU.VMA.Increment; + IPPU.FirstVRAMRead = FALSE; + } + #endif #ifdef DEBUGGER missing.vram_read = 1; #endif return (PPU.OpenBus1 = byte); case 0x213a: // VMDATAHREAD + #ifdef CORRECT_VRAM_READS byte = (IPPU.VRAMReadBuffer >> 8) & 0xff; if (PPU.VMA.High) { @@ -1241,6 +1436,26 @@ uint8 S9xGetPPU (uint16 Address) PPU.VMA.Address += PPU.VMA.Increment; } + #else + if (IPPU.FirstVRAMRead) + byte = Memory.VRAM[((PPU.VMA.Address << 1) + 1) & 0xffff]; + else + if (PPU.VMA.FullGraphicCount) + { + uint32 addr = PPU.VMA.Address - 1; + uint32 rem = addr & PPU.VMA.Mask1; + uint32 address = (addr & ~PPU.VMA.Mask1) + (rem >> PPU.VMA.Shift) + ((rem & (PPU.VMA.FullGraphicCount - 1)) << 3); + byte = Memory.VRAM[((address << 1) - 1) & 0xffff]; + } + else + byte = Memory.VRAM[((PPU.VMA.Address << 1) - 1) & 0xffff]; + + if (PPU.VMA.High) + { + PPU.VMA.Address += PPU.VMA.Increment; + IPPU.FirstVRAMRead = FALSE; + } + #endif #ifdef DEBUGGER missing.vram_read = 1; #endif @@ -1478,14 +1693,14 @@ void S9xSetCPU (uint8 Byte, uint16 Address) else PPU.HTimerEnabled = FALSE; - if (CPU.IRQLine && !PPU.HTimerEnabled && PPU.VTimerEnabled) - CPU.IRQTransition = TRUE; + S9xUpdateHVTimerPosition(); - if (!PPU.HTimerEnabled && !PPU.VTimerEnabled) - { - CPU.IRQLine = FALSE; - CPU.IRQTransition = FALSE; - } + // The case that IRQ will trigger in an instruction such as STA $4200. + // FIXME: not true but good enough for Snes9x, I think. + S9xCheckMissingHTimerRange(CPU.PrevCycles, CPU.Cycles - CPU.PrevCycles); + + if (!(Byte & 0x30)) + S9xClearIRQ(PPU_IRQ_SOURCE); // NMI can trigger immediately during VBlank as long as NMI_read ($4210) wasn't cleard. if ((Byte & 0x80) && !(Memory.FillRAM[0x4200] & 0x80) && @@ -1493,7 +1708,7 @@ void S9xSetCPU (uint8 Byte, uint16 Address) { // FIXME: triggered at HC+=6, checked just before the final CPU cycle, // then, when to call S9xOpcode_NMI()? - CPU.NMILine = TRUE; + CPU.Flags |= NMI_FLAG; Timings.NMITriggerPos = CPU.Cycles + 6 + 6; } @@ -1607,6 +1822,8 @@ void S9xSetCPU (uint8 Byte, uint16 Address) case 0x420c: // HDMAEN if (CPU.InDMAorHDMA) return; + if (Settings.DisableHDMA) + Byte = 0; Memory.FillRAM[0x420c] = Byte; // Yoshi's Island, Genjyu Ryodan, Mortal Kombat, Tales of Phantasia PPU.HDMA = Byte & ~PPU.HDMAEnded; @@ -1633,7 +1850,17 @@ void S9xSetCPU (uint8 Byte, uint16 Address) break; case 0x4210: // RDNMI + #if 0 + Memory.FillRAM[0x4210] = Model->_5A22; + #endif + return; + case 0x4211: // TIMEUP + #if 0 + S9xClearIRQ(PPU_IRQ_SOURCE); + #endif + return; + case 0x4212: // HVBJOY case 0x4213: // RDIO case 0x4214: // RDDIVL @@ -1749,17 +1976,22 @@ uint8 S9xGetCPU (uint16 Address) switch (Address) { case 0x4210: // RDNMI + #ifdef CPU_SHUTDOWN + CPU.WaitAddress = CPU.PBPCAtOpcodeStart; + #endif byte = Memory.FillRAM[0x4210]; Memory.FillRAM[0x4210] = Model->_5A22; return ((byte & 0x80) | (OpenBus & 0x70) | Model->_5A22); case 0x4211: // TIMEUP - byte = CPU.IRQLine ? 0x80 : 0; - CPU.IRQLine = FALSE; - CPU.IRQTransition = FALSE; + byte = (CPU.IRQActive & PPU_IRQ_SOURCE) ? 0x80 : 0; + S9xClearIRQ(PPU_IRQ_SOURCE); return (byte | (OpenBus & 0x7f)); case 0x4212: // HVBJOY + #ifdef CPU_SHUTDOWN + CPU.WaitAddress = CPU.PBPCAtOpcodeStart; + #endif return (REGISTER_4212() | (OpenBus & 0x3e)); case 0x4213: // RDIO @@ -1953,7 +2185,11 @@ void S9xSoftResetPPU (void) ZeroMemory(IPPU.TileCached[TILE_2BIT_ODD], MAX_2BIT_TILES); ZeroMemory(IPPU.TileCached[TILE_4BIT_EVEN], MAX_4BIT_TILES); ZeroMemory(IPPU.TileCached[TILE_4BIT_ODD], MAX_4BIT_TILES); +#ifdef CORRECT_VRAM_READS IPPU.VRAMReadBuffer = 0; // XXX: FIXME: anything better? +#else + IPPU.FirstVRAMRead = FALSE; +#endif IPPU.Interlace = FALSE; IPPU.InterlaceOBJ = FALSE; IPPU.DoubleWidthPixels = FALSE; diff --git a/source/snes9x/ppu.h b/source/snes9x/ppu.h index c5cb9d7..7f20e89 100644 --- a/source/snes9x/ppu.h +++ b/source/snes9x/ppu.h @@ -197,6 +197,11 @@ #define CLIP_XOR 2 #define CLIP_XNOR 3 +#define PPU_IRQ_SOURCE (1 << 1) +#define GSU_IRQ_SOURCE (1 << 2) +#define SA1_IRQ_SOURCE (1 << 7) +#define SA1_DMA_IRQ_SOURCE (1 << 5) + struct ClipData { uint8 Count; @@ -213,7 +218,11 @@ struct InternalPPU bool8 DirectColourMapsNeedRebuild; uint8 *TileCache[7]; uint8 *TileCached[7]; +#ifdef CORRECT_VRAM_READS uint16 VRAMReadBuffer; +#else + bool8 FirstVRAMRead; +#endif bool8 Interlace; bool8 InterlaceOBJ; bool8 PseudoHires; @@ -376,6 +385,9 @@ uint8 S9xGetPPU (uint16); void S9xSetCPU (uint8, uint16); uint8 S9xGetCPU (uint16); void S9xUpdateHVTimerPosition (void); +void S9xCheckMissingHTimerPosition (int32); +void S9xCheckMissingHTimerRange (int32, int32); +void S9xCheckMissingHTimerHalt (int32, int32); void S9xFixColourBrightness (void); void S9xDoAutoJoypad (void); diff --git a/source/snes9x/sa1.cpp b/source/snes9x/sa1.cpp index a35e553..df28bac 100644 --- a/source/snes9x/sa1.cpp +++ b/source/snes9x/sa1.cpp @@ -180,6 +180,7 @@ uint8 SA1OpenBus; +static void S9xSA1Reset (void); static void S9xSA1SetBWRAMMemMap (uint8); static void S9xSetSA1MemMap (uint32, uint8); static void S9xSA1CharConv2 (void); @@ -189,37 +190,31 @@ static void S9xSA1ReadVariableLengthData (bool8, bool8); void S9xSA1Init (void) { - SA1.Cycles = 0; - SA1.PrevCycles = 0; - SA1.Flags = 0; + SA1.IRQActive = FALSE; SA1.WaitingForInterrupt = FALSE; - + SA1.Waiting = FALSE; + SA1.Flags = 0; + SA1.Executing = FALSE; memset(&Memory.FillRAM[0x2200], 0, 0x200); Memory.FillRAM[0x2200] = 0x20; Memory.FillRAM[0x2220] = 0x00; Memory.FillRAM[0x2221] = 0x01; Memory.FillRAM[0x2222] = 0x02; Memory.FillRAM[0x2223] = 0x03; - Memory.FillRAM[0x2228] = 0x0f; - - SA1.in_char_dma = FALSE; - SA1.TimerIRQLastState = FALSE; - SA1.HTimerIRQPos = 0; - SA1.VTimerIRQPos = 0; - SA1.HCounter = 0; - SA1.VCounter = 0; - SA1.PrevHCounter = 0; - SA1.arithmetic_op = 0; + Memory.FillRAM[0x2228] = 0xff; SA1.op1 = 0; SA1.op2 = 0; + SA1.arithmetic_op = 0; SA1.sum = 0; SA1.overflow = FALSE; - SA1.VirtualBitmapFormat = 0; - SA1.variable_bit_pos = 0; + SA1.S9xOpcodes = NULL; +} +static void S9xSA1Reset (void) +{ SA1Registers.PBPC = 0; SA1Registers.PB = 0; - SA1Registers.PCw = 0; + SA1Registers.PCw = Memory.FillRAM[0x2203] | (Memory.FillRAM[0x2204] << 8); SA1Registers.D.W = 0; SA1Registers.DB = 0; SA1Registers.SH = 1; @@ -233,20 +228,17 @@ void S9xSA1Init (void) SA1SetFlags(MemoryFlag | IndexFlag | IRQ | Emulation); SA1ClearFlags(Decimal); - SA1.MemSpeed = SLOW_ONE_CYCLE; - SA1.MemSpeedx2 = SLOW_ONE_CYCLE * 2; - + SA1.WaitingForInterrupt = FALSE; + SA1.PCBase = NULL; + S9xSA1SetPCBase(SA1Registers.PBPC); SA1.S9xOpcodes = S9xSA1OpcodesM1X1; SA1.S9xOpLengths = S9xOpLengthsM1X1; - S9xSA1SetPCBase(SA1Registers.PBPC); - S9xSA1UnpackStatus(); S9xSA1FixCycles(); - + SA1.Executing = TRUE; SA1.BWRAM = Memory.SRAM; - - CPU.IRQExternal = FALSE; + Memory.FillRAM[0x2225] = 0; } static void S9xSA1SetBWRAMMemMap (uint8 val) @@ -288,6 +280,23 @@ void S9xSA1PostLoadState (void) SA1.VirtualBitmapFormat = (Memory.FillRAM[0x223f] & 0x80) ? 2 : 4; Memory.BWRAM = Memory.SRAM + (Memory.FillRAM[0x2224] & 7) * 0x2000; S9xSA1SetBWRAMMemMap(Memory.FillRAM[0x2225]); + + SA1.Waiting = (Memory.FillRAM[0x2200] & 0x60) != 0; + SA1.Executing = !SA1.Waiting; +} + +void S9xSA1ExecuteDuringSleep (void) +{ +#if 0 + if (SA1.Executing) + { + while (CPU.Cycles < CPU.NextEvent) + { + S9xSA1MainLoop(); + CPU.Cycles += TWO_CYCLES * 2; + } + } +#endif } static void S9xSetSA1MemMap (uint32 which1, uint8 map) @@ -317,48 +326,31 @@ uint8 S9xGetSA1 (uint32 address) { switch (address) { - case 0x2300: // S-CPU flag - return ((Memory.FillRAM[0x2209] & 0x5f) | (Memory.FillRAM[0x2300] & 0xa0)); + case 0x2300: + return ((uint8) ((Memory.FillRAM[0x2209] & 0x5f) | (CPU.IRQActive & (SA1_IRQ_SOURCE | SA1_DMA_IRQ_SOURCE)))); - case 0x2301: // SA-1 flag - return ((Memory.FillRAM[0x2200] & 0x0f) | (Memory.FillRAM[0x2301] & 0xf0)); + case 0x2301: + return ((Memory.FillRAM[0x2200] & 0xf) | (Memory.FillRAM[0x2301] & 0xf0)); - case 0x2302: // H counter (L) - SA1.HTimerIRQPos = SA1.HCounter / ONE_DOT_CYCLE; - SA1.VTimerIRQPos = SA1.VCounter; - return ((uint8) SA1.HTimerIRQPos); - - case 0x2303: // H counter (H) - return ((uint8) (SA1.HTimerIRQPos >> 8)); - - case 0x2304: // V counter (L) - return ((uint8) SA1.VTimerIRQPos); - - case 0x2305: // V counter (H) - return ((uint8) (SA1.VTimerIRQPos >> 8)); - - case 0x2306: // arithmetic result (LLL) + case 0x2306: return ((uint8) SA1.sum); - case 0x2307: // arithmetic result (LLH) + case 0x2307: return ((uint8) (SA1.sum >> 8)); - case 0x2308: // arithmetic result (LHL) + case 0x2308: return ((uint8) (SA1.sum >> 16)); - case 0x2309: // arithmetic result (LLH) + case 0x2309: return ((uint8) (SA1.sum >> 24)); - case 0x230a: // arithmetic result (HLL) + case 0x230a: return ((uint8) (SA1.sum >> 32)); - case 0x230b: // arithmetic overflow - return (SA1.overflow ? 0x80 : 0); - - case 0x230c: // variable-length data read port (L) + case 0x230c: return (Memory.FillRAM[0x230c]); - case 0x230d: // variable-length data read port (H) + case 0x230d: { uint8 byte = Memory.FillRAM[0x230d]; @@ -368,10 +360,8 @@ uint8 S9xGetSA1 (uint32 address) return (byte); } - case 0x230e: // version code register - return (0x01); - default: + //printf("R: %04x\n", address); break; } @@ -382,279 +372,337 @@ void S9xSetSA1 (uint8 byte, uint32 address) { switch (address) { - case 0x2200: // SA-1 control - #ifdef DEBUGGER - if (byte & 0x60) - printf("SA-1 sleep\n"); - #endif + case 0x2200: + SA1.Waiting = (byte & 0x60) != 0; + //SA1.Executing = !SA1.Waiting && SA1.S9xOpcodes; - // SA-1 reset - if (!(byte & 0x80) && (Memory.FillRAM[0x2200] & 0x20)) - { - #ifdef DEBUGGER - printf("SA-1 reset\n"); - #endif - SA1Registers.PBPC = 0; - SA1Registers.PB = 0; - SA1Registers.PCw = Memory.FillRAM[0x2203] | (Memory.FillRAM[0x2204] << 8); - S9xSA1SetPCBase(SA1Registers.PBPC); - } + if (!(byte & 0x20) && (Memory.FillRAM[0x2200] & 0x20)) + S9xSA1Reset(); - // SA-1 IRQ control if (byte & 0x80) { Memory.FillRAM[0x2301] |= 0x80; if (Memory.FillRAM[0x220a] & 0x80) - Memory.FillRAM[0x220b] &= ~0x80; + { + SA1.Flags |= IRQ_FLAG; + SA1.IRQActive |= SNES_IRQ_SOURCE; + SA1.Executing = !SA1.Waiting && SA1.S9xOpcodes; + } } - // SA-1 NMI control if (byte & 0x10) { Memory.FillRAM[0x2301] |= 0x10; if (Memory.FillRAM[0x220a] & 0x10) - Memory.FillRAM[0x220b] &= ~0x10; - } - - break; - - case 0x2201: // S-CPU interrupt enable - // S-CPU IRQ enable - if (((byte ^ Memory.FillRAM[0x2201]) & 0x80) && (Memory.FillRAM[0x2300] & byte & 0x80)) - { - Memory.FillRAM[0x2202] &= ~0x80; - CPU.IRQExternal = TRUE; - } - - // S-CPU CHDMA IRQ enable - if (((byte ^ Memory.FillRAM[0x2201]) & 0x20) && (Memory.FillRAM[0x2300] & byte & 0x20)) - { - Memory.FillRAM[0x2202] &= ~0x20; - CPU.IRQExternal = TRUE; - } - - break; - - case 0x2202: // S-CPU interrupt clear - // S-CPU IRQ clear - if (byte & 0x80) - Memory.FillRAM[0x2300] &= ~0x80; - - // S-CPU CHDMA IRQ clear - if (byte & 0x20) - Memory.FillRAM[0x2300] &= ~0x20; - - if (!(Memory.FillRAM[0x2300] & 0xa0)) - CPU.IRQExternal = FALSE; - - break; - - case 0x2203: // SA-1 reset vector (L) - case 0x2204: // SA-1 reset vector (H) - case 0x2205: // SA-1 NMI vector (L) - case 0x2206: // SA-1 NMI vector (H) - case 0x2207: // SA-1 IRQ vector (L) - case 0x2208: // SA-1 IRQ vector (H) - break; - - case 0x2209: // S-CPU control - // 0x40: S-CPU IRQ overwrite - // 0x20: S-CPU NMI overwrite - - // S-CPU IRQ control - if (byte & 0x80) - { - Memory.FillRAM[0x2300] |= 0x80; - if (Memory.FillRAM[0x2201] & 0x80) { - Memory.FillRAM[0x2202] &= ~0x80; - CPU.IRQExternal = TRUE; + SA1.Flags |= NMI_FLAG; + SA1.Executing = !SA1.Waiting && SA1.S9xOpcodes; } } break; - case 0x220a: // SA-1 interrupt enable - // SA-1 IRQ enable - if (((byte ^ Memory.FillRAM[0x220a]) & 0x80) && (Memory.FillRAM[0x2301] & byte & 0x80)) - Memory.FillRAM[0x220b] &= ~0x80; + case 0x2201: + if (((byte ^ Memory.FillRAM[0x2201]) & 0x80) && (Memory.FillRAM[0x2300] & byte & 0x80)) + S9xSetIRQ(SA1_IRQ_SOURCE); - // SA-1 timer IRQ enable - if (((byte ^ Memory.FillRAM[0x220a]) & 0x40) && (Memory.FillRAM[0x2301] & byte & 0x40)) - Memory.FillRAM[0x220b] &= ~0x40; - - // SA-1 DMA IRQ enable - if (((byte ^ Memory.FillRAM[0x220a]) & 0x20) && (Memory.FillRAM[0x2301] & byte & 0x20)) - Memory.FillRAM[0x220b] &= ~0x20; - - // SA-1 NMI enable - if (((byte ^ Memory.FillRAM[0x220a]) & 0x10) && (Memory.FillRAM[0x2301] & byte & 0x10)) - Memory.FillRAM[0x220b] &= ~0x10; + if (((byte ^ Memory.FillRAM[0x2201]) & 0x20) && (Memory.FillRAM[0x2300] & byte & 0x20)) + S9xSetIRQ(SA1_DMA_IRQ_SOURCE); break; - case 0x220b: // SA-1 interrupt clear - // SA-1 IRQ clear + case 0x2202: if (byte & 0x80) - Memory.FillRAM[0x2301] &= ~0x80; + { + Memory.FillRAM[0x2300] &= ~0x80; + S9xClearIRQ(SA1_IRQ_SOURCE); + } - // SA-1 timer IRQ clear - if (byte & 0x40) - Memory.FillRAM[0x2301] &= ~0x40; - - // SA-1 DMA IRQ clear if (byte & 0x20) - Memory.FillRAM[0x2301] &= ~0x20; + { + Memory.FillRAM[0x2300] &= ~0x20; + S9xClearIRQ(SA1_DMA_IRQ_SOURCE); + } + + break; + + case 0x2203: + //printf("SA1 reset vector: %04x\n", byte | (Memory.FillRAM[0x2204] << 8)); + break; + + case 0x2204: + //printf("SA1 reset vector: %04x\n", (byte << 8) | Memory.FillRAM[0x2203]); + break; + + case 0x2205: + //printf("SA1 NMI vector: %04x\n", byte | (Memory.FillRAM[0x2206] << 8)); + break; + + case 0x2206: + //printf("SA1 NMI vector: %04x\n", (byte << 8) | Memory.FillRAM[0x2205]); + break; + + case 0x2207: + //printf("SA1 IRQ vector: %04x\n", byte | (Memory.FillRAM[0x2208] << 8)); + break; + + case 0x2208: + //printf("SA1 IRQ vector: %04x\n", (byte << 8) | Memory.FillRAM[0x2207]); + break; + + case 0x2209: + Memory.FillRAM[0x2209] = byte; + + if (byte & 0x80) + Memory.FillRAM[0x2300] |= 0x80; + + if (byte & Memory.FillRAM[0x2201] & 0x80) + S9xSetIRQ(SA1_IRQ_SOURCE); + + break; + + case 0x220a: + if (((byte ^ Memory.FillRAM[0x220a]) & 0x80) && (Memory.FillRAM[0x2301] & byte & 0x80)) + { + SA1.Flags |= IRQ_FLAG; + SA1.IRQActive |= SNES_IRQ_SOURCE; + //SA1.Executing = !SA1.Waiting; + } + + if (((byte ^ Memory.FillRAM[0x220a]) & 0x40) && (Memory.FillRAM[0x2301] & byte & 0x40)) + { + SA1.Flags |= IRQ_FLAG; + SA1.IRQActive |= TIMER_IRQ_SOURCE; + //SA1.Executing = !SA1.Waiting; + } + + if (((byte ^ Memory.FillRAM[0x220a]) & 0x20) && (Memory.FillRAM[0x2301] & byte & 0x20)) + { + SA1.Flags |= IRQ_FLAG; + SA1.IRQActive |= DMA_IRQ_SOURCE; + //SA1.Executing = !SA1.Waiting; + } + + if (((byte ^ Memory.FillRAM[0x220a]) & 0x10) && (Memory.FillRAM[0x2301] & byte & 0x10)) + { + SA1.Flags |= NMI_FLAG; + //SA1.Executing = !SA1.Waiting; + } + + break; + + case 0x220b: + if (byte & 0x80) + { + SA1.IRQActive &= ~SNES_IRQ_SOURCE; + Memory.FillRAM[0x2301] &= ~0x80; + } + + if (byte & 0x40) + { + SA1.IRQActive &= ~TIMER_IRQ_SOURCE; + Memory.FillRAM[0x2301] &= ~0x40; + } + + if (byte & 0x20) + { + SA1.IRQActive &= ~DMA_IRQ_SOURCE; + Memory.FillRAM[0x2301] &= ~0x20; + } - // SA-1 NMI clear if (byte & 0x10) Memory.FillRAM[0x2301] &= ~0x10; + if (!SA1.IRQActive) + SA1.Flags &= ~IRQ_FLAG; + break; - case 0x220c: // S-CPU NMI vector (L) - case 0x220d: // S-CPU NMI vector (H) - case 0x220e: // S-CPU IRQ vector (L) - case 0x220f: // S-CPU IRQ vector (H) + case 0x220c: + //printf("SNES NMI vector: %04x\n", byte | (Memory.FillRAM[0x220d] << 8)); break; - case 0x2210: // SA-1 timer control - // 0x80: mode (linear / HV) - // 0x02: V timer enable - // 0x01: H timer enable - #ifdef DEBUGGER - printf("SA-1 timer control write:%02x\n", byte); + case 0x220d: + //printf("SNES NMI vector: %04x\n", (byte << 8) | Memory.FillRAM[0x220c]); + break; + + case 0x220e: + //printf("SNES IRQ vector: %04x\n", byte | (Memory.FillRAM[0x220f] << 8)); + break; + + case 0x220f: + //printf("SNES IRQ vector: %04x\n", (byte << 8) | Memory.FillRAM[0x220e]); + break; + + case 0x2210: + #if 0 + printf("Timer %s\n", (byte & 0x80) ? "linear" : "HV"); + printf("Timer H-IRQ %s\n", (byte & 1) ? "enabled" : "disabled"); + printf("Timer V-IRQ %s\n", (byte & 2) ? "enabled" : "disabled"); #endif break; - case 0x2211: // SA-1 timer reset - SA1.HCounter = 0; - SA1.VCounter = 0; + case 0x2211: + //printf("Timer reset\n"); break; - case 0x2212: // SA-1 H-timer (L) - SA1.HTimerIRQPos = byte | (Memory.FillRAM[0x2213] << 8); + case 0x2212: + //printf("H-Timer %04x\n", byte | (Memory.FillRAM[0x2213] << 8)); break; - case 0x2213: // SA-1 H-timer (H) - SA1.HTimerIRQPos = (byte << 8) | Memory.FillRAM[0x2212]; + case 0x2213: + //printf("H-Timer %04x\n", (byte << 8) | Memory.FillRAM[0x2212]); break; - case 0x2214: // SA-1 V-timer (L) - SA1.VTimerIRQPos = byte | (Memory.FillRAM[0x2215] << 8); + case 0x2214: + //printf("V-Timer %04x\n", byte | (Memory.FillRAM[0x2215] << 8)); break; - case 0x2215: // SA-1 V-timer (H) - SA1.VTimerIRQPos = (byte << 8) | Memory.FillRAM[0x2214]; + case 0x2215: + //printf("V-Timer %04x\n", (byte << 8) | Memory.FillRAM[0x2214]); break; - case 0x2220: // MMC bank C - case 0x2221: // MMC bank D - case 0x2222: // MMC bank E - case 0x2223: // MMC bank F + case 0x2220: + case 0x2221: + case 0x2222: + case 0x2223: + //printf("MMC: %02x\n", byte); S9xSetSA1MemMap(address - 0x2220, byte); break; - case 0x2224: // S-CPU BW-RAM mapping + case 0x2224: + //printf("BWRAM image SNES %02x -> 0x6000\n", byte); Memory.BWRAM = Memory.SRAM + (byte & 7) * 0x2000; break; - case 0x2225: // SA-1 BW-RAM mapping + case 0x2225: + //printf("BWRAM image SA1 %02x -> 0x6000 (%02x)\n", byte, Memory.FillRAM[0x2225]); if (byte != Memory.FillRAM[0x2225]) S9xSA1SetBWRAMMemMap(byte); - break; - case 0x2226: // S-CPU BW-RAM write enable - case 0x2227: // SA-1 BW-RAM write enable - case 0x2228: // BW-RAM write-protected area - case 0x2229: // S-CPU I-RAM write protection - case 0x222a: // SA-1 I-RAM write protection + case 0x2226: + //printf("BW-RAM SNES write %s\n", (byte & 0x80) ? "enabled" : "disabled"); break; - case 0x2230: // DMA control - // 0x80: enable - // 0x40: priority (DMA / SA-1) - // 0x20: character conversion / normal - // 0x10: BW-RAM -> I-RAM / SA-1 -> I-RAM - // 0x04: destinatin (BW-RAM / I-RAM) - // 0x03: source + case 0x2227: + //printf("BW-RAM SA1 write %s\n", (byte & 0x80) ? "enabled" : "disabled"); break; - case 0x2231: // character conversion DMA parameters - // 0x80: CHDEND (complete / incomplete) - // 0x03: color mode - // (byte >> 2) & 7: virtual VRAM width + case 0x2228: + //printf("BW-RAM write protect area %02x\n", byte); + break; + + case 0x2229: + //printf("I-RAM SNES write protect area %02x\n", byte); + break; + + case 0x222a: + //printf("I-RAM SA1 write protect area %02x\n", byte); + break; + + case 0x2230: + #if 0 + printf("SA1 DMA %s\n", (byte & 0x80) ? "enabled" : "disabled"); + printf("DMA priority %s\n", (byte & 0x40) ? "DMA" : "SA1"); + printf("DMA %s\n", (byte & 0x20) ? "char conv" : "normal"); + printf("DMA type %s\n", (byte & 0x10) ? "BW-RAM -> I-RAM" : "SA1 -> I-RAM"); + printf("DMA distination %s\n", (byte & 4) ? "BW-RAM" : "I-RAM"); + printf("DMA source %s\n", DMAsource[byte & 3]); + #endif + break; + + case 0x2231: if (byte & 0x80) SA1.in_char_dma = FALSE; - + #if 0 + printf("CHDEND %s\n", (byte & 0x80) ? "complete" : "incomplete"); + printf("DMA colour mode %d\n", byte & 3); + printf("virtual VRAM width %d\n", (byte >> 2) & 7); + #endif break; - case 0x2232: // DMA source start address (LL) - case 0x2233: // DMA source start address (LH) - case 0x2234: // DMA source start address (HL) + case 0x2232: + case 0x2233: + case 0x2234: + Memory.FillRAM[address] = byte; + #if 0 + printf("DMA source start %06x\n", Memory.FillRAM[0x2232] | (Memory.FillRAM[0x2233] << 8) | (Memory.FillRAM[0x2234] << 16)); + #endif break; - case 0x2235: // DMA destination start address (LL) + case 0x2235: + Memory.FillRAM[0x2235] = byte; break; - case 0x2236: // DMA destination start address (LH) + case 0x2236: Memory.FillRAM[0x2236] = byte; if ((Memory.FillRAM[0x2230] & 0xa4) == 0x80) // Normal DMA to I-RAM S9xSA1DMA(); else - if ((Memory.FillRAM[0x2230] & 0xb0) == 0xb0) // CC1 + if ((Memory.FillRAM[0x2230] & 0xb0) == 0xb0) { - SA1.in_char_dma = TRUE; - Memory.FillRAM[0x2300] |= 0x20; if (Memory.FillRAM[0x2201] & 0x20) - { - Memory.FillRAM[0x2202] &= ~0x20; - CPU.IRQExternal = TRUE; - } + S9xSetIRQ(SA1_DMA_IRQ_SOURCE); + SA1.in_char_dma = TRUE; } break; - case 0x2237: // DMA destination start address (HL) + case 0x2237: Memory.FillRAM[0x2237] = byte; if ((Memory.FillRAM[0x2230] & 0xa4) == 0x84) // Normal DMA to BW-RAM S9xSA1DMA(); - + #if 0 + printf("DMA dest address %06x\n", Memory.FillRAM[0x2235] | (Memory.FillRAM[0x2236] << 8) | (Memory.FillRAM[0x2237] << 16)); + #endif break; - case 0x2238: // DMA terminal counter (L) - case 0x2239: // DMA terminal counter (H) + case 0x2238: + case 0x2239: + Memory.FillRAM[address] = byte; + #if 0 + printf("DMA length %04x\n", Memory.FillRAM[0x2238] | (Memory.FillRAM[0x2239] << 8)); + #endif break; - case 0x223f: // BW-RAM bitmap format + case 0x223f: + //printf("virtual VRAM depth %d\n", (byte & 0x80) ? 2 : 4); SA1.VirtualBitmapFormat = (byte & 0x80) ? 2 : 4; break; - case 0x2240: // bitmap register 0 - case 0x2241: // bitmap register 1 - case 0x2242: // bitmap register 2 - case 0x2243: // bitmap register 3 - case 0x2244: // bitmap register 4 - case 0x2245: // bitmap register 5 - case 0x2246: // bitmap register 6 - case 0x2247: // bitmap register 7 - case 0x2248: // bitmap register 8 - case 0x2249: // bitmap register 9 - case 0x224a: // bitmap register A - case 0x224b: // bitmap register B - case 0x224c: // bitmap register C - case 0x224d: // bitmap register D - case 0x224e: // bitmap register E + case 0x2240: + case 0x2241: + case 0x2242: + case 0x2243: + case 0x2244: + case 0x2245: + case 0x2246: + case 0x2247: + case 0x2248: + case 0x2249: + case 0x224a: + case 0x224b: + case 0x224c: + case 0x224d: + case 0x224e: + #if 0 + if (!(SA1.Flags & TRACE_FLAG)) + { + TraceSA1(); + Trace(); + } + #endif + Memory.FillRAM[address] = byte; break; - case 0x224f: // bitmap register F + case 0x224f: Memory.FillRAM[0x224f] = byte; - if ((Memory.FillRAM[0x2230] & 0xb0) == 0xa0) // CC2 + if ((Memory.FillRAM[0x2230] & 0xb0) == 0xa0) // Char conversion 2 DMA enabled { memmove(&Memory.ROM[CMemory::MAX_ROM_SIZE - 0x10000] + SA1.in_char_dma * 16, &Memory.FillRAM[0x2240], 16); SA1.in_char_dma = (SA1.in_char_dma + 1) & 7; @@ -664,67 +712,58 @@ void S9xSetSA1 (uint8 byte, uint32 address) break; - case 0x2250: // arithmetic control + case 0x2250: if (byte & 2) SA1.sum = 0; SA1.arithmetic_op = byte & 3; break; - case 0x2251: // multiplicand / dividend (L) - SA1.op1 = (SA1.op1 & 0xff00) | byte; + case 0x2251: + SA1.op1 = (SA1.op1 & 0xff00) | byte; break; - case 0x2252: // multiplicand / dividend (H) - SA1.op1 = (SA1.op1 & 0x00ff) | (byte << 8); + case 0x2252: + SA1.op1 = (SA1.op1 & 0xff) | (byte << 8); break; - case 0x2253: // multiplier / divisor (L) - SA1.op2 = (SA1.op2 & 0xff00) | byte; + case 0x2253: + SA1.op2 = (SA1.op2 & 0xff00) | byte; break; - case 0x2254: // multiplier / divisor (H) - SA1.op2 = (SA1.op2 & 0x00ff) | (byte << 8); + case 0x2254: + SA1.op2 = (SA1.op2 & 0xff) | (byte << 8); switch (SA1.arithmetic_op) { - case 0: // signed multiplication - SA1.sum = (int16) SA1.op1 * (int16) SA1.op2; - SA1.op2 = 0; + case 0: // multiply + SA1.sum = SA1.op1 * SA1.op2; break; - case 1: // unsigned division + case 1: // divide if (SA1.op2 == 0) - SA1.sum = 0; + SA1.sum = SA1.op1 << 16; else - { - int16 quotient = (int16) SA1.op1 / (uint16) SA1.op2; - uint16 remainder = (int16) SA1.op1 % (uint16) SA1.op2; - SA1.sum = (remainder << 16) | quotient; - } - - SA1.op1 = 0; - SA1.op2 = 0; + SA1.sum = (SA1.op1 / (int) ((uint16) SA1.op2)) | ((SA1.op1 % (int) ((uint16) SA1.op2)) << 16); break; case 2: // cumulative sum default: - SA1.sum += (int16) SA1.op1 * (int16) SA1.op2; - SA1.overflow = (SA1.sum >= (1ULL << 40)); - SA1.sum &= (1ULL << 40) - 1; - SA1.op2 = 0; + SA1.sum += SA1.op1 * SA1.op2; + if (SA1.sum & ((int64) 0xffffff << 32)) + SA1.overflow = TRUE; break; } break; - case 0x2258: // variable bit-field length / auto inc / start + case 0x2258: // Variable bit-field length/auto inc/start. Memory.FillRAM[0x2258] = byte; S9xSA1ReadVariableLengthData(TRUE, FALSE); return; - case 0x2259: // variable bit-field start address (LL) - case 0x225a: // variable bit-field start address (LH) - case 0x225b: // variable bit-field start address (HL) + case 0x2259: // Variable bit-field start address + case 0x225a: + case 0x225b: Memory.FillRAM[address] = byte; // XXX: ??? SA1.variable_bit_pos = 0; @@ -732,6 +771,7 @@ void S9xSetSA1 (uint8 byte, uint32 address) return; default: + //printf("W: %02x->%04x\n", byte, address); break; } @@ -850,11 +890,14 @@ static void S9xSA1DMA (void) } memmove(d, s, len); - - // SA-1 DMA IRQ control Memory.FillRAM[0x2301] |= 0x20; + if (Memory.FillRAM[0x220a] & 0x20) - Memory.FillRAM[0x220b] &= ~0x20; + { + SA1.Flags |= IRQ_FLAG; + SA1.IRQActive |= DMA_IRQ_SOURCE; + //SA1.Executing = !SA1.Waiting; + } } static void S9xSA1ReadVariableLengthData (bool8 inc, bool8 no_shift) @@ -1053,10 +1096,6 @@ void S9xSA1SetPCBase (uint32 address) SA1Registers.PBPC = address & 0xffffff; SA1.ShiftedPB = address & 0xff0000; - // FIXME - SA1.MemSpeed = memory_speed(address); - SA1.MemSpeedx2 = SA1.MemSpeed << 1; - uint8 *GetAddress = SA1.Map[(address & 0xffffff) >> MEMMAP_SHIFT]; if (GetAddress >= (uint8 *) CMemory::MAP_LAST) @@ -1094,3 +1133,4 @@ void S9xSA1SetPCBase (uint32 address) return; } } + diff --git a/source/snes9x/sa1.h b/source/snes9x/sa1.h index eef38a2..7849c8c 100644 --- a/source/snes9x/sa1.h +++ b/source/snes9x/sa1.h @@ -198,33 +198,32 @@ struct SSA1 uint8 _Zero; uint8 _Negative; uint8 _Overflow; + bool8 CPUExecuting; uint32 ShiftedPB; uint32 ShiftedDB; uint32 Flags; - int32 Cycles; - int32 PrevCycles; uint8 *PCBase; + bool8 IRQActive; + bool8 Waiting; bool8 WaitingForInterrupt; + uint32 WaitAddress; + uint32 WaitCounter; + uint32 PBPCAtOpcodeStart; + uint8 *WaitByteAddress1; + uint8 *WaitByteAddress2; uint8 *Map[MEMMAP_NUM_BLOCKS]; uint8 *WriteMap[MEMMAP_NUM_BLOCKS]; uint8 *BWRAM; - bool8 in_char_dma; - bool8 TimerIRQLastState; - uint16 HTimerIRQPos; - uint16 VTimerIRQPos; - int16 HCounter; - int16 VCounter; - int16 PrevHCounter; - int32 MemSpeed; - int32 MemSpeedx2; - int32 arithmetic_op; - uint16 op1; - uint16 op2; - uint64 sum; + bool8 Executing; bool8 overflow; + bool8 in_char_dma; + int16 op1; + int16 op2; + int32 arithmetic_op; + int64 sum; uint8 VirtualBitmapFormat; uint8 variable_bit_pos; }; @@ -264,8 +263,13 @@ uint8 S9xGetSA1 (uint32); void S9xSetSA1 (uint8, uint32); void S9xSA1Init (void); void S9xSA1MainLoop (void); +void S9xSA1ExecuteDuringSleep (void); void S9xSA1PostLoadState (void); +#define SNES_IRQ_SOURCE (1 << 7) +#define TIMER_IRQ_SOURCE (1 << 6) +#define DMA_IRQ_SOURCE (1 << 5) + static inline void S9xSA1UnpackStatus (void) { SA1._Zero = (SA1Registers.PL & Zero) == 0; diff --git a/source/snes9x/sa1cpu.cpp b/source/snes9x/sa1cpu.cpp index 994e240..e509361 100644 --- a/source/snes9x/sa1cpu.cpp +++ b/source/snes9x/sa1cpu.cpp @@ -221,43 +221,20 @@ #define StackRelative SA1StackRelative #define StackRelativeIndirectIndexed SA1StackRelativeIndirectIndexed +//#undef CPU_SHUTDOWN #define SA1_OPCODES #include "cpuops.cpp" -static void S9xSA1UpdateTimer (void); - void S9xSA1MainLoop (void) { - if (Memory.FillRAM[0x2200] & 0x60) + if (SA1.Flags & NMI_FLAG) { - SA1.Cycles += 6; // FIXME - S9xSA1UpdateTimer(); - return; - } - - // SA-1 NMI - if ((Memory.FillRAM[0x2200] & 0x10) && !(Memory.FillRAM[0x220b] & 0x10)) - { - Memory.FillRAM[0x2301] |= 0x10; - Memory.FillRAM[0x220b] |= 0x10; - - if (SA1.WaitingForInterrupt) + if (Memory.FillRAM[0x2200] & 0x10) { - SA1.WaitingForInterrupt = FALSE; - SA1Registers.PCw++; - } - - S9xSA1Opcode_NMI(); - } - else - if (!SA1CheckFlag(IRQ)) - { - // SA-1 Timer IRQ - if ((Memory.FillRAM[0x220a] & 0x40) && !(Memory.FillRAM[0x220b] & 0x40)) - { - Memory.FillRAM[0x2301] |= 0x40; + SA1.Flags &= ~NMI_FLAG; + Memory.FillRAM[0x2301] |= 0x10; if (SA1.WaitingForInterrupt) { @@ -265,45 +242,38 @@ void S9xSA1MainLoop (void) SA1Registers.PCw++; } - S9xSA1Opcode_IRQ(); + S9xSA1Opcode_NMI(); + } + } + + if (SA1.Flags & IRQ_FLAG) + { + if (SA1.IRQActive) + { + if (SA1.WaitingForInterrupt) + { + SA1.WaitingForInterrupt = FALSE; + SA1Registers.PCw++; + } + + if (!SA1CheckFlag(IRQ)) + S9xSA1Opcode_IRQ(); } else - // SA-1 DMA IRQ - if ((Memory.FillRAM[0x220a] & 0x20) && !(Memory.FillRAM[0x220b] & 0x20)) - { - Memory.FillRAM[0x2301] |= 0x20; - - if (SA1.WaitingForInterrupt) - { - SA1.WaitingForInterrupt = FALSE; - SA1Registers.PCw++; - } - - S9xSA1Opcode_IRQ(); - } - else - // SA-1 IRQ - if ((Memory.FillRAM[0x2200] & 0x80) && !(Memory.FillRAM[0x220b] & 0x80)) - { - Memory.FillRAM[0x2301] |= 0x80; - - if (SA1.WaitingForInterrupt) - { - SA1.WaitingForInterrupt = FALSE; - SA1Registers.PCw++; - } - - S9xSA1Opcode_IRQ(); - } + SA1.Flags &= ~IRQ_FLAG; } - for (int i = 0; i < 3 && !(Memory.FillRAM[0x2200] & 0x60); i++) + for (int i = 0; i < 3 && SA1.Executing; i++) { #ifdef DEBUGGER if (SA1.Flags & TRACE_FLAG) S9xSA1Trace(); #endif + #ifdef CPU_SHUTDOWN + SA1.PBPCAtOpcodeStart = SA1Registers.PBPC; + #endif + register uint8 Op; register struct SOpcodes *Opcodes; @@ -329,71 +299,5 @@ void S9xSA1MainLoop (void) Registers.PCw++; (*Opcodes[Op].S9xOpcode)(); } - - S9xSA1UpdateTimer(); } -static void S9xSA1UpdateTimer (void) // FIXME -{ - SA1.PrevHCounter = SA1.HCounter; - - if (Memory.FillRAM[0x2210] & 0x80) - { - SA1.HCounter += (SA1.Cycles - SA1.PrevCycles); - if (SA1.HCounter >= 0x800) - { - SA1.HCounter -= 0x800; - SA1.PrevHCounter -= 0x800; - if (++SA1.VCounter >= 0x200) - SA1.VCounter = 0; - } - } - else - { - SA1.HCounter += (SA1.Cycles - SA1.PrevCycles); - if (SA1.HCounter >= Timings.H_Max_Master) - { - SA1.HCounter -= Timings.H_Max_Master; - SA1.PrevHCounter -= Timings.H_Max_Master; - if (++SA1.VCounter >= Timings.V_Max_Master) - SA1.VCounter = 0; - } - } - - if (SA1.Cycles >= Timings.H_Max_Master) - SA1.Cycles -= Timings.H_Max_Master; - - SA1.PrevCycles = SA1.Cycles; - - bool8 thisIRQ = Memory.FillRAM[0x2210] & 0x03; - - if (Memory.FillRAM[0x2210] & 0x01) - { - if (SA1.PrevHCounter >= SA1.HTimerIRQPos * ONE_DOT_CYCLE || SA1.HCounter < SA1.HTimerIRQPos * ONE_DOT_CYCLE) - thisIRQ = FALSE; - } - - if (Memory.FillRAM[0x2210] & 0x02) - { - if (SA1.VCounter != SA1.VTimerIRQPos * ONE_DOT_CYCLE) - thisIRQ = FALSE; - } - - // SA-1 Timer IRQ control - if (!SA1.TimerIRQLastState && thisIRQ) - { - Memory.FillRAM[0x2301] |= 0x40; - if (Memory.FillRAM[0x220a] & 0x40) - { - Memory.FillRAM[0x220b] &= ~0x40; - #ifdef DEBUGGER - S9xTraceFormattedMessage("--- SA-1 Timer IRQ triggered prev HC:%04d curr HC:%04d HTimer:%d Pos:%04d VTimer:%d Pos:%03d", - SA1.PrevHCounter, SA1.HCounter, - (Memory.FillRAM[0x2210] & 0x01) ? 1 : 0, SA1.HTimerIRQPos * ONE_DOT_CYCLE, - (Memory.FillRAM[0x2210] & 0x02) ? 1 : 0, SA1.VTimerIRQPos); - #endif - } - } - - SA1.TimerIRQLastState = thisIRQ; -} diff --git a/source/snes9x/snapshot.cpp b/source/snes9x/snapshot.cpp index 468a681..13934f0 100644 --- a/source/snes9x/snapshot.cpp +++ b/source/snes9x/snapshot.cpp @@ -342,7 +342,7 @@ struct SnapshotScreenshotInfo static struct Obsolete { - uint8 CPU_IRQActive; + uint8 reserved; } Obsolete; #define STRUCT struct SCPUState @@ -353,7 +353,7 @@ static FreezeData SnapCPU[] = INT_ENTRY(6, PrevCycles), INT_ENTRY(6, V_Counter), INT_ENTRY(6, Flags), - OBSOLETE_INT_ENTRY(6, 7, CPU_IRQActive), + INT_ENTRY(6, IRQActive), INT_ENTRY(6, IRQPending), INT_ENTRY(6, MemSpeed), INT_ENTRY(6, MemSpeedx2), @@ -366,14 +366,9 @@ static FreezeData SnapCPU[] = INT_ENTRY(6, WhichEvent), INT_ENTRY(6, NextEvent), INT_ENTRY(6, WaitingForInterrupt), - DELETED_INT_ENTRY(6, 7, WaitAddress, 4), - DELETED_INT_ENTRY(6, 7, WaitCounter, 4), - DELETED_INT_ENTRY(6, 7, PBPCAtOpcodeStart, 4), - INT_ENTRY(7, NMILine), - INT_ENTRY(7, IRQLine), - INT_ENTRY(7, IRQTransition), - INT_ENTRY(7, IRQLastState), - INT_ENTRY(7, IRQExternal) + INT_ENTRY(6, WaitAddress), + INT_ENTRY(6, WaitCounter), + INT_ENTRY(6, PBPCAtOpcodeStart) }; #undef STRUCT @@ -581,9 +576,7 @@ static FreezeData SnapTimings[] = INT_ENTRY(6, DMACPUSync), INT_ENTRY(6, NMIDMADelay), INT_ENTRY(6, IRQPendCount), - INT_ENTRY(6, APUSpeedup), - INT_ENTRY(7, IRQTriggerCycles), - INT_ENTRY(7, APUAllowTimeOverflow) + INT_ENTRY(6, APUSpeedup) }; #undef STRUCT @@ -652,17 +645,17 @@ static FreezeData SnapFX[] = static FreezeData SnapSA1[] = { - DELETED_INT_ENTRY(6, 7, CPUExecuting, 1), + INT_ENTRY(6, CPUExecuting), INT_ENTRY(6, ShiftedPB), INT_ENTRY(6, ShiftedDB), INT_ENTRY(6, Flags), - DELETED_INT_ENTRY(6, 7, IRQActive, 1), - DELETED_INT_ENTRY(6, 7, Waiting, 1), + INT_ENTRY(6, IRQActive), + INT_ENTRY(6, Waiting), INT_ENTRY(6, WaitingForInterrupt), - DELETED_INT_ENTRY(6, 7, WaitAddress, 4), - DELETED_INT_ENTRY(6, 7, WaitCounter, 4), - DELETED_INT_ENTRY(6, 7, PBPCAtOpcodeStart, 4), - DELETED_INT_ENTRY(6, 7, Executing, 1), + INT_ENTRY(6, WaitAddress), + INT_ENTRY(6, WaitCounter), + INT_ENTRY(6, PBPCAtOpcodeStart), + INT_ENTRY(6, Executing), INT_ENTRY(6, overflow), INT_ENTRY(6, in_char_dma), INT_ENTRY(6, op1), @@ -670,17 +663,7 @@ static FreezeData SnapSA1[] = INT_ENTRY(6, arithmetic_op), INT_ENTRY(6, sum), INT_ENTRY(6, VirtualBitmapFormat), - INT_ENTRY(6, variable_bit_pos), - INT_ENTRY(7, Cycles), - INT_ENTRY(7, PrevCycles), - INT_ENTRY(7, TimerIRQLastState), - INT_ENTRY(7, HTimerIRQPos), - INT_ENTRY(7, VTimerIRQPos), - INT_ENTRY(7, HCounter), - INT_ENTRY(7, VCounter), - INT_ENTRY(7, PrevHCounter), - INT_ENTRY(7, MemSpeed), - INT_ENTRY(7, MemSpeedx2) + INT_ENTRY(6, variable_bit_pos) }; #undef STRUCT @@ -1268,7 +1251,7 @@ bool8 S9xUnfreezeGame (const char *filename) void S9xFreezeToStream (STREAM stream) { char buffer[1024]; - uint8 *soundsnapshot = new uint8[SPC_SAVE_STATE_BLOCK_SIZE]; + uint8 *soundsnapshot = new uint8[SPC_SAVE_STATE_BLOCK_SIZE]; S9xSetSoundMute(TRUE); @@ -1677,40 +1660,6 @@ int S9xUnfreezeFromStream (STREAM stream) if (local_bsx_data) UnfreezeStructFromCopy(&BSX, SnapBSX, COUNT(SnapBSX), local_bsx_data, version); - if (version < SNAPSHOT_VERSION) - { - printf("Converting old snapshot version %d to %d\n...", version, SNAPSHOT_VERSION); - - CPU.NMILine = (CPU.Flags & (1 << 7)) ? TRUE : FALSE; - CPU.IRQLine = (CPU.Flags & (1 << 11)) ? TRUE : FALSE; - CPU.IRQTransition = FALSE; - CPU.IRQLastState = FALSE; - CPU.IRQExternal = (Obsolete.CPU_IRQActive & ~(1 << 1)) ? TRUE : FALSE; - - switch (CPU.WhichEvent) - { - case 12: case 1: CPU.WhichEvent = 1; break; - case 2: case 3: CPU.WhichEvent = 2; break; - case 4: case 5: CPU.WhichEvent = 3; break; - case 6: case 7: CPU.WhichEvent = 4; break; - case 8: case 9: CPU.WhichEvent = 5; break; - case 10: case 11: CPU.WhichEvent = 6; break; - } - - if (local_sa1) // FIXME - { - SA1.Cycles = SA1.PrevCycles = 0; - SA1.TimerIRQLastState = FALSE; - SA1.HTimerIRQPos = Memory.FillRAM[0x2212] | (Memory.FillRAM[0x2213] << 8); - SA1.VTimerIRQPos = Memory.FillRAM[0x2214] | (Memory.FillRAM[0x2215] << 8); - SA1.HCounter = 0; - SA1.VCounter = 0; - SA1.PrevHCounter = 0; - SA1.MemSpeed = SLOW_ONE_CYCLE; - SA1.MemSpeedx2 = SLOW_ONE_CYCLE * 2; - } - } - CPU.Flags |= old_flags & (DEBUG_MODE_FLAG | TRACE_FLAG | SINGLE_STEP_FLAG | FRAME_ADVANCE_FLAG); ICPU.ShiftedPB = Registers.PB << 16; ICPU.ShiftedDB = Registers.DB << 16; @@ -2298,3 +2247,4 @@ bool8 S9xSPCDump (const char *filename) return (TRUE); } + diff --git a/source/snes9x/snapshot.h b/source/snes9x/snapshot.h index 55c72d7..b3ead24 100644 --- a/source/snes9x/snapshot.h +++ b/source/snes9x/snapshot.h @@ -179,7 +179,7 @@ #define _SNAPSHOT_H_ #define SNAPSHOT_MAGIC "#!s9xsnp" -#define SNAPSHOT_VERSION 7 +#define SNAPSHOT_VERSION 6 #define SUCCESS 1 #define WRONG_FORMAT (-1) diff --git a/source/snes9x/snes9x.h b/source/snes9x/snes9x.h index ccab877..a58be71 100644 --- a/source/snes9x/snes9x.h +++ b/source/snes9x/snes9x.h @@ -186,6 +186,8 @@ #include "65c816.h" #include "messages.h" +#define S9X_ACCURACY_LEVEL 3 + #ifdef ZLIB #include #define STREAM gzFile @@ -261,6 +263,8 @@ #define TRACE_FLAG (1 << 1) // debugger #define SINGLE_STEP_FLAG (1 << 2) // debugger #define BREAK_FLAG (1 << 3) // debugger +#define NMI_FLAG (1 << 7) // CPU +#define IRQ_FLAG (1 << 11) // CPU #define SCAN_KEYS_FLAG (1 << 4) // CPU #define HALTED_FLAG (1 << 12) // APU #define FRAME_ADVANCE_FLAG (1 << 9) @@ -270,16 +274,12 @@ struct SCPUState { - uint32 Flags; int32 Cycles; int32 PrevCycles; int32 V_Counter; + uint32 Flags; uint8 *PCBase; - bool8 NMILine; - bool8 IRQLine; - bool8 IRQTransition; - bool8 IRQLastState; - bool8 IRQExternal; + bool8 IRQActive; int32 IRQPending; int32 MemSpeed; int32 MemSpeedx2; @@ -293,6 +293,9 @@ struct SCPUState uint8 WhichEvent; int32 NextEvent; bool8 WaitingForInterrupt; + uint32 WaitAddress; + uint32 WaitCounter; + uint32 PBPCAtOpcodeStart; uint32 AutoSaveTimer; bool8 SRAMModified; }; @@ -300,11 +303,17 @@ struct SCPUState enum { HC_HBLANK_START_EVENT = 1, - HC_HDMA_START_EVENT = 2, - HC_HCOUNTER_MAX_EVENT = 3, - HC_HDMA_INIT_EVENT = 4, - HC_RENDER_EVENT = 5, - HC_WRAM_REFRESH_EVENT = 6 + HC_IRQ_1_3_EVENT = 2, + HC_HDMA_START_EVENT = 3, + HC_IRQ_3_5_EVENT = 4, + HC_HCOUNTER_MAX_EVENT = 5, + HC_IRQ_5_7_EVENT = 6, + HC_HDMA_INIT_EVENT = 7, + HC_IRQ_7_9_EVENT = 8, + HC_RENDER_EVENT = 9, + HC_IRQ_9_A_EVENT = 10, + HC_WRAM_REFRESH_EVENT = 11, + HC_IRQ_A_1_EVENT = 12 }; struct STimings @@ -318,13 +327,12 @@ struct STimings int32 HDMAInit; int32 HDMAStart; int32 NMITriggerPos; - int32 IRQTriggerCycles; int32 WRAMRefreshPos; int32 RenderPos; bool8 InterlaceField; int32 DMACPUSync; // The cycles to synchronize DMA and CPU. Snes9x cannot emulate correctly. int32 NMIDMADelay; // The delay of NMI trigger after DMA transfers. Snes9x cannot emulate correctly. - int32 IRQPendCount; // This value is just a hack. + int32 IRQPendCount; // This value is just a hack, because Snes9x cannot emulate any events in an opcode. int32 APUSpeedup; bool8 APUAllowTimeOverflow; }; @@ -397,8 +405,12 @@ struct SSettings char CartBName[PATH_MAX + 1]; bool8 DisableGameSpecificHacks; + bool8 ShutdownMaster; + bool8 Shutdown; bool8 BlockInvalidVRAMAccessMaster; bool8 BlockInvalidVRAMAccess; + bool8 DisableIRQ; + bool8 DisableHDMA; int32 HDMATimingHack; bool8 ForcedPause;