private boolean emulateOperation(VarnodeOperation op, ContextState currentState,
			TaskMonitor monitor) throws CancelledException, InlineCallException {

		PcodeOp pcodeOp = op.getPCodeOp();
		SequenceNumber seq = pcodeOp.getSeqnum();
		Varnode[] inputs = pcodeOp.getInputs();
		Varnode[] values = op.getInputValues();

		switch (pcodeOp.getOpcode()) {

			case PcodeOp.STORE:
				if (!values[0].isConstant()) {
					throw new AssertException("Expected constatnt address space ID");
				}
				int spaceId = (int) values[0].getOffset();
				AddressSpace addressSpace = addrFactory.getAddressSpace(spaceId);
				if (addressSpace == null) {
					throw new IllegalArgumentException("Unknown spaceID: " + spaceId);
				}
				Varnode storedValue = values[2];
				int size = values[2].getSize();
				Varnode storageOffset = values[1];
				Varnode stackOffset = getStackOffset(pcodeOp, storageOffset, monitor);
				if (stackOffset != null) {
					// stackOffset is either constant or VarnodeOperation
					if (stackOffset.isConstant()) {
						long offset = getSignedOffset(stackOffset);
						AddressSpace stackSpace = addrFactory.getStackSpace();
						Varnode stackVarnode =
							new Varnode(stackSpace.getAddress(offset, true), size);
						currentState.store(stackVarnode, storedValue);
						if (analyzer != null) {
							int opIndex = findOpIndex(pcodeOp, pcodeOp.getInput(1));
							analyzer.stackReference(pcodeOp, opIndex, (int) offset, size, spaceId,
								RefType.WRITE, monitor);
						}
					}
					else if (stackOffset instanceof VarnodeOperation) {
						currentState.store(spaceId, storageOffset, storedValue, size);
						if (analyzer != null) {
							int opIndex = findOpIndex(pcodeOp, pcodeOp.getInput(1));
							analyzer.stackReference(pcodeOp, opIndex,
								(VarnodeOperation) stackOffset, size, spaceId, RefType.WRITE,
								monitor);
						}
					}
				}
				else if (storageOffset.isConstant()) {
					Varnode v =
						new Varnode(addressSpace.getAddress(storageOffset.getOffset() *
							addressSpace.getAddressableUnitSize()), size);
					currentState.store(v, storedValue);
					if (analyzer != null) {
						int opIndex = findOpIndex(pcodeOp, pcodeOp.getInput(1));
						analyzer.dataReference(pcodeOp, opIndex, v, RefType.WRITE, monitor);
					}
				}
				else {
					currentState.store(spaceId, storageOffset, storedValue, size);
					if (analyzer != null) {
						int opIndex = findOpIndex(pcodeOp, pcodeOp.getInput(1));
						analyzer.indirectDataReference(pcodeOp, opIndex, storageOffset, size,
							spaceId, RefType.WRITE, monitor);
					}
				}
				break;

			case PcodeOp.CBRANCH:			// Conditional branch, input 1 is boolean condition

				if (values[1].isConstant() && values[1].getOffset() == 0) {
// TODO: This is a problem, since branch case may never be evaluated!
					if (DEBUG) {
						Msg.debug(this, "Conditional Branch to " + inputs[0].getAddress() +
							" - Not taken due to false condition value");
					}
					break;  // Fall-through case - assume that a pre-condition is steering the execution
				}

				//TODO: Establish pre-condition on destination context state
				//TODO: Can we simplify the values[1] expression when we assume a constrained value of values[1]

				// Fall into BRANCH case below

			case PcodeOp.BRANCH:			// Always branch
				if (inputs[0].isConstant()) {
					SequenceNumber dest =
						new SequenceNumber(seq.getTarget(), seq.getTime() +
							(int) inputs[0].getOffset());
					if (DEBUG) {
						Msg.debug(this, "Internal " +
							(pcodeOp.getOpcode() == PcodeOp.CBRANCH ? "Conditional " : "") +
							"Branch to " + dest);
					}
					todoList.add(new BranchDestination(pcodeOp.getSeqnum(), dest, currentState));
				}
				else if (inputs[0].isAddress()) {
					if (DEBUG) {
						Msg.debug(this, (pcodeOp.getOpcode() == PcodeOp.CBRANCH ? "Conditional "
								: "") + "Branch to " + inputs[0].getAddress());
					}
					handleDirectFlow(pcodeOp, inputs[0].getAddress(), currentState, monitor);
				}
				else {
					// TODO: This should never occur
					throw new AssertException();
				}
				return false;

			case PcodeOp.BRANCHIND:			// An indirect branch (jumptable)
				if (values[0].isConstant()) {
					// Indirect branch was resolved, although more destinations may be possible
					AddressSpace space = currentState.getEntryPoint().getTarget().getAddressSpace();
					Address destAddr =
						space.getAddress(getUnsignedOffset(values[0], space.getPointerSize()));
					if (DEBUG) {
						Msg.debug(this, "Branch to " + destAddr);
					}
					handleDirectFlow(pcodeOp, destAddr, currentState, monitor);
				}
				else if (values[0].isAddress()) {

					Varnode brOffset = currentState.get(values[0], monitor);
					if (brOffset != null && brOffset.isConstant() && brOffset.getOffset() != 0) {
						AddressSpace space =
							currentState.getEntryPoint().getTarget().getAddressSpace();
						Address destAddr =
							space.getAddress(getUnsignedOffset(brOffset, space.getPointerSize()));
						if (DEBUG) {
							Msg.debug(this, "Indirect Branch to [" + values[0].getAddress() +
								"] -> " + destAddr);
						}
						handleDirectFlow(pcodeOp, destAddr, currentState, monitor);
					}
					else {
						if (DEBUG) {
							Msg.debug(this,
								"Indirect Branch to [" + values[0].toString(program.getLanguage()) +
									"]");
						}
						handleIndirectFlow(pcodeOp, values[0], currentState, monitor);
					}
				}
				else {
					if (DEBUG) {
						Msg.debug(this,
							"Indirect Branch to [" + values[0].toString(program.getLanguage()) +
								"]");
					}
					handleIndirectFlow(pcodeOp, values[0], currentState, monitor);
				}
				return false;

			case PcodeOp.CALL:		        // A call with absolute address	  
				if (inputs[0].isAddress()) {
					if (DEBUG) {
						Msg.debug(this, "Call to " + inputs[0].getAddress());
					}
					handleCall(pcodeOp, null, inputs[0].getAddress(), currentState, monitor);
				}
				else {
					//TODO: This should never occur
					throw new AssertException();
				}
				return false;

			case PcodeOp.CALLIND:			// An indirect call
				Address indirectPtr = inputs[0].isAddress() ? inputs[0].getAddress() : null;
// TODO: When indirect through a register we loose where the original pointer came from (indirect=null) which mean we can't find the thunk function
				if (values[0].isConstant()) {
					AddressSpace space = currentState.getEntryPoint().getTarget().getAddressSpace();
					Address destAddr =
						space.getAddress(getUnsignedOffset(values[0], space.getPointerSize()));
					if (DEBUG) {
						Msg.debug(this, "Call to " + destAddr);
					}
					handleCall(pcodeOp, indirectPtr, destAddr, currentState, monitor);
				}
				else if (values[0].isAddress()) {

					Varnode callOffset = currentState.get(values[0], monitor);
					if (callOffset != null && callOffset.isConstant() &&
						callOffset.getOffset() != 0) {
						AddressSpace space =
							currentState.getEntryPoint().getTarget().getAddressSpace();
						Address destAddr =
							space.getAddress(getUnsignedOffset(callOffset, space.getPointerSize()));
						if (DEBUG) {
							Msg.debug(this, "Indirect Call to [" + values[0].getAddress() +
								"] -> " + destAddr);
						}
						handleCall(pcodeOp, indirectPtr, destAddr, currentState, monitor);
					}
					else {
						if (DEBUG) {
							Msg.debug(this,
								"Indirect Call to [" + values[0].toString(program.getLanguage()) +
									"]");
						}
						handleIndirectCall(pcodeOp, indirectPtr, values[0], currentState, monitor);
					}
				}
				else {
					if (DEBUG) {
						Msg.debug(this,
							"Indirect Call to [" + values[0].toString(program.getLanguage()) + "]");
					}
					handleIndirectCall(pcodeOp, indirectPtr, values[0], currentState, monitor);
				}
				return false;

			case PcodeOp.CALLOTHER:    		// Other unusual subroutine calling conventions
				// TODO: Affects are ignored
				break;

			case PcodeOp.RETURN:			// A return from subroutine
				addEndState(pcodeOp.getSeqnum(), currentState);
				return false;

		}

		return true;
	}
Process an instruction pcode operation represented by a VarnodeOperation.
@param op
@param currentState
@param monitor task monitor
@return true if state can continue to be propagated, otherwise a new state is
required due to the processing of a branch operation.
@throws CancelledException
@throws InlineCallException
Ghidra/Features/Base/src/main/java/ghidra/util/state/ResultsState.java