private static string CalculatePcRelativeAddress(string instruction, int pcAddress, InstructionPart part, ushort bits) { var mult = GrabBits(part.Code, 8, 8); var add = GrabBits(part.Code, 0, 8); var numeric = (short)bits; numeric <<= 16 - part.Length; numeric >>= 16 - part.Length; // get all the extra bits set to one so that the numeric value is correct if (instruction.Contains("#")) { // this is the first # in this instruction. var address = pcAddress - (pcAddress % mult) + numeric * mult + add; var end = instruction.EndsWith("]") ? "]" : string.Empty; instruction = instruction.Split("#=")[0] + "#" + end; instruction = instruction.Replace("#", $"<{address:X6}>"); } else { // this is an additional # in the same instruction. // decode back from the old one var address = int.Parse(instruction.Split('<')[1].Split('>')[0], NumberStyles.HexNumber); address -= pcAddress - (pcAddress % mult) + add; address /= mult; address = (address & ((1 << part.Length) - 1)); // drop the high bits, keep only the data bits. This makes it lose the sign. // concat the new numeric address += bits << part.Length; // the new numeric is the higher bits // shift to get arithmetic sign bits address <<= 32 - part.Length * 2; address >>= 32 - part.Length * 2; // since address is a signed int, C# right-shift will carry 1-bits down if the high bit is set. This is what we want to happen. // encode again address *= mult; address += pcAddress - (pcAddress % mult) + add; instruction = instruction.Split('<')[0] + $"<{address:X6}>" + (instruction + " ").Split('>')[1].Trim(); // extra space / trim let's us get everything after the '>', even if it's empty } return(instruction); }
private Instruction(string compiled, string script) { var parts = compiled.ToLower().Split(' '); foreach (var part in parts) { if (part.StartsWith("0") || part.StartsWith("1")) { var code = ToBits(part); instructionParts.Add(new InstructionPart(InstructionArgType.OpCode, code, part.Length)); } else if (part.StartsWith("r")) { instructionParts.Add(new InstructionPart(InstructionArgType.Register, 0, 3, part)); } else if (part.StartsWith("#")) { ushort code = 0; if (script.Contains("#=pc+#*")) { var encoding = script.Split("#=pc+#*")[1].Split('+'); code = byte.TryParse(encoding[0], out var mult) ? mult : default; code <<= 8; code |= byte.TryParse(encoding[1].Trim(']'), out var add) ? add : default; } int length = 0; if (part.Length > 1) { int.TryParse(part.Substring(1), out length); } instructionParts.Add(new InstructionPart(InstructionArgType.Numeric, code, length)); } else if (part == "h") { instructionParts.Add(new InstructionPart(InstructionArgType.HighRegister, 0, 1)); } else if (part == "list") { instructionParts.Add(new InstructionPart(InstructionArgType.List, 0, 8)); } else if (part == "tsil") { instructionParts.Add(new InstructionPart(InstructionArgType.ReverseList, 0, 8)); } else if (part == "cond") { instructionParts.Add(new InstructionPart(InstructionArgType.Condition, 0, 4)); } } var totalLength = instructionParts.Sum(part => part.Length); if (totalLength > 16) { ByteLength = totalLength / 8; } var remainingLength = ByteLength * 8 - totalLength; for (int i = 0; i < instructionParts.Count && remainingLength > 0; i++) { if (instructionParts[i].Type != InstructionArgType.Numeric) { continue; } instructionParts[i] = new InstructionPart(InstructionArgType.Numeric, instructionParts[i].Code, remainingLength); totalLength += remainingLength; } if (totalLength % 16 != 0) { throw new ArgumentException($"There were {totalLength} bits in the command, but commands must be a multiple of 16 bits long!"); } template = script.ToLower(); }