/*
 * CiderPress
 * Copyright (C) 2007 by faddenSoft, LLC.  All Rights Reserved.
 * See the file LICENSE for distribution terms.
 */
/*
 * Reformat assembly-language source code.
 */
#ifndef __LR_ASM__
#define __LR_ASM__

#include "ReformatBase.h"

/*
 * Base class for assembly source conversion.
 *
 * We want to tab to certain offsets, either hard coded or found in the source
 * file.  To make this work right we want to buffer each line of output.  The
 * maximum line length is bounded by the editor.
 */
class ReformatAsm : public ReformatText {
public:
	ReformatAsm(void) {}
	virtual ~ReformatAsm(void) {}

	void OutputStart(void) {
        fOutBufIndex = 0;
        memset(fOutBuf, ' ', kMaxLineLen);
	}
	void OutputFinish(void) {
        Output('\0');
	}

	void Output(char ch) {
		if (fOutBufIndex < kMaxLineLen)
			fOutBuf[fOutBufIndex++] = ch;
	}
	void Output(const char* str) {
		int len = strlen(str);
		if (fOutBufIndex + len > kMaxLineLen)
			len = kMaxLineLen - fOutBufIndex;
		if (len > 0) {
			memcpy(fOutBuf + fOutBufIndex, str, len);
			fOutBufIndex += len;
		}
	}
	void OutputTab(int posn) {
		if (posn < kMaxLineLen && posn > fOutBufIndex)
			fOutBufIndex = posn;
		else
			Output(' ');        // always at least one
	}

	const char* GetOutBuf(void) const { return fOutBuf; }

private:
	enum {
		kMaxLineLen = 128,
	};

	char	fOutBuf[kMaxLineLen+1];
	int		fOutBufIndex;
};


/*
 * Reformat an S-C Assembler listing into readable text.
 */
class ReformatSCAssem : public ReformatText {
public:
	ReformatSCAssem(void) {}
	virtual ~ReformatSCAssem(void) {}

	virtual void Examine(ReformatHolder* pHolder);
	virtual int Process(const ReformatHolder* pHolder,
		ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
		ReformatOutput* pOutput);

	/* this gets called by Integer BASIC converter */
	static bool IsSCAssem(const ReformatHolder* pHolder);
};


/*
 * Reformat Merlin listing to have tabs.
 */
class ReformatMerlin : public ReformatAsm {
public:
	ReformatMerlin(void) {}
	virtual ~ReformatMerlin(void) {}

	virtual void Examine(ReformatHolder* pHolder);
	virtual int Process(const ReformatHolder* pHolder,
		ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
		ReformatOutput* pOutput);

private:
	static bool IsMerlin(const ReformatHolder* pHolder);
};


/*
 * Reformat LISA v2 (DOS) sources.
 */
class ReformatLISA2 : public ReformatAsm {
public:
	ReformatLISA2(void) {}
	virtual ~ReformatLISA2(void) {}

	virtual void Examine(ReformatHolder* pHolder);
	virtual int Process(const ReformatHolder* pHolder,
		ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
		ReformatOutput* pOutput);

private:
	bool IsLISA(const ReformatHolder* pHolder);

	enum {
		kOpTab = 9,
		kAdTab = 13,
		kComTab = 39,
	};

	void ProcessLine(const unsigned char* buf);
};

/*
 * Reformat LISA v3 (ProDOS) sources.
 */
class ReformatLISA3 : public ReformatAsm {
public:
	ReformatLISA3(void) : fSymTab(nil) {}
	virtual ~ReformatLISA3(void) {}

	virtual void Examine(ReformatHolder* pHolder);
	virtual int Process(const ReformatHolder* pHolder,
		ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
		ReformatOutput* pOutput);

	static bool IsLISA(const ReformatHolder* pHolder);

private:
	typedef enum OperandResult {
		kResultUnknown = 0,
		kResultFailed,
		kResultGotoOutOprtr,
		kResultGotoOutOprnd,
	} OperandResult;

	void ProcessLine(const unsigned char* codePtr, int len);
	void ConvertOperand(unsigned char mnemonic, const unsigned char** pCodePtr,
		int* pLen);
	OperandResult PrintNum(int adrsMode, unsigned char val,
		const unsigned char** pCodePtr, int* pLen);
	OperandResult PrintComplexOperand(unsigned char opr,
		const unsigned char** pCodePtr, int* pLen);
	void PrintSymEntry(int ent);
	void PrintMnemonic(unsigned char val);
	void PrintBin(unsigned char val);
	void PrintComment(int adrsMode, const unsigned char* ptr, int len);

	enum {
		kHeaderLen = 4,
		kOpTab = 9,
		kAdTab = 18,
		kComTab = 40,
	};
	/* token constants, from LISA.MNEMONICS */
	enum {
		kLCLTKN = 0xF0,
		kLBLTKN = 0xFA,
		// 0xFB used to indicate "second half" of symbol table
		kMACTKN = 0xFC,
		// 0xFD used to indicate "second half" of symbol table
		kCMNTTKN = 0xFE,
		kSN = 0x00,
		kSS = 0xB0,

		kGROUP1 = 0x00,
		kGROUP2 = 0x0b,
		kGROUP3 = 0x11,
		kGROUP4 = 0x24,
		kGROUP5 = 0x29,
		kGROUP6 = 0x34,
		kGROUP7 = 0x39,
		kGROUP8 = 0x3f,
		kGROUP9 = 0x4a,
	};

	const unsigned char*	fSymTab;
	unsigned short	fSymCount;

};

/*
 * Reformat LISA v4/v5 (ProDOS and GS/OS) sources.
 */
class ReformatLISA4 : public ReformatAsm {
public:
	ReformatLISA4(void) : fSymTab(nil) {}
	virtual ~ReformatLISA4(void) { delete[] fSymTab; }

	virtual void Examine(ReformatHolder* pHolder);
	virtual int Process(const ReformatHolder* pHolder,
		ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part,
		ReformatOutput* pOutput);

	static bool IsLISA(const ReformatHolder* pHolder);

private:
	enum {
		kHeaderLen = 16,
	};

	/* token constants, from MNEMONICS.A */
	enum {
		kBigndec4_tkn = 0x00,
		kBignhex4_tkn = 0x01,
		kBignbin4_tkn = 0x02,
		kBignhexs_tkn = 0x03,
		kBignstring_tkn = 0x04,
		kDec1_tkn = 0x1a,
		kDec2_tkn = 0x1b,
		kHex1_tkn = 0x1c,
		kHex2_tkn = 0x1d,
		kBin1_tkn = 0x1e,
		kBin2_tkn = 0x1f,
		kBign_tkn = 0x33,
		kDec3_tkn = 0x34,
		kHex3_tkn = 0x35,
		kBin3_tkn = 0x36,
		kcABS_tkn = 0x37,
		kcLONG_tkn = 0x38,
		kMVN_tkn = 0x41,
		kMVP_tkn = 0x42,
		kMacE_tkn = 0x5e,
		kStr31_tkn = 0x7f,

		kSS = 0x90,

		//kGROUP1_tkns = 0x01,
		//kGROUP2_tkns = 0x0c,
		kGROUP3_tkns = 0x12,
		//kGROUP4_tkns = 0x29,
		kGROUP5_tkns = 0x30,
		kGROUP6_tkns = 0x43,
		//kGROUP7_tkns = 0x4a,
		//kGROUP8_tkns = 0x51,
		//kGROUP9_tkns = 0x5f,

		kLocalTKN = 0xf0,
		kLabelTKN = 0xfa,
		kMacroTKN = 0xfc,
		kErrlnTKN = 0xfd,       // "is BlankTKN by itself"
		kBlankTKN = 0xfd,
		kCommentTKN = 0xfe,
		kComntStarTKN = 0xfe,
		kComntSemiTKN = 0xff,
	};

	typedef enum OperandResult {
		kResultUnknown = 0,
		kResultFailed,
		kResultGotoOutOprtr,
		kResultGotoOutOprnd,
	} OperandResult;

	void ProcessLine(const unsigned char* codePtr, int len);
	void ConvertOperand(unsigned char mnemonic, const unsigned char** pCodePtr,
		int* pLen);
	void PrintSymEntry(int ent);
	OperandResult PrintNum(unsigned char opr, const unsigned char** pCodePtr,
		int* pLen);
	OperandResult PrintComplexOperand(unsigned char opr,
		const unsigned char** pCodePtr, int* pLen);
	void PrintDec(int count, const unsigned char** pCodePtr,
		int* pLen);
	void PrintHex(int count, const unsigned char** pCodePtr,
		int* pLen);
	void PrintBin(int count, const unsigned char** pCodePtr,
		int* pLen);

	const unsigned char**	fSymTab;
	unsigned short	fSymCount;

	unsigned char fOpTab;
	unsigned char fAdTab;
	unsigned char fComTab;
	unsigned char fCpuType;
};

#endif /*__LR_ASM__*/
