ftp.nice.ch/pub/next/developer/hardware/dsp/gcc56k.N.s.tar.gz#/gcc56-01/out-dsp5616.c

This is out-dsp5616.c in view mode; [Download] [Up]

/*
    GCC5616 -- GCC 1.40 machine description for DSP5616 processors
    Copyright (C) 1991 Andrew Sterian and Bell-Northern Research

    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; either version 1, or (at your option)
    any later version.

    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.

    You should have received a copy of the GNU General Public License
    along with the GCC distribution; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

	Contact the author at 'asterian@bnr.ca' or post to the 'comp.dsp'
	newsgroup.
*/

#include <stdio.h>
#include <ctype.h>
#include "tree.h"
#include "flags.h"

void print_operand_address(FILE *, rtx, int);
void extendReg(rtx op);

static int regValid[FIRST_PSEUDO_REGISTER];

int
hard_regno_mode_ok(int regnum, enum machine_mode mode)
{
    if ((mode == DFmode) || (mode == DImode)) {
        switch(regnum) {
         case 0:    /* X1 */
         case 2:    /* Y1 */
         case 5:    /* A1 */
         case 8:    /* B1 */
             return 1;  /* All of the above are OK */

         default:
             return 0;
        }
    }

	/* Ensure A2, B2 don't get allocated */
	if ((regnum==4) || (regnum==7))
		return 0;

    if ((mode == QImode) || (mode == VOIDmode) || (mode == HImode) ||
		(mode == SFmode) || (mode == SImode)) {
        return 1;   /* All regs OK */
    }

	error("aux-output: Mode %u unknown",mode);
	return 0;   /* No other modes accepted */
}

int
regno_reg_class(int regnum)
{
    switch (regnum) {
	 case 0: case 2:					/* X1, Y1 */
		return ALU_DATA_REGS1;

	 case 1: case 3:					/* X0, Y0 */
		return ALU_DATA_REGS0;

	/* A0 and B0 have to be included because of address arithmetic */
	/* e.g. (set (reg:HI 8) (subreg:HI (reg:DI 5) 1)) */
	/* transforms into (set (reg:HI 8) (reg:HI 6)) */
	 case 5: case 8:					/* A1, B1 */
		return ACC_REGS_HI;

	 case 6: case 9:					/* A0, B0 */
		return ACC_REGS_LO;

     case 10: case 11: case 12: case 13: /* R0..R3 */
        return ADDRESS_REGS;

     case 18: case 19: case 20: case 21: /* N0..N3 */
        return INDEX_REGS;

     default:				/* A2, B2, Possibly pseudo-regs */
        return NO_REGS;
    }
}

int
reg_class_from_letter(int c)
{
        /* May not use the letters m o < > r i n I J K F G H s g p */

    switch(c) {
	 case 'A':		/* A1, A0, B1, B0, X1, X0, Y1, Y0 */
		return ALU_REGS;

	 case 'C':		/* X1, X0, Y1, Y0, A1, B1 */
		return MATHOP_REGS;

     case 'a':		/* R0..R3 */
        return ADDRESS_REGS;

	 case 'b':		/* R0..R3, A1, B1 */
		return BASE_REGS;

     /* For muldi operations -- not used */
	 case 'c':		/* Y1, Y0 */
		return ALU_DATA_REGSY;

     case 'd':		/* X1, X0, Y1, Y0 */
        return ALU_DATA_REGS;

	 /* Used for multiply instructions where mpy X1,X1,A is illegal */
     case 'e':      /* X0, Y1, Y0 */
        return ALU_DATA_REGS2;

     /* Used for multiply instructions where mpy X1,X1,A is illegal */
     case 'f':      /* X0, X1 */
        return ALU_DATA_REGSX;

	 /* Not used */
     case 'x':      /* N0..N7 */
        return INDEX_REGS;

     case 'l':      /* A1, B1 */
        return ACC_REGS_HI;

	 case 'u':		/* A1, A0, B1, B0 */
		return ACC_REGS;

     default:
        return NO_REGS;
    }
}

void
function_prologue(FILE *file, int size)
{
	fprintf(file,"\tmove R2,X:-(R3)\n");
	fprintf(file,"\tmove R3,R2\n");

	if (size > 1)
		fprintf(file,"\tmove #%d,N3\n",-size);

    fprintf(file,"\tmove SSH,X:-(R3)\n");

	if (size > 1)
		fprintf(file,"\tmove (R3)+N3\n");
	else if (size==1)
		fprintf(file,"\tmove (R3)-\n");
}

void
function_epilogue(FILE *file, int size)
{
	fprintf(file,"\tmove R2,R3\n");
	fprintf(file,"\tmove X:-(R2),SSH\n");
	fprintf(file,"\tmove X:(R3)+,R2\n");
	fprintf(file,"\trts\n");
}

/*
 * Codes:
 *      w -- For A and B, output A1 and B1
 *      x -- lower part of 32-bit DFmode or DImode value (X0 instead of X)
 *      y -- upper part of 32-bit DFmode or DImode value (X1 instead of X)
 *		z -- Extension part of 40-bit reg. (A2 instead of A)
 *      p -- Address or (Reg)+ -- first word of a DI/DFmode
 *      q -- Address+1 or (Reg)- -- second word of a DI/DFmode
 *		r -- Don't output size information
 *      s -- Candidate for movei instruction (different from GCC56K)
 *		Z -- Don't output memory space and output R0 instead of (R0)
 */
void
print_operand(FILE *filePtr, rtx operand, int code)
{
    enum machine_mode mode=GET_MODE(operand);
	char *p;
	int c;
    int outval;
	int modeIsLong, modeIsInt, isAcc;
	rtx memop;

	modeIsLong = ((mode==DFmode) || (mode==DImode));
	modeIsInt = ((mode==QImode) || (mode==HImode) || (mode==SImode));

    switch(GET_CODE(operand)) {
     case REG:
        p = reg_names[REGNO(operand)];
        c = *p;
		isAcc = ((REGNO(operand)==5) || (REGNO(operand)==8));

		if (REGNO(operand)==6) {
			fprintf(filePtr,"A0");
        } else if (REGNO(operand)==9) {
			fprintf(filePtr,"B0");
        } else if (code=='x') {
            fprintf(filePtr,"%c0",c);
        } else if (code=='y') {
            fprintf(filePtr,"%c1",c);
		} else if ((code=='z') && isAcc) {
			fprintf(filePtr,"%c2",c);
		} else if (modeIsLong) {
			fprintf(filePtr,"%c",c);
		} else if (isAcc) {
			/* Changed from 'if (modeIsInt && code=='w')' */
			/* 21Oct91 */
			if (code=='w')
				fprintf(filePtr,"%c1",c);
			else
				fprintf(filePtr,"%c",c);
        } else {
			fprintf(filePtr, "%s", p);
		}
        break;

     case MEM:
		memop = XEXP(operand,0);
		if (code != 'Z') {
			fprintf(filePtr,"X:");
		}
        print_operand_address(filePtr, memop, code);
        break;

	 case CONST_INT:
		outval = INTVAL(operand);

        outval &= 0xFFFF;

		if (code=='r')
			p = "#$%X";
		else if (code=='s' &&
				 (((outval & 0xFF80)==0xFF80) || (!(outval & 0xFF80)))) {
			if (outval & 0xFF80)
				outval |= 0xFFFF0000;
			else
				outval &= 0xFF;
        	p = "#<%d";
		} else
			p = "#>$%X";

		fprintf(filePtr, p, outval);
		break;

	 case CONST_DOUBLE:
        putc('#', filePtr);
        output_addr_const(filePtr, operand);
		break;

	 case LABEL_REF:
	 case CONST:
		fprintf(filePtr, "#>");
		output_addr_const(filePtr, operand);
		break;

	 case SYMBOL_REF:
		p = XSTR(operand,0);
		fprintf(filePtr,"#>");
		assemble_name(filePtr, p);
		break;

     default:
		fprintf(stderr,"%s:%u Unknown code %s:%u\n",__FILE__,__LINE__,
			rtx_name[GET_CODE(operand)], GET_MODE(operand));
			/* fall-through */

	 case CODE_LABEL:
        output_addr_const(filePtr, operand);
        break;
    }
}

extern FILE *asm_out_file;

void
output_preloadN(rtx operand)
{
    int regnum, val;
	rtx op0,op1;

	if (GET_CODE(operand) != MEM) return;

	operand = XEXP(operand,0);
	if (GET_CODE(operand) != PLUS) return;

	op0 = XEXP(operand,0); op1 = XEXP(operand,1);
	if (GET_CODE(op1) == REG && REGNO(op1)>=10) {
		op0 = op1; op1 = XEXP(operand,0);
	}

   	regnum = REGNO(op0);

    /* (R2+xx) is handled in print_operand_address, no need to preload */
	if (GET_CODE(op1) == CONST_INT) {
    	val = INTVAL(op1);
    	if ((regnum==12) && (val >= -128) && (val < 128)) return;
	}

	fprintf(asm_out_file,"\tmove ");
	print_operand(asm_out_file, op1, 'w');
    fprintf(asm_out_file,",N%u\n",regnum-10);
}

/* In the machine description, code=='p' *must* be followed by a */
/* statement with code=='q' since this routine modifies the */
/* referred-to register */
void
print_operand_address(FILE *filePtr, rtx operand, int code) 
{
	char *p;
	int digit;
	int opval;
	rtx op0,op1;

    switch(GET_CODE(operand)) {
     case REG:
        p = reg_names[REGNO(operand)];
        digit = *(p+1);

        switch(code) {
         case 'p':
            fprintf(filePtr, "(%s)+", p);
            break;

         case 'q':
            fprintf(filePtr, "(%s)-", p);
            break;

         default:
			if (code=='Z')
				fprintf(filePtr, "%s", p);
			else
            	fprintf(filePtr, "(%s)", p);
        }
        break;

     case PRE_DEC:
        p = reg_names[REGNO(XEXP(operand,0))];

        fprintf(filePtr, "-(%s)", p);
        break;

	 case POST_DEC:
		p = reg_names[REGNO(XEXP(operand,0))];

		fprintf(filePtr, "(%s)-", p);
		break;

     case POST_INC:
        p = reg_names[REGNO(XEXP(operand,0))];

        fprintf(filePtr, "(%s)+", p);
        break;

     case PLUS:
		op0=XEXP(operand,0); op1=XEXP(operand,1);
		if (GET_CODE(op1)==REG && REGNO(op1)>=10) {
			op0=op1; op1=XEXP(operand,0);
		}

        p = reg_names[REGNO(op0)];
        digit = *(p+1);

		if (GET_CODE(op1)!=CONST_INT) {
			fprintf(filePtr,"(%s+N%c)", p, digit);
			break;
		}

       	opval = INTVAL(op1);
		if ((code=='q') && (digit=='2') && (opval >= -128) && (opval < 127))
				fprintf(filePtr,"(%s%+d)", p, opval+1);
        else if ((digit=='2') && (opval >= -128) && (opval < 128))
            fprintf(filePtr, "(%s%+d)", p, opval);
        else
            fprintf(filePtr, "(%s+N%c)", p, digit);
        break;

	 case SYMBOL_REF:
		p = XSTR(operand, 0);
		assemble_name(filePtr, p);
        if (code=='q') fprintf(filePtr, "+1");
		break;

	 case CONST_INT:
		opval = INTVAL(operand) & 0xFFFF;
        if (code=='q') opval++;
        fprintf(filePtr, "$%04X", opval);
		break;

	 case CONST:
		output_addr_const(filePtr, operand);
        if (code=='q') fprintf(filePtr, "+1");
		break;

	 default:
        fprintf(filePtr, "%s:%u Unknown code %u", __FILE__, __LINE__,
                                                    GET_CODE(operand));
        error("Internal error");
		break;
    }
}

/* Sometime in the future */
int
legitimize_address(rtx x, rtx oldx, enum machine_mode mode)
{
    return 0;
}

void
select_rtx_section(enum machine_mode mode, rtx exp)
{
	switch(mode) {
     case QImode: case HImode: case SImode: case SFmode:
	 case DFmode: case DImode:
	 case BLKmode:
		x_const_section();
		break;

	 default:
		fprintf(stderr,"%s:%u Don\'t know how to handle mode %s\n",
				__FILE__,__LINE__,GET_MODE_NAME(mode));
		error("Internal error");
	}
}

void
select_section(tree x)
{
	unsigned char code=TREE_CODE(x);
	enum machine_mode mode=DECL_MODE(x);
	int constant = 0;

	switch(code) {
	 case STRING_CST:
	 	if (flag_writable_strings) {
			x_data_section();
		} else {
			x_const_section();
		}
		break;

	 case INTEGER_CST:
	 case REAL_CST:
		mode = DECL_MODE(TREE_TYPE(x));
		switch(mode) {
		 case QImode: case HImode: case SImode: case SFmode:
		 case DFmode: case DImode:
		 case BLKmode:
			x_const_section();
			break;

		 default:
			fprintf(stderr,"%s:%u Unknown mode %d/%s\n",__FILE__,__LINE__,
										mode,GET_MODE_NAME(mode));
#ifdef DEBUG56
			debug_tree(x);
#endif
			break;
		}
		break;

	 case VAR_DECL:
		/* No storage allocated for external references */
		if (TREE_EXTERNAL(x)) return;

		if (TREE_STATIC(x) && TREE_READONLY(x)) constant=1;

		if (constant)
			x_const_section();
		else
			x_data_section();
		break;

	 default:
		fprintf(stderr,"%s:%u Unknown code %u\n",__FILE__,__LINE__,code);
        error("Internal error");
#ifdef DEBUG56
		debug_tree(x);
#endif
		break;
	}
}

typedef union { double d; int i[2]; } float_u;

double ints_to_double(rtx op)
{
	float_u u;

	u.i[0] = CONST_DOUBLE_LOW(op);
	u.i[1] = CONST_DOUBLE_HIGH(op);
	return u.d;
}

int
const_double_to_SF(rtx op, int *intVal)
{
	register int i, outval;
	double inval, mask;

	inval = ints_to_double(op);
	if ((inval < -1.0) || (inval >= 1.0)) return 0;

	outval = 0;
	if (inval < 0) {
		outval = 1;
		inval += 1.0;
	}
	for (i=0, mask=0.5; i<15; i++, mask /= 2.0) {
		outval <<= 1;
		if (inval >= mask) {
			outval |= 1;
			inval -= mask;
		}
	}
	*intVal = outval;
	return 1;
}

int
const_inval_to_DF(double inval, int *intHigh, int *intLow)
{
	register int i, outval1, outval2;
	double mask;

	if ((inval < -1.0) || (inval >= 1.0)) return 0;

	outval1 = 0;
	if (inval < 0) {
		outval1 = 1;
		inval += 1.0;
	}

	for (i=0, mask=0.5; i<15; i++, mask /= 2.0) {
		outval1 <<= 1;
		if (inval >= mask) {
			outval1 |= 1;
			inval -= mask;
		}
	}
	for (i=0, outval2=0; i<16; i++, mask /= 2.0) {
		outval2 <<= 1;
		if (inval >= mask) {
			outval2 |= 1;
			inval -= mask;
		}
	}
	*intHigh = outval1;
	*intLow = outval2;
	return 1;
}

int
const_double_to_DF(rtx op, int *intHigh, int *intLow)
{
	return const_inval_to_DF(ints_to_double(op), intHigh, intLow);
}

int
const_double_ok_for_letter_p(rtx op, int c)
{
	double f;

	if ((c=='F' || c=='G')) {
		f = ints_to_double(op);
		if ((f >= -1.0) && (f < 1.0))
			return 1;
	}
	return 0;
}

int
constant_address_p(rtx op)
{
	int code = GET_CODE(op);

	return ((code==LABEL_REF) || (code==SYMBOL_REF) || (code==CONST_INT) ||
			(code==CONST));
}

void
asm_output_ascii(FILE *filePtr, char *name, int len)
{
	int i,j,k;

	while (len) {
		fprintf(filePtr, "\tdc\t");
		for (i=0; (i<10) && (len); i++,len--) {
			if (i) fprintf(filePtr, ",$%02X", *name++); else
			fprintf(filePtr, "$%02X", *name++);
		}

		for (k=i; k<10; k++)
			fprintf(filePtr, "    ");

		fprintf(filePtr, " ;\'");
		for (j=0; j<i; j++) {
			if (isprint(name[j-i])) fprintf(filePtr,"%c",name[j-i]);
			else fprintf(filePtr,".");
		}
		fprintf(filePtr,"\'\n");
	}
}

char *
remove_punct(char *name)
{
	static char outname[40];
	int i;

	/* Skip over leading punctuation */
	while (!isalnum(*name)) name++;

	for (i=0; (i<39) && *name; name++) {
		outname[i++] = (isalnum(*name) ? *name : '_');
	}
	outname[i] = 0;
	return outname;
}

/*
This routine returns the output string for general math ops. which
match the following pattern:	
 (set (match_operand:mode 0 "general_operand" "=l")
	(mathop:mode (match_operand:mode 1 "general_operand" "0")
				 (match_operand:mode 2 "general_operand" "dl")))
*/

#define ADD_56K 	0
#define SUB_56K 	1

char *
mathOpInsn(rtx *operands, int code)
{
	int reg0 = REGNO(operands[0]);
    int reg1;
    enum machine_mode mode=GET_MODE(operands[0]);

    /* Must be a value of 1 or -1 and only for integer modes */
    if (GET_CODE(operands[2]) == CONST_INT) {
		int intVal = INTVAL(operands[2]);

		if (!intVal) return "; +0";
		if (code==ADD_56K && intVal==1 || code==SUB_56K && intVal==-1)
            return ((mode==DImode) ? "inc %0" : "inc24 %0");
		else if (code==ADD_56K && intVal==-1 || code==SUB_56K && intVal==1)
            return ((mode==DImode) ? "dec %0" : "dec24 %0");
		else {
#if DEBUG56
			fprintf(stderr,"%s:%u Unknown combination\n",__FILE__,__LINE__);
#endif
			error("Internal error");
			return "???";
		}
    }

    reg1 = REGNO(operands[2]);
	switch(code) {
     case ADD_56K:
		if (reg0==reg1) {
			extendReg(operands[0]);
			regValid[reg0] = 0;
			return "asl  %0";
		} else {
			return "add  %2,%0";
		}

	 case SUB_56K:
		if (reg0==reg1) {
			return "clr  %0";
		} else {
			return "sub  %2,%0";
		}
	}
}

int
class_max_nregs(enum reg_class class, enum machine_mode mode)
{
	switch(class) {
	 case ALU_DATA_REGS0: case ALU_DATA_REGS1: case ALU_DATA_REGSX:
	 case ALU_DATA_REGSY: case ACC_REGS_HI: case ACC_REGS:
	 case ALU_DATA_REGS2: case ALU_DATA_REGS: case MATHOP_REGS:
	 case BASE_REGS: case ALU_REGS: case GENERAL_REGS: case GENERAL_REGS2:
	 case ALL_REGS:
		return (((mode==DImode) || (mode==DFmode)) ? 2 : 1);

	 case ACC_REGS_LO: case ADDRESS_REGS: case INDEX_REGS: case AGU_REGS:
	 	return 1;

	 default:
		fprintf(stderr,"%s:%u Unexpected class %u\n",__FILE__,__LINE__,
											class);
		error("Internal error");
		return 2;
	}
}

/* We use to clobber A0 or B0 for every INSN that writes to A or B */
int
insn_clobbers_regno_p(rtx insn, int regnum)
{
	rtx op=PATTERN(insn);
	int regdest;

	/* Only real INSN's clobber A0/B0 mysteriously */
	if (GET_CODE(insn) != INSN) return 0;

	/* Check for an assignment to register A1 (5) or B1 (8) */
	if (GET_CODE(op) != SET) return 0;
	if (GET_CODE(XEXP(op,0)) != REG) return 0;

	regdest=REGNO(XEXP(op,0));
	if ((regdest != 5) && (regdest != 8)) return 0;

	/* Is reg-to-check related to our destination register ? */
	if (regnum != (regdest+1)) return 0;

	/* Assume all assignments to A or B clobber A0 or B0 */
	return 1;
}

void
double_reg_to_memory(rtx operands[])
{
	int reg0;
	rtx op1 = XEXP(operands[0],0);
	rtx opB, opIx;
	rtx xoperands[2];
	int code = GET_CODE(op1);
	int intVal;

	output_preloadN(operands[0]);

	switch(code) {
	 case REG:
	 case POST_INC:
		output_asm_insn( "move %y1,%p0", operands);
		output_asm_insn( "move %x1,%q0", operands);
		return;

	 case POST_DEC:
		reg0 = REGNO(XEXP(op1,0));
		fprintf(asm_out_file, "\tlea  (%s)+,%s\n", reg_names[reg0],
													reg_names[reg0]);
		output_asm_insn( "move %x1,%p0", operands);
		output_asm_insn( "move %y1,%q0", operands);
		fprintf(asm_out_file, "\tmove (%s)-\n", reg_names[reg0]);
		return;

	 case PRE_DEC:
		output_asm_insn( "move %x1,%p0", operands);
		output_asm_insn( "move %y1,%q0", operands);
		return;

	 case PLUS:
    	output_asm_insn( "move %y1,%p0", operands);

		opIx = XEXP(op1, 1);	/* The index */
		opB  = XEXP(op1, 0); 	/* The base */
		if (GET_CODE(opIx)==REG && REGNO(opIx)>=10) {
			opB=opIx; opIx=XEXP(op1,0);
		}

		reg0 = REGNO(opB);
		if (GET_CODE(opIx)==CONST_INT) {
			intVal = INTVAL(opIx);
			if ((reg0==12) && (intVal >= -128) && (intVal < 127)) {
				output_asm_insn( "move %x1,%q0", operands);
				return;
			}
		}

		fprintf(asm_out_file, "\tlea  (%s)+,%s\n", reg_names[reg0],
													reg_names[reg0]);
		output_asm_insn( "move %x1,%q0", operands);
		fprintf(asm_out_file, "\tmove (%s)-\n", reg_names[reg0]);
		return;

	 case SYMBOL_REF:
	 case CONST_INT:
	 case CONST:
		output_asm_insn( "move %y1,%p0", operands);
		output_asm_insn( "move %x1,%q0", operands);
		return;

	 default:
		fprintf(stderr,"%s:%u Unknown code %d\n",__FILE__,__LINE__,code);
		error("Internal error");
	}
	return;
}

/* This routine must also take care of the move subreg:DI->SI RTL */
/* It is assumed that if the destination reg is not longmode, it */
/* is a result of this kind of RTL and only the low word of the DI */
/* is required. */
void
double_reg_from_memory(rtx operands[])
{
	int reg0,reg1;
	rtx op1 = XEXP(operands[1],0);
	rtx opB, opIx;
	int code = GET_CODE(op1);
	int intVal;
	enum machine_mode destMode = GET_MODE(operands[0]);
	int destModeIsLong;

	destModeIsLong = ((destMode==DImode) || (destMode==DFmode));

	output_preloadN(operands[1]);

	reg0 = REGNO(operands[0]);
	switch(code) {
	 case REG:
	 case POST_INC:
		if (destModeIsLong) {
			if (reg0 < 4) 
				output_asm_insn( "move %p1,%y0", operands);
			else
				output_asm_insn( "move %p1,%0", operands);
			output_asm_insn( "move %q1,%x0", operands);
		} else {
			/* Special subreg RTL case */
			output_asm_insn( "move %p1,%0 ;dummy", operands);
			output_asm_insn( "move %q1,%0", operands);
		}
		return;

	 case POST_DEC:
		reg1 = REGNO(XEXP(op1,0));
		fprintf(asm_out_file, "\tlea  (%s)+,%s\n", reg_names[reg1],
													reg_names[reg1]);
		if (destModeIsLong) {
			if (reg0 < 4)
				output_asm_insn( "move %p1,%y0", operands);
			else
				output_asm_insn( "move %p1,%0", operands);
			output_asm_insn( "move %q1,%x0", operands);
		} else {
			/* Special subreg RTL case */
			output_asm_insn( "move %p1,%0", operands);
			fprintf(asm_out_file, "\tmove (%s)- ;dummy\n", reg_names[reg1]);
		}
		fprintf(asm_out_file, "\tmove (%s)-\n", reg_names[reg1]);
		return;

	 case PRE_DEC:
		reg1 = REGNO(XEXP(op1,0));
		if (destModeIsLong) {
			output_asm_insn( "move %p1,%x0", operands);
			output_asm_insn( "move %q1,%y0", operands);
			if (reg0 > 3)
				output_asm_insn( "ext  %0", operands);
		} else {
			/* Special subreg RTL case */
			output_asm_insn( "move %p1,%0", operands);
			fprintf(asm_out_file, "\tmove (%s)- ;dummy\n", reg_names[reg1]);
		}
		return;

	 case PLUS:
		if (destModeIsLong)
			if (reg0 < 4)
    			output_asm_insn( "move %p1,%y0", operands);
			else
				output_asm_insn( "move %p1,%0", operands);

		opIx = XEXP(op1, 1); /* The index */
		opB  = XEXP(op1, 0); /* The base */
		if (GET_CODE(opIx)==REG && REGNO(opIx)>=10) {
			opB=opIx; opIx=XEXP(op1,0);
		}

		reg0 = REGNO(opB);
		if (GET_CODE(opIx)==CONST_INT) {
			intVal = INTVAL(opIx);
			if ((reg0==12) && (intVal >= -128) && (intVal < 127)) {
				if (destModeIsLong)
					output_asm_insn( "move %q1,%x0", operands);
				else
					/* Special subreg RTL case */
					output_asm_insn( "move %q1,%0", operands);
				return;
			}
		}
			/* This is inefficient if destModeIsLong is FALSE */
			/* and opIx is CONST_INT...just add one to INTVAL */
		fprintf(asm_out_file, "\tlea  (%s)+,%s\n", reg_names[reg0],
													reg_names[reg0]);
		if (destModeIsLong)
			output_asm_insn( "move %q1,%x0", operands);
		else
			/* Special subreg RTL case */
			output_asm_insn( "move %q1,%0", operands);
		fprintf(asm_out_file, "\tmove (%s)-\n", reg_names[reg0]);
		return;

	 case SYMBOL_REF:
	 case CONST_INT:
	 case CONST:
		if (destModeIsLong) {
			if (reg0 < 4)
				output_asm_insn( "move %p1,%y0", operands);
			else
				output_asm_insn( "move %p1,%0", operands);
			output_asm_insn( "move %q1,%x0", operands);
		} else
			/* Special subreg RTL case */
			output_asm_insn( "move %q1,%0", operands);
		return;

	 default:
		fprintf(stderr,"%s:%u Unknown code %d\n",__FILE__,__LINE__,code);
		error("Internal error");
	}
	return;
}

void
asm_output_double(FILE *filePtr, double value)
{
	int intHigh, intLow;

	if (const_inval_to_DF(value, &intHigh, &intLow)) {
		fprintf(filePtr,"\tdc\t$%04X\n",intHigh);
		fprintf(filePtr,"\tdc\t$%04X\n",intLow);
	} else {
		error("Floating point constant out of bounds");
	}
}

void
asm_output_float(FILE *filePtr, double value)
{
	if (value < 1.0 && value >= -1.0) {
		fprintf(filePtr,"\tdc\t%1.10lf\n",value);
	} else {
		error("Floating point constant out of bounds");
	}
}

void
asm_output_double_int(FILE *filePtr, rtx exp)
{
	int intHigh, intLow;

	if (GET_CODE(exp) == CONST_DOUBLE)
		intLow = CONST_DOUBLE_LOW(exp);
	else
		intLow = INTVAL(exp);

	intHigh = (intLow >> 16) & 0xFFFF;
	intLow &= 0xFFFF;

	fprintf(filePtr,"\tdc\t$%04X\n",intHigh);
	fprintf(filePtr,"\tdc\t$%04X\n",intLow);
}

#define ASHL_56K   		0
#define ASHR_56K   		1
#define LSHL_56K		2
#define LSHR_56K		3
#define ROTL_56K		4
#define ROTR_56K		5

char *
shiftOpInsn(rtx operands[], int op)
{
	int plus1=0, plus2=0, plus4=0, plus16=0;

	if (GET_CODE(operands[2]) == CONST_INT) {
		int intVal=INTVAL(operands[2]);
		if (!intVal) return "; shift 0";
		plus1 = (intVal == 1);
		plus2 = (intVal == 2);
		plus4 = (intVal == 4);
		plus16 = (intVal == 16);
	}
	
    if (operands[2] == const0_rtx) {
		return "; shift 0";
	}

	extendReg(operands[0]);
	regValid[REGNO(operands[0])] = 0;

	switch(op) {
	 case ASHL_56K:
		if (plus1) return "asl  %0";
		else if (plus2) return "asl  %0\n\tasl  %0";
		else if (plus4) return "asl4 %0";
		else return "rep  %r2\n\tasl  %0";

	 case ASHR_56K:
		if (plus1) return "asr  %0";
		else if (plus2) return "asr  %0\n\tasr  %0";
		else if (plus4) return "asr4 %0";
		else if (plus16) return "asr16 %0";
		else return "rep  %r2\n\tasr  %0";
    
	 case LSHL_56K:
		if (plus1) return "lsl  %0";
		else if (plus2) return "lsl  %0\n\tlsl  %0";
		else return "rep  %r2\n\tlsl  %0";

	 case LSHR_56K:
		if (plus1) return "lsr  %0";
		else if (plus2) return "lsr  %0\n\tlsr  %0";
		else return "rep  %r2\n\tlsr  %0";

	 case ROTL_56K:
		if (plus1) return "rol  %0";
		else if (plus2) return "rol  %0\n\trol  %0";
		else return "rep  %r2\n\trol  %0";

	 case ROTR_56K:
		if (plus1) return "ror  %0";
		else if (plus2) return "ror  %0\n\tror  %0";
		else return "rep  %r2\n\tror  %0";
	}
}

int
allRegsInvalid(void)
{
	regValid[5] = regValid[8] = 0;
}

void
extendReg(rtx op)
{
	int regnum = REGNO(op);
	enum machine_mode mode=GET_MODE(op);
	int modeIsLong=((mode==DImode) || (mode==DFmode));

	if (!TARGET_SAFECOMPARE || ((regnum != 5) && (regnum != 8))) return;

	if (!regValid[regnum]) {
		if (modeIsLong) {
			output_asm_insn("ext  %0 ;sc", &op);
		} else {
			output_asm_insn("move %w0,%0 ;sc", &op);
		}
		regValid[regnum]=1;
	}
}

void
asm_output_labelref(FILE *filePtr, char *name)
{
	while(*name && *name == '_') name++;
	fprintf(filePtr,"%s",name);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.