/*
	File:		cgswas.c
	Author:		John van Groningen
	Machine:	Sun 4
	At:			University of Nijmegen
*/

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

#include "cgport.h"
#include "cgrconst.h"
#include "cgtypes.h"
#include "cg.h"
#include "cgiconst.h"
#include "cgcode.h"
#include "cgswas.h"

#define GAS

#define IO_BUF_SIZE 8192

#define SP_G5

static FILE *assembly_file;

static void w_as_newline (VOID)
{
	putc ('\n',assembly_file);
}

static void w_as_opcode (char *opcode)
{
	fprintf (assembly_file,"\t%s\t",opcode);
}

static void w_as_instruction_without_parameters (char *opcode)
{
	fprintf (assembly_file,"\t%s\n",opcode);
}

static void w_as_define_local_label (int label_number)
{
	fprintf (assembly_file,"l_%d:\n",label_number);
}

static void w_as_define_internal_label (int label_number)
{
	fprintf (assembly_file,"i_%d:\n",label_number);
}

void w_as_internal_label_value (int label_id)
{
	fprintf (assembly_file,"\t.word\ti_%d\n",label_id);
}

static int in_data_section;

#ifdef FUNCTION_LEVEL_LINKING
void w_as_new_data_module (void)
{
}
#endif

void w_as_to_data_section (VOID)
{
	if (!in_data_section){
		in_data_section=1;
		w_as_instruction_without_parameters (".data");
	}
}

static void w_as_to_code_section (VOID)
{
	if (in_data_section){
		in_data_section=0;
		w_as_instruction_without_parameters (".text");
	}
}

void w_as_word_in_data_section (int n)
{
	w_as_to_data_section();
	w_as_opcode (".half");
	fprintf (assembly_file,"%d",n);
	w_as_newline();
}

void w_as_long_in_data_section (int n)
{
	w_as_to_data_section();
	w_as_opcode (".word");
	fprintf (assembly_file,"%d",n);
	w_as_newline();
}

void w_as_label_in_data_section (char *label_name)
{
	w_as_to_data_section ();
	fprintf (assembly_file,"\t.word\t%s\n",label_name);
}

static void w_as_label_in_code_section (char *label_name)
{
	w_as_to_code_section ();
	fprintf (assembly_file,"\t.word\t%s\n",label_name);
}

void w_as_descriptor_in_data_section (char *label_name)
{
	w_as_to_data_section ();
	fprintf (assembly_file,"\t.word\t%s+2\n",label_name);
}

#define MAX_BYTES_PER_LINE 16

static int w_as_data (register int n,register unsigned char *data,register int length)
{
	register int i,in_string;
	
	in_string=0;
	
	for (i=0; i<length; ++i){
		int c;
		
		if (n>=MAX_BYTES_PER_LINE){
			if (in_string){
				putc ('\"',assembly_file);
				in_string=0;
			}
			w_as_newline();
			n=0;
		}
		
		c=data[i];
		if (isalnum (c) || c=='_' || c==' '){
			if (!in_string){
				if (n!=0)
					w_as_newline();
				w_as_opcode (".ascii");
				putc ('\"',assembly_file);
				in_string=1;
			}
			putc (c,assembly_file);
		} else {
			if (n==0)
				w_as_opcode (".byte");
			else {
				if (in_string){
					putc ('\"',assembly_file);
					w_as_newline();
					w_as_opcode (".byte");
					in_string=0;
				} else
					putc (',',assembly_file);
			}

			fprintf (assembly_file,"0x%02x",c);
		}
		++n;
	}
	
	if (in_string){
		putc ('\"',assembly_file);
		w_as_newline();
		return 0;
	} else
		return n;
}

static int w_as_zeros (register int n,register int length)
{
	register int i;
	
	for (i=0; i<length; ++i){
		if (n>=MAX_BYTES_PER_LINE){
			w_as_newline();
			n=0;
		}
		if (n==0)
			w_as_opcode (".byte");
		else
			putc (',',assembly_file);
		fprintf (assembly_file,"0");
		++n;
	}
	return n;
}

void w_as_c_string_in_data_section (char *string,int length)
{
	register int n;
	
	w_as_to_data_section();
		
	n=w_as_data (0,string,length);
	n=w_as_zeros (n,4-(length & 3));
/*	n=w_as_zeros (n,length & 1 ? 1 : 2); */
	if (n>0)
		w_as_newline();
}

void w_as_define_data_label (int label_number)
{
	w_as_to_data_section();
	
	w_as_define_local_label (label_number);
}

void w_as_labeled_c_string_in_data_section (char *string,int length,int label_number)
{
	int n;
	
	w_as_to_data_section();
	
	w_as_define_local_label (label_number);
	
	n=w_as_data (0,string,length);
	if (length & 3)
		n=w_as_zeros (n,4-(length & 3));
	else
		n=w_as_zeros (n,4);
	if (n>0)
		w_as_newline();
}

void w_as_abc_string_in_data_section (char *string,int length)
{
	int n;
	
	w_as_to_data_section();
	
	w_as_opcode (".word");
	fprintf (assembly_file,"%d\n",length);
	n=w_as_data (0,string,length);
	if (length & 3)
		n=w_as_zeros (n,4-(length & 3));
	if (n>0)
		w_as_newline();
}

void w_as_descriptor_string_in_data_section
	(char *string,int length,int string_label_id,LABEL *string_label)
{
	int n;
	
	w_as_to_data_section();
	
	w_as_define_internal_label (string_label_id);
	w_as_define_local_label (string_label->label_number);

	w_as_opcode (".word");
	fprintf (assembly_file,"%d\n",length);
	n=w_as_data (0,string,length);
	if (length & 3)
		n=w_as_zeros (n,4-(length & 3));
	if (n>0)
		w_as_newline();
}

enum { SIZE_LONG, SIZE_WORD, SIZE_BYTE };

static void w_as_opcode_and_d (char *opcode)
{
	fprintf (assembly_file,"\t%sd\t",opcode);
}

static void w_as_label (char *label)
{
	int c;
	
	while (c=*label++,c!=0)
		putc (c,assembly_file);
}

static void w_as_colon (VOID)
{
	putc (':',assembly_file);
}

static void w_as_define_label_name (char *label_name)
{
	w_as_label (label_name);
	w_as_colon();
	w_as_newline();
}

void w_as_define_label (LABEL *label)
{
	if (label->label_flags & EXPORT_LABEL){
		w_as_opcode (".global");
		w_as_label (label->label_name);
		w_as_newline();
	}
			
	w_as_label (label->label_name);
	w_as_colon();
	w_as_newline();
}

static void w_as_local_label (int label_number)
{
	fprintf (assembly_file,"l_%d",label_number);
}

static void w_as_internal_label (int label_number)
{
	fprintf (assembly_file,"i_%d",label_number);
}

static void w_as_immediate (LONG i)
{
	fprintf (assembly_file,"%ld",i);
}

void w_as_abc_string_and_label_in_data_section (char *string,int length,char *label_name)
{
	int n;
	
	w_as_to_data_section();
	
	w_as_define_label_name (label_name);
	
	w_as_opcode (".long");
	fprintf (assembly_file,"%d\n",length);
	n=w_as_data (0,string,length);
	if (length & 3)
		n=w_as_zeros (n,4-(length & 3));
	if (n>0)
		w_as_newline();
}

static char register_type  [32]="ggggggiiggiiiiiilllllllloooooooo";
#ifdef SP_G5
static char register_number[32]="01234767565432100123456701234567";
#else
static char register_number[32]="01234567765432100123456701234567";
#endif

#define REGISTER_G0 (-16)
#define REGISTER_G5 (-11)
#define REGISTER_I6 (-10)
#define REGISTER_O0 8
#define REGISTER_O1 9
#define REGISTER_O7 15

static void w_as_indirect (int i,int reg)
{
	if (i!=0){
		if (i>0)
			fprintf (assembly_file,"[%%%c%c+%d]",register_type[reg+16],register_number[reg+16],i);
		else
			fprintf (assembly_file,"[%%%c%c-%d]",register_type[reg+16],register_number[reg+16],-i);
	} else
		fprintf (assembly_file,"[%%%c%c]",register_type[reg+16],register_number[reg+16]);
}

static void w_as_indexed (int offset,struct index_registers *index_registers)
{
	int reg1,reg2;
	
	if (offset!=0)
		internal_error_in_function ("w_as_indexed");

	reg1=index_registers->a_reg.r;
	reg2=index_registers->d_reg.r;
	fprintf (assembly_file,"[%%%c%c+%%%c%c]",
			 register_type[reg1+16],register_number[reg1+16],
			 register_type[reg2+16],register_number[reg2+16]);
}

static void w_as_register (int reg)
{
	putc ('%',assembly_file);
	putc (register_type[reg+16],assembly_file);
	putc (register_number[reg+16],assembly_file);
}

static void w_as_register_comma (int reg)
{
	putc ('%',assembly_file);
	putc (register_type[reg+16],assembly_file);
	putc (register_number[reg+16],assembly_file);
	putc (',',assembly_file);
}

static void w_as_register_newline (int reg)
{
	putc ('%',assembly_file);
	putc (register_type[reg+16],assembly_file);
	putc (register_number[reg+16],assembly_file);
	putc ('\n',assembly_file);
}

void w_as_c_string_and_label_in_code_section (char *string,int length,char *label_name)
{
	register int n;
	
/*	w_as_to_code_section(); */
	w_as_to_data_section();
	
	w_as_define_label_name (label_name);
	
	n=w_as_data (0,string,length);
	n=w_as_zeros (n,4-(length & 3));
	if (n>0)
		w_as_newline();
}

static void w_as_scratch_register (void)
{
	w_as_register (REGISTER_O0);
}

static void w_as_fp_register (int fp_reg)
{
	fprintf (assembly_file,"%%f%d",fp_reg);
}

static void w_as_opcode_descriptor (char *opcode,char *label_name,int arity)
{
	w_as_opcode (opcode);
	fprintf (assembly_file,"%s+0x%x",label_name,2/* 0x80000000 */+(arity<<3));
}
		
static void w_as_comma (VOID)
{
	putc (',',assembly_file);
}

static void w_as_opcode_indirect (char *opcode,int offset,int reg)
{
	if (((offset << (32-13)) >> (32-13))==offset){
		w_as_opcode (opcode);
		w_as_indirect (offset,reg);
	 } else {
		w_as_opcode ("sethi");
		w_as_immediate (offset>>10);
		w_as_comma();
		w_as_register (REGISTER_O1);
		w_as_newline();

		w_as_opcode ("add");
		w_as_register (REGISTER_O1);
		w_as_comma();
		w_as_register (reg);
		w_as_comma();
		w_as_register (REGISTER_O1);
		w_as_newline();

		w_as_opcode (opcode);
		w_as_indirect (offset & 1023,REGISTER_O1);
	}
}

static void w_as_parameter (struct parameter *parameter)
{
	switch (parameter->parameter_type){
		case P_REGISTER:
			w_as_register (parameter->parameter_data.reg.r);
			break;
		case P_LABEL:
			if (parameter->parameter_data.l->label_number!=0)
				w_as_local_label (parameter->parameter_data.l->label_number);
			else
				w_as_label (parameter->parameter_data.l->label_name);
			break;
		case P_INDIRECT:
			w_as_indirect (parameter->parameter_offset,parameter->parameter_data.reg.r);
			break;
		case P_INDEXED:
			w_as_indexed (parameter->parameter_offset,parameter->parameter_data.ir);
			break;
		case P_IMMEDIATE:
			fprintf (assembly_file,"%ld",parameter->parameter_data.i);
			break;
		case P_F_REGISTER:
			fprintf (assembly_file,"%%f%d",parameter->parameter_data.reg.r<<1);
			break;
		default:
			internal_error_in_function ("w_as_parameter");
	}
}

static void w_as_ld_parameter (struct parameter *parameter)
{
	if (parameter->parameter_type!=P_INDIRECT){
		w_as_opcode ("ld");
		w_as_parameter (parameter);
	} else
		w_as_opcode_indirect ("ld",parameter->parameter_offset,parameter->parameter_data.reg.r);
}

static void w_as_opcode_parameter (char *opcode,struct parameter *parameter)
{
	if (parameter->parameter_type!=P_INDIRECT){
		w_as_opcode (opcode);
		w_as_parameter (parameter);
	} else
		w_as_opcode_indirect (opcode,parameter->parameter_offset,parameter->parameter_data.reg.r);
}

static void w_as_jump_parameter (register struct parameter *parameter)
{
	switch (parameter->parameter_type){
		case P_LABEL:
			if (parameter->parameter_data.l->label_number!=0)
				w_as_local_label (parameter->parameter_data.l->label_number);
			else
				w_as_label (parameter->parameter_data.l->label_name);
			break;
		case P_INDIRECT:
		{
			int offset,reg;

			offset=parameter->parameter_offset;
			reg=parameter->parameter_data.reg.r;

			if (offset!=0)
				fprintf (assembly_file,"%%%c%c+%d",
						 register_type[reg+16],register_number[reg+16],offset);
			else
				fprintf (assembly_file,"%%%c%c",
						 register_type[reg+16],register_number[reg+16]);
			break;
		}
		default:
			internal_error_in_function ("w_as_jump_parameter");
	}
}

static struct parameter w_as_register_parameter (struct parameter parameter,int size_flag)
{
	switch (parameter.parameter_type){
		case P_DESCRIPTOR_NUMBER:
			w_as_opcode_descriptor ("set",
				parameter.parameter_data.l->label_name,
				parameter.parameter_offset
			);
			w_as_comma();
			w_as_register (REGISTER_O0);
			w_as_newline();

			parameter.parameter_type=P_REGISTER;
			parameter.parameter_data.reg.r=REGISTER_O0;
			break;
		case P_IMMEDIATE:
			if (parameter.parameter_data.i==0){
				parameter.parameter_type=P_REGISTER;
				parameter.parameter_data.reg.r=REGISTER_G0;
			} else {
				w_as_opcode ("set");
				w_as_immediate (parameter.parameter_data.i);
				w_as_comma();
				w_as_register (REGISTER_O0);
				w_as_newline();

				parameter.parameter_type=P_REGISTER;
				parameter.parameter_data.reg.r=REGISTER_O0;
			}
			break;
		case P_REGISTER:
			break;
		case P_INDIRECT:
			w_as_opcode_indirect (	size_flag==SIZE_LONG ? "ld" : size_flag==SIZE_WORD ? "ldsh" : "ldub",
									parameter.parameter_offset,parameter.parameter_data.reg.r);
			w_as_comma();
			w_as_register (REGISTER_O0);
			w_as_newline();

			parameter.parameter_type=P_REGISTER;
			parameter.parameter_data.reg.r=REGISTER_O0;
			break;
		case P_INDEXED:
			w_as_opcode (size_flag==SIZE_LONG ? "ld" : size_flag==SIZE_WORD ? "ldsh" : "ldub");
			w_as_indexed (parameter.parameter_offset,parameter.parameter_data.ir);
			w_as_comma();
			w_as_register (REGISTER_O0);
			w_as_newline();

			parameter.parameter_type=P_REGISTER;
			parameter.parameter_data.reg.r=REGISTER_O0;
			break;
		default:
			internal_error_in_function ("w_as_register_parameter");
	}
	return parameter;
}

static void w_as_move_instruction (struct instruction *instruction,int size_flag)
{
	switch (instruction->instruction_parameters[1].parameter_type){
		case P_REGISTER:
			switch (instruction->instruction_parameters[0].parameter_type){
				case P_DESCRIPTOR_NUMBER:
					w_as_opcode_descriptor ("set",
						instruction->instruction_parameters[0].parameter_data.l->label_name,
						instruction->instruction_parameters[0].parameter_offset
					);
					break;
				case P_IMMEDIATE:
					w_as_opcode ("set");
					w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
					break;
				case P_REGISTER:
					w_as_opcode ("mov");
					w_as_register (instruction->instruction_parameters[0].parameter_data.reg.r);
					break;
				case P_INDIRECT:
					w_as_opcode_indirect (	size_flag==SIZE_LONG ? "ld" : size_flag==SIZE_WORD ? "ldsh" : "ldub",
											instruction->instruction_parameters[0].parameter_offset,
											instruction->instruction_parameters[0].parameter_data.reg.r);
					break;
				case P_INDEXED:
					w_as_opcode (size_flag==SIZE_LONG ? "ld" : size_flag==SIZE_WORD ? "ldsh" : "ldub");
					w_as_indexed (instruction->instruction_parameters[0].parameter_offset,
								  instruction->instruction_parameters[0].parameter_data.ir);
					break;
				default:
					internal_error_in_function ("w_as_move_instruction");
					return;
			}
			w_as_comma();
			w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_newline();
			return;
		case P_INDIRECT:
		{
			struct parameter parameter_0;
			int offset;

			parameter_0=w_as_register_parameter (instruction->instruction_parameters[0],size_flag);

			offset=instruction->instruction_parameters[1].parameter_offset;

			if (((offset << (32-13)) >> (32-13))==offset){
				w_as_opcode (size_flag==SIZE_LONG ? "st" : size_flag==SIZE_WORD ? "sth" : "stb");
				w_as_register (parameter_0.parameter_data.reg.r);
				w_as_comma();
				w_as_indirect (offset,instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_newline();
			} else {
				w_as_opcode ("sethi");
				w_as_immediate (offset>>10);
				w_as_comma();
				w_as_register (REGISTER_O1);
				w_as_newline();
		
				w_as_opcode ("add");
				w_as_register (REGISTER_O1);
				w_as_comma();
				w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_comma();
				w_as_register (REGISTER_O1);
				w_as_newline();

				w_as_opcode (size_flag==SIZE_LONG ? "st" : size_flag==SIZE_WORD ? "sth" : "stb");
				w_as_register (parameter_0.parameter_data.reg.r);
				w_as_comma();
				w_as_indirect (offset & 1023,REGISTER_O1);
				w_as_newline();
			}

			return;
		}
		case P_INDEXED:
		{
			struct parameter parameter_0;

			parameter_0=w_as_register_parameter (instruction->instruction_parameters[0],size_flag);

			w_as_opcode (size_flag==SIZE_LONG ? "st" : size_flag==SIZE_WORD ? "sth" : "stb");
			w_as_register (parameter_0.parameter_data.reg.r);
			w_as_comma();
			w_as_indexed (instruction->instruction_parameters[1].parameter_offset,
						  instruction->instruction_parameters[1].parameter_data.ir);
			w_as_newline();
			return;
		}
		default:
			internal_error_in_function ("w_as_move_instruction");
	}
}

static void w_as_lea_instruction (struct instruction *instruction)
{
	if (instruction->instruction_parameters[1].parameter_type==P_REGISTER)
		switch (instruction->instruction_parameters[0].parameter_type){
			case P_LABEL:
				w_as_opcode ("set");
				w_as_parameter (&instruction->instruction_parameters[0]);
				if (instruction->instruction_parameters[0].parameter_offset!=0){
					int offset;
		
					offset=instruction->instruction_parameters[0].parameter_offset;
					fprintf (assembly_file,offset>=0 ? "+%d" : "%d",offset);
				}
				w_as_comma();
				w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_newline();
				return;
			case P_INDIRECT:
				w_as_opcode ("add");
				w_as_register (instruction->instruction_parameters[0].parameter_data.reg.r);
				w_as_comma();
				w_as_immediate (instruction->instruction_parameters[0].parameter_offset);
				w_as_comma();
				w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_newline();
				return;
			case P_INDEXED:
				w_as_opcode ("add");
				w_as_register (instruction->instruction_parameters[0].parameter_data.ir->a_reg.r);
				w_as_comma();
				w_as_register (instruction->instruction_parameters[0].parameter_data.ir->d_reg.r);
				w_as_comma();
				w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_newline();
				return;				
		}

	internal_error_in_function ("w_as_lea_instruction");
}

static void w_as_i_instruction (struct instruction *instruction,char *opcode)
{
	w_as_opcode (opcode);
	w_as_register (instruction->instruction_parameters[0].parameter_data.reg.r);
	w_as_comma();
	w_as_immediate (instruction->instruction_parameters[2].parameter_data.i);
	w_as_comma();
	w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_newline();
}

static void w_as_tryadic_instruction (struct instruction *instruction,char *opcode)
{
	struct parameter parameter;

	parameter=instruction->instruction_parameters[0];

	switch (parameter.parameter_type){
		case P_IMMEDIATE:
			if ((unsigned)(parameter.parameter_data.i+4096)>=(unsigned)8192){
				w_as_opcode ("set");
				w_as_immediate (parameter.parameter_data.i);
				w_as_comma();
				w_as_register (REGISTER_O0);
				w_as_newline();

				parameter.parameter_type=P_REGISTER;
				parameter.parameter_data.reg.r=REGISTER_O0;
			}
			break;
		case P_INDIRECT:
			w_as_opcode_indirect ("ld",parameter.parameter_offset,parameter.parameter_data.reg.r);
			w_as_comma();
			w_as_register (REGISTER_O0);
			w_as_newline();

			parameter.parameter_type=P_REGISTER;
			parameter.parameter_data.reg.r=REGISTER_O0;
			break;
		case P_INDEXED:
			w_as_opcode ("ld");
			w_as_indexed (parameter.parameter_offset,parameter.parameter_data.ir);
			w_as_comma();
			w_as_register (REGISTER_O0);
			w_as_newline();

			parameter.parameter_type=P_REGISTER;
			parameter.parameter_data.reg.r=REGISTER_O0;			
	}

	w_as_opcode (opcode);
	w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_comma();
	w_as_parameter (&parameter);
	w_as_comma();
	w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_newline();
}

static void w_as_add_instruction (struct instruction *instruction)
{
	switch (instruction->instruction_parameters[0].parameter_type){
		case P_REGISTER:
			w_as_opcode ("add");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register_comma (instruction->instruction_parameters[0].parameter_data.reg.r);
			w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
			return;
		case P_IMMEDIATE:
			if ((unsigned)(instruction->instruction_parameters[0].parameter_data.i+4096)<(unsigned)8192){
				w_as_opcode ("inc");
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_comma();
				w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
				return;
			} else {
				w_as_opcode ("set");
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_comma();
				w_as_scratch_register();
				w_as_newline();

				w_as_opcode ("add");
				w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_scratch_register();
				w_as_comma();
				w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
				return;
			}
		default:
			w_as_ld_parameter (&instruction->instruction_parameters[0]);
			w_as_comma();
			w_as_scratch_register();
			w_as_newline();

			w_as_opcode ("add");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_scratch_register();
			w_as_comma();
			w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
	}
}

static void w_as_add_o_instruction (struct instruction *instruction)
{
	switch (instruction->instruction_parameters[0].parameter_type){
		case P_REGISTER:
			w_as_opcode ("addcc");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register_comma (instruction->instruction_parameters[0].parameter_data.reg.r);
			w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
			return;
		case P_IMMEDIATE:
			if ((unsigned)(instruction->instruction_parameters[0].parameter_data.i+4096)<(unsigned)8192){
				w_as_opcode ("inccc");
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_comma();
				w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
				return;
			} else {
				w_as_opcode ("set");
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_comma();
				w_as_scratch_register();
				w_as_newline();

				w_as_opcode ("addcc");
				w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_scratch_register();
				w_as_comma();
				w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
				return;
			}
		default:
			w_as_ld_parameter (&instruction->instruction_parameters[0]);
			w_as_comma();
			w_as_scratch_register();
			w_as_newline();

			w_as_opcode ("addcc");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_scratch_register();
			w_as_comma();
			w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
	}
}

static void w_as_sub_instruction (struct instruction *instruction)
{
	switch (instruction->instruction_parameters[0].parameter_type){
		case P_REGISTER:
			w_as_opcode ("sub");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register_comma (instruction->instruction_parameters[0].parameter_data.reg.r);
			w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
			return;
		case P_IMMEDIATE:
			if ((unsigned)(instruction->instruction_parameters[0].parameter_data.i+4096)<(unsigned)8192){
				w_as_opcode ("dec");
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_comma();
				w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
				return;
			} else {
				w_as_opcode ("set");
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_comma();
				w_as_scratch_register();
				w_as_newline();

				w_as_opcode ("sub");
				w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_scratch_register();
				w_as_comma();
				w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
				return;
			}
		default:
			w_as_ld_parameter (&instruction->instruction_parameters[0]);
			w_as_comma();
			w_as_scratch_register();
			w_as_newline();

			w_as_opcode ("sub");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_scratch_register();
			w_as_comma();
			w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
	}
}

static void w_as_sub_o_instruction (struct instruction *instruction)
{
	switch (instruction->instruction_parameters[0].parameter_type){
		case P_REGISTER:
			w_as_opcode ("subcc");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register_comma (instruction->instruction_parameters[0].parameter_data.reg.r);
			w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
			return;
		case P_IMMEDIATE:
			if ((unsigned)(instruction->instruction_parameters[0].parameter_data.i+4096)<(unsigned)8192){
				w_as_opcode ("deccc");
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_comma();
				w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
				return;
			} else {
				w_as_opcode ("set");
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_comma();
				w_as_scratch_register();
				w_as_newline();

				w_as_opcode ("subcc");
				w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_scratch_register();
				w_as_comma();
				w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
				return;
			}
		default:
			w_as_ld_parameter (&instruction->instruction_parameters[0]);
			w_as_comma();
			w_as_scratch_register();
			w_as_newline();

			w_as_opcode ("subcc");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_scratch_register();
			w_as_comma();
			w_as_register_newline (instruction->instruction_parameters[1].parameter_data.reg.r);
	}
}

static void w_as_cmp_instruction (struct instruction *instruction,int size_flag)
{
	struct parameter parameter_0,parameter_1;

	parameter_0=instruction->instruction_parameters[0];
	parameter_1=instruction->instruction_parameters[1];

	if (parameter_1.parameter_type==P_INDIRECT){
		w_as_opcode_parameter (size_flag==SIZE_LONG ? "ld" : "ldsh",&parameter_1);
		w_as_comma();
		w_as_register (REGISTER_O1);
		w_as_newline();

		parameter_1.parameter_type=P_REGISTER;
		parameter_1.parameter_data.reg.r=REGISTER_O1;
	} else if (parameter_1.parameter_type==P_INDEXED){
		w_as_opcode (size_flag==SIZE_LONG ? "ld" : "ldsh");

		w_as_parameter (&parameter_1);
		w_as_comma();
		w_as_register (REGISTER_O1);
		w_as_newline();

		parameter_1.parameter_type=P_REGISTER;
		parameter_1.parameter_data.reg.r=REGISTER_O1;
	}

	switch (parameter_0.parameter_type){
		case P_DESCRIPTOR_NUMBER:
			w_as_opcode_descriptor ("set",
				parameter_0.parameter_data.l->label_name,
				parameter_0.parameter_offset
			);
			w_as_comma();
			w_as_register (REGISTER_O0);
			w_as_newline();

			parameter_0.parameter_type=P_REGISTER;
			parameter_0.parameter_data.reg.r=REGISTER_O0;
			break;
		case P_IMMEDIATE:
			if ((unsigned)(parameter_0.parameter_data.i+4096)>=(unsigned)8192){
				w_as_opcode ("set");
				w_as_parameter (&parameter_0);
				w_as_comma();
				w_as_register (REGISTER_O0);
				w_as_newline();

				parameter_0.parameter_type=P_REGISTER;
				parameter_0.parameter_data.reg.r=REGISTER_O0;
			}
			break;
		case P_REGISTER:
			break;
		default:
			switch (size_flag){
				case SIZE_WORD:
					w_as_opcode_parameter ("ldsh",&parameter_0);
					break;
				case SIZE_LONG:
					w_as_opcode_parameter ("ld",&parameter_0);
					break;
				default:
					internal_error_in_function ("w_as_cmp_instruction");
			}
			w_as_comma();
			w_as_register (REGISTER_O0);
			w_as_newline();

			parameter_0.parameter_type=P_REGISTER;
			parameter_0.parameter_data.reg.r=REGISTER_O0;
	}

	w_as_opcode ("cmp");
	w_as_parameter (&parameter_1);
	w_as_comma();
	w_as_parameter (&parameter_0);
	w_as_newline();
}

static void w_as_tst_instruction (struct instruction *instruction,int size_flag)
{
	struct parameter parameter_0;

	parameter_0=w_as_register_parameter (instruction->instruction_parameters[0],size_flag);

	w_as_opcode (size_flag==SIZE_LONG ? "tst" : "tstb");
	w_as_register (parameter_0.parameter_data.reg.r);
	w_as_newline();
}

static void w_as_btst_instruction (struct instruction *instruction)
{
	w_as_opcode ("btst");
	w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
	w_as_comma();
	w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_newline();
}

void w_as_jmp_instruction (struct instruction *instruction)
{
	if (instruction->instruction_parameters[0].parameter_type==P_LABEL){
		if (instruction->instruction_parameters[0].parameter_data.l->label_flags & LOCAL_LABEL){
			w_as_opcode ("b,a");
			w_as_jump_parameter (&instruction->instruction_parameters[0]);
			w_as_newline();

			return;
		} else
			w_as_opcode ("call");
	} else
		w_as_opcode ("jmp");

	w_as_jump_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();

	w_as_instruction_without_parameters ("nop");
}

static void w_as_branch_instruction (struct instruction *instruction,char *opcode)
{
	w_as_opcode (opcode);
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();

	w_as_instruction_without_parameters ("nop");
}

static void w_as_index_error_branch_instruction (struct instruction *instruction)
{
/*	w_as_opcode ("bleu"); */
	w_as_opcode ("bcs");
	fprintf (assembly_file,".+12");
	w_as_newline();
	
	w_as_instruction_without_parameters ("nop");

	w_as_opcode ("b,a");
	w_as_jump_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();
}

static void w_as_float_branch_instruction (struct instruction *instruction,char *opcode)
{
	w_as_instruction_without_parameters ("nop");

	w_as_opcode (opcode);
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();

	w_as_instruction_without_parameters ("nop");
}

static char local_c_stack_overflow_label_name[]="l_c_stack_overflow";

/*
static void w_as_check_c_stack (void)
{
	w_as_opcode ("cmp");
	w_as_register (C_STACK_POINTER);
	w_as_comma();
	w_as_register (REGISTER_G5);
	w_as_newline();

	w_as_opcode ("bleu");
	w_as_label (local_c_stack_overflow_label_name);
	w_as_newline();
}
*/

static void w_as_jsr_instruction (struct instruction *instruction)
{
	/*
	if (check_c_stack)
		w_as_check_c_stack();
	*/
	
	w_as_opcode ("call");
	w_as_jump_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();

	if (instruction->instruction_parameters[1].parameter_type==P_INDIRECT){
		w_as_opcode ("st");
		w_as_register (REGISTER_O7);
		w_as_comma();
		w_as_indirect (instruction->instruction_parameters[1].parameter_data.i,B_STACK_POINTER);
		w_as_newline();
	} else
		w_as_instruction_without_parameters ("nop");
}

static void w_as_rts_instruction (struct instruction *instruction)
{
	int b_offset;
	
	w_as_opcode_indirect ("ld",instruction->instruction_parameters[0].parameter_offset,B_STACK_POINTER);
	w_as_comma();
	w_as_register (REGISTER_O7);
	w_as_newline();

	w_as_instruction_without_parameters ("retl");

	b_offset=instruction->instruction_parameters[1].parameter_data.i;
	if (b_offset==0)
		w_as_instruction_without_parameters ("nop");
	else {
		if (b_offset<0){
			w_as_opcode ("dec");
			w_as_immediate (-b_offset);
		} else {
			w_as_opcode ("inc");
			w_as_immediate (b_offset);
		}
		w_as_comma();
		w_as_register (B_STACK_POINTER);
		w_as_newline();
	}
}

static void w_as_set_condition_instruction (struct instruction *instruction,char *opcode)
{
	w_as_opcode ("clr");
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();

	w_as_opcode (opcode);
	fprintf (assembly_file,".+8");
	w_as_newline();

	w_as_opcode ("mov");
	fprintf (assembly_file,"-1");
	w_as_comma();
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();
}

static void w_as_set_float_condition_instruction (struct instruction *instruction,char *opcode)
{
	w_as_instruction_without_parameters ("nop");
	w_as_set_condition_instruction (instruction,opcode);
}

static void w_as_mod_instruction (struct instruction *instruction)
{
	w_as_opcode ("mov");
	w_as_parameter (&instruction->instruction_parameters[2]);
	w_as_comma();
	w_as_register (REGISTER_O0);
	w_as_newline();

	switch (instruction->instruction_parameters[0].parameter_type){
		case P_REGISTER:
			w_as_opcode ("call");
			fprintf (assembly_file,".rem");
			w_as_newline();

			w_as_opcode ("mov");
			w_as_parameter (&instruction->instruction_parameters[0]);
			break;
		case P_IMMEDIATE:
		{
			long v;
			
			v=instruction->instruction_parameters[0].parameter_data.i;

			if ((unsigned)(v+4096) < (unsigned)8192){
				w_as_opcode ("call");
				fprintf (assembly_file,".rem");
				w_as_newline();
	
				w_as_opcode ("mov");
				w_as_immediate (v);
			} else {
				w_as_opcode ("sethi");
				fprintf (assembly_file,"%%hi ");
#ifdef GAS
				fprintf (assembly_file,"(");
#endif
				w_as_immediate (v);
#ifdef GAS
				fprintf (assembly_file,")");
#endif
				w_as_comma();
				w_as_register (REGISTER_O1);
				w_as_newline();

				w_as_opcode ("call");
				fprintf (assembly_file,".rem");
				w_as_newline();
			
				w_as_opcode ("or");
				w_as_register (REGISTER_O1);
				w_as_comma();
				fprintf (assembly_file,"%%lo ");
#ifdef GAS
				fprintf (assembly_file,"(");
#endif
				w_as_immediate (v);
#ifdef GAS
				fprintf (assembly_file,")");
#endif
			}
			break;
		}
		default:
			w_as_opcode ("call");
			fprintf (assembly_file,".rem");
			w_as_newline();

			w_as_ld_parameter (&instruction->instruction_parameters[0]);
	}
    w_as_comma();
	w_as_register (REGISTER_O1);
	w_as_newline();

	w_as_opcode ("mov");
	w_as_register (REGISTER_O0);
    w_as_comma();
    w_as_parameter (&instruction->instruction_parameters[1]);
	w_as_newline();
}

static void w_as_mul_or_div_instruction (struct instruction *instruction,char *mul_or_div_label_name)
{
	w_as_opcode ("mov");
	w_as_parameter (&instruction->instruction_parameters[1]);
	w_as_comma();
	w_as_register (REGISTER_O0);
	w_as_newline();

	switch (instruction->instruction_parameters[0].parameter_type){
		case P_REGISTER:
			w_as_opcode ("call");
			fprintf (assembly_file,mul_or_div_label_name);
			w_as_newline();

			w_as_opcode ("mov");
			w_as_parameter (&instruction->instruction_parameters[0]);
			break;
		case P_IMMEDIATE:
		{
			long v;
			
			v=instruction->instruction_parameters[0].parameter_data.i;

			if ((unsigned)(v+4096) < (unsigned)8192){
				w_as_opcode ("call");
				fprintf (assembly_file,mul_or_div_label_name);
				w_as_newline();
	
				w_as_opcode ("mov");
				w_as_immediate (v);
			} else {
				w_as_opcode ("sethi");
				fprintf (assembly_file,"%%hi ");
#ifdef GAS
				fprintf (assembly_file,"(");
#endif
				w_as_immediate (v);
#ifdef GAS
				fprintf (assembly_file,")");
#endif
				w_as_comma();
				w_as_register (REGISTER_O1);
				w_as_newline();

				w_as_opcode ("call");
				fprintf (assembly_file,mul_or_div_label_name);
				w_as_newline();
			
				w_as_opcode ("or");
				w_as_register (REGISTER_O1);
				w_as_comma();
				fprintf (assembly_file,"%%lo ");
#ifdef GAS
				fprintf (assembly_file,"(");
#endif
				w_as_immediate (v);
#ifdef GAS
				fprintf (assembly_file,")");
#endif
			}
			break;
		}
		default:
			w_as_opcode ("call");
			fprintf (assembly_file,mul_or_div_label_name);
			w_as_newline();

			w_as_ld_parameter (&instruction->instruction_parameters[0]);
	}

    w_as_comma();
	w_as_register (REGISTER_O1);
    w_as_newline();

	w_as_opcode ("mov");
	w_as_register (REGISTER_O0);
    w_as_comma();
    w_as_parameter (&instruction->instruction_parameters[1]);
	w_as_newline();
}

static void w_as_word_instruction (struct instruction *instruction)
{
	fprintf (assembly_file,"\t.word\t%d\n",
			(int)instruction->instruction_parameters[0].parameter_data.i);
}

static struct parameter w_as_float_parameter (struct parameter parameter)
{
	switch (parameter.parameter_type){
		case P_F_IMMEDIATE:
		{
			int label_number=next_label_id++;

			w_as_to_data_section();

			w_as_opcode (".align");
			fprintf (assembly_file,"8");
			w_as_newline();

			w_as_define_internal_label (label_number);

			w_as_opcode (".double");
			fprintf (assembly_file,"0r%.20e",*parameter.parameter_data.r);
			w_as_newline();

			w_as_to_code_section();

			w_as_opcode ("sethi");
#ifdef GAS
			fprintf (assembly_file,"%%hi (i_%d),%%o0",label_number);
#else
			fprintf (assembly_file,"%%hi i_%d,%%o0",label_number);
#endif
			w_as_newline();

			w_as_opcode ("ldd");
#ifdef GAS
			fprintf (assembly_file,"[%%o0+%%lo (i_%d)]",label_number);
#else
			fprintf (assembly_file,"[%%o0+%%lo i_%d]",label_number);
#endif
			w_as_comma();
			w_as_fp_register (30);
			w_as_newline();

			parameter.parameter_type=P_F_REGISTER;
			parameter.parameter_data.reg.r=15;
			break;
		}
		case P_INDIRECT:
			w_as_opcode ("ld");
			w_as_indirect (parameter.parameter_offset,parameter.parameter_data.reg.r);
			w_as_comma();
			w_as_fp_register (30);
			w_as_newline();

			w_as_opcode ("ld");
			w_as_indirect (parameter.parameter_offset+4,parameter.parameter_data.reg.r);
			w_as_comma();
			w_as_fp_register (31);
			w_as_newline();

			parameter.parameter_type=P_F_REGISTER;
			parameter.parameter_data.reg.r=15;
			break;
		case P_INDEXED:
			w_as_opcode ("add");
			w_as_register (parameter.parameter_data.ir->a_reg.r);
			w_as_comma();
			w_as_register (parameter.parameter_data.ir->d_reg.r);
			w_as_comma();
			w_as_register (REGISTER_O0);
			w_as_newline();

			w_as_opcode ("ld");
			w_as_indirect (parameter.parameter_offset>>2,REGISTER_O0);
			w_as_comma();
			w_as_fp_register (30);
			w_as_newline();

			w_as_opcode ("ld");
			w_as_indirect ((parameter.parameter_offset>>2)+4,REGISTER_O0);
			w_as_comma();
			w_as_fp_register (31);
			w_as_newline();

			parameter.parameter_type=P_F_REGISTER;
			parameter.parameter_data.reg.r=15;
			break;
	}
	return parameter;
}

static void w_as_compare_float_instruction (struct instruction *instruction)
{
	struct parameter parameter_0;

	parameter_0=w_as_float_parameter (instruction->instruction_parameters[0]);

	w_as_opcode_and_d ("fcmp");
	
	w_as_parameter (&instruction->instruction_parameters[1]);
	w_as_comma();
	w_as_parameter (&parameter_0);
	w_as_newline();
}

static void w_as_sqrt_float_instruction (struct instruction *instruction)
{
	struct parameter parameter_0;

	parameter_0=w_as_float_parameter (instruction->instruction_parameters[0]);

	w_as_opcode_and_d ("fsqrt");
	
	w_as_parameter (&parameter_0);
	w_as_comma();
	w_as_parameter (&instruction->instruction_parameters[1]);
	w_as_newline();
}

static void w_as_neg_float_instruction (struct instruction *instruction)
{
	struct parameter parameter_0;
	int freg1,freg2;

	parameter_0=w_as_float_parameter (instruction->instruction_parameters[0]);

	freg1=parameter_0.parameter_data.reg.r;
	freg2=instruction->instruction_parameters[1].parameter_data.reg.r;

	w_as_opcode ("fnegs");
	w_as_fp_register (freg1<<1);
	w_as_comma();
	w_as_fp_register (freg2<<1);
	w_as_newline();

	if (freg1!=freg2){
		w_as_opcode ("fmovs");
		w_as_fp_register ((freg1<<1)+1);
		w_as_comma();
		w_as_fp_register ((freg2<<1)+1);
		w_as_newline();
	}
}

static void w_as_tryadic_float_instruction (struct instruction *instruction,char *opcode)
{
	struct parameter parameter_0;

	parameter_0=w_as_float_parameter (instruction->instruction_parameters[0]);

	w_as_opcode_and_d (opcode);

	w_as_parameter (&instruction->instruction_parameters[1]);
	w_as_comma();
	w_as_parameter (&parameter_0);
	w_as_comma();
	w_as_parameter (&instruction->instruction_parameters[1]);
	w_as_newline();
}

static struct instruction *w_as_fmove_instruction (struct instruction *instruction)
{
	switch (instruction->instruction_parameters[1].parameter_type){
		case P_F_REGISTER:
			switch (instruction->instruction_parameters[0].parameter_type){
				case P_F_REGISTER:
				{
					struct instruction *next_instruction;
					int reg0,reg1;
					
					reg0=instruction->instruction_parameters[0].parameter_data.reg.r;
					reg1=instruction->instruction_parameters[1].parameter_data.reg.r;
					
					next_instruction=instruction->instruction_next;
					if (next_instruction)
						switch (next_instruction->instruction_icode){
							case IFADD: case IFSUB: case IFMUL: case IFDIV: case IFREM:
								if (next_instruction->instruction_parameters[1].parameter_data.reg.r==reg1)
								{
									struct parameter parameter_0;
									int reg_s;

									parameter_0=w_as_float_parameter (next_instruction->instruction_parameters[0]);

									reg_s=parameter_0.parameter_data.reg.r;
									if (reg_s==reg1)
										reg_s=reg0;
									
									switch (next_instruction->instruction_icode){
										case IFADD:
											w_as_opcode_and_d ("fadd");
											break;
										case IFSUB:
											w_as_opcode_and_d ("fsub");
											break;
										case IFMUL:
											w_as_opcode_and_d ("fmul");
											break;
										case IFDIV:
											w_as_opcode_and_d ("fdiv");
											break;
										case IFREM:
											w_as_opcode_and_d ("frem");
									}
									w_as_fp_register (reg0<<1);
									w_as_comma();
									w_as_fp_register (reg_s<<1);
									w_as_comma();
									w_as_fp_register (reg1<<1);
									w_as_newline();
									
									return next_instruction;
								}
						}

					w_as_opcode ("fmovs");
					w_as_fp_register (reg0<<1);
					w_as_comma();
					w_as_fp_register (reg1<<1);
					w_as_newline();

					w_as_opcode ("fmovs");
					w_as_fp_register ((reg0<<1)+1);
					w_as_comma();
					w_as_fp_register ((reg1<<1)+1);
					w_as_newline();
				
					return instruction;
				}
				case P_INDIRECT:
					w_as_opcode ("ld");
					w_as_indirect (instruction->instruction_parameters[0].parameter_offset,
								   instruction->instruction_parameters[0].parameter_data.reg.r);
					w_as_comma();
					w_as_fp_register (instruction->instruction_parameters[1].parameter_data.reg.r<<1);
					w_as_newline();

					w_as_opcode ("ld");
					w_as_indirect (instruction->instruction_parameters[0].parameter_offset+4,
								   instruction->instruction_parameters[0].parameter_data.reg.r);
					w_as_comma();
					w_as_fp_register ((instruction->instruction_parameters[1].parameter_data.reg.r<<1)+1);
					w_as_newline();

					return instruction;
				case P_INDEXED:
					w_as_opcode ("add");
					w_as_register (instruction->instruction_parameters[0].parameter_data.ir->a_reg.r);
					w_as_comma();
					w_as_register (instruction->instruction_parameters[0].parameter_data.ir->d_reg.r);
					w_as_comma();
					w_as_register (REGISTER_O0);
					w_as_newline();

					w_as_opcode ("ld");
					w_as_indirect (instruction->instruction_parameters[0].parameter_offset>>2,REGISTER_O0);
					w_as_comma();
					w_as_fp_register (instruction->instruction_parameters[1].parameter_data.reg.r<<1);
					w_as_newline();
		
					w_as_opcode ("ld");
					w_as_indirect ((instruction->instruction_parameters[0].parameter_offset>>2)+4,REGISTER_O0);
					w_as_comma();
					w_as_fp_register ((instruction->instruction_parameters[1].parameter_data.reg.r<<1)+1);
					w_as_newline();

					return instruction;
				case P_F_IMMEDIATE:
				{
					int label_number=next_label_id++;

					w_as_to_data_section();

					w_as_opcode (".align");
					fprintf (assembly_file,"8");
					w_as_newline();

					w_as_define_internal_label (label_number);

					w_as_opcode (".double");
					fprintf (assembly_file,"0r%.20e",*instruction->instruction_parameters[0].parameter_data.r);
					w_as_newline();

					w_as_to_code_section();

					w_as_opcode ("sethi");
#ifdef GAS
					fprintf (assembly_file,"%%hi (i_%d),%%o0",label_number);
#else
					fprintf (assembly_file,"%%hi i_%d,%%o0",label_number);
#endif
					w_as_newline();

					w_as_opcode ("ldd");
#ifdef GAS
					fprintf (assembly_file,"[%%o0+%%lo (i_%d)]",label_number);
#else
					fprintf (assembly_file,"[%%o0+%%lo i_%d]",label_number);
#endif
					w_as_comma();
					w_as_fp_register (instruction->instruction_parameters[1].parameter_data.reg.r<<1);
					w_as_newline();
					return instruction;
				}
			}
			break;
		case P_INDIRECT:
			if (instruction->instruction_parameters[0].parameter_type==P_F_REGISTER){
				w_as_opcode ("st");
				w_as_fp_register (instruction->instruction_parameters[0].parameter_data.reg.r<<1);
				w_as_comma();
				w_as_indirect (instruction->instruction_parameters[1].parameter_offset,
							   instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_newline();

				w_as_opcode ("st");
				w_as_fp_register ((instruction->instruction_parameters[0].parameter_data.reg.r<<1)+1);
				w_as_comma();
				w_as_indirect (instruction->instruction_parameters[1].parameter_offset+4,
							   instruction->instruction_parameters[1].parameter_data.reg.r);
				w_as_newline();
				return instruction;
			}
			break;
		case P_INDEXED:
			if (instruction->instruction_parameters[0].parameter_type==P_F_REGISTER){
				w_as_opcode ("add");
				w_as_register (instruction->instruction_parameters[1].parameter_data.ir->a_reg.r);
				w_as_comma();
				w_as_register (instruction->instruction_parameters[1].parameter_data.ir->d_reg.r);
				w_as_comma();
				w_as_register (REGISTER_O0);
				w_as_newline();

				w_as_opcode ("st");
				w_as_fp_register (instruction->instruction_parameters[0].parameter_data.reg.r<<1);
				w_as_comma();
				w_as_indirect (instruction->instruction_parameters[1].parameter_offset>>2,REGISTER_O0);
				w_as_newline();

				w_as_opcode ("st");
				w_as_fp_register ((instruction->instruction_parameters[0].parameter_data.reg.r<<1)+1);
				w_as_comma();
				w_as_indirect ((instruction->instruction_parameters[1].parameter_offset>>2)+4,REGISTER_O0);
				w_as_newline();

				return instruction;
			}
	}
	internal_error_in_function ("w_as_fmove_instruction");
	return instruction;
}

static void w_as_fmove_hl_instruction (struct instruction *instruction)
{
	if (instruction->instruction_parameters[0].parameter_type==P_INDIRECT
		&&	instruction->instruction_parameters[1].parameter_type==P_F_REGISTER)
	{
		w_as_opcode_indirect ("ld",
								instruction->instruction_parameters[0].parameter_offset,
						 		instruction->instruction_parameters[0].parameter_data.reg.r);
		w_as_comma();
		if (instruction->instruction_icode==IFMOVEHI)
			w_as_fp_register (instruction->instruction_parameters[1].parameter_data.reg.r<<1);
		else
			w_as_fp_register ((instruction->instruction_parameters[1].parameter_data.reg.r<<1)+1);
	
		w_as_newline();
		return;
	}
	
	internal_error_in_function ("w_as_fmove_instruction");
}

static void w_as_fmovel_instruction (struct instruction *instruction)
{
	if (instruction->instruction_parameters[0].parameter_type==P_F_REGISTER){
		if (instruction->instruction_parameters[1].parameter_type!=P_REGISTER)
			internal_error_in_function ("w_as_fmovel_instruction");

		w_as_opcode ("fdtoi");
		w_as_fp_register (instruction->instruction_parameters[0].parameter_data.reg.r<<1);
		w_as_comma();
		w_as_fp_register (31);
		w_as_newline();

		w_as_opcode ("st");
		w_as_fp_register (31);
		w_as_comma();
		w_as_indirect (-4,REGISTER_I6);
		w_as_newline();

		w_as_opcode ("ld");
		w_as_indirect (-4,REGISTER_I6);
		w_as_comma();
		w_as_register (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_newline();
	} else {
		switch (instruction->instruction_parameters[0].parameter_type){
			case P_REGISTER:
				w_as_opcode ("st");
				w_as_register (instruction->instruction_parameters[0].parameter_data.reg.r);
				w_as_comma();
				w_as_indirect (-4,REGISTER_I6);
				w_as_newline();

				w_as_opcode ("ld");
				w_as_indirect (-4,REGISTER_I6);
				w_as_comma();	
				w_as_fp_register (31);
				w_as_newline();
				break;
			case P_INDIRECT:
				w_as_opcode ("ld");
				w_as_indirect (instruction->instruction_parameters[0].parameter_offset,
							   instruction->instruction_parameters[0].parameter_data.reg.r);
				w_as_comma();
				w_as_fp_register (31);
				w_as_newline();
				break;
			case P_INDEXED:
				w_as_opcode ("ld");
				w_as_indexed (instruction->instruction_parameters[0].parameter_offset,
							  instruction->instruction_parameters[0].parameter_data.ir);
				w_as_comma();
				w_as_fp_register (31);
				w_as_newline();
				break;
			case P_IMMEDIATE:
				w_as_opcode ("set");
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_comma();
				w_as_scratch_register();
				w_as_newline();

				w_as_opcode ("st");
				w_as_scratch_register();
				w_as_comma();
				w_as_indirect (-4,REGISTER_I6);
				w_as_newline();

				w_as_opcode ("ld");
				w_as_indirect (-4,REGISTER_I6);
				w_as_comma();	
				w_as_fp_register (31);
				w_as_newline();
				break;			
			default:
				internal_error_in_function ("w_as_fmovel_instruction");
		}

		w_as_opcode ("fitod");
		w_as_fp_register (31);
		w_as_comma();
		w_as_fp_register (instruction->instruction_parameters[1].parameter_data.reg.r<<1);
		w_as_newline();
	}
}

static void w_as_instructions (register struct instruction *instruction)
{
	while (instruction!=NULL){
		switch (instruction->instruction_icode){
			case IMOVE:
				w_as_move_instruction (instruction,SIZE_LONG);
				break;
			case ILEA:
				w_as_lea_instruction (instruction);
				break;
			case IADD:
				w_as_add_instruction (instruction);
				break;
			case IADDI:
				w_as_i_instruction (instruction,"add");
				break;
			case ISUB:
				w_as_sub_instruction (instruction);
				break;
			case ICMP:
				w_as_cmp_instruction (instruction,SIZE_LONG);
				break;
			case IJMP:
				w_as_jmp_instruction (instruction);
				break;
			case IJSR:
				w_as_jsr_instruction (instruction);
				break;
			case IRTS:
				w_as_rts_instruction (instruction);
				break;
			case IBEQ:
				w_as_branch_instruction (instruction,"be");
				break;
			case IBGE:
				w_as_branch_instruction (instruction,"bge");
				break;
			case IBGT:
				w_as_branch_instruction (instruction,"bg");
				break;
			case IBHS:
				w_as_index_error_branch_instruction (instruction);
				break;
			case IBLE:
				w_as_branch_instruction (instruction,"ble");
				break;
			case IBLT:
				w_as_branch_instruction (instruction,"bl");
				break;
			case IBNE:
				w_as_branch_instruction (instruction,"bne");
				break;
			case IBNO:
				w_as_branch_instruction (instruction,"bvc");
				break;
			case IBO:
				w_as_branch_instruction (instruction,"bvs");
				break;
			case ILSLI:
				w_as_i_instruction (instruction,"sll");
				break;
			case ILSL:
				w_as_tryadic_instruction (instruction,"sll");
				break;
			case ILSR:
				w_as_tryadic_instruction (instruction,"srl");
				break;
			case IASR:
				w_as_tryadic_instruction (instruction,"sra");
				break;
			case IMUL:
				w_as_mul_or_div_instruction (instruction,".mul");
				break;
			case IDIV:
				w_as_mul_or_div_instruction (instruction,".div");
				break;
			case IMOD:
				w_as_mod_instruction (instruction);
				break;
			case IAND:
				w_as_tryadic_instruction (instruction,"and");
				break;
			case IOR:
				w_as_tryadic_instruction (instruction,"or");
				break;
			case IEOR:
				w_as_tryadic_instruction (instruction,"xor");
				break;
			case ISEQ:
				w_as_set_condition_instruction (instruction,"be,a");
				break;
			case ISGE:
				w_as_set_condition_instruction (instruction,"bge,a");
				break;
			case ISGT:
				w_as_set_condition_instruction (instruction,"bg,a");
				break;
			case ISLE:
				w_as_set_condition_instruction (instruction,"ble,a");
				break;
			case ISLT:
				w_as_set_condition_instruction (instruction,"bl,a");
				break;
			case ISNE:
				w_as_set_condition_instruction (instruction,"bne,a");
				break;
			case ISNO:
				w_as_set_condition_instruction (instruction,"bvc,a");
				break;
			case ISO:
				w_as_set_condition_instruction (instruction,"bvs,a");
				break;			
			case ICMPW:
				w_as_cmp_instruction (instruction,SIZE_WORD);
				break;
			case ITST:
				w_as_tst_instruction (instruction,SIZE_LONG);
				break;
			case IBTST:
				w_as_btst_instruction (instruction);
				break;
			case IMOVEW:
				w_as_move_instruction (instruction,SIZE_WORD);
				break;
			case IMOVEB:
				w_as_move_instruction (instruction,SIZE_BYTE);
				break;
			case IFMOVE:
				instruction=w_as_fmove_instruction (instruction);
				break;
			case IFMOVEHI:
			case IFMOVELO:
				w_as_fmove_hl_instruction (instruction);
				break;
			case IFADD:
				w_as_tryadic_float_instruction (instruction,"fadd");
				break;
			case IFSUB:
				w_as_tryadic_float_instruction (instruction,"fsub");
				break;
			case IFCMP:
				w_as_compare_float_instruction (instruction);
				break;
			case IFDIV:
				w_as_tryadic_float_instruction (instruction,"fdiv");
				break;
			case IFMUL:
				w_as_tryadic_float_instruction (instruction,"fmul");
				break;
			case IFREM:
				w_as_tryadic_float_instruction (instruction,"frem");
				break;
			case IFBEQ:
				w_as_float_branch_instruction (instruction,"fbe");
				break;
			case IFBGE:
				w_as_float_branch_instruction (instruction,"fbge");
				break;
			case IFBGT:
				w_as_float_branch_instruction (instruction,"fbg");
				break;
			case IFBLE:
				w_as_float_branch_instruction (instruction,"fble");
				break;
			case IFBLT:
				w_as_float_branch_instruction (instruction,"fbl");
				break;
			case IFBNE:
				w_as_float_branch_instruction (instruction,"fbne");
				break;
			case IFMOVEL:
				w_as_fmovel_instruction (instruction);
				break;
			case IFSQRT:
				w_as_sqrt_float_instruction (instruction);
				break;
			case IFNEG:
				w_as_neg_float_instruction (instruction);
				break;
			case IFSEQ:
				w_as_set_float_condition_instruction (instruction,"fbe,a");
				break;
			case IFSGE:
				w_as_set_float_condition_instruction (instruction,"fbge,a");
				break;
			case IFSGT:
				w_as_set_float_condition_instruction (instruction,"fbg,a");
				break;
			case IFSLE:
				w_as_set_float_condition_instruction (instruction,"fble,a");
				break;
			case IFSLT:
				w_as_set_float_condition_instruction (instruction,"fbl,a");
				break;
			case IFSNE:
				w_as_set_float_condition_instruction (instruction,"fbne,a");
				break;
			case IWORD:
				w_as_word_instruction (instruction);
				break;
			case IADDO:
				w_as_add_o_instruction (instruction);
				break;
			case ISUBO:
				w_as_sub_o_instruction (instruction);
				break;
			case IFTST:
			default:
				internal_error_in_function ("w_as_instructions");
		}
		instruction=instruction->instruction_next;
	}
}

static void w_as_number_of_arguments (int n_node_arguments)
{
	w_as_opcode (".word");
	fprintf (assembly_file,"%d",n_node_arguments);
	w_as_newline();
}


struct call_and_jump {
	struct call_and_jump *cj_next;
	WORD cj_label_id;
	WORD cj_jump_id;
	char *cj_call_label_name;
};

static struct call_and_jump *first_call_and_jump,*last_call_and_jump;

static void w_as_garbage_collect_test (register struct basic_block *block)
{
	LONG n_cells;
	int label_id_1,label_id_2;
	struct call_and_jump *new_call_and_jump;

	label_id_1=next_label_id++;
	label_id_2=next_label_id++;
	
	new_call_and_jump=allocate_memory_from_heap (sizeof (struct call_and_jump));

	new_call_and_jump->cj_next=NULL;
	new_call_and_jump->cj_label_id=label_id_1;
	new_call_and_jump->cj_jump_id=label_id_2;
	
	switch (block->block_n_begin_a_parameter_registers){
		case 0:		new_call_and_jump->cj_call_label_name="collect_0";	break;
		case 1:		new_call_and_jump->cj_call_label_name="collect_1";	break;
		case 2:		new_call_and_jump->cj_call_label_name="collect_2";	break;
		case 3:		new_call_and_jump->cj_call_label_name="collect_3";	break;
		default:	internal_error_in_function ("w_as_garbage_collect_test");
	}
	
	if (first_call_and_jump!=NULL)
		last_call_and_jump->cj_next=new_call_and_jump;
	else
		first_call_and_jump=new_call_and_jump;
	last_call_and_jump=new_call_and_jump;
	
	n_cells=block->block_n_new_heap_cells;	

	if (n_cells<4096){
		w_as_opcode ("deccc");
		w_as_immediate (n_cells);
		w_as_comma();
		w_as_register (REGISTER_D7);
		w_as_newline();
	} else {
		w_as_opcode ("set");
		w_as_immediate (n_cells);
		w_as_comma();
		w_as_scratch_register();
		w_as_newline();

		w_as_opcode ("subcc");
		w_as_register (REGISTER_D7);
		w_as_comma();
		w_as_scratch_register();
		w_as_comma();
		w_as_register (REGISTER_D7);
		w_as_newline();
	}
	
	w_as_opcode ("bcs,a");
	w_as_internal_label (label_id_1);
	w_as_newline ();

	w_as_opcode ("dec");
	w_as_immediate (4);
	w_as_comma();
	w_as_register (B_STACK_POINTER);
	w_as_newline();
	
	w_as_define_internal_label (label_id_2);
}

static void w_as_call_and_jump (struct call_and_jump *call_and_jump)
{
	w_as_define_internal_label (call_and_jump->cj_label_id);

	w_as_opcode ("call");
	w_as_label (call_and_jump->cj_call_label_name);
	w_as_newline();

	w_as_opcode ("st");
	w_as_register (REGISTER_O7);
	w_as_comma();
	w_as_indirect (0,B_STACK_POINTER);
	w_as_newline();
	
	w_as_opcode ("b,a");
	w_as_internal_label (call_and_jump->cj_jump_id);
	w_as_newline();
}

static void w_as_labels (struct block_label *labels)
{
	for (; labels!=NULL; labels=labels->block_label_next)
		if (labels->block_label_label->label_number==0){
			LABEL *label;
			
			label=labels->block_label_label;
			
			w_as_define_label (label);
		} else
			w_as_define_local_label (labels->block_label_label->label_number);
}

/*
static void init_c_stack_checking (VOID)
{
	w_as_define_label_name (local_c_stack_overflow_label_name);
	w_as_opcode ("ba");
	w_as_label (c_stack_overflow_label->label_name);
	w_as_newline();

	w_as_instruction_without_parameters ("nop");
}
*/

static char local_ab_stack_overflow_label_name[]="l_ab_stack_overflow";

/*
static void init_ab_stack_checking (VOID)
{
	w_as_define_label_name (local_ab_stack_overflow_label_name);
	w_as_opcode ("ba");
	w_as_label (ab_stack_overflow_label->label_name);
	w_as_newline();

	w_as_instruction_without_parameters ("nop");
}
*/
/*
static void w_as_check_ab_stack (int size,int n_d_parameters,int n_a_parameters)
{
	if (size<=4){
		w_as_opcode ("cmp");
		w_as_register (A_STACK_POINTER);
		w_as_comma();
		w_as_register (B_STACK_POINTER);
		w_as_newline();

		w_as_opcode ("bleu");
	} else {
		w_as_opcode ("add");
		w_as_register (B_STACK_POINTER);
		w_as_comma();
		w_as_immediate (size);
		w_as_comma();
		w_as_register (REGISTER_O0);
		w_as_newline();

		w_as_opcode ("cmp");
		w_as_register (REGISTER_O0);
		w_as_comma();
		w_as_register (A_STACK_POINTER);
		w_as_newline();

		w_as_opcode ("bgu");
	}
	w_as_label (local_ab_stack_overflow_label_name);
	w_as_newline();

	w_as_instruction_without_parameters ("nop");
}
*/

void initialize_write_assembly (FILE *ass_file)
{
	assembly_file=ass_file;
	
	in_data_section=0;

	first_call_and_jump=NULL;

	/*
	if (check_c_stack)
		init_c_stack_checking();
	if (check_ab_stack)
		init_ab_stack_checking();
	*/
}

static void w_as_indirect_node_entry_jump (LABEL *label)
{
	register char *new_label_name;

	new_label_name=fast_memory_allocate (strlen (label->label_name)+1+2);
	strcpy (new_label_name,"j_");
	strcat (new_label_name,label->label_name);

	if (label->label_flags & EA_LABEL){
		int label_arity;
		extern LABEL *eval_fill_label,*eval_upd_labels[];

		label_arity=label->label_arity;
		
		if (label_arity<-2)
			label_arity=1;
		
		if (label_arity>=0 && label->label_ea_label!=eval_fill_label){
			w_as_opcode ("sethi");
			fprintf (assembly_file,"%%hi ");
#ifdef GAS
			fprintf (assembly_file,"(");
#endif
			w_as_label (label->label_ea_label->label_name);
#ifdef GAS
			fprintf (assembly_file,")");
#endif
			w_as_comma();
			w_as_register (REGISTER_A2);
			w_as_newline();
		
			w_as_opcode ("call");
			w_as_label (eval_upd_labels[label_arity]->label_name);
			w_as_newline();

			w_as_opcode ("inc");
			fprintf (assembly_file,"%%lo ");
#ifdef GAS
			fprintf (assembly_file,"(");
#endif
			w_as_label (label->label_ea_label->label_name);
#ifdef GAS
			fprintf (assembly_file,")");
#endif
			w_as_comma();
			w_as_register (REGISTER_A2);
			w_as_newline();
		} else {
			w_as_opcode ("call");
			w_as_label (label->label_ea_label->label_name);
			w_as_newline();
		
			w_as_instruction_without_parameters ("nop");
			w_as_instruction_without_parameters ("nop");
		}
		
		if (label->label_arity<0 || parallel_flag){
			LABEL *descriptor_label;

			descriptor_label=label->label_descriptor;

			if (descriptor_label->label_id<0)
				descriptor_label->label_id=next_label_id++;

			w_as_opcode (".long");
			w_as_label (descriptor_label->label_name);
			w_as_newline();
		} else
			w_as_number_of_arguments (0);
	} else
		if (label->label_arity<0 || parallel_flag){
			LABEL *descriptor_label;

			descriptor_label=label->label_descriptor;

			if (descriptor_label->label_id<0)
				descriptor_label->label_id=next_label_id++;

			w_as_opcode (".long");
			w_as_label (descriptor_label->label_name);
			w_as_newline();
		}

	w_as_number_of_arguments (label->label_arity);
	
	w_as_define_label_name (new_label_name);
	
	w_as_opcode ("call");
	w_as_label (label->label_name);
	w_as_newline();
	
	label->label_name=new_label_name;
	
	w_as_instruction_without_parameters ("nop");
}

static void w_as_indirect_node_entry_jumps (register struct label_node *label_node)
{
	register LABEL *label;
	
	if (label_node==NULL)
		return;
	
	label=&label_node->label_node_label;
	
	if (!(label->label_flags & LOCAL_LABEL) && label->label_number==0)
		if (label->label_flags & NODE_ENTRY_LABEL)
			w_as_indirect_node_entry_jump (label);
	
	w_as_indirect_node_entry_jumps (label_node->label_node_left);
	w_as_indirect_node_entry_jumps (label_node->label_node_right);
}

#ifdef SOLARIS_
static int next_section_number;
extern char *this_module_name;
#endif

void write_assembly (VOID)
{
	struct basic_block *block;
	struct call_and_jump *call_and_jump;

#ifdef SOLARIS_
	next_section_number=0;
#endif

	w_as_to_code_section();
	
	w_as_indirect_node_entry_jumps (labels);

	for (block=first_block; block!=NULL; block=block->block_next){
#ifdef SOLARIS_
		if (block->block_begin_module && !block->block_link_module){
			if (block->block_labels!=NULL && block->block_labels->block_label_label->label_number==0 && block->block_labels->block_label_label->label_flags & EXPORT_LABEL)
				fprintf (assembly_file,"\t.section\t\".%s\",#alloc,#execinstr\n",block->block_labels->block_label_label->label_name);
			else
				fprintf (assembly_file,"\t.section\t\"%s.%d\",#alloc,#execinstr\n",this_module_name,next_section_number++);
		}
#endif

		if (block->block_n_node_arguments>-100){
			if (block->block_ea_label!=NULL){
				int n_node_arguments;
				extern LABEL *eval_fill_label,*eval_upd_labels[];
				
				n_node_arguments=block->block_n_node_arguments;
				
				if (n_node_arguments<-2)
					n_node_arguments=1;
				
				if (n_node_arguments>=0 && block->block_ea_label!=eval_fill_label){
					w_as_opcode ("sethi");
					fprintf (assembly_file,"%%hi ");
#ifdef GAS
					fprintf (assembly_file,"(");
#endif
					w_as_label (block->block_ea_label->label_name);
#ifdef GAS
					fprintf (assembly_file,")");
#endif
					w_as_comma();
					w_as_register (REGISTER_A2);
					w_as_newline();
				
					w_as_opcode ("call");
					w_as_label (eval_upd_labels[n_node_arguments]->label_name);
					w_as_newline();

					w_as_opcode ("inc");
					fprintf (assembly_file,"%%lo ");
#ifdef GAS
					fprintf (assembly_file,"(");
#endif
					w_as_label (block->block_ea_label->label_name);
#ifdef GAS
					fprintf (assembly_file,")");
#endif
					w_as_comma();
					w_as_register (REGISTER_A2);
					w_as_newline();
				} else {
					w_as_opcode ("call");
					w_as_label (block->block_ea_label->label_name);
					w_as_newline();
					
					w_as_instruction_without_parameters ("nop");
					w_as_instruction_without_parameters ("nop");
				}
				
				if (block->block_descriptor!=NULL
					&& (block->block_n_node_arguments<0 || parallel_flag))
				{
					w_as_label_in_code_section (block->block_descriptor->label_name);
				} else
					w_as_number_of_arguments (0);
			} else
				if (block->block_descriptor!=NULL)
					w_as_label_in_code_section (block->block_descriptor->label_name);
				else
					w_as_number_of_arguments (0);

			w_as_number_of_arguments (block->block_n_node_arguments);
		}

		w_as_labels (block->block_labels);

		if (block->block_n_new_heap_cells>0)
			w_as_garbage_collect_test (block);
		/*
		if (check_ab_stack && block->block_ab_stack_check_size>0)
			w_as_check_ab_stack (block->block_ab_stack_check_size,
				block->block_n_begin_d_parameter_registers,block->block_n_begin_a_parameter_registers);
		*/
		
		w_as_instructions (block->block_instructions);
	}
	
	for (call_and_jump=first_call_and_jump; call_and_jump!=NULL; call_and_jump=call_and_jump->cj_next)
		w_as_call_and_jump (call_and_jump);
	
	w_as_instruction_without_parameters ("nop");
}
