1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 
9 namespace Antmicro.Renode.Utilities
10 {
11     public class SimpleInstructionDecoder<TInstruction>
12     {
13         /// <remarks>
14         /// This decoder assumes that the opcode is at most 1 byte long. It will parse the opcode, starting from the opcode's MSB (Most Significant Bit).
15         /// </remarks>
AddOpcode(byte value, int opcodeLength, Func<TInstruction> newInstruction, int bitNumber = 7)16         public void AddOpcode(byte value, int opcodeLength, Func<TInstruction> newInstruction, int bitNumber = 7)
17         {
18             if(opcodeLength > 8)
19             {
20                 throw new ArgumentException($"The opcode cannot be longer than 8 bits: is {opcodeLength} bits long");
21             }
22             if(bitNumber < 8 - opcodeLength)
23             {
24                 // We're done parsing - we traversed the whole opcode length to get here
25                 // so we encoded the path to our instruction in the tree - this is the leaf node (the last in the tree, without any children)
26                 if(this.instruction != null)
27                 {
28                     // There already exists an instruction here - so throw exception, not to override it by the new one
29                     throw new InvalidOperationException($"Duplicate instruction registered: {value} already exists");
30                 }
31                 this.instruction = newInstruction;
32             }
33             else
34             {
35                 // If there is an instruction already here, and we haven't parsed the whole length
36                 // this means we entered an invalid state, where some instruction have different lengths than others
37                 // in such a way, they override the longer ones in the tree (e.g. instruction 0b11 with length 2 would silently override 0b1101 with length 4, if we parsed from MSB).
38                 // If we didn't abort here, the longer instruction wouldn't be parsed at all in `TryParseOpcode` and replaced with the shorter one each time!
39                 if(instruction != null)
40                 {
41                     throw new InvalidOperationException($"Cannot register instruction: {value} the lengths and contents will clash with instruction: {instruction}, which will prevent correct parsing");
42                 }
43                 // Construct a binary tree here, if we haven't already
44                 if(children == null)
45                 {
46                     children = new SimpleInstructionDecoder<TInstruction>[2];
47                     children[0] = new SimpleInstructionDecoder<TInstruction>();
48                     children[1] = new SimpleInstructionDecoder<TInstruction>();
49                 }
50                 var nextBit = BitHelper.IsBitSet(value, (byte)bitNumber);
51                 bitNumber--;
52                 // Depending whether the next bit of the opcode is 0 or 1, choose the path in the binary tree
53                 // and recursively call `AddOpcode` on the new instance of the Decoder,
54                 // that represents a child node in the tree
55                 children[nextBit ? 1 : 0].AddOpcode(value, opcodeLength, newInstruction, bitNumber);
56             }
57         }
58 
TryParseOpcode(byte value, out TInstruction result, byte bitNumber = 7)59         public bool TryParseOpcode(byte value, out TInstruction result, byte bitNumber = 7)
60         {
61             if(instruction != null)
62             {
63                 // There is an instruction here - it's the one we are looking for.
64                 // The instructions are added only in leaf node of the tree - there is a check to enforce that.
65                 // We end parsing right now with success.
66                 result = instruction();
67                 return true;
68             }
69             if(children == null)
70             {
71                 // If there are no further children, and we didn't already find the instruction
72                 // that means that it doesn't exist in our tree
73                 result = default(TInstruction);
74                 return false;
75             }
76             var nextBit = BitHelper.IsBitSet(value, bitNumber);
77             bitNumber--;
78             // To find the instruction, we have to traverse the binary tree,
79             // constructed when we called `AddOpcode` previously
80             return children[nextBit ? 1 : 0].TryParseOpcode(value, out result, bitNumber);
81         }
82 
83         // Binary tree, since opcodes differ by 0/1 on each bit position
84         private SimpleInstructionDecoder<TInstruction>[] children;
85         private Func<TInstruction> instruction;
86     }
87 }
88