private void Set(byte value, string instr, AddrMode addressMode, int cycles) { var op = new OpcodeInfo(); op.Instruction = instr; op.AddressMode = addressMode; op.Cycles = cycles; if (Opcodes[value] != null) { throw new Exception("opcode " + value + " already assigned"); } Opcodes[value] = op; }
// TODO: Don't use static method, create en interface and inject in consumer. public static ulong CalculateExtraCycles(AddrMode addrMode, bool addressCalculationCrossedPageBoundary) { ulong extraCycles = 0; if (addressCalculationCrossedPageBoundary && (addrMode == AddrMode.ABS_X || addrMode == AddrMode.ABS_Y || addrMode == AddrMode.IND_IX )) { extraCycles++; } return(extraCycles); }
protected void EmitDictionaryLookup(NodeFactory factory, ref X64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) { // INVARIANT: must not trash context register // Find the generic dictionary slot int dictionarySlot = 0; if (!relocsOnly) { // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup); } // Load the generic dictionary cell AddrMode loadEntry = new AddrMode( context, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitMOV(result, ref loadEntry); // If there's any invalid entries, we need to test for them // // Only do this in relocsOnly to make it easier to weed out bugs - the _hasInvalidEntries // flag can change over the course of compilation and the bad slot helper dependency // should be reported by someone else - the system should not rely on it coming from here. if (!relocsOnly && _hasInvalidEntries) { encoder.EmitCompareToZero(result); encoder.EmitJE(GetBadSlotHelper(factory)); } switch (lookup.LookupResultReferenceType(factory)) { case GenericLookupResultReferenceType.Indirect: // Do another indirection loadEntry = new AddrMode(result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(result, ref loadEntry); break; case GenericLookupResultReferenceType.ConditionalIndirect: // Test result, 0x1 // JEQ L1 // mov result, [result-1] // L1: throw new NotImplementedException(); default: break; } }
protected void EmitDictionaryLookup(NodeFactory factory, ref X64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) { // INVARIANT: must not trash context register // Find the generic dictionary slot int dictionarySlot = 0; if (!relocsOnly) { // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup); } // Load the generic dictionary cell AddrMode loadEntry = new AddrMode( context, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitMOV(result, ref loadEntry); }
public byte LSR() { Fetch(); SetFlag(FLAGS6502.C, fetched & 0x0001); temp = (ushort)(fetched >> 1); SetFlag(FLAGS6502.Z, (temp & 0x00FF) == 0x0000); SetFlag(FLAGS6502.N, temp & 0x0080); AddrMode addr = new AddrMode(IMP); if (instructions[opcode].addrMode.Equals(addr)) { a = (byte)(temp & 0x00FF); } else { Write(addr_abs, (byte)(temp & 0x00FF)); } return(0); }
protected override void EmitLoadGenericContext(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { // We start with Arg0 pointing to the EEType // Locate the VTable slot that points to the dictionary int vtableSlot = 0; if (!relocsOnly) { // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)_dictionaryOwner); } int pointerSize = factory.Target.PointerSize; int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); // Load the dictionary pointer from the VTable AddrMode loadDictionary = new AddrMode(encoder.TargetRegister.Arg0, null, slotOffset, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadDictionary); }
protected void EmitDictionaryLookup(NodeFactory factory, ref X64Emitter encoder, Register context, Register result, GenericLookupResult lookup, bool relocsOnly) { // INVARIANT: must not trash context register // Find the generic dictionary slot int dictionarySlot = 0; if (!relocsOnly) { // The concrete slot won't be known until we're emitting data - don't ask for it in relocsOnly. dictionarySlot = factory.GenericDictionaryLayout(_dictionaryOwner).GetSlotForEntry(lookup); } // Load the generic dictionary cell AddrMode loadEntry = new AddrMode( context, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitMOV(result, ref loadEntry); switch (lookup.LookupResultReferenceType(factory)) { case GenericLookupResultReferenceType.Indirect: // Do another indirection loadEntry = new AddrMode(result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(result, ref loadEntry); break; case GenericLookupResultReferenceType.ConditionalIndirect: // Test result, 0x1 // JEQ L1 // mov result, [result-1] // L1: throw new NotImplementedException(); default: break; } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int pointerSize = factory.Target.PointerSize; int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; // TODO: Swap argument order instead encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target), hasLazyStaticConstructor ? NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target) : 0); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: encoder.EmitINT3(); break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.Target); if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot); encoder.EmitRET(); } } break; case ReadyToRunHelperId.GenericLookupFromThis: { int pointerSize = factory.Target.PointerSize; var lookupInfo = (GenericLookupDescriptor)Target; // Arg0 points to the EEType // Locate the VTable slot that points to the dictionary int vtableSlot = 0; if (!relocsOnly) { vtableSlot = VirtualMethodSlotHelper.GetGenericDictionarySlot(factory, (TypeDesc)lookupInfo.CanonicalOwner); } int slotOffset = EETypeNode.GetVTableOffset(pointerSize) + (vtableSlot * pointerSize); // Load the dictionary pointer from the VTable AddrMode loadDictionary = new AddrMode(encoder.TargetRegister.Arg0, null, slotOffset, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadDictionary); // What's left now is the actual dictionary lookup goto case ReadyToRunHelperId.GenericLookupFromDictionary; } case ReadyToRunHelperId.GenericLookupFromDictionary: { var lookupInfo = (GenericLookupDescriptor)Target; // Find the generic dictionary slot int dictionarySlot = 0; if (!relocsOnly) { // The concrete slot won't be known until we're emitting data. dictionarySlot = factory.GenericDictionaryLayout(lookupInfo.CanonicalOwner).GetSlotForEntry(lookupInfo.Signature); } // Load the generic dictionary cell AddrMode loadEntry = new AddrMode( encoder.TargetRegister.Arg0, null, dictionarySlot * factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadEntry); encoder.EmitRET(); } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X86Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.VirtualCall: { encoder.EmitINT3(); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target); encoder.EmitMOV(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitMOV(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { encoder.EmitINT3(); } break; case ReadyToRunHelperId.GetGCStaticBase: { encoder.EmitINT3(); } break; case ReadyToRunHelperId.DelegateCtor: { encoder.EmitINT3(); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { encoder.EmitINT3(); } break; default: throw new NotImplementedException(); } }
public void Execute_And_Verify( AddrMode addrMode , bool ZP_X_Should_Wrap_Over_Byte = false , bool ZP_Y_Should_Wrap_Over_Byte = false , bool FullAddress_Should_Cross_Page_Boundary = false ) { if (!InsEffect.HasValue) { InsEffect = InstrEffect.Reg; } if (addrMode == AddrMode.Accumulator && FinalValue.HasValue) { throw new DotNet6502Exception($"If {nameof(AddrMode)} is {nameof(AddrMode.Accumulator)}, {nameof(FinalValue)} cannot be used."); } if (addrMode == AddrMode.Implied && FinalValue.HasValue) { throw new DotNet6502Exception($"If {nameof(addrMode)} is {nameof(AddrMode.Implied)}, {nameof(FinalValue)} cannot be used."); } if (addrMode != AddrMode.Indirect && FinalValueWord.HasValue) { throw new DotNet6502Exception($"If {nameof(AddrMode)} is other than {nameof(AddrMode.Indirect)}, {nameof(FinalValueWord)} cannot be used."); } if (InsEffect == InstrEffect.Reg) { if (ExpectedMemVal.HasValue) { throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Reg)}, {nameof(ExpectedMemVal)} is not supposed to be set (only used for comparing memory address changed by write)"); } if (addrMode == AddrMode.Accumulator && (ExpectedX.HasValue || ExpectedY.HasValue)) { throw new DotNet6502Exception($"If {nameof(addrMode)} is {nameof(AddrMode.Accumulator)}, {nameof(ExpectedX)} or {nameof(ExpectedY)} cannot be used, because addressing mode {nameof(AddrMode.Accumulator)} only can affect A register."); } if (ExpectedA.HasValue && !A.HasValue) { throw new DotNet6502Exception($"If {nameof(ExpectedA)} is set, {nameof(A)} must be set to an initial value"); } if (ExpectedX.HasValue && !X.HasValue) { throw new DotNet6502Exception($"If {nameof(ExpectedX)} is set, {nameof(X)} must be set to an initial value"); } if (ExpectedY.HasValue && !Y.HasValue) { throw new DotNet6502Exception($"If {nameof(ExpectedY)} is set, {nameof(Y)} must be set to an initial value"); } } else if (InsEffect == InstrEffect.Mem) { if (addrMode == AddrMode.Implied) { throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(addrMode)} {nameof(AddrMode.Implied)} cannot be used."); } if (addrMode == AddrMode.Accumulator) { throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(addrMode)} {nameof(AddrMode.Accumulator)} cannot be used."); } if (ExpectedMemVal.HasValue && addrMode == AddrMode.I) { throw new DotNet6502Exception($"{nameof(ExpectedMemVal)} cannot be used with addressing mode {nameof(AddrMode.I)}"); } if (ExpectedMemVal.HasValue && addrMode == AddrMode.Accumulator) { throw new DotNet6502Exception($"{nameof(ExpectedMemVal)} cannot be used with addressing mode {nameof(AddrMode.Accumulator)}"); } if (ExpectedMemVal.HasValue & !FinalValue.HasValue) { throw new DotNet6502Exception($"If {nameof(ExpectedMemVal)} is set, {nameof(FinalValue)} must also be set to an initial value that the memory will have before the instruction executes"); } if (ExpectedA.HasValue) { throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(ExpectedA)} is not supposed to be set (only used for comparing register change after instruction executed)"); } if (ExpectedX.HasValue) { throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(ExpectedX)} is not supposed to be set (only used for comparing register change after instruction executed)"); } if (ExpectedY.HasValue) { throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.Mem)}, {nameof(ExpectedY)} is not supposed to be set (only used for comparing register change after instruction executed)"); } } else if (InsEffect == InstrEffect.RegAndMem) { if (addrMode == AddrMode.Implied) { throw new DotNet6502Exception($"If {nameof(InsEffect)} is {nameof(InstrEffect.RegAndMem)}, {nameof(addrMode)} {nameof(AddrMode.Implied)} cannot be used."); } } // If processor flags aren't configured to be initialized by test, we automatically set them to the opposite of what was defined as the expected result if (ExpectedC.HasValue && !C.HasValue) { C = !ExpectedC.Value; } if (ExpectedZ.HasValue && !Z.HasValue) { Z = !ExpectedZ.Value; } if (ExpectedI.HasValue && !I.HasValue) { I = !ExpectedI.Value; } if (ExpectedD.HasValue && !D.HasValue) { D = !ExpectedZ.Value; } if (ExpectedB.HasValue && !B.HasValue) { B = !ExpectedB.Value; } if (ExpectedU.HasValue && !U.HasValue) { U = !ExpectedU.Value; } if (ExpectedV.HasValue && !V.HasValue) { V = !ExpectedV.Value; } if (ExpectedN.HasValue && !N.HasValue) { N = !ExpectedN.Value; } // Shorthand variables to cpu and memory var computer = TestContext.Computer; var cpu = computer.CPU; var mem = computer.Mem; // Init Program Counter if (PC.HasValue) { cpu.PC = PC.Value; } // We will start writing the instruction, and then operand, starting at the specified PC address var codeMemPos = cpu.PC; // Write instruction at start address mem.WriteByte(ref codeMemPos, OpCode); ushort ZPAddressX; ushort ZPAddressY; ushort fullAddressX; ushort fullAddressY; ushort?finalAddressUsed = null; switch (addrMode) { case AddrMode.Implied: // Implied instructions does not have operand. The complete instruction takes 1 byte. break; case AddrMode.Accumulator: // Accumulator addressing mode means operate on A register (instead of memory). No instruction operand is used. break; case AddrMode.Indirect: // JMP is the only instruction that uses Indirect addressing. // The instruction contains a 16 bit address which identifies the location of the least significant byte // of another 16 bit memory address which is the real target of the instruction. // Define memory address the instruction should use if (!FullAddress.HasValue) { FullAddress = 0xab12; } finalAddressUsed = FullAddress.Value; // Initialize memory the instruction should read or write to if (!FinalValueWord.HasValue) { FinalValueWord = 0x1265; // If not specified, initialize default value to be at final memory location } mem.WriteWord(finalAddressUsed.Value, FinalValueWord.Value); // Write instruction operand mem.WriteWord(ref codeMemPos, FullAddress.Value); break; case AddrMode.Relative: // Relative addressing mode is used by branching instruction such as BEQ and BNE. if (!FinalValue.HasValue) { FinalValue = 0xd0; // If not specified, initialize default value to be at final relative memory location to branch to } mem.WriteByte(ref codeMemPos, FinalValue.Value); break; case AddrMode.I: // Initialize memory the instruction should read or write to if (!FinalValue.HasValue) { FinalValue = 0x12; // If not specified, initialize default value to be at final memory location } mem.WriteByte(ref codeMemPos, FinalValue.Value); break; case AddrMode.ZP: // To avoid testing mistakes, setting up a test as ZP (parameter AddrMode), when the instruction being run (property Instruction) is ZP_X or ZP_Y, // we initialize registers that should not be used with ZP to non-default values. if (!X.HasValue) { X = 111; } if (!Y.HasValue) { Y = 15; } // Define memory address the instruction should calculate if (!ZeroPageAddress.HasValue) { ZeroPageAddress = 0x0010; } finalAddressUsed = ZeroPageAddress.Value; if (!FinalValue.HasValue) { FinalValue = 0x12; // If not specified, initialize default value to be at final memory location } // Initialize memory the instruction should read or write to mem.WriteByte(finalAddressUsed.Value, FinalValue.Value); // Write instruction operand mem.WriteByte(ref codeMemPos, ZeroPageAddress.Value.Lowbyte()); // Only least significant byte of the address is used in the instruction. break; case AddrMode.ZP_X: // Define memory address the instruction should calculate if (!ZeroPageAddress.HasValue) { ZeroPageAddress = 0x0010; } // Use a default value for X index if not specified in test if (!X.HasValue) { if (!ZP_X_Should_Wrap_Over_Byte) { X = 0x05; } else { X = 0xf5; // Force final ZP+X address bigger than one byte (0x0010 + 0xf5 = 0x0105) } } // Calculate ZeroPage + X address ZPAddressX = (ushort)(ZeroPageAddress + X); // Adjust that we expect the final address to wrap when getting larger than a byte if (ZP_X_Should_Wrap_Over_Byte) { ZPAddressX = (ushort)(ZPAddressX & 0xff); } finalAddressUsed = ZPAddressX; // Initialize memory the instruction should read or write to if (!FinalValue.HasValue) { FinalValue = 0x12; // If not specified, initialize default value to be at final memory location } mem.WriteByte(finalAddressUsed.Value, FinalValue.Value); // Write instruction operand mem.WriteByte(ref codeMemPos, ZeroPageAddress.Value.Lowbyte()); // Only least significant byte of the address is used in the instruction. break; case AddrMode.ZP_Y: // Define memory address the instruction should calculate if (!ZeroPageAddress.HasValue) { ZeroPageAddress = 0x0010; } // Use a default value for Y index if not specified in test if (!Y.HasValue) { if (!ZP_Y_Should_Wrap_Over_Byte) { Y = 0x05; } else { Y = 0xf5; // Force final ZP+Y address bigger than one byte (0x0010 + 0xf5 = 0x0105) } } // Calculate ZeroPage + Y address ZPAddressY = (ushort)(ZeroPageAddress + Y); // Adjust that we expect the final address to wrap when getting larger than a byte if (ZP_Y_Should_Wrap_Over_Byte) { ZPAddressY = (ushort)(ZPAddressY & 0xff); } finalAddressUsed = ZPAddressY; // Initialize memory the instruction should read or write to if (!FinalValue.HasValue) { FinalValue = 0x12; // If not specified, initialize default value to be at final memory location } mem.WriteByte(finalAddressUsed.Value, FinalValue.Value); // Write instruction operand mem.WriteByte(ref codeMemPos, ZeroPageAddress.Value.Lowbyte()); // Only least significant byte of the address is used in the instruction. break; case AddrMode.ABS: // To avoid testing mistakes, setting up a test as ABS (parameter AddrMode), when the instruction being run (property Instruction) is ABX_X or ABS_Y, // we initialize registers that should not be used with Absolute addressing to non-default values. if (!X.HasValue) { X = 60; } if (!Y.HasValue) { Y = 100; } // Define memory address the instruction should use if (!FullAddress.HasValue) { FullAddress = 0xab12; } finalAddressUsed = FullAddress.Value; // Initialize memory the instruction should read or write to if (!FinalValue.HasValue) { FinalValue = 0x12; // If not specified, initialize default value to be at final memory location } mem.WriteByte(finalAddressUsed.Value, FinalValue.Value); // Write instruction operand mem.WriteWord(ref codeMemPos, FullAddress.Value); break; case AddrMode.ABS_X: // Define memory address the instruction should use if (!FullAddress.HasValue) { FullAddress = 0xab12; } // Use a default value for X index if not specified in test if (!X.HasValue) { if (!FullAddress_Should_Cross_Page_Boundary) { X = 0x05; } else { X = 0xf0; // Force final FullAddress+X address crosses page boundary (0xab12 + 0xf0 = 0xac02) } } // Calculate final address with X offset fullAddressX = (ushort)(FullAddress + X); finalAddressUsed = fullAddressX; // Initialize memory the instruction should read or write to if (!FinalValue.HasValue) { FinalValue = 0x12; // If not specified, initialize default value to be at final memory location } mem.WriteByte(finalAddressUsed.Value, FinalValue.Value); // Write instruction operand mem.WriteWord(ref codeMemPos, FullAddress.Value); break; case AddrMode.ABS_Y: // Define memory address the instruction should use if (!FullAddress.HasValue) { FullAddress = 0xab12; } // Use a default value for X index if not specified in test if (!Y.HasValue) { if (!FullAddress_Should_Cross_Page_Boundary) { Y = 0x05; } else { Y = 0xf0; // Force final FullAddress+X address crosses page boundary (0xab12 + 0xf0 = 0xac02) } } // Calculate final address with X offset fullAddressY = (ushort)(FullAddress + Y); finalAddressUsed = fullAddressY; // Initialize memory the instruction should read or write to if (!FinalValue.HasValue) { FinalValue = 0x12; // If not specified, initialize default value to be at final memory location } mem.WriteByte(finalAddressUsed.Value, FinalValue.Value); // Write instruction operand mem.WriteWord(ref codeMemPos, FullAddress.Value); break; case AddrMode.IX_IND: // Define memory address the instruction should use if (!FullAddress.HasValue) { FullAddress = 0xab12; } // Use a default value for X index if not specified in test if (!X.HasValue) { if (!ZP_X_Should_Wrap_Over_Byte) { X = 0x05; } else { X = 0xf0; // Force final ZeroPage address +X to wrap around a byte (0x20 + 0xf0 = 0x10) } } //Calculate locations used to calculate the actual address to read from (fullAddress) // index_indirect_base_address = zero page address if (!ZeroPageAddress.HasValue) { ZeroPageAddress = 0x20; } // We should allways wrap around indirect ZeroPage address + X if exceeds one byte. Can be be done by truncating address to byte. ushort index_indirect_zeropage_address = (byte)(ZeroPageAddress + X); // Initialize zero page address + X the instruction will read final address from mem.WriteWord(index_indirect_zeropage_address, FullAddress.Value); finalAddressUsed = FullAddress.Value; // Initialize final memory the instruction should read or write to if (!FinalValue.HasValue) { FinalValue = 0x12; // If not specified, initialize default value to be at final memory location } mem.WriteByte(finalAddressUsed.Value, FinalValue.Value); // Write instruction operand mem.WriteByte(ref codeMemPos, (byte)ZeroPageAddress.Value); break; case AddrMode.IND_IX: // Initialize memory we will read from // We calculate a full address to store in a zero page address, adjusted by -Y (because the instruction will add Y when it retrieves it) // If we want to force crossing page boundary FullAddress + Y, we will hard code FullAddress and Y values if (FullAddress_Should_Cross_Page_Boundary) { // The address to be found in zero page address FullAddress = 0xab12; // Adding Y to the address found in zero page will cross page boundary Y = 0xff; } // Use a default value for FullAddress if not specified in test if (!FullAddress.HasValue) { FullAddress = 0xab12; } // Use a default value for Y index if not specified in test if (!Y.HasValue) { Y = 0x05; } //Calculate locations used to calculate the actual address to read from (fullAddress) // We use ZeroPage address as the "indirect indexed" zeropage address if (!ZeroPageAddress.HasValue) { ZeroPageAddress = 0x86; } // The address to be found in the zero page address should be the final address - the Y register. ushort address_at_zeropage_address = (ushort)(FullAddress - Y); // Initialize indirect indexed zero page address mem.WriteWord(ZeroPageAddress.Value, address_at_zeropage_address); finalAddressUsed = FullAddress.Value; // Initialize final memory the instruction should read or write to if (!FinalValue.HasValue) { FinalValue = 0x12; // If not specified, initialize default value to be at final memory location } mem.WriteByte(finalAddressUsed.Value, FinalValue.Value); // Write instruction operand mem.WriteByte(ref codeMemPos, (byte)ZeroPageAddress.Value); break; default: throw new DotNet6502Exception($"Unhandled addressing mode: {addrMode}"); } // Before we execute intruction, verify internal consistency of number of bytes the OpCode takes by our test-setup code above // with the value specified in OpCode.Bytes class. Assert.Equal(cpu.PC + cpu.InstructionList.GetOpCode(OpCode.ToByte()).Size, codeMemPos); // Init registers and flags if (A.HasValue) { cpu.A = A.Value; } if (X.HasValue) { cpu.X = X.Value; } if (Y.HasValue) { cpu.Y = Y.Value; } if (SP.HasValue) { cpu.SP = SP.Value; } if (C.HasValue) { cpu.ProcessorStatus.Carry = C.Value; } if (Z.HasValue) { cpu.ProcessorStatus.Zero = Z.Value; } if (I.HasValue) { cpu.ProcessorStatus.InterruptDisable = I.Value; } if (D.HasValue) { cpu.ProcessorStatus.Decimal = D.Value; } if (B.HasValue) { cpu.ProcessorStatus.Break = B.Value; } if (U.HasValue) { cpu.ProcessorStatus.Unused = U.Value; } if (V.HasValue) { cpu.ProcessorStatus.Overflow = V.Value; } if (N.HasValue) { cpu.ProcessorStatus.Negative = N.Value; } // Act var execOptions = new ExecOptions(); if (ExpectedCycles.HasValue) { execOptions.CyclesRequested = ExpectedCycles.Value; execOptions.MaxNumberOfInstructions = 1; } else { execOptions.CyclesRequested = null; execOptions.MaxNumberOfInstructions = 1; } var thisExecState = cpu.Execute(mem, execOptions); // Assert // Check that we didn't find any unknown opcode Assert.True(thisExecState.UnknownOpCodeCount == 0); // Verify Program Counter if (ExpectedPC.HasValue) { Assert.Equal(ExpectedPC.Value, cpu.PC); } else { // If no PC check has been defined, by default verify PC has been moved forward as many bytes we used up for the instruction. // But skip default verification if be used a branching or jump instruction // -- All that uses Relative addressing mode // -- Some other instructions in Implied addressing mode // -- JMP instructions // -- JSR instruction // if (addrMode != AddrMode.Relative && OpCode != OpCodeId.BRK && // Implied addressing mode OpCode != OpCodeId.RTS && // Implied addressing mode OpCode != OpCodeId.RTI && // Implied addressing mode OpCode != OpCodeId.JMP_IND && // Indirect addressing mode OpCode != OpCodeId.JMP_ABS && // Absolute addressing mode OpCode != OpCodeId.JSR // Absolute addressing mode ) { Assert.Equal(codeMemPos, cpu.PC); } } // Verify expected # of cycles if (ExpectedCycles.HasValue) { Assert.Equal(ExpectedCycles, thisExecState.CyclesConsumed); } // Verify registers (operations that affects registers) if (InsEffect == InstrEffect.Reg || InsEffect == InstrEffect.RegAndMem) { if (ExpectedA.HasValue) { Assert.Equal(ExpectedA.Value, cpu.A); } if (ExpectedX.HasValue) { Assert.Equal(ExpectedX.Value, cpu.X); } if (ExpectedY.HasValue) { Assert.Equal(ExpectedY.Value, cpu.Y); } } // Verify affected memory (operations that affect memory) else if (InsEffect == InstrEffect.Mem || InsEffect == InstrEffect.RegAndMem) { if (!finalAddressUsed.HasValue) { throw new DotNet6502Exception($"Incorrect use of TestSpec class, or bug in TestSpec class. Is correct addressing mode being tested? Variable {nameof(finalAddressUsed)} must be initialized in all addressing mode code paths that can modify memory"); } if (ExpectedMemVal.HasValue) { Assert.Equal(ExpectedMemVal.Value, mem[finalAddressUsed.Value]); } } // Verify stack pointer if (ExpectedSP.HasValue) { Assert.Equal(ExpectedSP.Value, cpu.SP); } // Verify status flags if (ExpectedC.HasValue) { Assert.Equal(ExpectedC.Value, cpu.ProcessorStatus.Carry); } if (ExpectedZ.HasValue) { Assert.Equal(ExpectedZ.Value, cpu.ProcessorStatus.Zero); } if (ExpectedI.HasValue) { Assert.Equal(ExpectedI.Value, cpu.ProcessorStatus.InterruptDisable); } if (ExpectedD.HasValue) { Assert.Equal(ExpectedD.Value, cpu.ProcessorStatus.Decimal); } if (ExpectedB.HasValue) { Assert.Equal(ExpectedB.Value, cpu.ProcessorStatus.Break); } if (ExpectedV.HasValue) { Assert.Equal(ExpectedV.Value, cpu.ProcessorStatus.Overflow); } if (ExpectedN.HasValue) { Assert.Equal(ExpectedN.Value, cpu.ProcessorStatus.Negative); } }
public MemImmediate(RegSP Base = default, AddrMode Mode = default, int imm = default) { this.Base = Base; this.Mode = Mode; this.imm = imm; }
public bool SupportsAddressingMode(AddrMode mode) { return(OpCodes.Exists(x => x.AddressingMode == mode)); }
protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { // First load the generic context into the context register. EmitLoadGenericContext(factory, ref encoder, relocsOnly); Register contextRegister = GetContextRegister(ref encoder); switch (_id) { case ReadyToRunHelperId.GetNonGCStaticBase: { Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); MetadataType target = (MetadataType)_target; if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); encoder.EmitRET(); } else { EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, _lookupSignature, relocsOnly); encoder.EmitMOV(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0); // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64); encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetGCStaticBase: { Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); MetadataType target = (MetadataType)_target; EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); AddrMode loadFromResult = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, nonGcRegionLookup, relocsOnly); int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64); encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { Debug.Assert(contextRegister == encoder.TargetRegister.Arg0); MetadataType target = (MetadataType)_target; // Look up the index cell EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); ISymbolNode helperEntrypoint; if (factory.TypeSystemContext.HasLazyStaticConstructor(target)) { // There is a lazy class constructor. We need the non-GC static base because that's where the // class constructor context lives. GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg2, null, -cctorContextSize, 0, AddrModeSize.Int64); encoder.EmitLEA(encoder.TargetRegister.Arg2, ref loadCctor); helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase); } else { helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); } // First arg: address of the TypeManager slot that provides the helper with // information about module index and the type manager instance (which is used // for initialization on first access). AddrMode loadFromArg1 = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg1); // Second arg: index of the type in the ThreadStatic section of the modules AddrMode loadFromArg1AndDelta = new AddrMode(encoder.TargetRegister.Arg1, null, factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg1AndDelta); encoder.EmitJMP(helperEntrypoint); } break; case ReadyToRunHelperId.DelegateCtor: { // This is a weird helper. Codegen populated Arg0 and Arg1 with the values that the constructor // method expects. Codegen also passed us the generic context in Arg2. // We now need to load the delegate target method into Arg2 (using a dictionary lookup) // and the optional 4th parameter, and call the ctor. Debug.Assert(contextRegister == encoder.TargetRegister.Arg2); var target = (DelegateCreationInfo)_target; EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg2, encoder.TargetRegister.Arg2, _lookupSignature, relocsOnly); if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; // These are all simple: just get the thing from the dictionary and we're done case ReadyToRunHelperId.TypeHandle: case ReadyToRunHelperId.MethodHandle: case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.MethodEntry: case ReadyToRunHelperId.VirtualDispatchCell: case ReadyToRunHelperId.DefaultConstructor: case ReadyToRunHelperId.ObjectAllocator: case ReadyToRunHelperId.TypeHandleForCasting: { EmitDictionaryLookup(factory, ref encoder, contextRegister, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); encoder.EmitRET(); } break; default: throw new NotImplementedException(); } }
private void Set(byte value, string instr, AddrMode addressMode, int cycles) { var op = new OpcodeInfo(); op.Instruction = instr; op.AddressMode = addressMode; op.Cycles = cycles; if (Opcodes[value] != null) throw new Exception("opcode " + value + " already assigned"); Opcodes[value] = op; }
public void OutputGen_Returns_Correctly_Formatted_Operand_String_For_AddrMode(AddrMode addrMode, byte[] operand, string expectedOutput) { // Act var outputString = OutputGen.BuildOperandString(addrMode, operand); // Assert Assert.Equal(expectedOutput, outputString); }
protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { // First load the generic context into Arg0. EmitLoadGenericContext(factory, ref encoder, relocsOnly); switch (_id) { case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)_target; if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); encoder.EmitRET(); } else { EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, _lookupSignature, relocsOnly); // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); AddrMode loadBase = new AddrMode(encoder.TargetRegister.Arg0, null, cctorContextSize, 0, AddrModeSize.Int64); encoder.EmitLEA(encoder.TargetRegister.Result, ref loadBase); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)_target; EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); AddrMode loadFromResult = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, nonGcRegionLookup, relocsOnly); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; // These are all simple: just get the thing from the dictionary and we're done case ReadyToRunHelperId.TypeHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.VirtualCall: case ReadyToRunHelperId.ResolveVirtualFunction: case ReadyToRunHelperId.MethodEntry: { EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); encoder.EmitRET(); } break; default: throw new NotImplementedException(); } }
public Dictionary <ushort, string> Disassemble(ushort nStart, ushort nStop) { Console.WriteLine("START"); var mapLines = new Dictionary <ushort, string>(); ushort addr = nStart; ushort line_addr = 0; byte value = 0x00, lo = 0x00, hi = 0x00; AddrMode _IMP = new AddrMode(IMP); AddrMode _IMM = new AddrMode(IMM); AddrMode _ZP0 = new AddrMode(ZP0); AddrMode _ZPX = new AddrMode(ZPX); AddrMode _ZPY = new AddrMode(ZPY); AddrMode _IZX = new AddrMode(IZX); AddrMode _IZY = new AddrMode(IZY); AddrMode _ABS = new AddrMode(ABS); AddrMode _ABX = new AddrMode(ABX); AddrMode _ABY = new AddrMode(ABY); AddrMode _IND = new AddrMode(IND); AddrMode _REL = new AddrMode(REL); while (addr < nStop - 1) { line_addr = addr; string sInst = "$" + Hex(addr, 4) + ": "; byte opcode = bus.cpuRead(addr, true); addr++; sInst += instructions[opcode].name + " "; if (instructions[opcode].addrMode.Equals(_IMP)) { sInst += " {IMP}"; } else if (instructions[opcode].addrMode.Equals(_IMM)) { value = bus.cpuRead(addr, true); addr++; sInst += "#$" + Hex(value, 2) + " {IMM}"; } else if (instructions[opcode].addrMode.Equals(_ZP0)) { lo = bus.cpuRead(addr, true); addr++; hi = 0x00; sInst += "$" + Hex(lo, 2) + " {ZP0}"; } else if (instructions[opcode].addrMode.Equals(_ZPX)) { lo = bus.cpuRead(addr, true); addr++; hi = 0x00; sInst += "$" + Hex(lo, 2) + ", X {ZPX}"; } else if (instructions[opcode].addrMode.Equals(_ZPY)) { lo = bus.cpuRead(addr, true); addr++; hi = 0x00; sInst += "$" + Hex(lo, 2) + ", Y {ZPY}"; } else if (instructions[opcode].addrMode.Equals(_IZX)) { lo = bus.cpuRead(addr, true); addr++; hi = 0x00; sInst += "($" + Hex(lo, 2) + ", X) {IZX}"; } else if (instructions[opcode].addrMode.Equals(_IZY)) { lo = bus.cpuRead(addr, true); addr++; hi = 0x00; sInst += "($" + Hex(lo, 2) + "), Y {IZY}"; } else if (instructions[opcode].addrMode.Equals(_ABS)) { lo = bus.cpuRead(addr, true); addr++; hi = bus.cpuRead(addr, true); addr++; sInst += "$" + Hex((ushort)((hi << 8) | lo), 4) + " {ABS}"; } else if (instructions[opcode].addrMode.Equals(_ABX)) { lo = bus.cpuRead(addr, true); addr++; hi = bus.cpuRead(addr, true); addr++; sInst += "$" + Hex((ushort)((hi << 8) | lo), 4) + ", X {ABX}"; } else if (instructions[opcode].addrMode.Equals(_ABY)) { lo = bus.cpuRead(addr, true); addr++; hi = bus.cpuRead(addr, true); addr++; sInst += "$" + Hex((ushort)((hi << 8) | lo), 4) + ", Y {ABY}"; } else if (instructions[opcode].addrMode.Equals(_IND)) { lo = bus.cpuRead(addr, true); addr++; hi = bus.cpuRead(addr, true); addr++; sInst += "($" + Hex((ushort)((hi << 8) | lo), 4) + ") {IND}"; } else if (instructions[opcode].addrMode.Equals(_REL)) { value = bus.cpuRead(addr, true); addr++; sInst += "$" + Hex(value, 2) + " [$" + Hex((ushort)(addr + value), 4) + "] {REL}"; } mapLines[line_addr] = sInst; } return(mapLines); }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); { int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, (MethodDesc)Target); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; // TODO: Swap argument order instead // mov arg1, arg0 encoder.Builder.EmitByte(0x48); encoder.Builder.EmitShort((short)((encoder.TargetRegister.Arg0 == Register.RCX) ? 0xD18B : 0xF78B)); encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; if (!target.HasStaticConstructor) { Debug.Assert(Id == ReadyToRunHelperId.GetNonGCStaticBase); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol(target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol(target)); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: encoder.EmitINT3(); break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; if (!target.HasStaticConstructor) { encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol(target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateInfo target = (DelegateInfo)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.MethodEntrypoint(target.Target)); if (target.ShuffleThunk != null) { encoder.EmitLEAQ(encoder.TargetRegister.Arg3, factory.MethodEntrypoint(target.ShuffleThunk)); } encoder.EmitJMP(factory.MethodEntrypoint(target.Ctor)); } break; case ReadyToRunHelperId.InterfaceDispatch: { encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; default: throw new NotImplementedException(); } }
public static string BuildOperandString(AddrMode addrMode, byte[] operand) { switch (addrMode) { case AddrMode.I: { return($"#${operand[0].ToHex(HexPrefix)}"); } case AddrMode.ZP: { return($"${operand[0].ToHex(HexPrefix)}"); } case AddrMode.ZP_X: { return($"${operand[0].ToHex(HexPrefix)},X"); } case AddrMode.ZP_Y: { return($"${operand[0].ToHex(HexPrefix)},Y"); } case AddrMode.ABS: { return($"${operand.ToLittleEndianWord().ToHex(HexPrefix)}"); } case AddrMode.ABS_X: { return($"${operand.ToLittleEndianWord().ToHex(HexPrefix)},X"); } case AddrMode.ABS_Y: { return($"${operand.ToLittleEndianWord().ToHex(HexPrefix)},Y"); } case AddrMode.IX_IND: { return($"(${operand[0].ToHex(HexPrefix)},X)"); } case AddrMode.IND_IX: { return($"(${operand[0].ToHex(HexPrefix)}),Y"); } case AddrMode.Indirect: { return($"(${operand.ToLittleEndianWord().ToHex(HexPrefix)})"); } case AddrMode.Relative: { var offset = (sbyte)operand[0]; return($"*{(offset>=0?"+":"")}{offset}"); } case AddrMode.Accumulator: { return("A"); } case AddrMode.Implied: { return(""); } default: throw new DotNet6502Exception($"Bug detected! Unhandled addressing mode: {addrMode}"); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Helper.Id) { case ReadyToRunHelperId.NewHelper: encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol((TypeDesc)Helper.Target)); encoder.EmitJMP(factory.ExternSymbol("__allocate_object")); break; case ReadyToRunHelperId.VirtualCall: if (relocsOnly) { break; } AddrMode loadFromRcx = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRcx); // TODO: More efficient lookup of the slot { MethodDesc method = (MethodDesc)Helper.Target; TypeDesc owningType = method.OwningType; int baseSlots = 0; var baseType = owningType.BaseType; while (baseType != null) { List <MethodDesc> baseVirtualSlots; factory.VirtualSlots.TryGetValue(baseType, out baseVirtualSlots); if (baseVirtualSlots != null) { baseSlots += baseVirtualSlots.Count; } baseType = baseType.BaseType; } List <MethodDesc> virtualSlots = factory.VirtualSlots[owningType]; int methodSlot = -1; for (int slot = 0; slot < virtualSlots.Count; slot++) { if (virtualSlots[slot] == method) { methodSlot = slot; break; } } Debug.Assert(methodSlot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, 16 + (baseSlots + methodSlot) * factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; case ReadyToRunHelperId.IsInstanceOf: encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Helper.Target)); encoder.EmitJMP(factory.ExternSymbol("__isinst_class")); break; case ReadyToRunHelperId.CastClass: encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Helper.Target)); encoder.EmitJMP(factory.ExternSymbol("__castclass_class")); break; case ReadyToRunHelperId.NewArr1: encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Helper.Target)); encoder.EmitJMP(factory.ExternSymbol("__allocate_array")); break; case ReadyToRunHelperId.GetNonGCStaticBase: if (!((MetadataType)Helper.Target).HasStaticConstructor) { Debug.Assert(Helper.Id == ReadyToRunHelperId.GetNonGCStaticBase); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol((MetadataType)Helper.Target)); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Helper.Target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol((MetadataType)Helper.Target)); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } break; case ReadyToRunHelperId.GetThreadStaticBase: encoder.EmitINT3(); break; case ReadyToRunHelperId.GetGCStaticBase: if (!((MetadataType)Helper.Target).HasStaticConstructor) { encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol((MetadataType)Helper.Target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Helper.Target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol((MetadataType)Helper.Target)); AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } break; case ReadyToRunHelperId.DelegateCtor: { DelegateInfo target = (DelegateInfo)Helper.Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.MethodEntrypoint(target.Target)); if (target.ShuffleThunk != null) { encoder.EmitLEAQ(encoder.TargetRegister.Arg3, factory.MethodEntrypoint(target.ShuffleThunk)); } encoder.EmitJMP(factory.MethodEntrypoint(target.Ctor)); } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int pointerSize = factory.Target.PointerSize; int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; // TODO: Swap argument order instead encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target), hasLazyStaticConstructor ? NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target) : 0); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: encoder.EmitINT3(); break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.Target); if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot); encoder.EmitRET(); } } break; case ReadyToRunHelperId.ResolveGenericVirtualMethod: encoder.EmitINT3(); break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewObjectHelperForType(target))); } break; case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(Register.R10, factory.InterfaceDispatchCell((MethodDesc)Target)); AddrMode jmpAddrMode = new AddrMode(Register.R10, null, 0, 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int pointerSize = factory.Target.PointerSize; int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } } break; case ReadyToRunHelperId.IsInstanceOf: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, false))); } break; case ReadyToRunHelperId.CastClass: { TypeDesc target = (TypeDesc)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetCastingHelperNameForType(target, true))); } break; case ReadyToRunHelperId.NewArr1: { TypeDesc target = (TypeDesc)Target; // TODO: Swap argument order instead encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Arg0); encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol(target)); encoder.EmitJMP(factory.ExternSymbol(JitHelper.GetNewArrayHelperForType(target))); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.TypeSystemContext.HasLazyStaticConstructor(target); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target), hasLazyStaticConstructor ? NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target) : 0); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); // First arg: address of the TypeManager slot that provides the helper with // information about module index and the type manager instance (which is used // for initialization on first access). AddrMode loadFromArg2 = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg2); // Second arg: index of the type in the ThreadStatic section of the modules AddrMode loadFromArg2AndDelta = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg2AndDelta); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); } else { encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); // TODO: performance optimization - inline the check verifying whether we need to trigger the cctor encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; if (target.TargetNeedsVTableLookup) { AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromThisPtr); int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod); } Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Arg2, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromSlot); } else { int delta = 0; ISymbolNode targetMethodNode = target.GetTargetNode(factory); if (targetMethodNode is IFatFunctionPointerNode) { delta = FatFunctionPointerConstants.Offset; } encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.GetTargetNode(factory), delta); } if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod); Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot); encoder.EmitRET(); } } break; default: throw new NotImplementedException(); } }
protected sealed override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { // First load the generic context into Arg0. EmitLoadGenericContext(factory, ref encoder, relocsOnly); switch (_id) { case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)_target; if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); encoder.EmitRET(); } else { EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, _lookupSignature, relocsOnly); encoder.EmitMOV(encoder.TargetRegister.Result, encoder.TargetRegister.Arg0); // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64); encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)_target; EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); AddrMode loadFromResult = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromResult); if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg0, nonGcRegionLookup, relocsOnly); int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize - cctorContextSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg0, null, -cctorContextSize, 0, AddrModeSize.Int64); encoder.EmitLEA(encoder.TargetRegister.Arg0, ref loadCctor); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)_target; // Look up the index cell EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg1, _lookupSignature, relocsOnly); ISymbolNode helperEntrypoint; if (factory.TypeSystemContext.HasLazyStaticConstructor(target)) { // There is a lazy class constructor. We need the non-GC static base because that's where the // class constructor context lives. GenericLookupResult nonGcRegionLookup = factory.GenericLookup.TypeNonGCStaticBase(target); EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Arg2, nonGcRegionLookup, relocsOnly); int cctorContextSize = NonGCStaticsNode.GetClassConstructorContextStorageSize(factory.Target, target); AddrMode loadCctor = new AddrMode(encoder.TargetRegister.Arg2, null, -cctorContextSize, 0, AddrModeSize.Int64); encoder.EmitLEA(encoder.TargetRegister.Arg2, ref loadCctor); helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase); } else { helperEntrypoint = factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType); } // First arg: address of the TypeManager slot that provides the helper with // information about module index and the type manager instance (which is used // for initialization on first access). AddrMode loadFromArg1 = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg1); // Second arg: index of the type in the ThreadStatic section of the modules AddrMode loadFromArg1AndDelta = new AddrMode(encoder.TargetRegister.Arg1, null, factory.Target.PointerSize, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg1AndDelta); encoder.EmitJMP(helperEntrypoint); } break; // These are all simple: just get the thing from the dictionary and we're done case ReadyToRunHelperId.TypeHandle: case ReadyToRunHelperId.MethodHandle: case ReadyToRunHelperId.FieldHandle: case ReadyToRunHelperId.MethodDictionary: case ReadyToRunHelperId.VirtualCall: case ReadyToRunHelperId.ResolveVirtualFunction: case ReadyToRunHelperId.MethodEntry: { EmitDictionaryLookup(factory, ref encoder, encoder.TargetRegister.Arg0, encoder.TargetRegister.Result, _lookupSignature, relocsOnly); encoder.EmitRET(); } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.VirtualCall: { MethodDesc targetMethod = (MethodDesc)Target; Debug.Assert(!targetMethod.OwningType.IsInterface); Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); int pointerSize = factory.Target.PointerSize; int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); Debug.Assert(slot != -1); } Debug.Assert(((NativeSequencePoint[])((INodeWithDebugInfo)this).GetNativeSequencePoints())[1].NativeOffset == encoder.Builder.CountBytes); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(pointerSize) + (slot * pointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; case ReadyToRunHelperId.GetNonGCStaticBase: { MetadataType target = (MetadataType)Target; bool hasLazyStaticConstructor = factory.PreinitializationManager.HasLazyStaticConstructor(target); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol(target)); if (!hasLazyStaticConstructor) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } } break; case ReadyToRunHelperId.GetThreadStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeThreadStaticIndex(target)); // First arg: address of the TypeManager slot that provides the helper with // information about module index and the type manager instance (which is used // for initialization on first access). AddrMode loadFromArg2 = new AddrMode(encoder.TargetRegister.Arg2, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg0, ref loadFromArg2); // Second arg: index of the type in the ThreadStatic section of the modules AddrMode loadFromArg2AndDelta = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromArg2AndDelta); if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) { encoder.EmitJMP(factory.ExternSymbol("RhpGetThreadStaticBaseForType")); } else { encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg2, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitJE(factory.ExternSymbol("RhpGetThreadStaticBaseForType")); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); } } break; case ReadyToRunHelperId.GetGCStaticBase: { MetadataType target = (MetadataType)Target; encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol(target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); if (!factory.PreinitializationManager.HasLazyStaticConstructor(target)) { encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base. It is stored at the beginning of the non-GC statics region. encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeNonGCStaticsSymbol(target), -NonGCStaticsNode.GetClassConstructorContextSize(factory.Target)); AddrMode initialized = new AddrMode(encoder.TargetRegister.Arg0, null, factory.Target.PointerSize, 0, AddrModeSize.Int32); encoder.EmitCMP(ref initialized, 1); encoder.EmitRETIfEqual(); encoder.EmitMOV(encoder.TargetRegister.Arg1, encoder.TargetRegister.Result); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } } break; case ReadyToRunHelperId.DelegateCtor: { DelegateCreationInfo target = (DelegateCreationInfo)Target; if (target.TargetNeedsVTableLookup) { Debug.Assert(!target.TargetMethod.CanMethodBeInSealedVTable()); AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromThisPtr); int slot = 0; if (!relocsOnly) { slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, target.TargetMethod, target.TargetMethod.OwningType); } Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Arg2, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg2, ref loadFromSlot); } else { ISymbolNode targetMethodNode = target.GetTargetNode(factory); encoder.EmitLEAQ(encoder.TargetRegister.Arg2, target.GetTargetNode(factory)); } if (target.Thunk != null) { Debug.Assert(target.Constructor.Method.Signature.Length == 3); encoder.EmitLEAQ(encoder.TargetRegister.Arg3, target.Thunk); } else { Debug.Assert(target.Constructor.Method.Signature.Length == 2); } encoder.EmitJMP(target.Constructor); } break; case ReadyToRunHelperId.ResolveVirtualFunction: { MethodDesc targetMethod = (MethodDesc)Target; if (targetMethod.OwningType.IsInterface) { encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.InterfaceDispatchCell(targetMethod)); encoder.EmitJMP(factory.ExternSymbol("RhpResolveInterfaceMethod")); } else { if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); Debug.Assert(!targetMethod.CanMethodBeInSealedVTable()); int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, targetMethod, targetMethod.OwningType); Debug.Assert(slot != -1); AddrMode loadFromSlot = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromSlot); encoder.EmitRET(); } } break; default: throw new NotImplementedException(); } }
protected override void EmitCode(NodeFactory factory, ref X64Emitter encoder, bool relocsOnly) { switch (Id) { case ReadyToRunHelperId.NewHelper: encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.ConstructedTypeSymbol((TypeDesc)Target)); encoder.EmitJMP(factory.ExternSymbol("__allocate_object")); break; case ReadyToRunHelperId.VirtualCall: if (relocsOnly) { break; } AddrMode loadFromThisPtr = new AddrMode(encoder.TargetRegister.Arg0, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromThisPtr); { int slot = VirtualMethodSlotHelper.GetVirtualMethodSlot(factory, (MethodDesc)Target); Debug.Assert(slot != -1); AddrMode jmpAddrMode = new AddrMode(encoder.TargetRegister.Result, null, EETypeNode.GetVTableOffset(factory.Target.PointerSize) + (slot * factory.Target.PointerSize), 0, AddrModeSize.Int64); encoder.EmitJmpToAddrMode(ref jmpAddrMode); } break; case ReadyToRunHelperId.IsInstanceOf: encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target)); encoder.EmitJMP(factory.ExternSymbol("__isinst_class")); break; case ReadyToRunHelperId.CastClass: encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target)); encoder.EmitJMP(factory.ExternSymbol("__castclass_class")); break; case ReadyToRunHelperId.NewArr1: encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.NecessaryTypeSymbol((TypeDesc)Target)); encoder.EmitJMP(factory.ExternSymbol("__allocate_array")); break; case ReadyToRunHelperId.GetNonGCStaticBase: if (!((MetadataType)Target).HasStaticConstructor) { Debug.Assert(Id == ReadyToRunHelperId.GetNonGCStaticBase); encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeNonGCStaticsSymbol((MetadataType)Target)); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeNonGCStaticsSymbol((MetadataType)Target)); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnNonGCStaticBase)); } break; case ReadyToRunHelperId.GetThreadStaticBase: encoder.EmitINT3(); break; case ReadyToRunHelperId.GetGCStaticBase: if (!((MetadataType)Target).HasStaticConstructor) { encoder.EmitLEAQ(encoder.TargetRegister.Result, factory.TypeGCStaticsSymbol((MetadataType)Target)); AddrMode loadFromRax = new AddrMode(encoder.TargetRegister.Result, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitMOV(encoder.TargetRegister.Result, ref loadFromRax); encoder.EmitRET(); } else { // We need to trigger the cctor before returning the base encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeCctorContextSymbol((MetadataType)Target)); encoder.EmitLEAQ(encoder.TargetRegister.Arg1, factory.TypeGCStaticsSymbol((MetadataType)Target)); AddrMode loadFromRdx = new AddrMode(encoder.TargetRegister.Arg1, null, 0, 0, AddrModeSize.Int64); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitMOV(encoder.TargetRegister.Arg1, ref loadFromRdx); encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnGCStaticBase)); } break; case ReadyToRunHelperId.DelegateCtor: { DelegateInfo target = (DelegateInfo)Target; encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.MethodEntrypoint(target.Target)); if (target.ShuffleThunk != null) { encoder.EmitLEAQ(encoder.TargetRegister.Arg3, factory.MethodEntrypoint(target.ShuffleThunk)); } encoder.EmitJMP(factory.MethodEntrypoint(target.Ctor)); } break; default: throw new NotImplementedException(); } }