public AddressSet followSubFlows(Address entryPoint, PseudoDisassemblerContext procContext,
			int maxInstr, PseudoFlowProcessor processor) {
		AddressSet body = new AddressSet();
		AddressSet instrStarts = new AddressSet();

		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

		// if entry point starts with 00 byte instruction, assume not valid
		Address tempAddr;

		try {
			tempAddr = entryPoint;

			byte[] ptrbytes = new byte[pointerSize];
			if (memory.getBytes(tempAddr, ptrbytes) == ptrbytes.length) {
				boolean allZero = true;
				for (byte ptrbyte : ptrbytes) {
					if (ptrbyte != 0) {
						allZero = false;
						break;
					}
				}
				if (allZero) {
					return body;
				}
			}
		}
		catch (MemoryAccessException e1) {
			return body;
		}
		catch (AddressOutOfBoundsException e2) {
			return body;
		}

		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 < maxInstr; i++) {
				PseudoInstruction instr;
				instr = disassemble(target, procContext, false);

				boolean doContinue = processor.process(instr);
				if (!doContinue) {
					return body;
				}

				if (instr == null) {
					target = getNextTarget(body, untriedTargetList);
					continue;
				}
				Address newTarget = null;
				body.addRange(instr.getMinAddress(), instr.getMaxAddress());
				instrStarts.addRange(instr.getMinAddress(), instr.getMinAddress());

				// check whether processor wants to follow flow on this instruction
				if (!processor.followFlows(instr)) {
					target = getNextTarget(body, untriedTargetList);
					continue;
				}

				// if instruction has fall thru
				if (instr.hasFallthrough()) {
					newTarget = instr.getFallThrough();
				}
				else {
					// check if any forward jump reference is targeted right after this instruction
					Address nextAddr = instr.getMaxAddress().next();
					if (targetList.contains(nextAddr)) {
						newTarget = nextAddr;
					}
					else if (instr.getFlowType().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);
					}
				}

				// if this is a jump, add it's targets to list of valid
				//   forward reference continuation points.
				if (instr.getFlowType().isJump()) {
					Address flows[] = instr.getFlows();
					if (flows != null) {
						for (Address address : flows) {
							targetList.add(address);
							untriedTargetList.add(address);
						}
					}
				}
				target = newTarget;
			}
		}
		catch (InsufficientBytesException e) {
			processor.process(null);
		}
		catch (UnknownInstructionException e) {
			processor.process(null);
		}
		catch (UnknownContextException e) {
			processor.process(null);
		}

		return body;
	}
Process a subroutine using the processor function.
The process function can control what flows are followed and when to stop.

@param entryPoint start address
@param procContext initial processor context for disassembly
@param maxInstr maximum number of instructions to evaluate
@param processor processor to use
@return the address set of instructions that were followed
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/util/PseudoDisassembler.java