/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#pragma once
enum AccessMode
{
NONE = 0,
READ = 1,
WRITE = 2,
MODIFY = 3,
JUMP = 5,
JSR = 8
};
inline uint8_t Immediate8Slow(AccessMode a)
{
uint8_t val = S9xGetByte(Registers.PC.xPBPC);
if (a & READ)
OpenBus = val;
++Registers.PC.W.xPC;
return val;
}
inline uint8_t Immediate8(AccessMode a)
{
uint8_t val = CPU.PCBase[Registers.PC.W.xPC];
if (a & READ)
OpenBus = val;
AddCycles(CPU.MemSpeed);
++Registers.PC.W.xPC;
return val;
}
inline uint16_t Immediate16Slow(AccessMode a)
{
uint16_t val = S9xGetWord(Registers.PC.xPBPC, WRAP_BANK);
if (a & READ)
OpenBus = static_cast<uint8_t>(val >> 8);
Registers.PC.W.xPC += 2;
return val;
}
inline uint16_t Immediate16(AccessMode a)
{
uint16_t val = READ_WORD(CPU.PCBase + Registers.PC.W.xPC);
if (a & READ)
OpenBus = static_cast<uint8_t>(val >> 8);
AddCycles(CPU.MemSpeedx2);
Registers.PC.W.xPC += 2;
return val;
}
inline uint32_t RelativeSlow(AccessMode a) // branch $xx
{
int8_t offset = Immediate8Slow(a);
return (static_cast<int16_t>(Registers.PC.W.xPC) + offset) & 0xffff;
}
inline uint32_t Relative(AccessMode a) // branch $xx
{
int8_t offset = Immediate8(a);
return (static_cast<int16_t>(Registers.PC.W.xPC) + offset) & 0xffff;
}
inline uint32_t RelativeLongSlow(AccessMode a) // BRL $xxxx
{
int16_t offset = Immediate16Slow(a);
return (static_cast<int32_t>(Registers.PC.W.xPC) + offset) & 0xffff;
}
inline uint32_t RelativeLong(AccessMode a) // BRL $xxxx
{
int16_t offset = Immediate16(a);
return (static_cast<int32_t>(Registers.PC.W.xPC) + offset) & 0xffff;
}
inline uint32_t AbsoluteIndexedIndirectSlow(AccessMode a) // (a,X)
{
uint16_t addr;
if (a & JSR)
{
// JSR (a,X) pushes the old address in the middle of loading the new.
// OpenBus needs to be set to account for this.
addr = Immediate8Slow(READ);
if (a == JSR)
OpenBus = Registers.PC.B.xPCl;
addr |= Immediate8Slow(READ) << 8;
}
else
addr = Immediate16Slow(READ);
AddCycles(ONE_CYCLE);
addr += Registers.X.W;
// Address load wraps within the bank
uint16_t addr2 = S9xGetWord(ICPU.ShiftedPB | addr, WRAP_BANK);
OpenBus = addr2 >> 8;
return addr2;
}
inline uint32_t AbsoluteIndexedIndirect(AccessMode) // (a,X)
{
uint16_t addr = Immediate16Slow(READ);
AddCycles(ONE_CYCLE);
addr += Registers.X.W;
// Address load wraps within the bank
uint16_t addr2 = S9xGetWord(ICPU.ShiftedPB | addr, WRAP_BANK);
OpenBus = addr2 >> 8;
return addr2;
}
template<typename F> inline uint32_t AbsoluteIndirectLongWrapper(F f, AccessMode)
{
uint16_t addr = f(READ);
// No info on wrapping, but it doesn't matter anyway due to mirroring
uint32_t addr2 = S9xGetWord(addr);
OpenBus = addr2 >> 8;
addr2 |= (OpenBus = S9xGetByte(addr + 2)) << 16;
return addr2;
}
inline uint32_t AbsoluteIndirectLongSlow(AccessMode a) // [a]
{
return AbsoluteIndirectLongWrapper(Immediate16Slow, a);
}
inline uint32_t AbsoluteIndirectLong(AccessMode a) // [a]
{
return AbsoluteIndirectLongWrapper(Immediate16, a);
}
template<typename F> inline uint32_t AbsoluteIndirectWrapper(F f, AccessMode)
{
// No info on wrapping, but it doesn't matter anyway due to mirroring
uint16_t addr2 = S9xGetWord(f(READ));
OpenBus = addr2 >> 8;
return addr2;
}
inline uint32_t AbsoluteIndirectSlow(AccessMode a) // (a)
{
return AbsoluteIndirectWrapper(Immediate16Slow, a);
}
inline uint32_t AbsoluteIndirect(AccessMode a) // (a)
{
return AbsoluteIndirectWrapper(Immediate16, a);
}
template<typename F> inline uint32_t AbsoluteWrapper(F f, AccessMode a)
{
return ICPU.ShiftedDB | f(a);
}
inline uint32_t AbsoluteSlow(AccessMode a) // a
{
return AbsoluteWrapper(Immediate16Slow, a);
}
inline uint32_t Absolute(AccessMode a) // a
{
return AbsoluteWrapper(Immediate16, a);
}
inline uint32_t AbsoluteLongSlow(AccessMode a) // l
{
uint32_t addr = Immediate16Slow(READ);
// JSR l pushes the old bank in the middle of loading the new.
// OpenBus needs to be set to account for this.
if (a == JSR)
OpenBus = Registers.PC.B.xPB;
addr |= Immediate8Slow(a) << 16;
return addr;
}
inline uint32_t AbsoluteLong(AccessMode a) // l
{
uint32_t addr = READ_3WORD(CPU.PCBase + Registers.PC.W.xPC);
AddCycles(CPU.MemSpeedx2 + CPU.MemSpeed);
if (a & READ)
OpenBus = addr >> 16;
Registers.PC.W.xPC += 3;
return addr;
}
template<typename F> inline uint32_t DirectWrapper(F f, AccessMode a)
{
uint16_t addr = f(a) + Registers.D.W;
if (Registers.D.B.l)
AddCycles(ONE_CYCLE);
return addr;
}
inline uint32_t DirectSlow(AccessMode a) // d
{
return DirectWrapper(Immediate8Slow, a);
}
inline uint32_t Direct(AccessMode a) // d
{
return DirectWrapper(Immediate8, a);
}
inline uint32_t DirectIndirectSlow(AccessMode a) // (d)
{
uint32_t addr = S9xGetWord(DirectSlow(READ), !CheckEmulation() || Registers.D.B.l ? WRAP_BANK : WRAP_PAGE);
if (a & READ)
OpenBus = static_cast<uint8_t>(addr >> 8);
addr |= ICPU.ShiftedDB;
return addr;
}
inline uint32_t DirectIndirectE0(AccessMode a) // (d)
{
uint32_t addr = S9xGetWord(Direct(READ));
if (a & READ)
OpenBus = static_cast<uint8_t>(addr >> 8);
addr |= ICPU.ShiftedDB;
return addr;
}
inline uint32_t DirectIndirectE1(AccessMode a) // (d)
{
uint32_t addr = S9xGetWord(DirectSlow(READ), Registers.D.B.l ? WRAP_BANK : WRAP_PAGE);
if (a & READ)
OpenBus = static_cast<uint8_t>(addr >> 8);
addr |= ICPU.ShiftedDB;
return addr;
}
inline uint32_t DirectIndirectIndexedSlow(AccessMode a) // (d),Y
{
uint32_t addr = DirectIndirectSlow(a);
if ((a & WRITE) || !CheckIndex() || (addr & 0xff) + Registers.Y.B.l >= 0x100)
AddCycles(ONE_CYCLE);
return addr + Registers.Y.W;
}
inline uint32_t DirectIndirectIndexedE0X0(AccessMode a) // (d),Y
{
uint32_t addr = DirectIndirectE0(a);
AddCycles(ONE_CYCLE);
return addr + Registers.Y.W;
}
inline uint32_t DirectIndirectIndexedE0X1(AccessMode a) // (d),Y
{
uint32_t addr = DirectIndirectE0(a);
if ((a & WRITE) || (addr & 0xff) + Registers.Y.B.l >= 0x100)
AddCycles(ONE_CYCLE);
return addr + Registers.Y.W;
}
inline uint32_t DirectIndirectIndexedE1(AccessMode a) // (d),Y
{
uint32_t addr = DirectIndirectE1(a);
if ((a & WRITE) || (addr & 0xff) + Registers.Y.B.l >= 0x100)
AddCycles(ONE_CYCLE);
return addr + Registers.Y.W;
}
template<typename F> inline uint32_t DirectIndirectLongWrapper(F f, AccessMode)
{
uint16_t addr = f(READ);
uint32_t addr2 = S9xGetWord(addr);
OpenBus = addr2 >> 8;
addr2 |= (OpenBus = S9xGetByte(addr + 2)) << 16;
return addr2;
}
inline uint32_t DirectIndirectLongSlow(AccessMode a) // [d]
{
return DirectIndirectLongWrapper(DirectSlow, a);
}
inline uint32_t DirectIndirectLong(AccessMode a) // [d]
{
return DirectIndirectLongWrapper(Direct, a);
}
template<typename F> inline uint32_t DirectIndirectIndexedLongWrapper(F f, AccessMode a)
{
return f(a) + Registers.Y.W;
}
inline uint32_t DirectIndirectIndexedLongSlow(AccessMode a) // [d],Y
{
return DirectIndirectIndexedLongWrapper(DirectIndirectLongSlow, a);
}
inline uint32_t DirectIndirectIndexedLong(AccessMode a) // [d],Y
{
return DirectIndirectIndexedLongWrapper(DirectIndirectLong, a);
}
inline uint32_t DirectIndexedXSlow(AccessMode a) // d,X
{
pair addr;
addr.W = DirectSlow(a);
if (!CheckEmulation() || Registers.D.B.l)
addr.W += Registers.X.W;
else
addr.B.l += Registers.X.B.l;
AddCycles(ONE_CYCLE);
return addr.W;
}
inline uint32_t DirectIndexedXE0(AccessMode a) // d,X
{
uint16_t addr = Direct(a) + Registers.X.W;
AddCycles(ONE_CYCLE);
return addr;
}
inline uint32_t DirectIndexedXE1(AccessMode a) // d,X
{
if (Registers.D.B.l)
return DirectIndexedXE0(a);
else
{
pair addr;
addr.W = Direct(a);
addr.B.l += Registers.X.B.l;
AddCycles(ONE_CYCLE);
return addr.W;
}
}
inline uint32_t DirectIndexedYSlow(AccessMode a) // d,Y
{
pair addr;
addr.W = DirectSlow(a);
if (!CheckEmulation() || Registers.D.B.l)
addr.W += Registers.Y.W;
else
addr.B.l += Registers.Y.B.l;
AddCycles(ONE_CYCLE);
return addr.W;
}
inline uint32_t DirectIndexedYE0(AccessMode a) // d,Y
{
uint16_t addr = Direct(a) + Registers.Y.W;
AddCycles(ONE_CYCLE);
return addr;
}
inline uint32_t DirectIndexedYE1(AccessMode a) // d,Y
{
if (Registers.D.B.l)
return DirectIndexedYE0(a);
else
{
pair addr;
addr.W = Direct(a);
addr.B.l += Registers.Y.B.l;
AddCycles(ONE_CYCLE);
return addr.W;
}
}
inline uint32_t DirectIndexedIndirectSlow(AccessMode a) // (d,X)
{
uint32_t addr = S9xGetWord(DirectIndexedXSlow(READ), !CheckEmulation() || Registers.D.B.l ? WRAP_BANK : WRAP_PAGE);
if (a & READ)
OpenBus = static_cast<uint8_t>(addr >> 8);
return ICPU.ShiftedDB | addr;
}
inline uint32_t DirectIndexedIndirectE0(AccessMode a) // (d,X)
{
uint32_t addr = S9xGetWord(DirectIndexedXE0(READ));
if (a & READ)
OpenBus = static_cast<uint8_t>(addr >> 8);
return ICPU.ShiftedDB | addr;
}
inline uint32_t DirectIndexedIndirectE1(AccessMode a) // (d,X)
{
uint32_t addr = S9xGetWord(DirectIndexedXE1(READ), Registers.D.B.l ? WRAP_BANK : WRAP_PAGE);
if (a & READ)
OpenBus = static_cast<uint8_t>(addr >> 8);
return ICPU.ShiftedDB | addr;
}
inline uint32_t AbsoluteIndexedXSlow(AccessMode a) // a,X
{
uint32_t addr = AbsoluteSlow(a);
if ((a & WRITE) || !CheckIndex() || (addr & 0xff) + Registers.X.B.l >= 0x100)
AddCycles(ONE_CYCLE);
return addr + Registers.X.W;
}
inline uint32_t AbsoluteIndexedXX0(AccessMode a) // a,X
{
uint32_t addr = Absolute(a);
AddCycles(ONE_CYCLE);
return addr + Registers.X.W;
}
inline uint32_t AbsoluteIndexedXX1(AccessMode a) // a,X
{
uint32_t addr = Absolute(a);
if ((a & WRITE) || (addr & 0xff) + Registers.X.B.l >= 0x100)
AddCycles(ONE_CYCLE);
return addr + Registers.X.W;
}
inline uint32_t AbsoluteIndexedYSlow(AccessMode a) // a,Y
{
uint32_t addr = AbsoluteSlow(a);
if ((a & WRITE) || !CheckIndex() || (addr & 0xff) + Registers.Y.B.l >= 0x100)
AddCycles(ONE_CYCLE);
return addr + Registers.Y.W;
}
inline uint32_t AbsoluteIndexedYX0(AccessMode a) // a,Y
{
uint32_t addr = Absolute(a);
AddCycles(ONE_CYCLE);
return addr + Registers.Y.W;
}
inline uint32_t AbsoluteIndexedYX1(AccessMode a) // a,Y
{
uint32_t addr = Absolute(a);
if ((a & WRITE) || (addr & 0xff) + Registers.Y.B.l >= 0x100)
AddCycles(ONE_CYCLE);
return addr + Registers.Y.W;
}
template<typename F> inline uint32_t AbsoluteLongIndexedXWrapper(F f, AccessMode a)
{
return f(a) + Registers.X.W;
}
inline uint32_t AbsoluteLongIndexedXSlow(AccessMode a) // l,X
{
return AbsoluteLongIndexedXWrapper(AbsoluteLongSlow, a);
}
inline uint32_t AbsoluteLongIndexedX(AccessMode a) // l,X
{
return AbsoluteLongIndexedXWrapper(AbsoluteLong, a);
}
template<typename F> inline uint32_t StackRelativeWrapper(F f, AccessMode a)
{
uint16_t addr = f(a) + Registers.S.W;
AddCycles(ONE_CYCLE);
return addr;
}
inline uint32_t StackRelativeSlow(AccessMode a) // d,S
{
return StackRelativeWrapper(Immediate8Slow, a);
}
inline uint32_t StackRelative(AccessMode a) // d,S
{
return StackRelativeWrapper(Immediate8, a);
}
template<typename F> inline uint32_t StackRelativeIndirectIndexedWrapper(F f, AccessMode a)
{
uint32_t addr = S9xGetWord(f(READ));
if (a & READ)
OpenBus = static_cast<uint8_t>(addr >> 8);
addr = (addr + Registers.Y.W + ICPU.ShiftedDB) & 0xffffff;
AddCycles(ONE_CYCLE);
return addr;
}
inline uint32_t StackRelativeIndirectIndexedSlow(AccessMode a) // (d,S),Y
{
return StackRelativeIndirectIndexedWrapper(StackRelativeSlow, a);
}
inline uint32_t StackRelativeIndirectIndexed(AccessMode a) // (d,S),Y
{
return StackRelativeIndirectIndexedWrapper(StackRelative, a);
}