Exemple #1
0
        public static void SUBN(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            Label CaseNotOverflow = gen.DefineLabel();
            Label Continue        = gen.DefineLabel();

            gen.EmitGetRegister(inst.Value.Reg0, ctx);
            gen.EmitGetRegister(inst.Value.Reg1, ctx);
            gen.Emit(OpCodes.Ldind_I1);
            gen.Emit(OpCodes.Conv_U1);
            gen.EmitGetRegister(inst.Value.Reg0, ctx);
            gen.Emit(OpCodes.Ldind_I1);
            gen.Emit(OpCodes.Conv_U1);
            gen.Emit(OpCodes.Sub);
            //now the stack contains the difference and the register address
            gen.Emit(OpCodes.Dup);
            gen.Emit(OpCodes.Ldc_I4_M1);
            gen.Emit(OpCodes.Bgt, CaseNotOverflow);

            //CaseOverflow
            gen.EmitGetRegister(0xF, ctx);
            gen.Emit(OpCodes.Ldc_I4, 0);
            gen.Emit(OpCodes.Br, Continue);

            gen.MarkLabel(CaseNotOverflow);
            gen.EmitGetRegister(0xF, ctx);
            gen.Emit(OpCodes.Ldc_I4, 1);

            //Assign value to VF and store sum
            gen.MarkLabel(Continue);
            gen.Emit(OpCodes.Stind_I1);             //Store the VF value that's on the stack
            gen.Emit(OpCodes.Conv_U1);              //Store the difference
            gen.Emit(OpCodes.Stind_I1);
        }
Exemple #2
0
 public static void LRI(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.Emit(OpCodes.Ldarg_0);
     gen.Emit(OpCodes.Ldc_I4, inst.Value.Immediate16);
     //gen.Emit(OpCodes.Conv_I2);
     gen.Emit(OpCodes.Call, ctx.RegisterI.SetMethod);
 }
Exemple #3
0
 public static void XOR(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.EmitPushRegistersAndIndirectTarget(inst.Value.Reg0, inst.Value.Reg1, ctx);
     gen.Emit(OpCodes.Xor);
     gen.Emit(OpCodes.Conv_U1);
     gen.Emit(OpCodes.Stind_I1);
 }
Exemple #4
0
        public static void SHL(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            Label Continue      = gen.DefineLabel();
            Label HighBitNotSet = gen.DefineLabel();

            gen.EmitGetRegister(inst.Value.Reg0, ctx);
            gen.Emit(OpCodes.Dup);             //Duplicate address for loading
            gen.Emit(OpCodes.Ldind_U1);        //Load value

            gen.Emit(OpCodes.Dup);             //duplicate value for VF comparison
            gen.Emit(OpCodes.Stloc_0);         //Store for VF comparison

            gen.Emit(OpCodes.Ldc_I4_1);
            gen.Emit(OpCodes.Shl);             //Shift and store
            gen.Emit(OpCodes.Stind_I1);

            gen.Emit(OpCodes.Ldloc_0);
            gen.Emit(OpCodes.Ldc_I4, 0x80);
            gen.Emit(OpCodes.And);
            gen.Emit(OpCodes.Ldc_I4_0);
            gen.Emit(OpCodes.Beq, HighBitNotSet);

            //HighBitSet:
            gen.EmitGetRegister(0xF, ctx);
            gen.Emit(OpCodes.Ldc_I4_1);
            gen.Emit(OpCodes.Br, Continue);

            //Not set
            gen.MarkLabel(HighBitNotSet);
            gen.EmitGetRegister(0xF, ctx);
            gen.Emit(OpCodes.Ldc_I4_0);

            gen.MarkLabel(Continue);
            gen.Emit(OpCodes.Stind_I1);
        }
Exemple #5
0
        public static void ADD(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            Label CaseNotOverflow = gen.DefineLabel();
            Label Continue        = gen.DefineLabel();

            gen.EmitPushRegistersAndIndirectTarget(inst.Value.Reg0, inst.Value.Reg1, ctx);
            gen.Emit(OpCodes.Add);
            //now the stack contains the sum and the register address
            gen.Emit(OpCodes.Dup);                  //Duplicate the sum for comparison
            gen.Emit(OpCodes.Ldc_I4, 0xFF);
            gen.Emit(OpCodes.Ble, CaseNotOverflow); //If greater goto case overflow

            //else CaseOverflow:
            gen.EmitGetRegister(0xF, ctx);             //Push VF register addres for storing the overflow bit
            gen.Emit(OpCodes.Ldc_I4, 1);
            gen.Emit(OpCodes.Br, Continue);

            //CaseNotOverflow:
            gen.MarkLabel(CaseNotOverflow);
            gen.EmitGetRegister(0xF, ctx);             //Push VF register addres for storing the overflow bit
            gen.Emit(OpCodes.Ldc_I4, 0);

            //Assign value to VF and store sum
            gen.MarkLabel(Continue);
            gen.Emit(OpCodes.Stind_I1);            //Store the VF value that's on the stack
            gen.Emit(OpCodes.Conv_U1);             //Store the sum value we duplicated earlier
            gen.Emit(OpCodes.Stind_I1);
        }
Exemple #6
0
 public static void LD(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.EmitGetRegister(inst.Value.Reg0, ctx); //Push target address
     gen.EmitGetRegister(inst.Value.Reg1, ctx); //Push source address
     gen.Emit(OpCodes.Ldind_U1);                //Pop source address, push value
     gen.Emit(OpCodes.Stind_I1);                //assign value
 }
Exemple #7
0
 public static void SDT(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.EmitGetRegister(inst.Value.Reg0, ctx);
     gen.Emit(OpCodes.Ldarg_0);
     gen.Emit(OpCodes.Call, ctx.RegisterDT.GetMethod);
     gen.Emit(OpCodes.Stind_I1);
 }
Exemple #8
0
 public static void IN(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     Debug.WriteLine("WARN: Unimplemented input instruction");
     //Wait for a key press, store the value of the key in Vx.
     gen.EmitGetRegister(inst.Value.Reg0, ctx);
     gen.Emit(OpCodes.Ldc_I4_0);
     gen.Emit(OpCodes.Stind_I1);
 }
Exemple #9
0
 public static void SEI(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.EmitGetRegister(inst.Value.Reg0, ctx);
     gen.Emit(OpCodes.Ldind_I1);
     gen.Emit(OpCodes.Conv_U1);
     gen.EmitLoadImmediate(inst);
     gen.Emit(OpCodes.Beq, ctx.Label(inst.Offset + 4));
 }
Exemple #10
0
        public static void CLS(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            var method = typeof(Interpreter.Implementation).GetMethod(nameof(Implementation.CLS));

            //Args are: Chip8State state

            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Call, method);
        }
Exemple #11
0
        public static void STBCD(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            var method = typeof(Interpreter.Implementation).GetMethod(nameof(Implementation.STBCD));

            //Args are: Chip8State state, ref byte regx

            gen.Emit(OpCodes.Ldarg_0);
            gen.EmitGetRegister(inst.Value.Reg0, ctx);
            gen.Emit(OpCodes.Call, method);
        }
Exemple #12
0
        public static void LDREG(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            var method = typeof(Interpreter.Implementation).GetMethod(nameof(Implementation.LDREG));

            //Args are: Chip8State state, byte regx

            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldc_I4_S, inst.Value.Reg0);
            gen.Emit(OpCodes.Call, method);
        }
Exemple #13
0
 public static void ADDI(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.EmitGetRegister(inst.Value.Reg0, ctx);
     gen.Emit(OpCodes.Dup);
     gen.Emit(OpCodes.Ldind_U1);
     gen.EmitLoadImmediate(inst);
     gen.Emit(OpCodes.Add);
     gen.Emit(OpCodes.Conv_U1);
     gen.Emit(OpCodes.Stind_I1);
 }
Exemple #14
0
 public static void SNER(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.EmitGetRegister(inst.Value.Reg0, ctx);
     gen.Emit(OpCodes.Ldind_I1);
     gen.Emit(OpCodes.Conv_U1);
     gen.EmitGetRegister(inst.Value.Reg1, ctx);
     gen.Emit(OpCodes.Ldind_I1);
     gen.Emit(OpCodes.Conv_U1);
     gen.Emit(OpCodes.Bne_Un, ctx.Label(inst.Offset + 4));
 }
Exemple #15
0
 public static void JMP(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     if (inst.Instr.Value.Immediate16 == inst.Offset)
     {
         gen.Emit(OpCodes.Ret);
     }
     else
     {
         gen.Emit(OpCodes.Br, ctx.Labels[inst.Value.Immediate16]);
     }
 }
Exemple #16
0
 public static void ADDRI(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.Emit(OpCodes.Ldarg_0);             //reference for the last store call
     gen.EmitGetRegister(inst.Value.Reg0, ctx);
     gen.Emit(OpCodes.Ldind_I1);
     gen.Emit(OpCodes.Conv_U1);
     gen.Emit(OpCodes.Ldarg_0);
     gen.Emit(OpCodes.Call, ctx.RegisterI.GetMethod);
     gen.Emit(OpCodes.Add);
     gen.Emit(OpCodes.Call, ctx.RegisterI.SetMethod);
 }
Exemple #17
0
        public static void DRW(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            var method = typeof(Interpreter.Implementation).GetMethod(nameof(Implementation.DRW));

            //Args are: Chip8State state, ref byte regx, ref byte regy, byte imm4

            gen.Emit(OpCodes.Ldarg_0);
            gen.EmitGetRegister(inst.Value.Reg0, ctx);
            gen.EmitGetRegister(inst.Value.Reg1, ctx);
            gen.Emit(OpCodes.Ldc_I4_S, inst.Value.Immediate4);
            gen.Emit(OpCodes.Call, method);
        }
Exemple #18
0
        private (DynamicMethod, ILGenerator) CreateMethod(string name, JITContext ctx)
        {
            DynamicMethod res = (DynamicMethod)(object)new DynamicMethod(name, typeof(void), new[]
            {
                typeof(Chip8State),
                typeof(Registers).MakeByRefType()
            });

            ILGenerator gen = res.GetILGenerator();

            gen.DeclareLocal(typeof(int));
            return(res, gen);
        }
Exemple #19
0
        public static void JMP0(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            List <Label> JumpTable = new List <Label>();

            for (int i = inst.Value.Immediate16; i < inst.Value.Immediate16 + 256; i++)
            {
                JumpTable.Add(ctx.Label(i));
            }

            gen.EmitGetRegister(0, ctx);
            gen.Emit(OpCodes.Ldind_I1);
            gen.Emit(OpCodes.Conv_U1);
            gen.Emit(OpCodes.Switch, JumpTable.ToArray());
        }
Exemple #20
0
        public JITROMDelegate JITROM(Span <byte> ROM, SortedSet <UInt16> BreakPoints = null)
        {
            if (BreakPoints == null)
            {
                BreakPoints = new SortedSet <ushort>();
            }

            var exe = disasm.DisassembleProgram(ROM);

            JITContext ctx = new JITContext
            {
                BreakPoints = BreakPoints,
                Disasm      = exe
            };

            var(res, gen) = CreateMethod("JITROM", ctx);

            //Find all function calls
            Dictionary <UInt16, TranslatedFunction> Functions = exe.Entries.Where(x => !x.IsData && x.Instr.Value.Instruction == Instruction.CALL)
                                                                .Select(x => x.Instr.Value.Immediate16)
                                                                .Distinct()
                                                                .ToDictionary(x => x, x => (TranslatedFunction)null);

            //Function calls will get translated to their own method to use the runtime call stack
            foreach (var f in Functions.Keys.ToArray())
            {
                var(tmpMethod, TmpGen) = CreateMethod("method_" + f.ToString("X4"), ctx);

                Functions[f] = new TranslatedFunction
                {
                    Method    = tmpMethod,
                    Generator = TmpGen,
                    Info      = tmpMethod
                };
            }

            ctx.Functions = Functions;

            //As detecting where a funnction ends is not trivial we're just going to JIT all functions to the end of the rom, disgusting i know
            foreach (var f in Functions.Keys.Where(x => x >= Chip8State.ProgramStart))
            {
                JITBlock(Functions[f].Generator, ctx, (f - Chip8State.ProgramStart) / 2, exe.Entries.Length);
            }

            //Then JIT the whole ROM
            JITBlock(gen, ctx, 0, exe.Entries.Length);

            return((JITROMDelegate)res.CreateDelegate(typeof(JITROMDelegate)));
        }
Exemple #21
0
        public static void SHR(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            gen.EmitGetRegister(inst.Value.Reg0, ctx);
            gen.Emit(OpCodes.Dup);             //Duplicate address for loading
            gen.Emit(OpCodes.Ldind_U1);        //Load value

            gen.Emit(OpCodes.Dup);             //duplicate value for VF comparison
            gen.Emit(OpCodes.Stloc_0);         //Store for VF comparison

            gen.Emit(OpCodes.Ldc_I4_1);
            gen.Emit(OpCodes.Shr_Un);             //Shift and store
            gen.Emit(OpCodes.Stind_I1);

            gen.EmitGetRegister(0xF, ctx);
            gen.Emit(OpCodes.Ldloc_0);
            gen.Emit(OpCodes.Ldc_I4_1);
            gen.Emit(OpCodes.And);
            gen.Emit(OpCodes.Stind_I1);
        }
Exemple #22
0
 public static void RET(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.Emit(OpCodes.Ret);
 }
Exemple #23
0
 /// <summary>
 /// Pushes the address of the N register on the stack
 /// </summary>
 internal static void EmitGetRegister(this ILGenerator gen, int N, JITContext ctx)
 {
     gen.Emit(OpCodes.Ldarg_1);
     gen.Emit(OpCodes.Ldflda, Registers.Fields[N]);
 }
Exemple #24
0
 public static void SYS(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     Debug.WriteLine("WARN: Sys called");
 }
Exemple #25
0
 public static void CALL(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
 {
     gen.Emit(OpCodes.Ldarg_0);
     gen.Emit(OpCodes.Ldarg_1);
     gen.Emit(OpCodes.Call, ctx.Functions[inst.Value.Immediate16].Method);
 }
Exemple #26
0
        public static void SKNP(JITContext ctx, Disassembler.DecompEntry inst, ILGenerator gen)
        {
            Debug.WriteLine("WARN: Unimplemented input instruction");

            //Skip next instruction if key with the value of Vx is pressed.
        }
Exemple #27
0
 /// <summary>
 /// After the code emitted by this method the managed stack looks like:
 ///
 /// <br>--bottom--</br>
 /// <br>target register address</br>
 /// <br>target value </br>
 /// <br>source value </br>
 ///
 /// </summary>
 /// <param name="target">target register index</param>
 /// <param name="source">source register index</param>
 /// <param name="ctx">state ctx</param>
 internal static void EmitPushRegistersAndIndirectTarget(this ILGenerator gen, int target, int source, JITContext ctx)
 {
     gen.EmitGetRegister(target, ctx);
     gen.Emit(OpCodes.Dup);
     gen.Emit(OpCodes.Ldind_U1);
     gen.EmitGetRegister(source, ctx);
     gen.Emit(OpCodes.Ldind_U1);
 }
Exemple #28
0
        void JITBlock(ILGenerator gen, JITContext ctx, int start, int end)
        {
            //Every function has its own set of labels
            ctx.Labels = new Dictionary <ushort, Label>();

            void AddLabel(int v)
            {
                //Is this offset outside of the ROM ? Sometimes data can be misinterpreted as code
                if (v < Chip8State.ProgramStart)
                {
                    return;
                }
                //Are we trying to jump from a function to code outside of it ?
                if ((v - Chip8State.ProgramStart) / 2 < start)
                {
                    Debugger.Break();
                }

                if (!ctx.Labels.ContainsKey((UInt16)v))
                {
                    ctx.Labels.Add((UInt16)v, gen.DefineLabel());
                }
            }

            //Calculate all the labels we need beforehand
            for (int i = start; i < end; i++)
            {
                var entry = ctx.Disasm.Entries[i];
                if (entry.IsData)
                {
                    continue;
                }

                if (!entry.Value.IsControlFlow() || entry.Instruction == Instruction.RET)
                {
                    continue;
                }

                if (entry.Value.IsSkipNext())
                {
                    AddLabel(entry.Offset + 4);
                }
                else if (entry.Value.IsJumpStatic())
                {
                    AddLabel(entry.Value.Immediate16);
                }
                else if (entry.Instr.Value.IsJumpVariable())
                {
                    /*
                     *      The JMP0 instruction jumps to immediate value + V0
                     *      The optimal way to implement this would be hooking jumps and JIT based on the address...
                     *      ...Or we can bruteforce all 256 cases by using a jumptable with the MSIL "switch" instruction
                     */
                    for (int j = entry.Value.Immediate16; j < entry.Instr.Value.Immediate16 + 256; j++)
                    {
                        AddLabel(j);
                    }
                }
                else
                {
                    throw new Exception("Invalid jump instruction");
                }
            }

            SortedSet <UInt16> MarkedLabels = new SortedSet <ushort>();

            for (int i = start; i < end; i++)
            {
                var entry = ctx.Disasm.Entries[i];

                if (entry.IsData)
                {
                    continue;
                }

                var instr = entry.Instr.Value.Instruction;

                //This is not really useful as visual studio can't debug "Lightweight functions"
                if (ctx.BreakPoints.Contains(entry.Offset))
                {
                    gen.Emit(OpCodes.Break);
                }

                //Mark labels as we go
                if (ctx.Labels.ContainsKey(entry.Offset))
                {
                    gen.MarkLabel(ctx.Labels[entry.Offset]);
                    MarkedLabels.Add(entry.Offset);
                }

                LinkEmitters[instr](ctx, entry, gen);
            }

            //Set all other labels to end of the fnction
            foreach (var lbl in ctx.Labels)
            {
                if (!MarkedLabels.Contains(lbl.Key))
                {
                    gen.MarkLabel(lbl.Value);
                }
            }

            gen.Emit(OpCodes.Ret);
        }