/*
 * CiderPress
 * Copyright (C) 2007 by faddenSoft, LLC.  All Rights Reserved.
 * See the file LICENSE for distribution terms.
 */
/*
 * Disassemble Apple II binaries.
 */
#include "StdAfx.h"
#include "Disasm.h"

/*
 * ===========================================================================
 *		Common stuff for 65xxx-series CPU
 * ===========================================================================
 */

/*
 * The color scheme from Don Lancaster's "Enhancing your Apple II, Volume 1,
 * Second Edition":
 *	(1) Paint all subroutine returns (RTS) green (entire line to the right
 *	    of the 4-character address field).
 *	(2) Paint all subroutine calls (JSR) orange (with an extended swipe for
 *	    out-of-range calls, e.g. JSR $FDED).  Paint the target address of
 *	    the JSR orange as well.  Write the name of the target in brown felt
 *	    tip after the orange arrow, e.g. "COUT (output char)".
 *	(3) Paint all absolute jumps pink.  Highlight across the entire line
 *	    (everything but the address).  Paint the address field of the target
 *	    location pink.
 *	(4) Show branches in blue, with lines down the left-hand side.
 *	(5) Paint constants green ("lda #$74") and variables pink ("sta $38"),
 *	    highlighting only the operation (i.e. not the hex values in the
 *	    middle of the listing).
 *	(6) Paint the housekeeping yellow (inx, dex, dey, txa, cld, sec, tsx).
 *	    Draw yellow lines between pha/pla pairs.
 */

/*
 * Return the #of bytes required by the specified addressing mode, given a
 * particular CPU and values for the 'e', 'm', and 'x' flags.
 *
 * Width is always > 0.  This guarantees that we don't loop forever even if
 * we hit stuff we don't recognize.
 */
int
ReformatDisasm65xxx::GetOpWidth(OpCode opCode, AddrMode addrMode, CPU cpu,
	bool emul, bool shortM, bool shortX)
{
	int width = 0;

	switch (addrMode) {
	case kAddrAcc:
	case kAddrImplied:
	case kAddrStackPull:
	case kAddrStackPush:
	case kAddrStackRTI:
	case kAddrStackRTL:
	case kAddrStackRTS:
		width = 1;
		break;
	case kAddrDP:
	case kAddrDPIndexX:
	case kAddrDPIndexY:
	case kAddrDPIndexXInd:
	case kAddrDPInd:
	case kAddrDPIndLong:
	case kAddrDPIndIndexY:
	case kAddrDPIndIndexYLong:
	case kAddrPCRel:
	case kAddrStackRel:
	case kAddrStackRelIndexY:
	case kAddrStackDPInd:
		width = 2;
		break;
	case kAddrAbs:
	case kAddrAbsIndexX:
	case kAddrAbsIndexY:
	case kAddrAbsIndexXInd:
	case kAddrAbsInd:
	case kAddrAbsIndLong:
	case kAddrBlockMove:
	case kAddrPCRelLong:
	case kAddrStackAbs:
	case kAddrStackPCRel:
		width = 3;
		break;
	case kAddrAbsLong:
	case kAddrAbsIndexXLong:
		width = 4;
		break;
	case kAddrStackInt:		// BRK/COP
		if (emul || fOneByteBrkCop)
			width = 1;
		else
			width = 2;
		break;
	case kAddrImm:
		width = 2;
		if (!emul) {
			if (opCode == kOpCPX ||
				opCode == kOpCPY ||
				opCode == kOpLDX ||
				opCode == kOpLDY)
			{
				if (!shortX)
					width = 3;
			} else
			if (opCode == kOpADC ||
				opCode == kOpAND ||
				opCode == kOpBIT ||
				opCode == kOpCMP ||
				opCode == kOpEOR ||
				opCode == kOpLDA ||
				opCode == kOpORA ||
				opCode == kOpSBC)
			{
				if (!shortM)
					width = 3;
			} else
			if (opCode == kOpREP ||
				opCode == kOpSEP)
			{
				/* keep width = 2 */
			} else {
				assert(false);
			}
		}
		break;
	case kAddrWDM:				// not really defined
		width = 2;
		break;

	case kAddrModeUnknown:		// unknown opcode
		width = 1;
		break;
	default:
		assert(false);
		width = 1;
		break;
	}

	assert(width > 0);
	return width;
}

/*
 * Returns "true" if it looks like we're pointing at a ProDOS 8 MLI call.
 *
 * The caller is expected to check to see if we're in bank 0.
 */
bool
ReformatDisasm65xxx::IsP8Call(const unsigned char* srcBuf, long srcLen)
{
	if (srcLen >= 6 &&
		srcBuf[0] == 0x20 && srcBuf[1] == 0x00 && srcBuf[2] == 0xbf)
	{
		return true;
	}
	return false;
}

/*
 * Returns "true" if it looks like we're pointing at a IIgs toolbox call.
 */
bool
ReformatDisasm65xxx::IsToolboxCall(const unsigned char* srcBuf, long srcLen,
	long backState)
{
	if (srcLen >= 4 && backState >= 3 &&
		srcBuf[0] == 0x22 && srcBuf[1] == 0x00 && srcBuf[2] == 0x00 &&
		srcBuf[3] == 0xe1 && srcBuf[-3] == 0xa2)	// LDX #xxxx
	{
		return true;
	}
	return false;
}

/*
 * Returns "true" if it looks like we're pointing at an inline GS/OS call.
 */
bool
ReformatDisasm65xxx::IsInlineGSOS(const unsigned char* srcBuf, long srcLen)
{
	if (srcLen >= 10 &&
		srcBuf[0] == 0x22 && srcBuf[1] == 0xa8 && srcBuf[2] == 0x00 &&
		srcBuf[3] == 0xe1)
	{
		return true;
	}
	return false;
}

/*
 * Returns "true" if it looks like we're pointing at a stack GS/OS call.
 */
bool
ReformatDisasm65xxx::IsStackGSOS(const unsigned char* srcBuf, long srcLen,
	long backState)
{
	if (srcLen >= 4 && backState >= 3 &&
		srcBuf[0] == 0x22 && srcBuf[1] == 0xb0 && srcBuf[2] == 0x00 &&
		srcBuf[3] == 0xe1 && srcBuf[-3] == 0xf4)	// PEA xxxx
	{
		return true;
	}
	return false;
}


/*
 * Output one or more lines of text similar to what the monitor would output
 * on an 8-bit Apple II.
 *
 * Returns the number of bytes consumed.
 */
int
ReformatDisasm65xxx::OutputMonitor8(const unsigned char* srcBuf, long srcLen,
	long backState, unsigned short addr)
{
	const CPU kCPU = kCPU65C02;		// 6502 or 65C02
	OpCode opCode;
	AddrMode addrMode;
	int bytesUsed;
	int opAndAddr;

	opAndAddr = kOpMap[*srcBuf].opAndAddr[kCPU];
	opCode = (OpCode) (opAndAddr & 0xff);
	assert(opCode >= kOpCodeUnknown && opCode < kOpCodeMAX);
	addrMode = (AddrMode) (opAndAddr >> 8);
	assert(addrMode >= kAddrModeUnknown && addrMode < kAddrModeMAX);

	bytesUsed = GetOpWidth(opCode, addrMode, kCPU, true, true, true);

	if (IsP8Call(srcBuf, srcLen)) {
		/* print and skip P8 inline call stuff */
		const char* callName;

		callName = NiftyList::LookupP8MLI(srcBuf[3]);
		if (callName == nil)
			callName = "(Unknown P8 MLI)";
		PrintMonitor8Line(opCode, addrMode, addr, srcBuf, bytesUsed, callName);
		BufPrintf("%04X-   %02X                $%02X",
			addr+3, srcBuf[3], srcBuf[3]);
		RTFNewPara();
		BufPrintf("%04X-   %02X %02X             $%02X%02X",
			addr+4, srcBuf[4], srcBuf[5], srcBuf[5], srcBuf[4]);
		RTFNewPara();
		bytesUsed += 3;
	} else
	if (srcLen < bytesUsed) {
		assert(bytesUsed <= kMaxByteConsumption);
		unsigned char tmpBuf[kMaxByteConsumption];
		memset(tmpBuf, 0, kMaxByteConsumption);
		memcpy(tmpBuf, srcBuf, srcLen);

		PrintMonitor8Line(opCode, addrMode, addr, tmpBuf, bytesUsed, nil);
	} else {
		PrintMonitor8Line(opCode, addrMode, addr, srcBuf, bytesUsed, nil);
	}


	return bytesUsed;
}

/*
 * Output one line of 8-bit monitor stuff.
 */
void
ReformatDisasm65xxx::PrintMonitor8Line(OpCode opCode, AddrMode addrMode,
	unsigned short addr, const unsigned char* srcBuf, long srcLen,
	const char* comment)
{
	char lineBuf[64];	// actual length is about 30 -- does not hold comment
	char* cp;
	const char* mnemonic = kOpCodeDetails[opCode].mnemonic;
	unsigned char byte0, byte1, byte2;

	cp = lineBuf;

	switch (srcLen) {
	case 1:
		byte0 = srcBuf[0];
		byte1 = byte2 = 0xcc;		// make bugs more obvious
		cp += sprintf(cp, "%04X-   %02X          %s", addr, byte0, mnemonic);
		break;
	case 2:
		byte0 = srcBuf[0];
		byte1 = srcBuf[1];
		byte2 = 0xcc;
		cp += sprintf(cp, "%04X-   %02X %02X       %s", addr, byte0, byte1,
			mnemonic);
		break;
	case 3:
		byte0 = srcBuf[0];
		byte1 = srcBuf[1];
		byte2 = srcBuf[2];
		cp += sprintf(cp, "%04X-   %02X %02X %02X    %s", addr, byte0, byte1,
			byte2, mnemonic);
		break;
	default:
		assert(false);
		return;
	}

	switch (addrMode) {
	case kAddrImplied:
	case kAddrStackPull:
	case kAddrStackPush:
	case kAddrStackRTI:
	case kAddrStackRTS:
		break;
	case kAddrAcc:
		//cp += sprintf(cp, "   A");
		break;

	case kAddrDP:
		cp += sprintf(cp, "   $%02X", byte1);
		break;
	case kAddrDPIndexX:
		cp += sprintf(cp, "   $%02X,X", byte1);
		break;
	case kAddrDPIndexY:
		cp += sprintf(cp, "   $%02X,Y", byte1);
		break;
	case kAddrDPIndexXInd:
		cp += sprintf(cp, "   ($%02X,X)", byte1);
		break;
	case kAddrDPInd:
		cp += sprintf(cp, "   ($%02X)", byte1);
		break;
	case kAddrDPIndIndexY:
		cp += sprintf(cp, "   ($%02X),Y", byte1);
		break;
	case kAddrImm:
		cp += sprintf(cp, "   #$%02X", byte1);
		break;
	case kAddrPCRel:
		cp += sprintf(cp, "   $%04X", RelOffset(addr, byte1));
		break;

	case kAddrAbs:
		cp += sprintf(cp, "   $%02X%02X", byte2, byte1);
		if (comment == nil)
			comment = NiftyList::Lookup00Addr(byte1 | byte2 << 8);
		break;
	case kAddrAbsIndexX:
		cp += sprintf(cp, "   $%02X%02X,X", byte2, byte1);
		break;
	case kAddrAbsIndexY:
		cp += sprintf(cp, "   $%02X%02X,Y", byte2, byte1);
		break;
	case kAddrAbsIndexXInd:
		cp += sprintf(cp, "   ($%02X%02X,X)", byte2, byte1);
		break;
	case kAddrAbsInd:
		cp += sprintf(cp, "   ($%02X%02X)", byte2, byte1);
		break;
	case kAddrStackInt:			// BRK/COP
		if (srcLen != 1)
			cp += sprintf(cp, "   $%02X", byte1);
		break;

	case kAddrAbsIndLong:		// JML
	case kAddrAbsLong:
	case kAddrAbsIndexXLong:
	case kAddrBlockMove:
	case kAddrDPIndLong:
	case kAddrDPIndIndexYLong:
	case kAddrPCRelLong:		// BRL
	case kAddrStackAbs:			// PEA
	case kAddrStackDPInd:		// PEI
	case kAddrStackPCRel:		// PER
	case kAddrStackRTL:
	case kAddrStackRel:
	case kAddrStackRelIndexY:
	case kAddrWDM:
		// not for 8-bit mode
		assert(false);
		break;
	case kAddrModeUnknown:
		assert(srcLen == 1);
		break;
	default:
		assert(false);
		break;
	}

	assert(strlen(cp)+1 < sizeof(lineBuf));
	if (comment == nil)
		BufPrintf("%s", lineBuf);
	else
		BufPrintf("%s    %s", lineBuf, comment);
	RTFNewPara();
}


/*
 * Output one or more lines of text similar to what the monitor would output
 * on an 8-bit Apple II.
 *
 * Returns the number of bytes consumed.
 */
int
ReformatDisasm65xxx::OutputMonitor16(const unsigned char* srcBuf, long srcLen,
	long backState, unsigned long addr, bool shortRegs)
{
	const CPU kCPU = kCPU65816;
	OpCode opCode;
	AddrMode addrMode;
	int bytesUsed;
	int opAndAddr;
		const char* callName;

	opAndAddr = kOpMap[*srcBuf].opAndAddr[kCPU];
	opCode = (OpCode) (opAndAddr & 0xff);
	assert(opCode >= kOpCodeUnknown && opCode < kOpCodeMAX);
	addrMode = (AddrMode) (opAndAddr >> 8);
	assert(addrMode >= kAddrModeUnknown && addrMode < kAddrModeMAX);

	bytesUsed = GetOpWidth(opCode, addrMode, kCPU, false, shortRegs, shortRegs);

	if (Bank(addr) == 0 && IsP8Call(srcBuf, srcLen)) {
		/* print and skip P8 inline call stuff */
		callName = NiftyList::LookupP8MLI(srcBuf[3]);
		if (callName == nil)
			callName = "(Unknown P8 MLI)";
		PrintMonitor16Line(opCode, addrMode, addr, srcBuf, bytesUsed, callName);
		BufPrintf("00/%04X: %02X                %02X",
			addr+3, srcBuf[3], srcBuf[3]);
		RTFNewPara();
		BufPrintf("00/%04X: %02X %02X             %02X%02X",
			addr+4, srcBuf[4], srcBuf[5], srcBuf[5], srcBuf[4]);
		RTFNewPara();
		bytesUsed += 3;
	} else
	if (IsToolboxCall(srcBuf, srcLen, backState)) {
		callName = NiftyList::LookupToolbox(srcBuf[-2] | srcBuf[-1] << 8);
		PrintMonitor16Line(opCode, addrMode, addr, srcBuf, bytesUsed, callName);
	} else
	if (IsInlineGSOS(srcBuf, srcLen)) {
		callName = NiftyList::LookupGSOS(srcBuf[4] | srcBuf[5] << 8);
		PrintMonitor16Line(opCode, addrMode, addr, srcBuf, bytesUsed, callName);
		BufPrintf("%02X/%04X: %02X %02X             %02X%02X",
			Bank(addr), Offset(addr+4), srcBuf[4], srcBuf[5],
			srcBuf[5], srcBuf[4]);
		RTFNewPara();
		BufPrintf("%02X/%04X: %02X %02X %02X %02X       %02X%02X%02X%02X",
			Bank(addr), Offset(addr+6),
			srcBuf[6], srcBuf[7], srcBuf[8], srcBuf[9],
			srcBuf[9], srcBuf[8], srcBuf[7], srcBuf[6]);
		RTFNewPara();
		bytesUsed += 6;
	} else
	if (IsStackGSOS(srcBuf, srcLen, backState)) {
		callName = NiftyList::LookupGSOS(srcBuf[-2] | srcBuf[-1] << 8);
		PrintMonitor16Line(opCode, addrMode, addr, srcBuf, bytesUsed, callName);
	} else
	if (srcLen < bytesUsed) {
		assert(bytesUsed <= kMaxByteConsumption);
		unsigned char tmpBuf[kMaxByteConsumption];
		memset(tmpBuf, 0, kMaxByteConsumption);
		memcpy(tmpBuf, srcBuf, srcLen);

		PrintMonitor16Line(opCode, addrMode, addr, tmpBuf, bytesUsed, nil);
	} else {
		PrintMonitor16Line(opCode, addrMode, addr, srcBuf, bytesUsed, nil);
	}

	return bytesUsed;
}

/*
 * Output one line of 16-bit monitor stuff.
 */
void
ReformatDisasm65xxx::PrintMonitor16Line(OpCode opCode, AddrMode addrMode,
	unsigned long addr, const unsigned char* srcBuf, long srcLen,
	const char* comment)
{
	char lineBuf[64];	// actual length is about 30 -- does not hold comment
	char* cp;
	const char* mnemonic = kOpCodeDetails[opCode].mnemonic;
	unsigned char byte0, byte1, byte2, byte3;
	short offset;

	cp = lineBuf;

	cp += sprintf(cp, "%02X/%04X: ", addr >> 16, addr & 0xffff);

	switch (srcLen) {
	case 1:
		byte0 = srcBuf[0];
		byte1 = byte2 = byte3 = 0xcc;		// make bugs more obvious
		cp += sprintf(cp, "%02X            %s", byte0, mnemonic);
		break;
	case 2:
		byte0 = srcBuf[0];
		byte1 = srcBuf[1];
		byte2 = byte3 = 0xcc;
		cp += sprintf(cp, "%02X %02X         %s", byte0, byte1, mnemonic);
		break;
	case 3:
		byte0 = srcBuf[0];
		byte1 = srcBuf[1];
		byte2 = srcBuf[2];
		byte3 = 0xcc;
		cp += sprintf(cp, "%02X %02X %02X      %s", byte0, byte1, byte2,
			mnemonic);
		break;
	case 4:
		byte0 = srcBuf[0];
		byte1 = srcBuf[1];
		byte2 = srcBuf[2];
		byte3 = srcBuf[3];
		cp += sprintf(cp, "%02X %02X %02X %02X   %s", byte0, byte1, byte2,
			byte3, mnemonic);
		break;
	default:
		assert(false);
		return;
	}

	switch (addrMode) {
	case kAddrImplied:
	case kAddrStackPull:
	case kAddrStackPush:
	case kAddrStackRTI:
	case kAddrStackRTS:
	case kAddrStackRTL:
		break;
	case kAddrAcc:
		//cp += sprintf(cp, "   A");
		break;

	case kAddrDP:
		cp += sprintf(cp, " %02X", byte1);
		break;
	case kAddrDPIndexX:
		cp += sprintf(cp, " %02X,X", byte1);
		break;
	case kAddrDPIndexY:
		cp += sprintf(cp, " %02X,Y", byte1);
		break;
	case kAddrDPIndexXInd:
		cp += sprintf(cp, " (%02X,X)", byte1);
		break;
	case kAddrDPInd:
		cp += sprintf(cp, " (%02X)", byte1);
		break;
	case kAddrDPIndIndexY:
		cp += sprintf(cp, " (%02X),Y", byte1);
		break;
	case kAddrImm:
		if (srcLen == 2)
			cp += sprintf(cp, " #%02X", byte1);
		else
			cp += sprintf(cp, " #%02X%02X", byte2, byte1);
		break;
	case kAddrPCRel:
		offset = (char) byte1;
		if (offset < 0)
			cp += sprintf(cp, " %04X {-%02X}",
				RelOffset((unsigned short) addr, byte1), -offset);
		else
			cp += sprintf(cp, " %04X {+%02X}",
				RelOffset((unsigned short) addr, byte1), offset);
		break;

	case kAddrAbs:
		cp += sprintf(cp, " %02X%02X", byte2, byte1);
		if (comment == nil && Bank(addr) == 0)
			comment = NiftyList::Lookup00Addr(byte1 | byte2 << 8);
		break;
	case kAddrAbsIndexX:
		cp += sprintf(cp, " %02X%02X,X", byte2, byte1);
		break;
	case kAddrAbsIndexY:
		cp += sprintf(cp, " %02X%02X,Y", byte2, byte1);
		break;
	case kAddrAbsIndexXInd:
		cp += sprintf(cp, " (%02X%02X,X)", byte2, byte1);
		break;
	case kAddrAbsInd:
		cp += sprintf(cp, " (%02X%02X)", byte2, byte1);
		break;
	case kAddrWDM:
	case kAddrStackInt:			// BRK/COP
		if (srcLen != 1)
			cp += sprintf(cp, " %02X", byte1);
		break;

	case kAddrAbsIndLong:		// JML
		cp += sprintf(cp, " (%02X%02X)", byte2, byte1);
		break;
	case kAddrAbsLong:
		cp += sprintf(cp, " %02X%02X%02X", byte3, byte2, byte1);
		if (comment == nil) {
			if (byte3 == 0x00)
				comment = NiftyList::Lookup00Addr(byte1 | byte2 << 8);
			else if (byte3 == 0x01)
				comment = NiftyList::Lookup01Vector(byte1 | byte2 << 8);
			else if (byte3 == 0xe0)
				comment = NiftyList::LookupE0Vector(byte1 | byte2 << 8);
			else if (byte3 == 0xe1)
				comment = NiftyList::LookupE1Vector(byte1 | byte2 << 8);
		}
		break;
	case kAddrAbsIndexXLong:
		cp += sprintf(cp, " %02X%02X%02X,X", byte3, byte2, byte1);
		break;
	case kAddrBlockMove:
		cp += sprintf(cp, " %02X%02X", byte2, byte1);
		break;
	case kAddrDPIndLong:
		cp += sprintf(cp, " [%02X]", byte1);
		break;
	case kAddrDPIndIndexYLong:
		cp += sprintf(cp, " [%02X],Y", byte1);
		break;
	case kAddrStackPCRel:		// PER
	case kAddrPCRelLong:		// BRL
		offset = (short) (byte1 | byte2 << 8);
		if (offset < 0)
			cp += sprintf(cp, " %04X {-%02X}",
				RelLongOffset((unsigned short) addr, offset), -offset);
		else
			cp += sprintf(cp, " %04X {+%02X}",
				RelLongOffset((unsigned short) addr, offset), offset);
		break;
	case kAddrStackAbs:			// PEA
		cp += sprintf(cp, " %02X%02X", byte2, byte1);
		break;
	case kAddrStackDPInd:		// PEI
		cp += sprintf(cp, " %02X", byte1);
		break;
	case kAddrStackRel:
		cp += sprintf(cp, " %02X,S", byte1);
		break;
	case kAddrStackRelIndexY:
		cp += sprintf(cp, " (%02X,S),Y", byte1);
		break;
	case kAddrModeUnknown:
		assert(srcLen == 1);
		break;
	default:
		assert(false);
		break;
	}

	assert(strlen(cp)+1 < sizeof(lineBuf));
	if (comment == nil)
		BufPrintf("%s", lineBuf);
	else {
		if (srcLen < 4)
			BufPrintf("%s    %s", lineBuf, comment);
		else
			BufPrintf("%s  %s", lineBuf, comment);
	}
	RTFNewPara();
}


/*
 * ===========================================================================
 *		Disassemble 8-bit code
 * ===========================================================================
 */

/*
 * For ProDOS 8 and DOS 3.3 stuff, we can't say for sure whether a "BIN"
 * file is code or, say, a hi-res graphic.  We use "maybe" level to put
 * disassembly below other formats.
 */
void
ReformatDisasm8::Examine(ReformatHolder* pHolder)
{
	ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
	int fileType = pHolder->GetFileType();

	if (fileType >= 0xf1 && fileType <= 0xf8) {
		applies = ReformatHolder::kApplicProbablyNot;
	} else if (fileType == 0x00 &&
		pHolder->GetSourceFormat() != ReformatHolder::kSourceFormatCPM)
	{
		applies = ReformatHolder::kApplicProbablyNot;
	} else if (fileType == kTypeBIN) {
		applies = ReformatHolder::kApplicMaybe;
	} else if (fileType == kTypeSYS || fileType == kTypeCMD ||
		fileType == kType8OB || fileType == kTypeP8C)
	{
		applies = ReformatHolder::kApplicYes;
	}

	pHolder->SetApplic(ReformatHolder::kReformatMonitor8, applies,
		ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);

	// not doing this yet
	pHolder->SetApplic(ReformatHolder::kReformatDisasmMerlin8,
		ReformatHolder::kApplicNot,
		ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
}

/*
 * Create a monitor listing or disassembly of a file.
 */
int
ReformatDisasm8::Process(const ReformatHolder* pHolder,
	ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
	ReformatOutput* pOutput)
{
	const unsigned char* srcBuf = pHolder->GetSourceBuf(part);
	long srcLen = pHolder->GetSourceLen(part);
	long fileType = pHolder->GetFileType();
	long backState = 0;
	unsigned short addr;
	fUseRTF = false;

	if (!srcLen)
		return -1;

	RTFBegin();

	/* pick a nice 16-bit start address */
	if (fileType == kTypeSYS || fileType == kTypeP8C || fileType == kType8OB)
		addr = 0x2000;
	else if (fileType == kTypeBIN || fileType == kTypeCMD)
		addr = (unsigned short) pHolder->GetAuxType();
	else
		addr = 0x0000;

	while (srcLen > 0) {
		int consumed;

		consumed = OutputMonitor8(srcBuf, srcLen, backState, addr);

		srcBuf += consumed;
		srcLen -= consumed;
		backState += consumed;
		addr += consumed;
	}

	RTFEnd();
	SetResultBuffer(pOutput);
	return 0;
}


/*
 * ===========================================================================
 *		Disassemble 16-bit code
 * ===========================================================================
 */

/*
 * For 16-bit stuff, the file types are always explicit.  However, there are
 * instances of 65802/65816 code being written for 8-bit platforms, so we
 * want to keep 16-bit disassembly available but prioritized below "raw" for
 * BIN files.
 */
void
ReformatDisasm16::Examine(ReformatHolder* pHolder)
{
	ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot;
	long fileType = pHolder->GetFileType();

	if (fileType == kTypeBIN || fileType == kTypeSYS || fileType == kTypeCMD ||
		fileType == kType8OB || fileType == kTypeP8C)
	{
		applies = ReformatHolder::kApplicProbablyNot;
	} else if (fileType == 0x00 &&
		pHolder->GetSourceFormat() != ReformatHolder::kSourceFormatCPM)
	{
		applies = ReformatHolder::kApplicProbablyNot;
	} else {
		/*
		 * Interesting file types (GS/OS ref table F-1):
		 *	$B1 OBJ Object
		 *	$B2 LIB Library
		 *	$B3 S16 GS/OS or ProDOS 16 app
		 *	$B4 RTL Run-time library
		 *	$B5 EXE Shell application
		 *	$B6 PIF Permanent initialization
		 *	$B7 TIF Temporary initialization
		 *	$B8 NDA New desk accessory
		 *	$B9 CDA Classic desk accessory
		 *	$BA TOL Tool set file
		 *	$BB DVR Apple IIgs device driver
		 *	$BC LDF Generic load file (application-specific)
		 *	$BD FST GS/OS file system translator
		 *
		 * We also handle non-OMF files:
		 *	$F9 OS  GS/OS System file
		 */
		if (fileType >= kTypeOBJ && fileType <= kTypeFST)
			applies = ReformatHolder::kApplicYes;
		else if (fileType == kTypeOS)
			applies = ReformatHolder::kApplicYes;
	}

	pHolder->SetApplic(ReformatHolder::kReformatMonitor16Long, applies,
		ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
	pHolder->SetApplic(ReformatHolder::kReformatMonitor16Short, applies,
		ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
	pHolder->SetApplicPreferred(ReformatHolder::kReformatMonitor16Long);

	// not doing this yet
	pHolder->SetApplic(ReformatHolder::kReformatDisasmOrcam16,
		ReformatHolder::kApplicNot,
		ReformatHolder::kApplicNot, ReformatHolder::kApplicNot);
}

/*
 * Disassemble or show monitor listing for 16-bit code.
 */
int
ReformatDisasm16::Process(const ReformatHolder* pHolder,
	ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
	ReformatOutput* pOutput)
{
	const unsigned char* srcBuf = pHolder->GetSourceBuf(part);
	long srcLen = pHolder->GetSourceLen(part);
	long fileType = pHolder->GetFileType();
	unsigned long addr = 0;
	bool shortRegs;

	fUseRTF = false;

	if (!srcLen)
		return -1;

	RTFBegin();

	if (id == ReformatHolder::kReformatMonitor16Long)
		shortRegs = false;
	else
		shortRegs = true;
	fOneByteBrkCop = pHolder->GetOption(ReformatHolder::kOptOneByteBrkCop) != 0;

	if (fileType >= kTypeOBJ && fileType <= kTypeFST) {
		if (!OutputOMF(srcBuf, srcLen, fileType, shortRegs)) {
			/* must not be OMF; do a generic list */
			fExpBuf.Reset();
			BufPrintf("[ Valid OMF expected but not found ]\r\n");
			RTFNewPara();
			OutputSection(srcBuf, srcLen, addr, shortRegs);
		}
	} else {
		/* pick a start address in bank 0 */
		if (fileType == kTypeSYS)
			addr = 0x2000;
		else if (fileType == kTypeBIN || fileType == kTypeCMD)
			addr = (unsigned short) pHolder->GetAuxType();
		OutputSection(srcBuf, srcLen, addr, shortRegs);
	}

	RTFEnd();
	SetResultBuffer(pOutput);
	return 0;
}

/*
 * Output one section of a file.
 */
void
ReformatDisasm16::OutputSection(const unsigned char* srcBuf, long srcLen,
	unsigned long addr, bool shortRegs)
{
	long backState = 0;

	while (srcLen > 0) {
		int consumed;

		consumed = OutputMonitor16(srcBuf, srcLen, backState, addr, shortRegs);

		srcBuf += consumed;
		srcLen -= consumed;
		backState += consumed;
		addr += consumed;
	}
}

/*
 * Break an OMF file into sections, and output each individually.
 */
bool
ReformatDisasm16::OutputOMF(const unsigned char* srcBuf, long srcLen,
	long fileType, bool shortRegs)
{
	const unsigned char* origBuf = srcBuf;
	long origLen = srcLen;
	OMFSegmentHeader segHdr;
	int segmentNumber = 1;

	BufPrintf(";\r\n");
	BufPrintf("; OMF segment summary:\r\n", segmentNumber);
	BufPrintf(";\r\n");

	/* pass #1: print a preview */
	while (srcLen > 0) {
		if (!segHdr.Unpack(srcBuf, srcLen, fileType)) {
			if (segmentNumber == 1)
				return false;
			else {
				BufPrintf("; (bad header found, ignoring last %ld bytes)\r\n",
					srcLen);
				break;	// out of while; display what we have
			}
		}

		PrintHeader(&segHdr, segmentNumber, false);

		srcBuf += segHdr.GetSegmentLen();
		srcLen -= segHdr.GetSegmentLen();
		segmentNumber++;
	}
	BufPrintf(";\r\n");
	RTFNewPara();

	segmentNumber = 1;
	srcBuf = origBuf;
	srcLen = origLen;
	while (srcLen > 0) {
		if (!segHdr.Unpack(srcBuf, srcLen, fileType)) {
			BufPrintf("!!!\r\n");
			BufPrintf("!!! Found bad OMF header at offset 0x%04x (remaining len=%ld)\r\n",
				srcBuf - origBuf, srcLen);
			BufPrintf("!!!\r\n");
			RTFNewPara();
			if (segmentNumber == 1)
				return false;
			else
				return true;
		}

		PrintHeader(&segHdr, segmentNumber, true);

		PrintSegment(&segHdr, srcBuf, srcLen, shortRegs);
		RTFNewPara();

		srcBuf += segHdr.GetSegmentLen();
		srcLen -= segHdr.GetSegmentLen();
		segmentNumber++;
	}

	return true;
}

/*
 * Print the interesting bits of the header.
 */
void
ReformatDisasm16::PrintHeader(const OMFSegmentHeader* pSegHdr,
	int segmentNumber, bool longFmt)
{
	//pSegHdr->Dump();

	const char* versStr;
	switch (pSegHdr->GetVersion()) {
	case 0:		versStr = "1.0";		break;
	case 1:		versStr = "2.0";		break;
	case 2:		versStr = "2.1";		break;
	default:	versStr = "(unknown)";	break;
	}

	const char* typeStr;
	switch (pSegHdr->GetSegmentType()) {
	case OMFSegmentHeader::kTypeCode:			typeStr = "CODE";		break;
	case OMFSegmentHeader::kTypeData:			typeStr = "DATA";		break;
	case OMFSegmentHeader::kTypeJumpTable:		typeStr = "JumpTab";	break;
	case OMFSegmentHeader::kTypePathName:		typeStr = "PathName";	break;
	case OMFSegmentHeader::kTypeLibraryDict:	typeStr = "LibDict";	break;
	case OMFSegmentHeader::kTypeInit:			typeStr = "Init";		break;
	case OMFSegmentHeader::kTypeAbsoluteBank:	typeStr = "AbsBank";	break;
	case OMFSegmentHeader::kTypeDPStack:		typeStr = "DP/Stack";	break;
	default:									typeStr = "(unknown)";	break;
	}

	if (longFmt) {
		BufPrintf(";\r\n");
		BufPrintf("; Segment #%d (%d): loadName='%s' segName='%s': \r\n",
			segmentNumber, pSegHdr->GetSegNum(),
			pSegHdr->GetLoadName(), pSegHdr->GetSegName());
		BufPrintf(";  type=%s  length=%ld  OMF v%s\r\n",
			typeStr, pSegHdr->GetSegmentLen(), versStr);
		BufPrintf(";  flags:%s%s%s%s%s%s%s%s\r\n",
			pSegHdr->GetSegmentFlag(OMFSegmentHeader::kFlagBankRelative) ? " bankRel" : "",
			pSegHdr->GetSegmentFlag(OMFSegmentHeader::kFlagSkip) ? " skip" : "",
			pSegHdr->GetSegmentFlag(OMFSegmentHeader::kFlagReload) ? " reload" : "",
			pSegHdr->GetSegmentFlag(OMFSegmentHeader::kFlagAbsoluteBank) ? " absBank" : "",
			pSegHdr->GetSegmentFlag(OMFSegmentHeader::kFlagNoSpecialMem) ? " noSpecial" : "",
			pSegHdr->GetSegmentFlag(OMFSegmentHeader::kFlagPositionIndep) ? " posnIndep" : "",
			pSegHdr->GetSegmentFlag(OMFSegmentHeader::kFlagPrivate) ? " private" : "",
			pSegHdr->GetSegmentFlag(OMFSegmentHeader::kFlagDynamic) ? " dynamic" : "");
		BufPrintf(";");
		RTFNewPara();
	} else {
		BufPrintf(";  #%02d: %-8s len=0x%06x  loadName='%s' segName='%s'\r\n",
			segmentNumber, typeStr, pSegHdr->GetSegmentLen(),
			pSegHdr->GetLoadName(), pSegHdr->GetSegName());
	}
}


/*
 * Print the contents of the segment.
 */
void
ReformatDisasm16::PrintSegment(const OMFSegmentHeader* pSegHdr,
	const unsigned char* srcBuf, long srcLen, bool shortRegs)
{
	unsigned long subLen;
	int offset = 0;

	assert(pSegHdr != nil);
	assert(srcBuf != nil);
	assert(srcLen > 0);

	srcBuf += pSegHdr->GetDispData();
	srcLen -= pSegHdr->GetDispData();
	if (srcLen < 0) {
		BufPrintf("GLITCH: ran out of data\r\n");
		return;
	}

	if (*srcBuf == 0xf2 && srcLen >= 5) {
		/* handle kSegOpLCONST just for fun */
		subLen = Get32LE(srcBuf+1);
		BufPrintf("OMF: LCONST record (0x%04x bytes follow)\r\n", subLen);
		offset = 5;
	}
	OutputSection(srcBuf + offset,
		pSegHdr->GetSegmentLen() - pSegHdr->GetDispData() - offset,
		0x000000, shortRegs);

#if 0
	OMFSegment seg;
	seg.Setup(pSegHdr, srcBuf, srcLen);
	const unsigned char* ptr;

	do {
		ptr = seg.ProcessNextChunk();
		if (ptr == nil) {
			BufPrintf("!!! bogus OMF values encountered\r\n");
			return;
		}

		switch (*ptr) {
		case OMFSegment::kSegOpEND:
			BufPrintf("OMF: END (bytesLeft=%d)\r\n", (ptr+1) - srcBuf);
			break;
		}
	} while (*ptr != OMFSegment::kSegOpEND);
#endif
}


/*
 * ===========================================================================
 *		OMFSegmentHeader
 * ===========================================================================
 */

/*
 * Unpack an OMF segment header from the current offset.
 *
 * Returns "true" on success, "false" on failure.
 */
bool
OMFSegmentHeader::Unpack(const unsigned char* srcBuf, long srcLen,
	int fileType)
{
	if (srcLen < kHdrMinSize) {
		WMSG1("OMF: Too short to be segment (%ld)\n", srcLen);
		return false;
	}

	fVersion = *(srcBuf + 0x0f);
	if (fVersion > kMaxVersion) {
		WMSG1("OMF: Wrong version number to be OMF (%d)\n", fVersion);
		return false;
	}

	if (fVersion == 0) {
		/* unpack OMF v1.0 */
		if (srcLen < kV0HdrMinSize)
			return false;

		fBlockCnt = Get32LE(srcBuf + 0x00);
		fResSpc = Get32LE(srcBuf + 0x04);
		fLength = Get32LE(srcBuf + 0x08);
		fType = *(srcBuf + 0x0c);
		fLabLen = *(srcBuf + 0x0d);
		fNumLen = *(srcBuf + 0x0e);
		fBankSize = Get32LE(srcBuf + 0x10);
		fOrg = Get32LE(srcBuf + 0x14);
		fAlign = Get32LE(srcBuf + 0x18);
		fNumSex = *(srcBuf + 0x1c);

		fByteCnt = fBlockCnt * 512;
		fKind = 0;
		fLCBank = 0;
		fSegNum = 0;
		fEntry = 0;
		fTempOrg = 0;
		fDispName = 0x24;
		if (fLabLen == 0)
			fDispData = fDispName + *(srcBuf + fDispName);
		else
			fDispData = fDispName + fLabLen;
	} else if (fVersion == 1) {
		/* unpack OMF v2.0 */
		if (srcLen < kV1HdrMinSize)
			return false;
		fBlockCnt = Get32LE(srcBuf + 0x00);
		fResSpc = Get32LE(srcBuf + 0x04);
		fLength = Get32LE(srcBuf + 0x08);
		fType = *(srcBuf + 0x0c);
		fLabLen = *(srcBuf + 0x0d);
		fNumLen = *(srcBuf + 0x0e);
		fBankSize = Get32LE(srcBuf + 0x10);
		// unused32 at 0x14
		fOrg = Get32LE(srcBuf + 0x18);
		fAlign = Get32LE(srcBuf + 0x1c);
		fNumSex = *(srcBuf + 0x20);
		fLCBank = *(srcBuf + 0x21);
		fSegNum = Get16LE(srcBuf + 0x22);
		fEntry = Get32LE(srcBuf + 0x24);
		fDispName = Get16LE(srcBuf + 0x28);
		fDispData = Get16LE(srcBuf + 0x2a);

		/*
		 * Orca/APW libs seem to use version 1 with a byte count in the
		 * first field, but the "LLRE" app has version 1 with a block count.
		 * The spec is pretty clear, so it looks like somebody's library
		 * builder screwed up.
		 *
		 * Special case type=LIB ($B2).
		 */
		if (fileType == 0xb2) {
			WMSG1("NOTE: switching blockCount=%ld to byte count\n", fBlockCnt);
			fByteCnt = fBlockCnt;
			fBlockCnt = 0;
		} else {
			fByteCnt = fBlockCnt * 512;
		}
		fKind = 0;
		fTempOrg = 0;
	} else {
		/* unpack OMF v2.1 */
		if (srcLen < kV2HdrMinSize)
			return false;
		fByteCnt = Get32LE(srcBuf + 0x00);
		fResSpc = Get32LE(srcBuf + 0x04);
		fLength = Get32LE(srcBuf + 0x08);
		// unused at +0x0c
		fLabLen = *(srcBuf + 0x0d);
		fNumLen = *(srcBuf + 0x0e);
		fBankSize = Get32LE(srcBuf + 0x10);
		fKind = Get16LE(srcBuf + 0x14);
		// unused at +0x16
		fOrg = Get32LE(srcBuf + 0x18);
		fAlign = Get32LE(srcBuf + 0x1c);
		fNumSex = *(srcBuf + 0x20);
		fSegNum = Get16LE(srcBuf + 0x22);
		// unused at +0x23
		fEntry = Get32LE(srcBuf + 0x24);
		fDispName = Get16LE(srcBuf + 0x28);
		fDispData = Get16LE(srcBuf + 0x2a);
		fTempOrg = Get32LE(srcBuf + 0x2c);

		fBlockCnt = 0;
		fType = 0;
		fLCBank = 0;
	}


	/* validate fields */
	if (fByteCnt < kHdrMinSize || fByteCnt > (unsigned long) srcLen) {
		WMSG3("OMF: Bad value for byteCnt (%ld, srcLen=%ld min=%d)\n",
			fByteCnt, srcLen, kHdrMinSize);
		return false;
	}
	if (fDispName < 0x24 || fDispName > (srcLen - kLoadNameLen)) {
		WMSG2("OMF: Bad value for dispName (%d, srcLen=%ld)\n",
			fDispName, srcLen);
		return false;
	}
	if (fDispData < 0x24 || fDispData > srcLen) {
		WMSG2("OMF: Bad value for dispData (%d, srcLen=%ld)\n",
			fDispData, srcLen);
		return false;
	}
	if (fDispData < fDispName + kLoadNameLen) {
		WMSG2("OMF: dispData is inside label region (%d / %d)\n",
			fDispData, fDispName);
		return false;
	}
	if (fBankSize != kExpectedBankSize && fBankSize != 0) {
		WMSG1("OMF: NOTE: bankSize=%ld\n", fBankSize);
		/* allowed, just weird */
	}
	if (fNumLen != kExpectedNumLen || fNumSex != 0) {
		WMSG2("OMF: WARNING: numLen=%d numSex=%d\n", fNumLen, fNumSex);
		/* big endian odd-sized numbers?? keep going, I guess */
	}

	const unsigned char* segName;
	int segLabelLen;

	/* copy the label entries over */
	segName = srcBuf + fDispName;
	if (fVersion > 0) {
		memcpy(fLoadName, srcBuf + fDispName, kLoadNameLen);
		fLoadName[kLoadNameLen] = '\0';

		segName +=  kLoadNameLen;
	}

	if (fLabLen == 0) {
		/* pascal-style string */
		segLabelLen = *segName++;
		memcpy(fSegName, segName, segLabelLen);
		fSegName[segLabelLen] = '\0';
		WMSG1(" OMF: Pascal segment label '%s'\n", fSegName);
	} else {
		/* C-style or non-terminated string */
		segLabelLen = fLabLen;
		memcpy(fSegName, segName, segLabelLen);
		fSegName[segLabelLen] = '\0';
		WMSG1(" OMF: Std segment label '%s'\n", fSegName);
	}

	fReady = true;
	return true;
}


/*
 * Pry the segment type out of the "type" or "kind" field.
 */
OMFSegmentHeader::SegmentType
OMFSegmentHeader::GetSegmentType(void) const
{
	assert(fReady);

	if (fVersion < 2)
		return (SegmentType) (fType & 0x1f);
	else
		return (SegmentType) (fKind & 0x1f);
}

/*
 * Return the value of one of the segment header flags.
 */
bool
OMFSegmentHeader::GetSegmentFlag(SegmentFlag flag) const
{
	if (fVersion < 2) {
		switch (flag) {
		case kFlagPositionIndep:
			return (fType & 0x20) != 0;
		case kFlagPrivate:
			return (fType & 0x40) != 0;
		case kFlagDynamic:
			return (fType & 0x80) != 0;
		default:
			return false;
		}
	} else {
		switch (flag) {
		case kFlagBankRelative:
			return (fKind & 0x0100) != 0;
		case kFlagSkip:
			return (fKind & 0x0200) != 0;
		case kFlagReload:
			return (fKind & 0x0400) != 0;
		case kFlagAbsoluteBank:
			return (fKind & 0x0800) != 0;
		case kFlagNoSpecialMem:
			return (fKind & 0x1000) != 0;
		case kFlagPositionIndep:
			return (fKind & 0x2000) != 0;
		case kFlagPrivate:
			return (fKind & 0x4000) != 0;
		case kFlagDynamic:
			return (fKind & 0x8000) != 0;
		default:
			assert(false);
			return false;
		}
	}
}


/*
 * Dump the contents of the segment header struct.
 */
void
OMFSegmentHeader::Dump(void) const
{
	WMSG0("OMF segment header:\n");
	WMSG3("  segNum=%d loadName='%s' segName='%s'\n",
		fSegNum, fLoadName, fSegName);
	WMSG4("  blockCnt=%ld byteCnt=%ld resSpc=%ld length=%ld\n",
		fBlockCnt, fByteCnt, fResSpc, fLength);
	WMSG3("  version=%d type=0x%02x kind=0x%04x\n",
		fVersion, fType, fKind);
	WMSG4("  labLen=%d numLen=%d bankSize=%ld lcBank=%d\n",
		fLabLen, fNumLen, fBankSize, fLCBank);
	WMSG4("  align=%ld numSex=%d org=%ld tempOrg=%ld\n",
		fAlign, fNumSex, fOrg, fTempOrg);
	WMSG3("  entry=%ld dispName=%d dispData=%d\n",
		fEntry, fDispName, fDispData);
}

#if 0
/*
 * ===========================================================================
 *		OMFSegment
 * ===========================================================================
 */

/*
 * Prepare to roll through a segment.
 */
void
OMFSegment::Setup(const OMFSegmentHeader* pSegHdr, const unsigned char* srcBuf,
	long srcLen)
{
	fSegBuf = fCurPtr = srcBuf + pSegHdr->GetDispData();
	fSegLen = srcLen - pSegHdr->GetDispData();

	fLabLen = pSegHdr->GetLabLen();
	fNumLen = pSegHdr->GetNumLen();
}

/*
 * Process the next chunk from the segment.
 *
 * Returns a pointer to the start of the chunk, or "nil" if we've encountered
 * some bogus condition (e.g. running off the end).
 */
const unsigned char*
OMFSegment::ProcessNextChunk(void)
{
	const unsigned char* prevPtr = fCurPtr;
	long remLen = fSegLen - (fCurPtr - fSegBuf);
	unsigned long subLen;
	int len = 1;		// one byte at least (for the opcode)

	assert(fLabLen >= 0);
	assert(fNumLen > 0);

	switch (*fCurPtr) {
	case kSegOpEND:
		WMSG1("  OMF END reached, remaining len = %d\n",
			fSegLen - (fCurPtr - fSegBuf));
		assert(len == 1);
		break;
	case kSegOpALIGN:
		len += fNumLen;
		break;
	case kSegOpORG:
		len += fNumLen;
		break;
	case kSegOpRELOC:
		len += 10;
		break;
	case kSegOpINTERSEG:
		len += 14;
		break;
	case kSegOpUSING:
	case kSegOpSTRONG:
		len += LabelLength(fCurPtr + 1);
		break;
	case kSegOpGLOBAL:
		len += LabelLength(fCurPtr + 1) + 4;
		break;
	case kSegOpGEQU:
		len += LabelLength(fCurPtr + 1) + 4;
		len += ExpressionLength(fCurPtr + len);
		break;
	case kSegOpMEM:		// not used on IIgs?
		len += fNumLen*2;
		break;
	case kSegOpEXPR:
	case kSegOpZEXPR:
	case kSegOpBEXPR:
		len += 1;
		len += ExpressionLength(fCurPtr + len);
		break;
	case kSegOpRELEXPR:
		len += 1 + fNumLen;
		len += ExpressionLength(fCurPtr + len);
		break;
	case kSegOpLOCAL:
		len += LabelLength(fCurPtr+1) + 4;
		break;
	case kSegOpEQU:
		len += LabelLength(fCurPtr+1) + 4;
		len += ExpressionLength(fCurPtr + len);
		break;
	case kSegOpDS:
		len += fNumLen;
		break;
	case kSegOpLCONST:
		subLen = Get32LE(fCurPtr+1);
		len += fNumLen + subLen;
		break;
	case kSegOpLEXPR:
		len += 1;
		len += ExpressionLength(fCurPtr + len);
		break;
	case kSegOpENTRY:
		len += 6;
		len += LabelLength(fCurPtr + len);
		break;
	case kSegOpcRELOC:
		len += 6;
		break;
	case kSegOpcINTERSEG:
		len += 7;
		break;
	case kSegOpSUPER:
		subLen = Get32LE(fCurPtr+1);
		len += 4 + subLen;
		break;

	case kSegOpGeneral:
	case kSegOpExperimental1:
	case kSegOpExperimental2:
	case kSegOpExperimental3:
	case kSegOpExperimental4:
		subLen = Get32LE(fCurPtr+1);	// assumes fNumLen==4
		WMSG2("  OMF found 'reserved' len=%lu (remLen=%ld)\n", subLen, remLen);
		if (subLen > (unsigned long) remLen)
			return nil;
		len += subLen + fNumLen;
		break;
	default:
		assert(len == 1);
		if (*fCurPtr >= kSegOpCONSTStart && *fCurPtr <= kSegOpCONSTEnd)
			len += *fCurPtr;
		break;
	}

	fCurPtr += len;

	return prevPtr;
}

/*
 * Determine the length of an OMF expression.
 *
 * Pass a pointer to the start of the expression.
 */
int
OMFSegment::ExpressionLength(const unsigned char* ptr)
{
	// do this someday
	return 1;
}
#endif
