/*************************************************************************

                    Linkable D-code Disassembler
                    ----------------------------

Author : David Allison
Issue  : 1.0
Date   : 11 August 1987

(C) 1987 Beebug Limited

This file is the main driving software for the disassembler.  It should be
linked with SPECIAL, the special opcode handler.

***************************************************************************/

# include <h.stdio>    /* standard input/output definitions */
# include <h.stdlib>   /* standard library definitions */
# include <h.stddef>   /* standard definitions */

# define NUM_OPCODES 67         /* number of opcodes */

/* these are the types */

# define INT 0                /* int */
# define UINT 1               /* unsigned int */
# define LONG 2               /* long int */
# define ULONG 3              /* unsigned long */
# define CHAR 4               /* char (signed) */
# define UCHAR 5              /* unsigned char */
# define BIT_FIELD 6          /* bit field */
# define FLOAT 7              /* float/double */

int address ;                 /* address of current opcode */
FILE *file_ptr ;              /* file ptr for file */


/* the opcodes are held in the following structure.  The num_bytes member
   holds various info about the opcode.  The bits in this member are:

   bit 7     - clear indicates that a type may follow the opcode
   bit6 ..0  - the number of bytes
   all set   - this is a special opcode requiring special treatment  */


struct op_list 
    {
    byte opcode ;             /* the opcode */
    char *string ;            /* the name of the opcode */
    byte num_bytes ;          /* number of bytes following opcode */
    } list[NUM_OPCODES] = {
             { 0xa0,"pre-inc",255 },
             { 0xa2,"add",0 },
             { 0xa3,"pre-dec",255 },
             { 0xa5,"sub",0 },
             { 0xa7,"mult",1 },
             { 0xaa,"and",0 },
             { 0xab,"onescomp",0 },
             { 0xac,"noteq",0 },
             { 0xad,"not",0 },
             { 0xaf,"mod",1 },
             { 0xb0,"gteq",1 },
             { 0xb2,"greater",1 },
             { 0xb3,"rshift",0 },
             { 0xb4,"lesseq",1 },
             { 0xb6,"less",1 },
             { 0xb7,"lshift",0 },
             { 0xb8,"equal",0 },
             { 0xb9,"store",1 },
             { 0xbb,"exor",0 },
             { 0xbe,"or",0 },
             { 0xc0,"div",1 },
             { 0xd0,"post-inc",255 },
             { 0xd3,"post-dec",255 },
             { 0xd7,"contents",1 },
             { 0xd5,"neg",0 },
             { 0xdd,"float-s",0 },
             { 0xe1,"defbytes",255 },
             { 0xe2,"defsym",255 },
             { 0xe3,"decsp",130 },
             { 0xe4,"jf",255 },
             { 0xe5,"jt",255 },
             { 0xe6,"jmp",255 },
             { 0xe7,"rts",0 },
             { 0xe8,"switch",0 },
             { 0xe9,"switch-list",255 },
             { 0xea,"end-list",255 },
             { 0xeb,"ldmem",255 },
             { 0xec,"ldaddrmem",255 },
             { 0xed,"ldstk",3 },
             { 0xee,"ldimm4",132 },
             { 0xef,"ldlitaddr",130 },
             { 0xf0,"ldind",1 },
             { 0xf1,"pop-as",0 },
             { 0xf2,"ldind-np",1 },
             { 0xf3,"incsp",130 },
             { 0xf4,"defspace",130 },
             { 0xf5,"deflitoff",130},
             { 0xf6,"trunc",0 },
             { 0xf7,"ldaddrstk",130 },
             { 0xf8,"call",255 },
             { 0xf9,"callind",129 },
             { 0xfa,"push-vs",0 },
             { 0xfb,"def-list",255 },
             { 0xfc,"push-as",0 },
             { 0x80,"func-name",255 },
             { 0x81,"line-number",130 },
             { 0x83,"reset-line-func",255 },
             { 0x82,"addf",0 },
             { 0x85,"subf",0 },
             { 0xb5,"negf",0 },
             { 0x86,"ldimm2",130 },
             { 0x87,"ldimm1",129 },
             { 0x88,"ldimm0",0 },
             { 0x89,"ldimm3",131 },
             { 0x8a,"defstatoff",255 },
             { 0xfd,"float-us",0 },
             { 0x8b,"sym-ref",255}
             } ;


/* this function returns the address of the opcode record (see above)
   associated with the opcode passed as a parameter */

struct op_list *opcode_record(byte opcode)
   {
   int i ;

   for (i=0 ; i<NUM_OPCODES; i++)      /* search list */
      if (opcode == list[i].opcode)    /* if opcode matches then  */
        return &list[i] ;              /* its address */
   return 0 ;                          /* error - unknown opcode */
   }


/* this is the main functional function.  It disassembles one opcode */

int disassemble_opcode (void)
   {
   struct op_list *code ;              /* pointer to opcode record */
   byte num_bytes, opcode ;
   int i,type ;

   if ((opcode = get_byte()) == EOF)
      return EOF ;                     /* end of file */
   code = opcode_record(opcode) ;      /* get the address of the opcode */
   if (code == 0)
      {
      fprintf (stderr,"\nUnknown opcode : %X\n",opcode) ;   /* bad opcode */
      exit (0) ;
      } 
   else
      {
      printf ("%04X      %-20s",address,code->string) ; 
      num_bytes = code->num_bytes ;                   /* get number of bytes */
      if (num_bytes == 0xff)
         disassemble_special(code) ;                  /* special opcode */
      else 
         if (num_bytes)                              /* do any bytes follow */
           {
            type = get_byte() ;                      /* get the type */
            if ((num_bytes & 0x80) == 0)             /* type allowed ? */
               {
               if (type==BIT_FIELD)                  /* for bitfield need */
                  num_bytes += 2 ;                   /* 2 more */
               print_type (type) ;                   /* print the type */
               }
            else
               printf ("%02X ",type) ;               /* no type */
            --num_bytes ;                           /* already have one byte */
            num_bytes &= 0x7f ;                     /* mask off top bit */
            for (i=0 ; i<num_bytes ; i++)           /* print all bytes */
               printf ("%02X ",get_byte() & 0xff) ;
            }
      }
   putchar ('\n') ;
   return 0 ;
   }

/* print out the type passed */

print_type (int type)
   {
   switch (type)
      {
      case INT       : printf ("int ") ;
                       break ;
      case UINT      : printf ("uint ") ;
                       break ;
      case LONG      : printf ("long ") ;
                       break ;
      case ULONG     : printf ("ulong ") ;
                       break ;
      case CHAR      : printf ("char ") ;
                       break ;
      case UCHAR     : printf ("uchar ") ;
                       break ;
      case BIT_FIELD : printf ("bitfield ") ;
                       break ;
      case FLOAT     : printf ("float ") ;
      }
   }


int get_byte(void)
   {
   address++ ;
   return bget(file_ptr) ;
   }


/* open the file */

open_file(char *name)
   {
   address = 0 ;
   if ((file_ptr = fopen (name,"r")) == NULL)
      {
      fprintf (stderr,"Can't open file \"%s\"\n",name) ;
      exit(0) ;
      }
   }


/* check that the file is a valid object file, and skip the header */

int skip_header(void)
   {
   static char header[] = "Beebug C Compiler V1.4\r" ;
   int i ;

   for (i=0 ; header[i] != '\r' ; i++)
     {
     if (header[i] != get_byte())
        return FALSE ;
     }
   get_byte() ;                     /* skip \r */
   get_byte() ;                     /* skip debug flag */
   while (get_byte() != '\r') ;     /* skip filename */
   return TRUE ;                    /* header ok */
   }


main(int argc, char *argv[])
   {
   char name[100] ;

   printf ("\nD-code Disassembler Version 1.0\n") ;
   printf ("Author : David Allison\n") ;
   printf ("(C) 1987 Beebug Ltd\n\n") ;
   if (argc < 2)
      {
      printf ("Filename:") ;
      scanf ("%s",name) ;            /* get the file to do */
      putchar ('\n') ;
      }
   else
      sscanf (argv[1],"%s",name) ;   /* file has been passed */
   open_file(name) ;                 /* open the file */
   if (skip_header() == FALSE)
      fprintf (stderr,"Bad object file\n") ;
   else
      while (disassemble_opcode() != EOF) ;
   fclose (file_ptr) ;                 /* close the file - finished */
   }

/* los endos... */
