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