mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-20 12:58:39 +00:00
274 lines
7.7 KiB
C#
274 lines
7.7 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|