private InstructionSet disassembleNextInstructionSet(InstructionBlock firstBlock,
			DumbMemBufferImpl programMemBuffer, AddressSetView previouslyDisassembled) {

		InstructionSet instructionSet = new InstructionSet(program.getAddressFactory());

		Address fallThruAddr = firstBlock.getStartAddress(); // allow us to enter loop with initial block

		InstructionBlock nextBlock;
		while ((nextBlock = disassemblerQueue.getNextBlockToBeDisassembled(fallThruAddr,
			programMemBuffer.getMemory(), monitor)) != null) {

			Address blockAddr = nextBlock.getStartAddress();
			Address flowFrom = nextBlock.getFlowFromAddress();

			if (flowFrom != null) {
				InstructionBlock containingBlock =
					instructionSet.getInstructionBlockContaining(blockAddr);
				if (containingBlock != null && containingBlock.getInstructionAt(flowFrom) != null) {
					// Skip block if start address already handled as a fallthrough.
					// NOTE: This behavior is relied upon by customized disassemblers which may
					// create a conjoined InstructionBlock for an unconditional branch which 
					// behaves like a fallthrough flow.
					continue;
				}
			}

			if (!disassemblerContext.isFlowActive()) {
				disassemblerContext.flowStart(blockAddr);
			}

			programMemBuffer.setPosition(blockAddr);
			disassembleInstructionBlock(nextBlock, programMemBuffer, nextBlock.getFlowFromAddress(),
				instructionSetSizeLimit - instructionSet.getInstructionCount(), instructionSet,
				true);
			if (monitor.isCancelled()) {
				break;
			}

			if (nextBlock.isEmpty()) {
				// no memory/bytes at specified blockAddr
				if (nextBlock.hasInstructionError()) {
					instructionSet.addBlock(nextBlock);
				}
				disassemblerContext.flowEnd(blockAddr);
				fallThruAddr = null;
			}
			else {
				instructionSet.addBlock(nextBlock);
				fallThruAddr = nextBlock.getFallThrough();
				if (fallThruAddr == null) {
					disassemblerContext.flowEnd(nextBlock.getMaxAddress());
				}
			}
			if (instructionSet.getInstructionCount() >= instructionSetSizeLimit) {
				// instruction set truncated
				break;
			}
		}
		if (disassemblerContext.isFlowActive()) {
			disassemblerContext.flowAbort();
		}
		return instructionSet;
	}
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