private static void UpdateFlags(DebuggableEmitter il) { il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I8, uint.MaxValue); il.Emit(OpCodes.And); }
private void PushOperand(DebuggableEmitter il, ulong index, UrclInstruction inst) { bool isImm; ulong value; switch (index) { case 0: isImm = inst.AType switch { OperandType.Register => false, OperandType.Immediate => true, _ => throw new InvalidOperationException(), }; value = inst.A; break; case 1: isImm = inst.BType switch { OperandType.Register => false, OperandType.Immediate => true, _ => throw new InvalidOperationException(), }; value = inst.B; break; case 2: isImm = inst.CType switch { OperandType.Register => false, OperandType.Immediate => true, _ => throw new InvalidOperationException(), }; value = inst.C; break; default: throw new InvalidOperationException(); } if (isImm) { il.Emit(OpCodes.Ldc_I8, value); } else { if (value == 0) { il.Emit(OpCodes.Ldc_I8, 0UL); } else { il.Emit(OpCodes.Ldloc, Registers[value - 1]); } } }
private void PopOperand(DebuggableEmitter il, ulong index, UrclInstruction inst) { ulong reg; switch (index) { case 0: if (inst.AType == OperandType.Register) { reg = inst.A; } else { throw new InvalidOperationException(); } break; case 1: if (inst.AType == OperandType.Register) { reg = inst.B; } else { throw new InvalidOperationException(); } break; case 2: if (inst.AType == OperandType.Register) { reg = inst.C; } else { throw new InvalidOperationException(); } break; default: throw new InvalidOperationException(); } if (reg == 0) { il.Emit(OpCodes.Pop); } else { il.Emit(OpCodes.Stloc, Registers[reg - 1]); } }
private static void UseIO(DebuggableEmitter il, ulong port, bool input) { switch ((UrclPort)port) { case UrclPort.ValueDisplay: case UrclPort.ValueDisplay32Dec: if (input) { throw new Exception("Reading from output only port is not allowed."); } else { il.Emit(OpCodes.Ldc_I8, (ulong)uint.MaxValue); il.Emit(OpCodes.And); il.Emit(OpCodes.Call, WriteValue); } break; case UrclPort.Teletype: case UrclPort.Teletype8BitASCII: if (input) { il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Call, ReadChar); il.Emit(OpCodes.Ldfld, KeyValue); il.Emit(OpCodes.Conv_I1); } else { il.Emit(OpCodes.Conv_I1); il.Emit(OpCodes.Call, WriteChar); } break; default: throw new Exception($"Unsupported I/O port {port}."); } }
public byte[] Emit(string name, IEnumerable <UrclInstruction> instructions, bool isLibrary) { var labels = new Dictionary <Label, System.Reflection.Emit.Label>(); var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(name), AssemblyBuilderAccess.RunAndCollect); var module = assembly.DefineDynamicModule(name); var type = module.DefineType("Program"); Memory = type.DefineField(nameof(Memory), typeof(uint[]), FieldAttributes.Static); ValueStack = type.DefineField(nameof(ValueStack), typeof(Stack <ulong>), FieldAttributes.Static); var method = type.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static); method.SetReturnType(typeof(void)); var il = new DebuggableEmitter(method.GetILGenerator()); Console.WriteLine("FLAGS"); il.DeclareLocal(typeof(ulong)); Console.WriteLine("STACK"); il.Emit(OpCodes.Newobj, StackInit); il.Emit(OpCodes.Stsfld, ValueStack); bool halted = false; foreach (var inst in instructions) { Console.WriteLine(inst); halted = false; switch (inst.Operation) { case Operation.NOP: il.Emit(OpCodes.Nop); break; case Operation.BRK: il.Emit(OpCodes.Break); break; case Operation.HLT: halted = true; il.Emit(OpCodes.Ret); break; case Operation.ADD: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Add); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.INC: PushOperand(il, 1, inst); il.Emit(OpCodes.Ldc_I8, (ulong)1); il.Emit(OpCodes.Add); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.SUB: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Sub); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.DEC: PushOperand(il, 1, inst); il.Emit(OpCodes.Ldc_I8, (ulong)1); il.Emit(OpCodes.Sub); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.MLT: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Mul); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.DIV: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Div_Un); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.MOD: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Rem_Un); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.AND: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.And); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.OR: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Or); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.XOR: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Xor); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.NAND: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.And); il.Emit(OpCodes.Not); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.NOR: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Or); il.Emit(OpCodes.Not); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.XNOR: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Xor); il.Emit(OpCodes.Not); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.NOT: PushOperand(il, 1, inst); il.Emit(OpCodes.Not); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.LSH: PushOperand(il, 1, inst); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Shl); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.BSL: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Conv_I4); il.Emit(OpCodes.Shl); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.RSH: PushOperand(il, 1, inst); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Shr); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.BSR: PushOperand(il, 1, inst); PushOperand(il, 2, inst); il.Emit(OpCodes.Conv_I4); il.Emit(OpCodes.Shr); UpdateFlags(il); PopOperand(il, 0, inst); break; case Operation.MOV: PushOperand(il, 1, inst); PopOperand(il, 0, inst); break; case Operation.IMM: PushOperand(il, 1, inst); PopOperand(il, 0, inst); break; case Operation.LOAD: il.Emit(OpCodes.Ldsfld, Memory); PushOperand(il, 1, inst); il.Emit(OpCodes.Ldelem, typeof(uint)); PopOperand(il, 0, inst); break; case Operation.STORE: il.Emit(OpCodes.Ldsfld, Memory); PushOperand(il, 0, inst); PushOperand(il, 1, inst); il.Emit(OpCodes.Stelem, typeof(uint)); break; case Operation.IN: UseIO(il, inst.B, true); PopOperand(il, 0, inst); break; case Operation.OUT: PushOperand(il, 1, inst); UseIO(il, inst.A, false); break; case Operation.PSH: il.Emit(OpCodes.Ldsfld, ValueStack); PushOperand(il, 0, inst); il.Emit(OpCodes.Call, PushStack); break; case Operation.POP: il.Emit(OpCodes.Ldsfld, ValueStack); il.Emit(OpCodes.Call, PopStack); PopOperand(il, 0, inst); break; case Operation.BRA: il.Emit(OpCodes.Br, labels[inst.ALabel]); break; case Operation.BRZ: il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Brfalse, labels[inst.ALabel]); break; case Operation.BNZ: il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Brtrue, labels[inst.ALabel]); break; case Operation.BRC: il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I8, (ulong)uint.MaxValue); il.Emit(OpCodes.Bgt_Un, labels[inst.ALabel]); break; case Operation.BNC: il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I8, (ulong)uint.MaxValue); il.Emit(OpCodes.Ble_Un, labels[inst.ALabel]); break; case Operation.BRP: il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I8, (ulong)int.MaxValue); il.Emit(OpCodes.Ble_Un, labels[inst.ALabel]); break; case Operation.BRN: il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Conv_I4); il.Emit(OpCodes.Conv_I8); il.Emit(OpCodes.Ldc_I8, (long)(ulong)int.MaxValue); il.Emit(OpCodes.Bgt_Un, labels[inst.ALabel]); break; case Operation.CAL: throw new Exception("CAL is not currently supported when targeting .NET."); case Operation.RET: throw new Exception("RET is not currently supported when targeting .NET."); case Operation.MINRAM: il.Emit(OpCodes.Ldc_I8, inst.A); il.Emit(OpCodes.Newarr, typeof(uint)); il.Emit(OpCodes.Stsfld, Memory); break; case Operation.BENCHMARK: BenchmarkValue = type.DefineField(nameof(BenchmarkValue), typeof(int), FieldAttributes.Static); il.Emit(OpCodes.Call, GetTime); il.Emit(OpCodes.Stsfld, BenchmarkValue); break; case Operation.COMPILER_CREATELABEL: labels.Add(inst.ALabel, il.DefineLabel()); break; case Operation.COMPILER_MARKLABEL: il.MarkLabel(labels[inst.ALabel]); break; case Operation.COMPILER_MAXREG: Registers = new LocalBuilder[inst.A]; for (ulong i = 0; i < inst.A; i++) { Registers[i] = il.DeclareLocal(typeof(ulong)); } break; case Operation.COMPILER_PRAGMA: //TODO break; default: throw new Exception($"Unimplemented instruction: \"{inst.Operation}\""); } } if (BenchmarkValue != null) { Console.WriteLine("BENCHMARK_FINISH"); il.Emit(OpCodes.Call, GetTime); il.Emit(OpCodes.Ldsfld, BenchmarkValue); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Conv_I8); il.Emit(OpCodes.Call, WriteValue); } if (!halted) { Console.WriteLine("HLT"); il.Emit(OpCodes.Ret); } type.CreateType(); var data = new MemoryStream(new Lokad.ILPack.AssemblyGenerator().GenerateAssemblyBytes(assembly)); var exportModule = Mono.Cecil.ModuleDefinition.ReadModule(data); exportModule.Assembly.MainModule.Kind = Mono.Cecil.ModuleKind.Console; exportModule.EntryPoint = exportModule.Types.Where(t => t.Name == "Program").First().Methods.Where(m => m.Name == "Main").First(); var buffer = new MemoryStream(); exportModule.Write(buffer); return(buffer.ToArray()); }