diff options
88 files changed, 47908 insertions, 0 deletions
diff --git a/gas/COPYING b/gas/COPYING new file mode 100644 index 0000000..9a17037 --- /dev/null +++ b/gas/COPYING @@ -0,0 +1,249 @@ + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/gas/ChangeLog b/gas/ChangeLog new file mode 100644 index 0000000..3b45c21 --- /dev/null +++ b/gas/ChangeLog @@ -0,0 +1,1196 @@ +Fri Jan 4 12:48:22 EST 1991 Jay Fenlason (hack@ai.mit.edu) + + * messages.c Moved as_perror from input-scrub.c Modified the + error messages to look better. + + * output-file.c Don't call as_fatal, just call exit() + + expr.c Slightly improve checking for foo-foo case in + clean_up_expression(). Detect foo: bar: ... foo-bar... + +Tue Dec 4 16:29:20 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * m68k.c Fixed an obscure bug involving AOFF mode with a + large constant displacement (Was forgetting to output the + extension word.) + + make-gas.com Added a three line patch from Eric Youngdale that + makes it possible to submit make-gas.com to a batch queue. + +Wed Nov 21 15:07:51 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * vms.c (VMS_TBT_Routine_END) Add a four line patch from + Eric Youngdale. + +Tue Nov 13 14:02:15 EST 1990 Jay Fenlason (hack@ai.mti.edu) + + * vms-dbg.c (VMS_DBG_record()) Another one character patch from + Eric Youngdale. + +Mon Oct 29 15:49:21 EST 1990 Jay Fenlason (hack@ai.mit.edu) + + * read.c Replace some as_warn calls with as_bad. + +Fri Oct 26 15:21:15 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * i386.c, i860.c, ns32k.c Add const changes. + +Mon Oct 22 14:04:26 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * sparc.c Add const changes. + + * make-gas.com define const= for VMS, since many older versions of + GCC don't work correctly with const under VMS. + +Thu Oct 18 12:44:11 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * i860.c i860-opcode.h Added patches from rgb@mcc.com + + * read.c, symbols.c, vms.c, + new_file vms-dbg-module.c + Added Eric Youngdale's <YOUNGDALE@v6550c.nrl.navy.mil> VMS debugging + patches, so debugging GCC output now works. + + * hash.c (hash_grow) Remember to blank out the wall entry in the new + hash table. This is important on systems where malloc() returns + non-zero storage. . . + +Tue Oct 16 10:11:35 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * output-file.c (output_file_create) if output filename is given as + '-', write to stdout. + + * m68k.c Finally get the PCREL code to work right. Add relaxation of + PCREL stuff This small fix from Ken Woodland + (kenny%datacube.uucp@uunet.uu.net). + + * m68k.c Added some const declarations to constants. (md_relax_table, + md_pseudo_table, etc. . .) + +Thu Oct 11 11:15:10 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * Makefile, read.c, write.c Include the i860 port. + (New files i860.c i860-opcode.h i860.h) + + * m68k.c Fix some addressing modes, (AOFF, AINDEX, etc) to work in + PC relative mode. + + * (all over) Raeburn's const hacking. This reduces the data-space size by + declaring many tables, etc, as 'const'. + +Mon Oct 22 22:48:22 1990 John Gilmore (gnu at cygint) + + Make gas work if you turn on the know() checks. + + * app.c: Only pass a single space through: the one after + the opcode. All other whitespace is removed, to match the + expectations of the parser in read.c. + + * as.h: Remove obsolete comments. Remove JF's NDEBUG so + that know() can really work if you turn it on. Make + SEG_MAXIMUM_ORDINAL == SEG_REGISTER. + + * expr.c (operand): Change BITS_PER_INT to 8*sizeof(int). + + * input-scrub.c: strlen("\0") doesn't return 1... + (as_where): Add space after line number in errors, like gcc. + + * m68k.c (s_bss): Fake .bss into data section 255. + We can't cope with a real "BSS section" yet, but we can at + least do the right thing less efficiently (with lots of + zeroes). + + * read.c: Turn lots of as_warn()'s into as_bad()'s. + + * read.h (SKIP_WHITESPACE): Replace last instance of ASSERT() + with know(). + + * sparc.c (s_seg): We can't put frags into the BSS segment + yet, so just fake bss seg as 255th data segment. + + * vax.c: Remove \'s from continued macro *parameters*. These + must have been added after the last time someone turned on + know() checking... + + * write.c (relax_segment): Refine what we know() about the + symbols referenced during relaxation. + + * Makefile (OTHER_ALIGN): Remove, handled in tables now. + Flip options a bit. These options really ought to go + elsewhere. + +Sun Oct 21 03:57:21 1990 John Gilmore (gnu at cygint) + + Sun-3 fixes. + + * expr.c, write.c: Missing semicolon after know(). + + * write.c (fixup_segment): Allow pc-relative accesses to undefined + external symbols. Previously this would turn off pc-rel calc + of displacement, while leaving pc-rel opcode alone, botching. + + * m68k.c (m68k_ip): Allow pc-relative effective addresses + for source operands. "pea" instructions are a good example + where we can shorten from abs long to pc+16bit. + (md_convert_frag): Fix "JBSR" comments to refer to "JSR", the + actual instruction. Insert comments about bug in 68000 bcc + and dbcc long code (that doesn't get exercised much). Add + comments about long pcrel displacements for -pic. Remove + "this code has not been tested" comment. + (md_estimate_size_before_relax): Now that fixup_segment + doesn't shortcircuit pc-relative fixups for undefined symbols, + only output them if -pic; else turn them absolute, which is + slightly faster. More JBSR->JSR comments. + (md_parse_options): Parse -pic. + +Fri Oct 19 14:35:20 1990 John Gilmore (gnu at cygint) + + * Make sparc assembler more compatible with Sun assembler. + sparc.c: reformat pseudo-op table to match main table. + (md_assemble): Add SPECIAL_CASE_FDIV to assemble FDIV* + instructions as fdiv followed by fmovs to get around chip bug. + (s_common, s_seg): Accept "bss" section name. + (md_assemble): Handle "set" instructions with absolute + operands, that only take one instruction rather than two. + sparc-opcode.h (fdiv*): Mark instructions "S"pecial. + subsegs.c (subseg_change): Move tail pointer too. + symbols.c (colon): Allow new definitions to override .comm symbols, + as in VMS. Sun CC depends on this. + write.c (new_fix): Always take r_type argument, not just on sparc. + Chain fixP's in order, using tail pointer, so relocation + records come out in forward order like Sun as. Remove SPARC + ifdefs. + write.h: Add seg_fix_tailP, data_fix_tailP, text_fix_tailP. + + * am29k.c: Use s_align_bytes rather than a local copy. + + * read.c (s_align): Rather than ifdef it, make two functions, + s_align_bytes and s_align_ptwo. Individual pseudo-op tables + can call whichever one they like. + + * write.c (append): Move from append.c to here. + append.c: Remove file. + + * Makefile (MDSRC, mdsrc): Easy way to edit all md.c's. + Fix options. Add option for -DDEBUG for know() and assert(). + Remove append.c, am29k.h. Don't build special read-sparc.o. + Remove sparc.h. "make clean" removes am29k .o's. Add + dependencies on reloc.h. + +Thu Oct 18 17:56:34 1990 John Gilmore (gnu at cygint) + + * Generalize sparc extensions to relocation info. Gas now + keeps relocation information internally in a different format + than how it is stored in the resulting .o. md_ri_to_bytes() + converts to external format. md_reloc_size says how large + each relocation record is in external format. + sparc.h: Remove this file. Rename to reloc.h. Rename struct + to reloc_info_generic. + reloc.h: Add relocation types for AMD 29000. + read.c, write.c: Always call fix_new with reloc-type argument. + write.c (emit_relocations): Make md_ri_to_bytes write directly + to output area rather than overwriting its argument then + bcopying it. + md.h: Declare md_reloc_size and md_ri_to_bytes. + i386.c, am29k.c, vax.c, ms32k.c, m68k.c, sparc.c: include reloc.h. + (md_reloc_size): Specify correct value. + (md_ri_to_bytes): Convert format from internal to external. + + write.c (write_object_file): Call md_section_align() which + rounds section sizes up if desired. + sparc.c (md_section_align): Round to 8 byte boundary. + i386.c, am29k.c, vax.c, ns32k.c, m68k.c (md_section_align): Nop. + +Mon Oct 15 22:06:11 1990 John Gilmore (gnu at cygint) + + Changes in support of the AMD 29000 version of gas. + + * am29k-opcode.h: Add dummy entry to end so we can examine + item N+1 without exceeding table. + + * am29k.h: New include file, derived from sparc.h. Kludged + together, still needs major work to get relocation working. + + * am29k.c: New file, derived from sparc.c. + Put 29k-specific ASM29K pseudo-ops into table. + Change comment_chars to ASM29K. + Change s_seg to s_use. + Change s_sparc_align to s_29k_align, default operand to 4. + (define_some_regs): Define special register names. + (md_begin): Preprocess opcode table to mash together all + the variants of single opcodes. This simplifies later handling. + Call define_some_regs to preset special reg names. + (parse_operand): Add, parses out an operand from a stmt. + (machine_ip): Simplify, since 29K is simpler asm language. + Handle the various keyletters in the opcode table. + + + Handle include files in the assembler, with a .include + pseudo-op. + * as.h (input_scrub_include_file): declare. + * as.c (perform_an_assembly_pass): Avoid buffer hacking. + Start us off in text segment. + * read.c (read_a_source_file): Take a name as argument, + internalize all buffer handling. Don't start a new text + subsegment on each entry. Actually use the start and end + pointers returned by input_scrub_next_buffer. + (s_include): Call input_scrub_include_file for .include. + * input-scrub.c: Fix comments. + (struct input_save): Add, for saving state at .include. + (input_scrub_push, input_scrub_pop): Add, push & pop state. + (input_scrub_begin): Initialize next_saved_file. + (input_scrub_end): Free buffer. + (input_scrub_include_file): Add, to include a file. + (input_scrub_close): Add, to close a file. + (input_scrub_next_buffer): Set buffer-start address for + caller. If we hit EOF and were included, pop to previous file. + * input-file.c: Remove old includes. Remove old file-descriptor + hacking code, that was commented out. + (struct saved_file): Add, for saving state at .include. + (input_file_push, input_file_pop): Add, push & pop state. + (input_file_open): Don't buffer all files in one place. + (input_file_close): Add, close input file. + * input-file.h: Declare new functions. + * app.c: (struct app_save): Add, for saving state at .include. + (app_push, app_pop): Add, push and pop state. + (do_scrub_next_char): Move its static state variables out so + they can be saved and restored. + + + * app.c: Allow overriding of character meanings by machine + dependent strings. Avoid wiring character constants into app.c. + (do_scrub_begin): New meanings override old ones, not "OR" them. + Only handle /* comments if / is not already in use. + (do_scrub_next_char): Reorganize mass of nested if's + into a switch for speed. Don't assume ';' is line terminator. + Reorganize switch on characters, into a switch on their (machine- + dependent) lexer table meanings. + + + Encapsulate knowledge of segment types in fewer places. + This allows us to add the "SEG_REGISTER" type, as well as + providing flexibility for eventual COFF/ELF support. + * struc-symbol.h (symbol_to_segment, symbol_to_segment_type, + set_symbol_segment, set_symbol_segment_keep_ext, + segment_name): Define macros to encapsulate this info. + * as.h: Remove externs for seg_name, seg_N_TYPE, N_TYPE_seg. + * symbols.c (symbol_new): Change 'type' arg to 'seg'. + * expr.c, i386.c, m68k.c, ns32k.c, read.c, symbols.c, vax.c, + write.c: Use macros. + * i386.c, m68k.c, ns32k.c, vax.c, write.c: Change 2nd arg type of + md_estimate_size_before_relax. + * expr.c, read.c: Change 'type' arg to symbol_new. + * read.c, symbols.c, vax.c, write.c: Move md.h to end of includes. + + + Allow expressions to evaluate to registers. + * as.h: Add SEG_REGISTER. + * struc-symbol.h: Add fake N_REGISTER type. + * subseg.c: Add types to tables. + * expr.c (operand): Symbols of SEG_REGISTER type are + immediately evaluated like those of SEG_ABSOLUTE. + (clean_up_expression): Clean up SEG_REGISTER exprs. + + + Allow machine descriptions to cleanly extend the set of + possible operands. + * expr.c (operand): Call md_operand before rejecting an + operand as unacceptable. + * md.h: declare. + * i386.c, ns32k.c, m68k.c, sparc.c, vax.c: Define null function. + * am29k.c (md_operand): Use this for %% and & prefix operators. + + + Allow machine descriptions to cleanly permit symbols to be + predefined upon first usage. + * symbols.c (symbol_find_or_make): Call md_undefined_symbol + before making an undefined symbol. + * md.h: declare. + * i386.c, ns32k.c, m68k.c, sparc.c, vax.c: Define null function. + * am29k.c (md_undefined_symbol): use this for the local and + global register names; since there are hundreds of them, it + only defines them upon their first use. + * expr.c (operand): Call symbol_find_or_make rather than + roll our own undefined symbols. + + + Miscellaneous changes: + + * as.h (know): Make this useful if DEBUG defined. + + * write.h: Support SPARC-like relocation throughout all + versions. + + * read.c (read_a_source_file): Report the name of invalid + pseudo-ops. Don't double-report junk characters. Close the + input file, which gas never used to do. + (ignore_rest_of_line): Report junk chars in ascii if + printable. + (s_ignore): Ignore entire statement; used for 'listing + control' statements from ASM29K, e.g. .eject. + + * read.c (s_lsym): Handle register equates too. + + * read.c: Add most ASM29K pseudo-ops to the master table. + Not all are implemented yet. + + * cond.c: New file, for functions implementing conditional + assembly pseudo-ops: .ifdef, .ifndef, .else, .endif, .ifseq, + .ifsne, .end. So far, they are just stubbed out. + + * read.c (pobegin): Allow the machine dependent pseudo-op + table to override the generic one. Remove #ifdef SPARC + on .word, since it can now be overridden. + + * expr.c (operand): Support radix-2 constants. Kill off + support for octals with digits '8' and '9'. Initial steps + toward more general support for local-labels. + + * symbols.h (symbol_table_lookup): Remove macro, change all + occurrences (in read.c, expr.c, symbols.c) to symbol_find. + + * read.h (is_end_of_line): Define for external use. + + * i386.c (alloca): Use builtin_alloca or include or extern. + + * Makefile: Add ALL and all: entries. Add asm29k entries. + Add cond.c and cond.o. Remove special handling for messages.o. + Add lint entry. + +Thu Sep 27 13:43:49 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * m68k.c (get_num) Fix so that 1:w is treated properly. + + * Makefile Replace references to a.out.h with a.out.gnu.h + +Tue Sep 25 15:50:36 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * sparc.c (md_number_to_imm) Fix so that RELOC_32 locations contain + zero, so that it will work with some sparc loaders which don't assume + that the locations in question do. A xix line patch from Michael Bloom + (usc!srhqla!quad1!psivax!ttidca!mb@zaphod.mps.ohio-state.edu) + +Mon Sep 24 11:43:15 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * as.c #include <sys/types.h> if _POSIX_SOURCE defined. This because + <signal.h> uses pid_t that's defined in it. + + * m68k.c reverse the sense of -l option, and allow :w and :l to + override the default size of AOFF indexes. + + Also, allow -l to shorten branches to unknown labels from LONG to WORD. + +Thu Sep 13 17:05:09 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * vax.c (md_parse_option) Don't print a warning msg if given -J + +Wed Sep 5 14:26:14 EDT 1990 Jay Fenlason + + * expr.c (operand) Don't get garbaged high-order bits when given a + lot of leading zeroes. + +Tue Sep 4 11:40:21 EDT 1990 Jay Fenlason + + * read.c (pseudo_set) Compain if we're setting the symbol to the + difference of two symbols which are in different frags. (We can't + find out how far apart they are.) + +Wed Aug 15 12:18:58 EDT 1990 Jay Fenlason + + * m68k.c (m68k_ip_op) Dyke out the code that deals with parsing + :[wl] at the end of expressions since it is handled in get_num() + and this was putting the result in the wrong place anyway. + Corrected a couple of other references to ->isiz instead of con?->e_siz + +Mon Aug 13 15:53:46 EDT 1990 Jay Fenlason + + * read.c Handle .align specially on the sparc, or any other machine + where OTHER_ALIGN is defined. Added option and comments about it + to Makefile. + +Fri Aug 10 12:24:33 EDT 1990 Jay Fenlason + + * m68k.c (get_num) Handle SEG_PASS1 expressions. + +Mon Aug 6 16:32:29 EDT 1990 Jay Fenlason + + * write.c (fixup_segment) Added two patches for the NS32k + and SEQUENT A six line patch from Ian Dall + (asgard!aegir!hugin!augean!sibyl!ian@munnari.oz.au) + +Wed Aug 1 13:30:48 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * m68k.c Include REGISTER_PREFIX ifdefs. + + * write.c Include LOCAL_LABEL() and DOT_LABEL_PREFIX feature. + + * vax.c (md_parse_option) Accept -H option. + + * vms.c New version of case hasher, etc. These from Eric Youngdale + <YOUNGDALE@v6550c.nrl.navy.mil> + +Fri Jul 20 13:39:02 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * README Added README.APOLLO and README.COFF stuff + +Wed Jul 18 16:29:22 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * Makefile Added option for SEQUENT_COMPATABILITY + + * ns32k.c Add configurable syntax feature from + ian@sibyl.eleceng.ua.oz@augean.ua.oz.au + and SEQUENT_COMPATABILITY + + * ns32k-opcode.h Add configurable syntax feature. + +Mon Jul 16 11:44:14 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * write.c (relax_segment) On ns32k, add fragP->fr_pcrel_adjust to + aim. + (fixup_segment) On ns32k, don't subtract size from + add_number on pcrel external symbols. + + * ns32k.c (md_relax_table) Use correct max displacements. + This is a six-line patch from ian@sibyl.eleceng.ua.oz.au + + * ns32k.c (md_atof, convert_iif) Emit floating point numbers in + the correct byte order. A seven line patch from + ian@sibyl.eleceng.ua.oz.au + + * ns32k.c (all over) Some lint fixes. + +Mon Jul 9 13:17:00 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * app.c (do_scrub_next_char) If a comment is #{whitespace} + don't treat the next line as comment also. + + * m68k.c (...) Accept apc@(num:[wl]), etc. + + * i386.c (md_assemble) Get bitfields correct when doing cross + assembly to 386. A two line patch from Michael Bloom. + (usc!srhqla!quad1!ttidca!mb@zaphod.mps.ohio-state.edu). + + * README.APOLLO a new file with vasta@apollo's name, address + and phone # in it. + + * make-gas.com Deleted references to gdb source files. + +Fri Jul 6 14:34:27 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * i386.c Ignore the .optim directive + + * input-file.c Change from _IOLBF to _IOFBF in setbuffer emulation + for SYSV. + +Mon Jun 18 15:36:49 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * sparc.c #ifdef DONTDEF s_sparc_align, since it isn't called from + anywhere. + +Fri Jun 15 15:53:30 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * vax.c (md_parse_option) make the code agree with the documentation + on the behaviour of the -d option. + +Thu Jun 7 14:23:54 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * atof-ieee.c (gen_to_words) Assemble 0r-0 correctly. + + * Makefile Remove last references to gdb*.c files. + + * version.c New version 1.36 + +Tue May 22 13:22:26 EDT 1990 Jay Fenlason (hack@ai.mit.edu) + + * Makefile Mention a work-around for a possible problem with HPUX7.0 + +Mon May 21 14:06:04 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * sparc.c (sparc_ip): Change error message from "not in hash table" + to "unknown opcode". + +Wed May 16 15:33:14 EDT 1990 hack@wookumz + + * i386.c (i386_operand) Print error msg if given an operand like + 4(mumble) which we can't parse. + + * m68k.c (md_assemble) Add '3' to the list of operand-places that + can be found in 'symbol-dependent info'. Also change + 'confusing width' diagnostic to something more meaningful. + +Fri May 11 12:09:21 EDT 1990 hack@wookumz + + app.c (do_scrub_next_char) Don't flush the line after a line + consisting of a single '/' A one-line patch from Mike Kupfer + (kupfer@orc.olivetti.com) + + * i386.c (md_assemble) Call frag_wane() before calling frag_new() + A one line patch from Steve Bleazard (steve@robobar.co.uk + +Tue May 8 12:56:25 EDT 1990 hack@wookumz + + * atof-generic.c (atof-generic) Modified the Infinity detection code + to accept 0rinfinity and 0r-infinity as well as 0rinf and 0r-inf + +Thu Apr 26 15:17:31 EDT 1990 hack@wookumz + + * atof-ieee.c Change value of NaNs to 0x7fff ffff (float) and + 0x7fff ffff ffff ffff (double) If you want some other value for + NaN, use .long and spell it out yourself. + + atof-generic.c (atof_generic) Cleaned up code that detects NaN + and Inf. + + vax.c (md_assemble) print a useful error message if expression() + returns SEG_PASS1 or SEG_DIFFERENCE and we can't deal with those. + +Thu Apr 19 10:30:47 EDT 1990 hack@wookumz + + * input-scrub.c (AFTER_STRING) Make AFTER_STRING contain a null + so that the strstr() call when looking for "#NO_APP" after a "#APP" + will work. A two character patch from Bruce Robertson + (bruce@heather.pooh.com) + + * Makefile, i386.c Use atof-ieee.c instead of atof-i386.c + +Mon Apr 16 16:20:55 EDT 1990 hack@wookumz + + * m68k.c (md_relax_table) Many of the offsets were off by two. + Fixed some generic spacing problems thoughout the file. + +Thu Apr 12 12:22:35 EDT 1990 hack@wookumz + + * sparc.c (md_ri_to_chars) Handle little-endian cross assembly. + + * write.c (relax_segment) Compare addresses correctly to avoid + accidentally relaxing a branch that we don't have to. + These small changes from John Gilmore (gnu@toad.com) + +Fri Apr 6 12:52:15 EDT 1990 hack@wookumz + + * Makefile, expr.c symbols.c Correctly document the SUN_ASM_SYNTAX + option, and make it work. + +Tue Mar 20 12:46:59 EST 1990 + + * as.c (main) Only trap SIGHUP, SIGINT, SIGPIPE, and SIGTERM, + and only if they aren't being ignored. A three line patch + from Paul Eggert (eggert@twinsun.com) + + * write.c (relax_segment) Correct typo 'growth - ' should have been + growth = + + * atof-vax.c (next_bits, flonum_gen2vax) Clean up by sharing some + variables. While we're at it, fix next_bits so that it + doesn't use littlenums that don't exist. . . + +Tue Mar 13 16:23:21 EST 1990 hack@wookumz + + * Rename atof-m68k.c atof-ieee.c + + * Delete atof-ns32k.c + + * m68k.c sparc.c ns32k.c atof-ieee.c Call atof-ieee instead of + atof-m68k or atof-ns32k + + * Makefile Compile with atof-ieee.c instead of atof-ns32k.c or + atof-m68k.c + +Mon Mar 12 14:06:55 EST 1990 hack@wookumz + + * as.c If the signal handler gets called twice, exit immediatly. + + * ns32k.c Call gen_to_words with a pointer of the proper type, and + call md_number_to_chars to put the results in the proper byte-order. + Whoever wrote this code was *sloppy*! + + * Makefile ns32k.o depends on ns32k.c + + * vax.c (md_parse_option) If VMS, accept -+ and -h options. + + * vms.c (VMS_Case_Hack_Symbol) Replace #if NO_CASE_HACKING + with references to the -h option. These small VMS patches + from Angel Li (angel@flipper.miami.edu). + +Thu Mar 8 19:18:59 EST 1990 hack@wookumz + * vms.c Some trivial patches from Eric Youngdale + (YOUNGDALE@v6550c.nrl.navy.mil) + +Wed Mar 7 17:12:09 EST 1990 hack@wookumz + * make-gas.com (Define error as as_fatal when compiling vax.c and vms.c + A two line patch from Eric Youngdale + (YOUNGDALE@v6550c.nrl.navy.mil) + +Tue Mar 6 16:01:09 EST 1990 hack@wookumz + + * Makefile Include ns32k options in makefile. A small patch from + David Taylor (taylor@think.com). + + * as.c read.c write.c Makefile #ifdef DONTDEF out all the gdb + symbol stuff, since it isn't used anymore and it doesn't work. + +Mon Mar 5 14:51:04 EST 1990 hack@wookumz + + * i386.c (md_assemble) Replace memchr() with index(). + + * as.c Trap signals 1 through NSIG, print an error msg, and don't + produce an object file. + + * m68k.c Added a hack so that fsincosx fpx,fpy:fpz works. + + * messages.c New function: as_bad This is like as_warn, except + -W doesn't disable it, and calling it inhibits production of an + object file and causes a non-zero exit code. + +Tue Feb 13 14:25:53 EST 1990 hack@wookumz + * Makefile Include G0 and LOADLIBES for Sequent Symmetry. + Based on a small patch from Johan Widen (jw@sics.se) + +Thu Feb 1 14:08:58 EST 1990 hack@wookumz + * m68k.c Replace 'abort' with 'abort()' which will work. + +Wed Jan 24 17:15:08 EST 1990 hack@ai.mit.edu + + * read.c (ignore_rest_of_line) Have it print the first junk char + in both decimal and %c form. + + (read_a_source_file) On bad pseudo-op, print out the unknown + pseudo-op's name. + +Tue Jan 23 13:12:48 EST 1990 hack@ai.mit.edu + + * read.c (pseudo_set) If the symbol is external, have it remain + external. + + * i386-opcode.h Allow jc as a synonym for jb and jnc as a syn for jnb. + + +Wed Jan 3 09:35:31 EST 1990 hack@ai.mit.edu + + * ns32k.c [cpureg_032] Change register id of psr from 0x0b to 0x0d + * ns32k-opcode.h Change shift-counts for lsh and lshd + to one byte instead of 2 and 4. + A Trivial patch from John F. Peters (think!ames!practic.com!jfp@eddie) + +Tue Dec 5 16:37:44 EST 1989 hack@ai.mit.edu + + * ns32k.c (md_create_{long,short}_jump) Six line patch from + John F Peters (think!ames!vine!practice.com!jfp) to use the + correct addressing mode and byte-order for broken-word stuff. + + * write.c (write_object_file) One line patch to call fix_new_ns32k + with the correct # of args. + +Fri Dec 1 16:44:21 EST 1989 hack@ai.mit.edu + + * atof-generic.c, flonum-mult.c A real fix for the trailing-zeroes + problem from Georg Feil (ghfeil@white.toronto.edu) (two line change) + +Mon Nov 27 15:30:46 EST 1989 hack@ai.mit.edu + + * i386-opcode.h Fixed opcode-table entry for ljmp. A one char + patch from eliot@mgm.mit.edu + +Mon Nov 20 12:41:28 EST 1989 hack@ai.mit.edu + + * expr.c Replace the generic_buffer hack with a more portable one */ + + * atof-generic.c (atof_generic) Ignore trailing zeroes after a decimal + point. For some reason trailing zeroes (but not trailing nonzeroes) were + causing loss of precision. I don't know why. . . + + * vms.c Change copyright notice. Install changes from Kenneth Adelman + (adelman@tgv.com) for c++? (A dozen lines or so) + +Mon Nov 13 11:48:44 EST 1989 hack@ai.mit.edu + + * Makefile Add BINDIR and use it to control where the executable is + installed. + + * i386.c Use __builtin_alloca if possible (trivial patch from + Marco S. Hyman pacbell!dumbcat!marc) + +Mon Nov 6 18:24:47 EST 1989 hack@ai.mit.edu + + * version.c New version: 1.35 will be distributed with the + 1.36 gcc release. + +Mon Oct 30 10:38:11 EST 1989 hack@ai.mit.edu + + * atof-m68k.c (atof_m68k) Don't put the bits[] array on the stack, + since it may be pointed to after atof-m68k exits. + +Tue Oct 24 11:15:57 EDT 1989 hack@ai.mit.edu + + * atof-m68k.c Added #define for bcopy on USG systems. + #ifdef TEST the print_gen() function. + + * a.out.h if USE_HP_INC_HDR then use ../binutils/hp-include/a.out.h + +Fri Oct 13 14:36:48 EDT 1989 hack@ai.mit.edu + + * vax.c (all) Ran vax through indent -gnu to make it readable. + + vax.c (vip_op) Correctly assemble code like jmp $*0x11223344 + by setting vip_nbytes to 4 when using an immediate address. + I hope this works! + + m68k.c (s_proc (new)) Added s_proc no-op pseudo-op. + + Makefile Added instructions for compiling on Sequent Symmetry + and HP 9000/300. + + a.out.h Modified to compile on Sequent and HP above. (HP port + based on a msg from asjl@comp.vuw.ac.nz (real name unknown)). + +Tue Oct 10 14:39:44 EDT 1989 hack@ai.mit.edu + * vax.c (vip_op) Fixed a typo in an error msg and cleaned + up some spacing stuff. + +Wed Sep 27 19:07:12 EDT 1989 hack@ai.mit.edu + + * app.c (do_scrub_next_char) Fixed parsing of + # <line> "file" garbage + text so that it'll work again? (8 line patch from Mike Hibler + (mike@cs.utah.edu)) + +Mon Sep 18 16:26:01 EDT 1989 hack@ai.mit.edu + + * app.c (do_scrub_next_char): Modify parsing of /* ... */ to work + on the text /* ****/ + + * sparc.c (sparc_ip): Don't abort on insns that use the Alternate + Spaces. Try to assemble them correctly. + +Thu Sep 14 11:42:44 EDT 1989 hack@ai.mit.edu + + * sparc.c (md_number_to_imm) Dozen line patch from jkp@sauna.hut.fi + (Jyrki Kuoppala) so that gas output will work with shared libraries. + + * ns32k.c Include <string.h> instead of <strings.h> if USG defined. + + (md_end) free(freeptr_static) instead of free(freeptr) . + + * atof-ns32k.c Include as.h so that sysV stuff (bzero) will be + defined if needed. These ns32k changes from + nixbur!mollers.pad@seismo.css.gov (Josef Moellers) + +Fri Sep 1 11:39:52 EDT 1989 hack@ai.mit.edu + + * atof-m68k.c (gen_to_words) Get the sign right on negative + floating-point numbers. + +Wed Aug 30 13:59:57 EDT 1989 hack@ai.mit.edu + + * Makefile Remove the rest of the $< entries that kill sun make + +Fri Aug 25 15:00:30 EDT 1989 Nobody You Know (hack@ai.mit.edu) + + * atof-m68k.c (gen_to_words) deal with denormalized floating-point + numbers. + +Tue Aug 22 02:03:05 1989 Roland McGrath (roland at hobbes.ai.mit.edu) + + * Makefile (gas-dist.tar): Put ChangeLog in the tar file. + + * version.c: Added comment telling Jay Fenl--I mean people--not to put + changes in version.c, but to use ChangeLog instead. + + * version.c (version_string): Put "GNU" in all-caps. + + * version.c: Moved all comments about changes to ChangeLog (this file). + Many anonymous entries have been attributed to Jay Fenlason (hack). + +Thu Aug 17 15:53:57 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * Makefile: Removed $< references that seem + to choke some versions of make. + + * frags.c (frag_grow): Fixed to deal with requests for very + large frags (larger than frags.chunk_size). + + * app.c (do_scrub_next_char): Have it ignore any characters + after the filename in a # line "filename". + + * sparc.c (s_common): On an error, don't print out + input_line_pointer past the end of the line where the error is. + + * atof-generic.c (atof_generic): Accept any case for + inf and nan. + + * m68k.c (m68_ip): Don't use PC-relative mode for alterable + addressing modes. + +Tue Aug 15 04:58:36 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * sparc.c (md_begin): Rewrote this function to perform consistency + checks with the new opcode table. + +Fri Aug 11 16:01:16 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * sparc-opcode.h (struct sparc_opcode): Replaced `mask' field with + `lose'; removed `last' field. Updated all opcodes accordingly. + Fixed several opcodes that generated the wrong instructions. + sparc.c (md_begin, sparc_ip): Changed to use new struct sparc_opcode. + +Thu Aug 3 14:44:24 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * Makefile (a32k): Use read- and write-ns32k.o + * ns32k.c (encode_operand): Make sure pcrel_adjust starts out zeroed. + * read.c (cons): Call fix_new_ns32k() if NS32K is defined. + * write.c (write_object_file): Ditto. + These so that .word sym-sym (etc) will produce values with + the proper byte-order. + +Wed Aug 2 12:55:?? 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * sparc.c (comment_chars[]): Removed '|' because it was causing + problems. Probably not the best fix, since I suspect other + assemblers (68020) may get | in .stabs also, and the 68020 needs + the '|' comment character. + +Mon Jul 31 09:22:28 1989 Roland McGrath (roland at apple-gunkies.ai.mit.edu) + + * sparc.c (sparc_ip): Allow the characters [0123] in opcodes. + +Tue Jul 25 16:32:12 1989 Jay Fenlason (hack) + + * atof-generic.c (atof_generic): Tried to keep + size_of_digits_in_littlenum from going negative. + + * sparc-opcode.h: Added duplicate [i+1] entries to go with + the [1+i] entries already there. A kludgy fix, but it works. + +Mon Jul 24 17:20:03 1989 Jay Fenlason (hack) + + * write.c (relax_segment): Modified rs_org code so it won't + occasionally dump core. + + * write.c (pseudo_set): Modified SEG_DIFFERENCE to (perhaps) + allow one to set a symbol to the difference of two other symbols. + + * ns32k.c (convert_iif): Moved size_so_far+=size and size=0 inside + the check for a valid type. + + * sparc-opcode.h: Modified the entries for std "q,[1+i]", "D,[1+i]", + and "Q,[1+i]". + +(In version 1.34) Jay Fenlason (hack) + + * Makefile: Reorganized, added stuff to make asparc. + + * sparc.c, sparc-opcode.h, sparc.h: Sparc port. + + * write.c: Set the size of text and bss segments to a multiple of eight + bytes. + + * m68k.c: Moved .single pseudo-op to machine independent part. + + * atof-generic.c: Fixed type in #ifdef __GNUC__. + + * sparc-opcode.h: Handle "mov REG, %y". + + * make-gas.com: Know that error.c no longer exists. + + * sparc.c: Handle [expr+reg]. + Don't call getExpression when looking for an immediate and getting + something that starts with % and isn't %hi or %lo. + + * Teach the 68k about long conditional branches. + +(In version 1.33) Jay Fenlason (hack) + + * Use __builtin_alloca if available. + + * README: Added more instructions for reporting bugs. + + * ns32k-opcode.h: Changed the acbb, acbw, and acbd insns. + + * vax.c: Replaced instances of LENGTH[STRING] with STRING[LENGTH]. + + * ns32k.c (encode_operand): Increased max size of bit field for exts + and inss instructions from 31 to 32 bits. + + * flonum-mult.c (flonum_multip): Fixed typo. + + * m68kc.: Allow #32 to be the same as #0 for bit-field ops. + + * make-gas.com, version.c, hex-value.c, flonum-const.c: VMS fixes. + + * ns32k.c, ns32k-opcode.h: More fixes from taylor@think.com. + Mostly typos in comments, etc. + + * ns32k-opcode.h: Fixed size of immediate operands to andw and andd + instructions. + +(In version 1.32) Jay Fenlason (hack) + + * read.c (s_set): Fixed misnamed variable. + + * as.c: Don't hang if given an invalid option. + + * m68k.c: Fixed bug in creating absolute long addresses for branches. + + * ns3k*: Some small ns32k patches. + + * m68k.c: Recognize 0rnan, 0rinf, 0r-inf. + + * app.c: Don't dump core on unterminated strings. + + * symbols.c: Give reasonable error messages. + + * ns32k*: Allow -m32032 and -m32532 options. + + * atof-*.c: Added support for NaN, Inf, and -Inf in atof_generic and + the various descriptions. + + * m68k.c (add_fix): Replace occurrences of "width==" with + "(width)==". This correct a precedence problem. + + * write.c, struc-symbol.h, m68k-opcode.h, m-hpux.h, Makefile: Changes + for HP-UX from Chris Hanson (cph@kleph.ai.mit.edu). + + * m68k-opcode.h: Reorder movem insns so gdb will see the ones using the + register list syntax first. + + * symbols.c (colon): Give more useful error messages when something was + defined as a .comm and is now trying to be defined locally. + Also, redefining a symbol is a fatal, not a warning. + + * m68k.c: Fixed a bug in using bignums as literal bit patterns for + floating-point numbers. + +(In version 1.31) Jay Fenlason (hack) + + * i386*: More patches. + + * Moved machine-dependent option parsing into the machine-dependent + source files. + +(In version 1.30) Jay Fenlason (hack) + + * i386*: New new version. + + * atof-m68k.c: Changed to be smaller, with somewhat better modularity. + Also fixed an obscure bug wherein next_bits would return random bits. + + * m68k.c: Be more careful about creating PC-relative addressing modes + on the 68000 and 68010. + + * frags.c (frag_new): Zero out the new frag. + + * Don't choke on "foo= bar" or on formfeeds. + + * read.c: Allow Sun-syntax local labels #ifdef SUN_ASM_SYNTAX. + * m-sun3.h: Defined SUN_ASM_SYNTAX. + +(In version 1.29) Jay Fenlason (hack) + + * i386.c: Newer version that fixes a bug wherein a jump instruction + would be split between two frags. + + * i386*: New version. + + * m68k.c: #ifdef M_SUN and -m68010, produce Sun-2 executables. + +(In version 1.28) Jay Fenlason (hack) + + * m68k.c: Added .single pseudo-op. + + * Made ". = X" and ".set .,X" equivalent to ".org X". + The pseudo-symbol "." has the value of the location the assembler is + currently assembling to. + +(In version 1.27) Jay Fenlason (hack) + + * Merged ns32k and i386 support. + +(In version 1.26) Jay Fenlason (hack) + + * Added partial ns32k support. + + * Added RMS's evil .word misfeature. Invented the -k (kludge) option + to warn that this misfeature was used. + + * Modified some files to get rid of warnings from GCC. + + * Added fix so that / can also be a comment character by itself. + +(In version 1.25) Jay Fenlason (hack) + + * Installed patches for VMS. + + * as.h (SIZEOF_STRUCT_FRAG): Added space before backslash-newline. + + * messages.c: Fixed typo. + + * app.c: Handle : correctly. + + * error.c: Removed; no longer used. + + * m68k-opcode.h: Added fnop. + Fixed to correctly handle fmovem with a register list and + non-predecriment addressing mode. + + * m68k-opcode.h: Fixed to know about long form of FBcc insns. + + * write.c: Warn if a fixup ended up being wider than its field width. + +(In version 1.24) Jay Fenlason (hack) + + * Accept and ignore -mc68010 and -m68010 switches. + + * Correctly assemble long subroutine calls on the 68000 without using a + 68020-specific instruction. + + * When calling with no filenames, read stdin. + +(In version 1.23) Jay Fenlason (hack) + + * app.c: Rewritten. + + * xmalloc.c, xrealloc.c: Replaced to work with GCC. + +(In version 1.22) Jay Fenlason (hack) + + * write.c: Fixed a VMS bug. + + * m68k.c: Fixed a bug having to do with turning absolute into + PC-relative. + + * atof-m68k.c (atof_m68k, gen_to_words): Try to avoid a problem with + running off the end of the LITTLENUMS. + + * vax.c: Fixed so parenthesized expressions work. + + * atof-generic.c: Added a cast that fixes problems with some C + compilers. + +(In version 1.21) + + * Changes for VMS support and correct bitfield order for + cross-assembly. + +(In version 1.20) + + * m68k*: Fixed "fmovel #N, fpcr". Added fpcr and fpsr to the list of + registers. + +(In version 1.19) + + * m68k.c? (md_convert_frag): Don't put the fixups for absolute long to + PC-relative in the data segment. + + * atof-generic.c: #include <alloca.h> #ifdef sparc. + +(In version 1.18) + + * Re-fixed _vfprintf stuff (?). + + * Made "movem REG, ADDR" work. + + * Improved preprocessing, without temporary files. + +(In version 1.17) + + * Don't produce an undefined empty symbol for ".globl foo," (a line + ending with a comma). + + * Fixed a bug wherein ".long X" became ".long 0" on the Sparc. + + * Fixed a bug which caused many "#APP" "#NO_APP" pairs to dump core. + + * Fixed calls to _doprnt to call _vfprintf #ifndef NO_VARARGS. + +(In version 1.16) + + * Merged HP-UX changes from Chris Hanson (cph@zurich.ai.mit.edu). + + * flonum-multip.c: Renamed to flonum-mult.c. + + * m-hpux.h: Created. + + * m68k.c (bcopy): Fixed. + +(In version 1.15) + + * struct-symbol.h: Renamed to struc-symbol.h. + +(In version 1.14) + + * vax.c: Added a quick fix for the offset of fixed-width branches not + fitting in the field given. + + * gdb-lines.c, read.c: Added support for .gdline and .gdbline + pseudo-ops. + +(In version 1.13) + + * read.c, atof-generic.c: Fixed bugs in reading in floating-point + numbers. + + * m68k-opcode.h: Made "fmovep a0@, fp0" work. + +(In version 1.12) + + * write.c: Fixed an obscure bug in relaction that would occasionally + cause the assembler to stop relaxing when it really had at least one + more pass to do. + +(In version 1.11) + + * m68k*: Allow register lists in fmovem. + + * Added more floating-point exponents. + + * Print an error message on exponent overflow. + +(In version 1.10) + + * Fixed floating point bugs that made it generate incorrect numbers for + values over 10^16 or so. + +(In version 1.09) + + * Fixed bug wherein you couldn't forward reference local label 0. + +(In version 1.08) + + * m68k.c, m68k-opcode.h: Added support for fmovem with register lists. + + * Fixed an obscure bug having to do with generating PC-relative + addressing mode for things in the middle of the instruction instead of + at the end. + +Wed Mar 1 15:29:24 1989 Randall Smith (randy at apple-gunkies.ai.mit.edu) + + * *.*: Modified copyright notices to reflect new General Public + License. + + * Makefile: Added copyright notice. + +Fri Feb 17 09:42:01 1989 Jay Fenlason (hack at spiff) + + * Patched frags.c so that new frags start out bzero()ed. + +Thu Jan 26 14:23:44 1989 Jay Fenlason (hack at apple-gunkies.ai.mit.edu) + + * Added patches from pace to files as.h i386.c i386-opcode.h + imull foo,%eax no longer gets assembled into the 32-64 bit + multiply, which clobbers %edx behind gcc's back + + jcxz/jecxz were backwards + + There was a bug when using %ebp as a base register with no + displacement + + Instructions like andb $0xffffff, %al used to put out too many + immediate bytes + + The splitting jump instructions across frags could happen when + obstack_room()==6 too. + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +End: diff --git a/gas/Makefile.in b/gas/Makefile.in new file mode 100644 index 0000000..3f263d1 --- /dev/null +++ b/gas/Makefile.in @@ -0,0 +1,584 @@ +# Makefile for GNU Assembler +# Copyright (C) 1987, 1988, 1990, 1991 Free Software Foundation, Inc. + +#This file is part of GNU GAS. + +#GNU 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 1, or (at your option) +#any later version. + +#GNU 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 GNU GAS; see the file COPYING. If not, write to +#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +# $Id$ + +# The targets for external use include: +# all, doc, proto, install, uninstall, includes, TAGS, +# clean, cleanconfig, realclean, stage1, stage2, stage3, stage4. + +# Variables that exist for you to override. +# See below for how to change them for certain systems. + +ALLOCA = +CFLAGS = -g $(XCFLAGS) # -I$(srcdir)/../include +INTERNAL_CFLAGS = $(CROSS) +OLDCC = cc +BISON = bison +BISONFLAGS = -v +AR = ar +OLDAR_FLAGS = qc +AR_FLAGS = rc +SHELL = /bin/sh +# on sysV, define this as cp. +INSTALL = install -c +# These permit overriding just for certain files. +INSTALL_PROGRAM = $(INSTALL) +INSTALL_FILE = $(INSTALL) + +# Define this as & to perform parallel make on a Sequent. +# Note that this has some bugs, and it seems currently necessary +# to compile all the gen* files first by hand to avoid erroneous results. +P = + +# How to invoke ranlib. +RANLIB = ranlib +# Test to use to see whether ranlib exists on the system. +RANLIB_TEST = [ -f /usr/bin/ranlib -o -f /bin/ranlib ] + +# CFLAGS for use with OLDCC, for compiling gnulib. +# NOTE: -O does not work on some Unix systems! +CCLIBFLAGS = -O + +# Version of ar to use when compiling gnulib. +OLDAR = ar + +version=`./gcc -dumpversion` + +# Directory where sources are, from where we are. +srcdir = . +# Common prefix for installation directories. +# NOTE: This directory must exist when you start installation. +prefix = /usr/local +# Directory in which to put the executable for the command `gcc' +bindir = $(prefix)/bin +# Directory in which to put the directories used by the compiler. +libdir = $(prefix)/lib +# Directory in which the compiler finds executables, libraries, etc. +libsubdir = $(libdir)/gcc/$(target)/$(version) +# Number to put in man-page filename. +manext = 1 +# Directory in which to put man pages. +mandir = $(prefix)/man/man$(manext) + +# Additional system libraries to link with. +CLIB= + +# Change this to a null string if obstacks are installed in the +# system library. +OBSTACK=obstack.o + +# Specify the rule for actually making gnulib. +GNULIB = gnulib.portable + +# Specify the rule for actually making gnulib2. +GNULIB2 = gnulib2.portable + +# List of extra C and assembler files to add to gnulib. +# Assembler files should have names ending in `.asm'. +LIBFUNCS_EXTRA = + +# Program to convert libraries. +LIBCONVERT = + +# Control whether header files are installed. +INSTALL_HEADERS=install-headers + +# Change this to empty to prevent installing limits.h +LIMITS_H = limits.h + +# Directory to link to, when using the target `maketest'. +DIR = ../gcc + +# For better debugging under COFF, define SEPARATE_AUX_OUTPUT in config.h +# and define the following variable as `aux-output2.c' in make-... +AUX_OUTPUT2 = + +# Flags to use when cross-building GCC. +# Prefix to apply to names of object files when using them +# to run on the machine we are compiling on. +HOST_PREFIX= +# Prefix to apply to names of object files when compiling them +# to run on the machine we are compiling on. +# The default for this variable is chosen to keep these rules +# out of the way of the other rules for compiling the same source files. +HOST_PREFIX_1=loser- +HOST_CC=$(CC) +HOST_CFLAGS=$(ALL_CFLAGS) +HOST_LDFLAGS=$(LDFLAGS) +HOST_CPPFLAGS=$(CPPFLAGS) + +# Choose the real default target. +ALL=gas + +# End of variables for you to override. + +# Lists of files for various purposes. + +REAL_SOURCES = \ + $(srcdir)/app.c \ + $(srcdir)/as.c \ + $(srcdir)/atof-generic.c \ + $(srcdir)/bignum-copy.c \ + $(srcdir)/cond.c \ + $(srcdir)/expr.c \ + $(srcdir)/flonum-const.c \ + $(srcdir)/flonum-copy.c \ + $(srcdir)/flonum-mult.c \ + $(srcdir)/frags.c \ + $(srcdir)/hash.c \ + $(srcdir)/hex-value.c \ + $(srcdir)/input-file.c \ + $(srcdir)/input-scrub.c \ + $(srcdir)/messages.c \ + $(srcdir)/output-file.c \ + $(srcdir)/read.c \ + $(srcdir)/strstr.c \ + $(srcdir)/subsegs.c \ + $(srcdir)/symbols.c \ + $(srcdir)/version.c \ + $(srcdir)/write.c \ + $(srcdir)/xmalloc.c \ + $(srcdir)/xrealloc.c + +# in an expedient order +LINKED_SOURCES = \ + targ-cpu.c \ + obj-format.c \ + atof-targ.c + +SOURCES = $(LINKED_SOURCES) $(REAL_SOURCES) + +REAL_HEADERS = \ + $(srcdir)/as.h \ + $(srcdir)/bignum.h \ + $(srcdir)/expr.h \ + $(srcdir)/flonum.h \ + $(srcdir)/frags.h \ + $(srcdir)/hash.h \ + $(srcdir)/input-file.h \ + $(srcdir)/tc.h \ + $(srcdir)/obj.h \ + $(srcdir)/read.h \ + $(srcdir)/reloc.h \ + $(srcdir)/struc-symbol.h \ + $(srcdir)/subsegs.h \ + $(srcdir)/symbols.h \ + $(srcdir)/syscalls.h \ + $(srcdir)/write.h + +LINKED_HEADERS = \ + a.out.gnu.h \ + a.out.h \ + host.h \ + targ-env.h \ + targ-cpu.h \ + obj-format.h \ + atof-targ.h + +HEADERS = $(LINKED_HEADERS) $(REAL_HEADERS) + +OBJS = \ + targ-cpu.o \ + obj-format.o \ + atof-targ.o \ + app.o \ + as.o \ + atof-generic.o \ + bignum-copy.o \ + cond.o \ + expr.o \ + flonum-const.o \ + flonum-copy.o \ + flonum-mult.o \ + frags.o \ + hash.o \ + hex-value.o \ + input-file.o \ + input-scrub.o \ + messages.o \ + output-file.o \ + read.o \ + strstr.o \ + subsegs.o \ + symbols.o \ + version.o \ + write.o \ + xmalloc.o \ + xrealloc.o + +# Definition of `all' is here so that new rules inserted by sed +# do not specify the default target. +# The real definition is under `all.internal'. + +all: $(ALL) + +# sed inserts variable overrides after the following line. +#### + +# Now figure out from those variables how to compile and link. + +# This is the variable actually used when we compile. +ALL_CFLAGS = $(INTERNAL_CFLAGS) $(CFLAGS) + +# Even if ALLOCA is set, don't use it if compiling with GCC. +USE_ALLOCA= `if [ x"${CC}" = x"${OLDCC}" ] ; then echo ${ALLOCA}; else true; fi` +USE_HOST_ALLOCA= `if [ x"${CC}" = x"${OLDCC}" ] ; then echo ${HOST_PREFIX}${ALLOCA}; else true; fi` + +# Dependency on obstack, alloca, malloc or whatever library facilities +# are not installed in the system libraries. +# We don't use USE_ALLOCA because backquote expansion doesn't work in deps. +LIBDEPS= $(OBSTACK) $(ALLOCA) $(MALLOC) + +# Likewise, for use in the tools that must run on this machine +# even if we are cross-building GCC. +# We don't use USE_ALLOCA because backquote expansion doesn't work in deps. +HOST_LIBDEPS= $(HOST_PREFIX)$(OBSTACK) $(HOST_PREFIX)$(ALLOCA) $(HOST_PREFIX)$(MALLOC) + +# How to link with both our special library facilities +# and the system's installed libraries. +LIBS = $(OBSTACK) $(USE_ALLOCA) $(MALLOC) $(CLIB) + +# Likewise, for use in the tools that must run on this machine +# even if we are cross-building GCC. +HOST_LIBS = $(HOST_PREFIX)$(OBSTACK) $(USE_HOST_ALLOCA) $(HOST_PREFIX)$(MALLOC) $(CLIB) + +# Specify the directories to be searched for header files. +# Both . and srcdir are used, in that order, +# so that tm.h and config.h will be found in the compilation +# subdirectory rather than in the source directory. +INCLUDES = -I. -I$(srcdir) -I$(srcdir)/config +SUBDIR_INCLUDES = -I.. -I../$(srcdir) -I../$(srcdir)/config + +# Always use -I$(srcdir)/config when compiling. +.c.o: + $(CC) -c $(ALL_CFLAGS) $(CPPFLAGS) $(INCLUDES) $< + +# This tells GNU make version 3 not to export all the variables +# defined in this file into the environment. +.NOEXPORT: + +# Files to be copied away after each stage in building. +STAGE_GCC=gcc +STAGESTUFF = *.o gas + +# The files that "belong" in CONFIG_H are deliberately omitted +# because having them there would not be useful in actual practice. +# All they would do is cause complete recompilation every time +# one of the machine description files is edited. +# That may or may not be what one wants to do. +# If it is, rm *.o is an easy way to do it. +# CONFIG_H = config.h tm.h +CONFIG_H = + +gas: $(OBJS) $(LIBDEPS) + $(CC) $(ALL_CFLAGS) $(LDFLAGS) -o gas $(OBJS) $(LIBS) + +all.internal: native +# This is what is made with the host's compiler if making a cross assembler. +native: config.status gas + +config.status: + @echo You must configure gas. Look at the INSTALL file for details. + @false + +compilations: ${OBJS} + +# Compiling object files from source files. + +# Note that dependencies on obstack.h are not written +# because that file is not part of GAS. + +app.o : app.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h +as.o : as.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h +atof-generic.o : atof-generic.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h +bignum-copy.o : bignum-copy.c as.h host.h \ + targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h +cond.o : cond.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + obstack.h +debug.o : debug.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + subsegs.h +expr.o : expr.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + obstack.h +flonum-const.o : flonum-const.c flonum.h bignum.h +flonum-copy.o : flonum-copy.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h +flonum-mult.o : flonum-mult.c flonum.h bignum.h +frags.o : frags.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + subsegs.h obstack.h +hash.o : hash.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h +hex-value.o : hex-value.c +input-file.o : input-file.c as.h host.h \ + targ-env.h obj-format.h targ-cpu.h \ + struc-symbol.h reloc.h write.h flonum.h bignum.h expr.h \ + frags.h hash.h read.h symbols.h tc.h obj.h input-file.h +input-scrub.o : input-scrub.c /usr/include/errno.h /usr/include/sys/errno.h \ + as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + input-file.h +messages.o : messages.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h +obstack.o : obstack.c obstack.h +output-file.o : output-file.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + output-file.h +read.o : read.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + obstack.h +strstr.o : strstr.c +subsegs.o : subsegs.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + subsegs.h obstack.h +symbols.o : symbols.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + obstack.h subsegs.h +version.o : version.c +write.o : write.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h symbols.h tc.h obj.h \ + subsegs.h obstack.h output-file.h +xmalloc.o : xmalloc.c +xrealloc.o : xrealloc.c +atof-targ.o : atof-targ.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h \ + symbols.h tc.h obj.h +obj-format.o : obj-format.c as.h host.h targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h \ + symbols.h tc.h obj.h obstack.h +targ-cpu.o : targ-cpu.c targ-env.h obj-format.h \ + targ-cpu.h struc-symbol.h reloc.h \ + write.h flonum.h bignum.h expr.h frags.h hash.h read.h \ + symbols.h tc.h obj.h obstack.h + +# Normally this target is not used; but it is used if you +# define ALLOCA=alloca.o. In that case, you must get a suitable alloca.c +# from the GNU Emacs distribution. +# Note some machines won't allow $(CC) without -S on this source file. +alloca.o: alloca.c + $(CC) $(ALL_CFLAGS) $(CPPFLAGS) -S `echo $(srcdir)/alloca.c | sed 's,^\./,,'` + as alloca.s -o alloca.o + +# Compile the libraries to be used by gen*. +# If we are not cross-building, gen* use the same .o's that cc1 will use, +# and HOST_PREFIX_1 is `foobar', just to ensure these rules don't conflict +# with the rules for rtl.o, alloca.o, etc. +$(HOST_PREFIX_1)alloca.o: alloca.c + rm -f $(HOST_PREFIX)alloca.c + cp $(srcdir)/alloca.c $(HOST_PREFIX)alloca.c + $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(HOST_PREFIX)alloca.c + +$(HOST_PREFIX_1)obstack.o: obstack.c + rm -f $(HOST_PREFIX)obstack.c + cp $(srcdir)/obstack.c $(HOST_PREFIX)obstack.c + $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(HOST_PREFIX)obstack.c + +$(HOST_PREFIX_1)malloc.o: malloc.c + rm -f $(HOST_PREFIX)malloc.c + cp $(srcdir)/malloc.c $(HOST_PREFIX)malloc.c + $(HOST_CC) -c $(HOST_CFLAGS) $(HOST_CPPFLAGS) $(INCLUDES) $(HOST_PREFIX)malloc.c + +# Remake the info files. + +doc: $(srcdir)/gas.info + +$(srcdir)/gas.info: $(srcdir)/gas.texinfo + makeinfo `echo $(srcdir)/gas.texinfo | sed 's,^\./,,'` + + +# Deletion of files made during compilation. +# There are three levels of this: `clean', `cleanconfig' and `realclean'. +# `clean' deletes what you want to delete ordinarily to save space. +# This is most, but not all, of the files made by compilation. +# `cleanconfig' also deletes everything depending +# on the choice of config files. +# `realclean' also deletes everything that could be regenerated automatically. + +clean: + -rm -f $(STAGESTUFF) +# Delete the temporary source copies for cross compilation. + -rm -f $(HOST_PREFIX_1)alloca.c $(HOST_PREFIX_1)malloc.c + -rm -f $(HOST_PREFIX_1)obstack.c +# Delete the stamp files except stamp-gnulib2. + -rm -f core + +# Like clean but also delete the links made to configure gas. +cleanconfig: clean + -rm -f config.status Makefile host.h targ-env.h + -rm -f targ-cpu.h targ-cpu.c + -rm -f obj-format.h obj-format.c + -rm -f atof-targ.c + +# Get rid of every file that's generated from some other file (except INSTALL). +realclean: cleanconfig + -rm -f gas.aux gas.cps gas.fns gas.info gas.kys gas.pgs gas.tps gas.vrs + -rm -f TAGS + -rm -f gas.info* gas.?? gas.??s gas.log gas.toc gas.*aux + -rm -f *.dvi + +# Entry points `install', `includes' and `uninstall'. + +# Copy the files into directories where they will be run. +install: + $(INSTALL_PROGRAM) gas $(bindir)/as + +# Create the installation directory. +install-dir: + -mkdir $(libdir) + -mkdir $(libdir)/gcc + -mkdir $(libdir)/gcc/$(target) + -mkdir $(libdir)/gcc/$(target)/$(version) + +# Install the compiler executables built during cross compilation. +install-cross: native install-dir + -if [ -f cc1 ] ; then $(INSTALL_PROGRAM) cc1 $(libsubdir)/cc1; else true; fi + -if [ -f cc1plus ] ; then $(INSTALL_PROGRAM) cc1plus $(libsubdir)/cc1plus; else true; fi + $(INSTALL_PROGRAM) cpp $(libsubdir)/cpp + ./gcc -dumpspecs > $(libsubdir)/specs + $(INSTALL_PROGRAM) gcc $(bindir)/gcc + +# Install the man pages. +install-man: install-dir $(srcdir)/gcc.1 protoize.1 unprotoize.1 + $(INSTALL_FILE) $(srcdir)/gcc.1 $(mandir)/gcc.$(manext) + chmod a-x $(mandir)/gcc.$(manext) + $(INSTALL_FILE) $(srcdir)/protoize.1 $(mandir)/protoize.$(manext) + chmod a-x $(mandir)/protoize.$(manext) + $(INSTALL_FILE) $(srcdir)/unprotoize.1 $(mandir)/unprotoize.$(manext) + chmod a-x $(mandir)/unprotoize.$(manext) + +# Cancel installation by deleting the installed files. +uninstall: + -rm -rf $(libsubdir) + -rm -rf $(bindir)/gas + -rm -rf $(mandir)/gas.$(manext) + + +# These exist for maintenance purposes. + +tags TAGS: force + etags $(REAL_SOURCES) $(REAL_HEADERS) README Makefile config/*.[hc] + +bootstrap: gas force + $(MAKE) stage1 + $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage1/ $(CFLAGS)" libdir=$(libdir) ALLOCA= gas + $(MAKE) stage2 + $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage2/ $(CFLAGS)" libdir=$(libdir) ALLOCA= gas + for i in *.o; do cmp $$i stage2/$$i; done + +bootstrap2: force + $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage1/ $(CFLAGS)" libdir=$(libdir) ALLOCA= gas + $(MAKE) stage2 + $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage2/ $(CFLAGS)" libdir=$(libdir) ALLOCA= gas + for i in *.o; do cmp $$i stage2/$$i; done + +bootstrap3: force + $(MAKE) CC="$(CC)" CFLAGS="-O -Bstage2/ $(CFLAGS)" libdir=$(libdir) ALLOCA= gas + for i in *.o; do cmp $$i stage2/$$i; done + +# Copy the object files from a particular stage into a subdirectory. +stage1: force + -mkdir stage1 + -mv $(STAGESTUFF) stage1 + -(cd stage1 ; ln -s gas as) + +stage2: force + -mkdir stage2 + -mv $(STAGESTUFF) stage2 + -(cd stage2 ; ln -s gas as) + + +stage3: force + -mkdir stage3 + -mv $(STAGESTUFF) $(STAGE_GCC) stage3 + -rm -f stage3/gnulib + -cp gnulib stage3 + -if $(RANLIB_TEST) ; then $(RANLIB) stage3/gnulib; else true; fi + +stage4: force + -mkdir stage4 + -mv $(STAGESTUFF) $(STAGE_GCC) stage4 + -rm -f stage4/gnulib + -cp gnulib stage4 + -if $(RANLIB_TEST) ; then $(RANLIB) stage4/gnulib; else true; fi + +# Copy just the executable files from a particular stage into a subdirectory, +# and delete the object files. Use this if you're just verifying a version +# that is pretty sure to work, and you are short of disk space. +risky-stage1: force + -mkdir stage1 + -mv cc1 cpp cccp gcc stage1 + -rm -f stage1/gnulib + -cp gnulib stage1 && $(RANLIB) stage1/gnulib + -make clean + +risky-stage2: force + -mkdir stage2 + -mv cc1 cpp cccp gcc stage2 + -rm -f stage2/gnulib + -cp gnulib stage2 && $(RANLIB) stage2/gnulib + -make clean + +risky-stage3: force + -mkdir stage3 + -mv cc1 cpp cccp gcc stage3 + -rm -f stage3/gnulib + -cp gnulib stage3 && $(RANLIB) stage3/gnulib + -make clean + +risky-stage4: force + -mkdir stage4 + -mv cc1 cpp cccp gcc stage4 + -rm -f stage4/gnulib + -cp gnulib stage4 && $(RANLIB) stage4/gnulib + -make clean + +#In GNU Make, ignore whether `stage*' exists. +.PHONY: stage1 stage2 stage3 stage4 clean realclean TAGS bootstrap +.PHONY: risky-stage1 risky-stage2 risky-stage3 risky-stage4 + +force: + +Makefile: $(srcdir)/Makefile.in $(srcdir)/configure + (cd $(srcdir) ; configure -host=$(host) $(target)) diff --git a/gas/README b/gas/README new file mode 100644 index 0000000..2042639 --- /dev/null +++ b/gas/README @@ -0,0 +1,139 @@ +This is the beta-test version of the GNU assembler. (Probably +around Version 1.38, but check version.c which gets updated more +often than this readme.) + +These files are currently set up to allow you to compile all of the +versions of the assembler on the same machine. 'make all' compiles +all of them. The resulting executable names are: + + 68020 a68 + Vax avax + NS 32xxx a32k + Intel 80386 a386 + SPARC asparc + AMD 29000 asm29k + +The Makefile contains instructions on how to make one of the +assemblers compile as the default. + +Before you can compile the 68020 version of the assembler, you must +make m68k.h be a link to m-sun3.h , m-hpux.h or m-generic.h . If +you are on a SUN-3 (or other machine that uses a magic number of +(2 << 16) | OMAGIC type 'ln -s m-sun3.h m68k.h' else if you are on a +machine running HP-UX, type 'ln m-hpux.h m689k.h' else type +'ln -s m-generic.h m68k.h' If your machine does not support symbolic +links, omit the '-s'. + +See the instructions in the Makefile for compiling gas for the Sequent +Symmetry (dynix 3.0.12 + others?) or for the HP 9000/300 + +If your machine does not have both varargs.h and vfprintf(), but does have +_doprnt() add -DNO_VARARGS to the CFLAGS line in the makefile. If your +machine has neither vfprintf() or _doprnt(), you will have to change +messages.c in order to get readable error messages from the assembler. + +The assembler has been modified to support a feature that is +potentially useful when assembling compiler output, but which may +confuse assembly language programmers. If assembler encounters a +.word pseudo-op of the form symbol1-symbol2 (the difference of two +symbols), and the difference of those two symbols will not fit in 16 +bits, the assembler will create a branch around a long jump to +symbol1, and insert this into the output directly before the next +label: The .word will (instead of containing garbage, or giving an +error message) contain (the address of the long jump)-symbol2. This +allows the assembler to assemble jump tables that jump to locations +very far away into code that works properly. If the next label is +more than 32K away from the .word, you lose (silently); RMS claims +this will never happen. If the -k option is given, you will get a +warning message when this happens. + + + REPORTING BUGS IN GAS + +Bugs in gas should be reported to bug-gnu-utils@prep.ai.mit.edu If you can't +get through to prep, try hack@gnu.ai.mit.edu or hack@media-lab.media.mit.edu + +If you report a bug in GAS, please remember to include: + +A description of exactly what went wrong. + +The type of machine GAS was running on (VAX, 68020, etc), + +The Operating System GAS was running under. + +The options given to GAS. + +The actual input file that caused the problem. + +It is silly to report a bug in GAS without including an input file for +GAS. Don't ask us to generate the file just because you made it from +files you think we have access to. + +1. You might be mistaken. +2. It might take us a lot of time to install things to regenerate that file. +3. We might get a different file from the one you got, and might not see any +bug. + +To save us these delays and uncertainties, always send the input file +for the program that failed. + +If the input file is very large, and you are on the internet, you may +want to make it avaliable for anonymous FTP instead of mailing it. If you +do, include instructions for FTP'ing it in your bug report. + +------------------------------ README.APOLLO --------------------------------- + +The changes required to get the GNU C compiler running on +Apollo 68K platforms are available via anonymous ftp from +labrea.stanford.edu (36.8.0.47) in the form of a compressed +tar file named "/pub/gnu/apollo-gcc-1.37.tar.Z". +The size of the file is 84145 bytes. + +To build GCC for the Apollo you'll need the virgin FSF +distributions of bison-1.03, gas-1.34, and gcc-1.37. They +are also on labrea.stanford.edu as well as prep.ai.mit.edu. +My changes are to enable gas to produce Apollo COFF object +files and allow gcc to parse some of the syntax extensions +which appear in Apollo C header files. Note that the +COFF encapsulation technique cannot be used on the Apollo. + +The tar file should be unpacked in the directory containing +the gas-1.34 and gcc-1.37 directories; a few files will be overlaid, +and an APOLLO-GCC-README file will appear in the top directory. +This file contains detailed instructions on how to proceed. + +These changes will only work for SR10.1 or later systems, using +the 6.6 or later version of the Apollo C compiler. + +If you do not have ftp access, I can mail you the changes in the +form of diffs; they are approximately 40K in length. If you request +them, be sure to give me a voice phone number so I can contact you +in case I can't send you mail; I've had several requests in the +past from people I can't contact. + +By the way, I'm working on getting the GNU C++ compiler running; +there are a couple problems to solve. I hope to be able to announce +the Apollo version shortly after the 1.37 version is released. + +John Vasta Hewlett-Packard Apollo Systems Division +vasta@apollo.hp.com M.S. CHA-01-LT +(508) 256-6600 x6362 300 Apollo Drive, Chelmsford, MA 01824 +UUCP: {decwrl!decvax, mit-eddie, attunix}!apollo!vasta + +------------------------------------ + +You might refer others who are interested in a similar thing. + +Kevin Buchs buchs@mayo.edu + + +------------------------------ README.COFF ----------------------------------- + +If you have a COFF system, you may wish to aquire + + UUCP: osu-cis!~/gnu/coff/gnu-coff.tar.Z + or + FTP: tut.cis.ohio-state.edu:/pub/gnu/coff/gnu-coff.tar.Z + +These contain patches for gas that will make it produce COFF output. +I have never seen these patches, so I don't know how well they work. diff --git a/gas/app.c b/gas/app.c new file mode 100644 index 0000000..c806cb7 --- /dev/null +++ b/gas/app.c @@ -0,0 +1,508 @@ +/* Copyright (C) 1987, 1990, 1991 Free Software Foundation, Inc. + +Modified by Allen Wirfs-Brock, Instantiations Inc 2/90 +*/ +/* This is the Assembler Pre-Processor + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* App, the assembler pre-processor. This pre-processor strips out excess + spaces, turns single-quoted characters into a decimal constant, and turns + # <number> <filename> <garbage> into a .line <number>\n.app-file <filename> pair. + This needs better error-handling. + */ + +/* static const char rcsid[] = "$Id$"; */ + +#include <stdio.h> +#include "as.h" /* For BAD_CASE() only */ + +#if !defined(__STDC__) && !defined(const) +#define const /* Nothing */ +#endif + +static char lex [256]; +static char symbol_chars[] = + "$._ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +/* These will go in BSS if not defined elsewhere, producing empty strings. */ +extern const char comment_chars[]; +extern const char line_comment_chars[]; +extern const char line_separator_chars[]; + +#define LEX_IS_SYMBOL_COMPONENT 1 +#define LEX_IS_WHITESPACE 2 +#define LEX_IS_LINE_SEPARATOR 3 +#define LEX_IS_COMMENT_START 4 +#define LEX_IS_LINE_COMMENT_START 5 +#define LEX_IS_TWOCHAR_COMMENT_1ST 6 +#define LEX_IS_TWOCHAR_COMMENT_2ND 7 +#define LEX_IS_STRINGQUOTE 8 +#define LEX_IS_COLON 9 +#define LEX_IS_NEWLINE 10 +#define LEX_IS_ONECHAR_QUOTE 11 +#define IS_SYMBOL_COMPONENT(c) (lex [c] == LEX_IS_SYMBOL_COMPONENT) +#define IS_WHITESPACE(c) (lex [c] == LEX_IS_WHITESPACE) +#define IS_LINE_SEPARATOR(c) (lex [c] == LEX_IS_LINE_SEPARATOR) +#define IS_COMMENT(c) (lex [c] == LEX_IS_COMMENT_START) +#define IS_LINE_COMMENT(c) (lex [c] == LEX_IS_LINE_COMMENT_START) +#define IS_NEWLINE(c) (lex [c] == LEX_IS_NEWLINE) + +void do_scrub_begin() { + const char *p; + + lex [' '] = LEX_IS_WHITESPACE; + lex ['\t'] = LEX_IS_WHITESPACE; + lex ['\n'] = LEX_IS_NEWLINE; + lex [';'] = LEX_IS_LINE_SEPARATOR; + lex ['"'] = LEX_IS_STRINGQUOTE; + lex ['\''] = LEX_IS_ONECHAR_QUOTE; + lex [':'] = LEX_IS_COLON; + + /* Note that these override the previous defaults, e.g. if ';' + is a comment char, then it isn't a line separator. */ + for (p =symbol_chars;*p;++p) + lex[*p] = LEX_IS_SYMBOL_COMPONENT; + for (p=comment_chars;*p;p++) + lex[*p] = LEX_IS_COMMENT_START; + for (p=line_comment_chars;*p;p++) + lex[*p] = LEX_IS_LINE_COMMENT_START; + for (p=line_separator_chars;*p;p++) + lex[*p] = LEX_IS_LINE_SEPARATOR; + + /* Only allow slash-star comments if slash is not in use */ + if (lex['/'] == 0) { + lex ['/'] = LEX_IS_TWOCHAR_COMMENT_1ST; + lex ['*'] = LEX_IS_TWOCHAR_COMMENT_2ND; + } +} + +FILE *scrub_file; + +int scrub_from_file() { + return getc(scrub_file); +} + +void scrub_to_file(ch) +int ch; +{ + ungetc(ch,scrub_file); +} /* scrub_to_file() */ + +char *scrub_string; +char *scrub_last_string; + +int scrub_from_string() { + return scrub_string == scrub_last_string ? EOF : *scrub_string++; +} /* scrub_from_string() */ + +void scrub_to_string(ch) +int ch; +{ + *--scrub_string=ch; +} /* scrub_to_string() */ + +/* Saved state of the scrubber */ +static int state; +static int old_state; +static char *out_string; +static char out_buf[20]; +static int add_newlines = 0; + +/* Data structure for saving the state of app across #include's. Note that + app is called asynchronously to the parsing of the .include's, so our + state at the time .include is interpreted is completely unrelated. + That's why we have to save it all. */ + +struct app_save { + int state; + int old_state; + char *out_string; + char out_buf[sizeof (out_buf)]; + int add_newlines; + char *scrub_string; + char *scrub_last_string; + FILE *scrub_file; +}; + +char *app_push() { + register struct app_save *saved; + + saved = (struct app_save *) xmalloc(sizeof (*saved)); + saved->state = state; + saved->old_state = old_state; + saved->out_string = out_string; + bcopy(saved->out_buf, out_buf, sizeof(out_buf)); + saved->add_newlines = add_newlines; + saved->scrub_string = scrub_string; + saved->scrub_last_string = scrub_last_string; + saved->scrub_file = scrub_file; + + /* do_scrub_begin() is not useful, just wastes time. */ + return (char *)saved; +} + +void app_pop(arg) +char *arg; +{ + register struct app_save *saved = (struct app_save *)arg; + + /* There is no do_scrub_end (). */ + state = saved->state; + old_state = saved->old_state; + out_string = saved->out_string; + bcopy (out_buf, saved->out_buf, sizeof (out_buf)); + add_newlines = saved->add_newlines; + scrub_string = saved->scrub_string; + scrub_last_string = saved->scrub_last_string; + scrub_file = saved->scrub_file; + + free (arg); +} /* app_pop() */ + +int do_scrub_next_char(get,unget) +int (*get)(); +void (*unget)(); +{ + /*State 0: beginning of normal line + 1: After first whitespace on line (flush more white) + 2: After first non-white (opcode) on line (keep 1white) + 3: after second white on line (into operands) (flush white) + 4: after putting out a .line, put out digits + 5: parsing a string, then go to old-state + 6: putting out \ escape in a "d string. + 7: After putting out a .app-file, put out string. + 8: After putting out a .app-file string, flush until newline. + -1: output string in out_string and go to the state in old_state + -2: flush text until a '*' '/' is seen, then go to state old_state + */ + + register int ch, ch2; + + switch (state) { + case -1: + ch= *out_string++; + if(*out_string==0) { + state=old_state; + old_state=3; + } + return ch; + + case -2: + for(;;) { + do { + ch=(*get)(); + } while(ch!=EOF && ch!='\n' && ch!='*'); + if(ch=='\n' || ch==EOF) + return ch; + + /* At this point, ch must be a '*' */ + while ( (ch=(*get)()) == '*' ){ + ; + } + if(ch==EOF || ch=='/') + break; + (*unget)(ch); + } + state=old_state; + return ' '; + + case 4: + ch=(*get)(); + if(ch==EOF || (ch>='0' && ch<='9')) + return ch; + else { + while(ch!=EOF && IS_WHITESPACE(ch)) + ch=(*get)(); + if(ch=='"') { + (*unget)(ch); + out_string="\n.app-file "; + old_state=7; + state= -1; + return *out_string++; + } else { + while(ch!=EOF && ch!='\n') + ch=(*get)(); + return ch; + } + } + + case 5: + ch=(*get)(); + if(ch=='"') { + state=old_state; + return '"'; + } else if(ch=='\\') { + state=6; + return ch; + } else if(ch==EOF) { + as_warn("End of file in string: inserted '\"'"); + state=old_state; + (*unget)('\n'); + return '"'; + } else { + return ch; + } + + case 6: + state=5; + ch=(*get)(); + switch(ch) { + /* This is neet. Turn "string + more string" into "string\n more string" + */ + case '\n': + (*unget)('n'); + add_newlines++; + return '\\'; + + case '"': + case '\\': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': +#ifdef BACKSLASH_V + case 'v': +#endif /* BACKSLASH_V */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + break; + +#ifdef ONLY_STANDARD_ESCAPES + default: + as_warn("Unknown escape '\\%c' in string: Ignored",ch); + break; +#else /* ONLY_STANDARD_ESCAPES */ + default: + /* Accept \x as x for any x */ + break; +#endif /* ONLY_STANDARD_ESCAPES */ + + case EOF: + as_warn("End of file in string: '\"' inserted"); + return '"'; + } + return ch; + + case 7: + ch=(*get)(); + state=5; + old_state=8; + return ch; + + case 8: + do ch= (*get)(); + while(ch!='\n'); + state=0; + return ch; + } + + /* OK, we are somewhere in states 0 through 4 */ + +/* flushchar: */ + ch=(*get)(); + recycle: + if (ch == EOF) { + if (state != 0) + as_warn("End of file not at end of a line: Newline inserted."); + return ch; + } + + switch (lex[ch]) { + case LEX_IS_WHITESPACE: + do ch=(*get)(); + while(ch!=EOF && IS_WHITESPACE(ch)); + if(ch==EOF) + return ch; + if(IS_COMMENT(ch) || (state==0 && IS_LINE_COMMENT(ch)) || ch=='/' || IS_LINE_SEPARATOR(ch)) { + goto recycle; + } + switch (state) { + case 0: state++; goto recycle; /* Punted leading sp */ + case 1: BAD_CASE(state); /* We can't get here */ + case 2: state++; (*unget)(ch); return ' '; /* Sp after opco */ + case 3: goto recycle; /* Sp in operands */ + default: BAD_CASE(state); + } + break; + + case LEX_IS_TWOCHAR_COMMENT_1ST: + ch2=(*get)(); + if (ch2 != EOF && lex[ch2] == LEX_IS_TWOCHAR_COMMENT_2ND) { + for(;;) { + do { + ch2=(*get)(); + if(ch2 != EOF && IS_NEWLINE(ch2)) + add_newlines++; + } while(ch2!=EOF && + (lex[ch2] != LEX_IS_TWOCHAR_COMMENT_2ND)); + + while (ch2!=EOF && + (lex[ch2] == LEX_IS_TWOCHAR_COMMENT_2ND)){ + ch2=(*get)(); + } + + if(ch2==EOF + || lex[ch2] == LEX_IS_TWOCHAR_COMMENT_1ST) + break; + (*unget)(ch); + } + if(ch2==EOF) + as_warn("End of file in multiline comment"); + + ch = ' '; + goto recycle; + } else { + if(ch2!=EOF) + (*unget)(ch2); + return ch; + } + break; + + case LEX_IS_STRINGQUOTE: + old_state=state; + state=5; + return ch; + + case LEX_IS_ONECHAR_QUOTE: + ch=(*get)(); + if(ch==EOF) { + as_warn("End-of-file after a one-character quote; \000 inserted"); + ch=0; + } + sprintf(out_buf,"%d", (int)(unsigned char)ch); + + /* None of these 'x constants for us. We want 'x'. + */ + if ( (ch=(*get)()) != '\'' ) { +#ifdef REQUIRE_CHAR_CLOSE_QUOTE + as_warn("Missing close quote: (assumed)"); +#else + (*unget)(ch); +#endif + } + + old_state=state; + state= -1; + out_string=out_buf; + return *out_string++; + + case LEX_IS_COLON: + if(state!=3) + state=0; + return ch; + + case LEX_IS_NEWLINE: + /* Roll out a bunch of newlines from inside comments, etc. */ + if(add_newlines) { + --add_newlines; + (*unget)(ch); + } + /* fall thru into... */ + + case LEX_IS_LINE_SEPARATOR: + state=0; + return ch; + + case LEX_IS_LINE_COMMENT_START: + if (state != 0) /* Not at start of line, act normal */ + goto de_fault; + do ch=(*get)(); + while(ch!=EOF && IS_WHITESPACE(ch)); + if(ch==EOF) { + as_warn("EOF in comment: Newline inserted"); + return '\n'; + } + if(ch<'0' || ch>'9') { + /* Non-numerics: Eat whole comment line */ + while(ch!=EOF && !IS_NEWLINE(ch)) + ch=(*get)(); + if(ch==EOF) + as_warn("EOF in Comment: Newline inserted"); + state=0; + return '\n'; + } + /* Numerics begin comment. Perhaps CPP `# 123 "filename"' */ + (*unget)(ch); + old_state=4; + state= -1; + out_string=".line "; + return *out_string++; + + case LEX_IS_COMMENT_START: + do ch=(*get)(); + while(ch!=EOF && !IS_NEWLINE(ch)); + if(ch==EOF) + as_warn("EOF in comment: Newline inserted"); + state=0; + return '\n'; + + default: + de_fault: + /* Some relatively `normal' character. */ + if(state==0) { + state=2; /* Now seeing opcode */ + return ch; + } else if(state==1) { + state=2; /* Ditto */ + return ch; + } else { + return ch; /* Opcode or operands already */ + } + } + return -1; +} + +#ifdef TEST + +char comment_chars[] = "|"; +char line_comment_chars[] = "#"; + +main() +{ + int ch; + + app_begin(); + while((ch=do_scrub_next_char(stdin))!=EOF) + putc(ch,stdout); +} + +as_warn(str) +char *str; +{ + fputs(str,stderr); + putc('\n',stderr); +} +#endif + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of app.c */ diff --git a/gas/as.c b/gas/as.c new file mode 100644 index 0000000..a885f0d --- /dev/null +++ b/gas/as.c @@ -0,0 +1,361 @@ +/* as.c - GAS main program. + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +/* + * Main program for AS; a 32-bit assembler of GNU. + * Understands command arguments. + * Has a few routines that don't fit in other modules because they + * are shared. + * + * + * bugs + * + * : initialisers + * Since no-one else says they will support them in future: I + * don't support them now. + * + */ + +#include <stdio.h> +#include <string.h> + +#ifdef _POSIX_SOURCE +#include <sys/types.h> /* For pid_t in signal.h */ +#endif +#include <signal.h> + +#define COMMON + +#include "as.h" + +#ifdef __STDC__ + + /* This prototype for got_sig() is ansi. If you want + anything else, then your compiler is lying to you when + it says that it is __STDC__. If you want to change it, + #ifdef protect it from those of us with real ansi + compilers. */ + +#define SIGTY void + +static void got_sig(int sig); +static char *stralloc(char *str); +static void perform_an_assembly_pass(int argc, char **argv); + +#else /* __STDC__ */ + +#ifndef SIGTY +#define SIGTY int +#endif + +static SIGTY got_sig(); +static char *stralloc(); /* Make a (safe) copy of a string. */ +static void perform_an_assembly_pass(); + +#endif /* __STDC__ */ + +#ifdef DONTDEF +static char * gdb_symbol_file_name; +long gdb_begin(); +#endif + +char *myname; /* argv[0] */ +extern char version_string[]; + +int main(argc,argv) +int argc; +char **argv; +{ + int work_argc; /* variable copy of argc */ + char **work_argv; /* variable copy of argv */ + char *arg; /* an arg to program */ + char a; /* an arg flag (after -) */ + static const int sig[] = { SIGHUP, SIGINT, SIGPIPE, SIGTERM, 0}; + + for(a=0;sig[a]!=0;a++) + if(signal(sig[a], SIG_IGN) != SIG_IGN) + signal(sig[a], got_sig); + + myname=argv[0]; + bzero (flagseen, sizeof(flagseen)); /* aint seen nothing yet */ +#ifndef OBJ_DEFAULT_OUTPUT_FILE_NAME +#define OBJ_DEFAULT_OUTPUT_FILE_NAME "a.out" +#endif /* OBJ_DEFAULT_OUTPUT_FILE_NAME */ + out_file_name = OBJ_DEFAULT_OUTPUT_FILE_NAME; + + symbol_begin(); /* symbols.c */ + subsegs_begin(); /* subsegs.c */ + read_begin(); /* read.c */ + md_begin(); /* MACHINE.c */ + input_scrub_begin(); /* input_scrub.c */ +#ifdef DONTDEF + gdb_symbol_file_name = 0; +#endif + /* + * Parse arguments, but we are only interested in flags. + * When we find a flag, we process it then make it's argv[] NULL. + * This helps any future argv[] scanners avoid what we processed. + * Since it is easy to do here we interpret the special arg "-" + * to mean "use stdin" and we set that argv[] pointing to "". + * After we have munged argv[], the only things left are source file + * name(s) and ""(s) denoting stdin. These file names are used + * (perhaps more than once) later. + */ + /* FIXME-SOMEDAY this should use getopt. */ + work_argc = argc-1; /* don't count argv[0] */ + work_argv = argv+1; /* skip argv[0] */ + for (;work_argc--;work_argv++) { + arg = * work_argv; /* work_argv points to this argument */ + + if (*arg!='-') /* Filename. We need it later. */ + continue; /* Keep scanning args looking for flags. */ + if (arg[1] == '-' && arg[2] == 0) { + /* "--" as an argument means read STDIN */ + /* on this scan, we don't want to think about filenames */ + * work_argv = ""; /* Code that means 'use stdin'. */ + continue; + } + /* This better be a switch. */ + arg ++; /*->letter. */ + + while ((a = * arg) != '\0') {/* scan all the 1-char flags */ + arg ++; /* arg->after letter. */ + a &= 0x7F; /* ascii only please */ + if (flagseen[a]) + as_tsktsk("%s: Flag option - %c has already been seen.", myname, a); + flagseen[a] = 1; + switch (a) { + case 'f': + break; /* -f means fast - no need for "app" preprocessor. */ + + case 'D': + /* DEBUG is implemented: it debugs different */ + /* things to other people's assemblers. */ + break; + +#ifdef DONTDEF + case 'G': /* GNU AS switch: include gdbsyms. */ + if (*arg) /* Rest of argument is file-name. */ + gdb_symbol_file_name = stralloc (arg); + else if (work_argc) { /* Next argument is file-name. */ + work_argc --; + * work_argv = NULL; /* Not a source file-name. */ + gdb_symbol_file_name = * ++ work_argv; + } else + as_warn("%s: I expected a filename after -G", myname); + arg = ""; /* Finished with this arg. */ + break; +#endif + + case 'I': { /* Include file directory */ + + char *temp; + if (*arg) + temp = stralloc (arg); + else if (work_argc) { + * work_argv = NULL; + work_argc--; + temp = * ++ work_argv; + } else + as_warn("%s: I expected a filename after -I", myname); + add_include_dir (temp); + arg = ""; /* Finished with this arg. */ + break; + } + +#ifndef WORKING_DOT_WORD + case 'k': + break; +#endif + + case 'L': /* -L means keep L* symbols */ + break; + + case 'o': + if (*arg) /* Rest of argument is object file-name. */ + out_file_name = stralloc (arg); + else if (work_argc) { /* Want next arg for a file-name. */ + * work_argv = NULL; /* This is not a file-name. */ + work_argc--; + out_file_name = * ++ work_argv; + } else + as_warn("%s: I expected a filename after -o. \"%s\" assumed.", myname, out_file_name); + arg = ""; /* Finished with this arg. */ + break; + + case 'R': + /* -R means put data into text segment */ + break; + + case 'v': +#ifdef VMS + { + extern char *compiler_version_string; + compiler_version_string = arg; + } +#else /* not VMS */ + fprintf(stderr,version_string); + if(*arg && strcmp(arg,"ersion")) + as_warn("Unknown -v option ignored"); +#endif + while(*arg) arg++; /* Skip the rest */ + break; + + case 'W': + /* -W means don't warn about things */ + case 'X': + /* -X means treat warnings as errors */ + case 'Z': + /* -Z means attempt to generate object file even after errors. */ + break; + + default: + --arg; + if(md_parse_option(&arg,&work_argc,&work_argv)==0) + as_warn("%s: I don't understand '%c' flag.", myname, a); + if(arg && *arg) + arg++; + break; + } + } + /* + * We have just processed a "-..." arg, which was not a + * file-name. Smash it so the + * things that look for filenames won't ever see it. + * + * Whatever work_argv points to, it has already been used + * as part of a flag, so DON'T re-use it as a filename. + */ + *work_argv = NULL; /* NULL means 'not a file-name' */ + } +#ifdef DONTDEF + if (gdb_begin(gdb_symbol_file_name) == 0) + flagseen ['G'] = 0; /* Don't do any gdbsym stuff. */ +#endif + /* Here with flags set up in flagseen[]. */ + perform_an_assembly_pass(argc,argv); /* Assemble it. */ +#ifdef TC_I960 + brtab_emit(); +#endif + if (seen_at_least_1_file() + && !((had_warnings() && flagseen['Z']) + || had_errors() > 0)) { + write_object_file(); /* relax() addresses then emit object file */ + } /* we also check in write_object_file() just before emit. */ + + input_scrub_end(); + md_end(); /* MACHINE.c */ + +#ifndef VMS + return((had_warnings() && flagseen['Z']) + || had_errors() > 0); /* WIN */ +#else /* VMS */ + return(!((had_warnings() && flagseen['Z']) + || had_errors() > 0)); /* WIN */ +#endif /* VMS */ + +} /* main() */ + + +/* perform_an_assembly_pass() + * + * Here to attempt 1 pass over each input file. + * We scan argv[*] looking for filenames or exactly "" which is + * shorthand for stdin. Any argv that is NULL is not a file-name. + * We set need_pass_2 TRUE if, after this, we still have unresolved + * expressions of the form (unknown value)+-(unknown value). + * + * Note the un*x semantics: there is only 1 logical input file, but it + * may be a catenation of many 'physical' input files. + */ +static void perform_an_assembly_pass(argc, argv) +int argc; +char **argv; +{ + int saw_a_file = 0; + + text_fix_root = NULL; + data_fix_root = NULL; + need_pass_2 = 0; + + subseg_new (SEG_TEXT, 0); + + argv++; /* skip argv[0] */ + argc--; /* skip argv[0] */ + while (argc--) { + if (*argv) { /* Is it a file-name argument? */ + saw_a_file++; + /* argv->"" if stdin desired, else->filename */ + read_a_source_file(*argv); + } + argv++; /* completed that argv */ + } + if(!saw_a_file) + read_a_source_file(""); +} /* perform_an_assembly_pass() */ + +/* + * stralloc() + * + * Allocate memory for a new copy of a string. Copy the string. + * Return the address of the new string. Die if there is any error. + */ + +static char * +stralloc (str) +char * str; +{ + register char * retval; + register long len; + + len = strlen (str) + 1; + retval = xmalloc (len); + (void) strcpy(retval, str); + return(retval); +} + +#ifdef comment +static void lose() { + as_fatal("%s: 2nd pass not implemented - get your code from random(3)", myname); + return; +} /* lose() */ +#endif /* comment */ + +static SIGTY +got_sig(sig) +int sig; +{ + static here_before = 0; + + as_bad("Interrupted by signal %d", sig); + if(here_before++) + exit(1); + return((SIGTY) 0); +} + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end: as.c */ diff --git a/gas/as.h b/gas/as.h new file mode 100644 index 0000000..f956893 --- /dev/null +++ b/gas/as.h @@ -0,0 +1,397 @@ +/* as.h - global header file + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#define GAS 1 + +#include "host.h" +#include "flonum.h" + +#ifndef __STDC__ +#define volatile /**/ +#ifndef const +#define const /**/ +#endif /* const */ +#endif /* __STDC__ */ + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#define register +#endif /* __GNUC__ */ + +#ifndef __LINE__ +#define __LINE__ "unknown" +#endif /* __LINE__ */ + +#ifndef __FILE__ +#define __FILE__ "unknown" +#endif /* __FILE__ */ + +/* + * I think this stuff is largely out of date. xoxorich. + * + * CAPITALISED names are #defined. + * "lowercaseH" is #defined if "lowercase.h" has been #include-d. + * "lowercaseT" is a typedef of "lowercase" objects. + * "lowercaseP" is type "pointer to object of type 'lowercase'". + * "lowercaseS" is typedef struct ... lowercaseS. + * + * #define DEBUG to enable all the "know" assertion tests. + * #define SUSPECT when debugging. + * #define COMMON as "extern" for all modules except one, where you #define + * COMMON as "". + * If TEST is #defined, then we are testing a module: #define COMMON as "". + */ + +/* These #defines are for parameters of entire assembler. */ + +/* #define SUSPECT JF remove for speed testing */ +/* These #includes are for type definitions etc. */ + +#include <stdio.h> +#include <assert.h> + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free xfree + +#define BAD_CASE(value) \ +{ \ + as_fatal("Case value %d unexpected at line %d of file \"%s\"\n", \ + value, __LINE__, __FILE__); \ +} + + +/* These are assembler-wide concepts */ + + +#ifndef COMMON +#ifdef TEST +#define COMMON /* declare our COMMONs storage here. */ +#else +#define COMMON extern /* our commons live elswhere */ +#endif +#endif + /* COMMON now defined */ +#define DEBUG /* temporary */ + +#ifdef DEBUG +#undef NDEBUG +#define know(p) assert(p) /* Verify our assumptions! */ +#else +#define know(p) /* know() checks are no-op.ed */ +#endif + + +#define xfree free + +/* input_scrub.c */ + +/* + * Supplies sanitised buffers to read.c. + * Also understands printing line-number part of error messages. + */ + + +/* subsegs.c Sub-segments. Also, segment(=expression type)s.*/ + +/* + * This table describes the use of segments as EXPRESSION types. + * + * X_seg X_add_symbol X_subtract_symbol X_add_number + * SEG_ABSENT no (legal) expression + * SEG_PASS1 no (defined) " + * SEG_BIG * > 32 bits const. + * SEG_ABSOLUTE 0 + * SEG_DATA * 0 + * SEG_TEXT * 0 + * SEG_BSS * 0 + * SEG_UNKNOWN * 0 + * SEG_DIFFERENCE 0 * 0 + * SEG_REGISTER * + * + * The blank fields MUST be 0, and are nugatory. + * The '0' fields MAY be 0. The '*' fields MAY NOT be 0. + * + * SEG_BIG: X_add_number is < 0 if the result is in + * generic_floating_point_number. The value is -'c' where c is the + * character that introduced the constant. e.g. "0f6.9" will have -'f' + * as a X_add_number value. + * X_add_number > 0 is a count of how many littlenums it took to + * represent a bignum. + * SEG_DIFFERENCE: + * If segments of both symbols are known, they are the same segment. + * X_add_symbol != X_sub_symbol (then we just cancel them, => SEG_ABSOLUTE). + */ + +typedef enum { + SEG_ABSOLUTE = 0, + SEG_TEXT, + SEG_DATA, + SEG_BSS, + SEG_UNKNOWN, + SEG_ABSENT, /* Mythical Segment (absent): NO expression seen. */ + SEG_PASS1, /* Mythical Segment: Need another pass. */ + SEG_GOOF, /* Only happens if AS has a logic error. */ + /* Invented so we don't crash printing */ + /* error message involving weird segment. */ + SEG_BIG, /* Bigger than 32 bits constant. */ + SEG_DIFFERENCE, /* Mythical Segment: absolute difference. */ + SEG_DEBUG, /* Debug segment */ + SEG_NTV, /* Transfert vector preload segment */ + SEG_PTV, /* Transfert vector postload segment */ + SEG_REGISTER, /* Mythical: a register-valued expression */ +} segT; + +#define SEG_MAXIMUM_ORDINAL (SEG_REGISTER) + +typedef int subsegT; + +COMMON subsegT now_subseg; + /* What subseg we are accreting now? */ + + +COMMON segT now_seg; + /* Segment our instructions emit to. */ + /* Only OK values are SEG_TEXT or SEG_DATA. */ + + +extern char *const seg_name[]; +extern int section_alignment[]; + + +/* relax() */ + +typedef enum +{ + rs_fill, /* Variable chars to be repeated fr_offset */ + /* times. Fr_symbol unused. */ + /* Used with fr_offset == 0 for a constant */ + /* length frag. */ + + rs_align, /* Align: Fr_offset: power of 2. */ + /* 1 variable char: fill character. */ + rs_org, /* Org: Fr_offset, fr_symbol: address. */ + /* 1 variable char: fill character. */ + + rs_machine_dependent, +#ifndef WORKING_DOT_WORD + rs_broken_word, /* JF: gunpoint */ +#endif +} +relax_stateT; + +/* typedef unsigned char relax_substateT; */ +/* JF this is more likely to leave the end of a struct frag on an align + boundry. Be very careful with this. */ +typedef unsigned long relax_substateT; + +typedef unsigned long relax_addressT;/* Enough bits for address. */ + /* Still an integer type. */ + + +/* frags.c */ + +/* + * A code fragment (frag) is some known number of chars, followed by some + * unknown number of chars. Typically the unknown number of chars is an + * instruction address whose size is yet unknown. We always know the greatest + * possible size the unknown number of chars may become, and reserve that + * much room at the end of the frag. + * Once created, frags do not change address during assembly. + * We chain the frags in (a) forward-linked list(s). The object-file address + * of the 1st char of a frag is generally not known until after relax(). + * Many things at assembly time describe an address by {object-file-address + * of a particular frag}+offset. + + BUG: it may be smarter to have a single pointer off to various different +notes for different frag kinds. See how code pans + */ +struct frag /* a code fragment */ +{ + unsigned long fr_address; /* Object file address. */ + struct frag *fr_next; /* Chain forward; ascending address order. */ + /* Rooted in frch_root. */ + + long fr_fix; /* (Fixed) number of chars we know we have. */ + /* May be 0. */ + long fr_var; /* (Variable) number of chars after above. */ + /* May be 0. */ + struct symbol *fr_symbol; /* For variable-length tail. */ + long fr_offset; /* For variable-length tail. */ + char *fr_opcode; /*->opcode low addr byte,for relax()ation*/ + relax_stateT fr_type; /* What state is my tail in? */ + relax_substateT fr_subtype; + /* These are needed only on the NS32K machines */ + char fr_pcrel_adjust; + char fr_bsr; + char fr_literal [1]; /* Chars begin here. */ + /* One day we will compile fr_literal[0]. */ +}; +#define SIZEOF_STRUCT_FRAG \ + ((int)zero_address_frag.fr_literal-(int)&zero_address_frag) + /* We want to say fr_literal[0] above. */ + +typedef struct frag fragS; + +COMMON fragS * frag_now; /* -> current frag we are building. */ + /* This frag is incomplete. */ + /* It is, however, included in frchain_now. */ + /* Frag_now->fr_fix is bogus. Use: */ +/* Virtual frag_now->fr_fix==obstack_next_free(&frags)-frag_now->fr_literal.*/ + +COMMON fragS zero_address_frag; /* For foreign-segment symbol fixups. */ +COMMON fragS bss_address_frag; /* For local common (N_BSS segment) fixups. */ + +/* main program "as.c" (command arguments etc) */ + +COMMON char +flagseen[128]; /* ['x'] TRUE if "-x" seen. */ + +COMMON char * +out_file_name; /* name of emitted object file */ + +COMMON int need_pass_2; /* TRUE if we need a second pass. */ + +typedef struct { + char * poc_name; /* assembler mnemonic, lower case, no '.' */ + void (*poc_handler)(); /* Do the work */ + int poc_val; /* Value to pass to handler */ +} pseudo_typeS; + +#if defined(__STDC__) & !defined(NO_STDARG) + +int had_errors(void); +int had_warnings(void); +void as_bad(const char *Format, ...); +void as_fatal(const char *Format, ...); +void as_tsktsk(const char *Format, ...); +void as_warn(const char *Format, ...); + +#else + +int had_errors(); +int had_warnings(); +void as_bad(); +void as_fatal(); +void as_tsktsk(); +void as_warn(); + +#endif /* __STDC__ & !NO_STDARG */ + +#ifdef __STDC__ + +char *app_push(void); +char *atof_ieee(char *str, int what_kind, LITTLENUM_TYPE *words); +char *input_scrub_include_file(char *filename, char *position); +char *input_scrub_new_file(char *filename); +char *input_scrub_next_buffer(char **bufp); +char *strstr(const char *s, const char *wanted); +char *xmalloc(int size); +char *xrealloc(char *ptr, long n); +int do_scrub_next_char(int (*get)(), void (*unget)()); +int gen_to_words(LITTLENUM_TYPE *words, int precision, long exponent_bits); +int had_err(void); +int had_errors(void); +int had_warnings(void); +int ignore_input(void); +int scrub_from_file(void); +int scrub_from_file(void); +int scrub_from_string(void); +int seen_at_least_1_file(void); +void app_pop(char *arg); +void as_howmuch(FILE *stream); +void as_perror(char *gripe, char *filename); +void as_where(void); +void bump_line_counters(void); +void do_scrub_begin(void); +void input_scrub_begin(void); +void input_scrub_close(void); +void input_scrub_end(void); +void int_to_gen(long x); +void new_logical_line(char *fname, int line_number); +void scrub_to_file(int ch); +void scrub_to_string(int ch); +void subseg_change(segT seg, int subseg); +void subseg_new(segT seg, subsegT subseg); +void subsegs_begin(void); + +#else /* __STDC__ */ + +char *app_push(); +char *atof_ieee(); +char *input_scrub_include_file(); +char *input_scrub_new_file(); +char *input_scrub_next_buffer(); +char *strstr(); +char *xmalloc(); +char *xrealloc(); +int do_scrub_next_char(); +int gen_to_words(); +int had_err(); +int had_errors(); +int had_warnings(); +int ignore_input(); +int scrub_from_file(); +int scrub_from_file(); +int scrub_from_string(); +int seen_at_least_1_file(); +void app_pop(); +void as_howmuch(); +void as_perror(); +void as_where(); +void bump_line_counters(); +void do_scrub_begin(); +void input_scrub_begin(); +void input_scrub_close(); +void input_scrub_end(); +void int_to_gen(); +void new_logical_line(); +void scrub_to_file(); +void scrub_to_string(); +void subseg_change(); +void subseg_new(); +void subsegs_begin(); + +#endif /* __STDC__ */ + + /* this one starts the chain of target dependant headers */ +#include "targ-env.h" + + /* these define types needed by the interfaces */ +#include "struc-symbol.h" +#include "reloc.h" +#include "write.h" +#include "expr.h" +#include "frags.h" +#include "hash.h" +#include "read.h" +#include "symbols.h" + +#include "tc.h" +#include "obj.h" + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end: as.h */ diff --git a/gas/atof-generic.c b/gas/atof-generic.c new file mode 100644 index 0000000..7d2d8f4 --- /dev/null +++ b/gas/atof-generic.c @@ -0,0 +1,549 @@ +/* atof_generic.c - turn a string of digits into a Flonum + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include <ctype.h> +#include <string.h> + +#include "as.h" + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else +#ifdef sparc +#include <alloca.h> +#endif +#endif + +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#endif + +/* #define FALSE (0) */ +/* #define TRUE (1) */ + +/***********************************************************************\ +* * +* Given a string of decimal digits , with optional decimal * +* mark and optional decimal exponent (place value) of the * +* lowest_order decimal digit: produce a floating point * +* number. The number is 'generic' floating point: our * +* caller will encode it for a specific machine architecture. * +* * +* Assumptions * +* uses base (radix) 2 * +* this machine uses 2's complement binary integers * +* target flonums use " " " " * +* target flonums exponents fit in a long * +* * +\***********************************************************************/ + +/* + + Syntax: + +<flonum> ::= <optional-sign> <decimal-number> <optional-exponent> +<optional-sign> ::= '+' | '-' | {empty} +<decimal-number> ::= <integer> + | <integer> <radix-character> + | <integer> <radix-character> <integer> + | <radix-character> <integer> +<optional-exponent> ::= {empty} | <exponent-character> <optional-sign> <integer> +<integer> ::= <digit> | <digit> <integer> +<digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' +<exponent-character> ::= {one character from "string_of_decimal_exponent_marks"} +<radix-character> ::= {one character from "string_of_decimal_marks"} + +*/ + +int /* 0 if OK */ +atof_generic ( + address_of_string_pointer, /* return pointer to just AFTER number we read. */ + string_of_decimal_marks, /* At most one per number. */ + string_of_decimal_exponent_marks, + address_of_generic_floating_point_number) + + char * * address_of_string_pointer; + const char * string_of_decimal_marks; + const char * string_of_decimal_exponent_marks; + FLONUM_TYPE * address_of_generic_floating_point_number; + +{ + + int return_value; /* 0 means OK. */ + char * first_digit; + /* char * last_digit; JF unused */ + int number_of_digits_before_decimal; + int number_of_digits_after_decimal; + long decimal_exponent; + int number_of_digits_available; + char digits_sign_char; + + { + /* + * Scan the input string, abstracting (1)digits (2)decimal mark (3) exponent. + * It would be simpler to modify the string, but we don't; just to be nice + * to caller. + * We need to know how many digits we have, so we can allocate space for + * the digits' value. + */ + + char * p; + char c; + int seen_significant_digit; + + first_digit = * address_of_string_pointer; + c= *first_digit; + if (c=='-' || c=='+') + { + digits_sign_char = c; + first_digit ++; + } + else + digits_sign_char = '+'; + + if( (first_digit[0]=='n' || first_digit[0]=='N') + && (first_digit[1]=='a' || first_digit[1]=='A') + && (first_digit[2]=='n' || first_digit[2]=='N')) { + address_of_generic_floating_point_number->sign=0; + address_of_generic_floating_point_number->exponent=0; + address_of_generic_floating_point_number->leader=address_of_generic_floating_point_number->low; + (*address_of_string_pointer)=first_digit+3; + return 0; + } + if( (first_digit[0]=='i' || first_digit[0]=='I') + && (first_digit[1]=='n' || first_digit[1]=='N') + && (first_digit[2]=='f' || first_digit[2]=='F')) { + address_of_generic_floating_point_number->sign= digits_sign_char=='+' ? 'P' : 'N'; + address_of_generic_floating_point_number->exponent=0; + address_of_generic_floating_point_number->leader=address_of_generic_floating_point_number->low; + if( (first_digit[3]=='i' || first_digit[3]=='I') + && (first_digit[4]=='n' || first_digit[4]=='N') + && (first_digit[5]=='i' || first_digit[5]=='I') + && (first_digit[6]=='t' || first_digit[6]=='T') + && (first_digit[7]=='y' || first_digit[7]=='Y')) + (*address_of_string_pointer)=first_digit+8; + else + (*address_of_string_pointer)=first_digit+3; + return 0; + } + + number_of_digits_before_decimal = 0; + number_of_digits_after_decimal = 0; + decimal_exponent = 0; + seen_significant_digit = 0; + for (p = first_digit; + ((c = * p) != '\0') + && (!c || ! strchr (string_of_decimal_marks, c) ) + && (!c || ! strchr (string_of_decimal_exponent_marks, c) ); + p ++) + { + if (isdigit(c)) + { + if (seen_significant_digit || c > '0') + { + number_of_digits_before_decimal ++; + seen_significant_digit = 1; + } + else + { + first_digit++; + } + } + else + { + break; /* p -> char after pre-decimal digits. */ + } + } /* For each digit before decimal mark. */ + +#ifndef OLD_FLOAT_READS + /* Ignore trailing 0's after the decimal point. The original code here + * (ifdef'd out) does not do this, and numbers like + * 4.29496729600000000000e+09 (2**31) + * come out inexact for some reason related to length of the digit + * string. + */ + if ( c && strchr(string_of_decimal_marks,c) ){ + int zeros = 0; /* Length of current string of zeros */ + + for ( p++; (c = *p) && isdigit(c); p++ ){ + if ( c == '0'){ + zeros++; + } else { + number_of_digits_after_decimal += 1 + zeros; + zeros = 0; + } + } + } +#else + if (c && strchr (string_of_decimal_marks, c)) + { + for (p ++; + ((c = * p) != '\0') + && (!c || ! strchr (string_of_decimal_exponent_marks, c) ); + p ++) + { + if (isdigit(c)) + { + number_of_digits_after_decimal ++; /* This may be retracted below. */ + if (/* seen_significant_digit || */ c > '0') + { + seen_significant_digit = TRUE; + } + } + else + { + if ( ! seen_significant_digit) + { + number_of_digits_after_decimal = 0; + } + break; + } + } /* For each digit after decimal mark. */ + } + while(number_of_digits_after_decimal && first_digit[number_of_digits_before_decimal+number_of_digits_after_decimal]=='0') + --number_of_digits_after_decimal; +/* last_digit = p; JF unused */ +#endif + + if (c && strchr (string_of_decimal_exponent_marks, c) ) + { + char digits_exponent_sign_char; + + c = * ++ p; + if (c && strchr ("+-",c)) + { + digits_exponent_sign_char = c; + c = * ++ p; + } + else + { + digits_exponent_sign_char = '+'; + } + for (; + (c); + c = * ++ p) + { + if (isdigit(c)) + { + decimal_exponent = decimal_exponent * 10 + c - '0'; + /* + * BUG! If we overflow here, we lose! + */ + } + else + { + break; + } + } + if (digits_exponent_sign_char == '-') + { + decimal_exponent = - decimal_exponent; + } + } + * address_of_string_pointer = p; + } + + number_of_digits_available = + number_of_digits_before_decimal + + number_of_digits_after_decimal; + return_value = 0; + if (number_of_digits_available == 0) + { + address_of_generic_floating_point_number -> exponent = 0; /* Not strictly necessary */ + address_of_generic_floating_point_number -> leader + = -1 + address_of_generic_floating_point_number -> low; + address_of_generic_floating_point_number -> sign = digits_sign_char; + /* We have just concocted (+/-)0.0E0 */ + } + else + { + LITTLENUM_TYPE * digits_binary_low; + int precision; + int maximum_useful_digits; + int number_of_digits_to_use; + int more_than_enough_bits_for_digits; + int more_than_enough_littlenums_for_digits; + int size_of_digits_in_littlenums; + int size_of_digits_in_chars; + FLONUM_TYPE power_of_10_flonum; + FLONUM_TYPE digits_flonum; + + + precision = (address_of_generic_floating_point_number -> high + - address_of_generic_floating_point_number -> low + + 1 + ); /* Number of destination littlenums. */ + /* Includes guard bits (two littlenums worth) */ + maximum_useful_digits = ( ((double) (precision - 2)) + * ((double) (LITTLENUM_NUMBER_OF_BITS)) + / (LOG_TO_BASE_2_OF_10) + ) + + 2; /* 2 :: guard digits. */ + if (number_of_digits_available > maximum_useful_digits) + { + number_of_digits_to_use = maximum_useful_digits; + } + else + { + number_of_digits_to_use = number_of_digits_available; + } + decimal_exponent += number_of_digits_before_decimal - number_of_digits_to_use; + + more_than_enough_bits_for_digits + = ((((double)number_of_digits_to_use) * LOG_TO_BASE_2_OF_10) + 1); + more_than_enough_littlenums_for_digits + = ( more_than_enough_bits_for_digits + / LITTLENUM_NUMBER_OF_BITS + ) + + 2; + + /* + * Compute (digits) part. In "12.34E56" this is the "1234" part. + * Arithmetic is exact here. If no digits are supplied then + * this part is a 0 valued binary integer. + * Allocate room to build up the binary number as littlenums. + * We want this memory to disappear when we leave this function. + * Assume no alignment problems => (room for n objects) == + * n * (room for 1 object). + */ + + size_of_digits_in_littlenums = more_than_enough_littlenums_for_digits; + size_of_digits_in_chars = size_of_digits_in_littlenums + * sizeof( LITTLENUM_TYPE ); + digits_binary_low = (LITTLENUM_TYPE *) + alloca (size_of_digits_in_chars); + bzero ((char *)digits_binary_low, size_of_digits_in_chars); + + /* Digits_binary_low[] is allocated and zeroed. */ + + { + /* + * Parse the decimal digits as if * digits_low was in the units position. + * Emit a binary number into digits_binary_low[]. + * + * Use a large-precision version of: + * (((1st-digit) * 10 + 2nd-digit) * 10 + 3rd-digit ...) * 10 + last-digit + */ + + char * p; + char c; + int count; /* Number of useful digits left to scan. */ + + for (p = first_digit, count = number_of_digits_to_use; + count; + p ++, -- count) + { + c = * p; + if (isdigit(c)) + { + /* + * Multiply by 10. Assume can never overflow. + * Add this digit to digits_binary_low[]. + */ + + long carry; + LITTLENUM_TYPE * littlenum_pointer; + LITTLENUM_TYPE * littlenum_limit; + + littlenum_limit + = digits_binary_low + + more_than_enough_littlenums_for_digits + - 1; + carry = c - '0'; /* char -> binary */ + for (littlenum_pointer = digits_binary_low; + littlenum_pointer <= littlenum_limit; + littlenum_pointer ++) + { + long work; + + work = carry + 10 * (long)(*littlenum_pointer); + * littlenum_pointer = work & LITTLENUM_MASK; + carry = work >> LITTLENUM_NUMBER_OF_BITS; + } + if (carry != 0) + { + /* + * We have a GROSS internal error. + * This should never happen. + */ + abort(); /* RMS prefers abort() to any message. */ + } + } + else + { + ++ count; /* '.' doesn't alter digits used count. */ + } /* if valid digit */ + } /* for each digit */ + } + + /* + * Digits_binary_low[] properly encodes the value of the digits. + * Forget about any high-order littlenums that are 0. + */ + while (digits_binary_low [size_of_digits_in_littlenums - 1] == 0 + && size_of_digits_in_littlenums >= 2) + size_of_digits_in_littlenums --; + + digits_flonum . low = digits_binary_low; + digits_flonum . high = digits_binary_low + size_of_digits_in_littlenums - 1; + digits_flonum . leader = digits_flonum . high; + digits_flonum . exponent = 0; + /* + * The value of digits_flonum . sign should not be important. + * We have already decided the output's sign. + * We trust that the sign won't influence the other parts of the number! + * So we give it a value for these reasons: + * (1) courtesy to humans reading/debugging + * these numbers so they don't get excited about strange values + * (2) in future there may be more meaning attached to sign, + * and what was + * harmless noise may become disruptive, ill-conditioned (or worse) + * input. + */ + digits_flonum . sign = '+'; + + { + /* + * Compute the mantssa (& exponent) of the power of 10. + * If sucessful, then multiply the power of 10 by the digits + * giving return_binary_mantissa and return_binary_exponent. + */ + + LITTLENUM_TYPE *power_binary_low; + int decimal_exponent_is_negative; + /* This refers to the "-56" in "12.34E-56". */ + /* FALSE: decimal_exponent is positive (or 0) */ + /* TRUE: decimal_exponent is negative */ + FLONUM_TYPE temporary_flonum; + LITTLENUM_TYPE *temporary_binary_low; + int size_of_power_in_littlenums; + int size_of_power_in_chars; + + size_of_power_in_littlenums = precision; +/* Precision has a built-in fudge factor so we get a few guard bits. */ + + + decimal_exponent_is_negative = decimal_exponent < 0; + if (decimal_exponent_is_negative) + { + decimal_exponent = - decimal_exponent; + } + /* From now on: the decimal exponent is > 0. Its sign is seperate. */ + + size_of_power_in_chars + = size_of_power_in_littlenums + * sizeof( LITTLENUM_TYPE ) + 2; + power_binary_low = (LITTLENUM_TYPE *) alloca ( size_of_power_in_chars ); + temporary_binary_low = (LITTLENUM_TYPE *) alloca ( size_of_power_in_chars ); + bzero ((char *)power_binary_low, size_of_power_in_chars); + * power_binary_low = 1; + power_of_10_flonum . exponent = 0; + power_of_10_flonum . low = power_binary_low; + power_of_10_flonum . leader = power_binary_low; + power_of_10_flonum . high = power_binary_low + size_of_power_in_littlenums - 1; + power_of_10_flonum . sign = '+'; + temporary_flonum . low = temporary_binary_low; + temporary_flonum . high = temporary_binary_low + size_of_power_in_littlenums - 1; + /* + * (power) == 1. + * Space for temporary_flonum allocated. + */ + + /* + * ... + * + * WHILE more bits + * DO find next bit (with place value) + * multiply into power mantissa + * OD + */ + { + int place_number_limit; + /* Any 10^(2^n) whose "n" exceeds this */ + /* value will fall off the end of */ + /* flonum_XXXX_powers_of_ten[]. */ + int place_number; + const FLONUM_TYPE * multiplicand; /* -> 10^(2^n) */ + + place_number_limit = table_size_of_flonum_powers_of_ten; + multiplicand + = ( decimal_exponent_is_negative + ? flonum_negative_powers_of_ten + : flonum_positive_powers_of_ten); + for (place_number = 1; /* Place value of this bit of exponent. */ + decimal_exponent; /* Quit when no more 1 bits in exponent. */ + decimal_exponent >>= 1 + , place_number ++) + { + if (decimal_exponent & 1) + { + if (place_number > place_number_limit) + { + /* + * The decimal exponent has a magnitude so great that + * our tables can't help us fragment it. Although this + * routine is in error because it can't imagine a + * number that big, signal an error as if it is the + * user's fault for presenting such a big number. + */ + return_value = ERROR_EXPONENT_OVERFLOW; + /* + * quit out of loop gracefully + */ + decimal_exponent = 0; + } + else + { +#ifdef TRACE +printf("before multiply, place_number = %d., power_of_10_flonum:\n", place_number); +flonum_print( & power_of_10_flonum ); +(void)putchar('\n'); +#endif + flonum_multip(multiplicand + place_number, &power_of_10_flonum, &temporary_flonum); + flonum_copy (& temporary_flonum, & power_of_10_flonum); + } /* If this bit of decimal_exponent was computable.*/ + } /* If this bit of decimal_exponent was set. */ + } /* For each bit of binary representation of exponent */ +#ifdef TRACE +printf( " after computing power_of_10_flonum: " ); +flonum_print( & power_of_10_flonum ); +(void)putchar('\n'); +#endif + } + + } + + /* + * power_of_10_flonum is power of ten in binary (mantissa) , (exponent). + * It may be the number 1, in which case we don't NEED to multiply. + * + * Multiply (decimal digits) by power_of_10_flonum. + */ + + flonum_multip (& power_of_10_flonum, & digits_flonum, address_of_generic_floating_point_number); + /* Assert sign of the number we made is '+'. */ + address_of_generic_floating_point_number -> sign = digits_sign_char; + + } /* If we had any significant digits. */ + return (return_value); +} /* atof_generic () */ + +/* end: atof_generic.c */ diff --git a/gas/bignum-copy.c b/gas/bignum-copy.c new file mode 100644 index 0000000..0dd5f9c --- /dev/null +++ b/gas/bignum-copy.c @@ -0,0 +1,77 @@ +/* bignum_copy.c - copy a bignum + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include "as.h" + +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#define bcopy(from,to,n) memcpy(to,from,n) +#endif + +/* + * bignum_copy () + * + * Copy a bignum from in to out. + * If the output is shorter than the input, copy lower-order littlenums. + * Return 0 or the number of significant littlenums dropped. + * Assumes littlenum arrays are densely packed: no unused chars between + * the littlenums. Uses bcopy() to move littlenums, and wants to + * know length (in chars) of the input bignum. + */ + +/* void */ +int +bignum_copy (in, in_length, out, out_length) + register LITTLENUM_TYPE * in; + register int in_length; /* in sizeof(littlenum)s */ + register LITTLENUM_TYPE * out; + register int out_length; /* in sizeof(littlenum)s */ +{ + register int significant_littlenums_dropped; + + if (out_length < in_length) + { + register LITTLENUM_TYPE * p; /* -> most significant (non-zero) input littlenum. */ + + bcopy ((char *)in, (char *)out, out_length << LITTLENUM_SHIFT); + for (p = in + in_length - 1; p >= in; -- p) + { + if (* p) break; + } + significant_littlenums_dropped = p - in - in_length + 1; + if (significant_littlenums_dropped < 0) + { + significant_littlenums_dropped = 0; + } + } + else + { + bcopy ((char *)in, (char *)out, in_length << LITTLENUM_SHIFT); + if (out_length > in_length) + { + bzero ((char *)(out + out_length), (out_length - in_length) << LITTLENUM_SHIFT); + } + significant_littlenums_dropped = 0; + } + return (significant_littlenums_dropped); +} + +/* end: bignum_copy.c */ diff --git a/gas/bignum.h b/gas/bignum.h new file mode 100644 index 0000000..9b1b8e8 --- /dev/null +++ b/gas/bignum.h @@ -0,0 +1,47 @@ +/* bignum.h-arbitrary precision integers + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/***********************************************************************\ +* * +* Arbitrary-precision integer arithmetic. * +* For speed, we work in groups of bits, even though this * +* complicates algorithms. * +* Each group of bits is called a 'littlenum'. * +* A bunch of littlenums representing a (possibly large) * +* integer is called a 'bignum'. * +* Bignums are >= 0. * +* * +\***********************************************************************/ + +#define LITTLENUM_NUMBER_OF_BITS (16) +#define LITTLENUM_RADIX (1 << LITTLENUM_NUMBER_OF_BITS) +#define LITTLENUM_MASK (0xFFFF) +#define LITTLENUM_SHIFT (1) +#define CHARS_PER_LITTLENUM (1 << LITTLENUM_SHIFT) +#ifndef BITS_PER_CHAR +#define BITS_PER_CHAR (8) +#endif + +typedef unsigned short LITTLENUM_TYPE; + +/* JF truncated this to get around a problem with GCC */ +#define LOG_TO_BASE_2_OF_10 (3.3219280948873623478703194294893901758651 ) + /* WARNING: I haven't checked that the trailing digits are correct! */ + +/* end: bignum.h */ diff --git a/gas/cond.c b/gas/cond.c new file mode 100644 index 0000000..38aec6f --- /dev/null +++ b/gas/cond.c @@ -0,0 +1,128 @@ +/* cond.c - conditional assembly pseudo-ops, and .include + Copyright (C) 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include "as.h" + +#include "obstack.h" + +void s_ifdef(arg) +int arg; +{ +/* register char c; */ + register char *name; /* points to name of symbol */ + register struct symbol * symbolP; /* Points to symbol */ + + SKIP_WHITESPACE(); /* Leading whitespace is part of operand. */ + name = input_line_pointer; + if (!is_name_beginner(*name)) { + as_bad("invalid identifier for .ifdef"); + obstack_1grow (&cond_obstack, 0); + } else { + get_symbol_end(); + ++input_line_pointer; + symbolP = symbol_find(name); + + /* ??? Should we try to optimize such that if we hit a .endif + before a .else, we don't need to push state? */ + obstack_1grow(&cond_obstack, (symbolP != 0) ^ arg); + } +} /* s_ifdef() */ + +/* This is allocated to grow and shrink as .ifdef/.endif pairs + are scanned. When the top element is nonzero, it means + we should accept input. Otherwise, we should ignore input. */ +struct obstack cond_obstack; + +void s_if(arg) +int arg; +{ + expressionS operand; + + SKIP_WHITESPACE(); /* Leading whitespace is part of operand. */ + expr(0, &operand); + + if (operand.X_add_symbol != NULL + || operand.X_subtract_symbol != NULL) + as_bad("non-constant expression in .if statement"); + + /* If the above error is signaled, this will dispatch + using an undefined result. No big deal. */ + obstack_1grow(&cond_obstack, (operand.X_add_number != 0) ^ arg); +} /* s_if() */ + +void s_endif(arg) +int arg; +{ + char *base = obstack_base(&cond_obstack); + char *ptr = obstack_next_free(&cond_obstack); + + if (ptr-1 == base) { + as_bad("unbalanced .endif"); + } else { + obstack_free(&cond_obstack, ptr-1); + cond_obstack.object_base = base; + } +} /* s_endif() */ + +void s_else(arg) +int arg; +{ + char *ptr = obstack_next_free(&cond_obstack); + if (ptr-1 == obstack_base(&cond_obstack)) { + as_bad(".else without matching .if"); + } else { + ptr[-1] = !ptr[-1]; + } +} /* s_else() */ + +void s_ifeqs(arg) +int arg; +{ + as_bad("ifeqs not implemented."); +} /* s_ifeqs() */ + +void s_end(arg) +int arg; +{ + ; +} /* s_end() */ + +int ignore_input() { + char *ptr = obstack_next_free (&cond_obstack); + + /* We cannot ignore certain pseudo ops. */ + if (input_line_pointer[-1] == '.') + { + if (input_line_pointer[0] == 'i' + && (!strncmp (input_line_pointer, "if", 2) + || !strncmp (input_line_pointer, "ifdef", 5) + || !strncmp (input_line_pointer, "ifndef", 6))) + return 0; + if (input_line_pointer[0] == 'e' + && (!strncmp (input_line_pointer, "else", 4) + || !strncmp (input_line_pointer, "endif", 5))) + return 0; + } + + return (ptr[-1] == 0); +} /* ignore_input() */ + +/* end of cond.c */ diff --git a/gas/config/atof-ieee.c b/gas/config/atof-ieee.c new file mode 100644 index 0000000..323d4e1 --- /dev/null +++ b/gas/config/atof-ieee.c @@ -0,0 +1,511 @@ +/* atof_ieee.c - turn a Flonum into an IEEE floating point number + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "as.h" + +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#define bcopy(from,to,n) memcpy((to),(from),(n)) +#endif + +extern FLONUM_TYPE generic_floating_point_number; /* Flonums returned here. */ + +#ifndef NULL +#define NULL (0) +#endif + +extern char EXP_CHARS[]; + /* Precision in LittleNums. */ +#define MAX_PRECISION (6) +#define F_PRECISION (2) +#define D_PRECISION (4) +#define X_PRECISION (6) +#define P_PRECISION (6) + + /* Length in LittleNums of guard bits. */ +#define GUARD (2) + +static unsigned long mask [] = { + 0x00000000, + 0x00000001, + 0x00000003, + 0x00000007, + 0x0000000f, + 0x0000001f, + 0x0000003f, + 0x0000007f, + 0x000000ff, + 0x000001ff, + 0x000003ff, + 0x000007ff, + 0x00000fff, + 0x00001fff, + 0x00003fff, + 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff, + 0x00ffffff, + 0x01ffffff, + 0x03ffffff, + 0x07ffffff, + 0x0fffffff, + 0x1fffffff, + 0x3fffffff, + 0x7fffffff, + 0xffffffff + }; + + +static int bits_left_in_littlenum; +static int littlenums_left; +static LITTLENUM_TYPE *littlenum_pointer; + +static int +next_bits (number_of_bits) + int number_of_bits; +{ + int return_value; + + if(!littlenums_left) + return 0; + if (number_of_bits >= bits_left_in_littlenum) + { + return_value = mask [bits_left_in_littlenum] & *littlenum_pointer; + number_of_bits -= bits_left_in_littlenum; + return_value <<= number_of_bits; + if(--littlenums_left) { + bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits; + littlenum_pointer --; + return_value |= (*littlenum_pointer>>bits_left_in_littlenum) & mask[number_of_bits]; + } + } + else + { + bits_left_in_littlenum -= number_of_bits; + return_value = mask [number_of_bits] & (*littlenum_pointer>>bits_left_in_littlenum); + } + return (return_value); +} + +/* Num had better be less than LITTLENUM_NUMBER_OF_BITS */ +static void +unget_bits(num) +int num; +{ + if(!littlenums_left) { + ++littlenum_pointer; + ++littlenums_left; + bits_left_in_littlenum=num; + } else if(bits_left_in_littlenum+num>LITTLENUM_NUMBER_OF_BITS) { + bits_left_in_littlenum= num-(LITTLENUM_NUMBER_OF_BITS-bits_left_in_littlenum); + ++littlenum_pointer; + ++littlenums_left; + } else + bits_left_in_littlenum+=num; +} + +static void +make_invalid_floating_point_number (words) + LITTLENUM_TYPE * words; +{ + as_bad("cannot create floating-point number"); + words[0]= ((unsigned)-1)>>1; /* Zero the leftmost bit */ + words[1]= -1; + words[2]= -1; + words[3]= -1; + words[4]= -1; + words[5]= -1; +} + +/***********************************************************************\ +* Warning: this returns 16-bit LITTLENUMs. It is up to the caller * +* to figure out any alignment problems and to conspire for the * +* bytes/word to be emitted in the right order. Bigendians beware! * +* * +\***********************************************************************/ + +/* Note that atof-ieee always has X and P precisions enabled. it is up + to md_atof to filter them out if the target machine does not support + them. */ + +char * /* Return pointer past text consumed. */ +atof_ieee (str, what_kind, words) + char * str; /* Text to convert to binary. */ + char what_kind; /* 'd', 'f', 'g', 'h' */ + LITTLENUM_TYPE * words; /* Build the binary here. */ +{ + static LITTLENUM_TYPE bits [MAX_PRECISION + MAX_PRECISION + GUARD]; + /* Extra bits for zeroed low-order bits. */ + /* The 1st MAX_PRECISION are zeroed, */ + /* the last contain flonum bits. */ + char * return_value; + int precision; /* Number of 16-bit words in the format. */ + long exponent_bits; + + return_value = str; + generic_floating_point_number.low = bits + MAX_PRECISION; + generic_floating_point_number.high = NULL; + generic_floating_point_number.leader = NULL; + generic_floating_point_number.exponent = NULL; + generic_floating_point_number.sign = '\0'; + + /* Use more LittleNums than seems */ + /* necessary: the highest flonum may have */ + /* 15 leading 0 bits, so could be useless. */ + + bzero (bits, sizeof(LITTLENUM_TYPE) * MAX_PRECISION); + + switch(what_kind) { + case 'f': + case 'F': + case 's': + case 'S': + precision = F_PRECISION; + exponent_bits = 8; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + precision = D_PRECISION; + exponent_bits = 11; + break; + + case 'x': + case 'X': + case 'e': + case 'E': + precision = X_PRECISION; + exponent_bits = 15; + break; + + case 'p': + case 'P': + + precision = P_PRECISION; + exponent_bits= -1; + break; + + default: + make_invalid_floating_point_number (words); + return NULL; + } + + generic_floating_point_number.high = generic_floating_point_number.low + precision - 1 + GUARD; + + if (atof_generic (& return_value, ".", EXP_CHARS, & generic_floating_point_number)) { + /* as_bad("Error converting floating point number (Exponent overflow?)"); */ + make_invalid_floating_point_number (words); + return NULL; + } + gen_to_words(words, precision, exponent_bits); + return return_value; +} + +/* Turn generic_floating_point_number into a real float/double/extended */ +int gen_to_words(words, precision, exponent_bits) +LITTLENUM_TYPE *words; +int precision; +long exponent_bits; +{ + int return_value=0; + + long exponent_1; + long exponent_2; + long exponent_3; + long exponent_4; + int exponent_skippage; + LITTLENUM_TYPE word1; + LITTLENUM_TYPE * lp; + + if (generic_floating_point_number.low > generic_floating_point_number.leader) { + /* 0.0e0 seen. */ + if(generic_floating_point_number.sign=='+') + words[0]=0x0000; + else + words[0]=0x8000; + bzero (&words[1], sizeof(LITTLENUM_TYPE) * (precision-1)); + return return_value; + } + + /* NaN: Do the right thing */ + if(generic_floating_point_number.sign==0) { + if(precision==F_PRECISION) { + words[0]=0x7fff; + words[1]=0xffff; + } else { + words[0]=0x7fff; + words[1]=0xffff; + words[2]=0xffff; + words[3]=0xffff; + } + return return_value; + } else if(generic_floating_point_number.sign=='P') { + /* +INF: Do the right thing */ + if(precision==F_PRECISION) { + words[0]=0x7f80; + words[1]=0; + } else { + words[0]=0x7ff0; + words[1]=0; + words[2]=0; + words[3]=0; + } + return return_value; + } else if(generic_floating_point_number.sign=='N') { + /* Negative INF */ + if(precision==F_PRECISION) { + words[0]=0xff80; + words[1]=0x0; + } else { + words[0]=0xfff0; + words[1]=0x0; + words[2]=0x0; + words[3]=0x0; + } + return return_value; + } + /* + * The floating point formats we support have: + * Bit 15 is sign bit. + * Bits 14:n are excess-whatever exponent. + * Bits n-1:0 (if any) are most significant bits of fraction. + * Bits 15:0 of the next word(s) are the next most significant bits. + * + * So we need: number of bits of exponent, number of bits of + * mantissa. + */ + bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS; + littlenum_pointer = generic_floating_point_number.leader; + littlenums_left = 1+generic_floating_point_number.leader - generic_floating_point_number.low; + /* Seek (and forget) 1st significant bit */ + for (exponent_skippage = 0;! next_bits(1); exponent_skippage ++) + ; + exponent_1 = generic_floating_point_number.exponent + generic_floating_point_number.leader + 1 - + generic_floating_point_number.low; + /* Radix LITTLENUM_RADIX, point just higher than generic_floating_point_number.leader. */ + exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS; + /* Radix 2. */ + exponent_3 = exponent_2 - exponent_skippage; + /* Forget leading zeros, forget 1st bit. */ + exponent_4 = exponent_3 + ((1 << (exponent_bits - 1)) - 2); + /* Offset exponent. */ + + lp = words; + + /* Word 1. Sign, exponent and perhaps high bits. */ + word1 = (generic_floating_point_number.sign == '+') ? 0 : (1<<(LITTLENUM_NUMBER_OF_BITS-1)); + + /* Assume 2's complement integers. */ + if(exponent_4<1 && exponent_4>=-62) { + int prec_bits; + int num_bits; + + unget_bits(1); + num_bits= -exponent_4; + prec_bits=LITTLENUM_NUMBER_OF_BITS*precision-(exponent_bits+1+num_bits); + if(precision==X_PRECISION && exponent_bits==15) + prec_bits-=LITTLENUM_NUMBER_OF_BITS+1; + + if(num_bits>=LITTLENUM_NUMBER_OF_BITS-exponent_bits) { + /* Bigger than one littlenum */ + num_bits-=(LITTLENUM_NUMBER_OF_BITS-1)-exponent_bits; + *lp++=word1; + if(num_bits+exponent_bits+1>=precision*LITTLENUM_NUMBER_OF_BITS) { + /* Exponent overflow */ + make_invalid_floating_point_number(words); + return return_value; + } + if(precision==X_PRECISION && exponent_bits==15) { + *lp++=0; + *lp++=0; + num_bits-=LITTLENUM_NUMBER_OF_BITS-1; + } + while(num_bits>=LITTLENUM_NUMBER_OF_BITS) { + num_bits-=LITTLENUM_NUMBER_OF_BITS; + *lp++=0; + } + if(num_bits) + *lp++=next_bits(LITTLENUM_NUMBER_OF_BITS-(num_bits)); + } else { + if(precision==X_PRECISION && exponent_bits==15) { + *lp++=word1; + *lp++=0; + if(num_bits==LITTLENUM_NUMBER_OF_BITS) { + *lp++=0; + *lp++=next_bits(LITTLENUM_NUMBER_OF_BITS-1); + } else if(num_bits==LITTLENUM_NUMBER_OF_BITS-1) + *lp++=0; + else + *lp++=next_bits(LITTLENUM_NUMBER_OF_BITS-1-num_bits); + num_bits=0; + } else { + word1|= next_bits ((LITTLENUM_NUMBER_OF_BITS-1) - (exponent_bits+num_bits)); + *lp++=word1; + } + } + while(lp<words+precision) + *lp++=next_bits(LITTLENUM_NUMBER_OF_BITS); + + /* Round the mantissa up, but don't change the number */ + if(next_bits(1)) { + --lp; + if(prec_bits>LITTLENUM_NUMBER_OF_BITS) { + int n = 0; + int tmp_bits; + + n=0; + tmp_bits=prec_bits; + while(tmp_bits>LITTLENUM_NUMBER_OF_BITS) { + if(lp[n]!=(LITTLENUM_TYPE)-1) + break; + --n; + tmp_bits-=LITTLENUM_NUMBER_OF_BITS; + } + if(tmp_bits>LITTLENUM_NUMBER_OF_BITS || (lp[n]&mask[tmp_bits])!=mask[tmp_bits]) { + unsigned long carry; + + for (carry = 1; carry && (lp >= words); lp --) { + carry = * lp + carry; + * lp = carry; + carry >>= LITTLENUM_NUMBER_OF_BITS; + } + } + } else if((*lp&mask[prec_bits])!=mask[prec_bits]) + lp++; + } + + return return_value; + } else if (exponent_4 & ~ mask [exponent_bits]) { + /* + * Exponent overflow. Lose immediately. + */ + + /* + * We leave return_value alone: admit we read the + * number, but return a floating exception + * because we can't encode the number. + */ + make_invalid_floating_point_number (words); + return return_value; + } else { + word1 |= (exponent_4 << ((LITTLENUM_NUMBER_OF_BITS-1) - exponent_bits)) + | next_bits ((LITTLENUM_NUMBER_OF_BITS-1) - exponent_bits); + } + + * lp ++ = word1; + + /* X_PRECISION is special: it has 16 bits of zero in the middle, + followed by a 1 bit. */ + if(exponent_bits==15 && precision==X_PRECISION) { + *lp++=0; + *lp++= 1<<(LITTLENUM_NUMBER_OF_BITS)|next_bits(LITTLENUM_NUMBER_OF_BITS-1); + } + + /* The rest of the words are just mantissa bits. */ + while(lp < words + precision) + *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS); + + if (next_bits (1)) { + unsigned long carry; + /* + * Since the NEXT bit is a 1, round UP the mantissa. + * The cunning design of these hidden-1 floats permits + * us to let the mantissa overflow into the exponent, and + * it 'does the right thing'. However, we lose if the + * highest-order bit of the lowest-order word flips. + * Is that clear? + */ + + +/* #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2) + Please allow at least 1 more bit in carry than is in a LITTLENUM. + We need that extra bit to hold a carry during a LITTLENUM carry + propagation. Another extra bit (kept 0) will assure us that we + don't get a sticky sign bit after shifting right, and that + permits us to propagate the carry without any masking of bits. +#endif */ + for (carry = 1, lp --; carry && (lp >= words); lp --) { + carry = * lp + carry; + * lp = carry; + carry >>= LITTLENUM_NUMBER_OF_BITS; + } + if ( (word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)) ) { + /* We leave return_value alone: admit we read the + * number, but return a floating exception + * because we can't encode the number. + */ + *words&= ~ (1 << (LITTLENUM_NUMBER_OF_BITS - 1)); + /* make_invalid_floating_point_number (words); */ + /* return return_value; */ + } + } + return (return_value); +} + +/* This routine is a real kludge. Someone really should do it better, but + I'm too lazy, and I don't understand this stuff all too well anyway + (JF) + */ +void +int_to_gen(x) +long x; +{ + char buf[20]; + char *bufp; + + sprintf(buf,"%ld",x); + bufp= &buf[0]; + if(atof_generic(&bufp,".", EXP_CHARS, &generic_floating_point_number)) + as_bad("Error converting number to floating point (Exponent overflow?)"); +} + +#ifdef TEST +char * +print_gen(gen) +FLONUM_TYPE *gen; +{ + FLONUM_TYPE f; + LITTLENUM_TYPE arr[10]; + double dv; + float fv; + static char sbuf[40]; + + if(gen) { + f=generic_floating_point_number; + generic_floating_point_number= *gen; + } + gen_to_words(&arr[0],4,11); + bcopy(&arr[0],&dv,sizeof(double)); + sprintf(sbuf,"%x %x %x %x %.14G ",arr[0],arr[1],arr[2],arr[3],dv); + gen_to_words(&arr[0],2,8); + bcopy(&arr[0],&fv,sizeof(float)); + sprintf(sbuf+strlen(sbuf),"%x %x %.12g\n",arr[0],arr[1],fv); + if(gen) + generic_floating_point_number=f; + return sbuf; +} +#endif diff --git a/gas/config/atof-vax.c b/gas/config/atof-vax.c new file mode 100644 index 0000000..43c81d6 --- /dev/null +++ b/gas/config/atof-vax.c @@ -0,0 +1,509 @@ +/* atof_vax.c - turn a Flonum into a VAX floating point number + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + /* JF added these two for md_atof() */ +#include "as.h" + +#include "flonum.h" + + + /* Precision in LittleNums. */ +#define MAX_PRECISION (8) +#define H_PRECISION (8) +#define G_PRECISION (4) +#define D_PRECISION (4) +#define F_PRECISION (2) + + /* Length in LittleNums of guard bits. */ +#define GUARD (2) + +int /* Number of chars in flonum type 'letter'. */ +atof_vax_sizeof (letter) + char letter; +{ + int return_value; + + /* + * Permitting uppercase letters is probably a bad idea. + * Please use only lower-cased letters in case the upper-cased + * ones become unsupported! + */ + switch (letter) + { + case 'f': + case 'F': + return_value = 4; + break; + + case 'd': + case 'D': + case 'g': + case 'G': + return_value = 8; + break; + + case 'h': + case 'H': + return_value = 16; + break; + + default: + return_value = 0; + break; + } + return (return_value); +} /* atof_vax_sizeof */ + +static const long mask [] = { + 0x00000000, + 0x00000001, + 0x00000003, + 0x00000007, + 0x0000000f, + 0x0000001f, + 0x0000003f, + 0x0000007f, + 0x000000ff, + 0x000001ff, + 0x000003ff, + 0x000007ff, + 0x00000fff, + 0x00001fff, + 0x00003fff, + 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff, + 0x00ffffff, + 0x01ffffff, + 0x03ffffff, + 0x07ffffff, + 0x0fffffff, + 0x1fffffff, + 0x3fffffff, + 0x7fffffff, + 0xffffffff + }; + + +/* Shared between flonum_gen2vax and next_bits */ +static int bits_left_in_littlenum; +static LITTLENUM_TYPE * littlenum_pointer; +static LITTLENUM_TYPE * littlenum_end; + +static int +next_bits (number_of_bits) + int number_of_bits; +{ + int return_value; + + if(littlenum_pointer<littlenum_end) + return 0; + if (number_of_bits >= bits_left_in_littlenum) + { + return_value = mask [bits_left_in_littlenum] & * littlenum_pointer; + number_of_bits -= bits_left_in_littlenum; + return_value <<= number_of_bits; + bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits; + littlenum_pointer --; + if(littlenum_pointer>=littlenum_end) + return_value |= ( (* littlenum_pointer) >> (bits_left_in_littlenum) ) & mask [number_of_bits]; + } + else + { + bits_left_in_littlenum -= number_of_bits; + return_value = mask [number_of_bits] & ( (* littlenum_pointer) >> bits_left_in_littlenum); + } + return (return_value); +} + +static void +make_invalid_floating_point_number (words) + LITTLENUM_TYPE * words; +{ + * words = 0x8000; /* Floating Reserved Operand Code */ +} + +static int /* 0 means letter is OK. */ +what_kind_of_float (letter, precisionP, exponent_bitsP) + char letter; /* In: lowercase please. What kind of float? */ + int * precisionP; /* Number of 16-bit words in the float. */ + long * exponent_bitsP; /* Number of exponent bits. */ +{ + int retval; /* 0: OK. */ + + retval = 0; + switch (letter) + { + case 'f': + * precisionP = F_PRECISION; + * exponent_bitsP = 8; + break; + + case 'd': + * precisionP = D_PRECISION; + * exponent_bitsP = 8; + break; + + case 'g': + * precisionP = G_PRECISION; + * exponent_bitsP = 11; + break; + + case 'h': + * precisionP = H_PRECISION; + * exponent_bitsP = 15; + break; + + default: + retval = 69; + break; + } + return (retval); +} + +/***********************************************************************\ +* * +* Warning: this returns 16-bit LITTLENUMs, because that is * +* what the VAX thinks in. It is up to the caller to figure * +* out any alignment problems and to conspire for the bytes/word * +* to be emitted in the right order. Bigendians beware! * +* * +\***********************************************************************/ + +char * /* Return pointer past text consumed. */ +atof_vax (str, what_kind, words) + char * str; /* Text to convert to binary. */ + char what_kind; /* 'd', 'f', 'g', 'h' */ + LITTLENUM_TYPE * words; /* Build the binary here. */ +{ + FLONUM_TYPE f; + LITTLENUM_TYPE bits [MAX_PRECISION + MAX_PRECISION + GUARD]; + /* Extra bits for zeroed low-order bits. */ + /* The 1st MAX_PRECISION are zeroed, */ + /* the last contain flonum bits. */ + char * return_value; + int precision; /* Number of 16-bit words in the format. */ + long exponent_bits; + + return_value = str; + f . low = bits + MAX_PRECISION; + f . high = NULL; + f . leader = NULL; + f . exponent = NULL; + f . sign = '\0'; + + if (what_kind_of_float (what_kind, & precision, & exponent_bits)) + { + return_value = NULL; /* We lost. */ + make_invalid_floating_point_number (words); + } + if (return_value) + { + bzero (bits, sizeof(LITTLENUM_TYPE) * MAX_PRECISION); + + /* Use more LittleNums than seems */ + /* necessary: the highest flonum may have */ + /* 15 leading 0 bits, so could be useless. */ + f . high = f . low + precision - 1 + GUARD; + + if (atof_generic (& return_value, ".", "eE", & f)) + { + make_invalid_floating_point_number (words); + return_value = NULL; /* we lost */ + } + else + { + if (flonum_gen2vax (what_kind, & f, words)) + { + return_value = NULL; + } + } + } + return (return_value); +} + +/* + * In: a flonum, a vax floating point format. + * Out: a vax floating-point bit pattern. + */ + +int /* 0: OK. */ +flonum_gen2vax (format_letter, f, words) + char format_letter; /* One of 'd' 'f' 'g' 'h'. */ + FLONUM_TYPE * f; + LITTLENUM_TYPE * words; /* Deliver answer here. */ +{ + LITTLENUM_TYPE * lp; + int precision; + long exponent_bits; + int return_value; /* 0 == OK. */ + + return_value = what_kind_of_float (format_letter, & precision, & exponent_bits); + if (return_value != 0) + { + make_invalid_floating_point_number (words); + } + else + { + if (f -> low > f -> leader) + { + /* 0.0e0 seen. */ + bzero (words, sizeof(LITTLENUM_TYPE) * precision); + } + else + { + long exponent_1; + long exponent_2; + long exponent_3; + long exponent_4; + int exponent_skippage; + LITTLENUM_TYPE word1; + + /* JF: Deal with new Nan, +Inf and -Inf codes */ + if(f->sign!='-' && f->sign!='+') { + make_invalid_floating_point_number(words); + return return_value; + } + /* + * All vaxen floating_point formats (so far) have: + * Bit 15 is sign bit. + * Bits 14:n are excess-whatever exponent. + * Bits n-1:0 (if any) are most significant bits of fraction. + * Bits 15:0 of the next word are the next most significant bits. + * And so on for each other word. + * + * All this to be compatible with a KF11?? (Which is still faster + * than lots of vaxen I can think of, but it also has higher + * maintenance costs ... sigh). + * + * So we need: number of bits of exponent, number of bits of + * mantissa. + */ + +#ifdef NEVER /******* This zeroing seems redundant - Dean 3may86 **********/ + /* + * No matter how few bits we got back from the atof() + * routine, add enough zero littlenums so the rest of the + * code won't run out of "significant" bits in the mantissa. + */ + { + LITTLENUM_TYPE * ltp; + for (ltp = f -> leader + 1; + ltp <= f -> low + precision; + ltp ++) + { + * ltp = 0; + } + } +#endif + + bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS; + littlenum_pointer = f -> leader; + littlenum_end = f->low; + /* Seek (and forget) 1st significant bit */ + for (exponent_skippage = 0; + ! next_bits(1); + exponent_skippage ++) + { + } + exponent_1 = f -> exponent + f -> leader + 1 - f -> low; + /* Radix LITTLENUM_RADIX, point just higher than f -> leader. */ + exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS; + /* Radix 2. */ + exponent_3 = exponent_2 - exponent_skippage; + /* Forget leading zeros, forget 1st bit. */ + exponent_4 = exponent_3 + (1 << (exponent_bits - 1)); + /* Offset exponent. */ + + if (exponent_4 & ~ mask [exponent_bits]) + { + /* + * Exponent overflow. Lose immediately. + */ + + make_invalid_floating_point_number (words); + + /* + * We leave return_value alone: admit we read the + * number, but return a floating exception + * because we can't encode the number. + */ + } + else + { + lp = words; + + /* Word 1. Sign, exponent and perhaps high bits. */ + /* Assume 2's complement integers. */ + word1 = ((exponent_4 & mask [exponent_bits]) << (15 - exponent_bits)) + | ((f -> sign == '+') ? 0 : 0x8000) + | next_bits (15 - exponent_bits); + * lp ++ = word1; + + /* The rest of the words are just mantissa bits. */ + for (; lp < words + precision; lp++) + { + * lp = next_bits (LITTLENUM_NUMBER_OF_BITS); + } + + if (next_bits (1)) + { + /* + * Since the NEXT bit is a 1, round UP the mantissa. + * The cunning design of these hidden-1 floats permits + * us to let the mantissa overflow into the exponent, and + * it 'does the right thing'. However, we lose if the + * highest-order bit of the lowest-order word flips. + * Is that clear? + */ + + unsigned long carry; + + /* + #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2) + Please allow at least 1 more bit in carry than is in a LITTLENUM. + We need that extra bit to hold a carry during a LITTLENUM carry + propagation. Another extra bit (kept 0) will assure us that we + don't get a sticky sign bit after shifting right, and that + permits us to propagate the carry without any masking of bits. + #endif + */ + for (carry = 1, lp --; + carry && (lp >= words); + lp --) + { + carry = * lp + carry; + * lp = carry; + carry >>= LITTLENUM_NUMBER_OF_BITS; + } + + if ( (word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)) ) + { + make_invalid_floating_point_number (words); + /* + * We leave return_value alone: admit we read the + * number, but return a floating exception + * because we can't encode the number. + */ + } + } /* if (we needed to round up) */ + } /* if (exponent overflow) */ + } /* if (0.0e0) */ + } /* if (float_type was OK) */ + return (return_value); +} + + +/* JF this used to be in vax.c but this looks like a better place for it */ + +/* + * md_atof() + * + * In: input_line_pointer -> the 1st character of a floating-point + * number. + * 1 letter denoting the type of statement that wants a + * binary floating point number returned. + * Address of where to build floating point literal. + * Assumed to be 'big enough'. + * Address of where to return size of literal (in chars). + * + * Out: Input_line_pointer -> of next char after floating number. + * Error message, or "". + * Floating point literal. + * Number of chars we used for the literal. + */ + +#define MAXIMUM_NUMBER_OF_LITTLENUMS (8) /* For .hfloats. */ + +char * +md_atof (what_statement_type, literalP, sizeP) + char what_statement_type; + char * literalP; + int * sizeP; +{ + LITTLENUM_TYPE words [MAXIMUM_NUMBER_OF_LITTLENUMS]; + register char kind_of_float; + register int number_of_chars; + register LITTLENUM_TYPE * littlenum_pointer; + + switch (what_statement_type) + { + case 'F': /* .float */ + case 'f': /* .ffloat */ + kind_of_float = 'f'; + break; + + case 'D': /* .double */ + case 'd': /* .dfloat */ + kind_of_float = 'd'; + break; + + case 'g': /* .gfloat */ + kind_of_float = 'g'; + break; + + case 'h': /* .hfloat */ + kind_of_float = 'h'; + break; + + default: + kind_of_float = 0; + break; + }; + + if (kind_of_float) + { + register LITTLENUM_TYPE * limit; + + input_line_pointer = atof_vax (input_line_pointer, + kind_of_float, + words); + /* + * The atof_vax() builds up 16-bit numbers. + * Since the assembler may not be running on + * a little-endian machine, be very careful about + * converting words to chars. + */ + number_of_chars = atof_vax_sizeof (kind_of_float); + know( number_of_chars <= MAXIMUM_NUMBER_OF_LITTLENUMS * sizeof(LITTLENUM_TYPE) ); + limit = words + (number_of_chars / sizeof(LITTLENUM_TYPE)); + for (littlenum_pointer = words; + littlenum_pointer < limit; + littlenum_pointer ++) + { + md_number_to_chars (literalP, * littlenum_pointer, sizeof(LITTLENUM_TYPE)); + literalP += sizeof(LITTLENUM_TYPE); + }; + } + else + { + number_of_chars = 0; + }; + + * sizeP = number_of_chars; + return (kind_of_float ? "" : "Bad call to md_atof()"); +} /* md_atof() */ + +/* atof_vax.c */ diff --git a/gas/config/obj-aout.c b/gas/config/obj-aout.c new file mode 100644 index 0000000..6c1e100 --- /dev/null +++ b/gas/config/obj-aout.c @@ -0,0 +1,500 @@ +/* a.out object file format + Copyright (C) 1989, 1990, 1991 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 1, +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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +#include "as.h" + +#include "obstack.h" + + /* in: segT out: N_TYPE bits */ +const short seg_N_TYPE[] = { + N_ABS, + N_TEXT, + N_DATA, + N_BSS, + N_UNDF, /* unknown */ + N_UNDF, /* absent */ + N_UNDF, /* pass1 */ + N_UNDF, /* error */ + N_UNDF, /* bignum/flonum */ + N_UNDF, /* difference */ + N_UNDF, /* debug */ + N_UNDF, /* ntv */ + N_UNDF, /* ptv */ + N_REGISTER, /* register */ +}; + +const segT N_TYPE_seg [N_TYPE+2] = { /* N_TYPE == 0x1E = 32-2 */ + SEG_UNKNOWN, /* N_UNDF == 0 */ + SEG_GOOF, + SEG_ABSOLUTE, /* N_ABS == 2 */ + SEG_GOOF, + SEG_TEXT, /* N_TEXT == 4 */ + SEG_GOOF, + SEG_DATA, /* N_DATA == 6 */ + SEG_GOOF, + SEG_BSS, /* N_BSS == 8 */ + SEG_GOOF, + SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, + SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, + SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, + SEG_REGISTER, /* dummy N_REGISTER for regs = 30 */ + SEG_GOOF, +}; + +#ifdef __STDC__ +static void obj_aout_stab(int what); +static void obj_aout_line(void); +static void obj_aout_desc(void); +#else /* __STDC__ */ +static void obj_aout_desc(); +static void obj_aout_stab(); +static void obj_aout_line(); +#endif /* __STDC__ */ + +const pseudo_typeS obj_pseudo_table[] = { + { "line", obj_aout_line, 0 }, /* source code line number */ + { "ln", obj_aout_line, 0 }, /* source code line number */ + { "desc", obj_aout_desc, 0 }, /* def */ + { "stabd", obj_aout_stab, 'd' }, /* stabs */ + { "stabn", obj_aout_stab, 'n' }, /* stabs */ + { "stabs", obj_aout_stab, 's' }, /* stabs */ + + { NULL} /* end sentinel */ +}; /* obj_pseudo_table */ + + +/* Relocation. */ + +/* + * In: length of relocation (or of address) in chars: 1, 2 or 4. + * Out: GNU LD relocation length code: 0, 1, or 2. + */ + +static unsigned char +nbytes_r_length [] = { + 42, 0, 1, 42, 2 + }; + +/* + * emit_relocations() + * + * Crawl along a fixS chain. Emit the segment's relocations. + */ +void obj_emit_relocations(where, fixP, segment_address_in_file) +char **where; +fixS *fixP; /* Fixup chain for this segment. */ +relax_addressT segment_address_in_file; +{ + struct reloc_info_generic ri; + register symbolS *symbolP; + + /* If a machine dependent emitter is needed, call it instead. */ + if (md_emit_relocations) { + (*md_emit_relocations) (fixP, segment_address_in_file); + return; + } + + /* JF this is for paranoia */ + bzero((char *)&ri,sizeof(ri)); + for (; fixP; fixP = fixP->fx_next) { + if ((symbolP = fixP->fx_addsy) != 0) { + ri.r_bsr = fixP->fx_bsr; + ri.r_disp = fixP->fx_im_disp; + ri.r_callj = fixP->fx_callj; + ri.r_length = nbytes_r_length [fixP->fx_size]; + ri.r_pcrel = fixP->fx_pcrel; + ri.r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file; + + if (S_GET_TYPE(symbolP) == N_UNDF) { + ri.r_extern = 1; + ri.r_symbolnum = symbolP->sy_number; + } else { + ri.r_extern = 0; + ri.r_symbolnum = S_GET_TYPE(symbolP); + } + + /* Output the relocation information in machine-dependent form. */ + md_ri_to_chars(*where, &ri); + *where += md_reloc_size; + } /* if there is an add symbol */ + } /* for each fix */ + + return; +} /* emit_relocations() */ + +/* Aout file generation & utilities */ +void obj_header_append(where, headers) +char **where; +object_headers *headers; +{ + tc_headers_hook(headers); + +#ifdef CROSS_ASSEMBLE + md_number_to_chars(*where, headers->header.a_info, sizeof(headers->header.a_info)); + *where += sizeof(headers->header.a_info); + md_number_to_chars(*where, headers->header.a_text, sizeof(headers->header.a_text)); + *where += sizeof(headers->header.a_text); + md_number_to_chars(*where, headers->header.a_data, sizeof(headers->header.a_data)); + *where += sizeof(headers->header.a_data); + md_number_to_chars(*where, headers->header.a_bss, sizeof(headers->header.a_bss)); + *where += sizeof(headers->header.a_bss); + md_number_to_chars(*where, headers->header.a_syms, sizeof(headers->header.a_syms)); + *where += sizeof(headers->header.a_syms); + md_number_to_chars(*where, headers->header.a_entry, sizeof(headers->header.a_entry)); + *where += sizeof(headers->header.a_entry); + md_number_to_chars(*where, headers->header.a_trsize, sizeof(headers->header.a_trsize)); + *where += sizeof(headers->header.a_trsize); + md_number_to_chars(*where, headers->header.a_drsize, sizeof(headers->header.a_drsize)); + *where += sizeof(headers->header.a_drsize); +#ifdef EXEC_MACHINE_TYPE + md_number_to_chars(*where, headers->header.a_machtype, sizeof(headers->header.a_machtype)); + *where += sizeof(headers->header.a_machtype); +#endif /* EXEC_MACHINE_TYPE */ +#ifdef EXEC_VERSION + md_number_to_chars(*where, headers->header.a_version, sizeof(headers->header.a_version)); + *where += sizeof(headers->header.a_version); +#endif /* EXEC_VERSION */ + +#else /* CROSS_ASSEMBLE */ + + append(where, (char *) &headers->header, sizeof(headers->header)); +#endif /* CROSS_ASSEMBLE */ + + return; +} /* obj_append_header() */ + +void obj_symbol_to_chars(where, symbolP) +char **where; +symbolS *symbolP; +{ + md_number_to_chars((char *)&(S_GET_OFFSET(symbolP)), S_GET_OFFSET(symbolP), sizeof(S_GET_OFFSET(symbolP))); + md_number_to_chars((char *)&(S_GET_DESC(symbolP)), S_GET_DESC(symbolP), sizeof(S_GET_DESC(symbolP))); + md_number_to_chars((char *)&(S_GET_VALUE(symbolP)), S_GET_VALUE(symbolP), sizeof(S_GET_VALUE(symbolP))); + + append(where, (char *)&symbolP->sy_symbol, sizeof(obj_symbol_type)); +} /* obj_symbol_to_chars() */ + +void obj_emit_symbols(where, symbol_rootP) +char **where; +symbolS *symbol_rootP; +{ + symbolS * symbolP; + + /* + * Emit all symbols left in the symbol chain. + */ + for(symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + /* Used to save the offset of the name. It is used to point + to the string in memory but must be a file offset. */ + register char *temp; + + temp = S_GET_NAME(symbolP); + S_SET_OFFSET(symbolP, symbolP->sy_name_offset); + + /* Any symbol still undefined and is not a dbg symbol is made N_EXT. */ + if (!S_IS_DEBUG(symbolP) && !S_IS_DEFINED(symbolP)) S_SET_EXTERNAL(symbolP); + + obj_symbol_to_chars(where, symbolP); + S_SET_NAME(symbolP,temp); + } +} /* emit_symbols() */ + +void obj_symbol_new_hook(symbolP) +symbolS *symbolP; +{ + S_SET_OTHER(symbolP, 0); + S_SET_DESC(symbolP, 0); + return; +} /* obj_symbol_new_hook() */ + +static void obj_aout_line() { + /* Assume delimiter is part of expression. */ + /* BSD4.2 as fails with delightful bug, so we */ + /* are not being incompatible here. */ + new_logical_line((char *)NULL, (int)(get_absolute_expression())); + demand_empty_rest_of_line(); +} /* obj_aout_line() */ + +/* + * stab() + * + * Handle .stabX directives, which used to be open-coded. + * So much creeping featurism overloaded the semantics that we decided + * to put all .stabX thinking in one place. Here. + * + * We try to make any .stabX directive legal. Other people's AS will often + * do assembly-time consistency checks: eg assigning meaning to n_type bits + * and "protecting" you from setting them to certain values. (They also zero + * certain bits before emitting symbols. Tut tut.) + * + * If an expression is not absolute we either gripe or use the relocation + * information. Other people's assemblers silently forget information they + * don't need and invent information they need that you didn't supply. + * + * .stabX directives always make a symbol table entry. It may be junk if + * the rest of your .stabX directive is malformed. + */ +static void obj_aout_stab(what) +int what; +{ + register symbolS * symbolP = 0; + register char * string; + int saved_type = 0; + int length; + int goof; /* TRUE if we have aborted. */ + long longint; + +/* + * Enter with input_line_pointer pointing past .stabX and any following + * whitespace. + */ + goof = 0; /* JF who forgot this?? */ + if (what == 's') { + string = demand_copy_C_string(& length); + SKIP_WHITESPACE(); + if (* input_line_pointer == ',') + input_line_pointer ++; + else { + as_bad("I need a comma after symbol's name"); + goof = 1; + } + } else + string = ""; + +/* + * Input_line_pointer->after ','. String->symbol name. + */ + if (! goof) { + symbolP = symbol_new(string, + SEG_UNKNOWN, + 0, + (struct frag *)0); + switch (what) { + case 'd': + S_SET_NAME(symbolP, NULL); /* .stabd feature. */ + S_SET_VALUE(symbolP, obstack_next_free(&frags) - frag_now->fr_literal); + symbolP->sy_frag = frag_now; + break; + + case 'n': + symbolP->sy_frag = &zero_address_frag; + break; + + case 's': + symbolP->sy_frag = & zero_address_frag; + break; + + default: + BAD_CASE(what); + break; + } + + if (get_absolute_expression_and_terminator(&longint) == ',') + symbolP->sy_symbol.n_type = saved_type = longint; + else { + as_bad("I want a comma after the n_type expression"); + goof = 1; + input_line_pointer --; /* Backup over a non-',' char. */ + } + } + + if (!goof) { + if (get_absolute_expression_and_terminator(&longint) == ',') + S_SET_OTHER(symbolP, longint); + else { + as_bad("I want a comma after the n_other expression"); + goof = 1; + input_line_pointer--; /* Backup over a non-',' char. */ + } + } + + if (!goof) { + S_SET_DESC(symbolP, get_absolute_expression()); + if (what == 's' || what == 'n') { + if (*input_line_pointer != ',') { + as_bad("I want a comma after the n_desc expression"); + goof = 1; + } else { + input_line_pointer++; + } + } + } + + if ((!goof) && (what=='s' || what=='n')) { + pseudo_set(symbolP); + symbolP->sy_symbol.n_type = saved_type; + } + + if (goof) + ignore_rest_of_line(); + else + demand_empty_rest_of_line (); +} /* obj_aout_stab() */ + +static void obj_aout_desc() { + register char *name; + register char c; + register char *p; + register symbolS *symbolP; + register int temp; + + /* + * Frob invented at RMS' request. Set the n_desc of a symbol. + */ + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + * p = c; + SKIP_WHITESPACE(); + if (*input_line_pointer != ',') { + *p = 0; + as_bad("Expected comma after name \"%s\"", name); + *p = c; + ignore_rest_of_line(); + } else { + input_line_pointer ++; + temp = get_absolute_expression(); + *p = 0; + symbolP = symbol_find_or_make(name); + *p = c; + S_SET_DESC(symbolP,temp); + } + demand_empty_rest_of_line(); +} /* obj_aout_desc() */ + +void obj_read_begin_hook() { + return; +} /* obj_read_begin_hook() */ + +void obj_crawl_symbol_chain(headers) +object_headers *headers; +{ + symbolS *symbolP; + symbolS **symbolPP; + int symbol_number = 0; + + /* JF deal with forward references first... */ + for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + if (symbolP->sy_forward) { + S_SET_VALUE(symbolP, S_GET_VALUE(symbolP) + + S_GET_VALUE(symbolP->sy_forward) + + symbolP->sy_forward->sy_frag->fr_address); + + symbolP->sy_forward=0; + } /* if it has a forward reference */ + } /* walk the symbol chain */ + + tc_crawl_symbol_chain(headers); + + symbolPP = &symbol_rootP; /*->last symbol chain link. */ + while ((symbolP = *symbolPP) != NULL) { + if (flagseen['R'] && (S_GET_SEGMENT(symbolP) == SEG_DATA)) { + S_SET_SEGMENT(symbolP, SEG_TEXT); + } /* if pusing data into text */ + + S_SET_VALUE(symbolP, S_GET_VALUE(symbolP) + symbolP->sy_frag->fr_address); + + /* OK, here is how we decide which symbols go out into the + brave new symtab. Symbols that do are: + + * symbols with no name (stabd's?) + * symbols with debug info in their N_TYPE + + Symbols that don't are: + * symbols that are registers + * symbols with \1 as their 3rd character (numeric labels) + * "local labels" as defined by S_LOCAL_NAME(name) + if the -L switch was passed to gas. + + All other symbols are output. We complain if a deleted + symbol was marked external. */ + + + if (!S_IS_REGISTER(symbolP) + && (!S_GET_NAME(symbolP) + || S_IS_DEBUG(symbolP) +#ifdef TC_I960 + /* FIXME-SOON this ifdef seems highly dubious to me. xoxorich. */ + || !S_IS_DEFINED(symbolP) + || S_IS_EXTERNAL(symbolP) +#endif /* TC_I960 */ + || (S_GET_NAME(symbolP)[0] != '\001' && (flagseen ['L'] || ! S_LOCAL_NAME(symbolP))))) { + symbolP->sy_number = symbol_number++; + + /* The + 1 after strlen account for the \0 at the + end of each string */ + if (!S_IS_STABD(symbolP)) { + /* Ordinary case. */ + symbolP->sy_name_offset = string_byte_count; + string_byte_count += strlen(S_GET_NAME(symbolP)) + 1; + } + else /* .Stabd case. */ + symbolP->sy_name_offset = 0; + symbolPP = &(symbol_next(symbolP)); + } else { + if (S_IS_EXTERNAL(symbolP) || !S_IS_DEFINED(symbolP)) { + as_bad("Local symbol %s never defined", S_GET_NAME(symbolP)); + } /* oops. */ + + /* Unhook it from the chain */ + *symbolPP = symbol_next(symbolP); + } /* if this symbol should be in the output */ + } /* for each symbol */ + + H_SET_SYMBOL_TABLE_SIZE(headers, symbol_number); + + return; +} /* obj_crawl_symbol_chain() */ + +/* + * Find strings by crawling along symbol table chain. + */ + +void obj_emit_strings(where) +char **where; +{ + symbolS *symbolP; + +#ifdef CROSS_ASSEMBLE + /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */ + md_number_to_chars(*where, string_byte_count, sizeof(string_byte_count)); + *where += sizeof(string_byte_count); +#else /* CROSS_ASSEMBLE */ + append (where, (char *)&string_byte_count, (unsigned long)sizeof(string_byte_count)); +#endif /* CROSS_ASSEMBLE */ + + for(symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + if(S_GET_NAME(symbolP)) + append(&next_object_file_charP, S_GET_NAME(symbolP), + (unsigned long)(strlen (S_GET_NAME(symbolP)) + 1)); + } /* walk symbol chain */ + + return; +} /* obj_emit_strings() */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of obj-aout.c */ diff --git a/gas/config/obj-aout.h b/gas/config/obj-aout.h new file mode 100644 index 0000000..602d760 --- /dev/null +++ b/gas/config/obj-aout.h @@ -0,0 +1,187 @@ +/* a.out object file format + Copyright (C) 1989, 1990, 1991 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 1, +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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +/* Tag to validate a.out object file format processing */ +#define OBJ_AOUT 1 + +#include "targ-cpu.h" + +#ifndef VMS +#include "a.out.gnu.h" /* Needed to define struct nlist. Sigh. */ +#else +#include "a_out.h" +#endif + +extern const short seg_N_TYPE[]; +extern const segT N_TYPE_seg[]; + +#ifndef DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE +#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (OMAGIC) +#endif /* DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE */ + +/* SYMBOL TABLE */ +/* Symbol table entry data type */ + +typedef struct nlist obj_symbol_type; /* Symbol table entry */ + +/* If compiler generate leading underscores, remove them. */ + +#ifndef STRIP_UNDERSCORE +#define STRIP_UNDERSCORE 0 +#endif /* STRIP_UNDERSCORE */ + +/* Symbol table macros and constants */ + +/* + * Macros to extract information from a symbol table entry. + * This syntaxic indirection allows independence regarding a.out or coff. + * The argument (s) of all these macros is a pointer to a symbol table entry. + */ + +/* True if the symbol is external */ +#define S_IS_EXTERNAL(s) ((s)->sy_symbol.n_type & N_EXT) + +/* True if symbol has been defined, ie is in N_{TEXT,DATA,BSS,ABS} or N_EXT */ +#define S_IS_DEFINED(s) ((S_GET_TYPE(s) != N_UNDF) || (S_GET_OTHER(s) != 0) || (S_GET_DESC(s) != 0)) + +#define S_IS_REGISTER(s) ((s)->sy_symbol.n_type == N_REGISTER) + +/* True if a debug special symbol entry */ +#define S_IS_DEBUG(s) ((s)->sy_symbol.n_type & N_STAB) +/* True if a symbol is local symbol name */ +/* A symbol name whose name begin with ^A is a gas internal pseudo symbol + nameless symbols come from .stab directives. */ +#define S_IS_LOCAL(s) (S_GET_NAME(s) && \ + !S_IS_DEBUG(s) && \ + (S_GET_NAME(s)[0] == '\001' || \ + (S_LOCAL_NAME(s) && !flagseen['L']))) +/* True if a symbol is not defined in this file */ +#define S_IS_EXTERN(s) ((s)->sy_symbol.n_type & N_EXT) +/* True if the symbol has been generated because of a .stabd directive */ +#define S_IS_STABD(s) (S_GET_NAME(s) == (char *)0) + +/* Accessors */ +/* The value of the symbol */ +#define S_GET_VALUE(s) (((s)->sy_symbol.n_value)) +/* The name of the symbol */ +#define S_GET_NAME(s) ((s)->sy_symbol.n_un.n_name) +/* The pointer to the string table */ +#define S_GET_OFFSET(s) ((s)->sy_symbol.n_un.n_strx) +/* The type of the symbol */ +#define S_GET_TYPE(s) ((s)->sy_symbol.n_type & N_TYPE) +/* The numeric value of the segment */ +#define S_GET_SEGMENT(s) (N_TYPE_seg[S_GET_TYPE(s)]) +/* The n_other expression value */ +#define S_GET_OTHER(s) ((s)->sy_symbol.n_other) +/* The n_desc expression value */ +#define S_GET_DESC(s) ((s)->sy_symbol.n_desc) + +/* Modifiers */ +/* Set the value of the symbol */ +#define S_SET_VALUE(s,v) ((s)->sy_symbol.n_value = (unsigned long) (v)) +/* Assume that a symbol cannot be simultaneously in more than on segment */ + /* set segment */ +#define S_SET_SEGMENT(s,seg) ((s)->sy_symbol.n_type &= ~N_TYPE,(s)->sy_symbol.n_type|=SEGMENT_TO_SYMBOL_TYPE(seg)) +/* The symbol is external */ +#define S_SET_EXTERNAL(s) ((s)->sy_symbol.n_type |= N_EXT) +/* The symbol is not external */ +#define S_CLEAR_EXTERNAL(s) ((s)->sy_symbol.n_type &= ~N_EXT) +/* Set the name of the symbol */ +#define S_SET_NAME(s,v) ((s)->sy_symbol.n_un.n_name = (v)) +/* Set the offset in the string table */ +#define S_SET_OFFSET(s,v) ((s)->sy_symbol.n_un.n_strx = (v)) +/* Set the n_other expression value */ +#define S_SET_OTHER(s,v) ((s)->sy_symbol.n_other = (v)) +/* Set the n_desc expression value */ +#define S_SET_DESC(s,v) ((s)->sy_symbol.n_desc = (v)) + +/* File header macro and type definition */ + +#define H_GET_FILE_SIZE(h) (sizeof(struct exec) + \ + H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \ + H_GET_SYMBOL_TABLE_SIZE(h) + \ + H_GET_TEXT_RELOCATION_SIZE(h) + \ + H_GET_DATA_RELOCATION_SIZE(h) + \ + (h)->string_table_size) + +#define H_GET_TEXT_SIZE(h) ((h)->header.a_text) +#define H_GET_DATA_SIZE(h) ((h)->header.a_data) +#define H_GET_BSS_SIZE(h) ((h)->header.a_bss) +#define H_GET_TEXT_RELOCATION_SIZE(h) ((h)->header.a_trsize) +#define H_GET_DATA_RELOCATION_SIZE(h) ((h)->header.a_drsize) +#define H_GET_SYMBOL_TABLE_SIZE(h) ((h)->header.a_syms) +#define H_GET_MAGIC_NUMBER(h) ((h)->header.a_info) +#define H_GET_ENTRY_POINT(h) ((h)->header.a_entry) +#define H_GET_STRING_SIZE(h) ((h)->string_table_size) +#ifdef EXEC_MACHINE_TYPE +#define H_GET_MACHINE_TYPE(h) ((h)->header.a_machtype) +#endif /* EXEC_MACHINE_TYPE */ +#ifdef EXEC_VERSION +#define H_GET_VERSION(h) ((h)->header.a_version) +#endif /* EXEC_VERSION */ + +#define H_SET_TEXT_SIZE(h,v) ((h)->header.a_text = md_section_align(SEG_TEXT, (v))) +#define H_SET_DATA_SIZE(h,v) ((h)->header.a_data = md_section_align(SEG_DATA, (v))) +#define H_SET_BSS_SIZE(h,v) ((h)->header.a_bss = md_section_align(SEG_BSS, (v))) + +#define H_SET_RELOCATION_SIZE(h,t,d) (H_SET_TEXT_RELOCATION_SIZE((h),(t)),\ + H_SET_DATA_RELOCATION_SIZE((h),(d))) + +#define H_SET_TEXT_RELOCATION_SIZE(h,v) ((h)->header.a_trsize = (v)) +#define H_SET_DATA_RELOCATION_SIZE(h,v) ((h)->header.a_drsize = (v)) +#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->header.a_syms = (v) * \ + sizeof(struct nlist)) + +#define H_SET_MAGIC_NUMBER(h,v) ((h)->header.a_info = (v)) + +#define H_SET_ENTRY_POINT(h,v) ((h)->header.a_entry = (v)) +#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v)) +#ifdef EXEC_MACHINE_TYPE +#define H_SET_MACHINE_TYPE(h,v) ((h)->header.a_machtype = (v)) +#endif /* EXEC_MACHINE_TYPE */ +#ifdef EXEC_VERSION +#define H_SET_VERSION(h,v) ((h)->header.a_version = (v)) +#endif /* EXEC_VERSION */ + +/* + * Current means for getting the name of a segment. + * This will change for infinite-segments support (e.g. COFF). + */ +#define segment_name(seg) ( seg_name[(int)(seg)] ) +extern char *const seg_name[]; + +typedef struct { + struct exec header; /* a.out header */ + long string_table_size; /* names + '\0' + sizeof(int) */ +} object_headers; + +/* line numbering stuff. */ +#define OBJ_EMIT_LINENO(a, b, c) ; +#define obj_pre_write_hook(a) ; + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of obj-aout.h */ diff --git a/gas/config/obj-bout.c b/gas/config/obj-bout.c new file mode 100644 index 0000000..abfdaa8 --- /dev/null +++ b/gas/config/obj-bout.c @@ -0,0 +1,485 @@ +/* b.out object file format + Copyright (C) 1989, 1990, 1991 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 1, +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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +#include "as.h" +#include "obstack.h" + +const short /* in: segT out: N_TYPE bits */ +seg_N_TYPE[] = { + N_ABS, + N_TEXT, + N_DATA, + N_BSS, + N_UNDF, /* unknown */ + N_UNDF, /* absent */ + N_UNDF, /* pass1 */ + N_UNDF, /* error */ + N_UNDF, /* bignum/flonum */ + N_UNDF, /* difference */ + N_REGISTER, /* register */ +}; + +const segT N_TYPE_seg [N_TYPE+2] = { /* N_TYPE == 0x1E = 32-2 */ + SEG_UNKNOWN, /* N_UNDF == 0 */ + SEG_GOOF, + SEG_ABSOLUTE, /* N_ABS == 2 */ + SEG_GOOF, + SEG_TEXT, /* N_TEXT == 4 */ + SEG_GOOF, + SEG_DATA, /* N_DATA == 6 */ + SEG_GOOF, + SEG_BSS, /* N_BSS == 8 */ + SEG_GOOF, + SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, + SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, + SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, + SEG_REGISTER, /* dummy N_REGISTER for regs = 30 */ + SEG_GOOF, +}; + +#ifdef __STDC__ +static void obj_bout_stab(int what); +static void obj_bout_line(void); +static void obj_bout_desc(void); +#else /* __STDC__ */ +static void obj_bout_desc(); +static void obj_bout_stab(); +static void obj_bout_line(); +#endif /* __STDC__ */ + +const pseudo_typeS obj_pseudo_table[] = { + /* stabs (aka a.out aka b.out directives for debug symbols) */ + { "desc", obj_bout_desc, 0 }, /* def */ + { "line", obj_bout_line, 0 }, /* source code line number */ + { "stabd", obj_bout_stab, 'd' }, /* stabs */ + { "stabn", obj_bout_stab, 'n' }, /* stabs */ + { "stabs", obj_bout_stab, 's' }, /* stabs */ + + /* coff debugging directives. Currently ignored silently */ + { "def", s_ignore, 0 }, + { "dim", s_ignore, 0 }, + { "endef", s_ignore, 0 }, + { "ln", s_ignore, 0 }, + { "scl", s_ignore, 0 }, + { "size", s_ignore, 0 }, + { "tag", s_ignore, 0 }, + { "type", s_ignore, 0 }, + { "val", s_ignore, 0 }, + + /* other stuff we don't handle */ + { "ABORT", s_ignore, 0 }, + { "ident", s_ignore, 0 }, + + { NULL} /* end sentinel */ +}; /* obj_pseudo_table */ + +/* Relocation. */ + +/* + * In: length of relocation (or of address) in chars: 1, 2 or 4. + * Out: GNU LD relocation length code: 0, 1, or 2. + */ + +static unsigned char +nbytes_r_length [] = { + 42, 0, 1, 42, 2 + }; + +/* + * emit_relocations() + * + * Crawl along a fixS chain. Emit the segment's relocations. + */ +void obj_emit_relocations(where, fixP, segment_address_in_file) +char **where; +fixS *fixP; /* Fixup chain for this segment. */ +relax_addressT segment_address_in_file; +{ + struct reloc_info_generic ri; + register symbolS * symbolP; + + /* If a machine dependent emitter is needed, call it instead. */ + if (md_emit_relocations) { + (*md_emit_relocations) (fixP, segment_address_in_file); + return; + } + + + /* JF this is for paranoia */ + bzero((char *)&ri,sizeof(ri)); + for (; fixP; fixP = fixP->fx_next) { + if ((symbolP = fixP->fx_addsy) != 0) { + ri . r_bsr = fixP->fx_bsr; + ri . r_disp = fixP->fx_im_disp; + ri . r_callj = fixP->fx_callj; + ri . r_length = nbytes_r_length [fixP->fx_size]; + ri . r_pcrel = fixP->fx_pcrel; + ri . r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file; + + if (S_GET_TYPE(symbolP) == N_UNDF) { + ri . r_extern = 1; + ri . r_symbolnum = symbolP->sy_number; + } else { + ri . r_extern = 0; + ri . r_symbolnum = S_GET_TYPE(symbolP); + } + + /* Output the relocation information in machine-dependent form. */ + md_ri_to_chars(*where, &ri); + *where += md_reloc_size; + } + } +} /* emit_relocations() */ + +/* Aout file generation & utilities */ + +/* Convert a lvalue to machine dependent data */ +void obj_header_append(where, headers) +char **where; +object_headers *headers; +{ + /* Always leave in host byte order */ + + headers->header.a_talign = section_alignment[SEG_TEXT]; + + if (headers->header.a_talign < 2){ + headers->header.a_talign = 2; + } /* force to at least 2 */ + + headers->header.a_dalign = section_alignment[SEG_DATA]; + headers->header.a_balign = section_alignment[SEG_BSS]; + + headers->header.a_tload = 0; + headers->header.a_dload = md_section_align(SEG_DATA, headers->header.a_text); + + append(where, (char *) &headers->header, sizeof(headers->header)); +} /* a_header_append() */ + +void obj_symbol_to_chars(where, symbolP) +char **where; +symbolS *symbolP; +{ + /* leave in host byte order */ + append(where, (char *)&symbolP->sy_symbol, sizeof(obj_symbol_type)); +} /* obj_symbol_to_chars() */ + +void obj_emit_symbols(where, symbol_rootP) +char **where; +symbolS *symbol_rootP; +{ + symbolS * symbolP; + + /* + * Emit all symbols left in the symbol chain. + */ + for(symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + /* Used to save the offset of the name. It is used to point + to the string in memory but must be a file offset. */ + char *temp; + + temp = S_GET_NAME(symbolP); + S_SET_OFFSET(symbolP, symbolP->sy_name_offset); + + /* Any symbol still undefined and is not a dbg symbol is made N_EXT. */ + if (!S_IS_DEBUG(symbolP) && !S_IS_DEFINED(symbolP)) S_SET_EXTERNAL(symbolP); + + obj_symbol_to_chars(where, symbolP); + S_SET_NAME(symbolP,temp); + } +} /* emit_symbols() */ + +void obj_symbol_new_hook(symbolP) +symbolS *symbolP; +{ + S_SET_OTHER(symbolP, 0); + S_SET_DESC(symbolP, 0); + return; +} /* obj_symbol_new_hook() */ + +static void obj_bout_line() { + /* Assume delimiter is part of expression. */ + /* BSD4.2 as fails with delightful bug, so we */ + /* are not being incompatible here. */ + new_logical_line ((char *)NULL, (int)(get_absolute_expression ())); + demand_empty_rest_of_line(); +} /* obj_bout_line() */ + +/* + * stab() + * + * Handle .stabX directives, which used to be open-coded. + * So much creeping featurism overloaded the semantics that we decided + * to put all .stabX thinking in one place. Here. + * + * We try to make any .stabX directive legal. Other people's AS will often + * do assembly-time consistency checks: eg assigning meaning to n_type bits + * and "protecting" you from setting them to certain values. (They also zero + * certain bits before emitting symbols. Tut tut.) + * + * If an expression is not absolute we either gripe or use the relocation + * information. Other people's assemblers silently forget information they + * don't need and invent information they need that you didn't supply. + * + * .stabX directives always make a symbol table entry. It may be junk if + * the rest of your .stabX directive is malformed. + */ +static void obj_bout_stab(what) +int what; +{ + register symbolS * symbolP = 0; + register char * string; + int saved_type = 0; + int length; + int goof; /* TRUE if we have aborted. */ + long longint; + +/* + * Enter with input_line_pointer pointing past .stabX and any following + * whitespace. + */ + goof = 0; /* JF who forgot this?? */ + if (what == 's') { + string = demand_copy_C_string(& length); + SKIP_WHITESPACE(); + if (*input_line_pointer == ',') + input_line_pointer ++; + else { + as_bad("I need a comma after symbol's name"); + goof = 1; + } + } else + string = ""; + +/* + * Input_line_pointer->after ','. String->symbol name. + */ + if (!goof) { + symbolP = symbol_new(string, + SEG_UNKNOWN, + 0, + (struct frag *)0); + switch (what) { + case 'd': + S_SET_NAME(symbolP,NULL); /* .stabd feature. */ + S_SET_VALUE(symbolP,obstack_next_free(&frags) - + frag_now->fr_literal); + symbolP->sy_frag = frag_now; + break; + + case 'n': + symbolP->sy_frag = &zero_address_frag; + break; + + case 's': + symbolP->sy_frag = & zero_address_frag; + break; + + default: + BAD_CASE(what); + break; + } + if (get_absolute_expression_and_terminator(& longint) == ',') + symbolP->sy_symbol.n_type = saved_type = longint; + else { + as_bad("I want a comma after the n_type expression"); + goof = 1; + input_line_pointer--; /* Backup over a non-',' char. */ + } + } + if (! goof) { + if (get_absolute_expression_and_terminator (& longint) == ',') + S_SET_OTHER(symbolP,longint); + else { + as_bad("I want a comma after the n_other expression"); + goof = 1; + input_line_pointer--; /* Backup over a non-',' char. */ + } + } + if (! goof) { + S_SET_DESC(symbolP, get_absolute_expression ()); + if (what == 's' || what == 'n') { + if (* input_line_pointer != ',') { + as_bad("I want a comma after the n_desc expression"); + goof = 1; + } else { + input_line_pointer ++; + } + } + } + if ((! goof) && (what=='s' || what=='n')) { + pseudo_set(symbolP); + symbolP->sy_symbol.n_type = saved_type; + } + if (goof) + ignore_rest_of_line (); + else + demand_empty_rest_of_line (); +} /* obj_bout_stab() */ + +static void obj_bout_desc() { + register char *name; + register char c; + register char *p; + register symbolS * symbolP; + register int temp; + + /* + * Frob invented at RMS' request. Set the n_desc of a symbol. + */ + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + * p = c; + SKIP_WHITESPACE(); + if (*input_line_pointer != ',') { + *p = 0; + as_bad("Expected comma after name \"%s\"", name); + *p = c; + ignore_rest_of_line(); + } else { + input_line_pointer ++; + temp = get_absolute_expression (); + *p = 0; + symbolP = symbol_find_or_make(name); + *p = c; + S_SET_DESC(symbolP,temp); + } + demand_empty_rest_of_line(); +} /* obj_bout_desc() */ + +void obj_read_begin_hook() { + return; +} /* obj_read_begin_hook() */ + +void obj_crawl_symbol_chain(headers) +object_headers *headers; +{ + symbolS **symbolPP; + symbolS *symbolP; + int symbol_number = 0; + + /* JF deal with forward references first... */ + for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + if (symbolP->sy_forward) { + S_SET_VALUE(symbolP, S_GET_VALUE(symbolP) + + S_GET_VALUE(symbolP->sy_forward) + + symbolP->sy_forward->sy_frag->fr_address); + + symbolP->sy_forward=0; + } /* if it has a forward reference */ + } /* walk the symbol chain */ + + tc_crawl_symbol_chain(headers); + + symbolPP = & symbol_rootP; /*->last symbol chain link. */ + while ((symbolP = *symbolPP) != NULL) { + if (flagseen['R'] && (S_GET_SEGMENT(symbolP) == SEG_DATA)) { + S_SET_SEGMENT(symbolP, SEG_TEXT); + } /* if pusing data into text */ + + S_SET_VALUE(symbolP, S_GET_VALUE(symbolP) + symbolP->sy_frag->fr_address); + + /* OK, here is how we decide which symbols go out into the + brave new symtab. Symbols that do are: + + * symbols with no name (stabd's?) + * symbols with debug info in their N_TYPE + + Symbols that don't are: + * symbols that are registers + * symbols with \1 as their 3rd character (numeric labels) + * "local labels" as defined by S_LOCAL_NAME(name) + if the -L switch was passed to gas. + + All other symbols are output. We complain if a deleted + symbol was marked external. */ + + + if (1 + && !S_IS_REGISTER(symbolP) + && (!S_GET_NAME(symbolP) + || S_IS_DEBUG(symbolP) +#ifdef TC_I960 + /* FIXME-SOON this ifdef seems highly dubious to me. xoxorich. */ + || !S_IS_DEFINED(symbolP) + || S_IS_EXTERNAL(symbolP) +#endif /* TC_I960 */ + || (S_GET_NAME(symbolP)[0] != '\001' && (flagseen ['L'] || ! S_LOCAL_NAME(symbolP))))) { + symbolP->sy_number = symbol_number++; + + /* The + 1 after strlen account for the \0 at the + end of each string */ + if (!S_IS_STABD(symbolP)) { + /* Ordinary case. */ + symbolP->sy_name_offset = string_byte_count; + string_byte_count += strlen(S_GET_NAME(symbolP)) + 1; + } + else /* .Stabd case. */ + symbolP->sy_name_offset = 0; + symbolPP = &(symbol_next(symbolP)); + } else { + if (S_IS_EXTERNAL(symbolP) || !S_IS_DEFINED(symbolP)) { + as_bad("Local symbol %s never defined", S_GET_NAME(symbolP)); + } /* oops. */ + + /* Unhook it from the chain */ + *symbolPP = symbol_next(symbolP); + } /* if this symbol should be in the output */ + } /* for each symbol */ + + H_SET_SYMBOL_TABLE_SIZE(headers, symbol_number); + + return; +} /* obj_crawl_symbol_chain() */ + +/* + * Find strings by crawling along symbol table chain. + */ + +void obj_emit_strings(where) +char **where; +{ + symbolS *symbolP; + +#ifdef CROSS_ASSEMBLE + /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */ + md_number_to_chars(*where, string_byte_count, sizeof(string_byte_count)); + *where += sizeof(string_byte_count); +#else /* CROSS_ASSEMBLE */ + append(where, (char *) &string_byte_count, (unsigned long) sizeof(string_byte_count)); +#endif /* CROSS_ASSEMBLE */ + + for(symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + if(S_GET_NAME(symbolP)) + append(where, S_GET_NAME(symbolP), (unsigned long)(strlen (S_GET_NAME(symbolP)) + 1)); + } /* walk symbol chain */ + + return; +} /* obj_emit_strings() */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of obj-bout.c */ diff --git a/gas/config/obj-bout.h b/gas/config/obj-bout.h new file mode 100644 index 0000000..fc95a35 --- /dev/null +++ b/gas/config/obj-bout.h @@ -0,0 +1,312 @@ +/* b.out object file format + Copyright (C) 1989, 1990, 1991 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 1, +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, 675 Mass Ave, Cambridge, MA +02139, USA. */ + +/* $Id$ */ + +/* + * This file is a modified version of 'a.out.h'. It is to be used in all GNU + * tools modified to support the i80960 b.out format (or tools that operate on + * object files created by such tools). + * + * All i80960 development is done in a CROSS-DEVELOPMENT environment. I.e., + * object code is generated on, and executed under the direction of a symbolic + * debugger running on, a host system. We do not want to be subject to the + * vagaries of which host it is or whether it supports COFF or a.out format, or + * anything else. We DO want to: + * + * o always generate the same format object files, regardless of host. + * + * o have an 'a.out' header that we can modify for our own purposes + * (the 80960 is typically an embedded processor and may require + * enhanced linker support that the normal a.out.h header can't + * accommodate). + * + * As for byte-ordering, the following rules apply: + * + * o Text and data that is actually downloaded to the target is always + * in i80960 (little-endian) order. + * + * o All other numbers (in the header, symbols, relocation directives) + * are in host byte-order: object files CANNOT be lifted from a + * little-end host and used on a big-endian (or vice versa) without + * modification. + * + * o The downloader ('comm960') takes care to generate a pseudo-header + * with correct (i80960) byte-ordering before shipping text and data + * off to the NINDY monitor in the target systems. Symbols and + * relocation info are never sent to the target. + */ + + +#define OBJ_BOUT 1 + +#include "targ-cpu.h" + + /* bout uses host byte order for headers */ +#ifdef CROSS_ASSEMBLE +#undef CROSS_ASSEMBLE +#endif /* CROSS_ASSEMBLE */ + + /* We want \v. */ +#define BACKSLASH_V 1 + +#define OBJ_DEFAULT_OUTPUT_FILE_NAME "b.out" + +extern const short seg_N_TYPE[]; +extern const segT N_TYPE_seg[]; + +#define BMAGIC 0415 +/* We don't accept the following (see N_BADMAG macro). + * They're just here so GNU code will compile. + */ +#define OMAGIC 0407 /* old impure format */ +#define NMAGIC 0410 /* read-only text */ +#define ZMAGIC 0413 /* demand load format */ + +#ifndef DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE +#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (BMAGIC) +#endif /* DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE */ + +/* FILE HEADER + * All 'lengths' are given as a number of bytes. + * All 'alignments' are for relinkable files only; an alignment of + * 'n' indicates the corresponding segment must begin at an + * address that is a multiple of (2**n). + */ +struct exec { + /* Standard stuff */ + unsigned long a_magic; /* Identifies this as a b.out file */ + unsigned long a_text; /* Length of text */ + unsigned long a_data; /* Length of data */ + unsigned long a_bss; /* Length of runtime uninitialized data area */ + unsigned long a_syms; /* Length of symbol table */ + unsigned long a_entry; /* Runtime start address */ + unsigned long a_trsize; /* Length of text relocation info */ + unsigned long a_drsize; /* Length of data relocation info */ + + /* Added for i960 */ + unsigned long a_tload; /* Text runtime load address */ + unsigned long a_dload; /* Data runtime load address */ + unsigned char a_talign; /* Alignment of text segment */ + unsigned char a_dalign; /* Alignment of data segment */ + unsigned char a_balign; /* Alignment of bss segment */ + unsigned char unused; /* (Just to make struct size a multiple of 4) */ +}; + +#define N_BADMAG(x) (((x).a_magic)!=BMAGIC) +#define N_TXTOFF(x) ( sizeof(struct exec) ) +#define N_DATOFF(x) ( N_TXTOFF(x) + (x).a_text ) +#define N_TROFF(x) ( N_DATOFF(x) + (x).a_data ) +#define N_DROFF(x) ( N_TROFF(x) + (x).a_trsize ) +#define N_SYMOFF(x) ( N_DROFF(x) + (x).a_drsize ) +#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms ) + +/* A single entry in the symbol table + */ +struct nlist { + union { + char *n_name; + struct nlist *n_next; + long n_strx; /* Index into string table */ + } n_un; + unsigned char n_type; /* See below */ + char n_other; /* Used in i80960 support -- see below */ + short n_desc; + unsigned long n_value; +}; + +typedef struct nlist obj_symbol_type; + +/* Legal values of n_type + */ +#define N_UNDF 0 /* Undefined symbol */ +#define N_ABS 2 /* Absolute symbol */ +#define N_TEXT 4 /* Text symbol */ +#define N_DATA 6 /* Data symbol */ +#define N_BSS 8 /* BSS symbol */ +#define N_FN 31 /* Filename symbol */ + +#define N_EXT 1 /* External symbol (OR'd in with one of above) */ +#define N_TYPE 036 /* Mask for all the type bits */ +#define N_STAB 0340 /* Mask for all bits used for SDB entries */ + +#ifndef CUSTOM_RELOC_FORMAT +struct relocation_info { + int r_address; /* File address of item to be relocated */ + unsigned + r_index:24,/* Index of symbol on which relocation is based*/ + r_pcrel:1, /* 1 => relocate PC-relative; else absolute + * On i960, pc-relative implies 24-bit + * address, absolute implies 32-bit. + */ + r_length:2, /* Number of bytes to relocate: + * 0 => 1 byte + * 1 => 2 bytes + * 2 => 4 bytes -- only value used for i960 + */ + r_extern:1, + r_bsr:1, /* Something for the GNU NS32K assembler */ + r_disp:1, /* Something for the GNU NS32K assembler */ + r_callj:1, /* 1 if relocation target is an i960 'callj' */ + nuthin:1; /* Unused */ +}; +#endif /* CUSTOM_RELOC_FORMAT */ + +/* If compiler generate leading underscores, remove them. */ + +#ifndef STRIP_UNDERSCORE +#define STRIP_UNDERSCORE 0 +#endif /* STRIP_UNDERSCORE */ + +/* + * Macros to extract information from a symbol table entry. + * This syntaxic indirection allows independence regarding a.out or coff. + * The argument (s) of all these macros is a pointer to a symbol table entry. + */ + +/* Predicates */ +/* True if the symbol is external */ +#define S_IS_EXTERNAL(s) ((s)->sy_symbol.n_type & N_EXT) + +/* True if symbol has been defined, ie is in N_{TEXT,DATA,BSS,ABS} or N_EXT */ +#define S_IS_DEFINED(s) ((S_GET_TYPE(s) != N_UNDF) || (S_GET_DESC(s) != 0)) +#define S_IS_REGISTER(s) ((s)->sy_symbol.n_type == N_REGISTER) + +/* True if a debug special symbol entry */ +#define S_IS_DEBUG(s) ((s)->sy_symbol.n_type & N_STAB) +/* True if a symbol is local symbol name */ +/* A symbol name whose name begin with ^A is a gas internal pseudo symbol + nameless symbols come from .stab directives. */ +#define S_IS_LOCAL(s) (S_GET_NAME(s) && \ + !S_IS_DEBUG(s) && \ + (S_GET_NAME(s)[0] == '\001' || \ + (S_LOCAL_NAME(s) && !flagseen['L']))) +/* True if a symbol is not defined in this file */ +#define S_IS_EXTERN(s) ((s)->sy_symbol.n_type & N_EXT) +/* True if the symbol has been generated because of a .stabd directive */ +#define S_IS_STABD(s) (S_GET_NAME(s) == NULL) + +/* Accessors */ +/* The value of the symbol */ +#define S_GET_VALUE(s) ((long) ((s)->sy_symbol.n_value)) +/* The name of the symbol */ +#define S_GET_NAME(s) ((s)->sy_symbol.n_un.n_name) +/* The pointer to the string table */ +#define S_GET_OFFSET(s) ((s)->sy_symbol.n_un.n_strx) +/* The type of the symbol */ +#define S_GET_TYPE(s) ((s)->sy_symbol.n_type & N_TYPE) +/* The numeric value of the segment */ +#define S_GET_SEGMENT(s) (N_TYPE_seg[S_GET_TYPE(s)]) +/* The n_other expression value */ +#define S_GET_OTHER(s) ((s)->sy_symbol.n_other) +/* The n_desc expression value */ +#define S_GET_DESC(s) ((s)->sy_symbol.n_desc) + +/* Modifiers */ +/* Set the value of the symbol */ +#define S_SET_VALUE(s,v) ((s)->sy_symbol.n_value = (unsigned long) (v)) +/* Assume that a symbol cannot be simultaneously in more than on segment */ + /* set segment */ +#define S_SET_SEGMENT(s,seg) ((s)->sy_symbol.n_type &= ~N_TYPE,(s)->sy_symbol.n_type|=SEGMENT_TO_SYMBOL_TYPE(seg)) +/* The symbol is external */ +#define S_SET_EXTERNAL(s) ((s)->sy_symbol.n_type |= N_EXT) +/* The symbol is not external */ +#define S_CLEAR_EXTERNAL(s) ((s)->sy_symbol.n_type &= ~N_EXT) +/* Set the name of the symbol */ +#define S_SET_NAME(s,v) ((s)->sy_symbol.n_un.n_name = (v)) +/* Set the offset in the string table */ +#define S_SET_OFFSET(s,v) ((s)->sy_symbol.n_un.n_strx = (v)) +/* Set the n_other expression value */ +#define S_SET_OTHER(s,v) ((s)->sy_symbol.n_other = (v)) +/* Set the n_desc expression value */ +#define S_SET_DESC(s,v) ((s)->sy_symbol.n_desc = (v)) + +/* File header macro and type definition */ + +#define H_GET_FILE_SIZE(h) (sizeof(struct exec) + \ + H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \ + H_GET_SYMBOL_TABLE_SIZE(h) + \ + H_GET_TEXT_RELOCATION_SIZE(h) + \ + H_GET_DATA_RELOCATION_SIZE(h) + \ + (h)->string_table_size) + +#define H_GET_TEXT_SIZE(h) ((h)->header.a_text) +#define H_GET_DATA_SIZE(h) ((h)->header.a_data) +#define H_GET_BSS_SIZE(h) ((h)->header.a_bss) +#define H_GET_TEXT_RELOCATION_SIZE(h) ((h)->header.a_trsize) +#define H_GET_DATA_RELOCATION_SIZE(h) ((h)->header.a_drsize) +#define H_GET_SYMBOL_TABLE_SIZE(h) ((h)->header.a_syms) +#define H_GET_MAGIC_NUMBER(h) ((h)->header.a_info) +#define H_GET_ENTRY_POINT(h) ((h)->header.a_entry) +#define H_GET_STRING_SIZE(h) ((h)->string_table_size) +#ifdef EXEC_MACHINE_TYPE +#define H_GET_MACHINE_TYPE(h) ((h)->header.a_machtype) +#endif /* EXEC_MACHINE_TYPE */ +#ifdef EXEC_VERSION +#define H_GET_VERSION(h) ((h)->header.a_version) +#endif /* EXEC_VERSION */ + +#define H_SET_TEXT_SIZE(h,v) ((h)->header.a_text = md_section_align(SEG_TEXT, (v))) +#define H_SET_DATA_SIZE(h,v) ((h)->header.a_data = md_section_align(SEG_DATA, (v))) +#define H_SET_BSS_SIZE(h,v) ((h)->header.a_bss = md_section_align(SEG_BSS, (v))) + +#define H_SET_RELOCATION_SIZE(h,t,d) (H_SET_TEXT_RELOCATION_SIZE((h),(t)),\ + H_SET_DATA_RELOCATION_SIZE((h),(d))) + +#define H_SET_TEXT_RELOCATION_SIZE(h,v) ((h)->header.a_trsize = (v)) +#define H_SET_DATA_RELOCATION_SIZE(h,v) ((h)->header.a_drsize = (v)) +#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->header.a_syms = (v) * \ + sizeof(struct nlist)) + +#define H_SET_MAGIC_NUMBER(h,v) ((h)->header.a_magic = (v)) + +#define H_SET_ENTRY_POINT(h,v) ((h)->header.a_entry = (v)) +#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v)) +#ifdef EXEC_MACHINE_TYPE +#define H_SET_MACHINE_TYPE(h,v) ((h)->header.a_machtype = (v)) +#endif /* EXEC_MACHINE_TYPE */ +#ifdef EXEC_VERSION +#define H_SET_VERSION(h,v) ((h)->header.a_version = (v)) +#endif /* EXEC_VERSION */ + +/* + * Current means for getting the name of a segment. + * This will change for infinite-segments support (e.g. COFF). + */ +#define segment_name(seg) ( seg_name[(int)(seg)] ) +extern char *const seg_name[]; + +typedef struct { + struct exec header; /* a.out header */ + long string_table_size; /* names + '\0' + sizeof(int) */ +} object_headers; + +/* unused hooks. */ +#define OBJ_EMIT_LINENO(a, b, c) ; +#define obj_pre_write_hook(a) ; + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of obj-bout.h */ diff --git a/gas/config/obj-coff.c b/gas/config/obj-coff.c new file mode 100644 index 0000000..ef48334 --- /dev/null +++ b/gas/config/obj-coff.c @@ -0,0 +1,1772 @@ +/* coff object file format + Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc. + +This file is part of GAS. + +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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +#include "as.h" + +#include "obstack.h" + +lineno* lineno_rootP; + +const short seg_N_TYPE[] = { /* in: segT out: N_TYPE bits */ + C_ABS_SECTION, + C_TEXT_SECTION, + C_DATA_SECTION, + C_BSS_SECTION, + C_UNDEF_SECTION, /* SEG_UNKNOWN */ + C_UNDEF_SECTION, /* SEG_ABSENT */ + C_UNDEF_SECTION, /* SEG_PASS1 */ + C_UNDEF_SECTION, /* SEG_GOOF */ + C_UNDEF_SECTION, /* SEG_BIG */ + C_UNDEF_SECTION, /* SEG_DIFFERENCE */ + C_DEBUG_SECTION, /* SEG_DEBUG */ + C_NTV_SECTION, /* SEG_NTV */ + C_PTV_SECTION, /* SEG_PTV */ +}; + + +/* Add 4 to the real value to get the index and compensate the negatives */ + +const segT N_TYPE_seg [32] = +{ + SEG_PTV, /* C_PTV_SECTION == -4 */ + SEG_NTV, /* C_NTV_SECTION == -3 */ + SEG_DEBUG, /* C_DEBUG_SECTION == -2 */ + SEG_ABSOLUTE, /* C_ABS_SECTION == -1 */ + SEG_UNKNOWN, /* C_UNDEF_SECTION == 0 */ + SEG_TEXT, /* C_TEXT_SECTION == 1 */ + SEG_DATA, /* C_DATA_SECTION == 2 */ + SEG_BSS, /* C_BSS_SECTION == 3 */ + SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF, + SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF, + SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF,SEG_GOOF +}; + +#ifdef __STDC__ + +char *s_get_name(symbolS *s); +static symbolS *tag_find_or_make(char *name); +static symbolS* tag_find(char *name); +static void c_section_header_append(char **where, SCNHDR *header); +static void obj_coff_def(int what); +static void obj_coff_dim(void); +static void obj_coff_endef(void); +static void obj_coff_line(void); +static void obj_coff_ln(void); +static void obj_coff_scl(void); +static void obj_coff_size(void); +static void obj_coff_stab(int what); +static void obj_coff_tag(void); +static void obj_coff_type(void); +static void obj_coff_val(void); +static void tag_init(void); +static void tag_insert(char *name, symbolS *symbolP); + +#else + +char *s_get_name(); +static symbolS *tag_find(); +static symbolS *tag_find_or_make(); +static void c_section_header_append(); +static void obj_coff_def(); +static void obj_coff_dim(); +static void obj_coff_endef(); +static void obj_coff_line(); +static void obj_coff_ln(); +static void obj_coff_scl(); +static void obj_coff_size(); +static void obj_coff_stab(); +static void obj_coff_tag(); +static void obj_coff_type(); +static void obj_coff_val(); +static void tag_init(); +static void tag_insert(); + +#endif /* __STDC__ */ + +static struct hash_control *tag_hash; +static symbolS *def_symbol_in_progress = NULL; + +const pseudo_typeS obj_pseudo_table[] = { +#ifndef IGNORE_DEBUG + { "def", obj_coff_def, 0 }, + { "dim", obj_coff_dim, 0 }, + { "endef", obj_coff_endef, 0 }, + { "line", obj_coff_line, 0 }, + { "ln", obj_coff_ln, 0 }, + { "scl", obj_coff_scl, 0 }, + { "size", obj_coff_size, 0 }, + { "tag", obj_coff_tag, 0 }, + { "type", obj_coff_type, 0 }, + { "val", obj_coff_val, 0 }, +#else + { "def", s_ignore, 0 }, + { "dim", s_ignore, 0 }, + { "endef", s_ignore, 0 }, + { "line", s_ignore, 0 }, + { "ln", s_ignore, 0 }, + { "scl", s_ignore, 0 }, + { "size", s_ignore, 0 }, + { "tag", s_ignore, 0 }, + { "type", s_ignore, 0 }, + { "val", s_ignore, 0 }, +#endif /* ignore debug */ + + /* stabs aka a.out aka b.out directives for debug symbols. + Currently ignored silently. Except for .line which we + guess at from context. */ + { "desc", s_ignore, 0 }, /* def */ +/* { "line", s_ignore, 0 }, */ /* source code line number */ + { "stabd", obj_coff_stab, 'd' }, /* stabs */ + { "stabn", obj_coff_stab, 'n' }, /* stabs */ + { "stabs", obj_coff_stab, 's' }, /* stabs */ + + /* other stuff */ + { "ABORT", s_abort, 0 }, + { "ident", s_ignore, 0 }, + + { NULL} /* end sentinel */ +}; /* obj_pseudo_table */ + + + /* obj dependant output values */ +static SCNHDR bss_section_header; +static SCNHDR data_section_header; +static SCNHDR text_section_header; + +/* Relocation. */ + +/* + * emit_relocations() + * + * Crawl along a fixS chain. Emit the segment's relocations. + */ + +void obj_emit_relocations(where, fixP, segment_address_in_file) +char **where; +fixS *fixP; /* Fixup chain for this segment. */ +relax_addressT segment_address_in_file; +{ + RELOC ri; + symbolS *symbolP; + + bzero((char *)&ri,sizeof(ri)); + for (; fixP; fixP = fixP->fx_next) { + if (symbolP = fixP->fx_addsy) { +#if defined(TC_M68K) + ri.r_type = (fixP->fx_pcrel ? + (fixP->fx_size == 1 ? R_PCRBYTE : + fixP->fx_size == 2 ? R_PCRWORD : + R_PCRLONG): + (fixP->fx_size == 1 ? R_RELBYTE : + fixP->fx_size == 2 ? R_RELWORD : + R_RELLONG)); +#elif defined(TC_I386) + /* FIXME-SOON R_OFF8 & R_DIR16 are a vague guess, completly untested. */ + ri.r_type = (fixP->fx_pcrel ? + (fixP->fx_size == 1 ? R_PCRBYTE : + fixP->fx_size == 2 ? R_PCRWORD : + R_PCRLONG): + (fixP->fx_size == 1 ? R_OFF8 : + fixP->fx_size == 2 ? R_DIR16 : + R_DIR32)); +#elif defined(TC_I960) + ri.r_type = (fixP->fx_pcrel + ? R_IPRMED + : R_RELLONG); +#elif defined(TC_A29K) + ri.r_type = tc_coff_fix2rtype(fixP); +#else + you lose +#endif /* TC_M68K || TC_I386 */ + ri.r_vaddr = fixP->fx_frag->fr_address + fixP->fx_where; + /* If symbol associated to relocation entry is a bss symbol + or undefined symbol just remember the index of the symbol. + Otherwise store the index of the symbol describing the + section the symbol belong to. This heuristic speeds up ld. + */ + /* Local symbols can generate relocation information. In case + of structure return for instance. But they have no symbol + number because they won't be emitted in the final object. + In the case where they are in the BSS section, this leads + to an incorrect r_symndx. + Under bsd the loader do not care if the symbol reference is + incorrect. But the SYS V ld complains about this. To avoid + this we associate the symbol to the associated section, + *even* if it is the BSS section. */ + /* If someone can tell me why the other symbols of the bss + section are not associated with the .bss section entry, + I'd be gratefull. I guess that it has to do with the special + nature of the .bss section. Or maybe this is because the + bss symbols are declared in the common section and can + be resized later. Can it break code some where ? */ + ri.r_symndx = (S_GET_SEGMENT(symbolP) == SEG_TEXT + ? dot_text_symbol->sy_number + : (S_GET_SEGMENT(symbolP) == SEG_DATA + ? dot_data_symbol->sy_number + : ((SF_GET_LOCAL(symbolP) + ? dot_bss_symbol->sy_number + : symbolP->sy_number)))); /* bss or undefined */ + + /* md_ri_to_chars((char *) &ri, ri); */ /* Last step : write md f */ + append(where, (char *) &ri, sizeof(ri)); + +#ifdef TC_I960 + if (fixP->fx_callj) { + ri.r_type = R_OPTCALL; + append(where, (char *) &ri, sizeof(ri)); + } /* if it's a callj, do it again for the opcode */ +#endif /* TC_I960 */ + + } /* if there's a symbol */ + } /* for each fixP */ + + return; +} /* obj_emit_relocations() */ + +/* Coff file generation & utilities */ + +void obj_header_append(where, headers) +char **where; +object_headers *headers; +{ + tc_headers_hook(headers); + +#ifdef CROSS_ASSEMBLE + /* Eventually swap bytes for cross compilation for file header */ + md_number_to_chars(*where, headers->filehdr.f_magic, sizeof(headers->filehdr.f_magic)); + *where += sizeof(headers->filehdr.f_magic); + md_number_to_chars(*where, headers->filehdr.f_nscns, sizeof(headers->filehdr.f_nscns)); + *where += sizeof(headers->filehdr.f_nscns); + md_number_to_chars(*where, headers->filehdr.f_timdat, sizeof(headers->filehdr.f_timdat)); + *where += sizeof(headers->filehdr.f_timdat); + md_number_to_chars(*where, headers->filehdr.f_symptr, sizeof(headers->filehdr.f_symptr)); + *where += sizeof(headers->filehdr.f_symptr); + md_number_to_chars(*where, headers->filehdr.f_nsyms, sizeof(headers->filehdr.f_nsyms)); + *where += sizeof(headers->filehdr.f_nsyms); + md_number_to_chars(*where, headers->filehdr.f_opthdr, sizeof(headers->filehdr.f_opthdr)); + *where += sizeof(headers->filehdr.f_opthdr); + md_number_to_chars(*where, headers->filehdr.f_flags, sizeof(headers->filehdr.f_flags)); + *where += sizeof(headers->filehdr.f_flags); + +#ifndef OBJ_COFF_OMIT_OPTIONAL_HEADER + /* Eventually swap bytes for cross compilation for a.out header */ + md_number_to_chars(*where, headers->aouthdr.magic, sizeof(headers->aouthdr.magic)); + *where += sizeof(headers->aouthdr.magic); + md_number_to_chars(*where, headers->aouthdr.vstamp, sizeof(headers->aouthdr.vstamp)); + *where += sizeof(headers->aouthdr.vstamp); + md_number_to_chars(*where, headers->aouthdr.tsize, sizeof(headers->aouthdr.tsize)); + *where += sizeof(headers->aouthdr.tsize); + md_number_to_chars(*where, headers->aouthdr.dsize, sizeof(headers->aouthdr.dsize)); + *where += sizeof(headers->aouthdr.dsize); + md_number_to_chars(*where, headers->aouthdr.bsize, sizeof(headers->aouthdr.bsize)); + *where += sizeof(headers->aouthdr.bsize); + md_number_to_chars(*where, headers->aouthdr.entry, sizeof(headers->aouthdr.entry)); + *where += sizeof(headers->aouthdr.entry); + md_number_to_chars(*where, headers->aouthdr.text_start, sizeof(headers->aouthdr.text_start)); + *where += sizeof(headers->aouthdr.text_start); + md_number_to_chars(*where, headers->aouthdr.data_start, sizeof(headers->aouthdr.data_start)); + *where += sizeof(headers->aouthdr.data_start); +#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */ + +#else /* CROSS_ASSEMBLE */ + + append(where, (char *) &headers->filehdr, sizeof(headers->filehdr)); +#ifndef OBJ_COFF_OMIT_OPTIONAL_HEADER + append(where, (char *) &headers->aouthdr, sizeof(headers->aouthdr)); +#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */ + +#endif /* CROSS_ASSEMBLE */ + + /* Output the section headers */ + c_section_header_append(where, &text_section_header); + c_section_header_append(where, &data_section_header); + c_section_header_append(where, &bss_section_header); + + return; +} /* obj_header_append() */ + +void obj_symbol_to_chars(where, symbolP) +char **where; +symbolS *symbolP; +{ + SYMENT *syment = &symbolP->sy_symbol.ost_entry; + int i; + char numaux = syment->n_numaux; + unsigned short type = S_GET_DATA_TYPE(symbolP); + +#ifdef CROSS_ASSEMBLE + md_number_to_chars(*where, syment->n_value, sizeof(syment->n_value)); + *where += sizeof(syment->n_value); + md_number_to_chars(*where, syment->n_scnum, sizeof(syment->n_scnum)); + *where += sizeof(syment->n_scnum); + md_number_to_chars(*where, syment->n_type, sizeof(syment->n_type)); + *where += sizeof(syment->n_type); + md_number_to_chars(*where, syment->n_sclass, sizeof(syment->n_sclass)); + *where += sizeof(syment->n_sclass); + md_number_to_chars(*where, syment->n_numaux, sizeof(syment->n_numaux)); + *where += sizeof(syment->n_numaux); +#else /* CROSS_ASSEMBLE */ + append(where, (char *) syment, sizeof(*syment)); +#endif /* CROSS_ASSEMBLE */ + + /* Should do the following : if (.file entry) MD(..)... else if (static entry) MD(..) */ + if (numaux > OBJ_COFF_MAX_AUXENTRIES) { + as_bad("Internal error? too many auxents for symbol"); + } /* too many auxents */ + + for (i = 0; i < numaux; ++i) { +#ifdef CROSS_ASSEMBLE +#if 0 /* This code has never been tested */ + /* The most common case, x_sym entry. */ + if ((SF_GET(symbolP) & (SF_FILE | SF_STATICS)) == 0) { + md_number_to_chars(*where, auxP->x_sym.x_tagndx, sizeof(auxP->x_sym.x_tagndx)); + *where += sizeof(auxP->x_sym.x_tagndx); + if (ISFCN(type)) { + md_number_to_chars(*where, auxP->x_sym.x_misc.x_fsize, sizeof(auxP->x_sym.x_misc.x_fsize)); + *where += sizeof(auxP->x_sym.x_misc.x_fsize); + } else { + md_number_to_chars(*where, auxP->x_sym.x_misc.x_lnno, sizeof(auxP->x_sym.x_misc.x_lnno)); + *where += sizeof(auxP->x_sym.x_misc.x_lnno); + md_number_to_chars(*where, auxP->x_sym.x_misc.x_size, sizeof(auxP->x_sym.x_misc.x_size)); + *where += sizeof(auxP->x_sym.x_misc.x_size); + } + if (ISARY(type)) { + register int index; + for (index = 0; index < DIMNUM; index++) + md_number_to_chars(*where, auxP->x_sym.x_fcnary.x_ary.x_dimen[index], sizeof(auxP->x_sym.x_fcnary.x_ary.x_dimen[index])); + *where += sizeof(auxP->x_sym.x_fcnary.x_ary.x_dimen[index]); + } else { + md_number_to_chars(*where, auxP->x_sym.x_fcnary.x_fcn.x_lnnoptr, sizeof(auxP->x_sym.x_fcnary.x_fcn.x_lnnoptr)); + *where += sizeof(auxP->x_sym.x_fcnary.x_fcn.x_lnnoptr); + md_number_to_chars(*where, auxP->x_sym.x_fcnary.x_fcn.x_endndx, sizeof(auxP->x_sym.x_fcnary.x_fcn.x_endndx)); + *where += sizeof(auxP->x_sym.x_fcnary.x_fcn.x_endndx); + } + md_number_to_chars(*where, auxP->x_sym.x_tvndx, sizeof(auxP->x_sym.x_tvndx)); + *where += sizeof(auxP->x_sym.x_tvndx); + } else if (SF_GET_FILE(symbolP)) { /* .file */ + ; + } else if (SF_GET_STATICS(symbolP)) { /* .text, .data, .bss symbols */ + md_number_to_chars(*where, auxP->x_scn.x_scnlen, sizeof(auxP->x_scn.x_scnlen)); + *where += sizeof(auxP->x_scn.x_scnlen); + md_number_to_chars(*where, auxP->x_scn.x_nreloc, sizeof(auxP->x_scn.x_nreloc)); + *where += sizeof(auxP->x_scn.x_nreloc); + md_number_to_chars(*where, auxP->x_scn.x_nlinno, sizeof(auxP->x_scn.x_nlinno)); + *where += sizeof(auxP->x_scn.x_nlinno); + } +#endif /* 0 */ +#else /* CROSS_ASSEMBLE */ + append(where, (char *) &symbolP->sy_symbol.ost_auxent[i], sizeof(symbolP->sy_symbol.ost_auxent[i])); +#endif /* CROSS_ASSEMBLE */ + + }; /* for each aux in use */ + + return; +} /* obj_symbol_to_chars() */ + +static void c_section_header_append(where, header) +char **where; +SCNHDR *header; +{ +#ifdef CROSS_ASSEMBLE + md_number_to_chars(*where, header->s_paddr, sizeof(header->s_paddr)); + *where += sizeof(header->s_paddr); + + md_number_to_chars(*where, header->s_vaddr, sizeof(header->s_vaddr)); + *where += sizeof(header->s_vaddr); + + md_number_to_chars(*where, header->s_size, sizeof(header->s_size)); + *where += sizeof(header->s_size); + + md_number_to_chars(*where, header->s_scnptr, sizeof(header->s_scnptr)); + *where += sizeof(header->s_scnptr); + + md_number_to_chars(*where, header->s_relptr, sizeof(header->s_relptr)); + *where += sizeof(header->s_relptr); + + md_number_to_chars(*where, header->s_lnnoptr, sizeof(header->s_lnnoptr)); + *where += sizeof(header->s_lnnoptr); + + md_number_to_chars(*where, header->s_nreloc, sizeof(header->s_nreloc)); + *where += sizeof(header->s_nreloc); + + md_number_to_chars(*where, header->s_nlnno, sizeof(header->s_nlnno)); + *where += sizeof(header->s_nlnno); + + md_number_to_chars(*where, header->s_flags, sizeof(header->s_flags)); + *where += sizeof(header->s_flags); + +#else /* CROSS_ASSEMBLE */ + + append(where, (char *) header, sizeof(*header)); + +#endif /* CROSS_ASSEMBLE */ + + return; +} /* c_section_header_append() */ + + +void obj_emit_symbols(where, symbol_rootP) +char **where; +symbolS *symbol_rootP; +{ + symbolS *symbolP; + /* + * Emit all symbols left in the symbol chain. + */ + for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + /* Used to save the offset of the name. It is used to point + to the string in memory but must be a file offset. */ + register char * temp; + + tc_coff_symbol_emit_hook(symbolP); + + temp = S_GET_NAME(symbolP); + if (SF_GET_STRING(symbolP)) { + S_SET_OFFSET(symbolP, symbolP->sy_name_offset); + S_SET_ZEROES(symbolP, 0); + } else { + bzero(symbolP->sy_symbol.ost_entry.n_name, SYMNMLEN); + strncpy(symbolP->sy_symbol.ost_entry.n_name, temp, SYMNMLEN); + } + obj_symbol_to_chars(where, symbolP); + S_SET_NAME(symbolP,temp); + } +} /* obj_emit_symbols() */ + +/* Merge a debug symbol containing debug information into a normal symbol. */ + +void c_symbol_merge(debug, normal) +symbolS *debug; +symbolS *normal; +{ + S_SET_DATA_TYPE(normal, S_GET_DATA_TYPE(debug)); + S_SET_STORAGE_CLASS(normal, S_GET_STORAGE_CLASS(debug)); + + if (S_GET_NUMBER_AUXILIARY(debug) > S_GET_NUMBER_AUXILIARY(normal)) { + S_SET_NUMBER_AUXILIARY(normal, S_GET_NUMBER_AUXILIARY(debug)); + } /* take the most we have */ + + if (S_GET_NUMBER_AUXILIARY(debug) > 0) { + memcpy((char*)&normal->sy_symbol.ost_auxent[0], (char*)&debug->sy_symbol.ost_auxent[0], S_GET_NUMBER_AUXILIARY(debug) * AUXESZ); + } /* Move all the auxiliary information */ + + /* Move the debug flags. */ + SF_SET_DEBUG_FIELD(normal, SF_GET_DEBUG_FIELD(debug)); +} /* c_symbol_merge() */ + +static symbolS *previous_file_symbol = NULL; + +void c_dot_file_symbol(filename) +char *filename; +{ + symbolS* symbolP; + + symbolP = symbol_new(".file", + SEG_DEBUG, + 0, + &zero_address_frag); + + S_SET_STORAGE_CLASS(symbolP, C_FILE); + S_SET_NUMBER_AUXILIARY(symbolP, 1); + SA_SET_FILE_FNAME(symbolP, filename); + SF_SET_DEBUG(symbolP); + S_SET_VALUE(symbolP, (long) previous_file_symbol); + + previous_file_symbol = symbolP; + + /* Make sure that the symbol is first on the symbol chain */ + if (symbol_rootP != symbolP) { + if (symbolP == symbol_lastP) { + symbol_lastP = symbol_lastP->sy_previous; + } /* if it was the last thing on the list */ + + symbol_remove(symbolP, &symbol_rootP, &symbol_lastP); + symbol_insert(symbolP, symbol_rootP, &symbol_rootP, &symbol_lastP); + symbol_rootP = symbolP; + } /* if not first on the list */ + +} /* c_dot_file_symbol() */ +/* + * Build a 'section static' symbol. + */ + +char *c_section_symbol(name, value, length, nreloc, nlnno) +char *name; +long value; +long length; +unsigned short nreloc; +unsigned short nlnno; +{ + symbolS *symbolP; + + symbolP = symbol_new(name, + (name[1] == 't' + ? SEG_TEXT + : (name[1] == 'd' + ? SEG_DATA + : SEG_BSS)), + value, + &zero_address_frag); + + S_SET_STORAGE_CLASS(symbolP, C_STAT); + S_SET_NUMBER_AUXILIARY(symbolP, 1); + + SA_SET_SCN_SCNLEN(symbolP, length); + SA_SET_SCN_NRELOC(symbolP, nreloc); + SA_SET_SCN_NLINNO(symbolP, nlnno); + + SF_SET_STATICS(symbolP); + + return (char*)symbolP; +} /* c_section_symbol() */ + +void c_section_header(header, + name, + core_address, + size, + data_ptr, + reloc_ptr, + lineno_ptr, + reloc_number, + lineno_number, + alignment) +SCNHDR *header; +char *name; +long core_address; +long size; +long data_ptr; +long reloc_ptr; +long lineno_ptr; +long reloc_number; +long lineno_number; +long alignment; +{ + strncpy(header->s_name, name, 8); + header->s_paddr = header->s_vaddr = core_address; + header->s_scnptr = ((header->s_size = size) != 0) ? data_ptr : 0; + header->s_relptr = reloc_ptr; + header->s_lnnoptr = lineno_ptr; + header->s_nreloc = reloc_number; + header->s_nlnno = lineno_number; + +#ifdef OBJ_COFF_SECTION_HEADER_HAS_ALIGNMENT +#ifdef OBJ_COFF_BROKEN_ALIGNMENT + header->s_align = ((name[1] == 'b' || (size > 0)) ? 16 : 0); +#else + header->s_align = ((alignment == 0) + ? 0 + : (1 << alignment)); +#endif /* OBJ_COFF_BROKEN_ALIGNMENT */ +#endif /* OBJ_COFF_SECTION_HEADER_HAS_ALIGNMENT */ + + header->s_flags = STYP_REG | (name[1] == 't' + ? STYP_TEXT + : (name[1] == 'd' + ? STYP_DATA + : (name[1] == 'b' + ? STYP_BSS + : STYP_INFO))); + return; +} /* c_section_header() */ + +/* Line number handling */ + +int function_lineoff = -1; /* Offset in line#s where the last function + started (the odd entry for line #0) */ +int text_lineno_number = 0; +int our_lineno_number = 0; /* we use this to build pointers from .bf's + into the linetable. It should match + exactly the values that are later + assigned in text_lineno_number by + write.c. */ +lineno* lineno_lastP = (lineno*)0; + +int +c_line_new(paddr, line_number, frag) +long paddr; +unsigned short line_number; +fragS* frag; +{ + lineno* new_line = (lineno*)xmalloc(sizeof(lineno)); + + new_line->line.l_addr.l_paddr = paddr; + new_line->line.l_lnno = line_number; + new_line->frag = (char*)frag; + new_line->next = (lineno*)0; + + if (lineno_rootP == (lineno*)0) + lineno_rootP = new_line; + else + lineno_lastP->next = new_line; + lineno_lastP = new_line; + return LINESZ * our_lineno_number++; +} + +void obj_emit_lineno(where, line, file_start) +char **where; +lineno *line; +char *file_start; +{ + LINENO *line_entry; + + for (; line; line = line->next) { + line_entry = &line->line; + + /* FIXME-SOMEDAY Resolving the sy_number of function linno's used to be done in + write_object_file() but their symbols need a fileptr to the lnno, so + I moved this resolution check here. xoxorich. */ + + if (line_entry->l_lnno == 0) { + /* There is a good chance that the symbol pointed to + is not the one that will be emitted and that the + sy_number is not accurate. */ +/* char *name; */ + symbolS *symbolP; + + symbolP = (symbolS *) line_entry->l_addr.l_symndx; + + line_entry->l_addr.l_symndx = symbolP->sy_number; + symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_lnnoptr = *where - file_start; + + } /* if this is a function linno */ + + /* No matter which member of the union we process, they are + both long. */ +#ifdef CROSS_ASSEMBLE + md_number_to_chars(*where, line_entry->l_addr.l_paddr, sizeof(line_entry->l_addr.l_paddr)); + *where += sizeof(line_entry->l_addr.l_paddr); + + md_number_to_chars(*where, line_entry->l_lnno, sizeof(line_entry->l_lnno)); + *where += sizeof(line_entry->l_lnno); +#else /* CROSS_ASSEMBLE */ + append(where, (char *) line_entry, LINESZ); +#endif /* CROSS_ASSEMBLE */ + + } /* for each line number */ + + return ; +} /* obj_emit_lineno() */ + +void obj_symbol_new_hook(symbolP) +symbolS *symbolP; +{ + char underscore = 0; /* Symbol has leading _ */ + + /* Effective symbol */ + /* Store the pointer in the offset. */ + S_SET_ZEROES(symbolP, 0L); + S_SET_DATA_TYPE(symbolP, T_NULL); + S_SET_STORAGE_CLASS(symbolP, 0); + S_SET_NUMBER_AUXILIARY(symbolP, 0); + /* Additional information */ + symbolP->sy_symbol.ost_flags = 0; + /* Auxiliary entries */ + bzero((char*)&symbolP->sy_symbol.ost_auxent[0], AUXESZ); + +#if STRIP_UNDERSCORE + /* Remove leading underscore at the beginning of the symbol. + * This is to be compatible with the standard librairies. + */ + if (*S_GET_NAME(symbolP) == '_') { + underscore = 1; + S_SET_NAME(symbolP, S_GET_NAME(symbolP) + 1); + } /* strip underscore */ +#endif /* STRIP_UNDERSCORE */ + + if (S_IS_STRING(symbolP)) + SF_SET_STRING(symbolP); + if (!underscore && S_IS_LOCAL(symbolP)) + SF_SET_LOCAL(symbolP); + + return; +} /* obj_symbol_new_hook() */ + + /* stack stuff */ +stack* stack_init(chunk_size, element_size) +unsigned long chunk_size; +unsigned long element_size; +{ + stack* st; + + if ((st = (stack*)malloc(sizeof(stack))) == (stack*)0) + return (stack*)0; + if ((st->data = malloc(chunk_size)) == (char*)0) { + free(st); + return (stack*)0; + } + st->pointer = 0; + st->size = chunk_size; + st->chunk_size = chunk_size; + st->element_size = element_size; + return st; +} /* stack_init() */ + +void stack_delete(st) +stack* st; +{ + free(st->data); + free(st); +} + +char *stack_push(st, element) +stack *st; +char *element; +{ + if (st->pointer + st->element_size >= st->size) { + st->size += st->chunk_size; + if ((st->data = xrealloc(st->data, st->size)) == (char*)0) + return (char*)0; + } + memcpy(st->data + st->pointer, element, st->element_size); + st->pointer += st->element_size; + return st->data + st->pointer; +} /* stack_push() */ + +char* stack_pop(st) +stack* st; +{ + if ((st->pointer -= st->element_size) < 0) { + st->pointer = 0; + return (char*)0; + } + return st->data + st->pointer; +} + +char* stack_top(st) +stack* st; +{ + return st->data + st->pointer - st->element_size; +} + + +/* + * Handle .ln directives. + */ + +static void obj_coff_ln() { + if (def_symbol_in_progress != NULL) { + as_warn(".ln pseudo-op inside .def/.endef: ignored."); + demand_empty_rest_of_line(); + return; + } /* wrong context */ + + c_line_new(obstack_next_free(&frags) - frag_now->fr_literal, + get_absolute_expression(), + frag_now); + + demand_empty_rest_of_line(); + return; +} /* obj_coff_line() */ + +/* + * def() + * + * Handle .def directives. + * + * One might ask : why can't we symbol_new if the symbol does not + * already exist and fill it with debug information. Because of + * the C_EFCN special symbol. It would clobber the value of the + * function symbol before we have a chance to notice that it is + * a C_EFCN. And a second reason is that the code is more clear this + * way. (at least I think it is :-). + * + */ + +#define SKIP_SEMI_COLON() while (*input_line_pointer++ != ';') +#define SKIP_WHITESPACES() while (*input_line_pointer == ' ' || \ + *input_line_pointer == '\t') \ + input_line_pointer++; + +static void obj_coff_def(what) +int what; +{ + char name_end; /* Char after the end of name */ + char *symbol_name; /* Name of the debug symbol */ + char *symbol_name_copy; /* Temporary copy of the name */ + unsigned int symbol_name_length; + /*$char* directiveP;$ */ /* Name of the pseudo opcode */ + /*$char directive[MAX_DIRECTIVE];$ */ /* Backup of the directive */ + /*$char end = 0;$ */ /* If 1, stop parsing */ + + if (def_symbol_in_progress != NULL) { + as_warn(".def pseudo-op used inside of .def/.endef: ignored."); + demand_empty_rest_of_line(); + return; + } /* if not inside .def/.endef */ + + SKIP_WHITESPACES(); + + def_symbol_in_progress = (symbolS *) obstack_alloc(¬es, sizeof(*def_symbol_in_progress)); + bzero(def_symbol_in_progress, sizeof(*def_symbol_in_progress)); + + symbol_name = input_line_pointer; + name_end = get_symbol_end(); + symbol_name_length = strlen(symbol_name); + symbol_name_copy = xmalloc(symbol_name_length + 1); + strcpy(symbol_name_copy, symbol_name); + + /* Initialize the new symbol */ +#if STRIP_UNDERSCORE + S_SET_NAME(def_symbol_in_progress, (*symbol_name_copy == '_' + ? symbol_name_copy + 1 + : symbol_name_copy)); +#else /* STRIP_UNDERSCORE */ + S_SET_NAME(def_symbol_in_progress, symbol_name_copy); +#endif /* STRIP_UNDERSCORE */ + /* free(symbol_name_copy); */ + def_symbol_in_progress->sy_name_offset = ~0; + def_symbol_in_progress->sy_number = ~0; + def_symbol_in_progress->sy_frag = &zero_address_frag; + + if (S_IS_STRING(def_symbol_in_progress)) { + SF_SET_STRING(def_symbol_in_progress); + } /* "long" name */ + + *input_line_pointer = name_end; + + demand_empty_rest_of_line(); + return; +} /* obj_coff_def() */ + +unsigned int dim_index; +static void obj_coff_endef() { + symbolS *symbolP; +/* DIM BUG FIX sac@cygnus.com */ + dim_index =0; + if (def_symbol_in_progress == NULL) { + as_warn(".endef pseudo-op used outside of .def/.endef: ignored."); + demand_empty_rest_of_line(); + return; + } /* if not inside .def/.endef */ + + /* Set the section number according to storage class. */ + switch (S_GET_STORAGE_CLASS(def_symbol_in_progress)) { + case C_STRTAG: + case C_ENTAG: + case C_UNTAG: + SF_SET_TAG(def_symbol_in_progress); + /* intentional fallthrough */ + case C_FILE: + case C_TPDEF: + SF_SET_DEBUG(def_symbol_in_progress); + S_SET_SEGMENT(def_symbol_in_progress, SEG_DEBUG); + break; + + case C_EFCN: + SF_SET_LOCAL(def_symbol_in_progress); /* Do not emit this symbol. */ + /* intentional fallthrough */ + case C_BLOCK: + SF_SET_PROCESS(def_symbol_in_progress); /* Will need processing before writing */ + /* intentional fallthrough */ + case C_FCN: + S_SET_SEGMENT(def_symbol_in_progress, SEG_TEXT); + + if (def_symbol_in_progress->sy_symbol.ost_entry.n_name[1] == 'b') { /* .bf */ + if (function_lineoff < 0) { + fprintf(stderr, "`.bf' symbol without preceding function\n"); + } /* missing function symbol */ + SA_GET_SYM_LNNOPTR(def_symbol_in_progress) = function_lineoff; + SF_SET_PROCESS(def_symbol_in_progress); /* Will need relocating */ + function_lineoff = -1; + } + break; + +#ifdef C_AUTOARG + case C_AUTOARG: +#endif /* C_AUTOARG */ + case C_AUTO: + case C_REG: + case C_MOS: + case C_MOE: + case C_MOU: + case C_ARG: + case C_REGPARM: + case C_FIELD: + case C_EOS: + SF_SET_DEBUG(def_symbol_in_progress); + S_SET_SEGMENT(def_symbol_in_progress, SEG_ABSOLUTE); + break; + + case C_EXT: + case C_STAT: + case C_LABEL: + /* Valid but set somewhere else (s_comm, s_lcomm, colon) */ + break; + + case C_USTATIC: + case C_EXTDEF: + case C_ULABEL: + as_warn("unexpected storage class %d", S_GET_STORAGE_CLASS(def_symbol_in_progress)); + break; + } /* switch on storage class */ + + /* Now that we have built a debug symbol, try to + find if we should merge with an existing symbol + or not. If a symbol is C_EFCN or SEG_ABSOLUTE or + untagged SEG_DEBUG it never merges. */ + + /* Two cases for functions. Either debug followed + by definition or definition followed by debug. + For definition first, we will merge the debug + symbol into the definition. For debug first, the + lineno entry MUST point to the definition + function or else it will point off into space + when obj_crawl_symbol_chain() merges the debug + symbol into the real symbol. Therefor, let's + presume the debug symbol is a real function + reference. */ + + /* FIXME-SOON If for some reason the definition + label/symbol is never seen, this will probably + leave an undefined symbol at link time. */ + + if (S_GET_STORAGE_CLASS(def_symbol_in_progress) == C_EFCN + || (S_GET_SEGMENT(def_symbol_in_progress) == SEG_DEBUG + && !SF_GET_TAG(def_symbol_in_progress)) + || S_GET_SEGMENT(def_symbol_in_progress) == SEG_ABSOLUTE + || (symbolP = symbol_find_base(S_GET_NAME(def_symbol_in_progress), DO_NOT_STRIP)) == NULL) { + + symbol_append(def_symbol_in_progress, symbol_lastP, &symbol_rootP, &symbol_lastP); + + } else { + /* This symbol already exists, merge the + newly created symbol into the old one. + This is not mandatory. The linker can + handle duplicate symbols correctly. But I + guess that it save a *lot* of space if + the assembly file defines a lot of + symbols. [loic] */ + + /* The debug entry (def_symbol_in_progress) + is merged into the previous definition. */ + + c_symbol_merge(def_symbol_in_progress, symbolP); + /* FIXME-SOON Should *def_symbol_in_progress be free'd? xoxorich. */ + def_symbol_in_progress = symbolP; + + if (SF_GET_FUNCTION(def_symbol_in_progress) + || SF_GET_TAG(def_symbol_in_progress)) { + /* For functions, and tags, the symbol *must* be where the debug symbol + appears. Move the existing symbol to the current place. */ + /* If it already is at the end of the symbol list, do nothing */ + if (def_symbol_in_progress != symbol_lastP) { + symbol_remove(def_symbol_in_progress, &symbol_rootP, &symbol_lastP); + symbol_append(def_symbol_in_progress, symbol_lastP, &symbol_rootP, &symbol_lastP); + } /* if not already in place */ + } /* if function */ + } /* normal or mergable */ + + if (SF_GET_TAG(def_symbol_in_progress) + && symbol_find_base(S_GET_NAME(def_symbol_in_progress), DO_NOT_STRIP) == NULL) { + tag_insert(S_GET_NAME(def_symbol_in_progress), def_symbol_in_progress); + } /* If symbol is a {structure,union} tag, associate symbol to its name. */ + + if (SF_GET_FUNCTION(def_symbol_in_progress)) { + know(sizeof(def_symbol_in_progress) <= sizeof(long)); + function_lineoff = c_line_new((long) def_symbol_in_progress, 0, &zero_address_frag); + SF_SET_PROCESS(def_symbol_in_progress); + + if (symbolP == NULL) { + /* That is, if this is the first + time we've seen the function... */ + symbol_table_insert(def_symbol_in_progress); + } /* definition follows debug */ + } /* Create the line number entry pointing to the function being defined */ + + def_symbol_in_progress = NULL; + demand_empty_rest_of_line(); + return; +} /* obj_coff_endef() */ +#if 0 +This code expects all the dims to be after one another, and that is not true +for gcc960 +sac@cygnus.com +static void obj_coff_dim() { + register int dim_index; + + if (def_symbol_in_progress == NULL) { + as_warn(".dim pseudo-op used outside of .def/.endef: ignored."); + demand_empty_rest_of_line(); + return; + } /* if not inside .def/.endef */ + + S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1); + + for (dim_index = 0; dim_index < DIMNUM; dim_index++) { + SKIP_WHITESPACES(); + SA_SET_SYM_DIMEN(def_symbol_in_progress, dim_index, get_absolute_expression()); + + switch (*input_line_pointer) { + + case ',': + input_line_pointer++; + break; + + default: + as_warn("badly formed .dim directive ignored"); + /* intentional fallthrough */ + case ';': + dim_index = DIMNUM; + break; + } /* switch on following character */ + } /* for each dimension */ + + demand_empty_rest_of_line(); + return; +} /* obj_coff_dim() */ +#else + +static void +obj_coff_dim() +{ + if (def_symbol_in_progress == NULL) { + as_warn(".dim pseudo-op used outside of .def/.endef: ignored."); + demand_empty_rest_of_line(); + return; + } /* if not inside .def/.endef */ + + + S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1); + + /* Grab as many dims as we can fit, until ; or full */ + while (dim_index < DIMNUM) + { + SKIP_WHITESPACES(); + SA_SET_SYM_DIMEN(def_symbol_in_progress, dim_index, get_absolute_expression()); + dim_index++; + if (*input_line_pointer == ';') break; + if (*input_line_pointer != ',') { + as_warn("badly formed .dim directive ignored"); + break; + } + input_line_pointer++; + } + demand_empty_rest_of_line(); + return; +} /* obj_coff_dim() */ +#endif + +static void obj_coff_line() { + if (def_symbol_in_progress == NULL) { + obj_coff_ln(); + return; + } /* if it looks like a stabs style line */ + + S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1); + SA_SET_SYM_LNNO(def_symbol_in_progress, get_absolute_expression()); + + demand_empty_rest_of_line(); + return; +} /* obj_coff_line() */ + +static void obj_coff_size() { + if (def_symbol_in_progress == NULL) { + as_warn(".size pseudo-op used outside of .def/.endef ignored."); + demand_empty_rest_of_line(); + return; + } /* if not inside .def/.endef */ + + S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1); + SA_SET_SYM_SIZE(def_symbol_in_progress, get_absolute_expression()); + demand_empty_rest_of_line(); + return; +} /* obj_coff_size() */ + +static void obj_coff_scl() { + if (def_symbol_in_progress == NULL) { + as_warn(".scl pseudo-op used outside of .def/.endef ignored."); + demand_empty_rest_of_line(); + return; + } /* if not inside .def/.endef */ + + S_SET_STORAGE_CLASS(def_symbol_in_progress, get_absolute_expression()); + demand_empty_rest_of_line(); + return; +} /* obj_coff_scl() */ + +static void obj_coff_tag() { + char *symbol_name; + char name_end; + + if (def_symbol_in_progress == NULL) { + as_warn(".tag pseudo-op used outside of .def/.endef ignored."); + demand_empty_rest_of_line(); + return; + } /* if not inside .def/.endef */ + + S_SET_NUMBER_AUXILIARY(def_symbol_in_progress, 1); + symbol_name = input_line_pointer; + name_end = get_symbol_end(); + + /* Assume that the symbol referred to by .tag is always defined. */ + /* This was a bad assumption. I've added find_or_make. xoxorich. */ + SA_SET_SYM_TAGNDX(def_symbol_in_progress, (long) tag_find_or_make(symbol_name)); + if (SA_GET_SYM_TAGNDX(def_symbol_in_progress) == 0L) { + as_warn("tag not found for .tag %s", symbol_name); + } /* not defined */ + + SF_SET_TAGGED(def_symbol_in_progress); + *input_line_pointer = name_end; + + demand_empty_rest_of_line(); + return; +} /* obj_coff_tag() */ + +static void obj_coff_type() { + if (def_symbol_in_progress == NULL) { + as_warn(".type pseudo-op used outside of .def/.endef ignored."); + demand_empty_rest_of_line(); + return; + } /* if not inside .def/.endef */ + + S_SET_DATA_TYPE(def_symbol_in_progress, get_absolute_expression()); + + if (ISFCN(S_GET_DATA_TYPE(def_symbol_in_progress)) && + S_GET_STORAGE_CLASS(def_symbol_in_progress) != C_TPDEF) { + SF_SET_FUNCTION(def_symbol_in_progress); + } /* is a function */ + + demand_empty_rest_of_line(); + return; +} /* obj_coff_type() */ + +static void obj_coff_val() { + if (def_symbol_in_progress == NULL) { + as_warn(".val pseudo-op used outside of .def/.endef ignored."); + demand_empty_rest_of_line(); + return; + } /* if not inside .def/.endef */ + + if (is_name_beginner(*input_line_pointer)) { + char *symbol_name = input_line_pointer; + char name_end = get_symbol_end(); + + if (!strcmp(symbol_name, ".")) { + def_symbol_in_progress->sy_frag = frag_now; + S_SET_VALUE(def_symbol_in_progress, obstack_next_free(&frags) - frag_now->fr_literal); + /* If the .val is != from the .def (e.g. statics) */ + } else if (strcmp(S_GET_NAME(def_symbol_in_progress), symbol_name)) { + def_symbol_in_progress->sy_forward = symbol_find_or_make(symbol_name); + + /* If the segment is undefined when the forward + reference is solved, then copy the segment id + from the forward symbol. */ + SF_SET_GET_SEGMENT(def_symbol_in_progress); + } + /* Otherwise, it is the name of a non debug symbol and its value will be calculated later. */ + *input_line_pointer = name_end; + } else { + S_SET_VALUE(def_symbol_in_progress, get_absolute_expression()); + } /* if symbol based */ + + demand_empty_rest_of_line(); + return; +} /* obj_coff_val() */ + +/* + * Maintain a list of the tagnames of the structres. + */ + +static void tag_init() { + tag_hash = hash_new(); + return ; +} /* tag_init() */ + +static void tag_insert(name, symbolP) +char *name; +symbolS *symbolP; +{ + register char * error_string; + + if (*(error_string = hash_jam(tag_hash, name, (char *)symbolP))) { + as_fatal("Inserting \"%s\" into structure table failed: %s", + name, error_string); + } + return ; +} /* tag_insert() */ + +static symbolS *tag_find_or_make(name) +char *name; +{ + symbolS *symbolP; + + if ((symbolP = tag_find(name)) == NULL) { + symbolP = symbol_new(name, + SEG_UNKNOWN, + 0, + &zero_address_frag); + + tag_insert(S_GET_NAME(symbolP), symbolP); + symbol_table_insert(symbolP); + } /* not found */ + + return(symbolP); +} /* tag_find_or_make() */ + +static symbolS *tag_find(name) +char *name; +{ +#if STRIP_UNDERSCORE + if (*name == '_') name++; +#endif /* STRIP_UNDERSCORE */ + return((symbolS*)hash_find(tag_hash, name)); +} /* tag_find() */ + +void obj_read_begin_hook() { + /* These had better be the same. Usually 18 bytes. */ + know(sizeof(SYMENT) == sizeof(AUXENT)); + know(SYMESZ == AUXESZ); + + tag_init(); + + return; +} /* obj_read_begin_hook() */ + +void obj_crawl_symbol_chain(headers) +object_headers *headers; +{ + int symbol_number = 0; + lineno *lineP; + symbolS *last_functionP = NULL; + symbolS *last_tagP; + symbolS *symbolP; + symbolS *symbol_externP = NULL; + symbolS *symbol_extern_lastP = NULL; + + /* Initialize the stack used to keep track of the matching .bb .be */ + stack* block_stack = stack_init(512, sizeof(symbolS*)); + + /* JF deal with forward references first... */ + for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + if (symbolP->sy_forward) { + S_SET_VALUE(symbolP, (S_GET_VALUE(symbolP) + + S_GET_VALUE(symbolP->sy_forward) + + symbolP->sy_forward->sy_frag->fr_address)); + + if (SF_GET_GET_SEGMENT(symbolP)) { + S_SET_SEGMENT(symbolP, S_GET_SEGMENT(symbolP->sy_forward)); + } /* forward segment also */ + + symbolP->sy_forward=0; + } /* if it has a forward reference */ + } /* walk the symbol chain */ + + tc_crawl_symbol_chain(headers); + + /* The symbol list should be ordered according to the following sequence + * order : + * . .file symbol + * . debug entries for functions + * . fake symbols for .text .data and .bss + * . defined symbols + * . undefined symbols + * But this is not mandatory. The only important point is to put the + * undefined symbols at the end of the list. + */ + + if (symbol_rootP == NULL + || S_GET_STORAGE_CLASS(symbol_rootP) != C_FILE) { + know(!previous_file_symbol); + c_dot_file_symbol("fake"); + } /* Is there a .file symbol ? If not insert one at the beginning. */ + + /* + * Build up static symbols for .text, .data and .bss + */ + dot_text_symbol = (symbolS*) + c_section_symbol(".text", + 0, + H_GET_TEXT_SIZE(headers), + 0/*text_relocation_number */, + 0/*text_lineno_number */); + + dot_data_symbol = (symbolS*) + c_section_symbol(".data", + H_GET_TEXT_SIZE(headers), + H_GET_DATA_SIZE(headers), + 0/*data_relocation_number */, + 0); /* There are no data lineno entries */ + + dot_bss_symbol = (symbolS*) + c_section_symbol(".bss", + H_GET_TEXT_SIZE(headers) + H_GET_DATA_SIZE(headers), + H_GET_BSS_SIZE(headers), + 0, /* No relocation for a bss section. */ + 0); /* There are no bss lineno entries */ + +#if defined(DEBUG) + verify_symbol_chain(symbol_rootP, symbol_lastP); +#endif /* DEBUG */ + + /* Three traversals of symbol chains here. The + first traversal yanks externals into a temporary + chain, removing the externals from the global + chain, numbers symbols, and does some other guck. + The second traversal is on the temporary chain of + externals and just appends them to the global + chain again, numbering them as we go. The third + traversal patches pointers to symbols (using sym + indexes). The last traversal was once done as + part of the first pass, but that fails when a + reference preceeds a definition as the definition + has no number at the time we process the + reference. */ + + /* Note that symbolP will be NULL at the end of a loop + if an external was at the beginning of the list (it + gets moved off the list). Hence the weird check in + the loop control. + */ + for (symbolP = symbol_rootP; symbolP; symbolP = symbolP ? symbol_next(symbolP) : symbol_rootP) { + + if (!SF_GET_DEBUG(symbolP)) { + /* Debug symbols do not need all this rubbish */ + symbolS* real_symbolP; + + /* L* and C_EFCN symbols never merge. */ + if (!SF_GET_LOCAL(symbolP) + && (real_symbolP = symbol_find_base(S_GET_NAME(symbolP), DO_NOT_STRIP)) + && real_symbolP != symbolP) { + /* FIXME-SOON: where do dups come from? Maybe tag references before definitions? xoxorich. */ + /* Move the debug data from the debug symbol to the + real symbol. Do NOT do the oposite (i.e. move from + real symbol to debug symbol and remove real symbol from the + list.) Because some pointers refer to the real symbol + whereas no pointers refer to the debug symbol. */ + c_symbol_merge(symbolP, real_symbolP); + /* Replace the current symbol by the real one */ + /* The symbols will never be the last or the first + because : 1st symbol is .file and 3 last symbols are + .text, .data, .bss */ + symbol_remove(real_symbolP, &symbol_rootP, &symbol_lastP); + symbol_insert(real_symbolP, symbolP, &symbol_rootP, &symbol_lastP); + symbol_remove(symbolP, &symbol_rootP, &symbol_lastP); + symbolP = real_symbolP; + } /* if not local but dup'd */ + + if (flagseen['R'] && (S_GET_SEGMENT(symbolP) == SEG_DATA)) { + S_SET_SEGMENT(symbolP, SEG_TEXT); + } /* push data into text */ + + S_SET_VALUE(symbolP, S_GET_VALUE(symbolP) + symbolP->sy_frag->fr_address); + + if (!S_IS_DEFINED(symbolP) && !SF_GET_LOCAL(symbolP)) { + S_SET_EXTERNAL(symbolP); + } else if (S_GET_STORAGE_CLASS(symbolP) == C_NULL) { + if (S_GET_SEGMENT(symbolP) == SEG_TEXT){ + S_SET_STORAGE_CLASS(symbolP, C_LABEL); + } else { + S_SET_STORAGE_CLASS(symbolP, C_STAT); + } + } /* no storage class yet */ + + /* Mainly to speed up if not -g */ + if (SF_GET_PROCESS(symbolP)) { + /* Handle the nested blocks auxiliary info. */ + if (S_GET_STORAGE_CLASS(symbolP) == C_BLOCK) { + if (!strcmp(S_GET_NAME(symbolP), ".bb")) + stack_push(block_stack, (char *) &symbolP); + else { /* .eb */ + register symbolS* begin_symbolP; + begin_symbolP = *(symbolS**)stack_pop(block_stack); + if (begin_symbolP == (symbolS*)0) + as_warn("mismatched .eb"); + else + SA_SET_SYM_ENDNDX(begin_symbolP, symbol_number+2); + } + } + /* If we are able to identify the type of a function, and we + are out of a function (last_functionP == 0) then, the + function symbol will be associated with an auxiliary + entry. */ + if (last_functionP == (symbolS*)0 && + SF_GET_FUNCTION(symbolP)) { + last_functionP = symbolP; + + if (S_GET_NUMBER_AUXILIARY(symbolP) < 1) { + S_SET_NUMBER_AUXILIARY(symbolP, 1); + } /* make it at least 1 */ + + /* Clobber possible stale .dim information. */ + bzero(symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen, + sizeof(symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen)); + } + /* The C_FCN doesn't need any additional information. + I don't even know if this is needed for sdb. But the + standard assembler generates it, so... + */ + if (S_GET_STORAGE_CLASS(symbolP) == C_EFCN) { + if (last_functionP == (symbolS*)0) + as_fatal("C_EFCN symbol out of scope"); + SA_SET_SYM_FSIZE(last_functionP, + (long)(S_GET_VALUE(symbolP) - + S_GET_VALUE(last_functionP))); + SA_SET_SYM_ENDNDX(last_functionP, symbol_number); + last_functionP = (symbolS*)0; + } + } + } else if (SF_GET_TAG(symbolP)) { + /* First descriptor of a structure must point to + the first slot after the structure description. */ + last_tagP = symbolP; + + } else if (S_GET_STORAGE_CLASS(symbolP) == C_EOS) { + /* +2 take in account the current symbol */ + SA_SET_SYM_ENDNDX(last_tagP, symbol_number + 2); + } else if (S_GET_STORAGE_CLASS(symbolP) == C_FILE) { + if (S_GET_VALUE(symbolP)) { + S_SET_VALUE((symbolS *) S_GET_VALUE(symbolP), symbol_number); + S_SET_VALUE(symbolP, 0); + } /* no one points at the first .file symbol */ + } /* if debug or tag or eos or file */ + + /* We must put the external symbols apart. The loader + does not bomb if we do not. But the references in + the endndx field for a .bb symbol are not corrected + if an external symbol is removed between .bb and .be. + I.e in the following case : + [20] .bb endndx = 22 + [21] foo external + [22] .be + ld will move the symbol 21 to the end of the list but + endndx will still be 22 instead of 21. */ + + if (SF_GET_LOCAL(symbolP)) { + /* remove C_EFCN and LOCAL (L...) symbols */ + /* next pointer remains valid */ + symbol_remove(symbolP, &symbol_rootP, &symbol_lastP); + + } else if (!S_IS_DEFINED(symbolP) && !S_IS_DEBUG(symbolP) && !SF_GET_STATICS(symbolP)) { +/* S_GET_STORAGE_CLASS(symbolP) == C_EXT && !SF_GET_FUNCTION(symbolP)) { */ + /* if external, Remove from the list */ + symbolS *hold = symbol_previous(symbolP); + + symbol_remove(symbolP, &symbol_rootP, &symbol_lastP); + symbol_clear_list_pointers(symbolP); + symbol_append(symbolP, symbol_extern_lastP, &symbol_externP, &symbol_extern_lastP); + symbolP = hold; + } else { + if (SF_GET_STRING(symbolP)) { + symbolP->sy_name_offset = string_byte_count; + string_byte_count += strlen(S_GET_NAME(symbolP)) + 1; + } else { + symbolP->sy_name_offset = 0; + } /* fix "long" names */ + + symbolP->sy_number = symbol_number; + symbol_number += 1 + S_GET_NUMBER_AUXILIARY(symbolP); + } /* if local symbol */ + } /* traverse the symbol list */ + + for (symbolP = symbol_externP; symbol_externP;) { + symbolS *tmp = symbol_externP; + + /* append */ + symbol_remove(tmp, &symbol_externP, &symbol_extern_lastP); + symbol_append(tmp, symbol_lastP, &symbol_rootP, &symbol_lastP); + + /* and process */ + if (SF_GET_STRING(tmp)) { + tmp->sy_name_offset = string_byte_count; + string_byte_count += strlen(S_GET_NAME(tmp)) + 1; + } else { + tmp->sy_name_offset = 0; + } /* fix "long" names */ + + tmp->sy_number = symbol_number; + symbol_number += 1 + S_GET_NUMBER_AUXILIARY(tmp); + } /* append the entire extern chain */ + + /* When a tag reference preceeds the tag definition, + the definition will not have a number at the time + we process the reference during the first + traversal. Thus, a second traversal. */ + + for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + if (SF_GET_TAGGED(symbolP)) { + SA_SET_SYM_TAGNDX(symbolP, ((symbolS*) SA_GET_SYM_TAGNDX(symbolP))->sy_number); + } /* If the symbol has a tagndx entry, resolve it */ + } /* second traversal */ + + know(symbol_externP == NULL); + know(symbol_extern_lastP == NULL); + + /* FIXME-SOMEDAY I'm counting line no's here so we know what to put in the section + headers, and I'm resolving the addresses since I'm not sure how to + do it later. I am NOT resolving the linno's representing functions. + Their symbols need a fileptr pointing to this linno when emitted. + Thus, I resolve them on emit. xoxorich. */ + + for (lineP = lineno_rootP; lineP; lineP = lineP->next) { + if (lineP->line.l_lnno > 0) { + lineP->line.l_addr.l_paddr += ((fragS*)lineP->frag)->fr_address; + } else { + ; + } + text_lineno_number++; + } /* for each line number */ + + H_SET_SYMBOL_TABLE_SIZE(headers, symbol_number); + + return; +} /* obj_crawl_symbol_chain() */ + +/* + * Find strings by crawling along symbol table chain. + */ + +void obj_emit_strings(where) +char **where; +{ + symbolS *symbolP; + +#ifdef CROSS_ASSEMBLE + /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */ + md_number_to_chars(*where, string_byte_count, sizeof(string_byte_count)); + where += sizeof(string_byte_count); +#else /* CROSS_ASSEMBLE */ + append(where, (char *) &string_byte_count, (unsigned long) sizeof(string_byte_count)); +#endif /* CROSS_ASSEMBLE */ + + for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + if (SF_GET_STRING(symbolP)) { + append(where, S_GET_NAME(symbolP), (unsigned long)(strlen(S_GET_NAME(symbolP)) + 1)); + } /* if it has a string */ + } /* walk the symbol chain */ + + return; +} /* obj_emit_strings() */ + +void obj_pre_write_hook(headers) +object_headers *headers; +{ + register int text_relocation_number = 0; + register int data_relocation_number = 0; + register fixS *fixP; + + /* FIXME-SOMEDAY this should be done at + fixup_segment time but I'm going to wait until I + do multiple segments. xoxorich. */ + /* Count the number of relocation entries for text and data */ + for (fixP = text_fix_root; fixP; fixP = fixP->fx_next) { + if (fixP->fx_addsy) { + ++text_relocation_number; +#ifdef TC_I960 + /* two relocs per callj under coff. */ + if (fixP->fx_callj) { + ++text_relocation_number; + } /* if callj and not already fixed. */ +#endif /* TC_I960 */ + + } /* if not yet fixed */ + } /* for each fix */ + + SA_SET_SCN_NRELOC(dot_text_symbol, text_relocation_number); + /* Assign the number of line number entries for the text section */ + SA_SET_SCN_NLINNO(dot_text_symbol, text_lineno_number); + /* Assign the size of the section */ + SA_SET_SCN_SCNLEN(dot_text_symbol, H_GET_TEXT_SIZE(headers)); + + for (fixP = data_fix_root; fixP; fixP = fixP->fx_next) { + if (fixP->fx_addsy) { + ++data_relocation_number; + } /* if still relocatable */ + } /* for each fix */ + + SA_SET_SCN_NRELOC(dot_data_symbol, data_relocation_number); + /* Assign the size of the section */ + SA_SET_SCN_SCNLEN(dot_data_symbol, H_GET_DATA_SIZE(headers)); + + /* Assign the size of the section */ + SA_SET_SCN_SCNLEN(dot_bss_symbol, H_GET_BSS_SIZE(headers)); + + /* Fill in extra coff fields */ + + /* Initialize general line number information. */ + H_SET_LINENO_SIZE(headers, text_lineno_number * LINESZ); + + /* filehdr */ + H_SET_FILE_MAGIC_NUMBER(headers, FILE_HEADER_MAGIC); + H_SET_NUMBER_OF_SECTIONS(headers, 3); /* text+data+bss */ + H_SET_TIME_STAMP(headers, (long)time((long*)0)); + H_SET_SYMBOL_TABLE_POINTER(headers, H_GET_SYMBOL_TABLE_FILE_OFFSET(headers)); + /* symbol table size allready set */ + H_SET_SIZEOF_OPTIONAL_HEADER(headers, OBJ_COFF_AOUTHDRSZ); +#ifndef OBJ_COFF_IGNORE_EXEC_FLAG + H_SET_FLAGS(headers, (text_lineno_number == 0 ? F_LNNO : 0) + | ((text_relocation_number + data_relocation_number) ? 0 : F_EXEC) + | BYTE_ORDERING); +#else /* OBJ_COFF_IGNORE_EXEC_FLAG */ + H_SET_FLAGS(headers, (text_lineno_number == 0 ? F_LNNO : 0) + | BYTE_ORDERING); +#endif /* OBJ_COFF_IGNORE_EXEC_FLAG */ + + /* aouthdr */ + /* magic number allready set */ + H_SET_VERSION_STAMP(headers, 0); + /* Text, data, bss size; entry point; text_start and data_start are already set */ + + /* Build section headers */ + + c_section_header(&text_section_header, + ".text", + 0, + H_GET_TEXT_SIZE(headers), + H_GET_TEXT_FILE_OFFSET(headers), + (SA_GET_SCN_NRELOC(dot_text_symbol) + ? H_GET_RELOCATION_FILE_OFFSET(headers) + : 0), + (text_lineno_number + ? H_GET_LINENO_FILE_OFFSET(headers) + : 0), + SA_GET_SCN_NRELOC(dot_text_symbol), + text_lineno_number, + section_alignment[(int) SEG_TEXT]); + + c_section_header(&data_section_header, + ".data", + H_GET_TEXT_SIZE(headers), + H_GET_DATA_SIZE(headers), + (H_GET_DATA_SIZE(headers) + ? H_GET_DATA_FILE_OFFSET(headers) + : 0), + (SA_GET_SCN_NRELOC(dot_data_symbol) + ? (H_GET_RELOCATION_FILE_OFFSET(headers) + + text_section_header.s_nreloc * RELSZ) + : 0), + 0, /* No line number information */ + SA_GET_SCN_NRELOC(dot_data_symbol), + 0, /* No line number information */ + section_alignment[(int) SEG_DATA]); + + c_section_header(&bss_section_header, + ".bss", + H_GET_TEXT_SIZE(headers) + H_GET_DATA_SIZE(headers), + H_GET_BSS_SIZE(headers), + 0, /* No file offset */ + 0, /* No relocation information */ + 0, /* No line number information */ + 0, /* No relocation information */ + 0, /* No line number information */ + section_alignment[(int) SEG_BSS]); + + return; +} /* obj_pre_write_hook() */ + +/* This is a copy from aout. All I do is neglect to actually build the symbol. */ + +static void obj_coff_stab(what) +int what; +{ + char *string; + expressionS e; + int goof = 0; /* TRUE if we have aborted. */ + int length; + int saved_type = 0; + long longint; + symbolS *symbolP = 0; + + if (what == 's') { + string = demand_copy_C_string(&length); + SKIP_WHITESPACE(); + + if (*input_line_pointer == ',') { + input_line_pointer++; + } else { + as_bad("I need a comma after symbol's name"); + goof = 1; + } /* better be a comma */ + } /* skip the string */ + + /* + * Input_line_pointer->after ','. String->symbol name. + */ + if (!goof) { + if (get_absolute_expression_and_terminator(&longint) != ',') { + as_bad("I want a comma after the n_type expression"); + goof = 1; + input_line_pointer--; /* Backup over a non-',' char. */ + } /* on error */ + } /* no error */ + + if (!goof) { + if (get_absolute_expression_and_terminator(&longint) != ',') { + as_bad("I want a comma after the n_other expression"); + goof = 1; + input_line_pointer--; /* Backup over a non-',' char. */ + } /* on error */ + } /* no error */ + + if (!goof) { + get_absolute_expression(); + + if (what == 's' || what == 'n') { + if (*input_line_pointer != ',') { + as_bad("I want a comma after the n_desc expression"); + goof = 1; + } else { + input_line_pointer++; + } /* on goof */ + } /* not stabd */ + } /* no error */ + + expression(&e); + + if (goof) { + ignore_rest_of_line(); + } else { + demand_empty_rest_of_line(); + } /* on error */ +} /* obj_coff_stab() */ + +#ifdef DEBUG + /* for debugging */ +char *s_get_name(s) +symbolS *s; +{ + return((s == NULL) ? "(NULL)" : S_GET_NAME(s)); +} /* s_get_name() */ + +void symbol_dump() { + symbolS *symbolP; + + for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { + printf("%3ld: 0x%lx \"%s\" type = %ld, class = %d, segment = %d\n", + symbolP->sy_number, + (unsigned long) symbolP, + S_GET_NAME(symbolP), + (long) S_GET_DATA_TYPE(symbolP), + S_GET_STORAGE_CLASS(symbolP), + (int) S_GET_SEGMENT(symbolP)); + } /* traverse symbols */ + + return; +} /* symbol_dump() */ +#endif /* DEBUG */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of obj-coff.c */ diff --git a/gas/config/obj-coff.h b/gas/config/obj-coff.h new file mode 100644 index 0000000..6ed5c84 --- /dev/null +++ b/gas/config/obj-coff.h @@ -0,0 +1,494 @@ +/* coff object file format + Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc. + +This file is part of GAS. + +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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +#define OBJ_COFF 1 + +#include "targ-cpu.h" + +#include "coff.gnu.h" + +#ifdef USE_NATIVE_HEADERS +#include <filehdr.h> +#include <aouthdr.h> +#include <scnhdr.h> +#include <storclass.h> +#include <linenum.h> +#include <syms.h> +#include <reloc.h> +#include <sys/types.h> +#endif /* USE_NATIVE_HEADERS */ + +/* Define some processor dependent values according to the processor we are + on. */ +#ifdef TC_M68K + +#define BYTE_ORDERING F_AR32W /* See filehdr.h for more info. */ +#ifndef FILE_HEADER_MAGIC +#define FILE_HEADER_MAGIC MC68MAGIC /* ... */ +#endif /* FILE_HEADER_MAGIC */ + +#elif defined(TC_I386) + +#define BYTE_ORDERING F_AR32WR /* See filehdr.h for more info. */ +#ifndef FILE_HEADER_MAGIC +#define FILE_HEADER_MAGIC I386MAGIC /* ... */ +#endif /* FILE_HEADER_MAGIC */ + +#elif defined(TC_I960) + +#define BYTE_ORDERING F_AR32WR /* See filehdr.h for more info. */ +#ifndef FILE_HEADER_MAGIC +#define FILE_HEADER_MAGIC I960ROMAGIC /* ... */ +#endif /* FILE_HEADER_MAGIC */ + +#elif defined(TC_A29K) + +#define BYTE_ORDERING F_AR32W /* big endian. */ +#ifndef FILE_HEADER_MAGIC +#define FILE_HEADER_MAGIC SIPFBOMAGIC +#endif /* FILE_HEADER_MAGIC */ + +#else +you lose +#endif + +#ifndef OBJ_COFF_MAX_AUXENTRIES +#define OBJ_COFF_MAX_AUXENTRIES 1 +#endif /* OBJ_COFF_MAX_AUXENTRIES */ + +extern const short seg_N_TYPE[]; +extern const segT N_TYPE_seg[]; + +/* Magic number of paged executable. */ +#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (OMAGIC) + +/* Add these definitions to have a consistent convention for all the + types used in COFF format. */ +#define AOUTHDR struct aouthdr +#define AOUTHDRSZ sizeof(AOUTHDR) + +/* SYMBOL TABLE */ + + /* targets may also set this */ +#ifndef SYMBOLS_NEED_BACKPOINTERS +#define SYMBOLS_NEED_BACKPOINTERS 1 +#endif /* SYMBOLS_NEED_BACKPOINTERS */ + +/* Symbol table entry data type */ + +typedef struct { + SYMENT ost_entry; /* Basic symbol */ + AUXENT ost_auxent[OBJ_COFF_MAX_AUXENTRIES]; /* Auxiliary entry. */ + unsigned int ost_flags; /* obj_coff internal use only flags */ +} obj_symbol_type; + +/* If compiler generate leading underscores, remove them. */ + +#ifndef STRIP_UNDERSCORE +#define STRIP_UNDERSCORE 0 +#endif /* STRIP_UNDERSCORE */ +#define DO_NOT_STRIP 0 +#define DO_STRIP 1 + +/* Symbol table macros and constants */ + +/* Possible and usefull section number in symbol table + * The values of TEXT, DATA and BSS may not be portable. + */ + +#define C_TEXT_SECTION ((short)1) +#define C_DATA_SECTION ((short)2) +#define C_BSS_SECTION ((short)3) +#define C_ABS_SECTION N_ABS +#define C_UNDEF_SECTION N_UNDEF +#define C_DEBUG_SECTION N_DEBUG +#define C_NTV_SECTION N_TV +#define C_PTV_SECTION P_TV + +/* + * Macros to extract information from a symbol table entry. + * This syntaxic indirection allows independence regarding a.out or coff. + * The argument (s) of all these macros is a pointer to a symbol table entry. + */ + +/* Predicates */ +/* True if the symbol is external */ +#define S_IS_EXTERNAL(s) ((s)->sy_symbol.ost_entry.n_scnum == C_UNDEF_SECTION) +/* True if symbol has been defined, ie : + section > 0 (DATA, TEXT or BSS) + section == 0 and value > 0 (external bss symbol) */ +#define S_IS_DEFINED(s) ((s)->sy_symbol.ost_entry.n_scnum > C_UNDEF_SECTION || \ + ((s)->sy_symbol.ost_entry.n_scnum == C_UNDEF_SECTION && \ + (s)->sy_symbol.ost_entry.n_value > 0)) +/* True if a debug special symbol entry */ +#define S_IS_DEBUG(s) ((s)->sy_symbol.ost_entry.n_scnum == C_DEBUG_SECTION) +/* True if a symbol is local symbol name */ +/* A symbol name whose name begin with ^A is a gas internal pseudo symbol */ +#define S_IS_LOCAL(s) (S_GET_NAME(s)[0] == '\001' || \ + (S_LOCAL_NAME(s) && !flagseen['L'])) +/* True if a symbol is not defined in this file */ +#define S_IS_EXTERN(s) ((s)->sy_symbol.ost_entry.n_scnum == 0 && (s)->sy_symbol.ost_entry.n_value == 0) +/* + * True if a symbol can be multiply defined (bss symbols have this def + * though it is bad practice) + */ +#define S_IS_COMMON(s) ((s)->sy_symbol.ost_entry.n_scnum == 0 && (s)->sy_symbol.ost_entry.n_value != 0) +/* True if a symbol name is in the string table, i.e. its length is > 8. */ +#define S_IS_STRING(s) (strlen(S_GET_NAME(s)) > 8 ? 1 : 0) + +/* Accessors */ +/* The name of the symbol */ +#define S_GET_NAME(s) ((char*)(s)->sy_symbol.ost_entry.n_offset) +/* The pointer to the string table */ +#define S_GET_OFFSET(s) ((s)->sy_symbol.ost_entry.n_offset) +/* The zeroes if symbol name is longer than 8 chars */ +#define S_GET_ZEROES(s) ((s)->sy_symbol.ost_entry.n_zeroes) +/* The value of the symbol */ +#define S_GET_VALUE(s) ((s)->sy_symbol.ost_entry.n_value) +/* The numeric value of the segment */ +#define S_GET_SEGMENT(s) (N_TYPE_seg[(s)->sy_symbol.ost_entry.n_scnum+4]) +/* The data type */ +#define S_GET_DATA_TYPE(s) ((s)->sy_symbol.ost_entry.n_type) +/* The storage class */ +#define S_GET_STORAGE_CLASS(s) ((s)->sy_symbol.ost_entry.n_sclass) +/* The number of auxiliary entries */ +#define S_GET_NUMBER_AUXILIARY(s) ((s)->sy_symbol.ost_entry.n_numaux) + +/* Modifiers */ +/* Set the name of the symbol */ +#define S_SET_NAME(s,v) ((s)->sy_symbol.ost_entry.n_offset = (unsigned long)(v)) +/* Set the offset of the symbol */ +#define S_SET_OFFSET(s,v) ((s)->sy_symbol.ost_entry.n_offset = (v)) +/* The zeroes if symbol name is longer than 8 chars */ +#define S_SET_ZEROES(s,v) ((s)->sy_symbol.ost_entry.n_zeroes = (v)) +/* Set the value of the symbol */ +#define S_SET_VALUE(s,v) ((s)->sy_symbol.ost_entry.n_value = (v)) +/* The numeric value of the segment */ +#define S_SET_SEGMENT(s,v) ((s)->sy_symbol.ost_entry.n_scnum = SEGMENT_TO_SYMBOL_TYPE(v)) +/* The data type */ +#define S_SET_DATA_TYPE(s,v) ((s)->sy_symbol.ost_entry.n_type = (v)) +/* The storage class */ +#define S_SET_STORAGE_CLASS(s,v) ((s)->sy_symbol.ost_entry.n_sclass = (v)) +/* The number of auxiliary entries */ +#define S_SET_NUMBER_AUXILIARY(s,v) ((s)->sy_symbol.ost_entry.n_numaux = (v)) + +/* Additional modifiers */ +/* The symbol is external (does not mean undefined) */ +#define S_SET_EXTERNAL(s) { S_SET_STORAGE_CLASS(s, C_EXT) ; SF_CLEAR_LOCAL(s); } + +/* Auxiliary entry macros. SA_ stands for symbol auxiliary */ +/* Omit the tv related fields */ +/* Accessors */ +#define SA_GET_SYM_TAGNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx) +#define SA_GET_SYM_LNNO(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_lnno) +#define SA_GET_SYM_SIZE(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_size) +#define SA_GET_SYM_FSIZE(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_fsize) +#define SA_GET_SYM_LNNOPTR(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_lnnoptr) +#define SA_GET_SYM_ENDNDX(s) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx) +#define SA_GET_SYM_DIMEN(s,i) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen[(i)]) +#define SA_GET_FILE_FNAME(s) ((s)->sy_symbol.ost_auxent[0].x_file.x_fname) +#define SA_GET_SCN_SCNLEN(s) ((s)->sy_symbol.ost_auxent[0].x_scn.x_scnlen) +#define SA_GET_SCN_NRELOC(s) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nreloc) +#define SA_GET_SCN_NLINNO(s) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nlinno) + +/* Modifiers */ +#define SA_SET_SYM_TAGNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_tagndx=(v)) +#define SA_SET_SYM_LNNO(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_lnno=(v)) +#define SA_SET_SYM_SIZE(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_lnsz.x_size=(v)) +#define SA_SET_SYM_FSIZE(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_misc.x_fsize=(v)) +#define SA_SET_SYM_LNNOPTR(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_lnnoptr=(v)) +#define SA_SET_SYM_ENDNDX(s,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_fcn.x_endndx=(v)) +#define SA_SET_SYM_DIMEN(s,i,v) ((s)->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen[(i)]=(v)) +#define SA_SET_FILE_FNAME(s,v) strncpy((s)->sy_symbol.ost_auxent[0].x_file.x_fname,(v),FILNMLEN) +#define SA_SET_SCN_SCNLEN(s,v) ((s)->sy_symbol.ost_auxent[0].x_scn.x_scnlen=(v)) +#define SA_SET_SCN_NRELOC(s,v) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nreloc=(v)) +#define SA_SET_SCN_NLINNO(s,v) ((s)->sy_symbol.ost_auxent[0].x_scn.x_nlinno=(v)) + +/* + * Internal use only definitions. SF_ stands for symbol flags. + * + * These values can be assigned to sy_symbol.ost_flags field of a symbolS. + * + * You'll break i960 if you shift the SYSPROC bits anywhere else. for + * more on the balname/callname hack, see tc-i960.h. b.out is done + * differently. + */ + +#define SF_I960_MASK (0x000001ff) /* Bits 0-8 are used by the i960 port. */ +#define SF_SYSPROC (0x0000003f) /* bits 0-5 are used to store the sysproc number */ +#define SF_IS_SYSPROC (0x00000040) /* bit 6 marks symbols that are sysprocs */ +#define SF_BALNAME (0x00000080) /* bit 7 marks BALNAME symbols */ +#define SF_CALLNAME (0x00000100) /* bit 8 marks CALLNAME symbols */ + +#define SF_NORMAL_MASK (0x0000ffff) /* bits 12-15 are general purpose. */ + +#define SF_STATICS (0x00001000) /* Mark the .text & all symbols */ +#define SF_DEFINED (0x00002000) /* Symbol is defined in this file */ +#define SF_STRING (0x00004000) /* Symbol name length > 8 */ +#define SF_LOCAL (0x00008000) /* Symbol must not be emitted */ + +#define SF_DEBUG_MASK (0xffff0000) /* bits 16-31 are debug info */ + +#define SF_FUNCTION (0x00010000) /* The symbol is a function */ +#define SF_PROCESS (0x00020000) /* Process symbol before write */ +#define SF_TAGGED (0x00040000) /* Is associated with a tag */ +#define SF_TAG (0x00080000) /* Is a tag */ +#define SF_DEBUG (0x00100000) /* Is in debug or abs section */ +#define SF_GET_SEGMENT (0x00200000) /* Get the section of the forward symbol. */ + /* All other bits are unused. */ + +/* Accessors */ +#define SF_GET(s) ((s)->sy_symbol.ost_flags) +#define SF_GET_NORMAL_FIELD(s) ((s)->sy_symbol.ost_flags & SF_NORMAL_MASK) +#define SF_GET_DEBUG_FIELD(s) ((s)->sy_symbol.ost_flags & SF_DEBUG_MASK) +#define SF_GET_FILE(s) ((s)->sy_symbol.ost_flags & SF_FILE) +#define SF_GET_STATICS(s) ((s)->sy_symbol.ost_flags & SF_STATICS) +#define SF_GET_DEFINED(s) ((s)->sy_symbol.ost_flags & SF_DEFINED) +#define SF_GET_STRING(s) ((s)->sy_symbol.ost_flags & SF_STRING) +#define SF_GET_LOCAL(s) ((s)->sy_symbol.ost_flags & SF_LOCAL) +#define SF_GET_FUNCTION(s) ((s)->sy_symbol.ost_flags & SF_FUNCTION) +#define SF_GET_PROCESS(s) ((s)->sy_symbol.ost_flags & SF_PROCESS) +#define SF_GET_DEBUG(s) ((s)->sy_symbol.ost_flags & SF_DEBUG) +#define SF_GET_TAGGED(s) ((s)->sy_symbol.ost_flags & SF_TAGGED) +#define SF_GET_TAG(s) ((s)->sy_symbol.ost_flags & SF_TAG) +#define SF_GET_GET_SEGMENT(s) ((s)->sy_symbol.ost_flags & SF_GET_SEGMENT) +#define SF_GET_I960(s) ((s)->sy_symbol.ost_flags & SF_I960_MASK) /* used by i960 */ +#define SF_GET_BALNAME(s) ((s)->sy_symbol.ost_flags & SF_BALNAME) /* used by i960 */ +#define SF_GET_CALLNAME(s) ((s)->sy_symbol.ost_flags & SF_CALLNAME) /* used by i960 */ +#define SF_GET_IS_SYSPROC(s) ((s)->sy_symbol.ost_flags & SF_IS_SYSPROC) /* used by i960 */ +#define SF_GET_SYSPROC(s) ((s)->sy_symbol.ost_flags & SF_SYSPROC) /* used by i960 */ + +/* Modifiers */ +#define SF_SET(s,v) ((s)->sy_symbol.ost_flags = (v)) +#define SF_SET_NORMAL_FIELD(s,v)((s)->sy_symbol.ost_flags |= ((v) & SF_NORMAL_MASK)) +#define SF_SET_DEBUG_FIELD(s,v) ((s)->sy_symbol.ost_flags |= ((v) & SF_DEBUG_MASK)) +#define SF_SET_FILE(s) ((s)->sy_symbol.ost_flags |= SF_FILE) +#define SF_SET_STATICS(s) ((s)->sy_symbol.ost_flags |= SF_STATICS) +#define SF_SET_DEFINED(s) ((s)->sy_symbol.ost_flags |= SF_DEFINED) +#define SF_SET_STRING(s) ((s)->sy_symbol.ost_flags |= SF_STRING) +#define SF_SET_LOCAL(s) ((s)->sy_symbol.ost_flags |= SF_LOCAL) +#define SF_CLEAR_LOCAL(s) ((s)->sy_symbol.ost_flags &= ~SF_LOCAL) +#define SF_SET_FUNCTION(s) ((s)->sy_symbol.ost_flags |= SF_FUNCTION) +#define SF_SET_PROCESS(s) ((s)->sy_symbol.ost_flags |= SF_PROCESS) +#define SF_SET_DEBUG(s) ((s)->sy_symbol.ost_flags |= SF_DEBUG) +#define SF_SET_TAGGED(s) ((s)->sy_symbol.ost_flags |= SF_TAGGED) +#define SF_SET_TAG(s) ((s)->sy_symbol.ost_flags |= SF_TAG) +#define SF_SET_GET_SEGMENT(s) ((s)->sy_symbol.ost_flags |= SF_GET_SEGMENT) +#define SF_SET_I960(s,v) ((s)->sy_symbol.ost_flags |= ((v) & SF_I960_MASK)) /* used by i960 */ +#define SF_SET_BALNAME(s) ((s)->sy_symbol.ost_flags |= SF_BALNAME) /* used by i960 */ +#define SF_SET_CALLNAME(s) ((s)->sy_symbol.ost_flags |= SF_CALLNAME) /* used by i960 */ +#define SF_SET_IS_SYSPROC(s) ((s)->sy_symbol.ost_flags |= SF_IS_SYSPROC) /* used by i960 */ +#define SF_SET_SYSPROC(s,v) ((s)->sy_symbol.ost_flags |= ((v) & SF_SYSPROC)) /* used by i960 */ + +/* File header macro and type definition */ + +/* + * File position calculators. Beware to use them when all the + * appropriate fields are set in the header. + */ + +#ifdef OBJ_COFF_OMIT_OPTIONAL_HEADER +#define OBJ_COFF_AOUTHDRSZ (0) +#else +#define OBJ_COFF_AOUTHDRSZ (AOUTHDRSZ) +#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */ + +#define H_GET_FILE_SIZE(h) \ + (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \ + H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \ + H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \ + H_GET_RELOCATION_SIZE(h) + H_GET_LINENO_SIZE(h) + \ + H_GET_SYMBOL_TABLE_SIZE(h) * SYMESZ + \ + (h)->string_table_size) +#define H_GET_TEXT_FILE_OFFSET(h) \ + (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \ + H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ) +#define H_GET_DATA_FILE_OFFSET(h) \ + (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \ + H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \ + H_GET_TEXT_SIZE(h)) +#define H_GET_BSS_FILE_OFFSET(h) 0 +#define H_GET_RELOCATION_FILE_OFFSET(h) \ + (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \ + H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \ + H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h)) +#define H_GET_LINENO_FILE_OFFSET(h) \ + (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \ + H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \ + H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \ + H_GET_RELOCATION_SIZE(h)) +#define H_GET_SYMBOL_TABLE_FILE_OFFSET(h) \ + (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \ + H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \ + H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \ + H_GET_RELOCATION_SIZE(h) + H_GET_LINENO_SIZE(h)) + +/* Accessors */ +/* aouthdr */ +#define H_GET_MAGIC_NUMBER(h) ((h)->aouthdr.magic) +#define H_GET_VERSION_STAMP(h) ((h)->aouthdr.vstamp) +#define H_GET_TEXT_SIZE(h) ((h)->aouthdr.tsize) +#define H_GET_DATA_SIZE(h) ((h)->aouthdr.dsize) +#define H_GET_BSS_SIZE(h) ((h)->aouthdr.bsize) +#define H_GET_ENTRY_POINT(h) ((h)->aouthdr.entry) +#define H_GET_TEXT_START(h) ((h)->aouthdr.text_start) +#define H_GET_DATA_START(h) ((h)->aouthdr.data_start) +/* filehdr */ +#define H_GET_FILE_MAGIC_NUMBER(h) ((h)->filehdr.f_magic) +#define H_GET_NUMBER_OF_SECTIONS(h) ((h)->filehdr.f_nscns) +#define H_GET_TIME_STAMP(h) ((h)->filehdr.f_timdat) +#define H_GET_SYMBOL_TABLE_POINTER(h) ((h)->filehdr.f_symptr) +#define H_GET_SYMBOL_TABLE_SIZE(h) ((h)->filehdr.f_nsyms) +#define H_GET_SIZEOF_OPTIONAL_HEADER(h) ((h)->filehdr.f_opthdr) +#define H_GET_FLAGS(h) ((h)->filehdr.f_flags) +/* Extra fields to achieve bsd a.out compatibility and for convenience */ +#define H_GET_RELOCATION_SIZE(h) ((h)->relocation_size) +#define H_GET_STRING_SIZE(h) ((h)->string_table_size) +#define H_GET_LINENO_SIZE(h) ((h)->lineno_size) + +/* Modifiers */ +/* aouthdr */ +#define H_SET_MAGIC_NUMBER(h,v) ((h)->aouthdr.magic = (v)) +#define H_SET_VERSION_STAMP(h,v) ((h)->aouthdr.vstamp = (v)) +#define H_SET_TEXT_SIZE(h,v) ((h)->aouthdr.tsize = (v)) +#define H_SET_DATA_SIZE(h,v) ((h)->aouthdr.dsize = (v)) +#define H_SET_BSS_SIZE(h,v) ((h)->aouthdr.bsize = (v)) +#define H_SET_ENTRY_POINT(h,v) ((h)->aouthdr.entry = (v)) +#define H_SET_TEXT_START(h,v) ((h)->aouthdr.text_start = (v)) +#define H_SET_DATA_START(h,v) ((h)->aouthdr.data_start = (v)) +/* filehdr */ +#define H_SET_FILE_MAGIC_NUMBER(h,v) ((h)->filehdr.f_magic = (v)) +#define H_SET_NUMBER_OF_SECTIONS(h,v) ((h)->filehdr.f_nscns = (v)) +#define H_SET_TIME_STAMP(h,v) ((h)->filehdr.f_timdat = (v)) +#define H_SET_SYMBOL_TABLE_POINTER(h,v) ((h)->filehdr.f_symptr = (v)) +#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->filehdr.f_nsyms = (v)) +#define H_SET_SIZEOF_OPTIONAL_HEADER(h,v) ((h)->filehdr.f_opthdr = (v)) +#define H_SET_FLAGS(h,v) ((h)->filehdr.f_flags = (v)) +/* Extra fields to achieve bsd a.out compatibility and for convinience */ +#define H_SET_RELOCATION_SIZE(h,t,d) ((h)->relocation_size = (t)+(d)) +#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v)) +#define H_SET_LINENO_SIZE(h,v) ((h)->lineno_size = (v)) + + /* Segment flipping */ +#define segment_name(v) (seg_name[(int) (v)]) + +typedef struct { + AOUTHDR aouthdr; /* a.out header */ + FILHDR filehdr; /* File header, not machine dep. */ + long string_table_size; /* names + '\0' + sizeof(int) */ + long relocation_size; /* Cumulated size of relocation + information for all sections in + bytes. */ + long lineno_size; /* Size of the line number information + table in bytes */ +} object_headers; + +/* -------------- Line number handling ------- */ +extern int text_lineno_number; + +/* line numbering stuff. */ + +typedef struct internal_lineno { + LINENO line; /* The lineno structure itself */ + char* frag; /* Frag to which the line number is related */ + struct internal_lineno* next; /* Forward chain pointer */ +} lineno; + +extern lineno *lineno_lastP; +extern lineno *lineno_rootP; +#define OBJ_EMIT_LINENO(a, b, c) obj_emit_lineno((a),(b),(c)) + +#ifdef __STDC__ +void obj_emit_lineno(char **where, lineno *line, char *file_start); +#else /* __STDC__ */ +void obj_emit_lineno(); +#endif /* __STDC__ */ + + /* stack stuff */ +typedef struct { + unsigned long chunk_size; + unsigned long element_size; + unsigned long size; + char* data; + unsigned long pointer; +} stack; + +#ifdef __STDC__ + +char *stack_pop(stack *st); +char *stack_push(stack *st, char *element); +char *stack_top(stack *st); +stack *stack_init(unsigned long chunk_size, unsigned long element_size); +void c_dot_file_symbol(char *filename); +void obj_extra_stuff(object_headers *headers); +void stack_delete(stack *st); + +#ifndef tc_headers_hook +void tc_headers_hook(object_headers *headers); +#endif /* tc_headers_hook */ + +#ifndef tc_coff_symbol_emit_hook +void tc_coff_symbol_emit_hook(); /* really tc_coff_symbol_emit_hook(symbolS *symbolP) */ +#endif /* tc_coff_symbol_emit_hook */ + +void c_section_header(SCNHDR *header, + char *name, + long core_address, + long size, + long data_ptr, + long reloc_ptr, + long lineno_ptr, + long reloc_number, + long lineno_number, + long alignment); + +#else /* __STDC__ */ + +char *stack_pop(); +char *stack_push(); +char *stack_top(); +stack *stack_init(); +void c_dot_file_symbol(); +void c_section_header(); +void obj_extra_stuff(); +void stack_delete(); +void tc_headers_hook(); +void tc_coff_symbol_emit_hook(); + +#endif /* __STDC__ */ + + + /* sanity check */ + +#ifdef TC_I960 +#ifndef C_LEAFSTAT +hey! Where is the C_LEAFSTAT definition? i960-coff support is depending on it. +#endif /* no C_LEAFSTAT */ +#endif /* TC_I960 */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of obj-coff.h */ diff --git a/gas/config/tc-a29k.c b/gas/config/tc-a29k.c new file mode 100644 index 0000000..8bb6d2a --- /dev/null +++ b/gas/config/tc-a29k.c @@ -0,0 +1,1157 @@ +/* tc-a29k.c -- Assemble for the AMD 29000. + Copyright (C) 1989, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +/* John Gilmore has reorganized this module somewhat, to make it easier + to convert it to new machines' assemblers as desired. There was too + much bloody rewriting required before. There still probably is. */ + +#include "as.h" + +#include "a29k-opcode.h" + +/* Make it easier to clone this machine desc into another one. */ +#define machine_opcode a29k_opcode +#define machine_opcodes a29k_opcodes +#define machine_ip a29k_ip +#define machine_it a29k_it + +const relax_typeS md_relax_table[] = { 0 }; + +#define IMMEDIATE_BIT 0x01000000 /* Turns RB into Immediate */ +#define ABSOLUTE_BIT 0x01000000 /* Turns PC-relative to Absolute */ +#define CE_BIT 0x00800000 /* Coprocessor enable in LOAD */ +#define UI_BIT 0x00000080 /* Unsigned integer in CONVERT */ + +/* handle of the OPCODE hash table */ +static struct hash_control *op_hash = NULL; + +struct machine_it { + char *error; + unsigned long opcode; + struct nlist *nlistp; + expressionS exp; + int pcrel; + int reloc_offset; /* Offset of reloc within insn */ + enum reloc_type reloc; +} the_insn; + +#ifdef __STDC__ + +/* static int getExpression(char *str); */ +static void machine_ip(char *str); +/* static void print_insn(struct machine_it *insn); */ +static void s_data1(void); +static void s_use(void); + +#else /* __STDC__ */ + +/* static int getExpression(); */ +static void machine_ip(); +/* static void print_insn(); */ +static void s_data1(); +static void s_use(); + +#endif /* __STDC__ */ + +const pseudo_typeS +md_pseudo_table[] = { + { "align", s_align_bytes, 4 }, + { "block", s_space, 0 }, + { "cputype", s_ignore, 0 }, /* CPU as 29000 or 29050 */ + { "file", s_ignore, 0 }, /* COFF File name for debug info? */ + { "line", s_ignore, 0 }, /* Line number of coff symbol */ + { "reg", s_lsym, 0 }, /* Register equate, same as equ */ + { "space", s_ignore, 0 }, /* Listing control */ + { "sect", s_ignore, 0 }, /* Creation of coff sections */ + { "use", s_use, 0 }, + { "word", cons, 4 }, + { NULL, 0, 0 }, +}; + +int md_short_jump_size = 4; +int md_long_jump_size = 4; +int md_reloc_size = 12; + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful */ +char comment_chars[] = ";"; + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that comments like this one will always work */ +char line_comment_chars[] = "#"; + +/* We needed an unused char for line separation to work around the + lack of macros, using sed and such. */ +char line_separator_chars[] = "@"; + +/* Chars that can be used to separate mant from exp in floating point nums */ +char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +char FLT_CHARS[] = "rRsSfFdDxXpP"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c . Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. + */ + +static unsigned char octal[256]; +#define isoctal(c) octal[c] +static unsigned char toHex[256]; + +/* + * anull bit - causes the branch delay slot instructions to not be executed + */ +#define ANNUL (1 << 29) + +static void +s_use() +{ + + if (strncmp(input_line_pointer, ".text", 5) == 0) { + input_line_pointer += 5; + s_text(); + return; + } + if (strncmp(input_line_pointer, ".data", 5) == 0) { + input_line_pointer += 5; + s_data(); + return; + } + if (strncmp(input_line_pointer, ".data1", 6) == 0) { + input_line_pointer += 6; + s_data1(); + return; + } + /* Literals can't go in the text segment because you can't read + from instruction memory on some 29k's. So, into initialized data. */ + if (strncmp(input_line_pointer, ".lit", 4) == 0) { + input_line_pointer += 4; + subseg_new(SEG_DATA, 200); + demand_empty_rest_of_line(); + return; + } + + as_bad("Unknown segment type"); + demand_empty_rest_of_line(); + return; +} + +static void +s_data1() +{ + subseg_new(SEG_DATA, 1); + demand_empty_rest_of_line(); + return; +} + +/* Install symbol definition that maps REGNAME to REGNO. + FIXME-SOON: These are not recognized in mixed case. */ + +static void +insert_sreg (regname, regnum) + char *regname; + int regnum; +{ + /* FIXME-SOON, put something in these syms so they won't be output to the symbol + table of the resulting object file. */ + + /* Must be large enough to hold the names of the special registers. */ + char buf[80]; + int i; + + symbol_table_insert(symbol_new(regname, SEG_REGISTER, regnum, &zero_address_frag)); + for (i = 0; regname[i]; i++) + buf[i] = islower (regname[i]) ? toupper (regname[i]) : regname[i]; + buf[i] = '\0'; + + symbol_table_insert(symbol_new(buf, SEG_REGISTER, regnum, &zero_address_frag)); +} /* insert_sreg() */ + +/* Install symbol definitions for assorted special registers. + See ASM29K Ref page 2-9. */ + +void define_some_regs() { +#define SREG 256 + + /* Protected special-purpose register names */ + insert_sreg ("vab", SREG+0); + insert_sreg ("ops", SREG+1); + insert_sreg ("cps", SREG+2); + insert_sreg ("cfg", SREG+3); + insert_sreg ("cha", SREG+4); + insert_sreg ("chd", SREG+5); + insert_sreg ("chc", SREG+6); + insert_sreg ("rbp", SREG+7); + insert_sreg ("tmc", SREG+8); + insert_sreg ("tmr", SREG+9); + insert_sreg ("pc0", SREG+10); + insert_sreg ("pc1", SREG+11); + insert_sreg ("pc2", SREG+12); + insert_sreg ("mmu", SREG+13); + insert_sreg ("lru", SREG+14); + + /* Unprotected special-purpose register names */ + insert_sreg ("ipc", SREG+128); + insert_sreg ("ipa", SREG+129); + insert_sreg ("ipb", SREG+130); + insert_sreg ("q", SREG+131); + insert_sreg ("alu", SREG+132); + insert_sreg ("bp", SREG+133); + insert_sreg ("fc", SREG+134); + insert_sreg ("cr", SREG+135); + insert_sreg ("fpe", SREG+160); + insert_sreg ("inte",SREG+161); + insert_sreg ("fps", SREG+162); + /* "", SREG+163); Reserved */ + insert_sreg ("exop",SREG+164); +} /* define_some_regs() */ + +/* This function is called once, at assembler startup time. It should + set up all the tables, etc. that the MD part of the assembler will need. */ +void +md_begin() +{ + register char *retval = NULL; + int lose = 0; + register int skipnext = 0; + register unsigned int i; + register char *strend, *strend2; + + /* Hash up all the opcodes for fast use later. */ + + op_hash = hash_new(); + if (op_hash == NULL) + as_fatal("Virtual memory exhausted"); + + for (i = 0; i < num_opcodes; i++) + { + const char *name = machine_opcodes[i].name; + + if (skipnext) { + skipnext = 0; + continue; + } + + /* Hack to avoid multiple opcode entries. We pre-locate all the + variations (b/i field and P/A field) and handle them. */ + + if (!strcmp (name, machine_opcodes[i+1].name)) { + if ((machine_opcodes[i].opcode ^ machine_opcodes[i+1].opcode) + != 0x01000000) + goto bad_table; + strend = machine_opcodes[i ].args+strlen(machine_opcodes[i ].args)-1; + strend2 = machine_opcodes[i+1].args+strlen(machine_opcodes[i+1].args)-1; + switch (*strend) { + case 'b': + if (*strend2 != 'i') goto bad_table; + break; + case 'i': + if (*strend2 != 'b') goto bad_table; + break; + case 'P': + if (*strend2 != 'A') goto bad_table; + break; + case 'A': + if (*strend2 != 'P') goto bad_table; + break; + default: + bad_table: + fprintf (stderr, "internal error: can't handle opcode %s\n", name); + lose = 1; + } + + /* OK, this is an i/b or A/P pair. We skip the higher-valued one, + and let the code for operand checking handle OR-ing in the bit. */ + if (machine_opcodes[i].opcode & 1) + continue; + else + skipnext = 1; + } + + retval = hash_insert (op_hash, name, &machine_opcodes[i]); + if (retval != NULL && *retval != '\0') + { + fprintf (stderr, "internal error: can't hash `%s': %s\n", + machine_opcodes[i].name, retval); + lose = 1; + } + } + + if (lose) + as_fatal("Broken assembler. No assembly attempted."); + + for (i = '0'; i < '8'; ++i) + octal[i] = 1; + for (i = '0'; i <= '9'; ++i) + toHex[i] = i - '0'; + for (i = 'a'; i <= 'f'; ++i) + toHex[i] = i + 10 - 'a'; + for (i = 'A'; i <= 'F'; ++i) + toHex[i] = i + 10 - 'A'; + + define_some_regs (); +} + +void md_end() { + return; +} + +/* Assemble a single instruction. Its label has already been handled + by the generic front end. We just parse opcode and operands, and + produce the bytes of data and relocation. */ + +void md_assemble(str) + char *str; +{ + char *toP; +/* !!!! int rsd; */ + + know(str); + machine_ip(str); + toP = frag_more(4); + /* put out the opcode */ + md_number_to_chars(toP, the_insn.opcode, 4); + + /* put out the symbol-dependent stuff */ + if (the_insn.reloc != NO_RELOC) { + fix_new( + frag_now, /* which frag */ + (toP - frag_now->fr_literal + the_insn.reloc_offset), /* where */ + 4, /* size */ + the_insn.exp.X_add_symbol, + the_insn.exp.X_subtract_symbol, + the_insn.exp.X_add_number, + the_insn.pcrel, + the_insn.reloc + ); + } +} + +char * +parse_operand (s, operandp) + char *s; + expressionS *operandp; +{ + char *save = input_line_pointer; + char *new; + segT seg; + + input_line_pointer = s; + seg = expr (0, operandp); + new = input_line_pointer; + input_line_pointer = save; + + switch (seg) { + case SEG_ABSOLUTE: + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_UNKNOWN: + case SEG_DIFFERENCE: + case SEG_BIG: + case SEG_REGISTER: + return new; + + case SEG_ABSENT: + as_bad("Missing operand"); + return new; + + default: + as_bad("Don't understand operand of type %s", segment_name (seg)); + return new; + } +} + +/* Instruction parsing. Takes a string containing the opcode. + Operands are at input_line_pointer. Output is in the_insn. + Warnings or errors are generated. */ + +static void +machine_ip(str) + char *str; +{ + char *s; + const char *args; +/* !!!! char c; */ +/* !!!! unsigned long i; */ + struct machine_opcode *insn; + char *argsStart; + unsigned long opcode; +/* !!!! unsigned int mask; */ + expressionS the_operand; + expressionS *operand = &the_operand; + unsigned int reg; + + /* Must handle `div0' opcode. */ + s = str; + if (isalpha(*s)) + for (; isalnum(*s); ++s) + if (isupper (*s)) + *s = tolower (*s); + + switch (*s) { + case '\0': + break; + + case ' ': /* FIXME-SOMEDAY more whitespace */ + *s++ = '\0'; + break; + + default: + as_bad("Unknown opcode: `%s'", str); + return; + } + if ((insn = (struct machine_opcode *) hash_find(op_hash, str)) == NULL) { + as_bad("Unknown opcode `%s'.", str); + return; + } + argsStart = s; + opcode = insn->opcode; + bzero(&the_insn, sizeof(the_insn)); + the_insn.reloc = NO_RELOC; + + /* + * Build the opcode, checking as we go to make + * sure that the operands match. + * + * If an operand matches, we modify the_insn or opcode appropriately, + * and do a "continue". If an operand fails to match, we "break". + */ + if (insn->args[0] != '\0') + s = parse_operand (s, operand); /* Prime the pump */ + + for (args = insn->args; ; ++args) { + switch (*args) { + + case '\0': /* end of args */ + if (*s == '\0') { + /* We are truly done. */ + the_insn.opcode = opcode; + return; + } + as_bad("Too many operands: %s", s); + break; + + case ',': /* Must match a comma */ + if (*s++ == ',') { + s = parse_operand (s, operand); /* Parse next opnd */ + continue; + } + break; + + case 'v': /* Trap numbers (immediate field) */ + if (operand->X_seg == SEG_ABSOLUTE) { + if (operand->X_add_number < 256) { + opcode |= (operand->X_add_number << 16); + continue; + } else { + as_bad("Immediate value of %d is too large", + operand->X_add_number); + continue; + } + } + the_insn.reloc = RELOC_8; + the_insn.reloc_offset = 1; /* BIG-ENDIAN Byte 1 of insn */ + the_insn.exp = *operand; + continue; + + case 'b': /* A general register or 8-bit immediate */ + case 'i': + /* We treat the two cases identically since we mashed + them together in the opcode table. */ + if (operand->X_seg == SEG_REGISTER) + goto general_reg; + + opcode |= IMMEDIATE_BIT; + if (operand->X_seg == SEG_ABSOLUTE) { + if (operand->X_add_number < 256) { + opcode |= operand->X_add_number; + continue; + } else { + as_bad("Immediate value of %d is too large", + operand->X_add_number); + continue; + } + } + the_insn.reloc = RELOC_8; + the_insn.reloc_offset = 3; /* BIG-ENDIAN Byte 3 of insn */ + the_insn.exp = *operand; + continue; + + case 'a': /* next operand must be a register */ + case 'c': + general_reg: + /* lrNNN or grNNN or %%expr or a user-def register name */ + if (operand->X_seg != SEG_REGISTER) + break; /* Only registers */ + know (operand->X_add_symbol == 0); + know (operand->X_subtract_symbol == 0); + reg = operand->X_add_number; + if (reg >= SREG) + break; /* No special registers */ + + /* + * Got the register, now figure out where + * it goes in the opcode. + */ + switch (*args) { + case 'a': + opcode |= reg << 8; + continue; + + case 'b': + case 'i': + opcode |= reg; + continue; + + case 'c': + opcode |= reg << 16; + continue; + } + abort(); + break; + + case 'x': /* 16 bit constant, zero-extended */ + case 'X': /* 16 bit constant, one-extended */ + if (operand->X_seg == SEG_ABSOLUTE) { + opcode |= (operand->X_add_number & 0xFF) << 0 | + ((operand->X_add_number & 0xFF00) << 8); + continue; + } + the_insn.reloc = RELOC_CONST; + the_insn.exp = *operand; + continue; + + case 'h': + if (operand->X_seg == SEG_ABSOLUTE) { + opcode |= (operand->X_add_number & 0x00FF0000) >> 16 | + (((unsigned long)operand->X_add_number + /* avoid sign ext */ & 0xFF000000) >> 8); + continue; + } + the_insn.reloc = RELOC_CONSTH; + the_insn.exp = *operand; + continue; + + case 'P': /* PC-relative jump address */ + case 'A': /* Absolute jump address */ + /* These two are treated together since we folded the + opcode table entries together. */ + if (operand->X_seg == SEG_ABSOLUTE) { + opcode |= ABSOLUTE_BIT | + (operand->X_add_number & 0x0003FC00) << 6 | + ((operand->X_add_number & 0x000003FC) >> 2); + continue; + } + the_insn.reloc = RELOC_JUMPTARG; + the_insn.exp = *operand; + the_insn.pcrel = 1; /* Assume PC-relative jump */ + /* FIXME-SOON, Do we figure out whether abs later, after know sym val? */ + continue; + + case 'e': /* Coprocessor enable bit for LOAD/STORE insn */ + if (operand->X_seg == SEG_ABSOLUTE) { + if (operand->X_add_number == 0) + continue; + if (operand->X_add_number == 1) { + opcode |= CE_BIT; + continue; + } + } + break; + + case 'n': /* Control bits for LOAD/STORE instructions */ + if (operand->X_seg == SEG_ABSOLUTE && + operand->X_add_number < 128) { + opcode |= (operand->X_add_number << 16); + continue; + } + break; + + case 's': /* Special register number */ + if (operand->X_seg != SEG_REGISTER) + break; /* Only registers */ + if (operand->X_add_number < SREG) + break; /* Not a special register */ + opcode |= (operand->X_add_number & 0xFF) << 8; + continue; + + case 'u': /* UI bit of CONVERT */ + if (operand->X_seg == SEG_ABSOLUTE) { + if (operand->X_add_number == 0) + continue; + if (operand->X_add_number == 1) { + opcode |= UI_BIT; + continue; + } + } + break; + + case 'r': /* RND bits of CONVERT */ + if (operand->X_seg == SEG_ABSOLUTE && + operand->X_add_number < 8) { + opcode |= operand->X_add_number << 4; + continue; + } + break; + + case 'd': /* FD bits of CONVERT */ + if (operand->X_seg == SEG_ABSOLUTE && + operand->X_add_number < 4) { + opcode |= operand->X_add_number << 2; + continue; + } + break; + + + case 'f': /* FS bits of CONVERT */ + if (operand->X_seg == SEG_ABSOLUTE && + operand->X_add_number < 4) { + opcode |= operand->X_add_number << 0; + continue; + } + break; + + case 'C': + if (operand->X_seg == SEG_ABSOLUTE && + operand->X_add_number < 4) { + opcode |= operand->X_add_number << 16; + continue; + } + break; + + case 'F': + if (operand->X_seg == SEG_ABSOLUTE && + operand->X_add_number < 16) { + opcode |= operand->X_add_number << 18; + continue; + } + break; + + default: + BAD_CASE (*args); + } + /* Types or values of args don't match. */ + as_bad("Invalid operands"); + return; + } +} + +/* + This is identical to the md_atof in m68k.c. I think this is right, + but I'm not sure. + + Turn a string in input_line_pointer into a floating point constant of type + type, and store the appropriate bytes in *litP. The number of LITTLENUMS + emitted is stored in *sizeP . An error message is returned, or NULL on OK. + */ + +/* Equal to MAX_PRECISION in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +char * +md_atof(type,litP,sizeP) + char type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + + switch(type) { + + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP=0; + return "Bad call to MD_ATOF()"; + } + t=atof_ieee(input_line_pointer,type,words); + if(t) + input_line_pointer=t; + *sizeP=prec * sizeof(LITTLENUM_TYPE); + for(wordP=words;prec--;) { + md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE)); + litP+=sizeof(LITTLENUM_TYPE); + } + return ""; /* Someone should teach Dean about null pointers */ +} + +/* + * Write out big-endian. + */ +void +md_number_to_chars(buf,val,n) + char *buf; + long val; + int n; +{ + + switch(n) { + + case 4: + *buf++ = val >> 24; + *buf++ = val >> 16; + case 2: + *buf++ = val >> 8; + case 1: + *buf = val; + break; + + default: + abort(); + } + return; +} + +void md_apply_fix(fixP, val) + fixS *fixP; + long val; +{ + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + + fixP->fx_addnumber = val; /* Remember value for emit_reloc */ + + if (fixP->fx_r_type == NO_RELOC) { + abort(); /* FIXME-SOON, if this is never used, remove */ + switch (fixP->fx_size) { + case 1: + *buf = val; + break; + case 2: + *buf++ = (val>>8); + *buf = val; + break; + case 4: + *buf++ = (val>>24); + *buf++ = (val>>16); + *buf++ = (val>>8); + *buf = val; + break; + default: + abort(); + } + return; + } + + know(fixP->fx_size == 4); + know(fixP->fx_r_type < NO_RELOC); + + /* + * This is a hack. There should be a better way to + * handle this. + */ + if (fixP->fx_r_type == RELOC_WDISP30 && fixP->fx_addsy) { + val += fixP->fx_where + fixP->fx_frag->fr_address; + } + + switch (fixP->fx_r_type) { + + case RELOC_32: + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; + break; + + case RELOC_8: + buf[0] = val; + break; + + case RELOC_WDISP30: + val = (val >>= 2) + 1; + buf[0] |= (val >> 24) & 0x3f; + buf[1]= (val >> 16); + buf[2] = val >> 8; + buf[3] = val; + break; + + case RELOC_HI22: + buf[1] |= (val >> 26) & 0x3f; + buf[2] = val >> 18; + buf[3] = val >> 10; + break; + + case RELOC_LO10: + buf[2] |= (val >> 8) & 0x03; + buf[3] = val; + break; + + case RELOC_BASE13: + buf[2] |= (val >> 8) & 0x1f; + buf[3] = val; + break; + + case RELOC_WDISP22: + val = (val >>= 2) + 1; + /* FALLTHROUGH */ + case RELOC_BASE22: + buf[1] |= (val >> 16) & 0x3f; + buf[2] = val >> 8; + buf[3] = val; + break; + +#if 0 + case RELOC_PC10: + case RELOC_PC22: + case RELOC_JMP_TBL: + case RELOC_SEGOFF16: + case RELOC_GLOB_DAT: + case RELOC_JMP_SLOT: + case RELOC_RELATIVE: +#endif + case RELOC_JUMPTARG: /* 00XX00XX pattern in a word */ + buf[1] = val >> 10; /* Holds bits 0003FFFC of address */ + buf[3] = val >> 2; + break; + + case RELOC_CONST: /* 00XX00XX pattern in a word */ + buf[1] = val >> 8; /* Holds bits 0000XXXX */ + buf[3] = val; + break; + + case RELOC_CONSTH: /* 00XX00XX pattern in a word */ + buf[1] = val >> 24; /* Holds bits XXXX0000 */ + buf[3] = val >> 16; + break; + + case NO_RELOC: + default: + as_bad("bad relocation type: 0x%02x", fixP->fx_r_type); + break; + } + return; +} + +#ifdef OBJ_COFF +short tc_coff_fix2rtype(fixP) +fixS *fixP; +{ + + /* FIXME-NOW: relocation type handling is not yet written for + a29k. */ + + know(0); + switch (fixP->fx_r_type) { + case RELOC_32: return(R_WORD); + case RELOC_8: return(R_BYTE); + default: know(0); + } /* switch on type */ + + return(0); +} /* tc_coff_fix2rtype() */ +#endif /* OBJ_COFF */ + +/* should never be called for sparc */ +void md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, to_addr; +fragS *frag; +symbolS *to_symbol; +{ + fprintf(stderr, "a29k_create_short_jmp\n"); + abort(); +} + +/* Translate internal representation of relocation info to target format. + + On sparc/29k: first 4 bytes are normal unsigned long address, next three + bytes are index, most sig. byte first. Byte 7 is broken up with + bit 7 as external, bits 6 & 5 unused, and the lower + five bits as relocation type. Next 4 bytes are long addend. */ +/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */ +void +md_ri_to_chars(the_bytes, ri) + char *the_bytes; + struct reloc_info_generic *ri; +{ + /* this is easy */ + md_number_to_chars(the_bytes, ri->r_address, 4); + /* now the fun stuff */ + the_bytes[4] = (ri->r_index >> 16) & 0x0ff; + the_bytes[5] = (ri->r_index >> 8) & 0x0ff; + the_bytes[6] = ri->r_index & 0x0ff; + the_bytes[7] = ((ri->r_extern << 7) & 0x80) | (0 & 0x60) | (ri->r_type & 0x1F); + /* Also easy */ + md_number_to_chars(&the_bytes[8], ri->r_addend, 4); +} + +/* should never be called for 29k */ +void md_convert_frag(fragP) + register fragS *fragP; +{ + fprintf(stderr, "sparc_convert_frag\n"); + abort(); +} + +/* should never be called for 29k */ +void md_create_long_jump(ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, + to_addr; + fragS *frag; + symbolS *to_symbol; +{ + fprintf(stderr, "sparc_create_long_jump\n"); + abort(); +} + +/* should never be called for sparc */ +int md_estimate_size_before_relax(fragP, segtype) + register fragS *fragP; +segT segtype; +{ + fprintf(stderr, "sparc_estimate_size_before_relax\n"); + abort(); + return 0; +} + +#if 0 +/* for debugging only */ +static void +print_insn(insn) + struct machine_it *insn; +{ + char *Reloc[] = { + "RELOC_8", + "RELOC_16", + "RELOC_32", + "RELOC_DISP8", + "RELOC_DISP16", + "RELOC_DISP32", + "RELOC_WDISP30", + "RELOC_WDISP22", + "RELOC_HI22", + "RELOC_22", + "RELOC_13", + "RELOC_LO10", + "RELOC_SFA_BASE", + "RELOC_SFA_OFF13", + "RELOC_BASE10", + "RELOC_BASE13", + "RELOC_BASE22", + "RELOC_PC10", + "RELOC_PC22", + "RELOC_JMP_TBL", + "RELOC_SEGOFF16", + "RELOC_GLOB_DAT", + "RELOC_JMP_SLOT", + "RELOC_RELATIVE", + "NO_RELOC" + }; + + if (insn->error) { + fprintf(stderr, "ERROR: %s\n"); + } + fprintf(stderr, "opcode=0x%08x\n", insn->opcode); + fprintf(stderr, "reloc = %s\n", Reloc[insn->reloc]); + fprintf(stderr, "exp = {\n"); + fprintf(stderr, "\t\tX_add_symbol = %s\n", + insn->exp.X_add_symbol ? + (S_GET_NAME(insn->exp.X_add_symbol) ? + S_GET_NAME(insn->exp.X_add_symbol) : "???") : "0"); + fprintf(stderr, "\t\tX_sub_symbol = %s\n", + insn->exp.X_subtract_symbol ? + (S_GET_NAME(insn->exp.X_subtract_symbol) ? + S_GET_NAME(insn->exp.X_subtract_symbol) : "???") : "0"); + fprintf(stderr, "\t\tX_add_number = %d\n", + insn->exp.X_add_number); + fprintf(stderr, "}\n"); + return; +} +#endif + +/* + * Sparc/A29K relocations are completely different, so it needs + * this machine dependent routine to emit them. + */ +#ifdef OBJ_AOUT +static void emit_machine_reloc(fixP, segment_address_in_file) +register fixS *fixP; +relax_addressT segment_address_in_file; +{ + struct reloc_info_generic ri; + register symbolS *symbolP; + extern char *next_object_file_charP; +/* !!!! long add_number; */ + + bzero((char *) &ri, sizeof(ri)); + for (; fixP; fixP = fixP->fx_next) { + + if (fixP->fx_r_type >= NO_RELOC) { + fprintf(stderr, "fixP->fx_r_type = %d\n", fixP->fx_r_type); + abort(); + } + + if ((symbolP = fixP->fx_addsy) != NULL) { + ri.r_address = fixP->fx_frag->fr_address + + fixP->fx_where - segment_address_in_file; + ri.r_addend = fixP->fx_addnumber; + if (!S_IS_DEFINED(symbolP)) { + ri.r_extern = 1; + ri.r_index = symbolP->sy_number; + } else { + ri.r_extern = 0; + ri.r_index = S_GET_TYPE(symbolP); + } + ri.r_type = fixP->fx_r_type; + + md_ri_to_chars (next_object_file_charP, &ri); + next_object_file_charP += md_reloc_size; + } + } +} /* emit_machine_reloc() */ + +void (*md_emit_relocations)() = emit_machine_reloc; + +#endif /* OBJ_AOUT */ + +int +md_parse_option(argP,cntP,vecP) + char **argP; + int *cntP; + char ***vecP; +{ + return 1; +} + + +/* Default the values of symbols known that should be "predefined". We + don't bother to predefine them unless you actually use one, since there + are a lot of them. */ + +symbolS *md_undefined_symbol (name) + char *name; +{ + long regnum; + char testbuf[5+ /*SLOP*/ 5]; + + if (name[0] == 'g' || name[0] == 'G' || name[0] == 'l' || name[0] == 'L') + { + /* Perhaps a global or local register name */ + if (name[1] == 'r' || name[1] == 'R') + { + /* Parse the number, make sure it has no extra zeroes or trailing + chars */ + regnum = atol(&name[2]); + if (regnum > 127) + return 0; + sprintf(testbuf, "%ld", regnum); + if (strcmp (testbuf, &name[2]) != 0) + return 0; /* gr007 or lr7foo or whatever */ + + /* We have a wiener! Define and return a new symbol for it. */ + if (name[0] == 'l' || name[0] == 'L') + regnum += 128; + return(symbol_new(name, SEG_REGISTER, regnum, &zero_address_frag)); + } + } + + return 0; +} + +/* Parse an operand that is machine-specific. */ + +void md_operand(expressionP) + expressionS *expressionP; +{ + + if (input_line_pointer[0] == '%' && input_line_pointer[1] == '%') + { + /* We have a numeric register expression. No biggy. */ + input_line_pointer += 2; /* Skip %% */ + (void)expression (expressionP); + if (expressionP->X_seg != SEG_ABSOLUTE + || expressionP->X_add_number > 255) + as_bad("Invalid expression after %%%%\n"); + expressionP->X_seg = SEG_REGISTER; + } + else if (input_line_pointer[0] == '&') + { + /* We are taking the 'address' of a register...this one is not + in the manual, but it *is* in traps/fpsymbol.h! What they + seem to want is the register number, as an absolute number. */ + input_line_pointer++; /* Skip & */ + (void)expression (expressionP); + if (expressionP->X_seg != SEG_REGISTER) + as_bad("Invalid register in & expression"); + else + expressionP->X_seg = SEG_ABSOLUTE; + } +} + +/* Round up a section size to the appropriate boundary. */ +long +md_section_align (segment, size) + segT segment; + long size; +{ + return size; /* Byte alignment is fine */ +} + +/* Exactly what point is a PC-relative offset relative TO? + On the 29000, they're relative to the address of the instruction, + which we have set up as the address of the fixup too. */ +long md_pcrel_from (fixP) + fixS *fixP; +{ + return fixP->fx_where + fixP->fx_frag->fr_address; +} + +/* + * Local Variables: + * comment-column: 0 + * End: + */ + +/* end of tc-a29k.c */ diff --git a/gas/config/tc-a29k.h b/gas/config/tc-a29k.h new file mode 100644 index 0000000..e689cfc --- /dev/null +++ b/gas/config/tc-a29k.h @@ -0,0 +1,29 @@ +/* tc-a29k.h -- Assemble for the AMD 29000. + Copyright (C) 1989, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +#define TC_A29K + +#define tc_headers_hook(a) ; /* not used */ +#define tc_headers_hook(a) ; /* not used */ +#define tc_crawl_symbol_chain(a) ; /* not used */ +#define tc_coff_symbol_emit_hook(a) ; /* not used */ + +/* end of tc-a29k.h */ diff --git a/gas/config/tc-generic.c b/gas/config/tc-generic.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/gas/config/tc-generic.c diff --git a/gas/config/tc-generic.h b/gas/config/tc-generic.h new file mode 100644 index 0000000..aa63410 --- /dev/null +++ b/gas/config/tc-generic.h @@ -0,0 +1,15 @@ +/* + * This file is tc-generic.h and is intended to be a template for + * target cpu specific header files. + */ + +#define TC_GENERIC 1 + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of tc-generic.h */ diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c new file mode 100644 index 0000000..cff66a4 --- /dev/null +++ b/gas/config/tc-i386.c @@ -0,0 +1,1983 @@ +/* i386.c -- Assemble code for the Intel 80386 + Copyright (C) 1989, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +/* + Intel 80386 machine specific gas. + Written by Eliot Dresselhaus (eliot@mgm.mit.edu). + Bugs & suggestions are completely welcome. This is free software. + Please help us make it better. +*/ + +#include "as.h" + +#include "obstack.h" +#include "i386-opcode.h" + +/* 'md_assemble ()' gathers together information and puts it into a + i386_insn. */ + +typedef struct { + /* TM holds the template for the insn were currently assembling. */ + template tm; + /* SUFFIX holds the opcode suffix (e.g. 'l' for 'movl') if given. */ + char suffix; + /* Operands are coded with OPERANDS, TYPES, DISPS, IMMS, and REGS. */ + + /* OPERANDS gives the number of given operands. */ + unsigned int operands; + + /* REG_OPERANDS, DISP_OPERANDS, MEM_OPERANDS, IMM_OPERANDS give the number of + given register, displacement, memory operands and immediate operands. */ + unsigned int reg_operands, disp_operands, mem_operands, imm_operands; + + /* TYPES [i] is the type (see above #defines) which tells us how to + search through DISPS [i] & IMMS [i] & REGS [i] for the required + operand. */ + unsigned int types [MAX_OPERANDS]; + + /* Displacements (if given) for each operand. */ + expressionS * disps [MAX_OPERANDS]; + + /* Immediate operands (if given) for each operand. */ + expressionS * imms [MAX_OPERANDS]; + + /* Register operands (if given) for each operand. */ + reg_entry * regs [MAX_OPERANDS]; + + /* BASE_REG, INDEX_REG, and LOG2_SCALE_FACTOR are used to encode + the base index byte below. */ + reg_entry * base_reg; + reg_entry * index_reg; + unsigned int log2_scale_factor; + + /* SEG gives the seg_entry of this insn. It is equal to zero unless + an explicit segment override is given. */ + seg_entry * seg; /* segment for memory operands (if given) */ + + /* PREFIX holds all the given prefix opcodes (usually null). + PREFIXES is the size of PREFIX. */ + char prefix [MAX_PREFIXES]; + unsigned int prefixes; + + /* RM and IB are the modrm byte and the base index byte where the addressing + modes of this insn are encoded. */ + + modrm_byte rm; + base_index_byte bi; +} i386_insn; + +char FLT_CHARS[] = "fFdDxX"; +char EXP_CHARS[] = "eE"; +char line_comment_chars[] = "#"; +char comment_chars[] = "#/"; + +/* tables for lexical analysis */ +static char opcode_chars[256]; +static char register_chars[256]; +static char operand_chars[256]; +static char space_chars[256]; +static char identifier_chars[256]; +static char digit_chars[256]; + +/* lexical macros */ +#define is_opcode_char(x) (opcode_chars[(unsigned char) x]) +#define is_operand_char(x) (operand_chars[(unsigned char) x]) +#define is_register_char(x) (register_chars[(unsigned char) x]) +#define is_space_char(x) (space_chars[(unsigned char) x]) +#define is_identifier_char(x) (identifier_chars[(unsigned char) x]) +#define is_digit_char(x) (digit_chars[(unsigned char) x]) + +/* put here all non-digit non-letter charcters that may occur in an operand */ +static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:"; + +static char *ordinal_names[] = { "first", "second", "third" }; /* for printfs */ + +/* md_assemble() always leaves the strings it's passed unaltered. To + effect this we maintain a stack of saved characters that we've smashed + with '\0's (indicating end of strings for various sub-fields of the + assembler instruction). */ +static char save_stack[32]; +static char *save_stack_p; /* stack pointer */ +#define END_STRING_AND_SAVE(s) *save_stack_p++ = *s; *s = '\0' +#define RESTORE_END_STRING(s) *s = *--save_stack_p + +/* The instruction we're assembling. */ +static i386_insn i; + +/* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */ +static expressionS disp_expressions[2], im_expressions[2]; + +/* pointers to ebp & esp entries in reg_hash hash table */ +static reg_entry *ebp, *esp; + +static int this_operand; /* current operand we are working on */ + +/* +Interface to relax_segment. +There are 2 relax states for 386 jump insns: one for conditional & one +for unconditional jumps. This is because the these two types of jumps +add different sizes to frags when we're figuring out what sort of jump +to choose to reach a given label. */ + +/* types */ +#define COND_JUMP 1 /* conditional jump */ +#define UNCOND_JUMP 2 /* unconditional jump */ +/* sizes */ +#define BYTE 0 +#define WORD 1 +#define DWORD 2 +#define UNKNOWN_SIZE 3 + +#define ENCODE_RELAX_STATE(type,size) ((type<<2) | (size)) +#define SIZE_FROM_RELAX_STATE(s) \ + ( (((s) & 0x3) == BYTE ? 1 : (((s) & 0x3) == WORD ? 2 : 4)) ) + +const relax_typeS md_relax_table[] = { +/* + The fields are: + 1) most positive reach of this state, + 2) most negative reach of this state, + 3) how many bytes this mode will add to the size of the current frag + 4) which index into the table to try if we can't fit into this one. +*/ + {1, 1, 0, 0}, + {1, 1, 0, 0}, + {1, 1, 0, 0}, + {1, 1, 0, 0}, + + /* For now we don't use word displacement jumps: they may be + untrustworthy. */ + {127+1, -128+1, 0, ENCODE_RELAX_STATE(COND_JUMP,DWORD) }, + /* word conditionals add 3 bytes to frag: + 2 opcode prefix; 1 displacement bytes */ + {32767+2, -32768+2, 3, ENCODE_RELAX_STATE(COND_JUMP,DWORD) }, + /* dword conditionals adds 4 bytes to frag: + 1 opcode prefix; 3 displacement bytes */ + {0, 0, 4, 0}, + {1, 1, 0, 0}, + + {127+1, -128+1, 0, ENCODE_RELAX_STATE(UNCOND_JUMP,DWORD) }, + /* word jmp adds 2 bytes to frag: + 1 opcode prefix; 1 displacement bytes */ + {32767+2, -32768+2, 2, ENCODE_RELAX_STATE(UNCOND_JUMP,DWORD) }, + /* dword jmp adds 3 bytes to frag: + 0 opcode prefix; 3 displacement bytes */ + {0, 0, 3, 0}, + {1, 1, 0, 0}, + +}; + +#ifdef __STDC__ + +static char *output_invalid(int c); +static int i386_operand(char *operand_string); +static reg_entry *parse_register(char *reg_string); + +#else /* __STDC__ */ + +static char *output_invalid(); +static int i386_operand(); +static reg_entry *parse_register(); + +#endif /* __STDC__ */ + + +/* Ignore certain directives generated by gcc. This probably should + not be here. */ +void dummy () +{ + while (*input_line_pointer && *input_line_pointer != '\n') + input_line_pointer++; +} + +const pseudo_typeS md_pseudo_table[] = { + { "ffloat", float_cons, 'f' }, + { "dfloat", float_cons, 'd' }, + { "tfloat", float_cons, 'x' }, + { "value", cons, 2 }, + { "ident", dummy, 0 }, /* ignore these directives */ +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + { "def", dummy, 0 }, +#endif /* OBJ_AOUT or OBJ_BOUT */ + { "def", dummy, 0 }, + { "optim", dummy, 0 }, /* For sun386i cc */ + { "version", dummy, 0 }, +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + { "ln", dummy, 0 }, +#endif /* OBJ_AOUT or OBJ_BOUT */ + { "ln", dummy, 0 }, + { 0, 0, 0 } +}; + +/* for interface with expression () */ +extern char * input_line_pointer; + +/* obstack for constructing various things in md_begin */ +struct obstack o; + +/* hash table for opcode lookup */ +static struct hash_control *op_hash = (struct hash_control *) 0; +/* hash table for register lookup */ +static struct hash_control *reg_hash = (struct hash_control *) 0; +/* hash table for prefix lookup */ +static struct hash_control *prefix_hash = (struct hash_control *) 0; + + +void md_begin () +{ + char * hash_err; + + obstack_begin (&o,4096); + + /* initialize op_hash hash table */ + op_hash = hash_new(); /* xmalloc handles error */ + + { + register const template *optab; + register templates *core_optab; + char *prev_name; + + optab = i386_optab; /* setup for loop */ + prev_name = optab->name; + obstack_grow (&o, optab, sizeof(template)); + core_optab = (templates *) xmalloc (sizeof (templates)); + + for (optab++; optab < i386_optab_end; optab++) { + if (! strcmp (optab->name, prev_name)) { + /* same name as before --> append to current template list */ + obstack_grow (&o, optab, sizeof(template)); + } else { + /* different name --> ship out current template list; + add to hash table; & begin anew */ + /* Note: end must be set before start! since obstack_next_free changes + upon opstack_finish */ + core_optab->end = (template *) obstack_next_free(&o); + core_optab->start = (template *) obstack_finish(&o); + hash_err = hash_insert (op_hash, prev_name, (char *) core_optab); + if (hash_err && *hash_err) { + hash_error: + as_fatal("Internal Error: Can't hash %s: %s", prev_name, hash_err); + } + prev_name = optab->name; + core_optab = (templates *) xmalloc (sizeof(templates)); + obstack_grow (&o, optab, sizeof(template)); + } + } + } + + /* initialize reg_hash hash table */ + reg_hash = hash_new(); + { + register const reg_entry *regtab; + + for (regtab = i386_regtab; regtab < i386_regtab_end; regtab++) { + hash_err = hash_insert (reg_hash, regtab->reg_name, regtab); + if (hash_err && *hash_err) goto hash_error; + } + } + + esp = (reg_entry *) hash_find (reg_hash, "esp"); + ebp = (reg_entry *) hash_find (reg_hash, "ebp"); + + /* initialize reg_hash hash table */ + prefix_hash = hash_new(); + { + register const prefix_entry *prefixtab; + + for (prefixtab = i386_prefixtab; + prefixtab < i386_prefixtab_end; prefixtab++) { + hash_err = hash_insert (prefix_hash, prefixtab->prefix_name, prefixtab); + if (hash_err && *hash_err) goto hash_error; + } + } + + /* fill in lexical tables: opcode_chars, operand_chars, space_chars */ + { + register unsigned int c; + + bzero (opcode_chars, sizeof(opcode_chars)); + bzero (operand_chars, sizeof(operand_chars)); + bzero (space_chars, sizeof(space_chars)); + bzero (identifier_chars, sizeof(identifier_chars)); + bzero (digit_chars, sizeof(digit_chars)); + + for (c = 0; c < 256; c++) { + if (islower(c) || isdigit(c)) { + opcode_chars[c] = c; + register_chars[c] = c; + } else if (isupper(c)) { + opcode_chars[c] = tolower(c); + register_chars[c] = opcode_chars[c]; + } else if (c == PREFIX_SEPERATOR) { + opcode_chars[c] = c; + } else if (c == ')' || c == '(') { + register_chars[c] = c; + } + + if (isupper(c) || islower(c) || isdigit(c)) + operand_chars[c] = c; + else if (c && strchr(operand_special_chars, c)) + operand_chars[c] = c; + + if (isdigit(c) || c == '-') digit_chars[c] = c; + + if (isalpha(c) || c == '_' || c == '.' || isdigit(c)) + identifier_chars[c] = c; + + if (c == ' ' || c == '\t') space_chars[c] = c; + } + } +} + +void md_end() {} /* not much to do here. */ + + +#ifdef DEBUG386 + +/* debugging routines for md_assemble */ +/* static void pi (), pte (), pt (), pe (), ps (); */ + +static void pi (line, x) + char * line; + i386_insn *x; +{ + register template *p; + int i; + + fprintf (stdout, "%s: template ", line); + pte (&x->tm); + fprintf (stdout, " modrm: mode %x reg %x reg/mem %x", + x->rm.mode, x->rm.reg, x->rm.regmem); + fprintf (stdout, " base %x index %x scale %x\n", + x->bi.base, x->bi.index, x->bi.scale); + for (i = 0; i < x->operands; i++) { + fprintf (stdout, " #%d: ", i+1); + pt (x->types[i]); + fprintf (stdout, "\n"); + if (x->types[i] & Reg) fprintf (stdout, "%s\n", x->regs[i]->reg_name); + if (x->types[i] & Imm) pe (x->imms[i]); + if (x->types[i] & (Disp|Abs)) pe (x->disps[i]); + } +} + +static void pte (t) + template *t; +{ + int i; + fprintf (stdout, " %d operands ", t->operands); + fprintf (stdout, "opcode %x ", + t->base_opcode); + if (t->extension_opcode != None) + fprintf (stdout, "ext %x ", t->extension_opcode); + if (t->opcode_modifier&D) + fprintf (stdout, "D"); + if (t->opcode_modifier&W) + fprintf (stdout, "W"); + fprintf (stdout, "\n"); + for (i = 0; i < t->operands; i++) { + fprintf (stdout, " #%d type ", i+1); + pt (t->operand_types[i]); + fprintf (stdout, "\n"); + } +} + +static void pe (e) + expressionS *e; +{ + fprintf (stdout, " segment %s\n", segment_name (e->X_seg)); + fprintf (stdout, " add_number %d (%x)\n", + e->X_add_number, e->X_add_number); + if (e->X_add_symbol) { + fprintf (stdout, " add_symbol "); + ps (e->X_add_symbol); + fprintf (stdout, "\n"); + } + if (e->X_subtract_symbol) { + fprintf (stdout, " sub_symbol "); + ps (e->X_subtract_symbol); + fprintf (stdout, "\n"); + } +} + +static void ps (s) + symbolS *s; +{ + fprintf (stdout, "%s type %s%s", + S_GET_NAME(s), + S_IS_EXTERNAL(s) ? "EXTERNAL " : "", + segment_name(S_GET_SEGMENT(s))); +} + +struct type_name { + unsigned int mask; + char *tname; +} type_names[] = { + { Reg8, "r8" }, { Reg16, "r16" }, { Reg32, "r32" }, { Imm8, "i8" }, + { Imm8S, "i8s" }, + { Imm16, "i16" }, { Imm32, "i32" }, { Mem8, "Mem8"}, { Mem16, "Mem16"}, + { Mem32, "Mem32"}, { BaseIndex, "BaseIndex" }, + { Abs8, "Abs8" }, { Abs16, "Abs16" }, { Abs32, "Abs32" }, + { Disp8, "d8" }, { Disp16, "d16" }, + { Disp32, "d32" }, { SReg2, "SReg2" }, { SReg3, "SReg3" }, { Acc, "Acc" }, + { InOutPortReg, "InOutPortReg" }, { ShiftCount, "ShiftCount" }, + { Imm1, "i1" }, { Control, "control reg" }, {Test, "test reg"}, + { FloatReg, "FReg"}, {FloatAcc, "FAcc"}, + { JumpAbsolute, "Jump Absolute"}, + { 0, "" } +}; + +static void pt (t) + unsigned int t; +{ + register struct type_name *ty; + + if (t == Unknown) { + fprintf (stdout, "Unknown"); + } else { + for (ty = type_names; ty->mask; ty++) + if (t & ty->mask) fprintf (stdout, "%s, ", ty->tname); + } + fflush (stdout); +} + +#endif /* DEBUG386 */ + +/* + This is the guts of the machine-dependent assembler. LINE points to a + machine dependent instruction. This funciton is supposed to emit + the frags/bytes it assembles to. + */ +void md_assemble (line) + char *line; +{ + /* Holds temlate once we've found it. */ + register template * t; + + /* Possible templates for current insn */ + templates *current_templates = (templates *) 0; + + /* Initialize globals. */ + bzero (&i, sizeof(i)); + bzero (disp_expressions, sizeof(disp_expressions)); + bzero (im_expressions, sizeof(im_expressions)); + save_stack_p = save_stack; /* reset stack pointer */ + + /* Fist parse an opcode & call i386_operand for the operands. + We assume that the scrubber has arranged it so that line[0] is the valid + start of a (possibly prefixed) opcode. */ + { + register char *l = line; /* Fast place to put LINE. */ + + /* 1 if operand is pending after ','. */ + unsigned int expecting_operand = 0; + /* 1 if we found a prefix only acceptable with string insns. */ + unsigned int expecting_string_instruction = 0; + /* Non-zero if operand parens not balenced. */ + unsigned int paren_not_balenced; + char * token_start = l; + + while (! is_space_char(*l) && *l != END_OF_INSN) { + if (! is_opcode_char(*l)) { + as_bad("invalid character %s in opcode", output_invalid(*l)); + return; + } else if (*l != PREFIX_SEPERATOR) { + *l = opcode_chars[(unsigned char) *l]; /* fold case of opcodes */ + l++; + } else { /* this opcode's got a prefix */ + register unsigned int q; + register prefix_entry * prefix; + + if (l == token_start) { + as_bad("expecting prefix; got nothing"); + return; + } + END_STRING_AND_SAVE (l); + prefix = (prefix_entry *) hash_find (prefix_hash, token_start); + if (! prefix) { + as_bad("no such opcode prefix ('%s')", token_start); + return; + } + RESTORE_END_STRING (l); + /* check for repeated prefix */ + for (q = 0; q < i.prefixes; q++) + if (i.prefix[q] == prefix->prefix_code) { + as_bad("same prefix used twice; you don't really want this!"); + return; + } + if (i.prefixes == MAX_PREFIXES) { + as_bad("too many opcode prefixes"); + return; + } + i.prefix[i.prefixes++] = prefix->prefix_code; + if (prefix->prefix_code == REPE || prefix->prefix_code == REPNE) + expecting_string_instruction = 1; + /* skip past PREFIX_SEPERATOR and reset token_start */ + token_start = ++l; + } + } + END_STRING_AND_SAVE (l); + if (token_start == l) { + as_bad("expecting opcode; got nothing"); + return; + } + + /* Lookup insn in hash; try intel & att naming conventions if appropriate; + that is: we only use the opcode suffix 'b' 'w' or 'l' if we need to. */ + current_templates = (templates *) hash_find (op_hash, token_start); + if (! current_templates) { + int last_index = strlen(token_start) - 1; + char last_char = token_start[last_index]; + switch (last_char) { + case DWORD_OPCODE_SUFFIX: + case WORD_OPCODE_SUFFIX: + case BYTE_OPCODE_SUFFIX: + token_start[last_index] = '\0'; + current_templates = (templates *) hash_find (op_hash, token_start); + token_start[last_index] = last_char; + i.suffix = last_char; + } + if (!current_templates) { + as_bad("no such 386 instruction: `%s'", token_start); return; + } + } + RESTORE_END_STRING (l); + + /* check for rep/repne without a string instruction */ + if (expecting_string_instruction && + ! IS_STRING_INSTRUCTION (current_templates-> + start->base_opcode)) { + as_bad("expecting string instruction after rep/repne"); + return; + } + + /* There may be operands to parse. */ + if (*l != END_OF_INSN && + /* For string instructions, we ignore any operands if given. This + kludges, for example, 'rep/movsb %ds:(%esi), %es:(%edi)' where + the operands are always going to be the same, and are not really + encoded in machine code. */ + ! IS_STRING_INSTRUCTION (current_templates-> + start->base_opcode)) { + /* parse operands */ + do { + /* skip optional white space before operand */ + while (! is_operand_char(*l) && *l != END_OF_INSN) { + if (! is_space_char(*l)) { + as_bad("invalid character %s before %s operand", + output_invalid(*l), + ordinal_names[i.operands]); + return; + } + l++; + } + token_start = l; /* after white space */ + paren_not_balenced = 0; + while (paren_not_balenced || *l != ',') { + if (*l == END_OF_INSN) { + if (paren_not_balenced) { + as_bad("unbalenced parenthesis in %s operand.", + ordinal_names[i.operands]); + return; + } else break; /* we are done */ + } else if (! is_operand_char(*l)) { + as_bad("invalid character %s in %s operand", + output_invalid(*l), + ordinal_names[i.operands]); + return; + } + if (*l == '(') ++paren_not_balenced; + if (*l == ')') --paren_not_balenced; + l++; + } + if (l != token_start) { /* yes, we've read in another operand */ + unsigned int operand_ok; + this_operand = i.operands++; + if (i.operands > MAX_OPERANDS) { + as_bad("spurious operands; (%d operands/instruction max)", + MAX_OPERANDS); + return; + } + /* now parse operand adding info to 'i' as we go along */ + END_STRING_AND_SAVE (l); + operand_ok = i386_operand (token_start); + RESTORE_END_STRING (l); /* restore old contents */ + if (!operand_ok) return; + } else { + if (expecting_operand) { + expecting_operand_after_comma: + as_bad("expecting operand after ','; got nothing"); + return; + } + if (*l == ',') { + as_bad("expecting operand before ','; got nothing"); + return; + } + } + + /* now *l must be either ',' or END_OF_INSN */ + if (*l == ',') { + if (*++l == END_OF_INSN) { /* just skip it, if it's \n complain */ + goto expecting_operand_after_comma; + } + expecting_operand = 1; + } + } while (*l != END_OF_INSN); /* until we get end of insn */ + } + } + + /* Now we've parsed the opcode into a set of templates, and have the + operands at hand. + Next, we find a template that matches the given insn, + making sure the overlap of the given operands types is consistent + with the template operand types. */ + +#define MATCH(overlap,given_type) \ + (overlap && \ + (overlap & (JumpAbsolute|BaseIndex|Mem8)) \ + == (given_type & (JumpAbsolute|BaseIndex|Mem8))) + + /* If m0 and m1 are register matches they must be consistent + with the expected operand types t0 and t1. + That is, if both m0 & m1 are register matches + i.e. ( ((m0 & (Reg)) && (m1 & (Reg)) ) ? + then, either 1. or 2. must be true: + 1. the expected operand type register overlap is null: + (t0 & t1 & Reg) == 0 + AND + the given register overlap is null: + (m0 & m1 & Reg) == 0 + 2. the expected operand type register overlap == the given + operand type overlap: (t0 & t1 & m0 & m1 & Reg). + */ +#define CONSISTENT_REGISTER_MATCH(m0, m1, t0, t1) \ + ( ((m0 & (Reg)) && (m1 & (Reg))) ? \ + ( ((t0 & t1 & (Reg)) == 0 && (m0 & m1 & (Reg)) == 0) || \ + ((t0 & t1) & (m0 & m1) & (Reg)) \ + ) : 1) + { + register unsigned int overlap0, overlap1; + expressionS * exp; + unsigned int overlap2; + unsigned int found_reverse_match; + + overlap0 = overlap1 = overlap2 = found_reverse_match = 0; + for (t = current_templates->start; + t < current_templates->end; + t++) { + + /* must have right number of operands */ + if (i.operands != t->operands) continue; + else if (!t->operands) break; /* 0 operands always matches */ + + overlap0 = i.types[0] & t->operand_types[0]; + switch (t->operands) { + case 1: + if (! MATCH (overlap0,i.types[0])) continue; + break; + case 2: case 3: + overlap1 = i.types[1] & t->operand_types[1]; + if (! MATCH (overlap0,i.types[0]) || + ! MATCH (overlap1,i.types[1]) || + ! CONSISTENT_REGISTER_MATCH(overlap0, overlap1, + t->operand_types[0], + t->operand_types[1])) { + + /* check if other direction is valid ... */ + if (! (t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS)) + continue; + + /* try reversing direction of operands */ + overlap0 = i.types[0] & t->operand_types[1]; + overlap1 = i.types[1] & t->operand_types[0]; + if (! MATCH (overlap0,i.types[0]) || + ! MATCH (overlap1,i.types[1]) || + ! CONSISTENT_REGISTER_MATCH (overlap0, overlap1, + t->operand_types[0], + t->operand_types[1])) { + /* does not match either direction */ + continue; + } + /* found a reverse match here -- slip through */ + /* found_reverse_match holds which of D or FloatD we've found */ + found_reverse_match = t->opcode_modifier & COMES_IN_BOTH_DIRECTIONS; + } /* endif: not forward match */ + /* found either forward/reverse 2 operand match here */ + if (t->operands == 3) { + overlap2 = i.types[2] & t->operand_types[2]; + if (! MATCH (overlap2,i.types[2]) || + ! CONSISTENT_REGISTER_MATCH (overlap0, overlap2, + t->operand_types[0], + t->operand_types[2]) || + ! CONSISTENT_REGISTER_MATCH (overlap1, overlap2, + t->operand_types[1], + t->operand_types[2])) + continue; + } + /* found either forward/reverse 2 or 3 operand match here: + slip through to break */ + } + break; /* we've found a match; break out of loop */ + } /* for (t = ... */ + if (t == current_templates->end) { /* we found no match */ + as_bad("operands given don't match any known 386 instruction"); + return; + } + + /* Copy the template we found (we may change it!). */ + bcopy (t, &i.tm, sizeof (template)); + t = &i.tm; /* alter new copy of template */ + + /* If there's no opcode suffix we try to invent one based on register + operands. */ + if (! i.suffix && i.reg_operands) { + /* We take i.suffix from the LAST register operand specified. This + assumes that the last register operands is the destination register + operand. */ + int o; + for (o = 0; o < MAX_OPERANDS; o++) + if (i.types[o] & Reg) { + i.suffix = (i.types[o] == Reg8) ? BYTE_OPCODE_SUFFIX : + (i.types[o] == Reg16) ? WORD_OPCODE_SUFFIX : + DWORD_OPCODE_SUFFIX; + } + } + + /* Make still unresolved immediate matches conform to size of immediate + given in i.suffix. Note: overlap2 cannot be an immediate! + We assume this. */ + if ((overlap0 & (Imm8|Imm8S|Imm16|Imm32)) + && overlap0 != Imm8 && overlap0 != Imm8S + && overlap0 != Imm16 && overlap0 != Imm32) { + if (! i.suffix) { + as_bad("no opcode suffix given; can't determine immediate size"); + return; + } + overlap0 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8|Imm8S) : + (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32)); + } + if ((overlap1 & (Imm8|Imm8S|Imm16|Imm32)) + && overlap1 != Imm8 && overlap1 != Imm8S + && overlap1 != Imm16 && overlap1 != Imm32) { + if (! i.suffix) { + as_bad("no opcode suffix given; can't determine immediate size"); + return; + } + overlap1 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8|Imm8S) : + (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32)); + } + + i.types[0] = overlap0; + i.types[1] = overlap1; + i.types[2] = overlap2; + + if (overlap0 & ImplicitRegister) i.reg_operands--; + if (overlap1 & ImplicitRegister) i.reg_operands--; + if (overlap2 & ImplicitRegister) i.reg_operands--; + if (overlap0 & Imm1) i.imm_operands = 0; /* kludge for shift insns */ + + if (found_reverse_match) { + unsigned int save; + save = t->operand_types[0]; + t->operand_types[0] = t->operand_types[1]; + t->operand_types[1] = save; + } + + /* Finalize opcode. First, we change the opcode based on the operand + size given by i.suffix: we never have to change things for byte insns, + or when no opcode suffix is need to size the operands. */ + + if (! i.suffix && (t->opcode_modifier & W)) { + as_bad("no opcode suffix given and no register operands; can't size instruction"); + return; + } + + if (i.suffix && i.suffix != BYTE_OPCODE_SUFFIX) { + /* Select between byte and word/dword operations. */ + if (t->opcode_modifier & W) + t->base_opcode |= W; + /* Now select between word & dword operations via the + operand size prefix. */ + if (i.suffix == WORD_OPCODE_SUFFIX) { + if (i.prefixes == MAX_PREFIXES) { + as_bad("%d prefixes given and 'w' opcode suffix gives too many prefixes", + MAX_PREFIXES); + return; + } + i.prefix[i.prefixes++] = WORD_PREFIX_OPCODE; + } + } + + /* For insns with operands there are more diddles to do to the opcode. */ + if (i.operands) { + /* If we found a reverse match we must alter the opcode direction bit + found_reverse_match holds bit to set (different for int & + float insns). */ + + if (found_reverse_match) { + t->base_opcode |= found_reverse_match; + } + + /* + The imul $imm, %reg instruction is converted into + imul $imm, %reg, %reg. */ + if (t->opcode_modifier & imulKludge) { + i.regs[2] = i.regs[1]; /* Pretend we saw the 3 operand case. */ + i.reg_operands = 2; + } + + /* Certain instructions expect the destination to be in the i.rm.reg + field. This is by far the exceptional case. For these instructions, + if the source operand is a register, we must reverse the i.rm.reg + and i.rm.regmem fields. We accomplish this by faking that the + two register operands were given in the reverse order. */ + if ((t->opcode_modifier & ReverseRegRegmem) && i.reg_operands == 2) { + unsigned int first_reg_operand = (i.types[0] & Reg) ? 0 : 1; + unsigned int second_reg_operand = first_reg_operand + 1; + reg_entry *tmp = i.regs[first_reg_operand]; + i.regs[first_reg_operand] = i.regs[second_reg_operand]; + i.regs[second_reg_operand] = tmp; + } + + if (t->opcode_modifier & ShortForm) { + /* The register or float register operand is in operand 0 or 1. */ + unsigned int o = (i.types[0] & (Reg|FloatReg)) ? 0 : 1; + /* Register goes in low 3 bits of opcode. */ + t->base_opcode |= i.regs[o]->reg_num; + } else if (t->opcode_modifier & ShortFormW) { + /* Short form with 0x8 width bit. Register is always dest. operand */ + t->base_opcode |= i.regs[1]->reg_num; + if (i.suffix == WORD_OPCODE_SUFFIX || + i.suffix == DWORD_OPCODE_SUFFIX) + t->base_opcode |= 0x8; + } else if (t->opcode_modifier & Seg2ShortForm) { + if (t->base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1) { + as_bad("you can't 'pop cs' on the 386."); + return; + } + t->base_opcode |= (i.regs[0]->reg_num << 3); + } else if (t->opcode_modifier & Seg3ShortForm) { + /* 'push %fs' is 0x0fa0; 'pop %fs' is 0x0fa1. + 'push %gs' is 0x0fa8; 'pop %fs' is 0x0fa9. + So, only if i.regs[0]->reg_num == 5 (%gs) do we need + to change the opcode. */ + if (i.regs[0]->reg_num == 5) + t->base_opcode |= 0x08; + } else if (t->opcode_modifier & Modrm) { + /* The opcode is completed (modulo t->extension_opcode which must + be put into the modrm byte. + Now, we make the modrm & index base bytes based on all the info + we've collected. */ + + /* i.reg_operands MUST be the number of real register operands; + implicit registers do not count. */ + if (i.reg_operands == 2) { + unsigned int source, dest; + source = (i.types[0] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 0 : 1; + dest = source + 1; + i.rm.mode = 3; + /* We must be careful to make sure that all segment/control/test/ + debug registers go into the i.rm.reg field (despite the whether + they are source or destination operands). */ + if (i.regs[dest]->reg_type & (SReg2|SReg3|Control|Debug|Test)) { + i.rm.reg = i.regs[dest]->reg_num; + i.rm.regmem = i.regs[source]->reg_num; + } else { + i.rm.reg = i.regs[source]->reg_num; + i.rm.regmem = i.regs[dest]->reg_num; + } + } else { /* if it's not 2 reg operands... */ + if (i.mem_operands) { + unsigned int fake_zero_displacement = 0; + unsigned int o = (i.types[0] & Mem) ? 0 : ((i.types[1] & Mem) ? 1 : 2); + + /* Encode memory operand into modrm byte and base index byte. */ + + if (i.base_reg == esp && ! i.index_reg) { + /* <disp>(%esp) becomes two byte modrm with no index register. */ + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]); + i.bi.base = ESP_REG_NUM; + i.bi.index = NO_INDEX_REGISTER; + i.bi.scale = 0; /* Must be zero! */ + } else if (i.base_reg == ebp && !i.index_reg) { + if (! (i.types[o] & Disp)) { + /* Must fake a zero byte displacement. + There is no direct way to code '(%ebp)' directly. */ + fake_zero_displacement = 1; + /* fake_zero_displacement code does not set this. */ + i.types[o] |= Disp8; + } + i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]); + i.rm.regmem = EBP_REG_NUM; + } else if (! i.base_reg && (i.types[o] & BaseIndex)) { + /* There are three cases here. + Case 1: '<32bit disp>(,1)' -- indirect absolute. + (Same as cases 2 & 3 with NO index register) + Case 2: <32bit disp> (,<index>) -- no base register with disp + Case 3: (, <index>) --- no base register; + no disp (must add 32bit 0 disp). */ + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + i.rm.mode = 0; /* 32bit mode */ + i.bi.base = NO_BASE_REGISTER; + i.types[o] &= ~Disp; + i.types[o] |= Disp32; /* Must be 32bit! */ + if (i.index_reg) { /* case 2 or case 3 */ + i.bi.index = i.index_reg->reg_num; + i.bi.scale = i.log2_scale_factor; + if (i.disp_operands == 0) + fake_zero_displacement = 1; /* case 3 */ + } else { + i.bi.index = NO_INDEX_REGISTER; + i.bi.scale = 0; + } + } else if (i.disp_operands && !i.base_reg && !i.index_reg) { + /* Operand is just <32bit disp> */ + i.rm.regmem = EBP_REG_NUM; + i.rm.mode = 0; + i.types[o] &= ~Disp; + i.types[o] |= Disp32; + } else { + /* It's not a special case; rev'em up. */ + i.rm.regmem = i.base_reg->reg_num; + i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]); + if (i.index_reg) { + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + i.bi.base = i.base_reg->reg_num; + i.bi.index = i.index_reg->reg_num; + i.bi.scale = i.log2_scale_factor; + if (i.base_reg == ebp && i.disp_operands == 0) { /* pace */ + fake_zero_displacement = 1; + i.types[o] |= Disp8; + i.rm.mode = MODE_FROM_DISP_SIZE (i.types[o]); + } + } + } + if (fake_zero_displacement) { + /* Fakes a zero displacement assuming that i.types[o] holds + the correct displacement size. */ + exp = &disp_expressions[i.disp_operands++]; + i.disps[o] = exp; + exp->X_seg = SEG_ABSOLUTE; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_subtract_symbol = (symbolS *) 0; + } + + /* Select the correct segment for the memory operand. */ + if (i.seg) { + const unsigned int seg_index; + const seg_entry * default_seg; + + if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING) { + seg_index = (i.rm.mode<<3) | i.bi.base; + default_seg = two_byte_segment_defaults [seg_index]; + } else { + seg_index = (i.rm.mode<<3) | i.rm.regmem; + default_seg = one_byte_segment_defaults [seg_index]; + } + /* If the specified segment is not the default, use an + opcode prefix to select it */ + if (i.seg != default_seg) { + if (i.prefixes == MAX_PREFIXES) { + as_bad("%d prefixes given and %s segment override gives too many prefixes", + MAX_PREFIXES, i.seg->seg_name); + return; + } + i.prefix[i.prefixes++] = i.seg->seg_prefix; + } + } + } + + /* Fill in i.rm.reg or i.rm.regmem field with register operand + (if any) based on t->extension_opcode. Again, we must be careful + to make sure that segment/control/debug/test registers are coded + into the i.rm.reg field. */ + if (i.reg_operands) { + unsigned int o = + (i.types[0] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 0 : + (i.types[1] & (Reg|SReg2|SReg3|Control|Debug|Test)) ? 1 : 2; + /* If there is an extension opcode to put here, the register number + must be put into the regmem field. */ + if (t->extension_opcode != None) + i.rm.regmem = i.regs[o]->reg_num; + else i.rm.reg = i.regs[o]->reg_num; + + /* Now, if no memory operand has set i.rm.mode = 0, 1, 2 + we must set it to 3 to indicate this is a register operand + int the regmem field */ + if (! i.mem_operands) i.rm.mode = 3; + } + + /* Fill in i.rm.reg field with extension opcode (if any). */ + if (t->extension_opcode != None) + i.rm.reg = t->extension_opcode; + } + } + } + } + + /* Handle conversion of 'int $3' --> special int3 insn. */ + if (t->base_opcode == INT_OPCODE && i.imms[0]->X_add_number == 3) { + t->base_opcode = INT3_OPCODE; + i.imm_operands = 0; + } + + /* We are ready to output the insn. */ + { + register char * p; + + /* Output jumps. */ + if (t->opcode_modifier & Jump) { + int n = i.disps[0]->X_add_number; + + switch (i.disps[0]->X_seg) { + case SEG_ABSOLUTE: + if (FITS_IN_SIGNED_BYTE (n)) { + p = frag_more (2); + p[0] = t->base_opcode; + p[1] = n; +#if 0 /* leave out 16 bit jumps - pace */ + } else if (FITS_IN_SIGNED_WORD (n)) { + p = frag_more (4); + p[0] = WORD_PREFIX_OPCODE; + p[1] = t->base_opcode; + md_number_to_chars (&p[2], n, 2); +#endif + } else { /* It's an absolute dword displacement. */ + if (t->base_opcode == JUMP_PC_RELATIVE) { /* pace */ + /* unconditional jump */ + p = frag_more (5); + p[0] = 0xe9; + md_number_to_chars (&p[1], n, 4); + } else { + /* conditional jump */ + p = frag_more (6); + p[0] = TWO_BYTE_OPCODE_ESCAPE; + p[1] = t->base_opcode + 0x10; + md_number_to_chars (&p[2], n, 4); + } + } + break; + default: + /* It's a symbol; end frag & setup for relax. + Make sure there are 6 chars left in the current frag; if not + we'll have to start a new one. */ + /* I caught it failing with obstack_room == 6, + so I changed to <= pace */ + if (obstack_room (&frags) <= 6) { + frag_wane(frag_now); + frag_new (0); + } + p = frag_more (1); + p[0] = t->base_opcode; + frag_var (rs_machine_dependent, + 6, /* 2 opcode/prefix + 4 displacement */ + 1, + ((unsigned char) *p == JUMP_PC_RELATIVE + ? ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE) + : ENCODE_RELAX_STATE (COND_JUMP, BYTE)), + i.disps[0]->X_add_symbol, + n, p); + break; + } + } else if (t->opcode_modifier & (JumpByte|JumpDword)) { + int size = (t->opcode_modifier & JumpByte) ? 1 : 4; + int n = i.disps[0]->X_add_number; + + if (FITS_IN_UNSIGNED_BYTE(t->base_opcode)) { + FRAG_APPEND_1_CHAR (t->base_opcode); + } else { + p = frag_more (2); /* opcode can be at most two bytes */ + /* put out high byte first: can't use md_number_to_chars! */ + *p++ = (t->base_opcode >> 8) & 0xff; + *p = t->base_opcode & 0xff; + } + + p = frag_more (size); + switch (i.disps[0]->X_seg) { + case SEG_ABSOLUTE: + md_number_to_chars (p, n, size); + if (size == 1 && ! FITS_IN_SIGNED_BYTE (n)) { + as_bad("loop/jecx only takes byte displacement; %d shortened to %d", + n, *p); + } + break; + default: + fix_new (frag_now, p - frag_now->fr_literal, size, + i.disps[0]->X_add_symbol, i.disps[0]->X_subtract_symbol, + i.disps[0]->X_add_number, 1, NO_RELOC); + break; + } + } else if (t->opcode_modifier & JumpInterSegment) { + p = frag_more (1 + 2 + 4); /* 1 opcode; 2 segment; 4 offset */ + p[0] = t->base_opcode; + if (i.imms[1]->X_seg == SEG_ABSOLUTE) + md_number_to_chars (p + 1, i.imms[1]->X_add_number, 4); + else + fix_new (frag_now, p + 1 - frag_now->fr_literal, 4, + i.imms[1]->X_add_symbol, + i.imms[1]->X_subtract_symbol, + i.imms[1]->X_add_number, 0, NO_RELOC); + if (i.imms[0]->X_seg != SEG_ABSOLUTE) + as_bad("can't handle non absolute segment in long call/jmp"); + md_number_to_chars (p + 5, i.imms[0]->X_add_number, 2); + } else { + /* Output normal instructions here. */ + register char *q; + + /* First the prefix bytes. */ + for (q = i.prefix; q < i.prefix + i.prefixes; q++) { + p = frag_more (1); + md_number_to_chars (p, (unsigned int) *q, 1); + } + + /* Now the opcode; be careful about word order here! */ + if (FITS_IN_UNSIGNED_BYTE(t->base_opcode)) { + FRAG_APPEND_1_CHAR (t->base_opcode); + } else if (FITS_IN_UNSIGNED_WORD(t->base_opcode)) { + p = frag_more (2); + /* put out high byte first: can't use md_number_to_chars! */ + *p++ = (t->base_opcode >> 8) & 0xff; + *p = t->base_opcode & 0xff; + } else { /* opcode is either 3 or 4 bytes */ + if (t->base_opcode & 0xff000000) { + p = frag_more (4); + *p++ = (t->base_opcode >> 24) & 0xff; + } else p = frag_more (3); + *p++ = (t->base_opcode >> 16) & 0xff; + *p++ = (t->base_opcode >> 8) & 0xff; + *p = (t->base_opcode ) & 0xff; + } + + /* Now the modrm byte and base index byte (if present). */ + if (t->opcode_modifier & Modrm) { + p = frag_more (1); + /* md_number_to_chars (p, i.rm, 1); */ + md_number_to_chars (p, (i.rm.regmem<<0 | i.rm.reg<<3 | i.rm.mode<<6), 1); + /* If i.rm.regmem == ESP (4) && i.rm.mode != Mode 3 (Register mode) + ==> need second modrm byte. */ + if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING && i.rm.mode != 3) { + p = frag_more (1); + /* md_number_to_chars (p, i.bi, 1); */ + md_number_to_chars (p,(i.bi.base<<0 | i.bi.index<<3 | i.bi.scale<<6), 1); + } + } + + if (i.disp_operands) { + register unsigned int n; + + for (n = 0; n < i.operands; n++) { + if (i.disps[n]) { + if (i.disps[n]->X_seg == SEG_ABSOLUTE) { + if (i.types[n] & (Disp8|Abs8)) { + p = frag_more (1); + md_number_to_chars (p, i.disps[n]->X_add_number, 1); + } else if (i.types[n] & (Disp16|Abs16)) { + p = frag_more (2); + md_number_to_chars (p, i.disps[n]->X_add_number, 2); + } else { /* Disp32|Abs32 */ + p = frag_more (4); + md_number_to_chars (p, i.disps[n]->X_add_number, 4); + } + } else { /* not SEG_ABSOLUTE */ + /* need a 32-bit fixup (don't support 8bit non-absolute disps) */ + p = frag_more (4); + fix_new (frag_now, p - frag_now->fr_literal, 4, + i.disps[n]->X_add_symbol, i.disps[n]->X_subtract_symbol, + i.disps[n]->X_add_number, 0, NO_RELOC); + } + } + } + } /* end displacement output */ + + /* output immediate */ + if (i.imm_operands) { + register unsigned int n; + + for (n = 0; n < i.operands; n++) { + if (i.imms[n]) { + if (i.imms[n]->X_seg == SEG_ABSOLUTE) { + if (i.types[n] & (Imm8|Imm8S)) { + p = frag_more (1); + md_number_to_chars (p, i.imms[n]->X_add_number, 1); + } else if (i.types[n] & Imm16) { + p = frag_more (2); + md_number_to_chars (p, i.imms[n]->X_add_number, 2); + } else { + p = frag_more (4); + md_number_to_chars (p, i.imms[n]->X_add_number, 4); + } + } else { /* not SEG_ABSOLUTE */ + /* need a 32-bit fixup (don't support 8bit non-absolute ims) */ + /* try to support other sizes ... */ + int size; + if (i.types[n] & (Imm8|Imm8S)) + size = 1; + else if (i.types[n] & Imm16) + size = 2; + else + size = 4; + p = frag_more (size); + fix_new (frag_now, p - frag_now->fr_literal, size, + i.imms[n]->X_add_symbol, i.imms[n]->X_subtract_symbol, + i.imms[n]->X_add_number, 0, NO_RELOC); + } + } + } + } /* end immediate output */ + } + +#ifdef DEBUG386 + if (flagseen ['D']) { + pi (line, &i); + } +#endif /* DEBUG386 */ + + } + return; +} + +/* Parse OPERAND_STRING into the i386_insn structure I. Returns non-zero + on error. */ + +static int i386_operand (operand_string) + char *operand_string; +{ + register char *op_string = operand_string; + + /* Address of '\0' at end of operand_string. */ + char * end_of_operand_string = operand_string + strlen(operand_string); + + /* Start and end of displacement string expression (if found). */ + char * displacement_string_start = 0; + char * displacement_string_end; + + /* We check for an absolute prefix (differentiating, + for example, 'jmp pc_relative_label' from 'jmp *absolute_label'. */ + if (*op_string == ABSOLUTE_PREFIX) { + op_string++; + i.types[this_operand] |= JumpAbsolute; + } + + /* Check if operand is a register. */ + if (*op_string == REGISTER_PREFIX) { + register reg_entry * r; + if (! (r = parse_register (op_string))) { + as_bad("bad register name ('%s')", op_string); + return 0; + } + /* Check for segment override, rather than segment register by + searching for ':' after %<x>s where <x> = s, c, d, e, f, g. */ + if ((r->reg_type & (SReg2|SReg3)) && op_string[3] == ':') { + switch (r->reg_num) { + case 0: + i.seg = &es; break; + case 1: + i.seg = &cs; break; + case 2: + i.seg = &ss; break; + case 3: + i.seg = &ds; break; + case 4: + i.seg = &fs; break; + case 5: + i.seg = &gs; break; + } + op_string += 4; /* skip % <x> s : */ + operand_string = op_string; /* Pretend given string starts here. */ + if (!is_digit_char(*op_string) && !is_identifier_char(*op_string) + && *op_string != '(' && *op_string != ABSOLUTE_PREFIX) { + as_bad("bad memory operand after segment override"); + return 0; + } + /* Handle case of %es:*foo. */ + if (*op_string == ABSOLUTE_PREFIX) { + op_string++; + i.types[this_operand] |= JumpAbsolute; + } + goto do_memory_reference; + } + i.types[this_operand] |= r->reg_type; + i.regs[this_operand] = r; + i.reg_operands++; + } else if (*op_string == IMMEDIATE_PREFIX) { /* ... or an immediate */ + char * save_input_line_pointer; + register expressionS *exp; + segT exp_seg; + if (i.imm_operands == MAX_IMMEDIATE_OPERANDS) { + as_bad("only 1 or 2 immediate operands are allowed"); + return 0; + } + exp = &im_expressions[i.imm_operands++]; + i.imms [this_operand] = exp; + save_input_line_pointer = input_line_pointer; + input_line_pointer = ++op_string; /* must advance op_string! */ + exp_seg = expression (exp); + input_line_pointer = save_input_line_pointer; + switch (exp_seg) { + case SEG_ABSENT: /* missing or bad expr becomes absolute 0 */ + as_bad("missing or invalid immediate expression '%s' taken as 0", + operand_string); + exp->X_seg = SEG_ABSOLUTE; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_subtract_symbol = (symbolS *) 0; + i.types[this_operand] |= Imm; + break; + case SEG_ABSOLUTE: + i.types[this_operand] |= SMALLEST_IMM_TYPE (exp->X_add_number); + break; + case SEG_TEXT: case SEG_DATA: case SEG_BSS: case SEG_UNKNOWN: + i.types[this_operand] |= Imm32; /* this is an address ==> 32bit */ + break; + default: +seg_unimplemented: + as_bad("Unimplemented segment type %d in parse_operand", exp_seg); + return 0; + } + /* shorten this type of this operand if the instruction wants + * fewer bits than are present in the immediate. The bit field + * code can put out 'andb $0xffffff, %al', for example. pace + * also 'movw $foo,(%eax)' + */ + switch (i.suffix) { + case WORD_OPCODE_SUFFIX: + i.types[this_operand] |= Imm16; + break; + case BYTE_OPCODE_SUFFIX: + i.types[this_operand] |= Imm16 | Imm8 | Imm8S; + break; + } + } else if (is_digit_char(*op_string) || is_identifier_char(*op_string) + || *op_string == '(') { + /* This is a memory reference of some sort. */ + register char * base_string; + unsigned int found_base_index_form; + + do_memory_reference: + if (i.mem_operands == MAX_MEMORY_OPERANDS) { + as_bad("more than 1 memory reference in instruction"); + return 0; + } + i.mem_operands++; + + /* Determine type of memory operand from opcode_suffix; + no opcode suffix implies general memory references. */ + switch (i.suffix) { + case BYTE_OPCODE_SUFFIX: + i.types[this_operand] |= Mem8; + break; + case WORD_OPCODE_SUFFIX: + i.types[this_operand] |= Mem16; + break; + case DWORD_OPCODE_SUFFIX: + default: + i.types[this_operand] |= Mem32; + } + + /* Check for base index form. We detect the base index form by + looking for an ')' at the end of the operand, searching + for the '(' matching it, and finding a REGISTER_PREFIX or ',' + after it. */ + base_string = end_of_operand_string - 1; + found_base_index_form = 0; + if (*base_string == ')') { + unsigned int parens_balenced = 1; + /* We've already checked that the number of left & right ()'s are equal, + so this loop will not be infinite. */ + do { + base_string--; + if (*base_string == ')') parens_balenced++; + if (*base_string == '(') parens_balenced--; + } while (parens_balenced); + base_string++; /* Skip past '('. */ + if (*base_string == REGISTER_PREFIX || *base_string == ',') + found_base_index_form = 1; + } + + /* If we can't parse a base index register expression, we've found + a pure displacement expression. We set up displacement_string_start + and displacement_string_end for the code below. */ + if (! found_base_index_form) { + displacement_string_start = op_string; + displacement_string_end = end_of_operand_string; + } else { + char *base_reg_name, *index_reg_name, *num_string; + int num; + + i.types[this_operand] |= BaseIndex; + + /* If there is a displacement set-up for it to be parsed later. */ + if (base_string != op_string + 1) { + displacement_string_start = op_string; + displacement_string_end = base_string - 1; + } + + /* Find base register (if any). */ + if (*base_string != ',') { + base_reg_name = base_string++; + /* skip past register name & parse it */ + while (isalpha(*base_string)) base_string++; + if (base_string == base_reg_name+1) { + as_bad("can't find base register name after '(%c'", + REGISTER_PREFIX); + return 0; + } + END_STRING_AND_SAVE (base_string); + if (! (i.base_reg = parse_register (base_reg_name))) { + as_bad("bad base register name ('%s')", base_reg_name); + return 0; + } + RESTORE_END_STRING (base_string); + } + + /* Now check seperator; must be ',' ==> index reg + OR num ==> no index reg. just scale factor + OR ')' ==> end. (scale factor = 1) */ + if (*base_string != ',' && *base_string != ')') { + as_bad("expecting ',' or ')' after base register in `%s'", + operand_string); + return 0; + } + + /* There may index reg here; and there may be a scale factor. */ + if (*base_string == ',' && *(base_string+1) == REGISTER_PREFIX) { + index_reg_name = ++base_string; + while (isalpha(*++base_string)); + END_STRING_AND_SAVE (base_string); + if (! (i.index_reg = parse_register(index_reg_name))) { + as_bad("bad index register name ('%s')", index_reg_name); + return 0; + } + RESTORE_END_STRING (base_string); + } + + /* Check for scale factor. */ + if (*base_string == ',' && isdigit(*(base_string+1))) { + num_string = ++base_string; + while (is_digit_char(*base_string)) base_string++; + if (base_string == num_string) { + as_bad("can't find a scale factor after ','"); + return 0; + } + END_STRING_AND_SAVE (base_string); + /* We've got a scale factor. */ + if (! sscanf (num_string, "%d", &num)) { + as_bad("can't parse scale factor from '%s'", num_string); + return 0; + } + RESTORE_END_STRING (base_string); + switch (num) { /* must be 1 digit scale */ + case 1: i.log2_scale_factor = 0; break; + case 2: i.log2_scale_factor = 1; break; + case 4: i.log2_scale_factor = 2; break; + case 8: i.log2_scale_factor = 3; break; + default: + as_bad("expecting scale factor of 1, 2, 4, 8; got %d", num); + return 0; + } + } else { + if (! i.index_reg && *base_string == ',') { + as_bad("expecting index register or scale factor after ','; got '%c'", + *(base_string+1)); + return 0; + } + } + } + + /* If there's an expression begining the operand, parse it, + assuming displacement_string_start and displacement_string_end + are meaningful. */ + if (displacement_string_start) { + register expressionS * exp; + segT exp_seg; + char * save_input_line_pointer; + exp = &disp_expressions[i.disp_operands]; + i.disps [this_operand] = exp; + i.disp_operands++; + save_input_line_pointer = input_line_pointer; + input_line_pointer = displacement_string_start; + END_STRING_AND_SAVE (displacement_string_end); + exp_seg = expression (exp); + if(*input_line_pointer) + as_bad("Ignoring junk '%s' after expression",input_line_pointer); + RESTORE_END_STRING (displacement_string_end); + input_line_pointer = save_input_line_pointer; + switch (exp_seg) { + case SEG_ABSENT: + /* missing expr becomes absolute 0 */ + as_bad("missing or invalid displacement '%s' taken as 0", + operand_string); + i.types[this_operand] |= (Disp|Abs); + exp->X_seg = SEG_ABSOLUTE; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_subtract_symbol = (symbolS *) 0; + break; + case SEG_ABSOLUTE: + i.types[this_operand] |= SMALLEST_DISP_TYPE (exp->X_add_number); + break; + case SEG_TEXT: case SEG_DATA: case SEG_BSS: + case SEG_UNKNOWN: /* must be 32 bit displacement (i.e. address) */ + i.types[this_operand] |= Disp32; + break; + default: + goto seg_unimplemented; + } + } + + /* Make sure the memory operand we've been dealt is valid. */ + if (i.base_reg && i.index_reg && + ! (i.base_reg->reg_type & i.index_reg->reg_type & Reg)) { + as_bad("register size mismatch in (base,index,scale) expression"); + return 0; + } + if ((i.base_reg && (i.base_reg->reg_type & Reg32) == 0) || + (i.index_reg && (i.index_reg->reg_type & Reg32) == 0)) { + as_bad("base/index register must be 32 bit register"); + return 0; + } + if (i.index_reg && i.index_reg == esp) { + as_bad("%s may not be used as an index register", esp->reg_name); + return 0; + } + } else { /* it's not a memory operand; argh! */ + as_bad("invalid char %s begining %s operand '%s'", + output_invalid(*op_string), ordinal_names[this_operand], + op_string); + return 0; + } + return 1; /* normal return */ +} + +/* + * md_estimate_size_before_relax() + * + * Called just before relax(). + * Any symbol that is now undefined will not become defined. + * Return the correct fr_subtype in the frag. + * Return the initial "guess for fr_var" to caller. + * The guess for fr_var is ACTUALLY the growth beyond fr_fix. + * Whatever we do to grow fr_fix or fr_var contributes to our returned value. + * Although it may not be explicit in the frag, pretend fr_var starts with a + * 0 value. + */ +int +md_estimate_size_before_relax (fragP, segment) + register fragS * fragP; + register segT segment; +{ + register unsigned char * opcode; + register int old_fr_fix; + + old_fr_fix = fragP -> fr_fix; + opcode = (unsigned char *) fragP -> fr_opcode; + /* We've already got fragP->fr_subtype right; all we have to do is check + for un-relaxable symbols. */ + if (S_GET_SEGMENT(fragP -> fr_symbol) != segment) { + /* symbol is undefined in this segment */ + switch (opcode[0]) { + case JUMP_PC_RELATIVE: /* make jmp (0xeb) a dword displacement jump */ + opcode[0] = 0xe9; /* dword disp jmp */ + fragP -> fr_fix += 4; + fix_new (fragP, old_fr_fix, 4, + fragP -> fr_symbol, + (symbolS *) 0, + fragP -> fr_offset, 1, NO_RELOC); + break; + + default: + /* This changes the byte-displacement jump 0x7N --> + the dword-displacement jump 0x0f8N */ + opcode[1] = opcode[0] + 0x10; + opcode[0] = TWO_BYTE_OPCODE_ESCAPE; /* two-byte escape */ + fragP -> fr_fix += 1 + 4; /* we've added an opcode byte */ + fix_new (fragP, old_fr_fix + 1, 4, + fragP -> fr_symbol, + (symbolS *) 0, + fragP -> fr_offset, 1, NO_RELOC); + break; + } + frag_wane (fragP); + } + return (fragP -> fr_var + fragP -> fr_fix - old_fr_fix); +} /* md_estimate_size_before_relax() */ + +/* + * md_convert_frag(); + * + * Called after relax() is finished. + * In: Address of frag. + * fr_type == rs_machine_dependent. + * fr_subtype is what the address relaxed to. + * + * Out: Any fixSs and constants are set up. + * Caller will turn frag into a ".space 0". + */ +void +md_convert_frag (fragP) + register fragS * fragP; +{ + register unsigned char * opcode; + unsigned char * where_to_put_displacement; + unsigned int target_address, opcode_address; + unsigned int extension; + int displacement_from_opcode_start; + + opcode = (unsigned char *) fragP -> fr_opcode; + + /* Address we want to reach in file space. */ + target_address = S_GET_VALUE(fragP->fr_symbol) + fragP->fr_offset; + + /* Address opcode resides at in file space. */ + opcode_address = fragP->fr_address + fragP->fr_fix; + + /* Displacement from opcode start to fill into instruction. */ + displacement_from_opcode_start = target_address - opcode_address; + + switch (fragP->fr_subtype) { + case ENCODE_RELAX_STATE (COND_JUMP, BYTE): + case ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE): + /* don't have to change opcode */ + extension = 1; /* 1 opcode + 1 displacement */ + where_to_put_displacement = &opcode[1]; + break; + + case ENCODE_RELAX_STATE (COND_JUMP, WORD): + opcode[1] = TWO_BYTE_OPCODE_ESCAPE; + opcode[2] = opcode[0] + 0x10; + opcode[0] = WORD_PREFIX_OPCODE; + extension = 4; /* 3 opcode + 2 displacement */ + where_to_put_displacement = &opcode[3]; + break; + + case ENCODE_RELAX_STATE (UNCOND_JUMP, WORD): + opcode[1] = 0xe9; + opcode[0] = WORD_PREFIX_OPCODE; + extension = 3; /* 2 opcode + 2 displacement */ + where_to_put_displacement = &opcode[2]; + break; + + case ENCODE_RELAX_STATE (COND_JUMP, DWORD): + opcode[1] = opcode[0] + 0x10; + opcode[0] = TWO_BYTE_OPCODE_ESCAPE; + extension = 5; /* 2 opcode + 4 displacement */ + where_to_put_displacement = &opcode[2]; + break; + + case ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD): + opcode[0] = 0xe9; + extension = 4; /* 1 opcode + 4 displacement */ + where_to_put_displacement = &opcode[1]; + break; + + default: + BAD_CASE(fragP -> fr_subtype); + break; + } + /* now put displacement after opcode */ + md_number_to_chars (where_to_put_displacement, + displacement_from_opcode_start - extension, + SIZE_FROM_RELAX_STATE (fragP->fr_subtype)); + fragP -> fr_fix += extension; +} + + +int md_short_jump_size = 2; /* size of byte displacement jmp */ +int md_long_jump_size = 5; /* size of dword displacement jmp */ +int md_reloc_size = 8; /* Size of relocation record */ + +void md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, to_addr; +fragS *frag; +symbolS *to_symbol; +{ + long offset; + + offset = to_addr - (from_addr + 2); + md_number_to_chars (ptr, (long) 0xeb, 1); /* opcode for byte-disp jump */ + md_number_to_chars (ptr + 1, offset, 1); +} + +void md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, to_addr; + fragS *frag; + symbolS *to_symbol; +{ + long offset; + + if (flagseen['m']) { + offset = to_addr - S_GET_VALUE(to_symbol); + md_number_to_chars (ptr, 0xe9, 1); /* opcode for long jmp */ + md_number_to_chars (ptr + 1, offset, 4); + fix_new (frag, (ptr+1) - frag->fr_literal, 4, + to_symbol, (symbolS *) 0, (long) 0, 0, NO_RELOC); + } else { + offset = to_addr - (from_addr + 5); + md_number_to_chars(ptr, (long) 0xe9, 1); + md_number_to_chars(ptr + 1, offset, 4); + } +} + +int +md_parse_option(argP,cntP,vecP) +char **argP; +int *cntP; +char ***vecP; +{ + return 1; +} + +void /* Knows about order of bytes in address. */ +md_number_to_chars (con, value, nbytes) + char con []; /* Return 'nbytes' of chars here. */ + long value; /* The value of the bits. */ + int nbytes; /* Number of bytes in the output. */ +{ + register char * p = con; + + switch (nbytes) { + case 1: + p[0] = value & 0xff; + break; + case 2: + p[0] = value & 0xff; + p[1] = (value >> 8) & 0xff; + break; + case 4: + p[0] = value & 0xff; + p[1] = (value>>8) & 0xff; + p[2] = (value>>16) & 0xff; + p[3] = (value>>24) & 0xff; + break; + default: + BAD_CASE (nbytes); + } +} + + +/* Apply a fixup (fixS) to segment data, once it has been determined + by our caller that we have all the info we need to fix it up. + + On the 386, immediates, displacements, and data pointers are all in + the same (little-endian) format, so we don't need to care about which + we are handling. */ + +void +md_apply_fix (fixP, value) + fixS * fixP; /* The fix we're to put in */ + long value; /* The value of the bits. */ +{ + register char * p = fixP->fx_where + fixP->fx_frag->fr_literal; + + switch (fixP->fx_size) { + case 1: + *p = value; + break; + case 2: + *p++ = value; + *p = (value>>8); + break; + case 4: + *p++ = value; + *p++ = (value>>8); + *p++ = (value>>16); + *p = (value>>24); + break; + default: + BAD_CASE (fixP->fx_size); + } +} + +long /* Knows about the byte order in a word. */ +md_chars_to_number (con, nbytes) +unsigned char con[]; /* Low order byte 1st. */ + int nbytes; /* Number of bytes in the input. */ +{ + long retval; + for (retval=0, con+=nbytes-1; nbytes--; con--) + { + retval <<= BITS_PER_CHAR; + retval |= *con; + } + return retval; +} + +/* Not needed for coff since relocation structure does not + contain bitfields. */ +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) +/* Output relocation information in the target's format. */ +void +md_ri_to_chars(the_bytes, ri) + char *the_bytes; + struct reloc_info_generic *ri; +{ + /* this is easy */ + md_number_to_chars(the_bytes, ri->r_address, 4); + /* now the fun stuff */ + the_bytes[6] = (ri->r_symbolnum >> 16) & 0x0ff; + the_bytes[5] = (ri->r_symbolnum >> 8) & 0x0ff; + the_bytes[4] = ri->r_symbolnum & 0x0ff; + the_bytes[7] = (((ri->r_extern << 3) & 0x08) | ((ri->r_length << 1) & 0x06) | + ((ri->r_pcrel << 0) & 0x01)) & 0x0F; +} +#endif /* OBJ_AOUT or OBJ_BOUT */ + + +#define MAX_LITTLENUMS 6 + +/* Turn the string pointed to by litP into a floating point constant of type + type, and emit the appropriate bytes. The number of LITTLENUMS emitted + is stored in *sizeP . An error message is returned, or NULL on OK. + */ +char * +md_atof(type,litP,sizeP) + char type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + + switch(type) { + case 'f': + case 'F': + prec = 2; + break; + + case 'd': + case 'D': + prec = 4; + break; + + case 'x': + case 'X': + prec = 5; + break; + + default: + *sizeP=0; + return "Bad call to md_atof ()"; + } + t = atof_ieee (input_line_pointer,type,words); + if(t) + input_line_pointer=t; + + *sizeP = prec * sizeof(LITTLENUM_TYPE); + /* this loops outputs the LITTLENUMs in REVERSE order; in accord with + the bigendian 386 */ + for(wordP = words + prec - 1;prec--;) { + md_number_to_chars (litP, (long) (*wordP--), sizeof(LITTLENUM_TYPE)); + litP += sizeof(LITTLENUM_TYPE); + } + return ""; /* Someone should teach Dean about null pointers */ +} + +char output_invalid_buf[8]; + +static char * output_invalid (c) + char c; +{ + if (isprint(c)) sprintf (output_invalid_buf, "'%c'", c); + else sprintf (output_invalid_buf, "(0x%x)", (unsigned) c); + return output_invalid_buf; +} + +static reg_entry *parse_register (reg_string) + char *reg_string; /* reg_string starts *before* REGISTER_PREFIX */ +{ + register char *s = reg_string; + register char *p; + char reg_name_given[MAX_REG_NAME_SIZE]; + + s++; /* skip REGISTER_PREFIX */ + for (p = reg_name_given; is_register_char (*s); p++, s++) { + *p = register_chars [*s]; + if (p >= reg_name_given + MAX_REG_NAME_SIZE) + return (reg_entry *) 0; + } + *p = '\0'; + return (reg_entry *) hash_find (reg_hash, reg_name_given); +} + + +/* We have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS * +md_undefined_symbol (name) + char *name; +{ + return 0; +} + +/* Parse an operand that is machine-specific. + We just return without modifying the expression if we have nothing + to do. */ + +/* ARGSUSED */ +void +md_operand (expressionP) + expressionS *expressionP; +{ +} + +/* Round up a section size to the appropriate boundary. */ +long +md_section_align (segment, size) + segT segment; + long size; +{ + return size; /* Byte alignment is fine */ +} + +/* Exactly what point is a PC-relative offset relative TO? + On the i386, they're relative to the address of the offset, plus + its size. (??? Is this right? FIXME-SOON!) */ +long +md_pcrel_from (fixP) + fixS *fixP; +{ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + +/* + * $Log$ + * Revision 1.1 1991/04/04 18:16:41 rich + * Initial revision + * + * Revision 1.2 1991/03/30 17:11:30 rich + * Updated md_create_short_jump calling protocol. + * + * + */ + +/* + * Local Variables: + * comment-column: 0 + * End: + */ + +/* end of tc-i386.c */ diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h new file mode 100644 index 0000000..a2b853e --- /dev/null +++ b/gas/config/tc-i386.h @@ -0,0 +1,247 @@ +/* i386.h -- Header file for i386.c + Copyright (C) 1989, 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define TC_I386 1 + +#define tc_crawl_symbol_chain(a) ; /* not used */ +#define tc_headers_hook(a) ; /* not used */ + +#define MAX_OPERANDS 3 /* max operands per insn */ +#define MAX_PREFIXES 4 /* max prefixes per opcode */ +#define MAX_IMMEDIATE_OPERANDS 2 /* max immediates per insn */ +#define MAX_MEMORY_OPERANDS 2 /* max memory ref per insn + * lcall uses 2 + */ +/* we define the syntax here (modulo base,index,scale syntax) */ +#define REGISTER_PREFIX '%' +#define IMMEDIATE_PREFIX '$' +#define ABSOLUTE_PREFIX '*' +#define PREFIX_SEPERATOR '/' + +#define TWO_BYTE_OPCODE_ESCAPE 0x0f + +/* register numbers */ +#define EBP_REG_NUM 5 +#define ESP_REG_NUM 4 + +/* modrm_byte.regmem for twobyte escape */ +#define ESCAPE_TO_TWO_BYTE_ADDRESSING ESP_REG_NUM +/* index_base_byte.index for no index register addressing */ +#define NO_INDEX_REGISTER ESP_REG_NUM +/* index_base_byte.base for no base register addressing */ +#define NO_BASE_REGISTER EBP_REG_NUM + +/* these are the att as opcode suffixes, making movl --> mov, for example */ +#define DWORD_OPCODE_SUFFIX 'l' +#define WORD_OPCODE_SUFFIX 'w' +#define BYTE_OPCODE_SUFFIX 'b' + +/* modrm.mode = REGMEM_FIELD_HAS_REG when a register is in there */ +#define REGMEM_FIELD_HAS_REG 0x3 /* always = 0x3 */ +#define REGMEM_FIELD_HAS_MEM (~REGMEM_FIELD_HAS_REG) + +#define END_OF_INSN '\0' + +/* +When an operand is read in it is classified by its type. This type includes +all the possible ways an operand can be used. Thus, '%eax' is both 'register +# 0' and 'The Accumulator'. In our language this is expressed by OR'ing +'Reg32' (any 32 bit register) and 'Acc' (the accumulator). +Operands are classified so that we can match given operand types with +the opcode table in i386-opcode.h. + */ +#define Unknown 0x0 +/* register */ +#define Reg8 0x1 /* 8 bit reg */ +#define Reg16 0x2 /* 16 bit reg */ +#define Reg32 0x4 /* 32 bit reg */ +#define Reg (Reg8|Reg16|Reg32) /* gen'l register */ +#define WordReg (Reg16|Reg32) /* for push/pop operands */ +/* immediate */ +#define Imm8 0x8 /* 8 bit immediate */ +#define Imm8S 0x10 /* 8 bit immediate sign extended */ +#define Imm16 0x20 /* 16 bit immediate */ +#define Imm32 0x40 /* 32 bit immediate */ +#define Imm1 0x80 /* 1 bit immediate */ +#define ImmUnknown Imm32 /* for unknown expressions */ +#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */ +/* memory */ +#define Disp8 0x200 /* 8 bit displacement (for jumps) */ +#define Disp16 0x400 /* 16 bit displacement */ +#define Disp32 0x800 /* 32 bit displacement */ +#define Disp (Disp8|Disp16|Disp32) /* General displacement */ +#define DispUnknown Disp32 /* for unknown size displacements */ +#define Mem8 0x1000 +#define Mem16 0x2000 +#define Mem32 0x4000 +#define BaseIndex 0x8000 +#define Mem (Disp|Mem8|Mem16|Mem32|BaseIndex) /* General memory */ +#define WordMem (Mem16|Mem32|Disp|BaseIndex) +#define ByteMem (Mem8|Disp|BaseIndex) +/* specials */ +#define InOutPortReg 0x10000 /* register to hold in/out port addr = dx */ +#define ShiftCount 0x20000 /* register to hold shift cound = cl */ +#define Control 0x40000 /* Control register */ +#define Debug 0x80000 /* Debug register */ +#define Test 0x100000 /* Test register */ +#define FloatReg 0x200000 /* Float register */ +#define FloatAcc 0x400000 /* Float stack top %st(0) */ +#define SReg2 0x800000 /* 2 bit segment register */ +#define SReg3 0x1000000 /* 3 bit segment register */ +#define Acc 0x2000000 /* Accumulator %al or %ax or %eax */ +#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc) +#define JumpAbsolute 0x4000000 +#define Abs8 0x08000000 +#define Abs16 0x10000000 +#define Abs32 0x20000000 +#define Abs (Abs8|Abs16|Abs32) + +#define MODE_FROM_DISP_SIZE(t) \ + ((t&(Disp8)) ? 1 : \ + ((t&(Disp32)) ? 2 : 0)) + +#define Byte (Reg8|Imm8|Imm8S) +#define Word (Reg16|Imm16) +#define DWord (Reg32|Imm32) + +/* convert opcode suffix ('b' 'w' 'l' typically) into type specifyer */ +#define OPCODE_SUFFIX_TO_TYPE(s) \ + (s == BYTE_OPCODE_SUFFIX ? Byte : \ + (s == WORD_OPCODE_SUFFIX ? Word : DWord)) + +#define FITS_IN_SIGNED_BYTE(num) ((num) >= -128 && (num) <= 127) +#define FITS_IN_UNSIGNED_BYTE(num) ((num) >= 0 && (num) <= 255) +#define FITS_IN_UNSIGNED_WORD(num) ((num) >= 0 && (num) <= 65535) +#define FITS_IN_SIGNED_WORD(num) ((num) >= -32768 && (num) <= 32767) + +#define SMALLEST_DISP_TYPE(num) \ + FITS_IN_SIGNED_BYTE(num) ? (Disp8|Disp32|Abs8|Abs32) : (Disp32|Abs32) + +#define SMALLEST_IMM_TYPE(num) \ + (num == 1) ? (Imm1|Imm8|Imm8S|Imm16|Imm32): \ + FITS_IN_SIGNED_BYTE(num) ? (Imm8S|Imm8|Imm16|Imm32) : \ + FITS_IN_UNSIGNED_BYTE(num) ? (Imm8|Imm16|Imm32): \ + (FITS_IN_SIGNED_WORD(num)||FITS_IN_UNSIGNED_WORD(num)) ? (Imm16|Imm32) : \ + (Imm32) + +typedef struct { + /* instruction name sans width suffix ("mov" for movl insns) */ + char *name; + + /* how many operands */ + unsigned int operands; + + /* base_opcode is the fundamental opcode byte with a optional prefix(es). */ + unsigned int base_opcode; + + /* extension_opcode is the 3 bit extension for group <n> insns. + If this template has no extension opcode (the usual case) use None */ + unsigned char extension_opcode; +#define None 0xff /* If no extension_opcode is possible. */ + + /* the bits in opcode_modifier are used to generate the final opcode from + the base_opcode. These bits also are used to detect alternate forms of + the same instruction */ + unsigned int opcode_modifier; + +/* opcode_modifier bits: */ +#define W 0x1 /* set if operands are words or dwords */ +#define D 0x2 /* D = 0 if Reg --> Regmem; D = 1 if Regmem --> Reg */ +/* direction flag for floating insns: MUST BE 0x400 */ +#define FloatD 0x400 +/* shorthand */ +#define DW (D|W) +#define ShortForm 0x10 /* register is in low 3 bits of opcode */ +#define ShortFormW 0x20 /* ShortForm and W bit is 0x8 */ +#define Seg2ShortForm 0x40 /* encoding of load segment reg insns */ +#define Seg3ShortForm 0x80 /* fs/gs segment register insns. */ +#define Jump 0x100 /* special case for jump insns. */ +#define JumpInterSegment 0x200 /* special case for intersegment leaps/calls */ +/* 0x400 CANNOT BE USED since it's already used by FloatD above */ +#define DONT_USE 0x400 +#define NoModrm 0x800 +#define Modrm 0x1000 +#define imulKludge 0x2000 +#define JumpByte 0x4000 +#define JumpDword 0x8000 +#define ReverseRegRegmem 0x10000 + + /* (opcode_modifier & COMES_IN_ALL_SIZES) is true if the + instuction comes in byte, word, and dword sizes and is encoded into + machine code in the canonical way. */ +#define COMES_IN_ALL_SIZES (W) + + /* (opcode_modifier & COMES_IN_BOTH_DIRECTIONS) indicates that the + source and destination operands can be reversed by setting either + the D (for integer insns) or the FloatD (for floating insns) bit + in base_opcode. */ +#define COMES_IN_BOTH_DIRECTIONS (D|FloatD) + + /* operand_types[i] describes the type of operand i. This is made + by OR'ing together all of the possible type masks. (e.g. + 'operand_types[i] = Reg|Imm' specifies that operand i can be + either a register or an immediate operand */ + unsigned int operand_types[3]; +} template; + +/* + 'templates' is for grouping together 'template' structures for opcodes + of the same name. This is only used for storing the insns in the grand + ole hash table of insns. + The templates themselves start at START and range up to (but not including) + END. +*/ +typedef struct { + template *start; + template *end; +} templates; + +/* these are for register name --> number & type hash lookup */ +typedef struct { + char * reg_name; + unsigned int reg_type; + unsigned int reg_num; +} reg_entry; + +typedef struct { + char * seg_name; + unsigned int seg_prefix; +} seg_entry; + +/* these are for prefix name --> prefix code hash lookup */ +typedef struct { + char * prefix_name; + unsigned char prefix_code; +} prefix_entry; + +/* 386 operand encoding bytes: see 386 book for details of this. */ +typedef struct { + unsigned regmem:3; /* codes register or memory operand */ + unsigned reg:3; /* codes register operand (or extended opcode) */ + unsigned mode:2; /* how to interpret regmem & reg */ +} modrm_byte; + +/* 386 opcode byte to code indirect addressing. */ +typedef struct { + unsigned base:3; + unsigned index:3; + unsigned scale:2; +} base_index_byte; + +/* end of tc-i386.h */ diff --git a/gas/config/tc-i860.c b/gas/config/tc-i860.c new file mode 100644 index 0000000..d9dd84c --- /dev/null +++ b/gas/config/tc-i860.c @@ -0,0 +1,1255 @@ +/* i860.c -- Assemble for the I860 + Copyright (C) 1989 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +#include "as.h" + +#include "i860-opcode.h" + +/* incorporated from i860.h */ +enum reloc_type /* NOTE: three bits max, see struct reloc_info_i860.r_type */ +{ + NO_RELOC = 0, BRADDR, LOW0, LOW1, LOW2, LOW3, LOW4, SPLIT0, SPLIT1, SPLIT2, RELOC_32, +}; + +enum highlow_type /* NOTE: two bits max, see reloc_info_i860.r_type */ +{ + NO_SPEC = 0, PAIR, HIGH, HIGHADJ, +}; + +struct reloc_info_i860 +{ + unsigned long r_address; +/* + * Using bit fields here is a bad idea because the order is not portable. :-( + */ + unsigned int r_symbolnum: 24; + unsigned int r_pcrel : 1; + unsigned int r_extern : 1; + /* combining the two field simplifies the argument passing in "new_fix()" */ + /* and is compatible with the existing Sparc #ifdef's */ + /* r_type: highlow_type - bits 5,4; reloc_type - bits 3-0 */ + unsigned int r_type : 6; + long r_addend; +}; + +#define relocation_info reloc_info_i860 + + +void md_begin(); +void md_end(); +void md_number_to_chars(); +void md_assemble(); +char *md_atof(); +void md_convert_frag(); +void md_create_short_jump(); +void md_create_long_jump(); +int md_estimate_size_before_relax(); +void md_number_to_imm(); +void md_number_to_disp(); +void md_number_to_field(); +void md_ri_to_chars(); +static void i860_ip(); +void emit_machine_reloc(); + +int md_reloc_size = sizeof(struct relocation_info); + +void (*md_emit_relocations)() = emit_machine_reloc; + +const relax_typeS md_relax_table[] = { 0 }; + +/* handle of the OPCODE hash table */ +static struct hash_control *op_hash = NULL; + +static void s_dual(), s_enddual(); +static void s_atmp(); + +const pseudo_typeS +md_pseudo_table[] = { + { "dual", s_dual, 4 }, + { "enddual", s_enddual, 4 }, + { "atmp", s_atmp, 4 }, + { NULL, 0, 0 }, +}; + +int md_short_jump_size = 4; +int md_long_jump_size = 4; + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful */ +char comment_chars[] = "!/"; /* JF removed '|' from comment_chars */ + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that comments like this one will always work. */ +char line_comment_chars[] = "#/"; + +/* Chars that can be used to separate mant from exp in floating point nums */ +char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +char FLT_CHARS[] = "rRsSfFdDxXpP"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c . Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. + */ +int size_reloc_info = sizeof(struct relocation_info); + +static unsigned char octal[256]; +#define isoctal(c) octal[c] +static unsigned char toHex[256]; + +struct i860_it { + char *error; + unsigned long opcode; + struct nlist *nlistp; + expressionS exp; + int pcrel; + enum expand_type expand; + enum highlow_type highlow; + enum reloc_type reloc; +} the_insn; + +#ifdef __STDC__ +static void print_insn(struct i860_it *insn); +static int getExpression(char *str); +#else +static void print_insn(); +static int getExpression(); +#endif +static char *expr_end; +static char last_expand; /* error if expansion after branch */ + +enum dual +{ + DUAL_OFF = 0, DUAL_ON, DUAL_DDOT, DUAL_ONDDOT, +}; +static enum dual dual_mode = DUAL_OFF; /* dual-instruction mode */ + +static void +s_dual() /* floating point instructions have dual set */ +{ + dual_mode = DUAL_ON; +} + +static void +s_enddual() /* floating point instructions have dual set */ +{ + dual_mode = DUAL_OFF; +} + +static int atmp = 31; /* temporary register for pseudo's */ + +static void +s_atmp() +{ + register int temp; + if (strncmp(input_line_pointer, "sp", 2) == 0) { + input_line_pointer += 2; + atmp = 2; + } + else if (strncmp(input_line_pointer, "fp", 2) == 0) { + input_line_pointer += 2; + atmp = 3; + } + else if (strncmp(input_line_pointer, "r", 1) == 0) { + input_line_pointer += 1; + temp = get_absolute_expression(); + if (temp >= 0 && temp <= 31) + atmp = temp; + else + as_bad("Unknown temporary pseudo register"); + } + else { + as_bad("Unknown temporary pseudo register"); + } + demand_empty_rest_of_line(); + return; +} + +/* This function is called once, at assembler startup time. It should + set up all the tables, etc. that the MD part of the assembler will need. */ +void +md_begin() +{ + register char *retval = NULL; + int lose = 0; + register unsigned int i = 0; + + op_hash = hash_new(); + if (op_hash == NULL) + as_fatal("Virtual memory exhausted"); + + while (i < NUMOPCODES) + { + const char *name = i860_opcodes[i].name; + retval = hash_insert(op_hash, name, &i860_opcodes[i]); + if(retval != NULL && *retval != '\0') + { + fprintf (stderr, "internal error: can't hash `%s': %s\n", + i860_opcodes[i].name, retval); + lose = 1; + } + do + { + if (i860_opcodes[i].match & i860_opcodes[i].lose) + { + fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n", + i860_opcodes[i].name, i860_opcodes[i].args); + lose = 1; + } + ++i; + } while (i < NUMOPCODES + && !strcmp(i860_opcodes[i].name, name)); + } + + if (lose) + as_fatal("Broken assembler. No assembly attempted."); + + for (i = '0'; i < '8'; ++i) + octal[i] = 1; + for (i = '0'; i <= '9'; ++i) + toHex[i] = i - '0'; + for (i = 'a'; i <= 'f'; ++i) + toHex[i] = i + 10 - 'a'; + for (i = 'A'; i <= 'F'; ++i) + toHex[i] = i + 10 - 'A'; +} + +void +md_end() +{ + return; +} + +void +md_assemble(str) + char *str; +{ + char *toP; + int rsd; + int no_opcodes = 1; + int i; + struct i860_it pseudo[3]; + + assert(str); + i860_ip(str); + + /* check for expandable flag to produce pseudo-instructions */ + if (the_insn.expand != 0 && the_insn.highlow == NO_SPEC) { + for (i = 0; i < 3; i++) + pseudo[i] = the_insn; + + switch (the_insn.expand) { + + case E_DELAY: + no_opcodes = 1; + break; + + case E_MOV: + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL && + (the_insn.exp.X_add_number < (1 << 15) && + the_insn.exp.X_add_number >= -(1 << 15))) + break; + /* or l%const,r0,ireg_dest */ + pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000; + pseudo[0].highlow = PAIR; + /* orh h%const,ireg_dest,ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000 | + ((the_insn.opcode & 0x001f0000) << 5); + pseudo[1].highlow = HIGH; + no_opcodes = 2; + break; + + case E_ADDR: + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL) + break; + /* orh ha%addr_expr,r0,r31 */ + pseudo[0].opcode = 0xec000000 | (atmp<<16); + pseudo[0].highlow = HIGHADJ; + pseudo[0].reloc = LOW0; /* must overwrite */ + /* l%addr_expr(r31),ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & ~0x003e0000) | (atmp << 21); + pseudo[1].highlow = PAIR; + no_opcodes = 2; + break; + + case E_U32: /* 2nd version emulates Intel as, not doc. */ + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL && + (the_insn.exp.X_add_number < (1 << 16) && + the_insn.exp.X_add_number >= 0)) + break; + /* $(opcode)h h%const,ireg_src2,ireg_dest + pseudo[0].opcode = (the_insn.opcode & 0xf3ffffff) | 0x0c000000; */ + /* $(opcode)h h%const,ireg_src2,r31 */ + pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000 | + (atmp << 16); + pseudo[0].highlow = HIGH; + /* $(opcode) l%const,ireg_dest,ireg_dest + pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | + ((the_insn.opcode & 0x001f0000) << 5); */ + /* $(opcode) l%const,r31,ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | + (atmp << 21); + pseudo[1].highlow = PAIR; + no_opcodes = 2; + break; + + case E_AND: /* 2nd version emulates Intel as, not doc. */ + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL && + (the_insn.exp.X_add_number < (1 << 16) && + the_insn.exp.X_add_number >= 0)) + break; + /* andnot h%const,ireg_src2,ireg_dest + pseudo[0].opcode = (the_insn.opcode & 0x03ffffff) | 0xd4000000; */ + /* andnot h%const,ireg_src2,r31 */ + pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000 | + (atmp << 16); + pseudo[0].highlow = HIGH; + pseudo[0].exp.X_add_number = -1 - the_insn.exp.X_add_number; + /* andnot l%const,ireg_dest,ireg_dest + pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | + ((the_insn.opcode & 0x001f0000) << 5); */ + /* andnot l%const,r31,ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | + (atmp << 21); + pseudo[1].highlow = PAIR; + pseudo[1].exp.X_add_number = -1 - the_insn.exp.X_add_number; + no_opcodes = 2; + break; + + case E_S32: + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL && + (the_insn.exp.X_add_number < (1 << 15) && + the_insn.exp.X_add_number >= -(1 << 15))) + break; + /* orh h%const,r0,r31 */ + pseudo[0].opcode = 0xec000000 | (atmp << 16); + pseudo[0].highlow = HIGH; + /* or l%const,r31,r31 */ + pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16); + pseudo[1].highlow = PAIR; + /* r31,ireg_src2,ireg_dest */ + pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11); + pseudo[2].reloc = NO_RELOC; + no_opcodes = 3; + break; + + default: + abort(); + } + + the_insn = pseudo[0]; + /* check for expanded opcode after branch or in dual */ + if (no_opcodes > 1 && last_expand == 1) + as_warn("Expanded opcode after delayed branch: `%s'", str); + if (no_opcodes > 1 && dual_mode != DUAL_OFF) + as_warn("Expanded opcode in dual mode: `%s'", str); + } + + i = 0; + do { /* always produce at least one opcode */ + toP = frag_more(4); + /* put out the opcode */ + md_number_to_chars(toP, the_insn.opcode, 4); + + /* check for expanded opcode after branch or in dual */ + last_expand = the_insn.pcrel; + + /* put out the symbol-dependent stuff */ + if (the_insn.reloc != NO_RELOC) { + fix_new( + frag_now, /* which frag */ + (toP - frag_now->fr_literal), /* where */ + 4, /* size */ + the_insn.exp.X_add_symbol, + the_insn.exp.X_subtract_symbol, + the_insn.exp.X_add_number, + the_insn.pcrel, + /* merge bit fields into one argument */ + (int)(((the_insn.highlow & 0x3) << 4) | (the_insn.reloc & 0xf)) + ); + } + the_insn = pseudo[++i]; + } while (--no_opcodes > 0); + +} + +static void +i860_ip(str) + char *str; +{ + char *s; + const char *args; + char c; + unsigned long i; + struct i860_opcode *insn; + char *argsStart; + unsigned long opcode; + unsigned int mask; + int match = 0; + int comma = 0; + + + for (s = str; islower(*s) || *s == '.' || *s == '3'; ++s) + ; + switch (*s) { + + case '\0': + break; + + case ',': + comma = 1; + + /*FALLTHROUGH*/ + + case ' ': + *s++ = '\0'; + break; + + default: + as_bad("Unknown opcode: `%s'", str); + exit(1); + } + + if (strncmp(str, "d.", 2) == 0) { /* check for d. opcode prefix */ + if (dual_mode == DUAL_ON) + dual_mode = DUAL_ONDDOT; + else + dual_mode = DUAL_DDOT; + str += 2; + } + + if ((insn = (struct i860_opcode *) hash_find(op_hash, str)) == NULL) { + if (dual_mode == DUAL_DDOT || dual_mode == DUAL_ONDDOT) + str -= 2; + as_bad("Unknown opcode: `%s'", str); + return; + } + if (comma) { + *--s = ','; + } + argsStart = s; + for (;;) { + opcode = insn->match; + bzero(&the_insn, sizeof(the_insn)); + the_insn.reloc = NO_RELOC; + + /* + * Build the opcode, checking as we go to make + * sure that the operands match + */ + for (args = insn->args; ; ++args) { + switch (*args) { + + case '\0': /* end of args */ + if (*s == '\0') { + match = 1; + } + break; + + case '+': + case '(': /* these must match exactly */ + case ')': + case ',': + case ' ': + if (*s++ == *args) + continue; + break; + + case '#': /* must be at least one digit */ + if (isdigit(*s++)) { + while (isdigit(*s)) { + ++s; + } + continue; + } + break; + + case '1': /* next operand must be a register */ + case '2': + case 'd': + switch (*s) { + + case 'f': /* frame pointer */ + s++; + if (*s++ == 'p') { + mask = 0x3; + break; + } + goto error; + + case 's': /* stack pointer */ + s++; + if (*s++ == 'p') { + mask= 0x2; + break; + } + goto error; + + case 'r': /* any register */ + s++; + if (!isdigit(c = *s++)) { + goto error; + } + if (isdigit(*s)) { + if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) { + goto error; + } + } else { + c -= '0'; + } + mask= c; + break; + + default: /* not this opcode */ + goto error; + } + /* + * Got the register, now figure out where + * it goes in the opcode. + */ + switch (*args) { + + case '1': + opcode |= mask << 11; + continue; + + case '2': + opcode |= mask << 21; + continue; + + case 'd': + opcode |= mask << 16; + continue; + + } + break; + + case 'e': /* next operand is a floating point register */ + case 'f': + case 'g': + if (*s++ == 'f' && isdigit(*s)) { + mask = *s++; + if (isdigit(*s)) { + mask = 10 * (mask - '0') + (*s++ - '0'); + if (mask >= 32) { + break; + } + } else { + mask -= '0'; + } + switch (*args) { + + case 'e': + opcode |= mask << 11; + continue; + + case 'f': + opcode |= mask << 21; + continue; + + case 'g': + opcode |= mask << 16; + if (dual_mode != DUAL_OFF) + opcode |= (1 << 9); /* dual mode instruction */ + if (dual_mode == DUAL_DDOT) + dual_mode = DUAL_OFF; + if (dual_mode == DUAL_ONDDOT) + dual_mode = DUAL_ON; + if ((opcode & (1 << 10)) && (mask == ((opcode >> 11) & 0x1f))) + as_warn("Fsr1 equals fdest with Pipelining"); + continue; + } + } + break; + + case 'c': /* next operand must be a control register */ + if (strncmp(s, "fir", 3) == 0) { + opcode |= 0x0 << 21; + s += 3; + continue; + } + if (strncmp(s, "psr", 3) == 0) { + opcode |= 0x1 << 21; + s += 3; + continue; + } + if (strncmp(s, "dirbase", 7) == 0) { + opcode |= 0x2 << 21; + s += 7; + continue; + } + if (strncmp(s, "db", 2) == 0) { + opcode |= 0x3 << 21; + s += 2; + continue; + } + if (strncmp(s, "fsr", 3) == 0) { + opcode |= 0x4 << 21; + s += 3; + continue; + } + if (strncmp(s, "epsr", 4) == 0) { + opcode |= 0x5 << 21; + s += 4; + continue; + } + break; + + case '5': /* 5 bit immediate in src1 */ + bzero(&the_insn, sizeof(the_insn)); + if ( !getExpression(s)) { + s = expr_end; + if (the_insn.exp.X_add_number & ~0x1f) + as_bad("5-bit immediate too large"); + opcode |= (the_insn.exp.X_add_number & 0x1f) << 11; + bzero(&the_insn, sizeof(the_insn)); + the_insn.reloc = NO_RELOC; + continue; + } + break; + + case 'l': /* 26 bit immediate, relative branch */ + the_insn.reloc = BRADDR; + the_insn.pcrel = 1; + goto immediate; + + case 's': /* 16 bit immediate, split relative branch */ + /* upper 5 bits of offset in dest field */ + the_insn.pcrel = 1; + the_insn.reloc = SPLIT0; + goto immediate; + + case 'S': /* 16 bit immediate, split (st), aligned */ + if (opcode & (1 << 28)) + if (opcode & 0x1) + the_insn.reloc = SPLIT2; + else + the_insn.reloc = SPLIT1; + else + the_insn.reloc = SPLIT0; + goto immediate; + + case 'I': /* 16 bit immediate, aligned */ + if (opcode & (1 << 28)) + if (opcode & 0x1) + the_insn.reloc = LOW2; + else + the_insn.reloc = LOW1; + else + the_insn.reloc = LOW0; + goto immediate; + + case 'i': /* 16 bit immediate */ + the_insn.reloc = LOW0; + + /*FALLTHROUGH*/ + + immediate: + if(*s==' ') + s++; + if (strncmp(s, "ha%", 3) == 0) { + the_insn.highlow = HIGHADJ; + s += 3; + } else if (strncmp(s, "h%", 2) == 0) { + the_insn.highlow = HIGH; + s += 2; + } else if (strncmp(s, "l%", 2) == 0) { + the_insn.highlow = PAIR; + s += 2; + } + the_insn.expand = insn->expand; + + /* Note that if the getExpression() fails, we will still have + created U entries in the symbol table for the 'symbols' + in the input string. Try not to create U symbols for + registers, etc. */ + + if ( !getExpression(s)) { + s = expr_end; + continue; + } + break; + + default: + abort(); + } + break; + } + error: + if (match == 0) + { + /* Args don't match. */ + if (&insn[1] - i860_opcodes < NUMOPCODES + && !strcmp(insn->name, insn[1].name)) + { + ++insn; + s = argsStart; + continue; + } + else + { + as_bad("Illegal operands"); + return; + } + } + break; + } + + the_insn.opcode = opcode; + return; +} + +static int +getExpression(str) + char *str; +{ + char *save_in; + segT seg; + + save_in = input_line_pointer; + input_line_pointer = str; + switch (seg = expression(&the_insn.exp)) { + + case SEG_ABSOLUTE: + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_UNKNOWN: + case SEG_DIFFERENCE: + case SEG_BIG: + case SEG_ABSENT: + break; + + default: + the_insn.error = "bad segment"; + expr_end = input_line_pointer; + input_line_pointer=save_in; + return 1; + } + expr_end = input_line_pointer; + input_line_pointer = save_in; + return 0; +} + + +/* + This is identical to the md_atof in m68k.c. I think this is right, + but I'm not sure. + + Turn a string in input_line_pointer into a floating point constant of type + type, and store the appropriate bytes in *litP. The number of LITTLENUMS + emitted is stored in *sizeP . An error message is returned, or NULL on OK. + */ + +/* Equal to MAX_PRECISION in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +char * +md_atof(type,litP,sizeP) + char type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + char *atof_ieee(); + + switch(type) { + + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP=0; + return "Bad call to MD_ATOF()"; + } + t=atof_ieee(input_line_pointer,type,words); + if(t) + input_line_pointer=t; + *sizeP=prec * sizeof(LITTLENUM_TYPE); + for(wordP=words;prec--;) { + md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE)); + litP+=sizeof(LITTLENUM_TYPE); + } + return ""; /* Someone should teach Dean about null pointers */ +} + +/* + * Write out big-endian. + */ +void +md_number_to_chars(buf,val,n) + char *buf; + long val; + int n; +{ + switch(n) { + + case 4: + *buf++ = val >> 24; + *buf++ = val >> 16; + case 2: + *buf++ = val >> 8; + case 1: + *buf = val; + break; + + default: + abort(); + } + return; +} + +void md_number_to_imm(buf,val,n, fixP) + char *buf; + long val; + int n; + fixS *fixP; +{ + enum reloc_type reloc = fixP->fx_r_type & 0xf; + enum highlow_type highlow = (fixP->fx_r_type >> 4) & 0x3; + + assert(buf); + assert(n == 4); /* always on i860 */ + + switch(highlow) + { + + case HIGHADJ: /* adjusts the high-order 16-bits */ + if (val & (1 << 15)) + val += (1 << 16); + + /*FALLTHROUGH*/ + + case HIGH: /* selects the high-order 16-bits */ + val >>= 16; + break; + + case PAIR: /* selects the low-order 16-bits */ + val = val & 0xffff; + break; + + default: + break; + } + + switch(reloc) + { + + case BRADDR: /* br,call,bc,bc.t,bnc,bnc.t w/26-bit immediate */ + if (fixP->fx_pcrel != 1) + as_bad("26-bit branch w/o pc relative set: 0x%08x", val); + val >>= 2; /* align pcrel offset, see manual */ + + if (val >= (1 << 25) || val < -(1 << 25)) /* check for overflow */ + as_bad("26-bit branch offset overflow: 0x%08x", val); + buf[0] = (buf[0] & 0xfc) | ((val >> 24) & 0x3); + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; + break; + + case SPLIT2: /* 16 bit immediate, 4-byte aligned */ + if (val & 0x3) + as_bad("16-bit immediate 4-byte alignment error: 0x%08x", val); + val &= ~0x3; /* 4-byte align value */ + /*FALLTHROUGH*/ + case SPLIT1: /* 16 bit immediate, 2-byte aligned */ + if (val & 0x1) + as_bad("16-bit immediate 2-byte alignment error: 0x%08x", val); + val &= ~0x1; /* 2-byte align value */ + /*FALLTHROUGH*/ + case SPLIT0: /* st,bla,bte,btne w/16-bit immediate */ + if (fixP->fx_pcrel == 1) + val >>= 2; /* align pcrel offset, see manual */ + /* check for bounds */ + if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15))) + as_bad("16-bit branch offset overflow: 0x%08x", val); + buf[1] = (buf[1] & ~0x1f) | ((val >> 11) & 0x1f); + buf[2] = (buf[2] & ~0x7) | ((val >> 8) & 0x7); + buf[3] |= val; /* perserve bottom opcode bits */ + break; + + case LOW4: /* fld,pfld,pst,flush 16-byte aligned */ + if (val & 0xf) + as_bad("16-bit immediate 16-byte alignment error: 0x%08x", val); + val &= ~0xf; /* 16-byte align value */ + /*FALLTHROUGH*/ + case LOW3: /* fld,pfld,pst,flush 8-byte aligned */ + if (val & 0x7) + as_bad("16-bit immediate 8-byte alignment error: 0x%08x", val); + val &= ~0x7; /* 8-byte align value */ + /*FALLTHROUGH*/ + case LOW2: /* 16 bit immediate, 4-byte aligned */ + if (val & 0x3) + as_bad("16-bit immediate 4-byte alignment error: 0x%08x", val); + val &= ~0x3; /* 4-byte align value */ + /*FALLTHROUGH*/ + case LOW1: /* 16 bit immediate, 2-byte aligned */ + if (val & 0x1) + as_bad("16-bit immediate 2-byte alignment error: 0x%08x", val); + val &= ~0x1; /* 2-byte align value */ + /*FALLTHROUGH*/ + case LOW0: /* 16 bit immediate, byte aligned */ + /* check for bounds */ + if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15))) + as_bad("16-bit immediate overflow: 0x%08x", val); + buf[2] = val >> 8; + buf[3] |= val; /* perserve bottom opcode bits */ + break; + + case NO_RELOC: + default: + as_bad("bad relocation type: 0x%02x", reloc); + break; + } + return; +} + +/* should never be called for i860 */ +void +md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, to_addr; +fragS *frag; +symbolS *to_symbol; +{ + fprintf(stderr, "i860_create_short_jmp\n"); + abort(); +} + +/* should never be called for i860 */ +void +md_number_to_disp(buf,val,n) + char *buf; + long val; +{ + fprintf(stderr, "md_number_to_disp\n"); + abort(); +} + +/* should never be called for i860 */ +void +md_number_to_field(buf,val,fix) + char *buf; + long val; + void *fix; +{ + fprintf(stderr, "i860_number_to_field\n"); + abort(); +} + +/* the bit-field entries in the relocation_info struct plays hell + with the byte-order problems of cross-assembly. So as a hack, + I added this mach. dependent ri twiddler. Ugly, but it gets + you there. -KWK */ +/* on i860: first 4 bytes are normal unsigned long address, next three + bytes are index, most sig. byte first. Byte 7 is broken up with + bit 7 as pcrel, bit 6 as extern, and the lower six bits as + relocation type (highlow 5-4). Next 4 bytes are long addend. */ +/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */ +void +md_ri_to_chars(ri_p, ri) + struct relocation_info *ri_p, ri; +{ +#if 0 + unsigned char the_bytes[sizeof(*ri_p)]; + + /* this is easy */ + md_number_to_chars(the_bytes, ri.r_address, sizeof(ri.r_address)); + /* now the fun stuff */ + the_bytes[4] = (ri.r_index >> 16) & 0x0ff; + the_bytes[5] = (ri.r_index >> 8) & 0x0ff; + the_bytes[6] = ri.r_index & 0x0ff; + the_bytes[7] = ((ri.r_extern << 7) & 0x80) | (0 & 0x60) | (ri.r_type & 0x1F); + /* Also easy */ + md_number_to_chars(&the_bytes[8], ri.r_addend, sizeof(ri.r_addend)); + /* now put it back where you found it, Junior... */ + bcopy (the_bytes, (char *)ri_p, sizeof(*ri_p)); +#endif +} + +/* should never be called for i860 */ +void +md_convert_frag(fragP) + register fragS *fragP; +{ + fprintf(stderr, "i860_convert_frag\n"); + abort(); +} + +/* should never be called for i860 */ +void +md_create_long_jump(ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, + to_addr; + fragS *frag; + symbolS *to_symbol; +{ + fprintf(stderr, "i860_create_long_jump\n"); + abort(); +} + +/* should never be called for i860 */ +int +md_estimate_size_before_relax(fragP, segtype) + register fragS *fragP; +segT segtype; +{ + fprintf(stderr, "i860_estimate_size_before_relax\n"); + abort(); + return 0; +} + +/* for debugging only, must match enum reloc_type */ +static char *Reloc[] = { + "NO_RELOC", + "BRADDR", + "LOW0", + "LOW1", + "LOW2", + "LOW3", + "LOW4", + "SPLIT0", + "SPLIT1", + "SPLIT2", + "RELOC_32", +}; +static char *Highlow[] = { + "NO_SPEC", + "PAIR", + "HIGH", + "HIGHADJ", +}; +static void +print_insn(insn) + struct i860_it *insn; +{ + if (insn->error) { + fprintf(stderr, "ERROR: %s\n"); + } + fprintf(stderr, "opcode=0x%08x\t", insn->opcode); + fprintf(stderr, "expand=0x%08x\t", insn->expand); + fprintf(stderr, "reloc = %s\t", Reloc[insn->reloc]); + fprintf(stderr, "highlow = %s\n", Highlow[insn->highlow]); + fprintf(stderr, "exp = {\n"); + fprintf(stderr, "\t\tX_add_symbol = %s\n", + insn->exp.X_add_symbol ? + (S_GET_NAME(insn->exp.X_add_symbol) ? + S_GET_NAME(insn->exp.X_add_symbol) : "???") : "0"); + fprintf(stderr, "\t\tX_sub_symbol = %s\n", + insn->exp.X_subtract_symbol ? + (S_GET_NAME(insn->exp.X_subtract_symbol) ? + S_GET_NAME(insn->exp.X_subtract_symbol) : "???") : "0"); + fprintf(stderr, "\t\tX_add_number = %d\n", + insn->exp.X_add_number); + fprintf(stderr, "}\n"); + return; +} + +int +md_parse_option(argP,cntP,vecP) + char **argP; + int *cntP; + char ***vecP; +{ + return 1; +} + +/* + * I860 relocations are completely different, so it needs + * this machine dependent routine to emit them. + */ +void +emit_machine_reloc(fixP, segment_address_in_file) + register fixS *fixP; + relax_addressT segment_address_in_file; +{ + struct reloc_info_i860 ri; + register symbolS *symbolP; + extern char *next_object_file_charP; + long add_number; + + bzero((char *) &ri, sizeof(ri)); + for (; fixP; fixP = fixP->fx_next) { + + if (fixP->fx_r_type & ~0x3f) { + fprintf(stderr, "fixP->fx_r_type = %d\n", fixP->fx_r_type); + abort(); + } + ri.r_pcrel = fixP->fx_pcrel; + ri.r_type = fixP->fx_r_type; + + if ((symbolP = fixP->fx_addsy) != NULL) { + ri.r_address = fixP->fx_frag->fr_address + + fixP->fx_where - segment_address_in_file; + if ((symbolP->sy_type & N_TYPE) == N_UNDF) { + ri.r_extern = 1; + ri.r_symbolnum = symbolP->sy_number; + } else { + ri.r_extern = 0; + ri.r_symbolnum = symbolP->sy_type & N_TYPE; + } + if (symbolP && symbolP->sy_frag) { + ri.r_addend = symbolP->sy_frag->fr_address; + } + ri.r_type = fixP->fx_r_type; + if (fixP->fx_pcrel) { + /* preserve actual offset vs. pc + 4 */ + ri.r_addend -= (ri.r_address + 4); + } else { + ri.r_addend = fixP->fx_addnumber; + } + + md_ri_to_chars((char *) &ri, ri); + append(&next_object_file_charP, (char *)& ri, sizeof(ri)); + } + } + return; +} + +/* Parse an operand that is machine-specific. + We just return without modifying the expression if we have nothing + to do. */ + +/* ARGSUSED */ +void +md_operand (expressionP) + expressionS *expressionP; +{ +} + +/* We have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS * +md_undefined_symbol (name) + char *name; +{ + return 0; +} + +/* Round up a section size to the appropriate boundary. */ +long +md_section_align (segment, size) + segT segment; + long size; +{ + return size; /* Byte alignment is fine */ +} + +/* Exactly what point is a PC-relative offset relative TO? + On the i860, they're relative to the address of the offset, plus + its size. (??? Is this right? FIXME-SOON!) */ +long +md_pcrel_from (fixP) + fixS *fixP; +{ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + +void +md_apply_fix(fixP, val) + fixS *fixP; + long val; +{ + char *place = fixP->fx_where + fixP->fx_frag->fr_literal; + + if (!fixP->fx_bit_fixP) { + + switch (fixP->fx_im_disp) { + case 0: + fixP->fx_addnumber = val; + md_number_to_imm(place, val, fixP->fx_size, fixP); + break; + case 1: + md_number_to_disp (place, + fixP->fx_pcrel ? val+fixP->fx_pcrel_adjust:val, + fixP->fx_size); + break; + case 2: /* fix requested for .long .word etc */ + md_number_to_chars (place, val, fixP->fx_size); + break; + default: + as_fatal("Internal error in md_apply_fix() in file \"%s\"", __FILE__); + } /* OVE: maybe one ought to put _imm _disp _chars in one md-func */ + } else { + md_number_to_field (place, val, fixP->fx_bit_fixP); + } + + return; +} /* md_apply_fix() */ + +/* + * $Log$ + * Revision 1.1 1991/04/04 18:16:48 rich + * Initial revision + * + * Revision 1.2 1991/03/30 17:11:32 rich + * Updated md_create_short_jump calling protocol. + * + * + */ + +/* + * Local Variables: + * fill-column: 131 + * comment-column: 0 + */ + +/* end of i860.c */ diff --git a/gas/config/tc-i860.h b/gas/config/tc-i860.h new file mode 100644 index 0000000..2fc6514 --- /dev/null +++ b/gas/config/tc-i860.h @@ -0,0 +1,14 @@ +/* + * This file is tc-i860.h. + */ + +#define TC_I860 1 + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of tc-i860.h */ diff --git a/gas/config/tc-i960.c b/gas/config/tc-i960.c new file mode 100644 index 0000000..a32325b --- /dev/null +++ b/gas/config/tc-i960.c @@ -0,0 +1,2800 @@ +/* i960.c - All the i80960-specific stuff + Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc. + +This file is part of GAS. + +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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +/* See comment on md_parse_option for 80960-specific invocation options. */ + +/****************************************************************************** + * i80690 NOTE!!!: + * Header, symbol, and relocation info will be used on the host machine + * only -- only executable code is actually downloaded to the i80960. + * Therefore, leave all such information in host byte order. + * + * (That's a slight lie -- we DO download some header information, but + * the downloader converts the file format and corrects the byte-ordering + * of the relevant fields while doing so.) + * + ***************************************************************************** */ + +/* There are 4 different lengths of (potentially) symbol-based displacements + * in the 80960 instruction set, each of which could require address fix-ups + * and (in the case of external symbols) emission of relocation directives: + * + * 32-bit (MEMB) + * This is a standard length for the base assembler and requires no + * special action. + * + * 13-bit (COBR) + * This is a non-standard length, but the base assembler has a hook for + * bit field address fixups: the fixS structure can point to a descriptor + * of the field, in which case our md_number_to_field() routine gets called + * to process it. + * + * I made the hook a little cleaner by having fix_new() (in the base + * assembler) return a pointer to the fixS in question. And I made it a + * little simpler by storing the field size (in this case 13) instead of + * of a pointer to another structure: 80960 displacements are ALWAYS + * stored in the low-order bits of a 4-byte word. + * + * Since the target of a COBR cannot be external, no relocation directives + * for this size displacement have to be generated. But the base assembler + * had to be modified to issue error messages if the symbol did turn out + * to be external. + * + * 24-bit (CTRL) + * Fixups are handled as for the 13-bit case (except that 24 is stored + * in the fixS). + * + * The relocation directive generated is the same as that for the 32-bit + * displacement, except that it's PC-relative (the 32-bit displacement + * never is). The i80960 version of the linker needs a mod to + * distinguish and handle the 24-bit case. + * + * 12-bit (MEMA) + * MEMA formats are always promoted to MEMB (32-bit) if the displacement + * is based on a symbol, because it could be relocated at link time. + * The only time we use the 12-bit format is if an absolute value of + * less than 4096 is specified, in which case we need neither a fixup nor + * a relocation directive. + */ + +#include <stdio.h> +#include <ctype.h> + +#include "as.h" + +#include "obstack.h" + +#include "i960-opcode.h" + +extern char *input_line_pointer; +extern struct hash_control *po_hash; +extern unsigned char nbytes_r_length[]; +extern char *next_object_file_charP; + +#ifdef OBJ_COFF +int md_reloc_size = sizeof(struct reloc); +#else /* OBJ_COFF */ +int md_reloc_size = sizeof(struct relocation_info); +#endif /* OBJ_COFF */ + +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) +#ifdef __STDC__ + +static void emit_machine_reloc(fixS *fixP, relax_addressT segment_address_in_file); + +#else /* __STDC__ */ + +static void emit_machine_reloc(); + +#endif /* __STDC__ */ + +void (*md_emit_relocations)() = emit_machine_reloc; +#endif /* OBJ_AOUT or OBJ_BOUT */ + + /*************************** + * Local i80960 routines * + ************************** */ + +static void brcnt_emit(); /* Emit branch-prediction instrumentation code */ +static char * brlab_next(); /* Return next branch local label */ + void brtab_emit(); /* Emit br-predict instrumentation table */ +static void cobr_fmt(); /* Generate COBR instruction */ +static void ctrl_fmt(); /* Generate CTRL instruction */ +static char * emit(); /* Emit (internally) binary */ +static int get_args(); /* Break arguments out of comma-separated list */ +static void get_cdisp(); /* Handle COBR or CTRL displacement */ +static char * get_ispec(); /* Find index specification string */ +static int get_regnum(); /* Translate text to register number */ +static int i_scan(); /* Lexical scan of instruction source */ +static void mem_fmt(); /* Generate MEMA or MEMB instruction */ +static void mema_to_memb(); /* Convert MEMA instruction to MEMB format */ +static segT parse_expr(); /* Parse an expression */ +static int parse_ldconst();/* Parse and replace a 'ldconst' pseudo-op */ +static void parse_memop(); /* Parse a memory operand */ +static void parse_po(); /* Parse machine-dependent pseudo-op */ +static void parse_regop(); /* Parse a register operand */ +static void reg_fmt(); /* Generate a REG format instruction */ + void reloc_callj(); /* Relocate a 'callj' instruction */ +static void relax_cobr(); /* "De-optimize" cobr into compare/branch */ +static void s_leafproc(); /* Process '.leafproc' pseudo-op */ +static void s_sysproc(); /* Process '.sysproc' pseudo-op */ +static int shift_ok(); /* Will a 'shlo' substiture for a 'ldconst'? */ +static void syntax(); /* Give syntax error */ +static int targ_has_sfr(); /* Target chip supports spec-func register? */ +static int targ_has_iclass();/* Target chip supports instruction set? */ +/* static void unlink_sym(); */ /* Remove a symbol from the symbol list */ + +/* See md_parse_option() for meanings of these options */ +static char norelax = 0; /* True if -norelax switch seen */ +static char instrument_branches = 0; /* True if -b switch seen */ + +/* Characters that always start a comment. + * If the pre-processor is disabled, these aren't very useful. + */ +char comment_chars[] = "#"; + +/* Characters that only start a comment at the beginning of + * a line. If the line seems to have the form '# 123 filename' + * .line and .file directives will appear in the pre-processed output. + * + * Note that input_file.c hand checks for '#' at the beginning of the + * first line of the input file. This is because the compiler outputs + * #NO_APP at the beginning of its output. + */ + +/* Also note that comments started like this one will always work. */ + +char line_comment_chars[] = ""; + +/* Chars that can be used to separate mant from exp in floating point nums */ +char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant, + * as in 0f12.456 or 0d1.2345e12 + */ +char FLT_CHARS[] = "fFdDtT"; + + +/* Table used by base assembler to relax addresses based on varying length + * instructions. The fields are: + * 1) most positive reach of this state, + * 2) most negative reach of this state, + * 3) how many bytes this mode will add to the size of the current frag + * 4) which index into the table to try if we can't fit into this one. + * + * For i80960, the only application is the (de-)optimization of cobr + * instructions into separate compare and branch instructions when a 13-bit + * displacement won't hack it. + */ +const relax_typeS +md_relax_table[] = { + {0, 0, 0,0}, /* State 0 => no more relaxation possible */ + {4088, -4096, 0,2}, /* State 1: conditional branch (cobr) */ + {0x800000-8,-0x800000,4,0}, /* State 2: compare (reg) & branch (ctrl) */ +}; + + +/* These are the machine dependent pseudo-ops. + * + * This table describes all the machine specific pseudo-ops the assembler + * has to support. The fields are: + * pseudo-op name without dot + * function to call to execute this pseudo-op + * integer arg to pass to the function + */ +#define S_LEAFPROC 1 +#define S_SYSPROC 2 + +const pseudo_typeS +md_pseudo_table[] = { + + { "bss", s_lcomm, 1 }, + { "extended", float_cons, 't' }, + { "leafproc", parse_po, S_LEAFPROC }, + { "sysproc", parse_po, S_SYSPROC }, + + { "word", cons, 4 }, + { "quad", big_cons, 16 }, + + { 0, 0, 0 } +}; + +/* Macros to extract info from an 'expressionS' structure 'e' */ +#define adds(e) e.X_add_symbol +#define subs(e) e.X_subtract_symbol +#define offs(e) e.X_add_number +#define segs(e) e.X_seg + + +/* Branch-prediction bits for CTRL/COBR format opcodes */ +#define BP_MASK 0x00000002 /* Mask for branch-prediction bit */ +#define BP_TAKEN 0x00000000 /* Value to OR in to predict branch */ +#define BP_NOT_TAKEN 0x00000002 /* Value to OR in to predict no branch */ + + +/* Some instruction opcodes that we need explicitly */ +#define BE 0x12000000 +#define BG 0x11000000 +#define BGE 0x13000000 +#define BL 0x14000000 +#define BLE 0x16000000 +#define BNE 0x15000000 +#define BNO 0x10000000 +#define BO 0x17000000 +#define CHKBIT 0x5a002700 +#define CMPI 0x5a002080 +#define CMPO 0x5a002000 + +#define B 0x08000000 +#define BAL 0x0b000000 +#define CALL 0x09000000 +#define CALLS 0x66003800 +#define RET 0x0a000000 + + +/* These masks are used to build up a set of MEMB mode bits. */ +#define A_BIT 0x0400 +#define I_BIT 0x0800 +#define MEMB_BIT 0x1000 +#define D_BIT 0x2000 + + +/* Mask for the only mode bit in a MEMA instruction (if set, abase reg is used) */ +#define MEMA_ABASE 0x2000 + +/* Info from which a MEMA or MEMB format instruction can be generated */ +typedef struct { + long opcode; /* (First) 32 bits of instruction */ + int disp; /* 0-(none), 12- or, 32-bit displacement needed */ + char *e; /* The expression in the source instruction from + * which the displacement should be determined + */ +} memS; + + +/* The two pieces of info we need to generate a register operand */ +struct regop { + int mode; /* 0 =>local/global/spec reg; 1=> literal or fp reg */ + int special; /* 0 =>not a sfr; 1=> is a sfr (not valid w/mode=0) */ + int n; /* Register number or literal value */ +}; + + +/* Number and assembler mnemonic for all registers that can appear in operands */ +static struct { + char *reg_name; + int reg_num; +} regnames[] = { + { "pfp", 0 }, { "sp", 1 }, { "rip", 2 }, { "r3", 3 }, + { "r4", 4 }, { "r5", 5 }, { "r6", 6 }, { "r7", 7 }, + { "r8", 8 }, { "r9", 9 }, { "r10", 10 }, { "r11", 11 }, + { "r12", 12 }, { "r13", 13 }, { "r14", 14 }, { "r15", 15 }, + { "g0", 16 }, { "g1", 17 }, { "g2", 18 }, { "g3", 19 }, + { "g4", 20 }, { "g5", 21 }, { "g6", 22 }, { "g7", 23 }, + { "g8", 24 }, { "g9", 25 }, { "g10", 26 }, { "g11", 27 }, + { "g12", 28 }, { "g13", 29 }, { "g14", 30 }, { "fp", 31 }, + + /* Numbers for special-function registers are for assembler internal + * use only: they are scaled back to range [0-31] for binary output. + */ +# define SF0 32 + + { "sf0", 32 }, { "sf1", 33 }, { "sf2", 34 }, { "sf3", 35 }, + { "sf4", 36 }, { "sf5", 37 }, { "sf6", 38 }, { "sf7", 39 }, + { "sf8", 40 }, { "sf9", 41 }, { "sf10",42 }, { "sf11",43 }, + { "sf12",44 }, { "sf13",45 }, { "sf14",46 }, { "sf15",47 }, + { "sf16",48 }, { "sf17",49 }, { "sf18",50 }, { "sf19",51 }, + { "sf20",52 }, { "sf21",53 }, { "sf22",54 }, { "sf23",55 }, + { "sf24",56 }, { "sf25",57 }, { "sf26",58 }, { "sf27",59 }, + { "sf28",60 }, { "sf29",61 }, { "sf30",62 }, { "sf31",63 }, + + /* Numbers for floating point registers are for assembler internal use + * only: they are scaled back to [0-3] for binary output. + */ +# define FP0 64 + + { "fp0", 64 }, { "fp1", 65 }, { "fp2", 66 }, { "fp3", 67 }, + + { NULL, 0 }, /* END OF LIST */ +}; + +#define IS_RG_REG(n) ((0 <= (n)) && ((n) < SF0)) +#define IS_SF_REG(n) ((SF0 <= (n)) && ((n) < FP0)) +#define IS_FP_REG(n) ((n) >= FP0) + +/* Number and assembler mnemonic for all registers that can appear as 'abase' + * (indirect addressing) registers. + */ +static struct { + char *areg_name; + int areg_num; +} aregs[] = { + { "(pfp)", 0 }, { "(sp)", 1 }, { "(rip)", 2 }, { "(r3)", 3 }, + { "(r4)", 4 }, { "(r5)", 5 }, { "(r6)", 6 }, { "(r7)", 7 }, + { "(r8)", 8 }, { "(r9)", 9 }, { "(r10)", 10 }, { "(r11)", 11 }, + { "(r12)", 12 }, { "(r13)", 13 }, { "(r14)", 14 }, { "(r15)", 15 }, + { "(g0)", 16 }, { "(g1)", 17 }, { "(g2)", 18 }, { "(g3)", 19 }, + { "(g4)", 20 }, { "(g5)", 21 }, { "(g6)", 22 }, { "(g7)", 23 }, + { "(g8)", 24 }, { "(g9)", 25 }, { "(g10)", 26 }, { "(g11)", 27 }, + { "(g12)", 28 }, { "(g13)", 29 }, { "(g14)", 30 }, { "(fp)", 31 }, + +# define IPREL 32 + /* for assembler internal use only: this number never appears in binary + * output. + */ + { "(ip)", IPREL }, + + { NULL, 0 }, /* END OF LIST */ +}; + + +/* Hash tables */ +static struct hash_control *op_hash = NULL; /* Opcode mnemonics */ +static struct hash_control *reg_hash = NULL; /* Register name hash table */ +static struct hash_control *areg_hash = NULL; /* Abase register hash table */ + + +/* Architecture for which we are assembling */ +#define ARCH_ANY 0 /* Default: no architecture checking done */ +#define ARCH_KA 1 +#define ARCH_KB 2 +#define ARCH_MC 3 +#define ARCH_CA 4 +int architecture = ARCH_ANY; /* Architecture requested on invocation line */ +int iclasses_seen = 0; /* OR of instruction classes (I_* constants) + * for which we've actually assembled + * instructions. + */ + + +/* BRANCH-PREDICTION INSTRUMENTATION + * + * The following supports generation of branch-prediction instrumentation + * (turned on by -b switch). The instrumentation collects counts + * of branches taken/not-taken for later input to a utility that will + * set the branch prediction bits of the instructions in accordance with + * the behavior observed. (Note that the KX series does not have + * brach-prediction.) + * + * The instrumentation consists of: + * + * (1) before and after each conditional branch, a call to an external + * routine that increments and steps over an inline counter. The + * counter itself, initialized to 0, immediately follows the call + * instruction. For each branch, the counter following the branch + * is the number of times the branch was not taken, and the difference + * between the counters is the number of times it was taken. An + * example of an instrumented conditional branch: + * + * call BR_CNT_FUNC + * .word 0 + * LBRANCH23: be label + * call BR_CNT_FUNC + * .word 0 + * + * (2) a table of pointers to the instrumented branches, so that an + * external postprocessing routine can locate all of the counters. + * the table begins with a 2-word header: a pointer to the next in + * a linked list of such tables (initialized to 0); and a count + * of the number of entries in the table (exclusive of the header. + * + * Note that input source code is expected to already contain calls + * an external routine that will link the branch local table into a + * list of such tables. + */ + +static int br_cnt = 0; /* Number of branches instrumented so far. + * Also used to generate unique local labels + * for each instrumented branch + */ + +#define BR_LABEL_BASE "LBRANCH" + /* Basename of local labels on instrumented + * branches, to avoid conflict with compiler- + * generated local labels. + */ + +#define BR_CNT_FUNC "__inc_branch" + /* Name of the external routine that will + * increment (and step over) an inline counter. + */ + +#define BR_TAB_NAME "__BRANCH_TABLE__" + /* Name of the table of pointers to branches. + * A local (i.e., non-external) symbol. + */ + +/***************************************************************************** + * md_begin: One-time initialization. + * + * Set up hash tables. + * + **************************************************************************** */ +void +md_begin() +{ + int i; /* Loop counter */ + const struct i960_opcode *oP; /* Pointer into opcode table */ + char *retval; /* Value returned by hash functions */ + + if (((op_hash = hash_new()) == 0) + || ((reg_hash = hash_new()) == 0) + || ((areg_hash = hash_new()) == 0)) { + as_fatal("virtual memory exceeded"); + } + + retval = ""; /* For some reason, the base assembler uses an empty + * string for "no error message", instead of a NULL + * pointer. + */ + + for (oP=i960_opcodes; oP->name && !*retval; oP++) { + retval = hash_insert(op_hash, oP->name, oP); + } + + for (i=0; regnames[i].reg_name && !*retval; i++) { + retval = hash_insert(reg_hash, regnames[i].reg_name, + ®names[i].reg_num); + } + + for (i=0; aregs[i].areg_name && !*retval; i++){ + retval = hash_insert(areg_hash, aregs[i].areg_name, + &aregs[i].areg_num); + } + + if (*retval) { + as_fatal("Hashing returned \"%s\".", retval); + } +} /* md_begin() */ + +/***************************************************************************** + * md_end: One-time final cleanup + * + * None necessary + * + **************************************************************************** */ +void +md_end() +{ +} + +/***************************************************************************** + * md_assemble: Assemble an instruction + * + * Assumptions about the passed-in text: + * - all comments, labels removed + * - text is an instruction + * - all white space compressed to single blanks + * - all character constants have been replaced with decimal + * + **************************************************************************** */ +void +md_assemble(textP) + char *textP; /* Source text of instruction */ +{ + char *args[4]; /* Parsed instruction text, containing NO whitespace: + * arg[0]->opcode mnemonic + * arg[1-3]->operands, with char constants + * replaced by decimal numbers + */ + int n_ops; /* Number of instruction operands */ + + struct i960_opcode *oP; + /* Pointer to instruction description */ + int branch_predict; + /* TRUE iff opcode mnemonic included branch-prediction + * suffix (".f" or ".t") + */ + long bp_bits; /* Setting of branch-prediction bit(s) to be OR'd + * into instruction opcode of CTRL/COBR format + * instructions. + */ + int n; /* Offset of last character in opcode mnemonic */ + + static const char bp_error_msg[] = "branch prediction invalid on this opcode"; + + + /* Parse instruction into opcode and operands */ + bzero(args, sizeof(args)); + n_ops = i_scan(textP, args); + if (n_ops == -1){ + return; /* Error message already issued */ + } + + /* Do "macro substitution" (sort of) on 'ldconst' pseudo-instruction */ + if (!strcmp(args[0],"ldconst")){ + n_ops = parse_ldconst(args); + if (n_ops == -1){ + return; + } + } + + /* Check for branch-prediction suffix on opcode mnemonic, strip it off */ + n = strlen(args[0]) - 1; + branch_predict = 0; + bp_bits = 0; + if (args[0][n-1] == '.' && (args[0][n] == 't' || args[0][n] == 'f')){ + /* We could check here to see if the target architecture + * supports branch prediction, but why bother? The bit + * will just be ignored by processors that don't use it. + */ + branch_predict = 1; + bp_bits = (args[0][n] == 't') ? BP_TAKEN : BP_NOT_TAKEN; + args[0][n-1] = '\0'; /* Strip suffix from opcode mnemonic */ + } + + /* Look up opcode mnemonic in table and check number of operands. + * Check that opcode is legal for the target architecture. + * If all looks good, assemble instruction. + */ + oP = (struct i960_opcode *) hash_find(op_hash, args[0]); + if (!oP || !targ_has_iclass(oP->iclass)) { + as_bad("invalid opcode, \"%s\".", args[0]); + + } else if (n_ops != oP->num_ops) { + as_bad("improper number of operands. expecting %d, got %d", oP->num_ops, n_ops); + + } else { + switch (oP->format){ + case FBRA: + case CTRL: + ctrl_fmt(args[1], oP->opcode | bp_bits, oP->num_ops); + if (oP->format == FBRA){ + /* Now generate a 'bno' to same arg */ + ctrl_fmt(args[1], BNO | bp_bits, 1); + } + break; + case COBR: + case COJ: + cobr_fmt(args, oP->opcode | bp_bits, oP); + break; + case REG: + if (branch_predict){ + as_warn(bp_error_msg); + } + reg_fmt(args, oP); + break; + case MEM1: + case MEM2: + case MEM4: + case MEM8: + case MEM12: + case MEM16: + if (branch_predict){ + as_warn(bp_error_msg); + } + mem_fmt(args, oP); + break; + case CALLJ: + if (branch_predict){ + as_warn(bp_error_msg); + } + /* Output opcode & set up "fixup" (relocation); + * flag relocation as 'callj' type. + */ + know(oP->num_ops == 1); + get_cdisp(args[1], "CTRL", oP->opcode, 24, 0, 1); + break; + default: + BAD_CASE(oP->format); + break; + } + } +} /* md_assemble() */ + +/***************************************************************************** + * md_number_to_chars: convert a number to target byte order + * + **************************************************************************** */ +void +md_number_to_chars(buf, value, n) + char *buf; /* Put output here */ + long value; /* The integer to be converted */ + int n; /* Number of bytes to output (significant bytes + * in 'value') + */ +{ + while (n--){ + *buf++ = value; + value >>= 8; + } + + /* XXX line number probably botched for this warning message. */ + if (value != 0 && value != -1){ + as_bad("Displacement too long for instruction field length."); + } +} /* md_number_to_chars() */ + +/***************************************************************************** + * md_chars_to_number: convert from target byte order to host byte order. + * + **************************************************************************** */ +int +md_chars_to_number(val, n) + unsigned char *val; /* Value in target byte order */ + int n; /* Number of bytes in the input */ +{ + int retval; + + for (retval=0; n--;){ + retval <<= 8; + retval |= val[n]; + } + return retval; +} + + +#define MAX_LITTLENUMS 6 +#define LNUM_SIZE sizeof(LITTLENUM_TYPE) + +/***************************************************************************** + * md_atof: convert ascii to floating point + * + * Turn a string at input_line_pointer into a floating point constant of type + * 'type', and store the appropriate bytes at *litP. The number of LITTLENUMS + * emitted is returned at 'sizeP'. An error message is returned, or a pointer + * to an empty message if OK. + * + * Note we call the i386 floating point routine, rather than complicating + * things with more files or symbolic links. + * + **************************************************************************** */ +char * md_atof(type, litP, sizeP) +int type; +char *litP; +int *sizeP; +{ + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + int prec; + char *t; + char *atof_ieee(); + + switch(type) { + case 'f': + case 'F': + prec = 2; + break; + + case 'd': + case 'D': + prec = 4; + break; + + case 't': + case 'T': + prec = 5; + type = 'x'; /* That's what atof_ieee() understands */ + break; + + default: + *sizeP=0; + return "Bad call to md_atof()"; + } + + t = atof_ieee(input_line_pointer, type, words); + if (t){ + input_line_pointer = t; + } + + *sizeP = prec * LNUM_SIZE; + + /* Output the LITTLENUMs in REVERSE order in accord with i80960 + * word-order. (Dunno why atof_ieee doesn't do it in the right + * order in the first place -- probably because it's a hack of + * atof_m68k.) + */ + + for(wordP = words + prec - 1; prec--;){ + md_number_to_chars(litP, (long) (*wordP--), LNUM_SIZE); + litP += sizeof(LITTLENUM_TYPE); + } + + return ""; /* Someone should teach Dean about null pointers */ +} + + +/***************************************************************************** + * md_number_to_imm + * + **************************************************************************** */ +void +md_number_to_imm(buf, val, n) + char *buf; + long val; + int n; +{ + md_number_to_chars(buf, val, n); +} + + +/***************************************************************************** + * md_number_to_disp + * + **************************************************************************** */ +void +md_number_to_disp(buf, val, n) + char *buf; + long val; + int n; +{ + md_number_to_chars(buf, val, n); +} + +/***************************************************************************** + * md_number_to_field: + * + * Stick a value (an address fixup) into a bit field of + * previously-generated instruction. + * + **************************************************************************** */ +void +md_number_to_field(instrP, val, bfixP) + char *instrP; /* Pointer to instruction to be fixed */ + long val; /* Address fixup value */ + bit_fixS *bfixP; /* Description of bit field to be fixed up */ +{ + int numbits; /* Length of bit field to be fixed */ + long instr; /* 32-bit instruction to be fixed-up */ + long sign; /* 0 or -1, according to sign bit of 'val' */ + + /* Convert instruction back to host byte order + */ + instr = md_chars_to_number(instrP, 4); + + /* Surprise! -- we stored the number of bits + * to be modified rather than a pointer to a structure. + */ + numbits = (int)bfixP; + if (numbits == 1){ + /* This is a no-op, stuck here by reloc_callj() */ + return; + } + + know ((numbits==13) || (numbits==24)); + + /* Propagate sign bit of 'val' for the given number of bits. + * Result should be all 0 or all 1 + */ + sign = val >> ((int)numbits - 1); + if (((val < 0) && (sign != -1)) + || ((val > 0) && (sign != 0))){ + as_bad("Fixup of %d too large for field width of %d", + val, numbits); + } else { + /* Put bit field into instruction and write back in target + * byte order. + */ + val &= ~(-1 << (int)numbits); /* Clear unused sign bits */ + instr |= val; + md_number_to_chars(instrP, instr, 4); + } +} /* md_number_to_field() */ + + +/***************************************************************************** + * md_parse_option + * Invocation line includes a switch not recognized by the base assembler. + * See if it's a processor-specific option. For the 960, these are: + * + * -norelax: + * Conditional branch instructions that require displacements + * greater than 13 bits (or that have external targets) should + * generate errors. The default is to replace each such + * instruction with the corresponding compare (or chkbit) and + * branch instructions. Note that the Intel "j" cobr directives + * are ALWAYS "de-optimized" in this way when necessary, + * regardless of the setting of this option. + * + * -b: + * Add code to collect information about branches taken, for + * later optimization of branch prediction bits by a separate + * tool. COBR and CNTL format instructions have branch + * prediction bits (in the CX architecture); if "BR" represents + * an instruction in one of these classes, the following rep- + * resents the code generated by the assembler: + * + * call <increment routine> + * .word 0 # pre-counter + * Label: BR + * call <increment routine> + * .word 0 # post-counter + * + * A table of all such "Labels" is also generated. + * + * + * -AKA, -AKB, -AKC, -ASA, -ASB, -AMC, -ACA: + * Select the 80960 architecture. Instructions or features not + * supported by the selected architecture cause fatal errors. + * The default is to generate code for any instruction or feature + * that is supported by SOME version of the 960 (even if this + * means mixing architectures!). + * + **************************************************************************** */ +int +md_parse_option(argP, cntP, vecP) + char **argP; + int *cntP; + char ***vecP; +{ + char *p; + struct tabentry { char *flag; int arch; }; + static struct tabentry arch_tab[] = { + "KA", ARCH_KA, + "KB", ARCH_KB, + "SA", ARCH_KA, /* Synonym for KA */ + "SB", ARCH_KB, /* Synonym for KB */ + "KC", ARCH_MC, /* Synonym for MC */ + "MC", ARCH_MC, + "CA", ARCH_CA, + NULL, 0 + }; + struct tabentry *tp; + + if (!strcmp(*argP,"norelax")){ + norelax = 1; + + } else if (**argP == 'b'){ + instrument_branches = 1; + + } else if (**argP == 'A'){ + p = (*argP) + 1; + + for (tp = arch_tab; tp->flag != NULL; tp++){ + if (!strcmp(p,tp->flag)){ + break; + } + } + + if (tp->flag == NULL){ + as_bad("unknown architecture: %s", p); + } else { + architecture = tp->arch; + } + } else { + /* Unknown option */ + (*argP)++; + return 0; + } + **argP = '\0'; /* Done parsing this switch */ + return 1; +} + +/***************************************************************************** + * md_convert_frag: + * Called by base assembler after address relaxation is finished: modify + * variable fragments according to how much relaxation was done. + * + * If the fragment substate is still 1, a 13-bit displacement was enough + * to reach the symbol in question. Set up an address fixup, but otherwise + * leave the cobr instruction alone. + * + * If the fragment substate is 2, a 13-bit displacement was not enough. + * Replace the cobr with a two instructions (a compare and a branch). + * + **************************************************************************** */ +void +md_convert_frag(fragP) + fragS * fragP; +{ + fixS *fixP; /* Structure describing needed address fix */ + + switch (fragP->fr_subtype){ + case 1: + /* LEAVE SINGLE COBR INSTRUCTION */ + fixP = fix_new(fragP, + fragP->fr_opcode-fragP->fr_literal, + 4, + fragP->fr_symbol, + 0, + fragP->fr_offset, + 1, + 0); + + fixP->fx_bit_fixP = (bit_fixS *) 13; /* size of bit field */ + break; + case 2: + /* REPLACE COBR WITH COMPARE/BRANCH INSTRUCTIONS */ + relax_cobr(fragP); + break; + default: + BAD_CASE(fragP->fr_subtype); + break; + } +} + +/***************************************************************************** + * md_estimate_size_before_relax: How much does it look like *fragP will grow? + * + * Called by base assembler just before address relaxation. + * Return the amount by which the fragment will grow. + * + * Any symbol that is now undefined will not become defined; cobr's + * based on undefined symbols will have to be replaced with a compare + * instruction and a branch instruction, and the code fragment will grow + * by 4 bytes. + * + **************************************************************************** */ +int +md_estimate_size_before_relax(fragP, segment_type) + register fragS *fragP; + register segT segment_type; +{ + /* If symbol is undefined in this segment, go to "relaxed" state + * (compare and branch instructions instead of cobr) right now. + */ + if (S_GET_SEGMENT(fragP->fr_symbol) != segment_type) { + relax_cobr(fragP); + return 4; + } + return 0; +} /* md_estimate_size_before_relax() */ + + +/***************************************************************************** + * md_ri_to_chars: + * This routine exists in order to overcome machine byte-order problems + * when dealing with bit-field entries in the relocation_info struct. + * + * But relocation info will be used on the host machine only (only + * executable code is actually downloaded to the i80960). Therefore, + * we leave it in host byte order. + * + **************************************************************************** */ +void md_ri_to_chars(the_bytes, ri) +char *the_bytes; +struct reloc_info_generic *ri; +{ + struct relocation_info br; + + (void) bzero(&br, sizeof(br)); + + br.r_address = ri->r_address; + br.r_index = ri->r_index; + br.r_pcrel = ri->r_pcrel; + br.r_length = ri->r_length; + br.r_extern = ri->r_extern; + br.r_bsr = ri->r_bsr; + br.r_disp = ri->r_disp; + br.r_callj = ri->r_callj; + + *((struct relocation_info *) the_bytes) = br; +} /* md_ri_to_chars() */ + + +#ifndef WORKING_DOT_WORD + +int md_short_jump_size = 0; +int md_long_jump_size = 0; + +void md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol) +char *ptr; +long from_addr; +long to_addr; +fragS *frag; +symbolS *to_symbol; +{ + abort(); +} + +void +md_create_long_jump(ptr,from_addr,to_addr,frag,to_symbol) + char *ptr; + long from_addr, to_addr; + fragS *frag; + symbolS *to_symbol; +{ + abort(); +} +#endif + + /************************************************************* + * * + * FOLLOWING ARE THE LOCAL ROUTINES, IN ALPHABETICAL ORDER * + * * + ************************************************************ */ + + + +/***************************************************************************** + * brcnt_emit: Emit code to increment inline branch counter. + * + * See the comments above the declaration of 'br_cnt' for details on + * branch-prediction instrumentation. + **************************************************************************** */ +static void +brcnt_emit() +{ + ctrl_fmt(BR_CNT_FUNC,CALL,1);/* Emit call to "increment" routine */ + emit(0); /* Emit inline counter to be incremented */ +} + +/***************************************************************************** + * brlab_next: generate the next branch local label + * + * See the comments above the declaration of 'br_cnt' for details on + * branch-prediction instrumentation. + **************************************************************************** */ +static char * +brlab_next() +{ + static char buf[20]; + + sprintf(buf, "%s%d", BR_LABEL_BASE, br_cnt++); + return buf; +} + +/***************************************************************************** + * brtab_emit: generate the fetch-prediction branch table. + * + * See the comments above the declaration of 'br_cnt' for details on + * branch-prediction instrumentation. + * + * The code emitted here would be functionally equivalent to the following + * example assembler source. + * + * .data + * .align 2 + * BR_TAB_NAME: + * .word 0 # link to next table + * .word 3 # length of table + * .word LBRANCH0 # 1st entry in table proper + * .word LBRANCH1 + * .word LBRANCH2 + ***************************************************************************** */ +void +brtab_emit() +{ + int i; + char buf[20]; + char *p; /* Where the binary was output to */ + fixS *fixP; /*->description of deferred address fixup */ + + if (!instrument_branches){ + return; + } + + subseg_new(SEG_DATA,0); /* .data */ + frag_align(2,0); /* .align 2 */ + record_alignment(now_seg,2); + colon(BR_TAB_NAME); /* BR_TAB_NAME: */ + emit(0); /* .word 0 #link to next table */ + emit(br_cnt); /* .word n #length of table */ + + for (i=0; i<br_cnt; i++){ + sprintf(buf, "%s%d", BR_LABEL_BASE, i); + p = emit(0); + fixP = fix_new(frag_now, + p - frag_now->fr_literal, + 4, + symbol_find(buf), + 0, + 0, + 0, + 0); + fixP->fx_im_disp = 2; /* 32-bit displacement fix */ + } +} + +/***************************************************************************** + * cobr_fmt: generate a COBR-format instruction + * + **************************************************************************** */ +static +void +cobr_fmt(arg, opcode, oP) + char *arg[]; /* arg[0]->opcode mnemonic, arg[1-3]->operands (ascii) */ + long opcode; /* Opcode, with branch-prediction bits already set + * if necessary. + */ + struct i960_opcode *oP; + /*->description of instruction */ +{ + long instr; /* 32-bit instruction */ + struct regop regop; /* Description of register operand */ + int n; /* Number of operands */ + int var_frag; /* 1 if varying length code fragment should + * be emitted; 0 if an address fix + * should be emitted. + */ + + instr = opcode; + n = oP->num_ops; + + if (n >= 1) { + /* First operand (if any) of a COBR is always a register + * operand. Parse it. + */ + parse_regop(®op, arg[1], oP->operand[0]); + instr |= (regop.n << 19) | (regop.mode << 13); + } + if (n >= 2) { + /* Second operand (if any) of a COBR is always a register + * operand. Parse it. + */ + parse_regop(®op, arg[2], oP->operand[1]); + instr |= (regop.n << 14) | regop.special; + } + + + if (n < 3){ + emit(instr); + + } else { + if (instrument_branches){ + brcnt_emit(); + colon(brlab_next()); + } + + /* A third operand to a COBR is always a displacement. + * Parse it; if it's relaxable (a cobr "j" directive, or any + * cobr other than bbs/bbc when the "-norelax" option is not in + * use) set up a variable code fragment; otherwise set up an + * address fix. + */ + var_frag = !norelax || (oP->format == COJ); /* TRUE or FALSE */ + get_cdisp(arg[3], "COBR", instr, 13, var_frag, 0); + + if (instrument_branches){ + brcnt_emit(); + } + } +} /* cobr_fmt() */ + + +/***************************************************************************** + * ctrl_fmt: generate a CTRL-format instruction + * + **************************************************************************** */ +static +void +ctrl_fmt(targP, opcode, num_ops) + char *targP; /* Pointer to text of lone operand (if any) */ + long opcode; /* Template of instruction */ + int num_ops; /* Number of operands */ +{ + int instrument; /* TRUE iff we should add instrumentation to track + * how often the branch is taken + */ + + + if (num_ops == 0){ + emit(opcode); /* Output opcode */ + } else { + + instrument = instrument_branches && (opcode!=CALL) + && (opcode!=B) && (opcode!=RET) && (opcode!=BAL); + + if (instrument){ + brcnt_emit(); + colon(brlab_next()); + } + + /* The operand MUST be an ip-relative displacment. Parse it + * and set up address fix for the instruction we just output. + */ + get_cdisp(targP, "CTRL", opcode, 24, 0, 0); + + if (instrument){ + brcnt_emit(); + } + } + +} + + +/***************************************************************************** + * emit: output instruction binary + * + * Output instruction binary, in target byte order, 4 bytes at a time. + * Return pointer to where it was placed. + * + **************************************************************************** */ +static +char * +emit(instr) + long instr; /* Word to be output, host byte order */ +{ + char *toP; /* Where to output it */ + + toP = frag_more(4); /* Allocate storage */ + md_number_to_chars(toP, instr, 4); /* Convert to target byte order */ + return toP; +} + + +/***************************************************************************** + * get_args: break individual arguments out of comma-separated list + * + * Input assumptions: + * - all comments and labels have been removed + * - all strings of whitespace have been collapsed to a single blank. + * - all character constants ('x') have been replaced with decimal + * + * Output: + * args[0] is untouched. args[1] points to first operand, etc. All args: + * - are NULL-terminated + * - contain no whitespace + * + * Return value: + * Number of operands (0,1,2, or 3) or -1 on error. + * + **************************************************************************** */ +static int get_args(p, args) + register char *p; /* Pointer to comma-separated operands; MUCKED BY US */ + char *args[]; /* Output arg: pointers to operands placed in args[1-3]. + * MUST ACCOMMODATE 4 ENTRIES (args[0-3]). + */ +{ + register int n; /* Number of operands */ + register char *to; +/* char buf[4]; */ +/* int len; */ + + + /* Skip lead white space */ + while (*p == ' '){ + p++; + } + + if (*p == '\0'){ + return 0; + } + + n = 1; + args[1] = p; + + /* Squeze blanks out by moving non-blanks toward start of string. + * Isolate operands, whenever comma is found. + */ + to = p; + while (*p != '\0'){ + + if (*p == ' '){ + p++; + + } else if (*p == ','){ + + /* Start of operand */ + if (n == 3){ + as_bad("too many operands"); + return -1; + } + *to++ = '\0'; /* Terminate argument */ + args[++n] = to; /* Start next argument */ + p++; + + } else { + *to++ = *p++; + } + } + *to = '\0'; + return n; +} + + +/***************************************************************************** + * get_cdisp: handle displacement for a COBR or CTRL instruction. + * + * Parse displacement for a COBR or CTRL instruction. + * + * If successful, output the instruction opcode and set up for it, + * depending on the arg 'var_frag', either: + * o an address fixup to be done when all symbol values are known, or + * o a varying length code fragment, with address fixup info. This + * will be done for cobr instructions that may have to be relaxed + * in to compare/branch instructions (8 bytes) if the final address + * displacement is greater than 13 bits. + * + **************************************************************************** */ +static +void +get_cdisp(dispP, ifmtP, instr, numbits, var_frag, callj) + char *dispP; /*->displacement as specified in source instruction */ + char *ifmtP; /*->"COBR" or "CTRL" (for use in error message) */ + long instr; /* Instruction needing the displacement */ + int numbits; /* # bits of displacement (13 for COBR, 24 for CTRL) */ + int var_frag; /* 1 if varying length code fragment should be emitted; + * 0 if an address fix should be emitted. + */ + int callj; /* 1 if callj relocation should be done; else 0 */ +{ + expressionS e; /* Parsed expression */ + fixS *fixP; /* Structure describing needed address fix */ + char *outP; /* Where instruction binary is output to */ + + fixP = NULL; + + switch (parse_expr(dispP,&e)) { + + case SEG_GOOF: + as_bad("expression syntax error"); + break; + + case SEG_TEXT: + case SEG_UNKNOWN: + if (var_frag) { + outP = frag_more(8); /* Allocate worst-case storage */ + md_number_to_chars(outP, instr, 4); + frag_variant(rs_machine_dependent, 4, 4, 1, + adds(e), offs(e), outP, 0, 0); + } else { + /* Set up a new fix structure, so address can be updated + * when all symbol values are known. + */ + outP = emit(instr); + fixP = fix_new(frag_now, + outP - frag_now->fr_literal, + 4, + adds(e), + 0, + offs(e), + 1, + 0); + + fixP->fx_callj = callj; + + /* We want to modify a bit field when the address is + * known. But we don't need all the garbage in the + * bit_fix structure. So we're going to lie and store + * the number of bits affected instead of a pointer. + */ + fixP->fx_bit_fixP = (bit_fixS *) numbits; + } + break; + + case SEG_DATA: + case SEG_BSS: + as_bad("attempt to branch into different segment"); + break; + + default: + as_bad("target of %s instruction must be a label", ifmtP); + break; + } +} + + +/***************************************************************************** + * get_ispec: parse a memory operand for an index specification + * + * Here, an "index specification" is taken to be anything surrounded + * by square brackets and NOT followed by anything else. + * + * If it's found, detach it from the input string, remove the surrounding + * square brackets, and return a pointer to it. Otherwise, return NULL. + * + **************************************************************************** */ +static +char * +get_ispec(textP) + char *textP; /*->memory operand from source instruction, no white space */ +{ + char *start; /*->start of index specification */ + char *end; /*->end of index specification */ + + /* Find opening square bracket, if any + */ + start = index(textP, '['); + + if (start != NULL){ + + /* Eliminate '[', detach from rest of operand */ + *start++ = '\0'; + + end = index(start, ']'); + + if (end == NULL){ + as_bad("unmatched '['"); + + } else { + /* Eliminate ']' and make sure it was the last thing + * in the string. + */ + *end = '\0'; + if (*(end+1) != '\0'){ + as_bad("garbage after index spec ignored"); + } + } + } + return start; +} + +/***************************************************************************** + * get_regnum: + * + * Look up a (suspected) register name in the register table and return the + * associated register number (or -1 if not found). + * + **************************************************************************** */ +static +int +get_regnum(regname) + char *regname; /* Suspected register name */ +{ + int *rP; + + rP = (int *) hash_find(reg_hash, regname); + return (rP == NULL) ? -1 : *rP; +} + + +/***************************************************************************** + * i_scan: perform lexical scan of ascii assembler instruction. + * + * Input assumptions: + * - input string is an i80960 instruction (not a pseudo-op) + * - all comments and labels have been removed + * - all strings of whitespace have been collapsed to a single blank. + * + * Output: + * args[0] points to opcode, other entries point to operands. All strings: + * - are NULL-terminated + * - contain no whitespace + * - have character constants ('x') replaced with a decimal number + * + * Return value: + * Number of operands (0,1,2, or 3) or -1 on error. + * + **************************************************************************** */ +static int i_scan(iP, args) + register char *iP; /* Pointer to ascii instruction; MUCKED BY US. */ + char *args[]; /* Output arg: pointers to opcode and operands placed + * here. MUST ACCOMMODATE 4 ENTRIES. + */ +{ + + /* Isolate opcode */ + if (*(iP) == ' ') { + iP++; + } /* Skip lead space, if any */ + args[0] = iP; + for (; *iP != ' '; iP++) { + if (*iP == '\0') { + /* There are no operands */ + if (args[0] == iP) { + /* We never moved: there was no opcode either! */ + as_bad("missing opcode"); + return -1; + } + return 0; + } + } + *iP++ = '\0'; /* Terminate opcode */ + return(get_args(iP, args)); +} /* i_scan() */ + + +/***************************************************************************** + * mem_fmt: generate a MEMA- or MEMB-format instruction + * + **************************************************************************** */ +static void mem_fmt(args, oP) + char *args[]; /* args[0]->opcode mnemonic, args[1-3]->operands */ + struct i960_opcode *oP; /* Pointer to description of instruction */ +{ + int i; /* Loop counter */ + struct regop regop; /* Description of register operand */ + char opdesc; /* Operand descriptor byte */ + memS instr; /* Description of binary to be output */ + char *outP; /* Where the binary was output to */ + expressionS expr; /* Parsed expression */ + fixS *fixP; /*->description of deferred address fixup */ + + bzero(&instr, sizeof(memS)); + instr.opcode = oP->opcode; + + /* Process operands. */ + for (i = 1; i <= oP->num_ops; i++){ + opdesc = oP->operand[i-1]; + + if (MEMOP(opdesc)){ + parse_memop(&instr, args[i], oP->format); + } else { + parse_regop(®op, args[i], opdesc); + instr.opcode |= regop.n << 19; + } + } + + /* Output opcode */ + outP = emit(instr.opcode); + + if (instr.disp == 0){ + return; + } + + /* Parse and process the displacement */ + switch (parse_expr(instr.e,&expr)){ + + case SEG_GOOF: + as_bad("expression syntax error"); + break; + + case SEG_ABSOLUTE: + if (instr.disp == 32){ + (void) emit(offs(expr)); /* Output displacement */ + } else { + /* 12-bit displacement */ + if (offs(expr) & ~0xfff){ + /* Won't fit in 12 bits: convert already-output + * instruction to MEMB format, output + * displacement. + */ + mema_to_memb(outP); + (void) emit(offs(expr)); + } else { + /* WILL fit in 12 bits: OR into opcode and + * overwrite the binary we already put out + */ + instr.opcode |= offs(expr); + md_number_to_chars(outP, instr.opcode, 4); + } + } + break; + + case SEG_DIFFERENCE: + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_UNKNOWN: + if (instr.disp == 12){ + /* Displacement is dependent on a symbol, whose value + * may change at link time. We HAVE to reserve 32 bits. + * Convert already-output opcode to MEMB format. + */ + mema_to_memb(outP); + } + + /* Output 0 displacement and set up address fixup for when + * this symbol's value becomes known. + */ + outP = emit((long) 0); + fixP = fix_new(frag_now, + outP - frag_now->fr_literal, + 4, + adds(expr), + subs(expr), + offs(expr), + 0, + 0); + fixP->fx_im_disp = 2; /* 32-bit displacement fix */ + break; + + default: + BAD_CASE(segs(expr)); + break; + } +} /* memfmt() */ + + +/***************************************************************************** + * mema_to_memb: convert a MEMA-format opcode to a MEMB-format opcode. + * + * There are 2 possible MEMA formats: + * - displacement only + * - displacement + abase + * + * They are distinguished by the setting of the MEMA_ABASE bit. + * + **************************************************************************** */ +static void mema_to_memb(opcodeP) + char *opcodeP; /* Where to find the opcode, in target byte order */ +{ + long opcode; /* Opcode in host byte order */ + long mode; /* Mode bits for MEMB instruction */ + + opcode = md_chars_to_number(opcodeP, 4); + know(!(opcode & MEMB_BIT)); + + mode = MEMB_BIT | D_BIT; + if (opcode & MEMA_ABASE){ + mode |= A_BIT; + } + + opcode &= 0xffffc000; /* Clear MEMA offset and mode bits */ + opcode |= mode; /* Set MEMB mode bits */ + + md_number_to_chars(opcodeP, opcode, 4); +} /* mema_to_memb() */ + + +/***************************************************************************** + * parse_expr: parse an expression + * + * Use base assembler's expression parser to parse an expression. + * It, unfortunately, runs off a global which we have to save/restore + * in order to make it work for us. + * + * An empty expression string is treated as an absolute 0. + * + * Return "segment" to which the expression evaluates. + * Return SEG_GOOF regardless of expression evaluation if entire input + * string is not consumed in the evaluation -- tolerate no dangling junk! + * + **************************************************************************** */ +static +segT +parse_expr(textP, expP) + char *textP; /* Text of expression to be parsed */ + expressionS *expP; /* Where to put the results of parsing */ +{ + char *save_in; /* Save global here */ + segT seg; /* Segment to which expression evaluates */ + symbolS *symP; + + know(textP); + + if (*textP == '\0') { + /* Treat empty string as absolute 0 */ + expP->X_add_symbol = expP->X_subtract_symbol = NULL; + expP->X_add_number = 0; + seg = expP->X_seg = SEG_ABSOLUTE; + + } else { + save_in = input_line_pointer; /* Save global */ + input_line_pointer = textP; /* Make parser work for us */ + + seg = expression(expP); + if (input_line_pointer - textP != strlen(textP)) { + /* Did not consume all of the input */ + seg = SEG_GOOF; + } + symP = expP->X_add_symbol; + if (symP && (hash_find(reg_hash, S_GET_NAME(symP)))) { + /* Register name in an expression */ + seg = SEG_GOOF; + } + + input_line_pointer = save_in; /* Restore global */ + } + return seg; +} + + +/***************************************************************************** + * parse_ldcont: + * Parse and replace a 'ldconst' pseudo-instruction with an appropriate + * i80960 instruction. + * + * Assumes the input consists of: + * arg[0] opcode mnemonic ('ldconst') + * arg[1] first operand (constant) + * arg[2] name of register to be loaded + * + * Replaces opcode and/or operands as appropriate. + * + * Returns the new number of arguments, or -1 on failure. + * + **************************************************************************** */ +static +int +parse_ldconst(arg) + char *arg[]; /* See above */ +{ + int n; /* Constant to be loaded */ + int shift; /* Shift count for "shlo" instruction */ + static char buf[5]; /* Literal for first operand */ + static char buf2[5]; /* Literal for second operand */ + expressionS e; /* Parsed expression */ + + + arg[3] = NULL; /* So we can tell at the end if it got used or not */ + + switch(parse_expr(arg[1],&e)){ + + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_UNKNOWN: + case SEG_DIFFERENCE: + /* We're dependent on one or more symbols -- use "lda" */ + arg[0] = "lda"; + break; + + case SEG_ABSOLUTE: + /* Try the following mappings: + * ldconst 0,<reg> ->mov 0,<reg> + * ldconst 31,<reg> ->mov 31,<reg> + * ldconst 32,<reg> ->addo 1,31,<reg> + * ldconst 62,<reg> ->addo 31,31,<reg> + * ldconst 64,<reg> ->shlo 8,3,<reg> + * ldconst -1,<reg> ->subo 1,0,<reg> + * ldconst -31,<reg>->subo 31,0,<reg> + * + * anthing else becomes: + * lda xxx,<reg> + */ + n = offs(e); + if ((0 <= n) && (n <= 31)){ + arg[0] = "mov"; + + } else if ((-31 <= n) && (n <= -1)){ + arg[0] = "subo"; + arg[3] = arg[2]; + sprintf(buf, "%d", -n); + arg[1] = buf; + arg[2] = "0"; + + } else if ((32 <= n) && (n <= 62)){ + arg[0] = "addo"; + arg[3] = arg[2]; + arg[1] = "31"; + sprintf(buf, "%d", n-31); + arg[2] = buf; + + } else if ((shift = shift_ok(n)) != 0){ + arg[0] = "shlo"; + arg[3] = arg[2]; + sprintf(buf, "%d", shift); + arg[1] = buf; + sprintf(buf2, "%d", n >> shift); + arg[2] = buf2; + + } else { + arg[0] = "lda"; + } + break; + + default: + as_bad("invalid constant"); + return -1; + break; + } + return (arg[3] == 0) ? 2: 3; +} + +/***************************************************************************** + * parse_memop: parse a memory operand + * + * This routine is based on the observation that the 4 mode bits of the + * MEMB format, taken individually, have fairly consistent meaning: + * + * M3 (bit 13): 1 if displacement is present (D_BIT) + * M2 (bit 12): 1 for MEMB instructions (MEMB_BIT) + * M1 (bit 11): 1 if index is present (I_BIT) + * M0 (bit 10): 1 if abase is present (A_BIT) + * + * So we parse the memory operand and set bits in the mode as we find + * things. Then at the end, if we go to MEMB format, we need only set + * the MEMB bit (M2) and our mode is built for us. + * + * Unfortunately, I said "fairly consistent". The exceptions: + * + * DBIA + * 0100 Would seem illegal, but means "abase-only". + * + * 0101 Would seem to mean "abase-only" -- it means IP-relative. + * Must be converted to 0100. + * + * 0110 Would seem to mean "index-only", but is reserved. + * We turn on the D bit and provide a 0 displacement. + * + * The other thing to observe is that we parse from the right, peeling + * things * off as we go: first any index spec, then any abase, then + * the displacement. + * + **************************************************************************** */ +static +void +parse_memop(memP, argP, optype) + memS *memP; /* Where to put the results */ + char *argP; /* Text of the operand to be parsed */ + int optype; /* MEM1, MEM2, MEM4, MEM8, MEM12, or MEM16 */ +{ + char *indexP; /* Pointer to index specification with "[]" removed */ + char *p; /* Temp char pointer */ + char iprel_flag;/* True if this is an IP-relative operand */ + int regnum; /* Register number */ + int scale; /* Scale factor: 1,2,4,8, or 16. Later converted + * to internal format (0,1,2,3,4 respectively). + */ + int mode; /* MEMB mode bits */ + int *intP; /* Pointer to register number */ + + /* The following table contains the default scale factors for each + * type of memory instruction. It is accessed using (optype-MEM1) + * as an index -- thus it assumes the 'optype' constants are assigned + * consecutive values, in the order they appear in this table + */ + static int def_scale[] = { + 1, /* MEM1 */ + 2, /* MEM2 */ + 4, /* MEM4 */ + 8, /* MEM8 */ + -1, /* MEM12 -- no valid default */ + 16 /* MEM16 */ + }; + + + iprel_flag = mode = 0; + + /* Any index present? */ + indexP = get_ispec(argP); + if (indexP) { + p = strchr(indexP, '*'); + if (p == NULL) { + /* No explicit scale -- use default for this + *instruction type. + */ + scale = def_scale[ optype - MEM1 ]; + } else { + *p++ = '\0'; /* Eliminate '*' */ + + /* Now indexP->a '\0'-terminated register name, + * and p->a scale factor. + */ + + if (!strcmp(p,"16")){ + scale = 16; + } else if (strchr("1248",*p) && (p[1] == '\0')){ + scale = *p - '0'; + } else { + scale = -1; + } + } + + regnum = get_regnum(indexP); /* Get index reg. # */ + if (!IS_RG_REG(regnum)){ + as_bad("invalid index register"); + return; + } + + /* Convert scale to its binary encoding */ + switch (scale){ + case 1: scale = 0 << 7; break; + case 2: scale = 1 << 7; break; + case 4: scale = 2 << 7; break; + case 8: scale = 3 << 7; break; + case 16: scale = 4 << 7; break; + default: as_bad("invalid scale factor"); return; + }; + + memP->opcode |= scale | regnum; /* Set index bits in opcode */ + mode |= I_BIT; /* Found a valid index spec */ + } + + /* Any abase (Register Indirect) specification present? */ + if ((p = strrchr(argP,'(')) != NULL) { + /* "(" is there -- does it start a legal abase spec? + * (If not it could be part of a displacement expression.) + */ + intP = (int *) hash_find(areg_hash, p); + if (intP != NULL){ + /* Got an abase here */ + regnum = *intP; + *p = '\0'; /* discard register spec */ + if (regnum == IPREL){ + /* We have to specialcase ip-rel mode */ + iprel_flag = 1; + } else { + memP->opcode |= regnum << 14; + mode |= A_BIT; + } + } + } + + /* Any expression present? */ + memP->e = argP; + if (*argP != '\0'){ + mode |= D_BIT; + } + + /* Special-case ip-relative addressing */ + if (iprel_flag){ + if (mode & I_BIT){ + syntax(); + } else { + memP->opcode |= 5 << 10; /* IP-relative mode */ + memP->disp = 32; + } + return; + } + + /* Handle all other modes */ + switch (mode){ + case D_BIT | A_BIT: + /* Go with MEMA instruction format for now (grow to MEMB later + * if 12 bits is not enough for the displacement). + * MEMA format has a single mode bit: set it to indicate + * that abase is present. + */ + memP->opcode |= MEMA_ABASE; + memP->disp = 12; + break; + + case D_BIT: + /* Go with MEMA instruction format for now (grow to MEMB later + * if 12 bits is not enough for the displacement). + */ + memP->disp = 12; + break; + + case A_BIT: + /* For some reason, the bit string for this mode is not + * consistent: it should be 0 (exclusive of the MEMB bit), + * so we set it "by hand" here. + */ + memP->opcode |= MEMB_BIT; + break; + + case A_BIT | I_BIT: + /* set MEMB bit in mode, and OR in mode bits */ + memP->opcode |= mode | MEMB_BIT; + break; + + case I_BIT: + /* Treat missing displacement as displacement of 0 */ + mode |= D_BIT; + /*********************** + * Fall into next case * + ********************** */ + case D_BIT | A_BIT | I_BIT: + case D_BIT | I_BIT: + /* set MEMB bit in mode, and OR in mode bits */ + memP->opcode |= mode | MEMB_BIT; + memP->disp = 32; + break; + + default: + syntax(); + break; + } +} + +/***************************************************************************** + * parse_po: parse machine-dependent pseudo-op + * + * This is a top-level routine for machine-dependent pseudo-ops. It slurps + * up the rest of the input line, breaks out the individual arguments, + * and dispatches them to the correct handler. + **************************************************************************** */ +static +void +parse_po(po_num) + int po_num; /* Pseudo-op number: currently S_LEAFPROC or S_SYSPROC */ +{ + char *args[4]; /* Pointers operands, with no embedded whitespace. + * arg[0] unused. + * arg[1-3]->operands + */ + int n_ops; /* Number of operands */ + char *p; /* Pointer to beginning of unparsed argument string */ + char eol; /* Character that indicated end of line */ + + extern char is_end_of_line[]; + + /* Advance input pointer to end of line. */ + p = input_line_pointer; + while (!is_end_of_line[ *input_line_pointer ]){ + input_line_pointer++; + } + eol = *input_line_pointer; /* Save end-of-line char */ + *input_line_pointer = '\0'; /* Terminate argument list */ + + /* Parse out operands */ + n_ops = get_args(p, args); + if (n_ops == -1){ + return; + } + + /* Dispatch to correct handler */ + switch(po_num){ + case S_SYSPROC: s_sysproc(n_ops, args); break; + case S_LEAFPROC: s_leafproc(n_ops, args); break; + default: BAD_CASE(po_num); break; + } + + /* Restore eol, so line numbers get updated correctly. Base assembler + * assumes we leave input pointer pointing at char following the eol. + */ + *input_line_pointer++ = eol; +} + +/***************************************************************************** + * parse_regop: parse a register operand. + * + * In case of illegal operand, issue a message and return some valid + * information so instruction processing can continue. + **************************************************************************** */ +static +void +parse_regop(regopP, optext, opdesc) + struct regop *regopP; /* Where to put description of register operand */ + char *optext; /* Text of operand */ + char opdesc; /* Descriptor byte: what's legal for this operand */ +{ + int n; /* Register number */ + expressionS e; /* Parsed expression */ + + /* See if operand is a register */ + n = get_regnum(optext); + if (n >= 0){ + if (IS_RG_REG(n)){ + /* global or local register */ + if (!REG_ALIGN(opdesc,n)){ + as_bad("unaligned register"); + } + regopP->n = n; + regopP->mode = 0; + regopP->special = 0; + return; + } else if (IS_FP_REG(n) && FP_OK(opdesc)){ + /* Floating point register, and it's allowed */ + regopP->n = n - FP0; + regopP->mode = 1; + regopP->special = 0; + return; + } else if (IS_SF_REG(n) && SFR_OK(opdesc)){ + /* Special-function register, and it's allowed */ + regopP->n = n - SF0; + regopP->mode = 0; + regopP->special = 1; + if (!targ_has_sfr(regopP->n)){ + as_bad("no such sfr in this architecture"); + } + return; + } + } else if (LIT_OK(opdesc)){ + /* + * How about a literal? + */ + regopP->mode = 1; + regopP->special = 0; + if (FP_OK(opdesc)){ /* floating point literal acceptable */ + /* Skip over 0f, 0d, or 0e prefix */ + if ( (optext[0] == '0') + && (optext[1] >= 'd') + && (optext[1] <= 'f') ){ + optext += 2; + } + + if (!strcmp(optext,"0.0") || !strcmp(optext,"0") ){ + regopP->n = 0x10; + return; + } + if (!strcmp(optext,"1.0") || !strcmp(optext,"1") ){ + regopP->n = 0x16; + return; + } + + } else { /* fixed point literal acceptable */ + if ((parse_expr(optext,&e) != SEG_ABSOLUTE) + || (offs(e) < 0) || (offs(e) > 31)){ + as_bad("illegal literal"); + offs(e) = 0; + } + regopP->n = offs(e); + return; + } + } + + /* Nothing worked */ + syntax(); + regopP->mode = 0; /* Register r0 is always a good one */ + regopP->n = 0; + regopP->special = 0; +} /* parse_regop() */ + +/***************************************************************************** + * reg_fmt: generate a REG-format instruction + * + **************************************************************************** */ +static void reg_fmt(args, oP) + char *args[]; /* args[0]->opcode mnemonic, args[1-3]->operands */ + struct i960_opcode *oP; /* Pointer to description of instruction */ +{ + long instr; /* Binary to be output */ + struct regop regop; /* Description of register operand */ + int n_ops; /* Number of operands */ + + + instr = oP->opcode; + n_ops = oP->num_ops; + + if (n_ops >= 1){ + parse_regop(®op, args[1], oP->operand[0]); + + if ((n_ops == 1) && !(instr & M3)){ + /* 1-operand instruction in which the dst field should + * be used (instead of src1). + */ + regop.n <<= 19; + if (regop.special){ + regop.mode = regop.special; + } + regop.mode <<= 13; + regop.special = 0; + } else { + /* regop.n goes in bit 0, needs no shifting */ + regop.mode <<= 11; + regop.special <<= 5; + } + instr |= regop.n | regop.mode | regop.special; + } + + if (n_ops >= 2) { + parse_regop(®op, args[2], oP->operand[1]); + + if ((n_ops == 2) && !(instr & M3)){ + /* 2-operand instruction in which the dst field should + * be used instead of src2). + */ + regop.n <<= 19; + if (regop.special){ + regop.mode = regop.special; + } + regop.mode <<= 13; + regop.special = 0; + } else { + regop.n <<= 14; + regop.mode <<= 12; + regop.special <<= 6; + } + instr |= regop.n | regop.mode | regop.special; + } + if (n_ops == 3){ + parse_regop(®op, args[3], oP->operand[2]); + if (regop.special){ + regop.mode = regop.special; + } + instr |= (regop.n <<= 19) | (regop.mode <<= 13); + } + emit(instr); +} + + +/***************************************************************************** + * relax_cobr: + * Replace cobr instruction in a code fragment with equivalent branch and + * compare instructions, so it can reach beyond a 13-bit displacement. + * Set up an address fix/relocation for the new branch instruction. + * + **************************************************************************** */ + +/* This "conditional jump" table maps cobr instructions into equivalent + * compare and branch opcodes. + */ +static +struct { + long compare; + long branch; +} coj[] = { /* COBR OPCODE: */ + CHKBIT, BNO, /* 0x30 - bbc */ + CMPO, BG, /* 0x31 - cmpobg */ + CMPO, BE, /* 0x32 - cmpobe */ + CMPO, BGE, /* 0x33 - cmpobge */ + CMPO, BL, /* 0x34 - cmpobl */ + CMPO, BNE, /* 0x35 - cmpobne */ + CMPO, BLE, /* 0x36 - cmpoble */ + CHKBIT, BO, /* 0x37 - bbs */ + CMPI, BNO, /* 0x38 - cmpibno */ + CMPI, BG, /* 0x39 - cmpibg */ + CMPI, BE, /* 0x3a - cmpibe */ + CMPI, BGE, /* 0x3b - cmpibge */ + CMPI, BL, /* 0x3c - cmpibl */ + CMPI, BNE, /* 0x3d - cmpibne */ + CMPI, BLE, /* 0x3e - cmpible */ + CMPI, BO, /* 0x3f - cmpibo */ +}; + +static +void +relax_cobr(fragP) + register fragS *fragP; /* fragP->fr_opcode is assumed to point to + * the cobr instruction, which comes at the + * end of the code fragment. + */ +{ + int opcode, src1, src2, m1, s2; + /* Bit fields from cobr instruction */ + long bp_bits; /* Branch prediction bits from cobr instruction */ + long instr; /* A single i960 instruction */ + char *iP; /*->instruction to be replaced */ + fixS *fixP; /* Relocation that can be done at assembly time */ + + /* PICK UP & PARSE COBR INSTRUCTION */ + iP = fragP->fr_opcode; + instr = md_chars_to_number(iP, 4); + opcode = ((instr >> 24) & 0xff) - 0x30; /* "-0x30" for table index */ + src1 = (instr >> 19) & 0x1f; + m1 = (instr >> 13) & 1; + s2 = instr & 1; + src2 = (instr >> 14) & 0x1f; + bp_bits= instr & BP_MASK; + + /* GENERATE AND OUTPUT COMPARE INSTRUCTION */ + instr = coj[opcode].compare + | src1 | (m1 << 11) | (s2 << 6) | (src2 << 14); + md_number_to_chars(iP, instr, 4); + + /* OUTPUT BRANCH INSTRUCTION */ + md_number_to_chars(iP+4, coj[opcode].branch | bp_bits, 4); + + /* SET UP ADDRESS FIXUP/RELOCATION */ + fixP = fix_new(fragP, + iP+4 - fragP->fr_literal, + 4, + fragP->fr_symbol, + 0, + fragP->fr_offset, + 1, + 0); + + fixP->fx_bit_fixP = (bit_fixS *) 24; /* Store size of bit field */ + + fragP->fr_fix += 4; + frag_wane(fragP); +} + + +/***************************************************************************** + * reloc_callj: Relocate a 'callj' instruction + * + * This is a "non-(GNU)-standard" machine-dependent hook. The base + * assembler calls it when it decides it can relocate an address at + * assembly time instead of emitting a relocation directive. + * + * Check to see if the relocation involves a 'callj' instruction to a: + * sysproc: Replace the default 'call' instruction with a 'calls' + * leafproc: Replace the default 'call' instruction with a 'bal'. + * other proc: Do nothing. + * + * See b.out.h for details on the 'n_other' field in a symbol structure. + * + * IMPORTANT!: + * Assumes the caller has already figured out, in the case of a leafproc, + * to use the 'bal' entry point, and has substituted that symbol into the + * passed fixup structure. + * + **************************************************************************** */ +void reloc_callj(fixP) +fixS *fixP; /* Relocation that can be done at assembly time */ +{ + char *where; /*->the binary for the instruction being relocated */ + + if (!fixP->fx_callj) { + return; + } /* This wasn't a callj instruction in the first place */ + + where = fixP->fx_frag->fr_literal + fixP->fx_where; + + if (TC_S_IS_SYSPROC(fixP->fx_addsy)) { + /* Symbol is a .sysproc: replace 'call' with 'calls'. + * System procedure number is (other-1). + */ + md_number_to_chars(where, CALLS|TC_S_GET_SYSPROC(fixP->fx_addsy), 4); + + /* Nothing else needs to be done for this instruction. + * Make sure 'md_number_to_field()' will perform a no-op. + */ + fixP->fx_bit_fixP = (bit_fixS *) 1; + + } else if (TC_S_IS_CALLNAME(fixP->fx_addsy)) { + /* Should not happen: see block comment above */ + as_fatal("Trying to 'bal' to %s", S_GET_NAME(fixP->fx_addsy)); + + } else if (TC_S_IS_BALNAME(fixP->fx_addsy)) { + /* Replace 'call' with 'bal'; both instructions have + * the same format, so calling code should complete + * relocation as if nothing happened here. + */ + md_number_to_chars(where, BAL, 4); + } else if (TC_S_IS_BADPROC(fixP->fx_addsy)) { + as_bad("Looks like a proc, but can't tell what kind.\n"); + } /* switch on proc type */ + + /* else Symbol is neither a sysproc nor a leafproc */ + + return; +} /* reloc_callj() */ + + +/***************************************************************************** + * s_leafproc: process .leafproc pseudo-op + * + * .leafproc takes two arguments, the second one is optional: + * arg[1]: name of 'call' entry point to leaf procedure + * arg[2]: name of 'bal' entry point to leaf procedure + * + * If the two arguments are identical, or if the second one is missing, + * the first argument is taken to be the 'bal' entry point. + * + * If there are 2 distinct arguments, we must make sure that the 'bal' + * entry point immediately follows the 'call' entry point in the linked + * list of symbols. + * + **************************************************************************** */ +static void s_leafproc(n_ops, args) +int n_ops; /* Number of operands */ +char *args[]; /* args[1]->1st operand, args[2]->2nd operand */ +{ + symbolS *callP; /* Pointer to leafproc 'call' entry point symbol */ + symbolS *balP; /* Pointer to leafproc 'bal' entry point symbol */ + + if ((n_ops != 1) && (n_ops != 2)) { + as_bad("should have 1 or 2 operands"); + return; + } /* Check number of arguments */ + + /* Find or create symbol for 'call' entry point. */ + callP = symbol_find_or_make(args[1]); + + if (TC_S_IS_CALLNAME(callP)) { + as_warn("Redefining leafproc %s", S_GET_NAME(callP)); + } /* is leafproc */ + + /* If that was the only argument, use it as the 'bal' entry point. + * Otherwise, mark it as the 'call' entry point and find or create + * another symbol for the 'bal' entry point. + */ + if ((n_ops == 1) || !strcmp(args[1],args[2])) { + TC_S_FORCE_TO_BALNAME(callP); + + } else { + TC_S_FORCE_TO_CALLNAME(callP); + + balP = symbol_find_or_make(args[2]); + if (TC_S_IS_CALLNAME(balP)) { + as_warn("Redefining leafproc %s", S_GET_NAME(balP)); + } + TC_S_FORCE_TO_BALNAME(balP); + + tc_set_bal_of_call(callP, balP); + } /* if only one arg, or the args are the same */ + + return; +} /* s_leafproc() */ + + +/* + * s_sysproc: process .sysproc pseudo-op + * + * .sysproc takes two arguments: + * arg[1]: name of entry point to system procedure + * arg[2]: 'entry_num' (index) of system procedure in the range + * [0,31] inclusive. + * + * For [ab].out, we store the 'entrynum' in the 'n_other' field of + * the symbol. Since that entry is normally 0, we bias 'entrynum' + * by adding 1 to it. It must be unbiased before it is used. + */ +static void s_sysproc(n_ops, args) +int n_ops; /* Number of operands */ +char *args[]; /* args[1]->1st operand, args[2]->2nd operand */ +{ + expressionS exp; + symbolS *symP; + + if (n_ops != 2) { + as_bad("should have two operands"); + return; + } /* bad arg count */ + + /* Parse "entry_num" argument and check it for validity. */ + if ((parse_expr(args[2],&exp) != SEG_ABSOLUTE) + || (offs(exp) < 0) + || (offs(exp) > 31)) { + as_bad("'entry_num' must be absolute number in [0,31]"); + return; + } + + /* Find/make symbol and stick entry number (biased by +1) into it */ + symP = symbol_find_or_make(args[1]); + + if (TC_S_IS_SYSPROC(symP)) { + as_warn("Redefining entrynum for sysproc %s", S_GET_NAME(symP)); + } /* redefining */ + + TC_S_SET_SYSPROC(symP, offs(exp)); /* encode entry number */ + TC_S_FORCE_TO_SYSPROC(symP); + + return; +} /* s_sysproc() */ + + +/***************************************************************************** + * shift_ok: + * Determine if a "shlo" instruction can be used to implement a "ldconst". + * This means that some number X < 32 can be shifted left to produce the + * constant of interest. + * + * Return the shift count, or 0 if we can't do it. + * Caller calculates X by shifting original constant right 'shift' places. + * + **************************************************************************** */ +static +int +shift_ok(n) + int n; /* The constant of interest */ +{ + int shift; /* The shift count */ + + if (n <= 0){ + /* Can't do it for negative numbers */ + return 0; + } + + /* Shift 'n' right until a 1 is about to be lost */ + for (shift = 0; (n & 1) == 0; shift++){ + n >>= 1; + } + + if (n >= 32){ + return 0; + } + return shift; +} + + +/***************************************************************************** + * syntax: issue syntax error + * + **************************************************************************** */ +static void syntax() { + as_bad("syntax error"); +} /* syntax() */ + + +/***************************************************************************** + * targ_has_sfr: + * Return TRUE iff the target architecture supports the specified + * special-function register (sfr). + * + **************************************************************************** */ +static +int +targ_has_sfr(n) + int n; /* Number (0-31) of sfr */ +{ + switch (architecture){ + case ARCH_KA: + case ARCH_KB: + case ARCH_MC: + return 0; + case ARCH_CA: + default: + return ((0<=n) && (n<=2)); + } +} + + +/***************************************************************************** + * targ_has_iclass: + * Return TRUE iff the target architecture supports the indicated + * class of instructions. + * + **************************************************************************** */ +static +int +targ_has_iclass(ic) + int ic; /* Instruction class; one of: + * I_BASE, I_CX, I_DEC, I_KX, I_FP, I_MIL, I_CASIM + */ +{ + iclasses_seen |= ic; + switch (architecture){ + case ARCH_KA: return ic & (I_BASE | I_KX); + case ARCH_KB: return ic & (I_BASE | I_KX | I_FP | I_DEC); + case ARCH_MC: return ic & (I_BASE | I_KX | I_FP | I_DEC | I_MIL); + case ARCH_CA: return ic & (I_BASE | I_CX | I_CASIM); + default: + if ((iclasses_seen & (I_KX|I_FP|I_DEC|I_MIL)) + && (iclasses_seen & I_CX)){ + as_warn("architecture of opcode conflicts with that of earlier instruction(s)"); + iclasses_seen &= ~ic; + } + return 1; + } +} + + +/* Parse an operand that is machine-specific. + We just return without modifying the expression if we have nothing + to do. */ + +/* ARGSUSED */ +void +md_operand (expressionP) + expressionS *expressionP; +{ +} + +/* We have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS *md_undefined_symbol(name) +char *name; +{ + return 0; +} /* md_undefined_symbol() */ + +/* Exactly what point is a PC-relative offset relative TO? + On the i960, they're relative to the address of the instruction, + which we have set up as the address of the fixup too. */ +long +md_pcrel_from (fixP) + fixS *fixP; +{ + return fixP->fx_where + fixP->fx_frag->fr_address; +} + +void +md_apply_fix(fixP, val) + fixS *fixP; + long val; +{ + char *place = fixP->fx_where + fixP->fx_frag->fr_literal; + + if (!fixP->fx_bit_fixP) { + + switch (fixP->fx_im_disp) { + case 0: + fixP->fx_addnumber = val; + md_number_to_imm(place, val, fixP->fx_size, fixP); + break; + case 1: + md_number_to_disp(place, + fixP->fx_pcrel ? val + fixP->fx_pcrel_adjust : val, + fixP->fx_size); + break; + case 2: /* fix requested for .long .word etc */ + md_number_to_chars(place, val, fixP->fx_size); + break; + default: + as_fatal("Internal error in md_apply_fix() in file \"%s\"", __FILE__); + } /* OVE: maybe one ought to put _imm _disp _chars in one md-func */ + } else { + md_number_to_field(place, val, fixP->fx_bit_fixP); + } + + return; +} /* md_apply_fix() */ + +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) +/* + * emit_relocations() + * + * Crawl along a fixS chain. Emit the segment's relocations. + */ +static void +emit_machine_reloc (fixP, segment_address_in_file) + register fixS * fixP; /* Fixup chain for this segment. */ + relax_addressT segment_address_in_file; +{ + struct reloc_info_generic ri; + register symbolS * symbolP; + + /* JF this is for paranoia */ + bzero((char *)&ri,sizeof(ri)); + for (; fixP; fixP = fixP->fx_next) + { + if ((symbolP = fixP->fx_addsy) != 0) + { + /* These two 'cuz of NS32K */ + ri . r_bsr = fixP->fx_bsr; + ri . r_disp = fixP->fx_im_disp; + + ri . r_callj = fixP->fx_callj; + + ri . r_length = nbytes_r_length [fixP->fx_size]; + ri . r_pcrel = fixP->fx_pcrel; + ri . r_address = fixP->fx_frag->fr_address + + fixP->fx_where + - segment_address_in_file; + if (!S_IS_DEFINED(symbolP)) + { + ri . r_extern = 1; + ri . r_symbolnum = symbolP->sy_number; + } + else + { + ri . r_extern = 0; + ri . r_symbolnum = S_GET_TYPE(symbolP); + } + + /* Output the relocation information in machine-dependent form. */ + md_ri_to_chars(next_object_file_charP, &ri); + next_object_file_charP += sizeof(struct relocation_info); + } + } + +} /* emit_machine_reloc() */ +#endif /* OBJ_AOUT or OBJ_BOUT */ + +/* Align an address by rounding it up to the specified boundary. + */ +long md_section_align(seg, addr) +segT seg; +long addr; /* Address to be rounded up */ +{ + return((addr + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg])); +} /* md_section_align() */ + +#ifdef OBJ_COFF +void tc_headers_hook(headers) +object_headers *headers; +{ + unsigned short arch_flag = 0; + + if (iclasses_seen == I_BASE){ + headers->filehdr.f_flags |= F_I960CORE; + } else if (iclasses_seen & I_CX){ + headers->filehdr.f_flags |= F_I960CA; + } else if (iclasses_seen & I_MIL){ + headers->filehdr.f_flags |= F_I960MC; + } else if (iclasses_seen & (I_DEC|I_FP)){ + headers->filehdr.f_flags |= F_I960KB; + } else { + headers->filehdr.f_flags |= F_I960KA; + } /* set arch flag */ + + if (flagseen['R']) { + headers->filehdr.f_magic = I960RWMAGIC; + headers->aouthdr.magic = OMAGIC; + } else { + headers->filehdr.f_magic = I960ROMAGIC; + headers->aouthdr.magic = NMAGIC; + } /* set magic numbers */ + + return; +} /* tc_headers_hook() */ +#endif /* OBJ_COFF */ + +/* + * Things going on here: + * + * For bout, We need to assure a couple of simplifying + * assumptions about leafprocs for the linker: the leafproc + * entry symbols will be defined in the same assembly in + * which they're declared with the '.leafproc' directive; + * and if a leafproc has both 'call' and 'bal' entry points + * they are both global or both local. + * + * For coff, the call symbol has a second aux entry that + * contains the bal entry point. The bal symbol becomes a + * label. + * + * For coff representation, the call symbol has a second aux entry that + * contains the bal entry point. The bal symbol becomes a label. + * + */ + +void tc_crawl_symbol_chain(headers) +object_headers *headers; +{ + symbolS *symbolP; + + for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next(symbolP)) { +#ifdef OBJ_COFF + if (TC_S_IS_SYSPROC(symbolP)) { + /* second aux entry already contains the sysproc number */ + S_SET_NUMBER_AUXILIARY(symbolP, 2); + S_SET_STORAGE_CLASS(symbolP, C_SCALL); + S_SET_DATA_TYPE(symbolP, S_GET_DATA_TYPE(symbolP) | (DT_FCN << N_BTSHFT)); + continue; + } /* rewrite sysproc */ +#endif /* OBJ_COFF */ + + if (!TC_S_IS_BALNAME(symbolP) && !TC_S_IS_CALLNAME(symbolP)) { + continue; + } /* Not a leafproc symbol */ + + if (!S_IS_DEFINED(symbolP)) { + as_bad("leafproc symbol '%s' undefined", S_GET_NAME(symbolP)); + } /* undefined leaf */ + + if (TC_S_IS_CALLNAME(symbolP)) { + symbolS *balP = tc_get_bal_of_call(symbolP); + if (S_IS_EXTERNAL(symbolP) != S_IS_EXTERNAL(balP)) { + S_SET_EXTERNAL(symbolP); + S_SET_EXTERNAL(balP); + as_warn("Warning: making leafproc entries %s and %s both global\n", + S_GET_NAME(symbolP), S_GET_NAME(balP)); + } /* externality mismatch */ + } /* if callname */ + } /* walk the symbol chain */ + + return; +} /* tc_crawl_symbol_chain() */ + +/* + * For aout or bout, the bal immediately follows the call. + * + * For coff, we cheat and store a pointer to the bal symbol + * in the second aux entry of the call. + */ + +void tc_set_bal_of_call(callP, balP) +symbolS *callP; +symbolS *balP; +{ + know(TC_S_IS_CALLNAME(callP)); + know(TC_S_IS_BALNAME(balP)); + +#ifdef OBJ_COFF + + callP->sy_symbol.ost_auxent[1].x_bal.x_balntry = (int) balP; + S_SET_NUMBER_AUXILIARY(callP,2); + +#elif defined(OBJ_AOUT) || defined(OBJ_BOUT) + + /* If the 'bal' entry doesn't immediately follow the 'call' + * symbol, unlink it from the symbol list and re-insert it. + */ + if (symbol_next(callP) != balP) { + symbol_remove(balP, &symbol_rootP, &symbol_lastP); + symbol_append(balP, callP, &symbol_rootP, &symbol_lastP); + } /* if not in order */ + +#else + (as yet unwritten.); +#endif /* switch on OBJ_FORMAT */ + + return; +} /* tc_set_bal_of_call() */ + +char *_tc_get_bal_of_call(callP) +symbolS *callP; +{ + symbolS *retval; + + know(TC_S_IS_CALLNAME(callP)); + +#ifdef OBJ_COFF + retval = (symbolS *) (callP->sy_symbol.ost_auxent[1].x_bal.x_balntry); +#elif defined(OBJ_AOUT) || defined(OBJ_BOUT) + retval = symbol_next(callP); +#else + (as yet unwritten.); +#endif /* switch on OBJ_FORMAT */ + + know(TC_S_IS_BALNAME(retval)); + return((char *) retval); +} /* _tc_get_bal_of_call() */ + +void tc_coff_symbol_emit_hook(symbolP) +symbolS *symbolP; +{ + if (TC_S_IS_CALLNAME(symbolP)) { +#ifdef OBJ_COFF + symbolS *balP = tc_get_bal_of_call(symbolP); + + /* second aux entry contains the bal entry point */ +/* S_SET_NUMBER_AUXILIARY(symbolP, 2); */ + symbolP->sy_symbol.ost_auxent[1].x_bal.x_balntry = S_GET_VALUE(balP); + S_SET_STORAGE_CLASS(symbolP, (!SF_GET_LOCAL(symbolP) ? C_LEAFEXT : C_LEAFSTAT)); + S_SET_DATA_TYPE(symbolP, S_GET_DATA_TYPE(symbolP) | (DT_FCN << N_BTSHFT)); + /* fix up the bal symbol */ + S_SET_STORAGE_CLASS(balP, C_LABEL); +#endif /* OBJ_COFF */ + } /* only on calls */ + + return; +} /* tc_coff_symbol_emit_hook() */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of i960.c */ diff --git a/gas/config/tc-i960.h b/gas/config/tc-i960.h new file mode 100644 index 0000000..2b05340 --- /dev/null +++ b/gas/config/tc-i960.h @@ -0,0 +1,279 @@ +/* tc-i960.h - Basic 80960 instruction formats. + Copyright (C) 1989, 1990, 1991 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 1, +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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +/* + * The 'COJ' instructions are actually COBR instructions with the 'b' in + * the mnemonic replaced by a 'j'; they are ALWAYS "de-optimized" if necessary: + * if the displacement will not fit in 13 bits, the assembler will replace them + * with the corresponding compare and branch instructions. + * + * All of the 'MEMn' instructions are the same format; the 'n' in the name + * indicates the default index scale factor (the size of the datum operated on). + * + * The FBRA formats are not actually an instruction format. They are the + * "convenience directives" for branching on floating-point comparisons, + * each of which generates 2 instructions (a 'bno' and one other branch). + * + * The CALLJ format is not actually an instruction format. It indicates that + * the instruction generated (a CTRL-format 'call') should have its relocation + * specially flagged for link-time replacement with a 'bal' or 'calls' if + * appropriate. + */ + +#define TC_I960 1 + + /* tailor gas */ +#define SYMBOLS_NEED_BACKPOINTERS +#define LOCAL_LABELS_FB +#define WANT_BITFIELDS + + /* tailor the coff format */ +#define OBJ_COFF_SECTION_HEADER_HAS_ALIGNMENT +#define OBJ_COFF_MAX_AUXENTRIES (2) + + /* other */ +#define CTRL 0 +#define COBR 1 +#define COJ 2 +#define REG 3 +#define MEM1 4 +#define MEM2 5 +#define MEM4 6 +#define MEM8 7 +#define MEM12 8 +#define MEM16 9 +#define FBRA 10 +#define CALLJ 11 + +/* Masks for the mode bits in REG format instructions */ +#define M1 0x0800 +#define M2 0x1000 +#define M3 0x2000 + +/* Generate the 12-bit opcode for a REG format instruction by placing the + * high 8 bits in instruction bits 24-31, the low 4 bits in instruction bits + * 7-10. + */ + +#define REG_OPC(opc) ((opc & 0xff0) << 20) | ((opc & 0xf) << 7) + +/* Generate a template for a REG format instruction: place the opcode bits + * in the appropriate fields and OR in mode bits for the operands that will not + * be used. I.e., + * set m1=1, if src1 will not be used + * set m2=1, if src2 will not be used + * set m3=1, if dst will not be used + * + * Setting the "unused" mode bits to 1 speeds up instruction execution(!). + * The information is also useful to us because some 1-operand REG instructions + * use the src1 field, others the dst field; and some 2-operand REG instructions + * use src1/src2, others src1/dst. The set mode bits enable us to distinguish. + */ +#define R_0(opc) ( REG_OPC(opc) | M1 | M2 | M3 ) /* No operands */ +#define R_1(opc) ( REG_OPC(opc) | M2 | M3 ) /* 1 operand: src1 */ +#define R_1D(opc) ( REG_OPC(opc) | M1 | M2 ) /* 1 operand: dst */ +#define R_2(opc) ( REG_OPC(opc) | M3 ) /* 2 ops: src1/src2 */ +#define R_2D(opc) ( REG_OPC(opc) | M2 ) /* 2 ops: src1/dst */ +#define R_3(opc) ( REG_OPC(opc) ) /* 3 operands */ + +/* DESCRIPTOR BYTES FOR REGISTER OPERANDS + * + * Interpret names as follows: + * R: global or local register only + * RS: global, local, or (if target allows) special-function register only + * RL: global or local register, or integer literal + * RSL: global, local, or (if target allows) special-function register; + * or integer literal + * F: global, local, or floating-point register + * FL: global, local, or floating-point register; or literal (including + * floating point) + * + * A number appended to a name indicates that registers must be aligned, + * as follows: + * 2: register number must be multiple of 2 + * 4: register number must be multiple of 4 + */ + +#define SFR 0x10 /* Mask for the "sfr-OK" bit */ +#define LIT 0x08 /* Mask for the "literal-OK" bit */ +#define FP 0x04 /* Mask for "floating-point-OK" bit */ + +/* This macro ors the bits together. Note that 'align' is a mask + * for the low 0, 1, or 2 bits of the register number, as appropriate. + */ +#define OP(align,lit,fp,sfr) ( align | lit | fp | sfr ) + +#define R OP( 0, 0, 0, 0 ) +#define RS OP( 0, 0, 0, SFR ) +#define RL OP( 0, LIT, 0, 0 ) +#define RSL OP( 0, LIT, 0, SFR ) +#define F OP( 0, 0, FP, 0 ) +#define FL OP( 0, LIT, FP, 0 ) +#define R2 OP( 1, 0, 0, 0 ) +#define RL2 OP( 1, LIT, 0, 0 ) +#define F2 OP( 1, 0, FP, 0 ) +#define FL2 OP( 1, LIT, FP, 0 ) +#define R4 OP( 3, 0, 0, 0 ) +#define RL4 OP( 3, LIT, 0, 0 ) +#define F4 OP( 3, 0, FP, 0 ) +#define FL4 OP( 3, LIT, FP, 0 ) + +#define M 0x7f /* Memory operand (MEMA & MEMB format instructions) */ + +/* Macros to extract info from the register operand descriptor byte 'od'. + */ +#define SFR_OK(od) (od & SFR) /* TRUE if sfr operand allowed */ +#define LIT_OK(od) (od & LIT) /* TRUE if literal operand allowed */ +#define FP_OK(od) (od & FP) /* TRUE if floating-point op allowed */ +#define REG_ALIGN(od,n) ((od & 0x3 & n) == 0) + /* TRUE if reg #n is properly aligned */ +#define MEMOP(od) (od == M) /* TRUE if operand is a memory operand*/ + +/* Classes of 960 intructions: + * - each instruction falls into one class. + * - each target architecture supports one or more classes. + * + * EACH CONSTANT MUST CONTAIN 1 AND ONLY 1 SET BIT!: see targ_has_iclass(). + */ +#define I_BASE 0x01 /* 80960 base instruction set */ +#define I_CX 0x02 /* 80960Cx instruction */ +#define I_DEC 0x04 /* Decimal instruction */ +#define I_FP 0x08 /* Floating point instruction */ +#define I_KX 0x10 /* 80960Kx instruction */ +#define I_MIL 0x20 /* Military instruction */ + +/* MEANING OF 'n_other' in the symbol record. + * + * If non-zero, the 'n_other' fields indicates either a leaf procedure or + * a system procedure, as follows: + * + * 1 <= n_other <= 32 : + * The symbol is the entry point to a system procedure. + * 'n_value' is the address of the entry, as for any other + * procedure. The system procedure number (which can be used in + * a 'calls' instruction) is (n_other-1). These entries come from + * '.sysproc' directives. + * + * n_other == N_CALLNAME + * the symbol is the 'call' entry point to a leaf procedure. + * The *next* symbol in the symbol table must be the corresponding + * 'bal' entry point to the procedure (see following). These + * entries come from '.leafproc' directives in which two different + * symbols are specified (the first one is represented here). + * + * + * n_other == N_BALNAME + * the symbol is the 'bal' entry point to a leaf procedure. + * These entries result from '.leafproc' directives in which only + * one symbol is specified, or in which the same symbol is + * specified twice. + * + * Note that an N_CALLNAME entry *must* have a corresponding N_BALNAME entry, + * but not every N_BALNAME entry must have an N_CALLNAME entry. + */ +#define N_CALLNAME (-1) +#define N_BALNAME (-2) + + + /* i960 uses a custom relocation record. */ + + /* let obj-aout.h know */ +#define CUSTOM_RELOC_FORMAT 1 + /* let a.out.gnu.h know */ +#define N_RELOCATION_INFO_DECLARED 1 +struct relocation_info { + int r_address; /* File address of item to be relocated */ + unsigned + r_index:24,/* Index of symbol on which relocation is based*/ + r_pcrel:1, /* 1 => relocate PC-relative; else absolute + * On i960, pc-relative implies 24-bit + * address, absolute implies 32-bit. + */ + r_length:2, /* Number of bytes to relocate: + * 0 => 1 byte + * 1 => 2 bytes + * 2 => 4 bytes -- only value used for i960 + */ + r_extern:1, + r_bsr:1, /* Something for the GNU NS32K assembler */ + r_disp:1, /* Something for the GNU NS32K assembler */ + r_callj:1, /* 1 if relocation target is an i960 'callj' */ + nuthin:1; /* Unused */ +}; + + /* hacks for tracking callj's */ +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + +#define TC_S_IS_SYSPROC(s) ((1<=S_GET_OTHER(s)) && (S_GET_OTHER(s)<=32)) +#define TC_S_IS_BALNAME(s) (S_GET_OTHER(s) == N_BALNAME) +#define TC_S_IS_CALLNAME(s) (S_GET_OTHER(s) == N_CALLNAME) +#define TC_S_IS_BADPROC(s) ((S_GET_OTHER(s) != 0) && !TC_S_IS_CALLNAME(s) && !TC_S_IS_BALNAME(s) && !TC_S_IS_SYSPROC(s)) + +#define TC_S_SET_SYSPROC(s, p) (S_SET_OTHER((s), (p)+1)) +#define TC_S_GET_SYSPROC(s) (S_GET_OTHER(s)-1) + +#define TC_S_FORCE_TO_BALNAME(s) (S_SET_OTHER((s), N_BALNAME)) +#define TC_S_FORCE_TO_CALLNAME(s) (S_SET_OTHER((s), N_CALLNAME)) +#define TC_S_FORCE_TO_SYSPROC(s) {;} + +#elif defined(OBJ_COFF) + +#define TC_S_IS_SYSPROC(s) (S_GET_STORAGE_CLASS(s) == C_SCALL) +#define TC_S_IS_BALNAME(s) (SF_GET_BALNAME(s)) +#define TC_S_IS_CALLNAME(s) (SF_GET_CALLNAME(s)) +#define TC_S_IS_BADPROC(s) (TC_S_IS_SYSPROC(s) && TC_S_GET_SYSPROC(s) < 0 && 31 < TC_S_GET_SYSPROC(s)) + +#define TC_S_SET_SYSPROC(s, p) ((s)->sy_symbol.ost_auxent[1].x_sc.x_stindx = (p)) +#define TC_S_GET_SYSPROC(s) ((s)->sy_symbol.ost_auxent[1].x_sc.x_stindx) + +#define TC_S_FORCE_TO_BALNAME(s) (SF_SET_BALNAME(s)) +#define TC_S_FORCE_TO_CALLNAME(s) (SF_SET_CALLNAME(s)) +#define TC_S_FORCE_TO_SYSPROC(s) (S_SET_STORAGE_CLASS((s), C_SCALL)) + +#else /* switch on OBJ */ +you lose +#endif /* witch on OBJ */ + +#ifdef __STDC__ + +void brtab_emit(void); +void reloc_callj(); /* this is really reloc_callj(fixS *fixP) but I don't want to change header inclusion order. */ +void tc_set_bal_of_call(); /* this is really tc_set_bal_of_call(symbolS *callP, symbolS *balP) */ + +#else /* __STDC__ */ + +void brtab_emit(); +void reloc_callj(); +void tc_set_bal_of_call(); + +#endif /* __STDC__ */ + +char *_tc_get_bal_of_call(); /* this is really symbolS *tc_get_bal_of_call(symbolS *callP). */ +#define tc_get_bal_of_call(c) ((symbolS *) _tc_get_bal_of_call(c)) + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of tp-i960.h */ diff --git a/gas/config/tc-m68851.h b/gas/config/tc-m68851.h new file mode 100644 index 0000000..ff984fe --- /dev/null +++ b/gas/config/tc-m68851.h @@ -0,0 +1,284 @@ + +/* + * pmmu.h + */ + +/* I suppose we have to copyright this file. Someone on the net sent it + to us as part of the changes for the m68851 Memory Management Unit */ + +/* Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of Gas, the GNU Assembler. + +The GNU assembler is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU Assembler General +Public License for full details. + +Everyone is granted permission to copy, modify and redistribute +the GNU Assembler, but only under the conditions described in the +GNU Assembler General Public License. A copy of this license is +supposed to have been given to you along with the GNU Assembler +so you can know your rights and responsibilities. It should be +in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. */ + +#ifdef m68851 + +/* + I didn't use much imagination in choosing the + following codes, so many of them aren't very + mnemonic. -rab + + P pmmu register + Possible values: + 000 TC Translation Control reg + 100 CAL Current Access Level + 101 VAL Validate Access Level + 110 SCC Stack Change Control + 111 AC Access Control + + W wide pmmu registers + Possible values: + 001 DRP Dma Root Pointer + 010 SRP Supervisor Root Pointer + 011 CRP Cpu Root Pointer + + f function code register + 0 SFC + 1 DFC + + V VAL register only + + X BADx, BACx + 100 BAD Breakpoint Acknowledge Data + 101 BAC Breakpoint Acknowledge Control + + Y PSR + Z PCSR + + | memory (modes 2-6, 7.*) + +*/ + +/* + * these defines should be in m68k.c but + * i put them here to keep all the m68851 stuff + * together -rab + * JF--Make sure these #s don't clash with the ones in m68k.c + * That would be BAD. + */ +#define TC (FPS+1) /* 48 */ +#define DRP (TC+1) /* 49 */ +#define SRP (DRP+1) /* 50 */ +#define CRP (SRP+1) /* 51 */ +#define CAL (CRP+1) /* 52 */ +#define VAL (CAL+1) /* 53 */ +#define SCC (VAL+1) /* 54 */ +#define AC (SCC+1) /* 55 */ +#define BAD (AC+1) /* 56,57,58,59, 60,61,62,63 */ +#define BAC (BAD+8) /* 64,65,66,67, 68,69,70,71 */ +#define PSR (BAC+8) /* 72 */ +#define PCSR (PSR+1) /* 73 */ + + /* name */ /* opcode */ /* match */ /* args */ + +{"pbac", one(0xf0c7), one(0xffbf), "Bc"}, +{"pbacw", one(0xf087), one(0xffbf), "Bc"}, +{"pbas", one(0xf0c6), one(0xffbf), "Bc"}, +{"pbasw", one(0xf086), one(0xffbf), "Bc"}, +{"pbbc", one(0xf0c1), one(0xffbf), "Bc"}, +{"pbbcw", one(0xf081), one(0xffbf), "Bc"}, +{"pbbs", one(0xf0c0), one(0xffbf), "Bc"}, +{"pbbsw", one(0xf080), one(0xffbf), "Bc"}, +{"pbcc", one(0xf0cf), one(0xffbf), "Bc"}, +{"pbccw", one(0xf08f), one(0xffbf), "Bc"}, +{"pbcs", one(0xf0ce), one(0xffbf), "Bc"}, +{"pbcsw", one(0xf08e), one(0xffbf), "Bc"}, +{"pbgc", one(0xf0cd), one(0xffbf), "Bc"}, +{"pbgcw", one(0xf08d), one(0xffbf), "Bc"}, +{"pbgs", one(0xf0cc), one(0xffbf), "Bc"}, +{"pbgsw", one(0xf08c), one(0xffbf), "Bc"}, +{"pbic", one(0xf0cb), one(0xffbf), "Bc"}, +{"pbicw", one(0xf08b), one(0xffbf), "Bc"}, +{"pbis", one(0xf0ca), one(0xffbf), "Bc"}, +{"pbisw", one(0xf08a), one(0xffbf), "Bc"}, +{"pblc", one(0xf0c3), one(0xffbf), "Bc"}, +{"pblcw", one(0xf083), one(0xffbf), "Bc"}, +{"pbls", one(0xf0c2), one(0xffbf), "Bc"}, +{"pblsw", one(0xf082), one(0xffbf), "Bc"}, +{"pbsc", one(0xf0c5), one(0xffbf), "Bc"}, +{"pbscw", one(0xf085), one(0xffbf), "Bc"}, +{"pbss", one(0xf0c4), one(0xffbf), "Bc"}, +{"pbssw", one(0xf084), one(0xffbf), "Bc"}, +{"pbwc", one(0xf0c9), one(0xffbf), "Bc"}, +{"pbwcw", one(0xf089), one(0xffbf), "Bc"}, +{"pbws", one(0xf0c8), one(0xffbf), "Bc"}, +{"pbwsw", one(0xf088), one(0xffbf), "Bc"}, + + +{"pdbac", two(0xf048, 0x0007), two(0xfff8, 0xffff), "DsBw"}, +{"pdbas", two(0xf048, 0x0006), two(0xfff8, 0xffff), "DsBw"}, +{"pdbbc", two(0xf048, 0x0001), two(0xfff8, 0xffff), "DsBw"}, +{"pdbbs", two(0xf048, 0x0000), two(0xfff8, 0xffff), "DsBw"}, +{"pdbcc", two(0xf048, 0x000f), two(0xfff8, 0xffff), "DsBw"}, +{"pdbcs", two(0xf048, 0x000e), two(0xfff8, 0xffff), "DsBw"}, +{"pdbgc", two(0xf048, 0x000d), two(0xfff8, 0xffff), "DsBw"}, +{"pdbgs", two(0xf048, 0x000c), two(0xfff8, 0xffff), "DsBw"}, +{"pdbic", two(0xf048, 0x000b), two(0xfff8, 0xffff), "DsBw"}, +{"pdbis", two(0xf048, 0x000a), two(0xfff8, 0xffff), "DsBw"}, +{"pdblc", two(0xf048, 0x0003), two(0xfff8, 0xffff), "DsBw"}, +{"pdbls", two(0xf048, 0x0002), two(0xfff8, 0xffff), "DsBw"}, +{"pdbsc", two(0xf048, 0x0005), two(0xfff8, 0xffff), "DsBw"}, +{"pdbss", two(0xf048, 0x0004), two(0xfff8, 0xffff), "DsBw"}, +{"pdbwc", two(0xf048, 0x0009), two(0xfff8, 0xffff), "DsBw"}, +{"pdbws", two(0xf048, 0x0008), two(0xfff8, 0xffff), "DsBw"}, + +{"pflusha", two(0xf000, 0x2400), two(0xffff, 0xffff), "" }, + +{"pflush", two(0xf000, 0x3010), two(0xffc0, 0xfe10), "T3T9" }, +{"pflush", two(0xf000, 0x3810), two(0xffc0, 0xfe10), "T3T9&s" }, +{"pflush", two(0xf000, 0x3008), two(0xffc0, 0xfe18), "D3T9" }, +{"pflush", two(0xf000, 0x3808), two(0xffc0, 0xfe18), "D3T9&s" }, +{"pflush", two(0xf000, 0x3000), two(0xffc0, 0xfe1e), "f3T9" }, +{"pflush", two(0xf000, 0x3800), two(0xffc0, 0xfe1e), "f3T9&s" }, + +{"pflushs", two(0xf000, 0x3410), two(0xfff8, 0xfe10), "T3T9" }, +{"pflushs", two(0xf000, 0x3c00), two(0xfff8, 0xfe00), "T3T9&s" }, +{"pflushs", two(0xf000, 0x3408), two(0xfff8, 0xfe18), "D3T9" }, +{"pflushs", two(0xf000, 0x3c08), two(0xfff8, 0xfe18), "D3T9&s" }, +{"pflushs", two(0xf000, 0x3400), two(0xfff8, 0xfe1e), "f3T9" }, +{"pflushs", two(0xf000, 0x3c00), two(0xfff8, 0xfe1e), "f3T9&s"}, + +{"pflushr", two(0xf000, 0xa000), two(0xffc0, 0xffff), "|s" }, + +{"ploadr", two(0xf000, 0x2210), two(0xffc0, 0xfff0), "T3&s" }, +{"ploadr", two(0xf000, 0x2208), two(0xffc0, 0xfff8), "D3&s" }, +{"ploadr", two(0xf000, 0x2200), two(0xffc0, 0xfffe), "f3&s" }, +{"ploadw", two(0xf000, 0x2010), two(0xffc0, 0xfff0), "T3&s" }, +{"ploadw", two(0xf000, 0x2008), two(0xffc0, 0xfff8), "D3&s" }, +{"ploadw", two(0xf000, 0x2000), two(0xffc0, 0xfffe), "f3&s" }, + +/* TC, CRP, DRP, SRP, CAL, VAL, SCC, AC */ +{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "*sP8" }, +{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "P8%s" }, +{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "|sW8" }, +{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "W8~s" }, + +/* BADx, BACx */ +{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xe3e3), "*sX3" }, +{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xe3e3), "X3%s" }, + +/* PSR, PCSR */ +/* {"pmove", two(0xf000, 0x6100), two(oxffc0, oxffff), "*sZ8" }, */ +{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xffff), "*sY8" }, +{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xffff), "Y8%s" }, +{"pmove", two(0xf000, 0x6600), two(0xffc0, 0xffff), "Z8%s" }, + +{"prestore", one(0xf140), one(0xffc0), "&s"}, +{"prestore", one(0xf158), one(0xfff8), "+s"}, +{"psave", one(0xf100), one(0xffc0), "&s"}, +{"psave", one(0xf100), one(0xffc0), "+s"}, + +{"psac", two(0xf040, 0x0007), two(0xffc0, 0xffff), "@s"}, +{"psas", two(0xf040, 0x0006), two(0xffc0, 0xffff), "@s"}, +{"psbc", two(0xf040, 0x0001), two(0xffc0, 0xffff), "@s"}, +{"psbs", two(0xf040, 0x0000), two(0xffc0, 0xffff), "@s"}, +{"pscc", two(0xf040, 0x000f), two(0xffc0, 0xffff), "@s"}, +{"pscs", two(0xf040, 0x000e), two(0xffc0, 0xffff), "@s"}, +{"psgc", two(0xf040, 0x000d), two(0xffc0, 0xffff), "@s"}, +{"psgs", two(0xf040, 0x000c), two(0xffc0, 0xffff), "@s"}, +{"psic", two(0xf040, 0x000b), two(0xffc0, 0xffff), "@s"}, +{"psis", two(0xf040, 0x000a), two(0xffc0, 0xffff), "@s"}, +{"pslc", two(0xf040, 0x0003), two(0xffc0, 0xffff), "@s"}, +{"psls", two(0xf040, 0x0002), two(0xffc0, 0xffff), "@s"}, +{"pssc", two(0xf040, 0x0005), two(0xffc0, 0xffff), "@s"}, +{"psss", two(0xf040, 0x0004), two(0xffc0, 0xffff), "@s"}, +{"pswc", two(0xf040, 0x0009), two(0xffc0, 0xffff), "@s"}, +{"psws", two(0xf040, 0x0008), two(0xffc0, 0xffff), "@s"}, + +{"ptestr", two(0xf000, 0x8210), two(0xffc0, 0xe3f0), "T3&sQ8" }, +{"ptestr", two(0xf000, 0x8310), two(0xffc0, 0xe310), "T3&sQ8A9" }, +{"ptestr", two(0xf000, 0x8208), two(0xffc0, 0xe3f8), "D3&sQ8" }, +{"ptestr", two(0xf000, 0x8308), two(0xffc0, 0xe318), "D3&sQ8A9" }, +{"ptestr", two(0xf000, 0x8200), two(0xffc0, 0xe3fe), "f3&sQ8" }, +{"ptestr", two(0xf000, 0x8300), two(0xffc0, 0xe31e), "f3&sQ8A9" }, + +{"ptestw", two(0xf000, 0x8010), two(0xffc0, 0xe3f0), "T3&sQ8" }, +{"ptestw", two(0xf000, 0x8110), two(0xffc0, 0xe310), "T3&sQ8A9" }, +{"ptestw", two(0xf000, 0x8008), two(0xffc0, 0xe3f8), "D3&sQ8" }, +{"ptestw", two(0xf000, 0x8108), two(0xffc0, 0xe318), "D3&sQ8A9" }, +{"ptestw", two(0xf000, 0x8000), two(0xffc0, 0xe3fe), "f3&sQ8" }, +{"ptestw", two(0xf000, 0x8100), two(0xffc0, 0xe31e), "f3&sQ8A9" }, + +{"ptrapacw", two(0xf07a, 0x0007), two(0xffff, 0xffff), "#w"}, +{"ptrapacl", two(0xf07b, 0x0007), two(0xffff, 0xffff), "#l"}, +{"ptrapac", two(0xf07c, 0x0007), two(0xffff, 0xffff), ""}, + +{"ptrapasw", two(0xf07a, 0x0006), two(0xffff, 0xffff), "#w"}, +{"ptrapasl", two(0xf07b, 0x0006), two(0xffff, 0xffff), "#l"}, +{"ptrapas", two(0xf07c, 0x0006), two(0xffff, 0xffff), ""}, + +{"ptrapbcw", two(0xf07a, 0x0001), two(0xffff, 0xffff), "#w"}, +{"ptrapbcl", two(0xf07b, 0x0001), two(0xffff, 0xffff), "#l"}, +{"ptrapbc", two(0xf07c, 0x0001), two(0xffff, 0xffff), ""}, + +{"ptrapbsw", two(0xf07a, 0x0000), two(0xffff, 0xffff), "#w"}, +{"ptrapbsl", two(0xf07b, 0x0000), two(0xffff, 0xffff), "#l"}, +{"ptrapbs", two(0xf07c, 0x0000), two(0xffff, 0xffff), ""}, + +{"ptrapccw", two(0xf07a, 0x000f), two(0xffff, 0xffff), "#w"}, +{"ptrapccl", two(0xf07b, 0x000f), two(0xffff, 0xffff), "#l"}, +{"ptrapcc", two(0xf07c, 0x000f), two(0xffff, 0xffff), ""}, + +{"ptrapcsw", two(0xf07a, 0x000e), two(0xffff, 0xffff), "#w"}, +{"ptrapcsl", two(0xf07b, 0x000e), two(0xffff, 0xffff), "#l"}, +{"ptrapcs", two(0xf07c, 0x000e), two(0xffff, 0xffff), ""}, + +{"ptrapgcw", two(0xf07a, 0x000d), two(0xffff, 0xffff), "#w"}, +{"ptrapgcl", two(0xf07b, 0x000d), two(0xffff, 0xffff), "#l"}, +{"ptrapgc", two(0xf07c, 0x000d), two(0xffff, 0xffff), ""}, + +{"ptrapgsw", two(0xf07a, 0x000c), two(0xffff, 0xffff), "#w"}, +{"ptrapgsl", two(0xf07b, 0x000c), two(0xffff, 0xffff), "#l"}, +{"ptrapgs", two(0xf07c, 0x000c), two(0xffff, 0xffff), ""}, + +{"ptrapicw", two(0xf07a, 0x000b), two(0xffff, 0xffff), "#w"}, +{"ptrapicl", two(0xf07b, 0x000b), two(0xffff, 0xffff), "#l"}, +{"ptrapic", two(0xf07c, 0x000b), two(0xffff, 0xffff), ""}, + +{"ptrapisw", two(0xf07a, 0x000a), two(0xffff, 0xffff), "#w"}, +{"ptrapisl", two(0xf07b, 0x000a), two(0xffff, 0xffff), "#l"}, +{"ptrapis", two(0xf07c, 0x000a), two(0xffff, 0xffff), ""}, + +{"ptraplcw", two(0xf07a, 0x0003), two(0xffff, 0xffff), "#w"}, +{"ptraplcl", two(0xf07b, 0x0003), two(0xffff, 0xffff), "#l"}, +{"ptraplc", two(0xf07c, 0x0003), two(0xffff, 0xffff), ""}, + +{"ptraplsw", two(0xf07a, 0x0002), two(0xffff, 0xffff), "#w"}, +{"ptraplsl", two(0xf07b, 0x0002), two(0xffff, 0xffff), "#l"}, +{"ptrapls", two(0xf07c, 0x0002), two(0xffff, 0xffff), ""}, + +{"ptrapscw", two(0xf07a, 0x0005), two(0xffff, 0xffff), "#w"}, +{"ptrapscl", two(0xf07b, 0x0005), two(0xffff, 0xffff), "#l"}, +{"ptrapsc", two(0xf07c, 0x0005), two(0xffff, 0xffff), ""}, + +{"ptrapssw", two(0xf07a, 0x0004), two(0xffff, 0xffff), "#w"}, +{"ptrapssl", two(0xf07b, 0x0004), two(0xffff, 0xffff), "#l"}, +{"ptrapss", two(0xf07c, 0x0004), two(0xffff, 0xffff), ""}, + +{"ptrapwcw", two(0xf07a, 0x0009), two(0xffff, 0xffff), "#w"}, +{"ptrapwcl", two(0xf07b, 0x0009), two(0xffff, 0xffff), "#l"}, +{"ptrapwc", two(0xf07c, 0x0009), two(0xffff, 0xffff), ""}, + +{"ptrapwsw", two(0xf07a, 0x0008), two(0xffff, 0xffff), "#w"}, +{"ptrapwsl", two(0xf07b, 0x0008), two(0xffff, 0xffff), "#l"}, +{"ptrapws", two(0xf07c, 0x0008), two(0xffff, 0xffff), ""}, + +{"pvalid", two(0xf000, 0x2800), two(0xffc0, 0xffff), "Vs&s"}, +{"pvalid", two(0xf000, 0x2c00), two(0xffc0, 0xfff8), "A3&s" }, + +#endif /* m68851 */ +/* end pmmu.h */ diff --git a/gas/config/tc-m68k.c b/gas/config/tc-m68k.c new file mode 100644 index 0000000..c2ba228 --- /dev/null +++ b/gas/config/tc-m68k.c @@ -0,0 +1,3808 @@ +/* m68k.c All the m68020 specific stuff in one convenient, huge, + slow to compile, easy to find file. + Copyright (C) 1987, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <ctype.h> + +#include "as.h" + +#include "obstack.h" + + /* note that this file includes real declarations and thus can only be included by one source file per executable. */ +#include "m68k-opcode.h" + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful */ +const char comment_chars[] = "|"; + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that comments like this one will always work. */ +const char line_comment_chars[] = "#"; + +/* Chars that can be used to separate mant from exp in floating point nums */ +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ + +const char FLT_CHARS[] = "rRsSfFdDxXeEpP"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c . Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. + */ + +int md_reloc_size = 8; /* Size of relocation record */ + +/* Its an arbitrary name: This means I don't approve of it */ +/* See flames below */ +static struct obstack robyn; + +#define TAB(x,y) (((x)<<2)+(y)) +#define TABTYPE(xy) ((xy) >> 2) +#define BYTE 0 +#define SHORT 1 +#define LONG 2 +#define SZ_UNDEF 3 + +#define BRANCH 1 +#define FBRANCH 2 +#define PCREL 3 +#define BCC68000 4 +#define DBCC 5 +#define PCLEA 6 + +struct m68k_exp { + char *e_beg; + char *e_end; + expressionS e_exp; + short e_siz; /* 0== default 1==short/byte 2==word 3==long */ +}; + +/* Internal form of an operand. */ +struct m68k_op { + char *error; /* Couldn't parse it */ + int mode; /* What mode this instruction is in. */ + unsigned long reg; /* Base register */ + struct m68k_exp *con1; + int ireg; /* Index register */ + int isiz; /* 0==unspec 1==byte(?) 2==short 3==long */ + int imul; /* Multipy ireg by this (1,2,4,or 8) */ + struct m68k_exp *con2; +}; + +/* internal form of a 68020 instruction */ +struct m68_it { + char *error; + char *args; /* list of opcode info */ + int numargs; + + int numo; /* Number of shorts in opcode */ + short opcode[11]; + + struct m68k_op operands[6]; + + int nexp; /* number of exprs in use */ + struct m68k_exp exprs[4]; + + int nfrag; /* Number of frags we have to produce */ + struct { + int fragoff; /* Where in the current opcode[] the frag ends */ + symbolS *fadd; + long foff; + int fragty; + } fragb[4]; + + int nrel; /* Num of reloc strucs in use */ + struct { + int n; + symbolS *add, + *sub; + long off; + char wid; + char pcrel; + } reloc[5]; /* Five is enough??? */ +}; + +struct m68_it the_ins; /* the instruction being assembled */ + + +/* Macros for adding things to the m68_it struct */ + +#define addword(w) the_ins.opcode[the_ins.numo++]=(w) + +/* Like addword, but goes BEFORE general operands */ +#define insop(w) {int z;\ + for(z=the_ins.numo;z>opcode->m_codenum;--z)\ + the_ins.opcode[z]=the_ins.opcode[z-1];\ + for(z=0;z<the_ins.nrel;z++)\ + the_ins.reloc[z].n+=2;\ + the_ins.opcode[opcode->m_codenum]=w;\ + the_ins.numo++;\ +} + + +#define add_exp(beg,end) (\ + the_ins.exprs[the_ins.nexp].e_beg=beg,\ + the_ins.exprs[the_ins.nexp].e_end=end,\ + &the_ins.exprs[the_ins.nexp++]\ +) + + +/* The numo+1 kludge is so we can hit the low order byte of the prev word. Blecch*/ +#define add_fix(width,exp,pc_rel) {\ + the_ins.reloc[the_ins.nrel].n= ((width)=='B') ? (the_ins.numo*2-1) : \ + (((width)=='b') ? ((the_ins.numo-1)*2) : (the_ins.numo*2));\ + the_ins.reloc[the_ins.nrel].add=adds((exp));\ + the_ins.reloc[the_ins.nrel].sub=subs((exp));\ + the_ins.reloc[the_ins.nrel].off=offs((exp));\ + the_ins.reloc[the_ins.nrel].wid=width;\ + the_ins.reloc[the_ins.nrel++].pcrel=pc_rel;\ +} + +#define add_frag(add,off,type) {\ + the_ins.fragb[the_ins.nfrag].fragoff=the_ins.numo;\ + the_ins.fragb[the_ins.nfrag].fadd=add;\ + the_ins.fragb[the_ins.nfrag].foff=off;\ + the_ins.fragb[the_ins.nfrag++].fragty=type;\ +} + +#define isvar(exp) ((exp) && (adds(exp) || subs(exp))) + +#define seg(exp) ((exp)->e_exp.X_seg) +#define adds(exp) ((exp)->e_exp.X_add_symbol) +#define subs(exp) ((exp)->e_exp.X_subtract_symbol) +#define offs(exp) ((exp)->e_exp.X_add_number) + + +struct m68_incant { + char *m_operands; + unsigned long m_opcode; + short m_opnum; + short m_codenum; + struct m68_incant *m_next; +}; + +#define getone(x) ((((x)->m_opcode)>>16)&0xffff) +#define gettwo(x) (((x)->m_opcode)&0xffff) + + +#ifdef __STDC__ + +static char *crack_operand(char *str, struct m68k_op *opP); +static int get_num(struct m68k_exp *exp, int ok); +static int get_regs(int i, char *str, struct m68k_op *opP); +static int reverse_16_bits(int in); +static int reverse_8_bits(int in); +static int try_index(char **s, struct m68k_op *opP); +static void install_gen_operand(int mode, int val); +static void install_operand(int mode, int val); +static void s_bss(void); +static void s_data1(void); +static void s_data2(void); +static void s_even(void); +static void s_proc(void); + +#else /* __STDC__ */ + +static char *crack_operand(); +static int get_num(); +static int get_regs(); +static int reverse_16_bits(); +static int reverse_8_bits(); +static int try_index(); +static void install_gen_operand(); +static void install_operand(); +static void s_bss(); +static void s_data1(); +static void s_data2(); +static void s_even(); +static void s_proc(); + +#endif /* __STDC__ */ + +/* BCC68000 is for patching in an extra jmp instruction for long offsets + on the 68000. The 68000 doesn't support long branches with branchs */ + +/* This table desribes how you change sizes for the various types of variable + size expressions. This version only supports two kinds. */ + +/* Note that calls to frag_var need to specify the maximum expansion needed */ +/* This is currently 10 bytes for DBCC */ + +/* The fields are: + How far Forward this mode will reach: + How far Backward this mode will reach: + How many bytes this mode will add to the size of the frag + Which mode to go to if the offset won't fit in this one + */ +const relax_typeS +md_relax_table[] = { +{ 1, 1, 0, 0 }, /* First entries aren't used */ +{ 1, 1, 0, 0 }, /* For no good reason except */ +{ 1, 1, 0, 0 }, /* that the VAX doesn't either */ +{ 1, 1, 0, 0 }, + +{ (127), (-128), 0, TAB(BRANCH,SHORT)}, +{ (32767), (-32768), 2, TAB(BRANCH,LONG) }, +{ 0, 0, 4, 0 }, +{ 1, 1, 0, 0 }, + +{ 1, 1, 0, 0 }, /* FBRANCH doesn't come BYTE */ +{ (32767), (-32768), 2, TAB(FBRANCH,LONG)}, +{ 0, 0, 4, 0 }, +{ 1, 1, 0, 0 }, + +{ 1, 1, 0, 0 }, /* PCREL doesn't come BYTE */ +{ (32767), (-32768), 2, TAB(PCREL,LONG)}, +{ 0, 0, 4, 0 }, +{ 1, 1, 0, 0 }, + +{ (127), (-128), 0, TAB(BCC68000,SHORT)}, +{ (32767), (-32768), 2, TAB(BCC68000,LONG) }, +{ 0, 0, 6, 0 }, /* jmp long space */ +{ 1, 1, 0, 0 }, + +{ 1, 1, 0, 0 }, /* DBCC doesn't come BYTE */ +{ (32767), (-32768), 2, TAB(DBCC,LONG) }, +{ 0, 0, 10, 0 }, /* bra/jmp long space */ +{ 1, 1, 0, 0 }, + +{ 1, 1, 0, 0 }, /* PCLEA doesn't come BYTE */ +{ 32767, -32768, 2, TAB(PCLEA,LONG) }, +{ 0, 0, 6, 0 }, +{ 1, 1, 0, 0 }, + +}; + +/* These are the machine dependent pseudo-ops. These are included so + the assembler can work on the output from the SUN C compiler, which + generates these. + */ + +/* This table describes all the machine specific pseudo-ops the assembler + has to support. The fields are: + pseudo-op name without dot + function to call to execute this pseudo-op + Integer arg to pass to the function + */ +const pseudo_typeS md_pseudo_table[] = { + { "data1", s_data1, 0 }, + { "data2", s_data2, 0 }, + { "bss", s_bss, 0 }, + { "even", s_even, 0 }, + { "skip", s_space, 0 }, + { "proc", s_proc, 0 }, + { 0, 0, 0 } +}; + + +/* #define isbyte(x) ((x)>=-128 && (x)<=127) */ +/* #define isword(x) ((x)>=-32768 && (x)<=32767) */ + +#define issbyte(x) ((x)>=-128 && (x)<=127) +#define isubyte(x) ((x)>=0 && (x)<=255) +#define issword(x) ((x)>=-32768 && (x)<=32767) +#define isuword(x) ((x)>=0 && (x)<=65535) + +#define isbyte(x) ((x)>=-128 && (x)<=255) +#define isword(x) ((x)>=-32768 && (x)<=65535) +#define islong(x) (1) + +extern char *input_line_pointer; + +/* Operands we can parse: (And associated modes) + +numb: 8 bit num +numw: 16 bit num +numl: 32 bit num +dreg: data reg 0-7 +reg: address or data register +areg: address register +apc: address register, PC, ZPC or empty string +num: 16 or 32 bit num +num2: like num +sz: w or l if omitted, l assumed +scale: 1 2 4 or 8 if omitted, 1 assumed + +7.4 IMMED #num --> NUM +0.? DREG dreg --> dreg +1.? AREG areg --> areg +2.? AINDR areg@ --> *(areg) +3.? AINC areg@+ --> *(areg++) +4.? ADEC areg@- --> *(--areg) +5.? AOFF apc@(numw) --> *(apc+numw) -- empty string and ZPC not allowed here +6.? AINDX apc@(num,reg:sz:scale) --> *(apc+num+reg*scale) +6.? AINDX apc@(reg:sz:scale) --> same, with num=0 +6.? APODX apc@(num)@(num2,reg:sz:scale) --> *(*(apc+num)+num2+reg*scale) +6.? APODX apc@(num)@(reg:sz:scale) --> same, with num2=0 +6.? AMIND apc@(num)@(num2) --> *(*(apc+num)+num2) (previous mode without an index reg) +6.? APRDX apc@(num,reg:sz:scale)@(num2) --> *(*(apc+num+reg*scale)+num2) +6.? APRDX apc@(reg:sz:scale)@(num2) --> same, with num=0 +7.0 ABSL num:sz --> *(num) + num --> *(num) (sz L assumed) +*** MSCR otherreg --> Magic +With -l option +5.? AOFF apc@(num) --> *(apc+num) -- empty string and ZPC not allowed here still + +examples: + #foo #0x35 #12 + d2 + a4 + a3@ + a5@+ + a6@- + a2@(12) pc@(14) + a1@(5,d2:w:1) @(45,d6:l:4) + pc@(a2) @(d4) + etc . . . + + +#name@(numw) -->turn into PC rel mode +apc@(num8,reg:sz:scale) --> *(apc+num8+reg*scale) + +*/ + +#define IMMED 1 +#define DREG 2 +#define AREG 3 +#define AINDR 4 +#define ADEC 5 +#define AINC 6 +#define AOFF 7 +#define AINDX 8 +#define APODX 9 +#define AMIND 10 +#define APRDX 11 +#define ABSL 12 +#define MSCR 13 +#define REGLST 14 + +#define FAIL 0 +#define OK 1 + +/* DATA and ADDR have to be contiguous, so that reg-DATA gives 0-7==data reg, + 8-15==addr reg for operands that take both types */ +#define DATA 1 /* 1- 8 == data registers 0-7 */ +#define ADDR (DATA+8) /* 9-16 == address regs 0-7 */ +#define FPREG (ADDR+8) /* 17-24 Eight FP registers */ +#define COPNUM (FPREG+8) /* 25-32 Co-processor #1-#8 */ + +#define PC (COPNUM+8) /* 33 Program counter */ +#define ZPC (PC+1) /* 34 Hack for Program space, but 0 addressing */ +#define SR (ZPC+1) /* 35 Status Reg */ +#define CCR (SR+1) /* 36 Condition code Reg */ + +/* These have to be in order for the movec instruction to work. */ +#define USP (CCR+1) /* 37 User Stack Pointer */ +#define ISP (USP+1) /* 38 Interrupt stack pointer */ +#define SFC (ISP+1) /* 39 */ +#define DFC (SFC+1) /* 40 */ +#define CACR (DFC+1) /* 41 */ +#define VBR (CACR+1) /* 42 */ +#define CAAR (VBR+1) /* 43 */ +#define MSP (CAAR+1) /* 44 */ + +#define FPI (MSP+1) /* 45 */ +#define FPS (FPI+1) /* 46 */ +#define FPC (FPS+1) /* 47 */ +/* + * these defines should be in m68k.c but + * i put them here to keep all the m68851 stuff + * together -rab + * JF--Make sure these #s don't clash with the ones in m68k.c + * That would be BAD. + */ +#define TC (FPC+1) /* 48 */ +#define DRP (TC+1) /* 49 */ +#define SRP (DRP+1) /* 50 */ +#define CRP (SRP+1) /* 51 */ +#define CAL (CRP+1) /* 52 */ +#define VAL (CAL+1) /* 53 */ +#define SCC (VAL+1) /* 54 */ +#define AC (SCC+1) /* 55 */ +#define BAD (AC+1) /* 56,57,58,59, 60,61,62,63 */ +#define BAC (BAD+8) /* 64,65,66,67, 68,69,70,71 */ +#define PSR (BAC+8) /* 72 */ +#define PCSR (PSR+1) /* 73 */ + + +/* Note that COPNUM==processor #1 -- COPNUM+7==#8, which stores as 000 */ +/* I think. . . */ + +#define SP ADDR+7 + +/* JF these tables here are for speed at the expense of size */ +/* You can replace them with the #if 0 versions if you really + need space and don't mind it running a bit slower */ + +static char mklower_table[256]; +#define mklower(c) (mklower_table[(unsigned char)(c)]) +static char notend_table[256]; +static char alt_notend_table[256]; +#define notend(s) ( !(notend_table[(unsigned char)(*s)] || (*s==':' &&\ + alt_notend_table[(unsigned char)(s[1])]))) + +#if 0 +#define mklower(c) (isupper(c) ? tolower(c) : c) +#endif + + +/* JF modified this to handle cases where the first part of a symbol name + looks like a register */ + +int +m68k_reg_parse(ccp) +register char **ccp; +{ + register char c1, + c2, + c3, + c4; + register int n = 0, + ret = FAIL; + + c1=mklower(ccp[0][0]); +#ifdef REGISTER_PREFIX + if(c1!=REGISTER_PREFIX) + return FAIL; + c1=mklower(ccp[0][1]); + c2=mklower(ccp[0][2]); + c3=mklower(ccp[0][3]); + c4=mklower(ccp[0][4]); +#else + c2=mklower(ccp[0][1]); + c3=mklower(ccp[0][2]); + c4=mklower(ccp[0][3]); +#endif + switch(c1) { + case 'a': + if(c2>='0' && c2<='7') { + n=2; + ret=ADDR+c2-'0'; + } +#ifdef m68851 + else if (c2 == 'c') { + n = 2; + ret = AC; + } +#endif + break; +#ifdef m68851 + case 'b': + if (c2 == 'a') { + if (c3 == 'd') { + if (c4 >= '0' && c4 <= '7') { + n = 4; + ret = BAD + c4 - '0'; + } + } + if (c3 == 'c') { + if (c4 >= '0' && c4 <= '7') { + n = 4; + ret = BAC + c4 - '0'; + } + } + } + break; +#endif + case 'c': +#ifdef m68851 + if (c2 == 'a' && c3 == 'l') { + n = 3; + ret = CAL; + } else +#endif + /* This supports both CCR and CC as the ccr reg. */ + if(c2=='c' && c3=='r') { + n=3; + ret = CCR; + } else if(c2=='c') { + n=2; + ret = CCR; + } else if(c2=='a' && (c3=='a' || c3=='c') && c4=='r') { + n=4; + ret = c3=='a' ? CAAR : CACR; + } +#ifdef m68851 + else if (c2 == 'r' && c3 == 'p') { + n = 3; + ret = (CRP); + } +#endif + break; + case 'd': + if(c2>='0' && c2<='7') { + n=2; + ret = DATA+c2-'0'; + } else if(c2=='f' && c3=='c') { + n=3; + ret = DFC; + } +#ifdef m68851 + else if (c2 == 'r' && c3 == 'p') { + n = 3; + ret = (DRP); + } +#endif + break; + case 'f': + if(c2=='p') { + if(c3>='0' && c3<='7') { + n=3; + ret = FPREG+c3-'0'; + if(c4==':') + ccp[0][3]=','; + } else if(c3=='i') { + n=3; + ret = FPI; + } else if(c3=='s') { + n= (c4 == 'r' ? 4 : 3); + ret = FPS; + } else if(c3=='c') { + n= (c4 == 'r' ? 4 : 3); + ret = FPC; + } + } + break; + case 'i': + if(c2=='s' && c3=='p') { + n=3; + ret = ISP; + } + break; + case 'm': + if(c2=='s' && c3=='p') { + n=3; + ret = MSP; + } + break; + case 'p': + if(c2=='c') { +#ifdef m68851 + if(c3 == 's' && c4=='r') { + n=4; + ret = (PCSR); + } else +#endif + { + n=2; + ret = PC; + } + } +#ifdef m68851 + else if (c2 == 's' && c3 == 'r') { + n = 3; + ret = (PSR); + } +#endif + break; + case 's': +#ifdef m68851 + if (c2 == 'c' && c3 == 'c') { + n = 3; + ret = (SCC); + } else if (c2 == 'r' && c3 == 'p') { + n = 3; + ret = (SRP); + } else +#endif + if(c2=='r') { + n=2; + ret = SR; + } else if(c2=='p') { + n=2; + ret = ADDR+7; + } else if(c2=='f' && c3=='c') { + n=3; + ret = SFC; + } + break; +#ifdef m68851 + case 't': + if(c2 == 'c') { + n=2; + ret=TC; + } + break; +#endif + case 'u': + if(c2=='s' && c3=='p') { + n=3; + ret = USP; + } + break; + case 'v': +#ifdef m68851 + if (c2 == 'a' && c3 == 'l') { + n = 3; + ret = (VAL); + } else +#endif + if(c2=='b' && c3=='r') { + n=3; + ret = VBR; + } + break; + case 'z': + if(c2=='p' && c3=='c') { + n=3; + ret = ZPC; + } + break; + default: + break; + } + if(n) { +#ifdef REGISTER_PREFIX + n++; +#endif + if(isalnum(ccp[0][n]) || ccp[0][n]=='_') + ret=FAIL; + else + ccp[0]+=n; + } else + ret = FAIL; + return ret; +} + +#define SKIP_WHITE() { str++; if(*str==' ') str++;} + +int +m68k_ip_op(str,opP) +char *str; +register struct m68k_op *opP; +{ + char *strend; + long i; + + if(*str==' ') + str++; + /* Find the end of the string */ + if(!*str) { + /* Out of gas */ + opP->error="Missing operand"; + return FAIL; + } + for(strend=str;*strend;strend++) + ; + --strend; + + /* Guess what: A constant. Shar and enjoy */ + if(*str=='#') { + str++; + opP->con1=add_exp(str,strend); + opP->mode=IMMED; + return OK; + } + i=m68k_reg_parse(&str); + if((i==FAIL || *str!='\0') && *str!='@') { + char *stmp; + + if(i!=FAIL && (*str=='/' || *str=='-')) { + opP->mode=REGLST; + return get_regs(i,str,opP); + } + if((stmp=strchr(str,'@')) != '\0') { + opP->con1=add_exp(str,stmp-1); + if(stmp==strend) { + opP->mode=AINDX; + return OK; + } + stmp++; + if(*stmp++!='(' || *strend--!=')') { + opP->error="Malformed operand"; + return FAIL; + } + i=try_index(&stmp,opP); + opP->con2=add_exp(stmp,strend); + if(i==FAIL) opP->mode=AMIND; + else opP->mode=APODX; + return OK; + } + opP->mode=ABSL; + opP->con1=add_exp(str,strend); + return OK; + } + opP->reg=i; + if(*str=='\0') { + if(i>=DATA+0 && i<=DATA+7) + opP->mode=DREG; + else if(i>=ADDR+0 && i<=ADDR+7) + opP->mode=AREG; + else + opP->mode=MSCR; + return OK; + } + if((i<ADDR+0 || i>ADDR+7) && i!=PC && i!=ZPC && i!=FAIL) { /* Can't indirect off non address regs */ + opP->error="Invalid indirect register"; + return FAIL; + } + if(*str!='@') + abort(); + str++; + switch(*str) { + case '\0': + opP->mode=AINDR; + return OK; + case '-': + opP->mode=ADEC; + return OK; + case '+': + opP->mode=AINC; + return OK; + case '(': + str++; + break; + default: + opP->error="Junk after indirect"; + return FAIL; + } + /* Some kind of indexing involved. Lets find out how bad it is */ + i=try_index(&str,opP); + /* Didn't start with an index reg, maybe its offset or offset,reg */ + if(i==FAIL) { + char *beg_str; + + beg_str=str; + for(i=1;i;) { + switch(*str++) { + case '\0': + opP->error="Missing )"; + return FAIL; + case ',': i=0; break; + case '(': i++; break; + case ')': --i; break; + } + } + /* if(str[-3]==':') { + int siz; + + switch(str[-2]) { + case 'b': + case 'B': + siz=1; + break; + case 'w': + case 'W': + siz=2; + break; + case 'l': + case 'L': + siz=3; + break; + default: + opP->error="Specified size isn't :w or :l"; + return FAIL; + } + opP->con1=add_exp(beg_str,str-4); + opP->con1->e_siz=siz; + } else */ + opP->con1=add_exp(beg_str,str-2); + /* Should be offset,reg */ + if(str[-1]==',') { + i=try_index(&str,opP); + if(i==FAIL) { + opP->error="Malformed index reg"; + return FAIL; + } + } + } + /* We've now got offset) offset,reg) or reg) */ + + if(*str=='\0') { + /* Th-the-thats all folks */ + if(opP->reg==FAIL) opP->mode=AINDX; /* Other form of indirect */ + else if(opP->ireg==FAIL) opP->mode=AOFF; + else opP->mode=AINDX; + return OK; + } + /* Next thing had better be another @ */ + if(*str!='@' || str[1]!='(') { + opP->error="junk after indirect"; + return FAIL; + } + str+=2; + if(opP->ireg!=FAIL) { + opP->mode=APRDX; + i=try_index(&str,opP); + if(i!=FAIL) { + opP->error="Two index registers! not allowed!"; + return FAIL; + } + } else + i=try_index(&str,opP); + if(i==FAIL) { + char *beg_str; + + beg_str=str; + for(i=1;i;) { + switch(*str++) { + case '\0': + opP->error="Missing )"; + return FAIL; + case ',': i=0; break; + case '(': i++; break; + case ')': --i; break; + } + } + opP->con2=add_exp(beg_str,str-2); + if(str[-1]==',') { + if(opP->ireg!=FAIL) { + opP->error="Can't have two index regs"; + return FAIL; + } + i=try_index(&str,opP); + if(i==FAIL) { + opP->error="malformed index reg"; + return FAIL; + } + opP->mode=APODX; + } else if(opP->ireg!=FAIL) + opP->mode=APRDX; + else + opP->mode=AMIND; + } else + opP->mode=APODX; + if(*str!='\0') { + opP->error="Junk after indirect"; + return FAIL; + } + return OK; +} + +static int try_index(s,opP) +char **s; +struct m68k_op *opP; +{ + register int i; + char *ss; +#define SKIP_W() { ss++; if(*ss==' ') ss++;} + + ss= *s; + /* SKIP_W(); */ + i=m68k_reg_parse(&ss); + if(!(i>=DATA+0 && i<=ADDR+7)) { /* if i is not DATA or ADDR reg */ + *s=ss; + return FAIL; + } + opP->ireg=i; + /* SKIP_W(); */ + if(*ss==')') { + opP->isiz=0; + opP->imul=1; + SKIP_W(); + *s=ss; + return OK; + } + if(*ss!=':') { + opP->error="Missing : in index register"; + *s=ss; + return FAIL; + } + SKIP_W(); + switch(*ss) { + case 'w': + case 'W': + opP->isiz=2; + break; + case 'l': + case 'L': + opP->isiz=3; + break; + default: + opP->error="Index register size spec not :w or :l"; + *s=ss; + return FAIL; + } + SKIP_W(); + if(*ss==':') { + SKIP_W(); + switch(*ss) { + case '1': + case '2': + case '4': + case '8': + opP->imul= *ss-'0'; + break; + default: + opP->error="index multiplier not 1, 2, 4 or 8"; + *s=ss; + return FAIL; + } + SKIP_W(); + } else opP->imul=1; + if(*ss!=')') { + opP->error="Missing )"; + *s=ss; + return FAIL; + } + SKIP_W(); + *s=ss; + return OK; +} /* try_index() */ + +#ifdef TEST1 /* TEST1 tests m68k_ip_op(), which parses operands */ +main() +{ + char buf[128]; + struct m68k_op thark; + + for(;;) { + if(!gets(buf)) + break; + bzero(&thark,sizeof(thark)); + if(!m68k_ip_op(buf,&thark)) printf("FAIL:"); + if(thark.error) + printf("op1 error %s in %s\n",thark.error,buf); + printf("mode %d, reg %d, ",thark.mode,thark.reg); + if(thark.b_const) + printf("Constant: '%.*s',",1+thark.e_const-thark.b_const,thark.b_const); + printf("ireg %d, isiz %d, imul %d ",thark.ireg,thark.isiz,thark.imul); + if(thark.b_iadd) + printf("Iadd: '%.*s'",1+thark.e_iadd-thark.b_iadd,thark.b_iadd); + printf("\n"); + } + exit(0); +} + +#endif + + +static struct hash_control* op_hash = NULL; /* handle of the OPCODE hash table + NULL means any use before m68_ip_begin() + will crash */ + + +/* + * m 6 8 _ i p ( ) + * + * This converts a string into a 68k instruction. + * The string must be a bare single instruction in sun format + * with RMS-style 68020 indirects + * (example: ) + * + * It provides some error messages: at most one fatal error message (which + * stops the scan) and at most one warning message for each operand. + * The 68k instruction is returned in exploded form, since we have no + * knowledge of how you parse (or evaluate) your expressions. + * We do however strip off and decode addressing modes and operation + * mnemonic. + * + * This function's value is a string. If it is not "" then an internal + * logic error was found: read this code to assign meaning to the string. + * No argument string should generate such an error string: + * it means a bug in our code, not in the user's text. + * + * You MUST have called m86_ip_begin() once and m86_ip_end() never before using + * this function. + */ + +/* JF this function no longer returns a useful value. Sorry */ +void +m68_ip (instring) +char *instring; +{ + register char *p; + register struct m68k_op *opP; + register struct m68_incant *opcode; + register char *s; + register int tmpreg, + baseo, + outro, + nextword; + int siz1, + siz2; + char c; + int losing; + int opsfound; + LITTLENUM_TYPE words[6]; + LITTLENUM_TYPE *wordp; + + if (*instring == ' ') + instring++; /* skip leading whitespace */ + + /* Scan up to end of operation-code, which MUST end in end-of-string + or exactly 1 space. */ + for (p = instring; *p != '\0'; p++) + if (*p == ' ') + break; + + + if (p == instring) { + the_ins.error = "No operator"; + the_ins.opcode[0] = (short) NULL; + /* the_ins.numo=1; */ + return; + } + + /* p now points to the end of the opcode name, probably whitespace. + make sure the name is null terminated by clobbering the whitespace, + look it up in the hash table, then fix it back. */ + c = *p; + *p = '\0'; + opcode = (struct m68_incant *)hash_find (op_hash, instring); + *p = c; + + if (opcode == NULL) { + the_ins.error = "Unknown operator"; + the_ins.opcode[0] = (short) NULL; + /* the_ins.numo=1; */ + return; + } + + /* found a legitimate opcode, start matching operands */ + for(opP= &the_ins.operands[0];*p;opP++) { + p = crack_operand (p, opP); + if(opP->error) { + the_ins.error=opP->error; + return; + } + } + + opsfound=opP- &the_ins.operands[0]; + /* This ugly hack is to support the floating pt opcodes in their standard form */ + /* Essentially, we fake a first enty of type COP#1 */ + if(opcode->m_operands[0]=='I') { + int n; + + for(n=opsfound;n>0;--n) + the_ins.operands[n]=the_ins.operands[n-1]; + + /* bcopy((char *)(&the_ins.operands[0]),(char *)(&the_ins.operands[1]),opsfound*sizeof(the_ins.operands[0])); */ + bzero((char *)(&the_ins.operands[0]),sizeof(the_ins.operands[0])); + the_ins.operands[0].mode=MSCR; + the_ins.operands[0].reg=COPNUM; /* COP #1 */ + opsfound++; + } + /* We've got the operands. Find an opcode that'll + accept them */ + for(losing=0;;) { + if(opsfound!=opcode->m_opnum) + losing++; + else for(s=opcode->m_operands,opP= &the_ins.operands[0];*s && !losing;s+=2,opP++) { + /* Warning: this switch is huge! */ + /* I've tried to organize the cases into this order: + non-alpha first, then alpha by letter. lower-case goes directly + before uppercase counterpart. */ + /* Code with multiple case ...: gets sorted by the lowest case ... + it belongs to. I hope this makes sense. */ + switch(*s) { + case '!': + if(opP->mode==MSCR || opP->mode==IMMED || + opP->mode==DREG || opP->mode==AREG || opP->mode==AINC || opP->mode==ADEC || opP->mode==REGLST) + losing++; + break; + + case '#': + if(opP->mode!=IMMED) + losing++; + else { + long t; + + t=get_num(opP->con1,80); + if(s[1]=='b' && !isbyte(t)) + losing++; + else if(s[1]=='w' && !isword(t)) + losing++; + } + break; + + case '^': + case 'T': + if(opP->mode!=IMMED) + losing++; + break; + + case '$': + if(opP->mode==MSCR || opP->mode==AREG || + opP->mode==IMMED || opP->reg==PC || opP->reg==ZPC || opP->mode==REGLST) + losing++; + break; + + case '%': + if(opP->mode==MSCR || opP->reg==PC || + opP->reg==ZPC || opP->mode==REGLST) + losing++; + break; + + + case '&': + if(opP->mode==MSCR || opP->mode==DREG || + opP->mode==AREG || opP->mode==IMMED || opP->reg==PC || opP->reg==ZPC || + opP->mode==AINC || opP->mode==ADEC || opP->mode==REGLST) + losing++; + break; + + case '*': + if(opP->mode==MSCR || opP->mode==REGLST) + losing++; + break; + + case '+': + if(opP->mode!=AINC) + losing++; + break; + + case '-': + if(opP->mode!=ADEC) + losing++; + break; + + case '/': + if(opP->mode==MSCR || opP->mode==AREG || + opP->mode==AINC || opP->mode==ADEC || opP->mode==IMMED || opP->mode==REGLST) + losing++; + break; + + case ';': + if(opP->mode==MSCR || opP->mode==AREG || opP->mode==REGLST) + losing++; + break; + + case '?': + if(opP->mode==MSCR || opP->mode==AREG || + opP->mode==AINC || opP->mode==ADEC || opP->mode==IMMED || opP->reg==PC || + opP->reg==ZPC || opP->mode==REGLST) + losing++; + break; + + case '@': + if(opP->mode==MSCR || opP->mode==AREG || + opP->mode==IMMED || opP->mode==REGLST) + losing++; + break; + + case '~': /* For now! (JF FOO is this right?) */ + if(opP->mode==MSCR || opP->mode==DREG || + opP->mode==AREG || opP->mode==IMMED || opP->reg==PC || opP->reg==ZPC || opP->mode==REGLST) + losing++; + break; + + case 'A': + if(opP->mode!=AREG) + losing++; + break; + + case 'B': /* FOO */ + if(opP->mode!=ABSL) + losing++; + break; + + case 'C': + if(opP->mode!=MSCR || opP->reg!=CCR) + losing++; + break; + + case 'd': /* FOO This mode is a KLUDGE!! */ + if(opP->mode!=AOFF && (opP->mode!=ABSL || + opP->con1->e_beg[0]!='(' || opP->con1->e_end[0]!=')')) + losing++; + break; + + case 'D': + if(opP->mode!=DREG) + losing++; + break; + + case 'F': + if(opP->mode!=MSCR || opP->reg<(FPREG+0) || opP->reg>(FPREG+7)) + losing++; + break; + + case 'I': + if(opP->mode!=MSCR || opP->reg<COPNUM || + opP->reg>=COPNUM+7) + losing++; + break; + + case 'J': + if(opP->mode!=MSCR || opP->reg<USP || opP->reg>MSP) + losing++; + break; + + case 'k': + if(opP->mode!=IMMED) + losing++; + break; + + case 'l': + case 'L': + if(opP->mode==DREG || opP->mode==AREG || opP->mode==FPREG) { + if(s[1]=='8') + losing++; + else { + opP->mode=REGLST; + opP->reg=1<<(opP->reg-DATA); + } + } else if(opP->mode!=REGLST) { + losing++; + } else if(s[1]=='8' && opP->reg&0x0FFffFF) + losing++; + else if(s[1]=='3' && opP->reg&0x7000000) + losing++; + break; + + case 'M': + if(opP->mode!=IMMED) + losing++; + else { + long t; + + t=get_num(opP->con1,80); + if(!issbyte(t) || isvar(opP->con1)) + losing++; + } + break; + + case 'O': + if(opP->mode!=DREG && opP->mode!=IMMED) + losing++; + break; + + case 'Q': + if(opP->mode!=IMMED) + losing++; + else { + long t; + + t=get_num(opP->con1,80); + if(t<1 || t>8 || isvar(opP->con1)) + losing++; + } + break; + + case 'R': + if(opP->mode!=DREG && opP->mode!=AREG) + losing++; + break; + + case 's': + if(opP->mode!=MSCR || !(opP->reg==FPI || opP->reg==FPS || opP->reg==FPC)) + losing++; + break; + + case 'S': + if(opP->mode!=MSCR || opP->reg!=SR) + losing++; + break; + + case 'U': + if(opP->mode!=MSCR || opP->reg!=USP) + losing++; + break; + + /* JF these are out of order. We could put them + in order if we were willing to put up with + bunches of #ifdef m68851s in the code */ +#ifdef m68851 + /* Memory addressing mode used by pflushr */ + case '|': + if(opP->mode==MSCR || opP->mode==DREG || + opP->mode==AREG || opP->mode==REGLST) + losing++; + break; + + case 'f': + if (opP->mode != MSCR || (opP->reg != SFC && opP->reg != DFC)) + losing++; + break; + + case 'P': + if (opP->mode != MSCR || (opP->reg != TC && opP->reg != CAL && + opP->reg != VAL && opP->reg != SCC && opP->reg != AC)) + losing++; + break; + + case 'V': + if (opP->reg != VAL) + losing++; + break; + + case 'W': + if (opP->mode != MSCR || (opP->reg != DRP && opP->reg != SRP && + opP->reg != CRP)) + losing++; + break; + + case 'X': + if (opP->mode != MSCR || + (!(opP->reg >= BAD && opP->reg <= BAD+7) && + !(opP->reg >= BAC && opP->reg <= BAC+7))) + losing++; + break; + + case 'Y': + if (opP->reg != PSR) + losing++; + break; + + case 'Z': + if (opP->reg != PCSR) + losing++; + break; +#endif + default: + as_fatal("Internal error: Operand mode %c unknown in line %s of file \"%s\"", + *s, __LINE__, __FILE__); + } + } + if(!losing) + break; + opcode=opcode->m_next; + if(!opcode) { /* Fell off the end */ + the_ins.error="instruction/operands mismatch"; + return; + } + losing=0; + } + the_ins.args=opcode->m_operands; + the_ins.numargs=opcode->m_opnum; + the_ins.numo=opcode->m_codenum; + the_ins.opcode[0]=getone(opcode); + the_ins.opcode[1]=gettwo(opcode); + + for(s=the_ins.args,opP= &the_ins.operands[0];*s;s+=2,opP++) { + /* This switch is a doozy. + What the first step; its a big one! */ + switch(s[0]) { + + case '*': + case '~': + case '%': + case ';': + case '@': + case '!': + case '&': + case '$': + case '?': + case '/': +#ifdef m68851 + case '|': +#endif + switch(opP->mode) { + case IMMED: + tmpreg=0x3c; /* 7.4 */ + if(strchr("bwl",s[1])) nextword=get_num(opP->con1,80); + else nextword=nextword=get_num(opP->con1,0); + if(isvar(opP->con1)) + add_fix(s[1],opP->con1,0); + switch(s[1]) { + case 'b': + if(!isbyte(nextword)) + opP->error="operand out of range"; + addword(nextword); + baseo=0; + break; + case 'w': + if(!isword(nextword)) + opP->error="operand out of range"; + addword(nextword); + baseo=0; + break; + case 'l': + addword(nextword>>16); + addword(nextword); + baseo=0; + break; + + case 'f': + baseo=2; + outro=8; + break; + case 'F': + baseo=4; + outro=11; + break; + case 'x': + baseo=6; + outro=15; + break; + case 'p': + baseo=6; + outro= -1; + break; + default: + as_fatal("Internal error: Can't decode %c%c in line %s of file \"%s\"", + *s, s[1], __LINE__, __FILE__); + } + if(!baseo) + break; + + /* We gotta put out some float */ + if(seg(opP->con1)!=SEG_BIG) { + int_to_gen(nextword); + gen_to_words(words,baseo,(long)outro); + for(wordp=words;baseo--;wordp++) + addword(*wordp); + break; + } /* Its BIG */ + if(offs(opP->con1)>0) { + as_warn("Bignum assumed to be binary bit-pattern"); + if(offs(opP->con1)>baseo) { + as_bad("Bignum too big for %c format; truncated",s[1]); + offs(opP->con1)=baseo; + } + baseo-=offs(opP->con1); + for(wordp=generic_bignum+offs(opP->con1)-1;offs(opP->con1)--;--wordp) + addword(*wordp); + while(baseo--) + addword(0); + break; + } + gen_to_words(words,baseo,(long)outro); + for(wordp=words;baseo--;wordp++) + addword(*wordp); + break; + case DREG: + tmpreg=opP->reg-DATA; /* 0.dreg */ + break; + case AREG: + tmpreg=0x08+opP->reg-ADDR; /* 1.areg */ + break; + case AINDR: + tmpreg=0x10+opP->reg-ADDR; /* 2.areg */ + break; + case ADEC: + tmpreg=0x20+opP->reg-ADDR; /* 4.areg */ + break; + case AINC: + tmpreg=0x18+opP->reg-ADDR; /* 3.areg */ + break; + case AOFF: + + nextword=get_num(opP->con1,80); + /* Force into index mode. Hope this works */ + + /* We do the first bit for 32-bit displacements, + and the second bit for 16 bit ones. It is + possible that we should make the default be + WORD instead of LONG, but I think that'd + break GCC, so we put up with a little + inefficiency for the sake of working output. + */ + + if( !issword(nextword) + || ( isvar(opP->con1) + && ( ( opP->con1->e_siz==0 + && flagseen['l']==0) + || opP->con1->e_siz==3))) { + + if(opP->reg==PC) + tmpreg=0x3B; /* 7.3 */ + else + tmpreg=0x30+opP->reg-ADDR; /* 6.areg */ + if(isvar(opP->con1)) { + if(opP->reg==PC) { + add_frag(adds(opP->con1), + offs(opP->con1), + TAB(PCLEA,SZ_UNDEF)); + break; + } else { + addword(0x0170); + add_fix('l',opP->con1,1); + } + } else + addword(0x0170); + addword(nextword>>16); + } else { + if(opP->reg==PC) + tmpreg=0x3A; /* 7.2 */ + else + tmpreg=0x28+opP->reg-ADDR; /* 5.areg */ + + if(isvar(opP->con1)) { + if(opP->reg==PC) { + add_fix('w',opP->con1,1); + } else + add_fix('w',opP->con1,0); + } + } + addword(nextword); + break; + case AINDX: + case APODX: + case AMIND: + case APRDX: + nextword=0; + baseo=get_num(opP->con1,80); + outro=get_num(opP->con2,80); + /* Figure out the 'addressing mode' */ + /* Also turn on the BASE_DISABLE bit, if needed */ + if(opP->reg==PC || opP->reg==ZPC) { + tmpreg=0x3b; /* 7.3 */ + if(opP->reg==ZPC) + nextword|=0x80; + } else if(opP->reg==FAIL) { + nextword|=0x80; + tmpreg=0x30; /* 6.garbage */ + } else tmpreg=0x30+opP->reg-ADDR; /* 6.areg */ + + siz1= (opP->con1) ? opP->con1->e_siz : 0; + siz2= (opP->con2) ? opP->con2->e_siz : 0; + + /* Index register stuff */ + if(opP->ireg>=DATA+0 && opP->ireg<=ADDR+7) { + nextword|=(opP->ireg-DATA)<<12; + + if(opP->isiz==0 || opP->isiz==3) + nextword|=0x800; + switch(opP->imul) { + case 1: break; + case 2: nextword|=0x200; break; + case 4: nextword|=0x400; break; + case 8: nextword|=0x600; break; + default: abort(); + } + /* IF its simple, + GET US OUT OF HERE! */ + + /* Must be INDEX, with an index + register. Address register + cannot be ZERO-PC, and either + :b was forced, or we know + it will fit */ + if( opP->mode==AINDX + && opP->reg!=FAIL + && opP->reg!=ZPC + && ( siz1==1 + || ( issbyte(baseo) + && !isvar(opP->con1)))) { + nextword +=baseo&0xff; + addword(nextword); + if(isvar(opP->con1)) + add_fix('B',opP->con1,0); + break; + } + } else + nextword|=0x40; /* No index reg */ + + /* It aint simple */ + nextword|=0x100; + /* If the guy specified a width, we assume that + it is wide enough. Maybe it isn't. Ifso, we lose + */ + switch(siz1) { + case 0: + if(isvar(opP->con1) || !issword(baseo)) { + siz1=3; + nextword|=0x30; + } else if(baseo==0) + nextword|=0x10; + else { + nextword|=0x20; + siz1=2; + } + break; + case 1: + as_warn("Byte dispacement won't work. Defaulting to :w"); + case 2: + nextword|=0x20; + break; + case 3: + nextword|=0x30; + break; + } + + /* Figure out innner displacement stuff */ + if(opP->mode!=AINDX) { + switch(siz2) { + case 0: + if(isvar(opP->con2) || !issword(outro)) { + siz2=3; + nextword|=0x3; + } else if(outro==0) + nextword|=0x1; + else { + nextword|=0x2; + siz2=2; + } + break; + case 1: + as_warn("Byte dispacement won't work. Defaulting to :w"); + case 2: + nextword|=0x2; + break; + case 3: + nextword|=0x3; + break; + } + if(opP->mode==APODX) nextword|=0x04; + else if(opP->mode==AMIND) nextword|=0x40; + } + addword(nextword); + + if(isvar(opP->con1)) { + if(opP->reg==PC || opP->reg==ZPC) { + add_fix(siz1==3 ? 'l' : 'w',opP->con1,1); + opP->con1->e_exp.X_add_number+=6; + } else + add_fix(siz1==3 ? 'l' : 'w',opP->con1,0); + } + if(siz1==3) + addword(baseo>>16); + if(siz1) + addword(baseo); + + if(isvar(opP->con2)) { + if(opP->reg==PC || opP->reg==ZPC) { + add_fix(siz2==3 ? 'l' : 'w',opP->con2,1); + opP->con1->e_exp.X_add_number+=6; + } else + add_fix(siz2==3 ? 'l' : 'w',opP->con2,0); + } + if(siz2==3) + addword(outro>>16); + if(siz2) + addword(outro); + + break; + + case ABSL: + nextword=get_num(opP->con1,80); + switch(opP->con1->e_siz) { + default: + as_bad("Unknown size for absolute reference"); + case 0: + if(!isvar(opP->con1) && issword(offs(opP->con1))) { + tmpreg=0x38; /* 7.0 */ + addword(nextword); + break; + } + if(isvar(opP->con1) && + !subs(opP->con1) && + !strchr("~%&$?", s[0])) { + tmpreg=0x3A; /* 7.2 */ + add_frag(adds(opP->con1), + offs(opP->con1), + TAB(PCREL,SZ_UNDEF)); + break; + } + case 3: /* Fall through into long */ + if(isvar(opP->con1)) + add_fix('l',opP->con1,0); + + tmpreg=0x39; /* 7.1 mode */ + addword(nextword>>16); + addword(nextword); + break; + + case 2: /* Word */ + if(isvar(opP->con1)) + add_fix('w',opP->con1,0); + + tmpreg=0x38; /* 7.0 mode */ + addword(nextword); + break; + } + break; + case MSCR: + default: + as_bad("unknown/incorrect operand"); + /* abort(); */ + } + install_gen_operand(s[1],tmpreg); + break; + + case '#': + case '^': + switch(s[1]) { /* JF: I hate floating point! */ + case 'j': + tmpreg=70; + break; + case '8': + tmpreg=20; + break; + case 'C': + tmpreg=50; + break; + case '3': + default: + tmpreg=80; + break; + } + tmpreg=get_num(opP->con1,tmpreg); + if(isvar(opP->con1)) + add_fix(s[1],opP->con1,0); + switch(s[1]) { + case 'b': /* Danger: These do no check for + certain types of overflow. + user beware! */ + if(!isbyte(tmpreg)) + opP->error="out of range"; + insop(tmpreg); + if(isvar(opP->con1)) + the_ins.reloc[the_ins.nrel-1].n=(opcode->m_codenum)*2; + break; + case 'w': + if(!isword(tmpreg)) + opP->error="out of range"; + insop(tmpreg); + if(isvar(opP->con1)) + the_ins.reloc[the_ins.nrel-1].n=(opcode->m_codenum)*2; + break; + case 'l': + insop(tmpreg); /* Because of the way insop works, we put these two out backwards */ + insop(tmpreg>>16); + if(isvar(opP->con1)) + the_ins.reloc[the_ins.nrel-1].n=(opcode->m_codenum)*2; + break; + case '3': + tmpreg&=0xFF; + case '8': + case 'C': + install_operand(s[1],tmpreg); + break; + default: + as_fatal("Internal error: Unknown mode #%c in line %s of file \"%s\"", s[1], __LINE__, __FILE__); + } + break; + + case '+': + case '-': + case 'A': + install_operand(s[1],opP->reg-ADDR); + break; + + case 'B': + tmpreg=get_num(opP->con1,80); + switch(s[1]) { + case 'g': + if(opP->con1->e_siz) { /* Deal with fixed size stuff by hand */ + switch(opP->con1->e_siz) { + case 1: + add_fix('b',opP->con1,1); + break; + case 2: + add_fix('w',opP->con1,1); + addword(0); + break; + case 3: + add_fix('l',opP->con1,1); + addword(0); + addword(0); + break; + default: + as_bad("Bad size for expression %d", opP->con1->e_siz); + } + } else if(subs(opP->con1)) { + /* We can't relax it */ + the_ins.opcode[the_ins.numo-1]|=0xff; + add_fix('l',opP->con1,1); + addword(0); + addword(0); + } else if(adds(opP->con1)) { + if (flagseen['m'] && + (the_ins.opcode[0] >= 0x6200) && + (the_ins.opcode[0] <= 0x6f00)) { + add_frag(adds(opP->con1),offs(opP->con1),TAB(BCC68000,SZ_UNDEF)); + } else { + add_frag(adds(opP->con1),offs(opP->con1),TAB(BRANCH,SZ_UNDEF)); + } + } else { + /* JF: This is the WRONG thing to do + add_frag((symbolS *)0,offs(opP->con1),TAB(BRANCH,BYTE)); */ + the_ins.opcode[the_ins.numo-1]|=0xff; + offs(opP->con1)+=4; + add_fix('l',opP->con1,1); + addword(0); + addword(0); + } + break; + case 'w': + if(isvar(opP->con1)) { + /* check for DBcc instruction */ + if ((the_ins.opcode[0] & 0xf0f8) ==0x50c8) { + /* size varies if patch */ + /* needed for long form */ + add_frag(adds(opP->con1),offs(opP->con1),TAB(DBCC,SZ_UNDEF)); + break; + } + + /* Don't ask! */ + opP->con1->e_exp.X_add_number+=2; + add_fix('w',opP->con1,1); + } + addword(0); + break; + case 'c': + if(opP->con1->e_siz) { + switch(opP->con1->e_siz) { + case 2: + add_fix('w',opP->con1,1) + addword(0); + break; + case 3: + the_ins.opcode[the_ins.numo-1]|=0x40; + add_fix('l',opP->con1,1); + addword(0); + addword(0); + break; + default: + as_bad("Bad size for offset, must be word or long"); + break; + } + } else if(subs(opP->con1)) { + add_fix('l',opP->con1,1); + add_frag((symbolS *)0,(long)0,TAB(FBRANCH,LONG)); + } else if(adds(opP->con1)) { + add_frag(adds(opP->con1),offs(opP->con1),TAB(FBRANCH,SZ_UNDEF)); + } else { + /* add_frag((symbolS *)0,offs(opP->con1),TAB(FBRANCH,SHORT)); */ + the_ins.opcode[the_ins.numo-1]|=0x40; + add_fix('l',opP->con1,1); + addword(0); + addword(4); + } + break; + default: + as_fatal("Internal error: operand type B%c unknown in line %s of file \"%s\"", + s[1], __LINE__, __FILE__); + } + break; + + case 'C': /* Ignore it */ + break; + + case 'd': /* JF this is a kludge */ + if(opP->mode==AOFF) { + install_operand('s',opP->reg-ADDR); + } else { + char *tmpP; + + tmpP=opP->con1->e_end-2; + opP->con1->e_beg++; + opP->con1->e_end-=4; /* point to the , */ + baseo=m68k_reg_parse(&tmpP); + if(baseo<ADDR+0 || baseo>ADDR+7) { + as_bad("Unknown address reg, using A0"); + baseo=0; + } else baseo-=ADDR; + install_operand('s',baseo); + } + tmpreg=get_num(opP->con1,80); + if(!issword(tmpreg)) { + as_bad("Expression out of range, using 0"); + tmpreg=0; + } + addword(tmpreg); + break; + + case 'D': + install_operand(s[1],opP->reg-DATA); + break; + + case 'F': + install_operand(s[1],opP->reg-FPREG); + break; + + case 'I': + tmpreg=1+opP->reg-COPNUM; + if(tmpreg==8) + tmpreg=0; + install_operand(s[1],tmpreg); + break; + + case 'J': /* JF foo */ + switch(opP->reg) { + case SFC: + tmpreg=0; + break; + case DFC: + tmpreg=0x001; + break; + case CACR: + tmpreg=0x002; + break; + case USP: + tmpreg=0x800; + break; + case VBR: + tmpreg=0x801; + break; + case CAAR: + tmpreg=0x802; + break; + case MSP: + tmpreg=0x803; + break; + case ISP: + tmpreg=0x804; + break; + default: + abort(); + } + install_operand(s[1],tmpreg); + break; + + case 'k': + tmpreg=get_num(opP->con1,55); + install_operand(s[1],tmpreg&0x7f); + break; + + case 'l': + tmpreg=opP->reg; + if(s[1]=='w') { + if(tmpreg&0x7FF0000) + as_bad("Floating point register in register list"); + insop(reverse_16_bits(tmpreg)); + } else { + if(tmpreg&0x700FFFF) + as_bad("Wrong register in floating-point reglist"); + install_operand(s[1],reverse_8_bits(tmpreg>>16)); + } + break; + + case 'L': + tmpreg=opP->reg; + if(s[1]=='w') { + if(tmpreg&0x7FF0000) + as_bad("Floating point register in register list"); + insop(tmpreg); + } else if(s[1]=='8') { + if(tmpreg&0x0FFFFFF) + as_bad("incorrect register in reglist"); + install_operand(s[1],tmpreg>>24); + } else { + if(tmpreg&0x700FFFF) + as_bad("wrong register in floating-point reglist"); + else + install_operand(s[1],tmpreg>>16); + } + break; + + case 'M': + install_operand(s[1],get_num(opP->con1,60)); + break; + + case 'O': + tmpreg= (opP->mode==DREG) + ? 0x20+opP->reg-DATA + : (get_num(opP->con1,40)&0x1F); + install_operand(s[1],tmpreg); + break; + + case 'Q': + tmpreg=get_num(opP->con1,10); + if(tmpreg==8) + tmpreg=0; + install_operand(s[1],tmpreg); + break; + + case 'R': + /* This depends on the fact that ADDR registers are + eight more than their corresponding DATA regs, so + the result will have the ADDR_REG bit set */ + install_operand(s[1],opP->reg-DATA); + break; + + case 's': + if(opP->reg==FPI) tmpreg=0x1; + else if(opP->reg==FPS) tmpreg=0x2; + else if(opP->reg==FPC) tmpreg=0x4; + else abort(); + install_operand(s[1],tmpreg); + break; + + case 'S': /* Ignore it */ + break; + + case 'T': + install_operand(s[1],get_num(opP->con1,30)); + break; + + case 'U': /* Ignore it */ + break; + +#ifdef m68851 + /* JF: These are out of order, I fear. */ + case 'f': + switch (opP->reg) { + case SFC: + tmpreg=0; + break; + case DFC: + tmpreg=1; + break; + default: + abort(); + } + install_operand(s[1],tmpreg); + break; + + case 'P': + switch(opP->reg) { + case TC: + tmpreg=0; + break; + case CAL: + tmpreg=4; + break; + case VAL: + tmpreg=5; + break; + case SCC: + tmpreg=6; + break; + case AC: + tmpreg=7; + break; + default: + abort(); + } + install_operand(s[1],tmpreg); + break; + + case 'V': + if (opP->reg == VAL) + break; + abort(); + + case 'W': + switch(opP->reg) { + + case DRP: + tmpreg=1; + break; + case SRP: + tmpreg=2; + break; + case CRP: + tmpreg=3; + break; + default: + abort(); + } + install_operand(s[1],tmpreg); + break; + + case 'X': + switch (opP->reg) { + case BAD: case BAD+1: case BAD+2: case BAD+3: + case BAD+4: case BAD+5: case BAD+6: case BAD+7: + tmpreg = (4 << 10) | ((opP->reg - BAD) << 2); + break; + + case BAC: case BAC+1: case BAC+2: case BAC+3: + case BAC+4: case BAC+5: case BAC+6: case BAC+7: + tmpreg = (5 << 10) | ((opP->reg - BAC) << 2); + break; + + default: + abort(); + } + install_operand(s[1], tmpreg); + break; + case 'Y': + if (opP->reg == PSR) + break; + abort(); + + case 'Z': + if (opP->reg == PCSR) + break; + abort(); +#endif /* m68851 */ + default: + as_fatal("Internal error: Operand type %c unknown in line %s of file \"%s\"", s[0], __LINE__, __FILE__); + } + } + /* By the time whe get here (FINALLY) the_ins contains the complete + instruction, ready to be emitted. . . */ +} + +static int get_regs(i,str,opP) +int i; +struct m68k_op *opP; +char *str; +{ + /* 26, 25, 24, 23-16, 15-8, 0-7 */ + /* Low order 24 bits encoded fpc,fps,fpi,fp7-fp0,a7-a0,d7-d0 */ + unsigned long cur_regs = 0; + int reg1, + reg2; + +#define ADD_REG(x) { if(x==FPI) cur_regs|=(1<<24);\ + else if(x==FPS) cur_regs|=(1<<25);\ + else if(x==FPC) cur_regs|=(1<<26);\ + else cur_regs|=(1<<(x-1)); } + + reg1=i; + for(;;) { + if(*str=='/') { + ADD_REG(reg1); + str++; + } else if(*str=='-') { + str++; + reg2=m68k_reg_parse(&str); + if(reg2<DATA || reg2>=FPREG+8 || reg1==FPI || reg1==FPS || reg1==FPC) { + opP->error="unknown register in register list"; + return FAIL; + } + while(reg1<=reg2) { + ADD_REG(reg1); + reg1++; + } + if(*str=='\0') + break; + } else if(*str=='\0') { + ADD_REG(reg1); + break; + } else { + opP->error="unknow character in register list"; + return FAIL; + } +/* DJA -- Bug Fix. Did't handle d1-d2/a1 until the following instruction was added */ + if (*str=='/') + str ++; + reg1=m68k_reg_parse(&str); + if((reg1<DATA || reg1>=FPREG+8) && !(reg1==FPI || reg1==FPS || reg1==FPC)) { + opP->error="unknown register in register list"; + return FAIL; + } + } + opP->reg=cur_regs; + return OK; +} /* get_regs() */ + +static int reverse_16_bits(in) +int in; +{ + int out=0; + int n; + + static int mask[16] = { +0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080, +0x0100,0x0200,0x0400,0x0800,0x1000,0x2000,0x4000,0x8000 + }; + for(n=0;n<16;n++) { + if(in&mask[n]) + out|=mask[15-n]; + } + return out; +} /* reverse_16_bits() */ + +static int reverse_8_bits(in) +int in; +{ + int out=0; + int n; + + static int mask[8] = { +0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080, + }; + + for(n=0;n<8;n++) { + if(in&mask[n]) + out|=mask[7-n]; + } + return out; +} /* reverse_8_bits() */ + +static void install_operand(mode,val) +int mode; +int val; +{ + switch(mode) { + case 's': + the_ins.opcode[0]|=val & 0xFF; /* JF FF is for M kludge */ + break; + case 'd': + the_ins.opcode[0]|=val<<9; + break; + case '1': + the_ins.opcode[1]|=val<<12; + break; + case '2': + the_ins.opcode[1]|=val<<6; + break; + case '3': + the_ins.opcode[1]|=val; + break; + case '4': + the_ins.opcode[2]|=val<<12; + break; + case '5': + the_ins.opcode[2]|=val<<6; + break; + case '6': + /* DANGER! This is a hack to force cas2l and cas2w cmds + to be three words long! */ + the_ins.numo++; + the_ins.opcode[2]|=val; + break; + case '7': + the_ins.opcode[1]|=val<<7; + break; + case '8': + the_ins.opcode[1]|=val<<10; + break; +#ifdef m68851 + case '9': + the_ins.opcode[1]|=val<<5; + break; +#endif + + case 't': + the_ins.opcode[1]|=(val<<10)|(val<<7); + break; + case 'D': + the_ins.opcode[1]|=(val<<12)|val; + break; + case 'g': + the_ins.opcode[0]|=val=0xff; + break; + case 'i': + the_ins.opcode[0]|=val<<9; + break; + case 'C': + the_ins.opcode[1]|=val; + break; + case 'j': + the_ins.opcode[1]|=val; + the_ins.numo++; /* What a hack */ + break; + case 'k': + the_ins.opcode[1]|=val<<4; + break; + case 'b': + case 'w': + case 'l': + break; + case 'c': + default: + abort(); + } +} /* install_operand() */ + +static void install_gen_operand(mode,val) +int mode; +int val; +{ + switch(mode) { + case 's': + the_ins.opcode[0]|=val; + break; + case 'd': + /* This is a kludge!!! */ + the_ins.opcode[0]|=(val&0x07)<<9|(val&0x38)<<3; + break; + case 'b': + case 'w': + case 'l': + case 'f': + case 'F': + case 'x': + case 'p': + the_ins.opcode[0]|=val; + break; + /* more stuff goes here */ + default: + abort(); + } +} /* install_gen_operand() */ + +static char *crack_operand(str,opP) +register char *str; +register struct m68k_op *opP; +{ + register int parens; + register int c; + register char *beg_str; + + if(!str) { + return str; + } + beg_str=str; + for(parens=0;*str && (parens>0 || notend(str));str++) { + if(*str=='(') parens++; + else if(*str==')') { + if(!parens) { /* ERROR */ + opP->error="Extra )"; + return str; + } + --parens; + } + } + if(!*str && parens) { /* ERROR */ + opP->error="Missing )"; + return str; + } + c= *str; + *str='\0'; + if(m68k_ip_op(beg_str,opP)==FAIL) { + *str=c; + return str; + } + *str=c; + if(c=='}') + c= *++str; /* JF bitfield hack */ + if(c) { + c= *++str; + if(!c) + as_bad("Missing operand"); + } + return str; +} + +/* See the comment up above where the #define notend(... is */ +#if 0 +notend(s) +char *s; +{ + if(*s==',') return 0; + if(*s=='{' || *s=='}') + return 0; + if(*s!=':') return 1; + /* This kludge here is for the division cmd, which is a kludge */ + if(strchr("aAdD#",s[1])) return 0; + return 1; +} +#endif + +/* This is the guts of the machine-dependent assembler. STR points to a + machine dependent instruction. This funciton is supposed to emit + the frags/bytes it assembles to. + */ +void +md_assemble(str) +char *str; +{ + char *er; + short *fromP; + char *toP; + int m,n; + char *to_beg_P; + int shorts_this_frag; + + bzero((char *)(&the_ins),sizeof(the_ins)); /* JF for paranoia sake */ + m68_ip(str); + er=the_ins.error; + if(!er) { + for(n=the_ins.numargs;n;--n) + if(the_ins.operands[n].error) { + er=the_ins.operands[n].error; + break; + } + } + if(er) { + as_bad("\"%s\" -- Statement '%s' ignored",er,str); + return; + } + + if(the_ins.nfrag==0) { /* No frag hacking involved; just put it out */ + toP=frag_more(2*the_ins.numo); + fromP= &the_ins.opcode[0]; + for(m=the_ins.numo;m;--m) { + md_number_to_chars(toP,(long)(*fromP),2); + toP+=2; + fromP++; + } + /* put out symbol-dependent info */ + for(m=0;m<the_ins.nrel;m++) { + switch(the_ins.reloc[m].wid) { + case 'B': + n=1; + break; + case 'b': + n=1; + break; + case '3': + n=2; + break; + case 'w': + n=2; + break; + case 'l': + n=4; + break; + default: + as_fatal("Don't know how to figure width of %c in md_assemble()",the_ins.reloc[m].wid); + } + + fix_new(frag_now, + (toP-frag_now->fr_literal)-the_ins.numo*2+the_ins.reloc[m].n, + n, + the_ins.reloc[m].add, + the_ins.reloc[m].sub, + the_ins.reloc[m].off, + the_ins.reloc[m].pcrel, + NO_RELOC); + } + return; + } + + /* There's some frag hacking */ + for(n=0,fromP= &the_ins.opcode[0];n<the_ins.nfrag;n++) { + int wid; + + if(n==0) wid=2*the_ins.fragb[n].fragoff; + else wid=2*(the_ins.numo-the_ins.fragb[n-1].fragoff); + toP=frag_more(wid); + to_beg_P=toP; + shorts_this_frag=0; + for(m=wid/2;m;--m) { + md_number_to_chars(toP,(long)(*fromP),2); + toP+=2; + fromP++; + shorts_this_frag++; + } + for(m=0;m<the_ins.nrel;m++) { + if((the_ins.reloc[m].n)>= 2*shorts_this_frag /* 2*the_ins.fragb[n].fragoff */) { + the_ins.reloc[m].n-= 2*shorts_this_frag /* 2*the_ins.fragb[n].fragoff */; + break; + } + wid=the_ins.reloc[m].wid; + if(wid==0) + continue; + the_ins.reloc[m].wid=0; + wid = (wid=='b') ? 1 : (wid=='w') ? 2 : (wid=='l') ? 4 : 4000; + + fix_new(frag_now, + (toP-frag_now->fr_literal)-the_ins.numo*2+the_ins.reloc[m].n, + wid, + the_ins.reloc[m].add, + the_ins.reloc[m].sub, + the_ins.reloc[m].off, + the_ins.reloc[m].pcrel, + NO_RELOC); + } + know(the_ins.fragb[n].fadd); + (void)frag_var(rs_machine_dependent,10,0,(relax_substateT)(the_ins.fragb[n].fragty), + the_ins.fragb[n].fadd,the_ins.fragb[n].foff,to_beg_P); + } + n=(the_ins.numo-the_ins.fragb[n-1].fragoff); + shorts_this_frag=0; + if(n) { + toP=frag_more(n*sizeof(short)); + while(n--) { + md_number_to_chars(toP,(long)(*fromP),2); + toP+=2; + fromP++; + shorts_this_frag++; + } + } + for(m=0;m<the_ins.nrel;m++) { + int wid; + + wid=the_ins.reloc[m].wid; + if(wid==0) + continue; + the_ins.reloc[m].wid=0; + wid = (wid=='b') ? 1 : (wid=='w') ? 2 : (wid=='l') ? 4 : 4000; + + fix_new(frag_now, + (the_ins.reloc[m].n + toP-frag_now->fr_literal)-/* the_ins.numo */ shorts_this_frag*2, + wid, + the_ins.reloc[m].add, + the_ins.reloc[m].sub, + the_ins.reloc[m].off, + the_ins.reloc[m].pcrel, + NO_RELOC); + } +} + +/* This function is called once, at assembler startup time. This should + set up all the tables, etc that the MD part of the assembler needs + */ +void +md_begin() +{ +/* + * md_begin -- set up hash tables with 68000 instructions. + * similar to what the vax assembler does. ---phr + */ + /* RMS claims the thing to do is take the m68k-opcode.h table, and make + a copy of it at runtime, adding in the information we want but isn't + there. I think it'd be better to have an awk script hack the table + at compile time. Or even just xstr the table and use it as-is. But + my lord ghod hath spoken, so we do it this way. Excuse the ugly var + names. */ + + register const struct m68k_opcode *ins; + register struct m68_incant *hack, + *slak; + register char *retval = 0; /* empty string, or error msg text */ + register unsigned int i; + register char c; + + if ((op_hash = hash_new()) == NULL) + as_fatal("Virtual memory exhausted"); + + obstack_begin(&robyn,4000); + for (ins = m68k_opcodes; ins < endop; ins++) { + hack=slak=(struct m68_incant *)obstack_alloc(&robyn,sizeof(struct m68_incant)); + do { + slak->m_operands=ins->args; + slak->m_opnum=strlen(slak->m_operands)/2; + slak->m_opcode=ins->opcode; + /* This is kludgey */ + slak->m_codenum=((ins->match)&0xffffL) ? 2 : 1; + if((ins+1)!=endop && !strcmp(ins->name,(ins+1)->name)) { + slak->m_next=(struct m68_incant *) +obstack_alloc(&robyn,sizeof(struct m68_incant)); + ins++; + } else + slak->m_next=0; + slak=slak->m_next; + } while(slak); + + retval = hash_insert (op_hash, ins->name,(char *)hack); + /* Didn't his mommy tell him about null pointers? */ + if(retval && *retval) + as_fatal("Internal Error: Can't hash %s: %s", ins->name,retval); + } + + for (i = 0; i < sizeof(mklower_table) ; i++) + mklower_table[i] = (isupper(c = (char) i)) ? tolower(c) : c; + + for (i = 0 ; i < sizeof(notend_table) ; i++) { + notend_table[i] = 0; + alt_notend_table[i] = 0; + } + notend_table[','] = 1; + notend_table['{'] = 1; + notend_table['}'] = 1; + alt_notend_table['a'] = 1; + alt_notend_table['A'] = 1; + alt_notend_table['d'] = 1; + alt_notend_table['D'] = 1; + alt_notend_table['#'] = 1; + alt_notend_table['f'] = 1; + alt_notend_table['F'] = 1; +#ifdef REGISTER_PREFIX + alt_notend_table[REGISTER_PREFIX] = 1; +#endif +} + +#if 0 +#define notend(s) ((*s == ',' || *s == '}' || *s == '{' \ + || (*s == ':' && strchr("aAdD#", s[1]))) \ + ? 0 : 1) +#endif + +/* This funciton is called once, before the assembler exits. It is + supposed to do any final cleanup for this part of the assembler. + */ +void +md_end() +{ +} + +/* Equal to MAX_PRECISION in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +/* Turn a string in input_line_pointer into a floating point constant of type + type, and store the appropriate bytes in *litP. The number of LITTLENUMS + emitted is stored in *sizeP . An error message is returned, or NULL on OK. + */ +char * +md_atof(type,litP,sizeP) +char type; +char *litP; +int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + + switch(type) { + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP=0; + return "Bad call to MD_ATOF()"; + } + t=atof_ieee(input_line_pointer,type,words); + if(t) + input_line_pointer=t; + + *sizeP=prec * sizeof(LITTLENUM_TYPE); + for(wordP=words;prec--;) { + md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE)); + litP+=sizeof(LITTLENUM_TYPE); + } + return ""; /* Someone should teach Dean about null pointers */ +} + +/* Turn an integer of n bytes (in val) into a stream of bytes appropriate + for use in the a.out file, and stores them in the array pointed to by buf. + This knows about the endian-ness of the target machine and does + THE RIGHT THING, whatever it is. Possible values for n are 1 (byte) + 2 (short) and 4 (long) Floating numbers are put out as a series of + LITTLENUMS (shorts, here at least) + */ +void +md_number_to_chars(buf,val,n) +char *buf; +long val; +int n; +{ + switch(n) { + case 1: + *buf++=val; + break; + case 2: + *buf++=(val>>8); + *buf++=val; + break; + case 4: + *buf++=(val>>24); + *buf++=(val>>16); + *buf++=(val>>8); + *buf++=val; + break; + default: + abort(); + } +} + +void +md_apply_fix(fixP, val) + fixS *fixP; + long val; +{ + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + + switch(fixP->fx_size) { + case 1: + *buf++=val; + break; + case 2: + *buf++=(val>>8); + *buf++=val; + break; + case 4: + *buf++=(val>>24); + *buf++=(val>>16); + *buf++=(val>>8); + *buf++=val; + break; + default: + BAD_CASE (fixP->fx_size); + } +} + + +/* *fragP has been relaxed to its final size, and now needs to have + the bytes inside it modified to conform to the new size There is UGLY + MAGIC here. .. + */ +void +md_convert_frag(fragP) +register fragS *fragP; +{ + long disp; + long ext; + + /* Address in gas core of the place to store the displacement. */ + register char *buffer_address = fragP -> fr_fix + fragP -> fr_literal; + /* Address in object code of the displacement. */ + register int object_address = fragP -> fr_fix + fragP -> fr_address; + + know(fragP->fr_symbol); + + /* The displacement of the address, from current location. */ + disp = (S_GET_VALUE(fragP->fr_symbol) + fragP->fr_offset) - object_address; + + switch(fragP->fr_subtype) { + case TAB(BCC68000,BYTE): + case TAB(BRANCH,BYTE): + know(issbyte(disp)); + if(disp==0) + as_bad("short branch with zero offset: use :w"); + fragP->fr_opcode[1]=disp; + ext=0; + break; + case TAB(DBCC,SHORT): + know(issword(disp)); + ext=2; + break; + case TAB(BCC68000,SHORT): + case TAB(BRANCH,SHORT): + know(issword(disp)); + fragP->fr_opcode[1]=0x00; + ext=2; + break; + case TAB(BRANCH,LONG): + if(flagseen['m']) { + if(fragP->fr_opcode[0]==0x61) { + fragP->fr_opcode[0]= 0x4E; + fragP->fr_opcode[1]= 0xB9; /* JSR with ABSL LONG offset */ + subseg_change(SEG_TEXT, 0); + + fix_new(fragP, + fragP->fr_fix, + 4, + fragP->fr_symbol, + 0, + fragP->fr_offset, + 0, + NO_RELOC); + + fragP->fr_fix+=4; + ext=0; + } else if(fragP->fr_opcode[0]==0x60) { + fragP->fr_opcode[0]= 0x4E; + fragP->fr_opcode[1]= 0xF9; /* JMP with ABSL LONG offset */ + subseg_change(SEG_TEXT, 0); + fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0, fragP->fr_offset,0, + NO_RELOC); + fragP->fr_fix+=4; + ext=0; + }else { + as_bad("Long branch offset not supported."); + } + } else { + fragP->fr_opcode[1]=0xff; + ext=4; + } + break; + case TAB(BCC68000,LONG): + /* only Bcc 68000 instructions can come here */ + /* change bcc into b!cc/jmp absl long */ + fragP->fr_opcode[0] ^= 0x01; /* invert bcc */ + fragP->fr_opcode[1] = 0x6; /* branch offset = 6 */ + + /* JF: these used to be fr_opcode[2,3], but they may be in a + different frag, in which case refering to them is a no-no. + Only fr_opcode[0,1] are guaranteed to work. */ + *buffer_address++ = 0x4e; /* put in jmp long (0x4ef9) */ + *buffer_address++ = 0xf9; + fragP->fr_fix += 2; /* account for jmp instruction */ + subseg_change(SEG_TEXT,0); + fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0, + fragP->fr_offset,0, + NO_RELOC); + fragP->fr_fix += 4; + ext=0; + break; + case TAB(DBCC,LONG): + /* only DBcc 68000 instructions can come here */ + /* change dbcc into dbcc/jmp absl long */ + /* JF: these used to be fr_opcode[2-7], but that's wrong */ + *buffer_address++ = 0x00; /* branch offset = 4 */ + *buffer_address++ = 0x04; + *buffer_address++ = 0x60; /* put in bra pc+6 */ + *buffer_address++ = 0x06; + *buffer_address++ = 0x4e; /* put in jmp long (0x4ef9) */ + *buffer_address++ = 0xf9; + + fragP->fr_fix += 6; /* account for bra/jmp instructions */ + subseg_change(SEG_TEXT,0); + fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0, + fragP->fr_offset,0, + NO_RELOC); + fragP->fr_fix += 4; + ext=0; + break; + case TAB(FBRANCH,SHORT): + know((fragP->fr_opcode[1]&0x40)==0); + ext=2; + break; + case TAB(FBRANCH,LONG): + fragP->fr_opcode[1]|=0x40; /* Turn on LONG bit */ + ext=4; + break; + case TAB(PCREL,SHORT): + ext=2; + break; + case TAB(PCREL,LONG): + /* FIXME-SOMEDAY, this should allow pcrel-long to be generated if -pic is on. + Else we can't handle position independent code. Pcrel-long costs an + extra index word though. Doing it requires more relax tables and + stuff elsewhere in this module though. */ + /* The thing to do here is force it to ABSOLUTE LONG, since + PCREL is really trying to shorten an ABSOLUTE address anyway */ + subseg_change(SEG_TEXT,0); + fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0, fragP->fr_offset, 0, + NO_RELOC); + if((fragP->fr_opcode[1] & 0x3F) != 0x3A) + as_bad("Internal error (long PC-relative operand) for insn 0x%04lx at 0x%lx", + fragP->fr_opcode[0],fragP->fr_address); + fragP->fr_opcode[1]&= ~0x3F; + fragP->fr_opcode[1]|=0x39; /* Mode 7.1 */ + fragP->fr_fix+=4; + ext=0; + break; + case TAB(PCLEA,SHORT): + subseg_change(SEG_TEXT,0); + fix_new(fragP,(int)(fragP->fr_fix),2,fragP->fr_symbol,(symbolS *)0,fragP->fr_offset,1, + NO_RELOC); + fragP->fr_opcode[1] &= ~0x3F; + fragP->fr_opcode[1] |= 0x3A; + ext=2; + break; + case TAB(PCLEA,LONG): + subseg_change(SEG_TEXT,0); + fix_new(fragP,(int)(fragP->fr_fix)+2,4,fragP->fr_symbol,(symbolS *)0,fragP->fr_offset+2,1, + NO_RELOC); + *buffer_address++ = 0x01; + *buffer_address++ = 0x70; + fragP->fr_fix+=2; + /* buffer_address+=2; */ + ext=4; + break; + + } + if(ext) { + md_number_to_chars(buffer_address,(long)disp,(int)ext); + fragP->fr_fix+=ext; + } +} + +/* Force truly undefined symbols to their maximum size, and generally set up + the frag list to be relaxed + */ +int md_estimate_size_before_relax(fragP, segment) +register fragS *fragP; +segT segment; +{ + int old_fix; + register char *buffer_address = fragP -> fr_fix + fragP -> fr_literal; + + old_fix=fragP->fr_fix; + + /* handle SZ_UNDEF first, it can be changed to BYTE or SHORT */ + switch(fragP->fr_subtype) { + case TAB(BRANCH,SZ_UNDEF): + if(S_GET_SEGMENT(fragP->fr_symbol) == segment) { + /* Symbol now defined; start at byte-size. */ + fragP->fr_subtype=TAB(TABTYPE(fragP->fr_subtype),BYTE); + break; + } else if(!flagseen['p'] || (!flagseen['l'] && flagseen['m'])) { + /* Symbol in another segment, or undef. + If we don't care about position independent code, + or if we're using long displacements on a 68000, + rewrite to short or long absolute. */ + if(fragP->fr_opcode[0]==0x61) { + if(flagseen['l']) { + fragP->fr_opcode[0]= 0x4E; + fragP->fr_opcode[1]= 0xB9; /* JBSR with ABSL WORD offset */ + subseg_change(SEG_TEXT, 0); + fix_new(fragP, fragP->fr_fix, 2, + fragP->fr_symbol, 0, fragP->fr_offset, 0, + NO_RELOC); + fragP->fr_fix+=2; + } else { + fragP->fr_opcode[0]= 0x4E; + fragP->fr_opcode[1]= 0xB9; /* JBSR with ABSL LONG offset */ + subseg_change(SEG_TEXT, 0); + fix_new(fragP, fragP->fr_fix, 4, + fragP->fr_symbol, 0, fragP->fr_offset, 0, + NO_RELOC); + fragP->fr_fix+=4; + } + frag_wane(fragP); + } else if(fragP->fr_opcode[0]==0x60) { + if(flagseen['l']) { + fragP->fr_opcode[0]= 0x4E; + fragP->fr_opcode[1]= 0xF8; /* JMP with ABSL WORD offset */ + subseg_change(SEG_TEXT, 0); + fix_new(fragP, fragP->fr_fix, 2, + fragP->fr_symbol, 0, fragP->fr_offset, 0, + NO_RELOC); + fragP->fr_fix+=2; + } else { + fragP->fr_opcode[0]= 0x4E; + fragP->fr_opcode[1]= 0xF9; /* JMP with ABSL LONG offset */ + subseg_change(SEG_TEXT, 0); + fix_new(fragP, fragP->fr_fix, 4, + fragP->fr_symbol, 0, fragP->fr_offset, 0, + NO_RELOC); + fragP->fr_fix+=4; + } + frag_wane(fragP); + } else { + as_bad("Long branch offset to extern symbol not supported."); + } + } else if(flagseen['l']) { + /* Symbol in other seg or undefined, and user + wants short pcrel offsets (-l). Set size to 2, fix + pcrel displacement after relax. */ + fix_new(fragP,(int)(fragP->fr_fix),2,fragP->fr_symbol, + (symbolS *)0,fragP->fr_offset+2,1, + NO_RELOC); + fragP->fr_fix+=2; + fragP->fr_opcode[1]=0x00; + frag_wane(fragP); + } else { + /* Symbol in other seg or undefined, and user + wants long pcrel offsets. Set size to 4, and fix + pcrel displacement after relax. */ + fix_new(fragP,(int)(fragP->fr_fix),4,fragP->fr_symbol, + (symbolS *)0,fragP->fr_offset + 4,1, + NO_RELOC); + fragP->fr_fix+=4; + fragP->fr_opcode[1]=0xff; + frag_wane(fragP); + break; + } + break; + + case TAB(FBRANCH,SZ_UNDEF): + if(S_GET_SEGMENT(fragP->fr_symbol) == segment + || flagseen['l']) { + fragP->fr_subtype=TAB(FBRANCH,SHORT); + fragP->fr_var+=2; + } else { + fragP->fr_subtype=TAB(FBRANCH,LONG); + fragP->fr_var+=4; + } + break; + + case TAB(PCREL,SZ_UNDEF): + if(S_GET_SEGMENT(fragP->fr_symbol) == segment + || flagseen['l']) { + fragP->fr_subtype=TAB(PCREL,SHORT); + fragP->fr_var+=2; + } else { + fragP->fr_subtype=TAB(PCREL,LONG); + fragP->fr_var+=4; + } + break; + + case TAB(BCC68000,SZ_UNDEF): + if(S_GET_SEGMENT(fragP->fr_symbol) == segment) { + fragP->fr_subtype=TAB(BCC68000,BYTE); + break; + } + /* only Bcc 68000 instructions can come here */ + /* change bcc into b!cc/jmp absl long */ + fragP->fr_opcode[0] ^= 0x01; /* invert bcc */ + if(flagseen['l']) { + fragP->fr_opcode[1] = 0x04; /* branch offset = 6 */ + /* JF: these were fr_opcode[2,3] */ + buffer_address[0] = 0x4e; /* put in jmp long (0x4ef9) */ + buffer_address[1] = 0xf8; + fragP->fr_fix += 2; /* account for jmp instruction */ + subseg_change(SEG_TEXT,0); + fix_new(fragP, fragP->fr_fix, 2, fragP->fr_symbol, 0, + fragP->fr_offset,0, + NO_RELOC); + fragP->fr_fix += 2; + } else { + fragP->fr_opcode[1] = 0x06; /* branch offset = 6 */ + /* JF: these were fr_opcode[2,3] */ + buffer_address[2] = 0x4e; /* put in jmp long (0x4ef9) */ + buffer_address[3] = 0xf9; + fragP->fr_fix += 2; /* account for jmp instruction */ + subseg_change(SEG_TEXT,0); + fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0, + fragP->fr_offset,0, + NO_RELOC); + fragP->fr_fix += 4; + } + frag_wane(fragP); + break; + + case TAB(DBCC,SZ_UNDEF): + if(S_GET_SEGMENT(fragP->fr_symbol) == segment) { + fragP->fr_subtype=TAB(DBCC,SHORT); + fragP->fr_var+=2; + break; + } + /* only DBcc 68000 instructions can come here */ + /* change dbcc into dbcc/jmp absl long */ + /* JF: these used to be fr_opcode[2-4], which is wrong. */ + buffer_address[0] = 0x00; /* branch offset = 4 */ + buffer_address[1] = 0x04; + buffer_address[2] = 0x60; /* put in bra pc + ... */ + if(flagseen['l']) { + /* JF: these were fr_opcode[5-7] */ + buffer_address[3] = 0x04; /* plus 4 */ + buffer_address[4] = 0x4e;/* Put in Jump Word */ + buffer_address[5] = 0xf8; + fragP->fr_fix += 6; /* account for bra/jmp instruction */ + subseg_change(SEG_TEXT,0); + fix_new(fragP, fragP->fr_fix, 2, fragP->fr_symbol, 0, + fragP->fr_offset,0, + NO_RELOC); + fragP->fr_fix+=2; + } else { + /* JF: these were fr_opcode[5-7] */ + buffer_address[3] = 0x06; /* Plus 6 */ + buffer_address[4] = 0x4e; /* put in jmp long (0x4ef9) */ + buffer_address[5] = 0xf9; + fragP->fr_fix += 6; /* account for bra/jmp instruction */ + subseg_change(SEG_TEXT,0); + fix_new(fragP, fragP->fr_fix, 4, fragP->fr_symbol, 0, + fragP->fr_offset,0, + NO_RELOC); + fragP->fr_fix += 4; + } + frag_wane(fragP); + break; + + case TAB(PCLEA,SZ_UNDEF): + if((S_GET_SEGMENT(fragP->fr_symbol))==segment || flagseen['l']) { + fragP->fr_subtype=TAB(PCLEA,SHORT); + fragP->fr_var+=2; + } else { + fragP->fr_subtype=TAB(PCLEA,LONG); + fragP->fr_var+=6; + } + break; + + default: + break; + } + + /* now that SZ_UNDEF are taken care of, check others */ + switch(fragP->fr_subtype) { + case TAB(BCC68000,BYTE): + case TAB(BRANCH,BYTE): + /* We can't do a short jump to the next instruction, + so we force word mode. */ + if(fragP->fr_symbol && S_GET_VALUE(fragP->fr_symbol)==0 && + fragP->fr_symbol->sy_frag==fragP->fr_next) { + fragP->fr_subtype=TAB(TABTYPE(fragP->fr_subtype),SHORT); + fragP->fr_var+=2; + } + break; + default: + break; + } + return fragP->fr_var + fragP->fr_fix - old_fix; +} + +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) +/* the bit-field entries in the relocation_info struct plays hell + with the byte-order problems of cross-assembly. So as a hack, + I added this mach. dependent ri twiddler. Ugly, but it gets + you there. -KWK */ +/* on m68k: first 4 bytes are normal unsigned long, next three bytes +are symbolnum, most sig. byte first. Last byte is broken up with +bit 7 as pcrel, bits 6 & 5 as length, bit 4 as pcrel, and the lower +nibble as nuthin. (on Sun 3 at least) */ +/* Translate the internal relocation information into target-specific + format. */ +void +md_ri_to_chars(the_bytes, ri) + char *the_bytes; + struct reloc_info_generic *ri; +{ + /* this is easy */ + md_number_to_chars(the_bytes, ri->r_address, 4); + /* now the fun stuff */ + the_bytes[4] = (ri->r_symbolnum >> 16) & 0x0ff; + the_bytes[5] = (ri->r_symbolnum >> 8) & 0x0ff; + the_bytes[6] = ri->r_symbolnum & 0x0ff; + the_bytes[7] = (((ri->r_pcrel << 7) & 0x80) | ((ri->r_length << 5) & 0x60) | + ((ri->r_extern << 4) & 0x10)); +} +#endif /* OBJ_AOUT or OBJ_BOUT */ + +#ifndef WORKING_DOT_WORD +const int md_short_jump_size = 4; +const int md_long_jump_size = 6; + +void +md_create_short_jump(ptr,from_addr,to_addr,frag,to_symbol) +char *ptr; +long from_addr, + to_addr; +fragS *frag; +symbolS *to_symbol; +{ + long offset; + + offset = to_addr - (from_addr+2); + + md_number_to_chars(ptr ,(long)0x6000,2); + md_number_to_chars(ptr+2,(long)offset,2); +} + +void +md_create_long_jump(ptr,from_addr,to_addr,frag,to_symbol) +char *ptr; +long from_addr, + to_addr; +fragS *frag; +symbolS *to_symbol; +{ + long offset; + + if(flagseen['m']) { + offset=to_addr-S_GET_VALUE(to_symbol); + md_number_to_chars(ptr ,(long)0x4EF9,2); + md_number_to_chars(ptr+2,(long)offset,4); + fix_new(frag,(ptr+2)-frag->fr_literal,4,to_symbol,(symbolS *)0,(long)0,0, + NO_RELOC); + } else { + offset=to_addr - (from_addr+2); + md_number_to_chars(ptr ,(long)0x60ff,2); + md_number_to_chars(ptr+2,(long)offset,4); + } +} + +#endif +/* Different values of OK tell what its OK to return. Things that aren't OK are an error (what a shock, no?) + + 0: Everything is OK + 10: Absolute 1:8 only + 20: Absolute 0:7 only + 30: absolute 0:15 only + 40: Absolute 0:31 only + 50: absolute 0:127 only + 55: absolute -64:63 only + 60: absolute -128:127 only + 70: absolute 0:4095 only + 80: No bignums + +*/ + +static int get_num(exp,ok) +struct m68k_exp *exp; +int ok; +{ +#ifdef TEST2 + long l = 0; + + if(!exp->e_beg) + return 0; + if(*exp->e_beg=='0') { + if(exp->e_beg[1]=='x') + sscanf(exp->e_beg+2,"%x",&l); + else + sscanf(exp->e_beg+1,"%O",&l); + return l; + } + return atol(exp->e_beg); +#else + char *save_in; + char c_save; + + if(!exp) { + /* Can't do anything */ + return 0; + } + if(!exp->e_beg || !exp->e_end) { + seg(exp)=SEG_ABSOLUTE; + adds(exp)=0; + subs(exp)=0; + offs(exp)= (ok==10) ? 1 : 0; + as_bad("Null expression defaults to %ld", offs(exp)); + return 0; + } + + exp->e_siz=0; + if(/* ok!=80 && */exp->e_end[-1]==':' && (exp->e_end-exp->e_beg)>=2) { + switch(exp->e_end[0]) { + case 's': + case 'S': + case 'b': + case 'B': + exp->e_siz=1; + break; + case 'w': + case 'W': + exp->e_siz=2; + break; + case 'l': + case 'L': + exp->e_siz=3; + break; + default: + as_bad("Unknown size for expression \"%c\"", exp->e_end[0]); + } + exp->e_end-=2; + } + c_save=exp->e_end[1]; + exp->e_end[1]='\0'; + save_in=input_line_pointer; + input_line_pointer=exp->e_beg; + switch(expression(&(exp->e_exp))) { + case SEG_PASS1: + seg(exp)=SEG_ABSOLUTE; + adds(exp)=0; + subs(exp)=0; + offs(exp)= (ok==10) ? 1 : 0; + as_bad("Unknown expression: '%s' defaulting to %d",exp->e_beg,offs(exp)); + break; + + case SEG_ABSENT: + /* Do the same thing the VAX asm does */ + seg(exp)=SEG_ABSOLUTE; + adds(exp)=0; + subs(exp)=0; + offs(exp)=0; + if(ok==10) { + as_bad("expression out of range: defaulting to 1"); + offs(exp)=1; + } + break; + case SEG_ABSOLUTE: + switch(ok) { + case 10: + if(offs(exp)<1 || offs(exp)>8) { + as_bad("expression out of range: defaulting to 1"); + offs(exp)=1; + } + break; + case 20: + if(offs(exp)<0 || offs(exp)>7) + goto outrange; + break; + case 30: + if(offs(exp)<0 || offs(exp)>15) + goto outrange; + break; + case 40: + if(offs(exp)<0 || offs(exp)>32) + goto outrange; + break; + case 50: + if(offs(exp)<0 || offs(exp)>127) + goto outrange; + break; + case 55: + if(offs(exp)<-64 || offs(exp)>63) + goto outrange; + break; + case 60: + if(offs(exp)<-128 || offs(exp)>127) + goto outrange; + break; + case 70: + if(offs(exp)<0 || offs(exp)>4095) { + outrange: + as_bad("expression out of range: defaulting to 0"); + offs(exp)=0; + } + break; + default: + break; + } + break; + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_UNKNOWN: + case SEG_DIFFERENCE: + if(ok>=10 && ok<=70) { + seg(exp)=SEG_ABSOLUTE; + adds(exp)=0; + subs(exp)=0; + offs(exp)= (ok==10) ? 1 : 0; + as_bad("Can't deal with expression \"%s\": defaulting to %ld",exp->e_beg,offs(exp)); + } + break; + case SEG_BIG: + if(ok==80 && offs(exp)<0) { /* HACK! Turn it into a long */ + LITTLENUM_TYPE words[6]; + + gen_to_words(words,2,8L);/* These numbers are magic! */ + seg(exp)=SEG_ABSOLUTE; + adds(exp)=0; + subs(exp)=0; + offs(exp)=words[1]|(words[0]<<16); + } else if(ok!=0) { + seg(exp)=SEG_ABSOLUTE; + adds(exp)=0; + subs(exp)=0; + offs(exp)= (ok==10) ? 1 : 0; + as_bad("Can't deal with expression \"%s\": defaulting to %ld",exp->e_beg,offs(exp)); + } + break; + default: + abort(); + } + if(input_line_pointer!=exp->e_end+1) + as_bad("Ignoring junk after expression"); + exp->e_end[1]=c_save; + input_line_pointer=save_in; + if(exp->e_siz) { + switch(exp->e_siz) { + case 1: + if(!isbyte(offs(exp))) + as_bad("expression doesn't fit in BYTE"); + break; + case 2: + if(!isword(offs(exp))) + as_bad("expression doesn't fit in WORD"); + break; + } + } + return offs(exp); +#endif +} /* get_num() */ + +/* These are the back-ends for the various machine dependent pseudo-ops. */ + +static void s_data1() { + subseg_new(SEG_DATA,1); + demand_empty_rest_of_line(); +} /* s_data1() */ + +static void s_data2() { + subseg_new(SEG_DATA,2); + demand_empty_rest_of_line(); +} /* s_data2() */ + +static void s_bss() { + /* We don't support putting frags in the BSS segment, but we + can put them into initialized data for now... */ + subseg_new(SEG_DATA,255); /* FIXME-SOON */ + demand_empty_rest_of_line(); +} /* s_bss() */ + +static void s_even() { + register int temp; + register long temp_fill; + + temp = 1; /* JF should be 2? */ + temp_fill = get_absolute_expression (); + if ( ! need_pass_2 ) /* Never make frag if expect extra pass. */ + frag_align (temp, (int)temp_fill); + demand_empty_rest_of_line(); +} /* s_even() */ + +static void s_proc() { + demand_empty_rest_of_line(); +} /* s_proc() */ + +/* s_space is defined in read.c .skip is simply an alias to it. */ + +int +md_parse_option(argP,cntP,vecP) +char **argP; +int *cntP; +char ***vecP; +{ + switch(**argP) { + case 'l': /* -l means keep externals to 2 byte branch offsets + rather than 4 byte branch offsets */ + break; + + case 'm': + /* Gas almost ignores this option! */ + (*argP)++; + if(**argP=='c') + (*argP)++; + if(!strcmp(*argP,"68000")) + flagseen['m']=2; + else if(!strcmp(*argP,"68010")) { +#ifdef TE_SUN + magic_number_for_object_file = 1<<16|OMAGIC; +#endif + flagseen['m']=1; + } else if(!strcmp(*argP,"68020")) + flagseen['m']=0; + else + as_warn("Unknown -m option ignored"); + while(**argP) + (*argP)++; + break; + + case 'p': + if (!strcmp(*argP,"pic")) { + (*argP) += 3; + break; /* -pic, Position Independent Code */ + } + else + return 0; + default: + return 0; + } + return 1; +} + + +#ifdef TEST2 + +/* TEST2: Test md_assemble() */ +/* Warning, this routine probably doesn't work anymore */ + +main() +{ + struct m68_it the_ins; + char buf[120]; + char *cp; + int n; + + m68_ip_begin(); + for(;;) { + if(!gets(buf) || !*buf) + break; + if(buf[0]=='|' || buf[1]=='.') + continue; + for(cp=buf;*cp;cp++) + if(*cp=='\t') + *cp=' '; + if(is_label(buf)) + continue; + bzero(&the_ins,sizeof(the_ins)); + m68_ip(&the_ins,buf); + if(the_ins.error) { + printf("Error %s in %s\n",the_ins.error,buf); + } else { + printf("Opcode(%d.%s): ",the_ins.numo,the_ins.args); + for(n=0;n<the_ins.numo;n++) + printf(" 0x%x",the_ins.opcode[n]&0xffff); + printf(" "); + print_the_insn(&the_ins.opcode[0],stdout); + (void)putchar('\n'); + } + for(n=0;n<strlen(the_ins.args)/2;n++) { + if(the_ins.operands[n].error) { + printf("op%d Error %s in %s\n",n,the_ins.operands[n].error,buf); + continue; + } + printf("mode %d, reg %d, ",the_ins.operands[n].mode,the_ins.operands[n].reg); + if(the_ins.operands[n].b_const) + printf("Constant: '%.*s', ",1+the_ins.operands[n].e_const-the_ins.operands[n].b_const,the_ins.operands[n].b_const); + printf("ireg %d, isiz %d, imul %d, ",the_ins.operands[n].ireg,the_ins.operands[n].isiz,the_ins.operands[n].imul); + if(the_ins.operands[n].b_iadd) + printf("Iadd: '%.*s',",1+the_ins.operands[n].e_iadd-the_ins.operands[n].b_iadd,the_ins.operands[n].b_iadd); + (void)putchar('\n'); + } + } + m68_ip_end(); + return 0; +} + +is_label(str) +char *str; +{ + while(*str==' ') + str++; + while(*str && *str!=' ') + str++; + if(str[-1]==':' || str[1]=='=') + return 1; + return 0; +} + +#endif + +/* Possible states for relaxation: + +0 0 branch offset byte (bra, etc) +0 1 word +0 2 long + +1 0 indexed offsets byte a0@(32,d4:w:1) etc +1 1 word +1 2 long + +2 0 two-offset index word-word a0@(32,d4)@(45) etc +2 1 word-long +2 2 long-word +2 3 long-long + +*/ + + + +#ifdef DONTDEF +abort() +{ + printf("ABORT!\n"); + exit(12); +} + +char *index(s,c) +char *s; +{ + while(*s!=c) { + if(!*s) return 0; + s++; + } + return s; +} + +bzero(s,n) +char *s; +{ + while(n--) + *s++=0; +} + +print_frags() +{ + fragS *fragP; + extern fragS *text_frag_root; + + for(fragP=text_frag_root;fragP;fragP=fragP->fr_next) { + printf("addr %lu next 0x%x fix %ld var %ld symbol 0x%x offset %ld\n", + fragP->fr_address,fragP->fr_next,fragP->fr_fix,fragP->fr_var,fragP->fr_symbol,fragP->fr_offset); + printf("opcode 0x%x type %d subtype %d\n\n",fragP->fr_opcode,fragP->fr_type,fragP->fr_subtype); + } + fflush(stdout); + return 0; +} +#endif + +#ifdef DONTDEF +/*VARARGS1*/ +panic(format,args) +char *format; +{ + fputs("Internal error:",stderr); + _doprnt(format,&args,stderr); + (void)putc('\n',stderr); + as_where(); + abort(); +} +#endif + +/* We have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS * +md_undefined_symbol (name) + char *name; +{ + return 0; +} + +/* Parse an operand that is machine-specific. + We just return without modifying the expression if we have nothing + to do. */ + +/* ARGSUSED */ +void +md_operand (expressionP) + expressionS *expressionP; +{ +} + +/* Round up a section size to the appropriate boundary. */ +long +md_section_align (segment, size) + segT segment; + long size; +{ + return size; /* Byte alignment is fine */ +} + +/* Exactly what point is a PC-relative offset relative TO? + On the 68k, they're relative to the address of the offset, plus + its size. (??? Is this right? FIXME-SOON!) */ +long +md_pcrel_from (fixP) + fixS *fixP; +{ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + +/* Opcode table for m68000/m68020 and m68881. + Copyright (C) 1989, Free Software Foundation. + +This file is part of GDB, the GNU Debugger and GAS, the GNU Assembler. + +Both GDB and GAS are free software; you can redistribute and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GDB and GAS are 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 GDB or GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* We store four bytes of opcode for all opcodes because that + is the most any of them need. The actual length of an instruction + is always at least 2 bytes, and is as much longer as necessary to + hold the operands it has. + + The match component is a mask saying which bits must match + particular opcode in order for an instruction to be an instance + of that opcode. + + The args component is a string containing two characters + for each operand of the instruction. The first specifies + the kind of operand; the second, the place it is stored. */ + +/* Kinds of operands: + D data register only. Stored as 3 bits. + A address register only. Stored as 3 bits. + R either kind of register. Stored as 4 bits. + F floating point coprocessor register only. Stored as 3 bits. + O an offset (or width): immediate data 0-31 or data register. + Stored as 6 bits in special format for BF... insns. + + autoincrement only. Stored as 3 bits (number of the address register). + - autodecrement only. Stored as 3 bits (number of the address register). + Q quick immediate data. Stored as 3 bits. + This matches an immediate operand only when value is in range 1 .. 8. + M moveq immediate data. Stored as 8 bits. + This matches an immediate operand only when value is in range -128..127 + T trap vector immediate data. Stored as 4 bits. + + k K-factor for fmove.p instruction. Stored as a 7-bit constant or + a three bit register offset, depending on the field type. + + # immediate data. Stored in special places (b, w or l) + which say how many bits to store. + ^ immediate data for floating point instructions. Special places + are offset by 2 bytes from '#'... + B pc-relative address, converted to an offset + that is treated as immediate data. + d displacement and register. Stores the register as 3 bits + and stores the displacement in the entire second word. + + C the CCR. No need to store it; this is just for filtering validity. + S the SR. No need to store, just as with CCR. + U the USP. No need to store, just as with CCR. + + I Coprocessor ID. Not printed if 1. The Coprocessor ID is always + extracted from the 'd' field of word one, which means that an extended + coprocessor opcode can be skipped using the 'i' place, if needed. + + s System Control register for the floating point coprocessor. + S List of system control registers for floating point coprocessor. + + J Misc register for movec instruction, stored in 'j' format. + Possible values: + 000 SFC Source Function Code reg + 001 DFC Data Function Code reg + 002 CACR Cache Control Register + 800 USP User Stack Pointer + 801 VBR Vector Base reg + 802 CAAR Cache Address Register + 803 MSP Master Stack Pointer + 804 ISP Interrupt Stack Pointer + + L Register list of the type d0-d7/a0-a7 etc. + (New! Improved! Can also hold fp0-fp7, as well!) + The assembler tries to see if the registers match the insn by + looking at where the insn wants them stored. + + l Register list like L, but with all the bits reversed. + Used for going the other way. . . + + They are all stored as 6 bits using an address mode and a register number; + they differ in which addressing modes they match. + + * all (modes 0-6,7.*) + ~ alterable memory (modes 2-6,7.0,7.1)(not 0,1,7.~) + % alterable (modes 0-6,7.0,7.1)(not 7.~) + ; data (modes 0,2-6,7.*)(not 1) + @ data, but not immediate (modes 0,2-6,7.? ? ?)(not 1,7.?) This may really be ;, the 68020 book says it is + ! control (modes 2,5,6,7.*-)(not 0,1,3,4,7.4) + & alterable control (modes 2,5,6,7.0,7.1)(not 0,1,7.? ? ?) + $ alterable data (modes 0,2-6,7.0,7.1)(not 1,7.~) + ? alterable control, or data register (modes 0,2,5,6,7.0,7.1)(not 1,3,4,7.~) + / control, or data register (modes 0,2,5,6,7.0,7.1,7.2,7.3)(not 1,3,4,7.4) +*/ + +/* JF: for the 68851 */ +/* + I didn't use much imagination in choosing the + following codes, so many of them aren't very + mnemonic. -rab + + P pmmu register + Possible values: + 000 TC Translation Control reg + 100 CAL Current Access Level + 101 VAL Validate Access Level + 110 SCC Stack Change Control + 111 AC Access Control + + W wide pmmu registers + Possible values: + 001 DRP Dma Root Pointer + 010 SRP Supervisor Root Pointer + 011 CRP Cpu Root Pointer + + f function code register + 0 SFC + 1 DFC + + V VAL register only + + X BADx, BACx + 100 BAD Breakpoint Acknowledge Data + 101 BAC Breakpoint Acknowledge Control + + Y PSR + Z PCSR + + | memory (modes 2-6, 7.*) + +*/ + +/* Places to put an operand, for non-general operands: + s source, low bits of first word. + d dest, shifted 9 in first word + 1 second word, shifted 12 + 2 second word, shifted 6 + 3 second word, shifted 0 + 4 third word, shifted 12 + 5 third word, shifted 6 + 6 third word, shifted 0 + 7 second word, shifted 7 + 8 second word, shifted 10 + D store in both place 1 and place 3; for divul and divsl. + b second word, low byte + w second word (entire) + l second and third word (entire) + g branch offset for bra and similar instructions. + The place to store depends on the magnitude of offset. + t store in both place 7 and place 8; for floating point operations + c branch offset for cpBcc operations. + The place to store is word two if bit six of word one is zero, + and words two and three if bit six of word one is one. + i Increment by two, to skip over coprocessor extended operands. Only + works with the 'I' format. + k Dynamic K-factor field. Bits 6-4 of word 2, used as a register number. + Also used for dynamic fmovem instruction. + C floating point coprocessor constant - 7 bits. Also used for static + K-factors... + j Movec register #, stored in 12 low bits of second word. + + Places to put operand, for general operands: + d destination, shifted 6 bits in first word + b source, at low bit of first word, and immediate uses one byte + w source, at low bit of first word, and immediate uses two bytes + l source, at low bit of first word, and immediate uses four bytes + s source, at low bit of first word. + Used sometimes in contexts where immediate is not allowed anyway. + f single precision float, low bit of 1st word, immediate uses 4 bytes + F double precision float, low bit of 1st word, immediate uses 8 bytes + x extended precision float, low bit of 1st word, immediate uses 12 bytes + p packed float, low bit of 1st word, immediate uses 12 bytes +*/ + +#define one(x) ((x) << 16) +#define two(x, y) (((x) << 16) + y) + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of tc-m68k.c */ diff --git a/gas/config/tc-m68k.h b/gas/config/tc-m68k.h new file mode 100644 index 0000000..4fa516c --- /dev/null +++ b/gas/config/tc-m68k.h @@ -0,0 +1,26 @@ +/* + * This file is tp-generic.h and is intended to be a template for + * target processor specific header files. + */ + +#define TC_M68K 1 + +#ifdef TE_SUN3 +/* This variable contains the value to write out at the beginning of + the a.out file. The 2<<16 means that this is a 68020 file instead + of an old-style 68000 file */ + +#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (2<<16|OMAGIC); /* Magic byte for file header */ +#endif /* TE_SUN3 */ + +#define tc_crawl_symbol_chain(a) ; /* not used */ +#define tc_headers_hook(a) ; /* not used */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of tc-m68k.h */ diff --git a/gas/config/tc-ns32k.c b/gas/config/tc-ns32k.c new file mode 100644 index 0000000..f4fd883 --- /dev/null +++ b/gas/config/tc-ns32k.c @@ -0,0 +1,1867 @@ +/* ns32k.c -- Assemble on the National Semiconductor 32k series + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/*#define SHOW_NUM 1*/ /* uncomment for debugging */ + +#include <stdio.h> +#include <ctype.h> +#ifdef USG +#include <string.h> +#else +#include <strings.h> +#endif +#include "ns32k-opcode.h" + +#include "as.h" + +#include "obstack.h" + +/* Macros */ +#define IIF_ENTRIES 13 /* number of entries in iif */ +#define PRIVATE_SIZE 256 /* size of my garbage memory */ +#define MAX_ARGS 4 +#define DEFAULT -1 /* addr_mode returns this value when plain constant or label is encountered */ + +#define IIF(ptr,a1,c1,e1,g1,i1,k1,m1,o1,q1,s1,u1) \ + iif.iifP[ptr].type= a1; \ + iif.iifP[ptr].size= c1; \ + iif.iifP[ptr].object= e1; \ + iif.iifP[ptr].object_adjust= g1; \ + iif.iifP[ptr].pcrel= i1; \ + iif.iifP[ptr].pcrel_adjust= k1; \ + iif.iifP[ptr].im_disp= m1; \ + iif.iifP[ptr].relax_substate= o1; \ + iif.iifP[ptr].bit_fixP= q1; \ + iif.iifP[ptr].addr_mode= s1; \ + iif.iifP[ptr].bsr= u1; + +#ifdef SEQUENT_COMPATABILITY +#define LINE_COMMENT_CHARS "|" +#define ABSOLUTE_PREFIX '@' +#define IMMEDIATE_PREFIX '#' +#endif + +#ifndef LINE_COMMENT_CHARS +#define LINE_COMMENT_CHARS "#" +#endif + +char comment_chars[] = "#"; +char line_comment_chars[] = LINE_COMMENT_CHARS; +#if !defined(ABSOLUTE_PREFIX) && !defined(IMMEDIATE_PREFIX) +#define ABSOLUTE_PREFIX '@' /* One or the other MUST be defined */ +#endif + +struct addr_mode { + char mode; /* addressing mode of operand (0-31) */ + char scaled_mode; /* mode combined with scaled mode */ + char scaled_reg; /* register used in scaled+1 (1-8) */ + char float_flag; /* set if R0..R7 was F0..F7 ie a floating-point-register */ + char am_size; /* estimated max size of general addr-mode parts*/ + char im_disp; /* if im_disp==1 we have a displacement */ + char pcrel; /* 1 if pcrel, this is really redundant info */ + char disp_suffix[2]; /* length of displacement(s), 0=undefined */ + char *disp[2]; /* pointer(s) at displacement(s) + or immediates(s) (ascii) */ + char index_byte; /* index byte */ +}; +typedef struct addr_mode addr_modeS; + + +char *freeptr,*freeptr_static; /* points at some number of free bytes */ +struct hash_control *inst_hash_handle; + +struct ns32k_opcode *desc; /* pointer at description of instruction */ +addr_modeS addr_modeP; +char EXP_CHARS[] = "eE"; +char FLT_CHARS[] = "fd"; /* we don't want to support lowercase, do we */ + +/* UPPERCASE denotes live names + * when an instruction is built, IIF is used as an intermidiate form to store + * the actual parts of the instruction. A ns32k machine instruction can + * be divided into a couple of sub PARTs. When an instruction is assembled + * the appropriate PART get an assignment. When an IIF has been completed it's + * converted to a FRAGment as specified in AS.H */ + +/* internal structs */ +struct option { + char *pattern; + unsigned long or; + unsigned long and; +}; + +typedef struct { + int type; /* how to interpret object */ + int size; /* Estimated max size of object */ + unsigned long object; /* binary data */ + int object_adjust; /* number added to object */ + int pcrel; /* True if object is pcrel */ + int pcrel_adjust; /* It's value reflects the length in bytes from the instruction start to the displacement */ + int im_disp; /* True if the object is a displacement */ + relax_substateT relax_substate; /* Initial relaxsubstate */ + bit_fixS *bit_fixP; /* Pointer at bit_fix struct */ + int addr_mode; /* What addrmode do we associate with this iif-entry */ + char bsr; /* Sequent hack */ +}iif_entryT; /* Internal Instruction Format */ +struct int_ins_form { + int instr_size; /* Max size of instruction in bytes. */ + iif_entryT iifP[IIF_ENTRIES+1]; +}; +struct int_ins_form iif; +expressionS exprP; +char *input_line_pointer; +/* description of the PARTs in IIF + *object[n]: + * 0 total length in bytes of entries in iif + * 1 opcode + * 2 index_byte_a + * 3 index_byte_b + * 4 disp_a_1 + * 5 disp_a_2 + * 6 disp_b_1 + * 7 disp_b_2 + * 8 imm_a + * 9 imm_b + * 10 implied1 + * 11 implied2 + * + * For every entry there is a datalength in bytes. This is stored in size[n]. + * 0, the objectlength is not explicitly given by the instruction + * and the operand is undefined. This is a case for relaxation. + * Reserve 4 bytes for the final object. + * + * 1, the entry contains one byte + * 2, the entry contains two bytes + * 3, the entry contains three bytes + * 4, the entry contains four bytes + * etc + * + * Furthermore, every entry has a data type identifier in type[n]. + * + * 0, the entry is void, ignore it. + * 1, the entry is a binary number. + * 2, the entry is a pointer at an expression. + * Where expression may be as simple as a single '1', + * and as complicated as foo-bar+12, + * foo and bar may be undefined but suffixed by :{b|w|d} to + * control the length of the object. + * + * 3, the entry is a pointer at a bignum struct + * + * + * The low-order-byte coresponds to low physical memory. + * Obviously a FRAGment must be created for each valid disp in PART whose + * datalength is undefined (to bad) . + * The case where just the expression is undefined is less severe and is + * handled by fix. Here the number of bytes in the objectfile is known. + * With this representation we simplify the assembly and separates the + * machine dependent/independent parts in a more clean way (said OE) + */ + +struct option opt1[]= /* restore, exit */ +{ + { "r0", 0x80, 0xff }, + { "r1", 0x40, 0xff }, + { "r2", 0x20, 0xff }, + { "r3", 0x10, 0xff }, + { "r4", 0x08, 0xff }, + { "r5", 0x04, 0xff }, + { "r6", 0x02, 0xff }, + { "r7", 0x01, 0xff }, + { 0 , 0x00, 0xff } +}; +struct option opt2[]= /* save, enter */ +{ + { "r0", 0x01, 0xff }, + { "r1", 0x02, 0xff }, + { "r2", 0x04, 0xff }, + { "r3", 0x08, 0xff }, + { "r4", 0x10, 0xff }, + { "r5", 0x20, 0xff }, + { "r6", 0x40, 0xff }, + { "r7", 0x80, 0xff }, + { 0 , 0x00, 0xff } +}; +struct option opt3[]= /* setcfg */ +{ + { "c", 0x8, 0xff }, + { "m", 0x4, 0xff }, + { "f", 0x2, 0xff }, + { "i", 0x1, 0xff }, + { 0 , 0x0, 0xff } +}; +struct option opt4[]= /* cinv */ +{ + { "a", 0x4, 0xff }, + { "i", 0x2, 0xff }, + { "d", 0x1, 0xff }, + { 0 , 0x0, 0xff } +}; +struct option opt5[]= /* string inst */ +{ + { "b", 0x2, 0xff }, + { "u", 0xc, 0xff }, + { "w", 0x4, 0xff }, + { 0 , 0x0, 0xff } +}; +struct option opt6[]= /* plain reg ext,cvtp etc */ +{ + { "r0", 0x00, 0xff }, + { "r1", 0x01, 0xff }, + { "r2", 0x02, 0xff }, + { "r3", 0x03, 0xff }, + { "r4", 0x04, 0xff }, + { "r5", 0x05, 0xff }, + { "r6", 0x06, 0xff }, + { "r7", 0x07, 0xff }, + { 0 , 0x00, 0xff } +}; + +#if !defined(NS32032) && !defined(NS32532) +#define NS32032 +#endif + +struct option cpureg_532[]= /* lpr spr */ +{ + { "us", 0x0, 0xff }, + { "dcr", 0x1, 0xff }, + { "bpc", 0x2, 0xff }, + { "dsr", 0x3, 0xff }, + { "car", 0x4, 0xff }, + { "fp", 0x8, 0xff }, + { "sp", 0x9, 0xff }, + { "sb", 0xa, 0xff }, + { "usp", 0xb, 0xff }, + { "cfg", 0xc, 0xff }, + { "psr", 0xd, 0xff }, + { "intbase", 0xe, 0xff }, + { "mod", 0xf, 0xff }, + { 0 , 0x00, 0xff } +}; +struct option mmureg_532[]= /* lmr smr */ +{ + { "mcr", 0x9, 0xff }, + { "msr", 0xa, 0xff }, + { "tear", 0xb, 0xff }, + { "ptb0", 0xc, 0xff }, + { "ptb1", 0xd, 0xff }, + { "ivar0", 0xe, 0xff }, + { "ivar1", 0xf, 0xff }, + { 0 , 0x0, 0xff } +}; + +struct option cpureg_032[]= /* lpr spr */ +{ + { "upsr", 0x0, 0xff }, + { "fp", 0x8, 0xff }, + { "sp", 0x9, 0xff }, + { "sb", 0xa, 0xff }, + { "psr", 0xd, 0xff }, + { "intbase", 0xe, 0xff }, + { "mod", 0xf, 0xff }, + { 0 , 0x0, 0xff } +}; +struct option mmureg_032[]= /* lmr smr */ +{ + { "bpr0", 0x0, 0xff }, + { "bpr1", 0x1, 0xff }, + { "pf0", 0x4, 0xff }, + { "pf1", 0x5, 0xff }, + { "sc", 0x8, 0xff }, + { "msr", 0xa, 0xff }, + { "bcnt", 0xb, 0xff }, + { "ptb0", 0xc, 0xff }, + { "ptb1", 0xd, 0xff }, + { "eia", 0xf, 0xff }, + { 0 , 0x0, 0xff } +}; + +#if defined(NS32532) +struct option *cpureg = cpureg_532; +struct option *mmureg = mmureg_532; +#else +struct option *cpureg = cpureg_032; +struct option *mmureg = mmureg_032; +#endif + + +const pseudo_typeS md_pseudo_table[]={ /* so far empty */ + { 0, 0, 0 } +}; + +#define IND(x,y) (((x)<<2)+(y)) + +/* those are index's to relax groups in md_relax_table + ie it must be multiplied by 4 to point at a group start. Viz IND(x,y) + Se function relax_segment in write.c for more info */ + +#define BRANCH 1 +#define PCREL 2 + +/* those are index's to entries in a relax group */ + +#define BYTE 0 +#define WORD 1 +#define DOUBLE 2 +#define UNDEF 3 +/* Those limits are calculated from the displacement start in memory. + The ns32k uses the begining of the instruction as displacement base. + This type of displacements could be handled here by moving the limit window + up or down. I choose to use an internal displacement base-adjust as there + are other routines that must consider this. Also, as we have two various + offset-adjusts in the ns32k (acb versus br/brs/jsr/bcond), two set of limits + would have had to be used. + Now we dont have to think about that. */ + + +const relax_typeS md_relax_table[]={ + { 1, 1, 0, 0 }, + { 1, 1, 0, 0 }, + { 1, 1, 0, 0 }, + { 1, 1, 0, 0 }, + + { (63), (-64), 1, IND(BRANCH,WORD) }, + { (8192), (-8192), 2, IND(BRANCH,DOUBLE) }, + { 0, 0, 4, 0 }, + { 1, 1, 0, 0 } +}; + +/* Array used to test if mode contains displacements. + Value is true if mode contains displacement. */ + +char disp_test[]={ 0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1, + 1,1,1,0,0,1,1,0, + 1,1,1,1,1,1,1,1 }; + +/* Array used to calculate max size of displacements */ + +char disp_size[]={ 4,1,2,0,4 }; + + +#ifdef __STDC__ + +static segT evaluate_expr(expressionS *resultP, char *ptr); +static void md_number_to_disp(char *buf, long val, int n); +static void md_number_to_imm(char *buf, long val, int n); + +#else /* __STDC__ */ + +static segT evaluate_expr(); +static void md_number_to_disp(); +static void md_number_to_imm(); + +#endif /* __STDC__ */ + +/* Parses a general operand into an addressingmode struct + + in: pointer at operand in ascii form + pointer at addr_mode struct for result + the level of recursion. (always 0 or 1) + + out: data in addr_mode struct + */ +int addr_mode(operand,addr_modeP,recursive_level) + char *operand; + register addr_modeS *addr_modeP; +int recursive_level; +{ + register char *str; + register int i; + register int strl; + register int mode; + int j; + mode = DEFAULT; /* default */ + addr_modeP->scaled_mode=0; /* why not */ + addr_modeP->scaled_reg=0; /* if 0, not scaled index */ + addr_modeP->float_flag=0; + addr_modeP->am_size=0; + addr_modeP->im_disp=0; + addr_modeP->pcrel=0; /* not set in this function */ + addr_modeP->disp_suffix[0]=0; + addr_modeP->disp_suffix[1]=0; + addr_modeP->disp[0]=NULL; + addr_modeP->disp[1]=NULL; + str=operand; + if (str[0]==0) {return (0);} /* we don't want this */ + strl=strlen(str); + switch (str[0]) { + /* the following three case statements controls the mode-chars + this is the place to ed if you want to change them */ +#ifdef ABSOLUTE_PREFIX + case ABSOLUTE_PREFIX: + if (str[strl-1]==']') break; + addr_modeP->mode=21; /* absolute */ + addr_modeP->disp[0]=str+1; + return (-1); +#endif +#ifdef IMMEDIATE_PREFIX + case IMMEDIATE_PREFIX: + if (str[strl-1]==']') break; + addr_modeP->mode=20; /* immediate */ + addr_modeP->disp[0]=str+1; + return (-1); +#endif + case '.': + if (str[strl-1]!=']') { + switch (str[1]) { + case'-':case'+': + if (str[2]!='\000') { + addr_modeP->mode=27; /* pc-relativ */ + addr_modeP->disp[0]=str+2; + return (-1); + } + default: + as_warn("Invalid syntax in PC-relative addressing mode"); + return(0); + } + } + break; + case'e': + if (str[strl-1]!=']') { + if((!strncmp(str,"ext(",4)) && strl>7) { /* external */ + addr_modeP->disp[0]=str+4; + i=0; + j=2; + do { /* disp[0]'s termination point */ + j+=1; + if (str[j]=='(') i++; + if (str[j]==')') i--; + } while (j<strl && i!=0); + if (i!=0 || !(str[j+1]=='-' || str[j+1]=='+') ) { + as_warn("Invalid syntax in External addressing mode"); + return(0); + } + str[j]='\000'; /* null terminate disp[0] */ + addr_modeP->disp[1]=str+j+2; + addr_modeP->mode=22; + return (-1); + } + } + break; + default:; + } + strl=strlen(str); + switch(strl) { + case 2: + switch (str[0]) { + case'f':addr_modeP->float_flag=1; + case'r': + if (str[1]>='0' && str[1]<'8') { + addr_modeP->mode=str[1]-'0'; + return (-1); + } + } + case 3: + if (!strncmp(str,"tos",3)) { + addr_modeP->mode=23; /* TopOfStack */ + return (-1); + } + default:; + } + if (strl>4) { + if (str[strl-1]==')') { + if (str[strl-2]==')') { + if (!strncmp(&str[strl-5],"(fp",3)) { + mode=16; /* Memory Relative */ + } + if (!strncmp(&str[strl-5],"(sp",3)) { + mode=17; + } + if (!strncmp(&str[strl-5],"(sb",3)) { + mode=18; + } + if (mode!=DEFAULT) { /* memory relative */ + addr_modeP->mode=mode; + j=strl-5; /* temp for end of disp[0] */ + i=0; + do { + strl-=1; + if (str[strl]==')') i++; + if (str[strl]=='(') i--; + } while (strl>-1 && i!=0); + if (i!=0) { + as_warn("Invalid syntax in Memory Relative addressing mode"); + return(0); + } + addr_modeP->disp[1]=str; + addr_modeP->disp[0]=str+strl+1; + str[j]='\000'; /* null terminate disp[0] */ + str[strl]='\000'; /* null terminate disp[1] */ + return (-1); + } + } + switch (str[strl-3]) { + case'r':case'R': + if (str[strl-2]>='0' && str[strl-2]<'8' && str[strl-4]=='(') { + addr_modeP->mode=str[strl-2]-'0'+8; + addr_modeP->disp[0]=str; + str[strl-4]=0; + return (-1); /* reg rel */ + } + default: + if (!strncmp(&str[strl-4],"(fp",3)) { + mode=24; + } + if (!strncmp(&str[strl-4],"(sp",3)) { + mode=25; + } + if (!strncmp(&str[strl-4],"(sb",3)) { + mode=26; + } + if (!strncmp(&str[strl-4],"(pc",3)) { + mode=27; + } + if (mode!=DEFAULT) { + addr_modeP->mode=mode; + addr_modeP->disp[0]=str; + str[strl-4]='\0'; + return (-1); /* memory space */ + } + } + } + /* no trailing ')' do we have a ']' ? */ + if (str[strl-1]==']') { + switch (str[strl-2]) { + case'b':mode=28;break; + case'w':mode=29;break; + case'd':mode=30;break; + case'q':mode=31;break; + default:; + as_warn("Invalid scaled-indexed mode, use (b,w,d,q)"); + if (str[strl-3]!=':' || str[strl-6]!='[' || + str[strl-5]=='r' || str[strl-4]<'0' || str[strl-4]>'7') { + as_warn("Syntax in scaled-indexed mode, use [Rn:m] where n=[0..7] m={b,w,d,q}"); + } + } /* scaled index */ + { + if (recursive_level>0) { + as_warn("Scaled-indexed addressing mode combined with scaled-index"); + return(0); + } + addr_modeP->am_size+=1; /* scaled index byte */ + j=str[strl-4]-'0'; /* store temporary */ + str[strl-6]='\000'; /* nullterminate for recursive call */ + i=addr_mode(str,addr_modeP,1); + if (!i || addr_modeP->mode==20) { + as_warn("Invalid or illegal addressing mode combined with scaled-index"); + return(0); + } + addr_modeP->scaled_mode=addr_modeP->mode; /* store the inferior mode */ + addr_modeP->mode=mode; + addr_modeP->scaled_reg=j+1; + return (-1); + } + } + } + addr_modeP->mode = DEFAULT; /* default to whatever */ + addr_modeP->disp[0]=str; + return (-1); +} + +/* ptr points at string + addr_modeP points at struct with result + This routine calls addr_mode to determine the general addr.mode of + the operand. When this is ready it parses the displacements for size + specifying suffixes and determines size of immediate mode via ns32k-opcode. + Also builds index bytes if needed. + */ +int get_addr_mode(ptr,addr_modeP) + char *ptr; + addr_modeS *addr_modeP; +{ + int tmp; + addr_mode(ptr,addr_modeP,0); + if (addr_modeP->mode == DEFAULT || addr_modeP->scaled_mode == -1) { + /* resolve ambigious operands, this shouldn't + be necessary if one uses standard NSC operand + syntax. But the sequent compiler doesn't!!! + This finds a proper addressinging mode if it + is implicitly stated. See ns32k-opcode.h */ + (void)evaluate_expr(&exprP,ptr); /* this call takes time Sigh! */ + if (addr_modeP->mode == DEFAULT) { + if (exprP.X_add_symbol || exprP.X_subtract_symbol) { + addr_modeP->mode=desc->default_model; /* we have a label */ + } else { + addr_modeP->mode=desc->default_modec; /* we have a constant */ + } + } else { + if (exprP.X_add_symbol || exprP.X_subtract_symbol) { + addr_modeP->scaled_mode=desc->default_model; + } else { + addr_modeP->scaled_mode=desc->default_modec; + } + } + /* must put this mess down in addr_mode to handle the scaled case better */ + } + /* It appears as the sequent compiler wants an absolute when we have a + label without @. Constants becomes immediates besides the addr case. + Think it does so with local labels too, not optimum, pcrel is better. + When I have time I will make gas check this and select pcrel when possible + Actually that is trivial. + */ + if (tmp=addr_modeP->scaled_reg) { /* build indexbyte */ + tmp--; /* remember regnumber comes incremented for flagpurpose */ + tmp|=addr_modeP->scaled_mode<<3; + addr_modeP->index_byte=(char)tmp; + addr_modeP->am_size+=1; + } + if (disp_test[addr_modeP->mode]) { /* there was a displacement, probe for length specifying suffix*/ + { + register char c; + register char suffix; + register char suffix_sub; + register int i; + register char *toP; + register char *fromP; + + addr_modeP->pcrel=0; + if (disp_test[addr_modeP->mode]) { /* there is a displacement */ + if (addr_modeP->mode==27 || addr_modeP->scaled_mode==27) { /* do we have pcrel. mode */ + addr_modeP->pcrel=1; + } + addr_modeP->im_disp=1; + for(i=0;i<2;i++) { + suffix_sub=suffix=0; + if (toP=addr_modeP->disp[i]) { /* suffix of expression, the largest size rules */ + fromP=toP; + while (c = *fromP++) { + *toP++=c; + if (c==':') { + switch (*fromP) { + case '\0': + as_warn("Premature end of suffix--Defaulting to d"); + suffix=4; + continue; + case 'b':suffix_sub=1;break; + case 'w':suffix_sub=2;break; + case 'd':suffix_sub=4;break; + default: + as_warn("Bad suffix after ':' use {b|w|d} Defaulting to d"); + suffix=4; + } + fromP++; + toP--; /* So we write over the ':' */ + if (suffix<suffix_sub) suffix=suffix_sub; + } + } + *toP='\0'; /* terminate properly */ + addr_modeP->disp_suffix[i]=suffix; + addr_modeP->am_size+=suffix ? suffix : 4; + } + } + } + } + } else { + if (addr_modeP->mode==20) { /* look in ns32k_opcode for size */ + addr_modeP->disp_suffix[0]=addr_modeP->am_size=desc->im_size; + addr_modeP->im_disp=0; + } + } + return addr_modeP->mode; +} + + +/* read an optionlist */ +void optlist(str,optionP,default_map) + char *str; /* the string to extract options from */ + struct option *optionP; /* how to search the string */ + unsigned long *default_map; /* default pattern and output */ +{ + register int i,j,k,strlen1,strlen2; + register char *patternP,*strP; + strlen1=strlen(str); + if (strlen1<1) { + as_fatal("Very short instr to option, ie you can't do it on a NULLstr"); + } + for (i=0;optionP[i].pattern!=0;i++) { + strlen2=strlen(optionP[i].pattern); + for (j=0;j<strlen1;j++) { + patternP=optionP[i].pattern; + strP = &str[j]; + for (k=0;k<strlen2;k++) { + if (*(strP++)!=*(patternP++)) break; + } + if (k==strlen2) { /* match */ + *default_map|=optionP[i].or; + *default_map&=optionP[i].and; + } + } + } +} +/* search struct for symbols + This function is used to get the short integer form of reg names + in the instructions lmr, smr, lpr, spr + return true if str is found in list */ + +int list_search(str,optionP,default_map) + char *str; /* the string to match */ + struct option *optionP; /* list to search */ + unsigned long *default_map; /* default pattern and output */ +{ + register int i; + for (i=0;optionP[i].pattern!=0;i++) { + if (!strncmp(optionP[i].pattern,str,20)) { /* use strncmp to be safe */ + *default_map|=optionP[i].or; + *default_map&=optionP[i].and; + return -1; + } + } + as_warn("No such entry in list. (cpu/mmu register)"); + return 0; +} +static segT evaluate_expr(resultP,ptr) +expressionS *resultP; +char *ptr; +{ + register char *tmp_line; + register segT segment; + tmp_line=input_line_pointer; + input_line_pointer=ptr; + segment=expression(&exprP); + input_line_pointer=tmp_line; + return (segment); +} + +/* Convert operands to iif-format and adds bitfields to the opcode. + Operands are parsed in such an order that the opcode is updated from + its most significant bit, that is when the operand need to alter the + opcode. + Be carefull not to put to objects in the same iif-slot. + */ + +void encode_operand(argc,argv,operandsP,suffixP,im_size,opcode_bit_ptr) + int argc; + char **argv; + char *operandsP; + char *suffixP; + char im_size; + char opcode_bit_ptr; +{ + register int i,j; + int pcrel,tmp,b,loop,pcrel_adjust; + for(loop=0;loop<argc;loop++) { + i=operandsP[loop<<1]-'1'; /* what operand are we supposed to work on */ + if (i>3) as_fatal("Internal consistency error. check ns32k-opcode.h"); + pcrel=0; + pcrel_adjust=0; + tmp=0; + switch (operandsP[(loop<<1)+1]) { + case 'f': /* operand of sfsr turns out to be a nasty specialcase */ + opcode_bit_ptr-=5; + case 'F': /* 32 bit float general form */ + case 'L': /* 64 bit float */ + case 'Q': /* quad-word */ + case 'B': /* byte */ + case 'W': /* word */ + case 'D': /* double-word */ + case 'A': /* double-word gen-address-form ie no regs allowed */ + get_addr_mode(argv[i],&addr_modeP); + iif.instr_size+=addr_modeP.am_size; + if (opcode_bit_ptr==desc->opcode_size) b=4; else b=6; + for (j=b;j<(b+2);j++) { + if (addr_modeP.disp[j-b]) { + IIF(j, + 2, + addr_modeP.disp_suffix[j-b], + (unsigned long)addr_modeP.disp[j-b], + 0, + addr_modeP.pcrel, + iif.instr_size-addr_modeP.am_size, /* this aint used (now) */ + addr_modeP.im_disp, + IND(BRANCH,BYTE), + NULL, + addr_modeP.scaled_reg ? addr_modeP.scaled_mode:addr_modeP.mode, + 0); + } + } + opcode_bit_ptr-=5; + iif.iifP[1].object|=((long)addr_modeP.mode)<<opcode_bit_ptr; + if (addr_modeP.scaled_reg) { + j=b/2; + IIF(j,1,1, (unsigned long)addr_modeP.index_byte,0,0,0,0,0, NULL,-1,0); + } + break; + case 'b': /* multiple instruction disp */ + freeptr++; /* OVE:this is an useful hack */ + tmp=(int)sprintf(freeptr,"((%s-1)*%d)\000",argv[i],desc->im_size); + argv[i]=freeptr; + freeptr=(char*)tmp; + pcrel-=1; /* make pcrel 0 inspite of what case 'p': wants */ + /* fall thru */ + case 'p': /* displacement - pc relative addressing */ + pcrel+=1; + /* fall thru */ + case 'd': /* displacement */ + iif.instr_size+=suffixP[i] ? suffixP[i] : 4; + IIF(12, 2, suffixP[i], (unsigned long)argv[i], 0, + pcrel, pcrel_adjust, 1, IND(BRANCH,BYTE), NULL,-1,0); + break; + case 'H': /* sequent-hack: the linker wants a bit set when bsr */ + pcrel=1; + iif.instr_size+=suffixP[i] ? suffixP[i] : 4; + IIF(12, 2, suffixP[i], (unsigned long)argv[i], 0, + pcrel, pcrel_adjust, 1, IND(BRANCH,BYTE), NULL,-1,1);break; + case 'q': /* quick */ + opcode_bit_ptr-=4; + IIF(11,2,42,(unsigned long)argv[i],0,0,0,0,0, + bit_fix_new(4,opcode_bit_ptr,-8,7,0,1,0),-1,0); + break; + case 'r': /* register number (3 bits) */ + list_search(argv[i],opt6,&tmp); + opcode_bit_ptr-=3; + iif.iifP[1].object|=tmp<<opcode_bit_ptr; + break; + case 'O': /* setcfg instruction optionslist */ + optlist(argv[i],opt3,&tmp); + opcode_bit_ptr-=4; + iif.iifP[1].object|=tmp<<15; + break; + case 'C': /* cinv instruction optionslist */ + optlist(argv[i],opt4,&tmp); + opcode_bit_ptr-=4; + iif.iifP[1].object|=tmp<<15;/*insert the regtype in opcode */ + break; + case 'S': /* stringinstruction optionslist */ + optlist(argv[i],opt5,&tmp); + opcode_bit_ptr-=4; + iif.iifP[1].object|=tmp<<15; + break; + case 'u':case 'U': /* registerlist */ + IIF(10,1,1,0,0,0,0,0,0,NULL,-1,0); + switch (operandsP[(i<<1)+1]) { + case 'u': /* restore, exit */ + optlist(argv[i],opt1,&iif.iifP[10].object); + break; + case 'U': /* save,enter */ + optlist(argv[i],opt2,&iif.iifP[10].object); + break; + } + iif.instr_size+=1; + break; + case 'M': /* mmu register */ + list_search(argv[i],mmureg,&tmp); + opcode_bit_ptr-=4; + iif.iifP[1].object|=tmp<<opcode_bit_ptr; + break; + case 'P': /* cpu register */ + list_search(argv[i],cpureg,&tmp); + opcode_bit_ptr-=4; + iif.iifP[1].object|=tmp<<opcode_bit_ptr; + break; + case 'g': /* inss exts */ + iif.instr_size+=1; /* 1 byte is allocated after the opcode */ + IIF(10,2,1, + (unsigned long)argv[i], /* i always 2 here */ + 0,0,0,0,0, + bit_fix_new(3,5,0,7,0,0,0), /* a bit_fix is targeted to the byte */ + -1,0); + case 'G': + IIF(11,2,42, + (unsigned long)argv[i], /* i always 3 here */ + 0,0,0,0,0, + bit_fix_new(5,0,1,32,-1,0,-1),-1,0); + break; + case 'i': + iif.instr_size+=1; + b=2+i; /* put the extension byte after opcode */ + IIF(b,2,1,0,0,0,0,0,0,0,-1,0); + default: + as_fatal("Bad opcode-table-option, check in file ns32k-opcode.h"); + } + } +} + +/* in: instruction line + out: internal structure of instruction + that has been prepared for direct conversion to fragment(s) and + fixes in a systematical fashion + Return-value = recursive_level +*/ +/* build iif of one assembly text line */ +int parse(line,recursive_level) + char *line; + int recursive_level; +{ + register char *lineptr,c,suffix_separator; + register int i; + int argc,arg_type; + char sqr,sep; + char suffix[MAX_ARGS],*argv[MAX_ARGS];/* no more than 4 operands */ + if (recursive_level<=0) { /* called from md_assemble */ + for (lineptr=line;(*lineptr)!='\0' && (*lineptr)!=' ';lineptr++); + c = *lineptr; + *lineptr='\0'; + if (!(desc=(struct ns32k_opcode*)hash_find(inst_hash_handle,line))) { + as_fatal("No such opcode"); + } + *lineptr=c; + } else { + lineptr=line; + } + argc=0; + if (*desc->operands) { + if (*lineptr++!='\0') { + sqr='['; + sep=','; + while (*lineptr!='\0') { + if (desc->operands[argc<<1]) { + suffix[argc]=0; + arg_type=desc->operands[(argc<<1)+1]; + switch (arg_type) { + case 'd': case 'b': case 'p': case 'H': /* the operand is supposed to be a displacement */ + /* Hackwarning: do not forget to update the 4 cases above when editing ns32k-opcode.h */ + suffix_separator=':'; + break; + default: + suffix_separator='\255'; /* if this char occurs we loose */ + } + suffix[argc]=0; /* 0 when no ':' is encountered */ + argv[argc]=freeptr; + *freeptr='\0'; + while ((c = *lineptr)!='\0' && c!=sep) { + if (c==sqr) { + if (sqr=='[') { + sqr=']';sep='\0'; + } else { + sqr='[';sep=','; + } + } + if (c==suffix_separator) { /* ':' - label/suffix separator */ + switch (lineptr[1]) { + case 'b':suffix[argc]=1;break; + case 'w':suffix[argc]=2;break; + case 'd':suffix[argc]=4;break; + default: as_warn("Bad suffix, defaulting to d"); + suffix[argc]=4; + if (lineptr[1]=='\0' || lineptr[1]==sep) { + lineptr+=1; + continue; + } + } + lineptr+=2; + continue; + } + *freeptr++=c; + lineptr++; + } + *freeptr++='\0'; + argc+=1; + if (*lineptr=='\0') continue; + lineptr+=1; + } else { + as_fatal("Too many operands passed to instruction"); + } + } + } + } + if (argc!=strlen(desc->operands)/2) { + if (strlen(desc->default_args)) { /* we can apply default, dont goof */ + if (parse(desc->default_args,1)!=1) { /* check error in default */ + as_fatal("Wrong numbers of operands in default, check ns32k-opcodes.h"); + } + } else { + as_fatal("Wrong number of operands"); + } + + } + for (i=0;i<IIF_ENTRIES;i++) { + iif.iifP[i].type=0; /* mark all entries as void*/ + } + + /* build opcode iif-entry */ + iif.instr_size=desc->opcode_size/8; + IIF(1,1,iif.instr_size,desc->opcode_seed,0,0,0,0,0,0,-1,0); + + /* this call encodes operands to iif format */ + if (argc) { + encode_operand(argc, + argv, + &desc->operands[0], + &suffix[0], + desc->im_size, + desc->opcode_size); + } + return recursive_level; +} + + + /* Convert iif to fragments. + From this point we start to dribble with functions in other files than + this one.(Except hash.c) So, if it's possible to make an iif for an other + CPU, you don't need to know what frags, relax, obstacks, etc is in order + to port this assembler. You only need to know if it's possible to reduce + your cpu-instruction to iif-format (takes some work) and adopt the other + md_? parts according to given instructions + Note that iif was invented for the clean ns32k`s architecure. + */ +void convert_iif() { + register int i,j; + fragS *inst_frag; + char *inst_offset,*inst_opcode; + char *memP; + segT segment; + int l,k; + register int rem_size; /* count the remaining bytes of instruction */ + register char type; + register char size = 0; + int size_so_far=0; /* used to calculate pcrel_adjust */ + + rem_size=iif.instr_size; + memP=frag_more(iif.instr_size); /* make sure we have enough bytes for instruction */ + inst_opcode=memP; + inst_offset=(char*)(memP-frag_now->fr_literal); + inst_frag=frag_now; + for (i=0;i<IIF_ENTRIES;i++) { + if (type=iif.iifP[i].type) { /* the object exist, so handle it */ + switch (size=iif.iifP[i].size) { + case 42: size=0; /* it's a bitfix that operates on an existing object*/ + if (iif.iifP[i].bit_fixP->fx_bit_base) { /* expand fx_bit_base to point at opcode */ + iif.iifP[i].bit_fixP->fx_bit_base=(long)inst_opcode; + } + case 8: /* bignum or doublefloat */ + bzero (memP,8); + case 1:case 2:case 3:case 4:/* the final size in objectmemory is known */ + j=(unsigned long)iif.iifP[i].bit_fixP; + switch (type) { + case 1: /* the object is pure binary */ + if (j || iif.iifP[i].pcrel) { + fix_new_ns32k(frag_now, + (long)(memP-frag_now->fr_literal), + size, + 0, + 0, + iif.iifP[i].object, + iif.iifP[i].pcrel, + (char)size_so_far, /*iif.iifP[i].pcrel_adjust,*/ + iif.iifP[i].im_disp, + j, + iif.iifP[i].bsr); /* sequent hack */ + } else { /* good, just put them bytes out */ + switch (iif.iifP[i].im_disp) { + case 0: + md_number_to_chars(memP,iif.iifP[i].object,size);break; + case 1: + md_number_to_disp(memP,iif.iifP[i].object,size);break; + default: as_fatal("iif convert internal pcrel/binary"); + } + } + memP+=size; + rem_size-=size; + break; + case 2: /* the object is a pointer at an expression, so unpack + it, note that bignums may result from the expression + */ + if ((segment=evaluate_expr(&exprP,(char*)iif.iifP[i].object))==SEG_BIG || size==8) { + if ((k=exprP.X_add_number)>0) { /* we have a bignum ie a quad */ + /* this can only happens in a long suffixed instruction */ + bzero(memP,size); /* size normally is 8 */ + if (k*2>size) as_warn("Bignum too big for long"); + if (k==3) memP+=2; + for (l=0;k>0;k--,l+=2) { + md_number_to_chars(memP+l,generic_bignum[l>>1],sizeof(LITTLENUM_TYPE)); + } + } else { /* flonum */ + LITTLENUM_TYPE words[4]; + + switch(size) { + case 4: + gen_to_words(words,2,8); + md_number_to_imm(memP ,(long)words[0],sizeof(LITTLENUM_TYPE)); + md_number_to_imm(memP+sizeof(LITTLENUM_TYPE),(long)words[1],sizeof(LITTLENUM_TYPE)); + break; + case 8: + gen_to_words(words,4,11); + md_number_to_imm(memP ,(long)words[0],sizeof(LITTLENUM_TYPE)); + md_number_to_imm(memP+sizeof(LITTLENUM_TYPE) ,(long)words[1],sizeof(LITTLENUM_TYPE)); + md_number_to_imm(memP+2*sizeof(LITTLENUM_TYPE),(long)words[2],sizeof(LITTLENUM_TYPE)); + md_number_to_imm(memP+3*sizeof(LITTLENUM_TYPE),(long)words[3],sizeof(LITTLENUM_TYPE)); + break; + } + } + memP+=size; + rem_size-=size; + break; + } + if (j || + exprP.X_add_symbol || + exprP.X_subtract_symbol || + iif.iifP[i].pcrel) { /* fixit */ + /* the expression was undefined due to an undefined label */ + /* create a fix so we can fix the object later */ + exprP.X_add_number+=iif.iifP[i].object_adjust; + fix_new_ns32k(frag_now, + (long)(memP-frag_now->fr_literal), + size, + exprP.X_add_symbol, + exprP.X_subtract_symbol, + exprP.X_add_number, + iif.iifP[i].pcrel, + (char)size_so_far, /*iif.iifP[i].pcrel_adjust,*/ + iif.iifP[i].im_disp, + j, + iif.iifP[i].bsr); /* sequent hack */ + + } else { /* good, just put them bytes out */ + switch (iif.iifP[i].im_disp) { + case 0: + md_number_to_imm(memP,exprP.X_add_number,size);break; + case 1: + md_number_to_disp(memP,exprP.X_add_number,size);break; + default: as_fatal("iif convert internal pcrel/pointer"); + } + } + memP+=size; + rem_size-=size; + break; + default: as_fatal("Internal logic error in iif.iifP[n].type"); + } + break; + case 0: /* To bad, the object may be undefined as far as its final + nsize in object memory is concerned. The size of the object + in objectmemory is not explicitly given. + If the object is defined its length can be determined and + a fix can replace the frag. + */ + { + int temp; + segment=evaluate_expr(&exprP,(char*)iif.iifP[i].object); + if ((exprP.X_add_symbol || exprP.X_subtract_symbol) && + !iif.iifP[i].pcrel) { /* OVE: hack, clamp to 4 bytes */ + size=4; /* we dont wan't to frag this, use 4 so it reaches */ + fix_new_ns32k(frag_now, + (long)(memP-frag_now->fr_literal), + size, + exprP.X_add_symbol, + exprP.X_subtract_symbol, + exprP.X_add_number, + 0, /* never iif.iifP[i].pcrel, */ + (char)size_so_far, /*iif.iifP[i].pcrel_adjust,*/ + 1, /* always iif.iifP[i].im_disp, */ + 0,0); + memP+=size; + rem_size-=4; + break; /* exit this absolute hack */ + } + + if (exprP.X_add_symbol || exprP.X_subtract_symbol) { /* frag it */ + if (exprP.X_subtract_symbol) { /* We cant relax this case */ + as_fatal("Can't relax difference"); + } + else { + /* at this stage we must undo some of the effect caused + by frag_more, ie we must make sure that frag_var causes + frag_new to creat a valid fix-size in the frag it`s closing + */ + temp = -(rem_size-4); + obstack_blank_fast(&frags,temp); + /* we rewind none, some or all of the requested size we + requested by the first frag_more for this iif chunk. + Note: that we allocate 4 bytes to an object we NOT YET + know the size of, thus rem_size-4. + */ + (void)frag_variant(rs_machine_dependent, + 4, + 0, + IND(BRANCH,UNDEF), /* expecting the worst */ + exprP.X_add_symbol, + exprP.X_add_number, + (char*)inst_opcode, + (char)size_so_far, /*iif.iifP[i].pcrel_adjust);*/ + iif.iifP[i].bsr); /* sequent linker hack */ + rem_size-=4; + if (rem_size>0) { + memP=frag_more(rem_size); + } + } + } + else {/* Double work, this is done in md_number_to_disp */ +/* exprP.X_add_number; what was this supposed to be? + xoxorich. */ + if (-64<=exprP.X_add_number && exprP.X_add_number<=63) { + size=1; + } else { + if (-8192<=exprP.X_add_number && exprP.X_add_number<=8191) { + size=2; + } else { + if (-0x1f000000<=exprP.X_add_number && + exprP.X_add_number<=0x1fffffff) + /* if (-0x40000000<=exprP.X_add_number && + exprP.X_add_number<=0x3fffffff) */ + { + size=4; + } else { + as_warn("Displacement to large for :d"); + size=4; + } + } + } + /* rewind the bytes not used */ + temp = -(4-size); + md_number_to_disp(memP,exprP.X_add_number,size); + obstack_blank_fast(&frags,temp); + memP+=size; + rem_size-=4; /* we allocated this amount */ + } + } + break; + default: + as_fatal("Internal logic error in iif.iifP[].type"); + } + size_so_far+=size; + size=0; + } + } +} + +void md_assemble(line) +char *line; +{ + freeptr=freeptr_static; + parse(line,0); /* explode line to more fix form in iif */ + convert_iif(); /* convert iif to frags, fix's etc */ +#ifdef SHOW_NUM + printf(" \t\t\t%s\n",line); +#endif +} + + +void md_begin() { + /* build a hashtable of the instructions */ + register const struct ns32k_opcode *ptr; + register char *stat; + inst_hash_handle=hash_new(); + for (ptr=ns32k_opcodes;ptr<endop;ptr++) { + if (*(stat=hash_insert(inst_hash_handle,ptr->name,(char*)ptr))) { + as_fatal("Can't hash %s: %s", ptr->name,stat); /*fatal*/ + exit(0); + } + } + freeptr_static=(char*)malloc(PRIVATE_SIZE); /* some private space please! */ +} + + +void +md_end() { + free(freeptr_static); +} + +/* Must be equal to MAX_PRECISON in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +/* Turn the string pointed to by litP into a floating point constant of type + type, and emit the appropriate bytes. The number of LITTLENUMS emitted + is stored in *sizeP . An error message is returned, or NULL on OK. + */ +char * +md_atof(type,litP,sizeP) +char type; +char *litP; +int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + + switch(type) { + case 'f': + prec = 2; + break; + + case 'd': + prec = 4; + break; + default: + *sizeP=0; + return "Bad call to MD_ATOF()"; + } + t=atof_ieee(input_line_pointer,type,words); + if(t) + input_line_pointer=t; + + *sizeP=prec * sizeof(LITTLENUM_TYPE); + for(wordP=words+prec;prec--;) { + md_number_to_chars(litP,(long)(*--wordP),sizeof(LITTLENUM_TYPE)); + litP+=sizeof(LITTLENUM_TYPE); + } + return ""; /* Someone should teach Dean about null pointers */ +} + +/* Convert number to chars in correct order */ + +void +md_number_to_chars (buf, value, nbytes) + char *buf; + long value; + int nbytes; +{ + while (nbytes--) + { +#ifdef SHOW_NUM + printf("%x ",value & 0xff); +#endif + *buf++ = value; /* Lint wants & MASK_CHAR. */ + value >>= BITS_PER_CHAR; + } +} +/* Convert number to chars in correct order */ + + + +/* This is a variant of md_numbers_to_chars. The reason for its' existence + is the fact that ns32k uses Huffman coded displacements. This implies + that the bit order is reversed in displacements and that they are prefixed + with a size-tag. + + binary: msb -> lsb 0xxxxxxx byte + 10xxxxxx xxxxxxxx word + 11xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx double word + + This must be taken care of and we do it here! + */ +static void md_number_to_disp(buf,val,n) + char *buf; + long val; + char n; +{ + switch(n) { + case 1: + if (val < -64 || val > 63) + as_warn("Byte displacement out of range. line number not valid"); + val&=0x7f; +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 2: + if (val < -8192 || val > 8191) + as_warn("Word displacement out of range. line number not valid"); + val&=0x3fff; + val|=0x8000; +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 4: + if (val < -0x1f000000 || val >= 0x20000000) + /* if (val < -0x20000000 || val >= 0x20000000) */ + as_warn("Double word displacement out of range"); + val|=0xc0000000; +#ifdef SHOW_NUM + printf("%x ",val>>24 & 0xff); +#endif + *buf++=(val>>24); +#ifdef SHOW_NUM + printf("%x ",val>>16 & 0xff); +#endif + *buf++=(val>>16); +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + default: + as_fatal("Internal logic error. line %s, file \"%s\"", __LINE__, __FILE__); + } +} + +static void md_number_to_imm(buf,val,n) + char *buf; + long val; + char n; +{ + switch(n) { + case 1: +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 2: +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 4: +#ifdef SHOW_NUM + printf("%x ",val>>24 & 0xff); +#endif + *buf++=(val>>24); +#ifdef SHOW_NUM + printf("%x ",val>>16 & 0xff); +#endif + *buf++=(val>>16); +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + default: + as_fatal("Internal logic error. line %s, file \"%s\"", __LINE__, __FILE__); + } +} + +/* Translate internal representation of relocation info into target format. + + OVE: on a ns32k the twiddling continues at an even deeper level + here we have to distinguish between displacements and immediates. + + The sequent has a bit for this. It also has a bit for relocobjects that + points at the target for a bsr (BranchSubRoutine) !?!?!?! + + This md_ri.... is tailored for sequent. + */ + +void +md_ri_to_chars(the_bytes, ri) + char *the_bytes; + struct reloc_info_generic *ri; +{ + if (ri->r_bsr) {ri->r_pcrel=0;} /* sequent seems to want this */ + md_number_to_chars(the_bytes, ri->r_address, sizeof(ri->r_address)); + md_number_to_chars(the_bytes+4, + (long)(ri->r_symbolnum ) | + (long)(ri->r_pcrel << 24 ) | + (long)(ri->r_length << 25 ) | + (long)(ri->r_extern << 27 ) | + (long)(ri->r_bsr << 28 ) | + (long)(ri->r_disp << 29 ), + 4); + /* the first and second md_number_to_chars never overlaps (32bit cpu case) */ +} + +/* fast bitfiddling support */ +/* mask used to zero bitfield before oring in the true field */ + +static unsigned long l_mask[]={ 0xffffffff, 0xfffffffe, 0xfffffffc, 0xfffffff8, + 0xfffffff0, 0xffffffe0, 0xffffffc0, 0xffffff80, + 0xffffff00, 0xfffffe00, 0xfffffc00, 0xfffff800, + 0xfffff000, 0xffffe000, 0xffffc000, 0xffff8000, + 0xffff0000, 0xfffe0000, 0xfffc0000, 0xfff80000, + 0xfff00000, 0xffe00000, 0xffc00000, 0xff800000, + 0xff000000, 0xfe000000, 0xfc000000, 0xf8000000, + 0xf0000000, 0xe0000000, 0xc0000000, 0x80000000, + }; +static unsigned long r_mask[]={ 0x00000000, 0x00000001, 0x00000003, 0x00000007, + 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, + 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, + 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, + 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, + 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, + 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, + 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + }; +#define MASK_BITS 31 +/* Insert bitfield described by field_ptr and val at buf + This routine is written for modification of the first 4 bytes pointed + to by buf, to yield speed. + The ifdef stuff is for selection between a ns32k-dependent routine + and a general version. (My advice: use the general version!) + */ + +static void +md_number_to_field(buf,val,field_ptr) + register char *buf; + register long val; + register bit_fixS *field_ptr; +{ + register unsigned long object; + register unsigned long mask; +/* define ENDIAN on a ns32k machine */ +#ifdef ENDIAN + register unsigned long *mem_ptr; +#else + register char *mem_ptr; +#endif + if (field_ptr->fx_bit_min<=val && val<=field_ptr->fx_bit_max) { +#ifdef ENDIAN + if (field_ptr->fx_bit_base) { /* override buf */ + mem_ptr=(unsigned long*)field_ptr->fx_bit_base; + } else { + mem_ptr=(unsigned long*)buf; + } +#else + if (field_ptr->fx_bit_base) { /* override buf */ + mem_ptr=(char*)field_ptr->fx_bit_base; + } else { + mem_ptr=buf; + } +#endif + mem_ptr+=field_ptr->fx_bit_base_adj; +#ifdef ENDIAN /* we have a nice ns32k machine with lowbyte at low-physical mem */ + object = *mem_ptr; /* get some bytes */ +#else /* OVE Goof! the machine is a m68k or dito */ + /* That takes more byte fiddling */ + object=0; + object|=mem_ptr[3] & 0xff; + object<<=8; + object|=mem_ptr[2] & 0xff; + object<<=8; + object|=mem_ptr[1] & 0xff; + object<<=8; + object|=mem_ptr[0] & 0xff; +#endif + mask=0; + mask|=(r_mask[field_ptr->fx_bit_offset]); + mask|=(l_mask[field_ptr->fx_bit_offset+field_ptr->fx_bit_size]); + object&=mask; + val+=field_ptr->fx_bit_add; + object|=((val<<field_ptr->fx_bit_offset) & (mask ^ 0xffffffff)); +#ifdef ENDIAN + *mem_ptr=object; +#else + mem_ptr[0]=(char)object; + object>>=8; + mem_ptr[1]=(char)object; + object>>=8; + mem_ptr[2]=(char)object; + object>>=8; + mem_ptr[3]=(char)object; +#endif + } else { + as_warn("Bit field out of range"); + } +} + +/* Apply a fixS (fixup of an instruction or data that we didn't have + enough info to complete immediately) to the data in a frag. + + On the ns32k, everything is in a different format, so we have broken + out separate functions for each kind of thing we could be fixing. + They all get called from here. */ + +void +md_apply_fix(fixP, val) + fixS *fixP; + long val; +{ + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + + if (fixP->fx_bit_fixP) { /* Bitfields to fix, sigh */ + md_number_to_field (buf, val, fixP->fx_bit_fixP); + } else switch (fixP->fx_im_disp) { + + case 0: /* Immediate field */ + md_number_to_imm (buf, val, fixP->fx_size); + break; + + case 1: /* Displacement field */ + md_number_to_disp (buf, + fixP->fx_pcrel? val + fixP->fx_pcrel_adjust: val, + fixP->fx_size); + break; + + case 2: /* Pointer in a data object */ + md_number_to_chars (buf, val, fixP->fx_size); + break; + } +} + +/* Convert a relaxed displacement to ditto in final output */ + +void +md_convert_frag(fragP) +register fragS *fragP; +{ + long disp; + long ext; + + /* Address in gas core of the place to store the displacement. */ + register char *buffer_address = fragP -> fr_fix + fragP -> fr_literal; + /* Address in object code of the displacement. */ + register int object_address = fragP -> fr_fix + fragP -> fr_address; + + know(fragP->fr_symbol); + + /* The displacement of the address, from current location. */ + disp = (S_GET_VALUE(fragP->fr_symbol) + fragP->fr_offset) - object_address; + disp+= fragP->fr_pcrel_adjust; + + switch(fragP->fr_subtype) { + case IND(BRANCH,BYTE): + ext=1; + break; + case IND(BRANCH,WORD): + ext=2; + break; + case IND(BRANCH,DOUBLE): + ext=4; + break; + } + if(ext) { + md_number_to_disp(buffer_address,(long)disp,(int)ext); + fragP->fr_fix+=ext; + } +} + + + +/* This function returns the estimated size a variable object will occupy, + one can say that we tries to guess the size of the objects before we + actually know it */ + +int md_estimate_size_before_relax(fragP, segment) + register fragS *fragP; + segT segment; +{ + int old_fix; + old_fix=fragP->fr_fix; + switch(fragP->fr_subtype) { + case IND(BRANCH,UNDEF): + if(S_GET_SEGMENT(fragP->fr_symbol) == segment) { + /* the symbol has been assigned a value */ + fragP->fr_subtype=IND(BRANCH,BYTE); + } else { + /* we don't relax symbols defined in an other segment + the thing to do is to assume the object will occupy 4 bytes */ + fix_new_ns32k(fragP, + (int)(fragP->fr_fix), + 4, + fragP->fr_symbol, + (symbolS *)0, + fragP->fr_offset, + 1, + fragP->fr_pcrel_adjust, + 1, + 0, + fragP->fr_bsr); /*sequent hack */ + fragP->fr_fix+=4; + /* fragP->fr_opcode[1]=0xff; */ + frag_wane(fragP); + break; + } + case IND(BRANCH,BYTE): + fragP->fr_var+=1; + break; + default: + break; + } + return fragP->fr_var + fragP->fr_fix - old_fix; +} + +int md_short_jump_size = 3; +int md_long_jump_size = 5; +int md_reloc_size = 8; /* Size of relocation record */ + +void +md_create_short_jump(ptr,from_addr,to_addr,frag,to_symbol) +char *ptr; +long from_addr, + to_addr; +fragS *frag; +symbolS *to_symbol; +{ + long offset; + + offset = to_addr - from_addr; + md_number_to_chars(ptr, (long)0xEA ,1); + md_number_to_disp(ptr+1,(long)offset,2); +} + +void +md_create_long_jump(ptr,from_addr,to_addr,frag,to_symbol) +char *ptr; +long from_addr, + to_addr; +fragS *frag; +symbolS *to_symbol; +{ + long offset; + + offset= to_addr - from_addr; + md_number_to_chars(ptr, (long)0xEA, 2); + md_number_to_disp(ptr+2,(long)offset,4); +} + +/* JF this is a new function to parse machine-dep options */ +int +md_parse_option(argP,cntP,vecP) +char **argP; +int *cntP; +char ***vecP; +{ + switch(**argP) { + case 'm': + (*argP)++; + + if(!strcmp(*argP,"32032")) { + cpureg = cpureg_032; + mmureg = mmureg_032; + } else if(!strcmp(*argP, "32532")) { + cpureg = cpureg_532; + mmureg = mmureg_532; + } else + as_warn("Unknown -m option ignored"); + + while(**argP) + (*argP)++; + break; + + default: + return 0; + } + return 1; +} + +/* + * bit_fix_new() + * + * Create a bit_fixS in obstack 'notes'. + * This struct is used to profile the normal fix. If the bit_fixP is a + * valid pointer (not NULL) the bit_fix data will be used to format the fix. + */ +bit_fixS *bit_fix_new (size,offset,min,max,add,base_type,base_adj) + char size; /* Length of bitfield */ + char offset; /* Bit offset to bitfield */ + long base_type; /* 0 or 1, if 1 it's exploded to opcode ptr */ + long base_adj; + long min; /* Signextended min for bitfield */ + long max; /* Signextended max for bitfield */ + long add; /* Add mask, used for huffman prefix */ +{ + register bit_fixS * bit_fixP; + + bit_fixP = (bit_fixS *)obstack_alloc(¬es,sizeof(bit_fixS)); + + bit_fixP -> fx_bit_size = size; + bit_fixP -> fx_bit_offset = offset; + bit_fixP -> fx_bit_base = base_type; + bit_fixP -> fx_bit_base_adj = base_adj; + bit_fixP -> fx_bit_max = max; + bit_fixP -> fx_bit_min = min; + bit_fixP -> fx_bit_add = add; + + return bit_fixP; +} + +void +fix_new_ns32k (frag, where, size, add_symbol, sub_symbol, offset, pcrel, + pcrel_adjust, im_disp, bit_fixP, bsr) + fragS * frag; /* Which frag? */ + int where; /* Where in that frag? */ + short int size; /* 1, 2 or 4 usually. */ + symbolS * add_symbol; /* X_add_symbol. */ + symbolS * sub_symbol; /* X_subtract_symbol. */ + long offset; /* X_add_number. */ + int pcrel; /* TRUE if PC-relative relocation. */ + char pcrel_adjust; /* not zero if adjustment of pcrel offset is needed */ + char im_disp; /* true if the value to write is a displacement */ + bit_fixS *bit_fixP; /* pointer at struct of bit_fix's, ignored if NULL */ + char bsr; /* sequent-linker-hack: 1 when relocobject is a bsr */ + +{ + register fixS * fixP; + + fixP = (fixS *)obstack_alloc(¬es,sizeof(fixS)); + fixP -> fx_frag = frag; + fixP -> fx_where = where; + fixP -> fx_size = size; + fixP -> fx_addsy = add_symbol; + fixP -> fx_subsy = sub_symbol; + fixP -> fx_offset = offset; + fixP -> fx_pcrel = pcrel; + fixP -> fx_pcrel_adjust = pcrel_adjust; + fixP -> fx_im_disp = im_disp; + fixP -> fx_bit_fixP = bit_fixP; + fixP -> fx_bsr = bsr; + fixP -> fx_next = * seg_fix_rootP; + + * seg_fix_rootP = fixP; +} + +/* We have no need to default values of symbols. */ + +symbolS * +md_undefined_symbol (name) + char *name; +{ + return 0; +} + +/* Parse an operand that is machine-specific. + We just return without modifying the expression if we have nothing + to do. */ + +/* ARGSUSED */ +void +md_operand (expressionP) + expressionS *expressionP; +{ +} + +/* Round up a section size to the appropriate boundary. */ +long +md_section_align (segment, size) + segT segment; + long size; +{ + return size; /* Byte alignment is fine */ +} + +/* Exactly what point is a PC-relative offset relative TO? + On the National warts, they're relative to the address of the offset, + with some funny adjustments in some circumstances during blue moons. + (??? Is this right? FIXME-SOON) */ +long +md_pcrel_from (fixP) + fixS *fixP; +{ + long res; + res = fixP->fx_where + fixP->fx_frag->fr_address; +#ifdef SEQUENT_COMPATABILITY + if (fixP->fx_frag->fr_bsr) + res += 0x12 /* FOO Kludge alert! */ +#endif + return res; +} + +/* + * $Log$ + * Revision 1.1 1991/04/04 18:17:05 rich + * Initial revision + * + * + */ + +/* + * Local Variables: + * comment-column: 0 + * End: + */ + +/* end of tc-ns32k.c */ diff --git a/gas/config/tc-ns32k.h b/gas/config/tc-ns32k.h new file mode 100644 index 0000000..b96b650 --- /dev/null +++ b/gas/config/tc-ns32k.h @@ -0,0 +1,57 @@ +/* ns32k-opcode.h -- Opcode table for National Semi 32k processor + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#ifdef SEQUENT_COMPATABILITY +#define DEF_MODEC 20 +#define DEF_MODEL 21 +#endif + +#ifndef DEF_MODEC +#define DEF_MODEC 20 +#endif + +#ifndef DEF_MODEL +#define DEF_MODEL 20 +#endif + +#define MAX_ARGS 4 +#define ARG_LEN 50 + +#ifdef __STDC__ + +void fix_new_ns32k(fragS *frag, + int where, + void *add_symbol, /* really symbolS */ + void *sub_symbol, /* really symbolS */ + long offset, + int pcrel, + int pcrel_adjust, + int im_disp, + void *bit_fixP, /* really bit_fixS */ + int bsr); + +#else /* __STDC__ */ + +void fix_new_ns32k(); + +#endif /* __STDC__ */ + + +/* end of tc-ns32k.h */ diff --git a/gas/config/tc-sparc.c b/gas/config/tc-sparc.c new file mode 100644 index 0000000..bd71fa8 --- /dev/null +++ b/gas/config/tc-sparc.c @@ -0,0 +1,1297 @@ +/* tc-sparc.c -- Assemble for the SPARC + Copyright (C) 1989, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include <stdio.h> +#include <ctype.h> + +#include "as.h" + +/* careful, this file includes data *declarations* */ +#include "sparc-opcode.h" + +void md_begin(); +void md_end(); +void md_number_to_chars(); +void md_assemble(); +char *md_atof(); +void md_convert_frag(); +void md_create_short_jump(); +void md_create_long_jump(); +int md_estimate_size_before_relax(); +void md_ri_to_chars(); +symbolS *md_undefined_symbol(); +static void sparc_ip(); + +const relax_typeS md_relax_table[] = { + 0 }; + +/* handle of the OPCODE hash table */ +static struct hash_control *op_hash = NULL; + +static void s_seg(), s_proc(), s_data1(), s_reserve(), s_common(); +extern void s_globl(), s_long(), s_short(), s_space(), cons(); +extern void s_align_bytes(), s_ignore(); + +const pseudo_typeS md_pseudo_table[] = { + { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0) */ + { "common", s_common, 0 }, + { "global", s_globl, 0 }, + { "half", cons, 2 }, + { "optim", s_ignore, 0 }, + { "proc", s_proc, 0 }, + { "reserve", s_reserve, 0 }, + { "seg", s_seg, 0 }, + { "skip", s_space, 0 }, + { "word", cons, 4 }, + { NULL, 0, 0 }, +}; + +int md_short_jump_size = 4; +int md_long_jump_size = 4; +int md_reloc_size = 12; /* Size of relocation record */ + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful */ +char comment_chars[] = "!"; /* JF removed '|' from comment_chars */ + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that comments started like this one will always work */ +char line_comment_chars[] = "#"; + +/* Chars that can be used to separate mant from exp in floating point nums */ +char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +char FLT_CHARS[] = "rRsSfFdDxXpP"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c . Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. + */ + +static unsigned char octal[256]; +#define isoctal(c) octal[c] +static unsigned char toHex[256]; + +/* + * anull bit - causes the branch delay slot instructions to not be executed + */ +#define ANNUL (1 << 29) + +struct sparc_it { + char *error; + unsigned long opcode; + struct nlist *nlistp; + expressionS exp; + int pcrel; + enum reloc_type reloc; +} the_insn, set_insn; + +#ifdef __STDC__ +#if 0 +static void print_insn(struct sparc_it *insn); +#endif +static int getExpression(char *str); +#else +#if 0 +static void print_insn(); +#endif +static int getExpression(); +#endif +static char *expr_end; +static int special_case; + +/* + * Instructions that require wierd handling because they're longer than + * 4 bytes. + */ +#define SPECIAL_CASE_SET 1 +#define SPECIAL_CASE_FDIV 2 + +/* + * sort of like s_lcomm + * + */ +static void s_reserve() { + char *name; + char c; + char *p; + int temp; + symbolS *symbolP; + + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE(); + if (* input_line_pointer != ',') { + as_bad("Expected comma after name"); + ignore_rest_of_line(); + return; + } + input_line_pointer ++; + if ((temp = get_absolute_expression()) < 0) { + as_bad("BSS length (%d.) <0! Ignored.", temp); + ignore_rest_of_line(); + return; + } + *p = 0; + symbolP = symbol_find_or_make(name); + *p = c; + if (strncmp(input_line_pointer, ",\"bss\"", 6) != 0) { + as_bad("bad .reserve segment: `%s'", input_line_pointer); + return; + } + input_line_pointer += 6; + if (S_GET_OTHER(symbolP) == 0 + && S_GET_DESC(symbolP) == 0 + && ((S_GET_TYPE(symbolP) == N_BSS + && S_GET_VALUE(symbolP) == local_bss_counter) + || !S_IS_DEFINED(symbolP))) { + S_SET_VALUE(symbolP, local_bss_counter); + S_SET_SEGMENT(symbolP, SEG_BSS); + symbolP->sy_frag = & bss_address_frag; + local_bss_counter += temp; + } else { + as_warn("Ignoring attempt to re-define symbol from %d. to %d.", + S_GET_VALUE(symbolP), local_bss_counter); + } + demand_empty_rest_of_line(); + return; +} /* s_reserve() */ + +static void s_common() { + register char *name; + register char c; + register char *p; + register int temp; + register symbolS * symbolP; + + name = input_line_pointer; + c = get_symbol_end(); + /* just after name is now '\0' */ + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE(); + if (* input_line_pointer != ',') { + as_bad("Expected comma after symbol-name"); + ignore_rest_of_line(); + return; + } + input_line_pointer ++; /* skip ',' */ + if ((temp = get_absolute_expression ()) < 0) { + as_bad(".COMMon length (%d.) <0! Ignored.", temp); + ignore_rest_of_line(); + return; + } + *p = 0; + symbolP = symbol_find_or_make(name); + *p = c; + if (S_IS_DEFINED(symbolP)) { + as_bad("Ignoring attempt to re-define symbol"); + ignore_rest_of_line(); + return; + } + if (S_GET_VALUE(symbolP) != 0) { + if (S_GET_VALUE(symbolP) != temp) { + as_warn("Length of .comm \"%s\" is already %d. Not changed to %d.", + S_GET_NAME(symbolP), S_GET_VALUE(symbolP), temp); + } + } else { + S_SET_VALUE(symbolP, temp); + S_SET_EXTERNAL(symbolP); + } + know(symbolP->sy_frag == &zero_address_frag); + if (strncmp(input_line_pointer, ",\"bss\"", 6) != 0 + && strncmp(input_line_pointer, ",\"data\"", 7) != 0) { + p=input_line_pointer; + while(*p && *p!='\n') + p++; + c= *p; + *p='\0'; + as_bad("bad .common segment: `%s'", input_line_pointer); + *p=c; + return; + } + input_line_pointer += 6 + (input_line_pointer[2] == 'd'); /* Skip either */ + demand_empty_rest_of_line(); + return; +} /* s_common() */ + +static void s_seg() { + + if (strncmp(input_line_pointer, "\"text\"", 6) == 0) { + input_line_pointer += 6; + s_text(); + return; + } + if (strncmp(input_line_pointer, "\"data\"", 6) == 0) { + input_line_pointer += 6; + s_data(); + return; + } + if (strncmp(input_line_pointer, "\"data1\"", 7) == 0) { + input_line_pointer += 7; + s_data1(); + return; + } + if (strncmp(input_line_pointer, "\"bss\"", 5) == 0) { + input_line_pointer += 5; + /* We only support 2 segments -- text and data -- for now, so + things in the "bss segment" will have to go into data for now. + You can still allocate SEG_BSS stuff with .lcomm or .reserve. */ + subseg_new(SEG_DATA, 255); /* FIXME-SOMEDAY */ + return; + } + as_bad("Unknown segment type"); + demand_empty_rest_of_line(); + return; +} /* s_seg() */ + +static void s_data1() { + subseg_new(SEG_DATA, 1); + demand_empty_rest_of_line(); + return; +} /* s_data1() */ + +static void s_proc() { + extern char is_end_of_line[]; + + while (!is_end_of_line[*input_line_pointer]) { + ++input_line_pointer; + } + ++input_line_pointer; + return; +} /* s_proc() */ + +/* This function is called once, at assembler startup time. It should + set up all the tables, etc. that the MD part of the assembler will need. */ +void md_begin() { + register char *retval = NULL; + int lose = 0; + register unsigned int i = 0; + + op_hash = hash_new(); + if (op_hash == NULL) + as_fatal("Virtual memory exhausted"); + + while (i < NUMOPCODES) { + const char *name = sparc_opcodes[i].name; + retval = hash_insert(op_hash, name, &sparc_opcodes[i]); + if(retval != NULL && *retval != '\0') { + fprintf (stderr, "internal error: can't hash `%s': %s\n", + sparc_opcodes[i].name, retval); + lose = 1; + } + do + { + if (sparc_opcodes[i].match & sparc_opcodes[i].lose) { + fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n", + sparc_opcodes[i].name, sparc_opcodes[i].args); + lose = 1; + } + ++i; + } while (i < NUMOPCODES + && !strcmp(sparc_opcodes[i].name, name)); + } + + if (lose) + as_fatal("Broken assembler. No assembly attempted."); + + for (i = '0'; i < '8'; ++i) + octal[i] = 1; + for (i = '0'; i <= '9'; ++i) + toHex[i] = i - '0'; + for (i = 'a'; i <= 'f'; ++i) + toHex[i] = i + 10 - 'a'; + for (i = 'A'; i <= 'F'; ++i) + toHex[i] = i + 10 - 'A'; +} /* md_begin() */ + +void md_end() { + return; +} /* md_end() */ + +void md_assemble(str) +char *str; +{ + char *toP; + int rsd; + + know(str); + sparc_ip(str); + + /* See if "set" operand is absolute and small; skip sethi if so. */ + if (special_case == SPECIAL_CASE_SET && the_insn.exp.X_seg == SEG_ABSOLUTE) { + if (the_insn.exp.X_add_number >= -(1<<12) + && the_insn.exp.X_add_number < (1<<12)) { + the_insn.opcode = 0x80102000 /* or %g0,imm,... */ + | (the_insn.opcode & 0x3E000000) /* dest reg */ + | (the_insn.exp.X_add_number & 0x1FFF); /* imm */ + special_case = 0; /* No longer special */ + the_insn.reloc = NO_RELOC; /* No longer relocated */ + } + } + + toP = frag_more(4); + /* put out the opcode */ + md_number_to_chars(toP, the_insn.opcode, 4); + + /* put out the symbol-dependent stuff */ + if (the_insn.reloc != NO_RELOC) { + fix_new(frag_now, /* which frag */ + (toP - frag_now->fr_literal), /* where */ + 4, /* size */ + the_insn.exp.X_add_symbol, + the_insn.exp.X_subtract_symbol, + the_insn.exp.X_add_number, + the_insn.pcrel, + the_insn.reloc); + } + switch (special_case) { + + case SPECIAL_CASE_SET: + special_case = 0; + assert(the_insn.reloc == RELOC_HI22); + /* See if "set" operand has no low-order bits; skip OR if so. */ + if (the_insn.exp.X_seg == SEG_ABSOLUTE + && ((the_insn.exp.X_add_number & 0x3FF) == 0)) + return; + toP = frag_more(4); + rsd = (the_insn.opcode >> 25) & 0x1f; + the_insn.opcode = 0x80102000 | (rsd << 25) | (rsd << 14); + md_number_to_chars(toP, the_insn.opcode, 4); + fix_new(frag_now, /* which frag */ + (toP - frag_now->fr_literal), /* where */ + 4, /* size */ + the_insn.exp.X_add_symbol, + the_insn.exp.X_subtract_symbol, + the_insn.exp.X_add_number, + the_insn.pcrel, + RELOC_LO10); + return; + + case SPECIAL_CASE_FDIV: + /* According to information leaked from Sun, the "fdiv" instructions + on early SPARC machines would produce incorrect results sometimes. + The workaround is to add an fmovs of the destination register to + itself just after the instruction. This was true on machines + with Weitek 1165 float chips, such as the Sun-4/260 and /280. */ + special_case = 0; + assert(the_insn.reloc == NO_RELOC); + toP = frag_more(4); + rsd = (the_insn.opcode >> 25) & 0x1f; + the_insn.opcode = 0x81A00020 | (rsd << 25) | rsd; /* fmovs dest,dest */ + md_number_to_chars(toP, the_insn.opcode, 4); + return; + + case 0: + return; + + default: + abort(); + } +} /* md_assemble() */ + +static void sparc_ip(str) +char *str; +{ + char *s; + const char *args; + char c; + struct sparc_opcode *insn; + char *argsStart; + unsigned long opcode; + unsigned int mask; + int match = 0; + int comma = 0; + + for (s = str; islower(*s) || (*s >= '0' && *s <= '3'); ++s) + ; + switch (*s) { + + case '\0': + break; + + case ',': + comma = 1; + + /*FALLTHROUGH */ + + case ' ': + *s++ = '\0'; + break; + + default: + as_bad("Unknown opcode: `%s'", str); + exit(1); + } + if ((insn = (struct sparc_opcode *) hash_find(op_hash, str)) == NULL) { + as_bad("Unknown opcode: `%s'", str); + return; + } + if (comma) { + *--s = ','; + } + argsStart = s; + for (;;) { + opcode = insn->match; + bzero(&the_insn, sizeof(the_insn)); + the_insn.reloc = NO_RELOC; + + /* + * Build the opcode, checking as we go to make + * sure that the operands match + */ + for (args = insn->args; ; ++args) { + switch (*args) { + + case '\0': /* end of args */ + if (*s == '\0') { + match = 1; + } + break; + + case '+': + if (*s == '+') { + ++s; + continue; + } + if (*s == '-') { + continue; + } + break; + + case '[': /* these must match exactly */ + case ']': + case ',': + case ' ': + if (*s++ == *args) + continue; + break; + + case '#': /* must be at least one digit */ + if (isdigit(*s++)) { + while (isdigit(*s)) { + ++s; + } + continue; + } + break; + + case 'C': /* coprocessor state register */ + if (strncmp(s, "%csr", 4) == 0) { + s += 4; + continue; + } + break; + + case 'b': /* next operand is a coprocessor register */ + case 'c': + case 'D': + if (*s++ == '%' && *s++ == 'c' && isdigit(*s)) { + mask = *s++; + if (isdigit(*s)) { + mask = 10 * (mask - '0') + (*s++ - '0'); + if (mask >= 32) { + break; + } + } else { + mask -= '0'; + } + switch (*args) { + + case 'b': + opcode |= mask << 14; + continue; + + case 'c': + opcode |= mask; + continue; + + case 'D': + opcode |= mask << 25; + continue; + } + } + break; + + case 'r': /* next operand must be a register */ + case '1': + case '2': + case 'd': + if (*s++ == '%') { + switch (c = *s++) { + + case 'f': /* frame pointer */ + if (*s++ == 'p') { + mask = 0x1e; + break; + } + goto error; + + case 'g': /* global register */ + if (isoctal(c = *s++)) { + mask = c - '0'; + break; + } + goto error; + + case 'i': /* in register */ + if (isoctal(c = *s++)) { + mask = c - '0' + 24; + break; + } + goto error; + + case 'l': /* local register */ + if (isoctal(c = *s++)) { + mask= (c - '0' + 16) ; + break; + } + goto error; + + case 'o': /* out register */ + if (isoctal(c = *s++)) { + mask= (c - '0' + 8) ; + break; + } + goto error; + + case 's': /* stack pointer */ + if (*s++ == 'p') { + mask= 0xe; + break; + } + goto error; + + case 'r': /* any register */ + if (!isdigit(c = *s++)) { + goto error; + } + /* FALLTHROUGH */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (isdigit(*s)) { + if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) { + goto error; + } + } else { + c -= '0'; + } + mask= c; + break; + + default: + goto error; + } + /* + * Got the register, now figure out where + * it goes in the opcode. + */ + switch (*args) { + + case '1': + opcode |= mask << 14; + continue; + + case '2': + opcode |= mask; + continue; + + case 'd': + opcode |= mask << 25; + continue; + + case 'r': + opcode |= (mask << 25) | (mask << 14); + continue; + } + } + break; + + case 'e': /* next operand is a floating point register */ + case 'f': + case 'g': + if (*s++ == '%' && *s++ == 'f' && isdigit(*s)) { + mask = *s++; + if (isdigit(*s)) { + mask = 10 * (mask - '0') + (*s++ - '0'); + if (mask >= 32) { + break; + } + } else { + mask -= '0'; + } + switch (*args) { + + case 'e': + opcode |= mask << 14; + continue; + + case 'f': + opcode |= mask; + continue; + + case 'g': + opcode |= mask << 25; + continue; + } + } + break; + + case 'F': + if (strncmp(s, "%fsr", 4) == 0) { + s += 4; + continue; + } + break; + + case 'h': /* high 22 bits */ + the_insn.reloc = RELOC_HI22; + goto immediate; + + case 'l': /* 22 bit PC relative immediate */ + the_insn.reloc = RELOC_WDISP22; + the_insn.pcrel = 1; + goto immediate; + + case 'L': /* 30 bit immediate */ + the_insn.reloc = RELOC_WDISP30; + the_insn.pcrel = 1; + goto immediate; + + case 'i': /* 13 bit immediate */ + the_insn.reloc = RELOC_BASE13; + + /*FALLTHROUGH */ + + immediate: + if(*s==' ') + s++; + if (*s == '%') { + if ((c = s[1]) == 'h' && s[2] == 'i') { + the_insn.reloc = RELOC_HI22; + s+=3; + } else if (c == 'l' && s[2] == 'o') { + the_insn.reloc = RELOC_LO10; + s+=3; + } else + break; + } + /* Note that if the getExpression() fails, we will still have + created U entries in the symbol table for the 'symbols' + in the input string. Try not to create U symbols for + registers, etc. */ + { + /* This stuff checks to see if the expression ends + in +%reg If it does, it removes the register from + the expression, and re-sets 's' to point to the + right place */ + + char *s1; + + for(s1=s;*s1 && *s1!=','&& *s1!=']';s1++) + ; + + if(s1!=s && isdigit(s1[-1])) { + if(s1[-2]=='%' && s1[-3]=='+') { + s1-=3; + *s1='\0'; + (void)getExpression(s); + *s1='+'; + s=s1; + continue; + } else if(strchr("goli0123456789",s1[-2]) && s1[-3]=='%' && s1[-4]=='+') { + s1-=4; + *s1='\0'; + (void)getExpression(s); + *s1='+'; + s=s1; + continue; + } + } + } + (void)getExpression(s); + s = expr_end; + continue; + + case 'a': + if (*s++ == 'a') { + opcode |= ANNUL; + continue; + } + break; + + case 'A': /* alternate space */ + if (isdigit(*s)) { + long num; + + num=0; + while (isdigit(*s)) { + num= num*10 + *s-'0'; + ++s; + } + opcode |= num<<5; + continue; + } + break; + /* abort(); */ + + case 'p': + if (strncmp(s, "%psr", 4) == 0) { + s += 4; + continue; + } + break; + + case 'q': /* floating point queue */ + if (strncmp(s, "%fq", 3) == 0) { + s += 3; + continue; + } + break; + + case 'Q': /* coprocessor queue */ + if (strncmp(s, "%cq", 3) == 0) { + s += 3; + continue; + } + break; + + case 'S': + if (strcmp(str, "set") == 0) { + special_case = SPECIAL_CASE_SET; + continue; + } else if (strncmp(str, "fdiv", 4) == 0) { + special_case = SPECIAL_CASE_FDIV; + continue; + } + break; + + case 't': + if (strncmp(s, "%tbr", 4) != 0) + break; + s += 4; + continue; + + case 'w': + if (strncmp(s, "%wim", 4) != 0) + break; + s += 4; + continue; + + case 'y': + if (strncmp(s, "%y", 2) != 0) + break; + s += 2; + continue; + + default: + abort(); + } + break; + } + error: + if (match == 0) { + /* Args don't match. */ + if (((unsigned) (&insn[1] - sparc_opcodes)) < NUMOPCODES + && !strcmp(insn->name, insn[1].name)) { + ++insn; + s = argsStart; + continue; + } + else + { + as_bad("Illegal operands"); + return; + } + } + break; + } + + the_insn.opcode = opcode; + return; +} /* sparc_ip() */ + +static int getExpression(str) +char *str; +{ + char *save_in; + segT seg; + + save_in = input_line_pointer; + input_line_pointer = str; + switch (seg = expression(&the_insn.exp)) { + + case SEG_ABSOLUTE: + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_UNKNOWN: + case SEG_DIFFERENCE: + case SEG_BIG: + case SEG_ABSENT: + break; + + default: + the_insn.error = "bad segment"; + expr_end = input_line_pointer; + input_line_pointer=save_in; + return 1; + } + expr_end = input_line_pointer; + input_line_pointer = save_in; + return 0; +} /* getExpression() */ + + +/* + This is identical to the md_atof in m68k.c. I think this is right, + but I'm not sure. + + Turn a string in input_line_pointer into a floating point constant of type + type, and store the appropriate bytes in *litP. The number of LITTLENUMS + emitted is stored in *sizeP . An error message is returned, or NULL on OK. + */ + +/* Equal to MAX_PRECISION in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +char *md_atof(type,litP,sizeP) +char type; +char *litP; +int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + char *atof_ieee(); + + switch(type) { + + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP=0; + return "Bad call to MD_ATOF()"; + } + t=atof_ieee(input_line_pointer,type,words); + if(t) + input_line_pointer=t; + *sizeP=prec * sizeof(LITTLENUM_TYPE); + for(wordP=words;prec--;) { + md_number_to_chars(litP,(long)(*wordP++),sizeof(LITTLENUM_TYPE)); + litP+=sizeof(LITTLENUM_TYPE); + } + return ""; /* Someone should teach Dean about null pointers */ +} /* md_atof() */ + +/* + * Write out big-endian. + */ +void md_number_to_chars(buf,val,n) +char *buf; +long val; +int n; +{ + + switch(n) { + + case 4: + *buf++ = val >> 24; + *buf++ = val >> 16; + case 2: + *buf++ = val >> 8; + case 1: + *buf = val; + break; + + default: + abort(); + } + return; +} /* md_number_to_chars() */ + +/* Apply a fixS to the frags, now that we know the value it ought to + hold. */ + +void md_apply_fix(fixP, val) +fixS *fixP; +long val; +{ + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + + assert(fixP->fx_size == 4); + assert(fixP->fx_r_type < NO_RELOC); + + fixP->fx_addnumber = val; /* Remember value for emit_reloc */ + + /* + * This is a hack. There should be a better way to + * handle this. + */ + if (fixP->fx_r_type == RELOC_WDISP30 && fixP->fx_addsy) { + val += fixP->fx_where + fixP->fx_frag->fr_address; + } + + switch (fixP->fx_r_type) { + + case RELOC_32: + buf[0] = 0; /* val >> 24; */ + buf[1] = 0; /* val >> 16; */ + buf[2] = 0; /* val >> 8; */ + buf[3] = 0; /* val; */ + break; + +#if 0 + case RELOC_8: /* These don't seem to ever be needed. */ + case RELOC_16: + case RELOC_DISP8: + case RELOC_DISP16: + case RELOC_DISP32: +#endif + case RELOC_WDISP30: + val = (val >>= 2) + 1; + buf[0] |= (val >> 24) & 0x3f; + buf[1]= (val >> 16); + buf[2] = val >> 8; + buf[3] = val; + break; + + case RELOC_HI22: + if(!fixP->fx_addsy) { + buf[1] |= (val >> 26) & 0x3f; + buf[2] = val >> 18; + buf[3] = val >> 10; + } else { + buf[2]=0; + buf[3]=0; + } + break; +#if 0 + case RELOC_22: + case RELOC_13: +#endif + case RELOC_LO10: + if(!fixP->fx_addsy) { + buf[2] |= (val >> 8) & 0x03; + buf[3] = val; + } else + buf[3]=0; + break; +#if 0 + case RELOC_SFA_BASE: + case RELOC_SFA_OFF13: + case RELOC_BASE10: +#endif + case RELOC_BASE13: + buf[2] |= (val >> 8) & 0x1f; + buf[3] = val; + break; + + case RELOC_WDISP22: + val = (val >>= 2) + 1; + /* FALLTHROUGH */ + case RELOC_BASE22: + buf[1] |= (val >> 16) & 0x3f; + buf[2] = val >> 8; + buf[3] = val; + break; + +#if 0 + case RELOC_PC10: + case RELOC_PC22: + case RELOC_JMP_TBL: + case RELOC_SEGOFF16: + case RELOC_GLOB_DAT: + case RELOC_JMP_SLOT: + case RELOC_RELATIVE: +#endif + + case NO_RELOC: + default: + as_bad("bad relocation type: 0x%02x", fixP->fx_r_type); + break; + } +} /* md_apply_fix() */ + +/* should never be called for sparc */ +void md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol) +char *ptr; +long from_addr; +long to_addr; +fragS *frag; +symbolS *to_symbol; +{ + fprintf(stderr, "sparc_create_short_jmp\n"); + abort(); +} /* md_create_short_jump() */ + +/* Translate internal representation of relocation info to target format. + + On sparc: first 4 bytes are normal unsigned long address, next three + bytes are index, most sig. byte first. Byte 7 is broken up with + bit 7 as external, bits 6 & 5 unused, and the lower + five bits as relocation type. Next 4 bytes are long addend. */ +/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */ +void md_ri_to_chars(the_bytes, ri) +char *the_bytes; +struct reloc_info_generic *ri; +{ + /* this is easy */ + md_number_to_chars(the_bytes, ri->r_address, 4); + /* now the fun stuff */ + the_bytes[4] = (ri->r_index >> 16) & 0x0ff; + the_bytes[5] = (ri->r_index >> 8) & 0x0ff; + the_bytes[6] = ri->r_index & 0x0ff; + the_bytes[7] = ((ri->r_extern << 7) & 0x80) | (0 & 0x60) | (ri->r_type & 0x1F); + /* Also easy */ + md_number_to_chars(&the_bytes[8], ri->r_addend, 4); +} /* md_ri_to_chars() */ + +/* should never be called for sparc */ +void md_convert_frag(fragP) +register fragS *fragP; +{ + fprintf(stderr, "sparc_convert_frag\n"); + abort(); +} /* md_convert_frag() */ + +/* should never be called for sparc */ +void md_create_long_jump(ptr, from_addr, to_addr, frag, to_symbol) +char *ptr; +long from_addr, to_addr; +fragS *frag; +symbolS *to_symbol; +{ + fprintf(stderr, "sparc_create_long_jump\n"); + abort(); +} /* md_create_long_jump() */ + +/* should never be called for sparc */ +int md_estimate_size_before_relax(fragP, segtype) +fragS *fragP; +segT segtype; +{ + fprintf(stderr, "sparc_estimate_size_before_relax\n"); + abort(); + return 0; +} /* md_estimate_size_before_relax() */ + +#if 0 +/* for debugging only */ +static void print_insn(insn) +struct sparc_it *insn; +{ + char *Reloc[] = { + "RELOC_8", + "RELOC_16", + "RELOC_32", + "RELOC_DISP8", + "RELOC_DISP16", + "RELOC_DISP32", + "RELOC_WDISP30", + "RELOC_WDISP22", + "RELOC_HI22", + "RELOC_22", + "RELOC_13", + "RELOC_LO10", + "RELOC_SFA_BASE", + "RELOC_SFA_OFF13", + "RELOC_BASE10", + "RELOC_BASE13", + "RELOC_BASE22", + "RELOC_PC10", + "RELOC_PC22", + "RELOC_JMP_TBL", + "RELOC_SEGOFF16", + "RELOC_GLOB_DAT", + "RELOC_JMP_SLOT", + "RELOC_RELATIVE", + "NO_RELOC" + }; + + if (insn->error) { + fprintf(stderr, "ERROR: %s\n"); + } + fprintf(stderr, "opcode=0x%08x\n", insn->opcode); + fprintf(stderr, "reloc = %s\n", Reloc[insn->reloc]); + fprintf(stderr, "exp = { +\n"); + fprintf(stderr, "\t\tX_add_symbol = %s\n", + ((insn->exp.X_add_symbol != NULL) + ? ((S_GET_NAME(insn->exp.X_add_symbol) != NULL) + ? S_GET_NAME(insn->exp.X_add_symbol) + : "???") + : "0")); + fprintf(stderr, "\t\tX_sub_symbol = %s\n", + ((insn->exp.X_subtract_symbol != NULL) + ? (S_GET_NAME(insn->exp.X_subtract_symbol) + ? S_GET_NAME(insn->exp.X_subtract_symbol) + : "???") + : "0")); + fprintf(stderr, "\t\tX_add_number = %d\n", + insn->exp.X_add_number); + fprintf(stderr, "}\n"); + return; +} /* print_insn() */ +#endif + +/* Set the hook... */ + +void emit_sparc_reloc(); +void (*md_emit_relocations)() = emit_sparc_reloc; + +/* + * Sparc/AM29K relocations are completely different, so it needs + * this machine dependent routine to emit them. + */ +#if defined(OBJ_AOUT) || defined(OBJ_BOUT) +void emit_sparc_reloc(fixP, segment_address_in_file) +register fixS *fixP; +relax_addressT segment_address_in_file; +{ + struct reloc_info_generic ri; + register symbolS *symbolP; + extern char *next_object_file_charP; + /* long add_number; */ + + bzero((char *) &ri, sizeof(ri)); + for (; fixP; fixP = fixP->fx_next) { + + if (fixP->fx_r_type >= NO_RELOC) { + fprintf(stderr, "fixP->fx_r_type = %d\n", fixP->fx_r_type); + abort(); + } + + if ((symbolP = fixP->fx_addsy) != NULL) { + ri.r_address = fixP->fx_frag->fr_address + + fixP->fx_where - segment_address_in_file; + if ((S_GET_TYPE(symbolP)) == N_UNDF) { + ri.r_extern = 1; + ri.r_index = symbolP->sy_number; + } else { + ri.r_extern = 0; + ri.r_index = S_GET_TYPE(symbolP); + } + if (symbolP && symbolP->sy_frag) { + ri.r_addend = symbolP->sy_frag->fr_address; + } + ri.r_type = fixP->fx_r_type; + if (fixP->fx_pcrel) { + /* ri.r_addend -= fixP->fx_where; */ + ri.r_addend -= ri.r_address; + } else { + ri.r_addend = fixP->fx_addnumber; + } + + md_ri_to_chars(next_object_file_charP, &ri); + next_object_file_charP += md_reloc_size; + } + } + return; +} /* emit_sparc_reloc() */ +#endif /* aout or bout */ + +int md_parse_option(argP,cntP,vecP) +char **argP; +int *cntP; +char ***vecP; +{ + return 1; +} /* md_parse_option() */ + +/* We have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS *md_undefined_symbol(name) +char *name; +{ + return 0; +} /* md_undefined_symbol() */ + +/* Parse an operand that is machine-specific. + We just return without modifying the expression if we have nothing + to do. */ + +/* ARGSUSED */ +void md_operand(expressionP) +expressionS *expressionP; +{ +} /* md_operand() */ + +/* Round up a section size to the appropriate boundary. */ +long md_section_align (segment, size) +segT segment; +long size; +{ + return (size + 7) & ~7; /* Round all sects to multiple of 8 */ +} /* md_section_align() */ + +/* Exactly what point is a PC-relative offset relative TO? + On the sparc, they're relative to the address of the offset, plus + its size. This gets us to the following instruction. + (??? Is this right? FIXME-SOON) */ +long md_pcrel_from(fixP) +fixS *fixP; +{ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} /* md_pcrel_from() */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of tp-sparc.c */ diff --git a/gas/config/tc-sparc.h b/gas/config/tc-sparc.h new file mode 100644 index 0000000..dd19fdb --- /dev/null +++ b/gas/config/tc-sparc.h @@ -0,0 +1,42 @@ +/* tc-sparc.h - Macros and type defines for the sparc. + Copyright (C) 1989, 1990, 1991 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 1, +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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +#define TC_SPARC 1 + +#ifdef OBJ_BOUT +#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE ((0x103 << 16) | BMAGIC) /* Magic number for header */ +#else +#ifdef OBJ_AOUT +#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE ((0x103 << 16) | OMAGIC) /* Magic number for header */ +#endif /* OBJ_AOUT */ +#endif /* OBJ_BOUT */ + +#define tc_headers_hook(a) ; /* don't need it. */ +#define tc_crawl_symbol_chain(a) ; /* don't need it. */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of tp-sparc.h */ diff --git a/gas/config/tc-vax.c b/gas/config/tc-vax.c new file mode 100644 index 0000000..11095c6 --- /dev/null +++ b/gas/config/tc-vax.c @@ -0,0 +1,3337 @@ +/* vax.c - vax-specific - + Copyright (C) 1987, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +/* JF I moved almost all the vax specific stuff into this one file 'cuz RMS + seems to think its a good idea. I hope I managed to get all the VAX-isms */ + + +#include "as.h" + +#include "read.h" +#include "flonum.h" +#include "vax-inst.h" +#include "obstack.h" /* For FRAG_APPEND_1_CHAR macro in "frags.h" */ +#include "frags.h" +#include "expr.h" +#include "symbols.h" + +/* These chars start a comment anywhere in a source file (except inside + another comment */ +const char comment_chars[] = "#"; + +/* These chars only start a comment at the beginning of a line. */ +/* Note that for the VAX the are the same as comment_chars above. */ +const char line_comment_chars[] = "#"; + +/* Chars that can be used to separate mant from exp in floating point nums */ +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* as in 0f123.456 */ +/* or 0H1.234E-12 (see exp chars above) */ +const char FLT_CHARS[] = "dDfFgGhH"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c . Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. + */ + +static expressionS /* Hold details of an operand expression */ + exp_of_operand[VIT_MAX_OPERANDS]; + +static struct vit + v; /* A vax instruction after decoding. */ + +LITTLENUM_TYPE big_operand_bits[VIT_MAX_OPERANDS][SIZE_OF_LARGE_NUMBER]; + /* Hold details of big operands. */ +FLONUM_TYPE float_operand[VIT_MAX_OPERANDS]; + /* Above is made to point into */ + /* big_operand_bits by md_begin(). */ + +/* + * For VAX, relative addresses of "just the right length" are easy. + * The branch displacement is always the last operand, even in + * synthetic instructions. + * For VAX, we encode the relax_substateTs (in e.g. fr_substate) as: + * + * 4 3 2 1 0 bit number + * ---/ /--+-------+-------+-------+-------+-------+ + * | what state ? | how long ? | + * ---/ /--+-------+-------+-------+-------+-------+ + * + * The "how long" bits are 00=byte, 01=word, 10=long. + * This is a Un*x convention. + * Not all lengths are legit for a given value of (what state). + * The "how long" refers merely to the displacement length. + * The address usually has some constant bytes in it as well. + * + +groups for VAX address relaxing. + +1. "foo" pc-relative. + length of byte, word, long + +2a. J<cond> where <cond> is a simple flag test. + length of byte, word, long. + VAX opcodes are: (Hex) + bneq/bnequ 12 + beql/beqlu 13 + bgtr 14 + bleq 15 + bgeq 18 + blss 19 + bgtru 1a + blequ 1b + bvc 1c + bvs 1d + bgequ/bcc 1e + blssu/bcs 1f + Always, you complement 0th bit to reverse condition. + Always, 1-byte opcode, then 1-byte displacement. + +2b. J<cond> where cond tests a memory bit. + length of byte, word, long. + Vax opcodes are: (Hex) + bbs e0 + bbc e1 + bbss e2 + bbcs e3 + bbsc e4 + bbcc e5 + bbssi e6 + bbcci e7 + Always, you complement 0th bit to reverse condition. + Always, 1-byte opcde, longword-address, byte-address, 1-byte-displacement + +2c. J<cond> where cond tests low-order memory bit + length of byte,word,long. + Vax opcodes are: (Hex) + blbs e8 + blbc e9 + Always, you complement 0th bit to reverse condition. + Always, 1-byte opcode, longword-address, 1-byte displacement. + +3. Jbs/Jbr. + length of byte,word,long. + Vax opcodes are: (Hex) + bsbb 10 + brb 11 + These are like (2) but there is no condition to reverse. + Always, 1 byte opcode, then displacement/absolute. + +4a. JacbX + length of word, long. + Vax opcodes are: (Hex) + acbw 3d + acbf 4f + acbd 6f + abcb 9d + acbl f1 + acbg 4ffd + acbh 6ffd + Always, we cannot reverse the sense of the branch; we have a word + displacement. + The double-byte op-codes don't hurt: we never want to modify the + opcode, so we don't care how many bytes are between the opcode and + the operand. + +4b. JXobXXX + length of long, long, byte. + Vax opcodes are: (Hex) + aoblss f2 + aobleq f3 + sobgeq f4 + sobgtr f5 + Always, we cannot reverse the sense of the branch; we have a byte + displacement. + +The only time we need to modify the opcode is for class 2 instructions. +After relax() we may complement the lowest order bit of such instruction +to reverse sense of branch. + +For class 2 instructions, we store context of "where is the opcode literal". +We can change an opcode's lowest order bit without breaking anything else. + +We sometimes store context in the operand literal. This way we can figure out +after relax() what the original addressing mode was. +*/ + + /* These displacements are relative to */ + /* the start address of the displacement. */ + /* The first letter is Byte, Word. */ + /* 2nd letter is Forward, Backward. */ +#define BF (1+ 127) +#define BB (1+-128) +#define WF (2+ 32767) +#define WB (2+-32768) + /* Dont need LF, LB because they always */ + /* reach. [They are coded as 0.] */ + + +#define C(a,b) ENCODE_RELAX(a,b) + /* This macro has no side-effects. */ +#define ENCODE_RELAX(what,length) (((what) << 2) + (length)) + +const relax_typeS +md_relax_table[] = +{ + { + 1, 1, 0, 0 + }, /* error sentinel 0,0 */ + { + 1, 1, 0, 0 + }, /* unused 0,1 */ + { + 1, 1, 0, 0 + }, /* unused 0,2 */ + { + 1, 1, 0, 0 + }, /* unused 0,3 */ + { + BF + 1, BB + 1, 2, C (1, 1) + }, /* B^"foo" 1,0 */ + { + WF + 1, WB + 1, 3, C (1, 2) + }, /* W^"foo" 1,1 */ + { + 0, 0, 5, 0 + }, /* L^"foo" 1,2 */ + { + 1, 1, 0, 0 + }, /* unused 1,3 */ + { + BF, BB, 1, C (2, 1) + }, /* b<cond> B^"foo" 2,0 */ + { + WF + 2, WB + 2, 4, C (2, 2) + }, /* br.+? brw X 2,1 */ + { + 0, 0, 7, 0 + }, /* br.+? jmp X 2,2 */ + { + 1, 1, 0, 0 + }, /* unused 2,3 */ + { + BF, BB, 1, C (3, 1) + }, /* brb B^foo 3,0 */ + { + WF, WB, 2, C (3, 2) + }, /* brw W^foo 3,1 */ + { + 0, 0, 5, 0 + }, /* Jmp L^foo 3,2 */ + { + 1, 1, 0, 0 + }, /* unused 3,3 */ + { + 1, 1, 0, 0 + }, /* unused 4,0 */ + { + WF, WB, 2, C (4, 2) + }, /* acb_ ^Wfoo 4,1 */ + { + 0, 0, 10, 0 + }, /* acb_,br,jmp L^foo4,2 */ + { + 1, 1, 0, 0 + }, /* unused 4,3 */ + { + BF, BB, 1, C (5, 1) + }, /* Xob___,,foo 5,0 */ + { + WF + 4, WB + 4, 6, C (5, 2) + }, /* Xob.+2,brb.+3,brw5,1 */ + { + 0, 0, 9, 0 + }, /* Xob.+2,brb.+6,jmp5,2 */ +}; + +#undef C +#undef BF +#undef BB +#undef WF +#undef WB + +void float_cons (); + +const pseudo_typeS md_pseudo_table[] = +{ + {"dfloat", float_cons, 'd'}, + {"ffloat", float_cons, 'f'}, + {"gfloat", float_cons, 'g'}, + {"hfloat", float_cons, 'h'}, + {0} +}; + +#define STATE_PC_RELATIVE (1) +#define STATE_CONDITIONAL_BRANCH (2) +#define STATE_ALWAYS_BRANCH (3) /* includes BSB... */ +#define STATE_COMPLEX_BRANCH (4) +#define STATE_COMPLEX_HOP (5) + +#define STATE_BYTE (0) +#define STATE_WORD (1) +#define STATE_LONG (2) +#define STATE_UNDF (3) /* Symbol undefined in pass1 */ + + +#define min(a, b) ((a) < (b) ? (a) : (b)) + + +void +md_begin () +{ + char *vip_begin (); + char *errtxt; + FLONUM_TYPE *fP; + int i; + + if (*(errtxt = vip_begin (TRUE, "$", "*", "`"))) + { + as_fatal("VIP_BEGIN error:%s", errtxt); + } + + for (i = 0, fP = float_operand; + fP < float_operand + VIT_MAX_OPERANDS; + i++, fP++) + { + fP->low = &big_operand_bits[i][0]; + fP->high = &big_operand_bits[i][SIZE_OF_LARGE_NUMBER - 1]; + } +} + +void +md_end () +{ + vip_end (); +} + +void /* Knows about order of bytes in address. */ +md_number_to_chars (con, value, nbytes) + char con[]; /* Return 'nbytes' of chars here. */ + long value; /* The value of the bits. */ + int nbytes; /* Number of bytes in the output. */ +{ + int n; + long v; + + n = nbytes; + v = value; + while (nbytes--) + { + *con++ = value; /* Lint wants & MASK_CHAR. */ + value >>= BITS_PER_CHAR; + } + /* XXX line number probably botched for this warning message. */ + if (value != 0 && value != -1) + as_bad("Displacement (%ld) long for instruction field length (%d).", v, n); +} + +/* Fix up some data or instructions after we find out the value of a symbol + that they reference. */ + +void /* Knows about order of bytes in address. */ +md_apply_fix(fixP, value) + fixS *fixP; /* Fixup struct pointer */ + long value; /* The value of the bits. */ +{ + char *buf = fixP->fx_where + fixP->fx_frag->fr_literal; + int nbytes; /* Number of bytes in the output. */ + + nbytes = fixP->fx_size; + while (nbytes--) + { + *buf++ = value; /* Lint wants & MASK_CHAR. */ + value >>= BITS_PER_CHAR; + } +} + +long /* Knows about the byte order in a word. */ +md_chars_to_number (con, nbytes) + unsigned char con[]; /* Low order byte 1st. */ + int nbytes; /* Number of bytes in the input. */ +{ + long retval; + for (retval = 0, con += nbytes - 1; nbytes--; con--) + { + retval <<= BITS_PER_CHAR; + retval |= *con; + } + return retval; +} + +/* vax:md_assemble() emit frags for 1 instruction */ + +void +md_assemble (instruction_string) + char *instruction_string; /* A string: assemble 1 instruction. */ +{ + char *p; + register struct vop *operandP;/* An operand. Scans all operands. */ + char *save_input_line_pointer; + char c_save; /* What used to live after an expression. */ + struct frag *fragP; /* Fragment of code we just made. */ + register int goofed; /* TRUE: instruction_string bad for all passes. */ + register struct vop *end_operandP; /* -> slot just after last operand */ + /* Limit of the for (each operand). */ + register expressionS *expP; /* -> expression values for this operand */ + + /* These refer to an instruction operand expression. */ + segT to_seg; /* Target segment of the address. */ + register valueT this_add_number; + register struct symbol *this_add_symbol; /* +ve (minuend) symbol. */ + register struct symbol *this_subtract_symbol; /* -ve(subtrahend) symbol. */ + + long opcode_as_number; /* As a number. */ + char *opcode_as_chars; /* Least significant byte 1st. */ + /* As an array of characters. */ + char *opcode_low_byteP; /* Least significant byte 1st */ + struct details *detP; /* The details of an ADxxx frag. */ + int length; /* length (bytes) meant by vop_short. */ + int at; /* 0, or 1 if '@' is in addressing mode. */ + int nbytes; /* From vop_nbytes: vax_operand_width (in bytes) */ + FLONUM_TYPE *floatP; + char *vip (); + LITTLENUM_TYPE literal_float[8]; + /* Big enough for any floating point literal. */ + + if (*(p = vip (&v, instruction_string))) + { + as_fatal("vax_assemble\"%s\" in=\"%s\"", p, instruction_string); + } + /* + * Now we try to find as many as_warn()s as we can. If we do any as_warn()s + * then goofed=TRUE. Notice that we don't make any frags yet. + * Should goofed be TRUE, then this instruction will wedge in any pass, + * and we can safely flush it, without causing interpass symbol phase + * errors. That is, without changing label values in different passes. + */ + if (goofed = (*v.vit_error)) + { + as_warn ("Ignoring statement due to \"%s\"", v.vit_error); + } + /* + * We need to use expression() and friends, which require us to diddle + * input_line_pointer. So we save it and restore it later. + */ + save_input_line_pointer = input_line_pointer; + for (operandP = v.vit_operand, + expP = exp_of_operand, + floatP = float_operand, + end_operandP = v.vit_operand + v.vit_operands; + + operandP < end_operandP; + + operandP++, + expP++, + floatP++ + ) /* for each operand */ + { + if (*(operandP->vop_error)) + { + as_warn ("Ignoring statement because \"%s\"", (operandP->vop_error)); + goofed = TRUE; + } + else + { /* statement has no syntax goofs: lets sniff the expression */ + int can_be_short; /* TRUE if a bignum can be reduced to a short literal. */ + + input_line_pointer = operandP->vop_expr_begin; + c_save = operandP->vop_expr_end[1]; + operandP->vop_expr_end[1] = '\0'; + /* If to_seg == SEG_PASS1, expression() will have set need_pass_2 = TRUE. */ + switch (to_seg = expression (expP)) + { + case SEG_ABSENT: + /* for BSD4.2 compatibility, missing expression is absolute 0 */ + to_seg = expP->X_seg = SEG_ABSOLUTE; + expP->X_add_number = 0; + /* for SEG_ABSOLUTE, we shouldnt need to set X_subtract_symbol, X_add_symbol to any particular value. */ + /* But, we will program defensively. Since this situation occurs */ + /* rarely so it costs us little to do, and stops Dean */ + /* worrying about the origin of random bits in expressionS's. */ + expP->X_add_symbol = NULL; + expP->X_subtract_symbol = NULL; + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_ABSOLUTE: + case SEG_UNKNOWN: + break; + + case SEG_DIFFERENCE: + case SEG_PASS1: + /* + * Major bug. We can't handle the case of a + * SEG_DIFFERENCE expression in a VIT_OPCODE_SYNTHETIC + * variable-length instruction. + * We don't have a frag type that is smart enough to + * relax a SEG_DIFFERENCE, and so we just force all + * SEG_DIFFERENCEs to behave like SEG_PASS1s. + * Clearly, if there is a demand we can invent a new or + * modified frag type and then coding up a frag for this + * case will be easy. SEG_DIFFERENCE was invented for the + * .words after a CASE opcode, and was never intended for + * instruction operands. + */ + need_pass_2 = TRUE; + as_warn("Can't relocate expression"); + break; + + case SEG_BIG: + /* Preserve the bits. */ + if (expP->X_add_number > 0) + { + bignum_copy (generic_bignum, expP->X_add_number, + floatP->low, SIZE_OF_LARGE_NUMBER); + } + else + { + know (expP->X_add_number < 0); + flonum_copy (&generic_floating_point_number, + floatP); + if (strchr ("s i", operandP->vop_short)) + { /* Could possibly become S^# */ + flonum_gen2vax (-expP->X_add_number, floatP, literal_float); + switch (-expP->X_add_number) + { + case 'f': + can_be_short = + (literal_float[0] & 0xFC0F) == 0x4000 + && literal_float[1] == 0; + break; + + case 'd': + can_be_short = + (literal_float[0] & 0xFC0F) == 0x4000 + && literal_float[1] == 0 + && literal_float[2] == 0 + && literal_float[3] == 0; + break; + + case 'g': + can_be_short = + (literal_float[0] & 0xFF81) == 0x4000 + && literal_float[1] == 0 + && literal_float[2] == 0 + && literal_float[3] == 0; + break; + + case 'h': + can_be_short = + (literal_float[0] & 0xFFF8) == 0x4000 + && (literal_float[1] & 0xE000) == 0 + && literal_float[2] == 0 + && literal_float[3] == 0 + && literal_float[4] == 0 + && literal_float[5] == 0 + && literal_float[6] == 0 + && literal_float[7] == 0; + break; + + default: + BAD_CASE (-expP->X_add_number); + break; + } /* switch (float type) */ + } /* if (could want to become S^#...) */ + } /* bignum or flonum ? */ + + if (operandP->vop_short == 's' + || operandP->vop_short == 'i' + || (operandP->vop_short == ' ' + && operandP->vop_reg == 0xF + && (operandP->vop_mode & 0xE) == 0x8)) + { + /* Saw a '#'. */ + if (operandP->vop_short == ' ') + { /* We must chose S^ or I^. */ + if (expP->X_add_number > 0) + { /* Bignum: Short literal impossible. */ + operandP->vop_short = 'i'; + operandP->vop_mode = 8; + operandP->vop_reg = 0xF; /* VAX PC. */ + } + else + { /* Flonum: Try to do it. */ + if (can_be_short) + { + operandP->vop_short = 's'; + operandP->vop_mode = 0; + operandP->vop_ndx = -1; + operandP->vop_reg = -1; + /* JF hope this is the right thing */ + expP->X_seg = SEG_ABSOLUTE; + } + else + { + operandP->vop_short = 'i'; + operandP->vop_mode = 8; + operandP->vop_reg = 0xF; /* VAX PC */ + } + } /* bignum or flonum ? */ + } /* if #, but no S^ or I^ seen. */ + /* No more ' ' case: either 's' or 'i'. */ + if (operandP->vop_short == 's') + { + /* Wants to be a short literal. */ + if (expP->X_add_number > 0) + { + as_warn ("Bignum not permitted in short literal. Immediate mode assumed."); + operandP->vop_short = 'i'; + operandP->vop_mode = 8; + operandP->vop_reg = 0xF; /* VAX PC. */ + } + else + { + if (!can_be_short) + { + as_warn ("Can't do flonum short literal: immediate mode used."); + operandP->vop_short = 'i'; + operandP->vop_mode = 8; + operandP->vop_reg = 0xF; /* VAX PC. */ + } + else + { /* Encode short literal now. */ + register int temp; + + switch (-expP->X_add_number) + { + case 'f': + case 'd': + temp = literal_float[0] >> 4; + break; + + case 'g': + temp = literal_float[0] >> 1; + break; + + case 'h': + temp = ((literal_float[0] << 3) & 070) + | ((literal_float[1] >> 13) & 07); + break; + + default: + BAD_CASE (-expP->X_add_number); + break; + } + + floatP->low[0] = temp & 077; + floatP->low[1] = 0; + } /* if can be short literal float */ + } /* flonum or bignum ? */ + } + else + { /* I^# seen: set it up if float. */ + if (expP->X_add_number < 0) + { + bcopy (literal_float, floatP->low, sizeof (literal_float)); + } + } /* if S^# seen. */ + } + else + { + as_warn ("A bignum/flonum may not be a displacement: 0x%x used", + expP->X_add_number = 0x80000000); + /* Chosen so luser gets the most offset bits to patch later. */ + } + expP->X_add_number = floatP->low[0] + | ((LITTLENUM_MASK & (floatP->low[1])) << LITTLENUM_NUMBER_OF_BITS); +/* + * For the SEG_BIG case we have: + * If vop_short == 's' then a short floating literal is in the + * lowest 6 bits of floatP -> low [0], which is + * big_operand_bits [---] [0]. + * If vop_short == 'i' then the appropriate number of elements + * of big_operand_bits [---] [...] are set up with the correct + * bits. + * Also, just in case width is byte word or long, we copy the lowest + * 32 bits of the number to X_add_number. + */ + break; + + default: + BAD_CASE (to_seg); + break; + } + if (input_line_pointer != operandP->vop_expr_end + 1) + { + as_warn ("Junk at end of expression \"%s\"", input_line_pointer); + goofed = TRUE; + } + operandP->vop_expr_end[1] = c_save; + } + } /* for(each operand) */ + input_line_pointer = save_input_line_pointer; + + if (!need_pass_2 && !goofed) + { + /* We saw no errors in any operands - try to make frag(s) */ + int is_undefined; /* True if operand expression's */ + /* segment not known yet. */ + int length_code; + + /* Emit op-code. */ + /* Remember where it is, in case we want to modify the op-code later. */ + opcode_low_byteP = frag_more (v.vit_opcode_nbytes); + bcopy (v.vit_opcode, opcode_low_byteP, v.vit_opcode_nbytes); + opcode_as_number = md_chars_to_number (opcode_as_chars = v.vit_opcode, 4); + for (operandP = v.vit_operand, + expP = exp_of_operand, + floatP = float_operand, + end_operandP = v.vit_operand + v.vit_operands; + + operandP < end_operandP; + + operandP++, + floatP++, + expP++ + ) /* for each operand */ + { + if (operandP->vop_ndx >= 0) + { + /* indexed addressing byte */ + /* Legality of indexed mode already checked: it is OK */ + FRAG_APPEND_1_CHAR (0x40 + operandP->vop_ndx); + } /* if(vop_ndx>=0) */ + + /* Here to make main operand frag(s). */ + this_add_number = expP->X_add_number; + this_add_symbol = expP->X_add_symbol; + this_subtract_symbol = expP->X_subtract_symbol; + to_seg = expP->X_seg; + is_undefined = (to_seg == SEG_UNKNOWN); + know (to_seg == SEG_UNKNOWN + ||to_seg == SEG_ABSOLUTE + ||to_seg == SEG_DATA + ||to_seg == SEG_TEXT + ||to_seg == SEG_BSS + ||to_seg == SEG_BIG + ); + at = operandP->vop_mode & 1; + length = operandP->vop_short == 'b' ? 1 : operandP->vop_short == 'w' ? 2 : operandP->vop_short == 'l' ? 4 : 0; + nbytes = operandP->vop_nbytes; + if (operandP->vop_access == 'b') + { + if (to_seg == now_seg || is_undefined) + { /* If is_undefined, then it might BECOME now_seg. */ + if (nbytes) + { + p = frag_more (nbytes); + fix_new (frag_now, p - frag_now->fr_literal, nbytes, + this_add_symbol, 0, this_add_number, 1); + } + else + { /* to_seg==now_seg || to_seg == SEG_UNKNOWN */ + /* nbytes==0 */ + length_code = is_undefined ? STATE_UNDF : STATE_BYTE; + if (opcode_as_number & VIT_OPCODE_SPECIAL) + { + if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP) + { + /* br or jsb */ + frag_var (rs_machine_dependent, 5, 1, + ENCODE_RELAX (STATE_ALWAYS_BRANCH, length_code), + this_add_symbol, this_add_number, + opcode_low_byteP); + } + else + { + if (operandP->vop_width == VAX_WIDTH_WORD_JUMP) + { + length_code = STATE_WORD; /* JF: There is no state_byte for this one! */ + frag_var (rs_machine_dependent, 10, 2, + ENCODE_RELAX (STATE_COMPLEX_BRANCH, length_code), + this_add_symbol, this_add_number, + opcode_low_byteP); + } + else + { + know (operandP->vop_width == VAX_WIDTH_BYTE_JUMP); + frag_var (rs_machine_dependent, 9, 1, + ENCODE_RELAX (STATE_COMPLEX_HOP, length_code), + this_add_symbol, this_add_number, + opcode_low_byteP); + } + } + } + else + { + know (operandP->vop_width == VAX_WIDTH_CONDITIONAL_JUMP); + frag_var (rs_machine_dependent, 7, 1, + ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, length_code), + this_add_symbol, this_add_number, + opcode_low_byteP); + } + } + } + else + { /* to_seg != now_seg && to_seg != SEG_UNKNOWN */ +/* + * --- SEG FLOAT MAY APPEAR HERE ---- + */ + if (to_seg == SEG_ABSOLUTE) + { + if (nbytes) + { + know (!(opcode_as_number & VIT_OPCODE_SYNTHETIC)); + p = frag_more (nbytes); + /* Conventional relocation. */ + fix_new (frag_now, p - frag_now->fr_literal, + nbytes, &abs_symbol, 0, this_add_number, 1); + } + else + { + know (opcode_as_number & VIT_OPCODE_SYNTHETIC); + if (opcode_as_number & VIT_OPCODE_SPECIAL) + { + if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP) + { + /* br or jsb */ + *opcode_low_byteP = opcode_as_chars[0] + VAX_WIDEN_LONG; + know (opcode_as_chars[1] == 0); + p = frag_more (5); + p[0] = VAX_ABSOLUTE_MODE; /* @#... */ + md_number_to_chars (p + 1, this_add_number, 4); + /* Now (eg) JMP @#foo or JSB @#foo. */ + } + else + { + if (operandP->vop_width == VAX_WIDTH_WORD_JUMP) + { + p = frag_more (10); + p[0] = 2; + p[1] = 0; + p[2] = VAX_BRB; + p[3] = 6; + p[4] = VAX_JMP; + p[5] = VAX_ABSOLUTE_MODE; /* @#... */ + md_number_to_chars (p + 6, this_add_number, 4); + /* + * Now (eg) ACBx 1f + * BRB 2f + * 1: JMP @#foo + * 2: + */ + } + else + { + know (operandP->vop_width == VAX_WIDTH_BYTE_JUMP); + p = frag_more (9); + p[0] = 2; + p[1] = VAX_BRB; + p[2] = 6; + p[3] = VAX_JMP; + p[4] = VAX_PC_RELATIVE_MODE + 1; /* @#... */ + md_number_to_chars (p + 5, this_add_number, 4); + /* + * Now (eg) xOBxxx 1f + * BRB 2f + * 1: JMP @#foo + * 2: + */ + } + } + } + else + { + /* b<cond> */ + *opcode_low_byteP ^= 1; /* To reverse the condition in a VAX branch, complement the lowest order bit. */ + p = frag_more (7); + p[0] = 6; + p[1] = VAX_JMP; + p[2] = VAX_ABSOLUTE_MODE; /* @#... */ + md_number_to_chars (p + 3, this_add_number, 4); + /* + * Now (eg) BLEQ 1f + * JMP @#foo + * 1: + */ + } + } + } + else + { /* to_seg != now_seg && to_seg != SEG_UNKNOWN && to_Seg != SEG_ABSOLUTE */ + if (nbytes > 0) + { + /* Pc-relative. Conventional relocation. */ + know (!(opcode_as_number & VIT_OPCODE_SYNTHETIC)); + p = frag_more (nbytes); + fix_new (frag_now, p - frag_now->fr_literal, + nbytes, &abs_symbol, 0, this_add_number, 1); + } + else + { + know (opcode_as_number & VIT_OPCODE_SYNTHETIC); + if (opcode_as_number & VIT_OPCODE_SPECIAL) + { + if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP) + { + /* br or jsb */ + know (opcode_as_chars[1] == 0); + *opcode_low_byteP = opcode_as_chars[0] + VAX_WIDEN_LONG; + p = frag_more (5); + p[0] = VAX_PC_RELATIVE_MODE; + fix_new (frag_now, + p + 1 - frag_now->fr_literal, 4, + this_add_symbol, 0, + this_add_number, 1); + /* Now eg JMP foo or JSB foo. */ + } + else + { + if (operandP->vop_width == VAX_WIDTH_WORD_JUMP) + { + p = frag_more (10); + p[0] = 0; + p[1] = 2; + p[2] = VAX_BRB; + p[3] = 6; + p[4] = VAX_JMP; + p[5] = VAX_PC_RELATIVE_MODE; + fix_new (frag_now, + p + 6 - frag_now->fr_literal, 4, + this_add_symbol, 0, + this_add_number, 1); + /* + * Now (eg) ACBx 1f + * BRB 2f + * 1: JMP foo + * 2: + */ + } + else + { + know (operandP->vop_width == VAX_WIDTH_BYTE_JUMP); + p = frag_more (10); + p[0] = 2; + p[1] = VAX_BRB; + p[2] = 6; + p[3] = VAX_JMP; + p[4] = VAX_PC_RELATIVE_MODE; + fix_new (frag_now, + p + 5 - frag_now->fr_literal, + 4, this_add_symbol, 0, + this_add_number, 1); + /* + * Now (eg) xOBxxx 1f + * BRB 2f + * 1: JMP foo + * 2: + */ + } + } + } + else + { + know (operandP->vop_width == VAX_WIDTH_CONDITIONAL_JUMP); + *opcode_low_byteP ^= 1; /* Reverse branch condition. */ + p = frag_more (7); + p[0] = 6; + p[1] = VAX_JMP; + p[2] = VAX_PC_RELATIVE_MODE; + fix_new (frag_now, p + 3 - frag_now->fr_literal, + 4, this_add_symbol, 0, + this_add_number, 1); + } + } + } + } + } + else + { + know (operandP->vop_access != 'b'); /* So it is ordinary operand. */ + know (operandP->vop_access != ' '); /* ' ' target-independent: elsewhere. */ + know (operandP->vop_access == 'a' || operandP->vop_access == 'm' || operandP->vop_access == 'r' || operandP->vop_access == 'v' || operandP->vop_access == 'w'); + if (operandP->vop_short == 's') + { + if (to_seg == SEG_ABSOLUTE) + { + if (this_add_number < 0 || this_add_number >= 64) + { + as_warn ("Short literal overflow(%d.), immediate mode assumed.", this_add_number); + operandP->vop_short = 'i'; + operandP->vop_mode = 8; + operandP->vop_reg = 0xF; + } + } + else + { + as_warn ("Forced short literal to immediate mode. now_seg=%s to_seg=%s", segment_name(now_seg), segment_name(to_seg)); + operandP->vop_short = 'i'; + operandP->vop_mode = 8; + operandP->vop_reg = 0xF; + } + } + if (operandP->vop_reg >= 0 && (operandP->vop_mode < 8 || (operandP->vop_reg != 0xF && operandP->vop_mode < 10))) + { /* One byte operand. */ + know (operandP->vop_mode > 3); + FRAG_APPEND_1_CHAR (operandP->vop_mode << 4 | operandP->vop_reg); + /* All 1-bytes except S^# happen here. */ + } + else + { /* {@}{q^}foo{(Rn)} or S^#foo */ + if (operandP->vop_reg == -1 && operandP->vop_short != 's') + { /* "{@}{q^}foo" */ + if (to_seg == now_seg) + { + if (length == 0) + { + know (operandP->vop_short == ' '); + p = frag_var (rs_machine_dependent, 10, 2, + ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE), + this_add_symbol, this_add_number, + opcode_low_byteP); + know (operandP->vop_mode == 10 + at); + *p = at << 4; + /* At is the only context we need to carry to */ + /* other side of relax() process. */ + /* Must be in the correct bit position of VAX */ + /* operand spec. byte. */ + } + else + { + know (length); + know (operandP->vop_short != ' '); + p = frag_more (length + 1); + /* JF is this array stuff really going to work? */ + p[0] = 0xF | ((at + "?\12\14?\16"[length]) << 4); + fix_new (frag_now, p + 1 - frag_now->fr_literal, + length, this_add_symbol, 0, + this_add_number, 1); + } + } + else + { /* to_seg != now_seg */ + if (this_add_symbol == NULL) + { + know (to_seg == SEG_ABSOLUTE); + /* Do @#foo: simpler relocation than foo-.(pc) anyway. */ + p = frag_more (5); + p[0] = VAX_ABSOLUTE_MODE; /* @#... */ + md_number_to_chars (p + 1, this_add_number, 4); + if (length && length != 4) + { + as_warn ("Length specification ignored. Address mode 9F used"); + } + } + else + { + /* {@}{q^}other_seg */ + know ((length == 0 && operandP->vop_short == ' ') + ||(length > 0 && operandP->vop_short != ' ')); + if (is_undefined) + { + /* + * We have a SEG_UNKNOWN symbol. It might + * turn out to be in the same segment as + * the instruction, permitting relaxation. + */ + p = frag_var (rs_machine_dependent, 5, 2, + ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF), + this_add_symbol, this_add_number, + 0); + p[0] = at << 4; + } + else + { + if (length == 0) + { + know (operandP->vop_short == ' '); + length = 4; /* Longest possible. */ + } + p = frag_more (length + 1); + p[0] = 0xF | ((at + "?\12\14?\16"[length]) << 4); + md_number_to_chars (p + 1, this_add_number, length); + fix_new (frag_now, + p + 1 - frag_now->fr_literal, + length, this_add_symbol, 0, + this_add_number, 1); + } + } + } + } + else + { /* {@}{q^}foo(Rn) or S^# or I^# or # */ + if (operandP->vop_mode < 0xA) + { /* # or S^# or I^# */ + /* know( (length == 0 && operandP->vop_short == ' ') + || (length > 0 && operandP->vop_short != ' ')); */ + if (length == 0 + && to_seg == SEG_ABSOLUTE + && operandP->vop_mode == 8 /* No '@'. */ + && this_add_number < 64 + && this_add_number >= 0) + { + operandP->vop_short = 's'; + } + if (operandP->vop_short == 's') + { + FRAG_APPEND_1_CHAR (this_add_number); + } + else + { /* I^#... */ + know (nbytes); + p = frag_more (nbytes + 1); + know (operandP->vop_reg == 0xF); + p[0] = (operandP->vop_mode << 4) | 0xF; + if (to_seg == SEG_ABSOLUTE) + { +/* + * If nbytes > 4, then we are scrod. We don't know if the + * high order bytes are to be 0xFF or 0x00. + * BSD4.2 & RMS say use 0x00. OK --- but this + * assembler needs ANOTHER rewrite to + * cope properly with this bug. + */ + md_number_to_chars (p + 1, this_add_number, min (4, nbytes)); + if (nbytes > 4) + { + bzero (p + 5, nbytes - 4); + } + } + else + { + if (to_seg == SEG_BIG) + { +/* + * Problem here is to get the bytes in the right order. + * We stored our constant as LITTLENUMs, not bytes. + */ + LITTLENUM_TYPE *lP; + + lP = floatP->low; + if (nbytes & 1) + { + know (nbytes == 1); + p[1] = *lP; + } + else + { + for (p++; nbytes; nbytes -= 2, p += 2, lP++) + { + md_number_to_chars (p, *lP, 2); + } + } + } + else + { + fix_new (frag_now, p + 1 - frag_now->fr_literal, + nbytes, this_add_symbol, 0, + this_add_number, 0); + } + } + } + } + else + { /* {@}{q^}foo(Rn) */ + know ((length == 0 && operandP->vop_short == ' ') + ||(length > 0 && operandP->vop_short != ' ')); + if (length == 0) + { + if (to_seg == SEG_ABSOLUTE) + { + register long test; + + test = this_add_number; + + if (test < 0) + test = ~test; + + length = test & 0xffff8000 ? 4 + : test & 0xffffff80 ? 2 + : 1; + } + else + { + length = 4; + } + } + p = frag_more (1 + length); + know (operandP->vop_reg >= 0); + p[0] = operandP->vop_reg + | ((at | "?\12\14?\16"[length]) << 4); + if (to_seg == SEG_ABSOLUTE) + { + md_number_to_chars (p + 1, this_add_number, length); + } + else + { + fix_new (frag_now, p + 1 - frag_now->fr_literal, + length, this_add_symbol, 0, + this_add_number, 0); + } + } + } + } /* if(single-byte-operand) */ + } + } /* for(operandP) */ + } /* if(!need_pass_2&&!goofed) */ +} /* vax_assemble() */ + +/* + * md_estimate_size_before_relax() + * + * Called just before relax(). + * Any symbol that is now undefined will not become defined. + * Return the correct fr_subtype in the frag. + * Return the initial "guess for fr_var" to caller. + * The guess for fr_var is ACTUALLY the growth beyond fr_fix. + * Whatever we do to grow fr_fix or fr_var contributes to our returned value. + * Although it may not be explicit in the frag, pretend fr_var starts with a + * 0 value. + */ +int +md_estimate_size_before_relax (fragP, segment) + register fragS *fragP; + register segT segment; +{ + register char *p; + register int old_fr_fix; + + old_fr_fix = fragP->fr_fix; + switch (fragP->fr_subtype) + { + case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF): + if (S_GET_SEGMENT(fragP->fr_symbol) == segment) + { /* A relaxable case. */ + fragP->fr_subtype = ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE); + } + else + { + p = fragP->fr_literal + old_fr_fix; + p[0] |= VAX_PC_RELATIVE_MODE; /* Preserve @ bit. */ + fragP->fr_fix += 1 + 4; + fix_new (fragP, old_fr_fix + 1, 4, fragP->fr_symbol, 0, + fragP->fr_offset, 1); + frag_wane (fragP); + } + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF): + if (S_GET_SEGMENT(fragP->fr_symbol) == segment) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE); + } + else + { + p = fragP->fr_literal + old_fr_fix; + *fragP->fr_opcode ^= 1; /* Reverse sense of branch. */ + p[0] = 6; + p[1] = VAX_JMP; + p[2] = VAX_PC_RELATIVE_MODE; /* ...(PC) */ + fragP->fr_fix += 1 + 1 + 1 + 4; + fix_new (fragP, old_fr_fix + 3, 4, fragP->fr_symbol, 0, + fragP->fr_offset, 1); + frag_wane (fragP); + } + break; + + case ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_UNDF): + if (S_GET_SEGMENT(fragP->fr_symbol) == segment) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_WORD); + } + else + { + p = fragP->fr_literal + old_fr_fix; + p[0] = 2; + p[1] = 0; + p[2] = VAX_BRB; + p[3] = 6; + p[4] = VAX_JMP; + p[5] = VAX_PC_RELATIVE_MODE; /* ...(pc) */ + fragP->fr_fix += 2 + 2 + 1 + 1 + 4; + fix_new (fragP, old_fr_fix + 6, 4, fragP->fr_symbol, 0, + fragP->fr_offset, 1); + frag_wane (fragP); + } + break; + + case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_UNDF): + if (S_GET_SEGMENT(fragP->fr_symbol) == segment) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_BYTE); + } + else + { + p = fragP->fr_literal + old_fr_fix; + p[0] = 2; + p[1] = VAX_BRB; + p[2] = 6; + p[3] = VAX_JMP; + p[4] = VAX_PC_RELATIVE_MODE; /* ...(pc) */ + fragP->fr_fix += 1 + 2 + 1 + 1 + 4; + fix_new (fragP, old_fr_fix + 5, 4, fragP->fr_symbol, 0, + fragP->fr_offset, 1); + frag_wane (fragP); + } + break; + + case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_UNDF): + if (S_GET_SEGMENT(fragP->fr_symbol) == segment) + { + fragP->fr_subtype = ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE); + } + else + { + p = fragP->fr_literal + old_fr_fix; + *fragP->fr_opcode += VAX_WIDEN_LONG; + p[0] = VAX_PC_RELATIVE_MODE; /* ...(PC) */ + fragP->fr_fix += 1 + 4; + fix_new (fragP, old_fr_fix + 1, 4, fragP->fr_symbol, 0, + fragP->fr_offset, 1); + frag_wane (fragP); + } + break; + + default: + break; + } + return (fragP->fr_var + fragP->fr_fix - old_fr_fix); +} /* md_estimate_size_before_relax() */ + +/* + * md_convert_frag(); + * + * Called after relax() is finished. + * In: Address of frag. + * fr_type == rs_machine_dependent. + * fr_subtype is what the address relaxed to. + * + * Out: Any fixSs and constants are set up. + * Caller will turn frag into a ".space 0". + */ +void +md_convert_frag (fragP) + register fragS *fragP; +{ + register char *addressP; /* -> _var to change. */ + register char *opcodeP; /* -> opcode char(s) to change. */ + register short int length_code; /* 2=long 1=word 0=byte */ + register short int extension; /* Size of relaxed address. */ + /* Added to fr_fix: incl. ALL var chars. */ + register symbolS *symbolP; + register long where; + register long address_of_var; + /* Where, in file space, is _var of *fragP? */ + register long target_address; + /* Where, in file space, does addr point? */ + + know (fragP->fr_type == rs_machine_dependent); + length_code = fragP->fr_subtype & 3; /* depends on ENCODE_RELAX() */ + know (length_code >= 0 && length_code < 3); + where = fragP->fr_fix; + addressP = fragP->fr_literal + where; + opcodeP = fragP->fr_opcode; + symbolP = fragP->fr_symbol; + know (symbolP); + target_address = symbolP->sy_value + fragP->fr_offset; + address_of_var = fragP->fr_address + where; + switch (fragP->fr_subtype) + { + case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE): + know (*addressP == 0 || *addressP == 0x10); /* '@' bit. */ + addressP[0] |= 0xAF; /* Byte displacement. */ + addressP[1] = target_address - (address_of_var + 2); + extension = 2; + break; + + case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD): + know (*addressP == 0 || *addressP == 0x10); /* '@' bit. */ + addressP[0] |= 0xCF; /* Word displacement. */ + md_number_to_chars (addressP + 1, target_address - (address_of_var + 3), 2); + extension = 3; + break; + + case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_LONG): + know (*addressP == 0 || *addressP == 0x10); /* '@' bit. */ + addressP[0] |= 0xEF; /* Long word displacement. */ + md_number_to_chars (addressP + 1, target_address - (address_of_var + 5), 4); + extension = 5; + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE): + addressP[0] = target_address - (address_of_var + 1); + extension = 1; + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD): + opcodeP[0] ^= 1; /* Reverse sense of test. */ + addressP[0] = 3; + addressP[1] = VAX_BRB + VAX_WIDEN_WORD; + md_number_to_chars (addressP + 2, target_address - (address_of_var + 4), 2); + extension = 4; + break; + + case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_LONG): + opcodeP[0] ^= 1; /* Reverse sense of test. */ + addressP[0] = 6; + addressP[1] = VAX_JMP; + addressP[2] = VAX_PC_RELATIVE_MODE; + md_number_to_chars (addressP + 3, target_address, 4); + extension = 7; + break; + + case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE): + addressP[0] = target_address - (address_of_var + 1); + extension = 1; + break; + + case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_WORD): + opcodeP[0] += VAX_WIDEN_WORD; /* brb -> brw, bsbb -> bsbw */ + md_number_to_chars (addressP, target_address - (address_of_var + 2), 2); + extension = 2; + break; + + case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_LONG): + opcodeP[0] += VAX_WIDEN_LONG; /* brb -> jmp, bsbb -> jsb */ + addressP[0] = VAX_PC_RELATIVE_MODE; + md_number_to_chars (addressP + 1, target_address - (address_of_var + 5), 4); + extension = 5; + break; + + case ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_WORD): + md_number_to_chars (addressP, target_address - (address_of_var + 2), 2); + extension = 2; + break; + + case ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_LONG): + addressP[0] = 2; + addressP[1] = 0; + addressP[2] = VAX_BRB; + addressP[3] = 6; + addressP[4] = VAX_JMP; + addressP[5] = VAX_PC_RELATIVE_MODE; + md_number_to_chars (addressP + 6, target_address, 4); + extension = 10; + break; + + case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_BYTE): + addressP[0] = target_address - (address_of_var + 1); + extension = 1; + break; + + case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_WORD): + addressP[0] = 2; + addressP[1] = VAX_BRB; + addressP[2] = 3; + addressP[3] = VAX_BRW; + md_number_to_chars (addressP + 4, target_address - (address_of_var + 6), 2); + extension = 6; + break; + + case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_LONG): + addressP[0] = 2; + addressP[1] = VAX_BRB; + addressP[2] = 6; + addressP[3] = VAX_JMP; + addressP[4] = VAX_PC_RELATIVE_MODE; + md_number_to_chars (addressP + 5, target_address, 4); + extension = 9; + break; + + default: + BAD_CASE (fragP->fr_subtype); + break; + } + fragP->fr_fix += extension; +} + +/* Translate internal format of relocation info into target format. + + On vax: first 4 bytes are normal unsigned long, next three bytes + are symbolnum, least sig. byte first. Last byte is broken up with + the upper nibble as nuthin, bit 3 as extern, bits 2 & 1 as length, and + bit 0 as pcrel. */ +void +md_ri_to_chars (the_bytes, ri) + char *the_bytes; + struct reloc_info_generic ri; +{ + /* this is easy */ + md_number_to_chars (the_bytes, ri.r_address, sizeof (ri.r_address)); + /* now the fun stuff */ + the_bytes[6] = (ri.r_symbolnum >> 16) & 0x0ff; + the_bytes[5] = (ri.r_symbolnum >> 8) & 0x0ff; + the_bytes[4] = ri.r_symbolnum & 0x0ff; + the_bytes[7] = (((ri.r_extern << 3) & 0x08) | ((ri.r_length << 1) & 0x06) | + ((ri.r_pcrel << 0) & 0x01)) & 0x0F; +} + +/* + * BUGS, GRIPES, APOLOGIA, etc. + * + * The opcode table 'votstrs' needs to be sorted on opcode frequency. + * That is, AFTER we hash it with hash_...(), we want most-used opcodes + * to come out of the hash table faster. + * + * I am sorry to inflict + * yet another VAX assembler on the world, but RMS says we must + * do everything from scratch, to prevent pin-heads restricting + * this software. + */ + +/* + * This is a vaguely modular set of routines in C to parse VAX + * assembly code using DEC mnemonics. It is NOT un*x specific. + * + * The idea here is that the assembler has taken care of all: + * labels + * macros + * listing + * pseudo-ops + * line continuation + * comments + * condensing any whitespace down to exactly one space + * and all we have to do is parse 1 line into a vax instruction + * partially formed. We will accept a line, and deliver: + * an error message (hopefully empty) + * a skeleton VAX instruction (tree structure) + * textual pointers to all the operand expressions + * a warning message that notes a silly operand (hopefully empty) + */ + +/* + * E D I T H I S T O R Y + * + * 17may86 Dean Elsner. Bug if line ends immediately after opcode. + * 30apr86 Dean Elsner. New vip_op() uses arg block so change call. + * 6jan86 Dean Elsner. Crock vip_begin() to call vip_op_defaults(). + * 2jan86 Dean Elsner. Invent synthetic opcodes. + * Widen vax_opcodeT to 32 bits. Use a bit for VIT_OPCODE_SYNTHETIC, + * which means this is not a real opcode, it is like a macro; it will + * be relax()ed into 1 or more instructions. + * Use another bit for VIT_OPCODE_SPECIAL if the op-code is not optimised + * like a regular branch instruction. Option added to vip_begin(): + * exclude synthetic opcodes. Invent synthetic_votstrs[]. + * 31dec85 Dean Elsner. Invent vit_opcode_nbytes. + * Also make vit_opcode into a char[]. We now have n-byte vax opcodes, + * so caller's don't have to know the difference between a 1-byte & a + * 2-byte op-code. Still need vax_opcodeT concept, so we know how + * big an object must be to hold an op.code. + * 30dec85 Dean Elsner. Widen typedef vax_opcodeT in "vax-inst.h" + * because vax opcodes may be 16 bits. Our crufty C compiler was + * happily initialising 8-bit vot_codes with 16-bit numbers! + * (Wouldn't the 'phone company like to compress data so easily!) + * 29dec85 Dean Elsner. New static table vax_operand_width_size[]. + * Invented so we know hw many bytes a "I^#42" needs in its immediate + * operand. Revised struct vop in "vax-inst.h": explicitly include + * byte length of each operand, and it's letter-code datum type. + * 17nov85 Dean Elsner. Name Change. + * Due to ar(1) truncating names, we learned the hard way that + * "vax-inst-parse.c" -> "vax-inst-parse." dropping the "o" off + * the archived object name. SO... we shortened the name of this + * source file, and changed the makefile. + */ + +static char *op_hash = NULL; /* handle of the OPCODE hash table */ + /* NULL means any use before vip_begin() */ + /* will crash */ + +/* + * In: 1 character, from "bdfghloqpw" being the data-type of an operand + * of a vax instruction. + * + * Out: the length of an operand of that type, in bytes. + * Special branch operands types "-?!" have length 0. + */ + +static const short int vax_operand_width_size[256] = +{ + +#define _ 0 + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, 1, _, 8, _, 4, 8, 16, _, _, _, 4, _, _, 16, /* ..b.d.fgh...l..o */ + _, 8, _, _, _, _, _, 2, _, _, _, _, _, _, _, _, /* .q.....w........ */ + _, _, 1, _, 8, _, 4, 8, 16, _, _, _, 4, _, _, 16, /* ..b.d.fgh...l..o */ + _, 8, _, _, _, _, _, 2, _, _, _, _, _, _, _, _, /* .q.....w........ */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _}; +#undef _ + +/* + * This perversion encodes all the vax opcodes as a bunch of strings. + * RMS says we should build our hash-table at run-time. Hmm. + * Please would someone arrange these in decreasing frequency of opcode? + * Because of the way hash_...() works, the most frequently used opcode + * should be textually first and so on. + * + * Input for this table was 'vax.opcodes', awk(1)ed by 'vax.opcodes.c.awk' . + * So change 'vax.opcodes', then re-generate this table. + */ + +#include "vax-opcode.h" + +/* + * This is a table of optional op-codes. All of them represent + * 'synthetic' instructions that seem popular. + * + * Here we make some pseudo op-codes. Every code has a bit set to say + * it is synthetic. This lets you catch them if you want to + * ban these opcodes. They are mnemonics for "elastic" instructions + * that are supposed to assemble into the fewest bytes needed to do a + * branch, or to do a conditional branch, or whatever. + * + * The opcode is in the usual place [low-order n*8 bits]. This means + * that if you mask off the bucky bits, the usual rules apply about + * how long the opcode is. + * + * All VAX branch displacements come at the end of the instruction. + * For simple branches (1-byte opcode + 1-byte displacement) the last + * operand is coded 'b?' where the "data type" '?' is a clue that we + * may reverse the sense of the branch (complement lowest order bit) + * and branch around a jump. This is by far the most common case. + * That is why the VIT_OPCODE_SYNTHETIC bit is set: it says this is + * a 0-byte op-code followed by 2 or more bytes of operand address. + * + * If the op-code has VIT_OPCODE_SPECIAL set, then we have a more unusual + * case. + * + * For JBSB & JBR the treatment is the similar, except (1) we have a 'bw' + * option before (2) we can directly JSB/JMP because there is no condition. + * These operands have 'b-' as their access/data type. + * + * That leaves a bunch of random opcodes: JACBx, JxOBxxx. In these + * cases, we do the same idea. JACBxxx are all marked with a 'b!' + * JAOBxxx & JSOBxxx are marked with a 'b:'. + * + */ +#if (VIT_OPCODE_SYNTHETIC != 0x80000000) +You have just broken the encoding below, which assumes the sign bit + means 'I am an imaginary instruction'. +#endif + +#if (VIT_OPCODE_SPECIAL != 0x40000000) + You have just broken the encoding below, which assumes the 0x40 M bit means + 'I am not to be "optimised" the way normal branches are'. +#endif + +static const struct vot + synthetic_votstrs[] = +{ + {"jbsb", + {"b-", 0xC0000010}}, /* BSD 4.2 */ + /* jsb used already */ + {"jbr", + {"b-", 0xC0000011}}, /* BSD 4.2 */ + {"jr", + {"b-", 0xC0000011}}, /* consistent */ + {"jneq", + {"b?", 0x80000012}}, + {"jnequ", + {"b?", 0x80000012}}, + {"jeql", + {"b?", 0x80000013}}, + {"jeqlu", + {"b?", 0x80000013}}, + {"jgtr", + {"b?", 0x80000014}}, + {"jleq", + {"b?", 0x80000015}}, + /* un-used opcodes here */ + {"jgeq", + {"b?", 0x80000018}}, + {"jlss", + {"b?", 0x80000019}}, + {"jgtru", + {"b?", 0x8000001a}}, + {"jlequ", + {"b?", 0x8000001b}}, + {"jvc", + {"b?", 0x8000001c}}, + {"jvs", + {"b?", 0x8000001d}}, + {"jgequ", + {"b?", 0x8000001e}}, + {"jcc", + {"b?", 0x8000001e}}, + {"jlssu", + {"b?", 0x8000001f}}, + {"jcs", + {"b?", 0x8000001f}}, + + {"jacbw", + {"rwrwmwb!", 0xC000003d}}, + {"jacbf", + {"rfrfmfb!", 0xC000004f}}, + {"jacbd", + {"rdrdmdb!", 0xC000006f}}, + {"jacbb", + {"rbrbmbb!", 0xC000009d}}, + {"jacbl", + {"rlrlmlb!", 0xC00000f1}}, + {"jacbg", + {"rgrgmgb!", 0xC0004ffd}}, + {"jacbh", + {"rhrhmhb!", 0xC0006ffd}}, + + {"jbs", + {"rlvbb?", 0x800000e0}}, + {"jbc", + {"rlvbb?", 0x800000e1}}, + {"jbss", + {"rlvbb?", 0x800000e2}}, + {"jbcs", + {"rlvbb?", 0x800000e3}}, + {"jbsc", + {"rlvbb?", 0x800000e4}}, + {"jbcc", + {"rlvbb?", 0x800000e5}}, + {"jbssi", + {"rlvbb?", 0x800000e6}}, + {"jbcci", + {"rlvbb?", 0x800000e7}}, + {"jlbs", + {"rlb?", 0x800000e8}}, /* JF changed from rlvbb? */ + {"jlbc", + {"rlb?", 0x800000e9}}, /* JF changed from rlvbb? */ + + {"jaoblss", + {"rlmlb:", 0xC00000f2}}, + {"jaobleq", + {"rlmlb:", 0xC00000f3}}, + {"jsobgeq", + {"mlb:", 0xC00000f4}}, /* JF was rlmlb: */ + {"jsobgtr", + {"mlb:", 0xC00000f5}}, /* JF was rlmlb: */ + +/* CASEx has no branch addresses in our conception of it. */ +/* You should use ".word ..." statements after the "case ...". */ + + {"", ""} /* empty is end sentinel */ + +}; /* synthetic_votstrs */ + +/* + * v i p _ b e g i n ( ) + * + * Call me once before you decode any lines. + * I decode votstrs into a hash table at op_hash (which I create). + * I return an error text: hopefully "". + * If you want, I will include the 'synthetic' jXXX instructions in the + * instruction table. + * You must nominate metacharacters for eg DEC's "#", "@", "^". + */ + +char * +vip_begin (synthetic_too, immediate, indirect, displen) + int synthetic_too; /* TRUE means include jXXX op-codes. */ + char *immediate, *indirect, *displen; +{ + register const struct vot *vP; /* scan votstrs */ + register char *retval; /* error text */ + + char *hash_insert (); /* */ + char *hash_new (); /* lies */ + + if ((op_hash = hash_new ())) + { + retval = ""; /* OK so far */ + for (vP = votstrs; *vP->vot_name && !*retval; vP++) + { + retval = hash_insert (op_hash, vP->vot_name, &vP->vot_detail); + } + if (synthetic_too) + { + for (vP = synthetic_votstrs; *vP->vot_name && !*retval; vP++) + { + retval = hash_insert (op_hash, vP->vot_name, &vP->vot_detail); + } + } + } + else + { + retval = "virtual memory exceeded"; + } +#ifndef CONST_TABLE + vip_op_defaults (immediate, indirect, displen); +#endif + + return (retval); +} + + +/* + * v i p _ e n d ( ) + * + * Call me once after you have decoded all lines. + * I do any cleaning-up needed. + * + * We don't have to do any cleanup ourselves: all of our operand + * symbol table is static, and free()ing it is naughty. + */ +vip_end () +{ +} + +/* + * v i p ( ) + * + * This converts a string into a vax instruction. + * The string must be a bare single instruction in dec-vax (with BSD4 frobs) + * format. + * It provides some error messages: at most one fatal error message (which + * stops the scan) and at most one warning message for each operand. + * The vax instruction is returned in exploded form, since we have no + * knowledge of how you parse (or evaluate) your expressions. + * We do however strip off and decode addressing modes and operation + * mnemonic. + * + * The exploded instruction is returned to a struct vit of your choice. + * #include "vax-inst.h" to know what a struct vit is. + * + * This function's value is a string. If it is not "" then an internal + * logic error was found: read this code to assign meaning to the string. + * No argument string should generate such an error string: + * it means a bug in our code, not in the user's text. + * + * You MUST have called vip_begin() once and vip_end() never before using + * this function. + */ + +char * /* "" or bug string */ +vip (vitP, instring) + struct vit *vitP; /* We build an exploded instruction here. */ + char *instring; /* Text of a vax instruction: we modify. */ +{ + register struct vot_wot *vwP; /* How to bit-encode this opcode. */ + register char *p; /* 1/skip whitespace.2/scan vot_how */ + register char *q; /* */ + register char *bug; /* "" or program logic error */ + register unsigned char count; /* counts number of operands seen */ + register struct vop *operandp;/* scan operands in struct vit */ + register char *alloperr; /* error over all operands */ + register char c; /* Remember char, (we clobber it */ + /* with '\0' temporarily). */ + register vax_opcodeT oc; /* Op-code of this instruction. */ + + struct vot_wot *hash_find (); + char *vip_op (); + + bug = ""; + if (*instring == ' ') + ++instring; /* Skip leading whitespace. */ + for (p = instring; *p && *p != ' '; p++) + ; /* MUST end in end-of-string or exactly 1 space. */ + /* Scanned up to end of operation-code. */ + /* Operation-code is ended with whitespace. */ + if (p - instring == 0) + { + vitP->vit_error = "No operator"; + count = 0; + bzero (vitP->vit_opcode, sizeof (vitP->vit_opcode)); + } + else + { + c = *p; + *p = '\0'; + /* + * Here with instring pointing to what better be an op-name, and p + * pointing to character just past that. + * We trust instring points to an op-name, with no whitespace. + */ + vwP = hash_find (op_hash, instring); + *p = c; /* Restore char after op-code. */ + if (vwP == 0) + { + vitP->vit_error = "Unknown operator"; + count = 0; + bzero (vitP->vit_opcode, sizeof (vitP->vit_opcode)); + } + else + { + /* + * We found a match! So lets pick up as many operands as the + * instruction wants, and even gripe if there are too many. + * We expect comma to seperate each operand. + * We let instring track the text, while p tracks a part of the + * struct vot. + */ + /* + * The lines below know about 2-byte opcodes starting FD,FE or FF. + * They also understand synthetic opcodes. Note: + * we return 32 bits of opcode, including bucky bits, BUT + * an opcode length is either 8 or 16 bits for vit_opcode_nbytes. + */ + oc = vwP->vot_code; /* The op-code. */ + vitP->vit_opcode_nbytes = (oc & 0xFF) >= 0xFD ? 2 : 1; + md_number_to_chars (vitP->vit_opcode, oc, 4); + count = 0; /* no operands seen yet */ + instring = p; /* point just past operation code */ + alloperr = ""; + for (p = vwP->vot_how, operandp = vitP->vit_operand; + !*alloperr && !*bug && *p; + operandp++, p += 2 + ) + { + /* + * Here to parse one operand. Leave instring pointing just + * past any one ',' that marks the end of this operand. + */ + if (!p[1]) + bug = "p"; /* ODD(!!) number of bytes in vot_how?? */ + else if (*instring) + { + for (q = instring; (c = *q) && c != ','; q++) + ; + /* + * Q points to ',' or '\0' that ends argument. C is that + * character. + */ + *q = 0; + operandp->vop_width = p[1]; + operandp->vop_nbytes = vax_operand_width_size[p[1]]; + operandp->vop_access = p[0]; + bug = vip_op (instring, operandp); + *q = c; /* Restore input text. */ + if (*(operandp->vop_error)) + alloperr = "Bad operand"; + instring = q + (c ? 1 : 0); /* next operand (if any) */ + count++; /* won another argument, may have an operr */ + } + else + alloperr = "Not enough operands"; + } + if (!*alloperr) + { + if (*instring == ' ') + instring++; /* Skip whitespace. */ + if (*instring) + alloperr = "Too many operands"; + } + vitP->vit_error = alloperr; + } + } + vitP->vit_operands = count; + return (bug); +} + +#ifdef test + +/* + * Test program for above. + */ + +struct vit myvit; /* build an exploded vax instruction here */ +char answer[100]; /* human types a line of vax assembler here */ +char *mybug; /* "" or an internal logic diagnostic */ +int mycount; /* number of operands */ +struct vop *myvop; /* scan operands from myvit */ +int mysynth; /* TRUE means want synthetic opcodes. */ +char my_immediate[200]; +char my_indirect[200]; +char my_displen[200]; + +char *vip (); + +main () +{ + char *p; + char *vip_begin (); + + printf ("0 means no synthetic instructions. "); + printf ("Value for vip_begin? "); + gets (answer); + sscanf (answer, "%d", &mysynth); + printf ("Synthetic opcodes %s be included.\n", mysynth ? "will" : "will not"); + printf ("enter immediate symbols eg enter # "); + gets (my_immediate); + printf ("enter indirect symbols eg enter @ "); + gets (my_indirect); + printf ("enter displen symbols eg enter ^ "); + gets (my_displen); + if (*(p = vip_begin (mysynth, my_immediate, my_indirect, my_displen))) + { + error ("vip_begin=%s", p); + } + printf ("An empty input line will quit you from the vax instruction parser\n"); + for (;;) + { + printf ("vax instruction: "); + fflush (stdout); + gets (answer); + if (!*answer) + { + break; /* out of for each input text loop */ + } + mybug = vip (&myvit, answer); + if (*mybug) + { + printf ("BUG:\"%s\"\n", mybug); + } + if (*myvit.vit_error) + { + printf ("ERR:\"%s\"\n", myvit.vit_error); + } + printf ("opcode="); + for (mycount = myvit.vit_opcode_nbytes, p = myvit.vit_opcode; + mycount; + mycount--, p++ + ) + { + printf ("%02x ", *p & 0xFF); + } + printf (" operand count=%d.\n", mycount = myvit.vit_operands); + for (myvop = myvit.vit_operand; mycount; mycount--, myvop++) + { + printf ("mode=%xx reg=%xx ndx=%xx len='%c'=%c%c%d. expr=\"", + myvop->vop_mode, myvop->vop_reg, myvop->vop_ndx, + myvop->vop_short, myvop->vop_access, myvop->vop_width, + myvop->vop_nbytes); + for (p = myvop->vop_expr_begin; p <= myvop->vop_expr_end; p++) + { + putchar (*p); + } + printf ("\"\n"); + if (*myvop->vop_error) + { + printf (" err:\"%s\"\n", myvop->vop_error); + } + if (*myvop->vop_warn) + { + printf (" wrn:\"%s\"\n", myvop->vop_warn); + } + } + } + vip_end (); + exit (); +} + +#endif /* #ifdef test */ + +/* end of vax_ins_parse.c */ + + /* JF this used to be a separate file also */ +/* vax_reg_parse.c - convert a VAX register name to a number */ + +/* Copyright (C) 1987 Free Software Foundation, Inc. A part of GNU. */ + +/* + * v a x _ r e g _ p a r s e ( ) + * + * Take 3 char.s, the last of which may be `\0` (non-existent) + * and return the VAX register number that they represent. + * + * Return -1 if they don't form a register name. Good names return + * a number from 0:15 inclusive. + * + * Case is not important in a name. + * + * Register names understood are: + * + * R0 + * R1 + * R2 + * R3 + * R4 + * R5 + * R6 + * R7 + * R8 + * R9 + * R10 + * R11 + * R12 AP + * R13 FP + * R14 SP + * R15 PC + * + */ + +#include <ctype.h> +#define AP (12) +#define FP (13) +#define SP (14) +#define PC (15) + +int /* return -1 or 0:15 */ +vax_reg_parse (c1, c2, c3) /* 3 chars of register name */ + char c1, c2, c3; /* c3 == 0 if 2-character reg name */ +{ + register int retval; /* return -1:15 */ + + retval = -1; + + if (isupper (c1)) + c1 = tolower (c1); + if (isupper (c2)) + c2 = tolower (c2); + if (isdigit (c2) && c1 == 'r') + { + retval = c2 - '0'; + if (isdigit (c3)) + { + retval = retval * 10 + c3 - '0'; + retval = (retval > 15) ? -1 : retval; + /* clamp the register value to 1 hex digit */ + } + else if (c3) + retval = -1; /* c3 must be '\0' or a digit */ + } + else if (c3) /* There are no three letter regs */ + retval = -1; + else if (c2 == 'p') + { + switch (c1) + { + case 's': + retval = SP; + break; + case 'f': + retval = FP; + break; + case 'a': + retval = AP; + break; + default: + retval = -1; + } + } + else if (c1 == 'p' && c2 == 'c') + retval = PC; + else + retval = -1; + return (retval); +} + +/* + * v i p _ o p ( ) + * + * Parse a vax operand in DEC assembler notation. + * For speed, expect a string of whitespace to be reduced to a single ' '. + * This is the case for GNU AS, and is easy for other DEC-compatible + * assemblers. + * + * Knowledge about DEC VAX assembler operand notation lives here. + * This doesn't even know what a register name is, except it believes + * all register names are 2 or 3 characters, and lets vax_reg_parse() say + * what number each name represents. + * It does, however, know that PC, SP etc are special registers so it can + * detect addressing modes that are silly for those registers. + * + * Where possible, it delivers 1 fatal or 1 warning message if the operand + * is suspect. Exactly what we test for is still evolving. + */ + +/* + * B u g s + * + * Arg block. + * + * There were a number of 'mismatched argument type' bugs to vip_op. + * The most general solution is to typedef each (of many) arguments. + * We used instead a typedef'd argument block. This is less modular + * than using seperate return pointers for each result, but runs faster + * on most engines, and seems to keep programmers happy. It will have + * to be done properly if we ever want to use vip_op as a general-purpose + * module (it was designed to be). + * + * G^ + * + * Doesn't support DEC "G^" format operands. These always take 5 bytes + * to express, and code as modes 8F or 9F. Reason: "G^" deprives you of + * optimising to (say) a "B^" if you are lucky in the way you link. + * When someone builds a linker smart enough to convert "G^" to "B^", "W^" + * whenever possible, then we should implement it. + * If there is some other use for "G^", feel free to code it in! + * + * + * speed + * + * If I nested if()s more, I could avoid testing (*err) which would save + * time, space and page faults. I didn't nest all those if()s for clarity + * and because I think the mode testing can be re-arranged 1st to test the + * commoner constructs 1st. Does anybody have statistics on this? + * + * + * + * error messages + * + * In future, we should be able to 'compose' error messages in a scratch area + * and give the user MUCH more informative error messages. Although this takes + * a little more code at run-time, it will make this module much more self- + * documenting. As an example of what sucks now: most error messages have + * hardwired into them the DEC VAX metacharacters "#^@" which are nothing like + * the Un*x characters "$`*", that most users will expect from this AS. + */ + +/* + * The input is a string, ending with '\0'. + * + * We also require a 'hint' of what kind of operand is expected: so + * we can remind caller not to write into literals for instance. + * + * The output is a skeletal instruction. + * + * The algorithm has two parts. + * 1. extract the syntactic features (parse off all the @^#-()+[] mode crud); + * 2. express the @^#-()+[] as some parameters suited to further analysis. + * + * 2nd step is where we detect the googles of possible invalid combinations + * a human (or compiler) might write. Note that if we do a half-way + * decent assembler, we don't know how long to make (eg) displacement + * fields when we first meet them (because they may not have defined values). + * So we must wait until we know how many bits are needed for each address, + * then we can know both length and opcodes of instructions. + * For reason(s) above, we will pass to our caller a 'broken' instruction + * of these major components, from which our caller can generate instructions: + * - displacement length I^ S^ L^ B^ W^ unspecified + * - mode (many) + * - register R0-R15 or absent + * - index register R0-R15 or absent + * - expression text what we don't parse + * - error text(s) why we couldn't understand the operand + */ + +/* + * To decode output of this, test errtxt. If errtxt[0] == '\0', then + * we had no errors that prevented parsing. Also, if we ever report + * an internal bug, errtxt[0] is set non-zero. So one test tells you + * if the other outputs are to be taken seriously. + */ + + + /* vax registers we need to know */ +/* JF #define SP (14) +/* JF for one big happy file #define PC (15) */ + + /* useful ideas */ +/* #define TRUE (1) */ +/* #define FALSE (0) */ + +/* + * Because this module is useful for both VMS and UN*X style assemblers + * and because of the variety of UN*X assemblers we must recognise + * the different conventions for assembler operand notation. For example + * VMS says "#42" for immediate mode, while most UN*X say "$42". + * We permit arbitrary sets of (single) characters to represent the + * 3 concepts that DEC writes '#', '@', '^'. + */ + + /* character tests */ +#define VIP_IMMEDIATE 01 /* Character is like DEC # */ +#define VIP_INDIRECT 02 /* Char is like DEC @ */ +#define VIP_DISPLEN 04 /* Char is like DEC ^ */ + +#define IMMEDIATEP(c) (vip_metacharacters [(c)&0xff]&VIP_IMMEDIATE) +#define INDIRECTP(c) (vip_metacharacters [(c)&0xff]&VIP_INDIRECT) +#define DISPLENP(c) (vip_metacharacters [(c)&0xff]&VIP_DISPLEN) + +/* We assume 8 bits per byte. Use vip_op_defaults() to set these up BEFORE we + * are ever called. + */ + +#if defined(CONST_TABLE) +#define _ 0, +#define I VIP_IMMEDIATE, +#define S VIP_INDIRECT, +#define D VIP_DISPLEN, +static const char +vip_metacharacters[256] = { +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*^@ ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K ^L ^M ^N ^O*/ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*^P ^Q ^R ^S ^T ^U ^V ^W ^X ^Y ^Z ^[ ^\ ^] ^^ ^_*/ +_ _ _ _ I _ _ _ _ _ S _ _ _ _ _/*sp ! " # $ % & ' ( ) * + , - . /*/ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*0 1 2 3 4 5 6 7 8 9 : ; < = > ?*/ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*@ A B C D E F G H I J K L M N O*/ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*P Q R S T U V W X Y Z [ \ ] ^ _*/ +D _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*` a b c d e f g h i j k l m n o*/ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _/*p q r s t u v w x y z { | } ~ ^?*/ + +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +}; +#undef _ +#undef I +#undef S +#undef D +#else +static char vip_metacharacters[256]; + +/* Macro is faster under GCC; The constant table is faster yet, but only works with ASCII */ +#if 0 +static +#ifdef __GNUC__ +inline +#endif +static void +vip_op_1(bit,syms) +int bit; +char *syms; +{ + unsigned char t; + + while(t= *syms++) + vip_metacharacters[t]|=bit; +} +#else +#define vip_op_1(bit,syms) { \ + unsigned char t; \ + char *table=vip_metacharacters; \ + while(t= *syms++) \ + table[t]|=bit; \ + } +#endif + +vip_op_defaults (immediate, indirect, displen) /* can be called any time */ + char *immediate, /* Strings of characters for each job. */ + *indirect, *displen; /* more arguments may appear in future! */ +{ + vip_op_1 (VIP_IMMEDIATE, immediate); + vip_op_1 (VIP_INDIRECT, indirect); + vip_op_1 (VIP_DISPLEN, displen); +} +#endif + + +/* + * Dec defines the semantics of address modes (and values) + * by a two-letter code, explained here. + * + * letter 1: access type + * + * a address calculation - no data access, registers forbidden + * b branch displacement + * m read - let go of bus - write back "modify" + * r read + * v bit field address: like 'a' but registers are OK + * w write + * space no operator (eg ".long foo") [our convention] + * + * letter 2: data type (i.e. width, alignment) + * + * b byte + * d double precision floating point (D format) + * f single precision floating point (F format) + * g G format floating + * h H format floating + * l longword + * o octaword + * q quadword + * w word + * ? simple synthetic branch operand + * - unconditional synthetic JSB/JSR operand + * ! complex synthetic branch operand + * + * The '-?!' letter 2's are not for external consumption. They are used + * for various assemblers. Generally, all unknown widths are assumed 0. + * We don't limit your choice of width character. + * + * DEC operands are hard work to parse. For example, '@' as the first + * character means indirect (deferred) mode but elswhere it is a shift + * operator. + * The long-winded explanation of how this is supposed to work is + * cancelled. Read a DEC vax manual. + * We try hard not to parse anything that MIGHT be part of the expression + * buried in that syntax. For example if we see @...(Rn) we don't check + * for '-' before the '(' because mode @-(Rn) does not exist. + * + * After parsing we have: + * + * at TRUE if leading '@' (or Un*x '*') + * len takes one value from " bilsw". eg B^ -> 'b'. + * hash TRUE if leading '#' (or Un*x '$') + * expr_begin, expr_end the expression we did not parse + * even though we don't interpret it, we make use + * of its presence or absence. + * sign -1: -(Rn) 0: absent +1: (Rn)+ + * paren TRUE if () are around register + * reg major register number 0:15 -1 means absent + * ndx index register number 0:15 -1 means absent + * + * Again, I dare not explain it: just trace ALL the code! + */ + +char * /* (code here) bug message, "" = OK */ + /* our code bug, NOT bad assembly language */ +vip_op (optext, vopP) + char *optext; /* user's input string e.g.: */ + /* "@B^foo@bar(AP)[FP]:" */ + struct vop *vopP; /* In: vop_access, vop_width. */ + /* Out: _ndx, _reg, _mode, _short, _warn, */ + /* _error _expr_begin, _expr_end, _nbytes. */ + /* vop_nbytes : number of bytes in a datum. */ +{ + char *p; /* track operand text forward */ + char *q; /* track operand text backward */ + int at; /* TRUE if leading '@' ('*') seen */ + char len; /* one of " bilsw" */ + int hash; /* TRUE if leading '#' ('$') seen */ + int sign; /* -1, 0 or +1 */ + int paren; /* TRUE if () surround register */ + int reg; /* register number, -1:absent */ + int ndx; /* index register number -1:absent */ + char *bug; /* report any logic error in here, ""==OK */ + char *err; /* report illegal operand, ""==OK */ + /* " " is a FAKE error: means we won */ + /* ANY err that begins with ' ' is a fake. */ + /* " " is converted to "" before return */ + char *wrn; /* warn about weird modes pf address */ + char *oldq; /* preserve q in case we backup */ + int mode; /* build up 4-bit operand mode here */ + /* note: index mode is in ndx, this is */ + /* the major mode of operand address */ +/* + * Notice how we move wrong-arg-type bugs INSIDE this module: if we + * get the types wrong below, we lose at compile time rather than at + * lint or run time. + */ + char access; /* vop_access. */ + char width; /* vop_width. */ + + int vax_reg_parse (); /* returns 0:15 or -1 if not a register */ + + access = vopP->vop_access; + width = vopP->vop_width; + bug = /* none of our code bugs (yet) */ + err = /* no user text errors */ + wrn = ""; /* no warnings even */ + + p = optext; + + if (*p == ' ') /* Expect all whitespace reduced to ' '. */ + p++; /* skip over whitespace */ + + if (at = INDIRECTP (*p)) + { /* TRUE if *p=='@'(or '*' for Un*x) */ + p++; /* at is determined */ + if (*p == ' ') /* Expect all whitespace reduced to ' '. */ + p++; /* skip over whitespace */ + } + + /* + * This code is subtle. It tries to detect all legal (letter)'^' + * but it doesn't waste time explicitly testing for premature '\0' because + * this case is rejected as a mismatch against either (letter) or '^'. + */ + { + register char c; + + c = *p; + if (isupper (c)) + c = tolower (c); + if (DISPLENP (p[1]) && strchr ("bilws", len = c)) + p += 2; /* skip (letter) '^' */ + else /* no (letter) '^' seen */ + len = ' '; /* len is determined */ + } + + if (*p == ' ') /* Expect all whitespace reduced to ' '. */ + p++; /* skip over whitespace */ + + if (hash = IMMEDIATEP (*p)) /* TRUE if *p=='#' ('$' for Un*x) */ + p++; /* hash is determined */ + + /* + * p points to what may be the beginning of an expression. + * We have peeled off the front all that is peelable. + * We know at, len, hash. + * + * Lets point q at the end of the text and parse that (backwards). + */ + + for (q = p; *q; q++) + ; + q--; /* now q points at last char of text */ + + if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */ + q--; + /* reverse over whitespace, but don't */ + /* run back over *p */ + + /* + * As a matter of policy here, we look for [Rn], although both Rn and S^# + * forbid [Rn]. This is because it is easy, and because only a sick + * cyborg would have [...] trailing an expression in a VAX-like assembler. + * A meticulous parser would first check for Rn followed by '(' or '[' + * and not parse a trailing ']' if it found another. We just ban expressions + * ending in ']'. + */ + if (*q == ']') + { + while (q >= p && *q != '[') + q--; + /* either q<p or we got matching '[' */ + if (q < p) + err = "no '[' to match ']'"; + else + { + /* + * Confusers like "[]" will eventually lose with a bad register + * name error. So again we don't need to check for early '\0'. + */ + if (q[3] == ']') + ndx = vax_reg_parse (q[1], q[2], 0); + else if (q[4] == ']') + ndx = vax_reg_parse (q[1], q[2], q[3]); + else + ndx = -1; + /* + * Since we saw a ']' we will demand a register name in the []. + * If luser hasn't given us one: be rude. + */ + if (ndx < 0) + err = "bad register in []"; + else if (ndx == PC) + err = "[PC] index banned"; + else + q--; /* point q just before "[...]" */ + } + } + else + ndx = -1; /* no ']', so no iNDeX register */ + + /* + * If err = "..." then we lost: run away. + * Otherwise ndx == -1 if there was no "[...]". + * Otherwise, ndx is index register number, and q points before "[...]". + */ + + if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */ + q--; + /* reverse over whitespace, but don't */ + /* run back over *p */ + if (!*err) + { + sign = 0; /* no ()+ or -() seen yet */ + + if (q > p + 3 && *q == '+' && q[-1] == ')') + { + sign = 1; /* we saw a ")+" */ + q--; /* q points to ')' */ + } + + if (*q == ')' && q > p + 2) + { + paren = TRUE; /* assume we have "(...)" */ + while (q >= p && *q != '(') + q--; + /* either q<p or we got matching '(' */ + if (q < p) + err = "no '(' to match ')'"; + else + { + /* + * Confusers like "()" will eventually lose with a bad register + * name error. So again we don't need to check for early '\0'. + */ + if (q[3] == ')') + reg = vax_reg_parse (q[1], q[2], 0); + else if (q[4] == ')') + reg = vax_reg_parse (q[1], q[2], q[3]); + else + reg = -1; + /* + * Since we saw a ')' we will demand a register name in the ')'. + * This is nasty: why can't our hypothetical assembler permit + * parenthesised expressions? BECAUSE I AM LAZY! That is why. + * Abuse luser if we didn't spy a register name. + */ + if (reg < 0) + { + /* JF allow parenthasized expressions. I hope this works */ + paren = FALSE; + while (*q != ')') + q++; + /* err = "unknown register in ()"; */ + } + else + q--; /* point just before '(' of "(...)" */ + /* + * If err == "..." then we lost. Run away. + * Otherwise if reg >= 0 then we saw (Rn). + */ + } + /* + * If err == "..." then we lost. + * Otherwise paren==TRUE and reg = register in "()". + */ + } + else + paren = FALSE; + /* + * If err == "..." then we lost. + * Otherwise, q points just before "(Rn)", if any. + * If there was a "(...)" then paren==TRUE, and reg is the register. + */ + + /* + * We should only seek '-' of "-(...)" if: + * we saw "(...)" paren == TRUE + * we have no errors so far ! *err + * we did not see '+' of "(...)+" sign < 1 + * We don't check len. We want a specific error message later if + * user tries "x^...-(Rn)". This is a feature not a bug. + */ + if (!*err) + { + if (paren && sign < 1)/* !sign is adequate test */ + { + if (*q == '-') + { + sign = -1; + q--; + } + } + /* + * We have back-tracked over most + * of the crud at the end of an operand. + * Unless err, we know: sign, paren. If paren, we know reg. + * The last case is of an expression "Rn". + * This is worth hunting for if !err, !paren. + * We wouldn't be here if err. + * We remember to save q, in case we didn't want "Rn" anyway. + */ + if (!paren) + { + if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */ + q--; + /* reverse over whitespace, but don't */ + /* run back over *p */ + if (q > p && q < p + 3) /* room for Rn or Rnn exactly? */ + reg = vax_reg_parse (p[0], p[1], q < p + 2 ? 0 : p[2]); + else + reg = -1; /* always comes here if no register at all */ + /* + * Here with a definitive reg value. + */ + if (reg >= 0) + { + oldq = q; + q = p - 1; + } + } + } + } + /* + * have reg. -1:absent; else 0:15 + */ + + /* + * We have: err, at, len, hash, ndx, sign, paren, reg. + * Also, any remaining expression is from *p through *q inclusive. + * Should there be no expression, q==p-1. So expression length = q-p+1. + * This completes the first part: parsing the operand text. + */ + + /* + * We now want to boil the data down, checking consistency on the way. + * We want: len, mode, reg, ndx, err, p, q, wrn, bug. + * We will deliver a 4-bit reg, and a 4-bit mode. + */ + + /* + * Case of branch operand. Different. No L^B^W^I^S^ allowed for instance. + * + * in: at ? + * len ? + * hash ? + * p:q ? + * sign ? + * paren ? + * reg ? + * ndx ? + * + * out: mode 0 + * reg -1 + * len ' ' + * p:q whatever was input + * ndx -1 + * err " " or error message, and other outputs trashed + */ + /* branch operands have restricted forms */ + if (!*err && access == 'b') + { + if (at || hash || sign || paren || ndx >= 0 || reg >= 0 || len != ' ') + err = "invalid branch operand"; + else + err = " "; + } + +/* Since nobody seems to use it: comment this 'feature'(?) out for now. */ +#ifdef NEVER + /* + * Case of stand-alone operand. e.g. ".long foo" + * + * in: at ? + * len ? + * hash ? + * p:q ? + * sign ? + * paren ? + * reg ? + * ndx ? + * + * out: mode 0 + * reg -1 + * len ' ' + * p:q whatever was input + * ndx -1 + * err " " or error message, and other outputs trashed + */ + if (!*err) + { + if (access == ' ') + { /* addresses have restricted forms */ + if (at) + err = "address prohibits @"; + else + { + if (hash) + err = "address prohibits #"; + else + { + if (sign) + { + if (sign < 0) + err = "address prohibits -()"; + else + err = "address prohibits ()+"; + } + else + { + if (paren) + err = "address prohibits ()"; + else + { + if (ndx >= 0) + err = "address prohibits []"; + else + { + if (reg >= 0) + err = "address prohibits register"; + else + { + if (len != ' ') + err = "address prohibits displacement length specifier"; + else + { + err = " "; /* succeed */ + mode = 0; + } + } + } + } + } + } + } + } + } +#endif /*#Ifdef NEVER*/ + + /* + * Case of S^#. + * + * in: at FALSE + * len 's' definition + * hash TRUE demand + * p:q demand not empty + * sign 0 by paren==FALSE + * paren FALSE by "()" scan logic because "S^" seen + * reg -1 or nn by mistake + * ndx -1 + * + * out: mode 0 + * reg -1 + * len 's' + * exp + * ndx -1 + */ + if (!*err && len == 's') + { + if (!hash || paren || at || ndx >= 0) + err = "invalid operand of S^#"; + else + { + if (reg >= 0) + { + /* + * SHIT! we saw S^#Rnn ! put the Rnn back in + * expression. KLUDGE! Use oldq so we don't + * need to know exact length of reg name. + */ + q = oldq; + reg = 0; + } + /* + * We have all the expression we will ever get. + */ + if (p > q) + err = "S^# needs expression"; + else if (access == 'r') + { + err = " "; /* WIN! */ + mode = 0; + } + else + err = "S^# may only read-access"; + } + } + + /* + * Case of -(Rn), which is weird case. + * + * in: at FALSE + * len ' + * hash FALSE + * p:q q<p + * sign -1 by definition + * paren TRUE by definition + * reg present by definition + * ndx optional + * + * out: mode 7 + * reg present + * len ' ' + * exp "" enforce empty expression + * ndx optional warn if same as reg + */ + if (!*err && sign < 0) + { + if (len != ' ' || hash || at || p <= q) + err = "invalid operand of -()"; + else + { + err = " "; /* win */ + mode = 7; + if (reg == PC) + wrn = "-(PC) unpredictable"; + else if (reg == ndx) + wrn = "[]index same as -()register: unpredictable"; + } + } + + /* + * We convert "(Rn)" to "@Rn" for our convenience. + * (I hope this is convenient: has someone got a better way to parse this?) + * A side-effect of this is that "@Rn" is a valid operand. + */ + if (paren && !sign && !hash && !at && len == ' ' && p > q) + { + at = TRUE; + paren = FALSE; + } + + /* + * Case of (Rn)+, which is slightly different. + * + * in: at + * len ' ' + * hash FALSE + * p:q q<p + * sign +1 by definition + * paren TRUE by definition + * reg present by definition + * ndx optional + * + * out: mode 8+@ + * reg present + * len ' ' + * exp "" enforce empty expression + * ndx optional warn if same as reg + */ + if (!*err && sign > 0) + { + if (len != ' ' || hash || p <= q) + err = "invalid operand of ()+"; + else + { + err = " "; /* win */ + mode = 8 + (at ? 1 : 0); + if (reg == PC) + wrn = "(PC)+ unpredictable"; + else if (reg == ndx) + wrn = "[]index same as ()+register: unpredictable"; + } + } + + /* + * Case of #, without S^. + * + * in: at + * len ' ' or 'i' + * hash TRUE by definition + * p:q + * sign 0 + * paren FALSE + * reg absent + * ndx optional + * + * out: mode 8+@ + * reg PC + * len ' ' or 'i' + * exp + * ndx optional + */ + if (!*err && hash) + { + if (len != 'i' && len != ' ') + err = "# conflicts length"; + else if (paren) + err = "# bars register"; + else + { + if (reg >= 0) + { + /* + * SHIT! we saw #Rnn! Put the Rnn back into the expression. + * By using oldq, we don't need to know how long Rnn was. + * KLUDGE! + */ + q = oldq; + reg = -1; /* no register any more */ + } + err = " "; /* win */ + + /* JF a bugfix, I think! */ + if(at && access=='a') + vopP->vop_nbytes=4; + + mode = (at ? 9 : 8); + reg = PC; + if ((access == 'm' || access == 'w') && !at) + wrn = "writing or modifying # is unpredictable"; + } + } + /* + * If !*err, then sign == 0 + * hash == FALSE + */ + + /* + * Case of Rn. We seperate this one because it has a few special + * errors the remaining modes lack. + * + * in: at optional + * len ' ' + * hash FALSE by program logic + * p:q empty + * sign 0 by program logic + * paren FALSE by definition + * reg present by definition + * ndx optional + * + * out: mode 5+@ + * reg present + * len ' ' enforce no length + * exp "" enforce empty expression + * ndx optional warn if same as reg + */ + if (!*err && !paren && reg >= 0) + { + if (len != ' ') + err = "length not needed"; + else if (at) + { + err = " "; /* win */ + mode = 6; /* @Rn */ + } + else if (ndx >= 0) + err = "can't []index a register, because it has no address"; + else if (access == 'a') + err = "a register has no address"; + else + { + /* + * Idea here is to detect from length of datum + * and from register number if we will touch PC. + * Warn if we do. + * vop_nbytes is number of bytes in operand. + * Compute highest byte affected, compare to PC0. + */ + if ((vopP->vop_nbytes + reg * 4) > 60) + wrn = "PC part of operand unpredictable"; + err = " "; /* win */ + mode = 5; /* Rn */ + } + } + /* + * If !*err, sign == 0 + * hash == FALSE + * paren == TRUE OR reg==-1 + */ + + /* + * Rest of cases fit into one bunch. + * + * in: at optional + * len ' ' or 'b' or 'w' or 'l' + * hash FALSE by program logic + * p:q expected (empty is not an error) + * sign 0 by program logic + * paren optional + * reg optional + * ndx optional + * + * out: mode 10 + @ + len + * reg optional + * len ' ' or 'b' or 'w' or 'l' + * exp maybe empty + * ndx optional warn if same as reg + */ + if (!*err) + { + err = " "; /* win (always) */ + mode = 10 + (at ? 1 : 0); + switch (len) + { + case 'l': + mode += 2; + case 'w': + mode += 2; + case ' ': /* assumed B^ until our caller changes it */ + case 'b': + break; + } + } + + /* + * here with completely specified mode + * len + * reg + * expression p,q + * ndx + */ + + if (*err == ' ') + err = ""; /* " " is no longer an error */ + + vopP->vop_mode = mode; + vopP->vop_reg = reg; + vopP->vop_short = len; + vopP->vop_expr_begin = p; + vopP->vop_expr_end = q; + vopP->vop_ndx = ndx; + vopP->vop_error = err; + vopP->vop_warn = wrn; + return (bug); + +} /* vip_op() */ + +/* + +Summary of vip_op outputs. + + mode reg len ndx +(Rn) => @Rn +{@}Rn 5+@ n ' ' optional +branch operand 0 -1 ' ' -1 +S^#foo 0 -1 's' -1 +-(Rn) 7 n ' ' optional +{@}(Rn)+ 8+@ n ' ' optional +{@}#foo, no S^ 8+@ PC " i" optional +{@}{q^}{(Rn)} 10+@+q option " bwl" optional + +*/ + +#ifdef TEST /* #Define to use this testbed. */ + +/* + * Follows a test program for this function. + * We declare arrays non-local in case some of our tiny-minded machines + * default to small stacks. Also, helps with some debuggers. + */ + +#include <stdio.h> + +char answer[100]; /* human types into here */ +char *p; /* */ +char *myerr; +char *mywrn; +char *mybug; +char myaccess; +char mywidth; +char mymode; +char myreg; +char mylen; +char *myleft; +char *myright; +char myndx; +int my_operand_length; +char my_immediate[200]; +char my_indirect[200]; +char my_displen[200]; + +main () +{ + char *vip_op (); /* make cc happy */ + + printf ("enter immediate symbols eg enter # "); + gets (my_immediate); + printf ("enter indirect symbols eg enter @ "); + gets (my_indirect); + printf ("enter displen symbols eg enter ^ "); + gets (my_displen); + vip_op_defaults (my_immediate, my_indirect, my_displen); + for (;;) + { + printf ("access,width (eg 'ab' or 'wh') [empty line to quit] : "); + fflush (stdout); + gets (answer); + if (!answer[0]) + exit (0); + myaccess = answer[0]; + mywidth = answer[1]; + switch (mywidth) + { + case 'b': + my_operand_length = 1; + break; + case 'd': + my_operand_length = 8; + break; + case 'f': + my_operand_length = 4; + break; + case 'g': + my_operand_length = 16; + break; + case 'h': + my_operand_length = 32; + break; + case 'l': + my_operand_length = 4; + break; + case 'o': + my_operand_length = 16; + break; + case 'q': + my_operand_length = 8; + break; + case 'w': + my_operand_length = 2; + break; + case '!': + case '?': + case '-': + my_operand_length = 0; + break; + + default: + my_operand_length = 2; + printf ("I dn't understand access width %c\n", mywidth); + break; + } + printf ("VAX assembler instruction operand: "); + fflush (stdout); + gets (answer); + mybug = vip_op (answer, myaccess, mywidth, my_operand_length, + &mymode, &myreg, &mylen, &myleft, &myright, &myndx, + &myerr, &mywrn); + if (*myerr) + { + printf ("error: \"%s\"\n", myerr); + if (*mybug) + printf (" bug: \"%s\"\n", mybug); + } + else + { + if (*mywrn) + printf ("warning: \"%s\"\n", mywrn); + mumble ("mode", mymode); + mumble ("register", myreg); + mumble ("index", myndx); + printf ("width:'%c' ", mylen); + printf ("expression: \""); + while (myleft <= myright) + putchar (*myleft++); + printf ("\"\n"); + } + } +} + +mumble (text, value) + char *text; + int value; +{ + printf ("%s:", text); + if (value >= 0) + printf ("%xx", value); + else + printf ("ABSENT"); + printf (" "); +} + +#endif /* ifdef TEST */ + +/* end: vip_op.c */ + +const int md_short_jump_size = 3; +const int md_long_jump_size = 6; +const int md_reloc_size = 8; /* Size of relocation record */ + +void +md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, to_addr; + fragS *frag; + symbolS *to_symbol; +{ + long offset; + + offset = to_addr - (from_addr + 1); + *ptr++ = 0x31; + md_number_to_chars (ptr, offset, 2); +} + +void +md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, to_addr; + fragS *frag; + symbolS *to_symbol; +{ + long offset; + + offset = to_addr - to_symbol->sy_value; + *ptr++ = 0x17; + *ptr++ = 0x9F; + md_number_to_chars (ptr, offset, 4); + fix_new (frag, ptr - frag->fr_literal, 4, to_symbol, (symbolS *) 0, (long) 0, 0); +} + +int +md_parse_option (argP, cntP, vecP) + char **argP; + int *cntP; + char ***vecP; +{ + char *temp_name; /* name for -t or -d options */ + char opt; + + switch (**argP) + { + case 'J': + /* as_warn ("I can do better than -J!"); */ + break; + + case 'S': + as_warn ("SYMBOL TABLE not implemented"); + break; /* SYMBOL TABLE not implemented */ + + case 'T': + as_warn ("TOKEN TRACE not implemented"); + break; /* TOKEN TRACE not implemented */ + + case 'd': + case 't': + opt= **argP; + if (**argP) + { /* Rest of argument is filename. */ + temp_name = *argP; + while (**argP) + (*argP)++; + } + else if (*cntP) + { + while (**argP) + (*argP)++; + --(*cntP); + temp_name = *++(*vecP); + **vecP = NULL; /* Remember this is not a file-name. */ + } + else + { + as_warn ("I expected a filename after -%c.",opt); + temp_name = "{absent}"; + } + + if(opt=='d') + as_warn ("Displacement length %s ignored!", temp_name); + else + as_warn ("I don't need or use temp. file \"%s\".", temp_name); + break; + + case 'V': + as_warn ("I don't use an interpass file! -V ignored"); + break; + +#ifdef VMS + case '+': /* For g++ */ + break; + + case 'h': /* No hashing of mixed-case names */ + break; + + case 'H': /* Show new symbol after hash truncation */ + break; +#endif + + default: + return 0; + + } + return 1; +} + +/* We have no need to default values of symbols. */ + +/* ARGSUSED */ +symbolS * +md_undefined_symbol (name) + char *name; +{ + return 0; +} + +/* Parse an operand that is machine-specific. + We just return without modifying the expression if we have nothing + to do. */ + +/* ARGSUSED */ +void +md_operand (expressionP) + expressionS *expressionP; +{ +} + +/* Round up a section size to the appropriate boundary. */ +long +md_section_align (segment, size) + segT segment; + long size; +{ + return size; /* Byte alignment is fine */ +} + +/* Exactly what point is a PC-relative offset relative TO? + On the vax, they're relative to the address of the offset, plus + its size. (??? Is this right? FIXME-SOON) */ +long +md_pcrel_from (fixP) + fixS *fixP; +{ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} diff --git a/gas/config/tc-vax.h b/gas/config/tc-vax.h new file mode 100644 index 0000000..0097782 --- /dev/null +++ b/gas/config/tc-vax.h @@ -0,0 +1,14 @@ +/* + * This file is tc-vax.h. + */ + +#define TC_VAX 1 + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of tc-vax.h */ diff --git a/gas/config/te-generic.h b/gas/config/te-generic.h new file mode 100644 index 0000000..4b40d61 --- /dev/null +++ b/gas/config/te-generic.h @@ -0,0 +1,18 @@ +/* + * This file is te-generic.h and is intended to be a template for + * target environment specific header files. + */ + +#define TE_GENERIC 1 + + /* these define interfaces */ +#include "obj-format.h" + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of te-generic.h */ diff --git a/gas/config/te-ic960.h b/gas/config/te-ic960.h new file mode 100644 index 0000000..15c3064 --- /dev/null +++ b/gas/config/te-ic960.h @@ -0,0 +1,28 @@ +/* + * This file is te-ic960.h and is intended to define ic960 environment + * specific differences. + */ + +/* $Id$ */ + +#define TE_IC960 1 + + /* intel uses host byte order for headers */ +#ifdef CROSS_ASSEMBLE +#undef CROSS_ASSEMBLE +#endif /* CROSS_ASSEMBLE */ + +#define OBJ_COFF_OMIT_OPTIONAL_HEADER +#define LOCAL_LABEL(name) ( (name[0] =='L') \ + || (name[0] =='.' \ + && (name[1]=='C' || name[1]=='I' || name[1]=='.'))) +#include "obj-format.h" + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of te-ic960.h */ diff --git a/gas/config/te-sun3.h b/gas/config/te-sun3.h new file mode 100644 index 0000000..9d1ece4 --- /dev/null +++ b/gas/config/te-sun3.h @@ -0,0 +1,48 @@ +/* te-sun3.h -- Sun-3 target environment declarations. + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +/* This header file contains the #defines specific + to SUN computer SUN 3 series computers. (The only kind + we have around here, unfortunatly.) + + Rumor has it that this file will work on the Sun-2 if the assembler + is called with -m68010 This is not tested. */ + + +#define TE_SUN3 1 +#define SUN_ASM_SYNTAX + +/* Could also be : +#define S_LOCAL_NAME(s) (S_GET_NAME(s)[0] == '.' && + S_GET_NAME(s)[1] == 'L' || + S_GET_NAME(s)[1] == '.') +*/ + +#include "obj-format.h" + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of te-sun3.h */ diff --git a/gas/config/vax-inst.h b/gas/config/vax-inst.h new file mode 100644 index 0000000..51b7c94 --- /dev/null +++ b/gas/config/vax-inst.h @@ -0,0 +1,77 @@ +/* vax-inst.h - GNU - Part of vax.c + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * This is part of vax-ins-parse.c & friends. + * We want to parse a vax instruction text into a tree defined here. + */ + +#define VIT_MAX_OPERANDS (6) /* maximum number of operands in one */ + /* single vax instruction */ + +struct vop /* vax instruction operand */ +{ + short int vop_ndx; /* -1, or index register. eg 7=[R7] */ + short int vop_reg; /* -1, or register number. eg @I^#=0xF */ + /* Helps distinguish "abs" from "abs(PC)". */ + short int vop_mode; /* addressing mode 4 bits. eg I^#=0x9 */ + char vop_short; /* operand displacement length as written */ + /* ' '=none, "bilsw"=B^I^L^S^W^. */ + char vop_access; /* 'b'branch ' 'no-instruction 'amrvw'norm */ + char vop_width; /* Operand width, one of "bdfghloqw" */ + char * vop_warn; /* warning message of this operand, if any */ + char * vop_error; /* say if operand is inappropriate */ + char * vop_expr_begin; /* Unparsed expression, 1st char ... */ + char * vop_expr_end; /* ... last char. */ + unsigned char vop_nbytes; /* number of bytes in datum */ +}; + + +typedef long vax_opcodeT; /* For initialising array of opcodes */ + /* Some synthetic opcodes > 16 bits! */ + +#define VIT_OPCODE_SYNTHETIC 0x80000000 /* Not real hardware instruction. */ +#define VIT_OPCODE_SPECIAL 0x40000000 /* Not normal branch optimising. */ + /* Never set without ..._SYNTHETIC */ + +#define VAX_WIDTH_UNCONDITIONAL_JUMP '-' /* These are encoded into */ +#define VAX_WIDTH_CONDITIONAL_JUMP '?' /* vop_width when vop_access=='b' */ +#define VAX_WIDTH_WORD_JUMP '!' /* and VIT_OPCODE_SYNTHETIC set. */ +#define VAX_WIDTH_BYTE_JUMP ':' /* */ + +#define VAX_JMP (0x17) /* Useful for branch optimising. Jump instr*/ +#define VAX_PC_RELATIVE_MODE (0xef) /* Use it after VAX_JMP */ +#define VAX_ABSOLUTE_MODE (0x9F) /* Use as @#... */ +#define VAX_BRB (0x11) /* Canonical branch. */ +#define VAX_BRW (0x31) /* Another canonical branch */ +#define VAX_WIDEN_WORD (0x20) /* Add this to byte branch to get word br. */ +#define VAX_WIDEN_LONG (0x6) /* Add this to byte branch to get long jmp.*/ + /* Needs VAX_PC_RELATIVE_MODE byte after it*/ + +struct vit /* vax instruction tree */ +{ + /* vit_opcode is char[] for portability. */ + char vit_opcode [ sizeof (vax_opcodeT) ]; + unsigned char vit_opcode_nbytes; /* How long is _opcode? (chars) */ + unsigned char vit_operands;/* */ + struct vop vit_operand[VIT_MAX_OPERANDS]; /* operands */ + char * vit_error; /* "" or error text */ +}; + +/* end: vax-inst.h */ diff --git a/gas/configure b/gas/configure new file mode 100755 index 0000000..f6bf811 --- /dev/null +++ b/gas/configure @@ -0,0 +1,876 @@ +#!/bin/sh +# Do not edit this file. It is generated automatically from configure.in +# and a configure template. +configdirs= + +#!/bin/sh +# Do not edit this file. It is generated automatically from configure.in +# and a configure template. +configdirs= + +# Configuration script template +# Copyright (C) 1988, 1990, 1991 Free Software Foundation, Inc. + +#This file is part of GNU. + +#GNU CC is free software; you can redistribute it and/or modify +#it under the terms of the GNU General Public License as published by +#the Free Software Foundation; either version 1, or (at your option) +#any later version. + +#GNU CC 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 GNU CC; see the file COPYING. If not, write to +#the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +# +# Shell script to create proper links to machine-dependent files in +# preparation for compiling gcc. +# +# Usage: configure [+srcdir=DIR] [+host=HOST] [+gas] [+nfp] TARGET +# +# If configure succeeds, it leaves its status in config.status. +# If configure fails after disturbing the status quo, +# config.status is removed. +# + +progname=$0 + +remove=rm +hard_link=ln +symbolic_link='ln -s' + +#for Test +#remove="echo rm" +#hard_link="echo ln" +#symbolic_link="echo ln -s" + +# clear some things potentially inherited from environment. +target= +template= +removing= +norecurse= +ansi= + +for arg in $*; +do + case $arg in + -ansi | +ansi) + ansi=true + ;; + -template=* | +template=*) + template=`echo $arg | sed 's/[+-]template=//'` + ;; + -norecurse | +norecurse) + norecurse=true + ;; + -rm | +rm) + removing=$arg + ;; + -srcdir=* | +srcdir=* | +srcdi=* | +srcd=* | +src=* | +sr=* | +s=*) + srcdir=`echo $arg | sed 's/[+-]s[a-z]*=//'` + ;; + -host=* | +host=* | +hos=* | +ho=* | +h=*) + host=`echo $arg | sed 's/[+-]h[a-z]*=//'` + ;; + -languages=* | +languages=* | -languag=* | +languag=* | langua=* \ + | +langua=* | -langu=* | +langu=* | -lang=* | +lang=* | -lan=* \ + | +lan=* | -la=* | +la=* | -l=* | +l=*) + languages="$languages `echo $arg | sed 's/[+-]l[a-z]*=//'`" + ;; + -gas | +gas | +ga | +g) + gas=yes + ;; + -nfp | +nfp | +nf | +n) + nfp=yes + ;; + *) +# Allow configure HOST TARGET + if [ x$host = x ] ; then host=$target ; fi + target=$arg + ;; + esac +done + +# process host and target only if not rebuilding configure itself. +if [ -z "$template" ] +then + # Complain if an arg is missing + if [ x$target = x ] + then + echo "Usage: $progname [+srcdir=DIR] [+host=HOST] [+gas] [+nfp] TARGET" + echo -n "Where HOST and TARGET are something like " + echo "\`vax', \`sun3', \`encore', etc." + if [ -r config.status ] + then + cat config.status + fi + exit 1 + fi + + # Default other arg + if [ x$host = x ] + then + host=$target + fi + + # Decode the host machine, then the target machine. + # For the host machine, we save the xm_file variable as host_xm_file; + # then we decode the target machine and forget everything else + # that came from the host machine. + for machine in $host $target; do + + # Separate what the user gave into CPU/company and OS (if any). + basic_machine=`echo $machine | sed 's/-[^-]*$//'` + if [ $basic_machine != $machine ] + then os=`echo $machine | sed 's/[^-]*-/-/'` + else os=; fi + + # Decode aliases for certain machine/company combinations. + case $basic_machine in + iris | iris4d) + basic_machine=mips/sgi + ;; + news | news800) + basic_machine=m68k/sony + ;; + 3b1 | 7300 | 7300/att | att-7300) + basic_machine=m68k/att + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300/motorola | delta/motorola) + basic_machine=m68k/motorola + ;; + vax/dec) + basic_machine=vax + ;; + balance) + basic_machine=ns32k/sequent + ;; + symmetry) + basic_machine=i386/sequent + ;; + sun2) + basic_machine=m68000/sun + ;; + sun3) + basic_machine=m68k/sun + ;; + sun4) + basic_machine=sparc/sun + ;; + sun386 | sun386i) + basic_machine=i386/sun + ;; + ps2) + basic_machine=i386/ibm + ;; + next) + basic_machine=m68k/next + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k/hp + ;; + hp9k31[0-9] | hp9k2[0-9][0-9]) + basic_machine=m68000/hp + ;; + isi68) + basic_machine=m68k/isi + ;; + apollo68) + basic_machine=m68k/apollo + ;; + altos | altos3068) + basic_machine=m68k/altos + ;; + miniframe) + basic_machine=m68000/convergent + ;; + tower | tower-32) + basic_machine=m68k/ncr + ;; + news-3600 | risc-news) + basic_machine=mips/sony + ;; + decstation | decstation-3100 | pmax) + basic_machine=mips/dec + ;; + gmicro) + basic_machine=tron + ;; + convex-c1) + basic_machine=c1/convex + ;; + convex-c2) + basic_machine=c2/convex + ;; + esac + + # Decode manufacturer-specific aliases for certain operating systems. + + case $os in + -newsos*) + os=-bsd + ;; + -ultrix*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -ctix*) + os=-sysv + ;; + esac + + machine=$basic_machine$os + + cpu_type= + xm_file= + tm_file= + make_var_file= + + case $machine in + vax | vax-bsd*) # vaxen running BSD + ;; + vax-vms | vms) # vaxen running VMS + cpu_type=vax + xm_file=xm-vms.h + tm_file=tm-vms.h + ;; + vax-sysv* | vaxv) # vaxen running system V + cpu_type=vax + xm_file=xm-vaxv.h + tm_file=tm-vaxv.h + ;; + tahoe | tahoe-bsd*) # tahoe running BSD + ;; + tahoe/harris*) # Harris tahoe, using COFF. + cpu_type=tahoe + ;; + i386/sequent* | i386/sequent-bsd*) # 80386 from Sequent + cpu_type=i386 + xm_file=xm-i386.h + tm_file=tm-seq386.h + ;; + i386-mach | i386/*-mach) + cpu_type=i386 + xm_file=xm-i386.h + tm_file=tm-i386gas.h + ;; + i386/sco | i386/sco-sysv* | i386/*-sco) # 80386 running SCO system + cpu_type=i386 + xm_file=xm-i386v.h + tm_file=tm-i386sco.h + make_var_file=make-i386sco + ;; + i386/isc | i386/isc-sysv* | i386/*-isc) # 80386 running ISC system + cpu_type=i386 + xm_file=xm-i386v.h + tm_file=tm-i386isc.h + make_var_file=make-i386isc + ;; + i386/ibm | i386-aix | i386/ibm-aix) # IBM PS/2 running AIX + cpu_type=i386 + tm_file=tm-i386v.h + xm_file=xm-i386v.h + make_var_file=make-i386v + ;; + i386/sun*) + cpu_type=i386 + xm_file=xm-sun386i.h + tm_file=tm-sun386i.h + ;; + i386-sysv4 | i386/*-sysv4 | i386v4) # Intel 80386's running system V.4 + cpu_type=i386 + xm_file=xm-i386v.h + make_var_file=make-i386v + tm_file=tm-i386v4.h + ;; + i386-sysv* | i386/*-sysv* | i386v) # Intel 80386's running system V + cpu_type=i386 + xm_file=xm-i386v.h + make_var_file=make-i386v + if [ x$gas = xyes ] + then + tm_file=tm-i386gas.h + else + tm_file=tm-i386v.h + fi + ;; + i860 | i860-sysv* | i860/*-sysv*) + cpu_type=i860 + if [ x$gas = xyes ] + then + tm_file=tm-i860g.h + else + tm_file=tm-i860.h + fi + ;; + i860-bsd* | i860/*-bsd*) + cpu_type=i860 + if [ x$gas = xyes ] + then + tm_file=tm-i860bsdg.h + else + tm_file=tm-i860bsd.h + fi + ;; + sparc | sparc/* | sparc-*os4 | sparc/*-*os4) + cpu_type=sparc + tm_file=tm-sparc.h + ;; + sparc-*os3 | sparc/*-*os3) + cpu_type=sparc + tm_file=tm-sun4os3.h + ;; + m68k/next) + cpu_type=m68k + tm_file=tm-next.h + out_file=out-next.c + xm_file=xm-next.h + ;; + m68k/sun-*os3) + cpu_type=m68k + if [ x$nfp = xyes ] + then + tm_file=tm-sun3os3nf.h + else + tm_file=tm-sun3os3.h + fi + ;; + m68k/sun-mach) + cpu_type=m68k + tm_file=tm-sun3mach.h + ;; + m68k/sun | m68k/sun-*os4) + cpu_type=m68k + if [ x$nfp = xyes ] + then + tm_file=tm-sun3nfp.h + else + tm_file=tm-sun3.h + fi + ;; + m68k/hp | m68k/hp-hpux*) # HP 9000 series 300 + cpu_type=m68k + xm_file=xm-hp9k320.h + if [ x$gas = xyes ] + then + make_var_file=make-hp9k320g + tm_file=tm-hp9k320g.h + else + make_var_file=make-hp9k320 + tm_file=tm-hp9k320.h + fi + ;; + m68k/hp-bsd*) # HP 9000/3xx running Berkeley Unix + cpu_type=m68k + tm_file=tm-hp9k3bsd.h + ;; + m68k/isi | m68k/isi-bsd*) + cpu_type=m68k + if [ x$nfp = xyes ] + then + tm_file=tm-isi68-nfp.h + else + tm_file=tm-isi68.h + fi + ;; + m68k/sony | m68k/sony-bsd*) + xm_file=xm-m68k.h + cpu_type=m68k + if [ x$gas = xyes ] + then + tm_file=tm-newsgas.h + else + tm_file=tm-news.h + fi + ;; + m68k/altos | m68k/altos-sysv*) # Altos 3068 + cpu_type=m68k + if [ x$gas = xyes ] + then + xm_file=xm-altos3068.h + tm_file=tm-altos3068.h + else + echo "The Altos is supported only with the GNU assembler" 1>&2 + exit 1 + fi + ;; + m68k/motorola | m68k/motorola-sysv*) + cpu_type=m68k + tm_file=tm-mot3300.h + xm_file=xm-mot3300.h + ;; + m68k/crds | m68k/crds-unos | m68k-unos | crds | unos) + cpu_type=m68k + xm_file=xm-crds.h + make_var_file=make-crds + tm_file=tm-crds.h + ;; + m68k/apollo) + cpu_type=m68k + make_var_file=make-apollo68 + tm_file=tm-apollo68.h + ;; + m68k/ncr | m68k/ncr-sysv*) # NCR Tower 32 SVR3 + cpu_type=m68k + tm_file=tm-tower-as.h + xm_file=xm-tower.h + ;; + m68000/sun | m68000/sun-*os3) + cpu_type=m68k + tm_file=tm-sun2.h + ;; + m68000/sun-*os4) + cpu_type=m68k + tm_file=tm-sun2os4.h + ;; + m68000/hp | m68000/hp-hpux*) # HP 9000 series 300 + cpu_type=m68k + xm_file=xm-hp9k310.h + if [ x$gas = xyes ] + then + make_var_file=make-hp9k320g + tm_file=tm-hp9k310g.h + else + make_var_file=make-hp9k320 + tm_file=tm-hp9k310.h + fi + ;; + m68000/hp-bsd*) # HP 9000/200 running BSD + cpu_type=m68k + tm_file=tm-hp9k2bsd.h + make_var_file=make-hp9k2bsd + ;; + m68000/att | m68000/att-sysv*) + cpu_type=m68k + xm_file=xm-3b1.h + if [ x$gas = xyes ] + then + tm_file=tm-3b1g.h + else + tm_file=tm-3b1.h + fi + ;; + m68000/convergent | m68000/convergent-sysv*) + cpu_type=m68k + xm_file=xm-3b1.h + tm_file=tm-ctix.h + ;; + ns32k/sequent | ns32k/sequent-bsd*) + cpu_type=ns32k + tm_file=tm-sequent.h + ;; + ns32k/encore | ns32k/encore-bsd* | encore | encore-bsd*) + cpu_type=ns32k + tm_file=tm-encore.h + ;; + ns32k-genix* | ns32k/*-genix* | genix) + cpu_type=ns32k + xm_file=xm-genix.h + make_var_file=make-genix + tm_file=tm-genix.h + ;; + merlin) + cpu_type=ns32k + ;; + m88k/dg | m88k/dg-dgux* | m88k-dgux*) + cpu_type=m88k + xm_file=xm-m88kdgux.h + make_var_file=make-m88kdgux + tm_file=tm-m88kdgux.h + ;; + m88k-v88r32 | m88k/*-v88r32) + cpu_type=m88k + tm_file=tm-v88r32.h + xm_file=xm-v88r32.h + ;; + m88k-sysv* | m88k/*-sysv*) + cpu_type=m88k + tm_file=tm-m88ksvr4.h + xm_file=xm-m88ksvr4.h + ;; + alliant | alliant/alliant) # Alliant FX/8 + cpu_type=alliant + tm_file=tm-alliant.h + ;; + c1/convex) # Convex C1 + if [ -r /usr/include/stdlib.h ] + then + tm_file=tm-convex1.h + else + tm_file=tm-conv1os7.h + fi + cpu_type=convex + ;; + c2/convex) # Convex C2 + if [ -r /usr/include/stdlib.h ] + then + tm_file=tm-convex2.h + else + tm_file=tm-conv2os7.h + fi + cpu_type=convex + ;; + mips/sgi | mips/sgi-sysv*) # Mostly like a MIPS. + cpu_type=mips + tm_file=tm-iris.h + xm_file=xm-iris.h + ;; + mips | mips/mips) # Default MIPS environment. + ;; + mips/dec | mips/dec-bsd*) # Decstation. + cpu_type=mips + tm_file=tm-decstatn.h + ;; + mips/sony | mips/sony-bsd*) # Sony NEWS 3600 or risc/news. + cpu_type=mips + tm_file=tm-mips-news.h + ;; + mips/*-sysv* | mips-sysv*) # SYSV variant of MIPS system. + cpu_type=mips + tm_file=tm-mips-sysv.h + ;; + mips/*-bsd* | mips-bsd*) # BSD 4.3 variant of MIPS system. + cpu_type=mips + tm_file=tm-mips-bsd.h + ;; + pyramid | pyramid/* | pyramid-*) + cpu_type=pyr + tm_file=tm-pyr.h + ;; + tron | tron/*) + cpu_type=gmicro + tm_file=tm_gmicro.h + ;; + a29k-bsd* | a29k/*-bsd*) + cpu_type=a29k + tm_file=tm-a29kunix.h + ;; + i960) # Default i960 environment. + ;; + # 370) + # ;; + esac + if [ x$pass1done = x ] + then + if [ x$cpu_type = x ]; then cpu_type=$host; fi + if [ x$xm_file = x ]; then host_xm_file=xm-$cpu_type.h + else host_xm_file=$xm_file + fi + if [ x$make_var_file = x ] + then make_var_file=make-$cpu_type; fi + host_make_var_file=$make_var_file + pass1done=yes + fi + done + + # Default the machine-specific variables that were not explicitly set. + if [ x$cpu_type = x ] + then cpu_type=$target; fi + + if [ x$tm_file = x ] + then tm_file=tm-$target.h; fi + + md_file=${cpu_type}.md + + if [ x$out_file = x ] + then out_file=out-$cpu_type.c; fi +fi + +#### configure.in files go here. +# This file is a shell script that supplies the information necessary +# to tailor a template configure script into the configure script +# appropriate for this directory. For more information, check any +# existing configure script. + +srctrigger=as.c +srcname="gas" + +case $target in +*-coff) + obj_format=coff + ;; +*-bout) + obj_format=bout + ;; +*) + obj_format=aout + ;; +esac + +case $target in +vax) + atof=vax + ;; +*) + atof=ieee + ;; +esac + +files="ho-${host}.h tc-${cpu_type}.c tc-${cpu_type}.h te-generic.h obj-${obj_format}.h obj-${obj_format}.c atof-${atof}.c" +links="host.h targ-cpu.c targ-cpu.h targ-env.h obj-format.h obj-format.c atof-targ.c" +### end of configure.in + +# are we rebuilding config itself? +if [ -n "$template" ] +then + if [ ! -r $template ] + then + echo "Can't find template ${template}." + exit 1 + fi + + mv configure configure.old + echo "#!/bin/sh" > configure + echo "# Do not edit this file. It is generated automatically from configure.in" >> configure + echo "# and a configure template." >> configure + echo "configdirs=" >> configure + echo >> configure + + if [ -r configure.in ] + then + sed -e "/^####/ r configure.in" $template >> configure + else + cat $template >> configure + fi + + chmod a+x configure + rm configure.old +# echo Rebuilt configure in `pwd` from ${template}. + echo Rebuilt configure in `pwd` + + if [ x$norecurse = x ] + then + while [ -n "$configdirs" ] + do + # set configdir to car of configdirs, configdirs to cdr of configdirs + set $configdirs; configdir=$1; shift; configdirs=$* + + if [ "`echo ${configdir}.*`" != "${configdir}.*" ] + then + targetspecificdirs=${configdir}.* + else + targetspecificdirs= + fi + + for i in ${configdir} ${targetspecificdirs} + do + if [ -r $i/configure ] + then + (cd $i ; + configure +template=${template}) + else + echo No configure script in `pwd`/$i + fi + done + done + fi + + exit 0 +fi + +# Temporarily, we support only direct subdir builds. +hostsubdir=Host-$host +targetsubdir=Target-$target + +if [ -n "$removing" ] +then + rm -rf $hostsubdir/$targetsubdir + + if [ -z "`(ls $hostsubdir) 2>&1 | grep Target-`" ] + then + rm -rf $hostsubdir + fi +else + if [ ! -d $hostsubdir ] ; then mkdir $hostsubdir ; fi + cd $hostsubdir + + if [ ! -d $targetsubdir ] ; then mkdir $targetsubdir ; fi + cd $targetsubdir + + srcdir=../.. + + ## Find the source files, if location was not specified. + #if [ x$srcdir = x ] + #then + # srcdirdefaulted=1 + # srcdir=. + # if [ ! -r ${srctrigger} ] + # then + # srcdir=.. + # fi + #fi + # + #if [ ! -r ${srcdir}/${srctrigger} ] + #then + # if [ x$srcdirdefaulted = x ] + # then + # echo "$progname: Can't find ${srcname} sources in \`${srcdir}'." 1>&2 + # else + # echo "$progname: Can't find ${srcname} sources in \`.' or \`..'." 1>&2 + # fi + # exit 1 + #fi + + + + # Set up the list of links to be made. + # $links is the list of link names, and $files is the list of names to link to. + + # Make the links. + while [ -n "$files" ] + do + # set file to car of files, files to cdr of files + set $files; file=$1; shift; files=$* + set $links; link=$1; shift; links=$* + + if [ ! -r ${srcdir}/config/$file ] + then + echo "$progname: cannot create a link \`$link'," 1>&2 + echo "since the file \`config/$file' does not exist." 1>&2 + exit 1 + fi + + $remove -f $link + rm -f config.status + # Make a symlink if possible, otherwise try a hard link + $symbolic_link ${srcdir}/config/$file $link 2>/dev/null || $hard_link ${srcdir}/config/$file $link + + if [ ! -r $link ] + then + echo "$progname: unable to link \`$link' to \`${srcdir}/config/$file'." 1>&2 + exit 1 + fi + echo "Linked \`$link' to \`${srcdir}/config/$file'." + done + + # Install a makefile, and make it set VPATH + # if necessary so that the sources are found. + # Also change its value of srcdir. + # Also create a .gdbinit file which runs the one in srcdir + # and tells GDB to look there for source files. + case $srcdir in + .) + ;; + *) + echo "VPATH = ${srcdir}" > x + cat x ${srcdir}/Makefile.in | sed "s@^srcdir = \.@srcdir = ${srcdir}@" > Makefile.in + rm x + echo "dir ." > .gdbinit + echo "dir ${srcdir}" >> .gdbinit + echo "source ${srcdir}/.gdbinit" >> .gdbinit + ;; + esac + + host_var_file=hmake-${host} + target_var_file=tmake-${target} + + # Conditionalize the makefile for this machine. + if [ -f ${srcdir}/config/${host_var_file} ] + then + sed -e "/^####/ r ${srcdir}/config/${host_var_file}" Makefile.in > Makefile.tem + else + cp Makefile.in Makefile.tem + fi + + if [ -f ${srcdir}/config/${target_var_file} ] + then + sed -e "/^####/ r ${srcdir}/config/${target_var_file}" Makefile.tem > Makefile.tem1 + mv Makefile.tem1 Makefile.tem + fi + + # Remove all formfeeds, since some Makes get confused by them. + sed "s///" Makefile.tem >> Makefile.tem1 + mv Makefile.tem1 Makefile.tem + + # reset SUBDIRS + sed "s:^SUBDIRS =.*$:SUBDIRS = ${configdirs}:" Makefile.tem > Makefile.tem1 + mv Makefile.tem1 Makefile.tem + + # reset NONSUBDIRS + sed "s:^NONSUBDIRS =.*$:NONSUBDIRS = ${noconfigdirs}:" Makefile.tem > Makefile.tem1 + mv Makefile.tem1 Makefile.tem + + # Delete the intermediate files + if [ x$srcdir != x. ] ; then rm Makefile.in ; fi + + rm -f Makefile + + # Define macro CROSS_COMPILE in compilation if this is a cross-compiler. + if [ x$host != x$target ] + then + echo "CROSS=-DCROSS_COMPILE" > Makefile + echo "ALL=start.encap" >> Makefile + else + echo "ALL=all.internal" > Makefile + fi + + # set target and host + echo "host = $host" >> Makefile + echo "target = $target" >> Makefile + + cat Makefile.tem >> Makefile + rm Makefile.tem + + using= + if [ -f ${srcdir}/config/${host_var_file} ] + then + using=" using \"${host_var_file}\"" + fi + + if [ -f ${srcdir}/config/${target_var_file} ] + then + if [ -z "${using}" ] + then + andusing=" using \"${target_var_file}\"" + else + andusing="${using} and \"${target_var_file}\"" + fi + else + andusing=${using} + fi + + echo "Created \"Makefile\""${andusing}. + + if [ x$host = x$target ] + then + echo "Links are now set up for use with a $target." \ + | tee ${srcdir}/config.status + else + echo "Links are now set up for host $host and target $target." \ + | tee ${srcdir}/config.status + fi + + cd ${srcdir} +fi + +# If there are subdirectories, then recurse. + +if [ x$norecurse != x ] ; then exit 0 ; fi + +while [ -n "$configdirs" ] +do + # set configdir to car of configdirs, configdirs to cdr of configdirs + set $configdirs; configdir=$1; shift; configdirs=$* + + # check for target override + targetspecificdir=${configdir}.${target} + if [ -d ${targetspecificdir} ] + then + configdir=${targetspecificdir} + fi + + echo Configuring ${configdir}... + (cd ${configdir} ; + configure +host=${host} ${target} ${removing}) \ + | sed 's/^/ /' +done + +exit 0 diff --git a/gas/configure.in b/gas/configure.in new file mode 100644 index 0000000..4da7737 --- /dev/null +++ b/gas/configure.in @@ -0,0 +1,31 @@ +# This file is a shell script that supplies the information necessary +# to tailor a template configure script into the configure script +# appropriate for this directory. For more information, check any +# existing configure script. + +srctrigger=as.c +srcname="gas" + +case $target in +*-coff) + obj_format=coff + ;; +*-bout) + obj_format=bout + ;; +*) + obj_format=aout + ;; +esac + +case $target in +vax) + atof=vax + ;; +*) + atof=ieee + ;; +esac + +files="ho-${host}.h tc-${cpu_type}.c tc-${cpu_type}.h te-generic.h obj-${obj_format}.h obj-${obj_format}.c atof-${atof}.c" +links="host.h targ-cpu.c targ-cpu.h targ-env.h obj-format.h obj-format.c atof-targ.c" diff --git a/gas/debug.c b/gas/debug.c new file mode 100644 index 0000000..c1e1dbe --- /dev/null +++ b/gas/debug.c @@ -0,0 +1,79 @@ +/* Routines for debug use only. Don't link into product. + */ + +#include "as.h" +#include "subsegs.h" + +dmp_frags() +{ + frchainS *chp; + char *p; + + for ( chp=frchain_root; chp; chp = chp->frch_next ){ + switch ( chp->frch_seg ){ + case SEG_DATA: + p ="Data"; + break; + case SEG_TEXT: + p ="Text"; + break; + default: + p ="???"; + break; + } + printf("\nSEGMENT %s %d\n", p, chp->frch_subseg); + dmp_frag( chp->frch_root,"\t"); + } +} + +dmp_frag( fp, indent ) + struct frag *fp; + char *indent; +{ + for ( ; fp; fp = fp->fr_next ){ + printf("%sFRAGMENT @ 0x%x\n", indent, fp); + switch( fp->fr_type ){ + case rs_align: + printf("%srs_align(%d)\n",indent, fp->fr_offset); + break; + case rs_fill: + printf("%srs_fill(%d)\n",indent, fp->fr_offset); + printf("%s", indent); + var_chars( fp, fp->fr_var + fp->fr_fix ); + printf("%s\t repeated %d times,", + indent, fp->fr_offset); + printf(" fixed length if # chars == 0)\n"); + break; + case rs_org: + printf("%srs_org(%d+sym @0x%x)\n",indent, + fp->fr_offset, fp->fr_symbol); + printf("%sfill with ",indent); + var_chars( fp, 1 ); + printf("\n"); + break; + case rs_machine_dependent: + printf("%smachine_dep\n",indent); + break; + default: + printf("%sunknown type\n",indent); + break; + } + printf("%saddr=%d(0x%x)\n",indent,fp->fr_address,fp->fr_address); + printf("%sfr_fix=%d\n",indent,fp->fr_fix); + printf("%sfr_var=%d\n",indent,fp->fr_var); + printf("%sfr_offset=%d\n",indent,fp->fr_offset); + printf("%schars @ 0x%x\n",indent,fp->fr_literal); + printf("\n"); + } +} + +var_chars( fp, n ) + struct frag *fp; + int n; +{ + unsigned char *p; + + for ( p=(unsigned char*)fp->fr_literal; n; n-- , p++ ){ + printf("%02x ", *p ); + } +} diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo new file mode 100644 index 0000000..ee3c3d2 --- /dev/null +++ b/gas/doc/as.texinfo @@ -0,0 +1,3227 @@ +\input texinfo @c -*-texinfo-*- +@tex +\special{twoside} +@end tex +@setfilename as +@settitle as +@titlepage +@center @titlefont{as} +@sp 1 +@center The GNU Assembler +@sp 2 +@center Dean Elsner, Jay Fenlason & friends +@sp 13 +The Free Software Foundation Inc. thanks The Nice Computer +Company of Australia for loaning Dean Elsner to write the +first (Vax) version of @code{as} for Project GNU. +The proprietors, management and staff of TNCCA thank FSF for +distracting the boss while they got some work +done. +@sp 3 + +Copyright @copyright{} 1986,1987 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the same conditions as for modified versions. + +@end titlepage +@node top, Syntax, top, top +@chapter Overview, Usage +@menu +* Syntax:: The (machine independent) syntax that assembly language + files must follow. The machine dependent syntax + can be found in the machine dependent section of + the manual for the machine that you are using. +* Segments:: How to use segments and subsegments, and how the + assembler and linker will relocate things. +* Symbols:: How to set up and manipulate symbols. +* Expressions:: And how the assembler deals with them. +* PseudoOps:: The assorted machine directives that tell the + assembler exactly what to do with its input. +* MachineDependent:: Information specific to each machine. +* Maintenance:: Keeping the assembler running. +* Retargeting:: Teaching the assembler about new machines. +@end menu + +This document describes the GNU assembler @code{as}. This document +does @emph{not} describe what an assembler does, or how it works. +This document also does @emph{not} describe the opcodes, registers +or addressing modes that @code{as} uses on any paticular computer +that @code{as} runs on. Consult a good book on assemblers or the +machine's architecture if you need that information. + +This document describes the directives that @code{as} understands, +and their syntax. This document also describes some of the +machine-dependent features of various flavors of the assembler. +This document also describes how the assembler works internally, and +provides some information that may be useful to people attempting to +port the assembler to another machine. + + +Throughout this document, we assume that you are running @dfn{GNU}, +the portable operating system from the @dfn{Free Software +Foundation, Inc.}. This restricts our attention to certain kinds of +computer (in paticular, the kinds of computers that GNU can run on); +once this assumption is granted examples and definitions need less +qualification. + +Readers should already comprehend: +@itemize @bullet +@item +Central processing unit +@item +registers +@item +memory address +@item +contents of memory address +@item +bit +@item +8-bit byte +@item +2's complement arithmetic +@end itemize + +@code{as} is part of a team of programs that turn a high-level +human-readable series of instructions into a low-level +computer-readable series of instructions. Different versions of +@code{as} are used for different kinds of computer. In paticular, +at the moment, @code{as} only works for the DEC Vax, the Motorola +680x0, the Intel 80386, the Sparc, and the National Semiconductor +32032/32532. + +@section Notation +GNU and @code{as} assume the computer that will run the programs it +assembles will obey these rules. + +A (memory) @dfn{address} is 32 bits. The lowest address is zero. + +The @dfn{contents} of any memory address is one @dfn{byte} of +exactly 8 bits. + +A @dfn{word} is 16 bits stored in two bytes of memory. The addresses +of the bytes differ by exactly 1. Notice that the interpretation of +the bits in a word and of how to address a word depends on which +particular computer you are assembling for. + +A @dfn{long word}, or @dfn{long}, is 32 bits composed of four bytes. +It is stored in 4 bytes of memory; these bytes have contiguous +addresses. Again the interpretation and addressing of those bits is +machine dependent. National Semiconductor 32x32 computers say +@i{double word} where we say @i{long}. + +Numeric quantities are usually @i{unsigned} or @i{2's complement}. +Bytes, words and longs may store numbers. @code{as} manipulates +integer expressions as 32-bit numbers in 2's complement format. +When asked to store an integer in a byte or word, the lowest order +bits are stored. The order of bytes in a word or long in memory is +determined by what kind of computer will run the assembled program. +We won't mention this important @i{caveat} again. + +The meaning of these terms has changed over time. Although @i{byte} +used to mean any length of contiguous bits, @i{byte} now pervasively +means exactly 8 contiguous bits. A @i{word} of 16 bits made sense +for 16-bit computers. Even on 32-bit computers, a @i{word} still +means 16 bits (to machine language programmers). To many other +programmers of GNU a @i{word} means 32 bits, so beware. Similarly +@i{long} means 32 bits: from ``long word''. National Semiconductor +32x32 machine language calls a 32-bit number a ``double word''. + +@example + + Names for integers of different sizes: some conventions + + +length as vax 32x32 680x0 GNU C +(bits) + + 8 byte byte byte byte char + 16 word word word word short (int) + 32 long long(-word) double-word long(-word) long (int) + 64 quad quad(-word) +128 octa octa-word + +@end example + +@section as, the GNU Assembler +@dfn{As} is an assembler; it is one of the team of programs that +`compile' your programs into the binary numbers that a computer uses +to `run' your program. Often @code{as} reads a @i{source} program +written by a compiler and writes an @dfn{object} program for the +linker (sometimes referred to as a @dfn{loader}) @code{ld} to read. + +The source program consists of @dfn{statements} and comments. Each +statement might @dfn{assemble} to one (and only one) machine +language instruction or to one very simple datum. + +Mostly you don't have to think about the assembler because the +compiler invokes it as needed; in that sense the assembler is just +another part of the compiler. If you write your own assembly +language program, then you must run the assembler yourself to get an +object file suitable for linking. You can read below how to do this. + +@code{as} is only intended to assemble the output of the C compiler +@code{cc} for use by the linker @code{ld}. @code{as} tries to +assemble correctly everything that the standard assembler would +assemble, with a few exceptions (described in the machine-dependent +chapters.) Note that this doesn't mean @code{as} will use the same +syntax as the standard assembler. For example, we know of several +incompatable syntaxes for the 680x0. + +Each version of the assembler knows about just one kind of machine +language, but much is common between the versions, including object +file formats, (most) assembler directives (often called +@dfn{pseudo-ops)} and assembler syntax. + +Unlike older assemblers, @code{as} tries to assemble a source program +in one pass of the source file. This subtly changes the meaning of +the @kbd{.org} directive (@xref{Org}.). + +If you want to write assembly language programs, you must tell +@code{as} what numbers should be in a computer's memory, and which +addresses should contain them, so that the program may be executed +by the computer. Using symbols will prevent many bookkeeping +mistakes that can occur if you use raw numbers. + +@section Command Line Synopsis +@example +as [ options @dots{} ] [ file1 @dots{} ] +@end example + +After the program name @code{as}, the command line may contain +options and file names. Options may be in any order, and may be +before, after, or between file names. The order of file names is +significant. + +@subsection Options + +Except for @samp{--} any command line argument that begins with a +hyphen (@samp{-}) is an option. Each option changes the behavior of +@code{as}. No option changes the way another option works. An +option is a @samp{-} followed by one ore more letters; the case of +the letter is important. No option (letter) should be used twice on +the same command line. (Nobody has decided what two copies of the +same option should mean.) All options are optional. + +Some options expect exactly one file name to follow them. The file +name may either immediately follow the option's letter (compatible +with older assemblers) or it may be the next command argument (GNU +standard). These two command lines are equivalent: + +@example +as -o my-object-file.o mumble +as -omy-object-file.o mumble +@end example + +Always, @file{--} (that's two hyphens, not one) by itself names the +standard input file. + +@section Input File(s) + +We use the words @dfn{source program}, abbreviated @dfn{source}, to +describe the program input to one run of @code{as}. The program may +be in one or more files; how the source is partitioned into files +doesn't change the meaning of the source. + +The source text is a catenation of the text in each file. + +Each time you run @code{as} it assembles exactly one source +program. A source program text is made of one or more files. +(The standard input is also a file.) + +You give @code{as} a command line that has zero or more input file +names. The input files are read (from left file name to right). A +command line argument (in any position) that has no special meaning +is taken to be an input file name. If @code{as} is given no file +names it attempts to read one input file from @code{as}'s standard +input. + +Use @file{--} if you need to explicitly name the standard input file +in your command line. + +It is OK to assemble an empty source. @code{as} will produce a +small, empty object file. + +If you try to assemble no files then @code{as} will try to read +standard input, which is normally your terminal. You may have to +type @key{ctl-D} to tell @code{as} there is no more program to +assemble. + +@subsection Input Filenames and Line-numbers +A line is text up to and including the next newline. The first line +of a file is numbered @b{1}, the next @b{2} and so on. + +There are two ways of locating a line in the input file(s) and both +are used in reporting error messages. One way refers to a line +number in a physical file; the other refers to a line number in a +logical file. + +@dfn{Physical files} are those files named in the command line given +to @code{as}. + +@dfn{Logical files} are ``pretend'' files which bear no relation to +physical files. Logical file names help error messages reflect the +proper source file. Often they are used when @code{as}' source is +itself synthesized from other files. + +@section Output (Object) File +Every time you run @code{as} it produces an output file, which is +your assembly language program translated into numbers. This file +is the object file; named @code{a.out} unless you tell @code{as} to +give it another name by using the @code{-o} option. Conventionally, +object file names end with @file{.o}. The default name of +@file{a.out} is used for historical reasons. Older assemblers were +capable of assembling self-contained programs directly into a +runnable program. This may still work, but hasn't been tested. + +The object file is for input to the linker @code{ld}. It contains +assembled program code, information to help @code{ld} to integrate +the assembled program into a runnable file and (optionally) symbolic +information for the debugger. The precise format of object files is +described elsewhere. + +@comment link above to some info file(s) like the description of a.out. +@comment don't forget to describe GNU info as well as Unix lossage. + +@section Error and Warning Messages + +@code{as} may write warnings and error messages to the standard +error file (usually your terminal). This should not happen when +@code{as} is run automatically by a compiler. Error messages are +useful for those (few) people who still write in assembly language. + +Warnings report an assumption made so that @code{as} could keep +assembling a flawed program. + +Errors report a grave problem that stops the assembly. + +Warning messages have the format +@example +file_name:line_number:Warning Message Text +@end example +If a logical file name has been given (@xref{File}.) it is used for +the filename, otherwise the name of the current input file is used. +If a logical line number was given (@xref{Line}.) then it is used to +calculate the number printed, otherwise the actual line in the +current source file is printed. The message text is intended to be +self explanatory (In the grand Unix tradition). + +Error messages have the format +@example +file_name:line_number:FATAL:Error Message Text +@end example +The file name and line number are derived the same as for warning +messages. The actual message text may be rather less explanatory +because many of them aren't supposed to happen. + +@section Options +@subsection -f Works Faster +@samp{-f} should only be used when assembling programs written by a +(trusted) compiler. @samp{-f} causes the assembler to not bother +pre-processing the input file(s) before assembling them. Needless +to say, if the files actually need to be pre-processed (if the +contain comments, for example), @code{as} will not work correctly if +@samp{-f} is used. + +@subsection -L Includes Local Labels +For historical reasons, labels beginning with @samp{L} (upper case +only) are called @dfn{local labels}. Normally you don't see such +labels because they are intended for the use of programs (like +compilers) that compose assembler programs, not for your notice. +Normally both @code{as} and @code{ld} discard such labels, so you +don't normally debug with them. + +This option tells @code{as} to retain those @samp{L@dots{}} symbols +in the object file. Usually if you do this you also tell the linker +@code{ld} to preserve symbols whose names begin with @samp{L}. + +@subsection -o Names the Object File +There is always one object file output when you run @code{as}. By +default it has the name @file{a.out}. You use this option (which +takes exactly one filename) to give the object file a different name. + +Whatever the object file is called, @code{as} will overwrite any +existing file of the same name. + +@subsection -R Folds Data Segment into Text Segment +@code{-R} tells @code{as} to write the object file as if all +data-segment data lives in the text segment. This is only done at +the very last moment: your binary data are the same, but data +segment parts are relocated differently. The data segment part of +your object file is zero bytes long because all it bytes are +appended to the text segment. (@xref{Segments}.) + +When you use @code{-R} it would be nice to generate shorter address +displacements (possible because we don't have to cross segments) +between text and data segment. We don't do this simply for +compatibility with older versions of @code{as}. @code{-R} may work +this way in future. + +@subsection -W Represses Warnings +@code{as} should never give a warning or error message when +assembling compiler output. But programs written by people often +cause @code{as} to give a warning that a particular assumption was +made. All such warnings are directed to the standard error file. +If you use this option, any warning is repressed. This option only +affects warning messages: it cannot change any detail of how +@code{as} assembles your file. Errors, which stop the assembly, are +still reported. + +@section Special Features to support Compilers + +In order to assemble compiler output into something that will work, +@code{as} will occasionlly do strange things to @samp{.word} +directives. In particular, when @code{gas} assembles a directive of +the form @samp{.word sym1-sym2}, and the difference between +@code{sym1} and @code{sym2} does not fit in 16 bits, @code{as} will +create a @dfn{secondary jump table}, immediately before the next +label. This @var{secondary jump table} will be preceeded by a +short-jump to the first byte after the table. The short-jump +prevents the flow-of-control from accidentally falling into the +table. Inside the table will be a long-jump to @code{sym2}. The +original @samp{.word} will contain @code{sym1} minus (the address of +the long-jump to sym2) If there were several @samp{.word sym1-sym2} +before the secondary jump table, all of them will be adjusted. If +ther was a @samp{.word sym3-sym4}, that also did not fit in sixteen +bits, a long-jump to @code{sym4} will be included in the secondary +jump table, and the @code{.word}(s), will be adjusted to contain +@code{sym3} minus (the address of the long-jump to sym4), etc. + +@emph{This feature may be disabled by compiling @code{as} with the +@samp{-DWORKING_DOT_WORD} option.} This feature is likely to confuse +assembly language programmers. + +@node Syntax, Segments, top, top +@chapter Syntax +This chapter informally defines the machine-independent syntax +allowed in a source file. @code{as} has ordinary syntax; it tries +to be upward compatible from BSD 4.2 assembler except @code{as} does +not assemble Vax bit-fields. + +@section The Pre-processor +The preprocess phase handles several aspects of the syntax. The +pre-processor will be disabled by the @samp{-f} option, or if the +first line of the source file is @code{#NO_APP}. The option to +disable the pre-processor was designed to make compiler output +assemble as fast as possible. + +The pre-processor adjusts and removes extra whitespace. It leaves +one space or tab before the keywords on a line, and turns any other +whitespace on the line into a single space. + +The pre-processor removes all comments, replacing them with a single +space (for /* @dots{} */ comments), or an appropriate number of +newlines. + +The pre-processor converts character constants into the appropriate +numeric values. + +This means that excess whitespace, comments, and character constants +cannot be used in the portions of the input text that are not +pre-processed. + +If the first line of an input file is @code{#NO_APP} or the +@samp{-f} option is given, the input file will not be +pre-processed. Within such an input file, parts of the file can be +pre-processed by putting a line that says @code{#APP} before the +text that should be pre-processed, and putting a line that says +@code{#NO_APP} after them. This feature is mainly intend to support +asm statements in compilers whose output normally does not need to +be pre-processed. + +@section Whitespace +@dfn{Whitespace} is one or more blanks or tabs, in any order. +Whitespace is used to separate symbols, and to make programs neater +for people to read. Unless within character constants +(@xref{Characters}.), any whitespace means the same as exactly one +space. + +@section Comments +There are two ways of rendering comments to @code{as}. In both +cases the comment is equivalent to one space. + +Anything from @samp{/*} through the next @samp{*/} is a comment. + +@example +/* + The only way to include a newline ('\n') in a comment + is to use this sort of comment. +*/ +/* This sort of comment does not nest. */ +@end example + +Anything from the @dfn{line comment} character to the next newline +considered a comment and is ignored. The line comment character is +@samp{#} on the Vax, and @samp{|} on the 680x0. +@xref{MachineDependent}. On some machines there are two different +line comment characters. One will only begin a comment if it is the +first non-whitespace character on a line, while the other will +always begin a comment. + +To be compatible with past assemblers a special interpretation is +given to lines that begin with @samp{#}. Following the @samp{#} an +absolute expression (@pxref{Expressions}) is expected: this will be +the logical line number of the @b{next} line. Then a string +(@xref{Strings}.) is allowed: if present it is a new logical file +name. The rest of the line, if any, should be whitespace. + +If the first non-whitespace characters on the line are not numeric, +the line is ignored. (Just like a comment.) +@example + # This is an ordinary comment. +# 42-6 "new_file_name" # New logical file name + # This is logical line # 36. +@end example +This feature is deprecated, and may disappear from future versions +of @code{as}. + +@section Symbols +A @dfn{symbol} is one or more characters chosen from the set of all +letters (both upper and lower case), digits and the three characters +@samp{_.$}. No symbol may begin with a digit. Case is +significant. There is no length limit: all characters are +significant. Symbols are delimited by characters not in that set, +or by begin/end-of-file. (@xref{Symbols}.) + +@section Statements +A @dfn{statement} ends at a newline character (@samp{\n}) or at a +semicolon (@samp{;}). The newline or semicolon is considered part +of the preceding statement. Newlines and semicolons within +character constants are an exception: they don't end statements. +It is an error to end any statement with end-of-file: the last +character of any input file should be a newline. + +You may write a statement on more than one line if you put a +backslash (@kbd{\}) immediately in front of any newlines within the +statement. When @code{as} reads a backslashed newline both +characters are ignored. You can even put backslashed newlines in +the middle of symbol names without changing the meaning of your +source program. + +An empty statement is OK, and may include whitespace. It is ignored. + +Statements begin with zero or more labels, followed by a @dfn{key +symbol} which determines what kind of statement it is. The key +symbol determines the syntax of the rest of the statement. If the +symbol begins with a dot (@t{.}) then the statement is an assembler +directive: typically valid for any computer. If the symbol begins +with a letter the statement is an assembly language +@dfn{instruction}: it will assemble into a machine language +instruction. Different versions of @code{as} for different +computers will recognize different instructions. In fact, the same +symbol may represent a different instruction in a different +computer's assembly language. + +A label is usually a symbol immediately followed by a colon +(@code{:}). Whitespace before a label or after a colon is OK. You +may not have whitespace between a label's symbol and its colon. +Labels are explained below. +@xref{Labels}. + +@example +label: .directive followed by something +another$label: # This is an empty statement. + instruction operand_1, operand_2, @dots{} +@end example + +@section Constants +A constant is a number, written so that its value is known by +inspection, without knowing any context. Like this: +@example +.byte 74, 0112, 092, 0x4A, 0X4a, 'J, '\J # All the same value. +.ascii "Ring the bell\7" # A string constant. +.octa 0x123456789abcdef0123456789ABCDEF0 # A bignum. +.float 0f-314159265358979323846264338327\ +95028841971.693993751E-40 # - pi, a flonum. +@end example + +@node Characters, Strings, , Syntax +@subsection Character Constants +There are two kinds of character constants. @dfn{Characters} stand +for one character in one byte and their values may be used in +numeric expressions. String constants (properly called string +@i{literals}) are potentially many bytes and their values may not be +used in arithmetic expressions. + +@node Strings, , Characters, Syntax +@subsubsection Strings +A @dfn{string} is written between double-quotes. It may contain +double-quotes or null characters. The way to get weird characters +into a string is to @dfn{escape} these characters: precede them with +a backslash (@code{\}) character. For example @samp{\\} represents +one backslash: the first @code{\} is an escape which tells +@code{as} to interpret the second character literally as a backslash +(which prevents @code{as} from recognizing the second @code{\} as an +escape character). The complete list of escapes follows. + +@table @kbd +@item \EOF +A @kbd{\} followed by end-of-file erroneous. It is treated just +like an end-of-file without a preceding backslash. +@c @item \a +@c Mnemonic for ACKnowledge; for ASCII this is octal code 007. +@item \b +Mnemonic for backspace; for ASCII this is octal code 010. +@c @item \e +@c Mnemonic for EOText; for ASCII this is octal code 004. +@item \f +Mnemonic for FormFeed; for ASCII this is octal code 014. +@item \n +Mnemonic for newline; for ASCII this is octal code 012. +@c @item \p +@c Mnemonic for prefix; for ASCII this is octal code 033, usually known as @code{escape}. +@item \r +Mnemonic for carriage-Return; for ASCII this is octal code 015. +@c @item \s +@c Mnemonic for space; for ASCII this is octal code 040. Included for compliance with +@c other assemblers. +@item \t +Mnemonic for horizontal Tab; for ASCII this is octal code 011. +@c @item \v +@c Mnemonic for Vertical tab; for ASCII this is octal code 013. +@c @item \x @var{digit} @var{digit} @var{digit} +@c A hexadecimal character code. The numeric code is 3 hexadecimal digits. +@item \ @var{digit} @var{digit} @var{digit} +An octal character code. The numeric code is 3 octal digits. +For compatibility with other Unix systems, 8 and 9 are legal digits +with values 010 and 011 respectively. +@item \\ +Represents one @samp{\} character. +@c @item \' +@c Represents one @samp{'} (accent acute) character. +@c This is needed in single character literals +@c (@xref{Characters}.) to represent +@c a @samp{'}. +@item \" +Represents one @samp{"} character. Needed in strings to represent +this character, because an unescaped @samp{"} would end the string. +@item \ @var{anything-else} +Any other character when escaped by @kbd{\} will give a warning, but +assemble as if the @samp{\} was not present. The idea is that if +you used an escape sequence you clearly didn't want the literal +interpretation of the following character. However @code{as} has no +other interpretation, so @code{as} knows it is giving you the wrong +code and warns you of the fact. +@end table + +Which characters are escapable, and what those escapes represent, +varies widely among assemblers. The current set is what we think +BSD 4.2 @code{as} recognizes, and is a subset of what most C +compilers recognize. If you are in doubt, don't use an escape +sequence. + +@subsubsection Characters +A single character may be written as a single quote immediately +followed by that character. The same escapes apply to characters as +to strings. So if you want to write the character backslash, you +must write @kbd{'\\} where the first @code{\} escapes the second +@code{\}. As you can see, the quote is an accent acute, not an +accent grave. A newline (or semicolon (@samp{;})) immediately +following an accent acute is taken as a literal character and does +not count as the end of a statement. The value of a character +constant in a numeric expression is the machine's byte-wide code for +that character. @code{as} assumes your character code is ASCII: @kbd{'A} +means 65, @kbd{'B} means 66, and so on. + +@subsection Number Constants +@code{as} distinguishes 3 flavors of numbers according to how they +are stored in the target machine. @i{Integers} are numbers that +would fit into an @code{int} in the C language. @i{Bignums} are +integers, but they are stored in a more than 32 bits. @i{Flonums} +are floating point numbers, described below. + +@subsubsection Integers +An octal integer is @samp{0} followed by zero or more of the octal +digits (@samp{01234567}). + +A decimal integer starts with a non-zero digit followed by zero or +more digits (@samp{0123456789}). + +A hexadecimal integer is @samp{0x} or @samp{0X} followed by one or +more hexadecimal digits chosen from @samp{0123456789abcdefABCDEF}. + +Integers have the obvious values. To denote a negative integer, use +the unary operator @samp{-} discussed under expressions +(@xref{Unops}.). + +@subsubsection Bignums +A @dfn{bignum} has the same syntax and semantics as an integer +except that the number (or its negative) takes more than 32 bits to +represent in binary. The distinction is made because in some places +integers are permitted while bignums are not. + +@subsubsection Flonums +A @dfn{flonum} represents a floating point number. The translation +is complex: a decimal floating point number from the text is +converted by @code{as} to a generic binary floating point number of +more than sufficient precision. This generic floating point number +is converted to the particular computer's floating point format(s) +by a portion of @code{as} specialized to that computer. + +A flonum is written by writing (in order) +@itemize @bullet +@item +The digit @samp{0}. +@item +A letter, to tell @code{as} the rest of the number is a flonum. +@kbd{e} +is recommended. Case is not important. +(Any otherwise illegal letter will work here, +but that might be changed. Vax BSD 4.2 assembler +seems to allow any of @samp{defghDEFGH}.) +@item +An optional sign: either @samp{+} or @samp{-}. +@item +An optional integer part: zero or more decimal digits. +@item +An optional fraction part: @samp{.} followed by zero +or more decimal digits. +@item +An optional exponent, consisting of: +@itemize @bullet +@item +A letter; the exact significance varies according to +the computer that executes the program. @code{as} +accepts any letter for now. Case is not important. +@item +Optional sign: either @samp{+} or @samp{-}. +@item +One or more decimal digits. +@end itemize +@end itemize + +At least one of @var{integer part} or @var{fraction part} must be +present. The floating point number has the obvious value. + +The computer running @code{as} needs no floating point hardware. +@code{as} does all processing using integers. + +@node Segments, Symbols, Syntax, top +@chapter (Sub)Segments & Relocation +Roughly, a @dfn{segment} is a range of addresses, with no gaps, with +all data ``in'' those addresses being treated the same. For example +there may be a ``read only'' segment. + +The linker @code{ld} reads many object files (partial programs) and +combines their contents to form a runnable program. When @code{as} +emits an object file, the partial program is assumed to start at +address 0. @code{ld} will assign the final addresses the partial +program occupies, so that different partial programs don't overlap. +That explanation is too simple, but it will suffice to explain how +@code{as} works. + +@code{ld} moves blocks of bytes of your program to their run-time +addresses. These blocks slide to their run-time addresses as rigid +units; their length does not change and neither does the order of +bytes within them. Such a rigid unit is called a @i{segment}. +Assigning run-time addresses to segments is called +@dfn{relocation}. It includes the task of adjusting mentions of +object-file addresses so they refer to the proper run-time addresses. + +An object file written by @code{as} has three segments, any of which +may be empty. These are named @i{text}, @i{data} and @i{bss} +segments. Within the object file, the text segment starts at +address 0, the data segment follows, and the bss segment follows the +data segment. + +To let @code{ld} know which data will change when the segments are +relocated, and how to change that data, @code{as} also writes to the +object file details of the relocation needed. To perform relocation +@code{ld} must know for each mention of an address in the object +file: +@itemize @bullet +@item +At what address in the object file does this mention of +an address begin? +@item +How long (in bytes) is this mention? +@item +Which segment does the address refer to? +What is the numeric value of (@var{address} @t{-} +@var{start-address of segment})? +@item +Is the mention of an address ``Program counter relative''? +@end itemize + +In fact, every address @code{as} ever thinks about is expressed as +(@var{segment} @t{+} @var{offset into segment}). Further, every +expression @code{as} computes is of this segmented nature. So +@dfn{absolute expression} means an expression with segment +``absolute'' (@xref{LdSegs}.). A @dfn{pass1 expression} means an +expression with segment ``pass1'' (@xref{MythSegs}.). In this +document ``(segment, offset)'' will be written as @{ segment-name +(offset into segment) @}. + +Apart from text, data and bss segments you need to know about the +@dfn{absolute} segment. When @code{ld} mixes partial programs, +addresses in the absolute segment remain unchanged. That is, +address @{absolute 0@} is ``relocated'' to run-time address 0 by +@code{ld}. Although two partial programs' data segments will not +overlap addresses after linking, @b{by definition} their absolute +segments will overlap. Address @{absolute 239@} in one partial +program will always be the same address when the program is running +as address @{absolute 239@} in any other partial program. + +The idea of segments is extended to the @dfn{undefined} segment. +Any address whose segment is unknown at assembly time is by +definition rendered @{undefined (something, unknown yet)@}. Since +numbers are always defined, the only way to generate an undefined +address is to mention an undefined symbol. A reference to a named +common block would be such a symbol: its value is unknown at assembly +time so it has segment @i{undefined}. + +By analogy the word @i{segment} is to describe groups of segments in +the linked program. @code{ld} puts all partial program's text +segments in contiguous addresses in the linked program. It is +customary to refer to the @i{text segment} of a program, meaning all +the addresses of all partial program's text segments. Likewise for +data and bss segments. + +@section Segments +Some segments are manipulated by @code{ld}; others are invented for +use of @code{as} and have no meaning except during assembly. + +@node LdSegs, , , +@subsection ld segments +@code{ld} deals with just 5 kinds of segments, summarized below. +@table @b +@item text segment +@itemx data segment +These segments hold your program bytes. @code{as} and @code{ld} +treat them as separate but equal segments. Anything you can say of +one segment is true of the other. When the program is running +however it is customary for the text segment to be unalterable: it +will contain instructions, constants and the like. The data segment +of a running program is usually alterable: for example, C variables +would be stored in the data segment. +@item bss segment +This segment contains zeroed bytes when your program begins +running. It is used to hold unitialized variables or common +storage. The length of each partial program's bss segment is +important, but because it starts out containing zeroed bytes there +is no need to store explicit zero bytes in the object file. The Bss +segment was invented to eliminate those explicit zeros from object +files. +@item absolute segment +Address 0 of this segment is always ``relocated'' to runtime address +0. This is useful if you want to refer to an address that @code{ld} +must not change when relocating. In this sense we speak of absolute +addresses being ``unrelocatable'': they don't change during +relocation. +@item undefined segment +This ``segment'' is a catch-all for address references to objects +not in the preceding segments. See the description of @file{a.out} +for details. +@end table +An idealized example of the 3 relocatable segments follows. Memory +addresses are on the horizontal axis. + +@example + +-----+----+--+ +partial program # 1: |ttttt|dddd|00| + +-----+----+--+ + + text data bss + seg. seg. seg. + + +---+---+---+ +partial program # 2: |TTT|DDD|000| + +---+---+---+ + + +--+---+-----+--+----+---+-----+~~ +linked program: | |TTT|ttttt| |dddd|DDD|00000| + +--+---+-----+--+----+---+-----+~~ + + addresses: 0 @dots{} +@end example + +@node MythSegs, , , +@subsection Mythical Segments +These segments are invented for the internal use of @code{as}. They +have no meaning at run-time. You don't need to know about these +segments except that they might be mentioned in @code{as}' warning +messages. These segments are invented to permit the value of every +expression in your assembly language program to be a segmented +address. + +@table @b +@item absent segment +An expression was expected and none was found. +@item goof segment +An internal assembler logic error has been found. This means there +is a bug in the assembler. +@item grand segment +A @dfn{grand number} is a bignum or a flonum, but not an integer. +If a number can't be written as a C @code{int} constant, it is a +grand number. @code{as} has to remember that a flonum or a bignum +does not fit into 32 bits, and cannot be a primary (@xref{Primary}.) +in an expression: this is done by making a flonum or bignum be of +type ``grand''. This is purely for internal @code{as} convenience; +grand segment behaves similarly to absolute segment. +@item pass1 segment +The expression was impossible to evaluate in the first pass. The +assembler will attempt a second pass (second reading of the source) +to evaluate the expression. Your expression mentioned an undefined +symbol in a way that defies the one-pass (segment + offset in +segment) assembly process. No compiler need emit such an expression. +@item difference segment +As an assist to the C compiler, expressions of the forms +@itemize @bullet +@item +(undefined symbol) @t{-} (expression) +@item +(something) @t{-} (undefined symbol) +@item +(undefined symbol) @t{-} (undefined symbol) +@end itemize +are permitted to belong to the ``difference'' segment. @code{as} +re-evaluates such expressions after the source file has been read +and the symbol table built. If by that time there are no undefined +symbols in the expression then the expression assumes a new segment. +The intention is to permit statements like @samp{.word label - +base_of_table} to be assembled in one pass where both @code{label} +and @code{base_of_table} are undefined. This is useful for +compiling C and Algol switch statements, Pascal case statements, +FORTRAN computed goto statements and the like. +@end table + +@section Sub-Segments +Assembled bytes fall into two segments: text and data. Because you +may have groups of text or data that you want to end up near to each +other in the object file, @code{as}, allows you to use +@dfn{subsegments}. Within each segment, there can be numbered +subsegments with values from 0 to 8192. Objects assembled into the +same subsegment will be grouped with other objects in the same +subsegment when they are all put into the object file. For example, +a compiler might want to store constants in the text segment, but +might not want to have them intersperced with the program being +assembled. In this case, the compiler could issue a @code{text 0} +before each section of code being output, and a @code{text 1} before +each group of constants being output. + +Subsegments are optional. If you don't used subsegments, everything +will be stored in subsegment number zero. + +Each subsegment is zero-padded up to a multiple of four bytes. +(Subsegments may be padded a different amount on different flavors +of @code{as}.) Subsegments appear in your object file in numeric +order, lowest numbered to highest. (All this to be compatible with +other people's assemblers.) The object file, @code{ld} @i{etc.} +have no concept of subsegments. They just see all your text +subsegments as a text segment, and all your data subsegments as a +data segment. + +To specify which subsegment you want subsequent statements assembled +into, use a @samp{.text @var{expression}} or a @samp{.data +@var{expression}} statement. @var{Expression} should be an absolute +expression. (@xref{Expressions}.) If you just say @samp{.text} +then @samp{.text 0} is assumed. Likewise @samp{.data} means +@samp{.data 0}. Assembly begins in @code{text 0}. +For instance: +@example +.text 0 # The default subsegment is text 0 anyway. +.ascii "This lives in the first text subsegment. *" +.text 1 +.ascii "But this lives in the second text subsegment." +.data 0 +.ascii "This lives in the data segment," +.ascii "in the first data subsegment." +.text 0 +.ascii "This lives in the first text segment," +.ascii "immediately following the asterisk (*)." +@end example + +Each segment has a @dfn{location counter} incremented by one for +every byte assembled into that segment. Because subsegments are +merely a convenience restricted to @code{as} there is no concept of +a subsegment location counter. There is no way to directly +manipulate a location counter. The location counter of the segment +that statements are being assembled into is said to be the +@dfn{active} location counter. + +@section Bss Segment +The @code{bss} segment is used for local common variable storage. +You may allocate address space in the @code{bss} segment, but you may +not dictate data to load into it before your program executes. When +your program starts running, all the contents of the @code{bss} +segment are zeroed bytes. + +Addresses in the bss segment are allocated with a special statement; +you may not assemble anything directly into the bss segment. Hence +there are no bss subsegments. + +@node Symbols, Expressions, Segments, top +@chapter Symbols +Because the linker uses symbols to link, the debugger uses symbols +to debug and the programmer uses symbols to name things, symbols are +a central concept. Symbols do not appear in the object file in the +order they are declared. This may break some debuggers. + +@node Labels, , , Symbols +@section Labels +A @dfn{label} is written as a symbol immediately followed by a colon +(@samp{:}). The symbol then represents the current value of the +active location counter, and is, for example, a suitable instruction +operand. You are warned if you use the same symbol to represent two +different locations: the first definition overrides any other +definitions. + +@section Giving Symbols Other Values +A symbol can be given an arbitrary value by writing a symbol followed +by an equals sign (@samp{=}) followed by an expression +(@pxref{Expressions}). This is equivalent to using the @code{.set} +directive. (@xref{Set}.) + +@section Symbol Names +Symbol names begin with a letter or with one of @samp{$._}. That +character may be followed by any string of digits, letters, +underscores and dollar signs. Case of letters is significant: +@code{foo} is a different symbol name than @code{Foo}. + +Each symbol has exactly one name. Each name in an assembly program +refers to exactly one symbol. You may use that symbol name any +number of times in an assembly program. + +@subsection Local Symbol Names + +Local symbols help compilers and programmers use names temporarily. +There are ten @dfn{local} symbol names, which are re-used throughout +the program. Their names are @samp{0} @samp{1} @dots{} @samp{9}. +To define a local symbol, write a label of the form +@var{digit}@t{:}. To refer to the most recent previous definition +of that symbol write @var{digit}@t{b}, using the same digit as when +you defined the label. To refer to the next definition of a local +label, write @var{digit}@t{f} where @var{digit} gives you a choice +of 10 forward references. The @samp{b} stands for ``backwards'' and +the @samp{f} stands for ``forwards''. + +Local symbols are not used by the current C compiler. + +There is no restriction on how you can use these labels, but +remember that at any point in the assembly you can refer to at most +10 prior local labels and to at most 10 forward local labels. + +Local symbol names are only a notation device. They are immediately +transformed into more conventional symbol names before the assembler +thinks about them. The symbol names stored in the symbol table, +appearing in error messages and optionally emitted to the object +file have these parts: +@table @kbd +@item L +All local labels begin with @samp{L}. Normally both @code{as} and +@code{ld} forget symbols that start with @samp{L}. These labels are +used for symbols you are never intended to see. If you give the +@samp{-L} option then @code{as} will retain these symbols in the +object file. By instructing @code{ld} to also retain these symbols, +you may use them in debugging. +@item @i{a digit} +If the label is written @samp{0:} then the digit is @samp{0}. +If the label is written @samp{1:} then the digit is @samp{1}. +And so on up through @samp{9:}. +@item @i{control}-A +This unusual character is included so you don't accidentally invent +a symbol of the same name. The character has ASCII value +@samp{\001}. +@item @i{an ordinal number} +This is like a serial number to keep the labels distinct. The first +@samp{0:} gets the number @samp{1}; The 15th @samp{0:} gets the +number @samp{15}; @i{etc.}. Likewise for the other labels @samp{1:} +through @samp{9:}. +@end table +For instance, the +first @code{1:} is named @code{L1^A1}, the 44th @code{3:} is named @code{L3^A44}. + +@section The Special Dot Symbol + +The special symbol @code{.} refers to the current address that +@code{as} is assembling into. Thus, the expression @samp{melvin: +.long .} will cause @var{melvin} to contain its own address. +Assigning a value to @code{.} is treated the same as a @code{.org} +directive. Thus, the expression @samp{.=.+4} is the same as saying +@samp{.space 4}. + +@section Symbol Attributes +Every symbol has the attributes discussed below. The detailed +definitions are in <a.out.h>. + +If you use a symbol without defining it, @code{as} assumes zero for +all these attributes, and probably won't warn you. This makes the +symbol an externally defined symbol, which is generally what you +would want. + +@subsection Value +The value of a symbol is (usually) 32 bits, the size of one C +@code{int}. For a symbol which labels a location in the +@code{text}, @code{data}, @code{bss} or @code{Absolute} segments the +value is the number of addresses from the start of that segment to +the label. Naturally for @code{text} @code{data} and @code{bss} +segments the value of a symbol changes as @code{ld} changes segment +base addresses during linking. @code{absolute} symbols' values do +not change during linking: that is why they are called absolute. + +The value of an undefined symbol is treated in a special way. If it +is 0 then the symbol is not defined in this assembler source +program, and @code{ld} will try to determine its value from other +programs it is linked with. You make this kind of symbol simply by +mentioning a symbol name without defining it. A non-zero value +represents a @code{.comm} common declaration. The value is how much +common storage to reserve, in bytes (@i{i.e.} addresses). The +symbol refers to the first address of the allocated storage. + +@subsection Type +The type attribute of a symbol is 8 bits encoded in a devious way. +We kept this coding standard for compatibility with older operating +systems. + +@example + + 7 6 5 4 3 2 1 0 bit numbers + +-----+-----+-----+-----+-----+-----+-----+-----+ + | | | | + | N_STAB bits | N_TYPE bits |N_EXT| + | | | bit | + +-----+-----+-----+-----+-----+-----+-----+-----+ + + n_type byte +@end example + +@subsubsection N_EXT bit +This bit is set if @code{ld} might need to use the symbol's value +and type bits. If this bit is re-set then @code{ld} can ignore the +symbol while linking. It is set in two cases. If the symbol is +undefined, then @code{ld} is expected to find the symbol's value +elsewhere in another program module. Otherwise the symbol has the +value given, but this symbol name and value are revealed to any other +programs linked in the same executable program. This second use of +the @code{N_EXT} bit is most often done by a @code{.globl} statement. + +@subsubsection N_TYPE bits +These establish the symbol's ``type'', which is mainly a relocation +concept. Common values are detailed in the manual describing the +executable file format. + +@subsubsection N_STAB bits +Common values for these bits are described in the manual on the +executable file format. + +@subsection Desc(riptor) +This is an arbitrary 16-bit value. You may establish a symbol's +descriptor value by using a @code{.desc} statement (@xref{Desc}.). +A descriptor value means nothing to @code{as}. + +@subsection Other +This is an arbitrary 8-bit value. It means nothing to @code{as}. + +@node Expressions, PseudoOps, Symbols, top +@chapter Expressions +An @dfn{expression} specifies an address or numeric value. +Whitespace may precede and/or follow an expression. + +@section Empty Expressions +An empty expression has no operands: it is just whitespace or null. +Wherever an absolute expression is required, you may omit the +expression and @code{as} will assume a value of (absolute) 0. This +is compatible with other assemblers. + +@section Integer Expressions +An @dfn{integer expression} is one or more @i{primaries} delimited +by @i{operators}. + +@node Primary, Unops, , Expressions +@subsection Primaries +@dfn{Primaries} are symbols, numbers or subexpressions. Other +languages might call primaries ``arithmetic operands'' but we don't +want them confused with ``instruction operands'' of the machine +language so we give them a different name. + +Symbols are evaluated to yield @{@var{segment} @var{value}@} where +@var{segment} is one of @b{text}, @b{data}, @b{bss}, @b{absolute}, +or @b{undefined}. @var{value} is a signed 2's complement 32 bit +integer. + +Numbers are usually integers. + +A number can be a flonum or bignum. In this case, you are warned +that only the low order 32 bits are used, and @code{as} pretends +these 32 bits are an integer. You may write integer-manipulating +instructions that act on exotic constants, compatible with other +assemblers. + +Subexpressions are a left parenthesis (@t{(}) followed by an integer +expression followed by a right parenthesis (@t{)}), or a unary +operator followed by an primary. + +@subsection Operators +@dfn{Operators} are arithmetic marks, like @t{+} or @t{%}. Unary +operators are followed by an primary. Binary operators appear +between primaries. Operators may be preceded and/or followed by +whitespace. + +@subsection Unary Operators +@node Unops, , Primary, Expressions +@code{as} has the following @dfn{unary operators}. They each take +one primary, which must be absolute. +@table @t +@item - +Hyphen. @dfn{Negation}. Two's complement negation. +@item ~ +Tilde. @dfn{Complementation}. Bitwise not. +@end table + +@subsection Binary Operators +@dfn{Binary operators} are infix. Operators are prioritized, but +equal priority operators are performed left to right. Apart from +@samp{+} or @samp{-}, both primaries must be absolute, and the +result is absolute, else one primary can be either undefined or +pass1 and the result is pass1. +@enumerate +@item +Highest Priority +@table @code +@item * +@dfn{Multiplication}. +@item / +@dfn{Division}. Truncation is the same as the C operator @samp{/} +of the compiler that compiled @code{as}. +@item % +@dfn{Remainder}. +@item < +@itemx << +@dfn{Shift Left}. Same as the C operator @samp{<<} of +the compiler that compiled @code{as}. +@item > +@itemx >> +@dfn{Shift Right}. Same as the C operator @samp{>>} of +the compiler that compiled @code{as}. +@end table +@item +Intermediate priority +@table @t +@item | +@dfn{Bitwise Inclusive Or}. +@item & +@dfn{Bitwise And}. +@item ^ +@dfn{Bitwise Exclusive Or}. +@item ! +@dfn{Bitwise Or Not}. +@end table +@item +Lowest Priority +@table @t +@item + +@dfn{Addition}. If either primary is absolute, the result +has the segment of the other primary. +If either primary is pass1 or undefined, result is pass1. +Otherwise @t{+} is illegal. +@item - +@dfn{Subtraction}. If the right primary is absolute, the +result has the segment of the left primary. +If either primary is pass1 the result is pass1. +If either primary is undefined the result is difference segment. +If both primaries are in the same segment, the result is absolute; provided +that segment is one of text, data or bss. +Otherwise @t{-} is illegal. +@end table +@end enumerate + +The sense of the rules is that you can't add or subtract quantities +from two different segments. If both primaries are in one of these +segments, they must be in the same segment: @b{text}, @b{data} or +@b{bss}, and the operator must be @samp{-}. + +@node PseudoOps, MachineDependent, Expressions, top +@chapter Assembler Directives +@menu +* Abort:: The Abort directive causes as to abort +* Align:: Pad the location counter to a power of 2 +* Ascii:: Fill memory with bytes of ASCII characters +* Asciz:: Fill memory with bytes of ASCII characters followed + by a null. +* Byte:: Fill memory with 8-bit integers +* Comm:: Reserve public space in the BSS segment +* Data:: Change to the data segment +* Desc:: Set the n_desc of a symbol +* Double:: Fill memory with double-precision floating-point numbers +* File:: Set the logical file name +* Fill:: Fill memory with repeated values +* Float:: Fill memory with single-precision floating-point numbers +* Global:: Make a symbol visible to the linker +* Int:: Fill memory with 32-bit integers +* Lcomm:: Reserve private space in the BSS segment +* Line:: Set the logical line number +* Long:: Fill memory with 32-bit integers +* Lsym:: Create a local symbol +* Octa:: Fill memory with 128-bit integers +* Org:: Change the location counter +* Quad:: Fill memory with 64-bit integers +* Set:: Set the value of a symbol +* Short:: Fill memory with 16-bit integers +* Space:: Fill memory with a repeated value +* Stab:: Store debugging information +* Text:: Change to the text segment +* Word:: Fill memory with 16-bit integers +@end menu + +All assembler directives begin with a symbol that begins with a +period (@samp{.}). The rest of the symbol is letters: their case +does not matter. + +@node Abort, Align, PseudoOps, PseudoOps +@section .abort +This directive stops the assembly immediately. It is for +compatibility with other assemblers. The original idea was that the +assembler program would be piped into the assembler. If the source +of program wanted to quit, then this directive tells @code{as} to +quit also. One day @code{.abort} will not be supported. + +@node Align, Ascii, Abort, PseudoOps +@section .align @var{absolute-expression} , @var{absolute-expression} +Pad the location counter (in the current subsegment) to a word, +longword or whatever boundary. The first expression is the number +of low-order zero bits the location counter will have after +advancement. For example @samp{.align 3} will advance the location +counter until it a multiple of 8. If the location counter is +already a multiple of 8, no change is needed. + +The second expression gives the value to be stored in the padding +bytes. It (and the comma) may be omitted. If it is omitted, the +padding bytes are zeroed. + +@node Ascii, Asciz, Align, PseudoOps +@section .ascii @var{strings} +This expects zero or more string literals (@xref{Strings}.) +separated by commas. It assembles each string (with no automatic +trailing zero byte) into consecutive addresses. + +@node Asciz, Byte, Ascii, PseudoOps +@section .asciz @var{strings} +This is just like .ascii, but each string is followed by a zero byte. +The `z' in `.asciz' stands for `zero'. + +@node Byte, Comm, Asciz, PseudoOps +@section .byte @var{expressions} + +This expects zero or more expressions, separated by commas. +Each expression is assembled into the next byte. + +@node Comm, Data, Byte, PseudoOps +@section .comm @var{symbol} , @var{length} +This declares a named common area in the bss segment. Normally +@code{ld} reserves memory addresses for it during linking, so no +partial program defines the location of the symbol. Tell @code{ld} +that it must be at least @var{length} bytes long. @code{ld} will +allocate space that is at least as long as the longest @code{.comm} +request in any of the partial programs linked. @var{length} is an +absolute expression. + +@node Data, Desc, Comm, PseudoOps +@section .data @var{subsegment} +This tells @code{as} to assemble the following statements onto the +end of the data subsegment numbered @var{subsegment} (which is an +absolute expression). If @var{subsegment} is omitted, it defaults +to zero. + +@node Desc, Double, Data, PseudoOps +@section .desc @var{symbol}, @var{absolute-expression} +This sets @code{n_desc} of the symbol to the low 16 bits of +@var{absolute-expression}. + +@node Double, File, Desc, PseudoOps +@section .double @var{flonums} +This expects zero or more flonums, separated by commas. It assembles +floating point numbers. The exact kind of floating point numbers +emitted depends on what computer @code{as} is assembling for. See +the machine-specific part of the manual for the machine the +assembler is running on for more information. + +@node File, Fill, Double, PseudoOps +@section .file @var{string} +This tells @code{as} that we are about to start a new logical +file. @var{String} is the new file name. An empty file name +is OK, but you must still give the quotes: @code{""}. This +statement may go away in future: it is only recognized to +be compatible with old @code{as} programs. + +@node Fill, Float, File, PseudoOps +@section .fill @var{repeat} , @var{size} , @var{value} +@var{result}, @var{size} and @var{value} are absolute expressions. +This emits @var{repeat} copies of @var{size} bytes. @var{Repeat} +may be zero or more. @var{Size} may be zero or more, but if it is +more than 8, then it is deemed to have the value 8, compatible with +other people's assemblers. The contents of each @var{repeat} bytes +is taken from an 8-byte number. The highest order 4 bytes are +zero. The lowest order 4 bytes are @var{value} rendered in the +byte-order of an integer on the computer @code{as} is assembling for. +Each @var{size} bytes in a repetition is taken from the lowest order +@var{size} bytes of this number. Again, this bizarre behavior is +compatible with other people's assemblers. + +@var{Size} and @var{value} are optional. +If the second comma and @var{value} are absent, @var{value} is +assumed zero. If the first comma and following tokens are absent, +@var{size} is assumed to be 1. + +@node Float, Global, Fill, PseudoOps +@section .float @var{flonums} +This directive assembles zero or more flonums, separated by commas. +The exact kind of floating point numbers emitted depends on what +computer @code{as} is assembling for. See the machine-specific part +of the manual for the machine the assembler is running on for more +information. + +@node Global, Int, Float, PseudoOps +@section .global @var{symbol} +This makes the symbol visible to @code{ld}. If you define +@var{symbol} in your partial program, its value is made available to +other partial programs that are linked with it. Otherwise, +@var{symbol} will take its attributes from a symbol of the same name +from another partial program it is linked with. + +This is done by setting the @code{N_EXT} bit +of that symbol's @code{n_type} to 1. + +@node Int, Lcomm, Global, PseudoOps +@section .int @var{expressions} +Expect zero or more @var{expressions}, of any segment, separated by +commas. For each expression, emit a 32-bit number that will, at run +time, be the value of that expression. The byte order of the +expression depends on what kind of computer will run the program. + +@node Lcomm, Line, Int, PseudoOps +@section .lcomm @var{symbol} , @var{length} +Reserve @var{length} (an absolute expression) bytes for a local +common and denoted by @var{symbol}, whose segment and value are +those of the new local common. The addresses are allocated in the +@code{bss} segment, so at run-time the bytes will start off zeroed. +@var{Symbol} is not declared global (@xref{Global}.), so is normally +not visible to @code{ld}. + +@node Line, Long, Lcomm, PseudoOps +@section .line @var{logical line number} +This tells @code{as} to change the logical line number. +@var{logical line number} is an absolute expression. The next line +will have that logical line number. So any other statements on the +current line (after a @code{;}) will be reported as on logical line +number @var{logical line number} - 1. One day this directive will +be unsupported: it is used only for compatibility with existing +assembler programs. + +@node Long, Lsym, Line, PseudoOps +@section .long @var{expressions} +This is the same as @samp{.int}, @pxref{Int}. + +@node Lsym, Octa, Long, PseudoOps +@section .lsym @var{symbol}, @var{expression} +This creates a new symbol named @var{symbol}, but do not put it in +the hash table, ensuring it cannot be referenced by name during the +rest of the assembly. This sets the attributes of the symbol to be +the same as the expression value. @code{n_other} = @code{n_desc} = +0. @code{n_type} = (whatever segment the expression has); the +@code{N_EXT} bit of @code{n_type} is zero. @code{n_value} = +(expression's value). + +@node Octa, Org, Lsym, PseudoOps +@section .octa @var{bignums} +This expects zero or more bignums, separated by commas. For each +bignum, it emits an 16-byte (@b{octa}-word) integer. + +@node Org, Quad, Octa, PseudoOps +@section .org @var{new-lc} , @var{fill} +This will advance the location counter of the current segment to +@var{new-lc}. @var{new-lc} is either an absolute expression or an +expression with the same segment as the current subsegment. That +is, you can't use @code{.org} to cross segments. Because @code{as} +tries to assemble programs in one pass @var{new-lc} must be defined. +If you really detest this restriction we eagerly await a chance to +share your improved assembler. To be compatible with former +assemblers, if the segment of @var{new-lc} is absolute then we +pretend the segment of @var{new-lc} is the same as the current +subsegment. + +Beware that the origin is relative to the start of the segment, not +to the start of the subsegment. This is compatible with other +people's assemblers. + +If the location counter (of the current subsegment) is advanced, the +intervening bytes are filled with @var{fill} which should be an +absolute expression. If the comma and @var{fill} are omitted, +@var{fill} defaults to zero. + +@node Quad, Set, Org, PseudoOps +@section .quad @var{bignums} +This expects zero or more bignums, separated by commas. For each +bignum, it emits an 8-byte (@b{quad}-word) integer. If the bignum +won't fit in a quad-word, it prints a warning message; and just +takes the lowest order 8 bytes of the bignum. + +@node Set, Short, Quad, PseudoOps +@section .set @var{symbol}, @var{expression} + +This sets the value of @var{symbol} to expression. This will change +@code{n_value} and @code{n_type} to conform to the @var{expression}. +if @code{n_ext} is set, it remains set. + +It is OK to @code{.set} a symbol many times in the same assembly. +If the expression's segment is unknowable during pass 1, a second +pass over the source program will be forced. The second pass is +currently not implemented. @code{as} will abort with an error +message if one is required. + +If you @code{.set} a global symbol, the value stored in the object +file is the last value stored into it. + +@node Short, Space, Set, PseudoOps +@section .short @var{expressions} +Except on the Sparc this is the same as @samp{.word}. @xref{Word}. +On the sparc, this expects zero or more @var{expressions}, and emits +a 16 bit number for each. + +@node Space, Stab, Short, PseudoOps +@section .space @var{size} , @var{fill} +This emits @var{size} bytes, each of value @var{fill}. Both +@var{size} and @var{fill} are absolute expressions. If the comma +and @var{fill} are omitted, @var{fill} is assumed to be zero. + +@node Stab, Text, Space, PseudoOps +@section .stabd, .stabn, .stabs +There are three directives that begin @code{.stab@dots{}}. +All emit symbols, for use by symbolic debuggers. +The symbols are not entered in @code{as}' hash table: they +cannot be referenced elsewhere in the source file. +Up to five fields are required: +@table @var +@item string +This is the symbol's name. It may contain any character except @samp{\000}, +so is more general than ordinary symbol names. Some debuggers used to +code arbitrarily complex structures into symbol names using this technique. +@item type +An absolute expression. The symbol's @code{n_type} is set to the low 8 +bits of this expression. +Any bit pattern is permitted, but @code{ld} and debuggers will choke on +silly bit patterns. +@item other +An absolute expression. +The symbol's @code{n_other} is set to the low 8 bits of this expression. +@item desc +An absolute expression. +The symbol's @code{n_desc} is set to the low 16 bits of this expression. +@item value +An absolute expression which becomes the symbol's @code{n_value}. +@end table + +If a warning is detected while reading the @code{.stab@dots{}} +statement the symbol has probably already been created and you will +get a half-formed symbol in your object file. This is compatible +with earlier assemblers (!) + +.stabd @var{type} , @var{other} , @var{desc} + +The ``name'' of the symbol generated is not even an empty string. +It is a null pointer, for compatibility. Older assemblers used a +null pointer so they didn't waste space in object files with empty +strings. + +The symbol's @code{n_value} is set to the location counter, +relocatably. When your program is linked, the value of this symbol +will be where the location counter was when the @code{.stabd} was +assembled. + +.stabn @var{type} , @var{other} , @var{desc} , @var{value} + +The name of the symbol is set to the empty string @code{""}. + +.stabs @var{string} , @var{type} , @var{other} , @var{desc} , @var{value} + +@node Text, Word, Stab, PseudoOps +@section .text @var{subsegment} +Tells @code{as} to assemble the following statements onto the end of +the text subsegment numbered @var{subsegment}, which is an absolute +expression. If @var{subsegment} is omitted, subsegment number zero +is used. + +@node Word, , Text, PseudoOps +@section .word @var{expressions} +On the Sparc, this produces 32-bit numbers instead of 16-bit ones. +This expect zero or more @var{expressions}, of any segment, +separated by commas. For each expression, emit a 16-bit number that +will, at run time, be the value of that expression. The byte order +of the expression depends on what kind of computer will run the +program. + +@section Deprecated Directives +One day these directives won't work. +They are included for compatibility with older assemblers. +@table @t +@item .abort +@item .file +@item .line +@end table + +@node MachineDependent, Maintenance, PseudoOps, top +@chapter Machine Dependent Features +@section Vax +@subsection Options + +The Vax version of @code{as} accepts any of the following options, +gives a warning message that the option was ignored and proceeds. +These options are for compatibility with scripts designed for other +people's assemblers. + +@table @asis +@item @kbd{-D} (Debug) +@itemx @kbd{-S} (Symbol Table) +@itemx @kbd{-T} (Token Trace) +These are obsolete options used to debug old assemblers. + +@item @kbd{-d} (Displacement size for JUMPs) +This option expects a number following the @kbd{-d}. Like options +that expect filenames, the number may immediately follow the +@kbd{-d} (old standard) or constitute the whole of the command line +argument that follows @kbd{-d} (GNU standard). + +@item @kbd{-V} (Virtualize Interpass Temporary File) +Some other assemblers use a temporary file. This option +commanded them to keep the information in active memory rather +than in a disk file. @code{as} always does this, so this +option is redundant. + +@item @kbd{-J} (JUMPify Longer Branches) +Many 32-bit computers permit a variety of branch instructions +to do the same job. Some of these instructions are short (and +fast) but have a limited range; others are long (and slow) but +can branch anywhere in virtual memory. Often there are 3 +flavors of branch: short, medium and long. Some other +assemblers would emit short and medium branches, unless told by +this option to emit short and long branches. + +@item @kbd{-t} (Temporary File Directory) +Some other assemblers may use a temporary file, and this option +takes a filename being the directory to site the temporary +file. @code{as} does not use a temporary disk file, so this +option makes no difference. @kbd{-t} needs exactly one +filename. +@end table + +The Vax version of the assembler accepts two options when +compiled for VMS. They are @kbd{-h}, and @kbd{-+}. The +@kbd{-h} option prevents @code{as} from modifying the +symbol-table entries for symbols that contain lowercase +characters (I think). The @kbd{-+} option causes @code{as} to +print warning messages if the FILENAME part of the object file, +or any symbol name is larger than 31 characters. The @kbd{-+} +option also insertes some code following the @samp{_main} +symbol so that the object file will be compatable with Vax-11 +"C". + +@subsection Floating Point +Conversion of flonums to floating point is correct, and +compatible with previous assemblers. Rounding is +towards zero if the remainder is exactly half the least significant bit. + +@code{D}, @code{F}, @code{G} and @code{H} floating point formats +are understood. + +Immediate floating literals (@i{e.g.} @samp{S`$6.9}) +are rendered correctly. Again, rounding is towards zero in the +boundary case. + +The @code{.float} directive produces @code{f} format numbers. +The @code{.double} directive produces @code{d} format numbers. + +@subsection Machine Directives +The Vax version of the assembler supports four directives for +generating Vax floating point constants. They are described in the +table below. + +@table @code +@item .dfloat +This expects zero or more flonums, separated by commas, and +assembles Vax @code{d} format 64-bit floating point constants. + +@item .ffloat +This expects zero or more flonums, separated by commas, and +assembles Vax @code{f} format 32-bit floating point constants. + +@item .gfloat +This expects zero or more flonums, separated by commas, and +assembles Vax @code{g} format 64-bit floating point constants. + +@item .hfloat +This expects zero or more flonums, separated by commas, and +assembles Vax @code{h} format 128-bit floating point constants. + +@end table + +@subsection Opcodes +All DEC mnemonics are supported. Beware that @code{case@dots{}} +instructions have exactly 3 operands. The dispatch table that +follows the @code{case@dots{}} instruction should be made with +@code{.word} statements. This is compatible with all unix +assemblers we know of. + +@subsection Branch Improvement +Certain pseudo opcodes are permitted. They are for branch +instructions. They expand to the shortest branch instruction that +will reach the target. Generally these mnemonics are made by +substituting @samp{j} for @samp{b} at the start of a DEC mnemonic. +This feature is included both for compatibility and to help +compilers. If you don't need this feature, don't use these +opcodes. Here are the mnemonics, and the code they can expand into. + +@table @code +@item jbsb +@samp{Jsb} is already an instruction mnemonic, so we chose @samp{jbsb}. +@table @asis +@item (byte displacement) +@kbd{bsbb @dots{}} +@item (word displacement) +@kbd{bsbw @dots{}} +@item (long displacement) +@kbd{jsb @dots{}} +@end table +@item jbr +@itemx jr +Unconditional branch. +@table @asis +@item (byte displacement) +@kbd{brb @dots{}} +@item (word displacement) +@kbd{brw @dots{}} +@item (long displacement) +@kbd{jmp @dots{}} +@end table +@item j@var{COND} +@var{COND} may be any one of the conditional branches +@code{neq nequ eql eqlu gtr geq lss gtru lequ vc vs gequ cc lssu cs}. +@var{COND} may also be one of the bit tests +@code{bs bc bss bcs bsc bcc bssi bcci lbs lbc}. +@var{NOTCOND} is the opposite condition to @var{COND}. +@table @asis +@item (byte displacement) +@kbd{b@var{COND} @dots{}} +@item (word displacement) +@kbd{b@var{UNCOND} foo ; brw @dots{} ; foo:} +@item (long displacement) +@kbd{b@var{UNCOND} foo ; jmp @dots{} ; foo:} +@end table +@item jacb@var{X} +@var{X} may be one of @code{b d f g h l w}. +@table @asis +@item (word displacement) +@kbd{@var{OPCODE} @dots{}} +@item (long displacement) +@kbd{@var{OPCODE} @dots{}, foo ; brb bar ; foo: jmp @dots{} ; bar:} +@end table +@item jaob@var{YYY} +@var{YYY} may be one of @code{lss leq}. +@item jsob@var{ZZZ} +@var{ZZZ} may be one of @code{geq gtr}. +@table @asis +@item (byte displacement) +@kbd{@var{OPCODE} @dots{}} +@item (word displacement) +@kbd{@var{OPCODE} @dots{}, foo ; brb bar ; foo: brw @var{destination} ; bar:} +@item (long displacement) +@kbd{@var{OPCODE} @dots{}, foo ; brb bar ; foo: jmp @var{destination} ; bar: } +@end table +@item aobleq +@itemx aoblss +@itemx sobgeq +@itemx sobgtr +@table @asis +@item (byte displacement) +@kbd{@var{OPCODE} @dots{}} +@item (word displacement) +@kbd{@var{OPCODE} @dots{}, foo ; brb bar ; foo: brw @var{destination} ; bar:} +@item (long displacement) +@kbd{@var{OPCODE} @dots{}, foo ; brb bar ; foo: jmp @var{destination} ; bar:} +@end table +@end table + +@subsection operands +The immediate character is @samp{$} for Unix compatibility, not +@samp{#} as DEC writes it. + +The indirect character is @samp{*} for Unix compatibility, not +@samp{@@} as DEC writes it. + +The displacement sizing character is @samp{`} (an accent grave) for +Unix compatibility, not @samp{^} as DEC writes it. The letter +preceding @samp{`} may have either case. @samp{G} is not +understood, but all other letters (@code{b i l s w}) are understood. + +Register names understood are @code{r0 r1 r2 @dots{} r15 ap fp sp +pc}. Any case of letters will do. + +For instance +@example +tstb *w`$4(r5) +@end example + +Any expression is permitted in an operand. Operands are comma +separated. + +@c There is some bug to do with recognizing expressions +@c in operands, but I forget what it is. It is +@c a syntax clash because () is used as an address mode +@c and to encapsulate sub-expressions. +@subsection Not Supported +Vax bit fields can not be assembled with @code{as}. Someone +can add the required code if they really need it. + +@section 680x0 +@subsection Options +The 680x0 version of @code{as} has two machine dependent options. +One shortens undefined references from 32 to 16 bits, while the +other is used to tell @code{as} what kind of machine it is +assembling for. + +You can use the @kbd{-l} option to shorten the size of references to +undefined symbols. If the @kbd{-l} option is not given, references +to undefined symbols will be a full long (32 bits) wide. (Since +@code{as} cannot know where these symbols will end up being, +@code{as} can only allocate space for the linker to fill in later. +Since @code{as} doesn't know how far away these symbols will be, it +allocates as much space as it can.) If this option is given, the +references will only be one word wide (16 bits). This may be useful +if you want the object file to be as small as possible, and you know +that the relevant symbols will be less than 17 bits away. + +The 680x0 version of @code{as} is usually used to assemble programs +for the Motorola MC68020 microprocessor. Occasionally it is used to +assemble programs for the mostly-similar-but-slightly-different +MC68000 or MC68010 microprocessors. You can give @code{as} the +options @samp{-m68000}, @samp{-mc68000}, @samp{-m68010}, +@samp{-mc68010}, @samp{-m68020}, and @samp{-mc68020} to tell it what +processor it should be assembling for. Unfortunately, these options +are almost entirely unused and untried. They make work, but nobody +has tested them much. + +@subsection Syntax + +The 680x0 version of @code{as} uses syntax similar to the Sun +assembler. Size modifieres are appended directly to the end of the +opcode without an intervening period. Thus, @samp{move.l} is +written @samp{movl}, etc. + +@c This is no longer true +@c Explicit size modifiers for branch instructions are ignored; @code{as} +@c automatically picks the smallest size that will reach the +destination. + +If @code{as} is compiled with SUN_ASM_SYNTAX defined, it will also +allow Sun-style local labels of the form @samp{1$} through @samp{$9}. + +In the following table @dfn{apc} stands for any of the address +registers (@samp{a0} through @samp{a7}), nothing, (@samp{}), the +Program Counter (@samp{pc}), or the zero-address relative to the +program counter (@samp{zpc}). + +The following addressing modes are understood: +@table @dfn +@item Immediate +@samp{#@var{digits}} + +@item Data Register +@samp{d0} through @samp{d7} + +@item Address Register +@samp{a0} through @samp{a7} + +@item Address Register Indirect +@samp{a0@@} through @samp{a7@@} + +@item Address Register Postincrement +@samp{a0@@+} through @samp{a7@@+} + +@item Address Register Predecrement +@samp{a0@@-} through @samp{a7@@-} + +@item Indirect Plus Offset +@samp{@var{apc}@@(@var{digits})} + +@item Index +@samp{@var{apc}@@(@var{digits},@var{register}:@var{size}:@var{scale})} +or @samp{@var{apc}@@(@var{register}:@var{size}:@var{scale})} + +@item Postindex +@samp{@var{apc}@@(@var{digits})@@(@var{digits},@var{register}:@var{size}:@var{scale})} +or @samp{@var{apc}@@(@var{digits})@@(@var{register}:@var{size}:@var{scale})} + +@item Preindex +@samp{@var{apc}@@(@var{digits},@var{register}:@var{size}:@var{scale})@@(@var{digits})} +or @samp{@var{apc}@@(@var{register}:@var{size}:@var{scale})@@(@var{digits})} + +@item Memory Indirect +@samp{@var{apc}@@(@var{digits})@@(@var{digits})} + +@item Absolute +@samp{@var{symbol}}, or @samp{@var{digits}}, or either of the above followed +by @samp{:b}, @samp{:w}, or @samp{:l}. +@end table + +@subsection Floating Point +The floating point code is not too well tested, and may have +subtle bugs in it. + +Packed decimal (P) format floating literals are not supported. +Feel free to add the code yourself. + +The floating point formats generated by directives are these. +@table @code +@item .float +@code{Single} precision floating point constants. +@item .double +@code{Double} precision floating point constants. +@end table + +There is no directive to produce regions of memory holding +extended precision numbers, however they can be used as +immediate operands to floating-point instructions. Adding a +directive to create extended precision numbers would not be +hard. Nobody has felt any burning need to do it. + +@subsection Machine Directives +In order to be compatible with the Sun assembler the 680x0 assembler +understands the following directives. +@table @code +@item .data1 +This directive is identical to a @code{.data 1} directive. +@item .data2 +This directive is identical to a @code{.data 2} directive. +@item .even +This directive is identical to a @code{.align 1} directive. +@c Is this true? does it work??? +@item .skip +This directive is identical to a @code{.space} directive. +@end table + +@subsection Opcodes +Danger: Several bugs have been found in the opcode table (and +fixed). More bugs may exist. Be careful when using obscure +instructions. + +The assembler automatically chooses the proper size for branch +instructions. However, most attempts to force a short displacement +will be honored. Branches that are forced to use a short +displacement will not be adjusted if the target is out of range. +Let The User Beware. + +The immediate character is @samp{#} for Sun compatibility. The +line-comment character is @samp{|}. If a @samp{#} appears at the +beginning of a line, it is treated as a comment unless it looks like +@samp{# line file}, in which case it is treated normally. + +@section 32x32 +@subsection Options +The 32x32 version of @code{as} accepts a @kbd{-m32032} option to +specify thiat it is compiling for a 32032 processor, or a +@kbd{-m32532} to specify that it is compiling for a 32532 option. +The default (if neither is specified) is chosen when the assembler +is compiled. + +@subsection Syntax +I don't know anything about the 32x32 syntax assembled by +@code{as}. Someone who undersands the processor (I've never seen +one) and the possible syntaxes should write this section. + +@subsection Floating Point +The 32x32 uses IEEE floating point numbers, but @code{as} will only +create single or double precision values. I don't know if the 32x32 +understands extended precision numbers. + +@subsection Machine Directives +The 32x32 has no machine dependent directives. + +@section Sparc +@subsection Options +The sparc has no machine dependent options. + +@subsection syntax +I don't know anything about Sparc syntax. Someone who does +will have to write this section. + +@subsection Floating Point +The Sparc uses ieee floating-point numbers. + +@subsection Machine Directives +The Sparc version of @code{as} supports the following additional +machine directives: + +@table @code +@item .common +This must be followed by a symbol name, a positive number, and +@code{"bss"}. This behaves somewhat like @code{.comm}, but the +syntax is different. + +@item .global +This is functionally identical to @code{.globl}. + +@item .half +This is functionally identical to @code{.short}. + +@item .proc +This directive is ignored. Any text following it on the same +line is also ignored. + +@item .reserve +This must be followed by a symbol name, a positive number, and +@code{"bss"}. This behaves somewhat like @code{.lcomm}, but the +syntax is different. + +@item .seg +This must be followed by @code{"text"}, @code{"data"}, or +@code{"data1"}. It behaves like @code{.text}, @code{.data}, or +@code{.data 1}. + +@item .skip +This is functionally identical to the .space directive. + +@item .word +On the Sparc, the .word directive produces 32 bit values, +instead of the 16 bit values it produces on every other machine. + +@end table + +@section Intel 80386 +@subsection Options +The 80386 has no machine dependent options. + +@subsection AT&T Syntax versus Intel Syntax +In order to maintain compatibility with the output of @code{GCC}, +@code{as} supports AT&T System V/386 assembler syntax. This is quite +different from Intel syntax. We mention these differences because +almost all 80386 documents used only Intel syntax. Notable differences +between the two syntaxes are: +@itemize @bullet +@item +AT&T immediate operands are preceded by @samp{$}; Intel immediate +operands are undelimited (Intel @samp{push 4} is AT&T @samp{pushl $4}). +AT&T register operands are preceded by @samp{%}; Intel register operands +are undelimited. AT&T absolute (as opposed to PC relative) jump/call +operands are prefixed by @samp{*}; they are undelimited in Intel syntax. + +@item +AT&T and Intel syntax use the opposite order for source and destination +operands. Intel @samp{add eax, 4} is @samp{addl $4, %eax}. The +@samp{source, dest} convention is maintained for compatibility with +previous Unix assemblers. + +@item +In AT&T syntax the size of memory operands is determined from the last +character of the opcode name. Opcode suffixes of @samp{b}, @samp{w}, +and @samp{l} specify byte (8-bit), word (16-bit), and long (32-bit) +memory references. Intel syntax accomplishes this by prefixes memory +operands (@emph{not} the opcodes themselves) with @samp{byte ptr}, +@samp{word ptr}, and @samp{dword ptr}. Thus, Intel @samp{mov al, byte +ptr @var{foo}} is @samp{movb @var{foo}, %al} in AT&T syntax. + +@item +Immediate form long jumps and calls are +@samp{lcall/ljmp $@var{segment}, $@var{offset}} in AT&T syntax; the +Intel syntax is +@samp{call/jmp far @var{segment}:@var{offset}}. Also, the far return +instruction +is @samp{lret $@var{stack-adjust}} in AT&T syntax; Intel syntax is +@samp{ret far @var{stack-adjust}}. + +@item +The AT&T assembler does not provide support for multiple segment +programs. Unix style systems expect all programs to be single segments. +@end itemize + +@subsection Opcode Naming +Opcode names are suffixed with one character modifiers which specify the +size of operands. The letters @samp{b}, @samp{w}, and @samp{l} specify +byte, word, and long operands. If no suffix is specified by an +instruction and it contains no memory operands then @code{as} tries to +fill in the missing suffix based on the destination register operand +(the last one by convention). Thus, @samp{mov %ax, %bx} is equivalent +to @samp{movw %ax, %bx}; also, @samp{mov $1, %bx} is equivalent to +@samp{movw $1, %bx}. Note that this is incompatible with the AT&T Unix +assembler which assumes that a missing opcode suffix implies long +operand size. (This incompatibility does not affect compiler output +since compilers always explicitly specify the opcode suffix.) + +Almost all opcodes have the same names in AT&T and Intel format. There +are a few exceptions. The sign extend and zero extend instructions need +two sizes to specify them. They need a size to sign/zero extend +@emph{from} and a size to zero extend @emph{to}. This is accomplished +by using two opcode suffixes in AT&T syntax. Base names for sign extend +and zero extend are @samp{movs@dots{}} and @samp{movz@dots{}} in AT&T +syntax (@samp{movsx} and @samp{movzx} in Intel syntax). The opcode +suffixes are tacked on to this base name, the @emph{from} suffix before +the @emph{to} suffix. Thus, @samp{movsbl %al, %edx} is AT&T syntax for +``move sign extend @emph{from} %al @emph{to} %edx.'' Possible suffixes, +thus, are @samp{bl} (from byte to long), @samp{bw} (from byte to word), +and @samp{wl} (from word to long). + +The Intel syntax conversion instructions +@itemize @bullet +@item +@samp{cbw} --- sign-extend byte in @samp{%al} to word in @samp{%ax}, +@item +@samp{cwde} --- sign-extend word in @samp{%ax} to long in @samp{%eax}, +@item +@samp{cwd} --- sign-extend word in @samp{%ax} to long in @samp{%dx:%ax}, +@item +@samp{cdq} --- sign-extend dword in @samp{%eax} to quad in @samp{%edx:%eax}, +@end itemize +are called @samp{cbtw}, @samp{cwtl}, @samp{cwtd}, and @samp{cltd} in +AT&T naming. @code{as} accepts either naming for these instructions. + +Far call/jump instructions are @samp{lcall} and @samp{ljmp} in +AT&T syntax, but are @samp{call far} and @samp{jump far} in Intel +convention. + +@subsection Register Naming +Register operands are always prefixes with @samp{%}. The 80386 registers +consist of +@itemize @bullet +@item +the 8 32-bit registers @samp{%eax} (the accumulator), @samp{%ebx}, +@samp{%ecx}, @samp{%edx}, @samp{%edi}, @samp{%esi}, @samp{%ebp} (the +frame pointer), and @samp{%esp} (the stack pointer). + +@item +the 8 16-bit low-ends of these: @samp{%ax}, @samp{%bx}, @samp{%cx}, +@samp{%dx}, @samp{%di}, @samp{%si}, @samp{%bp}, and @samp{%sp}. + +@item +the 8 8-bit registers: @samp{%ah}, @samp{%al}, @samp{%bh}, +@samp{%bl}, @samp{%ch}, @samp{%cl}, @samp{%dh}, and @samp{%dl} (These +are the high-bytes and low-bytes of @samp{%ax}, @samp{%bx}, +@samp{%cx}, and @samp{%dx}) + +@item +the 6 segment registers @samp{%cs} (code segment), @samp{%ds} +(data segment), @samp{%ss} (stack segment), @samp{%es}, @samp{%fs}, +and @samp{%gs}. + +@item +the 3 processor control registers @samp{%cr0}, @samp{%cr2}, and +@samp{%cr3}. + +@item +the 6 debug registers @samp{%db0}, @samp{%db1}, @samp{%db2}, +@samp{%db3}, @samp{%db6}, and @samp{%db7}. + +@item +the 2 test registers @samp{%tr6} and @samp{%tr7}. + +@item +the 8 floating point register stack @samp{%st} or equivalently +@samp{%st(0)}, @samp{%st(1)}, @samp{%st(2)}, @samp{%st(3)}, +@samp{%st(4)}, @samp{%st(5)}, @samp{%st(6)}, and @samp{%st(7)}. +@end itemize + +@subsection Opcode Prefixes +Opcode prefixes are used to modify the following opcode. They are used +to repeat string instructions, to provide segment overrides, to perform +bus lock operations, and to give operand and address size (16-bit +operands are specified in an instruction by prefixing what would +normally be 32-bit operands with a ``operand size'' opcode prefix). +Opcode prefixes are usually given as single-line instructions with no +operands, and must directly precede the instruction they act upon. For +example, the @samp{scas} (scan string) instruction is repeated with: +@example + repne + scas +@end example + +Here is a list of opcode prefixes: +@itemize @bullet +@item +Segment override prefixes @samp{cs}, @samp{ds}, @samp{ss}, @samp{es}, +@samp{fs}, @samp{gs}. These are automatically added by specifying +using the @var{segment}:@var{memory-operand} form for memory references. + +@item +Operand/Address size prefixes @samp{data16} and @samp{addr16} +change 32-bit operands/addresses into 16-bit operands/addresses. Note +that 16-bit addressing modes (i.e. 8086 and 80286 addressing modes) +are not supported (yet). + +@item +The bus lock prefix @samp{lock} inhibits interrupts during +execution of the instruction it precedes. (This is only valid with +certain instructions; see a 80386 manual for details). + +@item +The wait for coprocessor prefix @samp{wait} waits for the +coprocessor to complete the current instruction. This should never be +needed for the 80386/80387 combination. + +@item +The @samp{rep}, @samp{repe}, and @samp{repne} prefixes are added +to string instructions to make them repeat @samp{%ecx} times. +@end itemize + +@subsection Memory References +An Intel syntax indirect memory reference of the form +@example +@var{segment}:[@var{base} + @var{index}*@var{scale} + @var{disp}] +@end example +is translated into the AT&T syntax +@example +@var{segment}:@var{disp}(@var{base}, @var{index}, @var{scale}) +@end example +where @var{base} and @var{index} are the optional 32-bit base and +index registers, @var{disp} is the optional displacement, and +@var{scale}, taking the values 1, 2, 4, and 8, multiplies @var{index} +to calculate the address of the operand. If no @var{scale} is +specified, @var{scale} is taken to be 1. @var{segment} specifies the +optional segment register for the memory operand, and may override the +default segment register (see a 80386 manual for segment register +defaults). Note that segment overrides in AT&T syntax @emph{must} have +be preceded by a @samp{%}. If you specify a segment override which +coincides with the default segment register, @code{as} will @emph{not} +output any segment register override prefixes to assemble the given +instruction. Thus, segment overrides can be specified to emphasize which +segment register is used for a given memory operand. + +Here are some examples of Intel and AT&T style memory references: +@table @asis + +@item AT&T: @samp{-4(%ebp)}, Intel: @samp{[ebp - 4]} +@var{base} is @samp{%ebp}; @var{disp} is @samp{-4}. @var{segment} is +missing, and the default segment is used (@samp{%ss} for addressing with +@samp{%ebp} as the base register). @var{index}, @var{scale} are both missing. + +@item AT&T: @samp{foo(,%eax,4)}, Intel: @samp{[foo + eax*4]} +@var{index} is @samp{%eax} (scaled by a @var{scale} 4); @var{disp} is +@samp{foo}. All other fields are missing. The segment register here +defaults to @samp{%ds}. + +@item AT&T: @samp{foo(,1)}; Intel @samp{[foo]} +This uses the value pointed to by @samp{foo} as a memory operand. +Note that @var{base} and @var{index} are both missing, but there is only +@emph{one} @samp{,}. This is a syntactic exception. + +@item AT&T: @samp{%gs:foo}; Intel @samp{gs:foo} +This selects the contents of the variable @samp{foo} with segment +register @var{segment} being @samp{%gs}. + +@end table + +Absolute (as opposed to PC relative) call and jump operands must be +prefixed with @samp{*}. If no @samp{*} is specified, @code{as} will +always choose PC relative addressing for jump/call labels. + +Any instruction that has a memory operand @emph{must} specify its size (byte, +word, or long) with an opcode suffix (@samp{b}, @samp{w}, or @samp{l}, +respectively). + +@subsection Handling of Jump Instructions +Jump instructions are always optimized to use the smallest possible +displacements. This is accomplished by using byte (8-bit) displacement +jumps whenever the target is sufficiently close. If a byte displacement +is insufficient a long (32-bit) displacement is used. We do not support +word (16-bit) displacement jumps (i.e. prefixing the jump instruction +with the @samp{addr16} opcode prefix), since the 80386 insists upon masking +@samp{%eip} to 16 bits after the word displacement is added. + +Note that the @samp{jcxz}, @samp{jecxz}, @samp{loop}, @samp{loopz}, +@samp{loope}, @samp{loopnz} and @samp{loopne} instructions only come in +byte displacements, so that it is possible that use of these +instructions (@code{GCC} does not use them) will cause the assembler to +print an error message (and generate incorrect code). The AT&T 80386 +assembler tries to get around this problem by expanding @samp{jcxz foo} to +@example + jcxz cx_zero + jmp cx_nonzero +cx_zero: jmp foo +cx_nonzero: +@end example + +@subsection Floating Point +All 80387 floating point types except packed BCD are supported. +(BCD support may be added without much difficulty). These data +types are 16-, 32-, and 64- bit integers, and single (32-bit), +double (64-bit), and extended (80-bit) precision floating point. +Each supported type has an opcode suffix and a constructor +associated with it. Opcode suffixes specify operand's data +types. Constructors build these data types into memory. + +@itemize @bullet +@item +Floating point constructors are @samp{.float} or @samp{.single}, +@samp{.double}, and @samp{.tfloat} for 32-, 64-, and 80-bit formats. +These correspond to opcode suffixes @samp{s}, @samp{l}, and @samp{t}. +@samp{t} stands for temporary real, and that the 80387 only supports +this format via the @samp{fldt} (load temporary real to stack top) and +@samp{fstpt} (store temporary real and pop stack) instructions. + +@item +Integer constructors are @samp{.word}, @samp{.long} or @samp{.int}, and +@samp{.quad} for the 16-, 32-, and 64-bit integer formats. The corresponding +opcode suffixes are @samp{s} (single), @samp{l} (long), and @samp{q} +(quad). As with the temporary real format the 64-bit @samp{q} format is +only present in the @samp{fildq} (load quad integer to stack top) and +@samp{fistpq} (store quad integer and pop stack) instructions. +@end itemize + +Register to register operations do not require opcode suffixes, +so that @samp{fst %st, %st(1)} is equivalent to @samp{fstl %st, %st(1)}. + +Since the 80387 automatically synchronizes with the 80386 @samp{fwait} +instructions are almost never needed (this is not the case for the +80286/80287 and 8086/8087 combinations). Therefore, @code{as} supresses +the @samp{fwait} instruction whenever it is implicitly selected by one +of the @samp{fn@dots{}} instructions. For example, @samp{fsave} and +@samp{fnsave} are treated identically. In general, all the @samp{fn@dots{}} +instructions are made equivalent to @samp{f@dots{}} instructions. If +@samp{fwait} is desired it must be explicitly coded. + +@subsection Notes +There is some trickery concerning the @samp{mul} and @samp{imul} +instructions that deserves mention. The 16-, 32-, and 64-bit expanding +multiplies (base opcode @samp{0xf6}; extension 4 for @samp{mul} and 5 +for @samp{imul}) can be output only in the one operand form. Thus, +@samp{imul %ebx, %eax} does @emph{not} select the expanding multiply; +the expanding multiply would clobber the @samp{%edx} register, and this +would confuse @code{GCC} output. Use @samp{imul %ebx} to get the +64-bit product in @samp{%edx:%eax}. + +We have added a two operand form of @samp{imul} when the first operand +is an immediate mode expression and the second operand is a register. +This is just a shorthand, so that, multiplying @samp{%eax} by 69, for +example, can be done with @samp{imul $69, %eax} rather than @samp{imul +$69, %eax, %eax}. + +@node Maintenance, Retargeting, MachineDependent, top +@chapter Maintaining the Assembler +[[this chapter is still being built]] + +@section Design +We had these goals, in descending priority: +@table @b +@item Accuracy. +For every program composed by a compiler, @code{as} should emit +``correct'' code. This leaves some latitude in choosing addressing +modes, order of @code{relocation_info} structures in the object +file, @i{etc}. + +@item Speed, for usual case. +By far the most common use of @code{as} will be assembling compiler +emissions. + +@item Upward compatibility for existing assembler code. +Well @dots{} we don't support Vax bit fields but everything else +seems to be upward compatible. + +@item Readability. +The code should be maintainable with few surprises. (JF: ha!) + +@end table + +We assumed that disk I/O was slow and expensive while memory was +fast and access to memory was cheap. We expect the in-memory data +structures to be less than 10 times the size of the emitted object +file. (Contrast this with the C compiler where in-memory structures +might be 100 times object file size!) +This suggests: +@itemize @bullet +@item +Try to read the source file from disk only one time. For other +reasons, we keep large chunks of the source file in memory during +assembly so this is not a problem. Also the assembly algorithm +should only scan the source text once if the compiler composed the +text according to a few simple rules. +@item +Emit the object code bytes only once. Don't store values and then +backpatch later. +@item +Build the object file in memory and do direct writes to disk of +large buffers. +@end itemize + +RMS suggested a one-pass algorithm which seems to work well. By not +parsing text during a second pass considerable time is saved on +large programs (@i{e.g.} the sort of C program @code{yacc} would +emit). + +It happened that the data structures needed to emit relocation +information to the object file were neatly subsumed into the data +structures that do backpatching of addresses after pass 1. + +Many of the functions began life as re-usable modules, loosely +connected. RMS changed this to gain speed. For example, input +parsing routines which used to work on pre-sanitized strings now +must parse raw data. Hence they have to import knowledge of the +assemblers' comment conventions @i{etc}. + +@section Deprecated Feature(?)s +We have stopped supporting some features: +@itemize @bullet +@item +@code{.org} statements must have @b{defined} expressions. +@item +Vax Bit fields (@kbd{:} operator) are entirely unsupported. +@end itemize + +It might be a good idea to not support these features in a future release: +@itemize @bullet +@item +@kbd{#} should begin a comment, even in column 1. +@item +Why support the logical line & file concept any more? +@item +Subsegments are a good candidate for flushing. +Depends on which compilers need them I guess. +@end itemize + +@section Bugs, Ideas, Further Work +Clearly the major improvement is DON'T USE A TEXT-READING +ASSEMBLER for the back end of a compiler. It is much faster to +interpret binary gobbledygook from a compiler's tables than to +ask the compiler to write out human-readable code just so the +assembler can parse it back to binary. + +Assuming you use @code{as} for human written programs: here are +some ideas: +@itemize @bullet +@item +Document (here) @code{APP}. +@item +Take advantage of knowing no spaces except after opcode +to speed up @code{as}. (Modify @code{app.c} to flush useless spaces: +only keep space/tabs at begin of line or between 2 +symbols.) +@item +Put pointers in this documentation to @file{a.out} documentation. +@item +Split the assembler into parts so it can gobble direct binary +from @i{e.g.} @code{cc}. It is silly for@code{cc} to compose text +just so @code{as} can parse it back to binary. +@item +Rewrite hash functions: I want a more modular, faster library. +@item +Clean up LOTS of code. +@item +Include all the non-@file{.c} files in the maintenance chapter. +@item +Document flonums. +@item +Implement flonum short literals. +@item +Change all talk of expression operands to expression quantities, +or perhaps to expression primaries. +@item +Implement pass 2. +@item +Whenever a @code{.text} or @code{.data} statement is seen, we close +of the current frag with an imaginary @code{.fill 0}. This is +because we only have one obstack for frags, and we can't grow new +frags for a new subsegment, then go back to the old subsegment and +append bytes to the old frag. All this nonsense goes away if we +give each subsegment its own obstack. It makes code simpler in +about 10 places, but nobody has bothered to do it because C compiler +output rarely changes subsegments (compared to ending frags with +relaxable addresses, which is common). +@end itemize + +@section Sources +@c The following files in the @file{as} directory +@c are symbolic links to other files, of +@c the same name, in a different directory. +@c @itemize @bullet +@c @item +@c @file{atof_generic.c} +@c @item +@c @file{atof_vax.c} +@c @item +@c @file{flonum_const.c} +@c @item +@c @file{flonum_copy.c} +@c @item +@c @file{flonum_get.c} +@c @item +@c @file{flonum_multip.c} +@c @item +@c @file{flonum_normal.c} +@c @item +@c @file{flonum_print.c} +@c @end itemize + +Here is a list of the source files in the @file{as} directory. + +@table @file +@item app.c +This contains the pre-processing phase, which deletes comments, +handles whitespace, etc. This was recently re-written, since app +used to be a separate program, but RMS wanted it to be inline. + +@item append.c +This is a subroutine to append a string to another string returning a +pointer just after the last @code{char} appended. (JF: All these +little routines should probably all be put in one file.) + +@item as.c +Here you will find the main program of the assembler @code{as}. + +@item expr.c +This is a branch office of @file{read.c}. This understands +expressions, primaries. Inside @code{as}, primaries are called +(expression) @i{operands}. This is confusing, because we also talk +(elsewhere) about instruction @i{operands}. Also, expression +operands are called @i{quantities} explicitly to avoid confusion +with instruction operands. What a mess. + +@item frags.c +This implements the @b{frag} concept. Without frags, finding the +right size for branch instructions would be a lot harder. + +@item hash.c +This contains the symbol table, opcode table @i{etc.} hashing +functions. + +@item hex_value.c +This is a table of values of digits, for use in atoi() type +functions. Could probably be flushed by using calls to strtol(), or +something similar. + +@item input-file.c +This contains Operating system dependent source file reading +routines. Since error messages often say where we are in reading +the source file, they live here too. Since @code{as} is intended to +run under GNU and Unix only, this might be worth flushing. Anyway, +almost all C compilers support stdio. + +@item input-scrub.c +This deals with calling the pre-processor (if needed) and feeding the +chunks back to the rest of the assembler the right way. + +@item messages.c +This contains operating system independent parts of fatal and +warning message reporting. See @file{append.c} above. + +@item output-file.c +This contains operating system dependent functions that write an +object file for @code{as}. See @file{input-file.c} above. + +@item read.c +This implements all the directives of @code{as}. This also deals +with passing input lines to the machine dependent part of the +assembler. + +@item strstr.c +This is a C library function that isn't in most C libraries yet. +See @file{append.c} above. + +@item subsegs.c +This implements subsegments. + +@item symbols.c +This implements symbols. + +@item write.c +This contains the code to perform relaxation, and to write out +the object file. It is mostly operating system independent, but +different OSes have different object file formats in any case. + +@item xmalloc.c +This implements @code{malloc()} or bust. See @file{append.c} above. + +@item xrealloc.c +This implements @code{realloc()} or bust. See @file{append.c} above. + +@item atof-generic.c +The following files were taken from a machine-independent subroutine +library for manipulating floating point numbers and very large +integers. + +@file{atof-generic.c} turns a string into a flonum internal format +floating-point number. + +@item flonum-const.c +This contains some potentially useful floating point numbers in +flonum format. + +@item flonum-copy.c +This copies a flonum. + +@item flonum-multip.c +This multiplies two flonums together. + +@item bignum-copy.c +This copies a bignum. + +@end table + +Here is a table of all the machine-specific files (this includes +both source and header files). Typically, there is a +@var{machine}.c file, a @var{machine}-opcode.h file, and an +atof-@var{machine}.c file. The @var{machine}-opcode.h file should +be identical to the one used by GDB (which uses it for disassembly.) + +@table @file + +@item atof-ieee.c +This contains code to turn a flonum into a ieee literal constant. +This is used by tye 680x0, 32x32, sparc, and i386 versions of @code{as}. + +@item i386-opcode.h +This is the opcode-table for the i386 version of the assembler. + +@item i386.c +This contains all the code for the i386 version of the assembler. + +@item i386.h +This defines constants and macros used by the i386 version of the assembler. + +@item m-generic.h +generic 68020 header file. To be linked to m68k.h on a +non-sun3, non-hpux system. + +@item m-sun2.h +68010 header file for Sun2 workstations. Not well tested. To be linked +to m68k.h on a sun2. (See also @samp{-DSUN_ASM_SYNTAX} in the +@file{Makefile}.) + +@item m-sun3.h +68020 header file for Sun3 workstations. To be linked to m68k.h before +compiling on a Sun3 system. (See also @samp{-DSUN_ASM_SYNTAX} in the +@file{Makefile}.) + +@item m-hpux.h +68020 header file for a HPUX (system 5?) box. Which box, which +version of HPUX, etc? I don't know. + +@item m68k.h +A hard- or symbolic- link to one of @file{m-generic.h}, +@file{m-hpux.h} or @file{m-sun3.h} depending on which kind of +680x0 you are assembling for. (See also @samp{-DSUN_ASM_SYNTAX} in the +@file{Makefile}.) + +@item m68k-opcode.h +Opcode table for 68020. This is now a link to the opcode table +in the @code{GDB} source directory. + +@item m68k.c +All the mc680x0 code, in one huge, slow-to-compile file. + +@item ns32k.c +This contains the code for the ns32032/ns32532 version of the +assembler. + +@item ns32k-opcode.h +This contains the opcode table for the ns32032/ns32532 version +of the assembler. + +@item vax-inst.h +Vax specific file for describing Vax operands and other Vax-ish things. + +@item vax-opcode.h +Vax opcode table. + +@item vax.c +Vax specific parts of @code{as}. Also includes the former files +@file{vax-ins-parse.c}, @file{vax-reg-parse.c} and @file{vip-op.c}. + +@item atof-vax.c +Turns a flonum into a Vax constant. + +@item vms.c +This file contains the special code needed to put out a VMS +style object file for the Vax. + +@end table + +Here is a list of the header files in the source directory. +(Warning: This section may not be very accurate. I didn't +write the header files; I just report them.) Also note that I +think many of these header files could be cleaned up or +eliminated. + +@table @file + +@item a.out.h +This describes the structures used to create the binary header data +inside the object file. Perhaps we should use the one in +@file{/usr/include}? + +@item as.h +This defines all the globally useful things, and pulls in <stdio.h> +and <assert.h>. + +@item bignum.h +This defines macros useful for dealing with bignums. + +@item expr.h +Structure and macros for dealing with expression() + +@item flonum.h +This defines the structure for dealing with floating point +numbers. It #includes @file{bignum.h}. + +@item frags.h +This contains macro for appending a byte to the current frag. + +@item hash.h +Structures and function definitions for the hashing functions. + +@item input-file.h +Function headers for the input-file.c functions. + +@item md.h +structures and function headers for things defined in the +machine dependent part of the assembler. + +@item obstack.h +This is the GNU systemwide include file for manipulating obstacks. +Since nobody is running under real GNU yet, we include this file. + +@item read.h +Macros and function headers for reading in source files. + +@item struct-symbol.h +Structure definition and macros for dealing with the gas +internal form of a symbol. + +@item subsegs.h +structure definition for dealing with the numbered subsegments +of the text and data segments. + +@item symbols.h +Macros and function headers for dealing with symbols. + +@item write.h +Structure for doing segment fixups. +@end table + +@comment ~subsection Test Directory +@comment (Note: The test directory seems to have disappeared somewhere +@comment along the line. If you want it, you'll probably have to find a +@comment REALLY OLD dump tape~dots{}) +@comment +@comment The ~file{test/} directory is used for regression testing. +@comment After you modify ~code{as}, you can get a quick go/nogo +@comment confidence test by running the new ~code{as} over the source +@comment files in this directory. You use a shell script ~file{test/do}. +@comment +@comment The tests in this suite are evolving. They are not comprehensive. +@comment They have, however, caught hundreds of bugs early in the debugging +@comment cycle of ~code{as}. Most test statements in this suite were naturally +@comment selected: they were used to demonstrate actual ~code{as} bugs rather +@comment than being written ~i{a prioi}. +@comment +@comment Another testing suggestion: over 30 bugs have been found simply by +@comment running examples from this manual through ~code{as}. +@comment Some examples in this manual are selected +@comment to distinguish boundary conditions; they are good for testing ~code{as}. +@comment +@comment ~subsubsection Regression Testing +@comment Each regression test involves assembling a file and comparing the +@comment actual output of ~code{as} to ``known good'' output files. Both +@comment the object file and the error/warning message file (stderr) are +@comment inspected. Optionally ~code{as}' exit status may be checked. +@comment Discrepencies are reported. Each discrepency means either that +@comment you broke some part of ~code{as} or that the ``known good'' files +@comment are now out of date and should be changed to reflect the new +@comment definition of ``good''. +@comment +@comment Each regression test lives in its own directory, in a tree +@comment rooted in the directory ~file{test/}. Each such directory +@comment has a name ending in ~file{.ret}, where `ret' stands for +@comment REgression Test. The ~file{.ret} ending allows ~code{find +@comment (1)} to find all regression tests in the tree, without +@comment needing to list them explicitly. +@comment +@comment Any ~file{.ret} directory must contain a file called +@comment ~file{input} which is the source file to assemble. During +@comment testing an object file ~file{output} is created, as well as +@comment a file ~file{stdouterr} which contains the output to both +@comment stderr and stderr. If there is a file ~file{output.good} in +@comment the directory, and if ~file{output} contains exactly the +@comment same data as ~file{output.good}, the file ~file{output} is +@comment deleted. Likewise ~file{stdouterr} is removed if it exactly +@comment matches a file ~file{stdouterr.good}. If file +@comment ~file{status.good} is present, containing a decimal number +@comment before a newline, the exit status of ~code{as} is compared +@comment to this number. If the status numbers are not equal, a file +@comment ~file{status} is written to the directory, containing the +@comment actual status as a decimal number followed by newline. +@comment +@comment Should any of the ~file{*.good} files fail to match their corresponding +@comment actual files, this is noted by a 1-line message on the screen during +@comment the regression test, and you can use ~code{find (1)} to find any +@comment files named ~file{status}, ~file {output} or ~file{stdouterr}. +@comment +@node Retargeting, , Maintenance, top +@chapter Teaching the Assembler about a New Machine + +This chapter describes the steps required in order to make the +assembler work with another machine's assembly language. This +chapter is not complete, and only describes the steps in the +broadest terms. You should look at the source for the +currently supported machine in order to discover some of the +details that aren't mentioned here. + +You should create a new file called @file{@var{machine}.c}, and +add the appropriate lines to the file @file{Makefile} so that +you can compile your new version of the assembler. This should +be straighforward; simply add lines similar to the ones there +for the four current versions of the assembler. + +If you want to be compatable with GDB, (and the current +machine-dependent versions of the assembler), you should create +a file called @file{@var{machine}-opcode.h} which should +contain all the information about the names of the machine +instructions, their opcodes, and what addressing modes they +support. If you do this right, the assembler and GDB can share +this file, and you'll only have to write it once. Note that +while you're writing @code{as}, you may want to use an +independent program (if you have access to one), to make sure +that @code{as} is emitting the correct bytes. Since @code{as} +and @code{GDB} share the opcode table, an incorrect opcode +table entry may make invalid bytes look OK when you disassemble +them with @code{GDB}. + +@section Functions You will Have to Write + +Your file @file{@var{machine}.c} should contain definitions for +the following functions and variables. It will need to include +some header files in order to use some of the structures +defined in the machine-independent part of the assembler. The +needed header files are mentioned in the descriptions of the +functions that will need them. + +@table @code + +@item long omagic; +This long integer holds the value to place at the beginning of +the @file{a.out} file. It is usually @samp{OMAGIC}, except on +machines that store additional information in the magic-number. + +@item char comment_chars[]; +This character array holds the values of the characters that +start a comment anywhere in a line. Comments are stripped off +automatically by the machine independent part of the +assembler. Note that the @samp{/*} will always start a +comment, and that only @samp{*/} will end a comment started by +@samp{*/}. + +@item char line_comment_chars[]; +This character array holds the values of the chars that start a +comment only if they are the first (non-whitespace) character +on a line. If the character @samp{#} does not appear in this +list, you may get unexpected results. (Various +machine-independent parts of the assembler treat the comments +@samp{#APP} and @samp{#NO_APP} specially, and assume that lines +that start with @samp{#} are comments.) + +@item char EXP_CHARS[]; +This character array holds the letters that can separate the +mantissa and the exponent of a floating point number. Typical +values are @samp{e} and @samp{E}. + +@item char FLT_CHARS[]; +This character array holds the letters that--when they appear +immediately after a leading zero--indicate that a number is a +floating-point number. (Sort of how 0x indicates that a +hexadecimal number follows.) + +@item pseudo_typeS md_pseudo_table[]; +(@var{pseudo_typeS} is defined in @file{md.h}) +This array contains a list of the machine_dependent directives +the assembler must support. It contains the name of each +pseudo op (Without the leading @samp{.}), a pointer to a +function to be called when that directive is encountered, and +an integer argument to be passed to that function. + +@item void md_begin(void) +This function is called as part of the assembler's +initialization. It should do any initialization required by +any of your other routines. + +@item int md_parse_option(char **optionPTR, int *argcPTR, char ***argvPTR) +This routine is called once for each option on the command line +that the machine-independent part of @code{as} does not +understand. This function should return non-zero if the option +pointed to by @var{optionPTR} is a valid option. If it is not +a valid option, this routine should return zero. The variables +@var{argcPTR} and @var{argvPTR} are provided in case the option +requires a filename or something similar as an argument. If +the option is multi-character, @var{optionPTR} should be +advanced past the end of the option, otherwise every letter in +the option will be treated as a separate single-character +option. + +@item void md_assemble(char *string) +This routine is called for every machine-dependent +non-directive line in the source file. It does all the real +work involved in reading the opcode, parsing the operands, +etc. @var{string} is a pointer to a null-terminated string, +that comprises the input line, with all excess whitespace and +comments removed. + +@item void md_number_to_chars(char *outputPTR,long value,int nbytes) +This routine is called to turn a C long int, short int, or char +into the series of bytes that represents that number on the +target machine. @var{outputPTR} points to an array where the +result should be stored; @var{value} is the value to store; and +@var{nbytes} is the number of bytes in 'value' that should be +stored. + +@item void md_number_to_imm(char *outputPTR,long value,int nbytes) +This routine is called to turn a C long int, short int, or char +into the series of bytes that represent an immediate value on +the target machine. It is identical to the function @code{md_number_to_chars}, +except on NS32K machines.@refill + +@item void md_number_to_disp(char *outputPTR,long value,int nbytes) +This routine is called to turn a C long int, short int, or char +into the series of bytes that represent an displacement value on +the target machine. It is identical to the function @code{md_number_to_chars}, +except on NS32K machines.@refill + +@item void md_number_to_field(char *outputPTR,long value,int nbytes) +This routine is identical to @code{md_number_to_chars}, +except on NS32K machines. + +@item void md_ri_to_chars(struct relocation_info *riPTR,ri) +(@code{struct relocation_info} is defined in @file{a.out.h}) +This routine emits the relocation info in @var{ri} +in the appropriate bit-pattern for the target machine. +The result should be stored in the location pointed +to by @var{riPTR}. This routine may be a no-op unless you are +attempting to do cross-assembly. + +@item char *md_atof(char type,char *outputPTR,int *sizePTR) +This routine turns a series of digits into the appropriate +internal representation for a floating-point number. +@var{type} is a character from @var{FLT_CHARS[]} that describes +what kind of floating point number is wanted; @var{outputPTR} +is a pointer to an array that the result should be stored in; +and @var{sizePTR} is a pointer to an integer where the size (in +bytes) of the result should be stored. This routine should +return an error message, or an empty string (not (char *)0) for +success. + +@item int md_short_jump_size; +This variable holds the (maximum) size in bytes of a short (16 +bit or so) jump created by @code{md_create_short_jump()}. This +variable is used as part of the broken-word feature, and isn't +needed if the assembler is compiled with +@samp{-DWORKING_DOT_WORD}. + +@item int md_long_jump_size; +This variable holds the (maximum) size in bytes of a long (32 +bit or so) jump created by @code{md_create_long_jump()}. This +variable is used as part of the broken-word feature, and isn't +needed if the assembler is compiled with +@samp{-DWORKING_DOT_WORD}. + +@item void md_create_short_jump(char *resultPTR,long from_addr, +@code{long to_addr,fragS *frag,symbolS *to_symbol)} +This function emits a jump from @var{from_addr} to @var{to_addr} in +the array of bytes pointed to by @var{resultPTR}. If this creates a +type of jump that must be relocated, this function should call +@code{fix_new()} with @var{frag} and @var{to_symbol}. The jump +emitted by this function may be smaller than @var{md_short_jump_size}, +but it must never create a larger one. +(If it creates a smaller jump, the extra bytes of memory will not be +used.) This function is used as part of the broken-word feature, +and isn't needed if the assembler is compiled with +@samp{-DWORKING_DOT_WORD}.@refill + +@item void md_create_long_jump(char *ptr,long from_addr, +@code{long to_addr,fragS *frag,symbolS *to_symbol)} +This function is similar to the previous function, +@code{md_create_short_jump()}, except that it creates a long +jump instead of a short one. This function is used as part of +the broken-word feature, and isn't needed if the assembler is +compiled with @samp{-DWORKING_DOT_WORD}. + +@item int md_estimate_size_before_relax(fragS *fragPTR,int segment_type) +This function does the initial setting up for relaxation. This +includes forcing references to still-undefined symbols to the +appropriate addressing modes. + +@item relax_typeS md_relax_table[]; +(relax_typeS is defined in md.h) +This array describes the various machine dependent states a +frag may be in before relaxation. You will need one group of +entries for each type of addressing mode you intend to relax. + +@item void md_convert_frag(fragS *fragPTR) +(@var{fragS} is defined in @file{as.h}) +This routine does the required cleanup after relaxation. +Relaxation has changed the type of the frag to a type that can +reach its destination. This function should adjust the opcode +of the frag to use the appropriate addressing mode. +@var{fragPTR} points to the frag to clean up. + +@item void md_end(void) +This function is called just before the assembler exits. It +need not free up memory unless the operating system doesn't do +it automatically on exit. (In which case you'll also have to +track down all the other places where the assembler allocates +space but never frees it.) + +@end table + +@section External Variables You will Need to Use + +You will need to refer to or change the following external variables +from within the machine-dependent part of the assembler. + +@table @code +@item extern char flagseen[]; +This array holds non-zero values in locations corresponding to +the options that were on the command line. Thus, if the +assembler was called with @samp{-W}, @var{flagseen['W']} would +be non-zero. + +@item extern fragS *frag_now; +This pointer points to the current frag--the frag that bytes +are currently being added to. If nothing else, you will need +to pass it as an argument to various machine-independent +functions. It is maintained automatically by the +frag-manipulating functions; you should never have to change it +yourself. + +@item extern LITTLENUM_TYPE generic_bignum[]; +(@var{LITTLENUM_TYPE} is defined in @file{bignum.h}. +This is where @dfn{bignums}--numbers larger than 32 bits--are +returned when they are encountered in an expression. You will +need to use this if you need to implement directives (or +anything else) that must deal with these large numbers. +@code{Bignums} are of @code{segT} @code{SEG_BIG} (defined in +@file{as.h}, and have a positive @code{X_add_number}. The +@code{X_add_number} of a @code{bignum} is the number of +@code{LITTLENUMS} in @var{generic_bignum} that the number takes +up. + +@item extern FLONUM_TYPE generic_floating_point_number; +(@var{FLONUM_TYPE} is defined in @file{flonum.h}. +The is where @dfn{flonums}--floating-point numbers within +expressions--are returned. @code{Flonums} are of @code{segT} +@code{SEG_BIG}, and have a negative @code{X_add_number}. +@code{Flonums} are returned in a generic format. You will have +to write a routine to turn this generic format into the +appropriate floating-point format for your machine. + +@item extern int need_pass_2; +If this variable is non-zero, the assembler has encountered an +expression that cannot be assembled in a single pass. Since +the second pass isn't implemented, this flag means that the +assembler is punting, and is only looking for additional syntax +errors. (Or something like that.) + +@item extern segT now_seg; +This variable holds the value of the segment the assembler is +currently assembling into. + +@end table + +@section External functions will you need + +You will find the following external functions useful (or +indispensable) when you're writing the machine-dependent part +of the assembler. + +@table @code + +@item char *frag_more(int bytes) +This function allocates @var{bytes} more bytes in the current +frag (or starts a new frag, if it can't expand the current frag +any more.) for you to store some object-file bytes in. It +returns a pointer to the bytes, ready for you to store data in. + +@item void fix_new(fragS *frag, int where, short size, symbolS *add_symbol, symbolS *sub_symbol, long offset, int pcrel) +This function stores a relocation fixup to be acted on later. +@var{frag} points to the frag the relocation belongs in; +@var{where} is the location within the frag where the relocation begins; +@var{size} is the size of the relocation, and is usually 1 (a single byte), + 2 (sixteen bits), or 4 (a longword). +The value @var{add_symbol} @minus{} @var{sub_symbol} + @var{offset}, is added to the byte(s) +at @var{frag->literal[where]}. If @var{pcrel} is non-zero, the address of the +location is subtracted from the result. A relocation entry is also added +to the @file{a.out} file. @var{add_symbol}, @var{sub_symbol}, and/or +@var{offset} may be NULL.@refill + +@item char *frag_var(relax_stateT type, int max_chars, int var, +@code{relax_substateT subtype, symbolS *symbol, char *opcode)} +This function creates a machine-dependent frag of type @var{type} +(usually @code{rs_machine_dependent}). +@var{max_chars} is the maximum size in bytes that the frag may grow by; +@var{var} is the current size of the variable end of the frag; +@var{subtype} is the sub-type of the frag. The sub-type is used to index into +@var{md_relax_table[]} during @code{relaxation}. +@var{symbol} is the symbol whose value should be used to when relax-ing this frag. +@var{opcode} points into a byte whose value may have to be modified if the +addressing mode used by this frag changes. It typically points into the +@var{fr_literal[]} of the previous frag, and is used to point to a location +that @code{md_convert_frag()}, may have to change.@refill + +@item void frag_wane(fragS *fragPTR) +This function is useful from within @code{md_convert_frag}. It +changes a frag to type rs_fill, and sets the variable-sized +piece of the frag to zero. The frag will never change in size +again. + +@item segT expression(expressionS *retval) +(@var{segT} is defined in @file{as.h}; @var{expressionS} is defined in @file{expr.h}) +This function parses the string pointed to by the external char +pointer @var{input_line_pointer}, and returns the segment-type +of the expression. It also stores the results in the +@var{expressionS} pointed to by @var{retval}. +@var{input_line_pointer} is advanced to point past the end of +the expression. (@var{input_line_pointer} is used by other +parts of the assembler. If you modify it, be sure to restore +it to its original value.) + +@item as_warn(char *message,@dots{}) +If warning messages are disabled, this function does nothing. +Otherwise, it prints out the current file name, and the current +line number, then uses @code{fprintf} to print the +@var{message} and any arguments it was passed. + +@item as_bad(char *message,@dots{}) +This function should be called when @code{as} encounters +conditions that are bad enough that @code{as} should not +produce an object file, but should continue reading input and +printing warning and bad error messages. + +@item as_fatal(char *message,@dots{}) +This function prints out the current file name and line number, +prints the word @samp{FATAL:}, then uses @code{fprintf} to +print the @var{message} and any arguments it was passed. Then +the assembler exits. This function should only be used for +serious, unrecoverable errors. + +@item void float_const(int float_type) +This function reads floating-point constants from the current +input line, and calls @code{md_atof} to assemble them. It is +useful as the function to call for the directives +@samp{.single}, @samp{.double}, @samp{.float}, etc. +@var{float_type} must be a character from @var{FLT_CHARS}. + +@item void demand_empty_rest_of_line(void); +This function can be used by machine-dependent directives to +make sure the rest of the input line is empty. It prints a +warning message if there are additional characters on the line. + +@item long int get_absolute_expression(void) +This function can be used by machine-dependent directives to +read an absolute number from the current input line. It +returns the result. If it isn't given an absolute expression, +it prints a warning message and returns zero. + +@end table + + +@section The concept of Frags + +This assembler works to optimize the size of certain addressing +modes. (e.g. branch instructions) This means the size of many +pieces of object code cannot be determined until after assembly +is finished. (This means that the addresses of symbols cannot be +determined until assembly is finished.) In order to do this, +@code{as} stores the output bytes as @dfn{frags}. + +Here is the definition of a frag (from @file{as.h}) +@example +struct frag +@{ + long int fr_fix; + long int fr_var; + relax_stateT fr_type; + relax_substateT fr_substate; + unsigned long fr_address; + long int fr_offset; + struct symbol *fr_symbol; + char *fr_opcode; + struct frag *fr_next; + char fr_literal[]; +@} +@end example + +@table @var +@item fr_fix +is the size of the fixed-size piece of the frag. + +@item fr_var +is the maximum (?) size of the variable-sized piece of the frag. + +@item fr_type +is the type of the frag. +Current types are: +rs_fill +rs_align +rs_org +rs_machine_dependent + +@item fr_substate +This stores the type of machine-dependent frag this is. (what +kind of addressing mode is being used, and what size is being +tried/will fit/etc. + +@item fr_address +@var{fr_address} is only valid after relaxation is finished. +Before relaxation, the only way to store an address is (pointer +to frag containing the address) plus (offset into the frag). + +@item fr_offset +This contains a number, whose meaning depends on the type of +the frag. +for machine_dependent frags, this contains the offset from +fr_symbol that the frag wants to go to. Thus, for branch +instructions it is usually zero. (unless the instruction was +@samp{jba foo+12} or something like that.) + +@item fr_symbol +for machine_dependent frags, this points to the symbol the frag +needs to reach. + +@item fr_opcode +This points to the location in the frag (or in a previous frag) +of the opcode for the instruction that caused this to be a frag. +@var{fr_opcode} is needed if the actual opcode must be changed +in order to use a different form of the addressing mode. +(For example, if a conditional branch only comes in size tiny, +a large-size branch could be implemented by reversing the sense +of the test, and turning it into a tiny branch over a large jump. +This would require changing the opcode.) + +@var{fr_literal} is a variable-size array that contains the +actual object bytes. A frag consists of a fixed size piece of +object data, (which may be zero bytes long), followed by a +piece of object data whose size may not have been determined +yet. Other information includes the type of the frag (which +controls how it is relaxed), + +@item fr_next +This is the next frag in the singly-linked list. This is +usually only needed by the machine-independent part of +@code{as}. + +@end table + +@c Is this really a good idea? +@iftex +@center [end of manual] +@end iftex +@summarycontents +@contents +@bye diff --git a/gas/expr.c b/gas/expr.c new file mode 100644 index 0000000..c62e39c --- /dev/null +++ b/gas/expr.c @@ -0,0 +1,966 @@ +/* expr.c -operands, expressions- + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +/* + * This is really a branch office of as-read.c. I split it out to clearly + * distinguish the world of expressions from the world of statements. + * (It also gives smaller files to re-compile.) + * Here, "operand"s are of expressions, not instructions. + */ + +#include <ctype.h> +#include <string.h> + +#include "as.h" + +#include "obstack.h" + +#ifdef __STDC__ +static void clean_up_expression(expressionS *expressionP); +#else /* __STDC__ */ +static void clean_up_expression(); /* Internal. */ +#endif /* __STDC__ */ +extern const char EXP_CHARS[]; /* JF hide MD floating pt stuff all the same place */ +extern const char FLT_CHARS[]; + +#ifdef LOCAL_LABELS_DOLLAR +extern int local_label_defined[]; +#endif + +/* + * Build any floating-point literal here. + * Also build any bignum literal here. + */ + +/* LITTLENUM_TYPE generic_buffer [6]; */ /* JF this is a hack */ +/* Seems atof_machine can backscan through generic_bignum and hit whatever + happens to be loaded before it in memory. And its way too complicated + for me to fix right. Thus a hack. JF: Just make generic_bignum bigger, + and never write into the early words, thus they'll always be zero. + I hate Dean's floating-point code. Bleh. + */ +LITTLENUM_TYPE generic_bignum [SIZE_OF_LARGE_NUMBER+6]; +FLONUM_TYPE generic_floating_point_number = +{ + & generic_bignum [6], /* low (JF: Was 0) */ + & generic_bignum [SIZE_OF_LARGE_NUMBER+6 - 1], /* high JF: (added +6) */ + 0, /* leader */ + 0, /* exponent */ + 0 /* sign */ +}; +/* If nonzero, we've been asked to assemble nan, +inf or -inf */ +int generic_floating_point_magic; + +/* + * Summary of operand(). + * + * in: Input_line_pointer points to 1st char of operand, which may + * be a space. + * + * out: A expressionS. X_seg determines how to understand the rest of the + * expressionS. + * The operand may have been empty: in this case X_seg == SEG_ABSENT. + * Input_line_pointer->(next non-blank) char after operand. + * + */ + +static segT +operand (expressionP) + register expressionS * expressionP; +{ + register char c; + register char *name; /* points to name of symbol */ + register symbolS * symbolP; /* Points to symbol */ + + extern char hex_value[]; /* In hex_value.c */ + + SKIP_WHITESPACE(); /* Leading whitespace is part of operand. */ + c = * input_line_pointer ++; /* Input_line_pointer->past char in c. */ + if (isdigit(c)) + { + register valueT number; /* offset or (absolute) value */ + register short int digit; /* value of next digit in current radix */ + /* invented for humans only, hope */ + /* optimising compiler flushes it! */ + register short int radix; /* 2, 8, 10 or 16 */ + /* 0 means we saw start of a floating- */ + /* point constant. */ + register short int maxdig = 0;/* Highest permitted digit value. */ + register int too_many_digits = 0; /* If we see >= this number of */ + /* digits, assume it is a bignum. */ + register char * digit_2; /*->2nd digit of number. */ + int small; /* TRUE if fits in 32 bits. */ + + if (c == '0') { /* non-decimal radix */ + if ((c = *input_line_pointer ++)=='x' || c=='X') { + c = *input_line_pointer ++; /* read past "0x" or "0X" */ + maxdig = radix = 16; + too_many_digits = 9; + } else { + /* If it says '0f' and the line ends or it DOESN'T look like + a floating point #, its a local label ref. DTRT */ + /* likewise for the b's. xoxorich. */ + if ((c == 'f' || c == 'b' || c == 'B') + && (!*input_line_pointer || + (!strchr("+-.0123456789",*input_line_pointer) && + !strchr(EXP_CHARS,*input_line_pointer)))) { + maxdig = radix = 10; + too_many_digits = 11; + c = '0'; + input_line_pointer -= 2; + + } else if (c == 'b' || c == 'B') { + c = *input_line_pointer++; + maxdig = radix = 2; + too_many_digits = 33; + + } else if (c && strchr(FLT_CHARS,c)) { + radix = 0; /* Start of floating-point constant. */ + /* input_line_pointer->1st char of number. */ + expressionP->X_add_number = -(isupper(c) ? tolower(c) : c); + + } else { /* By elimination, assume octal radix. */ + radix = maxdig = 8; + too_many_digits = 11; + } + } /* c == char after "0" or "0x" or "0X" or "0e" etc. */ + } else { + maxdig = radix = 10; + too_many_digits = 11; + } /* if operand starts with a zero */ + + if (radix) { /* Fixed-point integer constant. */ + /* May be bignum, or may fit in 32 bits. */ +/* + * Most numbers fit into 32 bits, and we want this case to be fast. + * So we pretend it will fit into 32 bits. If, after making up a 32 + * bit number, we realise that we have scanned more digits than + * comfortably fit into 32 bits, we re-scan the digits coding + * them into a bignum. For decimal and octal numbers we are conservative: some + * numbers may be assumed bignums when in fact they do fit into 32 bits. + * Numbers of any radix can have excess leading zeros: we strive + * to recognise this and cast them back into 32 bits. + * We must check that the bignum really is more than 32 + * bits, and change it back to a 32-bit number if it fits. + * The number we are looking for is expected to be positive, but + * if it fits into 32 bits as an unsigned number, we let it be a 32-bit + * number. The cavalier approach is for speed in ordinary cases. + */ + digit_2 = input_line_pointer; + for (number=0; (digit=hex_value[c])<maxdig; c = * input_line_pointer ++) + { + number = number * radix + digit; + } + /* C contains character after number. */ + /* Input_line_pointer->char after C. */ + small = input_line_pointer - digit_2 < too_many_digits; + if (! small) + { + /* + * We saw a lot of digits. Manufacture a bignum the hard way. + */ + LITTLENUM_TYPE * leader; /*->high order littlenum of the bignum. */ + LITTLENUM_TYPE * pointer; /*->littlenum we are frobbing now. */ + long carry; + + leader = generic_bignum; + generic_bignum [0] = 0; + generic_bignum [1] = 0; + /* We could just use digit_2, but lets be mnemonic. */ + input_line_pointer = -- digit_2; /*->1st digit. */ + c = *input_line_pointer ++; + for (; (carry = hex_value [c]) < maxdig; c = * input_line_pointer ++) + { + for (pointer = generic_bignum; + pointer <= leader; + pointer ++) + { + long work; + + work = carry + radix * * pointer; + * pointer = work & LITTLENUM_MASK; + carry = work >> LITTLENUM_NUMBER_OF_BITS; + } + if (carry) + { + if (leader < generic_bignum + SIZE_OF_LARGE_NUMBER - 1) + { /* Room to grow a longer bignum. */ + * ++ leader = carry; + } + } + } + /* Again, C is char after number, */ + /* input_line_pointer->after C. */ + know(sizeof (int) * 8 == 32); + know(LITTLENUM_NUMBER_OF_BITS == 16); + /* Hence the constant "2" in the next line. */ + if (leader < generic_bignum + 2) + { /* Will fit into 32 bits. */ + number = + ((generic_bignum [1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS) + | (generic_bignum [0] & LITTLENUM_MASK); + small = 1; + } + else + { + number = leader - generic_bignum + 1; /* Number of littlenums in the bignum. */ + } + } + if (small) + { + /* + * Here with number, in correct radix. c is the next char. + * Note that unlike Un*x, we allow "011f" "0x9f" to + * both mean the same as the (conventional) "9f". This is simply easier + * than checking for strict canonical form. Syntax sux! + */ + if (number<10) + { + if (0 +#ifdef LOCAL_LABELS_FB + || c=='b' +#endif +#ifdef LOCAL_LABELS_DOLLAR + || (c=='$' && local_label_defined[number]) +#endif + ) + { + /* + * Backward ref to local label. + * Because it is backward, expect it to be DEFINED. + */ + /* + * Construct a local label. + */ + name = local_label_name ((int)number, 0); + if (((symbolP = symbol_find(name)) != NULL) /* seen before */ + && (S_IS_DEFINED(symbolP))) /* symbol is defined: OK */ + { /* Expected path: symbol defined. */ + /* Local labels are never absolute. Don't waste time checking absoluteness. */ + know((S_GET_SEGMENT(symbolP) == SEG_DATA) || (S_GET_SEGMENT(symbolP) == SEG_TEXT)); + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + expressionP->X_seg = S_GET_SEGMENT(symbolP); + } + else + { /* Either not seen or not defined. */ + as_bad("Backw. ref to unknown label \"%d:\", 0 assumed.", + number); + expressionP->X_add_number = 0; + expressionP->X_seg = SEG_ABSOLUTE; + } + } + else + { + if (0 +#ifdef LOCAL_LABELS_FB + || c == 'f' +#endif +#ifdef LOCAL_LABELS_DOLLAR + || (c=='$' && !local_label_defined[number]) +#endif + ) + { + /* + * Forward reference. Expect symbol to be undefined or + * unknown. Undefined: seen it before. Unknown: never seen + * it in this pass. + * Construct a local label name, then an undefined symbol. + * Don't create a XSEG frag for it: caller may do that. + * Just return it as never seen before. + */ + name = local_label_name((int)number, 1); + symbolP = symbol_find_or_make(name); + /* We have no need to check symbol properties. */ + know(S_GET_SEGMENT(symbolP) == SEG_UNKNOWN + || S_GET_SEGMENT(symbolP) == SEG_TEXT + || S_GET_SEGMENT(symbolP) == SEG_DATA); + expressionP->X_add_symbol = symbolP; + expressionP->X_seg = SEG_UNKNOWN; + expressionP->X_subtract_symbol = NULL; + expressionP->X_add_number = 0; + } + else + { /* Really a number, not a local label. */ + expressionP->X_add_number = number; + expressionP->X_seg = SEG_ABSOLUTE; + input_line_pointer --; /* Restore following character. */ + } /* if (c=='f') */ + } /* if (c=='b') */ + } + else + { /* Really a number. */ + expressionP->X_add_number = number; + expressionP->X_seg = SEG_ABSOLUTE; + input_line_pointer --; /* Restore following character. */ + } /* if (number<10) */ + } + else + { + expressionP->X_add_number = number; + expressionP->X_seg = SEG_BIG; + input_line_pointer --; /*->char following number. */ + } /* if (small) */ + } /* (If integer constant) */ + else + { /* input_line_pointer->*/ + /* floating-point constant. */ + int error_code; + + error_code = atof_generic + (& input_line_pointer, ".", EXP_CHARS, + & generic_floating_point_number); + + if (error_code) + { + if (error_code == ERROR_EXPONENT_OVERFLOW) + { + as_bad("Bad floating-point constant: exponent overflow, probably assembling junk"); + } + else + { + as_bad("Bad floating-point constant: unknown error code=%d.", error_code); + } + } + expressionP->X_seg = SEG_BIG; + /* input_line_pointer->just after constant, */ + /* which may point to whitespace. */ + know(expressionP->X_add_number < 0); /* < 0 means "floating point". */ + } /* if (not floating-point constant) */ + } + else if(c=='.' && !is_part_of_name(*input_line_pointer)) { + extern struct obstack frags; + + /* + JF: '.' is pseudo symbol with value of current location in current + segment. . . + */ + symbolP = symbol_new("L0\001", + now_seg, + (valueT)(obstack_next_free(&frags)-frag_now->fr_literal), + frag_now); + + expressionP->X_add_number=0; + expressionP->X_add_symbol=symbolP; + expressionP->X_seg = now_seg; + + } else if (is_name_beginner(c)) /* here if did not begin with a digit */ + { + /* + * Identifier begins here. + * This is kludged for speed, so code is repeated. + */ + name = -- input_line_pointer; + c = get_symbol_end(); + symbolP = symbol_find_or_make(name); + /* + * If we have an absolute symbol or a reg, then we know its value now. + */ + expressionP->X_seg = S_GET_SEGMENT(symbolP); + switch (expressionP->X_seg) + { + case SEG_ABSOLUTE: + case SEG_REGISTER: + expressionP->X_add_number = S_GET_VALUE(symbolP); + break; + + default: + expressionP->X_add_number = 0; + expressionP->X_add_symbol = symbolP; + } + * input_line_pointer = c; + expressionP->X_subtract_symbol = NULL; + } + else if (c=='(')/* didn't begin with digit & not a name */ + { + (void)expression(expressionP); + /* Expression() will pass trailing whitespace */ + if (* input_line_pointer ++ != ')') + { + as_bad("Missing ')' assumed"); + input_line_pointer --; + } + /* here with input_line_pointer->char after "(...)" */ + } + else if (c == '~' || c == '-' || c == '+') { + /* unary operator: hope for SEG_ABSOLUTE */ + switch (operand (expressionP)) { + case SEG_ABSOLUTE: + /* input_line_pointer->char after operand */ + if (c=='-') { + expressionP->X_add_number = - expressionP->X_add_number; + /* + * Notice: '-' may overflow: no warning is given. This is compatible + * with other people's assemblers. Sigh. + */ + } else if (c == '~') { + expressionP->X_add_number = ~ expressionP->X_add_number; + } else if (c != '+') { + know(0); + } /* switch on unary operator */ + break; + + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_PASS1: + case SEG_UNKNOWN: + if(c=='-') { /* JF I hope this hack works */ + expressionP->X_subtract_symbol=expressionP->X_add_symbol; + expressionP->X_add_symbol=0; + expressionP->X_seg=SEG_DIFFERENCE; + break; + } + default: /* unary on non-absolute is unsuported */ + as_bad("Unary operator %c ignored because bad operand follows", c); + break; + /* Expression undisturbed from operand(). */ + } + } + else if (c=='\'') + { +/* + * Warning: to conform to other people's assemblers NO ESCAPEMENT is permitted + * for a single quote. The next character, parity errors and all, is taken + * as the value of the operand. VERY KINKY. + */ + expressionP->X_add_number = * input_line_pointer ++; + expressionP->X_seg = SEG_ABSOLUTE; + } + else + { + /* can't imagine any other kind of operand */ + expressionP->X_seg = SEG_ABSENT; + input_line_pointer --; + md_operand (expressionP); + } +/* + * It is more 'efficient' to clean up the expressions when they are created. + * Doing it here saves lines of code. + */ + clean_up_expression (expressionP); + SKIP_WHITESPACE(); /*->1st char after operand. */ + know(* input_line_pointer != ' '); + return (expressionP->X_seg); +} /* operand() */ + +/* Internal. Simplify a struct expression for use by expr() */ + +/* + * In: address of a expressionS. + * The X_seg field of the expressionS may only take certain values. + * Now, we permit SEG_PASS1 to make code smaller & faster. + * Elsewise we waste time special-case testing. Sigh. Ditto SEG_ABSENT. + * Out: expressionS may have been modified: + * 'foo-foo' symbol references cancelled to 0, + * which changes X_seg from SEG_DIFFERENCE to SEG_ABSOLUTE; + * Unused fields zeroed to help expr(). + */ + +static void +clean_up_expression (expressionP) + register expressionS * expressionP; +{ + switch (expressionP->X_seg) + { + case SEG_ABSENT: + case SEG_PASS1: + expressionP->X_add_symbol = NULL; + expressionP->X_subtract_symbol = NULL; + expressionP->X_add_number = 0; + break; + + case SEG_BIG: + case SEG_ABSOLUTE: + expressionP->X_subtract_symbol = NULL; + expressionP->X_add_symbol = NULL; + break; + + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_UNKNOWN: + expressionP->X_subtract_symbol = NULL; + break; + + case SEG_DIFFERENCE: + /* + * It does not hurt to 'cancel' NULL==NULL + * when comparing symbols for 'eq'ness. + * It is faster to re-cancel them to NULL + * than to check for this special case. + */ + if (expressionP->X_subtract_symbol == expressionP->X_add_symbol + || (expressionP->X_subtract_symbol + && expressionP->X_add_symbol + && expressionP->X_subtract_symbol->sy_frag==expressionP->X_add_symbol->sy_frag + && S_GET_VALUE(expressionP->X_subtract_symbol) == S_GET_VALUE(expressionP->X_add_symbol))) { + expressionP->X_subtract_symbol = NULL; + expressionP->X_add_symbol = NULL; + expressionP->X_seg = SEG_ABSOLUTE; + } + break; + + case SEG_REGISTER: + expressionP->X_add_symbol = NULL; + expressionP->X_subtract_symbol = NULL; + break; + + default: + BAD_CASE (expressionP->X_seg); + break; + } +} /* clean_up_expression() */ + +/* + * expr_part () + * + * Internal. Made a function because this code is used in 2 places. + * Generate error or correct X_?????_symbol of expressionS. + */ + +/* + * symbol_1 += symbol_2 ... well ... sort of. + */ + +static segT +expr_part (symbol_1_PP, symbol_2_P) + symbolS ** symbol_1_PP; + symbolS * symbol_2_P; +{ + segT return_value; + + know((* symbol_1_PP) == NULL + || (S_GET_SEGMENT(*symbol_1_PP) == SEG_TEXT) + || (S_GET_SEGMENT(*symbol_1_PP) == SEG_DATA) + || (S_GET_SEGMENT(*symbol_1_PP) == SEG_BSS) + || (!S_IS_DEFINED(* symbol_1_PP))); + know(symbol_2_P == NULL + || (S_GET_SEGMENT(symbol_2_P) == SEG_TEXT) + || (S_GET_SEGMENT(symbol_2_P) == SEG_DATA) + || (S_GET_SEGMENT(symbol_2_P) == SEG_BSS) + || (!S_IS_DEFINED(symbol_2_P))); + if (* symbol_1_PP) + { + if (!S_IS_DEFINED(* symbol_1_PP)) + { + if (symbol_2_P) + { + return_value = SEG_PASS1; + * symbol_1_PP = NULL; + } + else + { + know(!S_IS_DEFINED(* symbol_1_PP)); + return_value = SEG_UNKNOWN; + } + } + else + { + if (symbol_2_P) + { + if (!S_IS_DEFINED(symbol_2_P)) + { + * symbol_1_PP = NULL; + return_value = SEG_PASS1; + } + else + { + /* {seg1} - {seg2} */ + as_bad("Expression too complex, 2 symbols forgotten: \"%s\" \"%s\"", + S_GET_NAME(* symbol_1_PP), S_GET_NAME(symbol_2_P)); + * symbol_1_PP = NULL; + return_value = SEG_ABSOLUTE; + } + } + else + { + return_value = S_GET_SEGMENT(* symbol_1_PP); + } + } + } + else + { /* (* symbol_1_PP) == NULL */ + if (symbol_2_P) + { + * symbol_1_PP = symbol_2_P; + return_value = S_GET_SEGMENT(symbol_2_P); + } + else + { + * symbol_1_PP = NULL; + return_value = SEG_ABSOLUTE; + } + } + know(return_value == SEG_ABSOLUTE + || return_value == SEG_TEXT + || return_value == SEG_DATA + || return_value == SEG_BSS + || return_value == SEG_UNKNOWN + || return_value == SEG_PASS1); + know((* symbol_1_PP) == NULL + || (S_GET_SEGMENT(* symbol_1_PP) == return_value)); + return (return_value); +} /* expr_part() */ + +/* Expression parser. */ + +/* + * We allow an empty expression, and just assume (absolute,0) silently. + * Unary operators and parenthetical expressions are treated as operands. + * As usual, Q==quantity==operand, O==operator, X==expression mnemonics. + * + * We used to do a aho/ullman shift-reduce parser, but the logic got so + * warped that I flushed it and wrote a recursive-descent parser instead. + * Now things are stable, would anybody like to write a fast parser? + * Most expressions are either register (which does not even reach here) + * or 1 symbol. Then "symbol+constant" and "symbol-symbol" are common. + * So I guess it doesn't really matter how inefficient more complex expressions + * are parsed. + * + * After expr(RANK,resultP) input_line_pointer->operator of rank <= RANK. + * Also, we have consumed any leading or trailing spaces (operand does that) + * and done all intervening operators. + */ + +typedef enum +{ +O_illegal, /* (0) what we get for illegal op */ + +O_multiply, /* (1) * */ +O_divide, /* (2) / */ +O_modulus, /* (3) % */ +O_left_shift, /* (4) < */ +O_right_shift, /* (5) > */ +O_bit_inclusive_or, /* (6) | */ +O_bit_or_not, /* (7) ! */ +O_bit_exclusive_or, /* (8) ^ */ +O_bit_and, /* (9) & */ +O_add, /* (10) + */ +O_subtract /* (11) - */ +} +operatorT; + +#define __ O_illegal + +static const operatorT op_encoding [256] = { /* maps ASCII->operators */ + +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + +__, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __, +__, __, O_multiply, O_add, __, O_subtract, __, O_divide, +__, __, __, __, __, __, __, __, +__, __, __, __, O_left_shift, __, O_right_shift, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, O_bit_exclusive_or, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, +__, __, __, __, O_bit_inclusive_or, __, __, __, + +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, +__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __ +}; + + +/* + * Rank Examples + * 0 operand, (expression) + * 1 + - + * 2 & ^ ! | + * 3 * / % << >> + */ +static const operator_rankT +op_rank [] = { 0, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1 }; + +/* Return resultP->X_seg. */ +segT expr(rank, resultP) +register operator_rankT rank; /* Larger # is higher rank. */ +register expressionS *resultP; /* Deliver result here. */ +{ + expressionS right; + register operatorT op_left; + register char c_left; /* 1st operator character. */ + register operatorT op_right; + register char c_right; + + know(rank >= 0); + (void)operand (resultP); + know(* input_line_pointer != ' '); /* Operand() gobbles spaces. */ + c_left = * input_line_pointer; /* Potential operator character. */ + op_left = op_encoding [c_left]; + while (op_left != O_illegal && op_rank [(int) op_left] > rank) + { + input_line_pointer ++; /*->after 1st character of operator. */ + /* Operators "<<" and ">>" have 2 characters. */ + if (* input_line_pointer == c_left && (c_left == '<' || c_left == '>')) + { + input_line_pointer ++; + } /*->after operator. */ + if (SEG_ABSENT == expr (op_rank[(int) op_left], &right)) + { + as_warn("Missing operand value assumed absolute 0."); + resultP->X_add_number = 0; + resultP->X_subtract_symbol = NULL; + resultP->X_add_symbol = NULL; + resultP->X_seg = SEG_ABSOLUTE; + } + know(* input_line_pointer != ' '); + c_right = * input_line_pointer; + op_right = op_encoding [c_right]; + if (* input_line_pointer == c_right && (c_right == '<' || c_right == '>')) + { + input_line_pointer ++; + } /*->after operator. */ + know((int) op_right == 0 + || op_rank [(int) op_right] <= op_rank[(int) op_left]); + /* input_line_pointer->after right-hand quantity. */ + /* left-hand quantity in resultP */ + /* right-hand quantity in right. */ + /* operator in op_left. */ + if (resultP->X_seg == SEG_PASS1 || right . X_seg == SEG_PASS1) + { + resultP->X_seg = SEG_PASS1; + } + else + { + if (resultP->X_seg == SEG_BIG) + { + as_warn("Left operand of %c is a %s. Integer 0 assumed.", + c_left, resultP->X_add_number > 0 ? "bignum" : "float"); + resultP->X_seg = SEG_ABSOLUTE; + resultP->X_add_symbol = 0; + resultP->X_subtract_symbol = 0; + resultP->X_add_number = 0; + } + if (right . X_seg == SEG_BIG) + { + as_warn("Right operand of %c is a %s. Integer 0 assumed.", + c_left, right . X_add_number > 0 ? "bignum" : "float"); + right . X_seg = SEG_ABSOLUTE; + right . X_add_symbol = 0; + right . X_subtract_symbol = 0; + right . X_add_number = 0; + } + if (op_left == O_subtract) + { + /* + * Convert - into + by exchanging symbols and negating number. + * I know -infinity can't be negated in 2's complement: + * but then it can't be subtracted either. This trick + * does not cause any further inaccuracy. + */ + + register symbolS * symbolP; + + right . X_add_number = - right . X_add_number; + symbolP = right . X_add_symbol; + right . X_add_symbol = right . X_subtract_symbol; + right . X_subtract_symbol = symbolP; + if (symbolP) + { + right . X_seg = SEG_DIFFERENCE; + } + op_left = O_add; + } + + if (op_left == O_add) + { + segT seg1; + segT seg2; + + know(resultP->X_seg == SEG_DATA + || resultP->X_seg == SEG_TEXT + || resultP->X_seg == SEG_BSS + || resultP->X_seg == SEG_UNKNOWN + || resultP->X_seg == SEG_DIFFERENCE + || resultP->X_seg == SEG_ABSOLUTE + || resultP->X_seg == SEG_PASS1); + know(right . X_seg == SEG_DATA + || right . X_seg == SEG_TEXT + || right . X_seg == SEG_BSS + || right . X_seg == SEG_UNKNOWN + || right . X_seg == SEG_DIFFERENCE + || right . X_seg == SEG_ABSOLUTE + || right . X_seg == SEG_PASS1); + + clean_up_expression (& right); + clean_up_expression (resultP); + + seg1 = expr_part (& resultP->X_add_symbol, right . X_add_symbol); + seg2 = expr_part (& resultP->X_subtract_symbol, right . X_subtract_symbol); + if (seg1 == SEG_PASS1 || seg2 == SEG_PASS1) { + need_pass_2 = 1; + resultP->X_seg = SEG_PASS1; + } else if (seg2 == SEG_ABSOLUTE) + resultP->X_seg = seg1; + else if (seg1 != SEG_UNKNOWN + && seg1 != SEG_ABSOLUTE + && seg2 != SEG_UNKNOWN + && seg1 != seg2) { + know(seg2 != SEG_ABSOLUTE); + know(resultP->X_subtract_symbol); + + know(seg1 == SEG_TEXT || seg1 == SEG_DATA || seg1== SEG_BSS); + know(seg2 == SEG_TEXT || seg2 == SEG_DATA || seg2== SEG_BSS); + know(resultP->X_add_symbol); + know(resultP->X_subtract_symbol); + as_bad("Expression too complex: forgetting %s - %s", + S_GET_NAME(resultP->X_add_symbol), + S_GET_NAME(resultP->X_subtract_symbol)); + resultP->X_seg = SEG_ABSOLUTE; + /* Clean_up_expression() will do the rest. */ + } else + resultP->X_seg = SEG_DIFFERENCE; + + resultP->X_add_number += right . X_add_number; + clean_up_expression (resultP); + } + else + { /* Not +. */ + if (resultP->X_seg == SEG_UNKNOWN || right . X_seg == SEG_UNKNOWN) + { + resultP->X_seg = SEG_PASS1; + need_pass_2 = 1; + } + else + { + resultP->X_subtract_symbol = NULL; + resultP->X_add_symbol = NULL; + /* Will be SEG_ABSOLUTE. */ + if (resultP->X_seg != SEG_ABSOLUTE || right . X_seg != SEG_ABSOLUTE) + { + as_bad("Relocation error. Absolute 0 assumed."); + resultP->X_seg = SEG_ABSOLUTE; + resultP->X_add_number = 0; + } + else + { + switch (op_left) + { + case O_bit_inclusive_or: + resultP->X_add_number |= right . X_add_number; + break; + + case O_modulus: + if (right . X_add_number) + { + resultP->X_add_number %= right . X_add_number; + } + else + { + as_warn("Division by 0. 0 assumed."); + resultP->X_add_number = 0; + } + break; + + case O_bit_and: + resultP->X_add_number &= right . X_add_number; + break; + + case O_multiply: + resultP->X_add_number *= right . X_add_number; + break; + + case O_divide: + if (right . X_add_number) + { + resultP->X_add_number /= right . X_add_number; + } + else + { + as_warn("Division by 0. 0 assumed."); + resultP->X_add_number = 0; + } + break; + + case O_left_shift: + resultP->X_add_number <<= right . X_add_number; + break; + + case O_right_shift: + resultP->X_add_number >>= right . X_add_number; + break; + + case O_bit_exclusive_or: + resultP->X_add_number ^= right . X_add_number; + break; + + case O_bit_or_not: + resultP->X_add_number |= ~ right . X_add_number; + break; + + default: + BAD_CASE(op_left); + break; + } /* switch(operator) */ + } + } /* If we have to force need_pass_2. */ + } /* If operator was +. */ + } /* If we didn't set need_pass_2. */ + op_left = op_right; + } /* While next operator is >= this rank. */ + return (resultP->X_seg); +} + +/* + * get_symbol_end() + * + * This lives here because it belongs equally in expr.c & read.c. + * Expr.c is just a branch office read.c anyway, and putting it + * here lessens the crowd at read.c. + * + * Assume input_line_pointer is at start of symbol name. + * Advance input_line_pointer past symbol name. + * Turn that character into a '\0', returning its former value. + * This allows a string compare (RMS wants symbol names to be strings) + * of the symbol name. + * There will always be a char following symbol name, because all good + * lines end in end-of-line. + */ +char +get_symbol_end() +{ + register char c; + + while (is_part_of_name(c = * input_line_pointer ++)) + ; + * -- input_line_pointer = 0; + return (c); +} + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end: expr.c */ diff --git a/gas/expr.h b/gas/expr.h new file mode 100644 index 0000000..350215e --- /dev/null +++ b/gas/expr.h @@ -0,0 +1,79 @@ +/* expr.h -> header file for expr.c + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Abbreviations (mnemonics). + * + * O operator + * Q quantity, operand + * X eXpression + */ + +/* + * By popular demand, we define a struct to represent an expression. + * This will no doubt mutate as expressions become baroque. + * + * Currently, we support expressions like "foo-bar+42". + * In other words we permit a (possibly undefined) minuend, a + * (possibly undefined) subtrahend and an (absolute) augend. + * RMS says this is so we can have 1-pass assembly for any compiler + * emmissions, and a 'case' statement might emit 'undefined1 - undefined2'. + * + * To simplify table-driven dispatch, we also have a "segment" for the + * entire expression. That way we don't require complex reasoning about + * whether particular components are defined; and we can change component + * semantics without re-working all the dispatch tables in the assembler. + * In other words the "type" of an expression is its segment. + */ + +typedef struct +{ + symbolS *X_add_symbol; /* foo */ + symbolS *X_subtract_symbol; /* bar */ + long X_add_number; /* 42. Must be signed. */ + segT X_seg; /* What segment (expr type)? */ +} +expressionS; + + /* result should be type (expressionS *). */ +#define expression(result) expr(0,result) + + /* If an expression is SEG_BIG, look here */ + /* for its value. These common data may */ + /* be clobbered whenever expr() is called. */ +extern FLONUM_TYPE generic_floating_point_number; /* Flonums returned here. */ + /* Enough to hold most precise flonum. */ +extern LITTLENUM_TYPE generic_bignum []; /* Bignums returned here. */ +#define SIZE_OF_LARGE_NUMBER (20) /* Number of littlenums in above. */ + +typedef char operator_rankT; + +#ifdef __STDC__ + +char get_symbol_end(void); +segT expr(int rank, expressionS *resultP); + +#else /* __STDC__ */ + +char get_symbol_end(); +segT expr(); + +#endif /* __STDC__ */ + +/* end: expr.h */ diff --git a/gas/flonum-copy.c b/gas/flonum-copy.c new file mode 100644 index 0000000..6678bdf --- /dev/null +++ b/gas/flonum-copy.c @@ -0,0 +1,79 @@ +/* flonum_copy.c - copy a flonum + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include "as.h" + +#ifdef USG +#define bzero(s,n) memset(s,0,n) +#define bcopy(from,to,n) memcpy(to,from,n) +#endif + +void +flonum_copy (in, out) + FLONUM_TYPE * in; + FLONUM_TYPE * out; +{ + int in_length; /* 0 origin */ + int out_length; /* 0 origin */ + + out -> sign = in -> sign; + in_length = in -> leader - in -> low; + if (in_length < 0) + { + out -> leader = out -> low - 1; /* 0.0 case */ + } + else + { + out_length = out -> high - out -> low; + /* + * Assume no GAPS in packing of littlenums. + * I.e. sizeof(array) == sizeof(element) * number_of_elements. + */ + if (in_length <= out_length) + { + { + /* + * For defensive programming, zero any high-order littlenums we don't need. + * This is destroying evidence and wasting time, so why bother??? + */ + if (in_length < out_length) + { + bzero ((char *)(out->low + in_length + 1), out_length - in_length); + } + } + bcopy ((char *)(in->low), (char *)(out->low), (int)((in_length + 1) * sizeof(LITTLENUM_TYPE))); + out -> exponent = in -> exponent; + out -> leader = in -> leader - in -> low + out -> low; + } + else + { + int shorten; /* 1-origin. Number of littlenums we drop. */ + + shorten = in_length - out_length; + /* Assume out_length >= 0 ! */ + bcopy ((char *)(in->low + shorten),(char *)( out->low), (int)((out_length + 1) * sizeof(LITTLENUM_TYPE))); + out -> leader = out -> high; + out -> exponent = in -> exponent + shorten; + } + } /* if any significant bits */ +} + +/* end: flonum_copy.c */ diff --git a/gas/flonum-mult.c b/gas/flonum-mult.c new file mode 100644 index 0000000..b01f93b --- /dev/null +++ b/gas/flonum-mult.c @@ -0,0 +1,201 @@ +/* flonum_multip.c - multiply two flonums + Copyright (C) 1987, 1990, 1991 Free Software Foundation, Inc. + +This file is part of Gas, the GNU Assembler. + +The GNU assembler is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY. No author or distributor +accepts responsibility to anyone for the consequences of using it +or for whether it serves any particular purpose or works at all, +unless he says so in writing. Refer to the GNU Assembler General +Public License for full details. + +Everyone is granted permission to copy, modify and redistribute +the GNU Assembler, but only under the conditions described in the +GNU Assembler General Public License. A copy of this license is +supposed to have been given to you along with the GNU Assembler +so you can know your rights and responsibilities. It should be +in a file named COPYING. Among other things, the copyright +notice and this notice must be preserved on all copies. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include "flonum.h" + +/* plan for a . b => p(roduct) + + + +-------+-------+-/ /-+-------+-------+ + | a | a | ... | a | a | + | A | A-1 | | 1 | 0 | + +-------+-------+-/ /-+-------+-------+ + + + +-------+-------+-/ /-+-------+-------+ + | b | b | ... | b | b | + | B | B-1 | | 1 | 0 | + +-------+-------+-/ /-+-------+-------+ + + + +-------+-------+-/ /-+-------+-/ /-+-------+-------+ + | p | p | ... | p | ... | p | p | + | A+B+1| A+B | | N | | 1 | 0 | + +-------+-------+-/ /-+-------+-/ /-+-------+-------+ + + /^\ + (carry) a .b ... | ... a .b a .b + A B | 0 1 0 0 + | + ... | ... a .b + | 1 0 + | + | ... + | + | + | + | ___ + | \ + +----- P = > a .b + N /__ i j + + N = 0 ... A+B + + for all i,j where i+j=N + [i,j integers > 0] + +a[], b[], p[] may not intersect. +Zero length factors signify 0 significant bits: treat as 0.0. +0.0 factors do the right thing. +Zero length product OK. + +I chose the ForTran accent "foo[bar]" instead of the C accent "*garply" +because I felt the ForTran way was more intuitive. The C way would +probably yield better code on most C compilers. Dean Elsner. +(C style also gives deeper insight [to me] ... oh well ...) +*/ + +void flonum_multip (a, b, product) +const FLONUM_TYPE *a; +const FLONUM_TYPE *b; +FLONUM_TYPE *product; +{ + int size_of_a; /* 0 origin */ + int size_of_b; /* 0 origin */ + int size_of_product; /* 0 origin */ + int size_of_sum; /* 0 origin */ + int extra_product_positions;/* 1 origin */ + unsigned long work; + unsigned long carry; + long exponent; + LITTLENUM_TYPE * q; + long significant; /* TRUE when we emit a non-0 littlenum */ + /* ForTran accent follows. */ + int P; /* Scan product low-order -> high. */ + int N; /* As in sum above. */ + int A; /* Which [] of a? */ + int B; /* Which [] of b? */ + + if((a->sign!='-' && a->sign!='+') || (b->sign!='-' && b->sign!='+')) { + /* ... + Got to fail somehow. Any suggestions? */ + product->sign=0; + return; + } + product -> sign = (a->sign == b->sign) ? '+' : '-'; + size_of_a = a -> leader - a -> low; + size_of_b = b -> leader - b -> low; + exponent = a -> exponent + b -> exponent; + size_of_product = product -> high - product -> low; + size_of_sum = size_of_a + size_of_b; + extra_product_positions = size_of_product - size_of_sum; + if (extra_product_positions < 0) + { + P = extra_product_positions; /* P < 0 */ + exponent -= extra_product_positions; /* Increases exponent. */ + } + else + { + P = 0; + } + carry = 0; + significant = 0; + for (N = 0; + N <= size_of_sum; + N++) + { + work = carry; + carry = 0; + for (A = 0; + A <= N; + A ++) + { + B = N - A; + if (A <= size_of_a && B <= size_of_b && B >= 0) + { +#ifdef TRACE +printf("a:low[%d.]=%04x b:low[%d.]=%04x work_before=%08x\n", A, a->low[A], B, b->low[B], work); +#endif + work += a -> low [A] * b -> low [B]; + carry += work >> LITTLENUM_NUMBER_OF_BITS; + work &= LITTLENUM_MASK; +#ifdef TRACE +printf("work=%08x carry=%04x\n", work, carry); +#endif + } + } + significant |= work; + if (significant || P<0) + { + if (P >= 0) + { + product -> low [P] = work; +#ifdef TRACE +printf("P=%d. work[p]:=%04x\n", P, work); +#endif + } + P ++; + } + else + { + extra_product_positions ++; + exponent ++; + } + } + /* + * [P]-> position # size_of_sum + 1. + * This is where 'carry' should go. + */ +#ifdef TRACE +printf("final carry =%04x\n", carry); +#endif + if (carry) + { + if (extra_product_positions > 0) + { + product -> low [P] = carry; + } + else + { + /* No room at high order for carry littlenum. */ + /* Shift right 1 to make room for most significant littlenum. */ + exponent ++; + P --; + for (q = product -> low + P; + q >= product -> low; + q --) + { + work = * q; + * q = carry; + carry = work; + } + } + } + else + { + P --; + } + product -> leader = product -> low + P; + product -> exponent = exponent; +} + +/* end: flonum_multip.c */ diff --git a/gas/flonum.h b/gas/flonum.h new file mode 100644 index 0000000..570bd3a --- /dev/null +++ b/gas/flonum.h @@ -0,0 +1,122 @@ +/* flonum.h - Floating point package + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +/***********************************************************************\ +* * +* Arbitrary-precision floating point arithmetic. * +* * +* * +* Notation: a floating point number is expressed as * +* MANTISSA * (2 ** EXPONENT). * +* * +* If this offends more traditional mathematicians, then * +* please tell me your nomenclature for flonums! * +* * +\***********************************************************************/ +#if !defined(__STDC__) && !defined(const) +#define const /* empty */ +#endif + +#include "bignum.h" + +/***********************************************************************\ +* * +* Variable precision floating point numbers. * +* * +* Exponent is the place value of the low littlenum. E.g.: * +* If 0: low points to the units littlenum. * +* If 1: low points to the LITTLENUM_RADIX littlenum. * +* If -1: low points to the 1/LITTLENUM_RADIX littlenum. * +* * +\***********************************************************************/ + +/* JF: A sign value of 0 means we have been asked to assemble NaN + A sign value of 'P' means we've been asked to assemble +Inf + A sign value of 'N' means we've been asked to assemble -Inf + */ +struct FLONUM_STRUCT +{ + LITTLENUM_TYPE * low; /* low order littlenum of a bignum */ + LITTLENUM_TYPE * high; /* high order littlenum of a bignum */ + LITTLENUM_TYPE * leader; /* -> 1st non-zero littlenum */ + /* If flonum is 0.0, leader==low-1 */ + long exponent; /* base LITTLENUM_RADIX */ + char sign; /* '+' or '-' */ +}; + +typedef struct FLONUM_STRUCT FLONUM_TYPE; + + +/***********************************************************************\ +* * +* Since we can (& do) meet with exponents like 10^5000, it * +* is silly to make a table of ~ 10,000 entries, one for each * +* power of 10. We keep a table where item [n] is a struct * +* FLONUM_FLOATING_POINT representing 10^(2^n). We then * +* multiply appropriate entries from this table to get any * +* particular power of 10. For the example of 10^5000, a table * +* of just 25 entries suffices: 10^(2^-12)...10^(2^+12). * +* * +\***********************************************************************/ + + +extern const FLONUM_TYPE flonum_positive_powers_of_ten[]; +extern const FLONUM_TYPE flonum_negative_powers_of_ten[]; +extern const int table_size_of_flonum_powers_of_ten; + /* Flonum_XXX_powers_of_ten[] table has */ + /* legal indices from 0 to */ + /* + this number inclusive. */ + + + +/***********************************************************************\ +* * +* Declare worker functions. * +* * +\***********************************************************************/ + +#ifdef __STDC__ + +int atof_generic(char **address_of_string_pointer, + const char *string_of_decimal_marks, + const char *string_of_decimal_exponent_marks, + FLONUM_TYPE *address_of_generic_floating_point_number); + +void flonum_copy(FLONUM_TYPE *in, FLONUM_TYPE *out); +void flonum_multip(const FLONUM_TYPE *a, const FLONUM_TYPE *b, FLONUM_TYPE *product); + +#else /* __STDC__ */ + +int atof_generic(); +void flonum_copy(); +void flonum_multip(); + +#endif /* __STDC__ */ + +/***********************************************************************\ +* * +* Declare error codes. * +* * +\***********************************************************************/ + +#define ERROR_EXPONENT_OVERFLOW (2) + +/* end: flonum.h */ diff --git a/gas/frags.c b/gas/frags.c new file mode 100644 index 0000000..3526603 --- /dev/null +++ b/gas/frags.c @@ -0,0 +1,285 @@ +/* frags.c - manage frags - + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include "as.h" +#include "subsegs.h" +#include "obstack.h" + +struct obstack frags; /* All, and only, frags live here. */ + +fragS zero_address_frag = { + 0, /* fr_address */ + NULL, /* fr_next */ + 0, /* fr_fix */ + 0, /* fr_var */ + 0, /* fr_symbol */ + 0, /* fr_offset */ + NULL, /* fr_opcode */ + rs_fill, /* fr_type */ + 0, /* fr_subtype */ + 0, /* fr_pcrel_adjust */ + 0, /* fr_bsr */ + 0 /* fr_literal [0] */ +}; + +fragS bss_address_frag = { + 0, /* fr_address. Gets filled in to make up + sy_value-s. */ + NULL, /* fr_next */ + 0, /* fr_fix */ + 0, /* fr_var */ + 0, /* fr_symbol */ + 0, /* fr_offset */ + NULL, /* fr_opcode */ + rs_fill, /* fr_type */ + 0, /* fr_subtype */ + 0, /* fr_pcrel_adjust */ + 0, /* fr_bsr */ + 0 /* fr_literal [0] */ +}; + +/* + * frag_grow() + * + * Internal. + * Try to augment current frag by nchars chars. + * If there is no room, close of the current frag with a ".fill 0" + * and begin a new frag. Unless the new frag has nchars chars available + * do not return. Do not set up any fields of *now_frag. + */ +static void frag_grow(nchars) +unsigned int nchars; +{ + if (obstack_room (&frags) < nchars) { + unsigned int n,oldn; + long oldc; + + frag_wane(frag_now); + frag_new(0); + oldn=(unsigned)-1; + oldc=frags.chunk_size; + frags.chunk_size=2*nchars; + while((n=obstack_room(&frags))<nchars && n<oldn) { + frag_wane(frag_now); + frag_new(0); + oldn=n; + } + frags.chunk_size=oldc; + } + if (obstack_room (&frags) < nchars) + as_fatal("Can't extend frag %d. chars", nchars); +} /* frag_grow() */ + +/* + * frag_new() + * + * Call this to close off a completed frag, and start up a new (empty) + * frag, in the same subsegment as the old frag. + * [frchain_now remains the same but frag_now is updated.] + * Because this calculates the correct value of fr_fix by + * looking at the obstack 'frags', it needs to know how many + * characters at the end of the old frag belong to (the maximal) + * fr_var: the rest must belong to fr_fix. + * It doesn't actually set up the old frag's fr_var: you may have + * set fr_var == 1, but allocated 10 chars to the end of the frag: + * in this case you pass old_frags_var_max_size == 10. + * + * Make a new frag, initialising some components. Link new frag at end + * of frchain_now. + */ +void frag_new(old_frags_var_max_size) +int old_frags_var_max_size; /* Number of chars (already allocated on + obstack frags) */ + /* in variable_length part of frag. */ +{ + register fragS * former_last_fragP; +/* char *throw_away_pointer; JF unused */ + register frchainS * frchP; + long tmp; /* JF */ + + frag_now->fr_fix = (char *) (obstack_next_free (&frags)) - + (frag_now->fr_literal) - old_frags_var_max_size; + /* Fix up old frag's fr_fix. */ + + obstack_finish (&frags); + /* This will align the obstack so the */ + /* next struct we allocate on it will */ + /* begin at a correct boundary. */ + frchP = frchain_now; + know (frchP); + former_last_fragP = frchP->frch_last; + know (former_last_fragP); + know (former_last_fragP == frag_now); + obstack_blank (&frags, SIZEOF_STRUCT_FRAG); + /* We expect this will begin at a correct */ + /* boundary for a struct. */ + tmp=obstack_alignment_mask(&frags); + obstack_alignment_mask(&frags)=0; /* Turn off alignment */ + /* If we ever hit a machine + where strings must be + aligned, we Lose Big */ + frag_now=(fragS *)obstack_finish(&frags); + obstack_alignment_mask(&frags)=tmp; /* Restore alignment */ + + /* Just in case we don't get zero'd bytes */ + bzero(frag_now, SIZEOF_STRUCT_FRAG); + +/* obstack_unaligned_done (&frags, &frag_now); */ +/* know (frags.obstack_c_next_free == frag_now->fr_literal); */ + /* Generally, frag_now->points to an */ + /* address rounded up to next alignment. */ + /* However, characters will add to obstack */ + /* frags IMMEDIATELY after the struct frag, */ + /* even if they are not starting at an */ + /* alignment address. */ + former_last_fragP->fr_next = frag_now; + frchP->frch_last = frag_now; + frag_now->fr_next = NULL; +} /* frag_new() */ + +/* + * frag_more() + * + * Start a new frag unless we have n more chars of room in the current frag. + * Close off the old frag with a .fill 0. + * + * Return the address of the 1st char to write into. Advance + * frag_now_growth past the new chars. + */ + +char *frag_more (nchars) +int nchars; +{ + register char *retval; + + frag_grow (nchars); + retval = obstack_next_free (&frags); + obstack_blank_fast (&frags, nchars); + return (retval); +} /* frag_more() */ + +/* + * frag_var() + * + * Start a new frag unless we have max_chars more chars of room in the current frag. + * Close off the old frag with a .fill 0. + * + * Set up a machine_dependent relaxable frag, then start a new frag. + * Return the address of the 1st char of the var part of the old frag + * to write into. + */ + +char *frag_var(type, max_chars, var, subtype, symbol, offset, opcode) +relax_stateT type; +int max_chars; +int var; +relax_substateT subtype; +symbolS *symbol; +long offset; +char *opcode; +{ + register char *retval; + + frag_grow (max_chars); + retval = obstack_next_free (&frags); + obstack_blank_fast (&frags, max_chars); + frag_now->fr_var = var; + frag_now->fr_type = type; + frag_now->fr_subtype = subtype; + frag_now->fr_symbol = symbol; + frag_now->fr_offset = offset; + frag_now->fr_opcode = opcode; + /* default these to zero. */ + frag_now->fr_pcrel_adjust = 0; + frag_now->fr_bsr = 0; + frag_new (max_chars); + return (retval); +} /* frag_var() */ + +/* + * frag_variant() + * + * OVE: This variant of frag_var assumes that space for the tail has been + * allocated by caller. + * No call to frag_grow is done. + * Two new arguments have been added. + */ + +char *frag_variant(type, max_chars, var, subtype, symbol, offset, opcode, pcrel_adjust,bsr) + relax_stateT type; + int max_chars; + int var; + relax_substateT subtype; + symbolS *symbol; + long offset; + char *opcode; + int pcrel_adjust; + char bsr; +{ + register char *retval; + +/* frag_grow (max_chars); */ + retval = obstack_next_free (&frags); +/* obstack_blank_fast (&frags, max_chars); */ /* OVE: so far the only diff */ + frag_now->fr_var = var; + frag_now->fr_type = type; + frag_now->fr_subtype = subtype; + frag_now->fr_symbol = symbol; + frag_now->fr_offset = offset; + frag_now->fr_opcode = opcode; + frag_now->fr_pcrel_adjust = pcrel_adjust; + frag_now->fr_bsr = bsr; + frag_new (max_chars); + return (retval); +} /* frag_variant() */ + +/* + * frag_wane() + * + * Reduce the variable end of a frag to a harmless state. + */ +void frag_wane(fragP) +register fragS * fragP; +{ + fragP->fr_type = rs_fill; + fragP->fr_offset = 0; + fragP->fr_var = 0; +} + +/* + * frag_align() + * + * Make a frag for ".align foo,bar". Call is "frag_align (foo,bar);". + * Foo & bar are absolute integers. + * + * Call to close off the current frag with a ".align", then start a new + * (so far empty) frag, in the same subsegment as the last frag. + */ + +void frag_align(alignment, fill_character) +int alignment; +int fill_character; +{ + *(frag_var (rs_align, 1, 1, (relax_substateT)0, (symbolS *)0, + (long)alignment, (char *)0)) = fill_character; +} /* frag_align() */ + +/* end: frags.c */ diff --git a/gas/frags.h b/gas/frags.h new file mode 100644 index 0000000..aa08995 --- /dev/null +++ b/gas/frags.h @@ -0,0 +1,84 @@ +/* frags.h - Header file for the frag concept. + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +extern struct obstack frags; + /* Frags ONLY live in this obstack. */ + /* We use obstack_next_free() macro */ + /* so please don't put any other objects */ + /* on this stack! */ + +/* + * A macro to speed up appending exactly 1 char + * to current frag. + */ +/* JF changed < 1 to <= 1 to avoid a race conditon */ +#define FRAG_APPEND_1_CHAR(datum) \ +{ \ + if (obstack_room( &frags ) <= 1) {\ + frag_wane (frag_now); \ + frag_new (0); \ + } \ + obstack_1grow( &frags, datum ); \ +} + + +#ifdef __STDC__ + +char *frag_more(int nchars); +void frag_align(int alignment, int fill_character); +void frag_new(int old_frags_var_max_size); +void frag_wane(fragS *fragP); + +char *frag_variant(relax_stateT type, + int max_chars, + int var, + relax_substateT subtype, + symbolS *symbol, + long offset, + char *opcode, + int pcrel_adjust, + int bsr); + +char *frag_var(relax_stateT type, + int max_chars, + int var, + relax_substateT subtype, + symbolS *symbol, + long offset, + char *opcode); + +#else /* __STDC__ */ + +char *frag_more(); +char *frag_var(); +char *frag_variant(); +void frag_align(); +void frag_new(); +void frag_wane(); + +#endif /* __STDC__ */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end: frags.h */ diff --git a/gas/hash.c b/gas/hash.c new file mode 100644 index 0000000..b57ba9e --- /dev/null +++ b/gas/hash.c @@ -0,0 +1,990 @@ +/* hash.c - hash table lookup strings - + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +/* + * BUGS, GRIPES, APOLOGIA etc. + * + * A typical user doesn't need ALL this: I intend to make a library out + * of it one day - Dean Elsner. + * Also, I want to change the definition of a symbol to (address,length) + * so I can put arbitrary binary in the names stored. [see hsh.c for that] + * + * This slime is common coupled inside the module. Com-coupling (and other + * vandalism) was done to speed running time. The interfaces at the + * module's edges are adequately clean. + * + * There is no way to (a) run a test script through this heap and (b) + * compare results with previous scripts, to see if we have broken any + * code. Use GNU (f)utilities to do this. A few commands assist test. + * The testing is awkward: it tries to be both batch & interactive. + * For now, interactive rules! + */ + +/* + * The idea is to implement a symbol table. A test jig is here. + * Symbols are arbitrary strings; they can't contain '\0'. + * [See hsh.c for a more general symbol flavour.] + * Each symbol is associated with a char*, which can point to anything + * you want, allowing an arbitrary property list for each symbol. + * + * The basic operations are: + * + * new creates symbol table, returns handle + * find (symbol) returns char* + * insert (symbol,char*) error if symbol already in table + * delete (symbol) returns char* if symbol was in table + * apply so you can delete all symbols before die() + * die destroy symbol table (free up memory) + * + * Supplementary functions include: + * + * say how big? what % full? + * replace (symbol,newval) report previous value + * jam (symbol,value) assert symbol:=value + * + * You, the caller, have control over errors: this just reports them. + * + * This package requires malloc(), free(). + * Malloc(size) returns NULL or address of char[size]. + * Free(address) frees same. + */ + +/* + * The code and its structures are re-enterent. + * Before you do anything else, you must call hash_new() which will + * return the address of a hash-table-control-block (or NULL if there + * is not enough memory). You then use this address as a handle of the + * symbol table by passing it to all the other hash_...() functions. + * The only approved way to recover the memory used by the symbol table + * is to call hash_die() with the handle of the symbol table. + * + * Before you call hash_die() you normally delete anything pointed to + * by individual symbols. After hash_die() you can't use that symbol + * table again. + * + * The char* you associate with a symbol may not be NULL (0) because + * NULL is returned whenever a symbol is not in the table. Any other + * value is OK, except DELETED, #defined below. + * + * When you supply a symbol string for insertion, YOU MUST PRESERVE THE + * STRING until that symbol is deleted from the table. The reason is that + * only the address you supply, NOT the symbol string itself, is stored + * in the symbol table. + * + * You may delete and add symbols arbitrarily. + * Any or all symbols may have the same 'value' (char *). In fact, these + * routines don't do anything with your symbol values. + * + * You have no right to know where the symbol:char* mapping is stored, + * because it moves around in memory; also because we may change how it + * works and we don't want to break your code do we? However the handle + * (address of struct hash_control) is never changed in + * the life of the symbol table. + * + * What you CAN find out about a symbol table is: + * how many slots are in the hash table? + * how many slots are filled with symbols? + * (total hashes,collisions) for (reads,writes) (*) + * All of the above values vary in time. + * (*) some of these numbers will not be meaningful if we change the + * internals. + */ + +/* + * I N T E R N A L + * + * Hash table is an array of hash_entries; each entry is a pointer to a + * a string and a user-supplied value 1 char* wide. + * + * The array always has 2 ** n elements, n>0, n integer. + * There is also a 'wall' entry after the array, which is always empty + * and acts as a sentinel to stop running off the end of the array. + * When the array gets too full, we create a new array twice as large + * and re-hash the symbols into the new array, then forget the old array. + * (Of course, we copy the values into the new array before we junk the + * old array!) + * + */ + +#include <stdio.h> + +#ifndef FALSE +#define FALSE (0) +#define TRUE (!FALSE) +#endif /* no FALSE yet */ + +#include <ctype.h> +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#include "as.h" + +#define error as_fatal + +#define DELETED ((char *)1) /* guarenteed invalid address */ +#define START_POWER (11) /* power of two: size of new hash table *//* JF was 6 */ +/* JF These next two aren't used any more. */ +/* #define START_SIZE (64) / * 2 ** START_POWER */ +/* #define START_FULL (32) / * number of entries before table expands */ +#define islive(ptr) (ptr->hash_string && ptr->hash_string!=DELETED) + /* above TRUE if a symbol is in entry @ ptr */ + +#define STAT_SIZE (0) /* number of slots in hash table */ + /* the wall does not count here */ + /* we expect this is always a power of 2 */ +#define STAT_ACCESS (1) /* number of hash_ask()s */ +#define STAT__READ (0) /* reading */ +#define STAT__WRITE (1) /* writing */ +#define STAT_COLLIDE (3) /* number of collisions (total) */ + /* this may exceed STAT_ACCESS if we have */ + /* lots of collisions/access */ +#define STAT_USED (5) /* slots used right now */ +#define STATLENGTH (6) /* size of statistics block */ +#if STATLENGTH != HASH_STATLENGTH +Panic! Please make #include "stat.h" agree with previous definitions! +#endif + +/* #define SUSPECT to do runtime checks */ +/* #define TEST to be a test jig for hash...() */ + +#ifdef TEST /* TEST: use smaller hash table */ +#undef START_POWER +#define START_POWER (3) +#undef START_SIZE +#define START_SIZE (8) +#undef START_FULL +#define START_FULL (4) +#endif + +/*------------------ plan ---------------------------------- i = internal + +struct hash_control * c; +struct hash_entry * e; i +int b[z]; buffer for statistics + z size of b +char * s; symbol string (address) [ key ] +char * v; value string (address) [datum] +boolean f; TRUE if we found s in hash table i +char * t; error string; "" means OK +int a; access type [0...n) i + +c=hash_new () create new hash_control + +hash_die (c) destroy hash_control (and hash table) + table should be empty. + doesn't check if table is empty. + c has no meaning after this. + +hash_say (c,b,z) report statistics of hash_control. + also report number of available statistics. + +v=hash_delete (c,s) delete symbol, return old value if any. + ask() NULL means no old value. + f + +v=hash_replace (c,s,v) replace old value of s with v. + ask() NULL means no old value: no table change. + f + +t=hash_insert (c,s,v) insert (s,v) in c. + ask() return error string. + f it is an error to insert if s is already + in table. + if any error, c is unchanged. + +t=hash_jam (c,s,v) assert that new value of s will be v. i + ask() it may decide to GROW the table. i + f i + grow() i +t=hash_grow (c) grow the hash table. i + jam() will invoke JAM. i + +?=hash_apply (c,y) apply y() to every symbol in c. + y evtries visited in 'unspecified' order. + +v=hash_find (c,s) return value of s, or NULL if s not in c. + ask() + f + +f,e=hash_ask() (c,s,a) return slot where s SHOULD live. i + code() maintain collision stats in c. i + +.=hash_code (c,s) compute hash-code for s, i + from parameters of c. i + +*/ + +static char hash_found; /* returned by hash_ask() to stop extra */ + /* testing. hash_ask() wants to return both */ + /* a slot and a status. This is the status. */ + /* TRUE: found symbol */ + /* FALSE: absent: empty or deleted slot */ + /* Also returned by hash_jam(). */ + /* TRUE: we replaced a value */ + /* FALSE: we inserted a value */ + +static struct hash_entry * hash_ask(); +static int hash_code (); +static char * hash_grow(); + +/* + * h a s h _ n e w ( ) + * + */ +struct hash_control * +hash_new() /* create a new hash table */ + /* return NULL if failed */ + /* return handle (address of struct hash) */ +{ + register struct hash_control * retval; + register struct hash_entry * room; /* points to hash table */ + register struct hash_entry * wall; + register struct hash_entry * entry; + register int * ip; /* scan stats block of struct hash_control */ + register int * nd; /* limit of stats block */ + + if (( room = (struct hash_entry *) malloc( sizeof(struct + hash_entry)*((1<<START_POWER) + 1) ) ) != NULL) + /* +1 for the wall entry */ + { + if (( retval = (struct hash_control *) malloc(sizeof(struct + hash_control)) ) != NULL) + { + nd = retval->hash_stat + STATLENGTH; + for (ip=retval->hash_stat; ip<nd; ip++) + { + *ip = 0; + } + + retval -> hash_stat[STAT_SIZE] = 1<<START_POWER; + retval -> hash_mask = (1<<START_POWER) - 1; + retval -> hash_sizelog = START_POWER; + /* works for 1's compl ok */ + retval -> hash_where = room; + retval -> hash_wall = + wall = room + (1<<START_POWER); + retval -> hash_full = (1<<START_POWER)/2; + for (entry=room; entry<=wall; entry++) + { + entry->hash_string = NULL; + } + } + } + else + { + retval = NULL; /* no room for table: fake a failure */ + } + return(retval); /* return NULL or set-up structs */ +} + +/* + * h a s h _ d i e ( ) + * + * Table should be empty, but this is not checked. + * To empty the table, try hash_apply()ing a symbol deleter. + * Return to free memory both the hash table and it's control + * block. + * 'handle' has no meaning after this function. + * No errors are recoverable. + */ +void +hash_die(handle) + struct hash_control * handle; +{ + free((char *)handle->hash_where); + free((char *)handle); +} + +/* + * h a s h _ s a y ( ) + * + * Return the size of the statistics table, and as many statistics as + * we can until either (a) we have run out of statistics or (b) caller + * has run out of buffer. + * NOTE: hash_say treats all statistics alike. + * These numbers may change with time, due to insertions, deletions + * and expansions of the table. + * The first "statistic" returned is the length of hash_stat[]. + * Then contents of hash_stat[] are read out (in ascending order) + * until your buffer or hash_stat[] is exausted. + */ +void +hash_say(handle,buffer,bufsiz) + register struct hash_control * handle; + register int buffer[/*bufsiz*/]; + register int bufsiz; +{ + register int * nd; /* limit of statistics block */ + register int * ip; /* scan statistics */ + + ip = handle -> hash_stat; + nd = ip + min(bufsiz-1,STATLENGTH); + if (bufsiz>0) /* trust nothing! bufsiz<=0 is dangerous */ + { + *buffer++ = STATLENGTH; + for (; ip<nd; ip++,buffer++) + { + *buffer = *ip; + } + } +} + +/* + * h a s h _ d e l e t e ( ) + * + * Try to delete a symbol from the table. + * If it was there, return its value (and adjust STAT_USED). + * Otherwise, return NULL. + * Anyway, the symbol is not present after this function. + * + */ +char * /* NULL if string not in table, else */ + /* returns value of deleted symbol */ +hash_delete(handle,string) + register struct hash_control * handle; + register char * string; +{ + register char * retval; /* NULL if string not in table */ + register struct hash_entry * entry; /* NULL or entry of this symbol */ + + entry = hash_ask(handle,string,STAT__WRITE); + if (hash_found) + { + retval = entry -> hash_value; + entry -> hash_string = DELETED; /* mark as deleted */ + handle -> hash_stat[STAT_USED] -= 1; /* slots-in-use count */ +#ifdef SUSPECT + if (handle->hash_stat[STAT_USED]<0) + { + error("hash_delete"); + } +#endif /* def SUSPECT */ + } + else + { + retval = NULL; + } + return(retval); +} + +/* + * h a s h _ r e p l a c e ( ) + * + * Try to replace the old value of a symbol with a new value. + * Normally return the old value. + * Return NULL and don't change the table if the symbol is not already + * in the table. + */ +char * +hash_replace(handle,string,value) + register struct hash_control * handle; + register char * string; + register char * value; +{ + register struct hash_entry * entry; + register char * retval; + + entry = hash_ask(handle,string,STAT__WRITE); + if (hash_found) + { + retval = entry -> hash_value; + entry -> hash_value = value; + } + else + { + retval = NULL; + } + ; + return (retval); +} + +/* + * h a s h _ i n s e r t ( ) + * + * Insert a (symbol-string, value) into the hash table. + * Return an error string, "" means OK. + * It is an 'error' to insert an existing symbol. + */ + +char * /* return error string */ +hash_insert(handle,string,value) + register struct hash_control * handle; + register char * string; + register char * value; +{ + register struct hash_entry * entry; + register char * retval; + + retval = ""; + if (handle->hash_stat[STAT_USED] > handle->hash_full) + { + retval = hash_grow(handle); + } + if ( ! * retval) + { + entry = hash_ask(handle,string,STAT__WRITE); + if (hash_found) + { + retval = "exists"; + } + else + { + entry -> hash_value = value; + entry -> hash_string = string; + handle-> hash_stat[STAT_USED] += 1; + } + } + return(retval); +} + +/* + * h a s h _ j a m ( ) + * + * Regardless of what was in the symbol table before, after hash_jam() + * the named symbol has the given value. The symbol is either inserted or + * (its value is) relpaced. + * An error message string is returned, "" means OK. + * + * WARNING: this may decide to grow the hashed symbol table. + * To do this, we call hash_grow(), WHICH WILL recursively CALL US. + * + * We report status internally: hash_found is TRUE if we replaced, but + * false if we inserted. + */ +char * +hash_jam(handle,string,value) + register struct hash_control * handle; + register char * string; + register char * value; +{ + register char * retval; + register struct hash_entry * entry; + + retval = ""; + if (handle->hash_stat[STAT_USED] > handle->hash_full) + { + retval = hash_grow(handle); + } + if (! * retval) + { + entry = hash_ask(handle,string,STAT__WRITE); + if ( ! hash_found) + { + entry -> hash_string = string; + handle->hash_stat[STAT_USED] += 1; + } + entry -> hash_value = value; + } + return(retval); +} + +/* + * h a s h _ g r o w ( ) + * + * Grow a new (bigger) hash table from the old one. + * We choose to double the hash table's size. + * Return a human-scrutible error string: "" if OK. + * Warning! This uses hash_jam(), which had better not recurse + * back here! Hash_jam() conditionally calls us, but we ALWAYS + * call hash_jam()! + * Internal. + */ +static char * +hash_grow(handle) /* make a hash table grow */ + struct hash_control * handle; +{ + register struct hash_entry * newwall; + register struct hash_entry * newwhere; + struct hash_entry * newtrack; + register struct hash_entry * oldtrack; + register struct hash_entry * oldwhere; + register struct hash_entry * oldwall; + register int temp; + int newsize; + char * string; + char * retval; +#ifdef SUSPECT + int oldused; +#endif + + /* + * capture info about old hash table + */ + oldwhere = handle -> hash_where; + oldwall = handle -> hash_wall; +#ifdef SUSPECT + oldused = handle -> hash_stat[STAT_USED]; +#endif + /* + * attempt to get enough room for a hash table twice as big + */ + temp = handle->hash_stat[STAT_SIZE]; + if (( newwhere = (struct hash_entry *) + xmalloc((long)((temp+temp+1)*sizeof(struct hash_entry)))) != NULL) + /* +1 for wall slot */ + { + retval = ""; /* assume success until proven otherwise */ + /* + * have enough room: now we do all the work. + * double the size of everything in handle, + * note: hash_mask frob works for 1's & for 2's complement machines + */ + handle->hash_mask = handle->hash_mask + handle->hash_mask + 1; + handle->hash_stat[STAT_SIZE] <<= 1; + newsize = handle->hash_stat[STAT_SIZE]; + handle->hash_where = newwhere; + handle->hash_full <<= 1; + handle->hash_sizelog += 1; + handle->hash_stat[STAT_USED] = 0; + handle->hash_wall = + newwall = newwhere + newsize; + /* + * set all those pesky new slots to vacant. + */ + for (newtrack=newwhere; newtrack <= newwall; newtrack++) + { + newtrack -> hash_string = NULL; + } + /* + * we will do a scan of the old table, the hard way, using the + * new control block to re-insert the data into new hash table. + */ + handle -> hash_stat[STAT_USED] = 0; /* inserts will bump it up to correct */ + for (oldtrack=oldwhere; oldtrack < oldwall; oldtrack++) + { + if ( ((string=oldtrack->hash_string) != NULL) && string!=DELETED ) + { + if ( * (retval = hash_jam(handle,string,oldtrack->hash_value) ) ) + { + break; + } + } + } +#ifdef SUSPECT + if ( !*retval && handle->hash_stat[STAT_USED] != oldused) + { + retval = "hash_used"; + } +#endif + if (!*retval) + { + /* + * we have a completely faked up control block. + * return the old hash table. + */ + free((char *)oldwhere); + /* + * Here with success. retval is already "". + */ + } + } + else + { + retval = "no room"; + } + return(retval); +} + +/* + * h a s h _ a p p l y ( ) + * + * Use this to scan each entry in symbol table. + * For each symbol, this calls (applys) a nominated function supplying the + * symbol's value (and the symbol's name). + * The idea is you use this to destroy whatever is associted with + * any values in the table BEFORE you destroy the table with hash_die. + * Of course, you can use it for other jobs; whenever you need to + * visit all extant symbols in the table. + * + * We choose to have a call-you-back idea for two reasons: + * asthetic: it is a neater idea to use apply than an explicit loop + * sensible: if we ever had to grow the symbol table (due to insertions) + * then we would lose our place in the table when we re-hashed + * symbols into the new table in a different order. + * + * The order symbols are visited depends entirely on the hashing function. + * Whenever you insert a (symbol, value) you risk expanding the table. If + * you do expand the table, then the hashing function WILL change, so you + * MIGHT get a different order of symbols visited. In other words, if you + * want the same order of visiting symbols as the last time you used + * hash_apply() then you better not have done any hash_insert()s or + * hash_jam()s since the last time you used hash_apply(). + * + * In future we may use the value returned by your nominated function. + * One idea is to abort the scan if, after applying the function to a + * certain node, the function returns a certain code. + * To be safe, please make your functions of type char *. If you always + * return NULL, then the scan will complete, visiting every symbol in + * the table exactly once. ALL OTHER RETURNED VALUES have no meaning yet! + * Caveat Actor! + * + * The function you supply should be of the form: + * char * myfunct(string,value) + * char * string; |* the symbol's name *| + * char * value; |* the symbol's value *| + * { + * |* ... *| + * return(NULL); + * } + * + * The returned value of hash_apply() is (char*)NULL. In future it may return + * other values. NULL means "completed scan OK". Other values have no meaning + * yet. (The function has no graceful failures.) + */ +char * +hash_apply(handle,function) + struct hash_control * handle; + char* (*function)(); +{ + register struct hash_entry * entry; + register struct hash_entry * wall; + + wall = handle->hash_wall; + for (entry = handle->hash_where; entry < wall; entry++) + { + if (islive(entry)) /* silly code: tests entry->string twice! */ + { + (*function)(entry->hash_string,entry->hash_value); + } + } + return (NULL); +} + +/* + * h a s h _ f i n d ( ) + * + * Given symbol string, find value (if any). + * Return found value or NULL. + */ +char * +hash_find(handle,string) /* return char* or NULL */ + struct hash_control * handle; + char * string; +{ + register struct hash_entry * entry; + register char * retval; + + entry = hash_ask(handle,string,STAT__READ); + if (hash_found) + { + retval = entry->hash_value; + } + else + { + retval = NULL; + } + return(retval); +} + +/* + * h a s h _ a s k ( ) + * + * Searches for given symbol string. + * Return the slot where it OUGHT to live. It may be there. + * Return hash_found: TRUE only if symbol is in that slot. + * Access argument is to help keep statistics in control block. + * Internal. + */ +static struct hash_entry * /* string slot, may be empty or deleted */ +hash_ask(handle,string,access) + struct hash_control * handle; + char * string; + int access; /* access type */ +{ + register char *string1; /* JF avoid strcmp calls */ + register char * s; + register int c; + register struct hash_entry * slot; + register int collision; /* count collisions */ + + slot = handle->hash_where + hash_code(handle,string); /* start looking here */ + handle->hash_stat[STAT_ACCESS+access] += 1; + collision = 0; + hash_found = FALSE; + while ( ((s = slot->hash_string) != NULL) && s!=DELETED ) + { + for(string1=string;;) { + if((c= *s++) == 0) { + if(!*string1) + hash_found = TRUE; + break; + } + if(*string1++!=c) + break; + } + if(hash_found) + break; + collision++; + slot++; + } + /* + * slot: return: + * in use: we found string slot + * at empty: + * at wall: we fell off: wrap round ???? + * in table: dig here slot + * at DELETED: dig here slot + */ + if (slot==handle->hash_wall) + { + slot = handle->hash_where; /* now look again */ + while( ((s = slot->hash_string) != NULL) && s!=DELETED ) + { + for(string1=string;*s;string1++,s++) { + if(*string1!=*s) + break; + } + if(*s==*string1) { + hash_found = TRUE; + break; + } + collision++; + slot++; + } + /* + * slot: return: + * in use: we found it slot + * empty: wall: ERROR IMPOSSIBLE !!!! + * in table: dig here slot + * DELETED:dig here slot + */ + } +/* fprintf(stderr,"hash_ask(%s)->%d(%d)\n",string,hash_code(handle,string),collision); */ + handle -> hash_stat[STAT_COLLIDE+access] += collision; + return(slot); /* also return hash_found */ +} + +/* + * h a s h _ c o d e + * + * Does hashing of symbol string to hash number. + * Internal. + */ +static int +hash_code(handle,string) + struct hash_control * handle; + register char * string; +{ + register long h; /* hash code built here */ + register long c; /* each character lands here */ + register int n; /* Amount to shift h by */ + + n = (handle->hash_sizelog - 3); + h = 0; + while ((c = *string++) != 0) + { + h += c; + h = (h<<3) + (h>>n) + c; + } + return (h & handle->hash_mask); +} + +/* + * Here is a test program to exercise above. + */ +#ifdef TEST + +#define TABLES (6) /* number of hash tables to maintain */ + /* (at once) in any testing */ +#define STATBUFSIZE (12) /* we can have 12 statistics */ + +int statbuf[STATBUFSIZE]; /* display statistics here */ +char answer[100]; /* human farts here */ +char * hashtable[TABLES]; /* we test many hash tables at once */ +char * h; /* points to curent hash_control */ +char ** pp; +char * p; +char * name; +char * value; +int size; +int used; +char command; +int number; /* number 0:TABLES-1 of current hashed */ + /* symbol table */ + +main() +{ + char (*applicatee()); + char * hash_find(); + char * destroy(); + char * what(); + struct hash_control * hash_new(); + char * hash_replace(); + int * ip; + + number = 0; + h = 0; + printf("type h <RETURN> for help\n"); + for(;;) + { + printf("hash_test command: "); + gets(answer); + command = answer[0]; + if (isupper(command)) command = tolower(command); /* ecch! */ + switch (command) + { + case '#': + printf("old hash table #=%d.\n",number); + whattable(); + break; + case '?': + for (pp=hashtable; pp<hashtable+TABLES; pp++) + { + printf("address of hash table #%d control block is %xx\n" + ,pp-hashtable,*pp); + } + break; + case 'a': + hash_apply(h,applicatee); + break; + case 'd': + hash_apply(h,destroy); + hash_die(h); + break; + case 'f': + p = hash_find(h,name=what("symbol")); + printf("value of \"%s\" is \"%s\"\n",name,p?p:"NOT-PRESENT"); + break; + case 'h': + printf("# show old, select new default hash table number\n"); + printf("? display all hashtable control block addresses\n"); + printf("a apply a simple display-er to each symbol in table\n"); + printf("d die: destroy hashtable\n"); + printf("f find value of nominated symbol\n"); + printf("h this help\n"); + printf("i insert value into symbol\n"); + printf("j jam value into symbol\n"); + printf("n new hashtable\n"); + printf("r replace a value with another\n"); + printf("s say what %% of table is used\n"); + printf("q exit this program\n"); + printf("x delete a symbol from table, report its value\n"); + break; + case 'i': + p = hash_insert(h,name=what("symbol"),value=what("value")); + if (*p) + { + printf("symbol=\"%s\" value=\"%s\" error=%s\n",name,value,p); + } + break; + case 'j': + p = hash_jam(h,name=what("symbol"),value=what("value")); + if (*p) + { + printf("symbol=\"%s\" value=\"%s\" error=%s\n",name,value,p); + } + break; + case 'n': + h = hashtable[number] = (char *) hash_new(); + break; + case 'q': + exit(); + case 'r': + p = hash_replace(h,name=what("symbol"),value=what("value")); + printf("old value was \"%s\"\n",p?p:"{}"); + break; + case 's': + hash_say(h,statbuf,STATBUFSIZE); + for (ip=statbuf; ip<statbuf+STATBUFSIZE; ip++) + { + printf("%d ",*ip); + } + printf("\n"); + break; + case 'x': + p = hash_delete(h,name=what("symbol")); + printf("old value was \"%s\"\n",p?p:"{}"); + break; + default: + printf("I can't understand command \"%c\"\n",command); + break; + } + } +} + +char * +what(description) + char * description; +{ + char * retval; + char * malloc(); + + printf(" %s : ",description); + gets(answer); + /* will one day clean up answer here */ + retval = malloc(strlen(answer)+1); + if (!retval) + { + error("room"); + } + (void)strcpy(retval,answer); + return(retval); +} + +char * +destroy(string,value) + char * string; + char * value; +{ + free(string); + free(value); + return(NULL); +} + + +char * +applicatee(string,value) + char * string; + char * value; +{ + printf("%.20s-%.20s\n",string,value); + return(NULL); +} + +whattable() /* determine number: what hash table to use */ + /* also determine h: points to hash_control */ +{ + + for (;;) + { + printf(" what hash table (%d:%d) ? ",0,TABLES-1); + gets(answer); + sscanf(answer,"%d",&number); + if (number>=0 && number<TABLES) + { + h = hashtable[number]; + if (!h) + { + printf("warning: current hash-table-#%d. has no hash-control\n",number); + } + return; + } + else + { + printf("invalid hash table number: %d\n",number); + } + } +} + + + +#endif /* #ifdef TEST */ + +/* end: hash.c */ diff --git a/gas/hash.h b/gas/hash.h new file mode 100644 index 0000000..fb68fd3 --- /dev/null +++ b/gas/hash.h @@ -0,0 +1,59 @@ +/* hash.h - for hash.c + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef hashH +#define hashH + +struct hash_entry +{ + char * hash_string; /* points to where the symbol string is */ + /* NULL means slot is not used */ + /* DELETED means slot was deleted */ + char * hash_value; /* user's datum, associated with symbol */ +}; + + +#define HASH_STATLENGTH (6) +struct hash_control +{ + struct hash_entry * hash_where; /* address of hash table */ + int hash_sizelog; /* Log of ( hash_mask + 1 ) */ + int hash_mask; /* masks a hash into index into table */ + int hash_full; /* when hash_stat[STAT_USED] exceeds this, */ + /* grow table */ + struct hash_entry * hash_wall; /* point just after last (usable) entry */ + /* here we have some statistics */ + int hash_stat[HASH_STATLENGTH]; /* lies & statistics */ + /* we need STAT_USED & STAT_SIZE */ +}; + + +/* returns */ +struct hash_control * hash_new(); /* [control block] */ +void hash_die(); +void hash_say(); +char * hash_delete(); /* previous value */ +char * hash_relpace(); /* previous value */ +char * hash_insert(); /* error string */ +char * hash_apply(); /* 0 means OK */ +char * hash_find(); /* value */ +char * hash_jam(); /* error text (internal) */ +#endif /* #ifdef hashH */ + +/* end: hash.c */ diff --git a/gas/input-file.c b/gas/input-file.c new file mode 100644 index 0000000..cf1a4c3 --- /dev/null +++ b/gas/input-file.c @@ -0,0 +1,322 @@ +/* input_file.c - Deal with Input Files - + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +/* + * Confines all details of reading source bytes to this module. + * All O/S specific crocks should live here. + * What we lose in "efficiency" we gain in modularity. + * Note we don't need to #include the "as.h" file. No common coupling! + */ + +#ifdef USG +#define setbuffer(stream, buf, size) setvbuf((stream), (buf), _IOFBF, (size)) +#endif + +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#include "as.h" +#include "input-file.h" + +/* This variable is non-zero if the file currently being read should be + preprocessed by app. It is zero if the file can be read straight in. + */ +int preprocess = 0; + +/* + * This code opens a file, then delivers BUFFER_SIZE character + * chunks of the file on demand. + * BUFFER_SIZE is supposed to be a number chosen for speed. + * The caller only asks once what BUFFER_SIZE is, and asks before + * the nature of the input files (if any) is known. + */ + +#define BUFFER_SIZE (32 * 1024) + +/* + * We use static data: the data area is not sharable. + */ + +FILE *f_in; +/* static JF remove static so app.c can use file_name */ +char * file_name; + +/* Struct for saving the state of this module for file includes. */ +struct saved_file { + FILE *f_in; + char *file_name; + int preprocess; + char *app_save; +}; + +/* These hooks accomodate most operating systems. */ + +void input_file_begin() { + f_in = (FILE *)0; +} + +void input_file_end () { } + + /* Return BUFFER_SIZE. */ +int input_file_buffer_size() { + return (BUFFER_SIZE); +} + +int input_file_is_open() { + return f_in!=(FILE *)0; +} + +/* Push the state of our input, returning a pointer to saved info that + can be restored with input_file_pop (). */ +char *input_file_push () { + register struct saved_file *saved; + + saved = (struct saved_file *)xmalloc (sizeof *saved); + + saved->f_in = f_in; + saved->file_name = file_name; + saved->preprocess = preprocess; + if (preprocess) + saved->app_save = app_push (); + + input_file_begin (); /* Initialize for new file */ + + return (char *)saved; +} + +void +input_file_pop (arg) + char *arg; +{ + register struct saved_file *saved = (struct saved_file *)arg; + + input_file_end (); /* Close out old file */ + + f_in = saved->f_in; + file_name = saved->file_name; + preprocess = saved->preprocess; + if (preprocess) + app_pop (saved->app_save); + + free(arg); +} + +#ifdef DONTDEF /* JF save old version in case we need it */ +void +input_file_open (filename, preprocess, debugging) + char * filename; /* "" means use stdin. Must not be 0. */ + int preprocess; /* TRUE if needs app. */ + int debugging; /* TRUE if we are debugging assembler. */ +{ + assert( filename != 0 ); /* Filename may not be NULL. */ + if (filename [0]) + { /* We have a file name. Suck it and see. */ + file_handle = open (filename, O_RDONLY, 0); + file_name = filename; + } + else + { /* use stdin for the input file. */ + file_handle = fileno (stdin); + file_name = "{standard input}"; /* For error messages. */ + } + if (file_handle < 0) + as_perror ("Can't open %s for reading", file_name); + if ( preprocess ) + { +/* + * This code was written in haste for a frobbed BSD 4.2. + * I have a flight to catch: will someone please do proper + * error checks? - Dean. + */ + int pid; + char temporary_file_name [12]; + int fd; + union wait status; + + (void)strcpy (temporary_file_name, "#appXXXXXX"); + (void)mktemp (temporary_file_name); + pid = vfork (); + if (pid == -1) + { + as_perror ("Vfork failed", file_name); + _exit (144); + } + if (pid == 0) + { + (void)dup2 (file_handle, fileno(stdin)); + fd = open (temporary_file_name, O_WRONLY + O_TRUNC + O_CREAT, 0666); + if (fd == -1) + { + (void)write(2,"Can't open temporary\n",21); + _exit (99); + } + (void)dup2 (fd, fileno(stdout)); +/* JF for testing #define PREPROCESSOR "/lib/app" */ +#define PREPROCESSOR "./app" + execl (PREPROCESSOR, PREPROCESSOR, 0); + execl ("app","app",0); + (void)write(2,"Exec of app failed. Get help.\n",31); + (void)unlink(temporary_file_name); + _exit (11); + } + (void)wait (& status); + if (status.w_status & 0xFF00) /* JF was 0xF000, was wrong */ + { + file_handle = -1; + as_bad( "Can't preprocess file \"%s\", status = %xx", file_name, status.w_status ); + } + else + { + file_handle = open (temporary_file_name, O_RDONLY, 0); + if ( ! debugging && unlink(temporary_file_name)) + as_perror ("Can't delete temp file %s", temporary_file_name); + } + if (file_handle == -1) + as_perror ("Can't retrieve temp file %s", temporary_file_name); + } +} +#else + +void +input_file_open (filename,pre) + char * filename; /* "" means use stdin. Must not be 0. */ + int pre; +{ + int c; + char buf[80]; + + preprocess = pre; + + assert( filename != 0 ); /* Filename may not be NULL. */ + if (filename [0]) { /* We have a file name. Suck it and see. */ + f_in=fopen(filename,"r"); + file_name=filename; + } else { /* use stdin for the input file. */ + f_in = stdin; + file_name = "{standard input}"; /* For error messages. */ + } + if (f_in==(FILE *)0) { + as_perror ("Can't open %s for reading", file_name); + return; + } + +#ifndef VMS + /* Ask stdio to buffer our input at BUFFER_SIZE, with a dynamically + allocated buffer. */ + setvbuf(f_in, (char *)NULL, _IOFBF, BUFFER_SIZE); +#endif /* VMS */ + + c = getc(f_in); + if (c == '#') { /* Begins with comment, may not want to preprocess */ + c = getc(f_in); + if (c == 'N') { + fgets(buf,80,f_in); + if (!strcmp(buf,"O_APP\n")) + preprocess=0; + if (!strchr(buf,'\n')) + ungetc('#',f_in); /* It was longer */ + else + ungetc('\n',f_in); + } else if(c=='\n') + ungetc('\n',f_in); + else + ungetc('#',f_in); + } else + ungetc(c,f_in); + +#ifdef DONTDEF + if ( preprocess ) { + char temporary_file_name [17]; + FILE *f_out; + + (void)strcpy (temporary_file_name, "/tmp/#appXXXXXX"); + (void)mktemp (temporary_file_name); + f_out=fopen(temporary_file_name,"w+"); + if(f_out==(FILE *)0) + as_perror("Can't open temp file %s",temporary_file_name); + + /* JF this will have to be moved on any system that + does not support removal of open files. */ + (void)unlink(temporary_file_name);/* JF do it NOW */ + do_scrub(f_in,f_out); + (void)fclose(f_in); /* All done with it */ + (void)rewind(f_out); + f_in=f_out; + } +#endif +} +#endif + +/* Close input file. */ +void input_file_close() { + fclose (f_in); + f_in = 0; +} + +char * +input_file_give_next_buffer (where) + char * where; /* Where to place 1st character of new buffer. */ +{ + char * return_value; /* -> Last char of what we read, + 1. */ + register int size; + + if (f_in == (FILE *)0) + return 0; + /* + * fflush (stdin); could be done here if you want to synchronise + * stdin and stdout, for the case where our input file is stdin. + * Since the assembler shouldn't do any output to stdout, we + * don't bother to synch output and input. + */ + if(preprocess) { + char *p; + int n; + int ch; + extern FILE *scrub_file; + + scrub_file=f_in; + for (p = where, n = BUFFER_SIZE; n; --n) { + + ch = do_scrub_next_char(scrub_from_file, scrub_to_file); + if (ch == EOF) + break; + *p++=ch; + } + size=BUFFER_SIZE-n; + } else + size= fread(where,sizeof(char),BUFFER_SIZE,f_in); + if (size < 0) + { + as_perror ("Can't read from %s", file_name); + size = 0; + } + if (size) + return_value = where + size; + else + { + if (fclose (f_in)) + as_perror ("Can't close %s", file_name); + f_in = (FILE *)0; + return_value = 0; + } + return (return_value); +} diff --git a/gas/input-file.h b/gas/input-file.h new file mode 100644 index 0000000..703d4c5 --- /dev/null +++ b/gas/input-file.h @@ -0,0 +1,84 @@ +/* input_file.h header for input-file.c + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/*"input_file.c":Operating-system dependant functions to read source files.*/ + + +/* + * No matter what the operating system, this module must provide the + * following services to its callers. + * + * input_file_begin() Call once before anything else. + * + * input_file_end() Call once after everything else. + * + * input_file_buffer_size() Call anytime. Returns largest possible + * delivery from + * input_file_give_next_buffer(). + * + * input_file_open(name) Call once for each input file. + * + * input_file_give_next_buffer(where) Call once to get each new buffer. + * Return 0: no more chars left in file, + * the file has already been closed. + * Otherwise: return a pointer to just + * after the last character we read + * into the buffer. + * If we can only read 0 characters, then + * end-of-file is faked. + * + * input_file_push() Push state, which can be restored + * later. Does implicit input_file_begin. + * Returns char * to saved state. + * + * input_file_pop (arg) Pops previously saved state. + * + * input_file_close () Closes opened file. + * + * All errors are reported (using as_perror) so caller doesn't have to think + * about I/O errors. No I/O errors are fatal: an end-of-file may be faked. + */ + +#ifdef __STDC__ + +char *input_file_give_next_buffer(char *where); +char *input_file_push(void); +int input_file_buffer_size(void); +int input_file_is_open(void); +void input_file_begin(void); +void input_file_close(void); +void input_file_end(void); +void input_file_open(char *filename, int pre); +void input_file_pop(char *arg); + +#else /* __STDC__ */ + +char *input_file_give_next_buffer(); +char *input_file_push(); +int input_file_buffer_size(); +int input_file_is_open(); +void input_file_begin(); +void input_file_close(); +void input_file_end(); +void input_file_open(); +void input_file_pop(); + +#endif /* __STDC__ */ + +/* end: input_file.h */ diff --git a/gas/input-scrub.c b/gas/input-scrub.c new file mode 100644 index 0000000..a710913 --- /dev/null +++ b/gas/input-scrub.c @@ -0,0 +1,478 @@ +/* input_scrub.c - Break up input buffers into whole numbers of lines. + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include <errno.h> /* Need this to make errno declaration right */ +#include "as.h" +#include "input-file.h" + +/* + * O/S independent module to supply buffers of sanitised source code + * to rest of assembler. We get sanitized input data of arbitrary length. + * We break these buffers on line boundaries, recombine pieces that + * were broken across buffers, and return a buffer of full lines to + * the caller. + * The last partial line begins the next buffer we build and return to caller. + * The buffer returned to caller is preceeded by BEFORE_STRING and followed + * by AFTER_STRING, as sentinels. The last character before AFTER_STRING + * is a newline. + * Also looks after line numbers, for e.g. error messages. + */ + +/* + * We don't care how filthy our buffers are, but our callers assume + * that the following sanitation has already been done. + * + * No comments, reduce a comment to a space. + * Reduce a tab to a space unless it is 1st char of line. + * All multiple tabs and spaces collapsed into 1 char. Tab only + * legal if 1st char of line. + * # line file statements converted to .line x;.file y; statements. + * Escaped newlines at end of line: remove them but add as many newlines + * to end of statement as you removed in the middle, to synch line numbers. + */ + +#define BEFORE_STRING ("\n") +#define AFTER_STRING ("\0") /* bcopy of 0 chars might choke. */ +#define BEFORE_SIZE (1) +#define AFTER_SIZE (1) + +static char * buffer_start; /*->1st char of full buffer area. */ +static char * partial_where; /*->after last full line in buffer. */ +static int partial_size; /* >=0. Number of chars in partial line in buffer. */ +static char save_source [AFTER_SIZE]; + /* Because we need AFTER_STRING just after last */ + /* full line, it clobbers 1st part of partial */ + /* line. So we preserve 1st part of partial */ + /* line here. */ +static int buffer_length; /* What is the largest size buffer that */ + /* input_file_give_next_buffer() could */ + /* return to us? */ + +/* Saved information about the file that .include'd this one. When we + hit EOF, we automatically pop to that file. */ + +static char *next_saved_file; + +/* +We can have more than one source file open at once, though the info for +all but the latest one are saved off in a struct input_save. These +files remain open, so we are limited by the number of open files allowed +by the underlying OS. +We may also sequentially read more than one source file in an assembly. + */ + + +/* +We must track the physical file and line number for error messages. +We also track a "logical" file and line number corresponding to (C?) +compiler source line numbers. +Whenever we open a file we must fill in physical_input_file. So if it is NULL +we have not opened any files yet. + */ + +static +char * physical_input_file, + * logical_input_file; + + + +typedef unsigned int line_numberT; /* 1-origin line number in a source file. */ + /* A line ends in '\n' or eof. */ + +static +line_numberT physical_input_line, + logical_input_line; + +/* Struct used to save the state of the input handler during include files */ +struct input_save { + char *buffer_start; + char *partial_where; + int partial_size; + char save_source [AFTER_SIZE]; + int buffer_length; + char *physical_input_file; + char *logical_input_file; + line_numberT physical_input_line; + line_numberT logical_input_line; + char *next_saved_file; /* Chain of input_saves */ + char *input_file_save; /* Saved state of input routines */ + char *saved_position; /* Caller's saved position in buf */ +}; + +#ifdef __STDC__ +static void as_1_char(unsigned int c, FILE *stream); +#else /* __STDC__ */ +static void as_1_char(); +#endif /* __STDC__ */ + +/* Push the state of input reading and scrubbing so that we can #include. + The return value is a 'void *' (fudged for old compilers) to a save + area, which can be restored by passing it to input_scrub_pop(). */ +char * +input_scrub_push(saved_position) + char *saved_position; +{ + register struct input_save *saved; + + saved = (struct input_save *) xmalloc(sizeof *saved); + + saved->saved_position = saved_position; + saved->buffer_start = buffer_start; + saved->partial_where = partial_where; + saved->partial_size = partial_size; + saved->buffer_length = buffer_length; + saved->physical_input_file = physical_input_file; + saved->logical_input_file = logical_input_file; + saved->physical_input_line = physical_input_line; + saved->logical_input_line = logical_input_line; + bcopy (saved->save_source, save_source, sizeof (save_source)); + saved->next_saved_file = next_saved_file; + saved->input_file_save = input_file_push (); + + input_scrub_begin (); /* Reinitialize! */ + + return (char *)saved; +} + +char * +input_scrub_pop (arg) + char *arg; +{ + register struct input_save *saved; + char *saved_position; + + input_scrub_end (); /* Finish off old buffer */ + + saved = (struct input_save *)arg; + + input_file_pop (saved->input_file_save); + saved_position = saved->saved_position; + buffer_start = saved->buffer_start; + buffer_length = saved->buffer_length; + physical_input_file = saved->physical_input_file; + logical_input_file = saved->logical_input_file; + physical_input_line = saved->physical_input_line; + logical_input_line = saved->logical_input_line; + partial_where = saved->partial_where; + partial_size = saved->partial_size; + next_saved_file = saved->next_saved_file; + bcopy (save_source, saved->save_source, sizeof (save_source)); + + free(arg); + return saved_position; +} + + +void +input_scrub_begin () +{ + know(strlen(BEFORE_STRING) == BEFORE_SIZE); + know(strlen(AFTER_STRING) == AFTER_SIZE + || (AFTER_STRING[0] == '\0' && AFTER_SIZE == 1)); + + input_file_begin (); + + buffer_length = input_file_buffer_size (); + + buffer_start = xmalloc((long)(BEFORE_SIZE + buffer_length + buffer_length + AFTER_SIZE)); + bcopy (BEFORE_STRING, buffer_start, (int)BEFORE_SIZE); + + /* Line number things. */ + logical_input_line = 0; + logical_input_file = (char *)NULL; + physical_input_file = NULL; /* No file read yet. */ + next_saved_file = NULL; /* At EOF, don't pop to any other file */ + do_scrub_begin(); +} + +void +input_scrub_end () +{ + if (buffer_start) + { + free (buffer_start); + buffer_start = 0; + input_file_end (); + } +} + +/* Start reading input from a new file. */ + +char * /* Return start of caller's part of buffer. */ +input_scrub_new_file (filename) + char * filename; +{ + input_file_open (filename, !flagseen['f']); + physical_input_file = filename[0] ? filename : "{standard input}"; + physical_input_line = 0; + + partial_size = 0; + return (buffer_start + BEFORE_SIZE); +} + + +/* Include a file from the current file. Save our state, cause it to + be restored on EOF, and begin handling a new file. Same result as + input_scrub_new_file. */ + +char * +input_scrub_include_file (filename, position) + char *filename; + char *position; +{ + next_saved_file = input_scrub_push (position); + return input_scrub_new_file (filename); +} + +void +input_scrub_close () +{ + input_file_close (); +} +char * +input_scrub_next_buffer (bufp) +char **bufp; +{ + register char * limit; /*->just after last char of buffer. */ + + *bufp = buffer_start + BEFORE_SIZE; + +#ifdef DONTDEF + if(preprocess) { + if(save_buffer) { + *bufp = save_buffer; + save_buffer = 0; + } + limit = input_file_give_next_buffer(buffer_start+BEFORE_SIZE); + if (!limit) { + partial_where = 0; + if(partial_size) + as_warn("Partial line at end of file ignored"); + return partial_where; + } + + if(partial_size) + bcopy(save_source, partial_where,(int)AFTER_SIZE); + do_scrub(partial_where,partial_size,buffer_start+BEFORE_SIZE,limit-(buffer_start+BEFORE_SIZE),&out_string,&out_length); + limit=out_string + out_length; + for(p=limit;*--p!='\n';) + ; + p++; + if(p<=buffer_start+BEFORE_SIZE) + as_fatal("Source line too long. Please change file '%s' and re-make the assembler.", __FILE__); + + partial_where = p; + partial_size = limit-p; + bcopy(partial_where, save_source,(int)AFTER_SIZE); + bcopy(AFTER_STRING, partial_where, (int)AFTER_SIZE); + + save_buffer = *bufp; + *bufp = out_string; + + return partial_where; + } + + /* We're not preprocessing. Do the right thing */ +#endif + if (partial_size) + { + bcopy (partial_where, buffer_start + BEFORE_SIZE, (int)partial_size); + bcopy (save_source, buffer_start + BEFORE_SIZE, (int)AFTER_SIZE); + } + limit = input_file_give_next_buffer (buffer_start + BEFORE_SIZE + partial_size); + if (limit) + { + register char * p; /* Find last newline. */ + + for (p = limit; * -- p != '\n';) + { + } + ++ p; + if (p <= buffer_start + BEFORE_SIZE) + { + as_fatal("Source line too long. Please change file %s then rebuild assembler.", __FILE__); + } + partial_where = p; + partial_size = limit - p; + bcopy (partial_where, save_source, (int)AFTER_SIZE); + bcopy (AFTER_STRING, partial_where, (int)AFTER_SIZE); + } + else + { + partial_where = 0; + if (partial_size > 0) + { + as_warn("Partial line at end of file ignored"); + } + /* If we should pop to another file at EOF, do it. */ + if (next_saved_file) + { + *bufp = input_scrub_pop (next_saved_file); /* Pop state */ + /* partial_where is now correct to return, since we popped it. */ + } + } + return (partial_where); +} + +/* + * The remaining part of this file deals with line numbers, error + * messages and so on. + */ + + +int +seen_at_least_1_file () /* TRUE if we opened any file. */ +{ + return (physical_input_file != NULL); +} + +void +bump_line_counters () +{ + ++ physical_input_line; + ++ logical_input_line; +} + +/* + * new_logical_line() + * + * Tells us what the new logical line number and file are. + * If the line_number is <0, we don't change the current logical line number. + * If the fname is NULL, we don't change the current logical file name. + */ +void new_logical_line(fname, line_number) + char *fname; /* DON'T destroy it! We point to it! */ + int line_number; +{ + if (fname) { + logical_input_file = fname; + } /* if we have a file name */ + + if (line_number >= 0) { + logical_input_line = line_number; + } /* if we have a line number */ +} /* new_logical_line() */ + +/* + * a s _ w h e r e () + * + * Write a line to stderr locating where we are in reading + * input source files. + * As a sop to the debugger of AS, pretty-print the offending line. + */ +void +as_where() +{ + char *p; + line_numberT line; + + if (physical_input_file) + { /* we tried to read SOME source */ + if (input_file_is_open()) + { /* we can still read lines from source */ +#ifdef DONTDEF + fprintf (stderr," @ physical line %ld., file \"%s\"", + (long) physical_input_line, physical_input_file); + fprintf (stderr," @ logical line %ld., file \"%s\"\n", + (long) logical_input_line, logical_input_file); + (void)putc(' ', stderr); + as_howmuch (stderr); + (void)putc('\n', stderr); +#else + p = logical_input_file ? logical_input_file : physical_input_file; + line = logical_input_line ? logical_input_line : physical_input_line; + fprintf(stderr,"%s:%u: ", p, line); +#endif + } + else + { +#ifdef DONTDEF + fprintf (stderr," After reading source.\n"); +#else + p = logical_input_file ? logical_input_file : physical_input_file; + line = logical_input_line ? logical_input_line : physical_input_line; + fprintf(stderr, "%s:%d:", p, (int) line); +#endif + } + } + else + { +#ifdef DONTDEF + fprintf (stderr," Before reading source.\n"); +#else +#endif + } +} + + + + +/* + * a s _ h o w m u c h () + * + * Output to given stream how much of line we have scanned so far. + * Assumes we have scanned up to and including input_line_pointer. + * No free '\n' at end of line. + */ +void +as_howmuch (stream) + FILE * stream; /* Opened for write please. */ +{ + register char * p; /* Scan input line. */ + /* register char c; JF unused */ + + for (p = input_line_pointer - 1; * p != '\n'; --p) + { + } + ++ p; /* p->1st char of line. */ + for (; p <= input_line_pointer; p++) + { + /* Assume ASCII. EBCDIC & other micro-computer char sets ignored. */ + /* c = *p & 0xFF; JF unused */ + as_1_char(*p, stream); + } +} + +static void as_1_char (c,stream) +unsigned int c; +FILE *stream; +{ + if (c > 127) + { + (void)putc('%', stream); + c -= 128; + } + if (c < 32) + { + (void)putc('^', stream); + c += '@'; + } + (void)putc(c, stream); +} + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end: input_scrub.c */ diff --git a/gas/messages.c b/gas/messages.c new file mode 100644 index 0000000..90e1f95 --- /dev/null +++ b/gas/messages.c @@ -0,0 +1,391 @@ +/* messages.c - error reporter - + Copyright (C) 1987, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +#include <stdio.h> /* define stderr */ +#include <errno.h> + +#include "as.h" + +#ifndef NO_STDARG +#include <stdarg.h> +#else +#ifndef NO_VARARGS +#include <varargs.h> +#endif /* NO_VARARGS */ +#endif /* NO_STDARG */ + +/* + * Despite the rest of the comments in this file, (FIXME-SOON), + * here is the current scheme for error messages etc: + * + * as_fatal() is used when gas is quite confused and + * continuing the assembly is pointless. In this case we + * exit immediately with error status. + * + * as_bad() is used to mark errors that result in what we + * presume to be a useless object file. Say, we ignored + * something that might have been vital. If we see any of + * these, assembly will continue to the end of the source, + * no object file will be produced, and we will terminate + * with error status. The new option, -Z, tells us to + * produce an object file anyway but we still exit with + * error status. The assumption here is that you don't want + * this object file but we could be wrong. + * + * as_warn() is used when we have an error from which we + * have a plausible error recovery. eg, masking the top + * bits of a constant that is longer than will fit in the + * destination. In this case we will continue to assemble + * the source, although we may have made a bad assumption, + * and we will produce an object file and return normal exit + * status (ie, no error). The new option -X tells us to + * treat all as_warn() errors as as_bad() errors. That is, + * no object file will be produced and we will exit with + * error status. The idea here is that we don't kill an + * entire make because of an error that we knew how to + * correct. On the other hand, sometimes you might want to + * stop the make at these points. + * + * as_tsktsk() is used when we see a minor error for which + * our error recovery action is almost certainly correct. + * In this case, we print a message and then assembly + * continues as though no error occurred. + */ + +/* + ERRORS + + JF: this is now bogus. We now print more standard error messages + that try to look like everyone else's. + + We print the error message 1st, beginning in column 1. + All ancillary info starts in column 2 on lines after the + key error text. + We try to print a location in logical and physical file + just after the main error text. + Caller then prints any appendices after that, begining all + lines with at least 1 space. + + Optionally, we may die. + There is no need for a trailing '\n' in your error text format + because we supply one. + + as_warn(fmt,args) Like fprintf(stderr,fmt,args) but also call errwhere(). + + as_fatal(fmt,args) Like as_warn() but exit with a fatal status. + + */ + +static int warning_count = 0; /* Count of number of warnings issued */ + +int had_warnings() { + return(warning_count); +} /* had_err() */ + +/* Nonzero if we've hit a 'bad error', and should not write an obj file, + and exit with a nonzero error code */ + +static int error_count = 0; + +int had_errors() { + return(error_count); +} /* had_errors() */ + + +/* + * a s _ p e r r o r + * + * Like perror(3), but with more info. + */ +void as_perror(gripe, filename) +char *gripe; /* Unpunctuated error theme. */ +char *filename; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + + as_where(); + fprintf(stderr,gripe,filename); + + if (errno > sys_nerr) + fprintf(stderr, "Unknown error #%d.\n", errno); + else + fprintf(stderr, "%s.\n", sys_errlist[errno]); + errno = 0; /* After reporting, clear it. */ +} /* as_perror() */ + +/* + * a s _ t s k t s k () + * + * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a warning, and locate warning + * in input file(s). + * Please only use this for when we have some recovery action. + * Please explain in string (which may have '\n's) what recovery was done. + */ + +#ifndef NO_STDARG +void as_tsktsk(Format) +const char *Format; +{ + va_list args; + + as_where(); + va_start(args, Format); + vfprintf(stderr, Format, args); + va_end(args); + (void) putc('\n', stderr); +} /* as_tsktsk() */ +#else +#ifndef NO_VARARGS +void as_tsktsk(Format,va_alist) +char *Format; +va_dcl +{ + va_list args; + + as_where(); + va_start(args); + vfprintf(stderr, Format, args); + va_end(args); + (void) putc('\n', stderr); +} /* as_tsktsk() */ +#else +/*VARARGS1 */ +as_tsktsk(Format,args) +char *Format; +{ + as_where(); + _doprnt (Format, &args, stderr); + (void)putc ('\n', stderr); + /* as_where(); */ +} /* as_tsktsk */ +#endif /* not NO_VARARGS */ +#endif /* not NO_STDARG */ + +#ifdef DONTDEF +void as_tsktsk(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an) +char *format; +{ + as_where(); + fprintf(stderr,Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an); + (void)putc('\n',stderr); +} /* as_tsktsk() */ +#endif +/* + * a s _ w a r n () + * + * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a warning, and locate warning + * in input file(s). + * Please only use this for when we have some recovery action. + * Please explain in string (which may have '\n's) what recovery was done. + */ + +#ifndef NO_STDARG +void as_warn(Format) +const char *Format; +{ + va_list args; + + if(!flagseen['W']) { + ++warning_count; + as_where(); + va_start(args, Format); + vfprintf(stderr, Format, args); + va_end(args); + (void) putc('\n', stderr); + } +} /* as_warn() */ +#else +#ifndef NO_VARARGS +void as_warn(Format,va_alist) +char *Format; +va_dcl +{ + va_list args; + + if(!flagseen['W']) { + ++warning_count; + as_where(); + va_start(args); + vfprintf(stderr, Format, args); + va_end(args); + (void) putc('\n', stderr); + } +} /* as_warn() */ +#else +/*VARARGS1 */ +as_warn(Format,args) +char *Format; +{ + /* -W supresses warning messages. */ + if (! flagseen ['W']) { + ++warning_count; + as_where(); + _doprnt (Format, &args, stderr); + (void)putc ('\n', stderr); + /* as_where(); */ + } +} /* as_warn() */ +#endif /* not NO_VARARGS */ +#endif /* not NO_STDARG */ + +#ifdef DONTDEF +void as_warn(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an) +char *format; +{ + if(!flagseen['W']) { + ++warning_count; + as_where(); + fprintf(stderr,Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an); + (void)putc('\n',stderr); + } +} /* as_warn() */ +#endif +/* + * a s _ b a d () + * + * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a warning, + * and locate warning in input file(s). + * Please us when there is no recovery, but we want to continue processing + * but not produce an object file. + * Please explain in string (which may have '\n's) what recovery was done. + */ + +#ifndef NO_STDARG +void as_bad(Format) +const char *Format; +{ + va_list args; + + ++error_count; + as_where(); + va_start(args, Format); + vfprintf(stderr, Format, args); + va_end(args); + (void) putc('\n', stderr); +} /* as_bad() */ +#else +#ifndef NO_VARARGS +void as_bad(Format,va_alist) +char *Format; +va_dcl +{ + va_list args; + + ++error_count; + as_where(); + va_start(args); + vfprintf(stderr, Format, args); + va_end(args); + (void) putc('\n', stderr); +} /* as_bad() */ +#else +/*VARARGS1 */ +as_bad(Format,args) +char *Format; +{ + ++error_count; + as_where(); + _doprnt (Format, &args, stderr); + (void)putc ('\n', stderr); + /* as_where(); */ +} /* as_bad() */ +#endif /* not NO_VARARGS */ +#endif /* not NO_STDARG */ + +#ifdef DONTDEF +void as_bad(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an) +char *format; +{ + ++error_count; + as_where(); + fprintf(stderr,Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an); + (void)putc('\n',stderr); +} /* as_bad() */ +#endif + +/* + * a s _ f a t a l () + * + * Send to stderr a string (with bell) (JF: Bell is obnoxious!) as a fatal + * message, and locate stdsource in input file(s). + * Please only use this for when we DON'T have some recovery action. + * It exit()s with a warning status. + */ + +#ifndef NO_STDARG +void as_fatal(Format) +const char *Format; +{ + va_list args; + + as_where(); + va_start(args, Format); + fprintf (stderr, "FATAL:"); + vfprintf(stderr, Format, args); + (void) putc('\n', stderr); + va_end(args); + exit(42); +} /* as_fatal() */ +#else +#ifndef NO_VARARGS +void as_fatal(Format,va_alist) +char *Format; +va_dcl +{ + va_list args; + + as_where(); + va_start(args); + fprintf (stderr, "FATAL:"); + vfprintf(stderr, Format, args); + (void) putc('\n', stderr); + va_end(args); + exit(42); +} /* as_fatal() */ +#else +/*VARARGS1 */ +as_fatal(Format, args) +char *Format; +{ + as_where(); + fprintf(stderr,"FATAL:"); + _doprnt (Format, &args, stderr); + (void)putc ('\n', stderr); + /* as_where(); */ + exit(42); /* What is a good exit status? */ +} /* as_fatal() */ +#endif /* not NO_VARARGS */ +#endif /* not NO_STDARG */ + +#ifdef DONTDEF +void as_fatal(Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an) +char *Format; +{ + as_where(); + fprintf (stderr, "FATAL:"); + fprintf(stderr, Format,aa,ab,ac,ad,ae,af,ag,ah,ai,aj,ak,al,am,an); + (void) putc('\n', stderr); + exit(42); +} /* as_fatal() */ +#endif + +/* end: messages.c */ diff --git a/gas/obj.h b/gas/obj.h new file mode 100644 index 0000000..68e4243 --- /dev/null +++ b/gas/obj.h @@ -0,0 +1,67 @@ +/* obj.h - defines the object dependent hooks for all object + format backends. + + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#ifdef __STDC__ + +char *obj_default_output_file_name(void); +void obj_crawl_symbol_chain(object_headers *headers); +void obj_emit_relocations(char **where, fixS *fixP, relax_addressT segment_address_in_file); +void obj_emit_strings(char **where); +void obj_emit_symbols(char **where, symbolS *symbol_rootP); +void obj_header_append(char **where, object_headers *headers); +void obj_read_begin_hook(void); +void obj_symbol_new_hook(symbolS *symbolP); +void obj_symbol_to_chars(char **where, symbolS *symbolP); + +#ifndef obj_pre_write_hook +void obj_pre_write_hook(object_headers *headers); +#endif /* obj_pre_write_hook */ + +#else + +char *obj_default_output_file_name(); +void obj_crawl_symbol_chain(); +void obj_emit_relocations(); +void obj_emit_strings(); +void obj_emit_symbols(); +void obj_header_append(); +void obj_read_begin_hook(); +void obj_symbol_new_hook(); +void obj_symbol_to_chars(); + +#ifndef obj_pre_write_hook +void obj_pre_write_hook(); +#endif /* obj_pre_write_hook */ + +#endif /* __STDC__ */ + +extern const pseudo_typeS obj_pseudo_table[]; + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of obj.h */ diff --git a/gas/output-file.c b/gas/output-file.c new file mode 100644 index 0000000..423bab2 --- /dev/null +++ b/gas/output-file.c @@ -0,0 +1,83 @@ +/* output-file.c - Deal with the output file + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +/* + * Confines all details of emitting object bytes to this module. + * All O/S specific crocks should live here. + * What we lose in "efficiency" we gain in modularity. + * Note we don't need to #include the "as.h" file. No common coupling! + */ + + /* note that we do need config info. xoxorich. */ + +/* #include "style.h" */ +#include <stdio.h> + +#include "as.h" + +#include "output-file.h" + +static FILE *stdoutput; + +void output_file_create(name) +char *name; +{ + if(name[0]=='-' && name[1]=='\0') + stdoutput=stdout; + else if ( ! (stdoutput = fopen( name, "w" )) ) + { + as_perror ("FATAL: Can't create %s", name); + exit(42); + } +} /* output_file_create() */ + + + +void output_file_close(filename) +char *filename; +{ + if ( EOF == fclose( stdoutput ) ) + { + as_perror ("FATAL: Can't close %s", filename); + exit(42); + } + stdoutput = NULL; /* Trust nobody! */ +} /* output_file_close() */ + +void output_file_append(where, length, filename) +char *where; +long length; +char *filename; +{ + + for (; length; length--,where++) + { + (void)putc(*where,stdoutput); + if(ferror(stdoutput)) + /* if ( EOF == (putc( *where, stdoutput )) ) */ + { + as_perror("Failed to emit an object byte", filename); + as_fatal("Can't continue"); + } + } +} /* output_file_append() */ + +/* end: output-file.c */ diff --git a/gas/output-file.h b/gas/output-file.h new file mode 100644 index 0000000..f5c8073 --- /dev/null +++ b/gas/output-file.h @@ -0,0 +1,18 @@ + + +#ifdef __STDC__ + +void output_file_append(char *where, long length, char *filename); +void output_file_close(char *filename); +void output_file_create(char *name); + +#else /* __STDC__ */ + +void output_file_append(); +void output_file_close(); +void output_file_create(); + +#endif /* __STDC__ */ + + +/* end of output-file.h */ diff --git a/gas/read.c b/gas/read.c new file mode 100644 index 0000000..818f139 --- /dev/null +++ b/gas/read.c @@ -0,0 +1,2281 @@ +/* read.c - read a source file - + Copyright (C) 1986, 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#define MASK_CHAR (0xFF) /* If your chars aren't 8 bits, you will + change this a bit. But then, GNU isn't + spozed to run on your machine anyway. + (RMS is so shortsighted sometimes.) + */ + +#define MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT (16) + /* This is the largest known floating point */ + /* format (for now). It will grow when we */ + /* do 4361 style flonums. */ + + +/* Routines that read assembler source text to build spagetti in memory. */ +/* Another group of these functions is in the as-expr.c module */ + +#include "as.h" + +#include "obstack.h" + +char *input_line_pointer; /*->next char of source file to parse. */ + + +#if BITS_PER_CHAR != 8 +The following table is indexed by [ (char) ] and will break if +a char does not have exactly 256 states (hopefully 0:255!) ! +#endif + +const char /* used by is_... macros. our ctype[] */ +lex_type [256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ABCDEFGHIJKLMNO */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* PQRSTUVWXYZ[\]^_ */ + 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, /* _!"#$%&'()*+,-./ */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0123456789:;<=>? */ + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* @ABCDEFGHIJKLMNO */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, /* PQRSTUVWXYZ[\]^_ */ + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* `abcdefghijklmno */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, /* pqrstuvwxyz{|}~. */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +/* + * In: a character. + * Out: 1 if this character ends a line. + */ +#define _ (0) +char is_end_of_line [256] = { +#ifdef CR_EOL + _, _, _, _, _, _, _, _, _, _,99, _, _, 99, _, _,/* @abcdefghijklmno */ +#else + _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, _, /* @abcdefghijklmno */ +#endif + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, /* 0123456789:;<=>? */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _ /* */ +}; +#undef _ + + /* Functions private to this file. */ + +char line_comment_chars[1]; +char line_separator_chars[1]; + +static char *buffer; /* 1st char of each buffer of lines is here. */ +static char *buffer_limit; /*->1 + last char in buffer. */ + +static char *bignum_low; /* Lowest char of bignum. */ +static char *bignum_limit; /* 1st illegal address of bignum. */ +static char *bignum_high; /* Highest char of bignum. */ + /* May point to (bignum_start-1). */ + /* Never >= bignum_limit. */ +static char *old_buffer = 0; /* JF a hack */ +static char *old_input; +static char *old_limit; + +/* Variables for handling include file directory list. */ + +char **include_dirs; /* List of pointers to directories to + search for .include's */ +int include_dir_count; /* How many are in the list */ +int include_dir_maxlen = 1; /* Length of longest in list */ + +#ifndef WORKING_DOT_WORD +struct broken_word *broken_words; +int new_broken_words = 0; +#endif + +#ifdef __STDC__ + +static char *demand_copy_string(int *lenP); +static int is_it_end_of_statement(void); +static unsigned int next_char_of_string(void); +static segT get_known_segmented_expression(expressionS *expP); +static void grow_bignum(void); +static void pobegin(void); +static void stringer(int append_zero); + +#else /* __STDC__ */ + +static char *demand_copy_string(); +static int is_it_end_of_statement(); +static unsigned int next_char_of_string(); +static segT get_known_segmented_expression(); +static void grow_bignum(); +static void pobegin(); +static void stringer(); + +#endif /* __STDC__ */ + + +void +read_begin() +{ + char *p; + + pobegin(); + obj_read_begin_hook(); + + obstack_begin(¬es, 5000); + /* Start off assuming that we won't need more than 20 levels + of .if/.endif; if we need more, we can always get it. */ + obstack_begin (&cond_obstack, 20); + /* We start life accepting input. */ + obstack_1grow (&cond_obstack, 1); + +#define BIGNUM_BEGIN_SIZE (16) + bignum_low = xmalloc((long)BIGNUM_BEGIN_SIZE); + bignum_limit = bignum_low + BIGNUM_BEGIN_SIZE; + + /* Use machine dependent syntax */ + for (p = line_separator_chars; *p; p++) + is_end_of_line[*p] = 1; + /* Use more. FIXME-SOMEDAY. */ +} + +/* set up pseudo-op tables */ + +struct hash_control * +po_hash = NULL; /* use before set up: NULL->address error */ + +#ifdef DONTDEF +void s_gdbline(), s_gdblinetab(); +void s_gdbbeg(), s_gdbblock(), s_gdbend(), s_gdbsym(); +#endif + +static const pseudo_typeS +potable[] = +{ + { "abort", s_abort, 0 }, + { "align", s_align_ptwo, 0 }, + { "ascii", stringer, 0 }, + { "asciz", stringer, 1 }, +/* block */ + { "byte", cons, 1 }, + { "comm", s_comm, 0 }, + { "data", s_data, 0 }, +/* dim */ + { "double", float_cons, 'd' }, +/* dsect */ + { "eject", s_ignore, 0 }, /* Formfeed listing */ + { "else", s_else, 0 }, + { "end", s_end, 0 }, + { "endif", s_endif, 0 }, +/* endef */ + { "equ", s_set, 0 }, +/* err */ +/* extend */ + { "extern", s_ignore, 0 }, /* We treat all undef as ext */ + { "app-file", s_app_file, 0 }, + { "file", s_app_file, 0 }, + { "fill", s_fill, 0 }, + { "float", float_cons, 'f' }, +#ifdef DONTDEF + { "gdbbeg", s_gdbbeg, 0 }, + { "gdbblock", s_gdbblock, 0 }, + { "gdbend", s_gdbend, 0 }, + { "gdbsym", s_gdbsym, 0 }, + { "gdbline", s_gdbline, 0 }, + { "gdblinetab",s_gdblinetab, 0 }, +#endif + { "global", s_globl, 0 }, + { "globl", s_globl, 0 }, + { "hword", cons, 2 }, + { "if", s_if, 0 }, + { "ifdef", s_ifdef, 0 }, + { "ifeqs", s_ifeqs, 0 }, + { "ifndef", s_ifdef, 1 }, + { "ifnes", s_ifeqs, 1 }, + { "ifnotdef", s_ifdef, 1 }, + { "include", s_include, 0 }, + { "int", cons, 4 }, + { "lcomm", s_lcomm, 0 }, + { "lflags", s_ignore, 0 }, /* Listing flags */ + { "list", s_ignore, 0 }, /* Turn listing on */ + { "long", cons, 4 }, + { "lsym", s_lsym, 0 }, + { "nolist", s_ignore, 0 }, /* Turn listing off */ + { "octa", big_cons, 16 }, + { "org", s_org, 0 }, +/* print */ + { "quad", big_cons, 8 }, + { "sbttl", s_ignore, 0 }, /* Subtitle of listing */ +/* scl */ +/* sect */ + { "set", s_set, 0 }, + { "short", cons, 2 }, + { "single", float_cons, 'f' }, +/* size */ + { "space", s_space, 0 }, +/* tag */ + { "text", s_text, 0 }, + { "title", s_ignore, 0 }, /* Listing title */ +/* type */ +/* use */ +/* val */ + { "word", cons, 2 }, + { NULL} /* end sentinel */ +}; + +static void pobegin() { + char * errtxt; /* error text */ + const pseudo_typeS * pop; + + po_hash = hash_new(); + + /* Do the target-specific pseudo ops. */ + for (pop=md_pseudo_table; pop->poc_name; pop++) { + errtxt = hash_insert (po_hash, pop->poc_name, (char *)pop); + if (errtxt && *errtxt) { + as_fatal("error constructing md pseudo-op table"); + } /* on error */ + } /* for each op */ + + /* Now object specific. Skip any that were in the target table. */ + for (pop=obj_pseudo_table; pop->poc_name; pop++) { + errtxt = hash_insert (po_hash, pop->poc_name, (char *)pop); + if (errtxt && *errtxt) { + if (!strcmp (errtxt, "exists")) { +#ifdef DIE_ON_OVERRIDES + as_fatal("pseudo op \".%s\" overridden.\n", pop->poc_name); +#endif /* DIE_ON_OVERRIDES */ + continue; /* OK if target table overrides. */ + } else { + as_fatal("error constructing obj pseudo-op table"); + } /* if overridden */ + } /* on error */ + } /* for each op */ + + /* Now portable ones. Skip any that we've seen already. */ + for (pop=potable; pop->poc_name; pop++) { + errtxt = hash_insert (po_hash, pop->poc_name, (char *)pop); + if (errtxt && *errtxt) { + if (!strcmp (errtxt, "exists")) { +#ifdef DIE_ON_OVERRIDES + as_fatal("pseudo op \".%s\" overridden.\n", pop->poc_name); +#endif /* DIE_ON_OVERRIDES */ + continue; /* OK if target table overrides. */ + } else { + as_fatal("error constructing obj pseudo-op table"); + } /* if overridden */ + } /* on error */ + } /* for each op */ + + return; +} /* pobegin() */ + +#define HANDLE_CONDITIONAL_ASSEMBLY() \ + if (ignore_input ()) \ + { \ + while (! is_end_of_line[*input_line_pointer++]) \ + if (input_line_pointer == buffer_limit) \ + break; \ + continue; \ + } + +/* read_a_source_file() + * + * We read the file, putting things into a web that + * represents what we have been reading. + */ +void read_a_source_file(name) +char *name; +{ + register char c; + register char * s; /* string of symbol, '\0' appended */ + register int temp; + /* register struct frag * fragP; JF unused */ /* a frag we just made */ + pseudo_typeS *pop; +#ifdef DONTDEF + void gdb_block_beg(); + void gdb_block_position(); + void gdb_block_end(); + void gdb_symbols_fixup(); +#endif + + buffer = input_scrub_new_file(name); + + while ((buffer_limit = input_scrub_next_buffer(&input_line_pointer)) != 0) { /* We have another line to parse. */ + know(buffer_limit[-1] == '\n'); /* Must have a sentinel. */ + contin: /* JF this goto is my fault I admit it. Someone brave please re-write + the whole input section here? Pleeze??? */ + while (input_line_pointer < buffer_limit) { /* We have more of this buffer to parse. */ + /* + * We now have input_line_pointer->1st char of next line. + * If input_line_pointer [-1] == '\n' then we just + * scanned another line: so bump line counters. + */ + if (input_line_pointer[-1] == '\n') { + bump_line_counters(); + } /* just passed a newline */ + /* + * We are at the begining of a line, or similar place. + * We expect a well-formed assembler statement. + * A "symbol-name:" is a statement. + * + * Depending on what compiler is used, the order of these tests + * may vary to catch most common case 1st. + * Each test is independent of all other tests at the (top) level. + * PLEASE make a compiler that doesn't use this assembler. + * It is crufty to waste a compiler's time encoding things for this + * assembler, which then wastes more time decoding it. + * (And communicating via (linear) files is silly! + * If you must pass stuff, please pass a tree!) + */ + if ((c = *input_line_pointer++) == '\t' || c == ' ' || c=='\f') { + c = *input_line_pointer++; + } + know(c != ' '); /* No further leading whitespace. */ + /* + * C is the 1st significant character. + * Input_line_pointer points after that character. + */ + if (is_name_beginner(c)) { /* want user-defined label or pseudo/opcode */ + HANDLE_CONDITIONAL_ASSEMBLY(); + + s = --input_line_pointer; + c = get_symbol_end(); /* name's delimiter */ + /* + * C is character after symbol. + * That character's place in the input line is now '\0'. + * S points to the beginning of the symbol. + * [In case of pseudo-op, s->'.'.] + * Input_line_pointer->'\0' where c was. + */ + if (c == ':') { + colon(s); /* user-defined label */ + * input_line_pointer ++ = ':'; /* Put ':' back for error messages' sake. */ + /* Input_line_pointer->after ':'. */ + SKIP_WHITESPACE(); + } else if (c == '=' || input_line_pointer[1] == '=') { /* JF deal with FOO=BAR */ + equals(s); + demand_empty_rest_of_line(); + } else { /* expect pseudo-op or machine instruction */ + if (*s == '.') { + /* + * PSEUDO - OP. + * + * WARNING: c has next char, which may be end-of-line. + * We lookup the pseudo-op table with s+1 because we + * already know that the pseudo-op begins with a '.'. + */ + + pop = (pseudo_typeS *) hash_find(po_hash, s+1); + + /* Print the error msg now, while we still can */ + if (!pop) { + as_bad("Unknown pseudo-op: `%s'",s); + *input_line_pointer = c; + s_ignore(0); + break; + } + + /* Put it back for error messages etc. */ + *input_line_pointer = c; + /* The following skip of whitespace is compulsory. */ + /* A well shaped space is sometimes all that separates keyword from operands. */ + if (c == ' ' || c == '\t') { + input_line_pointer++; + } /* Skip seperator after keyword. */ + /* + * Input_line is restored. + * Input_line_pointer->1st non-blank char + * after pseudo-operation. + */ + if (!pop) { + ignore_rest_of_line(); + break; + } else { + (*pop->poc_handler)(pop->poc_val); + } /* if we have one */ + } else { /* machine instruction */ + /* WARNING: c has char, which may be end-of-line. */ + /* Also: input_line_pointer->`\0` where c was. */ + * input_line_pointer = c; + while (!is_end_of_line[*input_line_pointer]) { + input_line_pointer++; + } + c = *input_line_pointer; + *input_line_pointer = '\0'; + md_assemble(s); /* Assemble 1 instruction. */ + *input_line_pointer++ = c; + /* We resume loop AFTER the end-of-line from this instruction */ + } /* if (*s=='.') */ + } /* if c==':' */ + continue; + } /* if (is_name_beginner(c) */ + + + if (is_end_of_line [c]) { + continue; + } /* empty statement */ + + if (isdigit(c)) { /* local label ("4:") */ + HANDLE_CONDITIONAL_ASSEMBLY (); + + temp = c - '0'; +#ifdef LOCAL_LABELS_DOLLAR + if (*input_line_pointer=='$') + input_line_pointer++; +#endif + if (* input_line_pointer ++ == ':') + { + local_colon (temp); + } + else + { + as_bad("Spurious digit %d.", temp); + input_line_pointer -- ; + ignore_rest_of_line(); + } + continue; + } /* local label ("4:") */ + + if (c && strchr(line_comment_chars,c)) { /* Its a comment. Better say APP or NO_APP */ + char *ends; + char *new_buf; + char *new_tmp; + int new_length; + char *tmp_buf = 0; + extern char *scrub_string,*scrub_last_string; + + bump_line_counters(); + s=input_line_pointer; + if (strncmp(s,"APP\n",4)) + continue; /* We ignore it */ + s+=4; + + ends=strstr(s,"#NO_APP\n"); + + if (!ends) { + int tmp_len; + int num; + + /* The end of the #APP wasn't in this buffer. We + keep reading in buffers until we find the #NO_APP + that goes with this #APP There is one. The specs + guarentee it. . . */ + tmp_len=buffer_limit-s; + tmp_buf=xmalloc(tmp_len); + bcopy(s,tmp_buf,tmp_len); + do { + new_tmp = input_scrub_next_buffer(&buffer); + if (!new_tmp) + break; + else + buffer_limit = new_tmp; + input_line_pointer = buffer; + ends = strstr(buffer,"#NO_APP\n"); + if (ends) + num=ends-buffer; + else + num=buffer_limit-buffer; + + tmp_buf = xrealloc(tmp_buf, tmp_len + num); + bcopy(buffer,tmp_buf+tmp_len,num); + tmp_len+=num; + } while(!ends); + + input_line_pointer= ends ? ends+8 : NULL; + + s=tmp_buf; + ends=s+tmp_len; + + } else { + input_line_pointer=ends+8; + } + new_buf=xmalloc(100); + new_length=100; + new_tmp=new_buf; + + scrub_string=s; + scrub_last_string = ends; + for(;;) { + int ch; + + ch = do_scrub_next_char(scrub_from_string, scrub_to_string); + if (ch==EOF) break; + *new_tmp++=ch; + if (new_tmp==new_buf+new_length) { + new_buf=xrealloc(new_buf,new_length+100); + new_tmp=new_buf+new_length; + new_length+=100; + } + } + + if (tmp_buf) + free(tmp_buf); + old_buffer=buffer; + old_input=input_line_pointer; + old_limit=buffer_limit; + buffer=new_buf; + input_line_pointer=new_buf; + buffer_limit=new_tmp; + continue; + } + + HANDLE_CONDITIONAL_ASSEMBLY(); + + /* as_warn("Junk character %d.",c); Now done by ignore_rest */ + input_line_pointer--; /* Report unknown char as ignored. */ + ignore_rest_of_line(); + } /* while (input_line_pointer<buffer_limit) */ + if (old_buffer) { + bump_line_counters(); + if (old_input != 0) { + buffer=old_buffer; + input_line_pointer=old_input; + buffer_limit=old_limit; + old_buffer = 0; + goto contin; + } + } + } /* while (more buffers to scan) */ + input_scrub_close(); /* Close the input file */ +} /* read_a_source_file() */ + +void s_abort() { + as_fatal(".abort detected. Abandoning ship."); +} /* s_abort() */ + +/* For machines where ".align 4" means align to a 4 byte boundary. */ +void s_align_bytes(arg) +int arg; +{ + register unsigned int temp; + register long temp_fill; + unsigned int i = 0; + unsigned long max_alignment = 1 << 15; + + if (is_end_of_line[*input_line_pointer]) + temp = arg; /* Default value from pseudo-op table */ + else + temp = get_absolute_expression (); + + if (temp > max_alignment) { + as_bad("Alignment too large: %d. assumed.", temp = max_alignment); + } + + /* + * For the sparc, `.align (1<<n)' actually means `.align n' + * so we have to convert it. + */ + if (temp != 0) { + for (i = 0; (temp & 1) == 0; temp >>= 1, ++i) + ; + } + if (temp != 1) + as_bad("Alignment not a power of 2"); + + temp = i; + if (*input_line_pointer == ',') { + input_line_pointer ++; + temp_fill = get_absolute_expression (); + } else { + temp_fill = 0; + } + /* Only make a frag if we HAVE to. . . */ + if (temp && ! need_pass_2) + frag_align(temp, (int)temp_fill); + + demand_empty_rest_of_line(); +} /* s_align_bytes() */ + +/* For machines where ".align 4" means align to 2**4 boundary. */ +void s_align_ptwo() { + register int temp; + register long temp_fill; + long max_alignment = 15; + + temp = get_absolute_expression (); + if (temp > max_alignment) + as_bad("Alignment too large: %d. assumed.", temp = max_alignment); + else if (temp < 0) { + as_bad("Alignment negative. 0 assumed."); + temp = 0; + } + if (*input_line_pointer == ',') { + input_line_pointer ++; + temp_fill = get_absolute_expression (); + } else + temp_fill = 0; + /* Only make a frag if we HAVE to. . . */ + if (temp && ! need_pass_2) + frag_align (temp, (int)temp_fill); + + record_alignment(now_seg, temp); + + demand_empty_rest_of_line(); +} /* s_align_ptwo() */ + +void s_comm() { + register char *name; + register char c; + register char *p; + register int temp; + register symbolS * symbolP; + + name = input_line_pointer; + c = get_symbol_end(); + /* just after name is now '\0' */ + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE(); + if (*input_line_pointer != ',') { + as_bad("Expected comma after symbol-name: rest of line ignored."); + ignore_rest_of_line(); + return; + } + input_line_pointer ++; /* skip ',' */ + if ((temp = get_absolute_expression()) < 0) { + as_warn(".COMMon length (%d.) <0! Ignored.", temp); + ignore_rest_of_line(); + return; + } + *p = 0; + symbolP = symbol_find_or_make(name); + *p = c; + if (S_IS_DEFINED(symbolP)) { + as_bad("Ignoring attempt to re-define symbol"); + ignore_rest_of_line(); + return; + } + if (S_GET_VALUE(symbolP)) { + if (S_GET_VALUE(symbolP) != temp) + as_bad("Length of .comm \"%s\" is already %d. Not changed to %d.", + S_GET_NAME(symbolP), + S_GET_VALUE(symbolP), + temp); + } else { + S_SET_VALUE(symbolP, temp); + S_SET_EXTERNAL(symbolP); + } +#ifdef VMS + if (!temp) + symbolP->sy_other = const_flag; +#endif + know(symbolP->sy_frag == &zero_address_frag); + demand_empty_rest_of_line(); +} /* s_comm() */ + +void +s_data() +{ + register int temp; + + temp = get_absolute_expression (); + subseg_new (SEG_DATA, (subsegT)temp); +#ifdef VMS + const_flag = 0; +#endif + demand_empty_rest_of_line(); +} + +void s_app_file() { + register char *s; + int length; + + /* Some assemblers tolerate immediately following '"' */ + if ((s = demand_copy_string(&length)) != 0) { + new_logical_line(s, -1); + demand_empty_rest_of_line(); + } +#ifdef OBJ_COFF + c_dot_file_symbol(s); +#endif /* OBJ_COFF */ +} /* s_app_file() */ + +void s_fill() { + long temp_repeat; + long temp_size; + register long temp_fill; + char *p; + + if (get_absolute_expression_and_terminator(& temp_repeat) != ',') { + input_line_pointer --; /* Backup over what was not a ','. */ + as_bad("Expect comma after rep-size in .fill:"); + ignore_rest_of_line(); + return; + } + if (get_absolute_expression_and_terminator(& temp_size) != ',') { + input_line_pointer --; /* Backup over what was not a ','. */ + as_bad("Expected comma after size in .fill"); + ignore_rest_of_line(); + return; + } + /* + * This is to be compatible with BSD 4.2 AS, not for any rational reason. + */ +#define BSD_FILL_SIZE_CROCK_8 (8) + if (temp_size > BSD_FILL_SIZE_CROCK_8) { + as_bad(".fill size clamped to %d.", BSD_FILL_SIZE_CROCK_8); + temp_size = BSD_FILL_SIZE_CROCK_8 ; + } if (temp_size < 0) { + as_warn("Size negative: .fill ignored."); + temp_size = 0; + } else if (temp_repeat <= 0) { + as_warn("Repeat < 0, .fill ignored"); + temp_size = 0; + } + temp_fill = get_absolute_expression (); + if (temp_size && !need_pass_2) { + p = frag_var(rs_fill, (int)temp_size, (int)temp_size, (relax_substateT)0, (symbolS *)0, temp_repeat, (char *)0); + bzero (p, (int)temp_size); +/* + * The magic number BSD_FILL_SIZE_CROCK_4 is from BSD 4.2 VAX flavoured AS. + * The following bizzare behaviour is to be compatible with above. + * I guess they tried to take up to 8 bytes from a 4-byte expression + * and they forgot to sign extend. Un*x Sux. + */ +#define BSD_FILL_SIZE_CROCK_4 (4) + md_number_to_chars (p, temp_fill, temp_size > BSD_FILL_SIZE_CROCK_4 ? BSD_FILL_SIZE_CROCK_4 : (int)temp_size); +/* + * Note: .fill (),0 emits no frag (since we are asked to .fill 0 bytes) + * but emits no error message because it seems a legal thing to do. + * It is a degenerate case of .fill but could be emitted by a compiler. + */ + } + demand_empty_rest_of_line(); +} + +#ifdef DONTDEF +void +s_gdbbeg() +{ + register int temp; + + temp = get_absolute_expression (); + if (temp < 0) + as_warn("Block number <0. Ignored."); + else if (flagseen ['G']) + gdb_block_beg ((long) temp, frag_now, (long)(obstack_next_free(& frags) - frag_now->fr_literal)); + demand_empty_rest_of_line (); +} + +void +s_gdbblock() +{ + register int position; + int temp; + + if (get_absolute_expression_and_terminator (&temp) != ',') { + as_bad("expected comma before position in .gdbblock"); + --input_line_pointer; + ignore_rest_of_line (); + return; + } + position = get_absolute_expression (); + if (flagseen ['G']) + gdb_block_position ((long) temp, (long) position); + demand_empty_rest_of_line (); +} + +void +s_gdbend() +{ + register int temp; + + temp = get_absolute_expression (); + if (temp < 0) + as_warn("Block number <0. Ignored."); + else if (flagseen ['G']) + gdb_block_end ((long) temp, frag_now, (long)(obstack_next_free(& frags) - frag_now->fr_literal)); + demand_empty_rest_of_line (); +} + +void +s_gdbsym() +{ + register char *name, + *p; + register char c; + register symbolS * symbolP; + register int temp; + + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + symbolP = symbol_find_or_make(name); + *p = c; + SKIP_WHITESPACE(); + if (* input_line_pointer != ',') { + as_bad("Expected comma after name"); + ignore_rest_of_line(); + return; + } + input_line_pointer ++; + if ((temp = get_absolute_expression ()) < 0) { + as_bad("Bad GDB symbol file offset (%d.) <0! Ignored.", temp); + ignore_rest_of_line(); + return; + } + if (flagseen ['G']) + gdb_symbols_fixup (symbolP, (long)temp); + demand_empty_rest_of_line (); +} + +void +s_gdbline() +{ + int file_number, + lineno; + + if (get_absolute_expression_and_terminator(&file_number) != ',') { + as_bad("expected comman after filenum in .gdbline"); + ignore_rest_of_line(); + return; + } + lineno=get_absolute_expression(); + if (flagseen['G']) + gdb_line(file_number,lineno); + demand_empty_rest_of_line(); +} + + +void +s_gdblinetab() +{ + int file_number, + offset; + + if (get_absolute_expression_and_terminator(&file_number) != ',') { + as_bad("expected comma after filenum in .gdblinetab"); + ignore_rest_of_line(); + return; + } + offset=get_absolute_expression(); + if (flagseen['G']) + gdb_line_tab(file_number,offset); + demand_empty_rest_of_line(); +} +#endif + +void s_globl() { + register char *name; + register int c; + register symbolS * symbolP; + + do { + name = input_line_pointer; + c = get_symbol_end(); + symbolP = symbol_find_or_make(name); + * input_line_pointer = c; + SKIP_WHITESPACE(); + S_SET_EXTERNAL(symbolP); + if (c==',') { + input_line_pointer++; + SKIP_WHITESPACE(); + if (*input_line_pointer=='\n') + c='\n'; + } + } while(c==','); + demand_empty_rest_of_line(); +} /* s_globl() */ + +void s_lcomm(needs_align) +int needs_align; /* 1 if this was a ".bss" directive, which may require + * a 3rd argument (alignment). + * 0 if it was an ".lcomm" (2 args only) + */ +{ + register char *name; + register char c; + register char *p; + register int temp; + register symbolS * symbolP; + const int max_alignment = 15; + int align; + + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE(); + if (* input_line_pointer != ',') { + as_bad("Expected comma after name"); + ignore_rest_of_line(); + return; + } + input_line_pointer ++; + + if (*input_line_pointer == '\n') { + as_bad("Missing size expression"); + return; + } + + if ((temp = get_absolute_expression ()) < 0) { + as_warn("BSS length (%d.) <0! Ignored.", temp); + ignore_rest_of_line(); + return; + } + + if (needs_align) { + align = 0; + SKIP_WHITESPACE(); + if (*input_line_pointer != ',') { + as_bad("Expected comma after size"); + ignore_rest_of_line(); + return; + } + input_line_pointer++; + SKIP_WHITESPACE(); + if (*input_line_pointer == '\n') { + as_bad("Missing alignment"); + return; + } + align = get_absolute_expression (); + if (align > max_alignment){ + align = max_alignment; + as_warn("Alignment too large: %d. assumed.", align); + } else if (align < 0) { + align = 0; + as_warn("Alignment negative. 0 assumed."); + } + + record_alignment(SEG_BSS, align); + } /* if needs align */ + + *p = 0; + symbolP = symbol_find_or_make(name); + *p = c; + if ( +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + S_GET_OTHER(symbolP) == 0 && + S_GET_DESC(symbolP) == 0 && +#endif /* OBJ_AOUT or OBJ_BOUT */ + (((S_GET_SEGMENT(symbolP) == SEG_BSS) && (S_GET_VALUE(symbolP) == local_bss_counter)) + || (!S_IS_DEFINED(symbolP) && S_GET_VALUE(symbolP) == 0))) { + if (needs_align){ + /* Align */ + align = ~ ((~0) << align); /* Convert to a mask */ + local_bss_counter = + (local_bss_counter + align) & (~align); + } + + S_SET_VALUE(symbolP,local_bss_counter); + S_SET_SEGMENT(symbolP, SEG_BSS); +#ifdef OBJ_COFF + /* The symbol may already have been created with a preceding + * ".globl" directive -- be careful not to step on storage + * class in that case. Otherwise, set it to static. + */ + if (S_GET_STORAGE_CLASS(symbolP) != C_EXT){ + S_SET_STORAGE_CLASS(symbolP, C_STAT); + } +#endif /* OBJ_COFF */ + symbolP->sy_frag = & bss_address_frag; + local_bss_counter += temp; + } else { + as_bad("Ignoring attempt to re-define symbol from %d. to %d.", + S_GET_VALUE(symbolP), local_bss_counter); + } + demand_empty_rest_of_line(); + + return; +} /* s_lcomm() */ + +void +s_long() +{ + cons(4); +} + +void +s_int() +{ + cons(4); +} + +void s_lsym() { + register char *name; + register char c; + register char *p; + register segT segment; + expressionS exp; + register symbolS *symbolP; + + /* we permit ANY defined expression: BSD4.2 demands constants */ + name = input_line_pointer; + c = get_symbol_end(); + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE(); + if (* input_line_pointer != ',') { + *p = 0; + as_bad("Expected comma after name \"%s\"", name); + *p = c; + ignore_rest_of_line(); + return; + } + input_line_pointer ++; + segment = expression(& exp); + if (segment != SEG_ABSOLUTE + && segment != SEG_DATA + && segment != SEG_TEXT + && segment != SEG_BSS + && segment != SEG_REGISTER) { + as_bad("Bad expression: %s", segment_name(segment)); + ignore_rest_of_line(); + return; + } + *p = 0; + symbolP = symbol_find_or_make(name); + + /* FIXME-SOON I pulled a (&& symbolP->sy_other == 0 + && symbolP->sy_desc == 0) out of this test + because coff doesn't have those fields, and I + can't see when they'd ever be tripped. I don't + think I understand why they were here so I may + have introduced a bug. As recently as 1.37 didn't + have this test anyway. xoxorich. */ + + if (S_GET_SEGMENT(symbolP) == SEG_UNKNOWN + && S_GET_VALUE(symbolP) == 0) { + /* The name might be an undefined .global symbol; be + sure to keep the "external" bit. */ + S_SET_SEGMENT(symbolP, segment); + S_SET_VALUE(symbolP, (valueT)(exp.X_add_number)); + } else { + as_bad("Symbol %s already defined", name); + } + *p = c; + demand_empty_rest_of_line(); +} /* s_lsym() */ + +void s_org() { + register segT segment; + expressionS exp; + register long temp_fill; + register char *p; +/* + * Don't believe the documentation of BSD 4.2 AS. + * There is no such thing as a sub-segment-relative origin. + * Any absolute origin is given a warning, then assumed to be segment-relative. + * Any segmented origin expression ("foo+42") had better be in the right + * segment or the .org is ignored. + * + * BSD 4.2 AS warns if you try to .org backwards. We cannot because we + * never know sub-segment sizes when we are reading code. + * BSD will crash trying to emit -ve numbers of filler bytes in certain + * .orgs. We don't crash, but see as-write for that code. + */ +/* + * Don't make frag if need_pass_2==1. + */ + segment = get_known_segmented_expression(&exp); + if (*input_line_pointer == ',') { + input_line_pointer ++; + temp_fill = get_absolute_expression (); + } else + temp_fill = 0; + if (! need_pass_2) { + if (segment != now_seg && segment != SEG_ABSOLUTE) + as_bad("Invalid segment \"%s\". Segment \"%s\" assumed.", + segment_name(segment), segment_name(now_seg)); + p = frag_var (rs_org, 1, 1, (relax_substateT)0, exp . X_add_symbol, + exp . X_add_number, (char *)0); + * p = temp_fill; + } /* if (ok to make frag) */ + demand_empty_rest_of_line(); +} /* s_org() */ + +void s_set() { + register char *name; + register char delim; + register char *end_name; + register symbolS *symbolP; + + /* + * Especial apologies for the random logic: + * this just grew, and could be parsed much more simply! + * Dean in haste. + */ + name = input_line_pointer; + delim = get_symbol_end(); + end_name = input_line_pointer; + *end_name = delim; + SKIP_WHITESPACE(); + + if (*input_line_pointer != ',') { + *end_name = 0; + as_bad("Expected comma after name \"%s\"", name); + *end_name = delim; + ignore_rest_of_line(); + return; + } + + input_line_pointer ++; + *end_name = 0; + + if (name[0]=='.' && name[1]=='\0') { + /* Turn '. = mumble' into a .org mumble */ + register segT segment; + expressionS exp; + register char *ptr; + + segment = get_known_segmented_expression(& exp); + + if (!need_pass_2) { + if (segment != now_seg && segment != SEG_ABSOLUTE) + as_bad("Invalid segment \"%s\". Segment \"%s\" assumed.", + segment_name(segment), + segment_name (now_seg)); + ptr = frag_var(rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol, + exp.X_add_number, (char *)0); + *ptr= 0; + } /* if (ok to make frag) */ + + *end_name = delim; + return; + } + + if ((symbolP = symbol_find(name)) == NULL + && (symbolP = md_undefined_symbol(name)) == NULL) { + symbolP = symbol_new(name, + SEG_UNKNOWN, + 0, + &zero_address_frag); +#ifdef OBJ_COFF + /* "set" symbols are local unless otherwise specified. */ + SF_SET_LOCAL(symbolP); +#endif /* OBJ_COFF */ + + } /* make a new symbol */ + + symbol_table_insert(symbolP); + + *end_name = delim; + pseudo_set(symbolP); + demand_empty_rest_of_line(); +} /* s_set() */ + +void s_space() { + long temp_repeat; + register long temp_fill; + register char *p; + + /* Just like .fill, but temp_size = 1 */ + if (get_absolute_expression_and_terminator(& temp_repeat) == ',') { + temp_fill = get_absolute_expression (); + } else { + input_line_pointer --; /* Backup over what was not a ','. */ + temp_fill = 0; + } + if (temp_repeat <= 0) { + as_warn("Repeat < 0, .space ignored"); + ignore_rest_of_line(); + return; + } + if (! need_pass_2) { + p = frag_var (rs_fill, 1, 1, (relax_substateT)0, (symbolS *)0, + temp_repeat, (char *)0); + * p = temp_fill; + } + demand_empty_rest_of_line(); +} /* s_space() */ + +void +s_text() +{ + register int temp; + + temp = get_absolute_expression (); + subseg_new (SEG_TEXT, (subsegT)temp); + demand_empty_rest_of_line(); +} /* s_text() */ + + +/*(JF was static, but can't be if machine dependent pseudo-ops are to use it */ + +void demand_empty_rest_of_line() { + SKIP_WHITESPACE(); + if (is_end_of_line [*input_line_pointer]) { + input_line_pointer++; + } else { + ignore_rest_of_line(); + } + /* Return having already swallowed end-of-line. */ +} /* Return pointing just after end-of-line. */ + +void +ignore_rest_of_line() /* For suspect lines: gives warning. */ +{ + if (! is_end_of_line [* input_line_pointer]) + { + if (isprint(*input_line_pointer)) + as_bad("Rest of line ignored. First ignored character is `%c'.", + *input_line_pointer); + else + as_bad("Rest of line ignored. First ignored character valued 0x%x.", + *input_line_pointer); + while (input_line_pointer < buffer_limit + && ! is_end_of_line [* input_line_pointer]) + { + input_line_pointer ++; + } + } + input_line_pointer ++; /* Return pointing just after end-of-line. */ + know(is_end_of_line [input_line_pointer [-1]]); +} + +/* + * pseudo_set() + * + * In: Pointer to a symbol. + * Input_line_pointer->expression. + * + * Out: Input_line_pointer->just after any whitespace after expression. + * Tried to set symbol to value of expression. + * Will change symbols type, value, and frag; + * May set need_pass_2 == 1. + */ +void +pseudo_set (symbolP) + symbolS * symbolP; +{ + expressionS exp; + register segT segment; +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + int ext; +#endif /* OBJ_AOUT or OBJ_BOUT */ + + know(symbolP); /* NULL pointer is logic error. */ +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + ext=S_IS_EXTERNAL(symbolP); +#endif /* OBJ_AOUT or OBJ_BOUT */ + + if ((segment = expression(& exp)) == SEG_ABSENT) + { + as_bad("Missing expression: absolute 0 assumed"); + exp . X_seg = SEG_ABSOLUTE; + exp . X_add_number = 0; + } + + switch (segment) + { + case SEG_BIG: + as_bad("%s number invalid. Absolute 0 assumed.", + exp . X_add_number > 0 ? "Bignum" : "Floating-Point"); + S_SET_SEGMENT(symbolP, SEG_ABSOLUTE); +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + ext ? S_SET_EXTERNAL(symbolP) : + S_CLEAR_EXTERNAL(symbolP); +#endif /* OBJ_AOUT or OBJ_BOUT */ + S_SET_VALUE(symbolP, 0); + symbolP->sy_frag = & zero_address_frag; + break; + + case SEG_ABSENT: + as_warn("No expression: Using absolute 0"); + S_SET_SEGMENT(symbolP, SEG_ABSOLUTE); +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + ext ? S_SET_EXTERNAL(symbolP) : + S_CLEAR_EXTERNAL(symbolP); +#endif /* OBJ_AOUT or OBJ_BOUT */ + S_SET_VALUE(symbolP, 0); + symbolP->sy_frag = & zero_address_frag; + break; + + case SEG_DIFFERENCE: + if (exp.X_add_symbol && exp.X_subtract_symbol + && (S_GET_SEGMENT(exp.X_add_symbol) == + S_GET_SEGMENT(exp.X_subtract_symbol))) { + if (exp.X_add_symbol->sy_frag != exp.X_subtract_symbol->sy_frag) { + as_bad("Unknown expression: symbols %s and %s are in different frags.", + S_GET_NAME(exp.X_add_symbol), S_GET_NAME(exp.X_subtract_symbol)); + need_pass_2++; + } + exp.X_add_number+=S_GET_VALUE(exp.X_add_symbol) - + S_GET_VALUE(exp.X_subtract_symbol); + } else + as_bad("Complex expression. Absolute segment assumed."); + case SEG_ABSOLUTE: + S_SET_SEGMENT(symbolP, SEG_ABSOLUTE); +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + ext ? S_SET_EXTERNAL(symbolP) : + S_CLEAR_EXTERNAL(symbolP); +#endif /* OBJ_AOUT or OBJ_BOUT */ + S_SET_VALUE(symbolP, exp.X_add_number); + symbolP->sy_frag = & zero_address_frag; + break; + + case SEG_DATA: + case SEG_TEXT: + case SEG_BSS: + switch(segment) { + case SEG_DATA: S_SET_SEGMENT(symbolP, SEG_DATA); break; + case SEG_TEXT: S_SET_SEGMENT(symbolP, SEG_TEXT); break; + case SEG_BSS: S_SET_SEGMENT(symbolP, SEG_BSS); break; + default: abort(); + } /* switch on segment */ + +#if defined(OBJ_AOUT) | defined(OBJ_BOUT) + if (ext) { + S_SET_EXTERNAL(symbolP); + } else { + S_CLEAR_EXTERNAL(symbolP); + } /* if external */ +#endif /* OBJ_AOUT or OBJ_BOUT */ + + S_SET_VALUE(symbolP, exp.X_add_number + S_GET_VALUE(exp.X_add_symbol)); + symbolP->sy_frag = exp . X_add_symbol->sy_frag; + break; + + case SEG_PASS1: /* Not an error. Just try another pass. */ + symbolP->sy_forward=exp.X_add_symbol; + as_bad("Unknown expression"); + know(need_pass_2 == 1); + break; + + case SEG_UNKNOWN: + symbolP->sy_forward=exp.X_add_symbol; + /* as_warn("unknown symbol"); */ + /* need_pass_2 = 1; */ + break; + + default: + BAD_CASE(segment); + break; + } +} + +/* + * cons() + * + * CONStruct more frag of .bytes, or .words etc. + * Should need_pass_2 be 1 then emit no frag(s). + * This understands EXPRESSIONS, as opposed to big_cons(). + * + * Bug (?) + * + * This has a split personality. We use expression() to read the + * value. We can detect if the value won't fit in a byte or word. + * But we can't detect if expression() discarded significant digits + * in the case of a long. Not worth the crocks required to fix it. + */ + + /* worker to do .byte etc statements */ + /* clobbers input_line_pointer, checks */ + /* end-of-line. */ +void cons(nbytes) +register unsigned int nbytes; /* 1=.byte, 2=.word, 4=.long */ +{ + register char c; + register long mask; /* High-order bits we will left-truncate, */ + /* but includes sign bit also. */ + register long get; /* what we get */ + register long use; /* get after truncation. */ + register long unmask; /* what bits we will store */ + register char * p; + register segT segment; + expressionS exp; + + /* + * Input_line_pointer->1st char after pseudo-op-code and could legally + * be a end-of-line. (Or, less legally an eof - which we cope with.) + */ + /* JF << of >= number of bits in the object is undefined. In particular + SPARC (Sun 4) has problems */ + + if (nbytes>=sizeof(long)) { + mask = 0; + } else { + mask = ~0 << (BITS_PER_CHAR * nbytes); /* Don't store these bits. */ + } /* bigger than a long */ + + unmask = ~mask; /* Do store these bits. */ + +#ifdef NEVER + "Do this mod if you want every overflow check to assume SIGNED 2's complement data."; + mask = ~ (unmask >> 1); /* Includes sign bit now. */ +#endif + + /* + * The following awkward logic is to parse ZERO or more expressions, + * comma seperated. Recall an expression includes its leading & + * trailing blanks. We fake a leading ',' if there is (supposed to + * be) a 1st expression, and keep demanding 1 expression for each ','. + */ + if (is_it_end_of_statement()) { + c = 0; /* Skip loop. */ + input_line_pointer++; /* Matches end-of-loop 'correction'. */ + } else { + c = ','; + } /* if the end else fake it */ + +/* Do loop. */ + while (c == ',') { + unsigned int bits_available = BITS_PER_CHAR * nbytes; + /* used for error messages and rescanning */ + char *hold = input_line_pointer; + + /* At least scan over the expression. */ + segment = expression(&exp); + +#ifdef WANT_BITFIELDS + /* Some other assemblers, (eg, asm960), allow + bitfields after ".byte" as w:x,y:z, where w and + y are bitwidths and x and y are values. They + then pack them all together. We do a little + better in that we allow them in words, longs, + etc. and we'll pack them in target byte order + for you. + + The rules are: pack least significat bit first, + if a field doesn't entirely fit, put it in the + next unit. Overflowing the bitfield is + explicitly *not* even a warning. The bitwidth + should be considered a "mask". + + FIXME-SOMEDAY: If this is considered generally + useful, this logic should probably be reworked. + xoxorich. */ + + if (*input_line_pointer == ':') { /* bitfields */ + long value = 0; + + for (;;) { + unsigned long width; + + if (*input_line_pointer != ':') { + input_line_pointer = hold; + break; + } /* next piece is not a bitfield */ + + /* In the general case, we can't allow + full expressions with symbol + differences and such. The relocation + entries for symbols not defined in this + assembly would require arbitrary field + widths, positions, and masks which most + of our current object formats don't + support. + + In the specific case where a symbol + *is* defined in this assembly, we + *could* build fixups and track it, but + this could lead to confusion for the + backends. I'm lazy. I'll take any + SEG_ABSOLUTE. I think that means that + you can use a previous .set or + .equ type symbol. xoxorich. */ + + if (segment == SEG_ABSENT) { + as_warn("Using a bit field width of zero."); + exp.X_add_number = 0; + segment = SEG_ABSOLUTE; + } /* implied zero width bitfield */ + + if (segment != SEG_ABSOLUTE) { + *input_line_pointer = '\0'; + as_bad("Field width \"%s\" too complex for a bitfield.\n", hold); + *input_line_pointer = ':'; + demand_empty_rest_of_line(); + return; + } /* too complex */ + + if ((width = exp.X_add_number) > (BITS_PER_CHAR * nbytes)) { + as_warn("Field width %d too big to fit in %d bytes: truncated to %d bits.", + width, nbytes, (BITS_PER_CHAR * nbytes)); + width = BITS_PER_CHAR * nbytes; + } /* too big */ + + if (width > bits_available) { + /* FIXME-SOMEDAY: backing up and + reparsing is wasteful */ + input_line_pointer = hold; + exp.X_add_number = value; + break; + } /* won't fit */ + + hold = ++input_line_pointer; /* skip ':' */ + + if ((segment = expression(&exp)) != SEG_ABSOLUTE) { + char cache = *input_line_pointer; + + *input_line_pointer = '\0'; + as_bad("Field value \"%s\" too complex for a bitfield.\n", hold); + *input_line_pointer = cache; + demand_empty_rest_of_line(); + return; + } /* too complex */ + + value |= (~(-1 << width) & exp.X_add_number) + << ((BITS_PER_CHAR * nbytes) - bits_available); + + if ((bits_available -= width) == 0 + || is_it_end_of_statement() + || *input_line_pointer != ',') { + break; + } /* all the bitfields we're gonna get */ + + hold = ++input_line_pointer; + segment = expression(&exp); + } /* forever loop */ + + exp.X_add_number = value; + segment = SEG_ABSOLUTE; + } /* if looks like a bitfield */ +#endif /* WANT_BITFIELDS */ + + if (!need_pass_2) { /* Still worthwhile making frags. */ + + /* Don't call this if we are going to junk this pass anyway! */ + know(segment != SEG_PASS1); + + if (segment == SEG_DIFFERENCE && exp.X_add_symbol == NULL) { + as_bad("Subtracting symbol \"%s\"(segment\"%s\") is too hard. Absolute segment assumed.", + S_GET_NAME(exp.X_subtract_symbol), + segment_name(S_GET_SEGMENT(exp.X_subtract_symbol))); + segment = SEG_ABSOLUTE; + /* Leave exp . X_add_number alone. */ + } + p = frag_more(nbytes); + switch (segment) { + case SEG_BIG: + as_bad("%s number invalid. Absolute 0 assumed.", + exp . X_add_number > 0 ? "Bignum" : "Floating-Point"); + md_number_to_chars (p, (long)0, nbytes); + break; + + case SEG_ABSENT: + as_warn("0 assumed for missing expression"); + exp . X_add_number = 0; + know(exp . X_add_symbol == NULL); + /* fall into SEG_ABSOLUTE */ + case SEG_ABSOLUTE: + get = exp . X_add_number; + use = get & unmask; + if ((get & mask) && (get & mask) != mask) + { /* Leading bits contain both 0s & 1s. */ + as_warn("Value x%x truncated to x%x.", get, use); + } + md_number_to_chars (p, use, nbytes); /* put bytes in right order. */ + break; + + case SEG_DIFFERENCE: +#ifndef WORKING_DOT_WORD + if (nbytes==2) { + struct broken_word *x; + + x=(struct broken_word *)xmalloc(sizeof(struct broken_word)); + x->next_broken_word=broken_words; + broken_words=x; + x->frag=frag_now; + x->word_goes_here=p; + x->dispfrag=0; + x->add=exp.X_add_symbol; + x->sub=exp.X_subtract_symbol; + x->addnum=exp.X_add_number; + x->added=0; + new_broken_words++; + break; + } + /* Else Fall through into. . . */ +#endif + case SEG_BSS: + case SEG_UNKNOWN: + case SEG_TEXT: + case SEG_DATA: +#ifdef TC_NS32K + fix_new_ns32k (frag_now, p - frag_now->fr_literal, nbytes, + exp . X_add_symbol, exp . X_subtract_symbol, + exp . X_add_number, 0, 0, 2, 0, 0); +#else + fix_new (frag_now, p - frag_now->fr_literal, nbytes, + exp . X_add_symbol, exp . X_subtract_symbol, + exp . X_add_number, 0, RELOC_32); +#endif /* TC_NS32K */ + break; + + default: + BAD_CASE(segment); + break; + } /* switch(segment) */ + } /* if (!need_pass_2) */ + c = *input_line_pointer++; + } /* while(c==',') */ + input_line_pointer--; /* Put terminator back into stream. */ + demand_empty_rest_of_line(); +} /* cons() */ + +/* + * big_cons() + * + * CONStruct more frag(s) of .quads, or .octa etc. + * Makes 0 or more new frags. + * If need_pass_2 == 1, generate no frag. + * This understands only bignums, not expressions. Cons() understands + * expressions. + * + * Constants recognised are '0...'(octal) '0x...'(hex) '...'(decimal). + * + * This creates objects with struct obstack_control objs, destroying + * any context objs held about a partially completed object. Beware! + * + * + * I think it sucks to have 2 different types of integers, with 2 + * routines to read them, store them etc. + * It would be nicer to permit bignums in expressions and only + * complain if the result overflowed. However, due to "efficiency"... + */ +/* worker to do .quad etc statements */ +/* clobbers input_line_pointer, checks */ +/* end-of-line. */ +/* 8=.quad 16=.octa ... */ + +void big_cons(nbytes) + register int nbytes; +{ + register char c; /* input_line_pointer->c. */ + register int radix; + register long length; /* Number of chars in an object. */ + register int digit; /* Value of 1 digit. */ + register int carry; /* For multi-precision arithmetic. */ + register int work; /* For multi-precision arithmetic. */ + register char * p; /* For multi-precision arithmetic. */ + + extern char hex_value[]; /* In hex_value.c. */ + + /* + * The following awkward logic is to parse ZERO or more strings, + * comma seperated. Recall an expression includes its leading & + * trailing blanks. We fake a leading ',' if there is (supposed to + * be) a 1st expression, and keep demanding 1 expression for each ','. + */ + if (is_it_end_of_statement()) + { + c = 0; /* Skip loop. */ + } + else + { + c = ','; /* Do loop. */ + -- input_line_pointer; + } + while (c == ',') + { + ++ input_line_pointer; + SKIP_WHITESPACE(); + c = * input_line_pointer; + /* C contains 1st non-blank character of what we hope is a number. */ + if (c == '0') + { + c = * ++ input_line_pointer; + if (c == 'x' || c=='X') + { + c = * ++ input_line_pointer; + radix = 16; + } + else + { + radix = 8; + } + } + else + { + radix = 10; + } + /* + * This feature (?) is here to stop people worrying about + * mysterious zero constants: which is what they get when + * they completely omit digits. + */ + if (hex_value[c] >= radix) { + as_bad("Missing digits. 0 assumed."); + } + bignum_high = bignum_low - 1; /* Start constant with 0 chars. */ + for(; (digit = hex_value [c]) < radix; c = * ++ input_line_pointer) + { + /* Multiply existing number by radix, then add digit. */ + carry = digit; + for (p=bignum_low; p <= bignum_high; p++) + { + work = (*p & MASK_CHAR) * radix + carry; + *p = work & MASK_CHAR; + carry = work >> BITS_PER_CHAR; + } + if (carry) + { + grow_bignum(); + * bignum_high = carry & MASK_CHAR; + know((carry & ~ MASK_CHAR) == 0); + } + } + length = bignum_high - bignum_low + 1; + if (length > nbytes) + { + as_warn("Most significant bits truncated in integer constant."); + } + else + { + register long leading_zeroes; + + for(leading_zeroes = nbytes - length; + leading_zeroes; + leading_zeroes --) + { + grow_bignum(); + * bignum_high = 0; + } + } + if (! need_pass_2) + { + p = frag_more (nbytes); + bcopy (bignum_low, p, (int)nbytes); + } + /* C contains character after number. */ + SKIP_WHITESPACE(); + c = * input_line_pointer; + /* C contains 1st non-blank character after number. */ + } + demand_empty_rest_of_line(); +} /* big_cons() */ + + /* Extend bignum by 1 char. */ +static void grow_bignum() { + register long length; + + bignum_high ++; + if (bignum_high >= bignum_limit) + { + length = bignum_limit - bignum_low; + bignum_low = xrealloc(bignum_low, length + length); + bignum_high = bignum_low + length; + bignum_limit = bignum_low + length + length; + } +} /* grow_bignum(); */ + +/* + * float_cons() + * + * CONStruct some more frag chars of .floats .ffloats etc. + * Makes 0 or more new frags. + * If need_pass_2 == 1, no frags are emitted. + * This understands only floating literals, not expressions. Sorry. + * + * A floating constant is defined by atof_generic(), except it is preceded + * by 0d 0f 0g or 0h. After observing the STRANGE way my BSD AS does its + * reading, I decided to be incompatible. This always tries to give you + * rounded bits to the precision of the pseudo-op. Former AS did premature + * truncatation, restored noisy bits instead of trailing 0s AND gave you + * a choice of 2 flavours of noise according to which of 2 floating-point + * scanners you directed AS to use. + * + * In: input_line_pointer->whitespace before, or '0' of flonum. + * + */ + +void /* JF was static, but can't be if VAX.C is goning to use it */ +float_cons(float_type) /* Worker to do .float etc statements. */ + /* Clobbers input_line-pointer, checks end-of-line. */ + register int float_type; /* 'f':.ffloat ... 'F':.float ... */ +{ + register char * p; + register char c; + int length; /* Number of chars in an object. */ + register char * err; /* Error from scanning floating literal. */ + char temp [MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT]; + + /* + * The following awkward logic is to parse ZERO or more strings, + * comma seperated. Recall an expression includes its leading & + * trailing blanks. We fake a leading ',' if there is (supposed to + * be) a 1st expression, and keep demanding 1 expression for each ','. + */ + if (is_it_end_of_statement()) + { + c = 0; /* Skip loop. */ + ++ input_line_pointer; /*->past termintor. */ + } + else + { + c = ','; /* Do loop. */ + } + while (c == ',') + { + /* input_line_pointer->1st char of a flonum (we hope!). */ + SKIP_WHITESPACE(); + /* Skip any 0{letter} that may be present. Don't even check if the + * letter is legal. Someone may invent a "z" format and this routine + * has no use for such information. Lusers beware: you get + * diagnostics if your input is ill-conditioned. + */ + + if (input_line_pointer[0]=='0' && isalpha(input_line_pointer[1])) + input_line_pointer+=2; + + err = md_atof (float_type, temp, &length); + know(length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT); + know(length > 0); + if (* err) + { + as_bad("Bad floating literal: %s", err); + ignore_rest_of_line(); + /* Input_line_pointer->just after end-of-line. */ + c = 0; /* Break out of loop. */ + } + else + { + if (! need_pass_2) + { + p = frag_more (length); + bcopy (temp, p, length); + } + SKIP_WHITESPACE(); + c = * input_line_pointer ++; + /* C contains 1st non-white character after number. */ + /* input_line_pointer->just after terminator (c). */ + } + } + -- input_line_pointer; /*->terminator (is not ','). */ + demand_empty_rest_of_line(); +} /* float_cons() */ + +/* + * stringer() + * + * We read 0 or more ',' seperated, double-quoted strings. + * + * Caller should have checked need_pass_2 is FALSE because we don't check it. + */ +static void stringer(append_zero) /* Worker to do .ascii etc statements. */ + /* Checks end-of-line. */ + register int append_zero; /* 0: don't append '\0', else 1 */ +{ + /* register char * p; JF unused */ + /* register int length; JF unused */ /* Length of string we read, excluding */ + /* trailing '\0' implied by closing quote. */ + /* register char * where; JF unused */ + /* register fragS * fragP; JF unused */ + register unsigned int c; + + /* + * The following awkward logic is to parse ZERO or more strings, + * comma seperated. Recall a string expression includes spaces + * before the opening '\"' and spaces after the closing '\"'. + * We fake a leading ',' if there is (supposed to be) + * a 1st, expression. We keep demanding expressions for each + * ','. + */ + if (is_it_end_of_statement()) + { + c = 0; /* Skip loop. */ + ++ input_line_pointer; /* Compensate for end of loop. */ + } + else + { + c = ','; /* Do loop. */ + } + for (; c == ','; c = *input_line_pointer++) { + SKIP_WHITESPACE(); + if (*input_line_pointer == '\"') { + ++input_line_pointer; /*->1st char of string. */ + while (is_a_char(c = next_char_of_string())) { + FRAG_APPEND_1_CHAR(c); + } + if (append_zero) { + FRAG_APPEND_1_CHAR(0); + } + know(input_line_pointer [-1] == '\"'); + } else { + as_warn("Expected \"-ed string"); + } + SKIP_WHITESPACE(); + } + --input_line_pointer; + demand_empty_rest_of_line(); +} /* stringer() */ + + /* FIXME-SOMEDAY: I had trouble here on characters with the + high bits set. We'll probably also have trouble with + multibyte chars, wide chars, etc. Also be careful about + returning values bigger than 1 byte. xoxorich. */ + +static unsigned int next_char_of_string() { + register unsigned int c; + + c = *input_line_pointer++ & CHAR_MASK; + switch (c) { + case '\"': + c = NOT_A_CHAR; + break; + + case '\\': + switch (c = *input_line_pointer++) { + case 'b': + c = '\b'; + break; + + case 'f': + c = '\f'; + break; + + case 'n': + c = '\n'; + break; + + case 'r': + c = '\r'; + break; + + case 't': + c = '\t'; + break; + +#ifdef BACKSLASH_V + case 'v': + c = '\013'; + break; +#endif + + case '\\': + case '"': + break; /* As itself. */ + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + long number; + + for (number = 0; isdigit(c); c = *input_line_pointer++) { + number = number * 8 + c - '0'; + } + c = number & 0xff; + } + --input_line_pointer; + break; + + case '\n': + /* To be compatible with BSD 4.2 as: give the luser a linefeed!! */ + as_warn("Unterminated string: Newline inserted."); + c = '\n'; + break; + + default: + +#ifdef ONLY_STANDARD_ESCAPES + as_bad("Bad escaped character in string, '?' assumed"); + c = '?'; +#endif /* ONLY_STANDARD_ESCAPES */ + + break; + } /* switch on escaped char */ + break; + + default: + break; + } /* switch on char */ + return(c); +} /* next_char_of_string() */ + +static segT +get_segmented_expression (expP) + register expressionS * expP; +{ + register segT retval; + + if ((retval = expression(expP)) == SEG_PASS1 || retval == SEG_ABSENT || retval == SEG_BIG) + { + as_bad("Expected address expression: absolute 0 assumed"); + retval = expP->X_seg = SEG_ABSOLUTE; + expP->X_add_number = 0; + expP->X_add_symbol = expP->X_subtract_symbol = 0; + } + return (retval); /* SEG_ ABSOLUTE,UNKNOWN,DATA,TEXT,BSS */ +} + +static segT get_known_segmented_expression(expP) +register expressionS *expP; +{ + register segT retval; + register char * name1; + register char * name2; + + if ((retval = get_segmented_expression (expP)) == SEG_UNKNOWN) + { + name1 = expP->X_add_symbol ? S_GET_NAME(expP->X_add_symbol) : ""; + name2 = expP->X_subtract_symbol ? + S_GET_NAME(expP->X_subtract_symbol) : + ""; + if (name1 && name2) + { + as_warn("Symbols \"%s\" \"%s\" are undefined: absolute 0 assumed.", + name1, name2); + } + else + { + as_warn("Symbol \"%s\" undefined: absolute 0 assumed.", + name1 ? name1 : name2); + } + retval = expP->X_seg = SEG_ABSOLUTE; + expP->X_add_number = 0; + expP->X_add_symbol = expP->X_subtract_symbol = NULL; + } + know(retval == SEG_ABSOLUTE || retval == SEG_DATA || retval == SEG_TEXT || retval == SEG_BSS || retval == SEG_DIFFERENCE); + return (retval); +} /* get_known_segmented_expression() */ + + + +/* static */ long /* JF was static, but can't be if the MD pseudos are to use it */ +get_absolute_expression () +{ + expressionS exp; + register segT s; + + if ((s = expression(& exp)) != SEG_ABSOLUTE) + { + if (s != SEG_ABSENT) + { + as_bad("Bad Absolute Expression, absolute 0 assumed."); + } + exp . X_add_number = 0; + } + return (exp . X_add_number); +} + +char /* return terminator */ +get_absolute_expression_and_terminator(val_pointer) + long * val_pointer; /* return value of expression */ +{ + * val_pointer = get_absolute_expression (); + return (* input_line_pointer ++); +} + +/* + * demand_copy_C_string() + * + * Like demand_copy_string, but return NULL if the string contains any '\0's. + * Give a warning if that happens. + */ +char * +demand_copy_C_string (len_pointer) + int * len_pointer; +{ + register char * s; + + if ((s = demand_copy_string(len_pointer)) != 0) + { + register int len; + + for (len = * len_pointer; + len > 0; + len--) + { + if (* s == 0) + { + s = 0; + len = 1; + * len_pointer = 0; + as_bad("This string may not contain \'\\0\'"); + } + } + } + return (s); +} + +/* + * demand_copy_string() + * + * Demand string, but return a safe (=private) copy of the string. + * Return NULL if we can't read a string here. + */ +static char *demand_copy_string(lenP) +int *lenP; +{ + register unsigned int c; + register int len; + char *retval; + + len = 0; + SKIP_WHITESPACE(); + if (*input_line_pointer == '\"') { + input_line_pointer++; /* Skip opening quote. */ + + while (is_a_char(c = next_char_of_string())) { + obstack_1grow(¬es, c); + len ++; + } + /* JF this next line is so demand_copy_C_string will return a null + termanated string. */ + obstack_1grow(¬es,'\0'); + retval=obstack_finish(¬es); + } else { + as_warn("Missing string"); + retval = NULL; + ignore_rest_of_line(); + } + *lenP = len; + return(retval); +} /* demand_copy_string() */ + +/* + * is_it_end_of_statement() + * + * In: Input_line_pointer->next character. + * + * Do: Skip input_line_pointer over all whitespace. + * + * Out: 1 if input_line_pointer->end-of-line. + */ +static int is_it_end_of_statement() { + SKIP_WHITESPACE(); + return (is_end_of_line [* input_line_pointer]); +} /* is_it_end_of_statement() */ + +void equals(sym_name) +char *sym_name; +{ + register symbolS *symbolP; /* symbol we are working with */ + + input_line_pointer++; + if (*input_line_pointer=='=') + input_line_pointer++; + + while(*input_line_pointer==' ' || *input_line_pointer=='\t') + input_line_pointer++; + + if (sym_name[0]=='.' && sym_name[1]=='\0') { + /* Turn '. = mumble' into a .org mumble */ + register segT segment; + expressionS exp; + register char *p; + + segment = get_known_segmented_expression(& exp); + if (! need_pass_2) { + if (segment != now_seg && segment != SEG_ABSOLUTE) + as_warn("Illegal segment \"%s\". Segment \"%s\" assumed.", + segment_name(segment), + segment_name(now_seg)); + p = frag_var(rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol, + exp.X_add_number, (char *)0); + * p = 0; + } /* if (ok to make frag) */ + } else { + symbolP=symbol_find_or_make(sym_name); + pseudo_set(symbolP); + } +} /* equals() */ + +/* .include -- include a file at this point. */ + +/* ARGSUSED */ +void s_include(arg) +int arg; +{ + char *newbuf; + char *filename; + int i; + FILE *try; + char *path; + + filename = demand_copy_string(&i); + demand_empty_rest_of_line(); + path = malloc(i + include_dir_maxlen + 5 /* slop */); + for (i = 0; i < include_dir_count; i++) { + strcpy(path, include_dirs[i]); + strcat(path, "/"); + strcat(path, filename); + if (0 != (try = fopen(path, "r"))) + { + fclose (try); + goto gotit; + } + } + free(path); + path = filename; +gotit: + /* malloc Storage leak when file is found on path. FIXME-SOMEDAY. */ + newbuf = input_scrub_include_file (path, input_line_pointer); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} /* s_include() */ + +void add_include_dir(path) +char *path; +{ + int i; + + if (include_dir_count == 0) + { + include_dirs = (char **)malloc (2 * sizeof (*include_dirs)); + include_dirs[0] = "."; /* Current dir */ + include_dir_count = 2; + } + else + { + include_dir_count++; + include_dirs = (char **) realloc(include_dirs, + include_dir_count*sizeof (*include_dirs)); + } + + include_dirs[include_dir_count-1] = path; /* New one */ + + i = strlen (path); + if (i > include_dir_maxlen) + include_dir_maxlen = i; +} /* add_include_dir() */ + +void s_ignore(arg) +int arg; +{ + extern char is_end_of_line[]; + + while (!is_end_of_line[*input_line_pointer]) { + ++input_line_pointer; + } + ++input_line_pointer; + + return; +} /* s_ignore() */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of read.c */ diff --git a/gas/read.h b/gas/read.h new file mode 100644 index 0000000..01351d6 --- /dev/null +++ b/gas/read.h @@ -0,0 +1,137 @@ +/* read.h - of read.c + Copyright (C) 1986, 1990 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +extern char *input_line_pointer; /* -> char we are parsing now. */ + +#define PERMIT_WHITESPACE /* Define to make whitespace be allowed in */ + /* many syntactically unnecessary places. */ + /* Normally undefined. For compatibility */ + /* with ancient GNU cc. */ +#undef PERMIT_WHITESPACE + +#ifdef PERMIT_WHITESPACE +#define SKIP_WHITESPACE() {if (* input_line_pointer == ' ') ++ input_line_pointer;} +#else +#define SKIP_WHITESPACE() know(*input_line_pointer != ' ' ) +#endif + + +#define LEX_NAME (1) /* may continue a name */ +#define LEX_BEGIN_NAME (2) /* may begin a name */ + +#define is_name_beginner(c) ( lex_type[c] & LEX_BEGIN_NAME ) +#define is_part_of_name(c) ( lex_type[c] & LEX_NAME ) + +#ifndef is_a_char +#define CHAR_MASK (0xff) +#define NOT_A_CHAR (CHAR_MASK+1) +#define is_a_char(c) (((unsigned)(c)) <= CHAR_MASK) +#endif /* is_a_char() */ + +extern const char lex_type[]; +extern char is_end_of_line[]; + +#ifdef __STDC__ + +char *demand_copy_C_string(int *len_pointer); +char get_absolute_expression_and_terminator(long *val_pointer); +long get_absolute_expression(void); +void add_include_dir(char *path); +void big_cons(int nbytes); +void cons(unsigned int nbytes); +void demand_empty_rest_of_line(void); +void equals(char *sym_name); +void float_cons(int float_type); +void ignore_rest_of_line(void); +void pseudo_set(symbolS *symbolP); +void read_a_source_file(char *name); +void read_begin(void); +void s_abort(void); +void s_align_bytes(int arg); +void s_align_ptwo(void); +void s_app_file(void); +void s_comm(void); +void s_data(void); +void s_else(int arg); +void s_end(int arg); +void s_endif(int arg); +void s_fill(void); +void s_globl(void); +void s_if(int arg); +void s_ifdef(int arg); +void s_ifeqs(int arg); +void s_ignore(int arg); +void s_include(int arg); +void s_lcomm(int needs_align); +void s_lsym(void); +void s_org(void); +void s_set(void); +void s_space(void); +void s_text(void); + +#else /* __STDC__ */ + +char *demand_copy_C_string(); +char get_absolute_expression_and_terminator(); +long get_absolute_expression(); +void add_include_dir(); +void big_cons(); +void cons(); +void demand_empty_rest_of_line(); +void equals(); +void float_cons(); +void ignore_rest_of_line(); +void pseudo_set(); +void read_a_source_file(); +void read_begin(); +void s_abort(); +void s_align_bytes(); +void s_align_ptwo(); +void s_app_file(); +void s_comm(); +void s_data(); +void s_else(); +void s_end(); +void s_endif(); +void s_fill(); +void s_globl(); +void s_if(); +void s_ifdef(); +void s_ifeqs(); +void s_ignore(); +void s_include(); +void s_lcomm(); +void s_lsym(); +void s_org(); +void s_set(); +void s_space(); +void s_text(); + +#endif /* __STDC__ */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end: read.h */ diff --git a/gas/struc-symbol.h b/gas/struc-symbol.h new file mode 100644 index 0000000..2827648 --- /dev/null +++ b/gas/struc-symbol.h @@ -0,0 +1,113 @@ +/* struct_symbol.h - Internal symbol structure + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +struct symbol /* our version of an nlist node */ +{ + obj_symbol_type sy_symbol; /* what we write in .o file (if permitted) */ + unsigned long sy_name_offset; /* 4-origin position of sy_name in symbols */ + /* part of object file. */ + /* 0 for (nameless) .stabd symbols. */ + /* Not used until write_object_file() time. */ + long sy_number; /* 24 bit symbol number. */ + /* Symbol numbers start at 0 and are */ + /* unsigned. */ + struct symbol *sy_next; /* forward chain, or NULL */ +#ifdef SYMBOLS_NEED_BACKPOINTERS + struct symbol *sy_previous; /* backward chain, or NULL */ +#endif /* SYMBOLS_NEED_BACKPOINTERS */ + struct frag *sy_frag; /* NULL or -> frag this symbol attaches to. */ + struct symbol *sy_forward; /* value is really that of this other symbol */ +/* We will probably want to add a sy_segment here soon. */ +}; + +typedef struct symbol symbolS; + +typedef unsigned valueT; /* The type of n_value. Helps casting. */ + +#ifndef WORKING_DOT_WORD +struct broken_word { + struct broken_word *next_broken_word;/* One of these strucs per .word x-y */ + fragS *frag; /* Which frag its in */ + char *word_goes_here;/* Where in the frag it is */ + fragS *dispfrag; /* where to add the break */ + symbolS *add; /* symbol_x */ + symbolS *sub; /* - symbol_y */ + long addnum; /* + addnum */ + int added; /* nasty thing happend yet? */ + /* 1: added and has a long-jump */ + /* 2: added but uses someone elses long-jump */ + struct broken_word *use_jump; /* points to broken_word with a similar + long-jump */ +}; +extern struct broken_word *broken_words; +#endif /* ndef WORKING_DOT_WORD */ + +/* + * Current means for getting from symbols to segments and vice verse. + * This will change for infinite-segments support (e.g. COFF). + */ +/* #define SYMBOL_TYPE_TO_SEGMENT(symP) ( N_TYPE_seg [(int) (symP)->sy_type & N_TYPE] ) */ +extern segT N_TYPE_seg[]; /* subseg.c */ + +#define SEGMENT_TO_SYMBOL_TYPE(seg) ( seg_N_TYPE [(int) (seg)] ) +extern const short seg_N_TYPE[]; /* subseg.c */ + +#define N_REGISTER 30 /* Fake N_TYPE value for SEG_REGISTER */ + +#ifdef SYMBOLS_NEED_BACKPOINTERS +#ifdef __STDC__ + +void symbol_clear_list_pointers(symbolS *symbolP); +void symbol_insert(symbolS *addme, symbolS *target, symbolS **rootP, symbolS **lastP); +void symbol_remove(symbolS *symbolP, symbolS **rootP, symbolS **lastP); +void verify_symbol_chain(symbolS *rootP, symbolS *lastP); + +#else /* __STDC__ */ + +void symbol_clear_list_pointers(); +void symbol_insert(); +void symbol_remove(); +void verify_symbol_chain(); + +#endif /* __STDC__ */ + +#define symbol_previous(s) ((s)->sy_previous) + +#else /* SYMBOLS_NEED_BACKPOINTERS */ + +#define symbol_clear_list_pointers(clearme) {clearme->sy_next = NULL;} + +#endif /* SYMBOLS_NEED_BACKPOINTERS */ + +#ifdef __STDC__ +void symbol_append(symbolS *addme, symbolS *target, symbolS **rootP, symbolS **lastP); +#else /* __STDC__ */ +void symbol_append(); +#endif /* __STDC__ */ + +#define symbol_next(s) ((s)->sy_next) + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of struc-symbol.h */ diff --git a/gas/subsegs.c b/gas/subsegs.c new file mode 100644 index 0000000..00937ea --- /dev/null +++ b/gas/subsegs.c @@ -0,0 +1,279 @@ +/* subsegs.c - subsegments - + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +/* + * Segments & sub-segments. + */ + +#include "as.h" + +#include "subsegs.h" +#include "obstack.h" + +frchainS* frchain_root, + * frchain_now, /* Commented in "subsegs.h". */ + * data0_frchainP; + + +char * const /* in: segT out: char* */ +seg_name[] = { + "absolute", + "text", + "data", + "bss", + "unknown", + "absent", + "pass1", + "ASSEMBLER-INTERNAL-LOGIC-ERROR!", + "bignum/flonum", + "difference", + "debug", + "transfert vector preload", + "transfert vector postload", + "register", + "", +}; /* Used by error reporters, dumpers etc. */ + + +void +subsegs_begin() +{ + /* Check table(s) seg_name[], seg_N_TYPE[] is in correct order */ + know( SEG_ABSOLUTE == 0 ); + know( SEG_TEXT == 1 ); + know( SEG_DATA == 2 ); + know( SEG_BSS == 3 ); + know( SEG_UNKNOWN == 4 ); + know( SEG_ABSENT == 5 ); + know( SEG_PASS1 == 6 ); + know( SEG_GOOF == 7 ); + know( SEG_BIG == 8 ); + know( SEG_DIFFERENCE == 9 ); + know( SEG_DEBUG == 10 ); + know( SEG_NTV == 11 ); + know( SEG_PTV == 12 ); + know( SEG_REGISTER == 13 ); + know( SEG_MAXIMUM_ORDINAL == SEG_REGISTER ); + know( segment_name (SEG_MAXIMUM_ORDINAL + 1) [0] == 0 ); + + obstack_begin( &frags, 5000); + frchain_root = NULL; + frchain_now = NULL; /* Warn new_subseg() that we are booting. */ + /* Fake up 1st frag. */ + /* It won't be used=> is ok if obstack... */ + /* pads the end of it for alignment. */ + frag_now=(fragS *)obstack_alloc(&frags,SIZEOF_STRUCT_FRAG); + /* obstack_1blank( &frags, SIZEOF_STRUCT_FRAG, & frag_now ); */ + /* This 1st frag will not be in any frchain. */ + /* We simply give subseg_new somewhere to scribble. */ + now_subseg = 42; /* Lie for 1st call to subseg_new. */ + subseg_new (SEG_DATA, 0); /* .data 0 */ + data0_frchainP = frchain_now; +} + +/* + * subseg_change() + * + * Change the subsegment we are in, BUT DO NOT MAKE A NEW FRAG for the + * subsegment. If we are already in the correct subsegment, change nothing. + * This is used eg as a worker for subseg_new [which does make a new frag_now] + * and for changing segments after we have read the source. We construct eg + * fixSs even after the source file is read, so we do have to keep the + * segment context correct. + */ +void +subseg_change (seg, subseg) + register segT seg; + register int subseg; +{ + now_seg = seg; + now_subseg = subseg; + if (seg == SEG_DATA) + { + seg_fix_rootP = & data_fix_root; + seg_fix_tailP = & data_fix_tail; + } + else + { + know (seg == SEG_TEXT); + seg_fix_rootP = & text_fix_root; + seg_fix_tailP = & text_fix_tail; + } +} + +/* + * subseg_new() + * + * If you attempt to change to the current subsegment, nothing happens. + * + * In: segT, subsegT code for new subsegment. + * frag_now -> incomplete frag for current subsegment. + * If frag_now==NULL, then there is no old, incomplete frag, so + * the old frag is not closed off. + * + * Out: now_subseg, now_seg updated. + * Frchain_now points to the (possibly new) struct frchain for this + * sub-segment. + * Frchain_root updated if needed. + */ + +void +subseg_new (seg, subseg) /* begin assembly for a new sub-segment */ + register segT seg; /* SEG_DATA or SEG_TEXT */ + register subsegT subseg; +{ + long tmp; /* JF for obstack alignment hacking */ + + know( seg == SEG_DATA || seg == SEG_TEXT ); + + if (seg != now_seg || subseg != now_subseg) + { /* we just changed sub-segments */ + register frchainS * frcP; /* crawl frchain chain */ + register frchainS** lastPP; /* address of last pointer */ + frchainS * newP; /* address of new frchain */ + register fragS * former_last_fragP; + register fragS * new_fragP; + + if (frag_now) /* If not bootstrapping. */ + { + frag_now -> fr_fix = obstack_next_free(& frags) - frag_now -> fr_literal; + frag_wane(frag_now); /* Close off any frag in old subseg. */ + } +/* + * It would be nice to keep an obstack for each subsegment, if we swap + * subsegments a lot. Hence we would have much fewer frag_wanes(). + */ + { + + obstack_finish( &frags); + /* + * If we don't do the above, the next object we put on obstack frags + * will appear to start at the fr_literal of the current frag. + * Also, above ensures that the next object will begin on a + * address that is aligned correctly for the engine that runs + * this program. + */ + } + subseg_change (seg, (int)subseg); + /* + * Attempt to find or make a frchain for that sub seg. + * Crawl along chain of frchainSs, begins @ frchain_root. + * If we need to make a frchainS, link it into correct + * position of chain rooted in frchain_root. + */ + for (frcP = * (lastPP = & frchain_root); + frcP + && (int)(frcP -> frch_seg) <= (int)seg; + frcP = * ( lastPP = & frcP -> frch_next) + ) + { + if ( (int)(frcP -> frch_seg) == (int)seg + && frcP -> frch_subseg >= subseg) + { + break; + } + } + /* + * frcP: Address of the 1st frchainS in correct segment with + * frch_subseg >= subseg. + * We want to either use this frchainS, or we want + * to insert a new frchainS just before it. + * + * If frcP==NULL, then we are at the end of the chain + * of frchainS-s. A NULL frcP means we fell off the end + * of the chain looking for a + * frch_subseg >= subseg, so we + * must make a new frchainS. + * + * If we ever maintain a pointer to + * the last frchainS in the chain, we change that pointer + * ONLY when frcP==NULL. + * + * lastPP: Address of the pointer with value frcP; + * Never NULL. + * May point to frchain_root. + * + */ + if ( ! frcP + || ( (int)(frcP -> frch_seg) > (int)seg + || frcP->frch_subseg > subseg)) /* Kinky logic only works with 2 segments. */ + { + /* + * This should be the only code that creates a frchainS. + */ + newP=(frchainS *)obstack_alloc(&frags,sizeof(frchainS)); + /* obstack_1blank( &frags, sizeof(frchainS), &newP); */ + /* This begines on a good boundary */ + /* because a obstack_done() preceeded it. */ + /* It implies an obstack_done(), so we */ + /* expect the next object allocated to */ + /* begin on a correct boundary. */ + *lastPP = newP; + newP -> frch_next = frcP; /* perhaps NULL */ + (frcP = newP) -> frch_subseg = subseg; + newP -> frch_seg = seg; + newP -> frch_last = NULL; + } + /* + * Here with frcP ->ing to the frchainS for subseg. + */ + frchain_now = frcP; + /* + * Make a fresh frag for the subsegment. + */ + /* We expect this to happen on a correct */ + /* boundary since it was proceeded by a */ + /* obstack_done(). */ + tmp=obstack_alignment_mask(&frags); /* JF disable alignment */ + obstack_alignment_mask(&frags)=0; + frag_now=(fragS *)obstack_alloc(&frags,SIZEOF_STRUCT_FRAG); + obstack_alignment_mask(&frags)=tmp; + /* know( frags . obstack_c_next_free == frag_now -> fr_literal ); */ + /* But we want any more chars to come */ + /* immediately after the structure we just made. */ + new_fragP = frag_now; + new_fragP -> fr_next = NULL; + /* + * Append new frag to current frchain. + */ + former_last_fragP = frcP -> frch_last; + if (former_last_fragP) + { + know( former_last_fragP -> fr_next == NULL ); + know( frchain_now -> frch_root ); + former_last_fragP -> fr_next = new_fragP; + } + else + { + frcP -> frch_root = new_fragP; + } + frcP -> frch_last = new_fragP; + } /* if (changing subsegments) */ +} /* subseg_new() */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end: subsegs.c */ diff --git a/gas/subsegs.h b/gas/subsegs.h new file mode 100644 index 0000000..b8dbaf7 --- /dev/null +++ b/gas/subsegs.h @@ -0,0 +1,65 @@ +/* subsegs.h -> subsegs.c + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * For every sub-segment the user mentions in the ASsembler program, + * we make one struct frchain. Each sub-segment has exactly one struct frchain + * and vice versa. + * + * Struct frchain's are forward chained (in ascending order of sub-segment + * code number). The chain runs through frch_next of each subsegment. + * This makes it hard to find a subsegment's frags + * if programmer uses a lot of them. Most programs only use text0 and + * data0, so they don't suffer. At least this way: + * (1) There are no "arbitrary" restrictions on how many subsegments + * can be programmed; + * (2) Subsegments' frchain-s are (later) chained together in the order in + * which they are emitted for object file viz text then data. + * + * From each struct frchain dangles a chain of struct frags. The frags + * represent code fragments, for that sub-segment, forward chained. + */ + +struct frchain /* control building of a frag chain */ +{ /* FRCH = FRagment CHain control */ + struct frag * frch_root; /* 1st struct frag in chain, or NULL */ + struct frag * frch_last; /* last struct frag in chain, or NULL */ + struct frchain * frch_next; /* next in chain of struct frchain-s */ + segT frch_seg; /* SEG_TEXT or SEG_DATA. */ + subsegT frch_subseg; /* subsegment number of this chain */ +}; + +typedef struct frchain frchainS; + +extern frchainS * frchain_root; /* NULL means no frchains yet. */ + /* all subsegments' chains hang off here */ + +extern frchainS * frchain_now; + /* Frchain we are assembling into now */ + /* That is, the current segment's frag */ + /* chain, even if it contains no (complete) */ + /* frags. */ + +extern frchainS * data0_frchainP; + /* Sentinel for frchain crawling. */ + /* Points to the 1st data-segment frchain. */ + /* (Which is pointed to by the last text- */ + /* segment frchain.) */ + +/* end: subsegs.h */ diff --git a/gas/symbols.c b/gas/symbols.c new file mode 100644 index 0000000..445160a --- /dev/null +++ b/gas/symbols.c @@ -0,0 +1,614 @@ +/* symbols.c -symbol table- + Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +#include "as.h" + +#include "obstack.h" /* For "symbols.h" */ +#include "subsegs.h" + +#ifndef WORKING_DOT_WORD +extern int new_broken_words; +#endif +#ifdef VMS +extern char const_flag; +#endif + +static +struct hash_control * +sy_hash; /* symbol-name => struct symbol pointer */ + + /* Below are commented in "symbols.h". */ +unsigned int local_bss_counter; +symbolS * symbol_rootP; +symbolS * symbol_lastP; +symbolS abs_symbol; + +symbolS* dot_text_symbol; +symbolS* dot_data_symbol; +symbolS* dot_bss_symbol; + +struct obstack notes; + +/* + * Un*x idea of local labels. They are made by "n:" where n + * is any decimal digit. Refer to them with + * "nb" for previous (backward) n: + * or "nf" for next (forward) n:. + * + * Like Un*x AS, we have one set of local label counters for entire assembly, + * not one set per (sub)segment like in most assemblers. This implies that + * one can refer to a label in another segment, and indeed some crufty + * compilers have done just that. + * + * I document the symbol names here to save duplicating words elsewhere. + * The mth occurence of label n: is turned into the symbol "Ln^Am" where + * n is a digit and m is a decimal number. "L" makes it a label discarded + * unless debugging and "^A"('\1') ensures no ordinary symbol SHOULD get the + * same name as a local label symbol. The first "4:" is "L4^A1" - the m + * numbers begin at 1. + */ + +typedef short unsigned int +local_label_countT; + +static local_label_countT +local_label_counter[10]; + +static /* Returned to caller, then copied. */ + char symbol_name_build[12]; /* used for created names ("4f") */ + +#ifdef LOCAL_LABELS_DOLLAR +int local_label_defined[10]; +#endif + + +void +symbol_begin() +{ + symbol_lastP = NULL; + symbol_rootP = NULL; /* In case we have 0 symbols (!!) */ + sy_hash = hash_new(); + bzero ((char *)(& abs_symbol), sizeof(abs_symbol)); + S_SET_SEGMENT(&abs_symbol, SEG_ABSOLUTE); /* Can't initialise a union. Sigh. */ + bzero ((char *)(local_label_counter), sizeof(local_label_counter) ); + local_bss_counter = 0; +} + +/* + * local_label_name() + * + * Caller must copy returned name: we re-use the area for the next name. + */ + +char * /* Return local label name. */ +local_label_name(n, augend) + register int n; /* we just saw "n:", "nf" or "nb" : n a digit */ + register int augend; /* 0 for nb, 1 for n:, nf */ +{ + register char * p; + register char * q; + char symbol_name_temporary[10]; /* build up a number, BACKWARDS */ + + know( n >= 0 ); + know( augend == 0 || augend == 1 ); + p = symbol_name_build; + * p ++ = 'L'; + * p ++ = n + '0'; /* Make into ASCII */ + * p ++ = 1; /* ^A */ + n = local_label_counter [ n ] + augend; + /* version number of this local label */ + /* + * Next code just does sprintf( {}, "%d", n); + * It is more elegant to do the next part recursively, but a procedure + * call for each digit emitted is considered too costly. + */ + q = symbol_name_temporary; + for (*q++=0; n; q++) /* emits NOTHING if n starts as 0 */ + { + know(n>0); /* We expect n > 0 always */ + *q = n % 10 + '0'; + n /= 10; + } + while (( * p ++ = * -- q ) != '\0') ;; + + /* The label, as a '\0' ended string, starts at symbol_name_build. */ + return(symbol_name_build); +} /* local_label_name() */ + + +void local_colon (n) +int n; /* just saw "n:" */ +{ + local_label_counter [n] ++; +#ifdef LOCAL_LABELS_DOLLAR + local_label_defined[n]=1; +#endif + colon (local_label_name (n, 0)); +} + +/* + * symbol_new() + * + * Return a pointer to a new symbol. + * Die if we can't make a new symbol. + * Fill in the symbol's values. + * Add symbol to end of symbol chain. + * + * + * Please always call this to create a new symbol. + * + * Changes since 1985: Symbol names may not contain '\0'. Sigh. + * 2nd argument is now a SEG rather than a TYPE. The mapping between + * segments and types is mostly encapsulated herein (actually, we inherit it + * from macros in struc-symbol.h). + */ + +symbolS *symbol_new(name, segment, value, frag) +char *name; /* It is copied, the caller can destroy/modify */ +segT segment; /* Segment identifier (SEG_<something>) */ +long value; /* Symbol value */ +fragS *frag; /* Associated fragment */ +{ + unsigned int name_length; + char *preserved_copy_of_name; + symbolS *symbolP; + + name_length = strlen(name) + 1; /* +1 for \0 */ + obstack_grow(¬es, name, name_length); + preserved_copy_of_name = obstack_finish(¬es); + symbolP = (symbolS *)obstack_alloc(¬es, sizeof(symbolS)); + +#if STRIP_UNDERSCORE + S_SET_NAME(symbolP, (*preserved_copy_of_name == '_' + ? preserved_copy_of_name + 1 + : preserved_copy_of_name)); +#else /* STRIP_UNDERSCORE */ + S_SET_NAME(symbolP, preserved_copy_of_name); +#endif /* STRIP_UNDERSCORE */ + + S_SET_SEGMENT(symbolP, segment); + S_SET_VALUE(symbolP, value); + symbol_clear_list_pointers(symbolP); + + symbolP->sy_frag = frag; + symbolP->sy_forward = NULL; /* JF */ + symbolP->sy_number = ~0; + symbolP->sy_name_offset = ~0; + + /* + * Link to end of symbol chain. + */ + symbol_append(symbolP, symbol_lastP, &symbol_rootP, &symbol_lastP); + + obj_symbol_new_hook(symbolP); + +#ifdef DEBUG + verify_symbol_chain(symbol_rootP, symbol_lastP); +#endif /* DEBUG */ + + return(symbolP); +} /* symbol_new() */ + + +/* + * colon() + * + * We have just seen "<name>:". + * Creates a struct symbol unless it already exists. + * + * Gripes if we are redefining a symbol incompatibly (and ignores it). + * + */ +void colon(sym_name) /* just seen "x:" - rattle symbols & frags */ + register char * sym_name; /* symbol name, as a cannonical string */ + /* We copy this string: OK to alter later. */ +{ + register symbolS * symbolP; /* symbol we are working with */ + +#ifdef LOCAL_LABELS_DOLLAR + /* Sun local labels go out of scope whenever a non-local symbol is defined. */ + + if(*sym_name !='L') + bzero((void *) local_label_defined, sizeof(local_label_defined)); +#endif + +#ifndef WORKING_DOT_WORD + if(new_broken_words) { + struct broken_word *a; + int possible_bytes; + fragS *frag_tmp; + char *frag_opcode; + extern md_short_jump_size; + extern md_long_jump_size; + + possible_bytes=md_short_jump_size + new_broken_words * md_long_jump_size; + frag_tmp=frag_now; + frag_opcode=frag_var(rs_broken_word, + possible_bytes, + possible_bytes, + (relax_substateT) 0, + (symbolS *) broken_words, + 0L, + NULL); + + /* We want to store the pointer to where to insert the jump table in the + fr_opcode of the rs_broken_word frag. This requires a little hackery */ + while(frag_tmp && (frag_tmp->fr_type!=rs_broken_word || frag_tmp->fr_opcode)) + frag_tmp=frag_tmp->fr_next; + know(frag_tmp); + frag_tmp->fr_opcode=frag_opcode; + new_broken_words = 0; + + for(a=broken_words;a && a->dispfrag==0;a=a->next_broken_word) + a->dispfrag=frag_tmp; + } +#endif + if ((symbolP = symbol_find(sym_name)) != 0) { +#ifdef VMS + /* + * If the new symbol is .comm AND it has a size of zero, + * we ignore it (i.e. the old symbol overrides it) + */ + if ((SEGMENT_TO_SYMBOL_TYPE((int) now_seg) == (N_UNDF | N_EXT)) && + ((obstack_next_free(& frags) - frag_now->fr_literal) == 0)) + return; + /* + * If the old symbol is .comm and it has a size of zero, + * we override it with the new symbol value. + */ + if ((symbolP->sy_type == (N_UNDF | N_EXT)) + && (S_GET_VALUE(symbolP) == 0)) { + symbolP->sy_frag = frag_now; + symbolP->sy_other = const_flag; + S_SET_VALUE(symbolP, obstack_next_free(& frags) - frag_now->fr_literal); + symbolP->sy_type |= SEGMENT_TO_SYMBOL_TYPE((int) now_seg); /* keep N_EXT bit */ + return; + } +#endif /* VMS */ + /* + * Now check for undefined symbols + */ + if (!S_IS_DEFINED(symbolP)) { + if (S_GET_VALUE(symbolP) == 0) { + symbolP->sy_frag = frag_now; +#ifdef VMS + symbolP->sy_other = const_flag; +#endif + S_SET_VALUE(symbolP, obstack_next_free(&frags) - frag_now->fr_literal); + S_SET_SEGMENT(symbolP, now_seg); +#ifdef N_UNDF + know(N_UNDF == 0); +#endif /* if we have one, it better be zero. */ + + } else { + /* + * There are still several cases to check: + * A .comm/.lcomm symbol being redefined as + * initialized data is OK + * A .comm/.lcomm symbol being redefined with + * a larger size is also OK + * + * This only used to be allowed on VMS gas, but Sun cc + * on the sparc also depends on it. + */ + char New_Type = SEGMENT_TO_SYMBOL_TYPE((int) now_seg); + + if (((!S_IS_DEBUG(symbolP) && !S_IS_DEFINED(symbolP) && S_IS_EXTERNAL(symbolP)) + || (S_GET_SEGMENT(symbolP) == SEG_BSS)) + && ((now_seg == SEG_DATA) + || (now_seg == S_GET_SEGMENT(symbolP)))) { + /* + * Select which of the 2 cases this is + */ + if (now_seg != SEG_DATA) { + /* + * New .comm for prev .comm symbol. + * If the new size is larger we just + * change its value. If the new size + * is smaller, we ignore this symbol + */ + if (S_GET_VALUE(symbolP) + < ((unsigned) (obstack_next_free(& frags) - frag_now->fr_literal))) { + S_SET_VALUE(symbolP, + obstack_next_free(& frags) - + frag_now->fr_literal); + } + } else { + /* + * It is a .comm/.lcomm being converted + * to initialized data. + */ + symbolP->sy_frag = frag_now; +#ifdef VMS + symbolP->sy_other = const_flag; +#endif /* VMS */ + S_SET_VALUE(symbolP, obstack_next_free(& frags) - frag_now->fr_literal); + S_SET_SEGMENT(symbolP, now_seg); /* keep N_EXT bit */ + } + } else { +#ifdef OBJ_COFF + as_fatal("Symbol \"%s\" is already defined as \"%s\"/%d.", + sym_name, + segment_name(S_GET_SEGMENT(symbolP)), + S_GET_VALUE(symbolP)); +#else /* OBJ_COFF */ + as_fatal("Symbol \"%s\" is already defined as \"%s\"/%d.%d.%d.", + sym_name, + segment_name(S_GET_SEGMENT(symbolP)), + S_GET_OTHER(symbolP), S_GET_DESC(symbolP), + S_GET_VALUE(symbolP)); +#endif /* OBJ_COFF */ + } + } /* if the undefined symbol has no value */ + } else { + as_fatal("Symbol %s already defined.", sym_name); + } /* if this symbol is not yet defined */ + + } else { + symbolP = symbol_new(sym_name, + now_seg, + (valueT)(obstack_next_free(&frags)-frag_now->fr_literal), + frag_now); +#ifdef VMS + S_SET_OTHER(symbolP, const_flag); +#endif /* VMS */ + + symbol_table_insert(symbolP); + } /* if we have seen this symbol before */ + + return; +} /* colon() */ + + +/* + * symbol_table_insert() + * + * Die if we can't insert the symbol. + * + */ + +void symbol_table_insert(symbolP) +symbolS *symbolP; +{ + register char *error_string; + + know(symbolP); + know(S_GET_NAME(symbolP)); + + if (*(error_string = hash_jam(sy_hash, S_GET_NAME(symbolP), (char *)symbolP))) { + as_fatal("Inserting \"%s\" into symbol table failed: %s", + S_GET_NAME(symbolP), error_string); + } /* on error */ +} /* symbol_table_insert() */ + +/* + * symbol_find_or_make() + * + * If a symbol name does not exist, create it as undefined, and insert + * it into the symbol table. Return a pointer to it. + */ +symbolS *symbol_find_or_make(name) +char *name; +{ + register symbolS *symbolP; + + symbolP = symbol_find(name); + + if (symbolP == NULL) { + symbolP = symbol_make(name); + + symbol_table_insert(symbolP); + } /* if symbol wasn't found */ + + return(symbolP); +} /* symbol_find_or_make() */ + +symbolS *symbol_make(name) +char *name; +{ + symbolS *symbolP; + + /* Let the machine description default it, e.g. for register names. */ + symbolP = md_undefined_symbol(name); + + if (!symbolP) { + symbolP = symbol_new(name, + SEG_UNKNOWN, + 0, + &zero_address_frag); + } /* if md didn't build us a symbol */ + + return(symbolP); +} /* symbol_make() */ + +/* + * symbol_find() + * + * Implement symbol table lookup. + * In: A symbol's name as a string: '\0' can't be part of a symbol name. + * Out: NULL if the name was not in the symbol table, else the address + * of a struct symbol associated with that name. + */ + +symbolS *symbol_find(name) +char *name; +{ +#ifndef STRIP_UNDERSCORE +#define STRIP_UNDERSCORE 0 +#endif /* STRIP_UNDERSCORE */ + return symbol_find_base(name, STRIP_UNDERSCORE); +} + +symbolS *symbol_find_base(name, strip_underscore) +char *name; +int strip_underscore; +{ + if(strip_underscore && *name == '_') name++; + return ( (symbolS *) hash_find( sy_hash, name )); +} + +/* + * Once upon a time, symbols were kept in a singly linked list. At + * least coff needs to be able to rearrange them from time to time, for + * which a doubly linked list is much more convenient. Loic did these + * as macros which seemed dangerous to me so they're now functions. + * xoxorich. + */ + +/* Link symbol ADDME after symbol TARGET in the chain. */ +void symbol_append(addme, target, rootPP, lastPP) +symbolS *addme; +symbolS *target; +symbolS **rootPP; +symbolS **lastPP; +{ + if (target == NULL) { + know(*rootPP == NULL); + know(*lastPP == NULL); + *rootPP = addme; + *lastPP = addme; + return; + } /* if the list is empty */ + + if (target->sy_next != NULL) { +#ifdef SYMBOLS_NEED_BACKPOINTERS + target->sy_next->sy_previous = addme; +#endif /* SYMBOLS_NEED_BACKPOINTERS */ + } else { + know(*lastPP == target); + *lastPP = addme; + } /* if we have a next */ + + addme->sy_next = target->sy_next; + target->sy_next = addme; + +#ifdef SYMBOLS_NEED_BACKPOINTERS + addme->sy_previous = target; +#endif /* SYMBOLS_NEED_BACKPOINTERS */ + +#ifdef DEBUG + verify_symbol_chain(*rootPP, *lastPP); +#endif /* DEBUG */ + + return; +} /* symbol_append() */ + +#ifdef SYMBOLS_NEED_BACKPOINTERS +/* Remove SYMBOLP from the list. */ +void symbol_remove(symbolP, rootPP, lastPP) +symbolS *symbolP; +symbolS **rootPP; +symbolS **lastPP; +{ + if (symbolP == *rootPP) { + *rootPP = symbolP->sy_next; + } /* if it was the root */ + + if (symbolP == *lastPP) { + *lastPP = symbolP->sy_previous; + } /* if it was the tail */ + + if (symbolP->sy_next != NULL) { + symbolP->sy_next->sy_previous = symbolP->sy_previous; + } /* if not last */ + + if (symbolP->sy_previous != NULL) { + symbolP->sy_previous->sy_next = symbolP->sy_next; + } /* if not first */ + +#ifdef DEBUG + verify_symbol_chain(*rootPP, *lastPP); +#endif /* DEBUG */ + + return; +} /* symbol_remove() */ + +/* Set the chain pointers of SYMBOL to null. */ +void symbol_clear_list_pointers(symbolP) +symbolS *symbolP; +{ + symbolP->sy_next = NULL; + symbolP->sy_previous = NULL; +} /* symbol_clear_list_pointers() */ + +/* Link symbol ADDME before symbol TARGET in the chain. */ +void symbol_insert(addme, target, rootPP, lastPP) +symbolS *addme; +symbolS *target; +symbolS **rootPP; +symbolS **lastPP; +{ + if (target->sy_previous != NULL) { + target->sy_previous->sy_next = addme; + } else { + know(*rootPP == target); + *rootPP = addme; + } /* if not first */ + + addme->sy_previous = target->sy_previous; + target->sy_previous = addme; + addme->sy_next = target; + +#ifdef DEBUG + verify_symbol_chain(*rootPP, *lastPP); +#endif /* DEBUG */ + + return; +} /* symbol_insert() */ +#endif /* SYMBOLS_NEED_BACKPOINTERS */ + +void verify_symbol_chain(rootP, lastP) +symbolS *rootP; +symbolS *lastP; +{ + symbolS *symbolP = rootP; + + if (symbolP == NULL) { + return; + } /* empty chain */ + + for ( ; symbol_next(symbolP) != NULL; symbolP = symbol_next(symbolP)) { +#ifdef SYMBOLS_NEED_BACKPOINTERS + /*$if (symbolP->sy_previous) { + know(symbolP->sy_previous->sy_next == symbolP); + } else { + know(symbolP == rootP); + }$*/ /* both directions */ + know(symbolP->sy_next->sy_previous == symbolP); +#else /* SYMBOLS_NEED_BACKPOINTERS */ + ; +#endif /* SYMBOLS_NEED_BACKPOINTERS */ + } /* verify pointers */ + + know(lastP == symbolP); + + return; +} /* verify_symbol_chain() */ + + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end: symbols.c */ diff --git a/gas/symbols.h b/gas/symbols.h new file mode 100644 index 0000000..8ced0d4 --- /dev/null +++ b/gas/symbols.h @@ -0,0 +1,77 @@ +/* symbols.h - + Copyright (C) 1987, 1990 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ */ + +extern struct obstack notes; /* eg FixS live here. */ + +extern struct obstack cond_obstack; /* this is where we track .ifdef/.endif + (if we do that at all). */ + +extern unsigned int local_bss_counter; /* Zeroed before a pass. */ + /* Only used by .lcomm directive. */ + +extern symbolS * symbol_rootP; /* all the symbol nodes */ +extern symbolS * symbol_lastP; /* last struct symbol we made, or NULL */ + +extern symbolS abs_symbol; + +extern symbolS* dot_text_symbol; +extern symbolS* dot_data_symbol; +extern symbolS* dot_bss_symbol; + +#ifdef __STDC__ + +char *local_label_name(int n, int augend); +symbolS *symbol_find(char *name); +symbolS *symbol_find_base(char *name, int strip_underscore); +symbolS *symbol_find_or_make(char *name); +symbolS *symbol_make(char *name); +symbolS *symbol_new(char *name, segT segment, long value, fragS *frag); +void colon(char *sym_name); +void local_colon(int n); +void symbol_begin(void); +void symbol_table_insert(symbolS *symbolP); +void verify_symbol_chain(symbolS *rootP, symbolS *lastP); + +#else + +char *local_label_name(); +symbolS *symbol_find(); +symbolS *symbol_find_base(); +symbolS *symbol_find_or_make(); +symbolS *symbol_make(); +symbolS *symbol_new(); +void colon(); +void local_colon(); +void symbol_begin(); +void symbol_table_insert(); +void verify_symbol_chain(); + +#endif /* __STDC__ */ + + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end: symbols.h */ diff --git a/gas/tc.h b/gas/tc.h new file mode 100644 index 0000000..b87ba60 --- /dev/null +++ b/gas/tc.h @@ -0,0 +1,112 @@ +/* tc.h -target cpu dependent- */ + +/* Copyright (C) 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +/* In theory (mine, at least!) the machine dependent part of the assembler + should only have to include one file. This one. -- JF */ + +extern const pseudo_typeS md_pseudo_table[]; + +/* JF moved this here from as.h under the theory that nobody except MACHINE.c + and write.c care about it anyway. */ + +typedef struct +{ + long rlx_forward; /* Forward reach. Signed number. > 0. */ + long rlx_backward; /* Backward reach. Signed number. < 0. */ + unsigned char rlx_length; /* Bytes length of this address. */ + relax_substateT rlx_more; /* Next longer relax-state. */ + /* 0 means there is no 'next' relax-state. */ +} +relax_typeS; + +extern const relax_typeS md_relax_table[]; /* Define it in MACHINE.c */ + +extern int md_reloc_size; /* Size of a relocation record */ + +extern void (*md_emit_relocations)(); + +#ifdef __STDC__ + +char *md_atof(int what_statement_type, char *literalP, int *sizeP); +int md_estimate_size_before_relax(fragS *fragP, segT segtype); +int md_parse_option(char **argP, int *cntP, char ***vecP); +long md_pcrel_from(fixS *fixP); +long md_section_align(segT seg, long align); +short tc_coff_fix2rtype(fixS *fixP); +symbolS *md_undefined_symbol(char *name); +void md_apply_fix(fixS *fixP, long val); +void md_assemble(char *str); +void md_begin(void); +void md_convert_frag(fragS *fragP); +void md_create_long_jump(char *ptr, long from_addr, long to_addr, fragS *frag, symbolS *to_symbol); +void md_create_short_jump(char *ptr, long from_addr, long to_addr, fragS *frag, symbolS *to_symbol); +void md_end(void); +void md_number_to_chars(char *buf, long val, int n); +void md_operand(expressionS *expressionP); +void md_ri_to_chars(char *the_bytes, struct reloc_info_generic *ri); + +#ifndef tc_crawl_symbol_chain +void tc_crawl_symbol_chain(object_headers *headers); +#endif /* tc_crawl_symbol_chain */ + +#ifndef tc_headers_hook +void tc_headers_hook(object_headers *headers); +#endif /* tc_headers_hook */ + +#else + +char *md_atof(); +int md_estimate_size_before_relax(); +int md_parse_option(); +long md_pcrel_from(); +long md_section_align(); +short tc_coff_fix2rtype(); +symbolS *md_undefined_symbol(); +void md_apply_fix(); +void md_assemble(); +void md_begin(); +void md_convert_frag(); +void md_create_long_jump(); +void md_create_short_jump(); +void md_end(); +void md_number_to_chars(); +void md_operand(); +void md_ri_to_chars(); + +#ifndef tc_headers_hook +void tc_headers_hook(); +#endif /* tc_headers_hook */ + +#ifndef tc_crawl_symbol_chain +void tc_crawl_symbol_chain(); +#endif /* tc_crawl_symbol_chain */ + +#endif /* __STDC_ */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of tp.h */ diff --git a/gas/write.c b/gas/write.c new file mode 100644 index 0000000..a0b36c3 --- /dev/null +++ b/gas/write.c @@ -0,0 +1,1162 @@ +/* write.c - emit .o file + Copyright (C) 1986, 1987, 1990, 1991 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* static const char rcsid[] = "$Id$"; */ + +/* + + This thing should be set up to do byteordering correctly. But... + + In order to cross-assemble the target machine must have an a.out header + similar to the one in a.out.h on THIS machine. Byteorder doesn't matter, + we take special care of it, but the numbers must be the same SIZE (# of + bytes) and in the same PLACE. If this is not true, you will have some + trouble. + */ + +#include "as.h" + +#include "subsegs.h" +#include "obstack.h" +#include "output-file.h" + +/* Hook for machine dependent relocation information output routine. + If not defined, the variable is allocated in BSS (Fortran common model). + If some other module defines it, we will see their value. */ + +void (*md_emit_relocations)(); + +/* + * In: length of relocation (or of address) in chars: 1, 2 or 4. + * Out: GNU LD relocation length code: 0, 1, or 2. + */ + +unsigned char +nbytes_r_length [] = { + 42, 0, 1, 42, 2 + }; + + +static struct frag *text_frag_root; +static struct frag *data_frag_root; + +static struct frag *text_last_frag; /* Last frag in segment. */ +static struct frag *data_last_frag; /* Last frag in segment. */ + +static object_headers headers; + +long string_byte_count; + +static char *the_object_file; + +char *next_object_file_charP; /* Tracks object file bytes. */ + +int magic_number_for_object_file = DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE; + +/* static long length; JF unused */ /* String length, including trailing '\0'. */ + + +#ifdef __STDC__ + +static int is_dnrange(struct frag *f1, struct frag *f2); +static long fixup_segment(fixS *fixP, segT this_segment_type); +static relax_addressT relax_align(relax_addressT address, long alignment); +static void relax_segment(struct frag *segment_frag_root, segT segment_type); + +#else + +static int is_dnrange(); +static long fixup_segment(); +static relax_addressT relax_align(); +static void relax_segment(); + +#endif /* __STDC__ */ + +/* + * fix_new() + * + * Create a fixS in obstack 'notes'. + */ +fixS *fix_new(frag, where, size, add_symbol, sub_symbol, offset, pcrel, r_type) +fragS *frag; /* Which frag? */ +int where; /* Where in that frag? */ +short int size; /* 1, 2 or 4 usually. */ +symbolS *add_symbol; /* X_add_symbol. */ +symbolS *sub_symbol; /* X_subtract_symbol. */ +long offset; /* X_add_number. */ +int pcrel; /* TRUE if PC-relative relocation. */ +enum reloc_type r_type; /* Relocation type */ +{ + register fixS * fixP; + + fixP = (fixS *)obstack_alloc(¬es,sizeof(fixS)); + + fixP->fx_frag = frag; + fixP->fx_where = where; + fixP->fx_size = size; + fixP->fx_addsy = add_symbol; + fixP->fx_subsy = sub_symbol; + fixP->fx_offset = offset; + fixP->fx_pcrel = pcrel; + fixP->fx_r_type = r_type; + fixP->fx_next = NULL; + + /* JF these 'cuz of the NS32K stuff */ + fixP->fx_im_disp = 0; + fixP->fx_pcrel_adjust = 0; + fixP->fx_bsr = 0; + fixP->fx_bit_fixP = 0; + + if (*seg_fix_tailP) + (*seg_fix_tailP)->fx_next = fixP; + else + *seg_fix_rootP = fixP; + *seg_fix_tailP = fixP; + fixP->fx_callj = 0; + return fixP; +} + +void write_object_file() { + register struct frchain * frchainP; /* Track along all frchains. */ + register fragS * fragP; /* Track along all frags. */ + register struct frchain * next_frchainP; + register fragS * * prev_fragPP; +/* register char * name; */ +/* symbolS *symbolP; */ +/* register symbolS ** symbolPP; */ + /* register fixS * fixP; JF unused */ + unsigned int data_siz; + +#ifdef DONTDEF + void gdb_emit(); + void gdb_end(); +#endif + long object_file_size; + +#ifdef VMS + /* + * Under VMS we try to be compatible with VAX-11 "C". Thus, we + * call a routine to check for the definition of the procedure + * "_main", and if so -- fix it up so that it can be program + * entry point. + */ + VMS_Check_For_Main(); +#endif /* VMS */ + /* + * After every sub-segment, we fake an ".align ...". This conforms to BSD4.2 + * brane-damage. We then fake ".fill 0" because that is the kind of frag + * that requires least thought. ".align" frags like to have a following + * frag since that makes calculating their intended length trivial. + */ +#define SUB_SEGMENT_ALIGN (2) + for (frchainP = frchain_root; frchainP; frchainP = frchainP->frch_next) { +#ifdef VMS + /* + * Under VAX/VMS, the linker (and PSECT specifications) + * take care of correctly aligning the segments. + * Doing the alignment here (on initialized data) can + * mess up the calculation of global data PSECT sizes. + */ +#undef SUB_SEGMENT_ALIGN +#define SUB_SEGMENT_ALIGN ((frchainP->frch_seg != SEG_DATA) ? 2 : 0) +#endif /* VMS */ + subseg_new (frchainP->frch_seg, frchainP->frch_subseg); + frag_align (SUB_SEGMENT_ALIGN, 0); + /* frag_align will have left a new frag. */ + /* Use this last frag for an empty ".fill". */ + /* + * For this segment ... + * Create a last frag. Do not leave a "being filled in frag". + */ + frag_wane (frag_now); + frag_now->fr_fix = 0; + know( frag_now->fr_next == NULL ); + /* know( frags . obstack_c_base == frags . obstack_c_next_free ); */ + /* Above shows we haven't left a half-completed object on obstack. */ + } /* walk the frag chain */ + + /* + * From now on, we don't care about sub-segments. + * Build one frag chain for each segment. Linked thru fr_next. + * We know that there is at least 1 text frchain & at least 1 data frchain. + */ + prev_fragPP = &text_frag_root; + for (frchainP = frchain_root; frchainP; frchainP = next_frchainP) { + know( frchainP->frch_root ); + * prev_fragPP = frchainP->frch_root; + prev_fragPP = & frchainP->frch_last->fr_next; + + if (((next_frchainP = frchainP->frch_next) == NULL) + || next_frchainP == data0_frchainP) { + prev_fragPP = & data_frag_root; + if (next_frchainP) { + text_last_frag = frchainP->frch_last; + } else { + data_last_frag = frchainP->frch_last; + } + } + } /* walk the frag chain */ + + /* + * We have two segments. If user gave -R flag, then we must put the + * data frags into the text segment. Do this before relaxing so + * we know to take advantage of -R and make shorter addresses. + */ + if (flagseen[ 'R' ]) { + fixS *tmp; + + text_last_frag->fr_next = data_frag_root; + text_last_frag = data_last_frag; + data_last_frag = NULL; + data_frag_root = NULL; + if (text_fix_root) { + for (tmp = text_fix_root; tmp->fx_next; tmp = tmp->fx_next) ;; + tmp->fx_next=data_fix_root; + } else + text_fix_root=data_fix_root; + data_fix_root=NULL; + } + + relax_segment(text_frag_root, SEG_TEXT); + relax_segment(data_frag_root, SEG_DATA); + /* + * Now the addresses of frags are correct within the segment. + */ + + know(text_last_frag->fr_type == rs_fill && text_last_frag->fr_offset == 0); + H_SET_TEXT_SIZE(&headers,text_last_frag->fr_address); + text_last_frag->fr_address=H_GET_TEXT_SIZE(&headers); + + /* + * Join the 2 segments into 1 huge segment. + * To do this, re-compute every rn_address in the SEG_DATA frags. + * Then join the data frags after the text frags. + * + * Determine a_data [length of data segment]. + */ + if (data_frag_root) { + register relax_addressT slide; + + know( text_last_frag->fr_type == rs_fill && text_last_frag->fr_offset == 0 ); + H_SET_DATA_SIZE(&headers, data_last_frag->fr_address); + data_last_frag->fr_address = H_GET_DATA_SIZE(&headers); + slide = H_GET_TEXT_SIZE(&headers); /* & in file of the data segment. */ + + for (fragP = data_frag_root; + fragP; + fragP = fragP->fr_next) + { + fragP->fr_address += slide; + } + know( text_last_frag ); + text_last_frag->fr_next = data_frag_root; + } else { + H_SET_DATA_SIZE(&headers,0); + data_siz = 0; + } + + bss_address_frag.fr_address = H_GET_TEXT_SIZE(&headers) + + H_GET_DATA_SIZE(&headers); + + H_SET_BSS_SIZE(&headers,local_bss_counter); + + /* + * + * Crawl the symbol chain. + * + * For each symbol whose value depends on a frag, take the address of + * that frag and subsume it into the value of the symbol. + * After this, there is just one way to lookup a symbol value. + * Values are left in their final state for object file emission. + * We adjust the values of 'L' local symbols, even if we do + * not intend to emit them to the object file, because their values + * are needed for fix-ups. + * + * Unless we saw a -L flag, remove all symbols that begin with 'L' + * from the symbol chain. (They are still pointed to by the fixes.) + * + * Count the remaining symbols. + * Assign a symbol number to each symbol. + * Count the number of string-table chars we will emit. + * Put this info into the headers as appropriate. + * + */ + know(zero_address_frag.fr_address == 0); + string_byte_count = sizeof(string_byte_count); + + obj_crawl_symbol_chain(&headers); + + if (string_byte_count == sizeof(string_byte_count)) { + string_byte_count = 0; + } /* if no strings, then no count. */ + + H_SET_STRING_SIZE(&headers, string_byte_count); + + /* + * Addresses of frags now reflect addresses we use in the object file. + * Symbol values are correct. + * Scan the frags, converting any ".org"s and ".align"s to ".fill"s. + * Also converting any machine-dependent frags using md_convert_frag(); + */ + subseg_change(SEG_TEXT, 0); + + for (fragP = text_frag_root; fragP; fragP = fragP->fr_next) { + switch (fragP->fr_type) { + case rs_align: + case rs_org: + fragP->fr_type = rs_fill; + know( fragP->fr_var == 1 ); + know( fragP->fr_next ); + fragP->fr_offset + = fragP->fr_next->fr_address + - fragP->fr_address + - fragP->fr_fix; + break; + + case rs_fill: + break; + + case rs_machine_dependent: + md_convert_frag (fragP); + /* + * After md_convert_frag, we make the frag into a ".space 0". + * Md_convert_frag() should set up any fixSs and constants + * required. + */ + frag_wane (fragP); + break; + +#ifndef WORKING_DOT_WORD + case rs_broken_word: { + struct broken_word *lie; + extern md_short_jump_size; + extern md_long_jump_size; + + if (fragP->fr_subtype) { + fragP->fr_fix+=md_short_jump_size; + for (lie=(struct broken_word *)(fragP->fr_symbol);lie && lie->dispfrag==fragP;lie=lie->next_broken_word) + if (lie->added==1) + fragP->fr_fix+=md_long_jump_size; + } + frag_wane(fragP); + } + break; +#endif + + default: + BAD_CASE( fragP->fr_type ); + break; + } /* switch (fr_type) */ + } /* for each frag. */ + +#ifndef WORKING_DOT_WORD + { + struct broken_word *lie; + struct broken_word **prevP; + + prevP= &broken_words; + for (lie=broken_words; lie; lie=lie->next_broken_word) + if (!lie->added) { +#ifdef TC_NS32K + fix_new_ns32k(lie->frag, + lie->word_goes_here - lie->frag->fr_literal, + 2, + lie->add, + lie->sub, + lie->addnum, + 0, 0, 2, 0, 0); +#else /* TC_NS32K */ + fix_new( lie->frag, lie->word_goes_here - lie->frag->fr_literal, + 2, lie->add, + lie->sub, lie->addnum, + 0, NO_RELOC); +#endif /* TC_NS32K */ + /* md_number_to_chars(lie->word_goes_here, + S_GET_VALUE(lie->add) + + lie->addnum + - S_GET_VALUE(lie->sub), + 2); */ + *prevP=lie->next_broken_word; + } else + prevP= &(lie->next_broken_word); + + for (lie=broken_words;lie;) { + struct broken_word *untruth; + char *table_ptr; + long table_addr; + long from_addr, + to_addr; + int n, + m; + + extern md_short_jump_size; + extern md_long_jump_size; + + fragP=lie->dispfrag; + + /* Find out how many broken_words go here */ + n=0; + for (untruth=lie;untruth && untruth->dispfrag==fragP;untruth=untruth->next_broken_word) + if (untruth->added==1) + n++; + + table_ptr=lie->dispfrag->fr_opcode; + table_addr=lie->dispfrag->fr_address+(table_ptr - lie->dispfrag->fr_literal); + /* Create the jump around the long jumps */ + /* This is a short jump from table_ptr+0 to table_ptr+n*long_jump_size */ + from_addr=table_addr; + to_addr = table_addr + md_short_jump_size + n * md_long_jump_size; + md_create_short_jump(table_ptr, from_addr, to_addr, lie->dispfrag, lie->add); + table_ptr+=md_short_jump_size; + table_addr+=md_short_jump_size; + + for (m=0;lie && lie->dispfrag==fragP;m++,lie=lie->next_broken_word) { + if (lie->added==2) + continue; + /* Patch the jump table */ + /* This is the offset from ??? to table_ptr+0 */ + to_addr = table_addr + - S_GET_VALUE(lie->sub); + md_number_to_chars(lie->word_goes_here,to_addr,2); + for (untruth=lie->next_broken_word;untruth && untruth->dispfrag==fragP;untruth=untruth->next_broken_word) { + if (untruth->use_jump==lie) + md_number_to_chars(untruth->word_goes_here,to_addr,2); + } + + /* Install the long jump */ + /* this is a long jump from table_ptr+0 to the final target */ + from_addr=table_addr; + to_addr=S_GET_VALUE(lie->add) + lie->addnum; + md_create_long_jump(table_ptr,from_addr,to_addr,lie->dispfrag,lie->add); + table_ptr+=md_long_jump_size; + table_addr+=md_long_jump_size; + } + } + } +#endif /* not WORKING_DOT_WORD */ + +#ifndef VMS + { /* not vms */ + /* + * Scan every FixS performing fixups. We had to wait until now to do + * this because md_convert_frag() may have made some fixSs. + */ + H_SET_RELOCATION_SIZE(&headers, + md_reloc_size * fixup_segment(text_fix_root, SEG_TEXT), + md_reloc_size * fixup_segment(data_fix_root, SEG_DATA)); + + /* FIXME move this stuff into the pre-write-hook */ + H_SET_MAGIC_NUMBER(&headers, magic_number_for_object_file); + H_SET_ENTRY_POINT(&headers,0); + +#ifdef EXEC_MACHINE_TYPE + H_SET_MACHINE_TYPE(&headers,EXEC_MACHINE_TYPE); +#endif +#ifdef EXEC_VERSION + H_SET_VERSION(&headers,EXEC_VERSION); +#endif + + obj_pre_write_hook(&headers); /* extra coff stuff */ + + if ((had_warnings() && flagseen['Z']) + || had_errors() > 0) { + if (flagseen['Z']) { + as_warn("%d error%s, %d warning%s, generating bad object file.\n", + had_errors(), had_errors() == 1 ? "" : "s", + had_warnings(), had_warnings() == 1 ? "" : "s"); + } else { + as_fatal("%d error%s, %d warning%s, no object file generated.\n", + had_errors(), had_errors() == 1 ? "" : "s", + had_warnings(), had_warnings() == 1 ? "" : "s"); + } /* on want output */ + } /* on error condition */ + + object_file_size = H_GET_FILE_SIZE(&headers); + next_object_file_charP = the_object_file = xmalloc(object_file_size); + + output_file_create(out_file_name); + + obj_header_append(&next_object_file_charP, &headers); + + /* + * Emit code. + */ + for (fragP = text_frag_root; fragP; fragP = fragP->fr_next) { + register long count; + register char * fill_literal; + register long fill_size; + + know( fragP->fr_type == rs_fill ); + append (& next_object_file_charP, fragP->fr_literal, (unsigned long)fragP->fr_fix); + fill_literal= fragP->fr_literal + fragP->fr_fix; + fill_size = fragP->fr_var; + know( fragP->fr_offset >= 0 ); + for (count = fragP->fr_offset; count; count --) + append (& next_object_file_charP, fill_literal, (unsigned long)fill_size); + } /* for each code frag. */ + + /* + * Emit relocations. + */ + obj_emit_relocations(&next_object_file_charP, text_fix_root, (relax_addressT)0); + +#ifdef TC_I960 + /* Make addresses in data relocation directives relative to beginning of + * first data fragment, not end of last text fragment: alignment of the + * start of the data segment may place a gap between the segments. + */ + obj_emit_relocations(&next_object_file_charP, data_fix_root, data0_frchainP->frch_root->fr_address); +#else /* TC_I960 */ + obj_emit_relocations(&next_object_file_charP, data_fix_root, text_last_frag->fr_address); +#endif /* TC_I960 */ + + /* + * Emit line number entries. + */ + OBJ_EMIT_LINENO(&next_object_file_charP, lineno_rootP, the_object_file); + + /* + * Emit symbols. + */ + obj_emit_symbols(&next_object_file_charP, symbol_rootP); + + /* + * Emit strings. + */ + + if (string_byte_count > 0) { + obj_emit_strings(&next_object_file_charP); + } /* only if we have a string table */ + + know(next_object_file_charP == the_object_file + object_file_size); + /* Write the data to the file */ + output_file_append(the_object_file,object_file_size,out_file_name); + +#ifdef DONTDEF + if (flagseen['G']) /* GDB symbol file to be appended? */ + { + gdb_emit (out_file_name); + gdb_end (); + } +#endif /* DONTDEF */ + + output_file_close(out_file_name); + } /* non vms output */ +#else /* VMS */ + /* + * Now do the VMS-dependent part of writing the object file + */ + VMS_write_object_file(text_siz, data_siz, text_frag_root, data_frag_root); +#endif /* VMS */ +} /* write_object_file() */ + +/* + * relax_segment() + * + * Now we have a segment, not a crowd of sub-segments, we can make fr_address + * values. + * + * Relax the frags. + * + * After this, all frags in this segment have addresses that are correct + * within the segment. Since segments live in different file addresses, + * these frag addresses may not be the same as final object-file addresses. + */ +#ifndef VMS +static +#endif /* not VMS */ +void relax_segment(segment_frag_root, segment_type) + struct frag * segment_frag_root; + segT segment_type; /* SEG_DATA or SEG_TEXT */ +{ + register struct frag * fragP; + register relax_addressT address; + /* register relax_addressT old_address; JF unused */ + /* register relax_addressT new_address; JF unused */ + + know( segment_type == SEG_DATA || segment_type == SEG_TEXT ); + + /* In case md_estimate_size_before_relax() wants to make fixSs. */ + subseg_change(segment_type, 0); + + /* + * For each frag in segment: count and store (a 1st guess of) fr_address. + */ + address = 0; + for ( fragP = segment_frag_root; fragP; fragP = fragP->fr_next ) + { + fragP->fr_address = address; + address += fragP->fr_fix; + switch (fragP->fr_type) + { + case rs_fill: + address += fragP->fr_offset * fragP->fr_var; + break; + + case rs_align: + address += relax_align(address, fragP->fr_offset); + break; + + case rs_org: + /* + * Assume .org is nugatory. It will grow with 1st relax. + */ + break; + + case rs_machine_dependent: + address += md_estimate_size_before_relax(fragP, segment_type); + break; + +#ifndef WORKING_DOT_WORD + /* Broken words don't concern us yet */ + case rs_broken_word: + break; +#endif + + default: + BAD_CASE( fragP->fr_type ); + break; + } /* switch(fr_type) */ + } /* for each frag in the segment */ + + /* + * Do relax(). + */ + { + register long stretch; /* May be any size, 0 or negative. */ + /* Cumulative number of addresses we have */ + /* relaxed this pass. */ + /* We may have relaxed more than one address. */ + register long stretched; /* Have we stretched on this pass? */ + /* This is 'cuz stretch may be zero, when, + in fact some piece of code grew, and + another shrank. If a branch instruction + doesn't fit anymore, we could be scrod */ + + do + { + stretch = stretched = 0; + for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next) + { + register long growth = 0; + register unsigned long was_address; + /* register long var; */ + register long offset; + register symbolS *symbolP; + register long target; + register long after; + register long aim; + + was_address = fragP->fr_address; + address = fragP->fr_address += stretch; + symbolP = fragP->fr_symbol; + offset = fragP->fr_offset; + /* var = fragP->fr_var; */ + switch (fragP->fr_type) + { + case rs_fill: /* .fill never relaxes. */ + growth = 0; + break; + +#ifndef WORKING_DOT_WORD + /* JF: This is RMS's idea. I do *NOT* want to be blamed + for it I do not want to write it. I do not want to have + anything to do with it. This is not the proper way to + implement this misfeature. */ + case rs_broken_word: + { + struct broken_word *lie; + struct broken_word *untruth; + extern int md_short_jump_size; + extern int md_long_jump_size; + + /* Yes this is ugly (storing the broken_word pointer + in the symbol slot). Still, this whole chunk of + code is ugly, and I don't feel like doing anything + about it. Think of it as stubbornness in action */ + growth=0; + for (lie=(struct broken_word *)(fragP->fr_symbol); + lie && lie->dispfrag==fragP; + lie=lie->next_broken_word) { + + if (lie->added) + continue; + offset= lie->add->sy_frag->fr_address+ S_GET_VALUE(lie->add) + lie->addnum - + (lie->sub->sy_frag->fr_address+ S_GET_VALUE(lie->sub)); + if (offset<=-32768 || offset>=32767) { + if (flagseen['k']) + as_warn(".word %s-%s+%ld didn't fit", + S_GET_NAME(lie->add), + S_GET_NAME(lie->sub), + lie->addnum); + lie->added=1; + if (fragP->fr_subtype==0) { + fragP->fr_subtype++; + growth+=md_short_jump_size; + } + for (untruth=lie->next_broken_word;untruth && untruth->dispfrag==lie->dispfrag;untruth=untruth->next_broken_word) + if ((untruth->add->sy_frag == lie->add->sy_frag) + && S_GET_VALUE(untruth->add) == S_GET_VALUE(lie->add)) { + untruth->added=2; + untruth->use_jump=lie; + } + growth+=md_long_jump_size; + } + } + } + break; +#endif + case rs_align: + growth = relax_align ((relax_addressT)(address + fragP->fr_fix), offset) + - relax_align ((relax_addressT)(was_address + fragP->fr_fix), offset); + break; + + case rs_org: + target = offset; + if (symbolP) + { + know((S_GET_SEGMENT(symbolP) == SEG_ABSOLUTE) || + (S_GET_SEGMENT(symbolP) == SEG_DATA) || + (S_GET_SEGMENT(symbolP) == SEG_TEXT)); + know(symbolP->sy_frag); + know(!(S_GET_SEGMENT(symbolP) == SEG_ABSOLUTE) || + symbolP->sy_frag==&zero_address_frag ); + target += + S_GET_VALUE(symbolP) + + symbolP->sy_frag->fr_address; + } + know( fragP->fr_next ); + after = fragP->fr_next->fr_address; + growth = ((target - after ) > 0) ? (target - after) : 0; + /* Growth may be -ve, but variable part */ + /* of frag cannot have < 0 chars. */ + /* That is, we can't .org backwards. */ + + growth -= stretch; /* This is an absolute growth factor */ + break; + + case rs_machine_dependent: + { + register const relax_typeS * this_type; + register const relax_typeS * start_type; + register relax_substateT next_state; + register relax_substateT this_state; + + start_type = this_type + = md_relax_table + (this_state = fragP->fr_subtype); + target = offset; + if (symbolP) + { + know((S_GET_SEGMENT(symbolP) == SEG_ABSOLUTE) || + (S_GET_SEGMENT(symbolP) == SEG_DATA) || + (S_GET_SEGMENT(symbolP) == SEG_TEXT)); + know(symbolP->sy_frag); + know(!(S_GET_SEGMENT(symbolP) == SEG_ABSOLUTE) || + symbolP->sy_frag==&zero_address_frag ); + target += + S_GET_VALUE(symbolP) + + symbolP->sy_frag->fr_address; + + /* If frag has yet to be reached on this pass, + assume it will move by STRETCH just as we did. + If this is not so, it will be because some frag + between grows, and that will force another pass. */ + + /* JF was just address */ + /* JF also added is_dnrange hack */ + /* There's gotta be a better/faster/etc way + to do this. . . */ + /* gnu@cygnus.com: I changed this from > to >= + because I ran into a zero-length frag (fr_fix=0) + which was created when the obstack needed a new + chunk JUST AFTER the opcode of a branch. Since + fr_fix is zero, fr_address of this frag is the same + as fr_address of the next frag. This + zero-length frag was variable and jumped to .+2 + (in the next frag), but since the > comparison + below failed (the two were =, not >), "stretch" + was not added to the target. Stretch was 178, so + the offset appeared to be .-176 instead, which did + not fit into a byte branch, so the assembler + relaxed the branch to a word. This didn't compare + with what happened when the same source file was + assembled on other machines, which is how I found it. + You might want to think about what other places have + trouble with zero length frags... */ + + if (symbolP->sy_frag->fr_address >= was_address && is_dnrange(fragP,symbolP->sy_frag)) + target += stretch; + + } + aim = target - address - fragP->fr_fix; + /* The displacement is affected by the instruction size + * for the 32k architecture. I think we ought to be able + * to add fragP->fr_pcrel_adjust in all cases (it should be + * zero if not used), but just in case it breaks something + * else we'll put this inside #ifdef NS32K ... #endif + */ +#ifdef TC_NS32K + aim += fragP->fr_pcrel_adjust; +#endif /* TC_NS32K */ + + if (aim < 0) + { + /* Look backwards. */ + for (next_state = this_type->rlx_more; next_state; ) + { + if (aim >= this_type->rlx_backward) + next_state = 0; + else + { /* Grow to next state. */ + this_type = md_relax_table + (this_state = next_state); + next_state = this_type->rlx_more; + } + } + } + else + { +#ifdef DONTDEF +/* JF these next few lines of code are for the mc68020 which can't handle short + offsets of zero in branch instructions. What a kludge! */ + if (aim==0 && this_state==(1<<2+0)) { /* FOO hard encoded from m.c */ + aim=this_type->rlx_forward+1; /* Force relaxation into word mode */ + } +#endif +/* JF end of 68020 code */ + /* Look forwards. */ + for (next_state = this_type->rlx_more; next_state; ) + { + if (aim <= this_type->rlx_forward) + next_state = 0; + else + { /* Grow to next state. */ + this_type = md_relax_table + (this_state = next_state); + next_state = this_type->rlx_more; + } + } + } + if ((growth = this_type->rlx_length - start_type->rlx_length) != 0) + fragP->fr_subtype = this_state; + } + break; + + default: + BAD_CASE( fragP->fr_type ); + break; + } + if (growth) { + stretch += growth; + stretched++; + } + } /* For each frag in the segment. */ + } while (stretched); /* Until nothing further to relax. */ + } + + /* + * We now have valid fr_address'es for each frag. + */ + + /* + * All fr_address's are correct, relative to their own segment. + * We have made all the fixS we will ever make. + */ +} /* relax_segment() */ + +/* + * Relax_align. Advance location counter to next address that has 'alignment' + * lowest order bits all 0s. + */ + + /* How many addresses does the .align take? */ +static relax_addressT relax_align(address, alignment) +register relax_addressT address; /* Address now. */ +register long alignment; /* Alignment (binary). */ +{ + relax_addressT mask; + relax_addressT new_address; + + mask = ~ ( (~0) << alignment ); + new_address = (address + mask) & (~ mask); + return (new_address - address); +} /* relax_align() */ + +/* fixup_segment() + + Go through all the fixS's in a segment and see which ones can be + handled now. (These consist of fixS where we have since discovered + the value of a symbol, or the address of the frag involved.) + For each one, call md_apply_fix to put the fix into the frag data. + + Result is a count of how many relocation structs will be needed to + handle the remaining fixS's that we couldn't completely handle here. + These will be output later by emit_relocations(). */ + +static long fixup_segment(fixP, this_segment_type) +register fixS * fixP; +segT this_segment_type; /* N_TYPE bits for segment. */ +{ + register long seg_reloc_count; + register symbolS *add_symbolP; + register symbolS *sub_symbolP; + register long add_number; + register int size; + register char *place; + register long where; + register char pcrel; + register fragS *fragP; + register segT add_symbol_segment = SEG_ABSOLUTE; + fixS *topP = fixP; + + + seg_reloc_count = 0; + + for ( ; fixP; fixP = fixP->fx_next) { + fragP = fixP->fx_frag; + know(fragP); + where = fixP->fx_where; + place = fragP->fr_literal + where; + size = fixP->fx_size; + add_symbolP = fixP->fx_addsy; +#ifdef TC_I960 + if (fixP->fx_callj && TC_S_IS_CALLNAME(add_symbolP)) { + /* Relocation should be done via the + associated 'bal' entry point + symbol. */ + + if (!TC_S_IS_BALNAME(tc_get_bal_of_call(add_symbolP))) { + as_bad("No 'bal' entry point for leafproc %s", + S_GET_NAME(add_symbolP)); + continue; + } + fixP->fx_addsy = add_symbolP = tc_get_bal_of_call(add_symbolP); + } /* callj relocation */ +#endif + sub_symbolP = fixP->fx_subsy; + add_number = fixP->fx_offset; + pcrel = fixP->fx_pcrel; + + if (add_symbolP) { + add_symbol_segment = S_GET_SEGMENT(add_symbolP); + } /* if there is an addend */ + + if (sub_symbolP) { + if (!add_symbolP) { + /* Its just -sym */ + if (S_GET_SEGMENT(sub_symbolP) != SEG_ABSOLUTE) { + as_bad("Negative of non-absolute symbol %s", S_GET_NAME(sub_symbolP)); + } /* not absolute */ + + add_number -= S_GET_VALUE(sub_symbolP); + + /* if sub_symbol is in the same segment that add_symbol + and add_symbol is either in DATA, TEXT, BSS or ABSOLUTE */ + } else if ((S_GET_SEGMENT(sub_symbolP) == add_symbol_segment) + && ((add_symbol_segment == SEG_DATA) + || (add_symbol_segment == SEG_TEXT) + || (add_symbol_segment == SEG_BSS) + || (add_symbol_segment == SEG_ABSOLUTE))) { + /* Difference of 2 symbols from same segment. */ + /* Can't make difference of 2 undefineds: 'value' means */ + /* something different for N_UNDF. */ +#ifdef TC_I960 + /* Makes no sense to use the difference of 2 arbitrary symbols + * as the target of a call instruction. + */ + if (fixP->fx_callj) { + as_bad("callj to difference of 2 symbols"); + } +#endif /* TC_I960 */ + add_number += S_GET_VALUE(add_symbolP) - + S_GET_VALUE(sub_symbolP); + + add_symbolP = NULL; + fixP->fx_addsy = NULL; + } else { + /* Different segments in subtraction. */ + know(!(S_IS_EXTERNAL(sub_symbolP) && (S_GET_SEGMENT(sub_symbolP) == SEG_ABSOLUTE))); + + if ((S_GET_SEGMENT(sub_symbolP) == SEG_ABSOLUTE)) { + add_number -= S_GET_VALUE(sub_symbolP); + } else { + as_bad("Can't emit reloc {- %s-seg symbol \"%s\"} @ file address %d.", + segment_name(S_GET_SEGMENT(sub_symbolP)), + S_GET_NAME(sub_symbolP), fragP->fr_address + where); + } /* if absolute */ + } + } /* if sub_symbolP */ + + if (add_symbolP) { + if (add_symbol_segment == this_segment_type && pcrel) { + /* + * This fixup was made when the symbol's segment was + * SEG_UNKNOWN, but it is now in the local segment. + * So we know how to do the address without relocation. + */ +#ifdef TC_I960 + /* reloc_callj() may replace a 'call' with a 'calls' or a 'bal', + * in which cases it modifies *fixP as appropriate. In the case + * of a 'calls', no further work is required, and *fixP has been + * set up to make the rest of the code below a no-op. + */ + reloc_callj(fixP); +#endif /* TC_I960 */ + + add_number += S_GET_VALUE(add_symbolP); + add_number -= md_pcrel_from (fixP); + pcrel = 0; /* Lie. Don't want further pcrel processing. */ + fixP->fx_addsy = NULL; /* No relocations please. */ + } else { + switch (add_symbol_segment) { + case SEG_ABSOLUTE: +#ifdef TC_I960 + reloc_callj(fixP); /* See comment about reloc_callj() above*/ +#endif /* TC_I960 */ + add_number += S_GET_VALUE(add_symbolP); + fixP->fx_addsy = NULL; + add_symbolP = NULL; + break; + + case SEG_BSS: + case SEG_DATA: + case SEG_TEXT: + seg_reloc_count ++; + add_number += S_GET_VALUE(add_symbolP); + break; + + case SEG_UNKNOWN: +#ifdef TC_I960 + if ((int)fixP->fx_bit_fixP == 13) { + /* This is a COBR instruction. They have only a + * 13-bit displacement and are only to be used + * for local branches: flag as error, don't generate + * relocation. + */ + as_bad("can't use COBR format with external label"); + fixP->fx_addsy = NULL; /* No relocations please. */ + continue; + } /* COBR */ +#endif /* TC_I960 */ + /* FIXME-SOON: I think this is trash, but I'm not sure. xoxorich. */ +#ifdef comment +#ifdef OBJ_COFF + if (S_IS_COMMON(add_symbolP)) + add_number += S_GET_VALUE(add_symbolP); +#endif /* OBJ_COFF */ +#endif /* comment */ + + ++seg_reloc_count; + break; + + default: + BAD_CASE(add_symbol_segment); + break; + } /* switch on symbol seg */ + } /* if not in local seg */ + } /* if there was a + symbol */ + + if (pcrel) { + add_number -= md_pcrel_from(fixP); + if (add_symbolP == 0) { + fixP->fx_addsy = & abs_symbol; + ++seg_reloc_count; + } /* if there's an add_symbol */ + } /* if pcrel */ + + if (!fixP->fx_bit_fixP) { + if ((size==1 && + (add_number& ~0xFF) && (add_number&~0xFF!=(-1&~0xFF))) || + (size==2 && + (add_number& ~0xFFFF) && (add_number&~0xFFFF!=(-1&~0xFFFF)))) { + as_bad("Value of %d too large for field of %d bytes at 0x%x", + add_number, size, fragP->fr_address + where); + } /* generic error checking */ + } /* not a bit fix */ + + md_apply_fix(fixP, add_number); + } /* For each fixS in this segment. */ + +#ifdef OBJ_COFF +#ifdef TC_I960 + /* two relocs per callj under coff. */ + for (fixP = topP; fixP; fixP = fixP->fx_next) { + if (fixP->fx_callj && fixP->fx_addsy != 0) { + ++seg_reloc_count; + } /* if callj and not already fixed. */ + } /* for each fix */ +#endif /* TC_I960 */ +#endif /* OBJ_COFF */ + return(seg_reloc_count); +} /* fixup_segment() */ + + +static int is_dnrange(f1,f2) +struct frag *f1; +struct frag *f2; +{ + while (f1) { + if (f1->fr_next==f2) + return 1; + f1=f1->fr_next; + } + return 0; +} /* is_dnrange() */ + +/* Append a string onto another string, bumping the pointer along. */ +void +append (charPP, fromP, length) +char **charPP; +char *fromP; +unsigned long length; +{ + if (length) { /* Don't trust bcopy() of 0 chars. */ + bcopy(fromP, *charPP, (int) length); + *charPP += length; + } +} + +int section_alignment[SEG_MAXIMUM_ORDINAL]; + +/* + * This routine records the largest alignment seen for each segment. + * If the beginning of the segment is aligned on the worst-case + * boundary, all of the other alignments within it will work. At + * least one object format really uses this info. + */ +void record_alignment(seg, align) +segT seg; /* Segment to which alignment pertains */ +int align; /* Alignment, as a power of 2 + * (e.g., 1 => 2-byte boundary, 2 => 4-byte boundary, etc.) + */ +{ + + if ( align > section_alignment[(int) seg] ){ + section_alignment[(int) seg] = align; + } /* if highest yet */ + + return; +} /* record_alignment() */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of write.c */ diff --git a/gas/write.h b/gas/write.h new file mode 100644 index 0000000..1eecdc0 --- /dev/null +++ b/gas/write.h @@ -0,0 +1,130 @@ +/* write.h -> write.c */ + +/* MODIFIED BY CHRIS BENENATI, FOR INTEL CORPORATION, 4/89 */ +/* write.h -> write.c + Copyright (C) 1987 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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef TC_I960 +#ifdef hpux +#define EXEC_MACHINE_TYPE HP9000S200_ID +#endif +#endif /* TC_I960 */ + +#ifndef LOCAL_LABEL +#ifdef DOT_LABEL_PREFIX +#define LOCAL_LABEL(name) (name[0] =='.' \ + && ( name [1] == 'L' || name [1] == '.' )) +#else /* not defined DOT_LABEL_PREFIX */ +#define LOCAL_LABEL(name) (name [0] == 'L' ) +#endif /* not defined DOT_LABEL_PREFIX */ +#endif /* LOCAL_LABEL */ + +#define S_LOCAL_NAME(s) (LOCAL_LABEL(S_GET_NAME(s))) + +/* The bit_fix was implemented to support machines that need variables + to be inserted in bitfields other than 1, 2 and 4 bytes. + Furthermore it gives us a possibillity to mask in bits in the symbol + when it's fixed in the objectcode and check the symbols limits. + + The or-mask is used to set the huffman bits in displacements for the + ns32k port. + The acbi, addqi, movqi, cmpqi instruction requires an assembler that + can handle bitfields. Ie handle an expression, evaluate it and insert + the result in an some bitfield. ( ex: 5 bits in a short field of a opcode) + */ + +struct bit_fix { + int fx_bit_size; /* Length of bitfield */ + int fx_bit_offset; /* Bit offset to bitfield */ + long fx_bit_base; /* Where do we apply the bitfix. + If this is zero, default is assumed. */ + long fx_bit_base_adj;/* Adjustment of base */ + long fx_bit_max; /* Signextended max for bitfield */ + long fx_bit_min; /* Signextended min for bitfield */ + long fx_bit_add; /* Or mask, used for huffman prefix */ +}; +typedef struct bit_fix bit_fixS; +/* + * FixSs may be built up in any order. + */ + +struct fix +{ + fragS * fx_frag; /* Which frag? */ + long fx_where; /* Where is the 1st byte to fix up? */ + symbolS * fx_addsy; /* NULL or Symbol whose value we add in. */ + symbolS * fx_subsy; /* NULL or Symbol whose value we subtract. */ + long fx_offset; /* Absolute number we add in. */ + struct fix * fx_next; /* NULL or -> next fixS. */ + short int fx_size; /* How many bytes are involved? */ + char fx_pcrel; /* TRUE: pc-relative. */ + char fx_pcrel_adjust;/* pc-relative offset adjust */ + char fx_im_disp; /* TRUE: value is a displacement */ + bit_fixS * fx_bit_fixP; /* IF NULL no bitfix's to do */ + char fx_bsr; /* sequent-hack */ + enum reloc_type fx_r_type; /* Sparc hacks */ + char fx_callj; /* TRUE if target is a 'callj' + (used by i960) */ + long fx_addnumber; +}; + +typedef struct fix fixS; + +COMMON char *next_object_file_charP; + +COMMON fixS *text_fix_root, *text_fix_tail; /* Chains fixSs. */ +COMMON fixS *data_fix_root, *data_fix_tail; /* Chains fixSs. */ +COMMON fixS **seg_fix_rootP, **seg_fix_tailP; /* -> one of above. */ +extern long string_byte_count; +extern int section_alignment[]; + +#ifdef __STDC__ + +bit_fixS *bit_fix_new(char size, char offset, long base_type, long base_adj, long min, long max, long add); +void append(char **charPP, char *fromP, unsigned long length); +void record_alignment(segT seg, int align); +void write_object_file(void); + +fixS *fix_new(fragS *frag, + int where, + int size, + symbolS *add_symbol, + symbolS *sub_symbol, + long offset, + int pcrel, + enum reloc_type r_type); + +#else + +bit_fixS *bit_fix_new(); +fixS *fix_new(); +void append(); +void record_alignment(); +void write_object_file(); + +#endif /* __STDC__ */ + +/* + * Local Variables: + * comment-column: 0 + * fill-column: 131 + * End: + */ + +/* end of write.h */ @@ -0,0 +1,132 @@ +/* ld.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#define flag_is_not_at_end(x) ((x) & BSF_NOT_AT_END) +#define flag_is_ordinary_local(x) (((x) & (BSF_LOCAL))&!((x) & (BSF_DEBUGGING))) +#define flag_is_debugger(x) ((x) & BSF_DEBUGGING) +#define flag_is_undefined_or_global(x) ((x) & (BSF_UNDEFINED | BSF_GLOBAL)) +#define flag_is_defined(x) (!((x) & (BSF_UNDEFINED))) +#define flag_is_global_or_common(x) ((x) & (BSF_GLOBAL | BSF_FORT_COMM)) +#define flag_is_undefined_or_global_or_common(x) ((x) & (BSF_UNDEFINED | BSF_GLOBAL | BSF_FORT_COMM)) +#define flag_is_common(x) ((x) & BSF_FORT_COMM) +#define flag_is_global(x) ((x) & (BSF_GLOBAL)) +#define flag_is_undefined(x) ((x) & BSF_UNDEFINED) +#define flag_set(x,y) (x = y) +#define flag_is_fort_comm(x) ((x) & BSF_FORT_COMM) +#define flag_is_absolute(x) ((x) & BSF_ABSOLUTE) +/* Extra information we hold on sections */ +typedef struct user_section_struct { + /* Pointer to the section where this data will go */ + struct lang_input_statement_struct *file; +} section_userdata_type; + + +#define get_userdata(x) ((x)->userdata) +#define as_output_section_statement(x) ((x)->otheruserdata) + +#if 0 +/* + * Structure for communication between do_file_warnings and it's + * helper routines. Will in practice be an array of three of these: + * 0) Current line, 1) Next line, 2) Source file info. + */ +struct line_debug_entry +{ + int line; + char *filename; + struct nlist *sym; +}; + +#endif + + +/* Which symbols should be stripped (omitted from the output): + none, all, or debugger symbols. */ +enum { STRIP_NONE, STRIP_ALL, STRIP_DEBUGGER } strip_symbols; + + + + +/* Which local symbols should be omitted: + none, all, or those starting with L. + This is irrelevant if STRIP_NONE. */ +enum { DISCARD_NONE, DISCARD_ALL, DISCARD_L } discard_locals; + + + + + + +#define ALIGN(this, boundary) ((( (this) + ((boundary) -1)) & (~((boundary)-1)))) +#if 0 +#define FOREACHGLOBALSYMBOL(x) ldsym_type *x; for (x = symbol_head; x; x=x->next) + + + + +#define SECTIONLOOP(abfd, ptr) \ + asection *ptr; for(ptr = abfd->sections; ptr;ptr=ptr->next) + + +#endif +typedef struct { + + /* 1 => assign space to common symbols even if `relocatable_output'. */ + boolean force_common_definition; + +} args_type; + +typedef int token_code_type; + +typedef struct +{ + unsigned int specified_data_size; + boolean magic_demand_paged; + boolean make_executable; + /* 1 => write relocation into output file so can re-input it later. */ + boolean relocateable_output; + + /* Will we build contstructors, or leave alone ? */ + boolean build_constructors; + /* 1 => write relocation such that a UNIX linker can understand it. + This is used mainly to finish of sets that were built. */ + boolean unix_relocate; + + +} ld_config_type; +#define set_asymbol_chain(x,y) ((x)->udata = (void *)y) +#define get_asymbol_chain(x) ((asymbol **)((x)->udata)) +#define get_loader_symbol(x) ((loader_global_asymbol *)((x)->udata)) +#define set_loader_symbol(x,y) ((x)->udata = (void *)y) + + + + + + +typedef enum { + lang_first_phase_enum, + lang_allocating_phase_enum, + lang_final_phase_enum } lang_phase_type; + + + + diff --git a/ld/ldexp.c b/ld/ldexp.c new file mode 100644 index 0000000..5b8581f --- /dev/null +++ b/ld/ldexp.c @@ -0,0 +1,770 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + $Id$ + + $Log$ + Revision 1.1 1991/03/21 21:28:34 gumby + Initial revision + + * Revision 1.1 1991/03/13 00:48:16 chrisb + * Initial revision + * + * Revision 1.6 1991/03/10 09:31:22 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:25:04 sac + * Added support for LONG, SHORT and BYTE keywords in scripts + * + * Revision 1.4 1991/03/06 02:27:15 sac + * Added LONG, SHORT and BYTE keywords + * + * Revision 1.3 1991/02/22 17:14:59 sac + * Added RCS keywords and copyrights + * +*/ + +/* + * Written by Steve Chamberlain + * steve@cygnus.com + * + * This module handles expression trees. + */ + + +#include "sysdep.h" +#include "bfd.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldmisc.h" +#include "ldexp.h" +#include "ldgram.tab.h" +#include "ldsym.h" +#include "ldlang.h" + +extern char *output_filename; +extern unsigned int undefined_global_sym_count; +extern unsigned int defined_global_sym_count; +extern bfd *output_bfd; +extern size_t largest_section; +extern lang_statement_list_type file_chain; +extern args_type command_line; +extern ld_config_type config; + +extern lang_input_statement_type *script_file; +extern unsigned int defined_global_sym_count; + +extern bfd_vma print_dot; + + +static void +exp_print_token(outfile, code) +FILE *outfile; +token_code_type code; +{ + static struct { + token_code_type code; + char *name; + } table[] = + { + INT, "int", + CHAR,"char", + NAME,"NAME", + PLUSEQ,"+=", + MINUSEQ,"-=", + MULTEQ,"*=", + DIVEQ,"/=", + LSHIFTEQ,"<<=", + RSHIFTEQ,">>=", + ANDEQ,"&=", + OREQ,"|=", + OROR,"||", + ANDAND,"&&", + EQ,"==", + NE,"!=", + LE,"<=", + GE,">=", + LSHIFT,"<<", + RSHIFT,">>=", + ALIGN_K,"ALIGN", + BLOCK,"BLOCK", + SECTIONS,"SECTIONS", + ALIGNMENT,"ALIGNMENT", + SIZEOF_HEADERS,"SIZEOF_HEADERS", + NEXT,"NEXT", + SIZEOF,"SIZEOF", + ADDR,"ADDR", + MEMORY,"MEMORY", + DSECT,"DSECT", + NOLOAD,"NOLOAD", + COPY,"COPY", + INFO,"INFO", + OVERLAY,"OVERLAY", + DEFINED,"DEFINED", + TARGET_K,"TARGET", + SEARCH_DIR,"SEARCH_DIR", + MAP,"MAP", + LONG,"LONG", + SHORT,"SHORT", + BYTE,"BYTE", + ENTRY,"ENTRY", + 0,(char *)NULL} ; + + + + unsigned int idx; + for (idx = 0; table[idx].name != (char*)NULL; idx++) { + if (table[idx].code == code) { + fprintf(outfile, "%s", table[idx].name); + return; + } + } + /* Not in table, just print it alone */ + fprintf(outfile, "%c",code); +} + +static void +make_abs(ptr) +etree_value_type *ptr; +{ + if (ptr->section != (lang_output_section_statement_type *)NULL) { + asection *s = ptr->section->bfd_section; + ptr->value += s->vma; + ptr->section = (lang_output_section_statement_type *)NULL; + } + +} +static +etree_value_type new_abs(value) +bfd_vma value; +{ + etree_value_type new; + new.valid = true; + new.section = (lang_output_section_statement_type *)NULL; + new.value = value; + return new; +} + +static void check(os) +lang_output_section_statement_type *os; +{ + if (os == (lang_output_section_statement_type *)NULL) { + info("%F%P undefined section"); + } + if (os->processed == false) { + info("%F%P forward reference of section"); + } +} + +etree_type *exp_intop(value) +bfd_vma value; +{ + etree_type *new = (etree_type *)ldmalloc(sizeof(new->value)); + new->type.node_code = INT; + new->value.value = value; + new->type.node_class = etree_value; + return new; + +} + + +static +etree_value_type new_rel(value, section) +bfd_vma value; +lang_output_section_statement_type *section; +{ + etree_value_type new; + new.valid = true; + new.value = value; + new.section = section; + return new; +} + +static +etree_value_type new_rel_from_section(value, section) +bfd_vma value; +lang_output_section_statement_type *section; +{ + etree_value_type new; + new.valid = true; + new.value = value; + new.section = section; + if (new.section != (lang_output_section_statement_type *)NULL) { + new.value -= section->bfd_section->vma; + } + return new; +} + +static etree_value_type +fold_binary(tree, current_section, allocation_done, dot, dotp) +etree_type *tree; +lang_output_section_statement_type *current_section; +lang_phase_type allocation_done; +bfd_vma dot; +bfd_vma *dotp; +{ + etree_value_type result; + + result = exp_fold_tree(tree->binary.lhs, current_section, + allocation_done, dot, dotp); + if (result.valid) { + etree_value_type other; + other = exp_fold_tree(tree->binary.rhs, + current_section, + allocation_done, dot,dotp) ; + if (other.valid) { + /* If values are from different sections, or this is an */ + /* absolute expression, make both source args absolute */ + if (result.section != other.section || + current_section == (lang_output_section_statement_type *)NULL) { + + make_abs(&result); + make_abs(&other); + } + + switch (tree->type.node_code) + { + case '%': + /* Mod, both absolule*/ + + if (other.value == 0) { + info("%F%S % by zero\n"); + } + result.value %= other.value; + break; + case '/': + if (other.value == 0) { + info("%F%S / by zero\n"); + } + result.value /= other.value; + break; +#define BOP(x,y) case x : result.value = result.value y other.value;break; + BOP('+',+); + BOP('*',*); + BOP('-',-); + BOP(LSHIFT,<<); + BOP(RSHIFT,>>); + BOP(EQ,==); + BOP(NE,!=); + BOP('<',<); + BOP('>',>); + BOP(LE,<=); + BOP(GE,>=); + BOP('&',&); + BOP('^',^); + BOP('|',|); + BOP(ANDAND,&&); + BOP(OROR,||); + default: + FAIL(); + } + } + } + return result; +} +etree_value_type invalid() +{ + etree_value_type new; + new.valid = false; + return new; +} + +etree_value_type fold_name(tree, current_section, allocation_done, dot) +etree_type *tree; +lang_output_section_statement_type *current_section; +lang_phase_type allocation_done; +bfd_vma dot; + +{ + etree_value_type result; + switch (tree->type.node_code) + { + case DEFINED: + result.value = + ldsym_get_soft(tree->name.name) != (ldsym_type *)NULL; + result.section = 0; + result.valid = true; + break; + case NAME: + result.valid = false; + if (tree->name.name[0] == '.' && tree->name.name[1] == 0) { + + if (allocation_done != lang_first_phase_enum) { + result = new_rel_from_section(dot, current_section); + } + else { + result = invalid(); + } + } + else { + if (allocation_done == lang_final_phase_enum) { + ldsym_type *sy = ldsym_get_soft(tree->name.name); + + if (sy) { + asymbol **sdefp = sy->sdefs_chain; + + if (sdefp) { + asymbol *sdef = *sdefp; + if (sdef->section == (asection *)NULL) { + /* This is an absolute symbol */ + result = new_abs(sdef->value); + } + else { + lang_output_section_statement_type *os = + lang_output_section_statement_lookup( sdef->section->output_section->name); + result = new_rel(sdef->value, os); + } + } + } + if (result.valid == false) { + info("%F%S: undefined symbol `%s' referenced in expression.\n", + tree->name.name); + } + + } + } + + break; + + case ADDR: + + if (allocation_done != lang_first_phase_enum) { + lang_output_section_statement_type *os = + lang_output_section_find(tree->name.name); + check(os); + result = new_rel((bfd_vma)0, os); + } + else { + result = invalid(); + } + break; + case SIZEOF: + if(allocation_done != lang_first_phase_enum) { + lang_output_section_statement_type *os = + lang_output_section_find(tree->name.name); + check(os); + result = new_abs((bfd_vma)(os->bfd_section->size)); + } + else { + result = invalid(); + } + break; + + default: + FAIL(); + break; + } + + return result; +} +etree_value_type exp_fold_tree(tree, current_section, allocation_done, + dot, dotp) +etree_type *tree; +lang_output_section_statement_type *current_section; +lang_phase_type allocation_done; +bfd_vma dot; +bfd_vma *dotp; +{ + etree_value_type result; + + if (tree == (etree_type *)NULL) { + result.valid = false; + } + else { + switch (tree->type.node_class) + { + case etree_value: + result = new_rel(tree->value.value, current_section); + break; + case etree_unary: + result = exp_fold_tree(tree->unary.child, + current_section, + allocation_done, dot, dotp); + if (result.valid == true) + { + switch(tree->type.node_code) + { + case ALIGN_K: + if (allocation_done != lang_first_phase_enum) { + result = new_rel_from_section(ALIGN(dot, + result.value) , + current_section); + + } + else { + result.valid = false; + } + break; + case '-': + result.value = -result.value; + break; + case NEXT: + result.valid = false; + break; + default: + FAIL(); + } + } + + break; + case etree_trinary: + + result = exp_fold_tree(tree->trinary.cond, + current_section, + allocation_done, dot, dotp); + if (result.valid) { + result = exp_fold_tree(result.value ? + tree->trinary.lhs:tree->trinary.rhs, + current_section, + allocation_done, dot, dotp); + } + + break; + case etree_binary: + result = fold_binary(tree, current_section, allocation_done, + dot, dotp); + break; + case etree_assign: + if (tree->assign.dst[0] == '.' && tree->assign.dst[1] == 0) { + /* Assignment to dot can only be done during allocation */ + if (allocation_done == lang_allocating_phase_enum) { + result = exp_fold_tree(tree->assign.src, + current_section, + lang_allocating_phase_enum, dot, dotp); + if (result.valid == false) { + info("%F%S invalid assignment to location counter\n"); + } + else { + if (current_section == + (lang_output_section_statement_type *)NULL) { + info("%F%S assignment to location counter invalid outside of SECTION\n"); + } + else { + unsigned long nextdot =result.value + + current_section->bfd_section->vma; + if (nextdot < dot) { + info("%F%S cannot move location counter backwards"); + } + else { + *dotp = nextdot; + } + } + } + } + } + else { + ldsym_type *sy = ldsym_get(tree->assign.dst); + + /* If this symbol has just been created then we'll place it into + * a section of our choice + */ + result = exp_fold_tree(tree->assign.src, + current_section, allocation_done, + dot, dotp); + if (result.valid) + { + asymbol *def; + asymbol **def_ptr = (asymbol **)ldmalloc(sizeof(asymbol **)); + /* Add this definition to script file */ + def = (asymbol *)bfd_make_empty_symbol(script_file->the_bfd); + *def_ptr = def; + + + def->value = result.value; + if (result.section != + (lang_output_section_statement_type *)NULL) { + if (current_section != + (lang_output_section_statement_type *)NULL) { + + def->section = result.section->bfd_section; + def->flags = BSF_GLOBAL | BSF_EXPORT; + } + else { + /* Force to absolute */ + def->value += result.section->bfd_section->vma; + def->section = (asection *)NULL; + def->flags = BSF_GLOBAL | BSF_EXPORT | BSF_ABSOLUTE; + } + + + } + else { + def->section = (asection *)NULL; + def->flags = BSF_GLOBAL | BSF_EXPORT | BSF_ABSOLUTE; + } + + + def->udata = (void *)NULL; + def->name = sy->name; + Q_enter_global_ref(def_ptr); + } + + } + + + break; + case etree_name: + result = fold_name(tree, current_section, allocation_done, dot); + break; + default: + info("%F%S Need more of these %d",tree->type.node_class ); + + } + } + + return result; +} + + +etree_value_type exp_fold_tree_no_dot(tree, current_section, allocation_done) +etree_type *tree; +lang_output_section_statement_type *current_section; +lang_phase_type allocation_done; +{ +return exp_fold_tree(tree, current_section, allocation_done, (bfd_vma) + 0, (bfd_vma *)NULL); +} + +etree_type * +exp_binop(code, lhs, rhs) +int code; +etree_type *lhs; +etree_type *rhs; +{ + etree_type value, *new; + etree_value_type r; + + value.type.node_code = code; + value.binary.lhs = lhs; + value.binary.rhs = rhs; + value.type.node_class = etree_binary; + r = exp_fold_tree_no_dot(&value, (lang_output_section_statement_type *)NULL, + lang_first_phase_enum ); + if (r.valid) + { + return exp_intop(r.value); + } + new = (etree_type *)ldmalloc(sizeof(new->binary)); + memcpy((char *)new, (char *)&value, sizeof(new->binary)); + return new; +} + +etree_type * +exp_trinop(code, cond, lhs, rhs) +int code; +etree_type *cond; +etree_type *lhs; +etree_type *rhs; +{ + etree_type value, *new; + etree_value_type r; + value.type.node_code = code; + value.trinary.lhs = lhs; + value.trinary.cond = cond; + value.trinary.rhs = rhs; + value.type.node_class = etree_trinary; + r= exp_fold_tree_no_dot(&value, (lang_output_section_statement_type + *)NULL,lang_first_phase_enum); + if (r.valid) { + return exp_intop(r.value); + } + new = (etree_type *)ldmalloc(sizeof(new->trinary)); + memcpy((char *)new,(char *) &value, sizeof(new->trinary)); + return new; +} + + +etree_type * +exp_unop(code, child) +int code; +etree_type *child; +{ + etree_type value, *new; + + etree_value_type r; + value.unary.type.node_code = code; + value.unary.child = child; + value.unary.type.node_class = etree_unary; +r = exp_fold_tree_no_dot(&value,(lang_output_section_statement_type *)NULL, + lang_first_phase_enum); +if (r.valid) { + return exp_intop(r.value); + } + new = (etree_type *)ldmalloc(sizeof(new->unary)); + memcpy((char *)new, (char *)&value, sizeof(new->unary)); + return new; +} + + +etree_type * +exp_nameop(code, name) +int code; +char *name; +{ + + etree_type value, *new; + + etree_value_type r; + value.name.type.node_code = code; + value.name.name = name; + value.name.type.node_class = etree_name; + + + r = exp_fold_tree_no_dot(&value,(lang_output_section_statement_type *)NULL, + lang_first_phase_enum); + if (r.valid) { + return exp_intop(r.value); + } + new = (etree_type *)ldmalloc(sizeof(new->name)); + memcpy((char *)new, (char *)&value, sizeof(new->name)); + return new; + +} + + + + +etree_type * +exp_assop(code, dst, src) +int code; +char *dst; +etree_type *src; +{ + etree_type value, *new; + + value.assign.type.node_code = code; + + + value.assign.src = src; + value.assign.dst = dst; + value.assign.type.node_class = etree_assign; + +#if 0 + if (exp_fold_tree_no_dot(&value, &result)) { + return exp_intop(result); + } +#endif + new = (etree_type*)ldmalloc(sizeof(new->assign)); + memcpy((char *)new, (char *)&value, sizeof(new->assign)); + return new; +} + +void +exp_print_tree(outfile, tree) +FILE *outfile; +etree_type *tree; +{ + switch (tree->type.node_class) { + case etree_value: + fprintf(outfile,"0x%08lx",(bfd_vma)(tree->value.value)); + return; + case etree_assign: +#if 0 + if (tree->assign.dst->sdefs != (asymbol *)NULL){ + fprintf(outfile,"%s (%x) ",tree->assign.dst->name, + tree->assign.dst->sdefs->value); + } + else { + fprintf(outfile,"%s (UNDEFINED)",tree->assign.dst->name); + } +#endif + fprintf(outfile,"%s ",tree->assign.dst); + exp_print_token(outfile,tree->type.node_code); + exp_print_tree(outfile,tree->assign.src); + break; + case etree_binary: + exp_print_tree(outfile,tree->binary.lhs); + exp_print_token(outfile,tree->type.node_code); + exp_print_tree(outfile,tree->binary.rhs); + break; + case etree_trinary: + exp_print_tree(outfile,tree->trinary.cond); + fprintf(outfile,"?"); + exp_print_tree(outfile,tree->trinary.lhs); + fprintf(outfile,":"); + exp_print_tree(outfile,tree->trinary.rhs); + break; + case etree_unary: + exp_print_token(outfile,tree->unary.type.node_code); + fprintf(outfile,"("); + exp_print_tree(outfile,tree->unary.child); + fprintf(outfile,")"); + break; + case etree_undef: + fprintf(outfile,"????????"); + break; + case etree_name: + if (tree->type.node_code == NAME) { + fprintf(outfile,"%s", tree->name.name); + } + else { + exp_print_token(outfile,tree->type.node_code); + fprintf(outfile,"(%s)", tree->name.name); + } + break; + default: + FAIL(); + break; + } +} + + + + +bfd_vma +exp_get_vma(tree, def, name, allocation_done) +etree_type *tree; +bfd_vma def; +char *name; +lang_phase_type allocation_done; +{ + etree_value_type r; + + if (tree != (etree_type *)NULL) { + r = exp_fold_tree_no_dot(tree, + (lang_output_section_statement_type *)NULL, + allocation_done); + if (r.valid == false && name) { + info("%F%S Nonconstant expression for %s\n",name); + } + return r.value; + } + else { + return def; + } +} + +int +exp_get_value_int(tree,def,name, allocation_done) +etree_type *tree; +int def; +char *name; +lang_phase_type allocation_done; +{ + return (int)exp_get_vma(tree,(bfd_vma)def,name, allocation_done); +} diff --git a/ld/ldexp.h b/ld/ldexp.h new file mode 100644 index 0000000..f7e1321 --- /dev/null +++ b/ld/ldexp.h @@ -0,0 +1,99 @@ +/* ldexp.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + + +/* The result of an expression tree */ +typedef struct +{ + bfd_vma value; + struct lang_output_section_statement_struct *section; + boolean valid; +} etree_value_type; + + + +typedef struct +{ + int node_code; + enum { etree_binary, + etree_trinary, + etree_unary, + etree_name, + etree_assign, + etree_undef, + etree_unspec, + etree_value } node_class; +} node_type; + + + +typedef union etree_union +{ + node_type type; + struct { + node_type type; + union etree_union *lhs; + union etree_union *rhs; + } binary; + struct { + node_type type; + union etree_union *cond; + union etree_union *lhs; + union etree_union *rhs; + } trinary; + struct { + node_type type; + char *dst; + union etree_union *src; + } assign; + + struct { + node_type type; + union etree_union *child; + } unary; + struct { + node_type type; + char *name; + } name; + struct { + node_type type; + bfd_vma value; + } value; + +} etree_type; + + +PROTO(etree_type *,exp_intop,(bfd_vma)); + +PROTO(etree_value_type, invalid,(void)); +PROTO(etree_value_type, exp_fold_tree,(etree_type *, struct + lang_output_section_statement_struct *, lang_phase_type, + bfd_vma, bfd_vma *)); + +PROTO(etree_type *, exp_binop,(int, etree_type *, etree_type *)); +PROTO(etree_type *,exp_trinop,(int,etree_type *, etree_type *, etree_type *)); +PROTO(etree_type *,exp_unop,(int, etree_type *)); +PROTO(etree_type *,exp_nameop,(int, char *)); +PROTO(etree_type *,exp_assop,(int, char *, etree_type *)); +PROTO(void, exp_print_tree,(struct _iobuf *, etree_type *)); +PROTO(bfd_vma, exp_get_vma,(etree_type *, bfd_vma, char *, enum boolean)); +PROTO(int, exp_get_value_int,(etree_type *, int, char *, enum boolean)); diff --git a/ld/ldfile.c b/ld/ldfile.c new file mode 100644 index 0000000..bc55f04 --- /dev/null +++ b/ld/ldfile.c @@ -0,0 +1,284 @@ + +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + $Id$ + + $Log$ + Revision 1.1 1991/03/21 21:28:37 gumby + Initial revision + + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:18 chrisb + * Initial revision + * + * Revision 1.4 1991/03/10 09:31:24 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.3 1991/02/22 17:15:00 sac + * Added RCS keywords and copyrights + * +*/ + +/* + ldfile.c + + look after all the file stuff + + */ + +#include "sysdep.h" +#include "bfd.h" + +#include "ldmisc.h" +#include "ldlang.h" +#include "ldfile.h" + +#include <ctype.h> + +/* EXPORT */ +char *ldfile_input_filename; +char *ldfile_output_machine_name; +unsigned long ldfile_output_machine; +enum bfd_architecture ldfile_output_architecture; +boolean had_script; + +/* IMPORT */ + +extern boolean option_v; + + + + + +/* LOACL */ +typedef struct search_dirs_struct +{ + char *name; + struct search_dirs_struct *next; +} search_dirs_type; + +static search_dirs_type *search_head; +static search_dirs_type **search_tail_ptr = &search_head; + +typedef struct search_arch_struct +{ + char *name; + struct search_arch_struct *next; +} search_arch_type; + +static search_arch_type *search_arch_head; +static search_arch_type **search_arch_tail_ptr = &search_arch_head; + +void +ldfile_add_library_path(name) +char *name; +{ + search_dirs_type *new = + (search_dirs_type *)ldmalloc(sizeof(search_dirs_type)); + new->name = name; + new->next = (search_dirs_type*)NULL; + *search_tail_ptr = new; + search_tail_ptr = &new->next; +} + + +static bfd* +cached_bfd_openr(attempt,entry) +char *attempt; +lang_input_statement_type *entry; +{ + entry->the_bfd = bfd_openr(attempt, entry->target); + + + return entry->the_bfd; +} + +static bfd * +open_a(arch, entry, lib, suffix) +char *arch; +lang_input_statement_type *entry; +char *lib; +char *suffix; +{ + bfd*desc; + search_dirs_type *search ; + for (search = search_head; + search != (search_dirs_type *)NULL; + search = search->next) + { + char buffer[1000]; + char *string; + if (entry->is_archive == true) { + sprintf(buffer, + "%s/%s%s%s%s", + search->name, + lib, + entry->filename, arch, suffix); + } + else { + if (entry->filename[0] == '/') { + strcpy(buffer, entry->filename); + } else { + sprintf(buffer,"%s/%s",search->name, entry->filename); + } /* */ + } + string = buystring(buffer); + desc = cached_bfd_openr (string, entry); + if (desc) + { + entry->filename = string; + entry->search_dirs_flag = false; + entry->the_bfd = desc; + return desc; + } + free(string); + } + return (bfd *)NULL; +} + +/* Open the input file specified by 'entry', and return a descriptor. + The open file is remembered; if the same file is opened twice in a row, + a new open is not actually done. */ + +void +ldfile_open_file (entry) +lang_input_statement_type *entry; +{ + + if (entry->superfile) + ldfile_open_file (entry->superfile); + + if (entry->search_dirs_flag) + { + search_arch_type *arch; + for (arch = search_arch_head; + arch != (search_arch_type *)NULL; + arch = arch->next) { + if (open_a(arch->name,entry,"","") != (bfd *)NULL) { + return; + } + if (open_a(arch->name,entry,"lib",".a") != (bfd *)NULL) { + return; + } + + } + + + } + else { + entry->the_bfd = cached_bfd_openr (entry->filename, entry); + + } + if (!entry->the_bfd) info("%F%P: %E %I\n", entry); + +} + + + + + + +static FILE * +try_open(name, exten) +char *name; +char *exten; +{ + FILE *result; + char buff[1000]; + result = fopen(name, "r"); + if (result && option_v == true) { + info("%s\n",name); + return result; + } + sprintf(buff, "%s%s", name, exten); + result = fopen(buff, "r"); + + if (result && option_v == true) { + info("%s\n", buff); + } + return result; +} +static FILE * +find_a_name(name, extend) +char *name; +char *extend; +{ + search_dirs_type *search; + FILE *result; + char buffer[1000]; + /* First try raw name */ + result = try_open(name,""); + if (result == (FILE *)NULL) { + /* Try now prefixes */ + for (search = search_head; + search != (search_dirs_type *)NULL; + search = search->next) { + sprintf(buffer,"%s/%s", search->name, name); + result = try_open(buffer, extend); + if (result)break; + } + } + return result; +} + +void ldfile_open_command_file(name) +char *name; +{ + extern FILE *ldlex_input_stack; + ldlex_input_stack = find_a_name(name, ".ld"); + + if (ldlex_input_stack == (FILE *)NULL) { + info("%P%F cannot open load script file %s\n",name); + } + ldfile_input_filename = name; + had_script = true; +} + + + + +void +ldfile_add_arch(name) +char *name; +{ + search_arch_type *new = + (search_arch_type *)ldmalloc(sizeof(search_arch_type)); + ldfile_output_machine_name = name; + + new->name = name; + new->next = (search_arch_type*)NULL; + while (*name) { + if (isupper(*name)) *name = tolower(*name); + name++; + } + *search_arch_tail_ptr = new; + search_arch_tail_ptr = &new->next; + +} diff --git a/ld/ldfile.h b/ld/ldfile.h new file mode 100644 index 0000000..876d47f --- /dev/null +++ b/ld/ldfile.h @@ -0,0 +1,27 @@ +/* ldfile.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +PROTO(void,ldfile_add_arch,(char *)); +PROTO(void,ldfile_add_library_path,(char *)); +PROTO(void,ldfile_open_command_file,(char *name)); +PROTO(void,ldfile_open_file,(struct lang_input_statement_struct *)); + diff --git a/ld/ldgram.y b/ld/ldgram.y new file mode 100644 index 0000000..aa0f325 --- /dev/null +++ b/ld/ldgram.y @@ -0,0 +1,693 @@ +%{ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:41 gumby + * Initial revision + * + * Revision 1.2 1991/03/16 22:27:24 rich + * fish + * + * Revision 1.1 1991/03/13 00:48:21 chrisb + * Initial revision + * + * Revision 1.6 1991/03/10 09:31:26 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:25:48 sac + * Can now parse the -Ur flag + * + * Revision 1.4 1991/03/06 02:26:01 sac + * Added support for constructor sections. + * Remove parsing ambiguity. + * Lint + * + * Revision 1.3 1991/02/22 17:15:13 sac + * Added RCS keywords and copyrights + * +*/ + +/* + This is a YACC grammer intended to parse a superset of the AT&T + linker scripting languaue. + + + Written by Steve Chamberlain steve@cygnus.com +*/ + + +/*SUPPRESS 166*/ +/*SUPPRESS 112*/ + +#include "sysdep.h" +#include "bfd.h" + +#include "ld.h" +#include "ldexp.h" +#include "ldversion.h" +#include "ldlang.h" +#include "ld-emul.h" +#include "ldfile.h" +#include "ldmisc.h" +#define YYDEBUG 1 + +boolean option_v; + + +extern unsigned int lineno; +extern boolean trace_files; +extern boolean write_map; + +boolean hex_mode; + + + + +lang_memory_region_type *region; + + +lang_memory_region_type *lang_memory_region_lookup(); +lang_output_section_statement_type *lang_output_section_statement_lookup(); + +#ifdef __STDC__ + +void lang_add_data(int type, union etree_union *exp); +void lang_enter_output_section_statement(char *output_section_statement_name, etree_type *address_exp, bfd_vma block_value); + +#else + +void lang_add_data(); +void lang_enter_output_section_statement(); + +#endif /* __STDC__ */ + +extern args_type command_line; +char *current_file; +boolean ldgram_want_filename = true; +boolean had_script = false; +boolean force_make_executable = false; +boolean ldgram_mustbe_filename = false; +boolean ldgram_mustbe_symbolname = false; +boolean ldgram_has_inputfile = false; + +/* LOCALS */ + + + + +%} +%union { + bfd_vma integer; + int voidval; + char *name; + int token; + union etree_union *etree; + asection *section; + struct lang_output_section_statement_struct *output_section_statement; + union lang_statement_union **statement_ptr; + int lineno; + struct { + FILE *file; + char *name; + unsigned int lineno; + } state; + + +} + +%type <etree> exp opt_exp exp_head +%type <integer> fill_opt opt_block +%type <name> memspec_opt +%token <integer> INT CHAR +%token <name> NAME +%type <integer> length + +%right <token> PLUSEQ MINUSEQ MULTEQ DIVEQ '=' LSHIFTEQ RSHIFTEQ ANDEQ OREQ +%right <token> '?' ':' +%left <token> OROR +%left <token> ANDAND +%left <token> '|' +%left <token> '^' +%left <token> '&' +%left <token> EQ NE +%left <token> '<' '>' LE GE +%left <token> LSHIFT RSHIFT +%left <token> '+' '-' +%left <token> '*' '/' '%' +%right UNARY +%left <token> '(' +%token <token> ALIGN_K BLOCK LONG SHORT BYTE +%token SECTIONS +%token '{' '}' +%token ALIGNMENT SIZEOF_HEADERS +%token NEXT SIZEOF ADDR +%token MEMORY +%token DSECT NOLOAD COPY INFO OVERLAY +%token NAME DEFINED TARGET_K SEARCH_DIR MAP ENTRY +%token OPTION_e OPTION_c OPTION_noinhibit_exec OPTION_s OPTION_S OPTION_format +%token OPTION_d OPTION_dc OPTION_dp OPTION_x OPTION_X +%token OPTION_v OPTION_M OPTION_t STARTUP HLL SYSLIB FLOAT NOFLOAT OPTION_defsym +%token OPTION_n OPTION_r OPTION_o OPTION_b OPTION_A +%token <name> OPTION_l OPTION_L OPTION_T OPTION_Aarch OPTION_Tfile OPTION_Texp +%token OPTION_Ur +%token ORIGIN FILL OPTION_g +%token LENGTH BIND SUBSECTION_ALIGN CREATE_OBJECT_SYMBOLS INPUT OUTPUT +%type <token> assign_op SIZEOF NEXT ADDR +%type <etree> assignment +%type <name> filename + +%{ +ld_config_type config; +%} + +%% + + + +file: command_line { lang_final(); }; + + +filename: + { + ldgram_mustbe_filename =true; + } + NAME + { + ldgram_mustbe_filename = false; + $$ = $2; + } + +command_line: + command_line command_line_option + | + ; + +command_line_option: + OPTION_v + { + ldversion(); + option_v = true; + } + | OPTION_t { + trace_files = true; + } + | OPTION_M { + write_map = true; + } + | OPTION_n { + config.magic_demand_paged = false; + config.make_executable = false; + } + | OPTION_s { + strip_symbols = STRIP_ALL; + } + | OPTION_S { + strip_symbols = STRIP_DEBUGGER; + } + + | OPTION_r { + config.relocateable_output = true; + config.build_constructors = false; + config.magic_demand_paged = false; + } + | OPTION_Ur { + config.relocateable_output = true; + config.build_constructors = true; + config.magic_demand_paged = false; + } + | OPTION_o filename + { + lang_add_output($2); + } + | OPTION_e NAME + { lang_add_entry($2); + } + | OPTION_X { + discard_locals = DISCARD_L; + } + | OPTION_x { + discard_locals = DISCARD_ALL; + } + + | OPTION_noinhibit_exec + { + force_make_executable = true; + } + | OPTION_d { + command_line.force_common_definition = true; + } + | OPTION_dc + { + command_line.force_common_definition = true; + } + | OPTION_g + { + /* Ignored */ + } + | OPTION_dp + { + command_line.force_common_definition = true; + } + | OPTION_format NAME + { + lang_add_target($2); + } + + | OPTION_Texp { hex_mode =true; } + exp_head + { lang_section_start($1, $3); + hex_mode = false; } + + | OPTION_Aarch + { ldfile_add_arch($1); } + | OPTION_b NAME + { + lang_add_target($2); + } + | OPTION_L + { + ldfile_add_library_path($1); + } + | ifile_p1 + | input_list + | OPTION_c filename + { ldfile_open_command_file($2); } + | OPTION_Tfile + { ldfile_open_command_file($1); } + + | OPTION_T filename + { ldfile_open_command_file($2); } + + | OPTION_l + { + lang_add_input_file($1, + lang_input_file_is_l_enum, + (char *)NULL); + } + | OPTION_A filename + { + lang_add_input_file($2, + lang_input_file_is_symbols_only_enum, + (char *)NULL); + } + | OPTION_defsym assignment_with_nospaces + ; + + +input_section_spec: + NAME + { + lang_add_wild((char *)NULL, $1); + } + | '[' + { + current_file = (char *)NULL; + } + file_NAME_list + ']' + | NAME + { + current_file =$1; + } + '(' file_NAME_list ')' + | '*' + { + current_file = (char *)NULL; + } + '(' file_NAME_list ')' + ; + + + +file_NAME_list: + NAME + { lang_add_wild($1, current_file); } + | file_NAME_list opt_comma NAME + { lang_add_wild($3, current_file); } + ; + + + +ifile_p1: + memory + | sections + | startup + | high_level_library + | low_level_library + | floating_point_support + | assignment end + | TARGET_K '(' NAME ')' + { lang_add_target($3); } + | SEARCH_DIR '(' filename ')' + { ldfile_add_library_path($3); } + | OUTPUT '(' filename ')' + { lang_add_output($3); } + | INPUT '(' input_list ')' + | MAP '(' filename ')' + { lang_add_map($3); } + ; + +input_list: + NAME + { lang_add_input_file($1,lang_input_file_is_file_enum, + (char *)NULL); } + | input_list ',' NAME + { lang_add_input_file($3,lang_input_file_is_file_enum, + (char *)NULL); } + | input_list NAME + { lang_add_input_file($2, lang_input_file_is_file_enum, + (char *)NULL); } + ; + +sections: + SECTIONS '{'sec_or_group_p1 '}' + ; + +sec_or_group_p1: + sec_or_group_p1 section + | sec_or_group_p1 statement_anywhere + | + ; + +statement_anywhere: + ENTRY '(' NAME ')' + { lang_add_entry($3); } + | assignment end + ; + +statement: + statement assignment end + | statement CREATE_OBJECT_SYMBOLS + { lang_add_attribute(lang_object_symbols_statement_enum); } + | statement input_section_spec + | statement length '(' exp_head ')' + { + lang_add_data($2,$4); + } + + | statement FILL '(' exp_head ')' + { + lang_add_fill + (exp_get_value_int($4, + 0, + "fill value", + lang_first_phase_enum)); + } + | + ; + +length: + LONG + { $$ = $1; } + | SHORT + { $$ = $1; } + | BYTE + { $$ = $1; } + ; + +fill_opt: + '=' exp_head + { + $$ = exp_get_value_int($2, + 0, + "fill value", + lang_first_phase_enum); + } + | { $$ = 0; } + ; + + + +assign_op: + PLUSEQ + { $$ = '+'; } + | MINUSEQ + { $$ = '-'; } + | MULTEQ + { $$ = '*'; } + | DIVEQ + { $$ = '/'; } + | LSHIFTEQ + { $$ = LSHIFT; } + | RSHIFTEQ + { $$ = RSHIFT; } + | ANDEQ + { $$ = '&'; } + | OREQ + { $$ = '|'; } + + ; + +end: ';' | ',' + ; + +assignment_with_nospaces: + { ldgram_want_filename = false; } + assignment + { ldgram_want_filename = true; } + ; + +assignment: + + NAME '=' exp_head + { + lang_add_assignment(exp_assop($2,$1,$3)); + } + | NAME assign_op exp_head + { + lang_add_assignment(exp_assop('=',$1,exp_binop($2,exp_nameop(NAME,$1),$3))); + } + + ; + + +opt_comma: + ',' | ; + + +memory: + MEMORY '{' memory_spec memory_spec_list '}' + ; + +memory_spec_list: + memory_spec_list memory_spec + | memory_spec_list ',' memory_spec + | + ; + + +memory_spec: + NAME + { region = lang_memory_region_lookup($1); } + attributes_opt ':' origin_spec opt_comma length_spec + + { + + + } + ; +origin_spec: + ORIGIN '=' exp + { region->current = + region->origin = + exp_get_vma($3, 0L,"origin", lang_first_phase_enum); } + ; +length_spec: + LENGTH '=' exp + { region->length = exp_get_vma($3, + ~((bfd_vma)0), + "length", + lang_first_phase_enum); + } + + +attributes_opt: + '(' NAME ')' + { + lang_set_flags(®ion->flags, $2); + } + | + + ; + +startup: + STARTUP '(' filename ')' + { lang_startup($3); } + ; + +high_level_library: + HLL '(' high_level_library_NAME_list ')' + | HLL '(' ')' + { ldemul_hll((char *)NULL); } + ; + +high_level_library_NAME_list: + high_level_library_NAME_list opt_comma filename + { ldemul_hll($3); } + | filename + { ldemul_hll($1); } + + ; + +low_level_library: + SYSLIB '(' low_level_library_NAME_list ')' + ; +low_level_library_NAME_list: + low_level_library_NAME_list opt_comma filename + { ldemul_syslib($3); } + | + ; + +floating_point_support: + FLOAT + { lang_float(true); } + | NOFLOAT + { lang_float(false); } + ; + + + + +exp : + '-' exp %prec UNARY + { $$ = exp_unop('-', $2); } + | '(' exp ')' + { $$ = $2; } + | NEXT '(' exp ')' %prec UNARY + { $$ = exp_unop($1,$3); } + | '!' exp %prec UNARY + { $$ = exp_unop('!', $2); } + | '+' exp %prec UNARY + { $$ = $2; } + | '~' exp %prec UNARY + { $$ = exp_unop('~', $2);} + + | exp '*' exp + { $$ = exp_binop('*', $1, $3); } + | exp '/' exp + { $$ = exp_binop('/', $1, $3); } + | exp '%' exp + { $$ = exp_binop('%', $1, $3); } + | exp '+' exp + { $$ = exp_binop('+', $1, $3); } + | exp '-' exp + { $$ = exp_binop('-' , $1, $3); } + | exp LSHIFT exp + { $$ = exp_binop(LSHIFT , $1, $3); } + | exp RSHIFT exp + { $$ = exp_binop(RSHIFT , $1, $3); } + | exp EQ exp + { $$ = exp_binop(EQ , $1, $3); } + | exp NE exp + { $$ = exp_binop(NE , $1, $3); } + | exp LE exp + { $$ = exp_binop(LE , $1, $3); } + | exp GE exp + { $$ = exp_binop(GE , $1, $3); } + | exp '<' exp + { $$ = exp_binop('<' , $1, $3); } + | exp '>' exp + { $$ = exp_binop('>' , $1, $3); } + | exp '&' exp + { $$ = exp_binop('&' , $1, $3); } + | exp '^' exp + { $$ = exp_binop('^' , $1, $3); } + | exp '|' exp + { $$ = exp_binop('|' , $1, $3); } + | exp '?' exp ':' exp + { $$ = exp_trinop('?' , $1, $3, $5); } + | exp ANDAND exp + { $$ = exp_binop(ANDAND , $1, $3); } + | exp OROR exp + { $$ = exp_binop(OROR , $1, $3); } + | DEFINED '(' NAME ')' + { $$ = exp_nameop(DEFINED, $3); } + | INT + { $$ = exp_intop($1); } + + | SIZEOF '(' NAME ')' + { $$ = exp_nameop($1,$3); } + | ADDR '(' NAME ')' + { $$ = exp_nameop($1,$3); } + | ALIGN_K '(' exp ')' + { $$ = exp_unop($1,$3); } + | NAME + { $$ = exp_nameop(NAME,$1); } + ; + + + + +section: NAME opt_exp opt_block ':' opt_things'{' + { + lang_enter_output_section_statement($1,$2,$3); + } + statement '}' fill_opt memspec_opt + { + lang_leave_output_section_statement($10, $11); + } + + ; + +opt_things: + { + + } + ; + +exp_head: + { ldgram_mustbe_symbolname = true; } + exp + { ldgram_mustbe_symbolname = false; + $$ = $2; + } + +opt_exp: + exp + { $$ = $1; } + | { $$= (etree_type *)NULL; } + ; + +opt_block: + BLOCK '(' exp_head ')' + { $$ = exp_get_value_int($3, + 1L, + "block", + lang_first_phase_enum); + } + | { $$ = 1; } + ; + +memspec_opt: + '>' NAME + { $$ = $2; } + | { $$ = "*default*"; } + ; + diff --git a/ld/ldlang.c b/ld/ldlang.c new file mode 100644 index 0000000..1fccbc2 --- /dev/null +++ b/ld/ldlang.c @@ -0,0 +1,2231 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:45 gumby + * Initial revision + * + * Revision 1.3 1991/03/16 22:19:21 rich + * pop + * + * Revision 1.2 1991/03/15 18:52:42 rich + * pop + * + * Revision 1.1 1991/03/13 00:48:23 chrisb + * Initial revision + * + * Revision 1.8 1991/03/10 09:31:28 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.7 1991/03/09 03:31:03 sac + * After a fatal info message, the output file is deleted. + * + * Revision 1.6 1991/03/09 03:25:06 sac + * Added support for LONG, SHORT and BYTE keywords in scripts + * + * Revision 1.5 1991/03/06 21:59:31 sac + * Completed G++ support + * + * Revision 1.4 1991/03/06 02:26:02 sac + * Added support for constructor sections. + * Remove parsing ambiguity. + * Lint + * + * Revision 1.3 1991/02/22 17:15:01 sac + * Added RCS keywords and copyrights + * +*/ + + + +#include "sysdep.h" +#include "bfd.h" + +#include "ld.h" +#include "ldmain.h" +#include "ldsym.h" +#include "ldgram.tab.h" +#include "ldmisc.h" +#include "ldlang.h" +#include "ldexp.h" +#include "ld-emul.h" +#include "ldlex.h" + +/* EXPORTS */ + + + +extern unsigned int undefined_global_sym_count; + +static char *startup_file; +static lang_input_statement_type *first_file; +lang_statement_list_type statement_list; +lang_statement_list_type *stat_ptr = &statement_list; +lang_statement_list_type lang_output_section_statement; +lang_statement_list_type input_file_chain; +lang_statement_list_type file_chain; +extern char *current_file; +static boolean placed_commons = false; + +boolean lang_float_flag; + +static lang_output_section_statement_type *default_common_section; + + +/* FORWARDS */ +PROTO(static void, print_statements,(void)); +PROTO(static void, print_statement,(lang_statement_union_type *, + lang_output_section_statement_type *)); + + + +/* EXPORTS */ +boolean lang_has_input_file = false; + + +extern bfd *output_bfd; +size_t largest_section; + + +extern enum bfd_architecture ldfile_output_architecture; +extern unsigned long ldfile_output_machine; +extern char *ldfile_output_machine_name; + + +extern ldsym_type *symbol_head; + +bfd_vma print_dot; +unsigned int commons_pending; + + + + +extern args_type command_line; +extern ld_config_type config; + +char *entry_symbol; + + + +lang_output_section_statement_type *create_object_symbols; + +extern boolean had_script; +static boolean map_option_f; + + +boolean had_output_filename = false; +extern boolean write_map; + + + + + + +size_t longest_section_name = 8; + + +lang_input_statement_type *script_file; + +section_userdata_type common_section_userdata; +asection common_section; + +#ifdef __STDC__ +#define cat(a,b) a##b +#else +#define cat(a,b) a/**/b +#endif + +#define new_stat(x,y) (cat(x,_type)*) new_statement(cat(x,_enum), sizeof(cat(x,_type)),y) + +#define outside_section_address(q) ( (q)->output_offset + (q)->output_section->vma) + +#define outside_symbol_address(q) ((q)->value + outside_section_address(q->section)) + +boolean option_longmap = false; + +static void lang_list_init(list) +lang_statement_list_type *list; +{ +list->head = (lang_statement_union_type *)NULL; +list->tail = &list->head; +} + +static void +print_section(name) +char *name; +{ + printf("%*s", -longest_section_name, name); +} +static void +print_space() +{ + printf(" "); +} +static void +print_nl() +{ + printf("\n"); +} +static void +print_address(value) +bfd_vma value; +{ + printf("%8lx", value); +} +static void +print_size(value) +size_t value; +{ + printf("%5x", (unsigned)value); +} +static void +print_alignment(value) +unsigned int value; +{ + printf("2**%2u",value); +} +static void +print_fill(value) +fill_type value; +{ + printf("%04x",(unsigned)value); +} + + +static +lang_statement_union_type *new_statement(type, size, list) +enum statement_enum type; +size_t size; +lang_statement_list_type *list; +{ + lang_statement_union_type *new = (lang_statement_union_type *) + ldmalloc(size); + new->header.type = type; + new->header.next = (lang_statement_union_type *)NULL; + lang_statement_append(list, new, &new->header.next); + return new; +} + +static lang_input_statement_type * +new_afile(name, file_type, target) +char *name; +lang_input_file_enum_type file_type; +char *target; +{ + lang_input_statement_type *p = new_stat(lang_input_statement, + stat_ptr); + lang_has_input_file = true; + p->target = target; + switch (file_type) { + case lang_input_file_is_symbols_only_enum: + p->filename = name; + p->is_archive =false; + p->real = true; + p->local_sym_name= name; + p->just_syms_flag = true; + p->search_dirs_flag = false; + break; + case lang_input_file_is_fake_enum: + p->filename = name; + p->is_archive =false; + p->real = false; + p->local_sym_name= name; + p->just_syms_flag = false; + p->search_dirs_flag =false; + + break; + case lang_input_file_is_l_enum: + p->is_archive = true; + p->filename = name; + p->real = true; + p->local_sym_name = concat("-l",name,""); + p->just_syms_flag = false; + p->search_dirs_flag = true; + break; + + case lang_input_file_is_search_file_enum: + case lang_input_file_is_marker_enum: + p->filename = name; + p->is_archive =false; + p->real = true; + p->local_sym_name= name; + p->just_syms_flag = false; + p->search_dirs_flag =true; + break; + + + + case lang_input_file_is_file_enum: + p->filename = name; + p->is_archive =false; + p->real = true; + p->local_sym_name= name; + p->just_syms_flag = false; + p->search_dirs_flag =false; + break; + + + default: + FAIL(); + } + p->asymbols = (asymbol **)NULL; + p->superfile = (lang_input_statement_type *)NULL; + + p->next_real_file = (lang_statement_union_type*)NULL; + p->next = (lang_statement_union_type*)NULL; + p->symbol_count = 0; + p->common_output_section = (asection *)NULL; + + lang_statement_append(&input_file_chain, + (lang_statement_union_type *)p, + &p->next_real_file); + return p; +} + +lang_input_statement_type * +lang_add_input_file(name, + file_type, + target) +char *name; +lang_input_file_enum_type file_type; +char *target; +{ + /* Look it up or build a new one */ + + lang_input_statement_type *p; + + for (p = (lang_input_statement_type *)input_file_chain.head; + p != (lang_input_statement_type *)NULL; + p = (lang_input_statement_type *)(p->next_real_file)) + { + /* Sometimes we have incomplete entries in here */ + if (p->filename != (char *)NULL) { + if(strcmp(name,p->filename) == 0) return p; + } + } + + return new_afile(name, file_type, target); +} + + + +void +lang_init() +{ + + stat_ptr= &statement_list; + lang_list_init(stat_ptr); + + lang_list_init(&input_file_chain); + lang_list_init(&lang_output_section_statement); + lang_list_init(&file_chain); + first_file = lang_add_input_file((char *)NULL, + lang_input_file_is_marker_enum, + (char *)NULL); + +} + +static void +lang_init2() +{ + script_file = lang_add_input_file("script file", + lang_input_file_is_fake_enum, + (char *)NULL); + script_file->the_bfd = bfd_create("script file", output_bfd); + script_file->symbol_count = 0; + + common_section.userdata = &common_section_userdata; + +} + + + +/* this function mainains a dictionary of regions. If the *default* + region is asked for then a pointer to the first region is + returned. If there is no first pointer then one is created +*/ + +static lang_memory_region_type *lang_memory_region_list; +static lang_memory_region_type **lang_memory_region_list_tail = &lang_memory_region_list; + +lang_memory_region_type * +lang_memory_region_lookup(name) +char *name; +{ + + lang_memory_region_type *p = lang_memory_region_list; + for (p = lang_memory_region_list; + p != ( lang_memory_region_type *)NULL; + p = p->next) { + if (strcmp(p->name, name) == 0) { + return p; + } + } + if (strcmp(name,"*default*")==0) { + /* This is the default region, dig out first one on the list */ + if (lang_memory_region_list != (lang_memory_region_type*)NULL){ + return lang_memory_region_list; + } + } + { + lang_memory_region_type *new = + (lang_memory_region_type *)ldmalloc(sizeof(lang_memory_region_type)); + new->name = name; + new->next = (lang_memory_region_type *)NULL; + + *lang_memory_region_list_tail = new; + lang_memory_region_list_tail = &new->next; + new->origin = 0; + new->length = ~0; + new->current = 0; + return new; + } +} + + + +lang_output_section_statement_type * +lang_output_section_find(name) +char *name; +{ + lang_statement_union_type *u; + lang_output_section_statement_type *lookup; + + for (u = lang_output_section_statement.head; + u != (lang_statement_union_type *)NULL; + u = lookup->next) + { + lookup = &u->output_section_statement; + if (strcmp(name, lookup->name)==0) { + return lookup; + } + } + return (lang_output_section_statement_type *)NULL; +} + +lang_output_section_statement_type * +lang_output_section_statement_lookup(name) +char *name; + +{ + lang_output_section_statement_type *lookup; + lookup =lang_output_section_find(name); + if (lookup == (lang_output_section_statement_type *)NULL) { + + lookup =(lang_output_section_statement_type *) + new_stat(lang_output_section_statement, stat_ptr); + lookup->region = (lang_memory_region_type *)NULL; + lookup->fill = 0; + lookup->block_value = 1; + lookup->name = name; + + lookup->next = (lang_statement_union_type*)NULL; + lookup->bfd_section = (asection *)NULL; + lookup->processed = false; + lookup->addr_tree = (etree_type *)NULL; + lang_list_init(&lookup->children); + + lang_statement_append(&lang_output_section_statement, + (lang_statement_union_type *)lookup, + &lookup->next); + } + return lookup; +} + + + + + +static void + print_flags(outfile, ignore_flags) +FILE *outfile; +lang_section_flags_type *ignore_flags; +{ + fprintf(outfile,"("); +#if 0 + if (flags->flag_read) fprintf(outfile,"R"); + if (flags->flag_write) fprintf(outfile,"W"); + if (flags->flag_executable) fprintf(outfile,"X"); + if (flags->flag_loadable) fprintf(outfile,"L"); +#endif + fprintf(outfile,")"); +} + +void +lang_map(outfile) + FILE *outfile; +{ + lang_memory_region_type *m; + fprintf(outfile,"**MEMORY CONFIGURATION**\n\n"); + + fprintf(outfile,"name\t\torigin\t\tlength\t\tattributes\n"); + for (m = lang_memory_region_list; + m != (lang_memory_region_type *)NULL; + m = m->next) + { + fprintf(outfile,"%-16s", m->name); + + fprintf(outfile,"%08lx\t%08lx\t", m->origin, m->length); + print_flags(outfile, &m->flags); + fprintf(outfile,"\n"); + } + fprintf(outfile,"\n\n**LINK EDITOR MEMORY MAP**\n\n"); + fprintf(outfile,"output\t\tinput\t\tvirtual\n"); + fprintf(outfile,"section\t\tsection\t\taddress\tsize\n\n"); + + print_statements(); + +} + +/* + * + */ +static void init_os(s) +lang_output_section_statement_type *s; +{ + section_userdata_type *new = + (section_userdata_type *) + ldmalloc(sizeof(section_userdata_type)); + + s->bfd_section = bfd_make_section(output_bfd, s->name); + s->bfd_section->output_section = s->bfd_section; + s->bfd_section->flags = SEC_NO_FLAGS; + /* We initialize an output sections output offset to minus its own */ + /* vma to allow us to output a section through itself */ + s->bfd_section->output_offset = 0; + get_userdata( s->bfd_section) = new; +} + +static void +wild_doit(ptr, section,output, file) +lang_statement_list_type *ptr; +asection *section; +lang_output_section_statement_type *output; +lang_input_statement_type *file; +{ + if(output->bfd_section == (asection *)NULL) + { + init_os(output); + } + + if (section != (asection *)NULL + && section->output_section == (asection *)NULL) { + /* Add a section reference to the list */ + lang_input_section_type *new = new_stat(lang_input_section, ptr); + + new->section = section; + new->ifile = file; + section->output_section = output->bfd_section; + section->output_section->flags |= section->flags; + if (section->alignment_power > output->bfd_section->alignment_power) { + output->bfd_section->alignment_power = section->alignment_power; + } + + } +} + +static asection * +our_bfd_get_section_by_name(abfd, section) +bfd *abfd; +char *section; +{ + return bfd_get_section_by_name(abfd, section); + +} +static void +wild_section(ptr, section, file , output) +lang_wild_statement_type *ptr; +char *section; +lang_input_statement_type *file; +lang_output_section_statement_type *output; +{ + asection *s; + if (section == (char *)NULL) { + /* Do the creation to all sections in the file */ + for (s = file->the_bfd->sections; s != (asection *)NULL; s=s->next) { + wild_doit(&ptr->children, s, output, file); + } + } + else { + /* Do the creation to the named section only */ + wild_doit(&ptr->children, + our_bfd_get_section_by_name(file->the_bfd, section), + output, file); + } + + + +} + + + +static +lang_input_statement_type *lookup_name(name, target) +char *name; +char *target; +{ + lang_input_statement_type *search; + for(search = (lang_input_statement_type *)input_file_chain.head; + search != (lang_input_statement_type *)NULL; + search = (lang_input_statement_type *)search->next_real_file) + { + if (search->filename == (char *)NULL && name == (char *)NULL) { + return search; + } + if (search->filename != (char *)NULL && name != (char *)NULL) { + if (strcmp(search->filename, name) == 0) { + Q_read_file_symbols(search); + return search; + } + } + } + + /* There isn't an afile entry for this file yet, this must be */ + /* because the name has only appeared inside a load script and not */ + /* on the command line */ + search = new_afile(name, lang_input_file_is_file_enum, target); + Q_read_file_symbols(search); + return search; +} + +static void + +wild(s, section, file, target, output) +lang_wild_statement_type *s; +char *section; +char *file; +char *target; +lang_output_section_statement_type *output; +{ + lang_input_statement_type *f; + if (file == (char *)NULL) { + /* Perform the iteration over all files in the list */ + for (f = (lang_input_statement_type *)file_chain.head; + f != (lang_input_statement_type *)NULL; + f = (lang_input_statement_type *)f->next) { + wild_section(s, section, f, output); + } + } + else { + /* Perform the iteration over a single file */ + wild_section( s, section, lookup_name(file, target), output); + } +} + +/* + read in all the files + */ +static bfd * +open_output(name, target) +char *name; +char *target; +{ + extern char *output_filename; + bfd * output = bfd_openw(name, target); + output_filename = name; + if (output == (bfd *)NULL) + { + if (bfd_error == invalid_target) { + info("%P%F target %s not found\n", target); + } + info("%P%F problem opening output file %s, %E", name); + } + + output->flags |= D_PAGED; + bfd_set_format(output, bfd_object); + return output; +} +extern char *default_target; +static void +lang_phase_0(sh,target) +lang_statement_union_type *sh; +char *target; +{ + lang_statement_union_type *s = (lang_statement_union_type *)sh; + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + switch (s->header.type) { + case lang_output_section_statement_enum: + lang_phase_0(s->output_section_statement.children.head, + target); + break; + case lang_output_statement_enum: +#if 1 + output_bfd = open_output(s->output_statement.name, + target == (char *)NULL ? + default_target : target); + ldemul_set_output_arch(); +#endif + break; + case lang_target_statement_enum: + target = s->target_statement.target; + break; + case lang_wild_statement_enum: + /* Maybe we should load the file's symbols */ + if (s->wild_statement.filename) { + (void) lookup_name(s->wild_statement.filename, target); + } + break; + /* Attatch this to the current output section */ + case lang_common_statement_enum: + case lang_fill_statement_enum: + case lang_input_section_enum: + case lang_object_symbols_statement_enum: + case lang_address_statement_enum: + case lang_data_statement_enum: + break; + case lang_afile_asection_pair_statement_enum: + + FAIL(); + break; + + case lang_input_statement_enum: + if (s->input_statement.real == true) { + s->input_statement.target = target; + lookup_name(s->input_statement.filename, target); + } + break; + case lang_assignment_statement_enum: +#if 0 + (void) exp_fold_tree(s->assignment_statement.exp, + output_section, + false); +#endif + break; + + case lang_padding_statement_enum: + + break; + } + } + +} + +/* If there are [COMMONS] statements, put a wild one into the bss section */ + +static void +lang_reasonable_defaults() +{ + default_common_section = + lang_output_section_statement_lookup(".bss"); + if (placed_commons == false) { + lang_wild_statement_type *new = + new_stat(lang_wild_statement, + &default_common_section->children); + new->section_name = "COMMON"; + new->filename = (char *)NULL; + lang_list_init(&new->children); + } +} + +static void lang() +{ + if (had_script == false) { + parse_line(ldemul_get_script()); + } + + lang_reasonable_defaults(); + lang_phase_0(statement_list.head,default_target); +} + + +/* Open input files and attatch to output sections */ +static void +lang_open_input(s, target, output_section_statement) +lang_statement_union_type *s; +char *target; +lang_output_section_statement_type *output_section_statement; +{ + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + switch (s->header.type) { + case lang_wild_statement_enum: + wild(&s->wild_statement, s->wild_statement.section_name, + s->wild_statement.filename, target, + output_section_statement); + + break; + + case lang_output_section_statement_enum: + lang_open_input(s->output_section_statement.children.head, + target, + &s->output_section_statement); + break; + case lang_output_statement_enum: + break; + case lang_target_statement_enum: + target = s->target_statement.target; + break; + case lang_common_statement_enum: + case lang_fill_statement_enum: + case lang_input_section_enum: + case lang_object_symbols_statement_enum: + case lang_data_statement_enum: + break; + case lang_afile_asection_pair_statement_enum: + FAIL(); + break; + + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + + break; + case lang_address_statement_enum: + /* Mark the specified section with the supplied address */ + { + lang_output_section_statement_type *os = + lang_output_section_statement_lookup + (s->address_statement.section_name); + os->addr_tree = s->address_statement.address; + } + break; + case lang_input_statement_enum: + /* A standard input statement, has no wildcards */ + /* Q_read_file_symbols(&s->input_statement);*/ + break; + } + } +} + + + + + +static void +print_output_section_statement(output_section_statement) +lang_output_section_statement_type *output_section_statement; +{ + asection *section = output_section_statement->bfd_section; + print_nl(); + print_section(output_section_statement->name); + + if (section) { + print_dot = section->vma; + print_space(); + print_section(""); + print_space(); + print_address(section->vma); + print_space(); + print_size(section->size); + print_space(); + print_alignment(section->alignment_power); + print_space(); +#if 0 + printf("%s flags", output_section_statement->region->name); + print_flags(stdout, &output_section_statement->flags); +#endif + + } + else { + printf("No attached output section"); + } + print_nl(); + print_statement(output_section_statement->children.head, + output_section_statement); + +} + +static void +print_assignment(assignment, output_section) +lang_assignment_statement_type *assignment; +lang_output_section_statement_type *output_section; +{ + etree_value_type result; + print_section(""); + print_space(); + print_section(""); + print_space(); + print_address(print_dot); + print_space(); + result = exp_fold_tree(assignment->exp->assign.src, + output_section, + lang_final_phase_enum, + print_dot, + &print_dot); + + if (result.valid) { + print_address(result.value); + } + else + { + printf("*undefined*"); + } + print_space(); + exp_print_tree(stdout, assignment->exp); + printf("\n"); +} + +static void +print_input_statement(statm) +lang_input_statement_type *statm; +{ + printf("LOAD %s\n",statm->filename); +} + +static void print_symbol(q) +asymbol *q; +{ + print_section(""); + printf(" "); + print_section(""); + printf(" "); + print_address(outside_symbol_address(q)); + printf(" %s", q->name ? q->name : " "); + print_nl(); +} +static void +print_input_section(in) +lang_input_section_type *in; +{ + asection *i = in->section; + + if(i->size != 0) { + print_section(""); + printf(" "); + print_section(i->name); + printf(" "); + if (i->output_section) { + print_address(i->output_section->vma + i->output_offset); + printf(" "); + print_size(i->size); + printf(" "); + print_alignment(i->alignment_power); + printf(" "); + if (in->ifile) { + bfd *abfd = in->ifile->the_bfd; + printf(" %s ",abfd->xvec->name); + if(abfd->my_archive != (bfd *)NULL) { + printf("[%s]%s", abfd->my_archive->filename, + abfd->filename); + } + else { + printf("%s", abfd->filename); + } + print_nl(); + + /* Find all the symbols in this file defined in this section */ + { + asymbol **p; + for (p = in->ifile->asymbols; *p; p++) { + asymbol *q = *p; + + if (bfd_get_section(q) == i && q->flags & BSF_GLOBAL) { + print_symbol(q); + } + } + } + } + else { + print_nl(); + } + + + print_dot = outside_section_address(i) + i->size; + } + else { + printf("No output section allocated\n"); + } + } +} +static void +print_common_statement() +{ + ldsym_type *lgs; + print_section(""); + print_space(); + print_section(common_section.output_section->name); + print_space(); + print_address(common_section.output_offset + + common_section.output_section->vma); + print_space(); + print_size(common_section.size); + print_space(); + printf("(common)"); + print_nl(); + /* Print out all the global symbols */ + + + for (lgs = symbol_head; lgs != (ldsym_type *)NULL; lgs = + lgs->next) { + if (lgs->sdefs_chain) { + asymbol *def = *(lgs->sdefs_chain); + if (def->section == &common_section) { + print_symbol(def); + } + } + + } + print_dot = common_section.output_offset + + common_section.output_section->vma + common_section.size; + + +} +static void +print_fill_statement(fill) +lang_fill_statement_type *fill; +{ + printf("FILL mask "); + print_fill( fill->fill); +} + +static void +print_data_statement(data) +lang_data_statement_type *data; +{ +/* bfd_vma value; */ + print_section(""); + print_space(); + print_section(""); + print_space(); + ASSERT(print_dot == data->output_vma); + + print_address(data->output_vma); + print_space(); + print_address(data->value); + print_space(); + switch (data->type) { + case BYTE : + printf("BYTE "); + print_dot += BYTE_SIZE; + break; + case SHORT: + printf("SHORT "); + print_dot += SHORT_SIZE; + break; + case LONG: + printf("LONG "); + print_dot += LONG_SIZE; + break; + } + + exp_print_tree(stdout, data->exp); + + printf("\n"); +} + + +static void +print_padding_statement(s) +lang_padding_statement_type *s; +{ + print_section(""); + print_space(); + print_section("*fill*"); + print_space(); + print_address(s->output_offset + s->output_section->vma); + print_space(); + print_size(s->size); + print_space(); + print_fill(s->fill); + print_nl(); +} + +static void print_wild_statement(w,os) +lang_wild_statement_type *w; +lang_output_section_statement_type *os; +{ + if (w->filename != (char *)NULL) { + printf("%s",w->filename); + } + else { + printf("*"); + } + if (w->section_name != (char *)NULL) { + printf("(%s)",w->section_name); + } + else { + printf("(*)"); + } + print_nl(); + print_statement(w->children.head, os); + +} +static void +print_statement(s, os) +lang_statement_union_type *s; +lang_output_section_statement_type *os; +{ + while (s) { + switch (s->header.type) { + case lang_wild_statement_enum: + print_wild_statement(&s->wild_statement, os); + break; + default: + printf("Fail with %d\n",s->header.type); + FAIL(); + break; + case lang_address_statement_enum: + printf("address\n"); + break; + case lang_common_statement_enum: + print_common_statement(); + break; + case lang_object_symbols_statement_enum: + printf("object symbols\n"); + break; + case lang_fill_statement_enum: + print_fill_statement(&s->fill_statement); + break; + case lang_data_statement_enum: + print_data_statement(&s->data_statement); + break; + + + case lang_input_section_enum: + print_input_section(&s->input_section); + break; + case lang_padding_statement_enum: + print_padding_statement(&s->padding_statement); + break; + case lang_output_section_statement_enum: + print_output_section_statement(&s->output_section_statement); + break; + case lang_assignment_statement_enum: + print_assignment(&s->assignment_statement, + os); + break; + + + case lang_target_statement_enum: + printf("TARGET(%s)\n", s->target_statement.target); + break; + case lang_output_statement_enum: + printf("OUTPUT(%s)\n", s->output_statement.name); + break; + case lang_input_statement_enum: + print_input_statement(&s->input_statement); + break; + case lang_afile_asection_pair_statement_enum: + FAIL(); + break; + } + s = s->next; + } +} + + +static void +print_statements() +{ + print_statement(statement_list.head, + (lang_output_section_statement_type *)NULL); +} + +static bfd_vma +insert_pad(this_ptr, fill, power, output_section_statement, dot) +lang_statement_union_type **this_ptr; +fill_type fill; +unsigned int power; +asection * output_section_statement; +bfd_vma dot; +{ + /* Align this section first to the + input sections requirement, then + to the output section's requirement. + If this alignment is > than any seen before, + then record it too. Perform the alignment by + inserting a magic 'padding' statement. + */ + + unsigned int alignment_needed = align_power(dot, power) - dot; + + if (alignment_needed != 0) + { + lang_statement_union_type *new = + (lang_statement_union_type *) + ldmalloc(sizeof(lang_padding_statement_type)); + /* Link into existing chain */ + new->header.next = *this_ptr; + *this_ptr = new; + new->header.type = lang_padding_statement_enum; + new->padding_statement.output_section = output_section_statement; + new->padding_statement.output_offset = + dot - output_section_statement->vma; + new->padding_statement.fill = fill; + new->padding_statement.size = alignment_needed; + } + + + /* Remember the most restrictive alignment */ + if (power > output_section_statement->alignment_power) { + output_section_statement->alignment_power = power; + } + output_section_statement->size += alignment_needed; + return alignment_needed + dot; + +} + +/* + size_common runs run though each global symboxl, and works + out how big the common section will be. + */ + +static bfd_vma +size_common(output_section_statement, this_ptr, dot) +lang_output_section_statement_type *output_section_statement; +lang_statement_union_type **this_ptr; +bfd_vma dot; +{ + extern ldsym_type *symbol_head; + ldsym_type *sp; + /* Make sure that each symbol is only defined once. + Allocate common symbols + Make the ref chain point to the defining asymbol. + */ + /* Now, for each symbol, verify that it is defined globally at most once. + Put the global value into the symbol entry. + Common symbols are allocated here, in the BSS section. + Each defined symbol is given a '->defined' field + which is the correct N_ code for its definition, + except in the case of common symbols with -r. + Then make all the references point at the symbol entry + instead of being chained together. */ + + + common_section.name = output_section_statement->bfd_section->name; + common_section.output_section = output_section_statement->bfd_section; + common_section.output_offset = + dot - output_section_statement->bfd_section->vma; + if (config.relocateable_output == false || + command_line.force_common_definition== true) { + dot = insert_pad(this_ptr, + 0x0, 4, output_section_statement->bfd_section, dot); + + for (sp = symbol_head; sp != (ldsym_type *)NULL; sp = sp->next) + { + /* Attatch this symbol to the correct output section*/ + + /* Allocate as common if wanted */ + + if (sp->scoms_chain ) + + { + unsigned long com = (*(sp->scoms_chain))->value; + /* Work out what alignment this common item s + hould be put on. Anything < int is int aligned, + anything bigger is self aligned, + up to the restriction of the machine */ + + unsigned int align = sizeof(int); + + /* Round up size of object to nearest int */ + com = ALIGN(com, sizeof(int)); + /* See what alignment is necessary -*/ + if (com) { + while ((com & align)==0) align <<=1; + /* FIXME */ + if (align > 8) { + align = 8; + } + } + dot = ALIGN(dot, align); + + + /* Transmogrify this from a common symbol + into a definition of a symbol in common + */ + sp->sdefs_chain = sp->scoms_chain; + + { + asymbol *com_ptr = *(sp->sdefs_chain); + + sp->scoms_chain = (asymbol **)NULL; + commons_pending--; + /* Assign address, but keep section relative */ + + /* Force the symbol to belong in the bss section */ + com_ptr->flags = BSF_EXPORT | BSF_GLOBAL ; + com_ptr->section = &common_section; + common_section.size += com; + if (write_map) + { + printf ("Allocating common %s: %lx at %lx\n", + sp->name, + com, + com_ptr->value); + } + com_ptr->value = common_section.size; + } + } + } + } + if (dot > + (common_section.output_section->vma + + common_section.output_section->size)) { + common_section.output_section->size = + dot - common_section.output_section->vma; + } + return dot + common_section.size; +} + +static bfd_vma +size_input_section( this_ptr, output_section_statement, fill, dot) +lang_statement_union_type **this_ptr; +lang_output_section_statement_type*output_section_statement; +unsigned short fill; +bfd_vma dot; +{ + lang_input_section_type *is = &((*this_ptr)->input_section); + asection *i = is->section; + + dot = insert_pad(this_ptr, fill, i->alignment_power, + output_section_statement->bfd_section, dot); + + /* remember the largest size so we can malloc the largest area */ + /* needed for the output stage */ + if (i->size > largest_section) { + largest_section = i->size; + } + + /* Remember where in the output section this input section goes */ + i->output_offset = dot - output_section_statement->bfd_section->vma; + + /* Mark how big the output section must be to contain this now */ + dot += i->size; + output_section_statement->bfd_section->size = + dot - output_section_statement->bfd_section->vma; + + + return dot ; +} + + +/* Work out the size of the output sections + from the sizes of the input sections */ +static bfd_vma +lang_size_sections(s, output_section_statement, prev, fill, dot) +lang_statement_union_type *s; +lang_output_section_statement_type * output_section_statement; +lang_statement_union_type **prev; +unsigned short fill; +bfd_vma dot; +{ + /* Size up the sections from their constituent parts */ + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + switch (s->header.type) { + case lang_output_section_statement_enum: + { + bfd_vma after; + lang_output_section_statement_type *os = + &(s->output_section_statement); + /* The start of a section */ + + if (os->addr_tree == (etree_type *)NULL) { + /* No address specified for this section, get one + from the region specification + */ + if (os->region == (lang_memory_region_type *)NULL) { + os->region = lang_memory_region_lookup("*default*"); + } + dot = os->region->current; + } + else { + etree_value_type r ; + r = exp_fold_tree(os->addr_tree, + (lang_output_section_statement_type *)NULL, + lang_allocating_phase_enum, + dot, &dot); + if (r.valid == false) { + info("%F%S: non constant address expression for section %s\n", + os->name); + } + dot = r.value; + } + /* The section starts here */ + /* First, align to what the section needs */ + + dot = align_power(dot, os->bfd_section->alignment_power); + os->bfd_section->vma = dot; + os->bfd_section->output_offset = 0; + + (void) lang_size_sections(os->children.head, os, &os->children.head, + os->fill, dot); + /* Ignore the size of the input sections, use the vma and size to */ + /* align against */ + + + after = ALIGN(os->bfd_section->vma + + os->bfd_section->size, + os->block_value) ; + + + os->bfd_section->size = after - os->bfd_section->vma; + dot = os->bfd_section->vma + os->bfd_section->size; + os->processed = true; + + /* Replace into region ? */ + if (os->addr_tree == (etree_type *)NULL + && os->region !=(lang_memory_region_type*)NULL ) { + os->region->current = dot; + } + } + + break; + + case lang_data_statement_enum: + { + unsigned int size; + s->data_statement.output_vma = dot; + s->data_statement.output_section = + output_section_statement->bfd_section; + + switch (s->data_statement.type) { + case LONG: + size = LONG_SIZE; + break; + case SHORT: + size = SHORT_SIZE; + break; + case BYTE: + size = BYTE_SIZE; + break; + + } + dot += size; + output_section_statement->bfd_section->size += size; + } + break; + + case lang_wild_statement_enum: + + dot = lang_size_sections(s->wild_statement.children.head, + output_section_statement, + &s->wild_statement.children.head, + + fill, dot); + + break; + + case lang_object_symbols_statement_enum: + create_object_symbols = output_section_statement; + break; + case lang_output_statement_enum: + + case lang_target_statement_enum: + break; + case lang_common_statement_enum: + dot = size_common(output_section_statement, prev, dot); + + break; + + case lang_input_section_enum: + dot = size_input_section(prev, + output_section_statement, + output_section_statement->fill, dot); + break; + case lang_input_statement_enum: + break; + case lang_fill_statement_enum: + fill = s->fill_statement.fill; + break; + case lang_assignment_statement_enum: + { + bfd_vma newdot = dot; + exp_fold_tree(s->assignment_statement.exp, + output_section_statement, + lang_allocating_phase_enum, + dot, + &newdot); + + if (newdot != dot) + /* We've been moved ! so insert a pad */ + { + lang_statement_union_type *new = + (lang_statement_union_type *) + ldmalloc(sizeof(lang_padding_statement_type)); + /* Link into existing chain */ + new->header.next = *prev; + *prev = new; + new->header.type = lang_padding_statement_enum; + new->padding_statement.output_section = + output_section_statement->bfd_section; + new->padding_statement.output_offset = + dot - output_section_statement->bfd_section->vma; + new->padding_statement.fill = fill; + new->padding_statement.size = newdot - dot; + output_section_statement->bfd_section->size += + new->padding_statement.size; + dot = newdot; + } + } + + break; + case lang_padding_statement_enum: + FAIL(); + break; + default: + FAIL(); + break; + case lang_address_statement_enum: + break; + } + prev = &s->header.next; + } + return dot; +} + + +static bfd_vma +lang_do_assignments(s, output_section_statement, fill, dot) +lang_statement_union_type *s; +lang_output_section_statement_type * output_section_statement; +unsigned short fill; +bfd_vma dot; +{ + + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + switch (s->header.type) { + case lang_output_section_statement_enum: + { + lang_output_section_statement_type *os = + &(s->output_section_statement); + dot = os->bfd_section->vma; + (void) lang_do_assignments(os->children.head, os, os->fill, dot); + dot = os->bfd_section->vma + os->bfd_section->size; + } + break; + case lang_wild_statement_enum: + + dot = lang_do_assignments(s->wild_statement.children.head, + output_section_statement, + fill, dot); + + break; + + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_common_statement_enum: + break; + case lang_data_statement_enum: + { + etree_value_type value ; + value = exp_fold_tree(s->data_statement.exp, + 0, lang_final_phase_enum, dot, &dot); + s->data_statement.value = value.value; + if (value.valid == false) info("%F%P: Invalid data statement\n"); + } + switch (s->data_statement.type) { + case LONG: + dot += LONG_SIZE; + break; + case SHORT: + dot += SHORT_SIZE; + break; + case BYTE: + dot += BYTE_SIZE; + break; + } + break; + case lang_input_section_enum: + { + asection *in = s->input_section.section; + dot += in->size; + } + break; + + case lang_input_statement_enum: + break; + case lang_fill_statement_enum: + fill = s->fill_statement.fill; + break; + case lang_assignment_statement_enum: + { + exp_fold_tree(s->assignment_statement.exp, + output_section_statement, + lang_final_phase_enum, + dot, + &dot); + } + + break; + case lang_padding_statement_enum: + dot += s->padding_statement.size; + break; + default: + FAIL(); + break; + case lang_address_statement_enum: + break; + } + + } + return dot; +} + + + +static void lang_relocate_globals() +{ + + /* + Each ldsym_type maintains a chain of pointers to asymbols which + references the definition. Replace each pointer to the referenence + with a pointer to only one place, preferably the definition. If + the defintion isn't available then the common symbol, and if + there isn't one of them then choose one reference. + */ + + FOR_EACH_LDSYM(lgs) { + asymbol *it; + if (lgs->sdefs_chain) { + it = *(lgs->sdefs_chain); + } + else if (lgs->scoms_chain != (asymbol **)NULL) { + it = *(lgs->scoms_chain); + } + else if (lgs->srefs_chain != (asymbol **)NULL) { + it = *(lgs->srefs_chain); + } + else { + FAIL(); + } + if (it != (asymbol *)NULL) + { + asymbol **ptr= lgs->srefs_chain; + + while (ptr != (asymbol **)NULL) { + asymbol *ref = *ptr; + *ptr = it; + ptr = (asymbol **)(ref->udata); + } + } + } +} + + + +/* now that all the jiggery pokery is finished, copy important data from + * out internal form to the bfd way. Also create a section + * for each dummy file + */ + +static void +lang_create_output_section_statements() +{ + lang_statement_union_type*os; + for (os = lang_output_section_statement.head; + os != (lang_statement_union_type*)NULL; + os = os->output_section_statement.next) { + lang_output_section_statement_type *s = + &os->output_section_statement; + init_os(s); + } + script_file->the_bfd->sections = output_bfd->sections; +} + +static void +lang_finish() +{ + ldsym_type *lgs; + + if (entry_symbol == (char *)NULL) { + /* No entry has been specified, look for start */ + entry_symbol = "start"; + } + lgs = ldsym_get_soft(entry_symbol); + if (lgs && lgs->sdefs_chain) { + asymbol *sy = *(lgs->sdefs_chain); + /* We can set the entry address*/ + bfd_set_start_address(output_bfd, + outside_symbol_address(sy)); + + } + else { + /* Can't find anything reasonable, + use the first address in the text section + */ + asection *ts = bfd_get_section_by_name(output_bfd, ".text"); + if (ts) { + bfd_set_start_address(output_bfd, ts->vma); + } + } +} + +/* By now we know the target architecture, and we may have an */ +/* ldfile_output_machine_name */ +static void +lang_check() +{ + lang_statement_union_type *file; + + + for (file = file_chain.head; + file != (lang_statement_union_type *)NULL; + file=file->input_statement.next) + { + /* Inspect the architecture and ensure we're linking like + with like + */ + + if (bfd_arch_compatible( file->input_statement.the_bfd, + output_bfd, + &ldfile_output_architecture, + &ldfile_output_machine)) { + bfd_set_arch_mach(output_bfd, + ldfile_output_architecture, ldfile_output_machine); + } + else { + enum bfd_architecture this_architecture = + bfd_get_architecture(file->input_statement.the_bfd); + unsigned long this_machine = + bfd_get_machine(file->input_statement.the_bfd); + + info("%I: architecture %s", + file, + bfd_printable_arch_mach(this_architecture, this_machine)); + info(" incompatible with output %s\n", + bfd_printable_arch_mach(ldfile_output_architecture, + ldfile_output_machine)); + ldfile_output_architecture = this_architecture; + ldfile_output_machine = this_machine; + bfd_set_arch_mach(output_bfd, + ldfile_output_architecture, + ldfile_output_machine); + + + } + } +} + + +/* + * run through all the global common symbols and tie them + * to the output section requested. + */ + +static void +lang_common() +{ + ldsym_type *lgs; + if (config.relocateable_output == false || + command_line.force_common_definition== true) { + for (lgs = symbol_head; + lgs != (ldsym_type *)NULL; + lgs=lgs->next) + { + asymbol *com ; + size_t size; + size_t align; + if (lgs->scoms_chain != (asymbol **)NULL) { + + com = *(lgs->scoms_chain); + size = com->value; + align = sizeof(int); + /* Round up size of object to nearest int */ + size = ALIGN(size, sizeof(int)); + /* Force alignment */ + if (size) { + while ((size & align)==0) align<<=1; + if (align > 8) { + align = 8; + } + } + /* Change from a common symbol into a definition of + a symbol */ + lgs->sdefs_chain = lgs->scoms_chain; + lgs->scoms_chain = (asymbol **)NULL; + commons_pending--; + /* Point to the correct common section */ + com->section = + ((lang_input_statement_type *) + (com->the_bfd->usrdata))->common_section; + /* Fix the size of the common section */ + + + com->flags = BSF_EXPORT | BSF_GLOBAL; + + if (write_map) + { + printf ("Allocating common %s: %x at %x\n", + lgs->name, + (unsigned) size, + (unsigned) com->section->size); + } + com->value = com->section->size; + com->section->size += size; + } + } + } +} + +/* +run through the input files and ensure that every input +section has somewhere to go. If one is found without +a destination then create an input request and place it +into the statement tree. +*/ + +static void lang_place_orphans() +{ + lang_input_statement_type *file; + for (file = (lang_input_statement_type*)file_chain.head; + file != (lang_input_statement_type*)NULL; + file = (lang_input_statement_type*)file->next) { + asection *s; + for (s = file->the_bfd->sections; + s != (asection *)NULL; + s = s->next) { + if ( s->output_section == (asection *)NULL) { + /* This section of the file is not attatched, root + around for a sensible place for it to go */ + + if (file->common_section == s) { + /* This is a lonely common section which must + have come from an archive. We attatch to the + section with the wildcard */ + wild_doit(&default_common_section->children, s, + default_common_section, file); + } + else { + lang_output_section_statement_type *os = + lang_output_section_statement_lookup(s->name); + + wild_doit(&os->children, s, os, file); + } + } + } + + } +} + + +/* + * phase_2 + * + * peformed after every file has been opened and symbols read + */ +static void +lang_phase_2() +{ + lang_init2(); + + lang_create_output_section_statements(); + lang_open_input(statement_list.head, (char *)NULL, + ( lang_output_section_statement_type *)NULL); + lang_place_orphans(); + lang_common(); + + ldemul_before_allocation(); + + lang_size_sections(statement_list.head, + (lang_output_section_statement_type *)NULL, + &(statement_list.head), 0, (bfd_vma)0); + ldemul_after_allocation(); + /* Do it once again now that we know the sizes of everything */ + + lang_do_assignments(statement_list.head, + (lang_output_section_statement_type *)NULL, + 0, (bfd_vma)0); + + + + lang_check(); + + lang_relocate_globals(); + + + lang_finish(); +} + + + + +void +lang_set_flags(ptr, flags) +lang_section_flags_type *ptr; +char *flags; +{ + boolean state = true; + ptr->flag_read = false; + ptr->flag_write = false; + ptr->flag_executable = false; + ptr->flag_loadable= false; + while (*flags) + { + if (*flags == '!') { + state = false; + flags++; + } + else state = true; + switch (*flags) { + case 'R': + ptr->flag_read = state; + break; + case 'W': + ptr->flag_write = state; + break; + case 'X': + ptr->flag_executable= state; + break; + case 'L': + ptr->flag_loadable= state; + break; + default: + info("%P%F illegal syntax in flags\n"); + break; + } + flags++; + } +} + + + +void +lang_for_each_file(func) +void (*func)(); +{ + lang_input_statement_type *f; + for (f = (lang_input_statement_type *)file_chain.head; + f != (lang_input_statement_type *)NULL; + f = (lang_input_statement_type *)f->next) + { + func(f); + } +} + + +void +lang_for_each_input_section(func) +void (*func)(); +{ + lang_input_statement_type *f; + for (f = (lang_input_statement_type *)file_chain.head; + f != (lang_input_statement_type *)NULL; + f = (lang_input_statement_type *)f->next) + { + asection *s; + for (s = f->the_bfd->sections; + s != (asection *)NULL; + s = s->next) { + func(f->the_bfd, s); + } + } +} + + + +void +ldlang_add_file(entry) +lang_input_statement_type *entry; +{ + lang_has_input_file = true; + lang_statement_append(&file_chain, + (lang_statement_union_type *)entry, + &entry->next); +} + + + +void +lang_add_output(name) +char *name; +{ + lang_output_statement_type *new = new_stat(lang_output_statement, + stat_ptr); + new->name = name; + had_output_filename = true; +} + + +static lang_output_section_statement_type *current_section; + +void +lang_enter_output_section_statement(output_section_statement_name, +address_exp, +block_value) +char *output_section_statement_name; +etree_type *address_exp; +bfd_vma block_value; +{ + lang_output_section_statement_type *os; + current_section = + os = + lang_output_section_statement_lookup(output_section_statement_name); + + + /* Add this statement to tree */ + /* add_statement(lang_output_section_statement_enum, + output_section_statement);*/ + /* Make next things chain into subchain of this */ + + if (os->addr_tree == + (etree_type *)NULL) { + os->addr_tree = + address_exp; + } + os->block_value = block_value; + stat_ptr = & os->children; + +} + + +void +lang_final() +{ + if (had_output_filename == false) { + lang_add_output("a.out"); + } + + +} + + + + + +asymbol *create_symbol(name, flags, section) +char *name; +flagword flags; +asection *section; +{ + extern lang_input_statement_type *script_file; + asymbol **def_ptr = (asymbol **)ldmalloc(sizeof(asymbol **)); + /* Add this definition to script file */ + asymbol *def = (asymbol *)bfd_make_empty_symbol(script_file->the_bfd); + def->name = name; + def->udata = 0; + def->flags = flags; + def->section = section; + + *def_ptr = def; + Q_enter_global_ref(def_ptr); + return def; +} + + +void +lang_process() +{ + lang(); + lang_phase_2(); +} + + +/* EXPORTED TO YACC */ +void +lang_section_start(name, address) +char *name; +etree_type *address; +{ + lang_address_statement_type *ad =new_stat(lang_address_statement, stat_ptr); + ad->section_name = name; + ad->address = address; +} +void lang_add_entry(name) +char *name; +{ + entry_symbol = name; +} + +void +lang_add_target(name) +char *name; +{ + lang_target_statement_type *new = new_stat(lang_target_statement, + stat_ptr); + new->target = name; + +} +void +lang_add_wild(section_name, filename) +char *section_name; +char *filename; +{ + lang_wild_statement_type *new = new_stat(lang_wild_statement, + stat_ptr); + + if (section_name != (char *)NULL && strcmp(section_name,"COMMON") == 0) + { + placed_commons = true; + } + new->section_name = section_name; + new->filename = filename; + lang_list_init(&new->children); +} + +void +lang_add_map(name) +char *name; +{ + while (*name) { + switch (*name) { + case 'F': + map_option_f = true; + break; + } + name++; + } +} + +void lang_add_fill(exp) +int exp; +{ + lang_fill_statement_type *new = new_stat(lang_fill_statement, + stat_ptr); + new->fill = exp; +} + +void lang_add_data(type, exp) +int type; +union etree_union *exp; +{ + + lang_data_statement_type *new = new_stat(lang_data_statement, + stat_ptr); + new->exp = exp; + new->type = type; + +} +void +lang_add_assignment(exp) +etree_type *exp; +{ + lang_assignment_statement_type *new = new_stat(lang_assignment_statement, + stat_ptr); + new->exp = exp; +} + +void +lang_add_attribute(attribute) +enum statement_enum attribute; +{ + new_statement(attribute, sizeof(lang_statement_union_type),stat_ptr); +} + + + +void +lang_startup(name) +char *name; +{ + if (startup_file != (char *)NULL) { + info("%P%FMultiple STARTUP files\n"); + } + first_file->filename = name; + first_file->local_sym_name = name; + + startup_file= name; +} +void +lang_float(maybe) +boolean maybe; +{ + lang_float_flag = maybe; +} + +void +lang_leave_output_section_statement(fill, memspec) +bfd_vma fill; +char *memspec; +{ + current_section->fill = fill; + current_section->region = lang_memory_region_lookup(memspec); + stat_ptr = &statement_list; +} + +void +lang_abs_symbol_at_end_of(section, name) +char *section; +char *name; +{ + extern bfd *output_bfd; + extern asymbol *create_symbol(); + asection *s = bfd_get_section_by_name(output_bfd, section); + /* Add a symbol called _end */ + asymbol *def = create_symbol(name, + BSF_GLOBAL | BSF_EXPORT | + BSF_ABSOLUTE, + (asection *)NULL); + if (s != (asection *)NULL) { + def->value = s->vma + s->size; + } + else { + def->value = 0; + } +} + +void +lang_statement_append(list, element, field) +lang_statement_list_type *list; +lang_statement_union_type *element; +lang_statement_union_type **field; +{ + *(list->tail) = element; + list->tail = field; +} + + +static void +lang_for_each_statement_worker(func, s) +void (*func)(); +lang_statement_union_type *s; +{ + for (; s != (lang_statement_union_type *)NULL ; s = s->next) + { + func(s); + + switch (s->header.type) { + case lang_output_section_statement_enum: + lang_for_each_statement_worker + (func, + s->output_section_statement.children.head); + break; + case lang_wild_statement_enum: + lang_for_each_statement_worker + (func, + s->wild_statement.children.head); + break; + case lang_data_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_common_statement_enum: + case lang_input_section_enum: + case lang_input_statement_enum: + case lang_fill_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + break; + default: + FAIL(); + break; + } + } +} + +void lang_for_each_statement(func) +void (*func)(); +{ + lang_for_each_statement_worker(func, + statement_list.head); + +} diff --git a/ld/ldlang.h b/ld/ldlang.h new file mode 100644 index 0000000..ab102a5 --- /dev/null +++ b/ld/ldlang.h @@ -0,0 +1,347 @@ +/* ldlang.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +typedef enum { + lang_input_file_is_l_enum, + lang_input_file_is_symbols_only_enum, + lang_input_file_is_marker_enum, + lang_input_file_is_fake_enum, +lang_input_file_is_search_file_enum, + lang_input_file_is_file_enum } lang_input_file_enum_type; + +typedef unsigned short fill_type; +typedef struct statement_list { + union lang_statement_union *head; + union lang_statement_union **tail; +} lang_statement_list_type; + + +typedef struct { + boolean flag_read; + boolean flag_write; + boolean flag_executable; + boolean flag_loadable; +} lang_section_flags_type; + +typedef struct memory_region_struct { + char *name; + struct memory_region_struct *next; + bfd_vma origin; + bfd_offset length; + bfd_vma current; + lang_section_flags_type flags; +} lang_memory_region_type ; + +typedef struct lang_statement_header_struct +{ +union lang_statement_union *next; + enum statement_enum { + lang_output_section_statement_enum, + lang_assignment_statement_enum, + lang_input_statement_enum, + lang_address_statement_enum, + lang_wild_statement_enum, + lang_input_section_enum, + lang_common_statement_enum, + lang_object_symbols_statement_enum, + lang_fill_statement_enum, + lang_data_statement_enum, + lang_target_statement_enum, + lang_output_statement_enum, + lang_padding_statement_enum, + + lang_afile_asection_pair_statement_enum + } type; + +} lang_statement_header_type; + + +typedef struct +{ + lang_statement_header_type header; + union etree_union *exp; +} lang_assignment_statement_type; + + +typedef struct lang_target_statement_struct { + lang_statement_header_type header; + char *target; +} lang_target_statement_type; + + +typedef struct lang_output_statement_struct { + lang_statement_header_type header; + char *name; +} lang_output_statement_type; + + +typedef struct lang_output_section_statement_struct +{ + lang_statement_header_type header; + union etree_union *addr_tree; + lang_statement_list_type children; + char *memspec; + union lang_statement_union *next; + char *name; + unsigned long subsection_alignment; + boolean processed; + + asection *bfd_section; + lang_section_flags_type flags; + struct memory_region_struct *region; + size_t block_value; + fill_type fill; +} lang_output_section_statement_type; + + +typedef struct { + lang_statement_header_type header; +} lang_common_statement_type; + +typedef struct { + lang_statement_header_type header; +} lang_object_symbols_statement_type; + +typedef struct { + lang_statement_header_type header; +fill_type fill; +} lang_fill_statement_type; + +typedef struct { + lang_statement_header_type header; + unsigned int type; + union etree_union *exp; + bfd_vma value; + asection *output_section; + bfd_vma output_vma; +} lang_data_statement_type; + + + + +typedef struct lang_input_statement_struct + { + lang_statement_header_type header; + /* Name of this file. */ + char *filename; + /* Name to use for the symbol giving address of text start */ + /* Usually the same as filename, but for a file spec'd with -l + this is the -l switch itself rather than the filename. */ + char *local_sym_name; + + /* Describe the layout of the contents of the file */ + + /* The file's a.out header. */ + /* struct exec header;*/ + /* Offset in file of GDB symbol segment, or 0 if there is none. */ + int symseg_offset; + + /* Describe data from the file loaded into core */ + + bfd *the_bfd; + + boolean closed; + file_ptr passive_position; + + /* Symbol table of the file. */ + asymbol **asymbols; + unsigned int symbol_count; + + /* Next two used only if `relocatable_output' or if needed for */ + /* output of undefined reference line numbers. */ + /* Text reloc info saved by `write_text' for `coptxtrel'. */ + + + /* Offset in bytes in the output file symbol table + of the first local symbol for this file. Set by `write_file_symbols'. */ + int local_syms_offset; + + /* For library members only */ + + /* For a library, points to chain of entries for the library members. */ + struct lang_input_statement_struct *subfiles; + /* For a library member, offset of the member within the archive. + Zero for files that are not library members. */ + /* int starting_offset;*/ + /* Size of contents of this file, if library member. */ + int total_size; + /* For library member, points to the library's own entry. */ + struct lang_input_statement_struct *superfile; + /* For library member, points to next entry for next member. */ + struct lang_input_statement_struct *chain; + /* Point to the next file - whatever it is, wanders up and down archives */ + union lang_statement_union *next; + /* Point to the next file, but skips archive contents */ + union lang_statement_union *next_real_file; + + boolean is_archive; + + /* 1 if file's header has been read into this structure. */ + boolean header_read_flag; + + /* 1 means search a set of directories for this file. */ + boolean search_dirs_flag; + + /* 1 means this is base file of incremental load. + Do not load this file's text or data. + Also default text_start to after this file's bss. */ + boolean just_syms_flag; + + boolean loaded; + + + /* unsigned int globals_in_this_file;*/ + char *target; + boolean real; + + asection *common_section; + asection *common_output_section; + } lang_input_statement_type; + +typedef struct { + lang_statement_header_type header; + asection *section; + lang_input_statement_type *ifile; +} lang_input_section_type; + + +typedef struct { + lang_statement_header_type header; + asection *section; + union lang_statement_union *file; +} lang_afile_asection_pair_statement_type; + +typedef struct lang_wild_statement_struct { + lang_statement_header_type header; + char *section_name; + char *filename; + lang_statement_list_type children; +} lang_wild_statement_type; + +typedef struct lang_address_statement_struct { + lang_statement_header_type header; + char *section_name; + union etree_union *address; +} lang_address_statement_type; + +typedef struct { + lang_statement_header_type header; + bfd_vma output_offset; + size_t size; + asection *output_section; + fill_type fill; +} lang_padding_statement_type; + +typedef union lang_statement_union +{ + lang_statement_header_type header; + union lang_statement_union *next; + lang_wild_statement_type wild_statement; + lang_data_statement_type data_statement; + lang_address_statement_type address_statement; + lang_output_section_statement_type output_section_statement; + lang_afile_asection_pair_statement_type afile_asection_pair_statement; + lang_assignment_statement_type assignment_statement; + lang_input_statement_type input_statement; + lang_target_statement_type target_statement; + lang_output_statement_type output_statement; + lang_input_section_type input_section; + lang_common_statement_type common_statement; + lang_object_symbols_statement_type object_symbols_statement; + lang_fill_statement_type fill_statement; + lang_padding_statement_type padding_statement; +} lang_statement_union_type; + + + +PROTO(void,lang_init,(void)); +PROTO(struct memory_region_struct ,*lang_memory_region_lookup,(char *)); +PROTO(struct lang_output_section_statement_struct *,lang_output_section_find,(char *)); + +PROTO(void ,lang_map,(struct _iobuf *)); +PROTO(void,lang_set_flags,(lang_section_flags_type *, char *)); +PROTO(void,lang_add_output,(char *)); + +PROTO(void,lang_final,(void)); +PROTO(struct symbol_cache_entry *,create_symbol,(char *, unsigned int, struct sec_struct *)); +PROTO(void ,lang_process,(void)); +PROTO(void ,lang_section_start,(char *, union etree_union *)); +PROTO(void,lang_add_entry,(char *)); +PROTO(void,lang_add_target,(char *)); +PROTO(void,lang_add_wild,(char *, char *)); +PROTO(void,lang_add_map,(char *)); +PROTO(void,lang_add_fill,(int)); +PROTO(void,lang_add_assignment,(union etree_union *)); +PROTO(void,lang_add_attribute,(enum statement_enum)); +PROTO(void,lang_startup,(char *)); +PROTO(void,lang_float,(enum boolean)); +PROTO(void,lang_leave_output_section_statement,(bfd_vma, char *)); +PROTO(void,lang_abs_symbol_at_end_of,(char *, char *)); +PROTO(void,lang_statement_append,(struct statement_list *, union lang_statement_union *, union lang_statement_union **)); +PROTO(void, lang_for_each_file,(void (*dothis)(lang_input_statement_type *))); + +#define LANG_FOR_EACH_ASYMBOL(asymbol) \ + +#define LANG_FOR_EACH_INPUT_STATEMENT(statement) \ + extern lang_statement_list_type file_chain; \ + lang_input_statement_type *statement; \ + for (statement = (lang_input_statement_type *)file_chain.head;\ + statement != (lang_input_statement_type *)NULL; \ + statement = (lang_input_statement_type *)statement->next)\ + +#define LANG_FOR_EACH_INPUT_SECTION(statement, abfd, section, x) \ +{ extern lang_statement_list_type file_chain; \ + lang_input_statement_type *statement; \ + for (statement = (lang_input_statement_type *)file_chain.head;\ + statement != (lang_input_statement_type *)NULL; \ + statement = (lang_input_statement_type *)statement->next)\ + { \ + asection *section; \ + bfd *abfd = statement->the_bfd; \ + for (section = abfd->sections; \ + section != (asection *)NULL; \ + section = section->next) { \ + x; \ + } \ + } \ + } + +#define LANG_FOR_EACH_OUTPUT_SECTION(section, x) \ + { extern bfd *output_bfd; \ + asection *section; \ + for (section = output_bfd->sections; \ + section != (asection *)NULL; \ + section = section->next) \ + { x; } \ + } + + +PROTO(void, lang_process,(void)); +PROTO(void, ldlang_add_file,(lang_input_statement_type *)); + +PROTO(lang_output_section_statement_type *,lang_output_section_find,()); + +PROTO(lang_input_statement_type *, + lang_add_input_file,(char *name, + lang_input_file_enum_type file_type, + char *target)); +PROTO(lang_output_section_statement_type *, +lang_output_section_statement_lookup,(char *name)); diff --git a/ld/ldlex.h b/ld/ldlex.h new file mode 100644 index 0000000..fe4e017 --- /dev/null +++ b/ld/ldlex.h @@ -0,0 +1,26 @@ +/* ldlex.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +PROTO(int, lex_input, (void)); +PROTO(void, lex_unput, (int)); +PROTO(int ,yywrap,(void)); +PROTO(void, parse_args,(int, char **)); +PROTO(void, parse_line,(char*)); + diff --git a/ld/ldlex.l b/ld/ldlex.l new file mode 100644 index 0000000..67af46b --- /dev/null +++ b/ld/ldlex.l @@ -0,0 +1,490 @@ +%{ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:50 gumby + * Initial revision + * + * Revision 1.3 1991/03/16 22:27:24 rich + * fish + * + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:27 chrisb + * Initial revision + * + * Revision 1.6 1991/03/10 09:31:32 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:25:49 sac + * Can now parse the -Ur flag + * + * Revision 1.4 1991/03/06 02:26:04 sac + * Added support for constructor sections. + * Remove parsing ambiguity. + * Lint + * + * Revision 1.3 1991/02/22 17:15:14 sac + * Added RCS keywords and copyrights + * +*/ + + + +/*SUPPRESS 529*/ +/*SUPPRESS 26*/ +/*SUPPRESS 29*/ +#define LEXDEBUG +#include "sysdep.h" +#include "bfd.h" + +#include <ctype.h> +#include "ldlex.h" + +#include "ld.h" +#include "ldexp.h" +#include "ldgram.tab.h" +#include "ldmisc.h" + +#undef input +#undef unput +#define input lex_input +#define unput lex_unput +int debug; +extern boolean ldgram_want_filename; +extern boolean ldgram_mustbe_filename; +extern boolean ldgram_mustbe_symbolname; +static char *command_line; + +extern int fgetc(); +extern int yyparse(); + +typedef struct { + char *name; +int value; +} keyword_type; +#define RTOKEN(x) { yylval.token = x; return x; } +keyword_type keywords[] = +{ +"MEMORY",MEMORY, +"ORIGIN",ORIGIN, +"BLOCK",BLOCK, +"LENGTH",LENGTH, +"ALIGN",ALIGN_K, +"SUBSECTION_ALIGN",SUBSECTION_ALIGN, +"ADDR",ADDR, +"ENTRY",ENTRY, +"NEXT",NEXT, +"MAP",MAP, +"SIZEOF",SIZEOF, +"TARGET",TARGET_K, +"SEARCH_DIR",SEARCH_DIR, +"OUTPUT",OUTPUT, +"INPUT",INPUT, +"DEFINED",DEFINED, +"CREATE_OBJECT_SYMBOLS",CREATE_OBJECT_SYMBOLS, +"SECTIONS",SECTIONS, +"FILL",FILL, +"STARTUP",STARTUP, +"HLL",HLL, +"SYSLIB",SYSLIB, +"FLOAT",FLOAT, +"LONG", LONG, +"SHORT", SHORT, +"BYTE", BYTE, +"NOFLOAT",NOFLOAT, +"o",ORIGIN, +"org",ORIGIN, +"l", LENGTH, +"len", LENGTH, +0,0}; +unsigned int lineno; +extern boolean hex_mode; +FILE *ldlex_input_stack; +static unsigned int have_pushback; +#define NPUSHBACK 10 +int pushback[NPUSHBACK]; +int thischar; +extern char *ldfile_input_filename; + +int +lex_input() +{ + /* + When we know that the next token must be a filename we force the + input routine to return a '#' character, which will cause the special + filname regexp to match the following chars even if they don't look + much like a filename to any sane person. + */ + if (ldgram_mustbe_filename) { + ldgram_mustbe_filename = false; + return '#'; + } + + if (have_pushback > 0) + { + have_pushback --; + return thischar = pushback[have_pushback]; + } + if (ldlex_input_stack) { + thischar = fgetc(ldlex_input_stack); + + if (thischar == EOF) { + fclose(ldlex_input_stack); + ldlex_input_stack = (FILE *)NULL; + ldfile_input_filename = (char *)NULL; + thischar = lex_input(); + + } + } + else if (command_line && *command_line) { + thischar = *(command_line++); + } + else thischar = 0; + if(thischar == '\t') thischar = ' '; + return thischar ; +} + +void +lex_unput(c) +int c; +{ + if (have_pushback > NPUSHBACK) { + info("%F%P Too many pushbacks\n"); + } + + pushback[have_pushback] = c; + have_pushback ++; +} + + + int +yywrap() + { return 1; } +/*VARARGS*/ + +void +allprint(x) +int x; +{ +fprintf(yyout,"%d",x); +} + +void +sprint(x) +char *x; +{ +fprintf(yyout,"%s",x); +} + +int thischar; + +void parse_line(arg) +char *arg; +{ + command_line = arg; + have_pushback = 0; + yyparse(); +} + + + +void +parse_args(ac, av) +int ac; +char **av; +{ + char *p; + int i; + size_t size = 0; + char *dst; + debug = 1; + for (i= 1; i < ac; i++) { + size += strlen(av[i]) + 2; + } + dst = p = (char *)ldmalloc(size + 2); +/* Put a space arount each option */ + + + for (i =1; i < ac; i++) { + + unsigned int s = strlen(av[i]); + *dst++ = ' '; + memcpy(dst, av[i], s); + dst[s] = ' '; + dst += s + 1; + } + *dst = 0; + parse_line(p); + + free(p); + + +} + +long number(text, base) +char *text; +int base; +{ +unsigned long l = 0; + char *p; + for (p = text; *p != 0; p++) { + if (*p == 'K') { + l =l * 1024; + } + else if(*p== 'M') { + l =l * 1024 * 1024; + } + else { + l =l * base; + if (isdigit(*p)) { + l += *p - '0'; + } + else if (islower(*p)) { + l += *p - 'a' + 10; + } + else { + l += *p - 'A' + 10; + } + } + } + return l; +} +%} + +%a 4000 +%o 5000 +FILENAMECHAR [a-zA-Z0-9\/\.\-\_\+] +FILENAME {FILENAMECHAR}+ + + +WHITE [ \t]+ + +%% +"\n" { lineno++; } + + +"\ -defsym" { return OPTION_defsym; } +"\ -noinhibit_exec" { return OPTION_noinhibit_exec; } +"\ -format" { return OPTION_format; } +"\ -n" { return OPTION_n; } +"\ -r" { return OPTION_r; } +"\ -Ur" { return OPTION_Ur; } +"\ -o" { return OPTION_o; } +"\ -g" { return OPTION_g; } +"\ -e" { return OPTION_e; } +"\ -b" { return OPTION_b; } +"\ -dc" { return OPTION_dc; } +"\ -dp" { return OPTION_dp; } +"\ -d" { return OPTION_d; } +"\ -v" { return OPTION_v; } +"\ -M" { return OPTION_M; } +"\ -t" { return OPTION_t; } +"\ -X" { return OPTION_X; } +"\ -x" { return OPTION_x; } +"\ -c" { return OPTION_c; } +"\ -s" { return OPTION_s; } +"\ -S" { return OPTION_S; } +"\ -l"{FILENAME} { + yylval.name = buystring(yytext+3); + return OPTION_l; + } + +"\ -L"{FILENAME} { + yylval.name = buystring(yytext+3); + return OPTION_L; + } +"\ -Ttext" { + yylval.name = ".text"; + return OPTION_Texp; + } +"\ -Tdata" { + yylval.name = ".data"; + return OPTION_Texp; + } +"\ -Tbss" { + yylval.name = ".bss"; + return OPTION_Texp; + } + +"\ -T"{FILENAME} { + yylval.name = buystring(yytext+3); + return OPTION_Tfile; + } +"\ -T" { + return OPTION_T; + } + +"\ -A"{FILENAME} { + yylval.name = buystring(yytext+3); + return OPTION_Aarch; + } +" " { } +"<<=" { RTOKEN(LSHIFTEQ);} +">>=" { RTOKEN(RSHIFTEQ);} +"||" { RTOKEN(OROR);} +"==" { RTOKEN(EQ);} +"!=" { RTOKEN(NE);} +">=" { RTOKEN(GE);} +"<=" { RTOKEN(LE);} +"<<" { RTOKEN(LSHIFT);} +">>" { RTOKEN(RSHIFT);} +"+=" { RTOKEN(PLUSEQ);} +"-=" { RTOKEN(MINUSEQ);} +"*=" { RTOKEN(MULTEQ);} +"/=" { RTOKEN(DIVEQ);} +"&=" { RTOKEN(ANDEQ);} +"|=" { RTOKEN(OREQ);} + +"&&" { RTOKEN(ANDAND);} +">" { RTOKEN('>');} +"," { RTOKEN(',');} +"&" { RTOKEN('&');} +"|" { RTOKEN('|');} +"~" { RTOKEN('~');} +"!" { RTOKEN('!');} +"?" { RTOKEN('?');} +"*" { RTOKEN('*');} +"%" { RTOKEN('%');} +"<" { RTOKEN('<');} +"+" { RTOKEN('+');} +">" { RTOKEN('>');} +"}" { RTOKEN('}') ; } +"{" { RTOKEN('{'); } +")" { RTOKEN(')');} +"(" { RTOKEN('(');} +"]" { RTOKEN(']');} +"[" { RTOKEN('[');} +":" { RTOKEN(':'); } +";" { RTOKEN(';');} +"-" { RTOKEN('-');} +"=" { RTOKEN('=');} + + +"/*" { + while (1) { + int ch; + ch = input(); + while (ch != '*') { + if (ch == '\n') {lineno++; } + ch = input(); + } + + + + if (input() == '/') { + break; + } + unput(yytext[yyleng-1]); + } +} + +"\""[^\"]*"\"" { + + yylval.name = buystring(yytext+1); + yylval.name[yyleng-2] = 0; /* Fry final quote */ + return NAME; +} +[0][0-7KM]* { + + yylval.integer = number(yytext+1, 8); + return INT; +} + +[0-9]+[KM]? { + if (hex_mode == true) { + yylval.integer = number(yytext, 16); + } + else { + yylval.integer = number(yytext, 10); + } + return INT; +} + +0[Xx][0-9a-fA-FKM]+ { + + yylval.integer = number(yytext+2,16); + return INT; +} + +"\#"{WHITE}*{FILENAMECHAR}+ { + char *p = yytext+1; + while(*p ==' ' || *p == '\t') p++; + yylval.name = buystring(p); + return NAME; +} + + +{FILENAMECHAR} { + +int ch; + keyword_type *k; + if (yytext[0] == '/' && ldgram_mustbe_symbolname) + { RTOKEN('/');} + ch = input(); + while (true) { + if (isalpha(ch) || isdigit(ch) || ch == '.' || ch == '_') { + yytext[yyleng++] = ch; + } + else if (ch == '-' && ldgram_want_filename == true) { + yytext[yyleng++] = ch; + } + else if (ch == '+' && ldgram_want_filename == true) { + yytext[yyleng++] = ch; + } + + else if (ch == '/' && ldgram_want_filename == true) { + yytext[yyleng++] = ch; + } + + else break; + ch = input(); + } + + yytext[yyleng] = 0; + unput(ch); + + for(k = keywords; k ->name != (char *)NULL; k++) { + + if (strcmp(k->name, yytext)==0) { + yylval.token = k->value; + return k->value; + } + } + yylval.name = buystring(yytext); + return NAME; +} + + + + + +%% diff --git a/ld/ldmain.c b/ld/ldmain.c new file mode 100644 index 0000000..3f9db08 --- /dev/null +++ b/ld/ldmain.c @@ -0,0 +1,806 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * Written by Steve Chamberlain steve@cygnus.com + * + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:52 gumby + * Initial revision + * + * Revision 1.1 1991/03/13 00:48:27 chrisb + * Initial revision + * + * Revision 1.7 1991/03/10 19:15:45 sac + * Fixed a prototype problem + * + * Revision 1.6 1991/03/10 09:31:32 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:31:02 sac + * After a fatal info message, the output file is deleted. + * + * Revision 1.4 1991/03/06 02:28:31 sac + * Fixed partial linking and error messages. + * + * Revision 1.3 1991/02/22 17:15:02 sac + * Added RCS keywords and copyrights + * + * + */ + + +#include "sysdep.h" +#include "bfd.h" + +#include "config.h" +#include "ld.h" +#include "ldmain.h" +#include "ldmisc.h" +#include "ldwrite.h" +#include "ldgram.h" +#include "ldsym.h" +#include "ldlang.h" +#include "ld-emul.h" +#include "ldlex.h" +#include "ldfile.h" + +/* IMPORTS */ +extern boolean lang_has_input_file; + +/* EXPORTS */ + +char *default_target; +char *output_filename = "a.out"; +/* Name this program was invoked by. */ +char *program_name; + +/* The file that we're creating */ +bfd *output_bfd; + +extern boolean option_v; + +/* The local symbol prefix */ +char lprefix = 'L'; + +/* Count the number of global symbols multiply defined. */ +int multiple_def_count; + + +/* Count the number of symbols defined through common declarations. + This count is referenced in symdef_library, linear_library, and + modified by enter_global_ref. + + It is incremented when a symbol is created as a common, and + decremented when the common declaration is overridden + + Another way of thinking of it is that this is a count of + all ldsym_types with a ->scoms field +*/ +unsigned int commons_pending; + + +/* Count the number of global symbols referenced and not defined. + common symbols are not included in this count. + */ + +unsigned int undefined_global_sym_count; + + + +/* Count the number of warning symbols encountered. */ +int warning_count; + +/* have we had a load script ? */ +extern boolean had_script; + + + +/* Nonzero means print names of input files as processed. */ +boolean trace_files; + + + +/* 1 => write load map. */ +boolean write_map; + + +int unix_relocate; + + + + + + + + +/* Force the make_executable to be output, even if there are non-fatal + errors */ +boolean force_make_executable; + + +/* A count of the total number of local symbols ever seen - by adding + the symbol_count field of each newly read afile.*/ + + +unsigned int total_symbols_seen; + +/* A count of the number of read files - the same as the number of elements + in file_chain + */ +unsigned int total_files_seen; + + +/* IMPORTS */ +args_type command_line; +ld_config_type config; +int +main (argc, argv) + char **argv; + int argc; +{ + char *emulation; + program_name = argv[0]; + output_filename = "a.out"; + + emulation = getenv(EMULATION_ENVIRON); + + /* Initialize the data about options. */ + strip_symbols = STRIP_NONE; + trace_files = false; + discard_locals = DISCARD_NONE; + + write_map = false; + config.relocateable_output = false; + unix_relocate = 0; + command_line.force_common_definition = false; + + ldfile_add_arch(""); + + config.make_executable = true; + force_make_executable = false; + + + /* Initialize the cumulative counts of symbols. */ + undefined_global_sym_count = 0; + warning_count = 0; + multiple_def_count = 0; + commons_pending = 0; + + config.magic_demand_paged = true ; + config.make_executable = true; + + if (emulation == (char *)NULL) { + emulation= DEFAULT_EMULATION; + } + ldemul_choose_mode(emulation); + + default_target = ldemul_choose_target(); + + lang_init(); + ldemul_before_parse(); + + lang_has_input_file = false; + parse_args(argc, argv); + + if (lang_has_input_file == false) { + info("%P%F: No input files\n"); + } + + ldemul_after_parse(); + + lang_process(); + + + + + /* Print error messages for any missing symbols, for any warning + symbols, and possibly multiple definitions */ + + /* Print a map, if requested. */ + + if (write_map) { + ldsym_print_symbol_table (); + lang_map(stdout); + } + + + if (config.relocateable_output) { + output_bfd->flags &= ~( D_PAGED); + output_bfd->flags |= EXEC_P; + ldwrite(); + bfd_close(output_bfd); + } + else { + output_bfd->flags |= EXEC_P; + + ldwrite(); + bfd_close(output_bfd); + if (config.make_executable == false && force_make_executable == false) { + unlink(output_filename); + } + return (!config.make_executable); + } + + return(0); +} /* main() */ + + +void +Q_read_entry_symbols (desc, entry) + bfd *desc; + struct lang_input_statement_struct *entry; +{ + if (entry->asymbols == (asymbol **)NULL) { + size_t table_size = get_symtab_upper_bound(desc); + entry->asymbols = (asymbol **)ldmalloc(table_size); + + entry->symbol_count = bfd_canonicalize_symtab(desc, entry->asymbols) ; + } +} + + +/* + * turn this item into a reference + */ +static void +refize(sp, nlist_p) +ldsym_type *sp; +asymbol **nlist_p; +{ + asymbol *sym = *nlist_p; + sym->value = 0; + sym->flags = BSF_UNDEFINED; + sym->section = (asection *)NULL; + sym->udata =(void *)( sp->srefs_chain); + sp->srefs_chain = nlist_p; +} +/* +This function is called for each name which is seen which has a global +scope. It enters the name into the global symbol table in the correct +symbol on the correct chain. Remember that each ldsym_type has three +chains attatched, one of all definitions of a symbol, one of all +references of a symbol and one of all common definitions of a symbol. + +When the function is over, the supplied is left connected to the bfd +to which is was born, with its udata field pointing to the next member +on the chain in which it has been inserted. + +A certain amount of jigery pokery is necessary since commons come +along and upset things, we only keep one item in the common chain; the +one with the biggest size seen sofar. When another common comes along +it either bumps the previous definition into the ref chain, since it +is bigger, or gets turned into a ref on the spot since the one on the +common chain is already bigger. If a real definition comes along then +the common gets bumped off anyway. + +Whilst all this is going on we keep a count of the number of multiple +definitions seen, undefined global symbols and pending commons. +*/ + + +void +Q_enter_global_ref (nlist_p) + asymbol **nlist_p; + +{ + asymbol *sym = *nlist_p; + char *name = sym->name; + ldsym_type *sp = ldsym_get (name); + + flagword this_symbol_flags = sym->flags; + + + ASSERT(sym->udata == 0); + + /* Just place onto correct chain */ + if (flag_is_common(this_symbol_flags)) { + /* If we have a definition of this symbol already then + * this common turns into a reference. Also we only + * ever point to the largest common, so if we + * have a common, but it's bigger that the new symbol + * the turn this into a reference too. + */ + if (sp->sdefs_chain) + { + /* This is a common symbol, but we already have a definition + for it, so just link it into the ref chain as if + it were a reference + */ + refize(sp, nlist_p); + } + else if (sp->scoms_chain) { + /* If we have a previous common, keep only the biggest */ + if ( (*(sp->scoms_chain))->value > sym->value) { + /* other common is bigger, throw this one away */ + refize(sp, nlist_p); + } + else if (sp->scoms_chain != nlist_p) { + /* other common is smaller, throw that away */ + refize(sp, sp->scoms_chain); + sp->scoms_chain = nlist_p; + } + } + else { + /* This is the first time we've seen a common, so + * remember it - if it was undefined before, we know it's defined now + */ + if (sp->srefs_chain) + undefined_global_sym_count--; + + commons_pending++; + sp->scoms_chain = nlist_p; + } + } + + else if (flag_is_defined(this_symbol_flags)) { + /* This is the definition of a symbol, add to def chain */ + if (sp->sdefs_chain && (*(sp->sdefs_chain))->section != sym->section) { + /* Multiple definition */ + asymbol *sy = *(sp->sdefs_chain); + lang_input_statement_type *stat = (lang_input_statement_type *) sy->the_bfd->usrdata; + lang_input_statement_type *stat1 = (lang_input_statement_type *) sym->the_bfd->usrdata; + asymbol ** stat1_symbols = stat1 ? stat1->asymbols: 0; + asymbol ** stat_symbols = stat ? stat->asymbols:0; + + multiple_def_count++; + info("%C: multiple definition of `%T'\n", + sym->the_bfd, + sym->section, + stat1_symbols, + sym->value, + sym); + + info("%C: first seen here\n", + sy->the_bfd, + sy->section, + stat_symbols, + sy->value); + } + else { + sym->udata =(void *)( sp->sdefs_chain); + sp->sdefs_chain = nlist_p; + } + /* A definition overrides a common symbol */ + if (sp->scoms_chain) { + refize(sp, sp->scoms_chain); + sp->scoms_chain = 0; + commons_pending--; + } + else if (sp->srefs_chain) { + /* If previously was undefined, then remember as defined */ + undefined_global_sym_count--; + } + } + else { + if (sp->scoms_chain == (asymbol **)NULL + && sp->srefs_chain == (asymbol **)NULL + && sp->sdefs_chain == (asymbol **)NULL) { + /* And it's the first time we've seen it */ + undefined_global_sym_count++; + + } + + refize(sp, nlist_p); + } + + ASSERT(sp->sdefs_chain == 0 || sp->scoms_chain == 0); + ASSERT(sp->scoms_chain ==0 || (*(sp->scoms_chain))->udata == 0); + + +} + +static void +Q_enter_file_symbols (entry) +lang_input_statement_type *entry; +{ + asymbol **q ; + entry->common_section = + bfd_make_section(entry->the_bfd, "COMMON"); + + ldlang_add_file(entry); + + + if (trace_files || option_v) { + info("%I\n", entry); + } + + total_symbols_seen += entry->symbol_count; + total_files_seen ++; + for (q = entry->asymbols; *q; q++) + { + asymbol *p = *q; + + if (flag_is_undefined_or_global_or_common(p->flags)) + { + + Q_enter_global_ref(q); + } + ASSERT(p->flags != 0); + } +} + + + +/* Searching libraries */ + +struct lang_input_statement_struct *decode_library_subfile (); +void linear_library (), symdef_library (); + +/* Search the library ENTRY, already open on descriptor DESC. + This means deciding which library members to load, + making a chain of `struct lang_input_statement_struct' for those members, + and entering their global symbols in the hash table. */ + +void +search_library (entry) + struct lang_input_statement_struct *entry; +{ + + /* No need to load a library if no undefined symbols */ + if (!undefined_global_sym_count) return; + + if (bfd_has_map(entry->the_bfd)) + symdef_library (entry); + else + linear_library (entry); + +} + + +void +Q_read_file_symbols (entry) +struct lang_input_statement_struct *entry; +{ + if (entry->asymbols == (asymbol **)NULL + &&entry->real == true + && entry->filename != (char *)NULL) + { + ldfile_open_file (entry); + + if (bfd_check_format(entry->the_bfd, bfd_object)) + { + entry->the_bfd->usrdata = (void*)entry; + + + Q_read_entry_symbols (entry->the_bfd, entry); + Q_enter_file_symbols (entry); + } + else if (bfd_check_format(entry->the_bfd, bfd_archive)) + { + entry->the_bfd->usrdata = (void *)entry; + + entry->subfiles = (lang_input_statement_type *)NULL; + search_library (entry); + } + else + { + info("%F%I: malformed input file (not rel or archive) \n", entry); + } + } + +} + + +/* Construct and return a lang_input_statement_struct for a library member. + The library's lang_input_statement_struct is library_entry, + and the library is open on DESC. + SUBFILE_OFFSET is the byte index in the library of this member's header. + We store the length of the member into *LENGTH_LOC. */ + +lang_input_statement_type * +decode_library_subfile (library_entry, subfile_offset) + struct lang_input_statement_struct *library_entry; + bfd *subfile_offset; +{ + register struct lang_input_statement_struct *subentry; + subentry = (struct lang_input_statement_struct *) ldmalloc (sizeof (struct lang_input_statement_struct)); + subentry->filename = subfile_offset -> filename; + subentry->local_sym_name = subfile_offset->filename; + subentry->asymbols = 0; + subentry->the_bfd = subfile_offset; + subentry->subfiles = 0; + subentry->next = 0; + subentry->superfile = library_entry; + subentry->is_archive = false; + subentry->header_read_flag = false; + subentry->just_syms_flag = false; + subentry->loaded = false; + subentry->chain = 0; + + return subentry; +} + +boolean subfile_wanted_p (); +void +clear_syms(entry, offset) +struct lang_input_statement_struct *entry; +file_ptr offset; +{ + carsym *car; + unsigned long indx = bfd_get_next_mapent(entry->the_bfd, + BFD_NO_MORE_SYMBOLS, + &car); + while (indx != BFD_NO_MORE_SYMBOLS) { + if (car->file_offset == offset) { + car->name = 0; + } + indx = bfd_get_next_mapent(entry->the_bfd, indx, &car); + } + +} + +/* Search a library that has a map + */ +void +symdef_library (entry) + struct lang_input_statement_struct *entry; + +{ + register struct lang_input_statement_struct *prev = 0; + + boolean not_finished = true; + + + while (not_finished == true) + { + carsym *exported_library_name; + bfd *prev_archive_member_bfd = 0; + + int idx = bfd_get_next_mapent(entry->the_bfd, + BFD_NO_MORE_SYMBOLS, + &exported_library_name); + + not_finished = false; + + while (idx != BFD_NO_MORE_SYMBOLS && undefined_global_sym_count) + { + + if (exported_library_name->name) + { + + ldsym_type *sp = ldsym_get_soft (exported_library_name->name); + + /* If we find a symbol that appears to be needed, think carefully + about the archive member that the symbol is in. */ + /* So - if it exists, and is referenced somewhere and is + undefined or */ + if (sp && sp->srefs_chain && !sp->sdefs_chain) + { + bfd *archive_member_bfd = bfd_get_elt_at_index(entry->the_bfd, idx); + struct lang_input_statement_struct *archive_member_lang_input_statement_struct; + + if (archive_member_bfd && bfd_check_format(archive_member_bfd, bfd_object)) + { + + /* Don't think carefully about any archive member + more than once in a given pass. */ + if (prev_archive_member_bfd != archive_member_bfd) + { + + prev_archive_member_bfd = archive_member_bfd; + + /* Read the symbol table of the archive member. */ + + if (archive_member_bfd->usrdata != (void *)NULL) { + + archive_member_lang_input_statement_struct =(lang_input_statement_type *) archive_member_bfd->usrdata; + } + else { + + archive_member_lang_input_statement_struct = + decode_library_subfile (entry, archive_member_bfd); + archive_member_bfd->usrdata = (void *) archive_member_lang_input_statement_struct; + + } + + if (archive_member_lang_input_statement_struct == 0) { + info ("%F%I contains invalid archive member %s\n", + entry, + sp->name); + } + + if (archive_member_lang_input_statement_struct->loaded == false) + { + + Q_read_entry_symbols (archive_member_bfd, archive_member_lang_input_statement_struct); + /* Now scan the symbol table and decide whether to load. */ + + + if (subfile_wanted_p (archive_member_lang_input_statement_struct) == true) + + { + /* This member is needed; load it. + Since we are loading something on this pass, + we must make another pass through the symdef data. */ + + not_finished = true; + + Q_enter_file_symbols (archive_member_lang_input_statement_struct); + + if (prev) + prev->chain = archive_member_lang_input_statement_struct; + else + entry->subfiles = archive_member_lang_input_statement_struct; + + + prev = archive_member_lang_input_statement_struct; + + + /* Clear out this member's symbols from the symdef data + so that following passes won't waste time on them. */ + clear_syms(entry, exported_library_name->file_offset); + archive_member_lang_input_statement_struct->loaded = true; + } + } + } + } + } + } + idx = bfd_get_next_mapent(entry->the_bfd, idx, &exported_library_name); + } + } +} + +void +linear_library (entry) +struct lang_input_statement_struct *entry; +{ + boolean more_to_do = true; + register struct lang_input_statement_struct *prev = 0; + + while (more_to_do) { + + bfd * archive = bfd_openr_next_archived_file(entry->the_bfd,0); + + more_to_do = false; + while (archive) { + if (bfd_check_format(archive, bfd_object)) + { + register struct lang_input_statement_struct *subentry; + + subentry = decode_library_subfile (entry, + archive); + + archive->usrdata = (void *) subentry; + if (!subentry) return; + if (subentry->loaded == false) { + Q_read_entry_symbols (archive, subentry); + + if (subfile_wanted_p (subentry) == true) + { + Q_enter_file_symbols (subentry); + + if (prev) + prev->chain = subentry; + else + entry->subfiles = subentry; + prev = subentry; + + more_to_do = true; + subentry->loaded = true; + } + } + } + archive = bfd_openr_next_archived_file(entry->the_bfd,archive); + + } + + } +} + + /* ENTRY is an entry for a library member. + Its symbols have been read into core, but not entered. + Return nonzero if we ought to load this member. */ + +boolean +subfile_wanted_p (entry) +struct lang_input_statement_struct *entry; +{ + asymbol **q; + + for (q = entry->asymbols; *q; q++) + { + asymbol *p = *q; + + /* If the symbol has an interesting definition, we could + potentially want it. */ + + if (p->flags & BSF_FORT_COMM + || p->flags & BSF_GLOBAL) + { + register ldsym_type *sp = ldsym_get_soft (p->name); + + + /* If this symbol has not been hashed, + we can't be looking for it. */ + if (sp != (ldsym_type *)NULL + && sp->sdefs_chain == (asymbol **)NULL) { + if (sp->srefs_chain != (asymbol **)NULL + || sp->scoms_chain != (asymbol **)NULL) + { + /* This is a symbol we are looking for. It is either + not yet defined or common. */ + + if (flag_is_common(p->flags)) + { + /* This libary member has something to + say about this element. We should + remember if its a new size */ + /* Move something from the ref list to the com list */ + if(sp->scoms_chain) { + /* Already a common symbol, maybe update it */ + if (p->value > (*(sp->scoms_chain))->value) { + (*(sp->scoms_chain))->value = p->value; + } + } + else { + /* Take a value from the ref chain + Here we are moving a symbol from the owning bfd + to another bfd. We must set up the + common_section portion of the bfd thing */ + + + + sp->scoms_chain = sp->srefs_chain; + sp->srefs_chain = + (asymbol **)((*(sp->srefs_chain))->udata); + (*(sp->scoms_chain))->udata = (void*)NULL; + + (*( sp->scoms_chain))->flags = BSF_FORT_COMM; + commons_pending++; + undefined_global_sym_count--; + } { + asymbol *com = *(sp->scoms_chain); + if (((lang_input_statement_type *) + (com->the_bfd->usrdata))->common_section == + (asection *)NULL) { + ((lang_input_statement_type *) + (com->the_bfd->usrdata))->common_section = + bfd_make_section(com->the_bfd, "COMMON"); + } + } + ASSERT(p->udata == 0); + } + + else { + if (write_map) + { + info("%I needed due to %s\n",entry, sp->name); + } + return true; + } + } + } + } + } + + return false; +} + + diff --git a/ld/ldmain.h b/ld/ldmain.h new file mode 100644 index 0000000..9f3fa1a --- /dev/null +++ b/ld/ldmain.h @@ -0,0 +1,23 @@ +/* ldmain.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +PROTO(void, Q_enter_global_ref,(asymbol **)); +PROTO(void, Q_read_file_symbols,(struct lang_input_statement_struct *)); + diff --git a/ld/ldmisc.c b/ld/ldmisc.c new file mode 100644 index 0000000..2f73066 --- /dev/null +++ b/ld/ldmisc.c @@ -0,0 +1,303 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:28:55 gumby + * Initial revision + * + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:30 chrisb + * Initial revision + * + * Revision 1.7 1991/03/10 09:31:34 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.6 1991/03/09 03:31:01 sac + * After a fatal info message, the output file is deleted. + * + * Revision 1.5 1991/03/06 21:59:54 sac + * Made %C print function name if available + * + * Revision 1.4 1991/03/06 02:27:45 sac + * Added support for linenumber printing via %C + * + * Revision 1.3 1991/02/22 17:15:03 sac + * Added RCS keywords and copyrights + * + */ + +/* + ldmisc.c + +*/ + +#include "sysdep.h" +#include <varargs.h> +#include "bfd.h" + +#include "ld.h" +#include "ldmisc.h" +#include "ldlang.h" + +/* IMPORTS */ + +extern char *program_name; + +extern FILE *ldlex_input_stack; +extern char *ldfile_input_filename; +extern ld_config_type config; + +void +yyerror(arg) +char *arg; +{ + info("%P%F: %S %s\n",arg); +} + +extern int errno; +extern int sys_nerr; +extern char *sys_errlist[]; + +/* + %F error is fatal + %P print progam name + %S print script file and linenumber + %E current bfd error or errno + %I filename from a lang_input_statement_type + %B filename from a bfd + %T symbol table entry + %X no object output, fail return + %V hex bfd_vma + %C Clever filename:linenumber + % +*/ +void info(va_alist) +va_dcl +{ + char *fmt; + boolean fatal = false; + va_list arg; + va_start(arg); + fmt = va_arg(arg, char *); + while (*fmt) { + while (*fmt != '%' && *fmt != '\0') { + fputc(*fmt, stderr); + fmt++; + } + if (*fmt == '%') { + fmt ++; + switch (*fmt++) { + case 'X': + config.make_executable = false; + break; + case 'V': + fprintf(stderr,"%08lx", va_arg(arg, bfd_vma)); + break; + case 'T': + { + asymbol *symbol = va_arg(arg, asymbol *); + if (symbol) { + asection *section = symbol->section; + if ((symbol->flags & BSF_UNDEFINED) == 0) { + char *section_name = section == (asection *)NULL ? + "absolute" : section->name; + fprintf(stderr,"%s (%s)", symbol->name, section_name); + } + else { + fprintf(stderr,"%s", symbol->name); + } + } + else { + fprintf(stderr,"no symbol"); + } + } + break; + case 'B': + { + bfd *abfd = va_arg(arg, bfd *); + if (abfd->my_archive) { + fprintf(stderr,"%s(%s)", abfd->my_archive->filename, + abfd->filename); + } + else { + fprintf(stderr,"%s", abfd->filename); + + } + } + break; + case 'F': + fatal = true; + break; + case 'P': + fprintf(stderr,"%s", program_name); + break; + case 'E': + /* Replace with the most recent errno explanation */ + + + fprintf(stderr, bfd_errmsg(bfd_error)); + + + break; + case 'I': + { + lang_input_statement_type *i = + va_arg(arg,lang_input_statement_type *); + + fprintf(stderr,"%s", i->local_sym_name); + } + break; + case 'S': + /* Print source script file and line number */ + + if (ldlex_input_stack) { + extern unsigned int lineno; + if (ldfile_input_filename == (char *)NULL) { + fprintf(stderr,"command line"); + } + else { + fprintf(stderr,"%s:%u", ldfile_input_filename, lineno + 1); + } + } + else { + fprintf(stderr,"command line "); + } + break; + case 'C': + { + char *filename; + char *functionname; + unsigned int linenumber; + bfd *abfd = va_arg(arg, bfd *); + asection *section = va_arg(arg, asection *); + asymbol **symbols = va_arg(arg, asymbol **); + bfd_vma offset = va_arg(arg, bfd_vma); + + if (bfd_find_nearest_line(abfd, + section, + symbols, + offset, + &filename, + &functionname, + &linenumber)) + { + if (filename == (char *)NULL) + filename = abfd->filename; + if (functionname != (char *)NULL) + fprintf(stderr,"%s:%u: (%s)", filename, linenumber, functionname); + else if (linenumber != 0) + fprintf(stderr,"%s:%u", filename, linenumber); + else + fprintf(stderr,"%s", filename); + + } + else { + fprintf(stderr,"%s", abfd->filename); + } + } + break; + + case 's': + fprintf(stderr,"%s", va_arg(arg, char *)); + break; + case 'd': + fprintf(stderr,"%d", va_arg(arg, int)); + break; + default: + fprintf(stderr,"%s", va_arg(arg, char *)); + break; + } + } + } + if (fatal == true) { + extern char *output_filename; + if (output_filename) + unlink(output_filename); + exit(1); + } + va_end(arg); +} + + +void +info_assert(file, line) +char *file; +unsigned int line; +{ + info("%F%P internal error %s %d\n", file,line); +} + +/* Return a newly-allocated string + whose contents concatenate those of S1, S2, S3. */ + +char * +concat (s1, s2, s3) + char *s1, *s2, *s3; +{ + size_t len1 = strlen (s1); + size_t len2 = strlen (s2); + size_t len3 = strlen (s3); + char *result = ldmalloc (len1 + len2 + len3 + 1); + + if (len1 != 0) + memcpy(result, s1, len1); + if (len2 != 0) + memcpy(result+len1, s2, len2); + if (len3 != 0) + memcpy(result+len1+len2, s2, len3); + *(result + len1 + len2 + len3) = 0; + + return result; +} + + + +char *ldmalloc (size) +size_t size; +{ + char * result = malloc (size); + + if (result == (char *)NULL && size != 0) + info("%F%P virtual memory exhausted\n"); + + return result; +} + + + +char *buystring(x) +char *x; +{ + size_t l = strlen(x)+1; + char *r = ldmalloc(l); + memcpy(r, x,l); + return r; +} diff --git a/ld/ldmisc.h b/ld/ldmisc.h new file mode 100644 index 0000000..e3463d1 --- /dev/null +++ b/ld/ldmisc.h @@ -0,0 +1,34 @@ +/* ldmisc.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* VARARGS*/ +PROTO(void,info,()); +PROTO(void,info_assert,(char *, unsigned int)); +PROTO(void,yyerror,(char *)); +PROTO(char *,concat,(char *, char *, char *)); +PROTO(char *, ldmalloc,(size_t)); +PROTO(char *,buystring,(char *)); +#define ASSERT(x) \ +{ if (!(x)) info_assert(__FILE__,__LINE__); } + +#define FAIL() \ +{ info_assert(__FILE__,__LINE__); } diff --git a/ld/ldwrite.c b/ld/ldwrite.c new file mode 100644 index 0000000..a4282b3 --- /dev/null +++ b/ld/ldwrite.c @@ -0,0 +1,441 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. + +This file is part of GLD, the Gnu Linker. + +GLD is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GLD 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 GLD; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * $Id$ + * + * $Log$ + * Revision 1.1 1991/03/21 21:29:04 gumby + * Initial revision + * + * Revision 1.2 1991/03/15 18:45:55 rich + * foo + * + * Revision 1.1 1991/03/13 00:48:37 chrisb + * Initial revision + * + * Revision 1.7 1991/03/10 19:15:03 sac + * Took out the abort() which had been put in the wrong place + * Updated the version #. + * + * Revision 1.6 1991/03/10 09:31:41 rich + * Modified Files: + * Makefile config.h ld-emul.c ld-emul.h ld-gld.c ld-gld960.c + * ld-lnk960.c ld.h lddigest.c ldexp.c ldexp.h ldfile.c ldfile.h + * ldgram.y ldinfo.h ldlang.c ldlang.h ldlex.h ldlex.l ldmain.c + * ldmain.h ldmisc.c ldmisc.h ldsym.c ldsym.h ldversion.c + * ldversion.h ldwarn.h ldwrite.c ldwrite.h y.tab.h + * + * As of this round of changes, ld now builds on all hosts of (Intel960) + * interest and copy passes my copy test on big endian hosts again. + * + * Revision 1.5 1991/03/09 03:25:08 sac + * Added support for LONG, SHORT and BYTE keywords in scripts + * + * Revision 1.4 1991/03/06 21:59:34 sac + * Completed G++ support + * + * Revision 1.3 1991/03/06 02:29:52 sac + * Added support for partial linking. + * + * Revision 1.2 1991/02/22 17:15:11 sac + * Added RCS keywords and copyrights + * +*/ + +/* + This module writes out the final image by reading sections from the + input files, relocating them and writing them out + + There are two main paths through this module, one for normal + operation and one for partial linking. + + During normal operation, raw section data is read along with the + associated relocation information, the relocation info applied and + the section data written out on a section by section basis. + + When partially linking, all the relocation records are read to work + out how big the output relocation vector will be. Then raw data is + read, relocated and written section by section. + + Written by Steve Chamberlain steve@cygnus.com + +*/ + + +#include "sysdep.h" +#include "bfd.h" + +#include "ldlang.h" +#include "ld.h" +#include "ldwrite.h" +#include "ldmisc.h" +#include "ldsym.h" +#include "ldgram.tab.h" + + + +char *ldmalloc(); +/* Static vars for do_warnings and subroutines of it */ +int list_unresolved_refs; /* List unresolved refs */ +int list_warning_symbols; /* List warning syms */ +int list_multiple_defs; /* List multiple definitions */ +extern int errno; +extern char *sys_errlist[]; + +extern unsigned int undefined_global_sym_count; + +extern bfd *output_bfd; + +extern struct lang_output_section_statement_struct * create_object_symbols; + +extern char lprefix; + +#ifdef __STDC__ +void lang_for_each_statement(void (*func)()); +#else /* __STDC__ */ +void lang_for_each_statement(); +#endif /* __STDC__ */ + +extern size_t largest_section; +ld_config_type config; + +extern unsigned int global_symbol_count; + +boolean trace_files; + +static void perform_relocation(input_bfd, + input_section, + data, + symbols) +bfd *input_bfd; +asection *input_section; +void *data; +asymbol **symbols; +{ + static asymbol *error_symbol = (asymbol *)NULL; + static unsigned int error_count = 0; +#define MAX_ERRORS_IN_A_ROW 5 + size_t reloc_size = get_reloc_upper_bound(input_bfd, input_section); + + arelent **reloc_vector = (arelent **)ldmalloc(reloc_size); + arelent **parent; + bfd *ob = output_bfd; + asection *os = input_section->output_section; + if (config.relocateable_output == false) ob = (bfd *)NULL; + + if (bfd_canonicalize_reloc(input_bfd, + input_section, + reloc_vector, + symbols) ) + { + for (parent = reloc_vector; *parent; parent++) + { + + bfd_reloc_status_enum_type r= + bfd_perform_relocation(input_bfd, + *parent, + data, + input_section, + ob); + + if (r == bfd_reloc_ok) { + if (ob != (bfd *)NULL) { + /* A parital link, so keep the relocs */ + os->orelocation[os->reloc_count] = *parent; + os->reloc_count++; + } + } + else + { + asymbol *s; + arelent *p = *parent; + + if (ob != (bfd *)NULL) { + /* A parital link, so keep the relocs */ + os->orelocation[os->reloc_count] = *parent; + os->reloc_count++; + } + + if (p->sym_ptr_ptr != (asymbol **)NULL) { + s = *(p->sym_ptr_ptr); + } + else { + s = (asymbol *)NULL; + } + switch (r) + { + case bfd_reloc_undefined: + /* We remember the symbol, and never print more than + a reasonable number of them in a row */ + if (s == error_symbol) { + error_count++; + } + else { + error_count = 0; + error_symbol = s; + } + if (error_count < MAX_ERRORS_IN_A_ROW) { + info("%C: undefined reference to `%T'\n", + input_bfd, + input_section, + symbols, + (*parent)->address, + s); + config.make_executable = false; + } + else if (error_count == MAX_ERRORS_IN_A_ROW) { + info("%C: more undefined references to `%T' follow\n", + input_bfd, + input_section, + symbols, + (*parent)->address, + s); + } + else { + /* Don't print any more */ + } + break; + case bfd_reloc_dangerous: + info("%B: relocation may be wrong `%T'\n", + input_bfd, + s); + break; + case bfd_reloc_outofrange: + info("%B:%s relocation address out of range %T (%x)\n", + input_bfd, + input_section->name, + s, + p->address); + break; + case bfd_reloc_overflow: + info("%B:%s relocation overflow in %T reloc type %d\n", + input_bfd, + input_section->name, + s, + p->howto->type); + break; + default: + info("%F%B: relocation error, symbol `%T'\n", + input_bfd, + s); + break; + } + } + } + } + free((char *)reloc_vector); +} + + + + + + +void *data_area; + +static void +copy_and_relocate(statement) +lang_statement_union_type *statement; +{ + switch (statement->header.type) { + case lang_fill_statement_enum: + { +#if 0 + bfd_byte play_area[SHORT_SIZE]; + unsigned int i; + bfd_putshort(output_bfd, statement->fill_statement.fill, play_area); + /* Write out all entire shorts */ + for (i = 0; + i < statement->fill_statement.size - SHORT_SIZE + 1; + i+= SHORT_SIZE) + { + bfd_set_section_contents(output_bfd, + statement->fill_statement.output_section, + play_area, + statement->data_statement.output_offset +i, + SHORT_SIZE); + + } + + /* Now write any remaining byte */ + if (i < statement->fill_statement.size) + { + bfd_set_section_contents(output_bfd, + statement->fill_statement.output_section, + play_area, + statement->data_statement.output_offset +i, + 1); + + } +#endif + } + break; + case lang_data_statement_enum: + { + bfd_vma value = statement->data_statement.value; + bfd_byte play_area[LONG_SIZE]; + unsigned int size; + switch (statement->data_statement.type) { + case LONG: + bfd_putlong(output_bfd, value, play_area); + size = LONG_SIZE; + break; + case SHORT: + bfd_putshort(output_bfd, value, play_area); + size = SHORT_SIZE; + break; + case BYTE: + bfd_putchar(output_bfd, value, play_area); + size = BYTE_SIZE; + break; + } + + bfd_set_section_contents(output_bfd, + statement->data_statement.output_section, + play_area, + statement->data_statement.output_vma, + size); + + + + + } + break; + case lang_input_section_enum: + { + + asection *i = statement->input_section.section; + asection *output_section = i->output_section; + lang_input_statement_type *ifile = statement->input_section.ifile; + bfd *inbfd = ifile->the_bfd; + if (output_section->flags & SEC_LOAD && i->size != 0) + { + if(bfd_get_section_contents(inbfd, + i, + data_area, + 0L, + i->size) == false) + { + info("%F%B error reading section contents %E\n", + inbfd); + } + perform_relocation (inbfd, i, data_area, ifile->asymbols); + + + if(bfd_set_section_contents(output_bfd, + output_section, + data_area, + (file_ptr)i->output_offset, + i->size) == false) + { + info("%F%B error writing section contents of %E\n", + output_bfd); + } + + } + } + break; + + default: + /* All the other ones fall through */ + ; + + } +} + +void +write_norel() +{ + /* Output the text and data segments, relocating as we go. */ + lang_for_each_statement(copy_and_relocate); +} + + +static void read_relocs(abfd, section, symbols) +bfd *abfd; +asection *section; +asymbol **symbols; +{ + /* Work out the output section ascociated with this input section */ + asection *output_section = section->output_section; + + size_t reloc_size = get_reloc_upper_bound(abfd, section); + arelent **reloc_vector = (arelent **)ldmalloc(reloc_size); + + if (bfd_canonicalize_reloc(abfd, + section, + reloc_vector, + symbols)) { + output_section->reloc_count += section->reloc_count; + } +} + + +static void +write_rel() +{ + /* + Run through each section of each file and work work out the total + number of relocation records which will finally be in each output + section + */ + + LANG_FOR_EACH_INPUT_SECTION + (statement, abfd, section, + (read_relocs(abfd, section, statement->asymbols))); + + + + /* + Now run though all the output sections and allocate the space for + all the relocations + */ + LANG_FOR_EACH_OUTPUT_SECTION + (section, + (section->orelocation = + (arelent **)ldmalloc((size_t)(sizeof(arelent **)* + section->reloc_count)), + section->reloc_count = 0, + section->flags |= SEC_HAS_CONTENTS)); + + + /* + Copy the data, relocating as we go + */ + lang_for_each_statement(copy_and_relocate); +} + +void +ldwrite () +{ + data_area = (void*) ldmalloc(largest_section); + if (config.relocateable_output == true) + { + write_rel(); + } + else + { + write_norel(); + } + free(data_area); + /* Output the symbol table (both globals and locals). */ + ldsym_write (); + +} + diff --git a/ld/ldwrite.h b/ld/ldwrite.h new file mode 100644 index 0000000..2658801 --- /dev/null +++ b/ld/ldwrite.h @@ -0,0 +1,24 @@ +/* ldwrite.h - + + Copyright (C) 1991 Free Software Foundation, Inc. + + This file is part of GLD, the Gnu Linker. + + GLD is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + GLD 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 GLD; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + + +PROTO(void, ldwrite, (void)); |