/* Program for helping to label the results of the diassembly */

#include <stdio.h>
#include <ctype.h>

typedef struct instruction_def
{
	int address;
	int byte[3];
	int numBytes;
	char instruction[5];
	char *opcode1;
	char *opcode2;
	int  opval1;
	int  opval2;
} INSTRUCTION_DEF;

#define MAX_IDEFS 2000
#define MEM_SIZE  0x10000

struct labelDef {
	char type;
	int value; };

struct labelDef memMap [MEM_SIZE];

INSTRUCTION_DEF idefs [MAX_IDEFS];
int num_idefs;

char ibuf[256];
char *strvec [32];

#define SCAN_OK		 1
#define SCAN_SKIP	 0
#define END_OF_INPUT	-1
#define BAD_SYNTAX	-2
#define BAD_ADDRESS	-3

#define BCC	0x90
#define BCS	0xb0
#define BEQ	0xf0
#define BMI	0x30
#define BNE     0xd0
#define BPL	0x10
#define BVC	0x50
#define BVS	0x70

#define JMP_ABS 0x4c

#define JSR     0x20

#define YES 1
#define NO  0
#define AND &&
#define OR  ||

static labelNum = 0;
static subNum = 0;

char *strcpy();
char *strlen();
char *malloc();

static char *copystr(s)
char *s;
{
	return ( strcpy((char*)malloc(strlen(s)+1),s) );
}

main()
{
	int i, status;
	int line = 0;
	num_idefs = 0;

	/* Initialize memMap */

	for (i=0; i<MEM_SIZE; i++)
	 { memMap[i].type = ' ';
	 } 

	while (num_idefs < MAX_IDEFS)
	 { INSTRUCTION_DEF *ip;

	   ip = &idefs[num_idefs];
	   status = unpack (ip, ibuf);
	   if (status == END_OF_INPUT)
	    { break;
	    }

	   line++;
	   switch (status)
	    { case SCAN_OK:
		   break;
	      case SCAN_SKIP:
		   printf ("%s", ibuf);
		   break;
	      case BAD_SYNTAX:
		   printf ("ERROR/ mallformed line, line number %d\n", line);
		   exit (-1);
		   break;
	      case BAD_ADDRESS:
		   printf ("ERROR/ illegal address, line %d\n", line);
		   exit (-1);
	    	   break;
	    }

	   /* Now go through and tag all subroutines and brach points */

	   if (status == SCAN_OK)
	    { switch (ip->byte[0])
	       { case BCC:
	         case BCS:
	         case BEQ:
	         case BMI:
	         case BNE:
	         case BPL:
	         case BVC:
	         case BVS:
	              if (ip->numBytes != 2)
		       { 
#if 0
			 printf ("ERROR/ malformed instruction, line %d\n", line);
		         exit (-1);
#else
			 break;
#endif
		       }
		      if (ip->opval1 < 0 OR ip->opval1 >= MEM_SIZE)
		       { printf ("ERROR/ address out range, line %d\n", line);
		         exit (-1);
		       }
		      memMap[ip->opval1].type = 'L';
		      break;
	         case JMP_ABS:
	              if (ip->numBytes != 3)
		       { 
#if 0
			 printf ("ERROR/ malformed instruction, line %d\n", line);
		         exit (-1);
#else
			 break;
#endif
		       }
		      if (ip->opval1 < 0 OR ip->opval1 >= MEM_SIZE)
		       { printf ("ERROR/ address out range, line %d\n", line);
		         exit (-1);
		       }
		      memMap[ip->opval1].type = 'L';
		      break;
	         case JSR:
	              if (ip->numBytes != 3)
		       { 
#if 0
			 printf ("ERROR/ malformed instruction, line %d\n", line);
		         exit (-1);
#else
			 break;
#endif
		       }
		      if (ip->opval1 < 0 OR ip->opval1 >= MEM_SIZE)
		       { printf ("ERROR/ address out range, line %d\n", line);
		         exit (-1);
		       }
		      memMap[ip->opval1].type = 'S';
		      break;
	       }
	      num_idefs++;
	    }
	 }

	if (num_idefs == MAX_IDEFS)
	 { printf ("WARNING: internal buffer filled to capacity\n");
	 }

	for (i=0; i<num_idefs; i++)
	 { int addr = idefs[i].address;
	   switch (memMap[addr].type)
	    { case 'S':
		   memMap[addr].value = subNum++;
		   break;
	      case 'L':
		   memMap[addr].value = labelNum++;
		   break;
	    }
	 }

	for (i=0; i<num_idefs; i++)
	 {
	   print_idef (&idefs[i]);
	 }
}

char *advanceField(s)
char *s;
{
	char *cp;

	cp = s;
	while  (isspace(*cp)) cp++;
	while (!isspace(*cp)) cp++;
	return (cp);
}

unpack (ip, buffer)
INSTRUCTION_DEF *ip;
char *buffer;
{
	int stringLength, numWords, cp, inWord;
	int opChar;
	int status, lastWord, numBytes;
	char *str;
	int i;

	while (1)
	 { char *cp;
	   if (gets (buffer) == NULL)
	    { return (END_OF_INPUT);
	    }
	   cp = buffer;
	   while (isspace(*cp))
	    { cp++;
	    }
	   if (*cp != ';' && *cp != '\0')
	    { break;
	    }
	   puts(buffer);
	 }	

	stringLength = 0;
	while (buffer[stringLength] != 0 && buffer[stringLength] != ';')
	 { stringLength++;
	 }
	buffer[stringLength++] = ' '; /* don't ask */

#ifdef EBUG
	printf("Line \"%s\"\n",buffer);
	printf ("stringLength = %d\n", stringLength);
#endif 

	/* Count the number of words */

	numWords = 0;
	inWord = NO;
	cp = 0;
	numBytes = 0;
	while (cp < stringLength)
	 { if (isspace (buffer[cp]))
	    { if (inWord)
	       { inWord = NO;
		 if ((cp-lastWord >= 3) && (numBytes == 0) && (numWords != 1))
		  { numBytes = numWords - 2;
		  }
	       }
	    }
	   else
	    { if (!inWord)
	       { inWord = YES;
	         numWords++;
		 lastWord = cp;
	       }
	    }
	   cp++;
	 }

#ifdef EBUG
	printf ("Number of words = %d, numBytes = %d, lastWord = %d\n",
		numWords, numBytes, lastWord);
#endif

	if (numWords == 0)
	 { return (SCAN_SKIP);
	 }

	if (numWords < 3 OR numWords > 7)
	 { return (BAD_SYNTAX);
	 }

	if ((numBytes < 1) OR (numBytes > 3))
	 { return(BAD_SYNTAX);
	 }

	if ((numWords-numBytes < 2) OR (numWords-numBytes > 4))
	 { return(BAD_SYNTAX);
	 }

	ip->numBytes = numBytes;
	str = buffer;
	if (sscanf(str,"%x",&ip->address) != 1)
	 { return(BAD_SYNTAX);
	 }
	str = advanceField(str);
	if (ip->address < 0 OR ip->address >= MEM_SIZE)
	 { return(BAD_ADDRESS);
	 }
	for (i=0;i<numBytes;i++)
	 { if (sscanf(str,"%x",&ip->byte[i]) != 1)
	    { return(BAD_SYNTAX);
	    }
	   str = advanceField(str);
	 }
	if (sscanf(str,"%4s",ip->instruction) != 1)
	 { return(BAD_SYNTAX);
	 }
	str = advanceField(str);
	if (numWords-numBytes == 2)
	 { ip->opcode1 = ip->opcode2 = NULL;
	 }
	else if (numWords == 7)
	 { /* bit of a hack for a special case */
	   char buf1[256];
	   char buf2[256];
	   if (sscanf(str,"%s%s",buf1,buf2) != 2)
	    { return(BAD_SYNTAX);
	    }
	   str = advanceField(str);
	   ip->opcode1 = copystr(buf1);
	   ip->opcode2 = copystr(buf2);
	 }
	else
	 { /* Now determine the size required for the last word */
#ifdef EBUG
	   printf("from lastWord on is \"%.*s\"\n",
			stringLength-lastWord,buffer+lastWord);
#endif
	   opChar = cp = lastWord;
	   while ((cp < stringLength) AND 
		  !isspace(buffer[cp])  AND 
		  buffer[cp] != ',')
	    { cp++;
	    }
	   ip->opcode1 = malloc (cp - opChar + 1);
	   strncpy (ip->opcode1, &buffer[opChar], cp-opChar);
	   ip->opcode1 [cp-opChar] = 0;

	   if (buffer[cp++] == ',')
	    { opChar = cp;
	      while ((cp < stringLength) AND !isspace(buffer[cp]))
	       { cp++;
	       }
	      ip->opcode2 = malloc (cp - opChar + 1);
	      strncpy (ip->opcode2, &buffer[opChar], cp-opChar);
	      ip->opcode2 [cp-opChar] = 0;
	    }
	   else
	    { ip->opcode2 = NULL;
	    }
	 }

	ip->opval1 = ip->opval2 = -1;
	if (ip->opcode1)
	 { if (ip->opcode1[0] == '$')
	    { if (sscanf (&ip->opcode1[1], "%x", &ip->opval1) != 1)
	       { return (BAD_SYNTAX);
	       }
	    }
	 }
	if (ip->opcode2)
	 { if (ip->opcode2[0] == '$')
	    { if (sscanf (&ip->opcode2[1], "%x", &ip->opval2) != 1)
	       { return (BAD_SYNTAX);
	       }
	    }
	 }
	return (SCAN_OK);
}

print_idef (ip)
INSTRUCTION_DEF *ip;
{
	int i;

	switch (memMap[ip->address].type)
	 { case ' ':
 	        printf ("\t%04x ", ip->address);
		break;
	   case 'L':
		printf ("L%d:\t%04x ", memMap[ip->address].value, ip->address);
		break;
	   case 'S':
		printf ("\nSUB%d:\t%04x ", memMap[ip->address].value, ip->address);
		break;
	 }
	
	for (i=0; i<ip->numBytes; i++)
	 { printf ("%02x ", ip->byte[i]);
	 }
	for (i=ip->numBytes; i<3; i++)
	 { printf ("   ");
	 }
	printf ("  %s ", ip->instruction);

	switch (ip->byte[0])
	 { case BCC:
	   case BCS:
	   case BEQ:
	   case BMI:
	   case BNE:
	   case BPL:
	   case BVC:
	   case BVS:
	   case JMP_ABS:
		printf ("L%-7d", memMap[ip->opval1].value);
		break;
	   case JSR:
		printf ("SUB%-5d", memMap[ip->opval1].value);
		break;
	   default:
		if (ip->opcode1)
		 { printf ("%s", ip->opcode1);
	  	 }
		if (ip->opcode2)
		 { printf (",%s", ip->opcode2);
		 }
		break;
	 }
	putchar ('\n');
}
