#include <stdio.h>
#include <strings.h>

#include "../h/malloc.h"

#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))


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


typedef struct {
	  unsigned char opcode;
	  char *name;
	  int addrmode; } INSTRDEF;

typedef struct {
	  int mode;
	  char *name;
	  char *format;
	  int nbytes; } ADDRMODE;

#define ADDR_NOSUCH 0
ADDRMODE addrmodes[] = { { ADDR_NOSUCH, "none such", "", 1 },
#define ADDR_IND_Y 1
			 { ADDR_IND_Y, "(ind),y", " ($%02x),y", 2 },
#define ADDR_IND_X 2
			 { ADDR_IND_X, "(ind,x)", " ($%02x,x)", 2 },
#define ADDR_ABS 3
			 { ADDR_ABS, "abs", " $%04x", 3 },
#define ADDR_ABS_X 4
			 { ADDR_ABS_X, "abs,x", " $%04x,x", 3 },
#define ADDR_ABS_Y 5
			 { ADDR_ABS_Y, "abs,y", " $%04x,y", 3 },
#define ADDR_ACCUM 6
			 { ADDR_ACCUM, "accum", "", 1 },
#define ADDR_IMM 7
			 { ADDR_IMM, "imm", " #$%02x", 2 },
#define ADDR_IMPLIED 8
			 { ADDR_IMPLIED, "implied", "", 1 },
#define ADDR_INDIRECT 9
			 { ADDR_INDIRECT, "indirect", " ($%04x)", 3 },
#define ADDR_RELATIVE 10
			 { ADDR_RELATIVE, "relative", "", 2 },
#define ADDR_ZP 11
			 { ADDR_ZP, "zp", " $%02x", 2 },
#define ADDR_ZP_THREE 12
			 { ADDR_ZP_THREE, "zp", " ZP-3 $%04x", 3 },
#define ADDR_ZP_X 13
			 { ADDR_ZP_X, "zp,x", " $%02x,x", 2 },
#define ADDR_ZP_Y 14
			 { ADDR_ZP_Y, "zp,y", " $%02x,y", 2 } };

int maxnb;
INSTRDEF instrdefs[256];

unsigned char memory[65536];

int curcol;

int catoi(s)
char *s;
{
 int v;
 int srv;

 v = 0;
 if (*s == '0')
  { if (s[1] == 'x')
     { srv = sscanf(s+2,"%x",&v);
     }
    else
     { srv = sscanf(s,"%o",&v);
     }
  }
 else
  { srv = sscanf(s,"%d",&v);
  }
 if (srv != 1)
  { v = 0;
  }
 return(v);
}

tab(n)
int n;
{
 while (curcol % n)
  { putchar(' ');
    curcol++;
  }
}

int oneColumnOnly = 0;

main(ac,av)
int ac;
char **av;
{
 int i;
 INSTRDEF *id;
 FILE *f;
 int opc;
 int nbytes;
 char nambuf[64];
 char amodebuf[64];
 char *progname;
 int base = -1;
 int errs;
 
	progname = *av;
	ac--; av++;
	while (ac > 0)
	 { if (strcmp (*av, "-1") == 0)
	    { oneColumnOnly = 1;
	    }
	   else if (base < 0)
	    { base = catoi(*av);
	    }
	   else
	    { ac = -1;
	      break;
	    }
	   ac--; av++;
	 }
	if (ac == -1)
	 { fprintf(stderr,"Usage: %s [-1] [base-address]\n",progname);
	   exit(1);
	 }
  	if (base < 0)
	 { base = 0;
	 }

 errs = 0;
 for (i=0;i<256;i++)
  { id = &instrdefs[i];
    id->opcode = i;
    id->name = "???";
    id->addrmode = ADDR_NOSUCH;
  }
 maxnb = 0;
 f = fopen("disas.instrs","r");
 if (f == 0)
  { fprintf(stderr,"Can't open `instrs' file\n");
    exit(1);
  }
 while (fscanf(f,"%x %s %s %d",&opc,nambuf,amodebuf,&nbytes) == 4)
  { if ((opc < 0) || (opc > 255))
     { fprintf(stderr,"Bad opcode %x in instruction table\n",opc);
       errs ++;
       continue;
     }
    for (i=0;i<ARRAYSIZE(addrmodes);i++)
     { if ( (addrmodes[i].nbytes == nbytes) &&
	    !strcmp(addrmodes[i].name,amodebuf) )
	{ break;
	}
     }
    if (i >= ARRAYSIZE(addrmodes))
     { fprintf(stderr,"Bad addressing mode \"%s\"/%d in instruction table\n",amodebuf,nbytes);
       errs ++;
       continue;
     }
    id = &instrdefs[opc];
    id->opcode = opc;
    id->name = copyofstr(nambuf);
    id->addrmode = addrmodes[i].mode;
    if (nbytes > maxnb)
     { maxnb = nbytes;
     }
  }
 fclose(f);
 if (errs)
  { exit(1);
  }
 bzero(memory,sizeof(memory));
 i = base;
 while (scanf("%x",&opc) == 1)
  { memory[i++] = opc;
    i &= 65535;
  }
 if (i == base)
  { exit(0);
  }
 curcol = 0;
 disassemble(base,i);
 exit(0);
}

#define OUT(str)	fputs(str,stdout); curcol += strlen(str);

disassemble(from,to)
int from;
int to;
{
 char obuf[256];
 int dot;
 int nextnl;
 int column;
 int opc;
 INSTRDEF *id;
 ADDRMODE *am;
 int nb;
 int i;
 int auxil;

 printf ("; Disassembly from 0x%04x to 0x%04x\n",from,to-1);
 dot = from;
 nextnl = from;
 while (1)
  { if (dot == to)
     { break;
     }
    opc = memory[dot];
    id = &instrdefs[opc];
    am = &addrmodes[id->addrmode];
    nb = am->nbytes;
    if (dot == nextnl)
     { putchar('\n');
       curcol = 0;
       column = 1;
       nextnl = dot + nb;
     }
    else
     { column++;
     }
    if (column == 1 || !oneColumnOnly)
     { tab(30);
       printf("%04x",dot);
       for (i=0;i<nb;i++)
	{ sprintf(obuf," %02x",(int)memory[dot+i]);
	  OUT (obuf);
	}
       for (;i<maxnb;i++)
	{ sprintf(obuf,"   ");
	  OUT (obuf);
	}
       auxil = 0;
       for (i=nb-1;i>0;i--)
	{ auxil = (auxil << 8) | (int)memory[dot+i];
	}
       sprintf(obuf," %s",id->name);
       OUT (obuf);
       if (id->addrmode == ADDR_RELATIVE)
	{ if (auxil & 0x80)
	   { auxil |= (~0)<<7;
	   }
	  auxil += dot + nb;
	  sprintf(obuf," $%04x",auxil);
	  OUT (obuf);
	}
       else
	{ sprintf(obuf,am->format,auxil);
	  OUT (obuf);
	}
     }
    dot ++;
  }
 putchar('\n');
 curcol = 0;
}
