protected void disassembleInstructionBlock(InstructionBlock block, MemBuffer blockMemBuffer,
			Address flowFrom, int limit, InstructionSet instructionSet,
			boolean skipIfBlockAlreadyDisassembled) {

		Address addr = blockMemBuffer.getAddress();
		repeatInstructionByteTracker.reset();

		if (initializedAddressSet != null && !initializedAddressSet.contains(addr)) {
			setMemoryConstraintError(block, flowFrom);
			return;
		}

		Instruction existingBlockStartInstr = null;
		if (skipIfBlockAlreadyDisassembled) {
			InstructionBlock existingBlock = instructionSet.getInstructionBlockContaining(addr);
			if (existingBlock != null && !existingBlock.isEmpty()) {
				existingBlockStartInstr = existingBlock.getInstructionAt(addr);
				if (existingBlockStartInstr == null) { // offcut condition
					existingBlockStartInstr =
						existingBlock.findFirstIntersectingInstruction(addr, addr);
					// TODO: no guarantee that the conflict code unit will actually get 
					// added to program - it may conflict itself with a program code unit
					block.setCodeUnitConflict(existingBlockStartInstr.getAddress(), addr, flowFrom,
						true, true);
					return;
				}
				else if (existingBlockStartInstr.isInDelaySlot() &&
					!delaySlottedInstructionHasFallthrough(existingBlockStartInstr,
						existingBlock)) {
					// Flow into existing delay slot - if delay-slotted instruction does not 
					// fall-through, we need to resume block after delay slots
					block = existingBlock;
					addr = existingBlock.getMaxAddress().next();
					existingBlockStartInstr = null;
				}
			}
		}

		try {
			while (!monitor.isCancelled() && addr != null) {

				if (restrictedAddressSet != null && !restrictedAddressSet.contains(addr)) {
					blockTerminated(block);
					return; // no fall-through
				}

				disassemblerContext.flowToAddress(addr);

				MemBuffer instrMemBuffer =
					new WrappedMemBuffer(blockMemBuffer, DISASSEMBLE_MEMORY_CACHE_SIZE,
						(int) addr.subtract(blockMemBuffer.getAddress()));

				InstructionPrototype prototype = parseInstructionPrototype(instrMemBuffer, block);

				RegisterValue contextValue = null;
				if (baseContextRegister != null) {
					contextValue = disassemblerContext.getRegisterValue(baseContextRegister);
				}

				// if fall-through already exists in another block - check for conflict 
				// and terminate block
				if (!block.isEmpty() && instructionSet != null &&
					instructionSet.containsBlockAt(addr)) {
					existingBlockStartInstr = instructionSet.getInstructionAt(addr);
					if (existingBlockStartInstr != null) {
						InstructionPrototype existingProto = existingBlockStartInstr.getPrototype();
						if (!existingProto.equals(prototype)) {

							PseudoInstruction badInst = getPseudoInstruction(instrMemBuffer,
								prototype, contextValue, block);
							InstructionError.dumpInstructionDifference(badInst,
								existingBlockStartInstr);

							block.setInconsistentPrototypeConflict(addr, flowFrom);
						}
						blockTerminated(block);
						return;
					}
					// existing block must be an empty conflicted block - just keep going 
				}

				if (skipIfBlockAlreadyDisassembled && block.isEmpty()) {
					// start of block - skip block if already disassembled in the same way
					if (existingBlockStartInstr == null) {
						// check program if not previously found within instruction set
						existingBlockStartInstr = listing.getInstructionAt(addr);
					}
					if (existingBlockStartInstr != null) {
						InstructionPrototype existingProto = existingBlockStartInstr.getPrototype();
						if (existingProto.isInDelaySlot()) {
							// redo prototype parse for delay slot comparison
							prototype = language.parse(instrMemBuffer, disassemblerContext, true);
							if (existingProto.equals(prototype)) {
								// delay slots assumed to always fall-through - queue next addr
								disassemblerContext.copyToFutureFlowState(addr);
								if (disassemblerQueue != null) {
									disassemblerQueue
											.queueDelaySlotFallthrough(existingBlockStartInstr);
								}
								blockTerminated(block);
								return;
							}
						}
						else if (existingProto.equals(prototype)) {
							// skip block start silently if it was previously disassembled
							blockTerminated(block);
							return;
						}

						PseudoInstruction badInst =
							getPseudoInstruction(instrMemBuffer, prototype, contextValue, block);
						InstructionError.dumpInstructionDifference(badInst,
							existingBlockStartInstr);

						block.setInconsistentPrototypeConflict(addr, flowFrom);
					}
				}

				PseudoInstruction inst =
					getPseudoInstruction(instrMemBuffer, prototype, contextValue, block);

				Address maxAddr = inst.getMaxAddress();
				if (instructionSet != null && instructionSet.intersects(addr, maxAddr)) {
					InstructionBlock existingBlock =
						instructionSet.getInstructionBlockContaining(addr);
					Instruction existingInstr = null;
					if (existingBlock != null) {
						existingInstr = existingBlock.getInstructionAt(addr);
					}
					if (existingInstr == null) {
						if (existingBlock == null) {
							existingBlock =
								instructionSet.findFirstIntersectingBlock(addr, maxAddr);
						}
						existingInstr =
							existingBlock.findFirstIntersectingInstruction(addr, maxAddr);
						// TODO: no guarantee that the conflict code unit will actually get 
						// added to program - it may conflict itself with a program code unit
						block.setCodeUnitConflict(existingInstr.getAddress(), addr, flowFrom, true,
							true);
					}
					else {
						InstructionPrototype existingProto = existingInstr.getPrototype();
						if (existingInstr.isInDelaySlot()) {
							// redo prototype parse for delay slot comparison
							prototype = language.parse(instrMemBuffer, disassemblerContext, true);
						}

						if (!existingProto.equals(prototype)) {
							PseudoInstruction badInst = getPseudoInstruction(instrMemBuffer,
								prototype, contextValue, block);
							InstructionError.dumpInstructionDifference(badInst,
								existingBlockStartInstr);

							block.setInconsistentPrototypeConflict(addr, flowFrom);
						}
					}
					blockTerminated(block);
					return;
				}

				if (repeatInstructionByteTracker.exceedsRepeatBytePattern(inst)) {
					block.setParseConflict(addr, contextValue, flowFrom,
						"Maximum run of repeated byte instructions exceeded");
				}

				// process instruction flows and obtain fallthrough address
				addr = processInstruction(inst, blockMemBuffer, block, instructionSet);

				if (addr == null || block.hasInstructionError()) {
					blockTerminated(block);
					return;
				}
				if (endBlockEarly(inst, addr, limit, block) || endBlockOnCall(inst, addr, block)) {
					// Preserve fallthrough context for future disassembly continuation.
					// No need to set block fallthrough, since special block flows
					// are added to facilitate future prioritization of flows
					// block.setFallThrough(addr);
					disassemblerContext.copyToFutureFlowState(addr);
					blockTerminated(block);
					return;
				}

				flowFrom = inst.getMinAddress();
			}
		}
		catch (AddressOutOfBoundsException | AddressOverflowException e) {
			block.setInstructionMemoryError(addr, flowFrom,
				"Instruction does not fit within address space constraint");
			blockTerminated(block);
		}
		catch (InsufficientBytesException e) {
			block.setInstructionMemoryError(addr, flowFrom, e.getMessage());
			blockTerminated(block);
		}
		catch (UnknownInstructionException e) {
			block.setParseConflict(addr,
				disassemblerContext.getRegisterValue(disassemblerContext.getBaseContextRegister()),
				flowFrom, e.getMessage());
			blockTerminated(block);
		}
	}
Examine delay-slotted instruction to determine if it has a fall-through
@param dsInstr pseudo-instruction within existingBlock
@param existingBlock InstructionBlock containing dsInstr
@return true if fallthrough exists
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/disassemble/Disassembler.java