public boolean checkValidSubroutine(Address entryPoint, PseudoDisassemblerContext procContext,
			boolean allowExistingInstructions, boolean mustTerminate, boolean requireContiguous) {
		
		AddressSet contiguousSet = new AddressSet();

		lastCheckValidDisassemblyCount = 0;
	
		if (!entryPoint.isMemoryAddress()) {
			return false;
		}
		AddressSet body = new AddressSet();
		AddressSet instrStarts = new AddressSet();
		AddressSetView execSet = memory.getExecuteSet();

		entryPoint = setTargetContextForDisassembly(procContext, entryPoint);

		Address target = entryPoint;

		ArrayList<Address> targetList = new ArrayList<>(); // list of valid targets
		ArrayList<Address> untriedTargetList = new ArrayList<>(); // list of valid targets
		boolean didTerminate = false;
		boolean didCallValidSubroutine = false;

		// if entry point starts with 00 byte instruction, assume not valid
		try {
			if (memory.getLong(entryPoint) == 0) {
				return false;
			}
		}
		catch (MemoryAccessException e1) {
			return false;
		}
		catch (AddressOutOfBoundsException e2) {
			return false;
		}

		RepeatInstructionByteTracker repeatInstructionByteTracker =
			new RepeatInstructionByteTracker(MAX_REPEAT_BYTES_LIMIT, null);

		procContext.flowStart(entryPoint);
		try {
			// look some number of fallthroughs to see if this
			//   is a valid run of instructions.

			for (int i = 0; target != null && i < maxInstructions; i++) {
				if (target.compareTo(procContext.getAddress()) < 0) {
					procContext.copyToFutureFlowState(target);
					procContext.flowEnd(procContext.getAddress());
					procContext.flowStart(target);
				}
				else {
					procContext.flowToAddress(target);
				}
				PseudoInstruction instr = disassemble(target, procContext, false);
				
				if (instr == null) {
					// if the target is in the external section, which is uninitialized, ignore it!
					//    it is probably a JUMP to an external function.
					MemoryBlock block = memory.getBlock(target);
					if (block == null || block.isInitialized() ||
						!block.getName().equals(MemoryBlock.EXTERNAL_BLOCK_NAME)) {
						return false;
					}
					targetList.remove(target);
					target = getNextTarget(body, untriedTargetList);
					repeatInstructionByteTracker.reset();
					continue;
				}
				
				// count valid instructions encountered, if checking contiguous only count if instructions merge into the first range
				if (contiguousSet.isEmpty() || !requireContiguous || contiguousSet.getFirstRange().getMaxAddress().isSuccessor(target)) {
					contiguousSet.add(instr.getMinAddress(),instr.getMaxAddress());
					lastCheckValidDisassemblyCount++;
				}
				
				// check if we are getting into bad instruction runs
				if (repeatInstructionByteTracker.exceedsRepeatBytePattern(instr)) {
					return false;
				}

				Address maxAddr = instr.getMaxAddress();

				Address newTarget = null;
				body.addRange(target, maxAddr);
				instrStarts.add(target);

				// If this is a delay slot instruction - make sure delay slots disassemble OK
				int delaySlots = instr.getDelaySlotDepth();
				Address addr = maxAddr;
				for (int delaySlot = 0; delaySlot < delaySlots; delaySlot++) {
					try {
						addr = addr.addNoWrap(1);
					}
					catch (AddressOverflowException e) {
						return false;
					}
					procContext.flowToAddress(addr);
					PseudoInstruction dsInstr = disassemble(addr, procContext, true);
					if (dsInstr == null) {
						return false;
					}
					maxAddr = dsInstr.getMaxAddress();
					body.addRange(addr, maxAddr);
					instrStarts.add(addr);
					addr = maxAddr;
				}

				FlowType flowType = instr.getFlowType();
				if (flowType.isTerminal()) {
					didTerminate |= isReallyReturn(instr);
				}

				// if instruction has fall thru
				Address fallThru = null;
				if (instr.hasFallthrough()) {
					if (checkNonReturning(program, flowType, instr)) {
						target = getNextTarget(body, untriedTargetList);
						repeatInstructionByteTracker.reset();
						continue;
					}
					newTarget = instr.getFallThrough();
					fallThru = newTarget;
				}
				else {
					// check if any forward jump reference is targeted right after this instruction
					Address nextAddr = maxAddr.next();
					if (targetList.contains(nextAddr)) {
						newTarget = nextAddr;
					}
					else if (flowType.isJump()) {
						// if this is a jump, and jumps forward only some number
						// of bytes
						// make that the new target.
						Address flows[] = instr.getFlows();
						if (flows != null) {
							for (Address address : flows) {
								if (!body.contains(address)) {
									newTarget = address;
									break;
								}
							}
						}
					}

					if (newTarget == null) {
						newTarget = getNextTarget(body, untriedTargetList);
						repeatInstructionByteTracker.reset();
					}
				}

				// if this is a jump, add it's targets to list of valid
				// forward reference continuation points.
				if (flowType.isJump()) {
					Address flows[] = instr.getFlows();
					if (flows != null && flows.length > 0) {
						for (Address address : flows) {
							// if jump target is the same as the fallthru
							// Instructions with delay slots are allowed.
							if (fallThru != null &&
								address.equals(fallThru) & !instr.getPrototype().hasDelaySlots()) {
								return false;
							}
							// if this code jumps to an existing function, allow it
							Function func = null;
							if (program != null) {
								func = program.getFunctionManager().getFunctionAt(address);
							}
							if (func != null) {
								didCallValidSubroutine = true;
								newTarget = getNextTarget(body, untriedTargetList);
								repeatInstructionByteTracker.reset();
								continue;
							}
							targetList.add(address);
							untriedTargetList.add(address);
						}
					}
					else if (flowType.isComputed()) {
						didTerminate = true;
					}
				}
				if (flowType.isCall() || (flowType.isJump() && flowType.isComputed())) {
					Address flows[] = instr.getFlows();
					if (flows == null || flows.length == 0) {
						Reference[] refsFrom = instr.getReferencesFrom();
						if (refsFrom != null && refsFrom.length > 0) {
							flows = new Address[1];
							flows[0] = refsFrom[0].getToAddress();
						}
					}
					if (flows != null && flows.length > 0) {
						for (Address flow : flows) {
							// does this reference a valid function?
							if (program != null) {
								Symbol primary = program.getSymbolTable().getPrimarySymbol(flow);
								if (primary != null &&
									primary.getSymbolType() == SymbolType.FUNCTION) {
									didCallValidSubroutine = true;
								}
							}
							// if respecting execute flag on memory, test to make sure we did flow into non-execute memory
							if (respectExecuteFlag && !execSet.isEmpty() &&
								!execSet.contains(flow)) {
								if (!flow.isExternalAddress()) {
									MemoryBlock block = memory.getBlock(flow);
									// flowing into non-executable, but readable memory is bad
									if (block != null && block.isRead() &&
										!MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName())) {
										return false;
									}
								}
							}
						}
					}
				}
				target = newTarget;
			}
		}
		catch (InsufficientBytesException e) {
			// can't parse not enough bytes
			return false;
		}
		catch (UnknownInstructionException e) {
			// bad instruction
			return false;
		}
		catch (UnknownContextException e) {
			// something wrong with context
			return false;
		}

		// get rid of anything on target list that is in body of instruction
		Iterator<Address> iter = targetList.iterator();
		while (iter.hasNext()) {
			Address targetAddr = iter.next();
			if (body.contains(targetAddr)) {
				iter.remove();
				// if this target does not refer to an instruction start.
				if (!instrStarts.contains(targetAddr)) {
					return false;
				}
			} else if (maxInstructions > 0) {
				// if there was a maximum, then don't worry about targets that
				// were never followed
				iter.remove();
			}
		}


		// if target list is empty, and we are at a terminal instruction
		if (targetList.isEmpty() && (didTerminate || !mustTerminate || didCallValidSubroutine)) {
			// check that the body of the function doesn't break any rules.
			return checkPseudoBody(entryPoint, body, instrStarts, allowExistingInstructions,
				didCallValidSubroutine);
		}

		return false;
	}
Check if there is a valid subroutine at the target address

@param entryPoint address to check
@param procContext processor context to use when pseudo disassembling instructions
@param allowExistingInstructions true to allow running into existing instructions
@param mustTerminate true if the subroutine must hit a terminator (return) instruction
@param requireContiguous true if the caller will require some number of contiguous instructions
       call getLastCheckValidInstructionCount() to get the initial number of contiguous instructions
       if this is true

@return true if entryPoint is the probable subroutine start
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoDisassembler.java