private void FormatParameterBlock(int offset, Param[] parms) { int blockAddr = Util.GetWord(mFileData, offset + 4, 2, false); // Try to format the parameter block. Start by figuring out how long it is. int blockLen = 0; foreach (Param parm in parms) { blockLen += parm.Length; } // Locate it and verify that the entire thing fits in the file. int blockOff = mAddrTrans.AddressToOffset(offset, blockAddr); if (Util.IsInBounds(mFileData, blockOff, blockLen)) { if (VERBOSE) { mAppRef.DebugLog("Formatting P8 block at +" + blockOff.ToString("x6")); } foreach (Param parm in parms) { // We could try to dereference pathname buffers to see if it's a // fixed value and not an empty buffer, but it's hard for us to // reliably tell the difference between a length-limited pathname // and junk. If the length byte is bad, we run the risk of lumping // a bunch of stuff into the pathname buffer. mAppRef.SetInlineDataFormat(blockOff, parm.Length, parm.Type, parm.SubType, null); blockOff += parm.Length; } } }
/// <summary> /// Converts a JSR/JMP operand to a file offset. /// </summary> /// <param name="vaddr">AVG address operand.</param> /// <param name="baseAddr">Base address of vector memory.</param> /// <param name="offset">File offset of instruction.</param> /// <returns>False if the target address is outside the file bounds.</returns> private bool Branch(int vaddr, int baseAddr, ref int offset) { int fileAddr = baseAddr + vaddr * 2; int fileOffset = mAddrTrans.AddressToOffset(offset, fileAddr); if (fileOffset < 0) { mAppRef.ReportError("JMP/JSR to " + vaddr.ToString("x4") + " invalid"); return(false); } offset = fileOffset; return(true); }
// IPlugin_SymbolList public void UpdateSymbolList(List <PlSymbol> plSyms) { // reset this every time, in case they remove the symbol mChrRomOffset = -1; foreach (PlSymbol sym in plSyms) { if (sym.Label == CHR_ROM) { int addr = sym.Value; mChrRomOffset = mAddrTrans.AddressToOffset(0, addr); break; } } mAppRef.DebugLog(CHR_ROM + " @ +" + mChrRomOffset.ToString("x6")); }
private IVisualization2d GenerateScreen(ReadOnlyDictionary <string, object> parms) { //const int RAW_IMAGE_SIZE = 0x1ff8; const int HR_WIDTH = 280; const int HR_BYTE_WIDTH = HR_WIDTH / 7; const int HR_HEIGHT = 192; int offset = Util.GetFromObjDict(parms, P_OFFSET, 0); bool isColor = Util.GetFromObjDict(parms, P_IS_COLOR, true); if (offset < 0 || offset >= mFileData.Length) { // should be caught by editor mAppRef.ReportError("Invalid parameter"); return(null); } //int lastOffset = offset + RAW_IMAGE_SIZE - 1; //if (lastOffset >= mFileData.Length) { // mAppRef.ReportError("Bitmap runs off end of file (last offset +" + // lastOffset.ToString("x6") + ")"); // return null; //} // Linearize the data. To handle programs that move themselves around before // executing we use the address translator (e.g. the title screen in Space Eggs // is contiguous in memory but split in half in the file). This is slower, but // mAddrTrans is a local (not proxy) object, so it's not too bad. byte[] buf = new byte[HR_BYTE_WIDTH * HR_HEIGHT]; int outIdx = 0; int baseAddr = mAddrTrans.OffsetToAddress(offset); for (int row = 0; row < HR_HEIGHT; row++) { // If row is ABCDEFGH, we want pppFGHCD EABAB000 (where p would be $20/$40). int low = ((row & 0xc0) >> 1) | ((row & 0xc0) >> 3) | ((row & 0x08) << 4); int high = ((row & 0x07) << 2) | ((row & 0x30) >> 4); int rowAddr = baseAddr + ((high << 8) | low); // Not expecting the data to wrap around, but it's possible. rowAddr = (baseAddr & 0xff0000) | (rowAddr & 0xffff); for (int col = 0; col < HR_BYTE_WIDTH; col++) { int srcOffset = mAddrTrans.AddressToOffset(offset, rowAddr + col); if (srcOffset < 0) { mAppRef.ReportError("Address $" + (rowAddr + col).ToString("x4") + " is outside of file"); return(null); } buf[outIdx++] = mFileData[srcOffset]; } } VisBitmap8 vb = new VisBitmap8(HR_WIDTH, HR_HEIGHT); SetHiResPalette(vb); RenderBitmap(buf, 0, HR_BYTE_WIDTH, HR_HEIGHT, 1, HR_BYTE_WIDTH, isColor ? ColorMode.SimpleColor : ColorMode.Mono, false, false, vb, 0, 0); return(vb); }
public void CheckBrk(int offset, bool twoByteBrk, out bool noContinue) { noContinue = true; // need BRK, function byte, and two-byte address if (!Util.IsInBounds(mFileData, offset, 4)) { return; } byte func = mFileData[offset + 1]; if (func != 0x85 && (func < 0x01 || func > 0x02)) { return; } Util.FormatBrkByte(mAppRef, twoByteBrk, offset, DataSubType.Hex, null); mAppRef.SetInlineDataFormat(offset + 2, 2, DataType.NumericLE, DataSubType.Address, null); noContinue = false; int structAddr = Util.GetWord(mFileData, offset + 2, 2, false); int structOff = mAddrTrans.AddressToOffset(offset, structAddr); if (structOff < 0) { mAppRef.DebugLog("Unable to get offset for address $" + structAddr.ToString("x6")); return; } switch (func) { case 0x01: if (!Util.IsInBounds(mFileData, structOff, 27)) { mAppRef.DebugLog("Struct doesn't fit in file"); return; } mAppRef.SetInlineDataFormat(structOff + 0, 2, DataType.NumericLE, DataSubType.Decimal, null); mAppRef.SetInlineDataFormat(structOff + 2, 2, DataType.NumericBE, DataSubType.Hex, null); mAppRef.SetInlineDataFormat(structOff + 4, 4, DataType.NumericLE, DataSubType.Hex, null); mAppRef.SetInlineDataFormat(structOff + 8, 4, DataType.NumericBE, DataSubType.Hex, null); mAppRef.SetInlineDataFormat(structOff + 12, 1, DataType.NumericLE, DataSubType.Ascii, null); mAppRef.SetInlineDataFormat(structOff + 13, 1, DataType.NumericLE, DataSubType.HighAscii, null); mAppRef.SetInlineDataFormat(structOff + 14, 8, DataType.StringDci, DataSubType.Ascii, null); mAppRef.SetInlineDataFormat(structOff + 22, 3, DataType.NumericLE, DataSubType.Address, null); mAppRef.SetInlineDataFormat(structOff + 25, 2, DataType.NumericLE, DataSubType.Symbol, "data02"); break; case 0x02: if (!Util.IsInBounds(mFileData, structOff, 2)) { mAppRef.DebugLog("Struct doesn't fit in file"); return; } mAppRef.SetInlineDataFormat(structOff, 2, DataType.NumericLE, DataSubType.Address, null); int nextAddr = Util.GetWord(mFileData, structOff + 2, 2, false); int nextOff = mAddrTrans.AddressToOffset(structOff, nextAddr); if (!Util.IsInBounds(mFileData, nextOff, 1)) { mAppRef.DebugLog("Struct doesn't fit in file"); return; } mAppRef.SetInlineDataFormat(nextOff, 8, DataType.StringGeneric, DataSubType.HighAscii, null); break; case 0x85: // do nothing further break; } }
// IPlugin_InlineJsr public void CheckJsr(int offset, int operand, out bool noContinue) { noContinue = false; // Do a quick test on the target address. int unused; if (!mInlineAddrs.TryGetValue(operand, out unused)) { // JSR destination address not special. return; } // Address matched. Translate the target address to the actual offset. This is // important when multiple offsets have the same address. int targetOffset = mAddrTrans.AddressToOffset(offset, operand); if (targetOffset < 0) { mAppRef.DebugLog("Failed to map address $" + operand.ToString("x4") + " to offset"); return; } InlineKind kind; if (!mInlineOffsets.TryGetValue(targetOffset, out kind)) { // Actual call target doesn't have a matching label. return; } offset += 3; // move past JSR switch (kind) { case InlineKind.InAZ: // Null-terminated ASCII string. FormatNullTermString(offset, false); break; case InlineKind.InA1: // Length-delimited ASCII string FormatL1String(offset, false); break; case InlineKind.InPZ: // Null-terminated PETSCII string. FormatNullTermString(offset, true); break; case InlineKind.InP1: // Length-delimited PETSCII string FormatL1String(offset, true); break; case InlineKind.InW: case InlineKind.InWA: // 16-bit value. Start by confirming next two bytes are inside the file bounds. if (!Util.IsInBounds(mFileData, offset, 2)) { return; } if (kind == InlineKind.InW) { // Format 16-bit value as default (hex). mAppRef.SetInlineDataFormat(offset, 2, DataType.NumericLE, DataSubType.None, null); } else { // Format 16-bit value as an address. mAppRef.SetInlineDataFormat(offset, 2, DataType.NumericLE, DataSubType.Address, null); } break; case InlineKind.InNR: // Non-returning call. noContinue = true; break; } }
private void FormatParameterBlock(int offset, ParamSet pset) { if (VERBOSE) { mAppRef.DebugLog("SOSPARM: trying to format SOS at +" + offset.ToString("x6")); } int blockAddr = Util.GetWord(mFileData, offset + 2, 2, false); // Try to format the parameter block. Start by figuring out how long the // required portion is. int blockLen = 0; foreach (Param parm in pset.Required) { blockLen += parm.Length; } int optionListAddr = -1; int optionListLen = -1; // Locate it and verify that the entire thing fits in the file. int blockOff = mAddrTrans.AddressToOffset(offset, blockAddr); if (VERBOSE) { mAppRef.DebugLog("SOSPARM: checking addr=$" + blockAddr.ToString("x4") + " off=+" + blockOff.ToString("x6") + " len=" + blockLen); } if (Util.IsInBounds(mFileData, blockOff, blockLen)) { if (VERBOSE) { mAppRef.DebugLog("SOSPARM: formatting block at +" + blockOff.ToString("x6")); } foreach (Param parm in pset.Required) { // Watch for option list parameters. if (parm == OPTION_LIST) { optionListAddr = Util.GetWord(mFileData, blockOff, 2, false); } else if (parm == OPTION_LENGTH) { optionListLen = mFileData[blockOff]; } // We could try to dereference pathname buffers to see if it's a // fixed value and not an empty buffer, but it's hard for us to // reliably tell the difference between a length-limited pathname // and junk. If the length byte is bad, we run the risk of lumping // a bunch of stuff into the pathname buffer. mAppRef.SetInlineDataFormat(blockOff, parm.Length, parm.Type, parm.SubType, null); blockOff += parm.Length; } } else { if (VERBOSE) { mAppRef.DebugLog("SOSPARM: NOT in bounds"); } } if (optionListAddr >= 0 && optionListLen > 0) { if (VERBOSE) { mAppRef.DebugLog("SOSPARM: format optionList addr=$" + optionListAddr.ToString("x4") + " len=" + optionListLen); } blockOff = mAddrTrans.AddressToOffset(offset, optionListAddr); if (Util.IsInBounds(mFileData, blockOff, optionListLen)) { // Format the parts of the option list that are present. int usedLen = 0; foreach (Param parm in pset.Optional) { if (usedLen + parm.Length > optionListLen) { // This parameter was not provided. break; } mAppRef.SetInlineDataFormat(blockOff, parm.Length, parm.Type, parm.SubType, null); blockOff += parm.Length; usedLen += parm.Length; } } } }
public void CheckJsr(int offset, int operand, out bool noContinue) { const int MLI_ENTRY = 0xbf00; noContinue = false; if (offset + 6 < mFileData.Length && operand == MLI_ENTRY) { // match! byte req = mFileData[offset + 3]; int blockAddr = Util.GetWord(mFileData, offset + 4, 2, false); if (VERBOSE) { mAppRef.DebugLog("P8 MLI call detected at +" + offset.ToString("x6") + ", cmd=$" + req.ToString("x2") + " addr=$" + blockAddr.ToString("x4")); } PlSymbol sym; if (mFunctionList.TryGetValue(req, out sym)) { mAppRef.SetInlineDataFormat(offset + 3, 1, DataType.NumericLE, DataSubType.Symbol, sym.Label); } else { mAppRef.SetInlineDataFormat(offset + 3, 1, DataType.NumericLE, DataSubType.None, null); } mAppRef.SetInlineDataFormat(offset + 4, 2, DataType.NumericLE, DataSubType.Address, null); Param[] parms; if (ParamDescrs.TryGetValue(req, out parms)) { // Try to format the parameter block. Start by figuring out how long it is. int blockLen = 0; foreach (Param parm in parms) { blockLen += parm.Length; } // Locate it and verify that the entire thing fits in the file. int blockOff = mAddrTrans.AddressToOffset(offset, blockAddr); if (Util.IsInBounds(mFileData, blockOff, blockLen)) { if (VERBOSE) { mAppRef.DebugLog("Formatting P8 block at +" + blockOff.ToString("x6")); } foreach (Param parm in parms) { // We could try to dereference pathname buffers to see if it's a // fixed value and not an empty buffer, but it's hard for us to // reliably tell the difference between a length-limited pathname // and junk. If the length byte is bad, we run the risk of lumping // a bunch of stuff into the pathname buffer. mAppRef.SetInlineDataFormat(blockOff, parm.Length, parm.Type, parm.SubType, null); blockOff += parm.Length; } } } if (req == 0x65) // QUIT call { noContinue = true; } } }
/// <summary> /// Attempts to format the parameter block that is passed into the GS/OS call. /// </summary> /// <remarks> /// All "class 1" GS/OS calls have a parameter block that begins with a parameter /// count. The count indicates how many parameters are provided after the pCount /// field. This is a field count, not a byte count, and may be zero. /// /// Sometimes different calls will overlap, e.g GET_FILE_INFO and SET_FILE_INFO. This /// is generally only done when the parameters line up, so I'm not expecting things /// to come out weird, but it's possible. /// </remarks> private void FormatParameterBlock(int offset, int req, int blockAddr) { Param[] parms; if (!ParamDescrs.TryGetValue(req, out parms)) { // We don't have a parameter list for this call. return; } int blockOff = mAddrTrans.AddressToOffset(offset, blockAddr); int paramCount; if (req < 0x0100) { // ProDOS-16 parameter blocks are fixed-length. paramCount = parms.Length; } else { // Confirm we can at least get the parameter count. if (!Util.IsInBounds(mFileData, blockOff, 2)) { return; } int pCount = Util.GetWord(mFileData, blockOff, 2, false); if (pCount >= parms.Length) { // Might be an uninitialized value. Just format the pCount. pCount = 1; } paramCount = pCount + 1; // add 1 for pCount itself } // Compute parameter block length in bytes. int blockLen = 0; for (int i = 0; i < paramCount; i++) { blockLen += parms[i].Length; } // Confirm that the entire thing fits in the file. if (!Util.IsInBounds(mFileData, blockOff, blockLen)) { return; } if (VERBOSE) { mAppRef.DebugLog("Formatting GS/OS call block at +" + blockOff.ToString("x6") + ", count=" + paramCount); } // Format each entry. for (int i = 0; i < paramCount; i++) { Param parm = parms[i]; mAppRef.SetInlineDataFormat(blockOff, parm.Length, parm.Type, parm.SubType, null); blockOff += parm.Length; } }