public ExprTree createBitRange(Location location, SpecificSymbol sym, int bitoffset,
			int numbits) {
		entry("createBitRange", location, sym, bitoffset, numbits);
		String errmsg = "";
		if (numbits == 0) {
			errmsg = "Size of bitrange is zero";
		}
		VarnodeTpl vn = sym.getVarnode();
		int finalsize = (numbits + 7) / 8; // Round up to neareast byte size
		int truncshift = 0;
		boolean maskneeded = ((numbits % 8) != 0);
		boolean truncneeded = true;

		// Special case where we can set the size, without invoking
		// a truncation operator
		if ((errmsg.length() == 0) && (bitoffset == 0) && (!maskneeded)) {
			if ((vn.getSpace().getType() == ConstTpl.const_type.handle) && vn.isZeroSize()) {
				vn.setSize(new ConstTpl(ConstTpl.const_type.real, finalsize));
				ExprTree res = new ExprTree(sym.getLocation(), vn);
				// VarnodeTpl *cruft = buildTemporary();
				// delete cruft;
				return res;
			}
		}

		if (errmsg.length() == 0) {
			VarnodeTpl truncvn = buildTruncatedVarnode(location, vn, bitoffset, numbits);
			if (truncvn != null) {		// If we are able to construct a simple truncated varnode
				ExprTree res = new ExprTree(location, truncvn);		// Return just the varnode as an expression
				return res;
			}
		}

		if (vn.getSize().getType() == ConstTpl.const_type.real) {
			// If we know the size of the input varnode, we can
			// do some immediate checks, and possibly simplify things
			int insize = (int) vn.getSize().getReal();
			if (insize > 0) {
				truncneeded = (finalsize < insize);
				insize *= 8; // Convert to number of bits
				if ((bitoffset >= insize) || (bitoffset + numbits > insize)) {
					errmsg = "Bitrange is bad";
				}
				if (maskneeded && ((bitoffset + numbits) == insize)) {
					maskneeded = false;
				}
			}
		}

		long mask = 2;
		mask = ((mask << (numbits - 1)) - 1);

		if (truncneeded && ((bitoffset % 8) == 0)) {
			truncshift = bitoffset / 8;
			bitoffset = 0;
		}

		if ((bitoffset == 0) && (!truncneeded) && (!maskneeded)) {
			errmsg = "Superfluous bitrange";
		}

		if (maskneeded && finalsize > 8) {
			errmsg =
				"Illegal masked bitrange producing varnode larger than 64 bits: " + sym.getName();
		}

		ExprTree res = new ExprTree(sym.getLocation(), vn);

		if (errmsg.length() > 0) { // Check for error condition
			reportError(location, errmsg);
			return res;
		}

		if (bitoffset != 0) {
			appendOp(location, OpCode.CPUI_INT_RIGHT, res, bitoffset, 4);
		}
		if (truncneeded) {
			appendOp(location, OpCode.CPUI_SUBPIECE, res, truncshift, 4);
		}
		if (maskneeded) {
			appendOp(location, OpCode.CPUI_INT_AND, res, mask, finalsize);
		}
		force_size(res.outvn, new ConstTpl(ConstTpl.const_type.real, finalsize), res.ops);
		return res;
	}
Handle a sleigh 'macro' invocation, returning the resulting p-code op templates (OpTpl)

@param location is the file/line where the macro is invoked
@param sym MacroSymbol is the macro symbol
@param param is the parsed list of operand expressions
@return a list of p-code op templates
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/PcodeCompile.java