/*
 *   Copyright (C) 1985, 1991 by the University of Waterloo,
 *   Computer Systems Group. All rights reserved. No part
 *   of this software may be reproduced in any form or by
 *   any means - graphic, electronic or mechanical,
 *   including photocopying, recording, taping or
 *   information storage and retrieval systems - except
 *   with the written permission of the copyright owner.
 */

/*
 * disasm -- given a 370 object file, generate assembler code that can
 *           be assembled to give an equivalent object file
 *
 * Notes:
 *  1. This file contains lines such as
 *      #ifdef TARGCMS
 *      #endif
 *     around blocks of code that are CMS dependent.  They can be
 *     enabled by compiling with a command such as
 *       CW DISASM ( PRM TARGCMS X
 *     TARGCMS is thus defined as a preprocessor constant.
 *     By default, MVS-dependent code is generated.
 *
 *  2. For this version, DISASM does not support COM, DXD, Q-cons
 *     and multiple CSECTs very well.  LISTTXT contains some code to
 *     process these, that needs to be incorporated into DISASM.
 */

#include <errno.h>
#include <file.h>
#include <obj370.h>
#include <setup.h>
#include <stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned short int usint;
typedef unsigned int uint;

typedef union obj_record
  {
    esd_record obj_esd;
    txt_record obj_txt;
    rld_record obj_rld;
    sym_record obj_sym;
    end_record obj_end;
  } obj_record;

typedef struct esd_recdes
  {
    char        esdrd_name[ 9 ];
    char        esdrd_type;
    int         esdrd_addr;
    char        esdrd_align;
    int         esdrd_length;
    usint       esdrd_esdid;
    struct esd_recdes *esdrd_next;
  } esd_recdes;

typedef struct txt_recdes
  {
    int         txtrd_addr;
    short int   txtrd_length;
    usint       txtrd_esdid;
    char        txtrd_buffer[ 56 ];
    struct txt_recdes *txtrd_next;
  } txt_recdes;

typedef struct rld_recdes
  {
    usint       rldrd_resdid;
    usint       rldrd_pesdid;
    char        rldrd_flag;
    int         rldrd_addr;
    struct rld_recdes *rldrd_next;
  } rld_recdes;

typedef struct end_recdes
  {
    int         endrd_entpt;
    usint       endrd_esdid;
  } end_recdes;

typedef struct csect_des
  {
    struct csect_des *csd_next;
    esd_recdes  *csd_esdlst;
    txt_recdes  *csd_txtlst;
    rld_recdes  *csd_rldlst;
    end_recdes  csd_end;
    char        csd_csname[ 9 ];
  } csect_des;

/* specific opttbl indices (origin 1)... */
#define OPT_NOREGEQU    1
#define OPT_REGEQU      2
#define OPT_NONE        3
#define OPT_YES         23      /* start of 'yes' options */

const static char opttbl[] =
  {                     /* tblookup retval */
    "NORegequ\0"        /* 1 */
    "Regequ\0"          /* 2 */
    "NONe\0"            /* 3 */
    "NOEsa\0"           /* 4 */
    "NOPriv\0"          /* 5 */
    "NOXa\0"            /* 6 */
    "NOBas\0"           /* 7 */
    "NOCSS\0"           /* 8 */
    "NOCs\0"            /* 9 */
    "NOTcc\0"           /* 10 */
    "NODC\0"            /* 11 */
    "NODas\0"           /* 12 */
    "NOEF\0"            /* 13 */
    "NOEPf\0"           /* 14 */
    "NOMi\0"            /* 15 */
    "NOMP\0"            /* 16 */
    "NOPKh\0"           /* 17 */
    "NOSki\0"           /* 18 */
    "NOSR\0"            /* 19 */
    "NOTB\0"            /* 20 */
    "NOTR\0"            /* 21 */
    "NOVector\0"        /* 22 */
    "Esa\0"
    "Priv\0"
    "Xa\0"
    "Bas\0"
    "CSS\0"
    "Cs\0"
    "Tcc\0"
    "DC\0"
    "Das\0"
    "EF\0"
    "EPf\0"
    "Mi\0"
    "MP\0"
    "PKh\0"
    "Ski\0"
    "SR\0"
    "TB\0"
    "TR\0"
    "Vector\0"
  };

const int _parms = UNTOKENIZED;
const char _clbwarn = 0;

extern unsigned short int *dasm370( const unsigned short int *src,
    char *dst, int dasmopt );

static int open_obj_file( const char *cmdln );
static void esd_proc( const esd_record *esdrecptr );
static void txt_proc( const txt_record *txtrecptr );
static void rld_proc( const rld_record *rldrecptr );
static void end_proc( const end_record *endrecptr );
static void d_assemble( void );
static void print_end( void );
static void disassemble( int last_addr );
static char *nextlabel( char *buf_ptr, char *new_ptr, int cur_addr );
static void print_addr_label( uint address );
static void print_rlds_and_esds( uint curaddr, uint endaddr,
    int orgback );
static void print_label( esd_recdes *esdrdptr );
static int print_rld( int addr, char *buf_ptr );
static void getname( char *name, usint esdid );
static char *str_chr( char *str, int chr );
static void *dmalloc( size_t size );
static void dprintf( const char *ctl, ... );

csect_des *csect_list;
esd_recdes *esd_ptr;
rld_recdes *rld_ptr;
char *txtbuffer, *end_ptr;
int cs_address, txtbufsize;
unsigned int dasmopt;


int main( int   argc,
          char  **argv )
  {
    int         objfd;
    obj_record  *objptr;
    char        *obj_label;

    csect_list = dmalloc( sizeof( csect_des ) );
    memzero( csect_list, sizeof( csect_des ) );
    txtbufsize = 0;
    objfd = open_obj_file( *argv );
    objptr = dmalloc( sizeof( obj_record ) );
    while( read( objfd, (char *) objptr, sizeof( obj_record ) ) > 0 )
      {
        if( objptr->obj_esd.esd_punch != PUNCH_12_2_9 )
            continue;
        obj_label = objptr->obj_esd.esd_label;
        if( memcmp( obj_label, "ESD", 3 ) == 0 )
            esd_proc( &objptr->obj_esd );
        else if( memcmp( obj_label, "TXT", 3 ) == 0 )
            txt_proc( &objptr->obj_txt );
        else if( memcmp( obj_label, "RLD", 3 ) == 0 )
            rld_proc( &objptr->obj_rld );
        else if( memcmp( obj_label, "END", 3 ) == 0 )
            end_proc( &objptr->obj_end );
        else if( memcmp( obj_label, "SYM", 3 ) == 0 )
            fprintf( stderr, "SYM records are not supported\n" );
        else
            fprintf( stderr,
                "Unsupported object record '%3.3s' (ignored)\n",
                obj_label );
      }
    close( objfd );
    d_assemble();
    return( 0 );
  }


static int open_obj_file( const char *cmdln )
  {
    char        *option_str, *fname, *ftype;
    int         objfd, errnum;
    unsigned int optlen, optnum;
    struct stat stbuff;
    char        sysmsg[ 100 ];

    option_str = strchr( cmdln, ' ' );
    if( option_str != NULL )
        option_str = strskip( option_str, ' ' );
    if( ( option_str == NULL ) ||
        ( *option_str == '?' ) ||
        ( *option_str == '\0' ) )
      {
#ifdef TARGCMS
        fputs(
          "Syntax:  <optional items are enclosed in angle brackets>\n"
          "\n"
          "DISASM fname <TEXT <fmode>> <( options> < >output-file>\n"
          "\n"
          "options:\n"
          "  <NO>Regequ  NONe        <NO>Esa     <NO>Priv    <NO>Xa\n"
          "  <NO>Bas     <NO>CSS     <NO>Cs      <NO>Tcc     <NO>DC\n"
          "  <NO>Das     <NO>EF      <NO>EPf     <NO>Mi      <NO>MP\n"
          "  <NO>PKh     <NO>Ski     <NO>SR      <NO>TB      <NO>TR\n"
          "  <NO>Vector\n"
          "\n"
          "Example:\n"
          "  DISASM MYFILE ( REG > MYFILE ASSEMBLE ( RECFM F LRECL 80\n",
            stderr );
#else
        fputs(
          "Syntax:  <optional items are enclosed in angle brackets>\n"
          "\n"
          "DISASM fname <<,>options> < >output-file>\n"
          "\n"
          "options:\n"
          "  <NO>Regequ  NONe        <NO>Esa     <NO>Priv    <NO>Xa\n"
          "  <NO>Bas     <NO>CSS     <NO>Cs      <NO>Tcc     <NO>DC\n"
          "  <NO>Das     <NO>EF      <NO>EPf     <NO>Mi      <NO>MP\n"
          "  <NO>PKh     <NO>Ski     <NO>SR      <NO>TB      <NO>TR\n"
          "  <NO>Vector\n"
          "\n"
          "Example:\n"
          "  DISASM OBJFNAME,REG > ASMFNAME\n",
            stderr );
#endif
        exit( 1 );
      }
    dasmopt = 0x80000000U;
    fname = alloca( strlen( option_str ) + 6 );
    strcpy( fname, option_str );
#ifdef TARGCMS
    option_str = strchr( fname, '(' );
    if( option_str != NULL )
#else
    option_str = fname + strcspn( fname, " ," );
    if( *option_str != '\0' )
#endif
      {
        *option_str = '\0';
        strrtrm( fname );
        ++option_str;
        for(;;)
          {
            for(;;)
              {
                option_str = strskip( option_str, ' ' );
                if( *option_str != ',' )
                    break;
                ++option_str;
              }
            if( *option_str == '\0' )
                break;
            optlen = strcspn( option_str, " ," );
            optnum = tblookup( opttbl, option_str, optlen );
            if( optnum == 0 )
              {
                fprintf( stderr, "Unknown option '%s'\n", option_str );
                exit( 24 );
              }
            if( optnum == OPT_NOREGEQU )
                dasmopt &= 0x7fffffff;
            else if( optnum == OPT_REGEQU )
                dasmopt |= 0x80000000;
            else if( optnum == OPT_NONE )
                dasmopt |= 0x7fffffff;
            else if( optnum < OPT_YES )
                dasmopt |= (1 << (optnum - OPT_NONE - 1));
            else
                dasmopt &= ~(1 << (optnum - OPT_YES));
            option_str += optlen;
          }
      }
#ifdef TARGCMS
    if( *( strskip( str_chr( fname, ' ' ), ' ' ) ) == '\0' )
        strcat( fname, " TEXT" );
#endif
    objfd = open( fname, O_RDONLY|O_BINARY );
    if( objfd == -1 )
      {
        errnum = errno;
        if( ( errnum == EIO ) ||
            ( errnum == ENOENT ) )
            strcpy( sysmsg, _syserr() );
        fprintf( stderr, "Unable to open file \"%s\" for input\n",
            fname );
        if( errnum == EIO )
            fprintf( stderr, " %s\n", sysmsg );
        else if( errnum == ENOENT )
            fprintf( stderr, " %s (%s)\n", strerror( errnum ),
                sysmsg );
        else
            fprintf( stderr, " %s\n", strerror( errnum ) );
        exit( 28 );
      }
#ifdef TARGCMS
    if( ( fstat( objfd, &stbuff ) != 0 ) ||
        ( stbuff.st_lrecl != sizeof( obj_record ) ) ||
        ( stbuff.st_recfm != 'F' ) )
      {
        fprintf( stderr,
            "File \"%s\" is not a RECFM F LRECL 80 object/text file\n",
            fname );
        close( objfd );
        exit( 32 );
      }
#endif
    return( objfd );
  }


static void esd_proc( const esd_record *esdrecptr )
  {
    esd_recdes  *esdrdptr, *esd_cursor, *prevrdptr;
    esd_item    *esditemptr;
    usint       first_esdid;
    int         len;

    first_esdid = esdrecptr->esd_esdid;
    esditemptr = esdrecptr->esd_items;
    for( len = esdrecptr->esd_itemcnt;
         len >= sizeof( esd_item );
         len -= sizeof( esd_item ) )
      {
        /* extract the contents of the esd_item into the esd_recdes... */
        esdrdptr = dmalloc( sizeof( esd_recdes ) );
        memcpy( esdrdptr->esdrd_name, esditemptr->esd_name, 8 );
        esdrdptr->esdrd_name[ 8 ] = '\0';
        esdrdptr->esdrd_type = esditemptr->esd__ta.esd_type;
        if( esdrdptr->esdrd_type == ESDT_LD )
            esdrdptr->esdrd_esdid = 0;
        else
            esdrdptr->esdrd_esdid = first_esdid++;
        esdrdptr->esdrd_addr = esditemptr->esd__ta.esd_addr;
        esdrdptr->esdrd_align = esditemptr->esd__ae.esd_align;
        esdrdptr->esdrd_length = esditemptr->esd__ae.esd_extra;
        /* sort the ESD list (insertion sort)... */
        esd_cursor = csect_list->csd_esdlst;
        prevrdptr = NULL;
        for(;;)
          {
            if( ( esd_cursor == NULL ) ||
                ( esd_cursor->esdrd_addr > esdrdptr->esdrd_addr ) ||
                ( ( esd_cursor->esdrd_addr == esdrdptr->esdrd_addr ) &&
                  ( esd_cursor->esdrd_type > esdrdptr->esdrd_type ) ) )
              {
                esdrdptr->esdrd_next = esd_cursor;
                if( prevrdptr == NULL )
                    csect_list->csd_esdlst = esdrdptr;
                else
                    prevrdptr->esdrd_next = esdrdptr;
                break;
              }
            prevrdptr = esd_cursor;
            esd_cursor = esd_cursor->esdrd_next;
          }
        ++esditemptr;
      }
  }


static void txt_proc( const txt_record *txtrecptr )
  {
    txt_recdes *txtrdptr, *txt_cursor, *prevrdptr;

    txtrdptr = dmalloc( sizeof( txt_recdes ) );
    txtrdptr->txtrd_addr = txtrecptr->txt__f.txt_faddr;
    txtrdptr->txtrd_length = txtrecptr->txt_size;
    txtbufsize += txtrdptr->txtrd_length;
    txtrdptr->txtrd_esdid = txtrecptr->txt_esdid;
    memcpy( txtrdptr->txtrd_buffer, txtrecptr->txt_info,
        txtrdptr->txtrd_length );
    /* sort the TXT list (insertion sort)... */
    txt_cursor = csect_list->csd_txtlst;
    prevrdptr = NULL;
    for(;;)
      {
        if( ( txt_cursor == NULL ) ||
            ( txt_cursor->txtrd_esdid > txtrdptr->txtrd_esdid ) ||
            ( ( txt_cursor->txtrd_esdid == txtrdptr->txtrd_esdid ) &&
              ( txt_cursor->txtrd_addr > txtrdptr->txtrd_addr ) ) )
          {
            txtrdptr->txtrd_next = txt_cursor;
            if( prevrdptr == NULL )
                csect_list->csd_txtlst = txtrdptr;
            else
                prevrdptr->txtrd_next = txtrdptr;
            break;
          }
        prevrdptr = txt_cursor;
        txt_cursor = txt_cursor->txtrd_next;
      }
  }


static void rld_proc( const rld_record *rldrecptr )
  {
    char        prev_flag;
    usint       len, relocation_esdid, position_esdid;
    rld_recdes  *rld_cursor, *rldrdptr, *prevrdptr;
    rld_entry   *rldentptr;
    rld_faddr   *rldfaptr;

    prev_flag = 0;
    len = rldrecptr->rld_size;
    rldentptr = rldrecptr->rld_data;
    while( len > 0 )
      {
        rldrdptr = dmalloc( sizeof( rld_recdes ) );
        /* if the least_significant_bit of the previous flag is off, */
        /*  new esdids are found... */
        if( ( prev_flag & 0x01 ) == 0 )
          {
            relocation_esdid = rldentptr->rld_relesdid;
            position_esdid = rldentptr->rld_posesdid;
            rldfaptr = &rldentptr->rld__fa;
            len -= ( sizeof( rld_entry ) - sizeof( rld_faddr ) );
          }
        rldrdptr->rldrd_resdid = relocation_esdid;
        rldrdptr->rldrd_pesdid = position_esdid;
        rldrdptr->rldrd_flag = rldfaptr->rld_flag;
        rldrdptr->rldrd_addr = rldfaptr->rld_addr;
        ++rldfaptr;
        rldentptr = (rld_entry *) rldfaptr;
        len -= sizeof( rld_faddr );
        prev_flag = rldrdptr->rldrd_flag;
        /* sort the RLD list (insertion sort)... */
        rld_cursor = csect_list->csd_rldlst;
        prevrdptr = NULL;
        for(;;)
          {
            if( ( rld_cursor == NULL ) ||
                ( rld_cursor->rldrd_addr > rldrdptr->rldrd_addr ) ||
                ( ( rld_cursor->rldrd_addr == rldrdptr->rldrd_addr ) &&
                  ( rld_cursor->rldrd_resdid > rldrdptr->rldrd_resdid )))
              {
                rldrdptr->rldrd_next = rld_cursor;
                if( prevrdptr == NULL )
                    csect_list->csd_rldlst = rldrdptr;
                else
                    prevrdptr->rldrd_next = rldrdptr;
                break;
              }
            prevrdptr = rld_cursor;
            rld_cursor = rld_cursor->rldrd_next;
          }
      }
  }


static void end_proc( const end_record *endrecptr )
  {
    csect_list->csd_end.endrd_entpt = endrecptr->end__e.end_entpt;
    if( csect_list->csd_end.endrd_entpt == 0x404040 )
        csect_list->csd_end.endrd_entpt = 0;
    csect_list->csd_end.endrd_esdid = endrecptr->end_esdid;
    if( csect_list->csd_end.endrd_esdid == 0x4040 )
        csect_list->csd_end.endrd_esdid = 0;
  }


static void d_assemble( void )
  {
    txt_recdes  *txtrdptr;
    int         cur_addr, last_addr;
    usint       cur_esdid;

    esd_ptr = csect_list->csd_esdlst;
    rld_ptr = csect_list->csd_rldlst;
    txtbuffer = dmalloc( txtbufsize );
    /* start copying the TXT list into txtbuffer... */
    txtrdptr = csect_list->csd_txtlst;
    cur_esdid = 1;
    last_addr = 0;
    end_ptr = txtbuffer;
    for(;;)
      {
        /* if the end of the TXT list is encountered... */
        if( txtrdptr == NULL )
          {
            disassemble( last_addr );
            print_rlds_and_esds( last_addr, 0xffffffff, 0 );
            print_end();
            return;
          }
        cur_addr = txtrdptr->txtrd_addr;
        /* if the start address of the new txt_recdes is different */
        /*  from the end address of the previous txt_recdes OR if the */
        /*  esdid of these record are different... */
        if( ( txtrdptr->txtrd_esdid != cur_esdid ) ||
            ( cur_addr != last_addr ) )
          {
            disassemble( last_addr );
            print_rlds_and_esds( last_addr, cur_addr, 1 );
            last_addr = cur_addr;
            end_ptr = txtbuffer;
            cur_esdid = txtrdptr->txtrd_esdid;
          }
        /* ...otherwise simply copy the txt_recdes into txtbuffer... */
        memcpy( end_ptr, txtrdptr->txtrd_buffer, txtrdptr->txtrd_length);
        end_ptr += txtrdptr->txtrd_length;
        last_addr += txtrdptr->txtrd_length;
        txtrdptr = txtrdptr->txtrd_next;
      }
  }


static void print_end( void )
  {
    esd_recdes  *esdrdptr;
    char        csect[ 9 ];
    int         addr;

    dprintf( "         END" );
    if( csect_list->csd_end.endrd_esdid )
      {
        strcpy( csect, "*UNKNOWN" );
        addr = 0;
        /* search the ESD list for the csect name and address... */
        for( esdrdptr = csect_list->csd_esdlst;
             esdrdptr != NULL;
             esdrdptr = esdrdptr->esdrd_next )
          {
            if( ( esdrdptr->esdrd_esdid == csect_list->csd_end.endrd_esdid ) &&
                ( esdrdptr->esdrd_type == ESDT_SD ) )
              {
                strcpy( csect, esdrdptr->esdrd_name );
                *( str_chr( csect, ' ' ) ) = '\0';
                addr = esdrdptr->esdrd_addr;
                break;
              }
          }
        dprintf( "   %s", csect );
        if( csect_list->csd_end.endrd_entpt )
            dprintf( "+X'%X'", addr + csect_list->csd_end.endrd_entpt );
      }
    dprintf( "\n" );
  }


static void disassemble( int last_addr )
  {
    int         cur_addr, len;
    char        *buf_ptr, *new_ptr, *lbl_ptr;
    char        fmtbuf[ 80 ];

    cur_addr = last_addr - ( end_ptr - txtbuffer );
    buf_ptr = txtbuffer;
    if( buf_ptr == end_ptr )
        print_addr_label( cur_addr );
    /* main processing loop... */
    while( buf_ptr < end_ptr )
      {
        print_addr_label( cur_addr );
        len = print_rld( cur_addr, buf_ptr );
        buf_ptr += len;
        cur_addr += len;
        if( len != 0 )
            continue;
        /* unaligned code is not permitted... */
        if( ((int) buf_ptr) & 1 )
            new_ptr = buf_ptr;
        else
            new_ptr = (char *) dasm370( (unsigned short int *) buf_ptr,
                fmtbuf, dasmopt );
        if( new_ptr == buf_ptr )
          {
            /* no valid instruction was found... */
            lbl_ptr = nextlabel( buf_ptr, new_ptr + 2, cur_addr );
            /* if labels occur within the next 2 bytes... */
            if( lbl_ptr != NULL )
              {
                cur_addr += ( lbl_ptr - buf_ptr );
                for( ; buf_ptr < lbl_ptr; ++buf_ptr )
                    dprintf( "         DC    X'%02X'\n", *buf_ptr );
              }
            else if( ((int) buf_ptr) & 1 )
              {
                dprintf( "         DC    X'%02X'\n", buf_ptr[ 0 ] );
                ++cur_addr;
                ++buf_ptr;
              }
            else
              {
                dprintf( "         DC    X'%02X%02X'\n",
                    buf_ptr[ 0 ], buf_ptr[ 1 ] );
                cur_addr += 2;
                buf_ptr += 2;
              }
          }
        else
          {
            /* a valid instruction was found... */
            lbl_ptr = nextlabel( buf_ptr, new_ptr, cur_addr );
            /* if labels occur within the instruction... */
            if( lbl_ptr != NULL )
              {
                cur_addr += ( lbl_ptr - buf_ptr );
                for( ; buf_ptr < lbl_ptr; ++buf_ptr )
                    dprintf( "         DC    X'%02X'\n", *buf_ptr );
              }
            else
              {
                dprintf( "         %s\n", fmtbuf );
                cur_addr += ( new_ptr - buf_ptr );
                buf_ptr = new_ptr;
              }
          }
      }
  }


static char *nextlabel( char *buf_ptr,
                     char *new_ptr,
                     int cur_addr )
  {
    esd_recdes *esdrdptr;

    for( ; buf_ptr < new_ptr; ++buf_ptr, ++cur_addr )
      {
        for(;;)
          {
            esdrdptr = esd_ptr;
            if( ( esdrdptr == NULL ) ||
                ( esdrdptr->esdrd_addr > cur_addr ) )
                break;
            if( esdrdptr->esdrd_addr == cur_addr )
                return( buf_ptr );
            esd_ptr = esd_ptr->esdrd_next;
          }
      }
    if( new_ptr > end_ptr )
        return( end_ptr );
    return( NULL );
  }


static void print_addr_label( uint address )
  {
    uint        curaddr;
    esd_recdes  *esdrdptr;

    curaddr = address;
    for(;;)
      {
        /* if no entries are found with the address... */
        esdrdptr = esd_ptr;
        if( ( esdrdptr == NULL ) ||
            ( esdrdptr->esdrd_addr > address ) )
          {
            if( curaddr != address )
                dprintf( "         ORG   %s+X'%X'\n",
                    csect_list->csd_csname, address - cs_address );
            return;
          }
        /* if an entry is found with the address... */
        if( esdrdptr->esdrd_addr == curaddr )
            print_label( esdrdptr );
        else
          {
            if( esdrdptr->esdrd_type == ESDT_LD )
              {
                curaddr = esdrdptr->esdrd_addr;
                dprintf( "         ORG   %s+X'%X'\n",
                    csect_list->csd_csname, curaddr - cs_address );
              }
            print_label( esdrdptr );
          }
        esd_ptr = esdrdptr->esdrd_next;
      }
  }


static void print_rlds_and_esds( uint curaddr, uint endaddr, int orgback)
  {
    for(;;)
      {
        if( esd_ptr != NULL )
          {
            if( ( rld_ptr == NULL ) ||
                ( esd_ptr->esdrd_addr <= rld_ptr->rldrd_addr ) )
              {
                if( esd_ptr->esdrd_addr >= endaddr )
                    break;
                if( ( curaddr != esd_ptr->esdrd_addr ) &&
                    ( esd_ptr->esdrd_type == ESDT_LD ) )
                  {
                    curaddr = esd_ptr->esdrd_addr;
                    dprintf( "         ORG   %s+X'%X'\n",
                        csect_list->csd_csname, curaddr - cs_address );
                  }
                print_label( esd_ptr );
                esd_ptr = esd_ptr->esdrd_next;
                continue;
              }
          }
        if( ( rld_ptr == NULL ) ||
            ( rld_ptr->rldrd_addr >= endaddr ) )
            break;
        if( curaddr != rld_ptr->rldrd_addr )
          {
            curaddr = rld_ptr->rldrd_addr;
            dprintf( "         ORG   %s+X'%X'\n",
                csect_list->csd_csname, curaddr - cs_address );
          }
        curaddr += print_rld( curaddr, "\0\0\0" );
      }
    if( ( orgback != 0 ) &&
        ( curaddr != endaddr ) )
        dprintf( "         ORG   %s+X'%X'\n", csect_list->csd_csname,
            endaddr - cs_address );
  }


static void print_label( esd_recdes *esdrdptr )
  {
    int esd_flag;

    switch( esdrdptr->esdrd_type )
      {
        case ESDT_SD:
            strcpy( csect_list->csd_csname,
                esdrdptr->esdrd_name );
            *( str_chr( csect_list->csd_csname, ' ' ) ) = '\0';
            dprintf( "%-9sCSECT\n", csect_list->csd_csname );
            esd_flag = esdrdptr->esdrd_align;
            if( esd_flag == 0x40 )
                printf( "*        NO AMODE/RMODE\n" );
            else
              {
                if( esd_flag & ESDA_RMODE_ANY )
                    printf( "%-9sRMODE ANY\n",
                        csect_list->csd_csname );
                else
                    printf( "%-9sRMODE 24\n",
                        csect_list->csd_csname );
                esd_flag &= 0x03;
                if( esd_flag == ESDA_AMODE_24D )
                    printf( "*        AMODE 24 (DEFAULT)\n" );
                else if( esd_flag == ESDA_AMODE_24 )
                    printf( "%-9sAMODE 24\n",
                        csect_list->csd_csname );
                else if( esd_flag == ESDA_AMODE_31 )
                    printf( "%-9sAMODE 31\n",
                        csect_list->csd_csname );
                else
                    printf( "%-9sAMODE ANY\n",
                        csect_list->csd_csname );
              }
            cs_address = esdrdptr->esdrd_addr;
            if( ( esdrdptr->esdrd_esdid == 0x0001 ) &&
                ( (dasmopt & 0x80000000) != 0 ) )
                dprintf( "R0       EQU   0\n"
                         "R1       EQU   1\n"
                         "R2       EQU   2\n"
                         "R3       EQU   3\n"
                         "R4       EQU   4\n"
                         "R5       EQU   5\n"
                         "R6       EQU   6\n"
                         "R7       EQU   7\n"
                         "R8       EQU   8\n"
                         "R9       EQU   9\n"
                         "R10      EQU   10\n"
                         "R11      EQU   11\n"
                         "R12      EQU   12\n"
                         "R13      EQU   13\n"
                         "R14      EQU   14\n"
                         "R15      EQU   15\n"
                         "F0       EQU   0\n"
                         "F2       EQU   2\n"
                         "F4       EQU   4\n"
                         "F6       EQU   6\n" );
            break;
        case ESDT_LD:
            dprintf( "         ENTRY %s\n",
                esdrdptr->esdrd_name );
            dprintf( "%-9sDS    0X\n", esdrdptr->esdrd_name );
            break;
        case ESDT_ER:
            dprintf( "         EXTRN %s\n",
                esdrdptr->esdrd_name );
            break;
        case ESDT_PC:
            dprintf( "         CSECT\n" );
            break;
        case ESDT_CM:
            dprintf( "         COM\n" );
            break;
        case ESDT_XD:
            dprintf( "         DXD\n" );
            break;
        case ESDT_WX:
            dprintf( "         WXTRN %s\n",
                esdrdptr->esdrd_name );
            break;
      }
  }


static int print_rld( int address,
                      char *buf_ptr )
  {
    rld_recdes  *rldrdptr;
    int         len, offset;
    char        type, name[ 9 ];

    rldrdptr = rld_ptr;
    for(;;)
      {
        if( ( rldrdptr == NULL ) ||
            ( rldrdptr->rldrd_addr > address ) )
            return( 0 );
        if( rldrdptr->rldrd_addr == address )
          {
            /* from bits 0 and 1 determine the length of offset... */
            len = ( ( rldrdptr->rldrd_flag & 0x0c ) >> 2 ) + 1;
            if( ( rldrdptr->rldrd_flag & 0xf0 ) == 0x30 )
              {
                dprintf( "         CXD\n" );
                rld_ptr = rldrdptr->rldrd_next;
                return( len );
              }
            offset = 0;
            memcpy( ( (char *) &offset ) + sizeof( offset ) - len,
                buf_ptr, len );
            /* from first digit determine the type... */
            switch( rldrdptr->rldrd_flag & 0xf0 )
              {
                case 0x00:
                    type = 'A';
                    break;
                case 0x10:
                    type = 'V';
                    break;
                case 0x20:
                    type = 'Q';
                    break;
              }
            if( offset )
                dprintf( "         DC    %cL%d(%d", type, len, offset );
            else
                dprintf( "         DC    %cL%d(", type, len );
            for(;;)
              {
                if( ( rldrdptr == NULL ) ||
                    ( rldrdptr->rldrd_addr > address ) )
                  {
                    dprintf( ")\n" );
                    rld_ptr = rldrdptr;
                    return( len );
                  }
                getname( name, rldrdptr->rldrd_resdid );
                /* from bit 2 of the second digit determine sign... */
                if( ( rldrdptr->rldrd_flag & 0x02 ) == 0x02 )
                    dprintf( "-" );
                else
                    if( offset != 0 )
                        dprintf( "+" );
                offset = 1;
                dprintf( "%s", name );
                rldrdptr = rldrdptr->rldrd_next;
              }
          }
        rldrdptr = rldrdptr->rldrd_next;
      }
  }


static void getname( char *name,
                     usint esdid )
  {
    esd_recdes *esdrdptr;

    for( esdrdptr = csect_list->csd_esdlst;
         esdrdptr != NULL;
         esdrdptr = esdrdptr->esdrd_next )
      {
        if( ( esdrdptr->esdrd_esdid == esdid ) &&
            ( ( esdrdptr->esdrd_type != ESDT_LD ) ||
              ( esdrdptr->esdrd_type != ESDT_ER ) ) )
          {
            strcpy( name, esdrdptr->esdrd_name );
            *( str_chr( name, ' ' ) ) = '\0';
            return;
          }
      }
  }


static char *str_chr( char *str, int chr )
  {
    /* return pointer to chr or '\0'... */
    while( ( *str != chr ) &&
           ( *str != '\0' ) )
        ++str;
    return( str );
  }


static void *dmalloc( size_t size )
  {
    void *ptr;

    ptr = malloc( size );
    if( ( ptr != NULL ) ||
        ( size == 0 ) )
        return( ptr );
    fputs( "Insufficient memory\n", stderr );
    exit( 41 );
  }


static void dprintf( const char *ctl, ... )
  {
    int         errnum;
    va_list     ap;
    char        sysmsg[ 100 ];

    errno = 0;
    va_start( ap, ctl );
    vprintf( ctl, ap );
    va_end( ap );
    errnum = errno;
    if( errnum == 0 )
        return;
    if( errnum == EIO )
        strcpy( sysmsg, _syserr() );
    fprintf( stderr, "Error writing output file\n %s\n",
        ( errnum == EIO ) ? sysmsg : strerror( errnum ) );
    exit( 28 );
  }