using System; using System.Collections.Generic; using System.Linq; using System.Text; using Lost.JIT.AMD64; using System.IO; using System.Diagnostics; //using Cosmos.Build.Windows; namespace Lost { class LostTest { [STAThread] static void Main(string[] args) { //using (var source = new StreamReader(args[0])) //{ // string srcLine; // while ((srcLine = source.ReadLine()) != null) // { // var op = ProcessorInstruction.Parse(srcLine); // Console.WriteLine(op); // } //} CompilationTest(); if (File.Exists(tempFile)) File.Delete(tempFile); Console.ReadKey(); } static string RunFasm(string filename, string dest) { //C:\Programs\Devel\asm\fasmw16726\ const string fasm = @"fasm.exe"; var par = new ProcessStartInfo(fasm, string.Format("\"{0}\" \"{1}\"", filename, dest)); par.CreateNoWindow = true; par.RedirectStandardOutput = true; par.RedirectStandardError = true; par.UseShellExecute = false; string result; using (var process = Process.Start(par)) using(var output = process.StandardOutput) using(var errorStream =process.StandardError) { result = output.ReadToEnd(); var error = errorStream.ReadToEnd().Trim(); process.WaitForExit(); if (!string.IsNullOrEmpty(error)) throw new InvalidProgramException(error); } return result; } static readonly string tempFile = Path.GetTempFileName(); static void CompilationTest() { #region ALU #region ADC Test("adc al, 1", new AddWithCarry(GeneralPurposeRegister.AL, (byte)1)); Test("adc rcx, 1", new AddWithCarry(GeneralPurposeRegister.RCX, (byte)1)); Test("adc rcx, 0xFFF", new AddWithCarry(GeneralPurposeRegister.RCX, 0xFFF)); Test("adc [rip + 1], rax", new AddWithCarry(new MemoryOperand() { RipBased = true, Displacement = 1, }, GeneralPurposeRegister.RAX)); Test("adc [rax], rax", new AddWithCarry(new MemoryOperand() { Base = GeneralPurposeRegister.RAX, }, GeneralPurposeRegister.RAX)); Test("adc [rax + 3], r11", new AddWithCarry(new MemoryOperand() { Displacement = 3, Base = GeneralPurposeRegister.RAX, }, GeneralPurposeRegister.R11)); Test("adc [rsp + 0xFFF], eax", new AddWithCarry(new MemoryOperand() { Displacement = 0xFFF, Base = GeneralPurposeRegister.RSP, }, GeneralPurposeRegister.EAX)); Test("adc al, [rax*2 + r11]", new AddWithCarry(GeneralPurposeRegister.AL, new MemoryOperand() { Base = GeneralPurposeRegister.R11, Index = GeneralPurposeRegister.RAX, Scale = 2, })); Test(new AddWithCarry(GeneralPurposeRegister.RAX, GeneralPurposeRegister.RBP)); Test(new AddWithCarry(GeneralPurposeRegister.RBP, GeneralPurposeRegister.RSP)); #endregion #region AND Test("and al, 1", new And(GeneralPurposeRegister.AL, (byte)1)); Test("and rcx, 1", new And(GeneralPurposeRegister.RCX, (byte)1)); Test("and rcx, 0xFFF", new And(GeneralPurposeRegister.RCX, 0xFFF)); Test("and [rip + 1], rax", new And(new MemoryOperand() { RipBased = true, Displacement = 1, }, GeneralPurposeRegister.RAX)); Test("and [rax], rax", new And(new MemoryOperand() { Base = GeneralPurposeRegister.RAX, }, GeneralPurposeRegister.RAX)); Test("and [rax + 3], r11", new And(new MemoryOperand() { Displacement = 3, Base = GeneralPurposeRegister.RAX, }, GeneralPurposeRegister.R11)); Test("and [rsp + 0xFFF], eax", new And(new MemoryOperand() { Displacement = 0xFFF, Base = GeneralPurposeRegister.RSP, }, GeneralPurposeRegister.EAX)); Test("and al, [rax*2 + r11]", new And(GeneralPurposeRegister.AL, new MemoryOperand() { Base = GeneralPurposeRegister.R11, Index = GeneralPurposeRegister.RAX, Scale = 2, })); #endregion #region CMP Test(new Compare(GeneralPurposeRegister.AL, (byte)1)); Test(new Compare(GeneralPurposeRegister.RCX, (byte)1)); Test(new Compare(GeneralPurposeRegister.R10, 0xFFF)); Test(new Compare(new MemoryOperand(){ Base = GeneralPurposeRegister.RAX, Index = GeneralPurposeRegister.RAX, Scale = 8, }, GeneralPurposeRegister.R12)); #endregion #endregion #region Stack #region POP Test("pop rax", new Pop(GeneralPurposeRegister.RAX)); Test("pop r12", new Pop(GeneralPurposeRegister.R12)); Test("pop qword [rax]", new Pop(new MemoryOperand() { Base = GeneralPurposeRegister.RAX, })); Test("pop qword [rip + 1]", new Pop(new MemoryOperand() { Displacement = 1, RipBased = true, })); Test("pop qword [rax*2 + r11]", new Pop( new MemoryOperand() { Base = GeneralPurposeRegister.R11, Index = GeneralPurposeRegister.RAX, Scale = 2, })); Test("pop qword [rsp + 0xFFF]", new Pop( new MemoryOperand() { Displacement = 0xFFF, Base = GeneralPurposeRegister.RSP, })); Test("pop qword [r12*8 + r11]", new Pop( new MemoryOperand() { Base = GeneralPurposeRegister.R11, Index = GeneralPurposeRegister.R12, Scale = 8, })); #endregion #region PUSH Test("push 1", new Push(1)); Test("push qword [r12*8 + r11]", new Push( new MemoryOperand() { Base = GeneralPurposeRegister.R11, Index = GeneralPurposeRegister.R12, Scale = 8, })); #endregion #endregion #region Control Transfers #region Unconditional Jumps Test("jmp 7", new Jump(5)); Test("jmp 0xFFF", new Jump(0xFFA)); Test("call rax", new Call(GeneralPurposeRegister.RAX)); Test("call qword [r12*8 + r11]", new Call(new MemoryOperand() { Base = GeneralPurposeRegister.R11, Index = GeneralPurposeRegister.R12, Scale = 8, })); Test(new Return()); Test(new Return(64)); #endregion #region Conditional Jumps Test("je 7", new JumpIfEqual(5)); Test("je 0x1005", new JumpIfEqual(0xFFF)); #warning FASM's immediates in Jumps are counted from current instruction IP #endregion #endregion #region MOV Test(new Move(GeneralPurposeRegister.R10, GeneralPurposeRegister.RSI)); Test(new Move(GeneralPurposeRegister.R10, new MemoryOperand() { Base = GeneralPurposeRegister.RAX, Index = GeneralPurposeRegister.RBP, Scale = 4, })); //Test(new Move(new MemoryOperand() { // Base = GeneralPurposeRegister.R11, // Index = GeneralPurposeRegister.RSP, // Scale = 2, //}, GeneralPurposeRegister.R11)); //Invalid Address #endregion MOV } static void Test(ProcessorInstruction my_code) { Test(null, my_code); } static void Test( string xxx, ProcessorInstruction my_code) { string fasm_code = my_code.ToFASM(); using (var writer = new StreamWriter(tempFile)) { writer.WriteLine("use64"); writer.WriteLine(fasm_code); writer.Close(); } var asm_out = RunFasm(tempFile, "fasm.out"); byte[] fasm_out; using (var stream = new FileStream("fasm.out", FileMode.Open, FileAccess.Read, FileShare.Read)) { fasm_out = new byte[stream.Length]; stream.Read(fasm_out, 0, fasm_out.Length); } byte[] my_out; using (var stream = new MemoryStream()) { my_code.Compile(stream); my_out = stream.ToArray(); } if (fasm_out.Length != my_out.Length) throw new InvalidProgramException(); for (int i = 0; i < fasm_out.Length; i++) if (fasm_out[i] != my_out[i]) throw new InvalidProgramException(i.ToString()); Console.WriteLine("{0}: passed", fasm_code); } } }