Skip to content
Snippets Groups Projects
SMPStaticAnalyzer.cpp 112 KiB
Newer Older
clc5q's avatar
clc5q committed
#if SMP_DEBUG_FIXUP_IDB
clc5q's avatar
clc5q committed
#endif
			return false;
		}
	} // end if (0 >= InstrLen) ... else ...
	return true;
} // end of MDPatchUnconvertedBytes()

// Use the lists of code addresses identified by IDA Pro (in IDAProLocs)
//  and an external disassembler (in DisasmLocs). Compare the lists and
//  try to convert addresses to code that are found in DisasmLocs but
//  not in IDAProLocs. Emit warnings when IDAProLocs has a code address
//  not found in DisasmLocs.
void FixCodeIdentification(void) {
	size_t DisasmIndex = 0;
	ea_t CurrDisasmAddr = DisasmLocs[DisasmIndex++];
	size_t IDAProIndex = 0;
	ea_t CurrAddr = IDAProLocs[IDAProIndex++];

	while (DisasmIndex <= DisasmLocs.size()) {
		// If the current address is less than the current
		//  external disasm address, we have the rare case in
		//  which IDA Pro has identified an address as code
		//  but the external disasm has not. Emit a warning
		//  message and go on to the next IDA address.
		if (CurrAddr < CurrDisasmAddr) {
			SMPInstr TempInstr(CurrAddr);
			TempInstr.Analyze();
			msg("Address %x is code in IDB but not in external disassembler: %s\n",
				CurrAddr, TempInstr.GetDisasm());
			if (IDAProIndex < IDAProLocs.size())
				CurrAddr = IDAProLocs[IDAProIndex++];
			else {
				// Last IDA addr; might still process Disasm addrs
				//  after loop exit.
				break;
			}
		}
		else if (CurrAddr == CurrDisasmAddr) {
			// If equal, no problem, we are moving through the
			//  code addresses in lockstep. Grab the next address
			//  from each source.
			if (DisasmIndex < DisasmLocs.size()) {
				CurrDisasmAddr = DisasmLocs[DisasmIndex++];
			}
			else {
				++DisasmIndex;  // cause loop exit; skip cleanup loop
			}
			if (IDAProIndex < IDAProLocs.size())
				CurrAddr = IDAProLocs[IDAProIndex++];
			else {
				// Last IDA addr; might still process Disasm addrs
				//  after loop exit in cleanup loop.
				break;
			}
		}
		else {
			// We must have CurrAddr > CurrDisasmAddr. That means
			//  IDA has jumped over some code addresses in
			//  DisasmLocs. We need to try to convert addresses
			//  to code until we can reach the current addr.
			int InstrLen;
			// For now, we will address only the case in which IDA
			//  has identified addresses as data bytes, and the
			//  external disassembler(e.g. objdump) has identified
			//  the same addresses as code. We only want to deal with
			//  contiguous areas of data-to-code conversion that do NOT
			//  follow a return statement.
			int AreaSize = 0;
			ea_t AreaStart = CurrDisasmAddr;
			ea_t AreaEnd;
#if SMP_DEBUG_FIXUP_IDB
			msg("CurrDisasmAddr: %x  CurrAddr: %x\n", CurrDisasmAddr, CurrAddr);
#endif
			bool SkipArea = FindDataToConvert(IDAProIndex, DisasmIndex, AreaSize);
			if (SkipArea) {
				// Skip over the extra external disasm addresses.
				while (CurrDisasmAddr < CurrAddr)
					CurrDisasmAddr = DisasmLocs[DisasmIndex++];
			}
			else { 
				// Convert the overlooked code region to unexplored.
				AreaEnd = CurrDisasmAddr + AreaSize;
#if SMP_DEBUG_FIXUP_IDB
				msg("Found data to convert: %x to %x\n", AreaStart, AreaEnd);
#endif
				do_unknown_range(AreaStart, AreaSize, DOUNK_SIMPLE);
				SMP_bounds_t ConvertRegion;
				ConvertRegion.startEA = AreaStart;
				ConvertRegion.endEA = AreaEnd;
				FixupRegion CurrRegion(ConvertRegion);
				CodeReanalyzeList.push_back(CurrRegion);
				do {
					flags_t InstrFlags = getFlags(CurrDisasmAddr);
					if (!isUnknown(InstrFlags)) {
						msg("Sync problem in FixCodeID: %x\n", CurrDisasmAddr);
					}
					else {
#if IDA_SDK_VERSION < 600
						InstrLen = ua_code(CurrDisasmAddr);
#else
						InstrLen = create_insn(CurrDisasmAddr);
						if (InstrLen > 0) { // Successfully converted to code
							SMPInstr NewInstr(CurrDisasmAddr);
							NewInstr.Analyze();
							msg("FixCodeID success at %x: len: %d %s\n", CurrDisasmAddr,
									InstrLen, NewInstr.GetDisasm());
#endif
						}
						else {
							if (MDPatchUnconvertedBytes(CurrDisasmAddr)) {
clc5q's avatar
clc5q committed
								;
#if SMP_DEBUG_FIXUP_IDB
								msg(" Patched bytes at %x\n", CurrDisasmAddr);
clc5q's avatar
clc5q committed
#endif
								CurrRegion.FixupInstrs.push_back(CurrDisasmAddr);
clc5q's avatar
clc5q committed
#if SMP_DEBUG_FIXUP_IDB
								msg("FixCodeID failure at %x\n", CurrDisasmAddr);
clc5q's avatar
clc5q committed
#endif
							}
						}
					} // end if (isCode(InstrFlags) ... else ...
					if (DisasmIndex < DisasmLocs.size()) {
						CurrDisasmAddr = DisasmLocs[DisasmIndex++];
					}
					else {
						// cause loops to exit
						CurrDisasmAddr = CurrAddr;
						++DisasmIndex; // skip cleanup loop
					}
				} while (CurrDisasmAddr < CurrAddr);
				if (AllConverted && AllNops) {
					// We want to convert the region back to unexplored bytes
					//  and take it off the work list. Regions that are all nops
					//  create data flow analysis problems sometimes. The region
					//  is often unreachable code and produces a basic block with
					//  no predecessors within a function. This often happens when
					//  an optimizing compiler uses nops as padding to align jump
					//  targets on cache line bounaries. With no fall through into
					//  the nops, they are unreachable and should be left as unknown.
					msg("FixCodeID nops region from %x to %x\n", CurrRegion.GetStart(),
						CurrRegion.GetEnd());
					do_unknown_range(CurrRegion.GetStart(),
						CurrRegion.GetEnd() - CurrRegion.GetStart(), DOUNK_SIMPLE);
					CodeReanalyzeList.pop_back();
				}
			} // end if (SkipArea) ... else ...
		} // end if (addr < CurrDisasmAddr) .. else if ... else ...
	} // end while (DisasmIndex <= DisasmLocs.size()

#if 0  // Make this code use FindDataToConvert()  **!!**
	// Cleanup loop:
	// If there are still Disasm addrs to process, try to turn them
	//  into code in the IDB.
	while (DisasmIndex <= DisasmLocs.size()) {
		flags_t InstrFlags = getFlags(CurrDisasmAddr);
		if (isCode(InstrFlags)) {
			msg("Sync problem in FixCodeID: %x\n", CurrDisasmAddr);
		}
		else {
			// Clear bytes to unexplored.
			segment_t *seg = getseg(CurrDisasmAddr);
			if (SEG_CODE == seg->type) {
				do_unknown_range(CurrDisasmAddr, seg->endEA - CurrDisasmAddr, DOUNK_SIMPLE);
			}
			else {
				// Might be safest to just discontinue processing
				//  if we wander into a non-code segment.
				//  DisasmLocs should not have an entire code segment
				//  that IDA Pro missed.
				break;
			}
			int InstrLen = ua_code(CurrDisasmAddr);
			if (InstrLen > 0) { // Successfully converted to code
				SMPInstr NewInstr(CurrDisasmAddr);
				NewInstr.Analyze();
				msg("FixCodeID success at %x: %s\n", CurrDisasmAddr,
						NewInstr.GetDisasm());
			}
			else {
				msg("FixCodeID failure at %x\n", CurrDisasmAddr);
			}
		} // end if (isCode(InstrFlags) ... else ...
		if (DisasmIndex < DisasmLocs.size()) {
			CurrDisasmAddr = DisasmLocs[DisasmIndex++];
		}
		else {
			++DisasmIndex; // cause loop to exit
		}
	} // end while (DisasmIndex <= DisasmLocs.size()
#endif

	return;
} // end of FixCodeIdentification()
// Analyze instructions that could not be analyzed earlier and were placed on the CodeReanalyzeList.
//  Earlier failures are usually because the instruction branches to an address that has not
//  yet been converted from data to code, so ua_code() failed. Now that all data to code
//  conversions have completed, ua_code() should succeed.
// Return the number of instructions successfully analyzed.
int FixupNewCodeChunks(void) {
	list<FixupRegion>::iterator CurrRegion;
	int changes = 0;
	for (CurrRegion = CodeReanalyzeList.begin(); CurrRegion != CodeReanalyzeList.end(); ++CurrRegion) {
		bool AllConverted = true;
		bool AllNops = true;
		bool NoFixups = (0 == CurrRegion->FixupInstrs.size());
		if (NoFixups) {
			CurrRegion->SetStart(BADADDR);  // mark for removal
			continue;  // skip to next region
		}
		list<ea_t>::iterator CurrInstr;
		for (CurrInstr = CurrRegion->FixupInstrs.begin(); CurrInstr != CurrRegion->FixupInstrs.end(); ++CurrInstr)  {
#if IDA_SDK_VERSION < 600
			int InstrLen = ua_code(*CurrInstr);
#else
			int InstrLen = create_insn(*CurrInstr);
			if (InstrLen > 0) { // Successfully converted to code
				SMPInstr NewInstr(*CurrInstr);
				NewInstr.Analyze();
#if SMP_DEBUG_FIXUP_IDB
				msg("FixupNewCodeChunks success at %x: len: %d\n", *CurrInstr, InstrLen);
#endif
					AllNops = false;
					*CurrInstr = BADADDR; // mark for removal
				}
			}
			else {
				AllConverted = false;
#if SMP_DEBUG_FIXUP_IDB
				msg("FixupNewCodeChunks failure at %x\n", *CurrInstr);
#endif
			}
		} // end for all instrs in CurrRegion
		if (AllConverted && !AllNops) {
#if SMP_DEBUG_FIXUP_IDB
			msg("FixupNewCodeChunks success for region from %x to %x\n",
				CurrRegion->GetStart(), CurrRegion->GetEnd());
#endif
			CurrRegion->SetStart(BADADDR); // mark for removal
		}
		else if (AllConverted && AllNops) {
#if SMP_DEBUG_FIXUP_IDB
			msg("FixupNewCodeChunks re-converting nops region from %x to %x\n",
				CurrRegion->GetStart(), CurrRegion->GetEnd());
#endif
			do_unknown_range(CurrRegion->GetStart(),
				CurrRegion->GetEnd() - CurrRegion->GetStart(), DOUNK_SIMPLE);
			CurrRegion->SetStart(BADADDR); // mark for removal
		}
		else {
			// Remove only the instructions that were fixed up.
			CurrInstr = CurrRegion->FixupInstrs.begin(); 
			while (CurrInstr != CurrRegion->FixupInstrs.end()) {
				if (BADADDR == *CurrInstr) {
					CurrInstr = CurrRegion->FixupInstrs.erase(CurrInstr);
				}
				else {
					++CurrInstr;
				}
			}
		}
	} // end for all regions in the CodeReanalyzeList

	// Remove completed regions from the CodeReanalyzeList
	CurrRegion = CodeReanalyzeList.begin();
	while (CurrRegion != CodeReanalyzeList.end()) {
		if (BADADDR == CurrRegion->GetStart())
			CurrRegion = CodeReanalyzeList.erase(CurrRegion);
		else
			++CurrRegion;
	}

#if 0
	if (AllConverted) {
					if (IsFunctionPrologue(AreaStart, AreaEnd)) {
						// Create a new function entry chunk here.
						//  **!!** TODO
						;
					}
					else {
						// Extend the previous chunk to include the
						//  converted code.
						ea_t PrevIDAAddr = IDAProLocs[IDAProIndex - 2];
						func_t *PrevChunk = get_fchunk(PrevIDAAddr);
#if SMP_DEBUG_FIXUP_IDB
						msg(" addr in chunk to extend: %x\n", PrevIDAAddr);
						msg(" func_t pointer for chunk: %x\n", PrevChunk);
#endif
#if 0  // temporary for debugging
						if (is_func_entry(PrevChunk)) {
							// Extend the func entry to contain the new code.
							if (func_setend(PrevIDAAddr, AreaEnd)) {
								msg("Func extended to include code from %x to %x\n",
									AreaStart, AreaEnd);
								FuncReanalyzeList.push_back(PrevIDAAddr);
							}
							else {
								msg("Failed to extend func from %x to %x\n",
									AreaStart, AreaEnd);
							}
						}
						else { // tail
							// See if this works for function tails, also.
							// Extend the func entry to contain the new code.
							if (func_setend(PrevIDAAddr, AreaEnd)) {
								msg("Tail extended to include code from %x to %x\n",
									AreaStart, AreaEnd);
								func_t *TailOwner = get_func(PrevChunk->owner);
								FuncReanalyzeList.push_back(PrevIDAAddr);
							}
							else {
								msg("Failed to extend tail from %x to %x\n",
									AreaStart, AreaEnd);
							}
						} // end if (is_func_entry()) ... else ...
#endif
					} // end if (IsFunctionPrologue()) ... else ...
				} // end if (AllConverted)
				else {
					msg("not AllConverted; cannot include new code in previous chunk.\n");
				}
#endif

	return changes;
} // end of FixupNewCodeChunnks()

// Audit the IDA code database by looking at all instructions in the
//  code segment and printing all those that are not contained in a
//  function. Emit the context-free annotations that we are able to
//  emit on a per-instruction basis.
void FindOrphanedCode(segment_t *CurrSeg, FILE *AnnotFile) {
	char disasm[MAXSTR];
	for (ea_t addr = CurrSeg->startEA; addr < CurrSeg->endEA;
		addr = get_item_end(addr)) {
		flags_t InstrFlags = getFlags(addr);
		if (isHead(InstrFlags) && isCode(InstrFlags)) {
			func_t *CurrFunc = get_func(addr);
			if (NULL == CurrFunc) {
				SMPInstr CurrInst(addr);
				CurrInst.Analyze();
clc5q's avatar
clc5q committed
#if SMP_DEBUG_FIXUP_IDB
				msg("Orphan code at %x : %s\n", addr, CurrInst.GetDisasm());
clc5q's avatar
clc5q committed
#endif
				// TODO: If there are code xrefs to the orphan code,
				//  see what kind. If a CALL, and orphan code looks
				//  like a prologue, make a function. If a JUMP of
				//  some kind, then make a function chunk and make
				//  it a tail of all functions that jump to it. **!!**

				// If instruction is still not included in a code chunk,
				//  emit annotations for it in isolation.
				CurrInst.EmitAnnotations(true, false, true, AnnotFile);
			}
		}
		else if (isUnknown(InstrFlags)) {
clc5q's avatar
clc5q committed
#if SMP_DEBUG_FIXUP_IDB
			msg("Unanalyzed byte at %x\n", addr);
clc5q's avatar
clc5q committed
#endif
			// Can IDA analyze this to be code?
			int InstrLen;
#if IDA_SDK_VERSION < 600
			InstrLen = ua_code(addr);
#else
			InstrLen = create_insn(addr);
#endif
			if (InstrLen > 0) {
				bool IDAsuccess = generate_disasm_line(addr, disasm, sizeof(disasm) - 1);
				if (IDAsuccess) {
					// Remove interactive color-coding tags.
					ssize_t StringLen = tag_remove(disasm, disasm, 0);
					if (-1 >= StringLen) {
						msg("ERROR: tag_remove failed at addr %x \n", addr);
					}
					else {
clc5q's avatar
clc5q committed
#if SMP_DEBUG_FIXUP_IDB
						msg("Successfully analyzed!  %s\n", disasm);
clc5q's avatar
clc5q committed
#endif
						SMPInstr UnknownInstr(addr);
						UnknownInstr.Analyze();
						// TODO: Get new code into a chunk.  **!!**
						// If instruction is still not included in a code chunk,
						//  emit annotations for it in isolation.
						UnknownInstr.EmitAnnotations(true, false, true, AnnotFile);
					}
				}
				else {
					msg("ERROR: generate_disasm_line failed at addr %x \n", addr);
				}
			}
		}
	} // end for (ea_t addr = CurrSeg->startEA; ...)
} // end of FindOrphanedCode()

// Version of FindOrphanedCode that does not emit annotations but can be used
//  to determine at what point in time code becomes orphaned.
void Debug_FindOrphanedCode(segment_t *CurrSeg, bool FirstRun) {
	char disasm[MAXSTR];
	ea_t DebugAddr = 0x8050db0;
	for (ea_t addr = CurrSeg->startEA; addr < CurrSeg->endEA;
		addr = get_item_end(addr)) {

		flags_t InstrFlags = getFlags(addr);
		if (isHead(InstrFlags) && isCode(InstrFlags)) {
			func_t *CurrFunc = get_func(addr);
			if (NULL == CurrFunc) {  // Code not in a func; orphaned
				pair<set<ea_t>::iterator, bool> pairib;
				pairib = CodeOrphans.insert(addr);
				if (DebugAddr == addr) {
					msg("DEBUG: Orphaned code addr %x found.\n", addr);
				}
				if ((!FirstRun) && (pairib.second)) {
					msg("SERIOUS WARNING: Newly orphaned code at %x \n", addr);
				}
			}
		}
	} // end for (ea_t addr = CurrSeg->startEA; ...)
} // end of Debug_FindOrphanedCode()

// Audit the IDA database with respect to branches and calls. They should
//  each have valid code targets (not data or unknown bytes) and the code
//  cross references should reflect the linkage.
void AuditCodeTargets(void) {
	// Cover all the code that IDA has grouped into functions by iterating
	//  through all function chunks in the program.
	size_t NumChunks = get_fchunk_qty();
	for (size_t ChunkIndex = 0; ChunkIndex < NumChunks; ++ChunkIndex) {
		func_t *ChunkInfo = getn_fchunk((int) ChunkIndex);
		char FuncName[MAXSTR];
		get_func_name(ChunkInfo->startEA, FuncName, sizeof(FuncName) - 1);

		// First, see if any calls to this function (if this chunk is
		//  an entry point) are not coming from within functions.
		if (is_func_entry(ChunkInfo)) {
			xrefblk_t xb;
			ea_t addr = ChunkInfo->startEA;
			for (bool ok = xb.first_to(addr, XREF_ALL);	ok; ok = xb.next_to()) {
				uchar XrefType = xb.type & XREF_MASK;
				if (xb.iscode) {
					if ((XrefType == fl_U) || (XrefType == fl_USobsolete)) {
						msg("Bad xref type: %x %s\n", addr, FuncName);
					}
					else if ((XrefType == fl_JF) || (XrefType == fl_JN)) {
						msg("Jump to func: %x %s from: %x\n",
							addr, FuncName, xb.from);
					}
					else if (XrefType == fl_F) {
						msg("Fall through to func: %x %s from: %x\n",
							addr, FuncName, xb.from);
					}
					else if ((XrefType == fl_CF) || (XrefType == fl_CN)) {
						// Far call or Near call
						func_t *CallingFunc = get_func(xb.from);
						if (NULL == CallingFunc) {
clc5q's avatar
clc5q committed
							;
#if SMP_DEBUG_FIXUP_IDB
							msg("Call to %x Func %s from %x not in function.\n",
								addr, FuncName, xb.from);
clc5q's avatar
clc5q committed
#endif
						}
					}
				} // end if (xb.iscode)
				else { // DATA xref
					if (XrefType == dr_O) {
clc5q's avatar
clc5q committed
#if SMP_DEBUG_FIXUP_IDB
						msg("Data xref to %x Func %s from %x\n",
							addr, FuncName, xb.from);
clc5q's avatar
clc5q committed
#endif
					}
					else {
						msg("Strange data xref %d to %x Func %s from %x\n",
							XrefType, addr, FuncName, xb.from);
					}
				}
			} // end for (bool ok = xb.first_to(); ...)
		} // end if (is_func_entry(ChunkInfo))

		// Next, see if any call or branch in this chunk references
		//  a target address that is not in a function. If so, and the
		//  callee address code looks like a function prologue, then
		//  create a function for the contiguous code starting at that
		//  address and ask IDA to analyze it and store it in the
		//  IDA database. If it is a branch target, not a call target,
		//  create a new TAIL chunk for the current parent functions.
		for (ea_t addr = ChunkInfo->startEA; addr < ChunkInfo->endEA;
			addr = get_item_end(addr)) {
			flags_t InstrFlags = getFlags(addr);
			if (isCode(InstrFlags) && isHead(InstrFlags)) {
				SMPInstr CurrInst(addr);
				CurrInst.Analyze();
				if ((CALL|JUMP|COND_BRANCH) & CurrInst.GetDataFlowType()) {
					xrefblk_t xb;
					for (bool ok = xb.first_from(addr, XREF_FAR); ok; ok = xb.next_from()) {
						if (xb.iscode) {
							func_t *FuncInfo = get_func(xb.to);
							if (NULL == FuncInfo) {
								// Found call to addr that is not in a func.
								ea_t FirstAddr = xb.to;
								// Find limits of contiguous code starting at FirstAddr.
								ea_t LastAddr = FindNewFuncLimit(xb.to);
								if (CALL == CurrInst.GetDataFlowType())
									msg("Found new func from %x to %x\n",
										FirstAddr, LastAddr);
								else
									msg("Found new chunk from %x to %x\n",
										FirstAddr, LastAddr);
							}
						}
					}
				}
			}
		}
	} // end for (size_t ChunkIndex = 0; ... )

	return;
} // end of AuditCodeTargets()

// Find the span of contiguous code that is not contained within any
//  function, starting at StartAddr, which should already be an example
//  of an instruction address that is outside of a function.
ea_t FindNewFuncLimit(ea_t StartAddr) {
	ea_t LimitAddr = StartAddr;
	segment_t *seg = getseg(StartAddr);
	if (NULL == seg)
		return LimitAddr;
	ea_t SegLimit = seg->endEA;

	for (ea_t addr = get_item_end(StartAddr); addr < SegLimit; addr = get_item_end(addr)) {
		flags_t InstrFlags = getFlags(addr);
		if (isCode(InstrFlags) && isHead(InstrFlags)) {
			LimitAddr = addr;
			func_t *FuncInfo = get_func(addr);
			if (NULL != FuncInfo)
				break; // ran into an existing function
		}
		else // Not a code head; time to stop.
			break;
	}
	return LimitAddr;
} // end of FindNewFuncLimit()

void SpecialDebugOutput(void) {
	char disasm[MAXSTR];
	vector<ea_t> ProblemAddrs;
	ProblemAddrs.push_back(0x8066d08);
	bool IDAsuccess;
	int InstLen;
	ssize_t StringLen;

	for (size_t index = 0; index < ProblemAddrs.size(); ++index) {
		ea_t addr = ProblemAddrs[index];
		flags_t InstrFlags = getFlags(addr);
		if (isCode(InstrFlags) && isHead(InstrFlags)) {
#if IDA_SDK_VERSION < 600
			InstLen = ua_ana0(addr);
#else
			InstLen = decode_insn(addr);
			if (0 < InstLen) {
				IDAsuccess = generate_disasm_line(addr, disasm, sizeof(disasm) - 1);
				if (IDAsuccess) {
					StringLen = tag_remove(disasm, disasm, 0);
					if (-1 < StringLen)
						msg("Problem addr %x : %s\n", addr, disasm);
					else
						msg("ERROR: tag_remove failed at addr %x \n", addr);
				}
				else {
					msg("ERROR: generate_disasm_line failed at addr %x \n", addr);
				}
			}
			else {
				msg("ERROR: decode_insn failed at addr %x \n", addr);
			}
		}
	}
	return;
} // end of SpecialDebugOutput()



// Initialize the OptCategory[] array to define how we emit
//   optimizing annotations.
void InitOptCategory(void) {
	// Default category is 0, no optimization without knowing context.
	(void) memset(OptCategory, 0, sizeof(OptCategory));
	// Category 1 instructions never need updating of their memory
	//  metadata by the Memory Monitor SDT. Currently, this is because
	//  these instructions only have effects on registers we do not maintain
	//  metadata for, such as the EIP and the FLAGS, e.g. jumps, compares,
	//  or because they are no-ops, including machine-dependent no-op idioms.
	// Category 2 instructions always have a result type of 'n' (number).
	// Category 3 instructions have a result type of 'n' (number)
	//  whenever the second source operand is an operand of type 'n'.
	//  NOTE: MOV is only current example, and this will take some thought if 
	// Category 4 instructions have a result type identical to the 1st source operand type.
	//  NOTE: This is currently set for single-operand instructions such as
	//   INC, DEC. As a result, these are treated pretty much as if
	//   they were category 1 instructions, as there is no metadata update,
	//   unless the operand is a memory operand (i.e. mem or [reg]).
	//   If new instructions are added to this category that are not single
	//   operand and do require some updating, the category should be split.
	// Category 5 instructions have a result type identical to the 1st source operand
	//  type whenever the 2nd source operand is an operand of type 'n'.
	//  If the destination is memory, metadata still needs to be checked; if
	//  not, no metadata check is needed, so it becomes category 1.
	// Category 6 instructions always have a result type of 'p' (pointer).
	// Category 7 instructions are category 2 instructions with two destinations,
	//  such as multiply and divide instructions that affect EDX:EAX. There are
	//  forms of these instructions that only have one destination, so they have
	//  to be distinguished via the operand info.
	// Category 8 instructions implicitly write a numeric value to EDX:EAX, but
	//  EDX and EAX are not listed as operands. RDTSC, RDPMC, RDMSR, and other
	//  instructions that copy machine registers into EDX:EAX are category 8.
	// Category 9 instructions are floating point instructions that either
	//  have a memory destination (treat as category 0) or a FP reg destination
	//  (treat as category 1).
	// Category 10 instructions are the same as category 8, but also write
	//  to register ECX in addition to EDX:EAX.

	// NOTE: The Memory Monitor SDT needs just three categories, corresponding
	//  to categories 0, 1, and all others. For all categories > 1, the
	//  annotation should tell the SDT exactly how to update its metadata.
	//  For example, a division instruction will write type 'n' (NUM) as
	//  the metadata for result registers EDX:EAX. So, the annotation should
	//  list 'n', EDX, EAX, and a terminator of ZZ. CWD (convert word to
	//  doubleword) should have a list of 'n', EAX, ZZ.

OptCategory[NN_null] = 0;            // Unknown Operation
OptCategory[NN_aaa] = 2;                 // ASCII Adjust after Addition
OptCategory[NN_aad] = 2;                 // ASCII Adjust AX before Division
OptCategory[NN_aam] = 2;                 // ASCII Adjust AX after Multiply
OptCategory[NN_aas] = 2;                 // ASCII Adjust AL after Subtraction
OptCategory[NN_adc] = 5;                 // Add with Carry
OptCategory[NN_add] = 5;                 // Add
OptCategory[NN_and] = 0;                 // Logical AND
OptCategory[NN_arpl] = 1;                // Adjust RPL Field of Selector
OptCategory[NN_bound] = 1;               // Check Array Index Against Bounds
OptCategory[NN_bsf] = 2;                 // Bit Scan Forward
OptCategory[NN_bsr] = 2;                 // Bit Scan Reverse
OptCategory[NN_bt] = 2;                  // Bit Test
OptCategory[NN_btc] = 2;                 // Bit Test and Complement
OptCategory[NN_btr] = 2;                 // Bit Test and Reset
OptCategory[NN_bts] = 2;                 // Bit Test and Set
OptCategory[NN_call] = 1;                // Call Procedure
OptCategory[NN_callfi] = 1;              // Indirect Call Far Procedure
OptCategory[NN_callni] = 1;              // Indirect Call Near Procedure
OptCategory[NN_cbw] = 2;                 // AL -> AX (with sign)            ** No ops?
OptCategory[NN_cwde] = 2;                // AX -> EAX (with sign)           **
OptCategory[NN_cdqe] = 2;                // EAX -> RAX (with sign)          **
OptCategory[NN_clc] = 1;                 // Clear Carry Flag
OptCategory[NN_cld] = 1;                 // Clear Direction Flag
OptCategory[NN_cli] = 1;                 // Clear Interrupt Flag
OptCategory[NN_clts] = 1;                // Clear Task-Switched Flag in CR0
OptCategory[NN_cmc] = 1;                 // Complement Carry Flag
OptCategory[NN_cmp] = 1;                 // Compare Two Operands
OptCategory[NN_cmps] = 1;                // Compare Strings
OptCategory[NN_cwd] = 2;                 // AX -> DX:AX (with sign)
OptCategory[NN_cdq] = 2;                 // EAX -> EDX:EAX (with sign)
OptCategory[NN_cqo] = 2;                 // RAX -> RDX:RAX (with sign)
OptCategory[NN_daa] = 2;                 // Decimal Adjust AL after Addition
OptCategory[NN_das] = 2;                 // Decimal Adjust AL after Subtraction
OptCategory[NN_dec] = 4;                 // Decrement by 1
OptCategory[NN_div] = 7;                 // Unsigned Divide
OptCategory[NN_enterw] = 0;              // Make Stack Frame for Procedure Parameters  **
OptCategory[NN_enter] = 0;               // Make Stack Frame for Procedure Parameters  **
OptCategory[NN_enterd] = 0;              // Make Stack Frame for Procedure Parameters  **
OptCategory[NN_enterq] = 0;              // Make Stack Frame for Procedure Parameters  **
OptCategory[NN_hlt] = 0;                 // Halt
OptCategory[NN_idiv] = 7;                // Signed Divide
OptCategory[NN_imul] = 7;                // Signed Multiply
OptCategory[NN_in] = 0;                  // Input from Port                         **
OptCategory[NN_inc] = 4;                 // Increment by 1
OptCategory[NN_ins] = 2;                 // Input Byte(s) from Port to String       **
OptCategory[NN_int] = 0;                 // Call to Interrupt Procedure
OptCategory[NN_into] = 0;                // Call to Interrupt Procedure if Overflow Flag = 1
OptCategory[NN_int3] = 0;                // Trap to Debugger
OptCategory[NN_iretw] = 0;               // Interrupt Return
OptCategory[NN_iret] = 0;                // Interrupt Return
OptCategory[NN_iretd] = 0;               // Interrupt Return (use32)
OptCategory[NN_iretq] = 0;               // Interrupt Return (use64)
OptCategory[NN_ja] = 1;                  // Jump if Above (CF=0 & ZF=0)
OptCategory[NN_jae] = 1;                 // Jump if Above or Equal (CF=0)
OptCategory[NN_jb] = 1;                  // Jump if Below (CF=1)
OptCategory[NN_jbe] = 1;                 // Jump if Below or Equal (CF=1 | ZF=1)
OptCategory[NN_jc] = 1;                  // Jump if Carry (CF=1)
OptCategory[NN_jcxz] = 1;                // Jump if CX is 0
OptCategory[NN_jecxz] = 1;               // Jump if ECX is 0
OptCategory[NN_jrcxz] = 1;               // Jump if RCX is 0
OptCategory[NN_je] = 1;                  // Jump if Equal (ZF=1)
OptCategory[NN_jg] = 1;                  // Jump if Greater (ZF=0 & SF=OF)
OptCategory[NN_jge] = 1;                 // Jump if Greater or Equal (SF=OF)
OptCategory[NN_jl] = 1;                  // Jump if Less (SF!=OF)
OptCategory[NN_jle] = 1;                 // Jump if Less or Equal (ZF=1 | SF!=OF)
OptCategory[NN_jna] = 1;                 // Jump if Not Above (CF=1 | ZF=1)
OptCategory[NN_jnae] = 1;                // Jump if Not Above or Equal (CF=1)
OptCategory[NN_jnb] = 1;                 // Jump if Not Below (CF=0)
OptCategory[NN_jnbe] = 1;                // Jump if Not Below or Equal (CF=0 & ZF=0)
OptCategory[NN_jnc] = 1;                 // Jump if Not Carry (CF=0)
OptCategory[NN_jne] = 1;                 // Jump if Not Equal (ZF=0)
OptCategory[NN_jng] = 1;                 // Jump if Not Greater (ZF=1 | SF!=OF)
OptCategory[NN_jnge] = 1;                // Jump if Not Greater or Equal (ZF=1)
OptCategory[NN_jnl] = 1;                 // Jump if Not Less (SF=OF)
OptCategory[NN_jnle] = 1;                // Jump if Not Less or Equal (ZF=0 & SF=OF)
OptCategory[NN_jno] = 1;                 // Jump if Not Overflow (OF=0)
OptCategory[NN_jnp] = 1;                 // Jump if Not Parity (PF=0)
OptCategory[NN_jns] = 1;                 // Jump if Not Sign (SF=0)
OptCategory[NN_jnz] = 1;                 // Jump if Not Zero (ZF=0)
OptCategory[NN_jo] = 1;                  // Jump if Overflow (OF=1)
OptCategory[NN_jp] = 1;                  // Jump if Parity (PF=1)
OptCategory[NN_jpe] = 1;                 // Jump if Parity Even (PF=1)
OptCategory[NN_jpo] = 1;                 // Jump if Parity Odd  (PF=0)
OptCategory[NN_js] = 1;                  // Jump if Sign (SF=1)
OptCategory[NN_jz] = 1;                  // Jump if Zero (ZF=1)
OptCategory[NN_jmp] = 1;                 // Jump
OptCategory[NN_jmpfi] = 1;               // Indirect Far Jump
OptCategory[NN_jmpni] = 1;               // Indirect Near Jump
OptCategory[NN_jmpshort] = 1;            // Jump Short (not used)
OptCategory[NN_lahf] = 2;                // Load Flags into AH Register
OptCategory[NN_lar] = 2;                 // Load Access Rights Byte
OptCategory[NN_lea] = 0;                 // Load Effective Address           **
OptCategory[NN_leavew] = 0;              // High Level Procedure Exit        **
OptCategory[NN_leave] = 0;               // High Level Procedure Exit        **
OptCategory[NN_leaved] = 0;              // High Level Procedure Exit        **
OptCategory[NN_leaveq] = 0;              // High Level Procedure Exit        **
OptCategory[NN_lgdt] = 0;                // Load Global Descriptor Table Register
OptCategory[NN_lidt] = 0;                // Load Interrupt Descriptor Table Register
OptCategory[NN_lgs] = 6;                 // Load Full Pointer to GS:xx
OptCategory[NN_lss] = 6;                 // Load Full Pointer to SS:xx
OptCategory[NN_lds] = 6;                 // Load Full Pointer to DS:xx
OptCategory[NN_les] = 6;                 // Load Full Pointer to ES:xx
OptCategory[NN_lfs] = 6;                 // Load Full Pointer to FS:xx
OptCategory[NN_lldt] = 0;                // Load Local Descriptor Table Register
OptCategory[NN_lmsw] = 1;                // Load Machine Status Word
OptCategory[NN_lock] = 1;                // Assert LOCK# Signal Prefix
OptCategory[NN_lods] = 0;                // Load String
OptCategory[NN_loopw] = 1;               // Loop while ECX != 0
OptCategory[NN_loop] = 1;                // Loop while CX != 0
OptCategory[NN_loopd] = 1;               // Loop while ECX != 0
OptCategory[NN_loopq] = 1;               // Loop while RCX != 0
OptCategory[NN_loopwe] = 1;              // Loop while CX != 0 and ZF=1
OptCategory[NN_loope] = 1;               // Loop while rCX != 0 and ZF=1
OptCategory[NN_loopde] = 1;              // Loop while ECX != 0 and ZF=1
OptCategory[NN_loopqe] = 1;              // Loop while RCX != 0 and ZF=1
OptCategory[NN_loopwne] = 1;             // Loop while CX != 0 and ZF=0
OptCategory[NN_loopne] = 1;              // Loop while rCX != 0 and ZF=0
OptCategory[NN_loopdne] = 1;             // Loop while ECX != 0 and ZF=0
OptCategory[NN_loopqne] = 1;             // Loop while RCX != 0 and ZF=0
OptCategory[NN_lsl] = 6;                 // Load Segment Limit
OptCategory[NN_ltr] = 1;                 // Load Task Register
OptCategory[NN_mov] = 3;                 // Move Data
OptCategory[NN_movsp] = 3;               // Move to/from Special Registers
OptCategory[NN_movs] = 0;                // Move Byte(s) from String to String
OptCategory[NN_movsx] = 3;               // Move with Sign-Extend
OptCategory[NN_movzx] = 3;               // Move with Zero-Extend
OptCategory[NN_mul] = 7;                 // Unsigned Multiplication of AL or AX
OptCategory[NN_neg] = 2;                 // Two's Complement Negation
OptCategory[NN_nop] = 1;                 // No Operation
OptCategory[NN_not] = 2;                 // One's Complement Negation
OptCategory[NN_or] = 0;                  // Logical Inclusive OR
OptCategory[NN_out] = 0;                 // Output to Port
OptCategory[NN_outs] = 0;                // Output Byte(s) to Port
OptCategory[NN_pop] = 0;                 // Pop a word from the Stack
OptCategory[NN_popaw] = 0;               // Pop all General Registers
OptCategory[NN_popa] = 0;                // Pop all General Registers
OptCategory[NN_popad] = 0;               // Pop all General Registers (use32)
OptCategory[NN_popaq] = 0;               // Pop all General Registers (use64)
OptCategory[NN_popfw] = 1;               // Pop Stack into Flags Register         **
OptCategory[NN_popf] = 1;                // Pop Stack into Flags Register         **
OptCategory[NN_popfd] = 1;               // Pop Stack into Eflags Register        **
OptCategory[NN_popfq] = 1;               // Pop Stack into Rflags Register        **
OptCategory[NN_push] = 0;                // Push Operand onto the Stack
OptCategory[NN_pushaw] = 0;              // Push all General Registers
OptCategory[NN_pusha] = 0;               // Push all General Registers
OptCategory[NN_pushad] = 0;              // Push all General Registers (use32)
OptCategory[NN_pushaq] = 0;              // Push all General Registers (use64)
OptCategory[NN_pushfw] = 0;              // Push Flags Register onto the Stack
OptCategory[NN_pushf] = 0;               // Push Flags Register onto the Stack
OptCategory[NN_pushfd] = 0;              // Push Flags Register onto the Stack (use32)
OptCategory[NN_pushfq] = 0;              // Push Flags Register onto the Stack (use64)
OptCategory[NN_rcl] = 2;                 // Rotate Through Carry Left
OptCategory[NN_rcr] = 2;                 // Rotate Through Carry Right
OptCategory[NN_rol] = 2;                 // Rotate Left
OptCategory[NN_ror] = 2;                 // Rotate Right
OptCategory[NN_rep] = 0;                 // Repeat String Operation
OptCategory[NN_repe] = 0;                // Repeat String Operation while ZF=1
OptCategory[NN_repne] = 0;               // Repeat String Operation while ZF=0
OptCategory[NN_retn] = 0;                // Return Near from Procedure
OptCategory[NN_retf] = 0;                // Return Far from Procedure
OptCategory[NN_sahf] = 1;                // Store AH into Flags Register
OptCategory[NN_sal] = 2;                 // Shift Arithmetic Left
OptCategory[NN_sar] = 2;                 // Shift Arithmetic Right
OptCategory[NN_shl] = 2;                 // Shift Logical Left
OptCategory[NN_shr] = 2;                 // Shift Logical Right
OptCategory[NN_sbb] = 5;                 // Integer Subtraction with Borrow
OptCategory[NN_scas] = 1;                // Compare String
OptCategory[NN_seta] = 2;                // Set Byte if Above (CF=0 & ZF=0)
OptCategory[NN_setae] = 2;               // Set Byte if Above or Equal (CF=0)
OptCategory[NN_setb] = 2;                // Set Byte if Below (CF=1)
OptCategory[NN_setbe] = 2;               // Set Byte if Below or Equal (CF=1 | ZF=1)
OptCategory[NN_setc] = 2;                // Set Byte if Carry (CF=1)
OptCategory[NN_sete] = 2;                // Set Byte if Equal (ZF=1)
OptCategory[NN_setg] = 2;                // Set Byte if Greater (ZF=0 & SF=OF)
OptCategory[NN_setge] = 2;               // Set Byte if Greater or Equal (SF=OF)
OptCategory[NN_setl] = 2;                // Set Byte if Less (SF!=OF)
OptCategory[NN_setle] = 2;               // Set Byte if Less or Equal (ZF=1 | SF!=OF)
OptCategory[NN_setna] = 2;               // Set Byte if Not Above (CF=1 | ZF=1)
OptCategory[NN_setnae] = 2;              // Set Byte if Not Above or Equal (CF=1)
OptCategory[NN_setnb] = 2;               // Set Byte if Not Below (CF=0)
OptCategory[NN_setnbe] = 2;              // Set Byte if Not Below or Equal (CF=0 & ZF=0)
OptCategory[NN_setnc] = 2;               // Set Byte if Not Carry (CF=0)
OptCategory[NN_setne] = 2;               // Set Byte if Not Equal (ZF=0)
OptCategory[NN_setng] = 2;               // Set Byte if Not Greater (ZF=1 | SF!=OF)
OptCategory[NN_setnge] = 2;              // Set Byte if Not Greater or Equal (ZF=1)
OptCategory[NN_setnl] = 2;               // Set Byte if Not Less (SF=OF)
OptCategory[NN_setnle] = 2;              // Set Byte if Not Less or Equal (ZF=0 & SF=OF)
OptCategory[NN_setno] = 2;               // Set Byte if Not Overflow (OF=0)
OptCategory[NN_setnp] = 2;               // Set Byte if Not Parity (PF=0)
OptCategory[NN_setns] = 2;               // Set Byte if Not Sign (SF=0)
OptCategory[NN_setnz] = 2;               // Set Byte if Not Zero (ZF=0)
OptCategory[NN_seto] = 2;                // Set Byte if Overflow (OF=1)
OptCategory[NN_setp] = 2;                // Set Byte if Parity (PF=1)
OptCategory[NN_setpe] = 2;               // Set Byte if Parity Even (PF=1)
OptCategory[NN_setpo] = 2;               // Set Byte if Parity Odd  (PF=0)
OptCategory[NN_sets] = 2;                // Set Byte if Sign (SF=1)
OptCategory[NN_setz] = 2;                // Set Byte if Zero (ZF=1)
OptCategory[NN_sgdt] = 0;                // Store Global Descriptor Table Register
OptCategory[NN_sidt] = 0;                // Store Interrupt Descriptor Table Register
OptCategory[NN_shld] = 2;                // Double Precision Shift Left
OptCategory[NN_shrd] = 2;                // Double Precision Shift Right
OptCategory[NN_sldt] = 6;                // Store Local Descriptor Table Register
OptCategory[NN_smsw] = 2;                // Store Machine Status Word
OptCategory[NN_stc] = 1;                 // Set Carry Flag
OptCategory[NN_std] = 1;                 // Set Direction Flag
OptCategory[NN_sti] = 1;                 // Set Interrupt Flag
OptCategory[NN_stos] = 0;                // Store String
OptCategory[NN_str] = 6;                 // Store Task Register
OptCategory[NN_sub] = 5;                 // Integer Subtraction
OptCategory[NN_test] = 1;                // Logical Compare
OptCategory[NN_verr] = 1;                // Verify a Segment for Reading
OptCategory[NN_verw] = 1;                // Verify a Segment for Writing
OptCategory[NN_wait] = 1;                // Wait until BUSY# Pin is Inactive (HIGH)
OptCategory[NN_xchg] = 0;                // Exchange Register/Memory with Register
OptCategory[NN_xlat] = 0;                // Table Lookup Translation
OptCategory[NN_xor] = 2;                 // Logical Exclusive OR

//
//      486 instructions
//

OptCategory[NN_cmpxchg] = 0;             // Compare and Exchange
OptCategory[NN_bswap] = 2;               // Swap bytes in register
OptCategory[NN_xadd] = 0;                // t<-dest; dest<-src+dest; src<-t
OptCategory[NN_invd] = 1;                // Invalidate Data Cache
OptCategory[NN_wbinvd] = 1;              // Invalidate Data Cache (write changes)
OptCategory[NN_invlpg] = 1;              // Invalidate TLB entry

//
//      Pentium instructions
//

OptCategory[NN_rdmsr] = 8;               // Read Machine Status Register
OptCategory[NN_wrmsr] = 1;               // Write Machine Status Register
OptCategory[NN_cpuid] = 8;               // Get CPU ID
OptCategory[NN_cmpxchg8b] = 0;           // Compare and Exchange Eight Bytes
OptCategory[NN_rdtsc] = 8;               // Read Time Stamp Counter
OptCategory[NN_rsm] = 1;                 // Resume from System Management Mode

//
//      Pentium Pro instructions
//

OptCategory[NN_cmova] = 0;               // Move if Above (CF=0 & ZF=0)
OptCategory[NN_cmovb] = 0;               // Move if Below (CF=1)
OptCategory[NN_cmovbe] = 0;              // Move if Below or Equal (CF=1 | ZF=1)
OptCategory[NN_cmovg] = 0;               // Move if Greater (ZF=0 & SF=OF)
OptCategory[NN_cmovge] = 0;              // Move if Greater or Equal (SF=OF)
OptCategory[NN_cmovl] = 0;               // Move if Less (SF!=OF)
OptCategory[NN_cmovle] = 0;              // Move if Less or Equal (ZF=1 | SF!=OF)
OptCategory[NN_cmovnb] = 0;              // Move if Not Below (CF=0)
OptCategory[NN_cmovno] = 0;              // Move if Not Overflow (OF=0)
OptCategory[NN_cmovnp] = 0;              // Move if Not Parity (PF=0)
OptCategory[NN_cmovns] = 0;              // Move if Not Sign (SF=0)
OptCategory[NN_cmovnz] = 0;              // Move if Not Zero (ZF=0)
OptCategory[NN_cmovo] = 0;               // Move if Overflow (OF=1)
OptCategory[NN_cmovp] = 0;               // Move if Parity (PF=1)
OptCategory[NN_cmovs] = 0;               // Move if Sign (SF=1)
OptCategory[NN_cmovz] = 0;               // Move if Zero (ZF=1)
OptCategory[NN_fcmovb] = 1;              // Floating Move if Below          
OptCategory[NN_fcmove] = 1;              // Floating Move if Equal          
OptCategory[NN_fcmovbe] = 1;             // Floating Move if Below or Equal 
OptCategory[NN_fcmovu] = 1;              // Floating Move if Unordered      
OptCategory[NN_fcmovnb] = 1;             // Floating Move if Not Below      
OptCategory[NN_fcmovne] = 1;             // Floating Move if Not Equal      
OptCategory[NN_fcmovnbe] = 1;            // Floating Move if Not Below or Equal
OptCategory[NN_fcmovnu] = 1;             // Floating Move if Not Unordered     
OptCategory[NN_fcomi] = 1;               // FP Compare, result in EFLAGS
OptCategory[NN_fucomi] = 1;              // FP Unordered Compare, result in EFLAGS
OptCategory[NN_fcomip] = 1;              // FP Compare, result in EFLAGS, pop stack
OptCategory[NN_fucomip] = 1;             // FP Unordered Compare, result in EFLAGS, pop stack
OptCategory[NN_rdpmc] = 8;               // Read Performance Monitor Counter

//
//      FPP instructuions
//

OptCategory[NN_fld] = 1;                 // Load Real             ** Infer src is 'n'
OptCategory[NN_fst] = 9;                 // Store Real            
OptCategory[NN_fstp] = 9;                // Store Real and Pop   
OptCategory[NN_fxch] = 1;                // Exchange Registers
OptCategory[NN_fild] = 1;                // Load Integer          ** Infer src is 'n'
OptCategory[NN_fist] = 0;                // Store Integer
OptCategory[NN_fistp] = 0;               // Store Integer and Pop
OptCategory[NN_fbld] = 1;                // Load BCD
OptCategory[NN_fbstp] = 0;               // Store BCD and Pop
OptCategory[NN_fadd] = 1;                // Add Real
OptCategory[NN_faddp] = 1;               // Add Real and Pop
OptCategory[NN_fiadd] = 1;               // Add Integer
OptCategory[NN_fsub] = 1;                // Subtract Real
OptCategory[NN_fsubp] = 1;               // Subtract Real and Pop
OptCategory[NN_fisub] = 1;               // Subtract Integer
OptCategory[NN_fsubr] = 1;               // Subtract Real Reversed
OptCategory[NN_fsubrp] = 1;              // Subtract Real Reversed and Pop
OptCategory[NN_fisubr] = 1;              // Subtract Integer Reversed
OptCategory[NN_fmul] = 1;                // Multiply Real
OptCategory[NN_fmulp] = 1;               // Multiply Real and Pop
OptCategory[NN_fimul] = 1;               // Multiply Integer
OptCategory[NN_fdiv] = 1;                // Divide Real
OptCategory[NN_fdivp] = 1;               // Divide Real and Pop
OptCategory[NN_fidiv] = 1;               // Divide Integer
OptCategory[NN_fdivr] = 1;               // Divide Real Reversed
OptCategory[NN_fdivrp] = 1;              // Divide Real Reversed and Pop
OptCategory[NN_fidivr] = 1;              // Divide Integer Reversed
OptCategory[NN_fsqrt] = 1;               // Square Root
OptCategory[NN_fscale] = 1;              // Scale:  st(0) <- st(0) * 2^st(1)
OptCategory[NN_fprem] = 1;               // Partial Remainder
OptCategory[NN_frndint] = 1;             // Round to Integer
OptCategory[NN_fxtract] = 1;             // Extract exponent and significand
OptCategory[NN_fabs] = 1;                // Absolute value
OptCategory[NN_fchs] = 1;                // Change Sign
OptCategory[NN_fcom] = 1;                // Compare Real
OptCategory[NN_fcomp] = 1;               // Compare Real and Pop
OptCategory[NN_fcompp] = 1;              // Compare Real and Pop Twice
OptCategory[NN_ficom] = 1;               // Compare Integer
OptCategory[NN_ficomp] = 1;              // Compare Integer and Pop
OptCategory[NN_ftst] = 1;                // Test
OptCategory[NN_fxam] = 1;                // Examine
OptCategory[NN_fptan] = 1;               // Partial tangent
OptCategory[NN_fpatan] = 1;              // Partial arctangent
OptCategory[NN_f2xm1] = 1;               // 2^x - 1
OptCategory[NN_fyl2x] = 1;               // Y * lg2(X)
OptCategory[NN_fyl2xp1] = 1;             // Y * lg2(X+1)
OptCategory[NN_fldz] = 1;                // Load +0.0
OptCategory[NN_fld1] = 1;                // Load +1.0
OptCategory[NN_fldpi] = 1;               // Load PI=3.14...
OptCategory[NN_fldl2t] = 1;              // Load lg2(10)
OptCategory[NN_fldl2e] = 1;              // Load lg2(e)
OptCategory[NN_fldlg2] = 1;              // Load lg10(2)
OptCategory[NN_fldln2] = 1;              // Load ln(2)
OptCategory[NN_finit] = 1;               // Initialize Processor
OptCategory[NN_fninit] = 1;              // Initialize Processor (no wait)
OptCategory[NN_fsetpm] = 1;              // Set Protected Mode
OptCategory[NN_fldcw] = 1;               // Load Control Word
OptCategory[NN_fstcw] = 0;               // Store Control Word
OptCategory[NN_fnstcw] = 0;              // Store Control Word (no wait)
OptCategory[NN_fstsw] = 2;               // Store Status Word to memory or AX
OptCategory[NN_fnstsw] = 2;              // Store Status Word (no wait) to memory or AX
OptCategory[NN_fclex] = 1;               // Clear Exceptions
OptCategory[NN_fnclex] = 1;              // Clear Exceptions (no wait)
OptCategory[NN_fstenv] = 0;              // Store Environment
OptCategory[NN_fnstenv] = 0;             // Store Environment (no wait)
OptCategory[NN_fldenv] = 1;              // Load Environment
OptCategory[NN_fsave] = 0;               // Save State
OptCategory[NN_fnsave] = 0;              // Save State (no wait)
OptCategory[NN_frstor] = 1;              // Restore State    **  infer src is 'n'
OptCategory[NN_fincstp] = 1;             // Increment Stack Pointer