/*
	File:      cgpwas.c
	Machine:   Power Macintosh
	Author:    John van Groningen
	Copyright: University of Nijmegen
*/

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

#include "cgport.h"

#ifdef MACH_O
#define GNU_SYNTAX
#endif
#undef ONLY_REGISTER_NUMBERS

#ifdef GNU_SYNTAX
# define IF_GNU(a,b) a
#else
# define IF_GNU(a,b) b
#endif

#ifdef MACH_O
# define IF_MACH_O(a,b) a
#else
# define IF_MACH_O(a,b) b
#endif

#ifndef MACH_O
# define NEWLINE_STRING "\015"
# define NEWLINE_CHAR '\015'
#else
# define NEWLINE_STRING "\012"
# define NEWLINE_CHAR '\012'
#endif

#ifdef G_POWER

#include "cgrconst.h"
#include "cgtypes.h"
#include "cg.h"
#include "cgiconst.h"
#include "cgcode.h"
#include "cginstructions.h"
#include "cgptoc.h"
#include "cgpwas.h"

#define for_l(v,l,n) for(v=(l);v!=NULL;v=v->n)

#define FSUB_FDIV_REVERSED

#define IO_BUF_SIZE 8192

static FILE *assembly_file;

static void w_as_newline (VOID)
{
	putc (NEWLINE_CHAR,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" NEWLINE_STRING,opcode);
}

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

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

#define DC_L IF_GNU (".long","dc.l")
#define DC_B IF_GNU (".byte","dc.b")

void w_as_internal_label_value (int label_id)
{
	fprintf (assembly_file,"\t" DC_L "\ti_%d" NEWLINE_STRING,label_id);
}

static int in_data_section;

static int data_module_number,code_module_number;

static void w_as_new_code_module (VOID)
{
	++code_module_number;
#ifdef GNU_SYNTAX
	fprintf (assembly_file,IF_MACH_O ("\t.text" NEWLINE_STRING,"\t.section\t\".text\"" NEWLINE_STRING));
#else
	fprintf (assembly_file,"\tcsect\t.m_%d{PR}" NEWLINE_STRING,code_module_number);
#endif
	in_data_section=0;
}

static void w_as_to_code_section (VOID)
{
	if (in_data_section){
		in_data_section=0;
#ifdef GNU_SYNTAX
		fprintf (assembly_file,IF_MACH_O ("\t.text" NEWLINE_STRING,"\t.section\t\".text\"" NEWLINE_STRING));
#else
		fprintf (assembly_file,"\tcsect\t.m_%d{PR}" NEWLINE_STRING,code_module_number);
#endif
	}
}

void w_as_new_data_module (VOID)
{
	++data_module_number;
#ifdef GNU_SYNTAX
	fprintf (assembly_file,IF_MACH_O ("\t.data" NEWLINE_STRING,"\t.section\t\".data\"" NEWLINE_STRING));
#else
	fprintf (assembly_file,"\tcsect\t.d_%d{RW}" NEWLINE_STRING,data_module_number);
#endif
	in_data_section=1;
}

void w_as_to_data_section (VOID)
{
	if (!in_data_section){
		in_data_section=1;
#ifdef GNU_SYNTAX
		fprintf (assembly_file,IF_MACH_O ("\t.data" NEWLINE_STRING,"\t.section\t\".data\"" NEWLINE_STRING));
#else
		fprintf (assembly_file,"\tcsect\t.d_%d{RW}" NEWLINE_STRING,data_module_number);
#endif
	}
}

void w_as_word_in_data_section (int n)
{
	w_as_to_data_section();
	w_as_opcode (IF_GNU (IF_MACH_O (".short",".word"),"dc.w"));
	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 (DC_L);
	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" DC_L "\t%s" NEWLINE_STRING,label_name);
}

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

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

#define MAX_BYTES_PER_LINE 16

static int w_as_data (int n,char *data_p,int length)
{
	int i,in_string;
	unsigned char *data;
	
	data=(unsigned char*)data_p;
	
	in_string=0;
	
	for (i=0; i<length; ++i){
		int c;
		
		if (n>=MAX_BYTES_PER_LINE){
			if (in_string){
				putc (IF_GNU ('\"','\''),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 (IF_MACH_O (".ascii",DC_B));
				putc (IF_GNU ('\"','\''),assembly_file);
				in_string=1;
			}
			putc (c,assembly_file);
		} else {
			if (n==0)
				w_as_opcode (DC_B);
			else {
				if (in_string){
					putc (IF_GNU ('\"','\''),assembly_file);
					w_as_newline();
					w_as_opcode (DC_B);
					in_string=0;
				} else
					putc (',',assembly_file);
			}

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

static int w_as_zeros (int n,int length)
{
	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 (DC_B);
		else
			putc (',',assembly_file);
		fprintf (assembly_file,"0");
		++n;
	}
	return n;
}

void w_as_c_string_in_data_section (char *string,int length)
{
	int n;
	
	w_as_to_data_section();

	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);
	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_abc_string_in_data_section (char *string,int length)
{
	int n;
	
	w_as_to_data_section();
	
	w_as_opcode (DC_L);
	fprintf (assembly_file,"%d" NEWLINE_STRING,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_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_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 (DC_L);
	fprintf (assembly_file,"%d" NEWLINE_STRING,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 (IF_GNU (IF_MACH_O (".globl",".global"),"export"));
		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 (DC_L);
	fprintf (assembly_file,"%d" NEWLINE_STRING,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();
}

#define REGISTER_O0 (-13)
#define REGISTER_O1 (-23)
#define REGISTER_R0 (-24)
#define REGISTER_R3 (-21)
#define REGISTER_SP (-12)

static unsigned char real_reg_num [32] =
{
	0,12,2,3,4,5,6,7,8,9,10,11,1,13,14,15,
	16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
};

#define reg_num(r) (real_reg_num[(r)+24])

static void w_as_indirect (int i,int reg)
{
#ifdef ONLY_REGISTER_NUMBERS
	if (i>=0)
		fprintf (assembly_file,"%d(%d)",i,reg_num (reg));
	else
		fprintf (assembly_file,"-%d(%d)",-i,reg_num (reg));
#else
	if (i>=0)
		fprintf (assembly_file,"%d(r%d)",i,reg_num (reg));
	else
		fprintf (assembly_file,"-%d(r%d)",-i,reg_num (reg));
#endif
}

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;
#ifdef ONLY_REGISTER_NUMBERS
	fprintf (assembly_file,"%d,%d",reg_num (reg1),reg_num (reg2));
#else
	fprintf (assembly_file,"r%d,r%d",reg_num (reg1),reg_num (reg2));
#endif
}

static void w_as_register (int reg)
{
#ifdef ONLY_REGISTER_NUMBERS
	fprintf (assembly_file,"%d",reg_num (reg));
#else
	fprintf (assembly_file,"r%d",reg_num (reg));
#endif
}

static void w_as_register_comma (int reg)
{
#ifdef ONLY_REGISTER_NUMBERS
	fprintf (assembly_file,"%d,",reg_num (reg));
#else
	fprintf (assembly_file,"r%d,",reg_num (reg));
#endif
}

void w_as_c_string_and_label_in_code_section (char *string,int length,char *label_name)
{
	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)
{
#ifdef ONLY_REGISTER_NUMBERS
	fprintf (assembly_file,"%d",fp_reg+14);
#else
	fprintf (assembly_file,IF_MACH_O ("f%d","fp%d"),fp_reg+14);
#endif
}

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

static void w_as_toc_label (struct toc_label *toc_label)
{
	struct label *label;
	int offset,t_label_number;
	
	offset=toc_label->toc_label_offset;
	label=toc_label->toc_label_label;	
	t_label_number=toc_label->toc_t_label_number;
	
	if (offset!=0){
		w_as_opcode ("tc");
		fprintf (assembly_file,"t_%d{TC}",t_label_number);
		w_as_comma();
		if (label->label_number!=0)
			w_as_local_label (label->label_number);
		else
			w_as_label (label->label_name);
		if (offset>=0)
			fprintf (assembly_file,"+%d" NEWLINE_STRING,offset);
		else
			fprintf (assembly_file,"-%d" NEWLINE_STRING,-offset);
	} else {
		w_as_opcode ("tc");
		fprintf (assembly_file,"t_%d{TC}",t_label_number);
		w_as_comma();
		if (label->label_number!=0)
			w_as_local_label (label->label_number);
		else
			w_as_label (label->label_name);
		w_as_newline();
	}
}

#ifdef GNU_SYNTAX
static void load_label (struct label *label,int offset,int reg)
{
	w_as_opcode ("lis");
	w_as_register_comma (reg);

# ifdef MACH_O
	fprintf (assembly_file,"ha16(");
# endif
	if (label->label_number!=0)
		w_as_local_label (label->label_number);
	else
		w_as_label (label->label_name);
	if (offset!=0){
		if (offset>=0)
			fprintf (assembly_file,"+%d",offset);
		else
			fprintf (assembly_file,"-%d",-offset);
	}
# ifdef MACH_O
	fprintf (assembly_file,")" NEWLINE_STRING);
# else
	fprintf (assembly_file,"@ha" NEWLINE_STRING);
# endif

	w_as_opcode ("addi");
	w_as_register_comma (reg);
	w_as_register_comma (reg);
	
# ifdef MACH_O
	fprintf (assembly_file,"lo16(");
# endif
	if (label->label_number!=0)
		w_as_local_label (label->label_number);
	else
		w_as_label (label->label_name);
	if (offset!=0){
		if (offset>=0)
			fprintf (assembly_file,"+%d",offset);
		else
			fprintf (assembly_file,"-%d",-offset);
	}
# ifdef MACH_O
	fprintf (assembly_file,")" NEWLINE_STRING);
# else
	fprintf (assembly_file,"@l" NEWLINE_STRING);
# endif
}
#endif

static void w_as_load_descriptor (struct parameter *parameter,int reg)
{
#ifdef GNU_SYNTAX
	load_label (parameter->parameter_data.l,2+(parameter->parameter_offset<<3),reg);
#else
	int t_label_number;

	t_label_number=make_toc_label (parameter->parameter_data.l,2+(parameter->parameter_offset<<3));

	w_as_opcode ("lwz");
	w_as_register_comma (reg);
	fprintf (assembly_file,"t_%d{TC}(RTOC)" NEWLINE_STRING,t_label_number);
#endif
}

static void w_as_load_label_parameter (struct parameter *parameter,int reg)
{
#ifdef GNU_SYNTAX
	load_label (parameter->parameter_data.l,parameter->parameter_offset,reg);
#else
	int t_label_number;

	t_label_number=make_toc_label (parameter->parameter_data.l,parameter->parameter_offset);

	w_as_opcode ("lwz");
	w_as_register_comma (reg);
	fprintf (assembly_file,"t_%d{TC}(RTOC)" NEWLINE_STRING,t_label_number);
#endif
}

static void w_as_load_label (struct label *label,int reg)
{
#ifdef GNU_SYNTAX
	load_label (label,0,reg);
#else
	int t_label_number;

	t_label_number=make_toc_label (label,0);

	w_as_opcode ("lwz");
	w_as_register_comma (reg);
	fprintf (assembly_file,"t_%d{TC}(RTOC)" NEWLINE_STRING,t_label_number);
#endif
}

static void w_as_load_label_with_offset (struct label *label,int offset,int reg)
{
#ifdef GNU_SYNTAX
	load_label (label,offset,reg);
#else
	int t_label_number;

	t_label_number=make_toc_label (label,offset);

	w_as_opcode ("lwz");
	w_as_register_comma (reg);
	fprintf (assembly_file,"t_%d{TC}(RTOC)" NEWLINE_STRING,t_label_number);
#endif
}

static void w_as_parameter (register 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:
#ifdef ONLY_REGISTER_NUMBERS
			fprintf (assembly_file,"%d",parameter->parameter_data.reg.r+14);
#else
			fprintf (assembly_file,IF_MACH_O ("f%d","fp%d"),parameter->parameter_data.reg.r+14);
#endif
			break;
		default:
			internal_error_in_function ("w_as_parameter");
	}
}

static int w_as_register_parameter (struct parameter parameter,int size_flag)
{
	switch (parameter.parameter_type){
		case P_DESCRIPTOR_NUMBER:
			w_as_load_descriptor (&parameter,REGISTER_O0);

			return REGISTER_O0;
		case P_IMMEDIATE:
		{
			int i;
				
			i=parameter.parameter_data.i;
			
			if (i!=(WORD)i){
				w_as_opcode ("lis");
				w_as_register_comma (REGISTER_O0);
				w_as_immediate ((i-(WORD)i)>>16);
				w_as_newline();
			
				i=(WORD)i;

				w_as_opcode ("addi");
				w_as_register_comma (REGISTER_O0);
				w_as_register_comma (REGISTER_O0);
				w_as_immediate (i);
				w_as_newline();
			} else {
				w_as_opcode ("li");
				w_as_register_comma (REGISTER_O0);
				w_as_immediate (i);
				w_as_newline();
			}		

			return REGISTER_O0;
		}
		case P_REGISTER:
			return parameter.parameter_data.reg.r;
		case P_INDIRECT:
			w_as_opcode (size_flag==SIZE_LONG ? "lwz" :
						 size_flag==SIZE_WORD ? "lha" : /* "ldsb" */ "lbz");
			w_as_register_comma (REGISTER_O0);
			w_as_indirect (parameter.parameter_offset,parameter.parameter_data.reg.r);
			w_as_newline();

			return REGISTER_O0;
		case P_INDIRECT_WITH_UPDATE:
			w_as_opcode (size_flag==SIZE_LONG ? "lwzu" :
						 size_flag==SIZE_WORD ? "lhau" : /* "ldsb" */ "lbzu");
			w_as_register_comma (REGISTER_O0);
			w_as_indirect (parameter.parameter_offset,parameter.parameter_data.reg.r);
			w_as_newline();

			return REGISTER_O0;
		case P_INDEXED:
			w_as_opcode (size_flag==SIZE_LONG ? "lwzx" :
						 size_flag==SIZE_WORD ? "lhax" : /*"ldsbx" */ "lbzx" );
			w_as_register_comma (REGISTER_O0);
			w_as_indexed (parameter.parameter_offset,parameter.parameter_data.ir);
			w_as_newline();

			return REGISTER_O0;
		default:
			internal_error_in_function ("w_as_register_parameter");
			return REGISTER_O0;
	}
}

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_load_descriptor (
						&instruction->instruction_parameters[0],
						instruction->instruction_parameters[1].parameter_data.reg.r
					);
					return;
				case P_IMMEDIATE:
				{
					int i,r;
					
					i=instruction->instruction_parameters[0].parameter_data.i;
					r=instruction->instruction_parameters[1].parameter_data.reg.r;
					
					if (i!=(WORD)i){
						w_as_opcode ("lis");
						w_as_register_comma (r);
						w_as_immediate ((i-(WORD)i)>>16);
						w_as_newline();
					
						i=(WORD)i;

						w_as_opcode ("addi");
						w_as_register_comma (r);
						w_as_register_comma (r);
						w_as_immediate (i);
						w_as_newline();
					} else {
						w_as_opcode ("li");
						w_as_register_comma (r);
						w_as_immediate (i);
						w_as_newline();
					}		
					return;
				}
				case P_REGISTER:
					w_as_opcode ("mr");
					w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
					w_as_register (instruction->instruction_parameters[0].parameter_data.reg.r);
					w_as_newline();
					return;
				case P_INDIRECT:
					w_as_opcode (size_flag==SIZE_LONG ? "lwz" :
								 size_flag==SIZE_WORD ? "lha" : "lbz");
					w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
					w_as_indirect (instruction->instruction_parameters[0].parameter_offset,
								   instruction->instruction_parameters[0].parameter_data.reg.r);
					w_as_newline();
					return;
				case P_INDIRECT_WITH_UPDATE:
					w_as_opcode (size_flag==SIZE_LONG ? "lwzu" :
								 size_flag==SIZE_WORD ? "lhau" : "lbzu");
					w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
					w_as_indirect (instruction->instruction_parameters[0].parameter_offset,
								   instruction->instruction_parameters[0].parameter_data.reg.r);
					w_as_newline();
					return;
				case P_INDEXED:
					w_as_opcode (size_flag==SIZE_LONG ? "lwzx" :
								 size_flag==SIZE_WORD ? "lhax" : "lbzx");
					w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
					w_as_indexed (instruction->instruction_parameters[0].parameter_offset,
								  instruction->instruction_parameters[0].parameter_data.ir);
					w_as_newline();
					return;
				default:
					internal_error_in_function ("w_as_move_instruction");
					return;
			}
		case P_INDIRECT:
		{
			int reg;

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

			w_as_opcode (size_flag==SIZE_LONG ? "stw" :
						 size_flag==SIZE_WORD ? "sth" : "stb");
			w_as_register_comma (reg);
			w_as_indirect (instruction->instruction_parameters[1].parameter_offset,
						   instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_newline();
			return;
		}
		case P_INDIRECT_WITH_UPDATE:
		{
			int reg;

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

			w_as_opcode (size_flag==SIZE_LONG ? "stwu" :
						 size_flag==SIZE_WORD ? "sthu" : "stbu");
			
			w_as_register_comma (reg);
			w_as_indirect (instruction->instruction_parameters[1].parameter_offset,
						   instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_newline();
			return;
		}
		case P_INDEXED:
		{
			int reg;

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

			w_as_opcode (size_flag==SIZE_LONG ? "stwx" :
						 size_flag==SIZE_WORD ? "sthx" : "stbx");
			w_as_register_comma (reg);
			w_as_indexed (instruction->instruction_parameters[1].parameter_offset,
						  instruction->instruction_parameters[1].parameter_data.ir);
			w_as_newline();
			return;
		}
		case P_INDIRECT_HP:
		{
			int reg1,reg2;
			LONG offset;

			reg1=w_as_register_parameter (instruction->instruction_parameters[0],size_flag);
			offset=instruction->instruction_parameters[1].parameter_data.i;
			reg2=HEAP_POINTER;

			if (offset!=(WORD)offset){
				w_as_opcode ("addis");
				w_as_register_comma (REGISTER_O0);
				w_as_register_comma (reg2);
				w_as_immediate ((offset-(WORD)offset)>>16);
				w_as_newline();
			
				reg2=REGISTER_O0;
				offset=(WORD)offset;
			}

			w_as_opcode (size_flag==SIZE_LONG ? "stw" :
						 size_flag==SIZE_WORD ? "sth" : "stb");
			w_as_register_comma (reg1);
			w_as_indirect (offset,reg2);
			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_load_label_parameter (&instruction->instruction_parameters[0],
					instruction->instruction_parameters[1].parameter_data.reg.r);				
				return;
			case P_INDIRECT:
				w_as_opcode ("addi");
				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_immediate (instruction->instruction_parameters[0].parameter_offset);
				w_as_newline();
				return;
			case P_INDEXED:
				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.ir->a_reg.r);
				w_as_register (instruction->instruction_parameters[0].parameter_data.ir->d_reg.r);
				w_as_newline();
				return;				
		}

	internal_error_in_function ("w_as_lea_instruction");
}

static void w_as_or_or_eor_instruction (struct instruction *instruction,char *opcode)
{	
	if (instruction->instruction_parameters[0].parameter_type==P_IMMEDIATE){
		long i;

		i=instruction->instruction_parameters[0].parameter_data.i;

		if ((unsigned short) i != i){
			int h;
			
			h=(unsigned)i >> (unsigned)16;
			
			fprintf (assembly_file,"\t%sis\t",opcode);
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_immediate (h);
			w_as_newline();
			
			i=(unsigned short)i;
		}

		fprintf (assembly_file,"\t%si\t",opcode);

		w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_immediate (i);
		w_as_newline();

	} else {
		int reg;

		reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);

		w_as_opcode (opcode);
		w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_register (reg);
		w_as_newline();
	}
}

static void w_as_tryadic_instruction (struct instruction *instruction,char *opcode)
{
	if (instruction->instruction_parameters[0].parameter_type==P_IMMEDIATE){
		fprintf (assembly_file,"\t%si\t",opcode);
	
		w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_parameter (&instruction->instruction_parameters[0]);
		w_as_newline();
	} else {
		int reg;

		reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);
	
		w_as_opcode (opcode);
		
		w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_register (reg);
		w_as_newline();
	}
}

static void w_as_i_instruction (struct instruction *instruction,char *opcode)
{
	w_as_opcode (opcode);
	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_immediate (instruction->instruction_parameters[2].parameter_data.i);
	w_as_newline();
}

static void w_as_and_instruction (struct instruction *instruction)
{
	int reg;
	
	if (instruction->instruction_parameters[0].parameter_type==P_IMMEDIATE){
		int i;

		i=instruction->instruction_parameters[0].parameter_data.i;

		if (i==(UWORD)i){
			w_as_opcode ("andi.");

			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_immediate (i);
			w_as_newline();

			return;
		} else {
			w_as_opcode ("lis");
			w_as_register_comma (REGISTER_O0);
			w_as_immediate ((i-(WORD)i)>>16);
			w_as_newline();
				
			i=(WORD)i;
	
			w_as_opcode ("addi");
			w_as_register_comma (REGISTER_O0);
			w_as_register_comma (REGISTER_O0);
			w_as_immediate (i);
			w_as_newline();

			reg=REGISTER_O0;
	
			w_as_opcode ("and");
		}
	} else {
		reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);

		w_as_opcode ("and");
	}
	
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register (reg);
	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[1].parameter_data.reg.r);
			w_as_register (instruction->instruction_parameters[0].parameter_data.reg.r);
			w_as_newline();
			return;
		case P_IMMEDIATE:
		{
			int i,r;
			
			i=instruction->instruction_parameters[0].parameter_data.i;
			r=instruction->instruction_parameters[1].parameter_data.reg.r;
			
			if (i!=(WORD)i){
				w_as_opcode ("addis");
				w_as_register_comma (r);
				w_as_register_comma (r);
				w_as_immediate ((i-(WORD)i)>>16);
				w_as_newline();
			
				i=(WORD)i;
			}

			w_as_opcode ("addi");
			w_as_register_comma (r);
			w_as_register_comma (r);
			w_as_immediate (i);
			w_as_newline();

			return;
		}
		default:
		{
			int reg;

			reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);
			
			w_as_opcode ("add");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register (reg);
			w_as_newline();
		}
	}
}

static void w_as_extb_instruction (struct instruction *instruction)
{
	int reg;
	
	reg=instruction->instruction_parameters[0].parameter_data.reg.r;

	w_as_opcode ("extsb");
	w_as_register_comma (reg);
	w_as_register (reg);
	w_as_newline();
}

static void w_as_addo_instruction (struct instruction *instruction)
{
	int reg;

	reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);
	
	w_as_opcode ("addo.");
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register (reg);
	w_as_newline();
}

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[1].parameter_data.reg.r);
			w_as_register (instruction->instruction_parameters[0].parameter_data.reg.r);
			w_as_newline();
			return;
		case P_IMMEDIATE:
		{
			int i,r;
			
			i= -instruction->instruction_parameters[0].parameter_data.i;
			r=instruction->instruction_parameters[1].parameter_data.reg.r;
			
			if (i!=(WORD)i){
				w_as_opcode ("addis");
				w_as_register_comma (r);
				w_as_register_comma (r);
				w_as_immediate ((i-(WORD)i)>>16);
				w_as_newline();
			
				i=(WORD)i;
			}

			w_as_opcode ("addi");
			w_as_register_comma (r);
			w_as_register_comma (r);
			w_as_immediate (i);
			w_as_newline();

			return;
		}
		default:
		{
			int reg;

			reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);
			
			w_as_opcode ("sub");
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
			w_as_register (reg);
			w_as_newline();
		}
	}
}

static void w_as_subo_instruction (struct instruction *instruction)
{
	int reg;

	reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);
	
	w_as_opcode ("subo.");
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register (reg);
	w_as_newline();
}

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 || parameter_1.parameter_type==P_INDEXED){
		if (parameter_1.parameter_type==P_INDIRECT)
			w_as_opcode (size_flag==SIZE_LONG ? "lwz" : "lha");
		else
			w_as_opcode (size_flag==SIZE_LONG ? "lwzx" : "lhax");

		w_as_register_comma (REGISTER_O1);
		w_as_parameter (&parameter_1);
		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_load_descriptor (&parameter_0,REGISTER_O0);

			parameter_0.parameter_type=P_REGISTER;
			parameter_0.parameter_data.reg.r=REGISTER_O0;
			break;
		case P_REGISTER:
			break;
		case P_IMMEDIATE:
		{
			int i;
				
			i=parameter_0.parameter_data.i;
			
			if (i!=(WORD)i){
				w_as_opcode ("lis");
				w_as_register_comma (REGISTER_O0);
				w_as_immediate ((i-(WORD)i)>>16);
				w_as_newline();
			
				i=(WORD)i;

				w_as_opcode ("addi");
				w_as_register_comma (REGISTER_O0);
				w_as_register_comma (REGISTER_O0);
				w_as_immediate (i);
				w_as_newline();

				parameter_0.parameter_type=P_REGISTER;
				parameter_0.parameter_data.reg.r=REGISTER_O0;
			}
			break;
		}
		case P_INDIRECT:
			switch (size_flag){
				case SIZE_WORD:
					w_as_opcode ("lha");
					break;
				case SIZE_LONG:
					w_as_opcode ("lwz");
					break;
				default:
					internal_error_in_function ("w_as_cmp_instruction");
			}
			w_as_register_comma (REGISTER_O0);
			w_as_parameter (&parameter_0);
			w_as_newline();

			parameter_0.parameter_type=P_REGISTER;
			parameter_0.parameter_data.reg.r=REGISTER_O0;
			break;
		case P_INDEXED:
			switch (size_flag){
				case SIZE_WORD:
					w_as_opcode ("lhax");
					break;
				case SIZE_LONG:
					w_as_opcode ("lwzx");
					break;
				default:
					internal_error_in_function ("w_as_cmp_instruction");
			}
			w_as_register_comma (REGISTER_O0);
			w_as_parameter (&parameter_0);
			w_as_newline();

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

	w_as_opcode (parameter_0.parameter_type==P_IMMEDIATE ? "cmpwi" : "cmpw");
	w_as_immediate (0);
	w_as_comma();
	w_as_parameter (&parameter_1);
	w_as_comma();
	w_as_parameter (&parameter_0);
	w_as_newline();
}

static void w_as_cmplw_instruction (struct instruction *instruction)
{
	w_as_opcode ("lwz");
	w_as_register_comma (REGISTER_O0);
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();

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

static void w_as_tst_instruction (struct instruction *instruction)
{
	int reg;

	reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);

	w_as_opcode ("cmpwi");
	w_as_immediate (0);
	w_as_comma();
	w_as_register_comma (reg);
	w_as_immediate (0);
	w_as_newline();
}

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

void w_as_jmp_instruction (struct instruction *instruction)
{
	struct parameter *parameter0;
	
	parameter0=&instruction->instruction_parameters[0];

	switch (parameter0->parameter_type){
		case P_LABEL:
			w_as_opcode ("b");
			if (parameter0->parameter_data.l->label_number!=0)
				w_as_local_label (parameter0->parameter_data.l->label_number);
			else
				w_as_label (parameter0->parameter_data.l->label_name);

			w_as_newline();
			return;
		case P_INDIRECT:
		{
			int offset,reg;

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

			if (offset!=0){
				w_as_opcode ("la");
				w_as_register_comma (REGISTER_O0);
				w_as_indirect (offset,reg);
				w_as_newline();

				w_as_opcode ("mtctr");
				w_as_register (REGISTER_O0);
				w_as_newline();
			} else {
				w_as_opcode ("mtctr");
				w_as_register (reg);
				w_as_newline();
			}

			w_as_instruction_without_parameters ("bctr");
			return;
		}
		default:
			internal_error_in_function ("w_as_jmp_instruction");
	}
}

static void w_as_jmpp_instruction (struct instruction *instruction)
{
	struct parameter *parameter0;
	
	parameter0=&instruction->instruction_parameters[0];

	switch (parameter0->parameter_type){
		case P_LABEL:
		{
			int offset;

			offset=instruction->instruction_parameters[0].parameter_offset;
			if (offset==0){
				w_as_opcode ("mflr");
				w_as_register (REGISTER_R0);
				w_as_newline();

				w_as_opcode ("bl");
				w_as_label ("profile_t");
				w_as_newline();
			}

			w_as_opcode ("b");
			if (instruction->instruction_parameters[0].parameter_data.l->label_number!=0)
				w_as_local_label (instruction->instruction_parameters[0].parameter_data.l->label_number);
			else
				w_as_label (instruction->instruction_parameters[0].parameter_data.l->label_name);

			if (offset!=0)
				fprintf (assembly_file,"+%d",offset);

			w_as_newline();
			return;
		}
		case P_INDIRECT:
		{
			int offset,reg;

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

			if (offset!=0){
				w_as_opcode ("la");
				w_as_register_comma (REGISTER_O0);
				w_as_indirect (offset,reg);
				w_as_newline();

				w_as_opcode ("mtctr");
				w_as_register (REGISTER_O0);
				w_as_newline();
			} else {
				w_as_opcode ("mtctr");
				w_as_register (reg);
				w_as_newline();
			}

			w_as_opcode ("b");
			w_as_label ("profile_ti");
			w_as_newline();
			return;
		}
		default:
			internal_error_in_function ("w_as_jmpp_instruction");
	}
}

struct call_and_jump {
	struct call_and_jump *	cj_next;
	WORD					cj_label_id;
	WORD					cj_jump_id; /* or -1 for far conditional jump */
	char *					cj_call_label_name;
};

static struct call_and_jump *first_call_and_jump,*last_call_and_jump;

static struct call_and_jump *allocate_new_call_and_jump (void)
{
	struct call_and_jump *new_call_and_jump;

	new_call_and_jump=allocate_memory_from_heap_type (struct call_and_jump);

	new_call_and_jump->cj_next=NULL;

	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;

	return new_call_and_jump;
}

static void w_as_branch_instruction (struct instruction *instruction,char *opcode)
{
	w_as_opcode (opcode);

	if (instruction->instruction_parameters[0].parameter_data.l->label_flags & FAR_CONDITIONAL_JUMP_LABEL){
		struct call_and_jump *new_call_and_jump;
		int label_id;

		label_id=next_label_id++;

		new_call_and_jump=allocate_new_call_and_jump();
					
		new_call_and_jump->cj_call_label_name=instruction->instruction_parameters[0].parameter_data.l->label_name;
		new_call_and_jump->cj_label_id=label_id;
		new_call_and_jump->cj_jump_id=-1;
		
		w_as_internal_label (label_id);
	} else
		w_as_parameter (&instruction->instruction_parameters[0]);

	w_as_newline();
}

static void w_as_index_error_branch_instruction (struct instruction *instruction)
{
	w_as_opcode ("blt+");
	fprintf (assembly_file,".+8");
	w_as_newline();

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

static void w_as_branchno_instruction (struct instruction *instruction)
{
	w_as_opcode ("bns");
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();

	w_as_opcode ("mcrxr");
	w_as_immediate (0);
	w_as_newline();
	
	w_as_opcode ("bng");
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();
}

static void w_as_brancho_instruction (struct instruction *instruction)
{
	w_as_opcode ("bns");
	fprintf (assembly_file,IF_GNU ("$+12","*+12"));
	w_as_newline();

	w_as_opcode ("mcrxr");
	w_as_immediate (0);
	w_as_newline();
	
	w_as_opcode ("bgt");
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_newline();
}

#ifdef MACH_O
struct stub {
	char *stub_label_name;
	struct stub *stub_next;
};

static struct stub *first_stub,**next_stub_l;

static void write_stub (char *label_name,int stub_n)
{
	fprintf (assembly_file,".picsymbol_stub" NEWLINE_STRING);
	fprintf (assembly_file,"L_%s$stub:" NEWLINE_STRING,label_name);
	fprintf (assembly_file,"\t.indirect_symbol _%s" NEWLINE_STRING,label_name);
	fprintf (assembly_file,"\tmflr\tr0" NEWLINE_STRING);
	fprintf (assembly_file,"\tbcl\t20,31,L%d$pb" NEWLINE_STRING,stub_n);
	fprintf (assembly_file,"L%d$pb:" NEWLINE_STRING,stub_n);
	fprintf (assembly_file,"\tmflr\tr11" NEWLINE_STRING);
	fprintf (assembly_file,"\taddis\tr11,r11,ha16(L%d$lz-L%d$pb)" NEWLINE_STRING,stub_n,stub_n);
	fprintf (assembly_file,"\tmtlr\tr0" NEWLINE_STRING);
	fprintf (assembly_file,"\tlwz\tr12,lo16(L%d$lz-L%d$pb)(r11)" NEWLINE_STRING,stub_n,stub_n);
	fprintf (assembly_file,"\tmtctr\tr12" NEWLINE_STRING);
	fprintf (assembly_file,"\taddi\tr11,r11,lo16(L%d$lz-L%d$pb )" NEWLINE_STRING,stub_n,stub_n);
	fprintf (assembly_file,"\tbctr" NEWLINE_STRING);
	fprintf (assembly_file,".lazy_symbol_pointer" NEWLINE_STRING);
	fprintf (assembly_file,"L%d$lz:" NEWLINE_STRING,stub_n);
	fprintf (assembly_file,".indirect_symbol _%s" NEWLINE_STRING,label_name);
	fprintf (assembly_file,"\t.long\tdyld_stub_binding_helper" NEWLINE_STRING);
}

static void write_stubs (void)
{
	struct stub *stub;
	int stub_n;
	
	stub_n=1;
	for_l (stub,first_stub,stub_next){
		write_stub (stub->stub_label_name,stub_n);
		++stub_n;
	}
		
}
#endif

static void w_as_jsr_instruction (struct instruction *instruction)
{
	struct parameter *parameter0;

	parameter0=&instruction->instruction_parameters[0];

	if (instruction->instruction_parameters[1].parameter_type==P_REGISTER){
		int frame_size;
		
		frame_size=instruction->instruction_parameters[1].parameter_data.i;

		if (parameter0->parameter_type==P_REGISTER){
			w_as_opcode ("mtctr");
			w_as_register (parameter0->parameter_data.reg.r);
			w_as_newline();
		}

		if (!(instruction->instruction_arity & NO_MFLR)){
			w_as_opcode ("mflr");
			w_as_register (REGISTER_R0);
			w_as_newline();
		}

#ifdef ALIGN_C_CALLS
# if 0
		w_as_opcode ("mr");
		w_as_register_comma (REGISTER_O0);
		w_as_register (B_STACK_POINTER);
		w_as_newline();

		w_as_opcode ("ori");
		w_as_register_comma (B_STACK_POINTER);
		w_as_register_comma (B_STACK_POINTER);
		w_as_immediate (28);
		w_as_newline();
# endif
		w_as_opcode ("stw");
		w_as_register_comma (REGISTER_R0);
		w_as_indirect (-28-4,REGISTER_SP);
		w_as_newline();

		w_as_opcode ("stwu");
		w_as_register_comma (REGISTER_O0);
		w_as_indirect (-(frame_size+28),REGISTER_SP);
		w_as_newline();		
#else
		w_as_opcode ("stw");
		w_as_register_comma (REGISTER_R0);
		w_as_indirect (-4,REGISTER_SP);
		w_as_newline();

		w_as_opcode ("stwu");
		w_as_register_comma (REGISTER_SP);
		w_as_indirect (-frame_size,REGISTER_SP);
		w_as_newline();
#endif
	
		if (parameter0->parameter_type==P_REGISTER){
			w_as_instruction_without_parameters ("bctrl");
		} else {
			w_as_opcode ("bl");
			if (parameter0->parameter_data.l->label_number!=0)
				w_as_local_label (parameter0->parameter_data.l->label_number);
			else
#ifdef MACH_O
			{
				char *label_name;
				
				label_name=parameter0->parameter_data.l->label_name;
				if (label_name[0]=='_'){
					int c;
					struct stub *new_stub;

					putc ('L',assembly_file);
					putc ('_',assembly_file);
					++label_name;
					
					if (!(parameter0->parameter_data.l->label_flags & STUB_GENERATED)){
						parameter0->parameter_data.l->label_flags |= STUB_GENERATED;
					
						new_stub=allocate_memory_from_heap (sizeof (struct stub));
						
						new_stub->stub_label_name=label_name;
						*next_stub_l=new_stub;
						next_stub_l=&new_stub->stub_next;
						new_stub->stub_next=NULL;
					}
					
					while (c=*label_name++,c!=0)
						putc (c,assembly_file);
					
					fprintf (assembly_file,"$stub");
				} else
					w_as_label (label_name);

			}
#else
				w_as_label (parameter0->parameter_data.l->label_name);
#endif
			w_as_newline();
		}
	
		w_as_instruction_without_parameters ("nop");

#ifdef ALIGN_C_CALLS
		w_as_opcode ("lwz");
		w_as_register_comma (REGISTER_R0);
		w_as_indirect (frame_size-4,REGISTER_SP);
		w_as_newline();

		w_as_opcode ("lwz");
		w_as_register_comma (REGISTER_SP);
		w_as_indirect (0,REGISTER_SP);
		w_as_newline();
#else
		w_as_opcode ("lwz");
		w_as_register_comma (REGISTER_R0);
		w_as_indirect (frame_size-4,REGISTER_SP);
		w_as_newline();

		w_as_opcode ("addi");
		w_as_register_comma (REGISTER_SP);
		w_as_register_comma (REGISTER_SP);
		w_as_immediate (frame_size);
		w_as_newline();
#endif

		if (!(instruction->instruction_arity & NO_MTLR)){
			w_as_opcode ("mtlr");
			w_as_register (REGISTER_R0);
			w_as_newline();
		}
		return;
	}


	if (parameter0->parameter_type==P_INDIRECT){
		int offset,reg;

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

		if (offset!=0){
			w_as_opcode ("la");
			w_as_register_comma (REGISTER_O0);
			w_as_indirect (offset,reg);
			w_as_newline();

			w_as_opcode ("mtctr");
			w_as_register (REGISTER_O0);
			w_as_newline();
		} else {
			w_as_opcode ("mtctr");
			w_as_register (reg);
			w_as_newline();
		}
	}

	if (!(instruction->instruction_arity & NO_MFLR)){
		w_as_opcode ("mflr");
		w_as_register (REGISTER_R0);
		w_as_newline();
	}

	w_as_opcode (instruction->instruction_parameters[1].parameter_type==P_INDIRECT_WITH_UPDATE
					? "stwu" : "stw");
	w_as_register_comma (REGISTER_R0);
	w_as_indirect (instruction->instruction_parameters[1].parameter_data.i,B_STACK_POINTER);
	w_as_newline();

	switch (parameter0->parameter_type){
		case P_LABEL:
			w_as_opcode ("bl");
			if (parameter0->parameter_data.l->label_number!=0)
				w_as_local_label (parameter0->parameter_data.l->label_number);
			else
				w_as_label (parameter0->parameter_data.l->label_name);
			break;
		case P_INDIRECT:
			w_as_opcode ("bctrl");
			break;
		default:
			internal_error_in_function ("w_as_jsr_instruction");
	}
	w_as_newline();

	if (!(instruction->instruction_arity & NO_MTLR)){
		w_as_opcode ("mtlr");
		w_as_register (REGISTER_R0);
		w_as_newline();
	}
}

static void w_as_call_and_jump (struct call_and_jump *call_and_jump)
{
	w_as_new_code_module();

	w_as_define_internal_label (call_and_jump->cj_label_id);

	if (call_and_jump->cj_jump_id==-1){
		w_as_opcode ("b");
		w_as_label (call_and_jump->cj_call_label_name);
		w_as_newline();		
	} else {
		w_as_opcode ("mflr");
		w_as_register (REGISTER_R0);
		w_as_newline();

		w_as_opcode ("bl");
		w_as_label (call_and_jump->cj_call_label_name);
		w_as_newline();
		
		w_as_opcode ("b");
		w_as_internal_label (call_and_jump->cj_jump_id);
		w_as_newline();
	}
}

static void w_as_rts_begin (struct instruction *instruction)
{
	LONG b_offset;

	if (instruction->instruction_parameters[0].parameter_type==P_INDIRECT){
		w_as_opcode ("lwz");
		w_as_register_comma (REGISTER_R0);
		w_as_indirect (instruction->instruction_parameters[0].parameter_offset,instruction->instruction_parameters[0].parameter_data.reg.r);
	} else {
		w_as_opcode ("mr");
		w_as_register_comma (REGISTER_R0);
		w_as_register (instruction->instruction_parameters[0].parameter_data.reg.r);
	}
	w_as_newline();

	b_offset=instruction->instruction_parameters[1].parameter_data.i;
	if (b_offset!=0){
		if (b_offset!=(WORD) b_offset){
			w_as_opcode ("addis");
			w_as_register_comma (B_STACK_POINTER);
			w_as_register_comma (B_STACK_POINTER);
			w_as_immediate ((b_offset-(WORD)b_offset)>>16);
			w_as_newline();
					
			b_offset=(WORD)b_offset;
		}
		
		w_as_opcode ("addi");
		w_as_register_comma (B_STACK_POINTER);
		w_as_register_comma (B_STACK_POINTER);
		w_as_immediate (b_offset);
		w_as_newline();
	}
}

static void w_as_rts_instruction (struct instruction *instruction)
{
	if (instruction->instruction_arity>0)
		w_as_rts_begin (instruction);

	w_as_instruction_without_parameters ("blr");
}

static void w_as_rtsp_instruction (struct instruction *instruction)
{
	w_as_rts_begin (instruction);

	w_as_opcode ("b");
	w_as_label ("profile_r");
	w_as_newline();
}

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

	w_as_opcode (opcode);
	fprintf (assembly_file,IF_GNU ("$+8","*+8"));
	w_as_newline();

	w_as_opcode ("li");
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_comma();
	w_as_immediate (-1);
	w_as_newline();
}

static void w_as_setno_condition_instruction (struct instruction *instruction)
{
	w_as_opcode ("li");
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_comma();
	w_as_immediate (0);
	w_as_newline();

	w_as_opcode ("bns");
	fprintf (assembly_file,IF_GNU ("$+12","*+12"));
	w_as_newline();

	w_as_opcode ("mcrxr");
	w_as_immediate (0);
	w_as_newline();
	
	w_as_opcode ("bgt");
	fprintf (assembly_file,IF_GNU ("$+8","*+8"));
	w_as_newline();

	w_as_opcode ("li");
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_comma();
	w_as_immediate (-1);
	w_as_newline();
}

static void w_as_seto_condition_instruction (struct instruction *instruction)
{
	w_as_opcode ("li");
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_comma();
	w_as_immediate (0);
	w_as_newline();

	w_as_opcode ("bns");
	fprintf (assembly_file,IF_GNU ("$+16","*+16"));
	w_as_newline();

	w_as_opcode ("mcrxr");
	w_as_immediate (0);
	w_as_newline();
	
	w_as_opcode ("bng");
	fprintf (assembly_file,IF_GNU ("$+8","*+8"));
	w_as_newline();

	w_as_opcode ("li");
	w_as_parameter (&instruction->instruction_parameters[0]);
	w_as_comma();
	w_as_immediate (-1);
	w_as_newline();
}

static void w_as_mod_instruction (struct instruction *instruction)
{
	int reg;

	reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);
	
	w_as_opcode ("divw");
	w_as_register_comma (REGISTER_O1);
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register (reg);
	w_as_newline();

	w_as_opcode ("mullw");
	w_as_register_comma (REGISTER_O1);
	w_as_register_comma (REGISTER_O1);
	w_as_register (reg);
	w_as_newline();

	w_as_opcode ("sub");
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register (REGISTER_O1);
    w_as_newline();
}

static void w_as_div_instruction (struct instruction *instruction)
{
	int reg;

	reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);
	
	w_as_opcode ("divw");
	
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
	w_as_register (reg);
	w_as_newline();
}

static void w_as_mul_instruction (struct instruction *instruction)
{
	int r,reg;
	
	r=instruction->instruction_parameters[1].parameter_data.reg.r;
	
	switch (instruction->instruction_parameters[0].parameter_type){
		case P_IMMEDIATE:
		{
			int i;
			
			i=instruction->instruction_parameters[0].parameter_data.i;
			
			if (i!=(WORD)i){
				w_as_opcode ("lis");
				w_as_register_comma (REGISTER_O0);
				w_as_immediate ((i-(WORD)i)>>16);
				w_as_newline();
			
				i=(WORD)i;

				w_as_opcode ("addi");
				w_as_register_comma (REGISTER_O0);
				w_as_register_comma (REGISTER_O0);
				w_as_immediate (i);
				w_as_newline();
			} else {
				w_as_opcode ("mulli");
				w_as_register_comma (r);
				w_as_register_comma (r);
				w_as_immediate (i);
				w_as_newline();
				
				return;
			}		
			
			reg=REGISTER_O0;
			break;
		}
		default:
			reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);
	}
	
	w_as_opcode ("mullw");
	
	w_as_register_comma (r);
	w_as_register_comma (r);
	w_as_register (reg);
	w_as_newline();
}

static void w_as_mulo_instruction (struct instruction *instruction)
{
	int r,reg;
		
	reg=w_as_register_parameter (instruction->instruction_parameters[0],SIZE_LONG);

	r=instruction->instruction_parameters[1].parameter_data.reg.r;
	
	w_as_opcode ("mullwo.");
	
	w_as_register_comma (r);
	w_as_register_comma (r);
	w_as_register (reg);
	w_as_newline();
}

static int next_bmove_label;

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

static void w_as_load_float_immediate (double float_value,int fp_reg)
{
	int label_number,t_label_number;
	struct label *new_label;

	new_label=(struct label*)allocate_memory_from_heap (sizeof (struct label));

	label_number=next_label_id++;

	w_as_to_data_section();
#ifdef GNU_SYNTAX
	fprintf (assembly_file,"\t.align\t8" NEWLINE_STRING);
#else
	fprintf (assembly_file,"\talign\t3" NEWLINE_STRING);
#endif
	new_label->label_flags=0;	
	new_label->label_number=label_number;

	/* w_as_define_internal_label (label_number); */

	w_as_define_local_label (label_number);
#ifdef GNU_SYNTAX
	w_as_opcode (".double");
	fprintf (assembly_file,"0d%.20e",float_value);
#else
	w_as_opcode ("dc.d");
	fprintf (assembly_file,"\"%.20e\"",float_value);
#endif
	w_as_newline();

/*
	w_as_instruction_without_parameters ("toc");

	w_as_opcode ("tc");
	fprintf (assembly_file,"t_%d{TC}",t_label_number);
	w_as_comma();
	w_as_internal_label (label_number);
	w_as_newline();
*/

#ifndef GNU_SYNTAX
	t_label_number=make_toc_label (new_label,0);
#endif
	w_as_to_code_section();

	w_as_opcode (IF_GNU ("lis","lwz"));
	w_as_scratch_register();
	w_as_comma();
#ifdef GNU_SYNTAX
# ifdef MACH_O
	fprintf (assembly_file,"ha16(");
# endif
	w_as_local_label (label_number);
# ifdef MACH_O
	fprintf (assembly_file,")" NEWLINE_STRING);
# else
	fprintf (assembly_file,"@ha" NEWLINE_STRING);
# endif
#else
	fprintf (assembly_file,"t_%d{TC}(RTOC)" NEWLINE_STRING,t_label_number);
#endif

	w_as_opcode ("lfd");
	w_as_fp_register (fp_reg);
	w_as_comma();
#ifdef GNU_SYNTAX
# ifdef MACH_O
	fprintf (assembly_file,"lo16(");
# endif
	w_as_local_label (label_number);
# ifdef MACH_O
	fprintf (assembly_file,")(");
# else
	fprintf (assembly_file,"@l(");
# endif
	w_as_scratch_register();
	fprintf (assembly_file,")" NEWLINE_STRING);
#else
	w_as_indirect (0,REGISTER_O0);
	w_as_newline();
#endif
	/*++t_label_number; */
}

static struct parameter w_as_float_parameter (struct parameter parameter)
{
	switch (parameter.parameter_type){
		case P_F_IMMEDIATE:
			w_as_load_float_immediate (*parameter.parameter_data.r,17);

			parameter.parameter_type=P_F_REGISTER;
			parameter.parameter_data.reg.r=17;
			break;
		case P_INDIRECT:
			w_as_opcode ("lfd");
			w_as_fp_register (17);
			w_as_comma();
			w_as_indirect (parameter.parameter_offset,parameter.parameter_data.reg.r);
			w_as_newline();

			parameter.parameter_type=P_F_REGISTER;
			parameter.parameter_data.reg.r=17;
			break;
		case P_INDEXED:
			w_as_opcode ("lfdx");
			w_as_fp_register (17);
			w_as_comma();
			w_as_indexed (parameter.parameter_offset,parameter.parameter_data.ir);
			w_as_newline();

			parameter.parameter_type=P_F_REGISTER;
			parameter.parameter_data.reg.r=17;
			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 ("fcmpu");
	
	w_as_immediate (0);
	w_as_comma();
	w_as_parameter (&instruction->instruction_parameters[1]);
	w_as_comma();
	w_as_parameter (&parameter_0);
	w_as_newline();
}

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

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

	w_as_opcode (opcode);

	w_as_parameter (&instruction->instruction_parameters[1]);
	w_as_comma();
	w_as_parameter (&parameter_0);
	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 (opcode);

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

#ifdef FMADD
#define FP_REG_LAST_USE 4

static struct instruction *w_as_fmul_instruction (struct instruction *instruction)
{
	struct instruction *next_instruction;
	
	next_instruction=instruction->instruction_next;
	if (fmadd_flag && next_instruction!=NULL)
		if (next_instruction->instruction_icode==IFADD){
			if (next_instruction->instruction_parameters[0].parameter_type==P_F_REGISTER &&
				next_instruction->instruction_parameters[0].parameter_data.reg.r!=next_instruction->instruction_parameters[1].parameter_data.reg.r)
			{
				if (next_instruction->instruction_parameters[0].parameter_flags & FP_REG_LAST_USE &&
					next_instruction->instruction_parameters[0].parameter_data.reg.r==instruction->instruction_parameters[1].parameter_data.reg.r)
				{	
					struct parameter parameter_0;

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

					w_as_parameter (&next_instruction->instruction_parameters[1]);
					w_as_comma();
					w_as_parameter (&parameter_0);
					w_as_comma();
					w_as_parameter (&instruction->instruction_parameters[1]);
					w_as_comma();
					w_as_parameter (&next_instruction->instruction_parameters[1]);
					w_as_newline();
					
					return next_instruction;
				} else if (next_instruction->instruction_parameters[1].parameter_data.reg.r==instruction->instruction_parameters[1].parameter_data.reg.r){	
					struct parameter parameter_0;

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

					w_as_parameter (&next_instruction->instruction_parameters[1]);
					w_as_comma();
					w_as_parameter (&parameter_0);
					w_as_comma();
					w_as_parameter (&instruction->instruction_parameters[1]);
					w_as_comma();
					w_as_parameter (&next_instruction->instruction_parameters[0]);
					w_as_newline();
					
					return next_instruction;
				}
			}
		} else if (next_instruction->instruction_icode==IFSUB){
			if (next_instruction->instruction_parameters[0].parameter_type==P_F_REGISTER &&
				next_instruction->instruction_parameters[0].parameter_data.reg.r!=next_instruction->instruction_parameters[1].parameter_data.reg.r)
			{
				if (next_instruction->instruction_parameters[0].parameter_flags & FP_REG_LAST_USE &&
					next_instruction->instruction_parameters[0].parameter_data.reg.r==instruction->instruction_parameters[1].parameter_data.reg.r)
				{
					struct parameter parameter_0;

					parameter_0=w_as_float_parameter (instruction->instruction_parameters[0]);
					
# ifdef FSUB_FDIV_REVERSED
					if (next_instruction->instruction_parameters[1].parameter_flags)
						w_as_opcode ("fmsub");
					else
# endif
						w_as_opcode ("fnmsub");

					w_as_parameter (&next_instruction->instruction_parameters[1]);
					w_as_comma();
					w_as_parameter (&parameter_0);
					w_as_comma();
					w_as_parameter (&instruction->instruction_parameters[1]);
					w_as_comma();
					w_as_parameter (&next_instruction->instruction_parameters[1]);
					w_as_newline();
					
					return next_instruction;					
				} else if (next_instruction->instruction_parameters[1].parameter_data.reg.r==instruction->instruction_parameters[1].parameter_data.reg.r){
					struct parameter parameter_0;

					parameter_0=w_as_float_parameter (instruction->instruction_parameters[0]);
					
# ifdef FSUB_FDIV_REVERSED
					if (next_instruction->instruction_parameters[1].parameter_flags)
						w_as_opcode ("fnmsub");
					else
# endif
						w_as_opcode ("fmsub");

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

	w_as_tryadic_float_instruction (instruction,"fmul");

	return instruction;
}
#endif

#ifdef FSUB_FDIV_REVERSED
static void w_as_tryadic_reversed_float_instruction (struct instruction *instruction,char *opcode)
{
	struct parameter parameter_0;

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

	w_as_opcode (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();
}
#endif

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 ("fadd");
											break;
										case IFSUB:
#ifdef FSUB_FDIV_REVERSED
											if (instruction->instruction_parameters[1].parameter_flags){
												int reg0_copy;
												
												reg0_copy=reg0;
												reg0=reg_s;
												reg_s=reg0_copy;
											}
#endif

											w_as_opcode ("fsub");
											break;
										case IFMUL:
#ifdef FMADD
										{
											struct instruction *next_of_next_instruction;
											
											next_of_next_instruction=next_instruction->instruction_next;
											if (fmadd_flag && next_of_next_instruction!=NULL){
												if (next_of_next_instruction->instruction_icode==IFADD &&
													next_of_next_instruction->instruction_parameters[0].parameter_type==P_F_REGISTER &&
													next_of_next_instruction->instruction_parameters[0].parameter_data.reg.r==reg1 &&
													next_of_next_instruction->instruction_parameters[0].parameter_flags & FP_REG_LAST_USE &&
													next_of_next_instruction->instruction_parameters[1].parameter_data.reg.r!=reg1)
												{
													w_as_opcode ("fmadd");

													w_as_parameter (&next_of_next_instruction->instruction_parameters[1]);
													w_as_comma();
													w_as_fp_register (reg0);
													w_as_comma();
													w_as_fp_register (reg_s);
													w_as_comma();
													w_as_parameter (&next_of_next_instruction->instruction_parameters[1]);
													w_as_newline();
													
													return next_of_next_instruction;
												} else if (next_of_next_instruction->instruction_icode==IFSUB &&
													next_of_next_instruction->instruction_parameters[0].parameter_type==P_F_REGISTER &&
													next_of_next_instruction->instruction_parameters[0].parameter_data.reg.r!=next_of_next_instruction->instruction_parameters[1].parameter_data.reg.r)
												{
													if (next_of_next_instruction->instruction_parameters[0].parameter_flags & FP_REG_LAST_USE &&
														next_of_next_instruction->instruction_parameters[0].parameter_data.reg.r==reg1)
													{
# ifdef FSUB_FDIV_REVERSED
														if (next_of_next_instruction->instruction_parameters[1].parameter_flags)
															w_as_opcode ("fmsub");
														else
# endif
															w_as_opcode ("fnmsub");

														w_as_parameter (&next_of_next_instruction->instruction_parameters[1]);
														w_as_comma();
														w_as_fp_register (reg0);
														w_as_comma();
														w_as_fp_register (reg_s);
														w_as_comma();
														w_as_parameter (&next_of_next_instruction->instruction_parameters[1]);
														w_as_newline();
														
														return next_of_next_instruction;					
													} else if (next_of_next_instruction->instruction_parameters[1].parameter_data.reg.r==reg1){
# ifdef FSUB_FDIV_REVERSED
														if (next_of_next_instruction->instruction_parameters[1].parameter_flags)
															w_as_opcode ("fnmsub");
														else
# endif
															w_as_opcode ("fmsub");

														w_as_parameter (&next_of_next_instruction->instruction_parameters[1]);
														w_as_comma();
														w_as_fp_register (reg0);
														w_as_comma();
														w_as_fp_register (reg_s);
														w_as_comma();
														w_as_parameter (&next_of_next_instruction->instruction_parameters[0]);
														w_as_newline();
														
														return next_of_next_instruction;
													}
												}
											}
										}
#endif
											w_as_opcode ("fmul");
											break;
										case IFDIV:
#ifdef FSUB_FDIV_REVERSED
											if (instruction->instruction_parameters[1].parameter_flags){
												int reg0_copy;
												
												reg0_copy=reg0;
												reg0=reg_s;
												reg_s=reg0_copy;
											}
#endif
											w_as_opcode ("fdiv");
											break;
										case IFREM:
											w_as_opcode ("frem");
									}
									w_as_fp_register (reg1);
									w_as_comma();
									w_as_fp_register (reg0);
									w_as_comma();
									w_as_fp_register (reg_s);
									w_as_newline();
									
									return next_instruction;
								}
						}

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

					return instruction;
				case P_F_IMMEDIATE:
					w_as_load_float_immediate (*instruction->instruction_parameters[0].parameter_data.r,instruction->instruction_parameters[1].parameter_data.reg.r);

					return instruction;
				case P_INDEXED:
					w_as_opcode ("lfdx");
					w_as_fp_register (instruction->instruction_parameters[1].parameter_data.reg.r);
					w_as_comma();
					w_as_indexed (instruction->instruction_parameters[0].parameter_offset,
								  instruction->instruction_parameters[0].parameter_data.ir);
					w_as_newline();

					return instruction;
			}
			break;
		case P_INDIRECT:
			if (instruction->instruction_parameters[0].parameter_type==P_F_REGISTER){
				w_as_opcode ("stfd");
				w_as_fp_register (instruction->instruction_parameters[0].parameter_data.reg.r);
				w_as_comma();
				w_as_indirect (instruction->instruction_parameters[1].parameter_offset,
							   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 ("stfdx");
				w_as_fp_register (instruction->instruction_parameters[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 instruction;
			}
			break;

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

extern LABEL *r_to_i_buffer_label;

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 ("fctiw");
		w_as_fp_register (17);
		w_as_comma();
		w_as_fp_register (instruction->instruction_parameters[0].parameter_data.reg.r);
		w_as_newline();

		w_as_load_label (r_to_i_buffer_label,REGISTER_O0);

		w_as_opcode ("stfd");
		w_as_fp_register (17);
		w_as_comma();
		w_as_indirect (0,REGISTER_O0);
		w_as_newline();

		w_as_opcode ("lwz");
		w_as_register_comma (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_indirect (4,REGISTER_O0);
		w_as_newline();
	} else {
		int reg;
		int label_number,t_label_number;
		struct label *new_label;

		switch (instruction->instruction_parameters[0].parameter_type){
			case P_REGISTER:
				reg=instruction->instruction_parameters[0].parameter_data.reg.r;
				break;
			case P_INDIRECT:
				w_as_opcode ("lwz");
				w_as_register_comma (REGISTER_O0);
				w_as_indirect (	instruction->instruction_parameters[0].parameter_offset,
								instruction->instruction_parameters[0].parameter_data.reg.r);
				w_as_newline();
	
				reg=REGISTER_O0;
				break;
			case P_IMMEDIATE:
				w_as_opcode ("li");
				w_as_register_comma (REGISTER_O0);
				w_as_immediate (instruction->instruction_parameters[0].parameter_data.i);
				w_as_newline();

				reg=REGISTER_O0;
				break;
			default:
				internal_error_in_function ("w_as_fmovel_instruction");
		}
		
		label_number=next_label_id++;

		new_label=(struct label*)allocate_memory_from_heap (sizeof (struct label));

		new_label->label_flags=0;	
		new_label->label_number=label_number;
	
		w_as_to_data_section();

#ifdef GNU_SYNTAX
		fprintf (assembly_file,"\t.align\t8" NEWLINE_STRING);
#else
		fprintf (assembly_file,"\talign\t3" NEWLINE_STRING);
#endif	
/*		w_as_define_internal_label (label_number); */
		w_as_define_local_label (label_number);
	
		fprintf (assembly_file,
			"\t" DC_L "\t0x43300000" NEWLINE_STRING
			"\t" DC_L "\t0x00000000" NEWLINE_STRING
			"\t" DC_L "\t0x43300000" NEWLINE_STRING
			"\t" DC_L "\t0x80000000" NEWLINE_STRING
		);
	
/*
		w_as_instruction_without_parameters ("toc");

		w_as_opcode ("tc");
		fprintf (assembly_file,"t_%d{TC}",t_label_number);
		w_as_comma();
		w_as_internal_label (label_number);
		w_as_newline();
*/
#ifndef GNU_SYNTAX
		t_label_number=make_toc_label (new_label,0);
#endif
		w_as_to_code_section();

		/*
		lwz		o1,t_n{TC}(RTOC)
		xoris	o0,reg,0x8000
		lfd		fp31,8(o1)
		stw		o0,4(o1)
		lfd		freg,0(o1)
		fsub	freg,freg,fp31
		*/
#ifdef GNU_SYNTAX
		load_label (new_label,0,REGISTER_O1);
#else
		w_as_opcode ("lwz");
		w_as_register_comma (REGISTER_O1);
		fprintf (assembly_file,"t_%d{TC}(RTOC)" NEWLINE_STRING,t_label_number);
#endif
		w_as_opcode ("xoris");
		w_as_register_comma (REGISTER_O0);
		w_as_register_comma (reg);
		w_as_immediate (0x8000);
		w_as_newline();
		
		w_as_opcode ("lfd");
		w_as_fp_register (17);
		w_as_comma();
		w_as_indirect (8,REGISTER_O1);
		w_as_newline();

		w_as_opcode ("stw");
		w_as_register_comma (REGISTER_O0);
		w_as_indirect (4,REGISTER_O1);
		w_as_newline();

		w_as_opcode ("lfd");
		w_as_fp_register (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_comma();
		w_as_indirect (0,REGISTER_O1);
		w_as_newline();

		w_as_opcode ("fsub");
		w_as_fp_register (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_comma();
		w_as_fp_register (instruction->instruction_parameters[1].parameter_data.reg.r);
		w_as_comma();
		w_as_fp_register (17);
		w_as_newline();
		
/*		++t_label_number; */
	}
}

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,"addi");
				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,"beq");
				break;
			case IBGE:
				w_as_branch_instruction (instruction,"bge");
				break;
			case IBGT:
				w_as_branch_instruction (instruction,"bgt");
				break;
			case IBLE:
				w_as_branch_instruction (instruction,"ble");
				break;
			case IBLT:
				w_as_branch_instruction (instruction,"blt");
				break;
			case IBNE:
				w_as_branch_instruction (instruction,"bne");
				break;
			case IBNEP:
				w_as_branch_instruction (instruction,"bne+");
				break;
			case IBHS:
				w_as_index_error_branch_instruction (instruction);
				break;
			case IBNO:
				w_as_branchno_instruction (instruction);
				break;
			case IBO:
				w_as_brancho_instruction (instruction);
				break;
			case ICMPLW:
				w_as_cmplw_instruction (instruction);
				break;
			case ILSLI:
				w_as_i_instruction (instruction,"slwi");
				break;
			case ILSL:
				w_as_tryadic_instruction (instruction,"slw");
				break;
			case ILSR:
				w_as_tryadic_instruction (instruction,"srw");
				break;
			case IASR:
				w_as_tryadic_instruction (instruction,"sraw");
				break;
			case IMUL:
				w_as_mul_instruction (instruction);
				break;
			case IDIV:
				w_as_div_instruction (instruction);
				break;
			case IMOD:
				w_as_mod_instruction (instruction);
				break;
			case IAND:
				w_as_and_instruction (instruction);
				break;
			case IOR:
				w_as_or_or_eor_instruction (instruction,"or");
				break;
			case IEOR:
				w_as_or_or_eor_instruction (instruction,"xor");
				break;
			case ISEQ:
				w_as_set_condition_instruction (instruction,"bne");
				break;
			case ISGE:
				w_as_set_condition_instruction (instruction,"blt");
				break;
			case ISGT:
				w_as_set_condition_instruction (instruction,"ble");
				break;
			case ISLE:
				w_as_set_condition_instruction (instruction,"bgt");
				break;
			case ISLT:
				w_as_set_condition_instruction (instruction,"bge");
				break;
			case ISNE:
				w_as_set_condition_instruction (instruction,"beq");
				break;
			case ISNO:
				w_as_setno_condition_instruction (instruction);
				break;
			case ISO:
				w_as_seto_condition_instruction (instruction);
				break;
			case ICMPW:
				w_as_cmp_instruction (instruction,SIZE_WORD);
				break;
			case ITST:
				w_as_tst_instruction (instruction);
				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 IEXTB:
				w_as_extb_instruction (instruction);
				break;
			case IFMOVE:
				instruction=w_as_fmove_instruction (instruction);
				break;
			case IFADD:
				w_as_tryadic_float_instruction (instruction,"fadd");
				break;
			case IFCMP:
				w_as_compare_float_instruction (instruction);
				break;
			case IFDIV:
#ifdef FSUB_FDIV_REVERSED
				if (instruction->instruction_parameters[1].parameter_flags)
					w_as_tryadic_reversed_float_instruction (instruction,"fdiv");
				else
#endif
					w_as_tryadic_float_instruction (instruction,"fdiv");
				break;
			case IFMUL:
#ifdef FMADD
				instruction=w_as_fmul_instruction (instruction);
#else
				w_as_tryadic_float_instruction (instruction,"fmul");
#endif
				break;
			case IFNEG:
				w_as_dyadic_float_instruction (instruction,"fneg");
				break;				
			case IFREM:
				w_as_tryadic_float_instruction (instruction,"frem");
				break;
			case IFSUB:
#ifdef FSUB_FDIV_REVERSED
				if (instruction->instruction_parameters[1].parameter_flags)
					w_as_tryadic_reversed_float_instruction (instruction,"fsub");
				else
#endif
					w_as_tryadic_float_instruction (instruction,"fsub");
				break;
			case IFBEQ:
				w_as_branch_instruction (instruction,"beq");
				break;
			case IFBGE:
				w_as_branch_instruction (instruction,"bge");
				break;
			case IFBGT:
				w_as_branch_instruction (instruction,"bgt");
				break;
			case IFBLE:
				w_as_branch_instruction (instruction,"ble");
				break;
			case IFBLT:
				w_as_branch_instruction (instruction,"blt");
				break;
			case IFBNE:
				w_as_branch_instruction (instruction,"bne");
				break;
			case IFMOVEL:
				w_as_fmovel_instruction (instruction);
				break;
			case IFSEQ:
				w_as_set_condition_instruction (instruction,"bne");
				break;
			case IFSGE:
				w_as_set_condition_instruction (instruction,"blt");
				break;
			case IFSGT:
				w_as_set_condition_instruction (instruction,"ble");
				break;
			case IFSLE:
				w_as_set_condition_instruction (instruction,"bgt");
				break;
			case IFSLT:
				w_as_set_condition_instruction (instruction,"bge");
				break;
			case IFSNE:
				w_as_set_condition_instruction (instruction,"beq");
				break;
			case IWORD:
				w_as_word_instruction (instruction);
				break;
			case IMTCTR:
				w_as_opcode ("mtctr");
				w_as_register (instruction->instruction_parameters[0].parameter_data.reg.r);
				w_as_newline();
				break;
			case IJMPP:
				w_as_jmpp_instruction (instruction);
				break;
			case IRTSP:
				w_as_rtsp_instruction (instruction);
				break;
			case IADDO:
				w_as_addo_instruction (instruction);
				break;
			case ISUBO:
				w_as_subo_instruction (instruction);
				break;
			case IMULO:
				w_as_mulo_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 (DC_L);
	fprintf (assembly_file,"%d",n_node_arguments);
	w_as_newline();
}

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

	n_cells= -block->block_n_new_heap_cells;	

	if (n_cells!=(WORD) n_cells){
		w_as_opcode ("addis");
		w_as_register_comma (REGISTER_D7);
		w_as_register_comma (REGISTER_D7);
		w_as_immediate ((n_cells-(WORD)n_cells)>>16);
		w_as_newline();
				
		n_cells=(WORD)n_cells;
	}

	w_as_opcode ("addic.");
	w_as_register_comma (REGISTER_D7);
	w_as_register_comma (REGISTER_D7);
	w_as_immediate (n_cells);
	w_as_newline();

	new_call_and_jump=allocate_new_call_and_jump();

	if (block->block_gc_kind!=0){
		int label_id_1;
				
		label_id_1=next_label_id++;
			
		new_call_and_jump->cj_label_id=label_id_1;
		new_call_and_jump->cj_jump_id=-1;

		switch (block->block_n_begin_a_parameter_registers){
			case 0:		new_call_and_jump->cj_call_label_name="collect_00";	break;
			case 1:		new_call_and_jump->cj_call_label_name="collect_01";	break;
			case 2:		new_call_and_jump->cj_call_label_name="collect_02";	break;
			case 3:		new_call_and_jump->cj_call_label_name="collect_03";	break;
			default:	internal_error_in_function ("w_as_garbage_collect_test");
		}

		if (block->block_gc_kind==2){
			w_as_opcode ("mflr");
			w_as_register (REGISTER_R0);
			w_as_newline();
		}

		w_as_opcode ("bltl-");
		w_as_internal_label (label_id_1);
		w_as_newline();

		if (block->block_gc_kind==1){
			w_as_opcode ("mtlr");
			w_as_register (REGISTER_R0);
			w_as_newline();
		}
	} else {
		int label_id_1,label_id_2;
				
		label_id_1=next_label_id++;
		label_id_2=next_label_id++;
			
		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");
		}
				
		w_as_opcode ("blt");
		w_as_internal_label (label_id_1);
		w_as_newline ();
	
		w_as_define_internal_label (label_id_2);
	}
}

static void w_as_check_stack (struct basic_block *block)
{
	int size;
	
	size=block->block_stack_check_size+1024;

	w_as_opcode ("sub");
	w_as_register_comma (REGISTER_O0);
	w_as_register_comma (B_STACK_POINTER);
	w_as_register (A_STACK_POINTER);
	w_as_newline();

	w_as_opcode ("cmpwi");
	w_as_register_comma (REGISTER_O0);
	w_as_immediate (size);
	w_as_newline();

	w_as_opcode ("ble");
	w_as_label (stack_overflow_label->label_name);
	w_as_newline();
}

static void w_as_labels (register 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);
}

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

	first_call_and_jump=NULL;
#ifndef GNU_SYNTAX
	fprintf (assembly_file,"\tstring\tasis" NEWLINE_STRING);
#endif
	data_module_number=0;
	code_module_number=0;
}

static void w_as_import_labels (register struct label_node *label_node)
{
	LABEL *label;
	
	if (label_node==NULL)
		return;
	
	label=&label_node->label_node_label;
	
	if (!(label->label_flags & LOCAL_LABEL) && label->label_number==0){
		w_as_opcode (IF_GNU (IF_MACH_O (".globl",".global"),"import"));
#ifdef MACH_O
		if (label->label_name[0]=='_'){
			char *label_name;
			int c;
				
			label_name=label->label_name;
			putc ('_',assembly_file);
			++label_name;
			while (c=*label_name++,c!=0)
				putc (c,assembly_file);

		} else
			w_as_label (label->label_name);
#else
		w_as_label (label->label_name);
#endif
		w_as_newline();
	}
		
	w_as_import_labels (label_node->label_node_left);
	w_as_import_labels (label_node->label_node_right);
}

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

#ifdef MACH_O
	first_stub=NULL;
	next_stub_l=&first_stub;
#endif
	
	w_as_to_code_section();
	
	w_as_import_labels (labels);

	for_l (block,first_block,block_next){
		if (block->block_begin_module && !block->block_link_module){
			if (first_call_and_jump!=NULL){
				struct call_and_jump *call_and_jump;
		
				for_l (call_and_jump,first_call_and_jump,cj_next)
					w_as_call_and_jump (call_and_jump);
				
				first_call_and_jump=NULL;
			}

			w_as_new_code_module();
		}

		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_load_label (block->block_ea_label,REGISTER_A2);

					if (!block->block_profile){					
						w_as_opcode ("b");
						w_as_label (eval_upd_labels[n_node_arguments]->label_name);
						w_as_newline();

						w_as_instruction_without_parameters ("nop");
#ifdef GNU_SYNTAX
						w_as_instruction_without_parameters ("nop");
#endif
					} else {						
						if (profile_table_flag){
							w_as_opcode ("b");
							w_as_label (eval_upd_labels[n_node_arguments]->label_name);
							fprintf (assembly_file,"-8");
							w_as_newline();

							w_as_load_label_with_offset (profile_table_label,32764,REGISTER_R3);

							w_as_opcode ("addi");
							w_as_register_comma (REGISTER_R3);
							w_as_register_comma (REGISTER_R3);
							w_as_immediate (block->block_profile_function_label->label_arity-32764);
							w_as_newline();
						
							w_as_opcode ("b");
							fprintf (assembly_file,IF_GNU ("$-16","*-16"));
							w_as_newline();
						} else {
							w_as_load_label (block->block_profile_function_label,REGISTER_R3);

							w_as_opcode ("b");
							w_as_label (eval_upd_labels[n_node_arguments]->label_name);
							fprintf (assembly_file,"-8");
							w_as_newline();
						}
					}
				} else {
					w_as_opcode ("b");
					w_as_label (block->block_ea_label->label_name);
					w_as_newline();
					
					w_as_instruction_without_parameters ("nop");
					w_as_instruction_without_parameters ("nop");
#ifdef GNU_SYNTAX
					w_as_instruction_without_parameters ("nop");
					w_as_instruction_without_parameters ("nop");
#endif
				}
				
				if (block->block_descriptor!=NULL && (block->block_n_node_arguments<0 || parallel_flag || module_info_flag))
#ifdef GNU_SYNTAX
					w_as_label_in_code_section (block->block_descriptor->label_name);
#else
					w_as_load_label (block->block_descriptor,REGISTER_R0);
#endif
				else
					w_as_number_of_arguments (0);
			} else {
				if (block->block_descriptor!=NULL && (block->block_n_node_arguments<0 || parallel_flag || module_info_flag))
#ifdef GNU_SYNTAX
					w_as_label_in_code_section (block->block_descriptor->label_name);
#else
					w_as_load_label (block->block_descriptor,REGISTER_R0);
#endif
				/* 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_profile){
			if (profile_table_flag){
				w_as_load_label_with_offset (profile_table_label,32764,REGISTER_R3);
			
				w_as_opcode ("mflr");
				w_as_register (REGISTER_R0);
				w_as_newline();
				
				w_as_opcode ("addi");
				w_as_register_comma (REGISTER_R3);
				w_as_register_comma (REGISTER_R3);
				w_as_immediate (block->block_profile_function_label->label_arity-32764);
				w_as_newline();
			} else {
				w_as_load_label (block->block_profile_function_label,REGISTER_R3);
			
				w_as_opcode ("mflr");
				w_as_register (REGISTER_R0);
				w_as_newline();
			}
			
			w_as_opcode ("bl");
			
			if (block->block_n_node_arguments>-100)
				w_as_label (block->block_profile==2 ? "profile_n2" : "profile_n");
			else {
				switch (block->block_profile){
					case 2:  w_as_label ("profile_s2"); break;
					case 4:  w_as_label ("profile_l"); break;
					case 5:  w_as_label ("profile_l2"); break;
					default: w_as_label ("profile_s");
				}
			}
			w_as_newline();
		}

		if (block->block_n_new_heap_cells!=0)
			w_as_garbage_collect_test (block);
		
		if (check_stack && block->block_stack_check_size>0)
			w_as_check_stack (block);
		
		w_as_instructions (block->block_instructions);
	}

	for_l (call_and_jump,first_call_and_jump,cj_next)
		w_as_call_and_jump (call_and_jump);

	*last_toc_next_p=NULL;

#ifndef GNU_SYNTAX
	w_as_instruction_without_parameters ("toc");

	for_l (toc_label,toc_labels,toc_next)
		w_as_toc_label (toc_label);	
#endif

#ifdef MACH_O
	write_stubs();
#endif
}

#endif
