/*

   2asm: Convert binary files to 80*86 assembler. Version 1.00

License:

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

Comments:

   The code was originally snaffled from the GNU C++ debugger, as ported
   to DOS by DJ Delorie and Kent Williams (williams@herky.cs.uiowa.edu).
   Extensively modified by Robin Hilliard in Jan and May 1992.

   This source compiles under Turbo C v2.01.  The disassembler is entirely
   table driven so it's fairly easy to change to suit your own tastes.

   The instruction table has been modified to correspond with that in
   `Programmer's Technical Reference: The Processor and Coprocessor',
   Robert L. Hummel, Ziff-Davis Press, 1992.  Missing (read "undocumented")
   instructions were added and many mistakes and omissions corrected.

   The emulated coprocessor instructions on interrupts 34--3E are disassembled
   if the "-e" command line option is specified.  I don't deal with segment
   overrides to emulated '87 instructions, read Hummel if you *really* want to
   know all the gory details (you don't.).  The Borland defined shortcuts on
   int 3E are not disassembled either (they're not real instructions anyway!)

   Command line switches (case sensitive):

   -e :  Disassemble (unoverridden) emulated 80*87 instructions (not default)

   -3 :  Assume code is 32 bit (default==16)

   -x :  Output all numbers in pure hex (no leading zeros or trailing "h"s.)

   -s :  Don't specify operand size (ie omit "byte ptr", "word ptr" and
         "dword ptr" from instruction output.

   -d :  Don't specify distance of calls and jumps (near/far/short)
         (not default)

Health warning:

   When writing and degbugging this code, I didn't have (and still don't have)
   a 32-bit disassembler to compare this guy's output with.  It's therefore
   quite likely that bugs will appear when disassembling instructions which use
   the 386 and 486's native 32 bit mode.  It seems to work fine in 16 bit mode.

Any comments/updates/bug reports to:

   Robin Hilliard, Lough Guitane, Killarney, Co. Kerry, Ireland.
   Tel:         [+353] 64-54014
   Internet:    softloft@iruccvax.ucc.ie
   Compu$erve:  100042, 1237

   If you feel like registering, and possibly get notices of updates and
   other items of software, then send me a post card of your home town.

   Thanks and enjoy!

*/

/* Code starts here...         */

#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <stdlib.h>

typedef unsigned long word32;
typedef unsigned short word16;
typedef unsigned char word8;
typedef signed long int32;
typedef signed short int16;
typedef signed char int8;

typedef union {
  struct {
    word16 ofs;
    word16 seg;
  } w;
  word32 dword;
} WORD32;

/* variables controlled by command line flags */
static int8  seg_size=16;   /* default size is 16 */
static int8  do_hex = 0;    /* default is to use reassemblable instructions */
static int8  do_distance = 1; /* default is to use reassemblable instructions */
static word8 do_emul87 = 0; /* don't try to disassemble emulated instrcutions */
static word8 do_size = 1;   /* default to outputting explicit operand size */
static word8 must_do_size;  /* used with do_size */

static int wordop;           /* dealing with word or byte operand */
static FILE *infile;         /* input stream */
static word8 instruction_length;
static instruction_offset;
static word16 done_space; /* for opcodes with > one space */
static word8 patch87;     /* fudge variable used in 8087 emu patching code */

static char ubuf[100], *ubufp;
static col;               /* output column */
static prefix;            /* segment override prefix byte */
static modrmv;            /* flag for getting modrm byte */
static sibv;              /* flag for getting sib byte   */
static opsize;            /* just like it says ...       */
static addrsize;
static jmp_buf reached_eof; /* jump back when reached eof */

/* some defines for extracting instruction bit fields from bytes */

#define MOD(a)	  (((a)>>6)&7)
#define REG(a)	  (((a)>>3)&7)
#define RM(a)	  ((a)&7)
#define SCALE(a)  (((a)>>6)&7)
#define INDEX(a)  (((a)>>3)&7)
#define BASE(a)   ((a)&7)


extern char *opmap1[];      /* stuff from text.c */
extern char *second[];
extern char *groups[][8];
extern char *f0[];
extern char *fop_9[];
extern char *fop_10[];
extern char *fop_12[];
extern char *fop_13[];
extern char *fop_14[];
extern char *fop_15[];
extern char *fop_21[];
extern char *fop_28[];
extern char *fop_32[];
extern char *fop_33[];
extern char *fop_36[];
extern char *fop_37[];
extern char *fop_38[];
extern char *fop_39[];
extern char *fop_40[];
extern char *fop_42[];
extern char *fop_43[];
extern char *fop_44[];
extern char *fop_45[];
extern char *fop_48[];
extern char *fop_49[];
extern char *fop_51[];
extern char *fop_52[];
extern char *fop_53[];
extern char *fop_54[];
extern char *fop_55[];
extern char *fop_60[];
extern char **fspecial[];
extern char *floatops[];


/* prototypes */

static void ua_str(char *);
static word8 unassemble(word16);
static char *addr_to_hex(int32,word8);
static word8 getbyte(void);
static word8 silent_getbyte(void);
static word8 silent_returnbyte(word8 );
static modrm(void);
static sib(void);
static void uprintf(char *, ...);
static void uputchar(char );
static int bytes(char );
static void outhex(char , int , int , int , int );
static void reg_name(int , char );
static void do_sib(int );
static void do_modrm(char );
static void floating_point(int );
static void percent(char , char );


static char *addr_to_hex(int32 addr, char splitup)
{
  static char buffer[11];
  WORD32 adr;
  char hexstr[2];

  strcpy(hexstr, do_hex?"h":"");
  adr.dword = addr;
  if (splitup) {
    if (adr.w.seg==0 || adr.w.seg==0xffff) /* 'coz of wraparound */
      sprintf(buffer, "%04X%s", adr.w.ofs, hexstr);
    else
      sprintf(buffer, "%04X%s:%04X%s", adr.w.seg, hexstr, adr.w.ofs, hexstr);
  } else {
    if (adr.w.seg==0 || adr.w.seg==0xffff) /* 'coz of wraparound */
      sprintf(buffer, "%04X%s", adr.w.ofs, hexstr);
    else
      sprintf(buffer, "%08lX%s", addr, hexstr);
  }
  return buffer;
}


static word8 getbyte(void)
{
  int16 c;

  c = fgetc(infile);
  if (c==EOF)
    longjmp(reached_eof, 1);
  printf("%02X", c);   /* print out byte */
  col+=2;
  if (patch87) {
    c -= 0x5C;     /* fixup second byte in emulated '87 instruction */
    patch87 = 0;
  }
  instruction_length++;
  instruction_offset++;
  return c;
}

/* used for lookahead */
static word8 silent_getbyte(void)
{
  return fgetc(infile);
}
/* return byte to input stream */
static word8 silent_returnbyte(word8 c)
{
  return ungetc(c, infile);
}



/*
   only one modrm or sib byte per instruction, tho' they need to be
   returned a few times...
*/

static modrm(void)
{
  if (modrmv == -1)
    modrmv = getbyte();
  return modrmv;
}


static sib(void)
{
  if (sibv == -1)
    sibv = getbyte();
  return sibv;
}



/*------------------------------------------------------------------------*/
static void uprintf(char *s, ...)
{
  vsprintf(ubufp, s, ...);
  while (*ubufp)
    ubufp++;
}



static void uputchar(char c)
{
  if (c == '\t') {
    if (done_space) {      /* don't tab out if already done so */
      uputchar(' ');
    } else {
      done_space = 1;
      do {
        *ubufp++ = ' ';
      } while ((ubufp-ubuf) % 8);
    }
  } else
    *ubufp++ = c;
  *ubufp = 0;
}


/*------------------------------------------------------------------------*/
static int bytes(char c)
{
  switch (c) {
  case 'b':
       return 1;
  case 'w':
       return 2;
  case 'd':
       return 4;
  case 'v':
       if (opsize == 32)
         return 4;
       else
         return 2;
  }
  return 0;
}



/*------------------------------------------------------------------------*/
static void outhex(char subtype, int extend, int optional, int defsize, int sign)
{
  int n=0, s=0, i;
  int32 delta;
  unsigned char buff[6];
  char *name;
  char  signchar;

  switch (subtype) {
  case 'q':
       if (wordop) {
         if (opsize==16) {
           n = 2;
         } else {
           n = 4;
         }
       } else {
         n = 1;
       }
       break;

  case 'a':
       break;
  case 'x':
       extend = 2;
       n = 1;
       break;
  case 'b':
       n = 1;
       break;
  case 'w':
       n = 2;
       break;
  case 'd':
       n = 4;
       break;
  case 's':
       n = 6;
       break;
  case 'c':
  case 'v':
       if (defsize == 32)
         n = 4;
       else
         n = 2;
       break;
  case 'p':
       if (defsize == 32)
         n = 6;
       else
         n = 4;
       s = 1;
       break;
  }
  for (i=0; i<n; i++)
    buff[i] = getbyte();
  for (; i<extend; i++)
    buff[i] = (buff[i-1] & 0x80) ? 0xff : 0;
  if (s) {
    uprintf("%02X%02X:", buff[n-1], buff[n-2]);
    n -= 2;
  }
  switch (n) {
  case 1:
       delta = *(signed char *)buff;
       break;
  case 2:
       delta = *(signed int *)buff;
       break;
  case 4:
       delta = *(signed long *)buff;
       break;
  }
  if (extend > n) {
    if (subtype!='x') {
      if ((long)delta<0) {
        delta = -delta;
        signchar = '-';
      } else
        signchar = '+';
      if (delta || !optional)
        uprintf(do_hex?"%c%0*lX":"%c%0*lXh", signchar, do_hex?extend:extend+1, delta);
    } else {
      if (extend==2)
        delta = (word16) delta;
      uprintf(do_hex?"%0.*lX":"%0.*lXh", 2*extend+1, delta);
/*      uprintf(do_hex?"%0.*lX":"%0.*lXh", 2*(do_hex?extend:extend+1), delta); */
    }
    return;
  }
  if ((n == 4) && !sign) {
    name = addr_to_hex(delta, 0);
    uprintf("%s", name);
    return;
  }
  switch (n) {
  case 1:
       if (sign && (char)delta<0) {
         delta = -delta;
         signchar = '-';
       } else
         signchar = '+';
       if (sign)
         uprintf(do_hex?"%c%02X":"%c%03Xh",signchar,(unsigned char)delta);
       else
         uprintf(do_hex?"%02X":"%03Xh", (unsigned char)delta);
       break;

  case 2:
       if (sign && (int)delta<0) {
         signchar = '-';
         delta = -delta;
       } else
         signchar = '+';
       if (sign)
         uprintf(do_hex?"%c%04X":"%c%05Xh", signchar,(int)delta);
       else
         uprintf(do_hex?"%04X":"%05Xh", (unsigned int)delta);
       break;

  case 4:
       if (sign && (long)delta<0) {
         delta = -delta;
         signchar = '-';
       } else
         signchar = '+';
       if (sign)
         uprintf(do_hex?"%c%08X":"%c%09lXh", signchar, (unsigned long)delta);
       else
         uprintf(do_hex?"%08X":"%09lXh", (unsigned long)delta);
       break;
  }
}

/*------------------------------------------------------------------------*/

static void reg_name(int regnum, char size)
{
  if (size == 'F') { /* floating point register? */
    uprintf("st(%d)", regnum);
    return;
  }
  if (((size == 'v') && (opsize == 32)) || (size == 'd'))
    uputchar('e');
  if ((size=='q' || size == 'b' || size=='c') && !wordop) {
    uputchar("acdbacdb"[regnum]);
    uputchar("llllhhhh"[regnum]);
  } else {
    uputchar("acdbsbsd"[regnum]);
    uputchar("xxxxppii"[regnum]);
  }
}


/*------------------------------------------------------------------------*/
static void do_sib(int m)
{
  int s, i, b;

  s = SCALE(sib());
  i = INDEX(sib());
  b = BASE(sib());
  switch (b) {     /* pick base */
  case 0: ua_str("%p:[eax"); break;
  case 1: ua_str("%p:[ecx"); break;
  case 2: ua_str("%p:[edx"); break;
  case 3: ua_str("%p:[ebx"); break;
  case 4: ua_str("%p:[esp"); break;
  case 5:
       if (m == 0) {
         ua_str("%p:[");
         outhex('d', 4, 0, addrsize, 0);
       } else {
         ua_str("%p:[ebp");
       }
       break;
  case 6: ua_str("%p:[esi"); break;
  case 7: ua_str("%p:[edi"); break;
  }
  switch (i) {     /* and index */
  case 0: uprintf("+eax"); break;
  case 1: uprintf("+ecx"); break;
  case 2: uprintf("+edx"); break;
  case 3: uprintf("+ebx"); break;
  case 4: break;
  case 5: uprintf("+ebp"); break;
  case 6: uprintf("+esi"); break;
  case 7: uprintf("+edi"); break;
  }
  if (i != 4) {
    switch (s) {    /* and scale */
      case 0: uprintf(""); break;
      case 1: uprintf("*2"); break;
      case 2: uprintf("*4"); break;
      case 3: uprintf("*8"); break;
    }
  }
}



/*------------------------------------------------------------------------*/
static void do_modrm(char subtype)
{
  int mod = MOD(modrm());
  int rm = RM(modrm());
  int extend = (addrsize == 32) ? 4 : 2;

  if (mod == 3) { /* specifies two registers */
    reg_name(rm, subtype);
    return;
  }
  if (must_do_size) {
    if (wordop) {
      if (addrsize==32 || opsize==32) {       /* then must specify size */
        ua_str("dword ptr ");
      } else {
        ua_str("word ptr ");
      }
    } else {
      ua_str("byte ptr ");
    }
  }
  if ((mod == 0) && (rm == 5) && (addrsize == 32)) {/* mem operand with 32 bit ofs */
    ua_str("%p:[");
    outhex('d', extend, 0, addrsize, 0);
    uputchar(']');
    return;
  }
  if ((mod == 0) && (rm == 6) && (addrsize == 16)) { /* 16 bit dsplcmnt */
    ua_str("%p:[");
    outhex('w', extend, 0, addrsize, 0);
    uputchar(']');
    return;
  }
  if ((addrsize != 32) || (rm != 4))
    ua_str("%p:[");
  if (addrsize == 16) {
    switch (rm) {
    case 0: uprintf("bx+si"); break;
    case 1: uprintf("bx+di"); break;
    case 2: uprintf("bp+si"); break;
    case 3: uprintf("bp+di"); break;
    case 4: uprintf("si"); break;
    case 5: uprintf("di"); break;
    case 6: uprintf("bp"); break;
    case 7: uprintf("bx"); break;
    }
  } else {
    switch (rm) {
    case 0: uprintf("eax"); break;
    case 1: uprintf("ecx"); break;
    case 2: uprintf("edx"); break;
    case 3: uprintf("ebx"); break;
    case 4: do_sib(mod); break;
    case 5: uprintf("ebp"); break;
    case 6: uprintf("esi"); break;
    case 7: uprintf("edi"); break;
    }
  }
  switch (mod) {
  case 1:
       outhex('b', extend, 1, addrsize, 0);
       break;
  case 2:
       outhex('v', extend, 1, addrsize, 1);
       break;
  }
  uputchar(']');
}



/*------------------------------------------------------------------------*/
static void floating_point(int e1)
{
  int esc = e1*8 + REG(modrm());

  if (MOD(modrm()) == 3) {
    if (fspecial[esc]) {
      if (fspecial[esc][0][0] == '*') {
        ua_str(fspecial[esc][0]+1);
      } else {
        ua_str(fspecial[esc][RM(modrm())]);
      }
    } else {
      ua_str(floatops[esc]);
      ua_str(" %EF");
    }
  } else {
    ua_str(floatops[esc]);
    ua_str(" %EF");
  }
}




/*------------------------------------------------------------------------*/
/* Main table driver                                                      */
static void percent(char type, char subtype)
{
  int32 vofs;
  char *name;
  int extend = (addrsize == 32) ? 4 : 2;
  char c;

start:
  switch (type) {
  case 'A':                          /* direct address */
       outhex(subtype, extend, 0, addrsize, 0);
       break;

  case 'C':                          /* reg(r/m) picks control reg */
       uprintf("C%d", REG(modrm()));
       must_do_size = 0;
       break;

  case 'D':                          /* reg(r/m) picks debug reg */
       uprintf("D%d", REG(modrm()));
       must_do_size = 0;
       break;

  case 'E':                          /* r/m picks operand */
       do_modrm(subtype);
       break;

  case 'G':                          /* reg(r/m) picks register */
       if (subtype == 'F')                 /* 80*87 operand?   */
         reg_name(RM(modrm()), subtype);
       else
         reg_name(REG(modrm()), subtype);
       must_do_size = 0;
       break;

  case 'I':                            /* immed data */
       outhex(subtype, 0, 0, opsize, 0);
       break;

  case 'J':                            /* relative IP offset */
       switch(bytes(subtype)) {              /* sizeof offset value */
       case 1:
            vofs = (int8)getbyte();
            break;
       case 2:
            vofs = getbyte();
            vofs += getbyte()<<8;
            vofs = (int16)vofs;
            break;
       case 4:
            vofs = (word32)getbyte();           /* yuk! */
            vofs |= (word32)getbyte() << 8;
            vofs |= (word32)getbyte() << 16;
            vofs |= (word32)getbyte() << 24;
            break;
       }
       name = addr_to_hex(vofs+instruction_offset,1);
       uprintf("%s", name);
       break;

  case 'K':
       if (do_distance==0)
         break;
       switch (subtype) {
       case 'f':
            ua_str("far ");
            break;
       case 'n':
            ua_str("near ");
            break;
       case 's':
            ua_str("short ");
            break;
       }
       break;

  case 'M':                            /* r/m picks memory */
       do_modrm(subtype);
       break;

  case 'O':                            /* offset only */
       ua_str("%p:[");
       outhex(subtype, extend, 0, addrsize, 0);
       uputchar(']');
       break;

  case 'P':                            /* prefix byte (rh) */
       ua_str("%p:");
       break;

  case 'R':                            /* mod(r/m) picks register */
       reg_name(REG(modrm()), subtype);      /* rh */
       must_do_size = 0;
       break;

  case 'S':                            /* reg(r/m) picks segment reg */
       uputchar("ecsdfg"[REG(modrm())]);
       uputchar('s');
       must_do_size = 0;
       break;

  case 'T':                            /* reg(r/m) picks T reg */
       uprintf("tr%d", REG(modrm()));
       must_do_size = 0;
       break;

  case 'X':                            /* ds:si type operator */
       uprintf("ds:[");
       if (addrsize == 32)
         uputchar('e');
       uprintf("si]");
       break;

  case 'Y':                            /* es:di type operator */
       uprintf("es:[");
       if (addrsize == 32)
         uputchar('e');
       uprintf("di]");
       break;

  case '2':                            /* old [pop cs]! now indexes */
       ua_str(second[getbyte()]);      /* instructions in 386/486   */
       break;

  case 'g':                            /* modrm group `subtype' (0--7) */
       ua_str(groups[subtype-'0'][REG(modrm())]);
       break;

  case 'd':                             /* sizeof operand==dword? */
       if (opsize == 32)
         uputchar('d');
       uputchar(subtype);
       break;

  case 'w':                             /* insert explicit size specifier */
       if (opsize == 32)
         uputchar('d');
       else
         uputchar('w');
       uputchar(subtype);
       break;

  case 'e':                         /* extended reg name */
       if (opsize == 32) {
         if (subtype == 'w')
           uputchar('d');
         else {
           uputchar('e');
           uputchar(subtype);
         }
       } else
         uputchar(subtype);
       break;

  case 'f':                    /* '87 opcode */
       floating_point(subtype-'0');
       break;

  case 'j':
       if (addrsize==32 || opsize==32) /* both of them?! */
         uputchar('e');
       break;

  case 'p':                    /* prefix byte */
       switch (subtype)  {
       case 'c':
       case 'd':
       case 'e':
       case 'f':
       case 'g':
       case 's':
            prefix = subtype;
            c = getbyte();
            wordop = c & 1;
            ua_str(opmap1[c]);
            break;
       case ':':
            if (prefix)
              uprintf("%cs:", prefix);
            break;
       case ' ':
            c = getbyte();
            wordop = c & 1;
            ua_str(opmap1[c]);
            break;
       }
       break;

  case 's':                           /* size override */
       switch (subtype) {
       case 'a':
            addrsize = 48 - addrsize;
            c = getbyte();
            wordop = c & 1;
            ua_str(opmap1[c]);
/*            ua_str(opmap1[getbyte()]); */
            break;
       case 'o':
            opsize = 48 - opsize;
            c = getbyte();
            wordop = c & 1;
            ua_str(opmap1[c]);
/*            ua_str(opmap1[getbyte()]); */
            break;
       }
       break;
   }
}



static void ua_str(char *str)
{
  int c;

  if (str == 0) {
    uprintf("<invalid>");
    return;
  }
  if (strpbrk(str, "CDFGRST")) /* specifiers for registers=>no size 2b specified */
    must_do_size = 0;
  while ((c = *str++) != 0) {
    if (c == '%') {
      c = *str++;
      percent(c, *str++);
    } else {
      if (c == ' ') {
        uputchar('\t');
      } else {
        uputchar(c);
      }
    }
  }
}


static word8 unassemble(word16 ofs)
{
  char *str;
  int   c, c2;

  printf("%04X ", ofs);
  prefix = 0;
  modrmv = sibv = -1;     /* set modrm and sib flags */
  opsize = addrsize = seg_size;
  col = 0;
  ubufp = ubuf;
  done_space = 0;
  instruction_length = 0;
  c = getbyte();
  wordop = c & 1;
  patch87 = 0;
  must_do_size = do_size;
  if (do_emul87) {
    if (c==0xcd) { /* wanna do emu '87 and ->ing to int? */
      c2 = silent_getbyte();
      if (c2 >= 0x34 && c2 <= 0x3E)
        patch87 = 1;       /* emulated instruction!  => must repatch two bytes */
      silent_returnbyte(c2);
      c -= 0x32;
    }
  }
  if ((str = opmap1[c])==NULL) {    /* invalid instruction? */
    uputchar('d');                  /* then output byte defines */
    uputchar('b');
    uputchar('\t');
    uprintf(do_hex?"%02X":"%02Xh",c);
  } else {
    ua_str(str);                      /* valid instruction */
  }
  printf("%*s", 15-col, " ");
  col = 15 + strlen(ubuf);
  do {
    uputchar(' ');
    col++;
  } while (col < 43);
  printf("%s\n", ubuf);
  return instruction_length;
}



static word8 isoption(char c)
{
  return (c=='/' || c=='-');
}



void main(int argc, char *argv[])
{
  word16 instr_len;
  word16 offset;
  char infilename[80];
  char c;

#if defined(DEBUG)
  clrscr();
#endif

  *infilename = 0;
  while (--argc) {
    argv++;
    if (**argv=='?') {
hlp:  fprintf(stderr,
      "2ASM  Version 1.01  (C) Copyright 1992, Robin Hilliard\n"
      "Converts binary files to 80*86 assembly\n"
      "Usage:\n"
      "\t2asm <file> [-e] [-3] [-x] [-s] [-d]\n"
      "Switches:\n"
      "\t-e :\tDisassemble (unoverridden) emulated 80*87 instructions\n"
      "\t-3 :\tAssume code is 32 bit (default==16)\n"
      "\t-x :\tOutput numbers in pure hex (default is reassemblable)\n"
      "\t-s :\tDon't specify operand size, even where necessary\n"
      "\t-d :\tDon't specify distance short/near/far jmps and calls"
      );
      exit(1);
    }
    if (isoption(**argv)) {
      while (isoption(**argv)) {
nextflag:
        switch (c = *(++*argv)) {
        case 'e':
             do_emul87 = 1;
             break;
        case '3':
             seg_size = 32;
             break;
        case 'x':
             do_hex = 1;
             break;
        case 's':
             do_size = 0;
             break;
        case 'd':
             do_distance = 0;
             break;
        case '?':
        case 'H':
             goto hlp;
        case '#': /* hidden flag in the Loft's programs! */
             fprintf(stderr,"Last compiled on " __TIME__ ", " __DATE__) ;
             exit(1);
        default:
             fprintf(stderr, "Unknown option: `-%c'", c);
             exit(1);
        }
        ++*argv;
      }
    } else { /* assume that its a file name */
      if (*infilename) {
        fprintf(stderr,"Unknown file argument: \"%s\"", *argv);
        exit(1);
      }
      strcpy(infilename,*argv);
    }
  }
  if ((infile=fopen(infilename,"rb"))==NULL) {
    printf("Unable to open %s",infilename);
    exit(2);
  }
  offset = 0;
  strlwr(infilename);
  if (strstr(infilename, ".com")) /* not perfect, fix later */
    instruction_offset = offset = 0x100;
  if (!setjmp(reached_eof)) {
    do {
      instr_len = unassemble(offset);
      offset += instr_len;
    } while (instr_len); /* whoops, no files > 64k */
  }
}

