Subversion Repositories tendra.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (c) 2002-2006 The TenDRA Project <http://www.tendra.org/>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of The TenDRA Project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id$
 */
/*
                 Crown Copyright (c) 1996

    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-

        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;

        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;

        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;

        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.
*/
/*
                            VERSION INFORMATION
                            ===================

--------------------------------------------------------------------------
$Header: /u/g/release/CVSROOT/Source/src/installers/680x0/common/output.c,v 1.1.1.1 1998/01/17 15:55:50 release Exp $
--------------------------------------------------------------------------
$Log: output.c,v $
 * Revision 1.1.1.1  1998/01/17  15:55:50  release
 * First version to be checked into rolling release.
 *
Revision 1.4  1997/11/13 08:27:18  ma
All avs test passed (except add_to_ptr).

Revision 1.3  1997/11/10 15:38:10  ma
.

Revision 1.2  1997/11/09 14:21:00  ma
.

Revision 1.1.1.1  1997/10/13 12:42:57  ma
First version.

Revision 1.3  1997/06/18 12:04:56  ma
Merged with Input Baseline changes.

Revision 1.2  1997/05/13 11:30:38  ma
Introduced make_comment for debug.

Revision 1.1.1.1  1997/03/14 07:50:16  ma
Imported from DRA

 * Revision 1.2  1996/09/20  13:51:41  john
 * *** empty log message ***
 *
 * Revision 1.1.1.1  1996/09/20  10:56:58  john
 *
 * Revision 1.1.1.1  1996/03/26  15:45:16  john
 *
 * Revision 1.2  94/02/21  16:02:42  16:02:42  ra (Robert Andrews)
 * Put in an explicit cast.
 *
 * Revision 1.1  93/02/22  17:16:29  17:16:29  ra (Robert Andrews)
 * Initial revision
 *
--------------------------------------------------------------------------
*/


#include "config.h"
#include "common_types.h"
#include "assembler.h"
#include "instrs.h"
#include "fbase.h"
#include "mach.h"
#include "mach_ins.h"
#include "mach_op.h"
#include "output.h"
#include "codex.h"


/*
    OUTPUT FILE
*/

FILE *fpout;


/*
    OPEN OUTPUT FILE

    This routine opens the file with the given name for output.  If the
    name is null, the standard output is used.
*/

void
open_output(char *nm)
{
        if (nm == null) {
                fpout = stdout;
        } else {
                fpout = fopen(nm, "w");
                if (fpout == null) {
                        error("Can't open output file, %s", nm);
                        exit(EXIT_FAILURE);
                }
        }
        return;
}


/*
    ARRAY OF INSTRUCTION NAMES

    This table gives the mapping between instruction numbers and the
    corresponding names.
*/

#ifdef asm_dotty_instrs
#define INSTR_SET_0
#endif /* asm_dotty_instrs */

#ifdef asm_simple_instrs
#define INSTR_SET_1
#endif /* asm_simple_instrs */

char *instr_names[] = {
#include "instr_aux.h"
};


/*
    ARRAY OF GLOBAL REGISTER NAMES

    This table gives the mapping between register numbers and register
    names.
*/

#ifdef asm_percent_regs
#define REGISTER_SET_0
#endif /* asm_percent_regs */

#ifdef asm_simple_regs
#define REGISTER_SET_1
#endif /* asm_simple_regs */

static char *glob_reg_names[] = {
#include "instr_aux.h"
};


/*
    ARRAY OF LOCAL REGISTER NAMES

    This table gives the local mapping between register numbers and
    register names.  It is initialized from the table of global register
    names, but may be changed thereafter.
*/

char *reg_names[NO_OF_REGS];


/*
    OUTPUT A REGISTER NAME

    This routine outputs the register name corresponding to a given
    register number.
*/

#define  out_reg_name(X)        outs(reg_names[(X)])


/*
    OUTPUT A SUM OF DATA, EXTERNALS AND LABELS

    This routine prints the sum of all data, external and labels, starting
    with ptr, and moving down the plus-chain.
*/

static void
out_data(mach_op *ptr)
{
        mach_op *p;
        bool neg_next = 0;
        bool need_plus = 0;
        for (p = ptr; p; p = p->plus) {
                switch (p->type) {
                case MACH_EXT:
                case MACH_EXTQ:
                        if (need_plus || neg_next) {
                                outc(neg_next ? '-' : '+');
                        }
                        outs(p->def.str);
                        need_plus = 1;
                        neg_next = 0;
                        break;
                case MACH_LAB:
                case MACH_LABQ:
                        if (need_plus || neg_next) {
                                outc(neg_next ? '-' : '+');
                        }
                        outc(LPREFIX);
                        outn(p->def.num);
                        need_plus = 1;
                        neg_next = 0;
                        break;
                case MACH_SPEC:
                case MACH_SPECQ:
                        if (!output_immediately && p->def.str == special_str) {
                                /* The value of LSx is known, so use it */
                                long n = ldisp;
                                if (neg_next) {
                                        n = (-n);
                                }
                                if (p->plus && p->plus->type == MACH_VAL) {
                                        p->plus->def.num += n;
                                } else {
                                        if (need_plus && n >= 0) {
                                                outc('+');
                                        }
                                        outn(n);
                                        need_plus = 1;
                                }
                        } else {
                                if (need_plus || neg_next) {
                                        outc(neg_next ? '-' : '+');
                                }
                                outc(LPREFIX);
                                outs(p->def.str);
                                outn((long)special_no);
                                need_plus = 1;
                        }
                        neg_next = 0;
                        break;
                case MACH_VAL:
                case MACH_VALQ: {
                        long n = p->def.num;
                        if (neg_next) {
                                n = (-n);
                        }
                        if (need_plus && n >= 0) {
                                outc('+');
                        }
                        outn(n);
                        need_plus = 1;
                        neg_next = 0;
                        break;
                }
                case MACH_HEX:
                case MACH_HEXQ: {
                        long n = p->def.num;
                        if (neg_next) {
                                n = (-n);
                        }
                        if (need_plus && n >= 0) {
                                outc('+');
                        }
                        outh(n);
                        need_plus = 1;
                        neg_next = 0;
                        break;
                }
                case MACH_NEG:
                        neg_next = 1;
                        break;
                default:
                        return;
                }
        }
        return;
}


/*
    OUTPUT A SCALED OPERAND

    This routine outputs a scaled register operand.
*/

static void
out_scaled(mach_op *ptr)
{
        long sf = ptr->def.num;
        asm_scale_before;
        out_reg_name(ptr->of->def.num);
        if (sf == 1) {
                asm_scale_1;
        } else {
                asm_scale;
                outn(sf);
        }
        return;
}


/*
    OUTPUT A FLOATING POINT NUMBER

    This routine outputs a floating point number.
*/

static void
out_float(flt *f)
{
#if (FBASE == 10)
        int i;
        asm_fprefix;
        if (f->sign < 0) {
                outc('-');
        }
        outc('0' + f->mant[0]);
        outc('.');
        for (i = 1; i < MANT_SIZE; i++) {
                outc('0' + f->mant[i]);
        }
        outc('e');
        if (f->exp >= 0) {
                outc('+');
        }
        outn(f->exp);
#else
        error("Illegal floating point constant");
#endif
        return;
}


/*
    MACROS FOR CONSTRUCTS DEPENDING ON asm_data_first
*/

#ifdef asm_data_first

#define  out_data_1(X)          if (X)out_data(X)
#define  out_data_1a(X)         if (X) { out_data(X); outc(','); }
#define  out_data_1b(X)         if (X) { outc(','); out_data(X); }
#define  out_sf_data(X, Y)      if (Y)out_scaled(Y)

#else /* asm_data_first */

#define  out_data_1( X )        /* empty */
#define  out_data_1a( X )       /* empty */
#define  out_data_1b( X )       /* empty */
#define  out_sf_data(X, Y)              \
    if (X) {                            \
            outc('(');                  \
            out_data(X);                \
            if (Y) {                    \
                    outc(',');          \
                    out_scaled(Y);      \
            }                           \
            outc(')');                  \
    } else {                            \
            if (Y) {                    \
                    outc('(');          \
                    out_scaled(Y);      \
                    outc(')');          \
            }                           \
    }

#endif /* asm_data_first */


/*
   OUTPUT A MACHINE OPERAND

   This routine prints a machine operand.
 */

static void
out_mach_op(mach_op *ptr)
{
        mach_op *p = ptr;
        switch (p->type) {
        case MACH_BF: {
                /* Bitfield operands */
                long bf_off = p->def.num;
                long bf_bits = p->plus->def.num;
                out_mach_op(p->of);
                asm_bf_before;
                asm_nprefix;
                outn(bf_off);
                asm_bf_middle;
                asm_nprefix;
                outn(bf_bits);
                asm_bf_after;
                return;
        }
        case MACH_CONT:
                p = p->of;
                switch (p->type) {
                case MACH_CONT: {
                        /* Memory indirect (post- or pre-indexed) */
                        mach_op *p1 = p->plus;
                        mach_op *p2 = null;
                        mach_op *q = p->of;
                        mach_op *q1 = q->plus;
                        mach_op *q2 = null;
                        if (p1 && p1->type == MACH_SCALE) {
                                p2 = p1;
                                p1 = p2->plus;
                        }
                        if (q1 && q1->type == MACH_SCALE) {
                                if (p2) {
                                        error("Illegal addressing mode");
                                        outs("error");
                                        return;
                                }
                                q2 = q1;
                                q1 = q2->plus;
                        }
                        asm_mem_before;
                        out_data_1a(q1);
                        out_reg_name(q->def.num);
                        asm_mem_second;
                        out_sf_data(q1, q2);
                        asm_mem_third;
                        out_sf_data(p1, p2);
                        if (p2) {
                                out_scaled(p2);
                        }
                        out_data_1b(p1);
                        asm_mem_after;
                        return;
                }
                case MACH_REG: {
                        /* Register indirect (with displacement or index) */
                        mach_op *p1 = p->plus;
                        mach_op *p2 = null;
                        if (p1) {
                                if (p1->type == MACH_SCALE) {
                                        p2 = p1;
                                        p1 = p2->plus;
                                }
                                out_data_1(p1);
                        }
                        asm_ind_before;
                        out_reg_name(p->def.num);
                        asm_ind_middle;
                        out_sf_data(p1, p2);
                        asm_ind_after;
                        return;
                }
                case MACH_EXTQ: {
                        /* External indirect (with displacement or index) */
                        mach_op *p1 = p->plus;
                        mach_op *p2 = null;
                        if (p1) {
                                if (p1->type == MACH_SCALE) {
                                        p2 = p1;
                                        p1 = p2->plus;
                                }
                                out_data_1(p1);
                        }
                        asm_ind_before;
                        outs(p->def.str);
                        asm_ind_middle;
                        out_sf_data(p1, p2);
                        asm_ind_after;
                        return;
                }
                case MACH_EXT:
                case MACH_LAB:
                case MACH_SPEC:
                case MACH_VAL:
                case MACH_HEX:
                case MACH_NEG:
                        /* Contents of immediate data, externals, labels */
                        out_data(p);
                        return;
                }
                error("Illegal addressing mode");
                outs("error");
                return;
        }
        case MACH_DEC:
                /* Register indirect with predecrement */
                asm_predec_before;
                out_reg_name(p->def.num);
                asm_predec_after;
                return;
        case MACH_INC:
                /* Register indirect with postincrement */
                asm_postinc_before;
                out_reg_name(p->def.num);
                asm_postinc_after;
                return;
        case MACH_REG:
                /* Register direct */
                out_reg_name(p->def.num);
                return;
        case MACH_RPAIR:
                /* Register pair */
                out_reg_name(p->def.num);
                asm_rpair_sep;
                out_reg_name(p->plus->def.num);
                return;
        case MACH_EXT:
        case MACH_LAB:
        case MACH_SPEC:
        case MACH_VAL:
        case MACH_HEX:
                /* Immediate data, externals, labels */
                asm_nprefix;
                out_data(p);
                return;
        case MACH_EXTQ:
        case MACH_LABQ:
        case MACH_SPECQ:
                /* Contents of externals, labels */
                out_data(p);
                return;
        case MACH_FLOATQ:
                /* Floating-point data */
                out_float(p->def.fp);
                return;
        case MACH_VALQ:
                /* Integer data */
                outn(p->def.num);
                return;
        case MACH_HEXQ:
                /* Integer data */
                outh(p->def.num);
                return;
        }
        error("Illegal addressing mode");
        outs("error");
        return;
}


/*
    OUTPUT ALL MACHINE INSTRUCTIONS

    This routine outputs all the machine instructions, together with their
    operands (if any).
*/

void
output_all(void)
{
        int n;
        mach_ins *p;
        for (p = all_mach_ins; p; p = p->next) {
                n = p->ins_no;
#ifdef EBUG
#if 1
                if (n != m_comment) {
                        outs("#inst");
                        outn(p->id);
                        outnl();
                }
                if (p->id == 4921) {
                        int found = 1;
                }
#endif
#endif
                switch (n) {
#ifdef EBUG
                case m_comment:
                        outs("#");
                        outs(p->op1->def.str);
                        outnl();
                        break;
#endif

#ifdef m_ignore_ins
                case m_ignore_ins:
                        /* Ignore */
                        break;
#endif /* m_ignore_ins */

                case m_label_ins:
                        /* Labels */
                        outc(LPREFIX);
                        outn(p->op1->def.num);
                        outc(':');
                        outnl();
                        break;
                case m_extern_ins:
                        /* Externals */
                        out_data(p->op1);
                        outc(':');
                        outnl();
                        break;
#ifdef asm_uses_equals
                case m_as_assign:
                        out_mach_op(p->op1);
                        outc('=');
                        out_mach_op(p->op2);
                        outnl();
                        break;
#endif /* asm_uses_equals */
                case m_as_byte:
                case m_as_short:
                case m_as_long:
                case m_stabs:
                case m_stabd:
                case m_stabn:
                case m_dd_special: {
                        /* Data */
                        mach_op *q;
                        bool started = 0;
                        int c = 0;
                        for (q = p->op1; q; q = q->of) {
                                if (c == 0) {
                                        if (started) {
                                                outnl();
                                        }
                                        outs(instr_names[n]);
                                } else {
                                        outc(',');
                                }
                                out_data(q);
                                started = 1;
                                if (++c == 8) {
                                        c = 0;
                                }
                        }
                        outnl();
                        break;
                }
                default:
                        if (is_jump(n)) {
                                /* Jumps */
#ifndef asm_does_jump_lens
                                if (is_unsized(n)) {
                                        n += long_jump;
                                }
#endif /* !asm_does_jump_lens */
                                outs(instr_names[n]);
                                outc(LPREFIX);
                                outn(p->op1->def.num);
                                if (n == m_bra || n == m_brab ||
                                    n == m_braw || n == m_bral) {
                                        /* Align after unconditional jumps */
                                        outnl();
#ifndef no_align_directives
                                        outs(instr_names[m_as_align4]);
#endif
                                }
                        } else {
                                /* Simple instructions */
                                outs(instr_names[n]);
                                if (p->op1) {
                                        out_mach_op(p->op1);
                                }
                                if (p->op2) {
                                        outc(',');
#ifdef EBUG
                                        outc(' ');
#endif /* EBUG */
                                        out_mach_op(p->op2);
                                }
                        }
                        outnl();
                        break;
                }
        }
        return;
}


/*
    INITIALIZE INSTRUCTIONS

    Apply a couple of patches for odd instruction quirks.
*/

void
init_instructions(void)
{
#ifdef asm_no_btst_suffix
        instr_names[m_btstb] = instr_names[m_btst];
        instr_names[m_btstl] = instr_names[m_btst];
#endif /* asm_no_btst_suffix */
        return;
}


/*
    INITIALIZE OUTPUT ROUTINES

    This routine copies the table of global register names into the
    table of global register names and
*/

void
init_output(void)
{
        memcpy(reg_names, glob_reg_names, sizeof(glob_reg_names));
#ifdef SYSV_ABI
        {
                char *r = reg_names[REG_A0];
                reg_names[REG_A0] = reg_names[REG_A1];
                reg_names[REG_A1] = r;
        }
#endif /* SYS_ABI */
        all_mach_ins = null;
        current_ins = null;
        return;
}


#ifdef EBUG

extern bool seek_line;
extern int seek_line_no;


/*
    OUTPUT NEW LINE (DEBUG MODE ONLY)

    In debug mode a count is keep of the current line number in the
    output file to allow stopping the debugger at a given line.  Normally
    outnl is a macro which just outputs a newline character.
*/

void
outnl(void)
{
        static int line_no = 0;
        outc('\n');
        line_no++;
        if (seek_line && line_no == seek_line_no) {
                warning("Line %d reached", line_no);
                breakpoint();
        }
        return;
}

#endif /* EBUG */