diff --git a/libs/UIX.Asm/Disassembler.cs b/libs/UIX.Asm/Disassembler.cs index 7f9e2d1..ca6d8f0 100644 --- a/libs/UIX.Asm/Disassembler.cs +++ b/libs/UIX.Asm/Disassembler.cs @@ -267,51 +267,8 @@ public class Disassembler // Insert a label to mark the start of the object section. yield return new SectionDirective("object"); - while (reader.CurrentOffset < reader.Size) - { - if (_offsetLabelMap.TryGetValue(reader.CurrentOffset, out var labels)) - foreach (var label in labels) - yield return label; - - var opCode = (OpCode)reader.ReadByte(); - - var instSchema = InstructionSet.InstructionSchema[opCode]; - Operand[] operands = new Operand[instSchema.Length]; - - for (int i = 0; i < instSchema.Length; i++) - { - var operandDataType = instSchema[i]; - Operand operand; - - if (operandDataType == LiteralDataType.ConstantIndex) - { - // Refer to the constant by name rather than index - var constantIndex = reader.ReadUInt16(); - - // TODO: Only generate name if not using a shared binary table - operand = new OperandReference($"const{constantIndex}"); - } - else - { - object literalValue = OperandLiteral.ReduceDataType(operandDataType) switch - { - LiteralDataType.Byte => reader.ReadByte(), - LiteralDataType.UInt16 => reader.ReadUInt16(), - LiteralDataType.UInt32 => reader.ReadUInt32(), - LiteralDataType.Int32 => reader.ReadInt32(), - _ => throw new InvalidOperationException($"Unexpected operand data type '{operandDataType}'") - }; - - operand = new OperandLiteral(literalValue, operandDataType); - } - - operands[i] = operand; - } - - yield return new Instruction(opCode, operands); - } - - yield break; + foreach (var codeItem in ObjectSection.Decode(reader, _offsetLabelMap)) + yield return codeItem; } private QualifiedTypeName GetQualifiedName(TypeSchema schema) diff --git a/libs/UIX.Asm/Lexer.Body.cs b/libs/UIX.Asm/Lexer.Body.cs index 4c15e8e..1a29ee5 100644 --- a/libs/UIX.Asm/Lexer.Body.cs +++ b/libs/UIX.Asm/Lexer.Body.cs @@ -110,7 +110,7 @@ partial class Lexer } } - bodyItem = new Instruction(identifier, operands ?? []) + bodyItem = new Instruction(identifier, operands ?? [], 0) { Line = line, Column = column, diff --git a/libs/UIX.Asm/Models/Instructions.cs b/libs/UIX.Asm/Models/Instructions.cs index bac2204..77f53e2 100644 --- a/libs/UIX.Asm/Models/Instructions.cs +++ b/libs/UIX.Asm/Models/Instructions.cs @@ -1,5 +1,4 @@ using Microsoft.Iris.Markup; -using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -7,18 +6,19 @@ using System.Linq; namespace Microsoft.Iris.Asm.Models; [DebuggerDisplay("{ToString()} " + DebuggerDisplay)] -public record Instruction(string Mnemonic, IEnumerable Operands) : CodeItem +public record Instruction(string Mnemonic, IEnumerable Operands, uint Offset) : CodeItem { - public Instruction(OpCode opCode, OperationType? operationType, IEnumerable Operands) - : this(InstructionSet.GetMnemonic(opCode, operationType), Operands) + public Instruction(OpCode opCode, OperationType? operationType, IEnumerable operands, uint offset) + : this(InstructionSet.GetMnemonic(opCode, operationType), operands, offset) { } - public Instruction(OpCode opCode, IEnumerable Operands) - : this(opCode, null, Operands) + public Instruction(OpCode opCode, IEnumerable operands, uint offset) + : this(opCode, null, operands, offset) { } public OpCode OpCode => InstructionSet.MnemonicToOpCode(Mnemonic); + public OperationType? OperationType => InstructionSet.TryOperationMnemonicToType(Mnemonic); public override string ToString() => ToString(true); @@ -30,24 +30,4 @@ public record Instruction(string Mnemonic, IEnumerable Operands) : Code ? $"{mnemonic} {string.Join(", ", Operands)}" : mnemonic; } - - public static Instruction CreateWithSchema(OpCode opCode, params object[] operands) - { - var schema = InstructionSet.InstructionSchema[opCode]; - if (operands.Length != schema.Length) - throw new ArgumentException($"{opCode} requires {schema.Length} operands, got {operands.Length}"); - - var operandModels = new OperandLiteral[operands.Length]; - for (int i = 0; i < schema.Length; i++) - { - var operandValue = operands[i]; - // Should we verify types? - - operandModels[i] = new(operandValue, schema[i]); - } - - var operationType = operands.Length > 0 ? operands[0] as OperationType? : null; - - return new Instruction(opCode, operationType, operandModels); - } } diff --git a/libs/UIX.Asm/Models/Operands.cs b/libs/UIX.Asm/Models/Operands.cs index 99e80c5..7255988 100644 --- a/libs/UIX.Asm/Models/Operands.cs +++ b/libs/UIX.Asm/Models/Operands.cs @@ -49,11 +49,14 @@ public record OperandLiteral : Operand public record OperandReference : Operand { - public OperandReference(string constantName) : base(constantName, constantName) + public OperandReference(string constantName, int index = -1) : base(constantName, constantName) { + Index = index; } public string ConstantName => Content; + public int Index { get; } + public override string ToString() => $"@{base.ToString()}"; } diff --git a/libs/UIX.Asm/ObjectSection.cs b/libs/UIX.Asm/ObjectSection.cs index f2006a0..d1a6c02 100644 --- a/libs/UIX.Asm/ObjectSection.cs +++ b/libs/UIX.Asm/ObjectSection.cs @@ -88,4 +88,52 @@ public class ObjectSection return writer.CreateReader(); } + + public static IEnumerable Decode(ByteCodeReader reader, Dictionary> offsetLabelMap = null) + { + while (reader.CurrentOffset < reader.Size) + { + var offset = reader.CurrentOffset; + if (offsetLabelMap?.TryGetValue(offset, out var labels) ?? false) + foreach (var label in labels) + yield return label; + + var opCode = (OpCode)reader.ReadByte(); + + var instSchema = InstructionSet.InstructionSchema[opCode]; + Operand[] operands = new Operand[instSchema.Length]; + + for (int i = 0; i < instSchema.Length; i++) + { + var operandDataType = instSchema[i]; + Operand operand; + + if (operandDataType == LiteralDataType.ConstantIndex) + { + // Refer to the constant by name rather than index + var constantIndex = reader.ReadUInt16(); + + // TODO: Only generate name if not using a shared binary table + operand = new OperandReference($"const{constantIndex}", constantIndex); + } + else + { + object literalValue = OperandLiteral.ReduceDataType(operandDataType) switch + { + LiteralDataType.Byte => reader.ReadByte(), + LiteralDataType.UInt16 => reader.ReadUInt16(), + LiteralDataType.UInt32 => reader.ReadUInt32(), + LiteralDataType.Int32 => reader.ReadInt32(), + _ => throw new InvalidOperationException($"Unexpected operand data type '{operandDataType}'") + }; + + operand = new OperandLiteral(literalValue, operandDataType); + } + + operands[i] = operand; + } + + yield return new Instruction(opCode, operands, offset); + } + } }