This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: sh64-elf (SH5) port: directory gas


On Feb  3, 2002, Alexandre Oliva <aoliva@redhat.com> wrote:

> I'm extremely pleased to contribute the SH5 port of binutils,
> developed mostly by Hans-Peter Nilsson, with some contributions by DJ
> Delorie and Ben Elliston, later extended to support PIC by myself.

Index: gas/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>
	Contribute sh64-elf.
	2002-01-31  Alexandre Oliva  <aoliva@redhat.com>
	* config/tc-sh.c (md_relax_table): Added default sizes for
	non-PC-relative UNDEF_MOVI, and relaxation sequences for
	MOVI_16, MOVI_32 and MOVI_48.
	* config/tc-sh64.c (shmedia_md_apply_fix3): Fix warning.
	(shmedia_md_convert_frag): Handle non-PC-relative UNDEF_MOVI
	and MOVI_16.
	(shmedia_md_estimate_size_before_relax): Remove redundant
	blocks.  Set fragP->fr_var even if relaxation type unchanged.
	Retain UNDEF_MOVI until expression decays to number.
	2002-01-24  Alexandre Oliva  <aoliva@redhat.com>
	* config/tc-sh64.c (shmedia_init_reloc): Handle new SHmedia PIC
	relocation types.  Take fixP->fx_addnumber into account too.
	(shmedia_md_apply_fix): Likewise.
	(shmedia_md_convert_frag): Likewise.
	(shmedia_build_Mytes): Likewise.
	(sh64_consume_datalabel): Complain about nested datalabel.
	Support PIC relocs.  Call sh_parse_name.
	* config/tc-sh64.h (TC_RELOC_RTSYM_LOC_FIXUP): Extend definition
	in tc-sh.h to SHmedia reloc types.
	* config/tc-sh.c (SH64PCRELPLT, MOVI_PLT, MOVI_GOTOFF,
	MOVI_GOTPC): New relaxation constants.
	(md_relax_table): Introduce relaxation directives for PIC-related
	constants.
	(sh_PIC_related_p): Handle datalabel.
	(sh_check_fixup): Choose SH5 PIC relocations.
	(sh_cons_fix_new): Added BDF_RELOC_64.
	(md_apply_fix3, sh_parse_name): Handle GOTPLT.
	2002-01-18  Alexandre Oliva  <aoliva@redhat.com>
	* config/tc-sh64.c (sh64_max_mem_for_rs_align_code): If the
	current ISA is SHmedia, get 7 bytes.
	2001-11-28  Nick Clifton  <nickc@cambridge.redhat.com>
	* config/tc-sh.c (md_apply_fix3): Treat shmedia_md_apply_fix3 as a
	void function.
	* config/tc-sh64.c (shmedia_apply_fix): Rename to
	shmedia_apply_fix3 and make void.
	2001-05-17  Alexandre Oliva  <aoliva@redhat.com>
	* config/tc-sh64.c (s_sh64_abi): Remove unused arguments passed to
	as_bad.
	2001-04-12  Alexandre Oliva  <aoliva@redhat.com>
	* config/tc-sh64.h (md_parse_name): Take &c as argument.
	2001-03-14  DJ Delorie  <dj@redhat.com>
	* doc/Makefile.am (CPU_DOCS): Added c-sh64.texi
	* doc/Makefile.in(CPU_DOCS): Ditto.
	* doc/c-sh64.texi: New file.
	* doc/as.texinfo: Add SH64 support.
	2001-03-13  DJ Delorie  <dj@redhat.com>
	* config/tc-sh64.c (shmedia_get_operands): Rename A_RESV_Fx to
	A_REUSE_PREV so that its purpose is more obvious.
	(shmedia_build_Mytes): Ditto.
	2001-03-07  DJ Delorie  <dj@redhat.com>
	* config/tc-sh64.c (sh64_vtable_entry): New, strip datalabels
	before processing.
	(sh64_vtable_inherit): Ditto.
	(strip_datalabels): New, strip "datalabel" from given line.
	* config/tc-sh.c (md_pseudo_table): Add sh64-specific vtable
	pseudos.
	2001-03-06  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (shmedia_md_assemble): Move dwarf2_emit_insn
	call ...
	(shmedia_build_Mytes): ... to here.
	2001-03-06  DJ Delorie  <dj@redhat.com>
	* config/tc-sh.c: Remove sh64-specific uaquad now that there
	is a generic one.
	2001-01-21  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.h (DWARF2_LINE_MIN_INSN_LENGTH): Override.
	* config/tc-sh64.c (shmedia_md_assemble): Offset recorded insn
	address by one in call to dwarf2_emit_insn.
	2001-01-13  Hans-Peter Nilsson  <hpn@cygnus.com>
	Implement ".abi" pseudo and correct .cranges descriptors.  Correct
	alignment handling broken by imported changes.
	* config/tc-sh64.h (HANDLE_ALIGN): Override definition in tc-sh.h.
	(sh64_handle_align): Declare.
	(MAX_MEM_FOR_RS_ALIGN_CODE): Override definition in tc-sh.h.
	(sh64_max_mem_for_rs_align_code): Declare.
 	(enum sh64_isa_values): Moved here from tc-sh64.c.
	(md_do_align): Define.
	(sh64_do_align): Declare.
	(struct sh64_tc_frag_data): New.
	(TC_FRAG_TYPE): Change to struct sh64_tc_frag_data.  Users
	changed.
	(TC_FRAG_INIT): Change to set new datatype.
	(struct sh64_segment_info_type): Rename member
	last_flushed_location to last_contents_mark.  All users changed.
	(md_elf_section_change_hook, TC_CONS_FIX_NEW): Do not define.
	(shmedia_elf_new_section, sh64_tc_cons_fix_new): Do not prototype.
	* config/tc-sh.c (md_pseudo_table): Add ".abi".
	(sh_elf_cons) [HAVE_SH64]: Call sh64_update_contents_mark instead
	of unsetting seen_insn.
	(md_assemble) [HAVE_SH64] <before new SHcompact sequence>: Also
	call sh64_update_contents_mark.
	(sh_handle_align): Remove HAVE_SH64-conditioned code.
	* config/tc-sh64.c (sh64_isa_mode): Correct type from boolean to
	enum sh64_isa_values.
	(sh64_set_contents_type): Drop segT parameter.  All callers changed.
	(emitting_crange): Boolean guard moved to file scope from function
	scope in sh64_set_contents_type.
	(s_sh64_abi): New.
	(sh64_update_contents_mark): New; most split out from
	sh64_flush_pending_output.
	(shmedia_md_end): Call sh64_update_contents_mark.  Set
	sh64_isa_mode to sh64_isa_sh5_guard unless sh64_isa_unspecified.
	(sh64_do_align): New function.
	(sh64_max_mem_for_rs_align_code): New function.
	(sh64_handle_align): Rename from shmedia_do_align.  Make
	non-static.  Add head comment.  Emit zero bytes for n bytes modulo
	four.  Change return-type to void.
	(shmedia_elf_new_section): Remove.
	(shmedia_md_assemble): Call sh64_update_contents_mark.
	(s_sh64_mode): Ditto.  Do not call md_flush_pending_output.  Make
	new frag.  Call sh64_update_contents_mark after making the new
	frag.
	(sh64_flush_pending_output): Just call sh64_update_contents_mark
	and sh_flush_pending_output.
	(sh64_flag_output): Also call md_flush_pending_output, but add
	condition on not emitting_crange.
	(sh64_tc_cons_fix_new): Remove.
	2001-01-12  Nick Clifton  <nickc@redhat.com>
	* config/tc-sh64.c (shmedia_do_align): Fix to work with new
	alignment handling scheme imported from sourceware.
	2001-01-12  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.h (TARGET_FORMAT): Define.
	(sh64_target_format): Prototype.
	* config/tc-sh64.c (sh64_target_mach): New function.
	2001-01-07  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (shmedia_md_end): When equating a symbol, use
	zero_address_frag instead of copying the frag of the symbol.
	(shmedia_frob_file_before_adjust): Ditto.
	(shmedia_md_apply_fix) <case BFD_RELOC_SH_IMM_MEDLOW16>: Cast mask
	to valueT to remove signedness.
	(shmedia_md_convert_frag): Add parameter final.  Rename parameter
	headers to output_bfd. 	Do not evaluate symbols if final is false;
	do emit fixups.
	(shmedia_md_estimate_size_before_relax) <case C (MOVI_IMM_32,
	UNDEF_MOVI) et al>: If symbol cannot be modified to be PC-relative
	to the current frag, call shmedia_md_convert_frag to emit fixups
	and make frag_wane neutralize the frag.  Update comments.
	* config/tc-sh.c (md_convert_frag): Change caller of
	shmedia_md_convert_frag.
	2001-01-06  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.h: Tweak comments and correct formatting.
	* config/tc-sh64.c: Ditto.
	(shmedia_md_convert_frag) <PT/PTA/PTB 32, 48 and 64 bit
	expansion, MOVI pcrel expansion>: Fix thinko calculating offset
	for the no-relocation case.
	(shmedia_check_limits): Fix range check being off-by-one for PTA.
	* config/tc-sh.c: Ditto.  Add proper comments to #ifdef/#ifndef
	wrappers.
	(SH64PCREL16_F): Increment for proper max-PTA handling.  Update
	comment.
	(SH64PCREL16_M, MOVI_16_M): Correct range thinko.
 	(SH64PCREL48_M, MOVI_48_M): Similar; don't count in length of
	expansion.
	(SH64PCREL32_M, MOVI_32_M): Ditto; handle overflowing expression.
	Correct comment.
	2001-01-05  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (shmedia_md_apply_fix) <second switch, case
	BFD_RELOC_SH_PT_16>: Set lowest bit in field to be relocated to 1.
	(shmedia_md_convert_frag) <case C (SH64PCREL16_32, SH64PCREL16) et
	al>: Set lowest bit of field to relocate to 1 and rest to empty,
	if reloc is emitted. 
	2000-12-31  Hans-Peter Nilsson  <hpn@cygnus.com>
	New options plus bugfixes.
	* config/tc-sh.c (md_longopts): New options "-no-expand" and
	"-expand-pt32".
	(md_parse_option): Handle new options.
	(md_show_usage): Add blurb for new options.
	* config/tc-sh64.c (SHMEDIA_BFD_RELOC_PT): New macro.
	(sh64_expand, sh64_pt32): New variables.
	(shmedia_init_reloc): Handle BFD_RELOC_SH_PT_16.
	(shmedia_md_apply_fix): Hold original fixP->fx_r_type in
	orig_fx_r_type.  Change SHMEDIA_BFD_RELOC_PT into
	BFD_RELOC_SH_PT_16.  Handle BFD_RELOC_SH_PT_16 as pc-relative.
	<resolved previously-pc-relative relocs>: Handle
	SHMEDIA_BFD_RELOC_PT and BFD_RELOC_SH_PT_16.
	(shmedia_md_convert_frag) <case C (SH64PCREL16PT_64, SH64PCREL16),
	case C (SH64PCREL16PT_32, SH64PCREL16)>: Modify to PTB if operand
	points to SHcompact code.
	<case C (SH64PCREL16_32, SH64PCREL16), case C (SH64PCREL16_64,
	SH64PCREL16)>: Check that ISA of what operand points at and
	PTA/PTB matches, or emit error.
	(shmedia_check_limits): Handle BFD_RELOC_SH_PT_16 and
	SHMEDIA_BFD_RELOC_PT.
	(shmedia_immediate_op): If pcrel, emit fixup also for constant
	operand.
	(shmedia_build_Mytes) <case A_IMMS16>: Also check sh64_expand in
	condition for MOVI expansion.
	<case A_PCIMMS16BY4>: Handle expansion to 32 bits only, if
	sh64_pt32.  Emit only a BFD_RELOC_SH_PT_16 fixup if not
	sh64_expand.
	<case A_PCIMMS16BY4_PT>: Likewise, but emit a SHMEDIA_BFD_RELOC_PT
	fixup.
	(sh64_target_format): Error-check setting of sh64_pt32 and
	sh64_expand.  Fix typo in check for sh64_shcompact_const_crange.
	(shmedia_md_pcrel_from_section): Handle BFD_RELOC_SH_PT_16 and
	SHMEDIA_BFD_RELOC_PT as coming from SHmedia code.
	2000-12-31  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c: Improve comments.
	(shmedia_md_convert_frag): Remove inactive is_pt_variant code.  Do
	not say the linker will check correctness of PTA/PTB expansion.
	(shmedia_md_end): Make non-static.
	* config/tc-sh64.h (md_end): Define to shmedia_md_end.  Add
	prototype.
	* config/tc-sh.c (sh_finalize): Remove.
	* config/tc-sh.h (md_end): Do not define.
	Remove prototype for sh_finalize.
	2000-12-30  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (shmedia_frob_section_type): Use a struct
 	sh64_section_data container when storing section type in tdata
 	field in elf_section_data.
	* config/tc-sh.c (sh_elf_final_processing): Change from EF_SH64 to
	EF_SH5.
	* Makefile.am: Update dependencies.
	* Makefile.in: Regenerate.
	2000-12-22  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (shmedia_md_assemble): Don't protect
	dwarf2_emit_insn call with test on debug_type.
	2000-12-19  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (sh64_set_contents_type): Make contents-type
	CRT_SH5_ISA32 sticky for 64-bit.
	2000-12-18  Hans-Peter Nilsson  <hpn@cygnus.com>
	Generate .crange sections when switching ISA mode or emitting
	constants in same section as code.
	* config/tc-sh64.c: Reformat structure definitions.
	(sh64_end_of_assembly, sh64_mix, sh64_shcompact_const_crange): New
	variables.
	(sh64_set_contents_type): Rename from sh64_init_section.  Rewrite
	to emit a .cranges descriptor when contents type changes.  Only
	emit error if changing contents type and -no-mix is in effect.
	(sh64_emit_crange, sh64_flush_last_crange, sh64_flag_output,
	sh64_flush_pending_output, sh64_tc_cons_fix_new): New functions.
	(shmedia_md_end): Set sh64_end_of_assembly.  Pass
	sh64_flush_last_crange over sections.
	When checking main symbol of datalabel symbol, check for
	STO_SH5_ISA32, not ISA type of section in definition.
	(shmedia_frob_file_before_adjust): Check main symbol for
	STO_SH5_ISA32; don't check ISA type of section in definition.
	(shmedia_frob_section_type): Adjust for .cranges; set section flag
	to SHF_SH5_ISA32_MIXED or SHF_SH5_ISA32 according to whether
	.cranges entries have been output.
	(shmedia_elf_new_section): Just call md_flush_pending_output.
	(shmedia_md_assemble): Do not emit a BFD_RELOC_SH_SHMEDIA_CODE
	fix.  Do not set tc_segment_info_data.in_code for section.  Call
	sh64_set_contents_type for SHmedia code.
	(s_sh64_mode): Do not call sh64_init_section or set seen_insn to
	false.  Call md_flush_pending_output.
	(sh64_target_format): Check that -no-mix and
	-shcompact-const-crange are used in sane combination with other
	options.
	(shmedia_md_pcrel_from_section): Check type of fix for how to
	adjust pc-relative.
	(sh64_consume_datalabel): Check symbol for having STO_SH5_ISA32,
	not ISA type of section in definition.
	* config/tc-sh64.h (struct sh64_segment_info_type): Rewrite to
	hold contents-type state.
	(md_flush_pending_output): Redefine to sh64_flush_pending_output.
	(sh64_flush_pending_output): Declare.
	(TC_CONS_FIX_NEW): Define to sh64_tc_cons_fix_new.
	(sh64_tc_cons_fix_new): Declare.
	* config/tc-sh.c (sh_elf_cons) [HAVE_SH64]: Unset seen_insn and
	call sh64_flag_output.
	(md_assemble) [HAVE_SH64]: Do not emit BFD_RELOC_SH_CODE.  Just
	call sh64_set_contents_type to mark SHcompact code and set
	seen_insn.
	(md_longopts): New options "-no-mix" and
	"-shcompact-const-crange".
	(md_parse_option): Handle new options.
	(md_show_usage): Add blurb for new options.
	(md_number_to_chars) [HAVE_SH64]: Call sh64_flag_output.
	2000-12-15  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c: Delete investigated and obsolete fixme:s.
 	(sh64_last_insn_frag): New.
	(shmedia_md_convert_frag): Use tc_frag_data field of incoming frag
	to get frag for insn opcode for generating fixups; do not assume it
	is the same frag.
	(shmedia_build_Mytes): Set sh64_last_insn_frag after growing frag
	for new insn.
	* config/tc-sh64.h (ELF_TC_SPECIAL_SECTIONS): Define for .cranges
	section.
	(TC_FRAG_TYPE): Define as fragS *.
	(TC_FRAG_INIT): Define to set tc_frag_data to sh64_last_insn_frag.
	(sh64_last_insn_frag): Declare.
	(sh64_consume_datalabel): Fix typo; check for seginfo != NULL,
	not == NULL before dereferencing.
	2000-12-12  Hans-Peter Nilsson  <hpn@cygnus.com>
	Get rid of BFD section flag and EF_SH64_ABI64.
	* config/tc-sh64.c (shmedia_frob_section_type): Use
	elf_section_data (sec)->tdata, not a specific BFD section flag, to
	communicate the section as containing SHmedia code.  Describe why.
	* config/tc-sh.c (sh_elf_final_processing): Tweak comment.  Set
	EF_SH64 regardless of ABI.
	* config/tc-sh64.c (shmedia_md_apply_fix): Decapitalize "invalid"
	in error message.  Handle resolved expressions for
	BFD_RELOC_SH_IMMS10, BFD_RELOC_SH_IMMS10BY2,
	BFD_RELOC_SH_IMMS10BY4 and BFD_RELOC_64.
	(shmedia_check_limits): Handle BFD_RELOC_64.
	(sh64_adjust_symtab): Do not decrement the GAS symbol value for
	a STO_SH5_ISA32 symbol, only the BFD value.
	2000-12-11  Ben Elliston  <bje@redhat.com>
	* config/tc-sh64.c: Call dwarf2_emit_insn, not the defunct
	dwarf2_generate_asm_lineno.
	2000-12-11  Hans-Peter Nilsson  <hpn@cygnus.com>
	Handle PC-relative MOVI expansions with assembler relaxation.
	Generate PC-relative relocs from 16-bit PC-relative expressions.
	* config/tc-sh64.c (SHMEDIA_MD_PCREL_FROM_FIX): Break out from...
	(shmedia_md_pcrel_from_section): ...here.
	(shmedia_md_apply_fix): Handle fixups for 16-bit operands that has
	turned completely resolved.  Adjust relocation type for 16-bit
	immediate operands that has turned PC-relative.  Adjust back for
	MD_PCREL_FROM_SECTION being applied twice.
	(shmedia_md_convert_frag): Always emit reloc for expression with
	global or weak symbol.  Handle relaxation result for PC-relative
	expressions.
	(shmedia_md_estimate_size_before_relax): An expression with a weak
	or global symbol can not be relaxed.  Break out tests for
	relaxable symbol into variable sym_relaxable.
	<cases C (MOVI_IMM_64, UNDEF_MOVI) and C (MOVI_IMM_32,
	UNDEF_MOVI)>: Break out any PC-relative expression and change
	relaxation type.
	(shmedia_build_Mytes): CSE &operands->operands[j] into variable
	opjp.
	<case A_IMMS16>: Fix typo for initial minor relaxation type of
	MOVI expansion.  If X_op_symbol of the immediate expression is
	set, make an expression symbol for the argument to frag_var.
	* config/tc-sh.c (MOVI_IMM_32_PCREL, MOVI_IMM_64_PCREL): New
	relaxations.
	(END): Adjust for new relaxations.
	(md_relax_table): Add entries for new relaxations.
	2000-12-07  Ben Elliston  <bje@redhat.com>
	* config/tc-sh64.c (shmedia_parse_reg): Initialize variable len.
	2000-12-07  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (shmedia_md_convert_frag): Correct all MOVI and
	SHORI operand offsets in PT/PTA/PTB expansions.
	2000-12-05  Hans-Peter Nilsson  <hpn@cygnus.com>
	Implement DataLabel semantics.
	* config/tc-sh.c (sh_frob_file) [HAVE_SH64]: Call
	shmedia_frob_file_before_adjust.
	* config/tc-sh64.c [! OBJ_ELF]: Emit #error.
	(DATALABEL_SUFFIX): Define.
	(shmedia_md_end) <before adjusting STO_SH5_ISA32 symbols>: Walk
	symbol list to update "datalabel" symbols to their main symbol
	counterparts.
	(shmedia_frob_file_before_adjust): New.
	(sh64_adjust_symtab): For remaining datalabel symbols, set to
	undefined and set STT_DATALABEL.
	(sh64_frob_label): Initialize TC symbol field.
	(sh64_consume_datalabel): Actually implement semantics.  New
	parameter operandf, call it instead of expression.
	(sh64_exclude_symbol): New.
	* config/tc-sh64.h (md_parse_name): Pass on the function operand
	to sh64_consume_datalabel.
	(tc_symbol_new_hook): Define to tc_frob_symbol.
	(TC_SYMFIELD_TYPE): Define to symbolS *.
	(tc_frob_symbol): Define to call sh64_exclude_symbol.
	2000-12-01  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (shmedia_init_reloc): Tweak comment for default
	case.
	(shmedia_md_assemble): Call dwarf2_generate_asm_lineno if
	generating dwarf2 debug information.
	2000-11-30  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (sh64_target_format): Use elf64-sh64l and
	elf64-sh64 for the 64-bit ABI.
	* config/tc-sh.c (md_show_usage): Tweak usage output for -abi=*
	option.
	2000-11-29  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh.c: Remove conditionalizing on HAVE_SH64 for
	case-insensitivity.
	2000-11-27  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c: Tweak comments, formatting and error messages.
 	(enum sh64_abi_values): New type.
	(enum sh64_isa_values): New type.
 	(sh64_isa_mode): Replace shmedia_mode.  All referers changed.
	(seen_shcompact_mode, seen_shmedia_mode): Delete.
	(sh64_abi): Replace shmedia_64.
	(shmedia_md_convert_frag) <C (MOVI_IMM_64, MOVI_64),
 	C (MOVI_IMM_32, MOVI_32)>: Correct register number handling.
	(s_sh64_mode): Check validity for this target.
	(sh64_target_format): Initialize defaults for ISA and ABI.
	Fallback to old object format if no SH64 ISA or ABI has been
	specified.
	* config/tc-sh.c (md_parse_option): Check combinations for errors.
	(sh_elf_final_processing): Change to have EF_SH64_ABI64 for 64-bit
	ABI and EF_SH64 for 32-bit ABI, if SH64 options are specified.
	* config/tc-sh64.h: Fix typo in comment.
	2000-11-25  Hans-Peter Nilsson  <hpn@cygnus.com>
	* config/tc-sh64.c (shmedia_md_estimate_size_before_relax)
	<PT fixups for absolute values>: Size will be longest, not
	shortest.
	(shmedia_md_convert_frag): Disable PTB-warning machinery.  Correct
	all MOVI and SHORI operand offsets in PT/PTA/PTB expansions.
	* config/tc-sh.c (parse_reg) [HAVE_SH64]: Add local variables l0
	and l1 to hold lowercase of two first characters.  Change all
	remaining TO_LOWER to tolower.
	* config/tc-sh64.c (TO_LOWER): Delete.
	(shmedia_find_cooked_opcode): Use tolower, not TO_LOWER.
	(md_parse_name): Define.
	(sh64_consume_datalabel): Declare.
	(DOLLAR_DOT): Define.
	* config/tc-sh64.c (shmedia_parse_exp): New.
	(sh64_consume_datalabel): New; just ignoring datalabel semantics.
	(shmedia_parse_reg): Remove const from src
	parameter.
	(shmedia_get_operands): Ditto for args parameter and ptr variable.
	(shmedia_md_assemble): Ditto for op_end variable.
	(shmedia_get_operand): Ditto for ptr parameter and src variable.
	Use shmedia_parse_exp, not parse_exp.
	* config/tc-sh64.c (shmedia_parse_reg): Add shmedia_arg_type
	parameter.  All callers changed.
	(shmedia_get_operand): Add shmedia_arg_type parameter.  All
	callers changed.
	(shmedia_parse_reg): Put first two character in local variables.
	Use tolower, not TO_LOWER.  If no register is found and argtype
	indicates a control register, scan shmedia_creg_table
	case-insensitive.
	2000-11-24  Hans-Peter Nilsson  <hpn@cygnus.com>
	* Makefile.am (CPU_TYPES): Add sh64.
	(TARGET_CPU_CFILES): Add config/tc-sh64.c.
	(TARGET_CPU_HFILES): Add config/tc-sh64.h.
	Regenerate dependencies.
	* Makefile.in: Regenerate.
	* configure.in: Add support for sh64-*-elf*.
	* configure: Regenerate.
	* config/tc-sh64.h: New.
	* config/tc-sh64.c: New.
	* config/tc-sh.c (md_pseudo_table) [HAVE_SH64]: New pseudos
	.mode, .isa and .uaquad.
	[HAVE_SH64] (SH64PCREL16_32, SH64PCREL16_64, SH64PCREL16PT_32,
	SH64PCREL16PT_64, MOVI_IMM_32, MOVI_IMM_64): Define.
	[HAVE_SH64] (END): Define as 10.
	[HAVE_SH64] (UNDEF_SH64PCREL, SH64PCREL16, SH64PCREL32,
	SH64PCREL48, SH64PCREL64, UNDEF_MOVI, MOVI_16, MOVI_32, MOVI_48,
	MOVI_64): Define.
	[HAVE_SH64] (SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH,
	SH64PCREL32_F, SH64PCREL32_M, SH64PCREL32_LENGTH, SH64PCREL48_F,
	SH64PCREL48_M, SH64PCREL48_LENGTH, SH64PCREL64_LENGTH,
	MOVI_16_LENGTH, MOVI_32_LENGTH, MOVI_48_LENGTH, MOVI_64_LENGTH):
	Define.
	(md_relax_table) [HAVE_SH64]: Provide relaxations for SHmedia.
	(md_begin) [HAVE_SH64]: Call shmedia_md_begin.
	(parse_reg) [HAVE_SH64]: Parse register names case-insensitive.
	(md_assemble) [HAVE_SH64]: Call shmedia_md_assemble if assembling
	SHmedia instructions.  Handle state-change after switching to
	SHcompact.
	(md_longopts) [HAVE_SH64]: New options --isa=* and --abi=*.
	(md_parse_option) [HAVE_SH64]: Parse new options.
	(md_show_usage) [HAVE_SH64]: Show usage of new options.
	(md_convert_frag) [HAVE_SH64] <default>: Call
	shmedia_md_convert_frag instead of abort.
	(sh_force_relocation) [HAVE_SH64]: Also force relocation for
	BFD_RELOC_SH_SHMEDIA_CODE.
	(sh_elf_final_processing) [HAVE_SH64]: Set flags identifying
	SHcompact or SHmedia code.
	(md_apply_fix) [HAVE_SH64] <default>: Return result from calling
	shmedia_md_apply_fix instead of abort.
	(md_estimate_size_before_relax) [HAVE_SH64] <default>: Return
	result from calling shmedia_md_estimate_size_before_relax instead
	of calling abort.
	(sh_do_align) [HAVE_SH64]: If shmedia_mode, let shmedia_do_align
	do the work.
	(tc_gen_reloc) [HAVE_SH64]: For unrecognized relocs, call
	shmedia_init_reloc and do nothing more if it returns non-zero.
	(sh_finalize) [HAVE_SH64]: Call shmedia_md_end.
	* po/POTFILES.in: Regenerate.
	* po/gas.pot: Regenerate.

Index: gas/Makefile.am
===================================================================
RCS file: /home/aoliva/cygnus/uberbaum/gas/Makefile.am,v
retrieving revision 1.48
diff -u -p -r1.48 Makefile.am
--- gas/Makefile.am 1 Feb 2002 03:26:33 -0000 1.48
+++ gas/Makefile.am 2 Feb 2002 04:00:29 -0000
@@ -79,6 +79,7 @@ CPU_TYPES = \
 	vax \
 	w65 \
 	v850 \
+	sh64 \
 	xstormy16 \
 	z8k
 
@@ -258,6 +259,7 @@ TARGET_CPU_CFILES = \
 	config/tc-ppc.c \
 	config/tc-s390.c \
 	config/tc-sh.c \
+	config/tc-sh64.c \
 	config/tc-sparc.c \
 	config/tc-tahoe.c \
 	config/tc-tic30.c \
@@ -304,6 +306,7 @@ TARGET_CPU_HFILES = \
 	config/tc-ppc.h \
 	config/tc-s390.h \
 	config/tc-sh.h \
+	config/tc-sh64.h \
 	config/tc-sparc.h \
 	config/tc-tahoe.h \
 	config/tc-tic30.h \
@@ -1784,6 +1787,20 @@ DEPOBJ_sh_elf = $(INCDIR)/symcat.h $(src
   $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(srcdir)/config/tc-sh.h \
   $(INCDIR)/safe-ctype.h subsegs.h $(INCDIR)/obstack.h \
   $(INCDIR)/aout/aout64.h
+DEPTC_sh64_elf = $(srcdir)/config/obj-elf.h $(BFDDIR)/elf-bfd.h \
+  $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
+  $(INCDIR)/bfdlink.h $(srcdir)/config/tc-sh64.h $(srcdir)/config/tc-sh.h \
+  $(srcdir)/../opcodes/sh64-opc.h $(srcdir)/config/tc-sh.c \
+  subsegs.h $(INCDIR)/obstack.h $(srcdir)/../opcodes/sh-opc.h \
+  struc-symbol.h $(INCDIR)/elf/sh.h $(INCDIR)/elf/reloc-macros.h \
+  dwarf2dbg.h $(INCDIR)/symcat.h
+DEPOBJ_sh64_elf = $(srcdir)/config/obj-elf.h $(BFDDIR)/elf-bfd.h \
+  $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
+  $(INCDIR)/bfdlink.h $(srcdir)/config/tc-sh64.h $(srcdir)/config/tc-sh.h \
+  subsegs.h $(INCDIR)/obstack.h $(INCDIR)/aout/aout64.h $(INCDIR)/elf/sh.h
+DEP_sh64_elf = $(srcdir)/config/obj-elf.h $(BFDDIR)/elf-bfd.h \
+  $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h $(INCDIR)/elf/external.h \
+  $(INCDIR)/bfdlink.h $(srcdir)/config/tc-sh64.h $(srcdir)/config/tc-sh.h
 DEPOBJ_sparc_aout = $(INCDIR)/symcat.h $(srcdir)/config/obj-aout.h \
   $(srcdir)/config/tc-sparc.h $(BFDDIR)/libaout.h $(INCDIR)/bfdlink.h \
   $(INCDIR)/aout/aout64.h $(INCDIR)/obstack.h
Index: gas/configure.in
===================================================================
RCS file: /home/aoliva/cygnus/uberbaum/gas/configure.in,v
retrieving revision 1.95
diff -u -p -r1.95 configure.in
--- gas/configure.in 2 Feb 2002 18:36:01 -0000 1.95
+++ gas/configure.in 3 Feb 2002 00:57:45 -0000
@@ -145,6 +145,7 @@ changequote([,])dnl
       rs6000*)		cpu_type=ppc ;;
       s390x*)		cpu_type=s390 arch=s390x ;;
       s390*)		cpu_type=s390 arch=s390 ;;
+      sh64*)            cpu_type=sh64 endian=big;;
       sh*le)		cpu_type=sh endian=little ;;
       sh*)		cpu_type=sh endian=big ;;
       sparclite*)	cpu_type=sparc arch=sparclite ;;
@@ -425,6 +426,7 @@ changequote([,])dnl
       sh-*-rtemself*)       fmt=elf ;;
       sh-*-rtems*)	    fmt=coff bfd_gas=yes;;
 
+      sh64-*-elf*)	    fmt=elf ;;
       ns32k-pc532-mach* | ns32k-pc532-ux*)    fmt=aout em=pc532mach ;;
       ns32k-pc532-netbsd* | ns32k-pc532-lites*)  fmt=aout em=nbsd532 ;;
       ns32k-pc532-openbsd*) fmt=aout em=nbsd532 ;;
Index: gas/config/tc-sh.c
===================================================================
RCS file: /home/aoliva/cygnus/uberbaum/gas/config/tc-sh.c,v
retrieving revision 1.53
diff -u -p -r1.53 tc-sh.c
--- gas/config/tc-sh.c 30 Jan 2002 18:25:30 -0000 1.53
+++ gas/config/tc-sh.c 2 Feb 2002 04:00:30 -0000
@@ -140,6 +140,18 @@ const pseudo_typeS md_pseudo_table[] =
   {"file", dwarf2_directive_file, 0 },
   {"loc", dwarf2_directive_loc, 0 },
 #endif
+#ifdef HAVE_SH64
+  {"mode", s_sh64_mode, 0 },
+
+  /* Have the old name too.  */
+  {"isa", s_sh64_mode, 0 },
+
+  /* Assert that the right ABI is used.  */
+  {"abi", s_sh64_abi, 0 },
+
+  { "vtable_inherit", sh64_vtable_inherit, 0 },
+  { "vtable_entry", sh64_vtable_entry, 0 },
+#endif /* HAVE_SH64 */
   {0, 0, 0}
 };
 
@@ -172,11 +184,37 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define GET_WHAT(x) ((x>>4))
 
 /* These are the three types of relaxable instrction.  */
+/* These are the types of relaxable instructions; except for END which is
+   a marker.  */
 #define COND_JUMP 1
 #define COND_JUMP_DELAY 2
 #define UNCOND_JUMP  3
+
+#ifdef HAVE_SH64
+
+/* A 16-bit (times four) pc-relative operand, at most expanded to 32 bits.  */
+#define SH64PCREL16_32 4
+/* A 16-bit (times four) pc-relative operand, at most expanded to 64 bits.  */
+#define SH64PCREL16_64 5
+
+/* Variants of the above for adjusting the insn to PTA or PTB according to
+   the label.  */
+#define SH64PCREL16PT_32 6
+#define SH64PCREL16PT_64 7
+
+/* A MOVI expansion, expanding to at most 32 or 64 bits.  */
+#define MOVI_IMM_32 8
+#define MOVI_IMM_32_PCREL 9
+#define MOVI_IMM_64 10
+#define MOVI_IMM_64_PCREL 11
+#define END 12
+
+#else  /* HAVE_SH64 */
+
 #define END 4
 
+#endif /* HAVE_SH64 */
+
 #define UNDEF_DISP 0
 #define COND8  1
 #define COND12 2
@@ -186,6 +224,24 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define UNCOND12 1
 #define UNCOND32 2
 
+#ifdef HAVE_SH64
+#define UNDEF_SH64PCREL 0
+#define SH64PCREL16 1
+#define SH64PCREL32 2
+#define SH64PCREL48 3
+#define SH64PCREL64 4
+#define SH64PCRELPLT 5
+
+#define UNDEF_MOVI 0
+#define MOVI_16 1
+#define MOVI_32 2
+#define MOVI_48 3
+#define MOVI_64 4
+#define MOVI_PLT 5
+#define MOVI_GOTOFF 6
+#define MOVI_GOTPC 7
+#endif /* HAVE_SH64 */
+
 /* Branch displacements are from the address of the branch plus
    four, thus all minimum and maximum values have 4 added to them.  */
 #define COND8_F 258
@@ -216,6 +272,85 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 #define UNCOND32_M -(1<<30)
 #define UNCOND32_LENGTH 14
 
+#ifdef HAVE_SH64
+/* The trivial expansion of a SH64PCREL16 relaxation is just a "PT label,
+   TRd" as is the current insn, so no extra length.  Note that the "reach"
+   is calculated from the address *after* that insn, but the offset in the
+   insn is calculated from the beginning of the insn.  We also need to
+   take into account the implicit 1 coded as the "A" in PTA when counting
+   forward.  If PTB reaches an odd address, we trap that as an error
+   elsewhere, so we don't have to have different relaxation entries.  We
+   don't add a one to the negative range, since PTB would then have the
+   farthest backward-reaching value skipped, not generated at relaxation.  */
+#define SH64PCREL16_F (32767 * 4 - 4 + 1)
+#define SH64PCREL16_M (-32768 * 4 - 4)
+#define SH64PCREL16_LENGTH 0
+
+/* The next step is to change that PT insn into
+     MOVI ((label - datalabel Ln) >> 16) & 65535, R25
+     SHORI (label - datalabel Ln) & 65535, R25
+    Ln:
+     PTREL R25,TRd
+   which means two extra insns, 8 extra bytes.  This is the limit for the
+   32-bit ABI.
+
+   The expressions look a bit bad since we have to adjust this to avoid overflow on a
+   32-bit host.  */
+#define SH64PCREL32_F ((((long) 1 << 30) - 1) * 2 + 1 - 4)
+#define SH64PCREL32_LENGTH (2 * 4)
+
+/* Similarly, we just change the MOVI and add a SHORI for the 48-bit
+   expansion.  */
+#if BFD_HOST_64BIT_LONG
+/* The "reach" type is long, so we can only do this for a 64-bit-long
+   host.  */
+#define SH64PCREL32_M (((long) -1 << 30) * 2 - 4)
+#define SH64PCREL48_F ((((long) 1 << 47) - 1) - 4)
+#define SH64PCREL48_M (((long) -1 << 47) - 4)
+#define SH64PCREL48_LENGTH (3 * 4)
+#else
+/* If the host does not have 64-bit longs, just make this state identical
+   in reach to the 32-bit state.  Note that we have a slightly incorrect
+   reach, but the correct one above will overflow a 32-bit number.  */
+#define SH64PCREL32_M (((long) -1 << 30) * 2)
+#define SH64PCREL48_F SH64PCREL32_F
+#define SH64PCREL48_M SH64PCREL32_M
+#define SH64PCREL48_LENGTH (3 * 4)
+#endif /* BFD_HOST_64BIT_LONG */
+
+/* And similarly for the 64-bit expansion; a MOVI + SHORI + SHORI + SHORI
+   + PTREL sequence.  */
+#define SH64PCREL64_LENGTH (4 * 4)
+
+/* For MOVI, we make the MOVI + SHORI... expansion you can see in the
+   SH64PCREL expansions.  The PCREL one is similar, but the other has no
+   pc-relative reach; it must be fully expanded in
+   shmedia_md_estimate_size_before_relax.  */
+#define MOVI_16_LENGTH 0
+#define MOVI_16_F (32767 - 4)
+#define MOVI_16_M (-32768 - 4)
+#define MOVI_32_LENGTH 4
+#define MOVI_32_F ((((long) 1 << 30) - 1) * 2 + 1 - 4)
+#define MOVI_48_LENGTH 8
+
+#if BFD_HOST_64BIT_LONG
+/* The "reach" type is long, so we can only do this for a 64-bit-long
+   host.  */
+#define MOVI_32_M (((long) -1 << 30) * 2 - 4)
+#define MOVI_48_F ((((long) 1 << 47) - 1) - 4)
+#define MOVI_48_M (((long) -1 << 47) - 4)
+#else
+/* If the host does not have 64-bit longs, just make this state identical
+   in reach to the 32-bit state.  Note that we have a slightly incorrect
+   reach, but the correct one above will overflow a 32-bit number.  */
+#define MOVI_32_M (((long) -1 << 30) * 2)
+#define MOVI_48_F MOVI_32_F
+#define MOVI_48_M MOVI_32_M
+#endif /* BFD_HOST_64BIT_LONG */
+
+#define MOVI_64_LENGTH 12
+#endif /* HAVE_SH64 */
+
 #define EMPTY { 0, 0, 0, 0 }
 
 const relax_typeS md_relax_table[C (END, 0)] = {
@@ -256,6 +391,118 @@ const relax_typeS md_relax_table[C (END,
   { 0, 0, UNCOND32_LENGTH, 0, },
   EMPTY, EMPTY, EMPTY,
   EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+#ifdef HAVE_SH64
+  /* C (SH64PCREL16_32, SH64PCREL16) */
+  EMPTY,
+  { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16_32, SH64PCREL32) },
+  /* C (SH64PCREL16_32, SH64PCREL32) */
+  { 0, 0, SH64PCREL32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  /* C (SH64PCREL16_32, SH64PCRELPLT) */
+  { 0, 0, SH64PCREL32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (SH64PCREL16_64, SH64PCREL16) */
+  EMPTY,
+  { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16_64, SH64PCREL32) },
+  /* C (SH64PCREL16_64, SH64PCREL32) */
+  { SH64PCREL32_F, SH64PCREL32_M, SH64PCREL32_LENGTH, C (SH64PCREL16_64, SH64PCREL48) },
+  /* C (SH64PCREL16_64, SH64PCREL48) */
+  { SH64PCREL48_F, SH64PCREL48_M, SH64PCREL48_LENGTH, C (SH64PCREL16_64, SH64PCREL64) },
+  /* C (SH64PCREL16_64, SH64PCREL64) */
+  { 0, 0, SH64PCREL64_LENGTH, 0 },
+  /* C (SH64PCREL16_64, SH64PCRELPLT) */
+  { 0, 0, SH64PCREL64_LENGTH, 0 },
+  EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (SH64PCREL16PT_32, SH64PCREL16) */
+  EMPTY,
+  { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16PT_32, SH64PCREL32) },
+  /* C (SH64PCREL16PT_32, SH64PCREL32) */
+  { 0, 0, SH64PCREL32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  /* C (SH64PCREL16PT_32, SH64PCRELPLT) */
+  { 0, 0, SH64PCREL32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (SH64PCREL16PT_64, SH64PCREL16) */
+  EMPTY,
+  { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16PT_64, SH64PCREL32) },
+  /* C (SH64PCREL16PT_64, SH64PCREL32) */
+  { SH64PCREL32_F,
+    SH64PCREL32_M, 
+    SH64PCREL32_LENGTH,
+    C (SH64PCREL16PT_64, SH64PCREL48) },
+  /* C (SH64PCREL16PT_64, SH64PCREL48) */
+  { SH64PCREL48_F, SH64PCREL48_M, SH64PCREL48_LENGTH, C (SH64PCREL16PT_64, SH64PCREL64) },
+  /* C (SH64PCREL16PT_64, SH64PCREL64) */
+  { 0, 0, SH64PCREL64_LENGTH, 0 },
+  /* C (SH64PCREL16PT_64, SH64PCRELPLT) */
+  { 0, 0, SH64PCREL64_LENGTH, 0},
+  EMPTY, EMPTY,
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (MOVI_IMM_32, UNDEF_MOVI) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  /* C (MOVI_IMM_32, MOVI_16) */
+  { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_32, MOVI_32) },
+  /* C (MOVI_IMM_32, MOVI_32) */
+  { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY,
+  /* C (MOVI_IMM_32, MOVI_GOTOFF) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (MOVI_IMM_32_PCREL, MOVI_16) */
+  EMPTY,
+  { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_32_PCREL, MOVI_32) },
+  /* C (MOVI_IMM_32_PCREL, MOVI_32) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  EMPTY, EMPTY,
+  /* C (MOVI_IMM_32_PCREL, MOVI_PLT) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  EMPTY,
+  /* C (MOVI_IMM_32_PCREL, MOVI_GOTPC) */
+  { 0, 0, MOVI_32_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (MOVI_IMM_64, UNDEF_MOVI) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  /* C (MOVI_IMM_64, MOVI_16) */
+  { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_64, MOVI_32) },
+  /* C (MOVI_IMM_64, MOVI_32) */
+  { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, C (MOVI_IMM_64, MOVI_48) },
+  /* C (MOVI_IMM_64, MOVI_48) */
+  { MOVI_48_F, MOVI_48_M, MOVI_48_LENGTH, C (MOVI_IMM_64, MOVI_64) },
+  /* C (MOVI_IMM_64, MOVI_64) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  EMPTY,
+  /* C (MOVI_IMM_64, MOVI_GOTOFF) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+  /* C (MOVI_IMM_64_PCREL, MOVI_16) */
+  EMPTY,
+  { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_32) },
+  /* C (MOVI_IMM_64_PCREL, MOVI_32) */
+  { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_48) },
+  /* C (MOVI_IMM_64_PCREL, MOVI_48) */
+  { MOVI_48_F, MOVI_48_M, MOVI_48_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_64) },
+  /* C (MOVI_IMM_64_PCREL, MOVI_64) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  /* C (MOVI_IMM_64_PCREL, MOVI_PLT) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  EMPTY,
+  /* C (MOVI_IMM_64_PCREL, MOVI_GOTPC) */
+  { 0, 0, MOVI_64_LENGTH, 0 },
+  EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+#endif /* HAVE_SH64 */
+
 };
 
 #undef EMPTY
@@ -278,6 +525,11 @@ sh_PIC_related_p (sym)
   if (sym == GOT_symbol)
     return 1;
 
+#ifdef HAVE_SH64
+  if (sh_PIC_related_p (*symbol_get_tc (sym)))
+    return 1;
+#endif
+
   exp = symbol_get_value_expression (sym);
 
   return (exp->X_op == O_PIC_reloc
@@ -341,11 +593,47 @@ sh_check_fixup (main_exp, r_type_p)
 
   if (exp->X_op == O_symbol || exp->X_op == O_add || exp->X_op == O_subtract)
     {
+#ifdef HAVE_SH64
+      if (exp->X_add_symbol
+	  && (exp->X_add_symbol == GOT_symbol
+	      || (GOT_symbol
+		  && *symbol_get_tc (exp->X_add_symbol) == GOT_symbol)))
+	{
+	  switch (*r_type_p)
+	    {
+	    case BFD_RELOC_SH_IMM_LOW16:
+	      *r_type_p = BFD_RELOC_SH_GOTPC_LOW16;
+	      break;
+
+	    case BFD_RELOC_SH_IMM_MEDLOW16:
+	      *r_type_p = BFD_RELOC_SH_GOTPC_MEDLOW16;
+	      break;
+
+	    case BFD_RELOC_SH_IMM_MEDHI16:
+	      *r_type_p = BFD_RELOC_SH_GOTPC_MEDHI16;
+	      break;
+
+	    case BFD_RELOC_SH_IMM_HI16:
+	      *r_type_p = BFD_RELOC_SH_GOTPC_HI16;
+	      break;
+
+	    case BFD_RELOC_NONE:
+	    case BFD_RELOC_UNUSED:
+	      *r_type_p = BFD_RELOC_SH_GOTPC;
+	      break;
+	      
+	    default:
+	      abort ();
+	    }
+	  return 0;
+	}
+#else
       if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
 	{
 	  *r_type_p = BFD_RELOC_SH_GOTPC;
 	  return 0;
 	}
+#endif
       exp = symbol_get_value_expression (exp->X_add_symbol);
       if (! exp)
 	return 0;
@@ -353,7 +641,116 @@ sh_check_fixup (main_exp, r_type_p)
 
   if (exp->X_op == O_PIC_reloc)
     {
+#ifdef HAVE_SH64
+      switch (*r_type_p)
+	{
+	case BFD_RELOC_NONE:
+	case BFD_RELOC_UNUSED:
+	  *r_type_p = exp->X_md;
+	  break;
+
+	case BFD_RELOC_SH_IMM_LOW16:
+	  switch (exp->X_md)
+	    {
+	    case BFD_RELOC_32_GOTOFF:
+	      *r_type_p = BFD_RELOC_SH_GOTOFF_LOW16;
+	      break;
+	      
+	    case BFD_RELOC_SH_GOTPLT32:
+	      *r_type_p = BFD_RELOC_SH_GOTPLT_LOW16;
+	      break;
+	      
+	    case BFD_RELOC_32_GOT_PCREL:
+	      *r_type_p = BFD_RELOC_SH_GOT_LOW16;
+	      break;
+	      
+	    case BFD_RELOC_32_PLT_PCREL:
+	      *r_type_p = BFD_RELOC_SH_PLT_LOW16;
+	      break;
+
+	    default:
+	      abort ();
+	    }
+	  break;
+
+	case BFD_RELOC_SH_IMM_MEDLOW16:
+	  switch (exp->X_md)
+	    {
+	    case BFD_RELOC_32_GOTOFF:
+	      *r_type_p = BFD_RELOC_SH_GOTOFF_MEDLOW16;
+	      break;
+	      
+	    case BFD_RELOC_SH_GOTPLT32:
+	      *r_type_p = BFD_RELOC_SH_GOTPLT_MEDLOW16;
+	      break;
+	      
+	    case BFD_RELOC_32_GOT_PCREL:
+	      *r_type_p = BFD_RELOC_SH_GOT_MEDLOW16;
+	      break;
+	      
+	    case BFD_RELOC_32_PLT_PCREL:
+	      *r_type_p = BFD_RELOC_SH_PLT_MEDLOW16;
+	      break;
+
+	    default:
+	      abort ();
+	    }
+	  break;
+
+	case BFD_RELOC_SH_IMM_MEDHI16:
+	  switch (exp->X_md)
+	    {
+	    case BFD_RELOC_32_GOTOFF:
+	      *r_type_p = BFD_RELOC_SH_GOTOFF_MEDHI16;
+	      break;
+	      
+	    case BFD_RELOC_SH_GOTPLT32:
+	      *r_type_p = BFD_RELOC_SH_GOTPLT_MEDHI16;
+	      break;
+	      
+	    case BFD_RELOC_32_GOT_PCREL:
+	      *r_type_p = BFD_RELOC_SH_GOT_MEDHI16;
+	      break;
+	      
+	    case BFD_RELOC_32_PLT_PCREL:
+	      *r_type_p = BFD_RELOC_SH_PLT_MEDHI16;
+	      break;
+
+	    default:
+	      abort ();
+	    }
+	  break;
+
+	case BFD_RELOC_SH_IMM_HI16:
+	  switch (exp->X_md)
+	    {
+	    case BFD_RELOC_32_GOTOFF:
+	      *r_type_p = BFD_RELOC_SH_GOTOFF_HI16;
+	      break;
+	      
+	    case BFD_RELOC_SH_GOTPLT32:
+	      *r_type_p = BFD_RELOC_SH_GOTPLT_HI16;
+	      break;
+	      
+	    case BFD_RELOC_32_GOT_PCREL:
+	      *r_type_p = BFD_RELOC_SH_GOT_HI16;
+	      break;
+	      
+	    case BFD_RELOC_32_PLT_PCREL:
+	      *r_type_p = BFD_RELOC_SH_PLT_HI16;
+	      break;
+
+	    default:
+	      abort ();
+	    }
+	  break;
+
+	default:
+	  abort ();
+	}
+#else
       *r_type_p = exp->X_md;
+#endif
       if (exp == main_exp)
 	exp->X_op = O_symbol;
       else
@@ -397,6 +794,12 @@ sh_cons_fix_new (frag, off, size, exp)
 	r_type = BFD_RELOC_32;
 	break;
 
+#ifdef HAVE_SH64
+      case 8:
+	r_type = BFD_RELOC_64;
+	break;
+#endif
+
       default:
 	goto error;
       }
@@ -420,6 +823,16 @@ sh_elf_cons (nbytes)
 {
   expressionS exp;
 
+#ifdef HAVE_SH64
+
+  /* Update existing range to include a previous insn, if there was one.  */
+  sh64_update_contents_mark (true);
+
+  /* We need to make sure the contents type is set to data.  */
+  sh64_flag_output ();
+
+#endif /* HAVE_SH64 */
+
   if (is_it_end_of_statement ())
     {
       demand_empty_rest_of_line ();
@@ -457,6 +870,10 @@ md_begin ()
   target_arch = arch_sh1_up & ~(sh_dsp ? arch_sh3e_up : arch_sh_dsp_up);
   valid_arch = target_arch;
 
+#ifdef HAVE_SH64
+  shmedia_md_begin ();
+#endif
+
   opcode_hash_control = hash_new ();
 
   /* Insert unique names into hash table.  */
@@ -1906,6 +2323,26 @@ md_assemble (str)
   sh_opcode_info *opcode;
   unsigned int size = 0;
 
+#ifdef HAVE_SH64
+  if (sh64_isa_mode == sh64_isa_shmedia)
+    {
+      shmedia_md_assemble (str);
+      return;
+    }
+  else
+    {
+      /* If we've seen pseudo-directives, make sure any emitted data or
+	 frags are marked as data.  */
+      if (seen_insn == false)
+	{
+	  sh64_update_contents_mark (true);
+	  sh64_set_contents_type (CRT_SH5_ISA16);
+	}
+
+      seen_insn = true;
+    }
+#endif /* HAVE_SH64 */
+
   opcode = find_cooked_opcode (&str);
   op_end = str;
 
@@ -2147,6 +2584,21 @@ struct option md_longopts[] =
   {"little", no_argument, NULL, OPTION_LITTLE},
   {"small", no_argument, NULL, OPTION_SMALL},
   {"dsp", no_argument, NULL, OPTION_DSP},
+#ifdef HAVE_SH64
+#define OPTION_ISA                    (OPTION_DSP + 1)
+#define OPTION_ABI                    (OPTION_ISA + 1)
+#define OPTION_NO_MIX                 (OPTION_ABI + 1)
+#define OPTION_SHCOMPACT_CONST_CRANGE (OPTION_NO_MIX + 1)
+#define OPTION_NO_EXPAND              (OPTION_SHCOMPACT_CONST_CRANGE + 1)
+#define OPTION_PT32                   (OPTION_NO_EXPAND + 1)
+  {"isa",                    required_argument, NULL, OPTION_ISA},
+  {"abi",                    required_argument, NULL, OPTION_ABI},
+  {"no-mix",                 no_argument, NULL, OPTION_NO_MIX},
+  {"shcompact-const-crange", no_argument, NULL, OPTION_SHCOMPACT_CONST_CRANGE},
+  {"no-expand",              no_argument, NULL, OPTION_NO_EXPAND},
+  {"expand-pt32",            no_argument, NULL, OPTION_PT32},
+#endif /* HAVE_SH64 */
+
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
@@ -2178,6 +2630,62 @@ md_parse_option (c, arg)
       sh_dsp = 1;
       break;
 
+#ifdef HAVE_SH64
+    case OPTION_ISA:
+      if (strcasecmp (arg, "shmedia") == 0)
+	{
+	  if (sh64_isa_mode == sh64_isa_shcompact)
+	    as_bad (_("Invalid combination: --isa=SHcompact with --isa=SHmedia"));
+	  sh64_isa_mode = sh64_isa_shmedia;
+	}
+      else if (strcasecmp (arg, "shcompact") == 0)
+	{
+	  if (sh64_isa_mode == sh64_isa_shmedia)
+	    as_bad (_("Invalid combination: --isa=SHmedia with --isa=SHcompact"));
+	  if (sh64_abi == sh64_abi_64)
+	    as_bad (_("Invalid combination: --abi=64 with --isa=SHcompact"));
+	  sh64_isa_mode = sh64_isa_shcompact;
+	}
+      else
+	as_bad ("Invalid argument to --isa option: %s", arg);
+      break;
+
+    case OPTION_ABI:
+      if (strcmp (arg, "32") == 0)
+	{
+	  if (sh64_abi == sh64_abi_64)
+	    as_bad (_("Invalid combination: --abi=32 with --abi=64"));
+	  sh64_abi = sh64_abi_32;
+	}
+      else if (strcmp (arg, "64") == 0)
+	{
+	  if (sh64_abi == sh64_abi_32)
+	    as_bad (_("Invalid combination: --abi=64 with --abi=32"));
+	  if (sh64_isa_mode == sh64_isa_shcompact)
+	    as_bad (_("Invalid combination: --isa=SHcompact with --abi=64"));
+	  sh64_abi = sh64_abi_64;
+	}
+      else
+	as_bad ("Invalid argument to --abi option: %s", arg);
+      break;
+
+    case OPTION_NO_MIX:
+      sh64_mix = false;
+      break;
+
+    case OPTION_SHCOMPACT_CONST_CRANGE:
+      sh64_shcompact_const_crange = true;
+      break;
+
+    case OPTION_NO_EXPAND:
+      sh64_expand = false;
+      break;
+
+    case OPTION_PT32:
+      sh64_pt32 = true;
+      break;
+#endif /* HAVE_SH64 */
+
     default:
       return 0;
     }
@@ -2196,6 +2704,22 @@ SH options:\n\
 -relax			alter jump instructions for long displacements\n\
 -small			align sections to 4 byte boundaries, not 16\n\
 -dsp			enable sh-dsp insns, and disable sh3e / sh4 insns.\n"));
+#ifdef HAVE_SH64
+  fprintf (stream, _("\
+-isa=[shmedia		set default instruction set for SH64\n\
+      | SHmedia\n\
+      | shcompact\n\
+      | SHcompact]\n\
+-abi=[32|64]		set size of expanded SHmedia operands and object\n\
+			file type\n\
+-shcompact-const-crange	emit code-range descriptors for constants in\n\
+			SHcompact code sections\n\
+-no-mix			disallow SHmedia code in the same section as\n\
+			constants and SHcompact code\n\
+-no-expand		do not expand MOVI, PT, PTA or PTB instructions\n\
+-expand-pt32		with -abi=64, expand PT, PTA and PTB instructions\n\
+			to 32 bits only"));
+#endif /* HAVE_SH64 */
 }
 
 /* This struct is used to pass arguments to sh_count_relocs through
@@ -2365,6 +2889,10 @@ sh_frob_section (abfd, sec, ignore)
 void
 sh_frob_file ()
 {
+#ifdef HAVE_SH64
+  shmedia_frob_file_before_adjust ();
+#endif
+
   if (! sh_relax)
     return;
 
@@ -2518,7 +3046,11 @@ md_convert_frag (headers, seg, fragP)
       break;
 
     default:
+#ifdef HAVE_SH64
+      shmedia_md_convert_frag (headers, seg, fragP, true);
+#else
       abort ();
+#endif
     }
 
   if (donerelax && !sh_relax)
@@ -2708,6 +3240,9 @@ sh_force_relocation (fix)
 	  || fix->fx_r_type == BFD_RELOC_SH_ALIGN
 	  || fix->fx_r_type == BFD_RELOC_SH_CODE
 	  || fix->fx_r_type == BFD_RELOC_SH_DATA
+#ifdef HAVE_SH64
+	  || fix->fx_r_type == BFD_RELOC_SH_SHMEDIA_CODE
+#endif
 	  || fix->fx_r_type == BFD_RELOC_SH_LABEL);
 }
 
@@ -2748,6 +3283,13 @@ sh_elf_final_processing ()
 
   /* Set file-specific flags to indicate if this code needs
      a processor with the sh-dsp / sh3e ISA to execute.  */
+#ifdef HAVE_SH64
+  /* SH5 and above don't know about the valid_arch arch_sh* bits defined
+     in sh-opc.h, so check SH64 mode before checking valid_arch.  */
+  if (sh64_isa_mode != sh64_isa_unspecified)
+    val = EF_SH5;
+  else
+#endif /* HAVE_SH64 */
   if (valid_arch & arch_sh1)
     val = EF_SH1;
   else if (valid_arch & arch_sh2)
@@ -2994,6 +3536,7 @@ md_apply_fix3 (fixP, valP, seg)
       break;
 
     case BFD_RELOC_32_GOT_PCREL:
+    case BFD_RELOC_SH_GOTPLT32:
       * valP = 0; /* Fully resolved at runtime.  No addend.  */
       md_number_to_chars (buf, 0, 4);
       break;
@@ -3004,7 +3547,12 @@ md_apply_fix3 (fixP, valP, seg)
 #endif
 
     default:
+#ifdef HAVE_SH64
+      shmedia_md_apply_fix3 (fixP, valP);
+      return;
+#else
       abort ();
+#endif
     }
 
   if (shift != 0)
@@ -3037,7 +3585,12 @@ md_estimate_size_before_relax (fragP, se
   switch (fragP->fr_subtype)
     {
     default:
+#ifdef HAVE_SH64
+      return shmedia_md_estimate_size_before_relax (fragP, segment_type);
+#else
       abort ();
+#endif
+
 
     case C (UNCOND_JUMP, UNDEF_DISP):
       /* Used to be a branch to somewhere which was unknown.  */
@@ -3106,6 +3659,11 @@ md_number_to_chars (ptr, use, nbytes)
      valueT use;
      int nbytes;
 {
+#ifdef HAVE_SH64
+  /* We might need to set the contents type to data.  */
+  sh64_flag_output ();
+#endif
+
   if (! target_big_endian)
     number_to_chars_littleendian (ptr, use, nbytes);
   else
@@ -3351,6 +3909,10 @@ tc_gen_reloc (section, fixp)
       rel->addend = 0;
       rel->address = rel->addend = fixp->fx_offset;
     }
+#ifdef HAVE_SH64
+  else if (shmedia_init_reloc (rel, fixp))
+    ;
+#endif
   else if (fixp->fx_pcrel)
     rel->addend = fixp->fx_addnumber;
   else if (r_type == BFD_RELOC_32 || r_type == BFD_RELOC_32_GOTOFF)
@@ -3436,6 +3998,8 @@ sh_parse_name (name, exprP, nextcharP)
     goto no_suffix;
   else if ((next_end = sh_end_of_match (next + 1, "GOTOFF")))
     reloc_type = BFD_RELOC_32_GOTOFF;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTPLT")))
+    reloc_type = BFD_RELOC_SH_GOTPLT32;
   else if ((next_end = sh_end_of_match (next + 1, "GOT")))
     reloc_type = BFD_RELOC_32_GOT_PCREL;
   else if ((next_end = sh_end_of_match (next + 1, "PLT")))
Index: gas/config/tc-sh64.c
===================================================================
RCS file: gas/config/tc-sh64.c
diff -N gas/config/tc-sh64.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gas/config/tc-sh64.c 2 Feb 2002 04:00:30 -0000
@@ -0,0 +1,3513 @@
+/* tc-sh64.c -- Assemble code for the Hitachi Super-H SHcompact and SHmedia.
+   Copyright (C) 2000, 2001, 2002 Free Software Foundation.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to
+   the Free Software Foundation, 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* This file defines SHmedia ISA-specific functions and includes tc-sh.c.
+   The SHcompact ISA is in all useful aspects the "old" sh4 as implemented
+   in tc-sh.c.  Not making this file part of tc-sh.c makes it easier to
+   keep a leaner sh[1-4]-only implementation.  */
+
+#define HAVE_SH64
+
+#include <stdio.h>
+#include "as.h"
+#include "safe-ctype.h"
+#include "opcodes/sh64-opc.h"
+
+#ifndef OBJ_ELF
+#error This file assumes object output is in the ELF format
+#endif
+
+/* Suffix used when we make "datalabel" symbol copies.  It must not
+   collide with anything that can normally appear in a symbol, "faked
+   symbol" or local symbol.  */
+#define DATALABEL_SUFFIX " DL"
+
+/* See shmedia_md_apply_fix3 and shmedia_md_pcrel_from_section for usage.  */
+#define SHMEDIA_MD_PCREL_FROM_FIX(FIXP) \
+ ((FIXP)->fx_size + (FIXP)->fx_where + (FIXP)->fx_frag->fr_address - 4)
+
+/* We use this internally to see which one is PT and which is a PTA/PTB
+   that should be error-checked.  We give it a better name here (but not
+   one that looks official).  Adding it to reloc.c would make it look too
+   much of a real reloc; it is just used temporarily as a fixup-type.  */
+#define SHMEDIA_BFD_RELOC_PT BFD_RELOC_12_PCREL
+
+typedef struct
+ {
+   shmedia_arg_type type;
+
+   /* These could go into a union, but that would uglify the code.  */
+   int reg;
+   expressionS immediate;
+
+   /* If IMMEDIATE was a shift-expression, like "(S >> N) & 65535", where
+      N = 0, 16, 32, 48, used to extract a certain 16-bit-field to make up
+      a MOVI or SHORI relocation for a symbol, then we put the
+      corresponding reloc-type here and modify the "immediate" expression
+      to S.  Otherwise, this is just BFD_RELOC_NONE.  */
+   bfd_reloc_code_real_type reloctype;
+ } shmedia_operand_info;
+
+/* Frag containing last base instruction.  This is put in the TC field in
+   a frag, so we can emit fixups for fr_opcode without needing to make
+   sure that the opcode is in the same frag as any variant operand.  */
+fragS *sh64_last_insn_frag = NULL;
+
+typedef struct
+ {
+   shmedia_operand_info operands[3];
+   unsigned long ops_val;
+ } shmedia_operands_info;
+
+enum sh64_abi_values
+ { sh64_abi_unspecified, sh64_abi_32, sh64_abi_64 };
+
+/* What ISA are we assembling code for?  */
+enum sh64_isa_values sh64_isa_mode = sh64_isa_unspecified;
+
+/* What ABI was specified, if any (implicitly or explicitly)?  */
+static enum sh64_abi_values sh64_abi = sh64_abi_unspecified;
+
+/* A note that says if we're in a sequence of insns without label
+   settings, segment or ISA mode changes or emitted data.  */
+static boolean seen_insn = false;
+
+/* This is set to true in shmedia_md_end, so that we don't emit any
+   .cranges entries when the assembler calls output functions while
+   grinding along after all input is seen.  */
+static boolean sh64_end_of_assembly = false;
+
+/* Controlled by the option -no-mix, this invalidates mixing SHcompact and
+   SHmedia code in the same section, and also invalidates mixing data and
+   SHmedia code in the same section.  No .cranges will therefore be
+   emitted, unless -shcompact-const-crange is specified and there is a
+   constant pool in SHcompact code.  */
+static boolean sh64_mix = true;
+
+static boolean sh64_shcompact_const_crange = false;
+
+/* Controlled by the option -no-expand, this says whether or not we expand
+   MOVI and PT/PTA/PTB.  When we do not expand these insns to fit an
+   operand, we will emit errors for operands out of range and generate the
+   basic instruction and reloc for an external symbol.  */
+static boolean sh64_expand = true;
+
+/* Controlled by the option -expand-pt32, this says whether we expand
+   PT/PTA/PTB of an external symbol to (only) 32 or (the full) 64 bits
+   when -abi=64 is in effect.  */
+static boolean sh64_pt32 = false;
+
+/* When emitting a .cranges descriptor, we want to avoid getting recursive
+   calls through emit_expr.  */
+static boolean emitting_crange = false;
+
+/* SHmedia mnemonics.  */
+static struct hash_control *shmedia_opcode_hash_control = NULL;
+
+static const unsigned char shmedia_big_nop_pattern[4] =
+ {
+   (SHMEDIA_NOP_OPC >> 24) & 255, (SHMEDIA_NOP_OPC >> 16) & 255,
+   (SHMEDIA_NOP_OPC >> 8) & 255, SHMEDIA_NOP_OPC & 255
+ };
+
+static const unsigned char shmedia_little_nop_pattern[4] =
+ {
+   SHMEDIA_NOP_OPC & 255, (SHMEDIA_NOP_OPC >> 8) & 255,
+   (SHMEDIA_NOP_OPC >> 16) & 255, (SHMEDIA_NOP_OPC >> 24) & 255
+ };
+
+static void shmedia_md_begin PARAMS ((void));
+static int shmedia_parse_reg PARAMS ((char *, int *, int *, shmedia_arg_type));
+static void shmedia_md_assemble PARAMS ((char *));
+static void shmedia_md_apply_fix3 PARAMS ((fixS *, valueT *));
+static int shmedia_md_estimate_size_before_relax PARAMS ((fragS *, segT));
+static int shmedia_init_reloc PARAMS ((arelent *, fixS *));
+static char *shmedia_get_operands PARAMS ((shmedia_opcode_info *, char *, shmedia_operands_info *));
+static void s_sh64_mode PARAMS ((int));
+static void s_sh64_abi PARAMS ((int));
+static void shmedia_md_convert_frag PARAMS ((bfd *, segT, fragS *, boolean));
+static void shmedia_check_limits PARAMS ((offsetT *, bfd_reloc_code_real_type, fixS *));
+static void sh64_set_contents_type PARAMS ((enum sh64_elf_cr_type));
+static void shmedia_get_operand PARAMS ((char **, shmedia_operand_info *, shmedia_arg_type));
+static unsigned long shmedia_immediate_op PARAMS ((char *, shmedia_operand_info *, int, bfd_reloc_code_real_type));
+static char *shmedia_parse_exp PARAMS ((char *, shmedia_operand_info *));
+static void shmedia_frob_file_before_adjust PARAMS ((void));
+static void sh64_emit_crange PARAMS ((symbolS *, symbolS *, enum sh64_elf_cr_type));
+static void sh64_flush_last_crange PARAMS ((bfd *, asection *, PTR));
+static void sh64_flag_output PARAMS ((void));
+static void sh64_update_contents_mark PARAMS ((boolean));
+static void sh64_vtable_entry PARAMS ((int));
+static void sh64_vtable_inherit  PARAMS ((int));
+static char * strip_datalabels PARAMS ((void));
+static int shmedia_build_Mytes PARAMS ((shmedia_opcode_info *, shmedia_operands_info *));
+static shmedia_opcode_info * shmedia_find_cooked_opcode PARAMS ((char **));
+static unsigned long shmedia_mask_number PARAMS ((unsigned long, bfd_reloc_code_real_type));
+
+#include "tc-sh.c"
+
+void
+shmedia_md_end ()
+{
+  symbolS *symp;
+
+  /* First, update the last range to include whatever data was last
+     emitted.  */
+  sh64_update_contents_mark (true);
+
+  /* Make sure frags generated after this point are not marked with the
+     wrong ISA; make them easily spottable.  We still want to distinguish
+     it from sh64_isa_unspecified when we compile for SHcompact or
+     SHmedia.  */
+  if (sh64_isa_mode != sh64_isa_unspecified)
+    sh64_isa_mode = sh64_isa_sh5_guard;
+
+  sh64_end_of_assembly = true;
+
+  bfd_map_over_sections (stdoutput, sh64_flush_last_crange, NULL);
+
+  /* Iterate over segments and emit the last .cranges descriptor.  */
+  for (symp = symbol_rootP; symp != NULL; symp = symp->sy_next)
+    {
+      symbolS *mainsym = *symbol_get_tc (symp);
+
+      /* Is this a datalabel symbol; does it have a pointer to the main
+	 symbol?  */
+      if (mainsym != NULL)
+	{
+	  /* If the datalabel symbol is undefined, check if the main
+	     symbol has changed in that respect.  */
+	  if (S_GET_SEGMENT (symp) == undefined_section)
+	    {
+	      segT symseg;
+
+	      symseg = S_GET_SEGMENT (mainsym);
+
+	      /* If the symbol is now defined to something that is not
+		 global and without STO_SH5_ISA32, we just equate the
+		 datalabel symbol to the main symbol, and the lack of
+		 STO_SH5_ISA32 will handle the datalabelness.  */
+	      if (symseg != undefined_section)
+		{
+		  if (S_GET_OTHER (mainsym) != STO_SH5_ISA32)
+		    {
+		      symp->sy_value.X_op = O_symbol;
+		      symp->sy_value.X_add_symbol = mainsym;
+		      symp->sy_value.X_op_symbol = NULL;
+		      symp->sy_value.X_add_number = 0;
+		      S_SET_SEGMENT (symp, S_GET_SEGMENT (mainsym));
+		      symbol_set_frag (symp, &zero_address_frag);
+		      copy_symbol_attributes (symp, mainsym);
+		    }
+		  else
+		    {
+		      /* An undefined symbol has since we saw it at
+			 "datalabel", been defined to a BranchTarget
+			 symbol.  What we need to do here is very similar
+			 to when we find the "datalabel" for a defined
+			 symbol.  FIXME: Break out to common function.  */
+		      symbol_set_value_expression (symp,
+						   symbol_get_value_expression
+						   (mainsym));
+		      S_SET_SEGMENT (symp, symseg);
+		      symbol_set_frag (symp, symbol_get_frag (mainsym));
+		      copy_symbol_attributes (symp, mainsym);
+
+		      /* Unset the BranchTarget mark that can be set at
+			 attribute-copying. */
+		      S_SET_OTHER (symp,
+				   S_GET_OTHER (symp) & ~STO_SH5_ISA32); 
+
+		      /* The GLOBAL and WEAK attributes are not copied
+			 over by copy_symbol_attributes.  Do it here. */
+		      if (S_IS_WEAK (mainsym))
+			S_SET_WEAK (symp);
+		      else if (S_IS_EXTERNAL (mainsym))
+			S_SET_EXTERNAL (symp);
+		    }
+		}
+	      else
+		{
+		  /* A symbol that was defined at the time we saw
+		     "datalabel" can since have been attributed with being
+		     weak or global.  */
+		  if (S_IS_WEAK (mainsym))
+		    S_SET_WEAK (symp);
+		  else if (S_IS_EXTERNAL (mainsym))
+		    S_SET_EXTERNAL (symp);
+		}
+	    }
+	}
+    }
+
+  for (symp = symbol_rootP; symp != NULL; symp = symp->sy_next)
+    if (S_GET_OTHER (symp) & STO_SH5_ISA32)
+      symp->sy_value.X_add_number++;
+}
+
+/* When resolving symbols, the main assembler has done us a misfavour.  It
+   has removed the equation to the main symbol for a datalabel reference
+   that should be equal to the main symbol, e.g. when it's a global or
+   weak symbol and is a non-BranchTarget symbol anyway.  We change that
+   back, so that relocs are against the main symbol, not the local "section
+   + offset" value.  */
+
+static void
+shmedia_frob_file_before_adjust ()
+{
+  symbolS *symp;
+  for (symp = symbol_rootP; symp != NULL; symp = symp->sy_next)
+    {
+      symbolS *mainsym = *symbol_get_tc (symp);
+
+      if (mainsym != NULL
+	  && S_GET_OTHER (mainsym) != STO_SH5_ISA32
+	  && (S_IS_EXTERN (mainsym) || S_IS_WEAK (mainsym)))
+	{
+	  symp->sy_value.X_op = O_symbol;
+	  symp->sy_value.X_add_symbol = mainsym;
+	  symp->sy_value.X_op_symbol = NULL;
+	  symp->sy_value.X_add_number = 0;
+
+	  /* For the "equation trick" to work, we have to set the section
+	     to undefined.  */
+	  S_SET_SEGMENT (symp, undefined_section);
+	  symbol_set_frag (symp, &zero_address_frag);
+	  copy_symbol_attributes (symp, mainsym);
+
+	  /* Don't forget to remove the STO_SH5_ISA32 attribute after
+	     copying the other attributes.  */
+	  S_SET_OTHER (symp, S_GET_OTHER (symp) & ~STO_SH5_ISA32);
+	}
+    }
+}
+
+/* We need to mark the current location after the alignment.  This is
+   copied code the caller, do_align.  We mark the frag location before and
+   after as we need and arrange to skip the same code in do_align.
+
+   An alternative to code duplication is to call the do_align recursively,
+   arranging to fall through into do_align if we're already here.  That
+   would require do_align as an incoming function parameter, since it's
+   static in read.c.  That solution was discarded a too kludgy.  */
+
+void
+sh64_do_align (n, fill, len, max)
+     int n;
+     const char *fill;
+     int len;
+     int max;
+{
+  /* Update region, or put a data region in front.  */
+  sh64_update_contents_mark (true);
+
+  /* Only make a frag if we HAVE to...  */
+  if (n != 0 && !need_pass_2)
+    {
+      if (fill == NULL)
+	{
+	  if (subseg_text_p (now_seg))
+	    frag_align_code (n, max);
+	  else
+	    frag_align (n, 0, max);
+	}
+      else if (len <= 1)
+	frag_align (n, *fill, max);
+      else
+	frag_align_pattern (n, fill, len, max);
+    }
+
+  /* Update mark for current region with current type.  */
+  sh64_update_contents_mark (false);
+}
+
+/* The MAX_MEM_FOR_RS_ALIGN_CODE worker.  We have to find out the ISA of
+   the current segment at this position.  We can't look just at
+   sh64_isa_shmedia, and we can't look at frag_now.  This is brittle:
+   callers are currently frag_align_code from subsegs_finish in write.c
+   (end of assembly) and frag_align_code from do_align in read.c (during
+   assembly).  */
+
+int
+sh64_max_mem_for_rs_align_code ()
+{
+  segment_info_type *seginfo;
+  fragS *mode_start_frag;
+  seginfo = seg_info (now_seg);
+
+  /* We don't use the contents type we find at the tc_segment_info_data,
+     since that does not give us absolute information about the ISA; the
+     contents type can presumably be CRT_DATA and we'd be none the wiser.
+     Instead we use the information stored at the frag of the symbol at
+     the start of this range.  If any information is missing or NULL,
+     assume SHcompact.  */
+  return
+    /* If the current ISA mode is SHmedia, that's the mode that we're
+       going to assign to the new frag, so request enough memory for
+       it, even if we switch modes afterwards, otherwise we may
+       allocate too little memory and end up overflowing our buffer.  */
+    (sh64_isa_mode == sh64_isa_shmedia
+     || (sh64_isa_mode != sh64_isa_unspecified
+	 && seginfo != NULL
+	 && seginfo->tc_segment_info_data.mode_start_symbol != NULL
+	 && ((mode_start_frag
+	      = (symbol_get_frag
+		 (seginfo->tc_segment_info_data.mode_start_symbol)))
+	     != NULL)
+	 && mode_start_frag->tc_frag_data.isa == sh64_isa_shmedia))
+    ? (3 + 4) : (2 + 1);
+}
+
+/* Put in SHmedia NOP:s if the alignment was created when in SHmedia mode.  */
+
+void
+sh64_handle_align (frag)
+     fragS * frag;
+{
+  int bytes = frag->fr_next->fr_address - frag->fr_address - frag->fr_fix;
+  char * p  = frag->fr_literal + frag->fr_fix;
+
+  if (frag->tc_frag_data.isa == sh64_isa_shmedia
+      && frag->fr_type == rs_align_code)
+    {
+      while (bytes & 3)
+	{
+	  *p++ = 0;
+	  bytes--;
+	  frag->fr_fix += 1;
+	}
+
+      if (target_big_endian)
+	{
+	  memcpy (p, shmedia_big_nop_pattern,
+		  sizeof shmedia_big_nop_pattern);
+	  frag->fr_var = sizeof shmedia_big_nop_pattern;
+	}
+      else
+	{
+	  memcpy (p, shmedia_little_nop_pattern,
+		  sizeof shmedia_little_nop_pattern);
+	  frag->fr_var = sizeof shmedia_little_nop_pattern;
+	}
+    }
+  else
+    /* Punt to SHcompact function.  */
+    sh_handle_align (frag);
+}
+
+/* Set SEC_SH64_ISA32 for SHmedia sections.  */
+
+void
+shmedia_frob_section_type (sec)
+     asection *sec;
+{
+  segment_info_type *seginfo;
+  seginfo = seg_info (sec);
+
+  /* This and elf32-sh64.c:sh64_elf_fake_sections are the only places
+     where we use anything else than ELF header flags to communicate the
+     section as containing SHmedia or other contents.  BFD SEC_* section
+     flags are running out and should not be overloaded with
+     target-specific semantics.  This target is ELF only (semantics not
+     defined for other formats), so we use the target-specific pointer
+     field of the ELF section data.  */
+  if (seginfo)
+    {
+      struct sh64_section_data *sec_elf_data;
+      flagword sec_type = 0;
+
+      if (seginfo->tc_segment_info_data.emitted_ranges != 0)
+	sec_type = SHF_SH5_ISA32_MIXED;
+      else if (seginfo->tc_segment_info_data.contents_type == CRT_SH5_ISA32)
+	sec_type = SHF_SH5_ISA32;
+
+      sec_elf_data = sh64_elf_section_data (sec);
+      if (sec_elf_data == NULL)
+	{
+	  sec_elf_data = xcalloc (1, sizeof (*sec_elf_data));
+	  sh64_elf_section_data (sec) = sec_elf_data;
+	}
+
+      sec_elf_data->contents_flags = sec_type;
+    }
+}
+
+/* This function is called by write_object_file right before the symbol
+   table is written.  We subtract 1 from all symbols marked STO_SH5_ISA32,
+   as their values are temporarily incremented in shmedia_md_end, before
+   symbols values are used by relocs and fixups.
+
+   To increment all symbols and then decrement here is admittedly a
+   hackish solution.  The alternative is to add infrastructure and hooks
+   to symbol evaluation that evaluates symbols differently internally to
+   the value output into the object file, but at the moment that just
+   seems too much for little benefit.  */
+
+void
+sh64_adjust_symtab ()
+{
+  symbolS *symp;
+
+  for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+    {
+      symbolS *main_symbol = *symbol_get_tc (symp);
+
+      if (main_symbol)
+	{
+	  char *sym_name = (char *) S_GET_NAME (symp);
+	  
+	  /* All datalabels not used in relocs should be gone by now.
+
+	     We change those remaining to have the name of the main
+	     symbol, and we set the ELF type of the symbol of the reloc to
+	     STT_DATALABEL.  */
+	  sym_name[strlen (sym_name) - strlen (DATALABEL_SUFFIX)] = 0;
+	  elf_symbol (symbol_get_bfdsym (symp))->internal_elf_sym.st_info
+	    = STT_DATALABEL;
+
+	  /* Also set this symbol to "undefined", so we'll have only one
+	     definition.  */
+	  S_SET_SEGMENT (symp, undefined_section);
+	}
+      else if (S_GET_OTHER (symp) & STO_SH5_ISA32)
+	{
+	  /* It's important to change the BFD symbol value, since it is now
+	     set to the GAS symbolS value.  */
+	  symp->bsym->value--;
+
+	  /* Note that we do *not* adjust symp->sy_value.X_add_number.  If
+	     you do this, the test case in sh/sh64/immexpr2.s will fail.
+	     This is because *after* symbols have been output but before
+	     relocs are output, fixups are inspected one more time, and
+	     some leftover expressions are resolved.  To resolve to the
+	     same values, those expressions must have the same GAS symbol
+	     values before as after symbols have been output.  We could
+	     "symp->sy_value.X_add_number++" on the STO_SH5_ISA32 symbols
+	     through tc_frob_file after symbols have been output, but that
+	     would be too gross.  */
+	}
+    }
+}
+
+/* Fill-in an allocated arelent.  */
+
+static int
+shmedia_init_reloc (rel, fixP)
+     arelent *rel;
+     fixS *fixP;
+{
+  /* Adjust parts of *relp according to *fixp, and tell that it has been
+     done, so default initializations will not happen.   */
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_64:
+    case BFD_RELOC_64_PCREL:
+    case BFD_RELOC_SH_IMM_LOW16:
+    case BFD_RELOC_SH_IMM_MEDLOW16:
+    case BFD_RELOC_SH_IMM_MEDHI16:
+    case BFD_RELOC_SH_IMM_HI16:
+    case BFD_RELOC_SH_IMM_LOW16_PCREL:
+    case BFD_RELOC_SH_IMM_MEDLOW16_PCREL:
+    case BFD_RELOC_SH_IMM_MEDHI16_PCREL:
+    case BFD_RELOC_SH_IMM_HI16_PCREL:
+    case BFD_RELOC_SH_IMMU5:
+    case BFD_RELOC_SH_IMMU6:
+    case BFD_RELOC_SH_IMMS6:
+    case BFD_RELOC_SH_IMMS10:
+    case BFD_RELOC_SH_IMMS10BY2:
+    case BFD_RELOC_SH_IMMS10BY4:
+    case BFD_RELOC_SH_IMMS10BY8:
+    case BFD_RELOC_SH_IMMS16:
+    case BFD_RELOC_SH_IMMU16:
+    case BFD_RELOC_SH_PT_16:
+    case BFD_RELOC_SH_GOT_LOW16:
+    case BFD_RELOC_SH_GOT_MEDLOW16:
+    case BFD_RELOC_SH_GOT_MEDHI16:
+    case BFD_RELOC_SH_GOT_HI16:
+    case BFD_RELOC_SH_GOT10BY4:
+    case BFD_RELOC_SH_GOT10BY8:
+    case BFD_RELOC_SH_GOTPLT_LOW16:
+    case BFD_RELOC_SH_GOTPLT_MEDLOW16:
+    case BFD_RELOC_SH_GOTPLT_MEDHI16:
+    case BFD_RELOC_SH_GOTPLT_HI16:
+    case BFD_RELOC_SH_GOTPLT10BY4:
+    case BFD_RELOC_SH_GOTPLT10BY8:
+    case BFD_RELOC_SH_GOTOFF_LOW16:
+    case BFD_RELOC_SH_GOTOFF_MEDLOW16:
+    case BFD_RELOC_SH_GOTOFF_MEDHI16:
+    case BFD_RELOC_SH_GOTOFF_HI16:
+    case BFD_RELOC_SH_GOTPC_LOW16:
+    case BFD_RELOC_SH_GOTPC_MEDLOW16:
+    case BFD_RELOC_SH_GOTPC_MEDHI16:
+    case BFD_RELOC_SH_GOTPC_HI16:
+    case BFD_RELOC_SH_PLT_LOW16:
+    case BFD_RELOC_SH_PLT_MEDLOW16:
+    case BFD_RELOC_SH_PLT_MEDHI16:
+    case BFD_RELOC_SH_PLT_HI16:
+      rel->addend = fixP->fx_addnumber + fixP->fx_offset;
+      return 1;
+
+    case BFD_RELOC_SH_IMMS6BY32:
+      /* This must be resolved in assembly; we do not support it as a
+	 reloc in an object file.  */
+      as_bad_where (fixP->fx_file, fixP->fx_line,
+		    _("This operand must be constant at assembly time"));
+      break;
+
+      /* There are valid cases where we get here for other than SHmedia
+	 relocs, so don't make a BAD_CASE out of this.  */
+    default:
+      ;
+    }
+
+  return 0;
+}
+
+/* Hook called from md_apply_fix3 in tc-sh.c.  */
+
+static void
+shmedia_md_apply_fix3 (fixP, valp)
+     fixS *fixP;
+     valueT *valp;
+{
+  offsetT val = *valp;
+  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+  unsigned long insn
+    = target_big_endian ? bfd_getb32 (buf) : bfd_getl32 (buf);
+  bfd_reloc_code_real_type orig_fx_r_type = fixP->fx_r_type;
+
+  /* Change a 64-bit pc-relative reloc into the correct type, just like
+     tc-sh.c:md_apply_fix.  */
+  if (fixP->fx_pcrel)
+    {
+      switch (orig_fx_r_type)
+	{
+	case BFD_RELOC_64:
+	case BFD_RELOC_SH_IMM_LOW16:
+	case BFD_RELOC_SH_IMM_MEDLOW16:
+	case BFD_RELOC_SH_IMM_MEDHI16:
+	case BFD_RELOC_SH_IMM_HI16:
+	  /* Because write.c calls MD_PCREL_FROM_SECTION twice, we need to
+	     undo one of the adjustments, if the relocation is not
+	     actually for a symbol within the same segment (which we
+	     cannot check, because we're not called from md_apply_fix3, so
+	     we have to keep the reloc).  FIXME: This is a bug in
+	     write.c:fixup_segment affecting most targets that change
+	     ordinary relocs to pcrel relocs in md_apply_fix.  */
+	  fixP->fx_offset
+	    = *valp + SHMEDIA_MD_PCREL_FROM_FIX (fixP);
+	  break;
+
+	case BFD_RELOC_SH_PLT_LOW16:
+	case BFD_RELOC_SH_PLT_MEDLOW16:
+	case BFD_RELOC_SH_PLT_MEDHI16:
+	case BFD_RELOC_SH_PLT_HI16:
+	case BFD_RELOC_SH_GOTPC_LOW16:
+	case BFD_RELOC_SH_GOTPC_MEDLOW16:
+	case BFD_RELOC_SH_GOTPC_MEDHI16:
+	case BFD_RELOC_SH_GOTPC_HI16:
+	  *valp = 0;
+	  return;
+
+	default:
+	  ;
+	}
+
+      /* We might need to change some relocs into the corresponding
+	 PC-relative one.  */
+      switch (orig_fx_r_type)
+	{
+	case BFD_RELOC_64:
+	  fixP->fx_r_type = BFD_RELOC_64_PCREL;
+	  break;
+
+	case BFD_RELOC_SH_IMM_LOW16:
+	  fixP->fx_r_type = BFD_RELOC_SH_IMM_LOW16_PCREL;
+	  break;
+
+	case BFD_RELOC_SH_IMM_MEDLOW16:
+	  fixP->fx_r_type = BFD_RELOC_SH_IMM_MEDLOW16_PCREL;
+	  break;
+
+	case BFD_RELOC_SH_IMM_MEDHI16:
+	  fixP->fx_r_type = BFD_RELOC_SH_IMM_MEDHI16_PCREL;
+	  break;
+
+	case BFD_RELOC_SH_IMM_HI16:
+	  fixP->fx_r_type = BFD_RELOC_SH_IMM_HI16_PCREL;
+	  break;
+
+	case SHMEDIA_BFD_RELOC_PT:
+	  /* This is how we see a difference between PT and PTA when not
+	     expanding (in which case we handle it in
+	     shmedia_md_convert_frag).  Note that we don't see a
+	     difference after the reloc is emitted.  */
+	  fixP->fx_r_type = BFD_RELOC_SH_PT_16;
+	  break;
+
+	case BFD_RELOC_SH_PT_16:
+	  /* This tells us there was a PTA or PTB insn explicitly
+	     expressed as such (not as PT).  We "or" in a 1 into the
+	     lowest bit in the (unused) destination field to tell the
+	     linker that it should check the right ISA type of the
+	     destination and not just change a PTA to PTB (if necessary).  */
+	  md_number_to_chars (buf, insn | (1 << 10), 4);
+	  break;
+
+	case BFD_RELOC_64_PCREL:
+	case BFD_RELOC_SH_IMM_LOW16_PCREL:
+	case BFD_RELOC_SH_IMM_MEDLOW16_PCREL:
+	case BFD_RELOC_SH_IMM_MEDHI16_PCREL:
+	case BFD_RELOC_SH_IMM_HI16_PCREL:
+	  /* Already handled.  */
+	  break;
+
+	default:
+	  /* Everything else that changes into a pc-relative relocation is
+	     an error.  */
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			_("Invalid operand expression"));
+	  break;
+	}
+
+      return;
+    }
+
+  /* If an expression looked like it was PC-relative, but was completely
+     resolvable, we end up here with the result only in *VALP, and no
+     relocation will be emitted.  */
+  if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+    {
+      /* Emit error for an out-of-range value.  */
+      shmedia_check_limits (valp, fixP->fx_r_type, fixP);
+
+      switch (fixP->fx_r_type)
+	{
+	case BFD_RELOC_SH_IMM_LOW16:
+	  md_number_to_chars (buf, insn | ((val & 65535) << 10), 4);
+	  break;
+
+	case BFD_RELOC_SH_IMM_MEDLOW16:
+	  md_number_to_chars (buf,
+			      insn
+			      | ((valueT) (val & ((valueT) 65535 << 16))
+				 >> (16 - 10)), 4);
+	  break;
+
+	case BFD_RELOC_SH_IMM_MEDHI16:
+	  md_number_to_chars (buf,
+			      insn
+			      | ((valueT) (val & ((valueT) 65535 << 32))
+				 >> (32 - 10)), 4);
+	  break;
+
+	case BFD_RELOC_SH_IMM_HI16:
+	  md_number_to_chars (buf,
+			      insn
+			      | ((valueT) (val & ((valueT) 65535 << 48))
+				 >> (48 - 10)), 4);
+	  break;
+
+	case BFD_RELOC_SH_IMMS16:
+	case BFD_RELOC_SH_IMMU16:
+	  md_number_to_chars (buf, insn | ((val & 65535) << 10), 4);
+	  break;
+
+	case BFD_RELOC_SH_IMMS10:
+	  md_number_to_chars (buf, insn | ((val & 0x3ff) << 10), 4);
+	  break;
+
+	case BFD_RELOC_SH_IMMS10BY2:
+	  md_number_to_chars (buf,
+			      insn | ((val & (0x3ff << 1)) << (10 - 1)), 4);
+	  break;
+
+	case BFD_RELOC_SH_IMMS10BY4:
+	  md_number_to_chars (buf,
+			      insn | ((val & (0x3ff << 2)) << (10 - 2)), 4);
+	  break;
+
+	case BFD_RELOC_SH_SHMEDIA_CODE:
+	  /* We just ignore and remove this one for the moment.  FIXME:
+	     Use it when implementing relaxing.  */
+	  break;
+
+	case BFD_RELOC_64:
+	  md_number_to_chars (buf, val, 8);
+	  break;
+
+	case SHMEDIA_BFD_RELOC_PT:
+	  /* Change a PT to PTB if the operand turned out to be SHcompact.
+	     The basic opcode specified with PT is equivalent to PTA.  */
+	  if ((val & 1) == 0)
+	    insn |= SHMEDIA_PTB_BIT;
+	  /* Fall through.  */
+
+	case BFD_RELOC_SH_PT_16:
+	  if (! sh64_expand || sh_relax)
+	    {
+	      /* Check if the operand of a PTA or PTB was for the "wrong"
+		 ISA.  A PT had an incoming fixup of SHMEDIA_BFD_RELOC_PT,
+		 which we have changed to the right type above.  */
+	      if (orig_fx_r_type != SHMEDIA_BFD_RELOC_PT)
+		{
+		  if ((insn & SHMEDIA_PTB_BIT) != 0 && (val & 1) != 0)
+		    as_bad_where (fixP->fx_file, fixP->fx_line,
+				  _("PTB operand is a SHmedia symbol"));
+		  else if ((insn & SHMEDIA_PTB_BIT) == 0 && (val & 1) == 0)
+		    as_bad_where (fixP->fx_file, fixP->fx_line,
+				  _("PTA operand is a SHcompact symbol"));
+		}
+
+	      md_number_to_chars (buf,
+				  insn | ((val & (0xffff << 2))
+					  << (10 - 2)),
+				  4);
+	      break;
+	    }
+	  /* Fall through.  */
+
+	default:
+	  /* This isn't a BAD_CASE, because presumably we can get here
+	     from unexpected operands.  Since we don't handle them, make
+	     them syntax errors.  */
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			_("invalid expression in operand"));
+	}
+      fixP->fx_done = 1;
+    }
+}
+
+/* Hook called from md_convert_frag in tc-sh.c.  */
+
+static void
+shmedia_md_convert_frag (output_bfd, seg, fragP, final)
+     bfd *output_bfd ATTRIBUTE_UNUSED;
+     segT seg ATTRIBUTE_UNUSED;
+     fragS *fragP;
+     boolean final;
+{
+  /* Pointer to first byte in variable-sized part of the frag.	*/
+  char *var_partp;
+
+  /* Pointer to first opcode byte in frag.  */
+  char *opcodep;
+
+  /* Pointer to frag of opcode.  */
+  fragS *opc_fragP = fragP->tc_frag_data.opc_frag;
+
+  /* Size in bytes of variable-sized part of frag.  */
+  int var_part_size = 0;
+
+  /* This is part of *fragP.  It contains all information about addresses
+     and offsets to varying parts.  */
+  symbolS *symbolP = fragP->fr_symbol;
+
+  boolean reloc_needed
+    = (! final
+       || sh_relax
+       || symbolP == NULL
+       || ! S_IS_DEFINED (symbolP)
+       || S_IS_EXTERN (symbolP)
+       || S_IS_WEAK (symbolP)
+       || (S_GET_SEGMENT (fragP->fr_symbol) != absolute_section
+	   && S_GET_SEGMENT (fragP->fr_symbol) != seg));
+
+  bfd_reloc_code_real_type reloctype = BFD_RELOC_NONE;
+
+  unsigned long var_part_offset;
+
+  /* Where, in file space, does addr point?  */
+  bfd_vma target_address;
+  bfd_vma opcode_address;
+
+  /* What was the insn?  */
+  unsigned long insn;
+  know (fragP->fr_type == rs_machine_dependent);
+
+  var_part_offset = fragP->fr_fix;
+  var_partp = fragP->fr_literal + var_part_offset;
+  opcodep = fragP->fr_opcode;
+
+  insn = target_big_endian ? bfd_getb32 (opcodep) : bfd_getl32 (opcodep);
+
+  target_address
+    = ((symbolP && final && ! sh_relax ? S_GET_VALUE (symbolP) : 0)
+       + fragP->fr_offset);
+
+  /* The opcode that would be extended is the last four "fixed" bytes.  */
+  opcode_address = fragP->fr_address + fragP->fr_fix - 4;
+
+  switch (fragP->fr_subtype)
+    {
+    case C (SH64PCREL16PT_64, SH64PCREL16):
+    case C (SH64PCREL16PT_32, SH64PCREL16):
+      /* We can get a PT to a relaxed SHcompact address if it is in the
+	 same section; a mixed-ISA section.  Change the opcode to PTB if
+	 so.  */
+      if ((target_address & 1) == 0)
+	insn |= SHMEDIA_PTB_BIT;
+      /* Fall through.  */
+
+    case C (SH64PCREL16_32, SH64PCREL16):
+    case C (SH64PCREL16_64, SH64PCREL16):
+      /* Check that a PTA or PTB points to the right type of target.  We
+	 can get here for a SHcompact target if we are in a mixed-ISA
+	 section.  */
+      if (((target_address & 1) == 0) && ((insn & SHMEDIA_PTB_BIT) == 0))
+	as_bad_where (fragP->fr_file, fragP->fr_line,
+		      _("PTA operand is a SHcompact symbol"));
+      if (((target_address & 1) != 0) && ((insn & SHMEDIA_PTB_BIT) != 0))
+	as_bad_where (fragP->fr_file, fragP->fr_line,
+		      _("PTB operand is a SHmedia symbol"));
+
+      /* When relaxing, we do not output the address in the insn, but
+	 instead a 1 into the low bit.  This matches what the linker
+	 expects to find for a BFD_RELOC_SH_PT_16 reloc, when it checks
+	 correctness for PTA/PTB insn; used when the target address is
+	 unknown (which is not the case here).  */
+      md_number_to_chars (opcodep,
+			  insn
+			  | (((sh_relax
+			       ? 1 : ((target_address - opcode_address) / 4))
+			      & ((1 << 16) - 1)) << 10),
+			  4);
+
+      /* Note that we do not emit info that this was originally a PT since
+	 we have resolved to which one of PTA or PTB it will be.  */
+      if (sh_relax)
+	fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		 fragP->fr_symbol, fragP->fr_offset, 1, BFD_RELOC_SH_PT_16);
+      var_part_size = 0;
+      break;
+
+    case C (SH64PCREL16_32, SH64PCRELPLT):
+    case C (SH64PCREL16PT_32, SH64PCRELPLT):
+      reloctype = BFD_RELOC_32_PLT_PCREL;
+      reloc_needed = 1;
+      /* Fall through */
+
+    case C (SH64PCREL16_32, SH64PCREL32):
+    case C (SH64PCREL16_64, SH64PCREL32):
+    case C (SH64PCREL16PT_32, SH64PCREL32):
+    case C (SH64PCREL16PT_64, SH64PCREL32):
+      /* In the fixed bit, put in a MOVI.  */
+      md_number_to_chars (opcodep,
+			  SHMEDIA_MOVI_OPC
+			  | (SHMEDIA_TEMP_REG << 4)
+			  | ((((reloc_needed
+				? 0 : (target_address - (opcode_address + 8))
+				) >> 16) & 65535) << 10),
+			  4);
+
+      /* Fill in a SHORI for the low part.  */
+      md_number_to_chars (var_partp,
+			  SHMEDIA_SHORI_OPC
+			  | (SHMEDIA_TEMP_REG << 4)
+			  | (((reloc_needed
+			       ? 0 : (target_address - (opcode_address + 8)))
+			      & 65535) << 10),
+			  4);
+
+      /* End with a "PTREL R25,TRd".  */
+      md_number_to_chars (var_partp + 4,
+			  SHMEDIA_PTREL_OPC | (insn & SHMEDIA_LIKELY_BIT)
+			  | (SHMEDIA_TEMP_REG << 10)
+			  | (insn & (7 << 4)),
+			  4);
+
+      /* We need relocs only if the target symbol was undefined or if
+	 we're relaxing.  */
+      if (reloc_needed)
+	{
+	  fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		   fragP->fr_symbol, fragP->fr_offset - 8, 1,
+		   reloctype == BFD_RELOC_32_PLT_PCREL
+		   ? BFD_RELOC_SH_PLT_MEDLOW16
+		   : BFD_RELOC_SH_IMM_MEDLOW16_PCREL);
+	  fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+		   fragP->fr_offset - 4, 1,
+		   reloctype == BFD_RELOC_32_PLT_PCREL
+		   ? BFD_RELOC_SH_PLT_LOW16
+		   : BFD_RELOC_SH_IMM_LOW16_PCREL);
+	}
+
+      var_part_size = 8;
+      break;
+
+    case C (SH64PCREL16_64, SH64PCREL48):
+    case C (SH64PCREL16PT_64, SH64PCREL48):
+      /* In the fixed bit, put in a MOVI.  */
+      md_number_to_chars (opcodep,
+			  SHMEDIA_MOVI_OPC
+			  | (SHMEDIA_TEMP_REG << 4)
+			  | ((((reloc_needed
+				? 0 : (target_address - (opcode_address + 12))
+				) >> 32) & 65535) << 10),
+			  4);
+
+      /* The first SHORI, for the medium part.  */
+      md_number_to_chars (var_partp,
+			  SHMEDIA_SHORI_OPC
+			  | (SHMEDIA_TEMP_REG << 4)
+			  | ((((reloc_needed
+				? 0 : (target_address - (opcode_address + 12))
+				) >> 16) & 65535) << 10),
+			  4);
+
+      /* Fill in a SHORI for the low part.  */
+      md_number_to_chars (var_partp + 4,
+			  SHMEDIA_SHORI_OPC
+			  | (SHMEDIA_TEMP_REG << 4)
+			  | (((reloc_needed
+			       ? 0 : (target_address - (opcode_address + 12)))
+			      & 65535) << 10),
+			  4);
+
+      /* End with a "PTREL R25,TRd".  */
+      md_number_to_chars (var_partp + 8,
+			  SHMEDIA_PTREL_OPC | (insn & SHMEDIA_LIKELY_BIT)
+			  | (SHMEDIA_TEMP_REG << 10)
+			  | (insn & (7 << 4)),
+			  4);
+
+      /* We need relocs only if the target symbol was undefined or if
+	 we're relaxing.  */
+      if (reloc_needed)
+	{
+	  fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		   fragP->fr_symbol, fragP->fr_offset - 12, 1,
+		   reloctype == BFD_RELOC_32_PLT_PCREL
+		   ? BFD_RELOC_SH_PLT_MEDHI16
+		   : BFD_RELOC_SH_IMM_MEDHI16_PCREL);
+	  fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+		   fragP->fr_offset - 8, 1,
+		   reloctype == BFD_RELOC_32_PLT_PCREL
+		   ? BFD_RELOC_SH_PLT_MEDLOW16
+		   : BFD_RELOC_SH_IMM_MEDLOW16_PCREL);
+	  fix_new (fragP, var_partp - fragP->fr_literal + 4, 4, fragP->fr_symbol,
+		   fragP->fr_offset - 4, 1,
+		   reloctype == BFD_RELOC_32_PLT_PCREL
+		   ? BFD_RELOC_SH_PLT_LOW16
+		   : BFD_RELOC_SH_IMM_LOW16_PCREL);
+	}
+
+      var_part_size = 12;
+      break;
+
+    case C (SH64PCREL16_64, SH64PCRELPLT):
+    case C (SH64PCREL16PT_64, SH64PCRELPLT):
+      reloctype = BFD_RELOC_32_PLT_PCREL;
+      reloc_needed = 1;
+      /* Fall through */
+
+    case C (SH64PCREL16_64, SH64PCREL64):
+    case C (SH64PCREL16PT_64, SH64PCREL64):
+      /* In the fixed bit, put in a MOVI.  */
+      md_number_to_chars (opcodep,
+			  SHMEDIA_MOVI_OPC
+			  | (SHMEDIA_TEMP_REG << 4)
+			  | ((((reloc_needed
+				? 0 : (target_address - (opcode_address + 16))
+				) >> 48) & 65535) << 10),
+			  4);
+
+      /* The first SHORI, for the medium-high part.  */
+      md_number_to_chars (var_partp,
+			  SHMEDIA_SHORI_OPC
+			  | (SHMEDIA_TEMP_REG << 4)
+			  | ((((reloc_needed
+				? 0 : (target_address - (opcode_address + 16))
+				) >> 32) & 65535) << 10),
+			  4);
+
+      /* A SHORI, for the medium-low part.  */
+      md_number_to_chars (var_partp + 4,
+			  SHMEDIA_SHORI_OPC
+			  | (SHMEDIA_TEMP_REG << 4)
+			  | ((((reloc_needed
+				? 0 : (target_address - (opcode_address + 16))
+				) >> 16) & 65535) << 10),
+			  4);
+
+      /* Fill in a SHORI for the low part.  */
+      md_number_to_chars (var_partp + 8,
+			  SHMEDIA_SHORI_OPC
+			  | (SHMEDIA_TEMP_REG << 4)
+			  | (((reloc_needed
+			       ? 0 : (target_address - (opcode_address + 16)))
+			      & 65535) << 10),
+			  4);
+
+      /* End with a "PTREL R25,TRd".  */
+      md_number_to_chars (var_partp + 12,
+			  SHMEDIA_PTREL_OPC | (insn & SHMEDIA_LIKELY_BIT)
+			  | (SHMEDIA_TEMP_REG << 10)
+			  | (insn & (7 << 4)),
+			  4);
+
+      /* We need relocs only if the target symbol was undefined or if
+	 we're relaxing.  */
+      if (reloc_needed)
+	{
+	  fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		   fragP->fr_symbol, fragP->fr_offset - 16, 1,
+		   reloctype == BFD_RELOC_32_PLT_PCREL
+		   ? BFD_RELOC_SH_PLT_HI16
+		   : BFD_RELOC_SH_IMM_HI16_PCREL);
+	  fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+		   fragP->fr_offset - 12, 1,
+		   reloctype == BFD_RELOC_32_PLT_PCREL
+		   ? BFD_RELOC_SH_PLT_MEDHI16
+		   : BFD_RELOC_SH_IMM_MEDHI16_PCREL);
+	  fix_new (fragP, var_partp - fragP->fr_literal + 4, 4, fragP->fr_symbol,
+		   fragP->fr_offset - 8, 1,
+		   reloctype == BFD_RELOC_32_PLT_PCREL
+		   ? BFD_RELOC_SH_PLT_MEDLOW16
+		   : BFD_RELOC_SH_IMM_MEDLOW16_PCREL);
+	  fix_new (fragP, var_partp - fragP->fr_literal + 8, 4, fragP->fr_symbol,
+		   fragP->fr_offset - 4, 1,
+		   reloctype == BFD_RELOC_32_PLT_PCREL
+		   ? BFD_RELOC_SH_PLT_LOW16
+		   : BFD_RELOC_SH_IMM_LOW16_PCREL);
+	}
+
+      var_part_size = 16;
+      break;
+
+    case C (MOVI_IMM_64, MOVI_GOTOFF):
+      reloctype = BFD_RELOC_32_GOTOFF;
+      reloc_needed = 1;
+      /* Fall through.  */
+      
+    case C (MOVI_IMM_64, UNDEF_MOVI):
+    case C (MOVI_IMM_64, MOVI_64):
+      {
+	/* We only get here for undefined symbols, so we can simplify
+	   handling compared to those above; we have 0 in the parts that
+	   will be filled with the symbol parts.  */
+
+	int reg = (insn >> 4) & 0x3f;
+
+	/* In the fixed bit, put in a MOVI.  */
+	md_number_to_chars (opcodep, SHMEDIA_MOVI_OPC | (reg << 4), 4);
+	fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		 fragP->fr_symbol, fragP->fr_offset, 0,
+		 reloctype == BFD_RELOC_NONE
+		 ? BFD_RELOC_SH_IMM_HI16
+		 : reloctype == BFD_RELOC_32_GOTOFF
+		 ? BFD_RELOC_SH_GOTOFF_HI16
+		 : (abort (), BFD_RELOC_SH_IMM_HI16));
+
+	/* The first SHORI, for the medium-high part.  */
+	md_number_to_chars (var_partp, SHMEDIA_SHORI_OPC | (reg << 4), 4);
+	fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+		 fragP->fr_offset, 0,
+		 reloctype == BFD_RELOC_NONE
+		 ? BFD_RELOC_SH_IMM_MEDHI16
+		 : reloctype == BFD_RELOC_32_GOTOFF
+		 ? BFD_RELOC_SH_GOTOFF_MEDHI16
+		 : (abort (), BFD_RELOC_SH_IMM_MEDHI16));
+
+	/* A SHORI, for the medium-low part.  */
+	md_number_to_chars (var_partp + 4,
+			    SHMEDIA_SHORI_OPC | (reg << 4), 4);
+	fix_new (fragP, var_partp - fragP->fr_literal + 4, 4, fragP->fr_symbol,
+		 fragP->fr_offset, 0,
+		 reloctype == BFD_RELOC_NONE
+		 ? BFD_RELOC_SH_IMM_MEDLOW16
+		 : reloctype == BFD_RELOC_32_GOTOFF
+		 ? BFD_RELOC_SH_GOTOFF_MEDLOW16
+		 : (abort (), BFD_RELOC_SH_IMM_MEDLOW16));
+
+	/* Fill in a SHORI for the low part.  */
+	md_number_to_chars (var_partp + 8,
+			    SHMEDIA_SHORI_OPC | (reg << 4), 4);
+	fix_new (fragP, var_partp - fragP->fr_literal + 8, 4, fragP->fr_symbol,
+		 fragP->fr_offset, 0,
+		 reloctype == BFD_RELOC_NONE
+		 ? BFD_RELOC_SH_IMM_LOW16
+		 : reloctype == BFD_RELOC_32_GOTOFF
+		 ? BFD_RELOC_SH_GOTOFF_LOW16
+		 : (abort (), BFD_RELOC_SH_IMM_LOW16));
+
+	var_part_size = 12;
+	break;
+      }
+
+    case C (MOVI_IMM_32, MOVI_GOTOFF):
+      reloctype = BFD_RELOC_32_GOTOFF;
+      reloc_needed = 1;
+      /* Fall through.  */
+      
+    case C (MOVI_IMM_32, UNDEF_MOVI):
+    case C (MOVI_IMM_32, MOVI_32):
+      {
+	/* Note that we only get here for undefined symbols.  */
+
+	int reg = (insn >> 4) & 0x3f;
+
+	/* A MOVI, for the high part.  */
+	md_number_to_chars (opcodep, SHMEDIA_MOVI_OPC | (reg << 4), 4);
+	fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		 fragP->fr_symbol, fragP->fr_offset, 0,
+		 reloctype == BFD_RELOC_NONE
+		 ? BFD_RELOC_SH_IMM_MEDLOW16
+		 : reloctype == BFD_RELOC_32_GOTOFF
+		 ? BFD_RELOC_SH_GOTOFF_MEDLOW16
+		 : reloctype == BFD_RELOC_SH_GOTPC
+		 ? BFD_RELOC_SH_GOTPC_MEDLOW16
+		 : reloctype == BFD_RELOC_32_PLT_PCREL
+		 ? BFD_RELOC_SH_PLT_MEDLOW16
+		 : (abort (), BFD_RELOC_SH_IMM_MEDLOW16));
+
+	/* Fill in a SHORI for the low part.  */
+	md_number_to_chars (var_partp,
+			    SHMEDIA_SHORI_OPC | (reg << 4), 4);
+	fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+		 fragP->fr_offset, 0,
+		 reloctype == BFD_RELOC_NONE
+		 ? BFD_RELOC_SH_IMM_LOW16
+		 : reloctype == BFD_RELOC_32_GOTOFF
+		 ? BFD_RELOC_SH_GOTOFF_LOW16
+		 : reloctype == BFD_RELOC_SH_GOTPC
+		 ? BFD_RELOC_SH_GOTPC_LOW16
+		 : reloctype == BFD_RELOC_32_PLT_PCREL
+		 ? BFD_RELOC_SH_PLT_LOW16
+		 : (abort (), BFD_RELOC_SH_IMM_LOW16));
+
+	var_part_size = 4;
+	break;
+      }
+
+    case C (MOVI_IMM_32_PCREL, MOVI_16):
+    case C (MOVI_IMM_64_PCREL, MOVI_16):
+      md_number_to_chars (opcodep,
+			  insn
+			  | (((reloc_needed
+			       ? 0 : (target_address - opcode_address))
+			      & 65535) << 10),
+			  4);
+      if (reloc_needed)
+	fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		 fragP->fr_symbol, fragP->fr_offset, 1,
+		 BFD_RELOC_SH_IMM_LOW16_PCREL);
+      var_part_size = 0;
+      break;
+
+    case C (MOVI_IMM_32, MOVI_16):
+    case C (MOVI_IMM_64, MOVI_16):
+      md_number_to_chars (opcodep,
+			  insn
+			  | (((reloc_needed ? 0 : target_address)
+			      & 65535) << 10),
+			  4);
+      if (reloc_needed)
+	abort ();
+      var_part_size = 0;
+      break;
+
+    case C (MOVI_IMM_32_PCREL, MOVI_PLT):
+      reloctype = BFD_RELOC_32_PLT_PCREL;
+      goto movi_imm_32_pcrel_reloc_needed;
+
+    case C (MOVI_IMM_32_PCREL, MOVI_GOTPC):
+      reloctype = BFD_RELOC_SH_GOTPC;
+      /* Fall through.  */
+
+    movi_imm_32_pcrel_reloc_needed:
+      reloc_needed = 1;
+      /* Fall through.  */
+
+    case C (MOVI_IMM_32_PCREL, MOVI_32):
+    case C (MOVI_IMM_64_PCREL, MOVI_32):
+      {
+	int reg = (insn >> 4) & 0x3f;
+
+	md_number_to_chars (opcodep,
+			    insn
+			    | (((((reloc_needed
+				   ? 0 : (target_address - opcode_address)))
+				>> 16) & 65535) << 10), 4);
+
+	/* A SHORI, for the low part.  */
+	md_number_to_chars (var_partp,
+			    SHMEDIA_SHORI_OPC
+			    | (reg << 4)
+			    | (((reloc_needed
+				 ? 0 : (target_address - opcode_address))
+				& 65535) << 10), 4);
+	if (reloc_needed)
+	  {
+	    fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		     fragP->fr_symbol, fragP->fr_offset, 1,
+		     reloctype == BFD_RELOC_NONE
+		     ? BFD_RELOC_SH_IMM_MEDLOW16_PCREL
+		     : reloctype == BFD_RELOC_SH_GOTPC
+		     ? BFD_RELOC_SH_GOTPC_MEDLOW16
+		     : reloctype == BFD_RELOC_32_PLT_PCREL
+		     ? BFD_RELOC_SH_PLT_MEDLOW16
+		     : (abort (), BFD_RELOC_SH_IMM_MEDLOW16_PCREL));
+	    fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+		     fragP->fr_offset + 4, 1,
+		     reloctype == BFD_RELOC_NONE
+		     ? BFD_RELOC_SH_IMM_LOW16_PCREL
+		     : reloctype == BFD_RELOC_SH_GOTPC
+		     ? BFD_RELOC_SH_GOTPC_LOW16
+		     : reloctype == BFD_RELOC_32_PLT_PCREL
+		     ? BFD_RELOC_SH_PLT_LOW16
+		     : (abort (), BFD_RELOC_SH_IMM_LOW16_PCREL));
+	  }
+	var_part_size = 4;
+      }
+      break;
+
+    case C (MOVI_IMM_32_PCREL, MOVI_48):
+    case C (MOVI_IMM_64_PCREL, MOVI_48):
+      {
+	int reg = (insn >> 4) & 0x3f;
+
+	md_number_to_chars (opcodep,
+			    insn
+			    | (((((reloc_needed
+				   ? 0 : (target_address - opcode_address)))
+				>> 32) & 65535) << 10), 4);
+
+	/* A SHORI, for the medium part.  */
+	md_number_to_chars (var_partp,
+			    SHMEDIA_SHORI_OPC
+			    | (reg << 4)
+			    | ((((reloc_needed
+				  ? 0 : (target_address - opcode_address))
+				 >> 16) & 65535) << 10), 4);
+
+	/* A SHORI, for the low part.  */
+	md_number_to_chars (var_partp + 4,
+			    SHMEDIA_SHORI_OPC
+			    | (reg << 4)
+			    | (((reloc_needed
+				 ? 0 : (target_address - opcode_address))
+				& 65535) << 10), 4);
+	if (reloc_needed)
+	  {
+	    fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		     fragP->fr_symbol, fragP->fr_offset, 1,
+		     BFD_RELOC_SH_IMM_MEDHI16_PCREL);
+	    fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+		     fragP->fr_offset + 4, 1, BFD_RELOC_SH_IMM_MEDLOW16_PCREL);
+	    fix_new (fragP, var_partp - fragP->fr_literal + 4, 4, fragP->fr_symbol,
+		     fragP->fr_offset + 8, 1, BFD_RELOC_SH_IMM_LOW16_PCREL);
+	  }
+	var_part_size = 8;
+      }
+      break;
+
+    case C (MOVI_IMM_64_PCREL, MOVI_PLT):
+      reloctype = BFD_RELOC_32_PLT_PCREL;
+      goto movi_imm_64_pcrel_reloc_needed;
+
+    case C (MOVI_IMM_64_PCREL, MOVI_GOTPC):
+      reloctype = BFD_RELOC_SH_GOTPC;
+      /* Fall through.  */
+
+    movi_imm_64_pcrel_reloc_needed:
+      reloc_needed = 1;
+      /* Fall through.  */
+ 
+    case C (MOVI_IMM_32_PCREL, MOVI_64):
+    case C (MOVI_IMM_64_PCREL, MOVI_64):
+      {
+	int reg = (insn >> 4) & 0x3f;
+
+	md_number_to_chars (opcodep,
+			    insn
+			    | (((((reloc_needed
+				   ? 0 : (target_address - opcode_address)))
+				>> 48) & 65535) << 10), 4);
+
+	/* A SHORI, for the medium-high part.  */
+	md_number_to_chars (var_partp,
+			    SHMEDIA_SHORI_OPC
+			    | (reg << 4)
+			    | ((((reloc_needed
+				  ? 0 : (target_address - opcode_address))
+				 >> 32) & 65535) << 10), 4);
+
+	/* A SHORI, for the medium-low part.  */
+	md_number_to_chars (var_partp + 4,
+			    SHMEDIA_SHORI_OPC
+			    | (reg << 4)
+			    | ((((reloc_needed
+				  ? 0 : (target_address - opcode_address))
+				 >> 16) & 65535) << 10), 4);
+
+	/* A SHORI, for the low part.  */
+	md_number_to_chars (var_partp + 8,
+			    SHMEDIA_SHORI_OPC
+			    | (reg << 4)
+			    | (((reloc_needed
+				 ? 0 : (target_address - opcode_address))
+				& 65535) << 10), 4);
+	if (reloc_needed)
+	  {
+	    fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+		     fragP->fr_symbol, fragP->fr_offset, 1,
+		     reloctype == BFD_RELOC_NONE
+		     ? BFD_RELOC_SH_IMM_HI16_PCREL
+		     : reloctype == BFD_RELOC_SH_GOTPC
+		     ? BFD_RELOC_SH_GOTPC_HI16
+		     : reloctype == BFD_RELOC_32_PLT_PCREL
+		     ? BFD_RELOC_SH_PLT_HI16
+		     : (abort (), BFD_RELOC_SH_IMM_HI16_PCREL));
+	    fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+		     fragP->fr_offset + 4, 1,
+		     reloctype == BFD_RELOC_NONE
+		     ? BFD_RELOC_SH_IMM_MEDHI16_PCREL
+		     : reloctype == BFD_RELOC_SH_GOTPC
+		     ? BFD_RELOC_SH_GOTPC_MEDHI16
+		     : reloctype == BFD_RELOC_32_PLT_PCREL
+		     ? BFD_RELOC_SH_PLT_MEDHI16
+		     : (abort (), BFD_RELOC_SH_IMM_MEDHI16_PCREL));
+	    fix_new (fragP, var_partp - fragP->fr_literal + 4, 4,
+		     fragP->fr_symbol,
+		     fragP->fr_offset + 8, 1,
+		     reloctype == BFD_RELOC_NONE
+		     ? BFD_RELOC_SH_IMM_MEDLOW16_PCREL
+		     : reloctype == BFD_RELOC_SH_GOTPC
+		     ? BFD_RELOC_SH_GOTPC_MEDLOW16
+		     : reloctype == BFD_RELOC_32_PLT_PCREL
+		     ? BFD_RELOC_SH_PLT_MEDLOW16
+		     : (abort (), BFD_RELOC_SH_IMM_MEDLOW16_PCREL));
+	    fix_new (fragP, var_partp - fragP->fr_literal + 8, 4,
+		     fragP->fr_symbol,
+		     fragP->fr_offset + 12, 1,
+		     reloctype == BFD_RELOC_NONE
+		     ? BFD_RELOC_SH_IMM_LOW16_PCREL
+		     : reloctype == BFD_RELOC_SH_GOTPC
+		     ? BFD_RELOC_SH_GOTPC_LOW16
+		     : reloctype == BFD_RELOC_32_PLT_PCREL
+		     ? BFD_RELOC_SH_PLT_LOW16
+		     : (abort (), BFD_RELOC_SH_IMM_LOW16_PCREL));
+	  }
+	var_part_size = 12;
+      }
+      break;
+
+    default:
+      BAD_CASE (fragP->fr_subtype);
+    }
+
+  fragP->fr_fix += var_part_size;
+  fragP->fr_var = 0;
+}
+
+/* Mask NUMBER (originating from a signed number) corresponding to the HOW
+   reloc.  */
+
+static unsigned long
+shmedia_mask_number (number, how)
+     unsigned long number;
+     bfd_reloc_code_real_type how;
+{
+  switch (how)
+    {
+    case BFD_RELOC_SH_IMMU5:
+      number &= (1 << 5) - 1;
+      break;
+
+    case BFD_RELOC_SH_IMMS6:
+    case BFD_RELOC_SH_IMMU6:
+      number &= (1 << 6) - 1;
+      break;
+
+    case BFD_RELOC_SH_IMMS6BY32:
+      number = (number & ((1 << (6 + 5)) - 1)) >> 5;
+      break;
+
+    case BFD_RELOC_SH_IMMS10:
+      number &= (1 << 10) - 1;
+      break;
+
+    case BFD_RELOC_SH_IMMS10BY2:
+      number = (number & ((1 << (10 + 1)) - 1)) >> 1;
+      break;
+
+    case BFD_RELOC_SH_IMMS10BY4:
+      number = (number & ((1 << (10 + 2)) - 1)) >> 2;
+      break;
+
+    case BFD_RELOC_SH_IMMS10BY8:
+      number = (number & ((1 << (10 + 3)) - 1)) >> 3;
+      break;
+
+    case BFD_RELOC_SH_IMMS16:
+    case BFD_RELOC_SH_IMMU16:
+      number &= (1 << 16) - 1;
+      break;
+
+    default:
+      BAD_CASE (how);
+    }
+
+  return number;
+}
+
+/* Emit errors for values out-of-range, using as_bad_where if FRAGP is
+   non-NULL, as_bad otherwise.  */
+
+static void
+shmedia_check_limits (valp, reloc, fixp)
+     offsetT *valp;
+     bfd_reloc_code_real_type reloc;
+     fixS *fixp;
+{
+  offsetT val = *valp;
+
+  char *msg = NULL;
+
+  switch (reloc)
+    {
+    case BFD_RELOC_SH_IMMU5:
+      if (val < 0 || val > (1 << 5) - 1)
+	msg = _("invalid operand, not a 5-bit unsigned value: %d");
+      break;
+
+    case BFD_RELOC_SH_IMMS6:
+      if (val < -(1 << 5) || val > (1 << 5) - 1)
+	msg = _("invalid operand, not a 6-bit signed value: %d");
+      break;
+
+    case BFD_RELOC_SH_IMMU6:
+      if (val < 0 || val > (1 << 6) - 1)
+	msg = _("invalid operand, not a 6-bit unsigned value: %d");
+      break;
+
+    case BFD_RELOC_SH_IMMS6BY32:
+      if (val < -(1 << 10) || val > (1 << 10) - 1)
+	msg = _("invalid operand, not a 11-bit signed value: %d");
+      else if (val & 31)
+	msg = _("invalid operand, not a multiple of 32: %d");
+      break;
+
+    case BFD_RELOC_SH_IMMS10:
+      if (val < -(1 << 9) || val > (1 << 9) - 1)
+	msg = _("invalid operand, not a 10-bit signed value: %d");
+      break;
+
+    case BFD_RELOC_SH_IMMS10BY2:
+      if (val < -(1 << 10) || val > (1 << 10) - 1)
+	msg = _("invalid operand, not a 11-bit signed value: %d");
+      else if (val & 1)
+	msg = _("invalid operand, not an even value: %d");
+      break;
+
+    case BFD_RELOC_SH_IMMS10BY4:
+      if (val < -(1 << 11) || val > (1 << 11) - 1)
+	msg = _("invalid operand, not a 12-bit signed value: %d");
+      else if (val & 3)
+	msg = _("invalid operand, not a multiple of 4: %d");
+      break;
+
+    case BFD_RELOC_SH_IMMS10BY8:
+      if (val < -(1 << 12) || val > (1 << 12) - 1)
+	msg = _("invalid operand, not a 13-bit signed value: %d");
+      else if (val & 7)
+	msg = _("invalid operand, not a multiple of 8: %d");
+      break;
+
+    case BFD_RELOC_SH_IMMS16:
+      if (val < -(1 << 15) || val > (1 << 15) - 1)
+	msg = _("invalid operand, not a 16-bit signed value: %d");
+      break;
+
+    case BFD_RELOC_SH_IMMU16:
+      if (val < 0 || val > (1 << 16) - 1)
+	msg = _("invalid operand, not an 16-bit unsigned value: %d");
+      break;
+
+    case BFD_RELOC_SH_PT_16:
+    case SHMEDIA_BFD_RELOC_PT:
+      if (val < -(1 << 15) * 4 || val > ((1 << 15) - 1) * 4 + 1)
+	msg = _("operand out of range for PT, PTA and PTB");
+      else if ((val % 4) != 0 && ((val - 1) % 4) != 0)
+	msg = _("operand not a multiple of 4 for PT, PTA or PTB: %d");
+      break;
+
+      /* These have no limits; they take a 16-bit slice of a 32- or 64-bit
+	 number.  */
+    case BFD_RELOC_SH_IMM_HI16:
+    case BFD_RELOC_SH_IMM_MEDHI16:
+    case BFD_RELOC_SH_IMM_MEDLOW16:
+    case BFD_RELOC_SH_IMM_LOW16:
+    case BFD_RELOC_SH_IMM_HI16_PCREL:
+    case BFD_RELOC_SH_IMM_MEDHI16_PCREL:
+    case BFD_RELOC_SH_IMM_MEDLOW16_PCREL:
+    case BFD_RELOC_SH_IMM_LOW16_PCREL:
+
+    case BFD_RELOC_SH_SHMEDIA_CODE:
+      break;
+
+      /* This one has limits out of our reach.  */
+    case BFD_RELOC_64:
+      break;
+
+    default:
+      BAD_CASE (reloc);
+    }
+
+  if (msg)
+    {
+      if (fixp)
+	as_bad_where (fixp->fx_file, fixp->fx_line, msg, val);
+      else
+	as_bad (msg, val);
+    }
+}
+
+/* Handle an immediate operand by checking limits and noting it for later
+   evaluation if not computable yet, and return a bitfield suitable to
+   "or" into the opcode (non-zero if the value was a constant number).  */
+
+static unsigned long
+shmedia_immediate_op (where, op, pcrel, how)
+     char *where;
+     shmedia_operand_info *op;
+     int pcrel;
+     bfd_reloc_code_real_type how;
+{
+  unsigned long retval = 0;
+
+  /* If this is not an absolute number, make it a fixup.  A constant in
+     place of a pc-relative operand also needs a fixup.  */
+  if (op->immediate.X_op != O_constant || pcrel)
+    fix_new_exp (frag_now,
+		 where - frag_now->fr_literal,
+		 4,
+		 &op->immediate,
+		 pcrel,
+		 how);
+  else
+    {
+      /* Check that the number is within limits as represented by the
+	 reloc, and return the number.  */
+      shmedia_check_limits (&op->immediate.X_add_number, how, NULL);
+
+      retval
+	= shmedia_mask_number ((unsigned long) op->immediate.X_add_number,
+			       how);
+    }
+
+  return retval << 10;
+}
+
+/* Try and parse a register name case-insensitively, return the number of
+   chars consumed.  */
+
+static int
+shmedia_parse_reg (src, mode, reg, argtype)
+     char *src;
+     int *mode;
+     int *reg;
+     shmedia_arg_type argtype;
+{
+  int l0 = TOLOWER (src[0]);
+  int l1 = l0 ? TOLOWER (src[1]) : 0;
+
+  if (l0 == 'r')
+    {
+      if (src[1] >= '1' && src[1] <= '5')
+	{
+	  if (src[2] >= '0' && src[2] <= '9'
+	      && ! IDENT_CHAR ((unsigned char) src[3]))
+	    {
+	      *mode = A_GREG_M;
+	      *reg = 10 * (src[1] - '0') + src[2] - '0';
+	      return 3;
+	    }
+	}
+
+      if (src[1] == '6')
+	{
+	  if (src[2] >= '0' && src[2] <= '3'
+	      && ! IDENT_CHAR ((unsigned char) src[3]))
+	    {
+	      *mode = A_GREG_M;
+	      *reg = 60 + src[2] - '0';
+	      return 3;
+	    }
+	}
+
+      if (src[1] >= '0' && src[1] <= '9'
+	  && ! IDENT_CHAR ((unsigned char) src[2]))
+	{
+	  *mode = A_GREG_M;
+	  *reg = (src[1] - '0');
+	  return 2;
+	}
+    }
+
+  if (l0 == 't' && l1 == 'r')
+    {
+      if (src[2] >= '0' && src[2] <= '7'
+	  && ! IDENT_CHAR ((unsigned char) src[3]))
+	{
+	  *mode = A_TREG_B;
+	  *reg = (src[2] - '0');
+	  return 3;
+	}
+    }
+
+  if (l0 == 'f' && l1 == 'r')
+    {
+      if (src[2] >= '1' && src[2] <= '5')
+	{
+	  if (src[3] >= '0' && src[3] <= '9'
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_FREG_G;
+	      *reg = 10 * (src[2] - '0') + src[3] - '0';
+	      return 4;
+	    }
+	}
+      if (src[2] == '6')
+	{
+	  if (src[3] >= '0' && src[3] <= '3'
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_FREG_G;
+	      *reg = 60 + src[3] - '0';
+	      return 4;
+	    }
+	}
+      if (src[2] >= '0' && src[2] <= '9'
+	  && ! IDENT_CHAR ((unsigned char) src[3]))
+	{
+	  *mode = A_FREG_G;
+	  *reg = (src[2] - '0');
+	  return 3;
+	}
+    }
+
+  if (l0 == 'f' && l1 == 'v')
+    {
+      if (src[2] >= '1' && src[2] <= '5')
+	{
+	  if (src[3] >= '0' && src[3] <= '9'
+	      && ((10 * (src[2] - '0') + src[3] - '0') % 4) == 0
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_FVREG_G;
+	      *reg = 10 * (src[2] - '0') + src[3] - '0';
+	      return 4;
+	    }
+	}
+      if (src[2] == '6')
+	{
+	  if (src[3] == '0'
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_FVREG_G;
+	      *reg = 60 + src[3] - '0';
+	      return 4;
+	    }
+	}
+      if (src[2] >= '0' && src[2] <= '9'
+	  && ((src[2] - '0') % 4) == 0
+	  && ! IDENT_CHAR ((unsigned char) src[3]))
+	{
+	  *mode = A_FVREG_G;
+	  *reg = (src[2] - '0');
+	  return 3;
+	}
+    }
+
+  if (l0 == 'd' && l1 == 'r')
+    {
+      if (src[2] >= '1' && src[2] <= '5')
+	{
+	  if (src[3] >= '0' && src[3] <= '9'
+	      && ((src[3] - '0') % 2) == 0
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_DREG_G;
+	      *reg = 10 * (src[2] - '0') + src[3] - '0';
+	      return 4;
+	    }
+	}
+
+      if (src[2] == '6')
+	{
+	  if ((src[3] == '0' || src[3] == '2')
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_DREG_G;
+	      *reg = 60 + src[3] - '0';
+	      return 4;
+	    }
+	}
+
+      if (src[2] >= '0' && src[2] <= '9'
+	  && ((src[2] - '0') % 2) == 0
+	  && ! IDENT_CHAR ((unsigned char) src[3]))
+	{
+	  *mode = A_DREG_G;
+	  *reg = (src[2] - '0');
+	  return 3;
+	}
+    }
+
+  if (l0 == 'f' && l1 == 'p')
+    {
+      if (src[2] >= '1' && src[2] <= '5')
+	{
+	  if (src[3] >= '0' && src[3] <= '9'
+	      && ((src[3] - '0') % 2) == 0
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_FPREG_G;
+	      *reg = 10 * (src[2] - '0') + src[3] - '0';
+	      return 4;
+	    }
+	}
+
+      if (src[2] == '6')
+	{
+	  if ((src[3] == '0' || src[3] == '2')
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_FPREG_G;
+	      *reg = 60 + src[3] - '0';
+	      return 4;
+	    }
+	}
+
+      if (src[2] >= '0' && src[2] <= '9'
+	  && ((src[2] - '0') % 2) == 0
+	  && ! IDENT_CHAR ((unsigned char) src[3]))
+	{
+	  *mode = A_FPREG_G;
+	  *reg = (src[2] - '0');
+	  return 3;
+	}
+    }
+
+  if (l0 == 'm' && strncasecmp (src, "mtrx", 4) == 0)
+    {
+      if (src[4] == '0' && ! IDENT_CHAR ((unsigned char) src[5]))
+	{
+	  *mode = A_FMREG_G;
+	  *reg = 0;
+	  return 5;
+	}
+
+      if (src[4] == '1' && src[5] == '6'
+	  && ! IDENT_CHAR ((unsigned char) src[6]))
+	{
+	  *mode = A_FMREG_G;
+	  *reg = 16;
+	  return 6;
+	}
+
+      if (src[4] == '3' && src[5] == '2'
+	  && ! IDENT_CHAR ((unsigned char) src[6]))
+	{
+	  *mode = A_FMREG_G;
+	  *reg = 32;
+	  return 6;
+	}
+
+      if (src[4] == '4' && src[5] == '8'
+	  && ! IDENT_CHAR ((unsigned char) src[6]))
+	{
+	  *mode = A_FMREG_G;
+	  *reg = 48;
+	  return 6;
+	}
+    }
+
+  if (l0 == 'c' && l1 == 'r')
+    {
+      if (src[2] >= '1' && src[2] <= '5')
+	{
+	  if (src[3] >= '0' && src[3] <= '9'
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_CREG_K;
+	      *reg = 10 * (src[2] - '0') + src[3] - '0';
+	      return 4;
+	    }
+	}
+      if (src[2] == '6')
+	{
+	  if (src[3] >= '0' && src[3] <= '3'
+	      && ! IDENT_CHAR ((unsigned char) src[4]))
+	    {
+	      *mode = A_CREG_K;
+	      *reg = 60 + src[3] - '0';
+	      return 4;
+	    }
+	}
+      if (src[2] >= '0' && src[2] <= '9'
+	  && ! IDENT_CHAR ((unsigned char) src[3]))
+	{
+	  *mode = A_CREG_K;
+	  *reg = (src[2] - '0');
+	  return 3;
+	}
+    }
+
+  /* We either have an error, a symbol or a control register by predefined
+     name.  To keep things simple but still fast for normal cases, we do
+     linear search in the (not to big) table of predefined control
+     registers.  We only do this when we *expect* a control register.
+     Those instructions should be rare enough that linear searching is ok.
+     Or just read them into a hash-table in shmedia_md_begin.  Since they
+     cannot be specified in the same place of symbol operands, don't add
+     them there to the *main* symbol table as being in "reg_section".  */
+  if (argtype == A_CREG_J || argtype == A_CREG_K)
+    {
+      const shmedia_creg_info *cregp;
+      int len = 0;
+
+      for (cregp = shmedia_creg_table; cregp->name != NULL; cregp++)
+	{
+	  len = strlen (cregp->name);
+	  if (strncasecmp (cregp->name, src, len) == 0
+	      && ! IDENT_CHAR (src[len]))
+	    break;
+	}
+
+      if (cregp->name != NULL)
+	{
+	  *mode = A_CREG_K;
+	  *reg = cregp->cregno;
+	  return len;
+	}
+    }
+
+  return 0;
+}
+
+/* Called from md_estimate_size_before_relax in tc-sh.c  */
+
+static int
+shmedia_md_estimate_size_before_relax (fragP, segment_type)
+     fragS *fragP;
+     segT segment_type ATTRIBUTE_UNUSED;
+{
+  int old_fr_fix;
+  expressionS *exp;
+
+  /* For ELF, we can't relax externally visible symbols; see tc-i386.c.  */
+  boolean sym_relaxable
+    = (fragP->fr_symbol
+       && S_GET_SEGMENT (fragP->fr_symbol) == segment_type
+       && ! S_IS_EXTERNAL (fragP->fr_symbol)
+       && ! S_IS_WEAK (fragP->fr_symbol));
+
+  old_fr_fix = fragP->fr_fix;
+
+  switch (fragP->fr_subtype)
+    {
+    case C (SH64PCREL16_32, UNDEF_SH64PCREL):
+    case C (SH64PCREL16PT_32, UNDEF_SH64PCREL):
+      /* Used to be to somewhere which was unknown.  */
+      if (sym_relaxable)
+	{
+	  int what = GET_WHAT (fragP->fr_subtype);
+
+	  /* In this segment, so head for shortest.  */
+	  fragP->fr_subtype = C (what, SH64PCREL16);
+	}
+      else
+	{
+	  int what = GET_WHAT (fragP->fr_subtype);
+	  /* We know the abs value, but we don't know where we will be
+	     linked, so we must make it the longest.  Presumably we could
+	     switch to a non-pcrel representation, but having absolute
+	     values in PT operands should be rare enough not to be worth
+	     adding that code.  */
+	  fragP->fr_subtype = C (what, SH64PCREL32);
+	}
+      fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
+      break;
+
+    case C (SH64PCREL16_64, UNDEF_SH64PCREL):
+    case C (SH64PCREL16PT_64, UNDEF_SH64PCREL):
+      /* Used to be to somewhere which was unknown.  */
+      if (sym_relaxable)
+	{
+	  int what = GET_WHAT (fragP->fr_subtype);
+
+	  /* In this segment, so head for shortest.  */
+	  fragP->fr_subtype = C (what, SH64PCREL16);
+	}
+      else
+	{
+	  int what = GET_WHAT (fragP->fr_subtype);
+	  /* We know the abs value, but we don't know where we will be
+	     linked, so we must make it the longest.  Presumably we could
+	     switch to a non-pcrel representation, but having absolute
+	     values in PT operands should be rare enough not to be worth
+	     adding that code.  */
+	  fragP->fr_subtype = C (what, SH64PCREL64);
+	}
+      fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
+      break;
+
+    case C (MOVI_IMM_64, UNDEF_MOVI):
+    case C (MOVI_IMM_32, UNDEF_MOVI):
+      exp = NULL;
+
+      /* Look inside the "symbol".  If we find a PC-relative expression,
+	 change this to a PC-relative, relaxable expression.  */
+      if (fragP->fr_symbol != NULL
+	  && (exp = symbol_get_value_expression (fragP->fr_symbol)) != NULL
+	  && exp->X_op == O_subtract
+	  && exp->X_op_symbol != NULL
+	  && S_GET_SEGMENT (exp->X_op_symbol) == segment_type)
+	{
+	  int what = GET_WHAT (fragP->fr_subtype);
+	  int what_high = what == MOVI_IMM_32 ? MOVI_32 : MOVI_64;
+	  expressionS *opexp
+	    = symbol_get_value_expression (exp->X_op_symbol);
+	  expressionS *addexp
+	    = symbol_get_value_expression (exp->X_add_symbol);
+
+	  /* Change the MOVI expression to the "X" in "X - Y" and subtract
+	     Y:s offset to this location from X.  Note that we can only
+	     allow an Y which is offset from this frag.  */
+	  if (opexp != NULL
+	      && addexp != NULL
+	      && opexp->X_op == O_constant
+	      && fragP == symbol_get_frag (exp->X_op_symbol))
+	    {
+	      /* At this point, before relaxing, the add-number of opexp
+		 is the offset from the fr_fix part.  */
+	      fragP->fr_offset
+		= (exp->X_add_number
+		   - (opexp->X_add_number - (fragP->fr_fix - 4)));
+	      fragP->fr_symbol = exp->X_add_symbol;
+
+	      what = what == MOVI_IMM_32
+		? MOVI_IMM_32_PCREL : MOVI_IMM_64_PCREL;
+
+	      /* Check the "X" symbol to estimate the size of this
+		 PC-relative expression.  */
+	      if (S_GET_SEGMENT (exp->X_add_symbol) == segment_type
+		  && ! S_IS_EXTERNAL (exp->X_add_symbol)
+		  && ! S_IS_WEAK (exp->X_add_symbol))
+		fragP->fr_subtype = C (what, MOVI_16);
+	      else
+		fragP->fr_subtype = C (what, what_high);
+
+	      /* This is now a PC-relative expression, fit to be relaxed.  */
+	    }
+	  else
+	    fragP->fr_subtype = C (what, what_high);
+	}
+      else if (fragP->fr_symbol == NULL
+	       || (S_GET_SEGMENT (fragP->fr_symbol) == absolute_section
+		   && exp->X_op == O_constant))
+	{
+	  unsigned long insn
+	    = (target_big_endian
+	       ? bfd_getb32 (fragP->fr_opcode)
+	       : bfd_getl32 (fragP->fr_opcode));
+	  offsetT one = (offsetT) 1;
+	  offsetT value = fragP->fr_offset
+	    + (fragP->fr_symbol == NULL ? 0 : S_GET_VALUE (fragP->fr_symbol));
+
+	  if (value >= ((offsetT) -1 << 15) && value < ((offsetT) 1 << 15))
+	    {
+	      /* Fits in 16-bit signed number.  */
+	      int what = GET_WHAT (fragP->fr_subtype);
+	      fragP->fr_subtype = C (what, MOVI_16);
+
+	      /* Just "or" in the value.  */
+	      md_number_to_chars (fragP->fr_opcode,
+				  insn | ((value & ((1 << 16) - 1)) << 10),
+				  4);
+	    }
+	  else if (value >= -(one << 31)
+		   && (value < (one << 31)
+		       || (sh64_abi == sh64_abi_32 && value < (one << 32))))
+	    {
+	      /* The value fits in a 32-bit signed number.  */
+	      int reg = (insn >> 4) & 0x3f;
+
+	      /* Just "or" in the high bits of the value, making the first
+		 MOVI.  */
+	      md_number_to_chars (fragP->fr_opcode,
+				  insn
+				  | (((value >> 16) & ((1 << 16) - 1)) << 10),
+				  4);
+
+	      /* Add a SHORI with the low bits.  Note that this insn lives
+		 in the variable fragment part.  */
+	      md_number_to_chars (fragP->fr_literal + old_fr_fix,
+				  SHMEDIA_SHORI_OPC
+				  | (reg << 4)
+				  | ((value & ((1 << 16) - 1)) << 10),
+				  4);
+
+	      /* We took a piece of the variable part.  */
+	      fragP->fr_fix += 4;
+	    }
+	  else if (GET_WHAT (fragP->fr_subtype) == MOVI_IMM_32)
+	    {
+	      /* Value out of range.  */
+	      as_bad_where (fragP->fr_file, fragP->fr_line,
+			    _("MOVI operand is not a 32-bit signed value: 0x%8x%08x"),
+			    ((unsigned int) (value >> 32)
+			     & (unsigned int) 0xffffffff),
+			    (unsigned int) value & (unsigned int) 0xffffffff);
+
+	      /* Must advance size, or we will get internal inconsistency
+		 and fall into an assert.  */
+	      fragP->fr_fix += 4;
+	    }
+	  /* Now we know we are allowed to expand to 48- and 64-bit values.  */
+	  else if (value >= -(one << 47) && value < (one << 47))
+	    {
+	      /* The value fits in a 48-bit signed number.  */
+	      int reg = (insn >> 4) & 0x3f;
+
+	      /* Just "or" in the high bits of the value, making the first
+		 MOVI.  */
+	      md_number_to_chars (fragP->fr_opcode,
+				  insn
+				  | (((value >> 32) & ((1 << 16) - 1)) << 10),
+				  4);
+
+	      /* Add a SHORI with the middle bits.  Note that this insn lives
+		 in the variable fragment part.  */
+	      md_number_to_chars (fragP->fr_literal + old_fr_fix,
+				  SHMEDIA_SHORI_OPC
+				  | (reg << 4)
+				  | (((value >> 16) & ((1 << 16) - 1)) << 10),
+				  4);
+
+	      /* Add a SHORI with the low bits.  */
+	      md_number_to_chars (fragP->fr_literal + old_fr_fix + 4,
+				  SHMEDIA_SHORI_OPC
+				  | (reg << 4)
+				  | ((value & ((1 << 16) - 1)) << 10),
+				  4);
+
+	      /* We took a piece of the variable part.  */
+	      fragP->fr_fix += 8;
+	    }
+	  else
+	    {
+	      /* A 64-bit number.  */
+	      int reg = (insn >> 4) & 0x3f;
+
+	      /* Just "or" in the high bits of the value, making the first
+		 MOVI.  */
+	      md_number_to_chars (fragP->fr_opcode,
+				  insn
+				  | (((value >> 48) & ((1 << 16) - 1)) << 10),
+				  4);
+
+	      /* Add a SHORI with the midhigh bits.  Note that this insn lives
+		 in the variable fragment part.  */
+	      md_number_to_chars (fragP->fr_literal + old_fr_fix,
+				  SHMEDIA_SHORI_OPC
+				  | (reg << 4)
+				  | (((value >> 32) & ((1 << 16) - 1)) << 10),
+				  4);
+
+	      /* Add a SHORI with the midlow bits.  */
+	      md_number_to_chars (fragP->fr_literal + old_fr_fix + 4,
+				  SHMEDIA_SHORI_OPC
+				  | (reg << 4)
+				  | (((value >> 16) & ((1 << 16) - 1)) << 10),
+				  4);
+
+	      /* Add a SHORI with the low bits.  */
+	      md_number_to_chars (fragP->fr_literal + old_fr_fix + 8,
+				  SHMEDIA_SHORI_OPC
+				  | (reg << 4)
+				  | ((value & ((1 << 16) - 1)) << 10), 4);
+	      /* We took all of the variable part.  */
+	      fragP->fr_fix += 12;
+	    }
+
+	  /* MOVI expansions that get here have not been converted to
+	     PC-relative frags, but instead expanded by
+	     md_number_to_chars or by calling shmedia_md_convert_frag
+	     with final == false.  We must not have them around as
+	     frags anymore; symbols would be prematurely evaluated
+	     when relaxing.  We will not need to have md_convert_frag
+	     called again with them; any further handling is through
+	     the already emitted fixups.  */
+	  frag_wane (fragP);
+	  break;
+	}
+      fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
+      break;
+
+      /* For relaxation states that remain unchanged, report the
+         estimated length.  */
+    case C (SH64PCREL16_32, SH64PCREL16):
+    case C (SH64PCREL16PT_32, SH64PCREL16):
+    case C (SH64PCREL16_32, SH64PCREL32):
+    case C (SH64PCREL16PT_32, SH64PCREL32):
+    case C (SH64PCREL16_32, SH64PCRELPLT):
+    case C (SH64PCREL16PT_32, SH64PCRELPLT):
+    case C (SH64PCREL16_64, SH64PCREL16):
+    case C (SH64PCREL16PT_64, SH64PCREL16):
+    case C (SH64PCREL16_64, SH64PCREL32):
+    case C (SH64PCREL16PT_64, SH64PCREL32):
+    case C (SH64PCREL16_64, SH64PCREL48):
+    case C (SH64PCREL16PT_64, SH64PCREL48):
+    case C (SH64PCREL16_64, SH64PCREL64):
+    case C (SH64PCREL16PT_64, SH64PCREL64):
+    case C (SH64PCREL16_64, SH64PCRELPLT):
+    case C (SH64PCREL16PT_64, SH64PCRELPLT):
+    case C (MOVI_IMM_32, MOVI_16):
+    case C (MOVI_IMM_32, MOVI_32):
+    case C (MOVI_IMM_32, MOVI_GOTOFF):
+    case C (MOVI_IMM_32_PCREL, MOVI_16):
+    case C (MOVI_IMM_32_PCREL, MOVI_32):
+    case C (MOVI_IMM_32_PCREL, MOVI_PLT):
+    case C (MOVI_IMM_32_PCREL, MOVI_GOTPC):
+    case C (MOVI_IMM_64, MOVI_16):
+    case C (MOVI_IMM_64, MOVI_32):
+    case C (MOVI_IMM_64, MOVI_48):
+    case C (MOVI_IMM_64, MOVI_64):
+    case C (MOVI_IMM_64, MOVI_GOTOFF):
+    case C (MOVI_IMM_64_PCREL, MOVI_16):
+    case C (MOVI_IMM_64_PCREL, MOVI_32):
+    case C (MOVI_IMM_64_PCREL, MOVI_48):
+    case C (MOVI_IMM_64_PCREL, MOVI_64):
+    case C (MOVI_IMM_64_PCREL, MOVI_PLT):
+    case C (MOVI_IMM_64_PCREL, MOVI_GOTPC):
+      fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
+      break;
+      
+    default:
+      abort ();
+    }
+
+  return fragP->fr_var + (fragP->fr_fix - old_fr_fix);
+}
+
+/* Parse an expression, SH64-style.  Copied from tc-sh.c, but with
+   datatypes adjusted.  */
+
+static char *
+shmedia_parse_exp (s, op)
+     char *s;
+     shmedia_operand_info *op;
+{
+  char *save;
+  char *new;
+
+  save = input_line_pointer;
+  input_line_pointer = s;
+  expression (&op->immediate);
+  if (op->immediate.X_op == O_absent)
+    as_bad (_("missing operand"));
+  new = input_line_pointer;
+  input_line_pointer = save;
+  return new;
+}
+
+/* Parse an operand.  Store pointer to next character in *PTR.  */
+
+static void
+shmedia_get_operand (ptr, op, argtype)
+     char **ptr;
+     shmedia_operand_info *op;
+     shmedia_arg_type argtype;
+{
+  char *src = *ptr;
+  int mode = -1;
+  unsigned int len;
+
+  len = shmedia_parse_reg (src, &mode, &(op->reg), argtype);
+  if (len)
+    {
+      *ptr = src + len;
+      op->type = mode;
+    }
+  else
+    {
+      /* Not a reg, so it must be a displacement.  */
+      *ptr = shmedia_parse_exp (src, op);
+      op->type = A_IMMM;
+
+      /* This is just an initialization; shmedia_get_operands will change
+	 as needed.  */
+      op->reloctype = BFD_RELOC_NONE;
+    }
+}
+
+/* Parse the operands for this insn; return NULL if invalid, else return
+   how much text was consumed.  */
+
+static char *
+shmedia_get_operands (info, args, operands)
+     shmedia_opcode_info *info;
+     char *args;
+     shmedia_operands_info *operands;
+{
+  char *ptr = args;
+  int i;
+
+  if (*ptr == ' ')
+    ptr++;
+
+  for (i = 0; info->arg[i] != 0; i++)
+    {
+      memset (operands->operands + i, 0, sizeof (operands->operands[0]));
+
+      /* No operand to get for these fields.  */
+      if (info->arg[i] == A_REUSE_PREV)
+	continue;
+
+      shmedia_get_operand (&ptr, &operands->operands[i], info->arg[i]);
+
+      /* Check operands type match.  */
+      switch (info->arg[i])
+	{
+	case A_GREG_M:
+	case A_GREG_N:
+	case A_GREG_D:
+	  if (operands->operands[i].type != A_GREG_M)
+	    return NULL;
+	  break;
+
+	case A_FREG_G:
+	case A_FREG_H:
+	case A_FREG_F:
+	  if (operands->operands[i].type != A_FREG_G)
+	    return NULL;
+	  break;
+
+	case A_FVREG_G:
+	case A_FVREG_H:
+	case A_FVREG_F:
+	  if (operands->operands[i].type != A_FVREG_G)
+	    return NULL;
+	  break;
+
+	case A_FMREG_G:
+	case A_FMREG_H:
+	case A_FMREG_F:
+	  if (operands->operands[i].type != A_FMREG_G)
+	    return NULL;
+	  break;
+
+	case A_FPREG_G:
+	case A_FPREG_H:
+	case A_FPREG_F:
+	  if (operands->operands[i].type != A_FPREG_G)
+	    return NULL;
+	  break;
+
+	case A_DREG_G:
+	case A_DREG_H:
+	case A_DREG_F:
+	  if (operands->operands[i].type != A_DREG_G)
+	    return NULL;
+	  break;
+
+	case A_TREG_A:
+	case A_TREG_B:
+	  if (operands->operands[i].type != A_TREG_B)
+	    return NULL;
+	  break;
+
+	case A_CREG_J:
+	case A_CREG_K:
+	  if (operands->operands[i].type != A_CREG_K)
+	    return NULL;
+	  break;
+
+	case A_IMMS16:
+	case A_IMMU16:
+	  /* Check for an expression that looks like S & 65535 or
+	     (S >> N) & 65535, where N = 0, 16, 32, 48.
+
+	     Get the S and put at operands->operands[i].immediate, and
+	     adjust operands->operands[i].reloctype.  */
+	  {
+	    expressionS *imm_expr = &operands->operands[i].immediate;
+	    expressionS *right_expr;
+
+	    if (operands->operands[i].type == A_IMMM
+		&& imm_expr->X_op == O_bit_and
+		&& imm_expr->X_op_symbol != NULL
+		&& ((right_expr
+		     = symbol_get_value_expression (imm_expr->X_op_symbol))
+		    ->X_op == O_constant)
+		&& right_expr->X_add_number == 0xffff)
+	      {
+		symbolS *inner = imm_expr->X_add_symbol;
+		bfd_reloc_code_real_type reloctype = BFD_RELOC_SH_IMM_LOW16;
+		expressionS *inner_expr
+		  = symbol_get_value_expression (inner);
+
+		if (inner_expr->X_op == O_right_shift)
+		  {
+		    expressionS *inner_right;
+
+		    if (inner_expr->X_op_symbol != NULL
+		      && ((inner_right
+			   = symbol_get_value_expression (inner_expr
+							  ->X_op_symbol))
+			  ->X_op == O_constant))
+		      {
+			offsetT addnum
+			  = inner_right->X_add_number;
+
+			if (addnum == 0 || addnum == 16 || addnum == 32
+			    || addnum == 48)
+			  {
+			    reloctype
+			      = (addnum == 0
+				 ? BFD_RELOC_SH_IMM_LOW16
+				 : (addnum == 16
+				    ? BFD_RELOC_SH_IMM_MEDLOW16
+				    : (addnum == 32
+				       ? BFD_RELOC_SH_IMM_MEDHI16
+				       : BFD_RELOC_SH_IMM_HI16)));
+
+			    inner = inner_expr->X_add_symbol;
+			    inner_expr = symbol_get_value_expression (inner);
+			  }
+		      }
+		  }
+
+		/* I'm not sure I understand the logic, but evidently the
+		   inner expression of a lone symbol is O_constant, with
+		   the actual symbol in expr_section.  For a constant, the
+		   section would be absolute_section.  For sym+offset,
+		   it's O_symbol as always.  See expr.c:make_expr_symbol,
+		   first statements.  */
+
+		if (inner_expr->X_op == O_constant
+		    && S_GET_SEGMENT (inner) != absolute_section)
+		  {
+		    operands->operands[i].immediate.X_op = O_symbol;
+		    operands->operands[i].immediate.X_add_symbol = inner;
+		    operands->operands[i].immediate.X_add_number = 0;
+		  }
+		else
+		  operands->operands[i].immediate
+		    = *symbol_get_value_expression (inner);
+
+		operands->operands[i].reloctype = reloctype;
+	      }
+	  }
+	  /* Fall through.  */
+	case A_IMMS6:
+	case A_IMMS6BY32:
+	case A_IMMS10:
+	case A_IMMS10BY1:
+	case A_IMMS10BY2:
+	case A_IMMS10BY4:
+	case A_IMMS10BY8:
+	case A_PCIMMS16BY4:
+	case A_PCIMMS16BY4_PT:
+	case A_IMMU5:
+	case A_IMMU6:
+	  if (operands->operands[i].type != A_IMMM)
+	    return NULL;
+
+	  if (sh_check_fixup (&operands->operands[i].immediate,
+			      &operands->operands[i].reloctype))
+	    {
+	      as_bad (_("invalid PIC reference"));
+	      return NULL;
+	    }
+
+	  break;
+
+	default:
+	  BAD_CASE (info->arg[i]);
+	}
+
+      if (*ptr == ',' && info->arg[i + 1])
+	ptr++;
+    }
+  return ptr;
+}
+
+
+/* Find an opcode at the start of *STR_P in the hash table, and set
+   *STR_P to the first character after the last one read.  */
+
+static shmedia_opcode_info *
+shmedia_find_cooked_opcode (str_p)
+     char **str_p;
+{
+  char *str = *str_p;
+  char *op_start;
+  char *op_end;
+  char name[20];
+  unsigned int nlen = 0;
+
+  /* Drop leading whitespace.  */
+  while (*str == ' ')
+    str++;
+
+  /* Find the op code end.  */
+  for (op_start = op_end = str;
+       *op_end
+       && nlen < sizeof (name) - 1
+       && ! is_end_of_line[(unsigned char) *op_end]
+       && ! ISSPACE ((unsigned char) *op_end);
+       op_end++)
+    {
+      unsigned char c = op_start[nlen];
+
+      /* The machine independent code will convert CMP/EQ into cmp/EQ
+	 because it thinks the '/' is the end of the symbol.  Moreover,
+	 all but the first sub-insn is a parallel processing insn won't
+	 be capitailzed.  Instead of hacking up the machine independent
+	 code, we just deal with it here.  */
+      c = TOLOWER (c);
+      name[nlen] = c;
+      nlen++;
+    }
+
+  name[nlen] = 0;
+  *str_p = op_end;
+
+  if (nlen == 0)
+    as_bad (_("can't find opcode"));
+
+  return
+    (shmedia_opcode_info *) hash_find (shmedia_opcode_hash_control, name);
+}
+
+/* Build up an instruction, including allocating the frag.  */
+
+static int
+shmedia_build_Mytes (opcode, operands)
+     shmedia_opcode_info *opcode;
+     shmedia_operands_info *operands;
+{
+  unsigned long insn = opcode->opcode_base;
+  int i, j;
+  char *insn_loc = frag_more (4);
+
+  /* The parameter to dwarf2_emit_insn is actually the offset to the start
+     of the insn from the fix piece of instruction that was emitted.
+     Since we want .debug_line addresses to record (address | 1) for
+     SHmedia insns, we get the wanted effect by taking one off the size,
+     knowing it's a multiple of 4.  We count from the first fix piece of
+     the insn.  There must be no frags changes (frag_more or frag_var)
+     calls in-between the frag_more call we account for, and this
+     dwarf2_emit_insn call.  */
+  dwarf2_emit_insn (3);
+
+  /* This is stored into any frag_var operand.  */
+  sh64_last_insn_frag = frag_now;
+
+  /* Loop over opcode info, emit an instruction.  */
+  for (i = 0, j = 0; opcode->arg[i]; i++)
+    {
+      shmedia_arg_type argtype = opcode->arg[i];
+      shmedia_operand_info *opjp = &operands->operands[j];
+      switch (argtype)
+	{
+	case A_TREG_A:
+	case A_TREG_B:
+	case A_GREG_M:
+	case A_GREG_N:
+	case A_GREG_D:
+	case A_FREG_G:
+	case A_FREG_H:
+	case A_FREG_F:
+	case A_FVREG_G:
+	case A_FVREG_H:
+	case A_FVREG_F:
+	case A_FMREG_G:
+	case A_FMREG_H:
+	case A_FMREG_F:
+	case A_FPREG_G:
+	case A_FPREG_H:
+	case A_FPREG_F:
+	case A_DREG_G:
+	case A_DREG_H:
+	case A_DREG_F:
+	case A_CREG_J:
+	case A_CREG_K:
+	  /* Six-bit register fields.  They just get filled with the
+	     parsed register number.  */
+	  insn |= (opjp->reg << opcode->nibbles[i]);
+	  j++;
+	  break;
+
+	case A_REUSE_PREV:
+	  /* Copy the register for the previous operand to this position.  */
+	  insn |= (operands->operands[j - 1].reg << opcode->nibbles[i]);
+	  j++;
+	  break;
+
+	case A_IMMS6:
+	  insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					BFD_RELOC_SH_IMMS6);
+	  j++;
+	  break;
+
+	case A_IMMS6BY32:
+	  insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					BFD_RELOC_SH_IMMS6BY32);
+	  j++;
+	  break;
+
+	case A_IMMS10BY1:
+	case A_IMMS10:
+	  insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					BFD_RELOC_SH_IMMS10);
+	  j++;
+	  break;
+
+	case A_IMMS10BY2:
+	  insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					BFD_RELOC_SH_IMMS10BY2);
+	  j++;
+	  break;
+
+	case A_IMMS10BY4:
+	  if (opjp->reloctype == BFD_RELOC_NONE)
+	    insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					  BFD_RELOC_SH_IMMS10BY4);
+	  else if (opjp->reloctype == BFD_RELOC_SH_GOTPLT32)
+	    insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					  BFD_RELOC_SH_GOTPLT10BY4);
+	  else if (opjp->reloctype == BFD_RELOC_32_GOT_PCREL)
+	    insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					  BFD_RELOC_SH_GOT10BY4);
+	  else
+	    as_bad (_("invalid PIC reference"));
+	  j++;
+	  break;
+
+	case A_IMMS10BY8:
+	  if (opjp->reloctype == BFD_RELOC_NONE)
+	    insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					  BFD_RELOC_SH_IMMS10BY8);
+	  else if (opjp->reloctype == BFD_RELOC_SH_GOTPLT32)
+	    insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					  BFD_RELOC_SH_GOTPLT10BY8);
+	  else if (opjp->reloctype == BFD_RELOC_32_GOT_PCREL)
+	    insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					  BFD_RELOC_SH_GOT10BY8);
+	  else
+	    as_bad (_("invalid PIC reference"));
+	  j++;
+	  break;
+
+	case A_IMMS16:
+	  /* Sneak a peek if this is the MOVI insn.  If so, check if we
+	     should expand it.  */
+	  if (opjp->reloctype == BFD_RELOC_32_GOT_PCREL)
+	    opjp->reloctype = BFD_RELOC_SH_GOT_LOW16;
+	  else if (opjp->reloctype == BFD_RELOC_SH_GOTPLT32)
+	    opjp->reloctype = BFD_RELOC_SH_GOTPLT_LOW16;
+	    
+	  if ((opjp->reloctype == BFD_RELOC_NONE
+	       || opjp->reloctype == BFD_RELOC_32_GOTOFF
+	       || opjp->reloctype == BFD_RELOC_32_PLT_PCREL
+	       || opjp->reloctype == BFD_RELOC_SH_GOTPC)
+	      && opcode->opcode_base == SHMEDIA_MOVI_OPC
+	      && (opjp->immediate.X_op != O_constant
+		  || opjp->immediate.X_add_number < -32768
+		  || opjp->immediate.X_add_number > 32767)
+	      && (sh64_expand
+		  || opjp->reloctype == BFD_RELOC_32_GOTOFF
+		  || opjp->reloctype == BFD_RELOC_32_PLT_PCREL
+		  || opjp->reloctype == BFD_RELOC_SH_GOTPC))
+	    {
+	      int what = sh64_abi == sh64_abi_64 ? MOVI_IMM_64 : MOVI_IMM_32;
+	      offsetT max = sh64_abi == sh64_abi_64 ? MOVI_64 : MOVI_32;
+	      offsetT min = MOVI_16;
+	      offsetT init = UNDEF_MOVI;
+	      valueT addvalue
+		= opjp->immediate.X_op_symbol != NULL
+		? 0 : opjp->immediate.X_add_number;
+	      symbolS *sym
+		= opjp->immediate.X_op_symbol != NULL
+		? make_expr_symbol (&opjp->immediate)
+		: opjp->immediate.X_add_symbol;
+
+	      if (opjp->reloctype == BFD_RELOC_32_GOTOFF)
+		init = max = min = MOVI_GOTOFF;
+	      else if (opjp->reloctype == BFD_RELOC_32_PLT_PCREL)
+		{
+		  init = max = min = MOVI_PLT;
+		  what = (sh64_abi == sh64_abi_64
+			  ? MOVI_IMM_64_PCREL
+			  : MOVI_IMM_32_PCREL);
+		}
+	      else if (opjp->reloctype == BFD_RELOC_SH_GOTPC)
+		{
+		  init = max = min = MOVI_GOTPC;
+		  what = (sh64_abi == sh64_abi_64
+			  ? MOVI_IMM_64_PCREL
+			  : MOVI_IMM_32_PCREL);
+		}
+
+	      frag_var (rs_machine_dependent,
+			md_relax_table[C (what, max)].rlx_length,
+			md_relax_table[C (what, min)].rlx_length,
+			C (what, init), sym, addvalue, insn_loc);
+	    }
+	  else
+	    insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					  (opjp->reloctype
+					   == BFD_RELOC_NONE)
+					  ? BFD_RELOC_SH_IMMS16
+					  : opjp->reloctype);
+	  j++;
+	  break;
+
+	case A_PCIMMS16BY4:
+	  {
+	    int what
+	      = ((sh64_abi == sh64_abi_64 && ! sh64_pt32)
+		 ? SH64PCREL16_64 : SH64PCREL16_32);
+	    offsetT max
+	      = ((sh64_abi == sh64_abi_64 && ! sh64_pt32)
+		 ? SH64PCREL64 : SH64PCREL32);
+	    offsetT min = SH64PCREL16;
+	    offsetT init = UNDEF_SH64PCREL;
+
+	    /* Don't allow complex expressions here.  */
+	    if (opjp->immediate.X_op_symbol != NULL)
+	      return 0;
+
+	    if (opjp->reloctype == BFD_RELOC_32_PLT_PCREL)
+	      init = max = min = SH64PCRELPLT;
+
+	    /* If we're not expanding, then just emit a fixup.  */
+	    if (sh64_expand || opjp->reloctype != BFD_RELOC_NONE)
+	      frag_var (rs_machine_dependent,
+			md_relax_table[C (what, max)].rlx_length,
+			md_relax_table[C (what, min)].rlx_length,
+			C (what, init),
+			opjp->immediate.X_add_symbol,
+			opjp->immediate.X_add_number,
+			insn_loc);
+	    else
+	      insn |= shmedia_immediate_op (insn_loc, opjp, 1,
+					    opjp->reloctype == BFD_RELOC_NONE
+					    ? BFD_RELOC_SH_PT_16
+					    : opjp->reloctype);
+	      
+	    j++;
+	    break;
+	  }
+
+	case A_PCIMMS16BY4_PT:
+	  {
+	    int what
+	      = ((sh64_abi == sh64_abi_64 && ! sh64_pt32)
+		 ? SH64PCREL16PT_64 : SH64PCREL16PT_32);
+	    offsetT max
+	      = ((sh64_abi == sh64_abi_64 && ! sh64_pt32)
+		 ? SH64PCREL64 : SH64PCREL32);
+	    offsetT min = SH64PCREL16;
+	    offsetT init = UNDEF_SH64PCREL;
+
+	    /* Don't allow complex expressions here.  */
+	    if (opjp->immediate.X_op_symbol != NULL)
+	      return 0;
+
+	    if (opjp->reloctype == BFD_RELOC_32_PLT_PCREL)
+	      init = max = min = SH64PCRELPLT;
+
+	    /* If we're not expanding, then just emit a fixup.  */
+	    if (sh64_expand || opjp->reloctype != BFD_RELOC_NONE)
+	      frag_var (rs_machine_dependent,
+			md_relax_table[C (what, max)].rlx_length,
+			md_relax_table[C (what, min)].rlx_length,
+			C (what, init),
+			opjp->immediate.X_add_symbol,
+			opjp->immediate.X_add_number,
+			insn_loc);
+	    else
+	      /* This reloc-type is just temporary, so we can distinguish
+		 PTA from PT.  It is changed in shmedia_md_apply_fix3 to
+		 BFD_RELOC_SH_PT_16.  */
+	      insn |= shmedia_immediate_op (insn_loc, opjp, 1,
+					    opjp->reloctype == BFD_RELOC_NONE
+					    ? SHMEDIA_BFD_RELOC_PT
+					    : opjp->reloctype);
+	      
+	    j++;
+	    break;
+	  }
+
+	case A_IMMU5:
+	  insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					BFD_RELOC_SH_IMMU5);
+	  j++;
+	  break;
+
+	case A_IMMU6:
+	  insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					BFD_RELOC_SH_IMMU6);
+	  j++;
+	  break;
+
+	case A_IMMU16:
+	  insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+					(opjp->reloctype
+					 == BFD_RELOC_NONE)
+					? BFD_RELOC_SH_IMMU16
+					: opjp->reloctype);
+	  j++;
+	  break;
+
+	default:
+	  BAD_CASE (argtype);
+	}
+    }
+
+  md_number_to_chars (insn_loc, insn, 4);
+  return 4;
+}
+
+/* Assemble a SHmedia instruction.  */
+
+static void
+shmedia_md_assemble (str)
+     char *str;
+{
+  char *op_end;
+  shmedia_opcode_info *opcode;
+  shmedia_operands_info operands;
+  int size;
+
+  opcode = shmedia_find_cooked_opcode (&str);
+  op_end = str;
+
+  if (opcode == NULL)
+    {
+      as_bad (_("unknown opcode"));
+      return;
+    }
+
+  /* Start a SHmedia code region, if there has been pseudoinsns or similar
+     seen since the last one.  */
+  if (seen_insn == false)
+    {
+      sh64_update_contents_mark (true);
+      sh64_set_contents_type (CRT_SH5_ISA32);
+      seen_insn = true;
+    }
+
+  op_end = shmedia_get_operands (opcode, op_end, &operands);
+
+  if (op_end == NULL)
+    {
+      as_bad (_("invalid operands to %s"), opcode->name);
+      return;
+    }
+
+  if (*op_end)
+    {
+      as_bad (_("excess operands to %s"), opcode->name);
+      return;
+    }
+
+  size = shmedia_build_Mytes (opcode, &operands);
+  if (size == 0)
+    return;
+}
+
+/* Hook called from md_begin in tc-sh.c.  */
+
+void
+shmedia_md_begin ()
+{
+  const shmedia_opcode_info *shmedia_opcode;
+  shmedia_opcode_hash_control = hash_new ();
+
+  /* Create opcode table for SHmedia mnemonics.  */
+  for (shmedia_opcode = shmedia_table;
+       shmedia_opcode->name;
+       shmedia_opcode++)
+    hash_insert (shmedia_opcode_hash_control, shmedia_opcode->name,
+		 (char *) shmedia_opcode);
+}
+
+/* Switch instruction set.  Only valid if one of the --isa or --abi
+   options was specified.  */
+
+static void
+s_sh64_mode (ignore)
+    int ignore ATTRIBUTE_UNUSED;
+{
+  char *name = input_line_pointer, ch;
+
+  /* Make sure data up to this location is handled according to the
+     previous ISA.  */
+  sh64_update_contents_mark (true);
+
+  while (!is_end_of_line[(unsigned char) *input_line_pointer])
+    input_line_pointer++;
+  ch = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  /* If the mode was not set before, explicitly or implicitly, then we're
+     not emitting SH64 code, so this pseudo is invalid.  */
+  if (sh64_isa_mode == sh64_isa_unspecified)
+    as_bad (_("The `.mode %s' directive is not valid with this architecture"),
+	    name);
+
+  if (strcasecmp (name, "shcompact") == 0)
+    sh64_isa_mode = sh64_isa_shcompact;
+  else if (strcasecmp (name, "shmedia") == 0)
+    sh64_isa_mode = sh64_isa_shmedia;
+  else
+    as_bad (_("Invalid argument to .mode: %s"), name);
+
+  /* Make a new frag, marking it with the supposedly-changed ISA.  */
+  frag_wane (frag_now);
+  frag_new (0);
+
+  /* Contents type up to this new point is the same as before; don't add a
+     data region just because the new frag we created.  */
+  sh64_update_contents_mark (false);
+
+  *input_line_pointer = ch;
+  demand_empty_rest_of_line ();
+}
+
+/* Check that the right ABI is used.  Only valid if one of the --isa or
+   --abi options was specified.  */
+
+static void
+s_sh64_abi (ignore)
+    int ignore ATTRIBUTE_UNUSED;
+{
+  char *name = input_line_pointer, ch;
+
+  while (!is_end_of_line[(unsigned char) *input_line_pointer])
+    input_line_pointer++;
+  ch = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  /* If the mode was not set before, explicitly or implicitly, then we're
+     not emitting SH64 code, so this pseudo is invalid.  */
+  if (sh64_abi == sh64_abi_unspecified)
+    as_bad (_("The `.abi %s' directive is not valid with this architecture"),
+	    name);
+
+  if (strcmp (name, "64") == 0)
+    {
+      if (sh64_abi != sh64_abi_64)
+	as_bad (_("`.abi 64' but command-line options do not specify 64-bit ABI"));
+    }
+  else if (strcmp (name, "32") == 0)
+    {
+      if (sh64_abi != sh64_abi_32)
+	as_bad (_("`.abi 32' but command-line options do not specify 32-bit ABI"));
+    }
+  else
+    as_bad (_("Invalid argument to .abi: %s"), name);
+
+  *input_line_pointer = ch;
+  demand_empty_rest_of_line ();
+}
+
+/* This function is the first target-specific function called after
+   parsing command-line options.  Therefore we set default values from
+   command-line options here and do some sanity checking we couldn't do
+   when options were being parsed.  */
+
+const char *
+sh64_target_format ()
+{
+#ifdef TE_LINUX
+  return "FIXME: No linux target yet";
+#endif
+
+  if (sh64_abi == sh64_abi_64 && sh64_isa_mode == sh64_isa_unspecified)
+    sh64_isa_mode = sh64_isa_shmedia;
+
+  if (sh64_abi == sh64_abi_32 && sh64_isa_mode == sh64_isa_unspecified)
+    sh64_isa_mode = sh64_isa_shcompact;
+
+  if (sh64_isa_mode == sh64_isa_shcompact
+      && sh64_abi == sh64_abi_unspecified)
+    sh64_abi = sh64_abi_32;
+
+  if (sh64_isa_mode == sh64_isa_shmedia
+      && sh64_abi == sh64_abi_unspecified)
+    sh64_abi = sh64_abi_64;
+
+  if (sh64_isa_mode == sh64_isa_unspecified && ! sh64_mix)
+    as_bad (_("-no-mix is invalid without specifying SHcompact or SHmedia"));
+
+  if ((sh64_isa_mode == sh64_isa_unspecified
+       || sh64_isa_mode == sh64_isa_shmedia)
+      && sh64_shcompact_const_crange)
+    as_bad (_("-shcompact-const-crange is invalid without SHcompact"));
+
+  if (sh64_pt32 && sh64_abi != sh64_abi_64)
+    as_bad (_("-expand-pt32 only valid with -abi=64"));
+
+  if (! sh64_expand && sh64_isa_mode == sh64_isa_unspecified)
+    as_bad (_("-no-expand only valid with SHcompact or SHmedia"));
+
+  if (sh64_pt32 && ! sh64_expand)
+    as_bad (_("-expand-pt32 invalid together with -no-expand"));
+
+  /* When the ISA is not one of SHmedia or SHcompact, use the old SH
+     object format.  */
+  if (sh64_isa_mode == sh64_isa_unspecified)
+    return (target_big_endian ? "elf32-sh" : "elf32-shl");
+  else if (sh64_abi == sh64_abi_64)
+    return (target_big_endian ? "elf64-sh64" : "elf64-sh64l");
+  else
+    return (target_big_endian ? "elf32-sh64" : "elf32-sh64l");
+}
+
+/* The worker function of TARGET_MACH.  */
+
+int
+sh64_target_mach ()
+{
+  /* We need to explicitly set bfd_mach_sh5 instead of the default 0.  But
+     we only do this for the 64-bit ABI: if we do it for the 32-bit ABI,
+     the SH5 info in the bfd_arch_info structure will be selected.
+     However correct, as the machine has 64-bit addresses, functions
+     expected to emit 32-bit data for addresses will start failing.  For
+     example, the dwarf2dbg.c functions will emit 64-bit debugging format,
+     and we don't want that in the 32-bit ABI.
+
+     We could have two bfd_arch_info structures for SH64; one for the
+     32-bit ABI and one for the rest (64-bit ABI).  But that would be a
+     bigger kludge: it's a flaw in the BFD design, and we need to just
+     work around it by having the default machine set here in the
+     assembler.  For everything else but the assembler, the various bfd
+     functions will set the machine type right to bfd_mach_sh5 from object
+     file header flags regardless of the 0 here.  */
+
+  return (sh64_abi == sh64_abi_64) ? bfd_mach_sh5 : 0;
+}
+
+/* This is MD_PCREL_FROM_SECTION, we we define so it is called instead of
+   md_pcrel_from (in tc-sh.c).  */
+
+valueT
+shmedia_md_pcrel_from_section (fixP, sec)
+     struct fix *fixP;
+     segT sec ATTRIBUTE_UNUSED;
+{
+  know (fixP->fx_frag->fr_type == rs_machine_dependent);
+
+  /* Use the ISA for the instruction to decide which offset to use.  We
+     can glean it from the fisup type.  */
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_SH_IMM_LOW16:
+    case BFD_RELOC_SH_IMM_MEDLOW16:
+    case BFD_RELOC_SH_IMM_MEDHI16:
+    case BFD_RELOC_SH_IMM_HI16:
+    case BFD_RELOC_SH_IMM_LOW16_PCREL:
+    case BFD_RELOC_SH_IMM_MEDLOW16_PCREL:
+    case BFD_RELOC_SH_IMM_MEDHI16_PCREL:
+    case BFD_RELOC_SH_IMM_HI16_PCREL:
+    case BFD_RELOC_SH_IMMU5:
+    case BFD_RELOC_SH_IMMU6:
+    case BFD_RELOC_SH_IMMS6:
+    case BFD_RELOC_SH_IMMS10:
+    case BFD_RELOC_SH_IMMS10BY2:
+    case BFD_RELOC_SH_IMMS10BY4:
+    case BFD_RELOC_SH_IMMS10BY8:
+    case BFD_RELOC_SH_IMMS16:
+    case BFD_RELOC_SH_IMMU16:
+    case BFD_RELOC_SH_PT_16:
+    case SHMEDIA_BFD_RELOC_PT:
+      /* PC-relative relocs are relative to the address of the last generated
+	 instruction, i.e. fx_size - 4.  */
+      return SHMEDIA_MD_PCREL_FROM_FIX (fixP);
+
+    case BFD_RELOC_64:
+    case BFD_RELOC_64_PCREL:
+      know (0 /* Shouldn't get here.  */);
+      break;
+
+    default:
+      /* If section was SHcompact, use its function.  */
+      return (valueT) md_pcrel_from_section (fixP, sec);
+    }
+
+  know (0 /* Shouldn't get here.  */);
+  return 0;
+}
+
+/* Create one .cranges descriptor from two symbols, STARTSYM marking begin
+   and ENDSYM marking end, and CR_TYPE specifying the type.  */
+
+static void
+sh64_emit_crange (startsym, endsym, cr_type)
+     symbolS *startsym;
+     symbolS *endsym;
+     enum sh64_elf_cr_type cr_type;
+{
+  expressionS exp;
+  segT current_seg = now_seg;
+  subsegT current_subseg = now_subseg;
+
+  asection *cranges
+    = bfd_make_section_old_way (stdoutput,
+				SH64_CRANGES_SECTION_NAME);
+
+  /* Temporarily change to the .cranges section.  */
+  subseg_set (cranges, 0);
+
+  /* Emit the cr_addr part.  */
+  exp.X_op = O_symbol;
+  exp.X_add_number = 0;
+  exp.X_op_symbol = NULL;
+  exp.X_add_symbol = startsym;
+  emit_expr (&exp, 4);
+
+  /* Emit the cr_size part.  */
+  exp.X_op = O_subtract;
+  exp.X_add_number = 0;
+  exp.X_add_symbol = endsym;
+  exp.X_op_symbol = startsym;
+  emit_expr (&exp, 4);
+
+  /* Emit the cr_size part. */
+  exp.X_op = O_constant;
+  exp.X_add_number = cr_type;
+  exp.X_add_symbol = NULL;
+  exp.X_op_symbol = NULL;
+  emit_expr (&exp, 2);
+
+  /* Now back to our regular program.  */
+  subseg_set (current_seg, current_subseg);
+}
+
+/* Called when the assembler is about to emit contents of some type into
+   SEG, so it is *known* that the type of that new contents is in
+   NEW_CONTENTS_TYPE.  If just switching back and forth between different
+   contents types (for example, with consecutive .mode pseudos), then this
+   function isn't called.  */
+
+static void
+sh64_set_contents_type (new_contents_type)
+     enum sh64_elf_cr_type new_contents_type;
+{
+  segment_info_type *seginfo;
+
+  /* We will not be called when emitting .cranges output, since callers
+     stop that.  Validize that assumption.  */
+  know (emitting_crange == false);
+
+  seginfo = seg_info (now_seg);
+
+  if (seginfo)
+    {
+      symbolS *symp = seginfo->tc_segment_info_data.last_contents_mark;
+
+      enum sh64_elf_cr_type contents_type
+	= seginfo->tc_segment_info_data.contents_type;
+
+      /* If it was just SHcompact switching between code and constant
+	 pool, don't change contents type.  Just make sure we don't set
+	 the contents type to data, as that would join with a data-region
+	 in SHmedia mode.  */
+      if (sh64_isa_mode == sh64_isa_shcompact
+	  && ! sh64_shcompact_const_crange)
+	new_contents_type = CRT_SH5_ISA16;
+
+      /* If nothing changed, stop here.  */
+      if (contents_type == new_contents_type)
+	return;
+
+      /* If we're in 64-bit ABI mode, we do not emit .cranges, as it is
+	 only specified for 32-bit addresses.  It could presumably be
+	 extended, but in 64-bit ABI mode we don't have SHcompact code, so
+	 we would only use it to mark code and data.  */
+      if (sh64_abi == sh64_abi_64)
+	{
+	  /* Make the code type "sticky".  We don't want to set the
+	     sections contents type to data if there's any code in it as
+	     we don't have .cranges in 64-bit mode to notice the
+	     difference.  */
+	  seginfo->tc_segment_info_data.contents_type
+	    = (new_contents_type == CRT_SH5_ISA32
+	       || contents_type == CRT_SH5_ISA32)
+	    ? CRT_SH5_ISA32 : new_contents_type;
+	  return;
+	}
+
+      /* If none was marked, create a start symbol for this range and
+	 perhaps as a closing symbol for the old one.  */
+      if (symp == NULL)
+	symp = symbol_new (FAKE_LABEL_NAME, now_seg, (valueT) frag_now_fix (),
+			   frag_now);
+
+      /* We will use this symbol, so don't leave a pointer behind.  */
+      seginfo->tc_segment_info_data.last_contents_mark = NULL;
+
+      /* We'll be making only datalabel references to it, if we emit a
+	 .cranges descriptor, so remove any code flag.  */
+      S_SET_OTHER (symp, S_GET_OTHER (symp) & ~STO_SH5_ISA32);
+
+      /* If we have already marked the start of a range, we need to close
+	 and emit it before marking a new one, so emit a new .cranges
+	 descriptor into the .cranges section.  */
+      if (seginfo->tc_segment_info_data.mode_start_symbol)
+	{
+	  /* If we're not supposed to emit mixed-mode sections, make it an
+	     error, but continue processing.  */
+	  if (! sh64_mix
+	      && (new_contents_type == CRT_SH5_ISA32
+		  || contents_type == CRT_SH5_ISA32))
+	    as_bad (
+_("SHmedia code not allowed in same section as constants and SHcompact code"));
+
+	  emitting_crange = true;
+	  sh64_emit_crange (seginfo->tc_segment_info_data.mode_start_symbol,
+			    symp, contents_type);
+	  emitting_crange = false;
+	  seginfo->tc_segment_info_data.emitted_ranges++;
+	}
+
+      seginfo->tc_segment_info_data.mode_start_symbol = symp;
+      seginfo->tc_segment_info_data.mode_start_subseg = now_subseg;
+      seginfo->tc_segment_info_data.contents_type = new_contents_type;
+
+      /* Always reset this, so the SHcompact code will emit a reloc when
+	 it prepares to relax.  */
+      seginfo->tc_segment_info_data.in_code = 0;
+    }
+  else
+    as_bad (_("No segment info for current section"));
+}
+
+/* Hook when defining symbols and labels.  We set the ST_OTHER field if
+   the symbol is "shmedia" (with "bitor 1" automatically applied).  Simple
+   semantics for a label being "shmedia" : It was defined when .mode
+   SHmedia was in effect, and it was defined in a code section.  It
+   doesn't matter whether or not an assembled opcode is nearby.  */
+
+void
+sh64_frob_label (symp)
+     symbolS *symp;
+{
+  segT seg = S_GET_SEGMENT (symp);
+  static const symbolS *null = NULL;
+
+  /* Reset the tc marker for all newly created symbols.  */
+  symbol_set_tc (symp, (symbolS **) &null);
+
+  if (seg != NULL && sh64_isa_mode == sh64_isa_shmedia && subseg_text_p (seg))
+    S_SET_OTHER (symp, S_GET_OTHER (symp) | STO_SH5_ISA32);
+}
+
+/* Handle the "datalabel" qualifier.  We need to call "operand", but it's
+   static, so a function pointer is passed here instead.  FIXME: A target
+   hook for qualifiers is needed; we currently use the md_parse_name
+   symbol hook.  */
+
+int
+sh64_consume_datalabel (name, exp, cp, operandf)
+     const char *name;
+     expressionS *exp;
+     char *cp;
+     segT (*operandf) PARAMS ((expressionS *));
+{
+  static int parsing_datalabel = 0;
+
+  if (strcasecmp (name, "datalabel") == 0)
+    {
+      int save_parsing_datalabel = parsing_datalabel;
+
+      if (parsing_datalabel)
+	as_bad (_("duplicate datalabel operator ignored"));
+
+      *input_line_pointer = *cp;
+      parsing_datalabel = 1;
+      (*operandf) (exp);
+      parsing_datalabel = save_parsing_datalabel;
+
+      if (exp->X_op == O_symbol || exp->X_op == O_PIC_reloc)
+	{
+	  symbolS *symp = exp->X_add_symbol;
+	  segT symseg = S_GET_SEGMENT (symp);
+
+	  /* If the symbol is defined to something that is already a
+	     datalabel, we don't need to bother with any special handling.  */
+	  if (symseg != undefined_section
+	      && S_GET_OTHER (symp) != STO_SH5_ISA32)
+	    /* Do nothing.  */
+	    ;
+	  else
+	    {
+	      symbolS *dl_symp;
+	      const char *name = S_GET_NAME (symp);
+	      char *dl_name
+		= xmalloc (strlen (name) + sizeof (DATALABEL_SUFFIX));
+
+	      /* Now we copy the datalabel-qualified symbol into a symbol
+		 with the same name, but with " DL" appended.  We mark the
+		 symbol using the TC_SYMFIELD_TYPE field with a pointer to
+		 the main symbol, so we don't have to inspect all symbol
+		 names.  Note that use of "datalabel" is not expected to
+		 be a common case.  */
+	      strcpy (dl_name, name);
+	      strcat (dl_name, DATALABEL_SUFFIX);
+
+	      /* A FAKE_LABEL_NAME marks "$" or ".".  There can be any
+		 number of them and all have the same (faked) name; we
+		 must make a new one each time.  */
+	      if (strcmp (name, FAKE_LABEL_NAME) == 0)
+		dl_symp = symbol_make (dl_name);
+	      else
+		dl_symp = symbol_find_or_make (dl_name);
+
+	      free (dl_name);
+	      symbol_set_value_expression (dl_symp,
+					   symbol_get_value_expression (symp));
+	      S_SET_SEGMENT (dl_symp, symseg);
+	      symbol_set_frag (dl_symp, symbol_get_frag (symp));
+	      symbol_set_tc (dl_symp, &symp);
+	      copy_symbol_attributes (dl_symp, symp);
+	      exp->X_add_symbol = dl_symp;
+
+	      /* Unset the BranchTarget mark that can be set at symbol
+		 creation or attributes copying. */
+	      S_SET_OTHER (dl_symp, S_GET_OTHER (dl_symp) & ~STO_SH5_ISA32);
+
+	      /* The GLOBAL and WEAK attributes are not copied over by
+		 copy_symbol_attributes.  Do it here. */
+	      if (S_IS_WEAK (symp))
+		S_SET_WEAK (dl_symp);
+	      else if (S_IS_EXTERNAL (symp))
+		S_SET_EXTERNAL (dl_symp);
+	    }
+	}
+      /* Complain about other types of operands than symbol, unless they
+	 have already been complained about.  A constant is always a
+	 datalabel.  Removing the low bit would therefore be wrong.
+	 Complaining about it would also be wrong.  */
+      else if (exp->X_op != O_illegal
+	       && exp->X_op != O_absent
+	       && exp->X_op != O_constant)
+	as_bad (_("Invalid DataLabel expression"));
+
+      *cp = *input_line_pointer;
+
+      return 1;
+    }
+
+  return sh_parse_name (name, exp, cp);
+}
+
+/* This function is called just before symbols are being output.  It
+   returns zero when a symbol must be output, non-zero otherwise.
+   Datalabel references that were fully resolved to local symbols are not
+   necessary to output.  We also do not want to output undefined symbols
+   that are not used in relocs.  For symbols that are used in a reloc, it
+   does not matter what we set here.  If it is *not* used in a reloc, then
+   it was probably the datalabel counterpart that was used in a reloc;
+   then we need not output the main symbol.  */
+
+int
+sh64_exclude_symbol (symp)
+     symbolS *symp;
+{
+  symbolS *main_symbol = *symbol_get_tc (symp);
+
+  return main_symbol != NULL || ! S_IS_DEFINED (symp);
+}
+
+/* If we haven't seen an insn since the last update, and location
+   indicators have moved (a new frag, new location within frag) we have
+   emitted data, so change contents type to data.  Forget that we have
+   seen a sequence of insns and store the current location so we can mark
+   a new region if needed.  */
+
+static void
+sh64_update_contents_mark (update_type)
+     boolean update_type;
+{
+  segment_info_type *seginfo;
+  seginfo = seg_info (now_seg);
+
+  if (seginfo != NULL)
+    {
+      symbolS *symp = seginfo->tc_segment_info_data.last_contents_mark;
+
+      if (symp == NULL)
+	{
+	  symp = symbol_new (FAKE_LABEL_NAME, now_seg,
+			     (valueT) frag_now_fix (), frag_now);
+	  seginfo->tc_segment_info_data.last_contents_mark = symp;
+	}
+      else
+	{
+	  /* If we have moved location since last flush, we need to emit a
+	     data range.  The previous contents type ended at the location
+	     of the last update.  */
+	  if ((S_GET_VALUE (symp) != frag_now_fix ()
+	       || symbol_get_frag (symp) != frag_now))
+	    {
+	      enum sh64_elf_cr_type contents_type
+		= seginfo->tc_segment_info_data.contents_type;
+
+	      if (update_type
+		  && contents_type != CRT_DATA
+		  && contents_type != CRT_NONE
+		  && ! seen_insn)
+		{
+		  sh64_set_contents_type (CRT_DATA);
+		  symp = seginfo->tc_segment_info_data.last_contents_mark;
+		}
+
+	      /* If the symbol wasn't used up to make up a new range
+		 descriptor, update it to this new location.  */
+	      if (symp)
+		{
+		  S_SET_VALUE (symp, (valueT) frag_now_fix ());
+		  symbol_set_frag (symp, frag_now);
+		}
+	    }
+	}
+    }
+
+  seen_insn = false;
+}
+
+/* Called when the assembler is about to output some data, or maybe it's
+   just switching segments.  */
+
+void
+sh64_flush_pending_output ()
+{
+  sh64_update_contents_mark (true);
+  sh_flush_pending_output ();
+}
+
+/* Flush out the last crange descriptor after all insns have been emitted.  */
+
+static void
+sh64_flush_last_crange (abfd, seg, countparg)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     asection *seg;
+     PTR countparg ATTRIBUTE_UNUSED;
+{
+  segment_info_type *seginfo;
+
+  seginfo = seg_info (seg);
+
+  if (seginfo
+      /* Only emit .cranges descriptors if we would make it more than one.  */
+      && seginfo->tc_segment_info_data.emitted_ranges != 0)
+    {
+      symbolS *symp;
+
+      /* We need a closing symbol, so switch to the indicated section and
+	 emit it.  */
+
+      /* Change to the section we're about to handle.  */
+      subseg_set (seg, seginfo->tc_segment_info_data.mode_start_subseg);
+
+      symp = symbol_new (FAKE_LABEL_NAME, now_seg, (valueT) frag_now_fix (),
+			 frag_now);
+
+      /* We'll be making a datalabel reference to it, so remove any code
+         flag.  */
+      S_SET_OTHER (symp, S_GET_OTHER (symp) & ~STO_SH5_ISA32);
+
+      sh64_emit_crange (seginfo->tc_segment_info_data.mode_start_symbol,
+			symp,
+			seginfo->tc_segment_info_data.contents_type);
+    }
+}
+
+/* If and only if we see a call to md_number_to_chars without flagging the
+   start of an insn, we set the contents type to CRT_DATA, and only when
+   in SHmedia mode.  Note that by default we don't bother changing when
+   going from SHcompact to data, as the constant pools in GCC-generated
+   SHcompact code would create an inordinate amount of .cranges
+   descriptors.  */
+
+static void
+sh64_flag_output ()
+{
+  if (sh64_isa_mode != sh64_isa_unspecified
+      && seen_insn == false
+      && sh64_end_of_assembly == false
+      && ! emitting_crange)
+    {
+      md_flush_pending_output ();
+      sh64_set_contents_type (CRT_DATA);
+    }
+}
+
+/* Vtables don't need "datalabel" but we allow it by simply deleting
+   any we find. */
+
+static char *
+strip_datalabels ()
+{
+  char *src, *dest, *start=input_line_pointer;
+
+  for (src=input_line_pointer, dest=input_line_pointer; *src != '\n'; )
+    {
+      if (strncasecmp (src, "datalabel", 9) == 0
+	  && ISSPACE (src[9])
+	  && (src == start || !(ISALNUM (src[-1])) || src[-1] == '_'))
+	src += 10;
+      else
+	*dest++ = *src++;
+    }
+
+  if (dest < src)
+    *dest = '\n';
+  return src + 1;
+}
+
+static void
+sh64_vtable_entry (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  char *eol = strip_datalabels ();
+
+  obj_elf_vtable_entry (0);
+  input_line_pointer = eol;
+}
+
+static void
+sh64_vtable_inherit (ignore)
+     int ignore ATTRIBUTE_UNUSED;
+{
+  char *eol = strip_datalabels ();
+
+  obj_elf_vtable_inherit (0);
+  input_line_pointer = eol;
+}
Index: gas/config/tc-sh64.h
===================================================================
RCS file: gas/config/tc-sh64.h
diff -N gas/config/tc-sh64.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gas/config/tc-sh64.h 2 Feb 2002 04:00:30 -0000
@@ -0,0 +1,212 @@
+/* This file is tc-sh64.h
+   Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to
+   the Free Software Foundation, 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#define TC_SH64
+#include "config/tc-sh.h"
+#include "elf/sh.h"
+
+/* We need to override the tc-sh.h settings of HANDLE_ALIGN and
+   MAX_MEM_FOR_RS_ALIGN_CODE; we might need to put in SHmedia NOP:s, not
+   SHcompact NOP:s.  */
+#undef  HANDLE_ALIGN
+#define HANDLE_ALIGN(frag) sh64_handle_align (frag)
+extern void sh64_handle_align PARAMS ((fragS *));
+
+#undef  MAX_MEM_FOR_RS_ALIGN_CODE
+#define MAX_MEM_FOR_RS_ALIGN_CODE sh64_max_mem_for_rs_align_code ()
+extern int sh64_max_mem_for_rs_align_code PARAMS ((void));
+
+#undef  LISTING_HEADER
+#define LISTING_HEADER					\
+  (target_big_endian ?					\
+     "Hitachi SHcompact/SHmedia Big Endian GAS"		\
+   : "Hitachi SHcompact/SHmedia Little Endian GAS")
+
+/* We need to record the new frag position after an .align.  */
+extern void sh64_do_align PARAMS ((int, const char *, int, int));
+#define md_do_align(n, fill, len, max, l) \
+ do { sh64_do_align (n, fill, len, max); goto l; } while (0)
+
+struct sh64_segment_info_type
+{
+  /* The type of the section is initialized when the range_start_symbol
+     member is non-NULL.  */
+  symbolS *mode_start_symbol;
+  subsegT mode_start_subseg;
+
+  /* A stored symbol indicating location of last call of
+     "md_flush_pending_output".  It is NULLed when we actually use it;
+     otherwise the contents is just filled in with segment, frag and
+     offset within frag.  */
+  symbolS *last_contents_mark;
+
+  unsigned int emitted_ranges;
+  enum sh64_elf_cr_type contents_type;
+
+  /* This is used by the SH1-4 parts; we set it to 0 for SHmedia code and
+     data.  */
+  unsigned int in_code : 1;
+};
+
+#undef  TC_SEGMENT_INFO_TYPE
+#define TC_SEGMENT_INFO_TYPE struct sh64_segment_info_type
+
+#undef  TARGET_FORMAT
+#define TARGET_FORMAT sh64_target_format ()
+extern const char *sh64_target_format PARAMS ((void));
+
+#define TARGET_MACH sh64_target_mach ()
+extern int sh64_target_mach PARAMS ((void));
+
+#undef TC_RELOC_RTSYM_LOC_FIXUP
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX)				\
+  ((FIX)->fx_r_type != BFD_RELOC_32_PLT_PCREL			\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_PLT_LOW16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_PLT_MEDLOW16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_PLT_MEDHI16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_PLT_HI16			\
+   && (FIX)->fx_r_type != BFD_RELOC_32_GOT_PCREL		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOT_LOW16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOT_MEDLOW16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOT_MEDHI16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOT_HI16			\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOT10BY4			\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOT10BY8			\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPLT32			\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPLT_LOW16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPLT_MEDLOW16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPLT_MEDHI16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPLT_HI16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPLT10BY4		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPLT10BY8		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPC			\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPC_LOW16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPC_MEDLOW16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPC_MEDHI16		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPC_HI16		\
+   && ((FIX)->fx_addsy == NULL					\
+       || (! S_IS_EXTERNAL ((FIX)->fx_addsy)			\
+	   && ! S_IS_WEAK ((FIX)->fx_addsy)			\
+	   && S_IS_DEFINED ((FIX)->fx_addsy)			\
+	   && ! S_IS_COMMON ((FIX)->fx_addsy))))
+
+/* Note the kludge: we want to put back C, and we also want to consume the
+   expression, since we have handled it ourselves.  FIXME: What we really
+   need is a new GAS infrastructure feature: md_qualifier.  */
+#undef md_parse_name
+#define md_parse_name(NAME, EXP, CP) \
+ sh64_consume_datalabel (NAME, EXP, CP, operand)
+extern int sh64_consume_datalabel
+ PARAMS ((const char *, expressionS *, char *, segT (*) (expressionS *)));
+
+/* Saying "$" is the same as saying ".".  */
+#define DOLLAR_DOT
+
+#define MD_PCREL_FROM_SECTION(FIXP, SEC)		\
+  shmedia_md_pcrel_from_section (FIXP, SEC)
+
+extern valueT shmedia_md_pcrel_from_section PARAMS ((struct fix *, segT));
+
+/* We need to mark this symbol as a BranchTarget; setting st_other for it
+   and adding 1 to its value (temporarily).  */
+extern void sh64_frob_label PARAMS ((symbolS *));
+
+#undef  tc_frob_label
+#define tc_frob_label(sym) \
+  do { sh_frob_label (); sh64_frob_label (sym); } while (0)
+
+#define tc_symbol_new_hook(s) sh64_frob_label (s)
+
+/* We use this to mark our "datalabel" symbol copies.  The "mark" is NULL
+   for an ordinary symbol, and the pointer to the "ordinary" symbol for a
+   datalabel symbol.  */
+#define TC_SYMFIELD_TYPE symbolS *
+
+#define tc_frob_symbol(symp, punt)		\
+ do						\
+   {						\
+     punt = sh64_exclude_symbol (symp);		\
+   }						\
+ while (0)
+
+extern int sh64_exclude_symbol PARAMS ((symbolS *));
+
+extern void sh64_adjust_symtab PARAMS ((void));
+#define tc_adjust_symtab sh64_adjust_symtab
+
+#undef  md_flush_pending_output
+#define md_flush_pending_output() sh64_flush_pending_output ()
+extern void sh64_flush_pending_output PARAMS ((void));
+
+/* Note that tc-sh.c has a sh_frob_section, but it's called from
+   tc_frob_file_before_adjust.  */
+#define tc_frob_section(sec) shmedia_frob_section_type (sec)
+extern void shmedia_frob_section_type PARAMS ((asection *));
+
+#define ELF_TC_SPECIAL_SECTIONS \
+  { ".cranges",	SHT_PROGBITS,	0 },
+
+/* We need to emit fixups relative to the frag in which the instruction
+   resides.  Safest way without calculating max fragment growth or making
+   it a fixed number is to provide a pointer to the opcode frag.
+
+   We also need to emit the right NOP pattern in .align frags.  This is
+   done after the text-to-bits assembly pass, so we need to mark it with
+   the ISA setting at the time the .align was assembled.  */
+#define TC_FRAG_TYPE struct sh64_tc_frag_data
+
+enum sh64_isa_values
+ {
+   sh64_isa_unspecified,
+   sh64_isa_shcompact,
+   sh64_isa_shmedia,
+
+   /* Special guard value used in contexts when we don't know which ISA it
+      is, just that it's specified (not sh64_isa_unspecified).  */
+   sh64_isa_sh5_guard
+ };
+
+struct sh64_tc_frag_data
+{
+  fragS *opc_frag;
+  enum sh64_isa_values isa;
+};
+
+extern enum sh64_isa_values sh64_isa_mode;
+
+#define TC_FRAG_INIT(FRAGP)					\
+ do								\
+   {								\
+     (FRAGP)->tc_frag_data.opc_frag = sh64_last_insn_frag;	\
+     (FRAGP)->tc_frag_data.isa = sh64_isa_mode;			\
+   }								\
+ while (0)
+
+/* This variable is set whenever we generate (or grow) a new opcode frag
+   in shmedia_build_Mytes.  */
+extern fragS *sh64_last_insn_frag;
+
+#define md_end() shmedia_md_end ()
+void shmedia_md_end PARAMS ((void));
+
+/* Because we make .debug_line hold the SHmedia instruction address | 1,
+   we have to say we only have minimum byte-size insns.  */
+#undef  DWARF2_LINE_MIN_INSN_LENGTH
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
Index: gas/doc/Makefile.am
===================================================================
RCS file: /home/aoliva/cygnus/uberbaum/gas/doc/Makefile.am,v
retrieving revision 1.16
diff -u -p -r1.16 Makefile.am
--- gas/doc/Makefile.am 26 Jan 2002 21:20:00 -0000 1.16
+++ gas/doc/Makefile.am 2 Feb 2002 04:00:30 -0000
@@ -46,6 +46,7 @@ CPU_DOCS = \
 	c-pj.texi \
 	c-ppc.texi \
 	c-sh.texi \
+	c-sh64.texi \
 	c-sparc.texi \
         c-tic54x.texi \
 	c-vax.texi \
Index: gas/doc/as.texinfo
===================================================================
RCS file: /home/aoliva/cygnus/uberbaum/gas/doc/as.texinfo,v
retrieving revision 1.53
diff -u -p -r1.53 as.texinfo
--- gas/doc/as.texinfo 1 Feb 2002 08:09:47 -0000 1.53
+++ gas/doc/as.texinfo 2 Feb 2002 04:00:30 -0000
@@ -5625,6 +5625,7 @@ subject, see the hardware manufacturer's
 @end ifset
 @ifset SH
 * SH-Dependent::                Hitachi SH Dependent Features
+* SH64-Dependent::              Hitachi SH64 Dependent Features
 @end ifset
 @ifset PDP11
 * PDP-11-Dependent::            PDP-11 Dependent Features
@@ -5772,6 +5773,7 @@ family.
 
 @ifset SH
 @include c-sh.texi
+@include c-sh64.texi
 @end ifset
 
 @ifset SPARC
Index: gas/doc/c-sh64.texi
===================================================================
RCS file: gas/doc/c-sh64.texi
diff -N gas/doc/c-sh64.texi
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gas/doc/c-sh64.texi 2 Feb 2002 04:00:30 -0000
@@ -0,0 +1,207 @@
+@c Copyright (C) 2001 Free Software Foundation, Inc.
+@c This is part of the GAS manual.
+@c For copying conditions, see the file as.texinfo.
+@page
+@node SH64-Dependent
+@chapter Hitachi SH64 Dependent Features
+
+@cindex SH64 support
+@menu
+* SH64 Options::              Options
+* SH64 Syntax::               Syntax
+* SH64 Directives::           SH64 Machine Directives
+* SH64 Opcodes::              Opcodes
+@end menu
+
+@node SH64 Options
+@section Options
+
+@cindex SH64 options
+@cindex options, SH64
+@table @code
+
+@cindex SH64 ISA options
+@cindex ISA options, SH64
+@item -isa=shmedia | -isa=shcompact
+Specify the default instruction set.  @code{SHmedia} specifies the
+32-bit opcodes, and @code{SHcompact} specifies the 16-bit opcodes
+compatible with previous SH families.  The default depends on the ABI
+selected; the default for the 64-bit ABI is SHmedia, and the default for
+the 32-bit ABI is SHcompact.  If neither the ABI nor the ISA is
+specified, the default is 32-bit SHcompact.
+
+Note that the @code{.mode} pseudo-op is not permitted if the ISA is not
+specified on the command line.
+
+@cindex SH64 ABI options
+@cindex ABI options, SH64
+@item -abi=32 | -abi=64
+Specify the default ABI.  If the ISA is specified and the ABI is not,
+the default ABI depends on the ISA, with SHmedia defaulting to 64-bit
+and SHcompact defaulting to 32-bit.
+
+Note that the @code{.abi} pseudo-op is not permitted if the ABI is not
+specified on the command line.  When the ABI is specified on the command
+line, any @code{.abi} pseudo-ops in the source must match it.
+
+@item -shcompact-const-crange
+Emit code-range descriptors for constants in SHcompact code sections.
+
+@item -no-mix
+Disallow SHmedia code in the same section as constants and SHcompact
+code.
+
+@item -no-expand
+Do not expand MOVI, PT, PTA or PTB instructions.
+
+@item -expand-pt32
+With -abi=64, expand PT, PTA and PTB instructions to 32 bits only.
+
+@end table
+
+@node SH64 Syntax
+@section Syntax
+
+@menu
+* SH64-Chars::                Special Characters
+* SH64-Regs::                 Register Names
+* SH64-Addressing::           Addressing Modes
+@end menu
+
+@node SH64-Chars
+@subsection Special Characters
+
+@cindex line comment character, SH64
+@cindex SH64 line comment character
+@samp{!} is the line comment character.
+
+@cindex line separator, SH64
+@cindex statement separator, SH64
+@cindex SH64 line separator
+You can use @samp{;} instead of a newline to separate statements.
+
+@cindex symbol names, @samp{$} in
+@cindex @code{$} in symbol names
+Since @samp{$} has no special meaning, you may use it in symbol names.
+
+@node SH64-Regs
+@subsection Register Names
+
+@cindex SH64 registers
+@cindex registers, SH64
+You can use the predefined symbols @samp{r0} through @samp{r63} to refer
+to the SH64 general registers, @samp{cr0} through @code{cr63} for
+control registers, @samp{tr0} through @samp{tr7} for target address
+registers, @samp{fr0} through @samp{fr63} for single-precision floating
+point registers, @samp{dr0} through @samp{dr62} (even numbered registers
+only) for double-precision floating point registers, @samp{fv0} through
+@samp{fv60} (multiples of four only) for single-precision floating point
+vectors, @samp{fp0} through @samp{fp62} (even numbered registers only)
+for single-precision floating point pairs, @samp{mtrx0} through
+@samp{mtrx48} (multiples of 16 only) for 4x4 matrices of
+single-precision floating point registers, @samp{pc} for the program
+counter, and @samp{fpscr} for the floating point status and control
+register.
+
+You can also refer to the control registers by the mnemonics @samp{sr},
+@samp{ssr}, @samp{pssr}, @samp{intevt}, @samp{expevt}, @samp{pexpevt},
+@samp{tra}, @samp{spc}, @samp{pspc}, @samp{resvec}, @samp{vbr},
+@samp{tea}, @samp{dcr}, @samp{kcr0}, @samp{kcr1}, @samp{ctc}, and
+@samp{usr}.
+
+@node SH64-Addressing
+@subsection Addressing Modes
+
+@cindex addressing modes, SH64
+@cindex SH64 addressing modes
+
+SH64 operands consist of either a register or immediate value.  The
+immediate value can be a constant or label reference (or portion of a
+label reference), as in this example:
+
+@example
+	movi	4,r2
+	pt	function, tr4
+	movi	(function >> 16) & 65535,r0
+	shori	function & 65535, r0
+	ld.l	r0,4,r0
+@end example
+
+@cindex datalabel, SH64
+Instruction label references can reference labels in either SHmedia or
+SHcompact.  To differentiate between the two, labels in SHmedia sections
+will always have the least significant bit set (i.e. they will be odd),
+which SHcompact labels will have the least significant bit reset
+(i.e. they will be even).  If you need to reference the actual address
+of a label, you can use the @code{datalabel} modifier, as in this
+example:
+
+@example
+	.long	function
+	.long	datalabel function
+@end example
+
+In that example, the first longword may or may not have the least
+significant bit set depending on whether the label is an SHmedia label
+or an SHcompact label.  The second longword will be the actual address
+of the label, regardless of what type of lable it is.
+
+@node SH64 Directives
+@section SH64 Machine Directives
+
+In addition to the SH directives, the SH64 provides the following
+directives:
+
+@cindex SH64 machine directives
+@cindex machine directives, SH64
+
+@table @code
+
+@item .mode [shmedia|shcompact]
+@itemx .isa [shmedia|shcompact]
+Specify the ISA for the following instructions (the two directives are
+equivalent).  Note that programs such as @code{objdump} rely on symbolic
+labels to determine when such mode switches occur (by checking the least
+significant bit of the label's address), so such mode/isa changes should
+always be followed by a label (in practice, this is true anyway).  Note
+that you cannot use these directives if you didn't specify an ISA on the
+command line.
+
+@item .abi [32|64]
+Specify the ABI for the following instructions.  Note that you cannot use
+this directive unless you specified an ABI on the command line, and the 
+ABIs specified must match.
+
+@item .uaquad
+Like .uaword and .ualong, this allows you to specify an intenionally
+unaligned quadword (64 bit word).
+
+@end table
+
+@node SH64 Opcodes
+@section Opcodes
+
+@cindex SH64 opcode summary
+@cindex opcode summary, SH64
+@cindex mnemonics, SH64
+@cindex instruction summary, SH64
+For detailed information on the SH64 machine instruction set, see
+@cite{SH64-Microcomputer User's Manual} (Hitachi Micro Systems, Inc.).
+
+@code{@value{AS}} implements all the standard SH64 opcodes.  In
+addition, the following pseudo-opcodes may be expanded into one or more
+alternate opcodes:
+
+@table @code
+
+@item movi
+If the value doesn't fit into a standard @code{movi} opcode,
+@code{@value{AS}} will replace the @code{movi} with a sequence of
+@code{movi} and @code{shori} opcodes.
+
+@item pt
+This expands to a sequence of @code{movi} and @code{shori} opcode,
+followed by a @code{ptrel} opcode, or to a @code{pta} or @code{ptb}
+opcode, depending on the label referenced.
+
+@end table

-- 
Alexandre Oliva   Enjoy Guarana', see http://www.ic.unicamp.br/~oliva/
Red Hat GCC Developer                  aoliva@{cygnus.com, redhat.com}
CS PhD student at IC-Unicamp        oliva@{lsd.ic.unicamp.br, gnu.org}
Free Software Evangelist                Professional serial bug killer

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]