Browse Source

Import most components (GCC/GAS patches, IDE, tigcc.exe, hsf2RC, ld-tigcc, tprbuilder).

git-svn-id: file:///var/svn/tigccpp/trunk/tigcc/ld-tigcc@9 9552661e-59e3-4036-b4f2-dbe53926924f
kevinkofler 19 years ago
commit
174d5f1c04
90 changed files with 16263 additions and 0 deletions
  1. 340 0
      GPL.txt
  2. 71 0
      Makefile
  3. 5 0
      ToDo.txt
  4. 82 0
      ar/data.h
  5. 61 0
      ar/dump.c
  6. 33 0
      ar/dump.h
  7. 143 0
      ar/export/exp_ar.c
  8. 32 0
      ar/export/exp_ar.h
  9. 33 0
      ar/export/exp_def.h
  10. 179 0
      ar/import/imp_amig.c
  11. 33 0
      ar/import/imp_amig.h
  12. 170 0
      ar/import/imp_coff.c
  13. 33 0
      ar/import/imp_coff.h
  14. 51 0
      ar/import/import.c
  15. 28 0
      ar/import/import.h
  16. 192 0
      ar/main.c
  17. 139 0
      ar/main_opt.inc
  18. 143 0
      ar/manip.c
  19. 40 0
      ar/manip.h
  20. 206 0
      bincode/cutrange.c
  21. 38 0
      bincode/cutrange.h
  22. 32 0
      bincode/emu.h
  23. 38 0
      bincode/fix_emu.c
  24. 29 0
      bincode/fix_emu.h
  25. 825 0
      bincode/fix_m68k.c
  26. 68 0
      bincode/fix_m68k.h
  27. 36 0
      bincode/fix_tios.c
  28. 29 0
      bincode/fix_tios.h
  29. 196 0
      bincode/m68k.h
  30. 32 0
      bincode/tios.h
  31. 279 0
      constmrg.c
  32. 46 0
      constmrg.h
  33. 277 0
      data.h
  34. 249 0
      dump.c
  35. 44 0
      dump.h
  36. 91 0
      export/exp_data.c
  37. 37 0
      export/exp_data.h
  38. 40 0
      export/exp_def.c
  39. 82 0
      export/exp_def.h
  40. 117 0
      export/exp_farg.c
  41. 38 0
      export/exp_farg.h
  42. 174 0
      export/exp_ndll.c
  43. 37 0
      export/exp_ndll.h
  44. 157 0
      export/exp_os.c
  45. 37 0
      export/exp_os.h
  46. 172 0
      export/exp_tios.c
  47. 37 0
      export/exp_tios.h
  48. 634 0
      export/export.c
  49. 45 0
      export/export.h
  50. 43 0
      filestat.h
  51. 95 0
      formats/amigaos.h
  52. 64 0
      formats/ar.h
  53. 148 0
      formats/coff.h
  54. 58 0
      formats/tios.h
  55. 36 0
      formats/tiosupgd.h
  56. 208 0
      generic.h
  57. 712 0
      import/imp_amig.c
  58. 33 0
      import/imp_amig.h
  59. 182 0
      import/imp_ar.c
  60. 37 0
      import/imp_ar.h
  61. 597 0
      import/imp_coff.c
  62. 33 0
      import/imp_coff.h
  63. 48 0
      import/import.c
  64. 28 0
      import/import.h
  65. 896 0
      insert/comprrlc.c
  66. 80 0
      insert/comprrlc.h
  67. 98 0
      insert/ins_def.c
  68. 37 0
      insert/ins_def.h
  69. 28 0
      insert/insert.h
  70. 571 0
      insert/kernel.c
  71. 61 0
      insert/kernel.h
  72. 408 0
      insert/model/list.c
  73. 135 0
      insert/model/list.h
  74. 147 0
      insert/other.c
  75. 34 0
      insert/other.h
  76. 99 0
      int_arb.c
  77. 3 0
      int_def.inc
  78. 41 0
      integers.c
  79. 154 0
      integers.h
  80. 130 0
      intrface.h
  81. 98 0
      lists.h
  82. 406 0
      main.c
  83. 432 0
      main_opt.inc
  84. 64 0
      main_vbs.inc
  85. 1987 0
      manip.c
  86. 224 0
      manip.h
  87. 333 0
      reorder.c
  88. 29 0
      reorder.h
  89. 1369 0
      special.c
  90. 147 0
      special.h

+ 340 - 0
GPL.txt

@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU 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.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), 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 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 show them these terms so they know 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.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  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 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 derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  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 License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+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.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary 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
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 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 Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing 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 for copying, distributing or modifying
+the Program or works based on it.
+
+  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.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. 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 this 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
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. 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
+
+  11. 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.
+
+  12. 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
+
+	    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 the public, 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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) 19yy 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 is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

+ 71 - 0
Makefile

@@ -0,0 +1,71 @@
+ifeq ($(OSTYPE), msys)
+EXEEXT = .exe
+else
+EXEEXT =
+endif
+
+LD_TIGCC = ld-tigcc$(EXEEXT)
+AR_TIGCC = ar-tigcc$(EXEEXT)
+LINK_DLL = link.dll
+
+ifeq ($(OSTYPE),msys)
+ALL_TARGETS = $(LD_TIGCC) $(AR_TIGCC) $(LINK_DLL)
+else
+ALL_TARGETS = $(LD_TIGCC) $(AR_TIGCC)
+endif
+
+CC = gcc
+CFLAGS = -s -Os -fno-exceptions
+WARN_CFLAGS = -W -Wall -Wwrite-strings -Wpointer-arith
+DEFINES = -DCOFF_SUPPORT -DAMIGAOS_SUPPORT -DTIOS_SUPPORT -DFLASH_OS_SUPPORT -DNOSTUB_DLL_SUPPORT -DFARGO_SUPPORT -DDATA_VAR_SUPPORT -DTIOS_FILE_SUPPORT -DTIOS_UPGRADE_FILE_SUPPORT -DCOFF_TIGCC_EXTENSIONS -DAMIGAOS_TIGCC_EXTENSIONS
+EXE_DEFINES = -DENABLE_HELP -DENABLE_STATS -DENABLE_DUMP
+
+RM = rm
+RMFLAGS = -f
+
+COMPILE_C = $(CC) -c $(CFLAGS) $(ARCHFLAGS) $(WARN_CFLAGS) $(DEFINES)
+
+HEADERS = *.h formats/*.h import/*.h export/*.h bincode/*.h insert/*.h insert/model/*.h int_def.inc
+AR_HEADERS = ar/*.h ar/import/*.h ar/export/*.h *.h formats/*.h
+OBJECTS = main.o integers.o int_arb.o manip.o constmrg.o reorder.o import/import.o import/imp_coff.o import/imp_amig.o import/imp_ar.o export/export.o export/exp_def.o export/exp_os.o export/exp_tios.o export/exp_ndll.o export/exp_farg.o export/exp_data.o bincode/fix_m68k.o bincode/fix_tios.o bincode/fix_emu.o bincode/cutrange.o insert/ins_def.o insert/model/list.o insert/kernel.o insert/comprrlc.o insert/other.o special.o dump.o
+AR_OBJECTS = ar/main.o ar/manip.o ar/import/import.o ar/import/imp_coff.o ar/import/imp_amig.o ar/export/exp_ar.o ar/dump.o integers.o
+DLL_OBJECTS = main.do integers.do int_arb.do manip.do constmrg.do reorder.do import/import.do import/imp_coff.do import/imp_amig.do import/imp_ar.do export/export.do export/exp_def.do export/exp_os.do export/exp_tios.do export/exp_ndll.do export/exp_farg.do export/exp_data.do bincode/fix_m68k.do bincode/fix_tios.do bincode/fix_emu.do bincode/cutrange.do insert/ins_def.do insert/model/list.do insert/kernel.do insert/comprrlc.do insert/other.do special.do
+DLL_AR_OBJECTS = ar/main.do ar/manip.do ar/import/import.do ar/import/imp_coff.do ar/import/imp_amig.do ar/export/exp_ar.do
+BACKUPS = *~ format/*~ import/*~ export/*~ bincode/*~ insert/*~ insert/model/*~ ar/*~ ar/import/*~ ar/export/*~
+
+.PHONY: all clean clean_backups
+
+all: $(ALL_TARGETS)
+
+ar/main.o: ar/main.c ar/main_opt.inc $(AR_HEADERS)
+	$(COMPILE_C) $(EXE_DEFINES) $< -o $@
+
+ar/%.o: ar/%.c $(AR_HEADERS)
+	$(COMPILE_C) $(EXE_DEFINES) $< -o $@
+
+ar/%.do: ar/%.c $(AR_HEADERS)
+	$(COMPILE_C) -DTARGET_DLL $< -o $@
+
+main.o: main.c main_opt.inc main_vbs.inc $(HEADERS)
+	$(COMPILE_C) $(EXE_DEFINES) $< -o $@
+
+%.o: %.c $(HEADERS)
+	$(COMPILE_C) $(EXE_DEFINES) $< -o $@
+
+%.do: %.c $(HEADERS)
+	$(COMPILE_C) -DTARGET_DLL $< -o $@
+
+$(LD_TIGCC): $(OBJECTS)
+	$(CC) $(CFLAGS) $(ARCHFLAGS) $(OBJECTS) -o $@
+
+$(AR_TIGCC): $(AR_OBJECTS)
+	$(CC) $(CFLAGS) $(ARCHFLAGS) $(AR_OBJECTS) -o $@
+
+$(LINK_DLL): $(DLL_OBJECTS) $(DLL_AR_OBJECTS)
+	$(CC) -shared --dll $(CFLAGS) $(ARCHFLAGS) $(DLL_OBJECTS) $(DLL_AR_OBJECTS) -o $@
+
+clean:
+	-$(RM) $(RMFLAGS) $(OBJECTS) $(AR_OBJECTS) $(DLL_OBJECTS) $(DLL_AR_OBJECTS) $(ALL_TARGETS)
+
+clean_backups:
+	-$(RM) $(RMFLAGS) $(BACKUPS)

+ 5 - 0
ToDo.txt

@@ -0,0 +1,5 @@
+ TO DO LIST FOR LD-TIGCC
+=========================
+
+* Add debug information dump.
+* Implement absolute symbols.

+ 82 - 0
ar/data.h

@@ -0,0 +1,82 @@
+/* data.h: Definitions for internal data handling
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef DATA_H
+#define DATA_H
+
+#include "../generic.h"
+#include "../lists.h"
+#include "../filestat.h"
+
+#define MAX_SYM_LEN 255
+
+struct ARCHIVE;
+struct OBJECT_FILE;
+struct SYMBOL;
+
+struct SYMBOL_TABLE;
+
+// Complete Archive
+typedef struct ARCHIVE {
+	struct {
+		LIST_HEADER(struct OBJECT_FILE);
+	} ObjectFiles;        // Object files contained in the archive.
+	FILE_STATS FileStats; // Statistics of the file.
+	COUNT SymbolCount;    // Number of symbols in the archive.
+	// Export help
+	SIZE ArFileSize;      // Size of the entire archive.
+	struct SYMBOL_TABLE *SymbolTable; // Symbol table information.
+} ARCHIVE;
+
+// Object File
+typedef struct OBJECT_FILE {
+	LIST_ITEM_HEADER(struct OBJECT_FILE);
+	ARCHIVE *Parent;
+	I1 *Data;             // Pointer to file contents (needs to be freed at the end).
+	SIZE Size;            // Size of file.
+	struct {
+		LIST_HEADER(struct SYMBOL);
+	} Symbols;            // Exported symbols.
+	const char *FileName; // File name.
+	FILE_STATS FileStats; // Statistics of the file.
+	// Export help
+	SIZE ArMemberSize;    // Size of the archive member, including padding.
+	OFFSET ArMemberOffset; // Offset of the member inside the archive file.
+} OBJECT_FILE;
+
+// Exported Symbol in an Object File
+typedef struct SYMBOL {
+	LIST_ITEM_HEADER(struct SYMBOL);
+	OBJECT_FILE *Parent;
+	char Name[MAX_SYM_LEN+1]; // Symbol name.
+	SIZE NameLength;      // Length of the symbol name.
+} SYMBOL;
+
+// Archive Symbol Table in Internal Representation
+typedef struct SYMBOL_TABLE {
+	ARCHIVE *Parent;
+	SIZE Size;            // Size of the symbol table when it is written to the archive.
+	// Export help
+	SIZE ArMemberSize;    // Size of the archive member, including padding.
+	OFFSET ArMemberOffset; // Offset of the member inside the archive file.
+	// Symbol array
+	COUNT SymbolCount;    // Number of symbols.
+	SYMBOL *Symbols VAR_ARRAY; // An array pointing to the symbols in this table.
+} SYMBOL_TABLE;
+
+#endif

+ 61 - 0
ar/dump.c

@@ -0,0 +1,61 @@
+/* dump.c: Routines to dump the internal data to a file
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "dump.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define FixIndent \
+	SIZE NewLen = (Indent ? strlen (Indent) : 0) + 2; \
+	char NextIndent[NewLen+1]; \
+	if (!Indent) \
+		Indent = ""; \
+	strcpy (NextIndent, Indent); \
+	NextIndent [NewLen - 2] = ' '; \
+	NextIndent [NewLen - 1] = ' '; \
+	NextIndent [NewLen] = 0;
+
+void DumpArchive (FILE *File, const char *Indent, const ARCHIVE *Archive)
+{
+	OBJECT_FILE *ObjectFile;
+	
+	FixIndent;
+	
+	for_each (ObjectFile, Archive->ObjectFiles)
+	{
+		if (ObjectFile->FileName)
+			fprintf (File, "%s%s\n", Indent, ObjectFile->FileName);
+		else
+			fprintf (File, "%s(unnamed)\n", Indent);
+		
+		DumpObjectFile (File, NextIndent, ObjectFile);
+	}
+}
+
+void DumpObjectFile (FILE *File, const char *Indent, const OBJECT_FILE *ObjectFile)
+{
+	SYMBOL *Symbol;
+	
+	FixIndent;
+	
+	for_each (Symbol, ObjectFile->Symbols)
+	{
+		fprintf (File, "%s%s\n", Indent, Symbol->Name);
+	}
+}

+ 33 - 0
ar/dump.h

@@ -0,0 +1,33 @@
+/* dump.h: Routines to dump the internal data to a file
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef DUMP_H
+#define DUMP_H
+
+#include "../generic.h"
+#include "data.h"
+
+#include <stdio.h>
+
+// Dump everything.
+void DumpArchive (FILE *File, const char *Indent, const ARCHIVE *Archive);
+
+// Dump a single object file.
+void DumpObjectFile (FILE *File, const char *Indent, const OBJECT_FILE *ObjectFile);
+
+#endif

+ 143 - 0
ar/export/exp_ar.c

@@ -0,0 +1,143 @@
+/* exp_ar.c: Routines to export an archive file
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "exp_ar.h"
+
+#include "../../formats/ar.h"
+
+#include <string.h>
+
+// Export the internal data structures into an archive file.
+BOOLEAN ExportArchiveFile (const ARCHIVE *Archive, EXP_FILE *File, BOOLEAN NoNames)
+{
+	// Call this instead of sprintf to overwrite the terminating null character with a space.
+#define iprintf(str,fmt...) (str [sprintf (str, fmt)] = ' ')
+	
+	SYMBOL_TABLE *SymbolTable = Archive->SymbolTable;
+	
+	// Write the file header.
+	ExportWrite (File, AR_FILE_HEADER_STRING, AR_FILE_HEADER_SIZE, 1);
+	
+	// Write the symbol table.
+	if (SymbolTable)
+	{
+		AR_MEMBER_HEADER MemberHeader;
+		
+		// Fill the entire header with spaces.
+		memset (&MemberHeader, ' ', sizeof (MemberHeader));
+		
+		MemberHeader.Name [0] = '/';
+		
+		// Insert the attributes
+		iprintf (MemberHeader.Date, "%lu", (long) (StatGetModificationTime (Archive->FileStats)));
+		iprintf (MemberHeader.UID, "%lu", (long) (StatGetUID (Archive->FileStats)));
+		iprintf (MemberHeader.GID, "%lu", (long) (StatGetGID (Archive->FileStats)));
+		iprintf (MemberHeader.Mode, "%lo", (long) (StatGetMode (Archive->FileStats)));
+		iprintf (MemberHeader.Size, "%lu", (long) (SymbolTable->Size));
+		memcpy (MemberHeader.Magic, AR_MEMBER_MAGIC, AR_MEMBER_MAGIC_SIZE);
+		
+		// Write the header.
+		ExportWrite (File, &MemberHeader, AR_MEMBER_HEADER_SIZE, 1);
+		
+		// Write the data.
+		{
+			OFFSET CurSymbol;
+			
+			ExportWriteI4 (File, SymbolTable->SymbolCount);
+			
+			for (CurSymbol = 0; CurSymbol < SymbolTable->SymbolCount; CurSymbol++)
+			{
+				SYMBOL *Symbol = SymbolTable->Symbols [CurSymbol];
+				if (Symbol)
+					ExportWriteI4 (File, Symbol->Parent->ArMemberOffset);
+			}
+			
+			for (CurSymbol = 0; CurSymbol < SymbolTable->SymbolCount; CurSymbol++)
+			{
+				SYMBOL *Symbol = SymbolTable->Symbols [CurSymbol];
+				if (Symbol)
+					ExportWrite (File, Symbol->Name, strlen (Symbol->Name) + 1, 1);
+			}
+		}
+		
+		// Write the padding.
+		ExportFill (File, SymbolTable->ArMemberOffset + SymbolTable->ArMemberSize, AR_MEMBER_PADDING_CHAR);
+	}
+	
+	// Write the members.
+	{
+		OBJECT_FILE *ObjectFile;
+		OFFSET CurObjectFile = 1;
+		
+		for_each (ObjectFile, Archive->ObjectFiles)
+		{
+			AR_MEMBER_HEADER MemberHeader;
+			
+			// Fill the entire header with spaces.
+			memset (&MemberHeader, ' ', sizeof (MemberHeader));
+			
+			if (NoNames)
+				// Use an arbitrary name.
+				iprintf (MemberHeader.Name, "fl%lu.o/", (long) CurObjectFile);
+			else
+			{
+				SIZE NameLen;
+				
+				// Seach for the last slash.
+				const char *Name = strrchr (ObjectFile->FileName, '/');
+				// If none is found, use the last backslash instead.
+				if (!Name)
+					Name = strrchr (ObjectFile->FileName, '\\');
+				
+				// Use only the file name without a path.
+				if (Name)
+					Name++;
+				else
+					Name = ObjectFile->FileName;
+				
+				// Insert the name into the header.
+				NameLen = strlen (Name);
+				if (NameLen > AR_MEMBER_MAX_NAME_LENGTH)
+					NameLen = AR_MEMBER_MAX_NAME_LENGTH;
+				memcpy (MemberHeader.Name, Name, NameLen);
+				MemberHeader.Name [NameLen] = '/';
+			}
+			
+			// Insert the attributes
+			iprintf (MemberHeader.Date, "%lu", (long) (StatGetModificationTime (ObjectFile->FileStats)));
+			iprintf (MemberHeader.UID, "%lu", (long) (StatGetUID (ObjectFile->FileStats)));
+			iprintf (MemberHeader.GID, "%lu", (long) (StatGetGID (ObjectFile->FileStats)));
+			iprintf (MemberHeader.Mode, "%lo", (long) (StatGetMode (ObjectFile->FileStats)));
+			iprintf (MemberHeader.Size, "%lu", (long) (ObjectFile->Size));
+			memcpy (MemberHeader.Magic, AR_MEMBER_MAGIC, AR_MEMBER_MAGIC_SIZE);
+			
+			// Write the header.
+			ExportWrite (File, &MemberHeader, AR_MEMBER_HEADER_SIZE, 1);
+			
+			// Write the data.
+			ExportWrite (File, ObjectFile->Data, 1, ObjectFile->Size);
+			
+			// Write the padding.
+			ExportFill (File, ObjectFile->ArMemberOffset + ObjectFile->ArMemberSize, AR_MEMBER_PADDING_CHAR);
+			
+			CurObjectFile++;
+		}
+	}
+	
+	return TRUE;
+}

+ 32 - 0
ar/export/exp_ar.h

@@ -0,0 +1,32 @@
+/* exp_ar.h: Routines to export an archive file
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef EXP_AR_H
+#define EXP_AR_H
+
+#include "../../generic.h"
+#include "exp_def.h"
+#include "../data.h"
+
+// Get the file size needed to export the program into a TIOS file.
+// Returns 0 on failure (e.g. too large).
+#define GetArchiveFileSize(Archive) ((Archive)->ArFileSize)
+// Export the internal data structures into an archive file.
+BOOLEAN ExportArchiveFile (const ARCHIVE *Archive, EXP_FILE *File, BOOLEAN NoNames);
+
+#endif

+ 33 - 0
ar/export/exp_def.h

@@ -0,0 +1,33 @@
+/* export.h: Definitions for file exports
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef EXPORT_H
+#define EXPORT_H
+
+#include "../../generic.h"
+#include "../../integers.h"
+
+#include <stdio.h>
+
+#define EXP_FILE FILE
+#define ExportWrite(F,P,L,C) (fwrite (P, L, C, F))
+#define ExportSeek(F,P) (fseek (F, P, SEEK_SET))
+#define ExportFill(F,P,C) ({ I1 __C = (C); while (ftell (F) < (P)) fwrite (&__C, 1, 1, F); })
+#define ExportWriteI4(F,D) ({ TI4 __D; WriteI4 (__D, D); ExportWrite (F, &__D, 4, 1); })
+
+#endif

+ 179 - 0
ar/import/imp_amig.c

@@ -0,0 +1,179 @@
+/* imp_amiga.c: Routines to import an AmigaOS-hunks file
+
+   Copyright (C) 2003 Kevin Kofler and Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "imp_amig.h"
+
+#ifdef AMIGAOS_SUPPORT
+
+#include "../../formats/amigaos.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Import the exported symbols of an AmigaOS-hunks file.
+BOOLEAN ArImportAmigaOSFile (OBJECT_FILE *ObjectFile)
+{
+	// Call this for a nice and clean failing exit.
+#define Fail() ({return FALSE;})
+#define FailWithError(ErrorMsg...) ({Error (FileName, ErrorMsg); Fail ();})
+#define TestMem(Ptr) ({ if (!(Ptr)) {FailWithError ("Out of memory.");} })
+
+	// Check if a given object with a given type is completely inside the file.
+#define IsInFile(Ptr,Type) ((Ptr) >= File && (Ptr) + sizeof (Type) <= File + FileSize)
+#define TestInFile(Ptr,Type) ({ if (!(IsInFile (Ptr, Type))) {FailWithError ("Corrupt A68k object file.");} })
+
+	// Get next big-endian longint. We will need this in many places here, since
+	// unlike COFF, AmigaOS-hunks is a sequential format, not a pointer-based one.
+#define GetNextTI4(Ptr) ({register TI4 __TI4;TestInFile(Ptr,TI4);__TI4=(*((TI4*)Ptr));Ptr+=4;ReadTI4(__TI4);})
+
+	// Local Variables
+	const I1 *File = ObjectFile->Data;
+	SIZE FileSize = ObjectFile->Size;
+	const char *FileName = ObjectFile->FileName;
+	const I1 *ptr = File;
+	I4 hunkSize;
+
+	if (!File)
+		return FALSE;
+	
+	// Read unit hunk
+	if (GetNextTI4(ptr) != AMIGAOS_HUNK_UNIT)
+		// This should already be trapped by IsAmigaOSFile, but just to make sure.
+		FailWithError("Corrupt A68k object file (unit hunk missing).");
+	hunkSize = GetNextTI4(ptr)<<2;
+	TestInFile(ptr,I1[hunkSize]);
+	ptr += hunkSize;
+
+	while (ptr < (File + FileSize)) {
+		I4 hunkType = GetNextTI4(ptr) & AMIGAOS_HUNK_TYPE_MASK;
+
+		switch (hunkType) {
+			case AMIGAOS_HUNK_UNIT:
+				FailWithError("Corrupt A68k object file (duplicate unit hunk).");
+				break;
+
+			case AMIGAOS_HUNK_NAME:
+			case AMIGAOS_HUNK_CODE:
+			case AMIGAOS_HUNK_DATA:
+				hunkSize = GetNextTI4(ptr)<<2;
+				ptr += hunkSize;
+				break;
+
+			case AMIGAOS_HUNK_BSS:
+				ptr += 4;
+				break;
+
+			case AMIGAOS_HUNK_RELOC_ABS2:
+			case AMIGAOS_HUNK_RELOC_ABS4:
+			case AMIGAOS_HUNK_RELOC_REL1:
+			case AMIGAOS_HUNK_RELOC_REL2:
+			case AMIGAOS_HUNK_RELOC_REL4:
+				hunkSize = GetNextTI4(ptr)<<2;
+				while (hunkSize) {
+					ptr += hunkSize+4;
+					hunkSize = GetNextTI4(ptr)<<2;
+				}
+				break;
+
+#ifdef AMIGAOS_TIGCC_EXTENSIONS
+			case AMIGAOS_HUNK_RELOC_ABS4_POSNEG:
+			case AMIGAOS_HUNK_RELOC_ABS2_POSNEG:
+			case AMIGAOS_HUNK_RELOC_ABS1_POSNEG:
+				hunkSize = GetNextTI4(ptr)<<4;
+				ptr += hunkSize;
+				break;
+#endif
+
+			case AMIGAOS_HUNK_EXT:
+				hunkSize = GetNextTI4(ptr);
+				while (hunkSize) {
+					// The most significant byte of the size longword encodes the symbol type.
+					hunkType = hunkSize>>24;
+					hunkSize = (hunkSize&0xffffffL)<<2;
+
+					TestInFile(ptr,I1[hunkSize]);
+					
+					{
+						char symName[hunkSize+1];
+						strncpy(symName,ptr,hunkSize);
+						symName[hunkSize] = 0;
+						ptr += hunkSize;
+
+						switch (hunkType) {
+							case AMIGAOS_EXT_ABS:
+							case AMIGAOS_EXT_DEF:
+								ptr += 4;
+								{
+									SYMBOL *Symbol = calloc(1,sizeof(SYMBOL));
+									TestMem(Symbol);
+									Symbol->Parent = ObjectFile;
+									strncpy(Symbol->Name,symName,MAX_SYM_LEN);
+									Symbol->NameLength = strlen (Symbol->Name);
+									Append(ObjectFile->Symbols,Symbol);
+									ObjectFile->Parent->SymbolCount++;
+								}
+								break;
+
+							case AMIGAOS_EXT_REF_ABS1:
+							case AMIGAOS_EXT_REF_ABS2:
+							case AMIGAOS_EXT_REF_ABS4:
+							case AMIGAOS_EXT_REF_REL1:
+							case AMIGAOS_EXT_REF_REL2:
+							case AMIGAOS_EXT_REF_REL4:
+								hunkSize = GetNextTI4(ptr)<<2;
+								ptr += hunkSize;
+								break;
+
+							default:
+								FailWithError("Unsupported AmigaOS symbol type `0x%lX'.",hunkType);
+								break;
+						}
+					}
+
+					hunkSize = GetNextTI4(ptr);
+				}
+				break;
+
+			case AMIGAOS_HUNK_SYMBOL:
+				hunkSize = GetNextTI4(ptr)<<2;
+				while (hunkSize) {
+					ptr += hunkSize+4;
+					hunkSize = GetNextTI4(ptr)<<2;
+				}
+				break;
+
+			case AMIGAOS_HUNK_END:
+				break;
+
+			default:
+				FailWithError("Unsupported AmigaOS hunk type `0x%lX'.",hunkType);
+				break;
+		}
+	}
+
+	return TRUE; // success
+
+#undef GetNextTI4
+#undef TestInFile
+#undef IsInFile
+#undef TestMem
+#undef FailWithError
+#undef Fail
+}
+
+#endif /* AMIGAOS_SUPPORT */

+ 33 - 0
ar/import/imp_amig.h

@@ -0,0 +1,33 @@
+/* imp_amig.h: Routines to import an AmigaOS-hunks file
+
+   Copyright (C) 2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef IMP_AMIG_H
+#define IMP_AMIG_H
+
+#include "../../generic.h"
+
+#ifdef AMIGAOS_SUPPORT
+
+#include "../data.h"
+
+// Import the exported symbols of an AmigaOS-hunks file.
+BOOLEAN ArImportAmigaOSFile (OBJECT_FILE *ObjectFile);
+
+#endif /* AMIGAOS_SUPPORT */
+
+#endif

+ 170 - 0
ar/import/imp_coff.c

@@ -0,0 +1,170 @@
+/* imp_coff.c: Routines to import a COFF file
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "imp_coff.h"
+
+#ifdef COFF_SUPPORT
+
+#include "../../formats/coff.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Import the exported symbols of a COFF file.
+BOOLEAN ArImportCOFFFile (OBJECT_FILE *ObjectFile)
+{
+	// Call this for a nice and clean failing exit.
+#define Fail() ({ return FALSE; })
+#define TestMem(Ptr) ({ if (!(Ptr)) { Error (FileName, "Out of memory."); Fail (); } })
+
+	// Check if a given object with a given type is completely inside the file.
+#define IsInFile(Ptr,Type) ((((const I1 *) (Ptr)) >= File) && (((const I1 *) (Ptr)) + sizeof (Type) <= File + FileSize))
+#define TestInFile(Ptr,Type) ({ if (!(IsInFile (Ptr, Type))) { Error (FileName, "Corrupt COFF object file."); Fail (); } })
+
+	// Local Variables
+	const I1 *File = ObjectFile->Data;
+	SIZE FileSize = ObjectFile->Size;
+	const char *FileName = ObjectFile->FileName;
+	COFF_INFO FileInfo;
+	const COFF_HEADER *COFFHeader = (const COFF_HEADER *) File;
+	const COFF_SECTIONS *COFFSections;
+	const COFF_SYMBOLS *COFFSymbols;
+	const char *COFFStringTable;
+	OFFSET CurCOFFSymbolNumber;
+	
+	if (!File)
+		return FALSE;
+	
+	// Create file information in FileInfo.
+	CreateCoffInfo (*COFFHeader, FileInfo);
+	// Get pointer to list of sections.
+	COFFSections = (const COFF_SECTIONS *) (File + FileInfo.PSections);
+	// Get pointer to list of symbols.
+	COFFSymbols = (const COFF_SYMBOLS *) (File + FileInfo.PSymbols);
+	// Get pointer to string table.
+	COFFStringTable = (const char *) (File + FileInfo.PStrings);
+	
+	// For each symbol...
+	for (CurCOFFSymbolNumber = 0; CurCOFFSymbolNumber < FileInfo.SymbolCount; CurCOFFSymbolNumber++)
+	{
+		// Get pointer to this symbol.
+		const COFF_SYMBOL *CurCOFFSymbol = &((*COFFSymbols) [CurCOFFSymbolNumber]);
+		TestInFile (CurCOFFSymbol, COFF_SYMBOL);
+		
+		{
+			// Check whether the symbol is exported.
+			if (ReadTI1 (CurCOFFSymbol->Class) == COFF_SYMBOL_EXTERNAL)
+			{
+				// Of course, a single attribute for exported symbols would
+				// be too easy. In COFF, finding out whether it is exported
+				// is a little complicated and dirty: If the symbol is
+				// "external" but lives in a specific section, this means
+				// that it is exported. If it does not live in a section, but
+				// still has a value, it is a common symbol; I think we can
+				// look at "external" common symbols as exported, too, even
+				// though multiple files can export them at once. If it does
+				// not have a section or value, it is either an imported
+				// symbol (which should be ignored) or an exported symbol
+				// which is declared but never defined (and since this is a
+				// convenient way of passing information to the linker
+				// without cluttering the source code, we should not ignore
+				// this kind of symbol). The only way to distinguish these
+				// two kinds of symbols seems to be to look for a reloc using
+				// the symbol. If a reloc exists, it is definitely an
+				// imported symbol. If none exists, it is hopefully an
+				// exported symbol.
+				
+				BOOLEAN Exported = TRUE;
+				
+				if ((IsZeroI2 (CurCOFFSymbol->Section)) && (IsZeroI4 (CurCOFFSymbol->Value)))
+				{
+					OFFSET CurCOFFSectionNumber;
+					
+					// For each section...
+					for (CurCOFFSectionNumber = 0; (CurCOFFSectionNumber < FileInfo.SectionCount) && Exported; CurCOFFSectionNumber++)
+					{
+						// Get pointer to this section.
+						const COFF_SECTION *CurCOFFSection = &((*COFFSections) [CurCOFFSectionNumber]);
+						TestInFile (CurCOFFSection, COFF_SECTION);
+						
+						{
+							// Get info about relocs.
+							COUNT RelocCount = ReadTI2 (CurCOFFSection->RelocCount);
+							const COFF_RELOCS *CurCOFFRelocs = (const COFF_RELOCS *) (File + ReadTI4 (CurCOFFSection->PRelocs));
+							OFFSET CurCOFFRelocNumber;
+							
+							// For each reloc...
+							for (CurCOFFRelocNumber = 0; CurCOFFRelocNumber < RelocCount; CurCOFFRelocNumber++)
+							{
+								// Get pointer to this reloc.
+								const COFF_RELOC *CurCOFFReloc = &((*CurCOFFRelocs) [CurCOFFRelocNumber]);
+								TestInFile (CurCOFFReloc, COFF_RELOC);
+								
+								// Test if the reloc points to the current symbol.
+								if ((OFFSET) (ReadTI4 (CurCOFFReloc->Symbol)) == CurCOFFSymbolNumber)
+								{
+									// It did. So the symbol is imported, not exported.
+									Exported = FALSE;
+									break;
+								}
+							}
+						}
+					}
+				}
+				
+				// Only add symbols which are really exported.
+				if (Exported)
+				{
+					// Create a new symbol inside the archive.
+					SYMBOL *Symbol = calloc (1, sizeof (SYMBOL));
+					TestMem (Symbol);
+					
+					Symbol->Parent = ObjectFile;
+					
+					// COFF symbol names are stored in a strange way. This is how to get them.
+					if (IsZero (CurCOFFSymbol->Name.StringRef.Zero))
+					{
+						const char *Str = COFFStringTable + ReadTI4 (CurCOFFSymbol->Name.StringRef.StringOffset);
+						TestInFile (Str, char);
+						strncpy (Symbol->Name, Str, MAX_SYM_LEN);
+					}
+					else
+						strncpy (Symbol->Name, CurCOFFSymbol->Name.Name, 8);
+					Symbol->NameLength = strlen (Symbol->Name);
+					
+					// Add the symbol to the linked list.
+					Append (ObjectFile->Symbols, Symbol);
+					ObjectFile->Parent->SymbolCount++;
+				}
+			}
+		}
+		
+		// Skip corresponding auxiliary symbols.
+		CurCOFFSymbolNumber += ReadTI1 (CurCOFFSymbol->AuxSymbolCount);
+	}
+	
+	// Exit with a positive result.
+	return TRUE;
+	
+#undef TestInFile
+#undef IsInFile
+#undef TestMem
+#undef Fail
+}
+
+#endif /* COFF_SUPPORT */

+ 33 - 0
ar/import/imp_coff.h

@@ -0,0 +1,33 @@
+/* imp_coff.h: Routines to import a COFF file
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef IMP_COFF_H
+#define IMP_COFF_H
+
+#include "../../generic.h"
+
+#ifdef COFF_SUPPORT
+
+#include "../data.h"
+
+// Import the exported symbols of a COFF file.
+BOOLEAN ArImportCOFFFile (OBJECT_FILE *ObjectFile);
+
+#endif /* COFF_SUPPORT */
+
+#endif

+ 51 - 0
ar/import/import.c

@@ -0,0 +1,51 @@
+/* import.c: Routines for object file imports
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "import.h"
+
+#ifdef COFF_SUPPORT
+#include "../../formats/coff.h"
+#include "imp_coff.h"
+#endif /* COFF_SUPPORT */
+
+#ifdef AMIGAOS_SUPPORT
+#include "../../formats/amigaos.h"
+#include "imp_amig.h"
+#endif /* AMIGAOS_SUPPORT */
+
+// Import an object file with an arbitrary format.
+BOOLEAN ArImportObjectFile (OBJECT_FILE *ObjectFile)
+{
+	if (!(ObjectFile->Data))
+		return FALSE;
+	
+#ifdef COFF_SUPPORT
+	if (IsCOFFFile (ObjectFile->Data, ObjectFile->Size))
+		return (ArImportCOFFFile (ObjectFile));
+	else
+#endif /* COFF_SUPPORT */
+#ifdef AMIGAOS_SUPPORT
+	if (IsAmigaOSFile (ObjectFile->Data, ObjectFile->Size))
+		return (ArImportAmigaOSFile (ObjectFile));
+	else
+#endif /* AMIGAOS_SUPPORT */
+	{
+		Warning (ObjectFile->FileName, "Unknown object file format.");
+		return FALSE;
+	}
+}

+ 28 - 0
ar/import/import.h

@@ -0,0 +1,28 @@
+/* import.h: Routines for object file imports
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef IMPORT_H
+#define IMPORT_H
+
+#include "../../generic.h"
+#include "../data.h"
+
+// Import an object file with an arbitrary format.
+BOOLEAN ArImportObjectFile (OBJECT_FILE *ObjectFile);
+
+#endif

+ 192 - 0
ar/main.c

@@ -0,0 +1,192 @@
+/* main.c: Main entry point for ar-tigcc, handling the command line input
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "../generic.h"
+#include "../intrface.h"
+#include "data.h"
+#include "manip.h"
+#include "import/import.h"
+#include "export/exp_ar.h"
+
+#ifdef ENABLE_DUMP
+#include "dump.h"
+#endif /* ENABLE_DUMP */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef __WIN32__
+// This definition seems to be missing in MinGW.
+extern void *alloca (size_t);
+#endif /* __WIN32__ */
+
+#define RESULT_OK             0
+#define RESULT_GENERAL_ERROR  1
+#define RESULT_EXPORT_ERROR   2
+#define RESULT_STRANGE_ERROR  3
+
+#ifdef TARGET_EMBEDDED
+extern ERROR_FUNCTION ErrorFunction;
+EXP_CREATE_ARCHIVE ()
+{
+	const char **CurFile;
+#else /* !TARGET_EMBEDDED */
+int main (int ArgCount, const char **Args)
+{
+	int CurArg;
+	const char *DestFile = NULL;
+	BOOLEAN NoNames = FALSE;
+#endif /* !TARGET_EMBEDDED */
+	
+	int Result = RESULT_GENERAL_ERROR;
+#ifdef ENABLE_DUMP
+	BOOLEAN Dump = FALSE;
+#endif /* ENABLE_DUMP */
+	
+	ARCHIVE Archive;
+	
+	// Check the sizes of basic integer types.
+	if (sizeof (I1) != 1 || sizeof (I2) != 2 || sizeof (I4) != 4 || sizeof (SI1) != 1 || sizeof (SI2) != 2 || sizeof (SI4) != 4 || sizeof (OFFSET) < sizeof (SI4))
+	{
+		Error (NULL, "Generic type size error!");
+		return RESULT_STRANGE_ERROR;
+	}
+	
+	// Initialize.
+	memset (&Archive, 0, sizeof (Archive));
+#ifdef TARGET_EMBEDDED
+	ErrorFunction = ErrorMessage;
+#endif /* TARGET_EMBEDDED */
+	
+#ifdef TARGET_EMBEDDED
+	for (CurFile = ObjectFiles; *CurFile; CurFile++)
+	{
+		FILE *File = fopen (*CurFile, "rb");
+		if (File)
+		{
+			SIZE Size;
+			fseek (File, 0, SEEK_END);
+			Size = ftell (File);
+			rewind (File);
+			{
+				I1 *Data = malloc (Size);
+				if (Data)
+				{
+					if (fread (Data, Size, 1, File) == 1)
+					{
+						// Create a new object file inside the current archive.
+						OBJECT_FILE *ObjectFile = calloc (1, sizeof (OBJECT_FILE));
+						
+						if (!ObjectFile)
+						{
+							Error (*CurFile, "Out of memory.");
+							break;
+						}
+						
+						ObjectFile->Parent = &Archive;
+						ObjectFile->Data = Data;
+						ObjectFile->Size = Size;
+						ObjectFile->FileName = *CurFile;
+						GetFileStats (*CurFile, ObjectFile->FileStats);
+						Append (Archive.ObjectFiles, ObjectFile);
+						
+						// Update the statistics of the archive accordingly.
+						if (StatFileIsNewer (ObjectFile->FileStats, Archive.FileStats))
+							StatCopyAttributes (Archive.FileStats, ObjectFile->FileStats);
+						
+						// Try to import the object file's contents.
+						ArImportObjectFile (ObjectFile);
+					}
+					else
+						Error (*CurFile, "Unable to read file.");
+				}
+				else
+					Error (*CurFile, "Not enough memory to load file.");
+			}
+			fclose (File);
+		}
+		else
+			Error (*CurFile, "Unable to open file.");
+	}
+#else /* !TARGET_EMBEDDED */
+#include "main_opt.inc"
+#endif /* !TARGET_EMBEDDED */
+	
+	{
+		OBJECT_FILE *FirstFile = GetFirst (Archive.ObjectFiles);
+		
+		if (FirstFile)
+		{
+			// Set the destination file, in case it has not been set yet.
+			if (!DestFile)
+			{
+				// alloca should not fail in any case, so we don't generate an error message.
+				DestFile = alloca (strlen (FirstFile->FileName) + 2 + 1);
+				if (DestFile)
+				{
+					strcpy ((char *) DestFile, FirstFile->FileName);
+					strcat ((char *) DestFile, ".a");
+				}
+			}
+			
+#ifdef ENABLE_DUMP
+			if (Dump)
+				DumpArchive (stdout, NULL, &Archive);
+#endif /* ENABLE_DUMP */
+			
+			// Create the symbol table information.
+			CreateSymbolTable (&Archive);
+			
+			// Create other necessary information.
+			FillExportHelpFields (&Archive);
+			
+			if (DestFile && (GetArchiveFileSize (&Archive) > 0))
+			{
+				// Write the archive.
+				FILE *File = fopen (DestFile, "wb");
+				if (File)
+				{
+					BOOLEAN ThisResult;
+					if ((ThisResult = ExportArchiveFile (&Archive, File, NoNames)))
+					{
+						if (Result == RESULT_GENERAL_ERROR)
+							Result = RESULT_OK;
+					}
+					else
+						Result = RESULT_EXPORT_ERROR;
+					fclose (File);
+					if (!ThisResult)
+						remove (DestFile);
+				}
+				else
+					Error (DestFile, "Unable to create file.");
+			}
+			else
+				Result = RESULT_EXPORT_ERROR;
+		}
+		else
+			Error (DestFile, "Cannot create empty archive.");
+	}
+		
+Cleanup: ATTRIBUTE_UNUSED
+	// Final Cleanup.
+	FreeArchive (&Archive);
+	
+	return Result;
+}

+ 139 - 0
ar/main_opt.inc

@@ -0,0 +1,139 @@
+/* main_opt.inc: Option parser for ld-tigcc command-line program
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+// This file is included by main.c.
+{
+	const char *Arg;
+	
+	BOOLEAN ArgMatches (const char *Name)
+	{
+		return (!(strcmp (Arg, Name)));
+	}
+	
+	// For each argument...
+	for (CurArg = 1; CurArg < ArgCount; CurArg++)
+	{
+		// Get pointer to arg.
+		Arg = Args [CurArg];
+		// Check whether it is empty.
+		if (Arg && *Arg)
+		{
+			// Check whether it is an option.
+			if (Arg [0] == '-')
+			{
+				Arg++;
+				if (Arg [0] == '-')
+					Arg++;
+				
+				// Handle the option.
+#ifdef ENABLE_HELP
+				if (ArgMatches ("version"))
+				{
+					printf ("ar-tigcc Version " PROGRAM_VERSION_STRING "\n"
+					        COPYRIGHT_NOTICE_STRING "\n"
+					        "ar-tigcc is free software; see the source code for details.\n");
+					goto Cleanup;
+				}
+				else if (ArgMatches ("help") || ArgMatches ("-h"))
+				{
+					printf ("Usage: ar-tigcc [options] <file> [<file> ...]\n"
+					        "Options:\n"
+					        "  -h --help           Display this message\n"
+					        "     --version        Display version number\n"
+#ifdef ENABLE_DUMP
+					        "     --dump           Display small dump of the archive contents\n"
+#endif /* ENABLE_DUMP */
+					        "  -o --output <file>  Write output to <file>\n"
+					        "  -rc/-qc <file>      Same as `-o' for compatibility with GNU ar\n"
+					        "     --no-names       Do not include object file names in archive\n");
+					goto Cleanup;
+				}
+				else
+#endif /* ENABLE_HELP */
+#ifdef ENABLE_DUMP
+				if (ArgMatches ("dump"))
+					Dump = TRUE;
+				else
+#endif /* ENABLE_DUMP */
+				if (ArgMatches ("no-names"))
+					NoNames = TRUE;
+				else if (ArgMatches ("o") || ArgMatches ("output") || ArgMatches ("rc") || ArgMatches ("-qc"))
+				{
+					if ((++CurArg) < ArgCount)
+						DestFile = Args [CurArg];
+					else
+						Error (NULL, "`--output' option must be followed by a name.");
+				}
+				else
+					Error (NULL, "Unrecognized option `%s'.", Args [CurArg]);
+			}
+			else
+			{
+				// Treat it as a file name.
+				
+				// Open the file and load it into memory.
+				FILE *File = fopen (Arg, "rb");
+				if (File)
+				{
+					SIZE Size;
+					fseek (File, 0, SEEK_END);
+					Size = ftell (File);
+					rewind (File);
+					{
+						I1 *Data = malloc (Size);
+						if (Data)
+						{
+							if (fread (Data, Size, 1, File) == 1)
+							{
+								// Create a new object file inside the current archive.
+								OBJECT_FILE *ObjectFile = calloc (1, sizeof (OBJECT_FILE));
+								
+								if (!ObjectFile)
+								{
+									Error (Arg, "Out of memory.");
+									break;
+								}
+								
+								ObjectFile->Parent = &Archive;
+								ObjectFile->Data = Data;
+								ObjectFile->Size = Size;
+								ObjectFile->FileName = Arg;
+								GetFileStats (Arg, ObjectFile->FileStats);
+								Append (Archive.ObjectFiles, ObjectFile);
+								
+								// Update the statistics of the archive accordingly.
+								if (StatFileIsNewer (ObjectFile->FileStats, Archive.FileStats))
+									StatCopyAttributes (Archive.FileStats, ObjectFile->FileStats);
+								
+								// Try to import the object file's contents.
+								ArImportObjectFile (ObjectFile);
+							}
+							else
+								Error (Arg, "Unable to read file.");
+						}
+						else
+							Error (Arg, "Not enough memory to load file.");
+					}
+					fclose (File);
+				}
+				else
+					Error (Arg, "Unable to open file.");
+			}
+		}
+	}
+}

+ 143 - 0
ar/manip.c

@@ -0,0 +1,143 @@
+/* manip.c: Routines to manipulate the internal data
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "manip.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Free the archive tree.
+void FreeArchive (ARCHIVE *Archive)
+{
+	OBJECT_FILE *ObjectFile, *NextObjectFile;
+	
+	// Free symbol table, if one exists.
+	if (Archive->SymbolTable)
+		free (Archive->SymbolTable);
+	
+	// Free all object files.
+	for (ObjectFile = GetLast (Archive->ObjectFiles); ObjectFile; ObjectFile = NextObjectFile)
+	{
+		SYMBOL *Symbol, *NextSymbol;
+		
+		NextObjectFile = GetPrev (ObjectFile);
+		
+		if (ObjectFile->Data)
+			free (ObjectFile->Data);
+		
+		// Free all symbols.
+		for (Symbol = GetLast (ObjectFile->Symbols); Symbol; Symbol = NextSymbol)
+		{
+			NextSymbol = GetPrev (Symbol);
+			free (Symbol);
+		}
+		
+		free (ObjectFile);
+	}
+	
+	// Set all data to 0.
+	memset (Archive, 0, sizeof (ARCHIVE));
+}
+
+// Create a symbol table for an archive.
+BOOLEAN CreateSymbolTable (ARCHIVE *Archive)
+{
+	if (Archive->SymbolTable)
+	{
+		free (Archive->SymbolTable);
+		Archive->SymbolTable = NULL;
+	}
+	
+	// Only create a symbol table if the file contains symbols.
+	if (Archive->SymbolCount > 0)
+	{
+		SYMBOL_TABLE *SymbolTable = calloc (1, sizeof (SYMBOL_TABLE) + (Archive->SymbolCount * sizeof (SYMBOL *)));
+		
+		if (SymbolTable)
+		{
+			OBJECT_FILE *ObjectFile;
+			
+			SymbolTable->Parent = Archive;
+			SymbolTable->Size = 4; // The first 4 bytes represent the number of symbols.
+			
+			// Add all symbols to the symbol table.
+			for_each (ObjectFile, Archive->ObjectFiles)
+			{
+				SYMBOL *Symbol;
+				for_each (Symbol, ObjectFile->Symbols)
+				{
+					if (SymbolTable->SymbolCount < Archive->SymbolCount)
+					{
+						SymbolTable->Symbols [SymbolTable->SymbolCount++] = Symbol;
+						SymbolTable->Size += 4 + Symbol->NameLength + 1; // 4 bytes for the file offset, 1 byte for the terminating null character.
+					}
+					else
+					{
+						Error (NULL, "Internal symbol table inconsistency.");
+						goto BreakSymbolLoop;
+					}
+				}
+			}
+BreakSymbolLoop:
+			
+			Archive->SymbolTable = SymbolTable;
+			
+			return TRUE;
+		}
+		else
+			Error (NULL, "Not enough memory to create symbol table.");
+	}
+	
+	return FALSE;
+}
+
+// Fill the "export help" fields of the archive.
+BOOLEAN FillExportHelpFields (ARCHIVE *Archive)
+{
+	// The symbol table (if it exists) is always the first member.
+	if (Archive->SymbolTable)
+	{
+		Archive->SymbolTable->ArMemberOffset = GetArStartOffset ();
+		Archive->SymbolTable->ArMemberSize = GetArMemberSize (Archive->SymbolTable->Size);
+	}
+	
+	// Get the dimensions of each archive member.
+	{
+		OBJECT_FILE *ObjectFile, *PrevObjectFile = NULL;
+		
+		for_each (ObjectFile, Archive->ObjectFiles)
+		{
+			if (PrevObjectFile)
+				ObjectFile->ArMemberOffset = PrevObjectFile->ArMemberOffset + PrevObjectFile->ArMemberSize;
+			else if (Archive->SymbolTable)
+				ObjectFile->ArMemberOffset = Archive->SymbolTable->ArMemberOffset + Archive->SymbolTable->ArMemberSize;
+			else
+				ObjectFile->ArMemberOffset = GetArStartOffset ();
+			
+			ObjectFile->ArMemberSize = GetArMemberSize (ObjectFile->Size);
+			
+			PrevObjectFile = ObjectFile;
+		}
+		
+		// Update the size of the archive.
+		if (PrevObjectFile)
+			Archive->ArFileSize = PrevObjectFile->ArMemberOffset + PrevObjectFile->ArMemberSize;
+	}
+	
+	return TRUE;
+}

+ 40 - 0
ar/manip.h

@@ -0,0 +1,40 @@
+/* manip.h: Routines to manipulate the internal data
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef MANIP_H
+#define MANIP_H
+
+#include "../generic.h"
+#include "data.h"
+#include "../formats/ar.h"
+
+// Free the archive tree.
+void FreeArchive (ARCHIVE *Archive);
+
+// Create a symbol table for an archive.
+BOOLEAN CreateSymbolTable (ARCHIVE *Archive);
+
+// Fill the "export help" fields of the archive.
+BOOLEAN FillExportHelpFields (ARCHIVE *Archive);
+
+// Get the offset of the first member of a file.
+#define GetArStartOffset() (AR_FILE_HEADER_SIZE)
+// Get the size of a member depending on the size of the original file.
+#define GetArMemberSize(FileSize) ((AR_MEMBER_HEADER_SIZE + (FileSize) + AR_MEMBER_BOUNDARY - 1) / AR_MEMBER_BOUNDARY * AR_MEMBER_BOUNDARY)
+
+#endif

+ 206 - 0
bincode/cutrange.c

@@ -0,0 +1,206 @@
+/* fix_cutr.c: Routines for range cutting
+
+   Copyright (C) 2003 Kevin Kofler
+   Portions taken from manip.c, Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "cutrange.h"
+
+#include "../manip.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Check whether the segments which overlap with the interval [Start,End)
+// allow range-cutting.
+BOOLEAN CanCutRange (SECTION *Section, OFFSET Start, OFFSET End)
+{
+	// If the section is as a whole does not allow range-cutting,
+	// the individual segments implicitly do not allow this either.
+	if (!(Section->CanCutRanges))
+		return FALSE;
+	else
+	{
+		SEGMENT *Segment;
+		for (Segment = FindSegment (Section, Start, End); Segment && (Segment->Location.End->Location < End); Segment = GetNext (Segment))
+		{
+			if (!(Segment->CanCutRanges))
+				return FALSE;
+		}
+		return TRUE;
+	}
+}
+
+// Adjust a referenced location to reflect the removal of the
+// specified range. The location needs to be adjusted if the range
+// is between the location's symbol and the place it actually points
+// to.
+void AdjustLocationForRangeCut (RELOC *Reloc, LOCATION *Location, OFFSET Start, OFFSET End)
+{
+	// Length of the range that has been cut.
+	OFFSET RangeLength = End - Start;
+	
+	// Get the location of the target symbol and of the actual target.
+	OFFSET SymbolLocation = Location->Symbol->Location;
+	OFFSET TargetLocation = SymbolLocation + Location->Offset;
+	
+	// Make sure the symbol is not in the range we will cut.
+	if (!((SymbolLocation > Start) && (SymbolLocation < End)))
+	{
+		// Make sure the target is not in the range we will cut.
+		if ((TargetLocation > Start) && (TargetLocation < End))
+		{
+			Warning (GetFileName (Reloc->Parent, Reloc->Location), "Reloc at 0x%lX in section `%s' pointing to %s%+ld in a range which is being optimized away.",
+					 Reloc->Location, Reloc->Parent->SectionSymbol->Name, Location->Symbol->Name, Location->Offset);
+			// Set location value to Start.
+			Location->Offset = Start - SymbolLocation;
+		}
+		else
+		{
+			// We will adjust the symbol address in the next step, if
+			// the range that has been cut is between SymbolLocation and
+			// TargetLocation. Two cases need adjustment now:
+			// 1. The target is >= End, but the actual symbol is <= Start.
+			//    This implies that the offset is positive.
+			//    We need to subtract RangeLength from the offset, moving
+			//    the target closer to the symbol.
+			// 2. The target is <= Start, but the actual symbol is >= End.
+			//    This implies that the offset is negative.
+			//    We need to add RangeLength to the offset, moving the
+			//    target closer to the symbol.
+			
+			// Case 1:
+			if ((TargetLocation >= End) && (SymbolLocation <= Start))
+				Location->Offset -= RangeLength;
+			// Case 2:
+			else if ((TargetLocation <= Start) && (SymbolLocation >= End))
+				Location->Offset += RangeLength;
+		}
+	}
+}
+
+// Cut the specified range (the interval [Start,End)) out of
+// the specified section. All items within this range are removed. All relocs
+// refering to targets or relations behind End (taking Symbol and Offset,
+// but NOT FixedOffset, into account) are adjusted.
+void CutRange (SECTION *Section, OFFSET Start, OFFSET End)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Length of the range about to be cut.
+	OFFSET RangeLength = End - Start;
+	
+	// Adjust the size.
+	SIZE NewSize = Section->Size - RangeLength;
+	
+	if (Section->Data)
+	{
+		// Move the data.
+		memmove (Section->Data + Start, Section->Data + End, Section->Size - End);
+	}
+	
+	Section->Size = NewSize;
+	
+	// Adjust reloc targets and locations. We need to adjust relocs _targeting_
+	// this section, so we need to check each section in the program.
+	{
+		SECTION *RelocSection;
+		
+		// For each section...
+		for_each (RelocSection, Program->Sections)
+		{
+			RELOC *Reloc;
+			
+			// For each reloc...
+			for_each (Reloc, RelocSection->Relocs)
+			{
+				// Only adjust relocs targeting this section.
+				if (Reloc->Target.Symbol && (Reloc->Target.Symbol->Parent == Section))
+					AdjustLocationForRangeCut (Reloc, &(Reloc->Target), Start, End);
+				
+				// Only adjust relations targeting this section.
+				if (Reloc->Relation && Reloc->Relation->Symbol && (Reloc->Relation->Symbol->Parent == Section))
+					AdjustLocationForRangeCut (Reloc, Reloc->Relation, Start, End);
+			}
+		}
+	}
+	
+	// Now adjust symbol positions.
+	{
+		SYMBOL *Symbol;
+		
+		// For each symbol inside or behind the range...
+		// (Ignore symbols at Start itself, they are not really inside the
+		// range and cause spurious warnings. So start the search at Start + 1.)
+		for (Symbol = FindSymbolAtPos (Section, Start + 1, TRUE); Symbol; Symbol = GetNext (Symbol))
+		{
+			// If the position was inside the range, put it at Start.
+			if (Symbol->Location < End)
+			{
+				Symbol->Location = Start;
+				Warning (GetFileName (Section, Symbol->Location), "Symbol `%s' at 0x%lX in section `%s' in a range which is being optimized away.", Symbol->Name, Symbol->Location, Section->SectionSymbol->Name);
+			}
+			// If the position was behind the range, adjust it.
+			else
+				Symbol->Location -= RangeLength;
+		}
+	}
+	
+	// Now adjust the positions of all non-symbol items.
+	// Define a macro to make moving items more simple.
+#define MoveItems(Type,Item,ItemName) \
+({ \
+	Type *Item; \
+	for (Item = Find##Item##AtPos (Section, Start, TRUE); Item; Item = GetNext (Item)) \
+	{ \
+		if (Item->Location + Item->Size > Start) \
+		{ \
+			if (Item->Location < End) \
+			{ \
+				Warning (GetFileName (Item->Parent, Item->Location), "%s at 0x%lX in section `%s' in a range which is being optimized away.", ItemName, Item->Location, Section->SectionSymbol->Name); \
+				if (Item->Location > Start) \
+					Item->Location = Start; \
+			} \
+			else \
+				Item->Location -= RangeLength; \
+		} \
+	} \
+})
+	
+	MoveItems (RELOC,    Reloc,   "Reloc");
+	MoveItems (ROM_CALL, ROMCall, "ROM call");
+	MoveItems (RAM_CALL, RAMCall, "RAM call");
+	MoveItems (LIB_CALL, LibCall, "Library reference");
+	
+#undef MoveItems
+}
+
+// Do the cleanup needed after cutting ranges in a section.
+void FinalizeRangeCutting (SECTION *Section)
+{
+	if (Section->Data)
+	{
+		// Reduce the allocated size.
+		I1 *NewData = realloc (Section->Data, Section->Size);
+		
+		// If NewData returns NULL, this means that either there was a weird "out
+		// of memory" error (in this case, Section->Data is still valid), or
+		// NewSize was 0. In the second case, it is all right to set Section->Data
+		// to NULL.
+		if (NewData || (!(Section->Size)))
+			Section->Data = NewData;
+	}
+}

+ 38 - 0
bincode/cutrange.h

@@ -0,0 +1,38 @@
+/* cutrange.h: Routines for range cutting
+
+   Copyright (C) 2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef BINCODE_CUTRANGE_H
+#define BINCODE_CUTRANGE_H
+
+#include "../generic.h"
+#include "../data.h"
+
+// Check whether the segments which overlap with the interval [Start,End)
+// allow range-cutting.
+BOOLEAN CanCutRange (SECTION *Section, OFFSET Start, OFFSET End);
+
+// Cut the specified range (the interval [Start,End)) out of
+// the specified section. All items within this range are removed. All relocs
+// refering to targets or relations behind End (taking Symbol and Offset,
+// but NOT FixedOffset, into account) are adjusted.
+void CutRange (SECTION *Section, OFFSET Start, OFFSET End);
+
+// Do the cleanup needed after cutting ranges in a section.
+void FinalizeRangeCutting (SECTION *Section);
+
+#endif

+ 32 - 0
bincode/emu.h

@@ -0,0 +1,32 @@
+/* emu.h: Definitions for emulator-specific M68000 code fixup
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef BINCODE_M68K_EMU_H
+#define BINCODE_M68K_EMU_H
+
+#include "../generic.h"
+
+// F-Line opcode of the program-relative JMP.W branch (own emulator only).
+#define M68K_EMU_JMP_W_0 0xFF
+#define M68K_EMU_JMP_W_1 0xEF
+
+// F-Line opcode of the program-relative JSR.W branch (own emulator only).
+#define M68K_EMU_JSR_W_0 0xFF
+#define M68K_EMU_JSR_W_1 0xEE
+
+#endif

+ 38 - 0
bincode/fix_emu.c

@@ -0,0 +1,38 @@
+/* fix_emu.c: Routines for emulator-specific M68000 code fixup
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "fix_emu.h"
+
+#include "emu.h"
+#include "../manip.h"
+
+// Turn an M68000 absolute JRA/JSR to a program-relative F-Line jump.
+void M68kEmuMakeFLineJump (RELOC *Reloc, I1 *Opcode, BOOLEAN IsJSR)
+{
+	if ((Reloc->Size == 4) && (!(Reloc->Relative)))
+	{
+		// Optimize the JRA or JSR into an F-Line JMP.W or JSR.W.
+		Opcode [0] = (IsJSR ? M68K_EMU_JSR_W_0 : M68K_EMU_JMP_W_0);
+		Opcode [1] = (IsJSR ? M68K_EMU_JSR_W_1 : M68K_EMU_JMP_W_1);
+		
+		// Change the reloc to 2-byte program-relative.
+		SetRelocProgramRelative (Reloc);
+		Reloc->Size = 2;
+		Reloc->FixedOffset -= 0x8000;
+	}
+}

+ 29 - 0
bincode/fix_emu.h

@@ -0,0 +1,29 @@
+/* fix_emu.h: Routines for emulator-specific M68000 code fixup
+
+   Copyright (C) 2003 Kevin Kofler
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef BINCODE_FIX_M68K_EMU_H
+#define BINCODE_FIX_M68K_EMU_H
+
+#include "../generic.h"
+#include "../data.h"
+
+// Turn an M68000 absolute JRA/JSR to a program-relative F-Line jump.
+void M68kEmuMakeFLineJump (RELOC *Reloc, I1 *Opcode, BOOLEAN IsJSR);
+
+#endif

+ 825 - 0
bincode/fix_m68k.c

@@ -0,0 +1,825 @@
+/* fix_m68k.c: Routines for M68000 code fixup
+
+   Copyright (C) 2003 Sebastian Reichelt
+   Copyright (C) 2003-2004 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "fix_m68k.h"
+
+#include "m68k.h"
+#include "fix_tios.h"
+#include "fix_emu.h"
+#include "cutrange.h"
+#include "../manip.h"
+
+#include <stdlib.h>
+
+// Apply generic code fixes and optimizations to a section.
+void M68kFixCode (SECTION *Section)
+{
+	M68kFixCodePreMerge (Section, NULL, 0);
+}
+
+// Fix and optionally optimize code executable on the M68k processor family,
+// for two sections which are to be merged.
+// Src may be NULL.
+// If DestSize is nonzero, it specifies a fixed size for the destination
+// section, which will not change even when cutting ranges from it.
+void M68kFixCodePreMerge (SECTION *Dest, SECTION *Src, SIZE DestSize)
+{
+	if (Dest->Code && (!(Dest->Frozen)) && (!(Dest->Parent->Frozen)))
+	{
+		OPTIMIZE_INFO *OptimizeInfo = Dest->Parent->OptimizeInfo;
+		SIZE OrigSize = Dest->Size;
+		
+		RELOC *Reloc, *NextReloc;
+		
+		// For each reloc...
+		for (Reloc = (DestSize ? GetFirst (Dest->Relocs) : GetLast (Dest->Relocs)); Reloc; Reloc = NextReloc)
+		{
+			NextReloc = (DestSize ? GetNext (Reloc) : GetPrev (Reloc));
+			
+			// Completely ignore builtin relocs. Also ignore relation-relative
+			// relocs, since the only relative relocs we can optimize further
+			// are branches, and those are never relation-relative.
+			// Ignore relocs which are not in code segments.
+			if ((!(Reloc->Target.Builtin || Reloc->Relation || Reloc->Unoptimizable)) && IsCodeRange (Dest, Reloc->Location, Reloc->Location + Reloc->Size))
+			{
+				// We can only fix or optimize a reloc whose target we know.
+				if (Reloc->Target.Symbol && ((Reloc->Target.Symbol->Parent == Dest) || (Reloc->Target.Symbol->Parent == Src)))
+				{
+					OFFSET TargetDistance;
+					// Get the distance of the target symbol, provided it is in the
+					// same section.
+					if (Reloc->Target.Symbol->Parent == Dest)
+						TargetDistance = GetLocationOffset (Dest, &(Reloc->Target));
+					// If it is in the next section, add the section size.
+					// This is not 100% correct, since we might pad the section, but
+					// currently there is no reason to add padding to code. If we get
+					// a slightly wrong distance, we will probably get away with it
+					// anyway.
+					else
+						TargetDistance = (DestSize ? : Dest->Size) + GetLocationOffset (Src, &(Reloc->Target));
+					// Add the fixed offset because it needs to be added to the reloc
+					// target. Subtract the location of the reloc.
+					TargetDistance += Reloc->FixedOffset - Reloc->Location;
+					
+					// Fix and possibly optimize or remove the reloc.
+					M68kFixReloc (Reloc, TargetDistance, OptimizeInfo);
+				}
+			}
+		}
+		
+		if (Dest->Size < OrigSize)
+			FinalizeRangeCutting (Dest);
+	}
+}
+
+// Cut the range between Start and End (not including End) if that is
+// permitted; otherwise fill it with NOPs. Start and End are expected
+// to be at even addresses. It is also assumed that IsBinaryDataRange
+// has been called on the range (without an exception, or with an
+// exception that has since been removed).
+void M68kCutOrFillRange (SECTION *Section, OFFSET Start, OFFSET End, OPTIMIZE_INFO *OptimizeInfo)
+{
+	if (End > Start)
+	{
+		BOOLEAN SectionEnd = End >= Section->Size;
+		
+		// Make sure Start is not outside of the section data;
+		if (Start < 0)
+			Start = 0;
+		// Make sure End is not outside of the section data;
+		if (SectionEnd)
+			End = Section->Size;
+		
+		// If the range is at the end of the section, shrink the section.
+		if (SectionEnd && CanShrinkSection (Section, Start, NULL))
+		{
+			// Look for a short branch to the end of the section. GNU as
+			// does this to simulate long conditional branches.
+			BOOLEAN BranchFound = FALSE;
+			const I1 *Data = Section->Data;
+			
+			if (Data)
+			{
+				SIZE Size = Section->Size;
+				OFFSET CurPos;
+				
+				for (CurPos = Size - 4; (CurPos >= 0) && (CurPos >= Size - 8); CurPos -= 2)
+				{
+					if (((Data [CurPos + 0] & M68K_Bcc_MASK_0) == M68K_Bcc_S_0) && (Data [CurPos + 1] == Size - (CurPos + 2)))
+					{
+						BranchFound = TRUE;
+						break;
+					}
+				}
+			}
+			
+			if (!BranchFound)
+			{
+				// No such branch was found, so we can (hopefully) safely cut
+				// the section.
+				CutSection (Section, Start);
+				return;
+			}
+		}
+		// If the range is in the middle of the section, try to cut it out.
+		else
+		{
+			if (CanCutRange (Section, Start, End))
+			{
+				SIZE Length = End - Start;
+				OptimizeInfo->CutRangesResult += Length;
+				
+				if (OptimizeInfo->CutRanges)
+				{
+					CutRange (Section, Start, End);
+					OptimizeInfo->NearAssemblyResult -= Length;
+					return;
+				}
+			}
+		}
+		
+		// If we cannot cut the range for some reason, fill it with NOPs.
+		{
+			I1 *Data = Section->Data;
+			if (Data)
+			{
+				OFFSET CurPos;
+				for (CurPos = Start; CurPos + 1 < End; CurPos += 2)
+				{
+					Data [CurPos + 0] = M68K_NOP_0;
+					Data [CurPos + 1] = M68K_NOP_1;
+				}
+			}
+		}
+	}
+}
+
+// Fix and possibly optimize a given relocation entry.
+// TargetDistance is the estimated (signed) distance of the relocation target, including the
+// fixed offset. OptimizeInfo contains information about what to optimize.
+// The reloc may be removed during the process.
+void M68kFixReloc (RELOC *Reloc, OFFSET TargetDistance, OPTIMIZE_INFO *OptimizeInfo)
+{
+	SECTION *Section = Reloc->Parent;
+	I1 *Data = Section->Data;
+	OFFSET RelocLocation = Reloc->Location;
+	OFFSET OpcodeLocation;
+	I1 *Opcode;
+	
+	if (!Data)
+		return;
+	
+	if (Reloc->Unoptimizable)
+		return;
+
+	// Be careful with relocs at the beginning of the section; they might
+	// cause segmentation faults when we try to determine the opcode.
+	// Usually, ignore all relocs whose location is less than 2.
+	// As a special exception, allow 1-byte relocs with a location of 1.
+	if (!((Reloc->Location >= 2) || ((Reloc->Size == 1) && (Reloc->Location == 1))))
+		return;
+	
+	// Optimize a subroutine branch followed by an RTS.
+	if (OptimizeInfo->OptimizeReturns)
+	{
+		OFFSET RelocEnd = RelocLocation + Reloc->Size;
+		OFFSET RTSEnd = RelocEnd + 2;
+		
+		if ((RTSEnd <= Section->Size) && (Data [RelocEnd + 0] == M68K_RTS_0) && (Data [RelocEnd + 1] == M68K_RTS_1))
+		{
+			OFFSET OpcodeLocation;
+			I1 *Opcode;
+			BOOLEAN Optimized = FALSE;
+			
+			if (Reloc->Relative)
+			{
+				switch (Reloc->Size)
+				{
+					// Optimize 1-byte relative relocs.
+					case 1:
+						OpcodeLocation = RelocLocation - 1;
+						Opcode = Data + OpcodeLocation;
+						
+						// Check if the reloc belongs to a BSR.S, and that there
+						// is nothing in our way.
+						if ((Opcode [0] == M68K_BSR_S_0) && (IsBinaryDataRange (Section, OpcodeLocation, RTSEnd, Reloc)))
+						{
+							// Optimize it into a BRA.S.
+							Opcode [0] = M68K_BRA_S_0;
+							Optimized = TRUE;
+						}
+						
+						break;
+					
+					// Optimize 2-byte relative relocs.
+					case 2:
+						OpcodeLocation = RelocLocation - 2;
+						Opcode = Data + OpcodeLocation;
+						
+						// Check if the reloc belongs to a BSR.W, and that there
+						// is nothing in our way.
+						if ((Opcode [0] == M68K_BSR_W_0) && (Opcode [1] == M68K_BSR_W_1) && (IsBinaryDataRange (Section, OpcodeLocation, RTSEnd, Reloc)))
+						{
+							// Optimize it into a JMP.
+							Opcode [0] = M68K_BRA_W_0;
+							Opcode [1] = M68K_BRA_W_1;
+							Optimized = TRUE;
+						}
+						
+						break;
+				}
+			}
+			else
+			{
+				// Optimize 4-byte absolute relocs.
+				if (Reloc->Size == 4)
+				{
+					OpcodeLocation = RelocLocation - 2;
+					Opcode = Data + OpcodeLocation;
+					
+					// Check if the reloc belongs to a JSR, and that there is
+					// nothing in our way.
+					if ((Opcode [0] == M68K_JSR_0) && (Opcode [1] == M68K_JSR_1) && (IsBinaryDataRange (Section, OpcodeLocation, RTSEnd, Reloc)))
+					{
+						// Optimize it into a JMP.
+						Opcode [0] = M68K_JMP_0;
+						Opcode [1] = M68K_JMP_1;
+						Optimized = TRUE;
+					}
+				}
+			}
+			
+			// Remove the RTS.
+			if (Optimized)
+				M68kCutOrFillRange (Section, RelocEnd, RTSEnd, OptimizeInfo);
+		}
+	}
+	
+	// Only branch fixes and optimizations make sense for relative relocs.
+	if (Reloc->Relative)
+	{
+		// Check if it is a 1-byte relative reloc at the end of the
+		// section, pointing to the next instruction. This means that
+		// the value will probably be 0, which is invalid for 1-byte
+		// branches.
+		if ((Reloc->Size == 1) && (TargetDistance == 0))
+		{
+			OpcodeLocation = RelocLocation - 1;
+			Opcode = Data + OpcodeLocation;
+			
+			// Check whether it belongs to a branch. If it does, it
+			// is invalid.
+			if (((Opcode [0] & M68K_Bcc_MASK_0) == M68K_Bcc_S_0) && (IsBinaryDataRange (Section, OpcodeLocation, OpcodeLocation + 2, Reloc)))
+			{
+				// If it is a BSR.S, removing it would modify the
+				// semantics. Instead, insert a NOP at the end of the
+				// section (if this is really the end of the section).
+				if (Opcode [0] == M68K_BSR_S_0)
+				{
+					if (OpcodeLocation + 2 == Section->Size)
+					{
+						// Allocate two bytes at the end of the section.
+						I1 *Space = AllocateSpaceInSection (Section, 2);
+						
+						if (Space)
+						{
+							Space [0] = M68K_NOP_0;
+							Space [1] = M68K_NOP_1;
+						}
+					}
+				}
+				else
+				{
+					// Delete the reloc.
+					FreeReloc (Reloc);
+					
+					// Cut or fill the gained space.
+					M68kCutOrFillRange (Section, OpcodeLocation, OpcodeLocation + 2, OptimizeInfo);
+					
+					// Since the reloc has been removed, return from the function.
+					return;
+				}
+			}
+		}
+		else if (OptimizeInfo->OptimizeBranches && (Reloc->Size == 2))
+		{
+			OpcodeLocation = RelocLocation - 2;
+			Opcode = Data + OpcodeLocation;
+			
+			// Check whether the reloc belongs to a branch.
+			if (((Opcode [0] & M68K_Bcc_MASK_0) == M68K_Bcc_W_0) && (Opcode [1] == M68K_Bcc_W_1) && (IsBinaryDataRange (Section, OpcodeLocation, OpcodeLocation + 4, Reloc)))
+			{
+				// Check whether it can be removed.
+				if ((TargetDistance == 2) && (Opcode [0] != M68K_BSR_W_0))
+				{
+					// Delete the reloc.
+					FreeReloc (Reloc);
+					
+					// Cut or fill the gained space.
+					M68kCutOrFillRange (Section, OpcodeLocation, OpcodeLocation + 4, OptimizeInfo);
+					
+					// Since the reloc has been removed, return from the function.
+					return;
+				}
+				// Check whether it is near enough for a Bcc.S.
+				else if ((TargetDistance != 2) && (M68K_REL_OK (TargetDistance, 1)))
+				{
+					// Optimize it into a Bcc.S.
+					Opcode [0] = M68K_Bcc_S_0 | (Data [OpcodeLocation] & (~M68K_Bcc_MASK_0));
+					Opcode [1] = 0;
+					// Change the reloc to 1-byte relative.
+					Reloc->Size = 1;
+					// Adjust the location.
+					Reloc->Location--;
+					RelocLocation--;
+					// Adjust the offset. A short branch always
+					// uses the next instruction as a reference.
+					Reloc->FixedOffset--;
+					
+					// Cut or fill the gained space.
+					M68kCutOrFillRange (Section, OpcodeLocation + 2, OpcodeLocation + 4, OptimizeInfo);
+				}
+			}
+		}
+	}
+	// Other than that, only change 4-byte absolute relocs.
+	else if (Reloc->Size == 4)
+	{
+		// Most opcodes are two bytes long, and the reloc follows
+		// immediately.
+		OpcodeLocation = RelocLocation - 2;
+		
+		// Safety check before accessing the section data.
+		if (IsBinaryDataRange (Section, OpcodeLocation, OpcodeLocation + 6, Reloc))
+		{
+			Opcode = Data + OpcodeLocation;
+			
+			{
+				// *** Branch Optimization ***
+				
+				// Try to optimize a 4-byte absolute branch into a 2-byte or 1-byte
+				// relative one.
+				// Check whether the reloc belongs to a branch.
+				BOOLEAN IsJMP = ((Opcode [0] == M68K_JMP_0) && (Opcode [1] == M68K_JMP_1));
+				BOOLEAN IsJSR = ((Opcode [0] == M68K_JSR_0) && (Opcode [1] == M68K_JSR_1));
+				
+				if ((IsJMP || IsJSR))
+				{
+					// Check whether it can be removed.
+					if (OptimizeInfo->OptimizeBranches && (TargetDistance == 4) && IsJMP)
+					{
+						// Delete the reloc.
+						FreeReloc (Reloc);
+						
+						// Cut or fill the gained space.
+						M68kCutOrFillRange (Section, OpcodeLocation, OpcodeLocation + 6, OptimizeInfo);
+						
+						OptimizeInfo->OptimizeBranchesResult++;
+					}
+					// Check whether it is near enough for a BRA.S or BSR.S.
+					else if (OptimizeInfo->OptimizeBranches && (TargetDistance != 4) && (M68K_REL_OK (TargetDistance, 1)))
+					{
+						// Optimize it into a BRA.S or BSR.S.
+						Opcode [0] = (IsJSR ? M68K_BSR_S_0 : M68K_BRA_S_0);
+						Opcode [1] = 0;
+						// Change the reloc to 1-byte relative.
+						Reloc->Relative = TRUE;
+						Reloc->Size = 1;
+						// Adjust the location.
+						Reloc->Location--;
+						// Adjust the offset. A short branch always
+						// uses the next instruction as a reference.
+						Reloc->FixedOffset--;
+						
+						// Cut or fill the gained space.
+						M68kCutOrFillRange (Section, OpcodeLocation + 2, OpcodeLocation + 6, OptimizeInfo);
+						
+						OptimizeInfo->OptimizeBranchesResult++;
+					}
+					// Treat it as a normal absolute to relative optimization.
+					else
+					{
+						BOOLEAN BranchOptimized = FALSE;
+						
+						// Check if the target is near enough for a BRA/BSR.
+						if (M68K_REL_OK (TargetDistance, 2))
+						{
+							OptimizeInfo->OptimizeBranchesResult++;
+							if (OptimizeInfo->NearAssemblyResult >= 0)
+								OptimizeInfo->NearAssemblyResult += 2;
+							
+							if (OptimizeInfo->OptimizeBranches)
+							{
+								// Optimize it into a BRA.W or BSR.W.
+								Opcode [0] = (IsJSR ? M68K_BSR_W_0 : M68K_BRA_W_0);
+								Opcode [1] = (IsJSR ? M68K_BSR_W_1 : M68K_BRA_W_1);
+								// Change the reloc to 2-byte relative.
+								Reloc->Relative = TRUE;
+								Reloc->Size = 2;
+								
+								// Cut or fill the gained space.
+								M68kCutOrFillRange (Section, OpcodeLocation + 4, OpcodeLocation + 6, OptimizeInfo);
+								
+								// Do not try to do anything else with this branch.
+								BranchOptimized = TRUE;
+							}
+						}
+						else
+						{
+							// The target is not near enough. This means that near
+							// assembly is not possible.
+							OptimizeInfo->NearAssemblyResult = -1;
+						}
+						
+						if (!BranchOptimized)
+						{
+							// Optimize F-Line jumps if desired.
+							OptimizeInfo->UseFLineJumpsResult++;
+							if (OptimizeInfo->Use4ByteFLineJumps)
+								M68kEmuMakeFLineJump (Reloc, Opcode, IsJSR);
+							else if (OptimizeInfo->UseFLineJumps)
+								M68kTIOSMakeFLineJump (Reloc, Opcode, IsJSR);
+							else
+								return;
+							
+							// Cut or fill the gained space.
+							M68kCutOrFillRange (Section, Reloc->Location + Reloc->Size, OpcodeLocation + 6, OptimizeInfo);
+						}
+					}
+				}
+				// Not a branch.
+				else
+				{
+					BOOLEAN Optimized = FALSE;
+					
+					// Check if the target is near enough.
+					if (M68K_REL_OK (TargetDistance, 2))
+					{
+						// *** Move Optimization ***
+						
+						// Optimize LEA(.L) var.L,reg into
+						// LEA(.L) var.W(%PC),reg.
+						if ((((Opcode [0] & M68K_LEA_ABS_MASK_0) == M68K_LEA_ABS_0) && ((Opcode [1] & M68K_LEA_ABS_MASK_1) == M68K_LEA_ABS_1))
+						// Optimize PEA(.L) var.L into PEA(.L) var.W(%PC).
+						 || ((Opcode [0] == M68K_PEA_ABS_0) && (Opcode [1] == M68K_PEA_ABS_1))
+						// Optimize MOVE.x var.L,reg/(reg)/(reg)+ into
+						// MOVE.x var.W(%PC),reg/(reg)/(reg)+.
+						 || (((Opcode [0] & M68K_MOVE_ABS_REG_MASK_0) == M68K_MOVE_ABS_REG_0) && ((Opcode [1] & M68K_MOVE_ABS_REG_MASK_1) == M68K_MOVE_ABS_REG_1)
+						  && (!((Opcode [0] & M68K_MOVE_ABS_REG_INV_0_MASK_0) == M68K_MOVE_ABS_REG_INV_0_0))
+						  && (!(((Opcode [0] & M68K_MOVE_ABS_REG_INV_1_MASK_0) == M68K_MOVE_ABS_REG_INV_1_0) && ((Opcode [1] & M68K_MOVE_ABS_REG_INV_1_MASK_1) == M68K_MOVE_ABS_REG_INV_1_1))))
+						// Optimize MOVE.x var.L,-(reg) into
+						// MOVE.x var.W(%PC),-(reg).
+						 || (((Opcode [0] & M68K_MOVE_ABS_PREDEC_MASK_0) == M68K_MOVE_ABS_PREDEC_0) && ((Opcode [1] & M68K_MOVE_ABS_PREDEC_MASK_1) == M68K_MOVE_ABS_PREDEC_1)
+						  && (!((Opcode [0] & M68K_MOVE_ABS_PREDEC_INV_0_MASK_0) == M68K_MOVE_ABS_PREDEC_INV_0_0))))
+						{
+							OptimizeInfo->OptimizeMovesResult++;
+							if (OptimizeInfo->NearAssemblyResult >= 0)
+								OptimizeInfo->NearAssemblyResult += 2;
+							
+							if (OptimizeInfo->OptimizeMoves)
+							{
+								// Turn the opcode into a pc-relative one.
+								M68K_MAKE_REL_OPCODE_1 (Opcode [1]);
+								
+								// Do everything else later.
+								Optimized = TRUE;
+							}
+						}
+						
+						// *** Test Optimization ***
+						
+						// Optimize CMP.x var.L,reg into CMP.x var.W(%PC),reg.
+						else if ((((Opcode [0] & M68K_CMP_ABS_REG_MASK_0) == M68K_CMP_ABS_REG_0) && ((Opcode [1] & M68K_CMP_ABS_REG_MASK_1) == M68K_CMP_ABS_REG_1)
+						       && (!((Opcode [1] & M68K_CMP_ABS_REG_INV_0_MASK_1) == M68K_CMP_ABS_REG_INV_0_1)))
+						// Optimize BTST reg,var.L into BTST reg,var.W(%PC).
+						      || (((Opcode [0] & M68K_BTST_REG_ABS_MASK_0) == M68K_BTST_REG_ABS_0) && ((Opcode [1] & M68K_BTST_REG_ABS_MASK_1) == M68K_BTST_REG_ABS_1)))
+						{
+							OptimizeInfo->OptimizeTestsResult++;
+							if (OptimizeInfo->NearAssemblyResult >= 0)
+								OptimizeInfo->NearAssemblyResult += 2;
+							
+							if (OptimizeInfo->OptimizeTests)
+							{
+								// Turn the opcode into a pc-relative one.
+								M68K_MAKE_REL_OPCODE_1 (Opcode [1]);
+								
+								// Do everything else later.
+								Optimized = TRUE;
+							}
+						}
+						
+						// *** Calculation Optimization ***
+						
+						// Optimize ADD/SUB.x var.L,reg into
+						// ADD/SUB.x var.W(%PC),reg.
+						else if ((((Opcode [0] & M68K_ADDSUB_ABS_REG_0_MASK_0) == M68K_ADDSUB_ABS_REG_0_0) && ((Opcode [1] & M68K_ADDSUB_ABS_REG_0_MASK_1) == M68K_ADDSUB_ABS_REG_0_1))
+						      || (((Opcode [0] & M68K_ADDSUB_ABS_REG_1_MASK_0) == M68K_ADDSUB_ABS_REG_1_0) && ((Opcode [1] & M68K_ADDSUB_ABS_REG_1_MASK_1) == M68K_ADDSUB_ABS_REG_1_1))
+						// Optimize MUL/DIV.x var.L,reg into
+						// MUL/DIV.x var.W(%PC),reg.
+						      || (((Opcode [0] & M68K_MULDIV_ABS_REG_MASK_0) == M68K_MULDIV_ABS_REG_0) && ((Opcode [1] & M68K_MULDIV_ABS_REG_MASK_1) == M68K_MULDIV_ABS_REG_1))
+						// Optimize AND/OR.x var.L,reg into
+						// AND/OR.x var.W(%PC),reg.
+						      || (((Opcode [0] & M68K_ANDOR_ABS_REG_MASK_0) == M68K_ANDOR_ABS_REG_0) && ((Opcode [1] & M68K_ANDOR_ABS_REG_MASK_1) == M68K_ANDOR_ABS_REG_1)))
+						{
+							OptimizeInfo->OptimizeCalcsResult++;
+							if (OptimizeInfo->NearAssemblyResult >= 0)
+								OptimizeInfo->NearAssemblyResult += 2;
+							
+							if (OptimizeInfo->OptimizeCalcs)
+							{
+								// Turn the opcode into a pc-relative one.
+								M68K_MAKE_REL_OPCODE_1 (Opcode [1]);
+								
+								// Do everything else later.
+								Optimized = TRUE;
+							}
+						}
+					}
+					
+					if (Optimized)
+					{
+						// Change the reloc to 2-byte relative.
+						Reloc->Relative = TRUE;
+						Reloc->Size = 2;
+						
+						// Cut or fill the gained space.
+						M68kCutOrFillRange (Section, OpcodeLocation + 4, OpcodeLocation + 6, OptimizeInfo);
+					}
+					else
+					{
+						// Check if the target is near enough.
+						if (M68K_REL_OK (TargetDistance, 2))
+						{
+							if (IsBinaryDataRange (Section, OpcodeLocation, OpcodeLocation + 8, Reloc))
+							{
+								// Optimize MOVE.x var.L,ofs(reg) into
+								// MOVE.x var.W(%PC),ofs(reg)
+								// We cannot handle this above because of the
+								// offset, which comes after the reloc.
+								if (((Opcode [0] & M68K_MOVE_ABS_OFSREG_MASK_0) == M68K_MOVE_ABS_OFSREG_0) && ((Opcode [1] & M68K_MOVE_ABS_OFSREG_MASK_1) == M68K_MOVE_ABS_OFSREG_1)
+								 && (!((Opcode [0] & M68K_MOVE_ABS_OFSREG_INV_0_MASK_0) == M68K_MOVE_ABS_OFSREG_INV_0_0)))
+								{
+									OptimizeInfo->OptimizeMovesResult++;
+									if (OptimizeInfo->NearAssemblyResult >= 0)
+										OptimizeInfo->NearAssemblyResult += 2;
+									
+									if (OptimizeInfo->OptimizeMoves)
+									{
+										// Turn the opcode into a pc-relative one.
+										M68K_MAKE_REL_OPCODE_1 (Opcode [1]);
+										
+										// Move the offset to the correct place.
+										Opcode [4] = Opcode [6];
+										Opcode [5] = Opcode [7];
+										
+										// Change the reloc to 2-byte relative.
+										Reloc->Relative = TRUE;
+										Reloc->Size = 2;
+										
+										// Cut or fill the gained space.
+										M68kCutOrFillRange (Section, OpcodeLocation + 6, OpcodeLocation + 8, OptimizeInfo);
+									}
+								}
+							}
+							
+							{
+								OpcodeLocation = RelocLocation - 4;
+								Opcode = Data + OpcodeLocation;
+								
+								if (IsBinaryDataRange (Section, OpcodeLocation, OpcodeLocation + 8, Reloc))
+								{
+									// Optimize MOVEM.x var.L,regs into
+									// MOVEM var.W(%PC),regs.
+									if (((Opcode [0] & M68K_MOVEM_ABS_REGS_MASK_0) == M68K_MOVEM_ABS_REGS_0) && ((Opcode [1] & M68K_MOVEM_ABS_REGS_MASK_1) == M68K_MOVEM_ABS_REGS_1))
+									{
+										OptimizeInfo->OptimizeMovesResult++;
+										if (OptimizeInfo->NearAssemblyResult >= 0)
+											OptimizeInfo->NearAssemblyResult += 2;
+										
+										if (OptimizeInfo->OptimizeMoves)
+										{
+											// Turn the opcode into a pc-relative one.
+											M68K_MAKE_REL_OPCODE_1 (Opcode [1]);
+											
+											// Change the reloc to 2-byte relative.
+											Reloc->Relative = TRUE;
+											Reloc->Size = 2;
+											
+											// Cut or fill the gained space.
+											M68kCutOrFillRange (Section, OpcodeLocation + 6, OpcodeLocation + 8, OptimizeInfo);
+										}
+									}
+									// Optimize BTST #num,var.L into
+									// BTST #num,var.W(%PC).
+									else if ((Opcode [0] == M68K_BTST_IMM_ABS_0) && (Opcode [1] == M68K_BTST_IMM_ABS_1) && (Opcode [2] == M68K_BTST_IMM_ABS_2))
+									{
+										OptimizeInfo->OptimizeTestsResult++;
+										if (OptimizeInfo->NearAssemblyResult >= 0)
+											OptimizeInfo->NearAssemblyResult += 2;
+										
+										if (OptimizeInfo->OptimizeTests)
+										{
+											// Turn the opcode into a pc-relative one.
+											M68K_MAKE_REL_OPCODE_1 (Opcode [1]);
+											
+											// Change the reloc to 2-byte relative.
+											Reloc->Relative = TRUE;
+											Reloc->Size = 2;
+											
+											// Cut or fill the gained space.
+											M68kCutOrFillRange (Section, OpcodeLocation + 6, OpcodeLocation + 8, OptimizeInfo);
+										}
+									}
+								}
+							}
+						}
+						else
+						{
+							// The target is not near enough. This means that near
+							// assembly is not possible.
+							OptimizeInfo->NearAssemblyResult = -1;
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
+// Checks if a specific reloc might be optimizable. This is currently
+// limited to 4-bytes absolute relocs because that is the only case
+// this is needed for.
+BOOLEAN M68kIsRelocOptimizable (const RELOC *Reloc)
+{
+	if (Reloc->Unoptimizable) return FALSE;
+
+	if (Reloc->Size == 4 && (!(Reloc->Relative)))
+	{
+		const SECTION *Section = Reloc->Parent;
+		
+		// Most opcodes are two bytes long, and the reloc follows
+		// immediately.
+		OFFSET OpcodeLocation = Reloc->Location - 2;
+		
+		// Safety check before accessing the section data.
+		if (IsBinaryDataRange (Section, OpcodeLocation, OpcodeLocation + 6, Reloc))
+		{
+			const I1 *Opcode = Section->Data + OpcodeLocation;
+			// *** Branch Optimization ***
+			if (((Opcode [0] == M68K_JMP_0) && (Opcode [1] == M68K_JMP_1))
+			 || ((Opcode [0] == M68K_JSR_0) && (Opcode [1] == M68K_JSR_1))
+			// *** Move Optimization ***
+			 || (((Opcode [0] & M68K_LEA_ABS_MASK_0) == M68K_LEA_ABS_0) && ((Opcode [1] & M68K_LEA_ABS_MASK_1) == M68K_LEA_ABS_1))
+			 || ((Opcode [0] == M68K_PEA_ABS_0) && (Opcode [1] == M68K_PEA_ABS_1))
+			 || (((Opcode [0] & M68K_MOVE_ABS_REG_MASK_0) == M68K_MOVE_ABS_REG_0) && ((Opcode [1] & M68K_MOVE_ABS_REG_MASK_1) == M68K_MOVE_ABS_REG_1)
+			  && (!((Opcode [0] & M68K_MOVE_ABS_REG_INV_0_MASK_0) == M68K_MOVE_ABS_REG_INV_0_0))
+			  && (!(((Opcode [0] & M68K_MOVE_ABS_REG_INV_1_MASK_0) == M68K_MOVE_ABS_REG_INV_1_0) && ((Opcode [1] & M68K_MOVE_ABS_REG_INV_1_MASK_1) == M68K_MOVE_ABS_REG_INV_1_1))))
+			 || (((Opcode [0] & M68K_MOVE_ABS_PREDEC_MASK_0) == M68K_MOVE_ABS_PREDEC_0) && ((Opcode [1] & M68K_MOVE_ABS_PREDEC_MASK_1) == M68K_MOVE_ABS_PREDEC_1)
+			  && (!((Opcode [0] & M68K_MOVE_ABS_PREDEC_INV_0_MASK_0) == M68K_MOVE_ABS_PREDEC_INV_0_0)))
+			 || (((Opcode [0] & M68K_MOVE_ABS_OFSREG_MASK_0) == M68K_MOVE_ABS_OFSREG_0) && ((Opcode [1] & M68K_MOVE_ABS_OFSREG_MASK_1) == M68K_MOVE_ABS_OFSREG_1)
+			  && (!((Opcode [0] & M68K_MOVE_ABS_OFSREG_INV_0_MASK_0) == M68K_MOVE_ABS_OFSREG_INV_0_0)))
+			 || (((Opcode [0] & M68K_MOVEM_ABS_REGS_MASK_0) == M68K_MOVEM_ABS_REGS_0) && ((Opcode [1] & M68K_MOVEM_ABS_REGS_MASK_1) == M68K_MOVEM_ABS_REGS_1))
+			// *** Test Optimization ***
+			 || (((Opcode [0] & M68K_CMP_ABS_REG_MASK_0) == M68K_CMP_ABS_REG_0) && ((Opcode [1] & M68K_CMP_ABS_REG_MASK_1) == M68K_CMP_ABS_REG_1)
+			  && (!((Opcode [1] & M68K_CMP_ABS_REG_INV_0_MASK_1) == M68K_CMP_ABS_REG_INV_0_1)))
+			 || (((Opcode [0] & M68K_BTST_REG_ABS_MASK_0) == M68K_BTST_REG_ABS_0) && ((Opcode [1] & M68K_BTST_REG_ABS_MASK_1) == M68K_BTST_REG_ABS_1))
+			 || ((Opcode [0] == M68K_BTST_IMM_ABS_0) && (Opcode [1] == M68K_BTST_IMM_ABS_1) && (Opcode [2] == M68K_BTST_IMM_ABS_2))
+			// *** Calculation Optimization ***
+			 || (((Opcode [0] & M68K_ADDSUB_ABS_REG_0_MASK_0) == M68K_ADDSUB_ABS_REG_0_0) && ((Opcode [1] & M68K_ADDSUB_ABS_REG_0_MASK_1) == M68K_ADDSUB_ABS_REG_0_1))
+			 || (((Opcode [0] & M68K_ADDSUB_ABS_REG_1_MASK_0) == M68K_ADDSUB_ABS_REG_1_0) && ((Opcode [1] & M68K_ADDSUB_ABS_REG_1_MASK_1) == M68K_ADDSUB_ABS_REG_1_1))
+			 || (((Opcode [0] & M68K_MULDIV_ABS_REG_MASK_0) == M68K_MULDIV_ABS_REG_0) && ((Opcode [1] & M68K_MULDIV_ABS_REG_MASK_1) == M68K_MULDIV_ABS_REG_1))
+			 || (((Opcode [0] & M68K_ANDOR_ABS_REG_MASK_0) == M68K_ANDOR_ABS_REG_0) && ((Opcode [1] & M68K_ANDOR_ABS_REG_MASK_1) == M68K_ANDOR_ABS_REG_1)))
+				return TRUE;
+		}
+	}
+	
+	return FALSE;
+}
+
+// If the section ends with exactly one NOP instruction, remove the NOP.
+void M68kRemoveTrailingNOP (SECTION *Section)
+{
+	I1 *Data = Section->Data;
+	SIZE Size = Section->Size;
+	
+	// Validate basic circumstances.
+	if (Data && (Size >= 4)
+	// Check for NOP in last instruction.
+     && (Data [Size - 2] == M68K_NOP_0) && (Data [Size - 1] == M68K_NOP_1) && (IsBinaryDataRange (Section, Size - 2, Size, NULL))
+    // Check for NOP in previous instruction (which would mean that we
+	// actually want the NOPs to be there).
+	 && (!((Data [Size - 4] == M68K_NOP_0) && (Data [Size - 3] == M68K_NOP_1)))
+	// Check if something prevents us from shrinking the section.
+	 && (CanShrinkSection (Section, Size - 2, NULL)))
+     	// Cut the NOP away.
+    	CutSection (Section, Size - 2);
+}
+
+// Fix and return the target offset of a reloc.
+// In particular, for 1-byte relative relocs, the target offset is
+// increased by 1, so the the reloc points to the symbol and not to
+// the symbol minus 1.
+OFFSET M68kFixTargetOffset (OFFSET Offset, SIZE RelocSize, BOOLEAN RelocRelative)
+{
+	if (RelocRelative && (RelocSize == 1))
+		return Offset + 1;
+	else
+		return Offset;
+}
+
+#define SHORT_IMPORTANCE 2048
+
+// Called by M68kGetSectionRelationship; see below.
+COUNT M68kGetRelocImportance (const RELOC *Reloc, OFFSET Offset)
+{
+	SECTION *Section = Reloc->Parent;
+	
+	if (Reloc->Unoptimizable) return 0;
+
+	// Byte offsets are useful only for jumps or branches, so detect
+	// them.
+	if (M68K_REL_OK (Offset, 1))
+	{
+		OFFSET RelocLocation = Reloc->Location;
+		OFFSET OpcodeLocation = RelocLocation - 2;
+		I1 *Opcode = Section->Data + OpcodeLocation;
+		if (Reloc->Size == 4)
+		{
+			// Check whether the reloc belongs to a branch.
+			BOOLEAN IsJMP = ((Opcode [0] == M68K_JMP_0)
+			                 && (Opcode [1] == M68K_JMP_1));
+			BOOLEAN IsJSR = ((Opcode [0] == M68K_JSR_0)
+			                 && (Opcode [1] == M68K_JSR_1));
+			
+			if ((IsJMP || IsJSR)
+			    && IsBinaryDataRange (Section,
+			                          OpcodeLocation,
+			                          OpcodeLocation + 6, Reloc))
+			{
+				return ((Offset == 4 && IsJMP) ? SHORT_IMPORTANCE * 2
+				                               : SHORT_IMPORTANCE);
+			}
+		}
+		else if (Reloc->Size == 2)
+		{
+			// Check whether the reloc belongs to a branch.
+			if ((Opcode [0] & M68K_Bcc_MASK_0) == M68K_Bcc_W_0
+			    && Opcode [1] == M68K_Bcc_W_1
+			    && IsBinaryDataRange (Section,
+			                          OpcodeLocation,
+			                          OpcodeLocation + 4, Reloc))
+			{
+				return (Offset == 2 && !(Opcode [0] == M68K_BSR_W_0
+				                         && Opcode [1] == M68K_BSR_W_1)) ? SHORT_IMPORTANCE * 2
+				                                                         : SHORT_IMPORTANCE;
+			}
+		}
+	}
+	
+	// Everything else is not optimizable.
+	return 0;
+}
+
+// Determines the amount of relationship between the two sections, for the
+// situation that Section2 might be put just behind Section1. A reference
+// that could potentially be short gets 2048 points; a reference that could
+// potentially be removed gets twice as much.
+// The effect is that a section with one potentially short reference can
+// be at most 2048 bytes long for being inserted immediately by reordering,
+// and so on.
+COUNT M68kGetSectionRelationship (const SECTION *Section1, const SECTION *Section2)
+{
+	COUNT Result = 0;
+	
+	const RELOC *Reloc;
+	for_each (Reloc, Section1->Relocs)
+	{
+		const SYMBOL *TargetSymbol = Reloc->Target.Symbol;
+		if (TargetSymbol && TargetSymbol->Parent == Section2)
+			Result += M68kGetRelocImportance (Reloc, Section1->Size - Reloc->Location + TargetSymbol->Location + Reloc->Target.Offset + Reloc->FixedOffset);
+	}
+	for_each (Reloc, Section2->Relocs)
+	{
+		const SYMBOL *TargetSymbol = Reloc->Target.Symbol;
+		if (TargetSymbol && TargetSymbol->Parent == Section1)
+			Result += M68kGetRelocImportance (Reloc, TargetSymbol->Location + Reloc->Target.Offset + Reloc->FixedOffset - Section1->Size - Reloc->Location);
+	}
+	
+	return Result;
+}

+ 68 - 0
bincode/fix_m68k.h

@@ -0,0 +1,68 @@
+/* fix_m68k.h: Routines for M68000 code fixup
+
+   Copyright (C) 2003 Sebastian Reichelt
+   Copyright (C) 2004 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef BINCODE_FIX_M68K_H
+#define BINCODE_FIX_M68K_H
+
+#include "../generic.h"
+#include "../data.h"
+
+// The alignment needed for sections with M68k code or data.
+#define M68K_SECTION_ALIGNMENT 2
+
+// Apply generic code fixes and optimizations to a section.
+void M68kFixCode (SECTION *Section);
+
+// Fix and optionally optimize code executable on the M68k processor family,
+// for two sections which are to be merged.
+// Src may be NULL.
+// If DestSize is nonzero, it specifies a fixed size for the destination
+// section, which will not change even when cutting ranges from it.
+void M68kFixCodePreMerge (SECTION *Dest, SECTION *Src, SIZE DestSize);
+
+// If the section ends with exactly one NOP instruction, remove the NOP.
+void M68kRemoveTrailingNOP (SECTION *Section);
+
+// Fix and possibly optimize a given relocation entry.
+// TargetDistance is the estimated (signed) distance of the relocation target,
+// including the fixed offset. OptimizeInfo contains information about what to
+// optimize.
+// The reloc may be removed during the process.
+void M68kFixReloc (RELOC *Reloc, OFFSET TargetDistance, OPTIMIZE_INFO *OptimizeInfo);
+
+// Checks if a specific reloc might be optimizable. This is currently
+// limited to 4-bytes absolute relocs because that is the only case
+// this is needed for.
+BOOLEAN M68kIsRelocOptimizable (const RELOC *Reloc);
+
+// Fix and return the target offset of a reloc.
+// In particular, for 1-byte relative relocs, the target offset is increased
+// by 1, so the the reloc points to the symbol and not to the symbol minus 1.
+OFFSET M68kFixTargetOffset (OFFSET Offset, SIZE RelocSize, BOOLEAN RelocRelative);
+
+// Determines the amount of relationship between the two sections, for the
+// situation that Section2 might be put just behind Section1. A reference
+// that could potentially be short gets 2048 points; a reference that could
+// potentially be removed gets twice as much.
+// The effect is that a section with one potentially short reference can
+// be at most 2048 bytes long for being inserted immediately by reordering,
+// and so on.
+COUNT M68kGetSectionRelationship (const SECTION *Section1, const SECTION *Section2);
+
+#endif

+ 36 - 0
bincode/fix_tios.c

@@ -0,0 +1,36 @@
+/* fix_tios.c: Routines for TIOS-specific M68000 code fixup
+
+   Copyright (C) 2003 Kevin Kofler
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "fix_tios.h"
+
+#include "tios.h"
+
+// Turn an M68000 absolute JRA/JSR to an F-Line jump.
+void M68kTIOSMakeFLineJump (RELOC *Reloc, I1 *Opcode, BOOLEAN IsJSR)
+{
+	if ((Reloc->Size == 4) && (!(Reloc->Relative)))
+	{
+		// Optimize the JRA or JSR into an F-Line BRA.L or BSR.L.
+		Opcode [0] = (IsJSR ? M68K_TIOS_BSR_L_0 : M68K_TIOS_BRA_L_0);
+		Opcode [1] = (IsJSR ? M68K_TIOS_BSR_L_1 : M68K_TIOS_BRA_L_1);
+		
+		// Change the reloc to 4-byte relative.
+		Reloc->Relative = TRUE;
+	}
+}

+ 29 - 0
bincode/fix_tios.h

@@ -0,0 +1,29 @@
+/* fix_tios.h: Routines for TIOS-specific M68000 code fixup
+
+   Copyright (C) 2003 Kevin Kofler
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef BINCODE_FIX_M68K_TIOS_H
+#define BINCODE_FIX_M68K_TIOS_H
+
+#include "../generic.h"
+#include "../data.h"
+
+// Turn an M68000 absolute JRA/JSR to an F-Line jump.
+void M68kTIOSMakeFLineJump (RELOC *Reloc, I1 *Opcode, BOOLEAN IsJSR);
+
+#endif

+ 196 - 0
bincode/m68k.h

@@ -0,0 +1,196 @@
+/* m68k.h: Definitions for M68000 code fixup
+
+   Copyright (C) 2003 Sebastian Reichelt
+   Copyright (C) 2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef BINCODE_M68K_H
+#define BINCODE_M68K_H
+
+#include "../generic.h"
+
+// *** Generic Definitions ***
+
+// Maximum distance for a relative reloc of the specified size.
+#define M68K_REL_MAX_2   0x7FFF
+#define M68K_REL_MIN_2 (-0x8000)
+#define M68K_REL_MAX_1     0x7F
+#define M68K_REL_MIN_1   (-0x7F)
+#define M68K_REL_MAX   M68K_REL_MAX_2
+#define M68K_REL_MIN   M68K_REL_MIN_2
+
+#define M68K_REL_OK(Distance,Size) (((Distance) >= M68K_REL_MIN_##Size) && ((Distance) <= M68K_REL_MAX_##Size))
+
+// Make a relative opcode out of an absolute one.
+#define M68K_MAKE_REL_OPCODE_1(Opcode_1) ((Opcode_1)++)
+
+// *** General Branches ***
+
+// Opcode of the unconditional absolute JMP instruction.
+#define M68K_JMP_0 0x4E
+#define M68K_JMP_1 0xF9
+
+#define M68K_BRA_W_0 0x60
+#define M68K_BRA_W_1 0x00
+
+#define M68K_BRA_S_0 0x60
+
+// Opcode of the Bcc.W instruction family (including BRA.W and BSR.W).
+#define M68K_Bcc_W_0 0x60
+#define M68K_Bcc_W_1 0x00
+// Opcode of the Bcc.S instruction family (including BRA.S and BSR.S).
+#define M68K_Bcc_S_0 0x60
+// Mask for the Bcc instruction family.
+#define M68K_Bcc_MASK_0 0xF0
+
+// *** Subroutine Branches ***
+
+// Opcode of the absolute JSR instruction.
+#define M68K_JSR_0 0x4E
+#define M68K_JSR_1 0xB9
+
+// Opcode of the BSR.W instruction.
+#define M68K_BSR_W_0 0x61
+#define M68K_BSR_W_1 0x00
+// Opcode of the BSR.S instruction.
+#define M68K_BSR_S_0 0x61
+
+// *** Move Instructions ***
+
+// Opcode of MOVE.x var.L,reg/(reg)/(reg)+.
+#define M68K_MOVE_ABS_REG_0 0x00
+#define M68K_MOVE_ABS_REG_1 0x39
+// Mask for MOVE.x var.L,reg/(reg)/(reg)+: 11000001 00111111.
+#define M68K_MOVE_ABS_REG_MASK_0 0xC1
+#define M68K_MOVE_ABS_REG_MASK_1 0x3F
+// Invalid instructions for MOVE.x var.L,reg/(reg)/(reg)+.
+// Everything starting with 0 on the first hex number is not a MOVE (0x0???).
+#define M68K_MOVE_ABS_REG_INV_0_0 0x00
+#define M68K_MOVE_ABS_REG_INV_0_MASK_0 0xF0
+// MOVE.B var.L,reg cannot use address registers for reg (0x1?7?).
+#define M68K_MOVE_ABS_REG_INV_1_0 0x10
+#define M68K_MOVE_ABS_REG_INV_1_1 0x70
+#define M68K_MOVE_ABS_REG_INV_1_MASK_0 0xF0
+#define M68K_MOVE_ABS_REG_INV_1_MASK_1 0xF0
+
+// Opcode of MOVE.x var.L,-(reg).
+#define M68K_MOVE_ABS_PREDEC_0 0x01
+#define M68K_MOVE_ABS_PREDEC_1 0x39
+// Mask for MOVE.x var.L,-(reg): 11000001 11111111.
+#define M68K_MOVE_ABS_PREDEC_MASK_0 0xC1
+#define M68K_MOVE_ABS_PREDEC_MASK_1 0xFF
+// Invalid instructions for MOVE.x var.L,-(reg).
+// Everything starting with 0 on the first hex number is not a MOVE (0x0???).
+#define M68K_MOVE_ABS_PREDEC_INV_0_0 0x00
+#define M68K_MOVE_ABS_PREDEC_INV_0_MASK_0 0xF0
+
+// Opcode of MOVE.x var.L,ofs(reg).
+#define M68K_MOVE_ABS_OFSREG_0 0x01
+#define M68K_MOVE_ABS_OFSREG_1 0x79
+// Mask for MOVE.x var.L,ofs(reg): 11000001 11111111.
+#define M68K_MOVE_ABS_OFSREG_MASK_0 0xC1
+#define M68K_MOVE_ABS_OFSREG_MASK_1 0xFF
+// Invalid instructions for MOVE.x var.L,ofs(reg).
+// Everything starting with 0 on the first hex number is not a MOVE (0x0???).
+#define M68K_MOVE_ABS_OFSREG_INV_0_0 0x00
+#define M68K_MOVE_ABS_OFSREG_INV_0_MASK_0 0xF0
+
+// Opcode of MOVEM.x var.L,regs
+#define M68K_MOVEM_ABS_REGS_0 0x4C
+#define M68K_MOVEM_ABS_REGS_1 0xB9
+// Mask for MOVEM.x var.L,regs: 11111111 10111111.
+#define M68K_MOVEM_ABS_REGS_MASK_0 0xFF
+#define M68K_MOVEM_ABS_REGS_MASK_1 0xBF
+
+// Opcode of LEA(.L) var.L,reg.
+#define M68K_LEA_ABS_0 0x41
+#define M68K_LEA_ABS_1 0xF9
+// Mask for LEA(.L) var.L,reg: 11110001 11111111.
+#define M68K_LEA_ABS_MASK_0 0xF1
+#define M68K_LEA_ABS_MASK_1 0xFF
+
+// Opcode of PEA(.L) var.L.
+#define M68K_PEA_ABS_0 0x48
+#define M68K_PEA_ABS_1 0x79
+
+// *** Test Instructions ***
+
+// Opcode of CMP.x var.L,reg.
+#define M68K_CMP_ABS_REG_0 0xB0
+#define M68K_CMP_ABS_REG_1 0x39
+// Mask for CMP.x var.L,reg: 11110001 00111111.
+#define M68K_CMP_ABS_REG_MASK_0 0xF1
+#define M68K_CMP_ABS_REG_MASK_1 0x3F
+// The second byte cannot start with F (0x??F?).
+#define M68K_CMP_ABS_REG_INV_0_1 0xF0
+#define M68K_CMP_ABS_REG_INV_0_MASK_1 0xF0
+
+// Opcode of BTST reg,var.L.
+#define M68K_BTST_REG_ABS_0 0x01
+#define M68K_BTST_REG_ABS_1 0x39
+// Mask for BTST reg,var.L: 11110001 11111111.
+#define M68K_BTST_REG_ABS_MASK_0 0xF1
+#define M68K_BTST_REG_ABS_MASK_1 0xFF
+
+// Opcode of BTST #num,var.L.
+#define M68K_BTST_IMM_ABS_0 0x08
+#define M68K_BTST_IMM_ABS_1 0x39
+#define M68K_BTST_IMM_ABS_2 0x00
+
+// *** Calculation Instructions ***
+
+// 1st Opcode of ADD/SUB.x var.L,reg.
+#define M68K_ADDSUB_ABS_REG_0_0 0x90
+#define M68K_ADDSUB_ABS_REG_0_1 0x39
+// 1st Mask for ADD/SUB.x var.L,reg: 10110001 00111111.
+#define M68K_ADDSUB_ABS_REG_0_MASK_0 0xB1
+#define M68K_ADDSUB_ABS_REG_0_MASK_1 0x3F
+
+// 2nd Opcode of ADD/SUB.x var.L,reg.
+#define M68K_ADDSUB_ABS_REG_1_0 0x91
+#define M68K_ADDSUB_ABS_REG_1_1 0xF9
+// 2nd Mask for ADD/SUB.x var.L,reg: 10110001 11111111.
+#define M68K_ADDSUB_ABS_REG_1_MASK_0 0xB1
+#define M68K_ADDSUB_ABS_REG_1_MASK_1 0xFF
+
+// Opcode of MULx/DIVx.x var.L,reg.
+#define M68K_MULDIV_ABS_REG_0 0x80
+#define M68K_MULDIV_ABS_REG_1 0xF9
+// Mask for MULx/DIVx.x var.L,reg.: 10110000 11111111.
+#define M68K_MULDIV_ABS_REG_MASK_0 0xB0
+#define M68K_MULDIV_ABS_REG_MASK_1 0xFF
+
+// Opcode of AND/OR.x var.L,reg.
+#define M68K_ANDOR_ABS_REG_0 0x80
+#define M68K_ANDOR_ABS_REG_1 0x39
+// Mask for AND/OR.x var.L,reg.: 10110001 00111111.
+#define M68K_ANDOR_ABS_REG_MASK_0 0xB1
+#define M68K_ANDOR_ABS_REG_MASK_1 0x3F
+// The second byte cannot start with F (0x??F?). Otherwise it is a MULx/DIVx.
+#define M68K_ANDOR_ABS_REG_INV_0_1 0xF0
+#define M68K_ANDOR_ABS_REG_INV_0_MASK_1 0xF0
+
+// *** Other Instructions ***
+
+// Opcode of the NOP instruction.
+#define M68K_NOP_0 0x4E
+#define M68K_NOP_1 0x71
+
+// Opcode of the RTS instruction.
+#define M68K_RTS_0 0x4E
+#define M68K_RTS_1 0x75
+
+#endif

+ 32 - 0
bincode/tios.h

@@ -0,0 +1,32 @@
+/* tios.h: Definitions for TIOS-specific M68000 code fixup
+
+   Copyright (C) 2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef BINCODE_M68K_TIOS_H
+#define BINCODE_M68K_TIOS_H
+
+#include "../generic.h"
+
+// F-Line opcode of the BRA.L branch (AMS 2.04 or higher).
+#define M68K_TIOS_BRA_L_0 0xFF
+#define M68K_TIOS_BRA_L_1 0xF1
+
+// F-Line opcode of the BSR.L branch (AMS 2.04 or higher).
+#define M68K_TIOS_BSR_L_0 0xFF
+#define M68K_TIOS_BSR_L_1 0xF0
+
+#endif

+ 279 - 0
constmrg.c

@@ -0,0 +1,279 @@
+/* constmrg.c: Routines to merge constants
+
+   Copyright (C) 2004 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "generic.h"
+#include "data.h"
+#include "constmrg.h"
+#include "manip.h"
+#include "bincode/cutrange.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef min
+#define min(a,b) ({typeof(a) __a = (a); typeof(b) __b = (b); (__a < __b) ? __a : __b;})
+#endif
+#ifndef max
+#define max(a,b) ({typeof(a) __a = (a); typeof(b) __b = (b); (__a > __b) ? __a : __b;})
+#endif
+
+// Merge constants (including strings) in sections marked Mergeable to avoid
+// duplication. A constant is considered to be an interval enclosed within 2
+// label groups, where a label group is the equivalence class formed by the
+// symbols at a given offset. A constant is said unaligned if the section
+// containing it also has the Unaligned flag set, and aligned otherwise. The
+// value of a constant is said to be the contents of the interval defining it.
+// The algorithm used is a prefix merge algorithm:
+// * If the value of a constant A is a prefix of the value of a constant B, and
+//   A is unaligned and/or B is aligned, then A is identified with B. 
+// * If the value of a constant A is a strict prefix of the value of a constant
+//   B, A is aligned and B is unaligned, then B is split into a new aligned
+//   section, and A is identified with the so-created B.
+// Identification works the following way: The label group defining A is moved
+// into the label group defining B. Then, if A was constituting an entire
+// section, the section is deleted. Otherwise, A is removed using the
+// range-cutting API.
+// The linker doesn't look for identical constants in the same section. It is
+// the compiler's job to merge constants in this case.
+void MergeConstants (PROGRAM *Program)
+{
+	// Loop through all constants.
+	SECTION *Section1, *NextSection1;
+	for (Section1 = GetFirst (Program->Sections); Section1; Section1 = NextSection1)
+	{
+		SYMBOL *Symbol1, *NextSymbol1;
+		NextSection1 = GetNext (Section1);
+		if (!Section1->Mergeable) continue; // Ignore non-mergeable sections.
+		for (Symbol1 = GetFirst(Section1->Symbols); Symbol1; Symbol1 = NextSymbol1)
+		{
+			// Compute the length of the constant.
+			OFFSET Constant1Start = Symbol1->Location, Constant1End, Constant1Length;
+			SECTION *Section2, *NextSection2;
+			NextSymbol1 = FindSymbolAtPos (Section1, Symbol1->Location + 1, TRUE);
+			Constant1End = NextSymbol1 ? NextSymbol1->Location : Section1->Size;
+			Constant1Length = Constant1End - Constant1Start;
+			// Loop through all constants in the following sections.
+			for (Section2 = NextSection1; Section2; Section2 = NextSection2)
+			{
+				SYMBOL *Symbol2, *NextSymbol2;
+				NextSection2 = GetNext (Section2);
+				if (!Section2->Mergeable) continue; // Ignore non-mergeable sections.
+				for (Symbol2 = GetFirst(Section2->Symbols); Symbol2; Symbol2 = NextSymbol2)
+				{
+					// Compute the length of the constant.
+					OFFSET Constant2Start = Symbol2->Location, Constant2End, Constant2Length;
+					NextSymbol2 = FindSymbolAtPos (Section2, Symbol2->Location + 1, TRUE);
+					Constant2End = NextSymbol2 ? NextSymbol2->Location : Section2->Size;
+					Constant2Length = Constant2End - Constant2Start;
+					// Check if one constant is a prefix of the other.
+					if (memcmp(Section1->Data + Constant1Start, Section2->Data + Constant2Start,
+					           min (Constant1Length, Constant2Length)))
+						continue;
+					// Now we know it is. We must now decide which constant to merge into which.
+					// Try direct merge of Constant2 into Constant1 first.
+					if ((Constant2Length <= Constant1Length) && (!Section1->Unaligned || Section2->Unaligned))
+					{
+						// Move the label group of Constant2 into Constant1.
+						SYMBOL *CurSymbol, *NextSymbol;
+						for (CurSymbol = Symbol2; CurSymbol && CurSymbol->Location == Constant2Start; CurSymbol = NextSymbol)
+						{
+							NextSymbol = GetNext (CurSymbol);
+							// Don't move the section symbol.
+							if (CurSymbol == Section2->SectionSymbol) continue;
+							Unlink (Section2->Symbols, CurSymbol);
+							CurSymbol->Parent = Section1;
+							CurSymbol->Location = Constant1Start;
+							InsertBefore (Section1->Symbols, CurSymbol, NextSymbol1);
+						}
+						// Delete Constant2.
+						if (Constant2Length == Section2->Size)
+						{
+							// Move the section symbol. We are deleting the section, so we need to
+							// put the symbol somewhere else. It should never be referenced anyway,
+							// but if it is, we don't want the linker to segfault. Do this here,
+							// because even when not explicitly skipping it in the loop above, it
+							// won't always be caught by the loop.
+							Unlink (Section2->Symbols, Section2->SectionSymbol);
+							Section2->SectionSymbol->Parent = Section1;
+							Section2->SectionSymbol->Location = Constant1Start;
+							InsertBefore (Section1->Symbols, Section2->SectionSymbol, NextSymbol1);
+							// If the Section2 was the next Section1, we must update NextSection1.
+							if (Section2 == NextSection1)
+								NextSection1 = GetNext (NextSection1);
+							// Free the section.
+							FreeSection (Section2);
+						}
+						else
+						{
+							// We don't check CanCutRange here. Mergeable ranges are supposed to be always cuttable.
+							CutRange (Section2, Constant2Start, Constant2End);
+							// Realloc the data now, we don't know if we will cut other ranges.
+							FinalizeRangeCutting (Section2);
+						}
+						// Proceed normally. If we deleted Section2, then NextSymbol2 is NULL and we'll exit the inner loop
+						// immediately. Otherwise, we can continue with NextSymbol2.
+					}
+					// Try direct merge of Constant1 into Constant2.
+					else if ((Constant1Length <= Constant2Length) && (!Section2->Unaligned || Section1->Unaligned))
+					{
+						// Move the label group of Constant1 into Constant2.
+						SYMBOL *CurSymbol, *NextSymbol;
+						for (CurSymbol = Symbol1; CurSymbol && CurSymbol->Location == Constant1Start; CurSymbol = NextSymbol)
+						{
+							NextSymbol = GetNext (CurSymbol);
+							// Don't move the section symbol.
+							if (CurSymbol == Section1->SectionSymbol) continue;
+							Unlink (Section1->Symbols, CurSymbol);
+							CurSymbol->Parent = Section2;
+							CurSymbol->Location = Constant2Start;
+							InsertBefore (Section2->Symbols, CurSymbol, NextSymbol2);
+						}
+						// Delete Constant1.
+						if (Constant1Length == Section1->Size)
+						{
+							// Move the section symbol. We are deleting the section, so we need to
+							// put the symbol somewhere else. It should never be referenced anyway,
+							// but if it is, we don't want the linker to segfault. Do this here,
+							// because even when not explicitly skipping it in the loop above, it
+							// won't always be caught by the loop.
+							Unlink (Section1->Symbols, Section1->SectionSymbol);
+							Section1->SectionSymbol->Parent = Section2;
+							Section1->SectionSymbol->Location = Constant2Start;
+							InsertBefore (Section2->Symbols, Section1->SectionSymbol, NextSymbol2);
+							// Free the section.
+							FreeSection (Section1);
+						}
+						else
+						{
+							// We don't check CanCutRange here. Mergeable ranges are supposed to be always cuttable.
+							CutRange (Section1, Constant1Start, Constant1End);
+							// Realloc the data now, we don't know if we will cut other ranges.
+							FinalizeRangeCutting (Section1);
+						}
+						// Exit the inner loops. We need a new Symbol1. (We will get back to Symbol2 later.)
+						goto NewSymbol1;
+					}
+					// Otherwise we have to throw away both Constant1 and Constant2 and create a completely new section.
+					else
+					{
+						// Create a new section, initialize it, and append it to the list of sections.
+						SECTION *Section = calloc (1, sizeof (SECTION));
+						if (!Section)
+						{
+OutOfMem:
+							Warning (NULL, "Out of memory during constant merging.");
+							return;
+						}
+						Section->Parent = Program;
+						Section->Initialized = TRUE;
+						Section->Mergeable = TRUE;
+						Section->Referenced = TRUE;
+						Section->Size = max (Constant1Length, Constant2Length);
+						Section->Data = malloc (Section->Size);
+						if (!Section->Data)
+						{
+FreeAndOutOfMem:
+							free (Section);
+							goto OutOfMem;
+						}
+						memcpy (Section->Data, Constant1Length >= Constant2Length ? Section1->Data + Constant1Start : Section2->Data + Constant2Start, Section->Size);
+						Section->FileName = Constant1Length >= Constant2Length ? Section1->FileName : Section2->FileName;
+						// Create a new section symbol. We can't use the Constant1 symbol because we can't move section symbols into other sections,
+						// but we may have to move the Constant1 symbol into another section.
+						if (!CreateSectionSymbol (Section, ".constmerge.auto")) goto FreeAndOutOfMem;
+						Append (Program->Sections, Section);
+						{
+							SYMBOL *CurSymbol, *NextSymbol;
+							// Move the label group of Constant1 into the new section.
+							for (CurSymbol = Symbol1; CurSymbol && CurSymbol->Location == Constant1Start; CurSymbol = NextSymbol)
+							{
+								NextSymbol = GetNext (CurSymbol);
+								// Don't move the section symbol.
+								if (CurSymbol == Section1->SectionSymbol) continue;
+								Unlink (Section1->Symbols, CurSymbol);
+								CurSymbol->Parent = Section;
+								CurSymbol->Location = 0;
+								Append (Section->Symbols, CurSymbol);
+							}
+							// Move the label group of Constant2 into the new section.
+							for (CurSymbol = Symbol2; CurSymbol && CurSymbol->Location == Constant2Start; CurSymbol = NextSymbol)
+							{
+								NextSymbol = GetNext (CurSymbol);
+								// Don't move the section symbol.
+								if (CurSymbol == Section2->SectionSymbol) continue;
+								Unlink (Section2->Symbols, CurSymbol);
+								CurSymbol->Parent = Section;
+								CurSymbol->Location = 0;
+								Append (Section->Symbols, CurSymbol);
+							}
+						}
+						// Delete Constant1.
+						if (Constant1Length == Section1->Size)
+						{
+							// Move the section symbol. We are deleting the section, so we need to
+							// put the symbol somewhere else. It should never be referenced anyway,
+							// but if it is, we don't want the linker to segfault. Do this here,
+							// because even when not explicitly skipping it in the loop above, it
+							// won't always be caught by the loop.
+							Unlink (Section1->Symbols, Section1->SectionSymbol);
+							Section1->SectionSymbol->Parent = Section;
+							Append (Section->Symbols, Section1->SectionSymbol);
+							// Free the section.
+							FreeSection (Section1);
+						}
+						else
+						{
+							// We don't check CanCutRange here. Mergeable ranges are supposed to be always cuttable.
+							CutRange (Section1, Constant1Start, Constant1End);
+							// Realloc the data now, we don't know if we will cut other ranges.
+							FinalizeRangeCutting (Section1);
+						}
+						// Delete Constant2.
+						if (Constant2Length == Section2->Size)
+						{
+							// Move the section symbol. We are deleting the section, so we need to
+							// put the symbol somewhere else. It should never be referenced anyway,
+							// but if it is, we don't want the linker to segfault. Do this here,
+							// because even when not explicitly skipping it in the loop above, it
+							// won't always be caught by the loop.
+							Unlink (Section2->Symbols, Section2->SectionSymbol);
+							Section2->SectionSymbol->Parent = Section;
+							Append (Section->Symbols, Section2->SectionSymbol);
+							// If the Section2 was the next Section1, we must update NextSection1.
+							if (Section2 == NextSection1)
+								NextSection1 = GetNext (NextSection1);
+							// Free the section.
+							FreeSection (Section2);
+						}
+						else
+						{
+							// We don't check CanCutRange here. Mergeable ranges are supposed to be always cuttable.
+							CutRange (Section2, Constant2Start, Constant2End);
+							// Realloc the data now, we don't know if we will cut other ranges.
+							FinalizeRangeCutting (Section2);
+						}
+						// Exit the inner loops. We need a new Symbol1. (If another symbol matches, we will get back to it later.)
+						goto NewSymbol1;
+					}
+				}
+			}
+NewSymbol1:;
+		}
+	}
+}
+

+ 46 - 0
constmrg.h

@@ -0,0 +1,46 @@
+/* constmrg.h: Routines to merge constants
+
+   Copyright (C) 2004 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef CONSTMRG_H
+#define CONSTMRG_H
+
+#include "generic.h"
+#include "data.h"
+
+// Merge constants (including strings) in sections marked Mergeable to avoid
+// duplication. A constant is considered to be an interval enclosed within 2
+// label groups, where a label group is the equivalence class formed by the
+// symbols at a given offset. A constant is said unaligned if the section
+// containing it also has the Unaligned flag set, and aligned otherwise. The
+// value of a constant is said to be the contents of the interval defining it.
+// The algorithm used is a prefix merge algorithm:
+// * If the value of a constant A is a prefix of the value of a constant B, and
+//   A is unaligned and/or B is aligned, then A is identified with B. 
+// * If the value of a constant A is a strict prefix of the value of a constant
+//   B, A is aligned and B is unaligned, then B is split into a new aligned
+//   section, and A is identified with the so-created B.
+// Identification works the following way: The label group defining A is moved
+// into the label group defining B. Then, if A was constituting an entire
+// section, the section is deleted. Otherwise, A is removed using the
+// range-cutting API.
+// The linker doesn't look for identical constants in the same section. It is
+// the compiler's job to merge constants in this case.
+void MergeConstants (PROGRAM *Program);
+
+#endif
+

+ 277 - 0
data.h

@@ -0,0 +1,277 @@
+/* data.h: Definitions for internal data handling
+
+   Copyright (C) 2002-2004 Sebastian Reichelt
+   Copyright (C) 2003-2004 Kevin Kofler
+   Copyright (C) 2004 Billy Charvet
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef DATA_H
+#define DATA_H
+
+#include "generic.h"
+#include "lists.h"
+#include "intrface.h"
+
+#define MAX_SYM_LEN 255
+
+struct PROGRAM;
+struct SECTION;
+struct SEGMENT;
+struct SYMBOL;
+struct RELOC;
+struct ROM_CALL;
+struct RAM_CALL;
+struct LIB_CALL;
+struct LIBRARY;
+struct ARCHIVE;
+struct ARCHIVE_OBJECT;
+struct ARCHIVE_SYMBOL;
+struct GLOBAL_IMPORT;
+
+// Program Type Enumeration
+// PT_NATIVE:     ld-tigcc native mode; requires the manual definition of at least one startup section; not for use with Fargo and nostub DLLs
+// PT_NOSTUB:     Traditional nostub mode; execution starts at the beginning of the first section
+// PT_KERNEL:     Traditional kernel mode; kernel stub is included automatically
+// PT_NOSTUB_DLL: Nostub DLL mode; uses specific output format
+// PT_FLASH_OS:   Flash OS mode: uses specific output and file format
+// PT_FARGO:      Fargo II mode; uses specific output format
+typedef enum {PT_NATIVE = 1, PT_NOSTUB = 2, PT_KERNEL = 0, PT_NOSTUB_DLL = 3, PT_FLASH_OS = 4, PT_FARGO = 5} ProgramTypes;
+
+typedef I4 VERSION;
+
+// Named Location in a Section
+typedef struct {
+	struct SYMBOL *Symbol;  // Pointer to symbol, may be NULL.
+	const char *SymbolName; // Symbol name. Must free if Symbol is NULL. Otherwise points to Symbol->Name. May not be NULL.
+	OFFSET Offset;          // Offset in relation to the symbol. When optimizing, this is considered part of the address.
+	BOOLEAN Builtin;        // Specifies whether the location is to be replaced by a built-in number.
+} LOCATION;
+
+// Section Markers
+typedef struct {
+	struct SYMBOL *Start, *End;
+} SECTION_MARKERS;
+
+// Complete Program
+typedef struct PROGRAM {
+	ProgramTypes Type;    // The target type of the program.
+	BOOLEAN Library;      // Specifies whether this is a library.
+	unsigned int Calcs;   // The calculators the program is designed for (constants defined by enum ProgramCalcs).
+	VERSION Version;      // Version number of the program or library.
+	I2 KernelFlags;       // Kernel program flags; only used in kernel mode.
+	struct {
+		LIST_HEADER(struct SECTION);
+	} Sections;           // Sections in the program.
+	struct {
+		LIST_HEADER(struct LIBRARY);
+		COUNT ReferencedCount; // The number of referenced libraries.
+	} Libraries;          // Run-time libraries used by the program.
+	struct {
+		LIST_HEADER(struct ARCHIVE);
+	} Archives;           // Available archives which object files can be imported from.
+	struct {
+		LIST_HEADER(struct GLOBAL_IMPORT);
+		BOOLEAN ResolveInversions; // Specifies whether negated symbols should be processed. Otherwise, they are delayed.
+	} GlobalImports;      // Global imports; see AddGlobalImport in manip.h.
+	BOOLEAN
+		ResolveAllBuiltins, // Specifies that all builtin symbols should be resolved now, e.g. to 0 or NULL in some cases.
+		IgnoreGlobalImports, // Specifies that the __ref_all_... symbol has no effect.
+		Frozen;           // Specifies that no positions, numbers, and sizes may be changed any more.
+	LOCATION EntryPoint;  // Entry point of the program.
+	struct SECTION
+		*MainSection,     // Pointer to the main section, as soon as it is known. Usually only this section has to be written to the file.
+		*DataSection,     // Pointer to the data section, if code and data are separated.
+		*BSSSection;      // Pointer to the BSS section, if the program contains one.
+	struct GLOBAL_IMPORT
+		*BSSImport;       // Pointer to the global import which handles the BSS section.
+	SECTION_MARKERS
+		Constructors,     // Beginning and end of constructor section.
+		Destructors;      // Beginning and end of destructor section.
+	OFFSET
+		HighestROMCall,   // Holds the highest ROM call number used.
+		HighestRAMCall;   // Holds the highest RAM call number used.
+#ifdef DATA_VAR_SUPPORT
+	DATA_VAR_INFO
+		*DataVarInfo;     // Data variable settings.
+#endif /* DATA_VAR_SUPPORT */
+	OPTIMIZE_INFO
+		*OptimizeInfo;    // Optimization settings and results.
+} PROGRAM;
+
+// Section in a Program
+typedef struct SECTION {
+	LIST_ITEM_HEADER(struct SECTION);
+	PROGRAM *Parent;
+	SIZE Size;            // Size of the section, in bytes.
+	I1 *Data;             // Pointer to section data, NULL if no data. Need to free this at the end.
+	BOOLEAN Initialized;  // If Data is NULL, this indicates whether the section is initialized with zeroes; otherwise it is always true.
+	BOOLEAN Code;         // Contains code. If this is true, Initialized must be true as well.
+	BOOLEAN Mergeable;    // Specifies whether this section contains one or more constants which may be merged on a symbol basis.
+	BOOLEAN Unaligned;    // Specifies whether this section can be placed at an unaligned address. If the section is mergeable, it is also assumed that each symbol can be unaligned.
+	                      // (The latter case is the only one currently implemented.)
+	BOOLEAN Referenced;   // Specifies whether the section is referenced somewhere (in a reloc).
+	OFFSET StartupNumber; // If nonzero, specifies the location of the section in respect to other startup sections.
+	BOOLEAN Constructors; // Is a vector of constructor functions.
+	BOOLEAN Destructors;  // Is a vector of destructor functions.
+	BOOLEAN CanCutRanges; // Range cutting is allowed at least in parts of the section (i.e. the section or segments in it do not contain any implicit relative relocs).
+	BOOLEAN Frozen;       // Aside from trivial changes (i.e. changes to binary data or additions at the end), this section must stay as it is.
+	BOOLEAN Handled;      // Specifies whether the section is already handled and doesn't need any further treatment. This implies Frozen.
+	struct {
+		LIST_HEADER(struct SEGMENT);
+	} Segments;           // Sorted list of segments. If empty, this means the section consists of one big segment.
+	struct {
+		LIST_HEADER(struct SYMBOL);
+	} Symbols;            // Sorted list of symbols (labels).
+	struct {
+		LIST_HEADER(struct RELOC);
+		COUNT UnresolvedCount; // The number of unresolved relocs and reloc relations.
+		COUNT EmittedCount; // The number of already emitted relocs (which have been removed or changed to program-relative ones).
+		COUNT OptRefCount; // The number of optimizable references pointing from and to this section.
+		BOOLEAN RelativeRefs; // Specifies whether there are relative references from or to this section.
+		BOOLEAN StartupRefs; // Specifies whether this section references or is referenced by a startup section.
+	} Relocs;             // Sorted list of relocation entries.
+	struct {
+		LIST_HEADER(struct ROM_CALL);
+		BOOLEAN Handled;  // Specifies whether the program has requested information about ROM calls.
+	} ROMCalls;           // Sorted list of ROM (AMS) calls.
+	struct {
+		LIST_HEADER(struct RAM_CALL);
+		BOOLEAN Handled;  // Specifies whether the program has requested information about RAM calls.
+	} RAMCalls;           // Sorted list of RAM (kernel) calls.
+	struct {
+		LIST_HEADER(struct LIB_CALL);
+		BOOLEAN Handled;  // Specifies whether the program has requested information about library calls.
+	} LibCalls;           // Sorted list of library calls.
+	struct SYMBOL
+		*SectionSymbol;   // Symbol pointing to the beginning of the section. May only be NULL on fatal errors.
+	const char *FileName; // File where the section originated from. May be NULL.
+} SECTION;
+
+// Segment of a Section.
+// Identifies a range that belongs to a specific pre-merge section.
+typedef struct SEGMENT {
+	LIST_ITEM_HEADER(struct SEGMENT);
+	SECTION_MARKERS Location; // Symbols specifying the beginning and end of the segment.
+	BOOLEAN Code;         // The segment contains code.
+	BOOLEAN CanCutRanges; // Range cutting is allowed (i.e. the segment does not contain any implicit relative relocs).
+	const char *FileName; // File where the segment originated from. May be NULL.
+} SEGMENT;
+
+// Symbol (Label) in a Section
+typedef struct SYMBOL {
+	LIST_ITEM_HEADER(struct SYMBOL);
+	SECTION *Parent;
+	OFFSET Location;      // Location of the symbol inside the section data.
+	char Name[MAX_SYM_LEN+1]; // Symbol name.
+	BOOLEAN Exported;     // Only exported symbols are possible targets for name resolution.
+} SYMBOL;
+
+// Relocation Entry in a Section
+typedef struct RELOC {
+	LIST_ITEM_HEADER(struct RELOC);
+	SECTION *Parent;
+	OFFSET Location;      // Location of the reloc inside the section data.
+	SIZE Size;            // Size of the reloc, in bytes.
+	LOCATION Target;      // The location of the target the reloc points to.
+	BOOLEAN Relative;     // If true, the value to be inserted is relative to the location of the reloc. If false, it is the absolute address of the target.
+	LOCATION *Relation;   // If Relative is true, this specifies that the value is not relative to the location of the reloc, but to this location. It must be freed.
+	OFFSET FixedOffset;   // Fixed offset to add after relocation. When optimizing (i.e., removing code), this is NOT considered part of the address.
+	BOOLEAN Unoptimizable;// If true, the reloc is not a source operand or branch target and can never be optimized.
+} RELOC;
+
+// ROM Call Reference in a Section
+typedef struct ROM_CALL {
+	LIST_ITEM_HEADER(struct ROM_CALL);
+	SECTION *Parent;
+	OFFSET Location;      // Location of the ROM call reference inside the section data.
+	SIZE Size;            // Size of the ROM call reference, in bytes.
+	OFFSET Number;        // Number of the ROM call.
+	OFFSET FixedOffset;   // Offset in relation to the destination.
+} ROM_CALL;
+
+// RAM Call Reference in a Section
+typedef struct RAM_CALL {
+	LIST_ITEM_HEADER(struct RAM_CALL);
+	SECTION *Parent;
+	OFFSET Location;      // Location of the RAM call reference inside the section data.
+	SIZE Size;            // Size of the RAM call reference, in bytes.
+	OFFSET Number;        // Number of the RAM call.
+	OFFSET FixedOffset;   // Offset in relation to the destination.
+	BOOLEAN ExtraRAMAddr; // Specifies whether we mean a program-defined extra RAM table address.
+} RAM_CALL;
+
+// Run-Time Library Used by the Program.
+typedef struct LIBRARY {
+	LIST_ITEM_HEADER(struct LIBRARY);
+	PROGRAM *Parent;
+	char Name[MAX_SYM_LEN+1]; // Library Name.
+	VERSION Version;      // Required minimum version number.
+	OFFSET Highest;       // Holds the highest function number imported from this library.
+	BOOLEAN Referenced;   // Library is actually used.
+} LIBRARY;
+
+// Library Call Reference in a Section
+typedef struct LIB_CALL {
+	LIST_ITEM_HEADER(struct LIB_CALL);
+	SECTION *Parent;
+	OFFSET Location;      // Location of the library call reference inside the section data.
+	SIZE Size;            // Size of the library call reference, in bytes.
+	LIBRARY *Library;     // Library the call references.
+	OFFSET Number;        // Number of the library export.
+	OFFSET FixedOffset;   // Offset in relation to the destination.
+} LIB_CALL;
+
+// Archive Available to the Program
+typedef struct ARCHIVE {
+	LIST_ITEM_HEADER(struct ARCHIVE);
+	PROGRAM *Parent;
+	const I1 *Data;       // Pointer to archive contents. Need to free this at the end.
+	SIZE Size;            // Size of the archive, in bytes.
+	struct {
+		LIST_HEADER(struct ARCHIVE_OBJECT);
+	} ObjectFiles;        // List of known object files.
+	struct {
+		LIST_HEADER(struct ARCHIVE_SYMBOL);
+	} Symbols;            // List of exported symbols.
+	const char *FileName; // File name of the archive.
+} ARCHIVE;
+
+// Object File in an Archive
+typedef struct ARCHIVE_OBJECT {
+	LIST_ITEM_HEADER(struct ARCHIVE_OBJECT);
+	ARCHIVE *Parent;
+	FILE_PTR FileOffset;  // Offset of the object file inside the archive.
+	BOOLEAN Imported;     // Specifies whether the file has already been imported.
+} ARCHIVE_OBJECT;
+
+// Exported Symbol in an Archive
+typedef struct ARCHIVE_SYMBOL {
+	LIST_ITEM_HEADER(struct ARCHIVE_SYMBOL);
+	ARCHIVE *Parent;
+	const char *Name;     // Symbol name (pointer into Parent->Data).
+	ARCHIVE_OBJECT *ObjectFile; // Object file this symbol belongs to.
+	BOOLEAN ContainsInversion; // Specifies whether the symbol contains an inversion (NOT operator) for global imports.
+} ARCHIVE_SYMBOL;
+
+// Global Import
+typedef struct GLOBAL_IMPORT {
+	LIST_ITEM_HEADER(struct GLOBAL_IMPORT);
+	PROGRAM *Parent;
+	char SymbolName[MAX_SYM_LEN+1]; // Symbol name.
+	BOOLEAN Succeeded;    // Specifies whether a symbol matching this import has been found.
+} GLOBAL_IMPORT;
+
+#endif

+ 249 - 0
dump.c

@@ -0,0 +1,249 @@
+/* dump.c: Routines to dump the internal data to a file
+
+   Copyright (C) 2002-2004 Sebastian Reichelt
+   Copyright (C) 2003-2004 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "dump.h"
+
+#ifdef ENABLE_DUMP
+
+#include "integers.h"
+#include "manip.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define FixIndent \
+	SIZE NewLen = (Indent ? strlen (Indent) : 0) + 2; \
+	char NextIndent[NewLen+1]; \
+	if (!Indent) \
+		Indent = ""; \
+	strcpy (NextIndent, Indent); \
+	NextIndent [NewLen - 2] = ' '; \
+	NextIndent [NewLen - 1] = ' '; \
+	NextIndent [NewLen] = 0;
+
+void PrintOffset (FILE *File, OFFSET Offset)
+{
+	if (Offset)
+	{
+		if (Offset > 0)
+			fprintf (File, " + %ld", (long) Offset);
+		else
+			fprintf (File, " - %ld", (long) (-Offset));
+	}
+}
+
+void DumpProgram (FILE *File, const char *Indent, const PROGRAM *Program)
+{
+	const SECTION *Section;
+	const GLOBAL_IMPORT *GlobalImport;
+	
+	FixIndent;
+	
+	if (Program->Frozen)
+		fprintf (File, "%sFrozen\n", Indent);
+	
+	for_each (Section, Program->Sections)
+	{
+		fprintf (File, "%sSection\n", Indent);
+		DumpSection (File, NextIndent, Section);
+	}
+	
+	if (!(IsEmpty (Program->GlobalImports)))
+	{
+		fprintf (File, "%sGlobal Imports\n", Indent);
+		for_each (GlobalImport, Program->GlobalImports)
+			fprintf (File, "%s  %s\n", Indent, GlobalImport->SymbolName);
+	}
+}
+
+void DumpSection (FILE *File, const char *Indent, const SECTION *Section)
+{
+	FixIndent;
+	
+	if (Section->FileName)
+		fprintf (File, "%sFile: %s\n", Indent, Section->FileName);
+	if (Section->StartupNumber)
+		fprintf (File, "%sStartup Section: %ld\n", Indent, (long) (Section->StartupNumber));
+	if (!(Section->Referenced))
+		fprintf (File, "%sNot Referenced\n", Indent);
+	if (Section->Frozen)
+		fprintf (File, "%sFrozen\n", Indent);
+	if (Section->CanCutRanges)
+		fprintf (File, "%sCan Cut Ranges\n", Indent);
+	if (Section->Mergeable)
+		fprintf (File, "%sMergeable\n", Indent);
+	if (Section->Unaligned)
+		fprintf (File, "%sUnaligned\n", Indent);
+	fprintf (File, Section->Code ? "%sCode\n" : "%sData\n", Indent);
+	
+	{
+		const SEGMENT  *NextSegment = GetFirst (Section->Segments);
+		const SYMBOL   *NextSymbol  = GetFirst (Section->Symbols);
+		const RELOC    *NextReloc   = GetFirst (Section->Relocs);
+		const ROM_CALL *NextROMCall = GetFirst (Section->ROMCalls);
+		const RAM_CALL *NextRAMCall = GetFirst (Section->RAMCalls);
+		const LIB_CALL *NextLibCall = GetFirst (Section->LibCalls);
+		
+		OFFSET CurPos = 0;
+		SIZE SectionSize = Section->Size;
+		
+		while ((CurPos < SectionSize) || NextSegment || NextSymbol || NextReloc || NextROMCall || NextRAMCall || NextLibCall)
+		{
+			OFFSET NextPos = MAX_OFFSET;
+			OFFSET StartPos;
+			
+			const char *CheckPos (void)
+			{
+				return ((CurPos == StartPos) && (CurPos <= SectionSize) ? "" : " (!)");
+			}
+			
+			// Segments
+			while (NextSegment && (CurPos >= (StartPos = NextSegment->Location.Start->Location)))
+			{
+				fprintf (File, "%s%s Segment: %s (%s%s)\n", Indent, CheckPos (), NextSegment->FileName ? : "", NextSegment->Code ? "Code" : "Data", NextSegment->CanCutRanges ? ", Can Cut Ranges" : "");
+				NextSegment = GetNext (NextSegment);
+			}
+			if (NextSegment && (NextPos > NextSegment->Location.Start->Location))
+				NextPos = NextSegment->Location.Start->Location;
+			
+			// Symbols
+			while (NextSymbol && (CurPos >= (StartPos = NextSymbol->Location)))
+			{
+				fprintf (File, "%s%s %s:%s\n", Indent, CheckPos (), NextSymbol->Name, NextSymbol->Exported ? "" : " (local)");
+				NextSymbol = GetNext (NextSymbol);
+			}
+			if (NextSymbol && (NextPos > NextSymbol->Location))
+				NextPos = NextSymbol->Location;
+			
+			// Relocs
+			if (NextReloc && (CurPos >= (StartPos = NextReloc->Location)))
+			{
+				OFFSET EndPos = StartPos + NextReloc->Size;
+				fprintf (File, "%s 0x%.06lX%s: <%ldB%s: %s%s", NextIndent, (long) StartPos, CheckPos (), (long) NextReloc->Size, NextReloc->Unoptimizable ? " U" : "", NextReloc->Target.SymbolName, NextReloc->Target.Symbol ? (NextReloc->Target.Symbol->Parent == NextReloc->Parent ? "" : " (->)") : " (?)");
+				if (NextReloc->Target.Offset)
+					fprintf (File, "%+ld", (long) (NextReloc->Target.Offset));
+				if (NextReloc->Relative)
+				{
+					if (NextReloc->Relation)
+					{
+						fprintf (File, " - %s%s%s", NextReloc->Relation->Offset ? "(" : "", NextReloc->Relation->SymbolName, NextReloc->Relation->Symbol ? (NextReloc->Relation->Symbol->Parent == NextReloc->Parent ? "" : " (->)") : " (?)");
+						if (NextReloc->Relation->Offset)
+							fprintf (File, "%+ld)", (long) (NextReloc->Relation->Offset));
+					}
+					else
+						fprintf (File, " (rel)");
+				}
+				PrintOffset (File, NextReloc->FixedOffset);
+				fprintf (File, ">%s\n", IsZeroDataRange (Section, StartPos, EndPos) ? "" : " (!)");
+				if (CurPos < EndPos)
+					CurPos = EndPos;
+				NextReloc = GetNext (NextReloc);
+			}
+			if (NextReloc && (NextPos > NextReloc->Location))
+				NextPos = NextReloc->Location;
+			
+			// ROM Calls
+			if (NextROMCall && (CurPos >= (StartPos = NextROMCall->Location)))
+			{
+				OFFSET EndPos = StartPos + NextROMCall->Size;
+				fprintf (File, "%s 0x%.06lX%s: <%ldB: ROM Call 0x%lX", NextIndent, (long) StartPos, CheckPos (), (long) NextROMCall->Size, (long) (NextROMCall->Number));
+				PrintOffset (File, NextROMCall->FixedOffset);
+				fprintf (File, ">%s\n", IsZeroDataRange (Section, StartPos, EndPos) ? "" : " (!)");
+				if (CurPos < EndPos)
+					CurPos = EndPos;
+				NextROMCall = GetNext (NextROMCall);
+			}
+			if (NextROMCall && (NextPos > NextROMCall->Location))
+				NextPos = NextROMCall->Location;
+			
+			// RAM Calls
+			if (NextRAMCall && (CurPos >= (StartPos = NextRAMCall->Location)))
+			{
+				OFFSET EndPos = StartPos + NextRAMCall->Size;
+				fprintf (File, "%s 0x%.06lX%s: <%ldB: RAM Call 0x%lX", NextIndent, (long) StartPos, CheckPos (), (long) NextRAMCall->Size, (long) (NextRAMCall->Number));
+				PrintOffset (File, NextRAMCall->FixedOffset);
+				fprintf (File, ">%s\n", IsZeroDataRange (Section, StartPos, EndPos) ? "" : " (!)");
+				if (CurPos < EndPos)
+					CurPos = EndPos;
+				NextRAMCall = GetNext (NextRAMCall);
+			}
+			if (NextRAMCall && (NextPos > NextRAMCall->Location))
+				NextPos = NextRAMCall->Location;
+			
+			// Library Calls
+			if (NextLibCall && (CurPos >= (StartPos = NextLibCall->Location)))
+			{
+				OFFSET EndPos = StartPos + NextLibCall->Size;
+				fprintf (File, "%s 0x%.06lX%s: <%ldB: Library Call %s::0x%lX", NextIndent, (long) StartPos, CheckPos (), (long) NextLibCall->Size, NextLibCall->Library->Name, (long) (NextLibCall->Number));
+				PrintOffset (File, NextLibCall->FixedOffset);
+				fprintf (File, ">%s\n", IsZeroDataRange (Section, StartPos, EndPos) ? "" : " (!)");
+				if (CurPos < EndPos)
+					CurPos = EndPos;
+				NextLibCall = GetNext (NextLibCall);
+			}
+			if (NextLibCall && (NextPos > NextLibCall->Location))
+				NextPos = NextLibCall->Location;
+			
+			if (CurPos > SectionSize)
+				fprintf (File, "%s (Section Size Exceeded!)\n", NextIndent);
+			
+			// Section Data
+			if (CurPos < NextPos)
+			{
+				OFFSET EndPos = NextPos;
+				if (EndPos > SectionSize)
+					EndPos = SectionSize;
+				if (CurPos < EndPos)
+				{
+					if (Section->Data)
+						DumpData (File, NextIndent, Section->Data, CurPos, EndPos);
+					else
+						DumpDummyData (File, NextIndent, Section->Initialized ? "00" : "??", CurPos, EndPos);
+				}
+				CurPos = NextPos;
+			}
+		}
+	}
+}
+
+void DumpData (FILE *File, const char *Indent, const I1 *Data, OFFSET Start, OFFSET End)
+{
+	OFFSET CurPos;
+	
+	FixIndent;
+	
+	fprintf (File, "%s 0x%.06lX:", Indent, (long) Start);
+	for (CurPos = Start; CurPos < End; CurPos++)
+		fprintf (File, " %.02hX", (short) (Data [CurPos]));
+	fprintf (File, "\n");
+}
+
+void DumpDummyData (FILE *File, const char *Indent, const char *Dummy, OFFSET Start, OFFSET End)
+{
+	OFFSET CurPos;
+	
+	FixIndent;
+	
+	fprintf (File, "%s 0x%.06lX:", Indent, (long) Start);
+	for (CurPos = Start; CurPos < End; CurPos++)
+		fprintf (File, " %s", Dummy);
+	fprintf (File, "\n");
+}
+
+#endif /* ENABLE_DUMP */

+ 44 - 0
dump.h

@@ -0,0 +1,44 @@
+/* dump.h: Routines to dump the internal data to a file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef DUMP_H
+#define DUMP_H
+
+#include "generic.h"
+
+#ifdef ENABLE_DUMP
+
+#include "data.h"
+
+#include <stdio.h>
+
+// Dump everything.
+void DumpProgram (FILE *File, const char *Indent, const PROGRAM *Program);
+
+// Dump a single section.
+void DumpSection (FILE *File, const char *Indent, const SECTION *Section);
+
+// Dump raw data.
+void DumpData (FILE *File, const char *Indent, const I1 *Data, OFFSET Start, OFFSET End);
+
+// Dump raw zero/uninitialized data.
+void DumpDummyData (FILE *File, const char *Indent, const char *Dummy, OFFSET Start, OFFSET End);
+
+#endif /* ENABLE_DUMP */
+
+#endif

+ 91 - 0
export/exp_data.c

@@ -0,0 +1,91 @@
+/* exp_data.c: Routines to export a data file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "exp_data.h"
+
+#ifdef DATA_VAR_SUPPORT
+
+// Get the file size needed to export the data file.
+// Returns 0 on failure.
+// Call this function once to receive necessary error messages.
+SIZE GetDataFileSize (const PROGRAM *Program)
+{
+	// Get a pointer to the main section.
+	SECTION *DataSection = Program->DataSection;
+	
+	if (!DataSection)
+	{
+		Error (NULL, "No data section.");
+		return 0;
+	}
+	
+	return DataSection->Size;
+}
+
+// Export the internal data structures into a TIOS file.
+BOOLEAN ExportDataFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize ATTRIBUTE_UNUSED, ProgramCalcs DestCalc ATTRIBUTE_UNUSED)
+{
+	// A simple macro to make the code more readable.
+#define FailWithError(Err...) ({ Error (Err); return FALSE; })
+	
+	const char *SectionFileName = NULL;
+	OFFSET DataStart = 0;
+	
+	// Get a pointer to the data section.
+	SECTION *DataSection = Program->DataSection;
+	
+	if (!DataSection)
+		FailWithError (NULL, "No data section.");
+	
+	SectionFileName = DataSection->FileName;
+	
+	if (!(DataSection->Data))
+		FailWithError (SectionFileName, "No section contents.");
+	
+	// Write out the section contents.
+	DataStart = ExportTell (File);
+	ExportWrite (File, DataSection->Data, DataSection->Size, 1);
+	
+	if (!(IsEmpty (DataSection->Relocs)))
+	{
+		RELOC *Reloc;
+		
+		for_each (Reloc, DataSection->Relocs)
+		{
+			// If this can be resolved to a calculator-dependent value, write the
+			// value into the section data.
+			if (!(EmitCalcBuiltinValue (Reloc, DestCalc, File, FileSize, DataStart)))
+				FailWithError (SectionFileName, "Relocs inside data variable not supported.");
+		}
+	}
+	
+	if (!(IsEmpty (DataSection->ROMCalls)))
+		FailWithError (SectionFileName, "ROM calls inside data variable not supported.");
+	
+	if (!(IsEmpty (DataSection->RAMCalls)))
+		FailWithError (SectionFileName, "RAM calls inside data variable not supported.");
+	
+	if (!(IsEmpty (DataSection->LibCalls)))
+		FailWithError (SectionFileName, "Library calls inside data variable not supported.");
+	
+	return TRUE;
+	
+#undef FailWithError
+}
+
+#endif /* DATA_VAR_SUPPORT */

+ 37 - 0
export/exp_data.h

@@ -0,0 +1,37 @@
+/* exp_data.h: Routines to export a data file
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef EXP_DATA_H
+#define EXP_DATA_H
+
+#include "../generic.h"
+
+#ifdef DATA_VAR_SUPPORT
+
+#include "exp_def.h"
+#include "../data.h"
+
+// Get the file size needed to export the data file.
+// Returns 0 on failure (e.g. too large).
+SIZE GetDataFileSize (const PROGRAM *Program);
+// Export the data file.
+BOOLEAN ExportDataFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize, ProgramCalcs DestCalc);
+
+#endif /* DATA_VAR_SUPPORT */
+
+#endif

+ 40 - 0
export/exp_def.c

@@ -0,0 +1,40 @@
+/* exp_def.c: File export utilities
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "exp_def.h"
+
+#include "../special.h"
+
+// If the reloc can be resolved to a calculator-dependent builtin value,
+// write the value into the data segment in the file which starts at
+// DataStart.
+BOOLEAN EmitCalcBuiltinValue (const RELOC *Reloc, ProgramCalcs DestCalc, EXP_FILE *File, SIZE FileSize, OFFSET DataStart)
+{
+	IMAX Value;
+	
+	if (GetCalcBuiltinValue (Reloc, DestCalc, &Value) && (Reloc->Location >= 0) && (Reloc->Location + Reloc->Size <= FileSize))
+	{
+		OFFSET CurPos = ExportTell (File);
+		ExportSeek (File, DataStart + Reloc->Location);
+		ExportWriteTI (File, Value, Reloc->Size, FALSE, TRUE);
+		ExportSeek (File, CurPos);
+		return TRUE;
+	}
+	
+	return FALSE;
+}

+ 82 - 0
export/exp_def.h

@@ -0,0 +1,82 @@
+/* exp_def.h: Definitions for file exports
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef EXP_DEF_H
+#define EXP_DEF_H
+
+#include "../generic.h"
+#include "../integers.h"
+#include "../intrface.h"
+#include "../data.h"
+
+#ifdef TARGET_EMBEDDED
+
+#include <string.h>
+// This is the complete export file, created locally by ExportFile.
+typedef struct {
+	INT_EXP_FILE File;  // Internal part of the export file.
+	OFFSET CurPos;      // Current writing position inside the file
+} EXP_FILE;
+#define ExportWrite(F,P,L,C) (memcpy ((F)->File.Data + (F)->CurPos, P, (L) * (C)), (F)->CurPos += (L) * (C))
+#define ExportSeek(F,P) ((F)->CurPos = (P))
+#define ExportTell(F) ((F)->CurPos)
+#define ExportWriteTI1(F,D) ({ WriteTI1 (*((TI1 *) ((F)->File.Data + (F)->CurPos)), D); (F)->CurPos += 1; })
+#define ExportWriteTI2(F,D) ({ WriteTI2 (*((TI2 *) ((F)->File.Data + (F)->CurPos)), D); (F)->CurPos += 2; })
+#define ExportWriteTI4(F,D) ({ WriteTI4 (*((TI4 *) ((F)->File.Data + (F)->CurPos)), D); (F)->CurPos += 4; })
+#define ExportWriteTI(F,D,L,AllowSigned,AllowUnsigned) ({ WriteTI (((F)->File.Data + (F)->CurPos), L, D, AllowSigned, AllowUnsigned); (F)->CurPos += (L); })
+
+#else /* !TARGET_EMBEDDED */
+
+#include <stdio.h>
+// This is the complete export file, created locally by ExportFile.
+typedef struct {
+	INT_EXP_FILE File;  // Internal part of the export file.
+} EXP_FILE;
+#define ExportWrite(F,P,L,C) ({ I1 *__P = (I1 *) (P); OFFSET __CP; fwrite (__P, L, C, (F)->File.File); for (__CP = 0; __CP < (OFFSET) ((L) * (C)); __CP++) (F)->File.CheckSum += *(__P++); })
+#define ExportSeek(F,P) (fseek ((F)->File.File, P, SEEK_SET))
+#define ExportTell(F) (ftell ((F)->File.File))
+
+#endif /* !TARGET_EMBEDDED */
+
+#ifndef ExportWriteTI1
+#define ExportWriteTI1(F,D) ({ TI1 __D; WriteTI1 (__D, D); ExportWrite (F, &__D, 1, 1); })
+#endif
+
+#ifndef ExportWriteTI2
+#define ExportWriteTI2(F,D) ({ TI2 __D; WriteTI2 (__D, D); ExportWrite (F, &__D, 2, 1); })
+#endif
+
+#ifndef ExportWriteTI4
+#define ExportWriteTI4(F,D) ({ TI4 __D; WriteTI4 (__D, D); ExportWrite (F, &__D, 4, 1); })
+#endif
+
+#ifndef ExportWriteTI
+#define ExportWriteTI(F,D,L,AllowSigned,AllowUnsigned) ({ I1 __D[(L)]; WriteTI (__D, L, D, AllowSigned, AllowUnsigned); ExportWrite (F, __D, L, 1); })
+#endif
+
+// This is the format of a "Get...FileSize" function.
+typedef SIZE (*GET_FILE_SIZE_FUNCTION) (const PROGRAM *Program);
+// This is the format of a "Export...File" function.
+typedef BOOLEAN (*EXPORT_FILE_FUNCTION) (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize, ProgramCalcs DestCalc);
+
+// If the reloc can be resolved to a calculator-dependent builtin value,
+// write the value into the data segment in the file which starts at
+// DataStart.
+BOOLEAN EmitCalcBuiltinValue (const RELOC *Reloc, ProgramCalcs DestCalc, EXP_FILE *File, SIZE FileSize, OFFSET DataStart);
+
+#endif

+ 117 - 0
export/exp_farg.c

@@ -0,0 +1,117 @@
+/* exp_farg.c: Routines to export to a Fargo file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+   Copyright (C) 2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "exp_farg.h"
+
+#ifdef FARGO_SUPPORT
+
+#include "../manip.h"
+
+// Fargo TI-BASIC fork
+const I1 FARGO_TIBASIC_FORK[] = {
+	0xE9,            // ENDSTACK_TAG
+	0x12, 0xE4,      // 'EndPrgm'
+	0x00, 0xE8,      // Newline
+	0x19, 0xE4,      // 'Prgm'
+	0xE5,            // No arguments, END_TAG only
+	0x00, 0x00, 0x40 // Flags
+};
+
+// Get the file size needed to export the program into a Fargo file.
+// Returns 0 on failure.
+// Call this function once to receive necessary error messages.
+SIZE GetFargoFileSize (const PROGRAM *Program)
+{
+	// Get a pointer to the main section.
+	SECTION *MainSection = Program->MainSection;
+	
+	// There must be exactly one section.
+	if (!MainSection)
+	{
+		Error (NULL, "No main section.");
+		return 0;
+	}
+	
+	// The program consists of a TI-Basic fork and the section.
+	return (MainSection->Size + sizeof (FARGO_TIBASIC_FORK));
+}
+
+// Export the internal data structures into a Fargo file.
+BOOLEAN ExportFargoFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize ATTRIBUTE_UNUSED, ProgramCalcs DestCalc ATTRIBUTE_UNUSED)
+{
+	// A simple macro to make the code more readable.
+#define FailWithError(Err...) ({ Error (Err); return FALSE; })
+	
+	const char *SectionFileName = NULL;
+	OFFSET DataStart = 0;
+	
+	// Get a pointer to the first section.
+	SECTION *MainSection = Program->MainSection;
+	
+	if (!MainSection)
+		FailWithError (NULL, "No main section.");
+	
+	// Get the section file name for error messages.
+	SectionFileName = MainSection->FileName;
+	
+	// Startup code should always be marked as such. When merging
+	// sections, the destination startup number is kept, so in the
+	// end, the complete code is marked as a startup section (which
+	// makes sense; it is startable).
+	if (!(MainSection->StartupNumber))
+		FailWithError (SectionFileName, "No startup section(s) defined.");
+	
+	if (!(MainSection->Data))
+		FailWithError (SectionFileName, "No section contents.");
+	
+	// Write out the section contents first.
+	DataStart = ExportTell (File);
+	ExportWrite (File, MainSection->Data, MainSection->Size, 1);
+	
+	// Write out the TI-BASIC fork.
+	ExportWrite (File, FARGO_TIBASIC_FORK, sizeof (FARGO_TIBASIC_FORK), 1);
+
+	if (!(IsEmpty (MainSection->Relocs)))
+	{
+		RELOC *Reloc;
+		
+		for_each (Reloc, MainSection->Relocs)
+		{
+			// If this can be resolved to a calculator-dependent value, write the
+			// value into the section data.
+			if (!(EmitCalcBuiltinValue (Reloc, DestCalc, File, FileSize, DataStart)))
+				FailWithError (SectionFileName, "Fargo does not support AMS relocs.");
+		}
+	}
+	
+	if ((!(IsEmpty (MainSection->ROMCalls))) && (!(MainSection->ROMCalls.Handled)))
+		FailWithError (SectionFileName, "Fargo does not support ROM calls. Use `tios__0000' and so on.");
+	
+	if ((!(IsEmpty (MainSection->RAMCalls))) && (!(MainSection->RAMCalls.Handled)))
+		FailWithError (SectionFileName, "Fargo does not support RAM calls. Use `kernel__0000' and so on.");
+	
+	if ((!(IsEmpty (MainSection->LibCalls))) && (!(MainSection->LibCalls.Handled)))
+		FailWithError (SectionFileName, "Library calls are not supported in this mode.");
+	
+	return TRUE;
+	
+#undef FailWithError
+}
+
+#endif /* FARGO_SUPPORT */

+ 38 - 0
export/exp_farg.h

@@ -0,0 +1,38 @@
+/* exp_farg.h: Routines to export to a Fargo file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+   Copyright (C) 2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef EXP_FARG_H
+#define EXP_FARG_H
+
+#include "../generic.h"
+
+#ifdef FARGO_SUPPORT
+
+#include "exp_def.h"
+#include "../data.h"
+
+// Get the file size needed to export the program into a Fargo file.
+// Returns 0 on failure.
+SIZE GetFargoFileSize (const PROGRAM *Program);
+// Export the internal data structures into a Fargo file.
+BOOLEAN ExportFargoFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize, ProgramCalcs DestCalc);
+
+#endif /* FARGO_SUPPORT */
+
+#endif

+ 174 - 0
export/exp_ndll.c

@@ -0,0 +1,174 @@
+/* exp_ndll.c: Routines to export to a Nostub DLL file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "exp_ndll.h"
+
+#ifdef NOSTUB_DLL_SUPPORT
+
+#include "../manip.h"
+#include "../special.h"
+
+// Get the file size needed to export the program into a Nostub DLL file.
+// Returns 0 on failure.
+// Call this function once to receive necessary error messages.
+SIZE GetNostubDLLFileSize (const PROGRAM *Program)
+{
+	// Get a pointer to the main section.
+	SECTION *MainSection = Program->MainSection;
+	
+	if (!MainSection)
+	{
+		Error (NULL, "No main section.");
+		return 0;
+	}
+	
+	{
+		// Start with two size bytes and the size of the section.
+		// Add 2 for the two null bytes at the beginning of the reloc table.
+		SIZE Size = 2 + MainSection->Size + 2;
+		
+		if (!(IsEmpty (MainSection->Relocs)))
+		{
+			RELOC *Reloc;
+			
+			// Add the size needed for the relocs.
+			for_each (Reloc, MainSection->Relocs)
+			{
+				// If this can be resolved to a calculator-dependent value, ignore the
+				// reloc for now.
+				if (IsPlainCalcBuiltin (Reloc))
+					continue;
+				
+				// We can only emit 4-byte absolute relocs.
+				if (Reloc->Relative || (Reloc->Size != 4))
+				{
+					Error (GetFileName (MainSection, Reloc->Location), "Cannot emit %ld byte %s reloc to `%s'.", (long) Reloc->Size, Reloc->Relative ? "relative" : "absolute", Reloc->Target.SymbolName);
+					Size = 0;
+				}
+				
+				if (Size)
+					// A reloc takes 4 bytes: 2 bytes for the location and 2 bytes for the target offset.
+					Size += 4;
+			}
+		}
+		
+		// Add 1 for the embedded tag byte.
+		Size ++;
+		
+		return Size;
+	}
+}
+
+// Export the internal data structures into a Nostub DLL file.
+BOOLEAN ExportNostubDLLFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize, ProgramCalcs DestCalc ATTRIBUTE_UNUSED)
+{
+	// A simple macro to make the code more readable.
+#define FailWithError(Err...) ({ Error (Err); return FALSE; })
+	
+	const char *SectionFileName = NULL;
+	OFFSET DataStart = 0;
+	
+	COUNT EmittedRelocCount = 0;
+	
+	// Get a pointer to the main section.
+	SECTION *MainSection = Program->MainSection;
+	
+	if (!MainSection)
+		FailWithError (NULL, "No main section.");
+	
+	// Get the section file name for error messages.
+	SectionFileName = MainSection->FileName;
+	
+	if (!(MainSection->Data))
+		FailWithError (SectionFileName, "No section contents.");
+	
+	// Write out the embedded size.
+	ExportWriteTI2 (File, FileSize - 2);
+	
+	// Write out the section contents first.
+	DataStart = ExportTell (File);
+	ExportWrite (File, MainSection->Data, MainSection->Size, 1);
+	
+	// Write out two zero bytes as a separator.
+	ExportWriteTI2 (File, 0);
+	
+	if (!(IsEmpty (MainSection->Relocs)))
+	{
+		RELOC *Reloc;
+		
+		// Write out the relocation table.
+		for (Reloc = GetLast (MainSection->Relocs); Reloc; Reloc = GetPrev (Reloc))
+		{
+			// Get the current file name for error messages.
+			const char *CurFileName = GetFileName (MainSection, Reloc->Location);
+			
+			// If this can be resolved to a calculator-dependent value, write the
+			// value into the section data.
+			if (EmitCalcBuiltinValue (Reloc, DestCalc, File, FileSize, DataStart))
+				continue;
+			
+			// We can only emit relocs with a target symbol in the same section.
+			if (!(Reloc->Target.Symbol))
+				FailWithError (CurFileName, "Unresolved reference to `%s'.", Reloc->Target.SymbolName);
+			if (Reloc->Target.Symbol->Parent != MainSection)
+				FailWithError (CurFileName, "Cannot emit reloc to `%s' in different section.", Reloc->Target.SymbolName);
+			
+			// We can only emit 4-byte absolute relocs.
+			if (Reloc->Relative || (Reloc->Size != 4))
+				FailWithError (CurFileName, "Cannot emit %ld byte %s reloc to `%s'.", (long) Reloc->Size, Reloc->Relative ? "relative" : "absolute", Reloc->Target.SymbolName);
+			
+			if (((I2) Reloc->Location) != Reloc->Location)
+				FailWithError (CurFileName, "Cannot emit reloc outside of limited program range to `%s'.", Reloc->Target.SymbolName);
+			
+			{
+				OFFSET TargetLocation = GetLocationOffset (MainSection, &(Reloc->Target)) + Reloc->FixedOffset;
+				
+				if (((I2) TargetLocation) != TargetLocation)
+					FailWithError (CurFileName, "Cannot emit reloc to `%s' (Offset %ld; Location 0x%lX) outside of limited program range.", Reloc->Target.SymbolName, (long) (Reloc->Target.Offset + Reloc->FixedOffset), (long) (TargetLocation));
+				
+				// Everything seems to be correct.
+				ExportWriteTI2 (File, TargetLocation);
+				ExportWriteTI2 (File, Reloc->Location);
+				
+				// Increase the statistics.
+				EmittedRelocCount++;
+			}
+		}
+	}
+	
+	// Write out the embedded tag byte (ASM_TAG).
+	ExportWriteTI1 (File, 0xF3);
+	
+	if ((!(IsEmpty (MainSection->ROMCalls))) && (!(MainSection->ROMCalls.Handled)))
+		FailWithError (SectionFileName, "ROM calls are not supported in this mode.");
+	
+	if ((!(IsEmpty (MainSection->RAMCalls))) && (!(MainSection->RAMCalls.Handled)))
+		FailWithError (SectionFileName, "RAM calls are not supported in this mode.");
+	
+	if ((!(IsEmpty (MainSection->LibCalls))) && (!(MainSection->LibCalls.Handled)))
+		FailWithError (SectionFileName, "Library calls are not supported in this mode.");
+	
+	if (Program->OptimizeInfo->NativeRelocCount < EmittedRelocCount)
+		Program->OptimizeInfo->NativeRelocCount = EmittedRelocCount;
+	
+	return TRUE;
+	
+#undef FailWithError
+}
+
+#endif /* NOSTUB_DLL_SUPPORT */

+ 37 - 0
export/exp_ndll.h

@@ -0,0 +1,37 @@
+/* exp_ndll.h: Routines to export to a nostub DLL file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef EXP_NDLL_H
+#define EXP_NDLL_H
+
+#include "../generic.h"
+
+#ifdef NOSTUB_DLL_SUPPORT
+
+#include "exp_def.h"
+#include "../data.h"
+
+// Get the file size needed to export the program into a Nostub DLL file.
+// Returns 0 on failure.
+SIZE GetNostubDLLFileSize (const PROGRAM *Program);
+// Export the internal data structures into a Nostub DLL file.
+BOOLEAN ExportNostubDLLFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize, ProgramCalcs DestCalc);
+
+#endif /* NOSTUB_DLL_SUPPORT */
+
+#endif

+ 157 - 0
export/exp_os.c

@@ -0,0 +1,157 @@
+/* exp_os.c: Routines to export to a Flash OS
+
+   Copyright (C) 2004 Billy Charvet
+   Copyright (C) 2004 Kevin Kofler
+   Copyright (C) 2004 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "exp_os.h"
+
+#ifdef FLASH_OS_SUPPORT
+
+#include "../manip.h"
+#include "../special.h"
+
+// Get the file size needed to export the program into a Flash OS.
+// Returns 0 on failure.
+// Call this function once to receive necessary error messages.
+SIZE GetFlashOSFileSize (const PROGRAM *Program)
+{
+	// Get a pointer to the main section.
+	SECTION *MainSection = Program->MainSection;
+	
+	if (!MainSection)
+	{
+		Error (NULL, "No main section.");
+		return 0;
+	}
+	
+	{
+		SIZE Size = MainSection->Size;
+		if (Size & 0x1)
+			Size++;
+		return Size;
+	}
+}
+
+// Export the internal data structures into a Flash OS.
+BOOLEAN ExportFlashOSFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize, ProgramCalcs DestCalc)
+{
+	// A simple macro to make the code more readable.
+#define FailWithError(Err...) ({ Error (Err); return FALSE; })
+	
+	// Get a pointer to the main section.
+	const char *SectionFileName = NULL;
+	OFFSET DataStart = 0;
+	I4 ROMBase;
+	const I1 ZeroByte = 0;
+	
+	SECTION *MainSection = Program->MainSection;
+	
+	switch (DestCalc)
+	{
+		case CALC_TI89:
+		case CALC_V200:
+			ROMBase = 0x200000;
+			break;
+		
+		case CALC_TI92PLUS:
+			ROMBase = 0x400000;
+			break;
+		
+		case CALC_TI89 | CALC_FLAG_TITANIUM:
+			ROMBase = 0x800000;
+			break;
+		
+		case CALC_TI92:
+			FailWithError (NULL, "Flash upgrades are not available for the TI-92.");
+		
+		default:
+			FailWithError (NULL, "Unknown calculator model.");
+	}
+	
+	if (!MainSection)
+		FailWithError (NULL, "No main section.");
+	
+	SectionFileName = MainSection->FileName;
+	
+	if (!(MainSection->StartupNumber || (Program->Type == PT_NOSTUB)))
+		FailWithError (SectionFileName, "No startup section(s) defined.");
+	
+	if (!(MainSection->Data))
+		FailWithError (SectionFileName, "No section contents.");
+	
+	// Write out the section contents first.
+	DataStart = ExportTell (File);
+	ExportWrite (File, MainSection->Data, MainSection->Size, 1);
+	// Pad the main section to an even length.
+	if (MainSection->Size & 0x1)
+		ExportWrite (File, &ZeroByte, 1, 1);
+	
+	if (!(IsEmpty (MainSection->Relocs)))
+	{
+		RELOC *Reloc;
+		
+		// Write out the relocation entries.
+		for (Reloc = GetLast (MainSection->Relocs); Reloc; Reloc = GetPrev (Reloc))
+		{
+			// Get the current file name for error messages.
+			const char *CurFileName = GetFileName (MainSection, Reloc->Location);
+			
+			// If this can be resolved to a calculator-dependent value, write the
+			// value into the section data.
+			if (EmitCalcBuiltinValue (Reloc, DestCalc, File, FileSize, DataStart))
+				continue;
+			
+			// We can only emit relocs with a target symbol in the same section.
+			if (!(Reloc->Target.Symbol))
+				FailWithError (CurFileName, "Unresolved reference to `%s'.", Reloc->Target.SymbolName);
+			if (Reloc->Target.Symbol->Parent != MainSection)
+				FailWithError (CurFileName, "Cannot emit reloc to `%s' in different section.", Reloc->Target.SymbolName);
+			
+			// We can only emit 4-byte absolute relocs.
+			if (Reloc->Relative || (Reloc->Size != 4))
+				FailWithError (CurFileName, "Cannot emit %ld byte %s reloc to `%s'.", (long) Reloc->Size, Reloc->Relative ? "relative" : "absolute", Reloc->Target.SymbolName);
+			
+			{
+				OFFSET TargetLocation = GetLocationOffset (MainSection, &(Reloc->Target)) + Reloc->FixedOffset;
+				
+				TargetLocation += (OFFSET) (ROMBase + 0x12000);
+				ExportSeek (File, DataStart + Reloc->Location);
+				ExportWriteTI (File, TargetLocation, Reloc->Size, TRUE, TRUE);
+				
+				// Do not increase the statistics, since that would give a false
+				// impression that the relocation entries actually take up some
+				// space in the OS.
+			}
+		}
+	}
+	
+	if ((!(IsEmpty (MainSection->ROMCalls))) && (!(MainSection->ROMCalls.Handled)))
+		FailWithError (SectionFileName, "ROM calls are not supported in Flash OSs.");
+	
+	if ((!(IsEmpty (MainSection->RAMCalls))) && (!(MainSection->RAMCalls.Handled)))
+		FailWithError (SectionFileName, "RAM calls are not supported in Flash OSs.");
+	
+	if ((!(IsEmpty (MainSection->LibCalls))) && (!(MainSection->LibCalls.Handled)))
+		FailWithError (SectionFileName, "Library calls are not supported in Flash OSs.");
+	
+	return TRUE;
+	
+#undef FailWithError
+}
+
+#endif /* FLASH_OS_SUPPORT */

+ 37 - 0
export/exp_os.h

@@ -0,0 +1,37 @@
+/* exp_os.h: Routines to export to a Flash OS
+
+   Copyright (C) 2004 Billy Charvet
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef EXP_FLASH_OS_H
+#define EXP_FLASH_OS_H
+
+#include "../generic.h"
+
+#ifdef FLASH_OS_SUPPORT
+
+#include "exp_def.h"
+#include "../data.h"
+
+// Get the file size needed to export the program into a Flash OS.
+// Returns 0 on failure.
+SIZE GetFlashOSFileSize (const PROGRAM *Program);
+// Export the internal data structures into a Flash OS.
+BOOLEAN ExportFlashOSFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize, ProgramCalcs DestCalc);
+
+#endif /* FLASH_OS_SUPPORT */
+
+#endif

+ 172 - 0
export/exp_tios.c

@@ -0,0 +1,172 @@
+/* exp_tios.c: Routines to export to a TIOS file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "exp_tios.h"
+
+#ifdef TIOS_SUPPORT
+
+#include "../manip.h"
+#include "../special.h"
+
+// Get the file size needed to export the program into a TIOS file.
+// Returns 0 on failure.
+// Call this function once to receive necessary error messages.
+SIZE GetTIOSFileSize (const PROGRAM *Program)
+{
+	// Get a pointer to the main section.
+	SECTION *MainSection = Program->MainSection;
+	
+	if (!MainSection)
+	{
+		Error (NULL, "No main section.");
+		return 0;
+	}
+	
+	{
+		// Start with the size of the section.
+		// Add 2 for the two null bytes at the beginning of the reloc table.
+		SIZE Size = MainSection->Size + 2;
+		
+		if (!(IsEmpty (MainSection->Relocs)))
+		{
+			RELOC *Reloc;
+			
+			// Add the size needed for the relocs.
+			for_each (Reloc, MainSection->Relocs)
+			{
+				// If this can be resolved to a calculator-dependent value, ignore the
+				// reloc for now.
+				if (IsPlainCalcBuiltin (Reloc))
+					continue;
+				
+				// We can only emit 4-byte absolute relocs.
+				if (Reloc->Relative || (Reloc->Size != 4))
+				{
+					Error (GetFileName (MainSection, Reloc->Location), "Cannot emit %ld byte %s reloc to `%s'.", (long) Reloc->Size, Reloc->Relative ? "relative" : "absolute", Reloc->Target.SymbolName);
+					Size = 0;
+				}
+				
+				if (Size)
+					// A reloc takes 4 bytes: 2 bytes for the location and 2 bytes for the target offset.
+					Size += 4;
+			}
+		}
+		
+		return Size;
+	}
+}
+
+// Export the internal data structures into a TIOS file.
+BOOLEAN ExportTIOSFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize, ProgramCalcs DestCalc)
+{
+	// A simple macro to make the code more readable.
+#define FailWithError(Err...) ({ Error (Err); return FALSE; })
+	
+	const char *SectionFileName = NULL;
+	OFFSET DataStart = 0;
+	
+	COUNT EmittedRelocCount = 0;
+	
+	// Get a pointer to the main section.
+	SECTION *MainSection = Program->MainSection;
+	
+	if (!MainSection)
+		FailWithError (NULL, "No main section.");
+	
+	SectionFileName = MainSection->FileName;
+	
+	// In nostub mode, execution simply starts at the beginning of
+	// the first section. But ideally, startup code should always be
+	// marked as such. When merging sections, the destination startup
+	// number is kept, so in the end, the complete code is marked as
+	// a startup section (which makes sense; it is startable).
+	if (!(MainSection->StartupNumber || (Program->Type == PT_NOSTUB)))
+		FailWithError (SectionFileName, "No startup section(s) defined.");
+	
+	if (!(MainSection->Data))
+		FailWithError (SectionFileName, "No section contents.");
+	
+	// Write out the section contents first.
+	DataStart = ExportTell (File);
+	ExportWrite (File, MainSection->Data, MainSection->Size, 1);
+	
+	// Write out two zero bytes as a separator.
+	ExportWriteTI2 (File, 0);
+	
+	if (!(IsEmpty (MainSection->Relocs)))
+	{
+		RELOC *Reloc;
+		
+		// Write out the relocation table.
+		for (Reloc = GetLast (MainSection->Relocs); Reloc; Reloc = GetPrev (Reloc))
+		{
+			// Get the current file name for error messages.
+			const char *CurFileName = GetFileName (MainSection, Reloc->Location);
+			
+			// If this can be resolved to a calculator-dependent value, write the
+			// value into the section data.
+			if (EmitCalcBuiltinValue (Reloc, DestCalc, File, FileSize, DataStart))
+				continue;
+			
+			// We can only emit relocs with a target symbol in the same section.
+			if (!(Reloc->Target.Symbol))
+				FailWithError (CurFileName, "Unresolved reference to `%s'.", Reloc->Target.SymbolName);
+			if (Reloc->Target.Symbol->Parent != MainSection)
+				FailWithError (CurFileName, "Cannot emit reloc to `%s' in different section.", Reloc->Target.SymbolName);
+			
+			// We can only emit 4-byte absolute relocs.
+			if (Reloc->Relative || (Reloc->Size != 4))
+				FailWithError (CurFileName, "Cannot emit %ld byte %s reloc to `%s'.", (long) Reloc->Size, Reloc->Relative ? "relative" : "absolute", Reloc->Target.SymbolName);
+			
+			if (((I2) Reloc->Location) != Reloc->Location)
+				FailWithError (CurFileName, "Cannot emit reloc outside of limited program range to `%s'.", Reloc->Target.SymbolName);
+			
+			{
+				OFFSET TargetLocation = GetLocationOffset (MainSection, &(Reloc->Target)) + Reloc->FixedOffset;
+				
+				if (((I2) TargetLocation) != TargetLocation)
+					FailWithError (CurFileName, "Cannot emit reloc to `%s' (Offset %ld; Location 0x%lX) outside of limited program range.", Reloc->Target.SymbolName, (long) (Reloc->Target.Offset + Reloc->FixedOffset), (long) (TargetLocation));
+				
+				// Everything seems to be correct.
+				ExportWriteTI2 (File, TargetLocation);
+				ExportWriteTI2 (File, Reloc->Location);
+				
+				// Increase the statistics.
+				EmittedRelocCount++;
+			}
+		}
+	}
+	
+	if ((!(IsEmpty (MainSection->ROMCalls))) && (!(MainSection->ROMCalls.Handled)))
+		FailWithError (SectionFileName, "ROM calls are not supported in this mode.");
+	
+	if ((!(IsEmpty (MainSection->RAMCalls))) && (!(MainSection->RAMCalls.Handled)))
+		FailWithError (SectionFileName, "RAM calls are not supported in this mode.");
+	
+	if ((!(IsEmpty (MainSection->LibCalls))) && (!(MainSection->LibCalls.Handled)))
+		FailWithError (SectionFileName, "Library calls are not supported in this mode.");
+	
+	if (Program->OptimizeInfo->NativeRelocCount < EmittedRelocCount)
+		Program->OptimizeInfo->NativeRelocCount = EmittedRelocCount;
+	
+	return TRUE;
+	
+#undef FailWithError
+}
+
+#endif /* TIOS_SUPPORT */

+ 37 - 0
export/exp_tios.h

@@ -0,0 +1,37 @@
+/* exp_tios.h: Routines to export to a TIOS file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef EXP_TIOS_H
+#define EXP_TIOS_H
+
+#include "../generic.h"
+
+#ifdef TIOS_SUPPORT
+
+#include "exp_def.h"
+#include "../data.h"
+
+// Get the file size needed to export the program into a TIOS file.
+// Returns 0 on failure.
+SIZE GetTIOSFileSize (const PROGRAM *Program);
+// Export the internal data structures into a TIOS file.
+BOOLEAN ExportTIOSFile (const PROGRAM *Program, EXP_FILE *File, SIZE FileSize, ProgramCalcs DestCalc);
+
+#endif /* TIOS_SUPPORT */
+
+#endif

+ 634 - 0
export/export.c

@@ -0,0 +1,634 @@
+/* export.c: Routines for file exports
+
+   Copyright (C) 2002-2004 Sebastian Reichelt
+   Copyright (C) 2003-2004 Kevin Kofler
+   Copyright (C) 2004 Billy Charvet
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "export.h"
+
+#ifdef TIOS_SUPPORT
+#include "exp_tios.h"
+#endif /* TIOS_SUPPORT */
+#ifdef FLASH_OS_SUPPORT
+#include "exp_os.h"
+#endif /* FLASH_OS_SUPPORT */
+#ifdef NOSTUB_DLL_SUPPORT
+#include "exp_ndll.h"
+#endif /* NOSTUB_DLL_SUPPORT */
+#ifdef FARGO_SUPPORT
+#include "exp_farg.h"
+#endif /* FARGO_SUPPORT */
+#ifdef DATA_VAR_SUPPORT
+#include "exp_data.h"
+#endif /* DATA_VAR_SUPPORT */
+
+#include "../formats/tios.h"
+#include "../formats/tiosupgd.h"
+
+#include "../special.h"
+
+#include <string.h>
+
+typedef struct {
+	OUTPUT_FILE_FUNCTION GetOutputFile;
+	OUTPUT_FILE_FINALIZE_FUNCTION FinalizeOutputFile;
+	GET_FILE_SIZE_FUNCTION GetFileSize;
+	EXPORT_FILE_FUNCTION ExportFile, ExportDataFile;
+	FileFormats FileFormat, DataFileFormat;
+	unsigned int FileType, DataFileType;
+	const char *Extension;
+} EXPORT_STRUCT;
+
+// Return the maximum file size allowed for a given format
+// (without header and footer).
+SIZE GetMaxFileSize (FileFormats FileFormat, ProgramCalcs Calc)
+{
+	switch (FileFormat)
+	{
+		case FF_TIOS:
+			return MAX_TIOS_FILE_SIZE;
+		case FF_TIOS_UPGRADE:
+			if (Calc == CALC_V200 || (Calc == (CALC_TI89 | CALC_FLAG_TITANIUM)))
+				return MAX_TIOS_UPGRADE_FILE_SIZE_NEW;
+			else
+				return MAX_TIOS_UPGRADE_FILE_SIZE_OLD;
+		default:
+			return 0;
+	}
+}
+
+// Export the internal data structures to an external file for the given
+// calculator.
+BOOLEAN ExportProgramToFormat (const PROGRAM *Program, const EXPORT_STRUCT *ExportStruct, ProgramCalcs Calc)
+{
+	BOOLEAN Success;
+	EXP_FILE File;
+	SIZE Size, MaxSize;
+	I4 EffectiveSize = 0;
+	
+	// Fill the output file struct with zeroes.
+	memset (&File, 0, sizeof (File));
+	
+	// Get the file size needed to export the file.
+	Size = ExportStruct->GetFileSize (Program);
+	
+	// A size of 0 indicates an error.
+	if (Size <= 0)
+		return FALSE;
+	
+	// Check for maximum file size.
+	MaxSize = GetMaxFileSize (ExportStruct->FileFormat, Calc);
+	
+	if (Size > MaxSize)
+	{
+		Error (NULL, "Program size of %ld bytes exceeds maximum by %ld.", (long) Size, (long) (Size - MaxSize));
+		return FALSE;
+	}
+	
+	// Create an output file with the specified format.
+	if (!(ExportStruct->GetOutputFile (&(File.File), Size, Calc, FR_MAIN, ExportStruct->FileFormat, ExportStruct->FileType, ExportStruct->Extension, !(Program->Library), &EffectiveSize)))
+		return FALSE;
+	
+	// Export the program into the output file.
+	Success = ExportStruct->ExportFile (Program, &File, Size, Calc);
+	
+	// Finalize and close the output file.
+	if (ExportStruct->FinalizeOutputFile)
+		ExportStruct->FinalizeOutputFile (&(File.File));
+	
+	if (Success)
+	{
+		if (Program->OptimizeInfo->ProgramSize < (SI4) EffectiveSize)
+			Program->OptimizeInfo->ProgramSize = (SI4) EffectiveSize;
+	}
+	else
+		return FALSE;
+	
+#ifdef DATA_VAR_SUPPORT
+	// Write the data file, if any.
+	if (Program->DataVarInfo->Name && Program->DataSection)
+	{
+		if (ExportStruct->DataFileFormat == FF_NONE)
+		{
+			Error (NULL, "Data variable is not supported in this file format.");
+			return FALSE;
+		}
+		
+		// Fill the output file struct with zeroes.
+		memset (&File, 0, sizeof (File));
+		
+		// Get the file size needed to export the file.
+		Size = GetDataFileSize (Program);
+		
+		// A size of 0 indicates an error.
+		if (Size <= 0)
+			return FALSE;
+		
+		MaxSize = GetMaxFileSize (ExportStruct->DataFileFormat, Calc);
+		
+		// Check for maximum file size.
+		if (Size > MaxSize)
+		{
+			Error (NULL, "Data variable size of %ld bytes exceeds maximum by %ld.", (long) Size, (long) (MaxSize - Size));
+			return FALSE;
+		}
+		// Create an output file with the specified format.
+		if (!(ExportStruct->GetOutputFile (&(File.File), Size, Calc, FR_DATA, ExportStruct->DataFileFormat, ExportStruct->DataFileType, "DAT", FALSE, &EffectiveSize)))
+			return FALSE;
+		
+		// Export the program into the output file.
+		Success = ExportStruct->ExportDataFile (Program, &File, Size, Calc);
+		
+		// Finalize and close the output file.
+		if (ExportStruct->FinalizeOutputFile)
+			ExportStruct->FinalizeOutputFile (&(File.File));
+		
+		if (Success)
+		{
+			if (Program->OptimizeInfo->DataSize < (SI4) EffectiveSize)
+				Program->OptimizeInfo->DataSize = (SI4) EffectiveSize;
+		}
+		else
+			return FALSE;
+	}
+#endif /* DATA_VAR_SUPPORT */
+	
+	return TRUE;
+}
+
+// Export the internal data structures to external files, creating as many
+// files as needed.
+BOOLEAN ExportProgram (const PROGRAM *Program, OUTPUT_FILE_FUNCTION GetOutputFile, OUTPUT_FILE_FINALIZE_FUNCTION FinalizeOutputFile)
+{
+	EXPORT_STRUCT ExportStruct = {GetOutputFile, FinalizeOutputFile, NULL, NULL, NULL, FF_TIOS, FF_TIOS, TIOS_TAG_ASM, TIOS_TAG_OTH, NULL};
+	
+	if (!GetOutputFile)
+		return FALSE;
+	
+	switch (Program->Type)
+	{
+#ifdef FLASH_OS_SUPPORT
+		case PT_FLASH_OS:
+			ExportStruct.GetFileSize    = GetFlashOSFileSize;
+			ExportStruct.ExportFile     = ExportFlashOSFile;
+			ExportStruct.FileFormat     = FF_TIOS_UPGRADE;
+			ExportStruct.FileType       = 0; // Different types for Flash OSs do not exist.
+			ExportStruct.DataFileFormat = FF_NONE;
+			ExportStruct.DataFileType   = 0;
+			break;
+#endif /* FLASH_OS_SUPPORT */
+		
+#ifdef NOSTUB_DLL_SUPPORT
+		case PT_NOSTUB_DLL:
+			ExportStruct.GetFileSize = GetNostubDLLFileSize;
+			ExportStruct.ExportFile  = ExportNostubDLLFile;
+			ExportStruct.FileType    = TIOS_TAG_OTH;
+			ExportStruct.Extension   = "DLL";
+			break;
+#endif /* NOSTUB_DLL_SUPPORT */
+		
+#ifdef FARGO_SUPPORT
+		case PT_FARGO:
+			ExportStruct.GetFileSize = GetFargoFileSize;
+			ExportStruct.ExportFile  = ExportFargoFile;
+			ExportStruct.FileType    = TIOS_TAG_PRGM;
+			break;
+#endif /* FARGO_SUPPORT */
+		
+		default:
+#ifdef TIOS_SUPPORT
+			ExportStruct.GetFileSize = GetTIOSFileSize;
+			ExportStruct.ExportFile  = ExportTIOSFile;
+#else /* !TIOS_SUPPORT */
+			Error (NULL, "No default export target for this program type.");
+			return FALSE;
+#endif /* !TIOS_SUPPORT */
+	}
+	
+#ifdef DATA_VAR_SUPPORT
+	ExportStruct.ExportDataFile = ExportDataFile;
+#endif /* DATA_VAR_SUPPORT */
+	
+	{
+		ProgramCalcs CurCalc;
+		
+		// For all calculators...
+		for (CurCalc = 1; CurCalc <= HIGHEST_CALC; CurCalc <<= 1)
+		{
+			if (Program->Calcs & CurCalc)
+			{
+				if (!(ExportProgramToFormat (Program, &ExportStruct, CurCalc)))
+					return FALSE;
+				
+				if (Program->Type == PT_FLASH_OS && CurCalc == CALC_TI89 && (Program->Calcs & CALC_FLAG_TITANIUM))
+				{
+					if (!(ExportProgramToFormat (Program, &ExportStruct, CurCalc | CALC_FLAG_TITANIUM)))
+						return FALSE;
+				}
+			}
+		}
+	}
+	
+	Program->OptimizeInfo->RelocCount += Program->OptimizeInfo->NativeRelocCount;
+	
+	return TRUE;
+}
+
+// Non-embedded file output functions as defined in intrface.h.
+#ifndef TARGET_EMBEDDED
+
+#include <stdio.h>
+
+// Destination file name without extension.
+const char *DestFile = NULL;
+SIZE DestFileSize = 0;
+
+// Folder and variable name for main file.
+char ProgramFolder[MAX_NAME_LEN+1] = "main", ProgramName[MAX_NAME_LEN+1] = "program";
+#ifdef DATA_VAR_SUPPORT
+// Folder and variable name for data file.
+char DataFolder[MAX_NAME_LEN+1] = "", DataName[MAX_NAME_LEN+1] = "data";
+#endif /* DATA_VAR_SUPPORT */
+
+// Specifies whether to output only the binary image of the program.
+BOOLEAN OutputBin = FALSE;
+
+// Maximum length of a file extension.
+#define MAX_FILE_EXT_LEN 3
+
+BOOLEAN GetOutputFile (INT_EXP_FILE *File, SIZE FileSize, unsigned int DestCalc, unsigned int FileRole, unsigned int FileFormat, unsigned int FileType, const char *Extension, BOOLEAN Executable ATTRIBUTE_UNUSED, I4 *EffectiveSize)
+{
+	// Keep some of the parameters for finalization.
+	File->OutputBin  = OutputBin;
+	File->FileFormat = FileFormat;
+	File->FileType   = FileType;
+	File->Extension  = Extension;
+	
+	// Determine the destination file format and act accordingly.
+	switch (FileFormat)
+	{
+#ifdef TIOS_FILE_SUPPORT
+		case FF_TIOS:
+			{
+				const char *FolderName, *VarName;
+				const char *FileExtCalc, *FileExtType;
+				unsigned int LinkType;
+				
+				// Calculate the size needed for the two size bytes and the
+				// tag.
+				SIZE VarFileSize = 2 + FileSize + 1;
+				
+				// If the file has an extension, add the size needed for it.
+				if (Extension)
+					VarFileSize += 1 + strlen (Extension) + 1;
+				
+				// The variable size is what the user will see.
+				*EffectiveSize = VarFileSize;
+				
+				// Determine the role of the file and set the folder and
+				// variable names according to the user settings.
+				switch (FileRole)
+				{
+					case FR_MAIN:
+						FolderName = ProgramFolder;
+						VarName = ProgramName;
+						break;
+#ifdef DATA_VAR_SUPPORT
+					case FR_DATA:
+						if (*DataFolder)
+							FolderName = DataFolder;
+						else
+							FolderName = ProgramFolder;
+						VarName = DataName;
+						break;
+#endif /* DATA_VAR_SUPPORT */
+					default:
+						Error (NULL, "Unknown file role.");
+						return FALSE;
+				}
+				
+				// Determine the destination calculator and set part of the
+				// file extension.
+				switch (DestCalc)
+				{
+					case CALC_TI92:
+						FileExtCalc = "92";
+						break;
+					case CALC_TI89:
+						FileExtCalc = "89";
+						break;
+					case CALC_TI92PLUS:
+						FileExtCalc = "9x";
+						break;
+					case CALC_V200:
+						FileExtCalc = "v2";
+						break;
+					default:
+						Error (NULL, "Unknown calculator.");
+						return FALSE;
+				}
+				
+				// Determine the file type and set the other part of the file
+				// extension.
+				switch (FileType)
+				{
+					case TIOS_TAG_STR:
+						FileExtType = "s";
+						LinkType = TIOS_LINK_TYPE_STR;
+						break;
+					case TIOS_TAG_PRGM:
+						FileExtType = "p";
+						LinkType = TIOS_LINK_TYPE_PRGM;
+						break;
+					case TIOS_TAG_ASM:
+						FileExtType = "z";
+						LinkType = TIOS_LINK_TYPE_ASM;
+						break;
+					case TIOS_TAG_OTH:
+						FileExtType = "y";
+						LinkType = TIOS_LINK_TYPE_OTH;
+						break;
+					default:
+						Error (NULL, "Unknown file type.");
+						return FALSE;
+				}
+				
+				{
+					SIZE FileNameSize = DestFileSize;
+					
+					// Create a temporary file name string.
+					char CurOutputFileName[FileNameSize+1+MAX_FILE_EXT_LEN+1];
+					strncpy (CurOutputFileName, DestFile, FileNameSize);
+					
+					// Insert the dot into the file name.
+					CurOutputFileName [FileNameSize++] = '.';
+					
+					// Insert the extension. Reverse in binary mode.
+					if (OutputBin)
+					{
+						strcpy (CurOutputFileName + FileNameSize, FileExtType);
+						FileNameSize += strlen (FileExtType);
+						strcpy (CurOutputFileName + FileNameSize, FileExtCalc);
+						FileNameSize += strlen (FileExtCalc);
+					}
+					else
+					{
+						strcpy (CurOutputFileName + FileNameSize, FileExtCalc);
+						FileNameSize += strlen (FileExtCalc);
+						strcpy (CurOutputFileName + FileNameSize, FileExtType);
+						FileNameSize += strlen (FileExtType);
+					}
+					
+					// Zero-terminate the string.
+					CurOutputFileName [FileNameSize] = 0;
+					
+					// Open a file with the specified name.
+					if (!(File->File = fopen (CurOutputFileName, "wb")))
+					{
+						Error (CurOutputFileName, "Could not open file for writing.");
+						return FALSE;
+					}
+					
+					// Write the host file header, if desired.
+					if (!OutputBin)
+					{
+						TIOS_HOST_FILE_HEADER Header;
+						
+						memset (&Header, 0, sizeof (Header));
+						
+						// Write the signature.
+						switch (DestCalc)
+						{
+							case CALC_TI92:
+								strcpy (Header.Signature, "**TI92**");
+								break;
+							case CALC_TI89:
+								strcpy (Header.Signature, "**TI89**");
+								break;
+							case CALC_TI92PLUS:
+							case CALC_V200:
+								strcpy (Header.Signature, "**TI92P*");
+								break;
+							default:
+								Error (NULL, "Unknown calculator.");
+								return FALSE;
+						}
+						
+						// Write folder name.
+						strncpy (Header.FolderName, FolderName, sizeof (Header.FolderName));
+						
+						// Write variable name.
+						strncpy (Header.VarName, VarName, sizeof (Header.VarName));
+						
+						// Write link type.
+						WriteHI1 (Header.LinkType, LinkType);
+						
+						// Write file size.
+						WriteHI4 (Header.FileSize, VarFileSize + sizeof (TIOS_HOST_FILE_HEADER) + sizeof (TIOS_HOST_FILE_FOOTER));
+						
+						// Write reserved bytes.
+						Header.Reserved1 [0] = 0x01;
+						Header.Reserved2 [0] = 0x01;
+						Header.Reserved2 [2] = 0x52;
+						Header.Reserved4 [0] = 0xA5;
+						Header.Reserved4 [1] = 0x5A;
+						
+						// Export the header.
+						fwrite (&Header, sizeof (Header), 1, File->File);
+					}
+					
+					// Write the on-calc file header.
+					{
+						TI2 EncVarFileSize;
+						
+						// Encode the embedded size into the target
+						// endianness.
+						WriteTI2 (EncVarFileSize, VarFileSize - 2);
+						
+						// Export the file size.
+						fwrite (&EncVarFileSize, 2, 1, File->File);
+						
+						// Update the checksum, since this is still included
+						// in the on-calc file image.
+						File->CheckSum += ReadTI1 (EncVarFileSize.Hi) + ReadTI1 (EncVarFileSize.Lo);
+					}
+					
+					return TRUE;
+				}
+			}
+			break;
+#endif /* TIOS_FILE_SUPPORT */
+		
+#ifdef TIOS_UPGRADE_FILE_SUPPORT
+		case FF_TIOS_UPGRADE:
+			*EffectiveSize = FileSize + sizeof (TIOS_UPGRADE_CALC_FOOTER);
+			if (OutputBin)
+			{
+				const char *FileExtCalc;
+				
+				// Determine the destination calculator and set part of the
+				// file extension.
+				switch (DestCalc)
+				{
+					case CALC_TI92:
+						FileExtCalc = "92";
+						break;
+					case CALC_TI89:
+						FileExtCalc = "89";
+						break;
+					case CALC_TI89 | CALC_FLAG_TITANIUM:
+						FileExtCalc = "89ti";
+						break;
+					case CALC_TI92PLUS:
+						FileExtCalc = "9x";
+						break;
+					case CALC_V200:
+						FileExtCalc = "v2";
+						break;
+					default:
+						Error (NULL, "Unknown calculator.");
+						return FALSE;
+				}
+				
+				{
+					SIZE FileNameSize = DestFileSize;
+					
+					// Create a temporary file name string.
+					char CurOutputFileName[FileNameSize+1+4+1+sizeof("tib")];
+					strncpy (CurOutputFileName, DestFile, FileNameSize);
+					
+					// Insert a dash into the file name.
+					CurOutputFileName [FileNameSize++] = '-';
+					
+					// Insert the calculator model into the file name.
+					strcpy (CurOutputFileName + FileNameSize, FileExtCalc);
+					FileNameSize += strlen (FileExtCalc);
+					
+					// Insert the dot into the file name.
+					CurOutputFileName [FileNameSize++] = '.';
+					
+					// Insert the .tib extension.
+					strcpy (CurOutputFileName + FileNameSize, "tib");
+					FileNameSize += sizeof ("tib") - 1;
+					
+					// Zero-terminate the string.
+					CurOutputFileName [FileNameSize] = 0;
+					
+					// Open a file with the specified name.
+					if (!(File->File = fopen (CurOutputFileName, "wb")))
+					{
+						Error (CurOutputFileName, "Could not open file for writing.");
+						return FALSE;
+					}
+					return TRUE;
+				}
+			}
+			else
+				Error (NULL, "Support for `.??u' files is not implemented yet. Use `--outputbin'.");
+			
+			break;
+#endif /* TIOS_UPGRADE_FILE_SUPPORT */
+		
+		default:
+			Error (NULL, "Unrecognized output file format.");
+	}
+	
+	return FALSE;
+}
+
+void FinalizeOutputFile (INT_EXP_FILE *File)
+{
+	// Determine the destination file format and finish it.
+	switch (File->FileFormat)
+	{
+#ifdef TIOS_FILE_SUPPORT
+		case FF_TIOS:
+			{
+				// Write the on-calc file footer.
+				{
+					TI1 EncFileTag;
+					
+					// Export the possible file extension.
+					if (File->Extension)
+					{
+						// Export the leading 0 byte.
+						WriteTI1 (EncFileTag, 0);
+						fwrite (&EncFileTag, 1, 1, File->File);
+						
+						// Export the extension string and terminating 0
+						// byte.
+						fwrite (File->Extension, 1, strlen (File->Extension) + 1, File->File);
+						
+						// Update the checksum.
+						while (*(File->Extension))
+							File->CheckSum += (*(File->Extension++));
+					}
+					
+					// Encode the file tag.
+					WriteTI1 (EncFileTag, File->FileType);
+					
+					// Export the file tag.
+					fwrite (&EncFileTag, 1, 1, File->File);
+					
+					File->CheckSum += (I1) (File->FileType);
+				}
+				
+				// Write the host file footer, if desired.
+				if (!OutputBin)
+				{
+					TIOS_HOST_FILE_FOOTER Footer;
+					
+					memset (&Footer, 0, sizeof (Footer));
+					
+					// Write the checksum.
+					WriteHI2 (Footer.CheckSum, File->CheckSum);
+					
+					// Export the footer.
+					fwrite (&Footer, sizeof (Footer), 1, File->File);
+				}
+			}
+			break;
+#endif /* TIOS_FILE_SUPPORT */
+		
+#ifdef TIOS_UPGRADE_FILE_SUPPORT
+		case FF_TIOS_UPGRADE:
+			{
+				// Write the on-calc upgrade file footer.
+				{
+					TIOS_UPGRADE_CALC_FOOTER Footer;
+					
+					// Since we do not have TI's private key, we cannot
+					// write a real checksum and signature.
+					memset (&Footer, 0, sizeof (Footer));
+					
+					// Encode the fixed data we know.
+					WriteTI2 (Footer.SignatureHeader, 0x020D);
+					WriteTI1 (Footer.SignatureType,     0x40);
+					
+					// Export the footer.
+					fwrite (&Footer, sizeof (Footer), 1, File->File);
+				}
+			}
+			break;
+#endif /* TIOS_UPGRADE_FILE_SUPPORT */
+	}
+	
+	// Close the file.
+	fclose (File->File);
+}
+
+#endif /* !TARGET_EMBEDDED */

+ 45 - 0
export/export.h

@@ -0,0 +1,45 @@
+/* export.h: Routines for file exports
+
+   Copyright (C) 2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef EXPORT_H
+#define EXPORT_H
+
+#include "../generic.h"
+#include "exp_def.h"
+#include "../intrface.h"
+#include "../data.h"
+
+// Export the internal data structures to external files, creating as many
+// files as needed.
+BOOLEAN ExportProgram (const PROGRAM *Program, OUTPUT_FILE_FUNCTION GetOutputFile, OUTPUT_FILE_FINALIZE_FUNCTION FinalizeOutputFile);
+
+// Non-embedded file output functions as defined in intrface.h.
+#ifndef TARGET_EMBEDDED
+#define MAX_NAME_LEN 8
+extern const char *DestFile;
+extern SIZE DestFileSize;
+extern char ProgramFolder[MAX_NAME_LEN+1], ProgramName[MAX_NAME_LEN+1];
+#ifdef DATA_VAR_SUPPORT
+extern char DataFolder[MAX_NAME_LEN+1], DataName[MAX_NAME_LEN+1];
+#endif /* DATA_VAR_SUPPORT */
+extern BOOLEAN OutputBin;
+BOOLEAN GetOutputFile (INT_EXP_FILE *File, SIZE FileSize, unsigned int DestCalc, unsigned int FileRole, unsigned int FileFormat, unsigned int FileType, const char *Extension, BOOLEAN Executable, I4 *EffectiveSize);
+void FinalizeOutputFile (INT_EXP_FILE *File);
+#endif /* !TARGET_EMBEDDED */
+
+#endif

+ 43 - 0
filestat.h

@@ -0,0 +1,43 @@
+/* filestat.h: Definitions for file statistics
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef FILESTAT_H
+#define FILESTAT_H
+
+#include "generic.h"
+
+#include <sys/stat.h>
+
+#ifdef WIN32
+#undef stat
+#define stat _stat
+#endif
+
+typedef struct stat FILE_STATS;
+
+#define GetFileStats(FileName,Stats) (stat ((FileName), &(Stats)))
+
+#define StatGetMode(Stat) ((Stat).st_mode)
+#define StatGetUID(Stat) ((Stat).st_uid)
+#define StatGetGID(Stat) ((Stat).st_gid)
+#define StatGetModificationTime(Stat) ((Stat).st_mtime)
+
+#define StatFileIsNewer(Stat1,Stat2) ((Stat1).st_mtime > (Stat2).st_mtime)
+#define StatCopyAttributes(Stat1,Stat2) ((Stat1).st_mode = (Stat2).st_mode, (Stat1).st_uid = (Stat2).st_uid, (Stat1).st_gid = (Stat2).st_gid, (Stat1).st_atime = (Stat2).st_atime, (Stat1).st_mtime = (Stat2).st_mtime, (Stat1).st_ctime = (Stat2).st_ctime)
+
+#endif

+ 95 - 0
formats/amigaos.h

@@ -0,0 +1,95 @@
+/* amigaos.h: Definitions for AmigaOS object files
+
+   Copyright (C) 2002-2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef AMIGAOS_H
+#define AMIGAOS_H
+
+#include "../generic.h"
+#include "../integers.h"
+
+// *** Constants ***
+
+// Possible Hunk Flags
+#define AMIGAOS_HUNK_FLAG_ADVISORY 0x20000000L
+#define AMIGAOS_HUNK_FLAG_CHIP     0x40000000L
+#define AMIGAOS_HUNK_FLAG_FAST     0x80000000L
+
+#define AMIGAOS_HUNK_TYPE_MASK 0x3FFFFFFFL
+
+// Possible Hunk Types
+#define AMIGAOS_HUNK_UNIT             0x3E7
+#define AMIGAOS_HUNK_NAME             0x3E8
+#define AMIGAOS_HUNK_CODE             0x3E9
+#define AMIGAOS_HUNK_DATA             0x3EA
+#define AMIGAOS_HUNK_BSS              0x3EB
+#define AMIGAOS_HUNK_RELOC_ABS4       0x3EC
+#define AMIGAOS_HUNK_RELOC_REL2       0x3ED
+#define AMIGAOS_HUNK_RELOC_REL1       0x3EE
+#define AMIGAOS_HUNK_EXT              0x3EF
+#define AMIGAOS_HUNK_SYMBOL           0x3F0
+#define AMIGAOS_HUNK_DEBUG            0x3F1
+#define AMIGAOS_HUNK_END              0x3F2
+#define AMIGAOS_HUNK_HEADER           0x3F3
+#define AMIGAOS_HUNK_OVERLAY          0x3F5
+#define AMIGAOS_HUNK_BREAK            0x3F6
+#define AMIGAOS_HUNK_DREL4            0x3F7
+#define AMIGAOS_HUNK_DREL2            0x3F8
+#define AMIGAOS_HUNK_DREL1            0x3F9
+#define AMIGAOS_HUNK_LIB              0x3FA
+#define AMIGAOS_HUNK_INDEX            0x3FB
+#define AMIGAOS_HUNK_RELOC_ABS4_SHORT 0x3FC
+#define AMIGAOS_HUNK_RELOC_REL4       0x3FD
+#define AMIGAOS_HUNK_RELOC_ABS2       0x3FE
+
+#ifdef AMIGAOS_TIGCC_EXTENSIONS
+#define AMIGAOS_HUNK_RELOC_ABS4_POSNEG 0x716CC0L
+#define AMIGAOS_HUNK_RELOC_ABS2_POSNEG 0x716CC1L
+#define AMIGAOS_HUNK_RELOC_ABS1_POSNEG 0x716CC2L
+#endif
+
+// Possible Symbol Types in AMIGAOS_HUNK_EXT
+// Definitions:
+#define AMIGAOS_EXT_SYMBOL     0x00
+#define AMIGAOS_EXT_DEF        0x01
+#define AMIGAOS_EXT_ABS        0x02
+#define AMIGAOS_EXT_RES        0x03
+#define AMIGAOS_EXT_COMMON_DEF 0x04
+// References:
+#define AMIGAOS_EXT_REF_ABS4   0x81
+#define AMIGAOS_EXT_COMMON     0x82
+#define AMIGAOS_EXT_REF_REL2   0x83
+#define AMIGAOS_EXT_REF_REL1   0x84
+#define AMIGAOS_EXT_DEXT4      0x85
+#define AMIGAOS_EXT_DEXT2      0x86
+#define AMIGAOS_EXT_DEXT1      0x87
+#define AMIGAOS_EXT_REF_REL4   0x88
+#define AMIGAOS_EXT_REL_COMMON 0x89
+#define AMIGAOS_EXT_REF_ABS2   0x8A
+#define AMIGAOS_EXT_REF_ABS1   0x8B
+
+// AmigaOS Reloc Flags (TIGCC extension)
+#ifdef AMIGAOS_TIGCC_EXTENSIONS
+#define AMIGAOS_RELOC_UNOPTIMIZABLE 0x80000000L
+#endif
+
+// *** File Type Check ***
+
+// Check whether a file has the AmigaOS-hunks format.
+#define IsAmigaOSFile(File,FileSize) (((FileSize) >= 12) && (ReadTI4 (*((TI4 *) (File))) == AMIGAOS_HUNK_UNIT) && (ReadTI4 (*((TI4 *) ((File) + (FileSize) - 4))) == AMIGAOS_HUNK_END))
+
+#endif

+ 64 - 0
formats/ar.h

@@ -0,0 +1,64 @@
+/* ar.h: Definitions for archive files
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef AR_H
+#define AR_H
+
+#include "../generic.h"
+#include "../integers.h"
+
+// *** Constants ***
+
+#define AR_MEMBER_BOUNDARY 2
+#define AR_MEMBER_PADDING_CHAR '\012'
+
+// *** File Mapping Definitions ***
+
+// Archive File Header
+#define AR_FILE_HEADER_STRING "!<arch>\012"
+#define AR_FILE_HEADER_SIZE 8
+
+// Archive Member Header
+typedef struct ATTRIBUTE_PACKED {
+	char Name[16];  // '/'-terminated file member name.
+	char Date[12];  // File time in ASCII-encoded decimal.
+	char UID[6];    // User ID in ASCII-encoded decimal.
+	char GID[6];    // Group ID in ASCII-encoded decimal.
+	char Mode[8];   // File mode in ASCII-encoded octal.
+	char Size[10];  // Size in ASCII-encoded decimal.
+	char Magic[2];  // Magic string: "`\012".
+} AR_MEMBER_HEADER;
+#define AR_MEMBER_HEADER_SIZE (sizeof (AR_MEMBER_HEADER))
+
+#define AR_MEMBER_MAX_NAME_LENGTH 15
+#define AR_MEMBER_MAGIC "`\012"
+#define AR_MEMBER_MAGIC_SIZE 2
+
+// Archive Symbol Table
+typedef struct ATTRIBUTE_PACKED {
+	TI4 SymbolCount;
+	TI4 PSymbols VAR_ARRAY;
+} AR_SYMBOL_TABLE_HEADER;
+
+// *** File Type Check ***
+
+#include <string.h>
+
+#define IsArchiveFile(File,FileSize) (((FileSize) > AR_FILE_HEADER_SIZE) && (!(strncmp ((const char *) (File), AR_FILE_HEADER_STRING, AR_FILE_HEADER_SIZE))))
+
+#endif

+ 148 - 0
formats/coff.h

@@ -0,0 +1,148 @@
+/* coff.h: Definitions for COFF object files
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef COFF_H
+#define COFF_H
+
+#include "../generic.h"
+#include "../integers.h"
+
+// *** File Mapping Definitions ***
+
+// File Header
+typedef struct ATTRIBUTE_PACKED {
+	TI2 Machine;
+	TI2 SectionCount;
+	TI4 TimeStamp;
+	TI4 PSymbols;
+	TI4 SymbolCount;
+	TI2 OptHdrSize;
+	TI2 Flags;
+} COFF_HEADER;
+
+#define COFF_SECTION_NAME_LEN 8
+
+// Section Header
+typedef struct ATTRIBUTE_PACKED {
+	char Name[COFF_SECTION_NAME_LEN];
+	TI4 PhysicalAddress;
+	TI4 VirtualAddress;
+	TI4 Size;
+	TI4 PData;
+	TI4 PRelocs;
+	TI4 PLines;
+	TI2 RelocCount;
+	TI2 LineCount;
+	TI4 Flags;
+} COFF_SECTION;
+
+typedef COFF_SECTION ATTRIBUTE_PACKED COFF_SECTIONS[];
+
+// Section Flags
+#define COFF_SECTION_TEXT 0x20
+#define COFF_SECTION_DATA 0x40
+#define COFF_SECTION_BSS  0x80
+
+#ifdef COFF_TIGCC_EXTENSIONS
+#define COFF_SECTION_MERGEABLE 0x1000000
+#define COFF_SECTION_UNALIGNED 0x2000000
+#endif
+
+// Symbol Name
+// (either directly or in string table)
+typedef union ATTRIBUTE_PACKED {
+	struct ATTRIBUTE_PACKED {
+		ZI4 Zero;          // Check this with IsZero.
+		TI4 StringOffset;  // If Zero is really zero, this is an offset into the string table.
+	} StringRef;
+	char Name[8];        // Otherwise, this is the name of the symbol.
+} SYM_NAME;
+
+// Symbol Entry
+typedef struct ATTRIBUTE_PACKED {
+	SYM_NAME Name;
+	TI4 Value;
+	TI2 Section;
+	TI2 Type;
+	TI1 Class;
+	TI1 AuxSymbolCount;
+} COFF_SYMBOL;
+
+typedef COFF_SYMBOL ATTRIBUTE_PACKED COFF_SYMBOLS[];
+
+#define COFF_SYMBOL_EXTERNAL 0x02
+
+// Reloc Entry
+typedef struct ATTRIBUTE_PACKED {
+	TI4 Location;
+	TI4 Symbol;
+	TI2 Type;
+} COFF_RELOC;
+
+typedef COFF_RELOC COFF_RELOCS[] ATTRIBUTE_PACKED;
+
+#define COFF_RELOC_DIR2 0x01
+#define COFF_RELOC_DIR4 0x06
+#define COFF_RELOC_ABS1 0x0F
+#define COFF_RELOC_ABS2 0x10
+#define COFF_RELOC_ABS4 0x11
+#define COFF_RELOC_REL1 0x12
+#define COFF_RELOC_REL2 0x13
+#define COFF_RELOC_REL4 0x14
+#define COFF_RELOC_ABS4_NEG 0x45
+
+#ifdef COFF_TIGCC_EXTENSIONS
+#define COFF_RELOC_ABS2_NEG 0x7161
+#define COFF_RELOC_ABS1_NEG 0x7162
+#define COFF_RELOC_UNOPTIMIZABLE 0x8000
+#endif
+
+
+// Line Number Entry
+typedef struct ATTRIBUTE_PACKED {
+	TI4 LineAddress;
+	TI2 LineNumber;
+} COFF_LINE_NUM;
+
+typedef COFF_LINE_NUM ATTRIBUTE_PACKED COFF_LINE_NUMS[];
+
+// *** Helping Definitions ***
+
+typedef struct {
+	FILE_PTR PSections;
+	COUNT SectionCount;
+	FILE_PTR PSymbols;
+	COUNT SymbolCount;
+	FILE_PTR PStrings;
+} COFF_INFO;
+
+#define CreateCoffInfo(Header,Info) \
+({ \
+	(Info).PSections = sizeof (Header) + ReadTI2 ((Header).OptHdrSize); \
+	(Info).SectionCount = ReadTI2 ((Header).SectionCount); \
+	(Info).PSymbols = ReadTI4 ((Header).PSymbols); \
+	(Info).SymbolCount = ReadTI4 ((Header).SymbolCount); \
+	(Info).PStrings = (Info).PSymbols + (Info).SymbolCount * sizeof (COFF_SYMBOL); \
+})
+
+// *** File Type Check ***
+
+// Check whether a file has the COFF format.
+#define IsCOFFFile(File,FileSize) (((FileSize) >= ((SIZE) (sizeof (COFF_HEADER)))) && ((File) [0] == 0x01) && ((File) [1] == 0x50))
+
+#endif

+ 58 - 0
formats/tios.h

@@ -0,0 +1,58 @@
+/* tios.h: Definitions for TIOS files (including host-wrapped)
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef TIOS_H
+#define TIOS_H
+
+#include "../generic.h"
+#include "../integers.h"
+
+// *** Constants ***
+
+#define MAX_TIOS_FILE_SIZE 0xFFEA
+
+#define TIOS_TAG_STR  0x2D
+#define TIOS_TAG_PRGM 0xDC
+#define TIOS_TAG_ASM  0xF3
+#define TIOS_TAG_OTH  0xF8
+
+#define TIOS_LINK_TYPE_STR  0x0C
+#define TIOS_LINK_TYPE_PRGM 0x12
+#define TIOS_LINK_TYPE_ASM  0x21
+#define TIOS_LINK_TYPE_OTH  0x1C
+
+// *** File Mapping Definitions ***
+
+typedef struct ATTRIBUTE_PACKED {
+	char Signature[8];   // "**TI92**", "**TI89**", or "**TI92P*".
+	I1 Reserved1[2];     // 01 00
+	char FolderName[8];  // Folder name.
+	char Desc[40];       // Not used.
+	I1 Reserved2[6];     // 01 00 52 00 00 00
+	char VarName[8];     // Variable name.
+	HI1 LinkType;        // Variable link type (see above).
+	I1 Reserved3[3];     // 00 00 00
+	HI4 FileSize;        // File size from Signature to CheckSum.
+	I1 Reserved4[6];     // A5 5A 00 00 00 00
+} TIOS_HOST_FILE_HEADER;
+
+typedef struct ATTRIBUTE_PACKED {
+	HI2 CheckSum;        // Checksum from DataSize to Tag.
+} TIOS_HOST_FILE_FOOTER;
+
+#endif

+ 36 - 0
formats/tiosupgd.h

@@ -0,0 +1,36 @@
+/* tiosupgd.h: Definitions for TIOS upgrade files
+
+   Copyright (C) 2004 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef TIOS_UPGRADE_H
+#define TIOS_UPGRADE_H
+
+// *** Constants ***
+
+#define MAX_TIOS_UPGRADE_FILE_SIZE_OLD 0x1EDF72
+#define MAX_TIOS_UPGRADE_FILE_SIZE_NEW (MAX_TIOS_UPGRADE_FILE_SIZE_OLD + 0x200000)
+
+// *** File Mapping Definitions ***
+
+typedef struct ATTRIBUTE_PACKED {
+	TI4 CheckSum;         // Encrypted checksum.
+	TI2 SignatureHeader;  // 02 0D
+	TI1 SignatureType;    // 40
+	TI1 Signature[64];    // Signature, encrypted using TI's private key.
+} TIOS_UPGRADE_CALC_FOOTER;
+
+#endif

+ 208 - 0
generic.h

@@ -0,0 +1,208 @@
+/* generic.h: Header file to be included by all other files
+
+   Copyright (C) 2002-2004 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/*
+
+ User-Definable Functionality
+==============================
+
+Specify these in the command line for GCC with the '-D...' switch, or define
+them in this file.
+
+ENABLE_HELP
+  Enable '--help' and '--version' options.
+ENABLE_STATS
+  Enable the display of program statistics via '--verbose'.
+ENABLE_DUMP
+  Include support for dumping the contents (via '--dump...' switches in
+  executable version).
+
+TARGET_EMBEDDED
+  Compile non-executable version which can be linked to a program dynamically
+  or statically.
+TARGET_DLL
+  Compile as a DLL (dynamic link library). This includes TARGET_EMBEDDED.
+
+COFF_SUPPORT
+  Support COFF input files.
+AMIGAOS_SUPPORT
+  Support AmigaOS input files.
+
+TIOS_SUPPORT
+  Support the TIOS output target.
+NOSTUB_DLL_SUPPORT
+  Support the Nostub DLL output target.
+FLASH_OS_SUPPORT
+  Support the Flash OS output target.
+FARGO_SUPPORT
+  Support the Fargo output target.
+
+DATA_VAR_SUPPORT
+  Support external data variables.
+
+TIOS_FILE_SUPPORT
+  Support the TIOS file format if TARGET_EMBEDDED is not defined.
+TIOS_UPGRADE_FILE_SUPPORT
+  Support the TIOS upgrade file format if TARGET_EMBEDDED is not defined.
+
+*/
+
+#ifndef GENERIC_H
+#define GENERIC_H
+
+// This defines the current version of ld-tigcc and ar-tigcc.
+#define PROGRAM_VERSION_STRING "1.06"
+#define COPYRIGHT_NOTICE_STRING "Copyright (C) 2002-2004 Sebastian Reichelt, Kevin Kofler and Billy Charvet"
+
+// Handling of user-defined functionality dependencies.
+#ifdef TARGET_DLL
+#define TARGET_EMBEDDED
+#endif /* TARGET_DLL */
+
+#define ATTRIBUTE_PACKED __attribute__((__packed__))
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+#define ATTRIBUTE_INTERFACE __attribute__((__cdecl__))
+
+// When compiling an executable, don't do anything with exported functions.
+// Otherwise, default to the cdecl calling convention.
+// When compiling a DLL, also mark the function as exported.
+#ifdef TARGET_DLL
+#define ATTRIBUTE_EXPORTED ATTRIBUTE_INTERFACE __declspec(dllexport)
+#else /* !TARGET_DLL */
+#ifdef TARGET_EMBEDDED
+#define ATTRIBUTE_EXPORTED ATTRIBUTE_INTERFACE
+#else /* !TARGET_EMBEDDED */
+#define ATTRIBUTE_EXPORTED 
+#endif /* !TARGET_EMBEDDED */
+#endif /* !TARGET_DLL */
+
+// This specifies how a variable-length array is specified (for example via
+// '[1]', '[0]', or '[]', depending on the capabilities of the compiler).
+#ifdef __GNUC__
+#if __GNUC__ >= 3
+#define VAR_ARRAY []
+#else /* __GNUC__ < 3 */
+#define VAR_ARRAY [0]
+#endif /* __GNUC__ < 3 */
+#else /* !__GNUC__ */
+#define VAR_ARRAY [1]
+#endif /* !__GNUC__ */
+
+// Attempt to auto-detect I1, I2, I4, SI1, SI2 and SI4 based on <limits.h>.
+// It is a good idea to double-check these definitions on every new system
+// you compile on.
+// Such a check is also performed at run time (or optimized away as dead
+// code).
+
+#include <limits.h>
+
+// Make sure that the character types take exactly 1 byte.
+#if UCHAR_MAX != 0xFF
+#error Need 1-byte unsigned char type.
+#endif /* UCHAR_MAX != 0xFF */
+#if SCHAR_MIN != (-0x80) || SCHAR_MAX != 0x7F
+#error Need 1-byte signed char type.
+#endif /* UCHAR_MAX != 0xFF */
+
+// Unsigned types.
+typedef unsigned char I1;
+
+#if USHRT_MAX == 0xFFFF
+typedef unsigned short I2;
+#elif UINT_MAX == 0xFFFF
+typedef unsigned int I2;
+#elif ULONG_MAX == 0xFFFF
+typedef unsigned long I2;
+#else /* no 2-byte unsigned int */
+#error No 2-byte unsigned integer type found.
+#endif /* 2-byte unsigned int */
+
+#if ULONG_MAX == 0xFFFFFFFF
+typedef unsigned long I4;
+#elif UINT_MAX == 0xFFFFFFFF
+typedef unsigned int I4;
+#elif ULONG_LONG_MAX == 0xFFFFFFFF || ULLONG_MAX == 0xFFFFFFFF
+typedef unsigned long long I4;
+#else /* no 4-byte unsigned int */
+#error No 4-byte unsigned integer type found.
+#endif /* 4-byte unsigned int */
+
+// Signed types.
+typedef signed char SI1;
+
+#if SHRT_MIN == (-0x8000) && SHRT_MAX == 0x7FFF
+typedef short SI2;
+#elif INT_MIN == (-0x8000) && INT_MAX == 0x7FFF
+typedef int SI2;
+#elif LONG_MIN == (-0x8000) && LONG_MAX == 0x7FFF
+typedef long SI2;
+#else /* no 2-byte signed int */
+#error No 2-byte signed integer type found.
+#endif /* 2-byte signed int */
+
+#if LONG_MIN == (-0x80000000) && LONG_MAX == 0x7FFFFFFF
+typedef long SI4;
+#elif INT_MIN == (-0x80000000) && INT_MAX == 0x7FFFFFFF
+typedef int SI4;
+#elif (LONG_LONG_MIN == (-0x80000000) && LONG_LONG_MAX == 0x7FFFFFFF) \
+      || (LLONG_MIN == (-0x80000000) && LLONG_MAX == 0x7FFFFFFF)
+typedef long long SI4;
+#else /* no 4-byte signed int */
+#error No 4-byte signed integer type found.
+#endif /* 4-byte signed int */
+
+// Maximum used specific-size integers.
+#define IMAX  I4
+#define SIMAX SI4
+
+// Boolean types.
+typedef I1 B1;
+typedef I2 B2;
+typedef I4 B4;
+
+// These should be all right in most cases.
+typedef int BOOLEAN;
+typedef SI4 OFFSET;
+#define MAX_OFFSET 0x7FFFFFFF
+typedef OFFSET FILE_PTR;
+typedef SI4 COUNT;
+typedef COUNT SIZE;
+
+// These are for setting only! Do not use them to check values.
+#define FALSE 0
+#define TRUE  (!FALSE)
+#define NULL  ((void *) 0)
+
+// Diagnostic messages.
+#ifdef TARGET_EMBEDDED
+extern void Error_Internal   (const char *FileName, const char *Text);
+extern void Warning_Internal (const char *FileName, const char *Text);
+#include <stdio.h>
+#define Error(FileName,Text...)   ({ char s__[256*4]; sprintf (s__, Text); Error_Internal   (FileName, s__); })
+#define Warning(FileName,Text...) ({ char s__[256*4]; sprintf (s__, Text); Warning_Internal (FileName, s__); })
+#else /* !TARGET_EMBEDDED */
+#include <stdio.h>
+#define Error(FileName,Text...)   ({ register const char *filename__ = (FileName); if (filename__) fprintf (stderr, "%s: Error: ",   filename__); else fprintf (stderr, "Error: ");   fprintf (stderr, Text); fprintf (stderr, "\n"); })
+#define Warning(FileName,Text...) ({ register const char *filename__ = (FileName); if (filename__) fprintf (stderr, "%s: Warning: ", filename__); else fprintf (stderr, "Warning: "); fprintf (stderr, Text); fprintf (stderr, "\n"); })
+#endif /* !TARGET_EMBEDDED */
+
+// Macro to check whether two ranges overlap. The ranges include Start1 and
+// Start2, but not End1 and End2.
+#define RangesOverlap(Start1,End1,Start2,End2) (((End1) > (Start2)) && ((End2) > (Start1)))
+
+#endif

+ 712 - 0
import/imp_amig.c

@@ -0,0 +1,712 @@
+/* imp_amig.c: Routines to import an AmigaOS-hunks file
+
+   Copyright (C) 2002-2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "imp_amig.h"
+
+#ifdef AMIGAOS_SUPPORT
+
+#include "../formats/amigaos.h"
+#include "../manip.h"
+#include "../special.h"
+#include "../bincode/fix_m68k.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// reallocate array, free it if reallocation failed, zero out the added size
+// otherwise
+static void *crealloc(void *array, SIZE oldSize, SIZE newSize)
+{
+	void *p = realloc(array, newSize);
+	if (!p) {
+		if (array) {
+			free(array);
+		}
+		return NULL;
+	}
+	if (newSize > oldSize) {
+		memset(((I1*)p)+oldSize, 0, newSize-oldSize);
+	}
+	return p;
+}
+
+// Defines by how much the reloc target hunks array is grown.
+#define RELOC_TARGET_HUNKS_INCREMENT (5 * sizeof(SECTION*))
+
+// Import an AmigaOS-hunks file into the internal data structures.
+BOOLEAN ImportAmigaOSFile (PROGRAM *Program, const I1 *File, SIZE FileSize, const char *FileName)
+{
+	// Call this for a nice and clean failing exit.
+#define Fail() ({if(currSectionData) free(currSectionData);if(hunkName) free(hunkName);if(symName) free(symName);if(relocTargetHunks) free(relocTargetHunks);return FALSE;})
+#define FailWithError(ErrorMsg...) ({Error (FileName, ErrorMsg); Fail ();})
+#define TestMem(Ptr) ({ if (!(Ptr)) {FailWithError ("Out of memory.");} })
+
+	// Check if a given object with a given type is completely inside the file.
+#define IsInFile(Ptr,Type) ((Ptr) >= File && (Ptr) + sizeof (Type) <= File + FileSize)
+#define TestInFile(Ptr,Type) ({ if (!(IsInFile (Ptr, Type))) {FailWithError ("Corrupt A68k object file.");} })
+
+	// Get next big-endian longint. We will need this in many places here, since
+	// unlike COFF, AmigaOS-hunks is a sequential format, not a pointer-based one.
+#define GetNextTI4(Ptr) ({register TI4 __TI4;TestInFile(Ptr,TI4);__TI4=(*((TI4*)Ptr));Ptr+=4;ReadTI4(__TI4);})
+
+	// Local Variables
+	const I1 *ptr = File;
+	I4 hunkSize;
+	char *hunkName = NULL, *symName = NULL;
+	SECTION *currSection = NULL;
+	SYMBOL *currSymbol;
+	I1 *currSectionData = NULL;
+	OFFSET StartupNumber;
+	BOOLEAN InitializeBSS = TRUE;
+	BOOLEAN AllRelocs = FALSE;
+	COUNT numRelocTargetHunks=0; // number of hunks which are candidates for reloc targets
+	SECTION **relocTargetHunks=NULL;
+	SIZE relocTargetHunksSize=0;
+	const RELOC *relocHint=NULL;
+
+	// Read unit hunk
+	if (GetNextTI4(ptr) != AMIGAOS_HUNK_UNIT)
+		// This should already be trapped by IsAmigaOSFile, but just to make sure.
+		FailWithError("Corrupt A68k object file (unit hunk missing).");
+	hunkSize = GetNextTI4(ptr)<<2;
+	TestInFile(ptr,I1[hunkSize]);
+	ptr += hunkSize;
+
+	while (ptr < (File + FileSize)) {
+		I4 hunkType = GetNextTI4(ptr);
+
+		// Hunk flags:
+		// AMIGAOS_HUNK_FLAG_ADVISORY:
+		//	 A68k never generates this one
+		if (hunkType & AMIGAOS_HUNK_FLAG_ADVISORY)
+			FailWithError("Unsupported hunk flag `advisory'.");
+		// AMIGAOS_HUNK_FLAG_CHIP,
+		// AMIGAOS_HUNK_FLAG_FAST:
+		//	 simply ignore those flags, they have no meaning on TI calculators
+		hunkType &= AMIGAOS_HUNK_TYPE_MASK;
+
+		switch (hunkType) {
+			// beginning section - there should be only one of those, handled above
+			case AMIGAOS_HUNK_UNIT:
+				FailWithError("Corrupt A68k object file (duplicate unit hunk).");
+				break;
+
+			// name of the section to follow - temporarily store a pointer to it
+			case AMIGAOS_HUNK_NAME:
+				hunkSize = GetNextTI4(ptr)<<2;
+				TestInFile(ptr,I1[hunkSize]);
+				hunkName = malloc(hunkSize+1);
+				TestMem(hunkName);
+				strncpy(hunkName,ptr,hunkSize);
+				hunkName[hunkSize]=0;
+				ptr += hunkSize;
+				break;
+
+			case AMIGAOS_HUNK_CODE:
+			case AMIGAOS_HUNK_DATA:
+				// allocate space for at least one more reloc target hunk
+				while ((SIZE) ((numRelocTargetHunks+1) * sizeof(SECTION*)) > relocTargetHunksSize) {
+					relocTargetHunks = crealloc(relocTargetHunks, relocTargetHunksSize,
+																			relocTargetHunksSize+RELOC_TARGET_HUNKS_INCREMENT);
+					TestMem(relocTargetHunks);
+					relocTargetHunksSize += RELOC_TARGET_HUNKS_INCREMENT;
+				}
+
+				// invent a section name if no reasonable one given
+				if (!hunkName || !*hunkName || *hunkName==' ') {
+					if (hunkName) {
+						free(hunkName);
+					}
+					hunkName = malloc(sizeof(".data"));
+					TestMem(hunkName);
+					strcpy(hunkName,((hunkType == AMIGAOS_HUNK_CODE)?".text":".data"));
+				}
+
+				// determine whether it is a startup section
+				// This is very important as we cannot omit it.
+				StartupNumber = GetStartupSectionNumber (hunkName, strlen(hunkName));
+
+				hunkSize = GetNextTI4(ptr)<<2;
+				// omit empty sections to simplify the output
+				if ((!StartupNumber) && (!hunkSize)) {
+					free(hunkName);
+					hunkName=NULL;
+					break; // no data to skip - there is none!
+				}
+
+				// Section is not empty (or a startup section).
+				// Try to allocate data for the section, if necessary.
+				TestInFile(ptr,I1[hunkSize]);
+				TestMem ((currSectionData = calloc (hunkSize, 1)));
+				memcpy (currSectionData, ptr, hunkSize);
+				ptr += hunkSize;
+			
+				// create a new section, initialize it, and append it to the list of sections.
+				if (relocTargetHunks[numRelocTargetHunks]) {
+					currSection = relocTargetHunks[numRelocTargetHunks];
+				} else {
+					currSection = calloc (1, sizeof (SECTION));
+					TestMem (currSection);
+					currSection->Parent = Program;
+					currSection->FileName = FileName;
+				}
+
+				currSection->Data = currSectionData;
+				currSection->Size = hunkSize;
+				currSection->Code = (hunkType == AMIGAOS_HUNK_CODE);
+				currSection->Initialized = TRUE;
+				currSection->StartupNumber = StartupNumber;
+				currSection->Constructors = (!(strcmp (hunkName, ".ctors")));
+				currSection->Destructors = (!(strcmp (hunkName, ".dtors")));
+				currSection->CanCutRanges = AllRelocs;
+
+				InsertSection (Program, currSection);
+
+				if (!(CreateSectionSymbol (currSection, hunkName)))
+					Fail ();
+
+				relocTargetHunks[numRelocTargetHunks++] = currSection;
+				currSectionData = NULL; // don't free handles already in linked list!
+
+				if (hunkType == AMIGAOS_HUNK_CODE && Program->OptimizeInfo->OptimizeNOPs)
+					M68kRemoveTrailingNOP (currSection);
+
+				free(hunkName);
+				hunkName = NULL;
+				break;
+
+			case AMIGAOS_HUNK_BSS:
+				// allocate space for at least one more reloc target hunk
+				while ((SIZE) ((numRelocTargetHunks+1) * sizeof(SECTION*)) > relocTargetHunksSize) {
+					relocTargetHunks = crealloc(relocTargetHunks, relocTargetHunksSize,
+																			relocTargetHunksSize+RELOC_TARGET_HUNKS_INCREMENT);
+					TestMem(relocTargetHunks);
+					relocTargetHunksSize += RELOC_TARGET_HUNKS_INCREMENT;
+				}
+
+				// invent a section name if no reasonable one given
+				if (!hunkName || !*hunkName || *hunkName==' ') {
+					if (hunkName) {
+						free(hunkName);
+					}
+					hunkName = malloc(sizeof(".bss"));
+					TestMem(hunkName);
+					strcpy(hunkName,".bss");
+				}
+
+				hunkSize = GetNextTI4(ptr)<<2;
+				// omit empty sections to simplify the output
+				if (!hunkSize) break; // no data to skip - there is none!
+
+				// Section is not empty.
+				// create a new section, initialize it, and append it to the list of sections.
+				if (relocTargetHunks[numRelocTargetHunks]) {
+					currSection = relocTargetHunks[numRelocTargetHunks];
+				} else {
+					currSection = calloc (1, sizeof (SECTION));
+					TestMem (currSection);
+					currSection->Parent = Program;
+					currSection->FileName = FileName;
+				}
+
+				currSection->Initialized = InitializeBSS;
+				currSection->Size = hunkSize;
+				currSection->CanCutRanges = AllRelocs;
+
+				Append (Program->Sections, currSection);
+
+				if (!(CreateSectionSymbol (currSection, hunkName)))
+					Fail ();
+
+				relocTargetHunks[numRelocTargetHunks++] = currSection;
+
+				free(hunkName);
+				hunkName = NULL;
+				break;
+
+			case AMIGAOS_HUNK_RELOC_REL1:
+			case AMIGAOS_HUNK_RELOC_REL2:
+			case AMIGAOS_HUNK_RELOC_REL4:
+			case AMIGAOS_HUNK_RELOC_ABS2:
+			case AMIGAOS_HUNK_RELOC_ABS4:
+				// make sure we have a section to put those relocs into
+				if (!currSection)
+					FailWithError("Relocation hunk (type `0x%lX') without context.",(long)hunkType);
+
+				{
+					SIZE relocSize = 0;
+					BOOLEAN relative = FALSE;
+					switch (hunkType) {
+						case AMIGAOS_HUNK_RELOC_REL1:
+							relative = TRUE;
+							relocSize = 1;
+							break;
+						case AMIGAOS_HUNK_RELOC_REL2:
+							relative = TRUE;
+						case AMIGAOS_HUNK_RELOC_ABS2:
+							relocSize = 2;
+							break;
+						case AMIGAOS_HUNK_RELOC_REL4:
+							relative = TRUE;
+						case AMIGAOS_HUNK_RELOC_ABS4:
+							relocSize = 4;
+							break;
+					}
+
+					hunkSize = GetNextTI4(ptr); // hunkSize is the number of relocs here.
+
+					while (hunkSize) {
+						OFFSET targetHunkNumber = GetNextTI4(ptr);
+						SECTION *targetHunk;
+						OFFSET i;
+
+						// allocate space for at least one more reloc target hunk
+						while ((SIZE) ((targetHunkNumber+1) * sizeof(SECTION*)) > relocTargetHunksSize) {
+							relocTargetHunks = crealloc(relocTargetHunks, relocTargetHunksSize,
+														relocTargetHunksSize+RELOC_TARGET_HUNKS_INCREMENT);
+							TestMem(relocTargetHunks);
+							relocTargetHunksSize += RELOC_TARGET_HUNKS_INCREMENT;
+						}
+
+						targetHunk = relocTargetHunks[targetHunkNumber];
+						if (!targetHunk) {
+							// create placeholder for section right now
+							targetHunk = calloc (1, sizeof (SECTION));
+							TestMem(targetHunk);
+							// initialize the fields to something acceptable to the backend:
+							// the dummy section is a 0-byte BSS section
+							targetHunk->Parent = Program;
+							targetHunk->FileName = FileName;
+
+							// This will get overwritten when the real section will be read in.
+							// Otherwise, the "targetHunk" section reference was invalid, hence
+							// the name of the dummy symbol.
+							if (!(CreateSectionSymbol (targetHunk, "(invalid AmigaOS target section)")))
+								Fail ();
+
+							relocTargetHunks[targetHunkNumber] = targetHunk;
+						}
+
+						for (i=0; i<(SIZE)hunkSize; i++) {
+							RELOC *newReloc;
+							I4 location = GetNextTI4(ptr);
+							OFFSET targetOffset = 0;
+							BOOLEAN unoptimizable = FALSE;
+#ifdef AMIGAOS_TIGCC_EXTENSIONS
+							unoptimizable = !!(location&AMIGAOS_RELOC_UNOPTIMIZABLE);
+							location &= ~AMIGAOS_RELOC_UNOPTIMIZABLE;
+#endif
+							newReloc = calloc (1, sizeof (RELOC));
+							TestMem (newReloc);
+							newReloc->Parent = currSection;
+							newReloc->Location = location;
+							newReloc->Target.Symbol = targetHunk->SectionSymbol;
+							newReloc->Target.SymbolName = newReloc->Target.Symbol->Name;
+							newReloc->Size = relocSize;
+							newReloc->Relative = relative;
+							newReloc->Unoptimizable = unoptimizable;
+							if ((OFFSET) (location+relocSize) <= currSection->Size) {
+								if (currSection->Data) {
+									targetOffset = ReadSTI(currSection->Data+location,relocSize);
+									// Zero out the section contents.
+									memset(currSection->Data+location,0,relocSize);
+									// We have to guess the first bytes of the targetOffset. This is
+									// the easiest way to do it.
+									if (targetHunk != currSection) {
+										OFFSET maxDistance = 1 << ((relocSize * 8) - 1);
+										// If the section is in front of the current section,
+										// set the reference location as close to the end as possible.
+										if (targetHunkNumber < (numRelocTargetHunks - 1)) {
+											location = targetHunk->Size - (maxDistance - 1);
+										// Otherwise, set the reference location as close to the start as possible.
+										} else {
+											location = maxDistance;
+										}
+									}
+									{
+										SI4 difference = targetOffset - location;
+										if (relocSize <= 1) {
+											targetOffset = location + ((SI1) difference);
+										} else if (relocSize <= 2) {
+											targetOffset = location + ((SI2) difference);
+										}
+									}
+								} else {
+									Warning(FileName,"Adding reloc at 0x%lX in section `%s' without data to section `%s'.",(long)location,currSection->SectionSymbol->Name,newReloc->Target.SymbolName);
+								}
+							} else {
+								Warning(FileName,"Invalid reloc location `0x%lX' in size 4 reloc table for origin section `%s' and target section `%s'",(long)location,currSection->SectionSymbol->Name,newReloc->Target.SymbolName);
+							}
+
+							// Apply architecture-specific fixes to the offset.
+							newReloc->Target.Offset = ((currSection->Code && newReloc->Target.Symbol->Parent->Code) ? M68kFixTargetOffset (targetOffset, newReloc->Size, newReloc->Relative) : targetOffset);
+
+							// Calculate the remaining part of the offset.
+							newReloc->FixedOffset = targetOffset - newReloc->Target.Offset;
+
+							// Put this reloc into the linked list.
+							InsertReloc(currSection,newReloc);
+						}
+
+						hunkSize = GetNextTI4(ptr);
+					}
+				}
+				break;
+
+#ifdef AMIGAOS_TIGCC_EXTENSIONS
+			case AMIGAOS_HUNK_RELOC_ABS1_POSNEG:
+			case AMIGAOS_HUNK_RELOC_ABS2_POSNEG:
+			case AMIGAOS_HUNK_RELOC_ABS4_POSNEG:
+				// make sure we have a section to put those relocs into
+				if (!currSection)
+					FailWithError("Relocation hunk (type `0x%lX') without context.",(long)hunkType);
+
+				{
+					OFFSET i;
+					SIZE relocSize = 0;
+
+					switch (hunkType) {
+						case AMIGAOS_HUNK_RELOC_ABS1_POSNEG:
+							relocSize = 1;
+							break;
+						case AMIGAOS_HUNK_RELOC_ABS2_POSNEG:
+							relocSize = 2;
+							break;
+						case AMIGAOS_HUNK_RELOC_ABS4_POSNEG:
+							relocSize = 4;
+							break;
+					}
+
+					hunkSize = GetNextTI4(ptr); // hunkSize is the number of relocs here.
+
+					for (i=0; i<(SIZE)hunkSize; i++) {
+						RELOC *newReloc;
+						OFFSET targetHunkNumber = GetNextTI4(ptr);
+						SECTION *targetHunk;
+						I4 location;
+						OFFSET targetOffset;
+						BOOLEAN unoptimizable;
+
+						// allocate space for at least one more reloc target hunk
+						while ((SIZE) ((targetHunkNumber+1) * sizeof(SECTION*)) > relocTargetHunksSize) {
+							relocTargetHunks = crealloc(relocTargetHunks, relocTargetHunksSize,
+														relocTargetHunksSize+RELOC_TARGET_HUNKS_INCREMENT);
+							TestMem(relocTargetHunks);
+							relocTargetHunksSize += RELOC_TARGET_HUNKS_INCREMENT;
+						}
+
+						targetHunk = relocTargetHunks[targetHunkNumber];
+						if (!targetHunk) {
+							// create placeholder for section right now
+							targetHunk = calloc (1, sizeof (SECTION));
+							TestMem(targetHunk);
+							// initialize the fields to something acceptable to the backend:
+							// the dummy section is a 0-byte BSS section
+							targetHunk->Parent = Program;
+							targetHunk->FileName = FileName;
+
+							// This will get overwritten when the real section will be read in.
+							// Otherwise, the "targetHunk" section reference was invalid, hence
+							// the name of the dummy symbol.
+							if (!(CreateSectionSymbol (targetHunk, "(invalid AmigaOS target section)")))
+								Fail ();
+
+							relocTargetHunks[targetHunkNumber] = targetHunk;
+						}
+
+						location = GetNextTI4(ptr);
+						unoptimizable = !!(location&AMIGAOS_RELOC_UNOPTIMIZABLE);
+						location &= ~AMIGAOS_RELOC_UNOPTIMIZABLE;
+						targetOffset = GetNextTI4(ptr);
+						if (GetNextTI4(ptr)) { // negative reloc
+							RELOC *PositiveReloc = NULL;
+
+							// Find a matching positive reloc.
+							PositiveReloc = FindMatchingReloc (currSection, location, relocSize, FALSE, NULL, relocHint);
+
+							if (PositiveReloc) {
+								LOCATION *relation = calloc (1, sizeof (LOCATION));
+								TestMem (relation);
+								relation->Symbol = targetHunk->SectionSymbol;
+								relation->SymbolName = relation->Symbol->Name;
+								relation->Offset = targetOffset;
+								PositiveReloc->Relative = TRUE;
+								PositiveReloc->Relation = relation;
+								HandleLocation(PositiveReloc, relation);
+
+								relocHint = PositiveReloc;
+							} else {
+								Warning (FileName, "Removing negative reloc at 0x%lX with no matching positive reloc.", (long) location);
+							}
+						} else { // positive reloc
+							newReloc = calloc (1, sizeof (RELOC));
+							TestMem (newReloc);
+							newReloc->Parent = currSection;
+							newReloc->Location = location;
+							newReloc->Target.Symbol = targetHunk->SectionSymbol;
+							newReloc->Target.SymbolName = newReloc->Target.Symbol->Name;
+							newReloc->Target.Offset = targetOffset;
+							newReloc->Size = relocSize;
+							newReloc->Relative = FALSE;
+							newReloc->Unoptimizable = unoptimizable;
+
+							// Those hunks put the target offset in the relocation table. However, we have to
+							// read a possible FixedOffset from the data stream.
+							if ((OFFSET) (location+relocSize) <= currSection->Size) {
+								if (currSection->Data) {
+									newReloc->FixedOffset = ReadSTI(currSection->Data+location,relocSize);
+									// Zero out the section contents.
+									memset(currSection->Data+location,0,relocSize);
+								} else {
+									Warning(FileName,"Adding reloc at 0x%lX in section `%s' without data to section `%s'.",(long)location,currSection->SectionSymbol->Name,newReloc->Target.SymbolName);
+								}
+							} else {
+								Warning(FileName,"Invalid reloc location `0x%lX' in extended size %ld reloc table for origin section `%s' and target section `%s'",(long)location,(long)relocSize,currSection->SectionSymbol->Name,newReloc->Target.SymbolName);
+							}
+
+							// Put this reloc into the linked list.
+							InsertReloc(currSection,newReloc);
+						}
+					}
+				}
+				break;
+#endif
+
+			case AMIGAOS_HUNK_END:
+				// Do nothing - we already skipped the hunk type, and that's all we need
+				// to do here.
+				break;
+
+			case AMIGAOS_HUNK_EXT:
+				// make sure we have a section to put those symbols into
+				if (!currSection) 
+					FailWithError("Symbol import/export hunk without context.");
+				hunkSize = GetNextTI4(ptr);
+				while (hunkSize) {
+					// The most significant byte of the size longword encodes the symbol type.
+					hunkType = hunkSize >> 24;
+					hunkSize = (hunkSize&0xffffffL)<<2;
+
+					TestInFile(ptr,I1[hunkSize]);
+					symName = malloc(hunkSize+1);
+					TestMem(symName);
+					strncpy(symName,ptr,hunkSize);
+					symName[hunkSize]=0;
+					ptr+=hunkSize;
+
+					switch (hunkType) {
+						// Definitions:
+						// Absolute definition: we cannot use those, but ignoring them
+						// should not be fatal, so we will ignore them with a warning.
+						case AMIGAOS_EXT_ABS:
+							Warning(FileName,"Cannot handle absolute symbol `%s'.",symName);
+							TestInFile(ptr,TI4);
+							ptr+=4;
+							break;
+
+						// Standard definition (offset from a section):
+						case AMIGAOS_EXT_DEF: {
+							I4 location = GetNextTI4(ptr);
+							if (HandleSpecialSymbol (Program, symName)) {
+								// This is the best we can get without
+								// adding too much extra code: Omit the
+								// initialization for all following BSS
+								// sections.
+								if (!(strcmp(symName,SYM_OMIT_BSS_INIT))) {
+									InitializeBSS=FALSE;
+								}
+								// __ld_all_relocs has to be exported from the
+								// first section. So we'll set CanCutRanges to
+								// TRUE for the section it appears in, and
+								// the variable AllRelocs will handle the rest.
+								else if (!(strcmp(symName,SYM_ALL_RELOCS))) {
+									currSection->CanCutRanges = TRUE;
+									AllRelocs = TRUE;
+								}
+								break;
+							}
+							currSymbol = calloc (1, sizeof (SYMBOL));
+							TestMem(currSymbol);
+							currSymbol->Parent = currSection;
+							currSymbol->Location = location;
+							strncpy(currSymbol->Name,symName,MAX_SYM_LEN);
+							currSymbol->Exported = TRUE;
+							InsertSymbol(currSection,currSymbol);
+							break; }
+
+						// References:
+						case AMIGAOS_EXT_REF_ABS1:
+						case AMIGAOS_EXT_REF_ABS2:
+						case AMIGAOS_EXT_REF_ABS4:
+						case AMIGAOS_EXT_REF_REL1:
+						case AMIGAOS_EXT_REF_REL2:
+						case AMIGAOS_EXT_REF_REL4: {
+							// Those are actually relocs in ld-tigcc terms.
+							OFFSET i;
+
+							SIZE relocSize = 0;
+							BOOLEAN relative = FALSE;
+							switch (hunkType) {
+								case AMIGAOS_EXT_REF_REL1:
+									relative = TRUE;
+								case AMIGAOS_EXT_REF_ABS1:
+									relocSize = 1;
+									break;
+								case AMIGAOS_EXT_REF_REL2:
+									relative = TRUE;
+								case AMIGAOS_EXT_REF_ABS2:
+									relocSize = 2;
+									break;
+								case AMIGAOS_EXT_REF_REL4:
+									relative = TRUE;
+								case AMIGAOS_EXT_REF_ABS4:
+									relocSize = 4;
+									break;
+							}
+
+							hunkSize = GetNextTI4(ptr); // hunkSize is the number of relocs here.
+
+							for (i=0; i<(SIZE)hunkSize; i++) {
+								RELOC *newReloc;
+								I4 location = GetNextTI4(ptr);
+								BOOLEAN unoptimizable = FALSE;
+								char *newName = malloc(strlen(symName)+1);
+								TestMem(newName);
+								strcpy(newName,symName);
+#ifdef AMIGAOS_TIGCC_EXTENSIONS
+								unoptimizable = !!(location&AMIGAOS_RELOC_UNOPTIMIZABLE);
+								location &= ~AMIGAOS_RELOC_UNOPTIMIZABLE;
+#endif
+								newReloc = calloc (1, sizeof (RELOC));
+								TestMem(newReloc);
+								newReloc->Parent = currSection;
+								newReloc->Location = location;
+								newReloc->Target.SymbolName = newName;
+								newReloc->Size = relocSize;
+								newReloc->Relative = relative;
+								newReloc->Unoptimizable = unoptimizable;
+								if ((OFFSET) (location+relocSize) <= currSection->Size) {
+									if (currSection->Data) {
+										newReloc->FixedOffset = ReadSTI(currSection->Data+location,relocSize);
+										memset(currSection->Data+location,0,relocSize);
+									} else {
+										Warning(FileName,"Adding reloc at 0x%lX in section `%s' without data to symbol `%s'.",(long)location,currSection->SectionSymbol->Name,symName);
+									}
+								} else {
+									Warning(FileName,"Invalid reloc location `0x%lX' in size %ld reference table for origin section `%s' and target symbol `%s'",(long)location,(long)relocSize,currSection->SectionSymbol->Name,symName);
+								}
+
+								// Put this reloc into the linked list
+								InsertReloc(currSection,newReloc);
+							}
+							// The (unused) last copy of the symbol name will be freed below.
+							break; }
+
+						// Other symbol types are never used by A68k.
+						default:
+							FailWithError("Unsupported AmigaOS symbol type `0x%lX'.",hunkType);
+							break;
+					}
+
+					free(symName);
+					symName = NULL; // Prevent symName from getting freed again.
+					hunkSize = GetNextTI4(ptr);
+				}
+				break;
+
+			case AMIGAOS_HUNK_SYMBOL:
+				// debugging symbol table - contains local (non-exported) symbols,
+				// which are in the object file for debugging purposes only
+
+				// make sure we have a section to put those symbols into
+				if (!currSection) 
+					FailWithError("Symbol table hunk without context.");
+
+				hunkSize = GetNextTI4(ptr)<<2;
+				while (hunkSize) {
+					SIZE symSize;
+					I4 location;
+					SYMBOL *symbol;
+					BOOLEAN found=FALSE;
+
+					TestInFile(ptr,I1[hunkSize]);
+					symSize = (hunkSize<MAX_SYM_LEN)?hunkSize:MAX_SYM_LEN;
+
+					for_each(symbol,currSection->Symbols) {
+						if (!strncmp(symbol->Name,ptr,symSize) && !(ptr[symSize])) {
+							found = TRUE;
+							break;
+						}
+					}
+
+					if (found) { // symbol already present - it was already defined as global
+						ptr+=hunkSize;
+
+						// skip location
+						TestInFile(ptr,TI4);
+						ptr+=4;
+					} else {
+						// copy symbol name
+						currSymbol = calloc (1, sizeof (SYMBOL));
+						TestMem(currSymbol);
+						currSymbol->Parent = currSection;
+						strncpy(currSymbol->Name,ptr,symSize);
+						currSymbol->Name[symSize+1]=0;
+						ptr+=hunkSize;
+						// Do NOT handle special symbols specially here! Those symbols are
+						// local and should NOT be interpreted by the linker.
+
+						// copy location
+						location = GetNextTI4(ptr);
+						currSymbol->Location = location;
+
+						//register symbol
+						InsertSymbol(currSection,currSymbol);
+					}
+
+					hunkSize = GetNextTI4(ptr)<<2;
+				}
+				break;
+
+			// The other hunk types are not well-documented or not applicable here, and A68k never generates them anyway.
+			default:
+				FailWithError("Unsupported AmigaOS hunk type `0x%lX'.",hunkType);
+				break;
+		}
+	}
+
+	// free allocated memory
+	if (hunkName) {
+		Warning(FileName,"Hunk name (`%s') with no corresponding hunk.",hunkName);
+		free(hunkName);
+	}
+	if (relocTargetHunks) {
+		free(relocTargetHunks);
+	}
+
+	return TRUE; // success
+
+#undef GetNextTI4
+#undef TestInFile
+#undef IsInFile
+#undef TestMem
+#undef FailWithError
+#undef Fail
+}
+
+#endif /* AMIGAOS_SUPPORT */

+ 33 - 0
import/imp_amig.h

@@ -0,0 +1,33 @@
+/* imp_amig.h: Routines to import an AmigaOS-hunks file
+
+   Copyright (C) 2002-2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef IMP_AMIG_H
+#define IMP_AMIG_H
+
+#include "../generic.h"
+
+#ifdef AMIGAOS_SUPPORT
+
+#include "../data.h"
+
+// Import a AmigaOS-hunks file into the internal data structures.
+BOOLEAN ImportAmigaOSFile (PROGRAM *Program, const I1 *File, SIZE FileSize, const char *FileName);
+
+#endif /* AMIGAOS_SUPPORT */
+
+#endif

+ 182 - 0
import/imp_ar.c

@@ -0,0 +1,182 @@
+/* imp_ar.c: Routines to import an archive file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "imp_ar.h"
+
+#include "../formats/ar.h"
+#include "import.h"
+#include "../manip.h"
+#include "../special.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+// Add an archive to the program's list of available archives.
+// Do not free the data related to the File parameter after this.
+BOOLEAN AddArchiveFile (PROGRAM *Program, const I1 *File, SIZE FileSize, const char *FileName)
+{
+	// Check if memory allocation was successful.
+#define TestMem(Ptr) ({ if (!(Ptr)) { Error (FileName, "Out of memory."); return FALSE; } })
+	
+	// Check if a given object with a given type is completely inside the file.
+#define IsInFile(Ptr,Type) (((const I1 *) (Ptr)) >= File && ((const I1 *) (Ptr)) + sizeof (Type) <= File + FileSize)
+#define TestInFile(Ptr,Type) ({ if (!(IsInFile (Ptr, Type))) { Error (FileName, "Corrupt archive file."); return FALSE; } })
+	
+	if (IsArchiveFile (File, FileSize))
+	{
+		const AR_MEMBER_HEADER *SymbolTableHeader = (const AR_MEMBER_HEADER *) (File + AR_FILE_HEADER_SIZE);
+		const AR_SYMBOL_TABLE_HEADER *SymbolTable = (const AR_SYMBOL_TABLE_HEADER *) (SymbolTableHeader + 1);
+		TestInFile (SymbolTableHeader, AR_MEMBER_HEADER);
+		
+		// Check the magic string.
+		if (!(strncmp (SymbolTableHeader->Magic, AR_MEMBER_MAGIC, sizeof (SymbolTableHeader->Magic))))
+		{
+			// Check whether the file contains a symbol table (Name is "/", padded with spaces).
+			if (SymbolTableHeader->Name [0] == '/' && SymbolTableHeader->Name [1] == ' ')
+			{
+				COUNT SymCount;
+				const char *SymbolTableStrings;
+				
+				TestInFile (&(SymbolTable->SymbolCount), TI4);
+				TestInFile (&(SymbolTable->PSymbols), TI4 [SymCount = ReadTI4 (SymbolTable->SymbolCount)]);
+				SymbolTableStrings = (const char *) (&(SymbolTable->PSymbols [SymCount]));
+				
+				// Add a new entry to the program's archive files.
+				{
+					ARCHIVE *Archive = calloc (1, sizeof (ARCHIVE));
+					
+					TestMem (Archive);
+					
+					Archive->Parent = Program;
+					Archive->Data = File;
+					Archive->Size = FileSize;
+					Archive->FileName = FileName;
+					Append (Program->Archives, Archive);
+					
+					{
+						OFFSET CurSym;
+						const char *CurStr = SymbolTableStrings;
+						
+						// Add all symbols to our own list of exported symbols.
+						for (CurSym = 0; CurSym < SymCount; CurSym++)
+						{
+							TestInFile (CurStr, char);
+							
+							{
+								ARCHIVE_SYMBOL *Symbol = calloc (1, sizeof (ARCHIVE_SYMBOL));
+								
+								TestMem (Symbol);
+								
+								Symbol->Parent = Archive;
+								Symbol->Name = CurStr;
+								Symbol->ObjectFile = GetArchiveObject (Archive, ReadTI4 (SymbolTable->PSymbols [CurSym]));
+								if (strstr (CurStr, SYMOP_NOT))
+									Symbol->ContainsInversion = TRUE;
+								Append (Archive->Symbols, Symbol);
+								CheckGlobalImports (Program, Symbol);
+							}
+							
+							CurStr += strlen (CurStr) + 1;
+						}
+					}
+				}
+			
+				return TRUE;
+			}
+			else
+			{
+				Error (FileName, "Archive has no symbol table.");
+				return FALSE;
+			}
+		}
+		else
+		{
+			Error (FileName, "Corrupt archive file (magic string mismatch).");
+			return FALSE;
+		}
+	}
+	else
+	{
+		Error (FileName, "Not an archive file.");
+		return FALSE;
+	}
+	
+#undef TestInFile
+#undef IsInFile
+#undef TestMem
+}
+
+// Import an object file inside an archive file into the internal data
+// structures.
+BOOLEAN ImportArchiveObject (PROGRAM *Program, ARCHIVE_OBJECT *Object)
+{
+	// Check if a given object with a given type is completely inside the file.
+#define IsInFile(Ptr,Type) (((const I1 *) (Ptr)) >= Archive->Data && ((const I1 *) (Ptr)) + sizeof (Type) <= Archive->Data + Archive->Size)
+#define TestInFile(Ptr,Type) ({ if (!(IsInFile (Ptr, Type))) { Error (Archive->FileName, "Corrupt archive file."); return FALSE; } })
+	
+	// Safety check for the ImportArchiveSymbol macro.
+	if (!Object)
+		return FALSE;
+	
+	// Do not import a file twice.
+	if (!(Object->Imported))
+	{
+		ARCHIVE *Archive = Object->Parent;
+		
+		// Find the location of the appropriate object file.
+		const AR_MEMBER_HEADER *ObjectFileHeader = (const AR_MEMBER_HEADER *) (Archive->Data + Object->FileOffset);
+		const I1 *ObjectFile = (const I1 *) (ObjectFileHeader + 1);
+		SIZE ObjectFileSize;
+		
+		TestInFile (ObjectFileHeader, AR_MEMBER_HEADER);
+		
+		// Check the magic string.
+		if (!(strncmp (ObjectFileHeader->Magic, AR_MEMBER_MAGIC, sizeof (ObjectFileHeader->Magic))))
+		{
+			// Get the size of the file.
+			ObjectFileSize = strtol (ObjectFileHeader->Size, NULL, 10);
+			
+			// Check whether the file is completely inside the archive.
+			TestInFile (ObjectFile, I1 [ObjectFileSize]);
+			
+			if (ObjectFileSize > 0)
+			{
+				// Mark the file as imported.
+				Object->Imported = TRUE;
+				
+				// Import the file.
+				return (ImportObjectFile (Program, ObjectFile, ObjectFileSize, Archive->FileName));
+			}
+			else
+			{
+				Error (Archive->FileName, "Corrupt object in archive.");
+				return FALSE;
+			}
+		}
+		else
+		{
+			Error (Archive->FileName, "Corrupt archive file (magic string mismatch).");
+			return FALSE;
+		}
+	}
+	else
+		return TRUE;
+	
+#undef TestInFile
+#undef IsInFile
+}

+ 37 - 0
import/imp_ar.h

@@ -0,0 +1,37 @@
+/* imp_ar.h: Routines to import an archive file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef IMP_AR_H
+#define IMP_AR_H
+
+#include "../generic.h"
+#include "../data.h"
+
+// Add an archive to the program's list of available archives.
+// Do not free the data related to the File parameter after this.
+BOOLEAN AddArchiveFile (PROGRAM *Program, const I1 *File, SIZE FileSize, const char *FileName);
+
+// Import an object file inside an archive file into the internal data
+// structures.
+BOOLEAN ImportArchiveObject (PROGRAM *Program, ARCHIVE_OBJECT *Object);
+
+// Import an exported symbol of an archive file into the internal data
+// structures.
+#define ImportArchiveSymbol(Program,Symbol) (ImportArchiveObject ((Program), (Symbol)->ObjectFile))
+
+#endif

+ 597 - 0
import/imp_coff.c

@@ -0,0 +1,597 @@
+/* imp_coff.c: Routines to import a COFF file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+   Copyright (C) 2003-2004 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "imp_coff.h"
+
+#ifdef COFF_SUPPORT
+
+#include "../formats/coff.h"
+#include "../manip.h"
+#include "../special.h"
+#include "../bincode/fix_m68k.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Extra Information for a COFF Section
+typedef struct {
+	SECTION *Section;
+	char Name[COFF_SECTION_NAME_LEN+1];
+	OFFSET VAddr;
+} SEC_INFO;
+
+// Extra Information for a COFF Symbol
+typedef struct {
+	SYMBOL *Symbol;
+} SYM_INFO;
+
+// Import a COFF file into the internal data structures.
+BOOLEAN ImportCOFFFile (PROGRAM *Program, const I1 *File, SIZE FileSize, const char *FileName)
+{
+	// Call this for a nice and clean failing exit.
+#define Fail() ({ if (SymInfo) free (SymInfo); if (SecInfo) free (SecInfo); return FALSE; })
+#define TestMem(Ptr) ({ if (!(Ptr)) { Error (FileName, "Out of memory."); Fail (); } })
+	
+	// Check if a given object with a given type is completely inside the file.
+#define IsInFile(Ptr,Type) (((const I1 *) (Ptr)) >= File && ((const I1 *) (Ptr)) + sizeof (Type) <= File + FileSize)
+#define TestInFile(Ptr,Type) ({ if (!(IsInFile (Ptr, Type))) { Error (FileName, "Corrupt COFF object file."); Fail (); } })
+	
+	// Local Variables
+	COFF_INFO FileInfo;
+	const COFF_HEADER *COFFHeader = (const COFF_HEADER *) File;
+	const COFF_SECTIONS *COFFSections;
+	const COFF_SYMBOLS *COFFSymbols;
+	const char *COFFStringTable;
+	SEC_INFO *SecInfo = NULL;
+	SYM_INFO *SymInfo = NULL;
+	BOOLEAN InitializeBSS = TRUE;
+	BOOLEAN AllRelocs = FALSE;
+	OFFSET CurCOFFSectionNumber, CurCOFFSymbolNumber, CurCOFFRelocNumber;
+	
+	// Create file information in FileInfo.
+	CreateCoffInfo (*COFFHeader, FileInfo);
+	// Get pointer to list of sections.
+	COFFSections = (const COFF_SECTIONS *) (File + FileInfo.PSections);
+	// Get pointer to list of symbols.
+	COFFSymbols = (const COFF_SYMBOLS *) (File + FileInfo.PSymbols);
+	// Get pointer to string table.
+	COFFStringTable = (const char *) (File + FileInfo.PStrings);
+	
+	// *** Process File-Local Special Symbols ***
+	
+	// For each symbol...
+	for (CurCOFFSymbolNumber = 0; CurCOFFSymbolNumber < FileInfo.SymbolCount; CurCOFFSymbolNumber++)
+	{
+		// Get pointer to this symbol.
+		const COFF_SYMBOL *CurCOFFSymbol = &((*COFFSymbols) [CurCOFFSymbolNumber]);
+		TestInFile (CurCOFFSymbol, COFF_SYMBOL);
+		
+		// Only process long symbol names.
+		if (IsZero (CurCOFFSymbol->Name.StringRef.Zero))
+		{
+			// If the name is special, set the appropriate flag.
+			const char *SymName = COFFStringTable + ReadTI4 (CurCOFFSymbol->Name.StringRef.StringOffset);
+			TestInFile (SymName, char);
+			if (!(strcmp (SymName, SYM_OMIT_BSS_INIT)))
+				InitializeBSS = FALSE;
+			else if (!(strcmp (SymName, SYM_ALL_RELOCS)))
+				AllRelocs = TRUE;
+			else if (!(strcmp (SymName, SYM_IGNORE_GLOBAL_IMPORTS)))
+				Program->IgnoreGlobalImports = TRUE;
+		}
+		
+		// Skip corresponding auxiliary symbols.
+		CurCOFFSymbolNumber += ReadTI1 (CurCOFFSymbol->AuxSymbolCount);
+	}
+	
+	// *** Import Sections ***
+	
+	// Create extra section information table.
+	SecInfo = calloc (FileInfo.SectionCount, sizeof (SEC_INFO));
+	TestMem (SecInfo);
+	
+	// For each section...
+	for (CurCOFFSectionNumber = 0; CurCOFFSectionNumber < FileInfo.SectionCount; CurCOFFSectionNumber++)
+	{
+		OFFSET StartupNumber;
+		// Get pointer to this section.
+		const COFF_SECTION *CurCOFFSection = &((*COFFSections) [CurCOFFSectionNumber]);
+		TestInFile (CurCOFFSection, COFF_SECTION);
+		// Put name and virtual address into section information.
+		strncpy (SecInfo[CurCOFFSectionNumber].Name, CurCOFFSection->Name, sizeof (CurCOFFSection->Name));
+		SecInfo[CurCOFFSectionNumber].VAddr = ReadTI4 (CurCOFFSection->VirtualAddress);
+		
+		// At first, determine whether it is a startup section.
+		// This is very important, as we cannot omit it.
+		StartupNumber = GetStartupSectionNumber (CurCOFFSection->Name, sizeof (CurCOFFSection->Name));
+		
+		// Omit empty sections to simplify the output.
+		if ((!StartupNumber) && (IsZeroI4 (CurCOFFSection->Size)))
+		{
+			// Section is empty.
+			// Check if the number of relocs is empty as well. If not, there
+			// is probably an error in the object file.
+			if (!(IsZeroI4 (CurCOFFSection->RelocCount)))
+				Warning (FileName, "Empty section %ld has relocs.", (long) CurCOFFSectionNumber);
+		}
+		else
+		{
+			// Section is not empty (or a startup section).
+			I4 Flags = ReadTI4 (CurCOFFSection->Flags);
+			
+			// Try to allocate data for the section, if necessary.
+			BOOLEAN HasData = (!(Flags & COFF_SECTION_BSS)) && (!(IsZeroI4 (CurCOFFSection->PData)));
+			SIZE Size = ReadTI4 (CurCOFFSection->Size);
+			I1 *Data = NULL;
+			
+			if (HasData)
+			{
+				const I1 *SrcData = File + ReadTI4 (CurCOFFSection->PData);
+				TestInFile (SrcData, I1 [Size]);
+				TestMem ((Data = calloc (Size, 1)));
+				memcpy (Data, SrcData, Size);
+			}
+			
+			{
+				char *SectionName = SecInfo[CurCOFFSectionNumber].Name;
+				
+				// Create a new section.
+				SECTION *Section = calloc (1, sizeof (SECTION));
+				TestMem (Section);
+				Section->Parent = Program;
+				
+				// Initialize the section.
+				Section->Data = Data;
+				Section->Size = Size;
+				// If neither the code flag nor the data flag is specified, default to data for non-startup sections and to code for startup sections.
+				Section->Code = Data && ((Flags & COFF_SECTION_TEXT) || (StartupNumber && (!(Flags & COFF_SECTION_DATA))));
+#ifdef COFF_TIGCC_EXTENSIONS
+				// Read TIGCC COFF section flags.
+				Section->Mergeable = !!(Flags & COFF_SECTION_MERGEABLE);
+				Section->Unaligned = !!(Flags & COFF_SECTION_UNALIGNED);
+#endif
+				Section->Initialized   = Data || InitializeBSS;
+				Section->StartupNumber = StartupNumber;
+				Section->Constructors  = (!(strcmp (SectionName, ".ctors")));
+				Section->Destructors   = (!(strcmp (SectionName, ".dtors")));
+				Section->CanCutRanges  = AllRelocs;
+				Section->FileName      = FileName;
+				
+				// Append/insert the section.
+				InsertSection (Program, Section);
+				
+				// Create a section symbol for this section.
+				if (!(CreateSectionSymbol (Section, SectionName)))
+					Fail ();
+				
+				// Put this section into the extra information table.
+				SecInfo[CurCOFFSectionNumber].Section = Section;
+			}
+			// Now we are stuck: We should import the symbols all at once,
+			// when all of the sections have been imported. We cannot import
+			// the relocation entries now because they reference symbols. So
+			// the only good way is to import all sections first, then import
+			// all symbols, and then the relocs for each section.
+		}
+	}
+	
+	// *** Import Symbols ***
+	
+	// Create extra symbol information table.
+	SymInfo = calloc (FileInfo.SymbolCount, sizeof (SYM_INFO));
+	TestMem (SymInfo);
+	
+	// For each symbol...
+	for (CurCOFFSymbolNumber = 0; CurCOFFSymbolNumber < FileInfo.SymbolCount; CurCOFFSymbolNumber++)
+	{
+		// Get pointer to this symbol.
+		const COFF_SYMBOL *CurCOFFSymbol = &((*COFFSymbols) [CurCOFFSymbolNumber]);
+		TestInFile (CurCOFFSymbol, COFF_SYMBOL);
+		
+		{
+			// Temporary placeholder for symbol section.
+			OFFSET SymSection = (SI2) (ReadTI2 (CurCOFFSymbol->Section));
+			// Temporary placeholder for symbol value.
+			OFFSET SymVal = (SI4) (ReadTI4 (CurCOFFSymbol->Value));
+			// Temporary placeholder for symbol class.
+			I1 SymClass = (ReadTI1 (CurCOFFSymbol->Class));
+			// Temporary placeholder for symbol name.
+			char SymName[MAX_SYM_LEN+1];
+			// Set to zero for terminating zero byte.
+			memset (SymName, 0, MAX_SYM_LEN + 1);
+			// COFF symbol names are stored in a strange way. This is how to get them.
+			if (IsZero (CurCOFFSymbol->Name.StringRef.Zero))
+			{
+				const char *Str = COFFStringTable + ReadTI4 (CurCOFFSymbol->Name.StringRef.StringOffset);
+				TestInFile (Str, char);
+				strncpy (SymName, Str, MAX_SYM_LEN);
+			}
+			else
+				strncpy (SymName, CurCOFFSymbol->Name.Name, 8);
+			
+			// Handle the symbol if it is special for this linker, only proceed if not.
+			if (!(HandleSpecialSymbol (Program, SymName)))
+			{
+				// Check if the section number is nonzero. If it is zero, the symbol
+				// is somewhat special.
+				if (SymSection)
+				{
+					// Check if the section number is positive and the class is
+					// <100. If not, the symbol is very special, e.g. absolute
+					// or debugging; so ignore it.
+					if (SymSection > 0 && SymClass < 100)
+					{
+						// Check if the section number is valid.
+						if (SymSection <= FileInfo.SectionCount)
+						{
+							SECTION *Section = SecInfo[SymSection-1].Section;
+							
+							// The symbol is a real label in a real section.
+							// First, check whether we didn't omit the section.
+							// If we did, no problem; we need to give an error only if
+							// someone actually uses the symbol (in a reloc).
+							if (Section)
+							{
+								BOOLEAN Exported = (ReadTI1 (CurCOFFSymbol->Class) == COFF_SYMBOL_EXTERNAL);
+								OFFSET Location = SymVal - SecInfo[SymSection-1].VAddr;
+								
+								if ((Location == 0) && (!Exported) && (!(strncmp (SymName, SecInfo[SymSection-1].Name, COFF_SECTION_NAME_LEN))))
+								{
+									// Use the section symbol instead.
+									SymInfo[CurCOFFSymbolNumber].Symbol = Section->SectionSymbol;
+									// Rename the section symbol to the full section name as stored in the symbol.
+									CreateSectionSymbol (Section, SymName);
+								}
+								else
+								{
+									// Simply create a new symbol entry in that section.
+									SYMBOL *Symbol = calloc (1, sizeof (SYMBOL));
+									TestMem (Symbol);
+									Symbol->Parent   = Section;
+									Symbol->Location = Location;
+									strcpy (Symbol->Name, SymName);
+									Symbol->Exported = Exported;
+									InsertSymbol (Section, Symbol);
+									
+									// Put this symbol in the extra information table.
+									SymInfo[CurCOFFSymbolNumber].Symbol = Symbol;
+								}
+							}
+						}
+						else
+							Warning (FileName, "Ignoring symbol in nonexisting section number %ld.", (long) SymSection);
+					}
+				}
+				else
+				{
+					// The section number is zero. This may have two reasons:
+					// If the value is zero as well, this is an external reference.
+					// If the value is positive, the symbol is a common uninitialized
+					// symbol (i.e. BSS).
+					if (SymVal > 0)
+					{
+						// The symbol is a common uninitialized symbol. Make one and
+						// put it in the extra information table.
+						if (!((SymInfo[CurCOFFSymbolNumber].Symbol = MakeCommonSymbol (Program, SymName, SymVal, InitializeBSS, FileName))))
+							Fail ();
+					}
+					// Otherwise, the symbol is an external reference or some strange
+					// unknown thing; we cannot handle it any further here, as we
+					// cannot assign it to a section.
+				}
+			}
+		}
+		
+		// Skip corresponding auxiliary symbols.
+		CurCOFFSymbolNumber += ReadTI1 (CurCOFFSymbol->AuxSymbolCount);
+	}
+	
+	// *** Import Relocation Entries ***
+	
+	// For each section...
+	for (CurCOFFSectionNumber = 0; CurCOFFSectionNumber < FileInfo.SectionCount; CurCOFFSectionNumber++)
+	{
+		const RELOC *RelocHint = NULL;
+		
+		// Get pointer to this section.
+		const COFF_SECTION *CurCOFFSection = &((*COFFSections) [CurCOFFSectionNumber]);
+		// Get pointer to section in internal data structure.
+		SECTION *Section = SecInfo[CurCOFFSectionNumber].Section;
+		// Only proceed if we didn't omit the section.
+		if (Section)
+		{
+			COUNT RelocCount = ReadTI2 (CurCOFFSection->RelocCount);
+			const COFF_RELOCS *CurCOFFRelocs = (const COFF_RELOCS *) (File + ReadTI4 (CurCOFFSection->PRelocs));
+			// For each reloc...
+			for (CurCOFFRelocNumber = 0; CurCOFFRelocNumber < RelocCount; CurCOFFRelocNumber++)
+			{
+				OFFSET SymNum, COFFLocation;
+				// Get pointer to this reloc.
+				const COFF_RELOC *CurCOFFReloc = &((*CurCOFFRelocs) [CurCOFFRelocNumber]);
+				TestInFile (CurCOFFReloc, COFF_RELOC);
+				// Read the location (for better error messages).
+				COFFLocation = ReadTI4 (CurCOFFReloc->Location);
+				// Temporary placeholder for symbol number.
+				SymNum = ReadTI4 (CurCOFFReloc->Symbol);
+				// Check if the symbol number is valid.
+				if (SymNum >= 0 && SymNum < FileInfo.SymbolCount)
+				{
+					// Determine the reloc's size and relation.
+					SIZE RelocSize = 0;
+					BOOLEAN Relative = FALSE;
+					BOOLEAN Negative = FALSE;
+					BOOLEAN Unoptimizable = FALSE;
+					I2 RelocType = ReadTI2 (CurCOFFReloc->Type);
+#ifdef COFF_TIGCC_EXTENSIONS
+					Unoptimizable = (RelocType & COFF_RELOC_UNOPTIMIZABLE);
+					RelocType &= ~COFF_RELOC_UNOPTIMIZABLE;
+#endif
+					switch (RelocType)
+					{
+						case COFF_RELOC_REL1:
+							Relative = TRUE;
+						case COFF_RELOC_ABS1:
+							RelocSize = 1;
+							break;
+						case COFF_RELOC_REL2:
+							Relative = TRUE;
+						case COFF_RELOC_ABS2:
+							RelocSize = 2;
+							break;
+						case COFF_RELOC_REL4:
+							Relative = TRUE;
+						case COFF_RELOC_ABS4:
+							RelocSize = 4;
+							break;
+#ifdef COFF_TIGCC_EXTENSIONS
+						case COFF_RELOC_ABS1_NEG:
+							RelocSize = 1;
+							Negative = TRUE;
+							break;
+						case COFF_RELOC_ABS2_NEG:
+							RelocSize = 2;
+							Negative = TRUE;
+							break;
+#endif
+						case COFF_RELOC_ABS4_NEG:
+							RelocSize = 4;
+							Negative = TRUE;
+							break;
+					}
+					// Check whether we were able to translate the reloc type.
+					if (RelocSize > 0)
+					{
+						OFFSET Location = COFFLocation - SecInfo[CurCOFFSectionNumber].VAddr;
+						SYMBOL *Symbol = SymInfo[SymNum].Symbol;
+						
+						// Check if we really imported the target symbol.
+						if (Symbol)
+						{
+							// Yes, we did.
+							// Find the virtual address of the target section.
+							// We will need this to subtract the virtual location of the target symbol.
+							OFFSET VAddr = 0;
+							OFFSET COFFSec;
+							for (COFFSec = 0; COFFSec < FileInfo.SectionCount; COFFSec++)
+							{
+								if (SecInfo[COFFSec].Section == Symbol->Parent)
+								{
+									VAddr = SecInfo[COFFSec].VAddr;
+									break;
+								}
+							}
+							
+							// Handle negative relocs by searching for matching positive ones.
+							if (Negative)
+							{
+								// Search for a matching positive reloc.
+								RELOC *PositiveReloc = FindMatchingReloc (Section, Location, RelocSize, FALSE, NULL, RelocHint);
+								
+								// We can only do something with the negative reloc
+								// if we have found a matching positive one.
+								if (PositiveReloc)
+								{
+									// Make the positive reloc relative, with
+									// this reloc's target as the relation.
+									LOCATION *Relation = calloc (1, sizeof (LOCATION));
+									TestMem (Relation);
+									Relation->Symbol     = Symbol;
+									Relation->SymbolName = Relation->Symbol->Name;
+									PositiveReloc->Relative = TRUE;
+									PositiveReloc->Relation = Relation;
+									// Subtract the target offset for this reloc
+									// from the positive reloc's fixed offset.
+									PositiveReloc->FixedOffset += VAddr + Symbol->Location;
+									HandleLocation (PositiveReloc, Relation);
+									
+									RelocHint = PositiveReloc;
+								}
+								else
+									Warning (FileName, "Removing negative reloc at 0x%lX in section %ld to `%s' with no matching positive reloc.", (long) Location, (long) CurCOFFSectionNumber, Symbol->Name);
+							}
+							else // Positive
+							{
+								OFFSET Offset = 0;
+								
+								// Now all we need to do is to set up a reloc to this symbol.
+								RELOC *Reloc = calloc (1, sizeof (RELOC));
+								TestMem (Reloc);
+								Reloc->Parent            = Section;
+								Reloc->Location          = Location;
+								Reloc->Target.Symbol     = Symbol;
+								Reloc->Target.SymbolName = Reloc->Target.Symbol->Name;
+								Reloc->Size              = RelocSize;
+								Reloc->Relative          = Relative;
+								Reloc->Unoptimizable     = Unoptimizable;
+								
+								// Calculate the reloc's target offset.
+								// The section contents contain the virtual
+								// address of the target.
+								if ((Location >= 0) && (Location + RelocSize <= Section->Size))
+								{
+									if (Section->Data)
+									{
+										Offset = ReadSTI (Section->Data + Location, RelocSize);
+										memset (Section->Data + Location, 0, RelocSize);
+									}
+									else
+										Warning (FileName, "Adding reloc %ld at 0x%lX in section %ld without data to `%s'.", (long) CurCOFFRelocNumber, (long) COFFLocation, (long) CurCOFFSectionNumber, Reloc->Target.SymbolName);
+								}
+								else
+									Warning (FileName, "Adding reloc %ld at 0x%lX outside of section %ld to `%s'.", (long) CurCOFFRelocNumber, (long) COFFLocation, (long) CurCOFFSectionNumber, Reloc->Target.SymbolName);
+								// Subtract the virtual address of the target symbol,
+								// since we only want the difference to the symbol.
+								Offset -= VAddr + Symbol->Location;
+								// Relative relocs contain the difference between
+								// the virtual target address and the current
+								// virtual address. So we have to add the current
+								// virtual address to get only the offset.
+								if (Relative)
+									Offset += Location + SecInfo[CurCOFFSectionNumber].VAddr;
+								
+								// For compatibility with files generated by
+								// unpatched versions of GNU as, treat offsets from
+								// section symbols as TargetOffset rather than
+								// FixedOffset.
+								// Apply architecture-specific fixes to the target offset.
+								// These fixes are not needed if the correct target
+								// symbol is referenced.
+								if (Symbol == Symbol->Parent->SectionSymbol)
+									Reloc->Target.Offset = ((Section->Code && Symbol->Parent->Code) ? M68kFixTargetOffset (Offset, RelocSize, Relative) : Offset);
+								
+								// Calculate the remaining part of the offset.
+								Reloc->FixedOffset = Offset - Reloc->Target.Offset;
+								
+								// Append the reloc to the linked list.
+								InsertReloc (Section, Reloc);
+							}
+						}
+						else
+						{
+							// No, we didn't. We have to find the cause of this.
+							// If it is an externally defined symbol, set up a
+							// reloc anyway. If not, give a warning.
+							const COFF_SYMBOL *CurCOFFSymbol = &((*COFFSymbols) [SymNum]);
+							// Check whether it is an external symbol.
+							if (IsZeroI2 (CurCOFFSymbol->Section) && IsZeroI4 (CurCOFFSymbol->Value))
+							{
+								// Read the symbol name.
+								char *SymName = calloc (MAX_SYM_LEN + 1, 1);
+								TestMem (SymName);
+								// COFF symbol names are stored in a strange way. This is how to get them.
+								if (IsZero (CurCOFFSymbol->Name.StringRef.Zero))
+								{
+									const char *Str = COFFStringTable + ReadTI4 (CurCOFFSymbol->Name.StringRef.StringOffset);
+									TestInFile (Str, char);
+									strncpy (SymName, Str, MAX_SYM_LEN);
+								}
+								else
+									strncpy (SymName, CurCOFFSymbol->Name.Name, 8);
+								
+								// Set up the reloc, without a specified symbol.
+								// Handle negative relocs by searching for matching positive ones.
+								if (Negative)
+								{
+									// Search for a matching positive reloc.
+									RELOC *PositiveReloc = FindMatchingReloc (Section, Location, RelocSize, FALSE, NULL, RelocHint);
+									
+									// We can only do something with the negative reloc
+									// if we have found a matching positive one.
+									if (PositiveReloc)
+									{
+										// Make the positive reloc relative, with
+										// this reloc's target as the relation.
+										LOCATION *Relation = calloc (1, sizeof (LOCATION));
+										TestMem (Relation);
+										Relation->SymbolName = SymName;
+										PositiveReloc->Relative = TRUE;
+										PositiveReloc->Relation = Relation;
+										HandleLocation (PositiveReloc, Relation);
+										
+										RelocHint = PositiveReloc;
+									}
+									else
+										Warning (FileName, "Removing negative reloc at 0x%lX in section %ld to `%s' with no matching positive reloc.", (long) Location, (long) CurCOFFSectionNumber, SymName);
+								}
+								else // Positive
+								{
+									RELOC *Reloc = calloc (1, sizeof (RELOC));
+									TestMem (Reloc);
+									Reloc->Parent            = Section;
+									Reloc->Location          = Location;
+									Reloc->Target.SymbolName = SymName;
+									Reloc->Size              = RelocSize;
+									Reloc->Relative          = Relative;
+									Reloc->Unoptimizable     = Unoptimizable;
+									
+									// Calculate the reloc's target offset
+									// (which is written into the fixed offset).
+									// The section contents contain the virtual
+									// address of the target.
+									if (Location >= 0 && Location + RelocSize <= Section->Size)
+									{
+										if (Section->Data)
+										{
+											Reloc->FixedOffset = ReadSTI (Section->Data + Location, RelocSize);
+											memset (Section->Data + Location, 0, RelocSize);
+										}
+										else
+											Warning (FileName, "Adding reloc %ld at 0x%lX in section %ld without data to `%s'.", (long) CurCOFFRelocNumber, (long) COFFLocation, (long) CurCOFFSectionNumber, Reloc->Target.SymbolName);
+									}
+									else
+										Warning (FileName, "Adding reloc %ld at 0x%lX outside of section %ld to `%s'.", (long) CurCOFFRelocNumber, (long) COFFLocation, (long) CurCOFFSectionNumber, Reloc->Target.SymbolName);
+									// Relative relocs contain the difference between
+									// the virtual target address (which is 0 or some
+									// small offset in this case) and the current
+									// virtual address. So we have to add the current
+									// virtual address to get only the offset.
+									if (Relative)
+										Reloc->FixedOffset += Location + SecInfo[CurCOFFSectionNumber].VAddr;
+									
+									// Add the reloc to the section.
+									InsertReloc (Section, Reloc);
+								}
+							}
+							else
+								Warning (FileName, "Ignoring reloc %ld at 0x%lX in section %ld to unimported symbol %ld.", (long) CurCOFFRelocNumber, (long) COFFLocation, (long) CurCOFFSectionNumber, (long) SymNum);
+						}
+					}
+					else
+						Warning (FileName, "Ignoring reloc %ld at 0x%lX in section %ld with unknown type `0x%lX'.", (long) CurCOFFRelocNumber, (long) COFFLocation, (long) CurCOFFSectionNumber, (long) (ReadTI2 (CurCOFFReloc->Type)));
+				}
+				else
+					Warning (FileName, "Ignoring reloc %ld at 0x%lX in section %ld to nonexisting symbol %ld.", (long) CurCOFFRelocNumber, (long) COFFLocation, (long) CurCOFFSectionNumber, (long) SymNum);
+			}
+		}
+	}
+	
+	// *** Finished ***
+	
+	// Free allocated memory and exit with a positive result.
+	free (SymInfo);
+	free (SecInfo);
+	return TRUE;
+	
+#undef TestInFile
+#undef IsInFile
+#undef TestMem
+#undef Fail
+}
+
+#endif /* COFF_SUPPORT */

+ 33 - 0
import/imp_coff.h

@@ -0,0 +1,33 @@
+/* imp_coff.h: Routines to import a COFF file
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef IMP_COFF_H
+#define IMP_COFF_H
+
+#include "../generic.h"
+
+#ifdef COFF_SUPPORT
+
+#include "../data.h"
+
+// Import a COFF file into the internal data structures.
+BOOLEAN ImportCOFFFile (PROGRAM *Program, const I1 *File, SIZE FileSize, const char *FileName);
+
+#endif /* COFF_SUPPORT */
+
+#endif

+ 48 - 0
import/import.c

@@ -0,0 +1,48 @@
+/* import.c: Routines for object file imports
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "import.h"
+
+#ifdef COFF_SUPPORT
+#include "../formats/coff.h"
+#include "imp_coff.h"
+#endif /* COFF_SUPPORT */
+
+#ifdef AMIGAOS_SUPPORT
+#include "../formats/amigaos.h"
+#include "imp_amig.h"
+#endif /* AMIGAOS_SUPPORT */
+
+// Import an object file with an arbitrary format.
+BOOLEAN ImportObjectFile (PROGRAM *Program, const I1 *File, SIZE FileSize, const char *FileName)
+{
+#ifdef COFF_SUPPORT
+	if (IsCOFFFile (File, FileSize))
+		return (ImportCOFFFile (Program, File, FileSize, FileName));
+	else
+#endif /* COFF_SUPPORT */
+#ifdef AMIGAOS_SUPPORT
+	if (IsAmigaOSFile (File, FileSize))
+		return (ImportAmigaOSFile (Program, File, FileSize, FileName));
+	else
+#endif /* AMIGAOS_SUPPORT */
+	{
+		Error (FileName, "Unknown object file format.");
+		return FALSE;
+	}
+}

+ 28 - 0
import/import.h

@@ -0,0 +1,28 @@
+/* import.h: Routines for object file imports
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef IMPORT_H
+#define IMPORT_H
+
+#include "../generic.h"
+#include "../data.h"
+
+// Import an object file with an arbitrary format.
+BOOLEAN ImportObjectFile (PROGRAM *Program, const I1 *File, SIZE FileSize, const char *FileName);
+
+#endif

+ 896 - 0
insert/comprrlc.c

@@ -0,0 +1,896 @@
+/* comprrlc.c: Routines for compressed relocation tables
+
+   Copyright (C) 2003-2004 Kevin Kofler
+   Portions copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "comprrlc.h"
+
+#include "../integers.h"
+#include "../special.h"
+#include "../manip.h"
+#include "model/list.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Append a new I1 to the section. Return TRUE on success, FALSE on failure.
+BOOLEAN AppendI1ToSection (SECTION *Section, I1 NewI1)
+{
+	TI1 *NewSpace = (TI1 *) AllocateSpaceInSection (Section, 1);
+	if (!NewSpace)
+		return FALSE;
+	
+	WriteTI1 (*NewSpace, NewI1);
+	return TRUE;
+}
+
+// Append a new I2 to the section. Return TRUE on success, FALSE on failure.
+BOOLEAN AppendI2ToSection (SECTION *Section, I2 NewI2)
+{
+	TI2 *NewSpace = (TI2 *) AllocateSpaceInSection (Section, 2);
+	if (!NewSpace)
+		return FALSE;
+
+	WriteTI2 (*NewSpace, NewI2);
+	return TRUE;
+}
+
+// Emit a compressed reloc nibble buffer. Return TRUE on success, FALSE on
+// failure.
+BOOLEAN EmitCompressedRelocNibbles (SECTION *Section, I4 NibbleCount, I1 *NibbleBuffer)
+{
+	I4 CurrentNibble;
+	I4 ByteCount = (NibbleCount + 1) >> 1; // number of bytes emitted as nibbles
+	
+#define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) return FALSE;})
+#define AppendNibbles(n1, n2) AppendI1 (((n1) << 4) + (n2))
+	
+	if (NibbleCount < 3)
+	{
+		// Too few entries for nibbles, emit bytes.
+		for (CurrentNibble = 0; CurrentNibble < NibbleCount; CurrentNibble++)
+			AppendI1 (NibbleBuffer[CurrentNibble] + 1);
+		return TRUE;
+	}
+	
+	// Emit the first nibble pair.
+	AppendNibbles (ByteCount + 6, *NibbleBuffer);
+	
+	// Emit the remaining nibble pairs.
+	for (CurrentNibble = 1; CurrentNibble < NibbleCount - 1; CurrentNibble += 2)
+		AppendNibbles (NibbleBuffer[CurrentNibble], NibbleBuffer[CurrentNibble + 1]);
+	
+	// If the number of nibbles is even, emit the last one as a byte.
+	if (!(NibbleCount & 1))
+		AppendI1 (NibbleBuffer[NibbleCount - 1] + 1);
+	
+#undef AppendI1
+#undef AppendNibbles
+	
+	return TRUE;
+}
+
+// Emit a compressed reloc. If Offset is -1, end the relocation table. Return
+// TRUE on success, FALSE on failure.
+BOOLEAN EmitCompressedReloc (SECTION *Section, OFFSET Offset)
+{
+// Maximum number of nibbles in a nibble group.
+#define MAX_NIBBLES 9
+	
+	static I4 NibbleCount = 0;
+	static I1 NibbleBuffer[MAX_NIBBLES] = {};
+	
+#define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) return FALSE;})
+#define AppendI2(i2) ({if (!AppendI2ToSection (Section, (i2))) return FALSE;})
+#define EmitNibbles() ({if (!EmitCompressedRelocNibbles (Section, NibbleCount, NibbleBuffer)) return FALSE; NibbleCount = 0;})
+	
+	if (Offset == -1)
+	{
+		// End the relocation table.
+		EmitNibbles();
+		AppendI1(0);
+	}
+	else
+	{
+Continue:
+		if (Offset < 16)
+		{
+			// We have a nibble. Check if we have room left in the buffer.
+			if (NibbleCount == MAX_NIBBLES)
+			{
+				// Room full. Emit the nibbles in the buffer.
+				EmitNibbles();
+			}
+			// Put the nibble into the buffer.
+			NibbleBuffer[NibbleCount++] = Offset;
+		}
+		else
+		{
+			// We have a byte or a word. Emit the nibbles in the buffer.
+			EmitNibbles();
+			
+			if (Offset < 127)
+			{
+				// We have a byte.
+				AppendI1 (Offset + 1);
+			}
+			else if (Offset < 16510)
+			{
+				// We have a word.
+				AppendI2 (Offset + 49025);
+			}
+			else
+			{
+				// We have an 0xFFFF word and an additional offset.
+				AppendI2 (65535);
+				Offset -= 16510;
+				goto Continue;
+			}
+		}
+	}
+	
+#undef AppendI1
+#undef AppendI2
+#undef EmitNibbles
+#undef MAX_NIBBLES
+	
+	return TRUE;
+}
+
+// Emit a compressed reloc given the true offset between the 2 relocs. Compute
+// the offset as used in the compressed reloc table or gives an error message if
+// it isn't representable. Call EmitCompressedReloc with the computed offset.
+// Return TRUE on success, FALSE on failure.
+BOOLEAN EmitCompressedRelocFromActualOffset (SECTION *Section, OFFSET Offset)
+{
+	if ((Offset > 0) && (Offset & 1))
+	{
+		Error (Section->FileName, "Odd offset `%ld' between 2 absolute relocs. Even offset needed.", (long) Offset);
+		return FALSE;
+	}
+
+	if (Offset < 4)
+	{
+		Error (Section->FileName, (Offset >= 0) ? "Cannot emit overlapping absolute relocs (offset `0x%lx', need offset >=4)."
+		                                        : "Invalid internal reloc sorting order (offset `-0x%lx', need positive offset).",
+		       (Offset >= 0) ? Offset : -Offset);
+		return FALSE;
+	}
+	
+	return EmitCompressedReloc (Section, (Offset - 4) >> 1);
+}
+
+// Emit a reloc table in compressed format for the items enumerated in the
+// list model specified by Model.
+BOOLEAN EmitCompressedFormatRelocs (LIST_MODEL *Model, SECTION *SourceSection, const LOCATION *SourceBase, void *UserData, SECTION *Section)
+{
+	if (!SourceSection)
+		SourceSection = Section;
+	
+	SourceSection->Frozen = TRUE;
+	
+	{
+		void *NextItem = NULL;
+		OFFSET Offset;
+		
+		// Output the first item in the list...
+		// Output the target offset, and get the reloc's location.
+		Offset = Model (SourceSection, &NextItem, UserData, FALSE, TRUE);
+		
+		// A return value < 0 indicates that the list was empty.
+		if (Offset >= 0)
+		{
+			OFFSET BaseAddress = GetLocationOffset (SourceSection, SourceBase);
+			
+			if (BaseAddress & 1)
+			{
+				Error (Section->FileName, "Invalid base address `0x%lx'. Even base address needed.", BaseAddress);
+				return FALSE;
+			}
+			
+			if (Offset & 1)
+			{
+				Error (Section->FileName, "Odd offset between base address `0x%lx' and first absolute reloc `0x%lx'. Even offset needed.", BaseAddress, Offset);
+				return FALSE;
+			}
+			
+			if (Offset < BaseAddress)
+			{
+				Error (Section->FileName, "Cannot emit absolute reloc located at `0x%lx' before base address.", Offset);
+				return FALSE;
+			}
+			
+			// Emit the reloc.
+			if (!(EmitCompressedReloc (Section, (Offset - BaseAddress) >> 1)))
+				return FALSE;
+			
+	 		// Output the remaining items in the list...
+			while (NextItem)
+			{
+				// Save the previous offset.
+				OFFSET LastOffset = Offset;
+				
+				// Output the target offset, and get the reloc's location.
+				Offset = Model (SourceSection, &NextItem, UserData, FALSE, TRUE);
+				
+				// A negative offset means the list ends here.
+				if (Offset >= 0)
+				{
+					// Emit the reloc.
+					if (!(EmitCompressedRelocFromActualOffset (Section, Offset - LastOffset)))
+						return FALSE;
+				}
+			}
+		}
+		
+		return EmitCompressedReloc (Section, -1);
+	}
+}
+
+// Append relocation entries in the format required by the TIGCCLIB relocation
+// code. If TargetSection is NULL, append all relocation entries that point to
+// unhandled sections. Otherwise, append all relocation entries pointing to this
+// section.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertCompressedRelocs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
+{
+	// Initialize user data for list model.
+	RELOC_USER_DATA UserData = {TargetSection};
+	
+	// If a target section is specified, it is referenced now, and it may
+	// not be modified any more.
+	if (TargetSection)
+		TargetSection->Frozen = TargetSection->Referenced = TRUE;
+	else
+		// Do code optimizations now, since this might reduce the number
+		// of relocs. If a target section was specified, this is pointless,
+		// as relocs into a separate section can never be optimized away.
+		FixCode (Section->Parent);
+	
+	// Apply the format documented in _compressed_format_relocs.s.
+	if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) RelocListModel, MergedSection, Reference, &UserData, Section)))
+		return FALSE;
+	
+	// If a target section was specified, and its Handled flag was not
+	// set yet, now setting it is probably correct.
+	if (TargetSection)
+		TargetSection->Handled = TRUE;
+	
+	return TRUE;
+}
+
+// Append relocation entries in the format required by the TIGCCLIB relocation
+// code, using InsertCompressedRelocs. If TargetSection is NULL, output an empty
+// relocation table. Otherwise, append all relocation entries pointing to this
+// section.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertCompressedSectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
+{
+	if (TargetSection)
+		return InsertCompressedRelocs (Section, TargetSection, MergedSection, Reference);
+	else
+		return (AllocateSpaceInSection (Section, 1) != NULL);
+}
+
+// Append ROM calls in the format required by the TIGCCLIB relocation code.
+BOOLEAN InsertCompressedROMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Initialize user data for list model.
+	ROM_CALL_USER_DATA UserData = {NULL, 0, -1};
+	
+	// Allocate space for dynamic user data.
+	if (!(UserData.ROMFunctions = calloc (Program->HighestROMCall + 1, sizeof (ROM_CALL_FUNCTION_DATA))))
+	{
+		Error (NULL, "Out of memory while inserting ROM calls with compressed relocs.");
+		return FALSE;
+	}
+	
+	{
+		BOOLEAN Result = TRUE;
+		
+		// Go through all ROM calls and increment the appropriate counters.
+		COUNT ROMRelocCount = GetSectionItemCount ((LIST_MODEL *) ROMCallListModel, MergedSection ? : Section, &UserData);
+		
+		// If no ROM calls are used, do not output anything but the final null-terminator.
+		if (ROMRelocCount > 0)
+		{
+			// Apply the format documented in _compressed_format_rom_calls.s.
+			OFFSET LastFunction = -1;
+			
+			for (UserData.CurFunction = 0; UserData.CurFunction <= Program->HighestROMCall; UserData.CurFunction++)
+			{
+				if (UserData.ROMFunctions[UserData.CurFunction].RelocCount)
+				{
+					// Emit the function number as a compressed offset.
+					// Yes, the offset is intentionally integrated into the table that follows!
+					OFFSET FunctionOffset = UserData.CurFunction - (LastFunction + 1);
+					if (!(EmitCompressedReloc (Section, FunctionOffset)))
+					{
+						Result = FALSE;
+						break;
+					}
+					LastFunction = UserData.CurFunction;
+						
+					// Emit all ROM calls using this function.
+					if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) ROMCallListModel, MergedSection, Reference, &UserData, Section)))
+					{
+						Result = FALSE;
+						break;
+					}
+				}
+			}
+		}
+		
+		// Output the final null-terminator.
+		if (!(AppendI1ToSection (Section, 0)))
+			Result = FALSE;
+		
+		// Free the extra information.
+		free (UserData.ROMFunctions);
+		
+		return Result;
+	}
+}
+
+// Emit a compressed index in PreOs format. Return TRUE on success, FALSE on
+// failure. This is NOT the same format as the compressed relocs!
+BOOLEAN EmitPreOsCompressedIndex (SECTION *Section, COUNT LastIndex, COUNT Index)
+{
+#define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) return FALSE;})
+#define AppendI2(i2) ({if (!AppendI2ToSection (Section, (i2))) return FALSE;})
+	
+	OFFSET Offset = Index - (LastIndex + 1);
+	
+	if (Offset < 0)
+	{
+		// The computed offset must be nonnegative (positive or zero).
+		Error (NULL, "Invalid index sequence during PreOs compressed relocs.");
+		return FALSE;
+	}
+	else
+	{
+		if (Offset <= 253)
+		{
+			// Offsets in the range 0 .. 253 are encoded as a single byte.
+			AppendI1 (Offset);
+		}
+		else if (Offset <= (254 + 255))
+		{
+			// Offsets in the range 254+0 .. 254+255 are encoded as 2 bytes.
+			AppendI1 (254);
+			AppendI1 (Offset - 254);
+		}
+		else
+		{
+			// For offsets larger than 254+255, we don't encode the offset, but
+			// the actual index, in a 3-byte encoding.
+			AppendI1 (255);
+			AppendI2 (Index);
+		}
+	}
+	
+#undef AppendI1
+#undef AppendI2
+	
+	return TRUE;
+}
+
+// Append the section size and relocs to the specified section in the
+// format required by PreOS.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertPreOSSectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
+{
+	if (TargetSection)
+	{
+		// Initialize user data for list model.
+		RELOC_USER_DATA UserData = {TargetSection};
+		
+		// Target size. Round up to the next multiple of 4, and output the size
+		// divided by 4.
+		if (!(AppendI2ToSection (Section, (TargetSection->Size + 3) >> 2)))
+			return FALSE;
+		
+		// Apply the format documented in _preos_program_header.s.
+		return EmitCompressedFormatRelocs ((LIST_MODEL *) RelocListModel, MergedSection, Reference, &UserData, Section);
+	}	
+	else
+	{
+		// Target size:
+		return (AllocateSpaceInSection (Section, 2) != NULL);
+		// The empty relocation table can be omitted.
+	}	
+}
+
+// Append ROM calls in the format required by PreOs.
+BOOLEAN InsertPreOsROMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Initialize user data for list model.
+	ROM_CALL_USER_DATA UserData = {NULL, 0, -1};
+	
+	// Allocate space for dynamic user data.
+	if (!(UserData.ROMFunctions = calloc (Program->HighestROMCall + 1, sizeof (ROM_CALL_FUNCTION_DATA))))
+	{
+		Error (NULL, "Out of memory while inserting ROM calls with compressed relocs.");
+		return FALSE;
+	}
+	
+	{
+		BOOLEAN Result = TRUE;
+		
+#define EmitIndex(oldidx,idx) ({if (!EmitPreOsCompressedIndex (Section, (oldidx), (idx))) { Result = FALSE; goto ROMCallsOutOfMem; } })
+		
+		// Go through all ROM calls and increment the appropriate counters.
+		COUNT ROMRelocCount = GetItemCount ((LIST_MODEL *) ROMCallListModel, Program, NULL, &UserData);
+		
+		// If no ROM calls are used, output an empty table.
+		if (!ROMRelocCount)
+		{
+			EmitIndex (-1, 0);
+		}
+		else
+		{
+			// Apply the format documented in _preos_program_header.s.
+			OFFSET LastFunction = -1;
+			
+			// Output the number of functions used.
+			EmitIndex (-1, UserData.ROMFunctionCount);
+			
+			for (UserData.CurFunction = 0; UserData.CurFunction <= Program->HighestROMCall; UserData.CurFunction++)
+			{
+				if (UserData.ROMFunctions[UserData.CurFunction].RelocCount)
+				{
+					// Output the current function number.
+					EmitIndex (LastFunction, UserData.CurFunction);
+					LastFunction = UserData.CurFunction;
+					
+					// Emit all ROM calls using this function.
+					if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) ROMCallListModel, MergedSection, Reference, &UserData, Section)))
+					{
+						Result = FALSE;
+						goto ROMCallsOutOfMem;
+					}
+				}
+			}
+		}
+		
+#undef EmitIndex
+		
+ROMCallsOutOfMem:
+		// Free the extra information.
+		free (UserData.ROMFunctions);
+		
+		return FALSE;
+	}
+}
+
+// Append RAM calls in the format required by PreOs.
+BOOLEAN InsertPreOsRAMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
+{
+	PROGRAM *Program = Section->Parent;
+	BOOLEAN Result = TRUE;
+	
+	// Initialize user data for list model.
+	RAM_CALL_USER_DATA UserData = {{{NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}}, 0, RT_ALL_TYPES, -1};
+	
+	// Allocate space for dynamic user data.
+	RAM_CALL_TYPE_DATA *CurType = UserData.RAMTypes;
+	RAM_CALL_TYPE LoopType;
+	for (LoopType = 0; LoopType < RAM_CALL_TYPE_COUNT; LoopType++, CurType++)
+	{
+		if (!(CurType->Functions = calloc (Program->HighestRAMCall + 1, sizeof (RAM_CALL_FUNCTION_DATA))))
+		{
+			Error (NULL, "Out of memory while inserting RAM calls with compressed relocs.");
+			Result = FALSE;
+			goto RAMCallsOutOfMem;
+		}
+	}
+	
+	{
+		
+#define EmitIndex(oldidx,idx) ({if (!EmitPreOsCompressedIndex (Section, (oldidx), (idx))) { Result = FALSE; goto RAMCallsOutOfMem; } })
+		
+		// Go through all RAM calls and increment the appropriate counters.
+		COUNT RAMRelocCount = GetItemCount ((LIST_MODEL *) RAMCallListModel, Program, NULL, &UserData);
+		
+		// If no RAM calls are used, output an empty table.
+		if (!RAMRelocCount)
+		{
+			EmitIndex (-1, 0);
+		}
+		else
+		{
+			// Apply the format documented in _preos_program_header.s.
+			OFFSET LastFunction = -1;
+			
+			// Output the number of functions used.
+			EmitIndex (-1, UserData.RAMFunctionCount);
+			
+			// For each possible RAM call type...
+			for (UserData.CurType = 0; UserData.CurType < RAM_CALL_TYPE_COUNT; UserData.CurType++)
+			{
+				// Check if the type has been used.
+				if (UserData.RAMTypes[UserData.CurType].FunctionCount)
+				{
+					// Gather information about the type.
+					SIZE TypeSize = ((UserData.CurType == RT_RAM_CALL_2) || (UserData.CurType == RT_EXTRA_RAM_2) ? 2 : 4);
+					BOOLEAN TypeExtraRAM = (UserData.CurType == RT_EXTRA_RAM_4) || (UserData.CurType == RT_EXTRA_RAM_2);
+					I2 TypeFlags = 0;
+					
+					if (TypeSize < 4)
+						TypeFlags |= (1 << 15);
+					if (TypeExtraRAM)
+						TypeFlags |= (1 << 14);
+					
+					// For each possible RAM function number...
+					for (UserData.CurFunction = 0; UserData.CurFunction <= Program->HighestRAMCall; UserData.CurFunction++)
+					{
+						// Check if a RAM function of this type and number has been used.
+						if (UserData.RAMTypes[UserData.CurType].Functions[UserData.CurFunction].RelocCount)
+						{
+							// Output the type and number.
+							COUNT RAMCallNumber = UserData.CurFunction | TypeFlags;
+							EmitIndex (LastFunction, RAMCallNumber);
+							LastFunction = RAMCallNumber;
+							
+							// Emit all RAM calls using this function.
+							if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) RAMCallListModel, MergedSection, Reference, &UserData, Section)))
+							{
+								Result = FALSE;
+								goto RAMCallsOutOfMem;
+							}
+						}
+					}
+				}
+			}
+		}
+		
+#undef EmitIndex
+		
+RAMCallsOutOfMem:
+		// Free the extra information.
+		for (UserData.CurType = 0; UserData.CurType < RAM_CALL_TYPE_COUNT; UserData.CurType++)
+		{
+			RAM_CALL_FUNCTION_DATA *Functions = UserData.RAMTypes[UserData.CurType].Functions;
+			if (Functions)
+				free (Functions);
+		}
+	}
+	
+	return Result;
+}
+
+// Append libraries and library calls in the format required by PreOs.
+BOOLEAN InsertPreOsLibraries (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Output an empty table if no libraries are referenced.
+	if (IsEmpty (Program->Libraries))
+		return (AppendI1ToSection (Section, 0)); // library count
+	else
+	{
+		BOOLEAN Result = TRUE;
+		
+		// Initialize user data for list model.
+		LIB_CALL_USER_DATA UserData = {0, NULL, 0, 0, NULL, -1, -1};
+		
+		if (InitializeLibCallUserData (&UserData, Program))
+		{
+			
+#define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) { Result = FALSE; goto PreOsLibsOutOfMem; } })
+#define EmitIndex(oldidx,idx) ({if (!EmitPreOsCompressedIndex (Section, (oldidx), (idx))) { Result = FALSE; goto PreOsLibsOutOfMem; } })
+			
+			// Go through all library calls and increment the appropriate counters.
+			COUNT LibRelocCount = GetItemCount ((LIST_MODEL *) LibCallListModel, Program, MergedSection, &UserData);
+			
+			// Output an empty table if no libraries are referenced.
+			if (!LibRelocCount)
+			{
+				AppendI1 (0); // library count
+			}
+			else
+			{
+				// Pointer to the inserted data.
+				I1 *NewData;
+				
+				// The number of requested libraries.
+				COUNT LibCount = UserData.UsedLibCount;
+				AppendI1 (LibCount);
+				
+				// Apply the format documented in _preos_program_header.s.
+				
+				// Allocate space for the library names.
+				NewData = AllocateSpaceInSection (Section, 10 * UserData.UsedLibCount);
+				
+				// Now handle the library names.
+				if (NewData)
+				{
+					// For each referenced library...
+					LIB_DATA *CurLib = UserData.Libs;
+					for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
+					{
+						if (CurLib->FunctionCount)
+						{
+							// Output its name.
+							if (strlen (CurLib->Lib->Name) > 8)
+								Warning (Section->FileName, "Library name `%s' too long; cutting off at 8th character.", CurLib->Lib->Name);
+							strncpy (NewData, CurLib->Lib->Name, 8);
+							WriteTI1 (*((TI1 *) (NewData + 9)), CurLib->Lib->Version);
+							NewData += 10;
+						}
+					}
+				}
+				else
+				{
+					Error (NULL, "Out of memory while inserting libraries with compressed relocs.");
+					Result = FALSE;
+					goto PreOsLibsOutOfMem;
+				}
+				
+				{
+					// For each referenced library...
+					LIB_DATA *CurLib = UserData.Libs;
+					for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
+					{
+						if (CurLib->FunctionCount)
+						{
+							OFFSET LastFunction = -1;
+							
+							// Output the number of exports used.
+							EmitIndex (-1, CurLib->FunctionCount - 1);
+							
+							// For each referenced export...
+							for (UserData.CurFunction = 0; UserData.CurFunction <= CurLib->Lib->Highest; UserData.CurFunction++)
+							{
+								if (CurLib->Functions[UserData.CurFunction].RelocCount)
+								{
+									// Output the export number.
+									EmitIndex (LastFunction, UserData.CurFunction);
+									LastFunction = UserData.CurFunction;
+									
+									// Emit all library calls using this export.
+									if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) LibCallListModel, MergedSection, Reference, &UserData, Section)))
+									{
+										Result = FALSE;
+										goto PreOsLibsOutOfMem;
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		else
+			Result = FALSE;
+		
+PreOsLibsOutOfMem:
+		// Free the extra information.
+		FinalizeLibCallUserData (&UserData);
+		
+#undef AppendI1
+#undef EmitIndex
+		
+		return Result;
+	}
+}
+
+// The reference address used for the compressed relocation tables in PreOs.
+#define PREOS_BASE_ADDR 36
+	
+// Append import tables and relocation entries in the format required by PreOs.
+// In order: library imports, ROM calls, RAM calls, relocs, BSS references. All
+// in one stream, pointed to by a single pointer.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertPreOsCompressedTables (SECTION *Section)
+{
+	PROGRAM *Program = Section->Parent;
+	SECTION *MergedSection = Program->MainSection;
+	LOCATION Reference = {Program->EntryPoint.Symbol, Program->EntryPoint.SymbolName, Program->EntryPoint.Offset + PREOS_BASE_ADDR, FALSE};
+	
+	// FIXME: Remove as soon as specification is not preliminary any more.
+	Warning (NULL, "Encoding relocation tables according to a PRELIMINARY specification!");
+	
+	return (InsertPreOsLibraries   (Section,                      MergedSection, &Reference)
+	     && InsertPreOsROMCalls    (Section,                      MergedSection, &Reference)
+	     && InsertPreOsRAMCalls    (Section,                      MergedSection, &Reference)
+	     && InsertCompressedRelocs (Section, NULL,                MergedSection, &Reference)
+	     && InsertPreOSSectionRefs (Section, Program->BSSSection, MergedSection, &Reference));
+}
+
+#ifdef FARGO_SUPPORT
+
+// Append BSS relocation entries in the format required by Fargo 0.2.1. Append
+// all relocation entries pointing to the BSS section.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertFargo021SectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference)
+{
+	if (TargetSection)
+	{
+		// Initialize user data for list model.
+		RELOC_USER_DATA UserData = {TargetSection};
+		
+		if (!(AppendI2ToSection (Section, TargetSection->Size)))
+			return FALSE;
+		
+		// Apply the format documented in _fargo021_program_header.s.
+		return EmitCompressedFormatRelocs ((LIST_MODEL *) RelocListModel, MergedSection, Reference, &UserData, Section);
+	}	
+	else
+		return (AllocateSpaceInSection (Section, 2) != NULL);
+}
+
+// Append library calls in the format required by Fargo 0.2.1.
+BOOLEAN InsertFargo021Libraries (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Output an empty table if no libraries are referenced.
+	if (IsEmpty (Program->Libraries))
+		return (AllocateSpaceInSection (Section, 2) != NULL);
+	else
+	{
+		BOOLEAN Result = TRUE;
+		
+		// Initialize user data for list model.
+		LIB_CALL_USER_DATA UserData = {0, NULL, 0, 0, NULL, -1, -1};
+		
+		if (InitializeLibCallUserData (&UserData, Program))
+		{
+			// Go through all library calls and increment the appropriate counters.
+			COUNT LibRelocCount = GetItemCount ((LIST_MODEL *) LibCallListModel, Program, NULL, &UserData);
+			
+			// Output an empty table if no libraries are referenced.
+			if (!LibRelocCount)
+			{
+				if (!(AllocateSpaceInSection (Section, 2)))
+					Result = FALSE;
+			}
+			else
+			{
+#define AppendI1(i1) ({if (!AppendI1ToSection (Section, (i1))) { Result = FALSE; goto Fargo021LibsOutOfMem; } })
+#define AppendI2(i2) ({if (!AppendI2ToSection (Section, (i2))) { Result = FALSE; goto Fargo021LibsOutOfMem; } })
+				
+				// Pointer to the inserted data.
+				I1 *NewData;
+				SIZE SizeToAllocate;
+				I1 *LibName;
+				
+				// The number of requested libraries.
+				COUNT LibCount = UserData.UsedLibCount;
+				
+				// The placeholder for the library names.
+				OFFSET LibNamesLocation;
+				
+				AppendI2 (LibCount);
+				
+				LibNamesLocation = Section->Size;
+				AppendI2 (0);
+							
+				// Apply the format documented in _fargo021_program_header.s.
+				
+				{
+					// For each referenced library...
+					LIB_DATA *CurLib = UserData.Libs;
+					for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
+					{
+						if (CurLib->FunctionCount)
+						{
+							OFFSET LastFunction = -1;
+							
+							// For each referenced export...
+							for (UserData.CurFunction = 0; UserData.CurFunction <= CurLib->Lib->Highest; UserData.CurFunction++)
+							{
+								if (CurLib->Functions[UserData.CurFunction].RelocCount)
+								{
+									// Output the export number.
+									OFFSET FunctionOffset = UserData.CurFunction - LastFunction;
+									// Emit the compressed offset. This is NOT the same
+									// format as the compressed relocs!
+									if (FunctionOffset < 128)
+										AppendI1 (FunctionOffset);
+									else
+										AppendI2 (0x8000 + FunctionOffset);
+									LastFunction = UserData.CurFunction;
+									
+									// Emit all library calls using this export.
+									if (!(EmitCompressedFormatRelocs ((LIST_MODEL *) LibCallListModel, MergedSection, Reference, &UserData, Section)))
+									{
+										Result = FALSE;
+										goto Fargo021LibsOutOfMem;
+									}
+								}
+							}
+							
+							AppendI1 (0);
+						}
+					}
+					
+					// Add the redundant null-terminator.
+					AppendI1 (0);
+					
+					// Allocate space for the library names.
+					SizeToAllocate = 0;
+					// For each referenced library...
+					CurLib = UserData.Libs;
+					for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
+					{
+						if (CurLib->FunctionCount)
+							SizeToAllocate += strlen (CurLib->Lib->Name) + 1;
+					}
+					NewData = AllocateSpaceInSection (Section, SizeToAllocate);
+					LibName = NewData;
+					
+					// Now handle the library names.
+					if (NewData)
+					{
+						// Create a new reloc at the given location, pointing to
+						// the location of the library names.
+						{
+							I1 *LibNamePlaceholder = Section->Data + LibNamesLocation;
+							RELOC *Reloc = CreateProgramRelativeReloc (Section, &LibNamePlaceholder, Section, LibName - Section->Data, 2);
+							if (!Reloc)
+							{
+								Error (NULL, "Out of memory while inserting libraries with compressed relocs.");
+								Result = FALSE;
+								goto Fargo021LibsOutOfMem;
+							}
+						}
+						
+						// For each referenced library...
+						CurLib = UserData.Libs;
+						for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
+						{
+							if (CurLib->FunctionCount)
+							{
+								// Output its name.
+								strcpy (LibName, CurLib->Lib->Name);
+								LibName += strlen (CurLib->Lib->Name) + 1;
+							}
+						}
+					}
+					else
+					{
+						Error (NULL, "Out of memory while inserting libraries with compressed relocs.");
+						Result = FALSE;
+					}
+				}
+			}
+		}
+		else
+			Result = FALSE;
+		
+Fargo021LibsOutOfMem:
+		// Free the extra information.
+		FinalizeLibCallUserData (&UserData);
+		
+		return Result;
+	}
+#undef AppendI1
+#undef AppendI2
+}
+
+#endif /* FARGO_SUPPORT */

+ 80 - 0
insert/comprrlc.h

@@ -0,0 +1,80 @@
+/* comprrlc.h: Routines for compressed relocation tables
+
+   Copyright (C) 2003 Kevin Kofler
+   Portions copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INSERT_COMPRRLC_H
+#define INSERT_COMPRRLC_H
+
+#include "../generic.h"
+#include "ins_def.h"
+#include "../data.h"
+#include "model/list.h"
+
+// Append relocation entries in the format required by the TIGCCLIB relocation
+// code. If TargetSection is NULL, append all relocation entries that point to
+// unhandled sections. Otherwise, append all relocation entries pointing to this
+// section.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertCompressedRelocs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference);
+
+// Append relocation entries in the format required by the TIGCCLIB relocation
+// code, using InsertCompressedRelocs. If TargetSection is NULL, output an empty
+// relocation table. Otherwise, append all relocation entries pointing to this
+// section.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertCompressedSectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference);
+
+// Append ROM calls in the format required by the TIGCCLIB relocation code.
+BOOLEAN InsertCompressedROMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference);
+
+// Append the section size and relocs to the specified section in the
+// format required by PreOS.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertPreOSSectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference);
+
+// Append ROM calls in the format required by PreOs.
+BOOLEAN InsertPreOsROMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference);
+
+// Append RAM calls in the format required by PreOs.
+BOOLEAN InsertPreOsRAMCalls (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference);
+
+// Append libraries and library calls in the format required by PreOs.
+BOOLEAN InsertPreOsLibraries (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference);
+
+// Append import tables and relocation entries in the format required by PreOs.
+// In order: library imports, ROM calls, RAM calls, relocs, BSS references. All
+// in one stream, pointed to by a single pointer.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertPreOsCompressedTables (SECTION *Section);
+
+#ifdef FARGO_SUPPORT
+// Append the section size and section relocation entries in the format
+// required by Fargo 0.2.1.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertFargo021SectionRefs (SECTION *Section, SECTION *TargetSection, SECTION *MergedSection, const LOCATION *Reference);
+
+// Append library calls in the format required by Fargo 0.2.1.
+BOOLEAN InsertFargo021Libraries (SECTION *Section, SECTION *MergedSection, const LOCATION *Reference);
+#endif /* FARGO_SUPPORT */
+
+#endif

+ 98 - 0
insert/ins_def.c

@@ -0,0 +1,98 @@
+/* ins_def.c: Common definitions for insertions
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "ins_def.h"
+
+#include "../manip.h"
+
+#include <stdlib.h>
+
+// Create a new program-relative reloc at a specific data location,
+// pointing to a specific location in the target section. Increase
+// NewData to point behind the reloc.
+RELOC *CreateProgramRelativeReloc (SECTION *Section, I1 **NewData, SECTION *TargetSection, OFFSET TargetLocation, SIZE Size)
+{
+	RELOC *Reloc = calloc (1, sizeof (RELOC));
+	if (Reloc)
+	{
+		Reloc->Parent             = Section;
+		Reloc->Location           = *NewData - Section->Data;
+		*NewData += ((Reloc->Size = Size));
+		Reloc->Target.Symbol      = TargetSection->SectionSymbol;
+		Reloc->Target.SymbolName  = TargetSection->SectionSymbol->Name;
+		Reloc->Target.Offset      = TargetLocation;
+		SetRelocProgramRelative (Reloc);
+		InsertReloc (Section, Reloc);
+		TargetSection->Referenced = TRUE;
+	}
+	else
+		Error (NULL, "Out of memory.");
+	
+	return Reloc;
+}
+
+// Allocate and initialize a LIB_CALL_USER_DATA structure.
+BOOLEAN InitializeLibCallUserData (LIB_CALL_USER_DATA *UserData, PROGRAM *Program)
+{
+	// Allocate space for dynamic user data.
+	if (!(UserData->Libs = calloc ((UserData->LibCount = Program->Libraries.ReferencedCount), sizeof (LIB_DATA))))
+	{
+		Error (NULL, "Out of memory while inserting libraries.");
+		return FALSE;
+	}
+	
+	{
+		LIBRARY *CurLibrary;
+		LIB_DATA *CurLib = UserData->Libs;
+		
+		// Query info about the libraries.
+		for_each (CurLibrary, Program->Libraries)
+		{
+			if (CurLibrary->Referenced)
+			{
+				// Set the library pointer.
+				CurLib->Lib = CurLibrary;
+				// Allocate space for the imports.
+				if (!(CurLib->Functions = calloc (CurLibrary->Highest + 1, sizeof (LIB_FUNCTION_DATA))))
+				{
+					Error (NULL, "Out of memory while inserting libraries.");
+					return FALSE;
+				}
+				CurLib++;
+			}
+		}
+	}
+	
+	return TRUE;
+}
+
+// Finalize and deallocate a LIB_CALL_USER_DATA structure.
+void FinalizeLibCallUserData (LIB_CALL_USER_DATA *UserData)
+{
+	LIB_DATA *CurLibPtr = UserData->Libs;
+	OFFSET CurLib;
+	
+	for (CurLib = 0; CurLib < UserData->LibCount; CurLib++, CurLibPtr++)
+	{
+		LIB_FUNCTION_DATA *Functions = CurLibPtr->Functions;
+		if (Functions)
+			free (Functions);
+	}
+	
+	free (UserData->Libs);
+}

+ 37 - 0
insert/ins_def.h

@@ -0,0 +1,37 @@
+/* ins_def.h: Common definitions for insertions
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INSERT_DEF_H
+#define INSERT_DEF_H
+
+#include "../generic.h"
+#include "../data.h"
+#include "model/list.h"
+
+// Create a new program-relative reloc at a specific data location,
+// pointing to a specific location in the target section. Increase
+// NewData to point behind the reloc.
+RELOC *CreateProgramRelativeReloc (SECTION *Section, I1 **NewData, SECTION *TargetSection, OFFSET TargetLocation, SIZE Size);
+
+// Allocate and initialize a LIB_CALL_USER_DATA structure.
+BOOLEAN InitializeLibCallUserData (LIB_CALL_USER_DATA *UserData, PROGRAM *Program);
+
+// Finalize and deallocate a LIB_CALL_USER_DATA structure.
+void FinalizeLibCallUserData (LIB_CALL_USER_DATA *UserData);
+
+#endif

+ 28 - 0
insert/insert.h

@@ -0,0 +1,28 @@
+/* insert.h: Routines to handle automatic insertion of section contents
+
+   Copyright (C) 2003 Sebastian Reichelt, Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INSERT_H
+#define INSERT_H
+
+#include "../generic.h"
+
+#include "kernel.h"
+#include "comprrlc.h"
+#include "other.h"
+
+#endif

+ 571 - 0
insert/kernel.c

@@ -0,0 +1,571 @@
+/* kernel.c: Routines to handle automatic insertion of section contents in kernel format
+
+   Copyright (C) 2003 Sebastian Reichelt, Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "kernel.h"
+
+#include "../integers.h"
+#include "../manip.h"
+#include "../special.h"
+#include "model/list.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Get the size needed to emit a reloc table in kernel format for the
+// items enumerated in the list model specified by Model. SourceSection
+// may be NULL.
+SIZE GetKernelFormatRelocSize (LIST_MODEL *Model, PROGRAM *Program, SECTION *SourceSection, void *UserData)
+{
+	// We simply need 2 bytes per item, and 2 additional bytes at the end.
+	return (2 * (GetItemCount (Model, Program, SourceSection, UserData)) + 2);
+}
+
+// Emit a reloc table in kernel format for the items enumerated in the
+// list model specified by Model. SourceSection may be NULL.
+BOOLEAN EmitKernelFormatRelocs (LIST_MODEL *Model, PROGRAM *Program, SECTION *SourceSection, void *UserData, SECTION *Section, I1 **NewData)
+{
+	SECTION *CurSection;
+	void *NextItem = NULL;
+	OFFSET Offset;
+	
+	// For each section (or just for SourceSection)...
+	for (CurSection = (SourceSection ? : GetFirst (Program->Sections)); CurSection; CurSection = GetNext (CurSection))
+	{
+		// For each item in the list...
+		do {
+			// Output the target offset, and get the reloc's location.
+			Offset = Model (CurSection, &NextItem, UserData, FALSE, TRUE);
+			// A return value < 0 indicates that there were no more items.
+			if (Offset >= 0)
+			{
+				// Create a new reloc at this location, pointing to the
+				// location of the list item.
+				if (!(CreateProgramRelativeReloc (Section, NewData, CurSection, Offset, 2)))
+					return FALSE;
+			}
+		} while (NextItem);
+		
+		// If we only want the items of one section, we have finished.
+		if (SourceSection)
+			break;
+	}
+	
+	// Output the two zero bytes.
+	*NewData += 2;
+	return TRUE;
+}
+
+// Append relocation entries in the format required by kernels. If
+// TargetSection is NULL, insert all relocation entries that point to
+// unhandled sections. Otherwise, insert all relocation entries pointing
+// to this section.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertKernelRelocs (SECTION *Section, SECTION *TargetSection)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Initialize user data for list model.
+	RELOC_USER_DATA UserData = {TargetSection};
+	
+	// If a target section is specified, it is referenced now.
+	if (TargetSection)
+		TargetSection->Referenced = TRUE;
+	else
+		// Do code optimizations now, since this might reduce the number
+		// of relocs. If a target section was specified, this is pointless,
+		// as relocs into a separate section can never be optimized away.
+		FixCode (Program);
+	
+	{
+		// Allocate space for the relocs.
+		I1 *NewData = AllocateSpaceInSection (Section, GetKernelFormatRelocSize ((LIST_MODEL *) RelocListModel, Program, NULL, &UserData));
+		
+		// Apply the format documented in _kernel_program_header.s.
+		if (NewData)
+		{
+			// Insert the relocs.
+			if (!(EmitKernelFormatRelocs ((LIST_MODEL *) RelocListModel, Program, NULL, &UserData, Section, &NewData)))
+				return FALSE;
+		}
+		else
+			return FALSE;
+	}
+	
+	// If a target section was specified, and its Handled flag was not
+	// set yet, now setting it is probably correct.
+	if (TargetSection)
+		TargetSection->Handled = TRUE;
+	
+	return TRUE;
+}
+
+// Append relocation entries to TargetSection in the format required by
+// kernels. If TargetSection is NULL, AlwaysTerminate specifies whether
+// to insert the two terminating zero bytes anyway.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertKernelSectionRefs (SECTION *Section, SECTION *TargetSection, BOOLEAN AlwaysTerminate)
+{
+	// If the target section exists, this is the same format as for kernels.
+	// However, if none exists, we need to output the terminating zero
+	// bytes anyway. However, we cannot use InsertKernelRelocs in this
+	// case because the TargetSection parameter will be NULL.
+	if (TargetSection)
+		return InsertKernelRelocs (Section, TargetSection);
+	else if (AlwaysTerminate)
+		// Insert empty relocation table.
+		return (AllocateSpaceInSection (Section, 2) != NULL);
+	else
+		return TRUE;
+}
+
+// Append ROM calls in the format required by kernels.
+BOOLEAN InsertKernelROMCalls (SECTION *Section)
+{
+	BOOLEAN Result = TRUE;
+	PROGRAM *Program = Section->Parent;
+	
+	// Initialize user data for list model.
+	ROM_CALL_USER_DATA UserData = {NULL, 0, -1};
+	
+	// Allocate space for dynamic user data.
+	if (!(UserData.ROMFunctions = calloc (Program->HighestROMCall + 1, sizeof (ROM_CALL_FUNCTION_DATA))))
+	{
+		Error (NULL, "Out of memory while inserting ROM calls.");
+		return FALSE;
+	}
+	
+	{
+		// Go through all ROM calls and increment the appropriate counters.
+		COUNT ROMRelocCount = GetItemCount ((LIST_MODEL *) ROMCallListModel, Program, NULL, &UserData);
+		
+		// If no ROM calls are used, do not output anything.
+		if (ROMRelocCount > 0)
+		{
+			// Allocate space for the ROM call numbers and references.
+			I1 *NewData = AllocateSpaceInSection (Section, (4 * UserData.ROMFunctionCount) + (2 * ROMRelocCount) + 2);
+			
+			// Apply the format documented in _kernel_program_header.s.
+			if (NewData)
+			{
+				// Output the number of functions used.
+				WriteTI2 (*((TI2 *) NewData), UserData.ROMFunctionCount - 1);
+				NewData += 2;
+				
+				for (UserData.CurFunction = 0; UserData.CurFunction <= Program->HighestROMCall; UserData.CurFunction++)
+				{
+					if (UserData.ROMFunctions[UserData.CurFunction].RelocCount)
+					{
+						// Output the current function number.
+						WriteTI2 (*((TI2 *) NewData), UserData.CurFunction);
+						NewData += 2;
+						
+						// Emit all ROM calls using this function.
+						if (!(EmitKernelFormatRelocs ((LIST_MODEL *) ROMCallListModel, Program, NULL, &UserData, Section, &NewData)))
+						{
+							Result = FALSE;
+							break;
+						}
+					}
+				}
+			}
+			else
+				Result = FALSE;
+		}
+	}
+	
+	// Free the extra information.
+	free (UserData.ROMFunctions);
+	
+	return Result;
+}
+
+// Append RAM calls in the format required by kernels.
+BOOLEAN InsertKernelRAMCalls (SECTION *Section)
+{
+	BOOLEAN Result = TRUE;
+	PROGRAM *Program = Section->Parent;
+	
+	// Initialize user data for list model.
+	RAM_CALL_USER_DATA UserData = {{{NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}}, 0, RT_ALL_TYPES, -1};
+	
+	// Allocate space for dynamic user data.
+	RAM_CALL_TYPE_DATA *CurType = UserData.RAMTypes;
+	RAM_CALL_TYPE LoopType;
+	for (LoopType = 0; LoopType < RAM_CALL_TYPE_COUNT; LoopType++, CurType++)
+	{
+		if (!(CurType->Functions = calloc (Program->HighestRAMCall + 1, sizeof (RAM_CALL_FUNCTION_DATA))))
+		{
+			Error (NULL, "Out of memory while inserting RAM calls.");
+			Result = FALSE;
+			break;
+		}
+	}
+	
+	if (Result)
+	{
+		// Go through all RAM calls and increment the appropriate counters.
+		COUNT RAMRelocCount = GetItemCount ((LIST_MODEL *) RAMCallListModel, Program, NULL, &UserData);
+		
+		// If no RAM calls are used, do not output anything.
+		if (RAMRelocCount)
+		{
+			// Allocate space for the RAM call numbers and references.
+			I1 *NewData = AllocateSpaceInSection (Section, (4 * UserData.RAMFunctionCount) + (2 * RAMRelocCount) + 2);
+			
+			// Apply the format documented in _kernel_program_header.s.
+			if (NewData)
+			{
+				// Output the number of functions used.
+				WriteTI2 (*((TI2 *) NewData), UserData.RAMFunctionCount - 1);
+				NewData += 2;
+				
+				// For each possible RAM call type...
+				CurType = UserData.RAMTypes;
+				for (UserData.CurType = 0; UserData.CurType < RAM_CALL_TYPE_COUNT; UserData.CurType++, CurType++)
+				{
+					// Check if the type has been used.
+					if (CurType->FunctionCount)
+					{
+						// Gather information about the type.
+						SIZE TypeSize = ((UserData.CurType == RT_RAM_CALL_2) || (UserData.CurType == RT_EXTRA_RAM_2) ? 2 : 4);
+						BOOLEAN TypeExtraRAM = (UserData.CurType == RT_EXTRA_RAM_4) || (UserData.CurType == RT_EXTRA_RAM_2);
+						I2 TypeFlags = 0;
+						
+						if (TypeSize < 4)
+							TypeFlags |= (1 << 15);
+						if (TypeExtraRAM)
+							TypeFlags |= (1 << 14);
+						
+						// For each possible RAM function number...
+						for (UserData.CurFunction = 0; UserData.CurFunction <= Program->HighestRAMCall; UserData.CurFunction++)
+						{
+							// Check if a RAM function of this type and number has been used.
+							if (CurType->Functions[UserData.CurFunction].RelocCount)
+							{
+								// Output the type and number.
+								if (NewData - Section->Data + 2 <= Section->Size)
+									WriteTI2 (*((TI2 *) NewData), UserData.CurFunction | TypeFlags);
+								NewData += 2;
+								// Emit all RAM calls using this function.
+								if (!(EmitKernelFormatRelocs ((LIST_MODEL *) RAMCallListModel, Program, NULL, &UserData, Section, &NewData)))
+								{
+									Result = FALSE;
+									break;
+								}
+							}
+						}
+					}
+					
+					if (!Result)
+						break;
+				}
+			}
+			else
+				Result = FALSE;
+		}
+	}
+	
+	// Free the extra information.
+	for (UserData.CurType = 0; UserData.CurType < RAM_CALL_TYPE_COUNT; UserData.CurType++)
+	{
+		RAM_CALL_FUNCTION_DATA *Functions = UserData.RAMTypes[UserData.CurType].Functions;
+		if (Functions)
+			free (Functions);
+	}
+	
+	return Result;
+}
+
+// Append library calls in the format required by kernels.
+BOOLEAN InsertKernelLibraries (SECTION *Section)
+{
+	BOOLEAN Result = TRUE;
+	PROGRAM *Program = Section->Parent;
+	
+	// Do not output anything if no libraries are referenced.
+	if (!(IsEmpty (Program->Libraries)))
+	{
+		// Initialize user data for list model.
+		LIB_CALL_USER_DATA UserData = {0, NULL, 0, 0, NULL, -1, -1};
+		
+		if (InitializeLibCallUserData (&UserData, Program))
+		{
+			// Go through all library calls and increment the appropriate counters.
+			COUNT LibRelocCount = GetItemCount ((LIST_MODEL *) LibCallListModel, Program, NULL, &UserData);
+			
+			// Do not output anything if no libraries are referenced.
+			if (LibRelocCount)
+			{
+				// Allocate space for the libraries and relocs.
+				I1 *NewData = AllocateSpaceInSection (Section, (12 * UserData.UsedLibCount) + (4 * UserData.LibFunctionCount) + (2 * LibRelocCount));
+				
+				// Apply the format documented in _kernel_program_header.s.
+				if (NewData)
+				{
+					// For each referenced library...
+					LIB_DATA *CurLib = UserData.Libs;
+					for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
+					{
+						if (CurLib->FunctionCount)
+						{
+							// Output its name.
+							if (strlen (CurLib->Lib->Name) > 8)
+								Warning (Section->FileName, "Library name `%s' too long; cutting off at 8th character.", CurLib->Lib->Name);
+							strncpy (NewData, CurLib->Lib->Name, 8);
+							WriteTI1 (*((TI1 *) (NewData + 9)), CurLib->Lib->Version);
+							NewData += 10;
+						}
+					}
+					
+					// For each referenced library...
+					CurLib = UserData.Libs;
+					for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
+					{
+						if (CurLib->FunctionCount)
+						{
+							// Output the number of exports used.
+							WriteTI2 (*((TI2 *) NewData), CurLib->FunctionCount - 1);
+							NewData += 2;
+							
+							// For each referenced export...
+							for (UserData.CurFunction = 0; UserData.CurFunction <= CurLib->Lib->Highest; UserData.CurFunction++)
+							{
+								if (CurLib->Functions[UserData.CurFunction].RelocCount)
+								{
+									// Output the export number.
+									if (NewData - Section->Data + 2 <= Section->Size)
+										WriteTI2 (*((TI2 *) NewData), UserData.CurFunction);
+									NewData += 2;
+									
+									// Emit all library calls using this export.
+									if (!(EmitKernelFormatRelocs ((LIST_MODEL *) LibCallListModel, Program, NULL, &UserData, Section, &NewData)))
+									{
+										Result = FALSE;
+										break;
+									}
+								}
+							}
+						}
+					}
+				}
+				else
+					Result = FALSE;
+			}
+		}
+		else
+			Result = FALSE;
+		
+		// Free the extra information.
+		FinalizeLibCallUserData (&UserData);
+	}
+	
+	return Result;
+}
+
+#ifdef FARGO_SUPPORT
+// Append library calls in the format required by Fargo v0.2.0.
+BOOLEAN InsertFargo020Libraries (SECTION *Section)
+{
+	BOOLEAN Result = TRUE;
+	PROGRAM *Program = Section->Parent;
+	
+	// Do not output anything if no libraries are referenced.
+	if (!(IsEmpty (Program->Libraries)))
+	{
+		// Initialize user data for list model.
+		LIB_CALL_USER_DATA UserData = {0, NULL, 0, 0, NULL, -1, -1};
+		
+		if (InitializeLibCallUserData (&UserData, Program))
+		{
+			// Go through all library calls and increment the appropriate counters.
+			COUNT LibRelocCount = GetItemCount ((LIST_MODEL *) LibCallListModel, Program, NULL, &UserData);
+			
+			// Do not output anything if no libraries are referenced.
+			if (LibRelocCount)
+			{
+				// Calculate the space needed for the library references.
+				SIZE RelocSize = (4 * UserData.UsedLibCount) + (4 * UserData.LibFunctionCount) + (2 * LibRelocCount) + 2;
+				
+				// Calculate the space needed for the library names.
+				SIZE SizeToAllocate = RelocSize;
+				LIB_DATA *CurLib = UserData.Libs;
+				for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
+				{
+					if (CurLib->FunctionCount)
+						SizeToAllocate += strlen (CurLib->Lib->Name) + 1;
+				}
+				// Pad to an even size.
+				if (SizeToAllocate & 1)
+					SizeToAllocate++;
+				
+				// Allocate space for the libraries and relocs.
+				{
+					I1 *NewData = AllocateSpaceInSection (Section, SizeToAllocate);
+					
+					// Apply the format documented in _fargo_program_header.s.
+					if (NewData)
+					{
+						I1 *LibName = NewData + RelocSize;
+						
+						// For each referenced library...
+						CurLib = UserData.Libs;
+						for (UserData.CurLib = 0; UserData.CurLib < UserData.LibCount; UserData.CurLib++, CurLib++)
+						{
+							if (CurLib->FunctionCount)
+							{
+								// Output its name.
+								strcpy (LibName, CurLib->Lib->Name);
+								// Create a new reloc at the current location, pointing to
+								// the location of the library name.
+								{
+									RELOC *Reloc = CreateProgramRelativeReloc (Section, &NewData, Section, LibName - Section->Data, 2);
+									if (!Reloc)
+									{
+										Result = FALSE;
+										break;
+									}
+								}
+								LibName += strlen (CurLib->Lib->Name) + 1;
+								
+								// Output the references to it.
+								{
+									// For each referenced export...
+									LIB_FUNCTION_DATA *CurFunction = CurLib->Functions;
+									for (UserData.CurFunction = 0; UserData.CurFunction <= CurLib->Lib->Highest; UserData.CurFunction++, CurFunction++)
+									{
+										if (CurFunction->RelocCount)
+										{
+											// Output its number.
+											if (NewData - Section->Data + 2 <= Section->Size)
+												WriteTI2 (*((TI2 *) NewData), UserData.CurFunction + 1);
+											NewData += 2;
+											
+											// Emit all library calls using this export.
+											if (!(EmitKernelFormatRelocs ((LIST_MODEL *) LibCallListModel, Program, NULL, &UserData, Section, &NewData)))
+											{
+												Result = FALSE;
+												break;
+											}
+										}
+									}
+									
+									if (!Result)
+										break;
+								}
+								
+								NewData += 2;
+							}
+						}
+					}
+					else
+						Result = FALSE;
+				}
+			}
+		}
+		else
+			Result = FALSE;
+		
+		// Free the extra information.
+		FinalizeLibCallUserData (&UserData);
+	}
+	
+	return Result;
+}
+#endif /* FARGO_SUPPORT */
+
+// Append exported symbols in the format required by kernels. If
+// TrailingZeroBytes is FALSE, use the Fargo v0.2.x format without
+// trailing zero bytes.
+BOOLEAN InsertKernelExports (SECTION *Section, BOOLEAN TrailingZeroBytes)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Export Counter.
+	COUNT ExportCount = 0;
+	
+	// Loop Variables.
+	SECTION *CurSection;
+	SYMBOL *CurSymbol;
+	
+	// Get the number of exports.
+	// The number of exports is equal to the highest export number + 1.
+	for_each (CurSection, Program->Sections)
+	{
+		for_each (CurSymbol, CurSection->Symbols)
+		{
+			if (CurSymbol->Exported)
+			{
+				OFFSET ExportNumber = GetExportNumber (CurSymbol->Name);
+				if ((ExportNumber >= 0) && (ExportCount < ExportNumber + 1))
+					ExportCount = ExportNumber + 1;
+			}
+		}
+	}
+	
+	if (ExportCount || TrailingZeroBytes)
+	{
+		// Allocate space for the exports.
+		I1 *NewData = AllocateSpaceInSection (Section, (2 * ExportCount) + (TrailingZeroBytes ? 2 : 0));
+		
+		// Apply the format documented in _kernel_program_header.s.
+		if (NewData)
+		{
+			// For each section...
+			for_each (CurSection, Program->Sections)
+			{
+				// For each symbol...
+				for_each (CurSymbol, CurSection->Symbols)
+				{
+					// Check if it is exported.
+					if (CurSymbol->Exported)
+					{
+						OFFSET ExportNumber = GetExportNumber (CurSymbol->Name);
+						if (ExportNumber >= 0)
+						{
+							// Create a new reloc at the appropriate location,
+							// pointing to the exported symbol.
+							RELOC *Reloc = calloc (1, sizeof (RELOC));
+							if (Reloc)
+							{
+								Reloc->Parent = Section;
+								Reloc->Location = NewData - Section->Data + 2 * ExportNumber;
+								Reloc->Size = 2;
+								Reloc->Target.Symbol = CurSymbol;
+								Reloc->Target.SymbolName = CurSymbol->Name;
+								SetRelocProgramRelative (Reloc);
+								InsertReloc (Section, Reloc);
+								CurSection->Referenced = TRUE;
+							}
+							else
+							{
+								Error (NULL, "Out of memory while inserting exports.");
+								return FALSE;
+							}
+						}
+					}
+				}
+			}
+		}
+		else
+			return FALSE;
+	}
+	
+	return TRUE;
+}

+ 61 - 0
insert/kernel.h

@@ -0,0 +1,61 @@
+/* kernel.h: Routines to handle automatic insertion of section contents in kernel format
+
+   Copyright (C) 2003 Sebastian Reichelt, Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INSERT_KERNEL_H
+#define INSERT_KERNEL_H
+
+#include "../generic.h"
+#include "ins_def.h"
+#include "../data.h"
+#include "model/list.h"
+
+// Append relocation entries in the format required by kernels. If
+// TargetSection is NULL, insert all relocation entries that point to
+// unhandled sections. Otherwise, insert all relocation entries pointing
+// to this section.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertKernelRelocs (SECTION *Section, SECTION *TargetSection);
+
+// Append relocation entries to TargetSection in the format required by
+// kernels. If TargetSection is NULL, AlwaysTerminate specifies whether
+// to insert the two terminating zero bytes anyway.
+// Warning: Inserting relocs is special: Since the relocs are changed
+// during the process, they can be inserted only once.
+BOOLEAN InsertKernelSectionRefs (SECTION *Section, SECTION *TargetSection, BOOLEAN AlwaysTerminate);
+
+// Append ROM calls in the format required by kernels.
+BOOLEAN InsertKernelROMCalls (SECTION *Section);
+
+// Append RAM calls in the format required by kernels.
+BOOLEAN InsertKernelRAMCalls (SECTION *Section);
+
+// Append library calls in the format required by kernels.
+BOOLEAN InsertKernelLibraries (SECTION *Section);
+
+#ifdef FARGO_SUPPORT
+// Append library calls in the format required by Fargo v0.2.0.
+BOOLEAN InsertFargo020Libraries (SECTION *Section);
+#endif /* FARGO_SUPPORT */
+
+// Append exported symbols in the format required by kernels. If
+// TrailingZeroBytes is FALSE, use the Fargo v0.2.x format without
+// trailing zero bytes.
+BOOLEAN InsertKernelExports (SECTION *Section, BOOLEAN TrailingZeroBytes);
+
+#endif

+ 408 - 0
insert/model/list.c

@@ -0,0 +1,408 @@
+/* list.c: List model definitions
+
+   Copyright (C) 2003 Sebastian Reichelt, Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "list.h"
+
+#include "../../integers.h"
+#include "../../manip.h"
+#include "../../special.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Get the number of items from the LIST_MODEL passed to the
+// function via the Model parameter, for a particular source section.
+COUNT GetSectionItemCount (LIST_MODEL *Model, SECTION *SourceSection, void *UserData)
+{
+	void *NextItem = NULL;
+	return Model (SourceSection, &NextItem, UserData, TRUE, FALSE);
+}
+
+// Get the number of items from the LIST_MODEL passed to the
+// function via the Model parameter. If SourceSection is NULL, loop
+// through all sections in the program.
+COUNT GetItemCount (LIST_MODEL *Model, PROGRAM *Program, SECTION *SourceSection, void *UserData)
+{
+	COUNT GetCurrentItemCount (void)
+	{
+		return GetSectionItemCount (Model, SourceSection, UserData);
+	}
+	
+	if (SourceSection)
+		return GetCurrentItemCount ();
+	else
+	{
+		COUNT Result = 0;
+		for_each (SourceSection, Program->Sections)
+			Result += GetCurrentItemCount ();
+		return Result;
+	}
+}
+
+// A LIST_MODEL for relocs. This list model will return all relocs
+// in SourceSection (that point to TargetSection if it is not NULL).
+OFFSET RelocListModel (SECTION *SourceSection, RELOC **NextItem, RELOC_USER_DATA *UserData, BOOLEAN Count, BOOLEAN Emit)
+{
+	COUNT Counter = 0;
+	RELOC *CurReloc;
+	
+	// If no next item is specified yet, this must be the first call to this model
+	// with this SourceSection. The caller cannot fill *NextItem because it does
+	// not know how to get the first item.
+	if (!(*NextItem))
+		*NextItem = GetFirst (SourceSection->Relocs);
+	
+	// Loop until we are done with counting or can return the next reloc that is
+	// important for us.
+	while ((CurReloc = *NextItem))
+	{
+		*NextItem = GetNext (CurReloc);
+		
+		// Returning a location < 0 would be fatal, since it means we are done.
+		if ((CurReloc->Location >= 0) && (CurReloc->Location + CurReloc->Size <= SourceSection->Size))
+		{
+			// Ignore relocs which we will not emit.
+			if (!(CurReloc->Relative || CurReloc->Target.Builtin))
+			{
+				// We can only emit 4-byte absolute relocs.
+				// Everything else would need to be specified in the user data structure.
+				if (CurReloc->Size == 4)
+				{
+					// If a target section is specified, use only relocs that explicitly point to it.
+					// Otherwise, use all relocs that do not explicitly point to a handled section.
+					if (UserData->TargetSection ? (CurReloc->Target.Symbol && (CurReloc->Target.Symbol->Parent == UserData->TargetSection)) : (!(CurReloc->Target.Symbol && (CurReloc->Target.Symbol->Parent->Handled))))
+					{
+						// When counting, simply increment the counter.
+						if (Count)
+							Counter++;
+						// Otherwise, return the location.
+						else
+						{
+							// If Emit is true, we must emit the fixed/target offset of the reloc
+							// into the section contents.
+							if (Emit)
+							{
+								// Change CurReloc to be relative to the section or program.
+								SetRelocRelation (CurReloc, UserData->TargetSection, 0);
+								// We won't see this reloc any more,
+								// so increase the statistics.
+								SourceSection->Relocs.EmittedCount++;
+								SourceSection->Parent->OptimizeInfo->RelocCount++;
+							}
+							// Return the location.
+							return CurReloc->Location;
+						}
+					}
+				}
+			}
+		}
+		// Output an error message only when counting, so we get it only once.
+		else if (Count)
+			Error (SourceSection->FileName, "Ignoring reloc outside of section.");
+	}
+	
+	// We have reached the end of the section.
+	// Return the number of items when counting.
+	if (Count)
+		return Counter;
+	else
+		return -1;
+}
+
+// A LIST_MODEL for ROM calls. This list model will return all ROM calls
+// in SourceSection (that point to CurFunction if it is >= 0).
+OFFSET ROMCallListModel (SECTION *SourceSection, ROM_CALL **NextItem, ROM_CALL_USER_DATA *UserData, BOOLEAN Count, BOOLEAN Emit)
+{
+	COUNT Counter = 0;
+	ROM_CALL_FUNCTION_DATA *ROMFunctions = UserData->ROMFunctions;
+	OFFSET CurFunction = UserData->CurFunction;
+	ROM_CALL *CurROMCall;
+	
+	if (!Count)
+	{
+		// After this enumeration, ROM calls are supported and do not need any further treatment.
+		SourceSection->ROMCalls.Handled = TRUE;
+	}
+	
+	// If no next item is specified yet, this must be the first call to this model
+	// with this SourceSection and CurFunction. The caller cannot fill *NextItem
+	// because it does not know how to get the first item.
+	if (!(*NextItem))
+		*NextItem = GetFirst (SourceSection->ROMCalls);
+	
+	// Loop until we are done with counting or can return the next ROM call that is
+	// important for us.
+	while ((CurROMCall = *NextItem))
+	{
+		*NextItem = GetNext (CurROMCall);
+		
+		// Returning a location < 0 would be fatal, since it means we are done.
+		if ((CurROMCall->Location >= 0) && (CurROMCall->Location + CurROMCall->Size <= SourceSection->Size))
+		{
+			// Only proceed if we want this ROM call number.
+			if ((CurFunction < 0) || (CurROMCall->Number == CurFunction))
+			{
+				// We can only emit 4-byte ROM calls.
+				// Everything else would need to be specified in the user data structure.
+				if (CurROMCall->Size == 4)
+				{
+					// When counting, increment the main counter and all counters
+					// from the user data structure.
+					if (Count)
+					{
+						Counter++;
+						if (!(ROMFunctions[CurROMCall->Number].RelocCount++))
+							UserData->ROMFunctionCount++;
+					}
+					// Otherwise, return the location.
+					else
+					{
+						// If Emit is true, we must emit the fixed offset of the ROM call
+						// into the section contents.
+						if (Emit)
+						{
+							// Write our target offset into the section data.
+							if (SourceSection->Data)
+								WriteTI4 (*((TI4 *) (SourceSection->Data + CurROMCall->Location)), CurROMCall->FixedOffset);
+						}
+						// If we want to get all ROM calls, we need information about
+						// this ROM call.
+						// Otherwise, this does not have any effect.
+						UserData->CurFunction = CurROMCall->Number;
+						// Return the location.
+						return CurROMCall->Location;
+					}
+				}
+				// Output an error message only when counting, so we get it only once.
+				else if (Count)
+					Error (GetFileName (SourceSection, CurROMCall->Location), "Cannot emit %ld byte ROM call 0x%lX.", (long) CurROMCall->Size, (long) CurROMCall->Number);
+			}
+		}
+		// Output an error message only when counting, so we get it only once.
+		else if (Count)
+			Error (SourceSection->FileName, "Ignoring ROM call outside of section.");
+	}
+	
+	// We have reached the end of the section.
+	// Return the number of items when counting.
+	if (Count)
+		return Counter;
+	else
+		return -1;
+}
+
+// A LIST_MODEL for RAM calls. This list model will return all ROM calls
+// in SourceSection (of type CurType if it is not RT_ALL_TYPES, that point
+// to CurFunction if it is >= 0).
+OFFSET RAMCallListModel (SECTION *SourceSection, RAM_CALL **NextItem, RAM_CALL_USER_DATA *UserData, BOOLEAN Count, BOOLEAN Emit)
+{
+	COUNT Counter = 0;
+	RAM_CALL_TYPE_DATA *RAMTypes = UserData->RAMTypes;
+	RAM_CALL_TYPE CurType = UserData->CurType;
+	OFFSET CurFunction = UserData->CurFunction;
+	RAM_CALL *CurRAMCall;
+	
+	if (!Count)
+	{
+		// After this enumeration, RAM calls are supported and do not need any further treatment.
+		SourceSection->RAMCalls.Handled = TRUE;
+	}
+	
+	// If no next item is specified yet, this must be the first call to this model
+	// with this SourceSection and CurFunction. The caller cannot fill *NextItem
+	// because it does not know how to get the first item.
+	if (!(*NextItem))
+		*NextItem = GetFirst (SourceSection->RAMCalls);
+	
+	// Loop until we are done with counting or can return the next RAM call that is
+	// important for us.
+	while ((CurRAMCall = *NextItem))
+	{
+		*NextItem = GetNext (CurRAMCall);
+		
+		// Returning a location < 0 would be fatal, since it means we are done.
+		if ((CurRAMCall->Location >= 0) && (CurRAMCall->Location + CurRAMCall->Size <= SourceSection->Size))
+		{
+			// Only proceed if we want this RAM call number.
+			if ((CurFunction < 0) || (CurRAMCall->Number == CurFunction))
+			{
+				// Check if the type is valid.
+				RAM_CALL_TYPE ThisType = GetRAMCallType (CurRAMCall);
+				if (ThisType >= 0)
+				{
+					// Only proceed if we want this RAM call type.
+					if ((CurType < 0) || (ThisType == CurType))
+					{
+						// When counting, increment the main counter and all counters
+						// from the user data structure.
+						if (Count)
+						{
+							Counter++;
+							if (!(RAMTypes[ThisType].Functions[CurRAMCall->Number].RelocCount++))
+							{
+								RAMTypes[ThisType].FunctionCount++;
+								UserData->RAMFunctionCount++;
+							}
+						}
+						// Otherwise, return the location.
+						else
+						{
+							// If Emit is true, we must emit the fixed offset of the RAM call
+							// into the section contents.
+							if (Emit)
+							{
+								// Write our target offset into the section data.
+								if (SourceSection->Data)
+									WriteTI (SourceSection->Data + CurRAMCall->Location, CurRAMCall->Size, CurRAMCall->FixedOffset, TRUE, FALSE);
+							}
+							// If we want to get all RAM calls, we need information about
+							// this RAM call.
+							// Otherwise, this does not have any effect.
+							UserData->CurType = ThisType;
+							UserData->CurFunction = CurRAMCall->Number;
+							// Return the location.
+							return CurRAMCall->Location;
+						}
+					}
+				}
+				// Output an error message only when counting, so we get it only once.
+				else if (Count)
+					Error (GetFileName (SourceSection, 0), "Cannot emit %ld byte RAM call 0x%lX.", (long) CurRAMCall->Size, (long) CurRAMCall->Number);
+			}
+		}
+		// Output an error message only when counting, so we get it only once.
+		else if (Count)
+			Error (SourceSection->FileName, "Ignoring RAM call outside of section.");
+	}
+	
+	// We have reached the end of the section.
+	// Return the number of items when counting.
+	if (Count)
+		return Counter;
+	else
+		return -1;
+}
+
+// A LIST_MODEL for library calls. This list model will return all library
+// calls in SourceSection (that point to CurFunction in CurLib if they are
+// >= 0).
+OFFSET LibCallListModel (SECTION *SourceSection, LIB_CALL **NextItem, LIB_CALL_USER_DATA *UserData, BOOLEAN Count, BOOLEAN Emit)
+{
+	COUNT Counter = 0;
+	COUNT LibCount = UserData->LibCount;
+	LIB_DATA *Libs = UserData->Libs;
+	OFFSET CurLib = UserData->CurLib;
+	OFFSET CurFunction = UserData->CurFunction;
+	LIB_CALL *CurLibCall;
+	
+	if (!Count)
+	{
+		// After this enumeration, library calls are supported and do not need any further treatment.
+		SourceSection->LibCalls.Handled = TRUE;
+	}
+	
+	// If no next item is specified yet, this must be the first call to this model
+	// with this SourceSection and CurFunction. The caller cannot fill *NextItem
+	// because it does not know how to get the first item.
+	if (!(*NextItem))
+		*NextItem = GetFirst (SourceSection->LibCalls);
+	
+	// Loop until we are done with counting or can return the next library call that is
+	// important for us.
+	while ((CurLibCall = *NextItem))
+	{
+		*NextItem = GetNext (CurLibCall);
+		
+		// Returning a location < 0 would be fatal, since it means we are done.
+		if ((CurLibCall->Location >= 0) && (CurLibCall->Location + CurLibCall->Size <= SourceSection->Size))
+		{
+			// Only proceed if we want this library and export.
+			if ((CurLib < 0) || ((CurLibCall->Library == Libs[CurLib].Lib) && ((CurFunction < 0) || (CurLibCall->Number == CurFunction))))
+			{
+				// We can only emit 4-byte library calls.
+				// Everything else would need to be specified in the user data structure.
+				if (CurLibCall->Size == 4)
+				{
+					// When counting, increment the main counter and all counters
+					// from the user data structure.
+					if (Count)
+					{
+						Counter++;
+						
+						{
+							OFFSET ThisLib;
+							
+							// Look for the correct library information.
+							// If CurLib >= 0, we already know the correct one.
+							for (ThisLib = (CurLib >= 0 ? CurLib : 0); ThisLib < LibCount; ThisLib++)
+							{
+								if (Libs[ThisLib].Lib == CurLibCall->Library)
+								{
+									if (!(Libs[ThisLib].Functions[CurLibCall->Number].RelocCount++))
+									{
+										UserData->LibFunctionCount++;
+										if (!(Libs[ThisLib].FunctionCount++))
+											UserData->UsedLibCount++;
+									}
+									
+									break;
+								}
+								
+								if (CurLib >= 0)
+									break;
+							}
+						}
+					}
+					// Otherwise, return the location.
+					else
+					{
+						// If Emit is true, we must emit the fixed offset of the library call
+						// into the section contents.
+						if (Emit)
+						{
+							// Write our target offset into the section data.
+							if (SourceSection->Data)
+								WriteTI4 (*((TI4 *) (SourceSection->Data + CurLibCall->Location)), CurLibCall->FixedOffset);
+						}
+						
+						// If we want to get all library calls, we need information about
+						// this library call.
+						// Otherwise, this does not have any effect.
+						UserData->CurLibrary = CurLibCall->Library;
+						UserData->CurFunction = CurLibCall->Number;
+						
+						return CurLibCall->Location;
+					}
+				}
+				// Output an error message only when counting, so we get it only once.
+				else if (Count)
+					Error (GetFileName (SourceSection, CurLibCall->Location), "Cannot emit %ld byte library call to `%s', function 0x%lX.", (long) CurLibCall->Size, CurLibCall->Library->Name, (long) CurLibCall->Number);
+			}
+		}
+		// Output an error message only when counting, so we get it only once.
+		else if (Count)
+			Error (SourceSection->FileName, "Ignoring library call outside of section.");
+	}
+	
+	// We have reached the end of the section.
+	// Return the number of items when counting.
+	if (Count)
+		return Counter;
+	else
+		return -1;
+}

+ 135 - 0
insert/model/list.h

@@ -0,0 +1,135 @@
+/* list.h: List model definitions
+
+   Copyright (C) 2003 Sebastian Reichelt, Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INSERT_MODEL_RELOC_H
+#define INSERT_MODEL_RELOC_H
+
+#include "../../generic.h"
+#include "../../data.h"
+
+// *** Generic ***
+
+// A type for a function implementing a reloc model. SourceSection is
+// the section whose relocs are to be determined. If Count is true,
+// the function must return the number of items; otherwise it must
+// return the location of the next item in SourceSection, or -1 if
+// there is no next item. NextItem may never be NULL; but the data it
+// points to is NULL in the first call. If it is NULL after a call,
+// this means that no more items follow.
+// If some user data is optional, then the list model will usually
+// fill it with information about the current item.
+typedef OFFSET (LIST_MODEL) (SECTION *SourceSection, void **NextItem, void *UserData, BOOLEAN Count, BOOLEAN Emit);
+
+// Get the number of items from the LIST_MODEL passed to the
+// function via the Model parameter, for a particular source section.
+COUNT GetSectionItemCount (LIST_MODEL *Model, SECTION *SourceSection, void *UserData);
+
+// Get the number of items from the LIST_MODEL passed to the
+// function via the Model parameter. If SourceSection is NULL, loop
+// through all sections in the program.
+COUNT GetItemCount (LIST_MODEL *Model, PROGRAM *Program, SECTION *SourceSection, void *UserData);
+
+// *** Relocs ***
+
+// User data for RelocListModel.
+typedef struct {
+	SECTION *TargetSection;  // If non-NULL, identifies the section the relocs must point to.
+} RELOC_USER_DATA;
+
+// A LIST_MODEL for relocs. This list model will return all relocs
+// in SourceSection (that point to TargetSection if it is not NULL).
+OFFSET RelocListModel (SECTION *SourceSection, RELOC **NextItem, RELOC_USER_DATA *UserData, BOOLEAN Count, BOOLEAN Emit);
+
+// *** ROM Calls ***
+
+// A structured type for a used ROM call.
+typedef struct {
+	COUNT RelocCount;  // The number of references to this ROM call.
+} ROM_CALL_FUNCTION_DATA;
+
+// User data for ROMCallListModel.
+typedef struct {
+	ROM_CALL_FUNCTION_DATA *ROMFunctions;  // Information about the ROM calls used in the program, as an array with the ROM call number as the index.
+	COUNT ROMFunctionCount;                // Number of used ROM calls.
+	OFFSET CurFunction;                    // Current ROM call number for which to count/return references (if >= 0).
+} ROM_CALL_USER_DATA;
+
+// A LIST_MODEL for ROM calls. This list model will return all ROM calls
+// in SourceSection (that point to CurFunction if it is >= 0).
+OFFSET ROMCallListModel (SECTION *SourceSection, ROM_CALL **NextItem, ROM_CALL_USER_DATA *UserData, BOOLEAN Count, BOOLEAN Emit);
+
+// *** RAM Calls ***
+
+// Available types of RAM calls.
+typedef enum {RT_RAM_CALL_4 = 0, RT_EXTRA_RAM_4 = 1, RT_RAM_CALL_2 = 2, RT_EXTRA_RAM_2 = 3, RT_ALL_TYPES = -1, RT_UNSUPPORTED = -2} RAM_CALL_TYPE;
+#define RAM_CALL_TYPE_COUNT 4
+#define GetRAMCallType(RAMCall) ((RAMCall)->Size == 4 ? ((RAMCall)->ExtraRAMAddr ? RT_EXTRA_RAM_4 : RT_RAM_CALL_4) : (RAMCall)->Size == 2 ? ((RAMCall)->ExtraRAMAddr ? RT_EXTRA_RAM_2 : RT_RAM_CALL_2) : RT_UNSUPPORTED)
+
+// A structured type for a used RAM call (with a specific type).
+typedef struct {
+	COUNT RelocCount;  // The number of references to this RAM call.
+} RAM_CALL_FUNCTION_DATA;
+
+// A structured type for a RAM call type, containing all RAM calls available for it.
+typedef struct {
+	RAM_CALL_FUNCTION_DATA *Functions;  // Information about the RAM calls used with this type, as an array with the RAM call number as the index.
+	COUNT FunctionCount;                // The number of RAM calls used in this type.
+} RAM_CALL_TYPE_DATA;
+
+// User data for RAMCallListModel.
+typedef struct {
+	RAM_CALL_TYPE_DATA RAMTypes[RAM_CALL_TYPE_COUNT];  // Information about the RAM calls used in the program, sorted by type.
+	COUNT RAMFunctionCount;                            // The total number of RAM calls used in the program.
+	RAM_CALL_TYPE CurType;                             // Current RAM call type to count/return, or RT_ALL_TYPES.
+	OFFSET CurFunction;                                // Current RAM call number for which to count/return references (if >= 0).
+} RAM_CALL_USER_DATA;
+
+// A LIST_MODEL for RAM calls. This list model will return all ROM calls
+// in SourceSection (of type CurType if it is not RT_ALL_TYPES, that point
+// to CurFunction if it is >= 0).
+OFFSET RAMCallListModel (SECTION *SourceSection, RAM_CALL **NextItem, RAM_CALL_USER_DATA *UserData, BOOLEAN Count, BOOLEAN Emit);
+
+// A structured type for an imported function in a library.
+typedef struct {
+	COUNT RelocCount;  // The number of references to this library export.
+} LIB_FUNCTION_DATA;
+
+// Extra information about each library.
+typedef struct {
+	LIBRARY *Lib;                  // The library this data belongs to.
+	LIB_FUNCTION_DATA *Functions;  // Information about the library exports used in the program.
+	COUNT FunctionCount;           // The number of library exports used.
+} LIB_DATA;
+
+// User data for LibCallListModel.
+typedef struct {
+	COUNT LibCount;          // The number of libraries in the Libs field.
+	LIB_DATA *Libs;          // An array of all libraries used in the program, with additional information.
+	COUNT UsedLibCount;      // The number of libraries actually referenced.
+	COUNT LibFunctionCount;  // The total number of library exports used in the program.
+	LIBRARY *CurLibrary;     // Pointer to the library of the current item (output only).
+	OFFSET CurLib;           // Current library (from Libs) for which to count/return references (if >= 0).
+	OFFSET CurFunction;      // Current library export for which to count/return references (if >= 0).
+} LIB_CALL_USER_DATA;
+
+// A LIST_MODEL for library calls. This list model will return all library
+// calls in SourceSection (that point to CurFunction in CurLib if they are
+// >= 0).
+OFFSET LibCallListModel (SECTION *SourceSection, LIB_CALL **NextItem, LIB_CALL_USER_DATA *UserData, BOOLEAN Count, BOOLEAN Emit);
+
+#endif

+ 147 - 0
insert/other.c

@@ -0,0 +1,147 @@
+/* other.c: Routines to handle automatic insertion of section contents
+
+   Copyright (C) 2003 Sebastian Reichelt, Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "other.h"
+
+#include "../integers.h"
+#include "../manip.h"
+#include "../special.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Insert the data required for the _nostub comment format.
+BOOLEAN InsertNostubComments (SECTION *Section)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Export number.
+	COUNT ExportCount = 0;
+	
+	// Highest export ID.
+	COUNT HighestExport = -1;
+
+	// Loop variables.
+	SECTION *CurSection;
+	SYMBOL *CurSymbol;
+	
+	// Get the number and the highest ID of _nostub comments.
+	for_each (CurSection, Program->Sections)
+	{
+		for_each (CurSymbol, CurSection->Symbols)
+		{
+			if (CurSymbol->Exported && (!(strncmp (CurSymbol->Name, SYMPF_NOSTUB_DATA, sizeof (SYMPF_NOSTUB_DATA) - 1))))
+			{
+				OFFSET ExportNumber = GetExportNumber (CurSymbol->Name + (sizeof (SYMPF_NOSTUB_DATA_START) - 1));
+				if (ExportNumber >= 0)
+				{
+					ExportCount++;
+					if (ExportNumber > HighestExport)
+						HighestExport = ExportNumber;
+				}
+			}
+		}
+	}
+	
+	// Do not output anything if no comments are specified.
+	if (ExportCount)
+	{
+		// Allocate space for the comments.
+		I1 *NewData = AllocateSpaceInSection (Section, 4 * ExportCount);
+		
+		// Apply the format documented in _nostub_comment_header.s.
+		if (NewData)
+		{
+			COUNT CurrentExport;
+			
+			// We need this outer loop because the exports must be written out
+			// in order (i.e. 0, 1, 6, 32768; not 1, 32768, 6, 0).
+			for (CurrentExport = 0; CurrentExport <= HighestExport; CurrentExport++)
+			{
+				// For each section...
+				for_each (CurSection, Program->Sections)
+				{
+					// For each symbol...
+					for_each (CurSymbol, CurSection->Symbols)
+					{
+						// Check whether it belongs to the _nostub comment export we are looking for.
+						if (CurSymbol->Exported && (!(strncmp (CurSymbol->Name, SYMPF_NOSTUB_DATA, sizeof (SYMPF_NOSTUB_DATA) - 1))))
+						{
+							OFFSET ExportNumber = GetExportNumber (CurSymbol->Name + (sizeof (SYMPF_NOSTUB_DATA_START) - 1));
+							if (ExportNumber == CurrentExport)
+							{
+								// Insert the number of the export.
+								WriteTI2 (*((TI2 *) NewData), ExportNumber);
+								NewData += 2;
+								
+								{
+									// Create a new reloc at the current location,
+									// pointing to the exported symbol.
+									RELOC *Reloc = calloc (1, sizeof (RELOC));
+									if (Reloc)
+									{
+										Reloc->Parent = Section;
+										Reloc->Location = NewData - Section->Data;
+										NewData += ((Reloc->Size = 2));
+										Reloc->Target.Symbol = CurSymbol;
+										Reloc->Target.SymbolName = CurSymbol->Name;
+										SetRelocProgramRelative (Reloc);
+										InsertReloc (Section, Reloc);
+										CurSection->Referenced = TRUE;
+									}
+									else
+									{
+										Error (NULL, "Out of memory while inserting comments.");
+										return FALSE;
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		else
+			return FALSE;
+	}
+	
+	return TRUE;
+}
+
+#ifdef DATA_VAR_SUPPORT
+// Insert the name of the data variable specified for the program.
+BOOLEAN InsertDataVarName (SECTION *Section)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Do not output anything if no data variable is specified.
+	if (Program->DataVarInfo->Name)
+	{
+		// Allocate space for the string.
+		I1 *NewData = AllocateSpaceInSection (Section, strlen (Program->DataVarInfo->Name) + 1);
+		
+		// Copy the string into the allocated space.
+		if (NewData)
+			strcpy (NewData, Program->DataVarInfo->Name);
+		else
+			return FALSE;
+	}
+	
+	return TRUE;
+}
+#endif /* DATA_VAR_SUPPORT */

+ 34 - 0
insert/other.h

@@ -0,0 +1,34 @@
+/* other.h: Routines to handle automatic insertion of section contents
+
+   Copyright (C) 2003 Sebastian Reichelt, Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INSERT_OTHER_H
+#define INSERT_OTHER_H
+
+#include "../generic.h"
+#include "ins_def.h"
+#include "../data.h"
+
+// Insert exported symbols in the _nostub comment format.
+BOOLEAN InsertNostubComments (SECTION *Section);
+
+#ifdef DATA_VAR_SUPPORT
+// Insert the name of the data variable specified for the program.
+BOOLEAN InsertDataVarName (SECTION *Section);
+#endif /* DATA_VAR_SUPPORT */
+
+#endif

+ 99 - 0
int_arb.c

@@ -0,0 +1,99 @@
+/* int_arb.c: Functions to read/write arbitrary-size integers
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "integers.h"
+
+// See integers.h for a description of these functions.
+
+IMAX ReadTI (const void *Data, SIZE Size)
+{
+#ifdef TARGET_BIG_ENDIAN
+#define DEFINE_INT_SIZE(TempSize) \
+	if (Size >= TempSize) \
+		return (ReadTI##TempSize (*((const TI##TempSize *) (((const I1 *) Data) + Size - TempSize)))); \
+	else
+#else /* !TARGET_BIG_ENDIAN */
+#define DEFINE_INT_SIZE(TempSize) \
+	if (Size >= TempSize) \
+		return (ReadTI##TempSize (*((const TI##TempSize *) Data))); \
+	else
+#endif /* !TARGET_BIG_ENDIAN */
+	
+#include "int_def.inc"
+		return 0;
+	
+#undef DEFINE_INT_SIZE
+}
+
+SIMAX ReadSTI (const void *Data, SIZE Size)
+{
+#ifdef TARGET_BIG_ENDIAN
+#define DEFINE_INT_SIZE(TempSize) \
+	if (Size >= TempSize) \
+		return ((SI##TempSize) (ReadTI##TempSize (*((const TI##TempSize *) (((const I1 *) Data) + Size - TempSize))))); \
+	else
+#else /* !TARGET_BIG_ENDIAN */
+#define DEFINE_INT_SIZE(TempSize) \
+	if (Size >= TempSize) \
+		return ((SI##TempSize) (ReadTI##TempSize (*((const TI##TempSize *) Data)))); \
+	else
+#endif /* !TARGET_BIG_ENDIAN */
+	
+#include "int_def.inc"
+		return 0;
+
+#undef DEFINE_INT_SIZE
+}
+
+BOOLEAN WriteTI (void *Data, SIZE Size, SIMAX Value, BOOLEAN AllowSigned, BOOLEAN AllowUnsigned)
+{
+#ifdef TARGET_BIG_ENDIAN
+#define DEFINE_INT_SIZE(TempSize) \
+	if (Size >= TempSize) \
+	{ \
+		if ((AllowSigned && (Value == ((SI##TempSize) Value))) || (AllowUnsigned && (((IMAX) Value) == ((I##TempSize) Value)))) \
+		{ \
+			WriteTI##TempSize (*((TI##TempSize *) (((I1 *) Data) + Size - TempSize)), Value); \
+			return TRUE; \
+		} \
+	} \
+	else
+#else /* !TARGET_BIG_ENDIAN */
+#define DEFINE_INT_SIZE(TempSize) \
+	if (Size >= TempSize) \
+	{ \
+		if ((AllowSigned && (Value == ((SI##TempSize) Value))) || (AllowUnsigned && (((IMAX) Value) == ((I##TempSize) Value)))) \
+		{ \
+			WriteTI##TempSize (*((TI##TempSize *) Data), Value); \
+			return TRUE; \
+		} \
+	} \
+	else
+#endif /* !TARGET_BIG_ENDIAN */
+	
+#include "int_def.inc"
+		return FALSE;
+	return FALSE;
+	
+#undef DEFINE_INT_SIZE
+}
+
+BOOLEAN AddTI (void *Data, SIZE Size, SIMAX Value, BOOLEAN AllowSigned, BOOLEAN AllowUnsigned)
+{
+	return WriteTI (Data, Size, Value + ReadSTI (Data, Size), AllowSigned, AllowUnsigned);
+}

+ 3 - 0
int_def.inc

@@ -0,0 +1,3 @@
+DEFINE_INT_SIZE(4)
+DEFINE_INT_SIZE(2)
+DEFINE_INT_SIZE(1)

+ 41 - 0
integers.c

@@ -0,0 +1,41 @@
+/* integers.c: Definitions for integers with arbitrary endianness
+
+   Copyright (C) 2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "integers.h"
+
+// See integers.h for a description of these functions.
+
+#define DEFINE_INT_SIZE(Size) \
+I##Size ReadTI##Size (const TI##Size I) \
+{ \
+	return (ReadI##Size (I)); \
+} \
+I##Size ReadHI##Size (const HI##Size I) \
+{ \
+	return (ReadI##Size (I)); \
+} \
+void WriteTI##Size##_ (TI##Size *I, I##Size V) \
+{ \
+	WriteI##Size (*I, V); \
+} \
+void WriteHI##Size##_ (HI##Size *I, I##Size V) \
+{ \
+	WriteI##Size (*I, V); \
+}
+#include "int_def.inc"
+#undef DEFINE_INT_SIZE

+ 154 - 0
integers.h

@@ -0,0 +1,154 @@
+/* integers.h: Definitions for integers with arbitrary endianness
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INTEGERS_H
+#define INTEGERS_H
+
+#include "generic.h"
+
+#define TARGET_BIG_ENDIAN
+
+// *** Zero Integer Definitions ***
+
+typedef I1 ZI1;
+typedef I2 ZI2;
+typedef I4 ZI4;
+
+#define IsZero(I) (!(I))
+
+#ifdef __i386__
+// This only works on targets which don't care about alignment.
+#define IsZeroI1(I) (IsZero (*((ZI1 *) &(I))))
+#define IsZeroI2(I) (IsZero (*((ZI2 *) &(I))))
+#define IsZeroI4(I) (IsZero (*((ZI4 *) &(I))))
+#else
+// Use only bytewise reads for targets which do care about alignment.
+#define IsZeroI1(I) (IsZero (ReadTI1 (*(TI1*)&(I))))
+#define IsZeroI2(I) (IsZero (ReadTI2 (*(TI2*)&(I))))
+#define IsZeroI4(I) (IsZero (ReadTI4 (*(TI4*)&(I))))
+#endif
+
+// *** Target Integer Definitions ***
+
+// 1 Byte Target Integer
+// Defined as structure to avoid unguarded reading/writing.
+typedef struct ATTRIBUTE_PACKED {
+	I1 Val;
+} TI1;
+
+// 2 Byte Target Integer
+typedef struct ATTRIBUTE_PACKED {
+#ifdef TARGET_BIG_ENDIAN
+	TI1 Hi, Lo;
+#else /* !TARGET_BIG_ENDIAN */
+	TI1 Lo, Hi;
+#endif /* !TARGET_BIG_ENDIAN */
+} TI2;
+
+// 4 Byte Target Integer
+typedef struct ATTRIBUTE_PACKED {
+#ifdef TARGET_BIG_ENDIAN
+	TI2 Hi, Lo;
+#else /* !TARGET_BIG_ENDIAN */
+	TI2 Lo, Hi;
+#endif /* !TARGET_BIG_ENDIAN */
+} TI4;
+
+// *** Host Integer Definitions ***
+
+// 1 Byte Host Integer
+// Defined as structure to avoid unguarded reading/writing.
+typedef struct ATTRIBUTE_PACKED {
+	unsigned char Val;
+} HI1;
+
+// 2 Byte Host Integer
+typedef struct ATTRIBUTE_PACKED {
+#ifdef HOST_BIG_ENDIAN
+	HI1 Hi, Lo;
+#else /* !HOST_BIG_ENDIAN */
+	HI1 Lo, Hi;
+#endif /* !HOST_BIG_ENDIAN */
+} HI2;
+
+// 4 Byte Host Integer
+typedef struct ATTRIBUTE_PACKED {
+#ifdef HOST_BIG_ENDIAN
+	HI2 Hi, Lo;
+#else /* !HOST_BIG_ENDIAN */
+	HI2 Lo, Hi;
+#endif /* !HOST_BIG_ENDIAN */
+} HI4;
+
+// *** Macros to read arbitrary integers ***
+
+#define ReadI1(I) ((I).Val)
+#define ReadI2(I) ((ReadI1 ((I).Hi) << 8)  | ReadI1 ((I).Lo))
+#define ReadI4(I) ((ReadI2 ((I).Hi) << 16) | ReadI2 ((I).Lo))
+
+// *** Functions to read integers ***
+
+#define DEFINE_INT_SIZE(size) I##size ReadTI##size (const TI##size I); I##size ReadHI##size (const HI##size I);
+#include "int_def.inc"
+#undef DEFINE_INT_SIZE
+
+// *** Macros to write arbitrary integers ***
+
+#define WriteI1(I,V) ((I).Val = (V))
+#define WriteI2(I,V) ({ register I2 __V2 = (V); WriteI1 ((I).Hi, (__V2) >> 8);  WriteI1 ((I).Lo, (__V2)); })
+#define WriteI4(I,V) ({ register I4 __V4 = (V); WriteI2 ((I).Hi, (__V4) >> 16); WriteI2 ((I).Lo, (__V4)); })
+
+// *** Functions to write integers ***
+
+#define DEFINE_INT_SIZE(size) void WriteTI##size##_ (TI##size *I, I##size V); void WriteHI##size##_ (HI##size *I, I##size V);
+#include "int_def.inc"
+#undef DEFINE_INT_SIZE
+
+#define WriteTI1(I,V) (WriteTI1_ (&(I), (V)))
+#define WriteTI2(I,V) (WriteTI2_ (&(I), (V)))
+#define WriteTI4(I,V) (WriteTI4_ (&(I), (V)))
+#define WriteHI1(I,V) (WriteHI1_ (&(I), (V)))
+#define WriteHI2(I,V) (WriteHI2_ (&(I), (V)))
+#define WriteHI4(I,V) (WriteHI4_ (&(I), (V)))
+
+// *** Functions to read arbitrary-length target integers ***
+
+// Read an unsigned target integer of specified size from a
+// data buffer.
+IMAX  ReadTI  (const void *Data, SIZE Size);
+// Read a signed target integer of specified size from a
+// data buffer.
+SIMAX ReadSTI (const void *Data, SIZE Size);
+
+// *** Functions to write arbitrary-length target integers ***
+
+// Write a target integer of specified size to a data buffer.
+// The integer may be interpreted as a signed or unsigned
+// value based on the AllowSigned and AllowUnsigned parameters.
+// If the value does not fit into the contents, FALSE is
+// returned.
+BOOLEAN WriteTI (void *Data, SIZE Size, SIMAX Value, BOOLEAN AllowSigned, BOOLEAN AllowUnsigned);
+
+// Add a target integer of specified size to a data buffer.
+// The integer may be interpreted as a signed or unsigned
+// value based on the AllowSigned and AllowUnsigned parameters.
+// If the value does not fit into the contents, FALSE is
+// returned.
+BOOLEAN AddTI (void *Data, SIZE Size, SIMAX Value, BOOLEAN AllowSigned, BOOLEAN AllowUnsigned);
+
+#endif

+ 130 - 0
intrface.h

@@ -0,0 +1,130 @@
+/* intrface.h: Header file for the interface of the embedded versions
+
+   Copyright (C) 2003-2004 Sebastian Reichelt
+   Copyright (C) 2003-2004 Kevin Kofler
+   Copyright (C) 2004 Billy Charvet
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef INTRFACE_H
+#define INTRFACE_H
+
+#include "generic.h"
+
+// This specifies the current version of the export interface. The number has
+// to be incremented whenever the interface changes.
+#define CURRENT_INTERFACE_VERSION 17
+
+// Optimization Information and Program Statistics
+typedef struct ATTRIBUTE_PACKED {
+	const char
+		*Name;              // Name of the data variable, with or without path. No data variable if NULL.
+	B2
+		CreateCopy,         // Create and work on a copy of the variable.
+		CopyOnlyIfArchived; // Work on the original variable if it is not archived.
+} DATA_VAR_INFO;
+
+// Optimization Information and Program Statistics
+typedef struct ATTRIBUTE_PACKED {
+	B2
+		RemoveUnused,       // Remove unused sections.
+		OptimizeRelocs,     // Optimize relocs for better readability in dumps.
+		OptimizeNOPs,       // Optimize trailing NOPs in formats that insert them.
+		OptimizeReturns,    // Optimize subroutine branches followed by RTS into jumps.
+		OptimizeBranches,   // Optimize branches (e.g. to reduce relocation overhead).
+		OptimizeMoves,      // Optimize move, load, and push instructions.
+		OptimizeTests,      // Optimize compare and test instructions.
+		OptimizeCalcs,      // Optimize calculation instructions.
+		UseFLineJumps,      // Use F-Line jumps and subroutine jumps.
+		Use4ByteFLineJumps, // Use 4-byte F-Line jumps and subroutine jumps.
+		CutRanges,          // Cut unneeded section ranges when optimizing.
+		ReorderSections,    // Reorder sections to shorten references.
+		MergeConstants;     // Merge constants and strings to avoid duplication.
+	SI4
+		ProgramSize,            // Size of the on-calc program variable.
+		DataSize,               // Size of the data variable.
+		BSSSize,                // Size of the BSS section.
+		RelocCount,             // Number of absolute relocs in the program.
+		NativeRelocCount,       // Number of absolute relocs passed to the output format.
+		OptimizeBranchesResult, // Absolute relocs saved/savable by OptimizeBranches.
+		OptimizeMovesResult,    // Absolute relocs saved/savable by OptimizeMoves.
+		OptimizeTestsResult,    // Absolute relocs saved/savable by OptimizeTests.
+		OptimizeCalcsResult,    // Absolute relocs saved/savable by OptimizeCalcs.
+		UseFLineJumpsResult,    // Absolute relocs saved/savable by UseFLineJumps or Use4ByteFLineJumps.
+		CutRangesResult,        // Bytes saved/savable by range-cutting.
+		NearAssemblyResult;     // Bytes savable by using pc-relative assembly ('-l' option in GNU as). A value < 0 indicates that pc-relative assembly is impossible.
+} OPTIMIZE_INFO;
+
+// Calculator Type Enumeration
+typedef enum {
+	// Calculator Models
+	CALC_TI92 = 0x01, CALC_TI89 = 0x02, CALC_TI92PLUS = 0x04, CALC_V200 = 0x08,
+	// Calculator Flags
+	CALC_FLAG_TITANIUM = 0x100
+} ProgramCalcs;
+#define HIGHEST_CALC CALC_V200
+
+// File Role Constants (File Output)
+typedef enum {FR_MAIN = 0, FR_DATA = 1} FileRoles;
+
+// File Format Constants (File Output)
+typedef enum {FF_NONE = -1, FF_TIOS = 0, FF_TIOS_UPGRADE = 1} FileFormats;
+
+#ifdef TARGET_EMBEDDED
+
+// This is the format of the exported GetInterfaceVersion function.
+// The format must never change, otherwise using the wrong DLL will result in
+// a crash.
+#define EXP_GET_INTERFACE_VERSION() ATTRIBUTE_EXPORTED I4 GetInterfaceVersion (void)
+extern EXP_GET_INTERFACE_VERSION ();
+
+// Diagnostic Messages
+typedef enum {MT_ERROR = 0, MT_WARNING = 1} MessageTypes;
+typedef void (*ATTRIBUTE_INTERFACE ERROR_FUNCTION) (const char *FileName, const char *Text, I4 MessageType);
+
+// File Output
+typedef struct ATTRIBUTE_PACKED {
+	I1 *Data;  // Pointer to the writable file data.
+} INT_EXP_FILE;
+
+typedef B2 (*ATTRIBUTE_INTERFACE OUTPUT_FILE_FUNCTION) (INT_EXP_FILE *File, I4 FileSize, I4 DestCalc, I4 FileRole, I4 FileFormat, I4 FileType, const char *Extension, B2 Executable, I4 *EffectiveSize);
+typedef void (*ATTRIBUTE_INTERFACE OUTPUT_FILE_FINALIZE_FUNCTION) (INT_EXP_FILE *File);
+
+// This is the format of the LinkFiles function of ld-tigcc.
+#define EXP_LINK_FILES() ATTRIBUTE_EXPORTED I2 LinkFiles (const char **ObjectFiles, const char **ArchiveFiles, ERROR_FUNCTION ErrorMessage, OUTPUT_FILE_FUNCTION GetOutputFile, OUTPUT_FILE_FINALIZE_FUNCTION FinalizeOutputFile, B2 NativeMode, B2 FlashOS, B2 Fargo, DATA_VAR_INFO *DataVarInfo, OPTIMIZE_INFO *OptimizeInfo, B2 OmitBSSInitialization)
+extern EXP_LINK_FILES ();
+
+// This is the format of the CreateArchive function of ar-tigcc.
+#define EXP_CREATE_ARCHIVE() ATTRIBUTE_EXPORTED I2 CreateArchive (const char *DestFile, const char **ObjectFiles, ERROR_FUNCTION ErrorMessage, B2 NoNames)
+extern EXP_CREATE_ARCHIVE ();
+
+#else /* !TARGET_EMBEDDED */
+
+// File Output (internal)
+typedef struct {
+	FILE *File;               // Pointer to FILE struct from stdio.h.
+	COUNT CheckSum;           // Checksum of all bytes written.
+	BOOLEAN OutputBin;        // Reduces the output to just the program image.
+	unsigned int FileFormat;  // File format as defined in intrface.h.
+	unsigned int FileType;    // File tag for FF_TIOS.
+	const char *Extension;    // File extension for FF_TIOS.
+} INT_EXP_FILE;
+
+typedef BOOLEAN (*OUTPUT_FILE_FUNCTION) (INT_EXP_FILE *File, SIZE FileSize, unsigned int DestCalc, unsigned int FileRole, unsigned int FileFormat, unsigned int FileType, const char *Extension, BOOLEAN Executable, I4 *EffectiveSize);
+typedef void (*OUTPUT_FILE_FINALIZE_FUNCTION) (INT_EXP_FILE *File);
+
+#endif /* !TARGET_EMBEDDED */
+
+#endif

+ 98 - 0
lists.h

@@ -0,0 +1,98 @@
+/* lists.h: Definitions for linked lists
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef LISTS_H
+#define LISTS_H
+
+#define LIST_HEADER(Type) struct { Type *First, *Last; } Header
+#define LIST_ITEM_HEADER(Type) struct { Type *Prev, *Next; } Header
+
+#define GetPrev(Item) ((Item)->Header.Prev)
+#define GetNext(Item) ((Item)->Header.Next)
+
+#define GetFirst(List) ((List).Header.First)
+#define GetLast(List)  ((List).Header.Last)
+
+#define IsEmpty(List) (!(GetFirst (List)))
+
+#define Unlink(List,Item) ({ \
+	if ((Item) == GetFirst (List)) \
+		(List).Header.First = GetNext (Item); \
+	if ((Item) == GetLast (List)) \
+		(List).Header.Last = GetPrev (Item); \
+	if (GetPrev (Item)) \
+		GetPrev(Item)->Header.Next = GetNext (Item); \
+	if (GetNext (Item)) \
+		GetNext(Item)->Header.Prev = GetPrev (Item); \
+	(Item)->Header.Prev = NULL; \
+	(Item)->Header.Next = NULL; \
+})
+
+#define Append(List,Item) ({ \
+	(Item)->Header.Prev = GetLast (List); \
+	(Item)->Header.Next = NULL; \
+	if (!(GetFirst (List))) \
+		(List).Header.First = (Item); \
+	if (GetLast (List)) \
+		GetLast(List)->Header.Next = (Item); \
+	(List).Header.Last = (Item); \
+})
+
+#define Push(List,Item) ({ \
+	(Item)->Header.Prev = NULL; \
+	(Item)->Header.Next = GetFirst (List); \
+	if (GetFirst (List)) \
+		GetFirst(List)->Header.Prev = (Item); \
+	(List).Header.First = (Item); \
+	if (!(GetLast (List))) \
+		(List).Header.Last = (Item); \
+})
+
+#define InsertAfter(List,Item,After) ({ \
+	if (!(After)) \
+		Push (List, Item); \
+	else \
+	{ \
+		(Item)->Header.Next = GetNext (After); \
+		(Item)->Header.Prev = (After); \
+		(After)->Header.Next = (Item); \
+		if (GetNext (Item)) \
+			GetNext(Item)->Header.Prev = (Item); \
+		else \
+			(List).Header.Last = (Item); \
+	} \
+})
+
+#define InsertBefore(List,Item,Before) ({ \
+	if (!(Before)) \
+		Append (List, Item); \
+	else \
+		InsertAfter (List, Item, GetPrev (Before)); \
+})
+
+#define for_each(Item,List) for ((Item) = GetFirst (List); (Item); (Item) = GetNext (Item))
+
+#define CountItems(List,Type) ({ \
+	unsigned long Result = 0; \
+	Type *Item; \
+	for_each (Item, List) \
+		Result++; \
+	Result; \
+})
+
+#endif

+ 406 - 0
main.c

@@ -0,0 +1,406 @@
+/* main.c: Main entry point for ld-tigcc, handling the command line input
+
+   Copyright (C) 2002-2004 Sebastian Reichelt
+   Copyright (C) 2004 Kevin Kofler
+   Copyright (C) 2004 Billy Charvet
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "generic.h"
+#include "intrface.h"
+#include "data.h"
+#include "manip.h"
+#include "constmrg.h"
+#include "reorder.h"
+#include "formats/ar.h"
+#include "import/import.h"
+#include "import/imp_ar.h"
+#include "export/export.h"
+#include "special.h"
+
+#ifdef ENABLE_DUMP
+#include "dump.h"
+#endif /* ENABLE_DUMP */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#define RESULT_OK             0
+#define RESULT_GENERAL_ERROR  1
+#define RESULT_EXPORT_ERROR   2
+#define RESULT_STRANGE_ERROR  3
+
+// When compiling a DLL, the caller needs to be able to identify the version
+// of the function prototypes.
+#ifdef TARGET_DLL
+EXP_GET_INTERFACE_VERSION ()
+{
+	return CURRENT_INTERFACE_VERSION;
+}
+#endif /* TARGET_DLL */
+
+// Main entry point.
+#ifdef TARGET_EMBEDDED
+ERROR_FUNCTION ErrorFunction;
+void Error_Internal (const char *FileName, const char *Text)
+{
+	ErrorFunction (FileName, Text, MT_ERROR);
+}
+void Warning_Internal (const char *FileName, const char *Text)
+{
+	ErrorFunction (FileName, Text, MT_WARNING);
+}
+EXP_LINK_FILES ()
+{
+#define OptInfo OptimizeInfo
+#ifdef DATA_VAR_SUPPORT
+#define DatVarInfo DataVarInfo
+#endif /* DATA_VAR_SUPPORT */
+	const char **CurFile;
+	BOOLEAN IsArchive;
+#else /* !TARGET_EMBEDDED */
+int main (int ArgCount, const char **Args)
+{
+	OPTIMIZE_INFO _OptimizeInfo;
+#define OptInfo (&_OptimizeInfo)
+#ifdef DATA_VAR_SUPPORT
+	DATA_VAR_INFO _DataVarInfo;
+#define DatVarInfo (&_DataVarInfo)
+#endif /* DATA_VAR_SUPPORT */
+	int CurArg;
+	BOOLEAN OmitBSSInitialization = FALSE;
+#ifdef ENABLE_STATS
+	BOOLEAN DisplayStats = FALSE;
+#endif /* ENABLE_STATS */
+#ifdef DATA_VAR_SUPPORT
+	char DataVarString[MAX_NAME_LEN+1+MAX_NAME_LEN+1];
+#endif /* DATA_VAR_SUPPORT */
+#endif /* !TARGET_EMBEDDED */
+	
+#ifdef ENABLE_DUMP
+#define DUMP_COUNT 9
+	BOOLEAN Dump [DUMP_COUNT] = {[0 ... (DUMP_COUNT - 1)] = FALSE};
+#define DoDump(DumpNumber) (_DoDump (DumpNumber, ""))
+#define DoSpecialDump(DumpNumber,SpecialText) (_DoDump (DumpNumber, " " SpecialText))
+#define _DoDump(DumpNumber,SpecialText) \
+({	if (((DumpNumber) >= 0) && ((DumpNumber) < DUMP_COUNT) && (Dump [(DumpNumber)])) \
+	{ \
+		printf ("*** DUMP " #DumpNumber SpecialText " ***\n"); \
+		DumpProgram (stdout, NULL, &Program); \
+		printf ("\n"); \
+	} })
+#else /* !ENABLE_DUMP */
+#define DoDump(DumpNumber) ((void) 0)
+#define DoSpecialDump(DumpNumber,SpecialText) ((void) 0)
+#endif /* !ENABLE_DUMP */
+	
+	int Result = RESULT_GENERAL_ERROR;
+	
+	PROGRAM Program;
+	
+	// Check the sizes of basic integer types.
+	if (sizeof (I1) != 1 || sizeof (I2) != 2 || sizeof (I4) != 4 || sizeof (SI1) != 1 || sizeof (SI2) != 2 || sizeof (SI4) != 4 || sizeof (OFFSET) < sizeof (SI4))
+	{
+		Error (NULL, "Generic type size error!");
+		return RESULT_STRANGE_ERROR;
+	}
+	
+	// Initialize.
+	memset (&Program, 0, sizeof (Program));
+	Program.EntryPoint.SymbolName = "__entry_point";
+#ifdef TARGET_EMBEDDED
+	ErrorFunction = ErrorMessage;
+	if (NativeMode)
+		Program.Type = PT_NATIVE;
+#else /* !TARGET_EMBEDDED */
+	memset (&_OptimizeInfo, 0, sizeof (_OptimizeInfo));
+#ifdef DATA_VAR_SUPPORT
+	memset (&_DataVarInfo, 0, sizeof (_DataVarInfo));
+#endif /* DATA_VAR_SUPPORT */
+	memset (ProgramName, 0, MAX_NAME_LEN + 1);
+	memset (ProgramFolder, 0, MAX_NAME_LEN + 1);
+	strcpy (ProgramFolder, "main");
+#endif /* !TARGET_EMBEDDED */
+	Program.OptimizeInfo = OptInfo;
+#ifdef DATA_VAR_SUPPORT
+	Program.DataVarInfo = DatVarInfo;
+#endif /* DATA_VAR_SUPPORT */
+	
+#ifdef TARGET_EMBEDDED
+	if (Fargo)	
+	{
+#ifdef FARGO_SUPPORT
+		Program.Type = PT_FARGO;
+		Program.Calcs |= CALC_TI92;
+		Warning (NULL, "Fargo support in TIGCC is experimental.");
+#else /* !FARGO_SUPPORT */
+		Error (NULL, "Fargo support is not compiled in.");
+		goto Cleanup;
+#endif /* !FARGO_SUPPORT */
+	}
+	
+	if (FlashOS)	
+	{
+#ifdef FLASH_OS_SUPPORT
+		Program.Type = PT_FLASH_OS;
+		Warning (NULL, "Flash OS support in TIGCC is experimental.");
+#else /* !FLASH_OS_SUPPORT */
+		Error (NULL, "Flash OS support is not compiled in.");
+		goto Cleanup;
+#endif /* !FLASH_OS_SUPPORT */
+	}
+	
+	CurFile = ObjectFiles;
+	IsArchive = FALSE;
+	while (CurFile && (*CurFile))
+	{
+		FILE *File = fopen (*CurFile, "rb");
+		if (File)
+		{
+			SIZE Size;
+			fseek (File, 0, SEEK_END);
+			Size = ftell (File);
+			rewind (File);
+			{
+				I1 *Data = malloc (Size);
+				if (Data)
+				{
+					if (fread (Data, Size, 1, File) == 1)
+					{
+						if (IsArchive)
+							AddArchiveFile (&Program, Data, Size, *CurFile);
+						else
+							ImportObjectFile (&Program, Data, Size, *CurFile);
+					}
+					else
+						Error (*CurFile, "Unable to read file.");
+					if (!IsArchive)
+						free (Data);
+				}
+				else
+					Error (*CurFile, "Not enough memory to load file.");
+			}
+			fclose (File);
+		}
+		else
+			Error (*CurFile, "Unable to open file.");
+		
+		if ((!IsArchive) && (!(*(CurFile + 1))))
+		{
+			CurFile = ArchiveFiles;
+			IsArchive = TRUE;
+		}
+		else
+			CurFile++;
+	}
+#else /* !TARGET_EMBEDDED */
+#include "main_opt.inc"
+#endif /* !TARGET_EMBEDDED */
+
+	if (IsEmpty (Program.Sections))
+		Error (NULL, "Cannot create empty program.");
+	else
+	{
+		DoDump (0);
+		
+		// Connect all relocs to the appropriate symbols, or convert them into
+		// ROM/RAM calls. Also import objects from archives.
+		// Report all unresolved references.
+		if (ResolveRelocs (&Program, TRUE))
+		{
+			DoDump (1);
+			
+			// Merge all zero-data and uninitialized sections.
+			Program.BSSSection = MergeAllSections (&Program, NULL, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE);
+			
+			// As a dirty trick, allow the caller to skip the BSS
+			// initialization entirely.
+			if (Program.BSSSection && OmitBSSInitialization)
+				Program.BSSSection->Initialized = FALSE;
+			
+			// Extract, merge, and mark constructor and destructor sections.
+			CreateSectionMarkers (&(Program.Constructors), MergeAllSections (&Program, NULL, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, TRUE,  FALSE, TRUE, TRUE));
+			CreateSectionMarkers (&(Program.Destructors),  MergeAllSections (&Program, NULL, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE,  TRUE, TRUE));
+				
+#ifdef DATA_VAR_SUPPORT
+			// If we want a separate data variable, merge all data
+			// sections.
+			if (DatVarInfo->Name)
+			{
+				Program.DataSection = MergeAllSections (&Program, NULL, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, FALSE);
+				// Mark the section as "handled" so it will not be merged
+				// with code.
+				if (Program.DataSection)
+					Program.DataSection->Handled = TRUE;
+				else
+					Warning (NULL, "No data to put into external variable.");
+			}
+#endif /* DATA_VAR_SUPPORT */
+			
+			DoDump (2);
+			
+			// Create all global imports needed by this program.
+			CreateSpecialGlobalImports (&Program);
+			
+			DoDump (3);
+			
+			// Resolve all remaining global imports. Usually, global imports
+			// are processed directly, but if a symbol contains an inverted
+			// condition, we have to wait until we really know that no such
+			// global import exists.
+			ResolveRemainingGlobalImports (&Program);
+			
+			// Resolve the relocs from the newly imported archive members.
+			if (ResolveRelocs (&Program, TRUE))
+			{
+				// No more startup sections may be added.
+				// So the program entry point is fixed from now on.
+				{
+					SECTION *FirstSection = GetFirst (Program.Sections);
+					if (FirstSection)
+					{
+						Program.EntryPoint.Symbol = FirstSection->SectionSymbol;
+						Program.EntryPoint.SymbolName = FirstSection->SectionSymbol->Name;
+#ifdef FARGO_SUPPORT
+						// Fargo programs use the location in front
+						// of the two size bytes as the entry point.
+						if (Program.Type == PT_FARGO)
+							Program.EntryPoint.Offset -= 2;
+#endif /* FARGO_SUPPORT */
+					}
+				}
+				
+				DoDump (4);
+				
+				// Now that all relocs have been resolved, there is no chance
+				// that some previously unknown archive member will add new imports.
+				CheckAllGlobalImports (&Program);
+				
+				if (OptInfo->OptimizeRelocs)
+				{
+					// Optimize relocs. This should not have any effect on the program.
+					OptimizeRelocs (&Program);
+					
+					DoSpecialDump (4, "(optimized)");
+				}
+				
+				if (OptInfo->RemoveUnused && (!(Program.Frozen)))
+				{
+					// Remove unreferenced sections.
+					RemoveUnusedSections (&Program);
+					
+					DoSpecialDump (4, "(cut)");
+				}
+				
+				if (OptInfo->MergeConstants && (!(Program.Frozen)))
+				{
+					// Merge constants.
+					MergeConstants (&Program);
+					
+					DoSpecialDump (4, "(const-merged)");
+				}
+				
+				if (OptInfo->ReorderSections && (!(Program.Frozen)))
+				{
+					// Reorder sections.
+					ReorderSections (&Program);
+					
+					DoSpecialDump (4, "(reordered)");
+				}
+				
+#ifdef FLASH_OS_SUPPORT
+				if (Program.Type == PT_FLASH_OS)
+				{
+					// Flash OS export: merge startup and normal sections separately.
+					// The resulting two parts are merged later, padding the first
+					// part to the full 24 KB of the OS startup area (base 1)
+					// + the 8 KB corresponding to the read protected FlashROM
+					// area.
+					// Thus, the startup sections end up in the OS startup area
+					// (base 1) and the non-startup areas end up in the OS main
+					// area (base 2), the big OS code part.
+					
+					// Merge all startup sections.
+					MergeAllSections (&Program, NULL, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE);
+					// Merge all normal sections.
+					MergeAllSections (&Program, NULL, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE);
+				}				
+#endif /* FLASH_OS_SUPPORT */
+				
+				// Merge all initialized sections.
+				Program.MainSection = MergeAllSections (&Program, NULL, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+				// Merge all unhandled sections.
+				Program.MainSection = MergeAllSections (&Program, Program.MainSection, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+				
+				// Record the size of the BSS section.
+				if (Program.BSSSection)
+					OptInfo->BSSSize = Program.BSSSection->Size;
+				
+				if (Program.MainSection && Program.Library && (Program.MainSection->StartupNumber > 0))
+					Warning (Program.MainSection->FileName, "Library only contains program startup sections.");
+				
+				DoDump (5);
+				
+				// Fix the code (if that is still possible).
+				FixCode (&Program);
+				
+				DoDump (6);
+				
+				// Resolve the relocs pointing to built-in symbols.
+				Program.ResolveAllBuiltins = TRUE;
+				// Resolve requested automatic insertions.
+				if (ResolveRelocs (&Program, TRUE))
+				{
+					DoDump (7);
+					
+					// Do one more optimization pass for relocs to automatic insertions.
+					FixCode (&Program);
+					
+					// Remove relocs where possible.
+					if (FixupRelativeRelocs (&Program))
+					{
+						DoDump (8);
+						
+						if (Program.Calcs)
+						{
+							// Export the program to the appropriate files.
+							if (ExportProgram (&Program, GetOutputFile, FinalizeOutputFile))
+							{
+								Result = RESULT_OK;
+#ifndef TARGET_EMBEDDED
+#ifdef ENABLE_STATS
+#include "main_vbs.inc"
+#endif /* ENABLE_STATS */
+#endif /* !TARGET_EMBEDDED */
+							}
+							else
+								Result = RESULT_EXPORT_ERROR;
+						}
+						else
+							Error (NULL, "No target calculators specified.");
+					}
+				}
+			}
+		}
+	}
+	
+Cleanup: ATTRIBUTE_UNUSED
+	// Final Cleanup.
+	FreeProgram (&Program);
+	
+	return Result;
+}

+ 432 - 0
main_opt.inc

@@ -0,0 +1,432 @@
+/* main_opt.inc: Option parser for ld-tigcc command-line program
+
+   Copyright (C) 2002-2004 Sebastian Reichelt
+   Copyright (C) 2004 Billy Charvet
+   Copyright (C) 2004 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+// This file is included by main.c.
+{
+	const char *Arg;
+	
+	BOOLEAN ArgMatches (const char *Name)
+	{
+		return (!(strcmp (Arg, Name)));
+	}
+	
+	// For each argument...
+	for (CurArg = 1; CurArg < ArgCount; CurArg++)
+	{
+		// Get pointer to arg.
+		Arg = Args [CurArg];
+		// Check whether it is empty.
+		if (Arg && *Arg)
+		{
+			// Check whether it is an option.
+			if (Arg [0] == '-')
+			{
+				// Skip this dash and possibly a second one.
+				Arg++;
+				if (Arg [0] == '-')
+					Arg++;
+				
+				// Handle the option.
+#ifdef ENABLE_HELP
+				if (ArgMatches ("version"))
+				{
+					printf ("ld-tigcc Version " PROGRAM_VERSION_STRING "\n"
+					        COPYRIGHT_NOTICE_STRING "\n"
+					        "ld-tigcc is free software; see the source code for details.\n");
+					goto Cleanup;
+				}
+				else if ((ArgMatches ("help")) || (ArgMatches ("h")))
+				{
+					printf ("Usage: ld-tigcc [options] <file> [<file> ...]\n"
+					        "Options:\n"
+					        "  -h --help               Display this message\n"
+					        "     --version            Display version number\n"
+#ifdef ENABLE_STATS
+					        "  -v --verbose            Display program statistics\n"
+#endif /* ENABLE_STATS */
+#ifdef ENABLE_DUMP
+					        "     --dump[n]            Display [n-th] dump of the program contents\n"
+#endif /* ENABLE_DUMP */
+					        "     --native             Link in TIGCC native mode\n"
+#ifdef FLASH_OS_SUPPORT
+					        "     --flash-os           Create (unsigned) Flash OS\n"
+#endif /* FLASH_OS_SUPPORT */
+#ifdef FARGO_SUPPORT
+					        "     --fargo              Create Fargo II program\n"
+#endif /* FARGO_SUPPORT */
+					        "     --remove-unused      Remove unused sections\n"
+					        "     --optimize-relocs    Update relocs to point to the closest symbol\n"
+					        "     --optimize-code      Perform all MC68000 code optimizations (!)\n"
+					        "       --optimize-nops      Remove format-specific NOP instructions\n"
+					        "       --optimize-returns   Optimize function return sequences (!)\n"
+					        "       --optimize-branches  Optimize branch instructions\n"
+					        "       --optimize-moves     Optimize move/load/push instructions (!)\n"
+					        "       --optimize-tests     Optimize compare/test instructions (!)\n"
+					        "       --optimize-calcs     Optimize calculation instructions (!)\n"
+					        "     --cut-ranges         Cut unneeded section ranges when optimizing (!)\n"
+					        "                          (ignored for files not assembled in all-relocs mode)\n"
+					        "     --reorder-sections   Reorder sections to make references shorter\n"
+					        "     --merge-constants    Merge constants and strings to avoid duplication\n"
+					        "     --omit-bss-init      Do not initialize BSS data to zero\n"
+					        "     --outputbin          Create program image without PC header\n"
+					        "  -o --output <file>      Write output to <file>.???\n"
+					        "  -n --varname <[folder\\]name>\n"
+					        "                          Name output variable <folder\\name>\n"
+#ifdef DATA_VAR_SUPPORT
+					        "  -d --data-var <[folder\\]name>\n"
+					        "                          Use data variable named <folder\\name>\n"
+					        "     --data-var-copy={never|archived|always}\n"
+					        "                          Work on original data variable or copy\n"
+#endif /* DATA_VAR_SUPPORT */
+					        "<file> can be an archive or an object file.\n"
+					        "Options marked with `(!)' are likely to cause errors in binary code.\n");
+					goto Cleanup;
+				}
+				else
+#endif /* ENABLE_HELP */
+#ifdef ENABLE_STATS
+				if ((ArgMatches ("verbose")) || (ArgMatches ("v")))
+					DisplayStats = TRUE;
+				else
+#endif /* ENABLE_STATS */
+#ifdef ENABLE_DUMP
+				if (!(strncmp (Arg, "dump", sizeof ("dump") - 1)))
+				{
+					Arg += sizeof ("dump") - 1;
+					if (*Arg)
+					{
+						int DumpIdx = atoi (Arg);
+						if ((DumpIdx >= 0) && (DumpIdx < DUMP_COUNT))
+							Dump [DumpIdx] = TRUE;
+					}
+					else
+						memset (Dump, -1, sizeof (Dump));
+				}
+				else
+#endif /* ENABLE_DUMP */
+				if (ArgMatches ("native"))
+					Program.Type = PT_NATIVE;
+				else
+#ifdef FLASH_OS_SUPPORT
+				if (ArgMatches ("flash-os"))
+				{
+					Program.Type = PT_FLASH_OS;
+					Warning (NULL, "Flash OS support in TIGCC is experimental.");
+				}
+				else
+#endif /* FLASH_OS_SUPPORT */
+#ifdef FARGO_SUPPORT
+				if (ArgMatches ("fargo"))
+				{
+					Program.Type = PT_FARGO;
+					Program.Calcs |= CALC_TI92;
+					Warning (NULL, "Fargo support in TIGCC is experimental.");
+				}
+				else
+#endif /* FARGO_SUPPORT */
+				if (ArgMatches ("remove-unused"))
+					OptInfo->RemoveUnused = TRUE;
+				else if (ArgMatches ("optimize-relocs"))
+					OptInfo->OptimizeRelocs = TRUE;
+				else if (ArgMatches ("optimize-code"))
+				{
+					OptInfo->OptimizeNOPs     = TRUE;
+					OptInfo->OptimizeReturns  = TRUE;
+					OptInfo->OptimizeBranches = TRUE;
+					OptInfo->OptimizeMoves    = TRUE;
+					OptInfo->OptimizeTests    = TRUE;
+					OptInfo->OptimizeCalcs    = TRUE;
+				}
+				else if (ArgMatches ("optimize-nops"))
+					OptInfo->OptimizeNOPs = TRUE;
+				else if (ArgMatches ("optimize-returns"))
+					OptInfo->OptimizeReturns = TRUE;
+				else if (ArgMatches ("optimize-branches"))
+					OptInfo->OptimizeBranches = TRUE;
+				else if (ArgMatches ("optimize-moves"))
+					OptInfo->OptimizeMoves = TRUE;
+				else if (ArgMatches ("optimize-tests"))
+					OptInfo->OptimizeTests = TRUE;
+				else if (ArgMatches ("optimize-calcs"))
+					OptInfo->OptimizeCalcs = TRUE;
+				else if (ArgMatches ("cut-ranges"))
+					OptInfo->CutRanges = TRUE;
+				else if (ArgMatches ("reorder-sections"))
+					OptInfo->ReorderSections = TRUE;
+				else if (ArgMatches ("merge-constants"))
+					OptInfo->MergeConstants = TRUE;
+				else if (ArgMatches ("omit-bss-init"))
+					OmitBSSInitialization = TRUE;
+				else if (ArgMatches ("outputbin"))
+					OutputBin = TRUE;
+				else if ((ArgMatches ("output")) || (ArgMatches ("o")))
+				{
+					if ((++CurArg) < ArgCount)
+					{
+						DestFile = Args [CurArg];
+						DestFileSize = strlen (DestFile);
+						// Adjust the program's name if it isn't set yet.
+						if (!(*ProgramName))
+						{
+							// Start at the last separator.
+							const char *Name = strrchr (DestFile, '/');
+							if (!Name)
+								Name = strrchr (DestFile, '\\');
+							if (Name)
+								Name++;
+							else
+								Name = DestFile;
+							// Copy into name field.
+							strncpy (ProgramName, Name, MAX_NAME_LEN);
+							{
+								// Cut at first dot.
+								char *S = strchr (ProgramName, '.');
+								if (S)
+									*S = 0;
+								// Cut at first space.
+								S = strchr (ProgramName, ' ');
+								if (S)
+									*S = 0;
+								// Convert to lowercase.
+								for (S = ProgramName; *S; S++)
+									*S = tolower (*S);
+							}
+						}
+					}
+					else
+						Error (NULL, "`--output' option must be followed by a name.");
+				}
+				else if ((ArgMatches ("varname")) || (ArgMatches ("n")))
+				{
+					if ((++CurArg) < ArgCount)
+					{
+						// Extract variable name.
+						const char *Name = strchr (Args [CurArg], '/');
+						if (!Name)
+							Name = strchr (Args [CurArg], '\\');
+						if (Name)
+						{
+							Name++;
+							// Extract folder name.
+							strncpy (ProgramFolder, Args [CurArg], MAX_NAME_LEN);
+							{
+								char *S;
+								for (S = ProgramFolder; *S; S++)
+								{
+									if (*S == '/' || *S == '\\')
+									{
+										*S = 0;
+										break;
+									}
+									else
+										*S = tolower (*S);
+								}
+							}
+						}
+						else
+							Name = Args [CurArg];
+						strncpy (ProgramName, Name, MAX_NAME_LEN);
+						{
+							char *S;
+							for (S = ProgramName; *S; S++)
+								*S = tolower (*S);
+						}
+					}
+					else
+						Error (NULL, "`--varname' option must be followed by a name.");
+				}
+#ifdef DATA_VAR_SUPPORT
+				else if (ArgMatches ("data-var") || ArgMatches ("d"))
+				{
+					if ((++CurArg) < ArgCount)
+					{
+						// Extract variable name.
+						const char *Name = strchr (Args [CurArg], '/');
+						if (!Name)
+							Name = strchr (Args [CurArg], '\\');
+						if (Name)
+						{
+							Name++;
+							// Extract folder name.
+							strncpy (DataFolder, Args [CurArg], MAX_NAME_LEN);
+							{
+								char *S;
+								for (S = DataFolder; *S; S++)
+								{
+									if (*S == '/' || *S == '\\')
+									{
+										*S = 0;
+										break;
+									}
+									else
+										*S = tolower (*S);
+								}
+							}
+							sprintf (DataVarString, "%s\\", DataFolder);
+						}
+						else
+						{
+							Name = Args [CurArg];
+							*DataVarString = 0;
+						}
+						strncpy (DataName, Name, MAX_NAME_LEN);
+						{
+							char *S;
+							for (S = DataName; *S; S++)
+								*S = tolower (*S);
+						}
+						strcat (DataVarString, DataName);
+						DatVarInfo->Name = DataVarString;
+						DatVarInfo->CreateCopy = TRUE;
+						DatVarInfo->CopyOnlyIfArchived = TRUE;
+					}
+					else
+						Error (NULL, "`--data-var' option must be followed by a name.");
+				}
+				else if (!(strncmp (Arg, "data-var-copy=", sizeof ("data-var-copy=") - 1)))
+				{
+					Arg += sizeof ("data-var-copy=") - 1;
+					if (ArgMatches ("always"))
+					{
+						DatVarInfo->CreateCopy = TRUE;
+						DatVarInfo->CopyOnlyIfArchived = FALSE;
+					}
+					else if (ArgMatches ("archived"))
+					{
+						DatVarInfo->CreateCopy = TRUE;
+						DatVarInfo->CopyOnlyIfArchived = TRUE;
+					}
+					else if (ArgMatches ("never"))
+					{
+						DatVarInfo->CreateCopy = FALSE;
+						DatVarInfo->CopyOnlyIfArchived = FALSE;
+					}
+					else
+						Error (NULL, "Unrecognized copying condition `%s'.", Arg);
+				}
+#endif /* DATA_VAR_SUPPORT */
+				else
+					Error (NULL, "Unrecognized option `%s'.", Args [CurArg]);
+			}
+			else
+			{
+				// Treat it as a file name.
+				
+				// Open the file and load it into memory.
+				{
+					FILE *File = fopen (Arg, "rb");
+					if (File)
+					{
+						SIZE Size;
+						fseek (File, 0, SEEK_END);
+						Size = ftell (File);
+						rewind (File);
+						{
+							I1 *Data = malloc (Size);
+							if (Data)
+							{
+								BOOLEAN IsArchive = FALSE;
+								
+								if (fread (Data, Size, 1, File) == 1)
+								{
+									// Act differently depending on the
+									// file type.
+									IsArchive = IsArchiveFile (Data, Size);
+									
+									if (IsArchive)
+									{
+										// Add the file to the list of
+										// available archives.
+										AddArchiveFile (&Program, Data, Size, Arg);
+									}
+									else
+									{
+										// Adjust the destination file
+										// name if it isn't set yet.
+										if (!DestFile)
+										{
+											const char *DotPos, *SlashPos;
+											// Assign the destination file name.
+											DestFile = Arg;
+											DestFileSize = strlen (Arg);
+											// Cut it at the last dot.
+											DotPos = strrchr (Arg, '.');
+											if (DotPos)
+											{
+												SlashPos = strrchr (Arg, '/');
+												if (!SlashPos)
+													SlashPos = strrchr (Arg, '\\');
+												if ((!SlashPos) || (DotPos > SlashPos))
+													DestFileSize = DotPos - Arg;
+											}
+										}
+										
+										// Import the file.
+										ImportObjectFile (&Program, Data, Size, Arg);
+									}
+								}
+								else
+									Error (Arg, "Unable to read file.");
+								
+								// Archives are not imported; they keep
+								// their data until they are freed.
+								if (!IsArchive)
+									free (Data);
+							}
+							else
+								Error (Arg, "Not enough memory to load file.");
+						}
+						fclose (File);
+					}
+					else
+						Error (Arg, "Unable to open file.");
+				}
+			}
+		}
+	}
+	
+	// Adjust the program's name if it isn't set yet.
+	if (DestFile && (!(*ProgramName)))
+	{
+		// Start at the last separator.
+		const char *Name = strrchr (DestFile, '/');
+		if (!Name)
+			Name = strrchr (DestFile, '\\');
+		if (Name)
+			Name++;
+		else
+			Name = DestFile;
+		// Copy into name field.
+		strncpy (ProgramName, Name, MAX_NAME_LEN);
+		{
+			// Cut at first dot.
+			char *S = strchr (ProgramName, '.');
+			if (S)
+				*S = 0;
+			// Cut at first space.
+			S = strchr (ProgramName, ' ');
+			if (S)
+				*S = 0;
+			// Convert to lowercase.
+			for (S = ProgramName; *S; S++)
+				*S = tolower (*S);
+		}
+	}
+}

+ 64 - 0
main_vbs.inc

@@ -0,0 +1,64 @@
+/* main_vbs.inc: Display of statistics for ld-tigcc
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+// This file is included by main.c
+{
+	if (DisplayStats)
+	{
+		printf ("Target Calculators:\n");
+		if (Program.Calcs & CALC_TI92)
+			printf ("  TI-92\n");
+		if (Program.Calcs & CALC_TI89)
+			printf ("  TI-89\n");
+		if (Program.Calcs & CALC_TI92PLUS)
+			printf ("  TI-92 Plus\n");
+		if (Program.Calcs & CALC_V200)
+			printf ("  V200\n");
+		printf ("Program Statistics:\n"
+				"  Program Variable Name:                    %s\\%s\n"
+				"  Program Variable Size:                    %ld Bytes\n",
+				ProgramFolder, ProgramName, (long) OptInfo->ProgramSize);
+		if (OptInfo->DataSize > 0)
+		{
+			printf ("  Data Variable Size:                       %ld Bytes\n",
+					(long) OptInfo->DataSize);
+		}
+		if (OptInfo->BSSSize > 0)
+		{
+			printf ("  BSS Size:                                 %ld Bytes\n",
+					(long) OptInfo->BSSSize);
+		}
+		printf ("  Absolute Relocs:                          %ld\n"
+				"  Natively Emitted Relocs:                  %ld\n",
+				(long) OptInfo->RelocCount, (long) OptInfo->NativeRelocCount);
+		if (OptInfo->OptimizeBranchesResult > 0)
+			printf (OptInfo->OptimizeBranches ? "  Relocs Removed by Branch Optimization:    %ld\n" : "  Relocs Removable by Branch Optimization:  %ld\n", (long) OptInfo->OptimizeBranchesResult);
+		if (OptInfo->OptimizeMovesResult > 0)
+			printf (OptInfo->OptimizeMoves ? "  Relocs Removed by Move Optimization:      %ld\n" : "  Relocs Removable by Move Optimization:    %ld\n", (long) OptInfo->OptimizeMovesResult);
+		if (OptInfo->OptimizeTestsResult > 0)
+			printf (OptInfo->OptimizeTests ? "  Relocs Removed by Test Optimization:      %ld\n" : "  Relocs Removable by Test Optimization:    %ld\n", (long) OptInfo->OptimizeTestsResult);
+		if (OptInfo->OptimizeCalcsResult > 0)
+			printf (OptInfo->OptimizeCalcs ? "  Relocs Removed by Calc Optimization:      %ld\n" : "  Relocs Removable by Calc Optimization:    %ld\n", (long) OptInfo->OptimizeCalcsResult);
+		if (OptInfo->UseFLineJumpsResult > 0)
+			printf (OptInfo->UseFLineJumps || OptInfo->Use4ByteFLineJumps ? "  Relocs Removed by F-Line Jumps:           %ld\n" : "  Relocs Removable by F-Line Jumps:         %ld\n", (long) OptInfo->UseFLineJumpsResult);
+		if (OptInfo->CutRangesResult > 0)
+			printf (OptInfo->CutRanges ? "  Space Saved by Range-Cutting:             %ld Bytes\n" : "  Estimated Space Savable by Range-Cutting: %ld Bytes\n", (long) OptInfo->CutRangesResult);
+		if (OptInfo->NearAssemblyResult > 0)
+			printf ("  Space Savable by Using GAS `-l' Switch:   %ld Bytes\n", (long) OptInfo->NearAssemblyResult);
+	}
+}

+ 1987 - 0
manip.c

@@ -0,0 +1,1987 @@
+/* manip.c: Routines to manipulate the internal data
+
+   Copyright (C) 2002-2004 Sebastian Reichelt
+   Copyright (C) 2003-2004 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "manip.h"
+
+#include "integers.h"
+#include "special.h"
+#include "import/imp_ar.h"
+#include "bincode/fix_m68k.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// Free the program tree.
+void FreeProgram (PROGRAM *Program)
+{
+	SECTION *Section, *NextSection;
+	LIBRARY *Library, *NextLibrary;
+	ARCHIVE *Archive, *NextArchive;
+	GLOBAL_IMPORT *GlobalImport, *NextGlobalImport;
+	
+	for (GlobalImport = GetLast (Program->GlobalImports); GlobalImport; GlobalImport = NextGlobalImport)
+	{
+		NextGlobalImport = GetPrev (GlobalImport);
+		free (GlobalImport);
+	}
+	
+	for (Archive = GetLast (Program->Archives); Archive; Archive = NextArchive)
+	{
+		ARCHIVE_OBJECT *Object, *NextObject;
+		ARCHIVE_SYMBOL *Symbol, *NextSymbol;
+		
+		NextArchive = GetPrev (Archive);
+		
+		for (Object = GetLast (Archive->ObjectFiles); Object; Object = NextObject)
+		{
+			NextObject = GetPrev (Object);
+			free (Object);
+		}
+		
+		for (Symbol = GetLast (Archive->Symbols); Symbol; Symbol = NextSymbol)
+		{
+			NextSymbol = GetPrev (Symbol);
+			free (Symbol);
+		}
+		
+		free ((I1 *) (Archive->Data));
+		free (Archive);
+	}
+	
+	for (Library = GetLast (Program->Libraries); Library; Library = NextLibrary)
+	{
+		NextLibrary = GetPrev (Library);
+		free (Library);
+	}
+	
+	for (Section = GetLast (Program->Sections); Section; Section = NextSection)
+	{
+		NextSection = GetPrev (Section);
+		FreeSection (Section);
+	}
+	
+	// Set all data to 0.
+	memset (Program, 0, sizeof (PROGRAM));
+}
+
+// Free a section. The section is assumed not to be referenced. Use
+// RemoveSectionIfUnused instead if the section might still be referenced.
+void FreeSection (SECTION *Section)
+{
+	PROGRAM *Program = Section->Parent;
+	
+	// Need to free the data, if any.
+	if (Section->Data)
+		free (Section->Data);
+	
+	// Define a macro to make things easier.
+#define FreeItems(Type,Item) \
+({ \
+	Type *Item, *Next##Item; \
+	for (Item = GetLast (Section->Item##s); Item; Item = Next##Item) \
+	{ \
+		Next##Item = GetPrev (Item); \
+		free (Item); \
+	} \
+})
+	
+	FreeItems (SEGMENT,  Segment);
+	FreeItems (LIB_CALL, LibCall);
+	FreeItems (RAM_CALL, RAMCall);
+	FreeItems (ROM_CALL, ROMCall);
+	
+	{
+		RELOC *Reloc, *NextReloc;
+		for (Reloc = GetLast (Section->Relocs); Reloc; Reloc = NextReloc)
+		{
+			NextReloc = GetPrev (Reloc);
+			FreeReloc (Reloc);
+		}
+	}
+	
+	FreeItems (SYMBOL,   Symbol);
+	
+#undef FreeItems
+	
+	if (Program->MainSection == Section)
+		Program->MainSection = NULL;
+	if (Program->BSSSection == Section)
+		Program->BSSSection = NULL;
+	if (Program->DataSection == Section)
+		Program->DataSection = NULL;
+	
+	Unlink (Program->Sections, Section);
+	free (Section);
+}
+
+// Free a relocation entry.
+void FreeReloc (RELOC *Reloc)
+{
+	SECTION *Section = Reloc->Parent;
+	Unlink (Section->Relocs, Reloc);
+	// Need to free the symbol name, if it is not a reference to Symbol->Name.
+	FreeLocationSymbolName (Section, &(Reloc->Target));
+	// Need to free the relation if it exists.
+	FreeRelocRelation (Reloc);
+	free (Reloc);
+}
+
+// Free the relation reference of a relocation entry, and set it to
+// NULL.
+void FreeRelocRelation (RELOC *Reloc)
+{
+	LOCATION *Relation = Reloc->Relation;
+	SECTION *Section = Reloc->Parent;
+	Reloc->Relation = NULL;
+	if (Relation && (Relation != &(Section->Parent->EntryPoint)))
+	{
+		FreeLocationSymbolName (Section, Relation);
+		free (Relation);
+	}
+}
+
+// Free the symbol name of a location, if this is necessary.
+// If a symbol is already known, set the symbol name to its name.
+// Decrease the number of unresolved relocs in the section.
+void FreeLocationSymbolName (SECTION *Section, LOCATION *Location)
+{
+	if (((!(Location->Symbol)) || (Location->SymbolName != Location->Symbol->Name)) && Location->SymbolName && (Location != &(Section->Parent->EntryPoint)))
+	{
+		if (--Section->Relocs.UnresolvedCount < 0)
+			Warning (Section->FileName, "Internal unresolved count mismatch.");
+		free ((char *) Location->SymbolName);
+	}
+	Location->SymbolName = (Location->Symbol ? Location->Symbol->Name : NULL);
+}
+
+// Free a section if it is no longer referenced. Update the ReferencedLibCount
+// accordingly.
+BOOLEAN RemoveSectionIfUnused (SECTION *Section)
+{
+	PROGRAM *Program = Section->Parent;
+	SECTION *OtherSection;
+	LIB_CALL *LibCall, *OtherSecLibCall;
+	
+	// Don't free the section if it is still referenced.
+	if (Section->Referenced)
+		return FALSE;
+
+	// If this section references any libraries, and if it was the last one to
+	// reference them, we need to mark the library as no longer referenced.
+	for_each (LibCall, Section->LibCalls)
+	{
+		LIBRARY *Library = LibCall->Library;
+		if (Library->Referenced)
+		{
+			for_each (OtherSection, Program->Sections)
+			{
+				// Not this section!
+				if (OtherSection == Section) continue;
+				for_each (OtherSecLibCall, OtherSection->LibCalls)
+				{
+					// If this library is still referenced, forget it.
+					if (OtherSecLibCall->Library == Library) goto NextLibCall;
+				}
+			}
+			// The library is no longer referenced after this section is removed.
+			Library->Referenced = FALSE;
+			Program->Libraries.ReferencedCount--;
+		}
+NextLibCall:;
+	}
+
+	// Now free the section.
+	FreeSection (Section);
+	
+	return TRUE;
+}
+
+// Create a section symbol for the given section, if none has been
+// created yet. If there already is one, set its name accordingly.
+// Return the section symbol.
+SYMBOL *CreateSectionSymbol (SECTION *Section, const char *SectionName)
+{
+	char *SymName;
+	
+	// Create a new symbol.
+	SYMBOL *Symbol = (Section->SectionSymbol ? : calloc (1, sizeof (SYMBOL)));
+	
+	if (!Symbol)
+	{
+		Error (Section->FileName, "Out of memory.");
+		return NULL;
+	}
+	
+	// Set the basic attributes of the symbol.
+	Symbol->Parent = Section;
+	// Insert the section's file name in front of the section name.
+	if (Section->FileName)
+	{
+		strncpy (Symbol->Name, Section->FileName, MAX_SYM_LEN);
+		SymName = Symbol->Name + strlen (Symbol->Name);
+		if (SectionName && (SymName < Symbol->Name + MAX_SYM_LEN))
+			*(SymName++) = ' ';
+	}
+	else
+		SymName = Symbol->Name;
+	// Insert the section name.
+	if (SectionName)
+		strncpy (SymName, SectionName, Symbol->Name + MAX_SYM_LEN - SymName);
+	
+	if (!(Section->SectionSymbol))
+	{
+		// Insert the symbol at the beginning of the linked list.
+		Push (Section->Symbols, Symbol);
+		// Mark the symbol as the section's beginning symbol.
+		Section->SectionSymbol = Symbol;
+	}
+	
+	return Symbol;
+}
+
+// Create a segment for the entire given section, if none has been
+// created yet. If the section already contains segments, return
+// NULL.
+SEGMENT *CreateSectionSegment (SECTION *Section)
+{
+	if ((IsEmpty (Section->Segments)) && (Section->Size > 0))
+	{
+		SEGMENT *Segment = calloc (1, sizeof (SEGMENT));
+		if (!Segment)
+		{
+			Error (Section->FileName, "Out of memory.");
+			return NULL;
+		}
+		
+		if (!(CreateSectionMarkers (&(Segment->Location), Section)))
+		{
+			free (Segment);
+			return NULL;
+		}
+		
+		Segment->Code         = Section->Code;
+		Segment->CanCutRanges = Section->CanCutRanges;
+		Segment->FileName     = Section->FileName;
+		
+		Push (Section->Segments, Segment);
+		
+		return Segment;
+	}
+	
+	return NULL;
+}
+
+// If the section is a startup section, insert it at the place where
+// it belongs. Otherwise, append it to the end of the program.
+void InsertSection (PROGRAM *Program, SECTION *Section)
+{
+	// Check if it is a startup section.
+	if (Section->StartupNumber)
+	{
+		// Find the location where we have to insert the
+		// section.
+		SECTION *PrevStartupSection = FindStartupSection (Program, Section->StartupNumber);
+		
+		// If the entry point is already fixed, and this will insert the
+		// section in front of the entry point symbol, the program will
+		// probably fail.
+		if (Program->EntryPoint.Symbol && (PrevStartupSection == GetFirst (Program->Sections)))
+			Warning (Section->FileName, "Inserting startup section with number %ld in front of entry point.", (long) Section->StartupNumber);
+		
+		// Cannot remove startup sections.
+		Section->Referenced = TRUE;
+		
+		// Insert the section where it belongs.
+		InsertAfter (Program->Sections, Section, PrevStartupSection);
+	}
+	// If it is not a startup section, append it at the end.
+	else
+		Append (Program->Sections, Section);
+}
+
+// Find the last startup section below or at StartupNumber.
+// If there is no such section, the function returns NULL.
+SECTION *FindStartupSection (const PROGRAM *Program, OFFSET StartupNumber)
+{
+	SECTION *Section;
+	
+	// For each section...
+	for_each (Section, Program->Sections)
+	{
+		// If it is not a startup section, or the startup number is higher,
+		// the previous section was the one (may be NULL).
+		if ((!(Section->StartupNumber)) || (Section->StartupNumber > StartupNumber))
+			return (GetPrev (Section));
+	}
+	
+	// After all the startup sections that were below the number, the
+	// list ended. Or maybe there are no sections at all. In any case,
+	// this is the correct thing to do.
+	return (GetLast (Program->Sections));
+}
+
+// Insert the symbol at the correct place in the section.
+void InsertSymbol (SECTION *Section, SYMBOL *Symbol)
+{
+	const SYMBOL *NextSymbol = FindSymbolAtPos (Section, Symbol->Location, TRUE);
+	InsertBefore (Section->Symbols, Symbol, NextSymbol);
+}
+
+// Increase the counters for references between the two sections, if the sections
+// are different.
+void IncRefCounts (SECTION *SourceSection, SECTION *TargetSection, const RELOC *Reloc)
+{
+	if (SourceSection != TargetSection)
+	{
+		if (Reloc->Relative && (!(Reloc->Relation)))
+		{
+			SourceSection->Relocs.RelativeRefs = TRUE;
+			TargetSection->Relocs.RelativeRefs = TRUE;
+		}
+		else if (M68kIsRelocOptimizable (Reloc))
+		{
+			SourceSection->Relocs.OptRefCount++;
+			TargetSection->Relocs.OptRefCount++;
+		}
+		if (SourceSection->StartupNumber || TargetSection->StartupNumber)
+		{
+			SourceSection->Relocs.StartupRefs = TRUE;
+			TargetSection->Relocs.StartupRefs = TRUE;
+		}
+	}
+}
+
+// Insert the reloc at the correct place in the section, and update the appropriate
+// counters. Also possibly mark the target section as referenced.
+void InsertReloc (SECTION *Section, RELOC *Reloc)
+{
+	const RELOC *NextReloc = FindRelocAtPos (Section, Reloc->Location, TRUE);
+	InsertBefore (Section->Relocs, Reloc, NextReloc);
+	HandleLocation (Reloc, &(Reloc->Target));
+	HandleLocation (Reloc, Reloc->Relation);
+	{
+		SYMBOL *TargetSymbol = Reloc->Target.Symbol;
+		if (TargetSymbol)
+			IncRefCounts (Section, TargetSymbol->Parent, Reloc);
+	}
+}
+
+// Find the item preceding or following the given location. May return NULL.
+#define DefineFindItemAtPos(Type,Item,LocationSuffix) \
+Type *Find##Item##AtPos (const SECTION *Section, OFFSET Location, BOOLEAN Following) \
+{ \
+	/* Find the place so that Item->Location is always less than Location */ \
+	/* and Next##Item->Location is always greater or equal. */ \
+	Type *Item = NULL, *Next##Item = NULL; \
+	if (Location > Section->Size >> 1) \
+	{ \
+		Item = GetLast (Section->Item##s); \
+		while (Item && (Item->Location LocationSuffix >= Location)) \
+		{ \
+			Next##Item = Item; \
+			Item = GetPrev (Item); \
+		} \
+	} \
+	else \
+	{ \
+		Next##Item = GetFirst (Section->Item##s); \
+		while (Next##Item && (Next##Item->Location LocationSuffix < Location)) \
+		{ \
+			Item = Next##Item; \
+			Next##Item = GetNext (Next##Item); \
+		} \
+	} \
+	/* If we are looking for something that follows, the case is clear. */ \
+	/* Otherwise, we need to check whether the next item is also OK. */ \
+	if (Following || (Next##Item && (Next##Item->Location LocationSuffix <= Location))) \
+		return Next##Item; \
+	else \
+		return Item; \
+}
+
+DefineFindItemAtPos (SYMBOL,   Symbol,  );
+DefineFindItemAtPos (RELOC,    Reloc,   );
+DefineFindItemAtPos (ROM_CALL, ROMCall, );
+DefineFindItemAtPos (RAM_CALL, RAMCall, );
+DefineFindItemAtPos (LIB_CALL, LibCall, );
+DefineFindItemAtPos (SEGMENT,  Segment, .Start->Location);
+
+#undef DefineFindItemAtPos
+
+// Get the file name where the code at a given location in the
+// section came from. May return NULL.
+const char *GetFileName (const SECTION *Section, OFFSET Location)
+{
+	const SEGMENT *Segment = FindSegmentAtPos (Section, Location, FALSE);
+	
+	// Check whether the location is inside the segment.
+	// It can be at the beginning, but not at the very end.
+	if (Segment && (Location < Segment->Location.End->Location) && Segment->FileName)
+		// Return the segment's file name.
+		return Segment->FileName;
+	
+	// If no segment was found, return the section's file name.
+	return Section->FileName;
+}
+
+// Return the offset of the specified location into the section. If the
+// offset is unknown, emit a warning and return 0.
+OFFSET GetLocationOffset (const SECTION *Section, const LOCATION *Location)
+{
+	if (Location && Location->Symbol && (Location->Symbol->Parent == Section))
+		return Location->Symbol->Location + Location->Offset;
+	else
+	{
+		Warning (Section->FileName, "Symbol `%s' is not in section `%s'; using arbitrary location.", Location->SymbolName ? : "(unknown)", Section->SectionSymbol->Name);
+		return 0;
+	}
+}
+
+// Increase the counters and reference information necessary when this
+// location is used in the specified reloc.
+void HandleLocation (RELOC *Reloc, LOCATION *Location)
+{
+	if (Location)
+	{
+		SECTION *Section = Reloc->Parent;
+		if (Location->Symbol)
+		{
+			SECTION *RelationSection = Location->Symbol->Parent;
+			if (RelationSection != Section)
+				RelationSection->Referenced = TRUE;
+		}
+		else
+		{
+			Section->Relocs.UnresolvedCount++;
+		}
+	}
+}
+
+// Point the location to the appropriate symbol, if one is found.
+SYMBOL *ResolveLocation (PROGRAM *Program, SECTION *Section, LOCATION *Location)
+{
+	if (Location->Symbol)
+		return Location->Symbol;
+	else
+	{
+		SECTION *CurSection;
+		
+		// For each section...
+		for_each (CurSection, Program->Sections)
+		{
+			SYMBOL *CurSymbol;
+			
+			// For each symbol...
+			for_each (CurSymbol, CurSection->Symbols)
+			{
+				// If the name matches, we have found the right symbol.
+				if (CurSymbol->Exported && (!(strcmp (Location->SymbolName, CurSymbol->Name))))
+				{
+					// Set up the reloc accordingly, freeing its
+					// destination string.
+					Location->Symbol = CurSymbol;
+					FreeLocationSymbolName (Section, Location);
+					if (CurSection != Section)
+						CurSection->Referenced = TRUE;
+					return CurSymbol;
+				}
+			}
+		}
+		
+		return NULL;
+	}
+}
+
+// If the reloc's target symbol name identifies a ROM, RAM,
+// or library call, replace it with the appropriate item.
+// This must be called in order, as the items are appended
+// unconditionally.
+BOOLEAN ResolveSpecialExternalSymbolReloc (RELOC *Reloc)
+{
+	SECTION *Section = Reloc->Parent;
+	PROGRAM *Program = Section->Parent;
+	
+	const char *CurFileName = NULL;
+	
+	// Translate the reloc's symbol name.
+	void *SymRef = NULL;
+	OFFSET SymNum = 0;
+	SpecialExternalSymbolTypes SymType = TranslateSpecialExternalSymbol (Program, (char *) (Reloc->Target.SymbolName), &SymRef, &SymNum);
+	
+	if (SymType != ST_NORMAL)
+		CurFileName = GetFileName (Section, Reloc->Location);
+	
+	// Replace the reloc with the another item.
+	switch (SymType)
+	{
+		case ST_ROM_CALL:
+			{
+				// Create a new ROM call.
+				ROM_CALL *ROMCall = calloc (1, sizeof (ROM_CALL));
+				if (!ROMCall)
+				{
+					Error (CurFileName, "Out of memory while resolving relocs.");
+					return FALSE;
+				}
+				// Assign the attributes of the reloc to the ROM call.
+				ROMCall->Parent      = Section;
+				ROMCall->Location    = Reloc->Location;
+				ROMCall->Number      = SymNum;
+				ROMCall->Size        = Reloc->Size;
+				ROMCall->FixedOffset = Reloc->Target.Offset + Reloc->FixedOffset;
+				if (Reloc->Relative)
+					Warning (CurFileName, "ROM calls cannot be relative; changing to absolute.");
+				// Remove the reloc.
+				FreeReloc (Reloc);
+				// Add the ROM call.
+				Append (Section->ROMCalls, ROMCall);
+				// Adjust ROM call statistics.
+				if (Program->HighestROMCall < SymNum)
+					Program->HighestROMCall = SymNum;
+				Section->ROMCalls.Handled = FALSE;
+			}
+			return TRUE;
+		
+		case ST_RAM_CALL:
+		case ST_EXTRA_RAM:
+			{
+				// Create a new ROM call.
+				RAM_CALL *RAMCall = calloc (1, sizeof (RAM_CALL));
+				if (!RAMCall)
+				{
+					Error (CurFileName, "Out of memory while resolving relocs.");
+					return FALSE;
+				}
+				// Assign the attributes of the reloc to the ROM call.
+				RAMCall->Parent       = Section;
+				RAMCall->Location     = Reloc->Location;
+				RAMCall->Number       = SymNum;
+				RAMCall->Size         = Reloc->Size;
+				RAMCall->FixedOffset  = Reloc->Target.Offset + Reloc->FixedOffset;
+				RAMCall->ExtraRAMAddr = (SymType == ST_EXTRA_RAM);
+				if (Reloc->Relative)
+					Warning (CurFileName, "RAM calls cannot be relative; changing to absolute.");
+				// Remove the reloc.
+				FreeReloc (Reloc);
+				// Add the RAM call.
+				Append (Section->RAMCalls, RAMCall);
+				// Adjust RAM call statistics.
+				if (Program->HighestRAMCall < SymNum)
+					Program->HighestRAMCall = SymNum;
+				Section->RAMCalls.Handled = FALSE;
+			}
+			return TRUE;
+		
+		case ST_LIB_CALL:
+			{
+				// Create a new library call.
+				LIB_CALL *LibCall = calloc (1, sizeof (LIB_CALL));
+				if (!LibCall)
+				{
+					Error (CurFileName, "Out of memory while resolving relocs.");
+					return FALSE;
+				}
+				// Assign the attributes of the reloc to the library call.
+				LibCall->Parent      = Section;
+				LibCall->Location    = Reloc->Location;
+				LibCall->Library     = SymRef;
+				LibCall->Number      = SymNum;
+				LibCall->Size        = Reloc->Size;
+				LibCall->FixedOffset = Reloc->Target.Offset + Reloc->FixedOffset;
+				if (Reloc->Relative)
+					Warning (CurFileName, "Library calls cannot be relative; changing to absolute.");
+				// Remove the reloc.
+				FreeReloc (Reloc);
+				// Add the library call.
+				Append (Section->LibCalls, LibCall);
+				if (LibCall->Library->Highest < SymNum)
+					LibCall->Library->Highest = SymNum;
+				if (!(LibCall->Library->Referenced))
+				{
+					LibCall->Library->Referenced = TRUE;
+					Program->Libraries.ReferencedCount++;
+				}
+				Section->LibCalls.Handled = FALSE;
+			}
+			return TRUE;
+		
+		default:
+			return FALSE;
+	}
+}
+
+// Resolve the reloc's target, if possible.
+// Force: Fail on unresolvable reference.
+BOOLEAN ResolveRelocTarget (RELOC *Reloc, BOOLEAN Force)
+{
+	SECTION *Section = Reloc->Parent;
+	PROGRAM *Program = Section->Parent;
+	
+	// If the target needs to be resolved, then try to do this.
+	if (!(Reloc->Target.Symbol))
+	{
+		COUNT LoopCount = 0;
+		
+		do {
+			BOOLEAN TryAgain = FALSE;
+			SYMBOL *Symbol;
+			
+			// Resolve it as a reloc to a symbol.
+			if ((Symbol = ResolveLocation (Program, Section, &(Reloc->Target))))
+			{
+				IncRefCounts (Section, Symbol->Parent, Reloc);
+				return TRUE;
+			}
+			// Resolve it as a ROM/RAM/library call.
+			else if (ResolveSpecialExternalSymbolReloc (Reloc))
+				return TRUE;
+			// Resolve it as a special ld-exported symbol reloc.
+			else if (ResolveSpecialSymbolRelocTarget (Reloc, &TryAgain))
+			{
+				if (!TryAgain)
+					return TRUE;
+			}
+			// Import the symbol from an archive.
+			else if ((!(Program->Frozen)) && (ImportSymbolFromArchive (Program, Reloc->Target.SymbolName)))
+				// Continue to loop.
+				;
+			// It seems the reloc is currently unresolvable.
+			else if (Force)
+			{
+				Error (GetFileName (Section, Reloc->Location), "Unresolved reference to `%s'.", Reloc->Target.SymbolName);
+				return FALSE;
+			}
+			
+			// Increase loop counter.
+			LoopCount++;
+			// Only re-loop twice. This enables us to use the
+			// "TryAgain" from ResolveSpecialSymbolReloc and
+			// ImportSymbolFromArchive, but prevents us from
+			// entering an infinite loop.
+		} while (LoopCount <= 2);
+		
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+// Resolve the reloc's relation, if possible.
+// Force: Fail on unresolvable reference.
+BOOLEAN ResolveRelocRelation (RELOC *Reloc, BOOLEAN Force)
+{
+	SECTION *Section = Reloc->Parent;
+	PROGRAM *Program = Section->Parent;
+	
+	// If the relation needs to be resolved, then try to do this.
+	if (Reloc->Relation && (!(Reloc->Relation->Symbol)))
+	{
+		COUNT LoopCount = 0;
+		
+		do {
+			BOOLEAN TryAgain = FALSE;
+			
+			// Resolve it as a (negative) reloc to a symbol.
+			if (ResolveLocation (Program, Section, Reloc->Relation))
+				return TRUE;
+			// Resolve it as a special ld-exported symbol reloc.
+			else if (ResolveSpecialSymbolRelocRelation (Reloc, &TryAgain))
+			{
+				if (!TryAgain)
+					return TRUE;
+			}
+			// Import the symbol from an archive.
+			else if ((!(Program->Frozen)) && (ImportSymbolFromArchive (Program, Reloc->Relation->SymbolName)))
+				// Continue to loop.
+				;
+			// It seems the reloc is currently unresolvable.
+			else if (Force)
+			{
+				Error (GetFileName (Section, Reloc->Location), "Unresolved reference to `%s'.", Reloc->Relation->SymbolName);
+				return FALSE;
+			}
+			
+			// Increase loop counter.
+			LoopCount++;
+			// Only re-loop twice. This enables us to use the
+			// "TryAgain" from ResolveSpecialSymbolReloc and
+			// ImportSymbolFromArchive, but prevents us from
+			// entering an infinite loop.
+		} while (LoopCount <= 2);
+		
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+// Resolve the reloc, if possible.
+// Force: Fail on unresolvable references.
+BOOLEAN ResolveReloc (RELOC *Reloc, BOOLEAN Force)
+{
+	// First, resolve the relation. This might add the
+	// resulting value to the reloc's target offset.
+	BOOLEAN Result = ResolveRelocRelation (Reloc, Force);
+	// Then resolve the target.
+	if (ResolveRelocTarget (Reloc, Force))
+		return Result;
+	else
+		return FALSE;
+}
+
+// Resolve all relocs in the program, if possible.
+// Force: Fail on unresolvable references.
+BOOLEAN ResolveRelocs (PROGRAM *Program, BOOLEAN Force)
+{
+	BOOLEAN Result = TRUE;
+	
+	SECTION *Section;
+	
+	// For each section...
+	for_each (Section, Program->Sections)
+	{
+		// Only do something if it contains unresolved references at all.
+		if (Section->Relocs.UnresolvedCount > 0)
+		{
+			RELOC *Reloc, *NextReloc;
+			
+			// For each reloc...
+			for (Reloc = GetFirst (Section->Relocs); Reloc; Reloc = NextReloc)
+			{
+				NextReloc = GetNext (Reloc);
+				
+				// Try to resolve it.
+				if (!(ResolveReloc (Reloc, Force)))
+					Result = FALSE;
+			}
+		}
+	}
+	
+	return Result;
+}
+
+// Optimize the location to have the least possible offset.
+void OptimizeLocation (LOCATION *Location)
+{
+	if (Location)
+	{
+		SYMBOL *OldSymbol = Location->Symbol;
+		
+		// Only proceed if the location points to a real symbol.
+		if (OldSymbol)
+		{
+			// Find the symbol the reloc really points to.
+			SYMBOL *NewSymbol = FindSymbolAtPos (OldSymbol->Parent, OldSymbol->Location, FALSE);
+			
+			// If we found a symbol that is closer, use that one.
+			if (NewSymbol && (NewSymbol->Location != OldSymbol->Location))
+			{
+				Location->Offset += OldSymbol->Location - NewSymbol->Location;
+				Location->Symbol = NewSymbol;
+				Location->SymbolName = NewSymbol->Name;
+			}
+		}
+	}
+}
+
+// Optimize all relocs to have the least possible target and relation offset.
+void OptimizeRelocs (PROGRAM *Program)
+{
+	SECTION *Section;
+	
+	// For each section...
+	for_each (Section, Program->Sections)
+	{
+		RELOC *Reloc;
+		
+		// For each reloc...
+		for_each (Reloc, Section->Relocs)
+		{
+			// Optimize the target.
+			OptimizeLocation (&(Reloc->Target));
+			
+			// Optimize the relation, if any.
+			OptimizeLocation (Reloc->Relation);
+		}
+	}
+}
+
+// Remove all unused sections.
+void RemoveUnusedSections (PROGRAM *Program)
+{
+	SECTION *Section, *NextSection;
+	
+	// For each section...
+	for (Section = GetFirst (Program->Sections); Section; Section = NextSection)
+	{
+		// Get the next section now, since GetNext won't work once the section
+		// has been freed.
+		NextSection = GetNext (Section);
+		
+		// Remove the section if it is unused.
+		RemoveSectionIfUnused (Section);
+	}
+}
+
+// Merge section Src into Dest.
+SECTION *MergeSections (SECTION *Dest, SECTION *Src)
+{
+	PROGRAM *Program = Dest->Parent;
+	SIZE OrigSize, OrigSizePadded = 0;
+	
+	// Merging a section into itself should succeed without doing anything.
+	if (Dest == Src)
+		return Dest;
+	
+	// Handle the insertions at the end of the destination section, assuming
+	// that the source section contains everything we need to output. This
+	// is the best we can do.
+	HandleSectionContents (Dest, Src);
+	
+#ifdef FLASH_OS_SUPPORT
+	// In Flash OS mode, if we are merging the startup and normal parts, pad the
+	// startup part to 32 KB.
+	if ((Program->Type == PT_FLASH_OS) && Dest->StartupNumber && (!(Src->StartupNumber)))
+		OrigSizePadded = 0x8000;
+#endif /* FLASH_OS_SUPPORT */
+	
+	// Fix the code for the two sections.
+	if (Dest->Code)
+		M68kFixCodePreMerge (Dest, Src, OrigSizePadded);
+	
+	// If the destination size is fixed, disable range cutting.
+	if (OrigSizePadded)
+		DisableRangeCutting (Dest);
+	
+	// Create segments covering the entire source and destination sections.
+	CreateSectionSegment (Dest);
+	CreateSectionSegment (Src);
+	
+	// A startup section in a nostub program is against the rules of "nostub",
+	// which means that no data except the raw program is included.
+	if ((Program->Type == PT_NOSTUB) && (Dest->StartupNumber || Src->StartupNumber))
+	{
+		Warning (Dest->FileName, "Program declared as `nostub' contains startup sections.");
+		// Switch to native mode to prevent more warnings, and because it makes sense.
+		Program->Type = PT_NATIVE;
+	}
+	
+	if (Program->Library && (Dest->StartupNumber < 0) && (Src->StartupNumber > 0))
+		Warning (Dest->FileName, "Adding program startup section to library.");
+	
+	// Store the size of the destination section.
+	OrigSize = Dest->Size;
+	
+	// If no size is specified yet, pad the destination section to have an even length.
+	if (!OrigSizePadded)
+		OrigSizePadded = GetPaddedSize (OrigSize, M68K_SECTION_ALIGNMENT);
+	
+	// Check whether the current size exceeds a fixed maximum.
+	{
+		SIZE MaxSize = OrigSizePadded;
+#ifdef FLASH_OS_SUPPORT
+		// In Flash OS mode, the maximum size for the startup part is actually 8 KB less.
+		if ((Program->Type == PT_FLASH_OS) && Dest->StartupNumber && (!(Src->StartupNumber)))
+			MaxSize -= 0x2000;
+#endif /* FLASH_OS_SUPPORT */
+		if (OrigSize > MaxSize)
+			Warning (Dest->FileName, "Section size of %ld bytes is larger than requested size of %ld.", (long) OrigSize, (long) MaxSize);
+	}
+	
+	// Check whether the padded size is really larger than the current size.
+	// If not, this has already generated the warning above.
+	// We check here (not above) because it's safer.
+	if (OrigSizePadded < OrigSize)
+		OrigSizePadded = OrigSize;
+	
+	// Adjust the size of the destination.
+	Dest->Size = OrigSizePadded + Src->Size;
+	// If one of the two sections has data, we need to keep it, so adjust the
+	// other section accordingly. Otherwise, merging is simple.
+	if (Dest->Data || Src->Data)
+	{
+		// Grow (or allocate) the data area of the destination section.
+		if (Dest->Data)
+		{
+			Dest->Data = realloc (Dest->Data, Dest->Size);
+			// Pad with zeroes if necessary.
+			if (Dest->Data && (OrigSizePadded > OrigSize))
+				memset (Dest->Data + OrigSize, 0, OrigSizePadded - OrigSize);
+		}
+		else if (Dest->Size)
+			Dest->Data = calloc (Dest->Size, 1);
+		if (Dest->Data || (!(Dest->Size)))
+		{
+			if (Dest->Data)
+			{
+				// Copy the data.
+				if (Src->Data)
+					memcpy (Dest->Data + OrigSizePadded, Src->Data, Src->Size);
+				else
+					memset (Dest->Data + OrigSizePadded, 0, Src->Size);
+			}
+		}
+		else
+		{
+			Error (Dest->FileName, "Out of memory while merging sections.");
+			return NULL;
+		}
+		Dest->Initialized = TRUE;
+	}
+	else
+	{
+		// Just need to merge attributes.
+		Dest->Initialized |= Src->Initialized;
+	}
+	Dest->Mergeable = Dest->Mergeable && Src->Mergeable;
+	Dest->Unaligned = Dest->Unaligned && Src->Unaligned;
+	Dest->Referenced |= Src->Referenced;
+	Dest->Constructors = Dest->Constructors && Src->Constructors;
+	Dest->Destructors = Dest->Destructors && Src->Destructors;
+	Dest->CanCutRanges |= Src->CanCutRanges;
+	Dest->Frozen |= Src->Frozen;
+	
+	// Enable code optimizations if they are enabled for one of the sections.
+	// It will be disabled on a segment basis if necessary.
+	Dest->Code |= Src->Code;
+	
+#define MergeHandling(Items) (Dest->Items.Handled = (Dest->Items.Handled && Src->Items.Handled) || (Dest->Items.Handled && IsEmpty(Src->Items)) || (Src->Items.Handled && IsEmpty(Dest->Items)))
+	
+	MergeHandling (ROMCalls);
+	MergeHandling (RAMCalls);
+	MergeHandling (LibCalls);
+	
+#undef MergeHandling
+	
+	// Define a macro to make merging items more simple.
+#define MergeItems(Type,Item) \
+({ \
+	Type *Item, *Next##Item; \
+	for (Item = GetFirst (Src->Item##s); Item; Item = Next##Item) \
+	{ \
+		Next##Item = GetNext (Item); \
+		Unlink (Src->Item##s, Item); \
+		Item->Location += OrigSizePadded; \
+		Append (Dest->Item##s, Item); \
+		Item->Parent = Dest; \
+	} \
+})
+	
+	// Merge everything, except segments.
+	MergeItems (SYMBOL,   Symbol);
+	MergeItems (RELOC,    Reloc);
+	MergeItems (ROM_CALL, ROMCall);
+	MergeItems (RAM_CALL, RAMCall);
+	MergeItems (LIB_CALL, LibCall);
+	
+#undef MergeItems
+	
+	// Merge segments.
+	{
+		SEGMENT *Segment, *NextSegment;
+		for (Segment = GetFirst (Src->Segments); Segment; Segment = NextSegment)
+		{
+			NextSegment = GetNext (Segment);
+			Unlink (Src->Segments,  Segment);
+			Append (Dest->Segments, Segment);
+		}
+	}
+	
+	Dest->Relocs.UnresolvedCount += Src->Relocs.UnresolvedCount;
+	Dest->Relocs.EmittedCount    += Src->Relocs.EmittedCount;
+	
+	// Free the source section.
+	FreeSection (Src);
+	
+	return Dest;
+}
+
+// Merge all sections of the specified type.
+SECTION *MergeAllSections (PROGRAM *Program, SECTION *Dest, BOOLEAN AcceptInitialized, BOOLEAN AcceptUninitialized, BOOLEAN AcceptZeroes, BOOLEAN AcceptContents, BOOLEAN AcceptData, BOOLEAN AcceptCode, BOOLEAN AcceptNonConstructors, BOOLEAN AcceptConstructors, BOOLEAN AcceptDestructors, BOOLEAN AcceptNonStartup, BOOLEAN AcceptStartup)
+{
+	BOOLEAN MergeForward = FALSE;
+	SECTION *CurMergedSection = NULL, *Section, *NextSection;
+	
+	// For each section...
+	for (Section = GetLast (Program->Sections); Section; Section = NextSection)
+	{
+		// Get the next section now, since GetNext won't work once the section
+		// has been freed.
+		NextSection = GetPrev (Section);
+		
+		// Check if the section meets the specified requirements.
+		if ((!(Section->Handled))
+			&& (AcceptInitialized     || (!(Section->Initialized)))
+			&& (AcceptUninitialized   || Section->Initialized)
+			&& (AcceptZeroes          || Section->Data || (!(Section->Initialized)) || Section->StartupNumber)
+			&& (AcceptContents        || (!(Section->Data)) || (!(Section->Initialized)))
+			&& (AcceptData            || Section->Code)
+			&& (AcceptCode            || (!(Section->Code)))
+			&& (AcceptNonConstructors || Section->Constructors || Section->Destructors)
+			&& (AcceptConstructors    || (!(Section->Constructors)))
+			&& (AcceptDestructors     || (!(Section->Destructors)))
+			&& (AcceptNonStartup      || Section->StartupNumber)
+			&& (AcceptStartup         || (!(Section->StartupNumber))))
+		{
+			// If no current merged section has been specified, make this
+			// one current. Otherwise, merge the current merged section
+			// into this one.
+			if (CurMergedSection)
+			{
+				if (MergeForward)
+					CurMergedSection = MergeSections (CurMergedSection, Section);
+				else
+					CurMergedSection = MergeSections (Section, CurMergedSection);
+				
+				if (!CurMergedSection)
+					return NULL;
+			}
+			else
+			{
+				CurMergedSection = Section;
+				if (!MergeForward)
+					M68kFixCode (Section);
+			}
+			
+			// If an explicit destination section exists, and this section
+			// is the destination, set a flag to append all following
+			// sections at the end.
+			if (CurMergedSection == Dest)
+				MergeForward = TRUE;
+		}
+	}
+	
+	if (CurMergedSection)
+	{
+		M68kFixCode (CurMergedSection);
+		HandleSectionContents (CurMergedSection, CurMergedSection);
+	}
+	
+	return CurMergedSection;
+}
+
+// Get the size that would result from padding a section of size
+// OrigSize to a multiple of Alignment.
+SIZE GetPaddedSize (SIZE OrigSize, COUNT Alignment)
+{
+	return (((OrigSize + Alignment - 1) / Alignment) * Alignment);
+}
+
+// Pad the section so its size becomes a multiple of Alignment.
+BOOLEAN PadSection (SECTION *Section, COUNT Alignment)
+{
+	SIZE OrigSize = Section->Size;
+	SIZE NewSize = GetPaddedSize (OrigSize, Alignment);
+	
+	if (NewSize > OrigSize)
+	{
+		// Increase the size of the section data.
+		if (Section->Data)
+		{
+			I1 *NewData = realloc (Section->Data, NewSize);
+			if (NewData)
+			{
+				memset (NewData + OrigSize, 0, NewSize - OrigSize);
+				Section->Data = NewData;
+			}
+			else
+			{
+				Error (NULL, "Out of memory.");
+				return FALSE;
+			}
+		}
+		
+		// Increase the size field.
+		Section->Size = NewSize;
+	}
+	
+	return TRUE;
+}
+
+// Try to resolve and remove relative relocs.
+BOOLEAN FixupRelativeRelocs (PROGRAM *Program)
+{
+	BOOLEAN Result = TRUE;
+	SECTION *Section;
+	
+	// For each section...
+	for_each (Section, Program->Sections)
+	{
+		// If it contains real data...
+		if (Section->Data)
+		{
+			RELOC *Reloc, *NextReloc;
+			
+			// For each reloc in the section...
+			for (Reloc = GetFirst (Section->Relocs); Reloc; Reloc = NextReloc)
+			{
+				// Get the next reloc now because we might remove this one.
+				NextReloc = GetNext (Reloc);
+				
+				if (Reloc->Relative)
+				{
+					// Try to resolve and remove this reloc.
+					if (!FixupRelativeReloc (Reloc))
+						Result = FALSE;
+				}
+			}
+		}
+	}
+	
+	return Result;
+}
+
+// Try to resolve and remove a single relative reloc.
+// This may cause the reloc to be freed.
+BOOLEAN FixupRelativeReloc (RELOC *Reloc)
+{
+	BOOLEAN Result = FALSE;
+	SECTION *Section = Reloc->Parent, *RelationSection;
+	const char *CurFileName = GetFileName (Section, Reloc->Location);
+	
+	if (Reloc->Relation && Reloc->Relation->Symbol)
+		RelationSection = Reloc->Relation->Symbol->Parent;
+	else
+		RelationSection = Section;
+	
+	// Check if we can resolve the reloc.
+	if (Reloc->Relative && Reloc->Target.Symbol && ((!(Reloc->Relation)) || Reloc->Relation->Symbol))
+	{
+		if (Reloc->Target.Symbol->Parent == RelationSection)
+		{
+			// Now there are hidden relocs in the section.
+			DisableRangeCutting (Section);
+			
+			if ((Reloc->Location >= 0) && (Reloc->Location + Reloc->Size <= Section->Size))
+			{
+				OFFSET Relation;
+				
+				// Check if the section contents at the reloc are zero.
+				if (!(IsZeroDataRange (Section, Reloc->Location, Reloc->Location + Reloc->Size)))
+					Warning (CurFileName, "Reloc at 0x%lX to `%s' on nonzero section contents. Overlapping with another?", (long) Reloc->Location, Reloc->Target.SymbolName);
+				
+				// Get the relative position of the target symbol (plus offset).
+				Relation = GetLocationOffset (RelationSection, &(Reloc->Target)) + Reloc->FixedOffset;
+				if (Reloc->Relation)
+					Relation -= GetLocationOffset (RelationSection, Reloc->Relation);
+				else
+					Relation -= Reloc->Location;
+				
+				{
+					// Try to tack the difference onto another reloc in order to avoid a "reloc on nonzero section contents" warning:
+					RELOC *AnotherReloc = FindCompatibleReloc (Reloc, TRUE);
+					if (AnotherReloc)
+					{
+						// Resolve the reloc by writing the address difference into the
+						// other reloc's fixed offset.
+						AnotherReloc->FixedOffset += Relation;
+						Result = TRUE;
+					}
+					else
+					{
+						// Resolve the reloc by writing the relative position into the section.
+						Result = AddTI (Section->Data + Reloc->Location, Reloc->Size, Relation, TRUE, Reloc->Relation != NULL);
+						
+						if (!Result)
+							Error (CurFileName, "Symbol `%s' too far for size %ld reloc at 0x%lX.", Reloc->Target.SymbolName, (long) Reloc->Size, (long) Reloc->Location);
+					}
+				}
+			}
+			else
+			{
+				Warning (CurFileName, "Removing reloc at 0x%lX to `%s' outside of section.", (long) Reloc->Location, Reloc->Target.SymbolName);
+				Result = TRUE;
+			}
+			
+			if (Result)
+				FreeReloc (Reloc);
+		}
+		else
+			Error (CurFileName, "Relative reloc at 0x%lX to `%s' in different section.", (long) Reloc->Location, Reloc->Target.SymbolName);
+	}
+	else
+		Error (CurFileName, "Cannot resolve reloc at 0x%lX to `%s'.", (long) Reloc->Location, Reloc->Target.SymbolName);
+	
+	return Result;
+}
+
+// Find a reloc with the same location and size as this one.
+RELOC *FindCompatibleReloc (const RELOC *Reloc, BOOLEAN AllowRelative)
+{
+	return FindMatchingReloc (Reloc->Parent, Reloc->Location, Reloc->Size, AllowRelative, Reloc, Reloc);
+}
+
+// Find a reloc which matches the specified criteria.
+RELOC *FindMatchingReloc (const SECTION *Section, OFFSET Location, SIZE Size, BOOLEAN AllowRelative, const void *Exception, const RELOC *Hint)
+{
+	if (Hint && (Hint->Parent != Section))
+		Hint = NULL;
+	
+	// Find the first reloc at the position.
+	while (Hint && (Hint->Location >= Location))
+		Hint = GetPrev (Hint);
+	while (Hint && (Hint->Location < Location))
+		Hint = GetNext (Hint);
+	if (!Hint)
+		Hint = FindRelocAtPos (Section, Location, TRUE);
+	
+	// For each reloc that still has the right location...
+	while (Hint && (Hint->Location == Location))
+	{
+		// Check whether it matches the criteria.
+		if ((Hint->Size == Size)
+		 && (AllowRelative || (!(Hint->Relative)))
+		 && (Hint != Exception))
+			return (RELOC *) Hint;
+		
+		Hint = GetNext (Hint);
+	}
+	
+	return NULL;
+}
+
+// Make a reloc relative, and set its relation to the beginning of
+// the section plus Offset. If Section is NULL, the program entry
+// point is used as the relation.
+BOOLEAN SetRelocRelation (RELOC *Reloc, SECTION *Section, OFFSET Offset)
+{
+	// Make this a relative reloc.
+	Reloc->Relative = TRUE;
+	// If it already has a relation, free it.
+	FreeRelocRelation (Reloc);
+	// Set the relation to the section's beginning.
+	if (Section)
+	{
+		if ((Reloc->Relation = calloc (1, sizeof (LOCATION))))
+		{
+			Reloc->Relation->Symbol = Section->SectionSymbol;
+			Reloc->Relation->SymbolName = Section->SectionSymbol->Name;
+			Reloc->Relation->Offset = Offset;
+		}
+		else
+		{
+			Error (NULL, "Out of memory.");
+			return FALSE;
+		}
+	}
+	else
+	{
+		PROGRAM *Program = Reloc->Parent->Parent;
+		// If Section is NULL, use the program's entry point, and
+		// adjust the fixed offset instead of the relation's offset.
+		Reloc->Relation = &(Program->EntryPoint);
+		Reloc->FixedOffset -= Offset;
+	}
+	return TRUE;
+}
+
+// Make a reloc relative to the program entry point.
+BOOLEAN SetRelocProgramRelative (RELOC *Reloc)
+{
+	return SetRelocRelation (Reloc, NULL, 0);
+}
+
+// Find or create a common symbol with specified name and size.
+SYMBOL *MakeCommonSymbol (PROGRAM *Program, const char *SymName, SIZE Size, BOOLEAN Initialize, const char *FileName)
+{
+	// We need to search for a BSS section with a symbol with the same name,
+	// and grow this section if necessary. If not found, we create a new
+	// small BSS section.
+	SECTION *CurSection;
+	// For each section...
+	for_each (CurSection, Program->Sections)
+	{
+		// Check only for sections with uninitialized or zeroed data.
+		// Check if it has at least one symbol in it.
+		if ((!(CurSection->Data)) && (!(IsEmpty (CurSection->Symbols))))
+		{
+			SYMBOL *CurSymbol;
+			// For each symbol...
+			for_each (CurSymbol, CurSection->Symbols)
+			{
+				// Check if the symbol name matches.
+				if (CurSymbol->Exported && (!(strcmp (CurSymbol->Name, SymName))))
+				{
+					// Yes, we have found our symbol.
+					// Check if the size is sufficient.
+					const SYMBOL *NextSymbol = GetNext (CurSymbol);
+					// If there is no symbol that follows this one, we
+					// assume the symbol takes up all the space from its
+					// label to the end of the section.
+					OFFSET SymEnd = NextSymbol ? NextSymbol->Location : CurSection->Size;
+					// Check if the size is sufficient.
+					if (SymEnd - CurSymbol->Location >= Size)
+						// We were lucky; we can use this symbol directly.
+						return CurSymbol;
+					else
+					{
+						// We need to grow the BSS section. However, we
+						// should do this only if the symbol is the last
+						// one; otherwise maybe we could adjust all other
+						// symbol locations, but I don't know if that is
+						// safe. Apart from that, sharing a common symbol
+						// with a probably non-common (e.g. A68k) BSS
+						// section is probably not a very good idea
+						// anyway.
+						if (!NextSymbol)
+						{
+							// Grow the section. If the symbol's location
+							// is not zero, we need to add this location
+							// to the section's new size.
+							CurSection->Size = CurSymbol->Location + Size;
+							return CurSymbol;
+						}
+					}
+				}
+			}
+		}
+	}
+	
+	// The symbol doesn't exist yet. Fine, we need to create
+	// a new BSS block of specified size, with one symbol in
+	// it, and refer to this symbol. This is a little bit of
+	// work, but not very difficult.
+	{
+		// Create a new section, initialize it, and append it to the list of sections.
+		SECTION *Section = calloc (1, sizeof (SECTION));
+		if (!Section)
+		{
+			Error (FileName, "Out of memory.");
+			return NULL;
+		}
+		Section->Parent = Program;
+		Section->Initialized = Initialize;
+		Section->Size = Size;
+		Section->FileName = FileName;
+		Append (Program->Sections, Section);
+		
+		{
+			// Create a symbol in the new section.
+			SYMBOL *Symbol = calloc (1, sizeof (SYMBOL));
+			if (!Symbol)
+			{
+				Error (FileName, "Out of memory.");
+				return NULL;
+			}
+			Symbol->Parent = Section;
+			strncpy (Symbol->Name, SymName, MAX_SYM_LEN);
+			Symbol->Exported = TRUE;
+			Append (Section->Symbols, Symbol);
+			
+			// Since the new symbol is at the beginning of the section, it is
+			// sufficient as a section symbol.
+			Section->SectionSymbol = Symbol;
+			
+			// Return the symbol.
+			return Symbol;
+		}
+	}
+}
+
+// Try to find and import a symbol with a given name in some archive.
+BOOLEAN ImportSymbolFromArchive (PROGRAM *Program, const char *SymName)
+{
+	ARCHIVE *Archive;
+	
+	// For each archive...
+	for_each (Archive, Program->Archives)
+	{
+		ARCHIVE_SYMBOL *Symbol;
+		
+		// For each symbol...
+		for_each (Symbol, Archive->Symbols)
+		{
+			// If the name matches...
+			if (!(strcmp (SymName, Symbol->Name)))
+				// Import it.
+				return (ImportArchiveSymbol (Program, Symbol));
+		}
+	}
+	
+	return FALSE;
+}
+
+// Create marker symbols at the beginning and end of the section, and write
+// them to Marker. Returns NULL on error.
+SECTION_MARKERS *CreateSectionMarkers (SECTION_MARKERS *Marker, SECTION *Section)
+{
+	// Check if the parameters are valid.
+	if (Marker && Section)
+	{
+		SYMBOL *Symbol;
+
+		// Mark the section as referenced so it isn't removed.
+		Section->Referenced = TRUE;
+		
+		// Use the section symbol for the beginning.
+		Marker->Start = Section->SectionSymbol;
+		
+		// Create a symbol at the end.
+		Symbol = calloc (1, sizeof (SYMBOL));
+		if (!Symbol)
+		{
+			Error (NULL, "Out of memory.");
+			return NULL;
+		}
+		Symbol->Parent = Section;
+		Symbol->Location = Section->Size;
+		strcpy (Symbol->Name, Section->SectionSymbol->Name);
+		if (strlen (Symbol->Name) + sizeof (" end") - 1 <= MAX_SYM_LEN)
+			strcat (Symbol->Name, " end");
+		Append (Section->Symbols, Symbol);
+		Marker->End = Symbol;
+		
+		return Marker;
+	}
+	else
+		return NULL;
+}
+
+// Get the object file at the specified position in the archive.
+ARCHIVE_OBJECT *GetArchiveObject (ARCHIVE *Archive, FILE_PTR FileOffset)
+{
+	ARCHIVE_OBJECT *Object;
+	
+	// Look whether an object file at this position is already known.
+	for_each (Object, Archive->ObjectFiles)
+	{
+		if (Object->FileOffset == FileOffset)
+			// Yes, so no need to do anything.
+			return Object;
+	}
+	
+	// Allocate a new library.
+	Object = calloc (1, sizeof (ARCHIVE_OBJECT));
+	if (!Object)
+	{
+		Error (NULL, "Out of memory.");
+		return NULL;
+	}
+	
+	// Assign the data.
+	Object->Parent = Archive;
+	Object->FileOffset = FileOffset;
+	
+	// Add the object file.
+	Append (Archive->ObjectFiles, Object);
+	
+	return Object;
+}
+
+// Get a reference to the library identified in the string. The library is
+// added to the program's used libraries if necessary.
+LIBRARY *GetLibrary (PROGRAM *Program, const char *LibName)
+{
+	LIBRARY *Library;
+	
+	// Look whether a library with this name already exists.
+	for_each (Library, Program->Libraries)
+	{
+		if (!(strcmp (Library->Name, LibName)))
+			// Yes, so no need to do anything.
+			return Library;
+	}
+	
+	// Allocate a new library.
+	Library = calloc (1, sizeof (LIBRARY));
+	if (!Library)
+	{
+		Error (NULL, "Out of memory.");
+		return NULL;
+	}
+	
+	// Assign the data.
+	Library->Parent = Program;
+	strncpy (Library->Name, LibName, MAX_SYM_LEN);
+	
+	// Add the library.
+	Append (Program->Libraries, Library);
+	
+	return Library;
+}
+
+// Add a global import. This means that all archive members which export this
+// symbol should be imported (unlike the import done by a reloc, which only
+// imports the first member exporting the symbol).
+GLOBAL_IMPORT *AddGlobalImport (PROGRAM *Program, const char *SymName)
+{
+	GLOBAL_IMPORT *Import = CreateGlobalImport (Program, SymName);
+
+	if (Import)
+		ResolveGlobalImport (Program, Import);
+
+	return Import;
+}
+
+// First part of AddGlobalImport. Add a global import to the list.
+GLOBAL_IMPORT *CreateGlobalImport (PROGRAM *Program, const char *SymName)
+{
+	GLOBAL_IMPORT *Import;
+	
+	// Look whether an import with this name already exists.
+	for_each (Import, Program->GlobalImports)
+	{
+		if (!(strcmp (Import->SymbolName, SymName)))
+			// Yes, so no need to do anything.
+			// Otherwise, we could even end up in infinite recursion.
+			// I don't know whether we should return NULL instead.
+			return Import;
+	}
+	
+	// Allocate a new import.
+	Import = calloc (1, sizeof (GLOBAL_IMPORT));
+	if (!Import)
+	{
+		Error (NULL, "Out of memory.");
+		return NULL;
+	}
+	
+	// Assign the data.
+	Import->Parent = Program;
+	strncpy (Import->SymbolName, SymName, MAX_SYM_LEN);
+	
+	// Add the import.
+	Append (Program->GlobalImports, Import);
+	
+	return Import;
+}
+
+// Second part of AddGlobalImport only. Try to resolve the newly added global
+// import against already available archives.
+void ResolveGlobalImport (PROGRAM *Program, GLOBAL_IMPORT *Import)
+{
+	ARCHIVE *Archive;
+	
+	// For each archive...
+	for_each (Archive, Program->Archives)
+	{
+		ARCHIVE_SYMBOL *Symbol;
+		
+		// For each symbol...
+		for_each (Symbol, Archive->Symbols)
+			// Check if the import and the symbol match.
+			CheckGlobalImport (Import, Symbol);
+	}
+}
+
+// Check whether the symbol matches any global import,
+// performing the appropriate actions if necessary.
+GLOBAL_IMPORT *CheckGlobalImports (PROGRAM *Program, ARCHIVE_SYMBOL *Symbol)
+{
+	GLOBAL_IMPORT *Import;
+	
+	for_each (Import, Program->GlobalImports)
+		if (CheckGlobalImport (Import, Symbol))
+			return Import;
+	
+	return NULL;
+}
+
+// Check whether the symbol matches the specified global import,
+// performing the appropriate actions if necessary.
+BOOLEAN CheckGlobalImport (GLOBAL_IMPORT *Import, ARCHIVE_SYMBOL *Symbol)
+{
+	PROGRAM *Program = Import->Parent;
+	
+	if (Symbol->ContainsInversion && (!(Program->GlobalImports.ResolveInversions)))
+		return FALSE;
+	
+	// If the name matches, import the appropriate file.
+	if (!(strcmp (Symbol->Name, Import->SymbolName)))
+	{
+		Import->Succeeded = TRUE;
+		ImportArchiveSymbol (Program, Symbol);
+		if ((Import == Program->BSSImport) && Program->BSSSection)
+			Program->BSSSection->Handled = TRUE;
+		return TRUE;
+	}
+	// If the symbol contains the name of the global import, it is probably an
+	// ANDed combination with other imports. In that case, import it if all
+	// conditions are met.
+	else if (strstr (Symbol->Name, Import->SymbolName))
+		return CheckMultiConditionSymbol (Program, Symbol);
+	else
+		return FALSE;
+}
+
+// Check whether all conditions are met for an archive symbol with multiple
+// or inverted conditions. If they are, import the appropriate file, and
+// increment the counters of all global imports fulfilling non-inverted
+// conditions.
+BOOLEAN CheckMultiConditionSymbol (PROGRAM *Program, ARCHIVE_SYMBOL *Symbol)
+{
+	BOOLEAN Result = TRUE;
+	BOOLEAN ContainsBSSImport = FALSE;
+	const char *SymName = Symbol->Name;
+	const char *AndOperatorPos;
+	
+	if (Symbol->ContainsInversion && (!(Program->GlobalImports.ResolveInversions)))
+		return FALSE;
+	
+	// Parse the symbol name from left to right, taking care of all operators.
+	do {
+		BOOLEAN Inverted = FALSE;
+		unsigned long SymNameLen;
+		GLOBAL_IMPORT *CurImport;
+		
+		// Check whether condition is inverted.
+		if (!(strncmp (SYMOP_NOT, SymName, sizeof (SYMOP_NOT) - 1)))
+		{
+			if (!(Program->GlobalImports.ResolveInversions))
+			{
+				// This cannot happen actually due to the check at the
+				// beginning, but it makes sense to check it anyway.
+				Symbol->ContainsInversion = TRUE;
+				return FALSE;
+			}
+			Inverted = TRUE;
+			SymName += sizeof (SYMOP_NOT) - 1;
+		}
+		
+		// Search for the next operator.
+		AndOperatorPos = strstr (SymName, SYMOP_AND);
+		if (AndOperatorPos)
+			SymNameLen = AndOperatorPos - SymName;
+		else
+			SymNameLen = strlen (SymName);
+		
+		// Search for a global import which matches this part of the
+		// symbol name.
+		for_each (CurImport, Program->GlobalImports)
+		{
+			if ((!(strncmp (SymName, CurImport->SymbolName, SymNameLen))) && (SymNameLen == strlen (CurImport->SymbolName)))
+			{
+				Inverted = !Inverted;
+				CurImport->Succeeded = TRUE;
+				if (CurImport == Program->BSSImport)
+					ContainsBSSImport = TRUE;
+				break;
+			}
+		}
+		
+		// If the condition is inverted and a symbol is found, or it is
+		// not inverted and no symbol is found, the import failed on this
+		// symbol (at least for now).
+		if (!Inverted)
+			Result = FALSE;
+		
+		// Skip the operator.
+		if (AndOperatorPos)
+			SymName = AndOperatorPos + sizeof (SYMOP_AND) - 1;
+	} while (AndOperatorPos);
+	
+	// All conditions have been met. Import the file.
+	if (Result)
+	{
+		ImportArchiveSymbol (Program, Symbol);
+		if (ContainsBSSImport && Program->BSSSection)
+			Program->BSSSection->Handled = TRUE;
+	}
+	
+	return Result;
+}
+
+// Resolve all remaining global imports. Usually, global imports are
+// processed directly, but if a symbol contains an inverted condition, we
+// have to wait until we really know that no such global import exists.
+void ResolveRemainingGlobalImports (PROGRAM *Program)
+{
+	ARCHIVE *Archive;
+	
+	// All negated symbols which appear now are to be seen as "not succeeded".
+	Program->GlobalImports.ResolveInversions = TRUE;
+	
+	// For each archive...
+	for_each (Archive, Program->Archives)
+	{
+		ARCHIVE_SYMBOL *Symbol;
+		
+		// For each symbol...
+		for_each (Symbol, Archive->Symbols)
+		{
+			// If it contains an inversion, we prevented it from being added
+			// before. Import it now.
+			if (Symbol->ContainsInversion)
+				CheckMultiConditionSymbol (Program, Symbol);
+		}
+	}
+}
+
+// Check whether all global imports have succeeded at least once.
+BOOLEAN CheckAllGlobalImports (PROGRAM *Program)
+{
+	BOOLEAN Result = TRUE;
+	GLOBAL_IMPORT *Import;
+	
+	// For each import...
+	for_each (Import, Program->GlobalImports)
+	{
+		// Check whether it has succeeded.
+		if (!(Import->Succeeded))
+		{
+			Warning (NULL, "Unresolved global import `%s'.", Import->SymbolName);
+			Result = FALSE;
+		}
+	}
+	
+	return Result;
+}
+
+// Find the first segment that overlaps with the specified range.
+SEGMENT *FindSegment (const SECTION *Section, OFFSET Start, OFFSET End)
+{
+	// Find the first segment that affects this range.
+	SEGMENT *Segment = FindSegmentAtPos (Section, Start, FALSE) ? : GetFirst (Section->Segments);
+	
+	// Check whether it really hits the specified range.
+	if (Segment && (RangesOverlap (Segment->Location.Start->Location, Segment->Location.End->Location, Start, End)))
+		return Segment;
+	else
+		return NULL;
+}
+
+// Check whether the specified range is declared to contain code.
+BOOLEAN IsCodeRange (const SECTION *Section, OFFSET Start, OFFSET End)
+{
+	if (Section->Code)
+	{
+		// If no segments are specified, the entire section is one segment.
+		if (IsEmpty (Section->Segments))
+			return TRUE;
+		// Otherwise, find the correct segment and look at it.
+		else
+		{
+			const SEGMENT *Segment = FindSegment (Section, Start, End);
+			return (Segment && Segment->Code);
+		}
+	}
+	
+	return FALSE;
+}
+
+// Check whether the section data in the specified range is zero. This only
+// checks the actual data, not the relocs, ROM calls, etc.
+BOOLEAN IsZeroDataRange (const SECTION *Section, OFFSET Start, OFFSET End)
+{
+	if (Section->Data)
+	{
+		OFFSET CurOffset;
+		for (CurOffset = Start; CurOffset < End; CurOffset++)
+		{
+			if (!(IsZero (Section->Data [CurOffset])))
+				return FALSE;
+		}
+		return TRUE;
+	}
+	else
+		return Section->Initialized;
+}
+
+// Check whether the specified range contains only binary data, without
+// relocs, ROM calls, or anything similar. The range includes Start, but not
+// End.
+// The Exception parameter specifies an item to skip when searching for
+// possible problems.
+BOOLEAN IsBinaryDataRange (const SECTION *Section, OFFSET Start, OFFSET End, const void *Exception)
+{
+	if ((Start < 0) || (End > Section->Size))
+		return FALSE;
+	{
+		const SYMBOL *Symbol = FindSymbolAtPos (Section, Start, TRUE);
+		while (Symbol && (Symbol->Location < End))
+		{
+			if ((Symbol != Exception) && (Symbol->Location > Start))
+				return FALSE;
+			Symbol = GetNext (Symbol);
+		}
+	}
+	
+	// Define a macro to make checking items more simple.
+#define CheckItems(Type,Item) \
+({ \
+	const Type *Item = Find##Item##AtPos (Section, Start, FALSE) ? : GetFirst (Section->Item##s); \
+	while (Item && (Item->Location < End)) \
+	{ \
+		if ((Item != Exception) && (RangesOverlap (Start, End, Item->Location, Item->Location + Item->Size))) \
+			return FALSE; \
+		Item = GetNext (Item); \
+	} \
+})
+	
+	CheckItems (RELOC,    Reloc);
+	CheckItems (ROM_CALL, ROMCall);
+	CheckItems (RAM_CALL, RAMCall);
+	CheckItems (LIB_CALL, LibCall);
+	
+#undef CheckItems
+	
+	return TRUE;
+}
+
+// Check whether there is a reloc or symbol or something similar which
+// prevents us from optimizing away some code at the end of the section.
+// The Exception parameter specifies an item to skip when searching for
+// possible problems.
+BOOLEAN CanShrinkSection (const SECTION *Section, OFFSET Location, const void *Exception)
+{
+	// If we are not actually shrinking the section, either return value does
+	// not make much sense. We return a negative result to stop the
+	// optimization which is about to be done.
+	if (Location >= Section->Size)
+		return FALSE;
+	else
+	{
+		// Define a macro to make checking items more simple.
+#define CheckItems(Type,Item,LocationSuffix) \
+({ \
+	const Type *Item = Find##Item##AtPos (Section, Location LocationSuffix, TRUE); \
+	while (Item) \
+	{ \
+		if (Item != Exception) \
+			return FALSE; \
+		Item = GetNext (Item); \
+	} \
+})
+		
+		CheckItems (SYMBOL,   Symbol,  + 1);
+		CheckItems (RELOC,    Reloc,   );
+		CheckItems (ROM_CALL, ROMCall, );
+		CheckItems (RAM_CALL, RAMCall, );
+		CheckItems (LIB_CALL, LibCall, );
+		
+#undef CheckItems
+		
+		return TRUE;
+	}
+}
+
+// Cut the section off at the specified location. All items behind this
+// location are removed.
+void CutSection (SECTION *Section, OFFSET Location)
+{
+	if (Section->Size > Location)
+	{
+		Section->Size = Location;
+		if (Section->Data)
+		{
+			I1 *NewData = realloc (Section->Data, Location);
+			if (NewData || (!Location))
+				Section->Data = NewData;
+		}
+		
+		// Define a macro to make removing items more simple.
+#define CutItems(Type,Item,ExtraCode) \
+({ \
+	Type *Item = Find##Item##AtPos (Section, Location, TRUE), *Next##Item; \
+	while (Item) \
+	{ \
+		Next##Item = GetNext (Item); \
+		if (!(ExtraCode)) \
+		{ \
+			Unlink (Section->Item##s, Item); \
+			free (Item); \
+		} \
+		Item = Next##Item; \
+	} \
+})
+		
+		// Do not cut symbols, since they might be referenced.
+		CutItems (RELOC,    Reloc,   ({ FreeReloc (Reloc); TRUE; }));
+		CutItems (ROM_CALL, ROMCall, FALSE);
+		CutItems (RAM_CALL, RAMCall, FALSE);
+		CutItems (LIB_CALL, LibCall, FALSE);
+		
+#undef CutItems
+		
+	}
+}
+
+// Allocate space at the end of the section and return a pointer to it (NULL
+// in case of error, or if Size is 0). The data is initialized to zero.
+I1 *AllocateSpaceInSection (SECTION *Section, SIZE Size)
+{
+	if (Size)
+	{
+		// The old data size specifies how much we need to initialize to 0.
+		SIZE OldSize = Section->Size, OldDataSize = (Section->Data ? OldSize : 0);
+		// Allocate the new space. If the section does not have any data, allocate
+		// data for it.
+		I1 *NewData = realloc (Section->Data, OldSize + Size);
+		// OldSize + Size should never be 0, so if NewData is NULL,
+		// this really means "out of memory", and the original
+		// pointer will still be valid.
+		if (NewData)
+		{
+			Section->Data = NewData;
+			// Set everything to 0.
+			memset (NewData + OldDataSize, 0, OldSize + Size - OldDataSize);
+			Section->Size += Size;
+			return (NewData + OldSize);
+		}
+		else
+			Error (NULL, "Out of memory.");
+	}
+	return NULL;
+}
+
+// Apply general architecture-specific code fixes and optimizations to the
+// program.
+void FixCode (PROGRAM *Program)
+{
+	SIZE OldSize, NewSize; // Used for repeated optimization.
+	
+	// When cutting ranges, do the optimizations in a loop
+	// because optimizing might bring targets into range.
+	do {
+		SECTION *Section;
+		
+		// Initialize the size counts in each iteration of the outer loop.
+		OldSize = 0;
+		NewSize = 0;
+		
+		// For each section...
+		for_each (Section, Program->Sections)
+		{
+			if (Section->Code)
+			{
+				// Count the section size before optimizing it.
+				OldSize += Section->Size;
+				
+				// If it contains code, apply fixes.
+				M68kFixCode (Section);
+				
+				// Count the section size after optimizing it.
+				NewSize += Section->Size;
+			}
+		}
+	} while (Program->OptimizeInfo->CutRanges && (NewSize < OldSize));
+}
+
+// Disable range cutting for an entire section.
+void DisableRangeCutting (SECTION *Section)
+{
+	// Disable range cutting in the entire section.
+	Section->CanCutRanges = FALSE;
+	// Disable range cutting in all segments.
+	{
+		SEGMENT *Segment;
+		for_each (Segment, Section->Segments)
+			Segment->CanCutRanges = FALSE;
+	}
+}

+ 224 - 0
manip.h

@@ -0,0 +1,224 @@
+/* manip.h: Routines to manipulate the internal data
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef MANIP_H
+#define MANIP_H
+
+#include "generic.h"
+#include "data.h"
+
+// Free the program tree.
+void FreeProgram (PROGRAM *Program);
+// Free a section. The section is assumed not to be referenced. Use
+// RemoveSectionIfUnused instead if the section might still be referenced.
+void FreeSection (SECTION *Section);
+// Free a relocation entry.
+void FreeReloc (RELOC *Reloc);
+// Free the relation reference of a relocation entry, and set it to
+// NULL.
+void FreeRelocRelation (RELOC *Reloc);
+// Free the symbol name of a location, if this is necessary.
+// If a symbol is already known, set the symbol name to its name.
+// Decrease the number of unresolved relocs in the section.
+void FreeLocationSymbolName (SECTION *Section, LOCATION *Location);
+
+// Free a section if it is no longer referenced. Update the ReferencedLibCount
+// accordingly.
+BOOLEAN RemoveSectionIfUnused (SECTION *Section);
+
+// Create a section symbol for the given section, if none has been
+// created yet. If there already is one, set its name accordingly.
+// Return the section symbol.
+SYMBOL *CreateSectionSymbol (SECTION *Section, const char *SectionName);
+
+// Create a segment for the entire given section, if none has been
+// created yet. If the section already contains segments, return
+// NULL.
+SEGMENT *CreateSectionSegment (SECTION *Section);
+
+// If the section is a startup section, insert it at the place where
+// it belongs. Otherwise, append it to the end of the program.
+void InsertSection (PROGRAM *Program, SECTION *Section);
+
+// Find the last startup section below or at StartupNumber.
+// If there is no such section, the function returns NULL.
+SECTION *FindStartupSection (const PROGRAM *Program, OFFSET StartupNumber);
+
+// Insert the item at the correct place in the section.
+void InsertSymbol (SECTION *Section, SYMBOL *Symbol);
+void InsertReloc  (SECTION *Section, RELOC  *Reloc);
+
+// Find the item preceding or following the given location. May return NULL.
+#define DefineFindItemAtPos(Type,Item) Type *Find##Item##AtPos (const SECTION *Section, OFFSET Location, BOOLEAN Following)
+DefineFindItemAtPos (SYMBOL,   Symbol);
+DefineFindItemAtPos (RELOC,    Reloc);
+DefineFindItemAtPos (ROM_CALL, ROMCall);
+DefineFindItemAtPos (RAM_CALL, RAMCall);
+DefineFindItemAtPos (LIB_CALL, LibCall);
+#undef DefineFindItemAtPos
+
+// Get the file name where the code at a given location in the
+// section came from. May return NULL.
+const char *GetFileName (const SECTION *Section, OFFSET Location);
+
+// Return the offset of the specified location into the section. If the
+// offset is unknown, emit a warning and return 0.
+OFFSET GetLocationOffset (const SECTION *Section, const LOCATION *Location);
+// Increase the counters and reference information necessary when this
+// location is used in the specified reloc.
+void HandleLocation (RELOC *Reloc, LOCATION *Location);
+
+// Point the location to the appropriate symbol, if one is found.
+SYMBOL *ResolveLocation (PROGRAM *Program, SECTION *Section, LOCATION *Location);
+// Resolve the reloc, if possible.
+// Force: Fail on unresolvable references.
+BOOLEAN ResolveReloc (RELOC *Reloc, BOOLEAN Force);
+// Resolve all relocs in the program, if possible.
+// Force: Fail on unresolvable references.
+BOOLEAN ResolveRelocs (PROGRAM *Program, BOOLEAN Force);
+
+// Optimize the location to have the least possible offset.
+void OptimizeLocation (LOCATION *Location);
+// Optimize all relocs to have the least possible target and relation offset.
+void OptimizeRelocs (PROGRAM *Program);
+
+// Remove all unused sections.
+void RemoveUnusedSections (PROGRAM *Program);
+
+// Merge section Src into Dest.
+// Returns Dest if successful.
+SECTION *MergeSections (SECTION *Dest, SECTION *Src);
+// Merge all sections of the specified type.
+// Returns the merged section if successful (even if Dest is NULL).
+SECTION *MergeAllSections (PROGRAM *Program, SECTION *Dest, BOOLEAN AcceptInitialized, BOOLEAN AcceptUninitialized, BOOLEAN AcceptZeroes, BOOLEAN AcceptContents, BOOLEAN AcceptData, BOOLEAN AcceptCode, BOOLEAN AcceptNonConstructors, BOOLEAN AcceptConstructors, BOOLEAN AcceptDestructors, BOOLEAN AcceptNonStartup, BOOLEAN AcceptStartup);
+
+// Get the size that would result from padding a section of size
+// OrigSize to a multiple of Alignment.
+SIZE GetPaddedSize (SIZE OrigSize, COUNT Alignment);
+// Pad the section so its size becomes a multiple of Alignment.
+BOOLEAN PadSection (SECTION *Section, COUNT Alignment);
+
+// Try to resolve and remove relative relocs.
+BOOLEAN FixupRelativeRelocs (PROGRAM *Program);
+// Try to resolve and remove a single relative reloc.
+// This may cause the reloc to be freed.
+BOOLEAN FixupRelativeReloc (RELOC *Reloc);
+
+// Find a reloc with the same location and size as this one.
+RELOC *FindCompatibleReloc (const RELOC *Reloc, BOOLEAN AllowRelative);
+
+// Find a reloc which matches the specified criteria.
+RELOC *FindMatchingReloc (const SECTION *Section, OFFSET Location, SIZE Size, BOOLEAN AllowRelative, const void *Exception, const RELOC *Hint);
+
+// Make a reloc relative, and set its relation to the beginning of
+// the section plus Offset. If Section is NULL, the program entry
+// point is used as the relation.
+BOOLEAN SetRelocRelation (RELOC *Reloc, SECTION *Section, OFFSET Offset);
+// Make a reloc relative to the program entry point.
+BOOLEAN SetRelocProgramRelative (RELOC *Reloc);
+
+// Find or create a common symbol with specified name and size.
+SYMBOL *MakeCommonSymbol (PROGRAM *Program, const char *SymName, SIZE Size, BOOLEAN Initialize, const char *FileName);
+
+// Try to find and import a symbol with a given name in some archive.
+BOOLEAN ImportSymbolFromArchive (PROGRAM *Program, const char *SymName);
+
+// Create marker symbols at the beginning and end of the section, and write
+// them to Marker. Returns NULL on error.
+SECTION_MARKERS *CreateSectionMarkers (SECTION_MARKERS *Marker, SECTION *Section);
+
+// Get the object file at the specified position in the archive.
+ARCHIVE_OBJECT *GetArchiveObject (ARCHIVE *Archive, FILE_PTR FileOffset);
+
+// Get a reference to the library identified in the string. The library is
+// added to the program's used libraries if necessary.
+LIBRARY *GetLibrary (PROGRAM *Program, const char *LibName);
+
+// Add a global import. This means that all archive members which export this
+// symbol should be imported (unlike the import done by a reloc, which only
+// imports the first member exporting the symbol).
+// If no member was imported because of this, a warning will be displayed at
+// the end.
+// This calls CreateGlobalImport and ResolveGlobalImport.
+GLOBAL_IMPORT *AddGlobalImport (PROGRAM *Program, const char *SymName);
+// First part of AddGlobalImport. Add a global import to the list.
+GLOBAL_IMPORT *CreateGlobalImport (PROGRAM *Program, const char *SymName);
+// Second part of AddGlobalImport. Try to resolve the newly added global
+// import against already available archives.
+void ResolveGlobalImport (PROGRAM *Program, GLOBAL_IMPORT *Import);
+
+// Check whether the symbol matches any global import,
+// performing the appropriate actions if necessary.
+GLOBAL_IMPORT *CheckGlobalImports (PROGRAM *Program, ARCHIVE_SYMBOL *Symbol);
+// Check whether the symbol matches the specified global import,
+// performing the appropriate actions if necessary.
+BOOLEAN CheckGlobalImport (GLOBAL_IMPORT *Import, ARCHIVE_SYMBOL *Symbol);
+
+// Check whether all conditions are met for an archive symbol with multiple
+// or inverted conditions. If they are, import the appropriate file, and
+// increment the counters of all global imports fulfilling non-inverted
+// conditions.
+BOOLEAN CheckMultiConditionSymbol (PROGRAM *Program, ARCHIVE_SYMBOL *Symbol);
+
+// Resolve all remaining global imports. Usually, global imports are
+// processed directly, but if a symbol contains an inverted condition, we
+// have to wait until we really know that no such global import exists.
+void ResolveRemainingGlobalImports (PROGRAM *Program);
+
+// Check whether all global imports have succeeded at least once.
+BOOLEAN CheckAllGlobalImports (PROGRAM *Program);
+
+// Find the first segment that overlaps with the specified range.
+SEGMENT *FindSegment (const SECTION *Section, OFFSET Start, OFFSET End);
+
+// Check whether the specified range is declared to contain code.
+BOOLEAN IsCodeRange (const SECTION *Section, OFFSET Start, OFFSET End);
+
+// Check whether the section data in the specified range is zero. This only
+// checks the actual data, not the relocs, ROM calls, etc.
+BOOLEAN IsZeroDataRange (const SECTION *Section, OFFSET Start, OFFSET End);
+
+// Check whether the specified range contains only binary data, without
+// relocs, ROM calls, or anything similar. The range includes Start, but not
+// End.
+// The Exception parameter specifies an item to skip when searching for
+// possible problems.
+BOOLEAN IsBinaryDataRange (const SECTION *Section, OFFSET Start, OFFSET End, const void *Exception);
+
+// Check whether there is a reloc or symbol or something similar which
+// prevents us from optimizing away some code at the end of the section.
+// The Exception parameter specifies an item to skip when searching for
+// possible problems.
+BOOLEAN CanShrinkSection (const SECTION *Section, OFFSET Location, const void *Exception);
+
+// Cut the section off at the specified location. All items behind this
+// location are removed.
+void CutSection (SECTION *Section, OFFSET Location);
+
+// Allocate space at the end of the section and return a pointer to it (NULL
+// in case of error, or if Size is 0). The data is initialized to zero.
+I1 *AllocateSpaceInSection (SECTION *Section, SIZE Size);
+
+// Apply general architecture-specific code fixes and optimizations to the
+// program.
+void FixCode (PROGRAM *Program);
+
+// Disable range cutting for an entire section.
+void DisableRangeCutting (SECTION *Section);
+
+#endif

+ 333 - 0
reorder.c

@@ -0,0 +1,333 @@
+/* reorder.c: Routines to reorder sections
+
+   Copyright (C) 2004 Sebastian Reichelt
+   (original version was written by Kevin Kofler)
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "reorder.h"
+
+#include "data.h"
+#include "manip.h"
+#include "bincode/fix_m68k.h" // We need this to estimate the possible gains.
+#include "bincode/m68k.h"
+
+#include <stdlib.h>
+
+// Maximum length of the search for related sections.
+#define MAX_RELATED_SEARCH_LENGTH 100
+
+// Maximum size of intentional unbalancing.
+#define MAX_UNBALANCED_SIZE 4096
+
+// Comparison function for qsort.
+static int SectionComparisonFunction (const void *Section1Ptr, const void *Section2Ptr)
+{
+	const SECTION *Section1 = *((SECTION **) Section1Ptr), *Section2 = *((SECTION **) Section2Ptr);
+	return (Section2->Relocs.OptRefCount * Section1->Size - Section1->Relocs.OptRefCount * Section2->Size);
+}
+
+COUNT GetRelocCountFromTo (const SECTION *Src, const SECTION *Dest)
+{
+	COUNT Result = 0;
+	RELOC *Reloc;
+	for_each (Reloc, Src->Relocs)
+	{
+		SYMBOL *TargetSymbol = Reloc->Target.Symbol;
+		if (TargetSymbol && TargetSymbol->Parent == Dest && M68kIsRelocOptimizable (Reloc))
+			Result++;
+	}
+	return Result;
+}
+
+COUNT GetRelocCountBetween (const SECTION *Section1, const SECTION *Section2)
+{
+	return (GetRelocCountFromTo (Section1, Section2) + GetRelocCountFromTo (Section2, Section1));
+}
+
+BOOLEAN AreSectionsRelated (const SECTION *Section1, const SECTION *Section2)
+{
+	if (Section1 && Section2)
+	{
+		COUNT Relationship = M68kGetSectionRelationship (Section1, Section2);
+		return (Relationship > Section1->Size || Relationship > Section2->Size);
+	}
+	return FALSE;
+}
+
+BOOLEAN CanMoveSection (const SECTION *Section)
+{
+	return (!(AreSectionsRelated (GetPrev (Section), Section) || AreSectionsRelated (Section, GetNext (Section))));
+}
+
+BOOLEAN CanMoveSectionToFront (const SECTION *Section, const SECTION *Dest)
+{
+	if (!(CanMoveSection (Section)))
+		return FALSE;
+	else
+	{
+		SECTION *CheckSection;
+		SIZE TotalSize = Section->Size;
+		for (CheckSection = (Dest ? GetNext (Dest) : GetFirst (Section->Parent->Sections)); CheckSection; CheckSection = GetNext (CheckSection))
+		{
+			TotalSize += CheckSection->Size;
+			if (TotalSize > M68K_REL_MAX && GetRelocCountBetween (Section, CheckSection) > 0)
+				return FALSE;
+		}
+		return TRUE;
+	}
+}
+
+BOOLEAN CanMoveSectionToBack (const SECTION *Section, const SECTION *Dest)
+{
+	if (!(CanMoveSection (Section)))
+		return FALSE;
+	else
+	{
+		SECTION *CheckSection;
+		SIZE TotalSize = Section->Size;
+		for (CheckSection = (Dest ? GetPrev (Dest) : GetLast (Section->Parent->Sections)); CheckSection; CheckSection = GetPrev (CheckSection))
+		{
+			TotalSize += CheckSection->Size;
+			if (TotalSize > M68K_REL_MAX && GetRelocCountBetween (Section, CheckSection) > 0)
+				return FALSE;
+		}
+		return TRUE;
+	}
+}
+
+// Reorder the sections to make references as short as possible. Uses heuristics
+// to avoid combinatorial explosion.
+void ReorderSections (PROGRAM *Program)
+{
+	SECTION **Sections = calloc (CountItems (Program->Sections, SECTION), sizeof (SECTION *));
+	if (!Sections)
+	{
+		Warning (NULL, "Out of memory reordering sections.");
+		return;
+	}
+	{
+		COUNT SectionCount = 0;
+		SECTION *InsertPos = NULL;
+		
+		// Build an array of all sections, and unlink those sections from the
+		// section list.
+		SECTION *Section = GetFirst (Program->Sections), *NextSection;
+		if (Section && Program->Type == PT_NOSTUB)
+			Section = GetNext (Section);
+		for (; Section; Section = NextSection)
+		{
+			NextSection = GetNext (Section);
+			if (!(Section->StartupNumber))
+			{
+				if (Section->Handled)
+				{
+					// The first handled section is a good place to insert the sorted sections.
+					if (!InsertPos)
+						InsertPos = Section;
+				}
+				else
+				{
+					Sections[SectionCount++] = Section;
+					Unlink (Program->Sections, Section);
+				}
+			}
+		}
+		
+		// Sort this array according to the number of references.
+		qsort (Sections, SectionCount, sizeof (SECTION *), SectionComparisonFunction);
+		
+		// Insert the sections in a new order, putting heavily referenced sections
+		// in the middle of the program.
+		// The reason this works best is actually very specific to M68k and TIOS.
+		{
+			SECTION *FirstPos = (InsertPos ? GetPrev (InsertPos) : GetLast (Program->Sections)), *SecondPos = InsertPos;
+			SIZE FirstSize = 0, SecondSize = 0;
+			OFFSET CurSectionIdx;
+			for (CurSectionIdx = 0; CurSectionIdx < SectionCount; CurSectionIdx++)
+			{
+				SECTION *Section = Sections[CurSectionIdx];
+				if (Section)
+				{
+					BOOLEAN InsertAtSecondPos = FALSE;
+					if (!(Section->Relocs.RelativeRefs))
+					{
+						if (!(Section->Relocs.StartupRefs))
+							InsertAtSecondPos = SecondSize < FirstSize;
+						if (SecondSize - FirstSize <= MAX_UNBALANCED_SIZE && FirstSize - SecondSize <= MAX_UNBALANCED_SIZE)
+						{
+							// Try to find out which of the two sides is actually better,
+							// i.e. which causes fewer absolute relocs.
+							COUNT FirstRelocCount = 0, SecondRelocCount = 0;
+							BOOLEAN FirstTrouble = FALSE, SecondTrouble = FALSE;
+							SIZE TotalSize = FirstSize + SecondSize + Section->Size;
+							SIZE SizeLeft = TotalSize;
+							SECTION *CheckSection;
+							for (CheckSection = (FirstPos ? GetNext (FirstPos) : GetFirst (Program->Sections)); CheckSection && CheckSection != SecondPos && SizeLeft > M68K_REL_MAX; CheckSection = GetNext (CheckSection))
+							{
+								COUNT RelocCount = GetRelocCountBetween (Section, CheckSection);
+								if (RelocCount > 0)
+								{
+									FirstTrouble = TRUE;
+									if (!(SecondSize - FirstSize + 2 * CheckSection->Size <= MAX_UNBALANCED_SIZE && CanMoveSectionToBack (CheckSection, SecondPos)))
+										FirstRelocCount += RelocCount;
+								}
+								SizeLeft -= CheckSection->Size;
+							}
+							SizeLeft = TotalSize;
+							for (CheckSection = (SecondPos ? GetPrev (SecondPos) : GetLast (Program->Sections)); CheckSection && CheckSection != FirstPos && SizeLeft > M68K_REL_MAX; CheckSection = GetPrev (CheckSection))
+							{
+								COUNT RelocCount = GetRelocCountBetween (Section, CheckSection);
+								if (RelocCount > 0)
+								{
+									SecondTrouble = TRUE;
+									if (!(FirstSize - SecondSize + 2 * CheckSection->Size <= MAX_UNBALANCED_SIZE && CanMoveSectionToFront (CheckSection, FirstPos)))
+										SecondRelocCount += RelocCount;
+								}
+								SizeLeft -= CheckSection->Size;
+							}
+							if (FirstRelocCount != SecondRelocCount)
+								InsertAtSecondPos = SecondRelocCount > FirstRelocCount;
+							else if (FirstTrouble && (!SecondTrouble))
+								InsertAtSecondPos = FALSE;
+							else if (SecondTrouble && (!FirstTrouble))
+								InsertAtSecondPos = TRUE;
+						}
+					}
+					{
+						OFFSET RelatedSectionIdx = CurSectionIdx + 1;
+						OFFSET RelatedSectionSearchEnd = CurSectionIdx + MAX_RELATED_SEARCH_LENGTH;
+						if (RelatedSectionSearchEnd > SectionCount)
+							RelatedSectionSearchEnd = SectionCount;
+						do {
+							// Insert the section.
+							if (InsertAtSecondPos)
+							{
+								SIZE OriginalDifference = SecondSize - FirstSize;
+								InsertBefore (Program->Sections, Section, SecondPos);
+								SecondSize += Section->Size;
+								if (OriginalDifference <= MAX_UNBALANCED_SIZE)
+								{
+									SIZE SizeLeft = FirstSize + SecondSize;
+									SECTION *CheckSection, *NextSection;
+									for (CheckSection = (FirstPos ? GetNext (FirstPos) : GetFirst (Program->Sections)); CheckSection && CheckSection != SecondPos && SizeLeft > M68K_REL_MAX; CheckSection = NextSection)
+									{
+										NextSection = GetNext (CheckSection);
+										if (GetRelocCountBetween (Section, CheckSection) > 0 && OriginalDifference + 2 * CheckSection->Size <= MAX_UNBALANCED_SIZE && CanMoveSectionToBack (CheckSection, SecondPos))
+										{
+											Unlink (Program->Sections, CheckSection);
+											InsertBefore (Program->Sections, CheckSection, SecondPos);
+											SecondSize += CheckSection->Size;
+											FirstSize  -= CheckSection->Size;
+										}
+										SizeLeft -= CheckSection->Size;
+									}
+								}
+							}
+							else
+							{
+								SIZE OriginalDifference = FirstSize - SecondSize;
+								InsertAfter (Program->Sections, Section, FirstPos);
+								FirstSize += Section->Size;
+								if (OriginalDifference <= MAX_UNBALANCED_SIZE)
+								{
+									SIZE SizeLeft = FirstSize + SecondSize;
+									SECTION *CheckSection, *NextSection;
+									for (CheckSection = (SecondPos ? GetPrev (SecondPos) : GetLast (Program->Sections)); CheckSection && CheckSection != FirstPos && SizeLeft > M68K_REL_MAX; CheckSection = NextSection)
+									{
+										NextSection = GetPrev (CheckSection);
+										if (GetRelocCountBetween (Section, CheckSection) > 0 && OriginalDifference + 2 * CheckSection->Size <= MAX_UNBALANCED_SIZE && CanMoveSectionToFront (CheckSection, FirstPos))
+										{
+											Unlink (Program->Sections, CheckSection);
+											InsertAfter (Program->Sections, CheckSection, FirstPos);
+											FirstSize  += CheckSection->Size;
+											SecondSize -= CheckSection->Size;
+										}
+										SizeLeft -= CheckSection->Size;
+									}
+								}
+							}
+							{
+								SECTION *RelatedSection = NULL;
+								// Look for a closely related section, which should be put right
+								// next to this one.
+								for (; RelatedSectionIdx < RelatedSectionSearchEnd && (!RelatedSection); RelatedSectionIdx++)
+								{
+									SECTION *SearchSection = Sections[RelatedSectionIdx];
+									if (SearchSection)
+									{
+										COUNT Relationship;
+										if (InsertAtSecondPos)
+											Relationship = M68kGetSectionRelationship (Section, SearchSection);
+										else
+											Relationship = M68kGetSectionRelationship (SearchSection, Section);
+										if (Relationship > SearchSection->Size)
+										{
+											RelatedSection = SearchSection;
+											Sections[RelatedSectionIdx] = NULL;
+										}
+									}
+								}
+								Section = RelatedSection;
+							}
+						} while (Section);
+					}
+				}
+			}
+			
+			// Look at the effect of putting sections referenced by startup sections
+			// at the beginning. If it doesn't seem to have any negative effect, then
+			// do it.
+			{
+				if (FirstPos)
+					InsertPos = GetNext (FirstPos);
+				else
+					InsertPos = GetFirst (Program->Sections);
+				{
+					SECTION *FirstProblem;
+					SIZE ProblemSize = 0;
+					for (FirstProblem = InsertPos; FirstProblem && ProblemSize <= M68K_REL_MAX; FirstProblem = GetNext (FirstProblem))
+						ProblemSize += FirstProblem->Size;
+					{
+						SECTION *Section, *NextSection;
+						for (Section = InsertPos; Section && Section != SecondPos; Section = NextSection)
+						{
+							NextSection = GetNext (Section);
+							if (Section->Relocs.StartupRefs && CanMoveSection (Section))
+							{
+								BOOLEAN CanMove = TRUE;
+								SECTION *CheckSection;
+								for (CheckSection = FirstProblem; CheckSection && CheckSection != SecondPos; CheckSection = GetNext (CheckSection))
+								{
+									if (GetRelocCountBetween (Section, CheckSection) > 0)
+									{
+										CanMove = FALSE;
+										break;
+									}
+								}
+								if (CanMove)
+								{
+									Unlink (Program->Sections, Section);
+									InsertAfter (Program->Sections, Section, FirstPos);
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	free (Sections);
+}

+ 29 - 0
reorder.h

@@ -0,0 +1,29 @@
+/* reorder.h: Routines to reorder sections
+
+   Copyright (C) 2004 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef REORDER_H
+#define REORDER_H
+
+#include "generic.h"
+#include "data.h"
+
+// Reorder the sections to make references as short as possible. Uses heuristics
+// to avoid combinatorial explosion.
+void ReorderSections (PROGRAM *Program);
+
+#endif

+ 1369 - 0
special.c

@@ -0,0 +1,1369 @@
+/* special.c: Routines to handle special characteristics of the linker
+
+   Copyright (C) 2002-2004 Sebastian Reichelt
+   Copyright (C) 2003-2004 Kevin Kofler
+   Copyright (C) 2004 Billy Charvet
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "special.h"
+
+#include "integers.h"
+#include "manip.h"
+#include "insert/insert.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+// Try to translate an external symbol into a special-feature symbol used by the linker.
+// If the symbol contains a number, it is written to the variable pointed to by Number.
+SpecialExternalSymbolTypes TranslateSpecialExternalSymbol (PROGRAM *Program, char *SymbolName, void **Reference, OFFSET *Number)
+{
+	SpecialExternalSymbolTypes SymType = ST_NORMAL;
+	char *SymNum = SymbolName, *Divider = NULL;
+	int NumBase = 16;
+	
+	if (!(strncmp (SymbolName, "tiamsapi_", sizeof ("tiamsapi_") - 1)))
+	{
+		SymType = ST_ROM_CALL;
+		SymNum += sizeof ("tiamsapi_") - 1;
+		NumBase = 10;
+	}
+	if (!(strncmp (SymbolName, "_ROM_CALL_", sizeof ("_ROM_CALL_") - 1)))
+	{
+		SymType = ST_ROM_CALL;
+		SymNum += sizeof ("_ROM_CALL_") - 1;
+	}
+	else if (!(strncmp (SymbolName, "_RAM_CALL_", sizeof ("_RAM_CALL_") - 1)))
+	{
+		SymType = ST_RAM_CALL;
+		SymNum += sizeof ("_RAM_CALL_") - 1;
+	}
+	else if (!(strncmp (SymbolName, "_extraramaddr@", sizeof ("_extraramaddr@") - 1)))
+	{
+		SymType = ST_EXTRA_RAM;
+		SymNum += sizeof ("_extraramaddr@") - 1;
+	}
+	else if (!(strncmp (SymbolName, "_extraramaddr__", sizeof ("_extraramaddr__") - 1)))
+	{
+		SymType = ST_EXTRA_RAM;
+		SymNum += sizeof ("_extraramaddr__") - 1;
+	}
+	else if (((Divider = strchr (SymbolName, '@'))) || (((Divider = strstr (SymbolName, "__"))) && (SymbolName [0] != '_') && (SymbolName [0] != '.') && (strncmp (SymbolName, "L_", sizeof ("L_") - 1))))
+	{
+		if (*Divider == '@')
+			SymNum = Divider + 1;
+		else
+			SymNum = Divider + 2;
+		if (strlen (SymNum) == 4)
+			SymType = ST_LIB_CALL;
+	}
+	
+	if (SymType != ST_NORMAL)
+	{
+		// Get the number the symbol contains (in hex).
+		if (Number)
+		{
+			char *Err;
+			*Number = strtoul (SymNum, &Err, NumBase);
+			// If the number has an error in it, revert to normal symbol.
+			if (*Err)
+				SymType = ST_NORMAL;
+		}
+	}
+	
+	if (SymType == ST_LIB_CALL)
+	{
+		// Cut SymbolName at the divider.
+		*Divider = 0;
+		// Get a reference to the library identified by SymbolName.
+		if (Reference)
+		{
+			*Reference = GetLibrary (Program, SymbolName);
+			if (!(*Reference))
+				SymType = ST_NORMAL;
+		}
+	}
+	
+	return SymType;
+}
+
+// Handle the symbol if it is a special one.
+// Returns TRUE if it was special; in this case it should not be used.
+BOOLEAN HandleSpecialSymbol (PROGRAM *Program, const char *SymbolName)
+{
+	BOOLEAN SymbolNameMatches (const char *Name)
+	{
+		return (!(strcmp (SymbolName, Name)));
+	}
+	
+	const char *Divider;
+	
+	// All special symbols start with an underscore.
+	if (SymbolName [0] == '_')
+	{
+		SymbolName++;
+		
+		// The most important special symbol: __ref_all_xxx.
+		// It adds xxx to the global imports.
+		if (!(strncmp (SymbolName, "_ref_all_", sizeof ("_ref_all_") - 1)))
+		{
+			if (!(Program->IgnoreGlobalImports))
+				AddGlobalImport (Program, SymbolName + sizeof ("_ref_all_") - 1);
+		}
+		else if (SymbolNameMatches ("tigcc_native"))
+		{
+			// Kernel is the default type. Everything else must be defined explicitly
+			// and should therefore take precedence over _tigcc_native.
+			if (Program->Type == PT_KERNEL)
+				Program->Type = PT_NATIVE;
+		}
+		else if (SymbolNameMatches ("nostub"))
+		{
+			SECTION *FirstSection = GetFirst (Program->Sections);
+			if (FirstSection && (!(FirstSection->StartupNumber)))
+				FirstSection->Referenced = TRUE;
+			Program->Type = PT_NOSTUB;
+		}
+#ifdef NOSTUB_DLL_SUPPORT
+		else if (SymbolNameMatches ("nostub_dll"))
+		{
+			SECTION *FirstSection = GetFirst (Program->Sections);
+			if (FirstSection && (!(FirstSection->StartupNumber)))
+				FirstSection->Referenced = TRUE;
+			Program->Type = PT_NOSTUB_DLL;
+			Program->Library = TRUE;
+		}
+#endif /* NOSTUB_DLL_SUPPORT */
+#ifdef FARGO_SUPPORT
+		else if (SymbolNameMatches ("fargo"))
+			Program->Type = PT_FARGO;
+#endif /* FARGO_SUPPORT */
+#ifdef FLASH_OS_SUPPORT
+		else if (SymbolNameMatches ("flash_os"))
+			Program->Type = PT_FLASH_OS;
+#endif /* FLASH_OS_SUPPORT */
+		else if (SymbolNameMatches ("library"))
+		{
+			Program->Library = TRUE;
+#ifdef FARGO_SUPPORT
+			// Under Fargo, _library is a normal symbol as well.
+			if (Program->Type == PT_FARGO)
+				return FALSE;
+#endif /* FARGO_SUPPORT */
+		}
+		else if (SymbolNameMatches ("ti92"))
+		{
+			Program->Calcs |= CALC_TI92;
+		}
+		else if (SymbolNameMatches ("ti89"))
+		{
+			Program->Calcs |= CALC_TI89;
+			Program->KernelFlags |= 0x02;
+		}
+		else if (SymbolNameMatches ("ti89ti"))
+		{
+			Program->Calcs |= CALC_TI89 | CALC_FLAG_TITANIUM;
+			Program->KernelFlags |= 0x42;
+		}
+		else if (SymbolNameMatches ("ti92plus"))
+		{
+			Program->Calcs |= CALC_TI92PLUS;
+			Program->KernelFlags |= 0x01;
+		}
+		else if (SymbolNameMatches ("v200"))
+		{
+			Program->Calcs |= CALC_V200;
+			Program->KernelFlags |= 0x20;
+		}
+		else if (!(strncmp (SymbolName, "flag_", sizeof ("flag_") - 1)))
+			Program->KernelFlags |= (1 << strtoul (SymbolName + sizeof ("flag_") - 1, NULL, 10));
+		else if (!(strncmp (SymbolName, "version", sizeof ("version") - 1)))
+			Program->Version = strtoul (SymbolName + sizeof ("version") - 1, NULL, 16);
+		else if (!(strcmp (SymbolName, "_ld_use_fline_jumps")))
+		{
+			if (Program->OptimizeInfo)
+				Program->OptimizeInfo->UseFLineJumps = TRUE;
+		}
+		else if (!(strcmp (SymbolName, "_ld_use_4byte_fline_jumps")))
+		{
+			if (Program->OptimizeInfo)
+				Program->OptimizeInfo->Use4ByteFLineJumps = TRUE;
+		}
+		else if (!(strcmp (SymbolName, SYM_IGNORE_GLOBAL_IMPORTS + 1)))
+			Program->IgnoreGlobalImports = TRUE;
+		else if (SymbolNameMatches (SYM_OMIT_BSS_INIT + 1) || SymbolNameMatches (SYM_ALL_RELOCS + 1))
+			// These are mostly file-local special symbols; they are handled
+			// by the importing function.
+			;
+		else
+			// Not a special symbol.
+			return FALSE;
+		
+		// The symbol was handled here.
+		return TRUE;
+	}
+	else if (((Divider = strstr (SymbolName, "@version"))) || (((Divider = strstr (SymbolName, "__version"))) && (SymbolName [0] != '_') && (SymbolName [0] != '.') && (strncmp (SymbolName, "L_", sizeof ("L_") - 1))))
+	{
+		// Required minimum version number for a library.
+		const char *Version;
+		VERSION VersionNumber;
+		
+		if (*Divider == '@')
+			Version = Divider + sizeof ("@version") - 1;
+		else
+			Version = Divider + sizeof ("__version") - 1;
+		VersionNumber = strtoul (Version, NULL, 16);
+		
+		if (VersionNumber)
+		{
+			if (Divider - SymbolName <= MAX_SYM_LEN)
+			{
+				char LibName[MAX_SYM_LEN+1];
+				LIBRARY *Library;
+				
+				strncpy (LibName, SymbolName, Divider - SymbolName);
+				LibName [Divider - SymbolName] = 0;
+				Library = GetLibrary (Program, LibName);
+				if (Library && (Library->Version < VersionNumber))
+					Library->Version = VersionNumber;
+			}
+			
+			return TRUE;
+		}
+	}
+	
+	return FALSE;
+}
+
+// Translate a section name into a startup section number.
+// Returns 0 if the name does not represent a startup section.
+OFFSET GetStartupSectionNumber (const char *SectionName, SIZE MaxLen)
+{
+	// Check if it looks like a startup section.
+	if ((MaxLen > (SIZE) (sizeof ("_st") - 1)) && (!(strncmp (SectionName, "_st", sizeof ("_st") - 1))))
+	{
+		// Copy the section name to a temporary null-terminated location.
+		char SecName[MaxLen+1];
+		memset (SecName, 0, MaxLen + 1);
+		strncpy (SecName, SectionName, MaxLen);
+		// Check whether it is a library startup section.
+		if (SecName [sizeof ("_st") - 1] == 'l')
+		{
+			// Library startup sections are special:
+			// They may always be included, even if the file is not executable.
+			// Therefore they get a negative number to distinguish them.
+			OFFSET RealNum = strtoul (SecName + sizeof ("_stl") - 1, NULL, 10);
+			return (RealNum ? RealNum - 10000 : 0);
+		}
+		else
+			return (strtoul (SecName + sizeof ("_st") - 1, NULL, 10));
+	}
+	else
+		return 0;
+}
+
+// Translate a symbol name into the number of an exported function, if the
+// function is meant to be exported.
+// Returns -1 if it is not an exported function.
+OFFSET GetExportNumber (const char *SymbolName)
+{
+	const char *Divider;
+	
+	if (((Divider = strchr (SymbolName, '@'))) || (((Divider = strstr (SymbolName, "__"))) && (SymbolName [0] != '_') && (SymbolName [0] != '.') && (strncmp (SymbolName, "L_", sizeof ("L_") - 1))))
+	{
+		OFFSET Number;
+		char *Err;
+		
+		if (*Divider == '@')
+			Divider++;
+		else
+			Divider += 2;
+		
+		Number = strtoul (Divider, &Err, 16);
+		if (*Err)
+			return (-1);
+		
+		return Number;
+	}
+	else
+		return (-1);
+}
+
+// Add all imports with names defined by this program, if they are needed.
+BOOLEAN CreateSpecialGlobalImports (PROGRAM *Program)
+{
+	BOOLEAN Result = TRUE;
+	SECTION *TempSection;
+	
+	if (Program->Type == PT_KERNEL)
+	{
+		if (Program->Library)
+			Result = Result && AddGlobalImport (Program, "__kernel_library_header");
+		else
+			Result = Result && AddGlobalImport (Program, "__kernel_program_header");
+	}
+#ifdef FARGO_SUPPORT
+	else if (Program->Type == PT_FARGO)
+	{
+		if (Program->Library)
+			Result = Result && AddGlobalImport (Program, "__fargo_library_header");
+		else
+			Result = Result && AddGlobalImport (Program, "__fargo_program_header");
+	}
+#endif /* FARGO_SUPPORT */
+#ifdef FLASH_OS_SUPPORT
+	else if (Program->Type == PT_FLASH_OS)
+		Result = Result && AddGlobalImport (Program, "__flash_os_header");
+#endif /* FLASH_OS_SUPPORT */
+	if (Program->Constructors.Start)
+		Result = Result && AddGlobalImport (Program, "__handle_constructors");
+	if (Program->Destructors.Start)
+		Result = Result && AddGlobalImport (Program, "__handle_destructors");
+	if (Program->BSSSection && Program->BSSSection->Initialized)
+		Result = Result && AddGlobalImport (Program, "__initialize_bss");
+	
+	// Handle BSS section if any.
+	if (Program->BSSSection && (!(Program->BSSSection->Handled)))
+	{
+		GLOBAL_IMPORT *Import = CreateGlobalImport (Program, "__handle_bss");
+		if (Import)
+		{
+			// This is the import that will mark the BSS section as handled
+			// if it succeeds.
+			Program->BSSImport = Import;
+			
+			// Try to resolve it against available archives.
+			ResolveGlobalImport (Program, Import);
+			
+			// The BSS section is simply merged into the rest if it is not
+			// handled.
+			Import->Succeeded = TRUE;
+		}
+		else
+			Result = FALSE;
+	}
+	
+	// Handle absolute relocs if there are any.
+	for_each (TempSection, Program->Sections)
+	{
+		BOOLEAN Done = FALSE;
+		RELOC *TempReloc;
+		for_each (TempReloc, TempSection->Relocs)
+		{
+			if ((!(TempReloc->Relative || TempReloc->Target.Builtin || (TempReloc->Target.Symbol && (TempReloc->Target.Symbol->Parent->Handled)))))
+			{
+				GLOBAL_IMPORT *Import = AddGlobalImport (Program, "__handle_relocs");
+				if (Import)
+					// Relocs are either handled by the export format, or a
+					// warning is emitted.
+					Import->Succeeded = TRUE;
+				else
+					Result = FALSE;
+				Done = TRUE;
+				break;
+			}
+		}
+		if (Done)
+			break;
+	}
+	
+	// Handle ROM calls if there are any.
+	for_each (TempSection, Program->Sections)
+	{
+		if (!(IsEmpty (TempSection->ROMCalls)))
+		{
+			GLOBAL_IMPORT *Import = AddGlobalImport (Program, "__handle_rom_calls");
+			if (Import)
+				// ROM calls are either handled by the export format, or a
+				// warning is emitted.
+				Import->Succeeded = TRUE;
+			else
+				Result = FALSE;
+			break;
+		}
+	}
+	
+	// Handle RAM calls if there are any.
+	for_each (TempSection, Program->Sections)
+	{
+		if (!(IsEmpty (TempSection->RAMCalls)))
+		{
+			GLOBAL_IMPORT *Import = AddGlobalImport (Program, "__handle_ram_calls");
+			if (Import)
+				// RAM calls are either handled by the export format, or a
+				// warning is emitted.
+				Import->Succeeded = TRUE;
+			else
+				Result = FALSE;
+			break;
+		}
+	}
+	
+	// Handle libraries if any libraries are referenced.
+	if (!(IsEmpty (Program->Libraries)))
+	{
+		GLOBAL_IMPORT *Import = AddGlobalImport (Program, "__handle_libraries");
+		if (Import)
+			// Libraries are either handled by the export format, or a
+			// warning is emitted.
+			Import->Succeeded = TRUE;
+		else
+			Result = FALSE;
+	}
+	
+	// Handle _nostub comments if any.
+	if ((Program->Type == PT_NATIVE) || (Program->Type == PT_NOSTUB))
+	{
+		BOOLEAN NostubComments = FALSE;
+		for_each (TempSection, Program->Sections)
+		{
+			SYMBOL *TempSymbol;
+			for_each (TempSymbol, TempSection->Symbols)
+			{
+				if (TempSymbol->Exported && (!(strncmp (TempSymbol->Name, "_nostub_data__", sizeof ("_nostub_data__") - 1))))
+				{
+					OFFSET ExportNumber = GetExportNumber (TempSymbol->Name + sizeof ("_nostub_") - 1);
+					if (ExportNumber >= 0)
+					{
+						NostubComments = TRUE;
+						TempSection->Referenced = TRUE;
+						break;
+					}
+				}
+			}
+		}
+		if (NostubComments)
+			Result = Result && AddGlobalImport (Program, "__nostub_comment_header");
+	}
+	
+#ifdef DATA_VAR_SUPPORT
+	// Handle data variable if desired.
+	if (Program->DataVarInfo->Name && Program->DataSection)
+	{
+		Result = Result && (AddGlobalImport (Program, "__handle_data_var"));
+		if (Program->DataVarInfo->CreateCopy)
+		{
+			Result = Result && (AddGlobalImport (Program, "__data_var_create_copy"));
+			if (Program->DataVarInfo->CopyOnlyIfArchived)
+				Result = Result && (AddGlobalImport (Program, "__data_var_copy_if_archived"));
+		}
+	}
+#endif /* DATA_VAR_SUPPORT */
+	
+	return Result;
+}
+
+// If the reloc or its relation point to a special ld-exported symbol,
+// change it to the appropriate value if possible. FALSE is returned
+// only if it is not a special symbol location, or if there was an error.
+BOOLEAN ResolveSpecialSymbolReloc (RELOC *Reloc, BOOLEAN *TryAgain)
+{
+	// At first, resolve the relation, because if this is a built-in
+	// number, we cannot write it into the section contents directly,
+	// but we have to subtract it from FixedOffset.
+	BOOLEAN Result = ResolveSpecialSymbolRelocRelation (Reloc, TryAgain);
+	
+	// Now resolve the target.
+	return (ResolveSpecialSymbolRelocTarget (Reloc, TryAgain) ? Result : FALSE);
+}
+	
+// If the reloc points to a special ld-exported symbol, change it to the
+// appropriate value if possible. FALSE is returned only if it is not a
+// special symbol reloc, or if there was an error.
+BOOLEAN ResolveSpecialSymbolRelocTarget (RELOC *Reloc, BOOLEAN *TryAgain)
+{
+	BOOLEAN Result = FALSE;
+	SECTION *Section = Reloc->Parent;
+	LOCATION *Location = &(Reloc->Target);
+	
+	// If the relation is an unresolved reference to a builtin number,
+	// do not even try to resolve the target. This would only cause
+	// trouble, since we would need to handle it immediately if it
+	// resolved to a number. There will be another call to this anyway
+	// after the relation has finally been resolved.
+	// Usually, we would at least need to set the Target.Builtin flag.
+	// However, currently all tests which check for Target.Builtin also
+	// check for Relation or Relative in general. If this ever changes,
+	// this code will break.
+	if (Reloc->Relation && Reloc->Relation->Builtin)
+		return TRUE;
+	
+	if (ResolveSpecialSymbolLocation (Section, Location, TryAgain))
+	{
+		// If it has resolved to a number, write it into the section
+		// contents.
+		if (!(Location->Symbol || Location->SymbolName))
+		{
+			if (Reloc->Relation)
+				Warning (GetFileName (Section, Reloc->Location), "Ignoring invalid negative reference to `%s' at 0x%lX.", Reloc->Relation->SymbolName, (long) Reloc->Location);
+			
+			if ((Reloc->Location >= 0) && (Reloc->Location + Reloc->Size <= Section->Size))
+			{
+				// Add the target offset (to support things such as "__ld_xxx+1").
+				OFFSET NewValue = Location->Offset + Reloc->FixedOffset;
+				
+				if (Reloc->Relative)
+					Warning (GetFileName (Section, Reloc->Location), "Invalid relative reference to built-in number `%s' at 0x%lX; changing to absolute.", Reloc->Target.SymbolName, (long) Reloc->Location);
+				
+				// Check if the section contents at the reloc are zero.
+				if (!(IsZeroDataRange (Section, Reloc->Location, Reloc->Location + Reloc->Size)))
+					Warning (GetFileName (Section, Reloc->Location), "Builtin reloc at 0x%lX to `%s' on nonzero section contents. Overlapping with another?", (long) Reloc->Location, Reloc->Target.SymbolName);
+				
+				// Resolve the reloc by writing the value into the section.
+				Result = AddTI (Section->Data + Reloc->Location, Reloc->Size, NewValue, TRUE, TRUE);
+				
+				if (!Result)
+					Error (GetFileName (Section, Reloc->Location), "Number `%s' (=%ld) too large for size %ld reloc at 0x%lX.", Reloc->Target.SymbolName, (long) NewValue, (long) Reloc->Size, (long) Reloc->Location);
+			}
+			else
+			{
+				Warning (GetFileName (Section, Reloc->Location), "Removing reloc at 0x%lX to `%s' outside of section.", (long) Reloc->Location, Reloc->Target.SymbolName);
+				Result = TRUE;
+			}
+			
+			FreeReloc (Reloc);
+		}
+		else
+			Result = TRUE;
+	}
+	
+	return Result;
+}
+
+// If the reloc's relation points to a special ld-exported symbol,
+// change it to the appropriate value if possible. FALSE is returned
+// only if it is not a special symbol location, or if there was an error.
+BOOLEAN ResolveSpecialSymbolRelocRelation (RELOC *Reloc, BOOLEAN *TryAgain)
+{
+	BOOLEAN Result = FALSE;
+	SECTION *Section = Reloc->Parent;
+	LOCATION *Location = Reloc->Relation;
+	
+	if (Reloc->Relation)
+	{
+		// Try to resolve the relation.
+		if (ResolveSpecialSymbolLocation (Section, Location, TryAgain))
+		{
+			// Check if it has really been resolved.
+			BOOLEAN Resolved = (Location->Symbol != NULL);
+			
+			// If it has been resolved to a number, subtract it from
+			// FixedOffset.
+			if (!(Location->Symbol || Location->SymbolName))
+			{
+				Reloc->FixedOffset -= Location->Offset;
+				FreeRelocRelation (Reloc);
+				Resolved = TRUE;
+			}
+			
+			Result = TRUE;
+		}
+	}
+	
+	return Result;
+}
+
+// If the location points to a special ld-exported symbol, change it to the
+// appropriate value if possible. FALSE is returned only if it is not a
+// special symbol location, or if there was an error.
+// Warning: If the special symbol resolves to a number, Location->Symbol and
+// Location->SymbolName will both be NULL, and Location->Offset will contain
+// the number.
+BOOLEAN ResolveSpecialSymbolLocation (SECTION *Section, LOCATION *Location, BOOLEAN *TryAgain)
+{
+	const char *SymName = Location->SymbolName;
+	
+	BOOLEAN SymNameMatches (const char *Name)
+	{
+		return (!(strcmp (SymName, Name)));
+	}
+	
+	// Locations that are already resolved do not need any treatment.
+	if (Location->Symbol)
+		return FALSE;
+	
+	// All special symbols start with '_'.
+	if (SymName && (SymName [0] == '_'))
+	{
+		PROGRAM *Program = Section->Parent;
+		BOOLEAN SetToEntryPoint = FALSE;
+		OFFSET NewValue = 0;
+		
+		SymName++;
+		
+		// All built-in symbols start with "__ld_".
+		if (!(strncmp (SymName, SYMPF_BUILTIN + 1, sizeof (SYMPF_BUILTIN) - 2)))
+		{
+			SIZE SymNameLength;
+			BOOLEAN HasValue = FALSE;
+			SYMBOL *NewSymbol = NULL;
+			OFFSET NewTargetOffset = 0;
+			SECTION *TempSection;
+			
+			// Skip the "__ld_" prefix.
+			SymName += sizeof (SYMPF_BUILTIN) - 2;
+			SymNameLength = strlen (SymName);
+			
+			// Find (but do not resolve) calculator constants.
+			if (IsCalcBuiltinLocation (Location))
+				;
+			// Resolve references to insertions.
+			else if (!(strncmp (SymName, SYMPF_INSERT, sizeof (SYMPF_INSERT) - 1)))
+			{
+				if ((!(TempSection = Program->MainSection))
+				 || (!(NewSymbol = HandleAutoInsertion (TempSection, Location->SymbolName))))
+					return TRUE;
+			}
+			// Resolve "has_...s".
+			else if ((!(strncmp (SymName, "has_", sizeof ("has_") - 1))) && (SymName [SymNameLength - 1] == 's'))
+			{
+				if (GetBuiltinValue (Program, SymName + (sizeof ("has_") - 1), SymNameLength - 1 - (sizeof ("has_") - 1), &NewValue, -1))
+					HasValue = (NewValue || Program->ResolveAllBuiltins);
+			}
+			// Resolve "..._count".
+			else if ((SymNameLength > ((SIZE) (sizeof ("_count") - 1))) && (!(strcmp (SymName + SymNameLength - (sizeof ("_count") - 1), "_count"))))
+			{
+				if (Program->ResolveAllBuiltins)
+				{
+					Program->Frozen = TRUE;
+					if (GetBuiltinValue (Program, SymName, SymNameLength - (sizeof ("_count") - 1), &NewValue, 0))
+						HasValue = TRUE;
+				}
+			}
+			else if (SymNameMatches ("entry_point"))
+				SetToEntryPoint = TRUE;
+			else if (SymNameMatches ("entry_point_plus_0x8000"))
+			{
+				SetToEntryPoint = TRUE;
+				NewValue = 0x8000;
+			}
+			else if (SymNameMatches ("constructors_start"))
+			{
+				if (!(NewSymbol = Program->Constructors.Start))
+					return TRUE;
+			}
+			else if (SymNameMatches ("constructors_end"))
+			{
+				if (!(NewSymbol = Program->Constructors.End))
+					return TRUE;
+			}
+			else if (SymNameMatches ("constructors_size"))
+			{
+				if (Program->Constructors.Start && Program->Constructors.End)
+				{
+					NewValue = Program->Constructors.End->Location - Program->Constructors.Start->Location;
+					HasValue = TRUE;
+				}
+				else if (Program->ResolveAllBuiltins)
+					HasValue = TRUE;
+			}
+			else if (SymNameMatches ("destructors_start"))
+			{
+				if (!(NewSymbol = Program->Destructors.Start))
+					return TRUE;
+			}
+			else if (SymNameMatches ("destructors_end"))
+			{
+				if (!(NewSymbol = Program->Destructors.End))
+					return TRUE;
+			}
+			else if (SymNameMatches ("destructors_size"))
+			{
+				if (Program->Destructors.Start && Program->Destructors.End)
+				{
+					NewValue = Program->Destructors.End->Location - Program->Destructors.Start->Location;
+					HasValue = TRUE;
+				}
+				else if (Program->ResolveAllBuiltins)
+					HasValue = TRUE;
+			}
+			else if (SymNameMatches ("data_start"))
+			{
+				if (Program->DataSection)
+					NewSymbol = Program->DataSection->SectionSymbol;
+				else
+					return TRUE;
+			}
+			else if (SymNameMatches ("data_end"))
+			{
+				if (Program->DataSection)
+				{
+					NewSymbol = Program->DataSection->SectionSymbol;
+					NewTargetOffset = Program->DataSection->Size;
+				}
+				else
+					return TRUE;
+			}
+			else if (SymNameMatches ("data_size"))
+			{
+				if (Program->DataSection)
+				{
+					NewValue = Program->DataSection->Size;
+					HasValue = TRUE;
+				}
+				else if (Program->ResolveAllBuiltins)
+					HasValue = TRUE;
+			}
+			else if (SymNameMatches ("bss_start"))
+			{
+				if (Program->BSSSection)
+					NewSymbol = Program->BSSSection->SectionSymbol;
+				else
+					return TRUE;
+			}
+			else if (SymNameMatches ("bss_end"))
+			{
+				if (Program->BSSSection)
+				{
+					NewSymbol = Program->BSSSection->SectionSymbol;
+					NewTargetOffset = Program->BSSSection->Size;
+				}
+				else
+					return TRUE;
+			}
+			else if (SymNameMatches ("bss_size"))
+			{
+				if (Program->BSSSection)
+				{
+					NewValue = Program->BSSSection->Size;
+					HasValue = TRUE;
+				}
+				else if (Program->ResolveAllBuiltins)
+					HasValue = TRUE;
+			}
+			else if (SymNameMatches ("file_version"))
+			{
+				if (Program->ResolveAllBuiltins || Program->Version)
+				{
+					NewValue = Program->Version;
+					HasValue = TRUE;
+				}
+			}
+			else if (SymNameMatches ("kernel_flags"))
+			{
+				if (Program->ResolveAllBuiltins)
+				{
+					NewValue = Program->KernelFlags;
+					HasValue = TRUE;
+				}
+			}
+			else if (SymNameMatches ("kernel_bss_table"))
+			{
+				if (Program->BSSSection)
+				{
+					strcpy ((char *) (Location->SymbolName), "__kernel_bss_table");
+					if (TryAgain)
+						*TryAgain = TRUE;
+					// This prevents the section from being merged, and prevents
+					// relocs to it from being emitted.
+					Program->BSSSection->Handled = TRUE;
+					return FALSE;
+				}
+				else if (Program->ResolveAllBuiltins)
+					SetToEntryPoint = TRUE;
+			}
+			else if (SymNameMatches ("program_size"))
+			{
+				if (Program->ResolveAllBuiltins && Program->MainSection)
+				{
+					NewValue = Program->MainSection->Size;
+					HasValue = TRUE;
+				}
+				else
+					return TRUE;
+			}
+			else if (SymNameMatches ("kernel_export_table"))
+			{
+				BOOLEAN HasExports = FALSE;
+				for_each (TempSection, Program->Sections)
+				{
+					SYMBOL *TempSymbol;
+					for_each (TempSymbol, TempSection->Symbols)
+					{
+						if (TempSymbol->Exported)
+						{
+							OFFSET ExportNumber = GetExportNumber (TempSymbol->Name);
+							if (ExportNumber >= 0)
+							{
+								HasExports = TRUE;
+								TempSection->Referenced = TRUE;
+								break;
+							}
+						}
+					}
+				}
+				
+				if (HasExports)
+				{
+					strcpy ((char *) (Location->SymbolName), "__kernel_export_table");
+					if (TryAgain)
+						*TryAgain = TRUE;
+					return FALSE;
+				}
+				else if (Program->ResolveAllBuiltins)
+					SetToEntryPoint = TRUE;
+			}
+#ifdef DATA_VAR_SUPPORT
+			else if (SymNameMatches ("data_var_name_end"))
+			{
+				// Point the reloc to the terminating zero byte of the name.
+				if (Program->DataVarInfo->Name)
+				{
+					strcpy ((char *) (Location->SymbolName), "__data_var_name_start");
+					Location->Offset += strlen (Program->DataVarInfo->Name) + 1;
+					if (TryAgain)
+						*TryAgain = TRUE;
+					return FALSE;
+				}
+			}
+#endif /* DATA_VAR_SUPPORT */
+			else
+				return FALSE;
+			
+			if (!SetToEntryPoint)
+			{
+				if (HasValue)
+				{
+					// Point the location to the new value.
+					Location->Symbol = NULL;
+					FreeLocationSymbolName (Section, Location);
+					Location->Offset += NewValue;
+				}
+				else if (NewSymbol)
+				{
+					// Point the location to the new symbol.
+					Location->Symbol = NewSymbol;
+					FreeLocationSymbolName (Section, Location);
+					Location->Offset += NewTargetOffset;
+					NewSymbol->Parent->Referenced = TRUE;
+				}
+				else
+					Location->Builtin = TRUE;
+				
+				return TRUE;
+			}
+		}
+		// If this is a reloc to a kernel-specific symbol, point it to the
+		// entry point. The result of this is that the reloc's value
+		// becomes 0 if the reloc was made up by something like
+		// _exit-__kernel_entry_point.
+		else if (SymNameMatches ("exit") || SymNameMatches ("comment") || SymNameMatches ("extraram") || SymNameMatches ("library"))
+			SetToEntryPoint = TRUE;
+		
+		if (SetToEntryPoint)
+		{
+			if (Program->EntryPoint.Symbol)
+			{
+				Location->Symbol = Program->EntryPoint.Symbol;
+				FreeLocationSymbolName (Section, Location);
+				Location->Offset += Program->EntryPoint.Offset + NewValue;
+			}
+			
+			return TRUE;
+		}
+	}
+	
+	return FALSE;
+}
+
+// Count the items for a specific built-in symbol, specified by SymName
+// and SymNameLength. If TrueValue is nonzero, items are not counted,
+// but NewValue is set to this value if at least one item was found. In
+// that case, if Program->ResolveAllBuiltins is false, NewValue may be
+// unchanged even though there are some items; you need to check back
+// later when Program->ResolveAllBuiltins is true.
+BOOLEAN GetBuiltinValue (PROGRAM *Program, const char *SymName, SIZE SymNameLength, OFFSET *NewValue, OFFSET TrueValue)
+{
+#define Count(n,op,code) \
+({ \
+	register OFFSET n__ = (n); \
+	if (TrueValue) \
+	{ \
+		if (n__) \
+		{ \
+			*NewValue = TrueValue; \
+			code; \
+		} \
+	} \
+	else \
+		*NewValue op n__; \
+})
+#define SetCounter(n) (Count ((n), =, (void) 0))
+#define IncreaseCounter(n) (Count ((n), +=, break))
+	
+	BOOLEAN SymNameMatches (const char *Name)
+	{
+		return (!(strncmp (SymName, Name, SymNameLength)));
+	}
+	
+	SECTION *TempSection;
+	RELOC *TempReloc;
+	SYMBOL *TempSymbol;
+	
+	if (SymNameMatches ("constructor"))
+	{
+		if (Program->Constructors.Start && Program->Constructors.End)
+			SetCounter ((Program->Constructors.End->Location - Program->Constructors.Start->Location) >> 2);
+	}
+	else if (SymNameMatches ("destructor"))
+	{
+		if (Program->Destructors.Start && Program->Destructors.End)
+			SetCounter ((Program->Destructors.End->Location - Program->Destructors.Start->Location) >> 2);
+	}
+	else if (SymNameMatches ("reloc"))
+	{
+		// Count all absolute relocs.
+		// Relative relocs will either be resolved completely or
+		// produce errors.
+		for_each (TempSection, Program->Sections)
+		{
+			IncreaseCounter (TempSection->Relocs.EmittedCount);
+			// Since relocs may be removed, if ResolveAllBuiltins
+			// is false, do not handle TrueValue.
+			if ((!TrueValue) || Program->ResolveAllBuiltins)
+			{
+				for_each (TempReloc, TempSection->Relocs)
+					if (!(TempReloc->Relative || TempReloc->Target.Builtin || (TempReloc->Target.Symbol && (TempReloc->Target.Symbol->Parent->Handled))))
+						IncreaseCounter (1);
+			}
+		}
+	}
+	else if (SymNameMatches ("data_ref"))
+	{
+		if (Program->DataSection)
+		{
+			// Count all absolute relocs to the data section.
+			for_each (TempSection, Program->Sections)
+				for_each (TempReloc, TempSection->Relocs)
+					if (TempReloc->Target.Symbol && (TempReloc->Target.Symbol->Parent == Program->DataSection) && (!(TempReloc->Relative || TempReloc->Target.Builtin)))
+						IncreaseCounter (1);
+		}
+	}
+	else if (SymNameMatches ("bss_ref"))
+	{
+		if (Program->BSSSection)
+		{
+			// Count all absolute relocs to the BSS section.
+			for_each (TempSection, Program->Sections)
+				for_each (TempReloc, TempSection->Relocs)
+					if (TempReloc->Target.Symbol && (TempReloc->Target.Symbol->Parent == Program->BSSSection) && (!(TempReloc->Relative || TempReloc->Target.Builtin)))
+						IncreaseCounter (1);
+		}
+	}
+	else if (SymNameMatches ("rom_call"))
+	{
+		for_each (TempSection, Program->Sections)
+			IncreaseCounter (CountItems (TempSection->ROMCalls, ROM_CALL));
+	}
+	else if (SymNameMatches ("ram_call"))
+	{
+		for_each (TempSection, Program->Sections)
+			IncreaseCounter (CountItems (TempSection->RAMCalls, RAM_CALL));
+	}
+	else if (SymNameMatches ("lib"))
+		SetCounter (CountItems (Program->Libraries, LIBRARY));
+	else if (SymNameMatches ("referenced_lib"))
+		SetCounter (Program->Libraries.ReferencedCount);
+	else if (SymNameMatches ("export"))
+	{
+		// The number of exports is equal to the highest export number + 1.
+		for_each (TempSection, Program->Sections)
+		{
+			for_each (TempSymbol, TempSection->Symbols)
+			{
+				if (TempSymbol->Exported)
+				{
+					OFFSET ExportNumber = GetExportNumber (TempSymbol->Name);
+					if ((ExportNumber >= 0) && (*NewValue < ExportNumber + 1))
+						Count (ExportNumber + 1, =, break);
+				}
+			}
+		}
+	}
+	else if (SymNameMatches ("nostub_comment"))
+	{
+		for_each (TempSection, Program->Sections)
+		{
+			for_each (TempSymbol, TempSection->Symbols)
+			{
+				if (TempSymbol->Exported && (!(strncmp (TempSymbol->Name, SYMPF_NOSTUB_DATA, sizeof (SYMPF_NOSTUB_DATA) - 1))))
+				{
+					OFFSET ExportNumber = GetExportNumber (TempSymbol->Name + (sizeof (SYMPF_NOSTUB_DATA_START) - 1));
+					if (ExportNumber >= 0)
+						IncreaseCounter (1);
+				}
+			}
+		}
+	}
+	else
+		return FALSE;
+	
+#undef IncreaseCounter
+#undef SetCounter
+#undef Count
+	
+	return TRUE;
+}
+
+// If the given symbol name belongs to a calculator-specific builtin
+// symbol, return a pointer to the part of it that holds the values.
+const char *GetCalcBuiltinValues (const char *SymName)
+{
+	if (!(strncmp (SymName, SYMPF_BUILTIN_CALC_CONST, sizeof (SYMPF_BUILTIN_CALC_CONST) - 1)))
+	{
+		return (SymName + (sizeof (SYMPF_BUILTIN_CALC_CONST) - 1));
+	}
+	return NULL;
+}
+
+// Check if the given location points to a calculator-specific builtin
+// symbol.
+BOOLEAN IsCalcBuiltinLocation (const LOCATION *Location)
+{
+	return (Location->SymbolName && (GetCalcBuiltinValues (Location->SymbolName) || (!(strcmp (Location->SymbolName, SYM_BUILTIN_HARDWARE_ID)))));
+}
+
+// Return whether the reloc can be resolved to a calculator-specific value.
+// ResolveSpecialSymbol or something related must have been called on the
+// reloc at least once.
+BOOLEAN IsPlainCalcBuiltin (const RELOC *Reloc)
+{
+	// To improve speed, check whether the target and relation (if it exists)
+	// are builtin symbols.
+	if (Reloc->Target.Builtin && ((!(Reloc->Relation)) || Reloc->Relation->Builtin))
+	{
+		// Check the target.
+		if (IsCalcBuiltinLocation (&(Reloc->Target)))
+		{
+			// If there is a relation, check it.
+			if (Reloc->Relation)
+				return IsCalcBuiltinLocation (Reloc->Relation);
+			// Otherwise, the reloc may not be relative, since that would
+			// mean that it cannot be resolved to a number.
+			else
+				return (!(Reloc->Relative));
+		}
+	}
+	
+	return FALSE;
+}
+
+// If the location can be resolved to a calculator-specific value, get the
+// value for the specified calculator.
+// If IsCalcBuiltinLocation returned a positive result for this reloc, this
+// function will not return a negative result.
+BOOLEAN GetCalcBuiltinLocationValue (const LOCATION *Location, ProgramCalcs DestCalc, IMAX *Value)
+{
+	// Basic sanity checks.
+	if (Value && Location->SymbolName)
+	{
+		// Special case: __ld_hardware_id
+		if (!(strcmp (Location->SymbolName, SYM_BUILTIN_HARDWARE_ID)))
+		{
+			switch (DestCalc)
+			{
+				case CALC_TI89:
+					*Value = 3;
+					break;
+					
+				case CALC_TI89 | CALC_FLAG_TITANIUM:
+					*Value = 9;
+					break;
+					
+				case CALC_TI92PLUS:
+					*Value = 1;
+					break;
+					
+				case CALC_V200:
+					*Value = 8;
+					break;
+					
+				default:
+					Warning (NULL, SYM_BUILTIN_HARDWARE_ID " not defined for this calculator.");
+					*Value = 0;
+			}
+			*Value += Location->Offset;
+			return TRUE;
+		}
+		else
+		{
+			// Get the part of the symbol name that holds the values,
+			// separated by '_'.
+			const char *Values = GetCalcBuiltinValues (Location->SymbolName);
+			if (Values)
+			{
+				// AND out the calculator flags.
+				DestCalc &= ~CALC_FLAG_TITANIUM;
+				
+				// While we still have at least one value left...
+				while (Values)
+				{
+					// Get the end of the value string.
+					const char *ValueEnd = strchr (Values, '_');
+					SIZE ValueSize = ValueEnd ? (SIZE) (ValueEnd - Values) : (SIZE) (strlen (Values));
+					
+					// If this is the value that belongs to the current calculator,
+					// extract the value and return.
+					if (DestCalc == 1)
+					{
+						char *EndPtr = NULL;
+						
+						// Create a copy of the value, with a terminating zero byte.
+						char ValueStr[ValueSize+1];
+						strncpy (ValueStr, Values, ValueSize);
+						ValueStr [ValueSize] = 0;
+						
+						// Convert this string into a number and return it.
+						*Value = strtoul (ValueStr, &EndPtr, 0);
+						if (EndPtr && *EndPtr)
+							Warning (NULL, "Invalid number `%s' in `%s'.", ValueStr, Location->SymbolName);
+						*Value += Location->Offset;
+						
+						return TRUE;
+					}
+					
+					// Advance to the next value.
+					Values = ValueEnd ? ValueEnd + 1 : NULL;
+					// Advance to the next calculator.
+					DestCalc >>= 1;
+				}
+				
+				// No more values were found, but the function did not exit yet.
+				Warning (NULL, "Calculator constant `%s' contains too few values.", Location->SymbolName);
+				*Value = 0;
+				
+				// We have to return a positive result anyway because
+				// IsCalcBuiltinLocation would as well.
+				return TRUE;
+			}
+		}
+	}
+	
+	return FALSE;
+}
+
+// If the reloc can be resolved to a calculator-specific value, get the
+// value for the specified calculator.
+// The return value is the same as for IsPlainCalcBuiltin.
+BOOLEAN GetCalcBuiltinValue (const RELOC *Reloc, ProgramCalcs DestCalc, IMAX *Value)
+{
+	if (Value)
+	{
+		// Get the value for the target.
+		if (GetCalcBuiltinLocationValue (&(Reloc->Target), DestCalc, Value))
+		{
+			// If there is a relation, subtract its value.
+			if (Reloc->Relation)
+			{
+				IMAX RelationValue;
+				if (!(GetCalcBuiltinLocationValue (Reloc->Relation, DestCalc, &RelationValue)))
+					return FALSE;
+				*Value -= RelationValue;
+			}
+			// Otherwise, the reloc may not be relative
+			// (see IsPlainCalcBuiltin).
+			else if (Reloc->Relative)
+				return FALSE;
+			
+			*Value += Reloc->FixedOffset;
+			return TRUE;
+		}
+	}
+	
+	return FALSE;
+}
+
+// If required by some special symbol(s) at the end of the section,
+// modify the contents of the section.
+// This can be used to insert special items such as relocation entries.
+// MergedSection specifies the (usually large) part of the program that
+// has already been merged.
+// If necessary, MergedSection is frozen automatically.
+BOOLEAN HandleSectionContents (SECTION *Section, SECTION *MergedSection)
+{
+	SYMBOL *Symbol;
+	
+	// Search the labels at the end of the secion to find special ones.
+	for (Symbol = FindSymbolAtPos (Section, Section->Size, TRUE); Symbol; Symbol = GetNext (Symbol))
+	{
+		if (!(strncmp (Symbol->Name, SYMPF_BUILTIN_INSERT, sizeof (SYMPF_BUILTIN_INSERT) - 1)))
+			return (HandleInsertion (Section, Symbol->Location, Symbol->Name + sizeof (SYMPF_BUILTIN_INSERT) - 1, MergedSection, FALSE));
+	}
+	
+	return TRUE;
+}
+
+// Insert contents for an insertion specified by SymbolName, and return
+// a symbol from where the insertion took place.
+// If necessary, Section is frozen automatically.
+SYMBOL *HandleAutoInsertion (SECTION *Section, const char *SymbolName)
+{
+	if (!(strncmp (SymbolName, SYMPF_BUILTIN_INSERT, sizeof (SYMPF_BUILTIN_INSERT) - 1)))
+	{
+		CreateSectionSegment (Section);
+		
+		// All insertions except compressed ones should be aligned on a 2-byte boundary.
+		if ((!(strncmp (SymbolName, SYMPF_BUILTIN_INSERT_COMPRESSED, sizeof (SYMPF_BUILTIN_INSERT_COMPRESSED) - 1)))
+		 || (!(strcmp (SymbolName, SYMPF_BUILTIN_INSERT "fargo021_relocs")))
+		 || (!(strcmp (SymbolName, SYMPF_BUILTIN_INSERT "preos_compressed_tables")))
+		 || PadSection (Section, 2))
+		{
+			// Create a new symbol at the end of the section.
+			SYMBOL *Result = calloc (1, sizeof (SYMBOL));
+			
+			if (Result)
+			{
+				Result->Parent   = Section;
+				Result->Location = Section->Size;
+				strncpy (Result->Name, SymbolName, MAX_SYM_LEN);
+				Result->Exported = TRUE;
+				Append (Section->Symbols, Result);
+				
+				// Insert the data.
+				if (AppendInsertionData (Section, SymbolName + sizeof (SYMPF_BUILTIN_INSERT) - 1, Section, TRUE))
+					return Result;
+			}
+			else
+				Error (NULL, "Out of memory.");
+		}
+	}
+	
+	return NULL;
+}
+
+// Handle an insertion by cutting the section off at the specified location
+// and inserting the contents specified by the name, taking into account
+// that MergedSection specifies the (usually large) part of the program
+// that has already been merged.
+BOOLEAN HandleInsertion (SECTION *Section, OFFSET Location, const char *Name, SECTION *MergedSection, BOOLEAN AlwaysTerminate)
+{
+	if (Location == Section->Size)
+		return AppendInsertionData (Section, Name, MergedSection, AlwaysTerminate);
+	else
+		return TRUE;
+}
+
+// Append the data required by an insertion (specified by name) to the
+// section specified by Section, taking into account that MergedSection
+// specifies the (usually large) part of the program that has already
+// been merged.
+BOOLEAN AppendInsertionData (SECTION *Section, const char *Name, SECTION *MergedSection, BOOLEAN AlwaysTerminate)
+{
+	BOOLEAN NameMatches (const char *InsertionName)
+	{
+		return (!(strcmp (Name, InsertionName)));
+	}
+	
+	PROGRAM *Program = Section->Parent;
+	
+#ifdef DATA_VAR_SUPPORT
+	// Data variable name.
+	if (NameMatches ("data_var_name"))
+		return InsertDataVarName (Section);
+	else
+#endif /* DATA_VAR_SUPPORT */
+	
+	// Nostub-specific formats.
+	if (NameMatches ("nostub_comments"))
+		return InsertNostubComments (Section);
+	
+	// Kernel-specific formats.
+	else if (NameMatches ("kernel_relocs"))
+		return InsertKernelRelocs (Section, NULL);
+	else if (NameMatches ("kernel_bss_refs"))
+		return InsertKernelSectionRefs (Section, Program->BSSSection,  AlwaysTerminate);
+	else if (NameMatches ("kernel_data_refs"))
+		return InsertKernelSectionRefs (Section, Program->DataSection, AlwaysTerminate);
+	else if (NameMatches ("kernel_rom_calls"))
+		return InsertKernelROMCalls (Section);
+	else if (NameMatches ("kernel_ram_calls"))
+		return InsertKernelRAMCalls (Section);
+	else if (NameMatches ("kernel_libs"))
+		return InsertKernelLibraries (Section);
+	else if (NameMatches ("kernel_exports"))
+		return InsertKernelExports (Section, TRUE);
+	
+#ifdef FARGO_SUPPORT
+	// Fargo-specific formats.
+	else if (NameMatches ("fargo_exports"))
+		return InsertKernelExports (Section, FALSE);
+	else if (NameMatches ("fargo020_bss_refs"))
+		return InsertKernelSectionRefs (Section, Program->BSSSection, TRUE);
+	else if (NameMatches ("fargo020_libs"))
+		return InsertFargo020Libraries (Section);
+#endif /* FARGO_SUPPORT */
+	
+	// PreOS-specific formats.
+	else if (NameMatches ("preos_compressed_tables"))
+		return InsertPreOsCompressedTables (Section);
+	
+	// Other compressed formats.
+	else
+	{
+		char *ReferenceName = malloc ((sizeof (SYMPF_BUILTIN) - 1) + strlen (Name) + sizeof ("_ref"));
+		
+		if (ReferenceName)
+		{
+			// Build the reference symbol name: "__ld_" Name "_ref".
+			strcpy (ReferenceName, SYMPF_BUILTIN);
+			strcat (ReferenceName, Name);
+			strcat (ReferenceName, "_ref");
+			
+			{
+				LOCATION Reference = {NULL, ReferenceName, 0, FALSE};
+				
+				// Try to find a reference symbol. If none is found, use
+				// the program entry point.
+				Section->Relocs.UnresolvedCount++;
+				if (!(ResolveLocation (Program, Section, &Reference)))
+				{
+					FreeLocationSymbolName (Section, &Reference);
+					Reference = Program->EntryPoint;
+				}
+				
+#ifdef FARGO_SUPPORT
+				// Fargo-specific formats.
+				if (NameMatches ("fargo021_relocs"))
+					return InsertCompressedRelocs (Section, NULL, MergedSection, &Reference);
+				else if (NameMatches ("fargo021_bss_refs"))
+					return InsertFargo021SectionRefs (Section, Program->BSSSection, MergedSection, &Reference);
+				else if (NameMatches ("fargo021_libs"))
+					return InsertFargo021Libraries (Section, MergedSection, &Reference);
+#endif /* FARGO_SUPPORT */
+				
+				// Compressed relocation tables using our own format.
+				else if (NameMatches ("compressed_relocs"))
+					return InsertCompressedRelocs (Section, NULL, MergedSection, &Reference);
+				else if (NameMatches ("compressed_bss_refs"))
+					return InsertCompressedSectionRefs (Section, Program->BSSSection, MergedSection, &Reference);
+				else if (NameMatches ("compressed_data_refs"))
+					return InsertCompressedSectionRefs (Section, Program->DataSection, MergedSection, &Reference);
+				else if (NameMatches ("compressed_rom_calls"))
+					return InsertCompressedROMCalls (Section, MergedSection, &Reference);
+				
+				else
+					Warning (GetFileName (Section, Section->Size), "Unrecognized insertion `%s'.", Name);
+			}
+		}
+		else
+		{
+			Error (NULL, "Out of memory.");
+			return FALSE;
+		}
+	}
+	
+	return TRUE;
+}

+ 147 - 0
special.h

@@ -0,0 +1,147 @@
+/* special.h: Routines to handle special characteristics of the linker
+
+   Copyright (C) 2002-2003 Sebastian Reichelt
+   Copyright (C) 2003 Kevin Kofler
+
+   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 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef SPECIAL_H
+#define SPECIAL_H
+
+#include "generic.h"
+#include "data.h"
+
+// Special symbols processed in other files.
+#define SYM_OMIT_BSS_INIT         "__ld_omit_bss_init"
+#define SYM_ALL_RELOCS            "__ld_all_relocs"
+#define SYM_IGNORE_GLOBAL_IMPORTS "__ld_ignore_global_imports"
+
+// Operators for global imports.
+#define SYMOP_NOT "NOT_"
+#define SYMOP_AND "_AND_"
+
+// Special symbol prefix for built-in symbols.
+#define SYMPF_BUILTIN "__ld_"
+
+// Special symbol prefixes for automatic insertions.
+#define SYMPF_INSERT "insert_"
+#define SYMPF_BUILTIN_INSERT SYMPF_BUILTIN SYMPF_INSERT
+
+#define SYMPF_COMPRESSED "compressed_"
+#define SYMPF_BUILTIN_INSERT_COMPRESSED SYMPF_BUILTIN_INSERT SYMPF_COMPRESSED
+
+#define SYMPF_CALC_CONST "calc_const_"
+#define SYMPF_BUILTIN_CALC_CONST SYMPF_BUILTIN SYMPF_CALC_CONST
+
+// Special symbol prefix for nostub comment data.
+#define SYMPF_NOSTUB_DATA "_nostub_data__"
+// Beginning of the nostub data prefix, so we can call GetExportNumber
+// on nostub data symbols by skipping this part.
+#define SYMPF_NOSTUB_DATA_START "_nostub_"
+
+// Special symbol resolving to hardware ID.
+#define SYM_BUILTIN_HARDWARE_ID SYMPF_BUILTIN "hardware_id"
+
+// Try to translate an external symbol into a special-feature symbol used by the linker.
+// If the symbol contains a number, it is written to the variable pointed to by Number.
+typedef enum {ST_NORMAL = 0, ST_ROM_CALL, ST_RAM_CALL, ST_EXTRA_RAM, ST_LIB_CALL} SpecialExternalSymbolTypes;
+SpecialExternalSymbolTypes TranslateSpecialExternalSymbol (PROGRAM *Program, char *SymbolName, void **Reference, OFFSET *Number);
+
+// Handle the symbol if it is a special one.
+// Returns TRUE if it was special; in this case it should not be used.
+BOOLEAN HandleSpecialSymbol (PROGRAM *Program, const char *SymbolName);
+
+// Translate a section name into a startup section number.
+// Returns 0 if the name does not represent a startup section.
+OFFSET GetStartupSectionNumber (const char *SectionName, SIZE MaxLen);
+
+// Translate a symbol name into the number of an exported function, if the
+// function is meant to be exported.
+// Returns -1 if it is not an exported function.
+OFFSET GetExportNumber (const char *SymbolName);
+
+// Add all imports with names defined by this program, if they are needed.
+// For example, they can add code to support constructors and destructors,
+// BSS blocks, etc.
+BOOLEAN CreateSpecialGlobalImports (PROGRAM *Program);
+
+// If the reloc or its relation point to a special ld-exported symbol,
+// change it to the appropriate value if possible. FALSE is returned
+// only if it is not a special symbol reloc, or if there was an error.
+BOOLEAN ResolveSpecialSymbolReloc (RELOC *Reloc, BOOLEAN *TryAgain);
+// If the reloc points to a special ld-exported symbol, change it to the
+// appropriate value if possible. FALSE is returned only if it is not a
+// special symbol reloc, or if there was an error.
+BOOLEAN ResolveSpecialSymbolRelocTarget (RELOC *Reloc, BOOLEAN *TryAgain);
+// If the reloc's relation points to a special ld-exported symbol,
+// change it to the appropriate value if possible. FALSE is returned
+// only if it is not a special symbol location, or if there was an error.
+BOOLEAN ResolveSpecialSymbolRelocRelation (RELOC *Reloc, BOOLEAN *TryAgain);
+// If the location points to a special ld-exported symbol, change it to the
+// appropriate value if possible. FALSE is returned only if it is not a
+// special symbol location, or if there was an error.
+// Warning: If the special symbol resolves to a number, Location->Symbol and
+// Location->SymbolName will both be NULL, and Location->Offset will contain
+// the number.
+BOOLEAN ResolveSpecialSymbolLocation (SECTION *Section, LOCATION *Location, BOOLEAN *TryAgain);
+
+// Count the items for a specific built-in symbol, specified by SymName
+// and SymNameLength. If TrueValue is nonzero, items are not counted,
+// but NewValue is set to this value if at least one item was found. In
+// that case, if Program->ResolveAllBuiltins is false, NewValue may be
+// unchanged even though there are some items; you need to check back
+// later when Program->ResolveAllBuiltins is true.
+BOOLEAN GetBuiltinValue (PROGRAM *Program, const char *SymName, SIZE SymNameLength, OFFSET *NewValue, OFFSET TrueValue);
+
+// Check if the given location points to a calculator-specific builtin
+// symbol.
+BOOLEAN IsCalcBuiltinLocation (const LOCATION *Location);
+// Return whether the reloc can be resolved to a calculator-specific value.
+// ResolveSpecialSymbol or something related must have been called on the
+// reloc at least once.
+BOOLEAN IsPlainCalcBuiltin (const RELOC *Reloc);
+// If the location can be resolved to a calculator-specific value, get the
+// value for the specified calculator.
+// If IsCalcBuiltinLocation returned a positive result for this reloc, this
+// function will not return a negative result.
+BOOLEAN GetCalcBuiltinLocationValue (const LOCATION *Location, ProgramCalcs DestCalc, IMAX *Value);
+// If the reloc can be resolved to a calculator-specific value, get the
+// value for the specified calculator.
+// The return value is the same as for IsPlainCalcBuiltin.
+BOOLEAN GetCalcBuiltinValue (const RELOC *Reloc, ProgramCalcs DestCalc, IMAX *Value);
+
+// If required by some special symbol(s) in the section, modify the
+// contents of the section.
+// This can be used to insert special items such as relocation entries.
+// MergedSection specifies the (usually large) part of the program that
+// has already been merged.
+// If necessary, MergedSection is frozen automatically.
+BOOLEAN HandleSectionContents (SECTION *Section, SECTION *MergedSection);
+// Insert contents for an insertion specified by SymbolName, and return
+// a symbol from where the insertion took place.
+// If necessary, MergedSection is frozen automatically.
+SYMBOL *HandleAutoInsertion (SECTION *Section, const char *SymbolName);
+// Handle an insertion by cutting the section off at the specified location
+// and inserting the contents specified by the name, taking into account
+// that MergedSection specifies the (usually large) part of the program
+// that has already been merged.
+BOOLEAN HandleInsertion (SECTION *Section, OFFSET Location, const char *Name, SECTION *MergedSection, BOOLEAN AlwaysTerminate);
+// Append the data required by an insertion (specified by name) to the
+// section specified by Section, taking into account that MergedSection
+// specifies the (usually large) part of the program that has already
+// been merged.
+BOOLEAN AppendInsertionData (SECTION *Section, const char *Name, SECTION *MergedSection, BOOLEAN AlwaysTerminate);
+
+#endif