/// <summary> /// Creates a word-sized data instruction. /// </summary> /// <param name="baseFile">The file to read from.</param> /// <param name="org">The origin address of the file.</param> /// <param name="offset">The offset into the file to read from.</param> /// <param name="outputInstruction">The GBInstruction to write the information to.</param> /// <returns>Returns true if the creation was successful, and false otherwise. /// </returns> public static bool CreateDWInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction) { outputInstruction = new GBInstruction(); if (baseFile == null || offset > baseFile.Length - 2) return false; int address = org + offset; outputInstruction.InstSize = 2; outputInstruction.InstType = InstructionType.dw; outputInstruction.Bank = (byte)(address >> 14); outputInstruction.Address = (ushort)(address & 0x3FFF); outputInstruction.ArgCount = 1; outputInstruction.Arg1.ArgType = GBArgumentType.Word; outputInstruction.Arg1.NumArg = BitConverter.ToUInt16(baseFile, offset); if (address > 0x4000) { outputInstruction.Address += 0x4000; } return true; }
/// <summary> /// Creates a byte-sized data instruction. /// </summary> /// <param name="baseFile">The file to read from.</param> /// <param name="org">The origin address of the file.</param> /// <param name="offset">The offset into the file to read from.</param> /// <param name="outputInstruction">The GBInstruction to write the information to.</param> /// <returns>Returns true if the creation was successful, and false otherwise. /// </returns> public static bool CreateDBInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction) { outputInstruction = new GBInstruction(); if (baseFile == null || offset > baseFile.Length - 1) return false; int address = org + offset; outputInstruction.InstSize = 1; outputInstruction.InstType = InstructionType.db; outputInstruction.Bank = (byte)(address >> 14); outputInstruction.Address = (ushort)(address & 0x3FFF); outputInstruction.ArgCount = 1; outputInstruction.Arg1.ArgType = GBArgumentType.Byte; outputInstruction.Arg1.NumArg = baseFile[offset]; if (address > 0x4000) { outputInstruction.Address += 0x4000; } return true; }
/// <summary> /// Creates a word-sized data instruction. /// </summary> /// <param name="baseFile">The file to read from.</param> /// <param name="org">The origin address of the file.</param> /// <param name="offset">The offset into the file to read from.</param> /// <param name="outputInstruction">The GBInstruction to write the information to.</param> /// <returns>Returns true if the creation was successful, and false otherwise. /// </returns> public static bool CreateDWInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction) { outputInstruction = new GBInstruction(); if (baseFile == null || offset > baseFile.Length - 2) { return(false); } int address = org + offset; outputInstruction.InstSize = 2; outputInstruction.InstType = InstructionType.dw; outputInstruction.Bank = (byte)(address >> 14); outputInstruction.Address = (ushort)(address & 0x3FFF); outputInstruction.ArgCount = 1; outputInstruction.Arg1.ArgType = GBArgumentType.Word; outputInstruction.Arg1.NumArg = BitConverter.ToUInt16(baseFile, offset); if (address > 0x4000) { outputInstruction.Address += 0x4000; } return(true); }
/// <summary> /// Creates a byte-sized data instruction. /// </summary> /// <param name="baseFile">The file to read from.</param> /// <param name="org">The origin address of the file.</param> /// <param name="offset">The offset into the file to read from.</param> /// <param name="outputInstruction">The GBInstruction to write the information to.</param> /// <returns>Returns true if the creation was successful, and false otherwise. /// </returns> public static bool CreateDBInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction) { outputInstruction = new GBInstruction(); if (baseFile == null || offset > baseFile.Length - 1) { return(false); } int address = org + offset; outputInstruction.InstSize = 1; outputInstruction.InstType = InstructionType.db; outputInstruction.Bank = (byte)(address >> 14); outputInstruction.Address = (ushort)(address & 0x3FFF); outputInstruction.ArgCount = 1; outputInstruction.Arg1.ArgType = GBArgumentType.Byte; outputInstruction.Arg1.NumArg = baseFile[offset]; if (address > 0x4000) { outputInstruction.Address += 0x4000; } return(true); }
/// <summary> /// Given a binary file and the appropriate offsets, constructs a GBInstructionUnit containing information about the instruction and its arguments. /// </summary> /// <param name="baseFile">The file to read from.</param> /// <param name="org">The origin address of the file.</param> /// <param name="offset">The offset into the file to read from.</param> /// <param name="outputInstruction">The GBInstruction to write the information to.</param> /// <returns>The success of the operation. Returns false if the information fetching went wrong for any reason /// </returns> public static bool GetInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction) { outputInstruction = new GBInstruction(); if (baseFile == null || offset > baseFile.Length - 1) { return(false); } // Holds the "actual" address of the instruction, in case the array provided isn't the full file. int address = org + offset; byte inst = baseFile[offset]; if (inst == 0xCB) { // If the inst is a CB instruction, it's a 2-byte one, and a different table is used. if (offset > baseFile.Length - 2) { // In case the inst is part of a bigger file, interpret as a DB instruction instead. // Prior behavior: return false return(GBASM.CreateDBInstruction(baseFile, org, offset, ref outputInstruction)); } outputInstruction = GBInstructions.CBInstructionUnitTable[baseFile[offset + 1]]; } else { outputInstruction = GBInstructions.InstructionUnitTable[baseFile[offset]]; } // If the instruction gotten is too big, then return a DB/DW instruction, as default. if (offset + outputInstruction.InstSize > baseFile.Length) { int retSize = baseFile.Length - offset; switch (retSize) { case 1: { return(GBASM.CreateDBInstruction(baseFile, org, offset, ref outputInstruction)); } case 2: { return(GBASM.CreateDWInstruction(baseFile, org, offset, ref outputInstruction)); } default: { return(false); } } } // Adjust the gotten inst's bank and address manually outputInstruction.Bank = (byte)(address >> 14); outputInstruction.Address = (ushort)(address & 0x3FFF); if (address > 0x4000) { outputInstruction.Address += 0x4000; } // Finally, adjust the values of the arguments if they are dependant on the other bytes // in the instruction. if (outputInstruction.InstSize == 1 && outputInstruction.InstType == InstructionType.db) { outputInstruction.Arg1.NumArg = baseFile[offset]; } else if (outputInstruction.InstSize == 2 && inst != 0xCB) { if (outputInstruction.ArgCount == 1) { if (outputInstruction.InstType == InstructionType.jr) { //jr nn int modifier = (baseFile[offset + 1] < 0x80 ? baseFile[offset + 1] : -(0x100 - baseFile[offset + 1])); ushort newAddress = (ushort)(outputInstruction.Address + 2 + modifier); outputInstruction.Arg1.NumArg = newAddress; } else { //and, or, sub, cp, xor nn outputInstruction.Arg1.NumArg = baseFile[offset + 1]; } } else if (outputInstruction.ArgCount == 2) { if (outputInstruction.InstType == InstructionType.jr) { //jr nn int modifier = (baseFile[offset + 1] < 0x80 ? baseFile[offset + 1] : -(0x100 - baseFile[offset + 1])); ushort newAddress = (ushort)(outputInstruction.Address + 2 + modifier); outputInstruction.Arg2.NumArg = newAddress; } else if (outputInstruction.Arg1.ArgType == GBArgumentType.MemMapWord) { outputInstruction.Arg1.NumArg = (ushort)(0xFF00 + baseFile[offset + 1]); } else if (outputInstruction.Arg2.ArgType == GBArgumentType.MemMapWord) { outputInstruction.Arg2.NumArg = (ushort)(0xFF00 + baseFile[offset + 1]); } else { outputInstruction.Arg2.NumArg = baseFile[offset + 1]; } } } else if (outputInstruction.InstSize == 3) { if (outputInstruction.ArgCount == 1 && outputInstruction.Arg1.ArgType == GBArgumentType.Word) { //jp nnnn, call nnnn outputInstruction.Arg1.NumArg = BitConverter.ToUInt16(baseFile, offset + 1); } else if (outputInstruction.ArgCount == 2) { if (outputInstruction.Arg1.ArgType == GBArgumentType.MemMapWord) { outputInstruction.Arg1.NumArg = BitConverter.ToUInt16(baseFile, offset + 1); } else if (outputInstruction.Arg2.ArgType == GBArgumentType.Word || outputInstruction.Arg2.ArgType == GBArgumentType.MemMapWord) { outputInstruction.Arg2.NumArg = BitConverter.ToUInt16(baseFile, offset + 1); } } } return(true); }
public int GuessLabelPrintLength(int offset) { int currentOffset = offset; int maxLength = 0x4000 - (currentOffset & 0x3FFF); bool done = false; int nopCount = 0; var jumpLocs = new List<int>(); bool endpoint = false; while (!done) { if (currentOffset >= CoreFile.Length) { break; } GBInstruction isu = new GBInstruction(); GBASM.GetInstruction(CoreFile.MainFile, 0, currentOffset, ref isu); currentOffset += isu.InstSize; if (currentOffset - offset >= maxLength) { break; } switch (isu.InstType) { case InstructionType.nop: nopCount++; if (nopCount > 3) { endpoint = true; } break; case InstructionType.ret: case InstructionType.reti: if (isu.ArgCount == 0) { endpoint = true; } break; case InstructionType.jp: case InstructionType.jr: { int jumpedOffset = 0; if (isu.ArgCount == 1) { jumpedOffset = Utility.GetRealAddress(isu.Bank, isu.Arg1.NumArg); } else if (isu.ArgCount == 2) { jumpedOffset = Utility.GetRealAddress(isu.Bank, isu.Arg2.NumArg); } if (jumpedOffset > currentOffset) { jumpLocs.Add(jumpedOffset); } if (isu.ArgCount == 1) { endpoint = true; } break; } default: break; } if (endpoint) { if (jumpLocs.Contains(currentOffset)) { jumpLocs.Remove(currentOffset); endpoint = false; } else { done = true; } } } return currentOffset - offset; }
private void SearchFileRangeForVarReference(Dictionary<string, VarRefType> results, int startingOffset, int length, int searchWord) { int currentOffset = startingOffset; while (currentOffset < startingOffset + length) { if (lc.isAddressMarkedAsData(currentOffset)) { currentOffset = lc.GetNextNonDataAddress(currentOffset); continue; } GBInstruction isu = new GBInstruction(); if (!GBASM.GetInstruction(CoreFile.MainFile, 0, currentOffset, ref isu)) break; if (isu.InstType == InstructionType.ld && isu.ArgCount == 2) { if (isu.Arg1.ArgType == GBArgumentType.MemMapWord && isu.Arg1.NumArg == searchWord) { results.Add(currentOffset.ToString("X"), VarRefType.Write); } else if (isu.Arg2.ArgType == GBArgumentType.MemMapWord && isu.Arg2.NumArg == searchWord) { results.Add(currentOffset.ToString("X"), VarRefType.Read); } else if (isu.Arg1.ArgType == GBArgumentType.RegisterDouble && isu.Arg2.NumArg == searchWord) { results.Add(currentOffset.ToString("X"), VarRefType.Ref); } } currentOffset += isu.InstSize; } }
private Dictionary<string, VarRefType> SearchFileForVarReference(int searchWord, SearchOptions options) { Dictionary<string, VarRefType> results = new Dictionary<string, VarRefType>(); if (options == SearchOptions.InFile) { SearchFileRangeForVarReference(results, 0, CoreFile.Length, searchWord); } else { foreach (FunctionLabel c in lc.FuncList) { VarRefType result = VarRefType.None; int currentOffset = c.Offset; int curLen = GuessLabelPrintLength(c.Offset); while (currentOffset < c.Offset + curLen) { if (lc.isAddressMarkedAsData(currentOffset)) { currentOffset = lc.GetNextNonDataAddress(currentOffset); continue; } GBInstruction isu = new GBInstruction(); if (!GBASM.GetInstruction(CoreFile.MainFile, 0, currentOffset, ref isu)) { break; } if (isu.InstType == InstructionType.ld && isu.ArgCount == 2) { if (isu.Arg1.ArgType == GBArgumentType.MemMapWord && isu.Arg1.NumArg == searchWord) { result |= VarRefType.Write; } else if (isu.Arg2.ArgType == GBArgumentType.MemMapWord && isu.Arg2.NumArg == searchWord) { result |= VarRefType.Read; } else if (isu.Arg1.ArgType == GBArgumentType.RegisterDouble && isu.Arg2.NumArg == searchWord) { result |= VarRefType.Ref; } } currentOffset += isu.InstSize; } if (result != VarRefType.None) results.Add(c.Name, result); } } return results; }
private void SearchFileRangeForFunctionCall(Dictionary<string, FuncRefType> results, int startingOffset, int length, FunctionLabel searchLabel) { if (startingOffset < 0x4000 && searchLabel.Offset > 0x3FFF) { return; } int currentOffset = startingOffset; while (currentOffset < startingOffset + length) { if (lc.isAddressMarkedAsData(currentOffset)) { currentOffset = lc.GetNextNonDataAddress(currentOffset); continue; } GBInstruction isu = new GBInstruction(); if (!GBASM.GetInstruction(CoreFile.MainFile, 0, currentOffset, ref isu)) break; if (isu.InstType == InstructionType.call || isu.InstType == InstructionType.jp || isu.InstType == InstructionType.jr) { ushort calledAddress = isu.ArgCount == 1 ? isu.Arg1.NumArg : isu.Arg2.NumArg; if (!(calledAddress < 0x4000 && currentOffset > 0x3FFF)) { int calledNum = Utility.GetRealAddress(isu.Bank, calledAddress); if (searchLabel.Offset == calledNum) { if (isu.InstType == InstructionType.call) results.Add(currentOffset.ToString("X"), FuncRefType.Call); else results.Add(currentOffset.ToString("X"), FuncRefType.Call); } } } currentOffset += isu.InstSize; } }
private Dictionary<string, FuncRefType> SearchForFunctionCall(FunctionLabel searchLabel, SearchOptions options) { var results = new Dictionary<string, FuncRefType>(); var searched = new HashSet<int>(); if (options == SearchOptions.InFile) { SearchFileRangeForFunctionCall(results, 0x0, CoreFile.Length, searchLabel); return results; } else { foreach (FunctionLabel c in lc.FuncList) { FuncRefType refType = FuncRefType.None; int currentOffset = c.Offset; if (searched.Contains(currentOffset)) { continue; } else { searched.Add(currentOffset); } int curLen = GuessLabelPrintLength(c.Offset); while (currentOffset < c.Offset + curLen) { if (currentOffset < 0x4000 && searchLabel.Offset > 0x3FFF) { break; } if (lc.isAddressMarkedAsData(currentOffset)) { currentOffset = lc.GetNextNonDataAddress(currentOffset); continue; } GBInstruction isu = new GBInstruction(); if (!GBASM.GetInstruction(CoreFile.MainFile, 0, currentOffset, ref isu)) break; if (isu.InstType == InstructionType.call || isu.InstType == InstructionType.jp || isu.InstType == InstructionType.jr) { ushort calledAddress = isu.ArgCount == 1 ? isu.Arg1.NumArg : isu.Arg2.NumArg; if (!(currentOffset < 0x4000 && calledAddress > 0x3FFF)) { int calledNum = Utility.GetRealAddress(isu.Bank, calledAddress); if (searchLabel.Offset == calledNum) { if (isu.InstType == InstructionType.call) refType |= FuncRefType.Call; else refType |= FuncRefType.Jump; } } } currentOffset += isu.InstSize; } if (refType != FuncRefType.None) results.Add(c.Name, refType); } return results; } }
public void AutoPopulateFunctionList() { int currentOffset = 0; while (currentOffset < CoreFile.Length) { if (lc.isAddressMarkedAsData(currentOffset)) { currentOffset = lc.GetNextNonDataAddress(currentOffset); continue; } GBInstruction isu = new GBInstruction(); if (!GBASM.GetInstruction(CoreFile.MainFile, 0, currentOffset, ref isu)) { return; } if (isu.InstType == InstructionType.call) { ushort curCallAddress = (isu.ArgCount == 1) ? isu.Arg1.NumArg : isu.Arg2.NumArg; if (curCallAddress < 0x4000) { lc.AddFuncLabel(new FunctionLabel(curCallAddress)); } else if (currentOffset >= 0x4000) { int curAdjustedCallAddress = Utility.GetRealAddress(isu.Bank, curCallAddress); lc.AddFuncLabel(new FunctionLabel(curAdjustedCallAddress)); } } currentOffset += isu.InstSize; } }
private string GetInstruction(BinFile refFile, int OrgOffset, int BinaryOffset, ref GBInstruction isu) { int currentAddress = OrgOffset + BinaryOffset; if (lc.isAddressMarkedAsData(currentAddress)) { GBASM.CreateDBInstruction(refFile.MainFile, OrgOffset, BinaryOffset, ref isu); } else { GBASM.GetInstruction(refFile.MainFile, OrgOffset, BinaryOffset, ref isu); } StringBuilder returned = new StringBuilder(); if (PrintOffsets || PrintBitPattern) { returned.Append("/*"); if (PrintOffsets) { returned.Append(AddressToString(isu)); } if (PrintBitPattern) { var bp = new string[] { " ", " ", " ", " " }; for (int i = 0; i < isu.InstSize; i++) { bp[i] = refFile.ReadByte(BinaryOffset + i).ToString("X2"); } returned.Append(string.Join("", bp)); } returned.Append("*/ "); } else { returned.Append(" "); } returned.Append(isu.InstType.ToString()); string numArg = ""; if (isu.ArgCount > 0) { returned.Append(" "); numArg = ArgumentToString(isu.Bank, isu.Address, isu.InstType, isu.Arg1); returned.Append(numArg); } if (isu.ArgCount == 2) { returned.Append(","); numArg = ArgumentToString(isu.Bank, isu.Address, isu.InstType, isu.Arg2); returned.Append(numArg); } #region Check comments if (PrintComments) { returned.Append(" ;"); int x = refFile.ReadByte(BinaryOffset); if (refFile.ReadByte(BinaryOffset) == 0xCB) { returned.AppendFormat(CBLongInst[x], numArg); } else { returned.AppendFormat(longInst[x], numArg); } } #endregion Check comments return returned.ToString(); }
public string PrintASM(BinFile file, int baseOffset, int start, int length) { StringBuilder output = new StringBuilder(); GBInstruction isu = new GBInstruction(); int current = start; while (current < start + length) { var labelsAt = from s in lc.FuncList where s.Offset == current orderby s.Name select s; var dataAt = from s in lc.DataList where s.Offset == current select s; foreach (var label in labelsAt) { output.AppendLine(label.ToASMString()); } int advanceBy = 0; if (dataAt.Count() != 0) { if (HideDefinedData) { output.AppendLine(String.Format("INCBIN \"{0}.bin\"", dataAt.First().Name)); } else { output.Append(ShowDataLabel(dataAt.First())); } advanceBy = dataAt.First().Length; } else { output.AppendLine(GetInstruction(file, baseOffset, current, ref isu)); advanceBy = isu.InstSize; } if (lc.Comments.ContainsKey(current)) { output.AppendLine(";" + lc.Comments[current].Replace("\n", "\n;")); } current += advanceBy; } return output.ToString(); }
private string AddressToString(GBInstruction isu) { switch (PrintedOffsetFormat) { case OffsetFormat.BankOffset: { return isu.Bank.ToString("X2") + ":" + isu.Address.ToString("X4") + " "; } case OffsetFormat.Hex: case OffsetFormat.Decimal: { int address = Utility.GetRealAddress(isu.Bank, isu.Address); if (PrintedOffsetFormat == OffsetFormat.Hex) { return address.ToString("X6") + " "; } else { return address.ToString("D7") + " "; } } default: return ""; } }
/// <summary> /// Given a binary file and the appropriate offsets, constructs a GBInstructionUnit containing information about the instruction and its arguments. /// </summary> /// <param name="baseFile">The file to read from.</param> /// <param name="org">The origin address of the file.</param> /// <param name="offset">The offset into the file to read from.</param> /// <param name="outputInstruction">The GBInstruction to write the information to.</param> /// <returns>The success of the operation. Returns false if the information fetching went wrong for any reason /// </returns> public static bool GetInstruction(byte[] baseFile, int org, int offset, ref GBInstruction outputInstruction) { outputInstruction = new GBInstruction(); if (baseFile == null || offset > baseFile.Length - 1) return false; // Holds the "actual" address of the instruction, in case the array provided isn't the full file. int address = org + offset; byte inst = baseFile[offset]; if (inst == 0xCB) { // If the inst is a CB instruction, it's a 2-byte one, and a different table is used. if (offset > baseFile.Length - 2) { // In case the inst is part of a bigger file, interpret as a DB instruction instead. // Prior behavior: return false return GBASM.CreateDBInstruction(baseFile, org, offset, ref outputInstruction); } outputInstruction = GBInstructions.CBInstructionUnitTable[baseFile[offset + 1]]; } else { outputInstruction = GBInstructions.InstructionUnitTable[baseFile[offset]]; } // If the instruction gotten is too big, then return a DB/DW instruction, as default. if (offset + outputInstruction.InstSize > baseFile.Length) { int retSize = baseFile.Length - offset; switch (retSize) { case 1: { return GBASM.CreateDBInstruction(baseFile, org, offset, ref outputInstruction); } case 2: { return GBASM.CreateDWInstruction(baseFile, org, offset, ref outputInstruction); } default: { return false; } } } // Adjust the gotten inst's bank and address manually outputInstruction.Bank = (byte)(address >> 14); outputInstruction.Address = (ushort)(address & 0x3FFF); if (address > 0x4000) { outputInstruction.Address += 0x4000; } // Finally, adjust the values of the arguments if they are dependant on the other bytes // in the instruction. if (outputInstruction.InstSize == 1 && outputInstruction.InstType == InstructionType.db) { outputInstruction.Arg1.NumArg = baseFile[offset]; } else if (outputInstruction.InstSize == 2 && inst != 0xCB) { if (outputInstruction.ArgCount == 1) { if (outputInstruction.InstType == InstructionType.jr) { //jr nn int modifier = (baseFile[offset + 1] < 0x80 ? baseFile[offset + 1] : -(0x100 - baseFile[offset + 1])); ushort newAddress = (ushort)(outputInstruction.Address + 2 + modifier); outputInstruction.Arg1.NumArg = newAddress; } else { //and, or, sub, cp, xor nn outputInstruction.Arg1.NumArg = baseFile[offset + 1]; } } else if (outputInstruction.ArgCount == 2) { if (outputInstruction.InstType == InstructionType.jr) { //jr nn int modifier = (baseFile[offset + 1] < 0x80 ? baseFile[offset + 1] : -(0x100 - baseFile[offset + 1])); ushort newAddress = (ushort)(outputInstruction.Address + 2 + modifier); outputInstruction.Arg2.NumArg = newAddress; } else if (outputInstruction.Arg1.ArgType == GBArgumentType.MemMapWord) { outputInstruction.Arg1.NumArg = (ushort)(0xFF00 + baseFile[offset + 1]); } else if (outputInstruction.Arg2.ArgType == GBArgumentType.MemMapWord) { outputInstruction.Arg2.NumArg = (ushort)(0xFF00 + baseFile[offset + 1]); } else { outputInstruction.Arg2.NumArg = baseFile[offset + 1]; } } } else if (outputInstruction.InstSize == 3) { if (outputInstruction.ArgCount == 1 && outputInstruction.Arg1.ArgType == GBArgumentType.Word) { //jp nnnn, call nnnn outputInstruction.Arg1.NumArg = BitConverter.ToUInt16(baseFile, offset + 1); } else if (outputInstruction.ArgCount == 2) { if (outputInstruction.Arg1.ArgType == GBArgumentType.MemMapWord) { outputInstruction.Arg1.NumArg = BitConverter.ToUInt16(baseFile, offset + 1); } else if (outputInstruction.Arg2.ArgType == GBArgumentType.Word || outputInstruction.Arg2.ArgType == GBArgumentType.MemMapWord) { outputInstruction.Arg2.NumArg = BitConverter.ToUInt16(baseFile, offset + 1); } } } return true; }