Skip to content

Fixed incorrect behavior of SHA, SHX, SHY, and SHS in NesHawk #4309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 35 additions & 36 deletions src/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ static MOS6502X()
/*TYA [implied]*/ new Uop[] { Uop.Imp_TYA, Uop.End },
/*STA addr,Y [absolute indexed WRITE]*/ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_Y, Uop.AbsIdx_Stage4, Uop.AbsIdx_WRITE_Stage5_STA, Uop.End },
/*TXS [implied]*/ new Uop[] { Uop.Imp_TXS, Uop.End },
/*SHS* addr,Y [absolute indexed WRITE Y] [unofficial] */ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_Y, Uop.AbsIdx_Stage4, Uop.AbsIdx_WRITE_Stage5_ERROR, Uop.End },
/*SHS* addr,Y [absolute indexed WRITE Y] [unofficial] */ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_Y, Uop.AbsIdx_Stage4_SHS, Uop.AbsIdx_WRITE_Stage5_SHS, Uop.End },
/*SHY** [absolute indexed WRITE] [unofficial]*/ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_X, Uop.AbsIdx_Stage4_SHY, Uop.AbsIdx_WRITE_Stage5_SHY, Uop.End },
/*STA addr,X [absolute indexed WRITE]*/ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_X, Uop.AbsIdx_Stage4, Uop.AbsIdx_WRITE_Stage5_STA, Uop.End },
/*SHX* addr,Y [absolute indexed WRITE Y] [unofficial]*/ new Uop[] { Uop.Fetch2, Uop.AbsIdx_Stage3_Y, Uop.AbsIdx_Stage4_SHX, Uop.AbsIdx_WRITE_Stage5_SHX, Uop.End },
Expand Down Expand Up @@ -416,7 +416,7 @@ private enum Uop
//[absolute indexed WRITE]
AbsIdx_WRITE_Stage5_STA,
AbsIdx_WRITE_Stage5_SHY, AbsIdx_WRITE_Stage5_SHX, //unofficials
AbsIdx_WRITE_Stage5_ERROR,
AbsIdx_WRITE_Stage5_SHS,
//[absolute indexed READ]
AbsIdx_READ_Stage4,
AbsIdx_READ_Stage5_LDA, AbsIdx_READ_Stage5_CMP, AbsIdx_READ_Stage5_SBC, AbsIdx_READ_Stage5_ADC, AbsIdx_READ_Stage5_EOR, AbsIdx_READ_Stage5_LDX, AbsIdx_READ_Stage5_AND, AbsIdx_READ_Stage5_ORA, AbsIdx_READ_Stage5_LDY, AbsIdx_READ_Stage5_NOP,
Expand Down Expand Up @@ -474,7 +474,8 @@ private enum Uop
IndIdx_WRITE_Stage5_SHA,
AbsIdx_Stage4_SHX,
AbsIdx_Stage4_SHY,
AbsIdx_Stage4_SHA
AbsIdx_Stage4_SHA,
AbsIdx_Stage4_SHS
}

private void InitOpcodeHandlers()
Expand Down Expand Up @@ -524,6 +525,7 @@ private void InitOpcodeHandlers()
public int mi; //microcode index
private bool iflag_pending; //iflag must be stored after it is checked in some cases (CLI and SEI).
public bool rdy_freeze; //true if the CPU must be frozen
private byte H; //internal temp variable used in the "unstable high byte group" of unofficial instructions

//tracks whether an interrupt condition has popped up recently.
//not sure if this is real or not but it helps with the branch_irq_hack
Expand Down Expand Up @@ -957,6 +959,7 @@ private void IndIdx_WRITE_Stage5()
private void IndIdx_WRITE_Stage5_SHA()
{
rdy_freeze = !RDY;
H = (byte) ((ea >> 8) + 1);
if (RDY)
{
_link.ReadMemory((ushort) ea);
Expand All @@ -965,8 +968,6 @@ private void IndIdx_WRITE_Stage5_SHA()
{
ea = (ushort) (ea & 0xFF | ((ea + 0x100) & 0xFF00 & ((A & X) << 8)));
}

ea += unchecked((ushort) (alu_temp & 0xFF00));
}
}

Expand Down Expand Up @@ -1008,13 +1009,7 @@ private void IndIdx_WRITE_Stage6_STA()

private void IndIdx_WRITE_Stage6_SHA()
{
alu_temp = A & X;

if (RDY)
{
alu_temp &= unchecked((byte) ((ea >> 8) + 1));
}

alu_temp = A & X & H;
_link.WriteMemory((ushort) ea, unchecked((byte) alu_temp));
}

Expand Down Expand Up @@ -2490,6 +2485,7 @@ private void AbsIdx_READ_Stage4()
private void AbsIdx_Stage4()
{
rdy_freeze = !RDY;
H = (byte) ((ea >> 8) + 1);

if (RDY)
{
Expand All @@ -2506,6 +2502,7 @@ private void AbsIdx_Stage4()
private void AbsIdx_Stage4_SHX()
{
rdy_freeze = !RDY;
H = (byte) ((ea >> 8) + 1);

if (RDY)
{
Expand All @@ -2522,6 +2519,7 @@ private void AbsIdx_Stage4_SHX()
private void AbsIdx_Stage4_SHY()
{
rdy_freeze = !RDY;
H = (byte) ((ea >> 8) + 1);

if (RDY)
{
Expand All @@ -2538,6 +2536,24 @@ private void AbsIdx_Stage4_SHY()
private void AbsIdx_Stage4_SHA()
{
rdy_freeze = !RDY;
H = (byte)((ea >> 8) + 1);

if (RDY)
{
var adjust = alu_temp.Bit(8);
alu_temp = _link.ReadMemory((ushort) ea);

if (adjust)
{
ea = (ushort) (ea & 0xFF | ((ea + 0x100) & 0xFF00 & ((A & X) << 8)));
}
}
}

private void AbsIdx_Stage4_SHS()
{
rdy_freeze = !RDY;
H = (byte) ((ea >> 8) + 1);

if (RDY)
{
Expand All @@ -2558,44 +2574,26 @@ private void AbsIdx_WRITE_Stage5_STA()

private void AbsIdx_WRITE_Stage5_SHY()
{
alu_temp = Y;

if (RDY)
{
alu_temp &= unchecked((byte)((ea >> 8) + 1));
}

alu_temp = Y & H;
_link.WriteMemory((ushort) ea, (byte) alu_temp);
}

private void AbsIdx_WRITE_Stage5_SHX()
{
alu_temp = X;

if (RDY)
{
alu_temp &= unchecked((byte)((ea >> 8) + 1));
}

alu_temp = X & H;
_link.WriteMemory((ushort) ea, (byte) alu_temp);
}

private void AbsIdx_WRITE_Stage5_SHA()
{
alu_temp = A & X;

if (RDY)
{
alu_temp &= unchecked((byte)((ea >> 8) + 1));
}

alu_temp = A & X & H;
_link.WriteMemory((ushort) ea, (byte) alu_temp);
}

private void AbsIdx_WRITE_Stage5_ERROR()
private void AbsIdx_WRITE_Stage5_SHS()
{
S = (byte)(X & A);
_link.WriteMemory((ushort)ea, (byte)(S & (opcode3+1)));
_link.WriteMemory((ushort)ea, (byte)(S & H));
}

private void AbsIdx_RMW_Stage5()
Expand Down Expand Up @@ -3225,11 +3223,12 @@ private void ExecuteOneRetry()
case Uop.AbsIdx_Stage4_SHX: AbsIdx_Stage4_SHX(); break;
case Uop.AbsIdx_Stage4_SHY: AbsIdx_Stage4_SHY(); break;
case Uop.AbsIdx_Stage4_SHA: AbsIdx_Stage4_SHA(); break;
case Uop.AbsIdx_Stage4_SHS: AbsIdx_Stage4_SHS(); break;
case Uop.AbsIdx_WRITE_Stage5_STA: AbsIdx_WRITE_Stage5_STA(); break;
case Uop.AbsIdx_WRITE_Stage5_SHY: AbsIdx_WRITE_Stage5_SHY(); break;
case Uop.AbsIdx_WRITE_Stage5_SHX: AbsIdx_WRITE_Stage5_SHX(); break;
case Uop.AbsIdx_WRITE_Stage5_SHA: AbsIdx_WRITE_Stage5_SHA(); break;
case Uop.AbsIdx_WRITE_Stage5_ERROR: AbsIdx_WRITE_Stage5_ERROR(); break;
case Uop.AbsIdx_WRITE_Stage5_SHS: AbsIdx_WRITE_Stage5_SHS(); break;
case Uop.AbsIdx_RMW_Stage5: AbsIdx_RMW_Stage5(); break;
case Uop.AbsIdx_RMW_Stage7: AbsIdx_RMW_Stage7(); break;
case Uop.AbsIdx_RMW_Stage6_DEC: AbsIdx_RMW_Stage6_DEC(); break;
Expand Down