mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-22 13:58:47 +00:00
[+] Every instruction supports converting to FASM code now.
This commit is contained in:
parent
10c45343bf
commit
c2cf1cefcb
24 changed files with 219 additions and 592 deletions
|
|
@ -48,5 +48,10 @@ namespace Lost.JIT.AMD64
|
|||
{
|
||||
get { return 0x10; }
|
||||
}
|
||||
|
||||
public override string OpCodeFASM
|
||||
{
|
||||
get { return "adc"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,5 +49,10 @@ namespace Lost.JIT.AMD64
|
|||
{
|
||||
get { return 0x20; }
|
||||
}
|
||||
|
||||
public override string OpCodeFASM
|
||||
{
|
||||
get { return "and"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,5 +18,14 @@ namespace Lost.JIT.AMD64
|
|||
{
|
||||
destStream.WriteByte(0xCC);
|
||||
}
|
||||
|
||||
public override string OpCodeFASM
|
||||
{
|
||||
get { return "int 3"; }
|
||||
}
|
||||
public override string ToFASM()
|
||||
{
|
||||
return OpCodeFASM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,5 +68,15 @@ namespace Lost.JIT.AMD64
|
|||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string OpCodeFASM
|
||||
{
|
||||
get { return "call"; }
|
||||
}
|
||||
|
||||
public override string ToFASM()
|
||||
{
|
||||
return string.Format(Dest is MemoryOperand? "call qword {0}": "call {0}", Dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
58
source/Tests/MathTest/Lost/JIT/AMD64/Compare.cs
Normal file
58
source/Tests/MathTest/Lost/JIT/AMD64/Compare.cs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace Lost.JIT.AMD64
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class Compare : DestSourceInstruction
|
||||
{
|
||||
public Compare(InstructionOperand dest, InstructionOperand source)
|
||||
: base(dest, source)
|
||||
{
|
||||
}
|
||||
public Compare(InstructionOperand dest, byte source)
|
||||
: base(dest, source)
|
||||
{
|
||||
}
|
||||
public Compare(InstructionOperand dest, short source)
|
||||
: base(dest, source)
|
||||
{
|
||||
}
|
||||
public Compare(InstructionOperand dest, int source)
|
||||
: base(dest, source)
|
||||
{
|
||||
}
|
||||
public Compare(InstructionOperand dest, long source)
|
||||
: base(dest, source)
|
||||
{
|
||||
}
|
||||
|
||||
public override byte AccumulatorOpCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0x3C;
|
||||
}
|
||||
}
|
||||
public override byte ImmediateExt
|
||||
{
|
||||
get { return 0x07; }
|
||||
}
|
||||
public override byte ImmediateOpCode
|
||||
{
|
||||
get { return 0x80; }
|
||||
}
|
||||
public override byte RegisterOpCode
|
||||
{
|
||||
get { return 0x38; }
|
||||
}
|
||||
|
||||
public override string OpCodeFASM
|
||||
{
|
||||
get { return "cmp"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ namespace Lost.JIT.AMD64
|
|||
|
||||
public override void Compile(Stream destStream)
|
||||
{
|
||||
if (TargetOffset.FitsInByte())
|
||||
if (TargetOffset.FitsInSByte())
|
||||
{
|
||||
destStream.WriteByte(OpcodeBase);
|
||||
destStream.WriteSByte(TargetOffset);
|
||||
|
|
@ -37,5 +37,11 @@ namespace Lost.JIT.AMD64
|
|||
}
|
||||
|
||||
protected abstract int OpcodeBase { get; }
|
||||
|
||||
public override string ToFASM()
|
||||
{
|
||||
return string.Format("{0} {1}", OpCodeFASM,
|
||||
TargetOffset + (TargetOffset.FitsInSByte() ? 2 : 6));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -277,6 +277,11 @@ namespace Lost.JIT.AMD64
|
|||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public override string ToFASM()
|
||||
{
|
||||
return string.Format("{0} {1}, {2}", OpCodeFASM, Dest, Source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// INSTR accum, imm
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using System.Text;
|
|||
namespace Lost.JIT.AMD64
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class GeneralPurposeRegister: InstructionOperand
|
||||
public sealed class GeneralPurposeRegister: InstructionOperand, IEquatable<GeneralPurposeRegister>
|
||||
{
|
||||
public GeneralPurposeRegister(Registers register, int size)
|
||||
{
|
||||
|
|
@ -17,6 +17,28 @@ namespace Lost.JIT.AMD64
|
|||
public int Size { get; private set; }
|
||||
public Registers Register { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Register.IsNew() && (Size == 8)) return Register.ToString();
|
||||
if (!Register.IsNew())
|
||||
{
|
||||
switch(Size)
|
||||
{
|
||||
case 2:
|
||||
return Register.ToString();
|
||||
case 4:
|
||||
return 'E' + Register.ToString();
|
||||
case 8:
|
||||
return 'R' + Register.ToString();
|
||||
case 1:
|
||||
return Register.ToString().Substring(0, 1)+ 'L';
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static readonly GeneralPurposeRegister RAX = new GeneralPurposeRegister(Registers.AX, 8);
|
||||
public static readonly GeneralPurposeRegister RBX = new GeneralPurposeRegister(Registers.BX, 8);
|
||||
public static readonly GeneralPurposeRegister RCX = new GeneralPurposeRegister(Registers.CX, 8);
|
||||
|
|
@ -60,5 +82,15 @@ namespace Lost.JIT.AMD64
|
|||
//public static readonly GeneralPurposeRegister DI = new GeneralPurposeRegister(Registers.DI, 1);
|
||||
//public static readonly GeneralPurposeRegister BP = new GeneralPurposeRegister(Registers.BP, 1);
|
||||
//public static readonly GeneralPurposeRegister SP = new GeneralPurposeRegister(Registers.SP, 1);
|
||||
|
||||
#region IEquatable<GeneralPurposeRegister> Members
|
||||
|
||||
public bool Equals(GeneralPurposeRegister other)
|
||||
{
|
||||
if (other == null) return false;
|
||||
return (other.Register == Register) && (other.Size == Size);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,5 +32,11 @@ namespace Lost.JIT.AMD64
|
|||
|
||||
public long Value { get; private set; }
|
||||
public int Size { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string digs = (Size * 2).ToString();
|
||||
return "0x" + Value.ToString("X" + digs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,5 +40,21 @@ namespace Lost.JIT.AMD64
|
|||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string OpCodeFASM
|
||||
{
|
||||
get { return "jmp"; }
|
||||
}
|
||||
|
||||
public override string ToFASM()
|
||||
{
|
||||
if (Dest is ImmediateOperand)
|
||||
{
|
||||
var dest = Dest as ImmediateOperand;
|
||||
return string.Format("jmp {0}", dest.Value + (dest.Value.FitsInSByte()? 2: 5));
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,10 @@ namespace Lost.JIT.AMD64
|
|||
{
|
||||
get { return 0x74; }
|
||||
}
|
||||
|
||||
public override string OpCodeFASM
|
||||
{
|
||||
get { return "je"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,5 +44,24 @@ namespace Lost.JIT.AMD64
|
|||
}
|
||||
return Base != null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (RipBased)
|
||||
return string.Format("[RIP + 0x{0}]", Displacement.ToString("X8"));
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("[{0}", Base);
|
||||
if (Index != null)
|
||||
if (Scale > 1)
|
||||
sb.AppendFormat(" + {0}*{1}", Index, Scale);
|
||||
else
|
||||
sb.AppendFormat(" + {0}", Index);
|
||||
|
||||
if (Displacement != 0)
|
||||
sb.AppendFormat(" + 0x{0}", Displacement.ToString("X16"));
|
||||
sb.Append(']');
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,5 +52,14 @@ namespace Lost.JIT.AMD64
|
|||
}
|
||||
throw new InvalidProgramException();
|
||||
}
|
||||
|
||||
public override string OpCodeFASM
|
||||
{
|
||||
get { return "pop"; }
|
||||
}
|
||||
public override string ToFASM()
|
||||
{
|
||||
return string.Format(Dest is MemoryOperand? "pop qword {0}":"pop {0}", Dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,15 @@ namespace Lost.JIT.AMD64
|
|||
public const byte AddressSizeOverride = 0x67;
|
||||
|
||||
public abstract int? Size { get; }
|
||||
public abstract string ToFASM();
|
||||
public abstract string OpCodeFASM { get; }
|
||||
public abstract void Compile(Stream destStream);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ToFASM();
|
||||
}
|
||||
|
||||
public static Rex NeedsRex(MemoryOperand memory)
|
||||
{
|
||||
if (memory.RipBased) return Rex.None;
|
||||
|
|
|
|||
|
|
@ -73,5 +73,14 @@ namespace Lost.JIT.AMD64
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string OpCodeFASM
|
||||
{
|
||||
get { return "push"; }
|
||||
}
|
||||
public override string ToFASM()
|
||||
{
|
||||
return string.Format(Source is MemoryOperand ? "push qword {0}" : "push {0}", Source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Lost.JIT.AMD64OLD
|
||||
{
|
||||
[Flags]
|
||||
enum Register: byte
|
||||
{
|
||||
Legacy = 0x07,
|
||||
AX = 0x00,
|
||||
BX = 0x03,
|
||||
CX = 0x01,
|
||||
DX = 0x02,
|
||||
SP = 0x04,
|
||||
BP = 0x05,
|
||||
SI = 0x06,
|
||||
DI = 0x07,
|
||||
R8 = 0x08,
|
||||
R9 = 0x09,
|
||||
R10 = 0x0A,
|
||||
R11 = 0x0B,
|
||||
R12 = 0x0C,
|
||||
R13 = 0x0D,
|
||||
R14 = 0x0E,
|
||||
R15 = 0x0F,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum Rex : byte
|
||||
{
|
||||
None = 0x40,
|
||||
/// <summary>
|
||||
/// Use 64-bit operand size
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Setting the REX.W bit to 1 specifies a 64-bit operand size. Like the
|
||||
/// existing 66h operand-size prefix, the REX 64-bit operand-size override has no effect on byte
|
||||
/// operations. For non-byte operations, the REX operand-size override takes precedence over the 66h
|
||||
/// prefix. If a 66h prefix is used together with a REX prefix that has the REX.W bit set to 1, the 66h
|
||||
/// prefix is ignored. However, if a 66h prefix is used together with a REX prefix that has the REX.W bit
|
||||
/// cleared to 0, the 66h prefix is not ignored and the operand size becomes 16 bits.
|
||||
///</remarks>
|
||||
Wide = None | (1 << 3),
|
||||
/// <summary>
|
||||
/// Reg index from ModRM extension.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The REX.R bit adds a 1-bit (high) extension to the ModRM reg field (page 17)
|
||||
/// when that field encodes a GPR, XMM, control, or debug register. REX.R does not modify ModRM reg
|
||||
/// when that field specifies other registers or opcodes. REX.R is ignored in such cases.
|
||||
/// </remarks>
|
||||
Reg = None | (1 << 2),
|
||||
/// <summary>
|
||||
/// SIB index register extension.
|
||||
/// </summary>
|
||||
Index = None | (1 << 1),
|
||||
/// <summary>
|
||||
/// SIB index register extension.
|
||||
/// Use Index mnenonic instead.
|
||||
/// </summary>
|
||||
X = None | (1 << 1),
|
||||
/// <summary>
|
||||
/// Extension of the ModRM r/m field1, SIB base field, or opcode reg field,
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The REX.B bit adds a 1-bit (high) extension to either the ModRM r/m field to specify
|
||||
/// a GPR or XMM register, or to the SIB base field to specify a GPR. (See Table 2-2 on page 40 for more
|
||||
/// about the REX.B bit.)
|
||||
/// </remarks>
|
||||
B = None | (1 << 0),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Lost.JIT.AMD64OLD
|
||||
{
|
||||
[Obsolete("")]
|
||||
class ImmediateOperand: InstructionOperand
|
||||
{
|
||||
internal ImmediateOperand(int size, ulong value):base(value.ToString(string.Format("0X{0}", size*2)))
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
byte b = checked((byte)value);
|
||||
break;
|
||||
case 2:
|
||||
ushort s = checked((ushort)value);
|
||||
break;
|
||||
case 4:
|
||||
uint i = checked((uint)value);
|
||||
break;
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("incorrect operand size");
|
||||
}
|
||||
_size = size;
|
||||
_value = (long)value;
|
||||
}
|
||||
|
||||
int _size;
|
||||
long _value;
|
||||
|
||||
/// <summary>
|
||||
/// Size of immediate operand, in bytes
|
||||
/// </summary>
|
||||
public int Size
|
||||
{
|
||||
get { return _size; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Value of immediate operand
|
||||
/// </summary>
|
||||
public long Value
|
||||
{
|
||||
get { return _value; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var val = _value.ToString(string.Format("X{0}", Size*2));
|
||||
return string.Format("0{0}h", val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Lost.JIT.AMD64OLD
|
||||
{
|
||||
class InstructionLabel
|
||||
{
|
||||
internal InstructionLabel(string name)
|
||||
{
|
||||
_name = name;
|
||||
|
||||
#warning TODO: check label for validity
|
||||
}
|
||||
|
||||
static readonly Regex _regex = new Regex(
|
||||
@"^(\s*(?<label>\w+)\u003A)*(?<rest>.*$)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
public static InstructionLabel[] ExtractLabels(ref string instruction)
|
||||
{
|
||||
var match = _regex.Match(instruction);
|
||||
|
||||
var result = new InstructionLabel[match.Groups["label"].Captures.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = new InstructionLabel(match.Groups["label"].Captures[i].Value);
|
||||
|
||||
instruction = match.Groups["rest"].Value;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
} string _name;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}:", Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lost.JIT.AMD64OLD
|
||||
{
|
||||
class InstructionOperand
|
||||
{
|
||||
public InstructionOperand(string value)
|
||||
{
|
||||
_stringValue = value;
|
||||
}
|
||||
|
||||
static readonly Regex _regex = new Regex(
|
||||
@"^(\s*(?<operand>(\w|\[|\]|\+|\-)+)\s*\,)*\s*$",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
internal static InstructionOperand[] GetOperands(string code)
|
||||
{
|
||||
if (code.Trim() != "") code += ",";
|
||||
var match = _regex.Match(code);
|
||||
|
||||
var result = new InstructionOperand[match.Groups["operand"].Captures.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = InstructionOperand.Parse(match.Groups["operand"].Captures[i].Value);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static InstructionOperand Parse(string operand)
|
||||
{
|
||||
return new InstructionOperand(operand);
|
||||
}
|
||||
|
||||
string _stringValue;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _stringValue;
|
||||
}
|
||||
|
||||
#region Parsing stuff
|
||||
public bool IsGeneralPurposeRegister
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isGeneralPurposeRegister == null)
|
||||
throw new NotImplementedException();
|
||||
return _isGeneralPurposeRegister.Value;
|
||||
}
|
||||
} bool? _isGeneralPurposeRegister;
|
||||
|
||||
public int Size
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_size == null)
|
||||
throw new NotImplementedException();
|
||||
|
||||
return _size.Value;
|
||||
}
|
||||
} int? _size;
|
||||
public ulong Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_value == null) throw new NotImplementedException();
|
||||
|
||||
return _value.Value;
|
||||
}
|
||||
} ulong? _value;
|
||||
|
||||
public Register Register
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_register == null)
|
||||
throw new NotImplementedException();
|
||||
|
||||
return _register.Value;
|
||||
}
|
||||
} Register? _register;
|
||||
|
||||
public bool IsImmediate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_immediate == null)
|
||||
throw new NotImplementedException();
|
||||
|
||||
return _immediate.Value;
|
||||
}
|
||||
} bool? _immediate;
|
||||
|
||||
public void WriteTo(Stream dest)
|
||||
{
|
||||
Debug.Assert(IsImmediate);
|
||||
|
||||
ulong value = Value;
|
||||
for (int i = 0; i < Size; i++, value >>= 8)
|
||||
dest.WriteByte((byte)(value & 0xFF));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lost.JIT.AMD64OLD
|
||||
{
|
||||
class InstructionPrefix
|
||||
{
|
||||
public InstructionPrefix(string name)
|
||||
{
|
||||
if (!_prefixes.Contains(name))
|
||||
throw new ArgumentException("name");
|
||||
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
} string _name;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
#region Parsing
|
||||
static readonly HashSet<string> _prefixes = new HashSet<string>() {
|
||||
"lock",
|
||||
};
|
||||
static InstructionPrefix()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var prefix in _prefixes) sb.AppendFormat("{0}|", prefix);
|
||||
|
||||
Debug.Assert(sb.Length > 0);
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
|
||||
_regex = new Regex(
|
||||
string.Format(@"^(\s*(?<prefix>({0})))*(?<rest>.*$)", sb),
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
}
|
||||
static readonly Regex _regex;
|
||||
|
||||
public static InstructionPrefix[] ExtractPrefixes(ref string instruction)
|
||||
{
|
||||
var match = _regex.Match(instruction);
|
||||
|
||||
var result = new InstructionPrefix[match.Groups["prefix"].Captures.Count];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = new InstructionPrefix(match.Groups["prefix"].Captures[i].Value);
|
||||
|
||||
instruction = match.Groups["rest"].Value;
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Lost.JIT.AMD64OLD
|
||||
{
|
||||
static class ModRM
|
||||
{
|
||||
public static void EncodeRegisters(this Stream dest, Register destReg, Register sourceReg)
|
||||
{
|
||||
destReg &= Register.Legacy;
|
||||
sourceReg &= Register.Legacy;
|
||||
|
||||
byte result = 0xC0; //11000000b
|
||||
result |= (byte)(((byte)destReg) << 3 | (byte)sourceReg);
|
||||
dest.WriteByte(result);
|
||||
}
|
||||
public static void EncodeIndirectMemory(this Stream dest, byte xcode, Register baseReg)
|
||||
{
|
||||
Debug.Assert((xcode & ~(byte)Register.Legacy) == 0);
|
||||
|
||||
byte modRM =(byte)((xcode << 3) | (byte)baseReg);
|
||||
dest.WriteByte(modRM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lost.JIT.AMD64OLD
|
||||
{
|
||||
class ProcessorInstruction
|
||||
{
|
||||
const byte Lock = 0xF0;
|
||||
const byte OperandSizeOverride = 0x66;
|
||||
const byte AddressSizeOverride = 0x67;
|
||||
const byte Rep = 0xF3;
|
||||
const byte RepE = 0xF3;
|
||||
const byte RepNE = 0xF2;
|
||||
|
||||
internal ProcessorInstruction()
|
||||
{
|
||||
_labels = new HashSet<InstructionLabel>();
|
||||
_prefixes = new HashSet<InstructionPrefix>();
|
||||
}
|
||||
|
||||
internal ProcessorInstruction(string opCode, InstructionOperand[] operands, InstructionLabel[] labels, InstructionPrefix[] prefixes, string comments)
|
||||
{
|
||||
_opCode = opCode;
|
||||
_operands = new List<InstructionOperand>(operands);
|
||||
_labels = new HashSet<InstructionLabel>(labels);
|
||||
_prefixes = new HashSet<InstructionPrefix>(prefixes);
|
||||
_comments = comments;
|
||||
}
|
||||
|
||||
ICollection<InstructionLabel> Labels
|
||||
{
|
||||
get { return _labels; }
|
||||
} HashSet<InstructionLabel> _labels;
|
||||
ICollection<InstructionPrefix> Prefixes
|
||||
{
|
||||
get { return _prefixes; }
|
||||
} HashSet<InstructionPrefix> _prefixes;
|
||||
IList<InstructionOperand> Operands
|
||||
{
|
||||
get { return _operands; }
|
||||
} List<InstructionOperand> _operands;
|
||||
string OpCode
|
||||
{
|
||||
get { return _opCode; }
|
||||
} string _opCode;
|
||||
string Comments
|
||||
{
|
||||
get { return _comments; }
|
||||
} string _comments;
|
||||
|
||||
#region Encoding
|
||||
InstructionOperand Dest
|
||||
{
|
||||
get { return _operands[0]; }
|
||||
}
|
||||
InstructionOperand Source
|
||||
{
|
||||
get { return _operands[1]; }
|
||||
}
|
||||
|
||||
#region Instructions
|
||||
void AddWithCarry(Stream dest)
|
||||
{
|
||||
if (Dest.IsGeneralPurposeRegister)
|
||||
if (Dest.Register == Register.AX && Source.IsImmediate)
|
||||
{
|
||||
switch (Dest.Size)
|
||||
{
|
||||
case 1:
|
||||
if (Source.Size != null)
|
||||
Debug.Assert(Source.Size == 1);
|
||||
|
||||
dest.WriteByte(0x14);
|
||||
break;
|
||||
case 2:
|
||||
if (Source.Size != null)
|
||||
Debug.Assert(Source.Size == 2);
|
||||
|
||||
dest.WriteByte(OperandSizeOverride);
|
||||
dest.WriteByte(0x15);
|
||||
break;
|
||||
case 4:
|
||||
if (Source.Size != null)
|
||||
Debug.Assert(Source.Size == 4);
|
||||
|
||||
dest.WriteByte(0x15);
|
||||
break;
|
||||
case 8:
|
||||
if (Source.Size != null)
|
||||
Debug.Assert(Source.Size == 4);
|
||||
|
||||
dest.WriteByte((byte)Rex.Wide);
|
||||
dest.WriteByte(0x15);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
Source.WriteTo(dest);
|
||||
return;
|
||||
} //adc al/ax/etc, imm
|
||||
if (Source.IsImmediate)
|
||||
{
|
||||
switch (Dest.Size)
|
||||
{
|
||||
case 1:
|
||||
if (Source.Size != null)
|
||||
Debug.Assert(Source.Size == 4);
|
||||
|
||||
dest.WriteByte(AddressSizeOverride);
|
||||
dest.WriteByte(0x80);
|
||||
dest.EncodeIndirectMemory(2, Dest.Register);
|
||||
break;
|
||||
}
|
||||
Source.WriteTo(dest);
|
||||
return;
|
||||
}
|
||||
Debug.Assert(false);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void Encode(Stream dest)
|
||||
{
|
||||
switch (_opCode)
|
||||
{
|
||||
case "adc":
|
||||
AddWithCarry(dest);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(_opCode);
|
||||
}
|
||||
}
|
||||
|
||||
static void EncodeRegisterRegister(Rex rex, Register dest, Register source, byte opcode, Stream stream)
|
||||
{
|
||||
if (rex != Rex.None) stream.WriteByte((byte)rex);
|
||||
stream.WriteByte(opcode);
|
||||
|
||||
stream.EncodeRegisters(dest, source);
|
||||
}
|
||||
static void EncodeRegister(Rex rex, Register dest, byte opcode, Stream stream)
|
||||
{
|
||||
if (rex != Rex.None) stream.WriteByte((byte)rex);
|
||||
|
||||
dest &= Register.Legacy;
|
||||
Debug.Assert((opcode | (byte)Register.Legacy) == 0);
|
||||
opcode |= (byte)dest;
|
||||
|
||||
stream.WriteByte(opcode);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Parsing
|
||||
public static ProcessorInstruction Parse(string code)
|
||||
{
|
||||
var labels = InstructionLabel.ExtractLabels(ref code);
|
||||
var prefixes = InstructionPrefix.ExtractPrefixes(ref code);
|
||||
var instr = ExtractInstruction(ref code);
|
||||
var comments = ExtractComments(ref code);
|
||||
var operands = InstructionOperand.GetOperands(code);
|
||||
|
||||
var result = new ProcessorInstruction(instr.ToUpper(), operands, labels, prefixes, comments);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static readonly Regex _regexComments = new Regex(
|
||||
@"^(?<rest>.*)\/\/\s*(?<comments>.*)\s*$",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
private static string ExtractComments(ref string code)
|
||||
{
|
||||
var match = _regexComments.Match(code);
|
||||
if (match == null) return null;
|
||||
if (!match.Success) return null;
|
||||
|
||||
var value = match.Groups["comments"].Value;
|
||||
code = match.Groups["rest"].Value;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static readonly Regex _regexInstruction = new Regex(
|
||||
@"^\s*(?<instr>\w+)(?<rest>.*$)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
private static string ExtractInstruction(ref string code)
|
||||
{
|
||||
var match = _regexInstruction.Match(code);
|
||||
|
||||
var value = match.Groups["instr"].Value;
|
||||
code = match.Groups["rest"].Value;
|
||||
|
||||
return value;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -52,17 +52,11 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="BinaryStreamExtensions.cs" />
|
||||
<Compile Include="BitUtils.cs" />
|
||||
<Compile Include="JIT\AMD64OLD\Enumerations.cs" />
|
||||
<Compile Include="JIT\AMD64OLD\ImmediateOperand.cs" />
|
||||
<Compile Include="JIT\AMD64OLD\InstructionLabel.cs" />
|
||||
<Compile Include="JIT\AMD64OLD\InstructionOperand.cs" />
|
||||
<Compile Include="JIT\AMD64OLD\InstructionPrefix.cs" />
|
||||
<Compile Include="JIT\AMD64OLD\ModRM.cs" />
|
||||
<Compile Include="JIT\AMD64OLD\ProcessorInstruction.cs" />
|
||||
<Compile Include="JIT\AMD64\AddWithCarry.cs" />
|
||||
<Compile Include="JIT\AMD64\And.cs" />
|
||||
<Compile Include="JIT\AMD64\BreakPoint.cs" />
|
||||
<Compile Include="JIT\AMD64\Call.cs" />
|
||||
<Compile Include="JIT\AMD64\Compare.cs" />
|
||||
<Compile Include="JIT\AMD64\ConditionalJumpInstruction.cs" />
|
||||
<Compile Include="JIT\AMD64\DestSourceInstruction.cs" />
|
||||
<Compile Include="JIT\AMD64\ImmediateOperand.cs" />
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ namespace Lost
|
|||
|
||||
static string RunFasm(string filename, string dest)
|
||||
{
|
||||
const string fasm = @"C:\Programs\Devel\asm\fasmw16726\fasm.exe";
|
||||
//C:\Programs\Devel\asm\fasmw16726\
|
||||
const string fasm = @"fasm.exe";
|
||||
|
||||
var par = new ProcessStartInfo(fasm, string.Format("\"{0}\" \"{1}\"", filename, dest));
|
||||
|
||||
|
|
@ -83,7 +84,7 @@ namespace Lost
|
|||
Test("adc [rsp + 0xFFF], eax",
|
||||
new AddWithCarry(new MemoryOperand() {
|
||||
Displacement = 0xFFF,
|
||||
Base = GeneralPurposeRegister.SP,
|
||||
Base = GeneralPurposeRegister.RSP,
|
||||
}, GeneralPurposeRegister.EAX));
|
||||
|
||||
Test("adc al, [rax*2 + r11]",
|
||||
|
|
@ -95,7 +96,7 @@ namespace Lost
|
|||
}));
|
||||
#endregion
|
||||
|
||||
#region ADC
|
||||
#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));
|
||||
|
|
@ -117,7 +118,7 @@ namespace Lost
|
|||
Test("and [rsp + 0xFFF], eax",
|
||||
new And(new MemoryOperand() {
|
||||
Displacement = 0xFFF,
|
||||
Base = GeneralPurposeRegister.SP,
|
||||
Base = GeneralPurposeRegister.RSP,
|
||||
}, GeneralPurposeRegister.EAX));
|
||||
|
||||
Test("and al, [rax*2 + r11]",
|
||||
|
|
@ -153,7 +154,7 @@ namespace Lost
|
|||
new Pop(
|
||||
new MemoryOperand() {
|
||||
Displacement = 0xFFF,
|
||||
Base = GeneralPurposeRegister.SP,
|
||||
Base = GeneralPurposeRegister.RSP,
|
||||
}));
|
||||
Test("pop qword [r12*8 + r11]",
|
||||
new Pop(
|
||||
|
|
@ -198,8 +199,16 @@ namespace Lost
|
|||
#endregion
|
||||
}
|
||||
|
||||
static void Test(string fasm_code, ProcessorInstruction my_code)
|
||||
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");
|
||||
|
|
|
|||
Loading…
Reference in a new issue