mirror of
https://github.com/danbulant/Cosmos
synced 2026-06-01 05:40:29 +00:00
[-] some unnecessary overloads [+] overloaded operators in order to allow defining memory operands easily [+] overloaded automatic conversions to allow operands [+] Labels support [+] code generator based on F# started
319 lines
8.6 KiB
C#
319 lines
8.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Diagnostics;
|
|
|
|
namespace Lost.JIT.AMD64
|
|
{
|
|
[Serializable]
|
|
public abstract class AluInstruction: ProcessorInstruction
|
|
{
|
|
public AluInstruction(InstructionOperand dest, InstructionOperand source)
|
|
{
|
|
this.Dest = dest;
|
|
this.Source = source;
|
|
}
|
|
|
|
public InstructionOperand Dest { get; private set; }
|
|
public InstructionOperand Source { get; private set; }
|
|
|
|
public override void Compile(Stream destStream)
|
|
{
|
|
#region INSTR dest, imm
|
|
if (Source is ImmediateOperand)
|
|
{
|
|
var source = Source as ImmediateOperand;
|
|
if (Dest is GeneralPurposeRegister)
|
|
{
|
|
var dest = Dest as GeneralPurposeRegister;
|
|
#region INSTR accum, imm
|
|
if (dest.Register == Registers.AX && (dest.Size == source.Size
|
|
|| (dest.Size == 8 && source.Size == 4)))
|
|
{
|
|
switch (dest.Size)
|
|
{
|
|
case 1:
|
|
destStream.WriteByte(AccumulatorOpCode);
|
|
destStream.WriteByte(source.Value);
|
|
return;
|
|
case 2:
|
|
destStream.WriteByte(OperandSizeOverride);
|
|
destStream.WriteByte(AccumulatorOpCode + 1);
|
|
destStream.WriteShort(source.Value);
|
|
return;
|
|
case 4:
|
|
destStream.WriteByte(AccumulatorOpCode + 1);
|
|
destStream.WriteInt(source.Value);
|
|
return;
|
|
case 8:
|
|
destStream.WriteByte((byte)Rex.Wide);
|
|
destStream.WriteByte(AccumulatorOpCode + 1);
|
|
destStream.WriteInt(source.Value);
|
|
return;
|
|
}
|
|
throw new NotSupportedException("Supported operand sizes are 1, 2, 4, and 8 bytes");
|
|
}
|
|
#endregion
|
|
|
|
Rex rex = Rex.None;
|
|
if (dest.Register.IsNew()) rex |= Rex.NewRegRM;
|
|
|
|
#region INSTR regX, immX //reg64, imm32
|
|
if (dest.Size == source.Size || (dest.Size == 8 && source.Size == 4))
|
|
{
|
|
switch (dest.Size)
|
|
{
|
|
case 1:
|
|
if (rex != Rex.None) destStream.WriteByte((byte)rex);
|
|
destStream.WriteByte(ImmediateOpCode);
|
|
destStream.WriteByte(ModRM(ImmediateExt, dest.Register));
|
|
destStream.WriteByte(source.Value);
|
|
return;
|
|
case 2:
|
|
destStream.WriteByte(OperandSizeOverride);
|
|
if (rex != Rex.None) destStream.WriteByte((byte)rex);
|
|
destStream.WriteByte(ImmediateOpCode + 1);
|
|
destStream.WriteByte(ModRM(ImmediateExt, dest.Register));
|
|
destStream.WriteShort(source.Value);
|
|
return;
|
|
case 4:
|
|
if (rex != Rex.None) destStream.WriteByte((byte)rex);
|
|
destStream.WriteByte(ImmediateOpCode + 1);
|
|
destStream.WriteByte(ModRM(ImmediateExt, dest.Register));
|
|
destStream.WriteInt(source.Value);
|
|
return;
|
|
case 8:
|
|
rex |= Rex.Wide;
|
|
destStream.WriteByte((byte)rex);
|
|
destStream.WriteByte(ImmediateOpCode + 1);
|
|
destStream.WriteByte(ModRM(ImmediateExt, dest.Register));
|
|
destStream.WriteInt(source.Value);
|
|
return;
|
|
}
|
|
throw new NotImplementedException("Supported operand sizes are 1, 2, 4, and 8 bytes");
|
|
}
|
|
#endregion
|
|
|
|
#region INST reg, imm8
|
|
if (source.Size == 1)
|
|
{
|
|
switch (dest.Size)
|
|
{
|
|
case 2:
|
|
destStream.WriteByte(OperandSizeOverride);
|
|
goto case 4;
|
|
case 4:
|
|
if (rex != Rex.None) destStream.WriteByte((byte)rex);
|
|
destStream.WriteByte(ImmediateOpCode + 3);
|
|
destStream.WriteByte(ModRM(ImmediateExt, dest.Register));
|
|
destStream.WriteByte(source.Value);
|
|
return;
|
|
case 8:
|
|
rex |= Rex.Wide;
|
|
goto case 4;
|
|
}
|
|
throw new NotSupportedException();
|
|
}
|
|
#endregion
|
|
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
}
|
|
#endregion
|
|
|
|
int opcode_base = RegisterOpCode;
|
|
#region OP regX, regX
|
|
if (Dest is GeneralPurposeRegister && Source is GeneralPurposeRegister)
|
|
{
|
|
var dest = Dest as GeneralPurposeRegister;
|
|
var source = Source as GeneralPurposeRegister;
|
|
if (dest.Size != source.Size) throw new NotSupportedException();
|
|
Rex rex = Rex.None;
|
|
if (dest.Register.IsNew()) rex |= Rex.NewRegRM;
|
|
if (source.Register.IsNew()) rex |= Rex.Reg;
|
|
switch (dest.Size)
|
|
{
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
destStream.WriteByte(OperandSizeOverride);
|
|
goto case 4;
|
|
case 4:
|
|
opcode_base++;
|
|
break;
|
|
case 8:
|
|
rex |= Rex.Wide;
|
|
goto case 4;
|
|
default:
|
|
throw new NotSupportedException();
|
|
}
|
|
if (rex != Rex.None) destStream.WriteRex(rex);
|
|
destStream.WriteByte(opcode_base);
|
|
WriteOperands(source.Register, dest.Register, destStream);
|
|
return;
|
|
}
|
|
#endregion OP regX, regX
|
|
|
|
#region OP memX/regX, regX (OP regX, memX/regX)
|
|
var memory = Dest is GeneralPurposeRegister ? Source as MemoryOperand : Dest as MemoryOperand;
|
|
var reg = Dest is GeneralPurposeRegister ? Dest as GeneralPurposeRegister : Source as GeneralPurposeRegister;
|
|
if (Dest is GeneralPurposeRegister) opcode_base += 2;
|
|
|
|
#region RIP-Relative addressing
|
|
if (memory.RipBased)
|
|
{
|
|
Rex rex = Rex.None;
|
|
if (reg.Register.IsNew()) rex |= Rex.Reg;
|
|
switch (reg.Size)
|
|
{
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
destStream.WriteByte(OperandSizeOverride);
|
|
goto case 4;
|
|
case 4:
|
|
opcode_base++;
|
|
break;
|
|
case 8:
|
|
rex |= Rex.Wide;
|
|
goto case 4;
|
|
default:
|
|
throw new NotSupportedException();
|
|
}
|
|
if (rex != Rex.None) destStream.WriteByte((byte)rex);
|
|
destStream.WriteByte(opcode_base);
|
|
ModRM(reg.Register, memory.Displacement, destStream);
|
|
return;
|
|
}
|
|
#endregion
|
|
|
|
#region SIB addressing
|
|
if (memory.RequiresSIB())
|
|
{
|
|
Rex rex = Rex.None;
|
|
if (reg.Register.IsNew()) rex |= Rex.Reg;
|
|
if ((memory.Base != null) && memory.Base.Register.IsNew()) rex |= Rex.B;
|
|
|
|
if ((memory.Base != null) && (memory.Base.Register == Registers.SP))
|
|
memory.Index = GeneralPurposeRegister.SP;
|
|
|
|
if ((memory.Index != null) && memory.Index.Register.IsNew()) rex |= Rex.NewRegIndex;
|
|
switch (reg.Size)
|
|
{
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
destStream.WriteByte(OperandSizeOverride);
|
|
goto case 4;
|
|
case 4:
|
|
opcode_base++;
|
|
break;
|
|
case 8:
|
|
rex |= Rex.Wide;
|
|
goto case 4;
|
|
default:
|
|
throw new NotSupportedException();
|
|
}
|
|
if (rex != Rex.None) destStream.WriteByte((byte)rex);
|
|
destStream.WriteByte(opcode_base);
|
|
SIB(reg.Register, memory, destStream);
|
|
return;
|
|
}
|
|
#endregion SIB addressing
|
|
|
|
#region mem is [reg]
|
|
if (memory.Index == null)
|
|
{
|
|
Rex rex = Rex.None;
|
|
if (reg.Register.IsNew()) rex |= Rex.Reg;
|
|
if (memory.Base.Register.IsNew()) rex |= Rex.B;
|
|
switch (reg.Size)
|
|
{
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
destStream.WriteByte(OperandSizeOverride);
|
|
goto case 4;
|
|
case 4:
|
|
opcode_base++;
|
|
break;
|
|
case 8:
|
|
rex |= Rex.Wide;
|
|
goto case 4;
|
|
default:
|
|
throw new NotSupportedException();
|
|
}
|
|
if (rex != Rex.None) destStream.WriteByte((byte)rex);
|
|
destStream.WriteByte(opcode_base);
|
|
int modRM = 0;
|
|
if (memory.Displacement == 0)
|
|
{
|
|
modRM += reg.Register.GetIndex() << 3;
|
|
modRM += memory.Base.Register.GetIndex();
|
|
destStream.WriteByte(modRM);
|
|
return;
|
|
}
|
|
switch (memory.DisplacementSize)
|
|
{
|
|
case 1:
|
|
modRM = 1 << 6;
|
|
break;
|
|
case 4:
|
|
modRM = 2 << 6;
|
|
break;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
modRM += reg.Register.GetIndex() << 3;
|
|
modRM += memory.Base.Register.GetIndex();
|
|
destStream.WriteByte(modRM);
|
|
switch (memory.DisplacementSize)
|
|
{
|
|
case 1:
|
|
destStream.WriteByte(memory.Displacement);
|
|
break;
|
|
case 4:
|
|
destStream.WriteInt(memory.Displacement);
|
|
break;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endregion mem is [reg]
|
|
#endregion OP memX/regX, regX (OP regX, memX/regX)
|
|
|
|
throw new NotImplementedException();
|
|
}
|
|
public override int? Size
|
|
{
|
|
get { throw new NotImplementedException(); }
|
|
}
|
|
|
|
public override string ToFASM()
|
|
{
|
|
return string.Format("{0} {1}, {2}", OpCodeFASM, Dest, Source);
|
|
}
|
|
|
|
/// <summary>
|
|
/// INSTR accum, imm
|
|
/// </summary>
|
|
public abstract byte AccumulatorOpCode { get; }
|
|
/// <summary>
|
|
/// INSTR reg/mem, imm
|
|
/// </summary>
|
|
public abstract byte ImmediateOpCode { get; }
|
|
/// <summary>
|
|
/// INSTR reg/mem, imm
|
|
/// </summary>
|
|
public abstract byte ImmediateExt { get; }
|
|
/// <summary>
|
|
/// INSTR reg, reg/mem
|
|
/// </summary>
|
|
public abstract byte RegisterOpCode { get; }
|
|
}
|
|
}
|