public AddressSet disassemble(Address startAddr, AddressSetView restrictedSet,
			RegisterValue initialContextValue, boolean doFollowFlow) {

		if (program == null) {
			throw new UnsupportedOperationException(
				"Method requires instantiation with a Program object");
		}

		this.followFlow = doFollowFlow;
		this.restrictedAddressSet = restrictedSet;

		if (initialContextValue != null &&
			initialContextValue.getRegister().getBaseRegister() != baseContextRegister) {
			throw new IllegalArgumentException("Invalid initialContextValue");
		}

		AddressSet disassembledAddrs = new AddressSet();
		AddressSet reallyDisassembledAddrs = new AddressSet();

		int addressableUnitSize = startAddr.getAddressSpace().getAddressableUnitSize();
		if ((instAlignment % addressableUnitSize) != 0 ||
			(startAddr.getOffset() % instAlignment) != 0) {
			reportMessage("Disassembly address " + startAddr + " violates " + instAlignment +
				"-byte instruction alignment");
			return reallyDisassembledAddrs;
		}

		disassemblerQueue = new DisassemblerQueue(startAddr, restrictedSet);

		DumbMemBufferImpl memBuffer = new DumbMemBufferImpl(program.getMemory(), startAddr);

		DisassemblerContextImpl seed = seedContext;
		if (seed != null) {
			RegisterValue seedValue = seed.getFlowContextValue(startAddr, false);
			if (seedValue != null) {
				disassemblerContext.setFutureRegisterValue(startAddr, seedValue);
			}
		}
		if (initialContextValue != null) {
			RegisterValue initialValue = disassemblerContext.getFlowContextValue(startAddr, false);
			if (initialValue != null) {
				initialValue = initialValue.combineValues(initialContextValue);
				disassemblerContext.setFutureRegisterValue(startAddr, initialValue);
			}
		}

		while (disassemblerQueue.continueProducingInstructionSets(monitor)) {

			try {
				InstructionBlock nextBlock =
					disassemblerQueue.getNextBlockToBeDisassembled(null, program.getMemory(), null);
				if (nextBlock == null) {
					break;
				}
				Address blockAddr = nextBlock.getStartAddress();
				CodeUnit cu = listing.getCodeUnitAt(blockAddr);
				if (cu instanceof Instruction) {
					continue; // skip call point silently if it was previously disassembled
				}
				if (!(cu instanceof Data) || ((Data) cu).isDefined()) {
					//Address flowAddr = flow.getDestinationAddress();
					if (cu == null) {
						// check for offcut conflict
						cu = listing.getCodeUnitContaining(blockAddr);
					}
					if (cu != null) {
						markCallConflict(blockAddr, blockAddr, cu);
						continue; // mark and skip call point if it conflicts with another code unit
					}
				}

				InstructionSet instructionSet =
					disassembleNextInstructionSet(nextBlock, memBuffer, disassembledAddrs);
				if (instructionSet == null) {
					continue;
				}

				// add instructions to program
				if (instructionSet.getInstructionCount() != 0) {
					AddressSetView newDisassembledAddrs =
						listing.addInstructions(instructionSet, false);
					if (newDisassembledAddrs != null) {
						if (doMarkUnimplPcode && !newDisassembledAddrs.isEmpty()) {
							markUnimplementedPcode(program, newDisassembledAddrs, monitor);
						}
						reallyDisassembledAddrs.add(newDisassembledAddrs);
					}
					disassembledAddrs.add(instructionSet.getAddressSet());
				}

				// Clear accumulated temporary program context which should
				// have been written to program
				disassemblerProgramContext.clearTemporaryContext();

				// check for disassembly errors and update disassembler queue
				disassembleCount +=
					disassemblerQueue.instructionSetAddedToProgram(instructionSet, this);

				if (disassembleCount >= NUM_ADDRS_FOR_NOTIFICATION) {
					totalCount += disassembleCount;
					monitor.setMessage("Disassembled  " + (totalCount / 1024) + " K");
					disassembleCount = 0;
				}
			}
			catch (CancelledException e) {
				break;
			}
			catch (CodeUnitInsertionException e) {
				Msg.error(this, e.getMessage());
			}
		}
		return reallyDisassembledAddrs;
	}
Disassembles code starting at startAddr and restricted to addrSet.
NOTE: A single instance of this Disassembler does not support concurrent
invocations of the various disassemble methods.  
Disassembler must be instantiated with a Program object.
@param startAddr the address to begin disassembling.
@param restrictedSet the set of addresses that disassembling is restricted to.
@param initialContextValue initial context value to be applied at the
startAddr.  If not null this value will take precedence when combined with
any seed value or program context.
@param doFollowFlow flag to follow references while disassembling.
@return AddressSet the set of addresses that were disassembled.
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java