/*****************************************************************************
* CINEDISM.C																					  *
*																									  *
* Cinematronics Disassembler.																  *
*																									  *
* This program is used to generate a disassembled listing of a program		  *
* written for the Cinematronics CPU cards.											  *
*																									  *
* Author:  Zonn Moore																		  *
* Version: 1.1																					  *
* Date:    01/31/97																			  *
*																									  *
* Revision History:																			  *
* -----------------																			  *
*																									  *
* Version 1.0, 01/31/97																		  *
*    Initial release.																		  *
*																									  *
* Version 1.1, 07/31/97																		  *
*    Removed '[j]' from JMP instructructions.										  *
*																									  *
* Released to public domain by the author, distribute freely.					  *
*****************************************************************************/
#include	<stdio.h>
#include        <stdlib.h>
#include        <string.h>
#include        <ctype.h>
#include        <unistd.h>
#include        <fcntl.h>
#include        <sys/types.h>

/* Some DOS emulation procedures since this code didn't come from Unix */

#define O_BINARY 0

#define stricmp(a,b) strcasecmp(a,b)

void strlwr(char *s)
{
  int c;
  if (s == NULL) return;
  for (;;) {
    c = *s++;
    if (c == '\0') break;
    if (isalpha(c) && isupper(c)) {
      c = tolower(c);
    }
  }
}

off_t filelength(int fd)
{
  off_t curpos = lseek(fd, 0, SEEK_CUR);
  off_t l = lseek(fd, 0, SEEK_END);
  lseek(fd, curpos, SEEK_SET);
  return(l);
}
/*  Define some common data types */

/*typedef unsigned int		uint;*/
typedef unsigned char	uchar;

#define	TAB_OPCODE	23						/*  Column to place opcode */
#define	TAB_PARAM	(TAB_OPCODE+8)		/*  Column to place parameters */

/*  Default filname extensions */

#define	EXT1		".t7"
#define	EXT2		".p7"
#define	EXT3		".u7"
#define	EXT4		".r7"

/*****************************************************************************
* Setup a table of opcode indexes.  These are used to index						  *
* the 'OpCodeName[]' array.																  *
*****************************************************************************/
enum OPCODE
{	CLR,  LDA,  INP,  ADD,  SUB,  LDJ,	LDP,  OUT,
	CMP,  LDI,  STA,  VIN,  VDR,	XLT,  MUL,  LLT,
	WAI,  AWD,  AND,  LSR,  LSL,	ASR,  ASRD, LSLD,

	JPPB, JMIB, JDRB, JLTB, JEQB, JNCB, JA0B, NOPB,
	JMPA, JMIA, JDRA, JLTA, JEQA, JNCA, JA0A, NOPA,
	JEIA, JEIB
};

/*****************************************************************************
* A table of opcode names.																	  *
*****************************************************************************/
char *OpcodeName[] =
{
	"CLR",  "LDA",  "INP",  "ADD",  "SUB",  "LDJ",  "LDP",  "OUT",
	"CMP",  "LDI",  "STA",  "VIN",  "VDR",	 "XLT",  "MUL",  "LLT",
	"WAI",  "AWD",  "AND",  "LSR",  "LSL",	 "ASR",  "ASRD", "LSLD",

	"JPPB", "JMIB", "JDRB", "JLTB", "JEQB", "JNCB", "JA0B", "USB",
	"JMP",  "JMI",  "JDR",  "JLT",  "JEQ",  "JNC",  "JA0",  "NOP",
	"JEI",  "JEIB"
};

/*****************************************************************************
* Addressing types.  One address type exist for every possible way a			  *
* parameter needs to be displayed.  Along with a couple of extended			  *
* types used to make a disassembly more readable.									  *
*****************************************************************************/
enum ADTYPE
{	ACC,		/*  Accumulator */
	ADIR,		/*  Acc Direct memory access */
	AIM4,		/*  Acc 4 bit immediate */
	AIM4X,	/*  Acc 4 bit immediate extended size */
	AIM8,		/*  Acc 8 bit immediate */
	AINDM,	/*  Acc indirect through memory */
	AIMX,		/*  Acc immediate extended A-reg */
	AXLT,		/*  Acc lookup ROM using Acc as pointer */
	AIRG,		/*  Acc Through the I-reg */
	IRG,		/*  Through the I-reg */
	IM4,		/*  4 bit immediate */
	IM12,		/*  12 bit immediate */
	DIR,		/*  Direct memory access */
	IMP,		/*  Implied */
	JUMP,		/*  Acc selection/Jump instruction */
	JUMPX		/*  Acc selection/Extended jump instruction */
};

/*****************************************************************************
* Define one member of the opcode lookup table.										  *
*****************************************************************************/
struct DECODE_S
{	enum OPCODE	name;		/*  name of opcode */
	enum ADTYPE	mode;		/*  addressing mode of opcode */
};

/*****************************************************************************
* Table of opcodes and their addressing modes.										  *
*****************************************************************************/
struct DECODE_S DecodeTbl[] =
{
	CLR, ACC,			/*  00 */
	LDA, AIM4X,			/*  01 */
	LDA, AIM4X,			/*  02 */
	LDA, AIM4X,			/*  03 */
	LDA, AIM4X,			/*  04 */
	LDA, AIM4X,			/*  05 */
	LDA, AIM4X,			/*  06 */
	LDA, AIM4X,			/*  07 */
	LDA, AIM4X,			/*  08 */
	LDA, AIM4X,			/*  09 */
	LDA, AIM4X,			/*  0A */
	LDA, AIM4X,			/*  0B */
	LDA, AIM4X,			/*  0C */
	LDA, AIM4X,			/*  0D */
	LDA, AIM4X,			/*  0E */
	LDA, AIM4X,			/*  0F */

	INP, ADIR, 			/*  10 */
	INP, ADIR, 			/*  11 */
	INP, ADIR, 			/*  12 */
	INP, ADIR, 			/*  13 */
	INP, ADIR, 			/*  14 */
	INP, ADIR, 			/*  15 */
	INP, ADIR, 			/*  16 */
	INP, ADIR, 			/*  17 */
	INP, ADIR, 			/*  18 */
	INP, ADIR, 			/*  19 */
	INP, ADIR, 			/*  1A */
	INP, ADIR, 			/*  1B */
	INP, ADIR, 			/*  1C */
	INP, ADIR, 			/*  1D */
	INP, ADIR, 			/*  1E */
	INP, ADIR, 			/*  1F */

	ADD, AIM8, 			/*  20 */
	ADD, AIM4, 			/*  21 */
	ADD, AIM4, 			/*  22 */
	ADD, AIM4, 			/*  23 */
	ADD, AIM4, 			/*  24 */
	ADD, AIM4, 			/*  25 */
	ADD, AIM4, 			/*  26 */
	ADD, AIM4, 			/*  27 */
	ADD, AIM4, 			/*  28 */
	ADD, AIM4, 			/*  29 */
	ADD, AIM4, 			/*  2A */
	ADD, AIM4, 			/*  2B */
	ADD, AIM4, 			/*  2C */
	ADD, AIM4, 			/*  2D */
	ADD, AIM4, 			/*  2E */
	ADD, AIM4, 			/*  2F */
	
	SUB, AIM8, 			/*  30 */
	SUB, AIM4, 			/*  31 */
	SUB, AIM4, 			/*  32 */
	SUB, AIM4, 			/*  33 */
	SUB, AIM4, 			/*  34 */
	SUB, AIM4, 			/*  35 */
	SUB, AIM4, 			/*  36 */
	SUB, AIM4, 			/*  37 */
	SUB, AIM4, 			/*  38 */
	SUB, AIM4, 			/*  39 */
	SUB, AIM4, 			/*  3A */
	SUB, AIM4, 			/*  3B */
	SUB, AIM4, 			/*  3C */
	SUB, AIM4, 			/*  3D */
	SUB, AIM4, 			/*  3E */
	SUB, AIM4, 			/*  3F */

	LDJ, IM12, 			/*  40 */
	LDJ, IM12, 			/*  41 */
	LDJ, IM12, 			/*  42 */
	LDJ, IM12, 			/*  43 */
	LDJ, IM12, 			/*  44 */
	LDJ, IM12, 			/*  45 */
	LDJ, IM12, 			/*  46 */
	LDJ, IM12, 			/*  47 */
	LDJ, IM12, 			/*  48 */
	LDJ, IM12, 			/*  49 */
	LDJ, IM12, 			/*  4A */
	LDJ, IM12, 			/*  4B */
	LDJ, IM12, 			/*  4C */
	LDJ, IM12, 			/*  4D */
	LDJ, IM12, 			/*  4E */
	LDJ, IM12, 			/*  4F */

	JPPB, JUMP,			/*  50 */
	JMIB, JUMP,			/*  51 */
	JDRB, JUMP,			/*  52 */
	JLTB, JUMP,			/*  53 */
	JEQB, JUMP,			/*  54 */
	JNCB, JUMP,			/*  55 */
	JA0B, JUMP,			/*  56 */
	NOPB, IMP, 			/*  57 */

	JMPA, JUMP,			/*  58 */
	JMIA, JUMP,			/*  59 */
	JDRA, JUMP,			/*  5A */
	JLTA, JUMP,			/*  5B */
	JEQA, JUMP,			/*  5C */
	JNCA, JUMP,			/*  5D */
	JA0A, JUMP,			/*  5E */
	NOPA, IMP, 			/*  5F */

	ADD, ADIR, 			/*  60 */
	ADD, ADIR, 			/*  61 */
	ADD, ADIR, 			/*  62 */
	ADD, ADIR, 			/*  63 */
	ADD, ADIR, 			/*  64 */
	ADD, ADIR, 			/*  65 */
	ADD, ADIR, 			/*  66 */
	ADD, ADIR, 			/*  67 */
	ADD, ADIR, 			/*  68 */
	ADD, ADIR, 			/*  69 */
	ADD, ADIR, 			/*  6A */
	ADD, ADIR, 			/*  6B */
	ADD, ADIR, 			/*  6C */
	ADD, ADIR, 			/*  6D */
	ADD, ADIR, 			/*  6E */
	ADD, ADIR, 			/*  6F */

	SUB, ADIR, 			/*  70 */
	SUB, ADIR, 			/*  71 */
	SUB, ADIR, 			/*  72 */
	SUB, ADIR, 			/*  73 */
	SUB, ADIR, 			/*  74 */
	SUB, ADIR, 			/*  75 */
	SUB, ADIR, 			/*  76 */
	SUB, ADIR, 			/*  77 */
	SUB, ADIR, 			/*  78 */
	SUB, ADIR, 			/*  79 */
	SUB, ADIR, 			/*  7A */
	SUB, ADIR, 			/*  7B */
	SUB, ADIR, 			/*  7C */
	SUB, ADIR, 			/*  7D */
	SUB, ADIR, 			/*  7E */
	SUB, ADIR, 			/*  7F */

	LDP, IM4,			/*  80 */
	LDP, IM4,			/*  81 */
	LDP, IM4,			/*  82 */
	LDP, IM4,			/*  83 */
	LDP, IM4,			/*  84 */
	LDP, IM4,			/*  85 */
	LDP, IM4,			/*  86 */
	LDP, IM4,			/*  87 */
	LDP, IM4,			/*  88 */
	LDP, IM4,			/*  89 */
	LDP, IM4,			/*  8A */
	LDP, IM4,			/*  8B */
	LDP, IM4,			/*  8C */
	LDP, IM4,			/*  8D */
	LDP, IM4,			/*  8E */
	LDP, IM4,			/*  8F */

	OUT, ADIR,			/*  90 */
	OUT, ADIR,			/*  91 */
	OUT, ADIR,			/*  92 */
	OUT, ADIR,			/*  93 */
	OUT, ADIR,			/*  94 */
	OUT, ADIR,			/*  95 */
	OUT, ADIR,			/*  96 */
	OUT, ADIR,			/*  97 */
	OUT, ADIR,			/*  98 */
	OUT, ADIR,			/*  99 */
	OUT, ADIR,			/*  9A */
	OUT, ADIR,			/*  9B */
	OUT, ADIR,			/*  9C */
	OUT, ADIR,			/*  9D */
	OUT, ADIR,			/*  9E */
	OUT, ADIR,			/*  9F */

	LDA, ADIR,			/*  A0 */
	LDA, ADIR,			/*  A1 */
	LDA, ADIR,			/*  A2 */
	LDA, ADIR,			/*  A3 */
	LDA, ADIR,			/*  A4 */
	LDA, ADIR,			/*  A5 */
	LDA, ADIR,			/*  A6 */
	LDA, ADIR,			/*  A7 */
	LDA, ADIR,			/*  A8 */
	LDA, ADIR,			/*  A9 */
	LDA, ADIR,			/*  AA */
	LDA, ADIR,			/*  AB */
	LDA, ADIR,			/*  AC */
	LDA, ADIR,			/*  AD */
	LDA, ADIR,			/*  AE */
	LDA, ADIR,			/*  AF */

	CMP, ADIR,			/*  B0 */
	CMP, ADIR,			/*  B1 */
	CMP, ADIR,			/*  B2 */
	CMP, ADIR,			/*  B3 */
	CMP, ADIR,			/*  B4 */
	CMP, ADIR,			/*  B5 */
	CMP, ADIR,			/*  B6 */
	CMP, ADIR,			/*  B7 */
	CMP, ADIR,			/*  B8 */
	CMP, ADIR,			/*  B9 */
	CMP, ADIR,			/*  BA */
	CMP, ADIR,			/*  BB */
	CMP, ADIR,			/*  BC */
	CMP, ADIR,			/*  BD */
	CMP, ADIR,			/*  BE */
	CMP, ADIR,			/*  BF */

	LDI, DIR,			/*  C0 */
	LDI, DIR,			/*  C1 */
	LDI, DIR,			/*  C2 */
	LDI, DIR,			/*  C3 */
	LDI, DIR,			/*  C4 */
	LDI, DIR,			/*  C5 */
	LDI, DIR,			/*  C6 */
	LDI, DIR,			/*  C7 */
	LDI, DIR,			/*  C8 */
	LDI, DIR,			/*  C9 */
	LDI, DIR,			/*  CA */
	LDI, DIR,			/*  CB */
	LDI, DIR,			/*  CC */
	LDI, DIR,			/*  CD */
	LDI, DIR,			/*  CE */
	LDI, DIR,			/*  CF */

	STA, ADIR,			/*  D0 */
	STA, ADIR,			/*  D1 */
	STA, ADIR,			/*  D2 */
	STA, ADIR,			/*  D3 */
	STA, ADIR,			/*  D4 */
	STA, ADIR,			/*  D5 */
	STA, ADIR,			/*  D6 */
	STA, ADIR,			/*  D7 */
	STA, ADIR,			/*  D8 */
	STA, ADIR,			/*  D9 */
	STA, ADIR,			/*  DA */
	STA, ADIR,			/*  DB */
	STA, ADIR,			/*  DC */
	STA, ADIR,			/*  DD */
	STA, ADIR,			/*  DE */
	STA, ADIR,			/*  DF */

	VDR, IMP,			/*  E0 */
	LDJ, IRG,			/*  E1 */
	XLT, AXLT,			/*  E2 */
	MUL, IRG,			/*  E3 */
	LLT, IMP,			/*  E4 */
	WAI, IMP,			/*  E5 */
	STA, AIRG,			/*  E6 */
	ADD, AIRG,			/*  E7 */
	SUB, AIRG,			/*  E8 */
	AND, AIRG,			/*  E9 */
	LDA, AIRG,			/*  EA */
	LSR, ACC,			/*  EB */
	LSL, ACC,			/*  EC */
	ASR, ACC,			/*  ED */
	ASRD, IMP,			/*  EE */
	LSLD, IMP,			/*  EF */

	VIN, IMP,			/*  F0 */
	LDJ, IRG,			/*  F1 */
	XLT, AXLT,			/*  F2 */
	MUL, IRG,			/*  F3 */
	LLT, IMP,			/*  F4 */
	WAI, IMP,			/*  F5 */
	STA, AIRG,			/*  F6 */
	AWD, AIRG,			/*  F7 */
	SUB, AIRG,			/*  F8 */
	AND, AIRG,			/*  F9 */
	LDA, AIRG,			/*  FA */
	LSR, ACC,			/*  FB */
	LSL, ACC,			/*  FC */
	ASR, ACC,			/*  FD */
	ASRD, IMP,			/*  FE */
	LSLD, IMP			/*  FF */
};

uchar		*ObjCode;	/*  Object code buffer for full object code */
char		OpcBfr[81];	/*  Temp buffer used to build opcodes */
char		DspBfr[81];	/*  Line display buffer */
uchar		Extd_f;		/*  Extended diss flag, if set display extended insts. */
uchar		Pause_f;		/*  Pause after each instruction flag */

/*****************************************************************************
* Print the opcode name to the 'dest' buffer.										  *
*																									  *
* Called with:																					  *
*    dest    = Pointer to buffer to receive opcode's name.						  *
*    opc     = Opcode's binary value.													  *
*    mode    = Addressing mode used by opcode.										  *
*    useBreg = Flag indicating Acc instruction should use B-reg				  *
*****************************************************************************/
void printOpc( char *dest, uint opc, uint mode, uint useBreg)
{
	int	ii;

	strcpy( OpcBfr, OpcodeName[opc]);

	/*  If extended instructions being printed, use the 'useBreg' */
	/*  to determine Acc used by any Acc instruction. */

	if (Extd_f)
	{
		/*  if Accumulator accessed, print it */

		if (mode <= AIRG)
		{
			if (useBreg)
				OpcBfr[3] = 'B';		/*  B register accessed */

			else
				OpcBfr[3] = 'A';		/*  A register accessed */

			OpcBfr[4] = '\0';			/*  terminate string */
		}
	}

	strlwr( OpcBfr);		/*  lower case string (just my preference) */

	/*  move into destination buffer without trailing '\0' */

	for (ii = 0; OpcBfr[ii] != '\0'; ii++)
		dest[ii] = OpcBfr[ii];
}

/*****************************************************************************
* Print parameters, based on the addressing mode of the instruction			  *
* to the 'dest' buffer.																		  *
*																									  *
* Called with:																					  *
*    dest    = Destination buffer of parameters.									  *
*    mode    = Addressing mode of opcode.												  *
*    adr     = Address of opcode (index into 'ObjCode[]').						  *
*    useBreg = Flag indicating Acc instruction should use B-reg				  *
*																									  *
* Returns:																						  *
*    Length of object code associated with opcode.									  *
*****************************************************************************/
uint printMode( char *dest, uint mode, uint adr, uint useBreg)
{
	uint	ii, jj, size;

	size = useBreg + 1;		/*  initial size of object code */
	adr += useBreg;			/*  if using B-reg skip 'USB' instruction */
	
	switch (mode)
	{
		/*  display 4 bit immediate value */

	case AIM4:
	case IM4:
		sprintf( OpcBfr, "#$%X", ObjCode[adr] & 0x0F);
		break;

		/*  display 4 bit extended immediate value */

	case AIM4X:	
		sprintf( OpcBfr, "#$%X", (ObjCode[adr] & 0x0F) << 8);
		break;

		/*  display 8 bit immediate value */

	case AIM8:
		sprintf( OpcBfr, "#$%02X", ObjCode[adr+1]);
		size++;		/*  add 8 bit immediate value to object code size */
		break;

		/*  display the '[i]', indicating a indirect through 'I' instruction */

	case AIRG:
	case IRG:
		strcpy( OpcBfr, "[i]");
		break;

		/*  *** display special 'extended' 12 bit immediate value */

		/*  This is done by reading two consective acc load / add / sub */
		/*  instructions and calculating what ends up in the Acc */

	case AIMX:
		ii = (ObjCode[adr] & 0x0F) << 8;	/*  get 4 bit extended immediate */
		adr += useBreg + 1;					/*  if point to next opcode, skip 'USB's */
		size += useBreg + 1;					/*  add to object code size, include 'USB's */
		jj = ObjCode[adr] & 0x0F;			/*  get 4 bit immediate value */

		/*  check if the next opcode is an 'ADD' instruction (0x20-0x2F) */

		if ((ObjCode[adr] & 0xF0) == 0x20)
		{
			/*  if 4 bit immediate value is 0, use 8 bit immediate value	 */

			if (jj == 0)
			{	ii += ObjCode[adr+1];	/*  add 8 bit immediate value */
				size++;						/*  add to oject code size */
			}
			else
				ii += jj;					/*  else, just add 4 bit immediate value */
		}

		/*  if not followed by an 'ADD' instruction, then must be a 'SUB' */

		else
		{
			/*  if 4 bit immediate value is 0, use 8 bit immediate value	 */

			if (jj == 0)
			{	ii -= ObjCode[adr+1];	/*  sub 8 bit immediate value */
				size++;						/*  sub to oject code size */
			}
			else
				ii -= jj;					/*  else, just sub 4 bit immediate value */
		}
		ii &= 0xFFF;							/*  remove any overflow */
		sprintf( OpcBfr, "#$%03X", ii);	/*  print to temperary buffer */
		break;

		/*  display no parameters */

	case ACC:
	case AXLT:
	case IMP:
		OpcBfr[0] = '\0';					/*  no parameters */
		break;

		/*  display 12 bit immediate value */

	case IM12:
		sprintf( OpcBfr, "#$%03X",
			(ObjCode[adr] & 0x0F) +
			(ObjCode[adr+1] & 0xF0) +
			((ObjCode[adr+1] & 0x0F) << 8)
		);
		size++;
		break;

		/*  display address of direct addressing mode */

	case ADIR:
	case DIR:
		sprintf( OpcBfr, "$%X", ObjCode[adr] & 0x0F);
		break;

		/*  indicate a jump through the 'J' register */

	case JUMP:
/* 		strcpy( OpcBfr, "[j]"); */
		OpcBfr[0] = '\0';					/*  no parameters */
		break;

		/*  *** special extended jump */
		/*  This address mode can be used to display a 'LDJ #nnn' instruction */
		/*  followed immediatly be a 'JMP' instruction. */

	case JUMPX:
		size += 2;		/*  add immediate value and 'JMP' inst to object size */

		/*  use 12 bit value of 'LDJ' instruction as parameter */

		sprintf( OpcBfr, "$%03X",
			(ObjCode[adr] & 0x0F) +
			(ObjCode[adr+1] & 0xF0) +
			((ObjCode[adr+1] & 0x0F) << 8)
		);
		break;
	}

	/*  move into destination buffer without trailing '\0' */

	for (ii = 0; OpcBfr[ii] != '\0'; ii++)
		dest[ii] = OpcBfr[ii];

	return (size);			/*  return size of opcode */
}

/*****************************************************************************
* Print the object code in hexidecimal.												  *
*																									  *
* Called with:																					  *
*    dest = Destination buffer used to receive data.								  *
*    adr  = Address of object code (index into 'ObjCode[]').					  *
*    size = Size of object code.															  *
*****************************************************************************/
void printObj( char *dest, uint adr, uint size)
{
	uint	ii;

	sprintf( OpcBfr, "%04X:", adr);		/*  print address */

	/*  print however many bytes contained in oject code */

	for (ii = 0; ii < size; ii++)
		sprintf( OpcBfr+((ii * 3) + 5), " %02X", ObjCode[adr+ii]);

	/*  move into destination buffer without trailing '\0' */

	for (ii = 0; OpcBfr[ii] != '\0'; ii++)
		dest[ii] = OpcBfr[ii];
}

/*****************************************************************************
* Fully disassemble one opcode.															  *
*																									  *
* Called with:																					  *
*    dest    = Destination buffer to receive ASCII data.							  *
*    adr     = Address of opcode (index into ObjCode[]).							  *
*    objSize = Pointer to (uint) to receive object code size.					  *
*    brkFlg  = Pointer to a flag indicating a break in instruction flow.	  *
*****************************************************************************/
void dissOpcode( char *dest, uint adr, uint *objSize, uint *brkFlg)
{
	uchar	opCode, nextOpc;
	uint	useBreg;

	opCode = ObjCode[adr];					/*  get opcode */

	/* check for break in code flow */

	if (DecodeTbl[opCode].name == JMPA || DecodeTbl[opCode].name == JPPB)
		*brkFlg = 1;							/*  indicate code break */

	else
		*brkFlg = 0;							/*  indicate no code break */

	/*  Check to see if extended instructions are being disassembled. */

	if (Extd_f)
	{

		/*  start by check for special prefixes */

		/*  *** Use B-reg? */

		if (DecodeTbl[opCode].name == NOPB)
		{
			nextOpc = ObjCode[adr+1];

			/*  does next opcode use Acc addressing? */

			if (DecodeTbl[nextOpc].mode > AIRG)
			{
				/*  No, disassemble opcode NOPB */

				printOpc( dest+TAB_OPCODE, DecodeTbl[opCode].name, DecodeTbl[opCode].mode, 0);
				*objSize = printMode( dest+TAB_PARAM, DecodeTbl[opCode].mode, adr, 0);
				printObj( dest, adr, *objSize);
				return;
			}
			else
			{	useBreg = 1;				/*  set to B-reg */
				opCode = nextOpc;			/*  get next opcode */
			}
		}
		else
			useBreg = 0;

		/*  check for extended LDA instructions */

		if ((opCode & 0xF0) == 0x00)
		{
			if (useBreg == 0 || (useBreg == 1 && ObjCode[adr+2] == 0x57))
			{
				nextOpc = ObjCode[adr+useBreg+useBreg+1];

				if ((nextOpc & 0xE0) == 0x20)
				{
					/*  disasemble extended LDA instruction */

					printOpc( dest+TAB_OPCODE, LDA, AIMX, useBreg);
					*objSize = printMode( dest+TAB_PARAM, AIMX, adr, useBreg);
					printObj( dest, adr, *objSize);
					return;
				}
			}
		}

		/*  check for extended JUMP instructions */

		if (DecodeTbl[opCode].name == LDJ && opCode != 0xE1)
		{
			nextOpc = ObjCode[adr+2];

			if (DecodeTbl[nextOpc].mode == JUMP)
			{
				/*  Disassemble extended JUMP */

				if (DecodeTbl[nextOpc].name == JMPA ||
						DecodeTbl[nextOpc].name == JPPB)
					*brkFlg = 1;		/*  set break in instruction flow flag */

				printOpc( dest+TAB_OPCODE, DecodeTbl[nextOpc].name, DecodeTbl[nextOpc].mode, useBreg);
				*objSize = printMode( dest+TAB_PARAM, JUMPX, adr, useBreg);
				printObj( dest, adr, *objSize);
				return;
			}
		}
	}
	else
		useBreg = 0;		/*  no extended instructions allowed */

	/*  Disassemble the opcode */

	printOpc( dest+TAB_OPCODE, DecodeTbl[opCode].name, DecodeTbl[opCode].mode, useBreg);
	*objSize = printMode( dest+TAB_PARAM, DecodeTbl[opCode].mode, adr, useBreg);
	printObj( dest, adr, *objSize);
	return;
}

void main( int argc, char *argv[])
{
	int			inFile, err, ii;
	static char	fileName[131];
	int			nameLen, romSize, startAdr, endAdr, romidx, objidx;
	long			tempSize;
	uint			objSize, brk_f;
	uchar			*romBfr;

	fputs( "\nCINEDISM - Cinematronics Game ROM Disassembler.", stderr);
	fputs( "\nVersion 1.1   Written by Zonn Moore.\n", stderr);
	fputs( "\nReleased to the public domain by the author, distribute freely.\n", stderr);

	if (argc < 4)
	{
		fputs( "\nUse: CINEDISM game start end [/x] [/p]\n", stderr);
		fputs( "\nWhere:", stderr);
		fputs( "\n   game  = Base name of game ROMs to disassemble.", stderr);
		fputs( "\n   start = Hexidecimal start address of disassembly.", stderr);
		fputs( "\n   end   = Hexidecimal end address of disassembly.", stderr);
		fputs( "\n   [/x]  = Use an intellegent 'extended' instruction set.", stderr);
		fputs( "\n   [/p]  = Pause after each instruction.\n", stderr);

		fputs( "\nThe base filename of the ROMs is given as 'game'.", stderr);

		fprintf( stderr, "\nThe assumed extensions are \"%s\", \"%s\", \"%s\", and \"%s\".\n",
				EXT1, EXT2, EXT3, EXT4);

		exit( 0);
	}

	/*  read in start and end addresses */

	sscanf( argv[2], "%x", &startAdr);
	sscanf( argv[3], "%x", &endAdr);

	if (startAdr > endAdr)
	{	fputs( "\nStart address must be greater than or equal to end address.\n", stderr);
		exit( 0);
	}

	Extd_f = 0;					/*  reset flags */
	Pause_f = 0;

	/*  scan for flags */

	for (ii = 4; ii < argc; ii++)
	{
		if (stricmp( argv[ii], "/x") == 0)
			Extd_f = 1;		/*  set flag true */

		else if (stricmp( argv[ii], "/p") == 0)
			Pause_f = 1;	/*  set flag true */

		/*  else, what the heck is it? */

		else
		{	fprintf( stderr, "\nInvalid parameter: %s\n", argv[ii]);
			exit( 0);
		}
	}

	/*  Read ROMs.  Start by allocating storage for ROMs and Object code */

	romBfr = (uchar *)malloc( 4096);		/*  Rom storage */
	ObjCode = (uchar *)malloc( 32768U);	/*  Object code */

	if (romBfr == 0 || ObjCode == 0)
	{	fputs( "\nNot enough memory!\n", stderr);
		exit( 0);
	}

	/*  get file name and read in ROMs */

	nameLen = strlen( argv[1]);				/*  get length of filename */

	memcpy( fileName, argv[1], nameLen);	/*  get games name */

	/*  open EXT1 (lower, even, ROM) */

	strcpy( fileName+nameLen, EXT1);

	inFile = open( fileName, O_BINARY | O_RDONLY);

	if (inFile == -1)
	{	perror( fileName);
		free( romBfr);
		free( ObjCode);
		exit( 0);
	}

	tempSize = filelength( inFile);		/*  get length of file */

	if (tempSize != 2048 && tempSize != 4096)
	{	fprintf( stderr, "\nFile %s wrong size. (Must be 2048 or 4096 bytes.)\n", fileName);
		free( romBfr);
		free( ObjCode);
		exit( 0);
	}

	romSize = (int)tempSize;

	err = read( inFile, romBfr, romSize);
	
	if (err == -1)
	{	perror( fileName);
		free( romBfr);
		free( ObjCode);
		exit( 0);
	}

	close( inFile);

	/*  move the ROM data into the Object code buffer */

	for (objidx = 0, romidx = 0; romidx < romSize; objidx += 2, romidx++)
		ObjCode[objidx] = romBfr[romidx];

	/*  open EXT2 (lower, odd, ROM) */

	strcpy( fileName+nameLen, EXT2);

	inFile = open( fileName, O_BINARY | O_RDONLY);

	if (inFile == -1)
	{	perror( fileName);
		free( romBfr);
		free( ObjCode);
		exit( 0);
	}

	tempSize = filelength( inFile);		/*  get length of file */

	if (tempSize != romSize)
	{	fprintf( stderr, "\nFile %s does not match previous ROM's size. (Must be %u)\n", fileName, romSize);
		free( romBfr);
		free( ObjCode);
		exit( 0);
	}

	err = read( inFile, romBfr, romSize);
	
	if (err == -1)
	{	perror( fileName);
		free( romBfr);
		free( ObjCode);
		exit( 0);
	}

	close( inFile);

	/*  move the ROM data into the Object code buffer */

	for (objidx = 1, romidx = 0; romidx < romSize; objidx += 2, romidx++)
		ObjCode[objidx] = romBfr[romidx];

	/*  open EXT3 (upper, even, ROM) */

	strcpy( fileName+nameLen, EXT3);

	inFile = open( fileName, O_BINARY | O_RDONLY);

	/*  if file exists, read them in */

	if (inFile != -1)
	{
		tempSize = filelength( inFile);		/*  get length of file */

		if (tempSize != romSize)
		{	fprintf( stderr, "\nFile %s does not match previous ROM's size. (Must be %u)\n", fileName, romSize);
			free( romBfr);
			free( ObjCode);
			exit( 0);
		}

		romSize = (int)tempSize;

		err = read( inFile, romBfr, romSize);
	
		if (err == -1)
		{	perror( fileName);
			free( romBfr);
			free( ObjCode);
			exit( 0);
		}

		close( inFile);

		/*  move the ROM data into the Object code buffer */
		
		for (objidx = romSize * 2, romidx = 0; romidx < romSize; objidx += 2, romidx++)
			ObjCode[objidx] = romBfr[romidx];

		/*  open EXT4 (upper, odd, ROM) */

		strcpy( fileName+nameLen, EXT4);

		inFile = open( fileName, O_BINARY | O_RDONLY);

		if (inFile == -1)
		{	perror( fileName);
			free( romBfr);
			free( ObjCode);
			exit( 0);
		}

		tempSize = filelength( inFile);		/*  get length of file */

		if (tempSize != romSize)
		{	fprintf( stderr, "\nFile %s does not match previous ROM's size. (Must be %u)\n", fileName, romSize);
			free( romBfr);
			free( ObjCode);
			exit( 0);
		}

		err = read( inFile, romBfr, romSize);
	
		if (err == -1)
		{	perror( fileName);
			free( romBfr);
			free( ObjCode);
			exit( 0);
		}

		close( inFile);

		/*  move the ROM data into the Object code buffer */

		for (objidx = (romSize * 2) + 1, romidx = 0; romidx < romSize; objidx += 2, romidx++)
			ObjCode[objidx] = romBfr[romidx];
	}
	free( romBfr);

	while (startAdr <= endAdr)
	{	memset( DspBfr, ' ', 80);					/*  set to blanks */
		DspBfr[80] = '\0';
		dissOpcode( DspBfr, startAdr, &objSize, &brk_f);	/*  setup buffer */
		startAdr += objSize;

		for (ii = 79; DspBfr[ii] == ' '; ii--)
			;

		DspBfr[ii+1] = '\0';											/*  terminate line */
		printf( "\n%s", DspBfr);									/*  print line */

		if (Pause_f)
		{
			ii = getchar();												/*  just wait */

			if (ii == '\x1B')
				exit( 0);
		}
		if (brk_f)
			putchar( '\n');											/*  print break */
	}
	fputc( '\n', stderr);		/*  make look nice on screen */
	free( ObjCode);
}

